From 642d58f22bcf08f9686686d1f140013c02757ff0 Mon Sep 17 00:00:00 2001 From: shingo <2877898@163.com> Date: Mon, 24 Jun 2024 08:48:03 +0800 Subject: [PATCH 01/13] =?UTF-8?q?=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20?= =?UTF-8?q?=20=20.version=20=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20a?= =?UTF-8?q?rch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20arc?= =?UTF-8?q?h/arm64/configs/lubancat=5Flinux=5Frk3588=5Fdefconfig=20=09?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20drivers/media/i2c/Kco?= =?UTF-8?q?nfig=20=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20drivers/med?= =?UTF-8?q?ia/i2c/Makefile=20=09=E6=96=B0=E6=96=87=E4=BB=B6=EF=BC=9A=20=20?= =?UTF-8?q?=20drivers/media/i2c/gc5603.c=20=09=E4=BF=AE=E6=94=B9=EF=BC=9A?= =?UTF-8?q?=20=20=20=20=20drivers/phy/rockchip/phy-rockchip-csi2-dphy.c=20?= =?UTF-8?q?=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20logo.bmp=20=09?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20logo=5Fkernel.bmp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .version | 2 +- .../dts/rockchip/rk3588s-lubancat-csi2.dtsi | 188 +- .../configs/lubancat_linux_rk3588_defconfig | 10 +- drivers/media/i2c/Kconfig | 11 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/gc5603.c | 1610 +++++++++++++++++ drivers/phy/rockchip/phy-rockchip-csi2-dphy.c | 4 +- logo.bmp | Bin 810656 -> 1215954 bytes logo_kernel.bmp | Bin 810656 -> 1215954 bytes 9 files changed, 1787 insertions(+), 39 deletions(-) create mode 100644 drivers/media/i2c/gc5603.c diff --git a/.version b/.version index 7ed6ff82de6bc..d00491fd7e5bb 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -5 +1 diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi index b1807f9e7f036..692c72f0043c3 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi @@ -11,6 +11,12 @@ clock-output-names = "ext_cam_37m_clk"; #clock-cells = <0>; }; + ext_cam_27m_clk: external-camera-27m-clock { + compatible = "fixed-clock"; + clock-frequency = <27000000>; + clock-output-names = "ext_cam_27m_clk"; + #clock-cells = <0>; + }; vdd_cam_5v: vdd-cam-5v-regulator { compatible = "regulator-fixed"; @@ -54,13 +60,45 @@ &i2c1 { - status = "disabled"; + status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&i2c1m2_xfer>; + dw9714_0: dw9714-0@c { + status = "okay"; + compatible = "dongwoon,dw9714"; + reg = <0xc>; + rockchip,camera-module-index = <0>; + rockchip,vcm-max-current = <100>; + rockchip,vcm-start-current = <0>; + rockchip,vcm-rated-current = <100>; + rockchip,vcm-step-mode = <0xd>; + rockchip,vcm-dlc-enable = <0>; + rockchip,vcm-mclk = <0>; + rockchip,vcm-t-src = <0>; + rockchip,camera-module-facing = "back"; + }; + + gc5603_0: gc5603-0@31 { + compatible = "galaxycore,gc5603"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_27m_clk>; + clock-names = "ext_cam_27m_clk"; + pwdn-gpios = <&gpio1 RK_PA3 GPIO_ACTIVE_LOW>; + rotation = <180>; + + port { + gc5603_out0: endpoint { + remote-endpoint = <&dcphy0_in_gc5603>; + data-lanes = <1 2>; + }; + }; + }; + imx415_0: imx415-0@1a { compatible = "sony,imx415"; - status = "disabled"; + status = "okay"; reg = <0x1a>; clocks = <&ext_cam_37m_clk>; clock-names = "xvclk"; @@ -83,11 +121,11 @@ }; &mipi_dcphy0 { - status = "disabled"; + status = "okay"; }; &csi2_dcphy0 { - status = "disabled"; + status = "okay"; ports { #address-cells = <1>; @@ -98,8 +136,14 @@ #address-cells = <1>; #size-cells = <0>; - dcphy0_in_imx415: endpoint@0 { + dcphy0_in_gc5603: endpoint@0 { reg = <0>; + remote-endpoint = <&gc5603_out0>; + data-lanes = <1 2>; + }; + + dcphy0_in_imx415: endpoint@1 { + reg = <1>; remote-endpoint = <&imx415_out0>; data-lanes = <1 2 3 4>; }; @@ -119,7 +163,7 @@ }; &mipi0_csi2 { - status = "disabled"; + status = "okay"; ports { #address-cells = <1>; @@ -150,13 +194,45 @@ }; &i2c5 { - status = "disabled"; + status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&i2c5m3_xfer>; + + dw9714_1: dw9714-1@c { + status = "okay"; + compatible = "dongwoon,dw9714"; + reg = <0xc>; + rockchip,camera-module-index = <0>; + rockchip,vcm-max-current = <100>; + rockchip,vcm-start-current = <0>; + rockchip,vcm-rated-current = <100>; + rockchip,vcm-step-mode = <0xd>; + rockchip,vcm-dlc-enable = <0>; + rockchip,vcm-mclk = <0>; + rockchip,vcm-t-src = <0>; + rockchip,camera-module-facing = "back"; + }; + + gc5603_1: gc5603-1@31 { + compatible = "galaxycore,gc5603"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_27m_clk>; + clock-names = "ext_cam_27m_clk"; + pwdn-gpios = <&gpio4 RK_PA5 GPIO_ACTIVE_LOW>; + rotation = <180>; + + port { + gc5603_out1: endpoint { + remote-endpoint = <&dcphy1_in_gc5603>; + data-lanes = <1 2>; + }; + }; + }; imx415_1: imx415-1@1a { compatible = "sony,imx415"; - status = "disabled"; + status = "okay"; reg = <0x1a>; clocks = <&ext_cam_37m_clk>; clock-names = "xvclk"; @@ -179,11 +255,11 @@ }; &mipi_dcphy1 { - status = "disabled"; + status = "okay"; }; &csi2_dcphy1 { - status = "disabled"; + status = "okay"; ports { #address-cells = <1>; @@ -193,9 +269,15 @@ reg = <0>; #address-cells = <1>; #size-cells = <0>; - - dcphy1_in_imx415: endpoint@0 { + + dcphy1_in_gc5603: endpoint@0 { reg = <0>; + remote-endpoint = <&gc5603_out1>; + data-lanes = <1 2>; + }; + + dcphy1_in_imx415: endpoint@1 { + reg = <1>; remote-endpoint = <&imx415_out1>; data-lanes = <1 2 3 4>; }; @@ -215,7 +297,7 @@ }; &mipi1_csi2 { - status = "disabled"; + status = "okay"; ports { #address-cells = <1>; @@ -246,13 +328,45 @@ }; &i2c6 { - status = "disabled"; + status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&i2c6m3_xfer>; + dw9714: dw9714@c { + status = "okay"; + compatible = "dongwoon,dw9714"; + reg = <0xc>; + rockchip,camera-module-index = <0>; + rockchip,vcm-max-current = <100>; + rockchip,vcm-start-current = <0>; + rockchip,vcm-rated-current = <100>; + rockchip,vcm-step-mode = <0xd>; + rockchip,vcm-dlc-enable = <0>; + rockchip,vcm-mclk = <0>; + rockchip,vcm-t-src = <0>; + rockchip,camera-module-facing = "back"; + }; + + gc5603_2: gc5603_2@31 { + compatible = "galaxycore,gc5603"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_27m_clk>; + clock-names = "ext_cam_27m_clk"; + pwdn-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_LOW>; + rotation = <180>; + + port { + gc5603_out2: endpoint { + remote-endpoint = <&dphy0_in_gc5603>; + data-lanes = <1 2>; + }; + }; + }; + imx415_2: imx415-2@1a { compatible = "sony,imx415"; - status = "disabled"; + status = "okay"; reg = <0x1a>; clocks = <&ext_cam_37m_clk>; clock-names = "xvclk"; @@ -275,11 +389,11 @@ }; &csi2_dphy0_hw { - status = "disabled"; + status = "okay"; }; &csi2_dphy0 { - status = "disabled"; + status = "okay"; ports { #address-cells = <1>; @@ -290,8 +404,14 @@ #address-cells = <1>; #size-cells = <0>; - dphy0_in_imx415: endpoint@0 { + dphy0_in_gc5603: endpoint@0 { reg = <0>; + remote-endpoint = <&gc5603_out2>; + data-lanes = <1 2 3 4>; + }; + + dphy0_in_imx415: endpoint@1 { + reg = <1>; remote-endpoint = <&imx415_out2>; data-lanes = <1 2 3 4>; }; @@ -311,7 +431,7 @@ }; &mipi2_csi2 { - status = "disabled"; + status = "okay"; ports { #address-cells = <1>; @@ -343,15 +463,15 @@ &rkcif { - status = "disabled"; + status = "okay"; }; &rkcif_mmu { - status = "disabled"; + status = "okay"; }; &rkcif_mipi_lvds { - status = "disabled"; + status = "okay"; port { cif_mipi0_in0: endpoint { @@ -361,7 +481,7 @@ }; &rkcif_mipi_lvds_sditf { - status = "disabled"; + status = "okay"; port { mipi_lvds_sditf: endpoint { @@ -371,7 +491,7 @@ }; &rkcif_mipi_lvds1 { - status = "disabled"; + status = "okay"; port { cif_mipi1_in0: endpoint { @@ -381,7 +501,7 @@ }; &rkcif_mipi_lvds1_sditf { - status = "disabled"; + status = "okay"; port { mipi_lvds1_sditf: endpoint { @@ -391,7 +511,7 @@ }; &rkcif_mipi_lvds2 { - status = "disabled"; + status = "okay"; port { cif_mipi2_in0: endpoint { @@ -401,7 +521,7 @@ }; &rkcif_mipi_lvds2_sditf { - status = "disabled"; + status = "okay"; port { mipi_lvds2_sditf: endpoint { @@ -411,15 +531,15 @@ }; &rkisp0 { - status = "disabled"; + status = "okay"; }; &isp0_mmu { - status = "disabled"; + status = "okay"; }; &rkisp0_vir0 { - status = "disabled"; + status = "okay"; port { #address-cells = <1>; @@ -433,7 +553,7 @@ }; &rkisp0_vir1 { - status = "disabled"; + status = "okay"; port { #address-cells = <1>; @@ -448,15 +568,15 @@ &rkisp1 { - status = "disabled"; + status = "okay"; }; &isp1_mmu { - status = "disabled"; + status = "okay"; }; &rkisp1_vir0 { - status = "disabled"; + status = "okay"; port { #address-cells = <1>; diff --git a/arch/arm64/configs/lubancat_linux_rk3588_defconfig b/arch/arm64/configs/lubancat_linux_rk3588_defconfig index 9fad404a3dab0..238366df74139 100644 --- a/arch/arm64/configs/lubancat_linux_rk3588_defconfig +++ b/arch/arm64/configs/lubancat_linux_rk3588_defconfig @@ -242,6 +242,7 @@ CONFIG_DUMMY=y CONFIG_MACVLAN=y CONFIG_IPVLAN=y CONFIG_VXLAN=y +CONFIG_TUN=m CONFIG_VETH=y # CONFIG_NET_VENDOR_3COM is not set # CONFIG_NET_VENDOR_ADAPTEC is not set @@ -351,7 +352,6 @@ CONFIG_RTL8192CU=m CONFIG_RTL8XXXU=m CONFIG_RTW88=y CONFIG_RTW88_8822BE=m -CONFIG_RTW88_8822CE=m CONFIG_RTW88_8723DE=m CONFIG_RTW88_DEBUG=y CONFIG_RTW88_DEBUGFS=y @@ -366,7 +366,7 @@ CONFIG_RTL8822CE=m CONFIG_RTL8852BE=m CONFIG_RTL8852BU=m CONFIG_USB_NET_RNDIS_WLAN=y -CONFIG_INPUT_FF_MEMLESS=y +CONFIG_INPUT_JOYDEV=y CONFIG_INPUT_EVDEV=y CONFIG_KEYBOARD_ADC=y # CONFIG_KEYBOARD_ATKBD is not set @@ -375,6 +375,11 @@ CONFIG_KEYBOARD_GPIO_POLLED=y # CONFIG_MOUSE_PS2 is not set CONFIG_MOUSE_CYAPA=y CONFIG_MOUSE_ELAN_I2C=y +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_SIDEWINDER=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_ATMEL_MXT=y CONFIG_TOUCHSCREEN_GOODIX=y @@ -486,6 +491,7 @@ CONFIG_VIDEO_OV7251=y CONFIG_VIDEO_OV8858=y CONFIG_VIDEO_OV13850=y CONFIG_VIDEO_DW9714=y +CONFIG_VIDEO_GC5603=y # CONFIG_VGA_ARB is not set CONFIG_DRM=y CONFIG_DRM_IGNORE_IOTCL_PERMIT=y diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 9f40652d5d731..16cb4466ceb90 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -965,6 +965,17 @@ config VIDEO_GC08A3 To compile this driver as a module, choose M here: the module will be called gc08a3. +config VIDEO_GC5603 + tristate "GalaxyCore GC5603 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the GalaxyCore GC5603 sensor. + + To compile this driver as a module, choose M here: the + module will be called gc5603. + config VIDEO_GC1084 tristate "GalaxyCore GC1084 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index ef76a5f4b0d34..cf97f876308db 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -197,6 +197,7 @@ obj-$(CONFIG_VIDEO_GC4C33) += gc4c33.o obj-$(CONFIG_VIDEO_GC5025) += gc5025.o obj-$(CONFIG_VIDEO_GC5035) += gc5035.o obj-$(CONFIG_VIDEO_GC8034) += gc8034.o +obj-$(CONFIG_VIDEO_GC5603) += gc5603.o obj-$(CONFIG_VIDEO_HI556) += hi556.o obj-$(CONFIG_VIDEO_IMX214) += imx214.o obj-$(CONFIG_VIDEO_IMX214_EEPROM) += imx214_eeprom.o diff --git a/drivers/media/i2c/gc5603.c b/drivers/media/i2c/gc5603.c new file mode 100644 index 0000000000000..884b932130b50 --- /dev/null +++ b/drivers/media/i2c/gc5603.c @@ -0,0 +1,1610 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * gc5603 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + * V0.0X01.0X03 add enum_frame_interval function. + * V0.0X01.0X04 add quick stream on/off + * V0.0X01.0X05 add function g_mbus_config + * V0.0X01.0X06 + * 1. add 2lane support. + * 2. add some debug info. + * 3. adjust gc5603_g_mbus_config function. + * V0.0X01.0X07 support get channel info + * V0.0X01.0X08 + * 1. default support 2lane full 30fps. + * 2. default support rk otp spec. + * V0.0X01.0X09 adjust supply sequence to suit spec + */ +//#define DEBUG +#include +#include +#include +#include +#include +#include +#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) +#define GC5603_NAME "gc5603" +#define GC5603_MEDIA_BUS_FMT MEDIA_BUS_FMT_SRGGB10_1X10 + //MEDIA_BUS_FMT_SBGGR10_1X10 + + +#define MIPI_FREQ_848M 423000000 +#define GC5603_XVCLK_FREQ 27000000 + +#define GC5603_PAGE_SELECT 0xFE + +#define GC5603_REG_CHIP_ID_H 0x03F0 +#define GC5603_REG_CHIP_ID_L 0x03F1 + +#define GC5603_REG_EXP_H 0x0202 +#define GC5603_REG_EXP_L 0x0203 + +#define GC5603_REG_VTS_H 0x0340 +#define GC5603_REG_VTS_L 0x0341 + +#define GC5603_REG_CTRL_MODE 0x0100 +#define GC5603_MODE_SW_STANDBY 0x00 +#define GC5603_MODE_STREAMING 0x09 + +#define REG_NULL 0xFFFF + +#define GC5603_CHIP_ID 0x5603 + +#define GC5603_VTS_MAX 0x7fff +#define GC5603_HTS_MAX 0xFFF + +#define GC5603_EXPOSURE_MAX 0x3FFF +#define GC5603_EXPOSURE_MIN 1 +#define GC5603_EXPOSURE_STEP 1 + +#define GC5603_GAIN_MIN 64 +#define GC5603_GAIN_MAX 0xffff +#define GC5603_GAIN_STEP 1 +#define GC5603_GAIN_DEFAULT 64 + +#define gc5603_REG_VALUE_08BIT 1 +#define gc5603_REG_VALUE_16BIT 2 +#define gc5603_REG_VALUE_24BIT 3 + + +#define GC5603_LANES 2 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + + + +#define GC5603_FLIP_MIRROR_REG 0x0101 + +#define GC_MIRROR_BIT_MASK BIT(0) +#define GC_FLIP_BIT_MASK BIT(1) + +static const char * const gc5603_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + +#define GC5603_NUM_SUPPLIES ARRAY_SIZE(gc5603_supply_names) + +#define to_gc5603(sd) container_of(sd, struct gc5603, subdev) + +struct regval { + u16 addr; + u8 val; +}; + +struct gc5603_mode { + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; + u32 hdr_mode; + u32 vc[PAD_MAX]; +}; + +struct gc5603 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct gpio_desc *pwren_gpio; + struct regulator_bulk_data supplies[GC5603_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 *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *h_flip; + struct v4l2_ctrl *v_flip; + struct mutex mutex; + bool streaming; + bool power_on; + const struct gc5603_mode *cur_mode; + unsigned int lane_num; + unsigned int cfg_num; + unsigned int pixel_rate; + + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + struct rkmodule_awb_cfg awb_cfg; + struct rkmodule_lsc_cfg lsc_cfg; + u32 flip; +}; + + +static const struct regval gc5603_2960x1666_regs_2lane[] = { +//version 1.3 +//mclk 27Mhz +//mipi 2 lane 846Mbps/lane +//vts = 1750 ,row_time=19.05us +//window 2960x1666 +//BGGR + {0x03fe, 0xf0}, + {0x03fe, 0x00}, + {0x03fe, 0x10}, + {0x03fe, 0x00}, + {0x0202, 0x01}, + {0x0203, 0x50}, + {0x0a38, 0x02}, + {0x0a38, 0x03}, + {0x0a20, 0x07}, + {0x061b, 0x03}, + {0x061c, 0x50}, + {0x061d, 0x05}, + {0x061e, 0x70}, + {0x061f, 0x03}, + {0x0a21, 0x08}, + {0x0a34, 0x40}, + {0x0a35, 0x11}, + {0x0a36, 0x5e}, + {0x0a37, 0x03}, + {0x0314, 0x50}, + {0x0315, 0x32}, + {0x031c, 0xce}, + {0x0219, 0x47}, + {0x0342, 0x04}, + {0x0343, 0xb0}, + {0x0340, 0x06}, + {0x0341, 0xd6}, + {0x0345, 0x02}, + {0x0347, 0x02}, + {0x0348, 0x0b}, + {0x0349, 0x98}, + {0x034a, 0x06}, + {0x034b, 0x8a}, + {0x0094, 0x0b}, + {0x0095, 0x90}, + {0x0096, 0x06}, + {0x0097, 0x82}, + {0x0099, 0x04}, + {0x009b, 0x04}, + {0x060c, 0x01}, + {0x060e, 0xd2}, + {0x060f, 0x05}, + {0x070c, 0x01}, + {0x070e, 0xd2}, + {0x070f, 0x05}, + {0x0909, 0x07}, + {0x0902, 0x04}, + {0x0904, 0x0b}, + {0x0907, 0x54}, + {0x0908, 0x06}, + {0x0903, 0x9d}, + {0x072a, 0x1c},//18 + {0x072b, 0x1c},//18 + {0x0724, 0x2b}, + {0x0727, 0x2b}, + {0x1466, 0x18}, + {0x1467, 0x08}, + {0x1468, 0x10}, + {0x1469, 0x80}, + {0x146a, 0xe8},//b8 + {0x1412, 0x20}, + {0x0707, 0x07}, + {0x0737, 0x0f}, + {0x0704, 0x01}, + {0x0706, 0x03}, + {0x0716, 0x03}, + {0x0708, 0xc8}, + {0x0718, 0xc8}, + {0x061a, 0x02}, + {0x1430, 0x80}, + {0x1407, 0x10}, + {0x1408, 0x16}, + {0x1409, 0x03}, + {0x1438, 0x01}, + {0x02ce, 0x03}, + {0x0245, 0xc9}, + {0x023a, 0x08},//3B + {0x02cd, 0x88}, + {0x0612, 0x02}, + {0x0613, 0xc7}, + {0x0243, 0x03},//06 + {0x0089, 0x03}, + {0x0002, 0xab}, + {0x0040, 0xa3}, + {0x0075, 0x64},//64 + {0x0004, 0x0f}, + {0x0053, 0x0a}, + {0x0205, 0x0c}, + + //auto_load}, + {0x0a67, 0x80}, + {0x0a54, 0x0e}, + {0x0a65, 0x10}, + {0x0a98, 0x04}, + {0x05be, 0x00}, + {0x05a9, 0x01}, + {0x0023, 0x00}, + {0x0022, 0x00}, + {0x0025, 0x00}, + {0x0024, 0x00}, + {0x0028, 0x0b}, + {0x0029, 0x98}, + {0x002a, 0x06}, + {0x002b, 0x86}, + {0x0a83, 0xe0}, + {0x0a72, 0x02}, + {0x0a73, 0x60}, + {0x0a75, 0x41}, + {0x0a70, 0x03}, + {0x0a5a, 0x80}, + {0x0181, 0x30}, + {0x0182, 0x05}, + {0x0185, 0x01}, + {0x0180, 0x46}, + {0x0100, 0x08}, + {0x010d, 0x74}, + {0x010e, 0x0e}, + {0x0113, 0x02}, + {0x0114, 0x01}, + {0x0115, 0x10}, + //{0x0a70, 0x00}, + //{0x0080, 0x02}, + //{0x0a67, 0x00}, + {0x0052, 0x02}, + {0x0076, 0x01}, + {0x021a, 0x10}, + {0x0049, 0x0f}, + {0x004a, 0x3c}, + {0x004b, 0x00}, + {0x0430, 0x25}, + {0x0431, 0x25}, + {0x0432, 0x25}, + {0x0433, 0x25}, + {0x0434, 0x59}, + {0x0435, 0x59}, + {0x0436, 0x59}, + {0x0437, 0x59}, + + {0x0100, 0x09}, + {REG_NULL, 0x00}, +}; + +static const struct gc5603_mode supported_modes[] = { + { + .width = 2960, + .height = 1666, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x6ce, + .hts_def = 0x0C80, + .vts_def = 0x06D6, + .reg_list = gc5603_2960x1666_regs_2lane, + //.bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, + .hdr_mode = NO_HDR, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, +}; + +static const s64 link_freq_menu_items[] = { + MIPI_FREQ_848M +}; +static int gc5603_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; + + 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 gc5603_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 = gc5603_write_reg(client, regs[i].addr, + gc5603_REG_VALUE_08BIT, regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int gc5603_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; + dev_info(&client->dev, + "gc5603 read reg(0x%x val:0x%x) \n", reg, *val); +} +static int gc5603_get_reso_dist(const struct gc5603_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct gc5603_mode * +gc5603_find_best_fit(struct gc5603 *gc5603, 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 < gc5603->cfg_num; i++) { + dist = gc5603_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 uint8_t regValTable[26][7] = { + //0614, 0615, 0225, 1467 1468, 00b8, 00b9 + { 0x00, 0x00, 0x04, 0x15, 0x15, 0x01, 0x00}, + { 0x90, 0x02, 0x04, 0x15, 0x15, 0x01, 0x0A}, + { 0x00, 0x00, 0x00, 0x15, 0x15, 0x01, 0x12}, + { 0x90, 0x02, 0x00, 0x15, 0x15, 0x01, 0x20}, + { 0x01, 0x00, 0x00, 0x15, 0x15, 0x01, 0x30}, + { 0x91, 0x02, 0x00, 0x15, 0x15, 0x02, 0x05}, + { 0x02, 0x00, 0x00, 0x15, 0x15, 0x02, 0x19}, + { 0x92, 0x02, 0x00, 0x16, 0x16, 0x02, 0x3F}, + { 0x03, 0x00, 0x00, 0x16, 0x16, 0x03, 0x20}, + { 0x93, 0x02, 0x00, 0x17, 0x17, 0x04, 0x0A}, + { 0x00, 0x00, 0x01, 0x18, 0x18, 0x05, 0x02}, + { 0x90, 0x02, 0x01, 0x19, 0x19, 0x05, 0x39}, + { 0x01, 0x00, 0x01, 0x19, 0x19, 0x06, 0x3C}, + { 0x91, 0x02, 0x01, 0x19, 0x19, 0x08, 0x0D}, + { 0x02, 0x00, 0x01, 0x1a, 0x1a, 0x09, 0x21}, + { 0x92, 0x02, 0x01, 0x1a, 0x1a, 0x0B, 0x0F}, + { 0x03, 0x00, 0x01, 0x1c, 0x1c, 0x0D, 0x17}, + { 0x93, 0x02, 0x01, 0x1c, 0x1c, 0x0F, 0x33}, + { 0x04, 0x00, 0x01, 0x1d, 0x1d, 0x12, 0x30}, + { 0x94, 0x02, 0x01, 0x1d, 0x1d, 0x16, 0x10}, + { 0x05, 0x00, 0x01, 0x1e, 0x1e, 0x1A, 0x19}, + { 0x95, 0x02, 0x01, 0x1e, 0x1e, 0x1F, 0x13}, + { 0x06, 0x00, 0x01, 0x20, 0x20, 0x25, 0x08}, + { 0x96, 0x02, 0x01, 0x20, 0x20, 0x2C, 0x03}, + { 0xb6, 0x04, 0x01, 0x20, 0x20, 0x34, 0x0F}, + { 0x86, 0x06, 0x01, 0x20, 0x20, 0x3D, 0x3D}, +}; + +static uint32_t gain_level_table[27] = { + 64, + 74, + 82, + 96, + 112, + 133, + 153, + 191, + 224, + 266, + 322, + 377, + 444, + 525, + 609, + 719, + 855, + 1011, + 1200, + 1424, + 1689, + 2003, + + 2376, + 2819, + + 3343, + 3965, + 0xffffffff, +}; +//static int total = sizeof(gain_level_table) / sizeof(uint32_t); + +static int gc5603_set_gain(struct gc5603 *gc5603, u32 gain) +{ + int ret; + uint16_t i = 0; + uint16_t total = 0; + uint16_t temp = 0; + + + for (i = 0; i < total; i++) { + if ((gain_level_table[i] <= gain) && (gain < gain_level_table[i+1])) + break; + } + + if((gain>3965)||(gain==3965)) + i =25; + + + ret = gc5603_write_reg(gc5603->client, 0x031d,gc5603_REG_VALUE_08BIT, 0x2d); + ret = gc5603_write_reg(gc5603->client, 0x0614,gc5603_REG_VALUE_08BIT,regValTable[i][0]); + ret = gc5603_write_reg(gc5603->client, 0x0615,gc5603_REG_VALUE_08BIT,regValTable[i][1]); + ret = gc5603_write_reg(gc5603->client, 0x0225,gc5603_REG_VALUE_08BIT,regValTable[i][2]); + + ret = gc5603_write_reg(gc5603->client, 0x031d,gc5603_REG_VALUE_08BIT, 0x28); + ret = gc5603_write_reg(gc5603->client, 0x1467,gc5603_REG_VALUE_08BIT,regValTable[i][3]); + ret = gc5603_write_reg(gc5603->client, 0x1468,gc5603_REG_VALUE_08BIT,regValTable[i][4]); + ret = gc5603_write_reg(gc5603->client, 0x00b8,gc5603_REG_VALUE_08BIT,regValTable[i][5]); + ret = gc5603_write_reg(gc5603->client, 0x00b9,gc5603_REG_VALUE_08BIT,regValTable[i][6]); + + + temp = 64 * gain / gain_level_table[i]; + + //dev_warn(&client->dev, "gc5603_set_gain gain=%d,i=%d,temp=%d \n", gain, i, temp); + + ret |= gc5603_write_reg(gc5603->client, 0x0064,gc5603_REG_VALUE_08BIT,(temp >> 6)); + ret |= gc5603_write_reg(gc5603->client, 0x0065,gc5603_REG_VALUE_08BIT,((temp&0x3f) << 2) ); + + return ret; +} + +static int gc5603_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc5603 *gc5603 = container_of(ctrl->handler, + struct gc5603, ctrl_handler); + struct i2c_client *client = gc5603->client; + s64 max; + int ret = 0; + u32 vts = 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 = gc5603->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(gc5603->exposure, + gc5603->exposure->minimum, max, + gc5603->exposure->step, + gc5603->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + + #if 1 + ret = gc5603_write_reg(gc5603->client, GC5603_REG_EXP_H,gc5603_REG_VALUE_08BIT, + (ctrl->val >> 8)); + ret |= gc5603_write_reg(gc5603->client, GC5603_REG_EXP_L,gc5603_REG_VALUE_08BIT, + ctrl->val & 0xff); + #endif + + break; + case V4L2_CID_ANALOGUE_GAIN: + gc5603_set_gain(gc5603, ctrl->val); + break; + case V4L2_CID_VBLANK: + vts = ctrl->val + gc5603->cur_mode->height; + + #if 1 + ret = gc5603_write_reg(gc5603->client, GC5603_REG_VTS_H,gc5603_REG_VALUE_08BIT,(vts >> 8)); + ret |= gc5603_write_reg(gc5603->client, GC5603_REG_VTS_L, gc5603_REG_VALUE_08BIT,vts & 0xff); + #endif + break; + case V4L2_CID_HFLIP: + if (ctrl->val) + gc5603->flip |= GC_MIRROR_BIT_MASK; + else + gc5603->flip &= ~GC_MIRROR_BIT_MASK; + break; + case V4L2_CID_VFLIP: + if (ctrl->val) + gc5603->flip |= GC_FLIP_BIT_MASK; + else + gc5603->flip &= ~GC_FLIP_BIT_MASK; + 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 gc5603_ctrl_ops = { + .s_ctrl = gc5603_set_ctrl, +}; + +static int gc5603_configure_regulators(struct gc5603 *gc5603) +{ + unsigned int i; + + for (i = 0; i < GC5603_NUM_SUPPLIES; i++) + gc5603->supplies[i].supply = gc5603_supply_names[i]; + + return devm_regulator_bulk_get(&gc5603->client->dev, + GC5603_NUM_SUPPLIES, + gc5603->supplies); +} + +static int gc5603_parse_of(struct gc5603 *gc5603) +{ + struct device *dev = &gc5603->client->dev; + struct device_node *endpoint; + struct fwnode_handle *fwnode; + int rval; + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "Failed to get endpoint\n"); + return -EINVAL; + } + fwnode = of_fwnode_handle(endpoint); + rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0); + if (rval <= 0) { + dev_warn(dev, " Get mipi lane num failed!\n"); + return -1; + } + + gc5603->lane_num = rval; + if (2 == gc5603->lane_num) { + gc5603->cur_mode = &supported_modes[0]; + gc5603->cfg_num = ARRAY_SIZE(supported_modes); + + /*pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + gc5603->pixel_rate = MIPI_FREQ_848M * 2U * (gc5603->lane_num) / 10U; + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + gc5603->lane_num, gc5603->pixel_rate); + } else { + dev_info(dev, "gc5603 can not support the lane num(%d)\n", gc5603->lane_num); + } + return 0; +} + +static int gc5603_initialize_controls(struct gc5603 *gc5603) +{ + const struct gc5603_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &gc5603->ctrl_handler; + mode = gc5603->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &gc5603->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, gc5603->pixel_rate, 1, gc5603->pixel_rate); + + h_blank = mode->hts_def - mode->width; + gc5603->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (gc5603->hblank) + gc5603->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + gc5603->vblank = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + GC5603_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + gc5603->exposure = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_EXPOSURE, + GC5603_EXPOSURE_MIN, + exposure_max, + GC5603_EXPOSURE_STEP, + mode->exp_def); + + gc5603->anal_gain = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + GC5603_GAIN_MIN, + GC5603_GAIN_MAX, + GC5603_GAIN_STEP, + GC5603_GAIN_DEFAULT); + + gc5603->h_flip = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + gc5603->v_flip = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + gc5603->flip = 0; + + if (handler->error) { + ret = handler->error; + dev_err(&gc5603->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + gc5603->subdev.ctrl_handler = handler; + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 gc5603_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, GC5603_XVCLK_FREQ / 1000 / 1000); +} + +static int __gc5603_power_on(struct gc5603 *gc5603) +{ + int ret; + u32 delay_us; + struct device *dev = &gc5603->client->dev; + + if (!IS_ERR_OR_NULL(gc5603->pins_default)) { + ret = pinctrl_select_state(gc5603->pinctrl, + gc5603->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + ret = clk_set_rate(gc5603->xvclk, GC5603_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(gc5603->xvclk) != GC5603_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(gc5603->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 0); + + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 0); + + usleep_range(500, 1000); + ret = regulator_bulk_enable(GC5603_NUM_SUPPLIES, gc5603->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + if (!IS_ERR(gc5603->pwren_gpio)) + gpiod_set_value_cansleep(gc5603->pwren_gpio, 1); + + usleep_range(1000, 1100); + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 1); + usleep_range(100, 150); + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = gc5603_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + return 0; + +disable_clk: + clk_disable_unprepare(gc5603->xvclk); + return ret; +} + +static void __gc5603_power_off(struct gc5603 *gc5603) +{ + int ret; + struct device *dev = &gc5603->client->dev; + + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 0); + clk_disable_unprepare(gc5603->xvclk); + + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 0); + + if (!IS_ERR_OR_NULL(gc5603->pins_sleep)) { + ret = pinctrl_select_state(gc5603->pinctrl, + gc5603->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + regulator_bulk_disable(GC5603_NUM_SUPPLIES, gc5603->supplies); + if (!IS_ERR(gc5603->pwren_gpio)) + gpiod_set_value_cansleep(gc5603->pwren_gpio, 0); + +} + + static int gc5603_check_sensor_id(struct gc5603 *gc5603, + struct i2c_client *client) +{ + struct device *dev = &gc5603->client->dev; + u16 id = 0; + u32 reg_H = 0; + u32 reg_L = 0; + int ret; + + ret = gc5603_read_reg(client, GC5603_REG_CHIP_ID_H, + gc5603_REG_VALUE_08BIT, ®_H); + ret |= gc5603_read_reg(client, GC5603_REG_CHIP_ID_L, + gc5603_REG_VALUE_08BIT, ®_L); + + id = ((reg_H << 8) & 0xff00) | (reg_L & 0xff); + if (!(reg_H == (GC5603_CHIP_ID >> 8) || reg_L == (GC5603_CHIP_ID & 0xff))) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + dev_info(dev, "detected gc%04x sensor\n", id); + return 0; +} + +static int gc5603_set_flip(struct gc5603 *gc5603, u8 mode) +{ +u32 match_reg = 0; + + gc5603_read_reg(gc5603->client, GC5603_FLIP_MIRROR_REG,gc5603_REG_VALUE_08BIT,&match_reg); + + if (mode == GC_FLIP_BIT_MASK) { + match_reg |= GC_FLIP_BIT_MASK; + match_reg &= ~GC_MIRROR_BIT_MASK; + } else if (mode == GC_MIRROR_BIT_MASK) { + match_reg |= GC_MIRROR_BIT_MASK; + match_reg &= ~GC_FLIP_BIT_MASK; + } else if (mode == (GC_MIRROR_BIT_MASK | + GC_FLIP_BIT_MASK)) { + match_reg |= GC_FLIP_BIT_MASK; + match_reg |= GC_MIRROR_BIT_MASK; + } else { + match_reg &= ~GC_FLIP_BIT_MASK; + match_reg &= ~GC_MIRROR_BIT_MASK; + } + return gc5603_write_reg(gc5603->client, GC5603_FLIP_MIRROR_REG,gc5603_REG_VALUE_08BIT ,match_reg); +} + +static int __gc5603_start_stream(struct gc5603 *gc5603) +{ + int ret; + + ret = gc5603_write_array(gc5603->client, gc5603->cur_mode->reg_list); + if (ret) + return ret; + + usleep_range(1000, 1100); + + + gc5603_write_reg(gc5603->client, 0x0a70,gc5603_REG_VALUE_08BIT, 0x00); + gc5603_write_reg(gc5603->client, 0x0080,gc5603_REG_VALUE_08BIT, 0x02); + gc5603_write_reg(gc5603->client, 0x0a67,gc5603_REG_VALUE_08BIT, 0x00); + + + + + /* In case these controls are set before streaming */ + mutex_unlock(&gc5603->mutex); + ret = __v4l2_ctrl_handler_setup(&gc5603->ctrl_handler); + mutex_lock(&gc5603->mutex); + + ret = gc5603_set_flip(gc5603, gc5603->flip); + if (ret) + return ret; + return gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_STREAMING); +} + +static int __gc5603_stop_stream(struct gc5603 *gc5603) +{ + return gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_SW_STANDBY); +} + +static void gc5603_get_module_inf(struct gc5603 *gc5603, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, GC5603_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, gc5603->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, gc5603->len_name, sizeof(inf->base.lens)); +} + +static int gc5603_get_channel_info(struct gc5603 *gc5603, struct rkmodule_channel_info *ch_info) +{ + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + ch_info->vc = gc5603->cur_mode->vc[ch_info->index]; + ch_info->width = gc5603->cur_mode->width; + ch_info->height = gc5603->cur_mode->height; + ch_info->bus_fmt = GC5603_MEDIA_BUS_FMT; + return 0; +} + +static void gc5603_set_awb_cfg(struct gc5603 *gc5603, + struct rkmodule_awb_cfg *cfg) +{ + mutex_lock(&gc5603->mutex); + memcpy(&gc5603->awb_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&gc5603->mutex); +} + +static void gc5603_set_lsc_cfg(struct gc5603 *gc5603, + struct rkmodule_lsc_cfg *cfg) +{ + mutex_lock(&gc5603->mutex); + memcpy(&gc5603->lsc_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&gc5603->mutex); +} + +static long gc5603_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + long ret = 0; + struct rkmodule_hdr_cfg *hdr_cfg; + u32 stream = 0; + struct rkmodule_channel_info *ch_info; + + switch (cmd) { + case RKMODULE_GET_HDR_CFG: + hdr_cfg = (struct rkmodule_hdr_cfg *)arg; + hdr_cfg->esp.mode = HDR_NORMAL_VC; + hdr_cfg->hdr_mode = gc5603->cur_mode->hdr_mode; + break; + case RKMODULE_SET_HDR_CFG: + case RKMODULE_SET_CONVERSION_GAIN: + break; + case RKMODULE_GET_MODULE_INFO: + gc5603_get_module_inf(gc5603, (struct rkmodule_inf *)arg); + break; + case RKMODULE_AWB_CFG: + gc5603_set_awb_cfg(gc5603, (struct rkmodule_awb_cfg *)arg); + break; + case RKMODULE_LSC_CFG: + gc5603_set_lsc_cfg(gc5603, (struct rkmodule_lsc_cfg *)arg); + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_STREAMING); + else + ret = gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_SW_STANDBY); + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = gc5603_get_channel_info(gc5603, ch_info); + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} + +#ifdef CONFIG_COMPAT +static long gc5603_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 *awb_cfg; + struct rkmodule_lsc_cfg *lsc_cfg; + struct rkmodule_hdr_cfg *hdr; + long ret = 0; + u32 cg = 0; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = gc5603_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + awb_cfg = kzalloc(sizeof(*awb_cfg), GFP_KERNEL); + if (!awb_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(awb_cfg, up, sizeof(*awb_cfg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, awb_cfg); + kfree(awb_cfg); + break; + case RKMODULE_LSC_CFG: + lsc_cfg = kzalloc(sizeof(*lsc_cfg), GFP_KERNEL); + if (!lsc_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(lsc_cfg, up, sizeof(*lsc_cfg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, lsc_cfg); + kfree(lsc_cfg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = gc5603_ioctl(sd, cmd, hdr); + if (!ret) + ret = copy_to_user(up, hdr, sizeof(*hdr)); + kfree(hdr); + break; + case RKMODULE_SET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(hdr, up, sizeof(*hdr)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, hdr); + kfree(hdr); + break; + case RKMODULE_SET_CONVERSION_GAIN: + ret = copy_from_user(&cg, up, sizeof(cg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, &cg); + break; + case RKMODULE_SET_QUICK_STREAM: + ret = copy_from_user(&stream, up, sizeof(u32)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, &stream); + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} +#endif + +static int gc5603_s_stream(struct v4l2_subdev *sd, int on) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct i2c_client *client = gc5603->client; + int ret = 0; + + mutex_lock(&gc5603->mutex); + on = !!on; + if (on == gc5603->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 = __gc5603_start_stream(gc5603); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __gc5603_stop_stream(gc5603); + pm_runtime_put(&client->dev); + } + + gc5603->streaming = on; + +unlock_and_return: + mutex_unlock(&gc5603->mutex); + return 0; +} + +static int gc5603_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + + mutex_lock(&gc5603->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&gc5603->mutex); + + return 0; +} + +static int gc5603_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + u32 val = 0; + + if (mode->hdr_mode == NO_HDR) + val = 1 << (GC5603_LANES - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + config->type = V4L2_MBUS_CSI2_DPHY; + config->flags = val; + return 0; +} + +static int gc5603_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = GC5603_MEDIA_BUS_FMT; + return 0; +} + +static int gc5603_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + + if (fse->index >= gc5603->cfg_num) + return -EINVAL; + + if (fse->code != GC5603_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; +} + +#define DST_WIDTH 2720 +#define DST_HEIGHT 1616 + +/* + * The resolution of the driver configuration needs to be exactly + * the same as the current output resolution of the sensor, + * the input width of the isp needs to be 16 aligned, + * the input height of the isp needs to be 8 aligned. + * Can be cropped to standard resolution by this function, + * otherwise it will crop out strange resolution according + * to the alignment rules. + */ +static int gc5603_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + sel->r.left =120; + sel->r.width = DST_WIDTH; + sel->r.top = 25; + sel->r.height = DST_HEIGHT; + return 0; + } + return -EINVAL; +} + +static int gc5603_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + + if (fie->index >= gc5603->cfg_num) + return -EINVAL; + + fie->code = GC5603_MEDIA_BUS_FMT; + fie->width = supported_modes[fie->index].width; + fie->height = supported_modes[fie->index].height; + fie->interval = supported_modes[fie->index].max_fps; + fie->reserved[0] = supported_modes[fie->index].hdr_mode; + return 0; +} + +static int gc5603_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&gc5603->mutex); + + mode = gc5603_find_best_fit(gc5603, fmt); + fmt->format.code = GC5603_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, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&gc5603->mutex); + return -ENOTTY; +#endif + } else { + gc5603->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(gc5603->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(gc5603->vblank, vblank_def, + GC5603_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&gc5603->mutex); + return 0; +} + +static int gc5603_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + + mutex_lock(&gc5603->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&gc5603->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = GC5603_MEDIA_BUS_FMT; + fmt->format.field = V4L2_FIELD_NONE; + + /* format info: width/height/data type/virctual channel */ + if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR) + fmt->reserved[0] = mode->vc[fmt->pad]; + else + fmt->reserved[0] = mode->vc[PAD0]; + + } + mutex_unlock(&gc5603->mutex); + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int gc5603_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct gc5603_mode *def_mode = &supported_modes[0]; + + mutex_lock(&gc5603->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = GC5603_MEDIA_BUS_FMT; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&gc5603->mutex); + /* No crop or compose */ + return 0; +} +#endif + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops gc5603_internal_ops = { + .open = gc5603_open, +}; +#endif + +static int gc5603_s_power(struct v4l2_subdev *sd, int on) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct i2c_client *client = gc5603->client; + int ret = 0; + + mutex_lock(&gc5603->mutex); + + /* If the power state is not modified - no work to do. */ + if (gc5603->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; + } + + gc5603->power_on = true; + } else { + pm_runtime_put(&client->dev); + gc5603->power_on = false; + } + +unlock_and_return: + mutex_unlock(&gc5603->mutex); + + return ret; +} + +static const struct v4l2_subdev_core_ops gc5603_core_ops = { + .s_power = gc5603_s_power, + .ioctl = gc5603_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = gc5603_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops gc5603_video_ops = { + .s_stream = gc5603_s_stream, + .g_frame_interval = gc5603_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops gc5603_pad_ops = { + .enum_mbus_code = gc5603_enum_mbus_code, + .enum_frame_size = gc5603_enum_frame_sizes, + .enum_frame_interval = gc5603_enum_frame_interval, + .get_fmt = gc5603_get_fmt, + .set_fmt = gc5603_set_fmt, + .get_selection = gc5603_get_selection, + .get_mbus_config = gc5603_g_mbus_config, +}; + +static const struct v4l2_subdev_ops gc5603_subdev_ops = { + .core = &gc5603_core_ops, + .video = &gc5603_video_ops, + .pad = &gc5603_pad_ops, +}; + +static int gc5603_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + __gc5603_power_on(gc5603); + return 0; +} + +static int gc5603_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + __gc5603_power_off(gc5603); + return 0; +} + +static const struct dev_pm_ops gc5603_pm_ops = { + SET_RUNTIME_PM_OPS(gc5603_runtime_suspend, + gc5603_runtime_resume, NULL) +}; + +static int gc5603_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct gc5603 *gc5603; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + gc5603 = devm_kzalloc(dev, sizeof(*gc5603), GFP_KERNEL); + if (!gc5603) + return -ENOMEM; + + gc5603->client = client; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &gc5603->module_index); + if (ret) { + dev_warn(dev, "could not get module index!\n"); + gc5603->module_index = 0; + } + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &gc5603->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &gc5603->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &gc5603->len_name); + if (ret) { + dev_err(dev, + "could not get module information!\n"); + return -EINVAL; + } + + gc5603->xvclk = devm_clk_get(&client->dev, "xvclk"); + if (IS_ERR(gc5603->xvclk)) { + dev_err(&client->dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + gc5603->pwren_gpio = devm_gpiod_get(dev, "pwren", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->pwdn_gpio)) + dev_warn(dev, "Failed to get pwren-gpios\n"); + + gc5603->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->reset_gpio)) + dev_info(dev, "Failed to get reset-gpios, maybe no used\n"); + + gc5603->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->pwdn_gpio)) + dev_warn(dev, "Failed to get power-gpios\n"); + + ret = gc5603_configure_regulators(gc5603); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + ret = gc5603_parse_of(gc5603); + if (ret != 0) + return -EINVAL; + + gc5603->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(gc5603->pinctrl)) { + gc5603->pins_default = + pinctrl_lookup_state(gc5603->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(gc5603->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + gc5603->pins_sleep = + pinctrl_lookup_state(gc5603->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(gc5603->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } else { + dev_err(dev, "no pinctrl\n"); + } + + mutex_init(&gc5603->mutex); + + sd = &gc5603->subdev; + v4l2_i2c_subdev_init(sd, client, &gc5603_subdev_ops); + ret = gc5603_initialize_controls(gc5603); + if (ret) + goto err_destroy_mutex; + + ret = __gc5603_power_on(gc5603); + if (ret) + goto err_free_handler; + + usleep_range(3000, 4000); + ret = gc5603_check_sensor_id(gc5603, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &gc5603_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + gc5603->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &gc5603->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(gc5603->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + gc5603->module_index, facing, + GC5603_NAME, dev_name(sd->dev)); + + ret = v4l2_async_register_subdev_sensor_common(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); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + +err_power_off: + __gc5603_power_off(gc5603); +err_free_handler: + v4l2_ctrl_handler_free(&gc5603->ctrl_handler); + +err_destroy_mutex: + mutex_destroy(&gc5603->mutex); + return ret; +} + +static int gc5603_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&gc5603->ctrl_handler); + mutex_destroy(&gc5603->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __gc5603_power_off(gc5603); + pm_runtime_set_suspended(&client->dev); + return 0; +} + +static const struct i2c_device_id gc5603_match_id[] = { + { "gc5603", 0 }, + { }, +}; + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id gc5603_of_match[] = { + { .compatible = "galaxycore,gc5603" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gc5603_of_match); +#endif + +static struct i2c_driver gc5603_i2c_driver = { + .driver = { + .name = GC5603_NAME, + .pm = &gc5603_pm_ops, + .of_match_table = of_match_ptr(gc5603_of_match), + }, + .probe = &gc5603_probe, + .remove = &gc5603_remove, + .id_table = gc5603_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&gc5603_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&gc5603_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("GC2035 CMOS Image Sensor driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c b/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c index 81827de21d9c2..08e562dc8e590 100644 --- a/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c +++ b/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c @@ -491,8 +491,8 @@ static int rockchip_csi2_dphy_fwnode_parse(struct device *dev, return -EINVAL; } - if (vep->bus_type == V4L2_MBUS_CSI2_DPHY) { - config->type = V4L2_MBUS_CSI2_DPHY; + if (vep->bus_type == V4L2_MBUS_CSI2_DPHY || vep->bus_type == V4L2_MBUS_CSI2_CPHY) { + config->type = vep->bus_type; config->flags = vep->bus.mipi_csi2.flags; s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes; } else if (vep->bus_type == V4L2_MBUS_CCP2) { diff --git a/logo.bmp b/logo.bmp index b0fab1583eef27a1480420b9ef07ef5f437470bd..24c70876c7d00715a450916e02a80681f74e8f2b 100644 GIT binary patch literal 1215954 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 diff --git a/logo_kernel.bmp b/logo_kernel.bmp index b0fab1583eef27a1480420b9ef07ef5f437470bd..24c70876c7d00715a450916e02a80681f74e8f2b 100644 GIT binary patch literal 1215954 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 From 6c39ee103253f0127a8290050d0b8a0a11ed9023 Mon Sep 17 00:00:00 2001 From: ShingoXY <36981700+shingoxy@users.noreply.github.com> Date: Mon, 24 Jun 2024 09:15:30 +0800 Subject: [PATCH 02/13] Update README.md --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 468ac2084b21a..a550875bee241 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,14 @@ +# 增加GC5603.c驱动并修改对应Kernel文件: +1. drivers/media/i2c/增加GC5603.c文件; +2. arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi修改Device tree; +3. arch/arm64/configs/lubancat_linux_rk3588_defconfig修改defconfig +4. drivers/media/i2c/Kconfig增加gc5603.c编译 +5. drivers/media/i2c/Makefile增加gc5603.c编译 +6. drivers/phy/rockchip/phy-rockchip-csi2-dphy.c修改后增加cphy支持 + +以上修改已经编译通过(2024年6月),将进行点亮验证。 + + # How do I submit patches to Android Common Kernels 1. BEST: Make all of your changes to upstream Linux. If appropriate, backport to the stable releases. From 42d830528e28da2c54ae5e5e6ef9e367a4ecf990 Mon Sep 17 00:00:00 2001 From: shingo <2877898@163.com> Date: Fri, 28 Jun 2024 10:37:07 +0800 Subject: [PATCH 03/13] =?UTF-8?q?=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20?= =?UTF-8?q?=20=20.version=20=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20a?= =?UTF-8?q?rch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi=20=09?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20arch/arm64/configs/lu?= =?UTF-8?q?bancat=5Flinux=5Frk3588=5Fdefconfig=20=09=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=EF=BC=9A=20=20=20=20=20drivers/media/i2c/gc5603.c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .version | 2 +- .../dts/rockchip/rk3588s-lubancat-csi2.dtsi | 37 +++++++++++++++---- .../configs/lubancat_linux_rk3588_defconfig | 2 +- drivers/media/i2c/gc5603.c | 32 ++++++++-------- 4 files changed, 48 insertions(+), 25 deletions(-) diff --git a/.version b/.version index d00491fd7e5bb..1e8b314962144 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -1 +6 diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi index 692c72f0043c3..26d37e1906f6e 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi @@ -58,14 +58,15 @@ }; }; - +//CAM 0 +/* Link path: sensor->csi2_dcphy0->mipi0_csi2->rkcif_mipi_lvds--->rkcif_mipi_lvds_sditf->rkisp0_vir0 */ &i2c1 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&i2c1m2_xfer>; dw9714_0: dw9714-0@c { - status = "okay"; + status = "disabled"; compatible = "dongwoon,dw9714"; reg = <0xc>; rockchip,camera-module-index = <0>; @@ -84,9 +85,14 @@ status = "okay"; reg = <0x31>; clocks = <&ext_cam_27m_clk>; - clock-names = "ext_cam_27m_clk"; + clock-names = "xvclk"; pwdn-gpios = <&gpio1 RK_PA3 GPIO_ACTIVE_LOW>; rotation = <180>; + + rockchip,camera-module-index = <0>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "GCDS01A"; + rockchip,camera-module-lens-name = "NoLens"; port { gc5603_out0: endpoint { @@ -193,13 +199,16 @@ }; }; + +//CAM 1 +/* Link path: sensor->csi2_dcphy1->mipi1_csi2->rkcif_mipi_lvds1--->rkcif_mipi_lvds1_sditf->rkisp0_vir1 */ &i2c5 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&i2c5m3_xfer>; dw9714_1: dw9714-1@c { - status = "okay"; + status = "disabled"; compatible = "dongwoon,dw9714"; reg = <0xc>; rockchip,camera-module-index = <0>; @@ -218,10 +227,15 @@ status = "okay"; reg = <0x31>; clocks = <&ext_cam_27m_clk>; - clock-names = "ext_cam_27m_clk"; + clock-names = "xvclk"; pwdn-gpios = <&gpio4 RK_PA5 GPIO_ACTIVE_LOW>; rotation = <180>; + rockchip,camera-module-index = <1>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "GCDS01A"; + rockchip,camera-module-lens-name = "NoLens"; + port { gc5603_out1: endpoint { remote-endpoint = <&dcphy1_in_gc5603>; @@ -327,13 +341,16 @@ }; }; + +//CAM2 +/* Link path: sensor->csi2_dphy1->mipi2_csi2->rkcif_mipi_lvds2--->rkcif_mipi_lvds2_sditf->rkisp0_vir2 */ &i2c6 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&i2c6m3_xfer>; dw9714: dw9714@c { - status = "okay"; + status = "disabled"; compatible = "dongwoon,dw9714"; reg = <0xc>; rockchip,camera-module-index = <0>; @@ -352,10 +369,14 @@ status = "okay"; reg = <0x31>; clocks = <&ext_cam_27m_clk>; - clock-names = "ext_cam_27m_clk"; + clock-names = "xvclk"; pwdn-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_LOW>; rotation = <180>; + rockchip,camera-module-index = <2>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "GCDS01A"; + rockchip,camera-module-lens-name = "NoLens"; port { gc5603_out2: endpoint { remote-endpoint = <&dphy0_in_gc5603>; @@ -407,7 +428,7 @@ dphy0_in_gc5603: endpoint@0 { reg = <0>; remote-endpoint = <&gc5603_out2>; - data-lanes = <1 2 3 4>; + data-lanes = <1 2>; }; dphy0_in_imx415: endpoint@1 { diff --git a/arch/arm64/configs/lubancat_linux_rk3588_defconfig b/arch/arm64/configs/lubancat_linux_rk3588_defconfig index 238366df74139..6c75e275429c2 100644 --- a/arch/arm64/configs/lubancat_linux_rk3588_defconfig +++ b/arch/arm64/configs/lubancat_linux_rk3588_defconfig @@ -490,7 +490,7 @@ CONFIG_VIDEO_OV5695=y CONFIG_VIDEO_OV7251=y CONFIG_VIDEO_OV8858=y CONFIG_VIDEO_OV13850=y -CONFIG_VIDEO_DW9714=y +#CONFIG_VIDEO_DW9714=y CONFIG_VIDEO_GC5603=y # CONFIG_VGA_ARB is not set CONFIG_DRM=y diff --git a/drivers/media/i2c/gc5603.c b/drivers/media/i2c/gc5603.c index 884b932130b50..da40b78201454 100644 --- a/drivers/media/i2c/gc5603.c +++ b/drivers/media/i2c/gc5603.c @@ -48,12 +48,8 @@ #define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x01) #define GC5603_NAME "gc5603" -#define GC5603_MEDIA_BUS_FMT MEDIA_BUS_FMT_SRGGB10_1X10 - //MEDIA_BUS_FMT_SBGGR10_1X10 - #define MIPI_FREQ_848M 423000000 -#define GC5603_XVCLK_FREQ 27000000 #define GC5603_PAGE_SELECT 0xFE @@ -103,6 +99,9 @@ #define GC_MIRROR_BIT_MASK BIT(0) #define GC_FLIP_BIT_MASK BIT(1) +#define GC5603_XVCLK_FREQ_24M 24000000 +#define GC5603_XVCLK_FREQ_27M 27000000 + static const char * const gc5603_supply_names[] = { "dovdd", /* Digital I/O power */ "avdd", /* Analog power */ @@ -119,6 +118,7 @@ struct regval { }; struct gc5603_mode { + u32 bus_fmt; u32 width; u32 height; struct v4l2_fract max_fps; @@ -128,6 +128,7 @@ struct gc5603_mode { const struct regval *reg_list; u32 hdr_mode; u32 vc[PAD_MAX]; + u32 xvclk; }; struct gc5603 { @@ -320,6 +321,7 @@ static const struct regval gc5603_2960x1666_regs_2lane[] = { static const struct gc5603_mode supported_modes[] = { { + .bus_fmt = MEDIA_BUS_FMT_SGRBG10_1X10, .width = 2960, .height = 1666, .max_fps = { @@ -330,9 +332,9 @@ static const struct gc5603_mode supported_modes[] = { .hts_def = 0x0C80, .vts_def = 0x06D6, .reg_list = gc5603_2960x1666_regs_2lane, - //.bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, .hdr_mode = NO_HDR, .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = GC5603_XVCLK_FREQ_24M }, }; @@ -740,7 +742,7 @@ static int gc5603_initialize_controls(struct gc5603 *gc5603) /* Calculate the delay in us by clock rate and clock cycles */ static inline u32 gc5603_cal_delay(u32 cycles) { - return DIV_ROUND_UP(cycles, GC5603_XVCLK_FREQ / 1000 / 1000); + return DIV_ROUND_UP(cycles, GC5603_XVCLK_FREQ_24M / 1000 / 1000); } static int __gc5603_power_on(struct gc5603 *gc5603) @@ -756,10 +758,10 @@ static int __gc5603_power_on(struct gc5603 *gc5603) dev_err(dev, "could not set pins\n"); } - ret = clk_set_rate(gc5603->xvclk, GC5603_XVCLK_FREQ); + ret = clk_set_rate(gc5603->xvclk, GC5603_XVCLK_FREQ_24M); if (ret < 0) dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); - if (clk_get_rate(gc5603->xvclk) != GC5603_XVCLK_FREQ) + if (clk_get_rate(gc5603->xvclk) != GC5603_XVCLK_FREQ_24M) dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); ret = clk_prepare_enable(gc5603->xvclk); if (ret < 0) { @@ -921,7 +923,7 @@ static int gc5603_get_channel_info(struct gc5603 *gc5603, struct rkmodule_channe ch_info->vc = gc5603->cur_mode->vc[ch_info->index]; ch_info->width = gc5603->cur_mode->width; ch_info->height = gc5603->cur_mode->height; - ch_info->bus_fmt = GC5603_MEDIA_BUS_FMT; + ch_info->bus_fmt = gc5603->cur_mode->bus_fmt; return 0; } @@ -1153,7 +1155,7 @@ static int gc5603_enum_mbus_code(struct v4l2_subdev *sd, { if (code->index != 0) return -EINVAL; - code->code = GC5603_MEDIA_BUS_FMT; + code->code = supported_modes[code->index].bus_fmt; return 0; } @@ -1166,7 +1168,7 @@ static int gc5603_enum_frame_sizes(struct v4l2_subdev *sd, if (fse->index >= gc5603->cfg_num) return -EINVAL; - if (fse->code != GC5603_MEDIA_BUS_FMT) + if (fse->code != supported_modes[fse->index].bus_fmt) return -EINVAL; fse->min_width = supported_modes[fse->index].width; @@ -1211,7 +1213,7 @@ static int gc5603_enum_frame_interval(struct v4l2_subdev *sd, if (fie->index >= gc5603->cfg_num) return -EINVAL; - fie->code = GC5603_MEDIA_BUS_FMT; + fie->code = supported_modes[fie->index].bus_fmt; fie->width = supported_modes[fie->index].width; fie->height = supported_modes[fie->index].height; fie->interval = supported_modes[fie->index].max_fps; @@ -1230,7 +1232,7 @@ static int gc5603_set_fmt(struct v4l2_subdev *sd, mutex_lock(&gc5603->mutex); mode = gc5603_find_best_fit(gc5603, fmt); - fmt->format.code = GC5603_MEDIA_BUS_FMT; + fmt->format.code = mode->bus_fmt; fmt->format.width = mode->width; fmt->format.height = mode->height; fmt->format.field = V4L2_FIELD_NONE; @@ -1274,7 +1276,7 @@ static int gc5603_get_fmt(struct v4l2_subdev *sd, } else { fmt->format.width = mode->width; fmt->format.height = mode->height; - fmt->format.code = GC5603_MEDIA_BUS_FMT; + fmt->format.code = mode->bus_fmt; fmt->format.field = V4L2_FIELD_NONE; /* format info: width/height/data type/virctual channel */ @@ -1300,7 +1302,7 @@ static int gc5603_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) /* Initialize try_fmt */ try_fmt->width = def_mode->width; try_fmt->height = def_mode->height; - try_fmt->code = GC5603_MEDIA_BUS_FMT; + try_fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; try_fmt->field = V4L2_FIELD_NONE; mutex_unlock(&gc5603->mutex); From f890b18d86dc4e346971684660c75f0972b70f6b Mon Sep 17 00:00:00 2001 From: shingo <2877898@163.com> Date: Fri, 28 Jun 2024 11:20:56 +0800 Subject: [PATCH 04/13] =?UTF-8?q?=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20?= =?UTF-8?q?=20=20README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a550875bee241..202639dff8072 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# 20240628:GC5603成功点亮,工作正常,i2c通信正常; + # 增加GC5603.c驱动并修改对应Kernel文件: 1. drivers/media/i2c/增加GC5603.c文件; 2. arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi修改Device tree; From c8d474b89b3722caa603694985ca564f3167ab11 Mon Sep 17 00:00:00 2001 From: ShingoXY <36981700+shingoxy@users.noreply.github.com> Date: Fri, 28 Jun 2024 11:22:51 +0800 Subject: [PATCH 05/13] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 202639dff8072..f31ac2d7ac557 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -# 20240628:GC5603成功点亮,工作正常,i2c通信正常; +# 20240628:GC5603成功点亮,工作正常,i2c通信正常: +image + + # 增加GC5603.c驱动并修改对应Kernel文件: 1. drivers/media/i2c/增加GC5603.c文件; From eff5fc08d4de8f0d85434f9c80548749befb82b3 Mon Sep 17 00:00:00 2001 From: shingoxy <2877898@163.com> Date: Wed, 10 Jul 2024 12:03:38 +0800 Subject: [PATCH 06/13] =?UTF-8?q?=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20?= =?UTF-8?q?=20=20arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi?= =?UTF-8?q?=20=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20arch/arm64/conf?= =?UTF-8?q?igs/lubancat=5Flinux=5Frk3588=5Fdefconfig=20=09=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=EF=BC=9A=20=20=20=20=20arch/arm64/configs/rockchip=5F?= =?UTF-8?q?defconfig=20=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20arch/a?= =?UTF-8?q?rm64/configs/rockchip=5Flinux=5Fdefconfig=20=09=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=EF=BC=9A=20=20=20=20=20drivers/media/i2c/gc5603.c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dts/rockchip/rk3588s-lubancat-csi2.dtsi | 162 +++++------------- 1 file changed, 44 insertions(+), 118 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi index 26d37e1906f6e..27d5c47c98068 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi @@ -17,6 +17,12 @@ clock-output-names = "ext_cam_27m_clk"; #clock-cells = <0>; }; + ext_cam_24m_clk: external-camera-24m-clock { + compatible = "fixed-clock"; + clock-frequency = <24000000>; + clock-output-names = "ext_cam_24m_clk"; + #clock-cells = <0>; + }; vdd_cam_5v: vdd-cam-5v-regulator { compatible = "regulator-fixed"; @@ -65,35 +71,18 @@ pinctrl-names = "default"; pinctrl-0 = <&i2c1m2_xfer>; - dw9714_0: dw9714-0@c { - status = "disabled"; - compatible = "dongwoon,dw9714"; - reg = <0xc>; - rockchip,camera-module-index = <0>; - rockchip,vcm-max-current = <100>; - rockchip,vcm-start-current = <0>; - rockchip,vcm-rated-current = <100>; - rockchip,vcm-step-mode = <0xd>; - rockchip,vcm-dlc-enable = <0>; - rockchip,vcm-mclk = <0>; - rockchip,vcm-t-src = <0>; - rockchip,camera-module-facing = "back"; - }; - - gc5603_0: gc5603-0@31 { + gc5603_0: gc5603@31 { compatible = "galaxycore,gc5603"; - status = "okay"; + status = "disabled"; reg = <0x31>; - clocks = <&ext_cam_27m_clk>; + clocks = <&ext_cam_24m_clk>; clock-names = "xvclk"; - pwdn-gpios = <&gpio1 RK_PA3 GPIO_ACTIVE_LOW>; - rotation = <180>; - - rockchip,camera-module-index = <0>; + pwdn-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_LOW>; + + rockchip,camera-module-index = <1>; rockchip,camera-module-facing = "back"; rockchip,camera-module-name = "GCDS01A"; - rockchip,camera-module-lens-name = "NoLens"; - + rockchip,camera-module-lens-name = "default"; port { gc5603_out0: endpoint { remote-endpoint = <&dcphy0_in_gc5603>; @@ -102,18 +91,18 @@ }; }; - imx415_0: imx415-0@1a { + imx415_0: imx415@1a { compatible = "sony,imx415"; - status = "okay"; + status = "disabled"; reg = <0x1a>; clocks = <&ext_cam_37m_clk>; clock-names = "xvclk"; avdd-supply = <&cam_avdd>; dovdd-supply = <&cam_dovdd>; dvdd-supply = <&cam_dvdd>; - reset-gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_LOW>; - rockchip,camera-module-index = <0>; + rockchip,camera-module-index = <2>; rockchip,camera-module-facing = "back"; rockchip,camera-module-name = "CMK-OT2022-PX1"; rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; @@ -127,11 +116,11 @@ }; &mipi_dcphy0 { - status = "okay"; + status = "disabled"; }; &csi2_dcphy0 { - status = "okay"; + status = "disabled"; ports { #address-cells = <1>; @@ -147,7 +136,6 @@ remote-endpoint = <&gc5603_out0>; data-lanes = <1 2>; }; - dcphy0_in_imx415: endpoint@1 { reg = <1>; remote-endpoint = <&imx415_out0>; @@ -169,7 +157,7 @@ }; &mipi0_csi2 { - status = "okay"; + status = "disabled"; ports { #address-cells = <1>; @@ -206,36 +194,19 @@ status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&i2c5m3_xfer>; - - dw9714_1: dw9714-1@c { - status = "disabled"; - compatible = "dongwoon,dw9714"; - reg = <0xc>; - rockchip,camera-module-index = <0>; - rockchip,vcm-max-current = <100>; - rockchip,vcm-start-current = <0>; - rockchip,vcm-rated-current = <100>; - rockchip,vcm-step-mode = <0xd>; - rockchip,vcm-dlc-enable = <0>; - rockchip,vcm-mclk = <0>; - rockchip,vcm-t-src = <0>; - rockchip,camera-module-facing = "back"; - }; - gc5603_1: gc5603-1@31 { + gc5603_1: gc5603@31 { compatible = "galaxycore,gc5603"; - status = "okay"; + status = "disabled"; reg = <0x31>; - clocks = <&ext_cam_27m_clk>; + clocks = <&ext_cam_24m_clk>; clock-names = "xvclk"; - pwdn-gpios = <&gpio4 RK_PA5 GPIO_ACTIVE_LOW>; - rotation = <180>; + pwdn-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_LOW>; rockchip,camera-module-index = <1>; rockchip,camera-module-facing = "back"; rockchip,camera-module-name = "GCDS01A"; - rockchip,camera-module-lens-name = "NoLens"; - + rockchip,camera-module-lens-name = "default"; port { gc5603_out1: endpoint { remote-endpoint = <&dcphy1_in_gc5603>; @@ -243,37 +214,14 @@ }; }; }; - - imx415_1: imx415-1@1a { - compatible = "sony,imx415"; - status = "okay"; - reg = <0x1a>; - clocks = <&ext_cam_37m_clk>; - clock-names = "xvclk"; - avdd-supply = <&cam_avdd>; - dovdd-supply = <&cam_dovdd>; - dvdd-supply = <&cam_dvdd>; - reset-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_LOW>; - - rockchip,camera-module-index = <1>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "CMK-OT2022-PX1"; - rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; - port { - imx415_out1: endpoint { - remote-endpoint = <&dcphy1_in_imx415>; - data-lanes = <1 2 3 4>; - }; - }; - }; }; &mipi_dcphy1 { - status = "okay"; + status = "disabled"; }; &csi2_dcphy1 { - status = "okay"; + status = "disabled"; ports { #address-cells = <1>; @@ -283,18 +231,12 @@ reg = <0>; #address-cells = <1>; #size-cells = <0>; - + dcphy1_in_gc5603: endpoint@0 { reg = <0>; remote-endpoint = <&gc5603_out1>; data-lanes = <1 2>; }; - - dcphy1_in_imx415: endpoint@1 { - reg = <1>; - remote-endpoint = <&imx415_out1>; - data-lanes = <1 2 3 4>; - }; }; port@1 { @@ -311,7 +253,7 @@ }; &mipi1_csi2 { - status = "okay"; + status = "disabled"; ports { #address-cells = <1>; @@ -343,40 +285,24 @@ //CAM2 -/* Link path: sensor->csi2_dphy1->mipi2_csi2->rkcif_mipi_lvds2--->rkcif_mipi_lvds2_sditf->rkisp0_vir2 */ +/* Link path: sensor->csi2_dphy0->mipi2_csi2->cif_mipi2--->rkcif_mipi_lvds2->rkisp1_vir0 */ &i2c6 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&i2c6m3_xfer>; - dw9714: dw9714@c { - status = "disabled"; - compatible = "dongwoon,dw9714"; - reg = <0xc>; - rockchip,camera-module-index = <0>; - rockchip,vcm-max-current = <100>; - rockchip,vcm-start-current = <0>; - rockchip,vcm-rated-current = <100>; - rockchip,vcm-step-mode = <0xd>; - rockchip,vcm-dlc-enable = <0>; - rockchip,vcm-mclk = <0>; - rockchip,vcm-t-src = <0>; - rockchip,camera-module-facing = "back"; - }; - - gc5603_2: gc5603_2@31 { + gc5603_2: gc5603@31 { compatible = "galaxycore,gc5603"; status = "okay"; reg = <0x31>; - clocks = <&ext_cam_27m_clk>; + clocks = <&ext_cam_24m_clk>; clock-names = "xvclk"; pwdn-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_LOW>; - rotation = <180>; - rockchip,camera-module-index = <2>; + rockchip,camera-module-index = <1>; rockchip,camera-module-facing = "back"; rockchip,camera-module-name = "GCDS01A"; - rockchip,camera-module-lens-name = "NoLens"; + rockchip,camera-module-lens-name = "default"; port { gc5603_out2: endpoint { remote-endpoint = <&dphy0_in_gc5603>; @@ -385,9 +311,9 @@ }; }; - imx415_2: imx415-2@1a { + imx415: imx415@1a { compatible = "sony,imx415"; - status = "okay"; + status = "disabled"; reg = <0x1a>; clocks = <&ext_cam_37m_clk>; clock-names = "xvclk"; @@ -492,7 +418,7 @@ }; &rkcif_mipi_lvds { - status = "okay"; + status = "disabled"; port { cif_mipi0_in0: endpoint { @@ -502,7 +428,7 @@ }; &rkcif_mipi_lvds_sditf { - status = "okay"; + status = "disabled"; port { mipi_lvds_sditf: endpoint { @@ -512,7 +438,7 @@ }; &rkcif_mipi_lvds1 { - status = "okay"; + status = "disabled"; port { cif_mipi1_in0: endpoint { @@ -522,7 +448,7 @@ }; &rkcif_mipi_lvds1_sditf { - status = "okay"; + status = "disabled"; port { mipi_lvds1_sditf: endpoint { @@ -552,15 +478,15 @@ }; &rkisp0 { - status = "okay"; + status = "disabled"; }; &isp0_mmu { - status = "okay"; + status = "disabled"; }; &rkisp0_vir0 { - status = "okay"; + status = "disabled"; port { #address-cells = <1>; @@ -574,7 +500,7 @@ }; &rkisp0_vir1 { - status = "okay"; + status = "disabled"; port { #address-cells = <1>; From 49abc6aeee0b4e8fa2638246ead0365c3dc81c30 Mon Sep 17 00:00:00 2001 From: shingoxy <2877898@163.com> Date: Wed, 10 Jul 2024 12:21:50 +0800 Subject: [PATCH 07/13] =?UTF-8?q?=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20?= =?UTF-8?q?=20=20arch/arm64/configs/lubancat=5Flinux=5Frk3588=5Fdefconfig?= =?UTF-8?q?=20=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20arch/arm64/conf?= =?UTF-8?q?igs/rockchip=5Fdefconfig=20=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20?= =?UTF-8?q?=20=20=20arch/arm64/configs/rockchip=5Flinux=5Fdefconfig=20=09?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20drivers/media/i2c/gc5?= =?UTF-8?q?603.c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../configs/lubancat_linux_rk3588_defconfig | 3 +- arch/arm64/configs/rockchip_defconfig | 1 + arch/arm64/configs/rockchip_linux_defconfig | 3 +- drivers/media/i2c/gc5603.c | 49 +- drivers/media/i2c/imx415.c | 1185 ++--------------- drivers/media/platform/rockchip/cif/capture.c | 3 + 6 files changed, 130 insertions(+), 1114 deletions(-) diff --git a/arch/arm64/configs/lubancat_linux_rk3588_defconfig b/arch/arm64/configs/lubancat_linux_rk3588_defconfig index 4934f0dec840a..beeb64072a59b 100644 --- a/arch/arm64/configs/lubancat_linux_rk3588_defconfig +++ b/arch/arm64/configs/lubancat_linux_rk3588_defconfig @@ -467,8 +467,10 @@ CONFIG_USB_VIDEO_CLASS=y CONFIG_V4L_PLATFORM_DRIVERS=y CONFIG_VIDEO_ROCKCHIP_CIF=y CONFIG_VIDEO_ROCKCHIP_ISP=y +CONFIG_VIDEO_ROCKCHIP_RKISP1=y CONFIG_VIDEO_ROCKCHIP_ISP_VERSION_V1X=y CONFIG_VIDEO_ROCKCHIP_ISP_VERSION_V21=y +CONFIG_VIDEO_ROCKCHIP_ISP_VERSION_V30=y CONFIG_VIDEO_ROCKCHIP_ISPP=y CONFIG_VIDEO_ROCKCHIP_HDMIRX=y CONFIG_V4L_MEM2MEM_DRIVERS=y @@ -490,7 +492,6 @@ CONFIG_VIDEO_OV5695=y CONFIG_VIDEO_OV7251=y CONFIG_VIDEO_OV8858=y CONFIG_VIDEO_OV13850=y -#CONFIG_VIDEO_DW9714=y CONFIG_VIDEO_GC5603=y # CONFIG_VGA_ARB is not set CONFIG_DRM=y diff --git a/arch/arm64/configs/rockchip_defconfig b/arch/arm64/configs/rockchip_defconfig index 7aae017d80a9f..004d113da6e47 100644 --- a/arch/arm64/configs/rockchip_defconfig +++ b/arch/arm64/configs/rockchip_defconfig @@ -592,6 +592,7 @@ CONFIG_VIDEO_TC35874X=y CONFIG_VIDEO_THCV244=y CONFIG_VIDEO_RK_IRCUT=y CONFIG_VIDEO_GC2053=y +CONFIG_VIDEO_GC5603=y CONFIG_VIDEO_GC2093=y CONFIG_VIDEO_GC2145=y CONFIG_VIDEO_GC2385=y diff --git a/arch/arm64/configs/rockchip_linux_defconfig b/arch/arm64/configs/rockchip_linux_defconfig index a9639edaca7e2..b6c973dae825e 100644 --- a/arch/arm64/configs/rockchip_linux_defconfig +++ b/arch/arm64/configs/rockchip_linux_defconfig @@ -35,7 +35,7 @@ CONFIG_ARCH_ROCKCHIP=y # CONFIG_ARM64_ERRATUM_1542419 is not set # CONFIG_ARM64_ERRATUM_1508412 is not set # CONFIG_ARM64_ERRATUM_2051678 is not set -# CONFIG_ARM64_ERRATUM_2054223 is not set +# CONFIG_ARM64_ERRATUM_2054223 is not setf # CONFIG_ARM64_ERRATUM_2067961 is not set # CONFIG_CAVIUM_ERRATUM_22375 is not set # CONFIG_CAVIUM_ERRATUM_23154 is not set @@ -317,6 +317,7 @@ CONFIG_VIDEO_RK628_BT1120=y CONFIG_VIDEO_TC35874X=y CONFIG_VIDEO_RK_IRCUT=y CONFIG_VIDEO_GC8034=y +CONFIG_VIDEO_GC5603=y CONFIG_VIDEO_IMX415=y CONFIG_VIDEO_IMX464=y CONFIG_VIDEO_OS04A10=y diff --git a/drivers/media/i2c/gc5603.c b/drivers/media/i2c/gc5603.c index da40b78201454..dcc053a62a262 100644 --- a/drivers/media/i2c/gc5603.c +++ b/drivers/media/i2c/gc5603.c @@ -46,7 +46,7 @@ #include #include -#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x01) +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x08) #define GC5603_NAME "gc5603" #define MIPI_FREQ_848M 423000000 @@ -154,13 +154,14 @@ struct gc5603 { struct v4l2_ctrl *v_flip; struct mutex mutex; bool streaming; - bool power_on; - const struct gc5603_mode *cur_mode; unsigned int lane_num; unsigned int cfg_num; unsigned int pixel_rate; + bool power_on; + const struct gc5603_mode *cur_mode; + - u32 module_index; + u32 module_index; const char *module_facing; const char *module_name; const char *len_name; @@ -952,6 +953,9 @@ static long gc5603_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) struct rkmodule_channel_info *ch_info; switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + gc5603_get_module_inf(gc5603, (struct rkmodule_inf *)arg); + break; case RKMODULE_GET_HDR_CFG: hdr_cfg = (struct rkmodule_hdr_cfg *)arg; hdr_cfg->esp.mode = HDR_NORMAL_VC; @@ -960,9 +964,7 @@ static long gc5603_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) case RKMODULE_SET_HDR_CFG: case RKMODULE_SET_CONVERSION_GAIN: break; - case RKMODULE_GET_MODULE_INFO: - gc5603_get_module_inf(gc5603, (struct rkmodule_inf *)arg); - break; + case RKMODULE_AWB_CFG: gc5603_set_awb_cfg(gc5603, (struct rkmodule_awb_cfg *)arg); break; @@ -1424,16 +1426,14 @@ static int gc5603_probe(struct i2c_client *client, gc5603->client = client; ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, &gc5603->module_index); - if (ret) { - dev_warn(dev, "could not get module index!\n"); - gc5603->module_index = 0; - } + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, &gc5603->module_facing); ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, &gc5603->module_name); ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, &gc5603->len_name); + dev_info(dev, "Module Information: index = %d, Facing = %s, ModuleName = %s, LensName = %s", gc5603->module_index, gc5603->module_facing, gc5603->module_name, gc5603->len_name); if (ret) { dev_err(dev, "could not get module information!\n"); @@ -1458,11 +1458,7 @@ static int gc5603_probe(struct i2c_client *client, if (IS_ERR(gc5603->pwdn_gpio)) dev_warn(dev, "Failed to get power-gpios\n"); - ret = gc5603_configure_regulators(gc5603); - if (ret) { - dev_err(dev, "Failed to get power regulators\n"); - return ret; - } + ret = gc5603_parse_of(gc5603); if (ret != 0) @@ -1485,6 +1481,11 @@ static int gc5603_probe(struct i2c_client *client, dev_err(dev, "no pinctrl\n"); } + ret = gc5603_configure_regulators(gc5603); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } mutex_init(&gc5603->mutex); sd = &gc5603->subdev; @@ -1504,7 +1505,8 @@ static int gc5603_probe(struct i2c_client *client, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &gc5603_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; #endif #if defined(CONFIG_MEDIA_CONTROLLER) gc5603->pad.flags = MEDIA_PAD_FL_SOURCE; @@ -1570,11 +1572,6 @@ static int gc5603_remove(struct i2c_client *client) return 0; } -static const struct i2c_device_id gc5603_match_id[] = { - { "gc5603", 0 }, - { }, -}; - #if IS_ENABLED(CONFIG_OF) static const struct of_device_id gc5603_of_match[] = { { .compatible = "galaxycore,gc5603" }, @@ -1582,6 +1579,12 @@ static const struct of_device_id gc5603_of_match[] = { }; MODULE_DEVICE_TABLE(of, gc5603_of_match); #endif +static const struct i2c_device_id gc5603_match_id[] = { + { "galaxycore, gc5603", 0 }, + { }, +}; + + static struct i2c_driver gc5603_i2c_driver = { .driver = { @@ -1607,6 +1610,6 @@ static void __exit sensor_mod_exit(void) device_initcall_sync(sensor_mod_init); module_exit(sensor_mod_exit); -MODULE_DESCRIPTION("GC2035 CMOS Image Sensor driver"); +MODULE_DESCRIPTION("GC5603 CMOS Image Sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c index 3c78fe098528d..83d968ab2314a 100644 --- a/drivers/media/i2c/imx415.c +++ b/drivers/media/i2c/imx415.c @@ -58,22 +58,17 @@ #define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN #endif -#define MIPI_FREQ_1188M 1188000000 #define MIPI_FREQ_891M 891000000 #define MIPI_FREQ_446M 446000000 #define MIPI_FREQ_743M 743000000 #define MIPI_FREQ_297M 297000000 #define IMX415_4LANES 4 -#define IMX415_2LANES 2 #define IMX415_MAX_PIXEL_RATE (MIPI_FREQ_891M / 10 * 2 * IMX415_4LANES) #define OF_CAMERA_HDR_MODE "rockchip,camera-hdr-mode" -#define DATA_LANES "rockchip,imx415-data-lanes" -#define OF_CAMERA_CAPTURE_MODE "rockchip,imx415-capture-mode" #define IMX415_XVCLK_FREQ_37M 37125000 -#define IMX415_XVCLK_FREQ_27M 27000000 /* TODO: Get the real chip id from reg */ #define CHIP_ID 0xE0 @@ -147,7 +142,6 @@ #define IMX415_FLIP_REG 0x3030 #define REG_NULL 0xFFFF -#define REG_DELAY 0xFFFE #define IMX415_REG_VALUE_08BIT 1 #define IMX415_REG_VALUE_16BIT 2 @@ -200,7 +194,6 @@ struct imx415_mode { const struct regval *reg_list; u32 hdr_mode; u32 vc[PAD_MAX]; - u32 xvclk; }; struct imx415 { @@ -230,7 +223,6 @@ struct imx415 { bool is_thunderboot; bool is_thunderboot_ng; bool is_first_streamoff; - const struct imx415_mode *supported_modes; const struct imx415_mode *cur_mode; u32 module_index; u32 cfg_num; @@ -240,8 +232,6 @@ struct imx415 { u32 cur_vts; bool has_init_exp; struct preisp_hdrae_exp_s init_hdrae_exp; - u32 lanes; - u32 capture_mode; }; static struct rkmodule_csi_dphy_param dcphy_param = { @@ -755,893 +745,6 @@ static __maybe_unused const struct regval imx415_hdr2_12bit_1932x1096_891M_regs[ {REG_NULL, 0x00}, }; -/* - * Xclk 27Mhz - * 15fps - * CSI-2_2lane - * AD:12bit Output:12bit - * 891Mbps - * Master Mode - * Time 9.988ms Gain:6dB - * All-pixel - */ -static __maybe_unused const struct regval imx415_linear_12bit_3864x2192_891M_regs_2lane[] = { - {0x3008, 0x5D}, - {0x300A, 0x42}, - {0x3028, 0x98}, - {0x3029, 0x08}, - {0x3033, 0x05}, - {0x3050, 0x79}, - {0x3051, 0x07}, - {0x3090, 0x14}, - {0x30C1, 0x00}, - {0x3116, 0x23}, - {0x3118, 0xC6}, - {0x311A, 0xE7}, - {0x311E, 0x23}, - {0x32D4, 0x21}, - {0x32EC, 0xA1}, - {0x344C, 0x2B}, - {0x344D, 0x01}, - {0x344E, 0xED}, - {0x344F, 0x01}, - {0x3450, 0xF6}, - {0x3451, 0x02}, - {0x3452, 0x7F}, - {0x3453, 0x03}, - {0x358A, 0x04}, - {0x35A1, 0x02}, - {0x35EC, 0x27}, - {0x35EE, 0x8D}, - {0x35F0, 0x8D}, - {0x35F2, 0x29}, - {0x36BC, 0x0C}, - {0x36CC, 0x53}, - {0x36CD, 0x00}, - {0x36CE, 0x3C}, - {0x36D0, 0x8C}, - {0x36D1, 0x00}, - {0x36D2, 0x71}, - {0x36D4, 0x3C}, - {0x36D6, 0x53}, - {0x36D7, 0x00}, - {0x36D8, 0x71}, - {0x36DA, 0x8C}, - {0x36DB, 0x00}, - {0x3720, 0x00}, - {0x3724, 0x02}, - {0x3726, 0x02}, - {0x3732, 0x02}, - {0x3734, 0x03}, - {0x3736, 0x03}, - {0x3742, 0x03}, - {0x3862, 0xE0}, - {0x38CC, 0x30}, - {0x38CD, 0x2F}, - {0x395C, 0x0C}, - {0x39A4, 0x07}, - {0x39A8, 0x32}, - {0x39AA, 0x32}, - {0x39AC, 0x32}, - {0x39AE, 0x32}, - {0x39B0, 0x32}, - {0x39B2, 0x2F}, - {0x39B4, 0x2D}, - {0x39B6, 0x28}, - {0x39B8, 0x30}, - {0x39BA, 0x30}, - {0x39BC, 0x30}, - {0x39BE, 0x30}, - {0x39C0, 0x30}, - {0x39C2, 0x2E}, - {0x39C4, 0x2B}, - {0x39C6, 0x25}, - {0x3A42, 0xD1}, - {0x3A4C, 0x77}, - {0x3AE0, 0x02}, - {0x3AEC, 0x0C}, - {0x3B00, 0x2E}, - {0x3B06, 0x29}, - {0x3B98, 0x25}, - {0x3B99, 0x21}, - {0x3B9B, 0x13}, - {0x3B9C, 0x13}, - {0x3B9D, 0x13}, - {0x3B9E, 0x13}, - {0x3BA1, 0x00}, - {0x3BA2, 0x06}, - {0x3BA3, 0x0B}, - {0x3BA4, 0x10}, - {0x3BA5, 0x14}, - {0x3BA6, 0x18}, - {0x3BA7, 0x1A}, - {0x3BA8, 0x1A}, - {0x3BA9, 0x1A}, - {0x3BAC, 0xED}, - {0x3BAD, 0x01}, - {0x3BAE, 0xF6}, - {0x3BAF, 0x02}, - {0x3BB0, 0xA2}, - {0x3BB1, 0x03}, - {0x3BB2, 0xE0}, - {0x3BB3, 0x03}, - {0x3BB4, 0xE0}, - {0x3BB5, 0x03}, - {0x3BB6, 0xE0}, - {0x3BB7, 0x03}, - {0x3BB8, 0xE0}, - {0x3BBA, 0xE0}, - {0x3BBC, 0xDA}, - {0x3BBE, 0x88}, - {0x3BC0, 0x44}, - {0x3BC2, 0x7B}, - {0x3BC4, 0xA2}, - {0x3BC8, 0xBD}, - {0x3BCA, 0xBD}, - {0x4001, 0x01}, - {0x4004, 0xC0}, - {0x4005, 0x06}, - {0x400C, 0x00}, - {0x4018, 0x7F}, - {0x401A, 0x37}, - {0x401C, 0x37}, - {0x401E, 0xF7}, - {0x401F, 0x00}, - {0x4020, 0x3F}, - {0x4022, 0x6F}, - {0x4024, 0x3F}, - {0x4026, 0x5F}, - {0x4028, 0x2F}, - {0x4074, 0x01}, - {0x3002, 0x00}, - //{0x3000, 0x00}, - {REG_DELAY, 0x1E},//wait_ms(30) - {REG_NULL, 0x00}, -}; - -static __maybe_unused const struct regval imx415_linear_10bit_3864x2192_891M_regs_2lane[] = { - {0x3008, 0x7F}, - {0x3031, 0x00}, - {0x3032, 0x00}, - {0x300A, 0x5B}, - {0x3028, 0x98}, - {0x3029, 0x08}, - {0x3033, 0x05}, - {0x3050, 0x79}, - {0x3051, 0x07}, - {0x3090, 0x14}, - {0x30C1, 0x00}, - {0x3116, 0x24}, - {0x3118, 0xC0}, - {0x311A, 0xE0}, - {0x311E, 0x24}, - {0x32D4, 0x21}, - {0x32EC, 0xA1}, - {0x344C, 0x2B}, - {0x344D, 0x01}, - {0x344E, 0xED}, - {0x344F, 0x01}, - {0x3450, 0xF6}, - {0x3451, 0x02}, - {0x3452, 0x7F}, - {0x3453, 0x03}, - {0x358A, 0x04}, - {0x35A1, 0x02}, - {0x35EC, 0x27}, - {0x35EE, 0x8D}, - {0x35F0, 0x8D}, - {0x35F2, 0x29}, - {0x36BC, 0x0C}, - {0x36CC, 0x53}, - {0x36CD, 0x00}, - {0x36CE, 0x3C}, - {0x36D0, 0x8C}, - {0x36D1, 0x00}, - {0x36D2, 0x71}, - {0x36D4, 0x3C}, - {0x36D6, 0x53}, - {0x36D7, 0x00}, - {0x36D8, 0x71}, - {0x36DA, 0x8C}, - {0x36DB, 0x00}, - {0x3720, 0x00}, - {0x3724, 0x02}, - {0x3726, 0x02}, - {0x3732, 0x02}, - {0x3734, 0x03}, - {0x3736, 0x03}, - {0x3742, 0x03}, - {0x3862, 0xE0}, - {0x38CC, 0x30}, - {0x38CD, 0x2F}, - {0x395C, 0x0C}, - {0x39A4, 0x07}, - {0x39A8, 0x32}, - {0x39AA, 0x32}, - {0x39AC, 0x32}, - {0x39AE, 0x32}, - {0x39B0, 0x32}, - {0x39B2, 0x2F}, - {0x39B4, 0x2D}, - {0x39B6, 0x28}, - {0x39B8, 0x30}, - {0x39BA, 0x30}, - {0x39BC, 0x30}, - {0x39BE, 0x30}, - {0x39C0, 0x30}, - {0x39C2, 0x2E}, - {0x39C4, 0x2B}, - {0x39C6, 0x25}, - {0x3A42, 0xD1}, - {0x3A4C, 0x77}, - {0x3AE0, 0x02}, - {0x3AEC, 0x0C}, - {0x3B00, 0x2E}, - {0x3B06, 0x29}, - {0x3B98, 0x25}, - {0x3B99, 0x21}, - {0x3B9B, 0x13}, - {0x3B9C, 0x13}, - {0x3B9D, 0x13}, - {0x3B9E, 0x13}, - {0x3BA1, 0x00}, - {0x3BA2, 0x06}, - {0x3BA3, 0x0B}, - {0x3BA4, 0x10}, - {0x3BA5, 0x14}, - {0x3BA6, 0x18}, - {0x3BA7, 0x1A}, - {0x3BA8, 0x1A}, - {0x3BA9, 0x1A}, - {0x3BAC, 0xED}, - {0x3BAD, 0x01}, - {0x3BAE, 0xF6}, - {0x3BAF, 0x02}, - {0x3BB0, 0xA2}, - {0x3BB1, 0x03}, - {0x3BB2, 0xE0}, - {0x3BB3, 0x03}, - {0x3BB4, 0xE0}, - {0x3BB5, 0x03}, - {0x3BB6, 0xE0}, - {0x3BB7, 0x03}, - {0x3BB8, 0xE0}, - {0x3BBA, 0xE0}, - {0x3BBC, 0xDA}, - {0x3BBE, 0x88}, - {0x3BC0, 0x44}, - {0x3BC2, 0x7B}, - {0x3BC4, 0xA2}, - {0x3BC8, 0xBD}, - {0x3BCA, 0xBD}, - {0x4001, 0x01}, - {0x4004, 0x48}, - {0x4005, 0x09}, - {0x400C, 0x00}, - {0x4018, 0x7F}, - {0x401A, 0x37}, - {0x401C, 0x37}, - {0x401E, 0xF7}, - {0x401F, 0x00}, - {0x4020, 0x3F}, - {0x4022, 0x6F}, - {0x4024, 0x3F}, - {0x4026, 0x5F}, - {0x4028, 0x2F}, - {0x4074, 0x01}, - {0x3002, 0x00}, - //{0x3000, 0x00}, - {REG_DELAY, 0x1E},//wait_ms(30) - {REG_NULL, 0x00}, -}; - - -static __maybe_unused const struct regval imx415_linear_10bit_1920x1080_891M_regs_2lane[] = { - {0x3008, 0x7f}, - {0x300A, 0x5b}, - {0x3034, 0x05}, - // {0x301C, 0x04}, - {0x3020, 0x01}, - {0x3021, 0x01}, - {0x3022, 0x01}, - {0x3024, 0xca}, - {0x3025, 0x08}, - {0x3028, 0x26}, - {0x3029, 0x02}, - {0x3031, 0x00}, - {0x3032, 0x00}, - {0x3033, 0x05}, - {0x3050, 0xC4}, - {0x3090, 0x14}, - {0x30C1, 0x00}, - {0x30D9, 0x02}, - {0x30DA, 0x01}, - {0x3116, 0x24}, - {0x3118, 0xC0}, - {0x311A, 0xE0}, - {0x311E, 0x24}, - {0x32D4, 0x21}, - {0x32EC, 0xA1}, - {0x344C, 0x2B}, - {0x344D, 0x01}, - {0x344E, 0xED}, - {0x344F, 0x01}, - {0x3450, 0xF6}, - {0x3451, 0x02}, - {0x3452, 0x7F}, - {0x3453, 0x03}, - {0x358A, 0x04}, - {0x35A1, 0x02}, - {0x35EC, 0x27}, - {0x35EE, 0x8D}, - {0x35F0, 0x8D}, - {0x35F2, 0x29}, - {0x36BC, 0x0C}, - {0x36CC, 0x53}, - {0x36CD, 0x00}, - {0x36CE, 0x3C}, - {0x36D0, 0x8C}, - {0x36D1, 0x00}, - {0x36D2, 0x71}, - {0x36D4, 0x3C}, - {0x36D6, 0x53}, - {0x36D7, 0x00}, - {0x36D8, 0x71}, - {0x36DA, 0x8C}, - {0x36DB, 0x00}, - {0x3701, 0x00}, - {0x3720, 0x00}, - {0x3724, 0x02}, - {0x3726, 0x02}, - {0x3732, 0x02}, - {0x3734, 0x03}, - {0x3736, 0x03}, - {0x3742, 0x03}, - {0x3862, 0xE0}, - {0x38CC, 0x30}, - {0x38CD, 0x2F}, - {0x395C, 0x0C}, - {0x39A4, 0x07}, - {0x39A8, 0x32}, - {0x39AA, 0x32}, - {0x39AC, 0x32}, - {0x39AE, 0x32}, - {0x39B0, 0x32}, - {0x39B2, 0x2F}, - {0x39B4, 0x2D}, - {0x39B6, 0x28}, - {0x39B8, 0x30}, - {0x39BA, 0x30}, - {0x39BC, 0x30}, - {0x39BE, 0x30}, - {0x39C0, 0x30}, - {0x39C2, 0x2E}, - {0x39C4, 0x2B}, - {0x39C6, 0x25}, - {0x3A42, 0xD1}, - {0x3A4C, 0x77}, - {0x3AE0, 0x02}, - {0x3AEC, 0x0C}, - {0x3B00, 0x2E}, - {0x3B06, 0x29}, - {0x3B98, 0x25}, - {0x3B99, 0x21}, - {0x3B9B, 0x13}, - {0x3B9C, 0x13}, - {0x3B9D, 0x13}, - {0x3B9E, 0x13}, - {0x3BA1, 0x00}, - {0x3BA2, 0x06}, - {0x3BA3, 0x0B}, - {0x3BA4, 0x10}, - {0x3BA5, 0x14}, - {0x3BA6, 0x18}, - {0x3BA7, 0x1A}, - {0x3BA8, 0x1A}, - {0x3BA9, 0x1A}, - {0x3BAC, 0xED}, - {0x3BAD, 0x01}, - {0x3BAE, 0xF6}, - {0x3BAF, 0x02}, - {0x3BB0, 0xA2}, - {0x3BB1, 0x03}, - {0x3BB2, 0xE0}, - {0x3BB3, 0x03}, - {0x3BB4, 0xE0}, - {0x3BB5, 0x03}, - {0x3BB6, 0xE0}, - {0x3BB7, 0x03}, - {0x3BB8, 0xE0}, - {0x3BBA, 0xE0}, - {0x3BBC, 0xDA}, - {0x3BBE, 0x88}, - {0x3BC0, 0x44}, - {0x3BC2, 0x7B}, - {0x3BC4, 0xA2}, - {0x3BC8, 0xBD}, - {0x3BCA, 0xBD}, - {0x4001, 0x01}, - {0x4004, 0x48}, - {0x4005, 0x09}, - {0x400c, 0x00}, - {0x4018, 0xE7}, - {0x401A, 0x37}, - {0x401C, 0x37}, - {0x401E, 0xf7}, - {0x401F, 0x00}, - {0x4020, 0x3f}, - {0x4022, 0x6F}, - {0x4023, 0x00}, - {0x4024, 0x3f}, - {0x4026, 0x5f}, - {0x4028, 0x2F}, - {0x4074, 0x1}, - {0x3002, 0x00}, - //{0x3000, 0x00}, - {REG_DELAY, 0x1E},//wait_ms(30) - {REG_NULL, 0x00}, -}; - - -static __maybe_unused const struct regval imx415_linear_10bit_1920x1080_891M_regs_4lane[] = { - {0x3008, 0x7f}, - {0x300A, 0x5b}, - {0x3034, 0x05}, - // {0x301C, 0x04}, - {0x3020, 0x01}, - {0x3021, 0x01}, - {0x3022, 0x01}, - {0x3024, 0xca}, - {0x3025, 0x08}, - {0x3028, 0x26}, - {0x3029, 0x02}, - {0x3031, 0x00}, - {0x3032, 0x00}, - {0x3033, 0x05}, - {0x3050, 0xC4}, - {0x3090, 0x14}, - {0x30C1, 0x00}, - {0x30D9, 0x02}, - {0x30DA, 0x01}, - {0x3116, 0x24}, - {0x3118, 0xC0}, - {0x311A, 0xE0}, - {0x311E, 0x24}, - {0x32D4, 0x21}, - {0x32EC, 0xA1}, - {0x344C, 0x2B}, - {0x344D, 0x01}, - {0x344E, 0xED}, - {0x344F, 0x01}, - {0x3450, 0xF6}, - {0x3451, 0x02}, - {0x3452, 0x7F}, - {0x3453, 0x03}, - {0x358A, 0x04}, - {0x35A1, 0x02}, - {0x35EC, 0x27}, - {0x35EE, 0x8D}, - {0x35F0, 0x8D}, - {0x35F2, 0x29}, - {0x36BC, 0x0C}, - {0x36CC, 0x53}, - {0x36CD, 0x00}, - {0x36CE, 0x3C}, - {0x36D0, 0x8C}, - {0x36D1, 0x00}, - {0x36D2, 0x71}, - {0x36D4, 0x3C}, - {0x36D6, 0x53}, - {0x36D7, 0x00}, - {0x36D8, 0x71}, - {0x36DA, 0x8C}, - {0x36DB, 0x00}, - {0x3701, 0x00}, - {0x3720, 0x00}, - {0x3724, 0x02}, - {0x3726, 0x02}, - {0x3732, 0x02}, - {0x3734, 0x03}, - {0x3736, 0x03}, - {0x3742, 0x03}, - {0x3862, 0xE0}, - {0x38CC, 0x30}, - {0x38CD, 0x2F}, - {0x395C, 0x0C}, - {0x39A4, 0x07}, - {0x39A8, 0x32}, - {0x39AA, 0x32}, - {0x39AC, 0x32}, - {0x39AE, 0x32}, - {0x39B0, 0x32}, - {0x39B2, 0x2F}, - {0x39B4, 0x2D}, - {0x39B6, 0x28}, - {0x39B8, 0x30}, - {0x39BA, 0x30}, - {0x39BC, 0x30}, - {0x39BE, 0x30}, - {0x39C0, 0x30}, - {0x39C2, 0x2E}, - {0x39C4, 0x2B}, - {0x39C6, 0x25}, - {0x3A42, 0xD1}, - {0x3A4C, 0x77}, - {0x3AE0, 0x02}, - {0x3AEC, 0x0C}, - {0x3B00, 0x2E}, - {0x3B06, 0x29}, - {0x3B98, 0x25}, - {0x3B99, 0x21}, - {0x3B9B, 0x13}, - {0x3B9C, 0x13}, - {0x3B9D, 0x13}, - {0x3B9E, 0x13}, - {0x3BA1, 0x00}, - {0x3BA2, 0x06}, - {0x3BA3, 0x0B}, - {0x3BA4, 0x10}, - {0x3BA5, 0x14}, - {0x3BA6, 0x18}, - {0x3BA7, 0x1A}, - {0x3BA8, 0x1A}, - {0x3BA9, 0x1A}, - {0x3BAC, 0xED}, - {0x3BAD, 0x01}, - {0x3BAE, 0xF6}, - {0x3BAF, 0x02}, - {0x3BB0, 0xA2}, - {0x3BB1, 0x03}, - {0x3BB2, 0xE0}, - {0x3BB3, 0x03}, - {0x3BB4, 0xE0}, - {0x3BB5, 0x03}, - {0x3BB6, 0xE0}, - {0x3BB7, 0x03}, - {0x3BB8, 0xE0}, - {0x3BBA, 0xE0}, - {0x3BBC, 0xDA}, - {0x3BBE, 0x88}, - {0x3BC0, 0x44}, - {0x3BC2, 0x7B}, - {0x3BC4, 0xA2}, - {0x3BC8, 0xBD}, - {0x3BCA, 0xBD}, - {0x4001, 0x03}, - {0x4004, 0x48}, - {0x4005, 0x09}, - {0x400c, 0x00}, - {0x4018, 0xE7}, - {0x401A, 0x37}, - {0x401C, 0x37}, - {0x401E, 0xf7}, - {0x401F, 0x00}, - {0x4020, 0x3f}, - {0x4022, 0x6F}, - {0x4023, 0x00}, - {0x4024, 0x3f}, - {0x4026, 0x5f}, - {0x4028, 0x2F}, - {0x4074, 0x1}, - {0x3002, 0x00}, - //{0x3000, 0x00}, - {REG_DELAY, 0x1E},//wait_ms(30) - {REG_NULL, 0x00}, -}; - -/* - * Xclk 27Mhz - * 90.059fps - * CSI-2_2lane - * AD:10bit Output:12bit - * 2376Mbps - * Master Mode - * Time 9.999ms Gain:6dB - * 2568x1440 2/2-line binning & Window cropping - */ -static __maybe_unused const struct regval imx415_linear_12bit_1284x720_2376M_regs_2lane[] = { - {0x3008, 0x5D}, - {0x300A, 0x42}, - {0x301C, 0x04}, - {0x3020, 0x01}, - {0x3021, 0x01}, - {0x3022, 0x01}, - {0x3024, 0xAB}, - {0x3025, 0x07}, - {0x3028, 0xA4}, - {0x3029, 0x01}, - {0x3031, 0x00}, - {0x3033, 0x00}, - {0x3040, 0x88}, - {0x3041, 0x02}, - {0x3042, 0x08}, - {0x3043, 0x0A}, - {0x3044, 0xF0}, - {0x3045, 0x02}, - {0x3046, 0x40}, - {0x3047, 0x0B}, - {0x3050, 0xC4}, - {0x3090, 0x14}, - {0x30C1, 0x00}, - {0x30D9, 0x02}, - {0x30DA, 0x01}, - {0x3116, 0x23}, - {0x3118, 0x08}, - {0x3119, 0x01}, - {0x311A, 0xE7}, - {0x311E, 0x23}, - {0x32D4, 0x21}, - {0x32EC, 0xA1}, - {0x344C, 0x2B}, - {0x344D, 0x01}, - {0x344E, 0xED}, - {0x344F, 0x01}, - {0x3450, 0xF6}, - {0x3451, 0x02}, - {0x3452, 0x7F}, - {0x3453, 0x03}, - {0x358A, 0x04}, - {0x35A1, 0x02}, - {0x35EC, 0x27}, - {0x35EE, 0x8D}, - {0x35F0, 0x8D}, - {0x35F2, 0x29}, - {0x36BC, 0x0C}, - {0x36CC, 0x53}, - {0x36CD, 0x00}, - {0x36CE, 0x3C}, - {0x36D0, 0x8C}, - {0x36D1, 0x00}, - {0x36D2, 0x71}, - {0x36D4, 0x3C}, - {0x36D6, 0x53}, - {0x36D7, 0x00}, - {0x36D8, 0x71}, - {0x36DA, 0x8C}, - {0x36DB, 0x00}, - {0x3701, 0x00}, - {0x3720, 0x00}, - {0x3724, 0x02}, - {0x3726, 0x02}, - {0x3732, 0x02}, - {0x3734, 0x03}, - {0x3736, 0x03}, - {0x3742, 0x03}, - {0x3862, 0xE0}, - {0x38CC, 0x30}, - {0x38CD, 0x2F}, - {0x395C, 0x0C}, - {0x39A4, 0x07}, - {0x39A8, 0x32}, - {0x39AA, 0x32}, - {0x39AC, 0x32}, - {0x39AE, 0x32}, - {0x39B0, 0x32}, - {0x39B2, 0x2F}, - {0x39B4, 0x2D}, - {0x39B6, 0x28}, - {0x39B8, 0x30}, - {0x39BA, 0x30}, - {0x39BC, 0x30}, - {0x39BE, 0x30}, - {0x39C0, 0x30}, - {0x39C2, 0x2E}, - {0x39C4, 0x2B}, - {0x39C6, 0x25}, - {0x3A42, 0xD1}, - {0x3A4C, 0x77}, - {0x3AE0, 0x02}, - {0x3AEC, 0x0C}, - {0x3B00, 0x2E}, - {0x3B06, 0x29}, - {0x3B98, 0x25}, - {0x3B99, 0x21}, - {0x3B9B, 0x13}, - {0x3B9C, 0x13}, - {0x3B9D, 0x13}, - {0x3B9E, 0x13}, - {0x3BA1, 0x00}, - {0x3BA2, 0x06}, - {0x3BA3, 0x0B}, - {0x3BA4, 0x10}, - {0x3BA5, 0x14}, - {0x3BA6, 0x18}, - {0x3BA7, 0x1A}, - {0x3BA8, 0x1A}, - {0x3BA9, 0x1A}, - {0x3BAC, 0xED}, - {0x3BAD, 0x01}, - {0x3BAE, 0xF6}, - {0x3BAF, 0x02}, - {0x3BB0, 0xA2}, - {0x3BB1, 0x03}, - {0x3BB2, 0xE0}, - {0x3BB3, 0x03}, - {0x3BB4, 0xE0}, - {0x3BB5, 0x03}, - {0x3BB6, 0xE0}, - {0x3BB7, 0x03}, - {0x3BB8, 0xE0}, - {0x3BBA, 0xE0}, - {0x3BBC, 0xDA}, - {0x3BBE, 0x88}, - {0x3BC0, 0x44}, - {0x3BC2, 0x7B}, - {0x3BC4, 0xA2}, - {0x3BC8, 0xBD}, - {0x3BCA, 0xBD}, - {0x4001, 0x01}, - {0x4004, 0xC0}, - {0x4005, 0x06}, - {0x4018, 0xE7}, - {0x401A, 0x8F}, - {0x401C, 0x8F}, - {0x401E, 0x7F}, - {0x401F, 0x02}, - {0x4020, 0x97}, - {0x4022, 0x0F}, - {0x4023, 0x01}, - {0x4024, 0x97}, - {0x4026, 0xF7}, - {0x4028, 0x7F}, - {0x3002, 0x00}, - //{0x3000, 0x00}, - {REG_DELAY, 0x1E},//wait_ms(30) - {REG_NULL, 0x00}, -}; - -static __maybe_unused const struct regval imx415_linear_10bit_1284x720_891M_regs_2lane[] = { - {0x3008, 0x5D}, - {0x300A, 0x42}, - {0x301C, 0x04}, - {0x3020, 0x01}, - {0x3021, 0x01}, - {0x3022, 0x01}, - {0x3024, 0xAB}, - {0x3025, 0x07}, - {0x3028, 0xA4}, - {0x3029, 0x01}, - {0x3031, 0x00}, - {0x3033, 0x00}, - {0x3040, 0x88}, - {0x3041, 0x02}, - {0x3042, 0x08}, - {0x3043, 0x0A}, - {0x3044, 0xF0}, - {0x3045, 0x02}, - {0x3046, 0x40}, - {0x3047, 0x0B}, - {0x3050, 0xC4}, - {0x3090, 0x14}, - {0x30C1, 0x00}, - {0x30D9, 0x02}, - {0x30DA, 0x01}, - {0x3116, 0x23}, - {0x3118, 0x08}, - {0x3119, 0x01}, - {0x311A, 0xE7}, - {0x311E, 0x23}, - {0x32D4, 0x21}, - {0x32EC, 0xA1}, - {0x344C, 0x2B}, - {0x344D, 0x01}, - {0x344E, 0xED}, - {0x344F, 0x01}, - {0x3450, 0xF6}, - {0x3451, 0x02}, - {0x3452, 0x7F}, - {0x3453, 0x03}, - {0x358A, 0x04}, - {0x35A1, 0x02}, - {0x35EC, 0x27}, - {0x35EE, 0x8D}, - {0x35F0, 0x8D}, - {0x35F2, 0x29}, - {0x36BC, 0x0C}, - {0x36CC, 0x53}, - {0x36CD, 0x00}, - {0x36CE, 0x3C}, - {0x36D0, 0x8C}, - {0x36D1, 0x00}, - {0x36D2, 0x71}, - {0x36D4, 0x3C}, - {0x36D6, 0x53}, - {0x36D7, 0x00}, - {0x36D8, 0x71}, - {0x36DA, 0x8C}, - {0x36DB, 0x00}, - {0x3701, 0x00}, - {0x3720, 0x00}, - {0x3724, 0x02}, - {0x3726, 0x02}, - {0x3732, 0x02}, - {0x3734, 0x03}, - {0x3736, 0x03}, - {0x3742, 0x03}, - {0x3862, 0xE0}, - {0x38CC, 0x30}, - {0x38CD, 0x2F}, - {0x395C, 0x0C}, - {0x39A4, 0x07}, - {0x39A8, 0x32}, - {0x39AA, 0x32}, - {0x39AC, 0x32}, - {0x39AE, 0x32}, - {0x39B0, 0x32}, - {0x39B2, 0x2F}, - {0x39B4, 0x2D}, - {0x39B6, 0x28}, - {0x39B8, 0x30}, - {0x39BA, 0x30}, - {0x39BC, 0x30}, - {0x39BE, 0x30}, - {0x39C0, 0x30}, - {0x39C2, 0x2E}, - {0x39C4, 0x2B}, - {0x39C6, 0x25}, - {0x3A42, 0xD1}, - {0x3A4C, 0x77}, - {0x3AE0, 0x02}, - {0x3AEC, 0x0C}, - {0x3B00, 0x2E}, - {0x3B06, 0x29}, - {0x3B98, 0x25}, - {0x3B99, 0x21}, - {0x3B9B, 0x13}, - {0x3B9C, 0x13}, - {0x3B9D, 0x13}, - {0x3B9E, 0x13}, - {0x3BA1, 0x00}, - {0x3BA2, 0x06}, - {0x3BA3, 0x0B}, - {0x3BA4, 0x10}, - {0x3BA5, 0x14}, - {0x3BA6, 0x18}, - {0x3BA7, 0x1A}, - {0x3BA8, 0x1A}, - {0x3BA9, 0x1A}, - {0x3BAC, 0xED}, - {0x3BAD, 0x01}, - {0x3BAE, 0xF6}, - {0x3BAF, 0x02}, - {0x3BB0, 0xA2}, - {0x3BB1, 0x03}, - {0x3BB2, 0xE0}, - {0x3BB3, 0x03}, - {0x3BB4, 0xE0}, - {0x3BB5, 0x03}, - {0x3BB6, 0xE0}, - {0x3BB7, 0x03}, - {0x3BB8, 0xE0}, - {0x3BBA, 0xE0}, - {0x3BBC, 0xDA}, - {0x3BBE, 0x88}, - {0x3BC0, 0x44}, - {0x3BC2, 0x7B}, - {0x3BC4, 0xA2}, - {0x3BC8, 0xBD}, - {0x3BCA, 0xBD}, - {0x4001, 0x01}, - {0x4004, 0xC0}, - {0x4005, 0x06}, - {0x4018, 0xE7}, - {0x401A, 0x8F}, - {0x401C, 0x8F}, - {0x401E, 0x7F}, - {0x401F, 0x02}, - {0x4020, 0x97}, - {0x4022, 0x0F}, - {0x4023, 0x01}, - {0x4024, 0x97}, - {0x4026, 0xF7}, - {0x4028, 0x7F}, - {0x3002, 0x00}, - //{0x3000, 0x00}, - {REG_DELAY, 0x1E},//wait_ms(30) - {REG_NULL, 0x00}, -}; - - /* * The width and height must be configured to be * the same as the current output resolution of the sensor. @@ -1676,29 +779,7 @@ static const struct imx415_mode supported_modes[] = { .mipi_freq_idx = 1, .bpp = 10, .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, - .xvclk = IMX415_XVCLK_FREQ_37M, - }, - { - /* 1H period = (1100 clock) = (1100 * 1 / 74.25MHz) */ - .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, - .width = 1944, - .height = 1096, - .max_fps = { - .numerator = 10000, - .denominator = 600000, - }, - .exp_def = 0x08ca - 0x08, - .hts_def = 0x044c *2, - .vts_def = 0x08ca, - .global_reg_list = NULL, - .reg_list = imx415_linear_10bit_1920x1080_891M_regs_4lane, - .hdr_mode = NO_HDR, - .mipi_freq_idx = 1, - .bpp = 10, - .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, - .xvclk = IMX415_XVCLK_FREQ_37M, }, -#if 0 { .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, .width = 3864, @@ -1723,7 +804,6 @@ static const struct imx415_mode supported_modes[] = { .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_1, .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr2 - .xvclk = IMX415_XVCLK_FREQ_37M, }, { .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, @@ -1749,7 +829,6 @@ static const struct imx415_mode supported_modes[] = { .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr0 .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_2,//S->csi wr2 - .xvclk = IMX415_XVCLK_FREQ_37M, }, { .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, @@ -1775,7 +854,6 @@ static const struct imx415_mode supported_modes[] = { .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr0 .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_2,//S->csi wr2 - .xvclk = IMX415_XVCLK_FREQ_37M, }, { /* 1H period = (1100 clock) = (1100 * 1 / 74.25MHz) */ @@ -1795,7 +873,6 @@ static const struct imx415_mode supported_modes[] = { .mipi_freq_idx = 1, .bpp = 12, .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, - .xvclk = IMX415_XVCLK_FREQ_37M, }, { .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, @@ -1821,7 +898,6 @@ static const struct imx415_mode supported_modes[] = { .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_1, .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr2 - .xvclk = IMX415_XVCLK_FREQ_37M, }, { .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, @@ -1847,7 +923,6 @@ static const struct imx415_mode supported_modes[] = { .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr0 .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_2,//S->csi wr2 - .xvclk = IMX415_XVCLK_FREQ_37M, }, { .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, @@ -1866,7 +941,6 @@ static const struct imx415_mode supported_modes[] = { .mipi_freq_idx = 0, .bpp = 12, .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, - .xvclk = IMX415_XVCLK_FREQ_37M, }, { .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, @@ -1892,51 +966,6 @@ static const struct imx415_mode supported_modes[] = { .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_1, .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr2 - .xvclk = IMX415_XVCLK_FREQ_37M, - }, -#endif -}; - -static const struct imx415_mode supported_modes_2lane[] = { - { - /* 1H period = (1100 clock) = (1100 * 1 / 74.25MHz) */ - .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, - .width = 3864, - .height = 2192, - .max_fps = { - .numerator = 10000, - .denominator = 150000, - }, - .exp_def = 0x08ca - 0x08, - .hts_def = 0x0898 * IMX415_2LANES * 2, - .vts_def = 0x08ca, - .global_reg_list = NULL, - .reg_list = imx415_linear_10bit_3864x2192_891M_regs_2lane, - .hdr_mode = NO_HDR, - .mipi_freq_idx = 1, - .bpp = 10, - .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, - .xvclk = IMX415_XVCLK_FREQ_37M, - }, - { - /* 1H period = (1100 clock) = (1100 * 1 / 74.25MHz) */ - .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, - .width = 1944, - .height = 1096, - .max_fps = { - .numerator = 10000, - .denominator = 600000, - }, - .exp_def = 0x08ca - 0x08, - .hts_def = 0x0800, - .vts_def = 0x08ca, - .global_reg_list = NULL, - .reg_list = imx415_linear_10bit_1920x1080_891M_regs_2lane, - .hdr_mode = NO_HDR, - .mipi_freq_idx = 1, - .bpp = 10, - .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, - .xvclk = IMX415_XVCLK_FREQ_37M, }, }; @@ -1945,7 +974,6 @@ static const s64 link_freq_items[] = { MIPI_FREQ_446M, MIPI_FREQ_743M, MIPI_FREQ_891M, - MIPI_FREQ_1188M, }; /* Write registers up to 4 at a time */ @@ -1982,18 +1010,10 @@ static int imx415_write_array(struct i2c_client *client, { u32 i; int ret = 0; - if (!regs) { - dev_err(&client->dev, "write reg array error\n"); - return ret; - } + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { - if (regs[i].addr == REG_DELAY) { - usleep_range(regs[i].val * 1000, regs[i].val * 1000 + 500); - dev_info(&client->dev, "write reg array, sleep %dms\n", regs[i].val); - } else { - ret = imx415_write_reg(client, regs[i].addr, - IMX415_REG_VALUE_08BIT, regs[i].val); - } + ret = imx415_write_reg(client, regs[i].addr, + IMX415_REG_VALUE_08BIT, regs[i].val); } return ret; } @@ -2050,9 +1070,9 @@ imx415_find_best_fit(struct imx415 *imx415, struct v4l2_subdev_format *fmt) unsigned int i; for (i = 0; i < imx415->cfg_num; i++) { - dist = imx415_get_reso_dist(&imx415->supported_modes[i], framefmt); + dist = imx415_get_reso_dist(&supported_modes[i], framefmt); if ((cur_best_fit_dist == -1 || dist < cur_best_fit_dist) && - imx415->supported_modes[i].bus_fmt == framefmt->code) { + supported_modes[i].bus_fmt == framefmt->code) { cur_best_fit_dist = dist; cur_best_fit = i; } @@ -2060,7 +1080,7 @@ imx415_find_best_fit(struct imx415 *imx415, struct v4l2_subdev_format *fmt) dev_info(&imx415->client->dev, "%s: cur_best_fit(%d)", __func__, cur_best_fit); - return &imx415->supported_modes[cur_best_fit]; + return &supported_modes[cur_best_fit]; } static int __imx415_power_on(struct imx415 *imx415); @@ -2074,8 +1094,8 @@ static void imx415_change_mode(struct imx415 *imx415, const struct imx415_mode * } imx415->cur_mode = mode; imx415->cur_vts = imx415->cur_mode->vts_def; - dev_info(&imx415->client->dev, "set fmt: cur_mode: %dx%d, hdr: %d, bpp: %d\n", - mode->width, mode->height, mode->hdr_mode, mode->bpp); + dev_dbg(&imx415->client->dev, "set fmt: cur_mode: %dx%d, hdr: %d\n", + mode->width, mode->height, mode->hdr_mode); } static int imx415_set_fmt(struct v4l2_subdev *sd, @@ -2086,7 +1106,6 @@ static int imx415_set_fmt(struct v4l2_subdev *sd, const struct imx415_mode *mode; s64 h_blank, vblank_def, vblank_min; u64 pixel_rate = 0; - u8 lanes = imx415->lanes; mutex_lock(&imx415->mutex); @@ -2115,8 +1134,7 @@ static int imx415_set_fmt(struct v4l2_subdev *sd, 1, vblank_def); __v4l2_ctrl_s_ctrl(imx415->vblank, vblank_def); __v4l2_ctrl_s_ctrl(imx415->link_freq, mode->mipi_freq_idx); - pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / - mode->bpp * 2 * lanes; + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * IMX415_4LANES; __v4l2_ctrl_s_ctrl_int64(imx415->pixel_rate, pixel_rate); } @@ -2167,7 +1185,7 @@ static int imx415_enum_mbus_code(struct v4l2_subdev *sd, if (code->index >= imx415->cfg_num) return -EINVAL; - code->code = imx415->supported_modes[code->index].bus_fmt; + code->code = supported_modes[code->index].bus_fmt; return 0; } @@ -2181,13 +1199,13 @@ static int imx415_enum_frame_sizes(struct v4l2_subdev *sd, if (fse->index >= imx415->cfg_num) return -EINVAL; - if (fse->code != imx415->supported_modes[fse->index].bus_fmt) + if (fse->code != supported_modes[fse->index].bus_fmt) return -EINVAL; - fse->min_width = imx415->supported_modes[fse->index].width; - fse->max_width = imx415->supported_modes[fse->index].width; - fse->max_height = imx415->supported_modes[fse->index].height; - fse->min_height = imx415->supported_modes[fse->index].height; + 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; } @@ -2209,9 +1227,8 @@ static int imx415_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, struct imx415 *imx415 = to_imx415(sd); const struct imx415_mode *mode = imx415->cur_mode; u32 val = 0; - u8 lanes = imx415->lanes; - val = 1 << (lanes - 1) | + val = 1 << (IMX415_4LANES - 1) | V4L2_MBUS_CSI2_CHANNEL_0 | V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; if (mode->hdr_mode != NO_HDR) @@ -2665,12 +1682,11 @@ static long imx415_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) struct imx415 *imx415 = to_imx415(sd); struct rkmodule_hdr_cfg *hdr; struct rkmodule_channel_info *ch_info; - u32 h, w, stream; + u32 i, h, w, stream; long ret = 0; const struct imx415_mode *mode; u64 pixel_rate = 0; struct rkmodule_csi_dphy_param *dphy_param; - u8 lanes = imx415->lanes; switch (cmd) { case PREISP_CMD_SET_HDRAE_EXP: @@ -2688,41 +1704,48 @@ static long imx415_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) hdr->hdr_mode = imx415->cur_mode->hdr_mode; break; case RKMODULE_SET_HDR_CFG: + hdr = (struct rkmodule_hdr_cfg *)arg; + w = imx415->cur_mode->width; + h = imx415->cur_mode->height; + for (i = 0; i < imx415->cfg_num; i++) { + if (w == supported_modes[i].width && + h == supported_modes[i].height && + supported_modes[i].hdr_mode == hdr->hdr_mode) { + imx415_change_mode(imx415, &supported_modes[i]); + break; + } + } + if (i == imx415->cfg_num) { + dev_err(&imx415->client->dev, + "not find hdr mode:%d %dx%d config\n", + hdr->hdr_mode, w, h); + ret = -EINVAL; + } else { + mode = imx415->cur_mode; + if (imx415->streaming) { + ret = imx415_write_reg(imx415->client, IMX415_GROUP_HOLD_REG, + IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_START); - imx415_change_mode(imx415, &imx415->supported_modes[imx415->capture_mode]); - - if(imx415->capture_mode == 0) - dev_info(&imx415->client->dev, "capture_mode is 3840x2160\n"); - else if(imx415->capture_mode == 1) - dev_info(&imx415->client->dev, "capture_mode is 1920x1080\n"); - else - dev_info(&imx415->client->dev, "not support capture_mode0\n"); - - mode = imx415->cur_mode; - if (imx415->streaming) { - ret = imx415_write_reg(imx415->client, IMX415_GROUP_HOLD_REG, - IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_START); - - ret |= imx415_write_array(imx415->client, imx415->cur_mode->reg_list); + ret |= imx415_write_array(imx415->client, imx415->cur_mode->reg_list); - ret |= imx415_write_reg(imx415->client, IMX415_GROUP_HOLD_REG, - IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_END); - if (ret) - return ret; + ret |= imx415_write_reg(imx415->client, IMX415_GROUP_HOLD_REG, + IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_END); + if (ret) + return ret; + } + w = mode->hts_def - imx415->cur_mode->width; + h = mode->vts_def - mode->height; + mutex_lock(&imx415->mutex); + __v4l2_ctrl_modify_range(imx415->hblank, w, w, 1, w); + __v4l2_ctrl_modify_range(imx415->vblank, h, + IMX415_VTS_MAX - mode->height, + 1, h); + __v4l2_ctrl_s_ctrl(imx415->link_freq, mode->mipi_freq_idx); + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * IMX415_4LANES; + __v4l2_ctrl_s_ctrl_int64(imx415->pixel_rate, + pixel_rate); + mutex_unlock(&imx415->mutex); } - w = mode->hts_def - imx415->cur_mode->width; - h = mode->vts_def - mode->height; - mutex_lock(&imx415->mutex); - __v4l2_ctrl_modify_range(imx415->hblank, w, w, 1, w); - __v4l2_ctrl_modify_range(imx415->vblank, h, - IMX415_VTS_MAX - mode->height, - 1, h); - __v4l2_ctrl_s_ctrl(imx415->link_freq, mode->mipi_freq_idx); - pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / - mode->bpp * 2 * lanes; - __v4l2_ctrl_s_ctrl_int64(imx415->pixel_rate, - pixel_rate); - mutex_unlock(&imx415->mutex); break; case RKMODULE_SET_QUICK_STREAM: @@ -3022,6 +2045,10 @@ int __imx415_power_on(struct imx415 *imx415) { int ret; struct device *dev = &imx415->client->dev; + + if (imx415->is_thunderboot) + return 0; + if (!IS_ERR_OR_NULL(imx415->pins_default)) { ret = pinctrl_select_state(imx415->pinctrl, imx415->pins_default); @@ -3029,23 +2056,26 @@ int __imx415_power_on(struct imx415 *imx415) dev_err(dev, "could not set pins\n"); } - if (!imx415->is_thunderboot) { - if (!IS_ERR(imx415->power_gpio)) - gpiod_direction_output(imx415->power_gpio, 1); - /* At least 500ns between power raising and XCLR */ - /* fix power on timing if insmod this ko */ - usleep_range(10 * 1000, 20 * 1000); - if (!IS_ERR(imx415->reset_gpio)) - gpiod_direction_output(imx415->reset_gpio, 0); - - /* At least 1us between XCLR and clk */ - /* fix power on timing if insmod this ko */ - usleep_range(10 * 1000, 20 * 1000); + ret = regulator_bulk_enable(IMX415_NUM_SUPPLIES, imx415->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto err_pinctrl; } - ret = clk_set_rate(imx415->xvclk, imx415->cur_mode->xvclk); + if (!IS_ERR(imx415->power_gpio)) + gpiod_direction_output(imx415->power_gpio, 1); + /* At least 500ns between power raising and XCLR */ + /* fix power on timing if insmod this ko */ + usleep_range(10 * 1000, 20 * 1000); + if (!IS_ERR(imx415->reset_gpio)) + gpiod_direction_output(imx415->reset_gpio, 0); + + /* At least 1us between XCLR and clk */ + /* fix power on timing if insmod this ko */ + usleep_range(10 * 1000, 20 * 1000); + ret = clk_set_rate(imx415->xvclk, IMX415_XVCLK_FREQ_37M); if (ret < 0) dev_warn(dev, "Failed to set xvclk rate\n"); - if (clk_get_rate(imx415->xvclk) != imx415->cur_mode->xvclk) + if (clk_get_rate(imx415->xvclk) != IMX415_XVCLK_FREQ_37M) dev_warn(dev, "xvclk mismatched\n"); ret = clk_prepare_enable(imx415->xvclk); if (ret < 0) { @@ -3061,6 +2091,9 @@ int __imx415_power_on(struct imx415 *imx415) err_clk: if (!IS_ERR(imx415->reset_gpio)) gpiod_direction_output(imx415->reset_gpio, 1); + regulator_bulk_disable(IMX415_NUM_SUPPLIES, imx415->supplies); + +err_pinctrl: if (!IS_ERR_OR_NULL(imx415->pins_sleep)) pinctrl_select_state(imx415->pinctrl, imx415->pins_sleep); @@ -3121,7 +2154,7 @@ static int imx415_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) struct imx415 *imx415 = to_imx415(sd); struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, fh->pad, 0); - const struct imx415_mode *def_mode = &imx415->supported_modes[0]; + const struct imx415_mode *def_mode = &supported_modes[0]; mutex_lock(&imx415->mutex); /* Initialize try_fmt */ @@ -3146,11 +2179,11 @@ static int imx415_enum_frame_interval(struct v4l2_subdev *sd, if (fie->index >= imx415->cfg_num) return -EINVAL; - fie->code = imx415->supported_modes[fie->index].bus_fmt; - fie->width = imx415->supported_modes[fie->index].width; - fie->height = imx415->supported_modes[fie->index].height; - fie->interval = imx415->supported_modes[fie->index].max_fps; - fie->reserved[0] = imx415->supported_modes[fie->index].hdr_mode; + fie->code = supported_modes[fie->index].bus_fmt; + fie->width = supported_modes[fie->index].width; + fie->height = supported_modes[fie->index].height; + fie->interval = supported_modes[fie->index].max_fps; + fie->reserved[0] = supported_modes[fie->index].hdr_mode; return 0; } @@ -3159,8 +2192,6 @@ static int imx415_enum_frame_interval(struct v4l2_subdev *sd, #define DST_HEIGHT_2160 2160 #define DST_WIDTH_1920 1920 #define DST_HEIGHT_1080 1080 -#define DST_WIDTH_1280 1280 -#define DST_HEIGHT_720 720 /* * The resolution of the driver configuration needs to be exactly @@ -3188,11 +2219,6 @@ static int imx415_get_selection(struct v4l2_subdev *sd, sel->r.width = DST_WIDTH_1920; sel->r.top = CROP_START(imx415->cur_mode->height, DST_HEIGHT_1080); sel->r.height = DST_HEIGHT_1080; - } else if (imx415->cur_mode->width == 1284) { - sel->r.left = CROP_START(imx415->cur_mode->width, DST_WIDTH_1280); - sel->r.width = DST_WIDTH_1280; - sel->r.top = CROP_START(imx415->cur_mode->height, DST_HEIGHT_720); - sel->r.height = DST_HEIGHT_720; } else { sel->r.left = CROP_START(imx415->cur_mode->width, imx415->cur_mode->width); sel->r.width = imx415->cur_mode->width; @@ -3375,10 +2401,8 @@ static int imx415_initialize_controls(struct imx415 *imx415) struct v4l2_ctrl_handler *handler; s64 exposure_max, vblank_def; u64 pixel_rate; - u64 max_pixel_rate; u32 h_blank; int ret; - u8 lanes = imx415->lanes; handler = &imx415->ctrl_handler; mode = imx415->cur_mode; @@ -3394,10 +2418,9 @@ static int imx415_initialize_controls(struct imx415 *imx415) v4l2_ctrl_s_ctrl(imx415->link_freq, mode->mipi_freq_idx); /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ - pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * lanes; - max_pixel_rate = MIPI_FREQ_1188M / mode->bpp * 2 * lanes; + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * IMX415_4LANES; imx415->pixel_rate = v4l2_ctrl_new_std(handler, NULL, - V4L2_CID_PIXEL_RATE, 0, max_pixel_rate, + V4L2_CID_PIXEL_RATE, 0, IMX415_MAX_PIXEL_RATE, 1, pixel_rate); h_blank = mode->hts_def - mode->width; @@ -3490,7 +2513,7 @@ static int imx415_probe(struct i2c_client *client, struct v4l2_subdev *sd; char facing[2]; int ret; - u32 hdr_mode; + u32 i, hdr_mode = 0; dev_info(dev, "driver version: %02x.%02x.%02x", DRIVER_VERSION >> 16, @@ -3520,32 +2543,16 @@ static int imx415_probe(struct i2c_client *client, dev_warn(dev, " Get hdr mode failed! no hdr default\n"); } imx415->client = client; - - ret = of_property_read_u32(node, DATA_LANES, &imx415->lanes); - if (ret) { - imx415->lanes = 4; - dev_warn(dev, " Get data lanes failed! 4 lanes default\n"); - } - if (imx415->lanes == IMX415_4LANES) { - imx415->supported_modes = supported_modes; - imx415->cfg_num = ARRAY_SIZE(supported_modes); - } else { - imx415->supported_modes = supported_modes_2lane; - imx415->cfg_num = ARRAY_SIZE(supported_modes_2lane); + imx415->cfg_num = ARRAY_SIZE(supported_modes); + for (i = 0; i < imx415->cfg_num; i++) { + if (hdr_mode == supported_modes[i].hdr_mode) { + imx415->cur_mode = &supported_modes[i]; + break; + } } - dev_info(dev, "detect imx415 lane %d\n", - imx415->lanes); imx415->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP); - ret = of_property_read_u32(node, OF_CAMERA_CAPTURE_MODE, &imx415->capture_mode); - if (ret) { - imx415->capture_mode = 0; - dev_warn(dev, " Get capture_mode failed! captrue revolution setting to 3840x2160\n"); - } - - imx415->cur_mode = &imx415->supported_modes[imx415->capture_mode]; - imx415->xvclk = devm_clk_get(dev, "xvclk"); if (IS_ERR(imx415->xvclk)) { dev_err(dev, "Failed to get xvclk\n"); @@ -3703,4 +2710,4 @@ device_initcall_sync(sensor_mod_init); module_exit(sensor_mod_exit); MODULE_DESCRIPTION("Sony imx415 sensor driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL v2"); \ No newline at end of file diff --git a/drivers/media/platform/rockchip/cif/capture.c b/drivers/media/platform/rockchip/cif/capture.c index 94a2347feb782..8c44b416af946 100644 --- a/drivers/media/platform/rockchip/cif/capture.c +++ b/drivers/media/platform/rockchip/cif/capture.c @@ -5104,8 +5104,11 @@ int rkcif_update_sensor_info(struct rkcif_stream *stream) } stream->cifdev->active_sensor = sensor; + //printf("shingo: sensor = %s. \n", sensor->sd->name); terminal_sensor = &stream->cifdev->terminal_sensor; + //printf("shingo: terminal sensor = %s. \n", terminal_sensor->sd->name); + get_remote_terminal_sensor(stream, &terminal_sensor->sd); if (terminal_sensor->sd) { ret = v4l2_subdev_call(terminal_sensor->sd, pad, get_mbus_config, From c0e652225625c0a5e990b3326ba719a6a8f8d499 Mon Sep 17 00:00:00 2001 From: ShingoXY <36981700+shingoxy@users.noreply.github.com> Date: Wed, 10 Jul 2024 13:11:39 +0800 Subject: [PATCH 08/13] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f31ac2d7ac557..bff4f375b8626 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +# 20240710:RKAIQ正常工作,3AOK,但iqfile为copy os04a10的,颜色、LSC、AWB均不正常; +![image](https://github.com/shingoxy/kernel-RK3588s-5.10Stable/assets/36981700/d51b1c1e-4d61-4433-b83b-e777f5db479d) + + # 20240628:GC5603成功点亮,工作正常,i2c通信正常: image From a586f9aee8f6ccc42c7b58febca4d896b03fdf3d Mon Sep 17 00:00:00 2001 From: shingoxy <2877898@163.com> Date: Mon, 12 Aug 2024 10:55:54 +0800 Subject: [PATCH 09/13] =?UTF-8?q?=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20?= =?UTF-8?q?=20=20arch/arm64/boot/dts/rockchip/rk3588s-lubancat-4.dts=20=09?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20arch/arm64/boot/dts/r?= =?UTF-8?q?ockchip/rk3588s-lubancat-40pin.dtsi=20=09=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=EF=BC=9A=20=20=20=20=20arch/arm64/boot/dts/rockchip/rk3588s-lu?= =?UTF-8?q?bancat-csi2.dtsi=20=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20?= =?UTF-8?q?=20arch/arm64/configs/lubancat=5Flinux=5Frk3588=5Fdefconfig=20?= =?UTF-8?q?=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20arch/arm64/configs?= =?UTF-8?q?/rockchip=5Fdefconfig=20=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20?= =?UTF-8?q?=20=20arch/arm64/configs/rockchip=5Flinux=5Fdefconfig=20=09?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20drivers/media/i2c/Kco?= =?UTF-8?q?nfig=20=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20drivers/med?= =?UTF-8?q?ia/i2c/Makefile=20=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20?= =?UTF-8?q?drivers/media/i2c/gc5603.c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boot/dts/rockchip/rk3588s-lubancat-4.dts | 2 +- .../dts/rockchip/rk3588s-lubancat-40pin.dtsi | 4 +- .../dts/rockchip/rk3588s-lubancat-csi2.dtsi | 26 ++++++++ .../configs/lubancat_linux_rk3588_defconfig | 1 + arch/arm64/configs/rockchip_defconfig | 1 + arch/arm64/configs/rockchip_linux_defconfig | 1 + drivers/media/i2c/Kconfig | 11 ++++ drivers/media/i2c/Makefile | 1 + drivers/media/i2c/gc5603.c | 60 +++++++++---------- 9 files changed, 73 insertions(+), 34 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-4.dts b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-4.dts index 098b476c50696..f994dd0767f99 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-4.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-4.dts @@ -18,7 +18,7 @@ #include "rk3588s.dtsi" #include "rk3588-linux.dtsi" -// #include "rk3588s-lubancat-40pin.dtsi" +#include "rk3588s-lubancat-40pin.dtsi" #include "rk3588s-lubancat-csi2.dtsi" // #include "rk3588s-lubancat-csi2_cam0.dtsi" // #include "rk3588s-lubancat-csi2_cam1.dtsi" diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-40pin.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-40pin.dtsi index 841dce7dfb2aa..f71f8cd3633d9 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-40pin.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-40pin.dtsi @@ -9,7 +9,7 @@ pinctrl-names = "default"; pinctrl-0 = <&uart0m2_xfer>; }; - +/* &i2c5 { status = "okay"; pinctrl-names = "default"; @@ -21,7 +21,7 @@ pinctrl-names = "default"; pinctrl-0 = <&i2c6m3_xfer>; }; - +*/ &spi0 { status = "okay"; diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi index 27d5c47c98068..ce719ce380d95 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi @@ -333,6 +333,26 @@ }; }; }; + + gc8613: gc8613@31 { + compatible = "galaxycore,gc8613"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_24m_clk>; + clock-names = "xvclk"; + pwdn-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_LOW>; + + rockchip,camera-module-index = <3>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "GCDS01A"; + rockchip,camera-module-lens-name = "default"; + port { + gc8613_out2: endpoint { + remote-endpoint = <&dphy0_in_gc8613>; + data-lanes = <1 2 3 4>; + }; + }; + }; }; &csi2_dphy0_hw { @@ -362,6 +382,12 @@ remote-endpoint = <&imx415_out2>; data-lanes = <1 2 3 4>; }; + + dphy0_in_gc8613: endpoint@2 { + reg = <2>; + remote-endpoint = <&gc8613_out2>; + data-lanes = <1 2 3 4>; + }; }; port@1 { diff --git a/arch/arm64/configs/lubancat_linux_rk3588_defconfig b/arch/arm64/configs/lubancat_linux_rk3588_defconfig index beeb64072a59b..90c2db44237f8 100644 --- a/arch/arm64/configs/lubancat_linux_rk3588_defconfig +++ b/arch/arm64/configs/lubancat_linux_rk3588_defconfig @@ -493,6 +493,7 @@ CONFIG_VIDEO_OV7251=y CONFIG_VIDEO_OV8858=y CONFIG_VIDEO_OV13850=y CONFIG_VIDEO_GC5603=y +CONFIG_VIDEO_GC8613=y # CONFIG_VGA_ARB is not set CONFIG_DRM=y CONFIG_DRM_IGNORE_IOTCL_PERMIT=y diff --git a/arch/arm64/configs/rockchip_defconfig b/arch/arm64/configs/rockchip_defconfig index 004d113da6e47..b41ba0407d476 100644 --- a/arch/arm64/configs/rockchip_defconfig +++ b/arch/arm64/configs/rockchip_defconfig @@ -593,6 +593,7 @@ CONFIG_VIDEO_THCV244=y CONFIG_VIDEO_RK_IRCUT=y CONFIG_VIDEO_GC2053=y CONFIG_VIDEO_GC5603=y +CONFIG_VIDEO_GC8613=y CONFIG_VIDEO_GC2093=y CONFIG_VIDEO_GC2145=y CONFIG_VIDEO_GC2385=y diff --git a/arch/arm64/configs/rockchip_linux_defconfig b/arch/arm64/configs/rockchip_linux_defconfig index b6c973dae825e..65486cd3afd9d 100644 --- a/arch/arm64/configs/rockchip_linux_defconfig +++ b/arch/arm64/configs/rockchip_linux_defconfig @@ -318,6 +318,7 @@ CONFIG_VIDEO_TC35874X=y CONFIG_VIDEO_RK_IRCUT=y CONFIG_VIDEO_GC8034=y CONFIG_VIDEO_GC5603=y +CONFIG_VIDEO_GC8613=y CONFIG_VIDEO_IMX415=y CONFIG_VIDEO_IMX464=y CONFIG_VIDEO_OS04A10=y diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 16cb4466ceb90..1ca47ee97f21c 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -976,6 +976,17 @@ config VIDEO_GC5603 To compile this driver as a module, choose M here: the module will be called gc5603. +config VIDEO_GC8613 + tristate "GalaxyCore GC8613 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the GalaxyCore GC8613 sensor. + + To compile this driver as a module, choose M here: the + module will be called gc8613. + config VIDEO_GC1084 tristate "GalaxyCore GC1084 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index cf97f876308db..b67ff645199f6 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -198,6 +198,7 @@ obj-$(CONFIG_VIDEO_GC5025) += gc5025.o obj-$(CONFIG_VIDEO_GC5035) += gc5035.o obj-$(CONFIG_VIDEO_GC8034) += gc8034.o obj-$(CONFIG_VIDEO_GC5603) += gc5603.o +obj-$(CONFIG_VIDEO_GC8613) += gc8613.o obj-$(CONFIG_VIDEO_HI556) += hi556.o obj-$(CONFIG_VIDEO_IMX214) += imx214.o obj-$(CONFIG_VIDEO_IMX214_EEPROM) += imx214_eeprom.o diff --git a/drivers/media/i2c/gc5603.c b/drivers/media/i2c/gc5603.c index dcc053a62a262..dc7a8a455956a 100644 --- a/drivers/media/i2c/gc5603.c +++ b/drivers/media/i2c/gc5603.c @@ -179,12 +179,10 @@ static const struct regval gc5603_2960x1666_regs_2lane[] = { //window 2960x1666 //BGGR {0x03fe, 0xf0}, - {0x03fe, 0x00}, - {0x03fe, 0x10}, - {0x03fe, 0x00}, - {0x0202, 0x01}, - {0x0203, 0x50}, - {0x0a38, 0x02}, + {0x03fe, 0x00}, + {0x03fe, 0x10}, + {0x03fe, 0x00}, + {0x0a38, 0x02}, {0x0a38, 0x03}, {0x0a20, 0x07}, {0x061b, 0x03}, @@ -197,12 +195,12 @@ static const struct regval gc5603_2960x1666_regs_2lane[] = { {0x0a35, 0x11}, {0x0a36, 0x5e}, {0x0a37, 0x03}, - {0x0314, 0x50}, - {0x0315, 0x32}, - {0x031c, 0xce}, - {0x0219, 0x47}, - {0x0342, 0x04}, - {0x0343, 0xb0}, + {0x0314, 0x50}, + {0x0315, 0x32}, + {0x031c, 0xce}, + {0x0219, 0x57}, + {0x0342, 0x04}, + {0x0343, 0xb0}, {0x0340, 0x06}, {0x0341, 0xd6}, {0x0345, 0x02}, @@ -223,29 +221,30 @@ static const struct regval gc5603_2960x1666_regs_2lane[] = { {0x070c, 0x01}, {0x070e, 0xd2}, {0x070f, 0x05}, + {0x0709, 0x40}, + {0x0719, 0x40}, {0x0909, 0x07}, {0x0902, 0x04}, {0x0904, 0x0b}, {0x0907, 0x54}, {0x0908, 0x06}, {0x0903, 0x9d}, - {0x072a, 0x1c},//18 - {0x072b, 0x1c},//18 + {0x072a, 0x1c}, + {0x072b, 0x1c}, {0x0724, 0x2b}, {0x0727, 0x2b}, {0x1466, 0x18}, - {0x1467, 0x08}, - {0x1468, 0x10}, - {0x1469, 0x80}, - {0x146a, 0xe8},//b8 - {0x1412, 0x20}, + {0x1467, 0x15}, + {0x1468, 0x15}, + {0x1469, 0x70}, + {0x146a, 0xe8}, {0x0707, 0x07}, {0x0737, 0x0f}, {0x0704, 0x01}, - {0x0706, 0x03}, - {0x0716, 0x03}, + {0x0706, 0x02}, + {0x0716, 0x02}, {0x0708, 0xc8}, - {0x0718, 0xc8}, + {0x0718, 0xc8}, {0x061a, 0x02}, {0x1430, 0x80}, {0x1407, 0x10}, @@ -254,20 +253,18 @@ static const struct regval gc5603_2960x1666_regs_2lane[] = { {0x1438, 0x01}, {0x02ce, 0x03}, {0x0245, 0xc9}, - {0x023a, 0x08},//3B + {0x023a, 0x08}, {0x02cd, 0x88}, {0x0612, 0x02}, {0x0613, 0xc7}, - {0x0243, 0x03},//06 + {0x0243, 0x03}, {0x0089, 0x03}, - {0x0002, 0xab}, + {0x0002, 0xab}, {0x0040, 0xa3}, - {0x0075, 0x64},//64 + {0x0075, 0x64}, {0x0004, 0x0f}, {0x0053, 0x0a}, {0x0205, 0x0c}, - - //auto_load}, {0x0a67, 0x80}, {0x0a54, 0x0e}, {0x0a65, 0x10}, @@ -298,9 +295,10 @@ static const struct regval gc5603_2960x1666_regs_2lane[] = { {0x0113, 0x02}, {0x0114, 0x01}, {0x0115, 0x10}, - //{0x0a70, 0x00}, - //{0x0080, 0x02}, - //{0x0a67, 0x00}, + {0x0100, 0x09}, + {0x0a70, 0x00}, + {0x0080, 0x02}, + {0x0a67, 0x00}, {0x0052, 0x02}, {0x0076, 0x01}, {0x021a, 0x10}, From f4b333545c9bff5dc5774fb8d3f7ad01601a00cd Mon Sep 17 00:00:00 2001 From: shingoxy <2877898@163.com> Date: Mon, 12 Aug 2024 10:57:33 +0800 Subject: [PATCH 10/13] =?UTF-8?q?=09=E6=96=B0=E6=96=87=E4=BB=B6=EF=BC=9A?= =?UTF-8?q?=20=20=20drivers/media/i2c/gc8613.c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- drivers/media/i2c/gc8613.c | 2317 ++++++++++++++++++++++++++++++++++++ 1 file changed, 2317 insertions(+) create mode 100644 drivers/media/i2c/gc8613.c diff --git a/drivers/media/i2c/gc8613.c b/drivers/media/i2c/gc8613.c new file mode 100644 index 0000000000000..460d4a9924a5a --- /dev/null +++ b/drivers/media/i2c/gc8613.c @@ -0,0 +1,2317 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * GC8613 driver + * + * Copyright (C) 2023 Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 init version. + * V0.0X01.0X02 support mirror and flip + */ + +#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, 0x02) +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" +#define OF_CAMERA_HDR_MODE "rockchip,camera-hdr-mode" +#define GC8613_NAME "gc8613" + +#define GC8613_IS_LINEAR 0 +#define GC8613_XVCLK_FREQ 24000000 +#define GC8613_LANES 4 + +#if GC8613_IS_LINEAR +#define GC8613_LINK_FREQ_LINEAR 396000000 +#define GC8613_PIXEL_RATE_LINEAR (GC8613_LINK_FREQ_LINEAR * 2 / 10 * GC8613_LANES) + +#else +#define GC8613_LINK_FREQ_LINEAR 594000000 +#define GC8613_PIXEL_RATE_LINEAR (GC8613_LINK_FREQ_LINEAR * 2 / 12 * GC8613_LANES) +#endif + +#define GC8613_LINK_FREQ_FPS 501190000 +#define GC8613_MAX_PIXEL_RATE (GC8613_LINK_FREQ_FPS * 2 / 10 * GC8613_LANES) + +#define GC8613_REG_EXPOSURE_H 0x0202 +#define GC8613_REG_EXPOSURE_L 0x0203 +#define GC8613_EXPOSURE_MIN 1 +#define GC8613_EXPOSURE_STEP 1 + +#define GC8613_VTS_MAX 0x3fff +#define GC8613_REG_VTS_H 0x0340 +#define GC8613_REG_VTS_L 0x0341 + +#define GC8613_GAIN_MIN 64 +#define GC8613_GAIN_MAX 0x7fffffff +#define GC8613_GAIN_STEP 1 +#define GC8613_GAIN_DEFAULT 64 + +#define GC8613_OTP_MIRROR_FLIP_REG 0x0a73 +#define GC8613_MIRROR_BIT_MASK BIT(0) +#define GC8613_MIRROR_FLIP_REG 0x022c +#define GC8613_FLIP_BIT_MASK BIT(1) + +#define GC8613_REG_CTRL_MODE 0x0100 +#define GC8613_MODE_SW_STANDBY 0x00 +#define GC8613_MODE_STREAMING 0x09 + +#define CHIP_ID 0x8613 +#define GC8613_REG_CHIP_ID_H 0x03f0 +#define GC8613_REG_CHIP_ID_L 0x03f1 + +#define GC8613_REG_VALUE_08BIT 1 +#define GC8613_REG_VALUE_16BIT 2 +#define GC8613_REG_VALUE_24BIT 3 + +#define GC8613_REG_TEST_PATTERN 0x008c +#define GC8613_TEST_PATTERN_ENABLE 0x14 +#define GC8613_TEST_PATTERN_DISABLE 0x10 + + +#define REG_DELAY 0xFFFE +#define REG_NULL 0xFFFF + + +static const char * const gc8613_supply_names[] = { + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ + "avdd", /* Analog power */ +}; + +/* config MIPI channel for linear or HDR mode different exposure time frame, open.k 20210924 +enum ar0233_max_pad { + PAD0, + PAD1, + PAD2, + PAD3, + PAD_MAX, +}; +*/ + +#define GC8613_NUM_SUPPLIES ARRAY_SIZE(gc8613_supply_names) + +struct gc8613_mode { + u32 bus_fmt; + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; + u32 mipi_freq_idx; + u32 bpp; + u32 hdr_mode; + u32 vc[PAD_MAX]; +}; + +struct gc8613 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct gpio_desc *pwren_gpio; + struct regulator_bulk_data supplies[GC8613_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 *h_flip; + struct v4l2_ctrl *v_flip; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + bool power_on; + const struct gc8613_mode *cur_mode; + u32 cfg_num; + u32 module_index; + u32 cur_vts; + u32 cur_pixel_rate; + u32 cur_link_freq; + struct preisp_hdrae_exp_s init_hdrae_exp; + const char *module_facing; + const char *module_name; + const char *len_name; + bool has_init_exp; + u8 flip; +}; + +#define to_gc8613(sd) container_of(sd, struct gc8613, subdev) + +struct regval { + u16 addr; + u8 val; +}; + +static const struct regval gc8613_global_regs[] = { + {REG_NULL, 0x00}, +}; + + + +//version 1.6 +// +//mclk 24MHz, mipiclk 1188Mbps, wpclk 216MHz, rpclk 198MHz +//rowtime 14.52us, vts 2296 +//darksun on, HDR off, fixposition off, DAG on +static const struct regval gc8613_nolinear12bit_3840x2160_regs[] ={ + {0x03fe,0xf0}, + {0x03fe,0x00}, + {0x03fe,0x10}, + {0x0a38,0x01}, + {0x0a20,0x19}, + {0x061b,0x17}, + {0x061c,0x48}, + {0x061d,0x05}, + {0x061e,0x5a}, + {0x061f,0x05}, + {0x0a21,0x24}, + {0x0a31,0xc6}, + {0x0a34,0x40}, + {0x0a35,0x08}, + {0x0a37,0x44}, + {0x0314,0x50}, + {0x0315,0x00}, + {0x031c,0xce}, + {0x0219,0x47}, + {0x0342,0x03}, + {0x0343,0x10}, + {0x0259,0x08}, + {0x025a,0x98}, + {0x0340,0x08}, + {0x0341,0xf8}, + {0x0345,0x02}, + {0x0347,0x02}, + {0x0348,0x0f}, + {0x0349,0x18}, + {0x034a,0x08}, + {0x034b,0x88}, + {0x034f,0xf0}, + {0x0094,0x0f}, + {0x0095,0x00}, + {0x0096,0x08}, + {0x0097,0x70}, + {0x0099,0x0c}, + {0x009b,0x0c}, + {0x060c,0x06}, + {0x060e,0x20}, + {0x060f,0x0f}, + {0x070c,0x06}, + {0x070e,0x20}, + {0x070f,0x0f}, + {0x0087,0x50}, + {0x0907,0xd5}, + {0x0909,0x06}, + {0x0902,0x0b}, + {0x0904,0x08}, + {0x0908,0x09}, + {0x0903,0xc5}, + {0x090c,0x09}, + {0x0905,0x10}, + {0x0906,0x00}, + + {0x0724,0x2b}, + {0x0727,0x2b}, + {0x072b,0x1c}, + {0x072a,0x7c}, + {0x073e,0x40}, + {0x0078,0x88}, + {0x0268,0x40}, + {0x0269,0x44}, + {0x0351,0x54}, + {0x0618,0x01}, + {0x1466,0x45}, + {0x1468,0x46}, + {0x1467,0x46}, + {0x0709,0x40}, + {0x0719,0x40}, + {0x1469,0xf0}, + {0x146a,0xd0}, + {0x146b,0x03}, + {0x1480,0x07}, + {0x1481,0x80}, + {0x1484,0x0b}, + {0x1485,0xc0}, + {0x1430,0x80}, + {0x1407,0x10}, + {0x1408,0x16}, + {0x1409,0x03}, + {0x1434,0x04}, + {0x1447,0x75}, + {0x1470,0x10}, + {0x1471,0x13}, + {0x0122,0x0b}, + {0x0123,0x30}, + {0x0124,0x04}, + {0x0125,0x30}, + {0x0126,0x0f}, + {0x0127,0x15}, + {0x0128,0xa8}, + {0x0129,0x0c}, + {0x012a,0x18}, + {0x012b,0x18}, + {0x1438,0x00}, + {0x143a,0x00}, + {0x024b,0x02}, + {0x0245,0xc7}, + {0x025b,0x07}, + {0x02bb,0x77}, + {0x0612,0x01}, + {0x0613,0x26}, + {0x0243,0x66}, + {0x0087,0x53}, + {0x0053,0x05}, + {0x0089,0x00}, + {0x0002,0xeb}, + {0x005a,0x0c}, + {0x0040,0x83}, + {0x0075,0x68}, + {0x0205,0x0c}, + {0x0202,0x03}, + {0x0203,0x27}, + {0x061a,0x02}, + {0x0213,0x64}, + {0x0265,0x01}, + {0x0618,0x05}, + {0x026e,0x74}, + {0x0270,0x02}, + {0x0709,0x00}, + {0x0719,0x00}, + {0x0812,0xdb}, + {0x0822,0x0f}, + {0x0821,0x18}, + {0x0002,0xef}, + {0x0813,0xfb}, + {0x0070,0x88}, + {0x03fe,0x00}, + {0x0106,0x78}, + {0x0136,0x00}, + {0x0181,0xf0}, + {0x0185,0x01}, + {0x0180,0x46}, + {0x0106,0x38}, + {0x010d,0x80}, + {0x010e,0x16}, + {0x0111,0x2c}, + {0x0112,0x02}, + {0x0114,0x03}, + {0x0100,0x09}, + {0x79cf,0x01}, + {0x0219,0x47}, + {0x0054,0x98}, + {0x0076,0x01}, + {0x0052,0x02}, + {0x021a,0x10}, + {0x0430,0x05}, + {0x0431,0x05}, + {0x0432,0x05}, + {0x0433,0x05}, + {0x0434,0x70}, + {0x0435,0x70}, + {0x0436,0x70}, + {0x0437,0x70}, + {0x0004,0x0f}, + {0x0704,0x03}, + {0x071d,0xdc}, + {0x071e,0x05}, + {0x0706,0x02}, + {0x0716,0x02}, + {0x0708,0xc8}, + {0x0718,0xc8}, + {0x071d,0xdc}, + {0x071e,0x05}, + {0x1469,0x80}, +//otp autoload + {0x031f,0x01}, + {0x031f,0x00}, + {0x0a67,0x80}, + {0x0a54,0x0e}, + {0x0a65,0x10}, + {0x0a98,0x04}, + {0x05be,0x00}, + {0x05a9,0x01}, + {0x0089,0x02}, + {0x0aa0,0x00}, + {0x0023,0x00}, + {0x0022,0x00}, + {0x0025,0x00}, + {0x0024,0x00}, + {0x0028,0x0f}, + {0x0029,0x18}, + {0x002a,0x08}, + {0x002b,0x88}, + {0x0317,0x1c}, + {0x0a70,0x03}, + {0x0a82,0x00}, + {0x0a83,0xe0}, + {0x0a71,0x00}, + {0x0a72,0x02}, + {0x0a73,0x60}, + {0x0a75,0x41}, + {0x0a70,0x03}, + {0x0a5a,0x80}, + {REG_DELAY, 0x14}, // sleep 20ms + + {0x0089,0x00}, + {0x05be,0x01}, + {0x0a70,0x00}, + {0x0080,0x02}, + {0x0a67,0x00}, + {0x024b,0x02}, + {0x0220,0x80}, + {0x0058,0x00}, + {0x0059,0x04}, + {REG_NULL, 0x00}, +}; + +//version v1.6 +// +//mclk 24MHz, mipiclk 1004Mbps, wpclk 216MHz, rpclk 200.8MHz +//rowtime 14.81us, vts 2250 +//darksun on, HDR off, fixposition off, DAG off +static const struct regval gc8613_linear10bit_3840x2160_regs[] ={ + {0x03fe,0xf0}, + {0x03fe,0x00}, + {0x03fe,0x10}, + {0x0a38,0x01}, + {0x0a20,0x19}, + {0x061b,0x17}, + {0x061c,0x50}, + {0x061d,0x06}, + {0x061e,0x87}, + {0x061f,0x05}, + {0x0a21,0x10}, + {0x0a31,0xfb}, + {0x0a34,0x40}, + {0x0a35,0x08}, + {0x0a37,0x46}, + {0x0314,0x50}, + {0x0315,0x00}, + {0x031c,0xce}, + {0x0219,0x47}, + {0x0342,0x03}, + {0x0343,0x20}, + {0x0259,0x08}, + {0x025a,0x96}, + {0x0340,0x08}, + {0x0341,0xca}, + {0x0351,0x00}, + {0x0345,0x02}, + {0x0347,0x02}, + {0x0348,0x0f}, + {0x0349,0x18}, + {0x034a,0x08}, + {0x034b,0x88}, + {0x034f,0xf0}, + {0x0094,0x0f}, + {0x0095,0x00}, + {0x0096,0x08}, + {0x0097,0x70}, + {0x0099,0x0c}, + {0x009b,0x0c}, + {0x060c,0x06}, + {0x060e,0x20}, + {0x060f,0x0f}, + {0x070c,0x06}, + {0x070e,0x20}, + {0x070f,0x0f}, + {0x0087,0x50}, + {0x0907,0xd5}, + {0x0909,0x06}, + {0x0902,0x0b}, + {0x0904,0x08}, + {0x0908,0x09}, + {0x0903,0xc5}, + {0x090c,0x09}, + {0x0905,0x10}, + {0x0906,0x00}, + {0x072a,0x7c}, + {0x0724,0x2b}, + {0x0727,0x2b}, + {0x072b,0x1c}, + {0x073e,0x40}, + {0x0078,0x88}, + {0x0618,0x01}, + {0x1466,0x12}, + {0x1468,0x10}, + {0x1467,0x10}, + {0x0709,0x40}, + {0x0719,0x40}, + {0x1469,0x80}, + {0x146a,0xc0}, + {0x146b,0x03}, + {0x1480,0x02}, + {0x1481,0x80}, + {0x1484,0x08}, + {0x1485,0xc0}, + {0x1430,0x80}, + {0x1407,0x10}, + {0x1408,0x16}, + {0x1409,0x03}, + {0x1434,0x04}, + {0x1447,0x75}, + {0x1470,0x10}, + {0x1471,0x13}, + {0x1438,0x00}, + {0x143a,0x00}, + {0x024b,0x02}, + {0x0245,0xc7}, + {0x025b,0x07}, + {0x02bb,0x77}, + {0x0612,0x01}, + {0x0613,0x26}, + {0x0243,0x66}, + {0x0087,0x53}, + {0x0053,0x05}, + {0x0089,0x02}, + {0x0002,0xeb}, + {0x005a,0x0c}, + {0x0040,0x83}, + {0x0075,0x54}, + {0x0205,0x0c}, + {0x0202,0x01}, + {0x0203,0x27}, + {0x061a,0x02}, + {0x03fe,0x00}, + {0x0106,0x78}, + {0x0136,0x03}, + {0x0181,0xf0}, + {0x0185,0x01}, + {0x0180,0x46}, + {0x0106,0x38}, + {0x010d,0xc0}, + {0x010e,0x12}, + {0x0113,0x02}, + {0x0114,0x03}, + {0x0100,0x09}, + {0x0004,0x0f}, + {0x0219,0x47}, + {0x0054,0x98}, + {0x0076,0x01}, + {0x0052,0x02}, + {0x021a,0x10}, + {0x0430,0x21}, + {0x0431,0x21}, + {0x0432,0x21}, + {0x0433,0x21}, + {0x0434,0x61}, + {0x0435,0x61}, + {0x0436,0x61}, + {0x0437,0x61}, + {0x0704,0x03}, + {0x071d,0xdc}, + {0x071e,0x05}, + {0x0706,0x02}, + {0x0716,0x02}, + {0x0708,0xc8}, + {0x0718,0xc8}, + {0x031f,0x01}, + {0x031f,0x00}, + {0x0a67,0x80}, + {0x0a54,0x0e}, + {0x0a65,0x10}, + {0x0a98,0x04}, + {0x05be,0x00}, + {0x05a9,0x01}, + {0x0089,0x02}, + {0x0aa0,0x00}, + {0x0023,0x00}, + {0x0022,0x00}, + {0x0025,0x00}, + {0x0024,0x00}, + {0x0028,0x0f}, + {0x0029,0x18}, + {0x002a,0x08}, + {0x002b,0x88}, + {0x0317,0x1c}, + {0x0a70,0x03}, + {0x0a82,0x00}, + {0x0a83,0xe0}, + {0x0a71,0x00}, + {0x0a72,0x02}, + {0x0a73,0x60}, + {0x0a75,0x41}, + {0x0a70,0x03}, + {0x0a5a,0x80}, + {REG_DELAY, 0x14}, // sleep 20ms + {0x0089,0x02}, + {0x05be,0x01}, + {0x0a70,0x00}, + {0x0080,0x02}, + {0x0a67,0x00}, + {0x024b,0x02}, + {0x0220,0x80}, + {0x0058,0x00}, + {0x0059,0x04}, + {REG_NULL, 0x00}, +}; + +static const struct regval gc8613_liner10bit__1920x1080_90fps_regs[] = { +//version 1.4 +//mclk 27Mhz +//wpclk:359.4375mhz,rpclk:359.4375mhz +//mipi 1002.38Mbps/lane +//vts 1532 +//window 1920 1080 +//row_time 7.25us +//bayer order RGrGbB + {0x03fe, 0xf0}, + {0x03fe, 0x00}, + {0x03fe, 0x10}, + {0x0a38, 0x01}, + {0x0a20, 0x19}, + {0x061b, 0x17}, + {0x061c, 0x44}, + {0x061d, 0x09}, + {0x061e, 0x46}, + {0x061f, 0x04}, + {0x0a21, 0x08}, + {0x0a28, 0x01}, + {0x0a30, 0x01}, + {0x0a31, 0x29}, + {0x0a34, 0x40}, + {0x0a35, 0x08}, + {0x0a37, 0x44}, + {0x0314, 0x70}, + {0x031c, 0xce}, + {0x0219, 0x47}, + {0x0342, 0x02}, + {0x0343, 0x83}, + {0x0259, 0x04}, + {0x025a, 0x00}, + {0x0340, 0x05}, + {0x0341, 0xfc}, + {0x0351, 0x00}, + {0x0345, 0x02}, + {0x0347, 0x02}, + {0x0348, 0x0f}, + {0x0349, 0x10}, //3856 + {0x034a, 0x08}, + {0x034b, 0x88}, //2184 + {0x034f, 0xf0}, + {0x0094, 0x0f}, + {0x0095, 0x00}, + {0x0096, 0x08}, + {0x0097, 0x70}, + {0x0099, 0x09}, + {0x009b, 0x09}, + {0x060c, 0x0a}, + {0x060e, 0x20}, + {0x060f, 0x0f}, + {0x070c, 0x0a}, + {0x070e, 0x20}, + {0x070f, 0x0f}, + {0x0087, 0x50}, + {0x0907, 0xd5}, + {0x0909, 0x06}, + {0x0902, 0x0b}, + {0x0904, 0x08}, + {0x0908, 0x09}, + {0x0903, 0xc5}, + {0x090c, 0x09}, + {0x0905, 0x10}, + {0x0906, 0x00}, + {0x072a, 0x7c}, + {0x0724, 0x2b}, + {0x0727, 0x2b}, + {0x072b, 0x1c}, + {0x073e, 0x40}, + {0x0078, 0x88}, + {0x0618, 0x01}, + {0x1466, 0x12}, + {0x1468, 0x10}, + {0x1467, 0x10}, + {0x0709, 0x40}, + {0x0719, 0x40}, + {0x1469, 0x80}, + {0x146a, 0xc0}, + {0x146b, 0x03}, + {0x1480, 0x02}, + {0x1481, 0x80}, + {0x1484, 0x08}, + {0x1485, 0xc0}, + {0x1430, 0x80}, + {0x1407, 0x10}, + {0x1408, 0x16}, + {0x1409, 0x03}, + {0x1434, 0x04}, + {0x1447, 0x75}, + {0x1470, 0x10}, + {0x1471, 0x13}, + {0x1438, 0x00}, + {0x143a, 0x00}, + {0x024b, 0x02}, + {0x0245, 0xc7}, + {0x025b, 0x07}, + {0x02bb, 0x77}, + {0x0612, 0x01}, + {0x0613, 0x26}, + {0x0243, 0x66}, + {0x0087, 0x53}, + {0x0053, 0x05}, + {0x0089, 0x02}, + {0x0002, 0xeb}, + {0x005a, 0x0c}, + {0x0040, 0x83}, + {0x0075, 0x54}, + {0x0077, 0x08}, + {0x0218, 0x10}, + {0x0205, 0x0c}, + {0x0202, 0x06}, + {0x0203, 0x27}, + {0x061a, 0x02}, + {0x0122, 0x11}, + {0x0123, 0x40}, + {0x0126, 0x0f}, + {0x0129, 0x08},///0x12 + {0x012a, 0x16}, + {0x012b, 0x0f}, + {0x03fe, 0x00}, + {0x0205, 0x0c}, + {0x0202, 0x01}, + {0x0203, 0x27}, + {0x061a, 0x02}, + {0x03fe, 0x00}, + {0x0106, 0x78}, + {0x0136, 0x03}, + {0x0181, 0xf0}, + {0x0185, 0x01}, + {0x0180, 0x46}, + {0x0106, 0x38}, + {0x010d, 0x60}, + {0x010e, 0x09}, + {0x0113, 0x02}, + {0x0114, 0x03}, + {0x0100, 0x09}, + {0x0004, 0x0f}, + {0x0219, 0x47}, + {0x0054, 0x98}, + {0x0076, 0x01}, + {0x0052, 0x02}, + {0x021a, 0x10}, + {0x0430, 0x21}, + {0x0431, 0x21}, + {0x0432, 0x21}, + {0x0433, 0x21}, + {0x0434, 0x61}, + {0x0435, 0x61}, + {0x0436, 0x61}, + {0x0437, 0x61}, + {0x0704, 0x07}, + {0x0706, 0x02}, + {0x0716, 0x02}, + {0x0708, 0xc8}, + {0x0718, 0xc8}, + //otp autoload + {0x031f, 0x01}, + {0x031f, 0x00}, + {0x0a67, 0x80}, + {0x0a54, 0x0e}, + {0x0a65, 0x10}, + {0x0a98, 0x04}, + {0x05be, 0x00}, + {0x05a9, 0x01}, + {0x0089, 0x02}, + {0x0aa0, 0x00}, + {0x0023, 0x00}, + {0x0022, 0x00}, + {0x0025, 0x00}, + {0x0024, 0x00}, + {0x0028, 0x0f}, + {0x0029, 0x18}, + {0x002a, 0x08}, + {0x002b, 0x88}, + {0x0317, 0x1c}, + {0x0a70, 0x03}, + {0x0a82, 0x00}, + {0x0a83, 0xe0}, + {0x0a71, 0x00}, + {0x0a72, 0x02}, + {0x0a73, 0x60}, + {0x0a75, 0x41}, + {0x0a70, 0x03}, + {0x0a5a, 0x80}, + + {REG_DELAY, 0x14},//sleep 20 + + {0x0089, 0x02}, + {0x05be, 0x01}, + {0x0a70, 0x00}, + {0x0080, 0x02}, + {0x0a67, 0x00}, + {0x024b, 0x02}, + {0x0220, 0xcf}, + {REG_NULL, 0x00}, +}; + +static const u32 gain_level_table_nolinear[23] = { + 1024 , + 1184 , + 1440 , + 1680 , + 2016 , + 2272 , + 2624 , + 3200 , + 3824 , + 4544 , + 5456 , + 6512 , + 7824 , + 8512 , + 10112, + 12288, + 15184, + 16768, + 20112, + 24000, + 28192, + 33856, + 0xffffffff, +}; + +static const u32 gain_level_table_linear[27] = { + 1024 , + 1184 , + 1440 , + 1680 , + 2016 , + 2272 , + 2624 , + 3200 , + 3824 , + 4544 , + 5456 , + 6512 , + 7824 , + 8512 , + 10112, + 12288, + 15184, + 16768, + 20112, + 24000, + 28192, + 33856, + 40320, + 48784, + 58688, + 69872, + 0xffffffff, +}; + + +static const u32 reg_val_table_nolinear[22][10] = { + //614 615 225 1467 1468 26e 270 1447 b8 b9 + {0x00, 0x00, 0x00, 0x46, 0x46, 0x74, 0x02, 0x77, 0x01, 0x00}, + {0x90, 0x02, 0x00, 0x47, 0x47, 0x74, 0x02, 0x77, 0x01, 0x0a}, + {0x01, 0x00, 0x00, 0x47, 0x47, 0x77, 0x02, 0x77, 0x01, 0x1a}, + {0x91, 0x02, 0x00, 0x48, 0x48, 0x77, 0x02, 0x77, 0x01, 0x29}, + {0x02, 0x00, 0x00, 0x48, 0x48, 0x79, 0x02, 0x77, 0x01, 0x3e}, + {0x00, 0x00, 0x00, 0x46, 0x46, 0x74, 0x02, 0x75, 0x02, 0x0d}, + {0x90, 0x02, 0x00, 0x47, 0x47, 0x74, 0x02, 0x75, 0x02, 0x24}, + {0x01, 0x00, 0x00, 0x47, 0x47, 0x77, 0x02, 0x75, 0x03, 0x08}, + {0x91, 0x02, 0x00, 0x48, 0x48, 0x79, 0x02, 0x75, 0x03, 0x2e}, + {0x02, 0x00, 0x00, 0x49, 0x49, 0x7a, 0x02, 0x75, 0x04, 0x1b}, + {0x92, 0x02, 0x00, 0x4b, 0x4b, 0x7b, 0x02, 0x75, 0x05, 0x14}, + {0x03, 0x00, 0x00, 0x4c, 0x4c, 0x7c, 0x02, 0x75, 0x06, 0x17}, + {0x93, 0x02, 0x00, 0x4d, 0x4d, 0x7d, 0x02, 0x75, 0x07, 0x29}, + {0x00, 0x00, 0x01, 0x4f, 0x4f, 0x7e, 0x02, 0x75, 0x08, 0x13}, + {0x90, 0x02, 0x01, 0x50, 0x50, 0x7f, 0x02, 0x75, 0x09, 0x38}, + {0x01, 0x00, 0x01, 0x51, 0x51, 0x7f, 0x02, 0x75, 0x0c, 0x00}, + {0x91, 0x02, 0x01, 0x53, 0x53, 0x7f, 0x02, 0x75, 0x0e, 0x35}, + {0x02, 0x00, 0x01, 0x54, 0x54, 0x7f, 0x02, 0x75, 0x10, 0x18}, + {0x92, 0x02, 0x01, 0x56, 0x56, 0x7f, 0x02, 0x75, 0x13, 0x29}, + {0x03, 0x00, 0x01, 0x58, 0x58, 0x7f, 0x02, 0x75, 0x17, 0x1c}, + {0x93, 0x02, 0x01, 0x5a, 0x5a, 0x7f, 0x01, 0x75, 0x1b, 0x22}, + {0x04, 0x00, 0x01, 0x5c, 0x5c, 0x7f, 0x01, 0x75, 0x21, 0x04}, +}; + +static const u32 reg_val_table_linear[26][8] = { +// 0614 0615 225 1467 1468 1447 b8 b9 + {0x00, 0x00, 0x00, 0x0d, 0x0d, 0x77, 0x01, 0x00}, + {0x90, 0x02, 0x00, 0x0e, 0x0e, 0x77, 0x01, 0x0a}, + {0x01, 0x00, 0x00, 0x0e, 0x0e, 0x77, 0x01, 0x1a}, + {0x91, 0x02, 0x00, 0x0f, 0x0f, 0x77, 0x01, 0x29}, + {0x02, 0x00, 0x00, 0x0f, 0x0f, 0x77, 0x01, 0x3e}, + {0x00, 0x00, 0x00, 0x0d, 0x0d, 0x75, 0x02, 0x0d}, + {0x90, 0x02, 0x00, 0x0d, 0x0d, 0x75, 0x02, 0x24}, + {0x01, 0x00, 0x00, 0x0e, 0x0e, 0x75, 0x03, 0x08}, + {0x91, 0x02, 0x00, 0x0e, 0x0e, 0x75, 0x03, 0x2e}, + {0x02, 0x00, 0x00, 0x0f, 0x0f, 0x75, 0x04, 0x1b}, + {0x92, 0x02, 0x00, 0x0f, 0x0f, 0x75, 0x05, 0x14}, + {0x03, 0x00, 0x00, 0x10, 0x10, 0x75, 0x06, 0x17}, + {0x93, 0x02, 0x00, 0x10, 0x10, 0x75, 0x07, 0x29}, + {0x00, 0x00, 0x01, 0x11, 0x11, 0x75, 0x08, 0x13}, + {0x90, 0x02, 0x01, 0x12, 0x12, 0x75, 0x09, 0x38}, + {0x01, 0x00, 0x01, 0x13, 0x13, 0x75, 0x0c, 0x00}, + {0x91, 0x02, 0x01, 0x14, 0x14, 0x75, 0x0e, 0x35}, + {0x02, 0x00, 0x01, 0x15, 0x15, 0x75, 0x10, 0x18}, + {0x92, 0x02, 0x01, 0x16, 0x16, 0x75, 0x13, 0x29}, + {0x03, 0x00, 0x01, 0x17, 0x17, 0x75, 0x17, 0x1c}, + {0x93, 0x02, 0x01, 0x18, 0x18, 0x75, 0x1b, 0x22}, + {0x04, 0x00, 0x01, 0x19, 0x19, 0x75, 0x21, 0x04}, + {0x94, 0x02, 0x01, 0x1b, 0x1b, 0x75, 0x27, 0x18}, + {0x05, 0x00, 0x01, 0x1d, 0x1d, 0x75, 0x2f, 0x29}, + {0x95, 0x02, 0x01, 0x1e, 0x1e, 0x75, 0x39, 0x0b}, + {0x06, 0x00, 0x01, 0x20, 0x20, 0x75, 0x44, 0x0f}, +}; + + +static const struct gc8613_mode supported_modes[] = { +#if GC8613_IS_LINEAR + { + .width = 3840, + .height = 2160, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0127, + .hts_def = 0x0320*8, + .vts_def = 0x08CA, + .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, + .reg_list = gc8613_linear10bit_3840x2160_regs, + .mipi_freq_idx = 0, + .bpp = 10, + .hdr_mode = NO_HDR, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, +#else + { + .width = 3840, + .height = 2160, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0327, + .hts_def = 0x0310*8, + .vts_def = 0x08f8, + .bus_fmt = MEDIA_BUS_FMT_SRGGB12_1X12, + .reg_list = gc8613_nolinear12bit_3840x2160_regs, + .mipi_freq_idx = 0, + .bpp = 12, + .hdr_mode = NO_HDR, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, +#endif + { + .width = 1920, + .height = 1080, + .max_fps = { + .numerator = 10000, + .denominator = 900000, + }, + .exp_def = 0x0127, + .hts_def = 0x0283*3, + .vts_def = 0x05fc, + .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, + .reg_list = gc8613_liner10bit__1920x1080_90fps_regs, + .mipi_freq_idx = 1, + .bpp = 10, + .hdr_mode = NO_HDR, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + +}; + +static const s64 link_freq_items[] = { + GC8613_LINK_FREQ_LINEAR, + GC8613_LINK_FREQ_FPS, +}; + +static const char * const gc8613_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", +}; + +/* Write registers up to 4 at a time */ +static int gc8613_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; + + 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 gc8613_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++) { + if (regs[i].addr == REG_DELAY) + usleep_range(regs[i].val * 1000, regs[i].val * 2 * 1000); + else + ret = gc8613_write_reg(client, regs[i].addr, + GC8613_REG_VALUE_08BIT, regs[i].val); + } + + return ret; +} + +/* Read registers up to 4 at a time */ +static int gc8613_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 gc8613_s_power(struct v4l2_subdev *sd, int on) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + struct i2c_client *client = gc8613->client; + int ret = 0; + + + mutex_lock(&gc8613->mutex); + + /* If the power state is not modified - no work to do. */ + if (gc8613->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; + } + + ret = gc8613_write_array(gc8613->client, gc8613_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + gc8613->power_on = true; + } else { + pm_runtime_put(&client->dev); + gc8613->power_on = false; + } + +unlock_and_return: + mutex_unlock(&gc8613->mutex); + + return ret; +} + +static void gc8613_get_module_inf(struct gc8613 *gc8613, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strscpy(inf->base.sensor, GC8613_NAME, sizeof(inf->base.sensor)); + strscpy(inf->base.module, gc8613->module_name, + sizeof(inf->base.module)); + strscpy(inf->base.lens, gc8613->len_name, sizeof(inf->base.lens)); +} +#if 0 +static int gc8613_get_channel_info(struct gc8613 *gc8613, struct rkmodule_channel_info *ch_info) +{ + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + ch_info->vc = gc8613->cur_mode->vc[ch_info->index]; + ch_info->width = gc8613->cur_mode->width; + ch_info->height = gc8613->cur_mode->height; + ch_info->bus_fmt = gc8613->cur_mode->bus_fmt; + return 0; +} +#endif +static long gc8613_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + struct rkmodule_hdr_cfg *hdr; + const struct gc8613_mode *mode; + + u32 i, h, w; + long ret = 0; + u32 stream = 0; + u64 pixel_rate = 0; +// struct rkmodule_channel_info *ch_info; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + gc8613_get_module_inf(gc8613, (struct rkmodule_inf *)arg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = (struct rkmodule_hdr_cfg *)arg; + hdr->esp.mode = HDR_NORMAL_VC; + hdr->hdr_mode = gc8613->cur_mode->hdr_mode; + break; + case RKMODULE_SET_HDR_CFG: + hdr = (struct rkmodule_hdr_cfg *)arg; + w = gc8613->cur_mode->width; + h = gc8613->cur_mode->height; + for (i = 0; i < gc8613->cfg_num; i++) { + if (w == supported_modes[i].width && + h == supported_modes[i].height && + supported_modes[i].hdr_mode == hdr->hdr_mode) { + gc8613->cur_mode = &supported_modes[i]; + break; + } + } + if (i == gc8613->cfg_num) { + dev_err(&gc8613->client->dev, + "not find hdr mode:%d %dx%d config\n", + hdr->hdr_mode, w, h); + ret = -EINVAL; + } else { + mode = gc8613->cur_mode; + w = gc8613->cur_mode->hts_def - + gc8613->cur_mode->width; + h = gc8613->cur_mode->vts_def - + gc8613->cur_mode->height; + __v4l2_ctrl_modify_range(gc8613->hblank, w, w, 1, w); + __v4l2_ctrl_modify_range(gc8613->vblank, h, + GC8613_VTS_MAX - + gc8613->cur_mode->height, + 1, h); + + +#if 0 + gc8613->cur_link_freq = 0; + gc8613->cur_pixel_rate = GC8613_PIXEL_RATE_LINEAR; + + __v4l2_ctrl_s_ctrl_int64(gc8613->pixel_rate, + gc8613->cur_pixel_rate); + __v4l2_ctrl_s_ctrl(gc8613->link_freq, + gc8613->cur_link_freq); + gc8613->cur_vts = gc8613->cur_mode->vts_def; +#endif + __v4l2_ctrl_s_ctrl(gc8613->link_freq, mode->mipi_freq_idx); + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * GC8613_LANES; + __v4l2_ctrl_s_ctrl_int64(gc8613->pixel_rate, pixel_rate); + + dev_info(&gc8613->client->dev, "sensor mode: %d\n", gc8613->cur_mode->hdr_mode); + } + break; + case RKMODULE_SET_QUICK_STREAM: + stream = *((u32 *)arg); + if (stream) + ret = gc8613_write_reg(gc8613->client, GC8613_REG_CTRL_MODE, + GC8613_REG_VALUE_08BIT, GC8613_MODE_STREAMING); + else + ret = gc8613_write_reg(gc8613->client, GC8613_REG_CTRL_MODE, + GC8613_REG_VALUE_08BIT, GC8613_MODE_SW_STANDBY); + break; + #if 0 + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = gc8613_get_channel_info(gc8613, ch_info); + break; + #endif + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long gc8613_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_hdr_cfg *hdr; + long ret; + u32 stream = 0; +// struct rkmodule_channel_info *ch_info; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = gc8613_ioctl(sd, cmd, inf); + if (!ret) { + ret = copy_to_user(up, inf, sizeof(*inf)); + if (ret) + ret = -EFAULT; + } + kfree(inf); + break; + case RKMODULE_GET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = gc8613_ioctl(sd, cmd, hdr); + if (!ret) { + ret = copy_to_user(up, hdr, sizeof(*hdr)); + if (ret) + ret = -EFAULT; + } + kfree(hdr); + break; + case RKMODULE_SET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(hdr, up, sizeof(*hdr)); + if (!ret) + ret = gc8613_ioctl(sd, cmd, hdr); + else + ret = -EFAULT; + kfree(hdr); + break; + case RKMODULE_SET_QUICK_STREAM: + ret = copy_from_user(&stream, up, sizeof(u32)); + if (!ret) + ret = gc8613_ioctl(sd, cmd, &stream); + else + ret = -EFAULT; + break; + #if 0 + case RKMODULE_GET_CHANNEL_INFO: + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); + if (!ch_info) { + ret = -ENOMEM; + return ret; + } + + ret = gc8613_ioctl(sd, cmd, ch_info); + if (!ret) { + ret = copy_to_user(up, ch_info, sizeof(*ch_info)); + if (ret) + ret = -EFAULT; + } + kfree(ch_info); + break; + #endif + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __gc8613_start_stream(struct gc8613 *gc8613) +{ + int ret; + + ret = gc8613_write_array(gc8613->client, gc8613->cur_mode->reg_list); + if (ret) + return ret; + + /* In case these controls are set before streaming */ + ret = __v4l2_ctrl_handler_setup(&gc8613->ctrl_handler); + if (gc8613->has_init_exp && gc8613->cur_mode->hdr_mode != NO_HDR) { + ret = gc8613_ioctl(&gc8613->subdev, PREISP_CMD_SET_HDRAE_EXP, + &gc8613->init_hdrae_exp); + if (ret) { + dev_err(&gc8613->client->dev, + "init exp fail in hdr mode\n"); + return ret; + } + } + if (ret) + return ret; + + ret |= gc8613_write_reg(gc8613->client, GC8613_REG_CTRL_MODE, + GC8613_REG_VALUE_08BIT, GC8613_MODE_STREAMING); + + return ret; +} + +static int __gc8613_stop_stream(struct gc8613 *gc8613) +{ + gc8613->has_init_exp = false; + return gc8613_write_reg(gc8613->client, GC8613_REG_CTRL_MODE, + GC8613_REG_VALUE_08BIT, GC8613_MODE_SW_STANDBY); +} + +static int gc8613_s_stream(struct v4l2_subdev *sd, int on) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + struct i2c_client *client = gc8613->client; + int ret = 0; + + mutex_lock(&gc8613->mutex); + on = !!on; + if (on == gc8613->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 = __gc8613_start_stream(gc8613); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __gc8613_stop_stream(gc8613); + pm_runtime_put(&client->dev); + } + + gc8613->streaming = on; + +unlock_and_return: + mutex_unlock(&gc8613->mutex); + + return ret; +} + +static int gc8613_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + const struct gc8613_mode *mode = gc8613->cur_mode; + + fi->interval = mode->max_fps; + + return 0; +} + +static int gc8613_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + + if (code->index != 0) + return -EINVAL; + code->code = gc8613->cur_mode->bus_fmt; + + return 0; +} + +static int gc8613_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + + if (fse->index >= gc8613->cfg_num) + return -EINVAL; + + if (fse->code != supported_modes[0].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 gc8613_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + + if (fie->index >= gc8613->cfg_num) + return -EINVAL; + + fie->code = supported_modes[fie->index].bus_fmt; + fie->width = supported_modes[fie->index].width; + fie->height = supported_modes[fie->index].height; + fie->interval = supported_modes[fie->index].max_fps; + fie->reserved[0] = supported_modes[fie->index].hdr_mode; + return 0; +} + +static int gc8613_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + const struct gc8613_mode *mode = gc8613->cur_mode; + + mutex_lock(&gc8613->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&gc8613->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->bus_fmt; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&gc8613->mutex); + + return 0; +} + +static int gc8613_get_reso_dist(const struct gc8613_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct gc8613_mode * +gc8613_find_best_fit(struct gc8613 *gc8613, 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 < gc8613->cfg_num; i++) { + dist = gc8613_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 gc8613_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + const struct gc8613_mode *mode; + s64 h_blank, vblank_def; + u64 pixel_rate = 0; + + mutex_lock(&gc8613->mutex); + + mode = gc8613_find_best_fit(gc8613, fmt); + fmt->format.code = mode->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, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&gc8613->mutex); + return -ENOTTY; +#endif + } else { + gc8613->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(gc8613->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(gc8613->vblank, vblank_def, + GC8613_VTS_MAX - mode->height, + 1, vblank_def); + + __v4l2_ctrl_s_ctrl(gc8613->link_freq, mode->mipi_freq_idx); + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * GC8613_LANES; + __v4l2_ctrl_s_ctrl_int64(gc8613->pixel_rate, pixel_rate); + } + mutex_unlock(&gc8613->mutex); + + return 0; +} +#if 0 +#define DST_WIDTH_3840 3840 +#define DST_HEIGHT_2160 2160 +#define DST_WIDTH_1920 1920 +#define DST_HEIGHT_1080 1080 +/* + * The resolution of the driver configuration needs to be exactly + * the same as the current output resolution of the sensor, + * the input width of the isp needs to be 16 aligned, + * the input height of the isp needs to be 8 aligned. + * Can be cropped to standard resolution by this function, + * otherwise it will crop out strange resolution according + * to the alignment rules. + */ +static int gc8613_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + if (gc8613->cur_mode->width == 3840) { + sel->r.left = CROP_START(gc8613->cur_mode->width, DST_WIDTH_3840); + sel->r.width = DST_WIDTH_3840; + sel->r.top = CROP_START(gc8613->cur_mode->height, DST_HEIGHT_2160); + sel->r.height = DST_HEIGHT_2160; + } else if (gc8613->cur_mode->width == 1920) { + sel->r.left = CROP_START(gc8613->cur_mode->width, DST_WIDTH_1920); + sel->r.width = DST_WIDTH_1920; + sel->r.top = CROP_START(gc8613->cur_mode->height, DST_HEIGHT_1080); + sel->r.height = DST_HEIGHT_1080; + } else { + sel->r.left = CROP_START(gc8613->cur_mode->width, gc8613->cur_mode->width); + sel->r.width = gc8613->cur_mode->width; + sel->r.top = CROP_START(gc8613->cur_mode->height, gc8613->cur_mode->height); + sel->r.height = gc8613->cur_mode->height; + } + return 0; + } + return -EINVAL; +} +#endif + + +static int gc8613_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + const struct gc8613_mode *mode = gc8613->cur_mode; + u32 val = 0; + + val = 1 << (GC8613_LANES - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + if (mode->hdr_mode != NO_HDR) + val |= V4L2_MBUS_CSI2_CHANNEL_1; + if (mode->hdr_mode == HDR_X3) + val |= V4L2_MBUS_CSI2_CHANNEL_2; + config->type = V4L2_MBUS_CSI2_DPHY; + config->flags = val; + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int gc8613_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct gc8613_mode *def_mode = &supported_modes[0]; + + mutex_lock(&gc8613->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = def_mode->bus_fmt; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&gc8613->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops gc8613_internal_ops = { + .open = gc8613_open, +}; +#endif + +static const struct v4l2_subdev_core_ops gc8613_core_ops = { + .s_power = gc8613_s_power, + .ioctl = gc8613_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = gc8613_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops gc8613_video_ops = { + .s_stream = gc8613_s_stream, + .g_frame_interval = gc8613_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops gc8613_pad_ops = { + .enum_mbus_code = gc8613_enum_mbus_code, + .enum_frame_size = gc8613_enum_frame_sizes, + .enum_frame_interval = gc8613_enum_frame_interval, + .get_fmt = gc8613_get_fmt, + .set_fmt = gc8613_set_fmt, + .get_mbus_config = gc8613_g_mbus_config, +// .get_selection = gc8613_get_selection, +}; + +static const struct v4l2_subdev_ops gc8613_subdev_ops = { + .core = &gc8613_core_ops, + .video = &gc8613_video_ops, + .pad = &gc8613_pad_ops, +}; + + +static int gc8613_configure_regulators(struct gc8613 *gc8613) +{ + unsigned int i; + + for (i = 0; i < GC8613_NUM_SUPPLIES; i++) + gc8613->supplies[i].supply = gc8613_supply_names[i]; + + return devm_regulator_bulk_get(&gc8613->client->dev, + GC8613_NUM_SUPPLIES, + gc8613->supplies); +} + +static int gc8613_set_gain_reg(struct gc8613 *gc8613, u32 gain) +{ + const struct gc8613_mode *mode; + int i; + int total; + s64 tol_dig_gain = 0; + mode = gc8613->cur_mode; + + +if (mode->bpp == 10) { +//#if GC8613_IS_LINEAR + if (gain < 64) + gain = 64; + total = ARRAY_SIZE(gain_level_table_linear) - 1; + for (i = 0; i < total; i++) { + if (gain_level_table_linear[i] <= gain && + gain < gain_level_table_linear[i + 1]) + break; + } + if(i == ARRAY_SIZE(gain_level_table_linear) - 1) + i = ARRAY_SIZE(gain_level_table_linear) - 2; + tol_dig_gain = gain * 64 / gain_level_table_linear[i]; + + gc8613_write_reg(gc8613->client, 0x031d, + GC8613_REG_VALUE_08BIT, 0x2d); + + gc8613_write_reg(gc8613->client, 0x0614, + GC8613_REG_VALUE_08BIT, reg_val_table_linear[i][0]); + gc8613_write_reg(gc8613->client, 0x0615, + GC8613_REG_VALUE_08BIT, reg_val_table_linear[i][1]); + + gc8613_write_reg(gc8613->client, 0x031d, + GC8613_REG_VALUE_08BIT, 0x28); + + gc8613_write_reg(gc8613->client, 0x0225, + GC8613_REG_VALUE_08BIT, reg_val_table_linear[i][2]); + gc8613_write_reg(gc8613->client, 0x1467, + GC8613_REG_VALUE_08BIT, reg_val_table_linear[i][3]); + gc8613_write_reg(gc8613->client, 0x1468, + GC8613_REG_VALUE_08BIT, reg_val_table_linear[i][4]); + gc8613_write_reg(gc8613->client, 0x1447, + GC8613_REG_VALUE_08BIT, reg_val_table_linear[i][5]); + gc8613_write_reg(gc8613->client, 0x00b8, + GC8613_REG_VALUE_08BIT, reg_val_table_linear[i][6]); + gc8613_write_reg(gc8613->client, 0x00b9, + GC8613_REG_VALUE_08BIT, reg_val_table_linear[i][7]); + + + gc8613_write_reg(gc8613->client, 0x0064, + GC8613_REG_VALUE_08BIT, (tol_dig_gain >> 6)); + gc8613_write_reg(gc8613->client, 0x0065, + GC8613_REG_VALUE_08BIT, (tol_dig_gain & 0x3f)); + //dev_err(&gc8613->client->dev,"gc8613_set_gain_reg 10 bits bbp:%u\n",mode->bpp); +} +else { +//#else + gain=gain*16; + if (gain < 1024) + gain = 1024; + total = ARRAY_SIZE(gain_level_table_nolinear) - 1; + for (i = 0; i < total; i++) { + if (gain_level_table_nolinear[i] <= gain && + gain < gain_level_table_nolinear[i + 1]) + break; + } + if(i == ARRAY_SIZE(gain_level_table_nolinear) - 1) + i = ARRAY_SIZE(gain_level_table_nolinear) - 2; + tol_dig_gain = gain * 64 / gain_level_table_nolinear[i]; + + gc8613_write_reg(gc8613->client, 0x031d, + GC8613_REG_VALUE_08BIT, 0x2d); + + gc8613_write_reg(gc8613->client, 0x0614, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][0]); + gc8613_write_reg(gc8613->client, 0x0615, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][1]); + + gc8613_write_reg(gc8613->client, 0x026e, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][5]); + gc8613_write_reg(gc8613->client, 0x0270, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][6]); + + gc8613_write_reg(gc8613->client, 0x031d, + GC8613_REG_VALUE_08BIT, 0x28); + + gc8613_write_reg(gc8613->client, 0x0225, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][2]); + gc8613_write_reg(gc8613->client, 0x1467, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][3]); + gc8613_write_reg(gc8613->client, 0x1468, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][4]); + + gc8613_write_reg(gc8613->client, 0x1447, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][7]); + + gc8613_write_reg(gc8613->client, 0x00b8, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][8]); + gc8613_write_reg(gc8613->client, 0x00b9, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][9]); + + gc8613_write_reg(gc8613->client, 0x0064, + GC8613_REG_VALUE_08BIT, (tol_dig_gain >> 6)); + gc8613_write_reg(gc8613->client, 0x0065, + GC8613_REG_VALUE_08BIT, (tol_dig_gain & 0x3f)); +// dev_err(&gc8613->client->dev,"gc8613_set_gain_reg 12bit bbp:%u\n",mode->bpp); + + } +//#endif + + return 0; + +} + +static int gc8613_enable_test_pattern(struct gc8613 *gc8613, u32 pattern) +{ + return 0; +} + +static int gc8613_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc8613 *gc8613 = container_of(ctrl->handler, + struct gc8613, ctrl_handler); + struct i2c_client *client = gc8613->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 = gc8613->cur_mode->height + ctrl->val - 8; + __v4l2_ctrl_modify_range(gc8613->exposure, + gc8613->exposure->minimum, + max, + gc8613->exposure->step, + gc8613->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 = gc8613_write_reg(gc8613->client, GC8613_REG_EXPOSURE_H, + GC8613_REG_VALUE_08BIT, + ctrl->val >> 8); + ret |= gc8613_write_reg(gc8613->client, GC8613_REG_EXPOSURE_L, + GC8613_REG_VALUE_08BIT, + ctrl->val & 0xff); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = gc8613_set_gain_reg(gc8613, ctrl->val); + break; + case V4L2_CID_VBLANK: + gc8613->cur_vts = ctrl->val + gc8613->cur_mode->height; + ret = gc8613_write_reg(gc8613->client, GC8613_REG_VTS_H, + GC8613_REG_VALUE_08BIT, + gc8613->cur_vts >> 8); + ret |= gc8613_write_reg(gc8613->client, GC8613_REG_VTS_L, + GC8613_REG_VALUE_08BIT, + gc8613->cur_vts & 0xff); + break; + case V4L2_CID_TEST_PATTERN: + ret = gc8613_enable_test_pattern(gc8613, ctrl->val); + break; + case V4L2_CID_HFLIP: + + if (ctrl->val) + gc8613->flip |= GC8613_MIRROR_BIT_MASK; + else + gc8613->flip &= ~ GC8613_MIRROR_BIT_MASK; + + switch (gc8613->flip) { + case 0: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 0); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 0); + break; + case 1: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 5); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 0); + break; + case 2: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 2); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 1); + break; + case 3: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 7); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 1); + break; + + default: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 0); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 0); + break; + } + + break; + case V4L2_CID_VFLIP: + if (ctrl->val) + gc8613->flip |= GC8613_FLIP_BIT_MASK; + else + gc8613->flip &= ~ GC8613_FLIP_BIT_MASK; + + switch (gc8613->flip) { + case 0: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 0); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 0); + break; + case 1: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 5); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 0); + break; + case 2: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 2); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 1); + break; + case 3: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 7); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 1); + break; + + default: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 0); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 0); + break; + } + 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 gc8613_ctrl_ops = { + .s_ctrl = gc8613_set_ctrl, +}; + +static int gc8613_initialize_controls(struct gc8613 *gc8613) +{ + const struct gc8613_mode *mode; + struct v4l2_ctrl_handler *handler; + u64 pixel_rate; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &gc8613->ctrl_handler; + mode = gc8613->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 9); + if (ret) + return ret; + handler->lock = &gc8613->mutex; + + gc8613->link_freq = v4l2_ctrl_new_int_menu(handler, NULL, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_items) - 1, 0, + link_freq_items); + __v4l2_ctrl_s_ctrl(gc8613->link_freq, mode->mipi_freq_idx); + + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * GC8613_LANES ; + dev_err(&gc8613->client->dev, " gc8613_LINK_FREQ(%u) gc8613_bpp(%u) pixel_rate(%llu)\n", (u32)link_freq_items[mode->mipi_freq_idx],mode->bpp,pixel_rate); + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + + + gc8613->pixel_rate = v4l2_ctrl_new_std(handler, NULL, + V4L2_CID_PIXEL_RATE, 0, GC8613_MAX_PIXEL_RATE, + 1, pixel_rate); + + h_blank = mode->hts_def - mode->width; + gc8613->cur_vts = mode->vts_def; + gc8613->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (gc8613->hblank) + gc8613->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + gc8613->vblank = v4l2_ctrl_new_std(handler, &gc8613_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + GC8613_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 8; + gc8613->exposure = v4l2_ctrl_new_std(handler, &gc8613_ctrl_ops, + V4L2_CID_EXPOSURE, + GC8613_EXPOSURE_MIN, + exposure_max, + GC8613_EXPOSURE_STEP, + mode->exp_def); + + gc8613->anal_gain = v4l2_ctrl_new_std(handler, &gc8613_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + GC8613_GAIN_MIN, + GC8613_GAIN_MAX, + GC8613_GAIN_STEP, + GC8613_GAIN_DEFAULT); + + gc8613->test_pattern = + v4l2_ctrl_new_std_menu_items(handler, + &gc8613_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(gc8613_test_pattern_menu) - 1, + 0, 0, gc8613_test_pattern_menu); + + gc8613->flip = 0; + gc8613->h_flip = v4l2_ctrl_new_std(handler, &gc8613_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + gc8613->v_flip = v4l2_ctrl_new_std(handler, &gc8613_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (handler->error) { + ret = handler->error; + dev_err(&gc8613->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + gc8613->subdev.ctrl_handler = handler; + gc8613->has_init_exp = false; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 gc8613_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, GC8613_XVCLK_FREQ / 1000 / 1000); +} + +static int __gc8613_power_on(struct gc8613 *gc8613) +{ + int ret; + u32 delay_us; + struct device *dev = &gc8613->client->dev; + + if (!IS_ERR_OR_NULL(gc8613->pins_default)) { + ret = pinctrl_select_state(gc8613->pinctrl, + gc8613->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(gc8613->xvclk, GC8613_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (27MHz)\n"); + if (clk_get_rate(gc8613->xvclk) != GC8613_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 27MHz\n"); + ret = clk_prepare_enable(gc8613->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(gc8613->reset_gpio)) + gpiod_set_value_cansleep(gc8613->reset_gpio, 0); + + if (!IS_ERR(gc8613->pwdn_gpio)) + gpiod_set_value_cansleep(gc8613->pwdn_gpio, 0); + + usleep_range(500, 1000); + ret = regulator_bulk_enable(GC8613_NUM_SUPPLIES, gc8613->supplies); + + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(gc8613->pwren_gpio)) + gpiod_set_value_cansleep(gc8613->pwren_gpio, 1); + + usleep_range(1000, 1100); + if (!IS_ERR(gc8613->pwdn_gpio)) + gpiod_set_value_cansleep(gc8613->pwdn_gpio, 1); + usleep_range(100, 150); + if (!IS_ERR(gc8613->reset_gpio)) + gpiod_set_value_cansleep(gc8613->reset_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = gc8613_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(gc8613->xvclk); + + return ret; +} + +static int gc8613_check_sensor_id(struct gc8613 *gc8613, + struct i2c_client *client) +{ + struct device *dev = &gc8613->client->dev; + u16 id = 0; + u32 reg_H = 0; + u32 reg_L = 0; + int ret; + + ret = gc8613_read_reg(client, GC8613_REG_CHIP_ID_H, + GC8613_REG_VALUE_08BIT, ®_H); + ret |= gc8613_read_reg(client, GC8613_REG_CHIP_ID_L, + GC8613_REG_VALUE_08BIT, ®_L); + id = ((reg_H << 8) & 0xff00) | (reg_L & 0xff); + if (!(reg_H == (CHIP_ID >> 8) || reg_L == (CHIP_ID & 0xff))) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + dev_info(dev, "detected gc%04x sensor\n", id); + return 0; +} + +static void __gc8613_power_off(struct gc8613 *gc8613) +{ + int ret; + struct device *dev = &gc8613->client->dev; + + if (!IS_ERR(gc8613->pwdn_gpio)) + gpiod_set_value_cansleep(gc8613->pwdn_gpio, 0); + clk_disable_unprepare(gc8613->xvclk); + if (!IS_ERR(gc8613->reset_gpio)) + gpiod_set_value_cansleep(gc8613->reset_gpio, 0); + if (!IS_ERR_OR_NULL(gc8613->pins_sleep)) { + ret = pinctrl_select_state(gc8613->pinctrl, + gc8613->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + regulator_bulk_disable(GC8613_NUM_SUPPLIES, gc8613->supplies); + if (!IS_ERR(gc8613->pwren_gpio)) + gpiod_set_value_cansleep(gc8613->pwren_gpio, 0); +} + +static int gc8613_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct gc8613 *gc8613; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + u32 i,hdr_mode = 0; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + gc8613 = devm_kzalloc(dev, sizeof(*gc8613), GFP_KERNEL); + if (!gc8613) + return -ENOMEM; + + of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode); + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &gc8613->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &gc8613->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &gc8613->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &gc8613->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + gc8613->client = client; + gc8613->cfg_num = ARRAY_SIZE(supported_modes); + + for (i = 0; i < gc8613->cfg_num; i++) { + if (hdr_mode == supported_modes[i].hdr_mode) { + gc8613->cur_mode = &supported_modes[i]; + break; + } + } + if (i == gc8613->cfg_num) + gc8613->cur_mode = &supported_modes[0]; + + gc8613->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(gc8613->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + gc8613->pwren_gpio = devm_gpiod_get(dev, "pwren", GPIOD_OUT_LOW); + if (IS_ERR(gc8613->pwren_gpio)) + dev_warn(dev, "Failed to get pwren-gpios\n"); + + gc8613->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gc8613->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + gc8613->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(gc8613->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + gc8613->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(gc8613->pinctrl)) { + gc8613->pins_default = + pinctrl_lookup_state(gc8613->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(gc8613->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + gc8613->pins_sleep = + pinctrl_lookup_state(gc8613->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(gc8613->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } else { + dev_err(dev, "no pinctrl\n"); + } + + ret = gc8613_configure_regulators(gc8613); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&gc8613->mutex); + + sd = &gc8613->subdev; + v4l2_i2c_subdev_init(sd, client, &gc8613_subdev_ops); + ret = gc8613_initialize_controls(gc8613); + if (ret) + goto err_destroy_mutex; + + ret = __gc8613_power_on(gc8613); + if (ret) + goto err_free_handler; + + usleep_range(3000, 4000); + + ret = gc8613_check_sensor_id(gc8613, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &gc8613_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + gc8613->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &gc8613->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(gc8613->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + gc8613->module_index, facing, + GC8613_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(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); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __gc8613_power_off(gc8613); +err_free_handler: + v4l2_ctrl_handler_free(&gc8613->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&gc8613->mutex); + + return ret; +} + +static int gc8613_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc8613 *gc8613 = to_gc8613(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&gc8613->ctrl_handler); + mutex_destroy(&gc8613->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __gc8613_power_off(gc8613); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +static int gc8613_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc8613 *gc8613 = to_gc8613(sd); + + return __gc8613_power_on(gc8613); +} + +static int gc8613_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc8613 *gc8613 = to_gc8613(sd); + + __gc8613_power_off(gc8613); + + return 0; +} + +static const struct dev_pm_ops gc8613_pm_ops = { + SET_RUNTIME_PM_OPS(gc8613_runtime_suspend, + gc8613_runtime_resume, NULL) +}; + + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id gc8613_of_match[] = { + { .compatible = "galaxycore,gc8613" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gc8613_of_match); +#endif + +static const struct i2c_device_id gc8613_match_id[] = { + { "galaxycore,gc8613", 0 }, + { }, +}; + +static struct i2c_driver gc8613_i2c_driver = { + .driver = { + .name = GC8613_NAME, + .pm = &gc8613_pm_ops, + .of_match_table = of_match_ptr(gc8613_of_match), + }, + .probe = &gc8613_probe, + .remove = &gc8613_remove, + .id_table = gc8613_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&gc8613_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&gc8613_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("galaxycore gc8613 sensor driver"); +MODULE_LICENSE("GPL"); From d38d4d6336f48bf65d6568e052a6d727ebea34f6 Mon Sep 17 00:00:00 2001 From: ShingoXY <36981700+shingoxy@users.noreply.github.com> Date: Mon, 12 Aug 2024 11:02:34 +0800 Subject: [PATCH 11/13] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index bff4f375b8626..658a84229acc8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# 20240812:增加GC8613.c,成功点亮,工作正常,但无rkaiq文件; + # 20240710:RKAIQ正常工作,3AOK,但iqfile为copy os04a10的,颜色、LSC、AWB均不正常; ![image](https://github.com/shingoxy/kernel-RK3588s-5.10Stable/assets/36981700/d51b1c1e-4d61-4433-b83b-e777f5db479d) From 318242420e1d6b00b160bfc87ca27c35192721fd Mon Sep 17 00:00:00 2001 From: shingoxy <2877898@163.com> Date: Fri, 30 Aug 2024 10:40:07 +0800 Subject: [PATCH 12/13] =?UTF-8?q?=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20?= =?UTF-8?q?=20=20arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi?= =?UTF-8?q?=20=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20arch/arm64/conf?= =?UTF-8?q?igs/lubancat=5Flinux=5Frk3588=5Fdefconfig=20=09=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=EF=BC=9A=20=20=20=20=20arch/arm64/configs/rockchip=5F?= =?UTF-8?q?defconfig=20=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20arch/a?= =?UTF-8?q?rm64/configs/rockchip=5Flinux=5Fdefconfig=20=09=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=EF=BC=9A=20=20=20=20=20drivers/media/i2c/Kconfig=20?= =?UTF-8?q?=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20drivers/media/i2c/?= =?UTF-8?q?Makefile=20=09=E4=BF=AE=E6=94=B9=EF=BC=9A=20=20=20=20=20drivers?= =?UTF-8?q?/media/i2c/gc5603.c=20=09=E6=96=B0=E6=96=87=E4=BB=B6=EF=BC=9A?= =?UTF-8?q?=20=20=20drivers/media/i2c/ov50h40.c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Backup/README(4088).md | 161 + Linux-Kernel.si4project/Backup/gc5603(4678).c | 1613 +++++++ Linux-Kernel.si4project/Backup/gc5603(7712).c | 1612 +++++++ .../lubancat_linux_rk3588_defconfig(2041) | 816 ++++ .../Backup/rk3588s-lubancat-csi2(6413).dtsi | 590 +++ .../Backup/rk3588s-lubancat-csi2(6446).dtsi | 597 +++ Linux-Kernel.si4project/Linux-Kernel.sip_sym | Bin 0 -> 9512 bytes Linux-Kernel.si4project/Linux-Kernel.sip_xab | Bin 0 -> 4096 bytes Linux-Kernel.si4project/Linux-Kernel.sip_xad | Bin 0 -> 368 bytes Linux-Kernel.si4project/Linux-Kernel.sip_xc | Bin 0 -> 512 bytes Linux-Kernel.si4project/Linux-Kernel.sip_xf | Bin 0 -> 512 bytes Linux-Kernel.si4project/Linux-Kernel.sip_xm | Bin 0 -> 4 bytes Linux-Kernel.si4project/Linux-Kernel.sip_xr | Bin 0 -> 512 bytes Linux-Kernel.si4project/Linux-Kernel.sip_xsb | Bin 0 -> 4096 bytes Linux-Kernel.si4project/Linux-Kernel.sip_xsd | Bin 0 -> 368 bytes Linux-Kernel.si4project/Linux-Kernel.siproj | Bin 0 -> 840 bytes .../Linux-Kernel.siproj_settings.xml | 22 + .../dts/rockchip/rk3588s-lubancat-csi2.dtsi | 71 +- .../configs/lubancat_linux_rk3588_defconfig | 1 + arch/arm64/configs/rockchip_defconfig | 1 + arch/arm64/configs/rockchip_linux_defconfig | 1 + drivers/media/i2c/Kconfig | 11 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/gc5603.c | 123 +- drivers/media/i2c/imx415.c.bak | 3706 ++++++++++++++++ drivers/media/i2c/imx708.c | 2121 +++++++++ drivers/media/i2c/ov50h40.c | 3776 +++++++++++++++++ 27 files changed, 15176 insertions(+), 47 deletions(-) create mode 100755 Linux-Kernel.si4project/Backup/README(4088).md create mode 100755 Linux-Kernel.si4project/Backup/gc5603(4678).c create mode 100755 Linux-Kernel.si4project/Backup/gc5603(7712).c create mode 100755 Linux-Kernel.si4project/Backup/lubancat_linux_rk3588_defconfig(2041) create mode 100755 Linux-Kernel.si4project/Backup/rk3588s-lubancat-csi2(6413).dtsi create mode 100755 Linux-Kernel.si4project/Backup/rk3588s-lubancat-csi2(6446).dtsi create mode 100755 Linux-Kernel.si4project/Linux-Kernel.sip_sym create mode 100755 Linux-Kernel.si4project/Linux-Kernel.sip_xab create mode 100755 Linux-Kernel.si4project/Linux-Kernel.sip_xad create mode 100755 Linux-Kernel.si4project/Linux-Kernel.sip_xc create mode 100755 Linux-Kernel.si4project/Linux-Kernel.sip_xf create mode 100755 Linux-Kernel.si4project/Linux-Kernel.sip_xm create mode 100755 Linux-Kernel.si4project/Linux-Kernel.sip_xr create mode 100755 Linux-Kernel.si4project/Linux-Kernel.sip_xsb create mode 100755 Linux-Kernel.si4project/Linux-Kernel.sip_xsd create mode 100755 Linux-Kernel.si4project/Linux-Kernel.siproj create mode 100755 Linux-Kernel.si4project/Linux-Kernel.siproj_settings.xml create mode 100644 drivers/media/i2c/imx415.c.bak create mode 100644 drivers/media/i2c/imx708.c create mode 100644 drivers/media/i2c/ov50h40.c diff --git a/Linux-Kernel.si4project/Backup/README(4088).md b/Linux-Kernel.si4project/Backup/README(4088).md new file mode 100755 index 0000000000000..a550875bee241 --- /dev/null +++ b/Linux-Kernel.si4project/Backup/README(4088).md @@ -0,0 +1,161 @@ +# 增加GC5603.c驱动并修改对应Kernel文件: +1. drivers/media/i2c/增加GC5603.c文件; +2. arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi修改Device tree; +3. arch/arm64/configs/lubancat_linux_rk3588_defconfig修改defconfig +4. drivers/media/i2c/Kconfig增加gc5603.c编译 +5. drivers/media/i2c/Makefile增加gc5603.c编译 +6. drivers/phy/rockchip/phy-rockchip-csi2-dphy.c修改后增加cphy支持 + +以上修改已经编译通过(2024年6月),将进行点亮验证。 + + +# How do I submit patches to Android Common Kernels + +1. BEST: Make all of your changes to upstream Linux. If appropriate, backport to the stable releases. + These patches will be merged automatically in the corresponding common kernels. If the patch is already + in upstream Linux, post a backport of the patch that conforms to the patch requirements below. + - Do not send patches upstream that contain only symbol exports. To be considered for upstream Linux, +additions of `EXPORT_SYMBOL_GPL()` require an in-tree modular driver that uses the symbol -- so include +the new driver or changes to an existing driver in the same patchset as the export. + - When sending patches upstream, the commit message must contain a clear case for why the patch +is needed and beneficial to the community. Enabling out-of-tree drivers or functionality is not +not a persuasive case. + +2. LESS GOOD: Develop your patches out-of-tree (from an upstream Linux point-of-view). Unless these are + fixing an Android-specific bug, these are very unlikely to be accepted unless they have been + coordinated with kernel-team@android.com. If you want to proceed, post a patch that conforms to the + patch requirements below. + +# Common Kernel patch requirements + +- All patches must conform to the Linux kernel coding standards and pass `script/checkpatch.pl` +- Patches shall not break gki_defconfig or allmodconfig builds for arm, arm64, x86, x86_64 architectures +(see https://source.android.com/setup/build/building-kernels) +- If the patch is not merged from an upstream branch, the subject must be tagged with the type of patch: +`UPSTREAM:`, `BACKPORT:`, `FROMGIT:`, `FROMLIST:`, or `ANDROID:`. +- All patches must have a `Change-Id:` tag (see https://gerrit-review.googlesource.com/Documentation/user-changeid.html) +- If an Android bug has been assigned, there must be a `Bug:` tag. +- All patches must have a `Signed-off-by:` tag by the author and the submitter + +Additional requirements are listed below based on patch type + +## Requirements for backports from mainline Linux: `UPSTREAM:`, `BACKPORT:` + +- If the patch is a cherry-pick from Linux mainline with no changes at all + - tag the patch subject with `UPSTREAM:`. + - add upstream commit information with a `(cherry picked from commit ...)` line + - Example: + - if the upstream commit message is +``` + important patch from upstream + + This is the detailed description of the important patch + + Signed-off-by: Fred Jones +``` +>- then Joe Smith would upload the patch for the common kernel as +``` + UPSTREAM: important patch from upstream + + This is the detailed description of the important patch + + Signed-off-by: Fred Jones + + Bug: 135791357 + Change-Id: I4caaaa566ea080fa148c5e768bb1a0b6f7201c01 + (cherry picked from commit c31e73121f4c1ec41143423ac6ce3ce6dafdcec1) + Signed-off-by: Joe Smith +``` + +- If the patch requires any changes from the upstream version, tag the patch with `BACKPORT:` +instead of `UPSTREAM:`. + - use the same tags as `UPSTREAM:` + - add comments about the changes under the `(cherry picked from commit ...)` line + - Example: +``` + BACKPORT: important patch from upstream + + This is the detailed description of the important patch + + Signed-off-by: Fred Jones + + Bug: 135791357 + Change-Id: I4caaaa566ea080fa148c5e768bb1a0b6f7201c01 + (cherry picked from commit c31e73121f4c1ec41143423ac6ce3ce6dafdcec1) + [joe: Resolved minor conflict in drivers/foo/bar.c ] + Signed-off-by: Joe Smith +``` + +## Requirements for other backports: `FROMGIT:`, `FROMLIST:`, + +- If the patch has been merged into an upstream maintainer tree, but has not yet +been merged into Linux mainline + - tag the patch subject with `FROMGIT:` + - add info on where the patch came from as `(cherry picked from commit )`. This +must be a stable maintainer branch (not rebased, so don't use `linux-next` for example). + - if changes were required, use `BACKPORT: FROMGIT:` + - Example: + - if the commit message in the maintainer tree is +``` + important patch from upstream + + This is the detailed description of the important patch + + Signed-off-by: Fred Jones +``` +>- then Joe Smith would upload the patch for the common kernel as +``` + FROMGIT: important patch from upstream + + This is the detailed description of the important patch + + Signed-off-by: Fred Jones + + Bug: 135791357 + (cherry picked from commit 878a2fd9de10b03d11d2f622250285c7e63deace + https://git.kernel.org/pub/scm/linux/kernel/git/foo/bar.git test-branch) + Change-Id: I4caaaa566ea080fa148c5e768bb1a0b6f7201c01 + Signed-off-by: Joe Smith +``` + + +- If the patch has been submitted to LKML, but not accepted into any maintainer tree + - tag the patch subject with `FROMLIST:` + - add a `Link:` tag with a link to the submittal on lore.kernel.org + - add a `Bug:` tag with the Android bug (required for patches not accepted into +a maintainer tree) + - if changes were required, use `BACKPORT: FROMLIST:` + - Example: +``` + FROMLIST: important patch from upstream + + This is the detailed description of the important patch + + Signed-off-by: Fred Jones + + Bug: 135791357 + Link: https://lore.kernel.org/lkml/20190619171517.GA17557@someone.com/ + Change-Id: I4caaaa566ea080fa148c5e768bb1a0b6f7201c01 + Signed-off-by: Joe Smith +``` + +## Requirements for Android-specific patches: `ANDROID:` + +- If the patch is fixing a bug to Android-specific code + - tag the patch subject with `ANDROID:` + - add a `Fixes:` tag that cites the patch with the bug + - Example: +``` + ANDROID: fix android-specific bug in foobar.c + + This is the detailed description of the important fix + + Fixes: 1234abcd2468 ("foobar: add cool feature") + Change-Id: I4caaaa566ea080fa148c5e768bb1a0b6f7201c01 + Signed-off-by: Joe Smith +``` + +- If the patch is a new feature + - tag the patch subject with `ANDROID:` + - add a `Bug:` tag with the Android bug (required for android-specific features) + diff --git a/Linux-Kernel.si4project/Backup/gc5603(4678).c b/Linux-Kernel.si4project/Backup/gc5603(4678).c new file mode 100755 index 0000000000000..c6291906f5c01 --- /dev/null +++ b/Linux-Kernel.si4project/Backup/gc5603(4678).c @@ -0,0 +1,1613 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * gc5603 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + * V0.0X01.0X03 add enum_frame_interval function. + * V0.0X01.0X04 add quick stream on/off + * V0.0X01.0X05 add function g_mbus_config + * V0.0X01.0X06 + * 1. add 2lane support. + * 2. add some debug info. + * 3. adjust gc5603_g_mbus_config function. + * V0.0X01.0X07 support get channel info + * V0.0X01.0X08 + * 1. default support 2lane full 30fps. + * 2. default support rk otp spec. + * V0.0X01.0X09 adjust supply sequence to suit spec + */ +//#define DEBUG +#include +#include +#include +#include +#include +#include +#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) +#define GC5603_NAME "gc5603" +#define GC5603_MEDIA_BUS_FMT MEDIA_BUS_FMT_SRGGB10_1X10 + +#define MIPI_FREQ_848M 423000000 + +#define GC5603_PAGE_SELECT 0xFE + +#define GC5603_REG_CHIP_ID_H 0x03F0 +#define GC5603_REG_CHIP_ID_L 0x03F1 + +#define GC5603_REG_EXP_H 0x0202 +#define GC5603_REG_EXP_L 0x0203 + +#define GC5603_REG_VTS_H 0x0340 +#define GC5603_REG_VTS_L 0x0341 + +#define GC5603_REG_CTRL_MODE 0x0100 +#define GC5603_MODE_SW_STANDBY 0x00 +#define GC5603_MODE_STREAMING 0x09 + +#define REG_NULL 0xFFFF + +#define GC5603_CHIP_ID 0x5603 + +#define GC5603_VTS_MAX 0x7fff +#define GC5603_HTS_MAX 0xFFF + +#define GC5603_EXPOSURE_MAX 0x3FFF +#define GC5603_EXPOSURE_MIN 1 +#define GC5603_EXPOSURE_STEP 1 + +#define GC5603_GAIN_MIN 64 +#define GC5603_GAIN_MAX 0xffff +#define GC5603_GAIN_STEP 1 +#define GC5603_GAIN_DEFAULT 64 + +#define gc5603_REG_VALUE_08BIT 1 +#define gc5603_REG_VALUE_16BIT 2 +#define gc5603_REG_VALUE_24BIT 3 + + +#define GC5603_LANES 2 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + + + +#define GC5603_FLIP_MIRROR_REG 0x0101 + +#define GC_MIRROR_BIT_MASK BIT(0) +#define GC_FLIP_BIT_MASK BIT(1) + +#define GC5603_XVCLK_FREQ_24M 24000000 +#define GC5603_XVCLK_FREQ_27M 27000000 + +static const char * const gc5603_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + +#define GC5603_NUM_SUPPLIES ARRAY_SIZE(gc5603_supply_names) + +#define to_gc5603(sd) container_of(sd, struct gc5603, subdev) + +struct regval { + u16 addr; + u8 val; +}; + +struct gc5603_mode { + u32 bus_fmt + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; + u32 hdr_mode; + u32 vc[PAD_MAX]; + u32 xvclk; +}; + +struct gc5603 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct gpio_desc *pwren_gpio; + struct regulator_bulk_data supplies[GC5603_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 *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *h_flip; + struct v4l2_ctrl *v_flip; + struct mutex mutex; + bool streaming; + bool power_on; + const struct gc5603_mode *cur_mode; + unsigned int lane_num; + unsigned int cfg_num; + unsigned int pixel_rate; + + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + struct rkmodule_awb_cfg awb_cfg; + struct rkmodule_lsc_cfg lsc_cfg; + u32 flip; +}; + + +static const struct regval gc5603_2960x1666_regs_2lane[] = { +//version 1.3 +//mclk 27Mhz +//mipi 2 lane 846Mbps/lane +//vts = 1750 ,row_time=19.05us +//window 2960x1666 +//BGGR + {0x03fe, 0xf0}, + {0x03fe, 0x00}, + {0x03fe, 0x10}, + {0x03fe, 0x00}, + {0x0202, 0x01}, + {0x0203, 0x50}, + {0x0a38, 0x02}, + {0x0a38, 0x03}, + {0x0a20, 0x07}, + {0x061b, 0x03}, + {0x061c, 0x50}, + {0x061d, 0x05}, + {0x061e, 0x70}, + {0x061f, 0x03}, + {0x0a21, 0x08}, + {0x0a34, 0x40}, + {0x0a35, 0x11}, + {0x0a36, 0x5e}, + {0x0a37, 0x03}, + {0x0314, 0x50}, + {0x0315, 0x32}, + {0x031c, 0xce}, + {0x0219, 0x47}, + {0x0342, 0x04}, + {0x0343, 0xb0}, + {0x0340, 0x06}, + {0x0341, 0xd6}, + {0x0345, 0x02}, + {0x0347, 0x02}, + {0x0348, 0x0b}, + {0x0349, 0x98}, + {0x034a, 0x06}, + {0x034b, 0x8a}, + {0x0094, 0x0b}, + {0x0095, 0x90}, + {0x0096, 0x06}, + {0x0097, 0x82}, + {0x0099, 0x04}, + {0x009b, 0x04}, + {0x060c, 0x01}, + {0x060e, 0xd2}, + {0x060f, 0x05}, + {0x070c, 0x01}, + {0x070e, 0xd2}, + {0x070f, 0x05}, + {0x0909, 0x07}, + {0x0902, 0x04}, + {0x0904, 0x0b}, + {0x0907, 0x54}, + {0x0908, 0x06}, + {0x0903, 0x9d}, + {0x072a, 0x1c},//18 + {0x072b, 0x1c},//18 + {0x0724, 0x2b}, + {0x0727, 0x2b}, + {0x1466, 0x18}, + {0x1467, 0x08}, + {0x1468, 0x10}, + {0x1469, 0x80}, + {0x146a, 0xe8},//b8 + {0x1412, 0x20}, + {0x0707, 0x07}, + {0x0737, 0x0f}, + {0x0704, 0x01}, + {0x0706, 0x03}, + {0x0716, 0x03}, + {0x0708, 0xc8}, + {0x0718, 0xc8}, + {0x061a, 0x02}, + {0x1430, 0x80}, + {0x1407, 0x10}, + {0x1408, 0x16}, + {0x1409, 0x03}, + {0x1438, 0x01}, + {0x02ce, 0x03}, + {0x0245, 0xc9}, + {0x023a, 0x08},//3B + {0x02cd, 0x88}, + {0x0612, 0x02}, + {0x0613, 0xc7}, + {0x0243, 0x03},//06 + {0x0089, 0x03}, + {0x0002, 0xab}, + {0x0040, 0xa3}, + {0x0075, 0x64},//64 + {0x0004, 0x0f}, + {0x0053, 0x0a}, + {0x0205, 0x0c}, + + //auto_load}, + {0x0a67, 0x80}, + {0x0a54, 0x0e}, + {0x0a65, 0x10}, + {0x0a98, 0x04}, + {0x05be, 0x00}, + {0x05a9, 0x01}, + {0x0023, 0x00}, + {0x0022, 0x00}, + {0x0025, 0x00}, + {0x0024, 0x00}, + {0x0028, 0x0b}, + {0x0029, 0x98}, + {0x002a, 0x06}, + {0x002b, 0x86}, + {0x0a83, 0xe0}, + {0x0a72, 0x02}, + {0x0a73, 0x60}, + {0x0a75, 0x41}, + {0x0a70, 0x03}, + {0x0a5a, 0x80}, + {0x0181, 0x30}, + {0x0182, 0x05}, + {0x0185, 0x01}, + {0x0180, 0x46}, + {0x0100, 0x08}, + {0x010d, 0x74}, + {0x010e, 0x0e}, + {0x0113, 0x02}, + {0x0114, 0x01}, + {0x0115, 0x10}, + //{0x0a70, 0x00}, + //{0x0080, 0x02}, + //{0x0a67, 0x00}, + {0x0052, 0x02}, + {0x0076, 0x01}, + {0x021a, 0x10}, + {0x0049, 0x0f}, + {0x004a, 0x3c}, + {0x004b, 0x00}, + {0x0430, 0x25}, + {0x0431, 0x25}, + {0x0432, 0x25}, + {0x0433, 0x25}, + {0x0434, 0x59}, + {0x0435, 0x59}, + {0x0436, 0x59}, + {0x0437, 0x59}, + + {0x0100, 0x09}, + {REG_NULL, 0x00}, +}; + +static const struct gc5603_mode supported_modes[] = { + { + .bus_fmt = GC5603_MEDIA_BUS_FMT + .width = 2960, + .height = 1666, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x6ce, + .hts_def = 0x0C80, + .vts_def = 0x06D6, + .reg_list = gc5603_2960x1666_regs_2lane, + .hdr_mode = NO_HDR, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = GC5603_XVCLK_FREQ_24M + }, +}; + +static const s64 link_freq_menu_items[] = { + MIPI_FREQ_848M +}; +static int gc5603_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; + + 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 gc5603_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 = gc5603_write_reg(client, regs[i].addr, + gc5603_REG_VALUE_08BIT, regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int gc5603_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; + dev_info(&client->dev, + "gc5603 read reg(0x%x val:0x%x) \n", reg, *val); +} +static int gc5603_get_reso_dist(const struct gc5603_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct gc5603_mode * +gc5603_find_best_fit(struct gc5603 *gc5603, 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 < gc5603->cfg_num; i++) { + dist = gc5603_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 uint8_t regValTable[26][7] = { + //0614, 0615, 0225, 1467 1468, 00b8, 00b9 + { 0x00, 0x00, 0x04, 0x15, 0x15, 0x01, 0x00}, + { 0x90, 0x02, 0x04, 0x15, 0x15, 0x01, 0x0A}, + { 0x00, 0x00, 0x00, 0x15, 0x15, 0x01, 0x12}, + { 0x90, 0x02, 0x00, 0x15, 0x15, 0x01, 0x20}, + { 0x01, 0x00, 0x00, 0x15, 0x15, 0x01, 0x30}, + { 0x91, 0x02, 0x00, 0x15, 0x15, 0x02, 0x05}, + { 0x02, 0x00, 0x00, 0x15, 0x15, 0x02, 0x19}, + { 0x92, 0x02, 0x00, 0x16, 0x16, 0x02, 0x3F}, + { 0x03, 0x00, 0x00, 0x16, 0x16, 0x03, 0x20}, + { 0x93, 0x02, 0x00, 0x17, 0x17, 0x04, 0x0A}, + { 0x00, 0x00, 0x01, 0x18, 0x18, 0x05, 0x02}, + { 0x90, 0x02, 0x01, 0x19, 0x19, 0x05, 0x39}, + { 0x01, 0x00, 0x01, 0x19, 0x19, 0x06, 0x3C}, + { 0x91, 0x02, 0x01, 0x19, 0x19, 0x08, 0x0D}, + { 0x02, 0x00, 0x01, 0x1a, 0x1a, 0x09, 0x21}, + { 0x92, 0x02, 0x01, 0x1a, 0x1a, 0x0B, 0x0F}, + { 0x03, 0x00, 0x01, 0x1c, 0x1c, 0x0D, 0x17}, + { 0x93, 0x02, 0x01, 0x1c, 0x1c, 0x0F, 0x33}, + { 0x04, 0x00, 0x01, 0x1d, 0x1d, 0x12, 0x30}, + { 0x94, 0x02, 0x01, 0x1d, 0x1d, 0x16, 0x10}, + { 0x05, 0x00, 0x01, 0x1e, 0x1e, 0x1A, 0x19}, + { 0x95, 0x02, 0x01, 0x1e, 0x1e, 0x1F, 0x13}, + { 0x06, 0x00, 0x01, 0x20, 0x20, 0x25, 0x08}, + { 0x96, 0x02, 0x01, 0x20, 0x20, 0x2C, 0x03}, + { 0xb6, 0x04, 0x01, 0x20, 0x20, 0x34, 0x0F}, + { 0x86, 0x06, 0x01, 0x20, 0x20, 0x3D, 0x3D}, +}; + +static uint32_t gain_level_table[27] = { + 64, + 74, + 82, + 96, + 112, + 133, + 153, + 191, + 224, + 266, + 322, + 377, + 444, + 525, + 609, + 719, + 855, + 1011, + 1200, + 1424, + 1689, + 2003, + + 2376, + 2819, + + 3343, + 3965, + 0xffffffff, +}; +//static int total = sizeof(gain_level_table) / sizeof(uint32_t); + +static int gc5603_set_gain(struct gc5603 *gc5603, u32 gain) +{ + int ret; + uint16_t i = 0; + uint16_t total = 0; + uint16_t temp = 0; + + + for (i = 0; i < total; i++) { + if ((gain_level_table[i] <= gain) && (gain < gain_level_table[i+1])) + break; + } + + if((gain>3965)||(gain==3965)) + i =25; + + + ret = gc5603_write_reg(gc5603->client, 0x031d,gc5603_REG_VALUE_08BIT, 0x2d); + ret = gc5603_write_reg(gc5603->client, 0x0614,gc5603_REG_VALUE_08BIT,regValTable[i][0]); + ret = gc5603_write_reg(gc5603->client, 0x0615,gc5603_REG_VALUE_08BIT,regValTable[i][1]); + ret = gc5603_write_reg(gc5603->client, 0x0225,gc5603_REG_VALUE_08BIT,regValTable[i][2]); + + ret = gc5603_write_reg(gc5603->client, 0x031d,gc5603_REG_VALUE_08BIT, 0x28); + ret = gc5603_write_reg(gc5603->client, 0x1467,gc5603_REG_VALUE_08BIT,regValTable[i][3]); + ret = gc5603_write_reg(gc5603->client, 0x1468,gc5603_REG_VALUE_08BIT,regValTable[i][4]); + ret = gc5603_write_reg(gc5603->client, 0x00b8,gc5603_REG_VALUE_08BIT,regValTable[i][5]); + ret = gc5603_write_reg(gc5603->client, 0x00b9,gc5603_REG_VALUE_08BIT,regValTable[i][6]); + + + temp = 64 * gain / gain_level_table[i]; + + //dev_warn(&client->dev, "gc5603_set_gain gain=%d,i=%d,temp=%d \n", gain, i, temp); + + ret |= gc5603_write_reg(gc5603->client, 0x0064,gc5603_REG_VALUE_08BIT,(temp >> 6)); + ret |= gc5603_write_reg(gc5603->client, 0x0065,gc5603_REG_VALUE_08BIT,((temp&0x3f) << 2) ); + + return ret; +} + +static int gc5603_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc5603 *gc5603 = container_of(ctrl->handler, + struct gc5603, ctrl_handler); + struct i2c_client *client = gc5603->client; + s64 max; + int ret = 0; + u32 vts = 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 = gc5603->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(gc5603->exposure, + gc5603->exposure->minimum, max, + gc5603->exposure->step, + gc5603->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + + #if 1 + ret = gc5603_write_reg(gc5603->client, GC5603_REG_EXP_H,gc5603_REG_VALUE_08BIT, + (ctrl->val >> 8)); + ret |= gc5603_write_reg(gc5603->client, GC5603_REG_EXP_L,gc5603_REG_VALUE_08BIT, + ctrl->val & 0xff); + #endif + + break; + case V4L2_CID_ANALOGUE_GAIN: + gc5603_set_gain(gc5603, ctrl->val); + break; + case V4L2_CID_VBLANK: + vts = ctrl->val + gc5603->cur_mode->height; + + #if 1 + ret = gc5603_write_reg(gc5603->client, GC5603_REG_VTS_H,gc5603_REG_VALUE_08BIT,(vts >> 8)); + ret |= gc5603_write_reg(gc5603->client, GC5603_REG_VTS_L, gc5603_REG_VALUE_08BIT,vts & 0xff); + #endif + break; + case V4L2_CID_HFLIP: + if (ctrl->val) + gc5603->flip |= GC_MIRROR_BIT_MASK; + else + gc5603->flip &= ~GC_MIRROR_BIT_MASK; + break; + case V4L2_CID_VFLIP: + if (ctrl->val) + gc5603->flip |= GC_FLIP_BIT_MASK; + else + gc5603->flip &= ~GC_FLIP_BIT_MASK; + 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 gc5603_ctrl_ops = { + .s_ctrl = gc5603_set_ctrl, +}; + +static int gc5603_configure_regulators(struct gc5603 *gc5603) +{ + unsigned int i; + + for (i = 0; i < GC5603_NUM_SUPPLIES; i++) + gc5603->supplies[i].supply = gc5603_supply_names[i]; + + return devm_regulator_bulk_get(&gc5603->client->dev, + GC5603_NUM_SUPPLIES, + gc5603->supplies); +} + +static int gc5603_parse_of(struct gc5603 *gc5603) +{ + struct device *dev = &gc5603->client->dev; + struct device_node *endpoint; + struct fwnode_handle *fwnode; + int rval; + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "Failed to get endpoint\n"); + return -EINVAL; + } + fwnode = of_fwnode_handle(endpoint); + rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0); + if (rval <= 0) { + dev_warn(dev, " Get mipi lane num failed!\n"); + return -1; + } + + gc5603->lane_num = rval; + if (2 == gc5603->lane_num) { + gc5603->cur_mode = &supported_modes[0]; + gc5603->cfg_num = ARRAY_SIZE(supported_modes); + + /*pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + gc5603->pixel_rate = MIPI_FREQ_848M * 2U * (gc5603->lane_num) / 10U; + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + gc5603->lane_num, gc5603->pixel_rate); + } else { + dev_info(dev, "gc5603 can not support the lane num(%d)\n", gc5603->lane_num); + } + return 0; +} + +static int gc5603_initialize_controls(struct gc5603 *gc5603) +{ + const struct gc5603_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &gc5603->ctrl_handler; + mode = gc5603->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &gc5603->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, gc5603->pixel_rate, 1, gc5603->pixel_rate); + + h_blank = mode->hts_def - mode->width; + gc5603->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (gc5603->hblank) + gc5603->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + gc5603->vblank = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + GC5603_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + gc5603->exposure = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_EXPOSURE, + GC5603_EXPOSURE_MIN, + exposure_max, + GC5603_EXPOSURE_STEP, + mode->exp_def); + + gc5603->anal_gain = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + GC5603_GAIN_MIN, + GC5603_GAIN_MAX, + GC5603_GAIN_STEP, + GC5603_GAIN_DEFAULT); + + gc5603->h_flip = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + gc5603->v_flip = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + gc5603->flip = 0; + + if (handler->error) { + ret = handler->error; + dev_err(&gc5603->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + gc5603->subdev.ctrl_handler = handler; + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 gc5603_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, GC5603_XVCLK_FREQ_24M / 1000 / 1000); +} + +static int __gc5603_power_on(struct gc5603 *gc5603) +{ + int ret; + u32 delay_us; + struct device *dev = &gc5603->client->dev; + + if (!IS_ERR_OR_NULL(gc5603->pins_default)) { + ret = pinctrl_select_state(gc5603->pinctrl, + gc5603->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + ret = clk_set_rate(gc5603->xvclk, GC5603_XVCLK_FREQ_24M); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(gc5603->xvclk) != GC5603_XVCLK_FREQ_24M) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(gc5603->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 0); + + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 0); + + usleep_range(500, 1000); + ret = regulator_bulk_enable(GC5603_NUM_SUPPLIES, gc5603->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + if (!IS_ERR(gc5603->pwren_gpio)) + gpiod_set_value_cansleep(gc5603->pwren_gpio, 1); + + usleep_range(1000, 1100); + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 1); + usleep_range(100, 150); + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = gc5603_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + return 0; + +disable_clk: + clk_disable_unprepare(gc5603->xvclk); + return ret; +} + +static void __gc5603_power_off(struct gc5603 *gc5603) +{ + int ret; + struct device *dev = &gc5603->client->dev; + + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 0); + clk_disable_unprepare(gc5603->xvclk); + + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 0); + + if (!IS_ERR_OR_NULL(gc5603->pins_sleep)) { + ret = pinctrl_select_state(gc5603->pinctrl, + gc5603->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + regulator_bulk_disable(GC5603_NUM_SUPPLIES, gc5603->supplies); + if (!IS_ERR(gc5603->pwren_gpio)) + gpiod_set_value_cansleep(gc5603->pwren_gpio, 0); + +} + + static int gc5603_check_sensor_id(struct gc5603 *gc5603, + struct i2c_client *client) +{ + struct device *dev = &gc5603->client->dev; + u16 id = 0; + u32 reg_H = 0; + u32 reg_L = 0; + int ret; + + ret = gc5603_read_reg(client, GC5603_REG_CHIP_ID_H, + gc5603_REG_VALUE_08BIT, ®_H); + ret |= gc5603_read_reg(client, GC5603_REG_CHIP_ID_L, + gc5603_REG_VALUE_08BIT, ®_L); + + id = ((reg_H << 8) & 0xff00) | (reg_L & 0xff); + if (!(reg_H == (GC5603_CHIP_ID >> 8) || reg_L == (GC5603_CHIP_ID & 0xff))) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + dev_info(dev, "detected gc%04x sensor\n", id); + return 0; +} + +static int gc5603_set_flip(struct gc5603 *gc5603, u8 mode) +{ +u32 match_reg = 0; + + gc5603_read_reg(gc5603->client, GC5603_FLIP_MIRROR_REG,gc5603_REG_VALUE_08BIT,&match_reg); + + if (mode == GC_FLIP_BIT_MASK) { + match_reg |= GC_FLIP_BIT_MASK; + match_reg &= ~GC_MIRROR_BIT_MASK; + } else if (mode == GC_MIRROR_BIT_MASK) { + match_reg |= GC_MIRROR_BIT_MASK; + match_reg &= ~GC_FLIP_BIT_MASK; + } else if (mode == (GC_MIRROR_BIT_MASK | + GC_FLIP_BIT_MASK)) { + match_reg |= GC_FLIP_BIT_MASK; + match_reg |= GC_MIRROR_BIT_MASK; + } else { + match_reg &= ~GC_FLIP_BIT_MASK; + match_reg &= ~GC_MIRROR_BIT_MASK; + } + return gc5603_write_reg(gc5603->client, GC5603_FLIP_MIRROR_REG,gc5603_REG_VALUE_08BIT ,match_reg); +} + +static int __gc5603_start_stream(struct gc5603 *gc5603) +{ + int ret; + + ret = gc5603_write_array(gc5603->client, gc5603->cur_mode->reg_list); + if (ret) + return ret; + + usleep_range(1000, 1100); + + + gc5603_write_reg(gc5603->client, 0x0a70,gc5603_REG_VALUE_08BIT, 0x00); + gc5603_write_reg(gc5603->client, 0x0080,gc5603_REG_VALUE_08BIT, 0x02); + gc5603_write_reg(gc5603->client, 0x0a67,gc5603_REG_VALUE_08BIT, 0x00); + + + + + /* In case these controls are set before streaming */ + mutex_unlock(&gc5603->mutex); + ret = __v4l2_ctrl_handler_setup(&gc5603->ctrl_handler); + mutex_lock(&gc5603->mutex); + + ret = gc5603_set_flip(gc5603, gc5603->flip); + if (ret) + return ret; + return gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_STREAMING); +} + +static int __gc5603_stop_stream(struct gc5603 *gc5603) +{ + return gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_SW_STANDBY); +} + +static void gc5603_get_module_inf(struct gc5603 *gc5603, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, GC5603_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, gc5603->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, gc5603->len_name, sizeof(inf->base.lens)); +} + +static int gc5603_get_channel_info(struct gc5603 *gc5603, struct rkmodule_channel_info *ch_info) +{ + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + ch_info->vc = gc5603->cur_mode->vc[ch_info->index]; + ch_info->width = gc5603->cur_mode->width; + ch_info->height = gc5603->cur_mode->height; + ch_info->bus_fmt = gc5603->cur_mode->bus_fmt; + return 0; +} + +static void gc5603_set_awb_cfg(struct gc5603 *gc5603, + struct rkmodule_awb_cfg *cfg) +{ + mutex_lock(&gc5603->mutex); + memcpy(&gc5603->awb_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&gc5603->mutex); +} + +static void gc5603_set_lsc_cfg(struct gc5603 *gc5603, + struct rkmodule_lsc_cfg *cfg) +{ + mutex_lock(&gc5603->mutex); + memcpy(&gc5603->lsc_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&gc5603->mutex); +} + +static long gc5603_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + long ret = 0; + struct rkmodule_hdr_cfg *hdr_cfg; + u32 stream = 0; + struct rkmodule_channel_info *ch_info; + + switch (cmd) { + case RKMODULE_GET_HDR_CFG: + hdr_cfg = (struct rkmodule_hdr_cfg *)arg; + hdr_cfg->esp.mode = HDR_NORMAL_VC; + hdr_cfg->hdr_mode = gc5603->cur_mode->hdr_mode; + break; + case RKMODULE_SET_HDR_CFG: + case RKMODULE_SET_CONVERSION_GAIN: + break; + case RKMODULE_GET_MODULE_INFO: + gc5603_get_module_inf(gc5603, (struct rkmodule_inf *)arg); + break; + case RKMODULE_AWB_CFG: + gc5603_set_awb_cfg(gc5603, (struct rkmodule_awb_cfg *)arg); + break; + case RKMODULE_LSC_CFG: + gc5603_set_lsc_cfg(gc5603, (struct rkmodule_lsc_cfg *)arg); + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_STREAMING); + else + ret = gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_SW_STANDBY); + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = gc5603_get_channel_info(gc5603, ch_info); + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} + +#ifdef CONFIG_COMPAT +static long gc5603_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 *awb_cfg; + struct rkmodule_lsc_cfg *lsc_cfg; + struct rkmodule_hdr_cfg *hdr; + long ret = 0; + u32 cg = 0; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = gc5603_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + awb_cfg = kzalloc(sizeof(*awb_cfg), GFP_KERNEL); + if (!awb_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(awb_cfg, up, sizeof(*awb_cfg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, awb_cfg); + kfree(awb_cfg); + break; + case RKMODULE_LSC_CFG: + lsc_cfg = kzalloc(sizeof(*lsc_cfg), GFP_KERNEL); + if (!lsc_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(lsc_cfg, up, sizeof(*lsc_cfg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, lsc_cfg); + kfree(lsc_cfg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = gc5603_ioctl(sd, cmd, hdr); + if (!ret) + ret = copy_to_user(up, hdr, sizeof(*hdr)); + kfree(hdr); + break; + case RKMODULE_SET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(hdr, up, sizeof(*hdr)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, hdr); + kfree(hdr); + break; + case RKMODULE_SET_CONVERSION_GAIN: + ret = copy_from_user(&cg, up, sizeof(cg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, &cg); + break; + case RKMODULE_SET_QUICK_STREAM: + ret = copy_from_user(&stream, up, sizeof(u32)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, &stream); + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} +#endif + +static int gc5603_s_stream(struct v4l2_subdev *sd, int on) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct i2c_client *client = gc5603->client; + int ret = 0; + + mutex_lock(&gc5603->mutex); + on = !!on; + if (on == gc5603->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 = __gc5603_start_stream(gc5603); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __gc5603_stop_stream(gc5603); + pm_runtime_put(&client->dev); + } + + gc5603->streaming = on; + +unlock_and_return: + mutex_unlock(&gc5603->mutex); + return 0; +} + +static int gc5603_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + + mutex_lock(&gc5603->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&gc5603->mutex); + + return 0; +} + +static int gc5603_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + u32 val = 0; + + if (mode->hdr_mode == NO_HDR) + val = 1 << (GC5603_LANES - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + config->type = V4L2_MBUS_CSI2_DPHY; + config->flags = val; + return 0; +} + +static int gc5603_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = gc5603->supported_modes[code->index].bus_fmt; + return 0; +} + +static int gc5603_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + + if (fse->index >= gc5603->cfg_num) + return -EINVAL; + + if (fse->code != gc5603->supported_modes[code->index].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; +} + +#define DST_WIDTH 2720 +#define DST_HEIGHT 1616 + +/* + * The resolution of the driver configuration needs to be exactly + * the same as the current output resolution of the sensor, + * the input width of the isp needs to be 16 aligned, + * the input height of the isp needs to be 8 aligned. + * Can be cropped to standard resolution by this function, + * otherwise it will crop out strange resolution according + * to the alignment rules. + */ +static int gc5603_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + sel->r.left =120; + sel->r.width = DST_WIDTH; + sel->r.top = 25; + sel->r.height = DST_HEIGHT; + return 0; + } + return -EINVAL; +} + +static int gc5603_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + + if (fie->index >= gc5603->cfg_num) + return -EINVAL; + + fie->code = supported_modes[fie->index].bus_fmt; + fie->width = supported_modes[fie->index].width; + fie->height = supported_modes[fie->index].height; + fie->interval = supported_modes[fie->index].max_fps; + fie->reserved[0] = supported_modes[fie->index].hdr_mode; + return 0; +} + +static int gc5603_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&gc5603->mutex); + + mode = gc5603_find_best_fit(gc5603, fmt); + fmt->format.code = mode->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, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&gc5603->mutex); + return -ENOTTY; +#endif + } else { + gc5603->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(gc5603->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(gc5603->vblank, vblank_def, + GC5603_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&gc5603->mutex); + return 0; +} + +static int gc5603_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + + mutex_lock(&gc5603->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&gc5603->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->bus_fmt; + fmt->format.field = V4L2_FIELD_NONE; + + /* format info: width/height/data type/virctual channel */ + if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR) + fmt->reserved[0] = mode->vc[fmt->pad]; + else + fmt->reserved[0] = mode->vc[PAD0]; + + } + mutex_unlock(&gc5603->mutex); + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int gc5603_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct gc5603_mode *def_mode = &supported_modes[0]; + + mutex_lock(&gc5603->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = GC5603_MEDIA_BUS_FMT; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&gc5603->mutex); + /* No crop or compose */ + return 0; +} +#endif + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops gc5603_internal_ops = { + .open = gc5603_open, +}; +#endif + +static int gc5603_s_power(struct v4l2_subdev *sd, int on) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct i2c_client *client = gc5603->client; + int ret = 0; + + mutex_lock(&gc5603->mutex); + + /* If the power state is not modified - no work to do. */ + if (gc5603->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; + } + + gc5603->power_on = true; + } else { + pm_runtime_put(&client->dev); + gc5603->power_on = false; + } + +unlock_and_return: + mutex_unlock(&gc5603->mutex); + + return ret; +} + +static const struct v4l2_subdev_core_ops gc5603_core_ops = { + .s_power = gc5603_s_power, + .ioctl = gc5603_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = gc5603_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops gc5603_video_ops = { + .s_stream = gc5603_s_stream, + .g_frame_interval = gc5603_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops gc5603_pad_ops = { + .enum_mbus_code = gc5603_enum_mbus_code, + .enum_frame_size = gc5603_enum_frame_sizes, + .enum_frame_interval = gc5603_enum_frame_interval, + .get_fmt = gc5603_get_fmt, + .set_fmt = gc5603_set_fmt, + .get_selection = gc5603_get_selection, + .get_mbus_config = gc5603_g_mbus_config, +}; + +static const struct v4l2_subdev_ops gc5603_subdev_ops = { + .core = &gc5603_core_ops, + .video = &gc5603_video_ops, + .pad = &gc5603_pad_ops, +}; + +static int gc5603_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + __gc5603_power_on(gc5603); + return 0; +} + +static int gc5603_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + __gc5603_power_off(gc5603); + return 0; +} + +static const struct dev_pm_ops gc5603_pm_ops = { + SET_RUNTIME_PM_OPS(gc5603_runtime_suspend, + gc5603_runtime_resume, NULL) +}; + +static int gc5603_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct gc5603 *gc5603; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + gc5603 = devm_kzalloc(dev, sizeof(*gc5603), GFP_KERNEL); + if (!gc5603) + return -ENOMEM; + + gc5603->client = client; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &gc5603->module_index); + if (ret) { + dev_warn(dev, "could not get module index!\n"); + gc5603->module_index = 0; + } + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &gc5603->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &gc5603->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &gc5603->len_name); + if (ret) { + dev_err(dev, + "could not get module information!\n"); + return -EINVAL; + } + + gc5603->xvclk = devm_clk_get(&client->dev, "xvclk"); + if (IS_ERR(gc5603->xvclk)) { + dev_err(&client->dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + gc5603->pwren_gpio = devm_gpiod_get(dev, "pwren", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->pwdn_gpio)) + dev_warn(dev, "Failed to get pwren-gpios\n"); + + gc5603->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->reset_gpio)) + dev_info(dev, "Failed to get reset-gpios, maybe no used\n"); + + gc5603->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->pwdn_gpio)) + dev_warn(dev, "Failed to get power-gpios\n"); + + ret = gc5603_configure_regulators(gc5603); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + ret = gc5603_parse_of(gc5603); + if (ret != 0) + return -EINVAL; + + gc5603->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(gc5603->pinctrl)) { + gc5603->pins_default = + pinctrl_lookup_state(gc5603->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(gc5603->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + gc5603->pins_sleep = + pinctrl_lookup_state(gc5603->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(gc5603->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } else { + dev_err(dev, "no pinctrl\n"); + } + + mutex_init(&gc5603->mutex); + + sd = &gc5603->subdev; + v4l2_i2c_subdev_init(sd, client, &gc5603_subdev_ops); + ret = gc5603_initialize_controls(gc5603); + if (ret) + goto err_destroy_mutex; + + ret = __gc5603_power_on(gc5603); + if (ret) + goto err_free_handler; + + usleep_range(3000, 4000); + ret = gc5603_check_sensor_id(gc5603, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &gc5603_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + gc5603->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &gc5603->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(gc5603->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + gc5603->module_index, facing, + GC5603_NAME, dev_name(sd->dev)); + + ret = v4l2_async_register_subdev_sensor_common(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); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + +err_power_off: + __gc5603_power_off(gc5603); +err_free_handler: + v4l2_ctrl_handler_free(&gc5603->ctrl_handler); + +err_destroy_mutex: + mutex_destroy(&gc5603->mutex); + return ret; +} + +static int gc5603_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&gc5603->ctrl_handler); + mutex_destroy(&gc5603->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __gc5603_power_off(gc5603); + pm_runtime_set_suspended(&client->dev); + return 0; +} + +static const struct i2c_device_id gc5603_match_id[] = { + { "gc5603", 0 }, + { }, +}; + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id gc5603_of_match[] = { + { .compatible = "galaxycore,gc5603" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gc5603_of_match); +#endif + +static struct i2c_driver gc5603_i2c_driver = { + .driver = { + .name = GC5603_NAME, + .pm = &gc5603_pm_ops, + .of_match_table = of_match_ptr(gc5603_of_match), + }, + .probe = &gc5603_probe, + .remove = &gc5603_remove, + .id_table = gc5603_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&gc5603_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&gc5603_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("GC2035 CMOS Image Sensor driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/Linux-Kernel.si4project/Backup/gc5603(7712).c b/Linux-Kernel.si4project/Backup/gc5603(7712).c new file mode 100755 index 0000000000000..06a751236f7f5 --- /dev/null +++ b/Linux-Kernel.si4project/Backup/gc5603(7712).c @@ -0,0 +1,1612 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * gc5603 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + * V0.0X01.0X03 add enum_frame_interval function. + * V0.0X01.0X04 add quick stream on/off + * V0.0X01.0X05 add function g_mbus_config + * V0.0X01.0X06 + * 1. add 2lane support. + * 2. add some debug info. + * 3. adjust gc5603_g_mbus_config function. + * V0.0X01.0X07 support get channel info + * V0.0X01.0X08 + * 1. default support 2lane full 30fps. + * 2. default support rk otp spec. + * V0.0X01.0X09 adjust supply sequence to suit spec + */ +//#define DEBUG +#include +#include +#include +#include +#include +#include +#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) +#define GC5603_NAME "gc5603" + +#define MIPI_FREQ_848M 423000000 + +#define GC5603_PAGE_SELECT 0xFE + +#define GC5603_REG_CHIP_ID_H 0x03F0 +#define GC5603_REG_CHIP_ID_L 0x03F1 + +#define GC5603_REG_EXP_H 0x0202 +#define GC5603_REG_EXP_L 0x0203 + +#define GC5603_REG_VTS_H 0x0340 +#define GC5603_REG_VTS_L 0x0341 + +#define GC5603_REG_CTRL_MODE 0x0100 +#define GC5603_MODE_SW_STANDBY 0x00 +#define GC5603_MODE_STREAMING 0x09 + +#define REG_NULL 0xFFFF + +#define GC5603_CHIP_ID 0x5603 + +#define GC5603_VTS_MAX 0x7fff +#define GC5603_HTS_MAX 0xFFF + +#define GC5603_EXPOSURE_MAX 0x3FFF +#define GC5603_EXPOSURE_MIN 1 +#define GC5603_EXPOSURE_STEP 1 + +#define GC5603_GAIN_MIN 64 +#define GC5603_GAIN_MAX 0xffff +#define GC5603_GAIN_STEP 1 +#define GC5603_GAIN_DEFAULT 64 + +#define gc5603_REG_VALUE_08BIT 1 +#define gc5603_REG_VALUE_16BIT 2 +#define gc5603_REG_VALUE_24BIT 3 + + +#define GC5603_LANES 2 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + + + +#define GC5603_FLIP_MIRROR_REG 0x0101 + +#define GC_MIRROR_BIT_MASK BIT(0) +#define GC_FLIP_BIT_MASK BIT(1) + +#define GC5603_XVCLK_FREQ_24M 24000000 +#define GC5603_XVCLK_FREQ_27M 27000000 + +static const char * const gc5603_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + +#define GC5603_NUM_SUPPLIES ARRAY_SIZE(gc5603_supply_names) + +#define to_gc5603(sd) container_of(sd, struct gc5603, subdev) + +struct regval { + u16 addr; + u8 val; +}; + +struct gc5603_mode { + u32 bus_fmt + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; + u32 hdr_mode; + u32 vc[PAD_MAX]; + u32 xvclk; +}; + +struct gc5603 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct gpio_desc *pwren_gpio; + struct regulator_bulk_data supplies[GC5603_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 *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *h_flip; + struct v4l2_ctrl *v_flip; + struct mutex mutex; + bool streaming; + bool power_on; + const struct gc5603_mode *cur_mode; + unsigned int lane_num; + unsigned int cfg_num; + unsigned int pixel_rate; + + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + struct rkmodule_awb_cfg awb_cfg; + struct rkmodule_lsc_cfg lsc_cfg; + u32 flip; +}; + + +static const struct regval gc5603_2960x1666_regs_2lane[] = { +//version 1.3 +//mclk 27Mhz +//mipi 2 lane 846Mbps/lane +//vts = 1750 ,row_time=19.05us +//window 2960x1666 +//BGGR + {0x03fe, 0xf0}, + {0x03fe, 0x00}, + {0x03fe, 0x10}, + {0x03fe, 0x00}, + {0x0202, 0x01}, + {0x0203, 0x50}, + {0x0a38, 0x02}, + {0x0a38, 0x03}, + {0x0a20, 0x07}, + {0x061b, 0x03}, + {0x061c, 0x50}, + {0x061d, 0x05}, + {0x061e, 0x70}, + {0x061f, 0x03}, + {0x0a21, 0x08}, + {0x0a34, 0x40}, + {0x0a35, 0x11}, + {0x0a36, 0x5e}, + {0x0a37, 0x03}, + {0x0314, 0x50}, + {0x0315, 0x32}, + {0x031c, 0xce}, + {0x0219, 0x47}, + {0x0342, 0x04}, + {0x0343, 0xb0}, + {0x0340, 0x06}, + {0x0341, 0xd6}, + {0x0345, 0x02}, + {0x0347, 0x02}, + {0x0348, 0x0b}, + {0x0349, 0x98}, + {0x034a, 0x06}, + {0x034b, 0x8a}, + {0x0094, 0x0b}, + {0x0095, 0x90}, + {0x0096, 0x06}, + {0x0097, 0x82}, + {0x0099, 0x04}, + {0x009b, 0x04}, + {0x060c, 0x01}, + {0x060e, 0xd2}, + {0x060f, 0x05}, + {0x070c, 0x01}, + {0x070e, 0xd2}, + {0x070f, 0x05}, + {0x0909, 0x07}, + {0x0902, 0x04}, + {0x0904, 0x0b}, + {0x0907, 0x54}, + {0x0908, 0x06}, + {0x0903, 0x9d}, + {0x072a, 0x1c},//18 + {0x072b, 0x1c},//18 + {0x0724, 0x2b}, + {0x0727, 0x2b}, + {0x1466, 0x18}, + {0x1467, 0x08}, + {0x1468, 0x10}, + {0x1469, 0x80}, + {0x146a, 0xe8},//b8 + {0x1412, 0x20}, + {0x0707, 0x07}, + {0x0737, 0x0f}, + {0x0704, 0x01}, + {0x0706, 0x03}, + {0x0716, 0x03}, + {0x0708, 0xc8}, + {0x0718, 0xc8}, + {0x061a, 0x02}, + {0x1430, 0x80}, + {0x1407, 0x10}, + {0x1408, 0x16}, + {0x1409, 0x03}, + {0x1438, 0x01}, + {0x02ce, 0x03}, + {0x0245, 0xc9}, + {0x023a, 0x08},//3B + {0x02cd, 0x88}, + {0x0612, 0x02}, + {0x0613, 0xc7}, + {0x0243, 0x03},//06 + {0x0089, 0x03}, + {0x0002, 0xab}, + {0x0040, 0xa3}, + {0x0075, 0x64},//64 + {0x0004, 0x0f}, + {0x0053, 0x0a}, + {0x0205, 0x0c}, + + //auto_load}, + {0x0a67, 0x80}, + {0x0a54, 0x0e}, + {0x0a65, 0x10}, + {0x0a98, 0x04}, + {0x05be, 0x00}, + {0x05a9, 0x01}, + {0x0023, 0x00}, + {0x0022, 0x00}, + {0x0025, 0x00}, + {0x0024, 0x00}, + {0x0028, 0x0b}, + {0x0029, 0x98}, + {0x002a, 0x06}, + {0x002b, 0x86}, + {0x0a83, 0xe0}, + {0x0a72, 0x02}, + {0x0a73, 0x60}, + {0x0a75, 0x41}, + {0x0a70, 0x03}, + {0x0a5a, 0x80}, + {0x0181, 0x30}, + {0x0182, 0x05}, + {0x0185, 0x01}, + {0x0180, 0x46}, + {0x0100, 0x08}, + {0x010d, 0x74}, + {0x010e, 0x0e}, + {0x0113, 0x02}, + {0x0114, 0x01}, + {0x0115, 0x10}, + //{0x0a70, 0x00}, + //{0x0080, 0x02}, + //{0x0a67, 0x00}, + {0x0052, 0x02}, + {0x0076, 0x01}, + {0x021a, 0x10}, + {0x0049, 0x0f}, + {0x004a, 0x3c}, + {0x004b, 0x00}, + {0x0430, 0x25}, + {0x0431, 0x25}, + {0x0432, 0x25}, + {0x0433, 0x25}, + {0x0434, 0x59}, + {0x0435, 0x59}, + {0x0436, 0x59}, + {0x0437, 0x59}, + + {0x0100, 0x09}, + {REG_NULL, 0x00}, +}; + +static const struct gc5603_mode supported_modes[] = { + { + .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10 + .width = 2960, + .height = 1666, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x6ce, + .hts_def = 0x0C80, + .vts_def = 0x06D6, + .reg_list = gc5603_2960x1666_regs_2lane, + .hdr_mode = NO_HDR, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = GC5603_XVCLK_FREQ_24M + }, +}; + +static const s64 link_freq_menu_items[] = { + MIPI_FREQ_848M +}; +static int gc5603_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; + + 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 gc5603_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 = gc5603_write_reg(client, regs[i].addr, + gc5603_REG_VALUE_08BIT, regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int gc5603_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; + dev_info(&client->dev, + "gc5603 read reg(0x%x val:0x%x) \n", reg, *val); +} +static int gc5603_get_reso_dist(const struct gc5603_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct gc5603_mode * +gc5603_find_best_fit(struct gc5603 *gc5603, 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 < gc5603->cfg_num; i++) { + dist = gc5603_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 uint8_t regValTable[26][7] = { + //0614, 0615, 0225, 1467 1468, 00b8, 00b9 + { 0x00, 0x00, 0x04, 0x15, 0x15, 0x01, 0x00}, + { 0x90, 0x02, 0x04, 0x15, 0x15, 0x01, 0x0A}, + { 0x00, 0x00, 0x00, 0x15, 0x15, 0x01, 0x12}, + { 0x90, 0x02, 0x00, 0x15, 0x15, 0x01, 0x20}, + { 0x01, 0x00, 0x00, 0x15, 0x15, 0x01, 0x30}, + { 0x91, 0x02, 0x00, 0x15, 0x15, 0x02, 0x05}, + { 0x02, 0x00, 0x00, 0x15, 0x15, 0x02, 0x19}, + { 0x92, 0x02, 0x00, 0x16, 0x16, 0x02, 0x3F}, + { 0x03, 0x00, 0x00, 0x16, 0x16, 0x03, 0x20}, + { 0x93, 0x02, 0x00, 0x17, 0x17, 0x04, 0x0A}, + { 0x00, 0x00, 0x01, 0x18, 0x18, 0x05, 0x02}, + { 0x90, 0x02, 0x01, 0x19, 0x19, 0x05, 0x39}, + { 0x01, 0x00, 0x01, 0x19, 0x19, 0x06, 0x3C}, + { 0x91, 0x02, 0x01, 0x19, 0x19, 0x08, 0x0D}, + { 0x02, 0x00, 0x01, 0x1a, 0x1a, 0x09, 0x21}, + { 0x92, 0x02, 0x01, 0x1a, 0x1a, 0x0B, 0x0F}, + { 0x03, 0x00, 0x01, 0x1c, 0x1c, 0x0D, 0x17}, + { 0x93, 0x02, 0x01, 0x1c, 0x1c, 0x0F, 0x33}, + { 0x04, 0x00, 0x01, 0x1d, 0x1d, 0x12, 0x30}, + { 0x94, 0x02, 0x01, 0x1d, 0x1d, 0x16, 0x10}, + { 0x05, 0x00, 0x01, 0x1e, 0x1e, 0x1A, 0x19}, + { 0x95, 0x02, 0x01, 0x1e, 0x1e, 0x1F, 0x13}, + { 0x06, 0x00, 0x01, 0x20, 0x20, 0x25, 0x08}, + { 0x96, 0x02, 0x01, 0x20, 0x20, 0x2C, 0x03}, + { 0xb6, 0x04, 0x01, 0x20, 0x20, 0x34, 0x0F}, + { 0x86, 0x06, 0x01, 0x20, 0x20, 0x3D, 0x3D}, +}; + +static uint32_t gain_level_table[27] = { + 64, + 74, + 82, + 96, + 112, + 133, + 153, + 191, + 224, + 266, + 322, + 377, + 444, + 525, + 609, + 719, + 855, + 1011, + 1200, + 1424, + 1689, + 2003, + + 2376, + 2819, + + 3343, + 3965, + 0xffffffff, +}; +//static int total = sizeof(gain_level_table) / sizeof(uint32_t); + +static int gc5603_set_gain(struct gc5603 *gc5603, u32 gain) +{ + int ret; + uint16_t i = 0; + uint16_t total = 0; + uint16_t temp = 0; + + + for (i = 0; i < total; i++) { + if ((gain_level_table[i] <= gain) && (gain < gain_level_table[i+1])) + break; + } + + if((gain>3965)||(gain==3965)) + i =25; + + + ret = gc5603_write_reg(gc5603->client, 0x031d,gc5603_REG_VALUE_08BIT, 0x2d); + ret = gc5603_write_reg(gc5603->client, 0x0614,gc5603_REG_VALUE_08BIT,regValTable[i][0]); + ret = gc5603_write_reg(gc5603->client, 0x0615,gc5603_REG_VALUE_08BIT,regValTable[i][1]); + ret = gc5603_write_reg(gc5603->client, 0x0225,gc5603_REG_VALUE_08BIT,regValTable[i][2]); + + ret = gc5603_write_reg(gc5603->client, 0x031d,gc5603_REG_VALUE_08BIT, 0x28); + ret = gc5603_write_reg(gc5603->client, 0x1467,gc5603_REG_VALUE_08BIT,regValTable[i][3]); + ret = gc5603_write_reg(gc5603->client, 0x1468,gc5603_REG_VALUE_08BIT,regValTable[i][4]); + ret = gc5603_write_reg(gc5603->client, 0x00b8,gc5603_REG_VALUE_08BIT,regValTable[i][5]); + ret = gc5603_write_reg(gc5603->client, 0x00b9,gc5603_REG_VALUE_08BIT,regValTable[i][6]); + + + temp = 64 * gain / gain_level_table[i]; + + //dev_warn(&client->dev, "gc5603_set_gain gain=%d,i=%d,temp=%d \n", gain, i, temp); + + ret |= gc5603_write_reg(gc5603->client, 0x0064,gc5603_REG_VALUE_08BIT,(temp >> 6)); + ret |= gc5603_write_reg(gc5603->client, 0x0065,gc5603_REG_VALUE_08BIT,((temp&0x3f) << 2) ); + + return ret; +} + +static int gc5603_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc5603 *gc5603 = container_of(ctrl->handler, + struct gc5603, ctrl_handler); + struct i2c_client *client = gc5603->client; + s64 max; + int ret = 0; + u32 vts = 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 = gc5603->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(gc5603->exposure, + gc5603->exposure->minimum, max, + gc5603->exposure->step, + gc5603->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + + #if 1 + ret = gc5603_write_reg(gc5603->client, GC5603_REG_EXP_H,gc5603_REG_VALUE_08BIT, + (ctrl->val >> 8)); + ret |= gc5603_write_reg(gc5603->client, GC5603_REG_EXP_L,gc5603_REG_VALUE_08BIT, + ctrl->val & 0xff); + #endif + + break; + case V4L2_CID_ANALOGUE_GAIN: + gc5603_set_gain(gc5603, ctrl->val); + break; + case V4L2_CID_VBLANK: + vts = ctrl->val + gc5603->cur_mode->height; + + #if 1 + ret = gc5603_write_reg(gc5603->client, GC5603_REG_VTS_H,gc5603_REG_VALUE_08BIT,(vts >> 8)); + ret |= gc5603_write_reg(gc5603->client, GC5603_REG_VTS_L, gc5603_REG_VALUE_08BIT,vts & 0xff); + #endif + break; + case V4L2_CID_HFLIP: + if (ctrl->val) + gc5603->flip |= GC_MIRROR_BIT_MASK; + else + gc5603->flip &= ~GC_MIRROR_BIT_MASK; + break; + case V4L2_CID_VFLIP: + if (ctrl->val) + gc5603->flip |= GC_FLIP_BIT_MASK; + else + gc5603->flip &= ~GC_FLIP_BIT_MASK; + 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 gc5603_ctrl_ops = { + .s_ctrl = gc5603_set_ctrl, +}; + +static int gc5603_configure_regulators(struct gc5603 *gc5603) +{ + unsigned int i; + + for (i = 0; i < GC5603_NUM_SUPPLIES; i++) + gc5603->supplies[i].supply = gc5603_supply_names[i]; + + return devm_regulator_bulk_get(&gc5603->client->dev, + GC5603_NUM_SUPPLIES, + gc5603->supplies); +} + +static int gc5603_parse_of(struct gc5603 *gc5603) +{ + struct device *dev = &gc5603->client->dev; + struct device_node *endpoint; + struct fwnode_handle *fwnode; + int rval; + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "Failed to get endpoint\n"); + return -EINVAL; + } + fwnode = of_fwnode_handle(endpoint); + rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0); + if (rval <= 0) { + dev_warn(dev, " Get mipi lane num failed!\n"); + return -1; + } + + gc5603->lane_num = rval; + if (2 == gc5603->lane_num) { + gc5603->cur_mode = &supported_modes[0]; + gc5603->cfg_num = ARRAY_SIZE(supported_modes); + + /*pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + gc5603->pixel_rate = MIPI_FREQ_848M * 2U * (gc5603->lane_num) / 10U; + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + gc5603->lane_num, gc5603->pixel_rate); + } else { + dev_info(dev, "gc5603 can not support the lane num(%d)\n", gc5603->lane_num); + } + return 0; +} + +static int gc5603_initialize_controls(struct gc5603 *gc5603) +{ + const struct gc5603_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &gc5603->ctrl_handler; + mode = gc5603->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &gc5603->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, gc5603->pixel_rate, 1, gc5603->pixel_rate); + + h_blank = mode->hts_def - mode->width; + gc5603->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (gc5603->hblank) + gc5603->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + gc5603->vblank = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + GC5603_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + gc5603->exposure = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_EXPOSURE, + GC5603_EXPOSURE_MIN, + exposure_max, + GC5603_EXPOSURE_STEP, + mode->exp_def); + + gc5603->anal_gain = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + GC5603_GAIN_MIN, + GC5603_GAIN_MAX, + GC5603_GAIN_STEP, + GC5603_GAIN_DEFAULT); + + gc5603->h_flip = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + gc5603->v_flip = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + gc5603->flip = 0; + + if (handler->error) { + ret = handler->error; + dev_err(&gc5603->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + gc5603->subdev.ctrl_handler = handler; + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 gc5603_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, GC5603_XVCLK_FREQ_24M / 1000 / 1000); +} + +static int __gc5603_power_on(struct gc5603 *gc5603) +{ + int ret; + u32 delay_us; + struct device *dev = &gc5603->client->dev; + + if (!IS_ERR_OR_NULL(gc5603->pins_default)) { + ret = pinctrl_select_state(gc5603->pinctrl, + gc5603->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + ret = clk_set_rate(gc5603->xvclk, GC5603_XVCLK_FREQ_24M); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(gc5603->xvclk) != GC5603_XVCLK_FREQ_24M) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(gc5603->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 0); + + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 0); + + usleep_range(500, 1000); + ret = regulator_bulk_enable(GC5603_NUM_SUPPLIES, gc5603->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + if (!IS_ERR(gc5603->pwren_gpio)) + gpiod_set_value_cansleep(gc5603->pwren_gpio, 1); + + usleep_range(1000, 1100); + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 1); + usleep_range(100, 150); + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = gc5603_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + return 0; + +disable_clk: + clk_disable_unprepare(gc5603->xvclk); + return ret; +} + +static void __gc5603_power_off(struct gc5603 *gc5603) +{ + int ret; + struct device *dev = &gc5603->client->dev; + + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 0); + clk_disable_unprepare(gc5603->xvclk); + + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 0); + + if (!IS_ERR_OR_NULL(gc5603->pins_sleep)) { + ret = pinctrl_select_state(gc5603->pinctrl, + gc5603->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + regulator_bulk_disable(GC5603_NUM_SUPPLIES, gc5603->supplies); + if (!IS_ERR(gc5603->pwren_gpio)) + gpiod_set_value_cansleep(gc5603->pwren_gpio, 0); + +} + + static int gc5603_check_sensor_id(struct gc5603 *gc5603, + struct i2c_client *client) +{ + struct device *dev = &gc5603->client->dev; + u16 id = 0; + u32 reg_H = 0; + u32 reg_L = 0; + int ret; + + ret = gc5603_read_reg(client, GC5603_REG_CHIP_ID_H, + gc5603_REG_VALUE_08BIT, ®_H); + ret |= gc5603_read_reg(client, GC5603_REG_CHIP_ID_L, + gc5603_REG_VALUE_08BIT, ®_L); + + id = ((reg_H << 8) & 0xff00) | (reg_L & 0xff); + if (!(reg_H == (GC5603_CHIP_ID >> 8) || reg_L == (GC5603_CHIP_ID & 0xff))) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + dev_info(dev, "detected gc%04x sensor\n", id); + return 0; +} + +static int gc5603_set_flip(struct gc5603 *gc5603, u8 mode) +{ +u32 match_reg = 0; + + gc5603_read_reg(gc5603->client, GC5603_FLIP_MIRROR_REG,gc5603_REG_VALUE_08BIT,&match_reg); + + if (mode == GC_FLIP_BIT_MASK) { + match_reg |= GC_FLIP_BIT_MASK; + match_reg &= ~GC_MIRROR_BIT_MASK; + } else if (mode == GC_MIRROR_BIT_MASK) { + match_reg |= GC_MIRROR_BIT_MASK; + match_reg &= ~GC_FLIP_BIT_MASK; + } else if (mode == (GC_MIRROR_BIT_MASK | + GC_FLIP_BIT_MASK)) { + match_reg |= GC_FLIP_BIT_MASK; + match_reg |= GC_MIRROR_BIT_MASK; + } else { + match_reg &= ~GC_FLIP_BIT_MASK; + match_reg &= ~GC_MIRROR_BIT_MASK; + } + return gc5603_write_reg(gc5603->client, GC5603_FLIP_MIRROR_REG,gc5603_REG_VALUE_08BIT ,match_reg); +} + +static int __gc5603_start_stream(struct gc5603 *gc5603) +{ + int ret; + + ret = gc5603_write_array(gc5603->client, gc5603->cur_mode->reg_list); + if (ret) + return ret; + + usleep_range(1000, 1100); + + + gc5603_write_reg(gc5603->client, 0x0a70,gc5603_REG_VALUE_08BIT, 0x00); + gc5603_write_reg(gc5603->client, 0x0080,gc5603_REG_VALUE_08BIT, 0x02); + gc5603_write_reg(gc5603->client, 0x0a67,gc5603_REG_VALUE_08BIT, 0x00); + + + + + /* In case these controls are set before streaming */ + mutex_unlock(&gc5603->mutex); + ret = __v4l2_ctrl_handler_setup(&gc5603->ctrl_handler); + mutex_lock(&gc5603->mutex); + + ret = gc5603_set_flip(gc5603, gc5603->flip); + if (ret) + return ret; + return gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_STREAMING); +} + +static int __gc5603_stop_stream(struct gc5603 *gc5603) +{ + return gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_SW_STANDBY); +} + +static void gc5603_get_module_inf(struct gc5603 *gc5603, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, GC5603_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, gc5603->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, gc5603->len_name, sizeof(inf->base.lens)); +} + +static int gc5603_get_channel_info(struct gc5603 *gc5603, struct rkmodule_channel_info *ch_info) +{ + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + ch_info->vc = gc5603->cur_mode->vc[ch_info->index]; + ch_info->width = gc5603->cur_mode->width; + ch_info->height = gc5603->cur_mode->height; + ch_info->bus_fmt = gc5603->cur_mode->bus_fmt; + return 0; +} + +static void gc5603_set_awb_cfg(struct gc5603 *gc5603, + struct rkmodule_awb_cfg *cfg) +{ + mutex_lock(&gc5603->mutex); + memcpy(&gc5603->awb_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&gc5603->mutex); +} + +static void gc5603_set_lsc_cfg(struct gc5603 *gc5603, + struct rkmodule_lsc_cfg *cfg) +{ + mutex_lock(&gc5603->mutex); + memcpy(&gc5603->lsc_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&gc5603->mutex); +} + +static long gc5603_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + long ret = 0; + struct rkmodule_hdr_cfg *hdr_cfg; + u32 stream = 0; + struct rkmodule_channel_info *ch_info; + + switch (cmd) { + case RKMODULE_GET_HDR_CFG: + hdr_cfg = (struct rkmodule_hdr_cfg *)arg; + hdr_cfg->esp.mode = HDR_NORMAL_VC; + hdr_cfg->hdr_mode = gc5603->cur_mode->hdr_mode; + break; + case RKMODULE_SET_HDR_CFG: + case RKMODULE_SET_CONVERSION_GAIN: + break; + case RKMODULE_GET_MODULE_INFO: + gc5603_get_module_inf(gc5603, (struct rkmodule_inf *)arg); + break; + case RKMODULE_AWB_CFG: + gc5603_set_awb_cfg(gc5603, (struct rkmodule_awb_cfg *)arg); + break; + case RKMODULE_LSC_CFG: + gc5603_set_lsc_cfg(gc5603, (struct rkmodule_lsc_cfg *)arg); + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_STREAMING); + else + ret = gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_SW_STANDBY); + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = gc5603_get_channel_info(gc5603, ch_info); + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} + +#ifdef CONFIG_COMPAT +static long gc5603_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 *awb_cfg; + struct rkmodule_lsc_cfg *lsc_cfg; + struct rkmodule_hdr_cfg *hdr; + long ret = 0; + u32 cg = 0; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = gc5603_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + awb_cfg = kzalloc(sizeof(*awb_cfg), GFP_KERNEL); + if (!awb_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(awb_cfg, up, sizeof(*awb_cfg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, awb_cfg); + kfree(awb_cfg); + break; + case RKMODULE_LSC_CFG: + lsc_cfg = kzalloc(sizeof(*lsc_cfg), GFP_KERNEL); + if (!lsc_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(lsc_cfg, up, sizeof(*lsc_cfg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, lsc_cfg); + kfree(lsc_cfg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = gc5603_ioctl(sd, cmd, hdr); + if (!ret) + ret = copy_to_user(up, hdr, sizeof(*hdr)); + kfree(hdr); + break; + case RKMODULE_SET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(hdr, up, sizeof(*hdr)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, hdr); + kfree(hdr); + break; + case RKMODULE_SET_CONVERSION_GAIN: + ret = copy_from_user(&cg, up, sizeof(cg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, &cg); + break; + case RKMODULE_SET_QUICK_STREAM: + ret = copy_from_user(&stream, up, sizeof(u32)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, &stream); + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} +#endif + +static int gc5603_s_stream(struct v4l2_subdev *sd, int on) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct i2c_client *client = gc5603->client; + int ret = 0; + + mutex_lock(&gc5603->mutex); + on = !!on; + if (on == gc5603->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 = __gc5603_start_stream(gc5603); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __gc5603_stop_stream(gc5603); + pm_runtime_put(&client->dev); + } + + gc5603->streaming = on; + +unlock_and_return: + mutex_unlock(&gc5603->mutex); + return 0; +} + +static int gc5603_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + + mutex_lock(&gc5603->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&gc5603->mutex); + + return 0; +} + +static int gc5603_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + u32 val = 0; + + if (mode->hdr_mode == NO_HDR) + val = 1 << (GC5603_LANES - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + config->type = V4L2_MBUS_CSI2_DPHY; + config->flags = val; + return 0; +} + +static int gc5603_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = gc5603->supported_modes[code->index].bus_fmt; + return 0; +} + +static int gc5603_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + + if (fse->index >= gc5603->cfg_num) + return -EINVAL; + + if (fse->code != gc5603->supported_modes[code->index].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; +} + +#define DST_WIDTH 2720 +#define DST_HEIGHT 1616 + +/* + * The resolution of the driver configuration needs to be exactly + * the same as the current output resolution of the sensor, + * the input width of the isp needs to be 16 aligned, + * the input height of the isp needs to be 8 aligned. + * Can be cropped to standard resolution by this function, + * otherwise it will crop out strange resolution according + * to the alignment rules. + */ +static int gc5603_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + sel->r.left =120; + sel->r.width = DST_WIDTH; + sel->r.top = 25; + sel->r.height = DST_HEIGHT; + return 0; + } + return -EINVAL; +} + +static int gc5603_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + + if (fie->index >= gc5603->cfg_num) + return -EINVAL; + + fie->code = supported_modes[fie->index].bus_fmt; + fie->width = supported_modes[fie->index].width; + fie->height = supported_modes[fie->index].height; + fie->interval = supported_modes[fie->index].max_fps; + fie->reserved[0] = supported_modes[fie->index].hdr_mode; + return 0; +} + +static int gc5603_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&gc5603->mutex); + + mode = gc5603_find_best_fit(gc5603, fmt); + fmt->format.code = mode->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, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&gc5603->mutex); + return -ENOTTY; +#endif + } else { + gc5603->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(gc5603->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(gc5603->vblank, vblank_def, + GC5603_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&gc5603->mutex); + return 0; +} + +static int gc5603_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + + mutex_lock(&gc5603->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&gc5603->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->bus_fmt; + fmt->format.field = V4L2_FIELD_NONE; + + /* format info: width/height/data type/virctual channel */ + if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR) + fmt->reserved[0] = mode->vc[fmt->pad]; + else + fmt->reserved[0] = mode->vc[PAD0]; + + } + mutex_unlock(&gc5603->mutex); + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int gc5603_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct gc5603_mode *def_mode = &supported_modes[0]; + + mutex_lock(&gc5603->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&gc5603->mutex); + /* No crop or compose */ + return 0; +} +#endif + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops gc5603_internal_ops = { + .open = gc5603_open, +}; +#endif + +static int gc5603_s_power(struct v4l2_subdev *sd, int on) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct i2c_client *client = gc5603->client; + int ret = 0; + + mutex_lock(&gc5603->mutex); + + /* If the power state is not modified - no work to do. */ + if (gc5603->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; + } + + gc5603->power_on = true; + } else { + pm_runtime_put(&client->dev); + gc5603->power_on = false; + } + +unlock_and_return: + mutex_unlock(&gc5603->mutex); + + return ret; +} + +static const struct v4l2_subdev_core_ops gc5603_core_ops = { + .s_power = gc5603_s_power, + .ioctl = gc5603_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = gc5603_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops gc5603_video_ops = { + .s_stream = gc5603_s_stream, + .g_frame_interval = gc5603_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops gc5603_pad_ops = { + .enum_mbus_code = gc5603_enum_mbus_code, + .enum_frame_size = gc5603_enum_frame_sizes, + .enum_frame_interval = gc5603_enum_frame_interval, + .get_fmt = gc5603_get_fmt, + .set_fmt = gc5603_set_fmt, + .get_selection = gc5603_get_selection, + .get_mbus_config = gc5603_g_mbus_config, +}; + +static const struct v4l2_subdev_ops gc5603_subdev_ops = { + .core = &gc5603_core_ops, + .video = &gc5603_video_ops, + .pad = &gc5603_pad_ops, +}; + +static int gc5603_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + __gc5603_power_on(gc5603); + return 0; +} + +static int gc5603_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + __gc5603_power_off(gc5603); + return 0; +} + +static const struct dev_pm_ops gc5603_pm_ops = { + SET_RUNTIME_PM_OPS(gc5603_runtime_suspend, + gc5603_runtime_resume, NULL) +}; + +static int gc5603_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct gc5603 *gc5603; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + gc5603 = devm_kzalloc(dev, sizeof(*gc5603), GFP_KERNEL); + if (!gc5603) + return -ENOMEM; + + gc5603->client = client; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &gc5603->module_index); + if (ret) { + dev_warn(dev, "could not get module index!\n"); + gc5603->module_index = 0; + } + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &gc5603->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &gc5603->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &gc5603->len_name); + if (ret) { + dev_err(dev, + "could not get module information!\n"); + return -EINVAL; + } + + gc5603->xvclk = devm_clk_get(&client->dev, "xvclk"); + if (IS_ERR(gc5603->xvclk)) { + dev_err(&client->dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + gc5603->pwren_gpio = devm_gpiod_get(dev, "pwren", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->pwdn_gpio)) + dev_warn(dev, "Failed to get pwren-gpios\n"); + + gc5603->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->reset_gpio)) + dev_info(dev, "Failed to get reset-gpios, maybe no used\n"); + + gc5603->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->pwdn_gpio)) + dev_warn(dev, "Failed to get power-gpios\n"); + + ret = gc5603_configure_regulators(gc5603); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + ret = gc5603_parse_of(gc5603); + if (ret != 0) + return -EINVAL; + + gc5603->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(gc5603->pinctrl)) { + gc5603->pins_default = + pinctrl_lookup_state(gc5603->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(gc5603->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + gc5603->pins_sleep = + pinctrl_lookup_state(gc5603->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(gc5603->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } else { + dev_err(dev, "no pinctrl\n"); + } + + mutex_init(&gc5603->mutex); + + sd = &gc5603->subdev; + v4l2_i2c_subdev_init(sd, client, &gc5603_subdev_ops); + ret = gc5603_initialize_controls(gc5603); + if (ret) + goto err_destroy_mutex; + + ret = __gc5603_power_on(gc5603); + if (ret) + goto err_free_handler; + + usleep_range(3000, 4000); + ret = gc5603_check_sensor_id(gc5603, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &gc5603_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + gc5603->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &gc5603->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(gc5603->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + gc5603->module_index, facing, + GC5603_NAME, dev_name(sd->dev)); + + ret = v4l2_async_register_subdev_sensor_common(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); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + +err_power_off: + __gc5603_power_off(gc5603); +err_free_handler: + v4l2_ctrl_handler_free(&gc5603->ctrl_handler); + +err_destroy_mutex: + mutex_destroy(&gc5603->mutex); + return ret; +} + +static int gc5603_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&gc5603->ctrl_handler); + mutex_destroy(&gc5603->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __gc5603_power_off(gc5603); + pm_runtime_set_suspended(&client->dev); + return 0; +} + +static const struct i2c_device_id gc5603_match_id[] = { + { "gc5603", 0 }, + { }, +}; + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id gc5603_of_match[] = { + { .compatible = "galaxycore,gc5603" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gc5603_of_match); +#endif + +static struct i2c_driver gc5603_i2c_driver = { + .driver = { + .name = GC5603_NAME, + .pm = &gc5603_pm_ops, + .of_match_table = of_match_ptr(gc5603_of_match), + }, + .probe = &gc5603_probe, + .remove = &gc5603_remove, + .id_table = gc5603_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&gc5603_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&gc5603_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("GC2035 CMOS Image Sensor driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/Linux-Kernel.si4project/Backup/lubancat_linux_rk3588_defconfig(2041) b/Linux-Kernel.si4project/Backup/lubancat_linux_rk3588_defconfig(2041) new file mode 100755 index 0000000000000..238366df74139 --- /dev/null +++ b/Linux-Kernel.si4project/Backup/lubancat_linux_rk3588_defconfig(2041) @@ -0,0 +1,816 @@ +CONFIG_DEFAULT_HOSTNAME="localhost" +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_UCLAMP_TASK=y +CONFIG_UCLAMP_BUCKETS_COUNT=20 +CONFIG_CGROUPS=y +CONFIG_MEMCG=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_SCHED=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_UCLAMP_TASK_GROUP=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_HUGETLB=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_BPF=y +CONFIG_NAMESPACES=y +CONFIG_USER_NS=y +CONFIG_CHECKPOINT_RESTORE=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_BPF_SYSCALL=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_ARCH_ROCKCHIP=y +# CONFIG_ARM64_ERRATUM_826319 is not set +# CONFIG_ARM64_ERRATUM_827319 is not set +# CONFIG_ARM64_ERRATUM_824069 is not set +# CONFIG_ARM64_ERRATUM_819472 is not set +# CONFIG_ARM64_ERRATUM_832075 is not set +# CONFIG_CAVIUM_ERRATUM_22375 is not set +# CONFIG_CAVIUM_ERRATUM_23154 is not set +CONFIG_SCHED_MC=y +CONFIG_NR_CPUS=8 +CONFIG_HZ_300=y +CONFIG_COMPAT=y +CONFIG_ARMV8_DEPRECATED=y +CONFIG_SWP_EMULATION=y +CONFIG_CP15_BARRIER_EMULATION=y +CONFIG_SETEND_EMULATION=y +CONFIG_ARM64_PSEUDO_NMI=y +CONFIG_PM_DEBUG=y +CONFIG_PM_ADVANCED_DEBUG=y +CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y +CONFIG_ENERGY_MODEL=y +CONFIG_CPU_IDLE=y +CONFIG_ARM_CPUIDLE=y +CONFIG_ARM_PSCI_CPUIDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_TIMES=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPUFREQ_DT=y +CONFIG_ARM_ROCKCHIP_CPUFREQ=y +CONFIG_ARM_SCMI_PROTOCOL=y +CONFIG_ROCKCHIP_SIP=y +CONFIG_ARM64_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_BLK_DEV_THROTTLING=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_CMDLINE_PARTITION=y +# CONFIG_COMPACTION is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_CMA=y +CONFIG_ZSMALLOC=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_PACKET_DIAG=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MROUTE=y +CONFIG_SYN_COOKIES=y +CONFIG_INET_ESP=y +# CONFIG_INET_DIAG is not set +# CONFIG_IPV6_SIT is not set +CONFIG_NETFILTER=y +CONFIG_BRIDGE_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_PROCFS=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_DSCP=y +CONFIG_NETFILTER_XT_TARGET_HL=y +CONFIG_NETFILTER_XT_TARGET_HMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_TEE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=y +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y +CONFIG_NETFILTER_XT_MATCH_BPF=y +CONFIG_NETFILTER_XT_MATCH_CGROUP=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_CPU=y +CONFIG_NETFILTER_XT_MATCH_DCCP=y +CONFIG_NETFILTER_XT_MATCH_DEVGROUP=y +CONFIG_NETFILTER_XT_MATCH_DSCP=y +CONFIG_NETFILTER_XT_MATCH_ECN=y +CONFIG_NETFILTER_XT_MATCH_ESP=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPCOMP=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_IPVS=y +CONFIG_NETFILTER_XT_MATCH_L2TP=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_NFACCT=y +CONFIG_NETFILTER_XT_MATCH_OSF=y +CONFIG_NETFILTER_XT_MATCH_OWNER=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +CONFIG_NETFILTER_XT_MATCH_RATEEST=y +CONFIG_NETFILTER_XT_MATCH_REALM=y +CONFIG_NETFILTER_XT_MATCH_RECENT=y +CONFIG_NETFILTER_XT_MATCH_SCTP=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_IP_VS=y +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_RR=y +CONFIG_IP_VS_NFCT=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_NAT=y +CONFIG_IP6_NF_TARGET_MASQUERADE=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_VLAN_FILTERING=y +CONFIG_VLAN_8021Q=y +CONFIG_NET_SCHED=y +CONFIG_NET_CLS_CGROUP=y +CONFIG_NETLINK_DIAG=y +CONFIG_CGROUP_NET_PRIO=y +CONFIG_CAN=y +CONFIG_CAN_ROCKCHIP=y +CONFIG_CANFD_ROCKCHIP=y +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y +CONFIG_BT_HS=y +CONFIG_BT_MSFTEXT=y +CONFIG_BT_HCIBTUSB=m +CONFIG_BT_HCIBTUSB_AUTOSUSPEND=y +CONFIG_BT_HCIBTUSB_MTK=y +CONFIG_BT_HCIBFUSB=m +CONFIG_BT_HCIVHCI=m +CONFIG_BT_MRVL=m +CONFIG_BT_MRVL_SDIO=m +CONFIG_MAC80211_DEBUGFS=y +CONFIG_RFKILL=y +CONFIG_RFKILL_RK=y +CONFIG_PCI=y +CONFIG_PCIEPORTBUS=y +CONFIG_PCIEASPM_POWERSAVE=y +CONFIG_PCIEASPM_EXT=y +CONFIG_PCIE_ROCKCHIP_HOST=y +CONFIG_PCIE_DW_ROCKCHIP=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_DEBUG_DEVRES=y +CONFIG_CONNECTOR=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_SPI_NAND=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_UBI=y +CONFIG_ZRAM=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=1 +CONFIG_BLK_DEV_NVME=y +CONFIG_SRAM=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_SPI_ATTRS=y +CONFIG_ATA=y +CONFIG_SATA_AHCI=y +CONFIG_SATA_AHCI_PLATFORM=y +# CONFIG_ATA_SFF is not set +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_THIN_PROVISIONING=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +CONFIG_MACVLAN=y +CONFIG_IPVLAN=y +CONFIG_VXLAN=y +CONFIG_TUN=m +CONFIG_VETH=y +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_NET_VENDOR_ADAPTEC is not set +# CONFIG_NET_VENDOR_AGERE is not set +# CONFIG_NET_VENDOR_ALTEON is not set +# CONFIG_NET_VENDOR_AMD is not set +# CONFIG_NET_VENDOR_ARC is not set +# CONFIG_NET_VENDOR_ATHEROS is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_BROCADE is not set +# CONFIG_NET_VENDOR_CAVIUM is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CISCO is not set +# CONFIG_NET_VENDOR_DEC is not set +# CONFIG_NET_VENDOR_DLINK is not set +# CONFIG_NET_VENDOR_EMULEX is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_HISILICON is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_MYRI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NVIDIA is not set +# CONFIG_NET_VENDOR_OKI is not set +# CONFIG_NET_VENDOR_QLOGIC is not set +# CONFIG_NET_VENDOR_QUALCOMM is not set +# CONFIG_NET_VENDOR_RDC is not set +CONFIG_R8169=y +# CONFIG_NET_VENDOR_RENESAS is not set +# CONFIG_NET_VENDOR_ROCKER is not set +# CONFIG_NET_VENDOR_SAMSUNG is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SILAN is not set +# CONFIG_NET_VENDOR_SIS is not set +# CONFIG_NET_VENDOR_SMSC is not set +CONFIG_STMMAC_ETH=y +# CONFIG_NET_VENDOR_SUN is not set +# CONFIG_NET_VENDOR_SYNOPSYS is not set +# CONFIG_NET_VENDOR_TEHUTI is not set +# CONFIG_NET_VENDOR_TI is not set +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +CONFIG_ROCKCHIP_PHY=y +CONFIG_JLSEMI_PHY=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +CONFIG_SLIP=y +CONFIG_SLIP_COMPRESSED=y +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_AX88179_178A=m +CONFIG_USB_NET_CDC_NCM=m +CONFIG_USB_NET_NET1080=m +CONFIG_USB_NET_CDC_SUBSET=m +CONFIG_USB_NET_ZAURUS=m +CONFIG_USB_NET_QMI_WWAN_Q=y +CONFIG_B43=m +CONFIG_B43_DEBUG=y +CONFIG_IPW2100=m +CONFIG_IPW2100_MONITOR=y +CONFIG_IPW2100_DEBUG=y +CONFIG_IPW2200=m +CONFIG_IPW2200_MONITOR=y +CONFIG_IPW2200_PROMISCUOUS=y +CONFIG_IPW2200_QOS=y +CONFIG_IPW2200_DEBUG=y +CONFIG_LIBIPW_DEBUG=y +CONFIG_IWL4965=m +CONFIG_IWL3945=m +CONFIG_IWLEGACY_DEBUG=y +CONFIG_IWLEGACY_DEBUGFS=y +CONFIG_IWLWIFI=m +CONFIG_IWLDVM=m +CONFIG_IWLMVM=m +CONFIG_IWLWIFI_BCAST_FILTERING=y +CONFIG_IWLWIFI_DEBUG=y +CONFIG_IWLWIFI_DEBUGFS=y +CONFIG_MT7601U=m +CONFIG_MT76x0U=m +CONFIG_MT76x0E=m +CONFIG_MT76x2E=m +CONFIG_MT76x2U=m +CONFIG_MT7603E=m +CONFIG_MT7615E=m +CONFIG_MT7663U=m +CONFIG_MT7663S=m +CONFIG_MT7915E=m +CONFIG_RTL8192CE=m +CONFIG_RTL8192SE=m +CONFIG_RTL8192DE=m +CONFIG_RTL8723BE=m +CONFIG_RTL8188EE=m +CONFIG_RTL8192EE=m +CONFIG_RTL8821AE=m +CONFIG_RTL8192CU=m +CONFIG_RTL8XXXU=m +CONFIG_RTW88=y +CONFIG_RTW88_8822BE=m +CONFIG_RTW88_8723DE=m +CONFIG_RTW88_DEBUG=y +CONFIG_RTW88_DEBUGFS=y +CONFIG_WL_ROCKCHIP=y +CONFIG_WIFI_BUILD_MODULE=y +# CONFIG_BCMDHD is not set +CONFIG_RTL8821CE=m +CONFIG_RTL8821CU=m +CONFIG_RTL8822BU=m +CONFIG_RTL8822CS=m +CONFIG_RTL8822CE=m +CONFIG_RTL8852BE=m +CONFIG_RTL8852BU=m +CONFIG_USB_NET_RNDIS_WLAN=y +CONFIG_INPUT_JOYDEV=y +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_ADC=y +# CONFIG_KEYBOARD_ATKBD is not set +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_GPIO_POLLED=y +# CONFIG_MOUSE_PS2 is not set +CONFIG_MOUSE_CYAPA=y +CONFIG_MOUSE_ELAN_I2C=y +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_SIDEWINDER=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL_MXT=y +CONFIG_TOUCHSCREEN_GOODIX=y +CONFIG_TOUCHSCREEN_GSL3673=y +CONFIG_TOUCHSCREEN_GT1X=y +CONFIG_TOUCHSCREEN_ELAN=y +CONFIG_TOUCHSCREEN_USB_COMPOSITE=y +CONFIG_ROCKCHIP_REMOTECTL=y +CONFIG_ROCKCHIP_REMOTECTL_PWM=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_RK805_PWRKEY=y +# CONFIG_SERIO is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=10 +CONFIG_SERIAL_8250_RUNTIME_UARTS=10 +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_99xx=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_ROCKCHIP=y +CONFIG_TCG_TPM=y +CONFIG_TCG_TIS_I2C_INFINEON=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_RK3X=y +CONFIG_SPI=y +CONFIG_SPI_BITBANG=y +CONFIG_SPI_ROCKCHIP=y +CONFIG_SPI_ROCKCHIP_SFC=y +CONFIG_SPI_SPIDEV=y +CONFIG_PINCTRL_RK805=y +CONFIG_PINCTRL_RK806=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_IRQ=y +CONFIG_POWER_RESET_GPIO=y +CONFIG_POWER_RESET_GPIO_RESTART=y +CONFIG_SYSCON_REBOOT_MODE=y +CONFIG_BATTERY_CW2017=y +CONFIG_BATTERY_SBS=y +CONFIG_CHARGER_GPIO=y +CONFIG_CHARGER_BQ24735=y +CONFIG_CHARGER_BQ25700=y +CONFIG_BATTERY_RK817=y +CONFIG_CHARGER_RK817=y +CONFIG_SENSORS_PWM_FAN=y +CONFIG_THERMAL=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR=y +CONFIG_THERMAL_GOV_FAIR_SHARE=y +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y +CONFIG_CPU_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y +CONFIG_ROCKCHIP_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_DW_WATCHDOG=y +CONFIG_MFD_RK806_SPI=y +CONFIG_MFD_RK808=y +CONFIG_MFD_TPS6586X=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_ACT8865=y +CONFIG_REGULATOR_FAN53555=y +CONFIG_REGULATOR_GPIO=y +CONFIG_REGULATOR_LP8752=y +CONFIG_REGULATOR_MP8865=y +CONFIG_REGULATOR_PWM=y +CONFIG_REGULATOR_RK806=y +CONFIG_REGULATOR_RK808=y +CONFIG_REGULATOR_RK860X=y +CONFIG_REGULATOR_TPS65132=y +CONFIG_REGULATOR_TPS6586X=y +CONFIG_REGULATOR_XZ3216=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=y +# CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV is not set +# CONFIG_USB_GSPCA is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_ROCKCHIP_CIF=y +CONFIG_VIDEO_ROCKCHIP_ISP=y +CONFIG_VIDEO_ROCKCHIP_ISP_VERSION_V1X=y +CONFIG_VIDEO_ROCKCHIP_ISP_VERSION_V21=y +CONFIG_VIDEO_ROCKCHIP_ISPP=y +CONFIG_VIDEO_ROCKCHIP_HDMIRX=y +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_VIDEO_ROCKCHIP_RGA=y +CONFIG_VIDEO_LT6911UXC=y +CONFIG_VIDEO_LT7911D=y +CONFIG_VIDEO_RK628_CSI=y +CONFIG_VIDEO_RK628_BT1120=y +CONFIG_VIDEO_TC35874X=y +CONFIG_VIDEO_RK_IRCUT=y +CONFIG_VIDEO_GC8034=y +CONFIG_VIDEO_IMX415=y +CONFIG_VIDEO_IMX464=y +CONFIG_VIDEO_OS04A10=y +CONFIG_VIDEO_OV4689=y +CONFIG_VIDEO_OV5647=y +CONFIG_VIDEO_OV5648=y +CONFIG_VIDEO_OV5695=y +CONFIG_VIDEO_OV7251=y +CONFIG_VIDEO_OV8858=y +CONFIG_VIDEO_OV13850=y +CONFIG_VIDEO_DW9714=y +CONFIG_VIDEO_GC5603=y +# CONFIG_VGA_ARB is not set +CONFIG_DRM=y +CONFIG_DRM_IGNORE_IOTCL_PERMIT=y +CONFIG_DRM_DP_AUX_CHARDEV=y +CONFIG_DRM_LOAD_EDID_FIRMWARE=y +CONFIG_DRM_ROCKCHIP=y +CONFIG_ROCKCHIP_VOP=y +CONFIG_ROCKCHIP_ANALOGIX_DP=y +CONFIG_ROCKCHIP_CDN_DP=y +CONFIG_ROCKCHIP_DW_HDMI=y +CONFIG_ROCKCHIP_DW_MIPI_DSI=y +CONFIG_ROCKCHIP_DW_DP=y +CONFIG_ROCKCHIP_INNO_HDMI=y +CONFIG_ROCKCHIP_LVDS=y +CONFIG_ROCKCHIP_RGB=y +CONFIG_ROCKCHIP_DW_HDCP2=y +CONFIG_DRM_PANEL_SIMPLE=y +CONFIG_DRM_DISPLAY_CONNECTOR=y +CONFIG_DRM_SII902X=y +CONFIG_DRM_DW_HDMI_I2S_AUDIO=y +CONFIG_DRM_DW_HDMI_CEC=y +CONFIG_MALI400=y +CONFIG_MALI450=y +# CONFIG_MALI400_PROFILING is not set +CONFIG_MALI_SHARED_INTERRUPTS=y +CONFIG_MALI_DT=y +CONFIG_MALI_DEVFREQ=y +CONFIG_MALI_MIDGARD=y +CONFIG_MALI_EXPERT=y +CONFIG_MALI_PLATFORM_THIRDPARTY=y +CONFIG_MALI_PLATFORM_THIRDPARTY_NAME="rk" +CONFIG_MALI_DEBUG=y +CONFIG_MALI_PWRSOFT_765=y +CONFIG_MALI_BIFROST=y +CONFIG_MALI_PLATFORM_NAME="rk" +CONFIG_MALI_CSF_SUPPORT=y +CONFIG_MALI_BIFROST_EXPERT=y +CONFIG_MALI_BIFROST_DEBUG=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_ROCKCHIP_MULTI_RGA=y +CONFIG_IEP=y +CONFIG_ROCKCHIP_MPP_SERVICE=y +CONFIG_ROCKCHIP_MPP_RKVDEC=y +CONFIG_ROCKCHIP_MPP_RKVDEC2=y +CONFIG_ROCKCHIP_MPP_RKVENC=y +CONFIG_ROCKCHIP_MPP_RKVENC2=y +CONFIG_ROCKCHIP_MPP_VDPU1=y +CONFIG_ROCKCHIP_MPP_VEPU1=y +CONFIG_ROCKCHIP_MPP_VDPU2=y +CONFIG_ROCKCHIP_MPP_VEPU2=y +CONFIG_ROCKCHIP_MPP_IEP2=y +CONFIG_ROCKCHIP_MPP_JPGDEC=y +CONFIG_ROCKCHIP_MPP_AV1DEC=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_HRTIMER=y +CONFIG_SND_DYNAMIC_MINORS=y +# CONFIG_SND_SUPPORT_OLD_API is not set +CONFIG_SND_SEQUENCER=y +CONFIG_SND_SEQ_DUMMY=y +# CONFIG_SND_PCI is not set +# CONFIG_SND_SPI is not set +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_ROCKCHIP=y +CONFIG_SND_SOC_ROCKCHIP_I2S_TDM=y +CONFIG_SND_SOC_ROCKCHIP_PDM=y +CONFIG_SND_SOC_ROCKCHIP_SPDIF=y +CONFIG_SND_SOC_ROCKCHIP_SPDIFRX=y +CONFIG_SND_SOC_ROCKCHIP_MAX98090=y +CONFIG_SND_SOC_ROCKCHIP_MULTICODECS=y +CONFIG_SND_SOC_ROCKCHIP_RT5645=y +CONFIG_SND_SOC_ROCKCHIP_HDMI=y +CONFIG_SND_SOC_DUMMY_CODEC=y +CONFIG_SND_SOC_ES7202=y +CONFIG_SND_SOC_ES7243E=y +CONFIG_SND_SOC_ES8311=y +CONFIG_SND_SOC_ES8316=y +CONFIG_SND_SOC_ES8323=y +CONFIG_SND_SOC_ES8326=y +CONFIG_SND_SOC_RK3308=y +CONFIG_SND_SOC_RK3328=y +CONFIG_SND_SOC_RK817=y +CONFIG_SND_SOC_RK_CODEC_DIGITAL=y +CONFIG_SND_SOC_RT5616=y +CONFIG_SND_SOC_RT5640=y +CONFIG_SND_SOC_RT5651=y +CONFIG_SND_SOC_SPDIF=y +CONFIG_SND_SIMPLE_CARD=y +CONFIG_HID_BATTERY_STRENGTH=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_MULTITOUCH=y +CONFIG_USB_HIDDEV=y +CONFIG_I2C_HID=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +# CONFIG_USB_DEFAULT_PERSIST is not set +CONFIG_USB_OTG=y +CONFIG_USB_MON=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_OHCI_HCD_PCI is not set +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_ACM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_UAS=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC2=y +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_CH341=m +CONFIG_USB_SERIAL_CP210X=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OTI6858=m +CONFIG_USB_SERIAL_QUALCOMM=m +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_EZUSB_FX2=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_DEBUG_FILES=y +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_ACM=y +CONFIG_USB_CONFIGFS_RNDIS=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_UAC1=y +CONFIG_USB_CONFIGFS_F_UAC2=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_UVC=y +CONFIG_TYPEC_TCPM=y +CONFIG_TYPEC_TCPCI=y +CONFIG_TYPEC_HUSB311=y +CONFIG_TYPEC_FUSB302=y +CONFIG_TYPEC_DP_ALTMODE=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_TEST=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_OF_ARASAN=y +CONFIG_MMC_SDHCI_OF_DWCMSHC=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_ROCKCHIP=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_IS31FL32XX=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_HYM8563=y +CONFIG_RTC_DRV_RK808=y +CONFIG_RTC_DRV_RX8010=y +CONFIG_DMADEVICES=y +CONFIG_PL330_DMA=y +CONFIG_SW_SYNC=y +CONFIG_DMABUF_HEAPS=y +CONFIG_DMABUF_SYSFS_STATS=y +CONFIG_DMABUF_HEAPS_DEFERRED_FREE=y +CONFIG_DMABUF_HEAPS_PAGE_POOL=y +CONFIG_DMABUF_HEAPS_SYSTEM=y +CONFIG_DMABUF_HEAPS_CMA=y +CONFIG_STAGING=y +CONFIG_COMMON_CLK_RK808=y +CONFIG_COMMON_CLK_SCMI=y +CONFIG_COMMON_CLK_PWM=y +CONFIG_ROCKCHIP_CLK_BOOST=y +CONFIG_ROCKCHIP_DDRCLK_SIP=y +CONFIG_ROCKCHIP_DDRCLK_SIP_V2=y +CONFIG_ROCKCHIP_PLL_RK3399=y +CONFIG_MAILBOX=y +CONFIG_IOMMU_LIMIT_IOVA_ALIGNMENT=y +CONFIG_IOMMU_IOVA_ALIGNMENT=4 +CONFIG_ROCKCHIP_IOMMU=y +CONFIG_ARM_SMMU_V3=y +CONFIG_CPU_RK3588=y +CONFIG_ROCKCHIP_CPUINFO=y +CONFIG_ROCKCHIP_GRF=y +CONFIG_ROCKCHIP_IODOMAIN=y +CONFIG_ROCKCHIP_IPA=y +CONFIG_ROCKCHIP_OPP=y +CONFIG_ROCKCHIP_PM_DOMAINS=y +CONFIG_ROCKCHIP_PVTM=y +CONFIG_ROCKCHIP_SUSPEND_MODE=y +CONFIG_ROCKCHIP_SYSTEM_MONITOR=y +CONFIG_ROCKCHIP_VENDOR_STORAGE=y +CONFIG_ROCKCHIP_MMC_VENDOR_STORAGE=y +CONFIG_ROCKCHIP_VENDOR_STORAGE_UPDATE_LOADER=y +CONFIG_FIQ_DEBUGGER=y +CONFIG_FIQ_DEBUGGER_NO_SLEEP=y +CONFIG_FIQ_DEBUGGER_CONSOLE=y +CONFIG_FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE=y +CONFIG_FIQ_DEBUGGER_TRUST_ZONE=y +CONFIG_RK_CONSOLE_THREAD=y +CONFIG_ROCKCHIP_DEBUG=y +CONFIG_PM_DEVFREQ=y +CONFIG_DEVFREQ_GOV_PERFORMANCE=y +CONFIG_DEVFREQ_GOV_POWERSAVE=y +CONFIG_DEVFREQ_GOV_USERSPACE=y +CONFIG_ARM_ROCKCHIP_BUS_DEVFREQ=y +CONFIG_ARM_ROCKCHIP_DMC_DEVFREQ=y +CONFIG_DEVFREQ_EVENT_ROCKCHIP_NOCP=y +CONFIG_IIO=y +CONFIG_ROCKCHIP_SARADC=y +CONFIG_SENSORS_ISL29018=y +CONFIG_SENSORS_TSL2563=y +CONFIG_TSL2583=y +CONFIG_IIO_SYSFS_TRIGGER=y +CONFIG_PWM=y +CONFIG_PWM_ROCKCHIP=y +CONFIG_PHY_ROCKCHIP_CSI2_DPHY=y +CONFIG_PHY_ROCKCHIP_DP=y +CONFIG_PHY_ROCKCHIP_EMMC=y +CONFIG_PHY_ROCKCHIP_INNO_HDMI=y +CONFIG_PHY_ROCKCHIP_INNO_USB2=y +CONFIG_PHY_ROCKCHIP_INNO_USB3=y +CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY=y +CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY=y +CONFIG_PHY_ROCKCHIP_NANENG_EDP=y +CONFIG_PHY_ROCKCHIP_PCIE=y +CONFIG_PHY_ROCKCHIP_SAMSUNG_DCPHY=y +CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX=y +CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX_HDMI=y +CONFIG_PHY_ROCKCHIP_SNPS_PCIE3=y +CONFIG_PHY_ROCKCHIP_TYPEC=y +CONFIG_PHY_ROCKCHIP_USB=y +CONFIG_PHY_ROCKCHIP_USBDP=y +CONFIG_ANDROID=y +CONFIG_ROCKCHIP_EFUSE=y +CONFIG_ROCKCHIP_OTP=y +CONFIG_TEE=y +CONFIG_OPTEE=y +CONFIG_RK_HEADSET=y +CONFIG_ROCKCHIP_RKNPU=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_XFS_FS=y +CONFIG_BTRFS_FS=y +CONFIG_BTRFS_FS_POSIX_ACL=y +# CONFIG_DNOTIFY is not set +CONFIG_FUSE_FS=y +CONFIG_OVERLAY_FS=y +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=936 +CONFIG_FAT_DEFAULT_IOCHARSET="utf8" +CONFIG_EXFAT_FS=y +CONFIG_NTFS_FS=m +CONFIG_NTFS_RW=y +CONFIG_NTFS3_FS=m +CONFIG_NTFS3_LZX_XPRESS=y +CONFIG_NTFS3_FS_POSIX_ACL=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_HUGETLBFS=y +CONFIG_EFIVAR_FS=y +CONFIG_JFFS2_FS=y +CONFIG_UBIFS_FS=y +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_LZO=y +CONFIG_SQUASHFS_XZ=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_SWAP=y +CONFIG_NFSD=y +CONFIG_NFSD_V3_ACL=y +CONFIG_NFSD_V4=y +CONFIG_NFSD_BLOCKLAYOUT=y +CONFIG_NFSD_SCSILAYOUT=y +CONFIG_NFSD_FLEXFILELAYOUT=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_936=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y +CONFIG_UNICODE=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_USER_API_HASH=y +CONFIG_CRYPTO_USER_API_SKCIPHER=y +CONFIG_CRYPTO_DEV_ROCKCHIP=y +CONFIG_CRYPTO_DEV_ROCKCHIP_V1=y +CONFIG_CRYPTO_DEV_ROCKCHIP_DEV=y +CONFIG_CRC_T10DIF=y +CONFIG_CRC7=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_DMA_CMA=y +CONFIG_PRINTK_TIME=y +CONFIG_PRINTK_TIME_FROM_ARM_ARCH_TIMER=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_INFO=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0 +CONFIG_SCHEDSTATS=y +CONFIG_DEBUG_CREDENTIALS=y +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_FUNCTION_TRACER=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_LKDTM=y diff --git a/Linux-Kernel.si4project/Backup/rk3588s-lubancat-csi2(6413).dtsi b/Linux-Kernel.si4project/Backup/rk3588s-lubancat-csi2(6413).dtsi new file mode 100755 index 0000000000000..692c72f0043c3 --- /dev/null +++ b/Linux-Kernel.si4project/Backup/rk3588s-lubancat-csi2(6413).dtsi @@ -0,0 +1,590 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2020 Rockchip Electronics Co., Ltd. + * + */ + +/ { + ext_cam_37m_clk: external-camera-37m-clock { + compatible = "fixed-clock"; + clock-frequency = <37125000>; + clock-output-names = "ext_cam_37m_clk"; + #clock-cells = <0>; + }; + ext_cam_27m_clk: external-camera-27m-clock { + compatible = "fixed-clock"; + clock-frequency = <27000000>; + clock-output-names = "ext_cam_27m_clk"; + #clock-cells = <0>; + }; + + vdd_cam_5v: vdd-cam-5v-regulator { + compatible = "regulator-fixed"; + regulator-name = "vdd_cam_5v"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + cam_dovdd: cam-dovdd { + compatible = "regulator-fixed"; + regulator-name = "cam_dovdd"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + vin-supply = <&vdd_cam_5v>; + }; + + cam_avdd: cam-avdd { + compatible = "regulator-fixed"; + regulator-name = "cam_avdd"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + vin-supply = <&vdd_cam_5v>; + }; + + cam_dvdd: cam-dvdd { + compatible = "regulator-fixed"; + regulator-name = "cam_dvdd"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + vin-supply = <&vdd_cam_5v>; + }; +}; + + +&i2c1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c1m2_xfer>; + + dw9714_0: dw9714-0@c { + status = "okay"; + compatible = "dongwoon,dw9714"; + reg = <0xc>; + rockchip,camera-module-index = <0>; + rockchip,vcm-max-current = <100>; + rockchip,vcm-start-current = <0>; + rockchip,vcm-rated-current = <100>; + rockchip,vcm-step-mode = <0xd>; + rockchip,vcm-dlc-enable = <0>; + rockchip,vcm-mclk = <0>; + rockchip,vcm-t-src = <0>; + rockchip,camera-module-facing = "back"; + }; + + gc5603_0: gc5603-0@31 { + compatible = "galaxycore,gc5603"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_27m_clk>; + clock-names = "ext_cam_27m_clk"; + pwdn-gpios = <&gpio1 RK_PA3 GPIO_ACTIVE_LOW>; + rotation = <180>; + + port { + gc5603_out0: endpoint { + remote-endpoint = <&dcphy0_in_gc5603>; + data-lanes = <1 2>; + }; + }; + }; + + imx415_0: imx415-0@1a { + compatible = "sony,imx415"; + status = "okay"; + reg = <0x1a>; + clocks = <&ext_cam_37m_clk>; + clock-names = "xvclk"; + avdd-supply = <&cam_avdd>; + dovdd-supply = <&cam_dovdd>; + dvdd-supply = <&cam_dvdd>; + reset-gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_LOW>; + + rockchip,camera-module-index = <0>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "CMK-OT2022-PX1"; + rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; + port { + imx415_out0: endpoint { + remote-endpoint = <&dcphy0_in_imx415>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&mipi_dcphy0 { + status = "okay"; +}; + +&csi2_dcphy0 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + dcphy0_in_gc5603: endpoint@0 { + reg = <0>; + remote-endpoint = <&gc5603_out0>; + data-lanes = <1 2>; + }; + + dcphy0_in_imx415: endpoint@1 { + reg = <1>; + remote-endpoint = <&imx415_out0>; + data-lanes = <1 2 3 4>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + csidcphy0_out: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi0_csi2_input>; + }; + }; + }; +}; + +&mipi0_csi2 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + mipi0_csi2_input: endpoint@1 { + reg = <1>; + remote-endpoint = <&csidcphy0_out>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + mipi0_csi2_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&cif_mipi0_in0>; + }; + }; + }; +}; + +&i2c5 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c5m3_xfer>; + + dw9714_1: dw9714-1@c { + status = "okay"; + compatible = "dongwoon,dw9714"; + reg = <0xc>; + rockchip,camera-module-index = <0>; + rockchip,vcm-max-current = <100>; + rockchip,vcm-start-current = <0>; + rockchip,vcm-rated-current = <100>; + rockchip,vcm-step-mode = <0xd>; + rockchip,vcm-dlc-enable = <0>; + rockchip,vcm-mclk = <0>; + rockchip,vcm-t-src = <0>; + rockchip,camera-module-facing = "back"; + }; + + gc5603_1: gc5603-1@31 { + compatible = "galaxycore,gc5603"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_27m_clk>; + clock-names = "ext_cam_27m_clk"; + pwdn-gpios = <&gpio4 RK_PA5 GPIO_ACTIVE_LOW>; + rotation = <180>; + + port { + gc5603_out1: endpoint { + remote-endpoint = <&dcphy1_in_gc5603>; + data-lanes = <1 2>; + }; + }; + }; + + imx415_1: imx415-1@1a { + compatible = "sony,imx415"; + status = "okay"; + reg = <0x1a>; + clocks = <&ext_cam_37m_clk>; + clock-names = "xvclk"; + avdd-supply = <&cam_avdd>; + dovdd-supply = <&cam_dovdd>; + dvdd-supply = <&cam_dvdd>; + reset-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_LOW>; + + rockchip,camera-module-index = <1>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "CMK-OT2022-PX1"; + rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; + port { + imx415_out1: endpoint { + remote-endpoint = <&dcphy1_in_imx415>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&mipi_dcphy1 { + status = "okay"; +}; + +&csi2_dcphy1 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + dcphy1_in_gc5603: endpoint@0 { + reg = <0>; + remote-endpoint = <&gc5603_out1>; + data-lanes = <1 2>; + }; + + dcphy1_in_imx415: endpoint@1 { + reg = <1>; + remote-endpoint = <&imx415_out1>; + data-lanes = <1 2 3 4>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + csidcphy1_out: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi1_csi2_input>; + }; + }; + }; +}; + +&mipi1_csi2 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + mipi1_csi2_input: endpoint@1 { + reg = <1>; + remote-endpoint = <&csidcphy1_out>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + mipi1_csi2_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&cif_mipi1_in0>; + }; + }; + }; +}; + +&i2c6 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c6m3_xfer>; + + dw9714: dw9714@c { + status = "okay"; + compatible = "dongwoon,dw9714"; + reg = <0xc>; + rockchip,camera-module-index = <0>; + rockchip,vcm-max-current = <100>; + rockchip,vcm-start-current = <0>; + rockchip,vcm-rated-current = <100>; + rockchip,vcm-step-mode = <0xd>; + rockchip,vcm-dlc-enable = <0>; + rockchip,vcm-mclk = <0>; + rockchip,vcm-t-src = <0>; + rockchip,camera-module-facing = "back"; + }; + + gc5603_2: gc5603_2@31 { + compatible = "galaxycore,gc5603"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_27m_clk>; + clock-names = "ext_cam_27m_clk"; + pwdn-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_LOW>; + rotation = <180>; + + port { + gc5603_out2: endpoint { + remote-endpoint = <&dphy0_in_gc5603>; + data-lanes = <1 2>; + }; + }; + }; + + imx415_2: imx415-2@1a { + compatible = "sony,imx415"; + status = "okay"; + reg = <0x1a>; + clocks = <&ext_cam_37m_clk>; + clock-names = "xvclk"; + avdd-supply = <&cam_avdd>; + dovdd-supply = <&cam_dovdd>; + dvdd-supply = <&cam_dvdd>; + reset-gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_LOW>; + + rockchip,camera-module-index = <2>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "CMK-OT2022-PX1"; + rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; + port { + imx415_out2: endpoint { + remote-endpoint = <&dphy0_in_imx415>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&csi2_dphy0_hw { + status = "okay"; +}; + +&csi2_dphy0 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + dphy0_in_gc5603: endpoint@0 { + reg = <0>; + remote-endpoint = <&gc5603_out2>; + data-lanes = <1 2 3 4>; + }; + + dphy0_in_imx415: endpoint@1 { + reg = <1>; + remote-endpoint = <&imx415_out2>; + data-lanes = <1 2 3 4>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + csidphy0_out: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi2_csi2_input>; + }; + }; + }; +}; + +&mipi2_csi2 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + mipi2_csi2_input: endpoint@1 { + reg = <1>; + remote-endpoint = <&csidphy0_out>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + mipi2_csi2_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&cif_mipi2_in0>; + }; + }; + }; +}; + + +&rkcif { + status = "okay"; +}; + +&rkcif_mmu { + status = "okay"; +}; + +&rkcif_mipi_lvds { + status = "okay"; + + port { + cif_mipi0_in0: endpoint { + remote-endpoint = <&mipi0_csi2_output>; + }; + }; +}; + +&rkcif_mipi_lvds_sditf { + status = "okay"; + + port { + mipi_lvds_sditf: endpoint { + remote-endpoint = <&isp0_vir0>; + }; + }; +}; + +&rkcif_mipi_lvds1 { + status = "okay"; + + port { + cif_mipi1_in0: endpoint { + remote-endpoint = <&mipi1_csi2_output>; + }; + }; +}; + +&rkcif_mipi_lvds1_sditf { + status = "okay"; + + port { + mipi_lvds1_sditf: endpoint { + remote-endpoint = <&isp0_vir1>; + }; + }; +}; + +&rkcif_mipi_lvds2 { + status = "okay"; + + port { + cif_mipi2_in0: endpoint { + remote-endpoint = <&mipi2_csi2_output>; + }; + }; +}; + +&rkcif_mipi_lvds2_sditf { + status = "okay"; + + port { + mipi_lvds2_sditf: endpoint { + remote-endpoint = <&isp1_vir0>; + }; + }; +}; + +&rkisp0 { + status = "okay"; +}; + +&isp0_mmu { + status = "okay"; +}; + +&rkisp0_vir0 { + status = "okay"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + isp0_vir0: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi_lvds_sditf>; + }; + }; +}; + +&rkisp0_vir1 { + status = "okay"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + isp0_vir1: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi_lvds1_sditf>; + }; + }; +}; + + +&rkisp1 { + status = "okay"; +}; + +&isp1_mmu { + status = "okay"; +}; + +&rkisp1_vir0 { + status = "okay"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + isp1_vir0: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi_lvds2_sditf>; + }; + }; +}; diff --git a/Linux-Kernel.si4project/Backup/rk3588s-lubancat-csi2(6446).dtsi b/Linux-Kernel.si4project/Backup/rk3588s-lubancat-csi2(6446).dtsi new file mode 100755 index 0000000000000..c9a1b56d0692b --- /dev/null +++ b/Linux-Kernel.si4project/Backup/rk3588s-lubancat-csi2(6446).dtsi @@ -0,0 +1,597 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2020 Rockchip Electronics Co., Ltd. + * + */ + +/ { + ext_cam_37m_clk: external-camera-37m-clock { + compatible = "fixed-clock"; + clock-frequency = <37125000>; + clock-output-names = "ext_cam_37m_clk"; + #clock-cells = <0>; + }; + ext_cam_27m_clk: external-camera-27m-clock { + compatible = "fixed-clock"; + clock-frequency = <27000000>; + clock-output-names = "ext_cam_27m_clk"; + #clock-cells = <0>; + }; + + vdd_cam_5v: vdd-cam-5v-regulator { + compatible = "regulator-fixed"; + regulator-name = "vdd_cam_5v"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + cam_dovdd: cam-dovdd { + compatible = "regulator-fixed"; + regulator-name = "cam_dovdd"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + vin-supply = <&vdd_cam_5v>; + }; + + cam_avdd: cam-avdd { + compatible = "regulator-fixed"; + regulator-name = "cam_avdd"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + vin-supply = <&vdd_cam_5v>; + }; + + cam_dvdd: cam-dvdd { + compatible = "regulator-fixed"; + regulator-name = "cam_dvdd"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + vin-supply = <&vdd_cam_5v>; + }; +}; + +//CAM 0 +/* Link path: sensor->csi2_dcphy0->mipi0_csi2->rkcif_mipi_lvds--->rkcif_mipi_lvds_sditf->rkisp0_vir0 */ +&i2c1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c1m2_xfer>; + + dw9714_0: dw9714-0@c { + status = "disabled"; + compatible = "dongwoon,dw9714"; + reg = <0xc>; + rockchip,camera-module-index = <0>; + rockchip,vcm-max-current = <100>; + rockchip,vcm-start-current = <0>; + rockchip,vcm-rated-current = <100>; + rockchip,vcm-step-mode = <0xd>; + rockchip,vcm-dlc-enable = <0>; + rockchip,vcm-mclk = <0>; + rockchip,vcm-t-src = <0>; + rockchip,camera-module-facing = "back"; + }; + + gc5603_0: gc5603-0@31 { + compatible = "galaxycore,gc5603"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_27m_clk>; + clock-names = "ext_cam_27m_clk"; + pwdn-gpios = <&gpio1 RK_PA3 GPIO_ACTIVE_LOW>; + rotation = <180>; + + port { + gc5603_out0: endpoint { + remote-endpoint = <&dcphy0_in_gc5603>; + data-lanes = <1 2>; + }; + }; + }; + + imx415_0: imx415-0@1a { + compatible = "sony,imx415"; + status = "okay"; + reg = <0x1a>; + clocks = <&ext_cam_37m_clk>; + clock-names = "xvclk"; + avdd-supply = <&cam_avdd>; + dovdd-supply = <&cam_dovdd>; + dvdd-supply = <&cam_dvdd>; + reset-gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_LOW>; + + rockchip,camera-module-index = <0>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "CMK-OT2022-PX1"; + rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; + port { + imx415_out0: endpoint { + remote-endpoint = <&dcphy0_in_imx415>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&mipi_dcphy0 { + status = "okay"; +}; + +&csi2_dcphy0 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + dcphy0_in_gc5603: endpoint@0 { + reg = <0>; + remote-endpoint = <&gc5603_out0>; + data-lanes = <1 2>; + }; + + dcphy0_in_imx415: endpoint@1 { + reg = <1>; + remote-endpoint = <&imx415_out0>; + data-lanes = <1 2 3 4>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + csidcphy0_out: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi0_csi2_input>; + }; + }; + }; +}; + +&mipi0_csi2 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + mipi0_csi2_input: endpoint@1 { + reg = <1>; + remote-endpoint = <&csidcphy0_out>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + mipi0_csi2_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&cif_mipi0_in0>; + }; + }; + }; +}; + + +//CAM 1 +/* Link path: sensor->csi2_dcphy1->mipi1_csi2->rkcif_mipi_lvds1--->rkcif_mipi_lvds1_sditf->rkisp0_vir1 */ +&i2c5 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c5m3_xfer>; + + dw9714_1: dw9714-1@c { + status = "disabled"; + compatible = "dongwoon,dw9714"; + reg = <0xc>; + rockchip,camera-module-index = <0>; + rockchip,vcm-max-current = <100>; + rockchip,vcm-start-current = <0>; + rockchip,vcm-rated-current = <100>; + rockchip,vcm-step-mode = <0xd>; + rockchip,vcm-dlc-enable = <0>; + rockchip,vcm-mclk = <0>; + rockchip,vcm-t-src = <0>; + rockchip,camera-module-facing = "back"; + }; + + gc5603_1: gc5603-1@31 { + compatible = "galaxycore,gc5603"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_27m_clk>; + clock-names = "ext_cam_27m_clk"; + pwdn-gpios = <&gpio4 RK_PA5 GPIO_ACTIVE_LOW>; + rotation = <180>; + + port { + gc5603_out1: endpoint { + remote-endpoint = <&dcphy1_in_gc5603>; + data-lanes = <1 2>; + }; + }; + }; + + imx415_1: imx415-1@1a { + compatible = "sony,imx415"; + status = "okay"; + reg = <0x1a>; + clocks = <&ext_cam_37m_clk>; + clock-names = "xvclk"; + avdd-supply = <&cam_avdd>; + dovdd-supply = <&cam_dovdd>; + dvdd-supply = <&cam_dvdd>; + reset-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_LOW>; + + rockchip,camera-module-index = <1>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "CMK-OT2022-PX1"; + rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; + port { + imx415_out1: endpoint { + remote-endpoint = <&dcphy1_in_imx415>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&mipi_dcphy1 { + status = "okay"; +}; + +&csi2_dcphy1 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + dcphy1_in_gc5603: endpoint@0 { + reg = <0>; + remote-endpoint = <&gc5603_out1>; + data-lanes = <1 2>; + }; + + dcphy1_in_imx415: endpoint@1 { + reg = <1>; + remote-endpoint = <&imx415_out1>; + data-lanes = <1 2 3 4>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + csidcphy1_out: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi1_csi2_input>; + }; + }; + }; +}; + +&mipi1_csi2 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + mipi1_csi2_input: endpoint@1 { + reg = <1>; + remote-endpoint = <&csidcphy1_out>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + mipi1_csi2_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&cif_mipi1_in0>; + }; + }; + }; +}; + + +CAM2 +/* Link path: sensor->csi2_dphy1->mipi2_csi2->rkcif_mipi_lvds2--->rkcif_mipi_lvds2_sditf->rkisp0_vir2 */ +&i2c6 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c6m3_xfer>; + + dw9714: dw9714@c { + status = "disabled"; + compatible = "dongwoon,dw9714"; + reg = <0xc>; + rockchip,camera-module-index = <0>; + rockchip,vcm-max-current = <100>; + rockchip,vcm-start-current = <0>; + rockchip,vcm-rated-current = <100>; + rockchip,vcm-step-mode = <0xd>; + rockchip,vcm-dlc-enable = <0>; + rockchip,vcm-mclk = <0>; + rockchip,vcm-t-src = <0>; + rockchip,camera-module-facing = "back"; + }; + + gc5603_2: gc5603_2@31 { + compatible = "galaxycore,gc5603"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_27m_clk>; + clock-names = "ext_cam_27m_clk"; + pwdn-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_LOW>; + rotation = <180>; + + port { + gc5603_out2: endpoint { + remote-endpoint = <&dphy0_in_gc5603>; + data-lanes = <1 2>; + }; + }; + }; + + imx415_2: imx415-2@1a { + compatible = "sony,imx415"; + status = "okay"; + reg = <0x1a>; + clocks = <&ext_cam_37m_clk>; + clock-names = "xvclk"; + avdd-supply = <&cam_avdd>; + dovdd-supply = <&cam_dovdd>; + dvdd-supply = <&cam_dvdd>; + reset-gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_LOW>; + + rockchip,camera-module-index = <2>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "CMK-OT2022-PX1"; + rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; + port { + imx415_out2: endpoint { + remote-endpoint = <&dphy0_in_imx415>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&csi2_dphy0_hw { + status = "okay"; +}; + +&csi2_dphy0 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + dphy0_in_gc5603: endpoint@0 { + reg = <0>; + remote-endpoint = <&gc5603_out2>; + data-lanes = <1 2>; + }; + + dphy0_in_imx415: endpoint@1 { + reg = <1>; + remote-endpoint = <&imx415_out2>; + data-lanes = <1 2 3 4>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + csidphy0_out: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi2_csi2_input>; + }; + }; + }; +}; + +&mipi2_csi2 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + mipi2_csi2_input: endpoint@1 { + reg = <1>; + remote-endpoint = <&csidphy0_out>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + mipi2_csi2_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&cif_mipi2_in0>; + }; + }; + }; +}; + + +&rkcif { + status = "okay"; +}; + +&rkcif_mmu { + status = "okay"; +}; + +&rkcif_mipi_lvds { + status = "okay"; + + port { + cif_mipi0_in0: endpoint { + remote-endpoint = <&mipi0_csi2_output>; + }; + }; +}; + +&rkcif_mipi_lvds_sditf { + status = "okay"; + + port { + mipi_lvds_sditf: endpoint { + remote-endpoint = <&isp0_vir0>; + }; + }; +}; + +&rkcif_mipi_lvds1 { + status = "okay"; + + port { + cif_mipi1_in0: endpoint { + remote-endpoint = <&mipi1_csi2_output>; + }; + }; +}; + +&rkcif_mipi_lvds1_sditf { + status = "okay"; + + port { + mipi_lvds1_sditf: endpoint { + remote-endpoint = <&isp0_vir1>; + }; + }; +}; + +&rkcif_mipi_lvds2 { + status = "okay"; + + port { + cif_mipi2_in0: endpoint { + remote-endpoint = <&mipi2_csi2_output>; + }; + }; +}; + +&rkcif_mipi_lvds2_sditf { + status = "okay"; + + port { + mipi_lvds2_sditf: endpoint { + remote-endpoint = <&isp1_vir0>; + }; + }; +}; + +&rkisp0 { + status = "okay"; +}; + +&isp0_mmu { + status = "okay"; +}; + +&rkisp0_vir0 { + status = "okay"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + isp0_vir0: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi_lvds_sditf>; + }; + }; +}; + +&rkisp0_vir1 { + status = "okay"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + isp0_vir1: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi_lvds1_sditf>; + }; + }; +}; + + +&rkisp1 { + status = "okay"; +}; + +&isp1_mmu { + status = "okay"; +}; + +&rkisp1_vir0 { + status = "okay"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + isp1_vir0: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi_lvds2_sditf>; + }; + }; +}; diff --git a/Linux-Kernel.si4project/Linux-Kernel.sip_sym b/Linux-Kernel.si4project/Linux-Kernel.sip_sym new file mode 100755 index 0000000000000000000000000000000000000000..bedf02d4c0805a946e5bbb34e1ac6f6836e20347 GIT binary patch literal 9512 zcmeIuF%1A96hpxdBq%^Me$dBf0Tbl9ymY60J{Mc03auTum%u+0Wt(;hO;dY*~U|{HEU|?Z@f`9-2|9`;_?ep literal 0 HcmV?d00001 diff --git a/Linux-Kernel.si4project/Linux-Kernel.sip_xf b/Linux-Kernel.si4project/Linux-Kernel.sip_xf new file mode 100755 index 0000000000000000000000000000000000000000..908d0a82a4f9ace8a5049917a1b57c39d0c010f9 GIT binary patch literal 512 ecmWd>U|{HEU|?Z@f`9-2|9`;_?ep literal 0 HcmV?d00001 diff --git a/Linux-Kernel.si4project/Linux-Kernel.sip_xm b/Linux-Kernel.si4project/Linux-Kernel.sip_xm new file mode 100755 index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4 GIT binary patch literal 4 LcmZQzU|;|M00aO5 literal 0 HcmV?d00001 diff --git a/Linux-Kernel.si4project/Linux-Kernel.sip_xr b/Linux-Kernel.si4project/Linux-Kernel.sip_xr new file mode 100755 index 0000000000000000000000000000000000000000..908d0a82a4f9ace8a5049917a1b57c39d0c010f9 GIT binary patch literal 512 ecmWd>U|{HEU|?Z@f`9-2|9`;_?ep literal 0 HcmV?d00001 diff --git a/Linux-Kernel.si4project/Linux-Kernel.sip_xsb b/Linux-Kernel.si4project/Linux-Kernel.sip_xsb new file mode 100755 index 0000000000000000000000000000000000000000..08e7df176454f3ee5eeda13efa0adaa54828dfd8 GIT binary patch literal 4096 ocmeIu0Sy2E0K%a6Pi+qe5hx58Fkrxd0RsjM7%*VKfPwdc0T2KH0RR91 literal 0 HcmV?d00001 diff --git a/Linux-Kernel.si4project/Linux-Kernel.sip_xsd b/Linux-Kernel.si4project/Linux-Kernel.sip_xsd new file mode 100755 index 0000000000000000000000000000000000000000..ab7c4c4910bd9cac7d50eb763d3a71ba0995529f GIT binary patch literal 368 zcmbOv!obkUz`(-5$S|Nl0BC>60J{Mc03auTum%u+0WuP1hO^yZ+{y*yGW`GlA4FjR am%#GK5-*rRfeJC>!HjUWiuuuOAb9|W3=XmY literal 0 HcmV?d00001 diff --git a/Linux-Kernel.si4project/Linux-Kernel.siproj b/Linux-Kernel.si4project/Linux-Kernel.siproj new file mode 100755 index 0000000000000000000000000000000000000000..d9e365f9b6806ef75f0252732b1b5372bef44711 GIT binary patch literal 840 zcmXqDXJF`LU|?ZnU|;~@fB*mg_W&_Q!H5WfzrgrT%u6ZC&rFF)Ov;Q;&(4fbOe{*y oFf-As$juoMVbE}P(b??1w-`ZbALL;m1qNyH`?2~TB#ABo008PKfdBvi literal 0 HcmV?d00001 diff --git a/Linux-Kernel.si4project/Linux-Kernel.siproj_settings.xml b/Linux-Kernel.si4project/Linux-Kernel.siproj_settings.xml new file mode 100755 index 0000000000000..4eb692e28e714 --- /dev/null +++ b/Linux-Kernel.si4project/Linux-Kernel.siproj_settings.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi index ce719ce380d95..3938fef235af0 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi @@ -23,6 +23,12 @@ clock-output-names = "ext_cam_24m_clk"; #clock-cells = <0>; }; + ext_cam_19d2m_clk: external-camera-19d2m-clock { + compatible = "fixed-clock"; + clock-frequency = <19200000>; + clock-output-names = "ext_cam_19d2m_clk"; + #clock-cells = <0>; + }; vdd_cam_5v: vdd-cam-5v-regulator { compatible = "regulator-fixed"; @@ -71,56 +77,36 @@ pinctrl-names = "default"; pinctrl-0 = <&i2c1m2_xfer>; - gc5603_0: gc5603@31 { - compatible = "galaxycore,gc5603"; - status = "disabled"; - reg = <0x31>; - clocks = <&ext_cam_24m_clk>; - clock-names = "xvclk"; - pwdn-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_LOW>; - - rockchip,camera-module-index = <1>; - rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "GCDS01A"; - rockchip,camera-module-lens-name = "default"; - port { - gc5603_out0: endpoint { - remote-endpoint = <&dcphy0_in_gc5603>; - data-lanes = <1 2>; - }; - }; - }; - - imx415_0: imx415@1a { - compatible = "sony,imx415"; - status = "disabled"; - reg = <0x1a>; - clocks = <&ext_cam_37m_clk>; + ov50h40_0: ov50h40@10 { + compatible = "ov,ov50h40"; + status = "enabled"; + reg = <0x10>; + clocks = <&ext_cam_19d2m_clk>; clock-names = "xvclk"; avdd-supply = <&cam_avdd>; dovdd-supply = <&cam_dovdd>; dvdd-supply = <&cam_dvdd>; reset-gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_LOW>; - rockchip,camera-module-index = <2>; + rockchip,camera-module-index = <1>; rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "CMK-OT2022-PX1"; - rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; + rockchip,camera-module-name = "Transsion"; + rockchip,camera-module-lens-name = "CM401-OIS"; port { - imx415_out0: endpoint { - remote-endpoint = <&dcphy0_in_imx415>; - data-lanes = <1 2 3 4>; + ov50h40_0_out0: endpoint { + remote-endpoint = <&dcphy0_in_ov50h40>; + data-lanes = <1 2 3>; }; }; }; }; &mipi_dcphy0 { - status = "disabled"; + status = "okay"; }; &csi2_dcphy0 { - status = "disabled"; + status = "okay"; ports { #address-cells = <1>; @@ -131,15 +117,10 @@ #address-cells = <1>; #size-cells = <0>; - dcphy0_in_gc5603: endpoint@0 { + dcphy0_in_ov50h40: endpoint@0 { reg = <0>; - remote-endpoint = <&gc5603_out0>; - data-lanes = <1 2>; - }; - dcphy0_in_imx415: endpoint@1 { - reg = <1>; - remote-endpoint = <&imx415_out0>; - data-lanes = <1 2 3 4>; + remote-endpoint = <&ov50h40_0_out0>; + data-lanes = <1 2 3>; }; }; @@ -157,7 +138,7 @@ }; &mipi0_csi2 { - status = "disabled"; + status = "okay"; ports { #address-cells = <1>; @@ -444,7 +425,7 @@ }; &rkcif_mipi_lvds { - status = "disabled"; + status = "okay"; port { cif_mipi0_in0: endpoint { @@ -454,7 +435,7 @@ }; &rkcif_mipi_lvds_sditf { - status = "disabled"; + status = "okay"; port { mipi_lvds_sditf: endpoint { @@ -512,7 +493,7 @@ }; &rkisp0_vir0 { - status = "disabled"; + status = "okay"; port { #address-cells = <1>; diff --git a/arch/arm64/configs/lubancat_linux_rk3588_defconfig b/arch/arm64/configs/lubancat_linux_rk3588_defconfig index 90c2db44237f8..bc6e3a8c026e0 100644 --- a/arch/arm64/configs/lubancat_linux_rk3588_defconfig +++ b/arch/arm64/configs/lubancat_linux_rk3588_defconfig @@ -494,6 +494,7 @@ CONFIG_VIDEO_OV8858=y CONFIG_VIDEO_OV13850=y CONFIG_VIDEO_GC5603=y CONFIG_VIDEO_GC8613=y +CONFIG_VIDEO_OV50H40=y # CONFIG_VGA_ARB is not set CONFIG_DRM=y CONFIG_DRM_IGNORE_IOTCL_PERMIT=y diff --git a/arch/arm64/configs/rockchip_defconfig b/arch/arm64/configs/rockchip_defconfig index b41ba0407d476..8badd772afce5 100644 --- a/arch/arm64/configs/rockchip_defconfig +++ b/arch/arm64/configs/rockchip_defconfig @@ -602,6 +602,7 @@ CONFIG_VIDEO_GC8034=y CONFIG_VIDEO_IMX415=y CONFIG_VIDEO_OV02B10=y CONFIG_VIDEO_OV50C40=y +CONFIG_VIDEO_OV50H40=y CONFIG_VIDEO_OV5695=y CONFIG_VIDEO_OV8858=y CONFIG_VIDEO_OV13850=y diff --git a/arch/arm64/configs/rockchip_linux_defconfig b/arch/arm64/configs/rockchip_linux_defconfig index 65486cd3afd9d..9dd6af23db40d 100644 --- a/arch/arm64/configs/rockchip_linux_defconfig +++ b/arch/arm64/configs/rockchip_linux_defconfig @@ -319,6 +319,7 @@ CONFIG_VIDEO_RK_IRCUT=y CONFIG_VIDEO_GC8034=y CONFIG_VIDEO_GC5603=y CONFIG_VIDEO_GC8613=y +CONFIG_VIDEO_OV50H40=y CONFIG_VIDEO_IMX415=y CONFIG_VIDEO_IMX464=y CONFIG_VIDEO_OS04A10=y diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 1ca47ee97f21c..534f53dcc5161 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1601,6 +1601,17 @@ config VIDEO_OV50C40 This is a Video4Linux2 sensor driver for the Omnivision OV50C40 camera sensor with a MIPI CSI-2 interface. +config VIDEO_OV50H40 + tristate "OmniVision OV50H40 sensor support" + depends on OF + depends on GPIOLIB && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + select VIDEO_OTP_EEPROM + help + This is a Video4Linux2 sensor driver for the Omnivision + OV50H40 camera sensor with a MIPI CSI-2 interface. + config VIDEO_OV5640 tristate "OmniVision OV5640 sensor support" depends on OF diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index b67ff645199f6..34f2ed04a341d 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -77,6 +77,7 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_OS02G10) += os02g10.o obj-$(CONFIG_VIDEO_OS03B10) += os03b10.o obj-$(CONFIG_VIDEO_OS04A10) += os04a10.o +obj-$(CONFIG_VIDEO_OV50H40) += ov50h40.o obj-$(CONFIG_VIDEO_OS05A20) += os05a20.o obj-$(CONFIG_VIDEO_OS08A20) += os08a20.o obj-$(CONFIG_VIDEO_OV02B10) += ov02b10.o diff --git a/drivers/media/i2c/gc5603.c b/drivers/media/i2c/gc5603.c index dc7a8a455956a..8db6c218d2f26 100644 --- a/drivers/media/i2c/gc5603.c +++ b/drivers/media/i2c/gc5603.c @@ -20,11 +20,13 @@ * V0.0X01.0X09 adjust supply sequence to suit spec */ //#define DEBUG +#include #include #include #include #include #include +#include #include #include #include @@ -45,7 +47,13 @@ #include #include #include +#include +#define OIS_On +#ifdef OIS_On +#define CM421_SLAVE_ADDR 0x30 +#define CM421_REG_VALUE_16bit 2 +#endif #define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x08) #define GC5603_NAME "gc5603" @@ -170,7 +178,6 @@ struct gc5603 { u32 flip; }; - static const struct regval gc5603_2960x1666_regs_2lane[] = { //version 1.3 //mclk 27Mhz @@ -340,6 +347,7 @@ static const struct gc5603_mode supported_modes[] = { static const s64 link_freq_menu_items[] = { MIPI_FREQ_848M }; + static int gc5603_write_reg(struct i2c_client *client, u16 reg, u32 len, u32 val) { @@ -418,6 +426,88 @@ static int gc5603_read_reg(struct i2c_client *client, u16 reg, unsigned int len, dev_info(&client->dev, "gc5603 read reg(0x%x val:0x%x) \n", reg, *val); } + +#ifdef OIS_On +static int CM421_write_reg(struct i2c_client *client, + unsigned char dev_addr, + u16 reg, + unsigned int len, + u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + int ret; + + struct i2c_msg message; + + if (len > 4) + return -EINVAL; + + buf[0] = (reg >> 8) & 0xFF;//寄存器地址 + 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++]; + + message.addr = dev_addr;//设备地址 + message.buf = buf; + message.flags = 0;//写标志 + message.len = 4; + + ret = i2c_transfer(client->adapter, &message, 1); + if (ret < 0) + { + dev_err(&client->dev, "Failed to write 0x%x,0x%x\n", reg, val); + return -EIO; + } + + return 0; +} + +static int CM421_read_reg(struct i2c_client *client, + unsigned char dev_addr, + 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 = dev_addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = dev_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; +} +#endif static int gc5603_get_reso_dist(const struct gc5603_mode *mode, struct v4l2_mbus_framefmt *framefmt) { @@ -514,7 +604,10 @@ static int gc5603_set_gain(struct gc5603 *gc5603, u32 gain) uint16_t i = 0; uint16_t total = 0; uint16_t temp = 0; - +#ifdef OIS_On + int tmp1, tmp2; + struct device *dev = &gc5603->client->dev; +#endif for (i = 0; i < total; i++) { if ((gain_level_table[i] <= gain) && (gain < gain_level_table[i+1])) @@ -543,6 +636,13 @@ static int gc5603_set_gain(struct gc5603 *gc5603, u32 gain) ret |= gc5603_write_reg(gc5603->client, 0x0064,gc5603_REG_VALUE_08BIT,(temp >> 6)); ret |= gc5603_write_reg(gc5603->client, 0x0065,gc5603_REG_VALUE_08BIT,((temp&0x3f) << 2) ); +#ifdef OIS_On + CM421_read_reg(gc5603->client, CM421_SLAVE_ADDR, 0x99ce, CM421_REG_VALUE_16bit, &tmp1); + dev_info(dev, "position 0x99ce= 0x%x\n", tmp1); + CM421_read_reg(gc5603->client, CM421_SLAVE_ADDR, 0x99d0, CM421_REG_VALUE_16bit, &tmp2); + dev_info(dev, "position 0x99d0= 0x%x\n", tmp2); +#endif + return ret; } @@ -1087,7 +1187,11 @@ static int gc5603_s_stream(struct v4l2_subdev *sd, int on) { struct gc5603 *gc5603 = to_gc5603(sd); struct i2c_client *client = gc5603->client; + struct device *dev = &gc5603->client->dev; int ret = 0; +#ifdef OIS_On + int value2; +#endif mutex_lock(&gc5603->mutex); on = !!on; @@ -1113,6 +1217,21 @@ static int gc5603_s_stream(struct v4l2_subdev *sd, int on) } gc5603->streaming = on; +#ifdef OIS_On + //Init OIS Function: + CM421_write_reg(gc5603->client, CM421_SLAVE_ADDR, 0x0018, CM421_REG_VALUE_16bit, 0x0001); + msleep(100); + CM421_write_reg(gc5603->client, CM421_SLAVE_ADDR, 0x9e18, CM421_REG_VALUE_16bit, 0x0002); + msleep(10); + CM421_write_reg(gc5603->client, CM421_SLAVE_ADDR, 0x0024, CM421_REG_VALUE_16bit, 0x0001); + msleep(400); + CM421_write_reg(gc5603->client, CM421_SLAVE_ADDR, 0x9b2c, CM421_REG_VALUE_16bit, 0x0001); + msleep(100); + CM421_write_reg(gc5603->client, CM421_SLAVE_ADDR, 0x9b2a, CM421_REG_VALUE_16bit, 0x0001); + msleep(200); + CM421_read_reg(gc5603->client, CM421_SLAVE_ADDR, 0x9b28, CM421_REG_VALUE_16bit, &value2); + dev_info(dev, "0x9b28 = 0x%x\n", value2); +#endif unlock_and_return: mutex_unlock(&gc5603->mutex); diff --git a/drivers/media/i2c/imx415.c.bak b/drivers/media/i2c/imx415.c.bak new file mode 100644 index 0000000000000..3c78fe098528d --- /dev/null +++ b/drivers/media/i2c/imx415.c.bak @@ -0,0 +1,3706 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * imx415 driver + * + * Copyright (C) 2020 Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X00 first version. + * V0.0X01.0X01 + * 1. fix hdr ae ratio error, + * 0x3260 should be set 0x01 in normal mode, + * should be 0x00 in hdr mode. + * 2. rhs1 should be 4n+1 when set hdr ae. + * V0.0X01.0X02 + * 1. shr0 should be greater than (rsh1 + 9). + * 2. rhs1 should be ceil to 4n + 1. + * V0.0X01.0X03 + * 1. support 12bit HDR DOL3 + * 2. support HDR/Linear quick switch + * V0.0X01.0X04 + * 1. support enum format info by aiq + * V0.0X01.0X05 + * 1. fixed 10bit hdr2/hdr3 frame rate issue + * V0.0X01.0X06 + * 1. support DOL3 10bit 20fps 1485Mbps + * 2. fixed linkfreq error + * V0.0X01.0X07 + * 1. fix set_fmt & ioctl get mode unmatched issue. + * 2. need to set default vblank when change format. + * 3. enum all supported mode mbus_code, not just cur_mode. + * V0.0X01.0X08 + * 1. add dcphy param for hdrx2 mode. + */ + +#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../platform/rockchip/isp/rkisp_tb_helper.h" + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x08) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define MIPI_FREQ_1188M 1188000000 +#define MIPI_FREQ_891M 891000000 +#define MIPI_FREQ_446M 446000000 +#define MIPI_FREQ_743M 743000000 +#define MIPI_FREQ_297M 297000000 + +#define IMX415_4LANES 4 +#define IMX415_2LANES 2 + +#define IMX415_MAX_PIXEL_RATE (MIPI_FREQ_891M / 10 * 2 * IMX415_4LANES) +#define OF_CAMERA_HDR_MODE "rockchip,camera-hdr-mode" +#define DATA_LANES "rockchip,imx415-data-lanes" +#define OF_CAMERA_CAPTURE_MODE "rockchip,imx415-capture-mode" + +#define IMX415_XVCLK_FREQ_37M 37125000 +#define IMX415_XVCLK_FREQ_27M 27000000 + +/* TODO: Get the real chip id from reg */ +#define CHIP_ID 0xE0 +#define IMX415_REG_CHIP_ID 0x311A + +#define IMX415_REG_CTRL_MODE 0x3000 +#define IMX415_MODE_SW_STANDBY BIT(0) +#define IMX415_MODE_STREAMING 0x0 + +#define IMX415_LF_GAIN_REG_H 0x3091 +#define IMX415_LF_GAIN_REG_L 0x3090 + +#define IMX415_SF1_GAIN_REG_H 0x3093 +#define IMX415_SF1_GAIN_REG_L 0x3092 + +#define IMX415_SF2_GAIN_REG_H 0x3095 +#define IMX415_SF2_GAIN_REG_L 0x3094 + +#define IMX415_LF_EXPO_REG_H 0x3052 +#define IMX415_LF_EXPO_REG_M 0x3051 +#define IMX415_LF_EXPO_REG_L 0x3050 + +#define IMX415_SF1_EXPO_REG_H 0x3056 +#define IMX415_SF1_EXPO_REG_M 0x3055 +#define IMX415_SF1_EXPO_REG_L 0x3054 + +#define IMX415_SF2_EXPO_REG_H 0x305A +#define IMX415_SF2_EXPO_REG_M 0x3059 +#define IMX415_SF2_EXPO_REG_L 0x3058 + +#define IMX415_RHS1_REG_H 0x3062 +#define IMX415_RHS1_REG_M 0x3061 +#define IMX415_RHS1_REG_L 0x3060 +#define IMX415_RHS1_DEFAULT 0x004D + +#define IMX415_RHS2_REG_H 0x3066 +#define IMX415_RHS2_REG_M 0x3065 +#define IMX415_RHS2_REG_L 0x3064 +#define IMX415_RHS2_DEFAULT 0x004D + +#define IMX415_EXPOSURE_MIN 4 +#define IMX415_EXPOSURE_STEP 1 +#define IMX415_VTS_MAX 0x7fff + +#define IMX415_GAIN_MIN 0x00 +#define IMX415_GAIN_MAX 0xf0 +#define IMX415_GAIN_STEP 1 +#define IMX415_GAIN_DEFAULT 0x00 + +#define IMX415_FETCH_GAIN_H(VAL) (((VAL) >> 8) & 0x07) +#define IMX415_FETCH_GAIN_L(VAL) ((VAL) & 0xFF) + +#define IMX415_FETCH_EXP_H(VAL) (((VAL) >> 16) & 0x0F) +#define IMX415_FETCH_EXP_M(VAL) (((VAL) >> 8) & 0xFF) +#define IMX415_FETCH_EXP_L(VAL) ((VAL) & 0xFF) + +#define IMX415_FETCH_RHS1_H(VAL) (((VAL) >> 16) & 0x0F) +#define IMX415_FETCH_RHS1_M(VAL) (((VAL) >> 8) & 0xFF) +#define IMX415_FETCH_RHS1_L(VAL) ((VAL) & 0xFF) + +#define IMX415_FETCH_VTS_H(VAL) (((VAL) >> 16) & 0x0F) +#define IMX415_FETCH_VTS_M(VAL) (((VAL) >> 8) & 0xFF) +#define IMX415_FETCH_VTS_L(VAL) ((VAL) & 0xFF) + +#define IMX415_VTS_REG_L 0x3024 +#define IMX415_VTS_REG_M 0x3025 +#define IMX415_VTS_REG_H 0x3026 + +#define IMX415_MIRROR_BIT_MASK BIT(0) +#define IMX415_FLIP_BIT_MASK BIT(1) +#define IMX415_FLIP_REG 0x3030 + +#define REG_NULL 0xFFFF +#define REG_DELAY 0xFFFE + +#define IMX415_REG_VALUE_08BIT 1 +#define IMX415_REG_VALUE_16BIT 2 +#define IMX415_REG_VALUE_24BIT 3 + +#define IMX415_GROUP_HOLD_REG 0x3001 +#define IMX415_GROUP_HOLD_START 0x01 +#define IMX415_GROUP_HOLD_END 0x00 + +/* Basic Readout Lines. Number of necessary readout lines in sensor */ +#define BRL_ALL 2228u +#define BRL_BINNING 1115u +/* Readout timing setting of SEF1(DOL2): RHS1 < 2 * BRL and should be 4n + 1 */ +#define RHS1_MAX_X2(VAL) (((VAL) * 2 - 1) / 4 * 4 + 1) +#define SHR1_MIN_X2 9u + +/* Readout timing setting of SEF1(DOL3): RHS1 < 3 * BRL and should be 6n + 1 */ +#define RHS1_MAX_X3(VAL) (((VAL) * 3 - 1) / 6 * 6 + 1) +#define SHR1_MIN_X3 13u + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define IMX415_NAME "imx415" + +static const char * const imx415_supply_names[] = { + "dvdd", /* Digital core power */ + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ +}; + +#define IMX415_NUM_SUPPLIES ARRAY_SIZE(imx415_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +struct imx415_mode { + u32 bus_fmt; + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + u32 mipi_freq_idx; + u32 bpp; + const struct regval *global_reg_list; + const struct regval *reg_list; + u32 hdr_mode; + u32 vc[PAD_MAX]; + u32 xvclk; +}; + +struct imx415 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *power_gpio; + struct regulator_bulk_data supplies[IMX415_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_a_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 mutex mutex; + bool streaming; + bool power_on; + bool is_thunderboot; + bool is_thunderboot_ng; + bool is_first_streamoff; + const struct imx415_mode *supported_modes; + const struct imx415_mode *cur_mode; + u32 module_index; + u32 cfg_num; + const char *module_facing; + const char *module_name; + const char *len_name; + u32 cur_vts; + bool has_init_exp; + struct preisp_hdrae_exp_s init_hdrae_exp; + u32 lanes; + u32 capture_mode; +}; + +static struct rkmodule_csi_dphy_param dcphy_param = { + .vendor = PHY_VENDOR_SAMSUNG, + .lp_vol_ref = 6, + .lp_hys_sw = {3, 0, 0, 0}, + .lp_escclk_pol_sel = {1, 1, 1, 1}, + .skew_data_cal_clk = {0, 3, 3, 3}, + .clk_hs_term_sel = 2, + .data_hs_term_sel = {2, 2, 2, 2}, + .reserved = {0}, +}; + +#define to_imx415(sd) container_of(sd, struct imx415, subdev) + +/* + * Xclk 37.125Mhz + */ +static __maybe_unused const struct regval imx415_global_12bit_3864x2192_regs[] = { + {0x3002, 0x00}, + {0x3008, 0x7F}, + {0x300A, 0x5B}, + {0x30C1, 0x00}, + {0x3031, 0x01}, + {0x3032, 0x01}, + {0x30D9, 0x06}, + {0x3116, 0x24}, + {0x3118, 0xC0}, + {0x311E, 0x24}, + {0x32D4, 0x21}, + {0x32EC, 0xA1}, + {0x3452, 0x7F}, + {0x3453, 0x03}, + {0x358A, 0x04}, + {0x35A1, 0x02}, + {0x36BC, 0x0C}, + {0x36CC, 0x53}, + {0x36CD, 0x00}, + {0x36CE, 0x3C}, + {0x36D0, 0x8C}, + {0x36D1, 0x00}, + {0x36D2, 0x71}, + {0x36D4, 0x3C}, + {0x36D6, 0x53}, + {0x36D7, 0x00}, + {0x36D8, 0x71}, + {0x36DA, 0x8C}, + {0x36DB, 0x00}, + {0x3701, 0x03}, + {0x3724, 0x02}, + {0x3726, 0x02}, + {0x3732, 0x02}, + {0x3734, 0x03}, + {0x3736, 0x03}, + {0x3742, 0x03}, + {0x3862, 0xE0}, + {0x38CC, 0x30}, + {0x38CD, 0x2F}, + {0x395C, 0x0C}, + {0x3A42, 0xD1}, + {0x3A4C, 0x77}, + {0x3AE0, 0x02}, + {0x3AEC, 0x0C}, + {0x3B00, 0x2E}, + {0x3B06, 0x29}, + {0x3B98, 0x25}, + {0x3B99, 0x21}, + {0x3B9B, 0x13}, + {0x3B9C, 0x13}, + {0x3B9D, 0x13}, + {0x3B9E, 0x13}, + {0x3BA1, 0x00}, + {0x3BA2, 0x06}, + {0x3BA3, 0x0B}, + {0x3BA4, 0x10}, + {0x3BA5, 0x14}, + {0x3BA6, 0x18}, + {0x3BA7, 0x1A}, + {0x3BA8, 0x1A}, + {0x3BA9, 0x1A}, + {0x3BAC, 0xED}, + {0x3BAD, 0x01}, + {0x3BAE, 0xF6}, + {0x3BAF, 0x02}, + {0x3BB0, 0xA2}, + {0x3BB1, 0x03}, + {0x3BB2, 0xE0}, + {0x3BB3, 0x03}, + {0x3BB4, 0xE0}, + {0x3BB5, 0x03}, + {0x3BB6, 0xE0}, + {0x3BB7, 0x03}, + {0x3BB8, 0xE0}, + {0x3BBA, 0xE0}, + {0x3BBC, 0xDA}, + {0x3BBE, 0x88}, + {0x3BC0, 0x44}, + {0x3BC2, 0x7B}, + {0x3BC4, 0xA2}, + {0x3BC8, 0xBD}, + {0x3BCA, 0xBD}, + {0x4004, 0x48}, + {0x4005, 0x09}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_linear_12bit_3864x2192_891M_regs[] = { + {0x3020, 0x00}, + {0x3021, 0x00}, + {0x3022, 0x00}, + {0x3024, 0xCA}, + {0x3025, 0x08}, + {0x3028, 0x4C}, + {0x3029, 0x04}, + {0x302C, 0x00}, + {0x302D, 0x00}, + {0x3033, 0x05}, + {0x3050, 0x08}, + {0x3051, 0x00}, + {0x3054, 0x19}, + {0x3058, 0x3E}, + {0x3060, 0x25}, + {0x3064, 0x4A}, + {0x30CF, 0x00}, + {0x3260, 0x01}, + {0x400C, 0x00}, + {0x4018, 0x7F}, + {0x401A, 0x37}, + {0x401C, 0x37}, + {0x401E, 0xF7}, + {0x401F, 0x00}, + {0x4020, 0x3F}, + {0x4022, 0x6F}, + {0x4024, 0x3F}, + {0x4026, 0x5F}, + {0x4028, 0x2F}, + {0x4074, 0x01}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_hdr2_12bit_3864x2192_1782M_regs[] = { + {0x3020, 0x00}, + {0x3021, 0x00}, + {0x3022, 0x00}, + {0x3024, 0xCA}, + {0x3025, 0x08}, + {0x3028, 0x26}, + {0x3029, 0x02}, + {0x302C, 0x01}, + {0x302D, 0x01}, + {0x3033, 0x04}, + {0x3050, 0x90}, + {0x3051, 0x0D}, + {0x3054, 0x09}, + {0x3058, 0x3E}, + {0x3060, 0x4D}, + {0x3064, 0x4A}, + {0x30CF, 0x01}, + {0x3260, 0x00}, + {0x400C, 0x01}, + {0x4018, 0xB7}, + {0x401A, 0x67}, + {0x401C, 0x6F}, + {0x401E, 0xDF}, + {0x401F, 0x01}, + {0x4020, 0x6F}, + {0x4022, 0xCF}, + {0x4024, 0x6F}, + {0x4026, 0xB7}, + {0x4028, 0x5F}, + {0x4074, 0x00}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_hdr3_12bit_3864x2192_1782M_regs[] = { + {0x3020, 0x00}, + {0x3021, 0x00}, + {0x3022, 0x00}, + {0x3024, 0x96}, + {0x3025, 0x06}, + {0x3028, 0x26}, + {0x3029, 0x02}, + {0x302C, 0x01}, + {0x302D, 0x02}, + {0x3033, 0x04}, + {0x3050, 0x14}, + {0x3051, 0x01}, + {0x3054, 0x0D}, + {0x3058, 0x26}, + {0x3060, 0x19}, + {0x3064, 0x32}, + {0x30CF, 0x03}, + {0x3260, 0x00}, + {0x400C, 0x01}, + {0x4018, 0xB7}, + {0x401A, 0x67}, + {0x401C, 0x6F}, + {0x401E, 0xDF}, + {0x401F, 0x01}, + {0x4020, 0x6F}, + {0x4022, 0xCF}, + {0x4024, 0x6F}, + {0x4026, 0xB7}, + {0x4028, 0x5F}, + {0x4074, 0x00}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_global_10bit_3864x2192_regs[] = { + {0x3002, 0x00}, + {0x3008, 0x7F}, + {0x300A, 0x5B}, + {0x3031, 0x00}, + {0x3032, 0x00}, + {0x30C1, 0x00}, + {0x30D9, 0x06}, + {0x3116, 0x24}, + {0x311E, 0x24}, + {0x32D4, 0x21}, + {0x32EC, 0xA1}, + {0x3452, 0x7F}, + {0x3453, 0x03}, + {0x358A, 0x04}, + {0x35A1, 0x02}, + {0x36BC, 0x0C}, + {0x36CC, 0x53}, + {0x36CD, 0x00}, + {0x36CE, 0x3C}, + {0x36D0, 0x8C}, + {0x36D1, 0x00}, + {0x36D2, 0x71}, + {0x36D4, 0x3C}, + {0x36D6, 0x53}, + {0x36D7, 0x00}, + {0x36D8, 0x71}, + {0x36DA, 0x8C}, + {0x36DB, 0x00}, + {0x3701, 0x00}, + {0x3724, 0x02}, + {0x3726, 0x02}, + {0x3732, 0x02}, + {0x3734, 0x03}, + {0x3736, 0x03}, + {0x3742, 0x03}, + {0x3862, 0xE0}, + {0x38CC, 0x30}, + {0x38CD, 0x2F}, + {0x395C, 0x0C}, + {0x3A42, 0xD1}, + {0x3A4C, 0x77}, + {0x3AE0, 0x02}, + {0x3AEC, 0x0C}, + {0x3B00, 0x2E}, + {0x3B06, 0x29}, + {0x3B98, 0x25}, + {0x3B99, 0x21}, + {0x3B9B, 0x13}, + {0x3B9C, 0x13}, + {0x3B9D, 0x13}, + {0x3B9E, 0x13}, + {0x3BA1, 0x00}, + {0x3BA2, 0x06}, + {0x3BA3, 0x0B}, + {0x3BA4, 0x10}, + {0x3BA5, 0x14}, + {0x3BA6, 0x18}, + {0x3BA7, 0x1A}, + {0x3BA8, 0x1A}, + {0x3BA9, 0x1A}, + {0x3BAC, 0xED}, + {0x3BAD, 0x01}, + {0x3BAE, 0xF6}, + {0x3BAF, 0x02}, + {0x3BB0, 0xA2}, + {0x3BB1, 0x03}, + {0x3BB2, 0xE0}, + {0x3BB3, 0x03}, + {0x3BB4, 0xE0}, + {0x3BB5, 0x03}, + {0x3BB6, 0xE0}, + {0x3BB7, 0x03}, + {0x3BB8, 0xE0}, + {0x3BBA, 0xE0}, + {0x3BBC, 0xDA}, + {0x3BBE, 0x88}, + {0x3BC0, 0x44}, + {0x3BC2, 0x7B}, + {0x3BC4, 0xA2}, + {0x3BC8, 0xBD}, + {0x3BCA, 0xBD}, + {0x4004, 0x48}, + {0x4005, 0x09}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_hdr3_10bit_3864x2192_1485M_regs[] = { + {0x3020, 0x00}, + {0x3021, 0x00}, + {0x3022, 0x00}, + {0x3024, 0xBD}, + {0x3025, 0x06}, + {0x3028, 0x1A}, + {0x3029, 0x02}, + {0x302C, 0x01}, + {0x302D, 0x02}, + {0x3033, 0x08}, + {0x3050, 0x90}, + {0x3051, 0x15}, + {0x3054, 0x0D}, + {0x3058, 0xA4}, + {0x3060, 0x97}, + {0x3064, 0xB6}, + {0x30CF, 0x03}, + {0x3118, 0xA0}, + {0x3260, 0x00}, + {0x400C, 0x01}, + {0x4018, 0xA7}, + {0x401A, 0x57}, + {0x401C, 0x5F}, + {0x401E, 0x97}, + {0x401F, 0x01}, + {0x4020, 0x5F}, + {0x4022, 0xAF}, + {0x4024, 0x5F}, + {0x4026, 0x9F}, + {0x4028, 0x4F}, + {0x4074, 0x00}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_hdr3_10bit_3864x2192_1782M_regs[] = { + {0x3020, 0x00}, + {0x3021, 0x00}, + {0x3022, 0x00}, + {0x3024, 0xEA}, + {0x3025, 0x07}, + {0x3028, 0xCA}, + {0x3029, 0x01}, + {0x302C, 0x01}, + {0x302D, 0x02}, + {0x3033, 0x04}, + {0x3050, 0x3E}, + {0x3051, 0x01}, + {0x3054, 0x0D}, + {0x3058, 0x9E}, + {0x3060, 0x91}, + {0x3064, 0xC2}, + {0x30CF, 0x03}, + {0x3118, 0xC0}, + {0x3260, 0x00}, + {0x400C, 0x01}, + {0x4018, 0xB7}, + {0x401A, 0x67}, + {0x401C, 0x6F}, + {0x401E, 0xDF}, + {0x401F, 0x01}, + {0x4020, 0x6F}, + {0x4022, 0xCF}, + {0x4024, 0x6F}, + {0x4026, 0xB7}, + {0x4028, 0x5F}, + {0x4074, 0x00}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_hdr2_10bit_3864x2192_1485M_regs[] = { + {0x3020, 0x00}, + {0x3021, 0x00}, + {0x3022, 0x00}, + {0x3024, 0xFC}, + {0x3025, 0x08}, + {0x3028, 0x1A}, + {0x3029, 0x02}, + {0x302C, 0x01}, + {0x302D, 0x01}, + {0x3033, 0x08}, + {0x3050, 0xA8}, + {0x3051, 0x0D}, + {0x3054, 0x09}, + {0x3058, 0x3E}, + {0x3060, 0x4D}, + {0x3064, 0x4a}, + {0x30CF, 0x01}, + {0x3118, 0xA0}, + {0x3260, 0x00}, + {0x400C, 0x01}, + {0x4018, 0xA7}, + {0x401A, 0x57}, + {0x401C, 0x5F}, + {0x401E, 0x97}, + {0x401F, 0x01}, + {0x4020, 0x5F}, + {0x4022, 0xAF}, + {0x4024, 0x5F}, + {0x4026, 0x9F}, + {0x4028, 0x4F}, + {0x4074, 0x00}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_linear_10bit_3864x2192_891M_regs[] = { + {0x3020, 0x00}, + {0x3021, 0x00}, + {0x3022, 0x00}, + {0x3024, 0xCA}, + {0x3025, 0x08}, + {0x3028, 0x4C}, + {0x3029, 0x04}, + {0x302C, 0x00}, + {0x302D, 0x00}, + {0x3033, 0x05}, + {0x3050, 0x08}, + {0x3051, 0x00}, + {0x3054, 0x19}, + {0x3058, 0x3E}, + {0x3060, 0x25}, + {0x3064, 0x4a}, + {0x30CF, 0x00}, + {0x3118, 0xC0}, + {0x3260, 0x01}, + {0x400C, 0x00}, + {0x4018, 0x7F}, + {0x401A, 0x37}, + {0x401C, 0x37}, + {0x401E, 0xF7}, + {0x401F, 0x00}, + {0x4020, 0x3F}, + {0x4022, 0x6F}, + {0x4024, 0x3F}, + {0x4026, 0x5F}, + {0x4028, 0x2F}, + {0x4074, 0x01}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_linear_12bit_1932x1096_594M_regs[] = { + {0x3020, 0x01}, + {0x3021, 0x01}, + {0x3022, 0x01}, + {0x3024, 0x5D}, + {0x3025, 0x0C}, + {0x3028, 0x0E}, + {0x3029, 0x03}, + {0x302C, 0x00}, + {0x302D, 0x00}, + {0x3031, 0x00}, + {0x3033, 0x07}, + {0x3050, 0x08}, + {0x3051, 0x00}, + {0x3054, 0x19}, + {0x3058, 0x3E}, + {0x3060, 0x25}, + {0x3064, 0x4A}, + {0x30CF, 0x00}, + {0x30D9, 0x02}, + {0x30DA, 0x01}, + {0x3118, 0x80}, + {0x3260, 0x01}, + {0x3701, 0x00}, + {0x400C, 0x00}, + {0x4018, 0x67}, + {0x401A, 0x27}, + {0x401C, 0x27}, + {0x401E, 0xB7}, + {0x401F, 0x00}, + {0x4020, 0x2F}, + {0x4022, 0x4F}, + {0x4024, 0x2F}, + {0x4026, 0x47}, + {0x4028, 0x27}, + {0x4074, 0x01}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_hdr2_12bit_1932x1096_891M_regs[] = { + {0x3020, 0x01}, + {0x3021, 0x01}, + {0x3022, 0x01}, + {0x3024, 0xFC}, + {0x3025, 0x08}, + {0x3028, 0x1A}, + {0x3029, 0x02}, + {0x302C, 0x01}, + {0x302D, 0x01}, + {0x3031, 0x00}, + {0x3033, 0x05}, + {0x3050, 0xB8}, + {0x3051, 0x00}, + {0x3054, 0x09}, + {0x3058, 0x3E}, + {0x3060, 0x25}, + {0x3064, 0x4A}, + {0x30CF, 0x01}, + {0x30D9, 0x02}, + {0x30DA, 0x01}, + {0x3118, 0xC0}, + {0x3260, 0x00}, + {0x3701, 0x00}, + {0x400C, 0x00}, + {0x4018, 0xA7}, + {0x401A, 0x57}, + {0x401C, 0x5F}, + {0x401E, 0x97}, + {0x401F, 0x01}, + {0x4020, 0x5F}, + {0x4022, 0xAF}, + {0x4024, 0x5F}, + {0x4026, 0x9F}, + {0x4028, 0x4F}, + {0x4074, 0x01}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 27Mhz + * 15fps + * CSI-2_2lane + * AD:12bit Output:12bit + * 891Mbps + * Master Mode + * Time 9.988ms Gain:6dB + * All-pixel + */ +static __maybe_unused const struct regval imx415_linear_12bit_3864x2192_891M_regs_2lane[] = { + {0x3008, 0x5D}, + {0x300A, 0x42}, + {0x3028, 0x98}, + {0x3029, 0x08}, + {0x3033, 0x05}, + {0x3050, 0x79}, + {0x3051, 0x07}, + {0x3090, 0x14}, + {0x30C1, 0x00}, + {0x3116, 0x23}, + {0x3118, 0xC6}, + {0x311A, 0xE7}, + {0x311E, 0x23}, + {0x32D4, 0x21}, + {0x32EC, 0xA1}, + {0x344C, 0x2B}, + {0x344D, 0x01}, + {0x344E, 0xED}, + {0x344F, 0x01}, + {0x3450, 0xF6}, + {0x3451, 0x02}, + {0x3452, 0x7F}, + {0x3453, 0x03}, + {0x358A, 0x04}, + {0x35A1, 0x02}, + {0x35EC, 0x27}, + {0x35EE, 0x8D}, + {0x35F0, 0x8D}, + {0x35F2, 0x29}, + {0x36BC, 0x0C}, + {0x36CC, 0x53}, + {0x36CD, 0x00}, + {0x36CE, 0x3C}, + {0x36D0, 0x8C}, + {0x36D1, 0x00}, + {0x36D2, 0x71}, + {0x36D4, 0x3C}, + {0x36D6, 0x53}, + {0x36D7, 0x00}, + {0x36D8, 0x71}, + {0x36DA, 0x8C}, + {0x36DB, 0x00}, + {0x3720, 0x00}, + {0x3724, 0x02}, + {0x3726, 0x02}, + {0x3732, 0x02}, + {0x3734, 0x03}, + {0x3736, 0x03}, + {0x3742, 0x03}, + {0x3862, 0xE0}, + {0x38CC, 0x30}, + {0x38CD, 0x2F}, + {0x395C, 0x0C}, + {0x39A4, 0x07}, + {0x39A8, 0x32}, + {0x39AA, 0x32}, + {0x39AC, 0x32}, + {0x39AE, 0x32}, + {0x39B0, 0x32}, + {0x39B2, 0x2F}, + {0x39B4, 0x2D}, + {0x39B6, 0x28}, + {0x39B8, 0x30}, + {0x39BA, 0x30}, + {0x39BC, 0x30}, + {0x39BE, 0x30}, + {0x39C0, 0x30}, + {0x39C2, 0x2E}, + {0x39C4, 0x2B}, + {0x39C6, 0x25}, + {0x3A42, 0xD1}, + {0x3A4C, 0x77}, + {0x3AE0, 0x02}, + {0x3AEC, 0x0C}, + {0x3B00, 0x2E}, + {0x3B06, 0x29}, + {0x3B98, 0x25}, + {0x3B99, 0x21}, + {0x3B9B, 0x13}, + {0x3B9C, 0x13}, + {0x3B9D, 0x13}, + {0x3B9E, 0x13}, + {0x3BA1, 0x00}, + {0x3BA2, 0x06}, + {0x3BA3, 0x0B}, + {0x3BA4, 0x10}, + {0x3BA5, 0x14}, + {0x3BA6, 0x18}, + {0x3BA7, 0x1A}, + {0x3BA8, 0x1A}, + {0x3BA9, 0x1A}, + {0x3BAC, 0xED}, + {0x3BAD, 0x01}, + {0x3BAE, 0xF6}, + {0x3BAF, 0x02}, + {0x3BB0, 0xA2}, + {0x3BB1, 0x03}, + {0x3BB2, 0xE0}, + {0x3BB3, 0x03}, + {0x3BB4, 0xE0}, + {0x3BB5, 0x03}, + {0x3BB6, 0xE0}, + {0x3BB7, 0x03}, + {0x3BB8, 0xE0}, + {0x3BBA, 0xE0}, + {0x3BBC, 0xDA}, + {0x3BBE, 0x88}, + {0x3BC0, 0x44}, + {0x3BC2, 0x7B}, + {0x3BC4, 0xA2}, + {0x3BC8, 0xBD}, + {0x3BCA, 0xBD}, + {0x4001, 0x01}, + {0x4004, 0xC0}, + {0x4005, 0x06}, + {0x400C, 0x00}, + {0x4018, 0x7F}, + {0x401A, 0x37}, + {0x401C, 0x37}, + {0x401E, 0xF7}, + {0x401F, 0x00}, + {0x4020, 0x3F}, + {0x4022, 0x6F}, + {0x4024, 0x3F}, + {0x4026, 0x5F}, + {0x4028, 0x2F}, + {0x4074, 0x01}, + {0x3002, 0x00}, + //{0x3000, 0x00}, + {REG_DELAY, 0x1E},//wait_ms(30) + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_linear_10bit_3864x2192_891M_regs_2lane[] = { + {0x3008, 0x7F}, + {0x3031, 0x00}, + {0x3032, 0x00}, + {0x300A, 0x5B}, + {0x3028, 0x98}, + {0x3029, 0x08}, + {0x3033, 0x05}, + {0x3050, 0x79}, + {0x3051, 0x07}, + {0x3090, 0x14}, + {0x30C1, 0x00}, + {0x3116, 0x24}, + {0x3118, 0xC0}, + {0x311A, 0xE0}, + {0x311E, 0x24}, + {0x32D4, 0x21}, + {0x32EC, 0xA1}, + {0x344C, 0x2B}, + {0x344D, 0x01}, + {0x344E, 0xED}, + {0x344F, 0x01}, + {0x3450, 0xF6}, + {0x3451, 0x02}, + {0x3452, 0x7F}, + {0x3453, 0x03}, + {0x358A, 0x04}, + {0x35A1, 0x02}, + {0x35EC, 0x27}, + {0x35EE, 0x8D}, + {0x35F0, 0x8D}, + {0x35F2, 0x29}, + {0x36BC, 0x0C}, + {0x36CC, 0x53}, + {0x36CD, 0x00}, + {0x36CE, 0x3C}, + {0x36D0, 0x8C}, + {0x36D1, 0x00}, + {0x36D2, 0x71}, + {0x36D4, 0x3C}, + {0x36D6, 0x53}, + {0x36D7, 0x00}, + {0x36D8, 0x71}, + {0x36DA, 0x8C}, + {0x36DB, 0x00}, + {0x3720, 0x00}, + {0x3724, 0x02}, + {0x3726, 0x02}, + {0x3732, 0x02}, + {0x3734, 0x03}, + {0x3736, 0x03}, + {0x3742, 0x03}, + {0x3862, 0xE0}, + {0x38CC, 0x30}, + {0x38CD, 0x2F}, + {0x395C, 0x0C}, + {0x39A4, 0x07}, + {0x39A8, 0x32}, + {0x39AA, 0x32}, + {0x39AC, 0x32}, + {0x39AE, 0x32}, + {0x39B0, 0x32}, + {0x39B2, 0x2F}, + {0x39B4, 0x2D}, + {0x39B6, 0x28}, + {0x39B8, 0x30}, + {0x39BA, 0x30}, + {0x39BC, 0x30}, + {0x39BE, 0x30}, + {0x39C0, 0x30}, + {0x39C2, 0x2E}, + {0x39C4, 0x2B}, + {0x39C6, 0x25}, + {0x3A42, 0xD1}, + {0x3A4C, 0x77}, + {0x3AE0, 0x02}, + {0x3AEC, 0x0C}, + {0x3B00, 0x2E}, + {0x3B06, 0x29}, + {0x3B98, 0x25}, + {0x3B99, 0x21}, + {0x3B9B, 0x13}, + {0x3B9C, 0x13}, + {0x3B9D, 0x13}, + {0x3B9E, 0x13}, + {0x3BA1, 0x00}, + {0x3BA2, 0x06}, + {0x3BA3, 0x0B}, + {0x3BA4, 0x10}, + {0x3BA5, 0x14}, + {0x3BA6, 0x18}, + {0x3BA7, 0x1A}, + {0x3BA8, 0x1A}, + {0x3BA9, 0x1A}, + {0x3BAC, 0xED}, + {0x3BAD, 0x01}, + {0x3BAE, 0xF6}, + {0x3BAF, 0x02}, + {0x3BB0, 0xA2}, + {0x3BB1, 0x03}, + {0x3BB2, 0xE0}, + {0x3BB3, 0x03}, + {0x3BB4, 0xE0}, + {0x3BB5, 0x03}, + {0x3BB6, 0xE0}, + {0x3BB7, 0x03}, + {0x3BB8, 0xE0}, + {0x3BBA, 0xE0}, + {0x3BBC, 0xDA}, + {0x3BBE, 0x88}, + {0x3BC0, 0x44}, + {0x3BC2, 0x7B}, + {0x3BC4, 0xA2}, + {0x3BC8, 0xBD}, + {0x3BCA, 0xBD}, + {0x4001, 0x01}, + {0x4004, 0x48}, + {0x4005, 0x09}, + {0x400C, 0x00}, + {0x4018, 0x7F}, + {0x401A, 0x37}, + {0x401C, 0x37}, + {0x401E, 0xF7}, + {0x401F, 0x00}, + {0x4020, 0x3F}, + {0x4022, 0x6F}, + {0x4024, 0x3F}, + {0x4026, 0x5F}, + {0x4028, 0x2F}, + {0x4074, 0x01}, + {0x3002, 0x00}, + //{0x3000, 0x00}, + {REG_DELAY, 0x1E},//wait_ms(30) + {REG_NULL, 0x00}, +}; + + +static __maybe_unused const struct regval imx415_linear_10bit_1920x1080_891M_regs_2lane[] = { + {0x3008, 0x7f}, + {0x300A, 0x5b}, + {0x3034, 0x05}, + // {0x301C, 0x04}, + {0x3020, 0x01}, + {0x3021, 0x01}, + {0x3022, 0x01}, + {0x3024, 0xca}, + {0x3025, 0x08}, + {0x3028, 0x26}, + {0x3029, 0x02}, + {0x3031, 0x00}, + {0x3032, 0x00}, + {0x3033, 0x05}, + {0x3050, 0xC4}, + {0x3090, 0x14}, + {0x30C1, 0x00}, + {0x30D9, 0x02}, + {0x30DA, 0x01}, + {0x3116, 0x24}, + {0x3118, 0xC0}, + {0x311A, 0xE0}, + {0x311E, 0x24}, + {0x32D4, 0x21}, + {0x32EC, 0xA1}, + {0x344C, 0x2B}, + {0x344D, 0x01}, + {0x344E, 0xED}, + {0x344F, 0x01}, + {0x3450, 0xF6}, + {0x3451, 0x02}, + {0x3452, 0x7F}, + {0x3453, 0x03}, + {0x358A, 0x04}, + {0x35A1, 0x02}, + {0x35EC, 0x27}, + {0x35EE, 0x8D}, + {0x35F0, 0x8D}, + {0x35F2, 0x29}, + {0x36BC, 0x0C}, + {0x36CC, 0x53}, + {0x36CD, 0x00}, + {0x36CE, 0x3C}, + {0x36D0, 0x8C}, + {0x36D1, 0x00}, + {0x36D2, 0x71}, + {0x36D4, 0x3C}, + {0x36D6, 0x53}, + {0x36D7, 0x00}, + {0x36D8, 0x71}, + {0x36DA, 0x8C}, + {0x36DB, 0x00}, + {0x3701, 0x00}, + {0x3720, 0x00}, + {0x3724, 0x02}, + {0x3726, 0x02}, + {0x3732, 0x02}, + {0x3734, 0x03}, + {0x3736, 0x03}, + {0x3742, 0x03}, + {0x3862, 0xE0}, + {0x38CC, 0x30}, + {0x38CD, 0x2F}, + {0x395C, 0x0C}, + {0x39A4, 0x07}, + {0x39A8, 0x32}, + {0x39AA, 0x32}, + {0x39AC, 0x32}, + {0x39AE, 0x32}, + {0x39B0, 0x32}, + {0x39B2, 0x2F}, + {0x39B4, 0x2D}, + {0x39B6, 0x28}, + {0x39B8, 0x30}, + {0x39BA, 0x30}, + {0x39BC, 0x30}, + {0x39BE, 0x30}, + {0x39C0, 0x30}, + {0x39C2, 0x2E}, + {0x39C4, 0x2B}, + {0x39C6, 0x25}, + {0x3A42, 0xD1}, + {0x3A4C, 0x77}, + {0x3AE0, 0x02}, + {0x3AEC, 0x0C}, + {0x3B00, 0x2E}, + {0x3B06, 0x29}, + {0x3B98, 0x25}, + {0x3B99, 0x21}, + {0x3B9B, 0x13}, + {0x3B9C, 0x13}, + {0x3B9D, 0x13}, + {0x3B9E, 0x13}, + {0x3BA1, 0x00}, + {0x3BA2, 0x06}, + {0x3BA3, 0x0B}, + {0x3BA4, 0x10}, + {0x3BA5, 0x14}, + {0x3BA6, 0x18}, + {0x3BA7, 0x1A}, + {0x3BA8, 0x1A}, + {0x3BA9, 0x1A}, + {0x3BAC, 0xED}, + {0x3BAD, 0x01}, + {0x3BAE, 0xF6}, + {0x3BAF, 0x02}, + {0x3BB0, 0xA2}, + {0x3BB1, 0x03}, + {0x3BB2, 0xE0}, + {0x3BB3, 0x03}, + {0x3BB4, 0xE0}, + {0x3BB5, 0x03}, + {0x3BB6, 0xE0}, + {0x3BB7, 0x03}, + {0x3BB8, 0xE0}, + {0x3BBA, 0xE0}, + {0x3BBC, 0xDA}, + {0x3BBE, 0x88}, + {0x3BC0, 0x44}, + {0x3BC2, 0x7B}, + {0x3BC4, 0xA2}, + {0x3BC8, 0xBD}, + {0x3BCA, 0xBD}, + {0x4001, 0x01}, + {0x4004, 0x48}, + {0x4005, 0x09}, + {0x400c, 0x00}, + {0x4018, 0xE7}, + {0x401A, 0x37}, + {0x401C, 0x37}, + {0x401E, 0xf7}, + {0x401F, 0x00}, + {0x4020, 0x3f}, + {0x4022, 0x6F}, + {0x4023, 0x00}, + {0x4024, 0x3f}, + {0x4026, 0x5f}, + {0x4028, 0x2F}, + {0x4074, 0x1}, + {0x3002, 0x00}, + //{0x3000, 0x00}, + {REG_DELAY, 0x1E},//wait_ms(30) + {REG_NULL, 0x00}, +}; + + +static __maybe_unused const struct regval imx415_linear_10bit_1920x1080_891M_regs_4lane[] = { + {0x3008, 0x7f}, + {0x300A, 0x5b}, + {0x3034, 0x05}, + // {0x301C, 0x04}, + {0x3020, 0x01}, + {0x3021, 0x01}, + {0x3022, 0x01}, + {0x3024, 0xca}, + {0x3025, 0x08}, + {0x3028, 0x26}, + {0x3029, 0x02}, + {0x3031, 0x00}, + {0x3032, 0x00}, + {0x3033, 0x05}, + {0x3050, 0xC4}, + {0x3090, 0x14}, + {0x30C1, 0x00}, + {0x30D9, 0x02}, + {0x30DA, 0x01}, + {0x3116, 0x24}, + {0x3118, 0xC0}, + {0x311A, 0xE0}, + {0x311E, 0x24}, + {0x32D4, 0x21}, + {0x32EC, 0xA1}, + {0x344C, 0x2B}, + {0x344D, 0x01}, + {0x344E, 0xED}, + {0x344F, 0x01}, + {0x3450, 0xF6}, + {0x3451, 0x02}, + {0x3452, 0x7F}, + {0x3453, 0x03}, + {0x358A, 0x04}, + {0x35A1, 0x02}, + {0x35EC, 0x27}, + {0x35EE, 0x8D}, + {0x35F0, 0x8D}, + {0x35F2, 0x29}, + {0x36BC, 0x0C}, + {0x36CC, 0x53}, + {0x36CD, 0x00}, + {0x36CE, 0x3C}, + {0x36D0, 0x8C}, + {0x36D1, 0x00}, + {0x36D2, 0x71}, + {0x36D4, 0x3C}, + {0x36D6, 0x53}, + {0x36D7, 0x00}, + {0x36D8, 0x71}, + {0x36DA, 0x8C}, + {0x36DB, 0x00}, + {0x3701, 0x00}, + {0x3720, 0x00}, + {0x3724, 0x02}, + {0x3726, 0x02}, + {0x3732, 0x02}, + {0x3734, 0x03}, + {0x3736, 0x03}, + {0x3742, 0x03}, + {0x3862, 0xE0}, + {0x38CC, 0x30}, + {0x38CD, 0x2F}, + {0x395C, 0x0C}, + {0x39A4, 0x07}, + {0x39A8, 0x32}, + {0x39AA, 0x32}, + {0x39AC, 0x32}, + {0x39AE, 0x32}, + {0x39B0, 0x32}, + {0x39B2, 0x2F}, + {0x39B4, 0x2D}, + {0x39B6, 0x28}, + {0x39B8, 0x30}, + {0x39BA, 0x30}, + {0x39BC, 0x30}, + {0x39BE, 0x30}, + {0x39C0, 0x30}, + {0x39C2, 0x2E}, + {0x39C4, 0x2B}, + {0x39C6, 0x25}, + {0x3A42, 0xD1}, + {0x3A4C, 0x77}, + {0x3AE0, 0x02}, + {0x3AEC, 0x0C}, + {0x3B00, 0x2E}, + {0x3B06, 0x29}, + {0x3B98, 0x25}, + {0x3B99, 0x21}, + {0x3B9B, 0x13}, + {0x3B9C, 0x13}, + {0x3B9D, 0x13}, + {0x3B9E, 0x13}, + {0x3BA1, 0x00}, + {0x3BA2, 0x06}, + {0x3BA3, 0x0B}, + {0x3BA4, 0x10}, + {0x3BA5, 0x14}, + {0x3BA6, 0x18}, + {0x3BA7, 0x1A}, + {0x3BA8, 0x1A}, + {0x3BA9, 0x1A}, + {0x3BAC, 0xED}, + {0x3BAD, 0x01}, + {0x3BAE, 0xF6}, + {0x3BAF, 0x02}, + {0x3BB0, 0xA2}, + {0x3BB1, 0x03}, + {0x3BB2, 0xE0}, + {0x3BB3, 0x03}, + {0x3BB4, 0xE0}, + {0x3BB5, 0x03}, + {0x3BB6, 0xE0}, + {0x3BB7, 0x03}, + {0x3BB8, 0xE0}, + {0x3BBA, 0xE0}, + {0x3BBC, 0xDA}, + {0x3BBE, 0x88}, + {0x3BC0, 0x44}, + {0x3BC2, 0x7B}, + {0x3BC4, 0xA2}, + {0x3BC8, 0xBD}, + {0x3BCA, 0xBD}, + {0x4001, 0x03}, + {0x4004, 0x48}, + {0x4005, 0x09}, + {0x400c, 0x00}, + {0x4018, 0xE7}, + {0x401A, 0x37}, + {0x401C, 0x37}, + {0x401E, 0xf7}, + {0x401F, 0x00}, + {0x4020, 0x3f}, + {0x4022, 0x6F}, + {0x4023, 0x00}, + {0x4024, 0x3f}, + {0x4026, 0x5f}, + {0x4028, 0x2F}, + {0x4074, 0x1}, + {0x3002, 0x00}, + //{0x3000, 0x00}, + {REG_DELAY, 0x1E},//wait_ms(30) + {REG_NULL, 0x00}, +}; + +/* + * Xclk 27Mhz + * 90.059fps + * CSI-2_2lane + * AD:10bit Output:12bit + * 2376Mbps + * Master Mode + * Time 9.999ms Gain:6dB + * 2568x1440 2/2-line binning & Window cropping + */ +static __maybe_unused const struct regval imx415_linear_12bit_1284x720_2376M_regs_2lane[] = { + {0x3008, 0x5D}, + {0x300A, 0x42}, + {0x301C, 0x04}, + {0x3020, 0x01}, + {0x3021, 0x01}, + {0x3022, 0x01}, + {0x3024, 0xAB}, + {0x3025, 0x07}, + {0x3028, 0xA4}, + {0x3029, 0x01}, + {0x3031, 0x00}, + {0x3033, 0x00}, + {0x3040, 0x88}, + {0x3041, 0x02}, + {0x3042, 0x08}, + {0x3043, 0x0A}, + {0x3044, 0xF0}, + {0x3045, 0x02}, + {0x3046, 0x40}, + {0x3047, 0x0B}, + {0x3050, 0xC4}, + {0x3090, 0x14}, + {0x30C1, 0x00}, + {0x30D9, 0x02}, + {0x30DA, 0x01}, + {0x3116, 0x23}, + {0x3118, 0x08}, + {0x3119, 0x01}, + {0x311A, 0xE7}, + {0x311E, 0x23}, + {0x32D4, 0x21}, + {0x32EC, 0xA1}, + {0x344C, 0x2B}, + {0x344D, 0x01}, + {0x344E, 0xED}, + {0x344F, 0x01}, + {0x3450, 0xF6}, + {0x3451, 0x02}, + {0x3452, 0x7F}, + {0x3453, 0x03}, + {0x358A, 0x04}, + {0x35A1, 0x02}, + {0x35EC, 0x27}, + {0x35EE, 0x8D}, + {0x35F0, 0x8D}, + {0x35F2, 0x29}, + {0x36BC, 0x0C}, + {0x36CC, 0x53}, + {0x36CD, 0x00}, + {0x36CE, 0x3C}, + {0x36D0, 0x8C}, + {0x36D1, 0x00}, + {0x36D2, 0x71}, + {0x36D4, 0x3C}, + {0x36D6, 0x53}, + {0x36D7, 0x00}, + {0x36D8, 0x71}, + {0x36DA, 0x8C}, + {0x36DB, 0x00}, + {0x3701, 0x00}, + {0x3720, 0x00}, + {0x3724, 0x02}, + {0x3726, 0x02}, + {0x3732, 0x02}, + {0x3734, 0x03}, + {0x3736, 0x03}, + {0x3742, 0x03}, + {0x3862, 0xE0}, + {0x38CC, 0x30}, + {0x38CD, 0x2F}, + {0x395C, 0x0C}, + {0x39A4, 0x07}, + {0x39A8, 0x32}, + {0x39AA, 0x32}, + {0x39AC, 0x32}, + {0x39AE, 0x32}, + {0x39B0, 0x32}, + {0x39B2, 0x2F}, + {0x39B4, 0x2D}, + {0x39B6, 0x28}, + {0x39B8, 0x30}, + {0x39BA, 0x30}, + {0x39BC, 0x30}, + {0x39BE, 0x30}, + {0x39C0, 0x30}, + {0x39C2, 0x2E}, + {0x39C4, 0x2B}, + {0x39C6, 0x25}, + {0x3A42, 0xD1}, + {0x3A4C, 0x77}, + {0x3AE0, 0x02}, + {0x3AEC, 0x0C}, + {0x3B00, 0x2E}, + {0x3B06, 0x29}, + {0x3B98, 0x25}, + {0x3B99, 0x21}, + {0x3B9B, 0x13}, + {0x3B9C, 0x13}, + {0x3B9D, 0x13}, + {0x3B9E, 0x13}, + {0x3BA1, 0x00}, + {0x3BA2, 0x06}, + {0x3BA3, 0x0B}, + {0x3BA4, 0x10}, + {0x3BA5, 0x14}, + {0x3BA6, 0x18}, + {0x3BA7, 0x1A}, + {0x3BA8, 0x1A}, + {0x3BA9, 0x1A}, + {0x3BAC, 0xED}, + {0x3BAD, 0x01}, + {0x3BAE, 0xF6}, + {0x3BAF, 0x02}, + {0x3BB0, 0xA2}, + {0x3BB1, 0x03}, + {0x3BB2, 0xE0}, + {0x3BB3, 0x03}, + {0x3BB4, 0xE0}, + {0x3BB5, 0x03}, + {0x3BB6, 0xE0}, + {0x3BB7, 0x03}, + {0x3BB8, 0xE0}, + {0x3BBA, 0xE0}, + {0x3BBC, 0xDA}, + {0x3BBE, 0x88}, + {0x3BC0, 0x44}, + {0x3BC2, 0x7B}, + {0x3BC4, 0xA2}, + {0x3BC8, 0xBD}, + {0x3BCA, 0xBD}, + {0x4001, 0x01}, + {0x4004, 0xC0}, + {0x4005, 0x06}, + {0x4018, 0xE7}, + {0x401A, 0x8F}, + {0x401C, 0x8F}, + {0x401E, 0x7F}, + {0x401F, 0x02}, + {0x4020, 0x97}, + {0x4022, 0x0F}, + {0x4023, 0x01}, + {0x4024, 0x97}, + {0x4026, 0xF7}, + {0x4028, 0x7F}, + {0x3002, 0x00}, + //{0x3000, 0x00}, + {REG_DELAY, 0x1E},//wait_ms(30) + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_linear_10bit_1284x720_891M_regs_2lane[] = { + {0x3008, 0x5D}, + {0x300A, 0x42}, + {0x301C, 0x04}, + {0x3020, 0x01}, + {0x3021, 0x01}, + {0x3022, 0x01}, + {0x3024, 0xAB}, + {0x3025, 0x07}, + {0x3028, 0xA4}, + {0x3029, 0x01}, + {0x3031, 0x00}, + {0x3033, 0x00}, + {0x3040, 0x88}, + {0x3041, 0x02}, + {0x3042, 0x08}, + {0x3043, 0x0A}, + {0x3044, 0xF0}, + {0x3045, 0x02}, + {0x3046, 0x40}, + {0x3047, 0x0B}, + {0x3050, 0xC4}, + {0x3090, 0x14}, + {0x30C1, 0x00}, + {0x30D9, 0x02}, + {0x30DA, 0x01}, + {0x3116, 0x23}, + {0x3118, 0x08}, + {0x3119, 0x01}, + {0x311A, 0xE7}, + {0x311E, 0x23}, + {0x32D4, 0x21}, + {0x32EC, 0xA1}, + {0x344C, 0x2B}, + {0x344D, 0x01}, + {0x344E, 0xED}, + {0x344F, 0x01}, + {0x3450, 0xF6}, + {0x3451, 0x02}, + {0x3452, 0x7F}, + {0x3453, 0x03}, + {0x358A, 0x04}, + {0x35A1, 0x02}, + {0x35EC, 0x27}, + {0x35EE, 0x8D}, + {0x35F0, 0x8D}, + {0x35F2, 0x29}, + {0x36BC, 0x0C}, + {0x36CC, 0x53}, + {0x36CD, 0x00}, + {0x36CE, 0x3C}, + {0x36D0, 0x8C}, + {0x36D1, 0x00}, + {0x36D2, 0x71}, + {0x36D4, 0x3C}, + {0x36D6, 0x53}, + {0x36D7, 0x00}, + {0x36D8, 0x71}, + {0x36DA, 0x8C}, + {0x36DB, 0x00}, + {0x3701, 0x00}, + {0x3720, 0x00}, + {0x3724, 0x02}, + {0x3726, 0x02}, + {0x3732, 0x02}, + {0x3734, 0x03}, + {0x3736, 0x03}, + {0x3742, 0x03}, + {0x3862, 0xE0}, + {0x38CC, 0x30}, + {0x38CD, 0x2F}, + {0x395C, 0x0C}, + {0x39A4, 0x07}, + {0x39A8, 0x32}, + {0x39AA, 0x32}, + {0x39AC, 0x32}, + {0x39AE, 0x32}, + {0x39B0, 0x32}, + {0x39B2, 0x2F}, + {0x39B4, 0x2D}, + {0x39B6, 0x28}, + {0x39B8, 0x30}, + {0x39BA, 0x30}, + {0x39BC, 0x30}, + {0x39BE, 0x30}, + {0x39C0, 0x30}, + {0x39C2, 0x2E}, + {0x39C4, 0x2B}, + {0x39C6, 0x25}, + {0x3A42, 0xD1}, + {0x3A4C, 0x77}, + {0x3AE0, 0x02}, + {0x3AEC, 0x0C}, + {0x3B00, 0x2E}, + {0x3B06, 0x29}, + {0x3B98, 0x25}, + {0x3B99, 0x21}, + {0x3B9B, 0x13}, + {0x3B9C, 0x13}, + {0x3B9D, 0x13}, + {0x3B9E, 0x13}, + {0x3BA1, 0x00}, + {0x3BA2, 0x06}, + {0x3BA3, 0x0B}, + {0x3BA4, 0x10}, + {0x3BA5, 0x14}, + {0x3BA6, 0x18}, + {0x3BA7, 0x1A}, + {0x3BA8, 0x1A}, + {0x3BA9, 0x1A}, + {0x3BAC, 0xED}, + {0x3BAD, 0x01}, + {0x3BAE, 0xF6}, + {0x3BAF, 0x02}, + {0x3BB0, 0xA2}, + {0x3BB1, 0x03}, + {0x3BB2, 0xE0}, + {0x3BB3, 0x03}, + {0x3BB4, 0xE0}, + {0x3BB5, 0x03}, + {0x3BB6, 0xE0}, + {0x3BB7, 0x03}, + {0x3BB8, 0xE0}, + {0x3BBA, 0xE0}, + {0x3BBC, 0xDA}, + {0x3BBE, 0x88}, + {0x3BC0, 0x44}, + {0x3BC2, 0x7B}, + {0x3BC4, 0xA2}, + {0x3BC8, 0xBD}, + {0x3BCA, 0xBD}, + {0x4001, 0x01}, + {0x4004, 0xC0}, + {0x4005, 0x06}, + {0x4018, 0xE7}, + {0x401A, 0x8F}, + {0x401C, 0x8F}, + {0x401E, 0x7F}, + {0x401F, 0x02}, + {0x4020, 0x97}, + {0x4022, 0x0F}, + {0x4023, 0x01}, + {0x4024, 0x97}, + {0x4026, 0xF7}, + {0x4028, 0x7F}, + {0x3002, 0x00}, + //{0x3000, 0x00}, + {REG_DELAY, 0x1E},//wait_ms(30) + {REG_NULL, 0x00}, +}; + + +/* + * The width and height must be configured to be + * the same as the current output resolution of the sensor. + * The input width of the isp needs to be 16 aligned. + * The input height of the isp needs to be 8 aligned. + * If the width or height does not meet the alignment rules, + * you can configure the cropping parameters with the following function to + * crop out the appropriate resolution. + * struct v4l2_subdev_pad_ops { + * .get_selection + * } + */ +static const struct imx415_mode supported_modes[] = { + /* + * frame rate = 1 / (Vtt * 1H) = 1 / (VMAX * 1H) + * VMAX >= (PIX_VWIDTH / 2) + 46 = height + 46 + */ + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 3864, + .height = 2192, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x08ca - 0x08, + .hts_def = 0x044c * IMX415_4LANES * 2, + .vts_def = 0x08ca, + .global_reg_list = imx415_global_10bit_3864x2192_regs, + .reg_list = imx415_linear_10bit_3864x2192_891M_regs, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 1, + .bpp = 10, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + /* 1H period = (1100 clock) = (1100 * 1 / 74.25MHz) */ + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 1944, + .height = 1096, + .max_fps = { + .numerator = 10000, + .denominator = 600000, + }, + .exp_def = 0x08ca - 0x08, + .hts_def = 0x044c *2, + .vts_def = 0x08ca, + .global_reg_list = NULL, + .reg_list = imx415_linear_10bit_1920x1080_891M_regs_4lane, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 1, + .bpp = 10, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = IMX415_XVCLK_FREQ_37M, + }, +#if 0 + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 3864, + .height = 2192, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x08fc * 2 - 0x0da8, + .hts_def = 0x0226 * IMX415_4LANES * 2, + /* + * IMX415 HDR mode T-line is half of Linear mode, + * make vts double to workaround. + */ + .vts_def = 0x08fc * 2, + .global_reg_list = imx415_global_10bit_3864x2192_regs, + .reg_list = imx415_hdr2_10bit_3864x2192_1485M_regs, + .hdr_mode = HDR_X2, + .mipi_freq_idx = 2, + .bpp = 10, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_1, + .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 + .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_1, + .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr2 + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 3864, + .height = 2192, + .max_fps = { + .numerator = 10000, + .denominator = 200000, + }, + .exp_def = 0x13e, + .hts_def = 0x021A * IMX415_4LANES * 2, + /* + * IMX415 HDR mode T-line is half of Linear mode, + * make vts double to workaround. + */ + .vts_def = 0x06BD * 4, + .global_reg_list = imx415_global_10bit_3864x2192_regs, + .reg_list = imx415_hdr3_10bit_3864x2192_1485M_regs, + .hdr_mode = HDR_X3, + .mipi_freq_idx = 2, + .bpp = 10, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_2, + .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr0 + .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 + .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_2,//S->csi wr2 + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 3864, + .height = 2192, + .max_fps = { + .numerator = 10000, + .denominator = 200000, + }, + .exp_def = 0x13e, + .hts_def = 0x01ca * IMX415_4LANES * 2, + /* + * IMX415 HDR mode T-line is half of Linear mode, + * make vts double to workaround. + */ + .vts_def = 0x07ea * 4, + .global_reg_list = imx415_global_10bit_3864x2192_regs, + .reg_list = imx415_hdr3_10bit_3864x2192_1782M_regs, + .hdr_mode = HDR_X3, + .mipi_freq_idx = 3, + .bpp = 10, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_2, + .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr0 + .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 + .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_2,//S->csi wr2 + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + /* 1H period = (1100 clock) = (1100 * 1 / 74.25MHz) */ + .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, + .width = 3864, + .height = 2192, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x08ca - 0x08, + .hts_def = 0x044c * IMX415_4LANES * 2, + .vts_def = 0x08ca, + .global_reg_list = imx415_global_12bit_3864x2192_regs, + .reg_list = imx415_linear_12bit_3864x2192_891M_regs, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 1, + .bpp = 12, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, + .width = 3864, + .height = 2192, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x08CA * 2 - 0x0d90, + .hts_def = 0x0226 * IMX415_4LANES * 2, + /* + * IMX415 HDR mode T-line is half of Linear mode, + * make vts double(that is FSC) to workaround. + */ + .vts_def = 0x08CA * 2, + .global_reg_list = imx415_global_12bit_3864x2192_regs, + .reg_list = imx415_hdr2_12bit_3864x2192_1782M_regs, + .hdr_mode = HDR_X2, + .mipi_freq_idx = 3, + .bpp = 12, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_1, + .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 + .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_1, + .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr2 + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, + .width = 3864, + .height = 2192, + .max_fps = { + .numerator = 10000, + .denominator = 200000, + }, + .exp_def = 0x114, + .hts_def = 0x0226 * IMX415_4LANES * 2, + /* + * IMX415 HDR mode T-line is half of Linear mode, + * make vts double(that is FSC) to workaround. + */ + .vts_def = 0x0696 * 4, + .global_reg_list = imx415_global_12bit_3864x2192_regs, + .reg_list = imx415_hdr3_12bit_3864x2192_1782M_regs, + .hdr_mode = HDR_X3, + .mipi_freq_idx = 3, + .bpp = 12, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_2, + .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr0 + .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 + .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_2,//S->csi wr2 + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, + .width = 1944, + .height = 1097, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x05dc - 0x08, + .hts_def = 0x030e * 3, + .vts_def = 0x0c5d, + .global_reg_list = imx415_global_12bit_3864x2192_regs, + .reg_list = imx415_linear_12bit_1932x1096_594M_regs, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 0, + .bpp = 12, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, + .width = 1944, + .height = 1097, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x08FC / 4, + .hts_def = 0x021A * 4, + /* + * IMX415 HDR mode T-line is half of Linear mode, + * make vts double(that is FSC) to workaround. + */ + .vts_def = 0x08FC * 2, + .global_reg_list = imx415_global_12bit_3864x2192_regs, + .reg_list = imx415_hdr2_12bit_1932x1096_891M_regs, + .hdr_mode = HDR_X2, + .mipi_freq_idx = 1, + .bpp = 12, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_1, + .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 + .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_1, + .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr2 + .xvclk = IMX415_XVCLK_FREQ_37M, + }, +#endif +}; + +static const struct imx415_mode supported_modes_2lane[] = { + { + /* 1H period = (1100 clock) = (1100 * 1 / 74.25MHz) */ + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 3864, + .height = 2192, + .max_fps = { + .numerator = 10000, + .denominator = 150000, + }, + .exp_def = 0x08ca - 0x08, + .hts_def = 0x0898 * IMX415_2LANES * 2, + .vts_def = 0x08ca, + .global_reg_list = NULL, + .reg_list = imx415_linear_10bit_3864x2192_891M_regs_2lane, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 1, + .bpp = 10, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + /* 1H period = (1100 clock) = (1100 * 1 / 74.25MHz) */ + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 1944, + .height = 1096, + .max_fps = { + .numerator = 10000, + .denominator = 600000, + }, + .exp_def = 0x08ca - 0x08, + .hts_def = 0x0800, + .vts_def = 0x08ca, + .global_reg_list = NULL, + .reg_list = imx415_linear_10bit_1920x1080_891M_regs_2lane, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 1, + .bpp = 10, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = IMX415_XVCLK_FREQ_37M, + }, +}; + +static const s64 link_freq_items[] = { + MIPI_FREQ_297M, + MIPI_FREQ_446M, + MIPI_FREQ_743M, + MIPI_FREQ_891M, + MIPI_FREQ_1188M, +}; + +/* Write registers up to 4 at a time */ +static int imx415_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; + + 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 imx415_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + if (!regs) { + dev_err(&client->dev, "write reg array error\n"); + return ret; + } + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + if (regs[i].addr == REG_DELAY) { + usleep_range(regs[i].val * 1000, regs[i].val * 1000 + 500); + dev_info(&client->dev, "write reg array, sleep %dms\n", regs[i].val); + } else { + ret = imx415_write_reg(client, regs[i].addr, + IMX415_REG_VALUE_08BIT, regs[i].val); + } + } + return ret; +} + +/* Read registers up to 4 at a time */ +static int imx415_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 imx415_get_reso_dist(const struct imx415_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct imx415_mode * +imx415_find_best_fit(struct imx415 *imx415, 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 < imx415->cfg_num; i++) { + dist = imx415_get_reso_dist(&imx415->supported_modes[i], framefmt); + if ((cur_best_fit_dist == -1 || dist < cur_best_fit_dist) && + imx415->supported_modes[i].bus_fmt == framefmt->code) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + dev_info(&imx415->client->dev, "%s: cur_best_fit(%d)", + __func__, cur_best_fit); + + return &imx415->supported_modes[cur_best_fit]; +} + +static int __imx415_power_on(struct imx415 *imx415); + +static void imx415_change_mode(struct imx415 *imx415, const struct imx415_mode *mode) +{ + if (imx415->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) { + imx415->is_thunderboot = false; + imx415->is_thunderboot_ng = true; + __imx415_power_on(imx415); + } + imx415->cur_mode = mode; + imx415->cur_vts = imx415->cur_mode->vts_def; + dev_info(&imx415->client->dev, "set fmt: cur_mode: %dx%d, hdr: %d, bpp: %d\n", + mode->width, mode->height, mode->hdr_mode, mode->bpp); +} + +static int imx415_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx415 *imx415 = to_imx415(sd); + const struct imx415_mode *mode; + s64 h_blank, vblank_def, vblank_min; + u64 pixel_rate = 0; + u8 lanes = imx415->lanes; + + mutex_lock(&imx415->mutex); + + mode = imx415_find_best_fit(imx415, fmt); + fmt->format.code = mode->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, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&imx415->mutex); + return -ENOTTY; +#endif + } else { + imx415_change_mode(imx415, mode); + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(imx415->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + /* VMAX >= (PIX_VWIDTH / 2) + 46 = height + 46 */ + vblank_min = (mode->height + 46) - mode->height; + __v4l2_ctrl_modify_range(imx415->vblank, vblank_min, + IMX415_VTS_MAX - mode->height, + 1, vblank_def); + __v4l2_ctrl_s_ctrl(imx415->vblank, vblank_def); + __v4l2_ctrl_s_ctrl(imx415->link_freq, mode->mipi_freq_idx); + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / + mode->bpp * 2 * lanes; + __v4l2_ctrl_s_ctrl_int64(imx415->pixel_rate, + pixel_rate); + } + dev_info(&imx415->client->dev, "%s: mode->mipi_freq_idx(%d)", + __func__, mode->mipi_freq_idx); + + mutex_unlock(&imx415->mutex); + + return 0; +} + +static int imx415_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx415 *imx415 = to_imx415(sd); + const struct imx415_mode *mode = imx415->cur_mode; + + mutex_lock(&imx415->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&imx415->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->bus_fmt; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR) + fmt->reserved[0] = mode->vc[fmt->pad]; + else + fmt->reserved[0] = mode->vc[PAD0]; + } + mutex_unlock(&imx415->mutex); + + return 0; +} + +static int imx415_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx415 *imx415 = to_imx415(sd); + + if (code->index >= imx415->cfg_num) + return -EINVAL; + + code->code = imx415->supported_modes[code->index].bus_fmt; + + return 0; +} + +static int imx415_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct imx415 *imx415 = to_imx415(sd); + + if (fse->index >= imx415->cfg_num) + return -EINVAL; + + if (fse->code != imx415->supported_modes[fse->index].bus_fmt) + return -EINVAL; + + fse->min_width = imx415->supported_modes[fse->index].width; + fse->max_width = imx415->supported_modes[fse->index].width; + fse->max_height = imx415->supported_modes[fse->index].height; + fse->min_height = imx415->supported_modes[fse->index].height; + + return 0; +} + +static int imx415_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct imx415 *imx415 = to_imx415(sd); + const struct imx415_mode *mode = imx415->cur_mode; + + fi->interval = mode->max_fps; + + return 0; +} + +static int imx415_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct imx415 *imx415 = to_imx415(sd); + const struct imx415_mode *mode = imx415->cur_mode; + u32 val = 0; + u8 lanes = imx415->lanes; + + val = 1 << (lanes - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + if (mode->hdr_mode != NO_HDR) + val |= V4L2_MBUS_CSI2_CHANNEL_1; + if (mode->hdr_mode == HDR_X3) + val |= V4L2_MBUS_CSI2_CHANNEL_2; + config->type = V4L2_MBUS_CSI2_DPHY; + config->flags = val; + + return 0; +} + +static void imx415_get_module_inf(struct imx415 *imx415, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, IMX415_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, imx415->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, imx415->len_name, sizeof(inf->base.lens)); +} + +static int imx415_set_hdrae_3frame(struct imx415 *imx415, + struct preisp_hdrae_exp_s *ae) +{ + struct i2c_client *client = imx415->client; + u32 l_exp_time, m_exp_time, s_exp_time; + u32 l_a_gain, m_a_gain, s_a_gain; + int shr2, shr1, shr0, rhs2, rhs1 = 0; + int rhs1_change_limit, rhs2_change_limit = 0; + static int rhs1_old = IMX415_RHS1_DEFAULT; + static int rhs2_old = IMX415_RHS2_DEFAULT; + int ret = 0; + u32 fsc; + int rhs1_max = 0; + int shr2_min = 0; + + if (!imx415->has_init_exp && !imx415->streaming) { + imx415->init_hdrae_exp = *ae; + imx415->has_init_exp = true; + dev_dbg(&imx415->client->dev, "imx415 is not streaming, save hdr ae!\n"); + return ret; + } + l_exp_time = ae->long_exp_reg; + m_exp_time = ae->middle_exp_reg; + s_exp_time = ae->short_exp_reg; + l_a_gain = ae->long_gain_reg; + m_a_gain = ae->middle_gain_reg; + s_a_gain = ae->short_gain_reg; + dev_dbg(&client->dev, + "rev exp req: L_exp: 0x%x, 0x%x, M_exp: 0x%x, 0x%x S_exp: 0x%x, 0x%x\n", + l_exp_time, m_exp_time, s_exp_time, + l_a_gain, m_a_gain, s_a_gain); + + ret = imx415_write_reg(client, IMX415_GROUP_HOLD_REG, + IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_START); + /* gain effect n+1 */ + ret |= imx415_write_reg(client, IMX415_LF_GAIN_REG_H, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_H(l_a_gain)); + ret |= imx415_write_reg(client, IMX415_LF_GAIN_REG_L, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_L(l_a_gain)); + ret |= imx415_write_reg(client, IMX415_SF1_GAIN_REG_H, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_H(m_a_gain)); + ret |= imx415_write_reg(client, IMX415_SF1_GAIN_REG_L, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_L(m_a_gain)); + ret |= imx415_write_reg(client, IMX415_SF2_GAIN_REG_H, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_H(s_a_gain)); + ret |= imx415_write_reg(client, IMX415_SF2_GAIN_REG_L, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_L(s_a_gain)); + + /* Restrictions + * FSC = 4 * VMAX and FSC should be 6n; + * exp_l = FSC - SHR0 + Toffset; + * + * SHR0 = FSC - exp_l + Toffset; + * SHR0 <= (FSC -12); + * SHR0 >= RHS2 + 13; + * SHR0 should be 3n; + * + * exp_m = RHS1 - SHR1 + Toffset; + * + * RHS1 < BRL * 3; + * RHS1 <= SHR2 - 13; + * RHS1 >= SHR1 + 12; + * SHR1 >= 13; + * SHR1 <= RHS1 - 12; + * RHS1(n+1) >= RHS1(n) + BRL * 3 -FSC + 3; + * + * SHR1 should be 3n+1 and RHS1 should be 6n+1; + * + * exp_s = RHS2 - SHR2 + Toffset; + * + * RHS2 < BRL * 3 + RHS1; + * RHS2 <= SHR0 - 13; + * RHS2 >= SHR2 + 12; + * SHR2 >= RHS1 + 13; + * SHR2 <= RHS2 - 12; + * RHS1(n+1) >= RHS1(n) + BRL * 3 -FSC + 3; + * + * SHR2 should be 3n+2 and RHS2 should be 6n+2; + */ + + /* The HDR mode vts is double by default to workaround T-line */ + fsc = imx415->cur_vts; + fsc = fsc / 6 * 6; + shr0 = fsc - l_exp_time; + dev_dbg(&client->dev, + "line(%d) shr0 %d, l_exp_time %d, fsc %d\n", + __LINE__, shr0, l_exp_time, fsc); + + rhs1 = (SHR1_MIN_X3 + m_exp_time + 5) / 6 * 6 + 1; + if (imx415->cur_mode->height == 2192) + rhs1_max = RHS1_MAX_X3(BRL_ALL); + else + rhs1_max = RHS1_MAX_X3(BRL_BINNING); + if (rhs1 < 25) + rhs1 = 25; + else if (rhs1 > rhs1_max) + rhs1 = rhs1_max; + dev_dbg(&client->dev, + "line(%d) rhs1 %d, m_exp_time %d rhs1_old %d\n", + __LINE__, rhs1, m_exp_time, rhs1_old); + + //Dynamic adjustment rhs2 must meet the following conditions + if (imx415->cur_mode->height == 2192) + rhs1_change_limit = rhs1_old + 3 * BRL_ALL - fsc + 3; + else + rhs1_change_limit = rhs1_old + 3 * BRL_BINNING - fsc + 3; + rhs1_change_limit = (rhs1_change_limit < 25) ? 25 : rhs1_change_limit; + rhs1_change_limit = (rhs1_change_limit + 5) / 6 * 6 + 1; + if (rhs1_max < rhs1_change_limit) { + dev_err(&client->dev, + "The total exposure limit makes rhs1 max is %d,but old rhs1 limit makes rhs1 min is %d\n", + rhs1_max, rhs1_change_limit); + return -EINVAL; + } + if (rhs1 < rhs1_change_limit) + rhs1 = rhs1_change_limit; + + dev_dbg(&client->dev, + "line(%d) m_exp_time %d rhs1_old %d, rhs1_new %d\n", + __LINE__, m_exp_time, rhs1_old, rhs1); + + rhs1_old = rhs1; + + /* shr1 = rhs1 - s_exp_time */ + if (rhs1 - m_exp_time <= SHR1_MIN_X3) { + shr1 = SHR1_MIN_X3; + m_exp_time = rhs1 - shr1; + } else { + shr1 = rhs1 - m_exp_time; + } + + shr2_min = rhs1 + 13; + rhs2 = (shr2_min + s_exp_time + 5) / 6 * 6 + 2; + if (rhs2 > (shr0 - 13)) + rhs2 = shr0 - 13; + else if (rhs2 < 50) + rhs2 = 50; + dev_dbg(&client->dev, + "line(%d) rhs2 %d, s_exp_time %d, rhs2_old %d\n", + __LINE__, rhs2, s_exp_time, rhs2_old); + + //Dynamic adjustment rhs2 must meet the following conditions + if (imx415->cur_mode->height == 2192) + rhs2_change_limit = rhs2_old + 3 * BRL_ALL - fsc + 3; + else + rhs2_change_limit = rhs2_old + 3 * BRL_BINNING - fsc + 3; + rhs2_change_limit = (rhs2_change_limit < 50) ? 50 : rhs2_change_limit; + rhs2_change_limit = (rhs2_change_limit + 5) / 6 * 6 + 2; + if ((shr0 - 13) < rhs2_change_limit) { + dev_err(&client->dev, + "The total exposure limit makes rhs2 max is %d,but old rhs1 limit makes rhs2 min is %d\n", + shr0 - 13, rhs2_change_limit); + return -EINVAL; + } + if (rhs2 < rhs2_change_limit) + rhs2 = rhs2_change_limit; + + rhs2_old = rhs2; + + /* shr2 = rhs2 - s_exp_time */ + if (rhs2 - s_exp_time <= shr2_min) { + shr2 = shr2_min; + s_exp_time = rhs2 - shr2; + } else { + shr2 = rhs2 - s_exp_time; + } + dev_dbg(&client->dev, + "line(%d) rhs2_new %d, s_exp_time %d shr2 %d, rhs2_change_limit %d\n", + __LINE__, rhs2, s_exp_time, shr2, rhs2_change_limit); + + if (shr0 < rhs2 + 13) + shr0 = rhs2 + 13; + else if (shr0 > fsc - 12) + shr0 = fsc - 12; + + dev_dbg(&client->dev, + "long exposure: l_exp_time=%d, fsc=%d, shr0=%d, l_a_gain=%d\n", + l_exp_time, fsc, shr0, l_a_gain); + dev_dbg(&client->dev, + "middle exposure(SEF1): m_exp_time=%d, rhs1=%d, shr1=%d, m_a_gain=%d\n", + m_exp_time, rhs1, shr1, m_a_gain); + dev_dbg(&client->dev, + "short exposure(SEF2): s_exp_time=%d, rhs2=%d, shr2=%d, s_a_gain=%d\n", + s_exp_time, rhs2, shr2, s_a_gain); + /* time effect n+1 */ + /* write SEF2 exposure RHS2 regs*/ + ret |= imx415_write_reg(client, + IMX415_RHS2_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_L(rhs2)); + ret |= imx415_write_reg(client, + IMX415_RHS2_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_M(rhs2)); + ret |= imx415_write_reg(client, + IMX415_RHS2_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_H(rhs2)); + /* write SEF2 exposure SHR2 regs*/ + ret |= imx415_write_reg(client, + IMX415_SF2_EXPO_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_L(shr2)); + ret |= imx415_write_reg(client, + IMX415_SF2_EXPO_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_M(shr2)); + ret |= imx415_write_reg(client, + IMX415_SF2_EXPO_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_H(shr2)); + /* write SEF1 exposure RHS1 regs*/ + ret |= imx415_write_reg(client, + IMX415_RHS1_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_L(rhs1)); + ret |= imx415_write_reg(client, + IMX415_RHS1_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_M(rhs1)); + ret |= imx415_write_reg(client, + IMX415_RHS1_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_H(rhs1)); + /* write SEF1 exposure SHR1 regs*/ + ret |= imx415_write_reg(client, + IMX415_SF1_EXPO_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_L(shr1)); + ret |= imx415_write_reg(client, + IMX415_SF1_EXPO_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_M(shr1)); + ret |= imx415_write_reg(client, + IMX415_SF1_EXPO_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_H(shr1)); + /* write LF exposure SHR0 regs*/ + ret |= imx415_write_reg(client, + IMX415_LF_EXPO_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_L(shr0)); + ret |= imx415_write_reg(client, + IMX415_LF_EXPO_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_M(shr0)); + ret |= imx415_write_reg(client, + IMX415_LF_EXPO_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_H(shr0)); + + ret |= imx415_write_reg(client, IMX415_GROUP_HOLD_REG, + IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_END); + return ret; +} + +static int imx415_set_hdrae(struct imx415 *imx415, + struct preisp_hdrae_exp_s *ae) +{ + struct i2c_client *client = imx415->client; + u32 l_exp_time, m_exp_time, s_exp_time; + u32 l_a_gain, m_a_gain, s_a_gain; + int shr1, shr0, rhs1, rhs1_max, rhs1_min; + static int rhs1_old = IMX415_RHS1_DEFAULT; + int ret = 0; + u32 fsc; + + if (!imx415->has_init_exp && !imx415->streaming) { + imx415->init_hdrae_exp = *ae; + imx415->has_init_exp = true; + dev_dbg(&imx415->client->dev, "imx415 is not streaming, save hdr ae!\n"); + return ret; + } + l_exp_time = ae->long_exp_reg; + m_exp_time = ae->middle_exp_reg; + s_exp_time = ae->short_exp_reg; + l_a_gain = ae->long_gain_reg; + m_a_gain = ae->middle_gain_reg; + s_a_gain = ae->short_gain_reg; + dev_dbg(&client->dev, + "rev exp req: L_exp: 0x%x, 0x%x, M_exp: 0x%x, 0x%x S_exp: 0x%x, 0x%x\n", + l_exp_time, m_exp_time, s_exp_time, + l_a_gain, m_a_gain, s_a_gain); + + if (imx415->cur_mode->hdr_mode == HDR_X2) { + l_a_gain = m_a_gain; + l_exp_time = m_exp_time; + } + + ret = imx415_write_reg(client, IMX415_GROUP_HOLD_REG, + IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_START); + /* gain effect n+1 */ + ret |= imx415_write_reg(client, IMX415_LF_GAIN_REG_H, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_H(l_a_gain)); + ret |= imx415_write_reg(client, IMX415_LF_GAIN_REG_L, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_L(l_a_gain)); + ret |= imx415_write_reg(client, IMX415_SF1_GAIN_REG_H, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_H(s_a_gain)); + ret |= imx415_write_reg(client, IMX415_SF1_GAIN_REG_L, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_L(s_a_gain)); + + /* Restrictions + * FSC = 2 * VMAX and FSC should be 4n; + * exp_l = FSC - SHR0 + Toffset; + * exp_l should be even value; + * + * SHR0 = FSC - exp_l + Toffset; + * SHR0 <= (FSC -8); + * SHR0 >= RHS1 + 9; + * SHR0 should be 2n; + * + * exp_s = RHS1 - SHR1 + Toffset; + * exp_s should be even value; + * + * RHS1 < BRL * 2; + * RHS1 <= SHR0 - 9; + * RHS1 >= SHR1 + 8; + * SHR1 >= 9; + * RHS1(n+1) >= RHS1(n) + BRL * 2 -FSC + 2; + * + * SHR1 should be 2n+1 and RHS1 should be 4n+1; + */ + + /* The HDR mode vts is double by default to workaround T-line */ + fsc = imx415->cur_vts; + shr0 = fsc - l_exp_time; + + if (imx415->cur_mode->height == 2192) { + rhs1_max = min(RHS1_MAX_X2(BRL_ALL), ((shr0 - 9u) / 4 * 4 + 1)); + rhs1_min = max(SHR1_MIN_X2 + 8u, rhs1_old + 2 * BRL_ALL - fsc + 2); + } else { + rhs1_max = min(RHS1_MAX_X2(BRL_BINNING), ((shr0 - 9u) / 4 * 4 + 1)); + rhs1_min = max(SHR1_MIN_X2 + 8u, rhs1_old + 2 * BRL_BINNING - fsc + 2); + } + rhs1_min = (rhs1_min + 3) / 4 * 4 + 1; + rhs1 = (SHR1_MIN_X2 + s_exp_time + 3) / 4 * 4 + 1;/* shall be 4n + 1 */ + dev_dbg(&client->dev, + "line(%d) rhs1 %d, rhs1 min %d rhs1 max %d\n", + __LINE__, rhs1, rhs1_min, rhs1_max); + if (rhs1_max < rhs1_min) { + dev_err(&client->dev, + "The total exposure limit makes rhs1 max is %d,but old rhs1 limit makes rhs1 min is %d\n", + rhs1_max, rhs1_min); + return -EINVAL; + } + rhs1 = clamp(rhs1, rhs1_min, rhs1_max); + dev_dbg(&client->dev, + "line(%d) rhs1 %d, short time %d rhs1_old %d, rhs1_new %d\n", + __LINE__, rhs1, s_exp_time, rhs1_old, rhs1); + + rhs1_old = rhs1; + + /* shr1 = rhs1 - s_exp_time */ + if (rhs1 - s_exp_time <= SHR1_MIN_X2) { + shr1 = SHR1_MIN_X2; + s_exp_time = rhs1 - shr1; + } else { + shr1 = rhs1 - s_exp_time; + } + + if (shr0 < rhs1 + 9) + shr0 = rhs1 + 9; + else if (shr0 > fsc - 8) + shr0 = fsc - 8; + + dev_dbg(&client->dev, + "fsc=%d,RHS1_MAX=%d,SHR1_MIN=%d,rhs1_max=%d\n", + fsc, RHS1_MAX_X2(BRL_ALL), SHR1_MIN_X2, rhs1_max); + dev_dbg(&client->dev, + "l_exp_time=%d,s_exp_time=%d,shr0=%d,shr1=%d,rhs1=%d,l_a_gain=%d,s_a_gain=%d\n", + l_exp_time, s_exp_time, shr0, shr1, rhs1, l_a_gain, s_a_gain); + /* time effect n+2 */ + ret |= imx415_write_reg(client, + IMX415_RHS1_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_L(rhs1)); + ret |= imx415_write_reg(client, + IMX415_RHS1_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_M(rhs1)); + ret |= imx415_write_reg(client, + IMX415_RHS1_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_H(rhs1)); + + ret |= imx415_write_reg(client, + IMX415_SF1_EXPO_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_L(shr1)); + ret |= imx415_write_reg(client, + IMX415_SF1_EXPO_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_M(shr1)); + ret |= imx415_write_reg(client, + IMX415_SF1_EXPO_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_H(shr1)); + ret |= imx415_write_reg(client, + IMX415_LF_EXPO_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_L(shr0)); + ret |= imx415_write_reg(client, + IMX415_LF_EXPO_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_M(shr0)); + ret |= imx415_write_reg(client, + IMX415_LF_EXPO_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_H(shr0)); + + ret |= imx415_write_reg(client, IMX415_GROUP_HOLD_REG, + IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_END); + return ret; +} + +static int imx415_get_channel_info(struct imx415 *imx415, struct rkmodule_channel_info *ch_info) +{ + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + ch_info->vc = imx415->cur_mode->vc[ch_info->index]; + ch_info->width = imx415->cur_mode->width; + ch_info->height = imx415->cur_mode->height; + ch_info->bus_fmt = imx415->cur_mode->bus_fmt; + return 0; +} + +static long imx415_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct imx415 *imx415 = to_imx415(sd); + struct rkmodule_hdr_cfg *hdr; + struct rkmodule_channel_info *ch_info; + u32 h, w, stream; + long ret = 0; + const struct imx415_mode *mode; + u64 pixel_rate = 0; + struct rkmodule_csi_dphy_param *dphy_param; + u8 lanes = imx415->lanes; + + switch (cmd) { + case PREISP_CMD_SET_HDRAE_EXP: + if (imx415->cur_mode->hdr_mode == HDR_X2) + ret = imx415_set_hdrae(imx415, arg); + else if (imx415->cur_mode->hdr_mode == HDR_X3) + ret = imx415_set_hdrae_3frame(imx415, arg); + break; + case RKMODULE_GET_MODULE_INFO: + imx415_get_module_inf(imx415, (struct rkmodule_inf *)arg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = (struct rkmodule_hdr_cfg *)arg; + hdr->esp.mode = HDR_NORMAL_VC; + hdr->hdr_mode = imx415->cur_mode->hdr_mode; + break; + case RKMODULE_SET_HDR_CFG: + + imx415_change_mode(imx415, &imx415->supported_modes[imx415->capture_mode]); + + if(imx415->capture_mode == 0) + dev_info(&imx415->client->dev, "capture_mode is 3840x2160\n"); + else if(imx415->capture_mode == 1) + dev_info(&imx415->client->dev, "capture_mode is 1920x1080\n"); + else + dev_info(&imx415->client->dev, "not support capture_mode0\n"); + + mode = imx415->cur_mode; + if (imx415->streaming) { + ret = imx415_write_reg(imx415->client, IMX415_GROUP_HOLD_REG, + IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_START); + + ret |= imx415_write_array(imx415->client, imx415->cur_mode->reg_list); + + ret |= imx415_write_reg(imx415->client, IMX415_GROUP_HOLD_REG, + IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_END); + if (ret) + return ret; + } + w = mode->hts_def - imx415->cur_mode->width; + h = mode->vts_def - mode->height; + mutex_lock(&imx415->mutex); + __v4l2_ctrl_modify_range(imx415->hblank, w, w, 1, w); + __v4l2_ctrl_modify_range(imx415->vblank, h, + IMX415_VTS_MAX - mode->height, + 1, h); + __v4l2_ctrl_s_ctrl(imx415->link_freq, mode->mipi_freq_idx); + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / + mode->bpp * 2 * lanes; + __v4l2_ctrl_s_ctrl_int64(imx415->pixel_rate, + pixel_rate); + mutex_unlock(&imx415->mutex); + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = imx415_write_reg(imx415->client, IMX415_REG_CTRL_MODE, + IMX415_REG_VALUE_08BIT, IMX415_MODE_STREAMING); + else + ret = imx415_write_reg(imx415->client, IMX415_REG_CTRL_MODE, + IMX415_REG_VALUE_08BIT, IMX415_MODE_SW_STANDBY); + break; + case RKMODULE_GET_SONY_BRL: + if (imx415->cur_mode->width == 3864 && imx415->cur_mode->height == 2192) + *((u32 *)arg) = BRL_ALL; + else + *((u32 *)arg) = BRL_BINNING; + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = imx415_get_channel_info(imx415, ch_info); + break; + case RKMODULE_GET_CSI_DPHY_PARAM: + if (imx415->cur_mode->hdr_mode == HDR_X2) { + dphy_param = (struct rkmodule_csi_dphy_param *)arg; + *dphy_param = dcphy_param; + dev_info(&imx415->client->dev, + "get sensor dphy param\n"); + } else + ret = -EINVAL; + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long imx415_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; + struct rkmodule_hdr_cfg *hdr; + struct preisp_hdrae_exp_s *hdrae; + struct rkmodule_channel_info *ch_info; + long ret; + u32 stream; + u32 brl = 0; + struct rkmodule_csi_dphy_param *dphy_param; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = imx415_ioctl(sd, cmd, inf); + if (!ret) { + if (copy_to_user(up, inf, sizeof(*inf))) { + kfree(inf); + return -EFAULT; + } + } + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + if (copy_from_user(cfg, up, sizeof(*cfg))) { + kfree(cfg); + return -EFAULT; + } + ret = imx415_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = imx415_ioctl(sd, cmd, hdr); + if (!ret) { + if (copy_to_user(up, hdr, sizeof(*hdr))) { + kfree(hdr); + return -EFAULT; + } + } + kfree(hdr); + break; + case RKMODULE_SET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + if (copy_from_user(hdr, up, sizeof(*hdr))) { + kfree(hdr); + return -EFAULT; + } + ret = imx415_ioctl(sd, cmd, hdr); + kfree(hdr); + break; + case PREISP_CMD_SET_HDRAE_EXP: + hdrae = kzalloc(sizeof(*hdrae), GFP_KERNEL); + if (!hdrae) { + ret = -ENOMEM; + return ret; + } + + if (copy_from_user(hdrae, up, sizeof(*hdrae))) { + kfree(hdrae); + return -EFAULT; + } + ret = imx415_ioctl(sd, cmd, hdrae); + kfree(hdrae); + break; + case RKMODULE_SET_QUICK_STREAM: + if (copy_from_user(&stream, up, sizeof(u32))) + return -EFAULT; + ret = imx415_ioctl(sd, cmd, &stream); + break; + case RKMODULE_GET_SONY_BRL: + ret = imx415_ioctl(sd, cmd, &brl); + if (!ret) { + if (copy_to_user(up, &brl, sizeof(u32))) + return -EFAULT; + } + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); + if (!ch_info) { + ret = -ENOMEM; + return ret; + } + + ret = imx415_ioctl(sd, cmd, ch_info); + if (!ret) { + ret = copy_to_user(up, ch_info, sizeof(*ch_info)); + if (ret) + ret = -EFAULT; + } + kfree(ch_info); + break; + case RKMODULE_GET_CSI_DPHY_PARAM: + dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL); + if (!dphy_param) { + ret = -ENOMEM; + return ret; + } + + ret = imx415_ioctl(sd, cmd, dphy_param); + if (!ret) { + ret = copy_to_user(up, dphy_param, sizeof(*dphy_param)); + if (ret) + ret = -EFAULT; + } + kfree(dphy_param); + break; + + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + + +static int __imx415_start_stream(struct imx415 *imx415) +{ + int ret; + + if (!imx415->is_thunderboot) { + ret = imx415_write_array(imx415->client, imx415->cur_mode->global_reg_list); + if (ret) + return ret; + ret = imx415_write_array(imx415->client, imx415->cur_mode->reg_list); + if (ret) + return ret; + } + + /* In case these controls are set before streaming */ + ret = __v4l2_ctrl_handler_setup(&imx415->ctrl_handler); + if (ret) + return ret; + if (imx415->has_init_exp && imx415->cur_mode->hdr_mode != NO_HDR) { + ret = imx415_ioctl(&imx415->subdev, PREISP_CMD_SET_HDRAE_EXP, + &imx415->init_hdrae_exp); + if (ret) { + dev_err(&imx415->client->dev, + "init exp fail in hdr mode\n"); + return ret; + } + } + return imx415_write_reg(imx415->client, IMX415_REG_CTRL_MODE, + IMX415_REG_VALUE_08BIT, 0); +} + +static int __imx415_stop_stream(struct imx415 *imx415) +{ + imx415->has_init_exp = false; + if (imx415->is_thunderboot) + imx415->is_first_streamoff = true; + return imx415_write_reg(imx415->client, IMX415_REG_CTRL_MODE, + IMX415_REG_VALUE_08BIT, 1); +} + +static int imx415_s_stream(struct v4l2_subdev *sd, int on) +{ + struct imx415 *imx415 = to_imx415(sd); + struct i2c_client *client = imx415->client; + int ret = 0; + + dev_info(&imx415->client->dev, "s_stream: %d. %dx%d, hdr: %d, bpp: %d\n", + on, imx415->cur_mode->width, imx415->cur_mode->height, + imx415->cur_mode->hdr_mode, imx415->cur_mode->bpp); + + mutex_lock(&imx415->mutex); + on = !!on; + if (on == imx415->streaming) + goto unlock_and_return; + + if (on) { + if (imx415->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) { + imx415->is_thunderboot = false; + __imx415_power_on(imx415); + } + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __imx415_start_stream(imx415); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __imx415_stop_stream(imx415); + pm_runtime_put(&client->dev); + } + + imx415->streaming = on; + +unlock_and_return: + mutex_unlock(&imx415->mutex); + + return ret; +} + +static int imx415_s_power(struct v4l2_subdev *sd, int on) +{ + struct imx415 *imx415 = to_imx415(sd); + struct i2c_client *client = imx415->client; + int ret = 0; + + mutex_lock(&imx415->mutex); + + if (imx415->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; + } + imx415->power_on = true; + } else { + pm_runtime_put(&client->dev); + imx415->power_on = false; + } + +unlock_and_return: + mutex_unlock(&imx415->mutex); + + return ret; +} + +int __imx415_power_on(struct imx415 *imx415) +{ + int ret; + struct device *dev = &imx415->client->dev; + if (!IS_ERR_OR_NULL(imx415->pins_default)) { + ret = pinctrl_select_state(imx415->pinctrl, + imx415->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + if (!imx415->is_thunderboot) { + if (!IS_ERR(imx415->power_gpio)) + gpiod_direction_output(imx415->power_gpio, 1); + /* At least 500ns between power raising and XCLR */ + /* fix power on timing if insmod this ko */ + usleep_range(10 * 1000, 20 * 1000); + if (!IS_ERR(imx415->reset_gpio)) + gpiod_direction_output(imx415->reset_gpio, 0); + + /* At least 1us between XCLR and clk */ + /* fix power on timing if insmod this ko */ + usleep_range(10 * 1000, 20 * 1000); + } + ret = clk_set_rate(imx415->xvclk, imx415->cur_mode->xvclk); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate\n"); + if (clk_get_rate(imx415->xvclk) != imx415->cur_mode->xvclk) + dev_warn(dev, "xvclk mismatched\n"); + ret = clk_prepare_enable(imx415->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + goto err_clk; + } + + /* At least 20us between XCLR and I2C communication */ + usleep_range(20*1000, 30*1000); + + return 0; + +err_clk: + if (!IS_ERR(imx415->reset_gpio)) + gpiod_direction_output(imx415->reset_gpio, 1); + if (!IS_ERR_OR_NULL(imx415->pins_sleep)) + pinctrl_select_state(imx415->pinctrl, imx415->pins_sleep); + + return ret; +} + +static void __imx415_power_off(struct imx415 *imx415) +{ + int ret; + struct device *dev = &imx415->client->dev; + + if (imx415->is_thunderboot) { + if (imx415->is_first_streamoff) { + imx415->is_thunderboot = false; + imx415->is_first_streamoff = false; + } else { + return; + } + } + + if (!IS_ERR(imx415->reset_gpio)) + gpiod_direction_output(imx415->reset_gpio, 1); + clk_disable_unprepare(imx415->xvclk); + if (!IS_ERR_OR_NULL(imx415->pins_sleep)) { + ret = pinctrl_select_state(imx415->pinctrl, + imx415->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + if (!IS_ERR(imx415->power_gpio)) + gpiod_direction_output(imx415->power_gpio, 0); + regulator_bulk_disable(IMX415_NUM_SUPPLIES, imx415->supplies); +} + +static int __maybe_unused imx415_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx415 *imx415 = to_imx415(sd); + + return __imx415_power_on(imx415); +} + +static int __maybe_unused imx415_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx415 *imx415 = to_imx415(sd); + + __imx415_power_off(imx415); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int imx415_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct imx415 *imx415 = to_imx415(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct imx415_mode *def_mode = &imx415->supported_modes[0]; + + mutex_lock(&imx415->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = def_mode->bus_fmt; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&imx415->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int imx415_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct imx415 *imx415 = to_imx415(sd); + + if (fie->index >= imx415->cfg_num) + return -EINVAL; + + fie->code = imx415->supported_modes[fie->index].bus_fmt; + fie->width = imx415->supported_modes[fie->index].width; + fie->height = imx415->supported_modes[fie->index].height; + fie->interval = imx415->supported_modes[fie->index].max_fps; + fie->reserved[0] = imx415->supported_modes[fie->index].hdr_mode; + return 0; +} + +#define CROP_START(SRC, DST) (((SRC) - (DST)) / 2 / 4 * 4) +#define DST_WIDTH_3840 3840 +#define DST_HEIGHT_2160 2160 +#define DST_WIDTH_1920 1920 +#define DST_HEIGHT_1080 1080 +#define DST_WIDTH_1280 1280 +#define DST_HEIGHT_720 720 + +/* + * The resolution of the driver configuration needs to be exactly + * the same as the current output resolution of the sensor, + * the input width of the isp needs to be 16 aligned, + * the input height of the isp needs to be 8 aligned. + * Can be cropped to standard resolution by this function, + * otherwise it will crop out strange resolution according + * to the alignment rules. + */ +static int imx415_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct imx415 *imx415 = to_imx415(sd); + + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + if (imx415->cur_mode->width == 3864) { + sel->r.left = CROP_START(imx415->cur_mode->width, DST_WIDTH_3840); + sel->r.width = DST_WIDTH_3840; + sel->r.top = CROP_START(imx415->cur_mode->height, DST_HEIGHT_2160); + sel->r.height = DST_HEIGHT_2160; + } else if (imx415->cur_mode->width == 1944) { + sel->r.left = CROP_START(imx415->cur_mode->width, DST_WIDTH_1920); + sel->r.width = DST_WIDTH_1920; + sel->r.top = CROP_START(imx415->cur_mode->height, DST_HEIGHT_1080); + sel->r.height = DST_HEIGHT_1080; + } else if (imx415->cur_mode->width == 1284) { + sel->r.left = CROP_START(imx415->cur_mode->width, DST_WIDTH_1280); + sel->r.width = DST_WIDTH_1280; + sel->r.top = CROP_START(imx415->cur_mode->height, DST_HEIGHT_720); + sel->r.height = DST_HEIGHT_720; + } else { + sel->r.left = CROP_START(imx415->cur_mode->width, imx415->cur_mode->width); + sel->r.width = imx415->cur_mode->width; + sel->r.top = CROP_START(imx415->cur_mode->height, imx415->cur_mode->height); + sel->r.height = imx415->cur_mode->height; + } + return 0; + } + return -EINVAL; +} + +static const struct dev_pm_ops imx415_pm_ops = { + SET_RUNTIME_PM_OPS(imx415_runtime_suspend, + imx415_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops imx415_internal_ops = { + .open = imx415_open, +}; +#endif + +static const struct v4l2_subdev_core_ops imx415_core_ops = { + .s_power = imx415_s_power, + .ioctl = imx415_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = imx415_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops imx415_video_ops = { + .s_stream = imx415_s_stream, + .g_frame_interval = imx415_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops imx415_pad_ops = { + .enum_mbus_code = imx415_enum_mbus_code, + .enum_frame_size = imx415_enum_frame_sizes, + .enum_frame_interval = imx415_enum_frame_interval, + .get_fmt = imx415_get_fmt, + .set_fmt = imx415_set_fmt, + .get_selection = imx415_get_selection, + .get_mbus_config = imx415_g_mbus_config, +}; + +static const struct v4l2_subdev_ops imx415_subdev_ops = { + .core = &imx415_core_ops, + .video = &imx415_video_ops, + .pad = &imx415_pad_ops, +}; + +static int imx415_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx415 *imx415 = container_of(ctrl->handler, + struct imx415, ctrl_handler); + struct i2c_client *client = imx415->client; + s64 max; + u32 vts = 0, val; + int ret = 0; + u32 shr0 = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + if (imx415->cur_mode->hdr_mode == NO_HDR) { + /* Update max exposure while meeting expected vblanking */ + max = imx415->cur_mode->height + ctrl->val - 8; + __v4l2_ctrl_modify_range(imx415->exposure, + imx415->exposure->minimum, max, + imx415->exposure->step, + imx415->exposure->default_value); + } + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if (imx415->cur_mode->hdr_mode != NO_HDR) + goto ctrl_end; + shr0 = imx415->cur_vts - ctrl->val; + ret = imx415_write_reg(imx415->client, IMX415_LF_EXPO_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_L(shr0)); + ret |= imx415_write_reg(imx415->client, IMX415_LF_EXPO_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_M(shr0)); + ret |= imx415_write_reg(imx415->client, IMX415_LF_EXPO_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_H(shr0)); + dev_dbg(&client->dev, "set exposure(shr0) %d = cur_vts(%d) - val(%d)\n", + shr0, imx415->cur_vts, ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + if (imx415->cur_mode->hdr_mode != NO_HDR) + goto ctrl_end; + ret = imx415_write_reg(imx415->client, IMX415_LF_GAIN_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_GAIN_H(ctrl->val)); + ret |= imx415_write_reg(imx415->client, IMX415_LF_GAIN_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_GAIN_L(ctrl->val)); + dev_dbg(&client->dev, "set analog gain 0x%x\n", + ctrl->val); + break; + case V4L2_CID_VBLANK: + vts = ctrl->val + imx415->cur_mode->height; + /* + * vts of hdr mode is double to correct T-line calculation. + * Restore before write to reg. + */ + if (imx415->cur_mode->hdr_mode == HDR_X2) { + vts = (vts + 3) / 4 * 4; + imx415->cur_vts = vts; + vts /= 2; + } else if (imx415->cur_mode->hdr_mode == HDR_X3) { + vts = (vts + 11) / 12 * 12; + imx415->cur_vts = vts; + vts /= 4; + } else { + imx415->cur_vts = vts; + } + ret = imx415_write_reg(imx415->client, IMX415_VTS_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_VTS_L(vts)); + ret |= imx415_write_reg(imx415->client, IMX415_VTS_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_VTS_M(vts)); + ret |= imx415_write_reg(imx415->client, IMX415_VTS_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_VTS_H(vts)); + dev_dbg(&client->dev, "set vblank 0x%x vts %d\n", + ctrl->val, vts); + break; + case V4L2_CID_HFLIP: + ret = imx415_read_reg(imx415->client, IMX415_FLIP_REG, + IMX415_REG_VALUE_08BIT, &val); + if (ret) + break; + if (ctrl->val) + val |= IMX415_MIRROR_BIT_MASK; + else + val &= ~IMX415_MIRROR_BIT_MASK; + ret = imx415_write_reg(imx415->client, IMX415_FLIP_REG, + IMX415_REG_VALUE_08BIT, val); + break; + case V4L2_CID_VFLIP: + ret = imx415_read_reg(imx415->client, IMX415_FLIP_REG, + IMX415_REG_VALUE_08BIT, &val); + if (ret) + break; + if (ctrl->val) + val |= IMX415_FLIP_BIT_MASK; + else + val &= ~IMX415_FLIP_BIT_MASK; + ret = imx415_write_reg(imx415->client, IMX415_FLIP_REG, + IMX415_REG_VALUE_08BIT, val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + +ctrl_end: + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx415_ctrl_ops = { + .s_ctrl = imx415_set_ctrl, +}; + +static int imx415_initialize_controls(struct imx415 *imx415) +{ + const struct imx415_mode *mode; + struct v4l2_ctrl_handler *handler; + s64 exposure_max, vblank_def; + u64 pixel_rate; + u64 max_pixel_rate; + u32 h_blank; + int ret; + u8 lanes = imx415->lanes; + + handler = &imx415->ctrl_handler; + mode = imx415->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &imx415->mutex; + + imx415->link_freq = v4l2_ctrl_new_int_menu(handler, NULL, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_items) - 1, 0, + link_freq_items); + v4l2_ctrl_s_ctrl(imx415->link_freq, mode->mipi_freq_idx); + + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * lanes; + max_pixel_rate = MIPI_FREQ_1188M / mode->bpp * 2 * lanes; + imx415->pixel_rate = v4l2_ctrl_new_std(handler, NULL, + V4L2_CID_PIXEL_RATE, 0, max_pixel_rate, + 1, pixel_rate); + + h_blank = mode->hts_def - mode->width; + imx415->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (imx415->hblank) + imx415->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + imx415->vblank = v4l2_ctrl_new_std(handler, &imx415_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + IMX415_VTS_MAX - mode->height, + 1, vblank_def); + imx415->cur_vts = mode->vts_def; + + exposure_max = mode->vts_def - 8; + imx415->exposure = v4l2_ctrl_new_std(handler, &imx415_ctrl_ops, + V4L2_CID_EXPOSURE, IMX415_EXPOSURE_MIN, + exposure_max, IMX415_EXPOSURE_STEP, + mode->exp_def); + + imx415->anal_a_gain = v4l2_ctrl_new_std(handler, &imx415_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, IMX415_GAIN_MIN, + IMX415_GAIN_MAX, IMX415_GAIN_STEP, + IMX415_GAIN_DEFAULT); + + v4l2_ctrl_new_std(handler, &imx415_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(handler, &imx415_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (handler->error) { + ret = handler->error; + dev_err(&imx415->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + imx415->subdev.ctrl_handler = handler; + imx415->has_init_exp = false; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int imx415_check_sensor_id(struct imx415 *imx415, + struct i2c_client *client) +{ + struct device *dev = &imx415->client->dev; + u32 id = 0; + int ret; + + if (imx415->is_thunderboot) { + dev_info(dev, "Enable thunderboot mode, skip sensor id check\n"); + return 0; + } + + ret = imx415_read_reg(client, IMX415_REG_CHIP_ID, + IMX415_REG_VALUE_08BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + + dev_info(dev, "Detected imx415 id %06x\n", CHIP_ID); + + return 0; +} + +static int imx415_configure_regulators(struct imx415 *imx415) +{ + unsigned int i; + + for (i = 0; i < IMX415_NUM_SUPPLIES; i++) + imx415->supplies[i].supply = imx415_supply_names[i]; + + return devm_regulator_bulk_get(&imx415->client->dev, + IMX415_NUM_SUPPLIES, + imx415->supplies); +} + +static int imx415_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct imx415 *imx415; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + u32 hdr_mode; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + imx415 = devm_kzalloc(dev, sizeof(*imx415), GFP_KERNEL); + if (!imx415) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &imx415->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &imx415->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &imx415->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &imx415->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + ret = of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode); + if (ret) { + hdr_mode = NO_HDR; + dev_warn(dev, " Get hdr mode failed! no hdr default\n"); + } + imx415->client = client; + + ret = of_property_read_u32(node, DATA_LANES, &imx415->lanes); + if (ret) { + imx415->lanes = 4; + dev_warn(dev, " Get data lanes failed! 4 lanes default\n"); + } + if (imx415->lanes == IMX415_4LANES) { + imx415->supported_modes = supported_modes; + imx415->cfg_num = ARRAY_SIZE(supported_modes); + } else { + imx415->supported_modes = supported_modes_2lane; + imx415->cfg_num = ARRAY_SIZE(supported_modes_2lane); + } + dev_info(dev, "detect imx415 lane %d\n", + imx415->lanes); + + imx415->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP); + + ret = of_property_read_u32(node, OF_CAMERA_CAPTURE_MODE, &imx415->capture_mode); + if (ret) { + imx415->capture_mode = 0; + dev_warn(dev, " Get capture_mode failed! captrue revolution setting to 3840x2160\n"); + } + + imx415->cur_mode = &imx415->supported_modes[imx415->capture_mode]; + + imx415->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(imx415->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + imx415->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS); + if (IS_ERR(imx415->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + imx415->power_gpio = devm_gpiod_get(dev, "power", GPIOD_ASIS); + if (IS_ERR(imx415->power_gpio)) + dev_warn(dev, "Failed to get power-gpios\n"); + imx415->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(imx415->pinctrl)) { + imx415->pins_default = + pinctrl_lookup_state(imx415->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(imx415->pins_default)) + dev_info(dev, "could not get default pinstate\n"); + + imx415->pins_sleep = + pinctrl_lookup_state(imx415->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(imx415->pins_sleep)) + dev_info(dev, "could not get sleep pinstate\n"); + } else { + dev_info(dev, "no pinctrl\n"); + } + + ret = imx415_configure_regulators(imx415); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&imx415->mutex); + + sd = &imx415->subdev; + v4l2_i2c_subdev_init(sd, client, &imx415_subdev_ops); + ret = imx415_initialize_controls(imx415); + if (ret) + goto err_destroy_mutex; + + ret = __imx415_power_on(imx415); + if (ret) + goto err_free_handler; + + ret = imx415_check_sensor_id(imx415, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &imx415_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + imx415->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &imx415->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(imx415->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + imx415->module_index, facing, + IMX415_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(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); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __imx415_power_off(imx415); +err_free_handler: + v4l2_ctrl_handler_free(&imx415->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&imx415->mutex); + + return ret; +} + +static int imx415_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx415 *imx415 = to_imx415(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&imx415->ctrl_handler); + mutex_destroy(&imx415->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __imx415_power_off(imx415); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id imx415_of_match[] = { + { .compatible = "sony,imx415" }, + {}, +}; +MODULE_DEVICE_TABLE(of, imx415_of_match); +#endif + +static const struct i2c_device_id imx415_match_id[] = { + { "sony,imx415", 0 }, + { }, +}; + +static struct i2c_driver imx415_i2c_driver = { + .driver = { + .name = IMX415_NAME, + .pm = &imx415_pm_ops, + .of_match_table = of_match_ptr(imx415_of_match), + }, + .probe = &imx415_probe, + .remove = &imx415_remove, + .id_table = imx415_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&imx415_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&imx415_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("Sony imx415 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx708.c b/drivers/media/i2c/imx708.c new file mode 100644 index 0000000000000..097f92e1f16e1 --- /dev/null +++ b/drivers/media/i2c/imx708.c @@ -0,0 +1,2121 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * imx708 driver + * + * Copyright (C) 2017 Rockchip Electronics Co., Ltd. + * V0.0X01.0X00 init version. + */ + +//#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "otp_eeprom.h" + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x00) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define IMX708_LINK_FREQ_450MHZ 450000000 // 800Mbps per lane +#define IMX708_LINK_FREQ_447MHZ 447000000 // 1250Mbps per lane +#define IMX708_LINK_FREQ_453MHZ 453000000 + +#define IMX708_LANES 4 + +#define PIXEL_RATE_WITH_848M_10BIT (IMX708_LINK_FREQ_400 * 2 / 10 * 4) +#define PIXEL_RATE_WITH_848M_12BIT (IMX708_LINK_FREQ_400 * 2 / 12 * 4) + +#define IMX708_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x0708 +#define IMX708_REG_CHIP_ID_H 0x0016 +#define IMX708_REG_CHIP_ID_L 0x0017 + +#define IMX708_REG_CTRL_MODE 0x0100 +#define IMX708_MODE_SW_STANDBY 0x0 +#define IMX708_MODE_STREAMING 0x1 + +#define IMX708_REG_EXPOSURE_H 0x0202 +#define IMX708_REG_EXPOSURE_L 0x0203 +#define IMX708_EXPOSURE_OFFSET 48 +#define IMX708_EXPOSURE_MIN 1 +#define IMX708_EXPOSURE_STEP 1 +#define IMX708_EXPOSURE_MAX (IMX708_VTS_MAX - IMX708_EXPOSURE_OFFSET) +#define IMX708_VTS_MAX 0xffff + +#define IMX708_REG_GAIN_H 0x0204 +#define IMX708_REG_GAIN_L 0x0205 +#define IMX708_GAIN_MIN 112 +#define IMX708_GAIN_MAX 960 +#define IMX708_GAIN_STEP 1 +#define IMX708_GAIN_DEFAULT IMX708_GAIN_MIN + +#define IMX708_REG_DGAIN 0x3130 +#define IMX708_DGAIN_MODE BIT(0) +#define IMX708_REG_DGAINGR_H 0x020e +#define IMX708_REG_DGAINGR_L 0x020f +#define IMX708_REG_DGAINR_H 0x0210 +#define IMX708_REG_DGAINR_L 0x0211 +#define IMX708_REG_DGAINB_H 0x0212 +#define IMX708_REG_DGAINB_L 0x0213 +#define IMX708_REG_DGAINGB_H 0x0214 +#define IMX708_REG_DGAINGB_L 0x0215 +#define IMX708_REG_GAIN_GLOBAL_H 0x3ffc +#define IMX708_REG_GAIN_GLOBAL_L 0x3ffd + +#define IMX708_REG_TEST_PATTERN 0x0600 +#define IMX708_TEST_PATTERN_ENABLE 0x1 +#define IMX708_TEST_PATTERN_DISABLE 0x0 + +#define IMX708_REG_VTS_H 0x0340 +#define IMX708_REG_VTS_L 0x0341 + +#define IMX708_FLIP_MIRROR_REG 0x0101 +#define IMX708_MIRROR_BIT_MASK BIT(0) +#define IMX708_FLIP_BIT_MASK BIT(1) + +#define IMX708_FETCH_EXP_H(VAL) (((VAL) >> 8) & 0xFF) +#define IMX708_FETCH_EXP_L(VAL) ((VAL) & 0xFF) + +#define IMX708_FETCH_AGAIN_H(VAL) (((VAL) >> 8) & 0x03) +#define IMX708_FETCH_AGAIN_L(VAL) ((VAL) & 0xFF) + +#define IMX708_FETCH_DGAIN_H(VAL) (((VAL) >> 8) & 0x0F) +#define IMX708_FETCH_DGAIN_L(VAL) ((VAL) & 0xFF) + +#define IMX708_FETCH_RHS1_H(VAL) (((VAL) >> 16) & 0x0F) +#define IMX708_FETCH_RHS1_M(VAL) (((VAL) >> 8) & 0xFF) +#define IMX708_FETCH_RHS1_L(VAL) ((VAL) & 0xFF) + +#define REG_DELAY 0xFFFE +#define REG_NULL 0xFFFF + +#define IMX708_REG_VALUE_08BIT 1 +#define IMX708_REG_VALUE_16BIT 2 +#define IMX708_REG_VALUE_24BIT 3 + +/* HDR exposure ratio (long:med == med:short) */ +#define IMX708_HDR_EXPOSURE_RATIO 4 +#define IMX708_REG_MID_EXPOSURE 0x3116 +#define IMX708_REG_SHT_EXPOSURE 0x0224 +#define IMX708_REG_MID_ANALOG_GAIN 0x3118 +#define IMX708_REG_SHT_ANALOG_GAIN 0x0216 + +#define OF_CAMERA_HDR_MODE "rockchip,camera-hdr-mode" + +#define IMX708_NAME "imx708" + +static const char * const imx708_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define IMX708_NUM_SUPPLIES ARRAY_SIZE(imx708_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +struct other_data { + u32 width; + u32 height; + u32 bus_fmt; + u32 data_type; + u32 data_bit; +}; + +struct imx708_mode { + u32 bus_fmt; + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *global_reg_list; + const struct regval *reg_list; + u32 hdr_mode; + u32 mipi_freq_idx; + const struct other_data *spd; + u32 vc[PAD_MAX]; +}; + +struct imx708 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[IMX708_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 *h_flip; + struct v4l2_ctrl *v_flip; + struct v4l2_ctrl *test_pattern; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct mutex mutex; + bool streaming; + bool power_on; + const struct imx708_mode *cur_mode; + u32 cfg_num; + u32 cur_pixel_rate; + u32 cur_link_freq; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + u32 cur_vts; + bool has_init_exp; + struct preisp_hdrae_exp_s init_hdrae_exp; + u8 flip; + struct otp_info *otp; + u32 spd_id; +}; + +#define to_imx708(sd) container_of(sd, struct imx708, subdev) + +/* + *IMX708LQR All-pixel scan CSI-2_4lane 24Mhz + *AD:10bit Output:10bit 1696Mbps Master Mode 30fps + * + */ +static const struct regval imx708_linear_10bit_global_regs[] = { + {0x0100, 0x00}, + {0x0136, 0x18}, + {0x0137, 0x00}, + {0x33F0, 0x02}, + {0x33F1, 0x05}, + {0x3062, 0x00}, + {0x3063, 0x12}, + {0x3068, 0x00}, + {0x3069, 0x12}, + {0x306A, 0x00}, + {0x306B, 0x30}, + {0x3076, 0x00}, + {0x3077, 0x30}, + {0x3078, 0x00}, + {0x3079, 0x30}, + {0x5E54, 0x0C}, + {0x6E44, 0x00}, + {0xB0B6, 0x01}, + {0xE829, 0x00}, + {0xF001, 0x08}, + {0xF003, 0x08}, + {0xF00D, 0x10}, + {0xF00F, 0x10}, + {0xF031, 0x08}, + {0xF033, 0x08}, + {0xF03D, 0x10}, + {0xF03F, 0x10}, + {0x0112, 0x0A}, + {0x0113, 0x0A}, + {0x0114, 0x01}, + {0x0B8E, 0x01}, + {0x0B8F, 0x00}, + {0x0B94, 0x01}, + {0x0B95, 0x00}, + {0x3400, 0x01}, + {0x3478, 0x01}, + {0x3479, 0x1c}, + {0x3091, 0x01}, + {0x3092, 0x00}, + {0x3419, 0x00}, + {0xBCF1, 0x02}, + {0x3094, 0x01}, + {0x3095, 0x01}, + {0x3362, 0x00}, + {0x3363, 0x00}, + {0x3364, 0x00}, + {0x3365, 0x00}, + {0x0138, 0x01}, + + {REG_NULL, 0x00}, +}; + +static const struct regval imx708_linear_10bit_4608x2592_regs[] = { + {0x0342, 0x3D}, + {0x0343, 0x20}, + {0x0340, 0x0A}, + {0x0341, 0x59}, + {0x0344, 0x00}, + {0x0345, 0x00}, + {0x0346, 0x00}, + {0x0347, 0x00}, + {0x0348, 0x11}, + {0x0349, 0xFF}, + {0x034A, 0X0A}, + {0x034B, 0x1F}, + {0x0220, 0x62}, + {0x0222, 0x01}, + {0x0900, 0x00}, + {0x0901, 0x11}, + {0x0902, 0x0A}, + {0x3200, 0x01}, + {0x3201, 0x01}, + {0x32D5, 0x01}, + {0x32D6, 0x00}, + {0x32DB, 0x01}, + {0x32DF, 0x00}, + {0x350C, 0x00}, + {0x350D, 0x00}, + {0x0408, 0x00}, + {0x0409, 0x00}, + {0x040A, 0x00}, + {0x040B, 0x00}, + {0x040C, 0x12}, + {0x040D, 0x00}, + {0x040E, 0x0A}, + {0x040F, 0x20}, + {0x034C, 0x12}, + {0x034D, 0x00}, + {0x034E, 0x0A}, + {0x034F, 0x20}, + {0x0301, 0x05}, + {0x0303, 0x02}, + {0x0305, 0x02}, + {0x0306, 0x00}, + {0x0307, 0x7C}, + {0x030B, 0x02}, + {0x030D, 0x04}, + {0x030E, 0x01}, //Set default MIPI frequency to 450MHz + {0x030F, 0x2c}, + {0x0310, 0x01}, + {0x3CA0, 0x00}, + {0x3CA1, 0x64}, + {0x3CA4, 0x00}, + {0x3CA5, 0x00}, + {0x3CA6, 0x00}, + {0x3CA7, 0x00}, + {0x3CAA, 0x00}, + {0x3CAB, 0x00}, + {0x3CB8, 0x00}, + {0x3CB9, 0x08}, + {0x3CBA, 0x00}, + {0x3CBB, 0x00}, + {0x3CBC, 0x00}, + {0x3CBD, 0x3C}, + {0x3CBE, 0x00}, + {0x3CBF, 0x00}, + {0x0202, 0x0A}, + {0x0203, 0x29}, + {0x0224, 0x01}, + {0x0225, 0xF4}, + {0x3116, 0x01}, + {0x3117, 0xF4}, + {0x0204, 0x00}, + {0x0205, 0x00}, + {0x0216, 0x00}, + {0x0217, 0x00}, + {0x0218, 0x01}, + {0x0219, 0x00}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x3118, 0x00}, + {0x3119, 0x00}, + {0x311A, 0x01}, + {0x311B, 0x00}, + {0x341a, 0x00}, + {0x341b, 0x00}, + {0x341c, 0x00}, + {0x341d, 0x00}, + {0x341e, 0x01}, + {0x341f, 0x20}, + {0x3420, 0x00}, + {0x3421, 0xd8}, + {0x3366, 0x00}, + {0x3367, 0x00}, + {0x3368, 0x00}, + {0x3369, 0x00}, + + {REG_NULL, 0x00}, +}; + +static const struct regval imx708_2x2binned_regs[] = { + {0x0342, 0x1E}, + {0x0343, 0x90}, + {0x0340, 0x05}, + {0x0341, 0x38}, + {0x0344, 0x00}, + {0x0345, 0x00}, + {0x0346, 0x00}, + {0x0347, 0x00}, + {0x0348, 0x11}, + {0x0349, 0xFF}, + {0x034A, 0X0A}, + {0x034B, 0x1F}, + {0x0220, 0x62}, + {0x0222, 0x01}, + {0x0900, 0x01}, + {0x0901, 0x22}, + {0x0902, 0x08}, + {0x3200, 0x41}, + {0x3201, 0x41}, + {0x32D5, 0x00}, + {0x32D6, 0x00}, + {0x32DB, 0x01}, + {0x32DF, 0x00}, + {0x350C, 0x00}, + {0x350D, 0x00}, + {0x0408, 0x00}, + {0x0409, 0x00}, + {0x040A, 0x00}, + {0x040B, 0x00}, + {0x040C, 0x09}, + {0x040D, 0x00}, + {0x040E, 0x05}, + {0x040F, 0x10}, + {0x034C, 0x09}, + {0x034D, 0x00}, + {0x034E, 0x05}, + {0x034F, 0x10}, + {0x0301, 0x05}, + {0x0303, 0x02}, + {0x0305, 0x02}, + {0x0306, 0x00}, + {0x0307, 0x7A}, + {0x030B, 0x02}, + {0x030D, 0x04}, + {0x030E, 0x01}, //Set default MIPI frequency to 450MHz + {0x030F, 0x2c}, + {0x0310, 0x01}, + {0x3CA0, 0x00}, + {0x3CA1, 0x3C}, + {0x3CA4, 0x00}, + {0x3CA5, 0x3C}, + {0x3CA6, 0x00}, + {0x3CA7, 0x00}, + {0x3CAA, 0x00}, + {0x3CAB, 0x00}, + {0x3CB8, 0x00}, + {0x3CB9, 0x1C}, + {0x3CBA, 0x00}, + {0x3CBB, 0x08}, + {0x3CBC, 0x00}, + {0x3CBD, 0x1E}, + {0x3CBE, 0x00}, + {0x3CBF, 0x0A}, + {0x0202, 0x05}, + {0x0203, 0x08}, + {0x0224, 0x01}, + {0x0225, 0xF4}, + {0x3116, 0x01}, + {0x3117, 0xF4}, + {0x0204, 0x00}, + {0x0205, 0x70}, + {0x0216, 0x00}, + {0x0217, 0x70}, + {0x0218, 0x01}, + {0x0219, 0x00}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x3118, 0x00}, + {0x3119, 0x70}, + {0x311A, 0x01}, + {0x311B, 0x00}, + {0x341a, 0x00}, + {0x341b, 0x00}, + {0x341c, 0x00}, + {0x341d, 0x00}, + {0x341e, 0x00}, + {0x341f, 0x90}, + {0x3420, 0x00}, + {0x3421, 0x6c}, + {0x3366, 0x00}, + {0x3367, 0x00}, + {0x3368, 0x00}, + {0x3369, 0x00}, + + {REG_NULL, 0x00}, +}; + +static const struct regval imx708_2x2binned_720p_regs[] = { + {0x0342, 0x14}, + {0x0343, 0x60}, + {0x0340, 0x04}, + {0x0341, 0xB6}, + {0x0344, 0x03}, + {0x0345, 0x00}, + {0x0346, 0x01}, + {0x0347, 0xB0}, + {0x0348, 0x0E}, + {0x0349, 0xFF}, + {0x034A, 0x08}, + {0x034B, 0x6F}, + {0x0220, 0x62}, + {0x0222, 0x01}, + {0x0900, 0x01}, + {0x0901, 0x22}, + {0x0902, 0x08}, + {0x3200, 0x41}, + {0x3201, 0x41}, + {0x32D5, 0x00}, + {0x32D6, 0x00}, + {0x32DB, 0x01}, + {0x32DF, 0x01}, + {0x350C, 0x00}, + {0x350D, 0x00}, + {0x0408, 0x00}, + {0x0409, 0x00}, + {0x040A, 0x00}, + {0x040B, 0x00}, + {0x040C, 0x06}, + {0x040D, 0x00}, + {0x040E, 0x03}, + {0x040F, 0x60}, + {0x034C, 0x06}, + {0x034D, 0x00}, + {0x034E, 0x03}, + {0x034F, 0x60}, + {0x0301, 0x05}, + {0x0303, 0x02}, + {0x0305, 0x02}, + {0x0306, 0x00}, + {0x0307, 0x76}, + {0x030B, 0x02}, + {0x030D, 0x04}, + {0x030E, 0x01}, //Set default MIPI frequency to 450MHz + {0x030F, 0x2c}, + {0x0310, 0x01}, + {0x3CA0, 0x00}, + {0x3CA1, 0x3C}, + {0x3CA4, 0x01}, + {0x3CA5, 0x5E}, + {0x3CA6, 0x00}, + {0x3CA7, 0x00}, + {0x3CAA, 0x00}, + {0x3CAB, 0x00}, + {0x3CB8, 0x00}, + {0x3CB9, 0x0C}, + {0x3CBA, 0x00}, + {0x3CBB, 0x04}, + {0x3CBC, 0x00}, + {0x3CBD, 0x1E}, + {0x3CBE, 0x00}, + {0x3CBF, 0x05}, + {0x0202, 0x04}, + {0x0203, 0x86}, + {0x0224, 0x01}, + {0x0225, 0xF4}, + {0x3116, 0x01}, + {0x3117, 0xF4}, + {0x0204, 0x00}, + {0x0205, 0x70}, + {0x0216, 0x00}, + {0x0217, 0x70}, + {0x0218, 0x01}, + {0x0219, 0x00}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x3118, 0x00}, + {0x3119, 0x70}, + {0x311A, 0x01}, + {0x311B, 0x00}, + {0x341a, 0x00}, + {0x341b, 0x00}, + {0x341c, 0x00}, + {0x341d, 0x00}, + {0x341e, 0x00}, + {0x341f, 0x60}, + {0x3420, 0x00}, + {0x3421, 0x48}, + {0x3366, 0x00}, + {0x3367, 0x00}, + {0x3368, 0x00}, + {0x3369, 0x00}, + + {REG_NULL, 0x00}, +}; + +static const struct regval imx708_hdr_regs[] = { + {0x0342, 0x14}, + {0x0343, 0x60}, + {0x0340, 0x0A}, + {0x0341, 0x5B}, + {0x0344, 0x00}, + {0x0345, 0x00}, + {0x0346, 0x00}, + {0x0347, 0x00}, + {0x0348, 0x11}, + {0x0349, 0xFF}, + {0x034A, 0X0A}, + {0x034B, 0x1F}, + {0x0220, 0x01}, + {0x0222, IMX708_HDR_EXPOSURE_RATIO}, + {0x0900, 0x00}, + {0x0901, 0x11}, + {0x0902, 0x0A}, + {0x3200, 0x01}, + {0x3201, 0x01}, + {0x32D5, 0x00}, + {0x32D6, 0x00}, + {0x32DB, 0x01}, + {0x32DF, 0x00}, + {0x350C, 0x00}, + {0x350D, 0x00}, + {0x0408, 0x00}, + {0x0409, 0x00}, + {0x040A, 0x00}, + {0x040B, 0x00}, + {0x040C, 0x09}, + {0x040D, 0x00}, + {0x040E, 0x05}, + {0x040F, 0x10}, + {0x034C, 0x09}, + {0x034D, 0x00}, + {0x034E, 0x05}, + {0x034F, 0x10}, + {0x0301, 0x05}, + {0x0303, 0x02}, + {0x0305, 0x02}, + {0x0306, 0x00}, + {0x0307, 0xA2}, + {0x030B, 0x02}, + {0x030D, 0x04}, + {0x030E, 0x01}, //Set default MIPI frequency to 450MHz + {0x030F, 0x2c}, + {0x0310, 0x01}, + {0x3CA0, 0x00}, + {0x3CA1, 0x00}, + {0x3CA4, 0x00}, + {0x3CA5, 0x00}, + {0x3CA6, 0x00}, + {0x3CA7, 0x28}, + {0x3CAA, 0x00}, + {0x3CAB, 0x00}, + {0x3CB8, 0x00}, + {0x3CB9, 0x30}, + {0x3CBA, 0x00}, + {0x3CBB, 0x00}, + {0x3CBC, 0x00}, + {0x3CBD, 0x32}, + {0x3CBE, 0x00}, + {0x3CBF, 0x00}, + {0x0202, 0x0A}, + {0x0203, 0x2B}, + {0x0224, 0x0A}, + {0x0225, 0x2B}, + {0x3116, 0x0A}, + {0x3117, 0x2B}, + {0x0204, 0x00}, + {0x0205, 0x00}, + {0x0216, 0x00}, + {0x0217, 0x00}, + {0x0218, 0x01}, + {0x0219, 0x00}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x3118, 0x00}, + {0x3119, 0x00}, + {0x311A, 0x01}, + {0x311B, 0x00}, + {0x341a, 0x00}, + {0x341b, 0x00}, + {0x341c, 0x00}, + {0x341d, 0x00}, + {0x341e, 0x00}, + {0x341f, 0x90}, + {0x3420, 0x00}, + {0x3421, 0x6c}, + {0x3360, 0x01}, + {0x3361, 0x01}, + {0x3366, 0x09}, + {0x3367, 0x00}, + {0x3368, 0x05}, + {0x3369, 0x10}, + + {REG_NULL, 0x00}, +}; + +static const struct imx708_mode supported_modes[] = { + { + .width = 4608, + .height = 2592, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0B00, + .hts_def = 0x3D20, + .vts_def = 0x0A59, + .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, + .global_reg_list = imx708_linear_10bit_global_regs, + .reg_list = imx708_linear_10bit_4608x2592_regs, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 0, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + { + /*2x2Binning*/ + .width = 2304, + .height = 1296, + .max_fps = { + .numerator = 10000, + .denominator = 64100, + }, + .exp_def = 0x0B00, + .hts_def = 0x1E90, + .vts_def = 0x0538, + .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, + .global_reg_list = imx708_linear_10bit_global_regs, + .reg_list = imx708_2x2binned_regs, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 0, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + { + /*2x2 binning 720p*/ + .width = 1536, + .height = 864, + .max_fps = { + .numerator = 10000, + .denominator = 64100, + }, + .exp_def = 0x0B00, + .hts_def = 0x1460, + .vts_def = 0x04B6, + .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, + .global_reg_list = imx708_linear_10bit_global_regs, + .reg_list = imx708_2x2binned_720p_regs, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 0, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + { + .width = 2304, + .height = 1296, + .max_fps = { + .numerator = 10000, + .denominator = 97000, + }, + .exp_def = 0x0B00, + .hts_def = 0x1460, + .vts_def = 0x0A5B, + .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, + .global_reg_list = imx708_linear_10bit_global_regs, + .reg_list = imx708_hdr_regs, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 1, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, +}; + +static const s64 link_freq_items[] = { + IMX708_LINK_FREQ_450MHZ, + IMX708_LINK_FREQ_447MHZ, + IMX708_LINK_FREQ_453MHZ, +}; + +static const char * const imx708_test_pattern_menu[] = { + "Disabled", + "Solid color", + "100% color bars", + "Fade to grey color bars", + "PN9" +}; + +/* Write registers up to 4 at a time */ +static int imx708_write_reg(struct i2c_client *client, u16 reg, + int len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + 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 imx708_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++) + if (unlikely(regs[i].addr == REG_DELAY)) + usleep_range(regs[i].val, regs[i].val * 2); + else + ret = imx708_write_reg(client, regs[i].addr, + IMX708_REG_VALUE_08BIT, + regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int imx708_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, i; + + 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]; + + for (i = 0; i < 3; i++) { + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret == ARRAY_SIZE(msgs)) + break; + } + if (ret != ARRAY_SIZE(msgs) && i == 3) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int imx708_get_reso_dist(const struct imx708_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct imx708_mode * +imx708_find_best_fit(struct imx708 *imx708, 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 < imx708->cfg_num; i++) { + dist = imx708_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 imx708_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx708 *imx708 = to_imx708(sd); + const struct imx708_mode *mode; + s64 h_blank, vblank_def; + u64 pixel_rate = 0; + + mutex_lock(&imx708->mutex); + + mode = imx708_find_best_fit(imx708, fmt); + fmt->format.code = mode->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, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&imx708->mutex); + return -ENOTTY; +#endif + } else { + imx708->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(imx708->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(imx708->vblank, vblank_def, + IMX708_VTS_MAX - mode->height, + 1, vblank_def); + + __v4l2_ctrl_s_ctrl(imx708->vblank, vblank_def); + __v4l2_ctrl_s_ctrl(imx708->link_freq, mode->mipi_freq_idx); + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / 10 * 2 * IMX708_LANES; + __v4l2_ctrl_s_ctrl_int64(imx708->pixel_rate, + pixel_rate); + } + + dev_info(&imx708->client->dev, "%s: mode->mipi_freq_idx(%d)", + __func__, mode->mipi_freq_idx); + + mutex_unlock(&imx708->mutex); + + return 0; +} + +static int imx708_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx708 *imx708 = to_imx708(sd); + const struct imx708_mode *mode = imx708->cur_mode; + + mutex_lock(&imx708->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&imx708->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + if (imx708->flip & IMX708_MIRROR_BIT_MASK) { + fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; + if (imx708->flip & IMX708_FLIP_BIT_MASK) + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + } else if (imx708->flip & IMX708_FLIP_BIT_MASK) { + fmt->format.code = MEDIA_BUS_FMT_SGBRG10_1X10; + } else { + fmt->format.code = mode->bus_fmt; + } + fmt->format.field = V4L2_FIELD_NONE; + /* format info: width/height/data type/virctual channel */ + if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR) + fmt->reserved[0] = mode->vc[fmt->pad]; + else + fmt->reserved[0] = mode->vc[PAD0]; + } + mutex_unlock(&imx708->mutex); + + return 0; +} + +static int imx708_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx708 *imx708 = to_imx708(sd); + + if (code->index != 0) + return -EINVAL; + code->code = imx708->cur_mode->bus_fmt; + + return 0; +} + +static int imx708_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct imx708 *imx708 = to_imx708(sd); + + if (fse->index >= imx708->cfg_num) + return -EINVAL; + + if (fse->code != supported_modes[0].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 imx708_enable_test_pattern(struct imx708 *imx708, u32 pattern) +{ + u32 val; + + if (pattern) + val = (pattern - 1) | IMX708_TEST_PATTERN_ENABLE; + else + val = IMX708_TEST_PATTERN_DISABLE; + + return imx708_write_reg(imx708->client, + IMX708_REG_TEST_PATTERN, + IMX708_REG_VALUE_08BIT, + val); +} + +static int imx708_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct imx708 *imx708 = to_imx708(sd); + const struct imx708_mode *mode = imx708->cur_mode; + + fi->interval = mode->max_fps; + + return 0; +} + +static int imx708_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct imx708 *imx708 = to_imx708(sd); + const struct imx708_mode *mode = imx708->cur_mode; + u32 val = 0; + + if (mode->hdr_mode == NO_HDR) + val = 1 << (IMX708_LANES - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + if (mode->hdr_mode == HDR_X2) + val = 1 << (IMX708_LANES - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | + V4L2_MBUS_CSI2_CHANNEL_1; + + config->type = V4L2_MBUS_CSI2_DPHY; + config->flags = val; + + return 0; +} + +static void imx708_get_otp(struct otp_info *otp, + struct rkmodule_inf *inf) +{ + u32 i, j; + u32 w, h; + + /* awb */ + if (otp->awb_data.flag) { + inf->awb.flag = 1; + inf->awb.r_value = otp->awb_data.r_ratio; + inf->awb.b_value = otp->awb_data.b_ratio; + inf->awb.gr_value = otp->awb_data.g_ratio; + inf->awb.gb_value = 0x0; + + inf->awb.golden_r_value = otp->awb_data.r_golden; + inf->awb.golden_b_value = otp->awb_data.b_golden; + inf->awb.golden_gr_value = otp->awb_data.g_golden; + inf->awb.golden_gb_value = 0x0; + } + + /* lsc */ + if (otp->lsc_data.flag) { + inf->lsc.flag = 1; + inf->lsc.width = otp->basic_data.size.width; + inf->lsc.height = otp->basic_data.size.height; + inf->lsc.table_size = otp->lsc_data.table_size; + + for (i = 0; i < 289; i++) { + inf->lsc.lsc_r[i] = (otp->lsc_data.data[i * 2] << 8) | + otp->lsc_data.data[i * 2 + 1]; + inf->lsc.lsc_gr[i] = (otp->lsc_data.data[i * 2 + 578] << 8) | + otp->lsc_data.data[i * 2 + 579]; + inf->lsc.lsc_gb[i] = (otp->lsc_data.data[i * 2 + 1156] << 8) | + otp->lsc_data.data[i * 2 + 1157]; + inf->lsc.lsc_b[i] = (otp->lsc_data.data[i * 2 + 1734] << 8) | + otp->lsc_data.data[i * 2 + 1735]; + } + } + + /* pdaf */ + if (otp->pdaf_data.flag) { + inf->pdaf.flag = 1; + inf->pdaf.gainmap_width = otp->pdaf_data.gainmap_width; + inf->pdaf.gainmap_height = otp->pdaf_data.gainmap_height; + inf->pdaf.dcc_mode = otp->pdaf_data.dcc_mode; + inf->pdaf.dcc_dir = otp->pdaf_data.dcc_dir; + inf->pdaf.dccmap_width = otp->pdaf_data.dccmap_width; + inf->pdaf.dccmap_height = otp->pdaf_data.dccmap_height; + w = otp->pdaf_data.gainmap_width; + h = otp->pdaf_data.gainmap_height; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + inf->pdaf.gainmap[i * w + j] = + (otp->pdaf_data.gainmap[(i * w + j) * 2] << 8) | + otp->pdaf_data.gainmap[(i * w + j) * 2 + 1]; + } + } + w = otp->pdaf_data.dccmap_width; + h = otp->pdaf_data.dccmap_height; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + inf->pdaf.dccmap[i * w + j] = + (otp->pdaf_data.dccmap[(i * w + j) * 2] << 8) | + otp->pdaf_data.dccmap[(i * w + j) * 2 + 1]; + } + } + } + + /* af */ + if (otp->af_data.flag) { + inf->af.flag = 1; + inf->af.dir_cnt = 1; + inf->af.af_otp[0].vcm_start = otp->af_data.af_inf; + inf->af.af_otp[0].vcm_end = otp->af_data.af_macro; + inf->af.af_otp[0].vcm_dir = 0; + } + +} + +static void imx708_get_module_inf(struct imx708 *imx708, + struct rkmodule_inf *inf) +{ + struct otp_info *otp = imx708->otp; + + memset(inf, 0, sizeof(*inf)); + strscpy(inf->base.sensor, IMX708_NAME, sizeof(inf->base.sensor)); + strscpy(inf->base.module, imx708->module_name, + sizeof(inf->base.module)); + strscpy(inf->base.lens, imx708->len_name, sizeof(inf->base.lens)); + if (otp) + imx708_get_otp(otp, inf); + +} + +static int imx708_get_channel_info(struct imx708 *imx708, struct rkmodule_channel_info *ch_info) +{ + const struct imx708_mode *mode = imx708->cur_mode; + + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + + if (ch_info->index == imx708->spd_id && mode->spd) { + ch_info->vc = V4L2_MBUS_CSI2_CHANNEL_0; + ch_info->width = mode->spd->width; + ch_info->height = mode->spd->height; + ch_info->bus_fmt = mode->spd->bus_fmt; + ch_info->data_type = mode->spd->data_type; + ch_info->data_bit = mode->spd->data_bit; + } else { + ch_info->vc = imx708->cur_mode->vc[ch_info->index]; + ch_info->width = imx708->cur_mode->width; + ch_info->height = imx708->cur_mode->height; + ch_info->bus_fmt = imx708->cur_mode->bus_fmt; + } + return 0; +} + +static long imx708_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct imx708 *imx708 = to_imx708(sd); + struct rkmodule_hdr_cfg *hdr; + struct rkmodule_channel_info *ch_info; + long ret = 0; + u32 i, h, w; + u32 stream = 0; + + switch (cmd) { + case PREISP_CMD_SET_HDRAE_EXP: + break; + case RKMODULE_GET_MODULE_INFO: + imx708_get_module_inf(imx708, (struct rkmodule_inf *)arg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = (struct rkmodule_hdr_cfg *)arg; + hdr->esp.mode = HDR_NORMAL_VC; + hdr->hdr_mode = imx708->cur_mode->hdr_mode; + break; + case RKMODULE_SET_HDR_CFG: + hdr = (struct rkmodule_hdr_cfg *)arg; + w = imx708->cur_mode->width; + h = imx708->cur_mode->height; + for (i = 0; i < imx708->cfg_num; i++) { + if (w == supported_modes[i].width && + h == supported_modes[i].height && + supported_modes[i].hdr_mode == hdr->hdr_mode) { + imx708->cur_mode = &supported_modes[i]; + break; + } + } + if (i == imx708->cfg_num) { + dev_err(&imx708->client->dev, + "not find hdr mode:%d %dx%d config\n", + hdr->hdr_mode, w, h); + ret = -EINVAL; + } else { + w = imx708->cur_mode->hts_def - + imx708->cur_mode->width; + h = imx708->cur_mode->vts_def - + imx708->cur_mode->height; + __v4l2_ctrl_modify_range(imx708->hblank, w, w, 1, w); + __v4l2_ctrl_modify_range(imx708->vblank, h, + IMX708_VTS_MAX - + imx708->cur_mode->height, + 1, h); + + if (imx708->cur_mode->bus_fmt == + MEDIA_BUS_FMT_SRGGB10_1X10) { + imx708->cur_link_freq = 0; + imx708->cur_pixel_rate = + PIXEL_RATE_WITH_848M_10BIT; + } else if (imx708->cur_mode->bus_fmt == + MEDIA_BUS_FMT_SRGGB12_1X12) { + imx708->cur_link_freq = 0; + imx708->cur_pixel_rate = + PIXEL_RATE_WITH_848M_12BIT; + } + + __v4l2_ctrl_s_ctrl_int64(imx708->pixel_rate, + imx708->cur_pixel_rate); + __v4l2_ctrl_s_ctrl(imx708->link_freq, + imx708->cur_link_freq); + } + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = imx708_write_reg(imx708->client, IMX708_REG_CTRL_MODE, + IMX708_REG_VALUE_08BIT, IMX708_MODE_STREAMING); + else + ret = imx708_write_reg(imx708->client, IMX708_REG_CTRL_MODE, + IMX708_REG_VALUE_08BIT, IMX708_MODE_SW_STANDBY); + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = imx708_get_channel_info(imx708, ch_info); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long imx708_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; + struct rkmodule_hdr_cfg *hdr; + struct preisp_hdrae_exp_s *hdrae; + struct rkmodule_channel_info *ch_info; + long ret; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = imx708_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 = imx708_ioctl(sd, cmd, cfg); + else + ret = -EFAULT; + kfree(cfg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = imx708_ioctl(sd, cmd, hdr); + if (!ret) { + ret = copy_to_user(up, hdr, sizeof(*hdr)); + if (ret) + ret = -EFAULT; + } + kfree(hdr); + break; + case RKMODULE_SET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + ret = copy_from_user(hdr, up, sizeof(*hdr)); + if (!ret) + ret = imx708_ioctl(sd, cmd, hdr); + else + ret = -EFAULT; + kfree(hdr); + break; + case PREISP_CMD_SET_HDRAE_EXP: + hdrae = kzalloc(sizeof(*hdrae), GFP_KERNEL); + if (!hdrae) { + ret = -ENOMEM; + return ret; + } + ret = copy_from_user(hdrae, up, sizeof(*hdrae)); + if (!ret) + ret = imx708_ioctl(sd, cmd, hdrae); + else + ret = -EFAULT; + kfree(hdrae); + break; + case RKMODULE_SET_QUICK_STREAM: + ret = copy_from_user(&stream, up, sizeof(u32)); + if (!ret) + ret = imx708_ioctl(sd, cmd, &stream); + else + ret = -EFAULT; + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); + if (!ch_info) { + ret = -ENOMEM; + return ret; + } + ret = imx708_ioctl(sd, cmd, ch_info); + if (!ret) { + ret = copy_to_user(up, ch_info, sizeof(*ch_info)); + if (ret) + ret = -EFAULT; + } + kfree(ch_info); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int imx708_set_flip(struct imx708 *imx708) +{ + int ret = 0; + u32 val = 0; + + ret = imx708_read_reg(imx708->client, IMX708_FLIP_MIRROR_REG, + IMX708_REG_VALUE_08BIT, &val); + if (imx708->flip & IMX708_MIRROR_BIT_MASK) + val |= IMX708_MIRROR_BIT_MASK; + else + val &= ~IMX708_MIRROR_BIT_MASK; + if (imx708->flip & IMX708_FLIP_BIT_MASK) + val |= IMX708_FLIP_BIT_MASK; + else + val &= ~IMX708_FLIP_BIT_MASK; + ret |= imx708_write_reg(imx708->client, IMX708_FLIP_MIRROR_REG, + IMX708_REG_VALUE_08BIT, val); + + return ret; +} + +static int __imx708_start_stream(struct imx708 *imx708) +{ + int ret; + + ret = imx708_write_array(imx708->client, imx708->cur_mode->global_reg_list); + if (ret) + return ret; + + ret = imx708_write_array(imx708->client, imx708->cur_mode->reg_list); + if (ret) + return ret; + imx708->cur_vts = imx708->cur_mode->vts_def; + /* In case these controls are set before streaming */ + ret = __v4l2_ctrl_handler_setup(&imx708->ctrl_handler); + if (ret) + return ret; + if (imx708->has_init_exp && imx708->cur_mode->hdr_mode != NO_HDR) { + ret = imx708_ioctl(&imx708->subdev, PREISP_CMD_SET_HDRAE_EXP, + &imx708->init_hdrae_exp); + if (ret) { + dev_err(&imx708->client->dev, + "init exp fail in hdr mode\n"); + return ret; + } + } + + imx708_set_flip(imx708); + + return imx708_write_reg(imx708->client, IMX708_REG_CTRL_MODE, + IMX708_REG_VALUE_08BIT, IMX708_MODE_STREAMING); +} + +static int __imx708_stop_stream(struct imx708 *imx708) +{ + return imx708_write_reg(imx708->client, IMX708_REG_CTRL_MODE, + IMX708_REG_VALUE_08BIT, IMX708_MODE_SW_STANDBY); +} + +static int imx708_s_stream(struct v4l2_subdev *sd, int on) +{ + struct imx708 *imx708 = to_imx708(sd); + struct i2c_client *client = imx708->client; + int ret = 0; + + dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on, + imx708->cur_mode->width, + imx708->cur_mode->height, + DIV_ROUND_CLOSEST(imx708->cur_mode->max_fps.denominator, + imx708->cur_mode->max_fps.numerator)); + + mutex_lock(&imx708->mutex); + on = !!on; + if (on == imx708->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 = __imx708_start_stream(imx708); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __imx708_stop_stream(imx708); + pm_runtime_put(&client->dev); + } + + imx708->streaming = on; + +unlock_and_return: + mutex_unlock(&imx708->mutex); + + return ret; +} + +static int imx708_s_power(struct v4l2_subdev *sd, int on) +{ + struct imx708 *imx708 = to_imx708(sd); + struct i2c_client *client = imx708->client; + int ret = 0; + + mutex_lock(&imx708->mutex); + + /* If the power state is not modified - no work to do. */ + if (imx708->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; + } + + imx708->power_on = true; + } else { + pm_runtime_put(&client->dev); + imx708->power_on = false; + } + +unlock_and_return: + mutex_unlock(&imx708->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 imx708_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, IMX708_XVCLK_FREQ / 1000 / 1000); +} + +static int __imx708_power_on(struct imx708 *imx708) +{ + int ret; + u32 delay_us; + struct device *dev = &imx708->client->dev; + + ret = clk_set_rate(imx708->xvclk, IMX708_XVCLK_FREQ); + if (ret < 0) { + dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); + return ret; + } + if (clk_get_rate(imx708->xvclk) != IMX708_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 37.125MHz\n"); + ret = clk_prepare_enable(imx708->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + if (!IS_ERR(imx708->reset_gpio)) + gpiod_set_value_cansleep(imx708->reset_gpio, 0); + + ret = regulator_bulk_enable(IMX708_NUM_SUPPLIES, imx708->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(imx708->reset_gpio)) + gpiod_set_value_cansleep(imx708->reset_gpio, 1); + + /* need wait 8ms to set register */ + usleep_range(8000, 10000); + + if (!IS_ERR(imx708->pwdn_gpio)) + gpiod_set_value_cansleep(imx708->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = imx708_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(imx708->xvclk); + + return ret; +} + +static void __imx708_power_off(struct imx708 *imx708) +{ + + if (!IS_ERR(imx708->pwdn_gpio)) + gpiod_set_value_cansleep(imx708->pwdn_gpio, 0); + clk_disable_unprepare(imx708->xvclk); + if (!IS_ERR(imx708->reset_gpio)) + gpiod_set_value_cansleep(imx708->reset_gpio, 0); + regulator_bulk_disable(IMX708_NUM_SUPPLIES, imx708->supplies); +} + +static int imx708_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx708 *imx708 = to_imx708(sd); + + return __imx708_power_on(imx708); +} + +static int imx708_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx708 *imx708 = to_imx708(sd); + + __imx708_power_off(imx708); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int imx708_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct imx708 *imx708 = to_imx708(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct imx708_mode *def_mode = &supported_modes[0]; + + mutex_lock(&imx708->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = def_mode->bus_fmt; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&imx708->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int imx708_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct imx708 *imx708 = to_imx708(sd); + + if (fie->index >= imx708->cfg_num) + return -EINVAL; + + fie->code = supported_modes[fie->index].bus_fmt; + fie->width = supported_modes[fie->index].width; + fie->height = supported_modes[fie->index].height; + fie->interval = supported_modes[fie->index].max_fps; + fie->reserved[0] = supported_modes[fie->index].hdr_mode; + return 0; +} + +static const struct dev_pm_ops imx708_pm_ops = { + SET_RUNTIME_PM_OPS(imx708_runtime_suspend, + imx708_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops imx708_internal_ops = { + .open = imx708_open, +}; +#endif + +static const struct v4l2_subdev_core_ops imx708_core_ops = { + .s_power = imx708_s_power, + .ioctl = imx708_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = imx708_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops imx708_video_ops = { + .s_stream = imx708_s_stream, + .g_frame_interval = imx708_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops imx708_pad_ops = { + .enum_mbus_code = imx708_enum_mbus_code, + .enum_frame_size = imx708_enum_frame_sizes, + .enum_frame_interval = imx708_enum_frame_interval, + .get_fmt = imx708_get_fmt, + .set_fmt = imx708_set_fmt, + .get_mbus_config = imx708_g_mbus_config, +}; + +static const struct v4l2_subdev_ops imx708_subdev_ops = { + .core = &imx708_core_ops, + .video = &imx708_video_ops, + .pad = &imx708_pad_ops, +}; + +static int imx708_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx708 *imx708 = container_of(ctrl->handler, + struct imx708, ctrl_handler); + struct i2c_client *client = imx708->client; + s64 max; + int ret = 0; + u32 again = 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 = imx708->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(imx708->exposure, + imx708->exposure->minimum, max, + imx708->exposure->step, + imx708->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 = imx708_write_reg(imx708->client, + IMX708_REG_EXPOSURE_H, + IMX708_REG_VALUE_08BIT, + IMX708_FETCH_EXP_H(ctrl->val)); + ret |= imx708_write_reg(imx708->client, + IMX708_REG_EXPOSURE_L, + IMX708_REG_VALUE_08BIT, + IMX708_FETCH_EXP_L(ctrl->val)); + dev_dbg(&client->dev, "set exposure 0x%x\n", + ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + /* gain_reg = 1024 - 1024 / gain_ana + * manual multiple 16 to add accuracy: + * then formula change to: + * gain_reg = 1024 - 1024 * 16 / (gain_ana * 16) + */ + if (ctrl->val > 0x400) + ctrl->val = 0x400; + if (ctrl->val < 0x10) + ctrl->val = 0x10; + + again = 1024 - 1024 * 16 / ctrl->val; + ret = imx708_write_reg(imx708->client, IMX708_REG_GAIN_H, + IMX708_REG_VALUE_08BIT, + IMX708_FETCH_AGAIN_H(again)); + ret |= imx708_write_reg(imx708->client, IMX708_REG_GAIN_L, + IMX708_REG_VALUE_08BIT, + IMX708_FETCH_AGAIN_L(again)); + + dev_dbg(&client->dev, "set analog gain 0x%x\n", + ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = imx708_write_reg(imx708->client, + IMX708_REG_VTS_H, + IMX708_REG_VALUE_08BIT, + (ctrl->val + imx708->cur_mode->height) + >> 8); + ret |= imx708_write_reg(imx708->client, + IMX708_REG_VTS_L, + IMX708_REG_VALUE_08BIT, + (ctrl->val + imx708->cur_mode->height) + & 0xff); + imx708->cur_vts = ctrl->val + imx708->cur_mode->height; + + dev_dbg(&client->dev, "set vblank 0x%x\n", + ctrl->val); + break; + case V4L2_CID_HFLIP: + if (ctrl->val) + imx708->flip |= IMX708_MIRROR_BIT_MASK; + else + imx708->flip &= ~IMX708_MIRROR_BIT_MASK; + dev_dbg(&client->dev, "set hflip 0x%x\n", + ctrl->val); + break; + case V4L2_CID_VFLIP: + if (ctrl->val) + imx708->flip |= IMX708_FLIP_BIT_MASK; + else + imx708->flip &= ~IMX708_FLIP_BIT_MASK; + dev_dbg(&client->dev, "set vflip 0x%x\n", + ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + dev_dbg(&client->dev, "set testpattern 0x%x\n", + ctrl->val); + ret = imx708_enable_test_pattern(imx708, 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 imx708_ctrl_ops = { + .s_ctrl = imx708_set_ctrl, +}; + +static int imx708_initialize_controls(struct imx708 *imx708) +{ + const struct imx708_mode *mode; + struct v4l2_ctrl_handler *handler; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &imx708->ctrl_handler; + mode = imx708->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 9); + if (ret) + return ret; + handler->lock = &imx708->mutex; + + imx708->link_freq = v4l2_ctrl_new_int_menu(handler, NULL, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_items) - 1, 0, + link_freq_items); + + if (imx708->cur_mode->bus_fmt == MEDIA_BUS_FMT_SRGGB10_1X10) { + imx708->cur_link_freq = 0; + imx708->cur_pixel_rate = PIXEL_RATE_WITH_848M_10BIT; + } else if (imx708->cur_mode->bus_fmt == MEDIA_BUS_FMT_SRGGB12_1X12) { + imx708->cur_link_freq = 0; + imx708->cur_pixel_rate = PIXEL_RATE_WITH_848M_12BIT; + } + + imx708->pixel_rate = v4l2_ctrl_new_std(handler, NULL, + V4L2_CID_PIXEL_RATE, + 0, PIXEL_RATE_WITH_848M_10BIT, + 1, imx708->cur_pixel_rate); + v4l2_ctrl_s_ctrl(imx708->link_freq, + imx708->cur_link_freq); + + h_blank = mode->hts_def - mode->width; + imx708->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (imx708->hblank) + imx708->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + imx708->vblank = v4l2_ctrl_new_std(handler, &imx708_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + IMX708_VTS_MAX - mode->height, + 1, vblank_def); + imx708->cur_vts = mode->vts_def; + exposure_max = mode->vts_def - 4; + imx708->exposure = v4l2_ctrl_new_std(handler, &imx708_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX708_EXPOSURE_MIN, + exposure_max, + IMX708_EXPOSURE_STEP, + mode->exp_def); + imx708->anal_gain = v4l2_ctrl_new_std(handler, &imx708_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + IMX708_GAIN_MIN, + IMX708_GAIN_MAX, + IMX708_GAIN_STEP, + IMX708_GAIN_DEFAULT); + imx708->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &imx708_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx708_test_pattern_menu) - 1, + 0, 0, imx708_test_pattern_menu); + + imx708->h_flip = v4l2_ctrl_new_std(handler, &imx708_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + imx708->v_flip = v4l2_ctrl_new_std(handler, &imx708_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + imx708->flip = 0; + + if (handler->error) { + ret = handler->error; + dev_err(&imx708->client->dev, + "Failed to init controls( %d )\n", ret); + goto err_free_handler; + } + + imx708->subdev.ctrl_handler = handler; + imx708->has_init_exp = false; + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int imx708_check_sensor_id(struct imx708 *imx708, + struct i2c_client *client) +{ + struct device *dev = &imx708->client->dev; + u16 id = 0; + u32 reg_H = 0; + u32 reg_L = 0; + int ret; + + ret = imx708_read_reg(client, IMX708_REG_CHIP_ID_H, + IMX708_REG_VALUE_08BIT, ®_H); + ret |= imx708_read_reg(client, IMX708_REG_CHIP_ID_L, + IMX708_REG_VALUE_08BIT, ®_L); + id = ((reg_H << 8) & 0xff00) | (reg_L & 0xff); + if (!(reg_H == (CHIP_ID >> 8) || reg_L == (CHIP_ID & 0xff))) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + dev_info(dev, "detected imx708 %04x sensor\n", id); + return 0; +} + +static int imx708_configure_regulators(struct imx708 *imx708) +{ + unsigned int i; + + for (i = 0; i < IMX708_NUM_SUPPLIES; i++) + imx708->supplies[i].supply = imx708_supply_names[i]; + + return devm_regulator_bulk_get(&imx708->client->dev, + IMX708_NUM_SUPPLIES, + imx708->supplies); +} + +static int imx708_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct imx708 *imx708; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + u32 i, hdr_mode = 0; + struct device_node *eeprom_ctrl_node; + struct i2c_client *eeprom_ctrl_client; + struct v4l2_subdev *eeprom_ctrl; + struct otp_info *otp_ptr; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + imx708 = devm_kzalloc(dev, sizeof(*imx708), GFP_KERNEL); + if (!imx708) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &imx708->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &imx708->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &imx708->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &imx708->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + ret = of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode); + if (ret) { + hdr_mode = NO_HDR; + dev_warn(dev, " Get hdr mode failed! no hdr default\n"); + } + + imx708->client = client; + imx708->cfg_num = ARRAY_SIZE(supported_modes); + for (i = 0; i < imx708->cfg_num; i++) { + if (hdr_mode == supported_modes[i].hdr_mode) { + imx708->cur_mode = &supported_modes[i]; + break; + } + } + + if (i == imx708->cfg_num) + imx708->cur_mode = &supported_modes[0]; + + imx708->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(imx708->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + imx708->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(imx708->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + imx708->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(imx708->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = of_property_read_u32(node, + "rockchip,spd-id", + &imx708->spd_id); + if (ret != 0) { + imx708->spd_id = PAD_MAX; + dev_err(dev, + "failed get spd_id, will not to use spd\n"); + } + + ret = imx708_configure_regulators(imx708); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&imx708->mutex); + + sd = &imx708->subdev; + v4l2_i2c_subdev_init(sd, client, &imx708_subdev_ops); + + ret = imx708_initialize_controls(imx708); + if (ret) + goto err_destroy_mutex; + + ret = __imx708_power_on(imx708); + if (ret) + goto err_free_handler; + + ret = imx708_check_sensor_id(imx708, client); + if (ret) + goto err_power_off; + eeprom_ctrl_node = of_parse_phandle(node, "eeprom-ctrl", 0); + if (eeprom_ctrl_node) { + eeprom_ctrl_client = + of_find_i2c_device_by_node(eeprom_ctrl_node); + of_node_put(eeprom_ctrl_node); + if (IS_ERR_OR_NULL(eeprom_ctrl_client)) { + dev_err(dev, "can not get node\n"); + goto continue_probe; + } + eeprom_ctrl = i2c_get_clientdata(eeprom_ctrl_client); + if (IS_ERR_OR_NULL(eeprom_ctrl)) { + dev_err(dev, "can not get eeprom i2c client\n"); + } else { + otp_ptr = devm_kzalloc(dev, sizeof(*otp_ptr), GFP_KERNEL); + if (!otp_ptr) + return -ENOMEM; + ret = v4l2_subdev_call(eeprom_ctrl, + core, ioctl, 0, otp_ptr); + if (!ret) { + imx708->otp = otp_ptr; + } else { + imx708->otp = NULL; + devm_kfree(dev, otp_ptr); + } + } + } +continue_probe: + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &imx708_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + imx708->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &imx708->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(imx708->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + imx708->module_index, facing, + IMX708_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(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); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __imx708_power_off(imx708); +err_free_handler: + v4l2_ctrl_handler_free(&imx708->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&imx708->mutex); + + return ret; +} + +static int imx708_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx708 *imx708 = to_imx708(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&imx708->ctrl_handler); + mutex_destroy(&imx708->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __imx708_power_off(imx708); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id imx708_of_match[] = { + { .compatible = "sony,imx708" }, + {}, +}; +MODULE_DEVICE_TABLE(of, imx708_of_match); +#endif + +static const struct i2c_device_id imx708_match_id[] = { + { "sony,imx708", 0 }, + { }, +}; + +static struct i2c_driver imx708_i2c_driver = { + .driver = { + .name = IMX708_NAME, + .pm = &imx708_pm_ops, + .of_match_table = of_match_ptr(imx708_of_match), + }, + .probe = &imx708_probe, + .remove = &imx708_remove, + .id_table = imx708_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&imx708_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&imx708_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("Sony imx708 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov50h40.c b/drivers/media/i2c/ov50h40.c new file mode 100644 index 0000000000000..afedc3ea5fde3 --- /dev/null +++ b/drivers/media/i2c/ov50h40.c @@ -0,0 +1,3776 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ov50h40 driver + * + * Copyright (C) 2020 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X00 first version. + * V0.0X01.0X01 support conversion gain switch. + * V0.0X01.0X02 add debug interface for conversion gain switch. + * V0.0X01.0X03 support enum sensor fmt + * V0.0X01.0X04 add quick stream on/off + * V0.0X01.0X05 support get dcg ratio from sensor + * V0.0X01.0X06 + * 1. fix 8K@12 mipi freq index. + * 2. fix set_fmt & ioctl get mode unmatched issue. + * 3. add debug info. + * V0.0X01.0X07 correct bayer pattern to match register setting + * V0.0X01.0X08 adjust some config for cts. + * 1. only enable 8K@12fps & 4K@30fps setting for use, other for debug. + */ +//#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../platform/rockchip/isp/rkisp_tb_helper.h" +#include +#include "otp_eeprom.h" + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x08) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define MIPI_FREQ_356M 356000000 +#define MIPI_FREQ_384M 384000000 +#define MIPI_FREQ_750M 750000000 +#define MIPI_FREQ_1250M 1250000000 + +#define PIXEL_RATE_WITH_1250M (MIPI_FREQ_1250M / 10 * 4 * 2) + +#define OF_CAMERA_HDR_MODE "rockchip,camera-hdr-mode" + +#define OV50H40_XVCLK_FREQ 19200000 + +#define CHIP_ID 0x564041 +#define OV50H40_REG_CHIP_ID 0x300a + +#define OV50H40_REG_CTRL_MODE 0x0100 +#define OV50H40_MODE_SW_STANDBY 0x0 +#define OV50H40_MODE_STREAMING BIT(0) + +#define OV50H40_EXPOSURE_MIN 4 +#define OV50H40_EXPOSURE_STEP 1 +#define OV50H40_VTS_MAX 0xffff + +#define OV50H40_REG_EXP_LONG_H 0x3500 + +#define OV50H40_REG_AGAIN_LONG_H 0x3508 +#define OV50H40_REG_DGAIN_LONG_H 0x350A +#define OV50H40_GAIN_MIN 0x80 +#define OV50H40_GAIN_MAX 0x7C00 +#define OV50H40_GAIN_STEP 1 +#define OV50H40_GAIN_DEFAULT 0x80 + +#define OV50H40_GROUP_UPDATE_ADDRESS 0x3208 +#define OV50H40_GROUP_UPDATE_START_DATA 0x00 +#define OV50H40_GROUP_UPDATE_END_DATA 0x10 +#define OV50H40_GROUP_UPDATE_END_LAUNCH 0xA0 + +#define OV50H40_SOFTWARE_RESET_REG 0x0103 + +#define OV50H40_FETCH_MSB_BYTE_EXP(VAL) (((VAL) >> 8) & 0xFF) /* 8 Bits */ +#define OV50H40_FETCH_LSB_BYTE_EXP(VAL) ((VAL) & 0xFF) /* 8 Bits */ + +#define OV50H40_FETCH_LSB_GAIN(VAL) (((VAL) << 4) & 0xf0) +#define OV50H40_FETCH_MSB_GAIN(VAL) (((VAL) >> 4) & 0x1f) + +#define OV50H40_REG_TEST_PATTERN 0x50C1 +#define OV50H40_TEST_PATTERN_ENABLE 0x01 +#define OV50H40_TEST_PATTERN_DISABLE 0x0 + +#define OV50H40_REG_VTS 0x380e + +#define REG_NULL 0xFFFF + +#define OV50H40_REG_VALUE_08BIT 1 +#define OV50H40_REG_VALUE_16BIT 2 +#define OV50H40_REG_VALUE_24BIT 3 + +#define OV50H40_LANES 3 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define OV50H40_NAME "ov50h40" + +static const char * const ov50h40_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define OV50H40_NUM_SUPPLIES ARRAY_SIZE(ov50h40_supply_names) + +#define OV50H40_FLIP_REG 0x3820 +#define OV50H40_MIRROR_REG 0x3821 +#define FLIP_BIT_MASK BIT(2) + +struct regval { + u16 addr; + u8 val; +}; + +struct other_data { + u32 width; + u32 height; + u32 bus_fmt; + u32 data_type; + u32 data_bit; +}; + +struct ov50h40_mode { + u32 bus_fmt; + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + u32 mipi_freq_idx; + u32 bpp; + const struct regval *reg_list; + u32 hdr_mode; + const struct other_data *spd; + u32 vc[PAD_MAX]; +}; + +struct ov50h40 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[OV50H40_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 *test_pattern; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *h_flip; + struct v4l2_ctrl *v_flip; + struct mutex mutex; + bool streaming; + bool power_on; + const struct ov50h40_mode *cur_mode; + const struct ov50h40_mode *support_modes; + u32 cfg_num; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + struct v4l2_fwnode_endpoint bus_cfg; + bool is_thunderboot; + bool is_thunderboot_ng; + bool is_first_streamoff; + struct otp_info *otp; + u32 spd_id; +}; + +#define to_ov50h40(sd) container_of(sd, struct ov50h40, subdev) + +static const struct regval ov50h40_10bit_4096x3072_dphy_30fps_regs[] = { + {REG_NULL, 0x00}, +}; + +static const struct regval ov50h40_10bit_8192x6144_dphy_12fps_regs[] = { + {REG_NULL, 0x00}, +}; + +static const struct regval ov50h40_10bit_4096x3072_cphy_regs[] = { + {0x0103, 0x01}, + {0x0102, 0x01}, + {0x6a03, 0x00}, + {0x0304, 0x02}, + {0x0305, 0xd0}, + {0x0306, 0x03}, + {0x0307, 0x00}, + {0x0308, 0x03}, + {0x0323, 0x12}, + {0x0324, 0x02}, + {0x0325, 0x58}, + {0x0327, 0x09}, + {0x0328, 0x9f}, + {0x0329, 0x01}, + {0x032a, 0x0f}, + {0x032b, 0x09}, + {0x032c, 0x00}, + {0x032e, 0x01}, + {0x0343, 0x02}, + {0x0344, 0x01}, + {0x0345, 0x20}, + {0x0346, 0xdf}, + {0x0347, 0x0f}, + {0x0348, 0x7f}, + {0x0349, 0x0f}, + {0x034a, 0x03}, + {0x034b, 0x02}, + {0x034c, 0x03}, + {0x034d, 0x01}, + {0x034e, 0x01}, + {0x0360, 0x09}, + {0x300d, 0x11}, + {0x300d, 0x11}, + {0x300e, 0x11}, + {0x3012, 0x31}, + {0x3014, 0xf0}, + {0x3015, 0x00}, + {0x3016, 0xf0}, + {0x3017, 0xf0}, + {0x301c, 0x01}, + {0x301d, 0x02}, + {0x301f, 0x98}, + {0x3020, 0x01}, + {0x3025, 0x03}, + {0x3026, 0x80}, + {0x3027, 0x00}, + {0x302c, 0x01}, + {0x302d, 0x00}, + {0x302e, 0x00}, + {0x302f, 0x00}, + {0x3030, 0x03}, + {0x3031, 0x00}, + {0x3044, 0xc2}, + {0x3047, 0x07}, + {0x3102, 0x0d}, + {0x3106, 0x80}, + {0x3400, 0x0c}, + {0x3401, 0x00}, + {0x3406, 0x08}, + {0x3407, 0x08}, + {0x3408, 0x08}, + {0x3409, 0x02}, + {0x340a, 0x03}, + {0x340e, 0x60}, + {0x3420, 0x03}, + {0x3421, 0x08}, + {0x3422, 0x08}, + {0x3423, 0x00}, + {0x3426, 0x15}, + {0x342b, 0x40}, + {0x342c, 0x15}, + {0x342d, 0x01}, + {0x342e, 0x00}, + {0x3500, 0x00}, + {0x3501, 0x00}, + {0x3502, 0x40}, + {0x3504, 0x4c}, + {0x3506, 0x78}, + {0x3507, 0x00}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {0x350a, 0x01}, + {0x350b, 0x00}, + {0x350c, 0x00}, + {0x350d, 0x01}, + {0x350e, 0x00}, + {0x350f, 0x00}, + {0x3519, 0x01}, + {0x351a, 0x71}, + {0x351b, 0x40}, + {0x3540, 0x00}, + {0x3541, 0x00}, + {0x3542, 0x30}, + {0x3544, 0x4c}, + {0x3546, 0x78}, + {0x3548, 0x01}, + {0x3549, 0x00}, + {0x354a, 0x01}, + {0x354b, 0x00}, + {0x354d, 0x01}, + {0x354e, 0x00}, + {0x354f, 0x00}, + {0x3559, 0x01}, + {0x355a, 0x71}, + {0x355b, 0x40}, + {0x3580, 0x00}, + {0x3581, 0x00}, + {0x3582, 0x20}, + {0x3584, 0x4c}, + {0x3586, 0x78}, + {0x3588, 0x01}, + {0x3589, 0x00}, + {0x358a, 0x01}, + {0x358b, 0x00}, + {0x358d, 0x01}, + {0x358e, 0x00}, + {0x358f, 0x00}, + {0x3599, 0x01}, + {0x359a, 0x71}, + {0x359b, 0x40}, + {0x3600, 0xe4}, + {0x3602, 0xe4}, + {0x3603, 0x80}, + {0x3605, 0x38}, + {0x3607, 0x10}, + {0x3608, 0x30}, + {0x3609, 0x80}, + {0x360a, 0xfa}, + {0x360b, 0xc7}, + {0x360c, 0x0f}, + {0x360d, 0xf4}, + {0x360e, 0x2b}, + {0x3610, 0x08}, + {0x3612, 0x00}, + {0x3614, 0x0c}, + {0x3616, 0x8c}, + {0x3617, 0x0d}, + {0x3618, 0xcf}, + {0x3619, 0x44}, + {0x361a, 0x81}, + {0x361b, 0x04}, + {0x361d, 0x1f}, + {0x3622, 0x00}, + {0x3627, 0xa0}, + {0x363b, 0x6a}, + {0x363c, 0x6a}, + {0x3640, 0x00}, + {0x3641, 0x02}, + {0x3643, 0x01}, + {0x3644, 0x00}, + {0x3645, 0x06}, + {0x3646, 0x40}, + {0x3647, 0x01}, + {0x3648, 0x8e}, + {0x364d, 0x10}, + {0x3650, 0xbf}, + {0x3651, 0x00}, + {0x3653, 0x03}, + {0x3657, 0x40}, + {0x3680, 0x00}, + {0x3682, 0x80}, + {0x3683, 0x00}, + {0x3684, 0x01}, + {0x3685, 0x04}, + {0x3688, 0x00}, + {0x3689, 0x88}, + {0x368a, 0x0e}, + {0x368b, 0xef}, + {0x368d, 0x00}, + {0x368e, 0x70}, + {0x3696, 0x41}, + {0x369a, 0x00}, + {0x369f, 0x20}, + {0x36a4, 0x00}, + {0x36a5, 0x00}, + {0x36d0, 0x00}, + {0x36d3, 0x80}, + {0x36d4, 0x00}, + {0x3700, 0x1c}, + {0x3701, 0x13}, + {0x3702, 0x30}, + {0x3703, 0x34}, + {0x3704, 0x03}, + {0x3706, 0x1c}, + {0x3707, 0x04}, + {0x3708, 0x25}, + {0x3709, 0x70}, + {0x370b, 0x3a}, + {0x370c, 0x04}, + {0x3712, 0x01}, + {0x3714, 0xf8}, + {0x3715, 0x00}, + {0x3716, 0x40}, + {0x3720, 0x0b}, + {0x3722, 0x05}, + {0x3724, 0x12}, + {0x372b, 0x00}, + {0x372e, 0x1c}, + {0x372f, 0x13}, + {0x3733, 0x00}, + {0x3735, 0x00}, + {0x373f, 0x00}, + {0x374b, 0x04}, + {0x374c, 0x0c}, + {0x374f, 0x58}, + {0x3754, 0x30}, + {0x3755, 0xb1}, + {0x3756, 0x00}, + {0x3757, 0x30}, + {0x3758, 0x00}, + {0x3759, 0x50}, + {0x375e, 0x00}, + {0x375f, 0x00}, + {0x3760, 0x10}, + {0x3761, 0x30}, + {0x3762, 0x10}, + {0x3763, 0x10}, + {0x3765, 0x20}, + {0x3766, 0x30}, + {0x3767, 0x20}, + {0x3768, 0x00}, + {0x3769, 0x10}, + {0x376a, 0x10}, + {0x376c, 0x00}, + {0x376e, 0x00}, + {0x3770, 0x01}, + {0x3780, 0x5c}, + {0x3782, 0x01}, + {0x378a, 0x01}, + {0x3791, 0x30}, + {0x3793, 0x1c}, + {0x3795, 0x1c}, + {0x3797, 0x8e}, + {0x3799, 0x3a}, + {0x379b, 0x3a}, + {0x379c, 0x01}, + {0x379d, 0x01}, + {0x379f, 0x01}, + {0x37a0, 0x70}, + {0x37a9, 0x01}, + {0x37b2, 0xc8}, + {0x37b7, 0x02}, + {0x37bd, 0x00}, + {0x37c1, 0x1a}, + {0x37c3, 0x1a}, + {0x37ca, 0xc4}, + {0x37cb, 0x02}, + {0x37cc, 0x51}, + {0x37cd, 0x01}, + {0x37d0, 0x00}, + {0x37d4, 0x00}, + {0x37d8, 0x00}, + {0x37d9, 0x08}, + {0x37da, 0x14}, + {0x37db, 0x10}, + {0x37dc, 0x1a}, + {0x37dd, 0x86}, + {0x37e0, 0x68}, + {0x37e3, 0x30}, + {0x37e4, 0xf6}, + {0x37f0, 0x01}, + {0x37f1, 0xe0}, + {0x37f2, 0x24}, + {0x37f6, 0x1a}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x20}, + {0x3805, 0x1f}, + {0x3806, 0x18}, + {0x3807, 0x3f}, + {0x3808, 0x20}, + {0x3809, 0x00}, + {0x380a, 0x18}, + {0x380b, 0x00}, + {0x380c, 0x03}, + {0x380d, 0x00}, + {0x380e, 0x0c}, + {0x380f, 0x80}, + {0x3810, 0x00}, + {0x3811, 0x0f}, + {0x3812, 0x00}, + {0x3813, 0x20}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x381a, 0x0c}, + {0x381b, 0x70}, + {0x381c, 0x01}, + {0x381d, 0x80}, + {0x381f, 0x00}, + {0x3820, 0x40}, + {0x3821, 0x04}, + {0x3822, 0x00}, + {0x3823, 0x04}, + {0x3827, 0x40}, + {0x3828, 0x27}, + {0x382a, 0x80}, + {0x382e, 0x49}, + {0x3830, 0x20}, + {0x3831, 0x10}, + {0x3837, 0x20}, + {0x383f, 0x08}, + {0x3840, 0x00}, + {0x3847, 0x00}, + {0x384a, 0x00}, + {0x384c, 0x03}, + {0x384d, 0x00}, + {0x3858, 0x00}, + {0x3860, 0x00}, + {0x3867, 0x11}, + {0x386a, 0x00}, + {0x386b, 0x00}, + {0x386c, 0x00}, + {0x386d, 0x7c}, + {0x3888, 0x00}, + {0x3889, 0x10}, + {0x388a, 0x00}, + {0x388b, 0x20}, + {0x388c, 0x20}, + {0x388d, 0x00}, + {0x388e, 0x18}, + {0x388f, 0x00}, + {0x3890, 0x11}, + {0x3894, 0x02}, + {0x3895, 0x80}, + {0x3896, 0x00}, + {0x3899, 0x00}, + {0x38a0, 0x00}, + {0x38a1, 0x1d}, + {0x38a2, 0x98}, + {0x38a3, 0x00}, + {0x38a4, 0x1d}, + {0x38a5, 0x98}, + {0x38ac, 0x40}, + {0x38ad, 0x00}, + {0x38ae, 0x00}, + {0x38af, 0x00}, + {0x38b0, 0x00}, + {0x38b1, 0x00}, + {0x38b2, 0x00}, + {0x38b3, 0x00}, + {0x38b4, 0x20}, + {0x38b5, 0x1f}, + {0x38b6, 0x18}, + {0x38b7, 0x1f}, + {0x38b8, 0x20}, + {0x38b9, 0x00}, + {0x38ba, 0x18}, + {0x38bb, 0x00}, + {0x38bc, 0x00}, + {0x38bd, 0x10}, + {0x38be, 0x00}, + {0x38bf, 0x10}, + {0x38c0, 0x11}, + {0x38c1, 0x11}, + {0x38c2, 0x00}, + {0x38c3, 0x00}, + {0x38c4, 0x00}, + {0x38c5, 0x00}, + {0x38c6, 0x11}, + {0x38c7, 0x00}, + {0x38c8, 0x11}, + {0x38c9, 0x00}, + {0x38ca, 0x11}, + {0x38cb, 0x00}, + {0x38cc, 0x11}, + {0x38cd, 0x00}, + {0x38ce, 0x11}, + {0x38cf, 0x00}, + {0x38d1, 0x11}, + {0x38d2, 0x00}, + {0x38d3, 0x00}, + {0x38d4, 0x08}, + {0x38d5, 0x00}, + {0x38d6, 0x08}, + {0x38db, 0x20}, + {0x38dd, 0x10}, + {0x38de, 0x0c}, + {0x38df, 0x20}, + {0x38e0, 0x00}, + {0x38f3, 0x00}, + {0x3900, 0x40}, + {0x3906, 0x24}, + {0x3907, 0x00}, + {0x390a, 0x05}, + {0x3913, 0x0c}, + {0x3918, 0x00}, + {0x3919, 0x15}, + {0x395b, 0x05}, + {0x3982, 0x40}, + {0x398b, 0x00}, + {0x3994, 0x0b}, + {0x3995, 0x30}, + {0x399d, 0x05}, + {0x39a0, 0x0b}, + {0x39dc, 0x01}, + {0x39fb, 0x01}, + {0x39fc, 0x01}, + {0x39fd, 0x06}, + {0x39fe, 0x06}, + {0x3a1d, 0x01}, + {0x3a1e, 0x01}, + {0x3a1f, 0x03}, + {0x3a21, 0x01}, + {0x3a22, 0x06}, + {0x3a23, 0x03}, + {0x3a68, 0x05}, + {0x3a69, 0x20}, + {0x3a6d, 0x50}, + {0x3a78, 0x03}, + {0x3a79, 0x03}, + {0x3a7c, 0x04}, + {0x3a7d, 0x04}, + {0x3a94, 0x04}, + {0x3ab5, 0x00}, + {0x3ab6, 0x01}, + {0x3ab7, 0x01}, + {0x3ab8, 0x01}, + {0x3ab9, 0x01}, + {0x3af2, 0x03}, + {0x3b01, 0x00}, + {0x3b02, 0x00}, + {0x3b16, 0x00}, + {0x3b3d, 0x07}, + {0x3b4a, 0x38}, + {0x3b4b, 0x38}, + {0x3b56, 0x20}, + {0x3b57, 0x21}, + {0x3b58, 0x21}, + {0x3b59, 0x21}, + {0x3b5a, 0x14}, + {0x3b5b, 0x14}, + {0x3b5c, 0x14}, + {0x3b5d, 0x14}, + {0x3b82, 0x14}, + {0x3ba1, 0x20}, + {0x3ba4, 0x77}, + {0x3ba5, 0x77}, + {0x3ba6, 0x00}, + {0x3ba7, 0x00}, + {0x3baa, 0x33}, + {0x3bab, 0x37}, + {0x3bac, 0x77}, + {0x3baf, 0x00}, + {0x3bba, 0x4c}, + {0x3bde, 0x01}, + {0x3be0, 0x30}, + {0x3be7, 0x08}, + {0x3be8, 0x0f}, + {0x3beb, 0x00}, + {0x3bf2, 0x03}, + {0x3bf3, 0x01}, + {0x3bf4, 0x50}, + {0x3bfb, 0x01}, + {0x3bfc, 0x50}, + {0x3bff, 0x08}, + {0x3d84, 0x00}, + {0x3d85, 0x0b}, + {0x3d8c, 0x9b}, + {0x3d8d, 0xa0}, + {0x3daa, 0x00}, + {0x3dab, 0x00}, + {0x3f00, 0x10}, + {0x4008, 0x00}, + {0x4009, 0x02}, + {0x400e, 0x14}, + {0x4010, 0x34}, + {0x4011, 0x01}, + {0x4012, 0x17}, + {0x4015, 0x00}, + {0x4016, 0x1f}, + {0x4017, 0x00}, + {0x4018, 0x0f}, + {0x401a, 0x40}, + {0x401b, 0x04}, + {0x40f8, 0x04}, + {0x40f9, 0x00}, + {0x40fa, 0x02}, + {0x40fb, 0x00}, + {0x4100, 0x00}, + {0x4101, 0x00}, + {0x4102, 0x00}, + {0x4103, 0x00}, + {0x4105, 0x00}, + {0x4288, 0x27}, + {0x4504, 0x80}, + {0x4505, 0x0c}, + {0x4506, 0x01}, + {0x4509, 0x07}, + {0x450c, 0x00}, + {0x450d, 0x30}, + {0x450e, 0x00}, + {0x450f, 0x20}, + {0x4510, 0x00}, + {0x4511, 0x00}, + {0x4512, 0x00}, + {0x4513, 0x00}, + {0x4514, 0x00}, + {0x4515, 0x00}, + {0x4516, 0x00}, + {0x4517, 0x00}, + {0x4518, 0x00}, + {0x4519, 0x00}, + {0x451a, 0x00}, + {0x451b, 0x00}, + {0x451c, 0x00}, + {0x451d, 0x00}, + {0x451e, 0x00}, + {0x451f, 0x00}, + {0x4520, 0x00}, + {0x4521, 0x00}, + {0x4522, 0x00}, + {0x4523, 0x00}, + {0x4524, 0x00}, + {0x4525, 0x00}, + {0x4526, 0x00}, + {0x4527, 0x18}, + {0x4545, 0x00}, + {0x4546, 0x07}, + {0x4547, 0x33}, + {0x4549, 0x00}, + {0x454a, 0x00}, + {0x454b, 0x00}, + {0x454c, 0x00}, + {0x454d, 0x00}, + {0x454e, 0x00}, + {0x454f, 0x00}, + {0x4550, 0x00}, + {0x4551, 0x00}, + {0x4552, 0x00}, + {0x4553, 0x00}, + {0x4554, 0x00}, + {0x4555, 0x00}, + {0x4556, 0x00}, + {0x4557, 0x00}, + {0x4558, 0x00}, + {0x4559, 0x00}, + {0x455a, 0x00}, + {0x455b, 0x00}, + {0x455c, 0x00}, + {0x455d, 0x00}, + {0x455e, 0x00}, + {0x455f, 0x00}, + {0x4560, 0x00}, + {0x4561, 0x00}, + {0x4562, 0x00}, + {0x4563, 0x00}, + {0x4564, 0x00}, + {0x4565, 0x00}, + {0x4580, 0x01}, + {0x4583, 0x00}, + {0x4584, 0x00}, + {0x4585, 0x00}, + {0x4586, 0x00}, + {0x458c, 0x02}, + {0x458d, 0x00}, + {0x458e, 0x00}, + {0x45c0, 0x1c}, + {0x45c1, 0x80}, + {0x45c2, 0x0a}, + {0x45c3, 0x84}, + {0x45c4, 0x10}, + {0x45c5, 0x80}, + {0x45c6, 0x08}, + {0x45c7, 0x00}, + {0x45c8, 0x00}, + {0x45c9, 0x00}, + {0x45ca, 0x00}, + {0x45cb, 0x00}, + {0x45cc, 0x00}, + {0x45cd, 0x07}, + {0x45ce, 0x13}, + {0x45cf, 0x13}, + {0x45d0, 0x13}, + {0x45d2, 0x00}, + {0x45d3, 0x00}, + {0x45d4, 0x00}, + {0x45d5, 0x00}, + {0x45d6, 0x00}, + {0x45d7, 0x00}, + {0x45d8, 0x00}, + {0x45d9, 0x00}, + {0x45da, 0x00}, + {0x45dd, 0x00}, + {0x45de, 0x00}, + {0x45df, 0x00}, + {0x45e0, 0x00}, + {0x45e1, 0x00}, + {0x45e2, 0x00}, + {0x45e3, 0x00}, + {0x45e4, 0x00}, + {0x45e5, 0x00}, + {0x45e7, 0x00}, + {0x4602, 0x00}, + {0x4603, 0x15}, + {0x460b, 0x07}, + {0x4640, 0x01}, + {0x4641, 0x00}, + {0x4643, 0x08}, + {0x4644, 0xe0}, + {0x4645, 0xbf}, + {0x4647, 0x02}, + {0x464a, 0x00}, + {0x464b, 0x00}, + {0x464c, 0x01}, + {0x4680, 0x11}, + {0x4681, 0x80}, + {0x4684, 0x2b}, + {0x4685, 0x17}, + {0x4686, 0x00}, + {0x4687, 0x00}, + {0x4688, 0x00}, + {0x4689, 0x00}, + {0x468e, 0x30}, + {0x468f, 0x00}, + {0x4690, 0x00}, + {0x4691, 0x00}, + {0x4694, 0x04}, + {0x4800, 0x64}, + {0x4802, 0x02}, + {0x4806, 0x40}, + {0x4813, 0x10}, + {0x481b, 0x25}, + {0x4825, 0x32}, + {0x4826, 0x32}, + {0x4829, 0x64}, + {0x4836, 0x32}, + {0x4837, 0x04}, + {0x4840, 0x00}, + {0x4850, 0x42}, + {0x4851, 0xaa}, + {0x4853, 0x10}, + {0x4854, 0x05}, + {0x4855, 0x1c}, + {0x4860, 0x01}, + {0x4861, 0xec}, + {0x4862, 0x3a}, + {0x4883, 0x24}, + {0x4884, 0x11}, + {0x4888, 0x10}, + {0x4889, 0x00}, + {0x4911, 0x00}, + {0x491a, 0x40}, + {0x49f5, 0x00}, + {0x49f8, 0x04}, + {0x49f9, 0x00}, + {0x49fa, 0x02}, + {0x49fb, 0x00}, + {0x4a11, 0x00}, + {0x4a1a, 0x40}, + {0x4af8, 0x04}, + {0x4af9, 0x00}, + {0x4afa, 0x02}, + {0x4afb, 0x00}, + {0x4d00, 0x04}, + {0x4d01, 0x9d}, + {0x4d02, 0xbb}, + {0x4d03, 0x6c}, + {0x4d04, 0xc4}, + {0x4d05, 0x71}, + {0x5000, 0x5b}, + {0x5001, 0x28}, + {0x5002, 0x00}, + {0x5003, 0x0e}, + {0x5004, 0x02}, + {0x5007, 0x06}, + {0x5009, 0x2e}, + {0x5053, 0x05}, + {0x5060, 0x10}, + {0x5069, 0x10}, + {0x506a, 0x20}, + {0x506b, 0x04}, + {0x506c, 0x04}, + {0x506d, 0x0c}, + {0x506e, 0x0c}, + {0x506f, 0x04}, + {0x5070, 0x0c}, + {0x5071, 0x14}, + {0x5072, 0x1c}, + {0x5091, 0x00}, + {0x50c1, 0x00}, + {0x5110, 0x90}, + {0x5111, 0x14}, + {0x5112, 0x9b}, + {0x5113, 0x27}, + {0x5114, 0x01}, + {0x5155, 0x08}, + {0x5156, 0x0c}, + {0x5157, 0x0c}, + {0x5159, 0x08}, + {0x515a, 0x0c}, + {0x515b, 0x0c}, + {0x5180, 0xc0}, + {0x518a, 0x04}, + {0x51d3, 0x0a}, + {0x5251, 0x00}, + {0x5312, 0x00}, + {0x53c1, 0x00}, + {0x5410, 0x90}, + {0x5411, 0x14}, + {0x5412, 0x9b}, + {0x5413, 0x27}, + {0x5455, 0x08}, + {0x5456, 0x0c}, + {0x5457, 0x0c}, + {0x5459, 0x08}, + {0x545a, 0x0c}, + {0x545b, 0x0c}, + {0x5480, 0xc0}, + {0x548a, 0x04}, + {0x56c1, 0x00}, + {0x5710, 0x90}, + {0x5711, 0x14}, + {0x5712, 0x9b}, + {0x5713, 0x27}, + {0x5755, 0x08}, + {0x5756, 0x0c}, + {0x5757, 0x0c}, + {0x5759, 0x08}, + {0x575a, 0x0c}, + {0x575b, 0x0c}, + {0x5780, 0xc0}, + {0x578a, 0x04}, + {0x5853, 0xfe}, + {0x5854, 0xfe}, + {0x5855, 0xfe}, + {0x5856, 0xff}, + {0x5857, 0xff}, + {0x5858, 0xff}, + {0x587b, 0x16}, + {0x58a7, 0x11}, + {0x58c0, 0x3f}, + {0x58fd, 0x0a}, + {0x5925, 0x00}, + {0x5926, 0x00}, + {0x5927, 0x00}, + {0x5928, 0x00}, + {0x5929, 0x00}, + {0x592c, 0x06}, + {0x592d, 0x00}, + {0x592e, 0x03}, + {0x59c2, 0x00}, + {0x59c3, 0xce}, + {0x59c4, 0x01}, + {0x59c5, 0x20}, + {0x59c6, 0x01}, + {0x59c7, 0x91}, + {0x59c8, 0x02}, + {0x59c9, 0x2f}, + {0x59ca, 0x03}, + {0x59cb, 0x0a}, + {0x59cc, 0x04}, + {0x59cd, 0x3d}, + {0x59ce, 0x05}, + {0x59cf, 0xe8}, + {0x59d0, 0x08}, + {0x59d1, 0x3c}, + {0x59d2, 0x0b}, + {0x59d3, 0x7a}, + {0x59d4, 0x0f}, + {0x59d5, 0xff}, + {0x59d6, 0x0f}, + {0x59d7, 0xff}, + {0x59d8, 0x0f}, + {0x59d9, 0xff}, + {0x59da, 0x0f}, + {0x59db, 0xff}, + {0x59ef, 0x5f}, + {0x6901, 0x18}, + {0x6924, 0x00}, + {0x6925, 0x00}, + {0x6926, 0x00}, + {0x6942, 0x00}, + {0x6943, 0x00}, + {0x6944, 0x00}, + {0x694b, 0x00}, + {0x6a20, 0x03}, + {0x6a21, 0x04}, + {0x6a22, 0x00}, + {0x6a53, 0xfe}, + {0x6a54, 0xfe}, + {0x6a55, 0xfe}, + {0x6a56, 0xff}, + {0x6a57, 0xff}, + {0x6a58, 0xff}, + {0x6a7b, 0x16}, + {0x6aa7, 0x11}, + {0x6ac0, 0x3f}, + {0x6afd, 0x0a}, + {0x6b25, 0x00}, + {0x6b26, 0x00}, + {0x6b27, 0x00}, + {0x6b28, 0x00}, + {0x6b29, 0x00}, + {0x6b2c, 0x06}, + {0x6b2d, 0x00}, + {0x6b2e, 0x03}, + {0x6bc2, 0x00}, + {0x6bc3, 0xce}, + {0x6bc4, 0x01}, + {0x6bc5, 0x20}, + {0x6bc6, 0x01}, + {0x6bc7, 0x91}, + {0x6bc8, 0x02}, + {0x6bc9, 0x2f}, + {0x6bca, 0x03}, + {0x6bcb, 0x0a}, + {0x6bcc, 0x04}, + {0x6bcd, 0x3d}, + {0x6bce, 0x05}, + {0x6bcf, 0xe8}, + {0x6bd0, 0x08}, + {0x6bd1, 0x3c}, + {0x6bd2, 0x0b}, + {0x6bd3, 0x7a}, + {0x6bd4, 0x0f}, + {0x6bd5, 0xff}, + {0x6bd6, 0x0f}, + {0x6bd7, 0xff}, + {0x6bd8, 0x0f}, + {0x6bd9, 0xff}, + {0x6bda, 0x0f}, + {0x6bdb, 0xff}, + {0x6bef, 0x5f}, + {0xc200, 0x00}, + {0xc201, 0x00}, + {0xc202, 0x00}, + {0xc203, 0x00}, + {0xc210, 0x00}, + {0xc211, 0x00}, + {0xc212, 0x00}, + {0xc213, 0x00}, + {0xc214, 0x00}, + {0xc230, 0x00}, + {0xc231, 0x00}, + {0xc232, 0x00}, + {0xc233, 0x00}, + {0xc240, 0x00}, + {0xc241, 0x00}, + {0xc242, 0x00}, + {0xc243, 0x00}, + {0xc250, 0x00}, + {0xc251, 0x00}, + {0xc252, 0x00}, + {0xc253, 0x00}, + {0xc260, 0x00}, + {0xc261, 0x00}, + {0xc262, 0x00}, + {0xc263, 0x00}, + {0xc270, 0x00}, + {0xc271, 0x00}, + {0xc272, 0x00}, + {0xc273, 0x00}, + {0xc40e, 0xa0}, + {0xc418, 0x02}, + {0xc42f, 0x00}, + {0xc448, 0x00}, + {0xc44e, 0x03}, + {0xc44f, 0x03}, + {0xc450, 0x04}, + {0xc451, 0x04}, + {0xc46e, 0x01}, + {0xc478, 0x01}, + {0xc49c, 0x00}, + {0xc49d, 0x00}, + {0xc49e, 0x1c}, + {0xc49f, 0x30}, + {0xc4a2, 0x3a}, + {0xc4a3, 0x8e}, + {0xc4b9, 0x09}, + {0xc4bf, 0x01}, + {0xc4c1, 0x07}, + {0xc4c2, 0x07}, + {0xc4c3, 0x77}, + {0xc4c4, 0x77}, + {0xc4d2, 0x38}, + {0xc4d3, 0x38}, + {0xc4d4, 0x38}, + {0xc4d5, 0x38}, + {0xc4e3, 0x14}, + {0xc4e9, 0x20}, + {0xc4f8, 0x01}, + {0xc500, 0x01}, + {0xc506, 0x14}, + {0xc507, 0x02}, + {0xc50b, 0x77}, + {0xc50e, 0x00}, + {0xc50f, 0x00}, + {0xc510, 0x00}, + {0xc511, 0x00}, + {0xc512, 0x00}, + {0xc513, 0x4e}, + {0xc514, 0x4f}, + {0xc515, 0x2a}, + {0xc516, 0x16}, + {0xc517, 0x0b}, + {0xc518, 0x33}, + {0xc519, 0x33}, + {0xc51a, 0x33}, + {0xc51b, 0x33}, + {0xc51c, 0x33}, + {0xc51d, 0x37}, + {0xc51e, 0x37}, + {0xc51f, 0x3a}, + {0xc520, 0x3a}, + {0xc521, 0x3a}, + {0xc52e, 0x0e}, + {0xc52f, 0x0e}, + {0xc530, 0x0e}, + {0xc531, 0x0e}, + {0xc532, 0x0e}, + {0xc533, 0x0e}, + {0xc534, 0x0e}, + {0xc535, 0x0e}, + {0xc53a, 0x0e}, + {0xc53b, 0x0e}, + {0xc53c, 0x0e}, + {0xc53d, 0x0e}, + {0xc53e, 0x0e}, + {0xc53f, 0x0e}, + {0xc540, 0x0e}, + {0xc541, 0x0e}, + {0xc542, 0x0e}, + {0xc543, 0x0e}, + {0xc544, 0x0e}, + {0xc545, 0x0e}, + {0xc546, 0x0e}, + {0xc547, 0x0e}, + {0xc548, 0x0e}, + {0xc549, 0x0e}, + {0xc57d, 0x80}, + {0xc57f, 0x18}, + {0xc580, 0x18}, + {0xc581, 0x18}, + {0xc582, 0x18}, + {0xc583, 0x01}, + {0xc584, 0x01}, + {0xc586, 0x0a}, + {0xc587, 0x18}, + {0xc588, 0x18}, + {0xc589, 0x18}, + {0xc58a, 0x0c}, + {0xc58b, 0x08}, + {0xc58c, 0x04}, + {0xc58e, 0x0a}, + {0xc58f, 0x28}, + {0xc590, 0x28}, + {0xc591, 0x28}, + {0xc592, 0x28}, + {0xc593, 0x04}, + {0xc594, 0x04}, + {0xc597, 0x2c}, + {0xc598, 0x2c}, + {0xc599, 0x2c}, + {0xc59a, 0x28}, + {0xc59b, 0x20}, + {0xc59c, 0x18}, + {0xc5e3, 0x07}, + {0xc5e4, 0x00}, + {0xc5e5, 0x01}, + {0xc5e8, 0x01}, + {0xc5eb, 0x55}, + {0xc5ec, 0x05}, + {0xc624, 0xf8}, + {0xc638, 0x01}, + {0xc639, 0x00}, + {0xc63c, 0x01}, + {0xc63d, 0x00}, + {0xc640, 0x01}, + {0xc641, 0x00}, + {0xc64c, 0x08}, + {0xc64d, 0x08}, + {0xc64e, 0x08}, + {0xc64f, 0x08}, + {0xc650, 0x08}, + {0xc651, 0x08}, + {0xc664, 0x00}, + {0xc66b, 0x00}, + {0xc66c, 0x00}, + {0xc66d, 0x00}, + {0xc66e, 0x01}, + {0xc66f, 0x00}, + {0xc700, 0x80}, + {0xc702, 0x00}, + {0xc703, 0x00}, + {0xc726, 0x03}, + {0xc72b, 0xff}, + {0xc72c, 0xff}, + {0xc72d, 0xff}, + {0xc72f, 0x08}, + {0xc730, 0x00}, + {0xc731, 0x00}, + {0xc732, 0x00}, + {0xc733, 0x00}, + {0xc734, 0x00}, + {0xc735, 0x00}, + {0xc736, 0x01}, + {0xc739, 0x18}, + {0xc73a, 0x49}, + {0xc73b, 0x92}, + {0xc73c, 0x24}, + {0xc73d, 0x00}, + {0xc73e, 0x00}, + {0xc73f, 0x00}, + {0xc740, 0x00}, + {0xc741, 0x00}, + {0xc742, 0x00}, + {0xc743, 0x00}, + {0xc744, 0x00}, + {0xc745, 0x00}, + {0xc746, 0x01}, + {0xc747, 0x04}, + {0xc749, 0x1c}, + {0xc74c, 0x40}, + {0xc74e, 0x00}, + {0xc750, 0x55}, + {0xc751, 0x00}, + {0xc758, 0x40}, + {0xc75b, 0x01}, + {0xc75c, 0x05}, + {0xc765, 0x2a}, + {0xc773, 0x02}, + {0xc774, 0x03}, + {0xc78a, 0x03}, + {0xc78b, 0x04}, + {0xc797, 0x03}, + {0xc798, 0x03}, + {0xc79c, 0x00}, + {0xc79e, 0x01}, + {0xc7a0, 0x12}, + {0xc7a2, 0x01}, + {0xc7a3, 0x01}, + {0xc7a6, 0x02}, + {0xc7a7, 0xff}, + {0xc7a8, 0xff}, + {0xc7a9, 0xff}, + {0xc7aa, 0xff}, + {0xc7ab, 0xff}, + {0xc7ac, 0x02}, + {0xc7ad, 0xff}, + {0xc7ae, 0xff}, + {0xc7af, 0xff}, + {0xc7b0, 0xff}, + {0xc7b1, 0xff}, + {0xc7b2, 0x01}, + {0xc7b3, 0xff}, + {0xc7b4, 0xff}, + {0xc7b5, 0xff}, + {0xc7b6, 0xff}, + {0xc7c3, 0xff}, + {0xc7c4, 0x00}, + {0xc7c5, 0xff}, + {0xc7d9, 0x50}, + {0xc7da, 0xaa}, + {0xc7db, 0x0a}, + {0xc7dc, 0xa0}, + {0xc7e2, 0x01}, + {0xc7e4, 0x01}, + {0xc7e8, 0x12}, + {0xc7fd, 0x12}, + {0xc855, 0x07}, + {0xc8a4, 0x07}, + {0xc95a, 0x77}, + {0xc95b, 0x77}, + {0xc95c, 0x77}, + {0xc95d, 0x77}, + {0xc97b, 0x10}, + {0xc9a8, 0x1c}, + {0xc9b9, 0x28}, + {0xc9be, 0x01}, + {0xc9f3, 0x01}, + {0xc9fe, 0x0a}, + {0xc9ff, 0x0e}, + {0xca00, 0x1a}, + {0xca01, 0x1a}, + {0xca02, 0x1a}, + {0xca02, 0x1a}, + {0xca17, 0x03}, + {0xca18, 0x1a}, + {0xca19, 0x1a}, + {0xca1a, 0x1a}, + {0xca1b, 0x1a}, + {0xca22, 0x12}, + {0xca23, 0x12}, + {0xca24, 0x12}, + {0xca25, 0x12}, + {0xca26, 0x12}, + {0xca31, 0x12}, + {0xca32, 0x12}, + {0xca33, 0x12}, + {0xca34, 0x12}, + {0xca35, 0x12}, + {0xca36, 0x12}, + {0xca37, 0x12}, + {0xca38, 0x12}, + {0xca39, 0x12}, + {0xca3a, 0x12}, + {0xca45, 0x12}, + {0xca46, 0x12}, + {0xca47, 0x12}, + {0xca48, 0x12}, + {0xca49, 0x12}, + {0xcaab, 0x18}, + {0xcaca, 0x0f}, + {0xcada, 0x03}, + {REG_NULL, 0x00}, +}; + +static const struct regval ov50h40_10bit_4096x3072_cphy_30fps_regs[] = { + {0x0304, 0x02}, + {0x0305, 0xd0}, + {0x0327, 0x0e}, + {0x0329, 0x01}, + {0x032c, 0x00}, + {0x0344, 0x01}, + {0x0345, 0x10}, + {0x0360, 0x09}, + {0x3027, 0x00}, + {0x3400, 0x0c}, + {0x3422, 0x08}, + {0x3423, 0x00}, + {0x3506, 0xf8}, + {0x350d, 0x00}, + {0x350e, 0xb2}, + {0x350f, 0x40}, + {0x3546, 0xf8}, + {0x354d, 0x00}, + {0x354e, 0xb2}, + {0x354f, 0x40}, + {0x3586, 0xf8}, + {0x358d, 0x00}, + {0x358e, 0xb2}, + {0x358f, 0x40}, + {0x3609, 0x80}, + {0x360c, 0x4f}, + {0x3610, 0x08}, + {0x3614, 0x10}, + {0x3618, 0xcf}, + {0x3619, 0x40}, + {0x361a, 0x01}, + {0x361d, 0x1f}, + {0x363b, 0x9f}, + {0x363c, 0x6e}, + {0x3640, 0x00}, + {0x3641, 0x02}, + {0x3644, 0x00}, + {0x3645, 0x06}, + {0x3647, 0x01}, + {0x3650, 0xbf}, + {0x3653, 0x03}, + {0x3680, 0x00}, + {0x3682, 0x80}, + {0x3684, 0x01}, + {0x3688, 0x00}, + {0x368a, 0x0e}, + {0x3696, 0x41}, + {0x369a, 0x00}, + {0x36d0, 0x00}, + {0x36d3, 0x40}, + {0x3700, 0x1c}, + {0x3701, 0x13}, + {0x3704, 0x03}, + {0x3706, 0x34}, + {0x3707, 0x04}, + {0x3709, 0x7c}, + {0x370b, 0x94}, + {0x3712, 0x00}, + {0x3714, 0xf2}, + {0x3716, 0x40}, + {0x3722, 0x05}, + {0x3724, 0x08}, + {0x372b, 0x00}, + {0x372e, 0x1c}, + {0x372f, 0x13}, + {0x373f, 0x00}, + {0x374f, 0x58}, + {0x3755, 0x7c}, + {0x3757, 0x7f}, + {0x3759, 0x50}, + {0x375e, 0x0d}, + {0x375f, 0x00}, + {0x3770, 0x04}, + {0x3780, 0x5e}, + {0x3782, 0x01}, + {0x378a, 0x01}, + {0x3791, 0x34}, + {0x3793, 0x1c}, + {0x3795, 0x1c}, + {0x3797, 0x94}, + {0x3799, 0x3a}, + {0x379b, 0x3a}, + {0x379c, 0x01}, + {0x379f, 0x01}, + {0x37a0, 0x9b}, + {0x37a9, 0x01}, + {0x37b2, 0xc8}, + {0x37b7, 0x02}, + {0x37bd, 0x00}, + {0x37c1, 0x1a}, + {0x37c3, 0x1a}, + {0x37cb, 0x02}, + {0x37cd, 0x02}, + {0x37d0, 0x22}, + {0x37d4, 0x00}, + {0x37db, 0x10}, + {0x37dc, 0x1a}, + {0x37e3, 0x30}, + {0x37f0, 0x01}, + {0x37f6, 0x1a}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x20}, + {0x3805, 0x1f}, + {0x3806, 0x18}, + {0x3807, 0x3f}, + {0x3808, 0x10}, + {0x3809, 0x00}, + {0x380a, 0x0c}, + {0x380b, 0x00}, + {0x380c, 0x04}, + {0x380d, 0x4c}, + {0x380e, 0x08}, + {0x380f, 0xe0}, + {0x3810, 0x00}, + {0x3811, 0x07}, + {0x3813, 0x10}, + {0x3815, 0x11}, + {0x3820, 0x46}, + {0x3821, 0x10}, + {0x3822, 0x10}, + {0x3823, 0x04}, + {0x3827, 0x40}, + {0x3828, 0x21}, + {0x3830, 0x20}, + {0x3831, 0x12}, + {0x3837, 0x20}, + {0x383f, 0x08}, + {0x384c, 0x04}, + {0x384d, 0x4c}, + {0x3888, 0x00}, + {0x3889, 0x08}, + {0x388b, 0x10}, + {0x388c, 0x10}, + {0x388d, 0x00}, + {0x388e, 0x0c}, + {0x388f, 0x00}, + {0x3896, 0x00}, + {0x38db, 0x08}, + {0x38dd, 0x04}, + {0x38de, 0x03}, + {0x38df, 0x08}, + {0x3906, 0x24}, + {0x390a, 0x15}, + {0x3919, 0x11}, + {0x3982, 0x40}, + {0x398b, 0x00}, + {0x399d, 0x13}, + {0x39dc, 0x00}, + {0x39fb, 0x01}, + {0x39fc, 0x01}, + {0x39fd, 0x01}, + {0x39fe, 0x01}, + {0x3a1d, 0x01}, + {0x3a1e, 0x01}, + {0x3a21, 0x01}, + {0x3a22, 0x01}, + {0x3a68, 0x13}, + {0x3a69, 0x20}, + {0x3ab6, 0x01}, + {0x3ab7, 0x01}, + {0x3af2, 0x03}, + {0x3b01, 0x1d}, + {0x3b02, 0x00}, + {0x3b3d, 0x07}, + {0x3b4a, 0x00}, + {0x3b4b, 0x00}, + {0x3b56, 0x1f}, + {0x3b57, 0x1f}, + {0x3b58, 0x20}, + {0x3b59, 0x20}, + {0x3b5a, 0x19}, + {0x3b5b, 0x19}, + {0x3b5c, 0x19}, + {0x3b5d, 0x19}, + {0x3b82, 0x19}, + {0x3ba1, 0x1e}, + {0x3ba6, 0x77}, + {0x3ba7, 0x77}, + {0x3baa, 0x33}, + {0x3bab, 0x2f}, + {0x3baf, 0x16}, + {0x3bba, 0x48}, + {0x3bf3, 0x01}, + {0x3bfb, 0x01}, + {0x3bfc, 0x50}, + {0x3bff, 0x08}, + {0x400e, 0x1c}, + {0x4010, 0x34}, + {0x4012, 0x17}, + {0x4015, 0x08}, + {0x4016, 0x17}, + {0x4018, 0x07}, + {0x4506, 0x01}, + {0x4509, 0x07}, + {0x450c, 0x00}, + {0x450d, 0x60}, + {0x4510, 0x03}, + {0x4516, 0x55}, + {0x4517, 0x55}, + {0x4518, 0x55}, + {0x4519, 0x55}, + {0x451a, 0xaa}, + {0x451b, 0xaa}, + {0x451c, 0xaa}, + {0x451d, 0xaa}, + {0x451e, 0xff}, + {0x451f, 0xff}, + {0x4520, 0xff}, + {0x4521, 0xff}, + {0x4522, 0x29}, + {0x4523, 0x08}, + {0x4524, 0xbb}, + {0x4525, 0x0c}, + {0x4545, 0x00}, + {0x4546, 0x03}, + {0x4547, 0x9a}, + {0x4549, 0x00}, + {0x454a, 0x29}, + {0x454b, 0x08}, + {0x454c, 0xbb}, + {0x454d, 0x0c}, + {0x454e, 0x29}, + {0x454f, 0x08}, + {0x4550, 0xbb}, + {0x4551, 0x0c}, + {0x4552, 0x29}, + {0x4553, 0x08}, + {0x4554, 0xbb}, + {0x4555, 0x0c}, + {0x4556, 0x29}, + {0x4557, 0x08}, + {0x4558, 0xbb}, + {0x4559, 0x0c}, + {0x455a, 0x29}, + {0x455b, 0x08}, + {0x455c, 0xbb}, + {0x455d, 0x0c}, + {0x455e, 0x29}, + {0x455f, 0x08}, + {0x4560, 0xbb}, + {0x4561, 0x0c}, + {0x4562, 0x29}, + {0x4563, 0x08}, + {0x4564, 0xbb}, + {0x4565, 0x0c}, + {0x45c0, 0x8e}, + {0x45c1, 0x80}, + {0x45c2, 0x0a}, + {0x45c3, 0x04}, + {0x45c4, 0x13}, + {0x45c5, 0x40}, + {0x45c6, 0x01}, + {0x4602, 0x00}, + {0x4603, 0x15}, + {0x460b, 0x07}, + {0x4640, 0x01}, + {0x4641, 0x00}, + {0x4643, 0x0c}, + {0x4680, 0x11}, + {0x4684, 0x2b}, + {0x468e, 0x30}, + {0x4813, 0x10}, + {0x4836, 0x32}, + {0x4837, 0x04}, + {0x49f5, 0x00}, + {0x5000, 0x2b}, + {0x5001, 0x08}, + {0x5002, 0x00}, + {0x5007, 0x06}, + {0x5009, 0x40}, + {0x5091, 0x00}, + {0x5180, 0xc0}, + {0x5480, 0xc0}, + {0x5780, 0xc0}, + {0x6a03, 0x00}, + {0xc200, 0x00}, + {0xc201, 0x00}, + {0xc202, 0x00}, + {0xc203, 0x00}, + {0xc210, 0x00}, + {0xc211, 0x00}, + {0xc212, 0x00}, + {0xc213, 0x00}, + {0xc214, 0x00}, + {0xc230, 0x00}, + {0xc231, 0x00}, + {0xc232, 0x00}, + {0xc233, 0x00}, + {0xc240, 0x00}, + {0xc241, 0x00}, + {0xc242, 0x00}, + {0xc243, 0x00}, + {0xc250, 0x00}, + {0xc251, 0x00}, + {0xc252, 0x00}, + {0xc253, 0x00}, + {0xc260, 0x00}, + {0xc261, 0x00}, + {0xc262, 0x00}, + {0xc263, 0x00}, + {0xc270, 0x00}, + {0xc271, 0x00}, + {0xc272, 0x00}, + {0xc273, 0x00}, + {0xc40e, 0x00}, + {0xc448, 0x00}, + {0xc46e, 0x01}, + {0xc478, 0x01}, + {0xc49e, 0x34}, + {0xc49f, 0x34}, + {0xc4a2, 0x94}, + {0xc4a3, 0x94}, + {0xc4c1, 0x07}, + {0xc4c2, 0x07}, + {0xc4c3, 0x77}, + {0xc4c4, 0x77}, + {0xc4d2, 0x00}, + {0xc4d3, 0x00}, + {0xc4d4, 0x00}, + {0xc4d5, 0x00}, + {0xc4e3, 0x19}, + {0xc4e9, 0x1e}, + {0xc506, 0x16}, + {0xc50e, 0x1f}, + {0xc50f, 0x1f}, + {0xc510, 0x0f}, + {0xc511, 0x07}, + {0xc512, 0x03}, + {0xc513, 0x4e}, + {0xc514, 0x4e}, + {0xc515, 0x27}, + {0xc516, 0x16}, + {0xc517, 0x0c}, + {0xc518, 0x33}, + {0xc519, 0x33}, + {0xc51a, 0x33}, + {0xc51b, 0x3b}, + {0xc51c, 0x3b}, + {0xc51d, 0x2f}, + {0xc51e, 0x2f}, + {0xc51f, 0x2f}, + {0xc520, 0x2f}, + {0xc521, 0x30}, + {0xc52e, 0x0e}, + {0xc52f, 0x0e}, + {0xc530, 0x0e}, + {0xc531, 0x0e}, + {0xc532, 0x0e}, + {0xc533, 0x0e}, + {0xc534, 0x0e}, + {0xc535, 0x0e}, + {0xc542, 0x0e}, + {0xc543, 0x0e}, + {0xc544, 0x0e}, + {0xc545, 0x0e}, + {0xc546, 0x0e}, + {0xc547, 0x0e}, + {0xc548, 0x0e}, + {0xc549, 0x0e}, + {0xc57d, 0x00}, + {0xc581, 0x18}, + {0xc582, 0x18}, + {0xc583, 0x02}, + {0xc584, 0x01}, + {0xc587, 0x18}, + {0xc589, 0x18}, + {0xc58a, 0x10}, + {0xc58b, 0x08}, + {0xc58c, 0x01}, + {0xc58f, 0x28}, + {0xc590, 0x28}, + {0xc591, 0x28}, + {0xc592, 0x28}, + {0xc593, 0x0a}, + {0xc594, 0x06}, + {0xc597, 0x2e}, + {0xc598, 0x2e}, + {0xc599, 0x2e}, + {0xc59a, 0x18}, + {0xc59b, 0x0e}, + {0xc59c, 0x08}, + {0xc5e4, 0x00}, + {0xc5e5, 0x07}, + {0xc5e8, 0x01}, + {0xc702, 0x10}, + {0xc726, 0x03}, + {0xc72b, 0xff}, + {0xc72c, 0xff}, + {0xc72d, 0xff}, + {0xc72f, 0x08}, + {0xc736, 0x01}, + {0xc739, 0x18}, + {0xc73a, 0xa6}, + {0xc73b, 0x00}, + {0xc73c, 0x00}, + {0xc746, 0x01}, + {0xc747, 0x04}, + {0xc749, 0x1c}, + {0xc75b, 0x01}, + {0xc75c, 0x05}, + {0xc765, 0x2a}, + {0xc773, 0x02}, + {0xc774, 0x03}, + {0xc78a, 0x03}, + {0xc78b, 0x04}, + {0xc798, 0x03}, + {0xc7a2, 0x01}, + {0xc7a6, 0x02}, + {0xc7a7, 0x02}, + {0xc7a8, 0xff}, + {0xc7a9, 0xff}, + {0xc7aa, 0xff}, + {0xc7ac, 0x02}, + {0xc7ad, 0x08}, + {0xc7ae, 0xff}, + {0xc7af, 0xff}, + {0xc7b0, 0xff}, + {0xc7b2, 0x01}, + {0xc7b3, 0x02}, + {0xc7b4, 0xff}, + {0xc7b5, 0xff}, + {0xc7b6, 0xff}, + {0xc7c4, 0x01}, + {0xc7c5, 0x00}, + {0xc7e2, 0x01}, + {0xc855, 0x77}, + {0xc8a4, 0x77}, + {0xc95a, 0x77}, + {0xc95b, 0x77}, + {0xc9b9, 0x18}, + {0xc9fe, 0x0a}, + {0xc9ff, 0x12}, + {0xca00, 0x1a}, + {0xca02, 0x1a}, + {0xca17, 0x04}, + {0xca18, 0x1a}, + {0xca19, 0x1a}, + {0x3501, 0x08}, + {0x3502, 0x00}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {REG_NULL, 0x00}, +}; + +static const struct regval ov50h40_10bit_8192x6144_cphy_30fps_regs[] = { + {0x0304, 0x02}, + {0x0305, 0xd0}, + {0x0327, 0x0e}, + {0x0329, 0x01}, + {0x032c, 0x00}, + {0x0344, 0x01}, + {0x0345, 0x20}, + {0x0360, 0x09}, + {0x3027, 0x00}, + {0x3400, 0x0c}, + {0x3422, 0x08}, + {0x3423, 0x00}, + {0x3506, 0x78}, + {0x350d, 0x01}, + {0x350e, 0x00}, + {0x350f, 0x00}, + {0x3546, 0x78}, + {0x354d, 0x01}, + {0x354e, 0x00}, + {0x354f, 0x00}, + {0x3586, 0x78}, + {0x358d, 0x01}, + {0x358e, 0x00}, + {0x358f, 0x00}, + {0x3609, 0x80}, + {0x360c, 0x0f}, + {0x3610, 0x08}, + {0x3614, 0x0c}, + {0x3618, 0xcf}, + {0x3619, 0x44}, + {0x361a, 0x81}, + {0x361d, 0x1f}, + {0x363b, 0x6a}, + {0x363c, 0x6a}, + {0x3640, 0x00}, + {0x3641, 0x02}, + {0x3644, 0x00}, + {0x3645, 0x06}, + {0x3647, 0x01}, + {0x3650, 0xbf}, + {0x3653, 0x03}, + {0x3680, 0x00}, + {0x3682, 0x80}, + {0x3684, 0x00}, + {0x3688, 0x00}, + {0x368a, 0x0e}, + {0x3696, 0x41}, + {0x369a, 0x00}, + {0x36d0, 0x00}, + {0x36d3, 0x80}, + {0x3700, 0x1c}, + {0x3701, 0x13}, + {0x3704, 0x03}, + {0x3706, 0x1c}, + {0x3707, 0x04}, + {0x3709, 0x70}, + {0x370b, 0x3a}, + {0x3712, 0x01}, + {0x3714, 0xf8}, + {0x3716, 0x40}, + {0x3722, 0x05}, + {0x3724, 0x5d}, + {0x372b, 0x00}, + {0x372e, 0x1c}, + {0x372f, 0x13}, + {0x373f, 0x00}, + {0x374f, 0x58}, + {0x3755, 0xb1}, + {0x3757, 0x30}, + {0x3759, 0x50}, + {0x375e, 0x00}, + {0x375f, 0x00}, + {0x3770, 0x01}, + {0x3780, 0x5c}, + {0x3782, 0x01}, + {0x378a, 0x01}, + {0x3791, 0x30}, + {0x3793, 0x1c}, + {0x3795, 0x1c}, + {0x3797, 0x8e}, + {0x3799, 0x3a}, + {0x379b, 0x3a}, + {0x379c, 0x01}, + {0x379f, 0x01}, + {0x37a0, 0x70}, + {0x37a9, 0x01}, + {0x37b2, 0xc8}, + {0x37b7, 0x02}, + {0x37bd, 0x00}, + {0x37c1, 0x1a}, + {0x37c3, 0x1a}, + {0x37cb, 0x02}, + {0x37cd, 0x01}, + {0x37d0, 0x00}, + {0x37d4, 0x00}, + {0x37db, 0x10}, + {0x37dc, 0x1a}, + {0x37e3, 0x30}, + {0x37f0, 0x01}, + {0x37f6, 0x1a}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x20}, + {0x3805, 0x1f}, + {0x3806, 0x18}, + {0x3807, 0x3f}, + {0x3808, 0x20}, + {0x3809, 0x00}, + {0x380a, 0x18}, + {0x380b, 0x00}, + {0x380c, 0x03}, + {0x380d, 0x06}, + {0x380e, 0x0c}, + {0x380f, 0x96}, + {0x3810, 0x00}, + {0x3811, 0x0f}, + {0x3813, 0x20}, + {0x3815, 0x11}, + {0x3820, 0x44}, + {0x3821, 0x00}, + {0x3822, 0x00}, + {0x3823, 0x04}, + {0x3827, 0x40}, + {0x3828, 0x27}, + {0x3830, 0x20}, + {0x3831, 0x10}, + {0x3837, 0x20}, + {0x383f, 0x08}, + {0x384c, 0x03}, + {0x384d, 0x06}, + {0x3888, 0x00}, + {0x3889, 0x10}, + {0x388b, 0x20}, + {0x388c, 0x20}, + {0x388d, 0x00}, + {0x388e, 0x18}, + {0x388f, 0x00}, + {0x3896, 0x00}, + {0x38db, 0x20}, + {0x38dd, 0x10}, + {0x38de, 0x0c}, + {0x38df, 0x20}, + {0x3906, 0x24}, + {0x390a, 0x05}, + {0x3919, 0x15}, + {0x3982, 0x40}, + {0x398b, 0x00}, + {0x399d, 0x05}, + {0x39dc, 0x01}, + {0x39fb, 0x01}, + {0x39fc, 0x01}, + {0x39fd, 0x06}, + {0x39fe, 0x06}, + {0x3a1d, 0x01}, + {0x3a1e, 0x01}, + {0x3a21, 0x01}, + {0x3a22, 0x06}, + {0x3a68, 0x05}, + {0x3a69, 0x20}, + {0x3ab6, 0x01}, + {0x3ab7, 0x01}, + {0x3af2, 0x03}, + {0x3b01, 0x00}, + {0x3b02, 0x00}, + {0x3b3d, 0x07}, + {0x3b4a, 0x38}, + {0x3b4b, 0x38}, + {0x3b56, 0x20}, + {0x3b57, 0x21}, + {0x3b58, 0x21}, + {0x3b59, 0x21}, + {0x3b5a, 0x14}, + {0x3b5b, 0x14}, + {0x3b5c, 0x14}, + {0x3b5d, 0x14}, + {0x3b82, 0x14}, + {0x3ba1, 0x20}, + {0x3ba6, 0x00}, + {0x3ba7, 0x00}, + {0x3baa, 0x33}, + {0x3bab, 0x37}, + {0x3baf, 0x00}, + {0x3bba, 0x4c}, + {0x3bf3, 0x01}, + {0x3bfb, 0x01}, + {0x3bfc, 0x50}, + {0x3bff, 0x08}, + {0x400e, 0x14}, + {0x4010, 0x34}, + {0x4012, 0x17}, + {0x4015, 0x10}, + {0x4016, 0x2f}, + {0x4018, 0x0f}, + {0x4506, 0x01}, + {0x4509, 0x07}, + {0x450c, 0x00}, + {0x450d, 0x30}, + {0x4510, 0x00}, + {0x4516, 0x00}, + {0x4517, 0x00}, + {0x4518, 0x00}, + {0x4519, 0x00}, + {0x451a, 0x00}, + {0x451b, 0x00}, + {0x451c, 0x00}, + {0x451d, 0x00}, + {0x451e, 0x00}, + {0x451f, 0x00}, + {0x4520, 0x00}, + {0x4521, 0x00}, + {0x4522, 0x00}, + {0x4523, 0x00}, + {0x4524, 0x00}, + {0x4525, 0x00}, + {0x4545, 0x00}, + {0x4546, 0x04}, + {0x4547, 0xcc}, + {0x4549, 0x00}, + {0x454a, 0x00}, + {0x454b, 0x00}, + {0x454c, 0x00}, + {0x454d, 0x00}, + {0x454e, 0x00}, + {0x454f, 0x00}, + {0x4550, 0x00}, + {0x4551, 0x00}, + {0x4552, 0x00}, + {0x4553, 0x00}, + {0x4554, 0x00}, + {0x4555, 0x00}, + {0x4556, 0x00}, + {0x4557, 0x00}, + {0x4558, 0x00}, + {0x4559, 0x00}, + {0x455a, 0x00}, + {0x455b, 0x00}, + {0x455c, 0x00}, + {0x455d, 0x00}, + {0x455e, 0x00}, + {0x455f, 0x00}, + {0x4560, 0x00}, + {0x4561, 0x00}, + {0x4562, 0x00}, + {0x4563, 0x00}, + {0x4564, 0x00}, + {0x4565, 0x00}, + {0x45c0, 0x9c}, + {0x45c1, 0x80}, + {0x45c2, 0x0a}, + {0x45c3, 0x04}, + {0x45c4, 0x13}, + {0x45c5, 0x80}, + {0x45c6, 0x08}, + {0x4602, 0x00}, + {0x4603, 0x15}, + {0x460b, 0x07}, + {0x4640, 0x01}, + {0x4641, 0x00}, + {0x4643, 0x08}, + {0x4680, 0x11}, + {0x4684, 0x2b}, + {0x468e, 0x30}, + {0x4813, 0x10}, + {0x4836, 0x32}, + {0x4837, 0x04}, + {0x49f5, 0x00}, + {0x5000, 0x5b}, + {0x5001, 0x28}, + {0x5002, 0x00}, + {0x5007, 0x06}, + {0x5009, 0x2e}, + {0x5091, 0x00}, + {0x5180, 0xc0}, + {0x5480, 0xc0}, + {0x5780, 0xc0}, + {0x6a03, 0x00}, + {0xc200, 0x00}, + {0xc201, 0x00}, + {0xc202, 0x00}, + {0xc203, 0x00}, + {0xc210, 0x00}, + {0xc211, 0x00}, + {0xc212, 0x00}, + {0xc213, 0x00}, + {0xc214, 0x00}, + {0xc230, 0x00}, + {0xc231, 0x00}, + {0xc232, 0x00}, + {0xc233, 0x00}, + {0xc240, 0x00}, + {0xc241, 0x00}, + {0xc242, 0x00}, + {0xc243, 0x00}, + {0xc250, 0x00}, + {0xc251, 0x00}, + {0xc252, 0x00}, + {0xc253, 0x00}, + {0xc260, 0x00}, + {0xc261, 0x00}, + {0xc262, 0x00}, + {0xc263, 0x00}, + {0xc270, 0x00}, + {0xc271, 0x00}, + {0xc272, 0x00}, + {0xc273, 0x00}, + {0xc40e, 0xa0}, + {0xc448, 0x00}, + {0xc46e, 0x01}, + {0xc478, 0x01}, + {0xc49e, 0x1c}, + {0xc49f, 0x30}, + {0xc4a2, 0x3a}, + {0xc4a3, 0x8e}, + {0xc4c1, 0x07}, + {0xc4c2, 0x07}, + {0xc4c3, 0x77}, + {0xc4c4, 0x77}, + {0xc4d2, 0x38}, + {0xc4d3, 0x38}, + {0xc4d4, 0x38}, + {0xc4d5, 0x38}, + {0xc4e3, 0x14}, + {0xc4e9, 0x20}, + {0xc506, 0x14}, + {0xc50e, 0x00}, + {0xc50f, 0x00}, + {0xc510, 0x00}, + {0xc511, 0x00}, + {0xc512, 0x00}, + {0xc513, 0x4e}, + {0xc514, 0x4f}, + {0xc515, 0x2a}, + {0xc516, 0x16}, + {0xc517, 0x0b}, + {0xc518, 0x33}, + {0xc519, 0x33}, + {0xc51a, 0x33}, + {0xc51b, 0x33}, + {0xc51c, 0x33}, + {0xc51d, 0x37}, + {0xc51e, 0x37}, + {0xc51f, 0x3a}, + {0xc520, 0x3a}, + {0xc521, 0x3a}, + {0xc52e, 0x0e}, + {0xc52f, 0x0e}, + {0xc530, 0x0e}, + {0xc531, 0x0e}, + {0xc532, 0x0e}, + {0xc533, 0x0e}, + {0xc534, 0x0e}, + {0xc535, 0x0e}, + {0xc542, 0x0e}, + {0xc543, 0x0e}, + {0xc544, 0x0e}, + {0xc545, 0x0e}, + {0xc546, 0x0e}, + {0xc547, 0x0e}, + {0xc548, 0x0e}, + {0xc549, 0x0e}, + {0xc57d, 0x80}, + {0xc581, 0x18}, + {0xc582, 0x18}, + {0xc583, 0x01}, + {0xc584, 0x01}, + {0xc587, 0x18}, + {0xc589, 0x18}, + {0xc58a, 0x0c}, + {0xc58b, 0x08}, + {0xc58c, 0x04}, + {0xc58f, 0x28}, + {0xc590, 0x28}, + {0xc591, 0x28}, + {0xc592, 0x28}, + {0xc593, 0x04}, + {0xc594, 0x04}, + {0xc597, 0x2c}, + {0xc598, 0x2c}, + {0xc599, 0x2c}, + {0xc59a, 0x28}, + {0xc59b, 0x20}, + {0xc59c, 0x18}, + {0xc5e4, 0x00}, + {0xc5e5, 0x01}, + {0xc5e8, 0x01}, + {0xc702, 0x00}, + {0xc726, 0x03}, + {0xc72b, 0xff}, + {0xc72c, 0xff}, + {0xc72d, 0xff}, + {0xc72f, 0x08}, + {0xc736, 0x01}, + {0xc739, 0x18}, + {0xc73a, 0x49}, + {0xc73b, 0x92}, + {0xc73c, 0x24}, + {0xc746, 0x01}, + {0xc747, 0x04}, + {0xc749, 0x1c}, + {0xc75b, 0x01}, + {0xc75c, 0x05}, + {0xc765, 0x2a}, + {0xc773, 0x02}, + {0xc774, 0x03}, + {0xc78a, 0x03}, + {0xc78b, 0x04}, + {0xc798, 0x03}, + {0xc7a2, 0x01}, + {0xc7a6, 0x02}, + {0xc7a7, 0xff}, + {0xc7a8, 0xff}, + {0xc7a9, 0xff}, + {0xc7aa, 0xff}, + {0xc7ac, 0x02}, + {0xc7ad, 0xff}, + {0xc7ae, 0xff}, + {0xc7af, 0xff}, + {0xc7b0, 0xff}, + {0xc7b2, 0x01}, + {0xc7b3, 0xff}, + {0xc7b4, 0xff}, + {0xc7b5, 0xff}, + {0xc7b6, 0xff}, + {0xc7c4, 0x00}, + {0xc7c5, 0xff}, + {0xc7e2, 0x01}, + {0xc855, 0x07}, + {0xc8a4, 0x07}, + {0xc95a, 0x77}, + {0xc95b, 0x77}, + {0xc9b9, 0x28}, + {0xc9fe, 0x0a}, + {0xc9ff, 0x0e}, + {0xca00, 0x1a}, + {0xca02, 0x1a}, + {0xca17, 0x03}, + {0xca18, 0x1a}, + {0xca19, 0x1a}, + {0x3501, 0x0c}, + {0x3502, 0x00}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {REG_NULL, 0x00}, +}; + +static const struct other_data ov50h40_spd = { + .width = 4096, + .height = 768, + .bus_fmt = MEDIA_BUS_FMT_SPD_2X8, + .data_type = 0x19, + .data_bit = 10, +}; + +/* + * The width and height must be configured to be + * the same as the current output resolution of the sensor. + * The input width of the isp needs to be 16 aligned. + * The input height of the isp needs to be 8 aligned. + * If the width or height does not meet the alignment rules, + * you can configure the cropping parameters with the following function to + * crop out the appropriate resolution. + * struct v4l2_subdev_pad_ops { + * .get_selection + * } + */ +static const struct ov50h40_mode supported_modes_dphy[] = { + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 4096, + .height = 3072, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0840, + .hts_def = 0x41a * 4, + .vts_def = 0x0c66, + .mipi_freq_idx = 2, + .bpp = 10, + .reg_list = ov50h40_10bit_4096x3072_dphy_30fps_regs, + .hdr_mode = NO_HDR, + .spd = &ov50h40_spd, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 8192, + .height = 6144, + .max_fps = { + .numerator = 10000, + .denominator = 120000, + }, + .exp_def = 0x0240, + .hts_def = 0x9f6 * 4, + .vts_def = 0x0cc3 * 2, + .mipi_freq_idx = 3, + .bpp = 10, + .reg_list = ov50h40_10bit_8192x6144_dphy_12fps_regs, + .hdr_mode = NO_HDR, + .spd = &ov50h40_spd, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, +#ifdef DEBUG + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 4096, + .height = 3072, + .max_fps = { + .numerator = 10000, + .denominator = 150000, + }, + .exp_def = 0x0240, + .hts_def = 0x0834 * 4, + .vts_def = 0x0c66, + .mipi_freq_idx = 1, + .bpp = 10, + .reg_list = ov50h40_10bit_4096x3072_dphy_regs, + .hdr_mode = NO_HDR, + .spd = &ov50h40_spd, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 8192, + .height = 6144, + .max_fps = { + .numerator = 10000, + .denominator = 30000, + }, + .exp_def = 0x0240, + .hts_def = 0x09f6 * 4, + .vts_def = 0x0cc3 * 2, + .mipi_freq_idx = 1, + .bpp = 10, + .reg_list = ov50h40_10bit_8192x6144_dphy_regs, + .hdr_mode = NO_HDR, + .spd = &ov50h40_spd, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 4096, + .height = 3072, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0840, + .hts_def = 0x3e8 * 8, + .vts_def = 0x0d05, + .mipi_freq_idx = 2, + .bpp = 10, + .reg_list = ov50h40_10bit_4096x3072_dphy_30fps_nopd_regs, + .hdr_mode = NO_HDR, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, +#endif +}; + +static const struct ov50h40_mode supported_modes_cphy[] = { + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 4096, + .height = 3072, + .max_fps = { + .numerator = 10000, + .denominator = 150000, + }, + .exp_def = 0x0C00, + .hts_def = 0x044c, + .vts_def = 0x08e0, + .mipi_freq_idx = 0, + .bpp = 10, + .reg_list = ov50h40_10bit_4096x3072_cphy_regs, + .hdr_mode = NO_HDR, + .spd = &ov50h40_spd, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 4096, + .height = 3072, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0c00, + .hts_def = 0x044c, + .vts_def = 0x08e0, + .mipi_freq_idx = 2, + .bpp = 10, + .reg_list = ov50h40_10bit_4096x3072_cphy_30fps_regs, + .hdr_mode = NO_HDR, + .spd = &ov50h40_spd, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 8192, + .height = 6144, + .max_fps = { + .numerator = 10000, + .denominator = 120000, + }, + .exp_def = 0x0c00, + .hts_def = 0x0306, + .vts_def = 0x0c96, + .mipi_freq_idx = 3, + .bpp = 10, + .reg_list = ov50h40_10bit_8192x6144_cphy_30fps_regs, + .hdr_mode = NO_HDR, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, +}; + +static const s64 link_freq_items[] = { + MIPI_FREQ_356M, + MIPI_FREQ_384M, + MIPI_FREQ_750M, + MIPI_FREQ_1250M, +}; + +static const char * const ov50h40_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +static int __ov50h40_power_on(struct ov50h40 *ov50h40); + +/* Write registers up to 4 at a time */ +static int ov50h40_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; + + 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) { + dev_err(&client->dev, "Failed to write 0x%04x,0x%x\n", reg, val); + return -EIO; + } + return 0; +} + +static int ov50h40_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 |= ov50h40_write_reg(client, regs[i].addr, + OV50H40_REG_VALUE_08BIT, regs[i].val); + } + return ret; +} + +/* Read registers up to 4 at a time */ +static int ov50h40_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 ov50h40_get_reso_dist(const struct ov50h40_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct ov50h40_mode * +ov50h40_find_best_fit(struct ov50h40 *ov50h40, 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 < ov50h40->cfg_num; i++) { + dist = ov50h40_get_reso_dist(&ov50h40->support_modes[i], framefmt); + if ((cur_best_fit_dist == -1 || dist < cur_best_fit_dist) && + (ov50h40->support_modes[i].bus_fmt == framefmt->code)) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + dev_info(&ov50h40->client->dev, "%s: cur_best_fit(%d)", + __func__, cur_best_fit); + return &ov50h40->support_modes[cur_best_fit]; +} + +static int ov50h40_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + const struct ov50h40_mode *mode; + s64 h_blank, vblank_def; + u64 pixel_rate = 0; + u32 lane_num = ov50h40->bus_cfg.bus.mipi_csi2.num_data_lanes; + + mutex_lock(&ov50h40->mutex); + + mode = ov50h40_find_best_fit(ov50h40, fmt); + fmt->format.code = mode->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, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&ov50h40->mutex); + return -ENOTTY; +#endif + } else { + ov50h40->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(ov50h40->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov50h40->vblank, vblank_def, + OV50H40_VTS_MAX - mode->height, + 1, vblank_def); + __v4l2_ctrl_s_ctrl(ov50h40->vblank, vblank_def); + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * lane_num; + __v4l2_ctrl_s_ctrl_int64(ov50h40->pixel_rate, + pixel_rate); + __v4l2_ctrl_s_ctrl(ov50h40->link_freq, + mode->mipi_freq_idx); + } + dev_info(&ov50h40->client->dev, "%s: mode->mipi_freq_idx(%d)", + __func__, mode->mipi_freq_idx); + + mutex_unlock(&ov50h40->mutex); + + return 0; +} + +static int ov50h40_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + const struct ov50h40_mode *mode = ov50h40->cur_mode; + + mutex_lock(&ov50h40->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&ov50h40->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->bus_fmt; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&ov50h40->mutex); + + return 0; +} + +static int ov50h40_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + + if (code->index != 0) + return -EINVAL; + code->code = ov50h40->cur_mode->bus_fmt; + + return 0; +} + +static int ov50h40_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + + if (fse->index >= ov50h40->cfg_num) + return -EINVAL; + + if (fse->code != ov50h40->support_modes[fse->index].bus_fmt) + return -EINVAL; + + fse->min_width = ov50h40->support_modes[fse->index].width; + fse->max_width = ov50h40->support_modes[fse->index].width; + fse->max_height = ov50h40->support_modes[fse->index].height; + fse->min_height = ov50h40->support_modes[fse->index].height; + + return 0; +} + +static int ov50h40_enable_test_pattern(struct ov50h40 *ov50h40, u32 pattern) +{ + u32 val; + int ret = 0; + + if (pattern) + val = ((pattern - 1) << 4) | OV50H40_TEST_PATTERN_ENABLE; + else + val = OV50H40_TEST_PATTERN_DISABLE; + ret = ov50h40_write_reg(ov50h40->client, OV50H40_REG_TEST_PATTERN, + OV50H40_REG_VALUE_08BIT, val); + return ret; +} + +static int ov50h40_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + const struct ov50h40_mode *mode = ov50h40->cur_mode; + + fi->interval = mode->max_fps; + + return 0; +} + +static int ov50h40_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + u32 lane_num = ov50h40->bus_cfg.bus.mipi_csi2.num_data_lanes; + u32 val = 0; + + val = 1 << (lane_num - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + config->type = ov50h40->bus_cfg.bus_type; + config->flags = val; + + return 0; +} + +static void ov50h40_get_otp(struct otp_info *otp, + struct rkmodule_inf *inf) +{ + u32 i, j; + u32 w, h; + + /* awb */ + if (otp->awb_data.flag) { + inf->awb.flag = 1; + inf->awb.r_value = otp->awb_data.r_ratio; + inf->awb.b_value = otp->awb_data.b_ratio; + inf->awb.gr_value = otp->awb_data.g_ratio; + inf->awb.gb_value = 0x0; + + inf->awb.golden_r_value = otp->awb_data.r_golden; + inf->awb.golden_b_value = otp->awb_data.b_golden; + inf->awb.golden_gr_value = otp->awb_data.g_golden; + inf->awb.golden_gb_value = 0x0; + } + + /* lsc */ + if (otp->lsc_data.flag) { + inf->lsc.flag = 1; + inf->lsc.width = otp->basic_data.size.width; + inf->lsc.height = otp->basic_data.size.height; + inf->lsc.table_size = otp->lsc_data.table_size; + + for (i = 0; i < 289; i++) { + inf->lsc.lsc_r[i] = (otp->lsc_data.data[i * 2] << 8) | + otp->lsc_data.data[i * 2 + 1]; + inf->lsc.lsc_gr[i] = (otp->lsc_data.data[i * 2 + 578] << 8) | + otp->lsc_data.data[i * 2 + 579]; + inf->lsc.lsc_gb[i] = (otp->lsc_data.data[i * 2 + 1156] << 8) | + otp->lsc_data.data[i * 2 + 1157]; + inf->lsc.lsc_b[i] = (otp->lsc_data.data[i * 2 + 1734] << 8) | + otp->lsc_data.data[i * 2 + 1735]; + } + } + + /* pdaf */ + if (otp->pdaf_data.flag) { + inf->pdaf.flag = 1; + inf->pdaf.gainmap_width = otp->pdaf_data.gainmap_width; + inf->pdaf.gainmap_height = otp->pdaf_data.gainmap_height; + inf->pdaf.dcc_mode = otp->pdaf_data.dcc_mode; + inf->pdaf.dcc_dir = otp->pdaf_data.dcc_dir; + inf->pdaf.dccmap_width = otp->pdaf_data.dccmap_width; + inf->pdaf.dccmap_height = otp->pdaf_data.dccmap_height; + w = otp->pdaf_data.gainmap_width; + h = otp->pdaf_data.gainmap_height; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + inf->pdaf.gainmap[i * w + j] = + (otp->pdaf_data.gainmap[(i * w + j) * 2] << 8) | + otp->pdaf_data.gainmap[(i * w + j) * 2 + 1]; + } + } + w = otp->pdaf_data.dccmap_width; + h = otp->pdaf_data.dccmap_height; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + inf->pdaf.dccmap[i * w + j] = + (otp->pdaf_data.dccmap[(i * w + j) * 2] << 8) | + otp->pdaf_data.dccmap[(i * w + j) * 2 + 1]; + } + } + } + + /* af */ + if (otp->af_data.flag) { + inf->af.flag = 1; + inf->af.dir_cnt = 1; + inf->af.af_otp[0].vcm_start = otp->af_data.af_inf; + inf->af.af_otp[0].vcm_end = otp->af_data.af_macro; + inf->af.af_otp[0].vcm_dir = 0; + } + +} + +static void ov50h40_get_module_inf(struct ov50h40 *ov50h40, + struct rkmodule_inf *inf) +{ + struct otp_info *otp = ov50h40->otp; + + memset(inf, 0, sizeof(*inf)); + strscpy(inf->base.sensor, OV50H40_NAME, sizeof(inf->base.sensor)); + strscpy(inf->base.module, ov50h40->module_name, + sizeof(inf->base.module)); + strscpy(inf->base.lens, ov50h40->len_name, sizeof(inf->base.lens)); + if (otp) + ov50h40_get_otp(otp, inf); +} + +static int ov50h40_get_channel_info(struct ov50h40 *ov50h40, struct rkmodule_channel_info *ch_info) +{ + const struct ov50h40_mode *mode = ov50h40->cur_mode; + + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + + if (ch_info->index == ov50h40->spd_id && mode->spd) { + ch_info->vc = V4L2_MBUS_CSI2_CHANNEL_1; + ch_info->width = mode->spd->width; + ch_info->height = mode->spd->height; + ch_info->bus_fmt = mode->spd->bus_fmt; + ch_info->data_type = mode->spd->data_type; + ch_info->data_bit = mode->spd->data_bit; + } else { + ch_info->vc = ov50h40->cur_mode->vc[ch_info->index]; + ch_info->width = ov50h40->cur_mode->width; + ch_info->height = ov50h40->cur_mode->height; + ch_info->bus_fmt = ov50h40->cur_mode->bus_fmt; + } + return 0; +} + +static long ov50h40_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + struct rkmodule_hdr_cfg *hdr_cfg; + struct rkmodule_channel_info *ch_info; + long ret = 0; + u32 i, h, w; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_SET_HDR_CFG: + hdr_cfg = (struct rkmodule_hdr_cfg *)arg; + w = ov50h40->cur_mode->width; + h = ov50h40->cur_mode->height; + for (i = 0; i < ov50h40->cfg_num; i++) { + if (w == ov50h40->support_modes[i].width && + h == ov50h40->support_modes[i].height && + ov50h40->support_modes[i].hdr_mode == hdr_cfg->hdr_mode) { + ov50h40->cur_mode = &ov50h40->support_modes[i]; + break; + } + } + if (i == ov50h40->cfg_num) { + dev_err(&ov50h40->client->dev, + "not find hdr mode:%d %dx%d config\n", + hdr_cfg->hdr_mode, w, h); + ret = -EINVAL; + } else { + w = ov50h40->cur_mode->hts_def - ov50h40->cur_mode->width; + h = ov50h40->cur_mode->vts_def - ov50h40->cur_mode->height; + __v4l2_ctrl_modify_range(ov50h40->hblank, w, w, 1, w); + __v4l2_ctrl_modify_range(ov50h40->vblank, h, + OV50H40_VTS_MAX - ov50h40->cur_mode->height, + 1, h); + dev_info(&ov50h40->client->dev, + "sensor mode: %d\n", + ov50h40->cur_mode->hdr_mode); + } + dev_info(&ov50h40->client->dev, "%s: matched mode index(%d)", + __func__, i); + break; + case RKMODULE_GET_MODULE_INFO: + ov50h40_get_module_inf(ov50h40, (struct rkmodule_inf *)arg); + break; + case RKMODULE_GET_HDR_CFG: + hdr_cfg = (struct rkmodule_hdr_cfg *)arg; + hdr_cfg->esp.mode = HDR_NORMAL_VC; + hdr_cfg->hdr_mode = ov50h40->cur_mode->hdr_mode; + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = ov50h40_write_reg(ov50h40->client, OV50H40_REG_CTRL_MODE, + OV50H40_REG_VALUE_08BIT, OV50H40_MODE_STREAMING); + else + ret = ov50h40_write_reg(ov50h40->client, OV50H40_REG_CTRL_MODE, + OV50H40_REG_VALUE_08BIT, OV50H40_MODE_SW_STANDBY); + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = ov50h40_get_channel_info(ov50h40, ch_info); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long ov50h40_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; + struct rkmodule_hdr_cfg *hdr; + struct rkmodule_channel_info *ch_info; + long ret; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = ov50h40_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 = ov50h40_ioctl(sd, cmd, cfg); + else + ret = -EFAULT; + kfree(cfg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = ov50h40_ioctl(sd, cmd, hdr); + if (!ret) { + ret = copy_to_user(up, hdr, sizeof(*hdr)); + if (ret) + ret = -EFAULT; + } + kfree(hdr); + break; + case RKMODULE_SET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(hdr, up, sizeof(*hdr)); + if (!ret) + ret = ov50h40_ioctl(sd, cmd, hdr); + else + ret = -EFAULT; + kfree(hdr); + break; + case RKMODULE_SET_QUICK_STREAM: + ret = copy_from_user(&stream, up, sizeof(u32)); + if (!ret) + ret = ov50h40_ioctl(sd, cmd, &stream); + else + ret = -EFAULT; + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); + if (!ch_info) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(ch_info, up, sizeof(*ch_info)); + if (ret) { + ret = -EFAULT; + return ret; + } + + ret = ov50h40_ioctl(sd, cmd, ch_info); + if (!ret) { + ret = copy_to_user(up, ch_info, sizeof(*ch_info)); + if (ret) + ret = -EFAULT; + } + kfree(ch_info); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __ov50h40_start_stream(struct ov50h40 *ov50h40) +{ + int ret; + + if (!ov50h40->is_thunderboot) { + ret = ov50h40_write_array(ov50h40->client, ov50h40->cur_mode->reg_list); + if (ret) + return ret; + } + + /* In case these controls are set before streaming */ + ret = __v4l2_ctrl_handler_setup(&ov50h40->ctrl_handler); + if (ret) + return ret; + + return ov50h40_write_reg(ov50h40->client, OV50H40_REG_CTRL_MODE, + OV50H40_REG_VALUE_08BIT, OV50H40_MODE_STREAMING); +} + +static int __ov50h40_stop_stream(struct ov50h40 *ov50h40) +{ + if (ov50h40->is_thunderboot) + ov50h40->is_first_streamoff = true; + return ov50h40_write_reg(ov50h40->client, OV50H40_REG_CTRL_MODE, + OV50H40_REG_VALUE_08BIT, OV50H40_MODE_SW_STANDBY); +} + +static int ov50h40_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + struct i2c_client *client = ov50h40->client; + int ret = 0; + + dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on, + ov50h40->cur_mode->width, + ov50h40->cur_mode->height, + DIV_ROUND_CLOSEST(ov50h40->cur_mode->max_fps.denominator, + ov50h40->cur_mode->max_fps.numerator)); + + mutex_lock(&ov50h40->mutex); + on = !!on; + if (on == ov50h40->streaming) + goto unlock_and_return; + + if (on) { + if (ov50h40->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) { + ov50h40->is_thunderboot = false; + __ov50h40_power_on(ov50h40); + } + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __ov50h40_start_stream(ov50h40); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __ov50h40_stop_stream(ov50h40); + pm_runtime_put(&client->dev); + } + + ov50h40->streaming = on; + +unlock_and_return: + mutex_unlock(&ov50h40->mutex); + + return ret; +} + +static int ov50h40_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + struct i2c_client *client = ov50h40->client; + int ret = 0; + + mutex_lock(&ov50h40->mutex); + + /* If the power state is not modified - no work to do. */ + if (ov50h40->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; + } + + if (!ov50h40->is_thunderboot) { + ret |= ov50h40_write_reg(ov50h40->client, + OV50H40_SOFTWARE_RESET_REG, + OV50H40_REG_VALUE_08BIT, + 0x01); + usleep_range(100, 200); + } + + ov50h40->power_on = true; + } else { + pm_runtime_put(&client->dev); + ov50h40->power_on = false; + } + +unlock_and_return: + mutex_unlock(&ov50h40->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ov50h40_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, OV50H40_XVCLK_FREQ / 1000 / 1000); +} + +static int __ov50h40_power_on(struct ov50h40 *ov50h40) +{ + int ret; + u32 delay_us; + struct device *dev = &ov50h40->client->dev; + + if (ov50h40->is_thunderboot) + return 0; + + if (!IS_ERR_OR_NULL(ov50h40->pins_default)) { + ret = pinctrl_select_state(ov50h40->pinctrl, + ov50h40->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(ov50h40->xvclk, OV50H40_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(ov50h40->xvclk) != OV50H40_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(ov50h40->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(ov50h40->reset_gpio)) + gpiod_direction_output(ov50h40->reset_gpio, 1); + + ret = regulator_bulk_enable(OV50H40_NUM_SUPPLIES, ov50h40->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(ov50h40->reset_gpio)) + gpiod_direction_output(ov50h40->reset_gpio, 0); + + usleep_range(500, 1000); + if (!IS_ERR(ov50h40->pwdn_gpio)) + gpiod_direction_output(ov50h40->pwdn_gpio, 0); + /* + * There is no need to wait for the delay of RC circuit + * if the reset signal is directly controlled by GPIO. + */ + if (!IS_ERR(ov50h40->reset_gpio)) + usleep_range(8000, 10000); + else + usleep_range(12000, 16000); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = ov50h40_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(ov50h40->xvclk); + + return ret; +} + +static void __ov50h40_power_off(struct ov50h40 *ov50h40) +{ + int ret; + struct device *dev = &ov50h40->client->dev; + + if (ov50h40->is_thunderboot) { + if (ov50h40->is_first_streamoff) { + ov50h40->is_thunderboot = false; + ov50h40->is_first_streamoff = false; + } else { + return; + } + } + + if (!IS_ERR(ov50h40->pwdn_gpio)) + gpiod_direction_output(ov50h40->pwdn_gpio, 1); + + clk_disable_unprepare(ov50h40->xvclk); + + if (!IS_ERR(ov50h40->reset_gpio)) + gpiod_direction_output(ov50h40->reset_gpio, 1); + if (!IS_ERR_OR_NULL(ov50h40->pins_sleep)) { + ret = pinctrl_select_state(ov50h40->pinctrl, + ov50h40->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + + if (ov50h40->is_thunderboot_ng) { + ov50h40->is_thunderboot_ng = false; + } + regulator_bulk_disable(OV50H40_NUM_SUPPLIES, ov50h40->supplies); +} + +static int __maybe_unused ov50h40_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov50h40 *ov50h40 = to_ov50h40(sd); + + return __ov50h40_power_on(ov50h40); +} + +static int __maybe_unused ov50h40_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov50h40 *ov50h40 = to_ov50h40(sd); + + __ov50h40_power_off(ov50h40); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int ov50h40_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct ov50h40_mode *def_mode = &ov50h40->support_modes[0]; + + mutex_lock(&ov50h40->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = def_mode->bus_fmt; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&ov50h40->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int ov50h40_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + + if (fie->index >= ov50h40->cfg_num) + return -EINVAL; + + fie->code = ov50h40->support_modes[fie->index].bus_fmt; + fie->width = ov50h40->support_modes[fie->index].width; + fie->height = ov50h40->support_modes[fie->index].height; + fie->interval = ov50h40->support_modes[fie->index].max_fps; + fie->reserved[0] = ov50h40->support_modes[fie->index].hdr_mode; + return 0; +} +//#define RK356X_TEST +#ifdef RK356X_TEST +#define CROP_START(SRC, DST) (((SRC) - (DST)) / 2 / 4 * 4) +#define DST_WIDTH 4096 +#define DST_HEIGHT 2304 +static int ov50h40_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + sel->r.left = CROP_START(ov50h40->cur_mode->width, DST_WIDTH); + sel->r.width = DST_WIDTH; + sel->r.top = CROP_START(ov50h40->cur_mode->height, DST_HEIGHT); + sel->r.height = DST_HEIGHT; + return 0; + } + return -EINVAL; +} +#endif + +static const struct dev_pm_ops ov50h40_pm_ops = { + SET_RUNTIME_PM_OPS(ov50h40_runtime_suspend, + ov50h40_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops ov50h40_internal_ops = { + .open = ov50h40_open, +}; +#endif + +static const struct v4l2_subdev_core_ops ov50h40_core_ops = { + .s_power = ov50h40_s_power, + .ioctl = ov50h40_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov50h40_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops ov50h40_video_ops = { + .s_stream = ov50h40_s_stream, + .g_frame_interval = ov50h40_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops ov50h40_pad_ops = { + .enum_mbus_code = ov50h40_enum_mbus_code, + .enum_frame_size = ov50h40_enum_frame_sizes, + .enum_frame_interval = ov50h40_enum_frame_interval, + .get_fmt = ov50h40_get_fmt, + .set_fmt = ov50h40_set_fmt, +#ifdef RK356X_TEST + .get_selection = ov50h40_get_selection, +#endif + .get_mbus_config = ov50h40_g_mbus_config, +}; + +static const struct v4l2_subdev_ops ov50h40_subdev_ops = { + .core = &ov50h40_core_ops, + .video = &ov50h40_video_ops, + .pad = &ov50h40_pad_ops, +}; + +static int ov50h40_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov50h40 *ov50h40 = container_of(ctrl->handler, + struct ov50h40, ctrl_handler); + struct i2c_client *client = ov50h40->client; + s64 max; + int ret = 0; + u32 again, dgain; + u32 val = 0; + u32 vts = 0; + u32 exp = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + if (ov50h40->cur_mode->height == 6144) + max = ov50h40->cur_mode->height + ctrl->val - 44; + else + max = ov50h40->cur_mode->height + ctrl->val - 22; + __v4l2_ctrl_modify_range(ov50h40->exposure, + ov50h40->exposure->minimum, max, + ov50h40->exposure->step, + ov50h40->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if (ov50h40->cur_mode->height == 6144) + exp = ctrl->val / 2; + else + exp = ctrl->val; + ret = ov50h40_write_reg(ov50h40->client, + OV50H40_REG_EXP_LONG_H, + OV50H40_REG_VALUE_24BIT, + exp); + dev_dbg(&client->dev, "set exposure 0x%x\n", + ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + if (ctrl->val > 1984) { + dgain = ctrl->val * 1024 / 1984; + again = 1984; + } else { + dgain = 1024; + again = ctrl->val; + } + ret = ov50h40_write_reg(ov50h40->client, + OV50H40_REG_AGAIN_LONG_H, + OV50H40_REG_VALUE_16BIT, + (again << 1) & 0x7ffe); + ret |= ov50h40_write_reg(ov50h40->client, + OV50H40_REG_DGAIN_LONG_H, + OV50H40_REG_VALUE_24BIT, + (dgain << 6) & 0xfffc0); + dev_dbg(&client->dev, "set analog gain 0x%x\n", + ctrl->val); + break; + case V4L2_CID_VBLANK: + vts = ctrl->val + ov50h40->cur_mode->height; + if (ov50h40->cur_mode->height == 6144) + vts /= 2; + ret = ov50h40_write_reg(ov50h40->client, OV50H40_REG_VTS, + OV50H40_REG_VALUE_16BIT, + vts); + dev_dbg(&client->dev, "set vblank 0x%x\n", + ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov50h40_enable_test_pattern(ov50h40, ctrl->val); + break; + case V4L2_CID_HFLIP: + ret = ov50h40_read_reg(ov50h40->client, OV50H40_MIRROR_REG, + OV50H40_REG_VALUE_08BIT, + &val); + if (ctrl->val) + val |= FLIP_BIT_MASK; + else + val &= ~FLIP_BIT_MASK; + ret = ov50h40_write_reg(ov50h40->client, OV50H40_MIRROR_REG, + OV50H40_REG_VALUE_08BIT, + val); + break; + case V4L2_CID_VFLIP: + ret = ov50h40_read_reg(ov50h40->client, OV50H40_FLIP_REG, + OV50H40_REG_VALUE_08BIT, + &val); + if (ctrl->val) + val |= FLIP_BIT_MASK; + else + val &= ~FLIP_BIT_MASK; + ret = ov50h40_write_reg(ov50h40->client, OV50H40_FLIP_REG, + OV50H40_REG_VALUE_08BIT, + 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 ov50h40_ctrl_ops = { + .s_ctrl = ov50h40_set_ctrl, +}; + +static int ov50h40_initialize_controls(struct ov50h40 *ov50h40) +{ + const struct ov50h40_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 = ov50h40->bus_cfg.bus.mipi_csi2.num_data_lanes; + + handler = &ov50h40->ctrl_handler; + mode = ov50h40->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 9); + if (ret) + return ret; + handler->lock = &ov50h40->mutex; + + ov50h40->link_freq = v4l2_ctrl_new_int_menu(handler, NULL, + V4L2_CID_LINK_FREQ, + 3, 0, link_freq_items); + + dst_pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * lane_num; + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + ov50h40->pixel_rate = v4l2_ctrl_new_std(handler, NULL, + V4L2_CID_PIXEL_RATE, + 0, PIXEL_RATE_WITH_1250M, + 1, dst_pixel_rate); + + __v4l2_ctrl_s_ctrl(ov50h40->link_freq, + mode->mipi_freq_idx); + + h_blank = mode->hts_def - mode->width; + ov50h40->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (ov50h40->hblank) + ov50h40->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + ov50h40->vblank = v4l2_ctrl_new_std(handler, &ov50h40_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + OV50H40_VTS_MAX - mode->height, + 1, vblank_def); + if (mode->height == 6144) + exposure_max = mode->vts_def - 44; + else + exposure_max = mode->vts_def - 22; + ov50h40->exposure = v4l2_ctrl_new_std(handler, &ov50h40_ctrl_ops, + V4L2_CID_EXPOSURE, OV50H40_EXPOSURE_MIN, + exposure_max, OV50H40_EXPOSURE_STEP, + mode->exp_def); + + ov50h40->anal_gain = v4l2_ctrl_new_std(handler, &ov50h40_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, OV50H40_GAIN_MIN, + OV50H40_GAIN_MAX, OV50H40_GAIN_STEP, + OV50H40_GAIN_DEFAULT); + + ov50h40->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &ov50h40_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov50h40_test_pattern_menu) - 1, + 0, 0, ov50h40_test_pattern_menu); + + ov50h40->h_flip = v4l2_ctrl_new_std(handler, &ov50h40_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + ov50h40->v_flip = v4l2_ctrl_new_std(handler, &ov50h40_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (handler->error) { + ret = handler->error; + dev_err(&ov50h40->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + ov50h40->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int ov50h40_check_sensor_id(struct ov50h40 *ov50h40, + struct i2c_client *client) +{ + struct device *dev = &ov50h40->client->dev; + u32 id = 0; + int ret; + + if (ov50h40->is_thunderboot) { + dev_info(dev, "Enable thunderboot mode, skip sensor id check\n"); + return 0; + } + + ret = ov50h40_read_reg(client, OV50H40_REG_CHIP_ID, + OV50H40_REG_VALUE_24BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + + dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID); + + return 0; +} + +static int ov50h40_configure_regulators(struct ov50h40 *ov50h40) +{ + unsigned int i; + + for (i = 0; i < OV50H40_NUM_SUPPLIES; i++) + ov50h40->supplies[i].supply = ov50h40_supply_names[i]; + + return devm_regulator_bulk_get(&ov50h40->client->dev, + OV50H40_NUM_SUPPLIES, + ov50h40->supplies); +} + +static int ov50h40_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct ov50h40 *ov50h40; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + struct device_node *endpoint; + struct device_node *eeprom_ctrl_node; + struct i2c_client *eeprom_ctrl_client; + struct v4l2_subdev *eeprom_ctrl; + struct otp_info *otp_ptr; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + ov50h40 = devm_kzalloc(dev, sizeof(*ov50h40), GFP_KERNEL); + if (!ov50h40) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov50h40->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov50h40->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov50h40->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov50h40->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + ov50h40->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP); + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "Failed to get endpoint\n"); + return -EINVAL; + } + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), + &ov50h40->bus_cfg); + if (ov50h40->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY) { + ov50h40->support_modes = supported_modes_dphy; + ov50h40->cfg_num = ARRAY_SIZE(supported_modes_dphy); + } else { + ov50h40->support_modes = supported_modes_cphy; + ov50h40->cfg_num = ARRAY_SIZE(supported_modes_cphy); + } + + ov50h40->client = client; + ov50h40->cur_mode = &ov50h40->support_modes[0]; + + ov50h40->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ov50h40->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + ov50h40->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS); + if (IS_ERR(ov50h40->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + ov50h40->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_ASIS); + if (IS_ERR(ov50h40->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = of_property_read_u32(node, + "rockchip,spd-id", + &ov50h40->spd_id); + if (ret != 0) { + ov50h40->spd_id = PAD_MAX; + dev_err(dev, + "failed get spd_id, will not to use spd\n"); + } + + ov50h40->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(ov50h40->pinctrl)) { + ov50h40->pins_default = + pinctrl_lookup_state(ov50h40->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(ov50h40->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + ov50h40->pins_sleep = + pinctrl_lookup_state(ov50h40->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(ov50h40->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } else { + dev_err(dev, "no pinctrl\n"); + } + + ret = ov50h40_configure_regulators(ov50h40); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&ov50h40->mutex); + + sd = &ov50h40->subdev; + v4l2_i2c_subdev_init(sd, client, &ov50h40_subdev_ops); + ret = ov50h40_initialize_controls(ov50h40); + if (ret) + goto err_destroy_mutex; + ret = __ov50h40_power_on(ov50h40); + if (ret) + goto err_free_handler; + + ret = ov50h40_check_sensor_id(ov50h40, client); + if (ret) + goto err_power_off; + eeprom_ctrl_node = of_parse_phandle(node, "eeprom-ctrl", 0); + if (eeprom_ctrl_node) { + eeprom_ctrl_client = + of_find_i2c_device_by_node(eeprom_ctrl_node); + of_node_put(eeprom_ctrl_node); + if (IS_ERR_OR_NULL(eeprom_ctrl_client)) { + dev_err(dev, "can not get node\n"); + goto continue_probe; + } + eeprom_ctrl = i2c_get_clientdata(eeprom_ctrl_client); + if (IS_ERR_OR_NULL(eeprom_ctrl)) { + dev_err(dev, "can not get eeprom i2c client\n"); + } else { + otp_ptr = devm_kzalloc(dev, sizeof(*otp_ptr), GFP_KERNEL); + if (!otp_ptr) + return -ENOMEM; + ret = v4l2_subdev_call(eeprom_ctrl, + core, ioctl, 0, otp_ptr); + if (!ret) { + ov50h40->otp = otp_ptr; + } else { + ov50h40->otp = NULL; + devm_kfree(dev, otp_ptr); + } + } + } +continue_probe: + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &ov50h40_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + ov50h40->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov50h40->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(ov50h40->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov50h40->module_index, facing, + OV50H40_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(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); + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __ov50h40_power_off(ov50h40); +err_free_handler: + v4l2_ctrl_handler_free(&ov50h40->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ov50h40->mutex); + + return ret; +} + +static int ov50h40_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov50h40 *ov50h40 = to_ov50h40(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&ov50h40->ctrl_handler); + mutex_destroy(&ov50h40->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __ov50h40_power_off(ov50h40); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov50h40_of_match[] = { + { .compatible = "ovti,ov50h40" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ov50h40_of_match); +#endif + +static const struct i2c_device_id ov50h40_match_id[] = { + { "ovti,ov50h40", 0 }, + { }, +}; + +static struct i2c_driver ov50h40_i2c_driver = { + .driver = { + .name = OV50H40_NAME, + .pm = &ov50h40_pm_ops, + .of_match_table = of_match_ptr(ov50h40_of_match), + }, + .probe = &ov50h40_probe, + .remove = &ov50h40_remove, + .id_table = ov50h40_match_id, +}; + +#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT +module_i2c_driver(ov50h40_i2c_driver); +#else +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&ov50h40_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&ov50h40_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); +#endif + +MODULE_DESCRIPTION("OmniVision ov50h40 sensor driver"); +MODULE_LICENSE("GPL v2"); From 6891ecde216e6ae208d753d1f89b48275ff5dd4f Mon Sep 17 00:00:00 2001 From: ShingoXY <36981700+shingoxy@users.noreply.github.com> Date: Fri, 30 Aug 2024 10:41:53 +0800 Subject: [PATCH 13/13] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 658a84229acc8..c40979172849f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# 20240830:增加OV50H40.c,CPHY接口CAM0,验证中; + # 20240812:增加GC8613.c,成功点亮,工作正常,但无rkaiq文件; # 20240710:RKAIQ正常工作,3AOK,但iqfile为copy os04a10的,颜色、LSC、AWB均不正常;