Commit 5ddfaaaeeb3996e9a219eaacffc725eef1ecfd28

Authored by Robby Cai
1 parent 363e746e8e

MLK-9917 imx6sx: fix csi modules prevent system entering low power mode

The reproduce step:
$ echo 8 > /proc/sys/kernel/printk
$ echo 1 > /sys/class/graphics/fb0/blank
$ ifconfig eth0 down
$ ifconfig eth1 down
$ cat /sys/kernel/debug/clk/osc/pll2_bus/pll2_pfd2_396m/periph2_pre/periph2/mmdc_podf/clk_rate
396000000

The expected result should be '24000000' and following message.

Bus freq set to 24000000 start...
Bus freq set to 24000000 done!

This patch did the following to fix it.
- Move the pm runtime handling into csi v4l2 driver, request high bus frequency
  when the device opens and release high bus frequency when device closes.
- Add new api csisw_reset() to mainly do DMA reflash otherwise potentially
  meet garbage data when CSI starts to work on imx6sl.

Signed-off-by: Robby Cai <r63905@freescale.com>
(cherry picked from commit 857a52585c92cad8d851751f859e8e23ea4ae250)

Showing 4 changed files with 124 additions and 61 deletions Side-by-side Diff

drivers/media/platform/mxc/capture/csi_v4l2_capture.c
... ... @@ -19,6 +19,7 @@
19 19 *
20 20 * @ingroup MXC_V4L2_CAPTURE
21 21 */
  22 +#include <linux/busfreq-imx6.h>
22 23 #include <linux/version.h>
23 24 #include <linux/module.h>
24 25 #include <linux/init.h>
... ... @@ -30,6 +31,7 @@
30 31 #include <linux/io.h>
31 32 #include <linux/semaphore.h>
32 33 #include <linux/pagemap.h>
  34 +#include <linux/pm_runtime.h>
33 35 #include <linux/vmalloc.h>
34 36 #include <linux/types.h>
35 37 #include <linux/fb.h>
... ... @@ -1401,6 +1403,8 @@
1401 1403 goto oops;
1402 1404  
1403 1405 if (cam->open_count++ == 0) {
  1406 + pm_runtime_get_sync(&cam->pdev->dev);
  1407 +
1404 1408 wait_event_interruptible(cam->power_queue,
1405 1409 cam->low_power == false);
1406 1410  
... ... @@ -1470,6 +1474,8 @@
1470 1474 file->private_data = NULL;
1471 1475 vidioc_int_s_power(cam->sensor, 0);
1472 1476 clk_disable_unprepare(sensor->sensor_clk);
  1477 +
  1478 + pm_runtime_put_sync_suspend(&cam->pdev->dev);
1473 1479 }
1474 1480  
1475 1481 return err;
... ... @@ -2156,6 +2162,8 @@
2156 2162 sprintf(cam->self->name, "csi_v4l2_cap%d", cam->csi);
2157 2163 cam->self->type = v4l2_int_type_master;
2158 2164 cam->self->u.master = &csi_v4l2_master;
  2165 +
  2166 + cam->pdev = pdev;
2159 2167 }
2160 2168  
2161 2169 /*!
... ... @@ -2223,6 +2231,7 @@
2223 2231 sg = g_cam->sg;
2224 2232 sg_init_table(sg, 2);
2225 2233  
  2234 + pm_runtime_enable(&g_cam->pdev->dev);
2226 2235 out:
2227 2236 return err;
2228 2237 }
... ... @@ -2243,6 +2252,7 @@
2243 2252 csi_stop_callback(g_cam);
2244 2253 video_unregister_device(g_cam->video_dev);
2245 2254 platform_set_drvdata(pdev, NULL);
  2255 + pm_runtime_disable(&g_cam->pdev->dev);
2246 2256  
2247 2257 kfree(g_cam);
2248 2258 g_cam = NULL;
... ... @@ -2251,6 +2261,36 @@
2251 2261 return 0;
2252 2262 }
2253 2263  
  2264 +#ifdef CONFIG_PM_RUNTIME
  2265 +static int csi_v4l2_runtime_suspend(struct device *dev)
  2266 +{
  2267 + int ret = 0;
  2268 +
  2269 + release_bus_freq(BUS_FREQ_HIGH);
  2270 + dev_dbg(dev, "csi v4l2 busfreq high release.\n");
  2271 +
  2272 + csi_regulator_disable();
  2273 +
  2274 + return ret;
  2275 +}
  2276 +
  2277 +static int csi_v4l2_runtime_resume(struct device *dev)
  2278 +{
  2279 + int ret = 0;
  2280 +
  2281 + request_bus_freq(BUS_FREQ_HIGH);
  2282 + dev_dbg(dev, "csi v4l2 busfreq high request.\n");
  2283 +
  2284 + csi_regulator_enable();
  2285 +
  2286 + return ret;
  2287 +}
  2288 +#else
  2289 +#define mxsfb_runtime_suspend NULL
  2290 +#define mxsfb_runtime_resume NULL
  2291 +#endif
  2292 +
  2293 +#ifdef CONFIG_PM_SLEEP
2254 2294 /*!
2255 2295 * This function is called to put the sensor in a low power state.
2256 2296 * Refer to the document driver-model/driver.txt in the kernel source tree
2257 2297  
... ... @@ -2262,9 +2302,9 @@
2262 2302 *
2263 2303 * @return The function returns 0 on success and -1 on failure.
2264 2304 */
2265   -static int csi_v4l2_suspend(struct platform_device *pdev, pm_message_t state)
  2305 +static int csi_v4l2_suspend(struct device *dev)
2266 2306 {
2267   - cam_data *cam = platform_get_drvdata(pdev);
  2307 + cam_data *cam = dev_get_drvdata(dev);
2268 2308  
2269 2309 pr_debug("In MVC: %s\n", __func__);
2270 2310  
2271 2311  
... ... @@ -2291,9 +2331,9 @@
2291 2331 *
2292 2332 * @return The function returns 0 on success and -1 on failure
2293 2333 */
2294   -static int csi_v4l2_resume(struct platform_device *pdev)
  2334 +static int csi_v4l2_resume(struct device *dev)
2295 2335 {
2296   - cam_data *cam = platform_get_drvdata(pdev);
  2336 + cam_data *cam = dev_get_drvdata(dev);
2297 2337  
2298 2338 pr_debug("In MVC: %s\n", __func__);
2299 2339  
2300 2340  
... ... @@ -2310,7 +2350,16 @@
2310 2350  
2311 2351 return 0;
2312 2352 }
  2353 +#else
  2354 +#define csi_v4l2_suspend NULL
  2355 +#define csi_v4l2_resume NULL
  2356 +#endif
2313 2357  
  2358 +static const struct dev_pm_ops csi_v4l2_pm_ops = {
  2359 + SET_RUNTIME_PM_OPS(csi_v4l2_runtime_suspend, csi_v4l2_runtime_resume, NULL)
  2360 + SET_SYSTEM_SLEEP_PM_OPS(csi_v4l2_suspend, csi_v4l2_resume)
  2361 +};
  2362 +
2314 2363 /*!
2315 2364 * This structure contains pointers to the power management callback functions.
2316 2365 */
2317 2366  
... ... @@ -2318,13 +2367,10 @@
2318 2367 .driver = {
2319 2368 .name = "csi_v4l2",
2320 2369 .of_match_table = of_match_ptr(imx_csi_v4l2_dt_ids),
  2370 + .pm = &csi_v4l2_pm_ops,
2321 2371 },
2322 2372 .probe = csi_v4l2_probe,
2323 2373 .remove = csi_v4l2_remove,
2324   -#ifdef CONFIG_PM
2325   - .suspend = csi_v4l2_suspend,
2326   - .resume = csi_v4l2_resume,
2327   -#endif
2328 2374 .shutdown = NULL,
2329 2375 };
2330 2376  
drivers/media/platform/mxc/capture/fsl_csi.c
... ... @@ -18,11 +18,10 @@
18 18 *
19 19 * @ingroup CSI
20 20 */
21   -#include <linux/busfreq-imx6.h>
22 21 #include <linux/types.h>
23 22 #include <linux/init.h>
24 23 #include <linux/platform_device.h>
25   -#include <linux/pm_runtime.h>
  24 +#include <linux/delay.h>
26 25 #include <linux/device.h>
27 26 #include <linux/err.h>
28 27 #include <linux/interrupt.h>
29 28  
30 29  
31 30  
... ... @@ -44,12 +43,28 @@
44 43 static struct clk *disp_axi_clk;
45 44 static struct clk *dcic_clk;
46 45 static struct clk *csi_clk;
47   -struct platform_device *csi_pdev;
  46 +static struct regulator *disp_reg;
48 47  
49   -void csi_clk_enable(void)
  48 +int csi_regulator_enable(void)
50 49 {
51   - pm_runtime_get_sync(&csi_pdev->dev);
  50 + int ret = 0;
52 51  
  52 + if (disp_reg)
  53 + ret = regulator_enable(disp_reg);
  54 +
  55 + return ret;
  56 +}
  57 +EXPORT_SYMBOL(csi_regulator_enable);
  58 +
  59 +void csi_regulator_disable(void)
  60 +{
  61 + if (disp_reg)
  62 + regulator_disable(disp_reg);
  63 +}
  64 +EXPORT_SYMBOL(csi_regulator_disable);
  65 +
  66 +void csi_clk_enable(void)
  67 +{
53 68 clk_prepare_enable(disp_axi_clk);
54 69 clk_prepare_enable(dcic_clk);
55 70 clk_prepare_enable(csi_clk);
... ... @@ -61,8 +76,6 @@
61 76 clk_disable_unprepare(csi_clk);
62 77 clk_disable_unprepare(dcic_clk);
63 78 clk_disable_unprepare(disp_axi_clk);
64   -
65   - pm_runtime_put_sync_suspend(&csi_pdev->dev);
66 79 }
67 80 EXPORT_SYMBOL(csi_clk_disable);
68 81  
... ... @@ -150,6 +163,39 @@
150 163 __raw_writel(CSICR3_RESET_VAL, csi->regbase + CSI_CSICR3);
151 164 }
152 165  
  166 +static void csisw_reset(struct csi_soc *csi)
  167 +{
  168 + int cr1, cr3, cr18, isr;
  169 +
  170 + /* Disable csi */
  171 + cr18 = __raw_readl(csi->regbase + CSI_CSICR18);
  172 + cr18 &= ~BIT_CSI_ENABLE;
  173 + __raw_writel(cr18, csi->regbase + CSI_CSICR18);
  174 +
  175 + /* Clear RX FIFO */
  176 + cr1 = __raw_readl(csi->regbase + CSI_CSICR1);
  177 + __raw_writel(cr1 & ~BIT_FCC, csi->regbase + CSI_CSICR1);
  178 + cr1 = __raw_readl(csi->regbase + CSI_CSICR1);
  179 + __raw_writel(cr1 | BIT_CLR_RXFIFO, csi->regbase + CSI_CSICR1);
  180 +
  181 + /* DMA reflash */
  182 + cr3 = __raw_readl(csi->regbase + CSI_CSICR3);
  183 + cr3 |= BIT_DMA_REFLASH_RFF | BIT_FRMCNT_RST;
  184 + __raw_writel(cr3, csi->regbase + CSI_CSICR3);
  185 +
  186 + msleep(2);
  187 +
  188 + cr1 = __raw_readl(csi->regbase + CSI_CSICR1);
  189 + __raw_writel(cr1 | BIT_FCC, csi->regbase + CSI_CSICR1);
  190 +
  191 + isr = __raw_readl(csi->regbase + CSI_CSISR);
  192 + __raw_writel(isr, csi->regbase + CSI_CSISR);
  193 +
  194 + /* Ensable csi */
  195 + cr18 |= BIT_CSI_ENABLE;
  196 + __raw_writel(cr18, csi->regbase + CSI_CSICR18);
  197 +}
  198 +
153 199 /*!
154 200 * csi_init_interface
155 201 * Init csi interface
156 202  
... ... @@ -256,9 +302,10 @@
256 302 struct csi_soc *csi = &csi_array[cam->csi];
257 303 unsigned long cr = __raw_readl(csi->regbase + CSI_CSICR18);
258 304  
259   - if (arg == 1)
  305 + if (arg == 1) {
  306 + csisw_reset(csi);
260 307 cr |= BIT_CSI_ENABLE;
261   - else
  308 + } else
262 309 cr &= ~BIT_CSI_ENABLE;
263 310 __raw_writel(cr, csi->regbase + CSI_CSICR18);
264 311 }
... ... @@ -421,8 +468,6 @@
421 468 struct resource *res;
422 469 int id;
423 470  
424   - csi_pdev = pdev;
425   -
426 471 id = of_alias_get_id(pdev->dev.of_node, "csi");
427 472 if (id < 0) {
428 473 dev_dbg(&pdev->dev, "can not get alias id\n");
429 474  
... ... @@ -470,15 +515,17 @@
470 515 return PTR_ERR(dcic_clk);
471 516 }
472 517  
473   - csi->disp_reg = devm_regulator_get(&pdev->dev, "disp");
474   - if (IS_ERR(csi->disp_reg)) {
475   - dev_dbg(&pdev->dev, "display regulator is not ready\n");
476   - csi->disp_reg = NULL;
  518 + if (disp_reg == NULL) {
  519 + disp_reg = devm_regulator_get(&pdev->dev, "disp");
  520 + if (IS_ERR(disp_reg)) {
  521 + dev_dbg(&pdev->dev, "display regulator is not ready\n");
  522 + disp_reg = NULL;
  523 + }
477 524 }
478 525  
479 526 platform_set_drvdata(pdev, csi);
480 527  
481   - pm_runtime_enable(&pdev->dev);
  528 + csi_regulator_enable();
482 529  
483 530 csi_clk_enable();
484 531 csihw_reset(csi);
... ... @@ -500,41 +547,6 @@
500 547 return 0;
501 548 }
502 549  
503   -#ifdef CONFIG_PM_RUNTIME
504   -static int csi_runtime_suspend(struct device *dev)
505   -{
506   - int ret = 0;
507   - struct platform_device *pdev = to_platform_device(dev);
508   - struct csi_soc *csi = platform_get_drvdata(pdev);
509   -
510   - release_bus_freq(BUS_FREQ_HIGH);
511   - dev_info(dev, "csi busfreq high release.\n");
512   -
513   - if (csi->disp_reg)
514   - ret = regulator_disable(csi->disp_reg);
515   -
516   - return ret;
517   -}
518   -
519   -static int csi_runtime_resume(struct device *dev)
520   -{
521   - int ret = 0;
522   - struct platform_device *pdev = to_platform_device(dev);
523   - struct csi_soc *csi = platform_get_drvdata(pdev);
524   -
525   - request_bus_freq(BUS_FREQ_HIGH);
526   - dev_info(dev, "csi busfreq high request.\n");
527   -
528   - if (csi->disp_reg)
529   - ret = regulator_enable(csi->disp_reg);
530   -
531   - return ret;
532   -}
533   -#else
534   -#define mxsfb_runtime_suspend NULL
535   -#define mxsfb_runtime_resume NULL
536   -#endif
537   -
538 550 #ifdef CONFIG_PM_SLEEP
539 551 static int csi_suspend(struct device *dev)
540 552 {
541 553  
... ... @@ -549,11 +561,13 @@
549 561 {
550 562 struct csi_soc *csi = dev_get_drvdata(dev);
551 563  
  564 + csi_regulator_enable();
552 565 csi_clk_enable();
553 566 csihw_reset(csi);
554 567 csi_init_interface(csi);
555 568 csi_dmareq_rff_disable(csi);
556 569 csi_clk_disable();
  570 + csi_regulator_disable();
557 571  
558 572 csi->online = true;
559 573  
... ... @@ -565,7 +579,6 @@
565 579 #endif
566 580  
567 581 static const struct dev_pm_ops csi_pm_ops = {
568   - SET_RUNTIME_PM_OPS(csi_runtime_suspend, csi_runtime_resume, NULL)
569 582 SET_SYSTEM_SLEEP_PM_OPS(csi_suspend, csi_resume)
570 583 };
571 584  
drivers/media/platform/mxc/capture/fsl_csi.h
... ... @@ -196,7 +196,6 @@
196 196 bool online;
197 197 int irq_nr;
198 198 void __iomem *regbase;
199   - struct regulator *disp_reg;
200 199 };
201 200  
202 201 typedef void (*csi_irq_callback_t) (void *data, unsigned long status);
... ... @@ -215,6 +214,8 @@
215 214 void csi_tvdec_enable(cam_data *cam, bool enable);
216 215 void csi_enable(cam_data *cam, int arg);
217 216 void csi_disable_int(cam_data *cam);
  217 +int csi_regulator_enable(void);
  218 +void csi_regulator_disable(void);
218 219 void csi_clk_enable(void);
219 220 void csi_clk_disable(void);
220 221 void csi_dmareq_rff_enable(struct csi_soc *csi);
drivers/media/platform/mxc/capture/mxc_v4l2_capture.h
... ... @@ -231,6 +231,9 @@
231 231 struct dma_async_tx_descriptor *txd;
232 232 dma_cookie_t cookie;
233 233 struct scatterlist sg[2];
  234 +
  235 + /* pdev from runtime pm */
  236 + struct platform_device *pdev;
234 237 } cam_data;
235 238  
236 239 struct sensor_data {