]> git.ipfire.org Git - people/teissler/ipfire-2.x.git/blob - src/patches/kernel/omap/3isp/0001-omap3isp-Use-the-common-clock-framework.patch
Merge branch 'next' of ssh://git.ipfire.org/pub/git/ipfire-2.x into next
[people/teissler/ipfire-2.x.git] / src / patches / kernel / omap / 3isp / 0001-omap3isp-Use-the-common-clock-framework.patch
1 From 369b60c157f67a71a6f302ab9843ae2de1805a2a Mon Sep 17 00:00:00 2001
2 From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
3 Date: Mon, 22 Oct 2012 15:43:00 +0200
4 Subject: [PATCH 1/6] omap3isp: Use the common clock framework
5
6 Expose the two ISP external clocks XCLKA and XCLKB as common clocks for
7 subdev drivers.
8
9 Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
10 Acked-by: Mike Turquette <mturquette@linaro.org>
11 ---
12 drivers/media/platform/omap3isp/isp.c | 277 ++++++++++++++++++++++++---------
13 drivers/media/platform/omap3isp/isp.h | 22 ++-
14 include/media/omap3isp.h | 10 +-
15 3 files changed, 225 insertions(+), 84 deletions(-)
16
17 diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
18 index 6e5ad8e..1d7dbd5 100644
19 --- a/drivers/media/platform/omap3isp/isp.c
20 +++ b/drivers/media/platform/omap3isp/isp.c
21 @@ -55,6 +55,7 @@
22 #include <asm/cacheflush.h>
23
24 #include <linux/clk.h>
25 +#include <linux/clkdev.h>
26 #include <linux/delay.h>
27 #include <linux/device.h>
28 #include <linux/dma-mapping.h>
29 @@ -148,6 +149,201 @@ void omap3isp_flush(struct isp_device *isp)
30 isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
31 }
32
33 +/* -----------------------------------------------------------------------------
34 + * XCLK
35 + */
36 +
37 +#define to_isp_xclk(_hw) container_of(_hw, struct isp_xclk, hw)
38 +
39 +static void isp_xclk_update(struct isp_xclk *xclk, u32 divider)
40 +{
41 + switch (xclk->id) {
42 + case ISP_XCLK_A:
43 + isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
44 + ISPTCTRL_CTRL_DIVA_MASK,
45 + divider << ISPTCTRL_CTRL_DIVA_SHIFT);
46 + break;
47 + case ISP_XCLK_B:
48 + isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
49 + ISPTCTRL_CTRL_DIVB_MASK,
50 + divider << ISPTCTRL_CTRL_DIVB_SHIFT);
51 + break;
52 + }
53 +}
54 +
55 +static int isp_xclk_prepare(struct clk_hw *hw)
56 +{
57 + struct isp_xclk *xclk = to_isp_xclk(hw);
58 +
59 + omap3isp_get(xclk->isp);
60 +
61 + return 0;
62 +}
63 +
64 +static void isp_xclk_unprepare(struct clk_hw *hw)
65 +{
66 + struct isp_xclk *xclk = to_isp_xclk(hw);
67 +
68 + omap3isp_put(xclk->isp);
69 +}
70 +
71 +static int isp_xclk_enable(struct clk_hw *hw)
72 +{
73 + struct isp_xclk *xclk = to_isp_xclk(hw);
74 + unsigned long flags;
75 +
76 + spin_lock_irqsave(&xclk->lock, flags);
77 + isp_xclk_update(xclk, xclk->divider);
78 + xclk->enabled = true;
79 + spin_unlock_irqrestore(&xclk->lock, flags);
80 +
81 + return 0;
82 +}
83 +
84 +static void isp_xclk_disable(struct clk_hw *hw)
85 +{
86 + struct isp_xclk *xclk = to_isp_xclk(hw);
87 + unsigned long flags;
88 +
89 + spin_lock_irqsave(&xclk->lock, flags);
90 + isp_xclk_update(xclk, 0);
91 + xclk->enabled = false;
92 + spin_unlock_irqrestore(&xclk->lock, flags);
93 +}
94 +
95 +static unsigned long isp_xclk_recalc_rate(struct clk_hw *hw,
96 + unsigned long parent_rate)
97 +{
98 + struct isp_xclk *xclk = to_isp_xclk(hw);
99 +
100 + return parent_rate / xclk->divider;
101 +}
102 +
103 +static u32 isp_xclk_calc_divider(unsigned long *rate, unsigned long parent_rate)
104 +{
105 + u32 divider;
106 +
107 + if (*rate >= parent_rate) {
108 + *rate = parent_rate;
109 + return ISPTCTRL_CTRL_DIV_BYPASS;
110 + }
111 +
112 + divider = DIV_ROUND_CLOSEST(parent_rate, *rate);
113 + if (divider >= ISPTCTRL_CTRL_DIV_BYPASS)
114 + divider = ISPTCTRL_CTRL_DIV_BYPASS - 1;
115 +
116 + *rate = parent_rate / divider;
117 + return divider;
118 +}
119 +
120 +static long isp_xclk_round_rate(struct clk_hw *hw, unsigned long rate,
121 + unsigned long *parent_rate)
122 +{
123 + isp_xclk_calc_divider(&rate, *parent_rate);
124 + return rate;
125 +}
126 +
127 +static int isp_xclk_set_rate(struct clk_hw *hw, unsigned long rate,
128 + unsigned long parent_rate)
129 +{
130 + struct isp_xclk *xclk = to_isp_xclk(hw);
131 + unsigned long flags;
132 + u32 divider;
133 +
134 + divider = isp_xclk_calc_divider(&rate, parent_rate);
135 +
136 + spin_lock_irqsave(&xclk->lock, flags);
137 +
138 + xclk->divider = divider;
139 + if (xclk->enabled)
140 + isp_xclk_update(xclk, divider);
141 +
142 + spin_unlock_irqrestore(&xclk->lock, flags);
143 +
144 + dev_dbg(xclk->isp->dev, "%s: cam_xclk%c set to %lu Hz (div %u)\n",
145 + __func__, xclk->id == ISP_XCLK_A ? 'a' : 'b', rate, divider);
146 + return 0;
147 +}
148 +
149 +static const struct clk_ops isp_xclk_ops = {
150 + .prepare = isp_xclk_prepare,
151 + .unprepare = isp_xclk_unprepare,
152 + .enable = isp_xclk_enable,
153 + .disable = isp_xclk_disable,
154 + .recalc_rate = isp_xclk_recalc_rate,
155 + .round_rate = isp_xclk_round_rate,
156 + .set_rate = isp_xclk_set_rate,
157 +};
158 +
159 +static const char *isp_xclk_parent_name = "cam_mclk";
160 +
161 +static const struct clk_init_data isp_xclk_init_data = {
162 + .name = "cam_xclk",
163 + .ops = &isp_xclk_ops,
164 + .parent_names = &isp_xclk_parent_name,
165 + .num_parents = 1,
166 +};
167 +
168 +static int isp_xclk_init(struct isp_device *isp)
169 +{
170 + struct isp_platform_data *pdata = isp->pdata;
171 + struct clk_init_data init;
172 + unsigned int i;
173 +
174 + for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) {
175 + struct isp_xclk *xclk = &isp->xclks[i];
176 + struct clk *clk;
177 +
178 + xclk->isp = isp;
179 + xclk->id = i == 0 ? ISP_XCLK_A : ISP_XCLK_B;
180 + xclk->divider = 1;
181 + spin_lock_init(&xclk->lock);
182 +
183 + init.name = i == 0 ? "cam_xclka" : "cam_xclkb";
184 + init.ops = &isp_xclk_ops;
185 + init.parent_names = &isp_xclk_parent_name;
186 + init.num_parents = 1;
187 +
188 + xclk->hw.init = &init;
189 +
190 + clk = devm_clk_register(isp->dev, &xclk->hw);
191 + if (IS_ERR(clk))
192 + return PTR_ERR(clk);
193 +
194 + if (pdata->xclks[i].con_id == NULL &&
195 + pdata->xclks[i].dev_id == NULL)
196 + continue;
197 +
198 + xclk->lookup = kzalloc(sizeof(*xclk->lookup), GFP_KERNEL);
199 + if (xclk->lookup == NULL)
200 + return -ENOMEM;
201 +
202 + xclk->lookup->con_id = pdata->xclks[i].con_id;
203 + xclk->lookup->dev_id = pdata->xclks[i].dev_id;
204 + xclk->lookup->clk = clk;
205 +
206 + clkdev_add(xclk->lookup);
207 + }
208 +
209 + return 0;
210 +}
211 +
212 +static void isp_xclk_cleanup(struct isp_device *isp)
213 +{
214 + unsigned int i;
215 +
216 + for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) {
217 + struct isp_xclk *xclk = &isp->xclks[i];
218 +
219 + if (xclk->lookup)
220 + clkdev_drop(xclk->lookup);
221 + }
222 +}
223 +
224 +/* -----------------------------------------------------------------------------
225 + * Interrupts
226 + */
227 +
228 /*
229 * isp_enable_interrupts - Enable ISP interrupts.
230 * @isp: OMAP3 ISP device
231 @@ -180,80 +376,6 @@ static void isp_disable_interrupts(struct isp_device *isp)
232 isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE);
233 }
234
235 -/**
236 - * isp_set_xclk - Configures the specified cam_xclk to the desired frequency.
237 - * @isp: OMAP3 ISP device
238 - * @xclk: Desired frequency of the clock in Hz. 0 = stable low, 1 is stable high
239 - * @xclksel: XCLK to configure (0 = A, 1 = B).
240 - *
241 - * Configures the specified MCLK divisor in the ISP timing control register
242 - * (TCTRL_CTRL) to generate the desired xclk clock value.
243 - *
244 - * Divisor = cam_mclk_hz / xclk
245 - *
246 - * Returns the final frequency that is actually being generated
247 - **/
248 -static u32 isp_set_xclk(struct isp_device *isp, u32 xclk, u8 xclksel)
249 -{
250 - u32 divisor;
251 - u32 currentxclk;
252 - unsigned long mclk_hz;
253 -
254 - if (!omap3isp_get(isp))
255 - return 0;
256 -
257 - mclk_hz = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]);
258 -
259 - if (xclk >= mclk_hz) {
260 - divisor = ISPTCTRL_CTRL_DIV_BYPASS;
261 - currentxclk = mclk_hz;
262 - } else if (xclk >= 2) {
263 - divisor = mclk_hz / xclk;
264 - if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS)
265 - divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1;
266 - currentxclk = mclk_hz / divisor;
267 - } else {
268 - divisor = xclk;
269 - currentxclk = 0;
270 - }
271 -
272 - switch (xclksel) {
273 - case ISP_XCLK_A:
274 - isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
275 - ISPTCTRL_CTRL_DIVA_MASK,
276 - divisor << ISPTCTRL_CTRL_DIVA_SHIFT);
277 - dev_dbg(isp->dev, "isp_set_xclk(): cam_xclka set to %d Hz\n",
278 - currentxclk);
279 - break;
280 - case ISP_XCLK_B:
281 - isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
282 - ISPTCTRL_CTRL_DIVB_MASK,
283 - divisor << ISPTCTRL_CTRL_DIVB_SHIFT);
284 - dev_dbg(isp->dev, "isp_set_xclk(): cam_xclkb set to %d Hz\n",
285 - currentxclk);
286 - break;
287 - case ISP_XCLK_NONE:
288 - default:
289 - omap3isp_put(isp);
290 - dev_dbg(isp->dev, "ISP_ERR: isp_set_xclk(): Invalid requested "
291 - "xclk. Must be 0 (A) or 1 (B).\n");
292 - return -EINVAL;
293 - }
294 -
295 - /* Do we go from stable whatever to clock? */
296 - if (divisor >= 2 && isp->xclk_divisor[xclksel - 1] < 2)
297 - omap3isp_get(isp);
298 - /* Stopping the clock. */
299 - else if (divisor < 2 && isp->xclk_divisor[xclksel - 1] >= 2)
300 - omap3isp_put(isp);
301 -
302 - isp->xclk_divisor[xclksel - 1] = divisor;
303 -
304 - omap3isp_put(isp);
305 -
306 - return currentxclk;
307 -}
308 -
309 /*
310 * isp_core_init - ISP core settings
311 * @isp: OMAP3 ISP device
312 @@ -1969,6 +2091,7 @@ static int isp_remove(struct platform_device *pdev)
313
314 isp_unregister_entities(isp);
315 isp_cleanup_modules(isp);
316 + isp_xclk_cleanup(isp);
317
318 __omap3isp_get(isp, false);
319 iommu_detach_device(isp->domain, &pdev->dev);
320 @@ -2042,7 +2165,6 @@ static int isp_probe(struct platform_device *pdev)
321 }
322
323 isp->autoidle = autoidle;
324 - isp->platform_cb.set_xclk = isp_set_xclk;
325
326 mutex_init(&isp->isp_mutex);
327 spin_lock_init(&isp->stat_lock);
328 @@ -2093,6 +2215,10 @@ static int isp_probe(struct platform_device *pdev)
329 if (ret < 0)
330 goto error_isp;
331
332 + ret = isp_xclk_init(isp);
333 + if (ret < 0)
334 + goto error_isp;
335 +
336 /* Memory resources */
337 for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++)
338 if (isp->revision == isp_res_maps[m].isp_rev)
339 @@ -2162,6 +2288,7 @@ detach_dev:
340 free_domain:
341 iommu_domain_free(isp->domain);
342 error_isp:
343 + isp_xclk_cleanup(isp);
344 omap3isp_put(isp);
345 error:
346 platform_set_drvdata(pdev, NULL);
347 diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h
348 index c77e1f2..cd3eff4 100644
349 --- a/drivers/media/platform/omap3isp/isp.h
350 +++ b/drivers/media/platform/omap3isp/isp.h
351 @@ -29,6 +29,7 @@
352
353 #include <media/omap3isp.h>
354 #include <media/v4l2-device.h>
355 +#include <linux/clk-provider.h>
356 #include <linux/device.h>
357 #include <linux/io.h>
358 #include <linux/iommu.h>
359 @@ -125,8 +126,20 @@ struct isp_reg {
360 u32 val;
361 };
362
363 -struct isp_platform_callback {
364 - u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel);
365 +enum isp_xclk_id {
366 + ISP_XCLK_A,
367 + ISP_XCLK_B,
368 +};
369 +
370 +struct isp_xclk {
371 + struct isp_device *isp;
372 + struct clk_hw hw;
373 + struct clk_lookup *lookup;
374 + enum isp_xclk_id id;
375 +
376 + spinlock_t lock; /* Protects enabled and divider */
377 + bool enabled;
378 + unsigned int divider;
379 };
380
381 /*
382 @@ -149,6 +162,7 @@ struct isp_platform_callback {
383 * @cam_mclk: Pointer to camera functional clock structure.
384 * @csi2_fck: Pointer to camera CSI2 complexIO clock structure.
385 * @l3_ick: Pointer to OMAP3 L3 bus interface clock.
386 + * @xclks: External clocks provided by the ISP
387 * @irq: Currently attached ISP ISR callbacks information structure.
388 * @isp_af: Pointer to current settings for ISP AutoFocus SCM.
389 * @isp_hist: Pointer to current settings for ISP Histogram SCM.
390 @@ -185,12 +199,12 @@ struct isp_device {
391 int has_context;
392 int ref_count;
393 unsigned int autoidle;
394 - u32 xclk_divisor[2]; /* Two clocks, a and b. */
395 #define ISP_CLK_CAM_ICK 0
396 #define ISP_CLK_CAM_MCLK 1
397 #define ISP_CLK_CSI2_FCK 2
398 #define ISP_CLK_L3_ICK 3
399 struct clk *clock[4];
400 + struct isp_xclk xclks[2];
401
402 /* ISP modules */
403 struct ispstat isp_af;
404 @@ -209,8 +223,6 @@ struct isp_device {
405 unsigned int subclk_resources;
406
407 struct iommu_domain *domain;
408 -
409 - struct isp_platform_callback platform_cb;
410 };
411
412 #define v4l2_dev_to_isp_device(dev) \
413 diff --git a/include/media/omap3isp.h b/include/media/omap3isp.h
414 index 9584269..c9d06d9 100644
415 --- a/include/media/omap3isp.h
416 +++ b/include/media/omap3isp.h
417 @@ -29,10 +29,6 @@
418 struct i2c_board_info;
419 struct isp_device;
420
421 -#define ISP_XCLK_NONE 0
422 -#define ISP_XCLK_A 1
423 -#define ISP_XCLK_B 2
424 -
425 enum isp_interface_type {
426 ISP_INTERFACE_PARALLEL,
427 ISP_INTERFACE_CSI2A_PHY2,
428 @@ -153,7 +149,13 @@ struct isp_v4l2_subdevs_group {
429 } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */
430 };
431
432 +struct isp_platform_xclk {
433 + const char *dev_id;
434 + const char *con_id;
435 +};
436 +
437 struct isp_platform_data {
438 + struct isp_platform_xclk xclks[2];
439 struct isp_v4l2_subdevs_group *subdevs;
440 void (*set_constraints)(struct isp_device *isp, bool enable);
441 };
442 --
443 1.7.10.4
444