2020-12-31 15:23:57 +01:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright ( C ) 2020 Bootlin
* Author : Paul Kocialkowski < paul . kocialkowski @ bootlin . com >
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/i2c.h>
# include <linux/module.h>
# include <linux/of_graph.h>
# include <linux/pm_runtime.h>
# include <linux/regulator/consumer.h>
# include <linux/videodev2.h>
# include <media/v4l2-ctrls.h>
# include <media/v4l2-device.h>
# include <media/v4l2-fwnode.h>
# include <media/v4l2-image-sizes.h>
# include <media/v4l2-mediabus.h>
/* Clock rate */
# define OV5648_XVCLK_RATE 24000000
/* Register definitions */
/* System */
# define OV5648_SW_STANDBY_REG 0x100
# define OV5648_SW_STANDBY_STREAM_ON BIT(0)
# define OV5648_SW_RESET_REG 0x103
# define OV5648_SW_RESET_RESET BIT(0)
# define OV5648_PAD_OEN0_REG 0x3000
# define OV5648_PAD_OEN1_REG 0x3001
# define OV5648_PAD_OEN2_REG 0x3002
# define OV5648_PAD_OUT0_REG 0x3008
# define OV5648_PAD_OUT1_REG 0x3009
# define OV5648_CHIP_ID_H_REG 0x300a
# define OV5648_CHIP_ID_H_VALUE 0x56
# define OV5648_CHIP_ID_L_REG 0x300b
# define OV5648_CHIP_ID_L_VALUE 0x48
# define OV5648_PAD_OUT2_REG 0x300d
# define OV5648_PAD_SEL0_REG 0x300e
# define OV5648_PAD_SEL1_REG 0x300f
# define OV5648_PAD_SEL2_REG 0x3010
# define OV5648_PAD_PK_REG 0x3011
# define OV5648_PAD_PK_PD_DATO_EN BIT(7)
# define OV5648_PAD_PK_DRIVE_STRENGTH_1X (0 << 5)
# define OV5648_PAD_PK_DRIVE_STRENGTH_2X (2 << 5)
# define OV5648_PAD_PK_FREX_N BIT(1)
# define OV5648_A_PWC_PK_O0_REG 0x3013
# define OV5648_A_PWC_PK_O0_BP_REGULATOR_N BIT(3)
# define OV5648_A_PWC_PK_O1_REG 0x3014
# define OV5648_MIPI_PHY0_REG 0x3016
# define OV5648_MIPI_PHY1_REG 0x3017
# define OV5648_MIPI_SC_CTRL0_REG 0x3018
# define OV5648_MIPI_SC_CTRL0_MIPI_LANES(v) (((v) << 5) & GENMASK(7, 5))
# define OV5648_MIPI_SC_CTRL0_PHY_HS_TX_PD BIT(4)
# define OV5648_MIPI_SC_CTRL0_PHY_LP_RX_PD BIT(3)
# define OV5648_MIPI_SC_CTRL0_MIPI_EN BIT(2)
# define OV5648_MIPI_SC_CTRL0_MIPI_SUSP BIT(1)
# define OV5648_MIPI_SC_CTRL0_LANE_DIS_OP BIT(0)
# define OV5648_MIPI_SC_CTRL1_REG 0x3019
# define OV5648_MISC_CTRL0_REG 0x3021
# define OV5648_MIPI_SC_CTRL2_REG 0x3022
# define OV5648_SUB_ID_REG 0x302a
# define OV5648_PLL_CTRL0_REG 0x3034
# define OV5648_PLL_CTRL0_PLL_CHARGE_PUMP(v) (((v) << 4) & GENMASK(6, 4))
# define OV5648_PLL_CTRL0_BITS(v) ((v) & GENMASK(3, 0))
# define OV5648_PLL_CTRL1_REG 0x3035
# define OV5648_PLL_CTRL1_SYS_DIV(v) (((v) << 4) & GENMASK(7, 4))
# define OV5648_PLL_CTRL1_MIPI_DIV(v) ((v) & GENMASK(3, 0))
# define OV5648_PLL_MUL_REG 0x3036
# define OV5648_PLL_MUL(v) ((v) & GENMASK(7, 0))
# define OV5648_PLL_DIV_REG 0x3037
# define OV5648_PLL_DIV_ROOT_DIV(v) ((((v) - 1) << 4) & BIT(4))
# define OV5648_PLL_DIV_PLL_PRE_DIV(v) ((v) & GENMASK(3, 0))
# define OV5648_PLL_DEBUG_REG 0x3038
# define OV5648_PLL_BYPASS_REG 0x3039
# define OV5648_PLLS_BYPASS_REG 0x303a
# define OV5648_PLLS_MUL_REG 0x303b
# define OV5648_PLLS_MUL(v) ((v) & GENMASK(4, 0))
# define OV5648_PLLS_CTRL_REG 0x303c
# define OV5648_PLLS_CTRL_PLL_CHARGE_PUMP(v) (((v) << 4) & GENMASK(6, 4))
# define OV5648_PLLS_CTRL_SYS_DIV(v) ((v) & GENMASK(3, 0))
# define OV5648_PLLS_DIV_REG 0x303d
# define OV5648_PLLS_DIV_PLLS_PRE_DIV(v) (((v) << 4) & GENMASK(5, 4))
# define OV5648_PLLS_DIV_PLLS_DIV_R(v) ((((v) - 1) << 2) & BIT(2))
# define OV5648_PLLS_DIV_PLLS_SEL_DIV(v) ((v) & GENMASK(1, 0))
# define OV5648_SRB_CTRL_REG 0x3106
# define OV5648_SRB_CTRL_SCLK_DIV(v) (((v) << 2) & GENMASK(3, 2))
# define OV5648_SRB_CTRL_RESET_ARBITER_EN BIT(1)
# define OV5648_SRB_CTRL_SCLK_ARBITER_EN BIT(0)
/* Group Hold */
# define OV5648_GROUP_ADR0_REG 0x3200
# define OV5648_GROUP_ADR1_REG 0x3201
# define OV5648_GROUP_ADR2_REG 0x3202
# define OV5648_GROUP_ADR3_REG 0x3203
# define OV5648_GROUP_LEN0_REG 0x3204
# define OV5648_GROUP_LEN1_REG 0x3205
# define OV5648_GROUP_LEN2_REG 0x3206
# define OV5648_GROUP_LEN3_REG 0x3207
# define OV5648_GROUP_ACCESS_REG 0x3208
/* Exposure/gain/banding */
# define OV5648_EXPOSURE_CTRL_HH_REG 0x3500
# define OV5648_EXPOSURE_CTRL_HH(v) (((v) & GENMASK(19, 16)) >> 16)
# define OV5648_EXPOSURE_CTRL_HH_VALUE(v) (((v) << 16) & GENMASK(19, 16))
# define OV5648_EXPOSURE_CTRL_H_REG 0x3501
# define OV5648_EXPOSURE_CTRL_H(v) (((v) & GENMASK(15, 8)) >> 8)
# define OV5648_EXPOSURE_CTRL_H_VALUE(v) (((v) << 8) & GENMASK(15, 8))
# define OV5648_EXPOSURE_CTRL_L_REG 0x3502
# define OV5648_EXPOSURE_CTRL_L(v) ((v) & GENMASK(7, 0))
# define OV5648_EXPOSURE_CTRL_L_VALUE(v) ((v) & GENMASK(7, 0))
# define OV5648_MANUAL_CTRL_REG 0x3503
# define OV5648_MANUAL_CTRL_FRAME_DELAY(v) (((v) << 4) & GENMASK(5, 4))
# define OV5648_MANUAL_CTRL_AGC_MANUAL_EN BIT(1)
# define OV5648_MANUAL_CTRL_AEC_MANUAL_EN BIT(0)
# define OV5648_GAIN_CTRL_H_REG 0x350a
# define OV5648_GAIN_CTRL_H(v) (((v) & GENMASK(9, 8)) >> 8)
# define OV5648_GAIN_CTRL_H_VALUE(v) (((v) << 8) & GENMASK(9, 8))
# define OV5648_GAIN_CTRL_L_REG 0x350b
# define OV5648_GAIN_CTRL_L(v) ((v) & GENMASK(7, 0))
# define OV5648_GAIN_CTRL_L_VALUE(v) ((v) & GENMASK(7, 0))
# define OV5648_ANALOG_CTRL0_REG_BASE 0x3600
# define OV5648_ANALOG_CTRL1_REG_BASE 0x3700
# define OV5648_AEC_CTRL0_REG 0x3a00
# define OV5648_AEC_CTRL0_DEBUG BIT(6)
# define OV5648_AEC_CTRL0_DEBAND_EN BIT(5)
# define OV5648_AEC_CTRL0_DEBAND_LOW_LIMIT_EN BIT(4)
# define OV5648_AEC_CTRL0_START_SEL_EN BIT(3)
# define OV5648_AEC_CTRL0_NIGHT_MODE_EN BIT(2)
# define OV5648_AEC_CTRL0_FREEZE_EN BIT(0)
# define OV5648_EXPOSURE_MIN_REG 0x3a01
# define OV5648_EXPOSURE_MAX_60_H_REG 0x3a02
# define OV5648_EXPOSURE_MAX_60_L_REG 0x3a03
# define OV5648_AEC_CTRL5_REG 0x3a05
# define OV5648_AEC_CTRL6_REG 0x3a06
# define OV5648_AEC_CTRL7_REG 0x3a07
# define OV5648_BANDING_STEP_50_H_REG 0x3a08
# define OV5648_BANDING_STEP_50_L_REG 0x3a09
# define OV5648_BANDING_STEP_60_H_REG 0x3a0a
# define OV5648_BANDING_STEP_60_L_REG 0x3a0b
# define OV5648_AEC_CTRLC_REG 0x3a0c
# define OV5648_BANDING_MAX_60_REG 0x3a0d
# define OV5648_BANDING_MAX_50_REG 0x3a0e
# define OV5648_WPT_REG 0x3a0f
# define OV5648_BPT_REG 0x3a10
# define OV5648_VPT_HIGH_REG 0x3a11
# define OV5648_AVG_MANUAL_REG 0x3a12
# define OV5648_PRE_GAIN_REG 0x3a13
# define OV5648_EXPOSURE_MAX_50_H_REG 0x3a14
# define OV5648_EXPOSURE_MAX_50_L_REG 0x3a15
# define OV5648_GAIN_BASE_NIGHT_REG 0x3a17
# define OV5648_AEC_GAIN_CEILING_H_REG 0x3a18
# define OV5648_AEC_GAIN_CEILING_L_REG 0x3a19
# define OV5648_DIFF_MAX_REG 0x3a1a
# define OV5648_WPT2_REG 0x3a1b
# define OV5648_LED_ADD_ROW_H_REG 0x3a1c
# define OV5648_LED_ADD_ROW_L_REG 0x3a1d
# define OV5648_BPT2_REG 0x3a1e
# define OV5648_VPT_LOW_REG 0x3a1f
# define OV5648_AEC_CTRL20_REG 0x3a20
# define OV5648_AEC_CTRL21_REG 0x3a21
# define OV5648_AVG_START_X_H_REG 0x5680
# define OV5648_AVG_START_X_L_REG 0x5681
# define OV5648_AVG_START_Y_H_REG 0x5682
# define OV5648_AVG_START_Y_L_REG 0x5683
# define OV5648_AVG_WINDOW_X_H_REG 0x5684
# define OV5648_AVG_WINDOW_X_L_REG 0x5685
# define OV5648_AVG_WINDOW_Y_H_REG 0x5686
# define OV5648_AVG_WINDOW_Y_L_REG 0x5687
# define OV5648_AVG_WEIGHT00_REG 0x5688
# define OV5648_AVG_WEIGHT01_REG 0x5689
# define OV5648_AVG_WEIGHT02_REG 0x568a
# define OV5648_AVG_WEIGHT03_REG 0x568b
# define OV5648_AVG_WEIGHT04_REG 0x568c
# define OV5648_AVG_WEIGHT05_REG 0x568d
# define OV5648_AVG_WEIGHT06_REG 0x568e
# define OV5648_AVG_WEIGHT07_REG 0x568f
# define OV5648_AVG_CTRL10_REG 0x5690
# define OV5648_AVG_WEIGHT_SUM_REG 0x5691
# define OV5648_AVG_READOUT_REG 0x5693
# define OV5648_DIG_CTRL0_REG 0x5a00
# define OV5648_DIG_COMP_MAN_H_REG 0x5a02
# define OV5648_DIG_COMP_MAN_L_REG 0x5a03
# define OV5648_GAINC_MAN_H_REG 0x5a20
# define OV5648_GAINC_MAN_L_REG 0x5a21
# define OV5648_GAINC_DGC_MAN_H_REG 0x5a22
# define OV5648_GAINC_DGC_MAN_L_REG 0x5a23
# define OV5648_GAINC_CTRL0_REG 0x5a24
# define OV5648_GAINF_ANA_NUM_REG 0x5a40
# define OV5648_GAINF_DIG_GAIN_REG 0x5a41
/* Timing */
# define OV5648_CROP_START_X_H_REG 0x3800
# define OV5648_CROP_START_X_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV5648_CROP_START_X_L_REG 0x3801
# define OV5648_CROP_START_X_L(v) ((v) & GENMASK(7, 0))
# define OV5648_CROP_START_Y_H_REG 0x3802
# define OV5648_CROP_START_Y_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV5648_CROP_START_Y_L_REG 0x3803
# define OV5648_CROP_START_Y_L(v) ((v) & GENMASK(7, 0))
# define OV5648_CROP_END_X_H_REG 0x3804
# define OV5648_CROP_END_X_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV5648_CROP_END_X_L_REG 0x3805
# define OV5648_CROP_END_X_L(v) ((v) & GENMASK(7, 0))
# define OV5648_CROP_END_Y_H_REG 0x3806
# define OV5648_CROP_END_Y_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV5648_CROP_END_Y_L_REG 0x3807
# define OV5648_CROP_END_Y_L(v) ((v) & GENMASK(7, 0))
# define OV5648_OUTPUT_SIZE_X_H_REG 0x3808
# define OV5648_OUTPUT_SIZE_X_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV5648_OUTPUT_SIZE_X_L_REG 0x3809
# define OV5648_OUTPUT_SIZE_X_L(v) ((v) & GENMASK(7, 0))
# define OV5648_OUTPUT_SIZE_Y_H_REG 0x380a
# define OV5648_OUTPUT_SIZE_Y_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV5648_OUTPUT_SIZE_Y_L_REG 0x380b
# define OV5648_OUTPUT_SIZE_Y_L(v) ((v) & GENMASK(7, 0))
# define OV5648_HTS_H_REG 0x380c
# define OV5648_HTS_H(v) (((v) & GENMASK(12, 8)) >> 8)
# define OV5648_HTS_L_REG 0x380d
# define OV5648_HTS_L(v) ((v) & GENMASK(7, 0))
# define OV5648_VTS_H_REG 0x380e
# define OV5648_VTS_H(v) (((v) & GENMASK(15, 8)) >> 8)
# define OV5648_VTS_L_REG 0x380f
# define OV5648_VTS_L(v) ((v) & GENMASK(7, 0))
# define OV5648_OFFSET_X_H_REG 0x3810
# define OV5648_OFFSET_X_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV5648_OFFSET_X_L_REG 0x3811
# define OV5648_OFFSET_X_L(v) ((v) & GENMASK(7, 0))
# define OV5648_OFFSET_Y_H_REG 0x3812
# define OV5648_OFFSET_Y_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV5648_OFFSET_Y_L_REG 0x3813
# define OV5648_OFFSET_Y_L(v) ((v) & GENMASK(7, 0))
# define OV5648_SUB_INC_X_REG 0x3814
# define OV5648_SUB_INC_X_ODD(v) (((v) << 4) & GENMASK(7, 4))
# define OV5648_SUB_INC_X_EVEN(v) ((v) & GENMASK(3, 0))
# define OV5648_SUB_INC_Y_REG 0x3815
# define OV5648_SUB_INC_Y_ODD(v) (((v) << 4) & GENMASK(7, 4))
# define OV5648_SUB_INC_Y_EVEN(v) ((v) & GENMASK(3, 0))
# define OV5648_HSYNCST_H_REG 0x3816
# define OV5648_HSYNCST_H(v) (((v) >> 8) & 0xf)
# define OV5648_HSYNCST_L_REG 0x3817
# define OV5648_HSYNCST_L(v) ((v) & GENMASK(7, 0))
# define OV5648_HSYNCW_H_REG 0x3818
# define OV5648_HSYNCW_H(v) (((v) >> 8) & 0xf)
# define OV5648_HSYNCW_L_REG 0x3819
# define OV5648_HSYNCW_L(v) ((v) & GENMASK(7, 0))
# define OV5648_TC20_REG 0x3820
# define OV5648_TC20_DEBUG BIT(6)
# define OV5648_TC20_FLIP_VERT_ISP_EN BIT(2)
# define OV5648_TC20_FLIP_VERT_SENSOR_EN BIT(1)
# define OV5648_TC20_BINNING_VERT_EN BIT(0)
# define OV5648_TC21_REG 0x3821
# define OV5648_TC21_FLIP_HORZ_ISP_EN BIT(2)
# define OV5648_TC21_FLIP_HORZ_SENSOR_EN BIT(1)
# define OV5648_TC21_BINNING_HORZ_EN BIT(0)
/* Strobe/exposure */
# define OV5648_STROBE_REG 0x3b00
# define OV5648_FREX_EXP_HH_REG 0x3b01
# define OV5648_SHUTTER_DLY_H_REG 0x3b02
# define OV5648_SHUTTER_DLY_L_REG 0x3b03
# define OV5648_FREX_EXP_H_REG 0x3b04
# define OV5648_FREX_EXP_L_REG 0x3b05
# define OV5648_FREX_CTRL_REG 0x3b06
# define OV5648_FREX_MODE_SEL_REG 0x3b07
# define OV5648_FREX_MODE_SEL_FREX_SA1 BIT(4)
# define OV5648_FREX_MODE_SEL_FX1_FM_EN BIT(3)
# define OV5648_FREX_MODE_SEL_FREX_INV BIT(2)
# define OV5648_FREX_MODE_SEL_MODE1 0x0
# define OV5648_FREX_MODE_SEL_MODE2 0x1
# define OV5648_FREX_MODE_SEL_ROLLING 0x2
# define OV5648_FREX_EXP_REQ_REG 0x3b08
# define OV5648_FREX_SHUTTER_DLY_REG 0x3b09
# define OV5648_FREX_RST_LEN_REG 0x3b0a
# define OV5648_STROBE_WIDTH_HH_REG 0x3b0b
# define OV5648_STROBE_WIDTH_H_REG 0x3b0c
/* OTP */
# define OV5648_OTP_DATA_REG_BASE 0x3d00
# define OV5648_OTP_PROGRAM_CTRL_REG 0x3d80
# define OV5648_OTP_LOAD_CTRL_REG 0x3d81
/* PSRAM */
# define OV5648_PSRAM_CTRL1_REG 0x3f01
# define OV5648_PSRAM_CTRLF_REG 0x3f0f
/* Black Level */
# define OV5648_BLC_CTRL0_REG 0x4000
# define OV5648_BLC_CTRL1_REG 0x4001
# define OV5648_BLC_CTRL1_START_LINE(v) ((v) & GENMASK(5, 0))
# define OV5648_BLC_CTRL2_REG 0x4002
# define OV5648_BLC_CTRL2_AUTO_EN BIT(6)
# define OV5648_BLC_CTRL2_RESET_FRAME_NUM(v) ((v) & GENMASK(5, 0))
# define OV5648_BLC_CTRL3_REG 0x4003
# define OV5648_BLC_LINE_NUM_REG 0x4004
# define OV5648_BLC_LINE_NUM(v) ((v) & GENMASK(7, 0))
# define OV5648_BLC_CTRL5_REG 0x4005
# define OV5648_BLC_CTRL5_UPDATE_EN BIT(1)
# define OV5648_BLC_LEVEL_REG 0x4009
/* Frame */
# define OV5648_FRAME_CTRL_REG 0x4200
# define OV5648_FRAME_ON_NUM_REG 0x4201
# define OV5648_FRAME_OFF_NUM_REG 0x4202
/* MIPI CSI-2 */
# define OV5648_MIPI_CTRL0_REG 0x4800
# define OV5648_MIPI_CTRL0_CLK_LANE_AUTOGATE BIT(5)
# define OV5648_MIPI_CTRL0_LANE_SYNC_EN BIT(4)
# define OV5648_MIPI_CTRL0_LANE_SELECT_LANE1 0
# define OV5648_MIPI_CTRL0_LANE_SELECT_LANE2 BIT(3)
# define OV5648_MIPI_CTRL0_IDLE_LP00 0
# define OV5648_MIPI_CTRL0_IDLE_LP11 BIT(2)
# define OV5648_MIPI_CTRL1_REG 0x4801
# define OV5648_MIPI_CTRL2_REG 0x4802
# define OV5648_MIPI_CTRL3_REG 0x4803
# define OV5648_MIPI_CTRL4_REG 0x4804
# define OV5648_MIPI_CTRL5_REG 0x4805
# define OV5648_MIPI_MAX_FRAME_COUNT_H_REG 0x4810
# define OV5648_MIPI_MAX_FRAME_COUNT_L_REG 0x4811
# define OV5648_MIPI_CTRL14_REG 0x4814
# define OV5648_MIPI_DT_SPKT_REG 0x4815
# define OV5648_MIPI_HS_ZERO_MIN_H_REG 0x4818
# define OV5648_MIPI_HS_ZERO_MIN_L_REG 0x4819
# define OV5648_MIPI_HS_TRAIN_MIN_H_REG 0x481a
# define OV5648_MIPI_HS_TRAIN_MIN_L_REG 0x481b
# define OV5648_MIPI_CLK_ZERO_MIN_H_REG 0x481c
# define OV5648_MIPI_CLK_ZERO_MIN_L_REG 0x481d
# define OV5648_MIPI_CLK_PREPARE_MIN_H_REG 0x481e
# define OV5648_MIPI_CLK_PREPARE_MIN_L_REG 0x481f
# define OV5648_MIPI_CLK_POST_MIN_H_REG 0x4820
# define OV5648_MIPI_CLK_POST_MIN_L_REG 0x4821
# define OV5648_MIPI_CLK_TRAIL_MIN_H_REG 0x4822
# define OV5648_MIPI_CLK_TRAIL_MIN_L_REG 0x4823
# define OV5648_MIPI_LPX_P_MIN_H_REG 0x4824
# define OV5648_MIPI_LPX_P_MIN_L_REG 0x4825
# define OV5648_MIPI_HS_PREPARE_MIN_H_REG 0x4826
# define OV5648_MIPI_HS_PREPARE_MIN_L_REG 0x4827
# define OV5648_MIPI_HS_EXIT_MIN_H_REG 0x4828
# define OV5648_MIPI_HS_EXIT_MIN_L_REG 0x4829
# define OV5648_MIPI_HS_ZERO_MIN_UI_REG 0x482a
# define OV5648_MIPI_HS_TRAIL_MIN_UI_REG 0x482b
# define OV5648_MIPI_CLK_ZERO_MIN_UI_REG 0x482c
# define OV5648_MIPI_CLK_PREPARE_MIN_UI_REG 0x482d
# define OV5648_MIPI_CLK_POST_MIN_UI_REG 0x482e
# define OV5648_MIPI_CLK_TRAIL_MIN_UI_REG 0x482f
# define OV5648_MIPI_LPX_P_MIN_UI_REG 0x4830
# define OV5648_MIPI_HS_PREPARE_MIN_UI_REG 0x4831
# define OV5648_MIPI_HS_EXIT_MIN_UI_REG 0x4832
# define OV5648_MIPI_REG_MIN_H_REG 0x4833
# define OV5648_MIPI_REG_MIN_L_REG 0x4834
# define OV5648_MIPI_REG_MAX_H_REG 0x4835
# define OV5648_MIPI_REG_MAX_L_REG 0x4836
# define OV5648_MIPI_PCLK_PERIOD_REG 0x4837
# define OV5648_MIPI_WKUP_DLY_REG 0x4838
# define OV5648_MIPI_LP_GPIO_REG 0x483b
# define OV5648_MIPI_SNR_PCLK_DIV_REG 0x4843
/* ISP */
# define OV5648_ISP_CTRL0_REG 0x5000
# define OV5648_ISP_CTRL0_BLACK_CORRECT_EN BIT(2)
# define OV5648_ISP_CTRL0_WHITE_CORRECT_EN BIT(1)
# define OV5648_ISP_CTRL1_REG 0x5001
# define OV5648_ISP_CTRL1_AWB_EN BIT(0)
# define OV5648_ISP_CTRL2_REG 0x5002
# define OV5648_ISP_CTRL2_WIN_EN BIT(6)
# define OV5648_ISP_CTRL2_OTP_EN BIT(1)
# define OV5648_ISP_CTRL2_AWB_GAIN_EN BIT(0)
# define OV5648_ISP_CTRL3_REG 0x5003
# define OV5648_ISP_CTRL3_BUF_EN BIT(3)
# define OV5648_ISP_CTRL3_BIN_MAN_SET BIT(2)
# define OV5648_ISP_CTRL3_BIN_AUTO_EN BIT(1)
# define OV5648_ISP_CTRL4_REG 0x5004
# define OV5648_ISP_CTRL5_REG 0x5005
# define OV5648_ISP_CTRL6_REG 0x5006
# define OV5648_ISP_CTRL7_REG 0x5007
# define OV5648_ISP_MAN_OFFSET_X_H_REG 0x5008
# define OV5648_ISP_MAN_OFFSET_X_L_REG 0x5009
# define OV5648_ISP_MAN_OFFSET_Y_H_REG 0x500a
# define OV5648_ISP_MAN_OFFSET_Y_L_REG 0x500b
# define OV5648_ISP_MAN_WIN_OFFSET_X_H_REG 0x500c
# define OV5648_ISP_MAN_WIN_OFFSET_X_L_REG 0x500d
# define OV5648_ISP_MAN_WIN_OFFSET_Y_H_REG 0x500e
# define OV5648_ISP_MAN_WIN_OFFSET_Y_L_REG 0x500f
# define OV5648_ISP_MAN_WIN_OUTPUT_X_H_REG 0x5010
# define OV5648_ISP_MAN_WIN_OUTPUT_X_L_REG 0x5011
# define OV5648_ISP_MAN_WIN_OUTPUT_Y_H_REG 0x5012
# define OV5648_ISP_MAN_WIN_OUTPUT_Y_L_REG 0x5013
# define OV5648_ISP_MAN_INPUT_X_H_REG 0x5014
# define OV5648_ISP_MAN_INPUT_X_L_REG 0x5015
# define OV5648_ISP_MAN_INPUT_Y_H_REG 0x5016
# define OV5648_ISP_MAN_INPUT_Y_L_REG 0x5017
# define OV5648_ISP_CTRL18_REG 0x5018
# define OV5648_ISP_CTRL19_REG 0x5019
# define OV5648_ISP_CTRL1A_REG 0x501a
# define OV5648_ISP_CTRL1D_REG 0x501d
# define OV5648_ISP_CTRL1F_REG 0x501f
# define OV5648_ISP_CTRL1F_OUTPUT_EN 3
# define OV5648_ISP_CTRL25_REG 0x5025
# define OV5648_ISP_CTRL3D_REG 0x503d
# define OV5648_ISP_CTRL3D_PATTERN_EN BIT(7)
# define OV5648_ISP_CTRL3D_ROLLING_BAR_EN BIT(6)
# define OV5648_ISP_CTRL3D_TRANSPARENT_MODE BIT(5)
# define OV5648_ISP_CTRL3D_SQUARES_BW_MODE BIT(4)
# define OV5648_ISP_CTRL3D_PATTERN_COLOR_BARS 0
# define OV5648_ISP_CTRL3D_PATTERN_RANDOM_DATA 1
# define OV5648_ISP_CTRL3D_PATTERN_COLOR_SQUARES 2
# define OV5648_ISP_CTRL3D_PATTERN_INPUT 3
# define OV5648_ISP_CTRL3E_REG 0x503e
# define OV5648_ISP_CTRL4B_REG 0x504b
# define OV5648_ISP_CTRL4B_POST_BIN_H_EN BIT(5)
# define OV5648_ISP_CTRL4B_POST_BIN_V_EN BIT(4)
# define OV5648_ISP_CTRL4C_REG 0x504c
# define OV5648_ISP_CTRL57_REG 0x5057
# define OV5648_ISP_CTRL58_REG 0x5058
# define OV5648_ISP_CTRL59_REG 0x5059
# define OV5648_ISP_WINDOW_START_X_H_REG 0x5980
# define OV5648_ISP_WINDOW_START_X_L_REG 0x5981
# define OV5648_ISP_WINDOW_START_Y_H_REG 0x5982
# define OV5648_ISP_WINDOW_START_Y_L_REG 0x5983
# define OV5648_ISP_WINDOW_WIN_X_H_REG 0x5984
# define OV5648_ISP_WINDOW_WIN_X_L_REG 0x5985
# define OV5648_ISP_WINDOW_WIN_Y_H_REG 0x5986
# define OV5648_ISP_WINDOW_WIN_Y_L_REG 0x5987
# define OV5648_ISP_WINDOW_MAN_REG 0x5988
/* White Balance */
# define OV5648_AWB_CTRL_REG 0x5180
# define OV5648_AWB_CTRL_FAST_AWB BIT(6)
# define OV5648_AWB_CTRL_GAIN_FREEZE_EN BIT(5)
# define OV5648_AWB_CTRL_SUM_FREEZE_EN BIT(4)
# define OV5648_AWB_CTRL_GAIN_MANUAL_EN BIT(3)
# define OV5648_AWB_DELTA_REG 0x5181
# define OV5648_AWB_STABLE_RANGE_REG 0x5182
# define OV5648_AWB_STABLE_RANGE_WIDE_REG 0x5183
# define OV5648_HSIZE_MAN_REG 0x5185
# define OV5648_GAIN_RED_MAN_H_REG 0x5186
# define OV5648_GAIN_RED_MAN_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV5648_GAIN_RED_MAN_L_REG 0x5187
# define OV5648_GAIN_RED_MAN_L(v) ((v) & GENMASK(7, 0))
# define OV5648_GAIN_GREEN_MAN_H_REG 0x5188
# define OV5648_GAIN_GREEN_MAN_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV5648_GAIN_GREEN_MAN_L_REG 0x5189
# define OV5648_GAIN_GREEN_MAN_L(v) ((v) & GENMASK(7, 0))
# define OV5648_GAIN_BLUE_MAN_H_REG 0x518a
# define OV5648_GAIN_BLUE_MAN_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV5648_GAIN_BLUE_MAN_L_REG 0x518b
# define OV5648_GAIN_BLUE_MAN_L(v) ((v) & GENMASK(7, 0))
# define OV5648_GAIN_RED_LIMIT_REG 0x518c
# define OV5648_GAIN_GREEN_LIMIT_REG 0x518d
# define OV5648_GAIN_BLUE_LIMIT_REG 0x518e
# define OV5648_AWB_FRAME_COUNT_REG 0x518f
# define OV5648_AWB_BASE_MAN_REG 0x51df
/* Macros */
# define ov5648_subdev_sensor(s) \
container_of ( s , struct ov5648_sensor , subdev )
# define ov5648_ctrl_subdev(c) \
2021-01-05 15:47:19 +01:00
( & container_of ( ( c ) - > handler , struct ov5648_sensor , \
ctrls . handler ) - > subdev )
2020-12-31 15:23:57 +01:00
/* Data structures */
struct ov5648_register_value {
u16 address ;
u8 value ;
unsigned int delay_ms ;
} ;
/*
* PLL1 Clock Tree :
*
* + - < XVCLK
* |
* + - + pll_pre_div ( 0x3037 [ 3 : 0 ] , special values : 5 : 1.5 , 7 : 2.5 )
* |
* + - + pll_mul ( 0x3036 [ 7 : 0 ] )
* |
* + - + sys_div ( 0x3035 [ 7 : 4 ] )
* |
* + - + mipi_div ( 0x3035 [ 3 : 0 ] )
* | |
* | + - > MIPI_SCLK
* | |
* | + - + mipi_phy_div ( 2 )
* | |
* | + - > MIPI_CLK
* |
* + - + root_div ( 0x3037 [ 4 ] )
* |
* + - + bit_div ( 0x3034 [ 3 : 0 ] , 8 bits : 2 , 10 bits : 2.5 , other : 1 )
* |
* + - + sclk_div ( 0x3106 [ 3 : 2 ] )
* |
* + - > SCLK
* |
* + - + mipi_div ( 0x3035 , 1 : PCLK = SCLK )
* |
* + - > PCLK
*/
struct ov5648_pll1_config {
unsigned int pll_pre_div ;
unsigned int pll_mul ;
unsigned int sys_div ;
unsigned int root_div ;
unsigned int sclk_div ;
unsigned int mipi_div ;
} ;
/*
* PLL2 Clock Tree :
*
* + - < XVCLK
* |
* + - + plls_pre_div ( 0x303d [ 5 : 4 ] , special values : 0 : 1 , 1 : 1.5 )
* |
* + - + plls_div_r ( 0x303d [ 2 ] )
* |
* + - + plls_mul ( 0x303b [ 4 : 0 ] )
* |
* + - + sys_div ( 0x303c [ 3 : 0 ] )
* |
* + - + sel_div ( 0x303d [ 1 : 0 ] , special values : 0 : 1 , 3 : 2.5 )
* |
* + - > ADCLK
*/
struct ov5648_pll2_config {
unsigned int plls_pre_div ;
unsigned int plls_div_r ;
unsigned int plls_mul ;
unsigned int sys_div ;
unsigned int sel_div ;
} ;
/*
* General formulas for ( array - centered ) mode calculation :
* - photo_array_width = 2624
* - crop_start_x = ( photo_array_width - output_size_x ) / 2
* - crop_end_x = crop_start_x + offset_x + output_size_x - 1
*
* - photo_array_height = 1956
* - crop_start_y = ( photo_array_height - output_size_y ) / 2
* - crop_end_y = crop_start_y + offset_y + output_size_y - 1
*/
struct ov5648_mode {
unsigned int crop_start_x ;
unsigned int offset_x ;
unsigned int output_size_x ;
unsigned int crop_end_x ;
unsigned int hts ;
unsigned int crop_start_y ;
unsigned int offset_y ;
unsigned int output_size_y ;
unsigned int crop_end_y ;
unsigned int vts ;
bool binning_x ;
bool binning_y ;
unsigned int inc_x_odd ;
unsigned int inc_x_even ;
unsigned int inc_y_odd ;
unsigned int inc_y_even ;
/* 8-bit frame interval followed by 10-bit frame interval. */
struct v4l2_fract frame_interval [ 2 ] ;
/* 8-bit config followed by 10-bit config. */
const struct ov5648_pll1_config * pll1_config [ 2 ] ;
const struct ov5648_pll2_config * pll2_config ;
const struct ov5648_register_value * register_values ;
unsigned int register_values_count ;
} ;
struct ov5648_state {
const struct ov5648_mode * mode ;
u32 mbus_code ;
bool streaming ;
} ;
struct ov5648_ctrls {
struct v4l2_ctrl * exposure_auto ;
struct v4l2_ctrl * exposure ;
struct v4l2_ctrl * gain_auto ;
struct v4l2_ctrl * gain ;
struct v4l2_ctrl * white_balance_auto ;
struct v4l2_ctrl * red_balance ;
struct v4l2_ctrl * blue_balance ;
struct v4l2_ctrl * link_freq ;
struct v4l2_ctrl * pixel_rate ;
struct v4l2_ctrl_handler handler ;
2022-01-10 17:16:51 +01:00
} ;
2020-12-31 15:23:57 +01:00
struct ov5648_sensor {
struct device * dev ;
struct i2c_client * i2c_client ;
struct gpio_desc * reset ;
struct gpio_desc * powerdown ;
struct regulator * avdd ;
struct regulator * dvdd ;
struct regulator * dovdd ;
struct clk * xvclk ;
struct v4l2_fwnode_endpoint endpoint ;
struct v4l2_subdev subdev ;
struct media_pad pad ;
struct mutex mutex ;
struct ov5648_state state ;
struct ov5648_ctrls ctrls ;
} ;
/* Static definitions */
/*
* XVCLK = 24 MHz
* SCLK = 84 MHz
* PCLK = 84 MHz
*/
static const struct ov5648_pll1_config ov5648_pll1_config_native_8_bits = {
. pll_pre_div = 3 ,
. pll_mul = 84 ,
. sys_div = 2 ,
. root_div = 1 ,
. sclk_div = 1 ,
. mipi_div = 1 ,
} ;
/*
* XVCLK = 24 MHz
* SCLK = 84 MHz
* PCLK = 84 MHz
*/
static const struct ov5648_pll1_config ov5648_pll1_config_native_10_bits = {
. pll_pre_div = 3 ,
. pll_mul = 105 ,
. sys_div = 2 ,
. root_div = 1 ,
. sclk_div = 1 ,
. mipi_div = 1 ,
} ;
/*
* XVCLK = 24 MHz
* ADCLK = 200 MHz
*/
static const struct ov5648_pll2_config ov5648_pll2_config_native = {
. plls_pre_div = 3 ,
. plls_div_r = 1 ,
. plls_mul = 25 ,
. sys_div = 1 ,
. sel_div = 1 ,
} ;
static const struct ov5648_mode ov5648_modes [ ] = {
/* 2592x1944 */
{
/* Horizontal */
. crop_start_x = 16 ,
. offset_x = 0 ,
. output_size_x = 2592 ,
. crop_end_x = 2607 ,
. hts = 2816 ,
/* Vertical */
. crop_start_y = 6 ,
. offset_y = 0 ,
. output_size_y = 1944 ,
. crop_end_y = 1949 ,
. vts = 1984 ,
/* Subsample increase */
. inc_x_odd = 1 ,
. inc_x_even = 1 ,
. inc_y_odd = 1 ,
. inc_y_even = 1 ,
/* Frame Interval */
. frame_interval = {
{ 1 , 15 } ,
{ 1 , 15 } ,
} ,
/* PLL */
. pll1_config = {
& ov5648_pll1_config_native_8_bits ,
& ov5648_pll1_config_native_10_bits ,
} ,
. pll2_config = & ov5648_pll2_config_native ,
} ,
/* 1600x1200 (UXGA) */
{
/* Horizontal */
. crop_start_x = 512 ,
. offset_x = 0 ,
. output_size_x = 1600 ,
. crop_end_x = 2111 ,
. hts = 2816 ,
/* Vertical */
. crop_start_y = 378 ,
. offset_y = 0 ,
. output_size_y = 1200 ,
. crop_end_y = 1577 ,
. vts = 1984 ,
/* Subsample increase */
. inc_x_odd = 1 ,
. inc_x_even = 1 ,
. inc_y_odd = 1 ,
. inc_y_even = 1 ,
/* Frame Interval */
. frame_interval = {
{ 1 , 15 } ,
{ 1 , 15 } ,
} ,
/* PLL */
. pll1_config = {
& ov5648_pll1_config_native_8_bits ,
& ov5648_pll1_config_native_10_bits ,
} ,
. pll2_config = & ov5648_pll2_config_native ,
} ,
/* 1920x1080 (Full HD) */
{
/* Horizontal */
. crop_start_x = 352 ,
. offset_x = 0 ,
. output_size_x = 1920 ,
. crop_end_x = 2271 ,
. hts = 2816 ,
/* Vertical */
. crop_start_y = 438 ,
. offset_y = 0 ,
. output_size_y = 1080 ,
. crop_end_y = 1517 ,
. vts = 1984 ,
/* Subsample increase */
. inc_x_odd = 1 ,
. inc_x_even = 1 ,
. inc_y_odd = 1 ,
. inc_y_even = 1 ,
/* Frame Interval */
. frame_interval = {
{ 1 , 15 } ,
{ 1 , 15 } ,
} ,
/* PLL */
. pll1_config = {
& ov5648_pll1_config_native_8_bits ,
& ov5648_pll1_config_native_10_bits ,
} ,
. pll2_config = & ov5648_pll2_config_native ,
} ,
/* 1280x960 */
{
/* Horizontal */
. crop_start_x = 16 ,
. offset_x = 8 ,
. output_size_x = 1280 ,
. crop_end_x = 2607 ,
. hts = 1912 ,
/* Vertical */
. crop_start_y = 6 ,
. offset_y = 6 ,
. output_size_y = 960 ,
. crop_end_y = 1949 ,
. vts = 1496 ,
/* Binning */
. binning_x = true ,
/* Subsample increase */
. inc_x_odd = 3 ,
. inc_x_even = 1 ,
. inc_y_odd = 3 ,
. inc_y_even = 1 ,
/* Frame Interval */
. frame_interval = {
{ 1 , 30 } ,
{ 1 , 30 } ,
} ,
/* PLL */
. pll1_config = {
& ov5648_pll1_config_native_8_bits ,
& ov5648_pll1_config_native_10_bits ,
} ,
. pll2_config = & ov5648_pll2_config_native ,
} ,
/* 1280x720 (HD) */
{
/* Horizontal */
. crop_start_x = 16 ,
. offset_x = 8 ,
. output_size_x = 1280 ,
. crop_end_x = 2607 ,
. hts = 1912 ,
/* Vertical */
. crop_start_y = 254 ,
. offset_y = 2 ,
. output_size_y = 720 ,
. crop_end_y = 1701 ,
. vts = 1496 ,
/* Binning */
. binning_x = true ,
/* Subsample increase */
. inc_x_odd = 3 ,
. inc_x_even = 1 ,
. inc_y_odd = 3 ,
. inc_y_even = 1 ,
/* Frame Interval */
. frame_interval = {
{ 1 , 30 } ,
{ 1 , 30 } ,
} ,
/* PLL */
. pll1_config = {
& ov5648_pll1_config_native_8_bits ,
& ov5648_pll1_config_native_10_bits ,
} ,
. pll2_config = & ov5648_pll2_config_native ,
} ,
/* 640x480 (VGA) */
{
/* Horizontal */
. crop_start_x = 0 ,
. offset_x = 8 ,
. output_size_x = 640 ,
. crop_end_x = 2623 ,
. hts = 1896 ,
/* Vertical */
. crop_start_y = 0 ,
. offset_y = 2 ,
. output_size_y = 480 ,
. crop_end_y = 1953 ,
. vts = 984 ,
/* Binning */
. binning_x = true ,
/* Subsample increase */
. inc_x_odd = 7 ,
. inc_x_even = 1 ,
. inc_y_odd = 7 ,
. inc_y_even = 1 ,
/* Frame Interval */
. frame_interval = {
{ 1 , 30 } ,
{ 1 , 30 } ,
} ,
/* PLL */
. pll1_config = {
& ov5648_pll1_config_native_8_bits ,
& ov5648_pll1_config_native_10_bits ,
} ,
. pll2_config = & ov5648_pll2_config_native ,
} ,
} ;
static const u32 ov5648_mbus_codes [ ] = {
MEDIA_BUS_FMT_SBGGR8_1X8 ,
MEDIA_BUS_FMT_SBGGR10_1X10 ,
} ;
static const struct ov5648_register_value ov5648_init_sequence [ ] = {
/* PSRAM */
{ OV5648_PSRAM_CTRL1_REG , 0x0d } ,
{ OV5648_PSRAM_CTRLF_REG , 0xf5 } ,
} ;
static const s64 ov5648_link_freq_menu [ ] = {
210000000 ,
168000000 ,
} ;
static const char * const ov5648_test_pattern_menu [ ] = {
" Disabled " ,
" Random data " ,
" Color bars " ,
" Color bars with rolling bar " ,
" Color squares " ,
" Color squares with rolling bar "
} ;
static const u8 ov5648_test_pattern_bits [ ] = {
0 ,
OV5648_ISP_CTRL3D_PATTERN_EN | OV5648_ISP_CTRL3D_PATTERN_RANDOM_DATA ,
OV5648_ISP_CTRL3D_PATTERN_EN | OV5648_ISP_CTRL3D_PATTERN_COLOR_BARS ,
OV5648_ISP_CTRL3D_PATTERN_EN | OV5648_ISP_CTRL3D_ROLLING_BAR_EN |
OV5648_ISP_CTRL3D_PATTERN_COLOR_BARS ,
OV5648_ISP_CTRL3D_PATTERN_EN | OV5648_ISP_CTRL3D_PATTERN_COLOR_SQUARES ,
OV5648_ISP_CTRL3D_PATTERN_EN | OV5648_ISP_CTRL3D_ROLLING_BAR_EN |
OV5648_ISP_CTRL3D_PATTERN_COLOR_SQUARES ,
} ;
/* Input/Output */
static int ov5648_read ( struct ov5648_sensor * sensor , u16 address , u8 * value )
{
unsigned char data [ 2 ] = { address > > 8 , address & 0xff } ;
struct i2c_client * client = sensor - > i2c_client ;
int ret ;
ret = i2c_master_send ( client , data , sizeof ( data ) ) ;
if ( ret < 0 ) {
dev_dbg ( & client - > dev , " i2c send error at address %#04x \n " ,
address ) ;
return ret ;
}
ret = i2c_master_recv ( client , value , 1 ) ;
if ( ret < 0 ) {
dev_dbg ( & client - > dev , " i2c recv error at address %#04x \n " ,
address ) ;
return ret ;
}
return 0 ;
}
static int ov5648_write ( struct ov5648_sensor * sensor , u16 address , u8 value )
{
unsigned char data [ 3 ] = { address > > 8 , address & 0xff , value } ;
struct i2c_client * client = sensor - > i2c_client ;
int ret ;
ret = i2c_master_send ( client , data , sizeof ( data ) ) ;
if ( ret < 0 ) {
dev_dbg ( & client - > dev , " i2c send error at address %#04x \n " ,
address ) ;
return ret ;
}
return 0 ;
}
static int ov5648_write_sequence ( struct ov5648_sensor * sensor ,
const struct ov5648_register_value * sequence ,
unsigned int sequence_count )
{
unsigned int i ;
int ret = 0 ;
for ( i = 0 ; i < sequence_count ; i + + ) {
ret = ov5648_write ( sensor , sequence [ i ] . address ,
sequence [ i ] . value ) ;
if ( ret )
break ;
if ( sequence [ i ] . delay_ms )
msleep ( sequence [ i ] . delay_ms ) ;
}
return ret ;
}
static int ov5648_update_bits ( struct ov5648_sensor * sensor , u16 address ,
u8 mask , u8 bits )
{
u8 value = 0 ;
int ret ;
ret = ov5648_read ( sensor , address , & value ) ;
if ( ret )
return ret ;
value & = ~ mask ;
value | = bits ;
ret = ov5648_write ( sensor , address , value ) ;
if ( ret )
return ret ;
return 0 ;
}
/* Sensor */
static int ov5648_sw_reset ( struct ov5648_sensor * sensor )
{
return ov5648_write ( sensor , OV5648_SW_RESET_REG , OV5648_SW_RESET_RESET ) ;
}
static int ov5648_sw_standby ( struct ov5648_sensor * sensor , int standby )
{
u8 value = 0 ;
if ( ! standby )
value = OV5648_SW_STANDBY_STREAM_ON ;
return ov5648_write ( sensor , OV5648_SW_STANDBY_REG , value ) ;
}
static int ov5648_chip_id_check ( struct ov5648_sensor * sensor )
{
u16 regs [ ] = { OV5648_CHIP_ID_H_REG , OV5648_CHIP_ID_L_REG } ;
u8 values [ ] = { OV5648_CHIP_ID_H_VALUE , OV5648_CHIP_ID_L_VALUE } ;
unsigned int i ;
u8 value ;
int ret ;
for ( i = 0 ; i < ARRAY_SIZE ( regs ) ; i + + ) {
ret = ov5648_read ( sensor , regs [ i ] , & value ) ;
if ( ret < 0 )
return ret ;
if ( value ! = values [ i ] ) {
dev_err ( sensor - > dev ,
" chip id value mismatch: %#x instead of %#x \n " ,
value , values [ i ] ) ;
return - EINVAL ;
}
}
return 0 ;
}
static int ov5648_avdd_internal_power ( struct ov5648_sensor * sensor , int on )
{
return ov5648_write ( sensor , OV5648_A_PWC_PK_O0_REG ,
on ? 0 : OV5648_A_PWC_PK_O0_BP_REGULATOR_N ) ;
}
static int ov5648_pad_configure ( struct ov5648_sensor * sensor )
{
int ret ;
/* Configure pads as input. */
ret = ov5648_write ( sensor , OV5648_PAD_OEN1_REG , 0 ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_PAD_OEN2_REG , 0 ) ;
if ( ret )
return ret ;
/* Disable FREX pin. */
return ov5648_write ( sensor , OV5648_PAD_PK_REG ,
OV5648_PAD_PK_DRIVE_STRENGTH_1X |
OV5648_PAD_PK_FREX_N ) ;
}
static int ov5648_mipi_configure ( struct ov5648_sensor * sensor )
{
2022-01-03 17:24:10 +01:00
struct v4l2_mbus_config_mipi_csi2 * bus_mipi_csi2 =
2020-12-31 15:23:57 +01:00
& sensor - > endpoint . bus . mipi_csi2 ;
unsigned int lanes_count = bus_mipi_csi2 - > num_data_lanes ;
int ret ;
ret = ov5648_write ( sensor , OV5648_MIPI_CTRL0_REG ,
OV5648_MIPI_CTRL0_CLK_LANE_AUTOGATE |
OV5648_MIPI_CTRL0_LANE_SELECT_LANE1 |
OV5648_MIPI_CTRL0_IDLE_LP11 ) ;
if ( ret )
return ret ;
return ov5648_write ( sensor , OV5648_MIPI_SC_CTRL0_REG ,
OV5648_MIPI_SC_CTRL0_MIPI_LANES ( lanes_count ) |
OV5648_MIPI_SC_CTRL0_PHY_LP_RX_PD |
OV5648_MIPI_SC_CTRL0_MIPI_EN ) ;
}
static int ov5648_black_level_configure ( struct ov5648_sensor * sensor )
{
int ret ;
/* Up to 6 lines are available for black level calibration. */
ret = ov5648_write ( sensor , OV5648_BLC_CTRL1_REG ,
OV5648_BLC_CTRL1_START_LINE ( 2 ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_BLC_CTRL2_REG ,
OV5648_BLC_CTRL2_AUTO_EN |
OV5648_BLC_CTRL2_RESET_FRAME_NUM ( 5 ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_BLC_LINE_NUM_REG ,
OV5648_BLC_LINE_NUM ( 4 ) ) ;
if ( ret )
return ret ;
return ov5648_update_bits ( sensor , OV5648_BLC_CTRL5_REG ,
OV5648_BLC_CTRL5_UPDATE_EN ,
OV5648_BLC_CTRL5_UPDATE_EN ) ;
}
static int ov5648_isp_configure ( struct ov5648_sensor * sensor )
{
u8 bits ;
int ret ;
/* Enable black and white level correction. */
bits = OV5648_ISP_CTRL0_BLACK_CORRECT_EN |
OV5648_ISP_CTRL0_WHITE_CORRECT_EN ;
ret = ov5648_update_bits ( sensor , OV5648_ISP_CTRL0_REG , bits , bits ) ;
if ( ret )
return ret ;
/* Enable AWB. */
ret = ov5648_write ( sensor , OV5648_ISP_CTRL1_REG ,
OV5648_ISP_CTRL1_AWB_EN ) ;
if ( ret )
return ret ;
/* Enable AWB gain and windowing. */
ret = ov5648_write ( sensor , OV5648_ISP_CTRL2_REG ,
OV5648_ISP_CTRL2_WIN_EN |
OV5648_ISP_CTRL2_AWB_GAIN_EN ) ;
if ( ret )
return ret ;
/* Enable buffering and auto-binning. */
ret = ov5648_write ( sensor , OV5648_ISP_CTRL3_REG ,
OV5648_ISP_CTRL3_BUF_EN |
OV5648_ISP_CTRL3_BIN_AUTO_EN ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_ISP_CTRL4_REG , 0 ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_ISP_CTRL1F_REG ,
OV5648_ISP_CTRL1F_OUTPUT_EN ) ;
if ( ret )
return ret ;
/* Enable post-binning filters. */
ret = ov5648_write ( sensor , OV5648_ISP_CTRL4B_REG ,
OV5648_ISP_CTRL4B_POST_BIN_H_EN |
OV5648_ISP_CTRL4B_POST_BIN_V_EN ) ;
if ( ret )
return ret ;
/* Disable debanding and night mode. Debug bit seems necessary. */
ret = ov5648_write ( sensor , OV5648_AEC_CTRL0_REG ,
OV5648_AEC_CTRL0_DEBUG |
OV5648_AEC_CTRL0_START_SEL_EN ) ;
if ( ret )
return ret ;
return ov5648_write ( sensor , OV5648_MANUAL_CTRL_REG ,
OV5648_MANUAL_CTRL_FRAME_DELAY ( 1 ) ) ;
}
static unsigned long ov5648_mode_pll1_rate ( struct ov5648_sensor * sensor ,
const struct ov5648_pll1_config * config )
{
unsigned long xvclk_rate ;
unsigned long pll1_rate ;
xvclk_rate = clk_get_rate ( sensor - > xvclk ) ;
pll1_rate = xvclk_rate * config - > pll_mul ;
switch ( config - > pll_pre_div ) {
case 5 :
pll1_rate * = 3 ;
pll1_rate / = 2 ;
break ;
case 7 :
pll1_rate * = 5 ;
pll1_rate / = 2 ;
break ;
default :
pll1_rate / = config - > pll_pre_div ;
break ;
}
return pll1_rate ;
}
static int ov5648_mode_pll1_configure ( struct ov5648_sensor * sensor ,
const struct ov5648_mode * mode ,
u32 mbus_code )
{
const struct ov5648_pll1_config * config ;
u8 value ;
int ret ;
value = OV5648_PLL_CTRL0_PLL_CHARGE_PUMP ( 1 ) ;
switch ( mbus_code ) {
case MEDIA_BUS_FMT_SBGGR8_1X8 :
config = mode - > pll1_config [ 0 ] ;
value | = OV5648_PLL_CTRL0_BITS ( 8 ) ;
break ;
case MEDIA_BUS_FMT_SBGGR10_1X10 :
config = mode - > pll1_config [ 1 ] ;
value | = OV5648_PLL_CTRL0_BITS ( 10 ) ;
break ;
default :
return - EINVAL ;
}
ret = ov5648_write ( sensor , OV5648_PLL_CTRL0_REG , value ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_PLL_DIV_REG ,
OV5648_PLL_DIV_ROOT_DIV ( config - > root_div ) |
OV5648_PLL_DIV_PLL_PRE_DIV ( config - > pll_pre_div ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_PLL_MUL_REG ,
OV5648_PLL_MUL ( config - > pll_mul ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_PLL_CTRL1_REG ,
OV5648_PLL_CTRL1_SYS_DIV ( config - > sys_div ) |
OV5648_PLL_CTRL1_MIPI_DIV ( config - > mipi_div ) ) ;
if ( ret )
return ret ;
return ov5648_write ( sensor , OV5648_SRB_CTRL_REG ,
OV5648_SRB_CTRL_SCLK_DIV ( config - > sclk_div ) |
OV5648_SRB_CTRL_SCLK_ARBITER_EN ) ;
}
static int ov5648_mode_pll2_configure ( struct ov5648_sensor * sensor ,
const struct ov5648_mode * mode )
{
const struct ov5648_pll2_config * config = mode - > pll2_config ;
int ret ;
ret = ov5648_write ( sensor , OV5648_PLLS_DIV_REG ,
OV5648_PLLS_DIV_PLLS_PRE_DIV ( config - > plls_pre_div ) |
OV5648_PLLS_DIV_PLLS_DIV_R ( config - > plls_div_r ) |
OV5648_PLLS_DIV_PLLS_SEL_DIV ( config - > sel_div ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_PLLS_MUL_REG ,
OV5648_PLLS_MUL ( config - > plls_mul ) ) ;
if ( ret )
return ret ;
return ov5648_write ( sensor , OV5648_PLLS_CTRL_REG ,
OV5648_PLLS_CTRL_PLL_CHARGE_PUMP ( 1 ) |
OV5648_PLLS_CTRL_SYS_DIV ( config - > sys_div ) ) ;
}
static int ov5648_mode_configure ( struct ov5648_sensor * sensor ,
const struct ov5648_mode * mode , u32 mbus_code )
{
int ret ;
/* Crop Start X */
ret = ov5648_write ( sensor , OV5648_CROP_START_X_H_REG ,
OV5648_CROP_START_X_H ( mode - > crop_start_x ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_CROP_START_X_L_REG ,
OV5648_CROP_START_X_L ( mode - > crop_start_x ) ) ;
if ( ret )
return ret ;
/* Offset X */
ret = ov5648_write ( sensor , OV5648_OFFSET_X_H_REG ,
OV5648_OFFSET_X_H ( mode - > offset_x ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_OFFSET_X_L_REG ,
OV5648_OFFSET_X_L ( mode - > offset_x ) ) ;
if ( ret )
return ret ;
/* Output Size X */
ret = ov5648_write ( sensor , OV5648_OUTPUT_SIZE_X_H_REG ,
OV5648_OUTPUT_SIZE_X_H ( mode - > output_size_x ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_OUTPUT_SIZE_X_L_REG ,
OV5648_OUTPUT_SIZE_X_L ( mode - > output_size_x ) ) ;
if ( ret )
return ret ;
/* Crop End X */
ret = ov5648_write ( sensor , OV5648_CROP_END_X_H_REG ,
OV5648_CROP_END_X_H ( mode - > crop_end_x ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_CROP_END_X_L_REG ,
OV5648_CROP_END_X_L ( mode - > crop_end_x ) ) ;
if ( ret )
return ret ;
/* Horizontal Total Size */
ret = ov5648_write ( sensor , OV5648_HTS_H_REG , OV5648_HTS_H ( mode - > hts ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_HTS_L_REG , OV5648_HTS_L ( mode - > hts ) ) ;
if ( ret )
return ret ;
/* Crop Start Y */
ret = ov5648_write ( sensor , OV5648_CROP_START_Y_H_REG ,
OV5648_CROP_START_Y_H ( mode - > crop_start_y ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_CROP_START_Y_L_REG ,
OV5648_CROP_START_Y_L ( mode - > crop_start_y ) ) ;
if ( ret )
return ret ;
/* Offset Y */
ret = ov5648_write ( sensor , OV5648_OFFSET_Y_H_REG ,
OV5648_OFFSET_Y_H ( mode - > offset_y ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_OFFSET_Y_L_REG ,
OV5648_OFFSET_Y_L ( mode - > offset_y ) ) ;
if ( ret )
return ret ;
/* Output Size Y */
ret = ov5648_write ( sensor , OV5648_OUTPUT_SIZE_Y_H_REG ,
OV5648_OUTPUT_SIZE_Y_H ( mode - > output_size_y ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_OUTPUT_SIZE_Y_L_REG ,
OV5648_OUTPUT_SIZE_Y_L ( mode - > output_size_y ) ) ;
if ( ret )
return ret ;
/* Crop End Y */
ret = ov5648_write ( sensor , OV5648_CROP_END_Y_H_REG ,
OV5648_CROP_END_Y_H ( mode - > crop_end_y ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_CROP_END_Y_L_REG ,
OV5648_CROP_END_Y_L ( mode - > crop_end_y ) ) ;
if ( ret )
return ret ;
/* Vertical Total Size */
ret = ov5648_write ( sensor , OV5648_VTS_H_REG , OV5648_VTS_H ( mode - > vts ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_VTS_L_REG , OV5648_VTS_L ( mode - > vts ) ) ;
if ( ret )
return ret ;
/* Flip/Mirror/Binning */
/*
* A debug bit is enabled by default and needs to be cleared for
* subsampling to work .
*/
ret = ov5648_update_bits ( sensor , OV5648_TC20_REG ,
OV5648_TC20_DEBUG |
OV5648_TC20_BINNING_VERT_EN ,
mode - > binning_y ? OV5648_TC20_BINNING_VERT_EN :
0 ) ;
if ( ret )
return ret ;
ret = ov5648_update_bits ( sensor , OV5648_TC21_REG ,
OV5648_TC21_BINNING_HORZ_EN ,
mode - > binning_x ? OV5648_TC21_BINNING_HORZ_EN :
0 ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_SUB_INC_X_REG ,
OV5648_SUB_INC_X_ODD ( mode - > inc_x_odd ) |
OV5648_SUB_INC_X_EVEN ( mode - > inc_x_even ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_SUB_INC_Y_REG ,
OV5648_SUB_INC_Y_ODD ( mode - > inc_y_odd ) |
OV5648_SUB_INC_Y_EVEN ( mode - > inc_y_even ) ) ;
if ( ret )
return ret ;
/* PLLs */
ret = ov5648_mode_pll1_configure ( sensor , mode , mbus_code ) ;
if ( ret )
return ret ;
ret = ov5648_mode_pll2_configure ( sensor , mode ) ;
if ( ret )
return ret ;
/* Extra registers */
if ( mode - > register_values ) {
ret = ov5648_write_sequence ( sensor , mode - > register_values ,
mode - > register_values_count ) ;
if ( ret )
return ret ;
}
return 0 ;
}
static unsigned long ov5648_mode_mipi_clk_rate ( struct ov5648_sensor * sensor ,
const struct ov5648_mode * mode ,
u32 mbus_code )
{
const struct ov5648_pll1_config * config ;
unsigned long pll1_rate ;
switch ( mbus_code ) {
case MEDIA_BUS_FMT_SBGGR8_1X8 :
config = mode - > pll1_config [ 0 ] ;
break ;
case MEDIA_BUS_FMT_SBGGR10_1X10 :
config = mode - > pll1_config [ 1 ] ;
break ;
default :
return 0 ;
}
pll1_rate = ov5648_mode_pll1_rate ( sensor , config ) ;
return pll1_rate / config - > sys_div / config - > mipi_div / 2 ;
}
/* Exposure */
static int ov5648_exposure_auto_configure ( struct ov5648_sensor * sensor ,
bool enable )
{
return ov5648_update_bits ( sensor , OV5648_MANUAL_CTRL_REG ,
OV5648_MANUAL_CTRL_AEC_MANUAL_EN ,
enable ? 0 : OV5648_MANUAL_CTRL_AEC_MANUAL_EN ) ;
}
static int ov5648_exposure_configure ( struct ov5648_sensor * sensor , u32 exposure )
{
struct ov5648_ctrls * ctrls = & sensor - > ctrls ;
int ret ;
if ( ctrls - > exposure_auto - > val ! = V4L2_EXPOSURE_MANUAL )
return - EINVAL ;
ret = ov5648_write ( sensor , OV5648_EXPOSURE_CTRL_HH_REG ,
OV5648_EXPOSURE_CTRL_HH ( exposure ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_EXPOSURE_CTRL_H_REG ,
OV5648_EXPOSURE_CTRL_H ( exposure ) ) ;
if ( ret )
return ret ;
return ov5648_write ( sensor , OV5648_EXPOSURE_CTRL_L_REG ,
OV5648_EXPOSURE_CTRL_L ( exposure ) ) ;
}
static int ov5648_exposure_value ( struct ov5648_sensor * sensor ,
u32 * exposure )
{
u8 exposure_hh = 0 , exposure_h = 0 , exposure_l = 0 ;
int ret ;
ret = ov5648_read ( sensor , OV5648_EXPOSURE_CTRL_HH_REG , & exposure_hh ) ;
if ( ret )
return ret ;
ret = ov5648_read ( sensor , OV5648_EXPOSURE_CTRL_H_REG , & exposure_h ) ;
if ( ret )
return ret ;
ret = ov5648_read ( sensor , OV5648_EXPOSURE_CTRL_L_REG , & exposure_l ) ;
if ( ret )
return ret ;
* exposure = OV5648_EXPOSURE_CTRL_HH_VALUE ( ( u32 ) exposure_hh ) |
OV5648_EXPOSURE_CTRL_H_VALUE ( ( u32 ) exposure_h ) |
OV5648_EXPOSURE_CTRL_L_VALUE ( ( u32 ) exposure_l ) ;
return 0 ;
}
/* Gain */
static int ov5648_gain_auto_configure ( struct ov5648_sensor * sensor , bool enable )
{
return ov5648_update_bits ( sensor , OV5648_MANUAL_CTRL_REG ,
OV5648_MANUAL_CTRL_AGC_MANUAL_EN ,
enable ? 0 : OV5648_MANUAL_CTRL_AGC_MANUAL_EN ) ;
}
static int ov5648_gain_configure ( struct ov5648_sensor * sensor , u32 gain )
{
struct ov5648_ctrls * ctrls = & sensor - > ctrls ;
int ret ;
if ( ctrls - > gain_auto - > val )
return - EINVAL ;
ret = ov5648_write ( sensor , OV5648_GAIN_CTRL_H_REG ,
OV5648_GAIN_CTRL_H ( gain ) ) ;
if ( ret )
return ret ;
return ov5648_write ( sensor , OV5648_GAIN_CTRL_L_REG ,
OV5648_GAIN_CTRL_L ( gain ) ) ;
}
static int ov5648_gain_value ( struct ov5648_sensor * sensor , u32 * gain )
{
u8 gain_h = 0 , gain_l = 0 ;
int ret ;
ret = ov5648_read ( sensor , OV5648_GAIN_CTRL_H_REG , & gain_h ) ;
if ( ret )
return ret ;
ret = ov5648_read ( sensor , OV5648_GAIN_CTRL_L_REG , & gain_l ) ;
if ( ret )
return ret ;
* gain = OV5648_GAIN_CTRL_H_VALUE ( ( u32 ) gain_h ) |
OV5648_GAIN_CTRL_L_VALUE ( ( u32 ) gain_l ) ;
return 0 ;
}
/* White Balance */
static int ov5648_white_balance_auto_configure ( struct ov5648_sensor * sensor ,
bool enable )
{
return ov5648_write ( sensor , OV5648_AWB_CTRL_REG ,
enable ? 0 : OV5648_AWB_CTRL_GAIN_MANUAL_EN ) ;
}
static int ov5648_white_balance_configure ( struct ov5648_sensor * sensor ,
u32 red_balance , u32 blue_balance )
{
struct ov5648_ctrls * ctrls = & sensor - > ctrls ;
int ret ;
if ( ctrls - > white_balance_auto - > val )
return - EINVAL ;
ret = ov5648_write ( sensor , OV5648_GAIN_RED_MAN_H_REG ,
OV5648_GAIN_RED_MAN_H ( red_balance ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_GAIN_RED_MAN_L_REG ,
OV5648_GAIN_RED_MAN_L ( red_balance ) ) ;
if ( ret )
return ret ;
ret = ov5648_write ( sensor , OV5648_GAIN_BLUE_MAN_H_REG ,
OV5648_GAIN_BLUE_MAN_H ( blue_balance ) ) ;
if ( ret )
return ret ;
return ov5648_write ( sensor , OV5648_GAIN_BLUE_MAN_L_REG ,
OV5648_GAIN_BLUE_MAN_L ( blue_balance ) ) ;
}
/* Flip */
static int ov5648_flip_vert_configure ( struct ov5648_sensor * sensor , bool enable )
{
u8 bits = OV5648_TC20_FLIP_VERT_ISP_EN |
OV5648_TC20_FLIP_VERT_SENSOR_EN ;
return ov5648_update_bits ( sensor , OV5648_TC20_REG , bits ,
enable ? bits : 0 ) ;
}
static int ov5648_flip_horz_configure ( struct ov5648_sensor * sensor , bool enable )
{
u8 bits = OV5648_TC21_FLIP_HORZ_ISP_EN |
OV5648_TC21_FLIP_HORZ_SENSOR_EN ;
return ov5648_update_bits ( sensor , OV5648_TC21_REG , bits ,
enable ? bits : 0 ) ;
}
/* Test Pattern */
static int ov5648_test_pattern_configure ( struct ov5648_sensor * sensor ,
unsigned int index )
{
if ( index > = ARRAY_SIZE ( ov5648_test_pattern_bits ) )
return - EINVAL ;
return ov5648_write ( sensor , OV5648_ISP_CTRL3D_REG ,
ov5648_test_pattern_bits [ index ] ) ;
}
/* State */
static int ov5648_state_mipi_configure ( struct ov5648_sensor * sensor ,
const struct ov5648_mode * mode ,
u32 mbus_code )
{
struct ov5648_ctrls * ctrls = & sensor - > ctrls ;
2022-01-03 17:24:10 +01:00
struct v4l2_mbus_config_mipi_csi2 * bus_mipi_csi2 =
2020-12-31 15:23:57 +01:00
& sensor - > endpoint . bus . mipi_csi2 ;
unsigned long mipi_clk_rate ;
unsigned int bits_per_sample ;
unsigned int lanes_count ;
unsigned int i , j ;
s64 mipi_pixel_rate ;
mipi_clk_rate = ov5648_mode_mipi_clk_rate ( sensor , mode , mbus_code ) ;
if ( ! mipi_clk_rate )
return - EINVAL ;
for ( i = 0 ; i < ARRAY_SIZE ( ov5648_link_freq_menu ) ; i + + ) {
s64 freq = ov5648_link_freq_menu [ i ] ;
if ( freq = = mipi_clk_rate )
break ;
}
for ( j = 0 ; j < sensor - > endpoint . nr_of_link_frequencies ; j + + ) {
u64 freq = sensor - > endpoint . link_frequencies [ j ] ;
if ( freq = = mipi_clk_rate )
break ;
}
if ( i = = ARRAY_SIZE ( ov5648_link_freq_menu ) ) {
dev_err ( sensor - > dev ,
" failed to find %lu clk rate in link freq \n " ,
mipi_clk_rate ) ;
} else if ( j = = sensor - > endpoint . nr_of_link_frequencies ) {
dev_err ( sensor - > dev ,
" failed to find %lu clk rate in endpoint link-frequencies \n " ,
mipi_clk_rate ) ;
} else {
__v4l2_ctrl_s_ctrl ( ctrls - > link_freq , i ) ;
}
switch ( mbus_code ) {
case MEDIA_BUS_FMT_SBGGR8_1X8 :
bits_per_sample = 8 ;
break ;
case MEDIA_BUS_FMT_SBGGR10_1X10 :
bits_per_sample = 10 ;
break ;
default :
return - EINVAL ;
}
lanes_count = bus_mipi_csi2 - > num_data_lanes ;
mipi_pixel_rate = mipi_clk_rate * 2 * lanes_count / bits_per_sample ;
__v4l2_ctrl_s_ctrl_int64 ( ctrls - > pixel_rate , mipi_pixel_rate ) ;
return 0 ;
}
static int ov5648_state_configure ( struct ov5648_sensor * sensor ,
const struct ov5648_mode * mode ,
u32 mbus_code )
{
int ret ;
if ( sensor - > state . streaming )
return - EBUSY ;
/* State will be configured at first power on otherwise. */
if ( pm_runtime_enabled ( sensor - > dev ) & &
! pm_runtime_suspended ( sensor - > dev ) ) {
ret = ov5648_mode_configure ( sensor , mode , mbus_code ) ;
if ( ret )
return ret ;
}
ret = ov5648_state_mipi_configure ( sensor , mode , mbus_code ) ;
if ( ret )
return ret ;
sensor - > state . mode = mode ;
sensor - > state . mbus_code = mbus_code ;
return 0 ;
}
static int ov5648_state_init ( struct ov5648_sensor * sensor )
{
2022-02-03 13:33:12 +01:00
int ret ;
mutex_lock ( & sensor - > mutex ) ;
ret = ov5648_state_configure ( sensor , & ov5648_modes [ 0 ] ,
ov5648_mbus_codes [ 0 ] ) ;
mutex_unlock ( & sensor - > mutex ) ;
return ret ;
2020-12-31 15:23:57 +01:00
}
/* Sensor Base */
static int ov5648_sensor_init ( struct ov5648_sensor * sensor )
{
int ret ;
ret = ov5648_sw_reset ( sensor ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to perform sw reset \n " ) ;
return ret ;
}
ret = ov5648_sw_standby ( sensor , 1 ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to set sensor standby \n " ) ;
return ret ;
}
ret = ov5648_chip_id_check ( sensor ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to check sensor chip id \n " ) ;
return ret ;
}
ret = ov5648_avdd_internal_power ( sensor , ! sensor - > avdd ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to set internal avdd power \n " ) ;
return ret ;
}
ret = ov5648_write_sequence ( sensor , ov5648_init_sequence ,
ARRAY_SIZE ( ov5648_init_sequence ) ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to write init sequence \n " ) ;
return ret ;
}
ret = ov5648_pad_configure ( sensor ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to configure pad \n " ) ;
return ret ;
}
ret = ov5648_mipi_configure ( sensor ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to configure MIPI \n " ) ;
return ret ;
}
ret = ov5648_isp_configure ( sensor ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to configure ISP \n " ) ;
return ret ;
}
ret = ov5648_black_level_configure ( sensor ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to configure black level \n " ) ;
return ret ;
}
/* Configure current mode. */
ret = ov5648_state_configure ( sensor , sensor - > state . mode ,
sensor - > state . mbus_code ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to configure state \n " ) ;
return ret ;
}
return 0 ;
}
static int ov5648_sensor_power ( struct ov5648_sensor * sensor , bool on )
{
/* Keep initialized to zero for disable label. */
int ret = 0 ;
/*
* General notes about the power sequence :
* - power - down GPIO must be active ( low ) during power - on ;
* - reset GPIO state does not matter during power - on ;
* - XVCLK must be provided 1 ms before register access ;
* - 10 ms are needed between power - down deassert and register access .
*/
/* Note that regulator-and-GPIO-based power is untested. */
if ( on ) {
gpiod_set_value_cansleep ( sensor - > reset , 1 ) ;
gpiod_set_value_cansleep ( sensor - > powerdown , 1 ) ;
ret = regulator_enable ( sensor - > dovdd ) ;
if ( ret ) {
dev_err ( sensor - > dev ,
" failed to enable DOVDD regulator \n " ) ;
goto disable ;
}
if ( sensor - > avdd ) {
ret = regulator_enable ( sensor - > avdd ) ;
if ( ret ) {
dev_err ( sensor - > dev ,
" failed to enable AVDD regulator \n " ) ;
goto disable ;
}
}
ret = regulator_enable ( sensor - > dvdd ) ;
if ( ret ) {
dev_err ( sensor - > dev ,
" failed to enable DVDD regulator \n " ) ;
goto disable ;
}
/* According to OV5648 power up diagram. */
usleep_range ( 5000 , 10000 ) ;
ret = clk_prepare_enable ( sensor - > xvclk ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to enable XVCLK clock \n " ) ;
goto disable ;
}
gpiod_set_value_cansleep ( sensor - > reset , 0 ) ;
gpiod_set_value_cansleep ( sensor - > powerdown , 0 ) ;
usleep_range ( 20000 , 25000 ) ;
} else {
disable :
gpiod_set_value_cansleep ( sensor - > powerdown , 1 ) ;
gpiod_set_value_cansleep ( sensor - > reset , 1 ) ;
clk_disable_unprepare ( sensor - > xvclk ) ;
regulator_disable ( sensor - > dvdd ) ;
if ( sensor - > avdd )
regulator_disable ( sensor - > avdd ) ;
regulator_disable ( sensor - > dovdd ) ;
}
return ret ;
}
/* Controls */
static int ov5648_g_volatile_ctrl ( struct v4l2_ctrl * ctrl )
{
struct v4l2_subdev * subdev = ov5648_ctrl_subdev ( ctrl ) ;
struct ov5648_sensor * sensor = ov5648_subdev_sensor ( subdev ) ;
struct ov5648_ctrls * ctrls = & sensor - > ctrls ;
int ret ;
switch ( ctrl - > id ) {
case V4L2_CID_EXPOSURE_AUTO :
ret = ov5648_exposure_value ( sensor , & ctrls - > exposure - > val ) ;
if ( ret )
return ret ;
break ;
case V4L2_CID_AUTOGAIN :
ret = ov5648_gain_value ( sensor , & ctrls - > gain - > val ) ;
if ( ret )
return ret ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int ov5648_s_ctrl ( struct v4l2_ctrl * ctrl )
{
struct v4l2_subdev * subdev = ov5648_ctrl_subdev ( ctrl ) ;
struct ov5648_sensor * sensor = ov5648_subdev_sensor ( subdev ) ;
struct ov5648_ctrls * ctrls = & sensor - > ctrls ;
unsigned int index ;
bool enable ;
int ret ;
/* Wait for the sensor to be on before setting controls. */
if ( pm_runtime_suspended ( sensor - > dev ) )
return 0 ;
switch ( ctrl - > id ) {
case V4L2_CID_EXPOSURE_AUTO :
enable = ctrl - > val = = V4L2_EXPOSURE_AUTO ;
ret = ov5648_exposure_auto_configure ( sensor , enable ) ;
if ( ret )
return ret ;
if ( ! enable & & ctrls - > exposure - > is_new ) {
ret = ov5648_exposure_configure ( sensor ,
ctrls - > exposure - > val ) ;
if ( ret )
return ret ;
}
break ;
case V4L2_CID_AUTOGAIN :
enable = ! ! ctrl - > val ;
ret = ov5648_gain_auto_configure ( sensor , enable ) ;
if ( ret )
return ret ;
if ( ! enable ) {
ret = ov5648_gain_configure ( sensor , ctrls - > gain - > val ) ;
if ( ret )
return ret ;
}
break ;
case V4L2_CID_AUTO_WHITE_BALANCE :
enable = ! ! ctrl - > val ;
ret = ov5648_white_balance_auto_configure ( sensor , enable ) ;
if ( ret )
return ret ;
if ( ! enable ) {
ret = ov5648_white_balance_configure ( sensor ,
ctrls - > red_balance - > val ,
ctrls - > blue_balance - > val ) ;
if ( ret )
return ret ;
}
break ;
case V4L2_CID_HFLIP :
enable = ! ! ctrl - > val ;
return ov5648_flip_horz_configure ( sensor , enable ) ;
case V4L2_CID_VFLIP :
enable = ! ! ctrl - > val ;
return ov5648_flip_vert_configure ( sensor , enable ) ;
case V4L2_CID_TEST_PATTERN :
index = ( unsigned int ) ctrl - > val ;
return ov5648_test_pattern_configure ( sensor , index ) ;
default :
return - EINVAL ;
}
return 0 ;
}
static const struct v4l2_ctrl_ops ov5648_ctrl_ops = {
. g_volatile_ctrl = ov5648_g_volatile_ctrl ,
. s_ctrl = ov5648_s_ctrl ,
} ;
static int ov5648_ctrls_init ( struct ov5648_sensor * sensor )
{
struct ov5648_ctrls * ctrls = & sensor - > ctrls ;
struct v4l2_ctrl_handler * handler = & ctrls - > handler ;
const struct v4l2_ctrl_ops * ops = & ov5648_ctrl_ops ;
int ret ;
v4l2_ctrl_handler_init ( handler , 32 ) ;
/* Use our mutex for ctrl locking. */
handler - > lock = & sensor - > mutex ;
/* Exposure */
ctrls - > exposure_auto = v4l2_ctrl_new_std_menu ( handler , ops ,
V4L2_CID_EXPOSURE_AUTO ,
V4L2_EXPOSURE_MANUAL , 0 ,
V4L2_EXPOSURE_AUTO ) ;
ctrls - > exposure = v4l2_ctrl_new_std ( handler , ops , V4L2_CID_EXPOSURE ,
16 , 1048575 , 16 , 512 ) ;
v4l2_ctrl_auto_cluster ( 2 , & ctrls - > exposure_auto , 1 , true ) ;
/* Gain */
ctrls - > gain_auto =
v4l2_ctrl_new_std ( handler , ops , V4L2_CID_AUTOGAIN , 0 , 1 , 1 , 1 ) ;
ctrls - > gain = v4l2_ctrl_new_std ( handler , ops , V4L2_CID_GAIN , 16 , 1023 ,
16 , 16 ) ;
v4l2_ctrl_auto_cluster ( 2 , & ctrls - > gain_auto , 0 , true ) ;
/* White Balance */
ctrls - > white_balance_auto =
v4l2_ctrl_new_std ( handler , ops , V4L2_CID_AUTO_WHITE_BALANCE , 0 ,
1 , 1 , 1 ) ;
ctrls - > red_balance = v4l2_ctrl_new_std ( handler , ops ,
V4L2_CID_RED_BALANCE , 0 , 4095 ,
1 , 1024 ) ;
ctrls - > blue_balance = v4l2_ctrl_new_std ( handler , ops ,
V4L2_CID_BLUE_BALANCE , 0 , 4095 ,
1 , 1024 ) ;
v4l2_ctrl_auto_cluster ( 3 , & ctrls - > white_balance_auto , 0 , false ) ;
/* Flip */
v4l2_ctrl_new_std ( handler , ops , V4L2_CID_HFLIP , 0 , 1 , 1 , 0 ) ;
v4l2_ctrl_new_std ( handler , ops , V4L2_CID_VFLIP , 0 , 1 , 1 , 0 ) ;
/* Test Pattern */
v4l2_ctrl_new_std_menu_items ( handler , ops , V4L2_CID_TEST_PATTERN ,
ARRAY_SIZE ( ov5648_test_pattern_menu ) - 1 ,
0 , 0 , ov5648_test_pattern_menu ) ;
/* MIPI CSI-2 */
ctrls - > link_freq =
v4l2_ctrl_new_int_menu ( handler , NULL , V4L2_CID_LINK_FREQ ,
ARRAY_SIZE ( ov5648_link_freq_menu ) - 1 ,
0 , ov5648_link_freq_menu ) ;
ctrls - > pixel_rate =
v4l2_ctrl_new_std ( handler , NULL , V4L2_CID_PIXEL_RATE , 1 ,
INT_MAX , 1 , 1 ) ;
if ( handler - > error ) {
ret = handler - > error ;
goto error_ctrls ;
}
ctrls - > exposure - > flags | = V4L2_CTRL_FLAG_VOLATILE ;
ctrls - > gain - > flags | = V4L2_CTRL_FLAG_VOLATILE ;
ctrls - > link_freq - > flags | = V4L2_CTRL_FLAG_READ_ONLY ;
ctrls - > pixel_rate - > flags | = V4L2_CTRL_FLAG_READ_ONLY ;
sensor - > subdev . ctrl_handler = handler ;
return 0 ;
error_ctrls :
v4l2_ctrl_handler_free ( handler ) ;
return ret ;
}
/* Subdev Video Operations */
static int ov5648_s_stream ( struct v4l2_subdev * subdev , int enable )
{
struct ov5648_sensor * sensor = ov5648_subdev_sensor ( subdev ) ;
struct ov5648_state * state = & sensor - > state ;
int ret ;
if ( enable ) {
2021-04-23 17:19:15 +02:00
ret = pm_runtime_resume_and_get ( sensor - > dev ) ;
if ( ret < 0 )
2020-12-31 15:23:57 +01:00
return ret ;
}
mutex_lock ( & sensor - > mutex ) ;
ret = ov5648_sw_standby ( sensor , ! enable ) ;
mutex_unlock ( & sensor - > mutex ) ;
if ( ret )
return ret ;
state - > streaming = ! ! enable ;
if ( ! enable )
pm_runtime_put ( sensor - > dev ) ;
return 0 ;
}
static int ov5648_g_frame_interval ( struct v4l2_subdev * subdev ,
struct v4l2_subdev_frame_interval * interval )
{
struct ov5648_sensor * sensor = ov5648_subdev_sensor ( subdev ) ;
const struct ov5648_mode * mode ;
int ret = 0 ;
mutex_lock ( & sensor - > mutex ) ;
mode = sensor - > state . mode ;
switch ( sensor - > state . mbus_code ) {
case MEDIA_BUS_FMT_SBGGR8_1X8 :
interval - > interval = mode - > frame_interval [ 0 ] ;
break ;
case MEDIA_BUS_FMT_SBGGR10_1X10 :
interval - > interval = mode - > frame_interval [ 1 ] ;
break ;
default :
ret = - EINVAL ;
}
mutex_unlock ( & sensor - > mutex ) ;
return ret ;
}
static const struct v4l2_subdev_video_ops ov5648_subdev_video_ops = {
. s_stream = ov5648_s_stream ,
. g_frame_interval = ov5648_g_frame_interval ,
. s_frame_interval = ov5648_g_frame_interval ,
} ;
/* Subdev Pad Operations */
static int ov5648_enum_mbus_code ( 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 ,
2020-12-31 15:23:57 +01:00
struct v4l2_subdev_mbus_code_enum * code_enum )
{
if ( code_enum - > index > = ARRAY_SIZE ( ov5648_mbus_codes ) )
return - EINVAL ;
code_enum - > code = ov5648_mbus_codes [ code_enum - > index ] ;
return 0 ;
}
static void ov5648_mbus_format_fill ( struct v4l2_mbus_framefmt * mbus_format ,
u32 mbus_code ,
const struct ov5648_mode * mode )
{
mbus_format - > width = mode - > output_size_x ;
mbus_format - > height = mode - > output_size_y ;
mbus_format - > code = mbus_code ;
mbus_format - > field = V4L2_FIELD_NONE ;
mbus_format - > colorspace = V4L2_COLORSPACE_RAW ;
mbus_format - > ycbcr_enc =
V4L2_MAP_YCBCR_ENC_DEFAULT ( mbus_format - > colorspace ) ;
mbus_format - > quantization = V4L2_QUANTIZATION_FULL_RANGE ;
mbus_format - > xfer_func =
V4L2_MAP_XFER_FUNC_DEFAULT ( mbus_format - > colorspace ) ;
}
static int ov5648_get_fmt ( 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 ,
2020-12-31 15:23:57 +01:00
struct v4l2_subdev_format * format )
{
struct ov5648_sensor * sensor = ov5648_subdev_sensor ( subdev ) ;
struct v4l2_mbus_framefmt * mbus_format = & format - > format ;
mutex_lock ( & sensor - > mutex ) ;
if ( format - > which = = V4L2_SUBDEV_FORMAT_TRY )
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 17:55:58 +03:00
* mbus_format = * v4l2_subdev_get_try_format ( subdev , sd_state ,
2020-12-31 15:23:57 +01:00
format - > pad ) ;
else
ov5648_mbus_format_fill ( mbus_format , sensor - > state . mbus_code ,
sensor - > state . mode ) ;
mutex_unlock ( & sensor - > mutex ) ;
return 0 ;
}
static int ov5648_set_fmt ( 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 ,
2020-12-31 15:23:57 +01:00
struct v4l2_subdev_format * format )
{
struct ov5648_sensor * sensor = ov5648_subdev_sensor ( subdev ) ;
struct v4l2_mbus_framefmt * mbus_format = & format - > format ;
const struct ov5648_mode * mode ;
u32 mbus_code = 0 ;
unsigned int index ;
int ret = 0 ;
mutex_lock ( & sensor - > mutex ) ;
if ( sensor - > state . streaming ) {
ret = - EBUSY ;
goto complete ;
}
/* Try to find requested mbus code. */
for ( index = 0 ; index < ARRAY_SIZE ( ov5648_mbus_codes ) ; index + + ) {
if ( ov5648_mbus_codes [ index ] = = mbus_format - > code ) {
mbus_code = mbus_format - > code ;
break ;
}
}
/* Fallback to default. */
if ( ! mbus_code )
mbus_code = ov5648_mbus_codes [ 0 ] ;
/* Find the mode with nearest dimensions. */
mode = v4l2_find_nearest_size ( ov5648_modes , ARRAY_SIZE ( ov5648_modes ) ,
output_size_x , output_size_y ,
mbus_format - > width , mbus_format - > height ) ;
if ( ! mode ) {
ret = - EINVAL ;
goto complete ;
}
ov5648_mbus_format_fill ( mbus_format , mbus_code , mode ) ;
if ( format - > which = = V4L2_SUBDEV_FORMAT_TRY )
media: v4l2-subdev: add subdev-wide state struct
We have 'struct v4l2_subdev_pad_config' which contains configuration for
a single pad used for the TRY functionality, and an array of those
structs is passed to various v4l2_subdev_pad_ops.
I was working on subdev internal routing between pads, and realized that
there's no way to add TRY functionality for routes, which is not pad
specific configuration. Adding a separate struct for try-route config
wouldn't work either, as e.g. set-fmt needs to know the try-route
configuration to propagate the settings.
This patch adds a new struct, 'struct v4l2_subdev_state' (which at the
moment only contains the v4l2_subdev_pad_config array) and the new
struct is used in most of the places where v4l2_subdev_pad_config was
used. All v4l2_subdev_pad_ops functions taking v4l2_subdev_pad_config
are changed to instead take v4l2_subdev_state.
The changes to drivers/media/v4l2-core/v4l2-subdev.c and
include/media/v4l2-subdev.h were written by hand, and all the driver
changes were done with the semantic patch below. The spatch needs to be
applied to a select list of directories. I used the following shell
commands to apply the spatch:
dirs="drivers/media/i2c drivers/media/platform drivers/media/usb drivers/media/test-drivers/vimc drivers/media/pci drivers/staging/media"
for dir in $dirs; do spatch -j8 --dir --include-headers --no-show-diff --in-place --sp-file v4l2-subdev-state.cocci $dir; done
Note that Coccinelle chokes on a few drivers (gcc extensions?). With
minor changes we can make Coccinelle run fine, and these changes can be
reverted after spatch. The diff for these changes is:
For drivers/media/i2c/s5k5baf.c:
@@ -1481,7 +1481,7 @@ static int s5k5baf_set_selection(struct v4l2_subdev *sd,
&s5k5baf_cis_rect,
v4l2_subdev_get_try_crop(sd, cfg, PAD_CIS),
v4l2_subdev_get_try_compose(sd, cfg, PAD_CIS),
- v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT)
+ v4l2_subdev_get_try_crop(sd, cfg, PAD_OUT),
};
s5k5baf_set_rect_and_adjust(rects, rtype, &sel->r);
return 0;
For drivers/media/platform/s3c-camif/camif-capture.c:
@@ -1230,7 +1230,7 @@ static int s3c_camif_subdev_get_fmt(struct v4l2_subdev *sd,
*mf = camif->mbus_fmt;
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* crop rectangle at camera interface input */
mf->width = camif->camif_crop.width;
mf->height = camif->camif_crop.height;
@@ -1332,7 +1332,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
break;
- case CAMIF_SD_PAD_SOURCE_C...CAMIF_SD_PAD_SOURCE_P:
+ case CAMIF_SD_PAD_SOURCE_C:
/* Pixel format can be only changed on the sink pad. */
mf->code = camif->mbus_fmt.code;
mf->width = crop->width;
The semantic patch is:
// <smpl>
// Change function parameter
@@
identifier func;
identifier cfg;
@@
func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...)
{
<...
- cfg
+ sd_state
...>
}
// Change function declaration parameter
@@
identifier func;
identifier cfg;
type T;
@@
T func(...,
- struct v4l2_subdev_pad_config *cfg
+ struct v4l2_subdev_state *sd_state
, ...);
// Change function return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...)
{
...
}
// Change function declaration return value
@@
identifier func;
@@
- struct v4l2_subdev_pad_config
+ struct v4l2_subdev_state
*func(...);
// Some drivers pass a local pad_cfg for a single pad to a called function. Wrap it
// inside a pad_state.
@@
identifier func;
identifier pad_cfg;
@@
func(...)
{
...
struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = { .pads = &pad_cfg };
<+...
(
v4l2_subdev_call
|
sensor_call
|
isi_try_fse
|
isc_try_fse
|
saa_call_all
)
(...,
- &pad_cfg
+ &pad_state
,...)
...+>
}
// If the function uses fields from pad_config, access via state->pads
@@
identifier func;
identifier state;
@@
func(...,
struct v4l2_subdev_state *state
, ...)
{
<...
(
- state->try_fmt
+ state->pads->try_fmt
|
- state->try_crop
+ state->pads->try_crop
|
- state->try_compose
+ state->pads->try_compose
)
...>
}
// If the function accesses the filehandle, use fh->state instead
@@
struct v4l2_subdev_fh *fh;
@@
- fh->pad
+ fh->state
@@
struct v4l2_subdev_fh fh;
@@
- fh.pad
+ fh.state
// Start of vsp1 specific
@@
@@
struct vsp1_entity {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
};
@@
symbol entity;
@@
vsp1_entity_init(...)
{
...
entity->config =
- v4l2_subdev_alloc_pad_config
+ v4l2_subdev_alloc_state
(&entity->subdev);
...
}
@@
symbol entity;
@@
vsp1_entity_destroy(...)
{
...
- v4l2_subdev_free_pad_config
+ v4l2_subdev_free_state
(entity->config);
...
}
@exists@
identifier func =~ "(^vsp1.*)|(hsit_set_format)|(sru_enum_frame_size)|(sru_set_format)|(uif_get_selection)|(uif_set_selection)|(uds_enum_frame_size)|(uds_set_format)|(brx_set_format)|(brx_get_selection)|(histo_get_selection)|(histo_set_selection)|(brx_set_selection)";
symbol config;
@@
func(...) {
...
- struct v4l2_subdev_pad_config *config;
+ struct v4l2_subdev_state *config;
...
}
// End of vsp1 specific
// Start of rcar specific
@@
identifier sd;
identifier pad_cfg;
@@
rvin_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
// End of rcar specific
// Start of rockchip specific
@@
identifier func =~ "(rkisp1_rsz_get_pad_fmt)|(rkisp1_rsz_get_pad_crop)|(rkisp1_rsz_register)";
symbol rsz;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = rsz->pad_cfg };
...
- rsz->pad_cfg
+ &state
...
}
@@
identifier func =~ "(rkisp1_isp_get_pad_fmt)|(rkisp1_isp_get_pad_crop)";
symbol isp;
symbol pad_cfg;
@@
func(...)
{
+ struct v4l2_subdev_state state = { .pads = isp->pad_cfg };
...
- isp->pad_cfg
+ &state
...
}
@@
symbol rkisp1;
symbol isp;
symbol pad_cfg;
@@
rkisp1_isp_register(...)
{
+ struct v4l2_subdev_state state = { .pads = rkisp1->isp.pad_cfg };
...
- rkisp1->isp.pad_cfg
+ &state
...
}
// End of rockchip specific
// Start of tegra-video specific
@@
identifier sd;
identifier pad_cfg;
@@
__tegra_channel_try_format(...)
{
...
- struct v4l2_subdev_pad_config *pad_cfg;
+ struct v4l2_subdev_state *sd_state;
...
- pad_cfg = v4l2_subdev_alloc_pad_config(sd);
+ sd_state = v4l2_subdev_alloc_state(sd);
<...
- pad_cfg
+ sd_state
...>
- v4l2_subdev_free_pad_config(pad_cfg);
+ v4l2_subdev_free_state(sd_state);
...
}
@@
identifier sd_state;
@@
__tegra_channel_try_format(...)
{
...
struct v4l2_subdev_state *sd_state;
<...
- sd_state->try_crop
+ sd_state->pads->try_crop
...>
}
// End of tegra-video specific
// </smpl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2021-06-10 17:55:58 +03:00
* v4l2_subdev_get_try_format ( subdev , sd_state , format - > pad ) =
2020-12-31 15:23:57 +01:00
* mbus_format ;
else if ( sensor - > state . mode ! = mode | |
sensor - > state . mbus_code ! = mbus_code )
ret = ov5648_state_configure ( sensor , mode , mbus_code ) ;
complete :
mutex_unlock ( & sensor - > mutex ) ;
return ret ;
}
static int ov5648_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 ,
2020-12-31 15:23:57 +01:00
struct v4l2_subdev_frame_size_enum * size_enum )
{
const struct ov5648_mode * mode ;
if ( size_enum - > index > = ARRAY_SIZE ( ov5648_modes ) )
return - EINVAL ;
mode = & ov5648_modes [ size_enum - > index ] ;
size_enum - > min_width = size_enum - > max_width = mode - > output_size_x ;
size_enum - > min_height = size_enum - > max_height = mode - > output_size_y ;
return 0 ;
}
static int ov5648_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 ,
2020-12-31 15:23:57 +01:00
struct v4l2_subdev_frame_interval_enum * interval_enum )
{
const struct ov5648_mode * mode = NULL ;
unsigned int mode_index ;
unsigned int interval_index ;
if ( interval_enum - > index > 0 )
return - EINVAL ;
/*
* Multiple modes with the same dimensions may have different frame
* intervals , so look up each relevant mode .
*/
for ( mode_index = 0 , interval_index = 0 ;
mode_index < ARRAY_SIZE ( ov5648_modes ) ; mode_index + + ) {
mode = & ov5648_modes [ mode_index ] ;
if ( mode - > output_size_x = = interval_enum - > width & &
mode - > output_size_y = = interval_enum - > height ) {
if ( interval_index = = interval_enum - > index )
break ;
interval_index + + ;
}
}
2021-01-25 09:46:03 +01:00
if ( mode_index = = ARRAY_SIZE ( ov5648_modes ) )
2020-12-31 15:23:57 +01:00
return - EINVAL ;
switch ( interval_enum - > code ) {
case MEDIA_BUS_FMT_SBGGR8_1X8 :
interval_enum - > interval = mode - > frame_interval [ 0 ] ;
break ;
case MEDIA_BUS_FMT_SBGGR10_1X10 :
interval_enum - > interval = mode - > frame_interval [ 1 ] ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static const struct v4l2_subdev_pad_ops ov5648_subdev_pad_ops = {
. enum_mbus_code = ov5648_enum_mbus_code ,
. get_fmt = ov5648_get_fmt ,
. set_fmt = ov5648_set_fmt ,
. enum_frame_size = ov5648_enum_frame_size ,
. enum_frame_interval = ov5648_enum_frame_interval ,
} ;
static const struct v4l2_subdev_ops ov5648_subdev_ops = {
. video = & ov5648_subdev_video_ops ,
. pad = & ov5648_subdev_pad_ops ,
} ;
static int ov5648_suspend ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct v4l2_subdev * subdev = i2c_get_clientdata ( client ) ;
struct ov5648_sensor * sensor = ov5648_subdev_sensor ( subdev ) ;
struct ov5648_state * state = & sensor - > state ;
int ret = 0 ;
mutex_lock ( & sensor - > mutex ) ;
if ( state - > streaming ) {
ret = ov5648_sw_standby ( sensor , true ) ;
if ( ret )
goto complete ;
}
ret = ov5648_sensor_power ( sensor , false ) ;
if ( ret )
ov5648_sw_standby ( sensor , false ) ;
complete :
mutex_unlock ( & sensor - > mutex ) ;
return ret ;
}
static int ov5648_resume ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct v4l2_subdev * subdev = i2c_get_clientdata ( client ) ;
struct ov5648_sensor * sensor = ov5648_subdev_sensor ( subdev ) ;
struct ov5648_state * state = & sensor - > state ;
int ret = 0 ;
mutex_lock ( & sensor - > mutex ) ;
ret = ov5648_sensor_power ( sensor , true ) ;
if ( ret )
goto complete ;
ret = ov5648_sensor_init ( sensor ) ;
if ( ret )
goto error_power ;
ret = __v4l2_ctrl_handler_setup ( & sensor - > ctrls . handler ) ;
if ( ret )
goto error_power ;
if ( state - > streaming ) {
ret = ov5648_sw_standby ( sensor , false ) ;
if ( ret )
goto error_power ;
}
goto complete ;
error_power :
ov5648_sensor_power ( sensor , false ) ;
complete :
mutex_unlock ( & sensor - > mutex ) ;
return ret ;
}
static int ov5648_probe ( struct i2c_client * client )
{
struct device * dev = & client - > dev ;
struct fwnode_handle * handle ;
struct ov5648_sensor * sensor ;
struct v4l2_subdev * subdev ;
struct media_pad * pad ;
unsigned long rate ;
int ret ;
sensor = devm_kzalloc ( dev , sizeof ( * sensor ) , GFP_KERNEL ) ;
if ( ! sensor )
return - ENOMEM ;
sensor - > dev = dev ;
sensor - > i2c_client = client ;
/* Graph Endpoint */
handle = fwnode_graph_get_next_endpoint ( dev_fwnode ( dev ) , NULL ) ;
if ( ! handle ) {
2021-01-13 11:05:17 +01:00
dev_err ( dev , " unable to find endpoint node \n " ) ;
2020-12-31 15:23:57 +01:00
return - EINVAL ;
}
sensor - > endpoint . bus_type = V4L2_MBUS_CSI2_DPHY ;
ret = v4l2_fwnode_endpoint_alloc_parse ( handle , & sensor - > endpoint ) ;
fwnode_handle_put ( handle ) ;
if ( ret ) {
dev_err ( dev , " failed to parse endpoint node \n " ) ;
return ret ;
}
/* GPIOs */
sensor - > powerdown = devm_gpiod_get_optional ( dev , " powerdown " ,
GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( sensor - > powerdown ) ) {
ret = PTR_ERR ( sensor - > powerdown ) ;
goto error_endpoint ;
}
sensor - > reset = devm_gpiod_get_optional ( dev , " reset " , GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( sensor - > reset ) ) {
ret = PTR_ERR ( sensor - > reset ) ;
goto error_endpoint ;
}
/* Regulators */
/* DVDD: digital core */
sensor - > dvdd = devm_regulator_get ( dev , " dvdd " ) ;
if ( IS_ERR ( sensor - > dvdd ) ) {
dev_err ( dev , " cannot get DVDD (digital core) regulator \n " ) ;
ret = PTR_ERR ( sensor - > dvdd ) ;
goto error_endpoint ;
}
/* DOVDD: digital I/O */
sensor - > dovdd = devm_regulator_get ( dev , " dovdd " ) ;
2021-03-30 15:04:46 +02:00
if ( IS_ERR ( sensor - > dovdd ) ) {
2020-12-31 15:23:57 +01:00
dev_err ( dev , " cannot get DOVDD (digital I/O) regulator \n " ) ;
2021-03-30 15:04:46 +02:00
ret = PTR_ERR ( sensor - > dovdd ) ;
2020-12-31 15:23:57 +01:00
goto error_endpoint ;
}
/* AVDD: analog */
sensor - > avdd = devm_regulator_get_optional ( dev , " avdd " ) ;
if ( IS_ERR ( sensor - > avdd ) ) {
dev_info ( dev , " no AVDD regulator provided, using internal \n " ) ;
sensor - > avdd = NULL ;
}
/* External Clock */
sensor - > xvclk = devm_clk_get ( dev , NULL ) ;
if ( IS_ERR ( sensor - > xvclk ) ) {
dev_err ( dev , " failed to get external clock \n " ) ;
ret = PTR_ERR ( sensor - > xvclk ) ;
goto error_endpoint ;
}
rate = clk_get_rate ( sensor - > xvclk ) ;
if ( rate ! = OV5648_XVCLK_RATE ) {
dev_err ( dev , " clock rate %lu Hz is unsupported \n " , rate ) ;
ret = - EINVAL ;
goto error_endpoint ;
}
/* Subdev, entity and pad */
subdev = & sensor - > subdev ;
v4l2_i2c_subdev_init ( subdev , client , & ov5648_subdev_ops ) ;
subdev - > flags | = V4L2_SUBDEV_FL_HAS_DEVNODE ;
subdev - > entity . function = MEDIA_ENT_F_CAM_SENSOR ;
pad = & sensor - > pad ;
pad - > flags = MEDIA_PAD_FL_SOURCE ;
ret = media_entity_pads_init ( & subdev - > entity , 1 , pad ) ;
if ( ret )
goto error_entity ;
/* Mutex */
mutex_init ( & sensor - > mutex ) ;
/* Sensor */
ret = ov5648_ctrls_init ( sensor ) ;
if ( ret )
goto error_mutex ;
ret = ov5648_state_init ( sensor ) ;
if ( ret )
goto error_ctrls ;
/* Runtime PM */
pm_runtime_enable ( sensor - > dev ) ;
pm_runtime_set_suspended ( sensor - > dev ) ;
/* V4L2 subdev register */
2021-03-05 17:42:34 +01:00
ret = v4l2_async_register_subdev_sensor ( subdev ) ;
2020-12-31 15:23:57 +01:00
if ( ret )
goto error_pm ;
return 0 ;
error_pm :
pm_runtime_disable ( sensor - > dev ) ;
error_ctrls :
v4l2_ctrl_handler_free ( & sensor - > ctrls . handler ) ;
error_mutex :
mutex_destroy ( & sensor - > mutex ) ;
error_entity :
media_entity_cleanup ( & sensor - > subdev . entity ) ;
error_endpoint :
v4l2_fwnode_endpoint_free ( & sensor - > endpoint ) ;
return ret ;
}
2022-08-15 10:02:30 +02:00
static void ov5648_remove ( struct i2c_client * client )
2020-12-31 15:23:57 +01:00
{
struct v4l2_subdev * subdev = i2c_get_clientdata ( client ) ;
struct ov5648_sensor * sensor = ov5648_subdev_sensor ( subdev ) ;
v4l2_async_unregister_subdev ( subdev ) ;
pm_runtime_disable ( sensor - > dev ) ;
v4l2_ctrl_handler_free ( & sensor - > ctrls . handler ) ;
mutex_destroy ( & sensor - > mutex ) ;
media_entity_cleanup ( & subdev - > entity ) ;
2022-09-20 11:27:48 -03:00
v4l2_fwnode_endpoint_free ( & sensor - > endpoint ) ;
2020-12-31 15:23:57 +01:00
}
static const struct dev_pm_ops ov5648_pm_ops = {
SET_RUNTIME_PM_OPS ( ov5648_suspend , ov5648_resume , NULL )
} ;
static const struct of_device_id ov5648_of_match [ ] = {
{ . compatible = " ovti,ov5648 " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , ov5648_of_match ) ;
static struct i2c_driver ov5648_driver = {
. driver = {
. name = " ov5648 " ,
. of_match_table = ov5648_of_match ,
. pm = & ov5648_pm_ops ,
} ,
. probe_new = ov5648_probe ,
. remove = ov5648_remove ,
} ;
module_i2c_driver ( ov5648_driver ) ;
MODULE_AUTHOR ( " Paul Kocialkowski <paul.kocialkowski@bootlin.com> " ) ;
MODULE_DESCRIPTION ( " V4L2 driver for the OmniVision OV5648 image sensor " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;