From fda282455274893ca6dc571896a9347fcbf09740 Mon Sep 17 00:00:00 2001 From: Hu Kejun Date: Wed, 24 Apr 2019 11:26:30 +0800 Subject: [PATCH] media: rockchip: isp1: add pipeline power management Change-Id: I0021482560db18ae50c78e91a590c4ae3d729761 Signed-off-by: Hu Kejun --- .../media/platform/rockchip/isp1/capture.c | 17 +- drivers/media/platform/rockchip/isp1/dev.c | 174 +++++++++++++----- drivers/media/platform/rockchip/isp1/dev.h | 1 + 3 files changed, 139 insertions(+), 53 deletions(-) diff --git a/drivers/media/platform/rockchip/isp1/capture.c b/drivers/media/platform/rockchip/isp1/capture.c index 8b8af2a4223d..16454d8e40a3 100644 --- a/drivers/media/platform/rockchip/isp1/capture.c +++ b/drivers/media/platform/rockchip/isp1/capture.c @@ -1715,12 +1715,18 @@ int rkisp1_fh_open(struct file *filp) { struct rkisp1_stream *stream = video_drvdata(filp); struct rkisp1_device *dev = stream->ispdev; + struct video_device *vdev = &stream->vnode.vdev; int ret; ret = v4l2_fh_open(filp); if (!ret) { if (atomic_inc_return(&dev->open_cnt) == 1) rkisp1_dma_attach_device(dev); + + ret = dev->pipe.pm_use(&vdev->entity, 1); + if (ret < 0) + v4l2_err(&dev->v4l2_dev, + "set pipeline power failed %d\n", ret); } return ret; @@ -1730,12 +1736,19 @@ int rkisp1_fop_release(struct file *file) { struct rkisp1_stream *stream = video_drvdata(file); struct rkisp1_device *dev = stream->ispdev; + struct video_device *vdev = &stream->vnode.vdev; int ret; ret = vb2_fop_release(file); + if (!ret) { + ret = dev->pipe.pm_use(&vdev->entity, 0); + if (ret < 0) + v4l2_err(&dev->v4l2_dev, + "set pipeline power failed %d\n", ret); - if (atomic_dec_return(&dev->open_cnt) == 0) - rkisp1_dma_detach_device(dev); + if (atomic_dec_return(&dev->open_cnt) == 0) + rkisp1_dma_detach_device(dev); + } return ret; } diff --git a/drivers/media/platform/rockchip/isp1/dev.c b/drivers/media/platform/rockchip/isp1/dev.c index dd9634ebfcde..29805442b027 100644 --- a/drivers/media/platform/rockchip/isp1/dev.c +++ b/drivers/media/platform/rockchip/isp1/dev.c @@ -127,47 +127,6 @@ static int __isp_pipeline_prepare(struct rkisp1_pipeline *p, return 0; } -static int __subdev_set_power(struct v4l2_subdev *sd, int on) -{ - int ret; - - if (!sd) - return -ENXIO; - - ret = v4l2_subdev_call(sd, core, s_power, on); - - return ret != -ENOIOCTLCMD ? ret : 0; -} - -static int __isp_pipeline_s_power(struct rkisp1_pipeline *p, bool on) -{ - struct rkisp1_device *dev = container_of(p, struct rkisp1_device, pipe); - int i, ret; - - if (on) { - __subdev_set_power(&dev->isp_sdev.sd, true); - - for (i = p->num_subdevs - 1; i >= 0; --i) { - ret = __subdev_set_power(p->subdevs[i], true); - if (ret < 0 && ret != -ENXIO) - goto err_power_off; - } - } else { - for (i = 0; i < p->num_subdevs; ++i) - __subdev_set_power(p->subdevs[i], false); - - __subdev_set_power(&dev->isp_sdev.sd, false); - } - - return 0; - -err_power_off: - for (++i; i < p->num_subdevs; ++i) - __subdev_set_power(p->subdevs[i], false); - __subdev_set_power(&dev->isp_sdev.sd, true); - return ret; -} - static int __isp_pipeline_s_isp_clk(struct rkisp1_pipeline *p) { struct rkisp1_device *dev = container_of(p, struct rkisp1_device, pipe); @@ -243,22 +202,14 @@ static int rkisp1_pipeline_open(struct rkisp1_pipeline *p, if (ret < 0) return ret; - ret = __isp_pipeline_s_power(p, 1); - if (ret < 0) - return ret; - return 0; } static int rkisp1_pipeline_close(struct rkisp1_pipeline *p) { - int ret; + atomic_dec(&p->power_cnt); - if (atomic_dec_return(&p->power_cnt) > 0) - return 0; - ret = __isp_pipeline_s_power(p, 0); - - return ret == -ENXIO ? 0 : ret; + return 0; } /* @@ -305,6 +256,125 @@ err_stream_off: return ret; } +static int rkisp1_pipeline_pm_use_count(struct media_entity *entity) +{ + struct media_entity_graph graph; + int use = 0; + + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + use += entity->use_count; + } + + return use; +} + +static int rkisp1_pipeline_pm_power_one(struct media_entity *entity, int change) +{ + struct v4l2_subdev *subdev; + int ret; + + subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV + ? media_entity_to_v4l2_subdev(entity) : NULL; + + if (entity->use_count == 0 && change > 0 && subdev) { + ret = v4l2_subdev_call(subdev, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + } + + entity->use_count += change; + WARN_ON(entity->use_count < 0); + + if (entity->use_count == 0 && change < 0 && subdev) + v4l2_subdev_call(subdev, core, s_power, 0); + + return 0; +} + +static int rkisp1_pipeline_pm_power(struct media_entity *entity, int change) +{ + struct media_entity_graph graph; + struct media_entity *first = entity; + int ret = 0; + + if (!change) + return 0; + + media_entity_graph_walk_start(&graph, entity); + + while (!ret && (entity = media_entity_graph_walk_next(&graph))) + if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + ret = rkisp1_pipeline_pm_power_one(entity, change); + + if (!ret) + return 0; + + media_entity_graph_walk_start(&graph, first); + + while ((first = media_entity_graph_walk_next(&graph)) && first != entity) + if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE) + rkisp1_pipeline_pm_power_one(first, -change); + + return ret; +} + +static int rkisp1_pipeline_pm_use(struct media_entity *entity, int use) +{ + int change = use ? 1 : -1; + int ret; + + mutex_lock(&entity->parent->graph_mutex); + + /* Apply use count to node. */ + entity->use_count += change; + WARN_ON(entity->use_count < 0); + + /* Apply power change to connected non-nodes. */ + ret = rkisp1_pipeline_pm_power(entity, change); + if (ret < 0) + entity->use_count -= change; + + mutex_unlock(&entity->parent->graph_mutex); + + return ret; +} + +static int rkisp1_pipeline_link_notify(struct media_link *link, u32 flags, + unsigned int notification) +{ + struct media_entity *source = link->source->entity; + struct media_entity *sink = link->sink->entity; + int source_use = rkisp1_pipeline_pm_use_count(source); + int sink_use = rkisp1_pipeline_pm_use_count(sink); + int ret; + + if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && + !(flags & MEDIA_LNK_FL_ENABLED)) { + /* Powering off entities is assumed to never fail. */ + rkisp1_pipeline_pm_power(source, -sink_use); + rkisp1_pipeline_pm_power(sink, -source_use); + return 0; + } + + if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && + (flags & MEDIA_LNK_FL_ENABLED)) { + ret = rkisp1_pipeline_pm_power(source, sink_use); + if (ret < 0) + return ret; + + ret = rkisp1_pipeline_pm_power(sink, source_use); + if (ret < 0) + rkisp1_pipeline_pm_power(source, -sink_use); + + return ret; + } + + return 0; +} + /***************************** media controller *******************************/ /* See http://opensource.rock-chips.com/wiki_Rockchip-isp1 for Topology */ @@ -1100,6 +1170,7 @@ static int rkisp1_plat_probe(struct platform_device *pdev) isp_dev->pipe.open = rkisp1_pipeline_open; isp_dev->pipe.close = rkisp1_pipeline_close; isp_dev->pipe.set_stream = rkisp1_pipeline_set_stream; + isp_dev->pipe.pm_use = rkisp1_pipeline_pm_use; rkisp1_stream_init(isp_dev, RKISP1_STREAM_SP); rkisp1_stream_init(isp_dev, RKISP1_STREAM_MP); @@ -1121,6 +1192,7 @@ static int rkisp1_plat_probe(struct platform_device *pdev) return ret; } + isp_dev->media_dev.link_notify = rkisp1_pipeline_link_notify; ret = media_device_register(&isp_dev->media_dev); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to register media device: %d\n", diff --git a/drivers/media/platform/rockchip/isp1/dev.h b/drivers/media/platform/rockchip/isp1/dev.h index d4baceed384a..80e925abe865 100644 --- a/drivers/media/platform/rockchip/isp1/dev.h +++ b/drivers/media/platform/rockchip/isp1/dev.h @@ -105,6 +105,7 @@ struct rkisp1_pipeline { struct media_entity *me, bool prepare); int (*close)(struct rkisp1_pipeline *p); int (*set_stream)(struct rkisp1_pipeline *p, bool on); + int (*pm_use)(struct media_entity *entity, int use); }; /*