2020-12-31 15:27:01 +01:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2020 Kévin L ' hôpital < kevin . lhopital @ bootlin . com >
* Copyright 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 OV8865_EXTCLK_RATE 24000000
/* Register definitions */
/* System */
# define OV8865_SW_STANDBY_REG 0x100
# define OV8865_SW_STANDBY_STREAM_ON BIT(0)
# define OV8865_SW_RESET_REG 0x103
# define OV8865_SW_RESET_RESET BIT(0)
# define OV8865_PLL_CTRL0_REG 0x300
# define OV8865_PLL_CTRL0_PRE_DIV(v) ((v) & GENMASK(2, 0))
# define OV8865_PLL_CTRL1_REG 0x301
# define OV8865_PLL_CTRL1_MUL_H(v) (((v) & GENMASK(9, 8)) >> 8)
# define OV8865_PLL_CTRL2_REG 0x302
# define OV8865_PLL_CTRL2_MUL_L(v) ((v) & GENMASK(7, 0))
# define OV8865_PLL_CTRL3_REG 0x303
# define OV8865_PLL_CTRL3_M_DIV(v) (((v) - 1) & GENMASK(3, 0))
# define OV8865_PLL_CTRL4_REG 0x304
# define OV8865_PLL_CTRL4_MIPI_DIV(v) ((v) & GENMASK(1, 0))
# define OV8865_PLL_CTRL5_REG 0x305
# define OV8865_PLL_CTRL5_SYS_PRE_DIV(v) ((v) & GENMASK(1, 0))
# define OV8865_PLL_CTRL6_REG 0x306
# define OV8865_PLL_CTRL6_SYS_DIV(v) (((v) - 1) & BIT(0))
# define OV8865_PLL_CTRL8_REG 0x308
# define OV8865_PLL_CTRL9_REG 0x309
# define OV8865_PLL_CTRLA_REG 0x30a
# define OV8865_PLL_CTRLA_PRE_DIV_HALF(v) (((v) - 1) & BIT(0))
# define OV8865_PLL_CTRLB_REG 0x30b
# define OV8865_PLL_CTRLB_PRE_DIV(v) ((v) & GENMASK(2, 0))
# define OV8865_PLL_CTRLC_REG 0x30c
# define OV8865_PLL_CTRLC_MUL_H(v) (((v) & GENMASK(9, 8)) >> 8)
# define OV8865_PLL_CTRLD_REG 0x30d
# define OV8865_PLL_CTRLD_MUL_L(v) ((v) & GENMASK(7, 0))
# define OV8865_PLL_CTRLE_REG 0x30e
# define OV8865_PLL_CTRLE_SYS_DIV(v) ((v) & GENMASK(2, 0))
# define OV8865_PLL_CTRLF_REG 0x30f
# define OV8865_PLL_CTRLF_SYS_PRE_DIV(v) (((v) - 1) & GENMASK(3, 0))
# define OV8865_PLL_CTRL10_REG 0x310
# define OV8865_PLL_CTRL11_REG 0x311
# define OV8865_PLL_CTRL12_REG 0x312
# define OV8865_PLL_CTRL12_PRE_DIV_HALF(v) ((((v) - 1) << 4) & BIT(4))
# define OV8865_PLL_CTRL12_DAC_DIV(v) (((v) - 1) & GENMASK(3, 0))
# define OV8865_PLL_CTRL1B_REG 0x31b
# define OV8865_PLL_CTRL1C_REG 0x31c
# define OV8865_PLL_CTRL1E_REG 0x31e
# define OV8865_PLL_CTRL1E_PLL1_NO_LAT BIT(3)
# define OV8865_PAD_OEN0_REG 0x3000
# define OV8865_PAD_OEN2_REG 0x3002
# define OV8865_CLK_RST5_REG 0x3005
# define OV8865_CHIP_ID_HH_REG 0x300a
# define OV8865_CHIP_ID_HH_VALUE 0x00
# define OV8865_CHIP_ID_H_REG 0x300b
# define OV8865_CHIP_ID_H_VALUE 0x88
# define OV8865_CHIP_ID_L_REG 0x300c
# define OV8865_CHIP_ID_L_VALUE 0x65
# define OV8865_PAD_OUT2_REG 0x300d
# define OV8865_PAD_SEL2_REG 0x3010
# define OV8865_PAD_PK_REG 0x3011
# define OV8865_PAD_PK_DRIVE_STRENGTH_1X (0 << 5)
# define OV8865_PAD_PK_DRIVE_STRENGTH_2X (1 << 5)
# define OV8865_PAD_PK_DRIVE_STRENGTH_3X (2 << 5)
# define OV8865_PAD_PK_DRIVE_STRENGTH_4X (3 << 5)
# define OV8865_PUMP_CLK_DIV_REG 0x3015
# define OV8865_PUMP_CLK_DIV_PUMP_N(v) (((v) << 4) & GENMASK(6, 4))
# define OV8865_PUMP_CLK_DIV_PUMP_P(v) ((v) & GENMASK(2, 0))
# define OV8865_MIPI_SC_CTRL0_REG 0x3018
# define OV8865_MIPI_SC_CTRL0_LANES(v) ((((v) - 1) << 5) & \
GENMASK ( 7 , 5 ) )
# define OV8865_MIPI_SC_CTRL0_MIPI_EN BIT(4)
# define OV8865_MIPI_SC_CTRL0_UNKNOWN BIT(1)
# define OV8865_MIPI_SC_CTRL0_LANES_PD_MIPI BIT(0)
# define OV8865_MIPI_SC_CTRL1_REG 0x3019
# define OV8865_CLK_RST0_REG 0x301a
# define OV8865_CLK_RST1_REG 0x301b
# define OV8865_CLK_RST2_REG 0x301c
# define OV8865_CLK_RST3_REG 0x301d
# define OV8865_CLK_RST4_REG 0x301e
# define OV8865_PCLK_SEL_REG 0x3020
# define OV8865_PCLK_SEL_PCLK_DIV_MASK BIT(3)
# define OV8865_PCLK_SEL_PCLK_DIV(v) ((((v) - 1) << 3) & BIT(3))
# define OV8865_MISC_CTRL_REG 0x3021
# define OV8865_MIPI_SC_CTRL2_REG 0x3022
# define OV8865_MIPI_SC_CTRL2_CLK_LANES_PD_MIPI BIT(1)
# define OV8865_MIPI_SC_CTRL2_PD_MIPI_RST_SYNC BIT(0)
# define OV8865_MIPI_BIT_SEL_REG 0x3031
# define OV8865_MIPI_BIT_SEL(v) (((v) << 0) & GENMASK(4, 0))
# define OV8865_CLK_SEL0_REG 0x3032
# define OV8865_CLK_SEL0_PLL1_SYS_SEL(v) (((v) << 7) & BIT(7))
# define OV8865_CLK_SEL1_REG 0x3033
# define OV8865_CLK_SEL1_MIPI_EOF BIT(5)
# define OV8865_CLK_SEL1_UNKNOWN BIT(2)
# define OV8865_CLK_SEL1_PLL_SCLK_SEL_MASK BIT(1)
# define OV8865_CLK_SEL1_PLL_SCLK_SEL(v) (((v) << 1) & BIT(1))
# define OV8865_SCLK_CTRL_REG 0x3106
# define OV8865_SCLK_CTRL_SCLK_DIV(v) (((v) << 4) & GENMASK(7, 4))
# define OV8865_SCLK_CTRL_SCLK_PRE_DIV(v) (((v) << 2) & GENMASK(3, 2))
# define OV8865_SCLK_CTRL_UNKNOWN BIT(0)
/* Exposure/gain */
# define OV8865_EXPOSURE_CTRL_HH_REG 0x3500
# define OV8865_EXPOSURE_CTRL_HH(v) (((v) & GENMASK(19, 16)) >> 16)
# define OV8865_EXPOSURE_CTRL_H_REG 0x3501
# define OV8865_EXPOSURE_CTRL_H(v) (((v) & GENMASK(15, 8)) >> 8)
# define OV8865_EXPOSURE_CTRL_L_REG 0x3502
# define OV8865_EXPOSURE_CTRL_L(v) ((v) & GENMASK(7, 0))
# define OV8865_EXPOSURE_GAIN_MANUAL_REG 0x3503
# define OV8865_GAIN_CTRL_H_REG 0x3508
# define OV8865_GAIN_CTRL_H(v) (((v) & GENMASK(12, 8)) >> 8)
# define OV8865_GAIN_CTRL_L_REG 0x3509
# define OV8865_GAIN_CTRL_L(v) ((v) & GENMASK(7, 0))
/* Timing */
# define OV8865_CROP_START_X_H_REG 0x3800
# define OV8865_CROP_START_X_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV8865_CROP_START_X_L_REG 0x3801
# define OV8865_CROP_START_X_L(v) ((v) & GENMASK(7, 0))
# define OV8865_CROP_START_Y_H_REG 0x3802
# define OV8865_CROP_START_Y_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV8865_CROP_START_Y_L_REG 0x3803
# define OV8865_CROP_START_Y_L(v) ((v) & GENMASK(7, 0))
# define OV8865_CROP_END_X_H_REG 0x3804
# define OV8865_CROP_END_X_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV8865_CROP_END_X_L_REG 0x3805
# define OV8865_CROP_END_X_L(v) ((v) & GENMASK(7, 0))
# define OV8865_CROP_END_Y_H_REG 0x3806
# define OV8865_CROP_END_Y_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV8865_CROP_END_Y_L_REG 0x3807
# define OV8865_CROP_END_Y_L(v) ((v) & GENMASK(7, 0))
# define OV8865_OUTPUT_SIZE_X_H_REG 0x3808
# define OV8865_OUTPUT_SIZE_X_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV8865_OUTPUT_SIZE_X_L_REG 0x3809
# define OV8865_OUTPUT_SIZE_X_L(v) ((v) & GENMASK(7, 0))
# define OV8865_OUTPUT_SIZE_Y_H_REG 0x380a
# define OV8865_OUTPUT_SIZE_Y_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV8865_OUTPUT_SIZE_Y_L_REG 0x380b
# define OV8865_OUTPUT_SIZE_Y_L(v) ((v) & GENMASK(7, 0))
# define OV8865_HTS_H_REG 0x380c
# define OV8865_HTS_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV8865_HTS_L_REG 0x380d
# define OV8865_HTS_L(v) ((v) & GENMASK(7, 0))
# define OV8865_VTS_H_REG 0x380e
# define OV8865_VTS_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV8865_VTS_L_REG 0x380f
# define OV8865_VTS_L(v) ((v) & GENMASK(7, 0))
# define OV8865_OFFSET_X_H_REG 0x3810
# define OV8865_OFFSET_X_H(v) (((v) & GENMASK(15, 8)) >> 8)
# define OV8865_OFFSET_X_L_REG 0x3811
# define OV8865_OFFSET_X_L(v) ((v) & GENMASK(7, 0))
# define OV8865_OFFSET_Y_H_REG 0x3812
# define OV8865_OFFSET_Y_H(v) (((v) & GENMASK(14, 8)) >> 8)
# define OV8865_OFFSET_Y_L_REG 0x3813
# define OV8865_OFFSET_Y_L(v) ((v) & GENMASK(7, 0))
# define OV8865_INC_X_ODD_REG 0x3814
# define OV8865_INC_X_ODD(v) ((v) & GENMASK(4, 0))
# define OV8865_INC_X_EVEN_REG 0x3815
# define OV8865_INC_X_EVEN(v) ((v) & GENMASK(4, 0))
# define OV8865_VSYNC_START_H_REG 0x3816
# define OV8865_VSYNC_START_H(v) (((v) & GENMASK(15, 8)) >> 8)
# define OV8865_VSYNC_START_L_REG 0x3817
# define OV8865_VSYNC_START_L(v) ((v) & GENMASK(7, 0))
# define OV8865_VSYNC_END_H_REG 0x3818
# define OV8865_VSYNC_END_H(v) (((v) & GENMASK(15, 8)) >> 8)
# define OV8865_VSYNC_END_L_REG 0x3819
# define OV8865_VSYNC_END_L(v) ((v) & GENMASK(7, 0))
# define OV8865_HSYNC_FIRST_H_REG 0x381a
# define OV8865_HSYNC_FIRST_H(v) (((v) & GENMASK(15, 8)) >> 8)
# define OV8865_HSYNC_FIRST_L_REG 0x381b
# define OV8865_HSYNC_FIRST_L(v) ((v) & GENMASK(7, 0))
# define OV8865_FORMAT1_REG 0x3820
# define OV8865_FORMAT1_FLIP_VERT_ISP_EN BIT(2)
# define OV8865_FORMAT1_FLIP_VERT_SENSOR_EN BIT(1)
# define OV8865_FORMAT2_REG 0x3821
# define OV8865_FORMAT2_HSYNC_EN BIT(6)
# define OV8865_FORMAT2_FST_VBIN_EN BIT(5)
# define OV8865_FORMAT2_FST_HBIN_EN BIT(4)
# define OV8865_FORMAT2_ISP_HORZ_VAR2_EN BIT(3)
# define OV8865_FORMAT2_FLIP_HORZ_ISP_EN BIT(2)
# define OV8865_FORMAT2_FLIP_HORZ_SENSOR_EN BIT(1)
# define OV8865_FORMAT2_SYNC_HBIN_EN BIT(0)
# define OV8865_INC_Y_ODD_REG 0x382a
# define OV8865_INC_Y_ODD(v) ((v) & GENMASK(4, 0))
# define OV8865_INC_Y_EVEN_REG 0x382b
# define OV8865_INC_Y_EVEN(v) ((v) & GENMASK(4, 0))
# define OV8865_ABLC_NUM_REG 0x3830
# define OV8865_ABLC_NUM(v) ((v) & GENMASK(4, 0))
# define OV8865_ZLINE_NUM_REG 0x3836
# define OV8865_ZLINE_NUM(v) ((v) & GENMASK(4, 0))
# define OV8865_AUTO_SIZE_CTRL_REG 0x3841
# define OV8865_AUTO_SIZE_CTRL_OFFSET_Y_REG BIT(5)
# define OV8865_AUTO_SIZE_CTRL_OFFSET_X_REG BIT(4)
# define OV8865_AUTO_SIZE_CTRL_CROP_END_Y_REG BIT(3)
# define OV8865_AUTO_SIZE_CTRL_CROP_END_X_REG BIT(2)
# define OV8865_AUTO_SIZE_CTRL_CROP_START_Y_REG BIT(1)
# define OV8865_AUTO_SIZE_CTRL_CROP_START_X_REG BIT(0)
# define OV8865_AUTO_SIZE_X_OFFSET_H_REG 0x3842
# define OV8865_AUTO_SIZE_X_OFFSET_L_REG 0x3843
# define OV8865_AUTO_SIZE_Y_OFFSET_H_REG 0x3844
# define OV8865_AUTO_SIZE_Y_OFFSET_L_REG 0x3845
# define OV8865_AUTO_SIZE_BOUNDARIES_REG 0x3846
# define OV8865_AUTO_SIZE_BOUNDARIES_Y(v) (((v) << 4) & GENMASK(7, 4))
# define OV8865_AUTO_SIZE_BOUNDARIES_X(v) ((v) & GENMASK(3, 0))
/* PSRAM */
# define OV8865_PSRAM_CTRL8_REG 0x3f08
/* Black Level */
# define OV8865_BLC_CTRL0_REG 0x4000
# define OV8865_BLC_CTRL0_TRIG_RANGE_EN BIT(7)
# define OV8865_BLC_CTRL0_TRIG_FORMAT_EN BIT(6)
# define OV8865_BLC_CTRL0_TRIG_GAIN_EN BIT(5)
# define OV8865_BLC_CTRL0_TRIG_EXPOSURE_EN BIT(4)
# define OV8865_BLC_CTRL0_TRIG_MANUAL_EN BIT(3)
# define OV8865_BLC_CTRL0_FREEZE_EN BIT(2)
# define OV8865_BLC_CTRL0_ALWAYS_EN BIT(1)
# define OV8865_BLC_CTRL0_FILTER_EN BIT(0)
# define OV8865_BLC_CTRL1_REG 0x4001
# define OV8865_BLC_CTRL1_DITHER_EN BIT(7)
# define OV8865_BLC_CTRL1_ZERO_LINE_DIFF_EN BIT(6)
# define OV8865_BLC_CTRL1_COL_SHIFT_256 (0 << 4)
# define OV8865_BLC_CTRL1_COL_SHIFT_128 (1 << 4)
# define OV8865_BLC_CTRL1_COL_SHIFT_64 (2 << 4)
# define OV8865_BLC_CTRL1_COL_SHIFT_32 (3 << 4)
# define OV8865_BLC_CTRL1_OFFSET_LIMIT_EN BIT(2)
# define OV8865_BLC_CTRL1_COLUMN_CANCEL_EN BIT(1)
# define OV8865_BLC_CTRL2_REG 0x4002
# define OV8865_BLC_CTRL3_REG 0x4003
# define OV8865_BLC_CTRL4_REG 0x4004
# define OV8865_BLC_CTRL5_REG 0x4005
# define OV8865_BLC_CTRL6_REG 0x4006
# define OV8865_BLC_CTRL7_REG 0x4007
# define OV8865_BLC_CTRL8_REG 0x4008
# define OV8865_BLC_CTRL9_REG 0x4009
# define OV8865_BLC_CTRLA_REG 0x400a
# define OV8865_BLC_CTRLB_REG 0x400b
# define OV8865_BLC_CTRLC_REG 0x400c
# define OV8865_BLC_CTRLD_REG 0x400d
# define OV8865_BLC_CTRLD_OFFSET_TRIGGER(v) ((v) & GENMASK(7, 0))
# define OV8865_BLC_CTRL1F_REG 0x401f
# define OV8865_BLC_CTRL1F_RB_REVERSE BIT(3)
# define OV8865_BLC_CTRL1F_INTERPOL_X_EN BIT(2)
# define OV8865_BLC_CTRL1F_INTERPOL_Y_EN BIT(1)
# define OV8865_BLC_ANCHOR_LEFT_START_H_REG 0x4020
# define OV8865_BLC_ANCHOR_LEFT_START_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV8865_BLC_ANCHOR_LEFT_START_L_REG 0x4021
# define OV8865_BLC_ANCHOR_LEFT_START_L(v) ((v) & GENMASK(7, 0))
# define OV8865_BLC_ANCHOR_LEFT_END_H_REG 0x4022
# define OV8865_BLC_ANCHOR_LEFT_END_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV8865_BLC_ANCHOR_LEFT_END_L_REG 0x4023
# define OV8865_BLC_ANCHOR_LEFT_END_L(v) ((v) & GENMASK(7, 0))
# define OV8865_BLC_ANCHOR_RIGHT_START_H_REG 0x4024
# define OV8865_BLC_ANCHOR_RIGHT_START_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV8865_BLC_ANCHOR_RIGHT_START_L_REG 0x4025
# define OV8865_BLC_ANCHOR_RIGHT_START_L(v) ((v) & GENMASK(7, 0))
# define OV8865_BLC_ANCHOR_RIGHT_END_H_REG 0x4026
# define OV8865_BLC_ANCHOR_RIGHT_END_H(v) (((v) & GENMASK(11, 8)) >> 8)
# define OV8865_BLC_ANCHOR_RIGHT_END_L_REG 0x4027
# define OV8865_BLC_ANCHOR_RIGHT_END_L(v) ((v) & GENMASK(7, 0))
# define OV8865_BLC_TOP_ZLINE_START_REG 0x4028
# define OV8865_BLC_TOP_ZLINE_START(v) ((v) & GENMASK(5, 0))
# define OV8865_BLC_TOP_ZLINE_NUM_REG 0x4029
# define OV8865_BLC_TOP_ZLINE_NUM(v) ((v) & GENMASK(4, 0))
# define OV8865_BLC_TOP_BLKLINE_START_REG 0x402a
# define OV8865_BLC_TOP_BLKLINE_START(v) ((v) & GENMASK(5, 0))
# define OV8865_BLC_TOP_BLKLINE_NUM_REG 0x402b
# define OV8865_BLC_TOP_BLKLINE_NUM(v) ((v) & GENMASK(4, 0))
# define OV8865_BLC_BOT_ZLINE_START_REG 0x402c
# define OV8865_BLC_BOT_ZLINE_START(v) ((v) & GENMASK(5, 0))
# define OV8865_BLC_BOT_ZLINE_NUM_REG 0x402d
# define OV8865_BLC_BOT_ZLINE_NUM(v) ((v) & GENMASK(4, 0))
# define OV8865_BLC_BOT_BLKLINE_START_REG 0x402e
# define OV8865_BLC_BOT_BLKLINE_START(v) ((v) & GENMASK(5, 0))
# define OV8865_BLC_BOT_BLKLINE_NUM_REG 0x402f
# define OV8865_BLC_BOT_BLKLINE_NUM(v) ((v) & GENMASK(4, 0))
# define OV8865_BLC_OFFSET_LIMIT_REG 0x4034
# define OV8865_BLC_OFFSET_LIMIT(v) ((v) & GENMASK(7, 0))
/* VFIFO */
# define OV8865_VFIFO_READ_START_H_REG 0x4600
# define OV8865_VFIFO_READ_START_H(v) (((v) & GENMASK(15, 8)) >> 8)
# define OV8865_VFIFO_READ_START_L_REG 0x4601
# define OV8865_VFIFO_READ_START_L(v) ((v) & GENMASK(7, 0))
/* MIPI */
# define OV8865_MIPI_CTRL0_REG 0x4800
# define OV8865_MIPI_CTRL1_REG 0x4801
# define OV8865_MIPI_CTRL2_REG 0x4802
# define OV8865_MIPI_CTRL3_REG 0x4803
# define OV8865_MIPI_CTRL4_REG 0x4804
# define OV8865_MIPI_CTRL5_REG 0x4805
# define OV8865_MIPI_CTRL6_REG 0x4806
# define OV8865_MIPI_CTRL7_REG 0x4807
# define OV8865_MIPI_CTRL8_REG 0x4808
# define OV8865_MIPI_FCNT_MAX_H_REG 0x4810
# define OV8865_MIPI_FCNT_MAX_L_REG 0x4811
# define OV8865_MIPI_CTRL13_REG 0x4813
# define OV8865_MIPI_CTRL14_REG 0x4814
# define OV8865_MIPI_CTRL15_REG 0x4815
# define OV8865_MIPI_EMBEDDED_DT_REG 0x4816
# define OV8865_MIPI_HS_ZERO_MIN_H_REG 0x4818
# define OV8865_MIPI_HS_ZERO_MIN_L_REG 0x4819
# define OV8865_MIPI_HS_TRAIL_MIN_H_REG 0x481a
# define OV8865_MIPI_HS_TRAIL_MIN_L_REG 0x481b
# define OV8865_MIPI_CLK_ZERO_MIN_H_REG 0x481c
# define OV8865_MIPI_CLK_ZERO_MIN_L_REG 0x481d
# define OV8865_MIPI_CLK_PREPARE_MAX_REG 0x481e
# define OV8865_MIPI_CLK_PREPARE_MIN_REG 0x481f
# define OV8865_MIPI_CLK_POST_MIN_H_REG 0x4820
# define OV8865_MIPI_CLK_POST_MIN_L_REG 0x4821
# define OV8865_MIPI_CLK_TRAIL_MIN_H_REG 0x4822
# define OV8865_MIPI_CLK_TRAIL_MIN_L_REG 0x4823
# define OV8865_MIPI_LPX_P_MIN_H_REG 0x4824
# define OV8865_MIPI_LPX_P_MIN_L_REG 0x4825
# define OV8865_MIPI_HS_PREPARE_MIN_REG 0x4826
# define OV8865_MIPI_HS_PREPARE_MAX_REG 0x4827
# define OV8865_MIPI_HS_EXIT_MIN_H_REG 0x4828
# define OV8865_MIPI_HS_EXIT_MIN_L_REG 0x4829
# define OV8865_MIPI_UI_HS_ZERO_MIN_REG 0x482a
# define OV8865_MIPI_UI_HS_TRAIL_MIN_REG 0x482b
# define OV8865_MIPI_UI_CLK_ZERO_MIN_REG 0x482c
# define OV8865_MIPI_UI_CLK_PREPARE_REG 0x482d
# define OV8865_MIPI_UI_CLK_POST_MIN_REG 0x482e
# define OV8865_MIPI_UI_CLK_TRAIL_MIN_REG 0x482f
# define OV8865_MIPI_UI_LPX_P_MIN_REG 0x4830
# define OV8865_MIPI_UI_HS_PREPARE_REG 0x4831
# define OV8865_MIPI_UI_HS_EXIT_MIN_REG 0x4832
# define OV8865_MIPI_PKT_START_SIZE_REG 0x4833
# define OV8865_MIPI_PCLK_PERIOD_REG 0x4837
# define OV8865_MIPI_LP_GPIO0_REG 0x4838
# define OV8865_MIPI_LP_GPIO1_REG 0x4839
# define OV8865_MIPI_CTRL3C_REG 0x483c
# define OV8865_MIPI_LP_GPIO4_REG 0x483d
# define OV8865_MIPI_CTRL4A_REG 0x484a
# define OV8865_MIPI_CTRL4B_REG 0x484b
# define OV8865_MIPI_CTRL4C_REG 0x484c
# define OV8865_MIPI_LANE_TEST_PATTERN_REG 0x484d
# define OV8865_MIPI_FRAME_END_DELAY_REG 0x484e
# define OV8865_MIPI_CLOCK_TEST_PATTERN_REG 0x484f
# define OV8865_MIPI_LANE_SEL01_REG 0x4850
# define OV8865_MIPI_LANE_SEL01_LANE0(v) (((v) << 0) & GENMASK(2, 0))
# define OV8865_MIPI_LANE_SEL01_LANE1(v) (((v) << 4) & GENMASK(6, 4))
# define OV8865_MIPI_LANE_SEL23_REG 0x4851
# define OV8865_MIPI_LANE_SEL23_LANE2(v) (((v) << 0) & GENMASK(2, 0))
# define OV8865_MIPI_LANE_SEL23_LANE3(v) (((v) << 4) & GENMASK(6, 4))
/* ISP */
# define OV8865_ISP_CTRL0_REG 0x5000
# define OV8865_ISP_CTRL0_LENC_EN BIT(7)
# define OV8865_ISP_CTRL0_WHITE_BALANCE_EN BIT(4)
# define OV8865_ISP_CTRL0_DPC_BLACK_EN BIT(2)
# define OV8865_ISP_CTRL0_DPC_WHITE_EN BIT(1)
# define OV8865_ISP_CTRL1_REG 0x5001
# define OV8865_ISP_CTRL1_BLC_EN BIT(0)
# define OV8865_ISP_CTRL2_REG 0x5002
# define OV8865_ISP_CTRL2_DEBUG BIT(3)
# define OV8865_ISP_CTRL2_VARIOPIXEL_EN BIT(2)
# define OV8865_ISP_CTRL2_VSYNC_LATCH_EN BIT(0)
# define OV8865_ISP_CTRL3_REG 0x5003
# define OV8865_ISP_GAIN_RED_H_REG 0x5018
# define OV8865_ISP_GAIN_RED_H(v) (((v) & GENMASK(13, 6)) >> 6)
# define OV8865_ISP_GAIN_RED_L_REG 0x5019
# define OV8865_ISP_GAIN_RED_L(v) ((v) & GENMASK(5, 0))
# define OV8865_ISP_GAIN_GREEN_H_REG 0x501a
# define OV8865_ISP_GAIN_GREEN_H(v) (((v) & GENMASK(13, 6)) >> 6)
# define OV8865_ISP_GAIN_GREEN_L_REG 0x501b
# define OV8865_ISP_GAIN_GREEN_L(v) ((v) & GENMASK(5, 0))
# define OV8865_ISP_GAIN_BLUE_H_REG 0x501c
# define OV8865_ISP_GAIN_BLUE_H(v) (((v) & GENMASK(13, 6)) >> 6)
# define OV8865_ISP_GAIN_BLUE_L_REG 0x501d
# define OV8865_ISP_GAIN_BLUE_L(v) ((v) & GENMASK(5, 0))
/* VarioPixel */
# define OV8865_VAP_CTRL0_REG 0x5900
# define OV8865_VAP_CTRL1_REG 0x5901
# define OV8865_VAP_CTRL1_HSUB_COEF(v) ((((v) - 1) << 2) & \
GENMASK ( 3 , 2 ) )
# define OV8865_VAP_CTRL1_VSUB_COEF(v) (((v) - 1) & GENMASK(1, 0))
/* Pre-DSP */
# define OV8865_PRE_CTRL0_REG 0x5e00
# define OV8865_PRE_CTRL0_PATTERN_EN BIT(7)
# define OV8865_PRE_CTRL0_ROLLING_BAR_EN BIT(6)
# define OV8865_PRE_CTRL0_TRANSPARENT_MODE BIT(5)
# define OV8865_PRE_CTRL0_SQUARES_BW_MODE BIT(4)
# define OV8865_PRE_CTRL0_PATTERN_COLOR_BARS 0
# define OV8865_PRE_CTRL0_PATTERN_RANDOM_DATA 1
# define OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES 2
# define OV8865_PRE_CTRL0_PATTERN_BLACK 3
/* Macros */
# define ov8865_subdev_sensor(s) \
container_of ( s , struct ov8865_sensor , subdev )
# define ov8865_ctrl_subdev(c) \
2021-01-05 15:47:19 +01:00
( & container_of ( ( c ) - > handler , struct ov8865_sensor , \
ctrls . handler ) - > subdev )
2020-12-31 15:27:01 +01:00
/* Data structures */
struct ov8865_register_value {
u16 address ;
u8 value ;
unsigned int delay_ms ;
} ;
/*
* PLL1 Clock Tree :
*
* + - < EXTCLK
* |
* + - + pll_pre_div_half ( 0x30a [ 0 ] )
* |
* + - + pll_pre_div ( 0x300 [ 2 : 0 ] , special values :
* | 0 : 1 , 1 : 1.5 , 3 : 2.5 , 4 : 3 , 5 : 4 , 7 : 8 )
* + - + pll_mul ( 0x301 [ 1 : 0 ] , 0x302 [ 7 : 0 ] )
* |
* + - + m_div ( 0x303 [ 3 : 0 ] )
* | |
* | + - > PHY_SCLK
* | |
* | + - + mipi_div ( 0x304 [ 1 : 0 ] , special values : 0 : 4 , 1 : 5 , 2 : 6 , 3 : 8 )
* | |
* | + - + pclk_div ( 0x3020 [ 3 ] )
* | |
* | + - > PCLK
* |
* + - + sys_pre_div ( 0x305 [ 1 : 0 ] , special values : 0 : 3 , 1 : 4 , 2 : 5 , 3 : 6 )
* |
* + - + sys_div ( 0x306 [ 0 ] )
* |
* + - + sys_sel ( 0x3032 [ 7 ] , 0 : PLL1 , 1 : PLL2 )
* |
* + - + sclk_sel ( 0x3033 [ 1 ] , 0 : sys_sel , 1 : PLL2 DAC_CLK )
* |
* + - + sclk_pre_div ( 0x3106 [ 3 : 2 ] , special values :
* | 0 : 1 , 1 : 2 , 2 : 4 , 3 : 1 )
* |
* + - + sclk_div ( 0x3106 [ 7 : 4 ] , special values : 0 : 1 )
* |
* + - > SCLK
*/
struct ov8865_pll1_config {
unsigned int pll_pre_div_half ;
unsigned int pll_pre_div ;
unsigned int pll_mul ;
unsigned int m_div ;
unsigned int mipi_div ;
unsigned int pclk_div ;
unsigned int sys_pre_div ;
unsigned int sys_div ;
} ;
/*
* PLL2 Clock Tree :
*
* + - < EXTCLK
* |
* + - + pll_pre_div_half ( 0x312 [ 4 ] )
* |
* + - + pll_pre_div ( 0x30b [ 2 : 0 ] , special values :
* | 0 : 1 , 1 : 1.5 , 3 : 2.5 , 4 : 3 , 5 : 4 , 7 : 8 )
* + - + pll_mul ( 0x30c [ 1 : 0 ] , 0x30d [ 7 : 0 ] )
* |
* + - + dac_div ( 0x312 [ 3 : 0 ] )
* | |
* | + - > DAC_CLK
* |
* + - + sys_pre_div ( 0x30f [ 3 : 0 ] )
* |
* + - + sys_div ( 0x30e [ 2 : 0 ] , special values :
* | 0 : 1 , 1 : 1.5 , 3 : 2.5 , 4 : 3 , 5 : 3.5 , 6 : 4 , 7 : 5 )
* |
* + - + sys_sel ( 0x3032 [ 7 ] , 0 : PLL1 , 1 : PLL2 )
* |
* + - + sclk_sel ( 0x3033 [ 1 ] , 0 : sys_sel , 1 : PLL2 DAC_CLK )
* |
* + - + sclk_pre_div ( 0x3106 [ 3 : 2 ] , special values :
* | 0 : 1 , 1 : 2 , 2 : 4 , 3 : 1 )
* |
* + - + sclk_div ( 0x3106 [ 7 : 4 ] , special values : 0 : 1 )
* |
* + - > SCLK
*/
struct ov8865_pll2_config {
unsigned int pll_pre_div_half ;
unsigned int pll_pre_div ;
unsigned int pll_mul ;
unsigned int dac_div ;
unsigned int sys_pre_div ;
unsigned int sys_div ;
} ;
struct ov8865_sclk_config {
unsigned int sys_sel ;
unsigned int sclk_sel ;
unsigned int sclk_pre_div ;
unsigned int sclk_div ;
} ;
/*
* General formulas for ( array - centered ) mode calculation :
* - photo_array_width = 3296
* - 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 = 2480
* - crop_start_y = ( photo_array_height - output_size_y ) / 2
* - crop_end_y = crop_start_y + offset_y + output_size_y - 1
*/
struct ov8865_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 ;
/* With auto size, only output and total sizes need to be set. */
bool size_auto ;
unsigned int size_auto_boundary_x ;
unsigned int size_auto_boundary_y ;
bool binning_x ;
bool binning_y ;
bool variopixel ;
unsigned int variopixel_hsub_coef ;
unsigned int variopixel_vsub_coef ;
/* Bits for the format register, used for binning. */
bool sync_hbin ;
bool horz_var2 ;
unsigned int inc_x_odd ;
unsigned int inc_x_even ;
unsigned int inc_y_odd ;
unsigned int inc_y_even ;
unsigned int vfifo_read_start ;
unsigned int ablc_num ;
unsigned int zline_num ;
unsigned int blc_top_zero_line_start ;
unsigned int blc_top_zero_line_num ;
unsigned int blc_top_black_line_start ;
unsigned int blc_top_black_line_num ;
unsigned int blc_bottom_zero_line_start ;
unsigned int blc_bottom_zero_line_num ;
unsigned int blc_bottom_black_line_start ;
unsigned int blc_bottom_black_line_num ;
u8 blc_col_shift_mask ;
unsigned int blc_anchor_left_start ;
unsigned int blc_anchor_left_end ;
unsigned int blc_anchor_right_start ;
unsigned int blc_anchor_right_end ;
struct v4l2_fract frame_interval ;
const struct ov8865_pll1_config * pll1_config ;
const struct ov8865_pll2_config * pll2_config ;
const struct ov8865_sclk_config * sclk_config ;
const struct ov8865_register_value * register_values ;
unsigned int register_values_count ;
} ;
struct ov8865_state {
const struct ov8865_mode * mode ;
u32 mbus_code ;
bool streaming ;
} ;
struct ov8865_ctrls {
struct v4l2_ctrl * link_freq ;
struct v4l2_ctrl * pixel_rate ;
struct v4l2_ctrl_handler handler ;
} ;
struct ov8865_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 * extclk ;
struct v4l2_fwnode_endpoint endpoint ;
struct v4l2_subdev subdev ;
struct media_pad pad ;
struct mutex mutex ;
struct ov8865_state state ;
struct ov8865_ctrls ctrls ;
} ;
/* Static definitions */
/*
* EXTCLK = 24 MHz
* PHY_SCLK = 720 MHz
* MIPI_PCLK = 90 MHz
*/
static const struct ov8865_pll1_config ov8865_pll1_config_native = {
. pll_pre_div_half = 1 ,
. pll_pre_div = 0 ,
. pll_mul = 30 ,
. m_div = 1 ,
. mipi_div = 3 ,
. pclk_div = 1 ,
. sys_pre_div = 1 ,
. sys_div = 2 ,
} ;
/*
* EXTCLK = 24 MHz
* DAC_CLK = 360 MHz
* SCLK = 144 MHz
*/
static const struct ov8865_pll2_config ov8865_pll2_config_native = {
. pll_pre_div_half = 1 ,
. pll_pre_div = 0 ,
. pll_mul = 30 ,
. dac_div = 2 ,
. sys_pre_div = 5 ,
. sys_div = 0 ,
} ;
/*
* EXTCLK = 24 MHz
* DAC_CLK = 360 MHz
* SCLK = 80 MHz
*/
static const struct ov8865_pll2_config ov8865_pll2_config_binning = {
. pll_pre_div_half = 1 ,
. pll_pre_div = 0 ,
. pll_mul = 30 ,
. dac_div = 2 ,
. sys_pre_div = 10 ,
. sys_div = 0 ,
} ;
static const struct ov8865_sclk_config ov8865_sclk_config_native = {
. sys_sel = 1 ,
. sclk_sel = 0 ,
. sclk_pre_div = 0 ,
. sclk_div = 0 ,
} ;
static const struct ov8865_register_value ov8865_register_values_native [ ] = {
/* Sensor */
{ 0x3700 , 0x48 } ,
{ 0x3701 , 0x18 } ,
{ 0x3702 , 0x50 } ,
{ 0x3703 , 0x32 } ,
{ 0x3704 , 0x28 } ,
{ 0x3706 , 0x70 } ,
{ 0x3707 , 0x08 } ,
{ 0x3708 , 0x48 } ,
{ 0x3709 , 0x80 } ,
{ 0x370a , 0x01 } ,
{ 0x370b , 0x70 } ,
{ 0x370c , 0x07 } ,
{ 0x3718 , 0x14 } ,
{ 0x3712 , 0x44 } ,
{ 0x371e , 0x31 } ,
{ 0x371f , 0x7f } ,
{ 0x3720 , 0x0a } ,
{ 0x3721 , 0x0a } ,
{ 0x3724 , 0x04 } ,
{ 0x3725 , 0x04 } ,
{ 0x3726 , 0x0c } ,
{ 0x3728 , 0x0a } ,
{ 0x3729 , 0x03 } ,
{ 0x372a , 0x06 } ,
{ 0x372b , 0xa6 } ,
{ 0x372c , 0xa6 } ,
{ 0x372d , 0xa6 } ,
{ 0x372e , 0x0c } ,
{ 0x372f , 0x20 } ,
{ 0x3730 , 0x02 } ,
{ 0x3731 , 0x0c } ,
{ 0x3732 , 0x28 } ,
{ 0x3736 , 0x30 } ,
{ 0x373a , 0x04 } ,
{ 0x373b , 0x18 } ,
{ 0x373c , 0x14 } ,
{ 0x373e , 0x06 } ,
{ 0x375a , 0x0c } ,
{ 0x375b , 0x26 } ,
{ 0x375d , 0x04 } ,
{ 0x375f , 0x28 } ,
{ 0x3767 , 0x1e } ,
{ 0x3772 , 0x46 } ,
{ 0x3773 , 0x04 } ,
{ 0x3774 , 0x2c } ,
{ 0x3775 , 0x13 } ,
{ 0x3776 , 0x10 } ,
{ 0x37a0 , 0x88 } ,
{ 0x37a1 , 0x7a } ,
{ 0x37a2 , 0x7a } ,
{ 0x37a3 , 0x02 } ,
{ 0x37a5 , 0x09 } ,
{ 0x37a7 , 0x88 } ,
{ 0x37a8 , 0xb0 } ,
{ 0x37a9 , 0xb0 } ,
{ 0x37aa , 0x88 } ,
{ 0x37ab , 0x5c } ,
{ 0x37ac , 0x5c } ,
{ 0x37ad , 0x55 } ,
{ 0x37ae , 0x19 } ,
{ 0x37af , 0x19 } ,
{ 0x37b3 , 0x84 } ,
{ 0x37b4 , 0x84 } ,
{ 0x37b5 , 0x66 } ,
/* PSRAM */
{ OV8865_PSRAM_CTRL8_REG , 0x16 } ,
/* ADC Sync */
{ 0x4500 , 0x68 } ,
} ;
static const struct ov8865_register_value ov8865_register_values_binning [ ] = {
/* Sensor */
{ 0x3700 , 0x24 } ,
{ 0x3701 , 0x0c } ,
{ 0x3702 , 0x28 } ,
{ 0x3703 , 0x19 } ,
{ 0x3704 , 0x14 } ,
{ 0x3706 , 0x38 } ,
{ 0x3707 , 0x04 } ,
{ 0x3708 , 0x24 } ,
{ 0x3709 , 0x40 } ,
{ 0x370a , 0x00 } ,
{ 0x370b , 0xb8 } ,
{ 0x370c , 0x04 } ,
{ 0x3718 , 0x12 } ,
{ 0x3712 , 0x42 } ,
{ 0x371e , 0x19 } ,
{ 0x371f , 0x40 } ,
{ 0x3720 , 0x05 } ,
{ 0x3721 , 0x05 } ,
{ 0x3724 , 0x02 } ,
{ 0x3725 , 0x02 } ,
{ 0x3726 , 0x06 } ,
{ 0x3728 , 0x05 } ,
{ 0x3729 , 0x02 } ,
{ 0x372a , 0x03 } ,
{ 0x372b , 0x53 } ,
{ 0x372c , 0xa3 } ,
{ 0x372d , 0x53 } ,
{ 0x372e , 0x06 } ,
{ 0x372f , 0x10 } ,
{ 0x3730 , 0x01 } ,
{ 0x3731 , 0x06 } ,
{ 0x3732 , 0x14 } ,
{ 0x3736 , 0x20 } ,
{ 0x373a , 0x02 } ,
{ 0x373b , 0x0c } ,
{ 0x373c , 0x0a } ,
{ 0x373e , 0x03 } ,
{ 0x375a , 0x06 } ,
{ 0x375b , 0x13 } ,
{ 0x375d , 0x02 } ,
{ 0x375f , 0x14 } ,
{ 0x3767 , 0x1c } ,
{ 0x3772 , 0x23 } ,
{ 0x3773 , 0x02 } ,
{ 0x3774 , 0x16 } ,
{ 0x3775 , 0x12 } ,
{ 0x3776 , 0x08 } ,
{ 0x37a0 , 0x44 } ,
{ 0x37a1 , 0x3d } ,
{ 0x37a2 , 0x3d } ,
{ 0x37a3 , 0x01 } ,
{ 0x37a5 , 0x08 } ,
{ 0x37a7 , 0x44 } ,
{ 0x37a8 , 0x58 } ,
{ 0x37a9 , 0x58 } ,
{ 0x37aa , 0x44 } ,
{ 0x37ab , 0x2e } ,
{ 0x37ac , 0x2e } ,
{ 0x37ad , 0x33 } ,
{ 0x37ae , 0x0d } ,
{ 0x37af , 0x0d } ,
{ 0x37b3 , 0x42 } ,
{ 0x37b4 , 0x42 } ,
{ 0x37b5 , 0x33 } ,
/* PSRAM */
{ OV8865_PSRAM_CTRL8_REG , 0x0b } ,
/* ADC Sync */
{ 0x4500 , 0x40 } ,
} ;
static const struct ov8865_mode ov8865_modes [ ] = {
/* 3264x2448 */
{
/* Horizontal */
. output_size_x = 3264 ,
. hts = 1944 ,
/* Vertical */
. output_size_y = 2448 ,
. vts = 2470 ,
. size_auto = true ,
. size_auto_boundary_x = 8 ,
. size_auto_boundary_y = 4 ,
/* Subsample increase */
. inc_x_odd = 1 ,
. inc_x_even = 1 ,
. inc_y_odd = 1 ,
. inc_y_even = 1 ,
/* VFIFO */
. vfifo_read_start = 16 ,
. ablc_num = 4 ,
. zline_num = 1 ,
/* Black Level */
. blc_top_zero_line_start = 0 ,
. blc_top_zero_line_num = 2 ,
. blc_top_black_line_start = 4 ,
. blc_top_black_line_num = 4 ,
. blc_bottom_zero_line_start = 2 ,
. blc_bottom_zero_line_num = 2 ,
. blc_bottom_black_line_start = 8 ,
. blc_bottom_black_line_num = 2 ,
. blc_anchor_left_start = 576 ,
. blc_anchor_left_end = 831 ,
. blc_anchor_right_start = 1984 ,
. blc_anchor_right_end = 2239 ,
/* Frame Interval */
. frame_interval = { 1 , 30 } ,
/* PLL */
. pll1_config = & ov8865_pll1_config_native ,
. pll2_config = & ov8865_pll2_config_native ,
. sclk_config = & ov8865_sclk_config_native ,
/* Registers */
. register_values = ov8865_register_values_native ,
. register_values_count =
ARRAY_SIZE ( ov8865_register_values_native ) ,
} ,
/* 3264x1836 */
{
/* Horizontal */
. output_size_x = 3264 ,
. hts = 2582 ,
/* Vertical */
. output_size_y = 1836 ,
. vts = 2002 ,
. size_auto = true ,
. size_auto_boundary_x = 8 ,
. size_auto_boundary_y = 4 ,
/* Subsample increase */
. inc_x_odd = 1 ,
. inc_x_even = 1 ,
. inc_y_odd = 1 ,
. inc_y_even = 1 ,
/* VFIFO */
. vfifo_read_start = 16 ,
. ablc_num = 4 ,
. zline_num = 1 ,
/* Black Level */
. blc_top_zero_line_start = 0 ,
. blc_top_zero_line_num = 2 ,
. blc_top_black_line_start = 4 ,
. blc_top_black_line_num = 4 ,
. blc_bottom_zero_line_start = 2 ,
. blc_bottom_zero_line_num = 2 ,
. blc_bottom_black_line_start = 8 ,
. blc_bottom_black_line_num = 2 ,
. blc_anchor_left_start = 576 ,
. blc_anchor_left_end = 831 ,
. blc_anchor_right_start = 1984 ,
. blc_anchor_right_end = 2239 ,
/* Frame Interval */
. frame_interval = { 1 , 30 } ,
/* PLL */
. pll1_config = & ov8865_pll1_config_native ,
. pll2_config = & ov8865_pll2_config_native ,
. sclk_config = & ov8865_sclk_config_native ,
/* Registers */
. register_values = ov8865_register_values_native ,
. register_values_count =
ARRAY_SIZE ( ov8865_register_values_native ) ,
} ,
/* 1632x1224 */
{
/* Horizontal */
. output_size_x = 1632 ,
. hts = 1923 ,
/* Vertical */
. output_size_y = 1224 ,
. vts = 1248 ,
. size_auto = true ,
. size_auto_boundary_x = 8 ,
. size_auto_boundary_y = 8 ,
/* Subsample increase */
. inc_x_odd = 3 ,
. inc_x_even = 1 ,
. inc_y_odd = 3 ,
. inc_y_even = 1 ,
/* Binning */
. binning_y = true ,
. sync_hbin = true ,
/* VFIFO */
. vfifo_read_start = 116 ,
. ablc_num = 8 ,
. zline_num = 2 ,
/* Black Level */
. blc_top_zero_line_start = 0 ,
. blc_top_zero_line_num = 2 ,
. blc_top_black_line_start = 4 ,
. blc_top_black_line_num = 4 ,
. blc_bottom_zero_line_start = 2 ,
. blc_bottom_zero_line_num = 2 ,
. blc_bottom_black_line_start = 8 ,
. blc_bottom_black_line_num = 2 ,
. blc_anchor_left_start = 288 ,
. blc_anchor_left_end = 415 ,
. blc_anchor_right_start = 992 ,
. blc_anchor_right_end = 1119 ,
/* Frame Interval */
. frame_interval = { 1 , 30 } ,
/* PLL */
. pll1_config = & ov8865_pll1_config_native ,
. pll2_config = & ov8865_pll2_config_binning ,
. sclk_config = & ov8865_sclk_config_native ,
/* Registers */
. register_values = ov8865_register_values_binning ,
. register_values_count =
ARRAY_SIZE ( ov8865_register_values_binning ) ,
} ,
/* 800x600 (SVGA) */
{
/* Horizontal */
. output_size_x = 800 ,
. hts = 1250 ,
/* Vertical */
. output_size_y = 600 ,
. vts = 640 ,
. size_auto = true ,
. size_auto_boundary_x = 8 ,
. size_auto_boundary_y = 8 ,
/* Subsample increase */
. inc_x_odd = 3 ,
. inc_x_even = 1 ,
. inc_y_odd = 5 ,
. inc_y_even = 3 ,
/* Binning */
. binning_y = true ,
. variopixel = true ,
. variopixel_hsub_coef = 2 ,
. variopixel_vsub_coef = 1 ,
. sync_hbin = true ,
. horz_var2 = true ,
/* VFIFO */
. vfifo_read_start = 80 ,
. ablc_num = 8 ,
. zline_num = 2 ,
/* Black Level */
. blc_top_zero_line_start = 0 ,
. blc_top_zero_line_num = 2 ,
. blc_top_black_line_start = 2 ,
. blc_top_black_line_num = 2 ,
. blc_bottom_zero_line_start = 0 ,
. blc_bottom_zero_line_num = 0 ,
. blc_bottom_black_line_start = 4 ,
. blc_bottom_black_line_num = 2 ,
. blc_col_shift_mask = OV8865_BLC_CTRL1_COL_SHIFT_128 ,
. blc_anchor_left_start = 288 ,
. blc_anchor_left_end = 415 ,
. blc_anchor_right_start = 992 ,
. blc_anchor_right_end = 1119 ,
/* Frame Interval */
. frame_interval = { 1 , 90 } ,
/* PLL */
. pll1_config = & ov8865_pll1_config_native ,
. pll2_config = & ov8865_pll2_config_binning ,
. sclk_config = & ov8865_sclk_config_native ,
/* Registers */
. register_values = ov8865_register_values_binning ,
. register_values_count =
ARRAY_SIZE ( ov8865_register_values_binning ) ,
} ,
} ;
static const u32 ov8865_mbus_codes [ ] = {
MEDIA_BUS_FMT_SBGGR10_1X10 ,
} ;
static const struct ov8865_register_value ov8865_init_sequence [ ] = {
/* Analog */
{ 0x3604 , 0x04 } ,
{ 0x3602 , 0x30 } ,
{ 0x3605 , 0x00 } ,
{ 0x3607 , 0x20 } ,
{ 0x3608 , 0x11 } ,
{ 0x3609 , 0x68 } ,
{ 0x360a , 0x40 } ,
{ 0x360c , 0xdd } ,
{ 0x360e , 0x0c } ,
{ 0x3610 , 0x07 } ,
{ 0x3612 , 0x86 } ,
{ 0x3613 , 0x58 } ,
{ 0x3614 , 0x28 } ,
{ 0x3617 , 0x40 } ,
{ 0x3618 , 0x5a } ,
{ 0x3619 , 0x9b } ,
{ 0x361c , 0x00 } ,
{ 0x361d , 0x60 } ,
{ 0x3631 , 0x60 } ,
{ 0x3633 , 0x10 } ,
{ 0x3634 , 0x10 } ,
{ 0x3635 , 0x10 } ,
{ 0x3636 , 0x10 } ,
{ 0x3638 , 0xff } ,
{ 0x3641 , 0x55 } ,
{ 0x3646 , 0x86 } ,
{ 0x3647 , 0x27 } ,
{ 0x364a , 0x1b } ,
/* Sensor */
{ 0x3700 , 0x24 } ,
{ 0x3701 , 0x0c } ,
{ 0x3702 , 0x28 } ,
{ 0x3703 , 0x19 } ,
{ 0x3704 , 0x14 } ,
{ 0x3705 , 0x00 } ,
{ 0x3706 , 0x38 } ,
{ 0x3707 , 0x04 } ,
{ 0x3708 , 0x24 } ,
{ 0x3709 , 0x40 } ,
{ 0x370a , 0x00 } ,
{ 0x370b , 0xb8 } ,
{ 0x370c , 0x04 } ,
{ 0x3718 , 0x12 } ,
{ 0x3719 , 0x31 } ,
{ 0x3712 , 0x42 } ,
{ 0x3714 , 0x12 } ,
{ 0x371e , 0x19 } ,
{ 0x371f , 0x40 } ,
{ 0x3720 , 0x05 } ,
{ 0x3721 , 0x05 } ,
{ 0x3724 , 0x02 } ,
{ 0x3725 , 0x02 } ,
{ 0x3726 , 0x06 } ,
{ 0x3728 , 0x05 } ,
{ 0x3729 , 0x02 } ,
{ 0x372a , 0x03 } ,
{ 0x372b , 0x53 } ,
{ 0x372c , 0xa3 } ,
{ 0x372d , 0x53 } ,
{ 0x372e , 0x06 } ,
{ 0x372f , 0x10 } ,
{ 0x3730 , 0x01 } ,
{ 0x3731 , 0x06 } ,
{ 0x3732 , 0x14 } ,
{ 0x3733 , 0x10 } ,
{ 0x3734 , 0x40 } ,
{ 0x3736 , 0x20 } ,
{ 0x373a , 0x02 } ,
{ 0x373b , 0x0c } ,
{ 0x373c , 0x0a } ,
{ 0x373e , 0x03 } ,
{ 0x3755 , 0x40 } ,
{ 0x3758 , 0x00 } ,
{ 0x3759 , 0x4c } ,
{ 0x375a , 0x06 } ,
{ 0x375b , 0x13 } ,
{ 0x375c , 0x40 } ,
{ 0x375d , 0x02 } ,
{ 0x375e , 0x00 } ,
{ 0x375f , 0x14 } ,
{ 0x3767 , 0x1c } ,
{ 0x3768 , 0x04 } ,
{ 0x3769 , 0x20 } ,
{ 0x376c , 0xc0 } ,
{ 0x376d , 0xc0 } ,
{ 0x376a , 0x08 } ,
{ 0x3761 , 0x00 } ,
{ 0x3762 , 0x00 } ,
{ 0x3763 , 0x00 } ,
{ 0x3766 , 0xff } ,
{ 0x376b , 0x42 } ,
{ 0x3772 , 0x23 } ,
{ 0x3773 , 0x02 } ,
{ 0x3774 , 0x16 } ,
{ 0x3775 , 0x12 } ,
{ 0x3776 , 0x08 } ,
{ 0x37a0 , 0x44 } ,
{ 0x37a1 , 0x3d } ,
{ 0x37a2 , 0x3d } ,
{ 0x37a3 , 0x01 } ,
{ 0x37a4 , 0x00 } ,
{ 0x37a5 , 0x08 } ,
{ 0x37a6 , 0x00 } ,
{ 0x37a7 , 0x44 } ,
{ 0x37a8 , 0x58 } ,
{ 0x37a9 , 0x58 } ,
{ 0x3760 , 0x00 } ,
{ 0x376f , 0x01 } ,
{ 0x37aa , 0x44 } ,
{ 0x37ab , 0x2e } ,
{ 0x37ac , 0x2e } ,
{ 0x37ad , 0x33 } ,
{ 0x37ae , 0x0d } ,
{ 0x37af , 0x0d } ,
{ 0x37b0 , 0x00 } ,
{ 0x37b1 , 0x00 } ,
{ 0x37b2 , 0x00 } ,
{ 0x37b3 , 0x42 } ,
{ 0x37b4 , 0x42 } ,
{ 0x37b5 , 0x33 } ,
{ 0x37b6 , 0x00 } ,
{ 0x37b7 , 0x00 } ,
{ 0x37b8 , 0x00 } ,
{ 0x37b9 , 0xff } ,
/* ADC Sync */
{ 0x4503 , 0x10 } ,
} ;
static const s64 ov8865_link_freq_menu [ ] = {
360000000 ,
} ;
static const char * const ov8865_test_pattern_menu [ ] = {
" Disabled " ,
" Random data " ,
" Color bars " ,
" Color bars with rolling bar " ,
" Color squares " ,
" Color squares with rolling bar "
} ;
static const u8 ov8865_test_pattern_bits [ ] = {
0 ,
OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_RANDOM_DATA ,
OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_COLOR_BARS ,
OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_ROLLING_BAR_EN |
OV8865_PRE_CTRL0_PATTERN_COLOR_BARS ,
OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES ,
OV8865_PRE_CTRL0_PATTERN_EN | OV8865_PRE_CTRL0_ROLLING_BAR_EN |
OV8865_PRE_CTRL0_PATTERN_COLOR_SQUARES ,
} ;
/* Input/Output */
static int ov8865_read ( struct ov8865_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 ov8865_write ( struct ov8865_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 ov8865_write_sequence ( struct ov8865_sensor * sensor ,
const struct ov8865_register_value * sequence ,
unsigned int sequence_count )
{
unsigned int i ;
int ret = 0 ;
for ( i = 0 ; i < sequence_count ; i + + ) {
ret = ov8865_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 ov8865_update_bits ( struct ov8865_sensor * sensor , u16 address ,
u8 mask , u8 bits )
{
u8 value = 0 ;
int ret ;
ret = ov8865_read ( sensor , address , & value ) ;
if ( ret )
return ret ;
value & = ~ mask ;
value | = bits ;
return ov8865_write ( sensor , address , value ) ;
}
/* Sensor */
static int ov8865_sw_reset ( struct ov8865_sensor * sensor )
{
return ov8865_write ( sensor , OV8865_SW_RESET_REG , OV8865_SW_RESET_RESET ) ;
}
static int ov8865_sw_standby ( struct ov8865_sensor * sensor , int standby )
{
u8 value = 0 ;
if ( ! standby )
value = OV8865_SW_STANDBY_STREAM_ON ;
return ov8865_write ( sensor , OV8865_SW_STANDBY_REG , value ) ;
}
static int ov8865_chip_id_check ( struct ov8865_sensor * sensor )
{
u16 regs [ ] = { OV8865_CHIP_ID_HH_REG , OV8865_CHIP_ID_H_REG ,
OV8865_CHIP_ID_L_REG } ;
u8 values [ ] = { OV8865_CHIP_ID_HH_VALUE , OV8865_CHIP_ID_H_VALUE ,
OV8865_CHIP_ID_L_VALUE } ;
unsigned int i ;
u8 value ;
int ret ;
for ( i = 0 ; i < ARRAY_SIZE ( regs ) ; i + + ) {
ret = ov8865_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 ov8865_charge_pump_configure ( struct ov8865_sensor * sensor )
{
return ov8865_write ( sensor , OV8865_PUMP_CLK_DIV_REG ,
OV8865_PUMP_CLK_DIV_PUMP_P ( 1 ) ) ;
}
static int ov8865_mipi_configure ( struct ov8865_sensor * sensor )
{
struct v4l2_fwnode_bus_mipi_csi2 * bus_mipi_csi2 =
& sensor - > endpoint . bus . mipi_csi2 ;
unsigned int lanes_count = bus_mipi_csi2 - > num_data_lanes ;
int ret ;
ret = ov8865_write ( sensor , OV8865_MIPI_SC_CTRL0_REG ,
OV8865_MIPI_SC_CTRL0_LANES ( lanes_count ) |
OV8865_MIPI_SC_CTRL0_MIPI_EN |
OV8865_MIPI_SC_CTRL0_UNKNOWN ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_MIPI_SC_CTRL2_REG ,
OV8865_MIPI_SC_CTRL2_PD_MIPI_RST_SYNC ) ;
if ( ret )
return ret ;
if ( lanes_count > = 2 ) {
ret = ov8865_write ( sensor , OV8865_MIPI_LANE_SEL01_REG ,
OV8865_MIPI_LANE_SEL01_LANE0 ( 0 ) |
OV8865_MIPI_LANE_SEL01_LANE1 ( 1 ) ) ;
if ( ret )
return ret ;
}
if ( lanes_count > = 4 ) {
ret = ov8865_write ( sensor , OV8865_MIPI_LANE_SEL23_REG ,
OV8865_MIPI_LANE_SEL23_LANE2 ( 2 ) |
OV8865_MIPI_LANE_SEL23_LANE3 ( 3 ) ) ;
if ( ret )
return ret ;
}
ret = ov8865_update_bits ( sensor , OV8865_CLK_SEL1_REG ,
OV8865_CLK_SEL1_MIPI_EOF ,
OV8865_CLK_SEL1_MIPI_EOF ) ;
if ( ret )
return ret ;
/*
* This value might need to change depending on PCLK rate ,
* but it ' s unclear how . This value seems to generally work
* while the default value was found to cause transmission errors .
*/
return ov8865_write ( sensor , OV8865_MIPI_PCLK_PERIOD_REG , 0x16 ) ;
}
static int ov8865_black_level_configure ( struct ov8865_sensor * sensor )
{
int ret ;
/* Trigger BLC on relevant events and enable filter. */
ret = ov8865_write ( sensor , OV8865_BLC_CTRL0_REG ,
OV8865_BLC_CTRL0_TRIG_RANGE_EN |
OV8865_BLC_CTRL0_TRIG_FORMAT_EN |
OV8865_BLC_CTRL0_TRIG_GAIN_EN |
OV8865_BLC_CTRL0_TRIG_EXPOSURE_EN |
OV8865_BLC_CTRL0_FILTER_EN ) ;
if ( ret )
return ret ;
/* Lower BLC offset trigger threshold. */
ret = ov8865_write ( sensor , OV8865_BLC_CTRLD_REG ,
OV8865_BLC_CTRLD_OFFSET_TRIGGER ( 16 ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_BLC_CTRL1F_REG , 0 ) ;
if ( ret )
return ret ;
/* Increase BLC offset maximum limit. */
return ov8865_write ( sensor , OV8865_BLC_OFFSET_LIMIT_REG ,
OV8865_BLC_OFFSET_LIMIT ( 63 ) ) ;
}
static int ov8865_isp_configure ( struct ov8865_sensor * sensor )
{
int ret ;
/* Disable lens correction. */
ret = ov8865_write ( sensor , OV8865_ISP_CTRL0_REG ,
OV8865_ISP_CTRL0_WHITE_BALANCE_EN |
OV8865_ISP_CTRL0_DPC_BLACK_EN |
OV8865_ISP_CTRL0_DPC_WHITE_EN ) ;
if ( ret )
return ret ;
return ov8865_write ( sensor , OV8865_ISP_CTRL1_REG ,
OV8865_ISP_CTRL1_BLC_EN ) ;
}
static unsigned long ov8865_mode_pll1_rate ( struct ov8865_sensor * sensor ,
const struct ov8865_mode * mode )
{
const struct ov8865_pll1_config * config = mode - > pll1_config ;
unsigned long extclk_rate ;
unsigned long pll1_rate ;
extclk_rate = clk_get_rate ( sensor - > extclk ) ;
pll1_rate = extclk_rate * config - > pll_mul / config - > pll_pre_div_half ;
switch ( config - > pll_pre_div ) {
case 0 :
break ;
case 1 :
pll1_rate * = 3 ;
pll1_rate / = 2 ;
break ;
case 3 :
pll1_rate * = 5 ;
pll1_rate / = 2 ;
break ;
case 4 :
pll1_rate / = 3 ;
break ;
case 5 :
pll1_rate / = 4 ;
break ;
case 7 :
pll1_rate / = 8 ;
break ;
default :
pll1_rate / = config - > pll_pre_div ;
break ;
}
return pll1_rate ;
}
static int ov8865_mode_pll1_configure ( struct ov8865_sensor * sensor ,
const struct ov8865_mode * mode ,
u32 mbus_code )
{
const struct ov8865_pll1_config * config = mode - > pll1_config ;
u8 value ;
int ret ;
switch ( mbus_code ) {
case MEDIA_BUS_FMT_SBGGR10_1X10 :
value = OV8865_MIPI_BIT_SEL ( 10 ) ;
break ;
default :
return - EINVAL ;
}
ret = ov8865_write ( sensor , OV8865_MIPI_BIT_SEL_REG , value ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_PLL_CTRLA_REG ,
OV8865_PLL_CTRLA_PRE_DIV_HALF ( config - > pll_pre_div_half ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_PLL_CTRL0_REG ,
OV8865_PLL_CTRL0_PRE_DIV ( config - > pll_pre_div ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_PLL_CTRL1_REG ,
OV8865_PLL_CTRL1_MUL_H ( config - > pll_mul ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_PLL_CTRL2_REG ,
OV8865_PLL_CTRL2_MUL_L ( config - > pll_mul ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_PLL_CTRL3_REG ,
OV8865_PLL_CTRL3_M_DIV ( config - > m_div ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_PLL_CTRL4_REG ,
OV8865_PLL_CTRL4_MIPI_DIV ( config - > mipi_div ) ) ;
if ( ret )
return ret ;
ret = ov8865_update_bits ( sensor , OV8865_PCLK_SEL_REG ,
2021-01-05 15:47:19 +01:00
OV8865_PCLK_SEL_PCLK_DIV_MASK ,
OV8865_PCLK_SEL_PCLK_DIV ( config - > pclk_div ) ) ;
2020-12-31 15:27:01 +01:00
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_PLL_CTRL5_REG ,
OV8865_PLL_CTRL5_SYS_PRE_DIV ( config - > sys_pre_div ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_PLL_CTRL6_REG ,
OV8865_PLL_CTRL6_SYS_DIV ( config - > sys_div ) ) ;
if ( ret )
return ret ;
return ov8865_update_bits ( sensor , OV8865_PLL_CTRL1E_REG ,
OV8865_PLL_CTRL1E_PLL1_NO_LAT ,
OV8865_PLL_CTRL1E_PLL1_NO_LAT ) ;
}
static int ov8865_mode_pll2_configure ( struct ov8865_sensor * sensor ,
const struct ov8865_mode * mode )
{
const struct ov8865_pll2_config * config = mode - > pll2_config ;
int ret ;
ret = ov8865_write ( sensor , OV8865_PLL_CTRL12_REG ,
OV8865_PLL_CTRL12_PRE_DIV_HALF ( config - > pll_pre_div_half ) |
OV8865_PLL_CTRL12_DAC_DIV ( config - > dac_div ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_PLL_CTRLB_REG ,
OV8865_PLL_CTRLB_PRE_DIV ( config - > pll_pre_div ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_PLL_CTRLC_REG ,
OV8865_PLL_CTRLC_MUL_H ( config - > pll_mul ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_PLL_CTRLD_REG ,
OV8865_PLL_CTRLD_MUL_L ( config - > pll_mul ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_PLL_CTRLF_REG ,
OV8865_PLL_CTRLF_SYS_PRE_DIV ( config - > sys_pre_div ) ) ;
if ( ret )
return ret ;
return ov8865_write ( sensor , OV8865_PLL_CTRLE_REG ,
OV8865_PLL_CTRLE_SYS_DIV ( config - > sys_div ) ) ;
}
static int ov8865_mode_sclk_configure ( struct ov8865_sensor * sensor ,
const struct ov8865_mode * mode )
{
const struct ov8865_sclk_config * config = mode - > sclk_config ;
int ret ;
ret = ov8865_write ( sensor , OV8865_CLK_SEL0_REG ,
OV8865_CLK_SEL0_PLL1_SYS_SEL ( config - > sys_sel ) ) ;
if ( ret )
return ret ;
ret = ov8865_update_bits ( sensor , OV8865_CLK_SEL1_REG ,
OV8865_CLK_SEL1_PLL_SCLK_SEL_MASK ,
OV8865_CLK_SEL1_PLL_SCLK_SEL ( config - > sclk_sel ) ) ;
if ( ret )
return ret ;
return ov8865_write ( sensor , OV8865_SCLK_CTRL_REG ,
OV8865_SCLK_CTRL_UNKNOWN |
OV8865_SCLK_CTRL_SCLK_DIV ( config - > sclk_div ) |
OV8865_SCLK_CTRL_SCLK_PRE_DIV ( config - > sclk_pre_div ) ) ;
}
static int ov8865_mode_binning_configure ( struct ov8865_sensor * sensor ,
const struct ov8865_mode * mode )
{
unsigned int variopixel_hsub_coef , variopixel_vsub_coef ;
u8 value ;
int ret ;
ret = ov8865_write ( sensor , OV8865_FORMAT1_REG , 0 ) ;
if ( ret )
return ret ;
value = OV8865_FORMAT2_HSYNC_EN ;
if ( mode - > binning_x )
value | = OV8865_FORMAT2_FST_HBIN_EN ;
if ( mode - > binning_y )
value | = OV8865_FORMAT2_FST_VBIN_EN ;
if ( mode - > sync_hbin )
value | = OV8865_FORMAT2_SYNC_HBIN_EN ;
if ( mode - > horz_var2 )
value | = OV8865_FORMAT2_ISP_HORZ_VAR2_EN ;
ret = ov8865_write ( sensor , OV8865_FORMAT2_REG , value ) ;
if ( ret )
return ret ;
ret = ov8865_update_bits ( sensor , OV8865_ISP_CTRL2_REG ,
OV8865_ISP_CTRL2_VARIOPIXEL_EN ,
mode - > variopixel ?
OV8865_ISP_CTRL2_VARIOPIXEL_EN : 0 ) ;
if ( ret )
return ret ;
if ( mode - > variopixel ) {
/* VarioPixel coefs needs to be > 1. */
variopixel_hsub_coef = mode - > variopixel_hsub_coef ;
variopixel_vsub_coef = mode - > variopixel_vsub_coef ;
} else {
variopixel_hsub_coef = 1 ;
variopixel_vsub_coef = 1 ;
}
ret = ov8865_write ( sensor , OV8865_VAP_CTRL1_REG ,
OV8865_VAP_CTRL1_HSUB_COEF ( variopixel_hsub_coef ) |
OV8865_VAP_CTRL1_VSUB_COEF ( variopixel_vsub_coef ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_INC_X_ODD_REG ,
OV8865_INC_X_ODD ( mode - > inc_x_odd ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_INC_X_EVEN_REG ,
OV8865_INC_X_EVEN ( mode - > inc_x_even ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_INC_Y_ODD_REG ,
OV8865_INC_Y_ODD ( mode - > inc_y_odd ) ) ;
if ( ret )
return ret ;
return ov8865_write ( sensor , OV8865_INC_Y_EVEN_REG ,
OV8865_INC_Y_EVEN ( mode - > inc_y_even ) ) ;
}
static int ov8865_mode_black_level_configure ( struct ov8865_sensor * sensor ,
const struct ov8865_mode * mode )
{
int ret ;
/* Note that a zero value for blc_col_shift_mask is the default 256. */
ret = ov8865_write ( sensor , OV8865_BLC_CTRL1_REG ,
mode - > blc_col_shift_mask |
OV8865_BLC_CTRL1_OFFSET_LIMIT_EN ) ;
if ( ret )
return ret ;
/* BLC top zero line */
ret = ov8865_write ( sensor , OV8865_BLC_TOP_ZLINE_START_REG ,
OV8865_BLC_TOP_ZLINE_START ( mode - > blc_top_zero_line_start ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_BLC_TOP_ZLINE_NUM_REG ,
OV8865_BLC_TOP_ZLINE_NUM ( mode - > blc_top_zero_line_num ) ) ;
if ( ret )
return ret ;
/* BLC top black line */
ret = ov8865_write ( sensor , OV8865_BLC_TOP_BLKLINE_START_REG ,
OV8865_BLC_TOP_BLKLINE_START ( mode - > blc_top_black_line_start ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_BLC_TOP_BLKLINE_NUM_REG ,
OV8865_BLC_TOP_BLKLINE_NUM ( mode - > blc_top_black_line_num ) ) ;
if ( ret )
return ret ;
/* BLC bottom zero line */
ret = ov8865_write ( sensor , OV8865_BLC_BOT_ZLINE_START_REG ,
OV8865_BLC_BOT_ZLINE_START ( mode - > blc_bottom_zero_line_start ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_BLC_BOT_ZLINE_NUM_REG ,
OV8865_BLC_BOT_ZLINE_NUM ( mode - > blc_bottom_zero_line_num ) ) ;
if ( ret )
return ret ;
/* BLC bottom black line */
ret = ov8865_write ( sensor , OV8865_BLC_BOT_BLKLINE_START_REG ,
OV8865_BLC_BOT_BLKLINE_START ( mode - > blc_bottom_black_line_start ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_BLC_BOT_BLKLINE_NUM_REG ,
OV8865_BLC_BOT_BLKLINE_NUM ( mode - > blc_bottom_black_line_num ) ) ;
if ( ret )
return ret ;
/* BLC anchor */
ret = ov8865_write ( sensor , OV8865_BLC_ANCHOR_LEFT_START_H_REG ,
OV8865_BLC_ANCHOR_LEFT_START_H ( mode - > blc_anchor_left_start ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_BLC_ANCHOR_LEFT_START_L_REG ,
OV8865_BLC_ANCHOR_LEFT_START_L ( mode - > blc_anchor_left_start ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_BLC_ANCHOR_LEFT_END_H_REG ,
OV8865_BLC_ANCHOR_LEFT_END_H ( mode - > blc_anchor_left_end ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_BLC_ANCHOR_LEFT_END_L_REG ,
OV8865_BLC_ANCHOR_LEFT_END_L ( mode - > blc_anchor_left_end ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_BLC_ANCHOR_RIGHT_START_H_REG ,
OV8865_BLC_ANCHOR_RIGHT_START_H ( mode - > blc_anchor_right_start ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_BLC_ANCHOR_RIGHT_START_L_REG ,
OV8865_BLC_ANCHOR_RIGHT_START_L ( mode - > blc_anchor_right_start ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_BLC_ANCHOR_RIGHT_END_H_REG ,
OV8865_BLC_ANCHOR_RIGHT_END_H ( mode - > blc_anchor_right_end ) ) ;
if ( ret )
return ret ;
return ov8865_write ( sensor , OV8865_BLC_ANCHOR_RIGHT_END_L_REG ,
OV8865_BLC_ANCHOR_RIGHT_END_L ( mode - > blc_anchor_right_end ) ) ;
}
static int ov8865_mode_configure ( struct ov8865_sensor * sensor ,
const struct ov8865_mode * mode , u32 mbus_code )
{
int ret ;
/* Output Size X */
ret = ov8865_write ( sensor , OV8865_OUTPUT_SIZE_X_H_REG ,
OV8865_OUTPUT_SIZE_X_H ( mode - > output_size_x ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_OUTPUT_SIZE_X_L_REG ,
OV8865_OUTPUT_SIZE_X_L ( mode - > output_size_x ) ) ;
if ( ret )
return ret ;
/* Horizontal Total Size */
ret = ov8865_write ( sensor , OV8865_HTS_H_REG , OV8865_HTS_H ( mode - > hts ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_HTS_L_REG , OV8865_HTS_L ( mode - > hts ) ) ;
if ( ret )
return ret ;
/* Output Size Y */
ret = ov8865_write ( sensor , OV8865_OUTPUT_SIZE_Y_H_REG ,
OV8865_OUTPUT_SIZE_Y_H ( mode - > output_size_y ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_OUTPUT_SIZE_Y_L_REG ,
OV8865_OUTPUT_SIZE_Y_L ( mode - > output_size_y ) ) ;
if ( ret )
return ret ;
/* Vertical Total Size */
ret = ov8865_write ( sensor , OV8865_VTS_H_REG , OV8865_VTS_H ( mode - > vts ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_VTS_L_REG , OV8865_VTS_L ( mode - > vts ) ) ;
if ( ret )
return ret ;
if ( mode - > size_auto ) {
/* Auto Size */
ret = ov8865_write ( sensor , OV8865_AUTO_SIZE_CTRL_REG ,
OV8865_AUTO_SIZE_CTRL_OFFSET_Y_REG |
OV8865_AUTO_SIZE_CTRL_OFFSET_X_REG |
OV8865_AUTO_SIZE_CTRL_CROP_END_Y_REG |
OV8865_AUTO_SIZE_CTRL_CROP_END_X_REG |
OV8865_AUTO_SIZE_CTRL_CROP_START_Y_REG |
OV8865_AUTO_SIZE_CTRL_CROP_START_X_REG ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_AUTO_SIZE_BOUNDARIES_REG ,
OV8865_AUTO_SIZE_BOUNDARIES_Y ( mode - > size_auto_boundary_y ) |
OV8865_AUTO_SIZE_BOUNDARIES_X ( mode - > size_auto_boundary_x ) ) ;
if ( ret )
return ret ;
} else {
/* Crop Start X */
ret = ov8865_write ( sensor , OV8865_CROP_START_X_H_REG ,
OV8865_CROP_START_X_H ( mode - > crop_start_x ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_CROP_START_X_L_REG ,
OV8865_CROP_START_X_L ( mode - > crop_start_x ) ) ;
if ( ret )
return ret ;
/* Offset X */
ret = ov8865_write ( sensor , OV8865_OFFSET_X_H_REG ,
OV8865_OFFSET_X_H ( mode - > offset_x ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_OFFSET_X_L_REG ,
OV8865_OFFSET_X_L ( mode - > offset_x ) ) ;
if ( ret )
return ret ;
/* Crop End X */
ret = ov8865_write ( sensor , OV8865_CROP_END_X_H_REG ,
OV8865_CROP_END_X_H ( mode - > crop_end_x ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_CROP_END_X_L_REG ,
OV8865_CROP_END_X_L ( mode - > crop_end_x ) ) ;
if ( ret )
return ret ;
/* Crop Start Y */
ret = ov8865_write ( sensor , OV8865_CROP_START_Y_H_REG ,
OV8865_CROP_START_Y_H ( mode - > crop_start_y ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_CROP_START_Y_L_REG ,
OV8865_CROP_START_Y_L ( mode - > crop_start_y ) ) ;
if ( ret )
return ret ;
/* Offset Y */
ret = ov8865_write ( sensor , OV8865_OFFSET_Y_H_REG ,
OV8865_OFFSET_Y_H ( mode - > offset_y ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_OFFSET_Y_L_REG ,
OV8865_OFFSET_Y_L ( mode - > offset_y ) ) ;
if ( ret )
return ret ;
/* Crop End Y */
ret = ov8865_write ( sensor , OV8865_CROP_END_Y_H_REG ,
OV8865_CROP_END_Y_H ( mode - > crop_end_y ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_CROP_END_Y_L_REG ,
OV8865_CROP_END_Y_L ( mode - > crop_end_y ) ) ;
if ( ret )
return ret ;
}
/* VFIFO */
ret = ov8865_write ( sensor , OV8865_VFIFO_READ_START_H_REG ,
OV8865_VFIFO_READ_START_H ( mode - > vfifo_read_start ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_VFIFO_READ_START_L_REG ,
OV8865_VFIFO_READ_START_L ( mode - > vfifo_read_start ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_ABLC_NUM_REG ,
OV8865_ABLC_NUM ( mode - > ablc_num ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_ZLINE_NUM_REG ,
OV8865_ZLINE_NUM ( mode - > zline_num ) ) ;
if ( ret )
return ret ;
/* Binning */
ret = ov8865_mode_binning_configure ( sensor , mode ) ;
if ( ret )
return ret ;
/* Black Level */
ret = ov8865_mode_black_level_configure ( sensor , mode ) ;
if ( ret )
return ret ;
/* PLLs */
ret = ov8865_mode_pll1_configure ( sensor , mode , mbus_code ) ;
if ( ret )
return ret ;
ret = ov8865_mode_pll2_configure ( sensor , mode ) ;
if ( ret )
return ret ;
ret = ov8865_mode_sclk_configure ( sensor , mode ) ;
if ( ret )
return ret ;
/* Extra registers */
if ( mode - > register_values ) {
ret = ov8865_write_sequence ( sensor , mode - > register_values ,
mode - > register_values_count ) ;
if ( ret )
return ret ;
}
return 0 ;
}
static unsigned long ov8865_mode_mipi_clk_rate ( struct ov8865_sensor * sensor ,
const struct ov8865_mode * mode )
{
const struct ov8865_pll1_config * config = mode - > pll1_config ;
unsigned long pll1_rate ;
pll1_rate = ov8865_mode_pll1_rate ( sensor , mode ) ;
return pll1_rate / config - > m_div / 2 ;
}
/* Exposure */
static int ov8865_exposure_configure ( struct ov8865_sensor * sensor , u32 exposure )
{
int ret ;
ret = ov8865_write ( sensor , OV8865_EXPOSURE_CTRL_HH_REG ,
OV8865_EXPOSURE_CTRL_HH ( exposure ) ) ;
if ( ret )
return ret ;
ret = ov8865_write ( sensor , OV8865_EXPOSURE_CTRL_H_REG ,
OV8865_EXPOSURE_CTRL_H ( exposure ) ) ;
if ( ret )
return ret ;
return ov8865_write ( sensor , OV8865_EXPOSURE_CTRL_L_REG ,
OV8865_EXPOSURE_CTRL_L ( exposure ) ) ;
}
/* Gain */
static int ov8865_gain_configure ( struct ov8865_sensor * sensor , u32 gain )
{
int ret ;
ret = ov8865_write ( sensor , OV8865_GAIN_CTRL_H_REG ,
OV8865_GAIN_CTRL_H ( gain ) ) ;
if ( ret )
return ret ;
return ov8865_write ( sensor , OV8865_GAIN_CTRL_L_REG ,
OV8865_GAIN_CTRL_L ( gain ) ) ;
}
/* White Balance */
static int ov8865_red_balance_configure ( struct ov8865_sensor * sensor ,
u32 red_balance )
{
int ret ;
ret = ov8865_write ( sensor , OV8865_ISP_GAIN_RED_H_REG ,
OV8865_ISP_GAIN_RED_H ( red_balance ) ) ;
if ( ret )
return ret ;
return ov8865_write ( sensor , OV8865_ISP_GAIN_RED_L_REG ,
OV8865_ISP_GAIN_RED_L ( red_balance ) ) ;
}
static int ov8865_blue_balance_configure ( struct ov8865_sensor * sensor ,
u32 blue_balance )
{
int ret ;
ret = ov8865_write ( sensor , OV8865_ISP_GAIN_BLUE_H_REG ,
OV8865_ISP_GAIN_BLUE_H ( blue_balance ) ) ;
if ( ret )
return ret ;
return ov8865_write ( sensor , OV8865_ISP_GAIN_BLUE_L_REG ,
OV8865_ISP_GAIN_BLUE_L ( blue_balance ) ) ;
}
/* Flip */
static int ov8865_flip_vert_configure ( struct ov8865_sensor * sensor , bool enable )
{
u8 bits = OV8865_FORMAT1_FLIP_VERT_ISP_EN |
OV8865_FORMAT1_FLIP_VERT_SENSOR_EN ;
return ov8865_update_bits ( sensor , OV8865_FORMAT1_REG , bits ,
enable ? bits : 0 ) ;
}
static int ov8865_flip_horz_configure ( struct ov8865_sensor * sensor , bool enable )
{
u8 bits = OV8865_FORMAT2_FLIP_HORZ_ISP_EN |
OV8865_FORMAT2_FLIP_HORZ_SENSOR_EN ;
return ov8865_update_bits ( sensor , OV8865_FORMAT2_REG , bits ,
enable ? bits : 0 ) ;
}
/* Test Pattern */
static int ov8865_test_pattern_configure ( struct ov8865_sensor * sensor ,
unsigned int index )
{
if ( index > = ARRAY_SIZE ( ov8865_test_pattern_bits ) )
return - EINVAL ;
return ov8865_write ( sensor , OV8865_PRE_CTRL0_REG ,
ov8865_test_pattern_bits [ index ] ) ;
}
/* State */
static int ov8865_state_mipi_configure ( struct ov8865_sensor * sensor ,
const struct ov8865_mode * mode ,
u32 mbus_code )
{
struct ov8865_ctrls * ctrls = & sensor - > ctrls ;
struct v4l2_fwnode_bus_mipi_csi2 * bus_mipi_csi2 =
& 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 = ov8865_mode_mipi_clk_rate ( sensor , mode ) ;
if ( ! mipi_clk_rate )
return - EINVAL ;
for ( i = 0 ; i < ARRAY_SIZE ( ov8865_link_freq_menu ) ; i + + ) {
s64 freq = ov8865_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 ( ov8865_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_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 ov8865_state_configure ( struct ov8865_sensor * sensor ,
const struct ov8865_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 = ov8865_mode_configure ( sensor , mode , mbus_code ) ;
if ( ret )
return ret ;
}
ret = ov8865_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 ov8865_state_init ( struct ov8865_sensor * sensor )
{
return ov8865_state_configure ( sensor , & ov8865_modes [ 0 ] ,
ov8865_mbus_codes [ 0 ] ) ;
}
/* Sensor Base */
static int ov8865_sensor_init ( struct ov8865_sensor * sensor )
{
int ret ;
ret = ov8865_sw_reset ( sensor ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to perform sw reset \n " ) ;
return ret ;
}
ret = ov8865_sw_standby ( sensor , 1 ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to set sensor standby \n " ) ;
return ret ;
}
ret = ov8865_chip_id_check ( sensor ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to check sensor chip id \n " ) ;
return ret ;
}
ret = ov8865_write_sequence ( sensor , ov8865_init_sequence ,
ARRAY_SIZE ( ov8865_init_sequence ) ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to write init sequence \n " ) ;
return ret ;
}
ret = ov8865_charge_pump_configure ( sensor ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to configure pad \n " ) ;
return ret ;
}
ret = ov8865_mipi_configure ( sensor ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to configure MIPI \n " ) ;
return ret ;
}
ret = ov8865_isp_configure ( sensor ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to configure ISP \n " ) ;
return ret ;
}
ret = ov8865_black_level_configure ( sensor ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to configure black level \n " ) ;
return ret ;
}
/* Configure current mode. */
ret = ov8865_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 ov8865_sensor_power ( struct ov8865_sensor * sensor , bool on )
{
/* Keep initialized to zero for disable label. */
int ret = 0 ;
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 ;
}
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 ;
}
ret = clk_prepare_enable ( sensor - > extclk ) ;
if ( ret ) {
dev_err ( sensor - > dev , " failed to enable EXTCLK clock \n " ) ;
goto disable ;
}
gpiod_set_value_cansleep ( sensor - > reset , 0 ) ;
gpiod_set_value_cansleep ( sensor - > powerdown , 0 ) ;
/* Time to enter streaming mode according to power timings. */
usleep_range ( 10000 , 12000 ) ;
} else {
disable :
gpiod_set_value_cansleep ( sensor - > powerdown , 1 ) ;
gpiod_set_value_cansleep ( sensor - > reset , 1 ) ;
clk_disable_unprepare ( sensor - > extclk ) ;
regulator_disable ( sensor - > dvdd ) ;
regulator_disable ( sensor - > avdd ) ;
regulator_disable ( sensor - > dovdd ) ;
}
return ret ;
}
/* Controls */
static int ov8865_s_ctrl ( struct v4l2_ctrl * ctrl )
{
struct v4l2_subdev * subdev = ov8865_ctrl_subdev ( ctrl ) ;
struct ov8865_sensor * sensor = ov8865_subdev_sensor ( subdev ) ;
unsigned int index ;
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 :
ret = ov8865_exposure_configure ( sensor , ctrl - > val ) ;
if ( ret )
return ret ;
break ;
case V4L2_CID_GAIN :
ret = ov8865_gain_configure ( sensor , ctrl - > val ) ;
if ( ret )
return ret ;
break ;
case V4L2_CID_RED_BALANCE :
return ov8865_red_balance_configure ( sensor , ctrl - > val ) ;
case V4L2_CID_BLUE_BALANCE :
return ov8865_blue_balance_configure ( sensor , ctrl - > val ) ;
case V4L2_CID_HFLIP :
return ov8865_flip_horz_configure ( sensor , ! ! ctrl - > val ) ;
case V4L2_CID_VFLIP :
return ov8865_flip_vert_configure ( sensor , ! ! ctrl - > val ) ;
case V4L2_CID_TEST_PATTERN :
index = ( unsigned int ) ctrl - > val ;
return ov8865_test_pattern_configure ( sensor , index ) ;
default :
return - EINVAL ;
}
return 0 ;
}
static const struct v4l2_ctrl_ops ov8865_ctrl_ops = {
. s_ctrl = ov8865_s_ctrl ,
} ;
static int ov8865_ctrls_init ( struct ov8865_sensor * sensor )
{
struct ov8865_ctrls * ctrls = & sensor - > ctrls ;
struct v4l2_ctrl_handler * handler = & ctrls - > handler ;
const struct v4l2_ctrl_ops * ops = & ov8865_ctrl_ops ;
int ret ;
v4l2_ctrl_handler_init ( handler , 32 ) ;
/* Use our mutex for ctrl locking. */
handler - > lock = & sensor - > mutex ;
/* Exposure */
v4l2_ctrl_new_std ( handler , ops , V4L2_CID_EXPOSURE , 16 , 1048575 , 16 ,
512 ) ;
/* Gain */
v4l2_ctrl_new_std ( handler , ops , V4L2_CID_GAIN , 128 , 8191 , 128 , 128 ) ;
/* White Balance */
v4l2_ctrl_new_std ( handler , ops , V4L2_CID_RED_BALANCE , 1 , 32767 , 1 ,
1024 ) ;
v4l2_ctrl_new_std ( handler , ops , V4L2_CID_BLUE_BALANCE , 1 , 32767 , 1 ,
1024 ) ;
/* 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 ( ov8865_test_pattern_menu ) - 1 ,
0 , 0 , ov8865_test_pattern_menu ) ;
/* MIPI CSI-2 */
ctrls - > link_freq =
v4l2_ctrl_new_int_menu ( handler , NULL , V4L2_CID_LINK_FREQ ,
ARRAY_SIZE ( ov8865_link_freq_menu ) - 1 ,
0 , ov8865_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 - > 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 ov8865_s_stream ( struct v4l2_subdev * subdev , int enable )
{
struct ov8865_sensor * sensor = ov8865_subdev_sensor ( subdev ) ;
struct ov8865_state * state = & sensor - > state ;
int ret ;
if ( enable ) {
2021-04-23 17:19:16 +02:00
ret = pm_runtime_resume_and_get ( sensor - > dev ) ;
if ( ret < 0 )
2020-12-31 15:27:01 +01:00
return ret ;
}
mutex_lock ( & sensor - > mutex ) ;
ret = ov8865_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 ov8865_g_frame_interval ( struct v4l2_subdev * subdev ,
struct v4l2_subdev_frame_interval * interval )
{
struct ov8865_sensor * sensor = ov8865_subdev_sensor ( subdev ) ;
const struct ov8865_mode * mode ;
mutex_lock ( & sensor - > mutex ) ;
mode = sensor - > state . mode ;
interval - > interval = mode - > frame_interval ;
mutex_unlock ( & sensor - > mutex ) ;
2021-03-10 09:52:14 +01:00
return 0 ;
2020-12-31 15:27:01 +01:00
}
static const struct v4l2_subdev_video_ops ov8865_subdev_video_ops = {
. s_stream = ov8865_s_stream ,
. g_frame_interval = ov8865_g_frame_interval ,
. s_frame_interval = ov8865_g_frame_interval ,
} ;
/* Subdev Pad Operations */
static int ov8865_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:27:01 +01:00
struct v4l2_subdev_mbus_code_enum * code_enum )
{
if ( code_enum - > index > = ARRAY_SIZE ( ov8865_mbus_codes ) )
return - EINVAL ;
code_enum - > code = ov8865_mbus_codes [ code_enum - > index ] ;
return 0 ;
}
static void ov8865_mbus_format_fill ( struct v4l2_mbus_framefmt * mbus_format ,
u32 mbus_code ,
const struct ov8865_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 ov8865_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:27:01 +01:00
struct v4l2_subdev_format * format )
{
struct ov8865_sensor * sensor = ov8865_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:27:01 +01:00
format - > pad ) ;
else
ov8865_mbus_format_fill ( mbus_format , sensor - > state . mbus_code ,
sensor - > state . mode ) ;
mutex_unlock ( & sensor - > mutex ) ;
return 0 ;
}
static int ov8865_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:27:01 +01:00
struct v4l2_subdev_format * format )
{
struct ov8865_sensor * sensor = ov8865_subdev_sensor ( subdev ) ;
struct v4l2_mbus_framefmt * mbus_format = & format - > format ;
const struct ov8865_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 ( ov8865_mbus_codes ) ; index + + ) {
if ( ov8865_mbus_codes [ index ] = = mbus_format - > code ) {
mbus_code = mbus_format - > code ;
break ;
}
}
/* Fallback to default. */
if ( ! mbus_code )
mbus_code = ov8865_mbus_codes [ 0 ] ;
/* Find the mode with nearest dimensions. */
mode = v4l2_find_nearest_size ( ov8865_modes , ARRAY_SIZE ( ov8865_modes ) ,
output_size_x , output_size_y ,
mbus_format - > width , mbus_format - > height ) ;
if ( ! mode ) {
ret = - EINVAL ;
goto complete ;
}
ov8865_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:27:01 +01:00
* mbus_format ;
else if ( sensor - > state . mode ! = mode | |
sensor - > state . mbus_code ! = mbus_code )
ret = ov8865_state_configure ( sensor , mode , mbus_code ) ;
complete :
mutex_unlock ( & sensor - > mutex ) ;
return ret ;
}
static int ov8865_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:27:01 +01:00
struct v4l2_subdev_frame_size_enum * size_enum )
{
const struct ov8865_mode * mode ;
if ( size_enum - > index > = ARRAY_SIZE ( ov8865_modes ) )
return - EINVAL ;
mode = & ov8865_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 ov8865_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:27:01 +01:00
struct v4l2_subdev_frame_interval_enum * interval_enum )
{
const struct ov8865_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 ( ov8865_modes ) ; mode_index + + ) {
mode = & ov8865_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-04-12 09:48:31 +02:00
if ( mode_index = = ARRAY_SIZE ( ov8865_modes ) )
2020-12-31 15:27:01 +01:00
return - EINVAL ;
interval_enum - > interval = mode - > frame_interval ;
return 0 ;
}
static const struct v4l2_subdev_pad_ops ov8865_subdev_pad_ops = {
. enum_mbus_code = ov8865_enum_mbus_code ,
. get_fmt = ov8865_get_fmt ,
. set_fmt = ov8865_set_fmt ,
. enum_frame_size = ov8865_enum_frame_size ,
. enum_frame_interval = ov8865_enum_frame_interval ,
} ;
static const struct v4l2_subdev_ops ov8865_subdev_ops = {
. video = & ov8865_subdev_video_ops ,
. pad = & ov8865_subdev_pad_ops ,
} ;
static int ov8865_suspend ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct v4l2_subdev * subdev = i2c_get_clientdata ( client ) ;
struct ov8865_sensor * sensor = ov8865_subdev_sensor ( subdev ) ;
struct ov8865_state * state = & sensor - > state ;
int ret = 0 ;
mutex_lock ( & sensor - > mutex ) ;
if ( state - > streaming ) {
ret = ov8865_sw_standby ( sensor , true ) ;
if ( ret )
goto complete ;
}
ret = ov8865_sensor_power ( sensor , false ) ;
if ( ret )
ov8865_sw_standby ( sensor , false ) ;
complete :
mutex_unlock ( & sensor - > mutex ) ;
return ret ;
}
static int ov8865_resume ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct v4l2_subdev * subdev = i2c_get_clientdata ( client ) ;
struct ov8865_sensor * sensor = ov8865_subdev_sensor ( subdev ) ;
struct ov8865_state * state = & sensor - > state ;
int ret = 0 ;
mutex_lock ( & sensor - > mutex ) ;
ret = ov8865_sensor_power ( sensor , true ) ;
if ( ret )
goto complete ;
ret = ov8865_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 = ov8865_sw_standby ( sensor , false ) ;
if ( ret )
goto error_power ;
}
goto complete ;
error_power :
ov8865_sensor_power ( sensor , false ) ;
complete :
mutex_unlock ( & sensor - > mutex ) ;
return ret ;
}
static int ov8865_probe ( struct i2c_client * client )
{
struct device * dev = & client - > dev ;
struct fwnode_handle * handle ;
struct ov8865_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:27:01 +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-01-20 09:42:38 +01:00
if ( IS_ERR ( sensor - > dovdd ) ) {
2020-12-31 15:27:01 +01:00
dev_err ( dev , " cannot get DOVDD (digital I/O) regulator \n " ) ;
2021-01-20 09:42:38 +01:00
ret = PTR_ERR ( sensor - > dovdd ) ;
2020-12-31 15:27:01 +01:00
goto error_endpoint ;
}
/* AVDD: analog */
sensor - > avdd = devm_regulator_get ( dev , " avdd " ) ;
if ( IS_ERR ( sensor - > avdd ) ) {
dev_err ( dev , " cannot get AVDD (analog) regulator \n " ) ;
2021-01-20 09:42:38 +01:00
ret = PTR_ERR ( sensor - > avdd ) ;
2020-12-31 15:27:01 +01:00
goto error_endpoint ;
}
/* External Clock */
sensor - > extclk = devm_clk_get ( dev , NULL ) ;
if ( IS_ERR ( sensor - > extclk ) ) {
dev_err ( dev , " failed to get external clock \n " ) ;
ret = PTR_ERR ( sensor - > extclk ) ;
goto error_endpoint ;
}
rate = clk_get_rate ( sensor - > extclk ) ;
if ( rate ! = OV8865_EXTCLK_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 , & ov8865_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 = ov8865_ctrls_init ( sensor ) ;
if ( ret )
goto error_mutex ;
ret = ov8865_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:27:01 +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 ;
}
static int ov8865_remove ( struct i2c_client * client )
{
struct v4l2_subdev * subdev = i2c_get_clientdata ( client ) ;
struct ov8865_sensor * sensor = ov8865_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 ) ;
v4l2_fwnode_endpoint_free ( & sensor - > endpoint ) ;
return 0 ;
}
static const struct dev_pm_ops ov8865_pm_ops = {
SET_RUNTIME_PM_OPS ( ov8865_suspend , ov8865_resume , NULL )
} ;
static const struct of_device_id ov8865_of_match [ ] = {
{ . compatible = " ovti,ov8865 " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , ov8865_of_match ) ;
static struct i2c_driver ov8865_driver = {
. driver = {
. name = " ov8865 " ,
. of_match_table = ov8865_of_match ,
. pm = & ov8865_pm_ops ,
} ,
. probe_new = ov8865_probe ,
. remove = ov8865_remove ,
} ;
module_i2c_driver ( ov8865_driver ) ;
MODULE_AUTHOR ( " Paul Kocialkowski <paul.kocialkowski@bootlin.com> " ) ;
MODULE_DESCRIPTION ( " V4L2 driver for the OmniVision OV8865 image sensor " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;