2020-06-12 17:47:11 +03:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Maxim MAX9286 GMSL Deserializer Driver
*
* Copyright ( C ) 2017 - 2019 Jacopo Mondi
* Copyright ( C ) 2017 - 2019 Kieran Bingham
* Copyright ( C ) 2017 - 2019 Laurent Pinchart
* Copyright ( C ) 2017 - 2019 Niklas Söderlund
* Copyright ( C ) 2016 Renesas Electronics Corporation
* Copyright ( C ) 2015 Cogent Embedded , Inc .
*/
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/fwnode.h>
# include <linux/gpio/consumer.h>
# include <linux/gpio/driver.h>
2021-12-17 17:30:18 +03:00
# include <linux/gpio/machine.h>
2020-06-12 17:47:11 +03:00
# include <linux/i2c.h>
# include <linux/i2c-mux.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/of_graph.h>
# include <linux/regulator/consumer.h>
# include <linux/slab.h>
# include <media/v4l2-async.h>
# include <media/v4l2-ctrls.h>
# include <media/v4l2-device.h>
# include <media/v4l2-fwnode.h>
# include <media/v4l2-subdev.h>
/* Register 0x00 */
# define MAX9286_MSTLINKSEL_AUTO (7 << 5)
# define MAX9286_MSTLINKSEL(n) ((n) << 5)
# define MAX9286_EN_VS_GEN BIT(4)
# define MAX9286_LINKEN(n) (1 << (n))
/* Register 0x01 */
# define MAX9286_FSYNCMODE_ECU (3 << 6)
# define MAX9286_FSYNCMODE_EXT (2 << 6)
# define MAX9286_FSYNCMODE_INT_OUT (1 << 6)
# define MAX9286_FSYNCMODE_INT_HIZ (0 << 6)
# define MAX9286_GPIEN BIT(5)
# define MAX9286_ENLMO_RSTFSYNC BIT(2)
# define MAX9286_FSYNCMETH_AUTO (2 << 0)
# define MAX9286_FSYNCMETH_SEMI_AUTO (1 << 0)
# define MAX9286_FSYNCMETH_MANUAL (0 << 0)
# define MAX9286_REG_FSYNC_PERIOD_L 0x06
# define MAX9286_REG_FSYNC_PERIOD_M 0x07
# define MAX9286_REG_FSYNC_PERIOD_H 0x08
/* Register 0x0a */
# define MAX9286_FWDCCEN(n) (1 << ((n) + 4))
# define MAX9286_REVCCEN(n) (1 << (n))
/* Register 0x0c */
# define MAX9286_HVEN BIT(7)
# define MAX9286_EDC_6BIT_HAMMING (2 << 5)
# define MAX9286_EDC_6BIT_CRC (1 << 5)
# define MAX9286_EDC_1BIT_PARITY (0 << 5)
# define MAX9286_DESEL BIT(4)
# define MAX9286_INVVS BIT(3)
# define MAX9286_INVHS BIT(2)
# define MAX9286_HVSRC_D0 (2 << 0)
# define MAX9286_HVSRC_D14 (1 << 0)
# define MAX9286_HVSRC_D18 (0 << 0)
/* Register 0x0f */
# define MAX9286_0X0F_RESERVED BIT(3)
/* Register 0x12 */
# define MAX9286_CSILANECNT(n) (((n) - 1) << 6)
# define MAX9286_CSIDBL BIT(5)
# define MAX9286_DBL BIT(4)
# define MAX9286_DATATYPE_USER_8BIT (11 << 0)
# define MAX9286_DATATYPE_USER_YUV_12BIT (10 << 0)
# define MAX9286_DATATYPE_USER_24BIT (9 << 0)
# define MAX9286_DATATYPE_RAW14 (8 << 0)
# define MAX9286_DATATYPE_RAW11 (7 << 0)
# define MAX9286_DATATYPE_RAW10 (6 << 0)
# define MAX9286_DATATYPE_RAW8 (5 << 0)
# define MAX9286_DATATYPE_YUV422_10BIT (4 << 0)
# define MAX9286_DATATYPE_YUV422_8BIT (3 << 0)
# define MAX9286_DATATYPE_RGB555 (2 << 0)
# define MAX9286_DATATYPE_RGB565 (1 << 0)
# define MAX9286_DATATYPE_RGB888 (0 << 0)
/* Register 0x15 */
# define MAX9286_VC(n) ((n) << 5)
# define MAX9286_VCTYPE BIT(4)
# define MAX9286_CSIOUTEN BIT(3)
# define MAX9286_0X15_RESV (3 << 0)
/* Register 0x1b */
# define MAX9286_SWITCHIN(n) (1 << ((n) + 4))
# define MAX9286_ENEQ(n) (1 << (n))
/* Register 0x27 */
# define MAX9286_LOCKED BIT(7)
/* Register 0x31 */
# define MAX9286_FSYNC_LOCKED BIT(6)
/* Register 0x34 */
# define MAX9286_I2CLOCACK BIT(7)
# define MAX9286_I2CSLVSH_1046NS_469NS (3 << 5)
# define MAX9286_I2CSLVSH_938NS_352NS (2 << 5)
# define MAX9286_I2CSLVSH_469NS_234NS (1 << 5)
# define MAX9286_I2CSLVSH_352NS_117NS (0 << 5)
# define MAX9286_I2CMSTBT_837KBPS (7 << 2)
# define MAX9286_I2CMSTBT_533KBPS (6 << 2)
# define MAX9286_I2CMSTBT_339KBPS (5 << 2)
# define MAX9286_I2CMSTBT_173KBPS (4 << 2)
# define MAX9286_I2CMSTBT_105KBPS (3 << 2)
# define MAX9286_I2CMSTBT_84KBPS (2 << 2)
# define MAX9286_I2CMSTBT_28KBPS (1 << 2)
# define MAX9286_I2CMSTBT_8KBPS (0 << 2)
# define MAX9286_I2CSLVTO_NONE (3 << 0)
# define MAX9286_I2CSLVTO_1024US (2 << 0)
# define MAX9286_I2CSLVTO_256US (1 << 0)
# define MAX9286_I2CSLVTO_64US (0 << 0)
/* Register 0x3b */
# define MAX9286_REV_TRF(n) ((n) << 4)
# define MAX9286_REV_AMP(n) ((((n) - 30) / 10) << 1) /* in mV */
# define MAX9286_REV_AMP_X BIT(0)
2021-06-16 15:46:05 +03:00
# define MAX9286_REV_AMP_HIGH 170
2020-06-12 17:47:11 +03:00
/* Register 0x3f */
# define MAX9286_EN_REV_CFG BIT(6)
# define MAX9286_REV_FLEN(n) ((n) - 20)
/* Register 0x49 */
# define MAX9286_VIDEO_DETECT_MASK 0x0f
/* Register 0x69 */
# define MAX9286_LFLTBMONMASKED BIT(7)
# define MAX9286_LOCKMONMASKED BIT(6)
# define MAX9286_AUTOCOMBACKEN BIT(5)
# define MAX9286_AUTOMASKEN BIT(4)
# define MAX9286_MASKLINK(n) ((n) << 0)
/*
* The sink and source pads are created to match the OF graph port numbers so
* that their indexes can be used interchangeably .
*/
# define MAX9286_NUM_GMSL 4
# define MAX9286_N_SINKS 4
# define MAX9286_N_PADS 5
# define MAX9286_SRC_PAD 4
struct max9286_source {
struct v4l2_subdev * sd ;
struct fwnode_handle * fwnode ;
} ;
2020-08-11 23:59:39 +03:00
struct max9286_asd {
struct v4l2_async_subdev base ;
struct max9286_source * source ;
} ;
static inline struct max9286_asd * to_max9286_asd ( struct v4l2_async_subdev * asd )
{
return container_of ( asd , struct max9286_asd , base ) ;
}
2020-06-12 17:47:11 +03:00
struct max9286_priv {
struct i2c_client * client ;
struct gpio_desc * gpiod_pwdn ;
struct v4l2_subdev sd ;
struct media_pad pads [ MAX9286_N_PADS ] ;
struct regulator * regulator ;
struct gpio_chip gpio ;
u8 gpio_state ;
struct i2c_mux_core * mux ;
unsigned int mux_channel ;
bool mux_open ;
2021-06-16 15:46:03 +03:00
/* The initial reverse control channel amplitude. */
u32 init_rev_chan_mv ;
2021-06-16 15:46:04 +03:00
u32 rev_chan_mv ;
2021-01-14 20:04:29 +03:00
2021-12-17 17:30:18 +03:00
u32 gpio_poc [ 2 ] ;
2020-06-12 17:47:11 +03:00
struct v4l2_ctrl_handler ctrls ;
struct v4l2_ctrl * pixelrate ;
struct v4l2_mbus_framefmt fmt [ MAX9286_N_SINKS ] ;
/* Protects controls and fmt structures */
struct mutex mutex ;
unsigned int nsources ;
unsigned int source_mask ;
unsigned int route_mask ;
unsigned int bound_sources ;
unsigned int csi2_data_lanes ;
struct max9286_source sources [ MAX9286_NUM_GMSL ] ;
struct v4l2_async_notifier notifier ;
} ;
static struct max9286_source * next_source ( struct max9286_priv * priv ,
struct max9286_source * source )
{
if ( ! source )
source = & priv - > sources [ 0 ] ;
else
source + + ;
for ( ; source < & priv - > sources [ MAX9286_NUM_GMSL ] ; source + + ) {
if ( source - > fwnode )
return source ;
}
return NULL ;
}
# define for_each_source(priv, source) \
for ( ( source ) = NULL ; ( ( source ) = next_source ( ( priv ) , ( source ) ) ) ; )
# define to_index(priv, source) ((source) - &(priv)->sources[0])
static inline struct max9286_priv * sd_to_max9286 ( struct v4l2_subdev * sd )
{
return container_of ( sd , struct max9286_priv , sd ) ;
}
/* -----------------------------------------------------------------------------
* I2C IO
*/
static int max9286_read ( struct max9286_priv * priv , u8 reg )
{
int ret ;
ret = i2c_smbus_read_byte_data ( priv - > client , reg ) ;
if ( ret < 0 )
dev_err ( & priv - > client - > dev ,
" %s: register 0x%02x read failed (%d) \n " ,
__func__ , reg , ret ) ;
return ret ;
}
static int max9286_write ( struct max9286_priv * priv , u8 reg , u8 val )
{
int ret ;
ret = i2c_smbus_write_byte_data ( priv - > client , reg , val ) ;
if ( ret < 0 )
dev_err ( & priv - > client - > dev ,
" %s: register 0x%02x write failed (%d) \n " ,
__func__ , reg , ret ) ;
return ret ;
}
/* -----------------------------------------------------------------------------
* I2C Multiplexer
*/
static void max9286_i2c_mux_configure ( struct max9286_priv * priv , u8 conf )
{
max9286_write ( priv , 0x0a , conf ) ;
/*
* We must sleep after any change to the forward or reverse channel
* configuration .
*/
usleep_range ( 3000 , 5000 ) ;
}
static void max9286_i2c_mux_open ( struct max9286_priv * priv )
{
/* Open all channels on the MAX9286 */
max9286_i2c_mux_configure ( priv , 0xff ) ;
priv - > mux_open = true ;
}
static void max9286_i2c_mux_close ( struct max9286_priv * priv )
{
/*
* Ensure that both the forward and reverse channel are disabled on the
* mux , and that the channel ID is invalidated to ensure we reconfigure
* on the next max9286_i2c_mux_select ( ) call .
*/
max9286_i2c_mux_configure ( priv , 0x00 ) ;
priv - > mux_open = false ;
priv - > mux_channel = - 1 ;
}
static int max9286_i2c_mux_select ( struct i2c_mux_core * muxc , u32 chan )
{
struct max9286_priv * priv = i2c_mux_priv ( muxc ) ;
/* Channel select is disabled when configured in the opened state. */
if ( priv - > mux_open )
return 0 ;
if ( priv - > mux_channel = = chan )
return 0 ;
priv - > mux_channel = chan ;
2021-06-16 15:46:02 +03:00
max9286_i2c_mux_configure ( priv , MAX9286_FWDCCEN ( chan ) |
MAX9286_REVCCEN ( chan ) ) ;
2020-06-12 17:47:11 +03:00
return 0 ;
}
static int max9286_i2c_mux_init ( struct max9286_priv * priv )
{
struct max9286_source * source ;
int ret ;
if ( ! i2c_check_functionality ( priv - > client - > adapter ,
I2C_FUNC_SMBUS_WRITE_BYTE_DATA ) )
return - ENODEV ;
priv - > mux = i2c_mux_alloc ( priv - > client - > adapter , & priv - > client - > dev ,
priv - > nsources , 0 , I2C_MUX_LOCKED ,
max9286_i2c_mux_select , NULL ) ;
if ( ! priv - > mux )
return - ENOMEM ;
priv - > mux - > priv = priv ;
for_each_source ( priv , source ) {
unsigned int index = to_index ( priv , source ) ;
ret = i2c_mux_add_adapter ( priv - > mux , 0 , index , 0 ) ;
if ( ret < 0 )
goto error ;
}
return 0 ;
error :
i2c_mux_del_adapters ( priv - > mux ) ;
return ret ;
}
static void max9286_configure_i2c ( struct max9286_priv * priv , bool localack )
{
u8 config = MAX9286_I2CSLVSH_469NS_234NS | MAX9286_I2CSLVTO_1024US |
MAX9286_I2CMSTBT_105KBPS ;
if ( localack )
config | = MAX9286_I2CLOCACK ;
max9286_write ( priv , 0x34 , config ) ;
usleep_range ( 3000 , 5000 ) ;
}
2021-01-14 20:04:28 +03:00
static void max9286_reverse_channel_setup ( struct max9286_priv * priv ,
unsigned int chan_amplitude )
2021-01-14 20:04:27 +03:00
{
2021-06-16 15:46:04 +03:00
u8 chan_config ;
if ( priv - > rev_chan_mv = = chan_amplitude )
return ;
priv - > rev_chan_mv = chan_amplitude ;
2021-01-14 20:04:28 +03:00
/* Reverse channel transmission time: default to 1. */
2021-06-16 15:46:04 +03:00
chan_config = MAX9286_REV_TRF ( 1 ) ;
2021-01-14 20:04:28 +03:00
2021-01-14 20:04:27 +03:00
/*
* Reverse channel setup .
*
* - Enable custom reverse channel configuration ( through register 0x3f )
* and set the first pulse length to 35 clock cycles .
2021-01-14 20:04:28 +03:00
* - Adjust reverse channel amplitude : values > 130 are programmed
* using the additional + 100 mV REV_AMP_X boost flag
2021-01-14 20:04:27 +03:00
*/
max9286_write ( priv , 0x3f , MAX9286_EN_REV_CFG | MAX9286_REV_FLEN ( 35 ) ) ;
2021-01-14 20:04:28 +03:00
if ( chan_amplitude > 100 ) {
/* It is not possible to express values (100 < x < 130) */
chan_amplitude = max ( 30U , chan_amplitude - 100 ) ;
chan_config | = MAX9286_REV_AMP_X ;
}
max9286_write ( priv , 0x3b , chan_config | MAX9286_REV_AMP ( chan_amplitude ) ) ;
2021-01-14 20:04:27 +03:00
usleep_range ( 2000 , 2500 ) ;
}
2020-06-12 17:47:11 +03:00
/*
* max9286_check_video_links ( ) - Make sure video links are detected and locked
*
* Performs safety checks on video link status . Make sure they are detected
* and all enabled links are locked .
*
* Returns 0 for success , - EIO for errors .
*/
static int max9286_check_video_links ( struct max9286_priv * priv )
{
unsigned int i ;
int ret ;
/*
* Make sure valid video links are detected .
* The delay is not characterized in de - serializer manual , wait up
* to 5 ms .
*/
for ( i = 0 ; i < 10 ; i + + ) {
ret = max9286_read ( priv , 0x49 ) ;
if ( ret < 0 )
return - EIO ;
if ( ( ret & MAX9286_VIDEO_DETECT_MASK ) = = priv - > source_mask )
break ;
usleep_range ( 350 , 500 ) ;
}
if ( i = = 10 ) {
dev_err ( & priv - > client - > dev ,
" Unable to detect video links: 0x%02x \n " , ret ) ;
return - EIO ;
}
/* Make sure all enabled links are locked (4ms max). */
for ( i = 0 ; i < 10 ; i + + ) {
ret = max9286_read ( priv , 0x27 ) ;
if ( ret < 0 )
return - EIO ;
if ( ret & MAX9286_LOCKED )
break ;
usleep_range ( 350 , 450 ) ;
}
if ( i = = 10 ) {
dev_err ( & priv - > client - > dev , " Not all enabled links locked \n " ) ;
return - EIO ;
}
return 0 ;
}
/*
* max9286_check_config_link ( ) - Detect and wait for configuration links
*
* Determine if the configuration channel is up and settled for a link .
*
* Returns 0 for success , - EIO for errors .
*/
static int max9286_check_config_link ( struct max9286_priv * priv ,
unsigned int source_mask )
{
unsigned int conflink_mask = ( source_mask & 0x0f ) < < 4 ;
unsigned int i ;
int ret ;
/*
* Make sure requested configuration links are detected .
* The delay is not characterized in the chip manual : wait up
* to 5 milliseconds .
*/
for ( i = 0 ; i < 10 ; i + + ) {
2020-07-20 19:13:35 +03:00
ret = max9286_read ( priv , 0x49 ) ;
2020-06-12 17:47:11 +03:00
if ( ret < 0 )
return - EIO ;
2020-07-20 19:13:35 +03:00
ret & = 0xf0 ;
2020-06-12 17:47:11 +03:00
if ( ret = = conflink_mask )
break ;
usleep_range ( 350 , 500 ) ;
}
if ( ret ! = conflink_mask ) {
dev_err ( & priv - > client - > dev ,
" Unable to detect configuration links: 0x%02x expected 0x%02x \n " ,
ret , conflink_mask ) ;
return - EIO ;
}
dev_info ( & priv - > client - > dev ,
" Successfully detected configuration links after %u loops: 0x%02x \n " ,
i , conflink_mask ) ;
return 0 ;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdev
*/
static int max9286_set_pixelrate ( struct max9286_priv * priv )
{
struct max9286_source * source = NULL ;
u64 pixelrate = 0 ;
for_each_source ( priv , source ) {
struct v4l2_ctrl * ctrl ;
u64 source_rate = 0 ;
/* Pixel rate is mandatory to be reported by sources. */
ctrl = v4l2_ctrl_find ( source - > sd - > ctrl_handler ,
V4L2_CID_PIXEL_RATE ) ;
if ( ! ctrl ) {
pixelrate = 0 ;
break ;
}
/* All source must report the same pixel rate. */
source_rate = v4l2_ctrl_g_ctrl_int64 ( ctrl ) ;
if ( ! pixelrate ) {
pixelrate = source_rate ;
} else if ( pixelrate ! = source_rate ) {
dev_err ( & priv - > client - > dev ,
" Unable to calculate pixel rate \n " ) ;
return - EINVAL ;
}
}
if ( ! pixelrate ) {
dev_err ( & priv - > client - > dev ,
" No pixel rate control available in sources \n " ) ;
return - EINVAL ;
}
/*
* The CSI - 2 transmitter pixel rate is the single source rate multiplied
* by the number of available sources .
*/
return v4l2_ctrl_s_ctrl_int64 ( priv - > pixelrate ,
pixelrate * priv - > nsources ) ;
}
static int max9286_notify_bound ( struct v4l2_async_notifier * notifier ,
struct v4l2_subdev * subdev ,
struct v4l2_async_subdev * asd )
{
struct max9286_priv * priv = sd_to_max9286 ( notifier - > sd ) ;
2020-08-11 23:59:39 +03:00
struct max9286_source * source = to_max9286_asd ( asd ) - > source ;
2020-06-12 17:47:11 +03:00
unsigned int index = to_index ( priv , source ) ;
unsigned int src_pad ;
int ret ;
ret = media_entity_get_fwnode_pad ( & subdev - > entity ,
source - > fwnode ,
MEDIA_PAD_FL_SOURCE ) ;
if ( ret < 0 ) {
dev_err ( & priv - > client - > dev ,
" Failed to find pad for %s \n " , subdev - > name ) ;
return ret ;
}
priv - > bound_sources | = BIT ( index ) ;
source - > sd = subdev ;
src_pad = ret ;
ret = media_create_pad_link ( & source - > sd - > entity , src_pad ,
& priv - > sd . entity , index ,
MEDIA_LNK_FL_ENABLED |
MEDIA_LNK_FL_IMMUTABLE ) ;
if ( ret ) {
dev_err ( & priv - > client - > dev ,
" Unable to link %s:%u -> %s:%u \n " ,
source - > sd - > name , src_pad , priv - > sd . name , index ) ;
return ret ;
}
dev_dbg ( & priv - > client - > dev , " Bound %s pad: %u on index %u \n " ,
subdev - > name , src_pad , index ) ;
/*
2021-06-16 15:46:06 +03:00
* As we register a subdev notifiers we won ' t get a . complete ( ) callback
* here , so we have to use bound_sources to identify when all remote
* serializers have probed .
2020-06-12 17:47:11 +03:00
*/
if ( priv - > bound_sources ! = priv - > source_mask )
return 0 ;
/*
* All enabled sources have probed and enabled their reverse control
* channels :
*
2021-01-14 20:04:29 +03:00
* - Increase the reverse channel amplitude to compensate for the
2021-06-16 15:46:05 +03:00
* remote ends high threshold
2020-06-12 17:47:11 +03:00
* - Verify all configuration links are properly detected
* - Disable auto - ack as communication on the control channel are now
* stable .
*/
2021-06-16 15:46:05 +03:00
max9286_reverse_channel_setup ( priv , MAX9286_REV_AMP_HIGH ) ;
2020-06-12 17:47:11 +03:00
max9286_check_config_link ( priv , priv - > source_mask ) ;
max9286_configure_i2c ( priv , false ) ;
return max9286_set_pixelrate ( priv ) ;
}
static void max9286_notify_unbind ( struct v4l2_async_notifier * notifier ,
struct v4l2_subdev * subdev ,
struct v4l2_async_subdev * asd )
{
struct max9286_priv * priv = sd_to_max9286 ( notifier - > sd ) ;
2020-08-11 23:59:39 +03:00
struct max9286_source * source = to_max9286_asd ( asd ) - > source ;
2020-06-12 17:47:11 +03:00
unsigned int index = to_index ( priv , source ) ;
source - > sd = NULL ;
priv - > bound_sources & = ~ BIT ( index ) ;
}
static const struct v4l2_async_notifier_operations max9286_notify_ops = {
. bound = max9286_notify_bound ,
. unbind = max9286_notify_unbind ,
} ;
static int max9286_v4l2_notifier_register ( struct max9286_priv * priv )
{
struct device * dev = & priv - > client - > dev ;
struct max9286_source * source = NULL ;
int ret ;
if ( ! priv - > nsources )
return 0 ;
2021-03-05 20:13:12 +03:00
v4l2_async_nf_init ( & priv - > notifier ) ;
2020-06-12 17:47:11 +03:00
for_each_source ( priv , source ) {
unsigned int i = to_index ( priv , source ) ;
2021-01-18 04:52:58 +03:00
struct max9286_asd * mas ;
2020-08-11 23:59:39 +03:00
2021-03-05 20:13:12 +03:00
mas = v4l2_async_nf_add_fwnode ( & priv - > notifier , source - > fwnode ,
struct max9286_asd ) ;
2021-01-18 04:52:58 +03:00
if ( IS_ERR ( mas ) ) {
2020-08-11 23:59:39 +03:00
dev_err ( dev , " Failed to add subdev for source %u: %ld " ,
2021-01-18 04:52:58 +03:00
i , PTR_ERR ( mas ) ) ;
2021-03-05 20:13:12 +03:00
v4l2_async_nf_cleanup ( & priv - > notifier ) ;
2021-01-18 04:52:58 +03:00
return PTR_ERR ( mas ) ;
2020-06-12 17:47:11 +03:00
}
2021-01-18 04:52:58 +03:00
mas - > source = source ;
2020-06-12 17:47:11 +03:00
}
priv - > notifier . ops = & max9286_notify_ops ;
2021-03-05 20:13:12 +03:00
ret = v4l2_async_subdev_nf_register ( & priv - > sd , & priv - > notifier ) ;
2020-06-12 17:47:11 +03:00
if ( ret ) {
dev_err ( dev , " Failed to register subdev_notifier " ) ;
2021-03-05 20:13:12 +03:00
v4l2_async_nf_cleanup ( & priv - > notifier ) ;
2020-06-12 17:47:11 +03:00
return ret ;
}
return 0 ;
}
static void max9286_v4l2_notifier_unregister ( struct max9286_priv * priv )
{
if ( ! priv - > nsources )
return ;
2021-03-05 20:13:12 +03:00
v4l2_async_nf_unregister ( & priv - > notifier ) ;
v4l2_async_nf_cleanup ( & priv - > notifier ) ;
2020-06-12 17:47:11 +03:00
}
static int max9286_s_stream ( struct v4l2_subdev * sd , int enable )
{
struct max9286_priv * priv = sd_to_max9286 ( sd ) ;
struct max9286_source * source ;
unsigned int i ;
bool sync = false ;
int ret ;
if ( enable ) {
/*
* The frame sync between cameras is transmitted across the
* reverse channel as GPIO . We must open all channels while
* streaming to allow this synchronisation signal to be shared .
*/
max9286_i2c_mux_open ( priv ) ;
/* Start all cameras. */
for_each_source ( priv , source ) {
ret = v4l2_subdev_call ( source - > sd , video , s_stream , 1 ) ;
if ( ret )
return ret ;
}
ret = max9286_check_video_links ( priv ) ;
if ( ret )
return ret ;
/*
* Wait until frame synchronization is locked .
*
* Manual says frame sync locking should take ~ 6 VTS .
* From practical experience at least 8 are required . Give
* 12 complete frames time ( ~ 400 ms at 30 fps ) to achieve frame
* locking before returning error .
*/
for ( i = 0 ; i < 40 ; i + + ) {
if ( max9286_read ( priv , 0x31 ) & MAX9286_FSYNC_LOCKED ) {
sync = true ;
break ;
}
usleep_range ( 9000 , 11000 ) ;
}
if ( ! sync ) {
dev_err ( & priv - > client - > dev ,
" Failed to get frame synchronization \n " ) ;
return - EXDEV ; /* Invalid cross-device link */
}
/*
* Enable CSI output , VC set according to link number .
* Bit 7 must be set ( chip manual says it ' s 0 and reserved ) .
*/
max9286_write ( priv , 0x15 , 0x80 | MAX9286_VCTYPE |
MAX9286_CSIOUTEN | MAX9286_0X15_RESV ) ;
} else {
max9286_write ( priv , 0x15 , MAX9286_VCTYPE | MAX9286_0X15_RESV ) ;
/* Stop all cameras. */
for_each_source ( priv , source )
v4l2_subdev_call ( source - > sd , video , s_stream , 0 ) ;
max9286_i2c_mux_close ( priv ) ;
}
return 0 ;
}
static int max9286_enum_mbus_code ( 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 ,
2020-06-12 17:47:11 +03:00
struct v4l2_subdev_mbus_code_enum * code )
{
if ( code - > pad | | code - > index > 0 )
return - EINVAL ;
code - > code = MEDIA_BUS_FMT_UYVY8_1X16 ;
return 0 ;
}
static struct v4l2_mbus_framefmt *
max9286_get_pad_format ( struct max9286_priv * priv ,
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 ,
2020-06-12 17:47:11 +03:00
unsigned int pad , u32 which )
{
switch ( which ) {
case V4L2_SUBDEV_FORMAT_TRY :
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
return v4l2_subdev_get_try_format ( & priv - > sd , sd_state , pad ) ;
2020-06-12 17:47:11 +03:00
case V4L2_SUBDEV_FORMAT_ACTIVE :
return & priv - > fmt [ pad ] ;
default :
return NULL ;
}
}
static int max9286_set_fmt ( 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 ,
2020-06-12 17:47:11 +03:00
struct v4l2_subdev_format * format )
{
struct max9286_priv * priv = sd_to_max9286 ( sd ) ;
struct v4l2_mbus_framefmt * cfg_fmt ;
if ( format - > pad = = MAX9286_SRC_PAD )
return - EINVAL ;
/* Refuse non YUV422 formats as we hardcode DT to 8 bit YUV422 */
switch ( format - > format . code ) {
case MEDIA_BUS_FMT_UYVY8_1X16 :
case MEDIA_BUS_FMT_VYUY8_1X16 :
case MEDIA_BUS_FMT_YUYV8_1X16 :
case MEDIA_BUS_FMT_YVYU8_1X16 :
break ;
default :
format - > format . code = MEDIA_BUS_FMT_UYVY8_1X16 ;
break ;
}
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
cfg_fmt = max9286_get_pad_format ( priv , sd_state , format - > pad ,
format - > which ) ;
2020-06-12 17:47:11 +03:00
if ( ! cfg_fmt )
return - EINVAL ;
mutex_lock ( & priv - > mutex ) ;
* cfg_fmt = format - > format ;
mutex_unlock ( & priv - > mutex ) ;
return 0 ;
}
static int max9286_get_fmt ( 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 ,
2020-06-12 17:47:11 +03:00
struct v4l2_subdev_format * format )
{
struct max9286_priv * priv = sd_to_max9286 ( sd ) ;
struct v4l2_mbus_framefmt * cfg_fmt ;
unsigned int pad = format - > pad ;
/*
* Multiplexed Stream Support : Support link validation by returning the
* format of the first bound link . All links must have the same format ,
* as we do not support mixing and matching of cameras connected to the
* max9286 .
*/
if ( pad = = MAX9286_SRC_PAD )
pad = __ffs ( priv - > bound_sources ) ;
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
cfg_fmt = max9286_get_pad_format ( priv , sd_state , pad , format - > which ) ;
2020-06-12 17:47:11 +03:00
if ( ! cfg_fmt )
return - EINVAL ;
mutex_lock ( & priv - > mutex ) ;
format - > format = * cfg_fmt ;
mutex_unlock ( & priv - > mutex ) ;
return 0 ;
}
static const struct v4l2_subdev_video_ops max9286_video_ops = {
. s_stream = max9286_s_stream ,
} ;
static const struct v4l2_subdev_pad_ops max9286_pad_ops = {
. enum_mbus_code = max9286_enum_mbus_code ,
. get_fmt = max9286_get_fmt ,
. set_fmt = max9286_set_fmt ,
} ;
static const struct v4l2_subdev_ops max9286_subdev_ops = {
. video = & max9286_video_ops ,
. pad = & max9286_pad_ops ,
} ;
static void max9286_init_format ( struct v4l2_mbus_framefmt * fmt )
{
fmt - > width = 1280 ;
fmt - > height = 800 ;
fmt - > code = MEDIA_BUS_FMT_UYVY8_1X16 ;
fmt - > colorspace = V4L2_COLORSPACE_SRGB ;
fmt - > field = V4L2_FIELD_NONE ;
fmt - > ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT ;
fmt - > quantization = V4L2_QUANTIZATION_DEFAULT ;
fmt - > xfer_func = V4L2_XFER_FUNC_DEFAULT ;
}
static int max9286_open ( struct v4l2_subdev * subdev , struct v4l2_subdev_fh * fh )
{
struct v4l2_mbus_framefmt * format ;
unsigned int i ;
for ( i = 0 ; i < MAX9286_N_SINKS ; i + + ) {
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
format = v4l2_subdev_get_try_format ( subdev , fh - > state , i ) ;
2020-06-12 17:47:11 +03:00
max9286_init_format ( format ) ;
}
return 0 ;
}
static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = {
. open = max9286_open ,
} ;
2022-01-13 23:51:50 +03:00
static const struct media_entity_operations max9286_media_ops = {
. link_validate = v4l2_subdev_link_validate
} ;
2020-06-12 17:47:11 +03:00
static int max9286_s_ctrl ( struct v4l2_ctrl * ctrl )
{
switch ( ctrl - > id ) {
case V4L2_CID_PIXEL_RATE :
return 0 ;
default :
return - EINVAL ;
}
}
static const struct v4l2_ctrl_ops max9286_ctrl_ops = {
. s_ctrl = max9286_s_ctrl ,
} ;
static int max9286_v4l2_register ( struct max9286_priv * priv )
{
struct device * dev = & priv - > client - > dev ;
struct fwnode_handle * ep ;
int ret ;
int i ;
/* Register v4l2 async notifiers for connected Camera subdevices */
ret = max9286_v4l2_notifier_register ( priv ) ;
if ( ret ) {
dev_err ( dev , " Unable to register V4L2 async notifiers \n " ) ;
return ret ;
}
/* Configure V4L2 for the MAX9286 itself */
for ( i = 0 ; i < MAX9286_N_SINKS ; i + + )
max9286_init_format ( & priv - > fmt [ i ] ) ;
v4l2_i2c_subdev_init ( & priv - > sd , priv - > client , & max9286_subdev_ops ) ;
priv - > sd . internal_ops = & max9286_subdev_internal_ops ;
priv - > sd . flags | = V4L2_SUBDEV_FL_HAS_DEVNODE ;
v4l2_ctrl_handler_init ( & priv - > ctrls , 1 ) ;
priv - > pixelrate = v4l2_ctrl_new_std ( & priv - > ctrls ,
& max9286_ctrl_ops ,
V4L2_CID_PIXEL_RATE ,
1 , INT_MAX , 1 , 50000000 ) ;
priv - > sd . ctrl_handler = & priv - > ctrls ;
ret = priv - > ctrls . error ;
if ( ret )
goto err_async ;
priv - > sd . entity . function = MEDIA_ENT_F_VID_IF_BRIDGE ;
2022-01-13 23:51:50 +03:00
priv - > sd . entity . ops = & max9286_media_ops ;
2020-06-12 17:47:11 +03:00
priv - > pads [ MAX9286_SRC_PAD ] . flags = MEDIA_PAD_FL_SOURCE ;
for ( i = 0 ; i < MAX9286_SRC_PAD ; i + + )
priv - > pads [ i ] . flags = MEDIA_PAD_FL_SINK ;
ret = media_entity_pads_init ( & priv - > sd . entity , MAX9286_N_PADS ,
priv - > pads ) ;
if ( ret )
goto err_async ;
ep = fwnode_graph_get_endpoint_by_id ( dev_fwnode ( dev ) , MAX9286_SRC_PAD ,
0 , 0 ) ;
if ( ! ep ) {
dev_err ( dev , " Unable to retrieve endpoint on \" port@4 \" \n " ) ;
ret = - ENOENT ;
goto err_async ;
}
priv - > sd . fwnode = ep ;
ret = v4l2_async_register_subdev ( & priv - > sd ) ;
if ( ret < 0 ) {
dev_err ( dev , " Unable to register subdevice \n " ) ;
goto err_put_node ;
}
return 0 ;
err_put_node :
fwnode_handle_put ( ep ) ;
err_async :
max9286_v4l2_notifier_unregister ( priv ) ;
return ret ;
}
static void max9286_v4l2_unregister ( struct max9286_priv * priv )
{
fwnode_handle_put ( priv - > sd . fwnode ) ;
v4l2_async_unregister_subdev ( & priv - > sd ) ;
max9286_v4l2_notifier_unregister ( priv ) ;
}
/* -----------------------------------------------------------------------------
* Probe / Remove
*/
static int max9286_setup ( struct max9286_priv * priv )
{
/*
* Link ordering values for all enabled links combinations . Orders must
* be assigned sequentially from 0 to the number of enabled links
* without leaving any hole for disabled links . We thus assign orders to
* enabled links first , and use the remaining order values for disabled
* links are all links must have a different order value ;
*/
static const u8 link_order [ ] = {
( 3 < < 6 ) | ( 2 < < 4 ) | ( 1 < < 2 ) | ( 0 < < 0 ) , /* xxxx */
( 3 < < 6 ) | ( 2 < < 4 ) | ( 1 < < 2 ) | ( 0 < < 0 ) , /* xxx0 */
( 3 < < 6 ) | ( 2 < < 4 ) | ( 0 < < 2 ) | ( 1 < < 0 ) , /* xx0x */
( 3 < < 6 ) | ( 2 < < 4 ) | ( 1 < < 2 ) | ( 0 < < 0 ) , /* xx10 */
( 3 < < 6 ) | ( 0 < < 4 ) | ( 2 < < 2 ) | ( 1 < < 0 ) , /* x0xx */
( 3 < < 6 ) | ( 1 < < 4 ) | ( 2 < < 2 ) | ( 0 < < 0 ) , /* x1x0 */
( 3 < < 6 ) | ( 1 < < 4 ) | ( 0 < < 2 ) | ( 2 < < 0 ) , /* x10x */
( 3 < < 6 ) | ( 1 < < 4 ) | ( 1 < < 2 ) | ( 0 < < 0 ) , /* x210 */
( 0 < < 6 ) | ( 3 < < 4 ) | ( 2 < < 2 ) | ( 1 < < 0 ) , /* 0xxx */
( 1 < < 6 ) | ( 3 < < 4 ) | ( 2 < < 2 ) | ( 0 < < 0 ) , /* 1xx0 */
( 1 < < 6 ) | ( 3 < < 4 ) | ( 0 < < 2 ) | ( 2 < < 0 ) , /* 1x0x */
( 2 < < 6 ) | ( 3 < < 4 ) | ( 1 < < 2 ) | ( 0 < < 0 ) , /* 2x10 */
( 1 < < 6 ) | ( 0 < < 4 ) | ( 3 < < 2 ) | ( 2 < < 0 ) , /* 10xx */
( 2 < < 6 ) | ( 1 < < 4 ) | ( 3 < < 2 ) | ( 0 < < 0 ) , /* 21x0 */
( 2 < < 6 ) | ( 1 < < 4 ) | ( 0 < < 2 ) | ( 3 < < 0 ) , /* 210x */
( 3 < < 6 ) | ( 2 < < 4 ) | ( 1 < < 2 ) | ( 0 < < 0 ) , /* 3210 */
} ;
/*
* Set the I2C bus speed .
*
* Enable I2C Local Acknowledge during the probe sequences of the camera
* only . This should be disabled after the mux is initialised .
*/
max9286_configure_i2c ( priv , true ) ;
2021-06-16 15:46:03 +03:00
max9286_reverse_channel_setup ( priv , priv - > init_rev_chan_mv ) ;
2020-06-12 17:47:11 +03:00
/*
* Enable GMSL links , mask unused ones and autodetect link
* used as CSI clock source .
*/
max9286_write ( priv , 0x00 , MAX9286_MSTLINKSEL_AUTO | priv - > route_mask ) ;
max9286_write ( priv , 0x0b , link_order [ priv - > route_mask ] ) ;
max9286_write ( priv , 0x69 , ( 0xf & ~ priv - > route_mask ) ) ;
/*
* Video format setup :
* Disable CSI output , VC is set according to Link number .
*/
max9286_write ( priv , 0x15 , MAX9286_VCTYPE | MAX9286_0X15_RESV ) ;
/* Enable CSI-2 Lane D0-D3 only, DBL mode, YUV422 8-bit. */
max9286_write ( priv , 0x12 , MAX9286_CSIDBL | MAX9286_DBL |
MAX9286_CSILANECNT ( priv - > csi2_data_lanes ) |
MAX9286_DATATYPE_YUV422_8BIT ) ;
/* Automatic: FRAMESYNC taken from the slowest Link. */
max9286_write ( priv , 0x01 , MAX9286_FSYNCMODE_INT_HIZ |
MAX9286_FSYNCMETH_AUTO ) ;
/* Enable HS/VS encoding, use D14/15 for HS/VS, invert VS. */
max9286_write ( priv , 0x0c , MAX9286_HVEN | MAX9286_INVVS |
MAX9286_HVSRC_D14 ) ;
/*
* The overlap window seems to provide additional validation by tracking
* the delay between vsync and frame sync , generating an error if the
* delay is bigger than the programmed window , though it ' s not yet clear
* what value should be set .
*
* As it ' s an optional value and can be disabled , we do so by setting
* a 0 overlap value .
*/
max9286_write ( priv , 0x63 , 0 ) ;
max9286_write ( priv , 0x64 , 0 ) ;
/*
* Wait for 2 ms to allow the link to resynchronize after the
* configuration change .
*/
usleep_range ( 2000 , 5000 ) ;
return 0 ;
}
2021-12-17 17:30:18 +03:00
static int max9286_gpio_set ( struct max9286_priv * priv , unsigned int offset ,
int value )
2020-06-12 17:47:11 +03:00
{
if ( value )
priv - > gpio_state | = BIT ( offset ) ;
else
priv - > gpio_state & = ~ BIT ( offset ) ;
2021-12-17 17:30:18 +03:00
return max9286_write ( priv , 0x0f ,
MAX9286_0X0F_RESERVED | priv - > gpio_state ) ;
2020-06-12 17:47:11 +03:00
}
2021-12-17 17:30:18 +03:00
static void max9286_gpiochip_set ( struct gpio_chip * chip ,
unsigned int offset , int value )
{
struct max9286_priv * priv = gpiochip_get_data ( chip ) ;
max9286_gpio_set ( priv , offset , value ) ;
}
static int max9286_gpiochip_get ( struct gpio_chip * chip , unsigned int offset )
2020-06-12 17:47:11 +03:00
{
struct max9286_priv * priv = gpiochip_get_data ( chip ) ;
return priv - > gpio_state & BIT ( offset ) ;
}
static int max9286_register_gpio ( struct max9286_priv * priv )
{
struct device * dev = & priv - > client - > dev ;
struct gpio_chip * gpio = & priv - > gpio ;
int ret ;
/* Configure the GPIO */
gpio - > label = dev_name ( dev ) ;
gpio - > parent = dev ;
gpio - > owner = THIS_MODULE ;
gpio - > ngpio = 2 ;
gpio - > base = - 1 ;
2021-12-17 17:30:18 +03:00
gpio - > set = max9286_gpiochip_set ;
gpio - > get = max9286_gpiochip_get ;
2020-06-12 17:47:11 +03:00
gpio - > can_sleep = true ;
ret = devm_gpiochip_add_data ( dev , gpio , priv ) ;
if ( ret )
dev_err ( dev , " Unable to create gpio_chip \n " ) ;
return ret ;
}
2021-12-17 17:30:18 +03:00
static int max9286_parse_gpios ( struct max9286_priv * priv )
{
struct device * dev = & priv - > client - > dev ;
int ret ;
/* GPIO values default to high */
priv - > gpio_state = BIT ( 0 ) | BIT ( 1 ) ;
/*
* Parse the " gpio-poc " vendor property . If the property is not
* specified the camera power is controlled by a regulator .
*/
ret = of_property_read_u32_array ( dev - > of_node , " maxim,gpio-poc " ,
priv - > gpio_poc , 2 ) ;
if ( ret = = - EINVAL ) {
/*
* If gpio lines are not used for the camera power , register
* a gpio controller for consumers .
*/
ret = max9286_register_gpio ( priv ) ;
if ( ret )
return ret ;
priv - > regulator = devm_regulator_get ( dev , " poc " ) ;
if ( IS_ERR ( priv - > regulator ) ) {
return dev_err_probe ( dev , PTR_ERR ( priv - > regulator ) ,
" Unable to get PoC regulator (%ld) \n " ,
PTR_ERR ( priv - > regulator ) ) ;
}
return 0 ;
}
/* If the property is specified make sure it is well formed. */
if ( ret | | priv - > gpio_poc [ 0 ] > 1 | |
( priv - > gpio_poc [ 1 ] ! = GPIO_ACTIVE_HIGH & &
priv - > gpio_poc [ 1 ] ! = GPIO_ACTIVE_LOW ) ) {
dev_err ( dev , " Invalid 'gpio-poc' property \n " ) ;
return - EINVAL ;
}
return 0 ;
}
static int max9286_poc_enable ( struct max9286_priv * priv , bool enable )
{
int ret ;
/* If the regulator is not available, use gpio to control power. */
if ( ! priv - > regulator )
ret = max9286_gpio_set ( priv , priv - > gpio_poc [ 0 ] ,
enable ^ priv - > gpio_poc [ 1 ] ) ;
else if ( enable )
ret = regulator_enable ( priv - > regulator ) ;
else
ret = regulator_disable ( priv - > regulator ) ;
if ( ret < 0 )
dev_err ( & priv - > client - > dev , " Unable to turn power %s \n " ,
enable ? " on " : " off " ) ;
return ret ;
}
2020-06-12 17:47:11 +03:00
static int max9286_init ( struct device * dev )
{
struct max9286_priv * priv ;
struct i2c_client * client ;
int ret ;
client = to_i2c_client ( dev ) ;
priv = i2c_get_clientdata ( client ) ;
2021-12-17 17:30:18 +03:00
ret = max9286_poc_enable ( priv , true ) ;
if ( ret )
2020-06-12 17:47:11 +03:00
return ret ;
ret = max9286_setup ( priv ) ;
if ( ret ) {
dev_err ( dev , " Unable to setup max9286 \n " ) ;
2021-12-17 17:30:18 +03:00
goto err_poc_disable ;
2020-06-12 17:47:11 +03:00
}
/*
* Register all V4L2 interactions for the MAX9286 and notifiers for
* any subdevices connected .
*/
ret = max9286_v4l2_register ( priv ) ;
if ( ret ) {
dev_err ( dev , " Failed to register with V4L2 \n " ) ;
2021-12-17 17:30:18 +03:00
goto err_poc_disable ;
2020-06-12 17:47:11 +03:00
}
ret = max9286_i2c_mux_init ( priv ) ;
if ( ret ) {
dev_err ( dev , " Unable to initialize I2C multiplexer \n " ) ;
goto err_v4l2_register ;
}
/* Leave the mux channels disabled until they are selected. */
max9286_i2c_mux_close ( priv ) ;
return 0 ;
err_v4l2_register :
max9286_v4l2_unregister ( priv ) ;
2021-12-17 17:30:18 +03:00
err_poc_disable :
max9286_poc_enable ( priv , false ) ;
2020-06-12 17:47:11 +03:00
return ret ;
}
static void max9286_cleanup_dt ( struct max9286_priv * priv )
{
struct max9286_source * source ;
for_each_source ( priv , source ) {
fwnode_handle_put ( source - > fwnode ) ;
source - > fwnode = NULL ;
}
}
static int max9286_parse_dt ( struct max9286_priv * priv )
{
struct device * dev = & priv - > client - > dev ;
struct device_node * i2c_mux ;
struct device_node * node = NULL ;
unsigned int i2c_mux_mask = 0 ;
2021-01-14 20:04:29 +03:00
u32 reverse_channel_microvolt ;
2020-06-12 17:47:11 +03:00
/* Balance the of_node_put() performed by of_find_node_by_name(). */
of_node_get ( dev - > of_node ) ;
i2c_mux = of_find_node_by_name ( dev - > of_node , " i2c-mux " ) ;
if ( ! i2c_mux ) {
dev_err ( dev , " Failed to find i2c-mux node \n " ) ;
return - EINVAL ;
}
/* Identify which i2c-mux channels are enabled */
for_each_child_of_node ( i2c_mux , node ) {
u32 id = 0 ;
of_property_read_u32 ( node , " reg " , & id ) ;
if ( id > = MAX9286_NUM_GMSL )
continue ;
if ( ! of_device_is_available ( node ) ) {
dev_dbg ( dev , " Skipping disabled I2C bus port %u \n " , id ) ;
continue ;
}
i2c_mux_mask | = BIT ( id ) ;
}
of_node_put ( node ) ;
of_node_put ( i2c_mux ) ;
/* Parse the endpoints */
for_each_endpoint_of_node ( dev - > of_node , node ) {
struct max9286_source * source ;
struct of_endpoint ep ;
of_graph_parse_endpoint ( node , & ep ) ;
dev_dbg ( dev , " Endpoint %pOF on port %d " ,
ep . local_node , ep . port ) ;
if ( ep . port > MAX9286_NUM_GMSL ) {
dev_err ( dev , " Invalid endpoint %s on port %d " ,
of_node_full_name ( ep . local_node ) , ep . port ) ;
continue ;
}
/* For the source endpoint just parse the bus configuration. */
if ( ep . port = = MAX9286_SRC_PAD ) {
struct v4l2_fwnode_endpoint vep = {
. bus_type = V4L2_MBUS_CSI2_DPHY
} ;
int ret ;
ret = v4l2_fwnode_endpoint_parse (
of_fwnode_handle ( node ) , & vep ) ;
if ( ret ) {
of_node_put ( node ) ;
return ret ;
}
priv - > csi2_data_lanes =
vep . bus . mipi_csi2 . num_data_lanes ;
continue ;
}
/* Skip if the corresponding GMSL link is unavailable. */
if ( ! ( i2c_mux_mask & BIT ( ep . port ) ) )
continue ;
if ( priv - > sources [ ep . port ] . fwnode ) {
dev_err ( dev ,
" Multiple port endpoints are not supported: %d " ,
ep . port ) ;
continue ;
}
source = & priv - > sources [ ep . port ] ;
source - > fwnode = fwnode_graph_get_remote_endpoint (
of_fwnode_handle ( node ) ) ;
if ( ! source - > fwnode ) {
dev_err ( dev ,
" Endpoint %pOF has no remote endpoint connection \n " ,
ep . local_node ) ;
continue ;
}
priv - > source_mask | = BIT ( ep . port ) ;
priv - > nsources + + ;
}
of_node_put ( node ) ;
2021-01-14 20:04:29 +03:00
/*
* Parse the initial value of the reverse channel amplitude from
* the firmware interface and convert it to millivolts .
*
* Default it to 170 mV for backward compatibility with DTBs that do not
* provide the property .
*/
if ( of_property_read_u32 ( dev - > of_node ,
" maxim,reverse-channel-microvolt " ,
& reverse_channel_microvolt ) )
2021-06-16 15:46:03 +03:00
priv - > init_rev_chan_mv = 170 ;
2021-01-14 20:04:29 +03:00
else
2021-06-16 15:46:03 +03:00
priv - > init_rev_chan_mv = reverse_channel_microvolt / 1000U ;
2021-01-14 20:04:29 +03:00
2020-06-12 17:47:11 +03:00
priv - > route_mask = priv - > source_mask ;
return 0 ;
}
static int max9286_probe ( struct i2c_client * client )
{
struct max9286_priv * priv ;
int ret ;
priv = devm_kzalloc ( & client - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
mutex_init ( & priv - > mutex ) ;
priv - > client = client ;
i2c_set_clientdata ( client , priv ) ;
priv - > gpiod_pwdn = devm_gpiod_get_optional ( & client - > dev , " enable " ,
GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( priv - > gpiod_pwdn ) )
return PTR_ERR ( priv - > gpiod_pwdn ) ;
gpiod_set_consumer_name ( priv - > gpiod_pwdn , " max9286-pwdn " ) ;
gpiod_set_value_cansleep ( priv - > gpiod_pwdn , 1 ) ;
/* Wait at least 4ms before the I2C lines latch to the address */
if ( priv - > gpiod_pwdn )
usleep_range ( 4000 , 5000 ) ;
/*
* The MAX9286 starts by default with all ports enabled , we disable all
* ports early to ensure that all channels are disabled if we error out
* and keep the bus consistent .
*/
max9286_i2c_mux_close ( priv ) ;
/*
* The MAX9286 initialises with auto - acknowledge enabled by default .
* This can be invasive to other transactions on the same bus , so
* disable it early . It will be enabled only as and when needed .
*/
max9286_configure_i2c ( priv , false ) ;
2021-12-17 17:30:18 +03:00
ret = max9286_parse_gpios ( priv ) ;
2020-06-12 17:47:11 +03:00
if ( ret )
goto err_powerdown ;
ret = max9286_parse_dt ( priv ) ;
if ( ret )
goto err_powerdown ;
ret = max9286_init ( & client - > dev ) ;
if ( ret < 0 )
goto err_cleanup_dt ;
return 0 ;
err_cleanup_dt :
max9286_cleanup_dt ( priv ) ;
err_powerdown :
gpiod_set_value_cansleep ( priv - > gpiod_pwdn , 0 ) ;
return ret ;
}
static int max9286_remove ( struct i2c_client * client )
{
struct max9286_priv * priv = i2c_get_clientdata ( client ) ;
i2c_mux_del_adapters ( priv - > mux ) ;
max9286_v4l2_unregister ( priv ) ;
2021-12-17 17:30:18 +03:00
max9286_poc_enable ( priv , false ) ;
2020-06-12 17:47:11 +03:00
gpiod_set_value_cansleep ( priv - > gpiod_pwdn , 0 ) ;
max9286_cleanup_dt ( priv ) ;
return 0 ;
}
static const struct of_device_id max9286_dt_ids [ ] = {
{ . compatible = " maxim,max9286 " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , max9286_dt_ids ) ;
static struct i2c_driver max9286_i2c_driver = {
. driver = {
. name = " max9286 " ,
. of_match_table = of_match_ptr ( max9286_dt_ids ) ,
} ,
. probe_new = max9286_probe ,
. remove = max9286_remove ,
} ;
module_i2c_driver ( max9286_i2c_driver ) ;
MODULE_DESCRIPTION ( " Maxim MAX9286 GMSL Deserializer Driver " ) ;
MODULE_AUTHOR ( " Jacopo Mondi, Kieran Bingham, Laurent Pinchart, Niklas Söderlund, Vladimir Barinov " ) ;
MODULE_LICENSE ( " GPL " ) ;