summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobby Cai <r63905@freescale.com>2014-12-08 10:39:26 +0800
committerRobby Cai <r63905@freescale.com>2014-12-09 16:37:54 +0800
commit5ddfaaaeeb3996e9a219eaacffc725eef1ecfd28 (patch)
tree65b620d94015da7b31f6e9126bf7becb819b2f8e
parent363e746e8e725c3e0f1fdc06da7432f53100e484 (diff)
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)
-rw-r--r--drivers/media/platform/mxc/capture/csi_v4l2_capture.c62
-rw-r--r--drivers/media/platform/mxc/capture/fsl_csi.c117
-rw-r--r--drivers/media/platform/mxc/capture/fsl_csi.h3
-rw-r--r--drivers/media/platform/mxc/capture/mxc_v4l2_capture.h3
4 files changed, 124 insertions, 61 deletions
diff --git a/drivers/media/platform/mxc/capture/csi_v4l2_capture.c b/drivers/media/platform/mxc/capture/csi_v4l2_capture.c
index ca82d5347abd..2ad738595542 100644
--- a/drivers/media/platform/mxc/capture/csi_v4l2_capture.c
+++ b/drivers/media/platform/mxc/capture/csi_v4l2_capture.c
@@ -19,6 +19,7 @@
*
* @ingroup MXC_V4L2_CAPTURE
*/
+#include <linux/busfreq-imx6.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -30,6 +31,7 @@
#include <linux/io.h>
#include <linux/semaphore.h>
#include <linux/pagemap.h>
+#include <linux/pm_runtime.h>
#include <linux/vmalloc.h>
#include <linux/types.h>
#include <linux/fb.h>
@@ -1401,6 +1403,8 @@ static int csi_v4l_open(struct file *file)
goto oops;
if (cam->open_count++ == 0) {
+ pm_runtime_get_sync(&cam->pdev->dev);
+
wait_event_interruptible(cam->power_queue,
cam->low_power == false);
@@ -1470,6 +1474,8 @@ static int csi_v4l_close(struct file *file)
file->private_data = NULL;
vidioc_int_s_power(cam->sensor, 0);
clk_disable_unprepare(sensor->sensor_clk);
+
+ pm_runtime_put_sync_suspend(&cam->pdev->dev);
}
return err;
@@ -2156,6 +2162,8 @@ static void init_camera_struct(cam_data *cam, struct platform_device *pdev)
sprintf(cam->self->name, "csi_v4l2_cap%d", cam->csi);
cam->self->type = v4l2_int_type_master;
cam->self->u.master = &csi_v4l2_master;
+
+ cam->pdev = pdev;
}
/*!
@@ -2223,6 +2231,7 @@ static int csi_v4l2_probe(struct platform_device *pdev)
sg = g_cam->sg;
sg_init_table(sg, 2);
+ pm_runtime_enable(&g_cam->pdev->dev);
out:
return err;
}
@@ -2243,6 +2252,7 @@ static int csi_v4l2_remove(struct platform_device *pdev)
csi_stop_callback(g_cam);
video_unregister_device(g_cam->video_dev);
platform_set_drvdata(pdev, NULL);
+ pm_runtime_disable(&g_cam->pdev->dev);
kfree(g_cam);
g_cam = NULL;
@@ -2251,6 +2261,36 @@ static int csi_v4l2_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_RUNTIME
+static int csi_v4l2_runtime_suspend(struct device *dev)
+{
+ int ret = 0;
+
+ release_bus_freq(BUS_FREQ_HIGH);
+ dev_dbg(dev, "csi v4l2 busfreq high release.\n");
+
+ csi_regulator_disable();
+
+ return ret;
+}
+
+static int csi_v4l2_runtime_resume(struct device *dev)
+{
+ int ret = 0;
+
+ request_bus_freq(BUS_FREQ_HIGH);
+ dev_dbg(dev, "csi v4l2 busfreq high request.\n");
+
+ csi_regulator_enable();
+
+ return ret;
+}
+#else
+#define mxsfb_runtime_suspend NULL
+#define mxsfb_runtime_resume NULL
+#endif
+
+#ifdef CONFIG_PM_SLEEP
/*!
* This function is called to put the sensor in a low power state.
* Refer to the document driver-model/driver.txt in the kernel source tree
@@ -2262,9 +2302,9 @@ static int csi_v4l2_remove(struct platform_device *pdev)
*
* @return The function returns 0 on success and -1 on failure.
*/
-static int csi_v4l2_suspend(struct platform_device *pdev, pm_message_t state)
+static int csi_v4l2_suspend(struct device *dev)
{
- cam_data *cam = platform_get_drvdata(pdev);
+ cam_data *cam = dev_get_drvdata(dev);
pr_debug("In MVC: %s\n", __func__);
@@ -2291,9 +2331,9 @@ static int csi_v4l2_suspend(struct platform_device *pdev, pm_message_t state)
*
* @return The function returns 0 on success and -1 on failure
*/
-static int csi_v4l2_resume(struct platform_device *pdev)
+static int csi_v4l2_resume(struct device *dev)
{
- cam_data *cam = platform_get_drvdata(pdev);
+ cam_data *cam = dev_get_drvdata(dev);
pr_debug("In MVC: %s\n", __func__);
@@ -2310,6 +2350,15 @@ static int csi_v4l2_resume(struct platform_device *pdev)
return 0;
}
+#else
+#define csi_v4l2_suspend NULL
+#define csi_v4l2_resume NULL
+#endif
+
+static const struct dev_pm_ops csi_v4l2_pm_ops = {
+ SET_RUNTIME_PM_OPS(csi_v4l2_runtime_suspend, csi_v4l2_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(csi_v4l2_suspend, csi_v4l2_resume)
+};
/*!
* This structure contains pointers to the power management callback functions.
@@ -2318,13 +2367,10 @@ static struct platform_driver csi_v4l2_driver = {
.driver = {
.name = "csi_v4l2",
.of_match_table = of_match_ptr(imx_csi_v4l2_dt_ids),
+ .pm = &csi_v4l2_pm_ops,
},
.probe = csi_v4l2_probe,
.remove = csi_v4l2_remove,
-#ifdef CONFIG_PM
- .suspend = csi_v4l2_suspend,
- .resume = csi_v4l2_resume,
-#endif
.shutdown = NULL,
};
diff --git a/drivers/media/platform/mxc/capture/fsl_csi.c b/drivers/media/platform/mxc/capture/fsl_csi.c
index cfbab430634a..88fe0200062b 100644
--- a/drivers/media/platform/mxc/capture/fsl_csi.c
+++ b/drivers/media/platform/mxc/capture/fsl_csi.c
@@ -18,11 +18,10 @@
*
* @ingroup CSI
*/
-#include <linux/busfreq-imx6.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/interrupt.h>
@@ -44,12 +43,28 @@ static void *g_callback_data;
static struct clk *disp_axi_clk;
static struct clk *dcic_clk;
static struct clk *csi_clk;
-struct platform_device *csi_pdev;
+static struct regulator *disp_reg;
-void csi_clk_enable(void)
+int csi_regulator_enable(void)
{
- pm_runtime_get_sync(&csi_pdev->dev);
+ int ret = 0;
+ if (disp_reg)
+ ret = regulator_enable(disp_reg);
+
+ return ret;
+}
+EXPORT_SYMBOL(csi_regulator_enable);
+
+void csi_regulator_disable(void)
+{
+ if (disp_reg)
+ regulator_disable(disp_reg);
+}
+EXPORT_SYMBOL(csi_regulator_disable);
+
+void csi_clk_enable(void)
+{
clk_prepare_enable(disp_axi_clk);
clk_prepare_enable(dcic_clk);
clk_prepare_enable(csi_clk);
@@ -61,8 +76,6 @@ void csi_clk_disable(void)
clk_disable_unprepare(csi_clk);
clk_disable_unprepare(dcic_clk);
clk_disable_unprepare(disp_axi_clk);
-
- pm_runtime_put_sync_suspend(&csi_pdev->dev);
}
EXPORT_SYMBOL(csi_clk_disable);
@@ -150,6 +163,39 @@ static void csihw_reset(struct csi_soc *csi)
__raw_writel(CSICR3_RESET_VAL, csi->regbase + CSI_CSICR3);
}
+static void csisw_reset(struct csi_soc *csi)
+{
+ int cr1, cr3, cr18, isr;
+
+ /* Disable csi */
+ cr18 = __raw_readl(csi->regbase + CSI_CSICR18);
+ cr18 &= ~BIT_CSI_ENABLE;
+ __raw_writel(cr18, csi->regbase + CSI_CSICR18);
+
+ /* Clear RX FIFO */
+ cr1 = __raw_readl(csi->regbase + CSI_CSICR1);
+ __raw_writel(cr1 & ~BIT_FCC, csi->regbase + CSI_CSICR1);
+ cr1 = __raw_readl(csi->regbase + CSI_CSICR1);
+ __raw_writel(cr1 | BIT_CLR_RXFIFO, csi->regbase + CSI_CSICR1);
+
+ /* DMA reflash */
+ cr3 = __raw_readl(csi->regbase + CSI_CSICR3);
+ cr3 |= BIT_DMA_REFLASH_RFF | BIT_FRMCNT_RST;
+ __raw_writel(cr3, csi->regbase + CSI_CSICR3);
+
+ msleep(2);
+
+ cr1 = __raw_readl(csi->regbase + CSI_CSICR1);
+ __raw_writel(cr1 | BIT_FCC, csi->regbase + CSI_CSICR1);
+
+ isr = __raw_readl(csi->regbase + CSI_CSISR);
+ __raw_writel(isr, csi->regbase + CSI_CSISR);
+
+ /* Ensable csi */
+ cr18 |= BIT_CSI_ENABLE;
+ __raw_writel(cr18, csi->regbase + CSI_CSICR18);
+}
+
/*!
* csi_init_interface
* Init csi interface
@@ -256,9 +302,10 @@ void csi_enable(cam_data *cam, int arg)
struct csi_soc *csi = &csi_array[cam->csi];
unsigned long cr = __raw_readl(csi->regbase + CSI_CSICR18);
- if (arg == 1)
+ if (arg == 1) {
+ csisw_reset(csi);
cr |= BIT_CSI_ENABLE;
- else
+ } else
cr &= ~BIT_CSI_ENABLE;
__raw_writel(cr, csi->regbase + CSI_CSICR18);
}
@@ -421,8 +468,6 @@ static int csi_probe(struct platform_device *pdev)
struct resource *res;
int id;
- csi_pdev = pdev;
-
id = of_alias_get_id(pdev->dev.of_node, "csi");
if (id < 0) {
dev_dbg(&pdev->dev, "can not get alias id\n");
@@ -470,15 +515,17 @@ static int csi_probe(struct platform_device *pdev)
return PTR_ERR(dcic_clk);
}
- csi->disp_reg = devm_regulator_get(&pdev->dev, "disp");
- if (IS_ERR(csi->disp_reg)) {
- dev_dbg(&pdev->dev, "display regulator is not ready\n");
- csi->disp_reg = NULL;
+ if (disp_reg == NULL) {
+ disp_reg = devm_regulator_get(&pdev->dev, "disp");
+ if (IS_ERR(disp_reg)) {
+ dev_dbg(&pdev->dev, "display regulator is not ready\n");
+ disp_reg = NULL;
+ }
}
platform_set_drvdata(pdev, csi);
- pm_runtime_enable(&pdev->dev);
+ csi_regulator_enable();
csi_clk_enable();
csihw_reset(csi);
@@ -500,41 +547,6 @@ static int csi_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_RUNTIME
-static int csi_runtime_suspend(struct device *dev)
-{
- int ret = 0;
- struct platform_device *pdev = to_platform_device(dev);
- struct csi_soc *csi = platform_get_drvdata(pdev);
-
- release_bus_freq(BUS_FREQ_HIGH);
- dev_info(dev, "csi busfreq high release.\n");
-
- if (csi->disp_reg)
- ret = regulator_disable(csi->disp_reg);
-
- return ret;
-}
-
-static int csi_runtime_resume(struct device *dev)
-{
- int ret = 0;
- struct platform_device *pdev = to_platform_device(dev);
- struct csi_soc *csi = platform_get_drvdata(pdev);
-
- request_bus_freq(BUS_FREQ_HIGH);
- dev_info(dev, "csi busfreq high request.\n");
-
- if (csi->disp_reg)
- ret = regulator_enable(csi->disp_reg);
-
- return ret;
-}
-#else
-#define mxsfb_runtime_suspend NULL
-#define mxsfb_runtime_resume NULL
-#endif
-
#ifdef CONFIG_PM_SLEEP
static int csi_suspend(struct device *dev)
{
@@ -549,11 +561,13 @@ static int csi_resume(struct device *dev)
{
struct csi_soc *csi = dev_get_drvdata(dev);
+ csi_regulator_enable();
csi_clk_enable();
csihw_reset(csi);
csi_init_interface(csi);
csi_dmareq_rff_disable(csi);
csi_clk_disable();
+ csi_regulator_disable();
csi->online = true;
@@ -565,7 +579,6 @@ static int csi_resume(struct device *dev)
#endif
static const struct dev_pm_ops csi_pm_ops = {
- SET_RUNTIME_PM_OPS(csi_runtime_suspend, csi_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(csi_suspend, csi_resume)
};
diff --git a/drivers/media/platform/mxc/capture/fsl_csi.h b/drivers/media/platform/mxc/capture/fsl_csi.h
index ca588af38224..161c27b6929e 100644
--- a/drivers/media/platform/mxc/capture/fsl_csi.h
+++ b/drivers/media/platform/mxc/capture/fsl_csi.h
@@ -196,7 +196,6 @@ struct csi_soc {
bool online;
int irq_nr;
void __iomem *regbase;
- struct regulator *disp_reg;
};
typedef void (*csi_irq_callback_t) (void *data, unsigned long status);
@@ -215,6 +214,8 @@ void csi_deinterlace_enable(cam_data *cam, bool enable);
void csi_tvdec_enable(cam_data *cam, bool enable);
void csi_enable(cam_data *cam, int arg);
void csi_disable_int(cam_data *cam);
+int csi_regulator_enable(void);
+void csi_regulator_disable(void);
void csi_clk_enable(void);
void csi_clk_disable(void);
void csi_dmareq_rff_enable(struct csi_soc *csi);
diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h
index b8ea5b9b2d27..ae56f95cf966 100644
--- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h
+++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h
@@ -231,6 +231,9 @@ typedef struct _cam_data {
struct dma_async_tx_descriptor *txd;
dma_cookie_t cookie;
struct scatterlist sg[2];
+
+ /* pdev from runtime pm */
+ struct platform_device *pdev;
} cam_data;
struct sensor_data {