2018-01-12 01:26:22 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2016-02-05 12:02:43 +03:00
/*
* Media Controller ancillary functions
*
2016-06-14 21:17:40 +03:00
* Copyright ( c ) 2016 Mauro Carvalho Chehab < mchehab @ kernel . org >
2016-02-12 02:41:25 +03:00
* Copyright ( C ) 2016 Shuah Khan < shuahkh @ osg . samsung . com >
2016-02-21 19:25:09 +03:00
* Copyright ( C ) 2006 - 2010 Nokia Corporation
* Copyright ( c ) 2016 Intel Corporation .
2016-02-05 12:02:43 +03:00
*/
# include <linux/module.h>
2016-02-21 19:25:09 +03:00
# include <linux/pci.h>
# include <linux/usb.h>
2016-02-12 02:41:25 +03:00
# include <media/media-device.h>
2016-02-21 19:25:09 +03:00
# include <media/media-entity.h>
2016-02-12 02:41:25 +03:00
# include <media/v4l2-fh.h>
2016-02-05 12:02:43 +03:00
# include <media/v4l2-mc.h>
2016-02-21 19:25:09 +03:00
# include <media/v4l2-subdev.h>
2016-02-12 02:41:25 +03:00
# include <media/videobuf2-core.h>
2016-02-05 12:02:43 +03:00
int v4l2_mc_create_media_graph ( struct media_device * mdev )
{
struct media_entity * entity ;
2016-02-12 13:07:59 +03:00
struct media_entity * if_vid = NULL , * if_aud = NULL ;
2016-04-03 15:03:49 +03:00
struct media_entity * tuner = NULL , * decoder = NULL ;
2016-02-05 12:02:43 +03:00
struct media_entity * io_v4l = NULL , * io_vbi = NULL , * io_swradio = NULL ;
bool is_webcam = false ;
u32 flags ;
2018-07-31 16:22:40 +03:00
int ret , pad_sink , pad_source ;
2016-02-05 12:02:43 +03:00
if ( ! mdev )
return 0 ;
media_device_for_each_entity ( entity , mdev ) {
switch ( entity - > function ) {
case MEDIA_ENT_F_IF_VID_DECODER :
if_vid = entity ;
break ;
case MEDIA_ENT_F_IF_AUD_DECODER :
if_aud = entity ;
break ;
case MEDIA_ENT_F_TUNER :
tuner = entity ;
break ;
case MEDIA_ENT_F_ATV_DECODER :
decoder = entity ;
break ;
case MEDIA_ENT_F_IO_V4L :
io_v4l = entity ;
break ;
case MEDIA_ENT_F_IO_VBI :
io_vbi = entity ;
break ;
case MEDIA_ENT_F_IO_SWRADIO :
io_swradio = entity ;
break ;
case MEDIA_ENT_F_CAM_SENSOR :
is_webcam = true ;
break ;
}
}
/* It should have at least one I/O entity */
2018-09-15 07:00:31 +03:00
if ( ! io_v4l & & ! io_vbi & & ! io_swradio ) {
dev_warn ( mdev - > dev , " Didn't find any I/O entity \n " ) ;
2016-02-05 12:02:43 +03:00
return - EINVAL ;
2018-09-15 07:00:31 +03:00
}
2016-02-05 12:02:43 +03:00
/*
* Here , webcams are modelled on a very simple way : the sensor is
* connected directly to the I / O entity . All dirty details , like
* scaler and crop HW are hidden . While such mapping is not enough
* for mc - centric hardware , it is enough for v4l2 interface centric
* PC - consumer ' s hardware .
*/
if ( is_webcam ) {
2018-09-15 07:00:31 +03:00
if ( ! io_v4l ) {
dev_warn ( mdev - > dev , " Didn't find a MEDIA_ENT_F_IO_V4L \n " ) ;
2016-02-05 12:02:43 +03:00
return - EINVAL ;
2018-09-15 07:00:31 +03:00
}
2016-02-05 12:02:43 +03:00
media_device_for_each_entity ( entity , mdev ) {
if ( entity - > function ! = MEDIA_ENT_F_CAM_SENSOR )
continue ;
ret = media_create_pad_link ( entity , 0 ,
io_v4l , 0 ,
MEDIA_LNK_FL_ENABLED ) ;
2018-09-15 07:00:31 +03:00
if ( ret ) {
dev_warn ( mdev - > dev , " Failed to create a sensor link \n " ) ;
2016-02-05 12:02:43 +03:00
return ret ;
2018-09-15 07:00:31 +03:00
}
2016-02-05 12:02:43 +03:00
}
if ( ! decoder )
return 0 ;
}
/* The device isn't a webcam. So, it should have a decoder */
2018-09-15 07:00:31 +03:00
if ( ! decoder ) {
dev_warn ( mdev - > dev , " Decoder not found \n " ) ;
2016-02-05 12:02:43 +03:00
return - EINVAL ;
2018-09-15 07:00:31 +03:00
}
2016-02-05 12:02:43 +03:00
/* Link the tuner and IF video output pads */
if ( tuner ) {
if ( if_vid ) {
2018-07-31 16:22:40 +03:00
pad_source = media_get_pad_index ( tuner , false ,
PAD_SIGNAL_ANALOG ) ;
pad_sink = media_get_pad_index ( if_vid , true ,
PAD_SIGNAL_ANALOG ) ;
2018-09-15 07:00:31 +03:00
if ( pad_source < 0 | | pad_sink < 0 ) {
dev_warn ( mdev - > dev , " Couldn't get tuner and/or PLL pad(s): (%d, %d) \n " ,
pad_source , pad_sink ) ;
2018-07-31 16:22:40 +03:00
return - EINVAL ;
2018-09-15 07:00:31 +03:00
}
2018-07-31 16:22:40 +03:00
ret = media_create_pad_link ( tuner , pad_source ,
if_vid , pad_sink ,
2016-02-05 12:02:43 +03:00
MEDIA_LNK_FL_ENABLED ) ;
2018-09-15 07:00:31 +03:00
if ( ret ) {
dev_warn ( mdev - > dev , " Couldn't create tuner->PLL link) \n " ) ;
2016-02-05 12:02:43 +03:00
return ret ;
2018-09-15 07:00:31 +03:00
}
2018-07-31 16:22:40 +03:00
pad_source = media_get_pad_index ( if_vid , false ,
PAD_SIGNAL_ANALOG ) ;
pad_sink = media_get_pad_index ( decoder , true ,
PAD_SIGNAL_ANALOG ) ;
2018-09-15 07:00:31 +03:00
if ( pad_source < 0 | | pad_sink < 0 ) {
dev_warn ( mdev - > dev , " get decoder and/or PLL pad(s): (%d, %d) \n " ,
pad_source , pad_sink ) ;
2018-07-31 16:22:40 +03:00
return - EINVAL ;
2018-09-15 07:00:31 +03:00
}
2018-07-31 16:22:40 +03:00
ret = media_create_pad_link ( if_vid , pad_source ,
decoder , pad_sink ,
MEDIA_LNK_FL_ENABLED ) ;
2018-09-15 07:00:31 +03:00
if ( ret ) {
dev_warn ( mdev - > dev , " couldn't link PLL to decoder \n " ) ;
2016-02-05 12:02:43 +03:00
return ret ;
2018-09-15 07:00:31 +03:00
}
2016-02-05 12:02:43 +03:00
} else {
2018-07-31 16:22:40 +03:00
pad_source = media_get_pad_index ( tuner , false ,
PAD_SIGNAL_ANALOG ) ;
pad_sink = media_get_pad_index ( decoder , true ,
PAD_SIGNAL_ANALOG ) ;
2018-09-15 07:00:31 +03:00
if ( pad_source < 0 | | pad_sink < 0 ) {
dev_warn ( mdev - > dev , " couldn't get tuner and/or decoder pad(s): (%d, %d) \n " ,
pad_source , pad_sink ) ;
2018-07-31 16:22:40 +03:00
return - EINVAL ;
2018-09-15 07:00:31 +03:00
}
2018-07-31 16:22:40 +03:00
ret = media_create_pad_link ( tuner , pad_source ,
decoder , pad_sink ,
MEDIA_LNK_FL_ENABLED ) ;
2016-02-05 12:02:43 +03:00
if ( ret )
return ret ;
}
if ( if_aud ) {
2018-07-31 16:22:40 +03:00
pad_source = media_get_pad_index ( tuner , false ,
PAD_SIGNAL_AUDIO ) ;
pad_sink = media_get_pad_index ( if_aud , true ,
PAD_SIGNAL_AUDIO ) ;
2018-09-15 07:00:31 +03:00
if ( pad_source < 0 | | pad_sink < 0 ) {
dev_warn ( mdev - > dev , " couldn't get tuner and/or decoder pad(s) for audio: (%d, %d) \n " ,
pad_source , pad_sink ) ;
2018-07-31 16:22:40 +03:00
return - EINVAL ;
2018-09-15 07:00:31 +03:00
}
2018-07-31 16:22:40 +03:00
ret = media_create_pad_link ( tuner , pad_source ,
if_aud , pad_sink ,
2016-02-05 12:02:43 +03:00
MEDIA_LNK_FL_ENABLED ) ;
2018-09-15 07:00:31 +03:00
if ( ret ) {
dev_warn ( mdev - > dev , " couldn't link tuner->audio PLL \n " ) ;
2016-02-05 12:02:43 +03:00
return ret ;
2018-09-15 07:00:31 +03:00
}
2016-02-05 12:02:43 +03:00
} else {
if_aud = tuner ;
}
}
/* Create demod to V4L, VBI and SDR radio links */
if ( io_v4l ) {
2018-07-31 16:22:40 +03:00
pad_source = media_get_pad_index ( decoder , false , PAD_SIGNAL_DV ) ;
2018-09-15 07:00:31 +03:00
if ( pad_source < 0 ) {
dev_warn ( mdev - > dev , " couldn't get decoder output pad for V4L I/O \n " ) ;
2018-07-31 16:22:40 +03:00
return - EINVAL ;
2018-09-15 07:00:31 +03:00
}
2018-07-31 16:22:40 +03:00
ret = media_create_pad_link ( decoder , pad_source ,
io_v4l , 0 ,
MEDIA_LNK_FL_ENABLED ) ;
2018-09-15 07:00:31 +03:00
if ( ret ) {
dev_warn ( mdev - > dev , " couldn't link decoder output to V4L I/O \n " ) ;
2016-02-05 12:02:43 +03:00
return ret ;
2018-09-15 07:00:31 +03:00
}
2016-02-05 12:02:43 +03:00
}
if ( io_swradio ) {
2018-07-31 16:22:40 +03:00
pad_source = media_get_pad_index ( decoder , false , PAD_SIGNAL_DV ) ;
2018-09-15 07:00:31 +03:00
if ( pad_source < 0 ) {
dev_warn ( mdev - > dev , " couldn't get decoder output pad for SDR \n " ) ;
2018-07-31 16:22:40 +03:00
return - EINVAL ;
2018-09-15 07:00:31 +03:00
}
2018-07-31 16:22:40 +03:00
ret = media_create_pad_link ( decoder , pad_source ,
io_swradio , 0 ,
MEDIA_LNK_FL_ENABLED ) ;
2018-09-15 07:00:31 +03:00
if ( ret ) {
dev_warn ( mdev - > dev , " couldn't link decoder output to SDR \n " ) ;
2016-02-05 12:02:43 +03:00
return ret ;
2018-09-15 07:00:31 +03:00
}
2016-02-05 12:02:43 +03:00
}
if ( io_vbi ) {
2018-07-31 16:22:40 +03:00
pad_source = media_get_pad_index ( decoder , false , PAD_SIGNAL_DV ) ;
2018-09-15 07:00:31 +03:00
if ( pad_source < 0 ) {
dev_warn ( mdev - > dev , " couldn't get decoder output pad for VBI \n " ) ;
2018-07-31 16:22:40 +03:00
return - EINVAL ;
2018-09-15 07:00:31 +03:00
}
2018-07-31 16:22:40 +03:00
ret = media_create_pad_link ( decoder , pad_source ,
2016-02-05 12:02:43 +03:00
io_vbi , 0 ,
MEDIA_LNK_FL_ENABLED ) ;
2018-09-15 07:00:31 +03:00
if ( ret ) {
dev_warn ( mdev - > dev , " couldn't link decoder output to VBI \n " ) ;
2016-02-05 12:02:43 +03:00
return ret ;
2018-09-15 07:00:31 +03:00
}
2016-02-05 12:02:43 +03:00
}
/* Create links for the media connectors */
flags = MEDIA_LNK_FL_ENABLED ;
media_device_for_each_entity ( entity , mdev ) {
switch ( entity - > function ) {
case MEDIA_ENT_F_CONN_RF :
if ( ! tuner )
continue ;
2018-07-31 16:22:40 +03:00
pad_sink = media_get_pad_index ( tuner , true ,
PAD_SIGNAL_ANALOG ) ;
2018-09-15 07:00:31 +03:00
if ( pad_sink < 0 ) {
dev_warn ( mdev - > dev , " couldn't get tuner analog pad sink \n " ) ;
2018-07-31 16:22:40 +03:00
return - EINVAL ;
2018-09-15 07:00:31 +03:00
}
2016-02-05 12:02:43 +03:00
ret = media_create_pad_link ( entity , 0 , tuner ,
2018-07-31 16:22:40 +03:00
pad_sink ,
2016-02-05 12:02:43 +03:00
flags ) ;
break ;
case MEDIA_ENT_F_CONN_SVIDEO :
case MEDIA_ENT_F_CONN_COMPOSITE :
2018-07-31 16:22:40 +03:00
pad_sink = media_get_pad_index ( decoder , true ,
PAD_SIGNAL_ANALOG ) ;
2018-09-15 07:00:31 +03:00
if ( pad_sink < 0 ) {
2021-03-15 16:58:54 +03:00
dev_warn ( mdev - > dev , " couldn't get decoder analog pad sink \n " ) ;
2018-07-31 16:22:40 +03:00
return - EINVAL ;
2018-09-15 07:00:31 +03:00
}
2016-02-05 12:02:43 +03:00
ret = media_create_pad_link ( entity , 0 , decoder ,
2018-07-31 16:22:40 +03:00
pad_sink ,
2016-02-05 12:02:43 +03:00
flags ) ;
break ;
default :
continue ;
}
if ( ret )
return ret ;
flags = 0 ;
}
2016-03-02 16:11:41 +03:00
2016-02-05 12:02:43 +03:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( v4l2_mc_create_media_graph ) ;
2016-02-12 02:41:25 +03:00
int v4l_enable_media_source ( struct video_device * vdev )
{
struct media_device * mdev = vdev - > entity . graph_obj . mdev ;
2016-11-30 02:59:54 +03:00
int ret = 0 , err ;
2016-02-12 02:41:25 +03:00
2016-11-30 02:59:54 +03:00
if ( ! mdev )
2016-02-12 02:41:25 +03:00
return 0 ;
2016-11-30 02:59:54 +03:00
mutex_lock ( & mdev - > graph_mutex ) ;
if ( ! mdev - > enable_source )
goto end ;
err = mdev - > enable_source ( & vdev - > entity , & vdev - > pipe ) ;
if ( err )
ret = - EBUSY ;
end :
mutex_unlock ( & mdev - > graph_mutex ) ;
return ret ;
2016-02-12 02:41:25 +03:00
}
EXPORT_SYMBOL_GPL ( v4l_enable_media_source ) ;
void v4l_disable_media_source ( struct video_device * vdev )
{
struct media_device * mdev = vdev - > entity . graph_obj . mdev ;
2016-11-30 02:59:54 +03:00
if ( mdev ) {
mutex_lock ( & mdev - > graph_mutex ) ;
if ( mdev - > disable_source )
mdev - > disable_source ( & vdev - > entity ) ;
mutex_unlock ( & mdev - > graph_mutex ) ;
}
2016-02-12 02:41:25 +03:00
}
EXPORT_SYMBOL_GPL ( v4l_disable_media_source ) ;
int v4l_vb2q_enable_media_source ( struct vb2_queue * q )
{
struct v4l2_fh * fh = q - > owner ;
2016-03-04 05:24:58 +03:00
if ( fh & & fh - > vdev )
return v4l_enable_media_source ( fh - > vdev ) ;
return 0 ;
2016-02-12 02:41:25 +03:00
}
EXPORT_SYMBOL_GPL ( v4l_vb2q_enable_media_source ) ;
2016-02-21 19:25:09 +03:00
2020-05-01 20:15:37 +03:00
int v4l2_create_fwnode_links_to_pad ( struct v4l2_subdev * src_sd ,
2021-02-15 07:27:20 +03:00
struct media_pad * sink , u32 flags )
2020-05-01 20:15:37 +03:00
{
struct fwnode_handle * endpoint ;
struct v4l2_subdev * sink_sd ;
if ( ! ( sink - > flags & MEDIA_PAD_FL_SINK ) | |
! is_media_entity_v4l2_subdev ( sink - > entity ) )
return - EINVAL ;
sink_sd = media_entity_to_v4l2_subdev ( sink - > entity ) ;
fwnode_graph_for_each_endpoint ( dev_fwnode ( src_sd - > dev ) , endpoint ) {
struct fwnode_handle * remote_ep ;
int src_idx , sink_idx , ret ;
struct media_pad * src ;
src_idx = media_entity_get_fwnode_pad ( & src_sd - > entity ,
endpoint ,
MEDIA_PAD_FL_SOURCE ) ;
if ( src_idx < 0 )
continue ;
remote_ep = fwnode_graph_get_remote_endpoint ( endpoint ) ;
if ( ! remote_ep )
continue ;
/*
* ask the sink to verify it owns the remote endpoint ,
* and translate to a sink pad .
*/
sink_idx = media_entity_get_fwnode_pad ( & sink_sd - > entity ,
remote_ep ,
MEDIA_PAD_FL_SINK ) ;
fwnode_handle_put ( remote_ep ) ;
if ( sink_idx < 0 | | sink_idx ! = sink - > index )
continue ;
/*
* the source endpoint corresponds to one of its source pads ,
* the source endpoint connects to an endpoint at the sink
* entity , and the sink endpoint corresponds to the sink
* pad requested , so we have found an endpoint connection
* that works , create the media link for it .
*/
src = & src_sd - > entity . pads [ src_idx ] ;
/* skip if link already exists */
if ( media_entity_find_link ( src , sink ) )
continue ;
dev_dbg ( sink_sd - > dev , " creating link %s:%d -> %s:%d \n " ,
src_sd - > entity . name , src_idx ,
sink_sd - > entity . name , sink_idx ) ;
ret = media_create_pad_link ( & src_sd - > entity , src_idx ,
2021-02-15 07:27:20 +03:00
& sink_sd - > entity , sink_idx , flags ) ;
2020-05-01 20:15:37 +03:00
if ( ret ) {
dev_err ( sink_sd - > dev ,
" link %s:%d -> %s:%d failed with %d \n " ,
src_sd - > entity . name , src_idx ,
sink_sd - > entity . name , sink_idx , ret ) ;
fwnode_handle_put ( endpoint ) ;
return ret ;
}
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( v4l2_create_fwnode_links_to_pad ) ;
int v4l2_create_fwnode_links ( struct v4l2_subdev * src_sd ,
struct v4l2_subdev * sink_sd )
{
unsigned int i ;
for ( i = 0 ; i < sink_sd - > entity . num_pads ; i + + ) {
struct media_pad * pad = & sink_sd - > entity . pads [ i ] ;
int ret ;
if ( ! ( pad - > flags & MEDIA_PAD_FL_SINK ) )
continue ;
2021-02-15 07:27:20 +03:00
ret = v4l2_create_fwnode_links_to_pad ( src_sd , pad , 0 ) ;
2020-05-01 20:15:37 +03:00
if ( ret )
return ret ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( v4l2_create_fwnode_links ) ;
2016-02-21 19:25:09 +03:00
/* -----------------------------------------------------------------------------
* 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 .
*
2020-01-24 23:35:43 +03:00
* The v4l2_pipeline_pm_ { get , put } ( ) functions must be called in the open ( ) and
2016-02-21 19:25:09 +03:00
* 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 ,
2016-11-21 19:48:30 +03:00
struct media_graph * graph )
2016-02-21 19:25:09 +03:00
{
int use = 0 ;
2016-11-21 19:48:30 +03:00
media_graph_walk_start ( graph , entity ) ;
2016-02-21 19:25:09 +03:00
2016-11-21 19:48:30 +03:00
while ( ( entity = media_graph_walk_next ( graph ) ) ) {
2016-02-29 14:45:45 +03:00
if ( is_media_entity_v4l2_video_device ( entity ) )
2016-02-21 19:25:09 +03:00
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 ,
2016-11-21 19:48:30 +03:00
struct media_graph * graph )
2016-02-21 19:25:09 +03:00
{
struct media_entity * first = entity ;
int ret = 0 ;
if ( ! change )
return 0 ;
2016-11-21 19:48:30 +03:00
media_graph_walk_start ( graph , entity ) ;
2016-02-21 19:25:09 +03:00
2016-11-21 19:48:30 +03:00
while ( ! ret & & ( entity = media_graph_walk_next ( graph ) ) )
2016-02-21 19:25:09 +03:00
if ( is_media_entity_v4l2_subdev ( entity ) )
ret = pipeline_pm_power_one ( entity , change ) ;
if ( ! ret )
return ret ;
2016-11-21 19:48:30 +03:00
media_graph_walk_start ( graph , first ) ;
2016-02-21 19:25:09 +03:00
2016-11-21 19:48:30 +03:00
while ( ( first = media_graph_walk_next ( graph ) )
2016-02-21 19:25:09 +03:00
& & first ! = entity )
if ( is_media_entity_v4l2_subdev ( first ) )
pipeline_pm_power_one ( first , - change ) ;
return ret ;
}
2020-01-24 23:35:43 +03:00
static int v4l2_pipeline_pm_use ( struct media_entity * entity , unsigned int use )
2016-02-21 19:25:09 +03:00
{
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 ;
}
2020-01-24 23:35:43 +03:00
int v4l2_pipeline_pm_get ( struct media_entity * entity )
{
return v4l2_pipeline_pm_use ( entity , 1 ) ;
}
EXPORT_SYMBOL_GPL ( v4l2_pipeline_pm_get ) ;
void v4l2_pipeline_pm_put ( struct media_entity * entity )
{
/* Powering off entities shouldn't fail. */
WARN_ON ( v4l2_pipeline_pm_use ( entity , 0 ) ) ;
}
EXPORT_SYMBOL_GPL ( v4l2_pipeline_pm_put ) ;
2016-02-21 19:25:09 +03:00
int v4l2_pipeline_link_notify ( struct media_link * link , u32 flags ,
unsigned int notification )
{
2016-11-21 19:48:30 +03:00
struct media_graph * graph = & link - > graph_obj . mdev - > pm_count_walk ;
2016-02-21 19:25:09 +03:00
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 ) ;