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:
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__);
} 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,
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
#include "stfcamss.h"
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;
{
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) {
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);
}
}
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--;
}
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);
#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)
{
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;
}
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,
#include <linux/dma-mapping.h>
#include <linux/uaccess.h>
#include <linux/mfd/syscon.h>
+#include <linux/clk-provider.h>
#include <linux/videodev2.h>
goto err_cam;
}
+ pm_runtime_enable(dev);
+
stfcamss->nclks = ARRAY_SIZE(stfcamss_clocks);
stfcamss->sys_clk = stfcamss_clocks;
stfcamss_unregister_subdevices(stfcamss);
v4l2_device_unregister(&stfcamss->v4l2_dev);
media_device_cleanup(&stfcamss->media_dev);
+ pm_runtime_disable(&pdev->dev);
kfree(stfcamss);
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),
},
};