2019-05-27 08:55:06 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-06-07 15:33:55 -03:00
/*
* video stream multiplexer controlled via mux control
*
* Copyright ( C ) 2013 Pengutronix , Sascha Hauer < kernel @ pengutronix . de >
* Copyright ( C ) 2016 - 2017 Pengutronix , Philipp Zabel < kernel @ pengutronix . de >
*/
# include <linux/err.h>
# include <linux/module.h>
# include <linux/mutex.h>
2017-07-18 09:26:00 -04:00
# include <linux/mux/consumer.h>
2017-06-07 15:33:55 -03:00
# include <linux/of.h>
# include <linux/of_graph.h>
# include <linux/platform_device.h>
2018-09-29 15:54:10 -04:00
# include <linux/slab.h>
2017-06-07 15:33:55 -03:00
# include <media/v4l2-async.h>
# include <media/v4l2-device.h>
2018-09-29 15:54:10 -04:00
# include <media/v4l2-fwnode.h>
2020-05-01 19:15:46 +02:00
# include <media/v4l2-mc.h>
2017-06-07 15:33:55 -03:00
# include <media/v4l2-subdev.h>
struct video_mux {
struct v4l2_subdev subdev ;
2020-05-01 19:15:38 +02:00
struct v4l2_async_notifier notifier ;
2017-06-07 15:33:55 -03:00
struct media_pad * pads ;
2017-07-18 09:26:00 -04:00
struct mux_control * mux ;
2017-06-07 15:33:55 -03:00
struct mutex lock ;
int active ;
} ;
2018-05-24 10:50:44 -04:00
static const struct v4l2_mbus_framefmt video_mux_format_mbus_default = {
. width = 1 ,
. height = 1 ,
. code = MEDIA_BUS_FMT_Y8_1X8 ,
. field = V4L2_FIELD_NONE ,
} ;
2020-05-01 19:15:46 +02:00
static inline struct video_mux *
notifier_to_video_mux ( struct v4l2_async_notifier * n )
{
return container_of ( n , struct video_mux , notifier ) ;
}
2017-06-07 15:33:55 -03:00
static inline struct video_mux * v4l2_subdev_to_video_mux ( struct v4l2_subdev * sd )
{
return container_of ( sd , struct video_mux , subdev ) ;
}
static int video_mux_link_setup ( struct media_entity * entity ,
const struct media_pad * local ,
const struct media_pad * remote , u32 flags )
{
struct v4l2_subdev * sd = media_entity_to_v4l2_subdev ( entity ) ;
struct video_mux * vmux = v4l2_subdev_to_video_mux ( sd ) ;
2018-04-03 15:50:22 -04:00
u16 source_pad = entity - > num_pads - 1 ;
2017-06-07 15:33:55 -03:00
int ret = 0 ;
/*
* The mux state is determined by the enabled sink pad link .
* Enabling or disabling the source pad link has no effect .
*/
if ( local - > flags & MEDIA_PAD_FL_SOURCE )
return 0 ;
dev_dbg ( sd - > dev , " link setup '%s':%d->'%s':%d[%d] " ,
remote - > entity - > name , remote - > index , local - > entity - > name ,
local - > index , flags & MEDIA_LNK_FL_ENABLED ) ;
mutex_lock ( & vmux - > lock ) ;
if ( flags & MEDIA_LNK_FL_ENABLED ) {
2023-05-24 14:29:25 +01:00
struct v4l2_subdev_state * sd_state ;
struct v4l2_mbus_framefmt * source_mbusformat ;
2017-06-07 15:33:55 -03:00
if ( vmux - > active = = local - > index )
goto out ;
if ( vmux - > active > = 0 ) {
ret = - EBUSY ;
goto out ;
}
dev_dbg ( sd - > dev , " setting %d active \n " , local - > index ) ;
2017-07-18 09:26:00 -04:00
ret = mux_control_try_select ( vmux - > mux , local - > index ) ;
2017-06-07 15:33:55 -03:00
if ( ret < 0 )
goto out ;
vmux - > active = local - > index ;
2018-04-03 15:50:22 -04:00
/* Propagate the active format to the source */
2023-05-24 14:29:25 +01:00
sd_state = v4l2_subdev_lock_and_get_active_state ( sd ) ;
source_mbusformat = v4l2_subdev_get_pad_format ( sd , sd_state ,
source_pad ) ;
* source_mbusformat = * v4l2_subdev_get_pad_format ( sd , sd_state ,
vmux - > active ) ;
v4l2_subdev_unlock_state ( sd_state ) ;
2017-06-07 15:33:55 -03:00
} else {
if ( vmux - > active ! = local - > index )
goto out ;
dev_dbg ( sd - > dev , " going inactive \n " ) ;
2017-07-18 09:26:00 -04:00
mux_control_deselect ( vmux - > mux ) ;
2017-06-07 15:33:55 -03:00
vmux - > active = - 1 ;
}
out :
mutex_unlock ( & vmux - > lock ) ;
return ret ;
}
static const struct media_entity_operations video_mux_ops = {
. link_setup = video_mux_link_setup ,
. link_validate = v4l2_subdev_link_validate ,
2020-05-01 19:15:42 +02:00
. get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1 ,
2017-06-07 15:33:55 -03:00
} ;
static int video_mux_s_stream ( struct v4l2_subdev * sd , int enable )
{
struct video_mux * vmux = v4l2_subdev_to_video_mux ( sd ) ;
struct v4l2_subdev * upstream_sd ;
struct media_pad * pad ;
if ( vmux - > active = = - 1 ) {
dev_err ( sd - > dev , " Can not start streaming on inactive mux \n " ) ;
return - EINVAL ;
}
2022-06-25 18:02:24 +01:00
pad = media_pad_remote_pad_first ( & sd - > entity . pads [ vmux - > active ] ) ;
2017-06-07 15:33:55 -03:00
if ( ! pad ) {
dev_err ( sd - > dev , " Failed to find remote source pad \n " ) ;
return - ENOLINK ;
}
if ( ! is_media_entity_v4l2_subdev ( pad - > entity ) ) {
dev_err ( sd - > dev , " Upstream entity is not a v4l2 subdev \n " ) ;
return - ENODEV ;
}
upstream_sd = media_entity_to_v4l2_subdev ( pad - > entity ) ;
return v4l2_subdev_call ( upstream_sd , video , s_stream , enable ) ;
}
static const struct v4l2_subdev_video_ops video_mux_subdev_video_ops = {
. s_stream = video_mux_s_stream ,
} ;
static int video_mux_set_format ( struct v4l2_subdev * sd ,
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 17:55:58 +03:00
struct v4l2_subdev_state * sd_state ,
2017-06-07 15:33:55 -03:00
struct v4l2_subdev_format * sdformat )
{
struct video_mux * vmux = v4l2_subdev_to_video_mux ( sd ) ;
2018-04-03 15:50:22 -04:00
struct v4l2_mbus_framefmt * mbusformat , * source_mbusformat ;
2017-06-07 15:33:55 -03:00
struct media_pad * pad = & vmux - > pads [ sdformat - > pad ] ;
2018-04-03 15:50:22 -04:00
u16 source_pad = sd - > entity . num_pads - 1 ;
2017-06-07 15:33:55 -03:00
2023-05-24 14:29:25 +01:00
mbusformat = v4l2_subdev_get_pad_format ( sd , sd_state , sdformat - > pad ) ;
2017-06-07 15:33:55 -03:00
if ( ! mbusformat )
return - EINVAL ;
2023-05-24 14:29:25 +01:00
source_mbusformat = v4l2_subdev_get_pad_format ( sd , sd_state , source_pad ) ;
2018-04-03 15:50:22 -04:00
if ( ! source_mbusformat )
return - EINVAL ;
2018-05-24 10:50:44 -04:00
/* No size limitations except V4L2 compliance requirements */
v4l_bound_align_image ( & sdformat - > format . width , 1 , 65536 , 0 ,
& sdformat - > format . height , 1 , 65536 , 0 , 0 ) ;
/* All formats except LVDS and vendor specific formats are acceptable */
switch ( sdformat - > format . code ) {
case MEDIA_BUS_FMT_RGB444_1X12 :
case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE :
case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE :
case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE :
case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE :
case MEDIA_BUS_FMT_RGB565_1X16 :
case MEDIA_BUS_FMT_BGR565_2X8_BE :
case MEDIA_BUS_FMT_BGR565_2X8_LE :
case MEDIA_BUS_FMT_RGB565_2X8_BE :
case MEDIA_BUS_FMT_RGB565_2X8_LE :
case MEDIA_BUS_FMT_RGB666_1X18 :
case MEDIA_BUS_FMT_RBG888_1X24 :
case MEDIA_BUS_FMT_RGB666_1X24_CPADHI :
case MEDIA_BUS_FMT_BGR888_1X24 :
case MEDIA_BUS_FMT_GBR888_1X24 :
case MEDIA_BUS_FMT_RGB888_1X24 :
case MEDIA_BUS_FMT_RGB888_2X12_BE :
case MEDIA_BUS_FMT_RGB888_2X12_LE :
case MEDIA_BUS_FMT_ARGB8888_1X32 :
case MEDIA_BUS_FMT_RGB888_1X32_PADHI :
case MEDIA_BUS_FMT_RGB101010_1X30 :
case MEDIA_BUS_FMT_RGB121212_1X36 :
case MEDIA_BUS_FMT_RGB161616_1X48 :
case MEDIA_BUS_FMT_Y8_1X8 :
case MEDIA_BUS_FMT_UV8_1X8 :
case MEDIA_BUS_FMT_UYVY8_1_5X8 :
case MEDIA_BUS_FMT_VYUY8_1_5X8 :
case MEDIA_BUS_FMT_YUYV8_1_5X8 :
case MEDIA_BUS_FMT_YVYU8_1_5X8 :
case MEDIA_BUS_FMT_UYVY8_2X8 :
case MEDIA_BUS_FMT_VYUY8_2X8 :
case MEDIA_BUS_FMT_YUYV8_2X8 :
case MEDIA_BUS_FMT_YVYU8_2X8 :
case MEDIA_BUS_FMT_Y10_1X10 :
case MEDIA_BUS_FMT_UYVY10_2X10 :
case MEDIA_BUS_FMT_VYUY10_2X10 :
case MEDIA_BUS_FMT_YUYV10_2X10 :
case MEDIA_BUS_FMT_YVYU10_2X10 :
case MEDIA_BUS_FMT_Y12_1X12 :
case MEDIA_BUS_FMT_UYVY12_2X12 :
case MEDIA_BUS_FMT_VYUY12_2X12 :
case MEDIA_BUS_FMT_YUYV12_2X12 :
case MEDIA_BUS_FMT_YVYU12_2X12 :
case MEDIA_BUS_FMT_UYVY8_1X16 :
case MEDIA_BUS_FMT_VYUY8_1X16 :
case MEDIA_BUS_FMT_YUYV8_1X16 :
case MEDIA_BUS_FMT_YVYU8_1X16 :
case MEDIA_BUS_FMT_YDYUYDYV8_1X16 :
case MEDIA_BUS_FMT_UYVY10_1X20 :
case MEDIA_BUS_FMT_VYUY10_1X20 :
case MEDIA_BUS_FMT_YUYV10_1X20 :
case MEDIA_BUS_FMT_YVYU10_1X20 :
case MEDIA_BUS_FMT_VUY8_1X24 :
case MEDIA_BUS_FMT_YUV8_1X24 :
case MEDIA_BUS_FMT_UYYVYY8_0_5X24 :
case MEDIA_BUS_FMT_UYVY12_1X24 :
case MEDIA_BUS_FMT_VYUY12_1X24 :
case MEDIA_BUS_FMT_YUYV12_1X24 :
case MEDIA_BUS_FMT_YVYU12_1X24 :
case MEDIA_BUS_FMT_YUV10_1X30 :
case MEDIA_BUS_FMT_UYYVYY10_0_5X30 :
case MEDIA_BUS_FMT_AYUV8_1X32 :
case MEDIA_BUS_FMT_UYYVYY12_0_5X36 :
case MEDIA_BUS_FMT_YUV12_1X36 :
case MEDIA_BUS_FMT_YUV16_1X48 :
case MEDIA_BUS_FMT_UYYVYY16_0_5X48 :
case MEDIA_BUS_FMT_JPEG_1X8 :
case MEDIA_BUS_FMT_AHSV8888_1X32 :
2019-02-06 10:13:27 -05:00
case MEDIA_BUS_FMT_SBGGR8_1X8 :
case MEDIA_BUS_FMT_SGBRG8_1X8 :
case MEDIA_BUS_FMT_SGRBG8_1X8 :
case MEDIA_BUS_FMT_SRGGB8_1X8 :
case MEDIA_BUS_FMT_SBGGR10_1X10 :
case MEDIA_BUS_FMT_SGBRG10_1X10 :
case MEDIA_BUS_FMT_SGRBG10_1X10 :
case MEDIA_BUS_FMT_SRGGB10_1X10 :
case MEDIA_BUS_FMT_SBGGR12_1X12 :
case MEDIA_BUS_FMT_SGBRG12_1X12 :
case MEDIA_BUS_FMT_SGRBG12_1X12 :
case MEDIA_BUS_FMT_SRGGB12_1X12 :
case MEDIA_BUS_FMT_SBGGR14_1X14 :
case MEDIA_BUS_FMT_SGBRG14_1X14 :
case MEDIA_BUS_FMT_SGRBG14_1X14 :
case MEDIA_BUS_FMT_SRGGB14_1X14 :
case MEDIA_BUS_FMT_SBGGR16_1X16 :
case MEDIA_BUS_FMT_SGBRG16_1X16 :
case MEDIA_BUS_FMT_SGRBG16_1X16 :
case MEDIA_BUS_FMT_SRGGB16_1X16 :
2018-05-24 10:50:44 -04:00
break ;
default :
sdformat - > format . code = MEDIA_BUS_FMT_Y8_1X8 ;
break ;
}
if ( sdformat - > format . field = = V4L2_FIELD_ANY )
sdformat - > format . field = V4L2_FIELD_NONE ;
2017-06-07 15:33:55 -03:00
mutex_lock ( & vmux - > lock ) ;
/* Source pad mirrors active sink pad, no limitations on sink pads */
if ( ( pad - > flags & MEDIA_PAD_FL_SOURCE ) & & vmux - > active > = 0 )
2023-05-24 14:29:25 +01:00
sdformat - > format = * v4l2_subdev_get_pad_format ( sd , sd_state ,
vmux - > active ) ;
2017-06-07 15:33:55 -03:00
* mbusformat = sdformat - > format ;
2018-04-03 15:50:22 -04:00
/* Propagate the format from an active sink to source */
if ( ( pad - > flags & MEDIA_PAD_FL_SINK ) & & ( pad - > index = = vmux - > active ) )
* source_mbusformat = sdformat - > format ;
2017-06-07 15:33:55 -03:00
mutex_unlock ( & vmux - > lock ) ;
return 0 ;
}
2018-05-24 10:50:44 -04:00
static int video_mux_init_cfg ( struct v4l2_subdev * sd ,
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 17:55:58 +03:00
struct v4l2_subdev_state * sd_state )
2018-05-24 10:50:44 -04:00
{
struct video_mux * vmux = v4l2_subdev_to_video_mux ( sd ) ;
struct v4l2_mbus_framefmt * mbusformat ;
unsigned int i ;
mutex_lock ( & vmux - > lock ) ;
for ( i = 0 ; i < sd - > entity . num_pads ; i + + ) {
2023-05-24 14:29:25 +01:00
mbusformat = v4l2_subdev_get_pad_format ( sd , sd_state , i ) ;
2018-05-24 10:50:44 -04:00
* mbusformat = video_mux_format_mbus_default ;
}
mutex_unlock ( & vmux - > lock ) ;
return 0 ;
}
2017-06-07 15:33:55 -03:00
static const struct v4l2_subdev_pad_ops video_mux_pad_ops = {
2018-05-24 10:50:44 -04:00
. init_cfg = video_mux_init_cfg ,
2023-05-24 14:29:25 +01:00
. get_fmt = v4l2_subdev_get_fmt ,
2017-06-07 15:33:55 -03:00
. set_fmt = video_mux_set_format ,
} ;
static const struct v4l2_subdev_ops video_mux_subdev_ops = {
. pad = & video_mux_pad_ops ,
. video = & video_mux_subdev_video_ops ,
} ;
2020-05-01 19:15:46 +02:00
static int video_mux_notify_bound ( struct v4l2_async_notifier * notifier ,
struct v4l2_subdev * sd ,
struct v4l2_async_subdev * asd )
{
struct video_mux * vmux = notifier_to_video_mux ( notifier ) ;
return v4l2_create_fwnode_links ( sd , & vmux - > subdev ) ;
}
static const struct v4l2_async_notifier_operations video_mux_notify_ops = {
. bound = video_mux_notify_bound ,
} ;
2018-09-29 15:54:10 -04:00
static int video_mux_async_register ( struct video_mux * vmux ,
unsigned int num_input_pads )
{
2020-05-01 19:15:38 +02:00
unsigned int i ;
2018-09-29 15:54:10 -04:00
int ret ;
2021-03-05 18:13:12 +01:00
v4l2_async_nf_init ( & vmux - > notifier ) ;
2018-09-29 15:54:10 -04:00
2020-05-01 19:15:38 +02:00
for ( i = 0 ; i < num_input_pads ; i + + ) {
struct v4l2_async_subdev * asd ;
2021-03-22 15:44:08 +01:00
struct fwnode_handle * ep , * remote_ep ;
2018-09-29 15:54:10 -04:00
2020-05-01 19:15:38 +02:00
ep = fwnode_graph_get_endpoint_by_id (
dev_fwnode ( vmux - > subdev . dev ) , i , 0 ,
FWNODE_GRAPH_ENDPOINT_NEXT ) ;
if ( ! ep )
continue ;
2021-03-22 15:44:08 +01:00
/* Skip dangling endpoints for backwards compatibility */
remote_ep = fwnode_graph_get_remote_endpoint ( ep ) ;
if ( ! remote_ep ) {
fwnode_handle_put ( ep ) ;
continue ;
}
fwnode_handle_put ( remote_ep ) ;
2021-03-05 18:13:12 +01:00
asd = v4l2_async_nf_add_fwnode_remote ( & vmux - > notifier , ep ,
struct v4l2_async_subdev ) ;
2020-05-01 19:15:38 +02:00
fwnode_handle_put ( ep ) ;
2021-01-18 02:52:45 +01:00
if ( IS_ERR ( asd ) ) {
ret = PTR_ERR ( asd ) ;
2020-05-01 19:15:38 +02:00
/* OK if asd already exists */
if ( ret ! = - EEXIST )
2023-05-24 14:29:24 +01:00
goto err_nf_cleanup ;
2020-05-01 19:15:38 +02:00
}
}
2020-05-01 19:15:46 +02:00
vmux - > notifier . ops = & video_mux_notify_ops ;
2021-03-05 18:13:12 +01:00
ret = v4l2_async_subdev_nf_register ( & vmux - > subdev , & vmux - > notifier ) ;
2020-05-01 19:15:38 +02:00
if ( ret )
2023-05-24 14:29:24 +01:00
goto err_nf_cleanup ;
ret = v4l2_async_register_subdev ( & vmux - > subdev ) ;
if ( ret )
goto err_nf_unregister ;
2020-05-01 19:15:38 +02:00
2023-05-24 14:29:24 +01:00
return 0 ;
err_nf_unregister :
v4l2_async_nf_unregister ( & vmux - > notifier ) ;
err_nf_cleanup :
v4l2_async_nf_cleanup ( & vmux - > notifier ) ;
return ret ;
2018-09-29 15:54:10 -04:00
}
2017-06-07 15:33:55 -03:00
static int video_mux_probe ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct device * dev = & pdev - > dev ;
struct device_node * ep ;
struct video_mux * vmux ;
unsigned int num_pads = 0 ;
2018-05-24 10:50:44 -04:00
unsigned int i ;
2017-06-07 15:33:55 -03:00
int ret ;
vmux = devm_kzalloc ( dev , sizeof ( * vmux ) , GFP_KERNEL ) ;
if ( ! vmux )
return - ENOMEM ;
platform_set_drvdata ( pdev , vmux ) ;
v4l2_subdev_init ( & vmux - > subdev , & video_mux_subdev_ops ) ;
2018-08-27 21:52:29 -04:00
snprintf ( vmux - > subdev . name , sizeof ( vmux - > subdev . name ) , " %pOFn " , np ) ;
2017-06-07 15:33:55 -03:00
vmux - > subdev . flags | = V4L2_SUBDEV_FL_HAS_DEVNODE ;
vmux - > subdev . dev = dev ;
/*
* The largest numbered port is the output port . It determines
* total number of pads .
*/
for_each_endpoint_of_node ( np , ep ) {
struct of_endpoint endpoint ;
of_graph_parse_endpoint ( ep , & endpoint ) ;
num_pads = max ( num_pads , endpoint . port + 1 ) ;
}
if ( num_pads < 2 ) {
dev_err ( dev , " Not enough ports %d \n " , num_pads ) ;
return - EINVAL ;
}
2017-07-18 09:26:00 -04:00
vmux - > mux = devm_mux_control_get ( dev , NULL ) ;
if ( IS_ERR ( vmux - > mux ) ) {
ret = PTR_ERR ( vmux - > mux ) ;
2022-04-12 08:29:05 +01:00
return dev_err_probe ( dev , ret , " Failed to get mux \n " ) ;
2017-06-07 15:33:55 -03:00
}
mutex_init ( & vmux - > lock ) ;
vmux - > active = - 1 ;
vmux - > pads = devm_kcalloc ( dev , num_pads , sizeof ( * vmux - > pads ) ,
GFP_KERNEL ) ;
2019-03-09 02:20:56 -05:00
if ( ! vmux - > pads )
return - ENOMEM ;
2023-05-24 14:29:25 +01:00
for ( i = 0 ; i < num_pads ; i + + )
2018-05-24 10:50:44 -04:00
vmux - > pads [ i ] . flags = ( i < num_pads - 1 ) ? MEDIA_PAD_FL_SINK
: MEDIA_PAD_FL_SOURCE ;
2017-06-07 15:33:55 -03:00
vmux - > subdev . entity . function = MEDIA_ENT_F_VID_MUX ;
ret = media_entity_pads_init ( & vmux - > subdev . entity , num_pads ,
vmux - > pads ) ;
if ( ret < 0 )
return ret ;
vmux - > subdev . entity . ops = & video_mux_ops ;
2023-05-24 14:29:25 +01:00
ret = v4l2_subdev_init_finalize ( & vmux - > subdev ) ;
if ( ret < 0 )
goto err_entity_cleanup ;
2020-05-01 19:15:38 +02:00
ret = video_mux_async_register ( vmux , num_pads - 1 ) ;
2023-05-24 14:29:24 +01:00
if ( ret )
2023-05-24 14:29:25 +01:00
goto err_subdev_cleanup ;
2023-05-24 14:29:24 +01:00
return 0 ;
2020-05-01 19:15:38 +02:00
2023-05-24 14:29:25 +01:00
err_subdev_cleanup :
v4l2_subdev_cleanup ( & vmux - > subdev ) ;
2023-05-24 14:29:24 +01:00
err_entity_cleanup :
media_entity_cleanup ( & vmux - > subdev . entity ) ;
2020-05-01 19:15:38 +02:00
return ret ;
2017-06-07 15:33:55 -03:00
}
2023-03-26 16:32:01 +02:00
static void video_mux_remove ( struct platform_device * pdev )
2017-06-07 15:33:55 -03:00
{
struct video_mux * vmux = platform_get_drvdata ( pdev ) ;
struct v4l2_subdev * sd = & vmux - > subdev ;
2021-03-05 18:13:12 +01:00
v4l2_async_nf_unregister ( & vmux - > notifier ) ;
v4l2_async_nf_cleanup ( & vmux - > notifier ) ;
2017-06-07 15:33:55 -03:00
v4l2_async_unregister_subdev ( sd ) ;
2023-05-24 14:29:25 +01:00
v4l2_subdev_cleanup ( sd ) ;
2017-06-07 15:33:55 -03:00
media_entity_cleanup ( & sd - > entity ) ;
}
static const struct of_device_id video_mux_dt_ids [ ] = {
{ . compatible = " video-mux " , } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , video_mux_dt_ids ) ;
static struct platform_driver video_mux_driver = {
. probe = video_mux_probe ,
2023-03-26 16:32:01 +02:00
. remove_new = video_mux_remove ,
2017-06-07 15:33:55 -03:00
. driver = {
. of_match_table = video_mux_dt_ids ,
. name = " video-mux " ,
} ,
} ;
module_platform_driver ( video_mux_driver ) ;
MODULE_DESCRIPTION ( " video stream multiplexer " ) ;
MODULE_AUTHOR ( " Sascha Hauer, Pengutronix " ) ;
MODULE_AUTHOR ( " Philipp Zabel, Pengutronix " ) ;
MODULE_LICENSE ( " GPL " ) ;