aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2012-10-22 09:43:00 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2013-04-14 19:21:12 -0400
commit9b28ee3c9122cea62f2db02f5bb1e1606bb343a6 (patch)
tree7092c63fa14f7e5162400ee5a571f92532bcab5c /drivers/media
parent4290fd1a5688f3510caa0625c62d73de568ed2c2 (diff)
[media] omap3isp: Use the common clock framework
Expose the two ISP external clocks XCLKA and XCLKB as common clocks for subdev drivers. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Acked-by: Mike Turquette <mturquette@linaro.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/platform/omap3isp/isp.c277
-rw-r--r--drivers/media/platform/omap3isp/isp.h22
2 files changed, 219 insertions, 80 deletions
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 6e5ad8ec0a22..1d7dbd5c0fba 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -55,6 +55,7 @@
55#include <asm/cacheflush.h> 55#include <asm/cacheflush.h>
56 56
57#include <linux/clk.h> 57#include <linux/clk.h>
58#include <linux/clkdev.h>
58#include <linux/delay.h> 59#include <linux/delay.h>
59#include <linux/device.h> 60#include <linux/device.h>
60#include <linux/dma-mapping.h> 61#include <linux/dma-mapping.h>
@@ -148,6 +149,201 @@ void omap3isp_flush(struct isp_device *isp)
148 isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); 149 isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
149} 150}
150 151
152/* -----------------------------------------------------------------------------
153 * XCLK
154 */
155
156#define to_isp_xclk(_hw) container_of(_hw, struct isp_xclk, hw)
157
158static void isp_xclk_update(struct isp_xclk *xclk, u32 divider)
159{
160 switch (xclk->id) {
161 case ISP_XCLK_A:
162 isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
163 ISPTCTRL_CTRL_DIVA_MASK,
164 divider << ISPTCTRL_CTRL_DIVA_SHIFT);
165 break;
166 case ISP_XCLK_B:
167 isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
168 ISPTCTRL_CTRL_DIVB_MASK,
169 divider << ISPTCTRL_CTRL_DIVB_SHIFT);
170 break;
171 }
172}
173
174static int isp_xclk_prepare(struct clk_hw *hw)
175{
176 struct isp_xclk *xclk = to_isp_xclk(hw);
177
178 omap3isp_get(xclk->isp);
179
180 return 0;
181}
182
183static void isp_xclk_unprepare(struct clk_hw *hw)
184{
185 struct isp_xclk *xclk = to_isp_xclk(hw);
186
187 omap3isp_put(xclk->isp);
188}
189
190static int isp_xclk_enable(struct clk_hw *hw)
191{
192 struct isp_xclk *xclk = to_isp_xclk(hw);
193 unsigned long flags;
194
195 spin_lock_irqsave(&xclk->lock, flags);
196 isp_xclk_update(xclk, xclk->divider);
197 xclk->enabled = true;
198 spin_unlock_irqrestore(&xclk->lock, flags);
199
200 return 0;
201}
202
203static void isp_xclk_disable(struct clk_hw *hw)
204{
205 struct isp_xclk *xclk = to_isp_xclk(hw);
206 unsigned long flags;
207
208 spin_lock_irqsave(&xclk->lock, flags);
209 isp_xclk_update(xclk, 0);
210 xclk->enabled = false;
211 spin_unlock_irqrestore(&xclk->lock, flags);
212}
213
214static unsigned long isp_xclk_recalc_rate(struct clk_hw *hw,
215 unsigned long parent_rate)
216{
217 struct isp_xclk *xclk = to_isp_xclk(hw);
218
219 return parent_rate / xclk->divider;
220}
221
222static u32 isp_xclk_calc_divider(unsigned long *rate, unsigned long parent_rate)
223{
224 u32 divider;
225
226 if (*rate >= parent_rate) {
227 *rate = parent_rate;
228 return ISPTCTRL_CTRL_DIV_BYPASS;
229 }
230
231 divider = DIV_ROUND_CLOSEST(parent_rate, *rate);
232 if (divider >= ISPTCTRL_CTRL_DIV_BYPASS)
233 divider = ISPTCTRL_CTRL_DIV_BYPASS - 1;
234
235 *rate = parent_rate / divider;
236 return divider;
237}
238
239static long isp_xclk_round_rate(struct clk_hw *hw, unsigned long rate,
240 unsigned long *parent_rate)
241{
242 isp_xclk_calc_divider(&rate, *parent_rate);
243 return rate;
244}
245
246static int isp_xclk_set_rate(struct clk_hw *hw, unsigned long rate,
247 unsigned long parent_rate)
248{
249 struct isp_xclk *xclk = to_isp_xclk(hw);
250 unsigned long flags;
251 u32 divider;
252
253 divider = isp_xclk_calc_divider(&rate, parent_rate);
254
255 spin_lock_irqsave(&xclk->lock, flags);
256
257 xclk->divider = divider;
258 if (xclk->enabled)
259 isp_xclk_update(xclk, divider);
260
261 spin_unlock_irqrestore(&xclk->lock, flags);
262
263 dev_dbg(xclk->isp->dev, "%s: cam_xclk%c set to %lu Hz (div %u)\n",
264 __func__, xclk->id == ISP_XCLK_A ? 'a' : 'b', rate, divider);
265 return 0;
266}
267
268static const struct clk_ops isp_xclk_ops = {
269 .prepare = isp_xclk_prepare,
270 .unprepare = isp_xclk_unprepare,
271 .enable = isp_xclk_enable,
272 .disable = isp_xclk_disable,
273 .recalc_rate = isp_xclk_recalc_rate,
274 .round_rate = isp_xclk_round_rate,
275 .set_rate = isp_xclk_set_rate,
276};
277
278static const char *isp_xclk_parent_name = "cam_mclk";
279
280static const struct clk_init_data isp_xclk_init_data = {
281 .name = "cam_xclk",
282 .ops = &isp_xclk_ops,
283 .parent_names = &isp_xclk_parent_name,
284 .num_parents = 1,
285};
286
287static int isp_xclk_init(struct isp_device *isp)
288{
289 struct isp_platform_data *pdata = isp->pdata;
290 struct clk_init_data init;
291 unsigned int i;
292
293 for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) {
294 struct isp_xclk *xclk = &isp->xclks[i];
295 struct clk *clk;
296
297 xclk->isp = isp;
298 xclk->id = i == 0 ? ISP_XCLK_A : ISP_XCLK_B;
299 xclk->divider = 1;
300 spin_lock_init(&xclk->lock);
301
302 init.name = i == 0 ? "cam_xclka" : "cam_xclkb";
303 init.ops = &isp_xclk_ops;
304 init.parent_names = &isp_xclk_parent_name;
305 init.num_parents = 1;
306
307 xclk->hw.init = &init;
308
309 clk = devm_clk_register(isp->dev, &xclk->hw);
310 if (IS_ERR(clk))
311 return PTR_ERR(clk);
312
313 if (pdata->xclks[i].con_id == NULL &&
314 pdata->xclks[i].dev_id == NULL)
315 continue;
316
317 xclk->lookup = kzalloc(sizeof(*xclk->lookup), GFP_KERNEL);
318 if (xclk->lookup == NULL)
319 return -ENOMEM;
320
321 xclk->lookup->con_id = pdata->xclks[i].con_id;
322 xclk->lookup->dev_id = pdata->xclks[i].dev_id;
323 xclk->lookup->clk = clk;
324
325 clkdev_add(xclk->lookup);
326 }
327
328 return 0;
329}
330
331static void isp_xclk_cleanup(struct isp_device *isp)
332{
333 unsigned int i;
334
335 for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) {
336 struct isp_xclk *xclk = &isp->xclks[i];
337
338 if (xclk->lookup)
339 clkdev_drop(xclk->lookup);
340 }
341}
342
343/* -----------------------------------------------------------------------------
344 * Interrupts
345 */
346
151/* 347/*
152 * isp_enable_interrupts - Enable ISP interrupts. 348 * isp_enable_interrupts - Enable ISP interrupts.
153 * @isp: OMAP3 ISP device 349 * @isp: OMAP3 ISP device
@@ -180,80 +376,6 @@ static void isp_disable_interrupts(struct isp_device *isp)
180 isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); 376 isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE);
181} 377}
182 378
183/**
184 * isp_set_xclk - Configures the specified cam_xclk to the desired frequency.
185 * @isp: OMAP3 ISP device
186 * @xclk: Desired frequency of the clock in Hz. 0 = stable low, 1 is stable high
187 * @xclksel: XCLK to configure (0 = A, 1 = B).
188 *
189 * Configures the specified MCLK divisor in the ISP timing control register
190 * (TCTRL_CTRL) to generate the desired xclk clock value.
191 *
192 * Divisor = cam_mclk_hz / xclk
193 *
194 * Returns the final frequency that is actually being generated
195 **/
196static u32 isp_set_xclk(struct isp_device *isp, u32 xclk, u8 xclksel)
197{
198 u32 divisor;
199 u32 currentxclk;
200 unsigned long mclk_hz;
201
202 if (!omap3isp_get(isp))
203 return 0;
204
205 mclk_hz = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]);
206
207 if (xclk >= mclk_hz) {
208 divisor = ISPTCTRL_CTRL_DIV_BYPASS;
209 currentxclk = mclk_hz;
210 } else if (xclk >= 2) {
211 divisor = mclk_hz / xclk;
212 if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS)
213 divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1;
214 currentxclk = mclk_hz / divisor;
215 } else {
216 divisor = xclk;
217 currentxclk = 0;
218 }
219
220 switch (xclksel) {
221 case ISP_XCLK_A:
222 isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
223 ISPTCTRL_CTRL_DIVA_MASK,
224 divisor << ISPTCTRL_CTRL_DIVA_SHIFT);
225 dev_dbg(isp->dev, "isp_set_xclk(): cam_xclka set to %d Hz\n",
226 currentxclk);
227 break;
228 case ISP_XCLK_B:
229 isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
230 ISPTCTRL_CTRL_DIVB_MASK,
231 divisor << ISPTCTRL_CTRL_DIVB_SHIFT);
232 dev_dbg(isp->dev, "isp_set_xclk(): cam_xclkb set to %d Hz\n",
233 currentxclk);
234 break;
235 case ISP_XCLK_NONE:
236 default:
237 omap3isp_put(isp);
238 dev_dbg(isp->dev, "ISP_ERR: isp_set_xclk(): Invalid requested "
239 "xclk. Must be 0 (A) or 1 (B).\n");
240 return -EINVAL;
241 }
242
243 /* Do we go from stable whatever to clock? */
244 if (divisor >= 2 && isp->xclk_divisor[xclksel - 1] < 2)
245 omap3isp_get(isp);
246 /* Stopping the clock. */
247 else if (divisor < 2 && isp->xclk_divisor[xclksel - 1] >= 2)
248 omap3isp_put(isp);
249
250 isp->xclk_divisor[xclksel - 1] = divisor;
251
252 omap3isp_put(isp);
253
254 return currentxclk;
255}
256
257/* 379/*
258 * isp_core_init - ISP core settings 380 * isp_core_init - ISP core settings
259 * @isp: OMAP3 ISP device 381 * @isp: OMAP3 ISP device
@@ -1969,6 +2091,7 @@ static int isp_remove(struct platform_device *pdev)
1969 2091
1970 isp_unregister_entities(isp); 2092 isp_unregister_entities(isp);
1971 isp_cleanup_modules(isp); 2093 isp_cleanup_modules(isp);
2094 isp_xclk_cleanup(isp);
1972 2095
1973 __omap3isp_get(isp, false); 2096 __omap3isp_get(isp, false);
1974 iommu_detach_device(isp->domain, &pdev->dev); 2097 iommu_detach_device(isp->domain, &pdev->dev);
@@ -2042,7 +2165,6 @@ static int isp_probe(struct platform_device *pdev)
2042 } 2165 }
2043 2166
2044 isp->autoidle = autoidle; 2167 isp->autoidle = autoidle;
2045 isp->platform_cb.set_xclk = isp_set_xclk;
2046 2168
2047 mutex_init(&isp->isp_mutex); 2169 mutex_init(&isp->isp_mutex);
2048 spin_lock_init(&isp->stat_lock); 2170 spin_lock_init(&isp->stat_lock);
@@ -2093,6 +2215,10 @@ static int isp_probe(struct platform_device *pdev)
2093 if (ret < 0) 2215 if (ret < 0)
2094 goto error_isp; 2216 goto error_isp;
2095 2217
2218 ret = isp_xclk_init(isp);
2219 if (ret < 0)
2220 goto error_isp;
2221
2096 /* Memory resources */ 2222 /* Memory resources */
2097 for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++) 2223 for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++)
2098 if (isp->revision == isp_res_maps[m].isp_rev) 2224 if (isp->revision == isp_res_maps[m].isp_rev)
@@ -2162,6 +2288,7 @@ detach_dev:
2162free_domain: 2288free_domain:
2163 iommu_domain_free(isp->domain); 2289 iommu_domain_free(isp->domain);
2164error_isp: 2290error_isp:
2291 isp_xclk_cleanup(isp);
2165 omap3isp_put(isp); 2292 omap3isp_put(isp);
2166error: 2293error:
2167 platform_set_drvdata(pdev, NULL); 2294 platform_set_drvdata(pdev, NULL);
diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h
index c77e1f2ae5ca..cd3eff45ae7d 100644
--- a/drivers/media/platform/omap3isp/isp.h
+++ b/drivers/media/platform/omap3isp/isp.h
@@ -29,6 +29,7 @@
29 29
30#include <media/omap3isp.h> 30#include <media/omap3isp.h>
31#include <media/v4l2-device.h> 31#include <media/v4l2-device.h>
32#include <linux/clk-provider.h>
32#include <linux/device.h> 33#include <linux/device.h>
33#include <linux/io.h> 34#include <linux/io.h>
34#include <linux/iommu.h> 35#include <linux/iommu.h>
@@ -125,8 +126,20 @@ struct isp_reg {
125 u32 val; 126 u32 val;
126}; 127};
127 128
128struct isp_platform_callback { 129enum isp_xclk_id {
129 u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel); 130 ISP_XCLK_A,
131 ISP_XCLK_B,
132};
133
134struct isp_xclk {
135 struct isp_device *isp;
136 struct clk_hw hw;
137 struct clk_lookup *lookup;
138 enum isp_xclk_id id;
139
140 spinlock_t lock; /* Protects enabled and divider */
141 bool enabled;
142 unsigned int divider;
130}; 143};
131 144
132/* 145/*
@@ -149,6 +162,7 @@ struct isp_platform_callback {
149 * @cam_mclk: Pointer to camera functional clock structure. 162 * @cam_mclk: Pointer to camera functional clock structure.
150 * @csi2_fck: Pointer to camera CSI2 complexIO clock structure. 163 * @csi2_fck: Pointer to camera CSI2 complexIO clock structure.
151 * @l3_ick: Pointer to OMAP3 L3 bus interface clock. 164 * @l3_ick: Pointer to OMAP3 L3 bus interface clock.
165 * @xclks: External clocks provided by the ISP
152 * @irq: Currently attached ISP ISR callbacks information structure. 166 * @irq: Currently attached ISP ISR callbacks information structure.
153 * @isp_af: Pointer to current settings for ISP AutoFocus SCM. 167 * @isp_af: Pointer to current settings for ISP AutoFocus SCM.
154 * @isp_hist: Pointer to current settings for ISP Histogram SCM. 168 * @isp_hist: Pointer to current settings for ISP Histogram SCM.
@@ -185,12 +199,12 @@ struct isp_device {
185 int has_context; 199 int has_context;
186 int ref_count; 200 int ref_count;
187 unsigned int autoidle; 201 unsigned int autoidle;
188 u32 xclk_divisor[2]; /* Two clocks, a and b. */
189#define ISP_CLK_CAM_ICK 0 202#define ISP_CLK_CAM_ICK 0
190#define ISP_CLK_CAM_MCLK 1 203#define ISP_CLK_CAM_MCLK 1
191#define ISP_CLK_CSI2_FCK 2 204#define ISP_CLK_CSI2_FCK 2
192#define ISP_CLK_L3_ICK 3 205#define ISP_CLK_L3_ICK 3
193 struct clk *clock[4]; 206 struct clk *clock[4];
207 struct isp_xclk xclks[2];
194 208
195 /* ISP modules */ 209 /* ISP modules */
196 struct ispstat isp_af; 210 struct ispstat isp_af;
@@ -209,8 +223,6 @@ struct isp_device {
209 unsigned int subclk_resources; 223 unsigned int subclk_resources;
210 224
211 struct iommu_domain *domain; 225 struct iommu_domain *domain;
212
213 struct isp_platform_callback platform_cb;
214}; 226};
215 227
216#define v4l2_dev_to_isp_device(dev) \ 228#define v4l2_dev_to_isp_device(dev) \