2022-10-11 14:30:30 +02:00
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for VGXY61 global shutter sensor family driver
*
* Copyright ( C ) 2022 STMicroelectronics SA
*/
# include <asm-generic/unaligned.h>
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/gpio/consumer.h>
# include <linux/i2c.h>
# include <linux/iopoll.h>
# include <linux/module.h>
# include <linux/pm_runtime.h>
# include <linux/regulator/consumer.h>
# include <linux/units.h>
# include <media/mipi-csi2.h>
# include <media/v4l2-async.h>
# include <media/v4l2-ctrls.h>
# include <media/v4l2-device.h>
# include <media/v4l2-fwnode.h>
# include <media/v4l2-subdev.h>
# define VGXY61_REG_8BIT(n) ((1 << 16) | (n))
# define VGXY61_REG_16BIT(n) ((2 << 16) | (n))
# define VGXY61_REG_32BIT(n) ((4 << 16) | (n))
# define VGXY61_REG_SIZE_SHIFT 16
# define VGXY61_REG_ADDR_MASK 0xffff
# define VGXY61_REG_MODEL_ID VGXY61_REG_16BIT(0x0000)
# define VG5661_MODEL_ID 0x5661
# define VG5761_MODEL_ID 0x5761
# define VGXY61_REG_REVISION VGXY61_REG_16BIT(0x0002)
# define VGXY61_REG_FWPATCH_REVISION VGXY61_REG_16BIT(0x0014)
# define VGXY61_REG_FWPATCH_START_ADDR VGXY61_REG_8BIT(0x2000)
# define VGXY61_REG_SYSTEM_FSM VGXY61_REG_8BIT(0x0020)
# define VGXY61_SYSTEM_FSM_SW_STBY 0x03
# define VGXY61_SYSTEM_FSM_STREAMING 0x04
# define VGXY61_REG_NVM VGXY61_REG_8BIT(0x0023)
# define VGXY61_NVM_OK 0x04
# define VGXY61_REG_STBY VGXY61_REG_8BIT(0x0201)
# define VGXY61_STBY_NO_REQ 0
# define VGXY61_STBY_REQ_TMP_READ BIT(2)
# define VGXY61_REG_STREAMING VGXY61_REG_8BIT(0x0202)
# define VGXY61_STREAMING_NO_REQ 0
# define VGXY61_STREAMING_REQ_STOP BIT(0)
# define VGXY61_STREAMING_REQ_START BIT(1)
# define VGXY61_REG_EXT_CLOCK VGXY61_REG_32BIT(0x0220)
# define VGXY61_REG_CLK_PLL_PREDIV VGXY61_REG_8BIT(0x0224)
# define VGXY61_REG_CLK_SYS_PLL_MULT VGXY61_REG_8BIT(0x0225)
# define VGXY61_REG_GPIO_0_CTRL VGXY61_REG_8BIT(0x0236)
# define VGXY61_REG_GPIO_1_CTRL VGXY61_REG_8BIT(0x0237)
# define VGXY61_REG_GPIO_2_CTRL VGXY61_REG_8BIT(0x0238)
# define VGXY61_REG_GPIO_3_CTRL VGXY61_REG_8BIT(0x0239)
# define VGXY61_REG_SIGNALS_POLARITY_CTRL VGXY61_REG_8BIT(0x023b)
# define VGXY61_REG_LINE_LENGTH VGXY61_REG_16BIT(0x0300)
# define VGXY61_REG_ORIENTATION VGXY61_REG_8BIT(0x0302)
# define VGXY61_REG_VT_CTRL VGXY61_REG_8BIT(0x0304)
# define VGXY61_REG_FORMAT_CTRL VGXY61_REG_8BIT(0x0305)
# define VGXY61_REG_OIF_CTRL VGXY61_REG_16BIT(0x0306)
# define VGXY61_REG_OIF_ROI0_CTRL VGXY61_REG_8BIT(0x030a)
# define VGXY61_REG_ROI0_START_H VGXY61_REG_16BIT(0x0400)
# define VGXY61_REG_ROI0_START_V VGXY61_REG_16BIT(0x0402)
# define VGXY61_REG_ROI0_END_H VGXY61_REG_16BIT(0x0404)
# define VGXY61_REG_ROI0_END_V VGXY61_REG_16BIT(0x0406)
# define VGXY61_REG_PATGEN_CTRL VGXY61_REG_32BIT(0x0440)
# define VGXY61_PATGEN_LONG_ENABLE BIT(16)
# define VGXY61_PATGEN_SHORT_ENABLE BIT(0)
# define VGXY61_PATGEN_LONG_TYPE_SHIFT 18
# define VGXY61_PATGEN_SHORT_TYPE_SHIFT 4
# define VGXY61_REG_FRAME_CONTENT_CTRL VGXY61_REG_8BIT(0x0478)
# define VGXY61_REG_COARSE_EXPOSURE_LONG VGXY61_REG_16BIT(0x0500)
# define VGXY61_REG_COARSE_EXPOSURE_SHORT VGXY61_REG_16BIT(0x0504)
# define VGXY61_REG_ANALOG_GAIN VGXY61_REG_8BIT(0x0508)
# define VGXY61_REG_DIGITAL_GAIN_LONG VGXY61_REG_16BIT(0x050a)
# define VGXY61_REG_DIGITAL_GAIN_SHORT VGXY61_REG_16BIT(0x0512)
# define VGXY61_REG_FRAME_LENGTH VGXY61_REG_16BIT(0x051a)
# define VGXY61_REG_SIGNALS_CTRL VGXY61_REG_16BIT(0x0522)
# define VGXY61_SIGNALS_GPIO_ID_SHIFT 4
# define VGXY61_REG_READOUT_CTRL VGXY61_REG_8BIT(0x0530)
# define VGXY61_REG_HDR_CTRL VGXY61_REG_8BIT(0x0532)
# define VGXY61_REG_PATGEN_LONG_DATA_GR VGXY61_REG_16BIT(0x092c)
# define VGXY61_REG_PATGEN_LONG_DATA_R VGXY61_REG_16BIT(0x092e)
# define VGXY61_REG_PATGEN_LONG_DATA_B VGXY61_REG_16BIT(0x0930)
# define VGXY61_REG_PATGEN_LONG_DATA_GB VGXY61_REG_16BIT(0x0932)
# define VGXY61_REG_PATGEN_SHORT_DATA_GR VGXY61_REG_16BIT(0x0950)
# define VGXY61_REG_PATGEN_SHORT_DATA_R VGXY61_REG_16BIT(0x0952)
# define VGXY61_REG_PATGEN_SHORT_DATA_B VGXY61_REG_16BIT(0x0954)
# define VGXY61_REG_PATGEN_SHORT_DATA_GB VGXY61_REG_16BIT(0x0956)
# define VGXY61_REG_BYPASS_CTRL VGXY61_REG_8BIT(0x0a60)
# define VGX661_WIDTH 1464
# define VGX661_HEIGHT 1104
# define VGX761_WIDTH 1944
# define VGX761_HEIGHT 1204
# define VGX661_DEFAULT_MODE 1
# define VGX761_DEFAULT_MODE 1
# define VGX661_SHORT_ROT_TERM 93
# define VGX761_SHORT_ROT_TERM 90
# define VGXY61_EXPOS_ROT_TERM 66
# define VGXY61_WRITE_MULTIPLE_CHUNK_MAX 16
# define VGXY61_NB_GPIOS 4
# define VGXY61_NB_POLARITIES 5
# define VGXY61_FRAME_LENGTH_DEF 1313
# define VGXY61_MIN_FRAME_LENGTH 1288
# define VGXY61_MIN_EXPOSURE 10
# define VGXY61_HDR_LINEAR_RATIO 10
# define VGXY61_TIMEOUT_MS 500
# define VGXY61_MEDIA_BUS_FMT_DEF MEDIA_BUS_FMT_Y8_1X8
# define VGXY61_FWPATCH_REVISION_MAJOR 2
# define VGXY61_FWPATCH_REVISION_MINOR 0
# define VGXY61_FWPATCH_REVISION_MICRO 5
static const u8 patch_array [ ] = {
0xbf , 0x00 , 0x05 , 0x20 , 0x06 , 0x01 , 0xe0 , 0xe0 , 0x04 , 0x80 , 0xe6 , 0x45 ,
0xed , 0x6f , 0xfe , 0xff , 0x14 , 0x80 , 0x1f , 0x84 , 0x10 , 0x42 , 0x05 , 0x7c ,
0x01 , 0xc4 , 0x1e , 0x80 , 0xb6 , 0x42 , 0x00 , 0xe0 , 0x1e , 0x82 , 0x1e , 0xc0 ,
0x93 , 0xdd , 0xc3 , 0xc1 , 0x0c , 0x04 , 0x00 , 0xfa , 0x86 , 0x0d , 0x70 , 0xe1 ,
0x04 , 0x98 , 0x15 , 0x00 , 0x28 , 0xe0 , 0x14 , 0x02 , 0x08 , 0xfc , 0x15 , 0x40 ,
0x28 , 0xe0 , 0x98 , 0x58 , 0xe0 , 0xef , 0x04 , 0x98 , 0x0e , 0x04 , 0x00 , 0xf0 ,
0x15 , 0x00 , 0x28 , 0xe0 , 0x19 , 0xc8 , 0x15 , 0x40 , 0x28 , 0xe0 , 0xc6 , 0x41 ,
0xfc , 0xe0 , 0x14 , 0x80 , 0x1f , 0x84 , 0x14 , 0x02 , 0xa0 , 0xfc , 0x1e , 0x80 ,
0x14 , 0x80 , 0x14 , 0x02 , 0x80 , 0xfb , 0x14 , 0x02 , 0xe0 , 0xfc , 0x1e , 0x80 ,
0x14 , 0xc0 , 0x1f , 0x84 , 0x14 , 0x02 , 0xa4 , 0xfc , 0x1e , 0xc0 , 0x14 , 0xc0 ,
0x14 , 0x02 , 0x80 , 0xfb , 0x14 , 0x02 , 0xe4 , 0xfc , 0x1e , 0xc0 , 0x0c , 0x0c ,
0x00 , 0xf2 , 0x93 , 0xdd , 0x86 , 0x00 , 0xf8 , 0xe0 , 0x04 , 0x80 , 0xc6 , 0x03 ,
0x70 , 0xe1 , 0x0e , 0x84 , 0x93 , 0xdd , 0xc3 , 0xc1 , 0x0c , 0x04 , 0x00 , 0xfa ,
0x6b , 0x80 , 0x06 , 0x40 , 0x6c , 0xe1 , 0x04 , 0x80 , 0x09 , 0x00 , 0xe0 , 0xe0 ,
0x0b , 0xa1 , 0x95 , 0x84 , 0x05 , 0x0c , 0x1c , 0xe0 , 0x86 , 0x02 , 0xf9 , 0x60 ,
0xe0 , 0xcf , 0x78 , 0x6e , 0x80 , 0xef , 0x25 , 0x0c , 0x18 , 0xe0 , 0x05 , 0x4c ,
0x1c , 0xe0 , 0x86 , 0x02 , 0xf9 , 0x60 , 0xe0 , 0xcf , 0x0b , 0x84 , 0xd8 , 0x6d ,
0x80 , 0xef , 0x05 , 0x4c , 0x18 , 0xe0 , 0x04 , 0xd8 , 0x0b , 0xa5 , 0x95 , 0x84 ,
0x05 , 0x0c , 0x2c , 0xe0 , 0x06 , 0x02 , 0x01 , 0x60 , 0xe0 , 0xce , 0x18 , 0x6d ,
0x80 , 0xef , 0x25 , 0x0c , 0x30 , 0xe0 , 0x05 , 0x4c , 0x2c , 0xe0 , 0x06 , 0x02 ,
0x01 , 0x60 , 0xe0 , 0xce , 0x0b , 0x84 , 0x78 , 0x6c , 0x80 , 0xef , 0x05 , 0x4c ,
0x30 , 0xe0 , 0x0c , 0x0c , 0x00 , 0xf2 , 0x93 , 0xdd , 0x46 , 0x01 , 0x70 , 0xe1 ,
0x08 , 0x80 , 0x0b , 0xa1 , 0x08 , 0x5c , 0x00 , 0xda , 0x06 , 0x01 , 0x68 , 0xe1 ,
0x04 , 0x80 , 0x4a , 0x40 , 0x84 , 0xe0 , 0x08 , 0x5c , 0x00 , 0x9a , 0x06 , 0x01 ,
0xe0 , 0xe0 , 0x04 , 0x80 , 0x15 , 0x00 , 0x60 , 0xe0 , 0x19 , 0xc4 , 0x15 , 0x40 ,
0x60 , 0xe0 , 0x15 , 0x00 , 0x78 , 0xe0 , 0x19 , 0xc4 , 0x15 , 0x40 , 0x78 , 0xe0 ,
0x93 , 0xdd , 0xc3 , 0xc1 , 0x46 , 0x01 , 0x70 , 0xe1 , 0x08 , 0x80 , 0x0b , 0xa1 ,
0x08 , 0x5c , 0x00 , 0xda , 0x06 , 0x01 , 0x68 , 0xe1 , 0x04 , 0x80 , 0x4a , 0x40 ,
0x84 , 0xe0 , 0x08 , 0x5c , 0x00 , 0x9a , 0x06 , 0x01 , 0xe0 , 0xe0 , 0x14 , 0x80 ,
0x25 , 0x02 , 0x54 , 0xe0 , 0x29 , 0xc4 , 0x25 , 0x42 , 0x54 , 0xe0 , 0x24 , 0x80 ,
0x35 , 0x04 , 0x6c , 0xe0 , 0x39 , 0xc4 , 0x35 , 0x44 , 0x6c , 0xe0 , 0x25 , 0x02 ,
0x64 , 0xe0 , 0x29 , 0xc4 , 0x25 , 0x42 , 0x64 , 0xe0 , 0x04 , 0x80 , 0x15 , 0x00 ,
0x7c , 0xe0 , 0x19 , 0xc4 , 0x15 , 0x40 , 0x7c , 0xe0 , 0x93 , 0xdd , 0xc3 , 0xc1 ,
0x4c , 0x04 , 0x7c , 0xfa , 0x86 , 0x40 , 0x98 , 0xe0 , 0x14 , 0x80 , 0x1b , 0xa1 ,
0x06 , 0x00 , 0x00 , 0xc0 , 0x08 , 0x42 , 0x38 , 0xdc , 0x08 , 0x64 , 0xa0 , 0xef ,
0x86 , 0x42 , 0x3c , 0xe0 , 0x68 , 0x49 , 0x80 , 0xef , 0x6b , 0x80 , 0x78 , 0x53 ,
0xc8 , 0xef , 0xc6 , 0x54 , 0x6c , 0xe1 , 0x7b , 0x80 , 0xb5 , 0x14 , 0x0c , 0xf8 ,
0x05 , 0x14 , 0x14 , 0xf8 , 0x1a , 0xac , 0x8a , 0x80 , 0x0b , 0x90 , 0x38 , 0x55 ,
0x80 , 0xef , 0x1a , 0xae , 0x17 , 0xc2 , 0x03 , 0x82 , 0x88 , 0x65 , 0x80 , 0xef ,
0x1b , 0x80 , 0x0b , 0x8e , 0x68 , 0x65 , 0x80 , 0xef , 0x9b , 0x80 , 0x0b , 0x8c ,
0x08 , 0x65 , 0x80 , 0xef , 0x6b , 0x80 , 0x0b , 0x92 , 0x1b , 0x8c , 0x98 , 0x64 ,
0x80 , 0xef , 0x1a , 0xec , 0x9b , 0x80 , 0x0b , 0x90 , 0x95 , 0x54 , 0x10 , 0xe0 ,
0xa8 , 0x53 , 0x80 , 0xef , 0x1a , 0xee , 0x17 , 0xc2 , 0x03 , 0x82 , 0xf8 , 0x63 ,
0x80 , 0xef , 0x1b , 0x80 , 0x0b , 0x8e , 0xd8 , 0x63 , 0x80 , 0xef , 0x1b , 0x8c ,
0x68 , 0x63 , 0x80 , 0xef , 0x6b , 0x80 , 0x0b , 0x92 , 0x65 , 0x54 , 0x14 , 0xe0 ,
0x08 , 0x65 , 0x84 , 0xef , 0x68 , 0x63 , 0x80 , 0xef , 0x7b , 0x80 , 0x0b , 0x8c ,
0xa8 , 0x64 , 0x84 , 0xef , 0x08 , 0x63 , 0x80 , 0xef , 0x14 , 0xe8 , 0x46 , 0x44 ,
0x94 , 0xe1 , 0x24 , 0x88 , 0x4a , 0x4e , 0x04 , 0xe0 , 0x14 , 0xea , 0x1a , 0x04 ,
0x08 , 0xe0 , 0x0a , 0x40 , 0x84 , 0xed , 0x0c , 0x04 , 0x00 , 0xe2 , 0x4a , 0x40 ,
0x04 , 0xe0 , 0x19 , 0x16 , 0xc0 , 0xe0 , 0x0a , 0x40 , 0x84 , 0xed , 0x21 , 0x54 ,
0x60 , 0xe0 , 0x0c , 0x04 , 0x00 , 0xe2 , 0x1b , 0xa5 , 0x0e , 0xea , 0x01 , 0x89 ,
0x21 , 0x54 , 0x64 , 0xe0 , 0x7e , 0xe8 , 0x65 , 0x82 , 0x1b , 0xa7 , 0x26 , 0x00 ,
0x00 , 0x80 , 0xa5 , 0x82 , 0x1b , 0xa9 , 0x65 , 0x82 , 0x1b , 0xa3 , 0x01 , 0x85 ,
0x16 , 0x00 , 0x00 , 0xc0 , 0x01 , 0x54 , 0x04 , 0xf8 , 0x06 , 0xaa , 0x01 , 0x83 ,
0x06 , 0xa8 , 0x65 , 0x81 , 0x06 , 0xa8 , 0x01 , 0x54 , 0x04 , 0xf8 , 0x01 , 0x83 ,
0x06 , 0xaa , 0x09 , 0x14 , 0x18 , 0xf8 , 0x0b , 0xa1 , 0x05 , 0x84 , 0xc6 , 0x42 ,
0xd4 , 0xe0 , 0x14 , 0x84 , 0x01 , 0x83 , 0x01 , 0x54 , 0x60 , 0xe0 , 0x01 , 0x54 ,
0x64 , 0xe0 , 0x0b , 0x02 , 0x90 , 0xe0 , 0x10 , 0x02 , 0x90 , 0xe5 , 0x01 , 0x54 ,
0x88 , 0xe0 , 0xb5 , 0x81 , 0xc6 , 0x40 , 0xd4 , 0xe0 , 0x14 , 0x80 , 0x0b , 0x02 ,
0xe0 , 0xe4 , 0x10 , 0x02 , 0x31 , 0x66 , 0x02 , 0xc0 , 0x01 , 0x54 , 0x88 , 0xe0 ,
0x1a , 0x84 , 0x29 , 0x14 , 0x10 , 0xe0 , 0x1c , 0xaa , 0x2b , 0xa1 , 0xf5 , 0x82 ,
0x25 , 0x14 , 0x10 , 0xf8 , 0x2b , 0x04 , 0xa8 , 0xe0 , 0x20 , 0x44 , 0x0d , 0x70 ,
0x03 , 0xc0 , 0x2b , 0xa1 , 0x04 , 0x00 , 0x80 , 0x9a , 0x02 , 0x40 , 0x84 , 0x90 ,
0x03 , 0x54 , 0x04 , 0x80 , 0x4c , 0x0c , 0x7c , 0xf2 , 0x93 , 0xdd , 0x00 , 0x00 ,
0x02 , 0xa9 , 0x00 , 0x00 , 0x64 , 0x4a , 0x40 , 0x00 , 0x08 , 0x2d , 0x58 , 0xe0 ,
0xa8 , 0x98 , 0x40 , 0x00 , 0x28 , 0x07 , 0x34 , 0xe0 , 0x05 , 0xb9 , 0x00 , 0x00 ,
0x28 , 0x00 , 0x41 , 0x05 , 0x88 , 0x00 , 0x41 , 0x3c , 0x98 , 0x00 , 0x41 , 0x52 ,
0x04 , 0x01 , 0x41 , 0x79 , 0x3c , 0x01 , 0x41 , 0x6a , 0x3d , 0xfe , 0x00 , 0x00 ,
} ;
static const char * const vgxy61_test_pattern_menu [ ] = {
" Disabled " ,
" Solid " ,
" Colorbar " ,
" Gradbar " ,
" Hgrey " ,
" Vgrey " ,
" Dgrey " ,
" PN28 " ,
} ;
static const char * const vgxy61_hdr_mode_menu [ ] = {
" HDR linearize " ,
" HDR substraction " ,
" No HDR " ,
} ;
static const char * const vgxy61_supply_name [ ] = {
" VCORE " ,
" VDDIO " ,
" VANA " ,
} ;
static const s64 link_freq [ ] = {
/*
* MIPI output freq is 804 Mhz / 2 , as it uses both rising edge and
* falling edges to send data
*/
402000000ULL
} ;
enum vgxy61_bin_mode {
VGXY61_BIN_MODE_NORMAL ,
VGXY61_BIN_MODE_DIGITAL_X2 ,
VGXY61_BIN_MODE_DIGITAL_X4 ,
} ;
enum vgxy61_hdr_mode {
VGXY61_HDR_LINEAR ,
VGXY61_HDR_SUB ,
VGXY61_NO_HDR ,
} ;
enum vgxy61_strobe_mode {
VGXY61_STROBE_DISABLED ,
VGXY61_STROBE_LONG ,
VGXY61_STROBE_ENABLED ,
} ;
struct vgxy61_mode_info {
u32 width ;
u32 height ;
enum vgxy61_bin_mode bin_mode ;
struct v4l2_rect crop ;
} ;
struct vgxy61_fmt_desc {
u32 code ;
u8 bpp ;
u8 data_type ;
} ;
static const struct vgxy61_fmt_desc vgxy61_supported_codes [ ] = {
{
. code = MEDIA_BUS_FMT_Y8_1X8 ,
. bpp = 8 ,
. data_type = MIPI_CSI2_DT_RAW8 ,
} ,
{
. code = MEDIA_BUS_FMT_Y10_1X10 ,
. bpp = 10 ,
. data_type = MIPI_CSI2_DT_RAW10 ,
} ,
{
. code = MEDIA_BUS_FMT_Y12_1X12 ,
. bpp = 12 ,
. data_type = MIPI_CSI2_DT_RAW12 ,
} ,
{
. code = MEDIA_BUS_FMT_Y14_1X14 ,
. bpp = 14 ,
. data_type = MIPI_CSI2_DT_RAW14 ,
} ,
{
. code = MEDIA_BUS_FMT_Y16_1X16 ,
. bpp = 16 ,
. data_type = MIPI_CSI2_DT_RAW16 ,
} ,
} ;
static const struct vgxy61_mode_info vgx661_mode_data [ ] = {
{
. width = VGX661_WIDTH ,
. height = VGX661_HEIGHT ,
. bin_mode = VGXY61_BIN_MODE_NORMAL ,
. crop = {
. left = 0 ,
. top = 0 ,
. width = VGX661_WIDTH ,
. height = VGX661_HEIGHT ,
} ,
} ,
{
. width = 1280 ,
. height = 720 ,
. bin_mode = VGXY61_BIN_MODE_NORMAL ,
. crop = {
. left = 92 ,
. top = 192 ,
. width = 1280 ,
. height = 720 ,
} ,
} ,
{
. width = 640 ,
. height = 480 ,
. bin_mode = VGXY61_BIN_MODE_DIGITAL_X2 ,
. crop = {
. left = 92 ,
. top = 72 ,
. width = 1280 ,
. height = 960 ,
} ,
} ,
{
. width = 320 ,
. height = 240 ,
. bin_mode = VGXY61_BIN_MODE_DIGITAL_X4 ,
. crop = {
. left = 92 ,
. top = 72 ,
. width = 1280 ,
. height = 960 ,
} ,
} ,
} ;
static const struct vgxy61_mode_info vgx761_mode_data [ ] = {
{
. width = VGX761_WIDTH ,
. height = VGX761_HEIGHT ,
. bin_mode = VGXY61_BIN_MODE_NORMAL ,
. crop = {
. left = 0 ,
. top = 0 ,
. width = VGX761_WIDTH ,
. height = VGX761_HEIGHT ,
} ,
} ,
{
. width = 1920 ,
. height = 1080 ,
. bin_mode = VGXY61_BIN_MODE_NORMAL ,
. crop = {
. left = 12 ,
. top = 62 ,
. width = 1920 ,
. height = 1080 ,
} ,
} ,
{
. width = 1280 ,
. height = 720 ,
. bin_mode = VGXY61_BIN_MODE_NORMAL ,
. crop = {
. left = 332 ,
. top = 242 ,
. width = 1280 ,
. height = 720 ,
} ,
} ,
{
. width = 640 ,
. height = 480 ,
. bin_mode = VGXY61_BIN_MODE_DIGITAL_X2 ,
. crop = {
. left = 332 ,
. top = 122 ,
. width = 1280 ,
. height = 960 ,
} ,
} ,
{
. width = 320 ,
. height = 240 ,
. bin_mode = VGXY61_BIN_MODE_DIGITAL_X4 ,
. crop = {
. left = 332 ,
. top = 122 ,
. width = 1280 ,
. height = 960 ,
} ,
} ,
} ;
struct vgxy61_dev {
struct i2c_client * i2c_client ;
struct v4l2_subdev sd ;
struct media_pad pad ;
struct regulator_bulk_data supplies [ ARRAY_SIZE ( vgxy61_supply_name ) ] ;
struct gpio_desc * reset_gpio ;
struct clk * xclk ;
u32 clk_freq ;
u16 id ;
u16 sensor_width ;
u16 sensor_height ;
u16 oif_ctrl ;
unsigned int nb_of_lane ;
u32 data_rate_in_mbps ;
u32 pclk ;
u16 line_length ;
u16 rot_term ;
bool gpios_polarity ;
/* Lock to protect all members below */
struct mutex lock ;
struct v4l2_ctrl_handler ctrl_handler ;
struct v4l2_ctrl * pixel_rate_ctrl ;
struct v4l2_ctrl * expo_ctrl ;
struct v4l2_ctrl * vblank_ctrl ;
struct v4l2_ctrl * vflip_ctrl ;
struct v4l2_ctrl * hflip_ctrl ;
bool streaming ;
struct v4l2_mbus_framefmt fmt ;
const struct vgxy61_mode_info * sensor_modes ;
unsigned int sensor_modes_nb ;
const struct vgxy61_mode_info * default_mode ;
const struct vgxy61_mode_info * current_mode ;
bool hflip ;
bool vflip ;
enum vgxy61_hdr_mode hdr ;
u16 expo_long ;
u16 expo_short ;
u16 expo_max ;
u16 expo_min ;
u16 vblank ;
u16 vblank_min ;
u16 frame_length ;
u16 digital_gain ;
u8 analog_gain ;
enum vgxy61_strobe_mode strobe_mode ;
u32 pattern ;
} ;
static u8 get_bpp_by_code ( __u32 code )
{
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( vgxy61_supported_codes ) ; i + + ) {
if ( vgxy61_supported_codes [ i ] . code = = code )
return vgxy61_supported_codes [ i ] . bpp ;
}
/* Should never happen */
WARN ( 1 , " Unsupported code %d. default to 8 bpp " , code ) ;
return 8 ;
}
static u8 get_data_type_by_code ( __u32 code )
{
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( vgxy61_supported_codes ) ; i + + ) {
if ( vgxy61_supported_codes [ i ] . code = = code )
return vgxy61_supported_codes [ i ] . data_type ;
}
/* Should never happen */
WARN ( 1 , " Unsupported code %d. default to MIPI_CSI2_DT_RAW8 data type " ,
code ) ;
return MIPI_CSI2_DT_RAW8 ;
}
static void compute_pll_parameters_by_freq ( u32 freq , u8 * prediv , u8 * mult )
{
const unsigned int predivs [ ] = { 1 , 2 , 4 } ;
unsigned int i ;
/*
* Freq range is [ 6 Mhz - 27 Mhz ] already checked .
* Output of divider should be in [ 6 Mhz - 12 Mhz [ .
*/
for ( i = 0 ; i < ARRAY_SIZE ( predivs ) ; i + + ) {
* prediv = predivs [ i ] ;
if ( freq / * prediv < 12 * HZ_PER_MHZ )
break ;
}
WARN_ON ( i = = ARRAY_SIZE ( predivs ) ) ;
/*
* Target freq is 804 Mhz . Don ' t change this as it will impact image
* quality .
*/
* mult = ( ( 804 * HZ_PER_MHZ ) * ( * prediv ) + freq / 2 ) / freq ;
}
static s32 get_pixel_rate ( struct vgxy61_dev * sensor )
{
return div64_u64 ( ( u64 ) sensor - > data_rate_in_mbps * sensor - > nb_of_lane ,
get_bpp_by_code ( sensor - > fmt . code ) ) ;
}
static inline struct vgxy61_dev * to_vgxy61_dev ( struct v4l2_subdev * sd )
{
return container_of ( sd , struct vgxy61_dev , sd ) ;
}
static inline struct v4l2_subdev * ctrl_to_sd ( struct v4l2_ctrl * ctrl )
{
return & container_of ( ctrl - > handler , struct vgxy61_dev ,
ctrl_handler ) - > sd ;
}
static unsigned int get_chunk_size ( struct vgxy61_dev * sensor )
{
struct i2c_adapter * adapter = sensor - > i2c_client - > adapter ;
int max_write_len = VGXY61_WRITE_MULTIPLE_CHUNK_MAX ;
if ( adapter - > quirks & & adapter - > quirks - > max_write_len )
max_write_len = adapter - > quirks - > max_write_len - 2 ;
max_write_len = min ( max_write_len , VGXY61_WRITE_MULTIPLE_CHUNK_MAX ) ;
return max ( max_write_len , 1 ) ;
}
static int vgxy61_read_multiple ( struct vgxy61_dev * sensor , u32 reg ,
unsigned int len )
{
struct i2c_client * client = sensor - > i2c_client ;
struct i2c_msg msg [ 2 ] ;
u8 buf [ 2 ] ;
u8 val [ sizeof ( u32 ) ] = { 0 } ;
int ret ;
if ( len > sizeof ( u32 ) )
return - EINVAL ;
buf [ 0 ] = reg > > 8 ;
buf [ 1 ] = reg & 0xff ;
msg [ 0 ] . addr = client - > addr ;
msg [ 0 ] . flags = client - > flags ;
msg [ 0 ] . buf = buf ;
msg [ 0 ] . len = sizeof ( buf ) ;
msg [ 1 ] . addr = client - > addr ;
msg [ 1 ] . flags = client - > flags | I2C_M_RD ;
msg [ 1 ] . buf = val ;
msg [ 1 ] . len = len ;
ret = i2c_transfer ( client - > adapter , msg , 2 ) ;
if ( ret < 0 ) {
dev_dbg ( & client - > dev , " %s: %x i2c_transfer, reg: %x => %d \n " ,
__func__ , client - > addr , reg , ret ) ;
return ret ;
}
return get_unaligned_le32 ( val ) ;
}
static inline int vgxy61_read_reg ( struct vgxy61_dev * sensor , u32 reg )
{
return vgxy61_read_multiple ( sensor , reg & VGXY61_REG_ADDR_MASK ,
( reg > > VGXY61_REG_SIZE_SHIFT ) & 7 ) ;
}
static int vgxy61_write_multiple ( struct vgxy61_dev * sensor , u32 reg ,
const u8 * data , unsigned int len , int * err )
{
struct i2c_client * client = sensor - > i2c_client ;
struct i2c_msg msg ;
u8 buf [ VGXY61_WRITE_MULTIPLE_CHUNK_MAX + 2 ] ;
unsigned int i ;
int ret ;
if ( err & & * err )
return * err ;
if ( len > VGXY61_WRITE_MULTIPLE_CHUNK_MAX )
return - EINVAL ;
buf [ 0 ] = reg > > 8 ;
buf [ 1 ] = reg & 0xff ;
for ( i = 0 ; i < len ; i + + )
buf [ i + 2 ] = data [ i ] ;
msg . addr = client - > addr ;
msg . flags = client - > flags ;
msg . buf = buf ;
msg . len = len + 2 ;
ret = i2c_transfer ( client - > adapter , & msg , 1 ) ;
if ( ret < 0 ) {
dev_dbg ( & client - > dev , " %s: i2c_transfer, reg: %x => %d \n " ,
__func__ , reg , ret ) ;
if ( err )
* err = ret ;
return ret ;
}
return 0 ;
}
static int vgxy61_write_array ( struct vgxy61_dev * sensor , u32 reg ,
unsigned int nb , const u8 * array )
{
const unsigned int chunk_size = get_chunk_size ( sensor ) ;
int ret ;
unsigned int sz ;
while ( nb ) {
sz = min ( nb , chunk_size ) ;
ret = vgxy61_write_multiple ( sensor , reg , array , sz , NULL ) ;
if ( ret < 0 )
return ret ;
nb - = sz ;
reg + = sz ;
array + = sz ;
}
return 0 ;
}
static inline int vgxy61_write_reg ( struct vgxy61_dev * sensor , u32 reg , u32 val ,
int * err )
{
return vgxy61_write_multiple ( sensor , reg & VGXY61_REG_ADDR_MASK ,
( u8 * ) & val ,
( reg > > VGXY61_REG_SIZE_SHIFT ) & 7 , err ) ;
}
static int vgxy61_poll_reg ( struct vgxy61_dev * sensor , u32 reg , u8 poll_val ,
unsigned int timeout_ms )
{
const unsigned int loop_delay_ms = 10 ;
int ret ;
return read_poll_timeout ( vgxy61_read_reg , ret ,
( ( ret < 0 ) | | ( ret = = poll_val ) ) ,
loop_delay_ms * 1000 , timeout_ms * 1000 ,
false , sensor , reg ) ;
}
static int vgxy61_wait_state ( struct vgxy61_dev * sensor , int state ,
unsigned int timeout_ms )
{
return vgxy61_poll_reg ( sensor , VGXY61_REG_SYSTEM_FSM , state ,
timeout_ms ) ;
}
static int vgxy61_check_bw ( struct vgxy61_dev * sensor )
{
/*
* Simplification of time needed to send short packets and for the MIPI
* to add transition times ( EoT , LPS , and SoT packet delimiters ) needed
* by the protocol to go in low power between 2 packets of data . This
* is a mipi IP constant for the sensor .
*/
const unsigned int mipi_margin = 1056 ;
unsigned int binning_scale = sensor - > current_mode - > crop . height /
sensor - > current_mode - > height ;
u8 bpp = get_bpp_by_code ( sensor - > fmt . code ) ;
unsigned int max_bit_per_line ;
unsigned int bit_per_line ;
u64 line_rate ;
line_rate = sensor - > nb_of_lane * ( u64 ) sensor - > data_rate_in_mbps *
sensor - > line_length ;
max_bit_per_line = div64_u64 ( line_rate , sensor - > pclk ) - mipi_margin ;
bit_per_line = ( bpp * sensor - > current_mode - > width ) / binning_scale ;
return bit_per_line > max_bit_per_line ? - EINVAL : 0 ;
}
static int vgxy61_apply_exposure ( struct vgxy61_dev * sensor )
{
int ret = 0 ;
/* We first set expo to zero to avoid forbidden parameters couple */
vgxy61_write_reg ( sensor , VGXY61_REG_COARSE_EXPOSURE_SHORT , 0 , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_COARSE_EXPOSURE_LONG ,
sensor - > expo_long , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_COARSE_EXPOSURE_SHORT ,
sensor - > expo_short , & ret ) ;
return ret ;
}
static int vgxy61_get_regulators ( struct vgxy61_dev * sensor )
{
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( vgxy61_supply_name ) ; i + + )
sensor - > supplies [ i ] . supply = vgxy61_supply_name [ i ] ;
return devm_regulator_bulk_get ( & sensor - > i2c_client - > dev ,
ARRAY_SIZE ( vgxy61_supply_name ) ,
sensor - > supplies ) ;
}
static int vgxy61_apply_reset ( struct vgxy61_dev * sensor )
{
gpiod_set_value_cansleep ( sensor - > reset_gpio , 0 ) ;
usleep_range ( 5000 , 10000 ) ;
gpiod_set_value_cansleep ( sensor - > reset_gpio , 1 ) ;
usleep_range ( 5000 , 10000 ) ;
gpiod_set_value_cansleep ( sensor - > reset_gpio , 0 ) ;
usleep_range ( 40000 , 100000 ) ;
return vgxy61_wait_state ( sensor , VGXY61_SYSTEM_FSM_SW_STBY ,
VGXY61_TIMEOUT_MS ) ;
}
static void vgxy61_fill_framefmt ( struct vgxy61_dev * sensor ,
const struct vgxy61_mode_info * mode ,
struct v4l2_mbus_framefmt * fmt , u32 code )
{
fmt - > code = code ;
fmt - > width = mode - > width ;
fmt - > height = mode - > height ;
fmt - > colorspace = V4L2_COLORSPACE_RAW ;
fmt - > field = V4L2_FIELD_NONE ;
fmt - > ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT ;
fmt - > quantization = V4L2_QUANTIZATION_DEFAULT ;
fmt - > xfer_func = V4L2_XFER_FUNC_DEFAULT ;
}
static int vgxy61_try_fmt_internal ( struct v4l2_subdev * sd ,
struct v4l2_mbus_framefmt * fmt ,
const struct vgxy61_mode_info * * new_mode )
{
struct vgxy61_dev * sensor = to_vgxy61_dev ( sd ) ;
const struct vgxy61_mode_info * mode = sensor - > sensor_modes ;
unsigned int index ;
for ( index = 0 ; index < ARRAY_SIZE ( vgxy61_supported_codes ) ; index + + ) {
if ( vgxy61_supported_codes [ index ] . code = = fmt - > code )
break ;
}
if ( index = = ARRAY_SIZE ( vgxy61_supported_codes ) )
index = 0 ;
mode = v4l2_find_nearest_size ( sensor - > sensor_modes ,
sensor - > sensor_modes_nb , width , height ,
fmt - > width , fmt - > height ) ;
if ( new_mode )
* new_mode = mode ;
vgxy61_fill_framefmt ( sensor , mode , fmt ,
vgxy61_supported_codes [ index ] . code ) ;
return 0 ;
}
static int vgxy61_get_selection ( struct v4l2_subdev * sd ,
struct v4l2_subdev_state * sd_state ,
struct v4l2_subdev_selection * sel )
{
struct vgxy61_dev * sensor = to_vgxy61_dev ( sd ) ;
switch ( sel - > target ) {
case V4L2_SEL_TGT_CROP :
sel - > r = sensor - > current_mode - > crop ;
return 0 ;
case V4L2_SEL_TGT_NATIVE_SIZE :
case V4L2_SEL_TGT_CROP_DEFAULT :
case V4L2_SEL_TGT_CROP_BOUNDS :
sel - > r . top = 0 ;
sel - > r . left = 0 ;
sel - > r . width = sensor - > sensor_width ;
sel - > r . height = sensor - > sensor_height ;
return 0 ;
}
return - EINVAL ;
}
static int vgxy61_enum_mbus_code ( struct v4l2_subdev * sd ,
struct v4l2_subdev_state * sd_state ,
struct v4l2_subdev_mbus_code_enum * code )
{
if ( code - > index > = ARRAY_SIZE ( vgxy61_supported_codes ) )
return - EINVAL ;
code - > code = vgxy61_supported_codes [ code - > index ] . code ;
return 0 ;
}
static int vgxy61_get_fmt ( struct v4l2_subdev * sd ,
struct v4l2_subdev_state * sd_state ,
struct v4l2_subdev_format * format )
{
struct vgxy61_dev * sensor = to_vgxy61_dev ( sd ) ;
struct v4l2_mbus_framefmt * fmt ;
mutex_lock ( & sensor - > lock ) ;
if ( format - > which = = V4L2_SUBDEV_FORMAT_TRY )
fmt = v4l2_subdev_get_try_format ( & sensor - > sd , sd_state ,
format - > pad ) ;
else
fmt = & sensor - > fmt ;
format - > format = * fmt ;
mutex_unlock ( & sensor - > lock ) ;
return 0 ;
}
static u16 vgxy61_get_vblank_min ( struct vgxy61_dev * sensor ,
enum vgxy61_hdr_mode hdr )
{
u16 min_vblank = VGXY61_MIN_FRAME_LENGTH -
sensor - > current_mode - > crop . height ;
/* Ensure the first rule of thumb can't be negative */
u16 min_vblank_hdr = VGXY61_MIN_EXPOSURE + sensor - > rot_term + 1 ;
if ( hdr ! = VGXY61_NO_HDR )
return max ( min_vblank , min_vblank_hdr ) ;
return min_vblank ;
}
static int vgxy61_enum_frame_size ( struct v4l2_subdev * sd ,
struct v4l2_subdev_state * sd_state ,
struct v4l2_subdev_frame_size_enum * fse )
{
struct vgxy61_dev * sensor = to_vgxy61_dev ( sd ) ;
if ( fse - > index > = sensor - > sensor_modes_nb )
return - EINVAL ;
fse - > min_width = sensor - > sensor_modes [ fse - > index ] . width ;
fse - > max_width = fse - > min_width ;
fse - > min_height = sensor - > sensor_modes [ fse - > index ] . height ;
fse - > max_height = fse - > min_height ;
return 0 ;
}
static int vgxy61_update_analog_gain ( struct vgxy61_dev * sensor , u32 target )
{
sensor - > analog_gain = target ;
if ( sensor - > streaming )
return vgxy61_write_reg ( sensor , VGXY61_REG_ANALOG_GAIN , target ,
NULL ) ;
return 0 ;
}
static int vgxy61_apply_digital_gain ( struct vgxy61_dev * sensor ,
u32 digital_gain )
{
int ret = 0 ;
/*
* For a monochrome version , configuring DIGITAL_GAIN_LONG_CH0 and
* DIGITAL_GAIN_SHORT_CH0 is enough to configure the gain of all
* four sub pixels .
*/
vgxy61_write_reg ( sensor , VGXY61_REG_DIGITAL_GAIN_LONG , digital_gain ,
& ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_DIGITAL_GAIN_SHORT , digital_gain ,
& ret ) ;
return ret ;
}
static int vgxy61_update_digital_gain ( struct vgxy61_dev * sensor , u32 target )
{
sensor - > digital_gain = target ;
if ( sensor - > streaming )
return vgxy61_apply_digital_gain ( sensor , sensor - > digital_gain ) ;
return 0 ;
}
static int vgxy61_apply_patgen ( struct vgxy61_dev * sensor , u32 index )
{
static const u8 index2val [ ] = {
0x0 , 0x1 , 0x2 , 0x3 , 0x10 , 0x11 , 0x12 , 0x13
} ;
u32 pattern = index2val [ index ] ;
u32 reg = ( pattern < < VGXY61_PATGEN_LONG_TYPE_SHIFT ) |
( pattern < < VGXY61_PATGEN_SHORT_TYPE_SHIFT ) ;
if ( pattern )
reg | = VGXY61_PATGEN_LONG_ENABLE | VGXY61_PATGEN_SHORT_ENABLE ;
return vgxy61_write_reg ( sensor , VGXY61_REG_PATGEN_CTRL , reg , NULL ) ;
}
static int vgxy61_update_patgen ( struct vgxy61_dev * sensor , u32 pattern )
{
sensor - > pattern = pattern ;
if ( sensor - > streaming )
return vgxy61_apply_patgen ( sensor , sensor - > pattern ) ;
return 0 ;
}
static int vgxy61_apply_gpiox_strobe_mode ( struct vgxy61_dev * sensor ,
enum vgxy61_strobe_mode mode ,
unsigned int idx )
{
static const u8 index2val [ ] = { 0x0 , 0x1 , 0x3 } ;
2022-11-10 13:41:02 +00:00
int reg ;
2022-10-11 14:30:30 +02:00
reg = vgxy61_read_reg ( sensor , VGXY61_REG_SIGNALS_CTRL ) ;
if ( reg < 0 )
return reg ;
reg & = ~ ( 0xf < < ( idx * VGXY61_SIGNALS_GPIO_ID_SHIFT ) ) ;
reg | = index2val [ mode ] < < ( idx * VGXY61_SIGNALS_GPIO_ID_SHIFT ) ;
return vgxy61_write_reg ( sensor , VGXY61_REG_SIGNALS_CTRL , reg , NULL ) ;
}
static int vgxy61_update_gpios_strobe_mode ( struct vgxy61_dev * sensor ,
enum vgxy61_hdr_mode hdr )
{
unsigned int i ;
int ret ;
switch ( hdr ) {
case VGXY61_HDR_LINEAR :
sensor - > strobe_mode = VGXY61_STROBE_ENABLED ;
break ;
case VGXY61_HDR_SUB :
case VGXY61_NO_HDR :
sensor - > strobe_mode = VGXY61_STROBE_LONG ;
break ;
default :
/* Should never happen */
WARN_ON ( true ) ;
break ;
}
if ( ! sensor - > streaming )
return 0 ;
for ( i = 0 ; i < VGXY61_NB_GPIOS ; i + + ) {
ret = vgxy61_apply_gpiox_strobe_mode ( sensor ,
sensor - > strobe_mode ,
i ) ;
if ( ret )
return ret ;
}
return 0 ;
}
static int vgxy61_update_gpios_strobe_polarity ( struct vgxy61_dev * sensor ,
bool polarity )
{
int ret = 0 ;
if ( sensor - > streaming )
return - EBUSY ;
vgxy61_write_reg ( sensor , VGXY61_REG_GPIO_0_CTRL , polarity < < 1 , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_GPIO_1_CTRL , polarity < < 1 , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_GPIO_2_CTRL , polarity < < 1 , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_GPIO_3_CTRL , polarity < < 1 , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_SIGNALS_POLARITY_CTRL , polarity ,
& ret ) ;
return ret ;
}
static u32 vgxy61_get_expo_long_max ( struct vgxy61_dev * sensor ,
unsigned int short_expo_ratio )
{
u32 first_rot_max_expo , second_rot_max_expo , third_rot_max_expo ;
/* Apply sensor's rules of thumb */
/*
* Short exposure + height must be less than frame length to avoid bad
* pixel line at the botom of the image
*/
first_rot_max_expo =
( ( sensor - > frame_length - sensor - > current_mode - > crop . height -
sensor - > rot_term ) * short_expo_ratio ) - 1 ;
/*
* Total exposition time must be less than frame length to avoid sensor
* crash
*/
second_rot_max_expo =
( ( ( sensor - > frame_length - VGXY61_EXPOS_ROT_TERM ) *
short_expo_ratio ) / ( short_expo_ratio + 1 ) ) - 1 ;
/*
* Short exposure times 71 must be less than frame length to avoid
* sensor crash
*/
third_rot_max_expo = ( sensor - > frame_length / 71 ) * short_expo_ratio ;
/* Take the minimum from all rules */
return min ( min ( first_rot_max_expo , second_rot_max_expo ) ,
third_rot_max_expo ) ;
}
static int vgxy61_update_exposure ( struct vgxy61_dev * sensor , u16 new_expo_long ,
enum vgxy61_hdr_mode hdr )
{
struct i2c_client * client = sensor - > i2c_client ;
u16 new_expo_short = 0 ;
u16 expo_short_max = 0 ;
u16 expo_long_min = VGXY61_MIN_EXPOSURE ;
2022-11-10 13:41:02 +00:00
u16 expo_long_max = 0 ;
2022-10-11 14:30:30 +02:00
/* Compute short exposure according to hdr mode and long exposure */
switch ( hdr ) {
case VGXY61_HDR_LINEAR :
/*
* Take ratio into account for minimal exposures in
* VGXY61_HDR_LINEAR
*/
expo_long_min = VGXY61_MIN_EXPOSURE * VGXY61_HDR_LINEAR_RATIO ;
new_expo_long = max ( expo_long_min , new_expo_long ) ;
expo_long_max =
vgxy61_get_expo_long_max ( sensor ,
VGXY61_HDR_LINEAR_RATIO ) ;
expo_short_max = ( expo_long_max +
( VGXY61_HDR_LINEAR_RATIO / 2 ) ) /
VGXY61_HDR_LINEAR_RATIO ;
new_expo_short = ( new_expo_long +
( VGXY61_HDR_LINEAR_RATIO / 2 ) ) /
VGXY61_HDR_LINEAR_RATIO ;
break ;
case VGXY61_HDR_SUB :
new_expo_long = max ( expo_long_min , new_expo_long ) ;
expo_long_max = vgxy61_get_expo_long_max ( sensor , 1 ) ;
/* Short and long are the same in VGXY61_HDR_SUB */
expo_short_max = expo_long_max ;
new_expo_short = new_expo_long ;
break ;
case VGXY61_NO_HDR :
new_expo_long = max ( expo_long_min , new_expo_long ) ;
/*
* As short expo is 0 here , only the second rule of thumb
* applies , see vgxy61_get_expo_long_max for more
*/
expo_long_max = sensor - > frame_length - VGXY61_EXPOS_ROT_TERM ;
break ;
default :
/* Should never happen */
WARN_ON ( true ) ;
break ;
}
/* If this happens, something is wrong with formulas */
WARN_ON ( expo_long_min > expo_long_max ) ;
if ( new_expo_long > expo_long_max ) {
dev_warn ( & client - > dev , " Exposure %d too high, clamping to %d \n " ,
new_expo_long , expo_long_max ) ;
new_expo_long = expo_long_max ;
new_expo_short = expo_short_max ;
}
sensor - > expo_long = new_expo_long ;
sensor - > expo_short = new_expo_short ;
sensor - > expo_max = expo_long_max ;
sensor - > expo_min = expo_long_min ;
if ( sensor - > streaming )
return vgxy61_apply_exposure ( sensor ) ;
return 0 ;
}
static int vgxy61_apply_framelength ( struct vgxy61_dev * sensor )
{
return vgxy61_write_reg ( sensor , VGXY61_REG_FRAME_LENGTH ,
sensor - > frame_length , NULL ) ;
}
static int vgxy61_update_vblank ( struct vgxy61_dev * sensor , u16 vblank ,
enum vgxy61_hdr_mode hdr )
{
int ret ;
sensor - > vblank_min = vgxy61_get_vblank_min ( sensor , hdr ) ;
sensor - > vblank = max ( sensor - > vblank_min , vblank ) ;
sensor - > frame_length = sensor - > current_mode - > crop . height +
sensor - > vblank ;
/* Update exposure according to vblank */
ret = vgxy61_update_exposure ( sensor , sensor - > expo_long , hdr ) ;
if ( ret )
return ret ;
if ( sensor - > streaming )
return vgxy61_apply_framelength ( sensor ) ;
return 0 ;
}
static int vgxy61_apply_hdr ( struct vgxy61_dev * sensor ,
enum vgxy61_hdr_mode index )
{
static const u8 index2val [ ] = { 0x1 , 0x4 , 0xa } ;
return vgxy61_write_reg ( sensor , VGXY61_REG_HDR_CTRL , index2val [ index ] ,
NULL ) ;
}
static int vgxy61_update_hdr ( struct vgxy61_dev * sensor ,
enum vgxy61_hdr_mode index )
{
int ret ;
/*
* vblank and short exposure change according to HDR mode , do it first
* as it can violate sensors ' rule of thumbs ' and therefore will require
* to change the long exposure .
*/
ret = vgxy61_update_vblank ( sensor , sensor - > vblank , index ) ;
if ( ret )
return ret ;
/* Update strobe mode according to HDR */
ret = vgxy61_update_gpios_strobe_mode ( sensor , index ) ;
if ( ret )
return ret ;
sensor - > hdr = index ;
if ( sensor - > streaming )
return vgxy61_apply_hdr ( sensor , sensor - > hdr ) ;
return 0 ;
}
static int vgxy61_apply_settings ( struct vgxy61_dev * sensor )
{
int ret ;
unsigned int i ;
ret = vgxy61_apply_hdr ( sensor , sensor - > hdr ) ;
if ( ret )
return ret ;
ret = vgxy61_apply_framelength ( sensor ) ;
if ( ret )
return ret ;
ret = vgxy61_apply_exposure ( sensor ) ;
if ( ret )
return ret ;
ret = vgxy61_write_reg ( sensor , VGXY61_REG_ANALOG_GAIN ,
sensor - > analog_gain , NULL ) ;
if ( ret )
return ret ;
ret = vgxy61_apply_digital_gain ( sensor , sensor - > digital_gain ) ;
if ( ret )
return ret ;
ret = vgxy61_write_reg ( sensor , VGXY61_REG_ORIENTATION ,
sensor - > hflip | ( sensor - > vflip < < 1 ) , NULL ) ;
if ( ret )
return ret ;
ret = vgxy61_apply_patgen ( sensor , sensor - > pattern ) ;
if ( ret )
return ret ;
for ( i = 0 ; i < VGXY61_NB_GPIOS ; i + + ) {
ret = vgxy61_apply_gpiox_strobe_mode ( sensor ,
sensor - > strobe_mode , i ) ;
if ( ret )
return ret ;
}
return 0 ;
}
static int vgxy61_stream_enable ( struct vgxy61_dev * sensor )
{
struct i2c_client * client = v4l2_get_subdevdata ( & sensor - > sd ) ;
const struct v4l2_rect * crop = & sensor - > current_mode - > crop ;
int ret = 0 ;
ret = vgxy61_check_bw ( sensor ) ;
if ( ret )
return ret ;
ret = pm_runtime_get_sync ( & client - > dev ) ;
if ( ret < 0 ) {
pm_runtime_put_autosuspend ( & client - > dev ) ;
return ret ;
}
2022-11-10 13:41:02 +00:00
/* pm_runtime_get_sync() can return 1 as a valid return code */
ret = 0 ;
2022-10-11 14:30:30 +02:00
vgxy61_write_reg ( sensor , VGXY61_REG_FORMAT_CTRL ,
get_bpp_by_code ( sensor - > fmt . code ) , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_OIF_ROI0_CTRL ,
get_data_type_by_code ( sensor - > fmt . code ) , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_READOUT_CTRL ,
sensor - > current_mode - > bin_mode , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_ROI0_START_H , crop - > left , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_ROI0_END_H ,
crop - > left + crop - > width - 1 , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_ROI0_START_V , crop - > top , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_ROI0_END_V ,
crop - > top + crop - > height - 1 , & ret ) ;
if ( ret )
goto err_rpm_put ;
ret = vgxy61_apply_settings ( sensor ) ;
if ( ret )
goto err_rpm_put ;
ret = vgxy61_write_reg ( sensor , VGXY61_REG_STREAMING ,
VGXY61_STREAMING_REQ_START , NULL ) ;
if ( ret )
goto err_rpm_put ;
ret = vgxy61_poll_reg ( sensor , VGXY61_REG_STREAMING ,
VGXY61_STREAMING_NO_REQ , VGXY61_TIMEOUT_MS ) ;
if ( ret )
goto err_rpm_put ;
ret = vgxy61_wait_state ( sensor , VGXY61_SYSTEM_FSM_STREAMING ,
VGXY61_TIMEOUT_MS ) ;
if ( ret )
goto err_rpm_put ;
/* vflip and hflip cannot change during streaming */
__v4l2_ctrl_grab ( sensor - > vflip_ctrl , true ) ;
__v4l2_ctrl_grab ( sensor - > hflip_ctrl , true ) ;
return 0 ;
err_rpm_put :
pm_runtime_put ( & client - > dev ) ;
return ret ;
}
static int vgxy61_stream_disable ( struct vgxy61_dev * sensor )
{
struct i2c_client * client = v4l2_get_subdevdata ( & sensor - > sd ) ;
int ret ;
ret = vgxy61_write_reg ( sensor , VGXY61_REG_STREAMING ,
VGXY61_STREAMING_REQ_STOP , NULL ) ;
if ( ret )
goto err_str_dis ;
ret = vgxy61_poll_reg ( sensor , VGXY61_REG_STREAMING ,
VGXY61_STREAMING_NO_REQ , 2000 ) ;
if ( ret )
goto err_str_dis ;
ret = vgxy61_wait_state ( sensor , VGXY61_SYSTEM_FSM_SW_STBY ,
VGXY61_TIMEOUT_MS ) ;
if ( ret )
goto err_str_dis ;
__v4l2_ctrl_grab ( sensor - > vflip_ctrl , false ) ;
__v4l2_ctrl_grab ( sensor - > hflip_ctrl , false ) ;
err_str_dis :
if ( ret )
WARN ( 1 , " Can't disable stream " ) ;
pm_runtime_put ( & client - > dev ) ;
return ret ;
}
static int vgxy61_s_stream ( struct v4l2_subdev * sd , int enable )
{
struct vgxy61_dev * sensor = to_vgxy61_dev ( sd ) ;
int ret = 0 ;
mutex_lock ( & sensor - > lock ) ;
ret = enable ? vgxy61_stream_enable ( sensor ) :
vgxy61_stream_disable ( sensor ) ;
if ( ! ret )
sensor - > streaming = enable ;
mutex_unlock ( & sensor - > lock ) ;
return ret ;
}
static int vgxy61_set_fmt ( struct v4l2_subdev * sd ,
struct v4l2_subdev_state * sd_state ,
struct v4l2_subdev_format * format )
{
struct vgxy61_dev * sensor = to_vgxy61_dev ( sd ) ;
const struct vgxy61_mode_info * new_mode ;
struct v4l2_mbus_framefmt * fmt ;
int ret ;
mutex_lock ( & sensor - > lock ) ;
if ( sensor - > streaming ) {
ret = - EBUSY ;
goto out ;
}
ret = vgxy61_try_fmt_internal ( sd , & format - > format , & new_mode ) ;
if ( ret )
goto out ;
if ( format - > which = = V4L2_SUBDEV_FORMAT_TRY ) {
fmt = v4l2_subdev_get_try_format ( sd , sd_state , 0 ) ;
* fmt = format - > format ;
} else if ( sensor - > current_mode ! = new_mode | |
sensor - > fmt . code ! = format - > format . code ) {
fmt = & sensor - > fmt ;
* fmt = format - > format ;
sensor - > current_mode = new_mode ;
/* Reset vblank and framelength to default */
ret = vgxy61_update_vblank ( sensor ,
VGXY61_FRAME_LENGTH_DEF -
new_mode - > crop . height ,
sensor - > hdr ) ;
/* Update controls to reflect new mode */
__v4l2_ctrl_s_ctrl_int64 ( sensor - > pixel_rate_ctrl ,
get_pixel_rate ( sensor ) ) ;
__v4l2_ctrl_modify_range ( sensor - > vblank_ctrl ,
sensor - > vblank_min ,
0xffff - new_mode - > crop . height ,
1 , sensor - > vblank ) ;
__v4l2_ctrl_s_ctrl ( sensor - > vblank_ctrl , sensor - > vblank ) ;
__v4l2_ctrl_modify_range ( sensor - > expo_ctrl , sensor - > expo_min ,
sensor - > expo_max , 1 ,
sensor - > expo_long ) ;
}
out :
mutex_unlock ( & sensor - > lock ) ;
return ret ;
}
static int vgxy61_init_cfg ( struct v4l2_subdev * sd ,
struct v4l2_subdev_state * sd_state )
{
struct vgxy61_dev * sensor = to_vgxy61_dev ( sd ) ;
struct v4l2_subdev_format fmt = { 0 } ;
sensor - > current_mode = sensor - > default_mode ;
vgxy61_fill_framefmt ( sensor , sensor - > current_mode , & fmt . format ,
VGXY61_MEDIA_BUS_FMT_DEF ) ;
return vgxy61_set_fmt ( sd , sd_state , & fmt ) ;
}
static int vgxy61_s_ctrl ( struct v4l2_ctrl * ctrl )
{
struct v4l2_subdev * sd = ctrl_to_sd ( ctrl ) ;
struct vgxy61_dev * sensor = to_vgxy61_dev ( sd ) ;
const struct vgxy61_mode_info * cur_mode = sensor - > current_mode ;
int ret ;
switch ( ctrl - > id ) {
case V4L2_CID_EXPOSURE :
ret = vgxy61_update_exposure ( sensor , ctrl - > val , sensor - > hdr ) ;
ctrl - > val = sensor - > expo_long ;
break ;
case V4L2_CID_ANALOGUE_GAIN :
ret = vgxy61_update_analog_gain ( sensor , ctrl - > val ) ;
break ;
case V4L2_CID_DIGITAL_GAIN :
ret = vgxy61_update_digital_gain ( sensor , ctrl - > val ) ;
break ;
case V4L2_CID_VFLIP :
case V4L2_CID_HFLIP :
if ( sensor - > streaming ) {
ret = - EBUSY ;
break ;
}
if ( ctrl - > id = = V4L2_CID_VFLIP )
sensor - > vflip = ctrl - > val ;
if ( ctrl - > id = = V4L2_CID_HFLIP )
sensor - > hflip = ctrl - > val ;
ret = 0 ;
break ;
case V4L2_CID_TEST_PATTERN :
ret = vgxy61_update_patgen ( sensor , ctrl - > val ) ;
break ;
case V4L2_CID_HDR_SENSOR_MODE :
ret = vgxy61_update_hdr ( sensor , ctrl - > val ) ;
/* Update vblank and exposure controls to match new hdr */
__v4l2_ctrl_modify_range ( sensor - > vblank_ctrl ,
sensor - > vblank_min ,
0xffff - cur_mode - > crop . height ,
1 , sensor - > vblank ) ;
__v4l2_ctrl_modify_range ( sensor - > expo_ctrl , sensor - > expo_min ,
sensor - > expo_max , 1 ,
sensor - > expo_long ) ;
break ;
case V4L2_CID_VBLANK :
ret = vgxy61_update_vblank ( sensor , ctrl - > val , sensor - > hdr ) ;
/* Update exposure control to match new vblank */
__v4l2_ctrl_modify_range ( sensor - > expo_ctrl , sensor - > expo_min ,
sensor - > expo_max , 1 ,
sensor - > expo_long ) ;
break ;
default :
ret = - EINVAL ;
break ;
}
return ret ;
}
static const struct v4l2_ctrl_ops vgxy61_ctrl_ops = {
. s_ctrl = vgxy61_s_ctrl ,
} ;
static int vgxy61_init_controls ( struct vgxy61_dev * sensor )
{
const struct v4l2_ctrl_ops * ops = & vgxy61_ctrl_ops ;
struct v4l2_ctrl_handler * hdl = & sensor - > ctrl_handler ;
const struct vgxy61_mode_info * cur_mode = sensor - > current_mode ;
struct v4l2_ctrl * ctrl ;
int ret ;
v4l2_ctrl_handler_init ( hdl , 16 ) ;
/* We can use our own mutex for the ctrl lock */
hdl - > lock = & sensor - > lock ;
v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_ANALOGUE_GAIN , 0 , 0x1c , 1 ,
sensor - > analog_gain ) ;
v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_DIGITAL_GAIN , 0 , 0xfff , 1 ,
sensor - > digital_gain ) ;
v4l2_ctrl_new_std_menu_items ( hdl , ops , V4L2_CID_TEST_PATTERN ,
ARRAY_SIZE ( vgxy61_test_pattern_menu ) - 1 ,
0 , 0 , vgxy61_test_pattern_menu ) ;
ctrl = v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_HBLANK , 0 ,
sensor - > line_length , 1 ,
sensor - > line_length - cur_mode - > width ) ;
if ( ctrl )
ctrl - > flags | = V4L2_CTRL_FLAG_READ_ONLY ;
ctrl = v4l2_ctrl_new_int_menu ( hdl , ops , V4L2_CID_LINK_FREQ ,
ARRAY_SIZE ( link_freq ) - 1 , 0 , link_freq ) ;
if ( ctrl )
ctrl - > flags | = V4L2_CTRL_FLAG_READ_ONLY ;
v4l2_ctrl_new_std_menu_items ( hdl , ops , V4L2_CID_HDR_SENSOR_MODE ,
ARRAY_SIZE ( vgxy61_hdr_mode_menu ) - 1 , 0 ,
VGXY61_NO_HDR , vgxy61_hdr_mode_menu ) ;
/*
* Keep a pointer to these controls as we need to update them when
* setting the format
*/
sensor - > pixel_rate_ctrl = v4l2_ctrl_new_std ( hdl , ops ,
V4L2_CID_PIXEL_RATE , 1 ,
INT_MAX , 1 ,
get_pixel_rate ( sensor ) ) ;
if ( sensor - > pixel_rate_ctrl )
sensor - > pixel_rate_ctrl - > flags | = V4L2_CTRL_FLAG_READ_ONLY ;
sensor - > expo_ctrl = v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_EXPOSURE ,
sensor - > expo_min ,
sensor - > expo_max , 1 ,
sensor - > expo_long ) ;
sensor - > vblank_ctrl = v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_VBLANK ,
sensor - > vblank_min ,
0xffff - cur_mode - > crop . height ,
1 , sensor - > vblank ) ;
sensor - > vflip_ctrl = v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_VFLIP ,
0 , 1 , 1 , sensor - > vflip ) ;
sensor - > hflip_ctrl = v4l2_ctrl_new_std ( hdl , ops , V4L2_CID_HFLIP ,
0 , 1 , 1 , sensor - > hflip ) ;
if ( hdl - > error ) {
ret = hdl - > error ;
goto free_ctrls ;
}
sensor - > sd . ctrl_handler = hdl ;
return 0 ;
free_ctrls :
v4l2_ctrl_handler_free ( hdl ) ;
return ret ;
}
static const struct v4l2_subdev_video_ops vgxy61_video_ops = {
. s_stream = vgxy61_s_stream ,
} ;
static const struct v4l2_subdev_pad_ops vgxy61_pad_ops = {
. init_cfg = vgxy61_init_cfg ,
. enum_mbus_code = vgxy61_enum_mbus_code ,
. get_fmt = vgxy61_get_fmt ,
. set_fmt = vgxy61_set_fmt ,
. get_selection = vgxy61_get_selection ,
. enum_frame_size = vgxy61_enum_frame_size ,
} ;
static const struct v4l2_subdev_ops vgxy61_subdev_ops = {
. video = & vgxy61_video_ops ,
. pad = & vgxy61_pad_ops ,
} ;
static const struct media_entity_operations vgxy61_subdev_entity_ops = {
. link_validate = v4l2_subdev_link_validate ,
} ;
static int vgxy61_tx_from_ep ( struct vgxy61_dev * sensor ,
struct fwnode_handle * handle )
{
struct v4l2_fwnode_endpoint ep = { . bus_type = V4L2_MBUS_CSI2_DPHY } ;
struct i2c_client * client = sensor - > i2c_client ;
u32 log2phy [ VGXY61_NB_POLARITIES ] = { ~ 0 , ~ 0 , ~ 0 , ~ 0 , ~ 0 } ;
u32 phy2log [ VGXY61_NB_POLARITIES ] = { ~ 0 , ~ 0 , ~ 0 , ~ 0 , ~ 0 } ;
int polarities [ VGXY61_NB_POLARITIES ] = { 0 , 0 , 0 , 0 , 0 } ;
int l_nb ;
unsigned int p , l , i ;
int ret ;
ret = v4l2_fwnode_endpoint_alloc_parse ( handle , & ep ) ;
if ( ret )
return - EINVAL ;
l_nb = ep . bus . mipi_csi2 . num_data_lanes ;
if ( l_nb ! = 1 & & l_nb ! = 2 & & l_nb ! = 4 ) {
dev_err ( & client - > dev , " invalid data lane number %d \n " , l_nb ) ;
goto error_ep ;
}
/* Build log2phy, phy2log and polarities from ep info */
log2phy [ 0 ] = ep . bus . mipi_csi2 . clock_lane ;
phy2log [ log2phy [ 0 ] ] = 0 ;
for ( l = 1 ; l < l_nb + 1 ; l + + ) {
log2phy [ l ] = ep . bus . mipi_csi2 . data_lanes [ l - 1 ] ;
phy2log [ log2phy [ l ] ] = l ;
}
/*
* Then fill remaining slots for every physical slot to have something
* valid for hardware stuff .
*/
for ( p = 0 ; p < VGXY61_NB_POLARITIES ; p + + ) {
if ( phy2log [ p ] ! = ~ 0 )
continue ;
phy2log [ p ] = l ;
log2phy [ l ] = p ;
l + + ;
}
for ( l = 0 ; l < l_nb + 1 ; l + + )
polarities [ l ] = ep . bus . mipi_csi2 . lane_polarities [ l ] ;
if ( log2phy [ 0 ] ! = 0 ) {
dev_err ( & client - > dev , " clk lane must be map to physical lane 0 \n " ) ;
goto error_ep ;
}
sensor - > oif_ctrl = ( polarities [ 4 ] < < 15 ) + ( ( phy2log [ 4 ] - 1 ) < < 13 ) +
( polarities [ 3 ] < < 12 ) + ( ( phy2log [ 3 ] - 1 ) < < 10 ) +
( polarities [ 2 ] < < 9 ) + ( ( phy2log [ 2 ] - 1 ) < < 7 ) +
( polarities [ 1 ] < < 6 ) + ( ( phy2log [ 1 ] - 1 ) < < 4 ) +
( polarities [ 0 ] < < 3 ) +
l_nb ;
sensor - > nb_of_lane = l_nb ;
dev_dbg ( & client - > dev , " tx uses %d lanes " , l_nb ) ;
for ( i = 0 ; i < 5 ; i + + ) {
dev_dbg ( & client - > dev , " log2phy[%d] = %d \n " , i , log2phy [ i ] ) ;
dev_dbg ( & client - > dev , " phy2log[%d] = %d \n " , i , phy2log [ i ] ) ;
dev_dbg ( & client - > dev , " polarity[%d] = %d \n " , i , polarities [ i ] ) ;
}
dev_dbg ( & client - > dev , " oif_ctrl = 0x%04x \n " , sensor - > oif_ctrl ) ;
v4l2_fwnode_endpoint_free ( & ep ) ;
return 0 ;
error_ep :
v4l2_fwnode_endpoint_free ( & ep ) ;
return - EINVAL ;
}
static int vgxy61_configure ( struct vgxy61_dev * sensor )
{
u32 sensor_freq ;
u8 prediv , mult ;
2022-11-10 13:41:02 +00:00
int line_length ;
2022-10-11 14:30:30 +02:00
int ret = 0 ;
compute_pll_parameters_by_freq ( sensor - > clk_freq , & prediv , & mult ) ;
sensor_freq = ( mult * sensor - > clk_freq ) / prediv ;
/* Frequency to data rate is 1:1 ratio for MIPI */
sensor - > data_rate_in_mbps = sensor_freq ;
/* Video timing ISP path (pixel clock) requires 804/5 mhz = 160 mhz */
sensor - > pclk = sensor_freq / 5 ;
line_length = vgxy61_read_reg ( sensor , VGXY61_REG_LINE_LENGTH ) ;
if ( line_length < 0 )
return line_length ;
sensor - > line_length = line_length ;
vgxy61_write_reg ( sensor , VGXY61_REG_EXT_CLOCK , sensor - > clk_freq , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_CLK_PLL_PREDIV , prediv , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_CLK_SYS_PLL_MULT , mult , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_OIF_CTRL , sensor - > oif_ctrl , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_FRAME_CONTENT_CTRL , 0 , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_BYPASS_CTRL , 4 , & ret ) ;
if ( ret )
return ret ;
vgxy61_update_gpios_strobe_polarity ( sensor , sensor - > gpios_polarity ) ;
/* Set pattern generator solid to middle value */
vgxy61_write_reg ( sensor , VGXY61_REG_PATGEN_LONG_DATA_GR , 0x800 , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_PATGEN_LONG_DATA_R , 0x800 , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_PATGEN_LONG_DATA_B , 0x800 , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_PATGEN_LONG_DATA_GB , 0x800 , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_PATGEN_SHORT_DATA_GR , 0x800 , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_PATGEN_SHORT_DATA_R , 0x800 , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_PATGEN_SHORT_DATA_B , 0x800 , & ret ) ;
vgxy61_write_reg ( sensor , VGXY61_REG_PATGEN_SHORT_DATA_GB , 0x800 , & ret ) ;
if ( ret )
return ret ;
return 0 ;
}
static int vgxy61_patch ( struct vgxy61_dev * sensor )
{
struct i2c_client * client = sensor - > i2c_client ;
2022-11-10 13:41:02 +00:00
int patch , ret ;
2022-10-11 14:30:30 +02:00
ret = vgxy61_write_array ( sensor , VGXY61_REG_FWPATCH_START_ADDR ,
sizeof ( patch_array ) , patch_array ) ;
if ( ret )
return ret ;
ret = vgxy61_write_reg ( sensor , VGXY61_REG_STBY , 0x10 , NULL ) ;
if ( ret )
return ret ;
ret = vgxy61_poll_reg ( sensor , VGXY61_REG_STBY , 0 , VGXY61_TIMEOUT_MS ) ;
if ( ret )
return ret ;
patch = vgxy61_read_reg ( sensor , VGXY61_REG_FWPATCH_REVISION ) ;
if ( patch < 0 )
return patch ;
if ( patch ! = ( VGXY61_FWPATCH_REVISION_MAJOR < < 12 ) +
( VGXY61_FWPATCH_REVISION_MINOR < < 8 ) +
VGXY61_FWPATCH_REVISION_MICRO ) {
dev_err ( & client - > dev , " bad patch version expected %d.%d.%d got %d.%d.%d \n " ,
VGXY61_FWPATCH_REVISION_MAJOR ,
VGXY61_FWPATCH_REVISION_MINOR ,
VGXY61_FWPATCH_REVISION_MICRO ,
patch > > 12 , ( patch > > 8 ) & 0x0f , patch & 0xff ) ;
return - ENODEV ;
}
dev_dbg ( & client - > dev , " patch %d.%d.%d applied \n " ,
patch > > 12 , ( patch > > 8 ) & 0x0f , patch & 0xff ) ;
return 0 ;
}
static int vgxy61_detect_cut_version ( struct vgxy61_dev * sensor )
{
struct i2c_client * client = sensor - > i2c_client ;
2022-11-10 13:41:02 +00:00
int device_rev ;
2022-10-11 14:30:30 +02:00
device_rev = vgxy61_read_reg ( sensor , VGXY61_REG_REVISION ) ;
if ( device_rev < 0 )
return device_rev ;
switch ( device_rev > > 8 ) {
case 0xA :
dev_dbg ( & client - > dev , " Cut1 detected \n " ) ;
dev_err ( & client - > dev , " Cut1 not supported by this driver \n " ) ;
return - ENODEV ;
case 0xB :
dev_dbg ( & client - > dev , " Cut2 detected \n " ) ;
return 0 ;
case 0xC :
dev_dbg ( & client - > dev , " Cut3 detected \n " ) ;
return 0 ;
default :
dev_err ( & client - > dev , " Unable to detect cut version \n " ) ;
return - ENODEV ;
}
}
static int vgxy61_detect ( struct vgxy61_dev * sensor )
{
struct i2c_client * client = sensor - > i2c_client ;
2022-11-10 13:41:02 +00:00
int id = 0 ;
int ret , st ;
2022-10-11 14:30:30 +02:00
id = vgxy61_read_reg ( sensor , VGXY61_REG_MODEL_ID ) ;
if ( id < 0 )
return id ;
if ( id ! = VG5661_MODEL_ID & & id ! = VG5761_MODEL_ID ) {
dev_warn ( & client - > dev , " Unsupported sensor id %x \n " , id ) ;
return - ENODEV ;
}
dev_dbg ( & client - > dev , " detected sensor id = 0x%04x \n " , id ) ;
sensor - > id = id ;
ret = vgxy61_wait_state ( sensor , VGXY61_SYSTEM_FSM_SW_STBY ,
VGXY61_TIMEOUT_MS ) ;
if ( ret )
return ret ;
st = vgxy61_read_reg ( sensor , VGXY61_REG_NVM ) ;
if ( st < 0 )
return st ;
if ( st ! = VGXY61_NVM_OK )
dev_warn ( & client - > dev , " Bad nvm state got %d \n " , st ) ;
ret = vgxy61_detect_cut_version ( sensor ) ;
if ( ret )
return ret ;
return 0 ;
}
/* Power/clock management functions */
static int vgxy61_power_on ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
struct vgxy61_dev * sensor = to_vgxy61_dev ( sd ) ;
int ret ;
2022-11-07 15:00:00 +00:00
ret = regulator_bulk_enable ( ARRAY_SIZE ( vgxy61_supply_name ) ,
sensor - > supplies ) ;
if ( ret ) {
dev_err ( & client - > dev , " failed to enable regulators %d \n " , ret ) ;
return ret ;
}
2022-10-11 14:30:30 +02:00
ret = clk_prepare_enable ( sensor - > xclk ) ;
if ( ret ) {
dev_err ( & client - > dev , " failed to enable clock %d \n " , ret ) ;
goto disable_bulk ;
}
if ( sensor - > reset_gpio ) {
ret = vgxy61_apply_reset ( sensor ) ;
if ( ret ) {
dev_err ( & client - > dev , " sensor reset failed %d \n " , ret ) ;
goto disable_clock ;
}
}
ret = vgxy61_patch ( sensor ) ;
if ( ret ) {
dev_err ( & client - > dev , " sensor patch failed %d \n " , ret ) ;
goto disable_clock ;
}
ret = vgxy61_configure ( sensor ) ;
if ( ret ) {
dev_err ( & client - > dev , " sensor configuration failed %d \n " , ret ) ;
goto disable_clock ;
}
return 0 ;
disable_clock :
clk_disable_unprepare ( sensor - > xclk ) ;
disable_bulk :
regulator_bulk_disable ( ARRAY_SIZE ( vgxy61_supply_name ) ,
sensor - > supplies ) ;
return ret ;
}
static int vgxy61_power_off ( struct device * dev )
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
struct vgxy61_dev * sensor = to_vgxy61_dev ( sd ) ;
clk_disable_unprepare ( sensor - > xclk ) ;
regulator_bulk_disable ( ARRAY_SIZE ( vgxy61_supply_name ) ,
sensor - > supplies ) ;
return 0 ;
}
static void vgxy61_fill_sensor_param ( struct vgxy61_dev * sensor )
{
if ( sensor - > id = = VG5761_MODEL_ID ) {
sensor - > sensor_width = VGX761_WIDTH ;
sensor - > sensor_height = VGX761_HEIGHT ;
sensor - > sensor_modes = vgx761_mode_data ;
sensor - > sensor_modes_nb = ARRAY_SIZE ( vgx761_mode_data ) ;
sensor - > default_mode = & vgx761_mode_data [ VGX761_DEFAULT_MODE ] ;
sensor - > rot_term = VGX761_SHORT_ROT_TERM ;
} else if ( sensor - > id = = VG5661_MODEL_ID ) {
sensor - > sensor_width = VGX661_WIDTH ;
sensor - > sensor_height = VGX661_HEIGHT ;
sensor - > sensor_modes = vgx661_mode_data ;
sensor - > sensor_modes_nb = ARRAY_SIZE ( vgx661_mode_data ) ;
sensor - > default_mode = & vgx661_mode_data [ VGX661_DEFAULT_MODE ] ;
sensor - > rot_term = VGX661_SHORT_ROT_TERM ;
} else {
/* Should never happen */
WARN_ON ( true ) ;
}
sensor - > current_mode = sensor - > default_mode ;
}
static int vgxy61_probe ( struct i2c_client * client )
{
struct device * dev = & client - > dev ;
struct fwnode_handle * handle ;
struct vgxy61_dev * sensor ;
int ret ;
sensor = devm_kzalloc ( dev , sizeof ( * sensor ) , GFP_KERNEL ) ;
if ( ! sensor )
return - ENOMEM ;
sensor - > i2c_client = client ;
sensor - > streaming = false ;
sensor - > hdr = VGXY61_NO_HDR ;
sensor - > expo_long = 200 ;
sensor - > expo_short = 0 ;
sensor - > hflip = false ;
sensor - > vflip = false ;
sensor - > analog_gain = 0 ;
sensor - > digital_gain = 256 ;
handle = fwnode_graph_get_endpoint_by_id ( dev_fwnode ( dev ) , 0 , 0 , 0 ) ;
if ( ! handle ) {
dev_err ( dev , " handle node not found \n " ) ;
return - EINVAL ;
}
ret = vgxy61_tx_from_ep ( sensor , handle ) ;
fwnode_handle_put ( handle ) ;
if ( ret ) {
dev_err ( dev , " Failed to parse handle %d \n " , ret ) ;
return ret ;
}
sensor - > xclk = devm_clk_get ( dev , NULL ) ;
if ( IS_ERR ( sensor - > xclk ) ) {
dev_err ( dev , " failed to get xclk \n " ) ;
return PTR_ERR ( sensor - > xclk ) ;
}
sensor - > clk_freq = clk_get_rate ( sensor - > xclk ) ;
if ( sensor - > clk_freq < 6 * HZ_PER_MHZ | |
sensor - > clk_freq > 27 * HZ_PER_MHZ ) {
dev_err ( dev , " Only 6Mhz-27Mhz clock range supported. provide %lu MHz \n " ,
sensor - > clk_freq / HZ_PER_MHZ ) ;
return - EINVAL ;
}
sensor - > gpios_polarity =
device_property_read_bool ( dev , " st,strobe-gpios-polarity " ) ;
v4l2_i2c_subdev_init ( & sensor - > sd , client , & vgxy61_subdev_ops ) ;
sensor - > sd . flags | = V4L2_SUBDEV_FL_HAS_DEVNODE ;
sensor - > pad . flags = MEDIA_PAD_FL_SOURCE ;
sensor - > sd . entity . ops = & vgxy61_subdev_entity_ops ;
sensor - > sd . entity . function = MEDIA_ENT_F_CAM_SENSOR ;
sensor - > reset_gpio = devm_gpiod_get_optional ( dev , " reset " ,
GPIOD_OUT_HIGH ) ;
ret = vgxy61_get_regulators ( sensor ) ;
if ( ret ) {
dev_err ( & client - > dev , " failed to get regulators %d \n " , ret ) ;
return ret ;
}
ret = vgxy61_power_on ( dev ) ;
if ( ret )
return ret ;
ret = vgxy61_detect ( sensor ) ;
if ( ret ) {
dev_err ( & client - > dev , " sensor detect failed %d \n " , ret ) ;
return ret ;
}
vgxy61_fill_sensor_param ( sensor ) ;
vgxy61_fill_framefmt ( sensor , sensor - > current_mode , & sensor - > fmt ,
VGXY61_MEDIA_BUS_FMT_DEF ) ;
ret = vgxy61_update_hdr ( sensor , sensor - > hdr ) ;
if ( ret )
return ret ;
mutex_init ( & sensor - > lock ) ;
ret = vgxy61_init_controls ( sensor ) ;
if ( ret ) {
dev_err ( & client - > dev , " controls initialization failed %d \n " ,
ret ) ;
goto error_power_off ;
}
ret = media_entity_pads_init ( & sensor - > sd . entity , 1 , & sensor - > pad ) ;
if ( ret ) {
dev_err ( & client - > dev , " pads init failed %d \n " , ret ) ;
goto error_handler_free ;
}
/* Enable runtime PM and turn off the device */
pm_runtime_set_active ( dev ) ;
pm_runtime_enable ( dev ) ;
pm_runtime_idle ( dev ) ;
ret = v4l2_async_register_subdev ( & sensor - > sd ) ;
if ( ret ) {
dev_err ( & client - > dev , " async subdev register failed %d \n " , ret ) ;
goto error_pm_runtime ;
}
pm_runtime_set_autosuspend_delay ( & client - > dev , 1000 ) ;
pm_runtime_use_autosuspend ( & client - > dev ) ;
dev_dbg ( & client - > dev , " vgxy61 probe successfully \n " ) ;
return 0 ;
error_pm_runtime :
pm_runtime_disable ( & client - > dev ) ;
pm_runtime_set_suspended ( & client - > dev ) ;
media_entity_cleanup ( & sensor - > sd . entity ) ;
error_handler_free :
v4l2_ctrl_handler_free ( sensor - > sd . ctrl_handler ) ;
mutex_destroy ( & sensor - > lock ) ;
error_power_off :
vgxy61_power_off ( dev ) ;
return ret ;
}
static void vgxy61_remove ( struct i2c_client * client )
{
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
struct vgxy61_dev * sensor = to_vgxy61_dev ( sd ) ;
v4l2_async_unregister_subdev ( & sensor - > sd ) ;
mutex_destroy ( & sensor - > lock ) ;
media_entity_cleanup ( & sensor - > sd . entity ) ;
pm_runtime_disable ( & client - > dev ) ;
if ( ! pm_runtime_status_suspended ( & client - > dev ) )
vgxy61_power_off ( & client - > dev ) ;
pm_runtime_set_suspended ( & client - > dev ) ;
}
static const struct of_device_id vgxy61_dt_ids [ ] = {
{ . compatible = " st,st-vgxy61 " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , vgxy61_dt_ids ) ;
static const struct dev_pm_ops vgxy61_pm_ops = {
SET_RUNTIME_PM_OPS ( vgxy61_power_off , vgxy61_power_on , NULL )
} ;
static struct i2c_driver vgxy61_i2c_driver = {
. driver = {
. name = " st-vgxy61 " ,
. of_match_table = vgxy61_dt_ids ,
. pm = & vgxy61_pm_ops ,
} ,
. probe_new = vgxy61_probe ,
. remove = vgxy61_remove ,
} ;
module_i2c_driver ( vgxy61_i2c_driver ) ;
MODULE_AUTHOR ( " Benjamin Mugnier <benjamin.mugnier@foss.st.com> " ) ;
MODULE_AUTHOR ( " Mickael Guene <mickael.guene@st.com> " ) ;
MODULE_AUTHOR ( " Sylvain Petinot <sylvain.petinot@foss.st.com> " ) ;
MODULE_DESCRIPTION ( " VGXY61 camera subdev driver " ) ;
MODULE_LICENSE ( " GPL " ) ;