From 18c6b8bc210f4fce35dd22b943cc52121b6ee912 Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Fri, 1 Mar 2024 14:22:53 +0800 Subject: [PATCH 01/18] media: i2c: add ox03c10 sensor driver Signed-off-by: Zefa Chen Change-Id: Iafc364cb36febaf46888f55037c0ba44e834373e --- drivers/media/i2c/Kconfig | 13 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ox03c10.c | 5597 +++++++++++++++++++++++++++++++++++ 3 files changed, 5611 insertions(+) create mode 100644 drivers/media/i2c/ox03c10.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 80fae6af6de2..581dee3af592 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1676,6 +1676,19 @@ config VIDEO_OV9734 To compile this driver as a module, choose M here: the module's name is ov9734. +config VIDEO_OX03C10 + tristate "OmniVision OX03C10 sensor support" + depends on VIDEO_DEV && I2C + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OX03C10 camera. + + To compile this driver as a module, choose M here: the + module's name is OX03C10. + config VIDEO_PREISP_DUMMY_SENSOR tristate "Preisp dummy sensor support" depends on VIDEO_DEV && I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 92e47e5330d4..c4c9de6adda0 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -207,6 +207,7 @@ obj-$(CONFIG_VIDEO_OV9282) += ov9282.o obj-$(CONFIG_VIDEO_OV9640) += ov9640.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_OV9734) += ov9734.o +obj-$(CONFIG_VIDEO_OX03C10) += ox03c10.o obj-$(CONFIG_VIDEO_PREISP_DUMMY_SENSOR) += preisp-dummy.o obj-$(CONFIG_VIDEO_PS5458) += ps5458.o obj-$(CONFIG_VIDEO_RDACM20) += rdacm20.o diff --git a/drivers/media/i2c/ox03c10.c b/drivers/media/i2c/ox03c10.c new file mode 100644 index 000000000000..e03cdef88380 --- /dev/null +++ b/drivers/media/i2c/ox03c10.c @@ -0,0 +1,5597 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ox03c10 driver + * + * Copyright (C) 2023 Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 first version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x01) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define OX03C10_LANES 4 +#define OX03C10_BITS_PER_SAMPLE 12 +#define OX03C10_LINK_FREQ_300MHZ 300000000 +#define OX03C10_LINK_FREQ_480MHZ 480000000 +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define OX03C10_PIXEL_RATE_300 (OX03C10_LINK_FREQ_300MHZ / OX03C10_BITS_PER_SAMPLE \ + * 2 * OX03C10_LANES) +#define OX03C10_PIXEL_RATE_480 (OX03C10_LINK_FREQ_480MHZ / OX03C10_BITS_PER_SAMPLE \ + * 2 * OX03C10_LANES) +#define OX03C10_PIXEL_RATE_MAX OX03C10_PIXEL_RATE_480 + +#define OX03C10_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x005803 +#define OX03C10_REG_CHIP_ID 0x300a + +#define OX03C10_REG_CTRL_MODE 0x0100 +#define OX03C10_MODE_SW_STANDBY 0x0 +#define OX03C10_MODE_STREAMING BIT(0) + +#define OX03C10_VTS_MAX 0x7fff + +#define OX03C10_GAIN_MIN 0x10 +#define OX03C10_GAIN_MAX 0xf7f +#define OX03C10_GAIN_STEP 0x01 +#define OX03C10_GAIN_DEFAULT 0x10 +// exposure ctrl reg for DCG +#define OX03C10_EXPOSURE_HCG_MIN 4 +#define OX03C10_EXPOSURE_HCG_STEP 1 +#define OX03C10_REG_EXPOSURE_DCG_H 0x3501 // bit[7:0] for exposure[15:8] +#define OX03C10_REG_EXPOSURE_DCG_L 0x3502 // bit[7:0] for exposure[7:0] +// gain ctrl reg for HCG +#define OX03C10_REG_AGAIN_HCG_H 0x3508 // bit[3:0] for RealGain[7:4] +#define OX03C10_REG_AGAIN_HCG_L 0x3509 // bit[7:4] for RealGain[3:0] +#define OX03C10_REG_DGAIN_HCG_H 0x350A // bit[3:0] for DigitalGain[13:10] +#define OX03C10_REG_DGAIN_HCG_M 0x350B // bit[7:0] for DigitalGain[9:2] +#define OX03C10_REG_DGAIN_HCG_L 0x350C // bit[7:6] for DigitalGain[1:0] +// gain ctrl reg for LCG +#define OX03C10_REG_AGAIN_LCG_H 0x3588 // bit[3:0] for RealGain[7:4] +#define OX03C10_REG_AGAIN_LCG_L 0x3589 // bit[7:4] for RealGain[3:0] +#define OX03C10_REG_DGAIN_LCG_H 0x358A // bit[3:0] for DigitalGain[13:10] +#define OX03C10_REG_DGAIN_LCG_M 0x358B // bit[7:0] for DigitalGain[9:2] +#define OX03C10_REG_DGAIN_LCG_L 0x358C // bit[7:6] for DigitalGain[1:0] +// exposure and gain ctrl reg for SPD +#define OX03C10_EXPOSURE_SPD_MIN 4 +#define OX03C10_EXPOSURE_SPD_STEP 1 +#define OX03C10_REG_EXPOSURE_SPD_H 0x3541 // bit[7:0] for exposure[15:8] +#define OX03C10_REG_EXPOSURE_SPD_L 0x3542 // bit[7:0] for exposure[7:0] +#define OX03C10_REG_AGAIN_SPD_H 0x3548 // bit[3:0] for RealGain[7:4] +#define OX03C10_REG_AGAIN_SPD_L 0x3549 // bit[7:4] for RealGain[3:0] +#define OX03C10_REG_DGAIN_SPD_H 0x354A // bit[3:0] for DigitalGain[13:10] +#define OX03C10_REG_DGAIN_SPD_M 0x354B // bit[7:0] for DigitalGain[9:2] +#define OX03C10_REG_DGAIN_SPD_L 0x354C // bit[7:6] for DigitalGain[1:0] +// exposure and gain ctrl reg for VS +#define OX03C10_EXPOSURE_VS_MIN 4 +#define OX03C10_EXPOSURE_VS_STEP 1 +#define OX03C10_REG_EXPOSURE_VS_H 0x35C1 // bit[7:0] for exposure[15:8] +#define OX03C10_REG_EXPOSURE_VS_L 0x35C2 // bit[7:0] for exposure[7:0] +#define OX03C10_REG_AGAIN_VS_H 0x35C8 // bit[3:0] for RealGain[7:4] +#define OX03C10_REG_AGAIN_VS_L 0x35C9 // bit[7:4] for RealGain[3:0] +#define OX03C10_REG_DGAIN_VS_H 0x35CA // bit[3:0] for DigitalGain[13:10] +#define OX03C10_REG_DGAIN_VS_M 0x35CB // bit[7:0] for DigitalGain[9:2] +#define OX03C10_REG_DGAIN_VS_L 0x35CC // bit[7:6] for DigitalGain[1:0] + +#define OX03C10_GROUP_UPDATE_ADDRESS 0x3208 +#define OX03C10_GROUP_UPDATE_START_DATA 0x00 +#define OX03C10_GROUP_UPDATE_END_DATA 0x10 +#define OX03C10_GROUP_UPDATE_LAUNCH 0xA0 + +#define OX03C10_GROUP1_UPDATE_START_DATA 0x01 +#define OX03C10_GROUP1_UPDATE_END_DATA 0x11 +#define OX03C10_GROUP1_UPDATE_LAUNCH 0xA1 + +#define OX03C10_REG_TEST_PATTERN 0x5040 +#define OX03C10_TEST_PATTERN_ENABLE 0x80 +#define OX03C10_TEST_PATTERN_DISABLE 0x0 + +#define OX03C10_REG_VTS 0x380e + +#define OX03C10_REG_HCG_B_GAIN 0x5280 +#define OX03C10_REG_HCG_GB_GAIN 0x5282 +#define OX03C10_REG_HCG_GR_GAIN 0x5284 +#define OX03C10_REG_HCG_R_GAIN 0x5286 + +#define OX03C10_REG_LCG_B_GAIN 0x5480 +#define OX03C10_REG_LCG_GB_GAIN 0x5482 +#define OX03C10_REG_LCG_GR_GAIN 0x5484 +#define OX03C10_REG_LCG_R_GAIN 0x5486 + +#define OX03C10_REG_SPD_B_GAIN 0x5680 +#define OX03C10_REG_SPD_GB_GAIN 0x5682 +#define OX03C10_REG_SPD_GR_GAIN 0x5684 +#define OX03C10_REG_SPD_R_GAIN 0x5686 + +#define OX03C10_REG_VS_B_GAIN 0x5880 +#define OX03C10_REG_VS_GB_GAIN 0x5882 +#define OX03C10_REG_VS_GR_GAIN 0x5884 +#define OX03C10_REG_VS_R_GAIN 0x5886 + +#define OX03C10_REG_HCG_BLC 0x4026 +#define OX03C10_REG_LCG_BLC 0x4028 +#define OX03C10_REG_SPD_BLC 0x402A +#define OX03C10_REG_VS_BLC 0x402C + +#define OX03C10_VFLIP_REG 0x3820 +#define MIRROR_BIT_MASK (BIT(5)) +#define FLIP_BIT_MASK (BIT(2)) + +#define REG_NULL 0xFFFF +#define DELAY_MS 0xEEEE + +#define OX03C10_REG_VALUE_08BIT 1 +#define OX03C10_REG_VALUE_16BIT 2 +#define OX03C10_REG_VALUE_24BIT 3 + +#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 OX03C10_NAME "ox03c10" + +static const char * const ox03c10_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define OX03C10_NUM_SUPPLIES ARRAY_SIZE(ox03c10_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +enum ox03c10_hdr_operating_mode { + OX03C10_HDR3_DCG_VS_12BIT = 0, + OX03C10_HDR3_DCG_SPD_12BIT = 1, + OX03C10_HDR3_DCG_VS_LFM_16BIT = 2, + OX03C10_HDR_OPERATING_MODE_COUNT, +}; + +struct ox03c10_mode { + u32 bus_fmt; + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + u32 bpp; + u32 mipi_freq_idx; + const struct regval *reg_list; + u32 hdr_mode; + struct rkmodule_hdr_compr *hdr_compr; + u32 hdr_operating_mode; + u32 hdr_ratio; + u32 vc[PAD_MAX]; + u32 exp_mode; +}; + +struct ox03c10 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[OX03C10_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *test_pattern; + struct v4l2_ctrl *operating_mode_ctrl; + struct v4l2_ctrl *hdr_ratio; + struct mutex mutex; + bool streaming; + bool power_on; + const struct ox03c10_mode *cur_mode; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + bool has_init_exp; + struct preisp_hdrae_exp_s init_hdrae_exp; + struct rkmodule_dcg_ratio dcg_ratio; + struct rkmodule_dcg_ratio spd_ratio; +}; + +#define to_ox03c10(sd) container_of(sd, struct ox03c10, subdev) + +static const struct regval ox03c10_1920x1080_30fps_HDR3_SPD_PWL12_mipi600[] = { + {0x0103, 0x01}, + {0x0107, 0x01}, + {DELAY_MS, 6}, + {0x4d5a, 0x1C}, + {0x4d09, 0xff}, + {0x4d09, 0xdf}, + {0x3208, 0x04}, + {0x4620, 0x04}, + {0x3208, 0x14}, + {0x3208, 0x05}, + {0x4620, 0x04}, + {0x3208, 0x15}, + {0x3208, 0x02}, + {0x3507, 0x00}, + {0x3208, 0x12}, + {0x3208, 0xa2}, + {0x0301, 0xc8}, + {0x0303, 0x01}, + {0x0304, 0x01}, + {0x0305, 0x2c}, + {0x0306, 0x04}, + {0x0307, 0x03}, + {0x0316, 0x00}, + {0x0317, 0x00}, + {0x0318, 0x00}, + {0x0323, 0x05}, + {0x0324, 0x01}, + {0x0325, 0x2c}, + {0x032e, 0x00}, + {0x032a, 0x04}, + {0x0326, 0x09}, + {0x0327, 0x04}, + {0x0331, 0x04}, + {0x0400, 0xe0}, + {0x0401, 0x80}, + {0x0403, 0xde}, + {0x0404, 0x34}, + {0x0405, 0x3b}, + {0x0406, 0xd4}, + {0x0407, 0x08}, + {0x0408, 0xe0}, + {0x0409, 0x62}, + {0x040a, 0xde}, + {0x040b, 0x34}, + {0x040c, 0x8f}, + {0x040d, 0x9b}, + {0x040e, 0x08}, + {0x2803, 0xfe}, + {0x280b, 0x00}, + {0x280c, 0x79}, + {0x3001, 0x03}, + {0x3002, 0xf8}, + {0x3005, 0x80}, + {0x3007, 0x01}, + {0x3008, 0x80}, + {0x3012, 0x41}, + {0x3020, 0x05}, + {0x3700, 0x28}, + {0x3701, 0x15}, + {0x3702, 0x19}, + {0x3703, 0x23}, + {0x3704, 0x0a}, + {0x3705, 0x00}, + {0x3706, 0x3e}, + {0x3707, 0x0d}, + {0x3708, 0x50}, + {0x3709, 0x5a}, + {0x370a, 0x00}, + {0x370b, 0x96}, + {0x3711, 0x11}, + {0x3712, 0x13}, + {0x3717, 0x02}, + {0x3718, 0x73}, + {0x372c, 0x40}, + {0x3733, 0x01}, + {0x3738, 0x36}, + {0x3739, 0x36}, + {0x373a, 0x25}, + {0x373b, 0x25}, + {0x373f, 0x21}, + {0x3740, 0x21}, + {0x3741, 0x21}, + {0x3742, 0x21}, + {0x3747, 0x28}, + {0x3748, 0x28}, + {0x3749, 0x19}, + {0x3755, 0x1a}, + {0x3756, 0x0a}, + {0x3757, 0x1c}, + {0x3765, 0x19}, + {0x3766, 0x05}, + {0x3767, 0x05}, + {0x3768, 0x13}, + {0x376c, 0x07}, + {0x3778, 0x20}, + {0x377c, 0xc8}, + {0x3781, 0x02}, + {0x3783, 0x02}, + {0x379c, 0x58}, + {0x379e, 0x00}, + {0x379f, 0x00}, + {0x37a0, 0x00}, + {0x37bc, 0x22}, + {0x37c0, 0x01}, + {0x37c4, 0x3e}, + {0x37c5, 0x3e}, + {0x37c6, 0x2a}, + {0x37c7, 0x28}, + {0x37c8, 0x02}, + {0x37c9, 0x12}, + {0x37cb, 0x29}, + {0x37cd, 0x29}, + {0x37d2, 0x00}, + {0x37d3, 0x73}, + {0x37d6, 0x00}, + {0x37d7, 0x6b}, + {0x37dc, 0x00}, + {0x37df, 0x54}, + {0x37e2, 0x00}, + {0x37e3, 0x00}, + {0x37f8, 0x00}, + {0x37f9, 0x01}, + {0x37fa, 0x00}, + {0x37fb, 0x19}, + {0x3c03, 0x01}, + {0x3c04, 0x01}, + {0x3c06, 0x21}, + {0x3c08, 0x01}, + {0x3c09, 0x01}, + {0x3c0a, 0x01}, + {0x3c0b, 0x21}, + {0x3c13, 0x21}, + {0x3c14, 0x82}, + {0x3c16, 0x13}, + {0x3c21, 0x00}, + {0x3c22, 0xf3}, + {0x3c37, 0x12}, + {0x3c38, 0x31}, + {0x3c3c, 0x00}, + {0x3c3d, 0x03}, + {0x3c44, 0x16}, + {0x3c5c, 0x8a}, + {0x3c5f, 0x03}, + {0x3c61, 0x80}, + {0x3c6f, 0x2b}, + {0x3c70, 0x5f}, + {0x3c71, 0x2c}, + {0x3c72, 0x2c}, + {0x3c73, 0x2c}, + {0x3c76, 0x12}, + {0x3182, 0x12}, + {0x320e, 0x00}, + {0x320f, 0x00}, + {0x3211, 0x61}, + {0x3215, 0xcd}, + {0x3219, 0x08}, + {0x3506, 0x20}, + {0x350a, 0x01}, + {0x350b, 0x00}, + {0x350c, 0x00}, + {0x3586, 0x30}, + {0x358a, 0x01}, + {0x358b, 0x00}, + {0x358c, 0x00}, + {0x3541, 0x00}, + {0x3542, 0x04}, + {0x3546, 0x10}, + {0x3548, 0x04}, + {0x3549, 0x40}, + {0x354a, 0x01}, + {0x354b, 0x00}, + {0x354c, 0x00}, + {0x35c1, 0x00}, + {0x35c2, 0x02}, + {0x35c6, 0x90}, + {0x3600, 0x8f}, + {0x3605, 0x16}, + {0x3609, 0xf0}, + {0x360a, 0x01}, + {0x360e, 0x1d}, + {0x360f, 0x10}, + {0x3610, 0x70}, + {0x3611, 0x3a}, + {0x3612, 0x28}, + {0x361a, 0x29}, + {0x361b, 0x6c}, + {0x361c, 0x0b}, + {0x361d, 0x00}, + {0x361e, 0xfc}, + {0x362a, 0x00}, + {0x364d, 0x0f}, + {0x364e, 0x18}, + {0x364f, 0x12}, + {0x3653, 0x1c}, + {0x3654, 0x00}, + {0x3655, 0x1f}, + {0x3656, 0x1f}, + {0x3657, 0x0c}, + {0x3658, 0x0a}, + {0x3659, 0x14}, + {0x365a, 0x18}, + {0x365b, 0x14}, + {0x365c, 0x10}, + {0x365e, 0x12}, + {0x3674, 0x08}, + {0x3677, 0x3a}, + {0x3678, 0x3a}, + {0x3679, 0x19}, + {0x3802, 0x00}, + {0x3803, 0x04}, + {0x3806, 0x05}, + {0x3807, 0x0b}, + {0x3808, 0x07}, + {0x3809, 0x80}, + {0x380a, 0x04}, + {0x380b, 0x38}, + {0x380c, 0x08}, + {0x380d, 0x8e}, + {0x380e, 0x02}, + {0x380f, 0xae}, + {0x3810, 0x00}, + {0x3811, 0x08}, + {0x3812, 0x00}, + {0x3813, 0x68}, + {0x3816, 0x01}, + {0x3817, 0x01}, + {0x381c, 0x18}, + {0x381e, 0x01}, + {0x381f, 0x01}, + {0x3820, 0x00}, + {0x3821, 0x19}, + {0x3832, 0x00}, + {0x3834, 0x00}, + {0x384c, 0x04}, + {0x384d, 0x1a}, + {0x3850, 0x00}, + {0x3851, 0x42}, + {0x3852, 0x00}, + {0x3853, 0x40}, + {0x3858, 0x04}, + {0x388c, 0x04}, + {0x388d, 0x56}, + {0x3b40, 0x05}, + {0x3b41, 0x40}, + {0x3b42, 0x00}, + {0x3b43, 0x90}, + {0x3b44, 0x00}, + {0x3b45, 0x20}, + {0x3b46, 0x00}, + {0x3b47, 0x20}, + {0x3b48, 0x19}, + {0x3b49, 0x12}, + {0x3b4a, 0x16}, + {0x3b4b, 0x2e}, + {0x3b4c, 0x00}, + {0x3b4d, 0x00}, + {0x3b86, 0x00}, + {0x3b87, 0x34}, + {0x3b88, 0x00}, + {0x3b89, 0x08}, + {0x3b8a, 0x05}, + {0x3b8b, 0x00}, + {0x3b8c, 0x07}, + {0x3b8d, 0x80}, + {0x3b8e, 0x00}, + {0x3b8f, 0x00}, + {0x3b92, 0x05}, + {0x3b93, 0x00}, + {0x3b94, 0x07}, + {0x3b95, 0x80}, + {0x3b9e, 0x09}, + {0x3d85, 0x05}, + {0x3d8a, 0x03}, + {0x3d8b, 0xff}, + {0x3d99, 0x00}, + {0x3d9a, 0x9f}, + {0x3d9b, 0x00}, + {0x3d9c, 0xa0}, + {0x3da4, 0x00}, + {0x3da7, 0x50}, + {0x420e, 0x6b}, + {0x420f, 0x6e}, + {0x4210, 0x06}, + {0x4211, 0xc1}, + {0x421e, 0x02}, + {0x421f, 0x45}, + {0x4220, 0xe1}, + {0x4221, 0x01}, + {0x4301, 0xff}, + {0x4307, 0x03}, + {0x4308, 0x13}, + {0x430a, 0x13}, + {0x430d, 0x93}, + {0x430f, 0x57}, + {0x4310, 0x95}, + {0x4311, 0x16}, + {0x4316, 0x00}, + {0x4317, 0x08}, + {0x4319, 0x03}, + {0x431a, 0x00}, + {0x431b, 0x00}, + {0x431d, 0x2a}, + {0x431e, 0x11}, + {0x431f, 0x20}, + {0x4320, 0x19}, + {0x4323, 0x80}, + {0x4324, 0x00}, + {0x4503, 0x4e}, + {0x4505, 0x00}, + {0x4509, 0x00}, + {0x450a, 0x00}, + {0x4580, 0xf8}, + {0x4583, 0x07}, + {0x4584, 0x6a}, + {0x4585, 0x08}, + {0x4586, 0x05}, + {0x4587, 0x04}, + {0x4588, 0x73}, + {0x4589, 0x05}, + {0x458a, 0x1f}, + {0x458b, 0x02}, + {0x458c, 0xdc}, + {0x458d, 0x03}, + {0x458e, 0x02}, + {0x4597, 0x07}, + {0x4598, 0x40}, + {0x4599, 0x0e}, + {0x459a, 0x0e}, + {0x459b, 0xfb}, + {0x459c, 0xf3}, + {0x4602, 0x00}, + {0x4603, 0x13}, + {0x4604, 0x00}, + {0x4609, 0x60}, + {0x460a, 0x30}, + {0x4610, 0x00}, + {0x4611, 0x40}, + {0x4612, 0x01}, + {0x4613, 0x00}, + {0x4614, 0x00}, + {0x4615, 0x40}, + {0x4616, 0x01}, + {0x4617, 0x00}, + {0x4800, 0x04}, + {0x480a, 0x22}, + {0x4813, 0xe4}, + {0x4814, 0x2a}, + {0x4837, 0x1a}, + {0x484b, 0x47}, + {0x484f, 0x00}, + {0x4887, 0x51}, + {0x4d00, 0x4a}, + {0x4d01, 0x18}, + {0x4d05, 0xff}, + {0x4d06, 0x88}, + {0x4d08, 0x63}, + {0x4d09, 0xdf}, + {0x4d15, 0x7d}, + {0x4d1a, 0x20}, + {0x4d30, 0x0a}, + {0x4d31, 0x00}, + {0x4d34, 0x7d}, + {0x4d3c, 0x7d}, + {0x4f00, 0x3f}, + {0x4f01, 0xff}, + {0x4f02, 0xff}, + {0x4f03, 0x2c}, + {0x4f04, 0xe0}, + {0x6a00, 0x00}, + {0x6a01, 0x20}, + {0x6a02, 0x00}, + {0x6a03, 0x20}, + {0x6a04, 0x02}, + {0x6a05, 0x80}, + {0x6a06, 0x01}, + {0x6a07, 0xe0}, + {0x6a08, 0xcf}, + {0x6a09, 0x01}, + {0x6a0a, 0x40}, + {0x6a20, 0x00}, + {0x6a21, 0x02}, + {0x6a22, 0x00}, + {0x6a23, 0x00}, + {0x6a24, 0x00}, + {0x6a25, 0x00}, + {0x6a26, 0x00}, + {0x6a27, 0x00}, + {0x6a28, 0x00}, + {0x5000, 0x8f}, + {0x5001, 0x75}, + {0x5002, 0x7f}, + {0x5003, 0x7a}, + {0x5004, 0x3e}, + {0x5005, 0x1e}, + {0x5006, 0x1e}, + {0x5007, 0x1e}, + {0x5008, 0x00}, + {0x500c, 0x00}, + {0x502c, 0x00}, + {0x502e, 0x00}, + {0x502f, 0x00}, + {0x504b, 0x00}, + {0x5053, 0x00}, + {0x505b, 0x00}, + {0x5063, 0x00}, + {0x5070, 0x00}, + {0x5074, 0x04}, + {0x507a, 0x04}, + {0x507b, 0x09}, + {0x5500, 0x02}, + {0x5700, 0x02}, + {0x5900, 0x02}, + {0x6007, 0x04}, + {0x6008, 0x05}, + {0x6009, 0x02}, + {0x600b, 0x08}, + {0x600c, 0x07}, + {0x600d, 0x88}, + {0x6016, 0x00}, + {0x6027, 0x04}, + {0x6028, 0x05}, + {0x6029, 0x02}, + {0x602b, 0x08}, + {0x602c, 0x07}, + {0x602d, 0x88}, + {0x6047, 0x04}, + {0x6048, 0x05}, + {0x6049, 0x02}, + {0x604b, 0x08}, + {0x604c, 0x07}, + {0x604d, 0x88}, + {0x6067, 0x04}, + {0x6068, 0x05}, + {0x6069, 0x02}, + {0x606b, 0x08}, + {0x606c, 0x07}, + {0x606d, 0x88}, + {0x6087, 0x04}, + {0x6088, 0x05}, + {0x6089, 0x02}, + {0x608b, 0x08}, + {0x608c, 0x07}, + {0x608d, 0x88}, + {0x5e00, 0x00}, + {0x5e01, 0x08}, + {0x5e02, 0x09}, + {0x5e03, 0x0a}, + {0x5e04, 0x0b}, + {0x5e05, 0x0c}, + {0x5e06, 0x0c}, + {0x5e07, 0x0c}, + {0x5e08, 0x0c}, + {0x5e09, 0x0c}, + {0x5e0a, 0x0d}, + {0x5e0b, 0x0d}, + {0x5e0c, 0x0d}, + {0x5e0d, 0x0d}, + {0x5e0e, 0x0d}, + {0x5e0f, 0x0d}, + {0x5e10, 0x0d}, + {0x5e11, 0x0d}, + {0x5e12, 0x0e}, + {0x5e13, 0x0e}, + {0x5e14, 0x0e}, + {0x5e15, 0x0e}, + {0x5e16, 0x0e}, + {0x5e17, 0x0e}, + {0x5e18, 0x0e}, + {0x5e19, 0x10}, + {0x5e1a, 0x11}, + {0x5e1b, 0x11}, + {0x5e1c, 0x12}, + {0x5e1d, 0x12}, + {0x5e1e, 0x14}, + {0x5e1f, 0x15}, + {0x5e20, 0x17}, + {0x5e21, 0x17}, + {0x5e22, 0x00}, + {0x5e23, 0x01}, + {0x5e26, 0x00}, + {0x5e27, 0x3f}, + {0x5e29, 0x00}, + {0x5e2a, 0x40}, + {0x5e2c, 0x00}, + {0x5e2d, 0x40}, + {0x5e2f, 0x00}, + {0x5e30, 0x40}, + {0x5e32, 0x00}, + {0x5e33, 0x40}, + {0x5e34, 0x00}, + {0x5e35, 0x00}, + {0x5e36, 0x40}, + {0x5e37, 0x00}, + {0x5e38, 0x00}, + {0x5e39, 0x40}, + {0x5e3a, 0x00}, + {0x5e3b, 0x00}, + {0x5e3c, 0x40}, + {0x5e3d, 0x00}, + {0x5e3e, 0x00}, + {0x5e3f, 0x40}, + {0x5e40, 0x00}, + {0x5e41, 0x00}, + {0x5e42, 0x60}, + {0x5e43, 0x00}, + {0x5e44, 0x00}, + {0x5e45, 0x60}, + {0x5e46, 0x00}, + {0x5e47, 0x00}, + {0x5e48, 0x60}, + {0x5e49, 0x00}, + {0x5e4a, 0x00}, + {0x5e4b, 0x60}, + {0x5e4c, 0x00}, + {0x5e4d, 0x00}, + {0x5e4e, 0x60}, + {0x5e50, 0x00}, + {0x5e51, 0x60}, + {0x5e53, 0x00}, + {0x5e54, 0x60}, + {0x5e56, 0x00}, + {0x5e57, 0x80}, + {0x5e59, 0x00}, + {0x5e5a, 0x80}, + {0x5e5c, 0x00}, + {0x5e5d, 0x80}, + {0x5e5f, 0x00}, + {0x5e60, 0x80}, + {0x5e62, 0x00}, + {0x5e63, 0x80}, + {0x5e65, 0x00}, + {0x5e66, 0x80}, + {0x5e68, 0x00}, + {0x5e69, 0x80}, + {0x5e6b, 0x01}, + {0x5e6c, 0x20}, + {0x5e6e, 0x01}, + {0x5e6f, 0xd0}, + {0x5e71, 0x01}, + {0x5e72, 0x30}, + {0x5e74, 0x01}, + {0x5e75, 0x80}, + {0x5e77, 0x01}, + {0x5e78, 0x00}, + {0x5e7a, 0x00}, + {0x5e7b, 0x00}, + {0x5e7d, 0x00}, + {0x5e7e, 0x00}, + {0x5e80, 0x00}, + {0x5e81, 0x00}, + {0x5e83, 0x00}, + {0x5e84, 0x00}, + {0x5f00, 0x02}, + {0x5f01, 0x08}, + {0x5f02, 0x09}, + {0x5f03, 0x0a}, + {0x5f04, 0x0b}, + {0x5f05, 0x0c}, + {0x5f06, 0x0c}, + {0x5f07, 0x0c}, + {0x5f08, 0x0c}, + {0x5f09, 0x0c}, + {0x5f0a, 0x0d}, + {0x5f0b, 0x0d}, + {0x5f0c, 0x0d}, + {0x5f0d, 0x0d}, + {0x5f0e, 0x0d}, + {0x5f0f, 0x0e}, + {0x5f10, 0x0e}, + {0x5f11, 0x0e}, + {0x5f12, 0x0e}, + {0x5f13, 0x0f}, + {0x5f14, 0x0f}, + {0x5f15, 0x10}, + {0x5f16, 0x11}, + {0x5f17, 0x11}, + {0x5f18, 0x12}, + {0x5f19, 0x12}, + {0x5f1a, 0x13}, + {0x5f1b, 0x13}, + {0x5f1c, 0x14}, + {0x5f1d, 0x14}, + {0x5f1e, 0x16}, + {0x5f1f, 0x16}, + {0x5f20, 0x16}, + {0x5f21, 0x08}, + {0x5f22, 0x00}, + {0x5f23, 0x01}, + {0x5f26, 0x02}, + {0x5f27, 0x00}, + {0x5f29, 0x02}, + {0x5f2a, 0x00}, + {0x5f2c, 0x02}, + {0x5f2d, 0x00}, + {0x5f2f, 0x02}, + {0x5f30, 0x00}, + {0x5f32, 0x02}, + {0x5f33, 0x00}, + {0x5f34, 0x00}, + {0x5f35, 0x02}, + {0x5f36, 0x00}, + {0x5f37, 0x00}, + {0x5f38, 0x02}, + {0x5f39, 0x00}, + {0x5f3a, 0x00}, + {0x5f3b, 0x02}, + {0x5f3c, 0x00}, + {0x5f3d, 0x00}, + {0x5f3e, 0x02}, + {0x5f3f, 0x00}, + {0x5f40, 0x00}, + {0x5f41, 0x02}, + {0x5f42, 0x00}, + {0x5f43, 0x00}, + {0x5f44, 0x02}, + {0x5f45, 0x00}, + {0x5f46, 0x00}, + {0x5f47, 0x04}, + {0x5f48, 0x00}, + {0x5f49, 0x00}, + {0x5f4a, 0x04}, + {0x5f4b, 0x00}, + {0x5f4c, 0x00}, + {0x5f4d, 0x04}, + {0x5f4e, 0x00}, + {0x5f50, 0x04}, + {0x5f51, 0x00}, + {0x5f53, 0x04}, + {0x5f54, 0x00}, + {0x5f56, 0x04}, + {0x5f57, 0x00}, + {0x5f59, 0x04}, + {0x5f5a, 0x00}, + {0x5f5c, 0x04}, + {0x5f5d, 0x00}, + {0x5f5f, 0x08}, + {0x5f60, 0x00}, + {0x5f62, 0x08}, + {0x5f63, 0x00}, + {0x5f65, 0x08}, + {0x5f66, 0x00}, + {0x5f68, 0x08}, + {0x5f69, 0x00}, + {0x5f6b, 0x08}, + {0x5f6c, 0x00}, + {0x5f6e, 0x10}, + {0x5f6f, 0x00}, + {0x5f71, 0x10}, + {0x5f72, 0x00}, + {0x5f74, 0x10}, + {0x5f75, 0x00}, + {0x5f77, 0x10}, + {0x5f78, 0x00}, + {0x5f7a, 0x20}, + {0x5f7b, 0x00}, + {0x5f7d, 0x20}, + {0x5f7e, 0x00}, + {0x5f80, 0x20}, + {0x5f81, 0x00}, + {0x5f83, 0x00}, + {0x5f84, 0xff}, + {0x5240, 0x0f}, + {0x5243, 0x00}, + {0x5244, 0x00}, + {0x5245, 0x00}, + {0x5246, 0x00}, + {0x5247, 0x00}, + {0x5248, 0x00}, + {0x5249, 0x00}, + {0x5440, 0x0f}, + {0x5443, 0x00}, + {0x5445, 0x00}, + {0x5447, 0x00}, + {0x5448, 0x00}, + {0x5449, 0x00}, + {0x5640, 0x0f}, + {0x5642, 0x00}, + {0x5643, 0x00}, + {0x5644, 0x00}, + {0x5645, 0x00}, + {0x5646, 0x00}, + {0x5647, 0x00}, + {0x5649, 0x00}, + {0x5840, 0x0f}, + {0x5842, 0x00}, + {0x5843, 0x00}, + {0x5845, 0x00}, + {0x5846, 0x00}, + {0x5847, 0x00}, + {0x5848, 0x00}, + {0x5849, 0x00}, + {0x4001, 0x2b}, + {0x4008, 0x02}, + {0x4009, 0x03}, + {0x4018, 0x12}, + {0x4022, 0x40}, + {0x4023, 0x20}, + {0x4026, 0x00}, + {0x4027, 0x40}, + {0x4028, 0x00}, + {0x4029, 0x40}, + {0x402a, 0x00}, + {0x402b, 0x40}, + {0x402c, 0x00}, + {0x402d, 0x40}, + {0x405e, 0x00}, + {0x405f, 0x00}, + {0x4060, 0x00}, + {0x4061, 0x00}, + {0x4062, 0x00}, + {0x4063, 0x00}, + {0x4064, 0x00}, + {0x4065, 0x00}, + {0x4066, 0x00}, + {0x4067, 0x00}, + {0x4068, 0x00}, + {0x4069, 0x00}, + {0x406a, 0x00}, + {0x406b, 0x00}, + {0x406c, 0x00}, + {0x406d, 0x00}, + {0x406e, 0x00}, + {0x406f, 0x00}, + {0x4070, 0x00}, + {0x4071, 0x00}, + {0x4072, 0x00}, + {0x4073, 0x00}, + {0x4074, 0x00}, + {0x4075, 0x00}, + {0x4076, 0x00}, + {0x4077, 0x00}, + {0x4078, 0x00}, + {0x4079, 0x00}, + {0x407a, 0x00}, + {0x407b, 0x00}, + {0x407c, 0x00}, + {0x407d, 0x00}, + {0x407e, 0xcc}, + {0x407f, 0x18}, + {0x4080, 0xff}, + {0x4081, 0xff}, + {0x4082, 0x01}, + {0x4083, 0x53}, + {0x4084, 0x01}, + {0x4085, 0x2b}, + {0x4086, 0x00}, + {0x4087, 0xb3}, + {0x4640, 0x40}, + {0x4641, 0x11}, + {0x4642, 0x0e}, + {0x4643, 0xee}, + {0x4646, 0x0f}, + {0x4648, 0x00}, + {0x4649, 0x03}, + {0x4d09, 0xff}, + {0x4d09, 0xdf}, + {0x5019, 0x00}, + {0x501a, 0xff}, + {0x501b, 0xff}, + {0x501d, 0x00}, + {0x501e, 0x23}, + {0x501f, 0x8e}, + {0x5021, 0x00}, + {0x5022, 0x00}, + {0x5023, 0x50}, + {0x5025, 0x00}, + {0x5026, 0x23}, + {0x5027, 0x8e}, + {0x5b80, 0x01}, + {0x5c00, 0x08}, + {0x5c80, 0x00}, + {0x5bbe, 0x12}, + {0x5c3e, 0x12}, + {0x5cbe, 0x12}, + {0x5b8a, 0x80}, + {0x5b8b, 0x80}, + {0x5b8c, 0x80}, + {0x5b8d, 0x80}, + {0x5b8e, 0x80}, + {0x5b8f, 0x40}, + {0x5b90, 0x80}, + {0x5b91, 0x80}, + {0x5b92, 0x80}, + {0x5b93, 0x60}, + {0x5b94, 0x00}, + {0x5b95, 0x00}, + {0x5b96, 0x40}, + {0x5b97, 0x80}, + {0x5b98, 0x10}, + {0x5b99, 0x00}, + {0x5b9a, 0x00}, + {0x5b9b, 0x00}, + {0x5b9c, 0x00}, + {0x5b9d, 0x00}, + {0x5b9e, 0x00}, + {0x5b9f, 0x00}, + {0x5ba0, 0x00}, + {0x5ba1, 0x00}, + {0x5ba2, 0x00}, + {0x5ba3, 0x00}, + {0x5ba4, 0x00}, + {0x5ba5, 0x00}, + {0x5ba6, 0x00}, + {0x5ba7, 0x00}, + {0x5ba8, 0x00}, + {0x5ba9, 0xc0}, + {0x5baa, 0x01}, + {0x5bab, 0x40}, + {0x5bac, 0x02}, + {0x5bad, 0x40}, + {0x5bae, 0x00}, + {0x5baf, 0x50}, + {0x5bb0, 0x00}, + {0x5bb1, 0x60}, + {0x5bb2, 0x00}, + {0x5bb3, 0xc0}, + {0x5c0a, 0x80}, + {0x5c0b, 0x80}, + {0x5c0c, 0x80}, + {0x5c0d, 0x80}, + {0x5c0e, 0x60}, + {0x5c0f, 0x80}, + {0x5c10, 0x80}, + {0x5c11, 0x80}, + {0x5c12, 0x60}, + {0x5c13, 0x20}, + {0x5c14, 0x80}, + {0x5c15, 0x80}, + {0x5c16, 0x80}, + {0x5c17, 0x20}, + {0x5c18, 0x00}, + {0x5c19, 0x80}, + {0x5c1a, 0x40}, + {0x5c1b, 0x20}, + {0x5c1c, 0x00}, + {0x5c1d, 0x00}, + {0x5c1e, 0x80}, + {0x5c1f, 0x00}, + {0x5c20, 0x00}, + {0x5c21, 0x00}, + {0x5c22, 0x00}, + {0x5c23, 0x00}, + {0x5c24, 0x00}, + {0x5c25, 0x00}, + {0x5c26, 0x00}, + {0x5c27, 0x00}, + {0x5c28, 0x02}, + {0x5c29, 0x00}, + {0x5c2a, 0x02}, + {0x5c2b, 0x76}, + {0x5c2c, 0x03}, + {0x5c2d, 0x08}, + {0x5c2e, 0x00}, + {0x5c2f, 0x80}, + {0x5c30, 0x01}, + {0x5c31, 0x00}, + {0x5c32, 0x02}, + {0x5c33, 0x00}, + {0x5c8a, 0x80}, + {0x5c8b, 0x80}, + {0x5c8c, 0x80}, + {0x5c8d, 0x80}, + {0x5c8e, 0x80}, + {0x5c8f, 0x80}, + {0x5c90, 0x80}, + {0x5c91, 0x80}, + {0x5c92, 0x80}, + {0x5c93, 0x60}, + {0x5c94, 0x80}, + {0x5c95, 0x80}, + {0x5c96, 0x80}, + {0x5c97, 0x60}, + {0x5c98, 0x40}, + {0x5c99, 0x80}, + {0x5c9a, 0x80}, + {0x5c9b, 0x80}, + {0x5c9c, 0x40}, + {0x5c9d, 0x20}, + {0x5c9e, 0x80}, + {0x5c9f, 0x80}, + {0x5ca0, 0x80}, + {0x5ca1, 0x20}, + {0x5ca2, 0x00}, + {0x5ca3, 0x80}, + {0x5ca4, 0x80}, + {0x5ca5, 0x80}, + {0x5ca6, 0x00}, + {0x5ca7, 0x00}, + {0x5ca8, 0x01}, + {0x5ca9, 0x00}, + {0x5caa, 0x02}, + {0x5cab, 0x00}, + {0x5cac, 0x03}, + {0x5cad, 0x08}, + {0x5cae, 0x01}, + {0x5caf, 0x00}, + {0x5cb0, 0x02}, + {0x5cb1, 0x00}, + {0x5cb2, 0x03}, + {0x5cb3, 0x08}, + {0x5be7, 0x80}, + {0x5bc9, 0x80}, + {0x5bca, 0x80}, + {0x5bcb, 0x80}, + {0x5bcc, 0x80}, + {0x5bcd, 0x80}, + {0x5bce, 0x80}, + {0x5bcf, 0x80}, + {0x5bd0, 0x80}, + {0x5bd1, 0x80}, + {0x5bd2, 0x20}, + {0x5bd3, 0x80}, + {0x5bd4, 0x40}, + {0x5bd5, 0x20}, + {0x5bd6, 0x00}, + {0x5bd7, 0x00}, + {0x5bd8, 0x00}, + {0x5bd9, 0x00}, + {0x5bda, 0x00}, + {0x5bdb, 0x00}, + {0x5bdc, 0x00}, + {0x5bdd, 0x00}, + {0x5bde, 0x00}, + {0x5bdf, 0x00}, + {0x5be0, 0x00}, + {0x5be1, 0x00}, + {0x5be2, 0x00}, + {0x5be3, 0x00}, + {0x5be4, 0x00}, + {0x5be5, 0x00}, + {0x5be6, 0x00}, + {0x5c49, 0x80}, + {0x5c4a, 0x80}, + {0x5c4b, 0x80}, + {0x5c4c, 0x80}, + {0x5c4d, 0x40}, + {0x5c4e, 0x80}, + {0x5c4f, 0x80}, + {0x5c50, 0x80}, + {0x5c51, 0x60}, + {0x5c52, 0x20}, + {0x5c53, 0x80}, + {0x5c54, 0x80}, + {0x5c55, 0x80}, + {0x5c56, 0x20}, + {0x5c57, 0x00}, + {0x5c58, 0x80}, + {0x5c59, 0x40}, + {0x5c5a, 0x20}, + {0x5c5b, 0x00}, + {0x5c5c, 0x00}, + {0x5c5d, 0x80}, + {0x5c5e, 0x00}, + {0x5c5f, 0x00}, + {0x5c60, 0x00}, + {0x5c61, 0x00}, + {0x5c62, 0x00}, + {0x5c63, 0x00}, + {0x5c64, 0x00}, + {0x5c65, 0x00}, + {0x5c66, 0x00}, + {0x5cc9, 0x80}, + {0x5cca, 0x80}, + {0x5ccb, 0x80}, + {0x5ccc, 0x80}, + {0x5ccd, 0x80}, + {0x5cce, 0x80}, + {0x5ccf, 0x80}, + {0x5cd0, 0x80}, + {0x5cd1, 0x80}, + {0x5cd2, 0x60}, + {0x5cd3, 0x80}, + {0x5cd4, 0x80}, + {0x5cd5, 0x80}, + {0x5cd6, 0x60}, + {0x5cd7, 0x40}, + {0x5cd8, 0x80}, + {0x5cd9, 0x80}, + {0x5cda, 0x80}, + {0x5cdb, 0x40}, + {0x5cdc, 0x20}, + {0x5cdd, 0x80}, + {0x5cde, 0x80}, + {0x5cdf, 0x80}, + {0x5ce0, 0x20}, + {0x5ce1, 0x00}, + {0x5ce2, 0x80}, + {0x5ce3, 0x80}, + {0x5ce4, 0x80}, + {0x5ce5, 0x00}, + {0x5ce6, 0x00}, + {0x5b84, 0x02}, + {0x5b85, 0xcc}, + {0x5bb4, 0x05}, + {0x5bb5, 0xc6}, + {0x5c04, 0x02}, + {0x5c05, 0xcc}, + {0x5c34, 0x05}, + {0x5c35, 0x33}, + {0x5c84, 0x02}, + {0x5c85, 0xcc}, + {0x5cb4, 0x05}, + {0x5cb5, 0x33}, + {0x5bbf, 0x00}, + {0x5bc0, 0x04}, + {0x5bc1, 0x06}, + {0x5bc2, 0xff}, + {0x5bc3, 0x00}, + {0x5bc4, 0x04}, + {0x5bc5, 0x02}, + {0x5bc6, 0xb8}, + {0x5c3f, 0x00}, + {0x5c40, 0x04}, + {0x5c41, 0x07}, + {0x5c42, 0xff}, + {0x5c43, 0x00}, + {0x5c44, 0x04}, + {0x5c45, 0x03}, + {0x5c46, 0xb8}, + {0x5cbf, 0x00}, + {0x5cc0, 0x20}, + {0x5cc1, 0x07}, + {0x5cc2, 0xff}, + {0x5cc3, 0x00}, + {0x5cc4, 0x20}, + {0x5cc5, 0x03}, + {0x5cc6, 0x00}, + {0x5b86, 0x05}, + {0x5c06, 0x05}, + {0x5c86, 0x05}, + {0x5bb8, 0x01}, + {0x5bb9, 0x01}, + {0x5c38, 0x01}, + {0x5c39, 0x01}, + {0x5cb8, 0x01}, + {0x5cb9, 0x01}, + {0x5bc7, 0x00}, + {0x5bc8, 0x80}, + {0x5c47, 0x00}, + {0x5c48, 0x80}, + {0x5cc7, 0x00}, + {0x5cc8, 0x80}, + {0x5bba, 0x01}, + {0x5bbb, 0x00}, + {0x5c3a, 0x01}, + {0x5c3b, 0x00}, + {0x5cba, 0x01}, + {0x5cbb, 0x00}, + {0x5d74, 0x01}, + {0x5d75, 0x00}, + {0x5d1f, 0x81}, + {0x5d11, 0x00}, + {0x5d12, 0x10}, + {0x5d13, 0x10}, + {0x5d15, 0x05}, + {0x5d16, 0x05}, + {0x5d17, 0x05}, + {0x5d08, 0x03}, + {0x5d09, 0x6b}, + {0x5d0a, 0x03}, + {0x5d0b, 0x6b}, + {0x5d18, 0x03}, + {0x5d19, 0x6b}, + {0x52c6, 0x00}, + {0x52c7, 0x12}, + {0x52c8, 0x04}, + {0x52c9, 0x02}, + {0x52ca, 0x01}, + {0x52cb, 0x01}, + {0x52cc, 0x04}, + {0x52cd, 0x02}, + {0x52ce, 0x01}, + {0x52cf, 0x01}, + {0x52d0, 0x03}, + {0x52d1, 0x08}, + {0x52d2, 0x0c}, + {0x54c6, 0x00}, + {0x54c7, 0x12}, + {0x54c8, 0x04}, + {0x54c9, 0x02}, + {0x54ca, 0x01}, + {0x54cb, 0x01}, + {0x54cc, 0x04}, + {0x54cd, 0x02}, + {0x54ce, 0x01}, + {0x54cf, 0x01}, + {0x54d0, 0x03}, + {0x54d1, 0x08}, + {0x54d2, 0x0c}, + {0x56c6, 0x00}, + {0x56c7, 0x12}, + {0x56c8, 0x04}, + {0x56c9, 0x02}, + {0x56ca, 0x01}, + {0x56cb, 0x01}, + {0x56cc, 0x04}, + {0x56cd, 0x02}, + {0x56ce, 0x01}, + {0x56cf, 0x01}, + {0x56d0, 0x03}, + {0x56d1, 0x08}, + {0x56d2, 0x0c}, + {0x58c6, 0x00}, + {0x58c7, 0x12}, + {0x58c8, 0x04}, + {0x58c9, 0x02}, + {0x58ca, 0x01}, + {0x58cb, 0x01}, + {0x58cc, 0x04}, + {0x58cd, 0x02}, + {0x58ce, 0x01}, + {0x58cf, 0x01}, + {0x58d0, 0x03}, + {0x58d1, 0x08}, + {0x58d2, 0x0c}, + {0x5004, 0x1e}, + {0x610a, 0x07}, + {0x610b, 0x80}, + {0x610c, 0x05}, + {0x610d, 0x00}, + {0x6102, 0x3f}, + {0x5d62, 0x07}, + {0x5d40, 0x02}, + {0x5d41, 0x01}, + {0x5d63, 0x08}, + {0x5d64, 0x01}, + {0x5d65, 0xff}, + {0x5d56, 0x00}, + {0x5d57, 0x20}, + {0x5d58, 0x00}, + {0x5d59, 0x20}, + {0x5d5a, 0x00}, + {0x5d5b, 0x0c}, + {0x5d5c, 0x02}, + {0x5d5d, 0x40}, + {0x5d5e, 0x02}, + {0x5d5f, 0x40}, + {0x5d60, 0x03}, + {0x5d61, 0x40}, + {0x5d4a, 0x02}, + {0x5d4b, 0x40}, + {0x5d4c, 0x02}, + {0x5d4d, 0x40}, + {0x5d4e, 0x02}, + {0x5d4f, 0x40}, + {0x5d50, 0x18}, + {0x5d51, 0x80}, + {0x5d52, 0x18}, + {0x5d53, 0x80}, + {0x5d54, 0x18}, + {0x5d55, 0x80}, + {0x5d46, 0x20}, + {0x5d47, 0x00}, + {0x5d48, 0x22}, + {0x5d49, 0x00}, + {0x5d42, 0x20}, + {0x5d43, 0x00}, + {0x5d44, 0x22}, + {0x5d45, 0x00}, + {0x5b40, 0x01}, + {0x5b41, 0x14}, + {0x5b42, 0x0f}, + {0x5b43, 0xf1}, + {0x5b44, 0x0f}, + {0x5b45, 0xfc}, + {0x5b46, 0x0f}, + {0x5b47, 0xf0}, + {0x5b48, 0x01}, + {0x5b49, 0x02}, + {0x5b4a, 0x00}, + {0x5b4b, 0x0e}, + {0x5b4c, 0x0f}, + {0x5b4d, 0xf1}, + {0x5b4e, 0x0f}, + {0x5b4f, 0xe4}, + {0x5b50, 0x01}, + {0x5b51, 0x2b}, + {0x5b52, 0x01}, + {0x5b53, 0x0f}, + {0x5b54, 0x0f}, + {0x5b55, 0xf1}, + {0x5b56, 0x00}, + {0x5b57, 0x00}, + {0x5b58, 0x0f}, + {0x5b59, 0xeb}, + {0x5b5a, 0x01}, + {0x5b5b, 0x04}, + {0x5b5c, 0x00}, + {0x5b5d, 0x11}, + {0x5b5e, 0x0f}, + {0x5b5f, 0xc8}, + {0x5b60, 0x0f}, + {0x5b61, 0xbd}, + {0x5b62, 0x01}, + {0x5b63, 0x7b}, + {0x5b64, 0x01}, + {0x5b65, 0x11}, + {0x5b66, 0x0f}, + {0x5b67, 0xf4}, + {0x5b68, 0x0f}, + {0x5b69, 0xfb}, + {0x5b6a, 0x0f}, + {0x5b6b, 0xf2}, + {0x5b6c, 0x01}, + {0x5b6d, 0x04}, + {0x5b6e, 0x00}, + {0x5b6f, 0x0a}, + {0x5b70, 0x0f}, + {0x5b71, 0xea}, + {0x5b72, 0x0f}, + {0x5b73, 0xc7}, + {0x5b74, 0x01}, + {0x5b75, 0x4e}, + {0x5b78, 0x00}, + {0x5b79, 0x4c}, + {0x5b7a, 0x00}, + {0x5b7b, 0xb9}, + {0x5b7c, 0x01}, + {0x5b7d, 0x38}, + {0x5b7e, 0x01}, + {0x5280, 0x05}, + {0x5281, 0xf2}, + {0x5282, 0x04}, + {0x5283, 0x00}, + {0x5284, 0x04}, + {0x5285, 0x00}, + {0x5286, 0x07}, + {0x5287, 0x3f}, + {0x5480, 0x05}, + {0x5481, 0xf2}, + {0x5482, 0x04}, + {0x5483, 0x00}, + {0x5484, 0x04}, + {0x5485, 0x00}, + {0x5486, 0x07}, + {0x5487, 0x3f}, + {0x5680, 0x06}, + {0x5681, 0x58}, + {0x5682, 0x04}, + {0x5683, 0x00}, + {0x5684, 0x04}, + {0x5685, 0x00}, + {0x5686, 0x06}, + {0x5687, 0x5f}, + {0x5880, 0x05}, + {0x5881, 0xf2}, + {0x5882, 0x04}, + {0x5883, 0x00}, + {0x5884, 0x04}, + {0x5885, 0x00}, + {0x5886, 0x07}, + {0x5887, 0x3f}, + {0x4221, 0x13}, + {0x380e, 0x03}, + {0x380f, 0x37}, + {0x3501, 0x01}, + {0x3502, 0xc8}, + {0x3541, 0x01}, + {0x3542, 0xc8}, + {0x35c1, 0x00}, + {0x35c2, 0x00}, + {0x35c8, 0x01}, + {0x420e, 0x66}, + {0x420f, 0x5d}, + {0x4210, 0xa8}, + {0x4211, 0x55}, + {0x507a, 0x5f}, + {0x507b, 0x46}, + {0x4f00, 0x00}, + {0x4f01, 0x01}, + {0x4f02, 0x80}, + {0x4f03, 0x2c}, + {0x4f04, 0xf8}, + {0x0100, 0x01}, + {REG_NULL, 0x00}, +}; + +static const struct regval ox03c10_1920x1080_30fps_HDR3_VS_PWL12_mipi600[] = { + {0x0103, 0x01}, + {0x0107, 0x01}, + {DELAY_MS, 6}, + {0X4d5a, 0X1C}, + {0X4d09, 0Xff}, + {0X4d09, 0Xdf}, + {0X3208, 0X04}, + {0X4620, 0X04}, + {0X3208, 0X14}, + {0X3208, 0X05}, + {0X4620, 0X04}, + {0X3208, 0X15}, + {0X3208, 0X02}, + {0X3507, 0X00}, + {0X3208, 0X12}, + {0X3208, 0Xa2}, + {0X0301, 0Xc8}, + {0X0303, 0X01}, + {0X0304, 0X01}, + {0X0305, 0X2c}, + {0X0306, 0X04}, + {0X0307, 0X03}, + {0X0316, 0X00}, + {0X0317, 0X00}, + {0X0318, 0X00}, + {0X0323, 0X05}, + {0X0324, 0X01}, + {0X0325, 0X2c}, + {0X032e, 0X00}, + {0X032a, 0X04}, + {0X0326, 0X09}, + {0X0327, 0X04}, + {0X0331, 0X04}, + {0X0400, 0Xe0}, + {0X0401, 0X80}, + {0X0403, 0Xde}, + {0X0404, 0X34}, + {0X0405, 0X3b}, + {0X0406, 0Xd4}, + {0X0407, 0X08}, + {0X0408, 0Xe0}, + {0X0409, 0X62}, + {0X040a, 0Xde}, + {0X040b, 0X34}, + {0X040c, 0X8f}, + {0X040d, 0X9b}, + {0X040e, 0X08}, + {0X2803, 0Xfe}, + {0X280b, 0X00}, + {0X280c, 0X79}, + {0X3001, 0X03}, + {0X3002, 0Xf8}, + {0X3005, 0X80}, + {0X3007, 0X01}, + {0X3008, 0X80}, + {0X3012, 0X41}, + {0X3020, 0X05}, + {0X3700, 0X28}, + {0X3701, 0X15}, + {0X3702, 0X19}, + {0X3703, 0X23}, + {0X3704, 0X0a}, + {0X3705, 0X00}, + {0X3706, 0X3e}, + {0X3707, 0X0d}, + {0X3708, 0X50}, + {0X3709, 0X5a}, + {0X370a, 0X00}, + {0X370b, 0X96}, + {0X3711, 0X11}, + {0X3712, 0X13}, + {0X3717, 0X02}, + {0X3718, 0X73}, + {0X372c, 0X40}, + {0X3733, 0X01}, + {0X3738, 0X36}, + {0X3739, 0X36}, + {0X373a, 0X25}, + {0X373b, 0X25}, + {0X373f, 0X21}, + {0X3740, 0X21}, + {0X3741, 0X21}, + {0X3742, 0X21}, + {0X3747, 0X28}, + {0X3748, 0X28}, + {0X3749, 0X19}, + {0X3755, 0X1a}, + {0X3756, 0X0a}, + {0X3757, 0X1c}, + {0X3765, 0X19}, + {0X3766, 0X05}, + {0X3767, 0X05}, + {0X3768, 0X13}, + {0X376c, 0X07}, + {0X3778, 0X20}, + {0X377c, 0Xc8}, + {0X3781, 0X02}, + {0X3783, 0X02}, + {0X379c, 0X58}, + {0X379e, 0X00}, + {0X379f, 0X00}, + {0X37a0, 0X00}, + {0X37bc, 0X22}, + {0X37c0, 0X01}, + {0X37c4, 0X3e}, + {0X37c5, 0X3e}, + {0X37c6, 0X2a}, + {0X37c7, 0X28}, + {0X37c8, 0X02}, + {0X37c9, 0X12}, + {0X37cb, 0X29}, + {0X37cd, 0X29}, + {0X37d2, 0X00}, + {0X37d3, 0X73}, + {0X37d6, 0X00}, + {0X37d7, 0X6b}, + {0X37dc, 0X00}, + {0X37df, 0X54}, + {0X37e2, 0X00}, + {0X37e3, 0X00}, + {0X37f8, 0X00}, + {0X37f9, 0X01}, + {0X37fa, 0X00}, + {0X37fb, 0X19}, + {0X3c03, 0X01}, + {0X3c04, 0X01}, + {0X3c06, 0X21}, + {0X3c08, 0X01}, + {0X3c09, 0X01}, + {0X3c0a, 0X01}, + {0X3c0b, 0X21}, + {0X3c13, 0X21}, + {0X3c14, 0X82}, + {0X3c16, 0X13}, + {0X3c21, 0X00}, + {0X3c22, 0Xf3}, + {0X3c37, 0X12}, + {0X3c38, 0X31}, + {0X3c3c, 0X00}, + {0X3c3d, 0X03}, + {0X3c44, 0X16}, + {0X3c5c, 0X8a}, + {0X3c5f, 0X03}, + {0X3c61, 0X80}, + {0X3c6f, 0X2b}, + {0X3c70, 0X5f}, + {0X3c71, 0X2c}, + {0X3c72, 0X2c}, + {0X3c73, 0X2c}, + {0X3c76, 0X12}, + {0X3182, 0X12}, + {0X320e, 0X00}, + {0X320f, 0X00}, + {0X3211, 0X61}, + {0X3215, 0Xcd}, + {0X3219, 0X08}, + {0X3506, 0X20}, + {0X350a, 0X01}, + {0X350b, 0X00}, + {0X350c, 0X00}, + {0X3586, 0X30}, + {0X358a, 0X01}, + {0X358b, 0X00}, + {0X358c, 0X00}, + {0X3541, 0X00}, + {0X3542, 0X04}, + {0X3546, 0X10}, + {0X3548, 0X04}, + {0X3549, 0X40}, + {0X354a, 0X01}, + {0X354b, 0X00}, + {0X354c, 0X00}, + {0X35c1, 0X00}, + {0X35c2, 0X02}, + {0X35c6, 0X90}, + {0X3600, 0X8f}, + {0X3605, 0X16}, + {0X3609, 0Xf0}, + {0X360a, 0X01}, + {0X360e, 0X1d}, + {0X360f, 0X10}, + {0X3610, 0X70}, + {0X3611, 0X3a}, + {0X3612, 0X28}, + {0X361a, 0X29}, + {0X361b, 0X6c}, + {0X361c, 0X0b}, + {0X361d, 0X00}, + {0X361e, 0Xfc}, + {0X362a, 0X00}, + {0X364d, 0X0f}, + {0X364e, 0X18}, + {0X364f, 0X12}, + {0X3653, 0X1c}, + {0X3654, 0X00}, + {0X3655, 0X1f}, + {0X3656, 0X1f}, + {0X3657, 0X0c}, + {0X3658, 0X0a}, + {0X3659, 0X14}, + {0X365a, 0X18}, + {0X365b, 0X14}, + {0X365c, 0X10}, + {0X365e, 0X12}, + {0X3674, 0X08}, + {0X3677, 0X3a}, + {0X3678, 0X3a}, + {0X3679, 0X19}, + {0X3802, 0X00}, + {0X3803, 0X04}, + {0X3806, 0X05}, + {0X3807, 0X0b}, + {0X3808, 0X07}, + {0X3809, 0X80}, + {0X380a, 0X04}, + {0X380b, 0X38}, + {0X380c, 0X08}, + {0X380d, 0X8e}, + {0X380e, 0X02}, + {0X380f, 0Xae}, + {0X3810, 0X00}, + {0X3811, 0X08}, + {0X3812, 0X00}, + {0X3813, 0X68}, + {0X3816, 0X01}, + {0X3817, 0X01}, + {0X381c, 0X18}, + {0X381e, 0X01}, + {0X381f, 0X01}, + {0X3820, 0X00}, + {0X3821, 0X19}, + {0x3832, 0x00}, + {0x3834, 0x00}, + {0x384c, 0x04}, + {0x384d, 0x1a}, + {0x3850, 0x00}, + {0x3851, 0x42}, + {0x3852, 0x00}, + {0x3853, 0x40}, + {0x3858, 0x04}, + {0x388c, 0x04}, + {0x388d, 0x56}, + {0x3b40, 0x05}, + {0x3b41, 0x40}, + {0x3b42, 0x00}, + {0x3b43, 0x90}, + {0x3b44, 0x00}, + {0x3b45, 0x20}, + {0x3b46, 0x00}, + {0x3b47, 0x20}, + {0x3b48, 0x19}, + {0x3b49, 0x12}, + {0x3b4a, 0x16}, + {0x3b4b, 0x2e}, + {0x3b4c, 0x00}, + {0x3b4d, 0x00}, + {0x3b86, 0x00}, + {0x3b87, 0x34}, + {0x3b88, 0x00}, + {0x3b89, 0x08}, + {0x3b8a, 0x05}, + {0x3b8b, 0x00}, + {0x3b8c, 0x07}, + {0x3b8d, 0x80}, + {0x3b8e, 0x00}, + {0x3b8f, 0x00}, + {0x3b92, 0x05}, + {0x3b93, 0x00}, + {0x3b94, 0x07}, + {0x3b95, 0x80}, + {0x3b9e, 0x09}, + {0x3d85, 0x05}, + {0x3d8a, 0x03}, + {0x3d8b, 0xff}, + {0x3d99, 0x00}, + {0x3d9a, 0x9f}, + {0x3d9b, 0x00}, + {0x3d9c, 0xa0}, + {0x3da4, 0x00}, + {0x3da7, 0x50}, + {0x420e, 0x6b}, + {0x420f, 0x6e}, + {0x4210, 0x06}, + {0x4211, 0xc1}, + {0x421e, 0x02}, + {0x421f, 0x45}, + {0x4220, 0xe1}, + {0x4221, 0x01}, + {0x4301, 0x0f}, + {0x4307, 0x03}, + {0x4308, 0x13}, + {0x430a, 0x13}, + {0x430d, 0x93}, + {0x430f, 0x57}, + {0x4310, 0x95}, + {0x4311, 0x16}, + {0x4316, 0x00}, + {0x4317, 0x08}, + {0x4319, 0x01}, + {0x431a, 0x00}, + {0x431b, 0x00}, + {0x431d, 0x2a}, + {0x431e, 0x11}, + {0x431f, 0x20}, + {0x4320, 0x19}, + {0x4323, 0x80}, + {0x4324, 0x00}, + {0x4503, 0x4e}, + {0x4505, 0x00}, + {0x4509, 0x00}, + {0x450a, 0x00}, + {0x4580, 0xf8}, + {0x4583, 0x07}, + {0x4584, 0x6a}, + {0x4585, 0x08}, + {0x4586, 0x05}, + {0x4587, 0x04}, + {0x4588, 0x73}, + {0x4589, 0x05}, + {0x458a, 0x1f}, + {0x458b, 0x02}, + {0x458c, 0xdc}, + {0x458d, 0x03}, + {0x458e, 0x02}, + {0x4597, 0x07}, + {0x4598, 0x40}, + {0x4599, 0x0e}, + {0x459a, 0x0e}, + {0x459b, 0xfb}, + {0x459c, 0xf3}, + {0x4602, 0x00}, + {0x4603, 0x13}, + {0x4604, 0x00}, + {0x4609, 0x60}, + {0x460a, 0x30}, + {0x4610, 0x00}, + {0x4611, 0x40}, + {0x4612, 0x01}, + {0x4613, 0x00}, + {0x4614, 0x00}, + {0x4615, 0x40}, + {0x4616, 0x01}, + {0x4617, 0x00}, + {0x4800, 0x04}, + {0x480a, 0x22}, + {0x4813, 0xe4}, + {0x4814, 0x2a}, + {0x4837, 0x1a}, + {0x484b, 0x47}, + {0x484f, 0x00}, + {0x4887, 0x51}, + {0x4d00, 0x4a}, + {0x4d01, 0x18}, + {0x4d05, 0xff}, + {0x4d06, 0x88}, + {0x4d08, 0x63}, + {0x4d09, 0xdf}, + {0x4d15, 0x7d}, + {0x4d1a, 0x20}, + {0x4d30, 0x0a}, + {0x4d31, 0x00}, + {0x4d34, 0x7d}, + {0x4d3c, 0x7d}, + {0x4f00, 0x3f}, + {0x4f01, 0xff}, + {0x4f02, 0xff}, + {0x4f03, 0x2c}, + {0x4f04, 0xe0}, + {0x6a00, 0x00}, + {0x6a01, 0x20}, + {0x6a02, 0x00}, + {0x6a03, 0x20}, + {0x6a04, 0x02}, + {0x6a05, 0x80}, + {0x6a06, 0x01}, + {0x6a07, 0xe0}, + {0x6a08, 0xcf}, + {0x6a09, 0x01}, + {0x6a0a, 0x40}, + {0x6a20, 0x00}, + {0x6a21, 0x02}, + {0x6a22, 0x00}, + {0x6a23, 0x00}, + {0x6a24, 0x00}, + {0x6a25, 0x00}, + {0x6a26, 0x00}, + {0x6a27, 0x00}, + {0x6a28, 0x00}, + {0x5000, 0x8f}, + {0x5001, 0x65}, + {0x5002, 0x7f}, + {0x5003, 0x7a}, + {0x5004, 0x3e}, + {0x5005, 0x1e}, + {0x5006, 0x1e}, + {0x5007, 0x1e}, + {0x5008, 0x00}, + {0x500c, 0x00}, + {0x502c, 0x00}, + {0x502e, 0x00}, + {0x502f, 0x00}, + {0x504b, 0x00}, + {0x5053, 0x00}, + {0x505b, 0x00}, + {0x5063, 0x00}, + {0x5070, 0x00}, + {0x5074, 0x04}, + {0x507a, 0x04}, + {0x507b, 0x09}, + {0x5500, 0x02}, + {0x5700, 0x02}, + {0x5900, 0x02}, + {0x6007, 0x04}, + {0x6008, 0x05}, + {0x6009, 0x02}, + {0x600b, 0x08}, + {0x600c, 0x07}, + {0x600d, 0x88}, + {0x6016, 0x00}, + {0x6027, 0x04}, + {0x6028, 0x05}, + {0x6029, 0x02}, + {0x602b, 0x08}, + {0x602c, 0x07}, + {0x602d, 0x88}, + {0x6047, 0x04}, + {0x6048, 0x05}, + {0x6049, 0x02}, + {0x604b, 0x08}, + {0x604c, 0x07}, + {0x604d, 0x88}, + {0x6067, 0x04}, + {0x6068, 0x05}, + {0x6069, 0x02}, + {0x606b, 0x08}, + {0x606c, 0x07}, + {0x606d, 0x88}, + {0x6087, 0x04}, + {0x6088, 0x05}, + {0x6089, 0x02}, + {0x608b, 0x08}, + {0x608c, 0x07}, + {0x608d, 0x88}, + {0x5e00, 0x00}, + {0x5e01, 0x08}, + {0x5e02, 0x09}, + {0x5e03, 0x0a}, + {0x5e04, 0x0b}, + {0x5e05, 0x0c}, + {0x5e06, 0x0c}, + {0x5e07, 0x0c}, + {0x5e08, 0x0c}, + {0x5e09, 0x0c}, + {0x5e0a, 0x0d}, + {0x5e0b, 0x0d}, + {0x5e0c, 0x0d}, + {0x5e0d, 0x0d}, + {0x5e0e, 0x0d}, + {0x5e0f, 0x0d}, + {0x5e10, 0x0d}, + {0x5e11, 0x0d}, + {0x5e12, 0x0e}, + {0x5e13, 0x0e}, + {0x5e14, 0x0e}, + {0x5e15, 0x0e}, + {0x5e16, 0x0e}, + {0x5e17, 0x0e}, + {0x5e18, 0x0e}, + {0x5e19, 0x10}, + {0x5e1a, 0x11}, + {0x5e1b, 0x11}, + {0x5e1c, 0x12}, + {0x5e1d, 0x12}, + {0x5e1e, 0x14}, + {0x5e1f, 0x15}, + {0x5e20, 0x17}, + {0x5e21, 0x17}, + {0x5e22, 0x00}, + {0x5e23, 0x01}, + {0x5e26, 0x00}, + {0x5e27, 0x3f}, + {0x5e29, 0x00}, + {0x5e2a, 0x40}, + {0x5e2c, 0x00}, + {0x5e2d, 0x40}, + {0x5e2f, 0x00}, + {0x5e30, 0x40}, + {0x5e32, 0x00}, + {0x5e33, 0x40}, + {0x5e34, 0x00}, + {0x5e35, 0x00}, + {0x5e36, 0x40}, + {0x5e37, 0x00}, + {0x5e38, 0x00}, + {0x5e39, 0x40}, + {0x5e3a, 0x00}, + {0x5e3b, 0x00}, + {0x5e3c, 0x40}, + {0x5e3d, 0x00}, + {0x5e3e, 0x00}, + {0x5e3f, 0x40}, + {0x5e40, 0x00}, + {0x5e41, 0x00}, + {0x5e42, 0x60}, + {0x5e43, 0x00}, + {0x5e44, 0x00}, + {0x5e45, 0x60}, + {0x5e46, 0x00}, + {0x5e47, 0x00}, + {0x5e48, 0x60}, + {0x5e49, 0x00}, + {0x5e4a, 0x00}, + {0x5e4b, 0x60}, + {0x5e4c, 0x00}, + {0x5e4d, 0x00}, + {0x5e4e, 0x60}, + {0x5e50, 0x00}, + {0x5e51, 0x60}, + {0x5e53, 0x00}, + {0x5e54, 0x60}, + {0x5e56, 0x00}, + {0x5e57, 0x80}, + {0x5e59, 0x00}, + {0x5e5a, 0x80}, + {0x5e5c, 0x00}, + {0x5e5d, 0x80}, + {0x5e5f, 0x00}, + {0x5e60, 0x80}, + {0x5e62, 0x00}, + {0x5e63, 0x80}, + {0x5e65, 0x00}, + {0x5e66, 0x80}, + {0x5e68, 0x00}, + {0x5e69, 0x80}, + {0x5e6b, 0x01}, + {0x5e6c, 0x20}, + {0x5e6e, 0x01}, + {0x5e6f, 0xd0}, + {0x5e71, 0x01}, + {0x5e72, 0x30}, + {0x5e74, 0x01}, + {0x5e75, 0x80}, + {0x5e77, 0x01}, + {0x5e78, 0x00}, + {0x5e7a, 0x00}, + {0x5e7b, 0x00}, + {0x5e7d, 0x00}, + {0x5e7e, 0x00}, + {0x5e80, 0x00}, + {0x5e81, 0x00}, + {0x5e83, 0x00}, + {0x5e84, 0x00}, + {0x5f00, 0x02}, + {0x5f01, 0x08}, + {0x5f02, 0x09}, + {0x5f03, 0x0a}, + {0x5f04, 0x0b}, + {0x5f05, 0x0c}, + {0x5f06, 0x0c}, + {0x5f07, 0x0c}, + {0x5f08, 0x0c}, + {0x5f09, 0x0c}, + {0x5f0a, 0x0d}, + {0x5f0b, 0x0d}, + {0x5f0c, 0x0d}, + {0x5f0d, 0x0d}, + {0x5f0e, 0x0d}, + {0x5f0f, 0x0e}, + {0x5f10, 0x0e}, + {0x5f11, 0x0e}, + {0x5f12, 0x0e}, + {0x5f13, 0x0f}, + {0x5f14, 0x0f}, + {0x5f15, 0x10}, + {0x5f16, 0x11}, + {0x5f17, 0x11}, + {0x5f18, 0x12}, + {0x5f19, 0x12}, + {0x5f1a, 0x13}, + {0x5f1b, 0x13}, + {0x5f1c, 0x14}, + {0x5f1d, 0x14}, + {0x5f1e, 0x16}, + {0x5f1f, 0x16}, + {0x5f20, 0x16}, + {0x5f21, 0x08}, + {0x5f22, 0x00}, + {0x5f23, 0x01}, + {0x5f26, 0x02}, + {0x5f27, 0x00}, + {0x5f29, 0x02}, + {0x5f2a, 0x00}, + {0x5f2c, 0x02}, + {0x5f2d, 0x00}, + {0x5f2f, 0x02}, + {0x5f30, 0x00}, + {0x5f32, 0x02}, + {0x5f33, 0x00}, + {0x5f34, 0x00}, + {0x5f35, 0x02}, + {0x5f36, 0x00}, + {0x5f37, 0x00}, + {0x5f38, 0x02}, + {0x5f39, 0x00}, + {0x5f3a, 0x00}, + {0x5f3b, 0x02}, + {0x5f3c, 0x00}, + {0x5f3d, 0x00}, + {0x5f3e, 0x02}, + {0x5f3f, 0x00}, + {0x5f40, 0x00}, + {0x5f41, 0x02}, + {0x5f42, 0x00}, + {0x5f43, 0x00}, + {0x5f44, 0x02}, + {0x5f45, 0x00}, + {0x5f46, 0x00}, + {0x5f47, 0x04}, + {0x5f48, 0x00}, + {0x5f49, 0x00}, + {0x5f4a, 0x04}, + {0x5f4b, 0x00}, + {0x5f4c, 0x00}, + {0x5f4d, 0x04}, + {0x5f4e, 0x00}, + {0x5f50, 0x04}, + {0x5f51, 0x00}, + {0x5f53, 0x04}, + {0x5f54, 0x00}, + {0x5f56, 0x04}, + {0x5f57, 0x00}, + {0x5f59, 0x04}, + {0x5f5a, 0x00}, + {0x5f5c, 0x04}, + {0x5f5d, 0x00}, + {0x5f5f, 0x08}, + {0x5f60, 0x00}, + {0x5f62, 0x08}, + {0x5f63, 0x00}, + {0x5f65, 0x08}, + {0x5f66, 0x00}, + {0x5f68, 0x08}, + {0x5f69, 0x00}, + {0x5f6b, 0x08}, + {0x5f6c, 0x00}, + {0x5f6e, 0x10}, + {0x5f6f, 0x00}, + {0x5f71, 0x10}, + {0x5f72, 0x00}, + {0x5f74, 0x10}, + {0x5f75, 0x00}, + {0x5f77, 0x10}, + {0x5f78, 0x00}, + {0x5f7a, 0x20}, + {0x5f7b, 0x00}, + {0x5f7d, 0x20}, + {0x5f7e, 0x00}, + {0x5f80, 0x20}, + {0x5f81, 0x00}, + {0x5f83, 0x00}, + {0x5f84, 0xff}, + {0x5240, 0x0f}, + {0x5243, 0x00}, + {0x5244, 0x00}, + {0x5245, 0x00}, + {0x5246, 0x00}, + {0x5247, 0x00}, + {0x5248, 0x00}, + {0x5249, 0x00}, + {0x5440, 0x0f}, + {0x5443, 0x00}, + {0x5445, 0x00}, + {0x5447, 0x00}, + {0x5448, 0x00}, + {0x5449, 0x00}, + {0x5640, 0x0f}, + {0x5642, 0x00}, + {0x5643, 0x00}, + {0x5644, 0x00}, + {0x5645, 0x00}, + {0x5646, 0x00}, + {0x5647, 0x00}, + {0x5649, 0x00}, + {0x5840, 0x0f}, + {0x5842, 0x00}, + {0x5843, 0x00}, + {0x5845, 0x00}, + {0x5846, 0x00}, + {0x5847, 0x00}, + {0x5848, 0x00}, + {0x5849, 0x00}, + {0x4001, 0x2b}, + {0x4008, 0x02}, + {0x4009, 0x03}, + {0x4018, 0x12}, + {0x4022, 0x40}, + {0x4023, 0x20}, + {0x4026, 0x00}, + {0x4027, 0x40}, + {0x4028, 0x00}, + {0x4029, 0x40}, + {0x402a, 0x00}, + {0x402b, 0x40}, + {0x402c, 0x00}, + {0x402d, 0x40}, + {0x405e, 0x00}, + {0x405f, 0x00}, + {0x4060, 0x00}, + {0x4061, 0x00}, + {0x4062, 0x00}, + {0x4063, 0x00}, + {0x4064, 0x00}, + {0x4065, 0x00}, + {0x4066, 0x00}, + {0x4067, 0x00}, + {0x4068, 0x00}, + {0x4069, 0x00}, + {0x406a, 0x00}, + {0x406b, 0x00}, + {0x406c, 0x00}, + {0x406d, 0x00}, + {0x406e, 0x00}, + {0x406f, 0x00}, + {0x4070, 0x00}, + {0x4071, 0x00}, + {0x4072, 0x00}, + {0x4073, 0x00}, + {0x4074, 0x00}, + {0x4075, 0x00}, + {0x4076, 0x00}, + {0x4077, 0x00}, + {0x4078, 0x00}, + {0x4079, 0x00}, + {0x407a, 0x00}, + {0x407b, 0x00}, + {0x407c, 0x00}, + {0x407d, 0x00}, + {0x407e, 0xcc}, + {0x407f, 0x18}, + {0x4080, 0xff}, + {0x4081, 0xff}, + {0x4082, 0x01}, + {0x4083, 0x53}, + {0x4084, 0x01}, + {0x4085, 0x2b}, + {0x4086, 0x00}, + {0x4087, 0xb3}, + {0x4640, 0x40}, + {0x4641, 0x11}, + {0x4642, 0x0e}, + {0x4643, 0xee}, + {0x4646, 0x0f}, + {0x4648, 0x00}, + {0x4649, 0x03}, + {0x4d09, 0xff}, + {0x4d09, 0xdf}, + {0x5019, 0x00}, + {0x501a, 0xff}, + {0x501b, 0xff}, + {0x501d, 0x00}, + {0x501e, 0x23}, + {0x501f, 0x8e}, + {0x5021, 0x00}, + {0x5022, 0x00}, + {0x5023, 0x50}, + {0x5025, 0x00}, + {0x5026, 0x23}, + {0x5027, 0x8e}, + {0x5b80, 0x08}, + {0x5c00, 0x08}, + {0x5c80, 0x00}, + {0x5bbe, 0x12}, + {0x5c3e, 0x12}, + {0x5cbe, 0x12}, + {0x5b8a, 0x80}, + {0x5b8b, 0x80}, + {0x5b8c, 0x80}, + {0x5b8d, 0x80}, + {0x5b8e, 0x80}, + {0x5b8f, 0x40}, + {0x5b90, 0x80}, + {0x5b91, 0x80}, + {0x5b92, 0x80}, + {0x5b93, 0x60}, + {0x5b94, 0x00}, + {0x5b95, 0x00}, + {0x5b96, 0x40}, + {0x5b97, 0x80}, + {0x5b98, 0x10}, + {0x5b99, 0x00}, + {0x5b9a, 0x00}, + {0x5b9b, 0x00}, + {0x5b9c, 0x00}, + {0x5b9d, 0x00}, + {0x5b9e, 0x00}, + {0x5b9f, 0x00}, + {0x5ba0, 0x00}, + {0x5ba1, 0x00}, + {0x5ba2, 0x00}, + {0x5ba3, 0x00}, + {0x5ba4, 0x00}, + {0x5ba5, 0x00}, + {0x5ba6, 0x00}, + {0x5ba7, 0x00}, + {0x5ba8, 0x00}, + {0x5ba9, 0xc0}, + {0x5baa, 0x01}, + {0x5bab, 0x40}, + {0x5bac, 0x02}, + {0x5bad, 0x40}, + {0x5bae, 0x00}, + {0x5baf, 0x50}, + {0x5bb0, 0x00}, + {0x5bb1, 0x60}, + {0x5bb2, 0x00}, + {0x5bb3, 0xc0}, + {0x5c0a, 0x80}, + {0x5c0b, 0x80}, + {0x5c0c, 0x80}, + {0x5c0d, 0x80}, + {0x5c0e, 0x60}, + {0x5c0f, 0x80}, + {0x5c10, 0x80}, + {0x5c11, 0x80}, + {0x5c12, 0x60}, + {0x5c13, 0x20}, + {0x5c14, 0x80}, + {0x5c15, 0x80}, + {0x5c16, 0x80}, + {0x5c17, 0x20}, + {0x5c18, 0x00}, + {0x5c19, 0x80}, + {0x5c1a, 0x40}, + {0x5c1b, 0x20}, + {0x5c1c, 0x00}, + {0x5c1d, 0x00}, + {0x5c1e, 0x80}, + {0x5c1f, 0x00}, + {0x5c20, 0x00}, + {0x5c21, 0x00}, + {0x5c22, 0x00}, + {0x5c23, 0x00}, + {0x5c24, 0x00}, + {0x5c25, 0x00}, + {0x5c26, 0x00}, + {0x5c27, 0x00}, + {0x5c28, 0x02}, + {0x5c29, 0x00}, + {0x5c2a, 0x02}, + {0x5c2b, 0x76}, + {0x5c2c, 0x03}, + {0x5c2d, 0x08}, + {0x5c2e, 0x00}, + {0x5c2f, 0x80}, + {0x5c30, 0x01}, + {0x5c31, 0x00}, + {0x5c32, 0x02}, + {0x5c33, 0x00}, + {0x5c8a, 0x80}, + {0x5c8b, 0x80}, + {0x5c8c, 0x80}, + {0x5c8d, 0x80}, + {0x5c8e, 0x80}, + {0x5c8f, 0x80}, + {0x5c90, 0x80}, + {0x5c91, 0x80}, + {0x5c92, 0x80}, + {0x5c93, 0x60}, + {0x5c94, 0x80}, + {0x5c95, 0x80}, + {0x5c96, 0x80}, + {0x5c97, 0x60}, + {0x5c98, 0x40}, + {0x5c99, 0x80}, + {0x5c9a, 0x80}, + {0x5c9b, 0x80}, + {0x5c9c, 0x40}, + {0x5c9d, 0x20}, + {0x5c9e, 0x80}, + {0x5c9f, 0x80}, + {0x5ca0, 0x80}, + {0x5ca1, 0x20}, + {0x5ca2, 0x00}, + {0x5ca3, 0x80}, + {0x5ca4, 0x80}, + {0x5ca5, 0x80}, + {0x5ca6, 0x00}, + {0x5ca7, 0x00}, + {0x5ca8, 0x01}, + {0x5ca9, 0x00}, + {0x5caa, 0x02}, + {0x5cab, 0x00}, + {0x5cac, 0x03}, + {0x5cad, 0x08}, + {0x5cae, 0x01}, + {0x5caf, 0x00}, + {0x5cb0, 0x02}, + {0x5cb1, 0x00}, + {0x5cb2, 0x03}, + {0x5cb3, 0x08}, + {0x5be7, 0x80}, + {0x5bc9, 0x80}, + {0x5bca, 0x80}, + {0x5bcb, 0x80}, + {0x5bcc, 0x80}, + {0x5bcd, 0x80}, + {0x5bce, 0x80}, + {0x5bcf, 0x80}, + {0x5bd0, 0x80}, + {0x5bd1, 0x80}, + {0x5bd2, 0x20}, + {0x5bd3, 0x80}, + {0x5bd4, 0x40}, + {0x5bd5, 0x20}, + {0x5bd6, 0x00}, + {0x5bd7, 0x00}, + {0x5bd8, 0x00}, + {0x5bd9, 0x00}, + {0x5bda, 0x00}, + {0x5bdb, 0x00}, + {0x5bdc, 0x00}, + {0x5bdd, 0x00}, + {0x5bde, 0x00}, + {0x5bdf, 0x00}, + {0x5be0, 0x00}, + {0x5be1, 0x00}, + {0x5be2, 0x00}, + {0x5be3, 0x00}, + {0x5be4, 0x00}, + {0x5be5, 0x00}, + {0x5be6, 0x00}, + {0x5c49, 0x80}, + {0x5c4a, 0x80}, + {0x5c4b, 0x80}, + {0x5c4c, 0x80}, + {0x5c4d, 0x40}, + {0x5c4e, 0x80}, + {0x5c4f, 0x80}, + {0x5c50, 0x80}, + {0x5c51, 0x60}, + {0x5c52, 0x20}, + {0x5c53, 0x80}, + {0x5c54, 0x80}, + {0x5c55, 0x80}, + {0x5c56, 0x20}, + {0x5c57, 0x00}, + {0x5c58, 0x80}, + {0x5c59, 0x40}, + {0x5c5a, 0x20}, + {0x5c5b, 0x00}, + {0x5c5c, 0x00}, + {0x5c5d, 0x80}, + {0x5c5e, 0x00}, + {0x5c5f, 0x00}, + {0x5c60, 0x00}, + {0x5c61, 0x00}, + {0x5c62, 0x00}, + {0x5c63, 0x00}, + {0x5c64, 0x00}, + {0x5c65, 0x00}, + {0x5c66, 0x00}, + {0x5cc9, 0x80}, + {0x5cca, 0x80}, + {0x5ccb, 0x80}, + {0x5ccc, 0x80}, + {0x5ccd, 0x80}, + {0x5cce, 0x80}, + {0x5ccf, 0x80}, + {0x5cd0, 0x80}, + {0x5cd1, 0x80}, + {0x5cd2, 0x60}, + {0x5cd3, 0x80}, + {0x5cd4, 0x80}, + {0x5cd5, 0x80}, + {0x5cd6, 0x60}, + {0x5cd7, 0x40}, + {0x5cd8, 0x80}, + {0x5cd9, 0x80}, + {0x5cda, 0x80}, + {0x5cdb, 0x40}, + {0x5cdc, 0x20}, + {0x5cdd, 0x80}, + {0x5cde, 0x80}, + {0x5cdf, 0x80}, + {0x5ce0, 0x20}, + {0x5ce1, 0x00}, + {0x5ce2, 0x80}, + {0x5ce3, 0x80}, + {0x5ce4, 0x80}, + {0x5ce5, 0x00}, + {0x5ce6, 0x00}, + {0x5b84, 0x02}, + {0x5b85, 0xcc}, + {0x5bb4, 0x05}, + {0x5bb5, 0xc6}, + {0x5c04, 0x02}, + {0x5c05, 0xcc}, + {0x5c34, 0x05}, + {0x5c35, 0x33}, + {0x5c84, 0x02}, + {0x5c85, 0xcc}, + {0x5cb4, 0x05}, + {0x5cb5, 0x33}, + {0x5bbf, 0x00}, + {0x5bc0, 0x04}, + {0x5bc1, 0x06}, + {0x5bc2, 0xff}, + {0x5bc3, 0x00}, + {0x5bc4, 0x04}, + {0x5bc5, 0x02}, + {0x5bc6, 0xb8}, + {0x5c3f, 0x00}, + {0x5c40, 0x04}, + {0x5c41, 0x07}, + {0x5c42, 0xff}, + {0x5c43, 0x00}, + {0x5c44, 0x04}, + {0x5c45, 0x03}, + {0x5c46, 0xb8}, + {0x5cbf, 0x00}, + {0x5cc0, 0x20}, + {0x5cc1, 0x07}, + {0x5cc2, 0xff}, + {0x5cc3, 0x00}, + {0x5cc4, 0x20}, + {0x5cc5, 0x03}, + {0x5cc6, 0x00}, + {0x5b86, 0x05}, + {0x5c06, 0x05}, + {0x5c86, 0x05}, + {0x5bb8, 0x01}, + {0x5bb9, 0x01}, + {0x5c38, 0x01}, + {0x5c39, 0x01}, + {0x5cb8, 0x01}, + {0x5cb9, 0x01}, + {0x5bc7, 0x00}, + {0x5bc8, 0x80}, + {0x5c47, 0x00}, + {0x5c48, 0x80}, + {0x5cc7, 0x00}, + {0x5cc8, 0x80}, + {0x5bba, 0x01}, + {0x5bbb, 0x00}, + {0x5c3a, 0x01}, + {0x5c3b, 0x00}, + {0x5cba, 0x01}, + {0x5cbb, 0x00}, + {0x5d74, 0x01}, + {0x5d75, 0x00}, + {0x5d1f, 0x81}, + {0x5d11, 0x00}, + {0x5d12, 0x10}, + {0x5d13, 0x10}, + {0x5d15, 0x05}, + {0x5d16, 0x05}, + {0x5d17, 0x05}, + {0x5d08, 0x03}, + {0x5d09, 0x6b}, + {0x5d0a, 0x03}, + {0x5d0b, 0x6b}, + {0x5d18, 0x03}, + {0x5d19, 0x6b}, + {0x52c6, 0x00}, + {0x52c7, 0x12}, + {0x52c8, 0x04}, + {0x52c9, 0x02}, + {0x52ca, 0x01}, + {0x52cb, 0x01}, + {0x52cc, 0x04}, + {0x52cd, 0x02}, + {0x52ce, 0x01}, + {0x52cf, 0x01}, + {0x52d0, 0x03}, + {0x52d1, 0x08}, + {0x52d2, 0x0c}, + {0x54c6, 0x00}, + {0x54c7, 0x12}, + {0x54c8, 0x04}, + {0x54c9, 0x02}, + {0x54ca, 0x01}, + {0x54cb, 0x01}, + {0x54cc, 0x04}, + {0x54cd, 0x02}, + {0x54ce, 0x01}, + {0x54cf, 0x01}, + {0x54d0, 0x03}, + {0x54d1, 0x08}, + {0x54d2, 0x0c}, + {0x56c6, 0x00}, + {0x56c7, 0x12}, + {0x56c8, 0x04}, + {0x56c9, 0x02}, + {0x56ca, 0x01}, + {0x56cb, 0x01}, + {0x56cc, 0x04}, + {0x56cd, 0x02}, + {0x56ce, 0x01}, + {0x56cf, 0x01}, + {0x56d0, 0x03}, + {0x56d1, 0x08}, + {0x56d2, 0x0c}, + {0x58c6, 0x00}, + {0x58c7, 0x12}, + {0x58c8, 0x04}, + {0x58c9, 0x02}, + {0x58ca, 0x01}, + {0x58cb, 0x01}, + {0x58cc, 0x04}, + {0x58cd, 0x02}, + {0x58ce, 0x01}, + {0x58cf, 0x01}, + {0x58d0, 0x03}, + {0x58d1, 0x08}, + {0x58d2, 0x0c}, + {0x5004, 0x1e}, + {0x610a, 0x07}, + {0x610b, 0x80}, + {0x610c, 0x05}, + {0x610d, 0x00}, + {0x6102, 0x3f}, + {0x5d62, 0x07}, + {0x5d40, 0x02}, + {0x5d41, 0x01}, + {0x5d63, 0x08}, + {0x5d64, 0x01}, + {0x5d65, 0xff}, + {0x5d56, 0x00}, + {0x5d57, 0x20}, + {0x5d58, 0x00}, + {0x5d59, 0x20}, + {0x5d5a, 0x00}, + {0x5d5b, 0x0c}, + {0x5d5c, 0x02}, + {0x5d5d, 0x40}, + {0x5d5e, 0x02}, + {0x5d5f, 0x40}, + {0x5d60, 0x03}, + {0x5d61, 0x40}, + {0x5d4a, 0x02}, + {0x5d4b, 0x40}, + {0x5d4c, 0x02}, + {0x5d4d, 0x40}, + {0x5d4e, 0x02}, + {0x5d4f, 0x40}, + {0x5d50, 0x18}, + {0x5d51, 0x80}, + {0x5d52, 0x18}, + {0x5d53, 0x80}, + {0x5d54, 0x18}, + {0x5d55, 0x80}, + {0x5d46, 0x20}, + {0x5d47, 0x00}, + {0x5d48, 0x22}, + {0x5d49, 0x00}, + {0x5d42, 0x20}, + {0x5d43, 0x00}, + {0x5d44, 0x22}, + {0x5d45, 0x00}, + {0x5b40, 0x01}, + {0x5b41, 0x14}, + {0x5b42, 0x0f}, + {0x5b43, 0xf1}, + {0x5b44, 0x0f}, + {0x5b45, 0xfc}, + {0x5b46, 0x0f}, + {0x5b47, 0xf0}, + {0x5b48, 0x01}, + {0x5b49, 0x02}, + {0x5b4a, 0x00}, + {0x5b4b, 0x0e}, + {0x5b4c, 0x0f}, + {0x5b4d, 0xf1}, + {0x5b4e, 0x0f}, + {0x5b4f, 0xe4}, + {0x5b50, 0x01}, + {0x5b51, 0x2b}, + {0x5b52, 0x01}, + {0x5b53, 0x0f}, + {0x5b54, 0x0f}, + {0x5b55, 0xf1}, + {0x5b56, 0x00}, + {0x5b57, 0x00}, + {0x5b58, 0x0f}, + {0x5b59, 0xeb}, + {0x5b5a, 0x01}, + {0x5b5b, 0x04}, + {0x5b5c, 0x00}, + {0x5b5d, 0x11}, + {0x5b5e, 0x0f}, + {0x5b5f, 0xc8}, + {0x5b60, 0x0f}, + {0x5b61, 0xbd}, + {0x5b62, 0x01}, + {0x5b63, 0x7b}, + {0x5b64, 0x01}, + {0x5b65, 0x11}, + {0x5b66, 0x0f}, + {0x5b67, 0xf4}, + {0x5b68, 0x0f}, + {0x5b69, 0xfb}, + {0x5b6a, 0x0f}, + {0x5b6b, 0xf2}, + {0x5b6c, 0x01}, + {0x5b6d, 0x04}, + {0x5b6e, 0x00}, + {0x5b6f, 0x0a}, + {0x5b70, 0x0f}, + {0x5b71, 0xea}, + {0x5b72, 0x0f}, + {0x5b73, 0xc7}, + {0x5b74, 0x01}, + {0x5b75, 0x4e}, + {0x5b78, 0x00}, + {0x5b79, 0x4c}, + {0x5b7a, 0x00}, + {0x5b7b, 0xb9}, + {0x5b7c, 0x01}, + {0x5b7d, 0x38}, + {0x5b7e, 0x01}, + {0x5280, 0x05}, + {0x5281, 0xf2}, + {0x5282, 0x04}, + {0x5283, 0x00}, + {0x5284, 0x04}, + {0x5285, 0x00}, + {0x5286, 0x07}, + {0x5287, 0x3f}, + {0x5480, 0x05}, + {0x5481, 0xf2}, + {0x5482, 0x04}, + {0x5483, 0x00}, + {0x5484, 0x04}, + {0x5485, 0x00}, + {0x5486, 0x07}, + {0x5487, 0x3f}, + {0x5680, 0x06}, + {0x5681, 0x58}, + {0x5682, 0x04}, + {0x5683, 0x00}, + {0x5684, 0x04}, + {0x5685, 0x00}, + {0x5686, 0x06}, + {0x5687, 0x5f}, + {0x5880, 0x05}, + {0x5881, 0xf2}, + {0x5882, 0x04}, + {0x5883, 0x00}, + {0x5884, 0x04}, + {0x5885, 0x00}, + {0x5886, 0x07}, + {0x5887, 0x3f}, + {0x4221, 0x13}, + {0x380e, 0x03}, + {0x380f, 0x37}, + {0x3501, 0x01}, + {0x3502, 0xc8}, + {0x3541, 0x01}, + {0x3542, 0xc8}, + {0x35c1, 0x00}, + {0x35c2, 0x01}, + {0x420e, 0x66}, + {0x420f, 0x5d}, + {0x4210, 0xa8}, + {0x4211, 0x55}, + {0x507a, 0x5f}, + {0x507b, 0x46}, + {0x4f00, 0x00}, + {0x4f01, 0x01}, + {0x4f02, 0x80}, + {0x4f03, 0x2c}, + {0x4f04, 0xf8}, + {0x0100, 0x01}, + {REG_NULL, 0xff}, +}; + +static const struct regval ox03c10_1920x1080_30fps_HDR3_LFM_PWL16_mipi996[] = { + {0x0103, 0x01}, + {0x0107, 0x01}, + {0x4d5a, 0x1c}, + {0x4d09, 0xff}, + {0x4d09, 0xdf}, + {0x3208, 0x04}, + {0x4620, 0x04}, + {0x3208, 0x14}, + {0x3208, 0x05}, + {0x4620, 0x04}, + {0x3208, 0x15}, + {0x3208, 0x02}, + {0x3507, 0x00}, + {0x3208, 0x12}, + {0x3208, 0xa2}, + {0x0301, 0xc8}, + {0x0303, 0x01}, + {0x0304, 0x01}, + {0x0305, 0x2c}, + {0x0306, 0x04}, + {0x0307, 0x01}, + {0x0316, 0x00}, + {0x0317, 0x00}, + {0x0318, 0x00}, + {0x0323, 0x05}, + {0x0324, 0x01}, + {0x0325, 0x2c}, + {0x0400, 0xe0}, + {0x0401, 0x80}, + {0x0403, 0xde}, + {0x0404, 0x34}, + {0x0405, 0x3b}, + {0x0406, 0xde}, + {0x0407, 0x08}, + {0x0408, 0xe0}, + {0x0409, 0x7f}, + {0x040a, 0xde}, + {0x040b, 0x34}, + {0x040c, 0x47}, + {0x040d, 0xd8}, + {0x040e, 0x08}, + {0x2803, 0xfe}, + {0x280b, 0x00}, + {0x280c, 0x79}, + {0x3001, 0x03}, + {0x3002, 0xf8}, + {0x3005, 0x80}, + {0x3007, 0x01}, + {0x3008, 0x80}, + {0x3012, 0x41}, + {0x3020, 0x05}, + {0x3700, 0x28}, + {0x3701, 0x15}, + {0x3702, 0x19}, + {0x3703, 0x23}, + {0x3704, 0x0a}, + {0x3705, 0x00}, + {0x3706, 0x3e}, + {0x3707, 0x0d}, + {0x3708, 0x50}, + {0x3709, 0x5a}, + {0x370a, 0x00}, + {0x370b, 0x96}, + {0x3711, 0x11}, + {0x3712, 0x13}, + {0x3717, 0x02}, + {0x3718, 0x73}, + {0x372c, 0x40}, + {0x3733, 0x01}, + {0x3738, 0x36}, + {0x3739, 0x36}, + {0x373a, 0x25}, + {0x373b, 0x25}, + {0x373f, 0x21}, + {0x3740, 0x21}, + {0x3741, 0x21}, + {0x3742, 0x21}, + {0x3747, 0x28}, + {0x3748, 0x28}, + {0x3749, 0x19}, + {0x3755, 0x1a}, + {0x3756, 0x0a}, + {0x3757, 0x1c}, + {0x3765, 0x19}, + {0x3766, 0x05}, + {0x3767, 0x05}, + {0x3768, 0x13}, + {0x376c, 0x07}, + {0x3778, 0x20}, + {0x377c, 0xc8}, + {0x3781, 0x02}, + {0x3783, 0x02}, + {0x379c, 0x58}, + {0x379e, 0x00}, + {0x379f, 0x00}, + {0x37a0, 0x00}, + {0x37bc, 0x22}, + {0x37c0, 0x01}, + {0x37c4, 0x3e}, + {0x37c5, 0x3e}, + {0x37c6, 0x2a}, + {0x37c7, 0x28}, + {0x37c8, 0x02}, + {0x37c9, 0x12}, + {0x37cb, 0x29}, + {0x37cd, 0x29}, + {0x37d2, 0x00}, + {0x37d3, 0x73}, + {0x37d6, 0x00}, + {0x37d7, 0x6b}, + {0x37dc, 0x00}, + {0x37df, 0x54}, + {0x37e2, 0x00}, + {0x37e3, 0x00}, + {0x37f8, 0x00}, + {0x37f9, 0x01}, + {0x37fa, 0x00}, + {0x37fb, 0x19}, + {0x3c03, 0x01}, + {0x3c04, 0x01}, + {0x3c06, 0x21}, + {0x3c08, 0x01}, + {0x3c09, 0x01}, + {0x3c0a, 0x01}, + {0x3c0b, 0x21}, + {0x3c13, 0x21}, + {0x3c14, 0x82}, + {0x3c16, 0x13}, + {0x3c21, 0x00}, + {0x3c22, 0xf3}, + {0x3c37, 0x12}, + {0x3c38, 0x31}, + {0x3c3c, 0x00}, + {0x3c3d, 0x03}, + {0x3c44, 0x16}, + {0x3c5c, 0x8a}, + {0x3c5f, 0x03}, + {0x3c61, 0x80}, + {0x3c6f, 0x2b}, + {0x3c70, 0x5f}, + {0x3c71, 0x2c}, + {0x3c72, 0x2c}, + {0x3c73, 0x2c}, + {0x3c76, 0x12}, + {0x3182, 0x12}, + {0x320e, 0x00}, + {0x320f, 0x00}, + {0x3211, 0x61}, + {0x3215, 0xcd}, + {0x3219, 0x08}, + {0x3506, 0x30}, + {0x350a, 0x01}, + {0x350b, 0x00}, + {0x350c, 0x00}, + {0x3586, 0x60}, + {0x358a, 0x01}, + {0x358b, 0x00}, + {0x358c, 0x00}, + {0x3541, 0x00}, + {0x3542, 0x04}, + {0x3548, 0x04}, + {0x3549, 0x40}, + {0x354a, 0x01}, + {0x354b, 0x00}, + {0x354c, 0x00}, + {0x35c1, 0x00}, + {0x35c2, 0x02}, + {0x35c6, 0xa0}, + {0x3600, 0x8f}, + {0x3605, 0x16}, + {0x3609, 0xf0}, + {0x360a, 0x01}, + {0x360e, 0x1d}, + {0x360f, 0x10}, + {0x3610, 0x70}, + {0x3611, 0x3a}, + {0x3612, 0x28}, + {0x361a, 0x29}, + {0x361b, 0x6c}, + {0x361c, 0x0b}, + {0x361d, 0x00}, + {0x361e, 0xfc}, + {0x362a, 0x00}, + {0x364d, 0x0f}, + {0x364e, 0x18}, + {0x364f, 0x12}, + {0x3653, 0x1c}, + {0x3654, 0x00}, + {0x3655, 0x1f}, + {0x3656, 0x1f}, + {0x3657, 0x0c}, + {0x3658, 0x0a}, + {0x3659, 0x14}, + {0x365a, 0x18}, + {0x365b, 0x14}, + {0x365c, 0x10}, + {0x365e, 0x12}, + {0x3674, 0x08}, + {0x3677, 0x3a}, + {0x3678, 0x3a}, + {0x3679, 0x19}, + {0x3802, 0x00}, + {0x3803, 0x04}, + {0x3806, 0x05}, + {0x3807, 0x0b}, + {0x3808, 0x07}, + {0x3809, 0x80}, + {0x380a, 0x05}, + {0x380b, 0x00}, + {0x380c, 0x04}, + {0x380d, 0xd3}, + {0x380e, 0x02}, + {0x380f, 0xae}, + {0x3810, 0x00}, + {0x3811, 0x08}, + {0x3812, 0x00}, + {0x3813, 0x04}, + {0x3816, 0x01}, + {0x3817, 0x01}, + {0x381c, 0x18}, + {0x381e, 0x01}, + {0x381f, 0x01}, + {0x3820, 0x00}, + {0x3821, 0x19}, + {0x3832, 0x00}, + {0x3834, 0x00}, + {0x384c, 0x02}, + {0x384d, 0x53}, + {0x3850, 0x00}, + {0x3851, 0x42}, + {0x3852, 0x00}, + {0x3853, 0x40}, + {0x3858, 0x04}, + {0x388c, 0x02}, + {0x388d, 0x71}, + {0x3b40, 0x05}, + {0x3b41, 0x40}, + {0x3b42, 0x00}, + {0x3b43, 0x90}, + {0x3b44, 0x00}, + {0x3b45, 0x20}, + {0x3b46, 0x00}, + {0x3b47, 0x20}, + {0x3b48, 0x19}, + {0x3b49, 0x12}, + {0x3b4a, 0x16}, + {0x3b4b, 0x2e}, + {0x3b4c, 0x00}, + {0x3b4d, 0x00}, + {0x3b86, 0x00}, + {0x3b87, 0x34}, + {0x3b88, 0x00}, + {0x3b89, 0x08}, + {0x3b8a, 0x05}, + {0x3b8b, 0x00}, + {0x3b8c, 0x07}, + {0x3b8d, 0x80}, + {0x3b8e, 0x00}, + {0x3b8f, 0x00}, + {0x3b92, 0x05}, + {0x3b93, 0x00}, + {0x3b94, 0x07}, + {0x3b95, 0x80}, + {0x3b9e, 0x09}, + {0x3d82, 0x73}, + {0x3d85, 0x05}, + {0x3d8a, 0x03}, + {0x3d8b, 0xff}, + {0x3d99, 0x00}, + {0x3d9a, 0x9f}, + {0x3d9b, 0x00}, + {0x3d9c, 0xa0}, + {0x3da4, 0x00}, + {0x3da7, 0x50}, + {0x420e, 0xff}, + {0x420f, 0xff}, + {0x4210, 0xff}, + {0x4211, 0xff}, + {0x421e, 0x02}, + {0x421f, 0x45}, + {0x4220, 0xe1}, + {0x4221, 0x05}, + {0x4301, 0x0f}, + {0x4307, 0x03}, + {0x4308, 0x13}, + {0x430a, 0x53}, + {0x430d, 0x93}, + {0x430f, 0x57}, + {0x4310, 0x95}, + {0x4311, 0x16}, + {0x4316, 0x00}, + {0x4317, 0x08}, + {0x4319, 0x09}, + {0x431a, 0x00}, + {0x431b, 0x22}, + {0x431d, 0x2a}, + {0x431e, 0x11}, + {0x431f, 0x30}, + {0x4320, 0x59}, + {0x4323, 0x80}, + {0x4324, 0x00}, + {0x4503, 0x4e}, + {0x4505, 0x00}, + {0x4509, 0x00}, + {0x450a, 0x00}, + {0x4580, 0xf8}, + {0x4583, 0x07}, + {0x4584, 0x6a}, + {0x4585, 0x08}, + {0x4586, 0x05}, + {0x4587, 0x04}, + {0x4588, 0x73}, + {0x4589, 0x05}, + {0x458a, 0x1f}, + {0x458b, 0x02}, + {0x458c, 0xdc}, + {0x458d, 0x03}, + {0x458e, 0x02}, + {0x4597, 0x07}, + {0x4598, 0x40}, + {0x4599, 0x0e}, + {0x459a, 0x0e}, + {0x459b, 0xfb}, + {0x459c, 0xf3}, + {0x4602, 0x00}, + {0x4603, 0x13}, + {0x4604, 0x00}, + {0x4609, 0x0a}, + {0x460a, 0x00}, + {0x4610, 0x00}, + {0x4611, 0x70}, + {0x4612, 0x00}, + {0x4613, 0x0c}, + {0x4614, 0x00}, + {0x4615, 0x70}, + {0x4616, 0x00}, + {0x4617, 0x0c}, + {0x4800, 0x04}, + {0x480a, 0x22}, + {0x4813, 0xe4}, + {0x4814, 0x2a}, + {0x4837, 0x0d}, + {0x484b, 0x47}, + {0x484f, 0x40}, + {0x4887, 0x51}, + {0x4d00, 0x4a}, + {0x4d01, 0x18}, + {0x4d05, 0xff}, + {0x4d06, 0x88}, + {0x4d08, 0x63}, + {0x4d09, 0xdf}, + {0x4d15, 0x7d}, + {0x4d1a, 0x20}, + {0x4d30, 0x0a}, + {0x4d31, 0x00}, + {0x4d34, 0x7d}, + {0x4d3c, 0x7d}, + {0x4f00, 0x3f}, + {0x4f01, 0xff}, + {0x4f02, 0xff}, + {0x4f03, 0x2c}, + {0x4f04, 0xe0}, + {0x6a00, 0x00}, + {0x6a01, 0x20}, + {0x6a02, 0x00}, + {0x6a03, 0x20}, + {0x6a04, 0x02}, + {0x6a05, 0x80}, + {0x6a06, 0x01}, + {0x6a07, 0xe0}, + {0x6a08, 0xcf}, + {0x6a09, 0x01}, + {0x6a0a, 0x40}, + {0x6a20, 0x00}, + {0x6a21, 0x02}, + {0x6a22, 0x00}, + {0x6a23, 0x00}, + {0x6a24, 0x00}, + {0x6a25, 0xf0}, + {0x6a26, 0x00}, + {0x6a27, 0x00}, + {0x6a28, 0x00}, + {0x5000, 0x8f}, + {0x5001, 0x65}, + {0x5002, 0x7f}, + {0x5003, 0x6a}, + {0x5004, 0x3e}, + {0x5005, 0x1e}, + {0x5006, 0x1e}, + {0x5007, 0x1e}, + {0x5008, 0x00}, + {0x500c, 0x00}, + {0x502c, 0x00}, + {0x502e, 0x00}, + {0x502f, 0x00}, + {0x504b, 0x00}, + {0x5053, 0x00}, + {0x505b, 0x00}, + {0x5063, 0x00}, + {0x5070, 0x00}, + {0x5074, 0x04}, + {0x507a, 0x00}, + {0x507b, 0x00}, + {0x5500, 0x02}, + {0x5700, 0x02}, + {0x5900, 0x02}, + {0x6007, 0x04}, + {0x6008, 0x05}, + {0x6009, 0x02}, + {0x600b, 0x08}, + {0x600c, 0x07}, + {0x600d, 0x88}, + {0x6016, 0x00}, + {0x6027, 0x04}, + {0x6028, 0x05}, + {0x6029, 0x02}, + {0x602b, 0x08}, + {0x602c, 0x07}, + {0x602d, 0x88}, + {0x6047, 0x04}, + {0x6048, 0x05}, + {0x6049, 0x02}, + {0x604b, 0x08}, + {0x604c, 0x07}, + {0x604d, 0x88}, + {0x6067, 0x04}, + {0x6068, 0x05}, + {0x6069, 0x02}, + {0x606b, 0x08}, + {0x606c, 0x07}, + {0x606d, 0x88}, + {0x6087, 0x04}, + {0x6088, 0x05}, + {0x6089, 0x02}, + {0x608b, 0x08}, + {0x608c, 0x07}, + {0x608d, 0x88}, + {0x5e00, 0x02}, + {0x5e01, 0x0b}, + {0x5e02, 0x00}, + {0x5e03, 0x00}, + {0x5e04, 0x00}, + {0x5e05, 0x0b}, + {0x5e06, 0x0c}, + {0x5e07, 0x0c}, + {0x5e08, 0x0c}, + {0x5e09, 0x0c}, + {0x5e0a, 0x0c}, + {0x5e0b, 0x0d}, + {0x5e0c, 0x0d}, + {0x5e0d, 0x0d}, + {0x5e0e, 0x0d}, + {0x5e0f, 0x0d}, + {0x5e10, 0x0d}, + {0x5e11, 0x0d}, + {0x5e12, 0x0e}, + {0x5e13, 0x0e}, + {0x5e14, 0x0e}, + {0x5e15, 0x0e}, + {0x5e16, 0x0e}, + {0x5e17, 0x0e}, + {0x5e18, 0x0e}, + {0x5e19, 0x10}, + {0x5e1a, 0x11}, + {0x5e1b, 0x11}, + {0x5e1c, 0x12}, + {0x5e1d, 0x12}, + {0x5e1e, 0x14}, + {0x5e1f, 0x15}, + {0x5e20, 0x16}, + {0x5e21, 0x17}, + {0x5e22, 0x00}, + {0x5e23, 0x08}, + {0x5e26, 0x00}, + {0x5e27, 0x00}, + {0x5e29, 0x00}, + {0x5e2a, 0x00}, + {0x5e2c, 0x00}, + {0x5e2d, 0x00}, + {0x5e2f, 0x03}, + {0x5e30, 0xff}, + {0x5e32, 0x04}, + {0x5e33, 0x00}, + {0x5e34, 0x00}, + {0x5e35, 0x04}, + {0x5e36, 0x00}, + {0x5e37, 0x00}, + {0x5e38, 0x04}, + {0x5e39, 0x00}, + {0x5e3a, 0x00}, + {0x5e3b, 0x04}, + {0x5e3c, 0x00}, + {0x5e3d, 0x00}, + {0x5e3e, 0x04}, + {0x5e3f, 0x00}, + {0x5e40, 0x00}, + {0x5e41, 0x06}, + {0x5e42, 0x00}, + {0x5e43, 0x00}, + {0x5e44, 0x06}, + {0x5e45, 0x00}, + {0x5e46, 0x00}, + {0x5e47, 0x06}, + {0x5e48, 0x00}, + {0x5e49, 0x00}, + {0x5e4a, 0x06}, + {0x5e4b, 0x00}, + {0x5e4c, 0x00}, + {0x5e4d, 0x06}, + {0x5e4e, 0x00}, + {0x5e50, 0x06}, + {0x5e51, 0x00}, + {0x5e53, 0x06}, + {0x5e54, 0x00}, + {0x5e56, 0x08}, + {0x5e57, 0x00}, + {0x5e59, 0x08}, + {0x5e5a, 0x00}, + {0x5e5c, 0x08}, + {0x5e5d, 0x00}, + {0x5e5f, 0x08}, + {0x5e60, 0x00}, + {0x5e62, 0x08}, + {0x5e63, 0x00}, + {0x5e65, 0x08}, + {0x5e66, 0x00}, + {0x5e68, 0x08}, + {0x5e69, 0x00}, + {0x5e6b, 0x16}, + {0x5e6c, 0x00}, + {0x5e6e, 0x20}, + {0x5e6f, 0x00}, + {0x5e71, 0x18}, + {0x5e72, 0x00}, + {0x5e74, 0x18}, + {0x5e75, 0x00}, + {0x5e77, 0x17}, + {0x5e78, 0xff}, + {0x5e7a, 0x00}, + {0x5e7b, 0x00}, + {0x5e7d, 0x00}, + {0x5e7e, 0x00}, + {0x5e80, 0x00}, + {0x5e81, 0x00}, + {0x5e83, 0x00}, + {0x5e84, 0x00}, + {0x5f00, 0x02}, + {0x5f01, 0x08}, + {0x5f02, 0x09}, + {0x5f03, 0x0a}, + {0x5f04, 0x0b}, + {0x5f05, 0x0c}, + {0x5f06, 0x0c}, + {0x5f07, 0x0c}, + {0x5f08, 0x0c}, + {0x5f09, 0x0c}, + {0x5f0a, 0x0d}, + {0x5f0b, 0x0d}, + {0x5f0c, 0x0d}, + {0x5f0d, 0x0d}, + {0x5f0e, 0x0d}, + {0x5f0f, 0x0e}, + {0x5f10, 0x0e}, + {0x5f11, 0x0e}, + {0x5f12, 0x0e}, + {0x5f13, 0x0f}, + {0x5f14, 0x0f}, + {0x5f15, 0x10}, + {0x5f16, 0x11}, + {0x5f17, 0x11}, + {0x5f18, 0x12}, + {0x5f19, 0x12}, + {0x5f1a, 0x13}, + {0x5f1b, 0x13}, + {0x5f1c, 0x14}, + {0x5f1d, 0x14}, + {0x5f1e, 0x16}, + {0x5f1f, 0x16}, + {0x5f20, 0x16}, + {0x5f21, 0x08}, + {0x5f22, 0x00}, + {0x5f23, 0x01}, + {0x5f26, 0x02}, + {0x5f27, 0x00}, + {0x5f29, 0x02}, + {0x5f2a, 0x00}, + {0x5f2c, 0x02}, + {0x5f2d, 0x00}, + {0x5f2f, 0x02}, + {0x5f30, 0x00}, + {0x5f32, 0x02}, + {0x5f33, 0x00}, + {0x5f34, 0x00}, + {0x5f35, 0x02}, + {0x5f36, 0x00}, + {0x5f37, 0x00}, + {0x5f38, 0x02}, + {0x5f39, 0x00}, + {0x5f3a, 0x00}, + {0x5f3b, 0x02}, + {0x5f3c, 0x00}, + {0x5f3d, 0x00}, + {0x5f3e, 0x02}, + {0x5f3f, 0x00}, + {0x5f40, 0x00}, + {0x5f41, 0x02}, + {0x5f42, 0x00}, + {0x5f43, 0x00}, + {0x5f44, 0x02}, + {0x5f45, 0x00}, + {0x5f46, 0x00}, + {0x5f47, 0x04}, + {0x5f48, 0x00}, + {0x5f49, 0x00}, + {0x5f4a, 0x04}, + {0x5f4b, 0x00}, + {0x5f4c, 0x00}, + {0x5f4d, 0x04}, + {0x5f4e, 0x00}, + {0x5f50, 0x04}, + {0x5f51, 0x00}, + {0x5f53, 0x04}, + {0x5f54, 0x00}, + {0x5f56, 0x04}, + {0x5f57, 0x00}, + {0x5f59, 0x04}, + {0x5f5a, 0x00}, + {0x5f5c, 0x04}, + {0x5f5d, 0x00}, + {0x5f5f, 0x08}, + {0x5f60, 0x00}, + {0x5f62, 0x08}, + {0x5f63, 0x00}, + {0x5f65, 0x08}, + {0x5f66, 0x00}, + {0x5f68, 0x08}, + {0x5f69, 0x00}, + {0x5f6b, 0x08}, + {0x5f6c, 0x00}, + {0x5f6e, 0x10}, + {0x5f6f, 0x00}, + {0x5f71, 0x10}, + {0x5f72, 0x00}, + {0x5f74, 0x10}, + {0x5f75, 0x00}, + {0x5f77, 0x10}, + {0x5f78, 0x00}, + {0x5f7a, 0x20}, + {0x5f7b, 0x00}, + {0x5f7d, 0x20}, + {0x5f7e, 0x00}, + {0x5f80, 0x20}, + {0x5f81, 0x00}, + {0x5f83, 0x00}, + {0x5f84, 0xff}, + {0x5240, 0x0f}, + {0x5243, 0x00}, + {0x5244, 0x00}, + {0x5245, 0x00}, + {0x5246, 0x00}, + {0x5247, 0x00}, + {0x5248, 0x00}, + {0x5249, 0x00}, + {0x5440, 0x0f}, + {0x5443, 0x00}, + {0x5445, 0x00}, + {0x5447, 0x00}, + {0x5448, 0x00}, + {0x5449, 0x00}, + {0x5640, 0x0f}, + {0x5642, 0x00}, + {0x5643, 0x00}, + {0x5644, 0x00}, + {0x5645, 0x00}, + {0x5646, 0x00}, + {0x5647, 0x00}, + {0x5649, 0x00}, + {0x5840, 0x0f}, + {0x5842, 0x00}, + {0x5843, 0x00}, + {0x5845, 0x00}, + {0x5846, 0x00}, + {0x5847, 0x00}, + {0x5848, 0x00}, + {0x5849, 0x00}, + {0x4001, 0x2b}, + {0x4008, 0x02}, + {0x4009, 0x03}, + {0x4018, 0x12}, + {0x4022, 0x40}, + {0x4023, 0x20}, + {0x4026, 0x00}, + {0x4027, 0x40}, + {0x4028, 0x00}, + {0x4029, 0x40}, + {0x402a, 0x00}, + {0x402b, 0x40}, + {0x402c, 0x00}, + {0x402d, 0x40}, + {0x405e, 0x00}, + {0x405f, 0x00}, + {0x4060, 0x00}, + {0x4061, 0x00}, + {0x4062, 0x00}, + {0x4063, 0x00}, + {0x4064, 0x00}, + {0x4065, 0x00}, + {0x4066, 0x00}, + {0x4067, 0x00}, + {0x4068, 0x00}, + {0x4069, 0x00}, + {0x406a, 0x00}, + {0x406b, 0x00}, + {0x406c, 0x00}, + {0x406d, 0x00}, + {0x406e, 0x00}, + {0x406f, 0x00}, + {0x4070, 0x00}, + {0x4071, 0x00}, + {0x4072, 0x00}, + {0x4073, 0x00}, + {0x4074, 0x00}, + {0x4075, 0x00}, + {0x4076, 0x00}, + {0x4077, 0x00}, + {0x4078, 0x00}, + {0x4079, 0x00}, + {0x407a, 0x00}, + {0x407b, 0x00}, + {0x407c, 0x00}, + {0x407d, 0x00}, + {0x407e, 0xcc}, + {0x407f, 0x18}, + {0x4080, 0xff}, + {0x4081, 0xff}, + {0x4082, 0x01}, + {0x4083, 0x53}, + {0x4084, 0x01}, + {0x4085, 0x2b}, + {0x4086, 0x00}, + {0x4087, 0xb3}, + {0x4640, 0x40}, + {0x4641, 0x11}, + {0x4642, 0x0e}, + {0x4643, 0xee}, + {0x4646, 0x0f}, + {0x4648, 0x00}, + {0x4649, 0x03}, + {0x4f04, 0xf8}, + {0x4d09, 0xff}, + {0x4d09, 0xdf}, + {0x5003, 0x7a}, + {0x5b80, 0x08}, + {0x5c00, 0x08}, + {0x5c80, 0x00}, + {0x5bbe, 0x12}, + {0x5c3e, 0x12}, + {0x5cbe, 0x12}, + {0x5b8a, 0x80}, + {0x5b8b, 0x80}, + {0x5b8c, 0x80}, + {0x5b8d, 0x80}, + {0x5b8e, 0x60}, + {0x5b8f, 0x80}, + {0x5b90, 0x80}, + {0x5b91, 0x80}, + {0x5b92, 0x80}, + {0x5b93, 0x20}, + {0x5b94, 0x80}, + {0x5b95, 0x80}, + {0x5b96, 0x80}, + {0x5b97, 0x20}, + {0x5b98, 0x00}, + {0x5b99, 0x80}, + {0x5b9a, 0x40}, + {0x5b9b, 0x20}, + {0x5b9c, 0x00}, + {0x5b9d, 0x00}, + {0x5b9e, 0x80}, + {0x5b9f, 0x00}, + {0x5ba0, 0x00}, + {0x5ba1, 0x00}, + {0x5ba2, 0x00}, + {0x5ba3, 0x00}, + {0x5ba4, 0x00}, + {0x5ba5, 0x00}, + {0x5ba6, 0x00}, + {0x5ba7, 0x00}, + {0x5ba8, 0x02}, + {0x5ba9, 0x00}, + {0x5baa, 0x02}, + {0x5bab, 0x76}, + {0x5bac, 0x03}, + {0x5bad, 0x08}, + {0x5bae, 0x00}, + {0x5baf, 0x80}, + {0x5bb0, 0x00}, + {0x5bb1, 0xc0}, + {0x5bb2, 0x01}, + {0x5bb3, 0x00}, + {0x5c0a, 0x80}, + {0x5c0b, 0x80}, + {0x5c0c, 0x80}, + {0x5c0d, 0x80}, + {0x5c0e, 0x60}, + {0x5c0f, 0x80}, + {0x5c10, 0x80}, + {0x5c11, 0x80}, + {0x5c12, 0x60}, + {0x5c13, 0x20}, + {0x5c14, 0x80}, + {0x5c15, 0x80}, + {0x5c16, 0x80}, + {0x5c17, 0x20}, + {0x5c18, 0x00}, + {0x5c19, 0x80}, + {0x5c1a, 0x40}, + {0x5c1b, 0x20}, + {0x5c1c, 0x00}, + {0x5c1d, 0x00}, + {0x5c1e, 0x80}, + {0x5c1f, 0x00}, + {0x5c20, 0x00}, + {0x5c21, 0x00}, + {0x5c22, 0x00}, + {0x5c23, 0x00}, + {0x5c24, 0x00}, + {0x5c25, 0x00}, + {0x5c26, 0x00}, + {0x5c27, 0x00}, + {0x5c28, 0x02}, + {0x5c29, 0x00}, + {0x5c2a, 0x02}, + {0x5c2b, 0x76}, + {0x5c2c, 0x03}, + {0x5c2d, 0x08}, + {0x5c2e, 0x00}, + {0x5c2f, 0x80}, + {0x5c30, 0x00}, + {0x5c31, 0xc0}, + {0x5c32, 0x01}, + {0x5c33, 0x00}, + {0x5c8a, 0x80}, + {0x5c8b, 0x80}, + {0x5c8c, 0x80}, + {0x5c8d, 0x80}, + {0x5c8e, 0x80}, + {0x5c8f, 0x80}, + {0x5c90, 0x80}, + {0x5c91, 0x80}, + {0x5c92, 0x80}, + {0x5c93, 0x60}, + {0x5c94, 0x80}, + {0x5c95, 0x80}, + {0x5c96, 0x80}, + {0x5c97, 0x60}, + {0x5c98, 0x40}, + {0x5c99, 0x80}, + {0x5c9a, 0x80}, + {0x5c9b, 0x80}, + {0x5c9c, 0x40}, + {0x5c9d, 0x00}, + {0x5c9e, 0x80}, + {0x5c9f, 0x80}, + {0x5ca0, 0x80}, + {0x5ca1, 0x20}, + {0x5ca2, 0x00}, + {0x5ca3, 0x80}, + {0x5ca4, 0x80}, + {0x5ca5, 0x00}, + {0x5ca6, 0x00}, + {0x5ca7, 0x00}, + {0x5ca8, 0x01}, + {0x5ca9, 0x00}, + {0x5caa, 0x02}, + {0x5cab, 0x00}, + {0x5cac, 0x03}, + {0x5cad, 0x08}, + {0x5cae, 0x01}, + {0x5caf, 0x00}, + {0x5cb0, 0x02}, + {0x5cb1, 0x00}, + {0x5cb2, 0x03}, + {0x5cb3, 0x08}, + {0x5be7, 0x80}, + {0x5bc9, 0x80}, + {0x5bca, 0x80}, + {0x5bcb, 0x80}, + {0x5bcc, 0x80}, + {0x5bcd, 0x80}, + {0x5bce, 0x80}, + {0x5bcf, 0x80}, + {0x5bd0, 0x80}, + {0x5bd1, 0x80}, + {0x5bd2, 0x20}, + {0x5bd3, 0x80}, + {0x5bd4, 0x40}, + {0x5bd5, 0x20}, + {0x5bd6, 0x00}, + {0x5bd7, 0x00}, + {0x5bd8, 0x00}, + {0x5bd9, 0x00}, + {0x5bda, 0x00}, + {0x5bdb, 0x00}, + {0x5bdc, 0x00}, + {0x5bdd, 0x00}, + {0x5bde, 0x00}, + {0x5bdf, 0x00}, + {0x5be0, 0x00}, + {0x5be1, 0x00}, + {0x5be2, 0x00}, + {0x5be3, 0x00}, + {0x5be4, 0x00}, + {0x5be5, 0x00}, + {0x5be6, 0x00}, + {0x5c49, 0x80}, + {0x5c4a, 0x80}, + {0x5c4b, 0x80}, + {0x5c4c, 0x80}, + {0x5c4d, 0x40}, + {0x5c4e, 0x80}, + {0x5c4f, 0x80}, + {0x5c50, 0x80}, + {0x5c51, 0x60}, + {0x5c52, 0x20}, + {0x5c53, 0x80}, + {0x5c54, 0x80}, + {0x5c55, 0x80}, + {0x5c56, 0x20}, + {0x5c57, 0x00}, + {0x5c58, 0x80}, + {0x5c59, 0x40}, + {0x5c5a, 0x20}, + {0x5c5b, 0x00}, + {0x5c5c, 0x00}, + {0x5c5d, 0x80}, + {0x5c5e, 0x00}, + {0x5c5f, 0x00}, + {0x5c60, 0x00}, + {0x5c61, 0x00}, + {0x5c62, 0x00}, + {0x5c63, 0x00}, + {0x5c64, 0x00}, + {0x5c65, 0x00}, + {0x5c66, 0x00}, + {0x5cc9, 0x80}, + {0x5cca, 0x80}, + {0x5ccb, 0x80}, + {0x5ccc, 0x80}, + {0x5ccd, 0x80}, + {0x5cce, 0x80}, + {0x5ccf, 0x80}, + {0x5cd0, 0x80}, + {0x5cd1, 0x80}, + {0x5cd2, 0x60}, + {0x5cd3, 0x80}, + {0x5cd4, 0x80}, + {0x5cd5, 0x80}, + {0x5cd6, 0x60}, + {0x5cd7, 0x40}, + {0x5cd8, 0x80}, + {0x5cd9, 0x80}, + {0x5cda, 0x80}, + {0x5cdb, 0x40}, + {0x5cdc, 0x20}, + {0x5cdd, 0x80}, + {0x5cde, 0x80}, + {0x5cdf, 0x80}, + {0x5ce0, 0x20}, + {0x5ce1, 0x00}, + {0x5ce2, 0x80}, + {0x5ce3, 0x80}, + {0x5ce4, 0x80}, + {0x5ce5, 0x00}, + {0x5ce6, 0x00}, + {0x5d74, 0x01}, + {0x5d75, 0x00}, + {0x5d1f, 0x81}, + {0x5d11, 0x00}, + {0x5d12, 0x10}, + {0x5d13, 0x10}, + {0x5d15, 0x05}, + {0x5d16, 0x05}, + {0x5d17, 0x05}, + {0x5d08, 0x03}, + {0x5d09, 0xb6}, + {0x5d0a, 0x03}, + {0x5d0b, 0xb6}, + {0x5d18, 0x03}, + {0x5d19, 0xb6}, + {0x5d62, 0x01}, + {0x5d40, 0x02}, + {0x5d41, 0x01}, + {0x5d63, 0x1f}, + {0x5d64, 0x00}, + {0x5d65, 0x80}, + {0x5d56, 0x00}, + {0x5d57, 0x20}, + {0x5d58, 0x00}, + {0x5d59, 0x20}, + {0x5d5a, 0x00}, + {0x5d5b, 0x0c}, + {0x5d5c, 0x02}, + {0x5d5d, 0x40}, + {0x5d5e, 0x02}, + {0x5d5f, 0x40}, + {0x5d60, 0x03}, + {0x5d61, 0x40}, + {0x5d4a, 0x02}, + {0x5d4b, 0x40}, + {0x5d4c, 0x02}, + {0x5d4d, 0x40}, + {0x5d4e, 0x02}, + {0x5d4f, 0x40}, + {0x5d50, 0x18}, + {0x5d51, 0x80}, + {0x5d52, 0x18}, + {0x5d53, 0x80}, + {0x5d54, 0x18}, + {0x5d55, 0x80}, + {0x5d46, 0x20}, + {0x5d47, 0x00}, + {0x5d48, 0x22}, + {0x5d49, 0x00}, + {0x5d42, 0x20}, + {0x5d43, 0x00}, + {0x5d44, 0x22}, + {0x5d45, 0x00}, + {0x5004, 0x1e}, + {0x4221, 0x03}, + {0x380e, 0x02}, + {0x380f, 0xae}, + {0x380c, 0x04}, + {0x380d, 0x47}, + {0x384c, 0x02}, + {0x384d, 0x0d}, + {0x388c, 0x02}, + {0x388d, 0x2b}, + {0x420e, 0x54}, + {0x420f, 0xa0}, + {0x4210, 0xca}, + {0x4211, 0xf2}, + {0x507a, 0x5f}, + {0x507b, 0x46}, + {0x3802, 0x00}, + {0x3803, 0x68}, + {0x3806, 0x04}, + {0x3807, 0xa7}, + {0x380a, 0x04}, + {0x380b, 0x38}, + {0x3812, 0x00}, + {0x3813, 0x04}, + {0x380c, 0x08}, + {0x380d, 0x8e}, + {0x384c, 0x04}, + {0x384d, 0x1a}, + {0x388c, 0x04}, + {0x388d, 0x56}, + {0x0304, 0x00}, + {0x0305, 0xf9}, + {0x4837, 0x10}, + {0x0408, 0x78}, + {0x0409, 0x00}, + {0x040a, 0xd1}, + {0x040b, 0x1e}, + {0x040c, 0x2e}, + {0x040d, 0x44}, + {0x040e, 0x0c}, + {0x0100, 0x01}, + {REG_NULL, 0xff}, +}; + +static struct rkmodule_hdr_compr ox03c10_hdr_compr_12 = { + .point = 30, + .src_bit = 20, + .k_shift = 7, + .data_src = {0, 256, 768, 1792, 3840, 7936, 12032, 16128, 20224, 24320, + 32512, 40704, 48896, 57088, 65280, 73472, 81664, 89856, 106240, 122624, + 139008, 155392, 171776, 188160, 204544, 270080, 401152, 532224, 794368, 1056512}, + .data_compr = {0, 256, 320, 384, 448, 512, 576, 640, 704, 768, + 832, 928, 1024, 1120, 1216, 1312, 1408, 1504, 1632, 1760, + 1888, 2016, 2144, 2272, 2400, 2688, 3152, 3456, 3840, 4096}, + .slope_k = {128, 1024, 2048, 4096, 8192, 8192, 8192, 8192, 8192, 16384, + 10922, 10922, 10922, 10922, 10922, 10922, 10922, 16384, 16384, 16384, + 16384, 16384, 16384, 16384, 29127, 36157, 55188, 87381, 131072, 0}, +}; + +static struct rkmodule_hdr_compr ox03c10_hdr_compr_16 = { + .point = 30, + .src_bit = 20, + .k_shift = 12, + .data_src = {0, 2048, 2048, 2048, 2048, 4096, 8192, 12288, 16384, 20480, + 24576, 32768, 40960, 49152, 57344, 65536, 73728, 81920, 98304, 114688, + 131072, 147456, 163840, 180224, 196608, 262144, 393216, 524288, 786432, 1048576}, + .data_compr = {0, 2048, 2048, 2048, 2048, 3071, 4095, 5119, 6143, 7167, + 8191, 9727, 11263, 12799, 14335, 15871, 17407, 18943, 20991, 23039, + 25087, 27135, 29183, 31231, 33279, 38911, 47103, 53247, 59391, 65534}, + .slope_k = {4096, 0, 0, 0, 8200, 16384, 16384, 16384, 16384, 16384, + 21845, 21845, 21845, 21845, 21845, 21845, 21845, 32768, 32768, 32768, + 32768, 32768, 32768, 32768, 47662, 65536, 87381, 174762, 174762, 0}, +}; + +static const struct ox03c10_mode supported_modes[] = { + { + .bus_fmt = MEDIA_BUS_FMT_SBGGR12_1X12, + .width = 1920, + .height = 1080, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0038, + .hts_def = 0x10fe, + .vts_def = 0x0337 * 2, + .reg_list = ox03c10_1920x1080_30fps_HDR3_VS_PWL12_mipi600, + .hdr_mode = HDR_COMPR, + .hdr_compr = &ox03c10_hdr_compr_12, + .bpp = 12, + .mipi_freq_idx = 0, + .hdr_operating_mode = OX03C10_HDR3_DCG_VS_12BIT, + .vc[PAD0] = 0, + .exp_mode = EXP_HDR3_DCG_VS, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SBGGR16_1X16, + .width = 1920, + .height = 1080, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0038, + .hts_def = 0x10FE, + .vts_def = 0x02AE * 2, + .reg_list = ox03c10_1920x1080_30fps_HDR3_LFM_PWL16_mipi996, + .hdr_mode = HDR_COMPR, + .hdr_compr = &ox03c10_hdr_compr_16, + .bpp = 16, + .mipi_freq_idx = 1, + .hdr_operating_mode = OX03C10_HDR3_DCG_VS_LFM_16BIT, + .vc[PAD0] = 0, + .exp_mode = EXP_HDR3_DCG_SPD, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SBGGR12_1X12, + .width = 1920, + .height = 1080, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0038, + .hts_def = 0x10fe, + .vts_def = 0x0337, + .reg_list = ox03c10_1920x1080_30fps_HDR3_SPD_PWL12_mipi600, + .hdr_mode = NO_HDR, + .hdr_compr = &ox03c10_hdr_compr_12, + .bpp = 12, + .mipi_freq_idx = 5, + .hdr_operating_mode = OX03C10_HDR3_DCG_SPD_12BIT, + .vc[PAD0] = 0, + }, +}; + +static const s64 link_freq_menu_items[] = { + OX03C10_LINK_FREQ_300MHZ, + OX03C10_LINK_FREQ_480MHZ, +}; + +static const char * const ox03c10_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +/* Write registers up to 4 at a time */ +static int ox03c10_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 ox03c10_write_array(struct i2c_client *client, + const struct regval *regs) +{ + int i, delay_ms; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + if (regs[i].addr == DELAY_MS) { + delay_ms = regs[i].val; + dev_info(&client->dev, "delay(%d) ms !\n", delay_ms); + usleep_range(1000 * delay_ms, 1000 * delay_ms + 100); + continue; + } + ret = ox03c10_write_reg(client, regs[i].addr, + OX03C10_REG_VALUE_08BIT, regs[i].val); + } + + return ret; +} + +/* Read registers up to 4 at a time */ +static int ox03c10_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 ox03c10_get_reso_dist(const struct ox03c10_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct ox03c10_mode * +ox03c10_find_best_fit(struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i, len = ARRAY_SIZE(supported_modes); + + for (i = 0; i < len; i++) { + dist = ox03c10_get_reso_dist(&supported_modes[i], framefmt); + if ((cur_best_fit_dist == -1 || dist < cur_best_fit_dist) && + fmt->format.code == supported_modes[i].bus_fmt) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static const struct ox03c10_mode * +ox03c10_find_operating_mode(int operating_mode) +{ + unsigned int i, len = ARRAY_SIZE(supported_modes); + int cur_best_fit = 0; + + for (i = 0; i < len; i++) { + if (supported_modes[i].hdr_operating_mode == operating_mode) { + cur_best_fit = i; + break; + } + } + return &supported_modes[cur_best_fit]; +} + +static int ox03c10_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ox03c10 *ox03c10 = to_ox03c10(sd); + const struct ox03c10_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&ox03c10->mutex); + + mode = ox03c10_find_best_fit(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, sd_state, fmt->pad) = fmt->format; +#else + mutex_unlock(&ox03c10->mutex); + return -ENOTTY; +#endif + } else { + ox03c10->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(ox03c10->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height / 2; + __v4l2_ctrl_modify_range(ox03c10->vblank, 46, + mode->height, + 1, vblank_def); + } + + dev_err(&ox03c10->client->dev, + "Set format done!(cur_mode:%d)\n", mode->hdr_mode); + + mutex_unlock(&ox03c10->mutex); + + return 0; +} + +static int ox03c10_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ox03c10 *ox03c10 = to_ox03c10(sd); + const struct ox03c10_mode *mode = ox03c10->cur_mode; + + mutex_lock(&ox03c10->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); +#else + mutex_unlock(&ox03c10->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(&ox03c10->mutex); + + return 0; +} + +static int ox03c10_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct ox03c10 *ox03c10 = to_ox03c10(sd); + + if (code->index != 0) + return -EINVAL; + code->code = ox03c10->cur_mode->bus_fmt; + + return 0; +} + +static int ox03c10_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SBGGR12_1X12) + 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 ox03c10_enable_test_pattern(struct ox03c10 *ox03c10, u32 pattern) +{ + u32 val; + + if (pattern) + val = (pattern - 1) | OX03C10_TEST_PATTERN_ENABLE; + else + val = OX03C10_TEST_PATTERN_DISABLE; + + return ox03c10_write_reg(ox03c10->client, OX03C10_REG_TEST_PATTERN, + OX03C10_REG_VALUE_08BIT, val); +} + +static int ox03c10_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ox03c10 *ox03c10 = to_ox03c10(sd); + const struct ox03c10_mode *mode = ox03c10->cur_mode; + + fi->interval = mode->max_fps; + + return 0; +} + +static int ox03c10_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + config->type = V4L2_MBUS_CSI2_DPHY; + config->bus.mipi_csi2.num_data_lanes = OX03C10_LANES; + + return 0; +} + +static void ox03c10_get_module_inf(struct ox03c10 *ox03c10, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strscpy(inf->base.sensor, OX03C10_NAME, sizeof(inf->base.sensor)); + strscpy(inf->base.module, ox03c10->module_name, + sizeof(inf->base.module)); + strscpy(inf->base.lens, ox03c10->len_name, sizeof(inf->base.lens)); +} + +static int ox03c10_set_hdrae(struct ox03c10 *ox03c10, + struct preisp_hdrae_exp_s *ae) +{ + int ret = 0; + u32 l_dgain = 1024; + u32 m_dgain = 1024; + u32 s_dgain = 1024; + u32 l_exp = ae->long_exp_reg; + u32 m_exp = ae->middle_exp_reg; + u32 s_exp = ae->short_exp_reg; + u32 l_again = ae->long_gain_reg; + u32 m_again = ae->middle_gain_reg; + u32 s_again = ae->short_gain_reg; + + if (!ox03c10->has_init_exp && !ox03c10->streaming) { + ox03c10->init_hdrae_exp = *ae; + ox03c10->has_init_exp = true; + dev_dbg(&ox03c10->client->dev, "ox03c10 don't stream, record exp for hdr!\n"); + return ret; + } + + dev_dbg(&ox03c10->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, l_again, m_exp, m_again, s_exp, s_again); + + if (l_exp < 4) + l_exp = 4; + if (s_exp < 1) + s_exp = 1; + if (s_exp > 35) + s_exp = 35; + if (l_again > 248) { + l_dgain = l_again * 1024 / 248; + l_again = 248; + } + if (m_again > 248) { + m_dgain = m_again * 1024 / 248; + m_again = 248; + } + if (s_again > 248) { + s_dgain = s_again * 1024 / 248; + s_again = 248; + } + + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP_UPDATE_START_DATA); + // dcg exposure + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_REG_EXPOSURE_DCG_H, + OX03C10_REG_VALUE_16BIT, + l_exp); + // hcg real gain + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_AGAIN_HCG_H, + OX03C10_REG_VALUE_08BIT, + (l_again >> 4) & 0x0f); + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_AGAIN_HCG_L, + OX03C10_REG_VALUE_08BIT, + (l_again << 4) & 0xf0); + // hcg digital gain + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_DGAIN_HCG_H, + OX03C10_REG_VALUE_08BIT, + (l_dgain >> 10) & 0xf); + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_DGAIN_HCG_M, + OX03C10_REG_VALUE_08BIT, + (l_dgain >> 8) & 0xff); + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_DGAIN_HCG_L, + OX03C10_REG_VALUE_08BIT, + (l_dgain << 6) & 0xc0); + // lcg real gain + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_AGAIN_LCG_H, + OX03C10_REG_VALUE_08BIT, + (m_again >> 4) & 0x0f); + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_AGAIN_LCG_L, + OX03C10_REG_VALUE_08BIT, + (m_again << 4) & 0xf0); + // lcg digital gain + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_DGAIN_LCG_H, + OX03C10_REG_VALUE_08BIT, + (m_dgain >> 10) & 0xf); + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_DGAIN_LCG_M, + OX03C10_REG_VALUE_08BIT, + (m_dgain >> 8) & 0xff); + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_DGAIN_LCG_L, + OX03C10_REG_VALUE_08BIT, + (m_dgain << 6) & 0xc0); + + if (ox03c10->cur_mode->exp_mode == EXP_HDR3_DCG_VS) { + // vs exposure + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_REG_EXPOSURE_VS_H, + OX03C10_REG_VALUE_16BIT, + s_exp); + // vs real gain + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_AGAIN_VS_H, + OX03C10_REG_VALUE_08BIT, + (s_again >> 4) & 0x0f); + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_AGAIN_VS_L, + OX03C10_REG_VALUE_08BIT, + (s_again << 4) & 0xf0); + // vs digital gain + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_DGAIN_VS_H, + OX03C10_REG_VALUE_08BIT, + (s_dgain >> 10) & 0xf); + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_DGAIN_VS_M, + OX03C10_REG_VALUE_08BIT, + (s_dgain >> 8) & 0xff); + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_DGAIN_VS_L, + OX03C10_REG_VALUE_08BIT, + (s_dgain << 6) & 0xc0); + } else if (ox03c10->cur_mode->exp_mode == EXP_HDR3_DCG_SPD) { + // spd exposure + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_REG_EXPOSURE_SPD_H, + OX03C10_REG_VALUE_16BIT, + s_exp); + // spd real gain + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_AGAIN_SPD_H, + OX03C10_REG_VALUE_08BIT, + (s_again >> 4) & 0x0f); + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_AGAIN_SPD_L, + OX03C10_REG_VALUE_08BIT, + (s_again << 4) & 0xf0); + // spd digital gain + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_DGAIN_SPD_H, + OX03C10_REG_VALUE_08BIT, + (s_dgain >> 10) & 0xf); + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_DGAIN_SPD_M, + OX03C10_REG_VALUE_08BIT, + (s_dgain >> 8) & 0xff); + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_DGAIN_SPD_L, + OX03C10_REG_VALUE_08BIT, + (s_dgain << 6) & 0xc0); + } + + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP_UPDATE_END_DATA); + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP_UPDATE_LAUNCH); + return ret; +} + +static int ox03c10_get_dcg_and_spd_ratio(struct ox03c10 *ox03c10) +{ + struct device *dev = &ox03c10->client->dev; + u32 val = 0; + int ret = 0; + + ret = ox03c10_write_reg(ox03c10->client, OX03C10_REG_CTRL_MODE, + OX03C10_REG_VALUE_08BIT, OX03C10_MODE_STREAMING); + usleep_range(3000, 5000); + ox03c10->dcg_ratio.integer = 0; + ret |= ox03c10_read_reg(ox03c10->client, 0x7057, + OX03C10_REG_VALUE_24BIT, &val); + ox03c10->dcg_ratio.decimal = val & 0x1ffff; + ret |= ox03c10_read_reg(ox03c10->client, 0x705b, + OX03C10_REG_VALUE_24BIT, &val); + ox03c10->dcg_ratio.div_coeff = val & 0x1ffff; + if (ret != 0 || val == 0) + dev_err(dev, "get dcg ratio fail, ret %d, dcg ratio %d, %d\n", + ret, ox03c10->dcg_ratio.integer, ox03c10->dcg_ratio.decimal); + else + dev_info(dev, "get dcg ratio reg val %d, %d\n", + ox03c10->dcg_ratio.integer, ox03c10->dcg_ratio.decimal); + + ox03c10->spd_ratio.integer = 0; + ox03c10->spd_ratio.decimal = val & 0x1ffff; + ret |= ox03c10_read_reg(ox03c10->client, 0x705f, + OX03C10_REG_VALUE_24BIT, &val); + ox03c10->spd_ratio.div_coeff = val & 0x1ffff; + if (ret != 0 || val == 0) + dev_err(dev, "get spd ratio fail, ret %d, spd ratio %d, %d\n", + ret, ox03c10->spd_ratio.integer, ox03c10->spd_ratio.decimal); + else + dev_info(dev, "get spd ratio reg val %d, %d\n", + ox03c10->spd_ratio.integer, ox03c10->spd_ratio.decimal); + + ox03c10_write_reg(ox03c10->client, OX03C10_REG_CTRL_MODE, + OX03C10_REG_VALUE_08BIT, OX03C10_MODE_SW_STANDBY); + return ret; +} + +static int ox03c10_set_wb_gain(struct ox03c10 *ox03c10, + struct rkmodule_wb_gain_group *wb_gain_group) +{ + struct rkmodule_wb_gain wb_gain; + u16 reg_bgain, reg_gbgain, reg_grgain, reg_rgain; + int i = 0; + int ret = 0; +#ifdef DEBUG + u32 bgain, gbgain, grgain, rgain; +#endif + + for (i = 0; i < wb_gain_group->group_num; i++) { + switch (wb_gain_group->wb_gain_type[i]) { + case RKMODULE_HCG_WB_GAIN: + reg_bgain = OX03C10_REG_HCG_B_GAIN; + reg_gbgain = OX03C10_REG_HCG_GB_GAIN; + reg_grgain = OX03C10_REG_HCG_GR_GAIN; + reg_rgain = OX03C10_REG_HCG_R_GAIN; + break; + case RKMODULE_LCG_WB_GAIN: + reg_bgain = OX03C10_REG_LCG_B_GAIN; + reg_gbgain = OX03C10_REG_LCG_GB_GAIN; + reg_grgain = OX03C10_REG_LCG_GR_GAIN; + reg_rgain = OX03C10_REG_LCG_R_GAIN; + break; + case RKMODULE_SPD_WB_GAIN: + reg_bgain = OX03C10_REG_SPD_B_GAIN; + reg_gbgain = OX03C10_REG_SPD_GB_GAIN; + reg_grgain = OX03C10_REG_SPD_GR_GAIN; + reg_rgain = OX03C10_REG_SPD_R_GAIN; + break; + case RKMODULE_VS_WB_GAIN: + reg_bgain = OX03C10_REG_VS_B_GAIN; + reg_gbgain = OX03C10_REG_VS_GB_GAIN; + reg_grgain = OX03C10_REG_VS_GR_GAIN; + reg_rgain = OX03C10_REG_VS_R_GAIN; + break; + default: + return -EINVAL; + } + wb_gain = wb_gain_group->wb_gain[i]; + ret = ox03c10_write_reg(ox03c10->client, reg_bgain, + OX03C10_REG_VALUE_16BIT, wb_gain.b_gain & 0xffff); + ret |= ox03c10_write_reg(ox03c10->client, reg_grgain, + OX03C10_REG_VALUE_16BIT, wb_gain.gr_gain & 0xffff); + ret |= ox03c10_write_reg(ox03c10->client, reg_gbgain, + OX03C10_REG_VALUE_16BIT, wb_gain.gb_gain & 0xffff); + ret |= ox03c10_write_reg(ox03c10->client, reg_rgain, + OX03C10_REG_VALUE_16BIT, wb_gain.r_gain & 0xffff); + dev_info(&ox03c10->client->dev, + "write wb gain, type:%d, b:0x%x, gb:0x%x, gr:0x%x, r:0x%x\n", + wb_gain_group->wb_gain_type[i], + wb_gain.b_gain, wb_gain.gb_gain, + wb_gain.gr_gain, wb_gain.r_gain); +#ifdef DEBUG + ret |= ox03c10_read_reg(ox03c10->client, reg_bgain, + OX03C10_REG_VALUE_16BIT, &bgain); + ret |= ox03c10_read_reg(ox03c10->client, reg_gbgain, + OX03C10_REG_VALUE_16BIT, &gbgain); + ret |= ox03c10_read_reg(ox03c10->client, reg_grgain, + OX03C10_REG_VALUE_16BIT, &grgain); + ret |= ox03c10_read_reg(ox03c10->client, reg_rgain, + OX03C10_REG_VALUE_16BIT, &rgain); + dev_info(&ox03c10->client->dev, + "read wb gain, type %d, b:0x%x, gb:0x%x, gr:0x%x, r:0x%x\n", + wb_gain_group->wb_gain_type[i], bgain, gbgain, grgain, rgain); +#endif + } + return ret; +} + +static int ox03c10_set_blc(struct ox03c10 *ox03c10, + struct rkmodule_blc_group *blc_group) +{ + u32 reg_blc = 0; + u32 blc_val = 0; + int i = 0; + int ret = 0; + + for (i = 0; i < blc_group->group_num; i++) { + switch (blc_group->blc_type[i]) { + case RKMODULE_HCG_BLC: + reg_blc = OX03C10_REG_HCG_BLC; + break; + case RKMODULE_LCG_BLC: + reg_blc = OX03C10_REG_LCG_BLC; + break; + case RKMODULE_SPD_BLC: + reg_blc = OX03C10_REG_SPD_BLC; + break; + case RKMODULE_VS_BLC: + reg_blc = OX03C10_REG_VS_BLC; + break; + default: + return -EINVAL; + } + blc_val = blc_group->blc[i]; + ret = ox03c10_write_reg(ox03c10->client, reg_blc, + OX03C10_REG_VALUE_16BIT, blc_val & 0x3ff); + dev_info(&ox03c10->client->dev, + "write blc, type:%d, blc_val:0x%x\n", + blc_group->blc_type[i], + blc_val); +#ifdef DEBUG + ret |= ox03c10_read_reg(ox03c10->client, reg_blc, + OX03C10_REG_VALUE_16BIT, &blc_val); + dev_info(&ox03c10->client->dev, + "read blc, type %d, blc_val:0x%x\n", + blc_group->blc_type[i], blc_val); +#endif + } + return ret; +} + +static int ox03c10_get_channel_info(struct ox03c10 *ox03c10, struct rkmodule_channel_info *ch_info) +{ + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + ch_info->vc = ox03c10->cur_mode->vc[ch_info->index]; + ch_info->width = ox03c10->cur_mode->width; + ch_info->height = ox03c10->cur_mode->height; + ch_info->bus_fmt = ox03c10->cur_mode->bus_fmt; + if (ox03c10->cur_mode->bus_fmt == MEDIA_BUS_FMT_SBGGR16_1X16) { + ch_info->data_type = 0x2a; + ch_info->data_bit = 16; + } + return 0; +} + +static int ox03c10_select_exp_mode(struct ox03c10 *ox03c10, u32 exp_mode) +{ + int ret = -EINVAL; + u32 i, h, w, hdr_mode; + + w = ox03c10->cur_mode->width; + h = ox03c10->cur_mode->height; + hdr_mode = ox03c10->cur_mode->hdr_mode; + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + if (w == supported_modes[i].width && + h == supported_modes[i].height && + supported_modes[i].hdr_mode == hdr_mode && + supported_modes[i].exp_mode == exp_mode) { + ox03c10->cur_mode = &supported_modes[i]; + w = ox03c10->cur_mode->hts_def - ox03c10->cur_mode->width; + h = ox03c10->cur_mode->vts_def - ox03c10->cur_mode->height / 2; + __v4l2_ctrl_modify_range(ox03c10->hblank, w, w, 1, w); + __v4l2_ctrl_modify_range(ox03c10->vblank, h, + OX03C10_VTS_MAX - ox03c10->cur_mode->height, 1, h); + ret = 0; + break; + } + } + return ret; +} + +static long ox03c10_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ox03c10 *ox03c10 = to_ox03c10(sd); + struct rkmodule_hdr_cfg *hdr; + struct rkmodule_dcg_ratio *dcg_ratio; + struct rkmodule_wb_gain_group *wb_gain_group; + struct rkmodule_blc_group *blc_group; + struct rkmodule_channel_info *ch_info; + u32 i, h, w; + long ret = 0; + u32 stream = 0; + u32 *exp_mode; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + ox03c10_get_module_inf(ox03c10, (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 = ox03c10->cur_mode->hdr_mode; + if (hdr->hdr_mode == HDR_COMPR) + hdr->compr = *ox03c10->cur_mode->hdr_compr; + break; + case RKMODULE_SET_HDR_CFG: + hdr = (struct rkmodule_hdr_cfg *)arg; + if (ox03c10->cur_mode->hdr_mode == HDR_COMPR) + hdr->hdr_mode = ox03c10->cur_mode->hdr_mode; + w = ox03c10->cur_mode->width; + h = ox03c10->cur_mode->height; + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + if (w == supported_modes[i].width && + h == supported_modes[i].height && + supported_modes[i].hdr_mode == hdr->hdr_mode) { + ox03c10->cur_mode = &supported_modes[i]; + break; + } + } + if (i == ARRAY_SIZE(supported_modes)) { + dev_err(&ox03c10->client->dev, + "not find hdr mode:%d %dx%d config\n", + hdr->hdr_mode, w, h); + ret = -EINVAL; + } else { + w = ox03c10->cur_mode->hts_def - ox03c10->cur_mode->width; + h = ox03c10->cur_mode->vts_def - ox03c10->cur_mode->height / 2; + __v4l2_ctrl_modify_range(ox03c10->hblank, w, w, 1, w); + __v4l2_ctrl_modify_range(ox03c10->vblank, h, + OX03C10_VTS_MAX - ox03c10->cur_mode->height, 1, h); + } + break; + case PREISP_CMD_SET_HDRAE_EXP: + return ox03c10_set_hdrae(ox03c10, arg); + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = ox03c10_write_reg(ox03c10->client, OX03C10_REG_CTRL_MODE, + OX03C10_REG_VALUE_08BIT, OX03C10_MODE_STREAMING); + else + ret = ox03c10_write_reg(ox03c10->client, OX03C10_REG_CTRL_MODE, + OX03C10_REG_VALUE_08BIT, OX03C10_MODE_SW_STANDBY); + break; + case RKMODULE_GET_DCG_RATIO: + dcg_ratio = (struct rkmodule_dcg_ratio *)arg; + *dcg_ratio = ox03c10->dcg_ratio; + break; + case RKMODULE_GET_SPD_RATIO: + dcg_ratio = (struct rkmodule_dcg_ratio *)arg; + *dcg_ratio = ox03c10->spd_ratio; + break; + case RKMODULE_SET_WB_GAIN: + wb_gain_group = (struct rkmodule_wb_gain_group *)arg; + ret = ox03c10_set_wb_gain(ox03c10, wb_gain_group); + break; + case RKMODULE_SET_BLC: + blc_group = (struct rkmodule_blc_group *)arg; + ret = ox03c10_set_blc(ox03c10, blc_group); + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = ox03c10_get_channel_info(ox03c10, ch_info); + break; + case RKMODULE_GET_EXP_MODE: + + exp_mode = (u32 *)arg; + *exp_mode = ox03c10->cur_mode->exp_mode; + break; + case RKMODULE_SET_EXP_MODE: + ret = ox03c10_select_exp_mode(ox03c10, *(u32 *)arg); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long ox03c10_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_dcg_ratio *dcg_ratio; + struct rkmodule_wb_gain_group *wb_gain_group; + struct rkmodule_blc_group *blc_group; + struct rkmodule_channel_info *ch_info; + long ret; + u32 stream = 0; + u32 exp_mode; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = ox03c10_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 = ox03c10_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 = ox03c10_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 = ox03c10_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 = ox03c10_ioctl(sd, cmd, hdrae); + kfree(hdrae); + break; + case RKMODULE_SET_QUICK_STREAM: + if (copy_from_user(&stream, up, sizeof(u32))) + return -EFAULT; + ret = ox03c10_ioctl(sd, cmd, &stream); + break; + case RKMODULE_GET_DCG_RATIO: + dcg_ratio = kzalloc(sizeof(*dcg_ratio), GFP_KERNEL); + if (!dcg_ratio) { + ret = -ENOMEM; + return ret; + } + + ret = ox03c10_ioctl(sd, cmd, dcg_ratio); + if (!ret) { + ret = copy_to_user(up, dcg_ratio, sizeof(*dcg_ratio)); + if (ret) + return -EFAULT; + } + kfree(dcg_ratio); + break; + case RKMODULE_GET_SPD_RATIO: + dcg_ratio = kzalloc(sizeof(*dcg_ratio), GFP_KERNEL); + if (!dcg_ratio) { + ret = -ENOMEM; + return ret; + } + + ret = ox03c10_ioctl(sd, cmd, dcg_ratio); + if (!ret) { + ret = copy_to_user(up, dcg_ratio, sizeof(*dcg_ratio)); + if (ret) + return -EFAULT; + } + kfree(dcg_ratio); + break; + case RKMODULE_SET_WB_GAIN: + wb_gain_group = kzalloc(sizeof(*wb_gain_group), GFP_KERNEL); + if (!wb_gain_group) { + ret = -ENOMEM; + return ret; + } + + ret = ox03c10_ioctl(sd, cmd, wb_gain_group); + if (!ret) { + ret = copy_to_user(up, wb_gain_group, sizeof(*wb_gain_group)); + if (ret) + return -EFAULT; + } + kfree(wb_gain_group); + break; + case RKMODULE_SET_BLC: + blc_group = kzalloc(sizeof(*blc_group), GFP_KERNEL); + if (!blc_group) { + ret = -ENOMEM; + return ret; + } + + ret = ox03c10_ioctl(sd, cmd, blc_group); + if (!ret) { + ret = copy_to_user(up, blc_group, sizeof(*blc_group)); + if (ret) + return -EFAULT; + } + kfree(blc_group); + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); + if (!ch_info) { + ret = -ENOMEM; + return ret; + } + + ret = ox03c10_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_EXP_MODE: + ret = ox03c10_ioctl(sd, cmd, &exp_mode); + if (!ret) { + ret = copy_to_user(up, &exp_mode, sizeof(exp_mode)); + if (ret) + return -EFAULT; + } + break; + case RKMODULE_SET_EXP_MODE: + if (copy_from_user(&exp_mode, up, sizeof(u32))) + return -EFAULT; + ret = ox03c10_ioctl(sd, cmd, &exp_mode); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __ox03c10_start_stream(struct ox03c10 *ox03c10) +{ + int ret; + + dev_info(&ox03c10->client->dev, "write: %d s cur_mode->reg_list done !\n", ox03c10->cur_mode->hdr_operating_mode); + ret = ox03c10_write_array(ox03c10->client, ox03c10->cur_mode->reg_list); + if (ret) + return ret; + /* In case these controls are set before streaming */ + ret = __v4l2_ctrl_handler_setup(&ox03c10->ctrl_handler); + if (ret) + return ret; + if (ox03c10->has_init_exp && ox03c10->cur_mode->hdr_mode != NO_HDR) { + ret = ox03c10_ioctl(&ox03c10->subdev, + PREISP_CMD_SET_HDRAE_EXP, + &ox03c10->init_hdrae_exp); + if (ret) { + dev_err(&ox03c10->client->dev, + "init exp fail in hdr mode\n"); + return ret; + } + } + + return ox03c10_write_reg(ox03c10->client, OX03C10_REG_CTRL_MODE, + OX03C10_REG_VALUE_08BIT, OX03C10_MODE_STREAMING); +} + +static int __ox03c10_stop_stream(struct ox03c10 *ox03c10) +{ + ox03c10->has_init_exp = false; + return ox03c10_write_reg(ox03c10->client, OX03C10_REG_CTRL_MODE, + OX03C10_REG_VALUE_08BIT, OX03C10_MODE_SW_STANDBY); +} + +static int ox03c10_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ox03c10 *ox03c10 = to_ox03c10(sd); + struct i2c_client *client = ox03c10->client; + int ret = 0; + + mutex_lock(&ox03c10->mutex); + on = !!on; + if (on == ox03c10->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 = __ox03c10_start_stream(ox03c10); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } else { + v4l2_err(sd, "start stream success\n"); + } + } else { + __ox03c10_stop_stream(ox03c10); + pm_runtime_put(&client->dev); + } + + ox03c10->streaming = on; + +unlock_and_return: + mutex_unlock(&ox03c10->mutex); + + return ret; +} + +static int ox03c10_s_power(struct v4l2_subdev *sd, int on) +{ + struct ox03c10 *ox03c10 = to_ox03c10(sd); + struct i2c_client *client = ox03c10->client; + int ret = 0; + + mutex_lock(&ox03c10->mutex); + + /* If the power state is not modified - no work to do. */ + if (ox03c10->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; + } + + ox03c10->power_on = true; + } else { + pm_runtime_put(&client->dev); + ox03c10->power_on = false; + } + +unlock_and_return: + mutex_unlock(&ox03c10->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ox03c10_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, OX03C10_XVCLK_FREQ / 1000 / 1000); +} + +static int __ox03c10_power_on(struct ox03c10 *ox03c10) +{ + int ret; + struct device *dev = &ox03c10->client->dev; + + if (!IS_ERR_OR_NULL(ox03c10->pins_default)) { + ret = pinctrl_select_state(ox03c10->pinctrl, + ox03c10->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(ox03c10->xvclk, OX03C10_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(ox03c10->xvclk) != OX03C10_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(ox03c10->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(ox03c10->reset_gpio)) + gpiod_set_value_cansleep(ox03c10->reset_gpio, 0); + + ret = regulator_bulk_enable(OX03C10_NUM_SUPPLIES, ox03c10->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(ox03c10->reset_gpio)) + gpiod_set_value_cansleep(ox03c10->reset_gpio, 1); + + if (!IS_ERR(ox03c10->reset_gpio)) + usleep_range(6000, 8000); + else + usleep_range(12000, 16000); + + return 0; + +disable_clk: + clk_disable_unprepare(ox03c10->xvclk); + + return ret; +} + +static void __ox03c10_power_off(struct ox03c10 *ox03c10) +{ + int ret; + struct device *dev = &ox03c10->client->dev; + + clk_disable_unprepare(ox03c10->xvclk); + if (!IS_ERR(ox03c10->reset_gpio)) + gpiod_set_value_cansleep(ox03c10->reset_gpio, 0); + if (!IS_ERR_OR_NULL(ox03c10->pins_sleep)) { + ret = pinctrl_select_state(ox03c10->pinctrl, + ox03c10->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + regulator_bulk_disable(OX03C10_NUM_SUPPLIES, ox03c10->supplies); +} + +static int ox03c10_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ox03c10 *ox03c10 = to_ox03c10(sd); + + return __ox03c10_power_on(ox03c10); +} + +static int ox03c10_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ox03c10 *ox03c10 = to_ox03c10(sd); + + __ox03c10_power_off(ox03c10); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int ox03c10_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ox03c10 *ox03c10 = to_ox03c10(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->state, 0); + const struct ox03c10_mode *def_mode = &supported_modes[0]; + + mutex_lock(&ox03c10->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(&ox03c10->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int ox03c10_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval_enum *fie) +{ + if (fie->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + 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 ox03c10_pm_ops = { + SET_RUNTIME_PM_OPS(ox03c10_runtime_suspend, + ox03c10_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops ox03c10_internal_ops = { + .open = ox03c10_open, +}; +#endif + +static const struct v4l2_subdev_core_ops ox03c10_core_ops = { + .s_power = ox03c10_s_power, + .ioctl = ox03c10_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ox03c10_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops ox03c10_video_ops = { + .s_stream = ox03c10_s_stream, + .g_frame_interval = ox03c10_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops ox03c10_pad_ops = { + .enum_mbus_code = ox03c10_enum_mbus_code, + .enum_frame_size = ox03c10_enum_frame_sizes, + .enum_frame_interval = ox03c10_enum_frame_interval, + .get_fmt = ox03c10_get_fmt, + .set_fmt = ox03c10_set_fmt, + .get_mbus_config = ox03c10_g_mbus_config, +}; + +static const struct v4l2_subdev_ops ox03c10_subdev_ops = { + .core = &ox03c10_core_ops, + .video = &ox03c10_video_ops, + .pad = &ox03c10_pad_ops, +}; + +static int ox03c10_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ox03c10 *ox03c10 = container_of(ctrl->handler, + struct ox03c10, ctrl_handler); + struct i2c_client *client = ox03c10->client; + int ret = 0; + u32 val = 0; + u32 again = 16, dgain = 1024; + s64 exposure_max = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + exposure_max = ox03c10->cur_mode->height + ctrl->val - 12; + __v4l2_ctrl_modify_range(ox03c10->exposure, + ox03c10->exposure->minimum, exposure_max, + ox03c10->exposure->step, + ox03c10->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if (ox03c10->cur_mode->hdr_mode != NO_HDR) + break; + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP_UPDATE_START_DATA); + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_EXPOSURE_DCG_H, + OX03C10_REG_VALUE_16BIT, + ctrl->val); + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP_UPDATE_END_DATA); + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP_UPDATE_LAUNCH); + break; + case V4L2_CID_ANALOGUE_GAIN: + if (ox03c10->cur_mode->hdr_mode != NO_HDR) + break; + // hcg real gain + again = ctrl->val; + if (again > 248) { + dgain = again * 1024 / 248; + again = 248; + } + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP1_UPDATE_START_DATA); + + // hcg real gain + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_AGAIN_HCG_H, + OX03C10_REG_VALUE_16BIT, + (again << 4) & 0xff0); + // hcg digital gain + ret |= ox03c10_write_reg(ox03c10->client, + OX03C10_REG_DGAIN_HCG_H, + OX03C10_REG_VALUE_24BIT, + (dgain << 6) & 0xfffc0); + + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP1_UPDATE_END_DATA); + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP1_UPDATE_LAUNCH); + + dev_err(&client->dev, "%s set gain val:0x%x ret:%d again:0x%x, dgain:0x%x", + __func__, ctrl->val, ret, + again, dgain); + break; + case V4L2_CID_VBLANK: + ret = ox03c10_write_reg(ox03c10->client, OX03C10_REG_VTS, + OX03C10_REG_VALUE_16BIT, + (ctrl->val + ox03c10->cur_mode->height) / 2); + break; + case V4L2_CID_TEST_PATTERN: + ret = ox03c10_enable_test_pattern(ox03c10, ctrl->val); + break; + case V4L2_CID_HFLIP: + ret = ox03c10_read_reg(ox03c10->client, OX03C10_VFLIP_REG, + OX03C10_REG_VALUE_08BIT, + &val); + if (ctrl->val) + val |= MIRROR_BIT_MASK; + else + val &= ~MIRROR_BIT_MASK; + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_VFLIP_REG, + OX03C10_REG_VALUE_08BIT, + val); + break; + case V4L2_CID_VFLIP: + ret = ox03c10_read_reg(ox03c10->client, OX03C10_VFLIP_REG, + OX03C10_REG_VALUE_08BIT, + &val); + if (ctrl->val) + val |= FLIP_BIT_MASK; + else + val &= ~FLIP_BIT_MASK; + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_VFLIP_REG, + OX03C10_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 ox03c10_ctrl_ops = { + .s_ctrl = ox03c10_set_ctrl, +}; + +static int ox03c10_initialize_controls(struct ox03c10 *ox03c10) +{ + const struct ox03c10_mode *mode; + struct v4l2_ctrl_handler *handler; + s64 vblank_def; + u32 h_blank; + u64 dst_pixel_rate = 0; + int ret; + s64 exposure_max = 0; + + handler = &ox03c10->ctrl_handler; + mode = ox03c10->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 9); + if (ret) + return ret; + handler->lock = &ox03c10->mutex; + + ox03c10->link_freq = v4l2_ctrl_new_int_menu(handler, NULL, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_menu_items), + 0, link_freq_menu_items); + + __v4l2_ctrl_s_ctrl(ox03c10->link_freq, mode->mipi_freq_idx); + + dst_pixel_rate = (u32)link_freq_menu_items[mode->mipi_freq_idx] / + mode->bpp * 2 * OX03C10_LANES; + ox03c10->pixel_rate = v4l2_ctrl_new_std(handler, NULL, + V4L2_CID_PIXEL_RATE, + 0, OX03C10_PIXEL_RATE_MAX, + 1, dst_pixel_rate); + + h_blank = mode->hts_def - mode->width; + ox03c10->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (ox03c10->hblank) + ox03c10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + ox03c10->vblank = v4l2_ctrl_new_std(handler, &ox03c10_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + OX03C10_VTS_MAX, + 1, vblank_def); + + exposure_max = mode->vts_def - 12; + ox03c10->exposure = v4l2_ctrl_new_std(handler, &ox03c10_ctrl_ops, + V4L2_CID_EXPOSURE, OX03C10_EXPOSURE_HCG_MIN, + exposure_max, OX03C10_EXPOSURE_HCG_STEP, + mode->exp_def); + dev_err(&ox03c10->client->dev, "exposure_max=%lld\n", exposure_max); + ox03c10->anal_gain = v4l2_ctrl_new_std(handler, &ox03c10_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, OX03C10_GAIN_MIN, + OX03C10_GAIN_MAX, OX03C10_GAIN_STEP, + OX03C10_GAIN_DEFAULT); + ox03c10->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &ox03c10_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ox03c10_test_pattern_menu) - 1, + 0, 0, ox03c10_test_pattern_menu); + v4l2_ctrl_new_std(handler, &ox03c10_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + v4l2_ctrl_new_std(handler, &ox03c10_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (handler->error) { + ret = handler->error; + dev_err(&ox03c10->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + ox03c10->subdev.ctrl_handler = handler; + ox03c10->has_init_exp = false; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int ox03c10_check_sensor_id(struct ox03c10 *ox03c10, + struct i2c_client *client) +{ + struct device *dev = &ox03c10->client->dev; + u32 id = 0; + int ret; + + ret = ox03c10_read_reg(client, OX03C10_REG_CHIP_ID, + OX03C10_REG_VALUE_16BIT, &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 ox03c10_configure_regulators(struct ox03c10 *ox03c10) +{ + unsigned int i; + + for (i = 0; i < OX03C10_NUM_SUPPLIES; i++) + ox03c10->supplies[i].supply = ox03c10_supply_names[i]; + + return devm_regulator_bulk_get(&ox03c10->client->dev, + OX03C10_NUM_SUPPLIES, + ox03c10->supplies); +} + +static int ox03c10_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct ox03c10 *ox03c10; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + u32 hdr_mode = 0, hdr_operating_mode; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + ox03c10 = devm_kzalloc(dev, sizeof(*ox03c10), GFP_KERNEL); + if (!ox03c10) + return -ENOMEM; + + of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode); + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ox03c10->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ox03c10->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ox03c10->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ox03c10->len_name); + hdr_mode = 0; + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + ox03c10->client = client; + hdr_operating_mode = OX03C10_HDR3_DCG_VS_12BIT; + dev_info(dev, "hdr_operating_mode: %d", hdr_operating_mode); + ox03c10->cur_mode = ox03c10_find_operating_mode(hdr_operating_mode); + + ox03c10->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ox03c10->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + ox03c10->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ox03c10->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + ox03c10->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(ox03c10->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ox03c10->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(ox03c10->pinctrl)) { + ox03c10->pins_default = + pinctrl_lookup_state(ox03c10->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(ox03c10->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + ox03c10->pins_sleep = + pinctrl_lookup_state(ox03c10->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(ox03c10->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } else { + dev_err(dev, "no pinctrl\n"); + } + + ret = ox03c10_configure_regulators(ox03c10); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&ox03c10->mutex); + + sd = &ox03c10->subdev; + v4l2_i2c_subdev_init(sd, client, &ox03c10_subdev_ops); + ret = ox03c10_initialize_controls(ox03c10); + if (ret) + goto err_destroy_mutex; + + ret = __ox03c10_power_on(ox03c10); + if (ret) + goto err_free_handler; + + ret = ox03c10_check_sensor_id(ox03c10, client); + if (ret) + goto err_power_off; + + ret = ox03c10_get_dcg_and_spd_ratio(ox03c10); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &ox03c10_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + ox03c10->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ox03c10->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(ox03c10->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ox03c10->module_index, facing, + OX03C10_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __ox03c10_power_off(ox03c10); +err_free_handler: + v4l2_ctrl_handler_free(&ox03c10->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ox03c10->mutex); + + return ret; +} + +static void ox03c10_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ox03c10 *ox03c10 = to_ox03c10(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&ox03c10->ctrl_handler); + mutex_destroy(&ox03c10->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __ox03c10_power_off(ox03c10); + pm_runtime_set_suspended(&client->dev); + +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ox03c10_of_match[] = { + { .compatible = "ovti,ox03c10" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ox03c10_of_match); +#endif + +static const struct i2c_device_id ox03c10_match_id[] = { + { "ovti,ox03c10", 0 }, + { }, +}; + +static struct i2c_driver ox03c10_i2c_driver = { + .driver = { + .name = OX03C10_NAME, + .pm = &ox03c10_pm_ops, + .of_match_table = of_match_ptr(ox03c10_of_match), + }, + .probe = &ox03c10_probe, + .remove = &ox03c10_remove, + .id_table = ox03c10_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&ox03c10_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&ox03c10_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("OmniVision ox03c10 sensor driver"); +MODULE_LICENSE("GPL"); From 9c4c5ea152646b870af0c66ca5e6fac5237b1eaf Mon Sep 17 00:00:00 2001 From: Shunqian Zheng Date: Sat, 10 May 2025 11:21:26 +0800 Subject: [PATCH 02/18] arm64: configs: rv1126b: add robot config fragment Change-Id: I20d3edbf5948f68ec78d17eb01cf29ef2f997147 Signed-off-by: Shunqian Zheng --- arch/arm64/configs/rv1126b_robot.config | 62 +++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 arch/arm64/configs/rv1126b_robot.config diff --git a/arch/arm64/configs/rv1126b_robot.config b/arch/arm64/configs/rv1126b_robot.config new file mode 100644 index 000000000000..b55cbad14975 --- /dev/null +++ b/arch/arm64/configs/rv1126b_robot.config @@ -0,0 +1,62 @@ +# CONFIG_BACKLIGHT_CLASS_DEVICE is not set +# CONFIG_BATTERY_RK817 is not set +CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +# CONFIG_CHARGER_RK817 is not set +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY is not set +CONFIG_PREEMPT=y +# CONFIG_PREEMPT_NONE is not set +# CONFIG_RK_HEADSET is not set +# CONFIG_ROCKCHIP_DW_MIPI_DSI is not set +# CONFIG_ROCKCHIP_FEPHY is not set +# CONFIG_ROCKCHIP_MINI_KERNEL is not set +# CONFIG_ROCKCHIP_MPP_OSAL is not set +CONFIG_ROCKCHIP_MPP_SERVICE=y +# CONFIG_ROCKCHIP_RGB is not set +CONFIG_SQUASHFS=y +# CONFIG_STMMAC_ETH is not set +# CONFIG_USB_VIDEO_CLASS is not set +# CONFIG_VIDEO_IMX415 is not set +CONFIG_VIDEO_OV7251=y +# CONFIG_VIDEO_SC200AI is not set +# CONFIG_VIDEO_SC850SL is not set +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_HDMI=y +CONFIG_I2C_ALGOBIT=y +CONFIG_LZ4_DECOMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_PREEMPTION=y +CONFIG_PREEMPT_BUILD=y +CONFIG_PREEMPT_COUNT=y +CONFIG_PREEMPT_RCU=y +# CONFIG_ROCKCHIP_MPP_AV1DEC is not set +# CONFIG_ROCKCHIP_MPP_IEP2 is not set +# CONFIG_ROCKCHIP_MPP_JPGDEC is not set +# CONFIG_ROCKCHIP_MPP_JPGENC is not set +CONFIG_ROCKCHIP_MPP_PROC_FS=y +# CONFIG_ROCKCHIP_MPP_RKVDEC is not set +CONFIG_ROCKCHIP_MPP_RKVDEC2=y +# CONFIG_ROCKCHIP_MPP_RKVENC is not set +CONFIG_ROCKCHIP_MPP_RKVENC2=y +# CONFIG_ROCKCHIP_MPP_VDPP is not set +# CONFIG_ROCKCHIP_MPP_VDPU1 is not set +# CONFIG_ROCKCHIP_MPP_VDPU2 is not set +# CONFIG_ROCKCHIP_MPP_VEPU1 is not set +# CONFIG_ROCKCHIP_MPP_VEPU2 is not set +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_DECOMP_MULTI is not set +CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y +# CONFIG_SQUASHFS_DECOMP_SINGLE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FILE_CACHE=y +# CONFIG_SQUASHFS_FILE_DIRECT is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +CONFIG_SQUASHFS_LZ4=y +CONFIG_SQUASHFS_LZO=y +# CONFIG_SQUASHFS_XATTR is not set +# CONFIG_SQUASHFS_XZ is not set +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_ZSTD is not set +CONFIG_UNINLINE_SPIN_UNLOCK=y From 705a6274362c6b2dd236074cc0d91eb0ba565a45 Mon Sep 17 00:00:00 2001 From: Luo Wei Date: Tue, 13 May 2025 12:12:29 +0800 Subject: [PATCH 03/18] mfd: display-serdes: fix kmemleak warning from kasprintf Signed-off-by: Luo Wei Change-Id: Icc4fc3f40f6b96f35ff4b21c2263f0870745361c --- drivers/mfd/display-serdes/serdes-gpio.c | 6 +++--- drivers/mfd/display-serdes/serdes-pinctrl.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/mfd/display-serdes/serdes-gpio.c b/drivers/mfd/display-serdes/serdes-gpio.c index 20b61d30f14b..1c49cf55fc28 100644 --- a/drivers/mfd/display-serdes/serdes-gpio.c +++ b/drivers/mfd/display-serdes/serdes-gpio.c @@ -42,10 +42,9 @@ static void serdes_gpio_set(struct gpio_chip *chip, unsigned int offset, int val { struct serdes_gpio *serdes_gpio = gpiochip_get_data(chip); struct serdes *serdes = serdes_gpio->parent->parent; - int ret = 0; if (serdes->chip_data->gpio_ops->set_level) - ret = serdes->chip_data->gpio_ops->set_level(serdes, offset, value); + serdes->chip_data->gpio_ops->set_level(serdes, offset, value); SERDES_DBG_MFD("%s: %s %s gpio=%d,val=%d\n", __func__, dev_name(serdes->dev), serdes->chip_data->name, offset, value); @@ -185,7 +184,8 @@ static int serdes_gpio_probe(struct platform_device *pdev) #ifdef CONFIG_OF_GPIO serdes_gpio->gpio_chip.of_node = serdes_gpio->dev->of_node; #endif - serdes_gpio->gpio_chip.label = kasprintf(GFP_KERNEL, "%s-gpio", chip_data->name); + serdes_gpio->gpio_chip.label = devm_kasprintf(serdes_gpio->dev, GFP_KERNEL, + "%s-gpio", chip_data->name); /* Add gpiochip */ ret = devm_gpiochip_add_data(&pdev->dev, &serdes_gpio->gpio_chip, diff --git a/drivers/mfd/display-serdes/serdes-pinctrl.c b/drivers/mfd/display-serdes/serdes-pinctrl.c index eeb8a6803ddc..27605732ff66 100644 --- a/drivers/mfd/display-serdes/serdes-pinctrl.c +++ b/drivers/mfd/display-serdes/serdes-pinctrl.c @@ -282,7 +282,7 @@ static int serdes_pinctrl_probe(struct platform_device *pdev) if (pin_base) { for (i = 0; i < pinctrl_info->num_pins; i++) { serdes_pinctrl->pdesc[i].number = pinctrl_info->pins[i].number + pin_base; - serdes_pinctrl->pdesc[i].name = kasprintf(GFP_KERNEL, "%s-gpio%d", + serdes_pinctrl->pdesc[i].name = devm_kasprintf(dev, GFP_KERNEL, "%s-gpio%d", pinctrl_info->pins[i].name, serdes_pinctrl->pdesc[i].number); SERDES_DBG_MFD("%s:pdesc number=%d, name=%s\n", __func__, @@ -336,7 +336,7 @@ static int serdes_pinctrl_probe(struct platform_device *pdev) } if (!serdes->route_enable) - ret = pinctrl_enable(serdes_pinctrl->pctl); + pinctrl_enable(serdes_pinctrl->pctl); ret = serdes_pinctrl_gpio_init(serdes); From 94bf2f3b0559601cf0a119e19680307b4b83bbba Mon Sep 17 00:00:00 2001 From: Yuefu Su Date: Tue, 13 May 2025 17:13:54 +0800 Subject: [PATCH 04/18] media: i2c: add imx678 sensor driver Signed-off-by: Yuefu Su Change-Id: I5cace6a4708ca44d55d700d2415e8d6da20dda12 --- drivers/media/i2c/Kconfig | 13 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/imx678.c | 3305 ++++++++++++++++++++++++++++++++++++ 3 files changed, 3319 insertions(+) create mode 100644 drivers/media/i2c/imx678.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 581dee3af592..2c45a69fff70 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -850,6 +850,19 @@ config VIDEO_IMX586 To compile this driver as a module, choose M here: the module will be called imx586. +config VIDEO_IMX678 + tristate "Sony IMX678 sensor support" + depends on I2C && VIDEO_DEV + depends on MEDIA_CAMERA_SUPPORT + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + help + This is a Video4Linux2 sensor driver for the Sony + IMX678 camera. + + To compile this driver as a module, choose M here: the + module will be called imx678. + config VIDEO_IMX766 tristate "Sony IMX766 sensor support" depends on I2C && VIDEO_DEV diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index c4c9de6adda0..a276ed814946 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -106,6 +106,7 @@ obj-$(CONFIG_VIDEO_IMX492) += imx492.o obj-$(CONFIG_VIDEO_IMX498) += imx498.o obj-$(CONFIG_VIDEO_IMX577) += imx577.o obj-$(CONFIG_VIDEO_IMX586) += imx586.o +obj-$(CONFIG_VIDEO_IMX678) += imx678.o obj-$(CONFIG_VIDEO_IMX766) += imx766.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o diff --git a/drivers/media/i2c/imx678.c b/drivers/media/i2c/imx678.c new file mode 100644 index 000000000000..f132d904cc2a --- /dev/null +++ b/drivers/media/i2c/imx678.c @@ -0,0 +1,3305 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * imx678 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 +#include +#include "../platform/rockchip/isp/rkisp_tb_helper.h" +#include "cam-tb-setup.h" +#include "cam-sleep-wakeup.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_1782M 891000000 +#define MIPI_FREQ_1188M 594000000 + +#define IMX678_4LANES 4 + +#define IMX678_MAX_PIXEL_RATE (MIPI_FREQ_1782M / 10 * 2 * IMX678_4LANES) +#define OF_CAMERA_HDR_MODE "rockchip,camera-hdr-mode" + +#define IMX678_XVCLK_FREQ_37M 37125000 + +/* TODO: Get the real chip id from reg */ +#define CHIP_ID 0x01 +#define IMX678_REG_CHIP_ID 0x3022 + +#define IMX678_REG_CTRL_MODE 0x3000 +#define IMX678_MODE_SW_STANDBY BIT(0) +#define IMX678_MODE_STREAMING 0x0 + +#define IMX678_HCG_ADDR 0x3030 +#define IMX678_HCG_SEL1_ADDR 0x3031 +#define IMX678_HCG_SEL2_ADDR 0x3032 + +#define IMX678_LF_GAIN_REG_H 0x3071 +#define IMX678_LF_GAIN_REG_L 0x3070 + +#define IMX678_SF1_GAIN_REG_H 0x3073 +#define IMX678_SF1_GAIN_REG_L 0x3072 + +#define IMX678_SF2_GAIN_REG_H 0x3075 +#define IMX678_SF2_GAIN_REG_L 0x3074 + +#define IMX678_LF_EXPO_REG_H 0x3052 +#define IMX678_LF_EXPO_REG_M 0x3051 +#define IMX678_LF_EXPO_REG_L 0x3050 + +#define IMX678_SF1_EXPO_REG_H 0x3056 +#define IMX678_SF1_EXPO_REG_M 0x3055 +#define IMX678_SF1_EXPO_REG_L 0x3054 + +#define IMX678_SF2_EXPO_REG_H 0x305A +#define IMX678_SF2_EXPO_REG_M 0x3059 +#define IMX678_SF2_EXPO_REG_L 0x3058 + +#define IMX678_RHS1_REG_H 0x3062 +#define IMX678_RHS1_REG_M 0x3061 +#define IMX678_RHS1_REG_L 0x3060 +#define IMX678_RHS1_DEFAULT 0x004D + +#define IMX678_RHS2_REG_H 0x3066 +#define IMX678_RHS2_REG_M 0x3065 +#define IMX678_RHS2_REG_L 0x3064 +#define IMX678_RHS2_DEFAULT 0x004D + +#define IMX678_EXPOSURE_MIN 4 +#define IMX678_EXPOSURE_STEP 1 +#define IMX678_VTS_MAX 0x7fff + +#define IMX678_GAIN_MIN 0x00 +#define IMX678_GAIN_MAX 0xf0 +#define IMX678_GAIN_STEP 1 +#define IMX678_GAIN_DEFAULT 0x00 + +#define IMX678_FETCH_GAIN_H(VAL) (((VAL) >> 8) & 0x07) +#define IMX678_FETCH_GAIN_L(VAL) ((VAL) & 0xFF) + +#define IMX678_FETCH_EXP_H(VAL) (((VAL) >> 16) & 0x0F) +#define IMX678_FETCH_EXP_M(VAL) (((VAL) >> 8) & 0xFF) +#define IMX678_FETCH_EXP_L(VAL) ((VAL) & 0xFF) + +#define IMX678_FETCH_RHS1_H(VAL) (((VAL) >> 16) & 0x0F) +#define IMX678_FETCH_RHS1_M(VAL) (((VAL) >> 8) & 0xFF) +#define IMX678_FETCH_RHS1_L(VAL) ((VAL) & 0xFF) + +#define IMX678_FETCH_VTS_H(VAL) (((VAL) >> 16) & 0x0F) +#define IMX678_FETCH_VTS_M(VAL) (((VAL) >> 8) & 0xFF) +#define IMX678_FETCH_VTS_L(VAL) ((VAL) & 0xFF) + +#define IMX678_VTS_REG_L 0x3028 +#define IMX678_VTS_REG_M 0x3029 +#define IMX678_VTS_REG_H 0x302a + +#define IMX678_MIRROR_BIT_MASK BIT(0) +#define IMX678_FLIP_BIT_MASK BIT(1) +#define IMX678_FLIP_REG 0x3021 +#define IMX678_MIRROR_REG 0x3020 + +#define REG_NULL 0xFFFF +#define REG_DELAY 0xFFFE + +#define IMX678_REG_VALUE_08BIT 1 +#define IMX678_REG_VALUE_16BIT 2 +#define IMX678_REG_VALUE_24BIT 3 + +#define IMX678_GROUP_HOLD_REG 0x3001 +#define IMX678_GROUP_HOLD_START 0x01 +#define IMX678_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 RKMODULE_CAMERA_FASTBOOT_ENABLE "rockchip,camera_fastboot" + +#define IMX678_NAME "imx678" + +static const char * const imx678_supply_names[] = { + "dvdd", /* Digital core power */ + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ +}; + +#define IMX678_NUM_SUPPLIES ARRAY_SIZE(imx678_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +struct imx678_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; + u32 vc[PAD_MAX]; + u32 xvclk; +}; + +struct imx678 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *power_gpio; + struct regulator_bulk_data supplies[IMX678_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; + u32 is_thunderboot; + bool is_thunderboot_ng; + bool is_first_streamoff; + const struct imx678_mode *supported_modes; + const struct imx678_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; + struct v4l2_fwnode_endpoint bus_cfg; + struct cam_sw_info *cam_sw_inf; + int rhs1_old; + int rhs2_old; + u32 cur_exposure[3]; + u32 cur_gain[3]; + u32 pclk; + u32 tline; + bool is_tline_init; +}; + +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_imx678(sd) container_of(sd, struct imx678, subdev) + +/* + * Xclk 37.125Mhz datarate 1188 12bit 4lane + */ +static __maybe_unused const struct regval imx678_linear_12bit_3840x2160_1188M_regs[] = { + {0x3000, 0x01}, //STANDBY + {0x3002, 0x01}, //XMSTA + {0x3014, 0x01}, //INCK_SEL[3:0] + {0x3015, 0x04}, //DATARATE_SEL[3:0] + {0x3018, 0x00}, //WINMODE[4:0] + {0x301A, 0x00}, //WDMODE + {0x301B, 0x00}, //ADDMODE[1:0] + {0x301C, 0x00}, //THIN_V_EN + {0x301E, 0x01}, //VCMODE + {0x3020, 0x00}, //HREVERSE + {0x3021, 0x00}, //VREVERSE + {0x3022, 0x01}, //ADBIT[1:0] + {0x3023, 0x01}, //MDBIT + {0x3028, 0xCA}, //VMAX[19:0] + {0x3029, 0x08}, //VMAX[19:0] + {0x302A, 0x00}, //VMAX[19:0] + {0x302C, 0x4C}, //HMAX[15:0] + {0x302D, 0x04}, //HMAX[15:0] + {0x3030, 0x00}, //FDG_SEL0[1:0] + {0x303C, 0x00}, //PIX_HST[12:0] + {0x303D, 0x00}, //PIX_HST[12:0] + {0x303E, 0x10}, //PIX_HWIDTH[12:0] + {0x303F, 0x0F}, //PIX_HWIDTH[12:0] + {0x3040, 0x03}, //LANEMODE[2:0] + {0x3044, 0x00}, //PIX_VST[11:0] + {0x3045, 0x00}, //PIX_VST[11:0] + {0x3046, 0x84}, //PIX_VWIDTH[11:0] + {0x3047, 0x08}, //PIX_VWIDTH[11:0] + {0x3050, 0x27}, //SHR0[19:0] + {0x3051, 0x06}, //SHR0[19:0] + {0x3052, 0x00}, //SHR0[19:0] + {0x30A6, 0x00}, //XVS_DRV[1:0] + {0x30DC, 0x32}, //BLKLEVEL[11:0] + {0x30DD, 0x40}, //BLKLEVEL[11:0] + {0x3460, 0x22}, + {0x355A, 0x64}, + {0x3A02, 0x7A}, + {0x3A10, 0xEC}, + {0x3A12, 0x71}, + {0x3A14, 0xDE}, + {0x3A20, 0x2B}, + {0x3A24, 0x22}, + {0x3A25, 0x25}, + {0x3A26, 0x2A}, + {0x3A27, 0x2C}, + {0x3A28, 0x39}, + {0x3A29, 0x38}, + {0x3A30, 0x04}, + {0x3A31, 0x04}, + {0x3A32, 0x03}, + {0x3A33, 0x03}, + {0x3A34, 0x09}, + {0x3A35, 0x06}, + {0x3A38, 0xCD}, + {0x3A3A, 0x4C}, + {0x3A3C, 0xB9}, + {0x3A3E, 0x30}, + {0x3A40, 0x2C}, + {0x3A42, 0x39}, + {0x3A4E, 0x00}, + {0x3A52, 0x00}, + {0x3A56, 0x00}, + {0x3A5A, 0x00}, + {0x3A5E, 0x00}, + {0x3A62, 0x00}, + {0x3A6E, 0xA0}, + {0x3A70, 0x50}, + {0x3A8C, 0x04}, + {0x3A8D, 0x03}, + {0x3A8E, 0x09}, + {0x3A90, 0x38}, + {0x3A91, 0x42}, + {0x3A92, 0x3C}, + {0x3B0E, 0xF3}, + {0x3B12, 0xE5}, + {0x3B27, 0xC0}, + {0x3B2E, 0xEF}, + {0x3B30, 0x6A}, + {0x3B32, 0xF6}, + {0x3B36, 0xE1}, + {0x3B3A, 0xE8}, + {0x3B5A, 0x17}, + {0x3B5E, 0xEF}, + {0x3B60, 0x6A}, + {0x3B62, 0xF6}, + {0x3B66, 0xE1}, + {0x3B6A, 0xE8}, + {0x3B88, 0xEC}, + {0x3B8A, 0xED}, + {0x3B94, 0x71}, + {0x3B96, 0x72}, + {0x3B98, 0xDE}, + {0x3B9A, 0xDF}, + {0x3C0F, 0x06}, + {0x3C10, 0x06}, + {0x3C11, 0x06}, + {0x3C12, 0x06}, + {0x3C13, 0x06}, + {0x3C18, 0x20}, + {0x3C3A, 0x7A}, + {0x3C40, 0xF4}, + {0x3C48, 0xE6}, + {0x3C54, 0xCE}, + {0x3C56, 0xD0}, + {0x3C6C, 0x53}, + {0x3C6E, 0x55}, + {0x3C70, 0xC0}, + {0x3C72, 0xC2}, + {0x3C7E, 0xCE}, + {0x3C8C, 0xCF}, + {0x3C8E, 0xEB}, + {0x3C98, 0x54}, + {0x3C9A, 0x70}, + {0x3C9C, 0xC1}, + {0x3C9E, 0xDD}, + {0x3CB0, 0x7A}, + {0x3CB2, 0xBA}, + {0x3CC8, 0xBC}, + {0x3CCA, 0x7C}, + {0x3CD4, 0xEA}, + {0x3CD5, 0x01}, + {0x3CD6, 0x4A}, + {0x3CD8, 0x00}, + {0x3CD9, 0x00}, + {0x3CDA, 0xFF}, + {0x3CDB, 0x03}, + {0x3CDC, 0x00}, + {0x3CDD, 0x00}, + {0x3CDE, 0xFF}, + {0x3CDF, 0x03}, + {0x3CE4, 0x4C}, + {0x3CE6, 0xEC}, + {0x3CE7, 0x01}, + {0x3CE8, 0xFF}, + {0x3CE9, 0x03}, + {0x3CEA, 0x00}, + {0x3CEB, 0x00}, + {0x3CEC, 0xFF}, + {0x3CED, 0x03}, + {0x3CEE, 0x00}, + {0x3CEF, 0x00}, + {0x3E28, 0x82}, + {0x3E2A, 0x80}, + {0x3E30, 0x85}, + {0x3E32, 0x7D}, + {0x3E5C, 0xCE}, + {0x3E5E, 0xD3}, + {0x3E70, 0x53}, + {0x3E72, 0x58}, + {0x3E74, 0xC0}, + {0x3E76, 0xC5}, + {0x3E78, 0xC0}, + {0x3E79, 0x01}, + {0x3E7A, 0xD4}, + {0x3E7B, 0x01}, + {0x3EB4, 0x0B}, + {0x3EB5, 0x02}, + {0x3EB6, 0x4D}, + {0x3EEC, 0xF3}, + {0x3EEE, 0xE7}, + {0x3F01, 0x01}, + {0x3F24, 0x10}, + {0x3F28, 0x2D}, + {0x3F2A, 0x2D}, + {0x3F2C, 0x2D}, + {0x3F2E, 0x2D}, + {0x3F30, 0x23}, + {0x3F38, 0x2D}, + {0x3F3A, 0x2D}, + {0x3F3C, 0x2D}, + {0x3F3E, 0x28}, + {0x3F40, 0x1E}, + {0x3F48, 0x2D}, + {0x3F4A, 0x2D}, + {0x4004, 0xE4}, + {0x4006, 0xFF}, + {0x4018, 0x69}, + {0x401A, 0x84}, + {0x401C, 0xD6}, + {0x401E, 0xF1}, + {0x4038, 0xDE}, + {0x403A, 0x00}, + {0x403B, 0x01}, + {0x404C, 0x63}, + {0x404E, 0x85}, + {0x4050, 0xD0}, + {0x4052, 0xF2}, + {0x4108, 0xDD}, + {0x410A, 0xF7}, + {0x411C, 0x62}, + {0x411E, 0x7C}, + {0x4120, 0xCF}, + {0x4122, 0xE9}, + {0x4138, 0xE6}, + {0x413A, 0xF1}, + {0x414C, 0x6B}, + {0x414E, 0x76}, + {0x4150, 0xD8}, + {0x4152, 0xE3}, + {0x417E, 0x03}, + {0x417F, 0x01}, + {0x4186, 0xE0}, + {0x4190, 0xF3}, + {0x4192, 0xF7}, + {0x419C, 0x78}, + {0x419E, 0x7C}, + {0x41A0, 0xE5}, + {0x41A2, 0xE9}, + {0x41C8, 0xE2}, + {0x41CA, 0xFD}, + {0x41DC, 0x67}, + {0x41DE, 0x82}, + {0x41E0, 0xD4}, + {0x41E2, 0xEF}, + {0x4200, 0xDE}, + {0x4202, 0xDA}, + {0x4218, 0x63}, + {0x421A, 0x5F}, + {0x421C, 0xD0}, + {0x421E, 0xCC}, + {0x425A, 0x82}, + {0x425C, 0xEF}, + {0x4348, 0xFE}, + {0x4349, 0x06}, + {0x4352, 0xCE}, + {0x4420, 0x0B}, + {0x4421, 0x02}, + {0x4422, 0x4D}, + {0x4426, 0xF5}, + {0x442A, 0xE7}, + {0x4432, 0xF5}, + {0x4436, 0xE7}, + {0x4466, 0xB4}, + {0x446E, 0x32}, + {0x449F, 0x1C}, + {0x44A4, 0x2C}, + {0x44A6, 0x2C}, + {0x44A8, 0x2C}, + {0x44AA, 0x2C}, + {0x44B4, 0x2C}, + {0x44B6, 0x2C}, + {0x44B8, 0x2C}, + {0x44BA, 0x2C}, + {0x44C4, 0x2C}, + {0x44C6, 0x2C}, + {0x44C8, 0x2C}, + {0x4506, 0xF3}, + {0x450E, 0xE5}, + {0x4516, 0xF3}, + {0x4522, 0xE5}, + {0x4524, 0xF3}, + {0x452C, 0xE5}, + {0x453C, 0x22}, + {0x453D, 0x1B}, + {0x453E, 0x1B}, + {0x453F, 0x15}, + {0x4540, 0x15}, + {0x4541, 0x15}, + {0x4542, 0x15}, + {0x4543, 0x15}, + {0x4544, 0x15}, + {0x4548, 0x00}, + {0x4549, 0x01}, + {0x454A, 0x01}, + {0x454B, 0x06}, + {0x454C, 0x06}, + {0x454D, 0x06}, + {0x454E, 0x06}, + {0x454F, 0x06}, + {0x4550, 0x06}, + {0x4554, 0x55}, + {0x4555, 0x02}, + {0x4556, 0x42}, + {0x4557, 0x05}, + {0x4558, 0xFD}, + {0x4559, 0x05}, + {0x455A, 0x94}, + {0x455B, 0x06}, + {0x455D, 0x06}, + {0x455E, 0x49}, + {0x455F, 0x07}, + {0x4560, 0x7F}, + {0x4561, 0x07}, + {0x4562, 0xA5}, + {0x4564, 0x55}, + {0x4565, 0x02}, + {0x4566, 0x42}, + {0x4567, 0x05}, + {0x4568, 0xFD}, + {0x4569, 0x05}, + {0x456A, 0x94}, + {0x456B, 0x06}, + {0x456D, 0x06}, + {0x456E, 0x49}, + {0x456F, 0x07}, + {0x4572, 0xA5}, + {0x460C, 0x7D}, + {0x460E, 0xB1}, + {0x4614, 0xA8}, + {0x4616, 0xB2}, + {0x461C, 0x7E}, + {0x461E, 0xA7}, + {0x4624, 0xA8}, + {0x4626, 0xB2}, + {0x462C, 0x7E}, + {0x462E, 0x8A}, + {0x4630, 0x94}, + {0x4632, 0xA7}, + {0x4634, 0xFB}, + {0x4636, 0x2F}, + {0x4638, 0x81}, + {0x4639, 0x01}, + {0x463A, 0xB5}, + {0x463B, 0x01}, + {0x463C, 0x26}, + {0x463E, 0x30}, + {0x4640, 0xAC}, + {0x4641, 0x01}, + {0x4642, 0xB6}, + {0x4643, 0x01}, + {0x4644, 0xFC}, + {0x4646, 0x25}, + {0x4648, 0x82}, + {0x4649, 0x01}, + {0x464A, 0xAB}, + {0x464B, 0x01}, + {0x464C, 0x26}, + {0x464E, 0x30}, + {0x4654, 0xFC}, + {0x4656, 0x08}, + {0x4658, 0x12}, + {0x465A, 0x25}, + {0x4662, 0xFC}, + {0x46A2, 0xFB}, + {0x46D6, 0xF3}, + {0x46E6, 0x00}, + {0x46E8, 0xFF}, + {0x46E9, 0x03}, + {0x46EC, 0x7A}, + {0x46EE, 0xE5}, + {0x46F4, 0xEE}, + {0x46F6, 0xF2}, + {0x470C, 0xFF}, + {0x470D, 0x03}, + {0x470E, 0x00}, + {0x4714, 0xE0}, + {0x4716, 0xE4}, + {0x471E, 0xED}, + {0x472E, 0x00}, + {0x4730, 0xFF}, + {0x4731, 0x03}, + {0x4734, 0x7B}, + {0x4736, 0xDF}, + {0x4754, 0x7D}, + {0x4756, 0x8B}, + {0x4758, 0x93}, + {0x475A, 0xB1}, + {0x475C, 0xFB}, + {0x475E, 0x09}, + {0x4760, 0x11}, + {0x4762, 0x2F}, + {0x4766, 0xCC}, + {0x4776, 0xCB}, + {0x477E, 0x4A}, + {0x478E, 0x49}, + {0x4794, 0x7C}, + {0x4796, 0x8F}, + {0x4798, 0xB3}, + {0x4799, 0x00}, + {0x479A, 0xCC}, + {0x479C, 0xC1}, + {0x479E, 0xCB}, + {0x47A4, 0x7D}, + {0x47A6, 0x8E}, + {0x47A8, 0xB4}, + {0x47A9, 0x00}, + {0x47AA, 0xC0}, + {0x47AC, 0xFA}, + {0x47AE, 0x0D}, + {0x47B0, 0x31}, + {0x47B1, 0x01}, + {0x47B2, 0x4A}, + {0x47B3, 0x01}, + {0x47B4, 0x3F}, + {0x47B6, 0x49}, + {0x47BC, 0xFB}, + {0x47BE, 0x0C}, + {0x47C0, 0x32}, + {0x47C1, 0x01}, + {0x47C2, 0x3E}, + {0x47C3, 0x01}, + {0x3002, 0x00}, + {REG_DELAY, 0x1E},//wait_ms(30) + {REG_NULL, 0x00}, +}; + +/* + * IMX678LQJ + * All-pixel scan CSI-2_4lane 37.125MHz + * AD:10bit Output:10bit 1782Mbps + * Master Mode LCG Mode DOL HDR 2frame VC 30fps + * Integration Time LEF:24ms SEF:1.007ms + */ +static __maybe_unused const struct regval imx678_hdr2_10bit_3840x2160_1782M_regs[] = { + {0x3000, 0x01}, + {0x3002, 0x01}, + {0x3014, 0x01}, + {0x3015, 0x02}, + {0x301A, 0x01}, + {0x301C, 0x01}, + {0x3022, 0x01}, + {0x3023, 0x01}, + {0x302C, 0x26}, + {0x302D, 0x02}, + {0x3050, 0xEC}, + {0x3051, 0x04}, + {0x3054, 0x05}, + {0x3055, 0x00}, + {0x3060, 0x8D}, + {0x3061, 0x00}, + {0x30A6, 0x00}, + {0x3400, 0x00}, + {0x3460, 0x22}, + {0x355A, 0x64}, + {0x3A02, 0x7A}, + {0x3A10, 0xEC}, + {0x3A12, 0x71}, + {0x3A14, 0xDE}, + {0x3A20, 0x2B}, + {0x3A24, 0x22}, + {0x3A25, 0x25}, + {0x3A26, 0x2A}, + {0x3A27, 0x2C}, + {0x3A28, 0x39}, + {0x3A29, 0x38}, + {0x3A30, 0x04}, + {0x3A31, 0x04}, + {0x3A32, 0x03}, + {0x3A33, 0x03}, + {0x3A34, 0x09}, + {0x3A35, 0x06}, + {0x3A38, 0xCD}, + {0x3A3A, 0x4C}, + {0x3A3C, 0xB9}, + {0x3A3E, 0x30}, + {0x3A40, 0x2C}, + {0x3A42, 0x39}, + {0x3A4E, 0x00}, + {0x3A52, 0x00}, + {0x3A56, 0x00}, + {0x3A5A, 0x00}, + {0x3A5E, 0x00}, + {0x3A62, 0x00}, + {0x3A6E, 0xA0}, + {0x3A70, 0x50}, + {0x3A8C, 0x04}, + {0x3A8D, 0x03}, + {0x3A8E, 0x09}, + {0x3A90, 0x38}, + {0x3A91, 0x42}, + {0x3A92, 0x3C}, + {0x3B0E, 0xF3}, + {0x3B12, 0xE5}, + {0x3B27, 0xC0}, + {0x3B2E, 0xEF}, + {0x3B30, 0x6A}, + {0x3B32, 0xF6}, + {0x3B36, 0xE1}, + {0x3B3A, 0xE8}, + {0x3B5A, 0x17}, + {0x3B5E, 0xEF}, + {0x3B60, 0x6A}, + {0x3B62, 0xF6}, + {0x3B66, 0xE1}, + {0x3B6A, 0xE8}, + {0x3B88, 0xEC}, + {0x3B8A, 0xED}, + {0x3B94, 0x71}, + {0x3B96, 0x72}, + {0x3B98, 0xDE}, + {0x3B9A, 0xDF}, + {0x3C0F, 0x06}, + {0x3C10, 0x06}, + {0x3C11, 0x06}, + {0x3C12, 0x06}, + {0x3C13, 0x06}, + {0x3C18, 0x20}, + {0x3C3A, 0x7A}, + {0x3C40, 0xF4}, + {0x3C48, 0xE6}, + {0x3C54, 0xCE}, + {0x3C56, 0xD0}, + {0x3C6C, 0x53}, + {0x3C6E, 0x55}, + {0x3C70, 0xC0}, + {0x3C72, 0xC2}, + {0x3C7E, 0xCE}, + {0x3C8C, 0xCF}, + {0x3C8E, 0xEB}, + {0x3C98, 0x54}, + {0x3C9A, 0x70}, + {0x3C9C, 0xC1}, + {0x3C9E, 0xDD}, + {0x3CB0, 0x7A}, + {0x3CB2, 0xBA}, + {0x3CC8, 0xBC}, + {0x3CCA, 0x7C}, + {0x3CD4, 0xEA}, + {0x3CD5, 0x01}, + {0x3CD6, 0x4A}, + {0x3CD8, 0x00}, + {0x3CD9, 0x00}, + {0x3CDA, 0xFF}, + {0x3CDB, 0x03}, + {0x3CDC, 0x00}, + {0x3CDD, 0x00}, + {0x3CDE, 0xFF}, + {0x3CDF, 0x03}, + {0x3CE4, 0x4C}, + {0x3CE6, 0xEC}, + {0x3CE7, 0x01}, + {0x3CE8, 0xFF}, + {0x3CE9, 0x03}, + {0x3CEA, 0x00}, + {0x3CEB, 0x00}, + {0x3CEC, 0xFF}, + {0x3CED, 0x03}, + {0x3CEE, 0x00}, + {0x3CEF, 0x00}, + {0x3E28, 0x82}, + {0x3E2A, 0x80}, + {0x3E30, 0x85}, + {0x3E32, 0x7D}, + {0x3E5C, 0xCE}, + {0x3E5E, 0xD3}, + {0x3E70, 0x53}, + {0x3E72, 0x58}, + {0x3E74, 0xC0}, + {0x3E76, 0xC5}, + {0x3E78, 0xC0}, + {0x3E79, 0x01}, + {0x3E7A, 0xD4}, + {0x3E7B, 0x01}, + {0x3EB4, 0x0B}, + {0x3EB5, 0x02}, + {0x3EB6, 0x4D}, + {0x3EEC, 0xF3}, + {0x3EEE, 0xE7}, + {0x3F01, 0x01}, + {0x3F24, 0x10}, + {0x3F28, 0x2D}, + {0x3F2A, 0x2D}, + {0x3F2C, 0x2D}, + {0x3F2E, 0x2D}, + {0x3F30, 0x23}, + {0x3F38, 0x2D}, + {0x3F3A, 0x2D}, + {0x3F3C, 0x2D}, + {0x3F3E, 0x28}, + {0x3F40, 0x1E}, + {0x3F48, 0x2D}, + {0x3F4A, 0x2D}, + {0x4004, 0xE4}, + {0x4006, 0xFF}, + {0x4018, 0x69}, + {0x401A, 0x84}, + {0x401C, 0xD6}, + {0x401E, 0xF1}, + {0x4038, 0xDE}, + {0x403A, 0x00}, + {0x403B, 0x01}, + {0x404C, 0x63}, + {0x404E, 0x85}, + {0x4050, 0xD0}, + {0x4052, 0xF2}, + {0x4108, 0xDD}, + {0x410A, 0xF7}, + {0x411C, 0x62}, + {0x411E, 0x7C}, + {0x4120, 0xCF}, + {0x4122, 0xE9}, + {0x4138, 0xE6}, + {0x413A, 0xF1}, + {0x414C, 0x6B}, + {0x414E, 0x76}, + {0x4150, 0xD8}, + {0x4152, 0xE3}, + {0x417E, 0x03}, + {0x417F, 0x01}, + {0x4186, 0xE0}, + {0x4190, 0xF3}, + {0x4192, 0xF7}, + {0x419C, 0x78}, + {0x419E, 0x7C}, + {0x41A0, 0xE5}, + {0x41A2, 0xE9}, + {0x41C8, 0xE2}, + {0x41CA, 0xFD}, + {0x41DC, 0x67}, + {0x41DE, 0x82}, + {0x41E0, 0xD4}, + {0x41E2, 0xEF}, + {0x4200, 0xDE}, + {0x4202, 0xDA}, + {0x4218, 0x63}, + {0x421A, 0x5F}, + {0x421C, 0xD0}, + {0x421E, 0xCC}, + {0x425A, 0x82}, + {0x425C, 0xEF}, + {0x4348, 0xFE}, + {0x4349, 0x06}, + {0x4352, 0xCE}, + {0x4420, 0x0B}, + {0x4421, 0x02}, + {0x4422, 0x4D}, + {0x4426, 0xF5}, + {0x442A, 0xE7}, + {0x4432, 0xF5}, + {0x4436, 0xE7}, + {0x4466, 0xB4}, + {0x446E, 0x32}, + {0x449F, 0x1C}, + {0x44A4, 0x2C}, + {0x44A6, 0x2C}, + {0x44A8, 0x2C}, + {0x44AA, 0x2C}, + {0x44B4, 0x2C}, + {0x44B6, 0x2C}, + {0x44B8, 0x2C}, + {0x44BA, 0x2C}, + {0x44C4, 0x2C}, + {0x44C6, 0x2C}, + {0x44C8, 0x2C}, + {0x4506, 0xF3}, + {0x450E, 0xE5}, + {0x4516, 0xF3}, + {0x4522, 0xE5}, + {0x4524, 0xF3}, + {0x452C, 0xE5}, + {0x453C, 0x22}, + {0x453D, 0x1B}, + {0x453E, 0x1B}, + {0x453F, 0x15}, + {0x4540, 0x15}, + {0x4541, 0x15}, + {0x4542, 0x15}, + {0x4543, 0x15}, + {0x4544, 0x15}, + {0x4548, 0x00}, + {0x4549, 0x01}, + {0x454A, 0x01}, + {0x454B, 0x06}, + {0x454C, 0x06}, + {0x454D, 0x06}, + {0x454E, 0x06}, + {0x454F, 0x06}, + {0x4550, 0x06}, + {0x4554, 0x55}, + {0x4555, 0x02}, + {0x4556, 0x42}, + {0x4557, 0x05}, + {0x4558, 0xFD}, + {0x4559, 0x05}, + {0x455A, 0x94}, + {0x455B, 0x06}, + {0x455D, 0x06}, + {0x455E, 0x49}, + {0x455F, 0x07}, + {0x4560, 0x7F}, + {0x4561, 0x07}, + {0x4562, 0xA5}, + {0x4564, 0x55}, + {0x4565, 0x02}, + {0x4566, 0x42}, + {0x4567, 0x05}, + {0x4568, 0xFD}, + {0x4569, 0x05}, + {0x456A, 0x94}, + {0x456B, 0x06}, + {0x456D, 0x06}, + {0x456E, 0x49}, + {0x456F, 0x07}, + {0x4572, 0xA5}, + {0x460C, 0x7D}, + {0x460E, 0xB1}, + {0x4614, 0xA8}, + {0x4616, 0xB2}, + {0x461C, 0x7E}, + {0x461E, 0xA7}, + {0x4624, 0xA8}, + {0x4626, 0xB2}, + {0x462C, 0x7E}, + {0x462E, 0x8A}, + {0x4630, 0x94}, + {0x4632, 0xA7}, + {0x4634, 0xFB}, + {0x4636, 0x2F}, + {0x4638, 0x81}, + {0x4639, 0x01}, + {0x463A, 0xB5}, + {0x463B, 0x01}, + {0x463C, 0x26}, + {0x463E, 0x30}, + {0x4640, 0xAC}, + {0x4641, 0x01}, + {0x4642, 0xB6}, + {0x4643, 0x01}, + {0x4644, 0xFC}, + {0x4646, 0x25}, + {0x4648, 0x82}, + {0x4649, 0x01}, + {0x464A, 0xAB}, + {0x464B, 0x01}, + {0x464C, 0x26}, + {0x464E, 0x30}, + {0x4654, 0xFC}, + {0x4656, 0x08}, + {0x4658, 0x12}, + {0x465A, 0x25}, + {0x4662, 0xFC}, + {0x46A2, 0xFB}, + {0x46D6, 0xF3}, + {0x46E6, 0x00}, + {0x46E8, 0xFF}, + {0x46E9, 0x03}, + {0x46EC, 0x7A}, + {0x46EE, 0xE5}, + {0x46F4, 0xEE}, + {0x46F6, 0xF2}, + {0x470C, 0xFF}, + {0x470D, 0x03}, + {0x470E, 0x00}, + {0x4714, 0xE0}, + {0x4716, 0xE4}, + {0x471E, 0xED}, + {0x472E, 0x00}, + {0x4730, 0xFF}, + {0x4731, 0x03}, + {0x4734, 0x7B}, + {0x4736, 0xDF}, + {0x4754, 0x7D}, + {0x4756, 0x8B}, + {0x4758, 0x93}, + {0x475A, 0xB1}, + {0x475C, 0xFB}, + {0x475E, 0x09}, + {0x4760, 0x11}, + {0x4762, 0x2F}, + {0x4766, 0xCC}, + {0x4776, 0xCB}, + {0x477E, 0x4A}, + {0x478E, 0x49}, + {0x4794, 0x7C}, + {0x4796, 0x8F}, + {0x4798, 0xB3}, + {0x4799, 0x00}, + {0x479A, 0xCC}, + {0x479C, 0xC1}, + {0x479E, 0xCB}, + {0x47A4, 0x7D}, + {0x47A6, 0x8E}, + {0x47A8, 0xB4}, + {0x47A9, 0x00}, + {0x47AA, 0xC0}, + {0x47AC, 0xFA}, + {0x47AE, 0x0D}, + {0x47B0, 0x31}, + {0x47B1, 0x01}, + {0x47B2, 0x4A}, + {0x47B3, 0x01}, + {0x47B4, 0x3F}, + {0x47B6, 0x49}, + {0x47BC, 0xFB}, + {0x47BE, 0x0C}, + {0x47C0, 0x32}, + {0x47C1, 0x01}, + {0x47C2, 0x3E}, + {0x47C3, 0x01}, + {0x4E3C, 0x07}, + {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 imx678_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}, +}; + +/* + * 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 imx678_mode supported_modes[] = { + /* + * frame rate = 1 / (Vtt * 1H) = 1 / (VMAX * 1H) + * VMAX >= (PIX_VWIDTH / 2) + 46 = height + 46 + */ + { + .bus_fmt = MEDIA_BUS_FMT_SRGGB12_1X12, + .width = 3840, + .height = 2160, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x08ca - 0x08, + .hts_def = 0x044c * IMX678_4LANES, + .vts_def = 0x08ca, + .reg_list = imx678_linear_12bit_3840x2160_1188M_regs, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 1, + .bpp = 12, + .vc[PAD0] = 0, + .xvclk = IMX678_XVCLK_FREQ_37M, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 3840, + .height = 2160, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x08fc * 2 - 0x0da8, + .hts_def = 0x0226 * IMX678_4LANES * 2, + /* + * IMX678 HDR mode T-line is half of Linear mode, + * make vts double to workaround. + */ + .vts_def = 0x08fc * 2, + .reg_list = imx678_hdr2_10bit_3840x2160_1782M_regs, + .hdr_mode = HDR_X2, + .mipi_freq_idx = 0, + .bpp = 10, + .vc[PAD0] = 1, + .vc[PAD1] = 0,//L->csi wr0 + .vc[PAD2] = 1, + .vc[PAD3] = 1,//M->csi wr2 + .xvclk = IMX678_XVCLK_FREQ_37M, + }, + +}; + +static const s64 link_freq_items[] = { + MIPI_FREQ_1782M, + MIPI_FREQ_1188M, +}; + +/* Write registers up to 4 at a time */ +static int imx678_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 imx678_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 = imx678_write_reg(client, regs[i].addr, + IMX678_REG_VALUE_08BIT, regs[i].val); + } + } + return ret; +} + +/* Read registers up to 4 at a time */ +static int imx678_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 imx678_get_reso_dist(const struct imx678_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct imx678_mode * +imx678_find_best_fit(struct imx678 *imx678, 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 < imx678->cfg_num; i++) { + dist = imx678_get_reso_dist(&imx678->supported_modes[i], framefmt); + if ((cur_best_fit_dist == -1 || dist < cur_best_fit_dist) && + imx678->supported_modes[i].bus_fmt == framefmt->code) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + dev_info(&imx678->client->dev, "%s: cur_best_fit(%d)", + __func__, cur_best_fit); + + return &imx678->supported_modes[cur_best_fit]; +} + +static int __imx678_power_on(struct imx678 *imx678); + +static void imx678_change_mode(struct imx678 *imx678, const struct imx678_mode *mode) +{ + if (imx678->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) { + imx678->is_thunderboot = false; + imx678->is_thunderboot_ng = true; + __imx678_power_on(imx678); + } + imx678->cur_mode = mode; + imx678->cur_vts = imx678->cur_mode->vts_def; + dev_info(&imx678->client->dev, "set fmt: cur_mode: %dx%d, hdr: %d, bpp: %d\n", + mode->width, mode->height, mode->hdr_mode, mode->bpp); +} + +static int imx678_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct imx678 *imx678 = to_imx678(sd); + const struct imx678_mode *mode; + s64 h_blank, vblank_def, vblank_min; + u64 pixel_rate = 0; + u8 lanes = imx678->bus_cfg.bus.mipi_csi2.num_data_lanes; + + mutex_lock(&imx678->mutex); + + mode = imx678_find_best_fit(imx678, 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, sd_state, fmt->pad) = fmt->format; +#else + mutex_unlock(&imx678->mutex); + return -ENOTTY; +#endif + } else { + imx678_change_mode(imx678, mode); + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(imx678->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(imx678->vblank, vblank_min, + IMX678_VTS_MAX - mode->height, + 1, vblank_def); + __v4l2_ctrl_s_ctrl(imx678->vblank, vblank_def); + __v4l2_ctrl_s_ctrl(imx678->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(imx678->pixel_rate, + pixel_rate); + } + dev_info(&imx678->client->dev, "%s: mode->mipi_freq_idx(%d)", + __func__, mode->mipi_freq_idx); + + mutex_unlock(&imx678->mutex); + + return 0; +} + +static int imx678_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct imx678 *imx678 = to_imx678(sd); + const struct imx678_mode *mode = imx678->cur_mode; + + mutex_lock(&imx678->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); +#else + mutex_unlock(&imx678->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(&imx678->mutex); + + return 0; +} + +static int imx678_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx678 *imx678 = to_imx678(sd); + + if (code->index >= imx678->cfg_num) + return -EINVAL; + + code->code = imx678->supported_modes[code->index].bus_fmt; + + return 0; +} + +static int imx678_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct imx678 *imx678 = to_imx678(sd); + + if (fse->index >= imx678->cfg_num) + return -EINVAL; + + if (fse->code != imx678->supported_modes[fse->index].bus_fmt) + return -EINVAL; + + fse->min_width = imx678->supported_modes[fse->index].width; + fse->max_width = imx678->supported_modes[fse->index].width; + fse->max_height = imx678->supported_modes[fse->index].height; + fse->min_height = imx678->supported_modes[fse->index].height; + + return 0; +} + +static int imx678_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct imx678 *imx678 = to_imx678(sd); + const struct imx678_mode *mode = imx678->cur_mode; + + fi->interval = mode->max_fps; + + return 0; +} + +static int imx678_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct imx678 *imx678 = to_imx678(sd); + u8 lanes = imx678->bus_cfg.bus.mipi_csi2.num_data_lanes; + + config->type = V4L2_MBUS_CSI2_DPHY; + config->bus.mipi_csi2.num_data_lanes = lanes; + + return 0; +} + +static void imx678_get_module_inf(struct imx678 *imx678, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strscpy(inf->base.sensor, IMX678_NAME, sizeof(inf->base.sensor)); + strscpy(inf->base.module, imx678->module_name, + sizeof(inf->base.module)); + strscpy(inf->base.lens, imx678->len_name, sizeof(inf->base.lens)); +} + +static void imx678_get_pclk_and_tline(struct imx678 *imx678) +{ + const struct imx678_mode *mode = imx678->cur_mode; + + imx678->pclk = (u32)div_u64((u64)mode->hts_def * mode->vts_def * + mode->max_fps.denominator, mode->max_fps.numerator); + imx678->tline = (u32)div_u64((u64)mode->hts_def * 1000000000, imx678->pclk); +} + +static void imx678_hdr_exposure_readback(struct imx678 *imx678) +{ + u32 shr, shr_l, shr_m, shr_h; + u32 rhs, rhs_l, rhs_m, rhs_h; + u32 gain, gain_l, gain_h; + int ret = 0; + + if (!imx678->is_tline_init) { + imx678_get_pclk_and_tline(imx678); + imx678->is_tline_init = true; + } + + ret = imx678_read_reg(imx678->client, IMX678_LF_EXPO_REG_L, + IMX678_REG_VALUE_08BIT, &shr_l); + ret |= imx678_read_reg(imx678->client, IMX678_LF_EXPO_REG_M, + IMX678_REG_VALUE_08BIT, &shr_m); + ret |= imx678_read_reg(imx678->client, IMX678_LF_EXPO_REG_H, + IMX678_REG_VALUE_08BIT, &shr_h); + if (!ret) { + shr = (shr_h << 16) | (shr_m << 8) | shr_l; + imx678->cur_exposure[0] = (imx678->cur_vts - shr) * imx678->tline; + } else { + dev_err(&imx678->client->dev, + "imx678 get exposure of long frame failed!\n"); + } + ret = imx678_read_reg(imx678->client, IMX678_LF_GAIN_REG_H, + IMX678_REG_VALUE_08BIT, &gain_h); + ret |= imx678_read_reg(imx678->client, IMX678_LF_GAIN_REG_L, + IMX678_REG_VALUE_08BIT, &gain_l); + if (!ret) { + gain = (gain_h << 8) | gain_l; + imx678->cur_gain[0] = gain * 300;//step=0.3db,factor=1000 + } else { + dev_err(&imx678->client->dev, + "imx678 get gain of long frame failed!\n"); + } + + ret = imx678_read_reg(imx678->client, IMX678_SF1_EXPO_REG_L, + IMX678_REG_VALUE_08BIT, &shr_l); + ret |= imx678_read_reg(imx678->client, IMX678_SF1_EXPO_REG_M, + IMX678_REG_VALUE_08BIT, &shr_m); + ret |= imx678_read_reg(imx678->client, IMX678_SF1_EXPO_REG_H, + IMX678_REG_VALUE_08BIT, &shr_h); + ret |= imx678_read_reg(imx678->client, IMX678_RHS1_REG_L, + IMX678_REG_VALUE_08BIT, &rhs_l); + ret |= imx678_read_reg(imx678->client, IMX678_RHS1_REG_M, + IMX678_REG_VALUE_08BIT, &rhs_m); + ret |= imx678_read_reg(imx678->client, IMX678_RHS1_REG_H, + IMX678_REG_VALUE_08BIT, &rhs_h); + if (!ret) { + shr = (shr_h << 16) | (shr_m << 8) | shr_l; + rhs = (rhs_h << 16) | (rhs_m << 8) | rhs_l; + imx678->cur_exposure[1] = (rhs - shr) * imx678->tline; + } else { + dev_err(&imx678->client->dev, + "imx678 get exposure of %s frame failed!\n", + imx678->cur_mode->hdr_mode == HDR_X2 ? + "short" : "middle"); + } + ret = imx678_read_reg(imx678->client, IMX678_SF1_GAIN_REG_H, + IMX678_REG_VALUE_08BIT, &gain_h); + ret |= imx678_read_reg(imx678->client, IMX678_SF1_GAIN_REG_L, + IMX678_REG_VALUE_08BIT, &gain_l); + if (!ret) { + gain = (gain_h << 8) | gain_l; + imx678->cur_gain[1] = gain * 300;//step=0.3db,factor=1000 + } else { + dev_err(&imx678->client->dev, + "imx678 get gain of %s frame failed!\n", + imx678->cur_mode->hdr_mode == HDR_X2 ? + "short" : "middle"); + } + + if (imx678->cur_mode->hdr_mode == HDR_X3) { + ret = imx678_read_reg(imx678->client, IMX678_SF2_EXPO_REG_L, + IMX678_REG_VALUE_08BIT, &shr_l); + ret |= imx678_read_reg(imx678->client, IMX678_SF2_EXPO_REG_M, + IMX678_REG_VALUE_08BIT, &shr_m); + ret |= imx678_read_reg(imx678->client, IMX678_SF2_EXPO_REG_H, + IMX678_REG_VALUE_08BIT, &shr_h); + ret |= imx678_read_reg(imx678->client, IMX678_RHS2_REG_L, + IMX678_REG_VALUE_08BIT, &rhs_l); + ret |= imx678_read_reg(imx678->client, IMX678_RHS2_REG_M, + IMX678_REG_VALUE_08BIT, &rhs_m); + ret |= imx678_read_reg(imx678->client, IMX678_RHS2_REG_H, + IMX678_REG_VALUE_08BIT, &rhs_h); + if (!ret) { + shr = (shr_h << 16) | (shr_m << 8) | shr_l; + rhs = (rhs_h << 16) | (rhs_m << 8) | rhs_l; + imx678->cur_exposure[2] = (rhs - shr) * imx678->tline; + } else { + dev_err(&imx678->client->dev, + "imx678 get exposure of short frame failed!\n"); + } + ret = imx678_read_reg(imx678->client, IMX678_SF2_GAIN_REG_H, + IMX678_REG_VALUE_08BIT, &gain_h); + ret |= imx678_read_reg(imx678->client, IMX678_SF2_GAIN_REG_L, + IMX678_REG_VALUE_08BIT, &gain_l); + if (!ret) { + gain = (gain_h << 8) | gain_l; + imx678->cur_gain[2] = gain * 300;//step=0.3db,factor=1000 + } else { + dev_err(&imx678->client->dev, + "imx678 get gain of short frame failed!\n"); + } + } +} + +static int imx678_set_hdrae_3frame(struct imx678 *imx678, + struct preisp_hdrae_exp_s *ae) +{ + struct i2c_client *client = imx678->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; + int ret = 0; + u32 fsc; + int rhs1_max = 0; + int shr2_min = 0; + + if (!imx678->has_init_exp && !imx678->streaming) { + imx678->init_hdrae_exp = *ae; + imx678->has_init_exp = true; + dev_dbg(&imx678->client->dev, "imx678 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 = imx678_write_reg(client, IMX678_GROUP_HOLD_REG, + IMX678_REG_VALUE_08BIT, IMX678_GROUP_HOLD_START); + /* gain effect n+1 */ + ret |= imx678_write_reg(client, IMX678_LF_GAIN_REG_H, + IMX678_REG_VALUE_08BIT, IMX678_FETCH_GAIN_H(l_a_gain)); + ret |= imx678_write_reg(client, IMX678_LF_GAIN_REG_L, + IMX678_REG_VALUE_08BIT, IMX678_FETCH_GAIN_L(l_a_gain)); + ret |= imx678_write_reg(client, IMX678_SF1_GAIN_REG_H, + IMX678_REG_VALUE_08BIT, IMX678_FETCH_GAIN_H(m_a_gain)); + ret |= imx678_write_reg(client, IMX678_SF1_GAIN_REG_L, + IMX678_REG_VALUE_08BIT, IMX678_FETCH_GAIN_L(m_a_gain)); + ret |= imx678_write_reg(client, IMX678_SF2_GAIN_REG_H, + IMX678_REG_VALUE_08BIT, IMX678_FETCH_GAIN_H(s_a_gain)); + ret |= imx678_write_reg(client, IMX678_SF2_GAIN_REG_L, + IMX678_REG_VALUE_08BIT, IMX678_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 = imx678->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 (imx678->cur_mode->height == 2160) + 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, imx678->rhs1_old); + + //Dynamic adjustment rhs2 must meet the following conditions + if (imx678->cur_mode->height == 2160) + rhs1_change_limit = imx678->rhs1_old + 3 * BRL_ALL - fsc + 3; + else + rhs1_change_limit = imx678->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, imx678->rhs1_old, rhs1); + + imx678->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, imx678->rhs2_old); + + //Dynamic adjustment rhs2 must meet the following conditions + if (imx678->cur_mode->height == 2160) + rhs2_change_limit = imx678->rhs2_old + 3 * BRL_ALL - fsc + 3; + else + rhs2_change_limit = imx678->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; + + imx678->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 |= imx678_write_reg(client, + IMX678_RHS2_REG_L, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_RHS1_L(rhs2)); + ret |= imx678_write_reg(client, + IMX678_RHS2_REG_M, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_RHS1_M(rhs2)); + ret |= imx678_write_reg(client, + IMX678_RHS2_REG_H, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_RHS1_H(rhs2)); + /* write SEF2 exposure SHR2 regs*/ + ret |= imx678_write_reg(client, + IMX678_SF2_EXPO_REG_L, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_L(shr2)); + ret |= imx678_write_reg(client, + IMX678_SF2_EXPO_REG_M, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_M(shr2)); + ret |= imx678_write_reg(client, + IMX678_SF2_EXPO_REG_H, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_H(shr2)); + /* write SEF1 exposure RHS1 regs*/ + ret |= imx678_write_reg(client, + IMX678_RHS1_REG_L, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_RHS1_L(rhs1)); + ret |= imx678_write_reg(client, + IMX678_RHS1_REG_M, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_RHS1_M(rhs1)); + ret |= imx678_write_reg(client, + IMX678_RHS1_REG_H, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_RHS1_H(rhs1)); + /* write SEF1 exposure SHR1 regs*/ + ret |= imx678_write_reg(client, + IMX678_SF1_EXPO_REG_L, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_L(shr1)); + ret |= imx678_write_reg(client, + IMX678_SF1_EXPO_REG_M, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_M(shr1)); + ret |= imx678_write_reg(client, + IMX678_SF1_EXPO_REG_H, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_H(shr1)); + /* write LF exposure SHR0 regs*/ + ret |= imx678_write_reg(client, + IMX678_LF_EXPO_REG_L, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_L(shr0)); + ret |= imx678_write_reg(client, + IMX678_LF_EXPO_REG_M, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_M(shr0)); + ret |= imx678_write_reg(client, + IMX678_LF_EXPO_REG_H, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_H(shr0)); + + ret |= imx678_write_reg(client, IMX678_GROUP_HOLD_REG, + IMX678_REG_VALUE_08BIT, IMX678_GROUP_HOLD_END); + imx678_hdr_exposure_readback(imx678); + return ret; +} + +static int imx678_set_hdrae(struct imx678 *imx678, + struct preisp_hdrae_exp_s *ae) +{ + struct i2c_client *client = imx678->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; + int ret = 0; + u32 fsc; + + if (!imx678->has_init_exp && !imx678->streaming) { + imx678->init_hdrae_exp = *ae; + imx678->has_init_exp = true; + dev_dbg(&imx678->client->dev, "imx678 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 (imx678->cur_mode->hdr_mode == HDR_X2) { + l_a_gain = m_a_gain; + l_exp_time = m_exp_time; + } + + ret = imx678_write_reg(client, IMX678_GROUP_HOLD_REG, + IMX678_REG_VALUE_08BIT, IMX678_GROUP_HOLD_START); + /* gain effect n+1 */ + ret |= imx678_write_reg(client, IMX678_LF_GAIN_REG_H, + IMX678_REG_VALUE_08BIT, IMX678_FETCH_GAIN_H(l_a_gain)); + ret |= imx678_write_reg(client, IMX678_LF_GAIN_REG_L, + IMX678_REG_VALUE_08BIT, IMX678_FETCH_GAIN_L(l_a_gain)); + ret |= imx678_write_reg(client, IMX678_SF1_GAIN_REG_H, + IMX678_REG_VALUE_08BIT, IMX678_FETCH_GAIN_H(s_a_gain)); + ret |= imx678_write_reg(client, IMX678_SF1_GAIN_REG_L, + IMX678_REG_VALUE_08BIT, IMX678_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 = imx678->cur_vts; + shr0 = fsc - l_exp_time; + + if (imx678->cur_mode->height == 2192) { + rhs1_max = min(RHS1_MAX_X2(BRL_ALL), ((shr0 - 9u) / 4 * 4 + 1)); + rhs1_min = max(SHR1_MIN_X2 + 8u, imx678->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, imx678->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, imx678->rhs1_old, rhs1); + + imx678->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 |= imx678_write_reg(client, + IMX678_RHS1_REG_L, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_RHS1_L(rhs1)); + ret |= imx678_write_reg(client, + IMX678_RHS1_REG_M, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_RHS1_M(rhs1)); + ret |= imx678_write_reg(client, + IMX678_RHS1_REG_H, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_RHS1_H(rhs1)); + + ret |= imx678_write_reg(client, + IMX678_SF1_EXPO_REG_L, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_L(shr1)); + ret |= imx678_write_reg(client, + IMX678_SF1_EXPO_REG_M, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_M(shr1)); + ret |= imx678_write_reg(client, + IMX678_SF1_EXPO_REG_H, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_H(shr1)); + ret |= imx678_write_reg(client, + IMX678_LF_EXPO_REG_L, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_L(shr0)); + ret |= imx678_write_reg(client, + IMX678_LF_EXPO_REG_M, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_M(shr0)); + ret |= imx678_write_reg(client, + IMX678_LF_EXPO_REG_H, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_H(shr0)); + + ret |= imx678_write_reg(client, IMX678_GROUP_HOLD_REG, + IMX678_REG_VALUE_08BIT, IMX678_GROUP_HOLD_END); + imx678_hdr_exposure_readback(imx678); + return ret; +} + +static int imx678_get_channel_info(struct imx678 *imx678, struct rkmodule_channel_info *ch_info) +{ + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + ch_info->vc = imx678->cur_mode->vc[ch_info->index]; + ch_info->width = imx678->cur_mode->width; + ch_info->height = imx678->cur_mode->height; + ch_info->bus_fmt = imx678->cur_mode->bus_fmt; + return 0; +} + +static long imx678_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct imx678 *imx678 = to_imx678(sd); + struct rkmodule_hdr_cfg *hdr; + struct rkmodule_channel_info *ch_info; + u32 i, h, w, stream; + long ret = 0; + const struct imx678_mode *mode; + u64 pixel_rate = 0; + struct rkmodule_csi_dphy_param *dphy_param; + u8 lanes = imx678->bus_cfg.bus.mipi_csi2.num_data_lanes; + struct rkmodule_exp_delay *exp_delay; + struct rkmodule_exp_info *exp_info; + int idx_max = 0; + + switch (cmd) { + case PREISP_CMD_SET_HDRAE_EXP: + if (imx678->cur_mode->hdr_mode == HDR_X2) + ret = imx678_set_hdrae(imx678, arg); + else if (imx678->cur_mode->hdr_mode == HDR_X3) + ret = imx678_set_hdrae_3frame(imx678, arg); + break; + case RKMODULE_GET_MODULE_INFO: + imx678_get_module_inf(imx678, (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 = imx678->cur_mode->hdr_mode; + break; + case RKMODULE_SET_HDR_CFG: + hdr = (struct rkmodule_hdr_cfg *)arg; + w = imx678->cur_mode->width; + h = imx678->cur_mode->height; + for (i = 0; i < imx678->cfg_num; i++) { + if (w == imx678->supported_modes[i].width && + h == imx678->supported_modes[i].height && + imx678->supported_modes[i].hdr_mode == hdr->hdr_mode) { + dev_info(&imx678->client->dev, "set hdr cfg, set mode to %d\n", i); + imx678_change_mode(imx678, &imx678->supported_modes[i]); + break; + } + } + if (i == imx678->cfg_num) { + dev_err(&imx678->client->dev, + "not find hdr mode:%d %dx%d config\n", + hdr->hdr_mode, w, h); + ret = -EINVAL; + } else { + mode = imx678->cur_mode; + if (imx678->streaming) { + ret = imx678_write_reg(imx678->client, IMX678_GROUP_HOLD_REG, + IMX678_REG_VALUE_08BIT, IMX678_GROUP_HOLD_START); + + ret |= imx678_write_array(imx678->client, + imx678->cur_mode->reg_list); + + ret |= imx678_write_reg(imx678->client, IMX678_GROUP_HOLD_REG, + IMX678_REG_VALUE_08BIT, IMX678_GROUP_HOLD_END); + if (ret) + return ret; + } + w = mode->hts_def - imx678->cur_mode->width; + h = mode->vts_def - mode->height; + mutex_lock(&imx678->mutex); + __v4l2_ctrl_modify_range(imx678->hblank, w, w, 1, w); + __v4l2_ctrl_modify_range(imx678->vblank, h, + IMX678_VTS_MAX - mode->height, + 1, h); + __v4l2_ctrl_s_ctrl(imx678->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(imx678->pixel_rate, + pixel_rate); + mutex_unlock(&imx678->mutex); + } + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = imx678_write_reg(imx678->client, IMX678_REG_CTRL_MODE, + IMX678_REG_VALUE_08BIT, IMX678_MODE_STREAMING); + else + ret = imx678_write_reg(imx678->client, IMX678_REG_CTRL_MODE, + IMX678_REG_VALUE_08BIT, IMX678_MODE_SW_STANDBY); + break; + case RKMODULE_GET_SONY_BRL: + if (imx678->cur_mode->width == 3840 && imx678->cur_mode->height == 2160) + *((u32 *)arg) = BRL_ALL; + else + *((u32 *)arg) = BRL_BINNING; + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = imx678_get_channel_info(imx678, ch_info); + break; + case RKMODULE_GET_CSI_DPHY_PARAM: + if (imx678->cur_mode->hdr_mode == HDR_X2) { + dphy_param = (struct rkmodule_csi_dphy_param *)arg; + *dphy_param = dcphy_param; + dev_info(&imx678->client->dev, + "get sensor dphy param\n"); + } else + ret = -EINVAL; + break; + case RKMODULE_GET_EXP_DELAY: + exp_delay = (struct rkmodule_exp_delay *)arg; + exp_delay->exp_delay = 2; + exp_delay->gain_delay = 2; + exp_delay->vts_delay = 1; + break; + case RKMODULE_GET_EXP_INFO: + exp_info = (struct rkmodule_exp_info *)arg; + if (imx678->cur_mode->hdr_mode == NO_HDR) + idx_max = 1; + else if (imx678->cur_mode->hdr_mode == HDR_X2) + idx_max = 2; + else + idx_max = 3; + for (i = 0; i < idx_max; i++) { + exp_info->exp[i] = imx678->cur_exposure[i]; + exp_info->gain[i] = imx678->cur_gain[i]; + } + exp_info->hts = imx678->cur_mode->hts_def; + exp_info->vts = imx678->cur_vts; + exp_info->pclk = imx678->pclk; + exp_info->gain_mode.gain_mode = RKMODULE_GAIN_MODE_DB; + exp_info->gain_mode.factor = 1000; + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long imx678_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; + struct rkmodule_exp_delay *exp_delay; + struct rkmodule_exp_info *exp_info; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = imx678_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 = imx678_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 = imx678_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 = imx678_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 = imx678_ioctl(sd, cmd, hdrae); + kfree(hdrae); + break; + case RKMODULE_SET_QUICK_STREAM: + if (copy_from_user(&stream, up, sizeof(u32))) + return -EFAULT; + ret = imx678_ioctl(sd, cmd, &stream); + break; + case RKMODULE_GET_SONY_BRL: + ret = imx678_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 = imx678_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 = imx678_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; + case RKMODULE_GET_EXP_DELAY: + exp_delay = kzalloc(sizeof(*exp_delay), GFP_KERNEL); + if (!exp_delay) { + ret = -ENOMEM; + return ret; + } + + ret = imx678_ioctl(sd, cmd, exp_delay); + if (!ret) { + ret = copy_to_user(up, exp_delay, sizeof(*exp_delay)); + if (ret) + ret = -EFAULT; + } + kfree(exp_delay); + break; + case RKMODULE_GET_EXP_INFO: + exp_info = kzalloc(sizeof(*exp_info), GFP_KERNEL); + if (!exp_info) { + ret = -ENOMEM; + return ret; + } + + ret = imx678_ioctl(sd, cmd, exp_info); + if (!ret) { + ret = copy_to_user(up, exp_info, sizeof(*exp_info)); + if (ret) + ret = -EFAULT; + } + kfree(exp_info); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __imx678_start_stream(struct imx678 *imx678) +{ + int ret; + + if (!imx678->is_thunderboot) { + ret = imx678_write_array(imx678->client, imx678->cur_mode->reg_list); + + if (ret) + return ret; + } + imx678_get_pclk_and_tline(imx678); + + /* In case these controls are set before streaming */ + ret = __v4l2_ctrl_handler_setup(&imx678->ctrl_handler); + if (ret) + return ret; + if (imx678->has_init_exp && imx678->cur_mode->hdr_mode != NO_HDR) { + imx678->rhs1_old = IMX678_RHS1_DEFAULT; + imx678->rhs2_old = IMX678_RHS2_DEFAULT; + ret = imx678_ioctl(&imx678->subdev, PREISP_CMD_SET_HDRAE_EXP, + &imx678->init_hdrae_exp); + if (ret) { + dev_err(&imx678->client->dev, + "init exp fail in hdr mode\n"); + return ret; + } + } + return imx678_write_reg(imx678->client, IMX678_REG_CTRL_MODE, + IMX678_REG_VALUE_08BIT, 0); +} + +static int __imx678_stop_stream(struct imx678 *imx678) +{ + imx678->has_init_exp = false; + if (imx678->is_thunderboot) + imx678->is_first_streamoff = true; + imx678->is_tline_init = false; + return imx678_write_reg(imx678->client, IMX678_REG_CTRL_MODE, + IMX678_REG_VALUE_08BIT, 1); +} + +static int imx678_s_stream(struct v4l2_subdev *sd, int on) +{ + struct imx678 *imx678 = to_imx678(sd); + struct i2c_client *client = imx678->client; + int ret = 0; + + dev_info(&imx678->client->dev, "s_stream: %d. %dx%d, hdr: %d, bpp: %d\n", + on, imx678->cur_mode->width, imx678->cur_mode->height, + imx678->cur_mode->hdr_mode, imx678->cur_mode->bpp); + + mutex_lock(&imx678->mutex); + on = !!on; + if (on == imx678->streaming) + goto unlock_and_return; + + if (on) { + if (imx678->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) { + imx678->is_thunderboot = false; + __imx678_power_on(imx678); + } + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __imx678_start_stream(imx678); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __imx678_stop_stream(imx678); + pm_runtime_put(&client->dev); + } + + imx678->streaming = on; + +unlock_and_return: + mutex_unlock(&imx678->mutex); + + return ret; +} + +static int imx678_s_power(struct v4l2_subdev *sd, int on) +{ + struct imx678 *imx678 = to_imx678(sd); + struct i2c_client *client = imx678->client; + int ret = 0; + + mutex_lock(&imx678->mutex); + + if (imx678->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; + } + imx678->power_on = true; + } else { + pm_runtime_put(&client->dev); + imx678->power_on = false; + } + +unlock_and_return: + mutex_unlock(&imx678->mutex); + + return ret; +} + +int __imx678_power_on(struct imx678 *imx678) +{ + int ret; + struct device *dev = &imx678->client->dev; + + if (!IS_ERR_OR_NULL(imx678->pins_default)) { + ret = pinctrl_select_state(imx678->pinctrl, + imx678->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + if (!imx678->is_thunderboot) { + if (!IS_ERR(imx678->power_gpio)) + gpiod_direction_output(imx678->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(imx678->reset_gpio)) + gpiod_direction_output(imx678->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(imx678->xvclk, imx678->cur_mode->xvclk); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate\n"); + if (clk_get_rate(imx678->xvclk) != imx678->cur_mode->xvclk) + dev_warn(dev, "xvclk mismatched\n"); + ret = clk_prepare_enable(imx678->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + goto err_clk; + } + + cam_sw_regulator_bulk_init(imx678->cam_sw_inf, IMX678_NUM_SUPPLIES, imx678->supplies); + + if (imx678->is_thunderboot) + return 0; + + ret = regulator_bulk_enable(IMX678_NUM_SUPPLIES, imx678->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto err_pinctrl; + } + + /* At least 20us between XCLR and I2C communication */ + usleep_range(20*1000, 30*1000); + + return 0; + +err_pinctrl: + clk_disable_unprepare(imx678->xvclk); + +err_clk: + if (!IS_ERR(imx678->reset_gpio)) + gpiod_direction_output(imx678->reset_gpio, 1); + if (!IS_ERR_OR_NULL(imx678->pins_sleep)) + pinctrl_select_state(imx678->pinctrl, imx678->pins_sleep); + + return ret; +} + +static void __imx678_power_off(struct imx678 *imx678) +{ + int ret; + struct device *dev = &imx678->client->dev; + + if (imx678->is_thunderboot) { + if (imx678->is_first_streamoff) { + imx678->is_thunderboot = false; + imx678->is_first_streamoff = false; + } else { + return; + } + } + + if (!IS_ERR(imx678->reset_gpio)) + gpiod_direction_output(imx678->reset_gpio, 1); + clk_disable_unprepare(imx678->xvclk); + if (!IS_ERR_OR_NULL(imx678->pins_sleep)) { + ret = pinctrl_select_state(imx678->pinctrl, + imx678->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + if (!IS_ERR(imx678->power_gpio)) + gpiod_direction_output(imx678->power_gpio, 0); + regulator_bulk_disable(IMX678_NUM_SUPPLIES, imx678->supplies); +} + +#if IS_REACHABLE(CONFIG_VIDEO_CAM_SLEEP_WAKEUP) +static int __maybe_unused imx678_resume(struct device *dev) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx678 *imx678 = to_imx678(sd); + + cam_sw_prepare_wakeup(imx678->cam_sw_inf, dev); + + usleep_range(4000, 5000); + cam_sw_write_array(imx678->cam_sw_inf); + + if (__v4l2_ctrl_handler_setup(&imx678->ctrl_handler)) + dev_err(dev, "__v4l2_ctrl_handler_setup fail!"); + + if (imx678->has_init_exp && imx678->cur_mode != NO_HDR) { // hdr mode + ret = imx678_ioctl(&imx678->subdev, PREISP_CMD_SET_HDRAE_EXP, + &imx678->cam_sw_inf->hdr_ae); + if (ret) { + dev_err(&imx678->client->dev, "set exp fail in hdr mode\n"); + return ret; + } + } + return 0; +} + +static int __maybe_unused imx678_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx678 *imx678 = to_imx678(sd); + + cam_sw_write_array_cb_init(imx678->cam_sw_inf, client, + (void *)imx678->cur_mode->reg_list, + (sensor_write_array)imx678_write_array); + cam_sw_prepare_sleep(imx678->cam_sw_inf); + + return 0; +} +#else +#define imx678_resume NULL +#define imx678_suspend NULL +#endif + +static int __maybe_unused imx678_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx678 *imx678 = to_imx678(sd); + + return __imx678_power_on(imx678); +} + +static int __maybe_unused imx678_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx678 *imx678 = to_imx678(sd); + + __imx678_power_off(imx678); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int imx678_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct imx678 *imx678 = to_imx678(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->state, 0); + const struct imx678_mode *def_mode = &imx678->supported_modes[0]; + + mutex_lock(&imx678->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(&imx678->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int imx678_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct imx678 *imx678 = to_imx678(sd); + + if (fie->index >= imx678->cfg_num) + return -EINVAL; + + fie->code = imx678->supported_modes[fie->index].bus_fmt; + fie->width = imx678->supported_modes[fie->index].width; + fie->height = imx678->supported_modes[fie->index].height; + fie->interval = imx678->supported_modes[fie->index].max_fps; + fie->reserved[0] = imx678->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 + +/* + * 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 imx678_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct imx678 *imx678 = to_imx678(sd); + + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + if (imx678->cur_mode->width == 3840) { + sel->r.left = CROP_START(imx678->cur_mode->width, DST_WIDTH_3840); + sel->r.width = DST_WIDTH_3840; + sel->r.top = CROP_START(imx678->cur_mode->height, DST_HEIGHT_2160); + sel->r.height = DST_HEIGHT_2160; + } else if (imx678->cur_mode->width == 1944) { + sel->r.left = CROP_START(imx678->cur_mode->width, DST_WIDTH_1920); + sel->r.width = DST_WIDTH_1920; + sel->r.top = CROP_START(imx678->cur_mode->height, DST_HEIGHT_1080); + sel->r.height = DST_HEIGHT_1080; + } else { + sel->r.left = CROP_START(imx678->cur_mode->width, imx678->cur_mode->width); + sel->r.width = imx678->cur_mode->width; + sel->r.top = CROP_START(imx678->cur_mode->height, imx678->cur_mode->height); + sel->r.height = imx678->cur_mode->height; + } + return 0; + } + return -EINVAL; +} + +static const struct dev_pm_ops imx678_pm_ops = { + SET_RUNTIME_PM_OPS(imx678_runtime_suspend, + imx678_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(imx678_suspend, imx678_resume) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops imx678_internal_ops = { + .open = imx678_open, +}; +#endif + +static const struct v4l2_subdev_core_ops imx678_core_ops = { + .s_power = imx678_s_power, + .ioctl = imx678_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = imx678_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops imx678_video_ops = { + .s_stream = imx678_s_stream, + .g_frame_interval = imx678_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops imx678_pad_ops = { + .enum_mbus_code = imx678_enum_mbus_code, + .enum_frame_size = imx678_enum_frame_sizes, + .enum_frame_interval = imx678_enum_frame_interval, + .get_fmt = imx678_get_fmt, + .set_fmt = imx678_set_fmt, + .get_selection = imx678_get_selection, + .get_mbus_config = imx678_g_mbus_config, +}; + +static const struct v4l2_subdev_ops imx678_subdev_ops = { + .core = &imx678_core_ops, + .video = &imx678_video_ops, + .pad = &imx678_pad_ops, +}; + +static void imx678_exposure_readback(struct imx678 *imx678) +{ + u32 shr, shr_l, shr_m, shr_h; + int ret = 0; + + if (!imx678->is_tline_init) { + imx678_get_pclk_and_tline(imx678); + imx678->is_tline_init = true; + } + + ret = imx678_read_reg(imx678->client, IMX678_LF_EXPO_REG_L, + IMX678_REG_VALUE_08BIT, &shr_l); + ret |= imx678_read_reg(imx678->client, IMX678_LF_EXPO_REG_M, + IMX678_REG_VALUE_08BIT, &shr_m); + ret |= imx678_read_reg(imx678->client, IMX678_LF_EXPO_REG_H, + IMX678_REG_VALUE_08BIT, &shr_h); + if (!ret) { + shr = (shr_h << 16) | (shr_m << 8) | shr_l; + imx678->cur_exposure[0] = (imx678->cur_vts - shr) * imx678->tline; + } +} + +static int imx678_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx678 *imx678 = container_of(ctrl->handler, + struct imx678, ctrl_handler); + struct i2c_client *client = imx678->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 (imx678->cur_mode->hdr_mode == NO_HDR) { + /* Update max exposure while meeting expected vblanking */ + max = imx678->cur_mode->height + ctrl->val - 8; + __v4l2_ctrl_modify_range(imx678->exposure, + imx678->exposure->minimum, max, + imx678->exposure->step, + imx678->exposure->default_value); + } + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if (imx678->cur_mode->hdr_mode != NO_HDR) + goto ctrl_end; + shr0 = imx678->cur_vts - ctrl->val; + ret = imx678_write_reg(imx678->client, IMX678_LF_EXPO_REG_L, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_L(shr0)); + ret |= imx678_write_reg(imx678->client, IMX678_LF_EXPO_REG_M, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_M(shr0)); + ret |= imx678_write_reg(imx678->client, IMX678_LF_EXPO_REG_H, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_EXP_H(shr0)); + imx678_exposure_readback(imx678); + dev_dbg(&client->dev, "set exposure(shr0) %d = cur_vts(%d) - val(%d)\n", + shr0, imx678->cur_vts, ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + if (imx678->cur_mode->hdr_mode != NO_HDR) + goto ctrl_end; + ret = imx678_write_reg(imx678->client, IMX678_LF_GAIN_REG_H, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_GAIN_H(ctrl->val)); + ret |= imx678_write_reg(imx678->client, IMX678_LF_GAIN_REG_L, + IMX678_REG_VALUE_08BIT, + IMX678_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 + imx678->cur_mode->height; + /* + * vts of hdr mode is double to correct T-line calculation. + * Restore before write to reg. + */ + if (imx678->cur_mode->hdr_mode == HDR_X2) { + vts = (vts + 3) / 4 * 4; + imx678->cur_vts = vts; + vts /= 2; + } else if (imx678->cur_mode->hdr_mode == HDR_X3) { + vts = (vts + 11) / 12 * 12; + imx678->cur_vts = vts; + vts /= 4; + } else { + imx678->cur_vts = vts; + } + ret = imx678_write_reg(imx678->client, IMX678_VTS_REG_L, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_VTS_L(vts)); + ret |= imx678_write_reg(imx678->client, IMX678_VTS_REG_M, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_VTS_M(vts)); + ret |= imx678_write_reg(imx678->client, IMX678_VTS_REG_H, + IMX678_REG_VALUE_08BIT, + IMX678_FETCH_VTS_H(vts)); + dev_dbg(&client->dev, "set vblank 0x%x vts %d\n", + ctrl->val, vts); + break; + case V4L2_CID_HFLIP: + ret = imx678_read_reg(imx678->client, IMX678_MIRROR_REG, + IMX678_REG_VALUE_08BIT, &val); + if (ret) + break; + if (ctrl->val) + val |= IMX678_MIRROR_BIT_MASK; + else + val &= ~IMX678_MIRROR_BIT_MASK; + ret = imx678_write_reg(imx678->client, IMX678_MIRROR_REG, + IMX678_REG_VALUE_08BIT, val); + break; + case V4L2_CID_VFLIP: + ret = imx678_read_reg(imx678->client, IMX678_FLIP_REG, + IMX678_REG_VALUE_08BIT, &val); + if (ret) + break; + if (ctrl->val) + val |= IMX678_FLIP_BIT_MASK; + else + val &= ~IMX678_FLIP_BIT_MASK; + ret = imx678_write_reg(imx678->client, IMX678_FLIP_REG, + IMX678_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 imx678_ctrl_ops = { + .s_ctrl = imx678_set_ctrl, +}; + +static int imx678_initialize_controls(struct imx678 *imx678) +{ + const struct imx678_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 = imx678->bus_cfg.bus.mipi_csi2.num_data_lanes; + + handler = &imx678->ctrl_handler; + mode = imx678->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &imx678->mutex; + + imx678->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(imx678->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; + imx678->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; + imx678->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (imx678->hblank) + imx678->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + imx678->vblank = v4l2_ctrl_new_std(handler, &imx678_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + IMX678_VTS_MAX - mode->height, + 1, vblank_def); + imx678->cur_vts = mode->vts_def; + + exposure_max = mode->vts_def - 8; + imx678->exposure = v4l2_ctrl_new_std(handler, &imx678_ctrl_ops, + V4L2_CID_EXPOSURE, IMX678_EXPOSURE_MIN, + exposure_max, IMX678_EXPOSURE_STEP, + mode->exp_def); + imx678->anal_a_gain = v4l2_ctrl_new_std(handler, &imx678_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, IMX678_GAIN_MIN, + IMX678_GAIN_MAX, IMX678_GAIN_STEP, + IMX678_GAIN_DEFAULT); + v4l2_ctrl_new_std(handler, &imx678_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(handler, &imx678_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (handler->error) { + ret = handler->error; + dev_err(&imx678->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + imx678->subdev.ctrl_handler = handler; + imx678->has_init_exp = false; + imx678->is_tline_init = false; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int imx678_check_sensor_id(struct imx678 *imx678, + struct i2c_client *client) +{ + struct device *dev = &imx678->client->dev; + u32 id = 0; + int ret; + + if (imx678->is_thunderboot) { + dev_info(dev, "Enable thunderboot mode, skip sensor id check\n"); + return 0; + } + + ret = imx678_read_reg(client, IMX678_REG_CHIP_ID, + IMX678_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 imx678 id %06x\n", CHIP_ID); + + return 0; +} + +static int imx678_configure_regulators(struct imx678 *imx678) +{ + unsigned int i; + + for (i = 0; i < IMX678_NUM_SUPPLIES; i++) + imx678->supplies[i].supply = imx678_supply_names[i]; + + return devm_regulator_bulk_get(&imx678->client->dev, + IMX678_NUM_SUPPLIES, + imx678->supplies); +} + +static int imx678_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct imx678 *imx678; + struct v4l2_subdev *sd; + struct device_node *endpoint; + 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); + + imx678 = devm_kzalloc(dev, sizeof(*imx678), GFP_KERNEL); + if (!imx678) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &imx678->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &imx678->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &imx678->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &imx678->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"); + } + + 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), + &imx678->bus_cfg); + of_node_put(endpoint); + if (ret) { + dev_err(dev, "Failed to get bus config\n"); + return -EINVAL; + } + + imx678->client = client; + if (imx678->bus_cfg.bus.mipi_csi2.num_data_lanes == IMX678_4LANES) { + imx678->supported_modes = supported_modes; + imx678->cfg_num = ARRAY_SIZE(supported_modes); + } else { + imx678->supported_modes = supported_modes; + imx678->cfg_num = ARRAY_SIZE(supported_modes); + } + dev_info(dev, "detect imx678 lane %d\n", + imx678->bus_cfg.bus.mipi_csi2.num_data_lanes); + + for (i = 0; i < imx678->cfg_num; i++) { + if (hdr_mode == imx678->supported_modes[i].hdr_mode) { + imx678->cur_mode = &imx678->supported_modes[i]; + break; + } + } + + of_property_read_u32(node, RKMODULE_CAMERA_FASTBOOT_ENABLE, + &imx678->is_thunderboot); + + imx678->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(imx678->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + imx678->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS); + if (IS_ERR(imx678->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + imx678->power_gpio = devm_gpiod_get(dev, "power", GPIOD_ASIS); + if (IS_ERR(imx678->power_gpio)) + dev_warn(dev, "Failed to get power-gpios\n"); + imx678->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(imx678->pinctrl)) { + imx678->pins_default = + pinctrl_lookup_state(imx678->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(imx678->pins_default)) + dev_info(dev, "could not get default pinstate\n"); + + imx678->pins_sleep = + pinctrl_lookup_state(imx678->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(imx678->pins_sleep)) + dev_info(dev, "could not get sleep pinstate\n"); + } else { + dev_info(dev, "no pinctrl\n"); + } + + ret = imx678_configure_regulators(imx678); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&imx678->mutex); + + sd = &imx678->subdev; + v4l2_i2c_subdev_init(sd, client, &imx678_subdev_ops); + ret = imx678_initialize_controls(imx678); + if (ret) + goto err_destroy_mutex; + + ret = __imx678_power_on(imx678); + if (ret) + goto err_free_handler; + + ret = imx678_check_sensor_id(imx678, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &imx678_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + imx678->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &imx678->pad); + if (ret < 0) + goto err_power_off; +#endif + memset(facing, 0, sizeof(facing)); + if (strcmp(imx678->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + imx678->module_index, facing, + IMX678_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __imx678_power_off(imx678); +err_free_handler: + v4l2_ctrl_handler_free(&imx678->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&imx678->mutex); + + return ret; +} + +static void imx678_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx678 *imx678 = to_imx678(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&imx678->ctrl_handler); + mutex_destroy(&imx678->mutex); + + cam_sw_deinit(imx678->cam_sw_inf); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __imx678_power_off(imx678); + pm_runtime_set_suspended(&client->dev); +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id imx678_of_match[] = { + { .compatible = "sony,imx678" }, + {}, +}; +MODULE_DEVICE_TABLE(of, imx678_of_match); +#endif + +static const struct i2c_device_id imx678_match_id[] = { + { "sony,imx678", 0 }, + { }, +}; + +static struct i2c_driver imx678_i2c_driver = { + .driver = { + .name = IMX678_NAME, + .pm = &imx678_pm_ops, + .of_match_table = of_match_ptr(imx678_of_match), + }, + .probe = &imx678_probe, + .remove = &imx678_remove, + .id_table = imx678_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&imx678_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&imx678_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("Sony imx678 sensor driver"); +MODULE_LICENSE("GPL"); From 8a152b2e8c0e71eae8811a968bda440404be725f Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Wed, 7 May 2025 10:37:34 +0800 Subject: [PATCH 05/18] mtd: spi-nor: winbond: Remove all devices SNOR_F_NO_READ_CR flags All winbond particles support 35H command, but some particles do not accurately feedback information after SDFP param parsing. Change-Id: I00ad45ccd7ca7f7798a2c0699d064a58c6e411c6 Signed-off-by: Jon Lin --- drivers/mtd/spi-nor/winbond.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index 58aba52022bf..42b51d3efb46 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -226,9 +226,19 @@ static void winbond_nor_late_init(struct spi_nor *nor) nor->params->otp.ops = &winbond_nor_otp_ops; } +static void winbond_nor_post_sfdp(struct spi_nor *nor) +{ + /* + * All winbond flash support 35H command, but some flash do + * not accurately feedback information after SDFP param parsing. + */ + nor->flags &= ~SNOR_F_NO_READ_CR; +} + static const struct spi_nor_fixups winbond_nor_fixups = { .default_init = winbond_nor_default_init, .late_init = winbond_nor_late_init, + .post_sfdp = winbond_nor_post_sfdp, }; const struct spi_nor_manufacturer spi_nor_winbond = { From 3cab45ddc147b085d2fb762972d7e226540c3d23 Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Wed, 7 May 2025 10:45:17 +0800 Subject: [PATCH 06/18] mtd: spi-nor: Add SNOR_F_NO_READ_CR warning SPI Nor usually has a way to obtain the CR status and add a warning to notify developers to make corresponding optimizations to avoid forced SR writing, which may cause SR wear out. Change-Id: Id0e32b94c33f564539cbbac3bb2147ad8227c3dd Signed-off-by: Jon Lin --- drivers/mtd/spi-nor/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index d4ee36e448bb..69a1f6e87d05 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1647,8 +1647,10 @@ int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor) { int ret; - if (nor->flags & SNOR_F_NO_READ_CR) + if (nor->flags & SNOR_F_NO_READ_CR) { + dev_warn(nor->dev, "it is recommended to optimize the SNOR_F_NO_READ_CR!\n"); return spi_nor_write_16bit_cr_and_check(nor, SR2_QUAD_EN_BIT1); + } ret = spi_nor_read_cr(nor, nor->bouncebuf); if (ret) From 9e0231a401f1a1389bc56628b78e993c46a88673 Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Tue, 13 May 2025 11:54:46 +0800 Subject: [PATCH 07/18] mtd: spi-nor: lock the spinor for shutdown progress During the reset process, some threads may still be accessing the flash, which will affect the soft reset behavior of the subsequent startup process. For example, the MXIC device may not meet the tREADY2 timing, so it is necessary to avoid flash access during the reset process. Change-Id: Ibb0aa2791a83ec820d7352c9558d3a962a2bf66d Signed-off-by: Jon Lin --- drivers/mtd/spi-nor/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 69a1f6e87d05..7af67b11da02 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -3471,6 +3471,7 @@ static void spi_nor_shutdown(struct spi_mem *spimem) { struct spi_nor *nor = spi_mem_get_drvdata(spimem); + mutex_lock(&nor->lock); spi_nor_restore(nor); } From 722cda0d68fbd0b3c092067f5d3d1198624678da Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Tue, 13 May 2025 17:19:00 +0800 Subject: [PATCH 08/18] include: rk-camera-module: add cmd RKMODULE_GET_BAYER_MODE Change-Id: I42076ecfe827c32d94b3585fcbb08bc7f55befff Signed-off-by: Zefa Chen --- include/uapi/linux/rk-camera-module.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/uapi/linux/rk-camera-module.h b/include/uapi/linux/rk-camera-module.h index 10df5c5611a8..dd44f8afdfd8 100644 --- a/include/uapi/linux/rk-camera-module.h +++ b/include/uapi/linux/rk-camera-module.h @@ -219,6 +219,9 @@ #define RKMODULE_SET_EXP_MODE \ _IOW('V', BASE_VIDIOC_PRIVATE + 51, __u32) +#define RKMODULE_GET_BAYER_MODE \ + _IOR('V', BASE_VIDIOC_PRIVATE + 52, __u32) + struct rkmodule_i2cdev_info { __u8 slave_addr; } __attribute__ ((packed)); @@ -956,4 +959,9 @@ struct rkmodule_blc_group { __u32 blc[RKMODULE_MAX_BLC_GROUP]; }; +enum rkmodule_bayer_mode { + RKMODULE_NORMAL_BAYER, + RKMODULE_QUARD_BAYER, +}; + #endif /* _UAPI_RKMODULE_CAMERA_H */ From 6220b24adcafb65c4fe454cacce32135f180418d Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Thu, 24 Apr 2025 17:46:34 +0800 Subject: [PATCH 09/18] media: i2c: add os12d40 sensor driver Change-Id: I1952ff79c18d311aebeb74d56fa81041f6a21d66 Signed-off-by: Zefa Chen --- drivers/media/i2c/Kconfig | 11 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/os12d40.c | 2397 +++++++++++++++++++++++++++++++++++ 3 files changed, 2409 insertions(+) create mode 100644 drivers/media/i2c/os12d40.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 2c45a69fff70..68853cccf05d 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1164,6 +1164,17 @@ config VIDEO_OS08A20 This is a Video4Linux2 sensor driver for the OmniVision OS08A20 camera. +config VIDEO_OS12D40 + tristate "OmniVision OS12D40 sensor support" + depends on I2C && VIDEO_DEV + depends on MEDIA_CAMERA_SUPPORT + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the OmniVision + OS12D40 camera. + config VIDEO_OV02A10 tristate "OmniVision OV02A10 sensor support" depends on VIDEO_DEV && I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index a276ed814946..dac1a04a40b2 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -162,6 +162,7 @@ obj-$(CONFIG_VIDEO_OS04D10) += os04d10.o obj-$(CONFIG_VIDEO_OS05A20) += os05a20.o obj-$(CONFIG_VIDEO_OS05L10) += os05l10.o obj-$(CONFIG_VIDEO_OS08A20) += os08a20.o +obj-$(CONFIG_VIDEO_OS12D40) += os12d40.o obj-$(CONFIG_VIDEO_OTP_EEPROM) += otp_eeprom.o obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o obj-$(CONFIG_VIDEO_OV02B10) += ov02b10.o diff --git a/drivers/media/i2c/os12d40.c b/drivers/media/i2c/os12d40.c new file mode 100644 index 000000000000..bbd898ada12e --- /dev/null +++ b/drivers/media/i2c/os12d40.c @@ -0,0 +1,2397 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * os12d40 driver + * + * Copyright (C) 2021 Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X00 init version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* verify default register values */ +//#define CHECK_REG_VALUE + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x00) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define MIPI_FREQ 498000000U +#define OS12D40_PIXEL_RATE (MIPI_FREQ * 2LL * 4LL / 10) +#define OS12D40_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x561441 +#define OS12D40_REG_CHIP_ID 0x300a + +#define OS12D40_REG_CTRL_MODE 0x0100 +#define OS12D40_MODE_SW_STANDBY 0x00 +#define OS12D40_MODE_STREAMING 0x01 + +#define OS12D40_REG_EXPOSURE 0x3501 +#define OS12D40_EXPOSURE_MIN 8 //MAX VTS-16 +#define OS12D40_EXPOSURE_STEP 1 +#define OS12D40_VTS_MAX 0xffff + +#define OS12D40_REG_GAIN_H 0x3508 +#define OS12D40_REG_GAIN_L 0x3509 +#define OS12D40_REG_DGAIN_H 0x350A +#define OS12D40_REG_DGAIN_M 0x350B +#define OS12D40_REG_DGAIN_L 0x350C +#define OS12D40_GAIN_L_MASK 0xff +#define OS12D40_GAIN_H_MASK 0x3f +#define OS12D40_GAIN_H_SHIFT 8 +#define ANALOG_GAIN_MIN 0x80 +#define ANALOG_GAIN_MAX 0x3e000 +#define ANALOG_GAIN_STEP 1 +#define ANALOG_GAIN_DEFAULT 1024 + +#define OS12D40_REG_GROUP 0x3208 +#define OS12D40_REG_FLIP 0x3820 +#define OS12D40_REG_MIRROR 0x3821 +#define MIRROR_BIT_MASK BIT(2) +#define FLIP_BIT_MASK BIT(2) + +#define OS12D40_REG_TEST_PATTERN 0x5081 +#define OS12D40_TEST_PATTERN_ENABLE 0x01 +#define OS12D40_TEST_PATTERN_DISABLE 0x0 + +#define OS12D40_REG_VTS 0x380e + +#define REG_NULL 0xFFFF +#define DELAY_MS 0xEEEE /* Array delay token */ + +#define OS12D40_REG_VALUE_08BIT 1 +#define OS12D40_REG_VALUE_16BIT 2 +#define OS12D40_REG_VALUE_24BIT 3 + +#define OS12D40_LANES 4 +#define OS12D40_BITS_PER_SAMPLE 10 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define OS12D40_NAME "os12d40" +#define OS12D40_MEDIA_BUS_FMT MEDIA_BUS_FMT_SBGGR10_1X10 + +#define USE_4_CELL (1) + +struct os12d40_otp_info { + int flag; // bit[7]: info, bit[6]:wb + int module_id; + int lens_id; + int year; + int month; + int day; + int rg_ratio; + int bg_ratio; +}; + +static const char * const os12d40_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define OS12D40_NUM_SUPPLIES ARRAY_SIZE(os12d40_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +struct os12d40_mode { + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; + u8 hdr_mode; +}; + +struct os12d40 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *power_gpio; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[OS12D40_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 mutex mutex; + bool streaming; + bool power_on; + const struct os12d40_mode *cur_mode; + unsigned int lane_num; + unsigned int cfg_num; + unsigned int pixel_rate; + u32 module_index; + struct os12d40_otp_info *otp; + const char *module_facing; + const char *module_name; + const char *len_name; + struct rkmodule_awb_cfg awb_cfg; +}; + +#define to_os12d40(sd) container_of(sd, struct os12d40, subdev) + +struct os12d40_id_name { + int id; + char name[RKMODULE_NAME_LEN]; +}; + +static const struct regval os12d40_global_regs[] = { + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * Pclk 398.4Mhz + * linelength 1080(0x438)12us + * framelength 2776(0xad8) + * grabwindow_width 4512 + * grabwindow_height 2512 + * max_framerate 30fps + * mipi_datarate per lane 996Mbps + */ +static const struct regval os12d40_4512x2512_regs_4lane[] = { + // Sysclk 90Mhz, MIPI4_996Mbps/Lane, 30Fps. + //Line_length =1080, Frame_length =2776 + {0x0103, 0x01}, + {0x0301, 0x80}, + {0x0302, 0x01}, + {0x0304, 0x01}, + {0x0305, 0xf0}, + {0x0306, 0x04}, + {0x0307, 0x01}, + {0x0309, 0x00}, + {0x0320, 0x20}, + {0x0324, 0x01}, + {0x0325, 0xc2}, + {0x0326, 0xd3}, + {0x032b, 0x06}, + {0x0344, 0x01}, + {0x0345, 0xb8}, + {0x0346, 0xcb}, + {0x0350, 0x02}, + {0x0360, 0x09}, + {0x3002, 0x80}, + {0x300d, 0x11}, + {0x300e, 0x11}, + {0x3012, 0x41}, + {0x3016, 0xf0}, + {0x3017, 0xd0}, + {0x3018, 0xf0}, + {0x3019, 0xc2}, + {0x301a, 0xf0}, + {0x301b, 0x34}, + {0x301c, 0x91}, + {0x301d, 0x02}, + {0x301e, 0x98}, + {0x301f, 0x21}, + {0x3022, 0xf0}, + {0x3027, 0x2e}, + {0x302c, 0x01}, + {0x302d, 0x00}, + {0x302e, 0x00}, + {0x302f, 0x00}, + {0x3030, 0x03}, + {0x3044, 0xc2}, + {0x304b, 0x00}, + {0x30d4, 0x00}, + {0x3209, 0x00}, + {0x320a, 0x00}, + {0x320b, 0x00}, + {0x320c, 0x00}, + {0x320d, 0x01}, + {0x3216, 0x01}, + {0x3218, 0x80}, + {0x33c0, 0x00}, + {0x33c3, 0x00}, + {0x33c4, 0x00}, + {0x3400, 0x04}, + {0x3408, 0x05}, + {0x340c, 0x10}, + {0x340e, 0x30}, + {0x3421, 0x08}, + {0x3422, 0x00}, + {0x3423, 0x15}, + {0x3424, 0x40}, + {0x3425, 0x10}, + {0x3426, 0x20}, + {0x3500, 0x00}, + {0x3501, 0x04}, + {0x3502, 0xd0}, + {0x3504, 0x0c}, + {0x3508, 0x06}, + {0x3509, 0x00}, + {0x350a, 0x01}, + {0x350b, 0x00}, + {0x350c, 0x00}, + {0x350e, 0x00}, + {0x3510, 0x01}, + {0x3511, 0x00}, + {0x3512, 0x00}, + {0x3513, 0x01}, + {0x3514, 0x00}, + {0x3515, 0x00}, + {0x3516, 0x01}, + {0x3517, 0x00}, + {0x3518, 0x00}, + {0x352d, 0x00}, + {0x352e, 0x00}, + {0x352f, 0x00}, + {0x3541, 0x00}, + {0x3542, 0x40}, + {0x3548, 0x01}, + {0x3549, 0x00}, + {0x354a, 0x01}, + {0x354b, 0x00}, + {0x354c, 0x00}, + {0x354e, 0x00}, + {0x3550, 0x01}, + {0x3551, 0x00}, + {0x3552, 0x00}, + {0x3581, 0x00}, + {0x3582, 0x40}, + {0x3588, 0x01}, + {0x3589, 0x00}, + {0x358a, 0x01}, + {0x358b, 0x00}, + {0x358c, 0x00}, + {0x3590, 0x01}, + {0x3591, 0x00}, + {0x3592, 0x00}, + {0x3610, 0x80}, + {0x3615, 0x27}, + {0x3617, 0x5a}, + {0x3624, 0x88}, + {0x3628, 0x77}, + {0x3644, 0x20}, + {0x3652, 0x00}, + {0x3653, 0x00}, + {0x3663, 0x6b}, + {0x3660, 0x4f}, + {0x3661, 0xd0}, + {0x3662, 0x09}, + {0x3680, 0xc1}, + {0x3683, 0x80}, + {0x3684, 0x03}, + {0x3685, 0x52}, + {0x3687, 0xc2}, + {0x3689, 0x27}, + {0x368a, 0x38}, + {0x368b, 0x08}, + {0x368c, 0x06}, + {0x368e, 0x00}, + {0x3692, 0x00}, + {0x3693, 0x00}, + {0x3696, 0x26}, + {0x3697, 0x1f}, + {0x3698, 0x1d}, + {0x3699, 0x59}, + {0x369a, 0x02}, + {0x369b, 0x34}, + {0x3700, 0x2e}, + {0x3701, 0x06}, + {0x3702, 0x4f}, + {0x3703, 0x28}, + {0x3704, 0x07}, + {0x3705, 0x00}, + {0x3706, 0x2f}, + {0x3707, 0x08}, + {0x3708, 0x2d}, + {0x3709, 0x5d}, + {0x370a, 0x00}, + {0x370b, 0x69}, + {0x370c, 0x0c}, + {0x3711, 0x00}, + {0x3712, 0x01}, + {0x3713, 0x00}, + {0x3714, 0x69}, + {0x371a, 0x1c}, + {0x371b, 0xd0}, + {0x371c, 0x04}, + {0x371d, 0x24}, + {0x371e, 0x13}, + {0x371f, 0x0c}, + {0x3720, 0x08}, + {0x3721, 0x15}, + {0x3724, 0x08}, + {0x3725, 0x32}, + {0x3727, 0x22}, + {0x3728, 0x11}, + {0x3729, 0x00}, + {0x372a, 0x00}, + {0x372b, 0x00}, + {0x3752, 0x02}, + {0x3753, 0x03}, + {0x3754, 0xee}, + {0x3760, 0x04}, + {0x3761, 0x14}, + {0x3762, 0x04}, + {0x3765, 0x08}, + {0x3766, 0x0c}, + {0x3767, 0x00}, + {0x376a, 0x00}, + {0x376b, 0x00}, + {0x376d, 0x1b}, + {0x376f, 0x02}, + {0x37d9, 0x08}, + {0x37f6, 0x07}, + {0x37f7, 0x04}, + {0x37f8, 0x2d}, + {0x37f9, 0x02}, + {0x37fa, 0x02}, + {0x37fb, 0x02}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x40}, + {0x3804, 0x12}, + {0x3805, 0x1f}, + {0x3806, 0x0a}, + {0x3807, 0x1f}, + {0x3808, 0x11}, + {0x3809, 0xa0}, + {0x380a, 0x09}, + {0x380b, 0xd0}, + {0x380c, 0x04}, + {0x380d, 0x38}, + {0x380e, 0x0a}, + {0x380f, 0xd8}, + {0x3810, 0x00}, + {0x3811, 0x40}, + {0x3812, 0x00}, + {0x3813, 0x08}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x381a, 0x00}, + {0x381b, 0x01}, + {0x381e, 0x00}, + {0x381f, 0x02}, + {0x3820, 0x00}, + {0x3821, 0x04}, + {0x3822, 0x00}, + {0x3823, 0x04}, + {0x3824, 0x00}, + {0x3825, 0x00}, + {0x3826, 0x00}, + {0x3827, 0x00}, + {0x3828, 0xf7}, + {0x382a, 0x83}, + {0x382c, 0x00}, + {0x382d, 0x00}, + {0x3835, 0x00}, + {0x3836, 0x00}, + {0x3837, 0x10}, + {0x3839, 0x00}, + {0x383b, 0x00}, + {0x383c, 0x00}, + {0x383d, 0x10}, + {0x383e, 0x00}, + {0x383f, 0x33}, + {0x3842, 0x00}, + {0x3856, 0x00}, + {0x3857, 0x10}, + {0x3858, 0x00}, + {0x3859, 0x20}, + {0x3865, 0x00}, + {0x3867, 0x08}, + {0x3868, 0x00}, + {0x3904, 0x33}, + {0x3907, 0x33}, + {0x390a, 0x9a}, + {0x3914, 0x34}, + {0x3938, 0x4b}, + {0x3939, 0x0c}, + {0x393b, 0x4b}, + {0x393c, 0x0c}, + {0x393e, 0x40}, + {0x393f, 0x0c}, + {0x394a, 0x01}, + {0x394b, 0xa5}, + {0x3975, 0x05}, + {0x3979, 0x32}, + {0x397d, 0x69}, + {0x3981, 0x15}, + {0x3983, 0x33}, + {0x3985, 0x1a}, + {0x3986, 0x08}, + {0x398a, 0x09}, + {0x39cd, 0x00}, + {0x39ce, 0x24}, + {0x39cf, 0x40}, + {0x39d0, 0x0a}, + {0x39d1, 0x50}, + {0x39d2, 0x05}, + {0x39d3, 0x94}, + {0x39d4, 0x01}, + {0x39d5, 0x79}, + {0x3a12, 0x00}, + {0x3a13, 0x00}, + {0x3a14, 0x00}, + {0x3a15, 0x00}, + {0x3a16, 0x00}, + {0x3a18, 0x04}, + {0x3a1a, 0x05}, + {0x3a1c, 0x0a}, + {0x3a1e, 0x03}, + {0x3a1f, 0x34}, + {0x3a22, 0x12}, + {0x3a24, 0x00}, + {0x3a25, 0xfe}, + {0x3a26, 0x01}, + {0x3a27, 0x01}, + {0x3a2a, 0xa8}, + {0x3a2b, 0xa8}, + {0x3a36, 0x00}, + {0x3d84, 0x00}, + {0x3d85, 0x1b}, + {0x3d88, 0x00}, + {0x3d89, 0x00}, + {0x3d8a, 0x03}, + {0x3d8b, 0xff}, + {0x3d8c, 0xa3}, + {0x3d8d, 0xc4}, + {0x3da4, 0x04}, + {0x3daa, 0xa0}, + {0x3dab, 0x10}, + {0x3dac, 0xa1}, + {0x3dad, 0x8c}, + {0x3dae, 0xa1}, + {0x3daf, 0xb3}, + {0x3e00, 0x0e}, + {0x3e01, 0x0e}, + {0x3e02, 0x0e}, + {0x3e03, 0x0e}, + {0x3e04, 0x0e}, + {0x3e05, 0x0e}, + {0x3e06, 0x0e}, + {0x3e07, 0x0e}, + {0x3e09, 0x47}, + {0x3e0b, 0x25}, + {0x3e0d, 0x13}, + {0x3e0f, 0x09}, + {0x3e11, 0x07}, + {0x3e13, 0x06}, + {0x3e15, 0x05}, + {0x3e17, 0x04}, + {0x3e18, 0x38}, + {0x3e19, 0x38}, + {0x3e1a, 0x13}, + {0x3e1b, 0x30}, + {0x3e1c, 0x07}, + {0x3e1d, 0x06}, + {0x3e1e, 0x05}, + {0x3e1f, 0x04}, + {0x3e20, 0x0f}, + {0x3e21, 0x0f}, + {0x3e22, 0x0f}, + {0x3e23, 0x0f}, + {0x3e24, 0x0f}, + {0x3e25, 0x0f}, + {0x3e26, 0x0f}, + {0x3e27, 0x0f}, + {0x3e28, 0x07}, + {0x3e29, 0x07}, + {0x3e2a, 0x07}, + {0x3e2b, 0x02}, + {0x3e2c, 0x07}, + {0x3e2d, 0x07}, + {0x3e30, 0x07}, + {0x3e3a, 0x02}, + {0x3e3b, 0xdf}, + {0x3e3c, 0xff}, + {0x3e3d, 0x44}, + {0x3e3e, 0x00}, + {0x3e3f, 0x00}, + {0x3e40, 0xc1}, + {0x3e42, 0x54}, + {0x3e43, 0x54}, + {0x3e44, 0x54}, + {0x3e45, 0x54}, + {0x3f00, 0x10}, + {0x3f01, 0x26}, + {0x3f03, 0x40}, + {0x4002, 0xf3}, + {0x4009, 0x02}, + {0x400e, 0xc6}, + {0x400f, 0x00}, + {0x4010, 0x38}, + {0x4011, 0x01}, + {0x4012, 0x0d}, + {0x4015, 0x04}, + {0x4016, 0x23}, + {0x4017, 0x00}, + {0x4018, 0x07}, + {0x401a, 0x40}, + {0x401e, 0x00}, + {0x401f, 0xcc}, + {0x4020, 0x04}, + {0x4021, 0x00}, + {0x4022, 0x04}, + {0x4023, 0x00}, + {0x4024, 0x04}, + {0x4025, 0x00}, + {0x4026, 0x04}, + {0x4027, 0x00}, + {0x4028, 0x01}, + {0x4030, 0x00}, + {0x4031, 0x00}, + {0x4032, 0x00}, + {0x4033, 0x00}, + {0x4034, 0x00}, + {0x4035, 0x00}, + {0x4036, 0x00}, + {0x4037, 0x00}, + {0x4040, 0x00}, + {0x4041, 0x40}, + {0x4042, 0x00}, + {0x4043, 0x40}, + {0x4044, 0x00}, + {0x4045, 0x40}, + {0x4046, 0x00}, + {0x4047, 0x40}, + {0x4050, 0x00}, + {0x4051, 0x00}, + {0x4056, 0x25}, + {0x4102, 0xf3}, + {0x4109, 0x02}, + {0x410e, 0xc6}, + {0x410f, 0x00}, + {0x4110, 0x28}, + {0x4111, 0x01}, + {0x4112, 0x0d}, + {0x4115, 0x04}, + {0x4116, 0x1b}, + {0x4117, 0x00}, + {0x4118, 0x07}, + {0x411a, 0x40}, + {0x411e, 0x00}, + {0x411f, 0xcc}, + {0x4128, 0x01}, + {0x4156, 0x25}, + {0x4702, 0xf3}, + {0x4709, 0x02}, + {0x470e, 0xc6}, + {0x470f, 0x00}, + {0x4710, 0x28}, + {0x4711, 0x01}, + {0x4712, 0x0d}, + {0x4715, 0x04}, + {0x4716, 0x1b}, + {0x4717, 0x00}, + {0x4718, 0x07}, + {0x471a, 0x40}, + {0x471e, 0x00}, + {0x471f, 0xcc}, + {0x4728, 0x01}, + {0x4756, 0x25}, + {0x4301, 0x00}, + {0x4303, 0x00}, + {0x4305, 0x00}, + {0x4307, 0x00}, + {0x4308, 0x00}, + {0x430b, 0xff}, + {0x430d, 0x00}, + {0x430e, 0x00}, + {0x4503, 0x0f}, + {0x4504, 0x82}, + {0x4508, 0x00}, + {0x451d, 0x00}, + {0x451e, 0x00}, + {0x451f, 0x00}, + {0x4523, 0x00}, + {0x4526, 0x00}, + {0x4527, 0x00}, + {0x4530, 0x80}, + {0x4547, 0x06}, + {0x4640, 0x00}, + {0x4641, 0x30}, + {0x4643, 0x00}, + {0x4645, 0x13}, + {0x464a, 0x00}, + {0x464b, 0x30}, + {0x4680, 0x00}, + {0x4681, 0x24}, + {0x4683, 0x00}, + {0x4800, 0x64}, + {0x480b, 0x10}, + {0x480c, 0x80}, + {0x480e, 0x04}, + {0x480f, 0x32}, + {0x4826, 0x32}, + {0x4833, 0x18}, + {0x4837, 0x10}, + {0x484b, 0x27}, + {0x4850, 0x47}, + {0x4853, 0x04}, + {0x4860, 0x00}, + {0x4861, 0xec}, + {0x4862, 0x04}, + {0x4883, 0x00}, + {0x4885, 0x10}, + {0x4888, 0x10}, + {0x4889, 0x03}, + {0x4d00, 0x04}, + {0x4d01, 0x8f}, + {0x4d02, 0xb9}, + {0x4d03, 0xc1}, + {0x4d04, 0xb6}, + {0x4d05, 0x7e}, + {0x5000, 0xfb}, + {0x5001, 0xdb}, + {0x5002, 0x15}, + {0x5003, 0x01}, + {0x5007, 0x1e}, + {0x5008, 0x00}, + {0x5009, 0x00}, + {0x500a, 0x00}, + {0x500b, 0x30}, + {0x500c, 0x12}, + {0x500d, 0x1f}, + {0x500e, 0x0a}, + {0x500f, 0x0f}, + {0x504b, 0x40}, + {0x5081, 0x00}, + {0x50c4, 0xaa}, + {0x50d0, 0x00}, + {0x50d1, 0x10}, + {0x50d2, 0x01}, + {0x50d3, 0xb3}, + {0x515a, 0x06}, + {0x515b, 0x06}, + {0x515c, 0x02}, + {0x515d, 0x02}, + {0x515e, 0x02}, + {0x515f, 0x06}, + {0x5160, 0x0a}, + {0x5161, 0x0e}, + {0x5180, 0x09}, + {0x5181, 0x10}, + {0x5182, 0x05}, + {0x5183, 0x20}, + {0x5184, 0x00}, + {0x5185, 0x08}, + {0x5186, 0x00}, + {0x5187, 0x00}, + {0x5188, 0x09}, + {0x5189, 0x00}, + {0x518a, 0x05}, + {0x518b, 0x20}, + {0x518d, 0x09}, + {0x5192, 0x00}, + {0x5193, 0x30}, + {0x51d2, 0x10}, + {0x51da, 0x00}, + {0x51db, 0x30}, + {0x5250, 0xec}, + {0x5251, 0x00}, + {0x5252, 0x10}, + {0x5254, 0x00}, + {0x5255, 0x72}, + {0x5256, 0x00}, + {0x5257, 0xca}, + {0x5258, 0x12}, + {0x5259, 0x20}, + {0x525a, 0x0a}, + {0x525b, 0x40}, + {0x525e, 0x00}, + {0x525f, 0x30}, + {0x5260, 0x00}, + {0x5261, 0x10}, + {0x5262, 0x00}, + {0x5263, 0x10}, + {0x5264, 0x12}, + {0x5265, 0x00}, + {0x5266, 0x0a}, + {0x5267, 0x20}, + {0x5268, 0x00}, + {0x5269, 0x00}, + {0x526a, 0x00}, + {0x526b, 0x00}, + {0x526c, 0x12}, + {0x526d, 0x10}, + {0x526e, 0x0a}, + {0x526f, 0x30}, + {0x5278, 0x10}, + {0x5279, 0x20}, + {0x527a, 0x01}, + {0x527b, 0x01}, + {0x527c, 0x0c}, + {0x527d, 0x0c}, + {0x527e, 0x04}, + {0x527f, 0x04}, + {0x5280, 0x04}, + {0x5281, 0x0c}, + {0x5282, 0x14}, + {0x5283, 0x1c}, + {0x5381, 0x00}, + {0x53c4, 0xaa}, + {0x545a, 0x06}, + {0x545b, 0x06}, + {0x545c, 0x02}, + {0x545d, 0x02}, + {0x545e, 0x02}, + {0x545f, 0x06}, + {0x5460, 0x0a}, + {0x5461, 0x0e}, + {0x5480, 0x09}, + {0x5481, 0x10}, + {0x5482, 0x05}, + {0x5483, 0x20}, + {0x5484, 0x00}, + {0x5485, 0x08}, + {0x5486, 0x00}, + {0x5487, 0x00}, + {0x5488, 0x09}, + {0x5489, 0x00}, + {0x548a, 0x05}, + {0x548b, 0x20}, + {0x548d, 0x09}, + {0x54d2, 0x10}, + {0x54da, 0x00}, + {0x54da, 0x00}, + {0x54db, 0x30}, + {0x54db, 0x30}, + {0x5550, 0xec}, + {0x5551, 0x00}, + {0x5552, 0x10}, + {0x5554, 0x00}, + {0x5555, 0x72}, + {0x5556, 0x00}, + {0x5557, 0xca}, + {0x5558, 0x12}, + {0x5559, 0x10}, + {0x555a, 0x0a}, + {0x555b, 0x30}, + {0x555e, 0x00}, + {0x555f, 0x30}, + {0x5560, 0x00}, + {0x5561, 0x00}, + {0x5562, 0x00}, + {0x5563, 0x08}, + {0x5564, 0x12}, + {0x5565, 0x10}, + {0x5566, 0x0a}, + {0x5567, 0x30}, + {0x5568, 0x00}, + {0x5569, 0x00}, + {0x556a, 0x00}, + {0x556b, 0x00}, + {0x556c, 0x12}, + {0x556d, 0x10}, + {0x556e, 0x0a}, + {0x556f, 0x30}, + {0x557c, 0x06}, + {0x557d, 0x06}, + {0x557e, 0x02}, + {0x557f, 0x02}, + {0x5580, 0x02}, + {0x5581, 0x06}, + {0x5582, 0x0a}, + {0x5583, 0x0e}, + {0x5681, 0x00}, + {0x56c4, 0xaa}, + {0x575a, 0x06}, + {0x575b, 0x06}, + {0x575c, 0x02}, + {0x575d, 0x02}, + {0x575e, 0x02}, + {0x575f, 0x06}, + {0x5760, 0x0a}, + {0x5761, 0x0e}, + {0x5780, 0x09}, + {0x5781, 0x10}, + {0x5782, 0x05}, + {0x5783, 0x20}, + {0x5784, 0x00}, + {0x5785, 0x08}, + {0x5786, 0x00}, + {0x5787, 0x00}, + {0x5788, 0x09}, + {0x5789, 0x00}, + {0x578a, 0x05}, + {0x578b, 0x20}, + {0x578d, 0x09}, + {0x5792, 0x00}, + {0x5793, 0x30}, + {0x57d2, 0x10}, + {0x57da, 0x00}, + {0x57db, 0x30}, + {0x5850, 0xec}, + {0x5851, 0x00}, + {0x5852, 0x10}, + {0x5854, 0x00}, + {0x5855, 0x72}, + {0x5856, 0x00}, + {0x5857, 0xca}, + {0x5858, 0x12}, + {0x5859, 0x10}, + {0x585a, 0x0a}, + {0x585b, 0x30}, + {0x585e, 0x00}, + {0x585f, 0x30}, + {0x5860, 0x00}, + {0x5861, 0x00}, + {0x5862, 0x00}, + {0x5863, 0x08}, + {0x5864, 0x12}, + {0x5865, 0x10}, + {0x5866, 0x0a}, + {0x5867, 0x30}, + {0x5868, 0x00}, + {0x5869, 0x00}, + {0x586a, 0x00}, + {0x586b, 0x00}, + {0x586c, 0x12}, + {0x586d, 0x10}, + {0x586e, 0x0a}, + {0x586f, 0x30}, + {0x587c, 0x06}, + {0x587d, 0x06}, + {0x587e, 0x02}, + {0x587f, 0x02}, + {0x5880, 0x02}, + {0x5881, 0x06}, + {0x5882, 0x0a}, + {0x5883, 0x0e}, + {0x5f06, 0x10}, + {0x5f07, 0x20}, + {0x5f08, 0x0c}, + {0x5f09, 0x0c}, + {0x5f0a, 0x04}, + {0x5f0b, 0x04}, + {0x5f0c, 0x04}, + {0x5f0d, 0x0c}, + {0x5f0e, 0x14}, + {0x5f0f, 0x1c}, + {0x5f10, 0x01}, + {0x5f11, 0x01}, + {0x5f18, 0x12}, + {0x5f19, 0x20}, + {0x5f1a, 0x0a}, + {0x5f1b, 0x40}, + {0x5f1d, 0x10}, + {0x5f1f, 0x10}, + {0x5f20, 0x12}, + {0x5f21, 0x00}, + {0x5f22, 0x0a}, + {0x5f23, 0x20}, + {0x5f25, 0x09}, + {0x5f2a, 0x00}, + {0x5f2b, 0x30}, + {0x6600, 0x00}, + {0x6601, 0x00}, + {0x6602, 0x00}, + {0x6603, 0x83}, + {0x6960, 0x0f}, + {0x69a2, 0x09}, + {0x69a3, 0x00}, + {0x69a6, 0x05}, + {0x69a7, 0x10}, + {0x69aa, 0x09}, + {0x69ab, 0x00}, + {0x69ae, 0x05}, + {0x69af, 0x10}, + {0x69b2, 0x09}, + {0x69b3, 0x00}, + {0x69b6, 0x05}, + {0x69b7, 0x10}, + {0x69ba, 0x09}, + {0x69bb, 0x00}, + {0x69be, 0x05}, + {0x69bf, 0x10}, + {0x6a24, 0x09}, + {0x6a25, 0x00}, + {0x6a2a, 0x05}, + {0x6a2b, 0x10}, + {0x6a61, 0x40}, + {0x6a64, 0x09}, + {0x6a65, 0x00}, + {0x6a6a, 0x05}, + {0x6a6b, 0x10}, + {0x6a23, 0x00}, + {0x6a27, 0x00}, + {0x6a63, 0x00}, + {0x6a67, 0x00}, + {0x69a1, 0x00}, + {0x69a5, 0x00}, + {0x69a9, 0x00}, + {0x69ad, 0x00}, + {0x69b1, 0x00}, + {0x69b5, 0x00}, + {0x69b9, 0x00}, + {0x69bd, 0x00}, + {0xfff4, 0x01}, + {0xfff6, 0x00}, + {0x0361, 0x07}, + {0x3644, 0x20}, + {0x50d4, 0x00}, + {0x5a02, 0x0f}, + {0x5002, 0x55}, + {0x5331, 0x0a}, + {0x5332, 0x43}, + {0x5333, 0x45}, + {0x5284, 0x03}, + {0x5285, 0x02}, + {0x5286, 0x02}, + {0x5287, 0x03}, + {0x5f15, 0xeb}, + {0x5290, 0x00}, + {0x5291, 0x50}, + {0x5292, 0x00}, + {0x5293, 0x50}, + {0x5294, 0x00}, + {0x5295, 0x50}, + {0x5296, 0x00}, + {0x5297, 0x50}, + {0x5298, 0x00}, + {0x5299, 0x50}, + {0x529a, 0x01}, + {0x529b, 0x00}, + {0x529c, 0x01}, + {0x529d, 0x00}, + {0x529e, 0x00}, + {0x529f, 0x50}, + {0x52a0, 0x00}, + {0x52a1, 0x50}, + {0x52a2, 0x01}, + {0x52a3, 0x00}, + {0x52a4, 0x01}, + {0x52a5, 0x00}, + {0x52a6, 0x00}, + {0x52a7, 0x50}, + {0x52a8, 0x00}, + {0x52a9, 0x50}, + {0x52aa, 0x00}, + {0x52ab, 0x50}, + {0x52ac, 0x00}, + {0x52ad, 0x50}, + {0x52ae, 0x00}, + {0x52af, 0x50}, + {0x52b0, 0x00}, + {0x52b1, 0x50}, + {0x52b2, 0x00}, + {0x52b3, 0x50}, + {0x52b4, 0x00}, + {0x52b5, 0x50}, + {0x52b6, 0x00}, + {0x52b7, 0x50}, + {0x52b8, 0x00}, + {0x52b9, 0x50}, + {0x52ba, 0x01}, + {0x52bb, 0x00}, + {0x52bc, 0x01}, + {0x52bd, 0x00}, + {0x52be, 0x00}, + {0x52bf, 0x50}, + {0x52c0, 0x00}, + {0x52c1, 0x50}, + {0x52c2, 0x01}, + {0x52c3, 0x00}, + {0x52c4, 0x01}, + {0x52c5, 0x00}, + {0x52c6, 0x00}, + {0x52c7, 0x50}, + {0x52c8, 0x00}, + {0x52c9, 0x50}, + {0x52ca, 0x00}, + {0x52cb, 0x50}, + {0x52cc, 0x00}, + {0x52cd, 0x50}, + {0x52ce, 0x00}, + {0x52cf, 0x50}, + {0x5f00, 0x29}, + {0x5f2d, 0x28}, + {0x5f2e, 0x28}, + {0x52f0, 0x04}, + {0x52f1, 0x03}, + {0x52f2, 0x02}, + {0x52f3, 0x01}, + {0x52f4, 0x08}, + {0x52f5, 0x07}, + {0x52f6, 0x06}, + {0x52f7, 0x05}, + {0x52f8, 0x0c}, + {0x52f9, 0x0b}, + {0x52fa, 0x0a}, + {0x52fb, 0x09}, + {0x52fc, 0x10}, + {0x52fd, 0x0f}, + {0x52fe, 0x0e}, + {0x52ff, 0x0d}, + {0x5300, 0x14}, + {0x5301, 0x13}, + {0x5302, 0x12}, + {0x5303, 0x11}, + {0x5304, 0x18}, + {0x5305, 0x17}, + {0x5306, 0x16}, + {0x5307, 0x15}, + {0x5308, 0x1c}, + {0x5309, 0x1b}, + {0x530a, 0x1a}, + {0x530b, 0x19}, + {0x530c, 0x20}, + {0x530d, 0x1f}, + {0x530e, 0x1e}, + {0x530f, 0x1d}, + {0x5353, 0x21}, + {0x5354, 0x01}, + {0x5355, 0x02}, + {0x5356, 0x04}, + {0x5357, 0x06}, + {0x5358, 0x08}, + {0x5359, 0x0c}, + {0x535a, 0x10}, + {0x535b, 0x10}, + {0x5990, 0x00}, + {0x5991, 0x00}, + {0x5992, 0x01}, + {0x5993, 0x01}, + {0x5994, 0x02}, + {0x5995, 0x04}, + {0x5996, 0x06}, + {0x5997, 0x08}, + {0x5998, 0x0a}, + {0x5999, 0x0c}, + {0x599a, 0x0e}, + {0x599b, 0x10}, + {0x599c, 0x12}, + {0x599d, 0x14}, + {0x599e, 0x16}, + {0x599f, 0x18}, + {0x3222, 0x03}, + {0x3208, 0x06}, + {0x3938, 0x41}, + {0x393b, 0x41}, + {0x3208, 0x16}, + {0x3208, 0x07}, + {0x3938, 0x43}, + {0x393b, 0x44}, + {0x3208, 0x17}, + {0x3208, 0x08}, + {0x3938, 0x45}, + {0x393b, 0x46}, + {0x3208, 0x18}, + {0x3208, 0x09}, + {0x3938, 0x4b}, + {0x393b, 0x4b}, + {0x3208, 0x19}, + {0x5000, 0x29}, +#if USE_4_CELL + {0x5001, 0xc1}, +#else + {0x5001, 0xd1}, +#endif + {REG_NULL, 0x00}, +}; + + +static const struct os12d40_mode supported_modes_4lane[] = { + { + .width = 4512, + .height = 2512, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x08b0, + .hts_def = 0x438 * 8, + .vts_def = 0x0ad8, + .reg_list = os12d40_4512x2512_regs_4lane, + .hdr_mode = NO_HDR, + }, +}; + +static const struct os12d40_mode *supported_modes; + +static const s64 link_freq_menu_items[] = { + MIPI_FREQ +}; + +static const char * const os12d40_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4", + "Square_BW Color Bar Type 1", + "Square_BW Color Bar Type 2", + "Square_BW Color Bar Type 3", + "Square_BW Color Bar Type 4", + "Transparent Color Bar Type 1", + "Transparent Color Bar Type 2", + "Transparent Color Bar Type 3", + "Transparent Color Bar Type 4", + "Rolling Color Bar Type 1", + "Rolling Color Bar Type 2", + "Rolling Color Bar Type 3", + "Rolling Color Bar Type 4", +}; + +/* Write registers up to 4 at a time */ +static int os12d40_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + dev_dbg(&client->dev, "%s(%d) enter!\n", __func__, __LINE__); + dev_dbg(&client->dev, "write reg(0x%x val:0x%x)!\n", reg, val); + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) { + dev_err(&client->dev, + "write reg(0x%x val:0x%x)failed !\n", reg, val); + return -EIO; + } + return 0; +} + +static int os12d40_write_array(struct i2c_client *client, + const struct regval *regs) +{ + int i, delay_ms, ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + if (regs[i].addr == DELAY_MS) { + delay_ms = regs[i].val; + dev_info(&client->dev, "delay(%d) ms !\n", delay_ms); + usleep_range(1000 * delay_ms, 1000 * delay_ms + 100); + continue; + } + ret = os12d40_write_reg(client, regs[i].addr, + OS12D40_REG_VALUE_08BIT, regs[i].val); + if (ret) + dev_err(&client->dev, "%s failed !\n", __func__); + } + return ret; +} + +/* Read registers up to 4 at a time */ +static int os12d40_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; +} + +/* Check Register value */ +#ifdef CHECK_REG_VALUE +static int os12d40_reg_verify(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + u32 value; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + ret = os12d40_read_reg(client, regs[i].addr, + OS12D40_REG_VALUE_08BIT, &value); + if (value != regs[i].val) { + dev_info(&client->dev, "%s: 0x%04x is 0x%x instead of 0x%x\n", + __func__, regs[i].addr, value, regs[i].val); + } + } + return ret; +} +#endif + +static int os12d40_get_reso_dist(const struct os12d40_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct os12d40_mode * +os12d40_find_best_fit(struct os12d40 *os12d40, + 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 < os12d40->cfg_num; i++) { + dist = os12d40_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 os12d40_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct os12d40 *os12d40 = to_os12d40(sd); + const struct os12d40_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&os12d40->mutex); + + mode = os12d40_find_best_fit(os12d40, fmt); + fmt->format.code = OS12D40_MEDIA_BUS_FMT; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; +#else + mutex_unlock(&os12d40->mutex); + return -ENOTTY; +#endif + } else { + os12d40->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(os12d40->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(os12d40->vblank, vblank_def, + OS12D40_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&os12d40->mutex); + + return 0; +} + +static int os12d40_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct os12d40 *os12d40 = to_os12d40(sd); + const struct os12d40_mode *mode = os12d40->cur_mode; + + mutex_lock(&os12d40->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); +#else + mutex_unlock(&os12d40->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = OS12D40_MEDIA_BUS_FMT; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&os12d40->mutex); + + return 0; +} + +static int os12d40_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = OS12D40_MEDIA_BUS_FMT; + + return 0; +} + +static int os12d40_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct os12d40 *os12d40 = to_os12d40(sd); + + if (fse->index >= os12d40->cfg_num) + return -EINVAL; + + if (fse->code != OS12D40_MEDIA_BUS_FMT) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int os12d40_enable_test_pattern(struct os12d40 *os12d40, u32 pattern) +{ + u32 val; + + if (pattern) + val = ((pattern - 1) << 4) | OS12D40_TEST_PATTERN_ENABLE; + else + val = OS12D40_TEST_PATTERN_DISABLE; + + /* test pattern select*/ + return os12d40_write_reg(os12d40->client, OS12D40_REG_TEST_PATTERN, + OS12D40_REG_VALUE_08BIT, val); +} + +static int os12d40_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct os12d40 *os12d40 = to_os12d40(sd); + const struct os12d40_mode *mode = os12d40->cur_mode; + + fi->interval = mode->max_fps; + + return 0; +} + +static void os12d40_get_module_inf(struct os12d40 *os12d40, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strscpy(inf->base.sensor, OS12D40_NAME, sizeof(inf->base.sensor)); + strscpy(inf->base.module, os12d40->module_name, + sizeof(inf->base.module)); + strscpy(inf->base.lens, os12d40->len_name, sizeof(inf->base.lens)); +} + +static void os12d40_set_awb_cfg(struct os12d40 *os12d40, + struct rkmodule_awb_cfg *cfg) +{ + mutex_lock(&os12d40->mutex); + memcpy(&os12d40->awb_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&os12d40->mutex); +} + +static long os12d40_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct os12d40 *os12d40 = to_os12d40(sd); + struct rkmodule_hdr_cfg *hdr; + long ret = 0; + u32 stream = 0; + u32 *bayer_mode; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + os12d40_get_module_inf(os12d40, (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 = os12d40->cur_mode->hdr_mode; + break; + case RKMODULE_SET_HDR_CFG: + hdr = (struct rkmodule_hdr_cfg *)arg; + if (hdr->hdr_mode != 0) + ret = -1; + break; + case RKMODULE_AWB_CFG: + os12d40_set_awb_cfg(os12d40, (struct rkmodule_awb_cfg *)arg); + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = os12d40_write_reg(os12d40->client, OS12D40_REG_CTRL_MODE, + OS12D40_REG_VALUE_08BIT, OS12D40_MODE_STREAMING); + else + ret = os12d40_write_reg(os12d40->client, OS12D40_REG_CTRL_MODE, + OS12D40_REG_VALUE_08BIT, OS12D40_MODE_SW_STANDBY); + break; + case RKMODULE_GET_BAYER_MODE: + bayer_mode = (u32 *)arg; +#ifdef USE_4_CELL + *bayer_mode = RKMODULE_QUARD_BAYER; +#else + *bayer_mode = RKMODULE_NORMAL_BAYER; +#endif + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long os12d40_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_hdr_cfg *hdr; + long ret; + u32 stream = 0; + u32 bayer_mode = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = os12d40_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 = os12d40_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; + } + + if (copy_from_user(hdr, up, sizeof(*hdr))) { + kfree(hdr); + return -EFAULT; + } + + ret = os12d40_ioctl(sd, cmd, hdr); + kfree(hdr); + break; + case RKMODULE_AWB_CFG: + awb_cfg = kzalloc(sizeof(*awb_cfg), GFP_KERNEL); + if (!awb_cfg) { + ret = -ENOMEM; + return ret; + } + + if (copy_from_user(awb_cfg, up, sizeof(*awb_cfg))) { + kfree(awb_cfg); + return -EFAULT; + } + + ret = os12d40_ioctl(sd, cmd, awb_cfg); + kfree(awb_cfg); + break; + case RKMODULE_SET_QUICK_STREAM: + if (copy_from_user(&stream, up, sizeof(u32))) + return -EFAULT; + + ret = os12d40_ioctl(sd, cmd, &stream); + break; + case RKMODULE_GET_BAYER_MODE: + ret = os12d40_ioctl(sd, cmd, &bayer_mode); + if (!ret) { + ret = copy_to_user(up, &bayer_mode, sizeof(bayer_mode)); + if (ret) + return -EFAULT; + } + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __os12d40_start_stream(struct os12d40 *os12d40) +{ + int ret; + + ret = os12d40_write_array(os12d40->client, os12d40->cur_mode->reg_list); + if (ret) + return ret; + +#ifdef CHECK_REG_VALUE + usleep_range(10000, 20000); + /* verify default values to make sure everything has */ + /* been written correctly as expected */ + dev_info(&os12d40->client->dev, "%s:Check register value!\n", + __func__); + ret = os12d40_reg_verify(os12d40->client, os12d40_global_regs); + if (ret) + return ret; + + ret = os12d40_reg_verify(os12d40->client, os12d40->cur_mode->reg_list); + if (ret) + return ret; +#endif + + /* In case these controls are set before streaming */ + mutex_unlock(&os12d40->mutex); + ret = v4l2_ctrl_handler_setup(&os12d40->ctrl_handler); + mutex_lock(&os12d40->mutex); + if (ret) + return ret; + + ret = os12d40_write_reg(os12d40->client, OS12D40_REG_CTRL_MODE, + OS12D40_REG_VALUE_08BIT, OS12D40_MODE_STREAMING); + return ret; +} + +static int __os12d40_stop_stream(struct os12d40 *os12d40) +{ + return os12d40_write_reg(os12d40->client, OS12D40_REG_CTRL_MODE, + OS12D40_REG_VALUE_08BIT, OS12D40_MODE_SW_STANDBY); +} + +static int os12d40_s_stream(struct v4l2_subdev *sd, int on) +{ + struct os12d40 *os12d40 = to_os12d40(sd); + struct i2c_client *client = os12d40->client; + int ret = 0; + + dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on, + os12d40->cur_mode->width, + os12d40->cur_mode->height, + DIV_ROUND_CLOSEST(os12d40->cur_mode->max_fps.denominator, + os12d40->cur_mode->max_fps.numerator)); + + mutex_lock(&os12d40->mutex); + on = !!on; + if (on == os12d40->streaming) + goto unlock_and_return; + + if (on) { + dev_info(&client->dev, "stream on!!!\n"); + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __os12d40_start_stream(os12d40); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + dev_info(&client->dev, "stream off!!!\n"); + __os12d40_stop_stream(os12d40); + pm_runtime_put(&client->dev); + } + + os12d40->streaming = on; + +unlock_and_return: + mutex_unlock(&os12d40->mutex); + + return ret; +} + +static int os12d40_s_power(struct v4l2_subdev *sd, int on) +{ + struct os12d40 *os12d40 = to_os12d40(sd); + struct i2c_client *client = os12d40->client; + int ret = 0; + + dev_dbg(&client->dev, "%s(%d) on(%d)\n", __func__, __LINE__, on); + + mutex_lock(&os12d40->mutex); + + /* If the power state is not modified - no work to do. */ + if (os12d40->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 = os12d40_write_array(os12d40->client, os12d40_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + os12d40->power_on = true; + /* export gpio */ + if (!IS_ERR(os12d40->reset_gpio)) + gpiod_export(os12d40->reset_gpio, false); + if (!IS_ERR(os12d40->pwdn_gpio)) + gpiod_export(os12d40->pwdn_gpio, false); + } else { + pm_runtime_put(&client->dev); + os12d40->power_on = false; + } + +unlock_and_return: + mutex_unlock(&os12d40->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 os12d40_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, OS12D40_XVCLK_FREQ / 1000 / 1000); +} + +static int __os12d40_power_on(struct os12d40 *os12d40) +{ + int ret; + u32 delay_us; + struct device *dev = &os12d40->client->dev; + + if (!IS_ERR(os12d40->power_gpio)) + gpiod_set_value_cansleep(os12d40->power_gpio, 1); + + usleep_range(1000, 2000); + + if (!IS_ERR_OR_NULL(os12d40->pins_default)) { + ret = pinctrl_select_state(os12d40->pinctrl, + os12d40->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(os12d40->xvclk, OS12D40_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(os12d40->xvclk) != OS12D40_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(os12d40->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + ret = regulator_bulk_enable(OS12D40_NUM_SUPPLIES, os12d40->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(os12d40->reset_gpio)) + gpiod_set_value_cansleep(os12d40->reset_gpio, 1); + + if (!IS_ERR(os12d40->pwdn_gpio)) + gpiod_set_value_cansleep(os12d40->pwdn_gpio, 1); + + /* export gpio */ + if (!IS_ERR(os12d40->reset_gpio)) + gpiod_export(os12d40->reset_gpio, false); + if (!IS_ERR(os12d40->pwdn_gpio)) + gpiod_export(os12d40->pwdn_gpio, false); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = os12d40_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + usleep_range(10000, 20000); + return 0; + +disable_clk: + clk_disable_unprepare(os12d40->xvclk); + + return ret; +} + +static void __os12d40_power_off(struct os12d40 *os12d40) +{ + int ret; + struct device *dev = &os12d40->client->dev; + + if (!IS_ERR(os12d40->pwdn_gpio)) + gpiod_set_value_cansleep(os12d40->pwdn_gpio, 0); + clk_disable_unprepare(os12d40->xvclk); + if (!IS_ERR(os12d40->reset_gpio)) + gpiod_set_value_cansleep(os12d40->reset_gpio, 0); + if (!IS_ERR_OR_NULL(os12d40->pins_sleep)) { + ret = pinctrl_select_state(os12d40->pinctrl, + os12d40->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + if (!IS_ERR(os12d40->power_gpio)) + gpiod_set_value_cansleep(os12d40->power_gpio, 0); + + regulator_bulk_disable(OS12D40_NUM_SUPPLIES, os12d40->supplies); +} + +static int os12d40_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct os12d40 *os12d40 = to_os12d40(sd); + + return __os12d40_power_on(os12d40); +} + +static int os12d40_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct os12d40 *os12d40 = to_os12d40(sd); + + __os12d40_power_off(os12d40); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int os12d40_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct os12d40 *os12d40 = to_os12d40(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->state, 0); + const struct os12d40_mode *def_mode = &supported_modes[0]; + + mutex_lock(&os12d40->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = OS12D40_MEDIA_BUS_FMT; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&os12d40->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int os12d40_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct os12d40 *os12d40 = to_os12d40(sd); + + if (fie->index >= os12d40->cfg_num) + return -EINVAL; + + fie->code = OS12D40_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 os12d40_g_mbus_config(struct v4l2_subdev *sd, + unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + config->type = V4L2_MBUS_CSI2_DPHY; + config->bus.mipi_csi2.num_data_lanes = OS12D40_LANES; + + return 0; +} + +#define CROP_START(SRC, DST) (((SRC) - (DST)) / 2 / 4 * 4) +#define DST_WIDTH 4096 +#define DST_HEIGHT 2512 +static int os12d40_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct os12d40 *os12d40 = to_os12d40(sd); + + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + sel->r.left = CROP_START(os12d40->cur_mode->width, DST_WIDTH); + sel->r.width = DST_WIDTH; + sel->r.top = CROP_START(os12d40->cur_mode->height, DST_HEIGHT); + sel->r.height = DST_HEIGHT; + return 0; + } + return -EINVAL; +} + +static const struct dev_pm_ops os12d40_pm_ops = { + SET_RUNTIME_PM_OPS(os12d40_runtime_suspend, + os12d40_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops os12d40_internal_ops = { + .open = os12d40_open, +}; +#endif + +static const struct v4l2_subdev_core_ops os12d40_core_ops = { + .s_power = os12d40_s_power, + .ioctl = os12d40_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = os12d40_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops os12d40_video_ops = { + .s_stream = os12d40_s_stream, + .g_frame_interval = os12d40_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops os12d40_pad_ops = { + .enum_mbus_code = os12d40_enum_mbus_code, + .enum_frame_size = os12d40_enum_frame_sizes, + .enum_frame_interval = os12d40_enum_frame_interval, + .get_fmt = os12d40_get_fmt, + .set_fmt = os12d40_set_fmt, + .get_mbus_config = os12d40_g_mbus_config, + .get_selection = os12d40_get_selection, +}; + +static const struct v4l2_subdev_ops os12d40_subdev_ops = { + .core = &os12d40_core_ops, + .video = &os12d40_video_ops, + .pad = &os12d40_pad_ops, +}; + +static int os12d40_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct os12d40 *os12d40 = container_of(ctrl->handler, + struct os12d40, ctrl_handler); + struct i2c_client *client = os12d40->client; + s64 max; + u32 val = 0; + int ret = 0; + u32 again = 0; + u32 dgain = 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 = os12d40->cur_mode->height + ctrl->val - 16; + __v4l2_ctrl_modify_range(os12d40->exposure, + os12d40->exposure->minimum, max, + os12d40->exposure->step, + os12d40->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = os12d40_write_reg(os12d40->client, OS12D40_REG_EXPOSURE, + OS12D40_REG_VALUE_16BIT, ctrl->val); + dev_dbg(&client->dev, "exposure %d\n", ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + /* + * again max 15.5x, step 128 + * dgain max 16x, step 1024 + * [1 15.5 128 0 1 128 1984], + * [15.5 248 1024 0 1 15872 253952] + */ + if (ctrl->val <= 1984) { + again = ctrl->val; + dgain = 1024; + } else { + again = 1984; + dgain = ctrl->val * 128 / 1984; + if (dgain >= 16383) + dgain = 16383; + } + ret = os12d40_write_reg(os12d40->client, OS12D40_REG_GAIN_H, + OS12D40_REG_VALUE_16BIT, + (again << 1)); + ret |= os12d40_write_reg(os12d40->client, OS12D40_REG_DGAIN_H, + OS12D40_REG_VALUE_24BIT, + (dgain << 6)); + dev_dbg(&client->dev, "total gain %d, again %d, dgain %d\n", + ctrl->val, again, dgain); + break; + case V4L2_CID_VBLANK: + ret = os12d40_write_reg(os12d40->client, OS12D40_REG_VTS, + OS12D40_REG_VALUE_16BIT, + ctrl->val + os12d40->cur_mode->height); + break; + case V4L2_CID_TEST_PATTERN: + ret = os12d40_enable_test_pattern(os12d40, ctrl->val); + break; + case V4L2_CID_HFLIP: + ret = os12d40_read_reg(os12d40->client, OS12D40_REG_MIRROR, + OS12D40_REG_VALUE_08BIT, + &val); + if (ctrl->val) + val |= MIRROR_BIT_MASK; + else + val &= ~MIRROR_BIT_MASK; + ret |= os12d40_write_reg(os12d40->client, OS12D40_REG_MIRROR, + OS12D40_REG_VALUE_08BIT, + val); + break; + case V4L2_CID_VFLIP: + ret = os12d40_read_reg(os12d40->client, OS12D40_REG_FLIP, + OS12D40_REG_VALUE_08BIT, + &val); + if (ctrl->val) + val |= FLIP_BIT_MASK; + else + val &= ~FLIP_BIT_MASK; + ret |= os12d40_write_reg(os12d40->client, OS12D40_REG_FLIP, + OS12D40_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 os12d40_ctrl_ops = { + .s_ctrl = os12d40_set_ctrl, +}; + +static int os12d40_initialize_controls(struct os12d40 *os12d40) +{ + const struct os12d40_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &os12d40->ctrl_handler; + mode = os12d40->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 9); + if (ret) + return ret; + handler->lock = &os12d40->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, os12d40->pixel_rate, 1, os12d40->pixel_rate); + + h_blank = mode->hts_def - mode->width; + os12d40->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (os12d40->hblank) + os12d40->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + os12d40->vblank = v4l2_ctrl_new_std(handler, &os12d40_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + OS12D40_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 16; + os12d40->exposure = v4l2_ctrl_new_std(handler, &os12d40_ctrl_ops, + V4L2_CID_EXPOSURE, OS12D40_EXPOSURE_MIN, + exposure_max, OS12D40_EXPOSURE_STEP, + mode->exp_def); + + os12d40->anal_gain = v4l2_ctrl_new_std(handler, &os12d40_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, ANALOG_GAIN_MIN, + ANALOG_GAIN_MAX, ANALOG_GAIN_STEP, + ANALOG_GAIN_DEFAULT); + + os12d40->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &os12d40_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(os12d40_test_pattern_menu) - 1, + 0, 0, os12d40_test_pattern_menu); + + v4l2_ctrl_new_std(handler, &os12d40_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + v4l2_ctrl_new_std(handler, &os12d40_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (handler->error) { + ret = handler->error; + dev_err(&os12d40->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + os12d40->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int os12d40_check_sensor_id(struct os12d40 *os12d40, + struct i2c_client *client) +{ + struct device *dev = &os12d40->client->dev; + u32 id = 0; + int ret; + + ret = os12d40_read_reg(client, OS12D40_REG_CHIP_ID, + OS12D40_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 os12d40_configure_regulators(struct os12d40 *os12d40) +{ + unsigned int i; + + for (i = 0; i < OS12D40_NUM_SUPPLIES; i++) + os12d40->supplies[i].supply = os12d40_supply_names[i]; + + return devm_regulator_bulk_get(&os12d40->client->dev, + OS12D40_NUM_SUPPLIES, + os12d40->supplies); +} + +static int os12d40_parse_of(struct os12d40 *os12d40) +{ + struct device *dev = &os12d40->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; + } + + os12d40->lane_num = rval; + if (os12d40->lane_num == 4) { + os12d40->cur_mode = &supported_modes_4lane[0]; + supported_modes = supported_modes_4lane; + os12d40->cfg_num = ARRAY_SIZE(supported_modes_4lane); + + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + os12d40->pixel_rate = MIPI_FREQ * 2U * os12d40->lane_num / 8U; + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + os12d40->lane_num, os12d40->pixel_rate); + } else { + dev_err(dev, "unsupported lane_num(%d)\n", os12d40->lane_num); + return -1; + } + + return 0; +} + +static int os12d40_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct os12d40 *os12d40; + struct v4l2_subdev *sd; + char facing[2] = "b"; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + os12d40 = devm_kzalloc(dev, sizeof(*os12d40), GFP_KERNEL); + if (!os12d40) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &os12d40->module_index); + if (ret) { + dev_warn(dev, "could not get module index!\n"); + os12d40->module_index = 0; + } + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &os12d40->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &os12d40->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &os12d40->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + os12d40->client = client; + + os12d40->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(os12d40->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + os12d40->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(os12d40->power_gpio)) + dev_warn(dev, "Failed to get power-gpios, maybe no use\n"); + + os12d40->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(os12d40->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios, maybe no use\n"); + + os12d40->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(os12d40->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = os12d40_configure_regulators(os12d40); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + ret = os12d40_parse_of(os12d40); + if (ret != 0) + return -EINVAL; + + os12d40->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(os12d40->pinctrl)) { + os12d40->pins_default = + pinctrl_lookup_state(os12d40->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(os12d40->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + os12d40->pins_sleep = + pinctrl_lookup_state(os12d40->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(os12d40->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } + + mutex_init(&os12d40->mutex); + + sd = &os12d40->subdev; + v4l2_i2c_subdev_init(sd, client, &os12d40_subdev_ops); + ret = os12d40_initialize_controls(os12d40); + if (ret) + goto err_destroy_mutex; + + ret = __os12d40_power_on(os12d40); + if (ret) + goto err_free_handler; + + ret = os12d40_check_sensor_id(os12d40, client); + if (ret < 0) { + dev_err(&client->dev, "%s(%d) Check id failed,\n" + "check following information:\n" + "Power/PowerDown/Reset/Mclk/I2cBus !!\n", + __func__, __LINE__); + goto err_power_off; + } + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &os12d40_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + os12d40->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &os12d40->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(os12d40->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + os12d40->module_index, facing, + OS12D40_NAME, dev_name(sd->dev)); + + ret = v4l2_async_register_subdev_sensor(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __os12d40_power_off(os12d40); +err_free_handler: + v4l2_ctrl_handler_free(&os12d40->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&os12d40->mutex); + + return ret; +} + +static void os12d40_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct os12d40 *os12d40 = to_os12d40(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&os12d40->ctrl_handler); + mutex_destroy(&os12d40->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __os12d40_power_off(os12d40); + pm_runtime_set_suspended(&client->dev); +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id os12d40_of_match[] = { + { .compatible = "ovti,os12d40" }, + {}, +}; +MODULE_DEVICE_TABLE(of, os12d40_of_match); +#endif + +static const struct i2c_device_id os12d40_match_id[] = { + { "ovti,os12d40", 0 }, + { }, +}; + +static struct i2c_driver os12d40_i2c_driver = { + .driver = { + .name = OS12D40_NAME, + .pm = &os12d40_pm_ops, + .of_match_table = of_match_ptr(os12d40_of_match), + }, + .probe = &os12d40_probe, + .remove = &os12d40_remove, + .id_table = os12d40_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&os12d40_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&os12d40_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("OmniVision os12d40 sensor driver"); +MODULE_LICENSE("GPL"); From d37ee119fe1915a0a3990275b3f8fa81a7f298a4 Mon Sep 17 00:00:00 2001 From: Liang Chen Date: Fri, 9 May 2025 11:45:55 +0800 Subject: [PATCH 10/18] soc: rockchip: system_monitor: add function to remove/restore cpu limit Signed-off-by: Liang Chen Change-Id: I60c7b3cb1ab68c06852871cf9fa435031915c328 --- .../soc/rockchip/rockchip_system_monitor.c | 38 +++++++++++++++++++ .../soc/rockchip/rockchip_system_monitor.h | 10 +++++ 2 files changed, 48 insertions(+) diff --git a/drivers/soc/rockchip/rockchip_system_monitor.c b/drivers/soc/rockchip/rockchip_system_monitor.c index 2639f5eeb61a..aed56d9ca059 100644 --- a/drivers/soc/rockchip/rockchip_system_monitor.c +++ b/drivers/soc/rockchip/rockchip_system_monitor.c @@ -1131,6 +1131,44 @@ int rockchip_monitor_suspend_low_temp_adjust(int cpu) } EXPORT_SYMBOL(rockchip_monitor_suspend_low_temp_adjust); +void rockchip_monitor_remove_cpu_limit(int cpu) +{ + struct monitor_dev_info *info; + + down_read(&mdev_list_sem); + list_for_each_entry(info, &monitor_dev_list, node) { + if (info->devp->type != MONITOR_TYPE_CPU) + continue; + if (cpumask_test_cpu(cpu, &info->devp->allowed_cpus)) { + if (info->status_max_limit) + freq_qos_update_request(&info->max_sta_freq_req, + FREQ_QOS_MAX_DEFAULT_VALUE); + break; + } + } + up_read(&mdev_list_sem); +} +EXPORT_SYMBOL(rockchip_monitor_remove_cpu_limit); + +void rockchip_monitor_restore_cpu_limit(int cpu) +{ + struct monitor_dev_info *info; + + down_read(&mdev_list_sem); + list_for_each_entry(info, &monitor_dev_list, node) { + if (info->devp->type != MONITOR_TYPE_CPU) + continue; + if (cpumask_test_cpu(cpu, &info->devp->allowed_cpus)) { + if (info->status_max_limit) + freq_qos_update_request(&info->max_sta_freq_req, + info->status_max_limit); + break; + } + } + up_read(&mdev_list_sem); +} +EXPORT_SYMBOL(rockchip_monitor_restore_cpu_limit); + static int rockchip_system_monitor_wide_temp_adjust(struct monitor_dev_info *info, int temp) diff --git a/include/soc/rockchip/rockchip_system_monitor.h b/include/soc/rockchip/rockchip_system_monitor.h index 83b61ba3ac70..a7204c02dec5 100644 --- a/include/soc/rockchip/rockchip_system_monitor.h +++ b/include/soc/rockchip/rockchip_system_monitor.h @@ -151,6 +151,8 @@ int rockchip_monitor_dev_low_temp_adjust(struct monitor_dev_info *info, int rockchip_monitor_dev_high_temp_adjust(struct monitor_dev_info *info, bool is_high); int rockchip_monitor_suspend_low_temp_adjust(int cpu); +void rockchip_monitor_remove_cpu_limit(int cpu); +void rockchip_monitor_restore_cpu_limit(int cpu); int rockchip_system_monitor_register_notifier(struct notifier_block *nb); void rockchip_system_monitor_unregister_notifier(struct notifier_block *nb); #else @@ -203,6 +205,14 @@ static inline int rockchip_monitor_suspend_low_temp_adjust(int cpu) return 0; }; +static inline void rockchip_monitor_remove_cpu_limit(int cpu) +{ +}; + +static inline void rockchip_monitor_restore_cpu_limit(int cpu) +{ +}; + static inline int rockchip_system_monitor_register_notifier(struct notifier_block *nb) { From 1a06d0b8a62f3baba17435906c7517c772e7ff3d Mon Sep 17 00:00:00 2001 From: Liang Chen Date: Fri, 16 Dec 2022 10:53:04 +0800 Subject: [PATCH 11/18] cpufreq: interactive: remove cpu limit of system monitor when touchboost Signed-off-by: Liang Chen Change-Id: I53be2f5a8093b5fbfc0be7744e1b5b40f22530f1 --- drivers/cpufreq/cpufreq_interactive.c | 44 +++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 81e6ae68f083..ce5d483483b8 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -37,6 +37,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include @@ -100,6 +101,7 @@ struct interactive_tunables { int touchboostpulse_duration_val; /* End time of touchboost pulse in ktime converted to usecs */ u64 touchboostpulse_endtime; + bool touchboost, is_touchboosted; #endif bool boosted; @@ -599,7 +601,44 @@ again: for_each_cpu(cpu, &tmp_mask) { struct interactive_cpu *icpu = &per_cpu(interactive_cpu, cpu); struct cpufreq_policy *policy; +#ifdef CONFIG_ARCH_ROCKCHIP + struct interactive_tunables *tunables; + bool update_policy = false; + u64 now; + now = ktime_to_us(ktime_get()); + if (!down_read_trylock(&icpu->enable_sem)) + continue; + + if (!icpu->ipolicy) { + up_read(&icpu->enable_sem); + continue; + } + + tunables = icpu->ipolicy->tunables; + if (!tunables) { + up_read(&icpu->enable_sem); + continue; + } + + if (tunables->touchboost && + now > tunables->touchboostpulse_endtime) { + tunables->touchboost = false; + rockchip_monitor_restore_cpu_limit(cpu); + update_policy = true; + } + + if (!tunables->is_touchboosted && tunables->touchboost) { + rockchip_monitor_remove_cpu_limit(cpu); + update_policy = true; + } + + tunables->is_touchboosted = tunables->touchboost; + + up_read(&icpu->enable_sem); + if (update_policy) + cpufreq_update_policy(cpu); +#endif policy = cpufreq_cpu_get(cpu); if (!policy) continue; @@ -1242,6 +1281,7 @@ static void cpufreq_interactive_input_event(struct input_handle *handle, cpumask_set_cpu(i, &speedchange_cpumask); pcpu->loc_hispeed_val_time = ktime_to_us(ktime_get()); + tunables->touchboost = true; anyboost = 1; } @@ -1475,6 +1515,10 @@ static void cpufreq_interactive_exit(struct cpufreq_policy *policy) idle_notifier_unregister(&cpufreq_interactive_idle_nb); #ifdef CONFIG_ARCH_ROCKCHIP input_unregister_handler(&cpufreq_interactive_input_handler); + if (tunables->touchboost) { + tunables->touchboost = false; + rockchip_monitor_restore_cpu_limit(policy->cpu); + } #endif } From 7b3016376133af997330118cf4923507ab3745b8 Mon Sep 17 00:00:00 2001 From: Liang Chen Date: Thu, 15 Dec 2022 16:03:03 +0800 Subject: [PATCH 12/18] cpufreq: interactive: add attribute for touchboost_freq/duration ls -l /sys/devices/system/cpu/cpu0/cpufreq/interactive/ ... -rw-r--r-- 1 root root 4096 2022-12-05 18:27 touchboost_duration -rw-r--r-- 1 root root 4096 2022-12-05 18:28 touchboost_freq Signed-off-by: Liang Chen Change-Id: I964a5553dcf7035d56357ee1cd4db074f60a1184 --- drivers/cpufreq/cpufreq_interactive.c | 43 +++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index ce5d483483b8..3dba4e99df71 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -1050,6 +1050,44 @@ static ssize_t store_io_is_busy(struct gov_attr_set *attr_set, const char *buf, return count; } +static ssize_t store_touchboost_freq(struct gov_attr_set *attr_set, + const char *buf, size_t count) +{ + struct interactive_tunables *tunables = to_tunables(attr_set); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + + tunables->touchboost_freq = val; + + return count; +} + +static ssize_t show_touchboost_duration(struct gov_attr_set *attr_set, char *buf) +{ + struct interactive_tunables *tunables = to_tunables(attr_set); + + return sprintf(buf, "%d\n", tunables->touchboostpulse_duration_val); +} + +static ssize_t store_touchboost_duration(struct gov_attr_set *attr_set, + const char *buf, size_t count) +{ + struct interactive_tunables *tunables = to_tunables(attr_set); + int val, ret; + + ret = kstrtoint(buf, 0, &val); + if (ret < 0) + return ret; + + tunables->touchboostpulse_duration_val = val; + + return count; +} + show_one(hispeed_freq, "%u"); show_one(go_hispeed_load, "%lu"); show_one(min_sample_time, "%lu"); @@ -1057,6 +1095,7 @@ show_one(timer_slack, "%lu"); show_one(boost, "%u"); show_one(boostpulse_duration, "%u"); show_one(io_is_busy, "%u"); +show_one(touchboost_freq, "%lu"); gov_attr_rw(target_loads); gov_attr_rw(above_hispeed_delay); @@ -1069,6 +1108,8 @@ gov_attr_rw(boost); gov_attr_wo(boostpulse); gov_attr_rw(boostpulse_duration); gov_attr_rw(io_is_busy); +gov_attr_rw(touchboost_freq); +gov_attr_rw(touchboost_duration); static struct attribute *interactive_attrs[] = { &target_loads.attr, @@ -1082,6 +1123,8 @@ static struct attribute *interactive_attrs[] = { &boostpulse.attr, &boostpulse_duration.attr, &io_is_busy.attr, + &touchboost_freq.attr, + &touchboost_duration.attr, NULL }; ATTRIBUTE_GROUPS(interactive); From a50578469a1881959545cc9a9ea4979d4605a714 Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Mon, 28 Apr 2025 16:54:15 +0800 Subject: [PATCH 13/18] video: rockchip: vehicle: add input mode and fix yuv input order error for rk3576 Signed-off-by: Jianwei Fan Change-Id: Ic2cf42382a889aa5940a2578ced19fda98242d66 --- .../rockchip/vehicle/vehicle_ad_gc2145.c | 1 + drivers/video/rockchip/vehicle/vehicle_cfg.h | 8 +++ drivers/video/rockchip/vehicle/vehicle_cif.c | 57 +++++++++++++++++-- 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/drivers/video/rockchip/vehicle/vehicle_ad_gc2145.c b/drivers/video/rockchip/vehicle/vehicle_ad_gc2145.c index bd4fabdd53ab..e83f69dc1dd0 100644 --- a/drivers/video/rockchip/vehicle/vehicle_ad_gc2145.c +++ b/drivers/video/rockchip/vehicle/vehicle_ad_gc2145.c @@ -849,6 +849,7 @@ static void gc2145_reinit_parameter(struct vehicle_ad_dev *ad, unsigned char cvs ad->cfg.mbus_flags = V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | V4L2_MBUS_PCLK_SAMPLE_RISING; + ad->cfg.input_mode = CIF_INPUT_MODE_BT601_YUV; break; } diff --git a/drivers/video/rockchip/vehicle/vehicle_cfg.h b/drivers/video/rockchip/vehicle/vehicle_cfg.h index 7702edd187ec..70d1675e4beb 100644 --- a/drivers/video/rockchip/vehicle/vehicle_cfg.h +++ b/drivers/video/rockchip/vehicle/vehicle_cfg.h @@ -38,6 +38,13 @@ enum { CIF_INPUT_FORMAT_NTSC_SW_COMPOSITE = 0xfe000000, }; +enum { + CIF_INPUT_MODE_BT601_YUV = 0, + CIF_INPUT_MODE_BT601_RAW = 1, + CIF_INPUT_MODE_BT656_YUV = 2, + CIF_INPUT_MODE_BT1120_YUV = 3, +}; + enum { CIF_OUTPUT_FORMAT_422 = 0, CIF_OUTPUT_FORMAT_420 = 1, @@ -163,6 +170,7 @@ struct vehicle_cfg { int rotate_mirror; struct rkmodule_csi_dphy_param *dphy_param; int drop_frames; + int input_mode; }; #endif diff --git a/drivers/video/rockchip/vehicle/vehicle_cif.c b/drivers/video/rockchip/vehicle/vehicle_cif.c index f0dd1b7dc987..16aa256b4b4e 100644 --- a/drivers/video/rockchip/vehicle/vehicle_cif.c +++ b/drivers/video/rockchip/vehicle/vehicle_cif.c @@ -1769,6 +1769,44 @@ static int rkcif_dvp_get_input_yuv_order(struct vehicle_cfg *cfg) return mask; } +static int rkcif_dvp_get_input_yuv_order_rk3576(struct vehicle_cfg *cfg) +{ + unsigned int mask; + + switch (cfg->mbus_code) { + case MEDIA_BUS_FMT_UYVY8_2X8: + mask = CSI_YUV_INPUT_ORDER_UYVY; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + mask = CSI_YUV_INPUT_ORDER_VYUY; + break; + case MEDIA_BUS_FMT_YUYV8_2X8: + mask = CSI_YUV_INPUT_ORDER_YUYV; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + mask = CSI_YUV_INPUT_ORDER_YVYU; + break; + default: + mask = CSI_YUV_INPUT_ORDER_UYVY; + break; + } + return mask; +} + +static u32 rkcif_determine_input_mode(struct vehicle_cif *cif) +{ + struct vehicle_cfg *cfg = &cif->cif_cfg; + u32 mode = cfg->input_mode << 2; + + if (cif->chip_id == CHIP_RK3568_VEHICLE_CIF && + cfg->input_mode == CIF_INPUT_MODE_BT1120_YUV) + mode = INPUT_MODE_BT1120; + if (cif->chip_id == CHIP_RK3576_VEHICLE_CIF) + mode = mode << 2; + + return mode; +} + static int cif_stream_setup(struct vehicle_cif *cif) { struct vehicle_cfg *cfg = &cif->cif_cfg; @@ -1791,27 +1829,36 @@ static int cif_stream_setup(struct vehicle_cif *cif) else rkvehicle_cif_cfg_dvp_clk_sampling_edge(cif, RKCIF_CLK_FALLING); - inputmode = cfg->input_format<<2; //INPUT_MODE_YUV or INPUT_MODE_BT656_YUV422 + inputmode = rkcif_determine_input_mode(cif); //INPUT_MODE_YUV or INPUT_MODE_BT656_YUV422 //YUV_INPUT_ORDER_UYVY, MEDIA_BUS_FMT_UYVY8_2X8, CCIR_INPUT_ORDER_ODD - input_format = (cfg->yuv_order<<5) | YUV_INPUT_422 | (cfg->field_order<<9); + + input_format = (cfg->yuv_order << 5) | YUV_INPUT_422 | (cfg->field_order << 9); if (cfg->output_format == CIF_OUTPUT_FORMAT_420) output_format = YUV_OUTPUT_420 | UV_STORAGE_ORDER_UVUV; else output_format = YUV_OUTPUT_422 | UV_STORAGE_ORDER_UVUV; if (cif->chip_id == CHIP_RK3568_VEHICLE_CIF) { - val = cfg->vsync | (cfg->href<<1) | inputmode | mipimode + val = cfg->vsync | (cfg->href << 1) | inputmode | mipimode | input_format | output_format | xfer_mode | yc_swap | multi_id_en | multi_id_sel | multi_id_mode | bt1120_edge_mode; - } else { + } else if (cif->chip_id == CHIP_RK3588_VEHICLE_CIF) { out_fmt_mask = (CSI_WRDDR_TYPE_YUV420SP_RK3588 << 11) | (CSI_YUV_OUTPUT_ORDER_UYVY << 1); in_fmt_yuv_order = rkcif_dvp_get_input_yuv_order(cfg); - val = cfg->vsync | (cfg->href<<1) | inputmode + val = cfg->vsync | (cfg->href << 1) | inputmode | in_fmt_yuv_order | out_fmt_mask | yc_swap | multi_id_en | multi_id_sel | sav_detect | multi_id_mode | bt1120_edge_mode; + } else { + out_fmt_mask = (CSI_WRDDR_TYPE_YUV420SP_RK3588 << 15) | + CSI_YUV_OUTPUT_ORDER_UYVY; + in_fmt_yuv_order = rkcif_dvp_get_input_yuv_order_rk3576(cfg); + val = cfg->vsync | (cfg->href << 1) | inputmode + | in_fmt_yuv_order | out_fmt_mask + | yc_swap | multi_id_en | multi_id_sel + | multi_id_mode | (bt1120_edge_mode >> 8); } if (cif->chip_id >= CHIP_RK3576_VEHICLE_CIF) From 279372aba256fc13849301b3c1546aaa57117a27 Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Wed, 30 Apr 2025 17:34:52 +0800 Subject: [PATCH 14/18] video: rockchip: vehicle: add dvp input interlace support Signed-off-by: Jianwei Fan Change-Id: I8445d9d79fae77f20247e82885719f704df9c02e --- drivers/video/rockchip/vehicle/vehicle_cif.c | 24 ++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/video/rockchip/vehicle/vehicle_cif.c b/drivers/video/rockchip/vehicle/vehicle_cif.c index 16aa256b4b4e..b57c6efcc020 100644 --- a/drivers/video/rockchip/vehicle/vehicle_cif.c +++ b/drivers/video/rockchip/vehicle/vehicle_cif.c @@ -1838,6 +1838,26 @@ static int cif_stream_setup(struct vehicle_cif *cif) else output_format = YUV_OUTPUT_422 | UV_STORAGE_ORDER_UVUV; + if (cif->chip_id == CHIP_RK3568_VEHICLE_CIF) { + if (cfg->input_format == CIF_INPUT_FORMAT_PAL || + cfg->input_format == CIF_INPUT_FORMAT_NTSC) + xfer_mode = BT1120_TRANSMIT_INTERFACE; + else + xfer_mode = BT1120_TRANSMIT_PROGRESS; + } else if (cif->chip_id == CHIP_RK3588_VEHICLE_CIF) { + if (cfg->input_format == CIF_INPUT_FORMAT_PAL || + cfg->input_format == CIF_INPUT_FORMAT_NTSC) + xfer_mode = BT1120_TRANSMIT_INTERFACE_RK3588; + else + xfer_mode = BT1120_TRANSMIT_PROGRESS_RK3588; + } else { + if (cfg->input_format == CIF_INPUT_FORMAT_PAL || + cfg->input_format == CIF_INPUT_FORMAT_NTSC) + xfer_mode = BT1120_TRANSMIT_INTERFACE_RK3576; + else + xfer_mode = BT1120_TRANSMIT_PROGRESS_RK3576; + } + if (cif->chip_id == CHIP_RK3568_VEHICLE_CIF) { val = cfg->vsync | (cfg->href << 1) | inputmode | mipimode | input_format | output_format @@ -1848,7 +1868,7 @@ static int cif_stream_setup(struct vehicle_cif *cif) (CSI_YUV_OUTPUT_ORDER_UYVY << 1); in_fmt_yuv_order = rkcif_dvp_get_input_yuv_order(cfg); val = cfg->vsync | (cfg->href << 1) | inputmode - | in_fmt_yuv_order | out_fmt_mask + | in_fmt_yuv_order | out_fmt_mask | xfer_mode | yc_swap | multi_id_en | multi_id_sel | sav_detect | multi_id_mode | bt1120_edge_mode; } else { @@ -1856,7 +1876,7 @@ static int cif_stream_setup(struct vehicle_cif *cif) CSI_YUV_OUTPUT_ORDER_UYVY; in_fmt_yuv_order = rkcif_dvp_get_input_yuv_order_rk3576(cfg); val = cfg->vsync | (cfg->href << 1) | inputmode - | in_fmt_yuv_order | out_fmt_mask + | in_fmt_yuv_order | out_fmt_mask | xfer_mode | yc_swap | multi_id_en | multi_id_sel | multi_id_mode | (bt1120_edge_mode >> 8); } From da66c6a3ee82dd794e3660530e735996fc7331bc Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Wed, 14 May 2025 10:45:48 +0800 Subject: [PATCH 15/18] drm/bridge: analogix_dp: replace readl()/writel() with analogix_dp_read()/_write() for SSC switch The analogix_dp_read()/analogix_dp_write() help workaround async issue between pclk clock and 24m clock. See the following commit for details: commit 33f5d1439fcd ("drm/bridge: analogix_dp: Workaround async issue between pclk clock and 24m clock") Change-Id: I41a0767184bfbfc5bcacceb2177189836dcc9e90 Signed-off-by: Damon Ding --- drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c index 810b5a2f6041..a432f8502933 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c @@ -532,25 +532,25 @@ static void analogix_dp_ssc_enable(struct analogix_dp_device *dp) u32 reg; /* 4500ppm */ - writel(0x19, dp->reg_base + ANALOIGX_DP_SSC_REG); + analogix_dp_write(dp, ANALOIGX_DP_SSC_REG, 0x19); /* * To apply updated SSC parameters into SSC operation, * firmware must disable and enable this bit. */ - reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_2); + reg = analogix_dp_read(dp, ANALOGIX_DP_FUNC_EN_2); reg |= SSC_FUNC_EN_N; - writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2); + analogix_dp_write(dp, ANALOGIX_DP_FUNC_EN_2, reg); reg &= ~SSC_FUNC_EN_N; - writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2); + analogix_dp_write(dp, ANALOGIX_DP_FUNC_EN_2, reg); } static void analogix_dp_ssc_disable(struct analogix_dp_device *dp) { u32 reg; - reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_2); + reg = analogix_dp_read(dp, ANALOGIX_DP_FUNC_EN_2); reg |= SSC_FUNC_EN_N; - writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2); + analogix_dp_write(dp, ANALOGIX_DP_FUNC_EN_2, reg); } bool analogix_dp_ssc_supported(struct analogix_dp_device *dp) From b176f5d83b2a7fa52f8e5208ec131b7df5532af5 Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Sat, 19 Apr 2025 13:30:35 +0800 Subject: [PATCH 16/18] PCI: rockchip: dw_ep: Avoid repeated calls to irq and works registration Change-Id: Ic98737ccaa515f1e36e39e1e4615c68cedb6d9de Signed-off-by: Jon Lin --- .../pci/controller/dwc/pcie-dw-ep-rockchip.c | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c index 8c9fb20f9809..198bf04cdb4b 100644 --- a/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c @@ -975,13 +975,6 @@ static int rockchip_pcie_config_host(struct rockchip_pcie *rockchip) already_linkup: /* Enable client reset or link down interrupt */ rockchip_pcie_writel_apb(rockchip, 0x40000, PCIE_CLIENT_INTR_MASK); - rockchip->hot_rst_wq = create_singlethread_workqueue("rkep_hot_rst_wq"); - if (!rockchip->hot_rst_wq) { - dev_err(dev, "Failed to create hot_rst workqueue\n"); - return -ENOMEM; - } - INIT_WORK(&rockchip->hot_rst_work, rockchip_pcie_hot_rst_work); - init_waitqueue_head(&rockchip->wq_head); /* Enable client elbi interrupt */ rockchip_pcie_writel_apb(rockchip, 0x80000000, PCIE_CLIENT_INTR_MASK); @@ -995,6 +988,29 @@ already_linkup: dw_pcie_writel_dbi(&rockchip->pci, PCIE_DMA_OFFSET + PCIE_DMA_WR_INT_MASK, 0x0); dw_pcie_writel_dbi(&rockchip->pci, PCIE_DMA_OFFSET + PCIE_DMA_RD_INT_MASK, 0x0); + /* Setting device */ + if (dw_pcie_readl_dbi(&rockchip->pci, PCIE_ATU_VIEWPORT) == 0xffffffff) + rockchip->pci.iatu_unroll_enabled = 1; + for (i = 0; i < PCIE_BAR_MAX_NUM; i++) + if (rockchip->ib_target_size[i]) + rockchip_pcie_ep_set_bar(rockchip, i, rockchip->ib_target_address[i]); + + return 0; +} + +static int rockchip_pcie_config_irq_and_works(struct rockchip_pcie *rockchip) +{ + struct device *dev = rockchip->pci.dev; + int ret; + + rockchip->hot_rst_wq = create_singlethread_workqueue("rkep_hot_rst_wq"); + if (!rockchip->hot_rst_wq) { + dev_err(dev, "Failed to create hot_rst workqueue\n"); + return -ENOMEM; + } + INIT_WORK(&rockchip->hot_rst_work, rockchip_pcie_hot_rst_work); + init_waitqueue_head(&rockchip->wq_head); + ret = devm_request_irq(dev, rockchip->irq, rockchip_pcie_sys_irq_handler, IRQF_SHARED, "pcie-sys", rockchip); if (ret) { @@ -1002,12 +1018,6 @@ already_linkup: return ret; } - /* Setting device */ - if (dw_pcie_readl_dbi(&rockchip->pci, PCIE_ATU_VIEWPORT) == 0xffffffff) - rockchip->pci.iatu_unroll_enabled = 1; - for (i = 0; i < PCIE_BAR_MAX_NUM; i++) - if (rockchip->ib_target_size[i]) - rockchip_pcie_ep_set_bar(rockchip, i, rockchip->ib_target_address[i]); rockchip_pcie_devmode_update(rockchip, RKEP_MODE_KERNEL, RKEP_SMODE_LNKUP); return 0; @@ -1400,6 +1410,12 @@ static int rockchip_pcie_ep_probe(struct platform_device *pdev) goto deinit_host; } + ret = rockchip_pcie_config_irq_and_works(rockchip); + if (ret) { + dev_err(dev, "Failed to config irq and works\n"); + goto deinit_host; + } + ret = rockchip_pcie_init_dma_trx(rockchip); if (ret) { dev_err(dev, "Failed to initial dma trx!\n"); From 032401c7c9de00cd1fce2f0e73de3abaf0d700d0 Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Mon, 21 Apr 2025 14:35:36 +0800 Subject: [PATCH 17/18] PCI: rockchip: dw_ep: Remove useless vm_flags definition Define vm_flags by remap_pfn_range. Change-Id: I6f1b7ca4e1653fab2eab0f4139c6b9648c903e10 Signed-off-by: Jon Lin --- drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c index 198bf04cdb4b..6cad158014ae 100644 --- a/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c @@ -1325,9 +1325,6 @@ static int pcie_ep_mmap(struct file *file, struct vm_area_struct *vma) return -EINVAL; } - vma->vm_flags |= VM_IO; - vma->vm_flags |= (VM_DONTEXPAND | VM_DONTDUMP); - if (rockchip->cur_mmap_res == PCIE_EP_MMAP_RESOURCE_BAR2) vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); else From 73d20803ec8e83587da510d6d0feeead120c7219 Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Mon, 21 Apr 2025 15:31:39 +0800 Subject: [PATCH 18/18] PCI: rockchip: dw_ep: Reset atu index when PM suspend/resume Change-Id: I131580165444f1e7b0393cb3645594b1c43021ea Signed-off-by: Jon Lin --- drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c index 6cad158014ae..6e8168472f14 100644 --- a/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c @@ -991,6 +991,8 @@ already_linkup: /* Setting device */ if (dw_pcie_readl_dbi(&rockchip->pci, PCIE_ATU_VIEWPORT) == 0xffffffff) rockchip->pci.iatu_unroll_enabled = 1; + memset(rockchip->ib_window_map, 0, BITS_TO_LONGS(rockchip->num_ib_windows) * sizeof(long)); + memset(rockchip->ob_window_map, 0, BITS_TO_LONGS(rockchip->num_ob_windows) * sizeof(long)); for (i = 0; i < PCIE_BAR_MAX_NUM; i++) if (rockchip->ib_target_size[i]) rockchip_pcie_ep_set_bar(rockchip, i, rockchip->ib_target_address[i]);