diff mbox

[3/5] v4l: Add generic pipeline power management code

Message ID 1453902658-29783-4-git-send-email-sakari.ailus@iki.fi (mailing list archive)
State New, archived
Headers show

Commit Message

Sakari Ailus Jan. 27, 2016, 1:50 p.m. UTC
When the Media controller framework was merged, it was decided not to add
pipeline power management code for it was not seen generic. As a result, a
number of drivers have copied the same piece of code, with same bugfixes
done to them at different points of time (or not at all).

Add these functions to V4L2. Their use is optional for drivers.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/media/v4l2-core/v4l2-common.c | 174 ++++++++++++++++++++++++++++++++++
 include/media/v4l2-common.h           |  37 ++++++++
 2 files changed, 211 insertions(+)

Comments

Hans Verkuil Jan. 27, 2016, 2:05 p.m. UTC | #1
On 01/27/16 14:50, Sakari Ailus wrote:
> When the Media controller framework was merged, it was decided not to add
> pipeline power management code for it was not seen generic. As a result, a
> number of drivers have copied the same piece of code, with same bugfixes
> done to them at different points of time (or not at all).
> 
> Add these functions to V4L2. Their use is optional for drivers.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> ---
>  drivers/media/v4l2-core/v4l2-common.c | 174 ++++++++++++++++++++++++++++++++++
>  include/media/v4l2-common.h           |  37 ++++++++

I think I would prefer to see a v4l2-mc.c source rather than adding it to common.
Since this is specific to both v4l2 and MC it only has to be compiled
if both config options are set.

Besides, I think we need to move more common mc code to such a file.

Regards,

	Hans

>  2 files changed, 211 insertions(+)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
> index 5b80850..0e063a9 100644
> --- a/drivers/media/v4l2-core/v4l2-common.c
> +++ b/drivers/media/v4l2-core/v4l2-common.c
> @@ -405,3 +405,177 @@ void v4l2_get_timestamp(struct timeval *tv)
>  	tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
>  }
>  EXPORT_SYMBOL_GPL(v4l2_get_timestamp);
> +
> +/* -----------------------------------------------------------------------------
> + * Pipeline power management
> + *
> + * Entities must be powered up when part of a pipeline that contains at least
> + * one open video device node.
> + *
> + * To achieve this use the entity use_count field to track the number of users.
> + * For entities corresponding to video device nodes the use_count field stores
> + * the users count of the node. For entities corresponding to subdevs the
> + * use_count field stores the total number of users of all video device nodes
> + * in the pipeline.
> + *
> + * The v4l2_pipeline_pm_use() function must be called in the open() and
> + * close() handlers of video device nodes. It increments or decrements the use
> + * count of all subdev entities in the pipeline.
> + *
> + * To react to link management on powered pipelines, the link setup notification
> + * callback updates the use count of all entities in the source and sink sides
> + * of the link.
> + */
> +
> +/*
> + * pipeline_pm_use_count - Count the number of users of a pipeline
> + * @entity: The entity
> + *
> + * Return the total number of users of all video device nodes in the pipeline.
> + */
> +static int 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 (is_media_entity_v4l2_io(entity))
> +			use += entity->use_count;
> +	}
> +
> +	return use;
> +}
> +
> +/*
> + * pipeline_pm_power_one - Apply power change to an entity
> + * @entity: The entity
> + * @change: Use count change
> + *
> + * Change the entity use count by @change. If the entity is a subdev update its
> + * power state by calling the core::s_power operation when the use count goes
> + * from 0 to != 0 or from != 0 to 0.
> + *
> + * Return 0 on success or a negative error code on failure.
> + */
> +static int pipeline_pm_power_one(struct media_entity *entity, int change)
> +{
> +	struct v4l2_subdev *subdev;
> +	int ret;
> +
> +	subdev = is_media_entity_v4l2_subdev(entity)
> +	       ? media_entity_to_v4l2_subdev(entity) : NULL;
> +
> +	if (entity->use_count == 0 && change > 0 && subdev != NULL) {
> +		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 != NULL)
> +		v4l2_subdev_call(subdev, core, s_power, 0);
> +
> +	return 0;
> +}
> +
> +/*
> + * pipeline_pm_power - Apply power change to all entities in a pipeline
> + * @entity: The entity
> + * @change: Use count change
> + *
> + * Walk the pipeline to update the use count and the power state of all non-node
> + * entities.
> + *
> + * Return 0 on success or a negative error code on failure.
> + */
> +static int 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 (is_media_entity_v4l2_subdev(entity))
> +			ret = pipeline_pm_power_one(entity, change);
> +
> +	if (!ret)
> +		return ret;
> +
> +	media_entity_graph_walk_start(graph, first);
> +
> +	while ((first = media_entity_graph_walk_next(graph))
> +	       && first != entity)
> +		if (is_media_entity_v4l2_subdev(first))
> +			pipeline_pm_power_one(first, -change);
> +
> +	return ret;
> +}
> +
> +int v4l2_pipeline_pm_use(struct media_entity *entity, int use)
> +{
> +	struct media_device *mdev = entity->graph_obj.mdev;
> +	int change = use ? 1 : -1;
> +	int ret;
> +
> +	mutex_lock(&mdev->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 = pipeline_pm_power(entity, change, &mdev->pm_count_walk);
> +	if (ret < 0)
> +		entity->use_count -= change;
> +
> +	mutex_unlock(&mdev->graph_mutex);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_use);
> +
> +int v4l2_pipeline_link_notify(struct media_link *link, u32 flags,
> +			      unsigned int notification)
> +{
> +	struct media_entity_graph *graph = &link->graph_obj.mdev->pm_count_walk;
> +	struct media_entity *source = link->source->entity;
> +	struct media_entity *sink = link->sink->entity;
> +	int source_use;
> +	int sink_use;
> +	int ret = 0;
> +
> +	source_use = pipeline_pm_use_count(source, graph);
> +	sink_use = pipeline_pm_use_count(sink, graph);
> +
> +	if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
> +	    !(flags & MEDIA_LNK_FL_ENABLED)) {
> +		/* Powering off entities is assumed to never fail. */
> +		pipeline_pm_power(source, -sink_use, graph);
> +		pipeline_pm_power(sink, -source_use, graph);
> +		return 0;
> +	}
> +
> +	if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
> +		(flags & MEDIA_LNK_FL_ENABLED)) {
> +
> +		ret = pipeline_pm_power(source, sink_use, graph);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = pipeline_pm_power(sink, source_use, graph);
> +		if (ret < 0)
> +			pipeline_pm_power(source, -sink_use, graph);
> +	}
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_pipeline_link_notify);
> diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
> index 1cc0c5b..0c23940 100644
> --- a/include/media/v4l2-common.h
> +++ b/include/media/v4l2-common.h
> @@ -189,4 +189,41 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
>  
>  void v4l2_get_timestamp(struct timeval *tv);
>  
> +/**
> + * v4l2_pipeline_pm_use - Update the use count of an entity
> + * @entity: The entity
> + * @use: Use (1) or stop using (0) the entity
> + *
> + * Update the use count of all entities in the pipeline and power entities on or
> + * off accordingly.
> + *
> + * This function is intended to be called in video node open (use ==
> + * 1) and release (use == 0). It uses struct media_entity.use_count to
> + * track the power status. The use of this function should be paired
> + * with v4l2_pipeline_link_notify().
> + *
> + * Return 0 on success or a negative error code on failure. Powering entities
> + * off is assumed to never fail. No failure can occur when the use parameter is
> + * set to 0.
> + */
> +int v4l2_pipeline_pm_use(struct media_entity *entity, int use);
> +
> +
> +/**
> + * v4l2_pipeline_link_notify - Link management notification callback
> + * @link: The link
> + * @flags: New link flags that will be applied
> + * @notification: The link's state change notification type (MEDIA_DEV_NOTIFY_*)
> + *
> + * React to link management on powered pipelines by updating the use count of
> + * all entities in the source and sink sides of the link. Entities are powered
> + * on or off accordingly. The use of this function should be paired
> + * with v4l2_pipeline_pm_use().
> + *
> + * Return 0 on success or a negative error code on failure. Powering entities
> + * off is assumed to never fail. This function will not fail for disconnection
> + * events.
> + */
> +int v4l2_pipeline_link_notify(struct media_link *link, u32 flags,
> +			      unsigned int notification);
>  #endif /* V4L2_COMMON_H_ */
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
kernel test robot Jan. 27, 2016, 2:18 p.m. UTC | #2
Hi Sakari,

[auto build test ERROR on linuxtv-media/master]
[also build test ERROR on v4.5-rc1 next-20160127]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Sakari-Ailus/Unify-MC-graph-power-management-code/20160127-215417
base:   git://linuxtv.org/media_tree.git master
config: x86_64-randconfig-x013-01270835 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All error/warnings (new ones prefixed by >>):

   In file included from include/linux/list.h:8:0,
                    from include/linux/module.h:9,
                    from drivers/media/v4l2-core/v4l2-common.c:47:
   drivers/media/v4l2-core/v4l2-common.c: In function 'pipeline_pm_power_one':
>> include/linux/kernel.h:841:27: error: 'struct v4l2_subdev' has no member named 'entity'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                              ^
>> include/media/v4l2-subdev.h:740:2: note: in expansion of macro 'container_of'
     container_of(ent, struct v4l2_subdev, entity)
     ^
>> drivers/media/v4l2-core/v4l2-common.c:468:11: note: in expansion of macro 'media_entity_to_v4l2_subdev'
            ? media_entity_to_v4l2_subdev(entity) : NULL;
              ^
   include/linux/kernel.h:841:48: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
>> include/media/v4l2-subdev.h:740:2: note: in expansion of macro 'container_of'
     container_of(ent, struct v4l2_subdev, entity)
     ^
>> drivers/media/v4l2-core/v4l2-common.c:468:11: note: in expansion of macro 'media_entity_to_v4l2_subdev'
            ? media_entity_to_v4l2_subdev(entity) : NULL;
              ^
   In file included from include/linux/compiler.h:56:0,
                    from include/uapi/linux/stddef.h:1,
                    from include/linux/stddef.h:4,
                    from include/uapi/linux/posix_types.h:4,
                    from include/uapi/linux/types.h:13,
                    from include/linux/types.h:5,
                    from include/linux/list.h:4,
                    from include/linux/module.h:9,
                    from drivers/media/v4l2-core/v4l2-common.c:47:
>> include/linux/compiler-gcc.h:158:2: error: 'struct v4l2_subdev' has no member named 'entity'
     __builtin_offsetof(a, b)
     ^
   include/linux/stddef.h:16:32: note: in expansion of macro '__compiler_offsetof'
    #define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE, MEMBER)
                                   ^
   include/linux/kernel.h:842:29: note: in expansion of macro 'offsetof'
     (type *)( (char *)__mptr - offsetof(type,member) );})
                                ^
>> include/media/v4l2-subdev.h:740:2: note: in expansion of macro 'container_of'
     container_of(ent, struct v4l2_subdev, entity)
     ^
>> drivers/media/v4l2-core/v4l2-common.c:468:11: note: in expansion of macro 'media_entity_to_v4l2_subdev'
            ? media_entity_to_v4l2_subdev(entity) : NULL;
              ^
   drivers/media/v4l2-core/v4l2-common.c: In function 'v4l2_pipeline_link_notify':
>> drivers/media/v4l2-core/v4l2-common.c:559:22: error: 'MEDIA_DEV_NOTIFY_POST_LINK_CH' undeclared (first use in this function)
     if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
                         ^
   drivers/media/v4l2-core/v4l2-common.c:559:22: note: each undeclared identifier is reported only once for each function it appears in
>> drivers/media/v4l2-core/v4l2-common.c:567:22: error: 'MEDIA_DEV_NOTIFY_PRE_LINK_CH' undeclared (first use in this function)
     if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
                         ^

vim +841 include/linux/kernel.h

^1da177e Linus Torvalds 2005-04-16  835   * @ptr:	the pointer to the member.
^1da177e Linus Torvalds 2005-04-16  836   * @type:	the type of the container struct this is embedded in.
^1da177e Linus Torvalds 2005-04-16  837   * @member:	the name of the member within the struct.
^1da177e Linus Torvalds 2005-04-16  838   *
^1da177e Linus Torvalds 2005-04-16  839   */
^1da177e Linus Torvalds 2005-04-16  840  #define container_of(ptr, type, member) ({			\
^1da177e Linus Torvalds 2005-04-16 @841  	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
^1da177e Linus Torvalds 2005-04-16  842  	(type *)( (char *)__mptr - offsetof(type,member) );})
^1da177e Linus Torvalds 2005-04-16  843  
b9d4f426 Arnaud Lacombe 2011-07-25  844  /* Rebuild everything on CONFIG_FTRACE_MCOUNT_RECORD */

:::::: The code at line 841 was first introduced by commit
:::::: 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 Linux-2.6.12-rc2

:::::: TO: Linus Torvalds <torvalds@ppc970.osdl.org>
:::::: CC: Linus Torvalds <torvalds@ppc970.osdl.org>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Sakari Ailus Jan. 27, 2016, 2:39 p.m. UTC | #3
On Wed, Jan 27, 2016 at 03:05:16PM +0100, Hans Verkuil wrote:
> On 01/27/16 14:50, Sakari Ailus wrote:
> > When the Media controller framework was merged, it was decided not to add
> > pipeline power management code for it was not seen generic. As a result, a
> > number of drivers have copied the same piece of code, with same bugfixes
> > done to them at different points of time (or not at all).
> > 
> > Add these functions to V4L2. Their use is optional for drivers.
> > 
> > Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > ---
> >  drivers/media/v4l2-core/v4l2-common.c | 174 ++++++++++++++++++++++++++++++++++
> >  include/media/v4l2-common.h           |  37 ++++++++
> 
> I think I would prefer to see a v4l2-mc.c source rather than adding it to common.
> Since this is specific to both v4l2 and MC it only has to be compiled
> if both config options are set.
> 
> Besides, I think we need to move more common mc code to such a file.

Ok. I'll move these to a separate file.
kernel test robot Jan. 27, 2016, 3:16 p.m. UTC | #4
Hi Sakari,

[auto build test WARNING on linuxtv-media/master]
[also build test WARNING on v4.5-rc1 next-20160127]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Sakari-Ailus/Unify-MC-graph-power-management-code/20160127-215417
base:   git://linuxtv.org/media_tree.git master
config: x86_64-randconfig-s1-01272247 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All warnings (new ones prefixed by >>):

   In file included from include/linux/list.h:8:0,
                    from include/linux/module.h:9,
                    from drivers/media/v4l2-core/v4l2-common.c:47:
   drivers/media/v4l2-core/v4l2-common.c: In function 'pipeline_pm_power_one':
   include/linux/kernel.h:841:27: error: 'struct v4l2_subdev' has no member named 'entity'
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                              ^
   include/media/v4l2-subdev.h:740:2: note: in expansion of macro 'container_of'
     container_of(ent, struct v4l2_subdev, entity)
     ^
   drivers/media/v4l2-core/v4l2-common.c:468:11: note: in expansion of macro 'media_entity_to_v4l2_subdev'
            ? media_entity_to_v4l2_subdev(entity) : NULL;
              ^
   include/linux/kernel.h:841:48: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
   include/media/v4l2-subdev.h:740:2: note: in expansion of macro 'container_of'
     container_of(ent, struct v4l2_subdev, entity)
     ^
   drivers/media/v4l2-core/v4l2-common.c:468:11: note: in expansion of macro 'media_entity_to_v4l2_subdev'
            ? media_entity_to_v4l2_subdev(entity) : NULL;
              ^
   In file included from include/linux/compiler.h:56:0,
                    from include/uapi/linux/stddef.h:1,
                    from include/linux/stddef.h:4,
                    from include/uapi/linux/posix_types.h:4,
                    from include/uapi/linux/types.h:13,
                    from include/linux/types.h:5,
                    from include/linux/list.h:4,
                    from include/linux/module.h:9,
                    from drivers/media/v4l2-core/v4l2-common.c:47:
   include/linux/compiler-gcc.h:158:2: error: 'struct v4l2_subdev' has no member named 'entity'
     __builtin_offsetof(a, b)
     ^
   include/linux/stddef.h:16:32: note: in expansion of macro '__compiler_offsetof'
    #define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE, MEMBER)
                                   ^
   include/linux/kernel.h:842:29: note: in expansion of macro 'offsetof'
     (type *)( (char *)__mptr - offsetof(type,member) );})
                                ^
   include/media/v4l2-subdev.h:740:2: note: in expansion of macro 'container_of'
     container_of(ent, struct v4l2_subdev, entity)
     ^
   drivers/media/v4l2-core/v4l2-common.c:468:11: note: in expansion of macro 'media_entity_to_v4l2_subdev'
            ? media_entity_to_v4l2_subdev(entity) : NULL;
              ^
   In file included from include/uapi/linux/stddef.h:1:0,
                    from include/linux/stddef.h:4,
                    from include/uapi/linux/posix_types.h:4,
                    from include/uapi/linux/types.h:13,
                    from include/linux/types.h:5,
                    from include/linux/list.h:4,
                    from include/linux/module.h:9,
                    from drivers/media/v4l2-core/v4l2-common.c:47:
   drivers/media/v4l2-core/v4l2-common.c: In function 'v4l2_pipeline_link_notify':
   drivers/media/v4l2-core/v4l2-common.c:559:22: error: 'MEDIA_DEV_NOTIFY_POST_LINK_CH' undeclared (first use in this function)
     if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
                         ^
   include/linux/compiler.h:147:28: note: in definition of macro '__trace_if'
     if (__builtin_constant_p((cond)) ? !!(cond) :   \
                               ^
>> drivers/media/v4l2-core/v4l2-common.c:559:2: note: in expansion of macro 'if'
     if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
     ^
   drivers/media/v4l2-core/v4l2-common.c:559:22: note: each undeclared identifier is reported only once for each function it appears in
     if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
                         ^
   include/linux/compiler.h:147:28: note: in definition of macro '__trace_if'
     if (__builtin_constant_p((cond)) ? !!(cond) :   \
                               ^
>> drivers/media/v4l2-core/v4l2-common.c:559:2: note: in expansion of macro 'if'
     if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
     ^
   drivers/media/v4l2-core/v4l2-common.c:567:22: error: 'MEDIA_DEV_NOTIFY_PRE_LINK_CH' undeclared (first use in this function)
     if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
                         ^
   include/linux/compiler.h:147:28: note: in definition of macro '__trace_if'
     if (__builtin_constant_p((cond)) ? !!(cond) :   \
                               ^
   drivers/media/v4l2-core/v4l2-common.c:567:2: note: in expansion of macro 'if'
     if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
     ^

vim +/if +559 drivers/media/v4l2-core/v4l2-common.c

   543	}
   544	EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_use);
   545	
   546	int v4l2_pipeline_link_notify(struct media_link *link, u32 flags,
   547				      unsigned int notification)
   548	{
   549		struct media_entity_graph *graph = &link->graph_obj.mdev->pm_count_walk;
   550		struct media_entity *source = link->source->entity;
   551		struct media_entity *sink = link->sink->entity;
   552		int source_use;
   553		int sink_use;
   554		int ret = 0;
   555	
   556		source_use = pipeline_pm_use_count(source, graph);
   557		sink_use = pipeline_pm_use_count(sink, graph);
   558	
 > 559		if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
   560		    !(flags & MEDIA_LNK_FL_ENABLED)) {
   561			/* Powering off entities is assumed to never fail. */
   562			pipeline_pm_power(source, -sink_use, graph);
   563			pipeline_pm_power(sink, -source_use, graph);
   564			return 0;
   565		}
   566	
   567		if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
diff mbox

Patch

diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index 5b80850..0e063a9 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -405,3 +405,177 @@  void v4l2_get_timestamp(struct timeval *tv)
 	tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
 }
 EXPORT_SYMBOL_GPL(v4l2_get_timestamp);
+
+/* -----------------------------------------------------------------------------
+ * Pipeline power management
+ *
+ * Entities must be powered up when part of a pipeline that contains at least
+ * one open video device node.
+ *
+ * To achieve this use the entity use_count field to track the number of users.
+ * For entities corresponding to video device nodes the use_count field stores
+ * the users count of the node. For entities corresponding to subdevs the
+ * use_count field stores the total number of users of all video device nodes
+ * in the pipeline.
+ *
+ * The v4l2_pipeline_pm_use() function must be called in the open() and
+ * close() handlers of video device nodes. It increments or decrements the use
+ * count of all subdev entities in the pipeline.
+ *
+ * To react to link management on powered pipelines, the link setup notification
+ * callback updates the use count of all entities in the source and sink sides
+ * of the link.
+ */
+
+/*
+ * pipeline_pm_use_count - Count the number of users of a pipeline
+ * @entity: The entity
+ *
+ * Return the total number of users of all video device nodes in the pipeline.
+ */
+static int 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 (is_media_entity_v4l2_io(entity))
+			use += entity->use_count;
+	}
+
+	return use;
+}
+
+/*
+ * pipeline_pm_power_one - Apply power change to an entity
+ * @entity: The entity
+ * @change: Use count change
+ *
+ * Change the entity use count by @change. If the entity is a subdev update its
+ * power state by calling the core::s_power operation when the use count goes
+ * from 0 to != 0 or from != 0 to 0.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+static int pipeline_pm_power_one(struct media_entity *entity, int change)
+{
+	struct v4l2_subdev *subdev;
+	int ret;
+
+	subdev = is_media_entity_v4l2_subdev(entity)
+	       ? media_entity_to_v4l2_subdev(entity) : NULL;
+
+	if (entity->use_count == 0 && change > 0 && subdev != NULL) {
+		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 != NULL)
+		v4l2_subdev_call(subdev, core, s_power, 0);
+
+	return 0;
+}
+
+/*
+ * pipeline_pm_power - Apply power change to all entities in a pipeline
+ * @entity: The entity
+ * @change: Use count change
+ *
+ * Walk the pipeline to update the use count and the power state of all non-node
+ * entities.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+static int 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 (is_media_entity_v4l2_subdev(entity))
+			ret = pipeline_pm_power_one(entity, change);
+
+	if (!ret)
+		return ret;
+
+	media_entity_graph_walk_start(graph, first);
+
+	while ((first = media_entity_graph_walk_next(graph))
+	       && first != entity)
+		if (is_media_entity_v4l2_subdev(first))
+			pipeline_pm_power_one(first, -change);
+
+	return ret;
+}
+
+int v4l2_pipeline_pm_use(struct media_entity *entity, int use)
+{
+	struct media_device *mdev = entity->graph_obj.mdev;
+	int change = use ? 1 : -1;
+	int ret;
+
+	mutex_lock(&mdev->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 = pipeline_pm_power(entity, change, &mdev->pm_count_walk);
+	if (ret < 0)
+		entity->use_count -= change;
+
+	mutex_unlock(&mdev->graph_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_use);
+
+int v4l2_pipeline_link_notify(struct media_link *link, u32 flags,
+			      unsigned int notification)
+{
+	struct media_entity_graph *graph = &link->graph_obj.mdev->pm_count_walk;
+	struct media_entity *source = link->source->entity;
+	struct media_entity *sink = link->sink->entity;
+	int source_use;
+	int sink_use;
+	int ret = 0;
+
+	source_use = pipeline_pm_use_count(source, graph);
+	sink_use = pipeline_pm_use_count(sink, graph);
+
+	if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
+	    !(flags & MEDIA_LNK_FL_ENABLED)) {
+		/* Powering off entities is assumed to never fail. */
+		pipeline_pm_power(source, -sink_use, graph);
+		pipeline_pm_power(sink, -source_use, graph);
+		return 0;
+	}
+
+	if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
+		(flags & MEDIA_LNK_FL_ENABLED)) {
+
+		ret = pipeline_pm_power(source, sink_use, graph);
+		if (ret < 0)
+			return ret;
+
+		ret = pipeline_pm_power(sink, source_use, graph);
+		if (ret < 0)
+			pipeline_pm_power(source, -sink_use, graph);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_pipeline_link_notify);
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index 1cc0c5b..0c23940 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -189,4 +189,41 @@  const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
 
 void v4l2_get_timestamp(struct timeval *tv);
 
+/**
+ * v4l2_pipeline_pm_use - Update the use count of an entity
+ * @entity: The entity
+ * @use: Use (1) or stop using (0) the entity
+ *
+ * Update the use count of all entities in the pipeline and power entities on or
+ * off accordingly.
+ *
+ * This function is intended to be called in video node open (use ==
+ * 1) and release (use == 0). It uses struct media_entity.use_count to
+ * track the power status. The use of this function should be paired
+ * with v4l2_pipeline_link_notify().
+ *
+ * Return 0 on success or a negative error code on failure. Powering entities
+ * off is assumed to never fail. No failure can occur when the use parameter is
+ * set to 0.
+ */
+int v4l2_pipeline_pm_use(struct media_entity *entity, int use);
+
+
+/**
+ * v4l2_pipeline_link_notify - Link management notification callback
+ * @link: The link
+ * @flags: New link flags that will be applied
+ * @notification: The link's state change notification type (MEDIA_DEV_NOTIFY_*)
+ *
+ * React to link management on powered pipelines by updating the use count of
+ * all entities in the source and sink sides of the link. Entities are powered
+ * on or off accordingly. The use of this function should be paired
+ * with v4l2_pipeline_pm_use().
+ *
+ * Return 0 on success or a negative error code on failure. Powering entities
+ * off is assumed to never fail. This function will not fail for disconnection
+ * events.
+ */
+int v4l2_pipeline_link_notify(struct media_link *link, u32 flags,
+			      unsigned int notification);
 #endif /* V4L2_COMMON_H_ */