diff --git a/Makefile b/Makefile index ce3af8288681..9b33bf1c736e 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,12 @@ obj-y += drivers/media/pci/intel/ export CONFIG_VIDEO_OV8856 = m obj-y += drivers/media/i2c/ +export CONFIG_VIDEO_GC5035 = m +obj-y += drivers/media/i2c/ + +export CONFIG_VIDEO_S5K3L6 = m +obj-y += drivers/media/i2c/ + KERNELRELEASE ?= $(shell uname -r) KERNEL_SRC ?= /lib/modules/$(KERNELRELEASE)/build MODSRC := $(shell pwd) diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 0de9ce9d7d27..a6793f46636e 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -1,4 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # Copyright (c) 2021 Intel Corporation. -obj-$(CONFIG_VIDEO_OV8856) += ov8856.o +CONFIG_MODULE_SIG=n +obj-$(CONFIG_VIDEO_GC5035) += gc5035.o +obj-$(CONFIG_VIDEO_S5K3L6) += s5k3l6.o +obj-$(CONFIG_VIDEO_DW9714) += dw9714.o diff --git a/drivers/media/i2c/gc5035.c b/drivers/media/i2c/gc5035.c new file mode 100644 index 000000000000..d500a706a006 --- /dev/null +++ b/drivers/media/i2c/gc5035.c @@ -0,0 +1,2125 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020 Bitland Inc. +// Copyright 2020 Google LLC.. +// Copyright (c) 2022 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* External clock frequency supported by the driver */ +#define GC5035_MCLK_RATE 24000000UL +/* Number of lanes supported by this driver */ +#define GC5035_DATA_LANES 2 +/* Bits per sample of sensor output */ +#define GC5035_BITS_PER_SAMPLE 10 + +#define MIPI_FREQ 438000000LL + +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define GC5035_PIXEL_RATE (MIPI_FREQ * 2LL * 2LL / 10) + +/* System registers (accessible regardless of the page. */ + +/* Chip ID */ +#define GC5035_REG_CHIP_ID_H 0xf0 +#define GC5035_REG_CHIP_ID_L 0xf1 +#define GC5035_CHIP_ID 0x5035 +#define GC5035_ID(_msb, _lsb) ((_msb) << 8 | (_lsb)) + +/* Register page selection register */ +#define GC5035_PAGE_REG 0xfe + +/* Page 0 registers */ + +/* Exposure control */ +#define GC5035_REG_EXPOSURE_H 0x03 +#define GC5035_REG_EXPOSURE_L 0x04 +#define GC5035_EXPOSURE_H_MASK 0x3f +#define GC5035_EXPOSURE_MIN 4 +#define GC5035_EXPOSURE_STEP 1 + +/* Analog gain control */ +#define GC5035_REG_ANALOG_GAIN 0xb6 +#define GC5035_ANALOG_GAIN_MIN 0 +#define GC5035_ANALOG_GAIN_MAX 31 +#define GC5035_ANALOG_GAIN_STEP 1 +#define GC5035_ANALOG_GAIN_DEFAULT GC5035_ANALOG_GAIN_MIN + +/* Digital gain control */ +#define GC5035_REG_DIGI_GAIN_H 0xb1 +#define GC5035_REG_DIGI_GAIN_L 0xb2 +#define GC5035_DGAIN_H_MASK 0x0f +#define GC5035_DGAIN_L_MASK 0xfc +#define GC5035_DGAIN_L_SHIFT 2 +#define GC5035_DIGI_GAIN_MIN 0 +#define GC5035_DIGI_GAIN_MAX 1023 +#define GC5035_DIGI_GAIN_STEP 1 +#define GC5035_DIGI_GAIN_DEFAULT GC5035_DIGI_GAIN_MAX + +/* Vblank control */ +#define GC5035_REG_VTS_H 0x41 +#define GC5035_REG_VTS_L 0x42 +#define GC5035_VTS_H_MASK 0x3f +#define GC5035_VTS_MAX 16383 +#define GC5035_EXPOSURE_MARGIN 16 + +#define GC5035_REG_CTRL_MODE 0x3e +#define GC5035_MODE_SW_STANDBY 0x01 +#define GC5035_MODE_STREAMING 0x91 + +/* Page 1 registers */ + +/* Test pattern control */ +#define GC5035_REG_TEST_PATTERN 0x8c +#define GC5035_TEST_PATTERN_ENABLE 0x11 +#define GC5035_TEST_PATTERN_DISABLE 0x10 + +/* Page 2 registers */ + +/* OTP access registers */ +#define GC5035_REG_OTP_MODE 0xf3 +#define GC5035_OTP_PRE_READ 0x20 +#define GC5035_OTP_READ_MODE 0x12 +#define GC5035_OTP_READ_DONE 0x00 +#define GC5035_REG_OTP_DATA 0x6c +#define GC5035_REG_OTP_ACCESS_ADDR_H 0x69 +#define GC5035_REG_OTP_ACCESS_ADDR_L 0x6a +#define GC5035_OTP_ACCESS_ADDR_H_MASK 0x1f +#define GC5035_OTP_ADDR_MASK 0x1fff +#define GC5035_OTP_ADDR_SHIFT 3 +#define GC5035_REG_DD_TOTALNUM_H 0x01 +#define GC5035_REG_DD_TOTALNUM_L 0x02 +#define GC5035_DD_TOTALNUM_H_MASK 0x07 +#define GC5035_REG_DD_LOAD_STATUS 0x06 +#define GC5035_OTP_BIT_LOAD BIT(0) + +/* OTP-related definitions */ + +#define GC5035_OTP_ID_SIZE 9 +#define GC5035_OTP_ID_DATA_OFFSET 0x0020 +#define GC5035_OTP_DATA_LENGTH 1024 + +/* OTP DPC parameters */ +#define GC5035_OTP_DPC_FLAG_OFFSET 0x0068 +#define GC5035_OTP_DPC_FLAG_MASK 0x03 +#define GC5035_OTP_FLAG_EMPTY 0x00 +#define GC5035_OTP_FLAG_VALID 0x01 +#define GC5035_OTP_DPC_TOTAL_NUMBER_OFFSET 0x0070 +#define GC5035_OTP_DPC_ERROR_NUMBER_OFFSET 0x0078 + +/* OTP register parameters */ +#define GC5035_OTP_REG_FLAG_OFFSET 0x0880 +#define GC5035_OTP_REG_DATA_OFFSET 0x0888 +#define GC5035_OTP_REG_ADDR_OFFSET 1 +#define GC5035_OTP_REG_VAL_OFFSET 2 +#define GC5035_OTP_PAGE_FLAG_OFFSET 3 +#define GC5035_OTP_PER_PAGE_SIZE 4 +#define GC5035_OTP_REG_PAGE_MASK 0x07 +#define GC5035_OTP_REG_MAX_GROUP 5 +#define GC5035_OTP_REG_BYTE_PER_GROUP 5 +#define GC5035_OTP_REG_PER_GROUP 2 +#define GC5035_OTP_REG_BYTE_PER_REG 2 +#define GC5035_OTP_REG_DATA_SIZE 25 +#define GC5035_OTP_REG_SIZE 10 + +#define GC5035_DD_DELAY_US (10 * 1000) +#define GC5035_DD_TIMEOUT_US (100 * 1000) + +/* The clock source index in INT3472 CLDB */ +#define INT3472_CLDB_CLKSRC_INDEX 14 + +/* + * 82c0d13a-78c5-4244-9bb1-eb8b539a8d11 + * This _DSM GUID calls CLKC and CLKF. + */ +static const guid_t clock_ctrl_guid = + GUID_INIT(0x82c0d13a, 0x78c5, 0x4244, + 0x9b, 0xb1, 0xeb, 0x8b, 0x53, 0x9a, 0x8d, 0x11); + +static const char * const gc5035_supplies[] = { + /* + * Requested separately due to power sequencing needs: + * "iovdd", * Power supply for I/O circuits * + */ + "dvdd12", /* Digital core power */ + "avdd21", /* Analog power */ +}; + +struct gc5035_regval { + u8 addr; + u8 val; +}; + +struct gc5035_reg { + u8 page; + struct gc5035_regval regval; +}; + +struct gc5035_otp_regs { + unsigned int num_regs; + struct gc5035_reg regs[GC5035_OTP_REG_SIZE]; +}; + +struct gc5035_dpc { + bool valid; + unsigned int total_num; +}; + +struct gc5035_mode { + u32 width; + u32 height; + u32 max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct gc5035_regval *reg_list; + size_t num_regs; +}; + + +struct gc5035_power_ctrl { + /* Control Logic ACPI device */ + struct acpi_device *ctrl_logic; + /* GPIO for reset */ + struct gpio_desc *reset_gpio; + /* GPIO for power enable */ + struct gpio_desc *pwren_gpio; + /* GPIO for privacy LED */ + struct gpio_desc *pled_gpio; + + int status; + /* Clock source index */ + u8 clk_source_index; +}; + +struct gc5035 { + struct i2c_client *client; + struct clk *mclk; + unsigned long mclk_rate; + //struct gpio_desc *resetb_gpio; + ///struct gpio_desc *pwdn_gpio; + struct regulator *iovdd_supply; + struct regulator_bulk_data supplies[ARRAY_SIZE(gc5035_supplies)]; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + + bool otp_read; + u8 otp_id[GC5035_OTP_ID_SIZE]; + struct gc5035_dpc dpc; + struct gc5035_otp_regs otp_regs; + + /* + * Serialize control access, get/set format, get selection + * and start streaming. + */ + struct mutex mutex; + struct gc5035_power_ctrl power; + bool streaming; + const struct gc5035_mode *cur_mode; +}; + +static inline struct gc5035 *to_gc5035(struct v4l2_subdev *sd) +{ + return container_of(sd, struct gc5035, subdev); +} + +static const struct gc5035_regval gc5035_otp_init_regs[] = { + {0xfc, 0x01}, + {0xf4, 0x40}, + {0xf5, 0xe9}, + {0xf6, 0x14}, + {0xf8, 0x49}, + {0xf9, 0x82}, + {0xfa, 0x00}, + {0xfc, 0x81}, + {0xfe, 0x00}, + {0x36, 0x01}, + {0xd3, 0x87}, + {0x36, 0x00}, + {0x33, 0x00}, + {0xf7, 0x01}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xee, 0x30}, + {0xfa, 0x10}, + {0xf5, 0xe9}, + {0xfe, 0x02}, + {0x67, 0xc0}, + {0x59, 0x3f}, + {0x55, 0x84}, + {0x65, 0x80}, + {0x66, 0x03}, + {0xfe, 0x00}, +}; + +static const struct gc5035_regval gc5035_otp_exit_regs[] = { + {0xfe, 0x02}, + {0x67, 0x00}, + {0xfe, 0x00}, + {0xfa, 0x00}, +}; + +static const struct gc5035_regval gc5035_dd_auto_load_regs[] = { + {0xfe, 0x02}, + {0xbe, 0x00}, + {0xa9, 0x01}, + {0x09, 0x33}, +}; + +static const struct gc5035_regval gc5035_otp_dd_regs[] = { + {0x03, 0x00}, + {0x04, 0x80}, + {0x95, 0x0a}, + {0x96, 0x30}, + {0x97, 0x0a}, + {0x98, 0x32}, + {0x99, 0x07}, + {0x9a, 0xa9}, + {0xf3, 0x80}, +}; + +static const struct gc5035_regval gc5035_otp_dd_enable_regs[] = { + {0xbe, 0x01}, + {0x09, 0x00}, + {0xfe, 0x01}, + {0x80, 0x02}, + {0xfe, 0x00}, +}; + +/* + * Xclk 24Mhz + * Pclk 87.6Mhz + * grabwindow_width 2592 + * grabwindow_height 1944 + * max_framerate 30fps + * mipi_datarate per lane 876Mbps + */ +static const struct gc5035_regval gc5035_global_regs[] = { + /*init*/ + {0xfc, 0x01}, + {0xf4, 0x40}, + {0xf5, 0xe9}, + {0xf6, 0x14}, + {0xf8, 0x49}, + {0xf9, 0x82}, + {0xfa, 0x00}, + {0xfc, 0x81}, + {0xfe, 0x00}, + {0x36, 0x01}, + {0xd3, 0x87}, + {0x36, 0x00}, + {0x33, 0x00}, + {0xfe, 0x03}, + {0x01, 0xe7}, + {0xf7, 0x01}, + {0xfc, 0x8f}, + {0xfc, 0x8f}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xee, 0x30}, + {0x87, 0x18}, + {0xfe, 0x01}, + {0x8c, 0x90}, + {0xfe, 0x00}, + + /* Analog & CISCTL */ + {0xfe, 0x00}, + {0x05, 0x02}, + {0x06, 0xda}, + {0x9d, 0x0c}, + {0x09, 0x00}, + {0x0a, 0x04}, + {0x0b, 0x00}, + {0x0c, 0x03}, + {0x0d, 0x07}, + {0x0e, 0xa8}, + {0x0f, 0x0a}, + {0x10, 0x30}, + {0x11, 0x02}, + {0x17, 0x80}, + {0x19, 0x05}, + {0xfe, 0x02}, + {0x30, 0x03}, + {0x31, 0x03}, + {0xfe, 0x00}, + {0xd9, 0xc0}, + {0x1b, 0x20}, + {0x21, 0x48}, + {0x28, 0x22}, + {0x29, 0x58}, + {0x44, 0x20}, + {0x4b, 0x10}, + {0x4e, 0x1a}, + {0x50, 0x11}, + {0x52, 0x33}, + {0x53, 0x44}, + {0x55, 0x10}, + {0x5b, 0x11}, + {0xc5, 0x02}, + {0x8c, 0x1a}, + {0xfe, 0x02}, + {0x33, 0x05}, + {0x32, 0x38}, + {0xfe, 0x00}, + {0x91, 0x80}, + {0x92, 0x28}, + {0x93, 0x20}, + {0x95, 0xa0}, + {0x96, 0xe0}, + {0xd5, 0xfc}, + {0x97, 0x28}, + {0x16, 0x0c}, + {0x1a, 0x1a}, + {0x1f, 0x11}, + {0x20, 0x10}, + {0x46, 0x83}, + {0x4a, 0x04}, + {0x54, 0x02}, + {0x62, 0x00}, + {0x72, 0x8f}, + {0x73, 0x89}, + {0x7a, 0x05}, + {0x7d, 0xcc}, + {0x90, 0x00}, + {0xce, 0x18}, + {0xd0, 0xb2}, + {0xd2, 0x40}, + {0xe6, 0xe0}, + {0xfe, 0x02}, + {0x12, 0x01}, + {0x13, 0x01}, + {0x14, 0x01}, + {0x15, 0x02}, + {0x22, 0x7c}, + {0x91, 0x00}, + {0x92, 0x00}, + {0x93, 0x00}, + {0x94, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + + /* Gain */ + {0xfe, 0x00}, + {0xb0, 0x6e}, + {0xb1, 0x01}, + {0xb2, 0x00}, + {0xb3, 0x00}, + {0xb4, 0x00}, + {0xb6, 0x00}, + + /* ISP */ + {0xfe, 0x01}, + {0x53, 0x00}, + {0x89, 0x03}, + {0x60, 0x40}, + + /* BLK */ + {0xfe, 0x01}, + {0x42, 0x21}, + {0x49, 0x03}, + {0x4a, 0xff}, + {0x4b, 0xc0}, + {0x55, 0x00}, + + /* Anti_blooming */ + {0xfe, 0x01}, + {0x41, 0x28}, + {0x4c, 0x00}, + {0x4d, 0x00}, + {0x4e, 0x3c}, + {0x44, 0x08}, + {0x48, 0x02}, + + /* Crop */ + {0xfe, 0x01}, + {0x91, 0x00}, + {0x92, 0x08}, + {0x93, 0x00}, + {0x94, 0x07}, + {0x95, 0x07}, + {0x96, 0x98}, + {0x97, 0x0a}, + {0x98, 0x20}, + {0x99, 0x00}, + + /* MIPI */ + {0xfe, 0x03}, + {0x02, 0x57}, + {0x03, 0xb7}, + {0x15, 0x14}, + {0x18, 0x0f}, + {0x21, 0x22}, + {0x22, 0x06}, + {0x23, 0x48}, + {0x24, 0x12}, + {0x25, 0x28}, + {0x26, 0x08}, + {0x29, 0x06}, + {0x2a, 0x58}, + {0x2b, 0x08}, + {0xfe, 0x01}, + {0x8c, 0x10}, + + {0xfe, 0x00}, + {0x3e, 0x01}, +}; + +/* + * Xclk 24Mhz + * Pclk 87.6Mhz + * grabwindow_width 2592 + * grabwindow_height 1944 + * max_framerate 30fps + * mipi_datarate per lane 876Mbps + */ +static const struct gc5035_regval gc5035_2592x1944_regs[] = { + /* System */ + {0xfe, 0x00}, + {0x3e, 0x01}, + {0xfc, 0x01}, + {0xf4, 0x40}, + {0xf5, 0xe9}, + {0xf6, 0x14}, + {0xf8, 0x49}, + {0xf9, 0x82}, + {0xfa, 0x00}, + {0xfc, 0x81}, + {0xfe, 0x00}, + {0x36, 0x01}, + {0xd3, 0x87}, + {0x36, 0x00}, + {0x33, 0x00}, + {0xfe, 0x03}, + {0x01, 0xe7}, + {0xf7, 0x01}, + {0xfc, 0x8f}, + {0xfc, 0x8f}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xee, 0x30}, + {0x87, 0x18}, + {0xfe, 0x01}, + {0x8c, 0x90}, + {0xfe, 0x00}, + + /* Analog & CISCTL */ + {0xfe, 0x00}, + {0x05, 0x02}, + {0x06, 0xda}, + {0x9d, 0x0c}, + {0x09, 0x00}, + {0x0a, 0x04}, + {0x0b, 0x00}, + {0x0c, 0x03}, + {0x0d, 0x07}, + {0x0e, 0xa8}, + {0x0f, 0x0a}, + {0x10, 0x30}, + {0x21, 0x48}, + {0x29, 0x58}, + {0x44, 0x20}, + {0x4e, 0x1a}, + {0x8c, 0x1a}, + {0x91, 0x80}, + {0x92, 0x28}, + {0x93, 0x20}, + {0x95, 0xa0}, + {0x96, 0xe0}, + {0xd5, 0xfc}, + {0x97, 0x28}, + {0x1f, 0x11}, + {0xce, 0x18}, + {0xd0, 0xb2}, + {0xfe, 0x02}, + {0x14, 0x01}, + {0x15, 0x02}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + + /* BLK */ + {0xfe, 0x01}, + {0x49, 0x03}, + {0x4a, 0xff}, + {0x4b, 0xc0}, + + /* Anti_blooming */ + {0xfe, 0x01}, + {0x4e, 0x3c}, + {0x44, 0x08}, + + /* Crop */ + {0xfe, 0x01}, + {0x91, 0x00}, + {0x92, 0x08}, + {0x93, 0x00}, + {0x94, 0x07}, + {0x95, 0x07}, + {0x96, 0x98}, + {0x97, 0x0a}, + {0x98, 0x20}, + {0x99, 0x00}, + + /* MIPI */ + {0xfe, 0x03}, + {0x02, 0x57}, + {0x22, 0x06}, + {0x26, 0x08}, + {0x29, 0x06}, + {0x2b, 0x08}, + {0xfe, 0x01}, + {0x8c, 0x10}, + + {0xfe, 0x00}, + {0x3e, 0x91}, +}; + +/* + * Xclk 24Mhz + * Pclk 87.6Mhz + * grabwindow_width 1296 + * grabwindow_height 972 + * mipi_datarate per lane 876Mbps + */ +static const struct gc5035_regval gc5035_1296x972_regs[] = { + /*NULL*/ + {0xfe, 0x00}, + {0x3e, 0x01}, + {0xfc, 0x01}, + {0xf4, 0x40}, + {0xf5, 0xe4}, + {0xf6, 0x14}, + {0xf8, 0x49}, + {0xf9, 0x12}, + {0xfa, 0x01}, + {0xfc, 0x81}, + {0xfe, 0x00}, + {0x36, 0x01}, + {0xd3, 0x87}, + {0x36, 0x00}, + {0x33, 0x20}, + {0xfe, 0x03}, + {0x01, 0x87}, + {0xf7, 0x11}, + {0xfc, 0x8f}, + {0xfc, 0x8f}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xee, 0x30}, + {0x87, 0x18}, + {0xfe, 0x01}, + {0x8c, 0x90}, + {0xfe, 0x00}, + + /* Analog & CISCTL */ + {0xfe, 0x00}, + {0x05, 0x02}, + {0x06, 0xda}, + {0x9d, 0x0c}, + {0x09, 0x00}, + {0x0a, 0x04}, + {0x0b, 0x00}, + {0x0c, 0x03}, + {0x0d, 0x07}, + {0x0e, 0xa8}, + {0x0f, 0x0a}, + {0x10, 0x30}, + {0x21, 0x60}, + {0x29, 0x30}, + {0x44, 0x18}, + {0x4e, 0x20}, + {0x8c, 0x20}, + {0x91, 0x15}, + {0x92, 0x3a}, + {0x93, 0x20}, + {0x95, 0x45}, + {0x96, 0x35}, + {0xd5, 0xf0}, + {0x97, 0x20}, + {0x1f, 0x19}, + {0xce, 0x18}, + {0xd0, 0xb3}, + {0xfe, 0x02}, + {0x14, 0x02}, + {0x15, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + + /* BLK */ + {0xfe, 0x01}, + {0x49, 0x00}, + {0x4a, 0x01}, + {0x4b, 0xf8}, + + /* Anti_blooming */ + {0xfe, 0x01}, + {0x4e, 0x06}, + {0x44, 0x02}, + + /* Crop */ + {0xfe, 0x01}, + {0x91, 0x00}, + {0x92, 0x04}, + {0x93, 0x00}, + {0x94, 0x03}, + {0x95, 0x03}, + {0x96, 0xcc}, + {0x97, 0x05}, + {0x98, 0x10}, + {0x99, 0x00}, + + /* MIPI */ + {0xfe, 0x03}, + {0x02, 0x58}, + {0x22, 0x03}, + {0x26, 0x06}, + {0x29, 0x03}, + {0x2b, 0x06}, + {0xfe, 0x01}, + {0x8c, 0x10}, +}; + +/* + * Xclk 24Mhz + * Pclk 87.6Mhz + * linelength 672{0x2a0) + * framelength 2232{0x8b8) + * grabwindow_width 1280 + * grabwindow_height 720 + * max_framerate 30fps + * mipi_datarate per lane 876Mbps + */ +static const struct gc5035_regval gc5035_1280x720_regs[] = { + /* System */ + {0xfe, 0x00}, + {0x3e, 0x01}, + {0xfc, 0x01}, + {0xf4, 0x40}, + {0xf5, 0xe4}, + {0xf6, 0x14}, + {0xf8, 0x49}, + {0xf9, 0x12}, + {0xfa, 0x01}, + {0xfc, 0x81}, + {0xfe, 0x00}, + {0x36, 0x01}, + {0xd3, 0x87}, + {0x36, 0x00}, + {0x33, 0x20}, + {0xfe, 0x03}, + {0x01, 0x87}, + {0xf7, 0x11}, + {0xfc, 0x8f}, + {0xfc, 0x8f}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xee, 0x30}, + {0x87, 0x18}, + {0xfe, 0x01}, + {0x8c, 0x90}, + {0xfe, 0x00}, + + /* Analog & CISCTL */ + {0xfe, 0x00}, + {0x05, 0x02}, + {0x06, 0xda}, + {0x9d, 0x0c}, + {0x09, 0x00}, + {0x0a, 0x04}, + {0x0b, 0x00}, + {0x0c, 0x03}, + {0x0d, 0x07}, + {0x0e, 0xa8}, + {0x0f, 0x0a}, + {0x10, 0x30}, + {0x21, 0x60}, + {0x29, 0x30}, + {0x44, 0x18}, + {0x4e, 0x20}, + {0x8c, 0x20}, + {0x91, 0x15}, + {0x92, 0x3a}, + {0x93, 0x20}, + {0x95, 0x45}, + {0x96, 0x35}, + {0xd5, 0xf0}, + {0x97, 0x20}, + {0x1f, 0x19}, + {0xce, 0x18}, + {0xd0, 0xb3}, + {0xfe, 0x02}, + {0x14, 0x02}, + {0x15, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfe, 0x00}, + {0xfc, 0x88}, + {0xfe, 0x10}, + {0xfe, 0x00}, + {0xfc, 0x8e}, + + /* BLK */ + {0xfe, 0x01}, + {0x49, 0x00}, + {0x4a, 0x01}, + {0x4b, 0xf8}, + + /* Anti_blooming */ + {0xfe, 0x01}, + {0x4e, 0x06}, + {0x44, 0x02}, + + /* Crop */ + {0xfe, 0x01}, + {0x91, 0x00}, + {0x92, 0x0a}, + {0x93, 0x00}, + {0x94, 0x0b}, + {0x95, 0x02}, + {0x96, 0xd0}, + {0x97, 0x05}, + {0x98, 0x00}, + {0x99, 0x00}, + + /* MIPI */ + {0xfe, 0x03}, + {0x02, 0x58}, + {0x22, 0x03}, + {0x26, 0x06}, + {0x29, 0x03}, + {0x2b, 0x06}, + {0xfe, 0x01}, + {0x8c, 0x10}, + {0xfe, 0x00}, + {0x3e, 0x91}, +}; + +static const struct gc5035_mode gc5035_modes[] = { + { + .width = 2592, + .height = 1944, + .max_fps = 30, + .exp_def = 0x258, + .hts_def = 2920, + .vts_def = 2008, + .reg_list = gc5035_2592x1944_regs, + .num_regs = ARRAY_SIZE(gc5035_2592x1944_regs), + }, + { + .width = 1296, + .height = 972, + .max_fps = 30, + .exp_def = 0x258, + .hts_def = 1460, + .vts_def = 2008, + .reg_list = gc5035_1296x972_regs, + .num_regs = ARRAY_SIZE(gc5035_1296x972_regs), + }, + { + .width = 1280, + .height = 720, + .max_fps = 60, + .exp_def = 0x258, + .hts_def = 1896, + .vts_def = 1536, + .reg_list = gc5035_1280x720_regs, + .num_regs = ARRAY_SIZE(gc5035_1280x720_regs), + }, +}; + +static const char * const gc5035_test_pattern_menu[] = { + "Disabled", + "Color Bar", +}; + +static const s64 gc5035_link_freqs[] = { + 438000000, +}; + +static u64 gc5035_link_to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = gc5035_link_freqs[f_index] * 2 * GC5035_DATA_LANES; + + do_div(pixel_rate, GC5035_BITS_PER_SAMPLE); + + return pixel_rate; +} + +static struct gpio_desc* gc5035_get_gpio(struct gc5035 *gc5035, + const char* name) +{ + struct device *dev = &gc5035->client->dev; + struct gpio_desc* gpio; + int ret; + + gpio = devm_gpiod_get(dev, name, GPIOD_OUT_HIGH); + ret = PTR_ERR_OR_ZERO(gpio); + if (ret < 0) { + gpio = NULL; + dev_warn(dev, "failed to get %s gpio: %d\n", name, ret); + } + + return gpio; +} + +static void gc5035_init_power_ctrl(struct gc5035 *gc5035) +{ + struct gc5035_power_ctrl* power = &gc5035->power; + acpi_handle handle = ACPI_HANDLE(&gc5035->client->dev); + struct acpi_handle_list dep_devices; + acpi_status status; + int i = 0; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + + power->ctrl_logic = NULL; + if (!acpi_has_method(handle, "_DEP")) + return; + + /* Call _DEP method of OV13B */ + status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices); + if (ACPI_FAILURE(status)) { + acpi_handle_debug(handle, "Failed to evaluate _DEP.\n"); + return; + } + + /* Find INT3472 in _DEP */ + for (i = 0; i < dep_devices.count; i++) { + struct acpi_device *dep_device = NULL; + const char* dep_hid = NULL; + + if (dep_devices.handles[i]) + dep_device = + acpi_fetch_acpi_dev(dep_devices.handles[i]); + if (dep_device) + dep_hid = acpi_device_hid(dep_device); + if (dep_hid && strcmp("INT3472", dep_hid) == 0) { + power->ctrl_logic = dep_device; + break; + } + } + + /* If we got INT3472 acpi device, read which clock source we'll use */ + if (power->ctrl_logic == NULL) + return; + status = acpi_evaluate_object(power->ctrl_logic->handle, + "CLDB", NULL, &buffer); + if (ACPI_FAILURE(status)) { + dev_warn(&gc5035->client->dev, "Read INT3472 CLDB failed"); + return; + } + + obj = buffer.pointer; + if (!obj) + dev_warn(&gc5035->client->dev, "INT3472 CLDB return NULL"); + if (obj->type != ACPI_TYPE_BUFFER) { + acpi_handle_err(power->ctrl_logic->handle, + "CLDB object is not an ACPI buffer\n"); + kfree(obj); + return; + } + if (obj->buffer.length < INT3472_CLDB_CLKSRC_INDEX + 1) { + acpi_handle_err(power->ctrl_logic->handle, + "The CLDB buffer size is wrong\n"); + kfree(obj); + return; + } + + /* Get the clock source index */ + gc5035->power.clk_source_index = + obj->buffer.pointer[INT3472_CLDB_CLKSRC_INDEX]; + kfree(obj); + + /* Get gpios mapped by INT3472 driver */ + power->reset_gpio = gc5035_get_gpio(gc5035, "reset"); + power->pwren_gpio = gc5035_get_gpio(gc5035, "pwren"); + power->pled_gpio = gc5035_get_gpio(gc5035, "pled"); + power->status = 0; +} + +static void gc5035_set_power(struct gc5035 *gc5035, int on) +{ + struct gc5035_power_ctrl* power = &gc5035->power; + + on = (on ? 1 : 0); + if (on == power->status) + return; + + /* First, set reset pin as low */ + if (power->reset_gpio) { + gpiod_set_value_cansleep(power->reset_gpio, 0); + msleep(5); + } + + /* Use _DSM of INT3472 to enable clock */ + if (power->ctrl_logic) { + u8 clock_args[] = { power->clk_source_index, on, 0x01,}; + union acpi_object clock_ctrl_args = { + .buffer = { + .type = ACPI_TYPE_BUFFER, + .length = 3, + .pointer = clock_args, + }, + }; + acpi_evaluate_dsm(power->ctrl_logic->handle, + &clock_ctrl_guid, 0x00, 0x01, + &clock_ctrl_args); + } + + /* Set power pin and privacy LED pin */ + if (power->pwren_gpio) + gpiod_set_value_cansleep(power->pwren_gpio, on); + if (power->pled_gpio) + gpiod_set_value_cansleep(power->pled_gpio, on); + + /* If we need to power on, set reset pin to high at last */ + if (on && power->reset_gpio) { + gpiod_set_value_cansleep(power->reset_gpio, 1); + msleep(5); + } + power->status = on; +} + +static int gc5035_write_reg(struct gc5035 *gc5035, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(gc5035->client, reg, val); +} + +static int gc5035_write_array(struct gc5035 *gc5035, + const struct gc5035_regval *regs, + size_t num_regs) +{ + unsigned int i; + int ret; + + for (i = 0; i < num_regs; i++) { + ret = gc5035_write_reg(gc5035, regs[i].addr, regs[i].val); + if (ret) + return ret; + } + + return 0; +} + +static int gc5035_read_reg(struct gc5035 *gc5035, u8 reg, u8 *val) +{ + int ret; + + ret = i2c_smbus_read_byte_data(gc5035->client, reg); + if (ret < 0) + return ret; + + *val = (unsigned char)ret; + + return 0; +} + +static int gc5035_otp_read_data(struct gc5035 *gc5035, u16 bit_addr, u8 *data, + size_t length) +{ + size_t i; + int ret; + + if (WARN_ON(bit_addr % 8)) + return -EINVAL; + + if (WARN_ON(bit_addr / 8 + length > GC5035_OTP_DATA_LENGTH)) + return -EINVAL; + + ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 2); + if (ret) + return ret; + + ret = gc5035_write_reg(gc5035, GC5035_REG_OTP_ACCESS_ADDR_H, + (bit_addr >> 8) & + GC5035_OTP_ACCESS_ADDR_H_MASK); + if (ret) + return ret; + + ret = gc5035_write_reg(gc5035, GC5035_REG_OTP_ACCESS_ADDR_L, + bit_addr & 0xff); + if (ret) + return ret; + + ret = gc5035_write_reg(gc5035, GC5035_REG_OTP_MODE, + GC5035_OTP_PRE_READ); + if (ret) + goto out_read_done; + + ret = gc5035_write_reg(gc5035, GC5035_REG_OTP_MODE, + GC5035_OTP_READ_MODE); + if (ret) + goto out_read_done; + + for (i = 0; i < length; i++) { + ret = gc5035_read_reg(gc5035, GC5035_REG_OTP_DATA, &data[i]); + if (ret) + goto out_read_done; + } + +out_read_done: + gc5035_write_reg(gc5035, GC5035_REG_OTP_MODE, GC5035_OTP_READ_DONE); + + return ret; +} + +static int gc5035_read_otp_regs(struct gc5035 *gc5035) +{ + struct device *dev = &gc5035->client->dev; + struct gc5035_otp_regs *otp_regs = &gc5035->otp_regs; + u8 regs[GC5035_OTP_REG_DATA_SIZE] = {0}; + unsigned int i, j; + u8 flag; + int ret; + + ret = gc5035_otp_read_data(gc5035, GC5035_OTP_REG_FLAG_OFFSET, + &flag, 1); + if (ret) { + dev_err(dev, "failed to read otp reg flag\n"); + return ret; + } + + dev_dbg(dev, "register update flag = 0x%x\n", flag); + + gc5035->otp_regs.num_regs = 0; + if (flag != GC5035_OTP_FLAG_VALID) + return 0; + + ret = gc5035_otp_read_data(gc5035, GC5035_OTP_REG_DATA_OFFSET, + regs, sizeof(regs)); + if (ret) { + dev_err(dev, "failed to read otp reg data\n"); + return ret; + } + + for (i = 0; i < GC5035_OTP_REG_MAX_GROUP; i++) { + unsigned int base_group = i * GC5035_OTP_REG_BYTE_PER_GROUP; + + for (j = 0; j < GC5035_OTP_REG_PER_GROUP; j++) { + struct gc5035_reg *reg; + + if (!(regs[base_group] & + BIT((GC5035_OTP_PER_PAGE_SIZE * j + + GC5035_OTP_PAGE_FLAG_OFFSET)))) + continue; + + reg = &otp_regs->regs[otp_regs->num_regs++]; + reg->page = (regs[base_group] >> + (GC5035_OTP_PER_PAGE_SIZE * j)) & + GC5035_OTP_REG_PAGE_MASK; + reg->regval.addr = regs[base_group + j * + GC5035_OTP_REG_BYTE_PER_REG + + GC5035_OTP_REG_ADDR_OFFSET]; + reg->regval.val = regs[base_group + j * + GC5035_OTP_REG_BYTE_PER_REG + + GC5035_OTP_REG_VAL_OFFSET]; + } + } + + return 0; +} + +static int gc5035_read_dpc(struct gc5035 *gc5035) +{ + struct device *dev = &gc5035->client->dev; + struct gc5035_dpc *dpc = &gc5035->dpc; + u8 dpc_flag = 0; + u8 error_number = 0; + u8 total_number = 0; + int ret; + + ret = gc5035_otp_read_data(gc5035, GC5035_OTP_DPC_FLAG_OFFSET, + &dpc_flag, 1); + if (ret) { + dev_err(dev, "failed to read dpc flag\n"); + return ret; + } + + dev_dbg(dev, "dpc flag = 0x%x\n", dpc_flag); + + dpc->valid = false; + + switch (dpc_flag & GC5035_OTP_DPC_FLAG_MASK) { + case GC5035_OTP_FLAG_EMPTY: + dev_dbg(dev, "dpc info is empty!!\n"); + break; + + case GC5035_OTP_FLAG_VALID: + dev_dbg(dev, "dpc info is valid!\n"); + ret = gc5035_otp_read_data(gc5035, + GC5035_OTP_DPC_TOTAL_NUMBER_OFFSET, + &total_number, 1); + if (ret) { + dev_err(dev, "failed to read dpc total number\n"); + return ret; + } + + ret = gc5035_otp_read_data(gc5035, + GC5035_OTP_DPC_ERROR_NUMBER_OFFSET, + &error_number, 1); + if (ret) { + dev_err(dev, "failed to read dpc error number\n"); + return ret; + } + + dpc->total_num = total_number + error_number; + dpc->valid = true; + dev_dbg(dev, "total_num = %d\n", dpc->total_num); + break; + + default: + break; + } + + return ret; +} + +static int gc5035_otp_read_sensor_info(struct gc5035 *gc5035) +{ + int ret; + + ret = gc5035_read_dpc(gc5035); + if (ret) + return ret; + + return gc5035_read_otp_regs(gc5035); +} + +static int gc5035_check_dd_load_status(struct gc5035 *gc5035) +{ + u8 status; + int ret; + + ret = gc5035_read_reg(gc5035, GC5035_REG_DD_LOAD_STATUS, &status); + if (ret) + return ret; + + if (status & GC5035_OTP_BIT_LOAD) + return status; + else + return 0; +} + +static int gc5035_otp_update_dd(struct gc5035 *gc5035) +{ + struct device *dev = &gc5035->client->dev; + struct gc5035_dpc *dpc = &gc5035->dpc; + int val, ret; + + if (!dpc->valid) { + dev_dbg(dev, "DPC table invalid, not updating DD.\n"); + return 0; + } + + dev_dbg(dev, "DD auto load start\n"); + + ret = gc5035_write_array(gc5035, gc5035_dd_auto_load_regs, + ARRAY_SIZE(gc5035_dd_auto_load_regs)); + if (ret) { + dev_err(dev, "failed to write dd auto load reg\n"); + return ret; + } + + ret = gc5035_write_reg(gc5035, GC5035_REG_DD_TOTALNUM_H, + (dpc->total_num >> 8) & + GC5035_DD_TOTALNUM_H_MASK); + if (ret) + return ret; + + ret = gc5035_write_reg(gc5035, GC5035_REG_DD_TOTALNUM_L, + dpc->total_num & 0xff); + if (ret) + return ret; + + ret = gc5035_write_array(gc5035, gc5035_otp_dd_regs, + ARRAY_SIZE(gc5035_otp_dd_regs)); + if (ret) + return ret; + + /* Wait for DD to finish loading automatically */ + ret = readx_poll_timeout(gc5035_check_dd_load_status, gc5035, + val, val <= 0, GC5035_DD_DELAY_US, + GC5035_DD_TIMEOUT_US); + if (ret < 0) { + dev_err(dev, "DD load timeout\n"); + return -EFAULT; + } + if (val < 0) { + dev_err(dev, "DD load failure\n"); + return val; + } + + ret = gc5035_write_array(gc5035, gc5035_otp_dd_enable_regs, + ARRAY_SIZE(gc5035_otp_dd_enable_regs)); + if (ret) + return ret; + + return 0; +} + +static int gc5035_otp_update_regs(struct gc5035 *gc5035) +{ + struct device *dev = &gc5035->client->dev; + struct gc5035_otp_regs *otp_regs = &gc5035->otp_regs; + unsigned int i; + int ret; + + dev_dbg(dev, "reg count = %d\n", otp_regs->num_regs); + + for (i = 0; i < otp_regs->num_regs; i++) { + ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, + otp_regs->regs[i].page); + if (ret) + return ret; + + ret = gc5035_write_reg(gc5035, + otp_regs->regs[i].regval.addr, + otp_regs->regs[i].regval.val); + if (ret) + return ret; + } + + return 0; +} + +static int gc5035_otp_update(struct gc5035 *gc5035) +{ + struct device *dev = &gc5035->client->dev; + int ret; + + ret = gc5035_otp_update_dd(gc5035); + if (ret) { + dev_err(dev, "failed to update otp dd\n"); + return ret; + } + + ret = gc5035_otp_update_regs(gc5035); + if (ret) { + dev_err(dev, "failed to update otp regs\n"); + return ret; + } + + return ret; +} + +static int gc5035_set_otp_config(struct gc5035 *gc5035) +{ + struct device *dev = &gc5035->client->dev; + u8 otp_id[GC5035_OTP_ID_SIZE]; + int ret; + + ret = gc5035_write_array(gc5035, gc5035_otp_init_regs, + ARRAY_SIZE(gc5035_otp_init_regs)); + if (ret) { + dev_err(dev, "failed to write otp init reg\n"); + return ret; + } + + ret = gc5035_otp_read_data(gc5035, GC5035_OTP_ID_DATA_OFFSET, + &otp_id[0], GC5035_OTP_ID_SIZE); + if (ret) { + dev_err(dev, "failed to read otp id\n"); + goto out_otp_exit; + } + + if (!gc5035->otp_read || memcmp(gc5035->otp_id, otp_id, sizeof(otp_id))) { + dev_dbg(dev, "reading OTP configuration\n"); + + memset(&gc5035->otp_regs, 0, sizeof(gc5035->otp_regs)); + memset(&gc5035->dpc, 0, sizeof(gc5035->dpc)); + + memcpy(gc5035->otp_id, otp_id, sizeof(gc5035->otp_id)); + + ret = gc5035_otp_read_sensor_info(gc5035); + if (ret < 0) { + dev_err(dev, "failed to read otp info\n"); + goto out_otp_exit; + } + + gc5035->otp_read = true; + } + + ret = gc5035_otp_update(gc5035); + if (ret < 0) + return ret; + +out_otp_exit: + ret = gc5035_write_array(gc5035, gc5035_otp_exit_regs, + ARRAY_SIZE(gc5035_otp_exit_regs)); + if (ret) { + dev_err(dev, "failed to write otp exit reg\n"); + return ret; + } + + return ret; +} + +static int gc5035_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct gc5035 *gc5035 = to_gc5035(sd); + const struct gc5035_mode *mode; + s64 h_blank, vblank_def; + + mode = v4l2_find_nearest_size(gc5035_modes, + ARRAY_SIZE(gc5035_modes), width, + height, fmt->format.width, + fmt->format.height); + + + //fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + + mutex_lock(&gc5035->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; + } else { + gc5035->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(gc5035->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = round_up(mode->vts_def, 4) - mode->height; + __v4l2_ctrl_modify_range(gc5035->vblank, vblank_def, + GC5035_VTS_MAX - mode->height, + 1, vblank_def); + } + mutex_unlock(&gc5035->mutex); + + return 0; +} + +static int gc5035_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct gc5035 *gc5035 = to_gc5035(sd); + const struct gc5035_mode *mode = gc5035->cur_mode; + + mutex_lock(&gc5035->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + //fmt->format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&gc5035->mutex); + + return 0; +} + +static int gc5035_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + + //code->code = MEDIA_BUS_FMT_SRGGB10_1X10; + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int gc5035_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(gc5035_modes)) + return -EINVAL; + + //if (fse->code != MEDIA_BUS_FMT_SRGGB10_1X10) + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = gc5035_modes[fse->index].width; + fse->max_width = gc5035_modes[fse->index].width; + fse->max_height = gc5035_modes[fse->index].height; + fse->min_height = gc5035_modes[fse->index].height; + + return 0; +} + +static int __gc5035_start_stream(struct gc5035 *gc5035) +{ + int ret; + + gc5035_set_power(gc5035, 1); + + ret = gc5035_write_array(gc5035, gc5035_global_regs, + ARRAY_SIZE(gc5035_global_regs)); + if (ret) + return ret; + + ret = gc5035_set_otp_config(gc5035); + if (ret) + return ret; + + ret = gc5035_write_array(gc5035, gc5035->cur_mode->reg_list, + gc5035->cur_mode->num_regs); + if (ret) + return ret; + + /* In case these controls are set before streaming */ + ret = __v4l2_ctrl_handler_setup(&gc5035->ctrl_handler); + if (ret) + return ret; + + gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); + if (ret) + return ret; + + return gc5035_write_reg(gc5035, GC5035_REG_CTRL_MODE, + GC5035_MODE_STREAMING); +} + +static void __gc5035_stop_stream(struct gc5035 *gc5035) +{ + int ret; + struct i2c_client *client = gc5035->client; + + ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); + if (ret) + dev_err(&client->dev, "failed to stop streaming!"); + + if(gc5035_write_reg(gc5035, GC5035_REG_CTRL_MODE, + GC5035_MODE_SW_STANDBY)) + dev_err(&client->dev, "failed to stop streaming"); + gc5035_set_power(gc5035, 0); +} + +static int gc5035_s_stream(struct v4l2_subdev *sd, int on) +{ + struct gc5035 *gc5035 = to_gc5035(sd); + struct i2c_client *client = gc5035->client; + int ret = 0; + + mutex_lock(&gc5035->mutex); + on = !!on; + if (on == gc5035->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __gc5035_start_stream(gc5035); + if (ret) { + dev_err(&client->dev, "start stream failed\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __gc5035_stop_stream(gc5035); + pm_runtime_put(&client->dev); + } + + gc5035->streaming = on; + +unlock_and_return: + mutex_unlock(&gc5035->mutex); + + return ret; +} + +static int gc5035_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5035 *gc5035 = to_gc5035(sd); + int ret; + + if (gc5035->streaming) { + ret = __gc5035_start_stream(gc5035); + if (ret) + goto error; + } + + return 0; + +error: + __gc5035_stop_stream(gc5035); + gc5035->streaming = false; + + return ret; +} + +static int gc5035_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5035 *gc5035 = to_gc5035(sd); + + if (gc5035->streaming) + __gc5035_stop_stream(gc5035); + + return 0; +} + +static int gc5035_entity_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .format = { + .width = 2592, + .height = 1944, + } + }; + + gc5035_set_fmt(subdev, sd_state, &fmt); + + return 0; +} + +static const struct dev_pm_ops gc5035_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(gc5035_runtime_suspend, + gc5035_runtime_resume, NULL) +}; + +static const struct v4l2_subdev_video_ops gc5035_video_ops = { + .s_stream = gc5035_s_stream, +}; + +static const struct v4l2_subdev_pad_ops gc5035_pad_ops = { + .init_cfg = gc5035_entity_init_cfg, + .enum_mbus_code = gc5035_enum_mbus_code, + .enum_frame_size = gc5035_enum_frame_sizes, + .get_fmt = gc5035_get_fmt, + .set_fmt = gc5035_set_fmt, +}; + +static const struct v4l2_subdev_ops gc5035_subdev_ops = { + .video = &gc5035_video_ops, + .pad = &gc5035_pad_ops, +}; + +static const struct media_entity_operations gc5035_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int gc5035_set_exposure(struct gc5035 *gc5035, int val) +{ + int ret; + + ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); + if (ret) + return ret; + + ret = gc5035_write_reg(gc5035, GC5035_REG_EXPOSURE_H, + (val >> 8) & GC5035_EXPOSURE_H_MASK); + if (ret) + return ret; + + return gc5035_write_reg(gc5035, GC5035_REG_EXPOSURE_L, val & 0xff); +} + +static int gc5035_set_analogue_gain(struct gc5035 *gc5035, int val) +{ + int ret; + + ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); + if (ret) + return ret; + + return gc5035_write_reg(gc5035, GC5035_REG_ANALOG_GAIN, val); +} + +static int gc5035_set_digital_gain(struct gc5035 *gc5035, int val) +{ + int ret; + + ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); + if (ret) + return ret; + + ret = gc5035_write_reg(gc5035, GC5035_REG_DIGI_GAIN_H, + (val >> 8) & GC5035_DGAIN_H_MASK); + if (ret) + return ret; + + return gc5035_write_reg(gc5035, GC5035_REG_DIGI_GAIN_L, + (val << GC5035_DGAIN_L_SHIFT) + & GC5035_DGAIN_L_MASK); +} + +static int gc5035_set_vblank(struct gc5035 *gc5035, int val) +{ + int frame_length = 0; + int ret; + + frame_length = val + gc5035->cur_mode->height; + + ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); + if (ret) + return ret; + + ret = gc5035_write_reg(gc5035, GC5035_REG_VTS_H, + (frame_length >> 8) & GC5035_VTS_H_MASK); + if (ret) + return ret; + + return gc5035_write_reg(gc5035, GC5035_REG_VTS_L, frame_length & 0xff); +} + +static int gc5035_enable_test_pattern(struct gc5035 *gc5035, int pattern) +{ + int ret; + + if (pattern) + pattern = GC5035_TEST_PATTERN_ENABLE; + else + pattern = GC5035_TEST_PATTERN_DISABLE; + + ret = gc5035_write_reg(gc5035, GC5035_PAGE_REG, 1); + if (ret) + return ret; + + ret = gc5035_write_reg(gc5035, GC5035_REG_TEST_PATTERN, pattern); + if (ret) + return ret; + + return gc5035_write_reg(gc5035, GC5035_PAGE_REG, 0); +} + +static int gc5035_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc5035 *gc5035 = container_of(ctrl->handler, + struct gc5035, ctrl_handler); + struct i2c_client *client = gc5035->client; + s64 max; + int ret; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = gc5035->cur_mode->height + ctrl->val + - GC5035_EXPOSURE_MARGIN; + __v4l2_ctrl_modify_range(gc5035->exposure, + gc5035->exposure->minimum, max, + gc5035->exposure->step, + gc5035->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = gc5035_set_exposure(gc5035, ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = gc5035_set_analogue_gain(gc5035, ctrl->val); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = gc5035_set_digital_gain(gc5035, ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = gc5035_set_vblank(gc5035, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = gc5035_enable_test_pattern(gc5035, ctrl->val); + break; + default: + ret = -EINVAL; + break; + }; + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops gc5035_ctrl_ops = { + .s_ctrl = gc5035_set_ctrl, +}; + +static int gc5035_initialize_controls(struct gc5035 *gc5035) +{ + const struct gc5035_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + u64 exposure_max; + u32 h_blank, vblank_def; + int ret; + + handler = &gc5035->ctrl_handler; + mode = gc5035->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + + handler->lock = &gc5035->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, gc5035_link_freqs); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, GC5035_PIXEL_RATE, 1, GC5035_PIXEL_RATE); + + h_blank = mode->hts_def - mode->width; + gc5035->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (gc5035->hblank) + gc5035->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = round_up(mode->vts_def, 4) - mode->height; + gc5035->vblank = v4l2_ctrl_new_std(handler, &gc5035_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + GC5035_VTS_MAX - mode->height, + 4, vblank_def); + + exposure_max = mode->vts_def - GC5035_EXPOSURE_MARGIN; + gc5035->exposure = v4l2_ctrl_new_std(handler, &gc5035_ctrl_ops, + V4L2_CID_EXPOSURE, + GC5035_EXPOSURE_MIN, exposure_max, + GC5035_EXPOSURE_STEP, + mode->exp_def); + + v4l2_ctrl_new_std(handler, &gc5035_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + GC5035_ANALOG_GAIN_MIN, GC5035_ANALOG_GAIN_MAX, + GC5035_ANALOG_GAIN_STEP, GC5035_ANALOG_GAIN_DEFAULT); + + v4l2_ctrl_new_std(handler, &gc5035_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + GC5035_DIGI_GAIN_MIN, GC5035_DIGI_GAIN_MAX, + GC5035_DIGI_GAIN_STEP, GC5035_DIGI_GAIN_DEFAULT); + + v4l2_ctrl_new_std_menu_items(handler, &gc5035_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(gc5035_test_pattern_menu) - 1, + 0, 0, gc5035_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&gc5035->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + gc5035->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int gc5035_check_sensor_id(struct gc5035 *gc5035, + struct i2c_client *client) +{ + struct device *dev = &gc5035->client->dev; + u16 id; + u8 pid = 0; + u8 ver = 0; + int ret; + + ret = gc5035_read_reg(gc5035, GC5035_REG_CHIP_ID_H, &pid); + if (ret) + return ret; + + ret = gc5035_read_reg(gc5035, GC5035_REG_CHIP_ID_L, &ver); + if (ret) + return ret; + + id = GC5035_ID(pid, ver); + if (id != GC5035_CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%04x)\n", id); + return -EINVAL; + } + + dev_dbg(dev, "Detected GC%04x sensor\n", id); + + return 0; +} + +static int gc5035_get_hwcfg(struct gc5035 *gc5035) +{ + struct device *dev = &gc5035->client->dev; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + unsigned long link_freq_mask = 0; + unsigned int i, j; + int ret; + + if (!fwnode) + return -ENODEV; + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return -ENODEV; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + if (ret) + goto out; + + dev_dbg(dev, "num of link freqs: %d", bus_cfg.nr_of_link_frequencies); + if (!bus_cfg.nr_of_link_frequencies) { + dev_warn(dev, "no link frequencies defined"); + goto out; + } + + for (i = 0; i < ARRAY_SIZE(gc5035_link_freqs); ++i) { + for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { + if (bus_cfg.link_frequencies[j] + == gc5035_link_freqs[i]) { + link_freq_mask |= BIT(i); + dev_dbg(dev, "Link frequency %lld supported\n", + gc5035_link_freqs[i]); + break; + } + } + } + if (!link_freq_mask) { + dev_err(dev, "No supported link frequencies found\n"); + ret = -EINVAL; + goto out; + } + +out: + v4l2_fwnode_endpoint_free(&bus_cfg); + fwnode_handle_put(ep); + return ret; +} + +static int gc5035_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct gc5035 *gc5035; + struct v4l2_subdev *sd; + int ret, i; + u32 freq; + + gc5035 = devm_kzalloc(dev, sizeof(*gc5035), GFP_KERNEL); + if (!gc5035) + return -ENOMEM; + + gc5035->client = client; + gc5035_init_power_ctrl(gc5035); + gc5035_set_power(gc5035, 1); + + gc5035->cur_mode = &gc5035_modes[0]; + +/** + gc5035->mclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(gc5035->mclk)) + return dev_err_probe(dev, PTR_ERR(gc5035->mclk), "Failed to get mclk\n"); + + ret = device_property_read_u32(dev, "clock-frequency", &freq); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get clock-frequency\n"); + if (freq != GC5035_MCLK_RATE) + dev_warn(dev, "Given clock-frequency %u is different from expected %lu\n", + freq, GC5035_MCLK_RATE); + + ret = gc5035_get_hwcfg(gc5035); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get hardware config\n"); + + ret = clk_set_rate(gc5035->mclk, freq); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to set mclk rate\n"); + gc5035->mclk_rate = clk_get_rate(gc5035->mclk); + if (gc5035->mclk_rate != freq) + dev_warn(dev, "mclk rate set to %lu instead of requested %u\n", + gc5035->mclk_rate, freq); + + gc5035->pwdn_gpio = devm_gpiod_get(dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(gc5035->pwdn_gpio)) + return dev_err_probe(dev, PTR_ERR(gc5035->pwdn_gpio), + "Failed to get pwdn-gpios\n"); + + gc5035->resetb_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(gc5035->resetb_gpio)) + return dev_err_probe(dev, PTR_ERR(gc5035->resetb_gpio), + "Failed to get resetb-gpios\n"); + + gc5035->iovdd_supply = devm_regulator_get(dev, "iovdd"); + if (IS_ERR(gc5035->iovdd_supply)) + return dev_err_probe(dev, PTR_ERR(gc5035->iovdd_supply), + "Failed to get iovdd regulator\n"); + + for (i = 0; i < ARRAY_SIZE(gc5035_supplies); i++) + gc5035->supplies[i].supply = gc5035_supplies[i]; + ret = devm_regulator_bulk_get(&gc5035->client->dev, + ARRAY_SIZE(gc5035_supplies), + gc5035->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); +*/ + mutex_init(&gc5035->mutex); + sd = &gc5035->subdev; + v4l2_i2c_subdev_init(sd, client, &gc5035_subdev_ops); + ret = gc5035_initialize_controls(gc5035); + if (ret) { + dev_err_probe(dev, ret, "Failed to initialize controls\n"); + goto err_destroy_mutex; + } + ret = gc5035_runtime_resume(dev); + if (ret) { + dev_err_probe(dev, ret, "Failed to power on\n"); + goto err_free_handler; + } + ret = gc5035_check_sensor_id(gc5035, client); + if (ret) { + dev_err_probe(dev, ret, "Sensor ID check failed\n"); + goto err_power_off; + } + + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->entity.ops = &gc5035_subdev_entity_ops; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + gc5035->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&sd->entity, 1, &gc5035->pad); + if (ret < 0) { + dev_err_probe(dev, ret, "Failed to initialize pads\n"); + goto err_power_off; + } + ret = v4l2_async_register_subdev_sensor(sd); + if (ret) { + dev_err_probe(dev, ret, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + gc5035_set_power(gc5035, 0); + + return 0; + +err_clean_entity: + media_entity_cleanup(&sd->entity); +err_power_off: + gc5035_runtime_suspend(dev); +err_free_handler: + v4l2_ctrl_handler_free(&gc5035->ctrl_handler); + gc5035_set_power(gc5035, 0); +err_destroy_mutex: + mutex_destroy(&gc5035->mutex); + + return ret; +} + +static int gc5035_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5035 *gc5035 = to_gc5035(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(&gc5035->ctrl_handler); + mutex_destroy(&gc5035->mutex); + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + gc5035_runtime_suspend(&client->dev); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id gc5035_acpi_ids[] = { + {"GCTI5035"}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, gc5035_acpi_ids); +#endif + +static const struct of_device_id gc5035_of_match[] = { + { .compatible = "galaxycore,gc5035" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gc5035_of_match); + +static struct i2c_driver gc5035_i2c_driver = { + .driver = { + .name = "gc5035", + .pm = &gc5035_pm_ops, + .acpi_match_table = ACPI_PTR(gc5035_acpi_ids), + .of_match_table = gc5035_of_match, + }, + .probe_new = &gc5035_probe, + .remove = &gc5035_remove, +}; +module_i2c_driver(gc5035_i2c_driver); + +MODULE_AUTHOR("Liang Wang "); +MODULE_AUTHOR("Hao He "); +MODULE_AUTHOR("Xingyu Wu "); +MODULE_AUTHOR("Tomasz Figa "); +MODULE_DESCRIPTION("GalaxyCore gc5035 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/s5k3l6.c b/drivers/media/i2c/s5k3l6.c new file mode 100644 index 000000000000..4fc7ac53e87d --- /dev/null +++ b/drivers/media/i2c/s5k3l6.c @@ -0,0 +1,1701 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * s5k3l6xx camera driver + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X00 first version. + * V0.0X01.0X01 + * 1.add flip and mirror support + * 2.fix stream on sequential + * + */ +// Copyright (c) 2022 Intel Corporation. + +#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x01) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define S5K3L6_LINK_FREQ_600MHZ 600000000U +#define S5K3L6_LINK_FREQ_284MHZ 284000000U +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define S5K3L6_PIXEL_RATE (S5K3L6_LINK_FREQ_600MHZ * 2LL * 4LL / 10LL) +#define S5K3L6_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x30c6 +#define S5K3L6_REG_CHIP_ID 0x0000 + +#define S5K3L6_REG_CTRL_MODE 0x0100 +#define S5K3L6_MODE_SW_STANDBY 0x0 +#define S5K3L6_MODE_STREAMING BIT(0) +#define S5K3L6_REG_STREAM_ON 0x3C1E + +#define S5K3L6_REG_EXPOSURE 0x0202 +#define S5K3L6_EXPOSURE_MIN 1 +#define S5K3L6_EXPOSURE_STEP 1 +#define S5K3L6_VTS_MAX 0xfff7 + +#define S5K3L6_REG_ANALOG_GAIN 0x0204 +#define S5K3L6_GAIN_MIN 0x20 +#define S5K3L6_GAIN_MAX 0x200 +#define S5K3L6_GAIN_STEP 1 +#define S5K3L6_GAIN_DEFAULT 0x100 + +#define S5K3L6_REG_TEST_PATTERN 0x0601 +#define S5K3L6_TEST_PATTERN_ENABLE 0x80 +#define S5K3L6_TEST_PATTERN_DISABLE 0x0 + +#define S5K3L6_REG_VTS 0x0340 + +#define REG_NULL 0xFFFF + +#define S5K3L6_REG_VALUE_08BIT 1 +#define S5K3L6_REG_VALUE_16BIT 2 +#define S5K3L6_REG_VALUE_24BIT 3 + +#define S5K3L6_LANES 4 +#define S5K3L6_BITS_PER_SAMPLE 10 + +#define S5K3L6_CHIP_REVISION_REG 0x0002 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define S5K3L6_NAME "s5k3l6" + +// #define S5K3L6_MIRROR +// #define S5K3L6_FLIP +// #define S5K3L6_FLIP_MIRROR +#ifdef S5K3L6_MIRROR +#define S5K3L6_MEDIA_BUS_FMT MEDIA_BUS_FMT_SRGGB10_1X10 +#elif defined S5K3L6_FLIP +#define S5K3L6_MEDIA_BUS_FMT MEDIA_BUS_FMT_SBGGR10_1X10 +#elif defined S5K3L6_FLIP_MIRROR +#define S5K3L6_MEDIA_BUS_FMT MEDIA_BUS_FMT_SGBRG10_1X10 +#else +#define S5K3L6_MEDIA_BUS_FMT MEDIA_BUS_FMT_SGRBG10_1X10 +#endif + +/* The clock source index in INT3472 CLDB */ +#define INT3472_CLDB_CLKSRC_INDEX 14 + +/* + * 82c0d13a-78c5-4244-9bb1-eb8b539a8d11 + * This _DSM GUID calls CLKC and CLKF. + */ +static const guid_t clock_ctrl_guid = + GUID_INIT(0x82c0d13a, 0x78c5, 0x4244, + 0x9b, 0xb1, 0xeb, 0x8b, 0x53, 0x9a, 0x8d, 0x11); + + +static const char * const s5k3l6_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define S5K3L6_NUM_SUPPLIES ARRAY_SIZE(s5k3l6_supply_names) + +struct regval { + u16 addr; + u16 val; +}; + +struct s5k3l6_mode { + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + u32 link_freq_idx; + u32 bpp; + const struct regval *reg_list; +}; + +struct s5k3l6_power_ctrl { + /* Control Logic ACPI device */ + struct acpi_device *ctrl_logic; + /* GPIO for reset */ + struct gpio_desc *reset_gpio; + /* GPIO for power enable */ + struct gpio_desc *pwren_gpio; + /* GPIO for privacy LED */ + struct gpio_desc *pled_gpio; + + int status; + /* Clock source index */ + u8 clk_source_index; +}; + +struct s5k3l6 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *power_gpio; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[S5K3L6_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + struct s5k3l6_power_ctrl power; + bool streaming; + bool power_on; + const struct s5k3l6_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; +}; + +#define to_s5k3l6(sd) container_of(sd, struct s5k3l6, subdev) + +static const struct regval s5k3l6_4208x3120_30fps_regs[] = { +#ifdef S5K3L6_MIRROR + {0x0100, 0x0001}, +#elif defined S5K3L6_FLIP + {0x0100, 0x0002}, +#elif defined S5K3L6_FLIP_MIRROR + {0x0100, 0x0003}, +#else + {0x0100, 0x0000}, +#endif + {0x0000, 0x0060}, + {0x0000, 0x30C6}, + {0x0A02, 0x3400}, + {0x3084, 0x1314}, + {0x3266, 0x0001}, + {0x3242, 0x2020}, + {0x306A, 0x2F4C}, + {0x306C, 0xCA01}, + {0x307A, 0x0D20}, + {0x309E, 0x002D}, + {0x3072, 0x0013}, + {0x3074, 0x0977}, + {0x3076, 0x9411}, + {0x3024, 0x0016}, + {0x3070, 0x3D00}, + {0x3002, 0x0E00}, + {0x3006, 0x1000}, + {0x300A, 0x0C00}, + {0x3010, 0x0400}, + {0x3018, 0xC500}, + {0x303A, 0x0204}, + {0x3452, 0x0001}, + {0x3454, 0x0001}, + {0x3456, 0x0001}, + {0x3458, 0x0001}, + {0x345a, 0x0002}, + {0x345C, 0x0014}, + {0x345E, 0x0002}, + {0x3460, 0x0014}, + {0x3464, 0x0006}, + {0x3466, 0x0012}, + {0x3468, 0x0012}, + {0x346A, 0x0012}, + {0x346C, 0x0012}, + {0x346E, 0x0012}, + {0x3470, 0x0012}, + {0x3472, 0x0008}, + {0x3474, 0x0004}, + {0x3476, 0x0044}, + {0x3478, 0x0004}, + {0x347A, 0x0044}, + {0x347E, 0x0006}, + {0x3480, 0x0010}, + {0x3482, 0x0010}, + {0x3484, 0x0010}, + {0x3486, 0x0010}, + {0x3488, 0x0010}, + {0x348A, 0x0010}, + {0x348E, 0x000C}, + {0x3490, 0x004C}, + {0x3492, 0x000C}, + {0x3494, 0x004C}, + {0x3496, 0x0020}, + {0x3498, 0x0006}, + {0x349A, 0x0008}, + {0x349C, 0x0008}, + {0x349E, 0x0008}, + {0x34A0, 0x0008}, + {0x34A2, 0x0008}, + {0x34A4, 0x0008}, + {0x34A8, 0x001A}, + {0x34AA, 0x002A}, + {0x34AC, 0x001A}, + {0x34AE, 0x002A}, + {0x34B0, 0x0080}, + {0x34B2, 0x0006}, + {0x32A2, 0x0000}, + {0x32A4, 0x0000}, + {0x32A6, 0x0000}, + {0x32A8, 0x0000}, + {0x0344, 0x0008}, + {0x0346, 0x0008}, + {0x0348, 0x1077}, + {0x034A, 0x0C37}, + {0x034C, 0x1070}, + {0x034E, 0x0C30}, + {0x0900, 0x0000}, + {0x0380, 0x0001}, + {0x0382, 0x0001}, + {0x0384, 0x0001}, + {0x0386, 0x0001}, + {0x0114, 0x0330}, + {0x0110, 0x0002}, + {0x0136, 0x1800}, + {0x0304, 0x0004}, + {0x0306, 0x0078}, + {0x3C1E, 0x0000}, + {0x030C, 0x0004}, + {0x030E, 0x0064}, + {0x3C16, 0x0000}, + {0x0300, 0x0006}, + {0x0342, 0x1320}, + {0x0340, 0x0CBC}, + {0x38C4, 0x0009}, + {0x38D8, 0x002A}, + {0x38DA, 0x000A}, + {0x38DC, 0x000B}, + {0x38C2, 0x000A}, + {0x38C0, 0x000F}, + {0x38D6, 0x000A}, + {0x38D4, 0x0009}, + {0x38B0, 0x000F}, + {0x3932, 0x1000}, + {0x3934, 0x0180}, + {0x3938, 0x000C}, + {0x0820, 0x04B0}, + {0x380C, 0x0090}, + {0x3064, 0xEFCF}, + {0x309C, 0x0640}, + {0x3090, 0x8800}, + {0x3238, 0x000C}, + {0x314A, 0x5F00}, + {0x32B2, 0x0000}, + {0x32B4, 0x0000}, + {0x32B6, 0x0000}, + {0x32B8, 0x0000}, + {0x3300, 0x0000}, + {0x3400, 0x0000}, + {0x3402, 0x4E42}, + {0x32B2, 0x0006}, + {0x32B4, 0x0006}, + {0x32B6, 0x0006}, + {0x32B8, 0x0006}, + {0x3C34, 0x0008}, + {0x3C36, 0x0000}, + {0x3C38, 0x0000}, + {0x393E, 0x4000}, + {REG_NULL, 0x0000}, +}; + +static const struct regval s5k3l6_2104x1560_30fps_regs[] = { +#ifdef S5K3L6_MIRROR + {0x0100, 0x0001}, +#elif defined S5K3L6_FLIP + {0x0100, 0x0002}, +#elif defined S5K3L6_FLIP_MIRROR + {0x0100, 0x0003}, +#else + {0x0100, 0x0000}, +#endif + {0x0000, 0x0050}, + {0x0000, 0x30C6}, + {0x0A02, 0x3400}, + {0x3084, 0x1314}, + {0x3266, 0x0001}, + {0x3242, 0x2020}, + {0x306A, 0x2F4C}, + {0x306C, 0xCA01}, + {0x307A, 0x0D20}, + {0x309E, 0x002D}, + {0x3072, 0x0013}, + {0x3074, 0x0977}, + {0x3076, 0x9411}, + {0x3024, 0x0016}, + {0x3070, 0x3D00}, + {0x3002, 0x0E00}, + {0x3006, 0x1000}, + {0x300A, 0x0C00}, + {0x3010, 0x0400}, + {0x3018, 0xC500}, + {0x303A, 0x0204}, + {0x3452, 0x0001}, + {0x3454, 0x0001}, + {0x3456, 0x0001}, + {0x3458, 0x0001}, + {0x345a, 0x0002}, + {0x345C, 0x0014}, + {0x345E, 0x0002}, + {0x3460, 0x0014}, + {0x3464, 0x0006}, + {0x3466, 0x0012}, + {0x3468, 0x0012}, + {0x346A, 0x0012}, + {0x346C, 0x0012}, + {0x346E, 0x0012}, + {0x3470, 0x0012}, + {0x3472, 0x0008}, + {0x3474, 0x0004}, + {0x3476, 0x0044}, + {0x3478, 0x0004}, + {0x347A, 0x0044}, + {0x347E, 0x0006}, + {0x3480, 0x0010}, + {0x3482, 0x0010}, + {0x3484, 0x0010}, + {0x3486, 0x0010}, + {0x3488, 0x0010}, + {0x348A, 0x0010}, + {0x348E, 0x000C}, + {0x3490, 0x004C}, + {0x3492, 0x000C}, + {0x3494, 0x004C}, + {0x3496, 0x0020}, + {0x3498, 0x0006}, + {0x349A, 0x0008}, + {0x349C, 0x0008}, + {0x349E, 0x0008}, + {0x34A0, 0x0008}, + {0x34A2, 0x0008}, + {0x34A4, 0x0008}, + {0x34A8, 0x001A}, + {0x34AA, 0x002A}, + {0x34AC, 0x001A}, + {0x34AE, 0x002A}, + {0x34B0, 0x0080}, + {0x34B2, 0x0006}, + {0x32A2, 0x0000}, + {0x32A4, 0x0000}, + {0x32A6, 0x0000}, + {0x32A8, 0x0000}, + {0x3066, 0x7E00}, + {0x3004, 0x0800}, + //mode setting + {0x0344, 0x0008}, + {0x0346, 0x0008}, + {0x0348, 0x1077}, + {0x034A, 0x0C37}, + {0x034C, 0x0838}, + {0x034E, 0x0618}, + {0x0900, 0x0122}, + {0x0380, 0x0001}, + {0x0382, 0x0001}, + {0x0384, 0x0001}, + {0x0386, 0x0003}, + {0x0114, 0x0330}, + {0x0110, 0x0002}, + {0x0136, 0x1800}, + {0x0304, 0x0004}, + {0x0306, 0x0078}, + {0x3C1E, 0x0000}, + {0x030C, 0x0003}, + {0x030E, 0x0047}, + {0x3C16, 0x0001}, + {0x0300, 0x0006}, + {0x0342, 0x1320}, + {0x0340, 0x0CBC}, + {0x38C4, 0x0004}, + {0x38D8, 0x0011}, + {0x38DA, 0x0005}, + {0x38DC, 0x0005}, + {0x38C2, 0x0005}, + {0x38C0, 0x0004}, + {0x38D6, 0x0004}, + {0x38D4, 0x0004}, + {0x38B0, 0x0007}, + {0x3932, 0x1000}, + {0x3934, 0x0180}, + {0x3938, 0x000C}, + {0x0820, 0x0238}, + {0x380C, 0x0049}, + {0x3064, 0xFFCF}, + {0x309C, 0x0640}, + {0x3090, 0x8000}, + {0x3238, 0x000B}, + {0x314A, 0x5F02}, + {0x3300, 0x0000}, + {0x3400, 0x0000}, + {0x3402, 0x4E46}, + {0x32B2, 0x0008}, + {0x32B4, 0x0008}, + {0x32B6, 0x0008}, + {0x32B8, 0x0008}, + {0x3C34, 0x0048}, + {0x3C36, 0x3000}, + {0x3C38, 0x0020}, + {0x393E, 0x4000}, + {0x303A, 0x0204}, + {0x3034, 0x4B01}, + {0x3036, 0x0029}, + {0x3032, 0x4800}, + {0x320E, 0x049E}, + {REG_NULL, 0x0000}, +}; + +static const struct s5k3l6_mode supported_modes[] = { + { + .width = 4208, + .height = 3120, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0cb0, + .hts_def = 0x1320, + .vts_def = 0x0cbc, + .bpp = 10, + .reg_list = s5k3l6_4208x3120_30fps_regs, + .link_freq_idx = 0, + }, + { + .width = 2104, + .height = 1560, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0cb0, + .hts_def = 0x1320, + .vts_def = 0x0cbc, + .bpp = 10, + .reg_list = s5k3l6_2104x1560_30fps_regs, + .link_freq_idx = 1, + }, +}; + +static const s64 link_freq_items[] = { + S5K3L6_LINK_FREQ_600MHZ, + S5K3L6_LINK_FREQ_284MHZ, +}; + +static const char * const s5k3l6_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3" +}; + + +static struct gpio_desc* s5k3l6_get_gpio(struct s5k3l6 *s5k3l6, + const char* name) +{ + struct device *dev = &s5k3l6->client->dev; + struct gpio_desc* gpio; + int ret; + + gpio = devm_gpiod_get(dev, name, GPIOD_OUT_HIGH); + ret = PTR_ERR_OR_ZERO(gpio); + if (ret < 0) { + gpio = NULL; + dev_warn(dev, "failed to get %s gpio: %d\n", name, ret); + } + + return gpio; +} + +static void s5k3l6_init_power_ctrl(struct s5k3l6 *s5k3l6) +{ + struct s5k3l6_power_ctrl* power = &s5k3l6->power; + acpi_handle handle = ACPI_HANDLE(&s5k3l6->client->dev); + struct acpi_handle_list dep_devices; + acpi_status status; + int i = 0; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + + power->ctrl_logic = NULL; + if (!acpi_has_method(handle, "_DEP")) + return; + + /* Call _DEP method of s5k3l6 */ + status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices); + if (ACPI_FAILURE(status)) { + acpi_handle_debug(handle, "Failed to evaluate _DEP.\n"); + return; + } + + /* Find INT3472 in _DEP */ + for (i = 0; i < dep_devices.count; i++) { + struct acpi_device *dep_device = NULL; + const char* dep_hid = NULL; + + if (dep_devices.handles[i]) + dep_device = + acpi_fetch_acpi_dev(dep_devices.handles[i]); + if (dep_device) + dep_hid = acpi_device_hid(dep_device); + if (dep_hid && strcmp("INT3472", dep_hid) == 0) { + power->ctrl_logic = dep_device; + break; + } + } + + /* If we got INT3472 acpi device, read which clock source we'll use */ + if (power->ctrl_logic == NULL) + return; + status = acpi_evaluate_object(power->ctrl_logic->handle, + "CLDB", NULL, &buffer); + if (ACPI_FAILURE(status)) { + dev_warn(&s5k3l6->client->dev, "Read INT3472 CLDB failed"); + return; + } + + obj = buffer.pointer; + if (!obj) + dev_warn(&s5k3l6->client->dev, "INT3472 CLDB return NULL"); + if (obj->type != ACPI_TYPE_BUFFER) { + acpi_handle_err(power->ctrl_logic->handle, + "CLDB object is not an ACPI buffer\n"); + kfree(obj); + return; + } + if (obj->buffer.length < INT3472_CLDB_CLKSRC_INDEX + 1) { + acpi_handle_err(power->ctrl_logic->handle, + "The CLDB buffer size is wrong\n"); + kfree(obj); + return; + } + + /* Get the clock source index */ + s5k3l6->power.clk_source_index = + obj->buffer.pointer[INT3472_CLDB_CLKSRC_INDEX]; + kfree(obj); + + /* Get gpios mapped by INT3472 driver */ + power->reset_gpio = s5k3l6_get_gpio(s5k3l6, "reset"); + power->pwren_gpio = s5k3l6_get_gpio(s5k3l6, "pwren"); + power->pled_gpio = s5k3l6_get_gpio(s5k3l6, "pled"); + power->status = 0; +} + +static void s5k3l6_set_power(struct s5k3l6 *s5k3l6, int on) +{ + struct s5k3l6_power_ctrl* power = &s5k3l6->power; + + on = (on ? 1 : 0); + if (on == power->status) + return; + + /* First, set reset pin as low */ + if (power->reset_gpio) { + gpiod_set_value_cansleep(power->reset_gpio, 0); + msleep(5); + } + + /* Use _DSM of INT3472 to enable clock */ + if (power->ctrl_logic) { + u8 clock_args[] = { power->clk_source_index, on, 0x01,}; + union acpi_object clock_ctrl_args = { + .buffer = { + .type = ACPI_TYPE_BUFFER, + .length = 3, + .pointer = clock_args, + }, + }; + acpi_evaluate_dsm(power->ctrl_logic->handle, + &clock_ctrl_guid, 0x00, 0x01, + &clock_ctrl_args); + } + + /* Set power pin and privacy LED pin */ + if (power->pwren_gpio) + gpiod_set_value_cansleep(power->pwren_gpio, on); + if (power->pled_gpio) + gpiod_set_value_cansleep(power->pled_gpio, on); + + /* If we need to power on, set reset pin to high at last */ + if (on && power->reset_gpio) { + gpiod_set_value_cansleep(power->reset_gpio, 1); + msleep(5); + } + power->status = on; +} + +/* Write registers up to 4 at a time */ +static int s5k3l6_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + dev_dbg(&client->dev, "write reg(0x%x val:0x%x)!\n", reg, val); + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int s5k3l6_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = s5k3l6_write_reg(client, regs[i].addr, + S5K3L6_REG_VALUE_16BIT, + regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int s5k3l6_read_reg(struct i2c_client *client, u16 reg, + unsigned int len, u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int s5k3l6_get_reso_dist(const struct s5k3l6_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct s5k3l6_mode * +s5k3l6_find_best_fit(struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = s5k3l6_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int s5k3l6_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct s5k3l6 *s5k3l6 = to_s5k3l6(sd); + const struct s5k3l6_mode *mode; + s64 h_blank, vblank_def; + u64 pixel_rate = 0; + u32 lane_num = S5K3L6_LANES; + + mutex_lock(&s5k3l6->mutex); + + mode = s5k3l6_find_best_fit(fmt); + fmt->format.code = S5K3L6_MEDIA_BUS_FMT; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; +#else + mutex_unlock(&s5k3l6->mutex); + return -ENOTTY; +#endif + } else { + s5k3l6->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(s5k3l6->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(s5k3l6->vblank, vblank_def, + S5K3L6_VTS_MAX - mode->height, + 1, vblank_def); + pixel_rate = (u32)link_freq_items[mode->link_freq_idx] / mode->bpp * 2 * lane_num; + + __v4l2_ctrl_s_ctrl_int64(s5k3l6->pixel_rate, + pixel_rate); + __v4l2_ctrl_s_ctrl(s5k3l6->link_freq, + mode->link_freq_idx); + } + + mutex_unlock(&s5k3l6->mutex); + + return 0; +} + +static int s5k3l6_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct s5k3l6 *s5k3l6 = to_s5k3l6(sd); + const struct s5k3l6_mode *mode = s5k3l6->cur_mode; + + mutex_lock(&s5k3l6->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); +#else + mutex_unlock(&s5k3l6->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = S5K3L6_MEDIA_BUS_FMT; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&s5k3l6->mutex); + + return 0; +} + +static int s5k3l6_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = S5K3L6_MEDIA_BUS_FMT; + + return 0; +} + +static int s5k3l6_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != S5K3L6_MEDIA_BUS_FMT) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int s5k3l6_enable_test_pattern(struct s5k3l6 *s5k3l6, u32 pattern) +{ + u32 val; + + if (pattern) + val = (pattern - 1) | S5K3L6_TEST_PATTERN_ENABLE; + else + val = S5K3L6_TEST_PATTERN_DISABLE; + + return s5k3l6_write_reg(s5k3l6->client, + S5K3L6_REG_TEST_PATTERN, + S5K3L6_REG_VALUE_08BIT, + val); +} + +static int s5k3l6_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct s5k3l6 *s5k3l6 = to_s5k3l6(sd); + const struct s5k3l6_mode *mode = s5k3l6->cur_mode; + + mutex_lock(&s5k3l6->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&s5k3l6->mutex); + + return 0; +} + +static void s5k3l6_get_module_inf(struct s5k3l6 *s5k3l6, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strscpy(inf->base.sensor, S5K3L6_NAME, sizeof(inf->base.sensor)); + strscpy(inf->base.module, s5k3l6->module_name, + sizeof(inf->base.module)); + strscpy(inf->base.lens, s5k3l6->len_name, sizeof(inf->base.lens)); +} + +static long s5k3l6_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct s5k3l6 *s5k3l6 = to_s5k3l6(sd); + long ret = 0; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + s5k3l6_get_module_inf(s5k3l6, (struct rkmodule_inf *)arg); + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = s5k3l6_write_reg(s5k3l6->client, + S5K3L6_REG_CTRL_MODE, + S5K3L6_REG_VALUE_08BIT, + S5K3L6_MODE_STREAMING); + else + ret = s5k3l6_write_reg(s5k3l6->client, + S5K3L6_REG_CTRL_MODE, + S5K3L6_REG_VALUE_08BIT, + S5K3L6_MODE_SW_STANDBY); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long s5k3l6_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + long ret = 0; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = s5k3l6_ioctl(sd, cmd, inf); + if (!ret) { + ret = copy_to_user(up, inf, sizeof(*inf)); + if (ret) + ret = -EFAULT; + } + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = s5k3l6_ioctl(sd, cmd, cfg); + else + ret = -EFAULT; + kfree(cfg); + break; + case RKMODULE_SET_QUICK_STREAM: + ret = copy_from_user(&stream, up, sizeof(u32)); + if (!ret) + ret = s5k3l6_ioctl(sd, cmd, &stream); + else + ret = -EFAULT; + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __s5k3l6_start_stream(struct s5k3l6 *s5k3l6) +{ + int ret; + + s5k3l6_set_power(s5k3l6, 1); + ret = s5k3l6_write_array(s5k3l6->client, s5k3l6->cur_mode->reg_list); + if (ret) + return ret; + + /* In case these controls are set before streaming */ + mutex_unlock(&s5k3l6->mutex); + ret = v4l2_ctrl_handler_setup(&s5k3l6->ctrl_handler); + mutex_lock(&s5k3l6->mutex); + if (ret) + return ret; + + s5k3l6_write_reg(s5k3l6->client, + S5K3L6_REG_STREAM_ON, + S5K3L6_REG_VALUE_08BIT, + S5K3L6_MODE_STREAMING); + s5k3l6_write_reg(s5k3l6->client, + S5K3L6_REG_CTRL_MODE, + S5K3L6_REG_VALUE_08BIT, + S5K3L6_MODE_STREAMING); + s5k3l6_write_reg(s5k3l6->client, + S5K3L6_REG_STREAM_ON, + S5K3L6_REG_VALUE_08BIT, + S5K3L6_MODE_SW_STANDBY); + return 0; +} + +static void __s5k3l6_stop_stream(struct s5k3l6 *s5k3l6) +{ + struct i2c_client *client = s5k3l6->client; + + if (s5k3l6_write_reg(s5k3l6->client, + S5K3L6_REG_CTRL_MODE, + S5K3L6_REG_VALUE_08BIT, + S5K3L6_MODE_SW_STANDBY)) + dev_err(&client->dev, "failed to stop streaming"); + s5k3l6_set_power(s5k3l6, 0); +} + +static int s5k3l6_s_stream(struct v4l2_subdev *sd, int on) +{ + struct s5k3l6 *s5k3l6 = to_s5k3l6(sd); + struct i2c_client *client = s5k3l6->client; + int ret = 0; + + dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on, + s5k3l6->cur_mode->width, + s5k3l6->cur_mode->height, + DIV_ROUND_CLOSEST(s5k3l6->cur_mode->max_fps.denominator, + s5k3l6->cur_mode->max_fps.numerator)); + + mutex_lock(&s5k3l6->mutex); + on = !!on; + if (on == s5k3l6->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __s5k3l6_start_stream(s5k3l6); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __s5k3l6_stop_stream(s5k3l6); + pm_runtime_put(&client->dev); + } + + s5k3l6->streaming = on; + +unlock_and_return: + mutex_unlock(&s5k3l6->mutex); + + return ret; +} + +static int s5k3l6_s_power(struct v4l2_subdev *sd, int on) +{ + struct s5k3l6 *s5k3l6 = to_s5k3l6(sd); + struct i2c_client *client = s5k3l6->client; + int ret = 0; + + mutex_lock(&s5k3l6->mutex); + + /* If the power state is not modified - no work to do. */ + if (s5k3l6->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + s5k3l6->power_on = true; + } else { + pm_runtime_put(&client->dev); + s5k3l6->power_on = false; + } + +unlock_and_return: + mutex_unlock(&s5k3l6->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 s5k3l6_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, S5K3L6_XVCLK_FREQ / 1000 / 1000); +} + +static int __s5k3l6_power_on(struct s5k3l6 *s5k3l6) +{ + int ret; + u32 delay_us; + struct device *dev = &s5k3l6->client->dev; + + if (!IS_ERR(s5k3l6->power_gpio)) + gpiod_set_value_cansleep(s5k3l6->power_gpio, 1); + + usleep_range(1000, 2000); + + if (!IS_ERR_OR_NULL(s5k3l6->pins_default)) { + ret = pinctrl_select_state(s5k3l6->pinctrl, + s5k3l6->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(s5k3l6->xvclk, S5K3L6_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(s5k3l6->xvclk) != S5K3L6_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(s5k3l6->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(s5k3l6->reset_gpio)) + gpiod_set_value_cansleep(s5k3l6->reset_gpio, 0); + + ret = regulator_bulk_enable(S5K3L6_NUM_SUPPLIES, s5k3l6->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(s5k3l6->reset_gpio)) + gpiod_set_value_cansleep(s5k3l6->reset_gpio, 1); + + usleep_range(500, 1000); + if (!IS_ERR(s5k3l6->pwdn_gpio)) + gpiod_set_value_cansleep(s5k3l6->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = s5k3l6_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(s5k3l6->xvclk); + + return ret; +} + +static void __s5k3l6_power_off(struct s5k3l6 *s5k3l6) +{ + int ret; + struct device *dev = &s5k3l6->client->dev; + + if (!IS_ERR(s5k3l6->pwdn_gpio)) + gpiod_set_value_cansleep(s5k3l6->pwdn_gpio, 0); + clk_disable_unprepare(s5k3l6->xvclk); + if (!IS_ERR(s5k3l6->reset_gpio)) + gpiod_set_value_cansleep(s5k3l6->reset_gpio, 0); + + if (!IS_ERR_OR_NULL(s5k3l6->pins_sleep)) { + ret = pinctrl_select_state(s5k3l6->pinctrl, + s5k3l6->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + if (!IS_ERR(s5k3l6->power_gpio)) + gpiod_set_value_cansleep(s5k3l6->power_gpio, 0); + + regulator_bulk_disable(S5K3L6_NUM_SUPPLIES, s5k3l6->supplies); +} + +static int s5k3l6_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct s5k3l6 *s5k3l6 = to_s5k3l6(sd); + int ret; + + if (s5k3l6->streaming) { + ret = __s5k3l6_start_stream(s5k3l6); + if (ret) + goto error; + } + + return 0; + +error: + __s5k3l6_stop_stream(s5k3l6); + s5k3l6->streaming = false; + + return ret; +} + +static int s5k3l6_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct s5k3l6 *s5k3l6 = to_s5k3l6(sd); + + if (s5k3l6->streaming) + __s5k3l6_stop_stream(s5k3l6); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int s5k3l6_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct s5k3l6 *s5k3l6 = to_s5k3l6(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->state, 0); + const struct s5k3l6_mode *def_mode = &supported_modes[0]; + + mutex_lock(&s5k3l6->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = S5K3L6_MEDIA_BUS_FMT; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&s5k3l6->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int s5k3l6_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval_enum *fie) +{ + if (fie->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fie->code != S5K3L6_MEDIA_BUS_FMT) + return -EINVAL; + + fie->width = supported_modes[fie->index].width; + fie->height = supported_modes[fie->index].height; + fie->interval = supported_modes[fie->index].max_fps; + + return 0; +} + +static int s5k3l6_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_config *config) +{ + if (2 == S5K3L6_LANES) { + config->type = V4L2_MBUS_CSI2_DPHY; + config->bus.parallel.flags = V4L2_MBUS_SLAVE | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH; + } else if (4 == S5K3L6_LANES) { + config->type = V4L2_MBUS_CSI2_DPHY; + config->bus.parallel.flags = V4L2_MBUS_HSYNC_ACTIVE_LOW | + V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_DATA_ACTIVE_HIGH; + } + + return 0; +} + +#define CROP_START(SRC, DST) (((SRC) - (DST)) / 2 / 4 * 4) +#define DST_WIDTH_2096 2096 +#define DST_HEIGHT_1560 1560 + +static int s5k3l6_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct s5k3l6 *s5k3l6 = to_s5k3l6(sd); + + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + if (s5k3l6->cur_mode->width == 2104) { + sel->r.left = CROP_START(s5k3l6->cur_mode->width, DST_WIDTH_2096); + sel->r.width = DST_WIDTH_2096; + sel->r.top = CROP_START(s5k3l6->cur_mode->height, DST_HEIGHT_1560); + sel->r.height = DST_HEIGHT_1560; + } else { + sel->r.left = CROP_START(s5k3l6->cur_mode->width, + s5k3l6->cur_mode->width); + sel->r.width = s5k3l6->cur_mode->width; + sel->r.top = CROP_START(s5k3l6->cur_mode->height, + s5k3l6->cur_mode->height); + sel->r.height = s5k3l6->cur_mode->height; + } + return 0; + } + + return -EINVAL; +} + +static const struct dev_pm_ops s5k3l6_pm_ops = { + SET_RUNTIME_PM_OPS(s5k3l6_runtime_suspend, + s5k3l6_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops s5k3l6_internal_ops = { + .open = s5k3l6_open, +}; +#endif + +static const struct v4l2_subdev_core_ops s5k3l6_core_ops = { + .s_power = s5k3l6_s_power, + .ioctl = s5k3l6_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = s5k3l6_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops s5k3l6_video_ops = { + .s_stream = s5k3l6_s_stream, + .g_frame_interval = s5k3l6_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops s5k3l6_pad_ops = { + .enum_mbus_code = s5k3l6_enum_mbus_code, + .enum_frame_size = s5k3l6_enum_frame_sizes, + .enum_frame_interval = s5k3l6_enum_frame_interval, + .get_fmt = s5k3l6_get_fmt, + .set_fmt = s5k3l6_set_fmt, + .get_selection = s5k3l6_get_selection, + .get_mbus_config = s5k3l6_g_mbus_config, +}; + +static const struct v4l2_subdev_ops s5k3l6_subdev_ops = { + .core = &s5k3l6_core_ops, + .video = &s5k3l6_video_ops, + .pad = &s5k3l6_pad_ops, +}; + +static int s5k3l6_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct s5k3l6 *s5k3l6 = container_of(ctrl->handler, + struct s5k3l6, ctrl_handler); + struct i2c_client *client = s5k3l6->client; + s64 max; + int ret = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = s5k3l6->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(s5k3l6->exposure, + s5k3l6->exposure->minimum, max, + s5k3l6->exposure->step, + s5k3l6->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = s5k3l6_write_reg(s5k3l6->client, + S5K3L6_REG_EXPOSURE, + S5K3L6_REG_VALUE_16BIT, + ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = s5k3l6_write_reg(s5k3l6->client, + S5K3L6_REG_ANALOG_GAIN, + S5K3L6_REG_VALUE_16BIT, + ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = s5k3l6_write_reg(s5k3l6->client, + S5K3L6_REG_VTS, + S5K3L6_REG_VALUE_16BIT, + ctrl->val + s5k3l6->cur_mode->height); + break; + case V4L2_CID_TEST_PATTERN: + ret = s5k3l6_enable_test_pattern(s5k3l6, ctrl->val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops s5k3l6_ctrl_ops = { + .s_ctrl = s5k3l6_set_ctrl, +}; + +static int s5k3l6_initialize_controls(struct s5k3l6 *s5k3l6) +{ + const struct s5k3l6_mode *mode; + struct v4l2_ctrl_handler *handler; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + u64 dst_pixel_rate = 0; + u32 lane_num = S5K3L6_LANES; + + handler = &s5k3l6->ctrl_handler; + mode = s5k3l6->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &s5k3l6->mutex; + + s5k3l6->link_freq = v4l2_ctrl_new_int_menu(handler, NULL, + V4L2_CID_LINK_FREQ, + 1, 0, link_freq_items); + + dst_pixel_rate = (u32)link_freq_items[mode->link_freq_idx] / mode->bpp * 2 * lane_num; + + s5k3l6->pixel_rate = v4l2_ctrl_new_std(handler, NULL, + V4L2_CID_PIXEL_RATE, + 0, S5K3L6_PIXEL_RATE, + 1, dst_pixel_rate); + + __v4l2_ctrl_s_ctrl(s5k3l6->link_freq, + mode->link_freq_idx); + + h_blank = mode->hts_def - mode->width; + s5k3l6->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (s5k3l6->hblank) + s5k3l6->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + s5k3l6->vblank = v4l2_ctrl_new_std(handler, &s5k3l6_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + S5K3L6_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + s5k3l6->exposure = v4l2_ctrl_new_std(handler, &s5k3l6_ctrl_ops, + V4L2_CID_EXPOSURE, S5K3L6_EXPOSURE_MIN, + exposure_max, S5K3L6_EXPOSURE_STEP, + mode->exp_def); + + s5k3l6->anal_gain = v4l2_ctrl_new_std(handler, &s5k3l6_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, S5K3L6_GAIN_MIN, + S5K3L6_GAIN_MAX, S5K3L6_GAIN_STEP, + S5K3L6_GAIN_DEFAULT); + + s5k3l6->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &s5k3l6_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(s5k3l6_test_pattern_menu) - 1, + 0, 0, s5k3l6_test_pattern_menu); + + if (handler->error) { + ret = handler->error; + dev_err(&s5k3l6->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + s5k3l6->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int s5k3l6_check_sensor_id(struct s5k3l6 *s5k3l6, + struct i2c_client *client) +{ + struct device *dev = &s5k3l6->client->dev; + u32 id = 0; + int ret; + + ret = s5k3l6_read_reg(client, S5K3L6_REG_CHIP_ID, + S5K3L6_REG_VALUE_16BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%04x), ret(%d)\n", id, ret); + return -ENODEV; + } + + ret = s5k3l6_read_reg(client, S5K3L6_CHIP_REVISION_REG, + S5K3L6_REG_VALUE_08BIT, &id); + if (ret) { + dev_err(dev, "Read chip revision register error\n"); + return ret; + } + + dev_info(dev, "Detected Samsung %04x sensor, REVISION 0x%x\n", CHIP_ID, id); + + return 0; +} + +static int s5k3l6_configure_regulators(struct s5k3l6 *s5k3l6) +{ + unsigned int i; + + for (i = 0; i < S5K3L6_NUM_SUPPLIES; i++) + s5k3l6->supplies[i].supply = s5k3l6_supply_names[i]; + + return devm_regulator_bulk_get(&s5k3l6->client->dev, + S5K3L6_NUM_SUPPLIES, + s5k3l6->supplies); +} + +static int s5k3l6_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct s5k3l6 *s5k3l6; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_err(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + s5k3l6 = devm_kzalloc(dev, sizeof(*s5k3l6), GFP_KERNEL); + if (!s5k3l6) + return -ENOMEM; + + s5k3l6->client = client; + s5k3l6_init_power_ctrl(s5k3l6); + s5k3l6_set_power(s5k3l6, 1); + s5k3l6->cur_mode = &supported_modes[0]; + + /** + s5k3l6->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(s5k3l6->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + s5k3l6->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(s5k3l6->power_gpio)) + dev_warn(dev, "Failed to get power-gpios, maybe no use\n"); + + s5k3l6->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(s5k3l6->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + s5k3l6->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(s5k3l6->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = s5k3l6_configure_regulators(s5k3l6); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + s5k3l6->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(s5k3l6->pinctrl)) { + s5k3l6->pins_default = + pinctrl_lookup_state(s5k3l6->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(s5k3l6->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + s5k3l6->pins_sleep = + pinctrl_lookup_state(s5k3l6->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(s5k3l6->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } + */ + mutex_init(&s5k3l6->mutex); + + sd = &s5k3l6->subdev; + v4l2_i2c_subdev_init(sd, client, &s5k3l6_subdev_ops); + ret = s5k3l6_initialize_controls(s5k3l6); + if (ret) + goto err_destroy_mutex; +// ret = __s5k3l6_power_on(s5k3l6); +// if (ret) +// goto err_free_handler; +// ret = s5k3l6_check_sensor_id(s5k3l6, client); +// if (ret) +// goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &s5k3l6_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + s5k3l6->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &s5k3l6->pad); + if (ret < 0) + goto err_power_off; +#endif + memset(facing, 0, sizeof(facing)); + facing[0] = 'b'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + s5k3l6->module_index, facing, + S5K3L6_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + s5k3l6_set_power(s5k3l6, 0); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + s5k3l6_runtime_suspend(dev); +err_free_handler: + v4l2_ctrl_handler_free(&s5k3l6->ctrl_handler); + s5k3l6_set_power(s5k3l6, 0); +err_destroy_mutex: + mutex_destroy(&s5k3l6->mutex); + + return ret; +} + +static int s5k3l6_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct s5k3l6 *s5k3l6 = to_s5k3l6(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&s5k3l6->ctrl_handler); + mutex_destroy(&s5k3l6->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + s5k3l6_runtime_suspend(&client->dev); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +static const struct of_device_id s5k3l6_of_match[] = { + { .compatible = "samsung,s5k3l6" }, + {}, +}; +MODULE_DEVICE_TABLE(of, s5k3l6_of_match); + +static const struct i2c_device_id s5k3l6_id_table[] = { + { "samsung,s5k3l6", 0 }, + {/* sentinel */ }, +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id s5k3l6_acpi_ids[] = { + {"S5K3L6"}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, s5k3l6_acpi_ids); +#endif + +static struct i2c_driver s5k3l6_i2c_driver = { + .driver = { + .name = S5K3L6_NAME, + .pm = &s5k3l6_pm_ops, + .acpi_match_table = ACPI_PTR(s5k3l6_acpi_ids), + //.of_match_table = of_match_ptr(s5k3l6_of_match), + }, + .probe_new = &s5k3l6_probe, + .remove = &s5k3l6_remove, +// .id_table = s5k3l6_id_table, +}; + +module_i2c_driver(s5k3l6_i2c_driver); + +MODULE_DESCRIPTION("Samsung s5k3l6 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/pci/intel/cio2-bridge.c b/drivers/media/pci/intel/cio2-bridge.c index 17c56d65a762..0ae88577ecaf 100644 --- a/drivers/media/pci/intel/cio2-bridge.c +++ b/drivers/media/pci/intel/cio2-bridge.c @@ -48,6 +48,10 @@ static const struct cio2_sensor_config cio2_supported_sensors[] = { /* Omnivision ov13b10 */ CIO2_SENSOR_CONFIG("OVTIDB10", 0, 0), CIO2_SENSOR_CONFIG("OVTI13B1", 0, 0), + /* samsung s5k3l6 */ + CIO2_SENSOR_CONFIG("S5K3L6", 0, 0), + /* galaxycore gc5035 */ + CIO2_SENSOR_CONFIG("GCTI5035", 0, 0), }; static const struct cio2_property_names prop_names = { @@ -72,6 +76,36 @@ static const char * const cio2_vcm_types[] = { "lc898212axb", }; +static const char * const cio2_nvm_types[] = { + "24c00", + "24c01", + "24cs01", + "24c02", + "24cs02", + "24mac402", + "24mac602", + "spd", + "24c64", + "24c02-vaio", + "24c04", + "24cs04", + "24c08", + "24cs08", + "24c16", + "24cs16", + "24c32", + "24cs32", + "24c64", + "24cs64", + "24c128", + "24c256", + "24c512", + "24c1024", + "24c1025", + "24c2048", + "at24", +}; + static int cio2_bridge_read_acpi_buffer(struct acpi_device *adev, char *id, void *data, u32 size) { @@ -180,6 +214,12 @@ cio2_bridge_create_fwnode_properties(struct cio2_sensor *sensor, sensor->dev_properties[3] = PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref); } + if (sensor->ssdb.romtype) { + sensor->nvm_ref[0] = + SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_NVM]); + sensor->dev_properties[4] = + PROPERTY_ENTRY_REF_ARRAY("eeprom", sensor->nvm_ref); + } sensor->ep_properties[0] = PROPERTY_ENTRY_U32(prop_name->bus_type, @@ -226,6 +266,7 @@ static void cio2_bridge_create_connection_swnodes(struct cio2_bridge *bridge, struct software_node *nodes = sensor->swnodes; char sensor_node_name[ACPI_ID_LEN + 2]; char vcm_node_name[ACPI_ID_LEN + 2]; + char nvm_node_name[ACPI_ID_LEN + 2]; cio2_bridge_init_swnode_names(sensor); @@ -253,6 +294,13 @@ static void cio2_bridge_create_connection_swnodes(struct cio2_bridge *bridge, sensor->ssdb.link); nodes[SWNODE_VCM] = NODE_VCM(vcm_node_name); } + if (sensor->ssdb.romtype && + sensor->ssdb.romtype <= ARRAY_SIZE(cio2_nvm_types)) { + snprintf(nvm_node_name, sizeof(nvm_node_name), + "%s-%u", cio2_nvm_types[sensor->ssdb.romtype - 1], + sensor->ssdb.link); + nodes[SWNODE_NVM] = NODE_VCM(nvm_node_name); + } } static void cio2_bridge_instantiate_vcm_i2c_client(struct cio2_sensor *sensor) @@ -281,6 +329,44 @@ static void cio2_bridge_instantiate_vcm_i2c_client(struct cio2_sensor *sensor) } } +static void cio2_bridge_instantiate_nvm_i2c_client(struct cio2_sensor *sensor, struct pci_dev *cio2) +{ + struct device *dev = &cio2->dev; + struct i2c_board_info board_info = { }; + char name[16]; + int retval = -EINVAL; + + if (!sensor->ssdb.romtype || + sensor->ssdb.romtype > ARRAY_SIZE(cio2_nvm_types)) + return; + + snprintf(name, sizeof(name), "%s-NVM", acpi_dev_name(sensor->adev)); + board_info.dev_name = name; + strscpy(board_info.type, cio2_nvm_types[sensor->ssdb.romtype - 1], + ARRAY_SIZE(board_info.type)); + board_info.swnode = &sensor->swnodes[SWNODE_NVM]; + + printk("liang: %s:board_info name %s, board_info.type = %s \n", __func__, board_info.dev_name, board_info.type); + sensor->nvm_i2c_client = + i2c_acpi_new_device_by_fwnode(acpi_fwnode_handle(sensor->adev), + 2, &board_info); + if (IS_ERR(sensor->nvm_i2c_client)) { + dev_warn(&sensor->adev->dev, + "Error instantiation NVM i2c-client: %ld\n", + PTR_ERR(sensor->nvm_i2c_client)); + sensor->nvm_i2c_client = NULL; + } + +/** printk("liang: %s: create firmware_node\n", __func__); + printk("liang: %s:physical node = %s\n", __func__, (acpi_get_first_physical_node(sensor->adev))->type->name); + retval = sysfs_create_link(&dev->kobj, &sensor->adev->dev.kobj, + "firmware_node"); + if (retval) + dev_err(dev, "Failed to create link firmware_node (%d)\n", + retval); +*/ +} + static void cio2_bridge_unregister_sensors(struct cio2_bridge *bridge) { struct cio2_sensor *sensor; @@ -292,6 +378,7 @@ static void cio2_bridge_unregister_sensors(struct cio2_bridge *bridge) ACPI_FREE(sensor->pld); acpi_dev_put(sensor->adev); i2c_unregister_device(sensor->vcm_i2c_client); + i2c_unregister_device(sensor->nvm_i2c_client); } } @@ -330,6 +417,14 @@ static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg, sensor->ssdb.vcmtype = 0; } + printk("liang: %s:sensor->ssdb.romtype = %d\n", __func__, sensor->ssdb.romtype); + if (sensor->ssdb.romtype > ARRAY_SIZE(cio2_nvm_types)) { + dev_warn(&adev->dev, "Unknown NVM type %d\n", + sensor->ssdb.romtype); + sensor->ssdb.romtype = 0; + } + + printk("liang: %s: %d \n", __func__, __LINE__); status = acpi_get_physical_device_location(adev->handle, &sensor->pld); if (ACPI_FAILURE(status)) { @@ -364,6 +459,7 @@ static int cio2_bridge_connect_sensor(const struct cio2_sensor_config *cfg, current_fwnode->secondary = fwnode; cio2_bridge_instantiate_vcm_i2c_client(sensor); + cio2_bridge_instantiate_nvm_i2c_client(sensor, cio2); dev_info(&cio2->dev, "Found supported sensor %s\n", acpi_dev_name(adev)); diff --git a/drivers/media/pci/intel/cio2-bridge.h b/drivers/media/pci/intel/cio2-bridge.h index a0c99ba8f700..d5ba8bebc85f 100644 --- a/drivers/media/pci/intel/cio2-bridge.h +++ b/drivers/media/pci/intel/cio2-bridge.h @@ -59,6 +59,7 @@ enum cio2_sensor_swnodes { SWNODE_CIO2_ENDPOINT, /* Must be last because it is optional / maybe empty */ SWNODE_VCM, + SWNODE_NVM, SWNODE_COUNT }; @@ -118,6 +119,7 @@ struct cio2_sensor { char name[ACPI_ID_LEN]; struct acpi_device *adev; struct i2c_client *vcm_i2c_client; + struct i2c_client *nvm_i2c_client; /* SWNODE_COUNT + 1 for terminating empty node */ struct software_node swnodes[SWNODE_COUNT + 1]; @@ -128,11 +130,12 @@ struct cio2_sensor { struct cio2_property_names prop_names; struct property_entry ep_properties[5]; - struct property_entry dev_properties[5]; + struct property_entry dev_properties[6]; struct property_entry cio2_properties[3]; struct software_node_ref_args local_ref[1]; struct software_node_ref_args remote_ref[1]; struct software_node_ref_args vcm_ref[1]; + struct software_node_ref_args nvm_ref[1]; }; struct cio2_bridge { diff --git a/include/uapi/linux/rk-camera-module.h b/include/uapi/linux/rk-camera-module.h new file mode 100644 index 000000000000..008b2355efe7 --- /dev/null +++ b/include/uapi/linux/rk-camera-module.h @@ -0,0 +1,763 @@ +/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR MIT) */ +/* + * Rockchip module information + * Copyright (C) 2018-2019 Rockchip Electronics Co., Ltd. + */ + +#ifndef _UAPI_RKMODULE_CAMERA_H +#define _UAPI_RKMODULE_CAMERA_H + +#include +#include + +#define RKMODULE_API_VERSION KERNEL_VERSION(0, 1, 0x2) + +/* using for rk3588 dual isp unite */ +#define RKMOUDLE_UNITE_EXTEND_PIXEL 32 +/* using for rv1109 and rv1126 */ +#define RKMODULE_EXTEND_LINE 24 + +#define RKMODULE_NAME_LEN 32 +#define RKMODULE_LSCDATA_LEN 289 + +#define RKMODULE_MAX_VC_CH 4 + +#define RKMODULE_PADF_GAINMAP_LEN 1024 +#define RKMODULE_PDAF_DCCMAP_LEN 256 +#define RKMODULE_AF_OTP_MAX_LEN 3 + +#define RKMODULE_MAX_SENSOR_NUM 8 + +#define RKMODULE_CAMERA_MODULE_INDEX "rockchip,camera-module-index" +#define RKMODULE_CAMERA_MODULE_FACING "rockchip,camera-module-facing" +#define RKMODULE_CAMERA_MODULE_NAME "rockchip,camera-module-name" +#define RKMODULE_CAMERA_LENS_NAME "rockchip,camera-module-lens-name" + +#define RKMODULE_CAMERA_SYNC_MODE "rockchip,camera-module-sync-mode" +#define RKMODULE_INTERNAL_MASTER_MODE "internal_master" +#define RKMODULE_EXTERNAL_MASTER_MODE "external_master" +#define RKMODULE_SLAVE_MODE "slave" + +/* BT.656 & BT.1120 multi channel + * On which channels it can send video data + * related with struct rkmodule_bt656_mbus_info + */ +#define RKMODULE_CAMERA_BT656_ID_EN_BITS_1 (0x1) +#define RKMODULE_CAMERA_BT656_ID_EN_BITS_2 (0x3) +#define RKMODULE_CAMERA_BT656_ID_EN_BITS_3 (0x7) +#define RKMODULE_CAMERA_BT656_ID_EN_BITS_4 (0xf) +#define RKMODULE_CAMERA_BT656_PARSE_ID_LSB BIT(0) +#define RKMODULE_CAMERA_BT656_PARSE_ID_MSB BIT(1) +#define RKMODULE_CAMERA_BT656_CHANNEL_0 BIT(2) +#define RKMODULE_CAMERA_BT656_CHANNEL_1 BIT(3) +#define RKMODULE_CAMERA_BT656_CHANNEL_2 BIT(4) +#define RKMODULE_CAMERA_BT656_CHANNEL_3 BIT(5) +#define RKMODULE_CAMERA_BT656_CHANNELS (RKMODULE_CAMERA_BT656_CHANNEL_0 | \ + RKMODULE_CAMERA_BT656_CHANNEL_1 | \ + RKMODULE_CAMERA_BT656_CHANNEL_2 | \ + RKMODULE_CAMERA_BT656_CHANNEL_3) + +#define DPHY_MAX_LANE 4 + +#define RKMODULE_GET_MODULE_INFO \ + _IOR('V', BASE_VIDIOC_PRIVATE + 0, struct rkmodule_inf) + +#define RKMODULE_AWB_CFG \ + _IOW('V', BASE_VIDIOC_PRIVATE + 1, struct rkmodule_awb_cfg) + +#define RKMODULE_AF_CFG \ + _IOW('V', BASE_VIDIOC_PRIVATE + 2, struct rkmodule_af_cfg) + +#define RKMODULE_LSC_CFG \ + _IOW('V', BASE_VIDIOC_PRIVATE + 3, struct rkmodule_lsc_cfg) + +#define RKMODULE_GET_HDR_CFG \ + _IOR('V', BASE_VIDIOC_PRIVATE + 4, struct rkmodule_hdr_cfg) + +#define RKMODULE_SET_HDR_CFG \ + _IOW('V', BASE_VIDIOC_PRIVATE + 5, struct rkmodule_hdr_cfg) + +#define RKMODULE_SET_CONVERSION_GAIN \ + _IOW('V', BASE_VIDIOC_PRIVATE + 6, __u32) + +#define RKMODULE_GET_LVDS_CFG \ + _IOR('V', BASE_VIDIOC_PRIVATE + 7, struct rkmodule_lvds_cfg) + +#define RKMODULE_SET_DPCC_CFG \ + _IOW('V', BASE_VIDIOC_PRIVATE + 8, struct rkmodule_dpcc_cfg) + +#define RKMODULE_GET_NR_SWITCH_THRESHOLD \ + _IOR('V', BASE_VIDIOC_PRIVATE + 9, struct rkmodule_nr_switch_threshold) + +#define RKMODULE_SET_QUICK_STREAM \ + _IOW('V', BASE_VIDIOC_PRIVATE + 10, __u32) + +#define RKMODULE_GET_BT656_INTF_TYPE \ + _IOR('V', BASE_VIDIOC_PRIVATE + 11, __u32) + +#define RKMODULE_GET_VC_FMT_INFO \ + _IOR('V', BASE_VIDIOC_PRIVATE + 12, struct rkmodule_vc_fmt_info) + +#define RKMODULE_GET_VC_HOTPLUG_INFO \ + _IOR('V', BASE_VIDIOC_PRIVATE + 13, struct rkmodule_vc_hotplug_info) + +#define RKMODULE_GET_START_STREAM_SEQ \ + _IOR('V', BASE_VIDIOC_PRIVATE + 14, __u32) + +#define RKMODULE_GET_VICAP_RST_INFO \ + _IOR('V', BASE_VIDIOC_PRIVATE + 15, struct rkmodule_vicap_reset_info) + +#define RKMODULE_SET_VICAP_RST_INFO \ + _IOW('V', BASE_VIDIOC_PRIVATE + 16, struct rkmodule_vicap_reset_info) + +#define RKMODULE_GET_BT656_MBUS_INFO \ + _IOR('V', BASE_VIDIOC_PRIVATE + 17, struct rkmodule_bt656_mbus_info) + +#define RKMODULE_GET_DCG_RATIO \ + _IOR('V', BASE_VIDIOC_PRIVATE + 18, struct rkmodule_dcg_ratio) + +#define RKMODULE_GET_SONY_BRL \ + _IOR('V', BASE_VIDIOC_PRIVATE + 19, __u32) + +#define RKMODULE_GET_CHANNEL_INFO \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 20, struct rkmodule_channel_info) + +#define RKMODULE_GET_SYNC_MODE \ + _IOR('V', BASE_VIDIOC_PRIVATE + 21, __u32) + +#define RKMODULE_SET_SYNC_MODE \ + _IOW('V', BASE_VIDIOC_PRIVATE + 22, __u32) + +#define RKMODULE_SET_MCLK \ + _IOW('V', BASE_VIDIOC_PRIVATE + 23, struct rkmodule_mclk_data) + +#define RKMODULE_SET_LINK_FREQ \ + _IOW('V', BASE_VIDIOC_PRIVATE + 24, __s64) + +#define RKMODULE_SET_BUS_CONFIG \ + _IOW('V', BASE_VIDIOC_PRIVATE + 25, struct rkmodule_bus_config) + +#define RKMODULE_GET_BUS_CONFIG \ + _IOR('V', BASE_VIDIOC_PRIVATE + 26, struct rkmodule_bus_config) + +#define RKMODULE_SET_REGISTER \ + _IOW('V', BASE_VIDIOC_PRIVATE + 27, struct rkmodule_reg) + +#define RKMODULE_SYNC_I2CDEV \ + _IOW('V', BASE_VIDIOC_PRIVATE + 28, __u8) + +#define RKMODULE_SYNC_I2CDEV_COMPLETE \ + _IOW('V', BASE_VIDIOC_PRIVATE + 29, __u8) + +#define RKMODULE_SET_DEV_INFO \ + _IOW('V', BASE_VIDIOC_PRIVATE + 30, struct rkmodule_dev_info) + +#define RKMODULE_SET_CSI_DPHY_PARAM \ + _IOW('V', BASE_VIDIOC_PRIVATE + 31, struct rkmodule_csi_dphy_param) + +#define RKMODULE_GET_CSI_DPHY_PARAM \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 32, struct rkmodule_csi_dphy_param) + +#define RKMODULE_GET_CSI_DSI_INFO \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 33, __u32) + +#define RKMODULE_GET_HDMI_MODE \ + _IOR('V', BASE_VIDIOC_PRIVATE + 34, __u32) + +#define RKMODULE_SET_SENSOR_INFOS \ + _IOWR('V', BASE_VIDIOC_PRIVATE + 35, struct rkmodule_sensor_infos) + +#define RKMODULE_GET_READOUT_LINE_CNT_PER_LINE \ + _IOR('V', BASE_VIDIOC_PRIVATE + 36, __u32) + +struct rkmodule_i2cdev_info { + u8 slave_addr; +} __attribute__ ((packed)); + +struct rkmodule_dev_info { + union { + struct rkmodule_i2cdev_info i2c_dev; + u32 reserved[8]; + }; +} __attribute__ ((packed)); + +/* csi0/csi1 phy support full/split mode */ +enum rkmodule_phy_mode { + PHY_FULL_MODE, + PHY_SPLIT_01, + PHY_SPLIT_23, +}; + +struct rkmodule_mipi_lvds_bus { + __u32 bus_type; + __u32 lanes; + __u32 phy_mode; /* data type enum rkmodule_phy_mode */ +}; + +struct rkmodule_bus_config { + union { + struct rkmodule_mipi_lvds_bus bus; + __u32 reserved[32]; + }; +} __attribute__ ((packed)); + +struct rkmodule_reg { + __u64 num_regs; + __u64 preg_addr; + __u64 preg_value; + __u64 preg_addr_bytes; + __u64 preg_value_bytes; +} __attribute__ ((packed)); + +/** + * struct rkmodule_base_inf - module base information + * + */ +struct rkmodule_base_inf { + char sensor[RKMODULE_NAME_LEN]; + char module[RKMODULE_NAME_LEN]; + char lens[RKMODULE_NAME_LEN]; +} __attribute__ ((packed)); + +/** + * struct rkmodule_fac_inf - module factory information + * + */ +struct rkmodule_fac_inf { + __u32 flag; + + char module[RKMODULE_NAME_LEN]; + char lens[RKMODULE_NAME_LEN]; + __u32 year; + __u32 month; + __u32 day; +} __attribute__ ((packed)); + +/** + * struct rkmodule_awb_inf - module awb information + * + */ +struct rkmodule_awb_inf { + __u32 flag; + + __u32 r_value; + __u32 b_value; + __u32 gr_value; + __u32 gb_value; + + __u32 golden_r_value; + __u32 golden_b_value; + __u32 golden_gr_value; + __u32 golden_gb_value; +} __attribute__ ((packed)); + +/** + * struct rkmodule_lsc_inf - module lsc information + * + */ +struct rkmodule_lsc_inf { + __u32 flag; + + __u16 lsc_w; + __u16 lsc_h; + __u16 decimal_bits; + + __u16 lsc_r[RKMODULE_LSCDATA_LEN]; + __u16 lsc_b[RKMODULE_LSCDATA_LEN]; + __u16 lsc_gr[RKMODULE_LSCDATA_LEN]; + __u16 lsc_gb[RKMODULE_LSCDATA_LEN]; + + __u16 width; + __u16 height; + __u16 table_size; +} __attribute__ ((packed)); + +/** + * enum rkmodule_af_dir - enum of module af otp direction + */ +enum rkmodele_af_otp_dir { + AF_OTP_DIR_HORIZONTAL = 0, + AF_OTP_DIR_UP = 1, + AF_OTP_DIR_DOWN = 2, +}; + +/** + * struct rkmodule_af_otp - module af otp in one direction + */ +struct rkmodule_af_otp { + __u32 vcm_start; + __u32 vcm_end; + __u32 vcm_dir; +}; + +/** + * struct rkmodule_af_inf - module af information + * + */ +struct rkmodule_af_inf { + __u32 flag; + __u32 dir_cnt; + struct rkmodule_af_otp af_otp[RKMODULE_AF_OTP_MAX_LEN]; +} __attribute__ ((packed)); + +/** + * struct rkmodule_pdaf_inf - module pdaf information + * + */ +struct rkmodule_pdaf_inf { + __u32 flag; + + __u32 gainmap_width; + __u32 gainmap_height; + __u32 dccmap_width; + __u32 dccmap_height; + __u32 dcc_mode; + __u32 dcc_dir; + __u16 gainmap[RKMODULE_PADF_GAINMAP_LEN]; + __u16 dccmap[RKMODULE_PDAF_DCCMAP_LEN]; +} __attribute__ ((packed)); + +/** + * struct rkmodule_otp_module_inf - otp module info + * + */ +struct rkmodule_otp_module_inf { + __u32 flag; + __u8 vendor[8]; + __u32 module_id; + __u16 version; + __u16 full_width; + __u16 full_height; + __u8 supplier_id; + __u8 year; + __u8 mouth; + __u8 day; + __u8 sensor_id; + __u8 lens_id; + __u8 vcm_id; + __u8 drv_id; + __u8 flip; +} __attribute__ ((packed)); + +/** + * struct rkmodule_inf - module information + * + */ +struct rkmodule_inf { + struct rkmodule_base_inf base; + struct rkmodule_fac_inf fac; + struct rkmodule_awb_inf awb; + struct rkmodule_lsc_inf lsc; + struct rkmodule_af_inf af; + struct rkmodule_pdaf_inf pdaf; + struct rkmodule_otp_module_inf module_inf; +} __attribute__ ((packed)); + +/** + * struct rkmodule_awb_inf - module awb information + * + */ +struct rkmodule_awb_cfg { + __u32 enable; + __u32 golden_r_value; + __u32 golden_b_value; + __u32 golden_gr_value; + __u32 golden_gb_value; +} __attribute__ ((packed)); + +/** + * struct rkmodule_af_cfg + * + */ +struct rkmodule_af_cfg { + __u32 enable; + __u32 vcm_start; + __u32 vcm_end; + __u32 vcm_dir; +} __attribute__ ((packed)); + +/** + * struct rkmodule_lsc_cfg + * + */ +struct rkmodule_lsc_cfg { + __u32 enable; +} __attribute__ ((packed)); + +/** + * NO_HDR: linear mode + * HDR_X2: hdr two frame or line mode + * HDR_X3: hdr three or line mode + * HDR_COMPR: linearised and compressed data for hdr + */ +enum rkmodule_hdr_mode { + NO_HDR = 0, + HDR_X2 = 5, + HDR_X3 = 6, + HDR_COMPR, +}; + +enum rkmodule_hdr_compr_segment { + HDR_COMPR_SEGMENT_4 = 4, + HDR_COMPR_SEGMENT_12 = 12, + HDR_COMPR_SEGMENT_16 = 16, +}; + +/* rkmodule_hdr_compr + * linearised and compressed data for hdr: data_src = K * data_compr + XX + * + * bit: bit of src data, max 20 bit. + * segment: linear segment, support 4, 6 or 16. + * k_shift: left shift bit of slop amplification factor, 2^k_shift, [0 15]. + * slope_k: K * 2^k_shift. + * data_src_shitf: left shift bit of source data, data_src = 2^data_src_shitf + * data_compr: compressed data. + */ +struct rkmodule_hdr_compr { + enum rkmodule_hdr_compr_segment segment; + __u8 bit; + __u8 k_shift; + __u8 data_src_shitf[HDR_COMPR_SEGMENT_16]; + __u16 data_compr[HDR_COMPR_SEGMENT_16]; + __u32 slope_k[HDR_COMPR_SEGMENT_16]; +}; + +/** + * HDR_NORMAL_VC: hdr frame with diff virtual channels + * HDR_LINE_CNT: hdr frame with line counter + * HDR_ID_CODE: hdr frame with identification code + */ +enum hdr_esp_mode { + HDR_NORMAL_VC = 0, + HDR_LINE_CNT, + HDR_ID_CODE, +}; + +/* + * CSI/DSI input select IOCTL + */ +enum rkmodule_csi_dsi_seq { + RKMODULE_CSI_INPUT = 0, + RKMODULE_DSI_INPUT, +}; + +/** + * lcnt: line counter + * padnum: the pixels of padding row + * padpix: the payload of padding + * idcd: identification code + * efpix: identification code of Effective line + * obpix: identification code of OB line + */ +struct rkmodule_hdr_esp { + enum hdr_esp_mode mode; + union { + struct { + __u32 padnum; + __u32 padpix; + } lcnt; + struct { + __u32 efpix; + __u32 obpix; + } idcd; + } val; +}; + +struct rkmodule_hdr_cfg { + __u32 hdr_mode; + struct rkmodule_hdr_esp esp; + struct rkmodule_hdr_compr compr; +} __attribute__ ((packed)); + +/* sensor lvds sync code + * sav: start of active video codes + * eav: end of active video codes + */ +struct rkmodule_sync_code { + __u16 sav; + __u16 eav; +}; + +/* sensor lvds difference sync code mode + * LS_FIRST: valid line ls-le or sav-eav + * invalid line fs-fe or sav-eav + * FS_FIRST: valid line fs-le + * invalid line ls-fe + * ls: line start + * le: line end + * fs: frame start + * fe: frame end + * SONY_DOL_HDR_1: sony dol hdr pattern 1 + * SONY_DOL_HDR_2: sony dol hdr pattern 2 + */ +enum rkmodule_lvds_mode { + LS_FIRST = 0, + FS_FIRST, + SONY_DOL_HDR_1, + SONY_DOL_HDR_2 +}; + +/* sync code of different frame type (hdr or linear) for lvds + * act: valid line sync code + * blk: invalid line sync code + */ +struct rkmodule_lvds_frm_sync_code { + struct rkmodule_sync_code act; + struct rkmodule_sync_code blk; +}; + +/* sync code for lvds of sensor + * odd_sync_code: sync code of odd frame id for lvds of sony sensor + * even_sync_code: sync code of even frame id for lvds of sony sensor + */ +struct rkmodule_lvds_frame_sync_code { + struct rkmodule_lvds_frm_sync_code odd_sync_code; + struct rkmodule_lvds_frm_sync_code even_sync_code; +}; + +/* lvds sync code category of sensor for different operation */ +enum rkmodule_lvds_sync_code_group { + LVDS_CODE_GRP_LINEAR = 0x0, + LVDS_CODE_GRP_LONG, + LVDS_CODE_GRP_MEDIUM, + LVDS_CODE_GRP_SHORT, + LVDS_CODE_GRP_MAX +}; + +/* struct rkmodule_lvds_cfg + * frm_sync_code[index]: + * index == LVDS_CODE_GRP_LONG: + * sync code for frame of linear mode or for long frame of hdr mode + * index == LVDS_CODE_GRP_MEDIUM: + * sync code for medium long frame of hdr mode + * index == LVDS_CODE_GRP_SHOR: + * sync code for short long frame of hdr mode + */ +struct rkmodule_lvds_cfg { + enum rkmodule_lvds_mode mode; + struct rkmodule_lvds_frame_sync_code frm_sync_code[LVDS_CODE_GRP_MAX]; +} __attribute__ ((packed)); + +/** + * struct rkmodule_dpcc_cfg + * enable: 0 -> disable dpcc, 1 -> enable multiple, + * 2 -> enable single, 3 -> enable all; + * cur_single_dpcc: the strength of single dpcc; + * cur_multiple_dpcc: the strength of multiple dpcc; + * total_dpcc: the max strength; + */ +struct rkmodule_dpcc_cfg { + __u32 enable; + __u32 cur_single_dpcc; + __u32 cur_multiple_dpcc; + __u32 total_dpcc; +} __attribute__ ((packed)); + +/** + * nr switch by gain + * direct: 0 -> up_thres LSNR to HSNR, 1 -> up_thres HSNR to LSNR + * up_thres: threshold of nr change from low gain to high gain + * down_thres: threshold of nr change from high gain to low gain; + * div_coeff: Coefficients converted from float to int + */ +struct rkmodule_nr_switch_threshold { + __u32 direct; + __u32 up_thres; + __u32 down_thres; + __u32 div_coeff; +} __attribute__ ((packed)); + +/** + * enum rkmodule_bt656_intf_type + * to support sony bt656 raw + */ +enum rkmodule_bt656_intf_type { + BT656_STD_RAW = 0, + BT656_SONY_RAW, +}; + +/** + * struct rkmodule_vc_fmt_info - virtual channels fmt info + * + */ +struct rkmodule_vc_fmt_info { + __u32 width[RKMODULE_MAX_VC_CH]; + __u32 height[RKMODULE_MAX_VC_CH]; + __u32 fps[RKMODULE_MAX_VC_CH]; +} __attribute__ ((packed)); + +/** + * struct rkmodule_vc_hotplug_info - virtual channels hotplug status info + * detect_status: hotplug status + * bit 0~3 means channels id, value : 0 -> plug out, 1 -> plug in. + */ +struct rkmodule_vc_hotplug_info { + __u8 detect_status; +} __attribute__ ((packed)); + + +/* sensor start stream sequence + * RKMODULE_START_STREAM_DEFAULT: by default + * RKMODULE_START_STREAM_BEHIND : sensor start stream should be behind the controller + * RKMODULE_START_STREAM_FRONT : sensor start stream should be in front of the controller + */ +enum rkmodule_start_stream_seq { + RKMODULE_START_STREAM_DEFAULT = 0, + RKMODULE_START_STREAM_BEHIND, + RKMODULE_START_STREAM_FRONT, +}; + +/* + * HDMI to MIPI-CSI MODE IOCTL + */ +enum rkmodule_hdmiin_mode_seq { + RKMODULE_HDMIIN_DEFAULT = 0, + RKMODULE_HDMIIN_MODE, +}; +/* + * the causation to do cif reset work + */ +enum rkmodule_reset_src { + RKCIF_RESET_SRC_NON = 0x0, + RKCIF_RESET_SRC_ERR_CSI2, + RKCIF_RESET_SRC_ERR_LVDS, + RKICF_RESET_SRC_ERR_CUTOFF, + RKCIF_RESET_SRC_ERR_HOTPLUG, + RKCIF_RESET_SRC_ERR_APP, +}; + +struct rkmodule_vicap_reset_info { + __u32 is_reset; + enum rkmodule_reset_src src; +} __attribute__ ((packed)); + +struct rkmodule_bt656_mbus_info { + __u32 flags; + __u32 id_en_bits; +} __attribute__ ((packed)); + +/* DCG ratio (float) = integer + decimal / div_coeff */ +struct rkmodule_dcg_ratio { + __u32 integer; + __u32 decimal; + __u32 div_coeff; +}; + +struct rkmodule_channel_info { + __u32 index; + __u32 vc; + __u32 width; + __u32 height; + __u32 bus_fmt; + __u32 data_type; + __u32 data_bit; +} __attribute__ ((packed)); + +/* + * link to vicap + * linear mode: pad0~pad3 for id0~id3; + * + * HDR_X2: id0 fiexd to vc0 for long frame + * id1 fixed to vc1 for short frame; + * id2~id3 reserved, can config by PAD2~PAD3 + * + * HDR_X3: id0 fiexd to vc0 for long frame + * id1 fixed to vc1 for middle frame + * id2 fixed to vc2 for short frame; + * id3 reserved, can config by PAD3 + * + * link to isp, the connection relationship is as follows + */ +enum rkmodule_max_pad { + PAD0, /* link to isp */ + PAD1, /* link to csi wr0 | hdr x2:L x3:M */ + PAD2, /* link to csi wr1 | hdr x3:L */ + PAD3, /* link to csi wr2 | hdr x2:M x3:S */ + PAD_MAX, +}; + +/* + * sensor exposure sync mode + */ +enum rkmodule_sync_mode { + NO_SYNC_MODE = 0, + EXTERNAL_MASTER_MODE, + INTERNAL_MASTER_MODE, + SLAVE_MODE, +}; + +struct rkmodule_mclk_data { + u32 enable; + u32 mclk_index; + u32 mclk_rate; + u32 reserved[8]; +}; + +/* + * csi dphy param + * lp_vol_ref -> Reference voltage-645mV for LP Function control pin + * for rk3588 dcphy + * 3'b000 : 605mV + * 3'b001 : 625mV + * 3'b010 : 635mV + * 3'b011 : 645mV + * 3'b100 : 655mV + * 3'b101 : 665mV + * 3'b110 : 685mV + * 3'b111 : 725mV + * + * lp_hys_sw -> LP-RX Hysteresis Level Control + * for rk3588 dcphy + * 2'b00=45mV + * 2'b01=65mV + * 2'b10=85mV + * 2'b11=100mV + * + * lp_escclk_pol_sel -> LP ESCCLK Polarity sel + * for rk3588 dcphy + * 1'b0: normal + * 1'b1: swap ,Increase 1ns delay + * + * skew_data_cal_clk -> Skew Calibration Manual Data Fine Delay Control Register + * for rk3588 dcphy + * BIT[4:0] 30ps a step + * + * clk_hs_term_sel/data_hs_term_sel -> HS-RX Termination Impedance Control + * for rk3588 dcphy + * 3b'000 : 102Ω + * 3b'001 : 99.1Ω + * 3b'010 : 96.6Ω (default) + * 3b'011 : 94.1Ω + * 3b'100 : 113Ω + * 3b'101 : 110Ω + * 3b'110 : 107Ω + * 3b'111 : 104Ω + */ + +enum csi2_dphy_vendor { + PHY_VENDOR_INNO = 0x0, + PHY_VENDOR_SAMSUNG = 0x01, +}; + +struct rkmodule_csi_dphy_param { + u32 vendor; + u32 lp_vol_ref; + u32 lp_hys_sw[DPHY_MAX_LANE]; + u32 lp_escclk_pol_sel[DPHY_MAX_LANE]; + u32 skew_data_cal_clk[DPHY_MAX_LANE]; + u32 clk_hs_term_sel; + u32 data_hs_term_sel[DPHY_MAX_LANE]; + u32 reserved[32]; +}; + +struct rkmodule_sensor_fmt { + __u32 sensor_index; + __u32 sensor_width; + __u32 sensor_height; +}; + +struct rkmodule_sensor_infos { + struct rkmodule_sensor_fmt sensor_fmt[RKMODULE_MAX_SENSOR_NUM]; +}; + +#endif /* _UAPI_RKMODULE_CAMERA_H */ diff --git a/include/uapi/linux/rk-video-format.h b/include/uapi/linux/rk-video-format.h new file mode 100644 index 000000000000..7f60140d47cc --- /dev/null +++ b/include/uapi/linux/rk-video-format.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: (GPL-2.0+ WITH Linux-syscall-note) OR MIT + * + * Copyright (C) 2021 Rockchip Electronics Co., Ltd. + */ +#ifndef _UAPI_RK_VIDEO_FORMAT_H +#define _UAPI_RK_VIDEO_FORMAT_H + +/* Four-character-code (FOURCC) */ +#define v4l2_fourcc(a, b, c, d)\ + ((__u32)(a) | ((__u32)(b) << 8) | ((__u32)(c) << 16) | ((__u32)(d) << 24)) +#define v4l2_fourcc_be(a, b, c, d) (v4l2_fourcc(a, b, c, d) | (1U << 31)) + +/* Rockchip yuv422sp frame buffer compression encoder */ +#define V4L2_PIX_FMT_FBC2 v4l2_fourcc('F', 'B', 'C', '2') +/* Rockchip yuv420sp frame buffer compression encoder */ +#define V4L2_PIX_FMT_FBC0 v4l2_fourcc('F', 'B', 'C', '0') +#define V4L2_PIX_FMT_FBCG v4l2_fourcc('F', 'B', 'C', 'G') +/* embedded data 8-bit */ +#define V4l2_PIX_FMT_EBD8 v4l2_fourcc('E', 'B', 'D', '8') +/* shield pix data 16-bit */ +#define V4l2_PIX_FMT_SPD16 v4l2_fourcc('S', 'P', 'D', '6') + +/* Vendor specific - used for Rockchip ISP1 camera sub-system */ +#define V4L2_META_FMT_RK_ISP1_PARAMS v4l2_fourcc('R', 'K', '1', 'P') /* Rockchip ISP1 params */ +#define V4L2_META_FMT_RK_ISP1_STAT_3A v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A statistics */ +#define V4L2_META_FMT_RK_ISP1_STAT_LUMA v4l2_fourcc('R', 'K', '1', 'L') /* Rockchip ISP1 luma statistics */ +#define V4L2_META_FMT_RK_ISPP_PARAMS v4l2_fourcc('R', 'K', 'P', 'P') /* Rockchip ISPP params */ +#define V4L2_META_FMT_RK_ISPP_STAT v4l2_fourcc('R', 'K', 'P', 'S') /* Rockchip ISPP statistics */ + +/* sensor embedded data format */ +#define MEDIA_BUS_FMT_EBD_1X8 0x5002 +/* sensor shield pix data format */ +#define MEDIA_BUS_FMT_SPD_2X8 0x5003 + +#endif /* _UAPI_RK_VIDEO_FORMAT_H */ diff --git a/patch.diff b/patch.diff index 2e0e7386c377..8519c02fc678 100644 --- a/patch.diff +++ b/patch.diff @@ -473,6 +473,12 @@ index ed4c9d760757..8b39d6656502 100644 + { "CJFLE23", { 0 }, NULL, true }, + /* OV13B10 */ + { "09B13", { 0 }, NULL, true }, ++ /* HIMX1092 */ ++ { "KPFB297", { 0 }, NULL, true }, ++ /* GC5035 */ ++ { "CJAK519", { 0 }, NULL, true }, ++ /* S5K3L6 */ ++ { "KBAG296", { 0 }, NULL, true }, }; static const struct int3472_sensor_config *