v4l2: VIN driver use pm
authorchanghuang.liang <changhuang.liang@starfivetech.com>
Mon, 26 Sep 2022 09:12:04 +0000 (17:12 +0800)
committermason.huo <mason.huo@starfivetech.com>
Thu, 27 Oct 2022 05:55:40 +0000 (13:55 +0800)
VIN driver use pm save power, delete turn on pmu multiple times
and modify isp clk and reset after turn on pmu.

Signed-off-by: changhuang.liang <changhuang.liang@starfivetech.com>
drivers/media/platform/starfive/v4l2_driver/stf_isp.c
drivers/media/platform/starfive/v4l2_driver/stf_vin.c
drivers/media/platform/starfive/v4l2_driver/stf_vin.h
drivers/media/platform/starfive/v4l2_driver/stf_vin_hw_ops.c
drivers/media/platform/starfive/v4l2_driver/stfcamss.c

index 6fb75d4..52c0833 100644 (file)
@@ -400,25 +400,16 @@ free_ctrls:
 static int isp_set_power(struct v4l2_subdev *sd, int on)
 {
        struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
-       struct stf_vin2_dev *vin_dev = isp_dev->stfcamss->vin_dev;
 
        st_debug(ST_ISP, "%s, %d\n", __func__, __LINE__);
        mutex_lock(&isp_dev->power_lock);
        if (on) {
-               if (isp_dev->power_count == 0) {
-                       /* Needs to enable vin clock before access ISP. */
-                       vin_dev->hw_ops->vin_top_clk_init(vin_dev);
-                       vin_dev->hw_ops->vin_clk_enable(vin_dev);
-                       isp_dev->hw_ops->isp_clk_enable(isp_dev);
-                       if (!user_config_isp)
-                               isp_dev->hw_ops->isp_config_set(isp_dev);
-               }
+               if (isp_dev->power_count == 0)
+                       st_debug(ST_ISP, "turn on isp\n");
                isp_dev->power_count++;
        } else {
                if (isp_dev->power_count == 0)
                        goto exit;
-               if (isp_dev->power_count == 1)
-                       isp_dev->hw_ops->isp_clk_disable(isp_dev);
                isp_dev->power_count--;
        }
 exit:
@@ -473,6 +464,9 @@ static int isp_set_stream(struct v4l2_subdev *sd, int enable)
        mutex_lock(&isp_dev->stream_lock);
        if (enable) {
                if (isp_dev->stream_count == 0) {
+                       isp_dev->hw_ops->isp_clk_enable(isp_dev);
+                       if (!user_config_isp)
+                               isp_dev->hw_ops->isp_config_set(isp_dev);
                        interface_type = isp_get_interface_type(&sd->entity);
                        if (interface_type < 0) {
                                st_err(ST_ISP, "%s, pipeline not config\n", __func__);
@@ -488,8 +482,10 @@ static int isp_set_stream(struct v4l2_subdev *sd, int enable)
        } else {
                if (isp_dev->stream_count == 0)
                        goto exit;
-               if (isp_dev->stream_count == 1)
+               if (isp_dev->stream_count == 1) {
                        isp_dev->hw_ops->isp_stream_set(isp_dev, enable);
+                       isp_dev->hw_ops->isp_clk_disable(isp_dev);
+               }
                isp_dev->stream_count--;
        }
        src_ch.type = V4L2_EVENT_SOURCE_CHANGE,
index 7625aad..ca60869 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
 
 #include "stfcamss.h"
 
@@ -218,23 +219,6 @@ int stf_vin_subdev_init(struct stfcamss *stfcamss)
        vin_dev->hw_ops->vin_wr_irq_enable(vin_dev, 1);
        vin_dev->hw_ops->vin_wr_irq_enable(vin_dev, 0);
 
-       /* Reset device */
-       /*Do not configure the CLK before powering on the device,
-        *add vin_power_on() to vin_set_power() 2021 1111
-        */
-       ret = vin_dev->hw_ops->vin_top_clk_init(vin_dev);
-       if (ret) {
-               st_err(ST_VIN, "Failed to reset device\n");
-               goto out;
-       }
-
-       // /* set the sysctl config */
-       // ret = vin_dev->hw_ops->vin_config_set(vin_dev);
-       // if (ret) {
-       //      st_err(ST_VIN, "Failed to config device\n");
-       //      goto out;
-       // }
-
        mutex_init(&vin_dev->power_lock);
        vin_dev->power_count = 0;
 
@@ -283,6 +267,7 @@ static int vin_set_power(struct v4l2_subdev *sd, int on)
 {
        struct vin_line *line = v4l2_get_subdevdata(sd);
        struct stf_vin2_dev *vin_dev = line_to_vin2_dev(line);
+       struct stfcamss *stfcamss = vin_dev->stfcamss;
 
        mutex_lock(&line->power_lock);
        if (on) {
@@ -303,7 +288,7 @@ exit_line:
        mutex_lock(&vin_dev->power_lock);
        if (on) {
                if (vin_dev->power_count == 0) {
-                       //vin_dev->hw_ops->vin_top_clk_init(vin_dev);
+                       pm_runtime_get_sync(stfcamss->dev);
                        vin_dev->hw_ops->vin_clk_enable(vin_dev);
                        vin_dev->hw_ops->vin_config_set(vin_dev);
                }
@@ -316,7 +301,7 @@ exit_line:
                }
                if (vin_dev->power_count == 1) {
                        vin_dev->hw_ops->vin_clk_disable(vin_dev);
-                       //vin_dev->hw_ops->vin_top_clk_deinit(vin_dev);
+                       pm_runtime_put_sync(stfcamss->dev);
                }
                vin_dev->power_count--;
        }
index b751afc..5625621 100644 (file)
@@ -87,8 +87,6 @@ struct vin_line {
 struct stf_vin2_dev;
 
 struct vin_hw_ops {
-       int (*vin_top_clk_init)(struct stf_vin2_dev *vin_dev);
-       int (*vin_top_clk_deinit)(struct stf_vin2_dev *vin_dev);
        int (*vin_clk_enable)(struct stf_vin2_dev *vin_dev);
        int (*vin_clk_disable)(struct stf_vin2_dev *vin_dev);
        int (*vin_config_set)(struct stf_vin2_dev *vin_dev);
index 9b06550..aaec27c 100644 (file)
@@ -7,8 +7,6 @@
 #include <media/v4l2-async.h>
 #include <media/v4l2-fwnode.h>
 #include <media/v4l2-subdev.h>
-#include <linux/clk-provider.h>
-#include <linux/pm_runtime.h>
 
 static void vin_intr_clear(void __iomem *sysctrl_base)
 {
@@ -213,47 +211,6 @@ static irqreturn_t stf_vin_isp_irq_csiline_handler(int irq, void *priv)
        return IRQ_HANDLED;
 }
 
-static int stf_vin_top_clk_init(struct stf_vin2_dev *vin_dev)
-{
-       struct stfcamss *stfcamss = vin_dev->stfcamss;
-       int ret;
-
-       pm_runtime_enable(stfcamss->dev);
-       ret = pm_runtime_get_sync(stfcamss->dev);
-       if (ret < 0) {
-               dev_err(stfcamss->dev,
-                       "vin_clk_init: failed to get pm runtime: %d\n", ret);
-               return ret;
-       }
-
-       if (!__clk_is_enabled(stfcamss->sys_clk[STFCLK_NOC_BUS_CLK_ISP_AXI].clk))
-               clk_prepare_enable(stfcamss->sys_clk[STFCLK_NOC_BUS_CLK_ISP_AXI].clk);
-       else
-               st_warn(ST_VIN, "noc_bus_clk_isp_axi already enable\n");
-
-       clk_prepare_enable(stfcamss->sys_clk[STFCLK_ISPCORE_2X].clk);
-       clk_prepare_enable(stfcamss->sys_clk[STFCLK_ISP_AXI].clk);
-       reset_control_deassert(stfcamss->sys_rst[STFRST_ISP_TOP_N].rstc);
-       reset_control_deassert(stfcamss->sys_rst[STFRST_ISP_TOP_AXI].rstc);
-
-       return 0;
-}
-
-static int stf_vin_top_clk_deinit(struct stf_vin2_dev *vin_dev)
-{
-       struct stfcamss *stfcamss = vin_dev->stfcamss;
-
-       reset_control_assert(stfcamss->sys_rst[STFRST_ISP_TOP_AXI].rstc);
-       reset_control_assert(stfcamss->sys_rst[STFRST_ISP_TOP_N].rstc);
-       clk_disable_unprepare(stfcamss->sys_clk[STFCLK_ISP_AXI].clk);
-       clk_disable_unprepare(stfcamss->sys_clk[STFCLK_ISPCORE_2X].clk);
-
-       pm_runtime_put_sync(stfcamss->dev);
-       pm_runtime_disable(stfcamss->dev);
-
-       return 0;
-}
-
 static int stf_vin_clk_enable(struct stf_vin2_dev *vin_dev)
 {
        struct stfcamss *stfcamss = vin_dev->stfcamss;
@@ -451,8 +408,6 @@ void dump_vin_reg(void *__iomem regbase)
 }
 
 struct vin_hw_ops vin_ops = {
-       .vin_top_clk_init      = stf_vin_top_clk_init,
-       .vin_top_clk_deinit    = stf_vin_top_clk_deinit,
        .vin_clk_enable        = stf_vin_clk_enable,
        .vin_clk_disable       = stf_vin_clk_disable,
        .vin_config_set        = stf_vin_config_set,
index f699766..f937285 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/uaccess.h>
 #include <linux/mfd/syscon.h>
+#include <linux/clk-provider.h>
 
 #include <linux/videodev2.h>
 
@@ -1011,6 +1012,8 @@ static int stfcamss_probe(struct platform_device *pdev)
                goto err_cam;
        }
 
+       pm_runtime_enable(dev);
+
        stfcamss->nclks = ARRAY_SIZE(stfcamss_clocks);
        stfcamss->sys_clk = stfcamss_clocks;
 
@@ -1175,6 +1178,7 @@ static int stfcamss_remove(struct platform_device *pdev)
        stfcamss_unregister_subdevices(stfcamss);
        v4l2_device_unregister(&stfcamss->v4l2_dev);
        media_device_cleanup(&stfcamss->media_dev);
+       pm_runtime_disable(&pdev->dev);
 
        kfree(stfcamss);
 
@@ -1188,11 +1192,148 @@ static const struct of_device_id stfcamss_of_match[] = {
 
 MODULE_DEVICE_TABLE(of, stfcamss_of_match);
 
+#ifdef CONFIG_PM_SLEEP
+static int stfcamss_suspend(struct device *dev)
+{
+       struct stfcamss *stfcamss = dev_get_drvdata(dev);
+       struct stf_vin2_dev *vin_dev = stfcamss->vin_dev;
+       struct media_entity *entity;
+       struct media_pad *pad;
+       struct v4l2_subdev *subdev;
+       struct stfcamss_video *video;
+       struct video_device *vdev;
+       int i = 0;
+
+       for (i = 0; i < VIN_LINE_MAX; i++) {
+               if (vin_dev->line[i].stream_count) {
+                       vin_dev->line[i].stream_count ++;
+                       video = &vin_dev->line[i].video_out;
+                       vdev = &vin_dev->line[i].video_out.vdev;
+                       entity = &vdev->entity;
+                       while (1) {
+                               pad = &entity->pads[0];
+                               if (!(pad->flags & MEDIA_PAD_FL_SINK))
+                                       break;
+
+                               pad = media_entity_remote_pad(pad);
+                               if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+                                       break;
+
+                               entity = pad->entity;
+                               subdev = media_entity_to_v4l2_subdev(entity);
+
+                               v4l2_subdev_call(subdev, video, s_stream, 0);
+                       }
+                       media_pipeline_stop(&vdev->entity);
+                       video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
+                       v4l2_pipeline_pm_put(&vdev->entity);
+               }
+       }
+
+       return pm_runtime_force_suspend(dev);
+}
+
+static int stfcamss_resume(struct device *dev)
+{
+       struct stfcamss *stfcamss = dev_get_drvdata(dev);
+       struct stf_vin2_dev *vin_dev = stfcamss->vin_dev;
+       struct media_entity *entity;
+       struct media_pad *pad;
+       struct v4l2_subdev *subdev;
+       struct stfcamss_video *video;
+       struct video_device *vdev;
+       int i = 0;
+       int ret = 0;
+
+       pm_runtime_force_resume(dev);
+
+       for (i = 0; i < VIN_LINE_MAX; i++) {
+               if (vin_dev->line[i].stream_count) {
+                       vin_dev->line[i].stream_count--;
+                       video = &vin_dev->line[i].video_out;
+                       vdev = &vin_dev->line[i].video_out.vdev;
+
+                       ret = v4l2_pipeline_pm_get(&vdev->entity);
+                       if (ret < 0)
+                               goto err;
+
+                       ret = media_pipeline_start(&vdev->entity, &video->stfcamss->pipe);
+                       if (ret < 0)
+                               goto err_pm_put;
+
+                       entity = &vdev->entity;
+                       while (1) {
+                               pad = &entity->pads[0];
+                               if (!(pad->flags & MEDIA_PAD_FL_SINK))
+                                       break;
+
+                               pad = media_entity_remote_pad(pad);
+                               if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+                                       break;
+
+                               entity = pad->entity;
+                               subdev = media_entity_to_v4l2_subdev(entity);
+
+                               ret = v4l2_subdev_call(subdev, video, s_stream, 1);
+                               if (ret < 0 && ret != -ENOIOCTLCMD)
+                                       goto err_pipeline_stop;
+                       }
+               }
+       }
+
+       return 0;
+
+err_pipeline_stop:
+       media_pipeline_stop(&vdev->entity);
+err_pm_put:
+       v4l2_pipeline_pm_put(&vdev->entity);
+err:
+       return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM
+static int stfcamss_runtime_suspend(struct device *dev)
+{
+       struct stfcamss *stfcamss = dev_get_drvdata(dev);
+
+       if (!__clk_is_enabled(stfcamss->sys_clk[STFCLK_NOC_BUS_CLK_ISP_AXI].clk))
+               clk_prepare_enable(stfcamss->sys_clk[STFCLK_NOC_BUS_CLK_ISP_AXI].clk);
+       else
+               st_warn(ST_VIN, "noc_bus_clk_isp_axi already enable\n");
+
+       reset_control_assert(stfcamss->sys_rst[STFRST_ISP_TOP_AXI].rstc);
+       reset_control_assert(stfcamss->sys_rst[STFRST_ISP_TOP_N].rstc);
+       clk_disable_unprepare(stfcamss->sys_clk[STFCLK_ISP_AXI].clk);
+       clk_disable_unprepare(stfcamss->sys_clk[STFCLK_ISPCORE_2X].clk);
+
+       return 0;
+}
+
+static int stfcamss_runtime_resume(struct device *dev)
+{
+       struct stfcamss *stfcamss = dev_get_drvdata(dev);
+
+       clk_prepare_enable(stfcamss->sys_clk[STFCLK_ISPCORE_2X].clk);
+       clk_prepare_enable(stfcamss->sys_clk[STFCLK_ISP_AXI].clk);
+       reset_control_deassert(stfcamss->sys_rst[STFRST_ISP_TOP_N].rstc);
+       reset_control_deassert(stfcamss->sys_rst[STFRST_ISP_TOP_AXI].rstc);
+
+       return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops stfcamss_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(stfcamss_suspend, stfcamss_resume)
+       SET_RUNTIME_PM_OPS(stfcamss_runtime_suspend, stfcamss_runtime_resume, NULL)
+};
+
 static struct platform_driver stfcamss_driver = {
        .probe = stfcamss_probe,
        .remove = stfcamss_remove,
        .driver = {
                .name = DRV_NAME,
+               .pm = &stfcamss_pm_ops,
                .of_match_table = of_match_ptr(stfcamss_of_match),
        },
 };