From 591bbc813d2d1e0f44158bfd14704dbd3522437a Mon Sep 17 00:00:00 2001 From: "liang1.wang" Date: Mon, 27 Feb 2023 14:01:40 +0800 Subject: [PATCH] Add two sensors gc5035 and s5k3l6 for ADL-M. Both drivers are based on online, but update to support x86 and add gpio pins support. Integrate IPU6 driver to support the new sensors. The original gc5035 is from the link: https://patchwork.kernel.org/project/linux-media/patch/20200902224813.14283-4-tfiga@chromium.org The original s5k3l6 is from github-kernel_fruitPi_5.10. With the patches, i2cdetected: :~$ sudo i2cdetect -r -y 3 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- UU -- -- --//vcm 10: UU -- -- -- -- -- -- -- -- -- -- -- -- -- -- --//s5k3l6 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: UU -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //nvm 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- :~$ sudo i2cdetect -r -y 2 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- UU -- -- -- -- -- -- -- -- //gc5035 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- Regarding to kernel update, don't forget to add the new sensors support in .config, Kconfig and Makefile. Signed-off-by: liang1.wang --- Makefile | 6 + drivers/media/i2c/Makefile | 5 +- drivers/media/i2c/gc5035.c | 2125 +++++++++++++++++++++++++ drivers/media/i2c/s5k3l6.c | 1701 ++++++++++++++++++++ drivers/media/pci/intel/cio2-bridge.c | 96 ++ drivers/media/pci/intel/cio2-bridge.h | 5 +- include/uapi/linux/rk-camera-module.h | 763 +++++++++ include/uapi/linux/rk-video-format.h | 35 + patch.diff | 6 + 9 files changed, 4740 insertions(+), 2 deletions(-) create mode 100644 drivers/media/i2c/gc5035.c create mode 100644 drivers/media/i2c/s5k3l6.c create mode 100644 include/uapi/linux/rk-camera-module.h create mode 100644 include/uapi/linux/rk-video-format.h 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 *