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)
2022-01-01 21:28:01 +03:00
# define MAX9286_DATATYPE_RAW12 (7 << 0)
2020-06-12 17:47:11 +03:00
# 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 */
2022-01-01 21:28:03 +03:00
# define MAX9286_CSI_IMAGE_TYP BIT(7)
2020-06-12 17:47:11 +03:00
# define MAX9286_VC(n) ((n) << 5)
# define MAX9286_VCTYPE BIT(4)
# define MAX9286_CSIOUTEN BIT(3)
2022-01-01 21:28:03 +03:00
# define MAX9286_SWP_ENDIAN BIT(2)
# define MAX9286_EN_CCBSYB_CLK_STR BIT(1)
# define MAX9286_EN_GPI_CCBSYB BIT(0)
2020-06-12 17:47:11 +03:00
/* 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
2022-01-01 21:28:02 +03:00
struct max9286_format_info {
u32 code ;
u8 datatype ;
} ;
2020-06-12 17:47:11 +03:00
struct max9286_source {
struct v4l2_subdev * sd ;
struct fwnode_handle * fwnode ;
2022-01-01 21:27:59 +03:00
struct regulator * regulator ;
2020-06-12 17:47:11 +03:00
} ;
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
2022-01-01 21:27:59 +03:00
bool use_gpio_poc ;
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 ;
2022-01-01 21:28:00 +03:00
struct v4l2_ctrl * pixelrate_ctrl ;
unsigned int pixelrate ;
2020-06-12 17:47:11 +03:00
struct v4l2_mbus_framefmt fmt [ MAX9286_N_SINKS ] ;
2022-01-01 21:28:00 +03:00
struct v4l2_fract interval ;
2020-06-12 17:47:11 +03:00
/* 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 ) ;
}
2022-01-01 21:28:02 +03:00
static const struct max9286_format_info max9286_formats [ ] = {
{
. code = MEDIA_BUS_FMT_UYVY8_1X16 ,
. datatype = MAX9286_DATATYPE_YUV422_8BIT ,
} , {
. code = MEDIA_BUS_FMT_VYUY8_1X16 ,
. datatype = MAX9286_DATATYPE_YUV422_8BIT ,
} , {
. code = MEDIA_BUS_FMT_YUYV8_1X16 ,
. datatype = MAX9286_DATATYPE_YUV422_8BIT ,
} , {
. code = MEDIA_BUS_FMT_YVYU8_1X16 ,
. datatype = MAX9286_DATATYPE_YUV422_8BIT ,
} , {
. code = MEDIA_BUS_FMT_SBGGR12_1X12 ,
. datatype = MAX9286_DATATYPE_RAW12 ,
} , {
. code = MEDIA_BUS_FMT_SGBRG12_1X12 ,
. datatype = MAX9286_DATATYPE_RAW12 ,
} , {
. code = MEDIA_BUS_FMT_SGRBG12_1X12 ,
. datatype = MAX9286_DATATYPE_RAW12 ,
} , {
. code = MEDIA_BUS_FMT_SRGGB12_1X12 ,
. datatype = MAX9286_DATATYPE_RAW12 ,
} ,
} ;
2020-06-12 17:47:11 +03:00
/* -----------------------------------------------------------------------------
* 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 ;
}
2022-01-01 21:28:02 +03:00
static void max9286_set_video_format ( struct max9286_priv * priv ,
const struct v4l2_mbus_framefmt * format )
{
const struct max9286_format_info * info = NULL ;
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( max9286_formats ) ; + + i ) {
if ( max9286_formats [ i ] . code = = format - > code ) {
info = & max9286_formats [ i ] ;
break ;
}
}
if ( WARN_ON ( ! info ) )
return ;
/*
2022-01-01 21:28:03 +03:00
* Video format setup : disable CSI output , set VC according to Link
* number , enable I2C clock stretching when CCBSY is low , enable CCBSY
* in external GPI - to - GPO mode .
2022-01-01 21:28:02 +03:00
*/
2022-01-01 21:28:03 +03:00
max9286_write ( priv , 0x15 , MAX9286_VCTYPE | MAX9286_EN_CCBSYB_CLK_STR |
MAX9286_EN_GPI_CCBSYB ) ;
2022-01-01 21:28:02 +03:00
/* Enable CSI-2 Lane D0-D3 only, DBL mode. */
max9286_write ( priv , 0x12 , MAX9286_CSIDBL | MAX9286_DBL |
MAX9286_CSILANECNT ( priv - > csi2_data_lanes ) |
info - > datatype ) ;
/* Enable HS/VS encoding, use D14/15 for HS/VS, invert VS. */
max9286_write ( priv , 0x0c , MAX9286_HVEN | MAX9286_INVVS |
MAX9286_HVSRC_D14 ) ;
}
2022-01-01 21:28:00 +03:00
static void max9286_set_fsync_period ( struct max9286_priv * priv )
{
u32 fsync ;
if ( ! priv - > interval . numerator | | ! priv - > interval . denominator ) {
/*
* Special case , a null interval enables automatic FRAMESYNC
* mode . FRAMESYNC is taken from the slowest link .
*/
max9286_write ( priv , 0x01 , MAX9286_FSYNCMODE_INT_HIZ |
MAX9286_FSYNCMETH_AUTO ) ;
return ;
}
/*
* Manual FRAMESYNC
*
* The FRAMESYNC generator is configured with a period expressed as a
* number of PCLK periods .
*/
fsync = div_u64 ( ( u64 ) priv - > pixelrate * priv - > interval . numerator ,
priv - > interval . denominator ) ;
dev_dbg ( & priv - > client - > dev , " fsync period %u (pclk %u) \n " , fsync ,
priv - > pixelrate ) ;
max9286_write ( priv , 0x01 , MAX9286_FSYNCMODE_INT_OUT |
MAX9286_FSYNCMETH_MANUAL ) ;
max9286_write ( priv , 0x06 , ( fsync > > 0 ) & 0xff ) ;
max9286_write ( priv , 0x07 , ( fsync > > 8 ) & 0xff ) ;
max9286_write ( priv , 0x08 , ( fsync > > 16 ) & 0xff ) ;
}
2020-06-12 17:47:11 +03:00
/* -----------------------------------------------------------------------------
* 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 ;
}
2022-01-01 21:28:00 +03:00
priv - > pixelrate = pixelrate ;
2020-06-12 17:47:11 +03:00
/*
* The CSI - 2 transmitter pixel rate is the single source rate multiplied
* by the number of available sources .
*/
2022-01-01 21:28:00 +03:00
return v4l2_ctrl_s_ctrl_int64 ( priv - > pixelrate_ctrl ,
2020-06-12 17:47:11 +03:00
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 ) {
2022-01-01 21:28:02 +03:00
const struct v4l2_mbus_framefmt * format ;
/*
* Get the format from the first used sink pad , as all sink
* formats must be identical .
*/
format = & priv - > fmt [ __ffs ( priv - > bound_sources ) ] ;
max9286_set_video_format ( priv , format ) ;
2022-01-01 21:28:00 +03:00
max9286_set_fsync_period ( priv ) ;
2020-06-12 17:47:11 +03:00
/*
* 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 */
}
/*
2022-01-01 21:28:03 +03:00
* Configure the CSI - 2 output to line interleaved mode ( W x ( N
* x H ) , as opposed to the ( N x W ) x H mode that outputs the
* images stitched side - by - side ) and enable it .
2020-06-12 17:47:11 +03:00
*/
2022-01-01 21:28:03 +03:00
max9286_write ( priv , 0x15 , MAX9286_CSI_IMAGE_TYP | MAX9286_VCTYPE |
MAX9286_CSIOUTEN | MAX9286_EN_CCBSYB_CLK_STR |
MAX9286_EN_GPI_CCBSYB ) ;
2020-06-12 17:47:11 +03:00
} else {
2022-01-01 21:28:03 +03:00
max9286_write ( priv , 0x15 , MAX9286_VCTYPE |
MAX9286_EN_CCBSYB_CLK_STR |
MAX9286_EN_GPI_CCBSYB ) ;
2020-06-12 17:47:11 +03:00
/* Stop all cameras. */
for_each_source ( priv , source )
v4l2_subdev_call ( source - > sd , video , s_stream , 0 ) ;
max9286_i2c_mux_close ( priv ) ;
}
return 0 ;
}
2022-01-01 21:28:00 +03:00
static int max9286_g_frame_interval ( struct v4l2_subdev * sd ,
struct v4l2_subdev_frame_interval * interval )
{
struct max9286_priv * priv = sd_to_max9286 ( sd ) ;
if ( interval - > pad ! = MAX9286_SRC_PAD )
return - EINVAL ;
interval - > interval = priv - > interval ;
return 0 ;
}
static int max9286_s_frame_interval ( struct v4l2_subdev * sd ,
struct v4l2_subdev_frame_interval * interval )
{
struct max9286_priv * priv = sd_to_max9286 ( sd ) ;
if ( interval - > pad ! = MAX9286_SRC_PAD )
return - EINVAL ;
priv - > interval = interval - > interval ;
return 0 ;
}
2020-06-12 17:47:11 +03:00
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 ;
2022-01-01 21:28:02 +03:00
unsigned int i ;
2020-06-12 17:47:11 +03:00
if ( format - > pad = = MAX9286_SRC_PAD )
return - EINVAL ;
2022-01-01 21:28:02 +03:00
/* Validate the format. */
for ( i = 0 ; i < ARRAY_SIZE ( max9286_formats ) ; + + i ) {
if ( max9286_formats [ i ] . code = = format - > format . code )
break ;
2020-06-12 17:47:11 +03:00
}
2022-01-01 21:28:02 +03:00
if ( i = = ARRAY_SIZE ( max9286_formats ) )
format - > format . code = max9286_formats [ 0 ] . code ;
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 ,
2022-01-01 21:28:00 +03:00
. g_frame_interval = max9286_g_frame_interval ,
. s_frame_interval = max9286_s_frame_interval ,
2020-06-12 17:47:11 +03:00
} ;
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 ,
} ;
2022-01-01 21:28:02 +03:00
static const struct v4l2_mbus_framefmt max9286_default_format = {
. width = 1280 ,
. height = 800 ,
. code = MEDIA_BUS_FMT_UYVY8_1X16 ,
. colorspace = V4L2_COLORSPACE_SRGB ,
. field = V4L2_FIELD_NONE ,
. ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT ,
. quantization = V4L2_QUANTIZATION_DEFAULT ,
. xfer_func = V4L2_XFER_FUNC_DEFAULT ,
} ;
2020-06-12 17:47:11 +03:00
static void max9286_init_format ( struct v4l2_mbus_framefmt * fmt )
{
2022-01-01 21:28:02 +03:00
* fmt = max9286_default_format ;
2020-06-12 17:47:11 +03:00
}
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 ) ;
2022-01-01 21:28:00 +03:00
priv - > pixelrate_ctrl = v4l2_ctrl_new_std ( & priv - > ctrls ,
& max9286_ctrl_ops ,
V4L2_CID_PIXEL_RATE ,
1 , INT_MAX , 1 , 50000000 ) ;
2020-06-12 17:47:11 +03:00
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 ) ) ;
2022-01-01 21:28:02 +03:00
max9286_set_video_format ( priv , & max9286_default_format ) ;
2022-01-01 21:28:00 +03:00
max9286_set_fsync_period ( priv ) ;
2020-06-12 17:47:11 +03:00
/*
* 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 ;
/*
* 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 .
*/
2022-01-01 21:27:59 +03:00
return max9286_register_gpio ( priv ) ;
2021-12-17 17:30:18 +03:00
}
/* 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 ;
}
2022-01-01 21:27:59 +03:00
priv - > use_gpio_poc = true ;
2021-12-17 17:30:18 +03:00
return 0 ;
}
2022-01-01 21:27:59 +03:00
static int max9286_poc_power_on ( struct max9286_priv * priv )
{
struct max9286_source * source ;
unsigned int enabled = 0 ;
int ret ;
/* Enable the global regulator if available. */
if ( priv - > regulator )
return regulator_enable ( priv - > regulator ) ;
if ( priv - > use_gpio_poc )
return max9286_gpio_set ( priv , priv - > gpio_poc [ 0 ] ,
! priv - > gpio_poc [ 1 ] ) ;
/* Otherwise use the per-port regulators. */
for_each_source ( priv , source ) {
ret = regulator_enable ( source - > regulator ) ;
if ( ret < 0 )
goto error ;
enabled | = BIT ( to_index ( priv , source ) ) ;
}
return 0 ;
error :
for_each_source ( priv , source ) {
if ( enabled & BIT ( to_index ( priv , source ) ) )
regulator_disable ( source - > regulator ) ;
}
return ret ;
}
static int max9286_poc_power_off ( struct max9286_priv * priv )
{
struct max9286_source * source ;
int ret = 0 ;
if ( priv - > regulator )
return regulator_disable ( priv - > regulator ) ;
if ( priv - > use_gpio_poc )
return max9286_gpio_set ( priv , priv - > gpio_poc [ 0 ] ,
priv - > gpio_poc [ 1 ] ) ;
for_each_source ( priv , source ) {
int err ;
err = regulator_disable ( source - > regulator ) ;
if ( ! ret )
ret = err ;
}
return ret ;
}
2021-12-17 17:30:18 +03:00
static int max9286_poc_enable ( struct max9286_priv * priv , bool enable )
{
int ret ;
2022-01-01 21:27:59 +03:00
if ( enable )
ret = max9286_poc_power_on ( priv ) ;
2021-12-17 17:30:18 +03:00
else
2022-01-01 21:27:59 +03:00
ret = max9286_poc_power_off ( priv ) ;
2021-12-17 17:30:18 +03:00
if ( ret < 0 )
dev_err ( & priv - > client - > dev , " Unable to turn power %s \n " ,
enable ? " on " : " off " ) ;
return ret ;
}
media: i2c: max9286: fix kernel oops when removing module
When removing the max9286 module we get a kernel oops:
Unable to handle kernel paging request at virtual address 000000aa00000094
Mem abort info:
ESR = 0x96000004
EC = 0x25: DABT (current EL), IL = 32 bits
SET = 0, FnV = 0
EA = 0, S1PTW = 0
FSC = 0x04: level 0 translation fault
Data abort info:
ISV = 0, ISS = 0x00000004
CM = 0, WnR = 0
user pgtable: 4k pages, 48-bit VAs, pgdp=0000000880d85000
[000000aa00000094] pgd=0000000000000000, p4d=0000000000000000
Internal error: Oops: 96000004 [#1] PREEMPT SMP
Modules linked in: fsl_jr_uio caam_jr rng_core libdes caamkeyblob_desc caamhash_desc caamalg_desc crypto_engine max9271 authenc crct10dif_ce mxc_jpeg_encdec
CPU: 2 PID: 713 Comm: rmmod Tainted: G C 5.15.5-00057-gaebcd29c8ed7-dirty #5
Hardware name: Freescale i.MX8QXP MEK (DT)
pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : i2c_mux_del_adapters+0x24/0xf0
lr : max9286_remove+0x28/0xd0 [max9286]
sp : ffff800013a9bbf0
x29: ffff800013a9bbf0 x28: ffff00080b6da940 x27: 0000000000000000
x26: 0000000000000000 x25: 0000000000000000 x24: 0000000000000000
x23: ffff000801a5b970 x22: ffff0008048b0890 x21: ffff800009297000
x20: ffff0008048b0f70 x19: 000000aa00000064 x18: 0000000000000000
x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
x14: 0000000000000014 x13: 0000000000000000 x12: ffff000802da49e8
x11: ffff000802051918 x10: ffff000802da4920 x9 : ffff000800030098
x8 : 0101010101010101 x7 : 7f7f7f7f7f7f7f7f x6 : fefefeff6364626d
x5 : 8080808000000000 x4 : 0000000000000000 x3 : 0000000000000000
x2 : ffffffffffffffff x1 : ffff00080b6da940 x0 : 0000000000000000
Call trace:
i2c_mux_del_adapters+0x24/0xf0
max9286_remove+0x28/0xd0 [max9286]
i2c_device_remove+0x40/0x110
__device_release_driver+0x188/0x234
driver_detach+0xc4/0x150
bus_remove_driver+0x60/0xe0
driver_unregister+0x34/0x64
i2c_del_driver+0x58/0xa0
max9286_i2c_driver_exit+0x1c/0x490 [max9286]
__arm64_sys_delete_module+0x194/0x260
invoke_syscall+0x48/0x114
el0_svc_common.constprop.0+0xd4/0xfc
do_el0_svc+0x2c/0x94
el0_svc+0x28/0x80
el0t_64_sync_handler+0xa8/0x130
el0t_64_sync+0x1a0/0x1a4
The Oops happens because the I2C client data does not point to
max9286_priv anymore but to v4l2_subdev. The change happened in
max9286_init() which calls v4l2_i2c_subdev_init() later on...
Besides fixing the max9286_remove() function, remove the call to
i2c_set_clientdata() in max9286_probe(), to avoid confusion, and make
the necessary changes to max9286_init() so that it doesn't have to use
i2c_get_clientdata() in order to fetch the pointer to priv.
Fixes: 66d8c9d2422d ("media: i2c: Add MAX9286 driver")
Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
2022-03-07 19:46:07 +03:00
static int max9286_init ( struct max9286_priv * priv )
2020-06-12 17:47:11 +03:00
{
media: i2c: max9286: fix kernel oops when removing module
When removing the max9286 module we get a kernel oops:
Unable to handle kernel paging request at virtual address 000000aa00000094
Mem abort info:
ESR = 0x96000004
EC = 0x25: DABT (current EL), IL = 32 bits
SET = 0, FnV = 0
EA = 0, S1PTW = 0
FSC = 0x04: level 0 translation fault
Data abort info:
ISV = 0, ISS = 0x00000004
CM = 0, WnR = 0
user pgtable: 4k pages, 48-bit VAs, pgdp=0000000880d85000
[000000aa00000094] pgd=0000000000000000, p4d=0000000000000000
Internal error: Oops: 96000004 [#1] PREEMPT SMP
Modules linked in: fsl_jr_uio caam_jr rng_core libdes caamkeyblob_desc caamhash_desc caamalg_desc crypto_engine max9271 authenc crct10dif_ce mxc_jpeg_encdec
CPU: 2 PID: 713 Comm: rmmod Tainted: G C 5.15.5-00057-gaebcd29c8ed7-dirty #5
Hardware name: Freescale i.MX8QXP MEK (DT)
pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : i2c_mux_del_adapters+0x24/0xf0
lr : max9286_remove+0x28/0xd0 [max9286]
sp : ffff800013a9bbf0
x29: ffff800013a9bbf0 x28: ffff00080b6da940 x27: 0000000000000000
x26: 0000000000000000 x25: 0000000000000000 x24: 0000000000000000
x23: ffff000801a5b970 x22: ffff0008048b0890 x21: ffff800009297000
x20: ffff0008048b0f70 x19: 000000aa00000064 x18: 0000000000000000
x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
x14: 0000000000000014 x13: 0000000000000000 x12: ffff000802da49e8
x11: ffff000802051918 x10: ffff000802da4920 x9 : ffff000800030098
x8 : 0101010101010101 x7 : 7f7f7f7f7f7f7f7f x6 : fefefeff6364626d
x5 : 8080808000000000 x4 : 0000000000000000 x3 : 0000000000000000
x2 : ffffffffffffffff x1 : ffff00080b6da940 x0 : 0000000000000000
Call trace:
i2c_mux_del_adapters+0x24/0xf0
max9286_remove+0x28/0xd0 [max9286]
i2c_device_remove+0x40/0x110
__device_release_driver+0x188/0x234
driver_detach+0xc4/0x150
bus_remove_driver+0x60/0xe0
driver_unregister+0x34/0x64
i2c_del_driver+0x58/0xa0
max9286_i2c_driver_exit+0x1c/0x490 [max9286]
__arm64_sys_delete_module+0x194/0x260
invoke_syscall+0x48/0x114
el0_svc_common.constprop.0+0xd4/0xfc
do_el0_svc+0x2c/0x94
el0_svc+0x28/0x80
el0t_64_sync_handler+0xa8/0x130
el0t_64_sync+0x1a0/0x1a4
The Oops happens because the I2C client data does not point to
max9286_priv anymore but to v4l2_subdev. The change happened in
max9286_init() which calls v4l2_i2c_subdev_init() later on...
Besides fixing the max9286_remove() function, remove the call to
i2c_set_clientdata() in max9286_probe(), to avoid confusion, and make
the necessary changes to max9286_init() so that it doesn't have to use
i2c_get_clientdata() in order to fetch the pointer to priv.
Fixes: 66d8c9d2422d ("media: i2c: Add MAX9286 driver")
Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
2022-03-07 19:46:07 +03:00
struct i2c_client * client = priv - > client ;
2020-06-12 17:47:11 +03:00
int ret ;
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 ) {
media: i2c: max9286: fix kernel oops when removing module
When removing the max9286 module we get a kernel oops:
Unable to handle kernel paging request at virtual address 000000aa00000094
Mem abort info:
ESR = 0x96000004
EC = 0x25: DABT (current EL), IL = 32 bits
SET = 0, FnV = 0
EA = 0, S1PTW = 0
FSC = 0x04: level 0 translation fault
Data abort info:
ISV = 0, ISS = 0x00000004
CM = 0, WnR = 0
user pgtable: 4k pages, 48-bit VAs, pgdp=0000000880d85000
[000000aa00000094] pgd=0000000000000000, p4d=0000000000000000
Internal error: Oops: 96000004 [#1] PREEMPT SMP
Modules linked in: fsl_jr_uio caam_jr rng_core libdes caamkeyblob_desc caamhash_desc caamalg_desc crypto_engine max9271 authenc crct10dif_ce mxc_jpeg_encdec
CPU: 2 PID: 713 Comm: rmmod Tainted: G C 5.15.5-00057-gaebcd29c8ed7-dirty #5
Hardware name: Freescale i.MX8QXP MEK (DT)
pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : i2c_mux_del_adapters+0x24/0xf0
lr : max9286_remove+0x28/0xd0 [max9286]
sp : ffff800013a9bbf0
x29: ffff800013a9bbf0 x28: ffff00080b6da940 x27: 0000000000000000
x26: 0000000000000000 x25: 0000000000000000 x24: 0000000000000000
x23: ffff000801a5b970 x22: ffff0008048b0890 x21: ffff800009297000
x20: ffff0008048b0f70 x19: 000000aa00000064 x18: 0000000000000000
x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
x14: 0000000000000014 x13: 0000000000000000 x12: ffff000802da49e8
x11: ffff000802051918 x10: ffff000802da4920 x9 : ffff000800030098
x8 : 0101010101010101 x7 : 7f7f7f7f7f7f7f7f x6 : fefefeff6364626d
x5 : 8080808000000000 x4 : 0000000000000000 x3 : 0000000000000000
x2 : ffffffffffffffff x1 : ffff00080b6da940 x0 : 0000000000000000
Call trace:
i2c_mux_del_adapters+0x24/0xf0
max9286_remove+0x28/0xd0 [max9286]
i2c_device_remove+0x40/0x110
__device_release_driver+0x188/0x234
driver_detach+0xc4/0x150
bus_remove_driver+0x60/0xe0
driver_unregister+0x34/0x64
i2c_del_driver+0x58/0xa0
max9286_i2c_driver_exit+0x1c/0x490 [max9286]
__arm64_sys_delete_module+0x194/0x260
invoke_syscall+0x48/0x114
el0_svc_common.constprop.0+0xd4/0xfc
do_el0_svc+0x2c/0x94
el0_svc+0x28/0x80
el0t_64_sync_handler+0xa8/0x130
el0t_64_sync+0x1a0/0x1a4
The Oops happens because the I2C client data does not point to
max9286_priv anymore but to v4l2_subdev. The change happened in
max9286_init() which calls v4l2_i2c_subdev_init() later on...
Besides fixing the max9286_remove() function, remove the call to
i2c_set_clientdata() in max9286_probe(), to avoid confusion, and make
the necessary changes to max9286_init() so that it doesn't have to use
i2c_get_clientdata() in order to fetch the pointer to priv.
Fixes: 66d8c9d2422d ("media: i2c: Add MAX9286 driver")
Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
2022-03-07 19:46:07 +03:00
dev_err ( & client - > 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 ) {
media: i2c: max9286: fix kernel oops when removing module
When removing the max9286 module we get a kernel oops:
Unable to handle kernel paging request at virtual address 000000aa00000094
Mem abort info:
ESR = 0x96000004
EC = 0x25: DABT (current EL), IL = 32 bits
SET = 0, FnV = 0
EA = 0, S1PTW = 0
FSC = 0x04: level 0 translation fault
Data abort info:
ISV = 0, ISS = 0x00000004
CM = 0, WnR = 0
user pgtable: 4k pages, 48-bit VAs, pgdp=0000000880d85000
[000000aa00000094] pgd=0000000000000000, p4d=0000000000000000
Internal error: Oops: 96000004 [#1] PREEMPT SMP
Modules linked in: fsl_jr_uio caam_jr rng_core libdes caamkeyblob_desc caamhash_desc caamalg_desc crypto_engine max9271 authenc crct10dif_ce mxc_jpeg_encdec
CPU: 2 PID: 713 Comm: rmmod Tainted: G C 5.15.5-00057-gaebcd29c8ed7-dirty #5
Hardware name: Freescale i.MX8QXP MEK (DT)
pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : i2c_mux_del_adapters+0x24/0xf0
lr : max9286_remove+0x28/0xd0 [max9286]
sp : ffff800013a9bbf0
x29: ffff800013a9bbf0 x28: ffff00080b6da940 x27: 0000000000000000
x26: 0000000000000000 x25: 0000000000000000 x24: 0000000000000000
x23: ffff000801a5b970 x22: ffff0008048b0890 x21: ffff800009297000
x20: ffff0008048b0f70 x19: 000000aa00000064 x18: 0000000000000000
x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
x14: 0000000000000014 x13: 0000000000000000 x12: ffff000802da49e8
x11: ffff000802051918 x10: ffff000802da4920 x9 : ffff000800030098
x8 : 0101010101010101 x7 : 7f7f7f7f7f7f7f7f x6 : fefefeff6364626d
x5 : 8080808000000000 x4 : 0000000000000000 x3 : 0000000000000000
x2 : ffffffffffffffff x1 : ffff00080b6da940 x0 : 0000000000000000
Call trace:
i2c_mux_del_adapters+0x24/0xf0
max9286_remove+0x28/0xd0 [max9286]
i2c_device_remove+0x40/0x110
__device_release_driver+0x188/0x234
driver_detach+0xc4/0x150
bus_remove_driver+0x60/0xe0
driver_unregister+0x34/0x64
i2c_del_driver+0x58/0xa0
max9286_i2c_driver_exit+0x1c/0x490 [max9286]
__arm64_sys_delete_module+0x194/0x260
invoke_syscall+0x48/0x114
el0_svc_common.constprop.0+0xd4/0xfc
do_el0_svc+0x2c/0x94
el0_svc+0x28/0x80
el0t_64_sync_handler+0xa8/0x130
el0t_64_sync+0x1a0/0x1a4
The Oops happens because the I2C client data does not point to
max9286_priv anymore but to v4l2_subdev. The change happened in
max9286_init() which calls v4l2_i2c_subdev_init() later on...
Besides fixing the max9286_remove() function, remove the call to
i2c_set_clientdata() in max9286_probe(), to avoid confusion, and make
the necessary changes to max9286_init() so that it doesn't have to use
i2c_get_clientdata() in order to fetch the pointer to priv.
Fixes: 66d8c9d2422d ("media: i2c: Add MAX9286 driver")
Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
2022-03-07 19:46:07 +03:00
dev_err ( & client - > 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 ) {
media: i2c: max9286: fix kernel oops when removing module
When removing the max9286 module we get a kernel oops:
Unable to handle kernel paging request at virtual address 000000aa00000094
Mem abort info:
ESR = 0x96000004
EC = 0x25: DABT (current EL), IL = 32 bits
SET = 0, FnV = 0
EA = 0, S1PTW = 0
FSC = 0x04: level 0 translation fault
Data abort info:
ISV = 0, ISS = 0x00000004
CM = 0, WnR = 0
user pgtable: 4k pages, 48-bit VAs, pgdp=0000000880d85000
[000000aa00000094] pgd=0000000000000000, p4d=0000000000000000
Internal error: Oops: 96000004 [#1] PREEMPT SMP
Modules linked in: fsl_jr_uio caam_jr rng_core libdes caamkeyblob_desc caamhash_desc caamalg_desc crypto_engine max9271 authenc crct10dif_ce mxc_jpeg_encdec
CPU: 2 PID: 713 Comm: rmmod Tainted: G C 5.15.5-00057-gaebcd29c8ed7-dirty #5
Hardware name: Freescale i.MX8QXP MEK (DT)
pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : i2c_mux_del_adapters+0x24/0xf0
lr : max9286_remove+0x28/0xd0 [max9286]
sp : ffff800013a9bbf0
x29: ffff800013a9bbf0 x28: ffff00080b6da940 x27: 0000000000000000
x26: 0000000000000000 x25: 0000000000000000 x24: 0000000000000000
x23: ffff000801a5b970 x22: ffff0008048b0890 x21: ffff800009297000
x20: ffff0008048b0f70 x19: 000000aa00000064 x18: 0000000000000000
x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
x14: 0000000000000014 x13: 0000000000000000 x12: ffff000802da49e8
x11: ffff000802051918 x10: ffff000802da4920 x9 : ffff000800030098
x8 : 0101010101010101 x7 : 7f7f7f7f7f7f7f7f x6 : fefefeff6364626d
x5 : 8080808000000000 x4 : 0000000000000000 x3 : 0000000000000000
x2 : ffffffffffffffff x1 : ffff00080b6da940 x0 : 0000000000000000
Call trace:
i2c_mux_del_adapters+0x24/0xf0
max9286_remove+0x28/0xd0 [max9286]
i2c_device_remove+0x40/0x110
__device_release_driver+0x188/0x234
driver_detach+0xc4/0x150
bus_remove_driver+0x60/0xe0
driver_unregister+0x34/0x64
i2c_del_driver+0x58/0xa0
max9286_i2c_driver_exit+0x1c/0x490 [max9286]
__arm64_sys_delete_module+0x194/0x260
invoke_syscall+0x48/0x114
el0_svc_common.constprop.0+0xd4/0xfc
do_el0_svc+0x2c/0x94
el0_svc+0x28/0x80
el0t_64_sync_handler+0xa8/0x130
el0t_64_sync+0x1a0/0x1a4
The Oops happens because the I2C client data does not point to
max9286_priv anymore but to v4l2_subdev. The change happened in
max9286_init() which calls v4l2_i2c_subdev_init() later on...
Besides fixing the max9286_remove() function, remove the call to
i2c_set_clientdata() in max9286_probe(), to avoid confusion, and make
the necessary changes to max9286_init() so that it doesn't have to use
i2c_get_clientdata() in order to fetch the pointer to priv.
Fixes: 66d8c9d2422d ("media: i2c: Add MAX9286 driver")
Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
2022-03-07 19:46:07 +03:00
dev_err ( & client - > dev , " Unable to initialize I2C multiplexer \n " ) ;
2020-06-12 17:47:11 +03:00
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 ;
}
2022-01-01 21:27:59 +03:00
static int max9286_get_poc_supplies ( struct max9286_priv * priv )
{
struct device * dev = & priv - > client - > dev ;
struct max9286_source * source ;
int ret ;
/* Start by getting the global regulator. */
priv - > regulator = devm_regulator_get_optional ( dev , " poc " ) ;
if ( ! IS_ERR ( priv - > regulator ) )
return 0 ;
if ( PTR_ERR ( priv - > regulator ) ! = - ENODEV )
return dev_err_probe ( dev , PTR_ERR ( priv - > regulator ) ,
" Unable to get PoC regulator \n " ) ;
/* If there's no global regulator, get per-port regulators. */
dev_dbg ( dev ,
" No global PoC regulator, looking for per-port regulators \n " ) ;
priv - > regulator = NULL ;
for_each_source ( priv , source ) {
unsigned int index = to_index ( priv , source ) ;
char name [ 10 ] ;
snprintf ( name , sizeof ( name ) , " port%u-poc " , index ) ;
source - > regulator = devm_regulator_get ( dev , name ) ;
if ( IS_ERR ( source - > regulator ) ) {
ret = PTR_ERR ( source - > regulator ) ;
dev_err_probe ( dev , ret ,
" Unable to get port %u PoC regulator \n " ,
index ) ;
return ret ;
}
}
return 0 ;
}
2020-06-12 17:47:11 +03:00
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 ;
2022-01-01 21:27:59 +03:00
/* GPIO values default to high */
priv - > gpio_state = BIT ( 0 ) | BIT ( 1 ) ;
2020-06-12 17:47:11 +03:00
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 )
2022-01-01 21:27:59 +03:00
goto err_cleanup_dt ;
if ( ! priv - > use_gpio_poc ) {
ret = max9286_get_poc_supplies ( priv ) ;
if ( ret )
goto err_cleanup_dt ;
}
2020-06-12 17:47:11 +03:00
media: i2c: max9286: fix kernel oops when removing module
When removing the max9286 module we get a kernel oops:
Unable to handle kernel paging request at virtual address 000000aa00000094
Mem abort info:
ESR = 0x96000004
EC = 0x25: DABT (current EL), IL = 32 bits
SET = 0, FnV = 0
EA = 0, S1PTW = 0
FSC = 0x04: level 0 translation fault
Data abort info:
ISV = 0, ISS = 0x00000004
CM = 0, WnR = 0
user pgtable: 4k pages, 48-bit VAs, pgdp=0000000880d85000
[000000aa00000094] pgd=0000000000000000, p4d=0000000000000000
Internal error: Oops: 96000004 [#1] PREEMPT SMP
Modules linked in: fsl_jr_uio caam_jr rng_core libdes caamkeyblob_desc caamhash_desc caamalg_desc crypto_engine max9271 authenc crct10dif_ce mxc_jpeg_encdec
CPU: 2 PID: 713 Comm: rmmod Tainted: G C 5.15.5-00057-gaebcd29c8ed7-dirty #5
Hardware name: Freescale i.MX8QXP MEK (DT)
pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : i2c_mux_del_adapters+0x24/0xf0
lr : max9286_remove+0x28/0xd0 [max9286]
sp : ffff800013a9bbf0
x29: ffff800013a9bbf0 x28: ffff00080b6da940 x27: 0000000000000000
x26: 0000000000000000 x25: 0000000000000000 x24: 0000000000000000
x23: ffff000801a5b970 x22: ffff0008048b0890 x21: ffff800009297000
x20: ffff0008048b0f70 x19: 000000aa00000064 x18: 0000000000000000
x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
x14: 0000000000000014 x13: 0000000000000000 x12: ffff000802da49e8
x11: ffff000802051918 x10: ffff000802da4920 x9 : ffff000800030098
x8 : 0101010101010101 x7 : 7f7f7f7f7f7f7f7f x6 : fefefeff6364626d
x5 : 8080808000000000 x4 : 0000000000000000 x3 : 0000000000000000
x2 : ffffffffffffffff x1 : ffff00080b6da940 x0 : 0000000000000000
Call trace:
i2c_mux_del_adapters+0x24/0xf0
max9286_remove+0x28/0xd0 [max9286]
i2c_device_remove+0x40/0x110
__device_release_driver+0x188/0x234
driver_detach+0xc4/0x150
bus_remove_driver+0x60/0xe0
driver_unregister+0x34/0x64
i2c_del_driver+0x58/0xa0
max9286_i2c_driver_exit+0x1c/0x490 [max9286]
__arm64_sys_delete_module+0x194/0x260
invoke_syscall+0x48/0x114
el0_svc_common.constprop.0+0xd4/0xfc
do_el0_svc+0x2c/0x94
el0_svc+0x28/0x80
el0t_64_sync_handler+0xa8/0x130
el0t_64_sync+0x1a0/0x1a4
The Oops happens because the I2C client data does not point to
max9286_priv anymore but to v4l2_subdev. The change happened in
max9286_init() which calls v4l2_i2c_subdev_init() later on...
Besides fixing the max9286_remove() function, remove the call to
i2c_set_clientdata() in max9286_probe(), to avoid confusion, and make
the necessary changes to max9286_init() so that it doesn't have to use
i2c_get_clientdata() in order to fetch the pointer to priv.
Fixes: 66d8c9d2422d ("media: i2c: Add MAX9286 driver")
Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
2022-03-07 19:46:07 +03:00
ret = max9286_init ( priv ) ;
2020-06-12 17:47:11 +03:00
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 ;
}
2022-08-15 11:02:30 +03:00
static void max9286_remove ( struct i2c_client * client )
2020-06-12 17:47:11 +03:00
{
media: i2c: max9286: fix kernel oops when removing module
When removing the max9286 module we get a kernel oops:
Unable to handle kernel paging request at virtual address 000000aa00000094
Mem abort info:
ESR = 0x96000004
EC = 0x25: DABT (current EL), IL = 32 bits
SET = 0, FnV = 0
EA = 0, S1PTW = 0
FSC = 0x04: level 0 translation fault
Data abort info:
ISV = 0, ISS = 0x00000004
CM = 0, WnR = 0
user pgtable: 4k pages, 48-bit VAs, pgdp=0000000880d85000
[000000aa00000094] pgd=0000000000000000, p4d=0000000000000000
Internal error: Oops: 96000004 [#1] PREEMPT SMP
Modules linked in: fsl_jr_uio caam_jr rng_core libdes caamkeyblob_desc caamhash_desc caamalg_desc crypto_engine max9271 authenc crct10dif_ce mxc_jpeg_encdec
CPU: 2 PID: 713 Comm: rmmod Tainted: G C 5.15.5-00057-gaebcd29c8ed7-dirty #5
Hardware name: Freescale i.MX8QXP MEK (DT)
pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : i2c_mux_del_adapters+0x24/0xf0
lr : max9286_remove+0x28/0xd0 [max9286]
sp : ffff800013a9bbf0
x29: ffff800013a9bbf0 x28: ffff00080b6da940 x27: 0000000000000000
x26: 0000000000000000 x25: 0000000000000000 x24: 0000000000000000
x23: ffff000801a5b970 x22: ffff0008048b0890 x21: ffff800009297000
x20: ffff0008048b0f70 x19: 000000aa00000064 x18: 0000000000000000
x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
x14: 0000000000000014 x13: 0000000000000000 x12: ffff000802da49e8
x11: ffff000802051918 x10: ffff000802da4920 x9 : ffff000800030098
x8 : 0101010101010101 x7 : 7f7f7f7f7f7f7f7f x6 : fefefeff6364626d
x5 : 8080808000000000 x4 : 0000000000000000 x3 : 0000000000000000
x2 : ffffffffffffffff x1 : ffff00080b6da940 x0 : 0000000000000000
Call trace:
i2c_mux_del_adapters+0x24/0xf0
max9286_remove+0x28/0xd0 [max9286]
i2c_device_remove+0x40/0x110
__device_release_driver+0x188/0x234
driver_detach+0xc4/0x150
bus_remove_driver+0x60/0xe0
driver_unregister+0x34/0x64
i2c_del_driver+0x58/0xa0
max9286_i2c_driver_exit+0x1c/0x490 [max9286]
__arm64_sys_delete_module+0x194/0x260
invoke_syscall+0x48/0x114
el0_svc_common.constprop.0+0xd4/0xfc
do_el0_svc+0x2c/0x94
el0_svc+0x28/0x80
el0t_64_sync_handler+0xa8/0x130
el0t_64_sync+0x1a0/0x1a4
The Oops happens because the I2C client data does not point to
max9286_priv anymore but to v4l2_subdev. The change happened in
max9286_init() which calls v4l2_i2c_subdev_init() later on...
Besides fixing the max9286_remove() function, remove the call to
i2c_set_clientdata() in max9286_probe(), to avoid confusion, and make
the necessary changes to max9286_init() so that it doesn't have to use
i2c_get_clientdata() in order to fetch the pointer to priv.
Fixes: 66d8c9d2422d ("media: i2c: Add MAX9286 driver")
Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
2022-03-07 19:46:07 +03:00
struct max9286_priv * priv = sd_to_max9286 ( i2c_get_clientdata ( client ) ) ;
2020-06-12 17:47:11 +03:00
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 ) ;
}
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 " ) ;