2018-10-05 18:58:31 -04:00
// SPDX-License-Identifier: GPL-2.0
/*
* imx214 . c - imx214 sensor driver
*
* Copyright 2018 Qtechnology A / S
*
2020-04-30 15:52:24 +02:00
* Ricardo Ribalda < ribalda @ kernel . org >
2018-10-05 18:58:31 -04:00
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/gpio/consumer.h>
# include <linux/i2c.h>
# include <linux/module.h>
# include <linux/pm_runtime.h>
# include <linux/regmap.h>
# include <linux/regulator/consumer.h>
# include <media/media-entity.h>
2024-12-20 14:26:02 +01:00
# include <media/v4l2-cci.h>
2018-10-05 18:58:31 -04:00
# include <media/v4l2-ctrls.h>
# include <media/v4l2-fwnode.h>
# include <media/v4l2-subdev.h>
2025-05-24 11:14:38 +02:00
# include "ccs-pll.h"
2024-12-20 14:26:09 +01:00
/* Chip ID */
# define IMX214_REG_CHIP_ID CCI_REG16(0x0016)
# define IMX214_CHIP_ID 0x0214
2024-12-20 14:26:02 +01:00
# define IMX214_REG_MODE_SELECT CCI_REG8(0x0100)
2023-12-06 23:33:55 +01:00
# define IMX214_MODE_STANDBY 0x00
# define IMX214_MODE_STREAMING 0x01
2024-12-20 14:26:03 +01:00
# define IMX214_REG_FAST_STANDBY_CTRL CCI_REG8(0x0106)
2024-12-20 14:26:12 +01:00
# define IMX214_DEFAULT_LINK_FREQ 600000000
/* Keep wrong link frequency for backward compatibility */
# define IMX214_DEFAULT_LINK_FREQ_LEGACY 480000000
2018-10-05 18:58:31 -04:00
# define IMX214_FPS 30
2024-12-20 14:26:03 +01:00
/* V-TIMING internal */
# define IMX214_REG_FRM_LENGTH_LINES CCI_REG16(0x0340)
2024-12-20 14:26:06 +01:00
# define IMX214_VTS_MAX 0xffff
# define IMX214_VBLANK_MIN 890
/* HBLANK control - read only */
# define IMX214_PPL_DEFAULT 5008
2024-12-20 14:26:03 +01:00
2023-12-06 23:33:55 +01:00
/* Exposure control */
2024-12-20 14:26:02 +01:00
# define IMX214_REG_EXPOSURE CCI_REG16(0x0202)
2024-12-20 14:26:06 +01:00
# define IMX214_EXPOSURE_OFFSET 10
# define IMX214_EXPOSURE_MIN 1
2023-12-06 23:33:55 +01:00
# define IMX214_EXPOSURE_STEP 1
# define IMX214_EXPOSURE_DEFAULT 3184
2024-12-20 14:26:03 +01:00
# define IMX214_REG_EXPOSURE_RATIO CCI_REG8(0x0222)
# define IMX214_REG_SHORT_EXPOSURE CCI_REG16(0x0224)
/* Analog gain control */
# define IMX214_REG_ANALOG_GAIN CCI_REG16(0x0204)
# define IMX214_REG_SHORT_ANALOG_GAIN CCI_REG16(0x0216)
2024-12-20 14:26:08 +01:00
# define IMX214_ANA_GAIN_MIN 0
# define IMX214_ANA_GAIN_MAX 448
# define IMX214_ANA_GAIN_STEP 1
# define IMX214_ANA_GAIN_DEFAULT 0x0
2024-12-20 14:26:03 +01:00
/* Digital gain control */
# define IMX214_REG_DIG_GAIN_GREENR CCI_REG16(0x020e)
# define IMX214_REG_DIG_GAIN_RED CCI_REG16(0x0210)
# define IMX214_REG_DIG_GAIN_BLUE CCI_REG16(0x0212)
# define IMX214_REG_DIG_GAIN_GREENB CCI_REG16(0x0214)
2024-12-20 14:26:08 +01:00
# define IMX214_DGTL_GAIN_MIN 0x0100
# define IMX214_DGTL_GAIN_MAX 0x0fff
# define IMX214_DGTL_GAIN_DEFAULT 0x0100
# define IMX214_DGTL_GAIN_STEP 1
2024-12-20 14:26:03 +01:00
# define IMX214_REG_ORIENTATION CCI_REG8(0x0101)
# define IMX214_REG_MASK_CORR_FRAMES CCI_REG8(0x0105)
# define IMX214_CORR_FRAMES_TRANSMIT 0
# define IMX214_CORR_FRAMES_MASK 1
# define IMX214_REG_CSI_DATA_FORMAT CCI_REG16(0x0112)
# define IMX214_CSI_DATA_FORMAT_RAW8 0x0808
# define IMX214_CSI_DATA_FORMAT_RAW10 0x0A0A
# define IMX214_CSI_DATA_FORMAT_COMP6 0x0A06
# define IMX214_CSI_DATA_FORMAT_COMP8 0x0A08
2025-05-24 11:14:38 +02:00
# define IMX214_BITS_PER_PIXEL_MASK 0xFF
2024-12-20 14:26:03 +01:00
# define IMX214_REG_CSI_LANE_MODE CCI_REG8(0x0114)
# define IMX214_CSI_2_LANE_MODE 1
# define IMX214_CSI_4_LANE_MODE 3
# define IMX214_REG_EXCK_FREQ CCI_REG16(0x0136)
# define IMX214_EXCK_FREQ(n) ((n) * 256) /* n expressed in MHz */
# define IMX214_REG_TEMP_SENSOR_CONTROL CCI_REG8(0x0138)
# define IMX214_REG_HDR_MODE CCI_REG8(0x0220)
# define IMX214_HDR_MODE_OFF 0
# define IMX214_HDR_MODE_ON 1
# define IMX214_REG_HDR_RES_REDUCTION CCI_REG8(0x0221)
# define IMX214_HDR_RES_REDU_THROUGH 0x11
# define IMX214_HDR_RES_REDU_2_BINNING 0x22
/* PLL settings */
# define IMX214_REG_VTPXCK_DIV CCI_REG8(0x0301)
# define IMX214_REG_VTSYCK_DIV CCI_REG8(0x0303)
# define IMX214_REG_PREPLLCK_VT_DIV CCI_REG8(0x0305)
# define IMX214_REG_PLL_VT_MPY CCI_REG16(0x0306)
# define IMX214_REG_OPPXCK_DIV CCI_REG8(0x0309)
# define IMX214_REG_OPSYCK_DIV CCI_REG8(0x030b)
# define IMX214_REG_PLL_MULT_DRIV CCI_REG8(0x0310)
# define IMX214_PLL_SINGLE 0
# define IMX214_PLL_DUAL 1
# define IMX214_REG_LINE_LENGTH_PCK CCI_REG16(0x0342)
# define IMX214_REG_X_ADD_STA CCI_REG16(0x0344)
# define IMX214_REG_Y_ADD_STA CCI_REG16(0x0346)
# define IMX214_REG_X_ADD_END CCI_REG16(0x0348)
# define IMX214_REG_Y_ADD_END CCI_REG16(0x034a)
# define IMX214_REG_X_OUTPUT_SIZE CCI_REG16(0x034c)
# define IMX214_REG_Y_OUTPUT_SIZE CCI_REG16(0x034e)
# define IMX214_REG_X_EVEN_INC CCI_REG8(0x0381)
# define IMX214_REG_X_ODD_INC CCI_REG8(0x0383)
# define IMX214_REG_Y_EVEN_INC CCI_REG8(0x0385)
# define IMX214_REG_Y_ODD_INC CCI_REG8(0x0387)
# define IMX214_REG_SCALE_MODE CCI_REG8(0x0401)
# define IMX214_SCALE_NONE 0
# define IMX214_SCALE_HORIZONTAL 1
# define IMX214_SCALE_FULL 2
# define IMX214_REG_SCALE_M CCI_REG16(0x0404)
# define IMX214_REG_DIG_CROP_X_OFFSET CCI_REG16(0x0408)
# define IMX214_REG_DIG_CROP_Y_OFFSET CCI_REG16(0x040a)
# define IMX214_REG_DIG_CROP_WIDTH CCI_REG16(0x040c)
# define IMX214_REG_DIG_CROP_HEIGHT CCI_REG16(0x040e)
# define IMX214_REG_REQ_LINK_BIT_RATE CCI_REG32(0x0820)
# define IMX214_LINK_BIT_RATE_MBPS(n) ((n) << 16)
/* Binning mode */
# define IMX214_REG_BINNING_MODE CCI_REG8(0x0900)
# define IMX214_BINNING_NONE 0
# define IMX214_BINNING_ENABLE 1
# define IMX214_REG_BINNING_TYPE CCI_REG8(0x0901)
# define IMX214_REG_BINNING_WEIGHTING CCI_REG8(0x0902)
# define IMX214_BINNING_AVERAGE 0x00
# define IMX214_BINNING_SUMMED 0x01
# define IMX214_BINNING_BAYER 0x02
# define IMX214_REG_SING_DEF_CORR_EN CCI_REG8(0x0b06)
# define IMX214_SING_DEF_CORR_OFF 0
# define IMX214_SING_DEF_CORR_ON 1
/* AWB control */
# define IMX214_REG_ABS_GAIN_GREENR CCI_REG16(0x0b8e)
# define IMX214_REG_ABS_GAIN_RED CCI_REG16(0x0b90)
# define IMX214_REG_ABS_GAIN_BLUE CCI_REG16(0x0b92)
# define IMX214_REG_ABS_GAIN_GREENB CCI_REG16(0x0b94)
# define IMX214_REG_RMSC_NR_MODE CCI_REG8(0x3001)
# define IMX214_REG_STATS_OUT_EN CCI_REG8(0x3013)
# define IMX214_STATS_OUT_OFF 0
# define IMX214_STATS_OUT_ON 1
/* Chroma noise reduction */
# define IMX214_REG_NML_NR_EN CCI_REG8(0x30a2)
# define IMX214_NML_NR_OFF 0
# define IMX214_NML_NR_ON 1
# define IMX214_REG_EBD_SIZE_V CCI_REG8(0x5041)
# define IMX214_EBD_NO 0
# define IMX214_EBD_4_LINE 4
# define IMX214_REG_RG_STATS_LMT CCI_REG16(0x6d12)
# define IMX214_RG_STATS_LMT_10_BIT 0x03FF
# define IMX214_RG_STATS_LMT_14_BIT 0x3FFF
# define IMX214_REG_ATR_FAST_MOVE CCI_REG8(0x9300)
2023-12-06 23:33:55 +01:00
2024-12-20 14:26:10 +01:00
/* Test Pattern Control */
# define IMX214_REG_TEST_PATTERN CCI_REG16(0x0600)
# define IMX214_TEST_PATTERN_DISABLE 0
# define IMX214_TEST_PATTERN_SOLID_COLOR 1
# define IMX214_TEST_PATTERN_COLOR_BARS 2
# define IMX214_TEST_PATTERN_GREY_COLOR 3
# define IMX214_TEST_PATTERN_PN9 4
/* Test pattern colour components */
# define IMX214_REG_TESTP_RED CCI_REG16(0x0602)
# define IMX214_REG_TESTP_GREENR CCI_REG16(0x0604)
# define IMX214_REG_TESTP_BLUE CCI_REG16(0x0606)
# define IMX214_REG_TESTP_GREENB CCI_REG16(0x0608)
# define IMX214_TESTP_COLOUR_MIN 0
# define IMX214_TESTP_COLOUR_MAX 0x03ff
# define IMX214_TESTP_COLOUR_STEP 1
2023-12-06 23:33:58 +01:00
/* IMX214 native and active pixel array size */
# define IMX214_NATIVE_WIDTH 4224U
# define IMX214_NATIVE_HEIGHT 3136U
# define IMX214_PIXEL_ARRAY_LEFT 8U
# define IMX214_PIXEL_ARRAY_TOP 8U
# define IMX214_PIXEL_ARRAY_WIDTH 4208U
# define IMX214_PIXEL_ARRAY_HEIGHT 3120U
2018-10-05 18:58:31 -04:00
static const char * const imx214_supply_name [ ] = {
" vdda " ,
" vddd " ,
" vdddo " ,
} ;
# define IMX214_NUM_SUPPLIES ARRAY_SIZE(imx214_supply_name)
2024-12-20 14:26:07 +01:00
/*
* The supported formats .
* This table MUST contain 4 entries per format , to cover the various flip
* combinations in the order
* - no flip
* - h flip
* - v flip
* - h & v flips
*/
static const u32 imx214_mbus_formats [ ] = {
MEDIA_BUS_FMT_SRGGB10_1X10 ,
MEDIA_BUS_FMT_SGRBG10_1X10 ,
MEDIA_BUS_FMT_SGBRG10_1X10 ,
MEDIA_BUS_FMT_SBGGR10_1X10 ,
} ;
2024-12-20 14:26:10 +01:00
static const char * const imx214_test_pattern_menu [ ] = {
" Disabled " ,
" Color Bars " ,
" Solid Color " ,
" Grey Color Bars " ,
" PN9 "
} ;
static const int imx214_test_pattern_val [ ] = {
IMX214_TEST_PATTERN_DISABLE ,
IMX214_TEST_PATTERN_COLOR_BARS ,
IMX214_TEST_PATTERN_SOLID_COLOR ,
IMX214_TEST_PATTERN_GREY_COLOR ,
IMX214_TEST_PATTERN_PN9 ,
} ;
2018-10-05 18:58:31 -04:00
struct imx214 {
struct device * dev ;
struct clk * xclk ;
struct regmap * regmap ;
2025-05-24 11:14:38 +02:00
struct ccs_pll pll ;
struct v4l2_fwnode_endpoint bus_cfg ;
2018-10-05 18:58:31 -04:00
struct v4l2_subdev sd ;
struct media_pad pad ;
struct v4l2_ctrl_handler ctrls ;
struct v4l2_ctrl * pixel_rate ;
struct v4l2_ctrl * link_freq ;
2024-12-20 14:26:06 +01:00
struct v4l2_ctrl * vblank ;
struct v4l2_ctrl * hblank ;
2018-10-05 18:58:31 -04:00
struct v4l2_ctrl * exposure ;
2019-10-07 12:06:36 -03:00
struct v4l2_ctrl * unit_size ;
2024-12-20 14:26:07 +01:00
struct {
struct v4l2_ctrl * hflip ;
struct v4l2_ctrl * vflip ;
} ;
2018-10-05 18:58:31 -04:00
struct regulator_bulk_data supplies [ IMX214_NUM_SUPPLIES ] ;
struct gpio_desc * enable_gpio ;
} ;
/*From imx214_mode_tbls.h*/
2024-12-20 14:26:02 +01:00
static const struct cci_reg_sequence mode_4096x2304 [ ] = {
2024-12-20 14:26:03 +01:00
{ IMX214_REG_HDR_MODE , IMX214_HDR_MODE_OFF } ,
{ IMX214_REG_HDR_RES_REDUCTION , IMX214_HDR_RES_REDU_THROUGH } ,
{ IMX214_REG_EXPOSURE_RATIO , 1 } ,
{ IMX214_REG_X_ADD_STA , 56 } ,
{ IMX214_REG_Y_ADD_STA , 408 } ,
{ IMX214_REG_X_ADD_END , 4151 } ,
{ IMX214_REG_Y_ADD_END , 2711 } ,
{ IMX214_REG_X_EVEN_INC , 1 } ,
{ IMX214_REG_X_ODD_INC , 1 } ,
{ IMX214_REG_Y_EVEN_INC , 1 } ,
{ IMX214_REG_Y_ODD_INC , 1 } ,
{ IMX214_REG_BINNING_MODE , IMX214_BINNING_NONE } ,
{ IMX214_REG_BINNING_TYPE , 0 } ,
{ IMX214_REG_BINNING_WEIGHTING , IMX214_BINNING_AVERAGE } ,
2024-12-20 14:26:02 +01:00
{ CCI_REG8 ( 0x3000 ) , 0x35 } ,
{ CCI_REG8 ( 0x3054 ) , 0x01 } ,
{ CCI_REG8 ( 0x305C ) , 0x11 } ,
2024-12-20 14:26:03 +01:00
{ IMX214_REG_CSI_DATA_FORMAT , IMX214_CSI_DATA_FORMAT_RAW10 } ,
{ IMX214_REG_X_OUTPUT_SIZE , 4096 } ,
{ IMX214_REG_Y_OUTPUT_SIZE , 2304 } ,
{ IMX214_REG_SCALE_MODE , IMX214_SCALE_NONE } ,
{ IMX214_REG_SCALE_M , 2 } ,
{ IMX214_REG_DIG_CROP_X_OFFSET , 0 } ,
{ IMX214_REG_DIG_CROP_Y_OFFSET , 0 } ,
{ IMX214_REG_DIG_CROP_WIDTH , 4096 } ,
{ IMX214_REG_DIG_CROP_HEIGHT , 2304 } ,
2024-12-20 14:26:02 +01:00
{ CCI_REG8 ( 0x3A03 ) , 0x09 } ,
{ CCI_REG8 ( 0x3A04 ) , 0x50 } ,
{ CCI_REG8 ( 0x3A05 ) , 0x01 } ,
2024-12-20 14:26:03 +01:00
{ IMX214_REG_SING_DEF_CORR_EN , IMX214_SING_DEF_CORR_ON } ,
{ IMX214_REG_NML_NR_EN , IMX214_NML_NR_OFF } ,
2024-12-20 14:26:02 +01:00
{ CCI_REG8 ( 0x30B4 ) , 0x00 } ,
{ CCI_REG8 ( 0x3A02 ) , 0xFF } ,
{ CCI_REG8 ( 0x3011 ) , 0x00 } ,
2024-12-20 14:26:03 +01:00
{ IMX214_REG_STATS_OUT_EN , IMX214_STATS_OUT_ON } ,
{ IMX214_REG_SHORT_EXPOSURE , 500 } ,
2024-12-20 14:26:02 +01:00
{ CCI_REG8 ( 0x4170 ) , 0x00 } ,
{ CCI_REG8 ( 0x4171 ) , 0x10 } ,
{ CCI_REG8 ( 0x4176 ) , 0x00 } ,
{ CCI_REG8 ( 0x4177 ) , 0x3C } ,
{ CCI_REG8 ( 0xAE20 ) , 0x04 } ,
{ CCI_REG8 ( 0xAE21 ) , 0x5C } ,
2018-10-05 18:58:31 -04:00
} ;
2024-12-20 14:26:02 +01:00
static const struct cci_reg_sequence mode_1920x1080 [ ] = {
2024-12-20 14:26:03 +01:00
{ IMX214_REG_HDR_MODE , IMX214_HDR_MODE_OFF } ,
{ IMX214_REG_HDR_RES_REDUCTION , IMX214_HDR_RES_REDU_THROUGH } ,
{ IMX214_REG_EXPOSURE_RATIO , 1 } ,
{ IMX214_REG_X_ADD_STA , 1144 } ,
{ IMX214_REG_Y_ADD_STA , 1020 } ,
{ IMX214_REG_X_ADD_END , 3063 } ,
{ IMX214_REG_Y_ADD_END , 2099 } ,
{ IMX214_REG_X_EVEN_INC , 1 } ,
{ IMX214_REG_X_ODD_INC , 1 } ,
{ IMX214_REG_Y_EVEN_INC , 1 } ,
{ IMX214_REG_Y_ODD_INC , 1 } ,
{ IMX214_REG_BINNING_MODE , IMX214_BINNING_NONE } ,
{ IMX214_REG_BINNING_TYPE , 0 } ,
{ IMX214_REG_BINNING_WEIGHTING , IMX214_BINNING_AVERAGE } ,
2024-12-20 14:26:02 +01:00
{ CCI_REG8 ( 0x3000 ) , 0x35 } ,
{ CCI_REG8 ( 0x3054 ) , 0x01 } ,
{ CCI_REG8 ( 0x305C ) , 0x11 } ,
2024-12-20 14:26:03 +01:00
{ IMX214_REG_CSI_DATA_FORMAT , IMX214_CSI_DATA_FORMAT_RAW10 } ,
{ IMX214_REG_X_OUTPUT_SIZE , 1920 } ,
{ IMX214_REG_Y_OUTPUT_SIZE , 1080 } ,
{ IMX214_REG_SCALE_MODE , IMX214_SCALE_NONE } ,
{ IMX214_REG_SCALE_M , 2 } ,
{ IMX214_REG_DIG_CROP_X_OFFSET , 0 } ,
{ IMX214_REG_DIG_CROP_Y_OFFSET , 0 } ,
{ IMX214_REG_DIG_CROP_WIDTH , 1920 } ,
{ IMX214_REG_DIG_CROP_HEIGHT , 1080 } ,
2024-12-20 14:26:02 +01:00
{ CCI_REG8 ( 0x3A03 ) , 0x04 } ,
{ CCI_REG8 ( 0x3A04 ) , 0xF8 } ,
{ CCI_REG8 ( 0x3A05 ) , 0x02 } ,
2024-12-20 14:26:03 +01:00
{ IMX214_REG_SING_DEF_CORR_EN , IMX214_SING_DEF_CORR_ON } ,
{ IMX214_REG_NML_NR_EN , IMX214_NML_NR_OFF } ,
2024-12-20 14:26:02 +01:00
{ CCI_REG8 ( 0x30B4 ) , 0x00 } ,
{ CCI_REG8 ( 0x3A02 ) , 0xFF } ,
{ CCI_REG8 ( 0x3011 ) , 0x00 } ,
2024-12-20 14:26:03 +01:00
{ IMX214_REG_STATS_OUT_EN , IMX214_STATS_OUT_ON } ,
{ IMX214_REG_SHORT_EXPOSURE , 500 } ,
2024-12-20 14:26:02 +01:00
{ CCI_REG8 ( 0x4170 ) , 0x00 } ,
{ CCI_REG8 ( 0x4171 ) , 0x10 } ,
{ CCI_REG8 ( 0x4176 ) , 0x00 } ,
{ CCI_REG8 ( 0x4177 ) , 0x3C } ,
{ CCI_REG8 ( 0xAE20 ) , 0x04 } ,
{ CCI_REG8 ( 0xAE21 ) , 0x5C } ,
2018-10-05 18:58:31 -04:00
} ;
2024-12-20 14:26:02 +01:00
static const struct cci_reg_sequence mode_table_common [ ] = {
2018-10-05 18:58:31 -04:00
/* software reset */
/* software standby settings */
2024-12-20 14:26:03 +01:00
{ IMX214_REG_MODE_SELECT , IMX214_MODE_STANDBY } ,
2018-10-05 18:58:31 -04:00
/* ATR setting */
2024-12-20 14:26:03 +01:00
{ IMX214_REG_ATR_FAST_MOVE , 2 } ,
2018-10-05 18:58:31 -04:00
/* global setting */
/* basic config */
2024-12-20 14:26:03 +01:00
{ IMX214_REG_MASK_CORR_FRAMES , IMX214_CORR_FRAMES_MASK } ,
{ IMX214_REG_FAST_STANDBY_CTRL , 1 } ,
2024-12-20 14:26:06 +01:00
{ IMX214_REG_LINE_LENGTH_PCK , IMX214_PPL_DEFAULT } ,
2024-12-20 14:26:02 +01:00
{ CCI_REG8 ( 0x4550 ) , 0x02 } ,
{ CCI_REG8 ( 0x4601 ) , 0x00 } ,
{ CCI_REG8 ( 0x4642 ) , 0x05 } ,
{ CCI_REG8 ( 0x6227 ) , 0x11 } ,
{ CCI_REG8 ( 0x6276 ) , 0x00 } ,
{ CCI_REG8 ( 0x900E ) , 0x06 } ,
{ CCI_REG8 ( 0xA802 ) , 0x90 } ,
{ CCI_REG8 ( 0xA803 ) , 0x11 } ,
{ CCI_REG8 ( 0xA804 ) , 0x62 } ,
{ CCI_REG8 ( 0xA805 ) , 0x77 } ,
{ CCI_REG8 ( 0xA806 ) , 0xAE } ,
{ CCI_REG8 ( 0xA807 ) , 0x34 } ,
{ CCI_REG8 ( 0xA808 ) , 0xAE } ,
{ CCI_REG8 ( 0xA809 ) , 0x35 } ,
{ CCI_REG8 ( 0xA80A ) , 0x62 } ,
{ CCI_REG8 ( 0xA80B ) , 0x83 } ,
{ CCI_REG8 ( 0xAE33 ) , 0x00 } ,
2018-10-05 18:58:31 -04:00
/* analog setting */
2024-12-20 14:26:02 +01:00
{ CCI_REG8 ( 0x4174 ) , 0x00 } ,
{ CCI_REG8 ( 0x4175 ) , 0x11 } ,
{ CCI_REG8 ( 0x4612 ) , 0x29 } ,
{ CCI_REG8 ( 0x461B ) , 0x12 } ,
{ CCI_REG8 ( 0x461F ) , 0x06 } ,
{ CCI_REG8 ( 0x4635 ) , 0x07 } ,
{ CCI_REG8 ( 0x4637 ) , 0x30 } ,
{ CCI_REG8 ( 0x463F ) , 0x18 } ,
{ CCI_REG8 ( 0x4641 ) , 0x0D } ,
{ CCI_REG8 ( 0x465B ) , 0x12 } ,
{ CCI_REG8 ( 0x465F ) , 0x11 } ,
{ CCI_REG8 ( 0x4663 ) , 0x11 } ,
{ CCI_REG8 ( 0x4667 ) , 0x0F } ,
{ CCI_REG8 ( 0x466F ) , 0x0F } ,
{ CCI_REG8 ( 0x470E ) , 0x09 } ,
{ CCI_REG8 ( 0x4909 ) , 0xAB } ,
{ CCI_REG8 ( 0x490B ) , 0x95 } ,
{ CCI_REG8 ( 0x4915 ) , 0x5D } ,
{ CCI_REG8 ( 0x4A5F ) , 0xFF } ,
{ CCI_REG8 ( 0x4A61 ) , 0xFF } ,
{ CCI_REG8 ( 0x4A73 ) , 0x62 } ,
{ CCI_REG8 ( 0x4A85 ) , 0x00 } ,
{ CCI_REG8 ( 0x4A87 ) , 0xFF } ,
2018-10-05 18:58:31 -04:00
/* embedded data */
2024-12-20 14:26:03 +01:00
{ IMX214_REG_EBD_SIZE_V , IMX214_EBD_4_LINE } ,
2024-12-20 14:26:02 +01:00
{ CCI_REG8 ( 0x583C ) , 0x04 } ,
{ CCI_REG8 ( 0x620E ) , 0x04 } ,
{ CCI_REG8 ( 0x6EB2 ) , 0x01 } ,
{ CCI_REG8 ( 0x6EB3 ) , 0x00 } ,
2024-12-20 14:26:03 +01:00
{ IMX214_REG_ATR_FAST_MOVE , 2 } ,
2018-10-05 18:58:31 -04:00
/* imagequality */
/* HDR setting */
2024-12-20 14:26:03 +01:00
{ IMX214_REG_RMSC_NR_MODE , 0x07 } ,
{ IMX214_REG_RG_STATS_LMT , IMX214_RG_STATS_LMT_14_BIT } ,
2024-12-20 14:26:02 +01:00
{ CCI_REG8 ( 0x9344 ) , 0x03 } ,
{ CCI_REG8 ( 0x9706 ) , 0x10 } ,
{ CCI_REG8 ( 0x9707 ) , 0x03 } ,
{ CCI_REG8 ( 0x9708 ) , 0x03 } ,
{ CCI_REG8 ( 0x9E04 ) , 0x01 } ,
{ CCI_REG8 ( 0x9E05 ) , 0x00 } ,
{ CCI_REG8 ( 0x9E0C ) , 0x01 } ,
{ CCI_REG8 ( 0x9E0D ) , 0x02 } ,
{ CCI_REG8 ( 0x9E24 ) , 0x00 } ,
{ CCI_REG8 ( 0x9E25 ) , 0x8C } ,
{ CCI_REG8 ( 0x9E26 ) , 0x00 } ,
{ CCI_REG8 ( 0x9E27 ) , 0x94 } ,
{ CCI_REG8 ( 0x9E28 ) , 0x00 } ,
{ CCI_REG8 ( 0x9E29 ) , 0x96 } ,
2018-10-05 18:58:31 -04:00
/* CNR parameter setting */
2024-12-20 14:26:02 +01:00
{ CCI_REG8 ( 0x69DB ) , 0x01 } ,
2018-10-05 18:58:31 -04:00
/* Moire reduction */
2024-12-20 14:26:02 +01:00
{ CCI_REG8 ( 0x6957 ) , 0x01 } ,
2018-10-05 18:58:31 -04:00
2019-02-18 14:28:58 -05:00
/* image enhancement */
2024-12-20 14:26:02 +01:00
{ CCI_REG8 ( 0x6987 ) , 0x17 } ,
{ CCI_REG8 ( 0x698A ) , 0x03 } ,
{ CCI_REG8 ( 0x698B ) , 0x03 } ,
2018-10-05 18:58:31 -04:00
/* white balanace */
2024-12-20 14:26:03 +01:00
{ IMX214_REG_ABS_GAIN_GREENR , 0x0100 } ,
{ IMX214_REG_ABS_GAIN_RED , 0x0100 } ,
{ IMX214_REG_ABS_GAIN_BLUE , 0x0100 } ,
{ IMX214_REG_ABS_GAIN_GREENB , 0x0100 } ,
2018-10-05 18:58:31 -04:00
/* ATR setting */
2024-12-20 14:26:02 +01:00
{ CCI_REG8 ( 0x6E50 ) , 0x00 } ,
{ CCI_REG8 ( 0x6E51 ) , 0x32 } ,
{ CCI_REG8 ( 0x9340 ) , 0x00 } ,
{ CCI_REG8 ( 0x9341 ) , 0x3C } ,
{ CCI_REG8 ( 0x9342 ) , 0x03 } ,
{ CCI_REG8 ( 0x9343 ) , 0xFF } ,
2018-10-05 18:58:31 -04:00
} ;
/*
* Declare modes in order , from biggest
* to smallest height .
*/
static const struct imx214_mode {
u32 width ;
u32 height ;
2024-12-20 14:26:06 +01:00
/* V-timing */
unsigned int vts_def ;
2024-12-20 14:26:02 +01:00
unsigned int num_of_regs ;
const struct cci_reg_sequence * reg_table ;
2018-10-05 18:58:31 -04:00
} imx214_modes [ ] = {
{
. width = 4096 ,
. height = 2304 ,
2024-12-20 14:26:06 +01:00
. vts_def = 3194 ,
2024-12-20 14:26:02 +01:00
. num_of_regs = ARRAY_SIZE ( mode_4096x2304 ) ,
2018-10-05 18:58:31 -04:00
. reg_table = mode_4096x2304 ,
} ,
{
. width = 1920 ,
. height = 1080 ,
2024-12-20 14:26:06 +01:00
. vts_def = 3194 ,
2024-12-20 14:26:02 +01:00
. num_of_regs = ARRAY_SIZE ( mode_1920x1080 ) ,
2018-10-05 18:58:31 -04:00
. reg_table = mode_1920x1080 ,
} ,
} ;
static inline struct imx214 * to_imx214 ( struct v4l2_subdev * sd )
{
return container_of ( sd , struct imx214 , sd ) ;
}
static int __maybe_unused imx214_power_on ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
struct imx214 * imx214 = to_imx214 ( sd ) ;
int ret ;
ret = regulator_bulk_enable ( IMX214_NUM_SUPPLIES , imx214 - > supplies ) ;
if ( ret < 0 ) {
dev_err ( imx214 - > dev , " failed to enable regulators: %d \n " , ret ) ;
return ret ;
}
usleep_range ( 2000 , 3000 ) ;
ret = clk_prepare_enable ( imx214 - > xclk ) ;
if ( ret < 0 ) {
regulator_bulk_disable ( IMX214_NUM_SUPPLIES , imx214 - > supplies ) ;
dev_err ( imx214 - > dev , " clk prepare enable failed \n " ) ;
return ret ;
}
gpiod_set_value_cansleep ( imx214 - > enable_gpio , 1 ) ;
usleep_range ( 12000 , 15000 ) ;
return 0 ;
}
static int __maybe_unused imx214_power_off ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
struct imx214 * imx214 = to_imx214 ( sd ) ;
gpiod_set_value_cansleep ( imx214 - > enable_gpio , 0 ) ;
clk_disable_unprepare ( imx214 - > xclk ) ;
regulator_bulk_disable ( IMX214_NUM_SUPPLIES , imx214 - > supplies ) ;
usleep_range ( 10 , 20 ) ;
return 0 ;
}
2024-12-20 14:26:07 +01:00
/* Get bayer order based on flip setting. */
static u32 imx214_get_format_code ( struct imx214 * imx214 )
{
unsigned int i ;
i = ( imx214 - > vflip - > val ? 2 : 0 ) | ( imx214 - > hflip - > val ? 1 : 0 ) ;
return imx214_mbus_formats [ i ] ;
}
2024-12-20 14:26:00 +01:00
static void imx214_update_pad_format ( struct imx214 * imx214 ,
const struct imx214_mode * mode ,
struct v4l2_mbus_framefmt * fmt , u32 code )
{
2024-12-20 14:26:07 +01:00
fmt - > code = imx214_get_format_code ( imx214 ) ;
2024-12-20 14:26:00 +01:00
fmt - > width = mode - > width ;
fmt - > height = mode - > height ;
fmt - > field = V4L2_FIELD_NONE ;
fmt - > colorspace = V4L2_COLORSPACE_SRGB ;
fmt - > ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT ( fmt - > colorspace ) ;
fmt - > quantization = V4L2_MAP_QUANTIZATION_DEFAULT ( true ,
fmt - > colorspace ,
fmt - > ycbcr_enc ) ;
fmt - > xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT ( fmt - > colorspace ) ;
}
2018-10-05 18:58:31 -04:00
static int imx214_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 ,
2018-10-05 18:58:31 -04:00
struct v4l2_subdev_mbus_code_enum * code )
{
2024-12-20 14:26:07 +01:00
struct imx214 * imx214 = to_imx214 ( sd ) ;
if ( code - > index > = ( ARRAY_SIZE ( imx214_mbus_formats ) / 4 ) )
2018-10-05 18:58:31 -04:00
return - EINVAL ;
2024-12-20 14:26:07 +01:00
code - > code = imx214_get_format_code ( imx214 ) ;
2018-10-05 18:58:31 -04:00
return 0 ;
}
static int imx214_enum_frame_size ( struct v4l2_subdev * subdev ,
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 17:55:58 +03:00
struct v4l2_subdev_state * sd_state ,
2018-10-05 18:58:31 -04:00
struct v4l2_subdev_frame_size_enum * fse )
{
2024-12-20 14:26:07 +01:00
struct imx214 * imx214 = to_imx214 ( subdev ) ;
u32 code ;
code = imx214_get_format_code ( imx214 ) ;
if ( fse - > code ! = code )
2018-10-05 18:58:31 -04:00
return - EINVAL ;
if ( fse - > index > = ARRAY_SIZE ( imx214_modes ) )
return - EINVAL ;
fse - > min_width = fse - > max_width = imx214_modes [ fse - > index ] . width ;
fse - > min_height = fse - > max_height = imx214_modes [ fse - > index ] . height ;
return 0 ;
}
# ifdef CONFIG_VIDEO_ADV_DEBUG
static int imx214_s_register ( struct v4l2_subdev * subdev ,
const struct v4l2_dbg_register * reg )
{
struct imx214 * imx214 = container_of ( subdev , struct imx214 , sd ) ;
return regmap_write ( imx214 - > regmap , reg - > reg , reg - > val ) ;
}
static int imx214_g_register ( struct v4l2_subdev * subdev ,
struct v4l2_dbg_register * reg )
{
struct imx214 * imx214 = container_of ( subdev , struct imx214 , sd ) ;
unsigned int aux ;
int ret ;
reg - > size = 1 ;
ret = regmap_read ( imx214 - > regmap , reg - > reg , & aux ) ;
reg - > val = aux ;
return ret ;
}
# endif
static const struct v4l2_subdev_core_ops imx214_core_ops = {
# ifdef CONFIG_VIDEO_ADV_DEBUG
. g_register = imx214_g_register ,
. s_register = imx214_s_register ,
# endif
} ;
static int imx214_set_format ( struct v4l2_subdev * sd ,
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 17:55:58 +03:00
struct v4l2_subdev_state * sd_state ,
2018-10-05 18:58:31 -04:00
struct v4l2_subdev_format * format )
{
struct imx214 * imx214 = to_imx214 ( sd ) ;
struct v4l2_mbus_framefmt * __format ;
struct v4l2_rect * __crop ;
const struct imx214_mode * mode ;
2018-12-07 06:43:03 -05:00
mode = v4l2_find_nearest_size ( imx214_modes ,
ARRAY_SIZE ( imx214_modes ) , width , height ,
format - > format . width ,
format - > format . height ) ;
2018-10-05 18:58:31 -04:00
2024-12-20 14:26:00 +01:00
imx214_update_pad_format ( imx214 , mode , & format - > format ,
format - > format . code ) ;
__format = v4l2_subdev_state_get_format ( sd_state , 0 ) ;
2018-10-05 18:58:31 -04:00
2024-12-20 14:26:00 +01:00
* __format = format - > format ;
2018-10-05 18:58:31 -04:00
2024-12-20 14:26:00 +01:00
__crop = v4l2_subdev_state_get_crop ( sd_state , 0 ) ;
__crop - > width = mode - > width ;
__crop - > height = mode - > height ;
2018-10-05 18:58:31 -04:00
2024-12-20 14:26:06 +01:00
if ( format - > which = = V4L2_SUBDEV_FORMAT_ACTIVE ) {
int exposure_max ;
int exposure_def ;
int hblank ;
/* Update blank limits */
__v4l2_ctrl_modify_range ( imx214 - > vblank , IMX214_VBLANK_MIN ,
IMX214_VTS_MAX - mode - > height , 2 ,
mode - > vts_def - mode - > height ) ;
/* Update max exposure while meeting expected vblanking */
exposure_max = mode - > vts_def - IMX214_EXPOSURE_OFFSET ;
exposure_def = min ( exposure_max , IMX214_EXPOSURE_DEFAULT ) ;
__v4l2_ctrl_modify_range ( imx214 - > exposure ,
imx214 - > exposure - > minimum ,
exposure_max , imx214 - > exposure - > step ,
exposure_def ) ;
/*
* Currently PPL is fixed to IMX214_PPL_DEFAULT , so hblank
* depends on mode - > width only , and is not changeable in any
* way other than changing the mode .
*/
hblank = IMX214_PPL_DEFAULT - mode - > width ;
__v4l2_ctrl_modify_range ( imx214 - > hblank , hblank , hblank , 1 ,
hblank ) ;
}
2018-10-05 18:58:31 -04:00
return 0 ;
}
static int imx214_get_selection ( struct v4l2_subdev * sd ,
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 17:55:58 +03:00
struct v4l2_subdev_state * sd_state ,
2018-10-05 18:58:31 -04:00
struct v4l2_subdev_selection * sel )
{
2023-12-06 23:33:58 +01:00
switch ( sel - > target ) {
case V4L2_SEL_TGT_CROP :
2024-12-20 14:26:00 +01:00
sel - > r = * v4l2_subdev_state_get_crop ( sd_state , 0 ) ;
2023-12-06 23:33:58 +01:00
return 0 ;
case V4L2_SEL_TGT_NATIVE_SIZE :
sel - > r . top = 0 ;
sel - > r . left = 0 ;
sel - > r . width = IMX214_NATIVE_WIDTH ;
sel - > r . height = IMX214_NATIVE_HEIGHT ;
return 0 ;
case V4L2_SEL_TGT_CROP_DEFAULT :
case V4L2_SEL_TGT_CROP_BOUNDS :
sel - > r . top = IMX214_PIXEL_ARRAY_TOP ;
sel - > r . left = IMX214_PIXEL_ARRAY_LEFT ;
sel - > r . width = IMX214_PIXEL_ARRAY_WIDTH ;
sel - > r . height = IMX214_PIXEL_ARRAY_HEIGHT ;
return 0 ;
}
2018-10-05 18:58:31 -04:00
2023-12-06 23:33:58 +01:00
return - EINVAL ;
2018-10-05 18:58:31 -04:00
}
2023-11-27 11:07:44 +02:00
static int imx214_entity_init_state ( struct v4l2_subdev * subdev ,
struct v4l2_subdev_state * sd_state )
2018-10-05 18:58:31 -04:00
{
struct v4l2_subdev_format fmt = { } ;
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
fmt . which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE ;
2024-12-20 14:26:07 +01:00
fmt . format . code = MEDIA_BUS_FMT_SRGGB10_1X10 ;
2018-10-05 18:58:31 -04:00
fmt . format . width = imx214_modes [ 0 ] . width ;
fmt . format . height = imx214_modes [ 0 ] . height ;
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
imx214_set_format ( subdev , sd_state , & fmt ) ;
2018-10-05 18:58:31 -04:00
return 0 ;
}
2025-05-24 11:14:37 +02:00
static int imx214_configure_pll ( struct imx214 * imx214 )
{
int ret = 0 ;
2025-05-24 11:14:38 +02:00
cci_write ( imx214 - > regmap , IMX214_REG_VTPXCK_DIV ,
imx214 - > pll . vt_bk . pix_clk_div , & ret ) ;
cci_write ( imx214 - > regmap , IMX214_REG_VTSYCK_DIV ,
imx214 - > pll . vt_bk . sys_clk_div , & ret ) ;
cci_write ( imx214 - > regmap , IMX214_REG_PREPLLCK_VT_DIV ,
imx214 - > pll . vt_fr . pre_pll_clk_div , & ret ) ;
cci_write ( imx214 - > regmap , IMX214_REG_PLL_VT_MPY ,
imx214 - > pll . vt_fr . pll_multiplier , & ret ) ;
cci_write ( imx214 - > regmap , IMX214_REG_OPPXCK_DIV ,
imx214 - > pll . op_bk . pix_clk_div , & ret ) ;
cci_write ( imx214 - > regmap , IMX214_REG_OPSYCK_DIV ,
imx214 - > pll . op_bk . sys_clk_div , & ret ) ;
2025-05-24 11:14:37 +02:00
cci_write ( imx214 - > regmap , IMX214_REG_PLL_MULT_DRIV ,
IMX214_PLL_SINGLE , & ret ) ;
cci_write ( imx214 - > regmap , IMX214_REG_EXCK_FREQ ,
2025-05-24 11:14:38 +02:00
IMX214_EXCK_FREQ ( imx214 - > pll . ext_clk_freq_hz / 1000000 ) , & ret ) ;
2025-05-24 11:14:37 +02:00
return ret ;
}
2024-12-20 14:26:08 +01:00
static int imx214_update_digital_gain ( struct imx214 * imx214 , u32 val )
{
int ret = 0 ;
cci_write ( imx214 - > regmap , IMX214_REG_DIG_GAIN_GREENR , val , & ret ) ;
cci_write ( imx214 - > regmap , IMX214_REG_DIG_GAIN_RED , val , & ret ) ;
cci_write ( imx214 - > regmap , IMX214_REG_DIG_GAIN_BLUE , val , & ret ) ;
cci_write ( imx214 - > regmap , IMX214_REG_DIG_GAIN_GREENB , val , & ret ) ;
return ret ;
}
2018-10-05 18:58:31 -04:00
static int imx214_set_ctrl ( struct v4l2_ctrl * ctrl )
{
struct imx214 * imx214 = container_of ( ctrl - > handler ,
struct imx214 , ctrls ) ;
2024-12-20 14:26:06 +01:00
const struct v4l2_mbus_framefmt * format = NULL ;
struct v4l2_subdev_state * state ;
2025-02-18 16:05:50 +03:00
int ret = 0 ;
2018-10-05 18:58:31 -04:00
2024-12-20 14:26:06 +01:00
if ( ctrl - > id = = V4L2_CID_VBLANK ) {
int exposure_max , exposure_def ;
state = v4l2_subdev_get_locked_active_state ( & imx214 - > sd ) ;
format = v4l2_subdev_state_get_format ( state , 0 ) ;
/* Update max exposure while meeting expected vblanking */
exposure_max =
format - > height + ctrl - > val - IMX214_EXPOSURE_OFFSET ;
exposure_def = min ( exposure_max , IMX214_EXPOSURE_DEFAULT ) ;
__v4l2_ctrl_modify_range ( imx214 - > exposure ,
imx214 - > exposure - > minimum ,
exposure_max , imx214 - > exposure - > step ,
exposure_def ) ;
}
2018-10-05 18:58:31 -04:00
/*
* Applying V4L2 control value only happens
* when power is up for streaming
*/
if ( ! pm_runtime_get_if_in_use ( imx214 - > dev ) )
return 0 ;
switch ( ctrl - > id ) {
2024-12-20 14:26:08 +01:00
case V4L2_CID_ANALOGUE_GAIN :
cci_write ( imx214 - > regmap , IMX214_REG_ANALOG_GAIN ,
ctrl - > val , & ret ) ;
cci_write ( imx214 - > regmap , IMX214_REG_SHORT_ANALOG_GAIN ,
ctrl - > val , & ret ) ;
break ;
case V4L2_CID_DIGITAL_GAIN :
ret = imx214_update_digital_gain ( imx214 , ctrl - > val ) ;
break ;
2018-10-05 18:58:31 -04:00
case V4L2_CID_EXPOSURE :
2024-12-20 14:26:02 +01:00
cci_write ( imx214 - > regmap , IMX214_REG_EXPOSURE , ctrl - > val , & ret ) ;
2018-10-05 18:58:31 -04:00
break ;
2024-12-20 14:26:07 +01:00
case V4L2_CID_HFLIP :
case V4L2_CID_VFLIP :
cci_write ( imx214 - > regmap , IMX214_REG_ORIENTATION ,
imx214 - > hflip - > val | imx214 - > vflip - > val < < 1 , & ret ) ;
break ;
2024-12-20 14:26:06 +01:00
case V4L2_CID_VBLANK :
cci_write ( imx214 - > regmap , IMX214_REG_FRM_LENGTH_LINES ,
format - > height + ctrl - > val , & ret ) ;
break ;
2024-12-20 14:26:10 +01:00
case V4L2_CID_TEST_PATTERN :
cci_write ( imx214 - > regmap , IMX214_REG_TEST_PATTERN ,
imx214_test_pattern_val [ ctrl - > val ] , & ret ) ;
break ;
case V4L2_CID_TEST_PATTERN_RED :
cci_write ( imx214 - > regmap , IMX214_REG_TESTP_RED ,
ctrl - > val , & ret ) ;
break ;
case V4L2_CID_TEST_PATTERN_GREENR :
cci_write ( imx214 - > regmap , IMX214_REG_TESTP_GREENR ,
ctrl - > val , & ret ) ;
break ;
case V4L2_CID_TEST_PATTERN_BLUE :
cci_write ( imx214 - > regmap , IMX214_REG_TESTP_BLUE ,
ctrl - > val , & ret ) ;
break ;
case V4L2_CID_TEST_PATTERN_GREENB :
cci_write ( imx214 - > regmap , IMX214_REG_TESTP_GREENB ,
ctrl - > val , & ret ) ;
break ;
2018-10-05 18:58:31 -04:00
default :
ret = - EINVAL ;
}
pm_runtime_put ( imx214 - > dev ) ;
return ret ;
}
static const struct v4l2_ctrl_ops imx214_ctrl_ops = {
. s_ctrl = imx214_set_ctrl ,
} ;
2023-12-06 23:33:56 +01:00
static int imx214_ctrls_init ( struct imx214 * imx214 )
{
static const struct v4l2_area unit_size = {
. width = 1120 ,
. height = 1120 ,
} ;
2024-12-20 14:26:06 +01:00
const struct imx214_mode * mode = & imx214_modes [ 0 ] ;
2023-12-06 23:33:57 +01:00
struct v4l2_fwnode_device_properties props ;
2023-12-06 23:33:56 +01:00
struct v4l2_ctrl_handler * ctrl_hdlr ;
2024-12-20 14:26:06 +01:00
int exposure_max , exposure_def ;
int hblank ;
2024-12-20 14:26:10 +01:00
int i , ret ;
2023-12-06 23:33:56 +01:00
2023-12-06 23:33:57 +01:00
ret = v4l2_fwnode_device_parse ( imx214 - > dev , & props ) ;
if ( ret < 0 )
return ret ;
2023-12-06 23:33:56 +01:00
ctrl_hdlr = & imx214 - > ctrls ;
2024-12-20 14:26:10 +01:00
ret = v4l2_ctrl_handler_init ( & imx214 - > ctrls , 13 ) ;
2023-12-06 23:33:56 +01:00
if ( ret )
return ret ;
2025-05-24 11:14:38 +02:00
imx214 - > pixel_rate =
v4l2_ctrl_new_std ( ctrl_hdlr , NULL , V4L2_CID_PIXEL_RATE , 1 ,
INT_MAX , 1 , 1 ) ;
2023-12-06 23:33:56 +01:00
imx214 - > link_freq = v4l2_ctrl_new_int_menu ( ctrl_hdlr , NULL ,
V4L2_CID_LINK_FREQ ,
2025-05-24 11:14:38 +02:00
imx214 - > bus_cfg . nr_of_link_frequencies - 1 ,
0 , imx214 - > bus_cfg . link_frequencies ) ;
2023-12-06 23:33:56 +01:00
if ( imx214 - > link_freq )
imx214 - > link_freq - > flags | = V4L2_CTRL_FLAG_READ_ONLY ;
/*
* WARNING !
* Values obtained reverse engineering blobs and / or devices .
* Ranges and functionality might be wrong .
*
* Sony , please release some register set documentation for the
* device .
*
* Yours sincerely , Ricardo .
*/
2024-12-20 14:26:06 +01:00
/* Initial vblank/hblank/exposure parameters based on current mode */
imx214 - > vblank = v4l2_ctrl_new_std ( ctrl_hdlr , & imx214_ctrl_ops ,
V4L2_CID_VBLANK , IMX214_VBLANK_MIN ,
IMX214_VTS_MAX - mode - > height , 2 ,
mode - > vts_def - mode - > height ) ;
hblank = IMX214_PPL_DEFAULT - mode - > width ;
imx214 - > hblank = v4l2_ctrl_new_std ( ctrl_hdlr , & imx214_ctrl_ops ,
V4L2_CID_HBLANK , hblank , hblank ,
1 , hblank ) ;
if ( imx214 - > hblank )
imx214 - > hblank - > flags | = V4L2_CTRL_FLAG_READ_ONLY ;
exposure_max = mode - > vts_def - IMX214_EXPOSURE_OFFSET ;
exposure_def = min ( exposure_max , IMX214_EXPOSURE_DEFAULT ) ;
2023-12-06 23:33:56 +01:00
imx214 - > exposure = v4l2_ctrl_new_std ( ctrl_hdlr , & imx214_ctrl_ops ,
V4L2_CID_EXPOSURE ,
IMX214_EXPOSURE_MIN ,
2024-12-20 14:26:06 +01:00
exposure_max ,
2023-12-06 23:33:56 +01:00
IMX214_EXPOSURE_STEP ,
2024-12-20 14:26:06 +01:00
exposure_def ) ;
2023-12-06 23:33:56 +01:00
2024-12-20 14:26:08 +01:00
v4l2_ctrl_new_std ( ctrl_hdlr , & imx214_ctrl_ops , V4L2_CID_ANALOGUE_GAIN ,
IMX214_ANA_GAIN_MIN , IMX214_ANA_GAIN_MAX ,
IMX214_ANA_GAIN_STEP , IMX214_ANA_GAIN_DEFAULT ) ;
v4l2_ctrl_new_std ( ctrl_hdlr , & imx214_ctrl_ops , V4L2_CID_DIGITAL_GAIN ,
IMX214_DGTL_GAIN_MIN , IMX214_DGTL_GAIN_MAX ,
IMX214_DGTL_GAIN_STEP , IMX214_DGTL_GAIN_DEFAULT ) ;
2024-12-20 14:26:07 +01:00
imx214 - > hflip = v4l2_ctrl_new_std ( ctrl_hdlr , & imx214_ctrl_ops ,
V4L2_CID_HFLIP , 0 , 1 , 1 , 0 ) ;
if ( imx214 - > hflip )
imx214 - > hflip - > flags | = V4L2_CTRL_FLAG_MODIFY_LAYOUT ;
imx214 - > vflip = v4l2_ctrl_new_std ( ctrl_hdlr , & imx214_ctrl_ops ,
V4L2_CID_VFLIP , 0 , 1 , 1 , 0 ) ;
if ( imx214 - > vflip )
imx214 - > vflip - > flags | = V4L2_CTRL_FLAG_MODIFY_LAYOUT ;
v4l2_ctrl_cluster ( 2 , & imx214 - > hflip ) ;
2024-12-20 14:26:10 +01:00
v4l2_ctrl_new_std_menu_items ( ctrl_hdlr , & imx214_ctrl_ops ,
V4L2_CID_TEST_PATTERN ,
ARRAY_SIZE ( imx214_test_pattern_menu ) - 1 ,
0 , 0 , imx214_test_pattern_menu ) ;
for ( i = 0 ; i < 4 ; i + + ) {
/*
* The assumption is that
* V4L2_CID_TEST_PATTERN_GREENR = = V4L2_CID_TEST_PATTERN_RED + 1
* V4L2_CID_TEST_PATTERN_BLUE = = V4L2_CID_TEST_PATTERN_RED + 2
* V4L2_CID_TEST_PATTERN_GREENB = = V4L2_CID_TEST_PATTERN_RED + 3
*/
v4l2_ctrl_new_std ( ctrl_hdlr , & imx214_ctrl_ops ,
V4L2_CID_TEST_PATTERN_RED + i ,
IMX214_TESTP_COLOUR_MIN ,
IMX214_TESTP_COLOUR_MAX ,
IMX214_TESTP_COLOUR_STEP ,
IMX214_TESTP_COLOUR_MAX ) ;
/* The "Solid color" pattern is white by default */
}
2023-12-06 23:33:56 +01:00
imx214 - > unit_size = v4l2_ctrl_new_std_compound ( ctrl_hdlr ,
NULL ,
V4L2_CID_UNIT_CELL_SIZE ,
2025-02-03 11:55:38 +00:00
v4l2_ctrl_ptr_create ( ( void * ) & unit_size ) ,
v4l2_ctrl_ptr_create ( NULL ) ,
v4l2_ctrl_ptr_create ( NULL ) ) ;
2023-12-06 23:33:56 +01:00
2023-12-06 23:33:57 +01:00
v4l2_ctrl_new_fwnode_properties ( ctrl_hdlr , & imx214_ctrl_ops , & props ) ;
2023-12-06 23:33:56 +01:00
ret = ctrl_hdlr - > error ;
if ( ret ) {
v4l2_ctrl_handler_free ( ctrl_hdlr ) ;
dev_err ( imx214 - > dev , " failed to add controls: %d \n " , ret ) ;
return ret ;
}
imx214 - > sd . ctrl_handler = ctrl_hdlr ;
return 0 ;
} ;
2018-10-05 18:58:31 -04:00
static int imx214_start_streaming ( struct imx214 * imx214 )
{
2024-12-20 14:26:00 +01:00
const struct v4l2_mbus_framefmt * fmt ;
struct v4l2_subdev_state * state ;
2018-10-05 18:58:31 -04:00
const struct imx214_mode * mode ;
2025-05-24 11:14:38 +02:00
int bit_rate_mbps ;
2018-10-05 18:58:31 -04:00
int ret ;
2024-12-20 14:26:02 +01:00
ret = cci_multi_reg_write ( imx214 - > regmap , mode_table_common ,
ARRAY_SIZE ( mode_table_common ) , NULL ) ;
2018-10-05 18:58:31 -04:00
if ( ret < 0 ) {
dev_err ( imx214 - > dev , " could not sent common table %d \n " , ret ) ;
2024-12-20 14:26:00 +01:00
return ret ;
2018-10-05 18:58:31 -04:00
}
2025-05-24 11:14:37 +02:00
ret = imx214_configure_pll ( imx214 ) ;
if ( ret ) {
dev_err ( imx214 - > dev , " failed to configure PLL: %d \n " , ret ) ;
return ret ;
}
2025-05-24 11:14:38 +02:00
bit_rate_mbps = ( imx214 - > pll . pixel_rate_csi / 1000000 )
* imx214 - > pll . bits_per_pixel ;
2025-05-24 11:14:37 +02:00
ret = cci_write ( imx214 - > regmap , IMX214_REG_REQ_LINK_BIT_RATE ,
2025-05-24 11:14:38 +02:00
IMX214_LINK_BIT_RATE_MBPS ( bit_rate_mbps ) , NULL ) ;
2025-05-24 11:14:37 +02:00
if ( ret ) {
dev_err ( imx214 - > dev , " failed to configure link bit rate \n " ) ;
return ret ;
}
2024-12-20 14:26:05 +01:00
ret = cci_write ( imx214 - > regmap , IMX214_REG_CSI_LANE_MODE ,
IMX214_CSI_4_LANE_MODE , NULL ) ;
if ( ret ) {
dev_err ( imx214 - > dev , " failed to configure lanes \n " ) ;
return ret ;
}
2024-12-20 14:26:00 +01:00
state = v4l2_subdev_get_locked_active_state ( & imx214 - > sd ) ;
fmt = v4l2_subdev_state_get_format ( state , 0 ) ;
mode = v4l2_find_nearest_size ( imx214_modes , ARRAY_SIZE ( imx214_modes ) ,
width , height , fmt - > width , fmt - > height ) ;
2024-12-20 14:26:02 +01:00
ret = cci_multi_reg_write ( imx214 - > regmap , mode - > reg_table ,
mode - > num_of_regs , NULL ) ;
2018-10-05 18:58:31 -04:00
if ( ret < 0 ) {
dev_err ( imx214 - > dev , " could not sent mode table %d \n " , ret ) ;
2024-12-20 14:26:00 +01:00
return ret ;
2018-10-05 18:58:31 -04:00
}
2024-12-20 14:26:02 +01:00
usleep_range ( 10000 , 10500 ) ;
2024-12-20 14:26:03 +01:00
cci_write ( imx214 - > regmap , IMX214_REG_TEMP_SENSOR_CONTROL , 0x01 , NULL ) ;
2024-12-20 14:26:02 +01:00
2018-10-05 18:58:31 -04:00
ret = __v4l2_ctrl_handler_setup ( & imx214 - > ctrls ) ;
if ( ret < 0 ) {
dev_err ( imx214 - > dev , " could not sync v4l2 controls \n " ) ;
2024-12-20 14:26:00 +01:00
return ret ;
2018-10-05 18:58:31 -04:00
}
2024-12-20 14:26:02 +01:00
ret = cci_write ( imx214 - > regmap , IMX214_REG_MODE_SELECT ,
IMX214_MODE_STREAMING , NULL ) ;
2024-12-20 14:26:00 +01:00
if ( ret < 0 )
2018-10-05 18:58:31 -04:00
dev_err ( imx214 - > dev , " could not sent start table %d \n " , ret ) ;
return ret ;
}
static int imx214_stop_streaming ( struct imx214 * imx214 )
{
int ret ;
2024-12-20 14:26:02 +01:00
ret = cci_write ( imx214 - > regmap , IMX214_REG_MODE_SELECT ,
IMX214_MODE_STANDBY , NULL ) ;
2018-10-05 18:58:31 -04:00
if ( ret < 0 )
dev_err ( imx214 - > dev , " could not sent stop table %d \n " , ret ) ;
return ret ;
}
static int imx214_s_stream ( struct v4l2_subdev * subdev , int enable )
{
struct imx214 * imx214 = to_imx214 ( subdev ) ;
2024-12-20 14:26:00 +01:00
struct v4l2_subdev_state * state ;
2018-10-05 18:58:31 -04:00
int ret ;
if ( enable ) {
2021-04-23 17:19:12 +02:00
ret = pm_runtime_resume_and_get ( imx214 - > dev ) ;
if ( ret < 0 )
2018-10-05 18:58:31 -04:00
return ret ;
2024-12-20 14:26:00 +01:00
state = v4l2_subdev_lock_and_get_active_state ( subdev ) ;
2018-10-05 18:58:31 -04:00
ret = imx214_start_streaming ( imx214 ) ;
2024-12-20 14:26:00 +01:00
v4l2_subdev_unlock_state ( state ) ;
2018-10-05 18:58:31 -04:00
if ( ret < 0 )
goto err_rpm_put ;
} else {
2019-10-03 16:46:24 +02:00
ret = imx214_stop_streaming ( imx214 ) ;
2018-10-05 18:58:31 -04:00
if ( ret < 0 )
goto err_rpm_put ;
pm_runtime_put ( imx214 - > dev ) ;
}
return 0 ;
err_rpm_put :
pm_runtime_put ( imx214 - > dev ) ;
return ret ;
}
2025-05-24 11:14:38 +02:00
static int imx214_pll_calculate ( struct imx214 * imx214 , struct ccs_pll * pll ,
unsigned int link_freq )
{
struct ccs_pll_limits limits = {
. min_ext_clk_freq_hz = 6000000 ,
. max_ext_clk_freq_hz = 27000000 ,
. vt_fr = {
. min_pre_pll_clk_div = 1 ,
. max_pre_pll_clk_div = 15 ,
/* Value is educated guess as we don't have a spec */
. min_pll_ip_clk_freq_hz = 6000000 ,
/* Value is educated guess as we don't have a spec */
. max_pll_ip_clk_freq_hz = 12000000 ,
. min_pll_multiplier = 12 ,
. max_pll_multiplier = 1200 ,
. min_pll_op_clk_freq_hz = 338000000 ,
. max_pll_op_clk_freq_hz = 1200000000 ,
} ,
. vt_bk = {
. min_sys_clk_div = 2 ,
. max_sys_clk_div = 4 ,
. min_pix_clk_div = 5 ,
. max_pix_clk_div = 10 ,
. min_pix_clk_freq_hz = 30000000 ,
. max_pix_clk_freq_hz = 120000000 ,
} ,
. op_bk = {
. min_sys_clk_div = 1 ,
. max_sys_clk_div = 2 ,
. min_pix_clk_div = 6 ,
. max_pix_clk_div = 10 ,
. min_pix_clk_freq_hz = 30000000 ,
. max_pix_clk_freq_hz = 120000000 ,
} ,
. min_line_length_pck_bin = IMX214_PPL_DEFAULT ,
. min_line_length_pck = IMX214_PPL_DEFAULT ,
} ;
unsigned int num_lanes = imx214 - > bus_cfg . bus . mipi_csi2 . num_data_lanes ;
/*
* There are no documented constraints on the sys clock frequency , for
* either branch . Recover them based on the PLL output clock frequency
* and sys_clk_div limits on one hand , and the pix clock frequency and
* the pix_clk_div limits on the other hand .
*/
limits . vt_bk . min_sys_clk_freq_hz =
max ( limits . vt_fr . min_pll_op_clk_freq_hz / limits . vt_bk . max_sys_clk_div ,
limits . vt_bk . min_pix_clk_freq_hz * limits . vt_bk . min_pix_clk_div ) ;
limits . vt_bk . max_sys_clk_freq_hz =
min ( limits . vt_fr . max_pll_op_clk_freq_hz / limits . vt_bk . min_sys_clk_div ,
limits . vt_bk . max_pix_clk_freq_hz * limits . vt_bk . max_pix_clk_div ) ;
limits . op_bk . min_sys_clk_freq_hz =
max ( limits . vt_fr . min_pll_op_clk_freq_hz / limits . op_bk . max_sys_clk_div ,
limits . op_bk . min_pix_clk_freq_hz * limits . op_bk . min_pix_clk_div ) ;
limits . op_bk . max_sys_clk_freq_hz =
min ( limits . vt_fr . max_pll_op_clk_freq_hz / limits . op_bk . min_sys_clk_div ,
limits . op_bk . max_pix_clk_freq_hz * limits . op_bk . max_pix_clk_div ) ;
memset ( pll , 0 , sizeof ( * pll ) ) ;
pll - > bus_type = CCS_PLL_BUS_TYPE_CSI2_DPHY ;
pll - > op_lanes = num_lanes ;
pll - > vt_lanes = num_lanes ;
pll - > csi2 . lanes = num_lanes ;
pll - > binning_horizontal = 1 ;
pll - > binning_vertical = 1 ;
pll - > scale_m = 1 ;
pll - > scale_n = 1 ;
pll - > bits_per_pixel =
IMX214_CSI_DATA_FORMAT_RAW10 & IMX214_BITS_PER_PIXEL_MASK ;
pll - > flags = CCS_PLL_FLAG_LANE_SPEED_MODEL ;
pll - > link_freq = link_freq ;
pll - > ext_clk_freq_hz = clk_get_rate ( imx214 - > xclk ) ;
return ccs_pll_calculate ( imx214 - > dev , & limits , pll ) ;
}
static int imx214_pll_update ( struct imx214 * imx214 )
{
u64 link_freq ;
int ret ;
link_freq = imx214 - > bus_cfg . link_frequencies [ imx214 - > link_freq - > val ] ;
ret = imx214_pll_calculate ( imx214 , & imx214 - > pll , link_freq ) ;
if ( ret ) {
dev_err ( imx214 - > dev , " PLL calculations failed: %d \n " , ret ) ;
return ret ;
}
ret = v4l2_ctrl_s_ctrl_int64 ( imx214 - > pixel_rate ,
imx214 - > pll . pixel_rate_pixel_array ) ;
if ( ret ) {
dev_err ( imx214 - > dev , " failed to set pixel rate \n " ) ;
return ret ;
}
return 0 ;
}
2023-12-13 17:00:04 +02:00
static int imx214_get_frame_interval ( struct v4l2_subdev * subdev ,
struct v4l2_subdev_state * sd_state ,
struct v4l2_subdev_frame_interval * fival )
2018-10-05 18:58:31 -04:00
{
2023-12-13 17:00:05 +02:00
/*
* FIXME : Implement support for V4L2_SUBDEV_FORMAT_TRY , using the V4L2
* subdev active state API .
*/
if ( fival - > which ! = V4L2_SUBDEV_FORMAT_ACTIVE )
return - EINVAL ;
2018-10-05 18:58:31 -04:00
fival - > interval . numerator = 1 ;
fival - > interval . denominator = IMX214_FPS ;
return 0 ;
}
2024-12-20 14:26:06 +01:00
/*
* Raw sensors should be using the VBLANK and HBLANK controls to determine
* the frame rate . However this driver was initially added using the
* [ S | G | ENUM ] _FRAME_INTERVAL ioctls with a fixed rate of 30f ps .
* Retain the frame_interval ops for backwards compatibility , but they do
* nothing .
*/
2018-10-05 18:58:31 -04:00
static int imx214_enum_frame_interval ( struct v4l2_subdev * subdev ,
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 17:55:58 +03:00
struct v4l2_subdev_state * sd_state ,
2018-10-05 18:58:31 -04:00
struct v4l2_subdev_frame_interval_enum * fie )
{
2024-12-20 14:26:06 +01:00
struct imx214 * imx214 = to_imx214 ( subdev ) ;
2018-10-05 18:58:31 -04:00
const struct imx214_mode * mode ;
2024-12-20 14:26:06 +01:00
dev_warn_once ( imx214 - > dev , " frame_interval functions return an unreliable value for compatibility reasons. Use the VBLANK and HBLANK controls to determine the correct frame rate. \n " ) ;
2018-10-05 18:58:31 -04:00
if ( fie - > index ! = 0 )
return - EINVAL ;
mode = v4l2_find_nearest_size ( imx214_modes ,
ARRAY_SIZE ( imx214_modes ) , width , height ,
fie - > width , fie - > height ) ;
2024-12-20 14:26:07 +01:00
fie - > code = imx214_get_format_code ( imx214 ) ;
2018-10-05 18:58:31 -04:00
fie - > width = mode - > width ;
fie - > height = mode - > height ;
fie - > interval . numerator = 1 ;
fie - > interval . denominator = IMX214_FPS ;
return 0 ;
}
static const struct v4l2_subdev_video_ops imx214_video_ops = {
. s_stream = imx214_s_stream ,
} ;
static const struct v4l2_subdev_pad_ops imx214_subdev_pad_ops = {
. enum_mbus_code = imx214_enum_mbus_code ,
. enum_frame_size = imx214_enum_frame_size ,
. enum_frame_interval = imx214_enum_frame_interval ,
2024-12-20 14:26:00 +01:00
. get_fmt = v4l2_subdev_get_fmt ,
2018-10-05 18:58:31 -04:00
. set_fmt = imx214_set_format ,
. get_selection = imx214_get_selection ,
2023-12-13 17:00:04 +02:00
. get_frame_interval = imx214_get_frame_interval ,
. set_frame_interval = imx214_get_frame_interval ,
2018-10-05 18:58:31 -04:00
} ;
static const struct v4l2_subdev_ops imx214_subdev_ops = {
. core = & imx214_core_ops ,
. video = & imx214_video_ops ,
. pad = & imx214_subdev_pad_ops ,
} ;
2023-11-27 11:07:44 +02:00
static const struct v4l2_subdev_internal_ops imx214_internal_ops = {
. init_state = imx214_entity_init_state ,
} ;
2018-10-05 18:58:31 -04:00
static int imx214_get_regulators ( struct device * dev , struct imx214 * imx214 )
{
unsigned int i ;
for ( i = 0 ; i < IMX214_NUM_SUPPLIES ; i + + )
imx214 - > supplies [ i ] . supply = imx214_supply_name [ i ] ;
return devm_regulator_bulk_get ( dev , IMX214_NUM_SUPPLIES ,
imx214 - > supplies ) ;
}
2024-12-20 14:26:09 +01:00
/* Verify chip ID */
static int imx214_identify_module ( struct imx214 * imx214 )
{
struct i2c_client * client = v4l2_get_subdevdata ( & imx214 - > sd ) ;
int ret ;
u64 val ;
ret = cci_read ( imx214 - > regmap , IMX214_REG_CHIP_ID , & val , NULL ) ;
if ( ret )
return dev_err_probe ( & client - > dev , ret ,
" failed to read chip id %x \n " ,
IMX214_CHIP_ID ) ;
if ( val ! = IMX214_CHIP_ID )
return dev_err_probe ( & client - > dev , - EIO ,
" chip id mismatch: %x!=%llx \n " ,
IMX214_CHIP_ID , val ) ;
return 0 ;
}
2025-05-24 11:14:38 +02:00
static int imx214_parse_fwnode ( struct device * dev , struct imx214 * imx214 )
2018-10-05 18:58:31 -04:00
{
2025-05-24 11:14:38 +02:00
struct v4l2_fwnode_endpoint * bus_cfg = & imx214 - > bus_cfg ;
2018-10-05 18:58:31 -04:00
struct fwnode_handle * endpoint ;
unsigned int i ;
int ret ;
endpoint = fwnode_graph_get_next_endpoint ( dev_fwnode ( dev ) , NULL ) ;
2024-12-20 14:26:01 +01:00
if ( ! endpoint )
return dev_err_probe ( dev , - EINVAL , " endpoint node not found \n " ) ;
2018-10-05 18:58:31 -04:00
2025-05-24 11:14:38 +02:00
bus_cfg - > bus_type = V4L2_MBUS_CSI2_DPHY ;
ret = v4l2_fwnode_endpoint_alloc_parse ( endpoint , bus_cfg ) ;
fwnode_handle_put ( endpoint ) ;
2018-10-05 18:58:31 -04:00
if ( ret ) {
2024-12-20 14:26:01 +01:00
dev_err_probe ( dev , ret , " parsing endpoint node failed \n " ) ;
2025-05-24 11:14:38 +02:00
goto error ;
2018-10-05 18:58:31 -04:00
}
2024-12-20 14:26:05 +01:00
/* Check the number of MIPI CSI2 data lanes */
2025-05-24 11:14:38 +02:00
if ( bus_cfg - > bus . mipi_csi2 . num_data_lanes ! = 4 ) {
2024-12-20 14:26:05 +01:00
ret = dev_err_probe ( dev , - EINVAL ,
" only 4 data lanes are currently supported \n " ) ;
2025-05-24 11:14:38 +02:00
goto error ;
2024-12-20 14:26:05 +01:00
}
2025-05-24 11:14:38 +02:00
if ( bus_cfg - > nr_of_link_frequencies ! = 1 )
2024-12-20 14:26:12 +01:00
dev_warn ( dev , " Only one link-frequency supported, please review your DT. Continuing anyway \n " ) ;
2025-05-24 11:14:38 +02:00
for ( i = 0 ; i < bus_cfg - > nr_of_link_frequencies ; i + + ) {
u64 freq = bus_cfg - > link_frequencies [ i ] ;
struct ccs_pll pll ;
if ( ! imx214_pll_calculate ( imx214 , & pll , freq ) )
2018-10-05 18:58:31 -04:00
break ;
2025-05-24 11:14:38 +02:00
if ( freq = = IMX214_DEFAULT_LINK_FREQ_LEGACY ) {
2024-12-20 14:26:12 +01:00
dev_warn ( dev ,
" link-frequencies %d not supported, please review your DT. Continuing anyway \n " ,
IMX214_DEFAULT_LINK_FREQ ) ;
2025-05-24 11:14:38 +02:00
freq = IMX214_DEFAULT_LINK_FREQ ;
if ( imx214_pll_calculate ( imx214 , & pll , freq ) )
continue ;
bus_cfg - > link_frequencies [ i ] = freq ;
2024-12-20 14:26:12 +01:00
break ;
}
2018-10-05 18:58:31 -04:00
}
2025-05-24 11:14:38 +02:00
if ( i = = bus_cfg - > nr_of_link_frequencies )
2024-12-20 14:26:12 +01:00
ret = dev_err_probe ( dev , - EINVAL ,
2025-05-24 11:14:38 +02:00
" link-frequencies %lld not supported, please review your DT \n " ,
bus_cfg - > nr_of_link_frequencies ?
bus_cfg - > link_frequencies [ 0 ] : 0 ) ;
2024-12-20 14:26:12 +01:00
2025-05-24 11:14:38 +02:00
return 0 ;
error :
v4l2_fwnode_endpoint_free ( & imx214 - > bus_cfg ) ;
2018-10-05 18:58:31 -04:00
return ret ;
}
static int imx214_probe ( struct i2c_client * client )
{
struct device * dev = & client - > dev ;
struct imx214 * imx214 ;
int ret ;
imx214 = devm_kzalloc ( dev , sizeof ( * imx214 ) , GFP_KERNEL ) ;
if ( ! imx214 )
return - ENOMEM ;
imx214 - > dev = dev ;
imx214 - > xclk = devm_clk_get ( dev , NULL ) ;
2024-12-20 14:26:01 +01:00
if ( IS_ERR ( imx214 - > xclk ) )
return dev_err_probe ( dev , PTR_ERR ( imx214 - > xclk ) ,
" failed to get xclk \n " ) ;
2018-10-05 18:58:31 -04:00
ret = imx214_get_regulators ( dev , imx214 ) ;
2024-12-20 14:26:01 +01:00
if ( ret < 0 )
return dev_err_probe ( dev , ret , " failed to get regulators \n " ) ;
2018-10-05 18:58:31 -04:00
imx214 - > enable_gpio = devm_gpiod_get ( dev , " enable " , GPIOD_OUT_LOW ) ;
2024-12-20 14:26:01 +01:00
if ( IS_ERR ( imx214 - > enable_gpio ) )
return dev_err_probe ( dev , PTR_ERR ( imx214 - > enable_gpio ) ,
" failed to get enable gpio \n " ) ;
2018-10-05 18:58:31 -04:00
2024-12-20 14:26:02 +01:00
imx214 - > regmap = devm_cci_regmap_init_i2c ( client , 16 ) ;
2024-12-20 14:26:01 +01:00
if ( IS_ERR ( imx214 - > regmap ) )
return dev_err_probe ( dev , PTR_ERR ( imx214 - > regmap ) ,
2024-12-20 14:26:02 +01:00
" failed to initialize CCI \n " ) ;
2018-10-05 18:58:31 -04:00
2025-05-24 11:14:38 +02:00
ret = imx214_parse_fwnode ( dev , imx214 ) ;
2025-05-24 11:14:36 +02:00
if ( ret )
return ret ;
2018-10-05 18:58:31 -04:00
v4l2_i2c_subdev_init ( & imx214 - > sd , client , & imx214_subdev_ops ) ;
2023-11-27 11:07:44 +02:00
imx214 - > sd . internal_ops = & imx214_internal_ops ;
2018-10-05 18:58:31 -04:00
/*
* Enable power initially , to avoid warnings
* from clk_disable on power_off
*/
2025-05-24 11:14:38 +02:00
ret = imx214_power_on ( imx214 - > dev ) ;
if ( ret < 0 )
goto error_fwnode ;
2018-10-05 18:58:31 -04:00
2024-12-20 14:26:09 +01:00
ret = imx214_identify_module ( imx214 ) ;
if ( ret )
goto error_power_off ;
2023-12-06 23:33:56 +01:00
ret = imx214_ctrls_init ( imx214 ) ;
if ( ret < 0 )
goto error_power_off ;
2018-10-05 18:58:31 -04:00
imx214 - > sd . flags | = V4L2_SUBDEV_FL_HAS_DEVNODE ;
imx214 - > pad . flags = MEDIA_PAD_FL_SOURCE ;
imx214 - > sd . dev = & client - > dev ;
imx214 - > sd . entity . function = MEDIA_ENT_F_CAM_SENSOR ;
ret = media_entity_pads_init ( & imx214 - > sd . entity , 1 , & imx214 - > pad ) ;
if ( ret < 0 ) {
2024-12-20 14:26:01 +01:00
dev_err_probe ( dev , ret , " failed to init entity pads \n " ) ;
2018-10-05 18:58:31 -04:00
goto free_ctrl ;
}
2024-12-20 14:26:00 +01:00
imx214 - > sd . state_lock = imx214 - > ctrls . lock ;
ret = v4l2_subdev_init_finalize ( & imx214 - > sd ) ;
if ( ret < 0 ) {
2024-12-20 14:26:01 +01:00
dev_err_probe ( dev , ret , " subdev init error \n " ) ;
2024-12-20 14:26:00 +01:00
goto free_entity ;
}
2018-10-05 18:58:31 -04:00
2025-01-10 14:36:01 +02:00
pm_runtime_set_active ( imx214 - > dev ) ;
pm_runtime_enable ( imx214 - > dev ) ;
2025-05-24 11:14:38 +02:00
ret = imx214_pll_update ( imx214 ) ;
if ( ret < 0 ) {
dev_err_probe ( dev , ret , " failed to update PLL \n " ) ;
goto error_subdev_cleanup ;
}
2021-03-05 17:42:34 +01:00
ret = v4l2_async_register_subdev_sensor ( & imx214 - > sd ) ;
2018-10-05 18:58:31 -04:00
if ( ret < 0 ) {
2024-12-20 14:26:01 +01:00
dev_err_probe ( dev , ret ,
" failed to register sensor sub-device \n " ) ;
2024-12-20 14:26:00 +01:00
goto error_subdev_cleanup ;
2018-10-05 18:58:31 -04:00
}
2025-01-10 14:36:01 +02:00
pm_runtime_idle ( imx214 - > dev ) ;
2018-10-05 18:58:31 -04:00
return 0 ;
2024-12-20 14:26:00 +01:00
error_subdev_cleanup :
2025-01-10 14:36:01 +02:00
pm_runtime_disable ( imx214 - > dev ) ;
pm_runtime_set_suspended ( & client - > dev ) ;
2024-12-20 14:26:00 +01:00
v4l2_subdev_cleanup ( & imx214 - > sd ) ;
free_entity :
2018-10-05 18:58:31 -04:00
media_entity_cleanup ( & imx214 - > sd . entity ) ;
2025-01-10 14:36:01 +02:00
2018-10-05 18:58:31 -04:00
free_ctrl :
v4l2_ctrl_handler_free ( & imx214 - > ctrls ) ;
2025-01-10 14:36:01 +02:00
2023-12-06 23:33:56 +01:00
error_power_off :
2025-01-10 14:36:01 +02:00
imx214_power_off ( imx214 - > dev ) ;
2018-10-05 18:58:31 -04:00
2025-05-24 11:14:38 +02:00
error_fwnode :
v4l2_fwnode_endpoint_free ( & imx214 - > bus_cfg ) ;
2018-10-05 18:58:31 -04:00
return ret ;
}
2022-08-15 10:02:30 +02:00
static void imx214_remove ( struct i2c_client * client )
2018-10-05 18:58:31 -04:00
{
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
struct imx214 * imx214 = to_imx214 ( sd ) ;
v4l2_async_unregister_subdev ( & imx214 - > sd ) ;
2024-12-20 14:26:00 +01:00
v4l2_subdev_cleanup ( sd ) ;
2018-10-05 18:58:31 -04:00
media_entity_cleanup ( & imx214 - > sd . entity ) ;
v4l2_ctrl_handler_free ( & imx214 - > ctrls ) ;
2025-05-24 11:14:38 +02:00
v4l2_fwnode_endpoint_free ( & imx214 - > bus_cfg ) ;
2025-01-10 14:36:01 +02:00
pm_runtime_disable ( & client - > dev ) ;
if ( ! pm_runtime_status_suspended ( & client - > dev ) ) {
imx214_power_off ( imx214 - > dev ) ;
pm_runtime_set_suspended ( & client - > dev ) ;
}
2018-10-05 18:58:31 -04:00
}
static const struct of_device_id imx214_of_match [ ] = {
{ . compatible = " sony,imx214 " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , imx214_of_match ) ;
static const struct dev_pm_ops imx214_pm_ops = {
SET_RUNTIME_PM_OPS ( imx214_power_off , imx214_power_on , NULL )
} ;
static struct i2c_driver imx214_i2c_driver = {
. driver = {
. of_match_table = imx214_of_match ,
. pm = & imx214_pm_ops ,
. name = " imx214 " ,
} ,
2023-05-14 14:04:07 +02:00
. probe = imx214_probe ,
2018-10-05 18:58:31 -04:00
. remove = imx214_remove ,
} ;
module_i2c_driver ( imx214_i2c_driver ) ;
2019-06-12 08:05:10 -04:00
MODULE_DESCRIPTION ( " Sony IMX214 Camera driver " ) ;
2020-04-30 15:52:24 +02:00
MODULE_AUTHOR ( " Ricardo Ribalda <ribalda@kernel.org> " ) ;
2018-10-05 18:58:31 -04:00
MODULE_LICENSE ( " GPL v2 " ) ;