diff --git a/.version b/.version deleted file mode 100644 index 1e8b314962144..0000000000000 --- a/.version +++ /dev/null @@ -1 +0,0 @@ -6 diff --git a/Linux-Kernel.si4project/Backup/README(4088).md b/Linux-Kernel.si4project/Backup/README(4088).md new file mode 100755 index 0000000000000..a550875bee241 --- /dev/null +++ b/Linux-Kernel.si4project/Backup/README(4088).md @@ -0,0 +1,161 @@ +# 增加GC5603.c驱动并修改对应Kernel文件: +1. drivers/media/i2c/增加GC5603.c文件; +2. arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi修改Device tree; +3. arch/arm64/configs/lubancat_linux_rk3588_defconfig修改defconfig +4. drivers/media/i2c/Kconfig增加gc5603.c编译 +5. drivers/media/i2c/Makefile增加gc5603.c编译 +6. drivers/phy/rockchip/phy-rockchip-csi2-dphy.c修改后增加cphy支持 + +以上修改已经编译通过(2024年6月),将进行点亮验证。 + + +# How do I submit patches to Android Common Kernels + +1. BEST: Make all of your changes to upstream Linux. If appropriate, backport to the stable releases. + These patches will be merged automatically in the corresponding common kernels. If the patch is already + in upstream Linux, post a backport of the patch that conforms to the patch requirements below. + - Do not send patches upstream that contain only symbol exports. To be considered for upstream Linux, +additions of `EXPORT_SYMBOL_GPL()` require an in-tree modular driver that uses the symbol -- so include +the new driver or changes to an existing driver in the same patchset as the export. + - When sending patches upstream, the commit message must contain a clear case for why the patch +is needed and beneficial to the community. Enabling out-of-tree drivers or functionality is not +not a persuasive case. + +2. LESS GOOD: Develop your patches out-of-tree (from an upstream Linux point-of-view). Unless these are + fixing an Android-specific bug, these are very unlikely to be accepted unless they have been + coordinated with kernel-team@android.com. If you want to proceed, post a patch that conforms to the + patch requirements below. + +# Common Kernel patch requirements + +- All patches must conform to the Linux kernel coding standards and pass `script/checkpatch.pl` +- Patches shall not break gki_defconfig or allmodconfig builds for arm, arm64, x86, x86_64 architectures +(see https://source.android.com/setup/build/building-kernels) +- If the patch is not merged from an upstream branch, the subject must be tagged with the type of patch: +`UPSTREAM:`, `BACKPORT:`, `FROMGIT:`, `FROMLIST:`, or `ANDROID:`. +- All patches must have a `Change-Id:` tag (see https://gerrit-review.googlesource.com/Documentation/user-changeid.html) +- If an Android bug has been assigned, there must be a `Bug:` tag. +- All patches must have a `Signed-off-by:` tag by the author and the submitter + +Additional requirements are listed below based on patch type + +## Requirements for backports from mainline Linux: `UPSTREAM:`, `BACKPORT:` + +- If the patch is a cherry-pick from Linux mainline with no changes at all + - tag the patch subject with `UPSTREAM:`. + - add upstream commit information with a `(cherry picked from commit ...)` line + - Example: + - if the upstream commit message is +``` + important patch from upstream + + This is the detailed description of the important patch + + Signed-off-by: Fred Jones +``` +>- then Joe Smith would upload the patch for the common kernel as +``` + UPSTREAM: important patch from upstream + + This is the detailed description of the important patch + + Signed-off-by: Fred Jones + + Bug: 135791357 + Change-Id: I4caaaa566ea080fa148c5e768bb1a0b6f7201c01 + (cherry picked from commit c31e73121f4c1ec41143423ac6ce3ce6dafdcec1) + Signed-off-by: Joe Smith +``` + +- If the patch requires any changes from the upstream version, tag the patch with `BACKPORT:` +instead of `UPSTREAM:`. + - use the same tags as `UPSTREAM:` + - add comments about the changes under the `(cherry picked from commit ...)` line + - Example: +``` + BACKPORT: important patch from upstream + + This is the detailed description of the important patch + + Signed-off-by: Fred Jones + + Bug: 135791357 + Change-Id: I4caaaa566ea080fa148c5e768bb1a0b6f7201c01 + (cherry picked from commit c31e73121f4c1ec41143423ac6ce3ce6dafdcec1) + [joe: Resolved minor conflict in drivers/foo/bar.c ] + Signed-off-by: Joe Smith +``` + +## Requirements for other backports: `FROMGIT:`, `FROMLIST:`, + +- If the patch has been merged into an upstream maintainer tree, but has not yet +been merged into Linux mainline + - tag the patch subject with `FROMGIT:` + - add info on where the patch came from as `(cherry picked from commit )`. This +must be a stable maintainer branch (not rebased, so don't use `linux-next` for example). + - if changes were required, use `BACKPORT: FROMGIT:` + - Example: + - if the commit message in the maintainer tree is +``` + important patch from upstream + + This is the detailed description of the important patch + + Signed-off-by: Fred Jones +``` +>- then Joe Smith would upload the patch for the common kernel as +``` + FROMGIT: important patch from upstream + + This is the detailed description of the important patch + + Signed-off-by: Fred Jones + + Bug: 135791357 + (cherry picked from commit 878a2fd9de10b03d11d2f622250285c7e63deace + https://git.kernel.org/pub/scm/linux/kernel/git/foo/bar.git test-branch) + Change-Id: I4caaaa566ea080fa148c5e768bb1a0b6f7201c01 + Signed-off-by: Joe Smith +``` + + +- If the patch has been submitted to LKML, but not accepted into any maintainer tree + - tag the patch subject with `FROMLIST:` + - add a `Link:` tag with a link to the submittal on lore.kernel.org + - add a `Bug:` tag with the Android bug (required for patches not accepted into +a maintainer tree) + - if changes were required, use `BACKPORT: FROMLIST:` + - Example: +``` + FROMLIST: important patch from upstream + + This is the detailed description of the important patch + + Signed-off-by: Fred Jones + + Bug: 135791357 + Link: https://lore.kernel.org/lkml/20190619171517.GA17557@someone.com/ + Change-Id: I4caaaa566ea080fa148c5e768bb1a0b6f7201c01 + Signed-off-by: Joe Smith +``` + +## Requirements for Android-specific patches: `ANDROID:` + +- If the patch is fixing a bug to Android-specific code + - tag the patch subject with `ANDROID:` + - add a `Fixes:` tag that cites the patch with the bug + - Example: +``` + ANDROID: fix android-specific bug in foobar.c + + This is the detailed description of the important fix + + Fixes: 1234abcd2468 ("foobar: add cool feature") + Change-Id: I4caaaa566ea080fa148c5e768bb1a0b6f7201c01 + Signed-off-by: Joe Smith +``` + +- If the patch is a new feature + - tag the patch subject with `ANDROID:` + - add a `Bug:` tag with the Android bug (required for android-specific features) + diff --git a/Linux-Kernel.si4project/Backup/gc5603(4678).c b/Linux-Kernel.si4project/Backup/gc5603(4678).c new file mode 100755 index 0000000000000..c6291906f5c01 --- /dev/null +++ b/Linux-Kernel.si4project/Backup/gc5603(4678).c @@ -0,0 +1,1613 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * gc5603 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + * V0.0X01.0X03 add enum_frame_interval function. + * V0.0X01.0X04 add quick stream on/off + * V0.0X01.0X05 add function g_mbus_config + * V0.0X01.0X06 + * 1. add 2lane support. + * 2. add some debug info. + * 3. adjust gc5603_g_mbus_config function. + * V0.0X01.0X07 support get channel info + * V0.0X01.0X08 + * 1. default support 2lane full 30fps. + * 2. default support rk otp spec. + * V0.0X01.0X09 adjust supply sequence to suit spec + */ +//#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x01) +#define GC5603_NAME "gc5603" +#define GC5603_MEDIA_BUS_FMT MEDIA_BUS_FMT_SRGGB10_1X10 + +#define MIPI_FREQ_848M 423000000 + +#define GC5603_PAGE_SELECT 0xFE + +#define GC5603_REG_CHIP_ID_H 0x03F0 +#define GC5603_REG_CHIP_ID_L 0x03F1 + +#define GC5603_REG_EXP_H 0x0202 +#define GC5603_REG_EXP_L 0x0203 + +#define GC5603_REG_VTS_H 0x0340 +#define GC5603_REG_VTS_L 0x0341 + +#define GC5603_REG_CTRL_MODE 0x0100 +#define GC5603_MODE_SW_STANDBY 0x00 +#define GC5603_MODE_STREAMING 0x09 + +#define REG_NULL 0xFFFF + +#define GC5603_CHIP_ID 0x5603 + +#define GC5603_VTS_MAX 0x7fff +#define GC5603_HTS_MAX 0xFFF + +#define GC5603_EXPOSURE_MAX 0x3FFF +#define GC5603_EXPOSURE_MIN 1 +#define GC5603_EXPOSURE_STEP 1 + +#define GC5603_GAIN_MIN 64 +#define GC5603_GAIN_MAX 0xffff +#define GC5603_GAIN_STEP 1 +#define GC5603_GAIN_DEFAULT 64 + +#define gc5603_REG_VALUE_08BIT 1 +#define gc5603_REG_VALUE_16BIT 2 +#define gc5603_REG_VALUE_24BIT 3 + + +#define GC5603_LANES 2 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + + + +#define GC5603_FLIP_MIRROR_REG 0x0101 + +#define GC_MIRROR_BIT_MASK BIT(0) +#define GC_FLIP_BIT_MASK BIT(1) + +#define GC5603_XVCLK_FREQ_24M 24000000 +#define GC5603_XVCLK_FREQ_27M 27000000 + +static const char * const gc5603_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + +#define GC5603_NUM_SUPPLIES ARRAY_SIZE(gc5603_supply_names) + +#define to_gc5603(sd) container_of(sd, struct gc5603, subdev) + +struct regval { + u16 addr; + u8 val; +}; + +struct gc5603_mode { + u32 bus_fmt + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; + u32 hdr_mode; + u32 vc[PAD_MAX]; + u32 xvclk; +}; + +struct gc5603 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct gpio_desc *pwren_gpio; + struct regulator_bulk_data supplies[GC5603_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *h_flip; + struct v4l2_ctrl *v_flip; + struct mutex mutex; + bool streaming; + bool power_on; + const struct gc5603_mode *cur_mode; + unsigned int lane_num; + unsigned int cfg_num; + unsigned int pixel_rate; + + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + struct rkmodule_awb_cfg awb_cfg; + struct rkmodule_lsc_cfg lsc_cfg; + u32 flip; +}; + + +static const struct regval gc5603_2960x1666_regs_2lane[] = { +//version 1.3 +//mclk 27Mhz +//mipi 2 lane 846Mbps/lane +//vts = 1750 ,row_time=19.05us +//window 2960x1666 +//BGGR + {0x03fe, 0xf0}, + {0x03fe, 0x00}, + {0x03fe, 0x10}, + {0x03fe, 0x00}, + {0x0202, 0x01}, + {0x0203, 0x50}, + {0x0a38, 0x02}, + {0x0a38, 0x03}, + {0x0a20, 0x07}, + {0x061b, 0x03}, + {0x061c, 0x50}, + {0x061d, 0x05}, + {0x061e, 0x70}, + {0x061f, 0x03}, + {0x0a21, 0x08}, + {0x0a34, 0x40}, + {0x0a35, 0x11}, + {0x0a36, 0x5e}, + {0x0a37, 0x03}, + {0x0314, 0x50}, + {0x0315, 0x32}, + {0x031c, 0xce}, + {0x0219, 0x47}, + {0x0342, 0x04}, + {0x0343, 0xb0}, + {0x0340, 0x06}, + {0x0341, 0xd6}, + {0x0345, 0x02}, + {0x0347, 0x02}, + {0x0348, 0x0b}, + {0x0349, 0x98}, + {0x034a, 0x06}, + {0x034b, 0x8a}, + {0x0094, 0x0b}, + {0x0095, 0x90}, + {0x0096, 0x06}, + {0x0097, 0x82}, + {0x0099, 0x04}, + {0x009b, 0x04}, + {0x060c, 0x01}, + {0x060e, 0xd2}, + {0x060f, 0x05}, + {0x070c, 0x01}, + {0x070e, 0xd2}, + {0x070f, 0x05}, + {0x0909, 0x07}, + {0x0902, 0x04}, + {0x0904, 0x0b}, + {0x0907, 0x54}, + {0x0908, 0x06}, + {0x0903, 0x9d}, + {0x072a, 0x1c},//18 + {0x072b, 0x1c},//18 + {0x0724, 0x2b}, + {0x0727, 0x2b}, + {0x1466, 0x18}, + {0x1467, 0x08}, + {0x1468, 0x10}, + {0x1469, 0x80}, + {0x146a, 0xe8},//b8 + {0x1412, 0x20}, + {0x0707, 0x07}, + {0x0737, 0x0f}, + {0x0704, 0x01}, + {0x0706, 0x03}, + {0x0716, 0x03}, + {0x0708, 0xc8}, + {0x0718, 0xc8}, + {0x061a, 0x02}, + {0x1430, 0x80}, + {0x1407, 0x10}, + {0x1408, 0x16}, + {0x1409, 0x03}, + {0x1438, 0x01}, + {0x02ce, 0x03}, + {0x0245, 0xc9}, + {0x023a, 0x08},//3B + {0x02cd, 0x88}, + {0x0612, 0x02}, + {0x0613, 0xc7}, + {0x0243, 0x03},//06 + {0x0089, 0x03}, + {0x0002, 0xab}, + {0x0040, 0xa3}, + {0x0075, 0x64},//64 + {0x0004, 0x0f}, + {0x0053, 0x0a}, + {0x0205, 0x0c}, + + //auto_load}, + {0x0a67, 0x80}, + {0x0a54, 0x0e}, + {0x0a65, 0x10}, + {0x0a98, 0x04}, + {0x05be, 0x00}, + {0x05a9, 0x01}, + {0x0023, 0x00}, + {0x0022, 0x00}, + {0x0025, 0x00}, + {0x0024, 0x00}, + {0x0028, 0x0b}, + {0x0029, 0x98}, + {0x002a, 0x06}, + {0x002b, 0x86}, + {0x0a83, 0xe0}, + {0x0a72, 0x02}, + {0x0a73, 0x60}, + {0x0a75, 0x41}, + {0x0a70, 0x03}, + {0x0a5a, 0x80}, + {0x0181, 0x30}, + {0x0182, 0x05}, + {0x0185, 0x01}, + {0x0180, 0x46}, + {0x0100, 0x08}, + {0x010d, 0x74}, + {0x010e, 0x0e}, + {0x0113, 0x02}, + {0x0114, 0x01}, + {0x0115, 0x10}, + //{0x0a70, 0x00}, + //{0x0080, 0x02}, + //{0x0a67, 0x00}, + {0x0052, 0x02}, + {0x0076, 0x01}, + {0x021a, 0x10}, + {0x0049, 0x0f}, + {0x004a, 0x3c}, + {0x004b, 0x00}, + {0x0430, 0x25}, + {0x0431, 0x25}, + {0x0432, 0x25}, + {0x0433, 0x25}, + {0x0434, 0x59}, + {0x0435, 0x59}, + {0x0436, 0x59}, + {0x0437, 0x59}, + + {0x0100, 0x09}, + {REG_NULL, 0x00}, +}; + +static const struct gc5603_mode supported_modes[] = { + { + .bus_fmt = GC5603_MEDIA_BUS_FMT + .width = 2960, + .height = 1666, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x6ce, + .hts_def = 0x0C80, + .vts_def = 0x06D6, + .reg_list = gc5603_2960x1666_regs_2lane, + .hdr_mode = NO_HDR, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = GC5603_XVCLK_FREQ_24M + }, +}; + +static const s64 link_freq_menu_items[] = { + MIPI_FREQ_848M +}; +static int gc5603_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int gc5603_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = gc5603_write_reg(client, regs[i].addr, + gc5603_REG_VALUE_08BIT, regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int gc5603_read_reg(struct i2c_client *client, u16 reg, unsigned int len, + u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + + return 0; + dev_info(&client->dev, + "gc5603 read reg(0x%x val:0x%x) \n", reg, *val); +} +static int gc5603_get_reso_dist(const struct gc5603_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct gc5603_mode * +gc5603_find_best_fit(struct gc5603 *gc5603, struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < gc5603->cfg_num; i++) { + dist = gc5603_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist <= cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static uint8_t regValTable[26][7] = { + //0614, 0615, 0225, 1467 1468, 00b8, 00b9 + { 0x00, 0x00, 0x04, 0x15, 0x15, 0x01, 0x00}, + { 0x90, 0x02, 0x04, 0x15, 0x15, 0x01, 0x0A}, + { 0x00, 0x00, 0x00, 0x15, 0x15, 0x01, 0x12}, + { 0x90, 0x02, 0x00, 0x15, 0x15, 0x01, 0x20}, + { 0x01, 0x00, 0x00, 0x15, 0x15, 0x01, 0x30}, + { 0x91, 0x02, 0x00, 0x15, 0x15, 0x02, 0x05}, + { 0x02, 0x00, 0x00, 0x15, 0x15, 0x02, 0x19}, + { 0x92, 0x02, 0x00, 0x16, 0x16, 0x02, 0x3F}, + { 0x03, 0x00, 0x00, 0x16, 0x16, 0x03, 0x20}, + { 0x93, 0x02, 0x00, 0x17, 0x17, 0x04, 0x0A}, + { 0x00, 0x00, 0x01, 0x18, 0x18, 0x05, 0x02}, + { 0x90, 0x02, 0x01, 0x19, 0x19, 0x05, 0x39}, + { 0x01, 0x00, 0x01, 0x19, 0x19, 0x06, 0x3C}, + { 0x91, 0x02, 0x01, 0x19, 0x19, 0x08, 0x0D}, + { 0x02, 0x00, 0x01, 0x1a, 0x1a, 0x09, 0x21}, + { 0x92, 0x02, 0x01, 0x1a, 0x1a, 0x0B, 0x0F}, + { 0x03, 0x00, 0x01, 0x1c, 0x1c, 0x0D, 0x17}, + { 0x93, 0x02, 0x01, 0x1c, 0x1c, 0x0F, 0x33}, + { 0x04, 0x00, 0x01, 0x1d, 0x1d, 0x12, 0x30}, + { 0x94, 0x02, 0x01, 0x1d, 0x1d, 0x16, 0x10}, + { 0x05, 0x00, 0x01, 0x1e, 0x1e, 0x1A, 0x19}, + { 0x95, 0x02, 0x01, 0x1e, 0x1e, 0x1F, 0x13}, + { 0x06, 0x00, 0x01, 0x20, 0x20, 0x25, 0x08}, + { 0x96, 0x02, 0x01, 0x20, 0x20, 0x2C, 0x03}, + { 0xb6, 0x04, 0x01, 0x20, 0x20, 0x34, 0x0F}, + { 0x86, 0x06, 0x01, 0x20, 0x20, 0x3D, 0x3D}, +}; + +static uint32_t gain_level_table[27] = { + 64, + 74, + 82, + 96, + 112, + 133, + 153, + 191, + 224, + 266, + 322, + 377, + 444, + 525, + 609, + 719, + 855, + 1011, + 1200, + 1424, + 1689, + 2003, + + 2376, + 2819, + + 3343, + 3965, + 0xffffffff, +}; +//static int total = sizeof(gain_level_table) / sizeof(uint32_t); + +static int gc5603_set_gain(struct gc5603 *gc5603, u32 gain) +{ + int ret; + uint16_t i = 0; + uint16_t total = 0; + uint16_t temp = 0; + + + for (i = 0; i < total; i++) { + if ((gain_level_table[i] <= gain) && (gain < gain_level_table[i+1])) + break; + } + + if((gain>3965)||(gain==3965)) + i =25; + + + ret = gc5603_write_reg(gc5603->client, 0x031d,gc5603_REG_VALUE_08BIT, 0x2d); + ret = gc5603_write_reg(gc5603->client, 0x0614,gc5603_REG_VALUE_08BIT,regValTable[i][0]); + ret = gc5603_write_reg(gc5603->client, 0x0615,gc5603_REG_VALUE_08BIT,regValTable[i][1]); + ret = gc5603_write_reg(gc5603->client, 0x0225,gc5603_REG_VALUE_08BIT,regValTable[i][2]); + + ret = gc5603_write_reg(gc5603->client, 0x031d,gc5603_REG_VALUE_08BIT, 0x28); + ret = gc5603_write_reg(gc5603->client, 0x1467,gc5603_REG_VALUE_08BIT,regValTable[i][3]); + ret = gc5603_write_reg(gc5603->client, 0x1468,gc5603_REG_VALUE_08BIT,regValTable[i][4]); + ret = gc5603_write_reg(gc5603->client, 0x00b8,gc5603_REG_VALUE_08BIT,regValTable[i][5]); + ret = gc5603_write_reg(gc5603->client, 0x00b9,gc5603_REG_VALUE_08BIT,regValTable[i][6]); + + + temp = 64 * gain / gain_level_table[i]; + + //dev_warn(&client->dev, "gc5603_set_gain gain=%d,i=%d,temp=%d \n", gain, i, temp); + + ret |= gc5603_write_reg(gc5603->client, 0x0064,gc5603_REG_VALUE_08BIT,(temp >> 6)); + ret |= gc5603_write_reg(gc5603->client, 0x0065,gc5603_REG_VALUE_08BIT,((temp&0x3f) << 2) ); + + return ret; +} + +static int gc5603_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc5603 *gc5603 = container_of(ctrl->handler, + struct gc5603, ctrl_handler); + struct i2c_client *client = gc5603->client; + s64 max; + int ret = 0; + u32 vts = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = gc5603->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(gc5603->exposure, + gc5603->exposure->minimum, max, + gc5603->exposure->step, + gc5603->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + + #if 1 + ret = gc5603_write_reg(gc5603->client, GC5603_REG_EXP_H,gc5603_REG_VALUE_08BIT, + (ctrl->val >> 8)); + ret |= gc5603_write_reg(gc5603->client, GC5603_REG_EXP_L,gc5603_REG_VALUE_08BIT, + ctrl->val & 0xff); + #endif + + break; + case V4L2_CID_ANALOGUE_GAIN: + gc5603_set_gain(gc5603, ctrl->val); + break; + case V4L2_CID_VBLANK: + vts = ctrl->val + gc5603->cur_mode->height; + + #if 1 + ret = gc5603_write_reg(gc5603->client, GC5603_REG_VTS_H,gc5603_REG_VALUE_08BIT,(vts >> 8)); + ret |= gc5603_write_reg(gc5603->client, GC5603_REG_VTS_L, gc5603_REG_VALUE_08BIT,vts & 0xff); + #endif + break; + case V4L2_CID_HFLIP: + if (ctrl->val) + gc5603->flip |= GC_MIRROR_BIT_MASK; + else + gc5603->flip &= ~GC_MIRROR_BIT_MASK; + break; + case V4L2_CID_VFLIP: + if (ctrl->val) + gc5603->flip |= GC_FLIP_BIT_MASK; + else + gc5603->flip &= ~GC_FLIP_BIT_MASK; + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + return ret; +} + +static const struct v4l2_ctrl_ops gc5603_ctrl_ops = { + .s_ctrl = gc5603_set_ctrl, +}; + +static int gc5603_configure_regulators(struct gc5603 *gc5603) +{ + unsigned int i; + + for (i = 0; i < GC5603_NUM_SUPPLIES; i++) + gc5603->supplies[i].supply = gc5603_supply_names[i]; + + return devm_regulator_bulk_get(&gc5603->client->dev, + GC5603_NUM_SUPPLIES, + gc5603->supplies); +} + +static int gc5603_parse_of(struct gc5603 *gc5603) +{ + struct device *dev = &gc5603->client->dev; + struct device_node *endpoint; + struct fwnode_handle *fwnode; + int rval; + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "Failed to get endpoint\n"); + return -EINVAL; + } + fwnode = of_fwnode_handle(endpoint); + rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0); + if (rval <= 0) { + dev_warn(dev, " Get mipi lane num failed!\n"); + return -1; + } + + gc5603->lane_num = rval; + if (2 == gc5603->lane_num) { + gc5603->cur_mode = &supported_modes[0]; + gc5603->cfg_num = ARRAY_SIZE(supported_modes); + + /*pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + gc5603->pixel_rate = MIPI_FREQ_848M * 2U * (gc5603->lane_num) / 10U; + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + gc5603->lane_num, gc5603->pixel_rate); + } else { + dev_info(dev, "gc5603 can not support the lane num(%d)\n", gc5603->lane_num); + } + return 0; +} + +static int gc5603_initialize_controls(struct gc5603 *gc5603) +{ + const struct gc5603_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &gc5603->ctrl_handler; + mode = gc5603->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &gc5603->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, gc5603->pixel_rate, 1, gc5603->pixel_rate); + + h_blank = mode->hts_def - mode->width; + gc5603->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (gc5603->hblank) + gc5603->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + gc5603->vblank = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + GC5603_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + gc5603->exposure = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_EXPOSURE, + GC5603_EXPOSURE_MIN, + exposure_max, + GC5603_EXPOSURE_STEP, + mode->exp_def); + + gc5603->anal_gain = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + GC5603_GAIN_MIN, + GC5603_GAIN_MAX, + GC5603_GAIN_STEP, + GC5603_GAIN_DEFAULT); + + gc5603->h_flip = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + gc5603->v_flip = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + gc5603->flip = 0; + + if (handler->error) { + ret = handler->error; + dev_err(&gc5603->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + gc5603->subdev.ctrl_handler = handler; + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 gc5603_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, GC5603_XVCLK_FREQ_24M / 1000 / 1000); +} + +static int __gc5603_power_on(struct gc5603 *gc5603) +{ + int ret; + u32 delay_us; + struct device *dev = &gc5603->client->dev; + + if (!IS_ERR_OR_NULL(gc5603->pins_default)) { + ret = pinctrl_select_state(gc5603->pinctrl, + gc5603->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + ret = clk_set_rate(gc5603->xvclk, GC5603_XVCLK_FREQ_24M); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(gc5603->xvclk) != GC5603_XVCLK_FREQ_24M) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(gc5603->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 0); + + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 0); + + usleep_range(500, 1000); + ret = regulator_bulk_enable(GC5603_NUM_SUPPLIES, gc5603->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + if (!IS_ERR(gc5603->pwren_gpio)) + gpiod_set_value_cansleep(gc5603->pwren_gpio, 1); + + usleep_range(1000, 1100); + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 1); + usleep_range(100, 150); + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = gc5603_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + return 0; + +disable_clk: + clk_disable_unprepare(gc5603->xvclk); + return ret; +} + +static void __gc5603_power_off(struct gc5603 *gc5603) +{ + int ret; + struct device *dev = &gc5603->client->dev; + + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 0); + clk_disable_unprepare(gc5603->xvclk); + + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 0); + + if (!IS_ERR_OR_NULL(gc5603->pins_sleep)) { + ret = pinctrl_select_state(gc5603->pinctrl, + gc5603->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + regulator_bulk_disable(GC5603_NUM_SUPPLIES, gc5603->supplies); + if (!IS_ERR(gc5603->pwren_gpio)) + gpiod_set_value_cansleep(gc5603->pwren_gpio, 0); + +} + + static int gc5603_check_sensor_id(struct gc5603 *gc5603, + struct i2c_client *client) +{ + struct device *dev = &gc5603->client->dev; + u16 id = 0; + u32 reg_H = 0; + u32 reg_L = 0; + int ret; + + ret = gc5603_read_reg(client, GC5603_REG_CHIP_ID_H, + gc5603_REG_VALUE_08BIT, ®_H); + ret |= gc5603_read_reg(client, GC5603_REG_CHIP_ID_L, + gc5603_REG_VALUE_08BIT, ®_L); + + id = ((reg_H << 8) & 0xff00) | (reg_L & 0xff); + if (!(reg_H == (GC5603_CHIP_ID >> 8) || reg_L == (GC5603_CHIP_ID & 0xff))) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + dev_info(dev, "detected gc%04x sensor\n", id); + return 0; +} + +static int gc5603_set_flip(struct gc5603 *gc5603, u8 mode) +{ +u32 match_reg = 0; + + gc5603_read_reg(gc5603->client, GC5603_FLIP_MIRROR_REG,gc5603_REG_VALUE_08BIT,&match_reg); + + if (mode == GC_FLIP_BIT_MASK) { + match_reg |= GC_FLIP_BIT_MASK; + match_reg &= ~GC_MIRROR_BIT_MASK; + } else if (mode == GC_MIRROR_BIT_MASK) { + match_reg |= GC_MIRROR_BIT_MASK; + match_reg &= ~GC_FLIP_BIT_MASK; + } else if (mode == (GC_MIRROR_BIT_MASK | + GC_FLIP_BIT_MASK)) { + match_reg |= GC_FLIP_BIT_MASK; + match_reg |= GC_MIRROR_BIT_MASK; + } else { + match_reg &= ~GC_FLIP_BIT_MASK; + match_reg &= ~GC_MIRROR_BIT_MASK; + } + return gc5603_write_reg(gc5603->client, GC5603_FLIP_MIRROR_REG,gc5603_REG_VALUE_08BIT ,match_reg); +} + +static int __gc5603_start_stream(struct gc5603 *gc5603) +{ + int ret; + + ret = gc5603_write_array(gc5603->client, gc5603->cur_mode->reg_list); + if (ret) + return ret; + + usleep_range(1000, 1100); + + + gc5603_write_reg(gc5603->client, 0x0a70,gc5603_REG_VALUE_08BIT, 0x00); + gc5603_write_reg(gc5603->client, 0x0080,gc5603_REG_VALUE_08BIT, 0x02); + gc5603_write_reg(gc5603->client, 0x0a67,gc5603_REG_VALUE_08BIT, 0x00); + + + + + /* In case these controls are set before streaming */ + mutex_unlock(&gc5603->mutex); + ret = __v4l2_ctrl_handler_setup(&gc5603->ctrl_handler); + mutex_lock(&gc5603->mutex); + + ret = gc5603_set_flip(gc5603, gc5603->flip); + if (ret) + return ret; + return gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_STREAMING); +} + +static int __gc5603_stop_stream(struct gc5603 *gc5603) +{ + return gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_SW_STANDBY); +} + +static void gc5603_get_module_inf(struct gc5603 *gc5603, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, GC5603_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, gc5603->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, gc5603->len_name, sizeof(inf->base.lens)); +} + +static int gc5603_get_channel_info(struct gc5603 *gc5603, struct rkmodule_channel_info *ch_info) +{ + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + ch_info->vc = gc5603->cur_mode->vc[ch_info->index]; + ch_info->width = gc5603->cur_mode->width; + ch_info->height = gc5603->cur_mode->height; + ch_info->bus_fmt = gc5603->cur_mode->bus_fmt; + return 0; +} + +static void gc5603_set_awb_cfg(struct gc5603 *gc5603, + struct rkmodule_awb_cfg *cfg) +{ + mutex_lock(&gc5603->mutex); + memcpy(&gc5603->awb_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&gc5603->mutex); +} + +static void gc5603_set_lsc_cfg(struct gc5603 *gc5603, + struct rkmodule_lsc_cfg *cfg) +{ + mutex_lock(&gc5603->mutex); + memcpy(&gc5603->lsc_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&gc5603->mutex); +} + +static long gc5603_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + long ret = 0; + struct rkmodule_hdr_cfg *hdr_cfg; + u32 stream = 0; + struct rkmodule_channel_info *ch_info; + + switch (cmd) { + case RKMODULE_GET_HDR_CFG: + hdr_cfg = (struct rkmodule_hdr_cfg *)arg; + hdr_cfg->esp.mode = HDR_NORMAL_VC; + hdr_cfg->hdr_mode = gc5603->cur_mode->hdr_mode; + break; + case RKMODULE_SET_HDR_CFG: + case RKMODULE_SET_CONVERSION_GAIN: + break; + case RKMODULE_GET_MODULE_INFO: + gc5603_get_module_inf(gc5603, (struct rkmodule_inf *)arg); + break; + case RKMODULE_AWB_CFG: + gc5603_set_awb_cfg(gc5603, (struct rkmodule_awb_cfg *)arg); + break; + case RKMODULE_LSC_CFG: + gc5603_set_lsc_cfg(gc5603, (struct rkmodule_lsc_cfg *)arg); + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_STREAMING); + else + ret = gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_SW_STANDBY); + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = gc5603_get_channel_info(gc5603, ch_info); + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} + +#ifdef CONFIG_COMPAT +static long gc5603_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *awb_cfg; + struct rkmodule_lsc_cfg *lsc_cfg; + struct rkmodule_hdr_cfg *hdr; + long ret = 0; + u32 cg = 0; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = gc5603_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + awb_cfg = kzalloc(sizeof(*awb_cfg), GFP_KERNEL); + if (!awb_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(awb_cfg, up, sizeof(*awb_cfg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, awb_cfg); + kfree(awb_cfg); + break; + case RKMODULE_LSC_CFG: + lsc_cfg = kzalloc(sizeof(*lsc_cfg), GFP_KERNEL); + if (!lsc_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(lsc_cfg, up, sizeof(*lsc_cfg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, lsc_cfg); + kfree(lsc_cfg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = gc5603_ioctl(sd, cmd, hdr); + if (!ret) + ret = copy_to_user(up, hdr, sizeof(*hdr)); + kfree(hdr); + break; + case RKMODULE_SET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(hdr, up, sizeof(*hdr)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, hdr); + kfree(hdr); + break; + case RKMODULE_SET_CONVERSION_GAIN: + ret = copy_from_user(&cg, up, sizeof(cg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, &cg); + break; + case RKMODULE_SET_QUICK_STREAM: + ret = copy_from_user(&stream, up, sizeof(u32)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, &stream); + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} +#endif + +static int gc5603_s_stream(struct v4l2_subdev *sd, int on) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct i2c_client *client = gc5603->client; + int ret = 0; + + mutex_lock(&gc5603->mutex); + on = !!on; + if (on == gc5603->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __gc5603_start_stream(gc5603); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __gc5603_stop_stream(gc5603); + pm_runtime_put(&client->dev); + } + + gc5603->streaming = on; + +unlock_and_return: + mutex_unlock(&gc5603->mutex); + return 0; +} + +static int gc5603_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + + mutex_lock(&gc5603->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&gc5603->mutex); + + return 0; +} + +static int gc5603_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + u32 val = 0; + + if (mode->hdr_mode == NO_HDR) + val = 1 << (GC5603_LANES - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + config->type = V4L2_MBUS_CSI2_DPHY; + config->flags = val; + return 0; +} + +static int gc5603_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = gc5603->supported_modes[code->index].bus_fmt; + return 0; +} + +static int gc5603_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + + if (fse->index >= gc5603->cfg_num) + return -EINVAL; + + if (fse->code != gc5603->supported_modes[code->index].bus_fmt) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + return 0; +} + +#define DST_WIDTH 2720 +#define DST_HEIGHT 1616 + +/* + * The resolution of the driver configuration needs to be exactly + * the same as the current output resolution of the sensor, + * the input width of the isp needs to be 16 aligned, + * the input height of the isp needs to be 8 aligned. + * Can be cropped to standard resolution by this function, + * otherwise it will crop out strange resolution according + * to the alignment rules. + */ +static int gc5603_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + sel->r.left =120; + sel->r.width = DST_WIDTH; + sel->r.top = 25; + sel->r.height = DST_HEIGHT; + return 0; + } + return -EINVAL; +} + +static int gc5603_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + + if (fie->index >= gc5603->cfg_num) + return -EINVAL; + + fie->code = supported_modes[fie->index].bus_fmt; + fie->width = supported_modes[fie->index].width; + fie->height = supported_modes[fie->index].height; + fie->interval = supported_modes[fie->index].max_fps; + fie->reserved[0] = supported_modes[fie->index].hdr_mode; + return 0; +} + +static int gc5603_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&gc5603->mutex); + + mode = gc5603_find_best_fit(gc5603, fmt); + fmt->format.code = mode->bus_fmt; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&gc5603->mutex); + return -ENOTTY; +#endif + } else { + gc5603->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(gc5603->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(gc5603->vblank, vblank_def, + GC5603_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&gc5603->mutex); + return 0; +} + +static int gc5603_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + + mutex_lock(&gc5603->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&gc5603->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->bus_fmt; + fmt->format.field = V4L2_FIELD_NONE; + + /* format info: width/height/data type/virctual channel */ + if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR) + fmt->reserved[0] = mode->vc[fmt->pad]; + else + fmt->reserved[0] = mode->vc[PAD0]; + + } + mutex_unlock(&gc5603->mutex); + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int gc5603_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct gc5603_mode *def_mode = &supported_modes[0]; + + mutex_lock(&gc5603->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = GC5603_MEDIA_BUS_FMT; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&gc5603->mutex); + /* No crop or compose */ + return 0; +} +#endif + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops gc5603_internal_ops = { + .open = gc5603_open, +}; +#endif + +static int gc5603_s_power(struct v4l2_subdev *sd, int on) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct i2c_client *client = gc5603->client; + int ret = 0; + + mutex_lock(&gc5603->mutex); + + /* If the power state is not modified - no work to do. */ + if (gc5603->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + gc5603->power_on = true; + } else { + pm_runtime_put(&client->dev); + gc5603->power_on = false; + } + +unlock_and_return: + mutex_unlock(&gc5603->mutex); + + return ret; +} + +static const struct v4l2_subdev_core_ops gc5603_core_ops = { + .s_power = gc5603_s_power, + .ioctl = gc5603_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = gc5603_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops gc5603_video_ops = { + .s_stream = gc5603_s_stream, + .g_frame_interval = gc5603_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops gc5603_pad_ops = { + .enum_mbus_code = gc5603_enum_mbus_code, + .enum_frame_size = gc5603_enum_frame_sizes, + .enum_frame_interval = gc5603_enum_frame_interval, + .get_fmt = gc5603_get_fmt, + .set_fmt = gc5603_set_fmt, + .get_selection = gc5603_get_selection, + .get_mbus_config = gc5603_g_mbus_config, +}; + +static const struct v4l2_subdev_ops gc5603_subdev_ops = { + .core = &gc5603_core_ops, + .video = &gc5603_video_ops, + .pad = &gc5603_pad_ops, +}; + +static int gc5603_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + __gc5603_power_on(gc5603); + return 0; +} + +static int gc5603_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + __gc5603_power_off(gc5603); + return 0; +} + +static const struct dev_pm_ops gc5603_pm_ops = { + SET_RUNTIME_PM_OPS(gc5603_runtime_suspend, + gc5603_runtime_resume, NULL) +}; + +static int gc5603_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct gc5603 *gc5603; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + gc5603 = devm_kzalloc(dev, sizeof(*gc5603), GFP_KERNEL); + if (!gc5603) + return -ENOMEM; + + gc5603->client = client; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &gc5603->module_index); + if (ret) { + dev_warn(dev, "could not get module index!\n"); + gc5603->module_index = 0; + } + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &gc5603->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &gc5603->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &gc5603->len_name); + if (ret) { + dev_err(dev, + "could not get module information!\n"); + return -EINVAL; + } + + gc5603->xvclk = devm_clk_get(&client->dev, "xvclk"); + if (IS_ERR(gc5603->xvclk)) { + dev_err(&client->dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + gc5603->pwren_gpio = devm_gpiod_get(dev, "pwren", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->pwdn_gpio)) + dev_warn(dev, "Failed to get pwren-gpios\n"); + + gc5603->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->reset_gpio)) + dev_info(dev, "Failed to get reset-gpios, maybe no used\n"); + + gc5603->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->pwdn_gpio)) + dev_warn(dev, "Failed to get power-gpios\n"); + + ret = gc5603_configure_regulators(gc5603); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + ret = gc5603_parse_of(gc5603); + if (ret != 0) + return -EINVAL; + + gc5603->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(gc5603->pinctrl)) { + gc5603->pins_default = + pinctrl_lookup_state(gc5603->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(gc5603->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + gc5603->pins_sleep = + pinctrl_lookup_state(gc5603->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(gc5603->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } else { + dev_err(dev, "no pinctrl\n"); + } + + mutex_init(&gc5603->mutex); + + sd = &gc5603->subdev; + v4l2_i2c_subdev_init(sd, client, &gc5603_subdev_ops); + ret = gc5603_initialize_controls(gc5603); + if (ret) + goto err_destroy_mutex; + + ret = __gc5603_power_on(gc5603); + if (ret) + goto err_free_handler; + + usleep_range(3000, 4000); + ret = gc5603_check_sensor_id(gc5603, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &gc5603_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + gc5603->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &gc5603->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(gc5603->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + gc5603->module_index, facing, + GC5603_NAME, dev_name(sd->dev)); + + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + +err_power_off: + __gc5603_power_off(gc5603); +err_free_handler: + v4l2_ctrl_handler_free(&gc5603->ctrl_handler); + +err_destroy_mutex: + mutex_destroy(&gc5603->mutex); + return ret; +} + +static int gc5603_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&gc5603->ctrl_handler); + mutex_destroy(&gc5603->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __gc5603_power_off(gc5603); + pm_runtime_set_suspended(&client->dev); + return 0; +} + +static const struct i2c_device_id gc5603_match_id[] = { + { "gc5603", 0 }, + { }, +}; + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id gc5603_of_match[] = { + { .compatible = "galaxycore,gc5603" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gc5603_of_match); +#endif + +static struct i2c_driver gc5603_i2c_driver = { + .driver = { + .name = GC5603_NAME, + .pm = &gc5603_pm_ops, + .of_match_table = of_match_ptr(gc5603_of_match), + }, + .probe = &gc5603_probe, + .remove = &gc5603_remove, + .id_table = gc5603_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&gc5603_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&gc5603_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("GC2035 CMOS Image Sensor driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/Linux-Kernel.si4project/Backup/gc5603(7712).c b/Linux-Kernel.si4project/Backup/gc5603(7712).c new file mode 100755 index 0000000000000..06a751236f7f5 --- /dev/null +++ b/Linux-Kernel.si4project/Backup/gc5603(7712).c @@ -0,0 +1,1612 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * gc5603 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + * V0.0X01.0X03 add enum_frame_interval function. + * V0.0X01.0X04 add quick stream on/off + * V0.0X01.0X05 add function g_mbus_config + * V0.0X01.0X06 + * 1. add 2lane support. + * 2. add some debug info. + * 3. adjust gc5603_g_mbus_config function. + * V0.0X01.0X07 support get channel info + * V0.0X01.0X08 + * 1. default support 2lane full 30fps. + * 2. default support rk otp spec. + * V0.0X01.0X09 adjust supply sequence to suit spec + */ +//#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x01) +#define GC5603_NAME "gc5603" + +#define MIPI_FREQ_848M 423000000 + +#define GC5603_PAGE_SELECT 0xFE + +#define GC5603_REG_CHIP_ID_H 0x03F0 +#define GC5603_REG_CHIP_ID_L 0x03F1 + +#define GC5603_REG_EXP_H 0x0202 +#define GC5603_REG_EXP_L 0x0203 + +#define GC5603_REG_VTS_H 0x0340 +#define GC5603_REG_VTS_L 0x0341 + +#define GC5603_REG_CTRL_MODE 0x0100 +#define GC5603_MODE_SW_STANDBY 0x00 +#define GC5603_MODE_STREAMING 0x09 + +#define REG_NULL 0xFFFF + +#define GC5603_CHIP_ID 0x5603 + +#define GC5603_VTS_MAX 0x7fff +#define GC5603_HTS_MAX 0xFFF + +#define GC5603_EXPOSURE_MAX 0x3FFF +#define GC5603_EXPOSURE_MIN 1 +#define GC5603_EXPOSURE_STEP 1 + +#define GC5603_GAIN_MIN 64 +#define GC5603_GAIN_MAX 0xffff +#define GC5603_GAIN_STEP 1 +#define GC5603_GAIN_DEFAULT 64 + +#define gc5603_REG_VALUE_08BIT 1 +#define gc5603_REG_VALUE_16BIT 2 +#define gc5603_REG_VALUE_24BIT 3 + + +#define GC5603_LANES 2 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + + + +#define GC5603_FLIP_MIRROR_REG 0x0101 + +#define GC_MIRROR_BIT_MASK BIT(0) +#define GC_FLIP_BIT_MASK BIT(1) + +#define GC5603_XVCLK_FREQ_24M 24000000 +#define GC5603_XVCLK_FREQ_27M 27000000 + +static const char * const gc5603_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + +#define GC5603_NUM_SUPPLIES ARRAY_SIZE(gc5603_supply_names) + +#define to_gc5603(sd) container_of(sd, struct gc5603, subdev) + +struct regval { + u16 addr; + u8 val; +}; + +struct gc5603_mode { + u32 bus_fmt + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; + u32 hdr_mode; + u32 vc[PAD_MAX]; + u32 xvclk; +}; + +struct gc5603 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct gpio_desc *pwren_gpio; + struct regulator_bulk_data supplies[GC5603_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *h_flip; + struct v4l2_ctrl *v_flip; + struct mutex mutex; + bool streaming; + bool power_on; + const struct gc5603_mode *cur_mode; + unsigned int lane_num; + unsigned int cfg_num; + unsigned int pixel_rate; + + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + struct rkmodule_awb_cfg awb_cfg; + struct rkmodule_lsc_cfg lsc_cfg; + u32 flip; +}; + + +static const struct regval gc5603_2960x1666_regs_2lane[] = { +//version 1.3 +//mclk 27Mhz +//mipi 2 lane 846Mbps/lane +//vts = 1750 ,row_time=19.05us +//window 2960x1666 +//BGGR + {0x03fe, 0xf0}, + {0x03fe, 0x00}, + {0x03fe, 0x10}, + {0x03fe, 0x00}, + {0x0202, 0x01}, + {0x0203, 0x50}, + {0x0a38, 0x02}, + {0x0a38, 0x03}, + {0x0a20, 0x07}, + {0x061b, 0x03}, + {0x061c, 0x50}, + {0x061d, 0x05}, + {0x061e, 0x70}, + {0x061f, 0x03}, + {0x0a21, 0x08}, + {0x0a34, 0x40}, + {0x0a35, 0x11}, + {0x0a36, 0x5e}, + {0x0a37, 0x03}, + {0x0314, 0x50}, + {0x0315, 0x32}, + {0x031c, 0xce}, + {0x0219, 0x47}, + {0x0342, 0x04}, + {0x0343, 0xb0}, + {0x0340, 0x06}, + {0x0341, 0xd6}, + {0x0345, 0x02}, + {0x0347, 0x02}, + {0x0348, 0x0b}, + {0x0349, 0x98}, + {0x034a, 0x06}, + {0x034b, 0x8a}, + {0x0094, 0x0b}, + {0x0095, 0x90}, + {0x0096, 0x06}, + {0x0097, 0x82}, + {0x0099, 0x04}, + {0x009b, 0x04}, + {0x060c, 0x01}, + {0x060e, 0xd2}, + {0x060f, 0x05}, + {0x070c, 0x01}, + {0x070e, 0xd2}, + {0x070f, 0x05}, + {0x0909, 0x07}, + {0x0902, 0x04}, + {0x0904, 0x0b}, + {0x0907, 0x54}, + {0x0908, 0x06}, + {0x0903, 0x9d}, + {0x072a, 0x1c},//18 + {0x072b, 0x1c},//18 + {0x0724, 0x2b}, + {0x0727, 0x2b}, + {0x1466, 0x18}, + {0x1467, 0x08}, + {0x1468, 0x10}, + {0x1469, 0x80}, + {0x146a, 0xe8},//b8 + {0x1412, 0x20}, + {0x0707, 0x07}, + {0x0737, 0x0f}, + {0x0704, 0x01}, + {0x0706, 0x03}, + {0x0716, 0x03}, + {0x0708, 0xc8}, + {0x0718, 0xc8}, + {0x061a, 0x02}, + {0x1430, 0x80}, + {0x1407, 0x10}, + {0x1408, 0x16}, + {0x1409, 0x03}, + {0x1438, 0x01}, + {0x02ce, 0x03}, + {0x0245, 0xc9}, + {0x023a, 0x08},//3B + {0x02cd, 0x88}, + {0x0612, 0x02}, + {0x0613, 0xc7}, + {0x0243, 0x03},//06 + {0x0089, 0x03}, + {0x0002, 0xab}, + {0x0040, 0xa3}, + {0x0075, 0x64},//64 + {0x0004, 0x0f}, + {0x0053, 0x0a}, + {0x0205, 0x0c}, + + //auto_load}, + {0x0a67, 0x80}, + {0x0a54, 0x0e}, + {0x0a65, 0x10}, + {0x0a98, 0x04}, + {0x05be, 0x00}, + {0x05a9, 0x01}, + {0x0023, 0x00}, + {0x0022, 0x00}, + {0x0025, 0x00}, + {0x0024, 0x00}, + {0x0028, 0x0b}, + {0x0029, 0x98}, + {0x002a, 0x06}, + {0x002b, 0x86}, + {0x0a83, 0xe0}, + {0x0a72, 0x02}, + {0x0a73, 0x60}, + {0x0a75, 0x41}, + {0x0a70, 0x03}, + {0x0a5a, 0x80}, + {0x0181, 0x30}, + {0x0182, 0x05}, + {0x0185, 0x01}, + {0x0180, 0x46}, + {0x0100, 0x08}, + {0x010d, 0x74}, + {0x010e, 0x0e}, + {0x0113, 0x02}, + {0x0114, 0x01}, + {0x0115, 0x10}, + //{0x0a70, 0x00}, + //{0x0080, 0x02}, + //{0x0a67, 0x00}, + {0x0052, 0x02}, + {0x0076, 0x01}, + {0x021a, 0x10}, + {0x0049, 0x0f}, + {0x004a, 0x3c}, + {0x004b, 0x00}, + {0x0430, 0x25}, + {0x0431, 0x25}, + {0x0432, 0x25}, + {0x0433, 0x25}, + {0x0434, 0x59}, + {0x0435, 0x59}, + {0x0436, 0x59}, + {0x0437, 0x59}, + + {0x0100, 0x09}, + {REG_NULL, 0x00}, +}; + +static const struct gc5603_mode supported_modes[] = { + { + .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10 + .width = 2960, + .height = 1666, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x6ce, + .hts_def = 0x0C80, + .vts_def = 0x06D6, + .reg_list = gc5603_2960x1666_regs_2lane, + .hdr_mode = NO_HDR, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = GC5603_XVCLK_FREQ_24M + }, +}; + +static const s64 link_freq_menu_items[] = { + MIPI_FREQ_848M +}; +static int gc5603_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int gc5603_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = gc5603_write_reg(client, regs[i].addr, + gc5603_REG_VALUE_08BIT, regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int gc5603_read_reg(struct i2c_client *client, u16 reg, unsigned int len, + u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + + return 0; + dev_info(&client->dev, + "gc5603 read reg(0x%x val:0x%x) \n", reg, *val); +} +static int gc5603_get_reso_dist(const struct gc5603_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct gc5603_mode * +gc5603_find_best_fit(struct gc5603 *gc5603, struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < gc5603->cfg_num; i++) { + dist = gc5603_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist <= cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static uint8_t regValTable[26][7] = { + //0614, 0615, 0225, 1467 1468, 00b8, 00b9 + { 0x00, 0x00, 0x04, 0x15, 0x15, 0x01, 0x00}, + { 0x90, 0x02, 0x04, 0x15, 0x15, 0x01, 0x0A}, + { 0x00, 0x00, 0x00, 0x15, 0x15, 0x01, 0x12}, + { 0x90, 0x02, 0x00, 0x15, 0x15, 0x01, 0x20}, + { 0x01, 0x00, 0x00, 0x15, 0x15, 0x01, 0x30}, + { 0x91, 0x02, 0x00, 0x15, 0x15, 0x02, 0x05}, + { 0x02, 0x00, 0x00, 0x15, 0x15, 0x02, 0x19}, + { 0x92, 0x02, 0x00, 0x16, 0x16, 0x02, 0x3F}, + { 0x03, 0x00, 0x00, 0x16, 0x16, 0x03, 0x20}, + { 0x93, 0x02, 0x00, 0x17, 0x17, 0x04, 0x0A}, + { 0x00, 0x00, 0x01, 0x18, 0x18, 0x05, 0x02}, + { 0x90, 0x02, 0x01, 0x19, 0x19, 0x05, 0x39}, + { 0x01, 0x00, 0x01, 0x19, 0x19, 0x06, 0x3C}, + { 0x91, 0x02, 0x01, 0x19, 0x19, 0x08, 0x0D}, + { 0x02, 0x00, 0x01, 0x1a, 0x1a, 0x09, 0x21}, + { 0x92, 0x02, 0x01, 0x1a, 0x1a, 0x0B, 0x0F}, + { 0x03, 0x00, 0x01, 0x1c, 0x1c, 0x0D, 0x17}, + { 0x93, 0x02, 0x01, 0x1c, 0x1c, 0x0F, 0x33}, + { 0x04, 0x00, 0x01, 0x1d, 0x1d, 0x12, 0x30}, + { 0x94, 0x02, 0x01, 0x1d, 0x1d, 0x16, 0x10}, + { 0x05, 0x00, 0x01, 0x1e, 0x1e, 0x1A, 0x19}, + { 0x95, 0x02, 0x01, 0x1e, 0x1e, 0x1F, 0x13}, + { 0x06, 0x00, 0x01, 0x20, 0x20, 0x25, 0x08}, + { 0x96, 0x02, 0x01, 0x20, 0x20, 0x2C, 0x03}, + { 0xb6, 0x04, 0x01, 0x20, 0x20, 0x34, 0x0F}, + { 0x86, 0x06, 0x01, 0x20, 0x20, 0x3D, 0x3D}, +}; + +static uint32_t gain_level_table[27] = { + 64, + 74, + 82, + 96, + 112, + 133, + 153, + 191, + 224, + 266, + 322, + 377, + 444, + 525, + 609, + 719, + 855, + 1011, + 1200, + 1424, + 1689, + 2003, + + 2376, + 2819, + + 3343, + 3965, + 0xffffffff, +}; +//static int total = sizeof(gain_level_table) / sizeof(uint32_t); + +static int gc5603_set_gain(struct gc5603 *gc5603, u32 gain) +{ + int ret; + uint16_t i = 0; + uint16_t total = 0; + uint16_t temp = 0; + + + for (i = 0; i < total; i++) { + if ((gain_level_table[i] <= gain) && (gain < gain_level_table[i+1])) + break; + } + + if((gain>3965)||(gain==3965)) + i =25; + + + ret = gc5603_write_reg(gc5603->client, 0x031d,gc5603_REG_VALUE_08BIT, 0x2d); + ret = gc5603_write_reg(gc5603->client, 0x0614,gc5603_REG_VALUE_08BIT,regValTable[i][0]); + ret = gc5603_write_reg(gc5603->client, 0x0615,gc5603_REG_VALUE_08BIT,regValTable[i][1]); + ret = gc5603_write_reg(gc5603->client, 0x0225,gc5603_REG_VALUE_08BIT,regValTable[i][2]); + + ret = gc5603_write_reg(gc5603->client, 0x031d,gc5603_REG_VALUE_08BIT, 0x28); + ret = gc5603_write_reg(gc5603->client, 0x1467,gc5603_REG_VALUE_08BIT,regValTable[i][3]); + ret = gc5603_write_reg(gc5603->client, 0x1468,gc5603_REG_VALUE_08BIT,regValTable[i][4]); + ret = gc5603_write_reg(gc5603->client, 0x00b8,gc5603_REG_VALUE_08BIT,regValTable[i][5]); + ret = gc5603_write_reg(gc5603->client, 0x00b9,gc5603_REG_VALUE_08BIT,regValTable[i][6]); + + + temp = 64 * gain / gain_level_table[i]; + + //dev_warn(&client->dev, "gc5603_set_gain gain=%d,i=%d,temp=%d \n", gain, i, temp); + + ret |= gc5603_write_reg(gc5603->client, 0x0064,gc5603_REG_VALUE_08BIT,(temp >> 6)); + ret |= gc5603_write_reg(gc5603->client, 0x0065,gc5603_REG_VALUE_08BIT,((temp&0x3f) << 2) ); + + return ret; +} + +static int gc5603_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc5603 *gc5603 = container_of(ctrl->handler, + struct gc5603, ctrl_handler); + struct i2c_client *client = gc5603->client; + s64 max; + int ret = 0; + u32 vts = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = gc5603->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(gc5603->exposure, + gc5603->exposure->minimum, max, + gc5603->exposure->step, + gc5603->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + + #if 1 + ret = gc5603_write_reg(gc5603->client, GC5603_REG_EXP_H,gc5603_REG_VALUE_08BIT, + (ctrl->val >> 8)); + ret |= gc5603_write_reg(gc5603->client, GC5603_REG_EXP_L,gc5603_REG_VALUE_08BIT, + ctrl->val & 0xff); + #endif + + break; + case V4L2_CID_ANALOGUE_GAIN: + gc5603_set_gain(gc5603, ctrl->val); + break; + case V4L2_CID_VBLANK: + vts = ctrl->val + gc5603->cur_mode->height; + + #if 1 + ret = gc5603_write_reg(gc5603->client, GC5603_REG_VTS_H,gc5603_REG_VALUE_08BIT,(vts >> 8)); + ret |= gc5603_write_reg(gc5603->client, GC5603_REG_VTS_L, gc5603_REG_VALUE_08BIT,vts & 0xff); + #endif + break; + case V4L2_CID_HFLIP: + if (ctrl->val) + gc5603->flip |= GC_MIRROR_BIT_MASK; + else + gc5603->flip &= ~GC_MIRROR_BIT_MASK; + break; + case V4L2_CID_VFLIP: + if (ctrl->val) + gc5603->flip |= GC_FLIP_BIT_MASK; + else + gc5603->flip &= ~GC_FLIP_BIT_MASK; + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + return ret; +} + +static const struct v4l2_ctrl_ops gc5603_ctrl_ops = { + .s_ctrl = gc5603_set_ctrl, +}; + +static int gc5603_configure_regulators(struct gc5603 *gc5603) +{ + unsigned int i; + + for (i = 0; i < GC5603_NUM_SUPPLIES; i++) + gc5603->supplies[i].supply = gc5603_supply_names[i]; + + return devm_regulator_bulk_get(&gc5603->client->dev, + GC5603_NUM_SUPPLIES, + gc5603->supplies); +} + +static int gc5603_parse_of(struct gc5603 *gc5603) +{ + struct device *dev = &gc5603->client->dev; + struct device_node *endpoint; + struct fwnode_handle *fwnode; + int rval; + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "Failed to get endpoint\n"); + return -EINVAL; + } + fwnode = of_fwnode_handle(endpoint); + rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0); + if (rval <= 0) { + dev_warn(dev, " Get mipi lane num failed!\n"); + return -1; + } + + gc5603->lane_num = rval; + if (2 == gc5603->lane_num) { + gc5603->cur_mode = &supported_modes[0]; + gc5603->cfg_num = ARRAY_SIZE(supported_modes); + + /*pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + gc5603->pixel_rate = MIPI_FREQ_848M * 2U * (gc5603->lane_num) / 10U; + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + gc5603->lane_num, gc5603->pixel_rate); + } else { + dev_info(dev, "gc5603 can not support the lane num(%d)\n", gc5603->lane_num); + } + return 0; +} + +static int gc5603_initialize_controls(struct gc5603 *gc5603) +{ + const struct gc5603_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &gc5603->ctrl_handler; + mode = gc5603->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &gc5603->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, gc5603->pixel_rate, 1, gc5603->pixel_rate); + + h_blank = mode->hts_def - mode->width; + gc5603->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (gc5603->hblank) + gc5603->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + gc5603->vblank = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + GC5603_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + gc5603->exposure = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_EXPOSURE, + GC5603_EXPOSURE_MIN, + exposure_max, + GC5603_EXPOSURE_STEP, + mode->exp_def); + + gc5603->anal_gain = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + GC5603_GAIN_MIN, + GC5603_GAIN_MAX, + GC5603_GAIN_STEP, + GC5603_GAIN_DEFAULT); + + gc5603->h_flip = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + gc5603->v_flip = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + gc5603->flip = 0; + + if (handler->error) { + ret = handler->error; + dev_err(&gc5603->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + gc5603->subdev.ctrl_handler = handler; + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 gc5603_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, GC5603_XVCLK_FREQ_24M / 1000 / 1000); +} + +static int __gc5603_power_on(struct gc5603 *gc5603) +{ + int ret; + u32 delay_us; + struct device *dev = &gc5603->client->dev; + + if (!IS_ERR_OR_NULL(gc5603->pins_default)) { + ret = pinctrl_select_state(gc5603->pinctrl, + gc5603->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + ret = clk_set_rate(gc5603->xvclk, GC5603_XVCLK_FREQ_24M); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(gc5603->xvclk) != GC5603_XVCLK_FREQ_24M) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(gc5603->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 0); + + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 0); + + usleep_range(500, 1000); + ret = regulator_bulk_enable(GC5603_NUM_SUPPLIES, gc5603->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + if (!IS_ERR(gc5603->pwren_gpio)) + gpiod_set_value_cansleep(gc5603->pwren_gpio, 1); + + usleep_range(1000, 1100); + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 1); + usleep_range(100, 150); + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = gc5603_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + return 0; + +disable_clk: + clk_disable_unprepare(gc5603->xvclk); + return ret; +} + +static void __gc5603_power_off(struct gc5603 *gc5603) +{ + int ret; + struct device *dev = &gc5603->client->dev; + + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 0); + clk_disable_unprepare(gc5603->xvclk); + + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 0); + + if (!IS_ERR_OR_NULL(gc5603->pins_sleep)) { + ret = pinctrl_select_state(gc5603->pinctrl, + gc5603->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + regulator_bulk_disable(GC5603_NUM_SUPPLIES, gc5603->supplies); + if (!IS_ERR(gc5603->pwren_gpio)) + gpiod_set_value_cansleep(gc5603->pwren_gpio, 0); + +} + + static int gc5603_check_sensor_id(struct gc5603 *gc5603, + struct i2c_client *client) +{ + struct device *dev = &gc5603->client->dev; + u16 id = 0; + u32 reg_H = 0; + u32 reg_L = 0; + int ret; + + ret = gc5603_read_reg(client, GC5603_REG_CHIP_ID_H, + gc5603_REG_VALUE_08BIT, ®_H); + ret |= gc5603_read_reg(client, GC5603_REG_CHIP_ID_L, + gc5603_REG_VALUE_08BIT, ®_L); + + id = ((reg_H << 8) & 0xff00) | (reg_L & 0xff); + if (!(reg_H == (GC5603_CHIP_ID >> 8) || reg_L == (GC5603_CHIP_ID & 0xff))) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + dev_info(dev, "detected gc%04x sensor\n", id); + return 0; +} + +static int gc5603_set_flip(struct gc5603 *gc5603, u8 mode) +{ +u32 match_reg = 0; + + gc5603_read_reg(gc5603->client, GC5603_FLIP_MIRROR_REG,gc5603_REG_VALUE_08BIT,&match_reg); + + if (mode == GC_FLIP_BIT_MASK) { + match_reg |= GC_FLIP_BIT_MASK; + match_reg &= ~GC_MIRROR_BIT_MASK; + } else if (mode == GC_MIRROR_BIT_MASK) { + match_reg |= GC_MIRROR_BIT_MASK; + match_reg &= ~GC_FLIP_BIT_MASK; + } else if (mode == (GC_MIRROR_BIT_MASK | + GC_FLIP_BIT_MASK)) { + match_reg |= GC_FLIP_BIT_MASK; + match_reg |= GC_MIRROR_BIT_MASK; + } else { + match_reg &= ~GC_FLIP_BIT_MASK; + match_reg &= ~GC_MIRROR_BIT_MASK; + } + return gc5603_write_reg(gc5603->client, GC5603_FLIP_MIRROR_REG,gc5603_REG_VALUE_08BIT ,match_reg); +} + +static int __gc5603_start_stream(struct gc5603 *gc5603) +{ + int ret; + + ret = gc5603_write_array(gc5603->client, gc5603->cur_mode->reg_list); + if (ret) + return ret; + + usleep_range(1000, 1100); + + + gc5603_write_reg(gc5603->client, 0x0a70,gc5603_REG_VALUE_08BIT, 0x00); + gc5603_write_reg(gc5603->client, 0x0080,gc5603_REG_VALUE_08BIT, 0x02); + gc5603_write_reg(gc5603->client, 0x0a67,gc5603_REG_VALUE_08BIT, 0x00); + + + + + /* In case these controls are set before streaming */ + mutex_unlock(&gc5603->mutex); + ret = __v4l2_ctrl_handler_setup(&gc5603->ctrl_handler); + mutex_lock(&gc5603->mutex); + + ret = gc5603_set_flip(gc5603, gc5603->flip); + if (ret) + return ret; + return gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_STREAMING); +} + +static int __gc5603_stop_stream(struct gc5603 *gc5603) +{ + return gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_SW_STANDBY); +} + +static void gc5603_get_module_inf(struct gc5603 *gc5603, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, GC5603_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, gc5603->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, gc5603->len_name, sizeof(inf->base.lens)); +} + +static int gc5603_get_channel_info(struct gc5603 *gc5603, struct rkmodule_channel_info *ch_info) +{ + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + ch_info->vc = gc5603->cur_mode->vc[ch_info->index]; + ch_info->width = gc5603->cur_mode->width; + ch_info->height = gc5603->cur_mode->height; + ch_info->bus_fmt = gc5603->cur_mode->bus_fmt; + return 0; +} + +static void gc5603_set_awb_cfg(struct gc5603 *gc5603, + struct rkmodule_awb_cfg *cfg) +{ + mutex_lock(&gc5603->mutex); + memcpy(&gc5603->awb_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&gc5603->mutex); +} + +static void gc5603_set_lsc_cfg(struct gc5603 *gc5603, + struct rkmodule_lsc_cfg *cfg) +{ + mutex_lock(&gc5603->mutex); + memcpy(&gc5603->lsc_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&gc5603->mutex); +} + +static long gc5603_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + long ret = 0; + struct rkmodule_hdr_cfg *hdr_cfg; + u32 stream = 0; + struct rkmodule_channel_info *ch_info; + + switch (cmd) { + case RKMODULE_GET_HDR_CFG: + hdr_cfg = (struct rkmodule_hdr_cfg *)arg; + hdr_cfg->esp.mode = HDR_NORMAL_VC; + hdr_cfg->hdr_mode = gc5603->cur_mode->hdr_mode; + break; + case RKMODULE_SET_HDR_CFG: + case RKMODULE_SET_CONVERSION_GAIN: + break; + case RKMODULE_GET_MODULE_INFO: + gc5603_get_module_inf(gc5603, (struct rkmodule_inf *)arg); + break; + case RKMODULE_AWB_CFG: + gc5603_set_awb_cfg(gc5603, (struct rkmodule_awb_cfg *)arg); + break; + case RKMODULE_LSC_CFG: + gc5603_set_lsc_cfg(gc5603, (struct rkmodule_lsc_cfg *)arg); + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_STREAMING); + else + ret = gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_SW_STANDBY); + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = gc5603_get_channel_info(gc5603, ch_info); + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} + +#ifdef CONFIG_COMPAT +static long gc5603_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *awb_cfg; + struct rkmodule_lsc_cfg *lsc_cfg; + struct rkmodule_hdr_cfg *hdr; + long ret = 0; + u32 cg = 0; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = gc5603_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + awb_cfg = kzalloc(sizeof(*awb_cfg), GFP_KERNEL); + if (!awb_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(awb_cfg, up, sizeof(*awb_cfg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, awb_cfg); + kfree(awb_cfg); + break; + case RKMODULE_LSC_CFG: + lsc_cfg = kzalloc(sizeof(*lsc_cfg), GFP_KERNEL); + if (!lsc_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(lsc_cfg, up, sizeof(*lsc_cfg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, lsc_cfg); + kfree(lsc_cfg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = gc5603_ioctl(sd, cmd, hdr); + if (!ret) + ret = copy_to_user(up, hdr, sizeof(*hdr)); + kfree(hdr); + break; + case RKMODULE_SET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(hdr, up, sizeof(*hdr)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, hdr); + kfree(hdr); + break; + case RKMODULE_SET_CONVERSION_GAIN: + ret = copy_from_user(&cg, up, sizeof(cg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, &cg); + break; + case RKMODULE_SET_QUICK_STREAM: + ret = copy_from_user(&stream, up, sizeof(u32)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, &stream); + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} +#endif + +static int gc5603_s_stream(struct v4l2_subdev *sd, int on) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct i2c_client *client = gc5603->client; + int ret = 0; + + mutex_lock(&gc5603->mutex); + on = !!on; + if (on == gc5603->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __gc5603_start_stream(gc5603); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __gc5603_stop_stream(gc5603); + pm_runtime_put(&client->dev); + } + + gc5603->streaming = on; + +unlock_and_return: + mutex_unlock(&gc5603->mutex); + return 0; +} + +static int gc5603_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + + mutex_lock(&gc5603->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&gc5603->mutex); + + return 0; +} + +static int gc5603_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + u32 val = 0; + + if (mode->hdr_mode == NO_HDR) + val = 1 << (GC5603_LANES - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + config->type = V4L2_MBUS_CSI2_DPHY; + config->flags = val; + return 0; +} + +static int gc5603_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = gc5603->supported_modes[code->index].bus_fmt; + return 0; +} + +static int gc5603_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + + if (fse->index >= gc5603->cfg_num) + return -EINVAL; + + if (fse->code != gc5603->supported_modes[code->index].bus_fmt) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + return 0; +} + +#define DST_WIDTH 2720 +#define DST_HEIGHT 1616 + +/* + * The resolution of the driver configuration needs to be exactly + * the same as the current output resolution of the sensor, + * the input width of the isp needs to be 16 aligned, + * the input height of the isp needs to be 8 aligned. + * Can be cropped to standard resolution by this function, + * otherwise it will crop out strange resolution according + * to the alignment rules. + */ +static int gc5603_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + sel->r.left =120; + sel->r.width = DST_WIDTH; + sel->r.top = 25; + sel->r.height = DST_HEIGHT; + return 0; + } + return -EINVAL; +} + +static int gc5603_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + + if (fie->index >= gc5603->cfg_num) + return -EINVAL; + + fie->code = supported_modes[fie->index].bus_fmt; + fie->width = supported_modes[fie->index].width; + fie->height = supported_modes[fie->index].height; + fie->interval = supported_modes[fie->index].max_fps; + fie->reserved[0] = supported_modes[fie->index].hdr_mode; + return 0; +} + +static int gc5603_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&gc5603->mutex); + + mode = gc5603_find_best_fit(gc5603, fmt); + fmt->format.code = mode->bus_fmt; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&gc5603->mutex); + return -ENOTTY; +#endif + } else { + gc5603->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(gc5603->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(gc5603->vblank, vblank_def, + GC5603_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&gc5603->mutex); + return 0; +} + +static int gc5603_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + + mutex_lock(&gc5603->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&gc5603->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->bus_fmt; + fmt->format.field = V4L2_FIELD_NONE; + + /* format info: width/height/data type/virctual channel */ + if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR) + fmt->reserved[0] = mode->vc[fmt->pad]; + else + fmt->reserved[0] = mode->vc[PAD0]; + + } + mutex_unlock(&gc5603->mutex); + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int gc5603_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct gc5603_mode *def_mode = &supported_modes[0]; + + mutex_lock(&gc5603->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&gc5603->mutex); + /* No crop or compose */ + return 0; +} +#endif + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops gc5603_internal_ops = { + .open = gc5603_open, +}; +#endif + +static int gc5603_s_power(struct v4l2_subdev *sd, int on) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct i2c_client *client = gc5603->client; + int ret = 0; + + mutex_lock(&gc5603->mutex); + + /* If the power state is not modified - no work to do. */ + if (gc5603->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + gc5603->power_on = true; + } else { + pm_runtime_put(&client->dev); + gc5603->power_on = false; + } + +unlock_and_return: + mutex_unlock(&gc5603->mutex); + + return ret; +} + +static const struct v4l2_subdev_core_ops gc5603_core_ops = { + .s_power = gc5603_s_power, + .ioctl = gc5603_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = gc5603_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops gc5603_video_ops = { + .s_stream = gc5603_s_stream, + .g_frame_interval = gc5603_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops gc5603_pad_ops = { + .enum_mbus_code = gc5603_enum_mbus_code, + .enum_frame_size = gc5603_enum_frame_sizes, + .enum_frame_interval = gc5603_enum_frame_interval, + .get_fmt = gc5603_get_fmt, + .set_fmt = gc5603_set_fmt, + .get_selection = gc5603_get_selection, + .get_mbus_config = gc5603_g_mbus_config, +}; + +static const struct v4l2_subdev_ops gc5603_subdev_ops = { + .core = &gc5603_core_ops, + .video = &gc5603_video_ops, + .pad = &gc5603_pad_ops, +}; + +static int gc5603_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + __gc5603_power_on(gc5603); + return 0; +} + +static int gc5603_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + __gc5603_power_off(gc5603); + return 0; +} + +static const struct dev_pm_ops gc5603_pm_ops = { + SET_RUNTIME_PM_OPS(gc5603_runtime_suspend, + gc5603_runtime_resume, NULL) +}; + +static int gc5603_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct gc5603 *gc5603; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + gc5603 = devm_kzalloc(dev, sizeof(*gc5603), GFP_KERNEL); + if (!gc5603) + return -ENOMEM; + + gc5603->client = client; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &gc5603->module_index); + if (ret) { + dev_warn(dev, "could not get module index!\n"); + gc5603->module_index = 0; + } + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &gc5603->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &gc5603->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &gc5603->len_name); + if (ret) { + dev_err(dev, + "could not get module information!\n"); + return -EINVAL; + } + + gc5603->xvclk = devm_clk_get(&client->dev, "xvclk"); + if (IS_ERR(gc5603->xvclk)) { + dev_err(&client->dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + gc5603->pwren_gpio = devm_gpiod_get(dev, "pwren", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->pwdn_gpio)) + dev_warn(dev, "Failed to get pwren-gpios\n"); + + gc5603->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->reset_gpio)) + dev_info(dev, "Failed to get reset-gpios, maybe no used\n"); + + gc5603->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->pwdn_gpio)) + dev_warn(dev, "Failed to get power-gpios\n"); + + ret = gc5603_configure_regulators(gc5603); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + ret = gc5603_parse_of(gc5603); + if (ret != 0) + return -EINVAL; + + gc5603->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(gc5603->pinctrl)) { + gc5603->pins_default = + pinctrl_lookup_state(gc5603->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(gc5603->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + gc5603->pins_sleep = + pinctrl_lookup_state(gc5603->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(gc5603->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } else { + dev_err(dev, "no pinctrl\n"); + } + + mutex_init(&gc5603->mutex); + + sd = &gc5603->subdev; + v4l2_i2c_subdev_init(sd, client, &gc5603_subdev_ops); + ret = gc5603_initialize_controls(gc5603); + if (ret) + goto err_destroy_mutex; + + ret = __gc5603_power_on(gc5603); + if (ret) + goto err_free_handler; + + usleep_range(3000, 4000); + ret = gc5603_check_sensor_id(gc5603, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &gc5603_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + gc5603->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &gc5603->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(gc5603->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + gc5603->module_index, facing, + GC5603_NAME, dev_name(sd->dev)); + + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + +err_power_off: + __gc5603_power_off(gc5603); +err_free_handler: + v4l2_ctrl_handler_free(&gc5603->ctrl_handler); + +err_destroy_mutex: + mutex_destroy(&gc5603->mutex); + return ret; +} + +static int gc5603_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&gc5603->ctrl_handler); + mutex_destroy(&gc5603->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __gc5603_power_off(gc5603); + pm_runtime_set_suspended(&client->dev); + return 0; +} + +static const struct i2c_device_id gc5603_match_id[] = { + { "gc5603", 0 }, + { }, +}; + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id gc5603_of_match[] = { + { .compatible = "galaxycore,gc5603" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gc5603_of_match); +#endif + +static struct i2c_driver gc5603_i2c_driver = { + .driver = { + .name = GC5603_NAME, + .pm = &gc5603_pm_ops, + .of_match_table = of_match_ptr(gc5603_of_match), + }, + .probe = &gc5603_probe, + .remove = &gc5603_remove, + .id_table = gc5603_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&gc5603_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&gc5603_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("GC2035 CMOS Image Sensor driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/Linux-Kernel.si4project/Backup/lubancat_linux_rk3588_defconfig(2041) b/Linux-Kernel.si4project/Backup/lubancat_linux_rk3588_defconfig(2041) new file mode 100755 index 0000000000000..238366df74139 --- /dev/null +++ b/Linux-Kernel.si4project/Backup/lubancat_linux_rk3588_defconfig(2041) @@ -0,0 +1,816 @@ +CONFIG_DEFAULT_HOSTNAME="localhost" +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_UCLAMP_TASK=y +CONFIG_UCLAMP_BUCKETS_COUNT=20 +CONFIG_CGROUPS=y +CONFIG_MEMCG=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_SCHED=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_UCLAMP_TASK_GROUP=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_HUGETLB=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_BPF=y +CONFIG_NAMESPACES=y +CONFIG_USER_NS=y +CONFIG_CHECKPOINT_RESTORE=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_BPF_SYSCALL=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_ARCH_ROCKCHIP=y +# CONFIG_ARM64_ERRATUM_826319 is not set +# CONFIG_ARM64_ERRATUM_827319 is not set +# CONFIG_ARM64_ERRATUM_824069 is not set +# CONFIG_ARM64_ERRATUM_819472 is not set +# CONFIG_ARM64_ERRATUM_832075 is not set +# CONFIG_CAVIUM_ERRATUM_22375 is not set +# CONFIG_CAVIUM_ERRATUM_23154 is not set +CONFIG_SCHED_MC=y +CONFIG_NR_CPUS=8 +CONFIG_HZ_300=y +CONFIG_COMPAT=y +CONFIG_ARMV8_DEPRECATED=y +CONFIG_SWP_EMULATION=y +CONFIG_CP15_BARRIER_EMULATION=y +CONFIG_SETEND_EMULATION=y +CONFIG_ARM64_PSEUDO_NMI=y +CONFIG_PM_DEBUG=y +CONFIG_PM_ADVANCED_DEBUG=y +CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y +CONFIG_ENERGY_MODEL=y +CONFIG_CPU_IDLE=y +CONFIG_ARM_CPUIDLE=y +CONFIG_ARM_PSCI_CPUIDLE=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_TIMES=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPUFREQ_DT=y +CONFIG_ARM_ROCKCHIP_CPUFREQ=y +CONFIG_ARM_SCMI_PROTOCOL=y +CONFIG_ROCKCHIP_SIP=y +CONFIG_ARM64_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_BLK_DEV_THROTTLING=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_CMDLINE_PARTITION=y +# CONFIG_COMPACTION is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_CMA=y +CONFIG_ZSMALLOC=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_PACKET_DIAG=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MROUTE=y +CONFIG_SYN_COOKIES=y +CONFIG_INET_ESP=y +# CONFIG_INET_DIAG is not set +# CONFIG_IPV6_SIT is not set +CONFIG_NETFILTER=y +CONFIG_BRIDGE_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_PROCFS=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_DSCP=y +CONFIG_NETFILTER_XT_TARGET_HL=y +CONFIG_NETFILTER_XT_TARGET_HMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_TEE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=y +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y +CONFIG_NETFILTER_XT_MATCH_BPF=y +CONFIG_NETFILTER_XT_MATCH_CGROUP=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_CPU=y +CONFIG_NETFILTER_XT_MATCH_DCCP=y +CONFIG_NETFILTER_XT_MATCH_DEVGROUP=y +CONFIG_NETFILTER_XT_MATCH_DSCP=y +CONFIG_NETFILTER_XT_MATCH_ECN=y +CONFIG_NETFILTER_XT_MATCH_ESP=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPCOMP=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_IPVS=y +CONFIG_NETFILTER_XT_MATCH_L2TP=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_NFACCT=y +CONFIG_NETFILTER_XT_MATCH_OSF=y +CONFIG_NETFILTER_XT_MATCH_OWNER=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +CONFIG_NETFILTER_XT_MATCH_RATEEST=y +CONFIG_NETFILTER_XT_MATCH_REALM=y +CONFIG_NETFILTER_XT_MATCH_RECENT=y +CONFIG_NETFILTER_XT_MATCH_SCTP=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_IP_VS=y +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_RR=y +CONFIG_IP_VS_NFCT=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_NAT=y +CONFIG_IP6_NF_TARGET_MASQUERADE=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_VLAN_FILTERING=y +CONFIG_VLAN_8021Q=y +CONFIG_NET_SCHED=y +CONFIG_NET_CLS_CGROUP=y +CONFIG_NETLINK_DIAG=y +CONFIG_CGROUP_NET_PRIO=y +CONFIG_CAN=y +CONFIG_CAN_ROCKCHIP=y +CONFIG_CANFD_ROCKCHIP=y +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y +CONFIG_BT_HS=y +CONFIG_BT_MSFTEXT=y +CONFIG_BT_HCIBTUSB=m +CONFIG_BT_HCIBTUSB_AUTOSUSPEND=y +CONFIG_BT_HCIBTUSB_MTK=y +CONFIG_BT_HCIBFUSB=m +CONFIG_BT_HCIVHCI=m +CONFIG_BT_MRVL=m +CONFIG_BT_MRVL_SDIO=m +CONFIG_MAC80211_DEBUGFS=y +CONFIG_RFKILL=y +CONFIG_RFKILL_RK=y +CONFIG_PCI=y +CONFIG_PCIEPORTBUS=y +CONFIG_PCIEASPM_POWERSAVE=y +CONFIG_PCIEASPM_EXT=y +CONFIG_PCIE_ROCKCHIP_HOST=y +CONFIG_PCIE_DW_ROCKCHIP=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_DEBUG_DEVRES=y +CONFIG_CONNECTOR=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_SPI_NAND=y +CONFIG_MTD_SPI_NOR=y +CONFIG_MTD_UBI=y +CONFIG_ZRAM=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=1 +CONFIG_BLK_DEV_NVME=y +CONFIG_SRAM=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_SPI_ATTRS=y +CONFIG_ATA=y +CONFIG_SATA_AHCI=y +CONFIG_SATA_AHCI_PLATFORM=y +# CONFIG_ATA_SFF is not set +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_THIN_PROVISIONING=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +CONFIG_MACVLAN=y +CONFIG_IPVLAN=y +CONFIG_VXLAN=y +CONFIG_TUN=m +CONFIG_VETH=y +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_NET_VENDOR_ADAPTEC is not set +# CONFIG_NET_VENDOR_AGERE is not set +# CONFIG_NET_VENDOR_ALTEON is not set +# CONFIG_NET_VENDOR_AMD is not set +# CONFIG_NET_VENDOR_ARC is not set +# CONFIG_NET_VENDOR_ATHEROS is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_BROCADE is not set +# CONFIG_NET_VENDOR_CAVIUM is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CISCO is not set +# CONFIG_NET_VENDOR_DEC is not set +# CONFIG_NET_VENDOR_DLINK is not set +# CONFIG_NET_VENDOR_EMULEX is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_HISILICON is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_MYRI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NVIDIA is not set +# CONFIG_NET_VENDOR_OKI is not set +# CONFIG_NET_VENDOR_QLOGIC is not set +# CONFIG_NET_VENDOR_QUALCOMM is not set +# CONFIG_NET_VENDOR_RDC is not set +CONFIG_R8169=y +# CONFIG_NET_VENDOR_RENESAS is not set +# CONFIG_NET_VENDOR_ROCKER is not set +# CONFIG_NET_VENDOR_SAMSUNG is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SILAN is not set +# CONFIG_NET_VENDOR_SIS is not set +# CONFIG_NET_VENDOR_SMSC is not set +CONFIG_STMMAC_ETH=y +# CONFIG_NET_VENDOR_SUN is not set +# CONFIG_NET_VENDOR_SYNOPSYS is not set +# CONFIG_NET_VENDOR_TEHUTI is not set +# CONFIG_NET_VENDOR_TI is not set +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +CONFIG_ROCKCHIP_PHY=y +CONFIG_JLSEMI_PHY=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +CONFIG_SLIP=y +CONFIG_SLIP_COMPRESSED=y +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_AX88179_178A=m +CONFIG_USB_NET_CDC_NCM=m +CONFIG_USB_NET_NET1080=m +CONFIG_USB_NET_CDC_SUBSET=m +CONFIG_USB_NET_ZAURUS=m +CONFIG_USB_NET_QMI_WWAN_Q=y +CONFIG_B43=m +CONFIG_B43_DEBUG=y +CONFIG_IPW2100=m +CONFIG_IPW2100_MONITOR=y +CONFIG_IPW2100_DEBUG=y +CONFIG_IPW2200=m +CONFIG_IPW2200_MONITOR=y +CONFIG_IPW2200_PROMISCUOUS=y +CONFIG_IPW2200_QOS=y +CONFIG_IPW2200_DEBUG=y +CONFIG_LIBIPW_DEBUG=y +CONFIG_IWL4965=m +CONFIG_IWL3945=m +CONFIG_IWLEGACY_DEBUG=y +CONFIG_IWLEGACY_DEBUGFS=y +CONFIG_IWLWIFI=m +CONFIG_IWLDVM=m +CONFIG_IWLMVM=m +CONFIG_IWLWIFI_BCAST_FILTERING=y +CONFIG_IWLWIFI_DEBUG=y +CONFIG_IWLWIFI_DEBUGFS=y +CONFIG_MT7601U=m +CONFIG_MT76x0U=m +CONFIG_MT76x0E=m +CONFIG_MT76x2E=m +CONFIG_MT76x2U=m +CONFIG_MT7603E=m +CONFIG_MT7615E=m +CONFIG_MT7663U=m +CONFIG_MT7663S=m +CONFIG_MT7915E=m +CONFIG_RTL8192CE=m +CONFIG_RTL8192SE=m +CONFIG_RTL8192DE=m +CONFIG_RTL8723BE=m +CONFIG_RTL8188EE=m +CONFIG_RTL8192EE=m +CONFIG_RTL8821AE=m +CONFIG_RTL8192CU=m +CONFIG_RTL8XXXU=m +CONFIG_RTW88=y +CONFIG_RTW88_8822BE=m +CONFIG_RTW88_8723DE=m +CONFIG_RTW88_DEBUG=y +CONFIG_RTW88_DEBUGFS=y +CONFIG_WL_ROCKCHIP=y +CONFIG_WIFI_BUILD_MODULE=y +# CONFIG_BCMDHD is not set +CONFIG_RTL8821CE=m +CONFIG_RTL8821CU=m +CONFIG_RTL8822BU=m +CONFIG_RTL8822CS=m +CONFIG_RTL8822CE=m +CONFIG_RTL8852BE=m +CONFIG_RTL8852BU=m +CONFIG_USB_NET_RNDIS_WLAN=y +CONFIG_INPUT_JOYDEV=y +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_ADC=y +# CONFIG_KEYBOARD_ATKBD is not set +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_GPIO_POLLED=y +# CONFIG_MOUSE_PS2 is not set +CONFIG_MOUSE_CYAPA=y +CONFIG_MOUSE_ELAN_I2C=y +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_SIDEWINDER=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL_MXT=y +CONFIG_TOUCHSCREEN_GOODIX=y +CONFIG_TOUCHSCREEN_GSL3673=y +CONFIG_TOUCHSCREEN_GT1X=y +CONFIG_TOUCHSCREEN_ELAN=y +CONFIG_TOUCHSCREEN_USB_COMPOSITE=y +CONFIG_ROCKCHIP_REMOTECTL=y +CONFIG_ROCKCHIP_REMOTECTL_PWM=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_RK805_PWRKEY=y +# CONFIG_SERIO is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=10 +CONFIG_SERIAL_8250_RUNTIME_UARTS=10 +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_99xx=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_ROCKCHIP=y +CONFIG_TCG_TPM=y +CONFIG_TCG_TIS_I2C_INFINEON=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_RK3X=y +CONFIG_SPI=y +CONFIG_SPI_BITBANG=y +CONFIG_SPI_ROCKCHIP=y +CONFIG_SPI_ROCKCHIP_SFC=y +CONFIG_SPI_SPIDEV=y +CONFIG_PINCTRL_RK805=y +CONFIG_PINCTRL_RK806=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_IRQ=y +CONFIG_POWER_RESET_GPIO=y +CONFIG_POWER_RESET_GPIO_RESTART=y +CONFIG_SYSCON_REBOOT_MODE=y +CONFIG_BATTERY_CW2017=y +CONFIG_BATTERY_SBS=y +CONFIG_CHARGER_GPIO=y +CONFIG_CHARGER_BQ24735=y +CONFIG_CHARGER_BQ25700=y +CONFIG_BATTERY_RK817=y +CONFIG_CHARGER_RK817=y +CONFIG_SENSORS_PWM_FAN=y +CONFIG_THERMAL=y +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR=y +CONFIG_THERMAL_GOV_FAIR_SHARE=y +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y +CONFIG_CPU_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y +CONFIG_ROCKCHIP_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_DW_WATCHDOG=y +CONFIG_MFD_RK806_SPI=y +CONFIG_MFD_RK808=y +CONFIG_MFD_TPS6586X=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_ACT8865=y +CONFIG_REGULATOR_FAN53555=y +CONFIG_REGULATOR_GPIO=y +CONFIG_REGULATOR_LP8752=y +CONFIG_REGULATOR_MP8865=y +CONFIG_REGULATOR_PWM=y +CONFIG_REGULATOR_RK806=y +CONFIG_REGULATOR_RK808=y +CONFIG_REGULATOR_RK860X=y +CONFIG_REGULATOR_TPS65132=y +CONFIG_REGULATOR_TPS6586X=y +CONFIG_REGULATOR_XZ3216=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=y +# CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV is not set +# CONFIG_USB_GSPCA is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_ROCKCHIP_CIF=y +CONFIG_VIDEO_ROCKCHIP_ISP=y +CONFIG_VIDEO_ROCKCHIP_ISP_VERSION_V1X=y +CONFIG_VIDEO_ROCKCHIP_ISP_VERSION_V21=y +CONFIG_VIDEO_ROCKCHIP_ISPP=y +CONFIG_VIDEO_ROCKCHIP_HDMIRX=y +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_VIDEO_ROCKCHIP_RGA=y +CONFIG_VIDEO_LT6911UXC=y +CONFIG_VIDEO_LT7911D=y +CONFIG_VIDEO_RK628_CSI=y +CONFIG_VIDEO_RK628_BT1120=y +CONFIG_VIDEO_TC35874X=y +CONFIG_VIDEO_RK_IRCUT=y +CONFIG_VIDEO_GC8034=y +CONFIG_VIDEO_IMX415=y +CONFIG_VIDEO_IMX464=y +CONFIG_VIDEO_OS04A10=y +CONFIG_VIDEO_OV4689=y +CONFIG_VIDEO_OV5647=y +CONFIG_VIDEO_OV5648=y +CONFIG_VIDEO_OV5695=y +CONFIG_VIDEO_OV7251=y +CONFIG_VIDEO_OV8858=y +CONFIG_VIDEO_OV13850=y +CONFIG_VIDEO_DW9714=y +CONFIG_VIDEO_GC5603=y +# CONFIG_VGA_ARB is not set +CONFIG_DRM=y +CONFIG_DRM_IGNORE_IOTCL_PERMIT=y +CONFIG_DRM_DP_AUX_CHARDEV=y +CONFIG_DRM_LOAD_EDID_FIRMWARE=y +CONFIG_DRM_ROCKCHIP=y +CONFIG_ROCKCHIP_VOP=y +CONFIG_ROCKCHIP_ANALOGIX_DP=y +CONFIG_ROCKCHIP_CDN_DP=y +CONFIG_ROCKCHIP_DW_HDMI=y +CONFIG_ROCKCHIP_DW_MIPI_DSI=y +CONFIG_ROCKCHIP_DW_DP=y +CONFIG_ROCKCHIP_INNO_HDMI=y +CONFIG_ROCKCHIP_LVDS=y +CONFIG_ROCKCHIP_RGB=y +CONFIG_ROCKCHIP_DW_HDCP2=y +CONFIG_DRM_PANEL_SIMPLE=y +CONFIG_DRM_DISPLAY_CONNECTOR=y +CONFIG_DRM_SII902X=y +CONFIG_DRM_DW_HDMI_I2S_AUDIO=y +CONFIG_DRM_DW_HDMI_CEC=y +CONFIG_MALI400=y +CONFIG_MALI450=y +# CONFIG_MALI400_PROFILING is not set +CONFIG_MALI_SHARED_INTERRUPTS=y +CONFIG_MALI_DT=y +CONFIG_MALI_DEVFREQ=y +CONFIG_MALI_MIDGARD=y +CONFIG_MALI_EXPERT=y +CONFIG_MALI_PLATFORM_THIRDPARTY=y +CONFIG_MALI_PLATFORM_THIRDPARTY_NAME="rk" +CONFIG_MALI_DEBUG=y +CONFIG_MALI_PWRSOFT_765=y +CONFIG_MALI_BIFROST=y +CONFIG_MALI_PLATFORM_NAME="rk" +CONFIG_MALI_CSF_SUPPORT=y +CONFIG_MALI_BIFROST_EXPERT=y +CONFIG_MALI_BIFROST_DEBUG=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_ROCKCHIP_MULTI_RGA=y +CONFIG_IEP=y +CONFIG_ROCKCHIP_MPP_SERVICE=y +CONFIG_ROCKCHIP_MPP_RKVDEC=y +CONFIG_ROCKCHIP_MPP_RKVDEC2=y +CONFIG_ROCKCHIP_MPP_RKVENC=y +CONFIG_ROCKCHIP_MPP_RKVENC2=y +CONFIG_ROCKCHIP_MPP_VDPU1=y +CONFIG_ROCKCHIP_MPP_VEPU1=y +CONFIG_ROCKCHIP_MPP_VDPU2=y +CONFIG_ROCKCHIP_MPP_VEPU2=y +CONFIG_ROCKCHIP_MPP_IEP2=y +CONFIG_ROCKCHIP_MPP_JPGDEC=y +CONFIG_ROCKCHIP_MPP_AV1DEC=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_HRTIMER=y +CONFIG_SND_DYNAMIC_MINORS=y +# CONFIG_SND_SUPPORT_OLD_API is not set +CONFIG_SND_SEQUENCER=y +CONFIG_SND_SEQ_DUMMY=y +# CONFIG_SND_PCI is not set +# CONFIG_SND_SPI is not set +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_ROCKCHIP=y +CONFIG_SND_SOC_ROCKCHIP_I2S_TDM=y +CONFIG_SND_SOC_ROCKCHIP_PDM=y +CONFIG_SND_SOC_ROCKCHIP_SPDIF=y +CONFIG_SND_SOC_ROCKCHIP_SPDIFRX=y +CONFIG_SND_SOC_ROCKCHIP_MAX98090=y +CONFIG_SND_SOC_ROCKCHIP_MULTICODECS=y +CONFIG_SND_SOC_ROCKCHIP_RT5645=y +CONFIG_SND_SOC_ROCKCHIP_HDMI=y +CONFIG_SND_SOC_DUMMY_CODEC=y +CONFIG_SND_SOC_ES7202=y +CONFIG_SND_SOC_ES7243E=y +CONFIG_SND_SOC_ES8311=y +CONFIG_SND_SOC_ES8316=y +CONFIG_SND_SOC_ES8323=y +CONFIG_SND_SOC_ES8326=y +CONFIG_SND_SOC_RK3308=y +CONFIG_SND_SOC_RK3328=y +CONFIG_SND_SOC_RK817=y +CONFIG_SND_SOC_RK_CODEC_DIGITAL=y +CONFIG_SND_SOC_RT5616=y +CONFIG_SND_SOC_RT5640=y +CONFIG_SND_SOC_RT5651=y +CONFIG_SND_SOC_SPDIF=y +CONFIG_SND_SIMPLE_CARD=y +CONFIG_HID_BATTERY_STRENGTH=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_MULTITOUCH=y +CONFIG_USB_HIDDEV=y +CONFIG_I2C_HID=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +# CONFIG_USB_DEFAULT_PERSIST is not set +CONFIG_USB_OTG=y +CONFIG_USB_MON=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_OHCI_HCD_PCI is not set +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_ACM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_UAS=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC2=y +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_CH341=m +CONFIG_USB_SERIAL_CP210X=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OTI6858=m +CONFIG_USB_SERIAL_QUALCOMM=m +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_EZUSB_FX2=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_DEBUG_FILES=y +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_ACM=y +CONFIG_USB_CONFIGFS_RNDIS=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_UAC1=y +CONFIG_USB_CONFIGFS_F_UAC2=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_UVC=y +CONFIG_TYPEC_TCPM=y +CONFIG_TYPEC_TCPCI=y +CONFIG_TYPEC_HUSB311=y +CONFIG_TYPEC_FUSB302=y +CONFIG_TYPEC_DP_ALTMODE=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_TEST=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_OF_ARASAN=y +CONFIG_MMC_SDHCI_OF_DWCMSHC=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_ROCKCHIP=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_IS31FL32XX=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_HYM8563=y +CONFIG_RTC_DRV_RK808=y +CONFIG_RTC_DRV_RX8010=y +CONFIG_DMADEVICES=y +CONFIG_PL330_DMA=y +CONFIG_SW_SYNC=y +CONFIG_DMABUF_HEAPS=y +CONFIG_DMABUF_SYSFS_STATS=y +CONFIG_DMABUF_HEAPS_DEFERRED_FREE=y +CONFIG_DMABUF_HEAPS_PAGE_POOL=y +CONFIG_DMABUF_HEAPS_SYSTEM=y +CONFIG_DMABUF_HEAPS_CMA=y +CONFIG_STAGING=y +CONFIG_COMMON_CLK_RK808=y +CONFIG_COMMON_CLK_SCMI=y +CONFIG_COMMON_CLK_PWM=y +CONFIG_ROCKCHIP_CLK_BOOST=y +CONFIG_ROCKCHIP_DDRCLK_SIP=y +CONFIG_ROCKCHIP_DDRCLK_SIP_V2=y +CONFIG_ROCKCHIP_PLL_RK3399=y +CONFIG_MAILBOX=y +CONFIG_IOMMU_LIMIT_IOVA_ALIGNMENT=y +CONFIG_IOMMU_IOVA_ALIGNMENT=4 +CONFIG_ROCKCHIP_IOMMU=y +CONFIG_ARM_SMMU_V3=y +CONFIG_CPU_RK3588=y +CONFIG_ROCKCHIP_CPUINFO=y +CONFIG_ROCKCHIP_GRF=y +CONFIG_ROCKCHIP_IODOMAIN=y +CONFIG_ROCKCHIP_IPA=y +CONFIG_ROCKCHIP_OPP=y +CONFIG_ROCKCHIP_PM_DOMAINS=y +CONFIG_ROCKCHIP_PVTM=y +CONFIG_ROCKCHIP_SUSPEND_MODE=y +CONFIG_ROCKCHIP_SYSTEM_MONITOR=y +CONFIG_ROCKCHIP_VENDOR_STORAGE=y +CONFIG_ROCKCHIP_MMC_VENDOR_STORAGE=y +CONFIG_ROCKCHIP_VENDOR_STORAGE_UPDATE_LOADER=y +CONFIG_FIQ_DEBUGGER=y +CONFIG_FIQ_DEBUGGER_NO_SLEEP=y +CONFIG_FIQ_DEBUGGER_CONSOLE=y +CONFIG_FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE=y +CONFIG_FIQ_DEBUGGER_TRUST_ZONE=y +CONFIG_RK_CONSOLE_THREAD=y +CONFIG_ROCKCHIP_DEBUG=y +CONFIG_PM_DEVFREQ=y +CONFIG_DEVFREQ_GOV_PERFORMANCE=y +CONFIG_DEVFREQ_GOV_POWERSAVE=y +CONFIG_DEVFREQ_GOV_USERSPACE=y +CONFIG_ARM_ROCKCHIP_BUS_DEVFREQ=y +CONFIG_ARM_ROCKCHIP_DMC_DEVFREQ=y +CONFIG_DEVFREQ_EVENT_ROCKCHIP_NOCP=y +CONFIG_IIO=y +CONFIG_ROCKCHIP_SARADC=y +CONFIG_SENSORS_ISL29018=y +CONFIG_SENSORS_TSL2563=y +CONFIG_TSL2583=y +CONFIG_IIO_SYSFS_TRIGGER=y +CONFIG_PWM=y +CONFIG_PWM_ROCKCHIP=y +CONFIG_PHY_ROCKCHIP_CSI2_DPHY=y +CONFIG_PHY_ROCKCHIP_DP=y +CONFIG_PHY_ROCKCHIP_EMMC=y +CONFIG_PHY_ROCKCHIP_INNO_HDMI=y +CONFIG_PHY_ROCKCHIP_INNO_USB2=y +CONFIG_PHY_ROCKCHIP_INNO_USB3=y +CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY=y +CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY=y +CONFIG_PHY_ROCKCHIP_NANENG_EDP=y +CONFIG_PHY_ROCKCHIP_PCIE=y +CONFIG_PHY_ROCKCHIP_SAMSUNG_DCPHY=y +CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX=y +CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX_HDMI=y +CONFIG_PHY_ROCKCHIP_SNPS_PCIE3=y +CONFIG_PHY_ROCKCHIP_TYPEC=y +CONFIG_PHY_ROCKCHIP_USB=y +CONFIG_PHY_ROCKCHIP_USBDP=y +CONFIG_ANDROID=y +CONFIG_ROCKCHIP_EFUSE=y +CONFIG_ROCKCHIP_OTP=y +CONFIG_TEE=y +CONFIG_OPTEE=y +CONFIG_RK_HEADSET=y +CONFIG_ROCKCHIP_RKNPU=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_XFS_FS=y +CONFIG_BTRFS_FS=y +CONFIG_BTRFS_FS_POSIX_ACL=y +# CONFIG_DNOTIFY is not set +CONFIG_FUSE_FS=y +CONFIG_OVERLAY_FS=y +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=936 +CONFIG_FAT_DEFAULT_IOCHARSET="utf8" +CONFIG_EXFAT_FS=y +CONFIG_NTFS_FS=m +CONFIG_NTFS_RW=y +CONFIG_NTFS3_FS=m +CONFIG_NTFS3_LZX_XPRESS=y +CONFIG_NTFS3_FS_POSIX_ACL=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_HUGETLBFS=y +CONFIG_EFIVAR_FS=y +CONFIG_JFFS2_FS=y +CONFIG_UBIFS_FS=y +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_LZO=y +CONFIG_SQUASHFS_XZ=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_SWAP=y +CONFIG_NFSD=y +CONFIG_NFSD_V3_ACL=y +CONFIG_NFSD_V4=y +CONFIG_NFSD_BLOCKLAYOUT=y +CONFIG_NFSD_SCSILAYOUT=y +CONFIG_NFSD_FLEXFILELAYOUT=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_936=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y +CONFIG_UNICODE=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_USER_API_HASH=y +CONFIG_CRYPTO_USER_API_SKCIPHER=y +CONFIG_CRYPTO_DEV_ROCKCHIP=y +CONFIG_CRYPTO_DEV_ROCKCHIP_V1=y +CONFIG_CRYPTO_DEV_ROCKCHIP_DEV=y +CONFIG_CRC_T10DIF=y +CONFIG_CRC7=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_DMA_CMA=y +CONFIG_PRINTK_TIME=y +CONFIG_PRINTK_TIME_FROM_ARM_ARCH_TIMER=y +CONFIG_DYNAMIC_DEBUG=y +CONFIG_DEBUG_INFO=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0 +CONFIG_SCHEDSTATS=y +CONFIG_DEBUG_CREDENTIALS=y +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_FUNCTION_TRACER=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_LKDTM=y diff --git a/Linux-Kernel.si4project/Backup/rk3588s-lubancat-csi2(6413).dtsi b/Linux-Kernel.si4project/Backup/rk3588s-lubancat-csi2(6413).dtsi new file mode 100755 index 0000000000000..692c72f0043c3 --- /dev/null +++ b/Linux-Kernel.si4project/Backup/rk3588s-lubancat-csi2(6413).dtsi @@ -0,0 +1,590 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2020 Rockchip Electronics Co., Ltd. + * + */ + +/ { + ext_cam_37m_clk: external-camera-37m-clock { + compatible = "fixed-clock"; + clock-frequency = <37125000>; + clock-output-names = "ext_cam_37m_clk"; + #clock-cells = <0>; + }; + ext_cam_27m_clk: external-camera-27m-clock { + compatible = "fixed-clock"; + clock-frequency = <27000000>; + clock-output-names = "ext_cam_27m_clk"; + #clock-cells = <0>; + }; + + vdd_cam_5v: vdd-cam-5v-regulator { + compatible = "regulator-fixed"; + regulator-name = "vdd_cam_5v"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + cam_dovdd: cam-dovdd { + compatible = "regulator-fixed"; + regulator-name = "cam_dovdd"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + vin-supply = <&vdd_cam_5v>; + }; + + cam_avdd: cam-avdd { + compatible = "regulator-fixed"; + regulator-name = "cam_avdd"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + vin-supply = <&vdd_cam_5v>; + }; + + cam_dvdd: cam-dvdd { + compatible = "regulator-fixed"; + regulator-name = "cam_dvdd"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + vin-supply = <&vdd_cam_5v>; + }; +}; + + +&i2c1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c1m2_xfer>; + + dw9714_0: dw9714-0@c { + status = "okay"; + compatible = "dongwoon,dw9714"; + reg = <0xc>; + rockchip,camera-module-index = <0>; + rockchip,vcm-max-current = <100>; + rockchip,vcm-start-current = <0>; + rockchip,vcm-rated-current = <100>; + rockchip,vcm-step-mode = <0xd>; + rockchip,vcm-dlc-enable = <0>; + rockchip,vcm-mclk = <0>; + rockchip,vcm-t-src = <0>; + rockchip,camera-module-facing = "back"; + }; + + gc5603_0: gc5603-0@31 { + compatible = "galaxycore,gc5603"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_27m_clk>; + clock-names = "ext_cam_27m_clk"; + pwdn-gpios = <&gpio1 RK_PA3 GPIO_ACTIVE_LOW>; + rotation = <180>; + + port { + gc5603_out0: endpoint { + remote-endpoint = <&dcphy0_in_gc5603>; + data-lanes = <1 2>; + }; + }; + }; + + imx415_0: imx415-0@1a { + compatible = "sony,imx415"; + status = "okay"; + reg = <0x1a>; + clocks = <&ext_cam_37m_clk>; + clock-names = "xvclk"; + avdd-supply = <&cam_avdd>; + dovdd-supply = <&cam_dovdd>; + dvdd-supply = <&cam_dvdd>; + reset-gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_LOW>; + + rockchip,camera-module-index = <0>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "CMK-OT2022-PX1"; + rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; + port { + imx415_out0: endpoint { + remote-endpoint = <&dcphy0_in_imx415>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&mipi_dcphy0 { + status = "okay"; +}; + +&csi2_dcphy0 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + dcphy0_in_gc5603: endpoint@0 { + reg = <0>; + remote-endpoint = <&gc5603_out0>; + data-lanes = <1 2>; + }; + + dcphy0_in_imx415: endpoint@1 { + reg = <1>; + remote-endpoint = <&imx415_out0>; + data-lanes = <1 2 3 4>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + csidcphy0_out: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi0_csi2_input>; + }; + }; + }; +}; + +&mipi0_csi2 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + mipi0_csi2_input: endpoint@1 { + reg = <1>; + remote-endpoint = <&csidcphy0_out>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + mipi0_csi2_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&cif_mipi0_in0>; + }; + }; + }; +}; + +&i2c5 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c5m3_xfer>; + + dw9714_1: dw9714-1@c { + status = "okay"; + compatible = "dongwoon,dw9714"; + reg = <0xc>; + rockchip,camera-module-index = <0>; + rockchip,vcm-max-current = <100>; + rockchip,vcm-start-current = <0>; + rockchip,vcm-rated-current = <100>; + rockchip,vcm-step-mode = <0xd>; + rockchip,vcm-dlc-enable = <0>; + rockchip,vcm-mclk = <0>; + rockchip,vcm-t-src = <0>; + rockchip,camera-module-facing = "back"; + }; + + gc5603_1: gc5603-1@31 { + compatible = "galaxycore,gc5603"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_27m_clk>; + clock-names = "ext_cam_27m_clk"; + pwdn-gpios = <&gpio4 RK_PA5 GPIO_ACTIVE_LOW>; + rotation = <180>; + + port { + gc5603_out1: endpoint { + remote-endpoint = <&dcphy1_in_gc5603>; + data-lanes = <1 2>; + }; + }; + }; + + imx415_1: imx415-1@1a { + compatible = "sony,imx415"; + status = "okay"; + reg = <0x1a>; + clocks = <&ext_cam_37m_clk>; + clock-names = "xvclk"; + avdd-supply = <&cam_avdd>; + dovdd-supply = <&cam_dovdd>; + dvdd-supply = <&cam_dvdd>; + reset-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_LOW>; + + rockchip,camera-module-index = <1>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "CMK-OT2022-PX1"; + rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; + port { + imx415_out1: endpoint { + remote-endpoint = <&dcphy1_in_imx415>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&mipi_dcphy1 { + status = "okay"; +}; + +&csi2_dcphy1 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + dcphy1_in_gc5603: endpoint@0 { + reg = <0>; + remote-endpoint = <&gc5603_out1>; + data-lanes = <1 2>; + }; + + dcphy1_in_imx415: endpoint@1 { + reg = <1>; + remote-endpoint = <&imx415_out1>; + data-lanes = <1 2 3 4>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + csidcphy1_out: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi1_csi2_input>; + }; + }; + }; +}; + +&mipi1_csi2 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + mipi1_csi2_input: endpoint@1 { + reg = <1>; + remote-endpoint = <&csidcphy1_out>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + mipi1_csi2_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&cif_mipi1_in0>; + }; + }; + }; +}; + +&i2c6 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c6m3_xfer>; + + dw9714: dw9714@c { + status = "okay"; + compatible = "dongwoon,dw9714"; + reg = <0xc>; + rockchip,camera-module-index = <0>; + rockchip,vcm-max-current = <100>; + rockchip,vcm-start-current = <0>; + rockchip,vcm-rated-current = <100>; + rockchip,vcm-step-mode = <0xd>; + rockchip,vcm-dlc-enable = <0>; + rockchip,vcm-mclk = <0>; + rockchip,vcm-t-src = <0>; + rockchip,camera-module-facing = "back"; + }; + + gc5603_2: gc5603_2@31 { + compatible = "galaxycore,gc5603"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_27m_clk>; + clock-names = "ext_cam_27m_clk"; + pwdn-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_LOW>; + rotation = <180>; + + port { + gc5603_out2: endpoint { + remote-endpoint = <&dphy0_in_gc5603>; + data-lanes = <1 2>; + }; + }; + }; + + imx415_2: imx415-2@1a { + compatible = "sony,imx415"; + status = "okay"; + reg = <0x1a>; + clocks = <&ext_cam_37m_clk>; + clock-names = "xvclk"; + avdd-supply = <&cam_avdd>; + dovdd-supply = <&cam_dovdd>; + dvdd-supply = <&cam_dvdd>; + reset-gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_LOW>; + + rockchip,camera-module-index = <2>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "CMK-OT2022-PX1"; + rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; + port { + imx415_out2: endpoint { + remote-endpoint = <&dphy0_in_imx415>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&csi2_dphy0_hw { + status = "okay"; +}; + +&csi2_dphy0 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + dphy0_in_gc5603: endpoint@0 { + reg = <0>; + remote-endpoint = <&gc5603_out2>; + data-lanes = <1 2 3 4>; + }; + + dphy0_in_imx415: endpoint@1 { + reg = <1>; + remote-endpoint = <&imx415_out2>; + data-lanes = <1 2 3 4>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + csidphy0_out: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi2_csi2_input>; + }; + }; + }; +}; + +&mipi2_csi2 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + mipi2_csi2_input: endpoint@1 { + reg = <1>; + remote-endpoint = <&csidphy0_out>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + mipi2_csi2_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&cif_mipi2_in0>; + }; + }; + }; +}; + + +&rkcif { + status = "okay"; +}; + +&rkcif_mmu { + status = "okay"; +}; + +&rkcif_mipi_lvds { + status = "okay"; + + port { + cif_mipi0_in0: endpoint { + remote-endpoint = <&mipi0_csi2_output>; + }; + }; +}; + +&rkcif_mipi_lvds_sditf { + status = "okay"; + + port { + mipi_lvds_sditf: endpoint { + remote-endpoint = <&isp0_vir0>; + }; + }; +}; + +&rkcif_mipi_lvds1 { + status = "okay"; + + port { + cif_mipi1_in0: endpoint { + remote-endpoint = <&mipi1_csi2_output>; + }; + }; +}; + +&rkcif_mipi_lvds1_sditf { + status = "okay"; + + port { + mipi_lvds1_sditf: endpoint { + remote-endpoint = <&isp0_vir1>; + }; + }; +}; + +&rkcif_mipi_lvds2 { + status = "okay"; + + port { + cif_mipi2_in0: endpoint { + remote-endpoint = <&mipi2_csi2_output>; + }; + }; +}; + +&rkcif_mipi_lvds2_sditf { + status = "okay"; + + port { + mipi_lvds2_sditf: endpoint { + remote-endpoint = <&isp1_vir0>; + }; + }; +}; + +&rkisp0 { + status = "okay"; +}; + +&isp0_mmu { + status = "okay"; +}; + +&rkisp0_vir0 { + status = "okay"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + isp0_vir0: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi_lvds_sditf>; + }; + }; +}; + +&rkisp0_vir1 { + status = "okay"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + isp0_vir1: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi_lvds1_sditf>; + }; + }; +}; + + +&rkisp1 { + status = "okay"; +}; + +&isp1_mmu { + status = "okay"; +}; + +&rkisp1_vir0 { + status = "okay"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + isp1_vir0: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi_lvds2_sditf>; + }; + }; +}; diff --git a/Linux-Kernel.si4project/Backup/rk3588s-lubancat-csi2(6446).dtsi b/Linux-Kernel.si4project/Backup/rk3588s-lubancat-csi2(6446).dtsi new file mode 100755 index 0000000000000..c9a1b56d0692b --- /dev/null +++ b/Linux-Kernel.si4project/Backup/rk3588s-lubancat-csi2(6446).dtsi @@ -0,0 +1,597 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2020 Rockchip Electronics Co., Ltd. + * + */ + +/ { + ext_cam_37m_clk: external-camera-37m-clock { + compatible = "fixed-clock"; + clock-frequency = <37125000>; + clock-output-names = "ext_cam_37m_clk"; + #clock-cells = <0>; + }; + ext_cam_27m_clk: external-camera-27m-clock { + compatible = "fixed-clock"; + clock-frequency = <27000000>; + clock-output-names = "ext_cam_27m_clk"; + #clock-cells = <0>; + }; + + vdd_cam_5v: vdd-cam-5v-regulator { + compatible = "regulator-fixed"; + regulator-name = "vdd_cam_5v"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + cam_dovdd: cam-dovdd { + compatible = "regulator-fixed"; + regulator-name = "cam_dovdd"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + vin-supply = <&vdd_cam_5v>; + }; + + cam_avdd: cam-avdd { + compatible = "regulator-fixed"; + regulator-name = "cam_avdd"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + vin-supply = <&vdd_cam_5v>; + }; + + cam_dvdd: cam-dvdd { + compatible = "regulator-fixed"; + regulator-name = "cam_dvdd"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + vin-supply = <&vdd_cam_5v>; + }; +}; + +//CAM 0 +/* Link path: sensor->csi2_dcphy0->mipi0_csi2->rkcif_mipi_lvds--->rkcif_mipi_lvds_sditf->rkisp0_vir0 */ +&i2c1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c1m2_xfer>; + + dw9714_0: dw9714-0@c { + status = "disabled"; + compatible = "dongwoon,dw9714"; + reg = <0xc>; + rockchip,camera-module-index = <0>; + rockchip,vcm-max-current = <100>; + rockchip,vcm-start-current = <0>; + rockchip,vcm-rated-current = <100>; + rockchip,vcm-step-mode = <0xd>; + rockchip,vcm-dlc-enable = <0>; + rockchip,vcm-mclk = <0>; + rockchip,vcm-t-src = <0>; + rockchip,camera-module-facing = "back"; + }; + + gc5603_0: gc5603-0@31 { + compatible = "galaxycore,gc5603"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_27m_clk>; + clock-names = "ext_cam_27m_clk"; + pwdn-gpios = <&gpio1 RK_PA3 GPIO_ACTIVE_LOW>; + rotation = <180>; + + port { + gc5603_out0: endpoint { + remote-endpoint = <&dcphy0_in_gc5603>; + data-lanes = <1 2>; + }; + }; + }; + + imx415_0: imx415-0@1a { + compatible = "sony,imx415"; + status = "okay"; + reg = <0x1a>; + clocks = <&ext_cam_37m_clk>; + clock-names = "xvclk"; + avdd-supply = <&cam_avdd>; + dovdd-supply = <&cam_dovdd>; + dvdd-supply = <&cam_dvdd>; + reset-gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_LOW>; + + rockchip,camera-module-index = <0>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "CMK-OT2022-PX1"; + rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; + port { + imx415_out0: endpoint { + remote-endpoint = <&dcphy0_in_imx415>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&mipi_dcphy0 { + status = "okay"; +}; + +&csi2_dcphy0 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + dcphy0_in_gc5603: endpoint@0 { + reg = <0>; + remote-endpoint = <&gc5603_out0>; + data-lanes = <1 2>; + }; + + dcphy0_in_imx415: endpoint@1 { + reg = <1>; + remote-endpoint = <&imx415_out0>; + data-lanes = <1 2 3 4>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + csidcphy0_out: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi0_csi2_input>; + }; + }; + }; +}; + +&mipi0_csi2 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + mipi0_csi2_input: endpoint@1 { + reg = <1>; + remote-endpoint = <&csidcphy0_out>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + mipi0_csi2_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&cif_mipi0_in0>; + }; + }; + }; +}; + + +//CAM 1 +/* Link path: sensor->csi2_dcphy1->mipi1_csi2->rkcif_mipi_lvds1--->rkcif_mipi_lvds1_sditf->rkisp0_vir1 */ +&i2c5 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c5m3_xfer>; + + dw9714_1: dw9714-1@c { + status = "disabled"; + compatible = "dongwoon,dw9714"; + reg = <0xc>; + rockchip,camera-module-index = <0>; + rockchip,vcm-max-current = <100>; + rockchip,vcm-start-current = <0>; + rockchip,vcm-rated-current = <100>; + rockchip,vcm-step-mode = <0xd>; + rockchip,vcm-dlc-enable = <0>; + rockchip,vcm-mclk = <0>; + rockchip,vcm-t-src = <0>; + rockchip,camera-module-facing = "back"; + }; + + gc5603_1: gc5603-1@31 { + compatible = "galaxycore,gc5603"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_27m_clk>; + clock-names = "ext_cam_27m_clk"; + pwdn-gpios = <&gpio4 RK_PA5 GPIO_ACTIVE_LOW>; + rotation = <180>; + + port { + gc5603_out1: endpoint { + remote-endpoint = <&dcphy1_in_gc5603>; + data-lanes = <1 2>; + }; + }; + }; + + imx415_1: imx415-1@1a { + compatible = "sony,imx415"; + status = "okay"; + reg = <0x1a>; + clocks = <&ext_cam_37m_clk>; + clock-names = "xvclk"; + avdd-supply = <&cam_avdd>; + dovdd-supply = <&cam_dovdd>; + dvdd-supply = <&cam_dvdd>; + reset-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_LOW>; + + rockchip,camera-module-index = <1>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "CMK-OT2022-PX1"; + rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; + port { + imx415_out1: endpoint { + remote-endpoint = <&dcphy1_in_imx415>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&mipi_dcphy1 { + status = "okay"; +}; + +&csi2_dcphy1 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + dcphy1_in_gc5603: endpoint@0 { + reg = <0>; + remote-endpoint = <&gc5603_out1>; + data-lanes = <1 2>; + }; + + dcphy1_in_imx415: endpoint@1 { + reg = <1>; + remote-endpoint = <&imx415_out1>; + data-lanes = <1 2 3 4>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + csidcphy1_out: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi1_csi2_input>; + }; + }; + }; +}; + +&mipi1_csi2 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + mipi1_csi2_input: endpoint@1 { + reg = <1>; + remote-endpoint = <&csidcphy1_out>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + mipi1_csi2_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&cif_mipi1_in0>; + }; + }; + }; +}; + + +CAM2 +/* Link path: sensor->csi2_dphy1->mipi2_csi2->rkcif_mipi_lvds2--->rkcif_mipi_lvds2_sditf->rkisp0_vir2 */ +&i2c6 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c6m3_xfer>; + + dw9714: dw9714@c { + status = "disabled"; + compatible = "dongwoon,dw9714"; + reg = <0xc>; + rockchip,camera-module-index = <0>; + rockchip,vcm-max-current = <100>; + rockchip,vcm-start-current = <0>; + rockchip,vcm-rated-current = <100>; + rockchip,vcm-step-mode = <0xd>; + rockchip,vcm-dlc-enable = <0>; + rockchip,vcm-mclk = <0>; + rockchip,vcm-t-src = <0>; + rockchip,camera-module-facing = "back"; + }; + + gc5603_2: gc5603_2@31 { + compatible = "galaxycore,gc5603"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_27m_clk>; + clock-names = "ext_cam_27m_clk"; + pwdn-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_LOW>; + rotation = <180>; + + port { + gc5603_out2: endpoint { + remote-endpoint = <&dphy0_in_gc5603>; + data-lanes = <1 2>; + }; + }; + }; + + imx415_2: imx415-2@1a { + compatible = "sony,imx415"; + status = "okay"; + reg = <0x1a>; + clocks = <&ext_cam_37m_clk>; + clock-names = "xvclk"; + avdd-supply = <&cam_avdd>; + dovdd-supply = <&cam_dovdd>; + dvdd-supply = <&cam_dvdd>; + reset-gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_LOW>; + + rockchip,camera-module-index = <2>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "CMK-OT2022-PX1"; + rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; + port { + imx415_out2: endpoint { + remote-endpoint = <&dphy0_in_imx415>; + data-lanes = <1 2 3 4>; + }; + }; + }; +}; + +&csi2_dphy0_hw { + status = "okay"; +}; + +&csi2_dphy0 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + dphy0_in_gc5603: endpoint@0 { + reg = <0>; + remote-endpoint = <&gc5603_out2>; + data-lanes = <1 2>; + }; + + dphy0_in_imx415: endpoint@1 { + reg = <1>; + remote-endpoint = <&imx415_out2>; + data-lanes = <1 2 3 4>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + csidphy0_out: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi2_csi2_input>; + }; + }; + }; +}; + +&mipi2_csi2 { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + mipi2_csi2_input: endpoint@1 { + reg = <1>; + remote-endpoint = <&csidphy0_out>; + }; + }; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + mipi2_csi2_output: endpoint@0 { + reg = <0>; + remote-endpoint = <&cif_mipi2_in0>; + }; + }; + }; +}; + + +&rkcif { + status = "okay"; +}; + +&rkcif_mmu { + status = "okay"; +}; + +&rkcif_mipi_lvds { + status = "okay"; + + port { + cif_mipi0_in0: endpoint { + remote-endpoint = <&mipi0_csi2_output>; + }; + }; +}; + +&rkcif_mipi_lvds_sditf { + status = "okay"; + + port { + mipi_lvds_sditf: endpoint { + remote-endpoint = <&isp0_vir0>; + }; + }; +}; + +&rkcif_mipi_lvds1 { + status = "okay"; + + port { + cif_mipi1_in0: endpoint { + remote-endpoint = <&mipi1_csi2_output>; + }; + }; +}; + +&rkcif_mipi_lvds1_sditf { + status = "okay"; + + port { + mipi_lvds1_sditf: endpoint { + remote-endpoint = <&isp0_vir1>; + }; + }; +}; + +&rkcif_mipi_lvds2 { + status = "okay"; + + port { + cif_mipi2_in0: endpoint { + remote-endpoint = <&mipi2_csi2_output>; + }; + }; +}; + +&rkcif_mipi_lvds2_sditf { + status = "okay"; + + port { + mipi_lvds2_sditf: endpoint { + remote-endpoint = <&isp1_vir0>; + }; + }; +}; + +&rkisp0 { + status = "okay"; +}; + +&isp0_mmu { + status = "okay"; +}; + +&rkisp0_vir0 { + status = "okay"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + isp0_vir0: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi_lvds_sditf>; + }; + }; +}; + +&rkisp0_vir1 { + status = "okay"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + isp0_vir1: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi_lvds1_sditf>; + }; + }; +}; + + +&rkisp1 { + status = "okay"; +}; + +&isp1_mmu { + status = "okay"; +}; + +&rkisp1_vir0 { + status = "okay"; + + port { + #address-cells = <1>; + #size-cells = <0>; + + isp1_vir0: endpoint@0 { + reg = <0>; + remote-endpoint = <&mipi_lvds2_sditf>; + }; + }; +}; diff --git a/Linux-Kernel.si4project/Linux-Kernel.sip_sym b/Linux-Kernel.si4project/Linux-Kernel.sip_sym new file mode 100755 index 0000000000000..bedf02d4c0805 Binary files /dev/null and b/Linux-Kernel.si4project/Linux-Kernel.sip_sym differ diff --git a/Linux-Kernel.si4project/Linux-Kernel.sip_xab b/Linux-Kernel.si4project/Linux-Kernel.sip_xab new file mode 100755 index 0000000000000..08e7df176454f Binary files /dev/null and b/Linux-Kernel.si4project/Linux-Kernel.sip_xab differ diff --git a/Linux-Kernel.si4project/Linux-Kernel.sip_xad b/Linux-Kernel.si4project/Linux-Kernel.sip_xad new file mode 100755 index 0000000000000..875f0935d824b Binary files /dev/null and b/Linux-Kernel.si4project/Linux-Kernel.sip_xad differ diff --git a/Linux-Kernel.si4project/Linux-Kernel.sip_xc b/Linux-Kernel.si4project/Linux-Kernel.sip_xc new file mode 100755 index 0000000000000..908d0a82a4f9a Binary files /dev/null and b/Linux-Kernel.si4project/Linux-Kernel.sip_xc differ diff --git a/Linux-Kernel.si4project/Linux-Kernel.sip_xf b/Linux-Kernel.si4project/Linux-Kernel.sip_xf new file mode 100755 index 0000000000000..908d0a82a4f9a Binary files /dev/null and b/Linux-Kernel.si4project/Linux-Kernel.sip_xf differ diff --git a/Linux-Kernel.si4project/Linux-Kernel.sip_xm b/Linux-Kernel.si4project/Linux-Kernel.sip_xm new file mode 100755 index 0000000000000..593f4708db84a Binary files /dev/null and b/Linux-Kernel.si4project/Linux-Kernel.sip_xm differ diff --git a/Linux-Kernel.si4project/Linux-Kernel.sip_xr b/Linux-Kernel.si4project/Linux-Kernel.sip_xr new file mode 100755 index 0000000000000..908d0a82a4f9a Binary files /dev/null and b/Linux-Kernel.si4project/Linux-Kernel.sip_xr differ diff --git a/Linux-Kernel.si4project/Linux-Kernel.sip_xsb b/Linux-Kernel.si4project/Linux-Kernel.sip_xsb new file mode 100755 index 0000000000000..08e7df176454f Binary files /dev/null and b/Linux-Kernel.si4project/Linux-Kernel.sip_xsb differ diff --git a/Linux-Kernel.si4project/Linux-Kernel.sip_xsd b/Linux-Kernel.si4project/Linux-Kernel.sip_xsd new file mode 100755 index 0000000000000..ab7c4c4910bd9 Binary files /dev/null and b/Linux-Kernel.si4project/Linux-Kernel.sip_xsd differ diff --git a/Linux-Kernel.si4project/Linux-Kernel.siproj b/Linux-Kernel.si4project/Linux-Kernel.siproj new file mode 100755 index 0000000000000..d9e365f9b6806 Binary files /dev/null and b/Linux-Kernel.si4project/Linux-Kernel.siproj differ diff --git a/Linux-Kernel.si4project/Linux-Kernel.siproj_settings.xml b/Linux-Kernel.si4project/Linux-Kernel.siproj_settings.xml new file mode 100755 index 0000000000000..4eb692e28e714 --- /dev/null +++ b/Linux-Kernel.si4project/Linux-Kernel.siproj_settings.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/README.md b/README.md index 468ac2084b21a..c40979172849f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,27 @@ +# 20240830:增加OV50H40.c,CPHY接口CAM0,验证中; + +# 20240812:增加GC8613.c,成功点亮,工作正常,但无rkaiq文件; + +# 20240710:RKAIQ正常工作,3AOK,但iqfile为copy os04a10的,颜色、LSC、AWB均不正常; +![image](https://github.com/shingoxy/kernel-RK3588s-5.10Stable/assets/36981700/d51b1c1e-4d61-4433-b83b-e777f5db479d) + + +# 20240628:GC5603成功点亮,工作正常,i2c通信正常: +image + + + +# 增加GC5603.c驱动并修改对应Kernel文件: +1. drivers/media/i2c/增加GC5603.c文件; +2. arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi修改Device tree; +3. arch/arm64/configs/lubancat_linux_rk3588_defconfig修改defconfig +4. drivers/media/i2c/Kconfig增加gc5603.c编译 +5. drivers/media/i2c/Makefile增加gc5603.c编译 +6. drivers/phy/rockchip/phy-rockchip-csi2-dphy.c修改后增加cphy支持 + +以上修改已经编译通过(2024年6月),将进行点亮验证。 + + # How do I submit patches to Android Common Kernels 1. BEST: Make all of your changes to upstream Linux. If appropriate, backport to the stable releases. diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-4.dts b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-4.dts index 098b476c50696..f994dd0767f99 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-4.dts +++ b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-4.dts @@ -18,7 +18,7 @@ #include "rk3588s.dtsi" #include "rk3588-linux.dtsi" -// #include "rk3588s-lubancat-40pin.dtsi" +#include "rk3588s-lubancat-40pin.dtsi" #include "rk3588s-lubancat-csi2.dtsi" // #include "rk3588s-lubancat-csi2_cam0.dtsi" // #include "rk3588s-lubancat-csi2_cam1.dtsi" diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-40pin.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-40pin.dtsi index 841dce7dfb2aa..f71f8cd3633d9 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-40pin.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-40pin.dtsi @@ -9,7 +9,7 @@ pinctrl-names = "default"; pinctrl-0 = <&uart0m2_xfer>; }; - +/* &i2c5 { status = "okay"; pinctrl-names = "default"; @@ -21,7 +21,7 @@ pinctrl-names = "default"; pinctrl-0 = <&i2c6m3_xfer>; }; - +*/ &spi0 { status = "okay"; diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi index b1807f9e7f036..3938fef235af0 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s-lubancat-csi2.dtsi @@ -11,6 +11,24 @@ clock-output-names = "ext_cam_37m_clk"; #clock-cells = <0>; }; + ext_cam_27m_clk: external-camera-27m-clock { + compatible = "fixed-clock"; + clock-frequency = <27000000>; + clock-output-names = "ext_cam_27m_clk"; + #clock-cells = <0>; + }; + ext_cam_24m_clk: external-camera-24m-clock { + compatible = "fixed-clock"; + clock-frequency = <24000000>; + clock-output-names = "ext_cam_24m_clk"; + #clock-cells = <0>; + }; + ext_cam_19d2m_clk: external-camera-19d2m-clock { + compatible = "fixed-clock"; + clock-frequency = <19200000>; + clock-output-names = "ext_cam_19d2m_clk"; + #clock-cells = <0>; + }; vdd_cam_5v: vdd-cam-5v-regulator { compatible = "regulator-fixed"; @@ -52,42 +70,43 @@ }; }; - +//CAM 0 +/* Link path: sensor->csi2_dcphy0->mipi0_csi2->rkcif_mipi_lvds--->rkcif_mipi_lvds_sditf->rkisp0_vir0 */ &i2c1 { - status = "disabled"; + status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&i2c1m2_xfer>; - imx415_0: imx415-0@1a { - compatible = "sony,imx415"; - status = "disabled"; - reg = <0x1a>; - clocks = <&ext_cam_37m_clk>; + ov50h40_0: ov50h40@10 { + compatible = "ov,ov50h40"; + status = "enabled"; + reg = <0x10>; + clocks = <&ext_cam_19d2m_clk>; clock-names = "xvclk"; avdd-supply = <&cam_avdd>; dovdd-supply = <&cam_dovdd>; dvdd-supply = <&cam_dvdd>; - reset-gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_LOW>; - rockchip,camera-module-index = <0>; + rockchip,camera-module-index = <1>; rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "CMK-OT2022-PX1"; - rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; + rockchip,camera-module-name = "Transsion"; + rockchip,camera-module-lens-name = "CM401-OIS"; port { - imx415_out0: endpoint { - remote-endpoint = <&dcphy0_in_imx415>; - data-lanes = <1 2 3 4>; + ov50h40_0_out0: endpoint { + remote-endpoint = <&dcphy0_in_ov50h40>; + data-lanes = <1 2 3>; }; }; }; }; &mipi_dcphy0 { - status = "disabled"; + status = "okay"; }; &csi2_dcphy0 { - status = "disabled"; + status = "okay"; ports { #address-cells = <1>; @@ -98,10 +117,10 @@ #address-cells = <1>; #size-cells = <0>; - dcphy0_in_imx415: endpoint@0 { + dcphy0_in_ov50h40: endpoint@0 { reg = <0>; - remote-endpoint = <&imx415_out0>; - data-lanes = <1 2 3 4>; + remote-endpoint = <&ov50h40_0_out0>; + data-lanes = <1 2 3>; }; }; @@ -119,7 +138,7 @@ }; &mipi0_csi2 { - status = "disabled"; + status = "okay"; ports { #address-cells = <1>; @@ -149,30 +168,30 @@ }; }; + +//CAM 1 +/* Link path: sensor->csi2_dcphy1->mipi1_csi2->rkcif_mipi_lvds1--->rkcif_mipi_lvds1_sditf->rkisp0_vir1 */ &i2c5 { - status = "disabled"; + status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&i2c5m3_xfer>; - imx415_1: imx415-1@1a { - compatible = "sony,imx415"; + gc5603_1: gc5603@31 { + compatible = "galaxycore,gc5603"; status = "disabled"; - reg = <0x1a>; - clocks = <&ext_cam_37m_clk>; + reg = <0x31>; + clocks = <&ext_cam_24m_clk>; clock-names = "xvclk"; - avdd-supply = <&cam_avdd>; - dovdd-supply = <&cam_dovdd>; - dvdd-supply = <&cam_dvdd>; - reset-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_LOW>; + pwdn-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_LOW>; rockchip,camera-module-index = <1>; rockchip,camera-module-facing = "back"; - rockchip,camera-module-name = "CMK-OT2022-PX1"; - rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; + rockchip,camera-module-name = "GCDS01A"; + rockchip,camera-module-lens-name = "default"; port { - imx415_out1: endpoint { - remote-endpoint = <&dcphy1_in_imx415>; - data-lanes = <1 2 3 4>; + gc5603_out1: endpoint { + remote-endpoint = <&dcphy1_in_gc5603>; + data-lanes = <1 2>; }; }; }; @@ -194,10 +213,10 @@ #address-cells = <1>; #size-cells = <0>; - dcphy1_in_imx415: endpoint@0 { + dcphy1_in_gc5603: endpoint@0 { reg = <0>; - remote-endpoint = <&imx415_out1>; - data-lanes = <1 2 3 4>; + remote-endpoint = <&gc5603_out1>; + data-lanes = <1 2>; }; }; @@ -245,12 +264,35 @@ }; }; + +//CAM2 +/* Link path: sensor->csi2_dphy0->mipi2_csi2->cif_mipi2--->rkcif_mipi_lvds2->rkisp1_vir0 */ &i2c6 { - status = "disabled"; + status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&i2c6m3_xfer>; - imx415_2: imx415-2@1a { + gc5603_2: gc5603@31 { + compatible = "galaxycore,gc5603"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_24m_clk>; + clock-names = "xvclk"; + pwdn-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_LOW>; + + rockchip,camera-module-index = <1>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "GCDS01A"; + rockchip,camera-module-lens-name = "default"; + port { + gc5603_out2: endpoint { + remote-endpoint = <&dphy0_in_gc5603>; + data-lanes = <1 2>; + }; + }; + }; + + imx415: imx415@1a { compatible = "sony,imx415"; status = "disabled"; reg = <0x1a>; @@ -272,14 +314,34 @@ }; }; }; + + gc8613: gc8613@31 { + compatible = "galaxycore,gc8613"; + status = "okay"; + reg = <0x31>; + clocks = <&ext_cam_24m_clk>; + clock-names = "xvclk"; + pwdn-gpios = <&gpio4 RK_PA7 GPIO_ACTIVE_LOW>; + + rockchip,camera-module-index = <3>; + rockchip,camera-module-facing = "back"; + rockchip,camera-module-name = "GCDS01A"; + rockchip,camera-module-lens-name = "default"; + port { + gc8613_out2: endpoint { + remote-endpoint = <&dphy0_in_gc8613>; + data-lanes = <1 2 3 4>; + }; + }; + }; }; &csi2_dphy0_hw { - status = "disabled"; + status = "okay"; }; &csi2_dphy0 { - status = "disabled"; + status = "okay"; ports { #address-cells = <1>; @@ -290,11 +352,23 @@ #address-cells = <1>; #size-cells = <0>; - dphy0_in_imx415: endpoint@0 { + dphy0_in_gc5603: endpoint@0 { reg = <0>; + remote-endpoint = <&gc5603_out2>; + data-lanes = <1 2>; + }; + + dphy0_in_imx415: endpoint@1 { + reg = <1>; remote-endpoint = <&imx415_out2>; data-lanes = <1 2 3 4>; }; + + dphy0_in_gc8613: endpoint@2 { + reg = <2>; + remote-endpoint = <&gc8613_out2>; + data-lanes = <1 2 3 4>; + }; }; port@1 { @@ -311,7 +385,7 @@ }; &mipi2_csi2 { - status = "disabled"; + status = "okay"; ports { #address-cells = <1>; @@ -343,15 +417,15 @@ &rkcif { - status = "disabled"; + status = "okay"; }; &rkcif_mmu { - status = "disabled"; + status = "okay"; }; &rkcif_mipi_lvds { - status = "disabled"; + status = "okay"; port { cif_mipi0_in0: endpoint { @@ -361,7 +435,7 @@ }; &rkcif_mipi_lvds_sditf { - status = "disabled"; + status = "okay"; port { mipi_lvds_sditf: endpoint { @@ -391,7 +465,7 @@ }; &rkcif_mipi_lvds2 { - status = "disabled"; + status = "okay"; port { cif_mipi2_in0: endpoint { @@ -401,7 +475,7 @@ }; &rkcif_mipi_lvds2_sditf { - status = "disabled"; + status = "okay"; port { mipi_lvds2_sditf: endpoint { @@ -419,7 +493,7 @@ }; &rkisp0_vir0 { - status = "disabled"; + status = "okay"; port { #address-cells = <1>; @@ -448,15 +522,15 @@ &rkisp1 { - status = "disabled"; + status = "okay"; }; &isp1_mmu { - status = "disabled"; + status = "okay"; }; &rkisp1_vir0 { - status = "disabled"; + status = "okay"; port { #address-cells = <1>; diff --git a/arch/arm64/configs/lubancat_linux_rk3588_defconfig b/arch/arm64/configs/lubancat_linux_rk3588_defconfig index ba311f60e9644..bc6e3a8c026e0 100644 --- a/arch/arm64/configs/lubancat_linux_rk3588_defconfig +++ b/arch/arm64/configs/lubancat_linux_rk3588_defconfig @@ -467,8 +467,10 @@ CONFIG_USB_VIDEO_CLASS=y CONFIG_V4L_PLATFORM_DRIVERS=y CONFIG_VIDEO_ROCKCHIP_CIF=y CONFIG_VIDEO_ROCKCHIP_ISP=y +CONFIG_VIDEO_ROCKCHIP_RKISP1=y CONFIG_VIDEO_ROCKCHIP_ISP_VERSION_V1X=y CONFIG_VIDEO_ROCKCHIP_ISP_VERSION_V21=y +CONFIG_VIDEO_ROCKCHIP_ISP_VERSION_V30=y CONFIG_VIDEO_ROCKCHIP_ISPP=y CONFIG_VIDEO_ROCKCHIP_HDMIRX=y CONFIG_V4L_MEM2MEM_DRIVERS=y @@ -490,7 +492,9 @@ CONFIG_VIDEO_OV5695=y CONFIG_VIDEO_OV7251=y CONFIG_VIDEO_OV8858=y CONFIG_VIDEO_OV13850=y -CONFIG_VIDEO_DW9714=y +CONFIG_VIDEO_GC5603=y +CONFIG_VIDEO_GC8613=y +CONFIG_VIDEO_OV50H40=y # CONFIG_VGA_ARB is not set CONFIG_DRM=y CONFIG_DRM_IGNORE_IOTCL_PERMIT=y diff --git a/arch/arm64/configs/rockchip_defconfig b/arch/arm64/configs/rockchip_defconfig index 7aae017d80a9f..8badd772afce5 100644 --- a/arch/arm64/configs/rockchip_defconfig +++ b/arch/arm64/configs/rockchip_defconfig @@ -592,6 +592,8 @@ CONFIG_VIDEO_TC35874X=y CONFIG_VIDEO_THCV244=y CONFIG_VIDEO_RK_IRCUT=y CONFIG_VIDEO_GC2053=y +CONFIG_VIDEO_GC5603=y +CONFIG_VIDEO_GC8613=y CONFIG_VIDEO_GC2093=y CONFIG_VIDEO_GC2145=y CONFIG_VIDEO_GC2385=y @@ -600,6 +602,7 @@ CONFIG_VIDEO_GC8034=y CONFIG_VIDEO_IMX415=y CONFIG_VIDEO_OV02B10=y CONFIG_VIDEO_OV50C40=y +CONFIG_VIDEO_OV50H40=y CONFIG_VIDEO_OV5695=y CONFIG_VIDEO_OV8858=y CONFIG_VIDEO_OV13850=y diff --git a/arch/arm64/configs/rockchip_linux_defconfig b/arch/arm64/configs/rockchip_linux_defconfig index a9639edaca7e2..9dd6af23db40d 100644 --- a/arch/arm64/configs/rockchip_linux_defconfig +++ b/arch/arm64/configs/rockchip_linux_defconfig @@ -35,7 +35,7 @@ CONFIG_ARCH_ROCKCHIP=y # CONFIG_ARM64_ERRATUM_1542419 is not set # CONFIG_ARM64_ERRATUM_1508412 is not set # CONFIG_ARM64_ERRATUM_2051678 is not set -# CONFIG_ARM64_ERRATUM_2054223 is not set +# CONFIG_ARM64_ERRATUM_2054223 is not setf # CONFIG_ARM64_ERRATUM_2067961 is not set # CONFIG_CAVIUM_ERRATUM_22375 is not set # CONFIG_CAVIUM_ERRATUM_23154 is not set @@ -317,6 +317,9 @@ CONFIG_VIDEO_RK628_BT1120=y CONFIG_VIDEO_TC35874X=y CONFIG_VIDEO_RK_IRCUT=y CONFIG_VIDEO_GC8034=y +CONFIG_VIDEO_GC5603=y +CONFIG_VIDEO_GC8613=y +CONFIG_VIDEO_OV50H40=y CONFIG_VIDEO_IMX415=y CONFIG_VIDEO_IMX464=y CONFIG_VIDEO_OS04A10=y diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 9f40652d5d731..534f53dcc5161 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -965,6 +965,28 @@ config VIDEO_GC08A3 To compile this driver as a module, choose M here: the module will be called gc08a3. +config VIDEO_GC5603 + tristate "GalaxyCore GC5603 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the GalaxyCore GC5603 sensor. + + To compile this driver as a module, choose M here: the + module will be called gc5603. + +config VIDEO_GC8613 + tristate "GalaxyCore GC8613 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + Support for the GalaxyCore GC8613 sensor. + + To compile this driver as a module, choose M here: the + module will be called gc8613. + config VIDEO_GC1084 tristate "GalaxyCore GC1084 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API @@ -1579,6 +1601,17 @@ config VIDEO_OV50C40 This is a Video4Linux2 sensor driver for the Omnivision OV50C40 camera sensor with a MIPI CSI-2 interface. +config VIDEO_OV50H40 + tristate "OmniVision OV50H40 sensor support" + depends on OF + depends on GPIOLIB && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + select VIDEO_OTP_EEPROM + help + This is a Video4Linux2 sensor driver for the Omnivision + OV50H40 camera sensor with a MIPI CSI-2 interface. + config VIDEO_OV5640 tristate "OmniVision OV5640 sensor support" depends on OF diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index ef76a5f4b0d34..34f2ed04a341d 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -77,6 +77,7 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_OS02G10) += os02g10.o obj-$(CONFIG_VIDEO_OS03B10) += os03b10.o obj-$(CONFIG_VIDEO_OS04A10) += os04a10.o +obj-$(CONFIG_VIDEO_OV50H40) += ov50h40.o obj-$(CONFIG_VIDEO_OS05A20) += os05a20.o obj-$(CONFIG_VIDEO_OS08A20) += os08a20.o obj-$(CONFIG_VIDEO_OV02B10) += ov02b10.o @@ -197,6 +198,8 @@ obj-$(CONFIG_VIDEO_GC4C33) += gc4c33.o obj-$(CONFIG_VIDEO_GC5025) += gc5025.o obj-$(CONFIG_VIDEO_GC5035) += gc5035.o obj-$(CONFIG_VIDEO_GC8034) += gc8034.o +obj-$(CONFIG_VIDEO_GC5603) += gc5603.o +obj-$(CONFIG_VIDEO_GC8613) += gc8613.o obj-$(CONFIG_VIDEO_HI556) += hi556.o obj-$(CONFIG_VIDEO_IMX214) += imx214.o obj-$(CONFIG_VIDEO_IMX214_EEPROM) += imx214_eeprom.o diff --git a/drivers/media/i2c/gc5603.c b/drivers/media/i2c/gc5603.c new file mode 100644 index 0000000000000..8db6c218d2f26 --- /dev/null +++ b/drivers/media/i2c/gc5603.c @@ -0,0 +1,1732 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * gc5603 driver + * + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 add poweron function. + * V0.0X01.0X02 fix mclk issue when probe multiple camera. + * V0.0X01.0X03 add enum_frame_interval function. + * V0.0X01.0X04 add quick stream on/off + * V0.0X01.0X05 add function g_mbus_config + * V0.0X01.0X06 + * 1. add 2lane support. + * 2. add some debug info. + * 3. adjust gc5603_g_mbus_config function. + * V0.0X01.0X07 support get channel info + * V0.0X01.0X08 + * 1. default support 2lane full 30fps. + * 2. default support rk otp spec. + * V0.0X01.0X09 adjust supply sequence to suit spec + */ +//#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OIS_On +#ifdef OIS_On +#define CM421_SLAVE_ADDR 0x30 +#define CM421_REG_VALUE_16bit 2 +#endif +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x08) +#define GC5603_NAME "gc5603" + +#define MIPI_FREQ_848M 423000000 + +#define GC5603_PAGE_SELECT 0xFE + +#define GC5603_REG_CHIP_ID_H 0x03F0 +#define GC5603_REG_CHIP_ID_L 0x03F1 + +#define GC5603_REG_EXP_H 0x0202 +#define GC5603_REG_EXP_L 0x0203 + +#define GC5603_REG_VTS_H 0x0340 +#define GC5603_REG_VTS_L 0x0341 + +#define GC5603_REG_CTRL_MODE 0x0100 +#define GC5603_MODE_SW_STANDBY 0x00 +#define GC5603_MODE_STREAMING 0x09 + +#define REG_NULL 0xFFFF + +#define GC5603_CHIP_ID 0x5603 + +#define GC5603_VTS_MAX 0x7fff +#define GC5603_HTS_MAX 0xFFF + +#define GC5603_EXPOSURE_MAX 0x3FFF +#define GC5603_EXPOSURE_MIN 1 +#define GC5603_EXPOSURE_STEP 1 + +#define GC5603_GAIN_MIN 64 +#define GC5603_GAIN_MAX 0xffff +#define GC5603_GAIN_STEP 1 +#define GC5603_GAIN_DEFAULT 64 + +#define gc5603_REG_VALUE_08BIT 1 +#define gc5603_REG_VALUE_16BIT 2 +#define gc5603_REG_VALUE_24BIT 3 + + +#define GC5603_LANES 2 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + + + +#define GC5603_FLIP_MIRROR_REG 0x0101 + +#define GC_MIRROR_BIT_MASK BIT(0) +#define GC_FLIP_BIT_MASK BIT(1) + +#define GC5603_XVCLK_FREQ_24M 24000000 +#define GC5603_XVCLK_FREQ_27M 27000000 + +static const char * const gc5603_supply_names[] = { + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ + "dvdd", /* Digital core power */ +}; + +#define GC5603_NUM_SUPPLIES ARRAY_SIZE(gc5603_supply_names) + +#define to_gc5603(sd) container_of(sd, struct gc5603, subdev) + +struct regval { + u16 addr; + u8 val; +}; + +struct gc5603_mode { + u32 bus_fmt; + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; + u32 hdr_mode; + u32 vc[PAD_MAX]; + u32 xvclk; +}; + +struct gc5603 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct gpio_desc *pwren_gpio; + struct regulator_bulk_data supplies[GC5603_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *h_flip; + struct v4l2_ctrl *v_flip; + struct mutex mutex; + bool streaming; + unsigned int lane_num; + unsigned int cfg_num; + unsigned int pixel_rate; + bool power_on; + const struct gc5603_mode *cur_mode; + + + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + struct rkmodule_awb_cfg awb_cfg; + struct rkmodule_lsc_cfg lsc_cfg; + u32 flip; +}; + +static const struct regval gc5603_2960x1666_regs_2lane[] = { +//version 1.3 +//mclk 27Mhz +//mipi 2 lane 846Mbps/lane +//vts = 1750 ,row_time=19.05us +//window 2960x1666 +//BGGR + {0x03fe, 0xf0}, + {0x03fe, 0x00}, + {0x03fe, 0x10}, + {0x03fe, 0x00}, + {0x0a38, 0x02}, + {0x0a38, 0x03}, + {0x0a20, 0x07}, + {0x061b, 0x03}, + {0x061c, 0x50}, + {0x061d, 0x05}, + {0x061e, 0x70}, + {0x061f, 0x03}, + {0x0a21, 0x08}, + {0x0a34, 0x40}, + {0x0a35, 0x11}, + {0x0a36, 0x5e}, + {0x0a37, 0x03}, + {0x0314, 0x50}, + {0x0315, 0x32}, + {0x031c, 0xce}, + {0x0219, 0x57}, + {0x0342, 0x04}, + {0x0343, 0xb0}, + {0x0340, 0x06}, + {0x0341, 0xd6}, + {0x0345, 0x02}, + {0x0347, 0x02}, + {0x0348, 0x0b}, + {0x0349, 0x98}, + {0x034a, 0x06}, + {0x034b, 0x8a}, + {0x0094, 0x0b}, + {0x0095, 0x90}, + {0x0096, 0x06}, + {0x0097, 0x82}, + {0x0099, 0x04}, + {0x009b, 0x04}, + {0x060c, 0x01}, + {0x060e, 0xd2}, + {0x060f, 0x05}, + {0x070c, 0x01}, + {0x070e, 0xd2}, + {0x070f, 0x05}, + {0x0709, 0x40}, + {0x0719, 0x40}, + {0x0909, 0x07}, + {0x0902, 0x04}, + {0x0904, 0x0b}, + {0x0907, 0x54}, + {0x0908, 0x06}, + {0x0903, 0x9d}, + {0x072a, 0x1c}, + {0x072b, 0x1c}, + {0x0724, 0x2b}, + {0x0727, 0x2b}, + {0x1466, 0x18}, + {0x1467, 0x15}, + {0x1468, 0x15}, + {0x1469, 0x70}, + {0x146a, 0xe8}, + {0x0707, 0x07}, + {0x0737, 0x0f}, + {0x0704, 0x01}, + {0x0706, 0x02}, + {0x0716, 0x02}, + {0x0708, 0xc8}, + {0x0718, 0xc8}, + {0x061a, 0x02}, + {0x1430, 0x80}, + {0x1407, 0x10}, + {0x1408, 0x16}, + {0x1409, 0x03}, + {0x1438, 0x01}, + {0x02ce, 0x03}, + {0x0245, 0xc9}, + {0x023a, 0x08}, + {0x02cd, 0x88}, + {0x0612, 0x02}, + {0x0613, 0xc7}, + {0x0243, 0x03}, + {0x0089, 0x03}, + {0x0002, 0xab}, + {0x0040, 0xa3}, + {0x0075, 0x64}, + {0x0004, 0x0f}, + {0x0053, 0x0a}, + {0x0205, 0x0c}, + {0x0a67, 0x80}, + {0x0a54, 0x0e}, + {0x0a65, 0x10}, + {0x0a98, 0x04}, + {0x05be, 0x00}, + {0x05a9, 0x01}, + {0x0023, 0x00}, + {0x0022, 0x00}, + {0x0025, 0x00}, + {0x0024, 0x00}, + {0x0028, 0x0b}, + {0x0029, 0x98}, + {0x002a, 0x06}, + {0x002b, 0x86}, + {0x0a83, 0xe0}, + {0x0a72, 0x02}, + {0x0a73, 0x60}, + {0x0a75, 0x41}, + {0x0a70, 0x03}, + {0x0a5a, 0x80}, + {0x0181, 0x30}, + {0x0182, 0x05}, + {0x0185, 0x01}, + {0x0180, 0x46}, + {0x0100, 0x08}, + {0x010d, 0x74}, + {0x010e, 0x0e}, + {0x0113, 0x02}, + {0x0114, 0x01}, + {0x0115, 0x10}, + {0x0100, 0x09}, + {0x0a70, 0x00}, + {0x0080, 0x02}, + {0x0a67, 0x00}, + {0x0052, 0x02}, + {0x0076, 0x01}, + {0x021a, 0x10}, + {0x0049, 0x0f}, + {0x004a, 0x3c}, + {0x004b, 0x00}, + {0x0430, 0x25}, + {0x0431, 0x25}, + {0x0432, 0x25}, + {0x0433, 0x25}, + {0x0434, 0x59}, + {0x0435, 0x59}, + {0x0436, 0x59}, + {0x0437, 0x59}, + + {0x0100, 0x09}, + {REG_NULL, 0x00}, +}; + +static const struct gc5603_mode supported_modes[] = { + { + .bus_fmt = MEDIA_BUS_FMT_SGRBG10_1X10, + .width = 2960, + .height = 1666, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x6ce, + .hts_def = 0x0C80, + .vts_def = 0x06D6, + .reg_list = gc5603_2960x1666_regs_2lane, + .hdr_mode = NO_HDR, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = GC5603_XVCLK_FREQ_24M + }, +}; + +static const s64 link_freq_menu_items[] = { + MIPI_FREQ_848M +}; + +static int gc5603_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int gc5603_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = gc5603_write_reg(client, regs[i].addr, + gc5603_REG_VALUE_08BIT, regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int gc5603_read_reg(struct i2c_client *client, u16 reg, unsigned int len, + u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + + return 0; + dev_info(&client->dev, + "gc5603 read reg(0x%x val:0x%x) \n", reg, *val); +} + +#ifdef OIS_On +static int CM421_write_reg(struct i2c_client *client, + unsigned char dev_addr, + u16 reg, + unsigned int len, + u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + int ret; + + struct i2c_msg message; + + if (len > 4) + return -EINVAL; + + buf[0] = (reg >> 8) & 0xFF;//寄存器地址 + buf[1] = (reg) & 0xFF;//寄存器地址 + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + message.addr = dev_addr;//设备地址 + message.buf = buf; + message.flags = 0;//写标志 + message.len = 4; + + ret = i2c_transfer(client->adapter, &message, 1); + if (ret < 0) + { + dev_err(&client->dev, "Failed to write 0x%x,0x%x\n", reg, val); + return -EIO; + } + + return 0; +} + +static int CM421_read_reg(struct i2c_client *client, + unsigned char dev_addr, + u16 reg, + unsigned int len, + u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = dev_addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = dev_addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} +#endif +static int gc5603_get_reso_dist(const struct gc5603_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct gc5603_mode * +gc5603_find_best_fit(struct gc5603 *gc5603, struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < gc5603->cfg_num; i++) { + dist = gc5603_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist <= cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static uint8_t regValTable[26][7] = { + //0614, 0615, 0225, 1467 1468, 00b8, 00b9 + { 0x00, 0x00, 0x04, 0x15, 0x15, 0x01, 0x00}, + { 0x90, 0x02, 0x04, 0x15, 0x15, 0x01, 0x0A}, + { 0x00, 0x00, 0x00, 0x15, 0x15, 0x01, 0x12}, + { 0x90, 0x02, 0x00, 0x15, 0x15, 0x01, 0x20}, + { 0x01, 0x00, 0x00, 0x15, 0x15, 0x01, 0x30}, + { 0x91, 0x02, 0x00, 0x15, 0x15, 0x02, 0x05}, + { 0x02, 0x00, 0x00, 0x15, 0x15, 0x02, 0x19}, + { 0x92, 0x02, 0x00, 0x16, 0x16, 0x02, 0x3F}, + { 0x03, 0x00, 0x00, 0x16, 0x16, 0x03, 0x20}, + { 0x93, 0x02, 0x00, 0x17, 0x17, 0x04, 0x0A}, + { 0x00, 0x00, 0x01, 0x18, 0x18, 0x05, 0x02}, + { 0x90, 0x02, 0x01, 0x19, 0x19, 0x05, 0x39}, + { 0x01, 0x00, 0x01, 0x19, 0x19, 0x06, 0x3C}, + { 0x91, 0x02, 0x01, 0x19, 0x19, 0x08, 0x0D}, + { 0x02, 0x00, 0x01, 0x1a, 0x1a, 0x09, 0x21}, + { 0x92, 0x02, 0x01, 0x1a, 0x1a, 0x0B, 0x0F}, + { 0x03, 0x00, 0x01, 0x1c, 0x1c, 0x0D, 0x17}, + { 0x93, 0x02, 0x01, 0x1c, 0x1c, 0x0F, 0x33}, + { 0x04, 0x00, 0x01, 0x1d, 0x1d, 0x12, 0x30}, + { 0x94, 0x02, 0x01, 0x1d, 0x1d, 0x16, 0x10}, + { 0x05, 0x00, 0x01, 0x1e, 0x1e, 0x1A, 0x19}, + { 0x95, 0x02, 0x01, 0x1e, 0x1e, 0x1F, 0x13}, + { 0x06, 0x00, 0x01, 0x20, 0x20, 0x25, 0x08}, + { 0x96, 0x02, 0x01, 0x20, 0x20, 0x2C, 0x03}, + { 0xb6, 0x04, 0x01, 0x20, 0x20, 0x34, 0x0F}, + { 0x86, 0x06, 0x01, 0x20, 0x20, 0x3D, 0x3D}, +}; + +static uint32_t gain_level_table[27] = { + 64, + 74, + 82, + 96, + 112, + 133, + 153, + 191, + 224, + 266, + 322, + 377, + 444, + 525, + 609, + 719, + 855, + 1011, + 1200, + 1424, + 1689, + 2003, + + 2376, + 2819, + + 3343, + 3965, + 0xffffffff, +}; +//static int total = sizeof(gain_level_table) / sizeof(uint32_t); + +static int gc5603_set_gain(struct gc5603 *gc5603, u32 gain) +{ + int ret; + uint16_t i = 0; + uint16_t total = 0; + uint16_t temp = 0; +#ifdef OIS_On + int tmp1, tmp2; + struct device *dev = &gc5603->client->dev; +#endif + + for (i = 0; i < total; i++) { + if ((gain_level_table[i] <= gain) && (gain < gain_level_table[i+1])) + break; + } + + if((gain>3965)||(gain==3965)) + i =25; + + + ret = gc5603_write_reg(gc5603->client, 0x031d,gc5603_REG_VALUE_08BIT, 0x2d); + ret = gc5603_write_reg(gc5603->client, 0x0614,gc5603_REG_VALUE_08BIT,regValTable[i][0]); + ret = gc5603_write_reg(gc5603->client, 0x0615,gc5603_REG_VALUE_08BIT,regValTable[i][1]); + ret = gc5603_write_reg(gc5603->client, 0x0225,gc5603_REG_VALUE_08BIT,regValTable[i][2]); + + ret = gc5603_write_reg(gc5603->client, 0x031d,gc5603_REG_VALUE_08BIT, 0x28); + ret = gc5603_write_reg(gc5603->client, 0x1467,gc5603_REG_VALUE_08BIT,regValTable[i][3]); + ret = gc5603_write_reg(gc5603->client, 0x1468,gc5603_REG_VALUE_08BIT,regValTable[i][4]); + ret = gc5603_write_reg(gc5603->client, 0x00b8,gc5603_REG_VALUE_08BIT,regValTable[i][5]); + ret = gc5603_write_reg(gc5603->client, 0x00b9,gc5603_REG_VALUE_08BIT,regValTable[i][6]); + + + temp = 64 * gain / gain_level_table[i]; + + //dev_warn(&client->dev, "gc5603_set_gain gain=%d,i=%d,temp=%d \n", gain, i, temp); + + ret |= gc5603_write_reg(gc5603->client, 0x0064,gc5603_REG_VALUE_08BIT,(temp >> 6)); + ret |= gc5603_write_reg(gc5603->client, 0x0065,gc5603_REG_VALUE_08BIT,((temp&0x3f) << 2) ); +#ifdef OIS_On + CM421_read_reg(gc5603->client, CM421_SLAVE_ADDR, 0x99ce, CM421_REG_VALUE_16bit, &tmp1); + dev_info(dev, "position 0x99ce= 0x%x\n", tmp1); + CM421_read_reg(gc5603->client, CM421_SLAVE_ADDR, 0x99d0, CM421_REG_VALUE_16bit, &tmp2); + dev_info(dev, "position 0x99d0= 0x%x\n", tmp2); +#endif + + + return ret; +} + +static int gc5603_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc5603 *gc5603 = container_of(ctrl->handler, + struct gc5603, ctrl_handler); + struct i2c_client *client = gc5603->client; + s64 max; + int ret = 0; + u32 vts = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = gc5603->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(gc5603->exposure, + gc5603->exposure->minimum, max, + gc5603->exposure->step, + gc5603->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + + #if 1 + ret = gc5603_write_reg(gc5603->client, GC5603_REG_EXP_H,gc5603_REG_VALUE_08BIT, + (ctrl->val >> 8)); + ret |= gc5603_write_reg(gc5603->client, GC5603_REG_EXP_L,gc5603_REG_VALUE_08BIT, + ctrl->val & 0xff); + #endif + + break; + case V4L2_CID_ANALOGUE_GAIN: + gc5603_set_gain(gc5603, ctrl->val); + break; + case V4L2_CID_VBLANK: + vts = ctrl->val + gc5603->cur_mode->height; + + #if 1 + ret = gc5603_write_reg(gc5603->client, GC5603_REG_VTS_H,gc5603_REG_VALUE_08BIT,(vts >> 8)); + ret |= gc5603_write_reg(gc5603->client, GC5603_REG_VTS_L, gc5603_REG_VALUE_08BIT,vts & 0xff); + #endif + break; + case V4L2_CID_HFLIP: + if (ctrl->val) + gc5603->flip |= GC_MIRROR_BIT_MASK; + else + gc5603->flip &= ~GC_MIRROR_BIT_MASK; + break; + case V4L2_CID_VFLIP: + if (ctrl->val) + gc5603->flip |= GC_FLIP_BIT_MASK; + else + gc5603->flip &= ~GC_FLIP_BIT_MASK; + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + return ret; +} + +static const struct v4l2_ctrl_ops gc5603_ctrl_ops = { + .s_ctrl = gc5603_set_ctrl, +}; + +static int gc5603_configure_regulators(struct gc5603 *gc5603) +{ + unsigned int i; + + for (i = 0; i < GC5603_NUM_SUPPLIES; i++) + gc5603->supplies[i].supply = gc5603_supply_names[i]; + + return devm_regulator_bulk_get(&gc5603->client->dev, + GC5603_NUM_SUPPLIES, + gc5603->supplies); +} + +static int gc5603_parse_of(struct gc5603 *gc5603) +{ + struct device *dev = &gc5603->client->dev; + struct device_node *endpoint; + struct fwnode_handle *fwnode; + int rval; + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "Failed to get endpoint\n"); + return -EINVAL; + } + fwnode = of_fwnode_handle(endpoint); + rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0); + if (rval <= 0) { + dev_warn(dev, " Get mipi lane num failed!\n"); + return -1; + } + + gc5603->lane_num = rval; + if (2 == gc5603->lane_num) { + gc5603->cur_mode = &supported_modes[0]; + gc5603->cfg_num = ARRAY_SIZE(supported_modes); + + /*pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + gc5603->pixel_rate = MIPI_FREQ_848M * 2U * (gc5603->lane_num) / 10U; + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + gc5603->lane_num, gc5603->pixel_rate); + } else { + dev_info(dev, "gc5603 can not support the lane num(%d)\n", gc5603->lane_num); + } + return 0; +} + +static int gc5603_initialize_controls(struct gc5603 *gc5603) +{ + const struct gc5603_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &gc5603->ctrl_handler; + mode = gc5603->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &gc5603->mutex; + + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, gc5603->pixel_rate, 1, gc5603->pixel_rate); + + h_blank = mode->hts_def - mode->width; + gc5603->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (gc5603->hblank) + gc5603->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + gc5603->vblank = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + GC5603_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 4; + gc5603->exposure = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_EXPOSURE, + GC5603_EXPOSURE_MIN, + exposure_max, + GC5603_EXPOSURE_STEP, + mode->exp_def); + + gc5603->anal_gain = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + GC5603_GAIN_MIN, + GC5603_GAIN_MAX, + GC5603_GAIN_STEP, + GC5603_GAIN_DEFAULT); + + gc5603->h_flip = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + gc5603->v_flip = v4l2_ctrl_new_std(handler, &gc5603_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + gc5603->flip = 0; + + if (handler->error) { + ret = handler->error; + dev_err(&gc5603->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + gc5603->subdev.ctrl_handler = handler; + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 gc5603_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, GC5603_XVCLK_FREQ_24M / 1000 / 1000); +} + +static int __gc5603_power_on(struct gc5603 *gc5603) +{ + int ret; + u32 delay_us; + struct device *dev = &gc5603->client->dev; + + if (!IS_ERR_OR_NULL(gc5603->pins_default)) { + ret = pinctrl_select_state(gc5603->pinctrl, + gc5603->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + ret = clk_set_rate(gc5603->xvclk, GC5603_XVCLK_FREQ_24M); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(gc5603->xvclk) != GC5603_XVCLK_FREQ_24M) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(gc5603->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 0); + + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 0); + + usleep_range(500, 1000); + ret = regulator_bulk_enable(GC5603_NUM_SUPPLIES, gc5603->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + if (!IS_ERR(gc5603->pwren_gpio)) + gpiod_set_value_cansleep(gc5603->pwren_gpio, 1); + + usleep_range(1000, 1100); + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 1); + usleep_range(100, 150); + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = gc5603_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + return 0; + +disable_clk: + clk_disable_unprepare(gc5603->xvclk); + return ret; +} + +static void __gc5603_power_off(struct gc5603 *gc5603) +{ + int ret; + struct device *dev = &gc5603->client->dev; + + if (!IS_ERR(gc5603->pwdn_gpio)) + gpiod_set_value_cansleep(gc5603->pwdn_gpio, 0); + clk_disable_unprepare(gc5603->xvclk); + + if (!IS_ERR(gc5603->reset_gpio)) + gpiod_set_value_cansleep(gc5603->reset_gpio, 0); + + if (!IS_ERR_OR_NULL(gc5603->pins_sleep)) { + ret = pinctrl_select_state(gc5603->pinctrl, + gc5603->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + regulator_bulk_disable(GC5603_NUM_SUPPLIES, gc5603->supplies); + if (!IS_ERR(gc5603->pwren_gpio)) + gpiod_set_value_cansleep(gc5603->pwren_gpio, 0); + +} + + static int gc5603_check_sensor_id(struct gc5603 *gc5603, + struct i2c_client *client) +{ + struct device *dev = &gc5603->client->dev; + u16 id = 0; + u32 reg_H = 0; + u32 reg_L = 0; + int ret; + + ret = gc5603_read_reg(client, GC5603_REG_CHIP_ID_H, + gc5603_REG_VALUE_08BIT, ®_H); + ret |= gc5603_read_reg(client, GC5603_REG_CHIP_ID_L, + gc5603_REG_VALUE_08BIT, ®_L); + + id = ((reg_H << 8) & 0xff00) | (reg_L & 0xff); + if (!(reg_H == (GC5603_CHIP_ID >> 8) || reg_L == (GC5603_CHIP_ID & 0xff))) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + dev_info(dev, "detected gc%04x sensor\n", id); + return 0; +} + +static int gc5603_set_flip(struct gc5603 *gc5603, u8 mode) +{ +u32 match_reg = 0; + + gc5603_read_reg(gc5603->client, GC5603_FLIP_MIRROR_REG,gc5603_REG_VALUE_08BIT,&match_reg); + + if (mode == GC_FLIP_BIT_MASK) { + match_reg |= GC_FLIP_BIT_MASK; + match_reg &= ~GC_MIRROR_BIT_MASK; + } else if (mode == GC_MIRROR_BIT_MASK) { + match_reg |= GC_MIRROR_BIT_MASK; + match_reg &= ~GC_FLIP_BIT_MASK; + } else if (mode == (GC_MIRROR_BIT_MASK | + GC_FLIP_BIT_MASK)) { + match_reg |= GC_FLIP_BIT_MASK; + match_reg |= GC_MIRROR_BIT_MASK; + } else { + match_reg &= ~GC_FLIP_BIT_MASK; + match_reg &= ~GC_MIRROR_BIT_MASK; + } + return gc5603_write_reg(gc5603->client, GC5603_FLIP_MIRROR_REG,gc5603_REG_VALUE_08BIT ,match_reg); +} + +static int __gc5603_start_stream(struct gc5603 *gc5603) +{ + int ret; + + ret = gc5603_write_array(gc5603->client, gc5603->cur_mode->reg_list); + if (ret) + return ret; + + usleep_range(1000, 1100); + + + gc5603_write_reg(gc5603->client, 0x0a70,gc5603_REG_VALUE_08BIT, 0x00); + gc5603_write_reg(gc5603->client, 0x0080,gc5603_REG_VALUE_08BIT, 0x02); + gc5603_write_reg(gc5603->client, 0x0a67,gc5603_REG_VALUE_08BIT, 0x00); + + + + + /* In case these controls are set before streaming */ + mutex_unlock(&gc5603->mutex); + ret = __v4l2_ctrl_handler_setup(&gc5603->ctrl_handler); + mutex_lock(&gc5603->mutex); + + ret = gc5603_set_flip(gc5603, gc5603->flip); + if (ret) + return ret; + return gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_STREAMING); +} + +static int __gc5603_stop_stream(struct gc5603 *gc5603) +{ + return gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_SW_STANDBY); +} + +static void gc5603_get_module_inf(struct gc5603 *gc5603, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, GC5603_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, gc5603->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, gc5603->len_name, sizeof(inf->base.lens)); +} + +static int gc5603_get_channel_info(struct gc5603 *gc5603, struct rkmodule_channel_info *ch_info) +{ + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + ch_info->vc = gc5603->cur_mode->vc[ch_info->index]; + ch_info->width = gc5603->cur_mode->width; + ch_info->height = gc5603->cur_mode->height; + ch_info->bus_fmt = gc5603->cur_mode->bus_fmt; + return 0; +} + +static void gc5603_set_awb_cfg(struct gc5603 *gc5603, + struct rkmodule_awb_cfg *cfg) +{ + mutex_lock(&gc5603->mutex); + memcpy(&gc5603->awb_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&gc5603->mutex); +} + +static void gc5603_set_lsc_cfg(struct gc5603 *gc5603, + struct rkmodule_lsc_cfg *cfg) +{ + mutex_lock(&gc5603->mutex); + memcpy(&gc5603->lsc_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&gc5603->mutex); +} + +static long gc5603_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + long ret = 0; + struct rkmodule_hdr_cfg *hdr_cfg; + u32 stream = 0; + struct rkmodule_channel_info *ch_info; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + gc5603_get_module_inf(gc5603, (struct rkmodule_inf *)arg); + break; + case RKMODULE_GET_HDR_CFG: + hdr_cfg = (struct rkmodule_hdr_cfg *)arg; + hdr_cfg->esp.mode = HDR_NORMAL_VC; + hdr_cfg->hdr_mode = gc5603->cur_mode->hdr_mode; + break; + case RKMODULE_SET_HDR_CFG: + case RKMODULE_SET_CONVERSION_GAIN: + break; + + case RKMODULE_AWB_CFG: + gc5603_set_awb_cfg(gc5603, (struct rkmodule_awb_cfg *)arg); + break; + case RKMODULE_LSC_CFG: + gc5603_set_lsc_cfg(gc5603, (struct rkmodule_lsc_cfg *)arg); + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_STREAMING); + else + ret = gc5603_write_reg(gc5603->client, GC5603_REG_CTRL_MODE,gc5603_REG_VALUE_08BIT, + GC5603_MODE_SW_STANDBY); + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = gc5603_get_channel_info(gc5603, ch_info); + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} + +#ifdef CONFIG_COMPAT +static long gc5603_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *awb_cfg; + struct rkmodule_lsc_cfg *lsc_cfg; + struct rkmodule_hdr_cfg *hdr; + long ret = 0; + u32 cg = 0; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = gc5603_ioctl(sd, cmd, inf); + if (!ret) + ret = copy_to_user(up, inf, sizeof(*inf)); + kfree(inf); + break; + case RKMODULE_AWB_CFG: + awb_cfg = kzalloc(sizeof(*awb_cfg), GFP_KERNEL); + if (!awb_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(awb_cfg, up, sizeof(*awb_cfg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, awb_cfg); + kfree(awb_cfg); + break; + case RKMODULE_LSC_CFG: + lsc_cfg = kzalloc(sizeof(*lsc_cfg), GFP_KERNEL); + if (!lsc_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(lsc_cfg, up, sizeof(*lsc_cfg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, lsc_cfg); + kfree(lsc_cfg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = gc5603_ioctl(sd, cmd, hdr); + if (!ret) + ret = copy_to_user(up, hdr, sizeof(*hdr)); + kfree(hdr); + break; + case RKMODULE_SET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(hdr, up, sizeof(*hdr)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, hdr); + kfree(hdr); + break; + case RKMODULE_SET_CONVERSION_GAIN: + ret = copy_from_user(&cg, up, sizeof(cg)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, &cg); + break; + case RKMODULE_SET_QUICK_STREAM: + ret = copy_from_user(&stream, up, sizeof(u32)); + if (!ret) + ret = gc5603_ioctl(sd, cmd, &stream); + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} +#endif + +static int gc5603_s_stream(struct v4l2_subdev *sd, int on) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct i2c_client *client = gc5603->client; + struct device *dev = &gc5603->client->dev; + int ret = 0; +#ifdef OIS_On + int value2; +#endif + + mutex_lock(&gc5603->mutex); + on = !!on; + if (on == gc5603->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __gc5603_start_stream(gc5603); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __gc5603_stop_stream(gc5603); + pm_runtime_put(&client->dev); + } + + gc5603->streaming = on; +#ifdef OIS_On + //Init OIS Function: + CM421_write_reg(gc5603->client, CM421_SLAVE_ADDR, 0x0018, CM421_REG_VALUE_16bit, 0x0001); + msleep(100); + CM421_write_reg(gc5603->client, CM421_SLAVE_ADDR, 0x9e18, CM421_REG_VALUE_16bit, 0x0002); + msleep(10); + CM421_write_reg(gc5603->client, CM421_SLAVE_ADDR, 0x0024, CM421_REG_VALUE_16bit, 0x0001); + msleep(400); + CM421_write_reg(gc5603->client, CM421_SLAVE_ADDR, 0x9b2c, CM421_REG_VALUE_16bit, 0x0001); + msleep(100); + CM421_write_reg(gc5603->client, CM421_SLAVE_ADDR, 0x9b2a, CM421_REG_VALUE_16bit, 0x0001); + msleep(200); + CM421_read_reg(gc5603->client, CM421_SLAVE_ADDR, 0x9b28, CM421_REG_VALUE_16bit, &value2); + dev_info(dev, "0x9b28 = 0x%x\n", value2); +#endif + +unlock_and_return: + mutex_unlock(&gc5603->mutex); + return 0; +} + +static int gc5603_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + + mutex_lock(&gc5603->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&gc5603->mutex); + + return 0; +} + +static int gc5603_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + u32 val = 0; + + if (mode->hdr_mode == NO_HDR) + val = 1 << (GC5603_LANES - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + config->type = V4L2_MBUS_CSI2_DPHY; + config->flags = val; + return 0; +} + +static int gc5603_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = supported_modes[code->index].bus_fmt; + return 0; +} + +static int gc5603_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + + if (fse->index >= gc5603->cfg_num) + return -EINVAL; + + if (fse->code != supported_modes[fse->index].bus_fmt) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + return 0; +} + +#define DST_WIDTH 2720 +#define DST_HEIGHT 1616 + +/* + * The resolution of the driver configuration needs to be exactly + * the same as the current output resolution of the sensor, + * the input width of the isp needs to be 16 aligned, + * the input height of the isp needs to be 8 aligned. + * Can be cropped to standard resolution by this function, + * otherwise it will crop out strange resolution according + * to the alignment rules. + */ +static int gc5603_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + sel->r.left =120; + sel->r.width = DST_WIDTH; + sel->r.top = 25; + sel->r.height = DST_HEIGHT; + return 0; + } + return -EINVAL; +} + +static int gc5603_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + + if (fie->index >= gc5603->cfg_num) + return -EINVAL; + + fie->code = supported_modes[fie->index].bus_fmt; + fie->width = supported_modes[fie->index].width; + fie->height = supported_modes[fie->index].height; + fie->interval = supported_modes[fie->index].max_fps; + fie->reserved[0] = supported_modes[fie->index].hdr_mode; + return 0; +} + +static int gc5603_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&gc5603->mutex); + + mode = gc5603_find_best_fit(gc5603, fmt); + fmt->format.code = mode->bus_fmt; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&gc5603->mutex); + return -ENOTTY; +#endif + } else { + gc5603->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(gc5603->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(gc5603->vblank, vblank_def, + GC5603_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&gc5603->mutex); + return 0; +} + +static int gc5603_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + const struct gc5603_mode *mode = gc5603->cur_mode; + + mutex_lock(&gc5603->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&gc5603->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->bus_fmt; + fmt->format.field = V4L2_FIELD_NONE; + + /* format info: width/height/data type/virctual channel */ + if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR) + fmt->reserved[0] = mode->vc[fmt->pad]; + else + fmt->reserved[0] = mode->vc[PAD0]; + + } + mutex_unlock(&gc5603->mutex); + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int gc5603_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct gc5603_mode *def_mode = &supported_modes[0]; + + mutex_lock(&gc5603->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&gc5603->mutex); + /* No crop or compose */ + return 0; +} +#endif + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops gc5603_internal_ops = { + .open = gc5603_open, +}; +#endif + +static int gc5603_s_power(struct v4l2_subdev *sd, int on) +{ + struct gc5603 *gc5603 = to_gc5603(sd); + struct i2c_client *client = gc5603->client; + int ret = 0; + + mutex_lock(&gc5603->mutex); + + /* If the power state is not modified - no work to do. */ + if (gc5603->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + gc5603->power_on = true; + } else { + pm_runtime_put(&client->dev); + gc5603->power_on = false; + } + +unlock_and_return: + mutex_unlock(&gc5603->mutex); + + return ret; +} + +static const struct v4l2_subdev_core_ops gc5603_core_ops = { + .s_power = gc5603_s_power, + .ioctl = gc5603_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = gc5603_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops gc5603_video_ops = { + .s_stream = gc5603_s_stream, + .g_frame_interval = gc5603_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops gc5603_pad_ops = { + .enum_mbus_code = gc5603_enum_mbus_code, + .enum_frame_size = gc5603_enum_frame_sizes, + .enum_frame_interval = gc5603_enum_frame_interval, + .get_fmt = gc5603_get_fmt, + .set_fmt = gc5603_set_fmt, + .get_selection = gc5603_get_selection, + .get_mbus_config = gc5603_g_mbus_config, +}; + +static const struct v4l2_subdev_ops gc5603_subdev_ops = { + .core = &gc5603_core_ops, + .video = &gc5603_video_ops, + .pad = &gc5603_pad_ops, +}; + +static int gc5603_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + __gc5603_power_on(gc5603); + return 0; +} + +static int gc5603_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + __gc5603_power_off(gc5603); + return 0; +} + +static const struct dev_pm_ops gc5603_pm_ops = { + SET_RUNTIME_PM_OPS(gc5603_runtime_suspend, + gc5603_runtime_resume, NULL) +}; + +static int gc5603_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct gc5603 *gc5603; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + gc5603 = devm_kzalloc(dev, sizeof(*gc5603), GFP_KERNEL); + if (!gc5603) + return -ENOMEM; + + gc5603->client = client; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &gc5603->module_index); + + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &gc5603->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &gc5603->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &gc5603->len_name); + dev_info(dev, "Module Information: index = %d, Facing = %s, ModuleName = %s, LensName = %s", gc5603->module_index, gc5603->module_facing, gc5603->module_name, gc5603->len_name); + if (ret) { + dev_err(dev, + "could not get module information!\n"); + return -EINVAL; + } + + gc5603->xvclk = devm_clk_get(&client->dev, "xvclk"); + if (IS_ERR(gc5603->xvclk)) { + dev_err(&client->dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + gc5603->pwren_gpio = devm_gpiod_get(dev, "pwren", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->pwdn_gpio)) + dev_warn(dev, "Failed to get pwren-gpios\n"); + + gc5603->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->reset_gpio)) + dev_info(dev, "Failed to get reset-gpios, maybe no used\n"); + + gc5603->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(gc5603->pwdn_gpio)) + dev_warn(dev, "Failed to get power-gpios\n"); + + + + ret = gc5603_parse_of(gc5603); + if (ret != 0) + return -EINVAL; + + gc5603->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(gc5603->pinctrl)) { + gc5603->pins_default = + pinctrl_lookup_state(gc5603->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(gc5603->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + gc5603->pins_sleep = + pinctrl_lookup_state(gc5603->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(gc5603->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } else { + dev_err(dev, "no pinctrl\n"); + } + + ret = gc5603_configure_regulators(gc5603); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + mutex_init(&gc5603->mutex); + + sd = &gc5603->subdev; + v4l2_i2c_subdev_init(sd, client, &gc5603_subdev_ops); + ret = gc5603_initialize_controls(gc5603); + if (ret) + goto err_destroy_mutex; + + ret = __gc5603_power_on(gc5603); + if (ret) + goto err_free_handler; + + usleep_range(3000, 4000); + ret = gc5603_check_sensor_id(gc5603, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &gc5603_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + gc5603->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &gc5603->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(gc5603->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + gc5603->module_index, facing, + GC5603_NAME, dev_name(sd->dev)); + + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + +err_power_off: + __gc5603_power_off(gc5603); +err_free_handler: + v4l2_ctrl_handler_free(&gc5603->ctrl_handler); + +err_destroy_mutex: + mutex_destroy(&gc5603->mutex); + return ret; +} + +static int gc5603_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc5603 *gc5603 = to_gc5603(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&gc5603->ctrl_handler); + mutex_destroy(&gc5603->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __gc5603_power_off(gc5603); + pm_runtime_set_suspended(&client->dev); + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id gc5603_of_match[] = { + { .compatible = "galaxycore,gc5603" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gc5603_of_match); +#endif +static const struct i2c_device_id gc5603_match_id[] = { + { "galaxycore, gc5603", 0 }, + { }, +}; + + + +static struct i2c_driver gc5603_i2c_driver = { + .driver = { + .name = GC5603_NAME, + .pm = &gc5603_pm_ops, + .of_match_table = of_match_ptr(gc5603_of_match), + }, + .probe = &gc5603_probe, + .remove = &gc5603_remove, + .id_table = gc5603_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&gc5603_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&gc5603_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("GC5603 CMOS Image Sensor driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/media/i2c/gc8613.c b/drivers/media/i2c/gc8613.c new file mode 100644 index 0000000000000..460d4a9924a5a --- /dev/null +++ b/drivers/media/i2c/gc8613.c @@ -0,0 +1,2317 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * GC8613 driver + * + * Copyright (C) 2023 Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 init version. + * V0.0X01.0X02 support mirror and flip + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" +#define OF_CAMERA_HDR_MODE "rockchip,camera-hdr-mode" +#define GC8613_NAME "gc8613" + +#define GC8613_IS_LINEAR 0 +#define GC8613_XVCLK_FREQ 24000000 +#define GC8613_LANES 4 + +#if GC8613_IS_LINEAR +#define GC8613_LINK_FREQ_LINEAR 396000000 +#define GC8613_PIXEL_RATE_LINEAR (GC8613_LINK_FREQ_LINEAR * 2 / 10 * GC8613_LANES) + +#else +#define GC8613_LINK_FREQ_LINEAR 594000000 +#define GC8613_PIXEL_RATE_LINEAR (GC8613_LINK_FREQ_LINEAR * 2 / 12 * GC8613_LANES) +#endif + +#define GC8613_LINK_FREQ_FPS 501190000 +#define GC8613_MAX_PIXEL_RATE (GC8613_LINK_FREQ_FPS * 2 / 10 * GC8613_LANES) + +#define GC8613_REG_EXPOSURE_H 0x0202 +#define GC8613_REG_EXPOSURE_L 0x0203 +#define GC8613_EXPOSURE_MIN 1 +#define GC8613_EXPOSURE_STEP 1 + +#define GC8613_VTS_MAX 0x3fff +#define GC8613_REG_VTS_H 0x0340 +#define GC8613_REG_VTS_L 0x0341 + +#define GC8613_GAIN_MIN 64 +#define GC8613_GAIN_MAX 0x7fffffff +#define GC8613_GAIN_STEP 1 +#define GC8613_GAIN_DEFAULT 64 + +#define GC8613_OTP_MIRROR_FLIP_REG 0x0a73 +#define GC8613_MIRROR_BIT_MASK BIT(0) +#define GC8613_MIRROR_FLIP_REG 0x022c +#define GC8613_FLIP_BIT_MASK BIT(1) + +#define GC8613_REG_CTRL_MODE 0x0100 +#define GC8613_MODE_SW_STANDBY 0x00 +#define GC8613_MODE_STREAMING 0x09 + +#define CHIP_ID 0x8613 +#define GC8613_REG_CHIP_ID_H 0x03f0 +#define GC8613_REG_CHIP_ID_L 0x03f1 + +#define GC8613_REG_VALUE_08BIT 1 +#define GC8613_REG_VALUE_16BIT 2 +#define GC8613_REG_VALUE_24BIT 3 + +#define GC8613_REG_TEST_PATTERN 0x008c +#define GC8613_TEST_PATTERN_ENABLE 0x14 +#define GC8613_TEST_PATTERN_DISABLE 0x10 + + +#define REG_DELAY 0xFFFE +#define REG_NULL 0xFFFF + + +static const char * const gc8613_supply_names[] = { + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ + "avdd", /* Analog power */ +}; + +/* config MIPI channel for linear or HDR mode different exposure time frame, open.k 20210924 +enum ar0233_max_pad { + PAD0, + PAD1, + PAD2, + PAD3, + PAD_MAX, +}; +*/ + +#define GC8613_NUM_SUPPLIES ARRAY_SIZE(gc8613_supply_names) + +struct gc8613_mode { + u32 bus_fmt; + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; + u32 mipi_freq_idx; + u32 bpp; + u32 hdr_mode; + u32 vc[PAD_MAX]; +}; + +struct gc8613 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct gpio_desc *pwren_gpio; + struct regulator_bulk_data supplies[GC8613_NUM_SUPPLIES]; + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *h_flip; + struct v4l2_ctrl *v_flip; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + bool power_on; + const struct gc8613_mode *cur_mode; + u32 cfg_num; + u32 module_index; + u32 cur_vts; + u32 cur_pixel_rate; + u32 cur_link_freq; + struct preisp_hdrae_exp_s init_hdrae_exp; + const char *module_facing; + const char *module_name; + const char *len_name; + bool has_init_exp; + u8 flip; +}; + +#define to_gc8613(sd) container_of(sd, struct gc8613, subdev) + +struct regval { + u16 addr; + u8 val; +}; + +static const struct regval gc8613_global_regs[] = { + {REG_NULL, 0x00}, +}; + + + +//version 1.6 +// +//mclk 24MHz, mipiclk 1188Mbps, wpclk 216MHz, rpclk 198MHz +//rowtime 14.52us, vts 2296 +//darksun on, HDR off, fixposition off, DAG on +static const struct regval gc8613_nolinear12bit_3840x2160_regs[] ={ + {0x03fe,0xf0}, + {0x03fe,0x00}, + {0x03fe,0x10}, + {0x0a38,0x01}, + {0x0a20,0x19}, + {0x061b,0x17}, + {0x061c,0x48}, + {0x061d,0x05}, + {0x061e,0x5a}, + {0x061f,0x05}, + {0x0a21,0x24}, + {0x0a31,0xc6}, + {0x0a34,0x40}, + {0x0a35,0x08}, + {0x0a37,0x44}, + {0x0314,0x50}, + {0x0315,0x00}, + {0x031c,0xce}, + {0x0219,0x47}, + {0x0342,0x03}, + {0x0343,0x10}, + {0x0259,0x08}, + {0x025a,0x98}, + {0x0340,0x08}, + {0x0341,0xf8}, + {0x0345,0x02}, + {0x0347,0x02}, + {0x0348,0x0f}, + {0x0349,0x18}, + {0x034a,0x08}, + {0x034b,0x88}, + {0x034f,0xf0}, + {0x0094,0x0f}, + {0x0095,0x00}, + {0x0096,0x08}, + {0x0097,0x70}, + {0x0099,0x0c}, + {0x009b,0x0c}, + {0x060c,0x06}, + {0x060e,0x20}, + {0x060f,0x0f}, + {0x070c,0x06}, + {0x070e,0x20}, + {0x070f,0x0f}, + {0x0087,0x50}, + {0x0907,0xd5}, + {0x0909,0x06}, + {0x0902,0x0b}, + {0x0904,0x08}, + {0x0908,0x09}, + {0x0903,0xc5}, + {0x090c,0x09}, + {0x0905,0x10}, + {0x0906,0x00}, + + {0x0724,0x2b}, + {0x0727,0x2b}, + {0x072b,0x1c}, + {0x072a,0x7c}, + {0x073e,0x40}, + {0x0078,0x88}, + {0x0268,0x40}, + {0x0269,0x44}, + {0x0351,0x54}, + {0x0618,0x01}, + {0x1466,0x45}, + {0x1468,0x46}, + {0x1467,0x46}, + {0x0709,0x40}, + {0x0719,0x40}, + {0x1469,0xf0}, + {0x146a,0xd0}, + {0x146b,0x03}, + {0x1480,0x07}, + {0x1481,0x80}, + {0x1484,0x0b}, + {0x1485,0xc0}, + {0x1430,0x80}, + {0x1407,0x10}, + {0x1408,0x16}, + {0x1409,0x03}, + {0x1434,0x04}, + {0x1447,0x75}, + {0x1470,0x10}, + {0x1471,0x13}, + {0x0122,0x0b}, + {0x0123,0x30}, + {0x0124,0x04}, + {0x0125,0x30}, + {0x0126,0x0f}, + {0x0127,0x15}, + {0x0128,0xa8}, + {0x0129,0x0c}, + {0x012a,0x18}, + {0x012b,0x18}, + {0x1438,0x00}, + {0x143a,0x00}, + {0x024b,0x02}, + {0x0245,0xc7}, + {0x025b,0x07}, + {0x02bb,0x77}, + {0x0612,0x01}, + {0x0613,0x26}, + {0x0243,0x66}, + {0x0087,0x53}, + {0x0053,0x05}, + {0x0089,0x00}, + {0x0002,0xeb}, + {0x005a,0x0c}, + {0x0040,0x83}, + {0x0075,0x68}, + {0x0205,0x0c}, + {0x0202,0x03}, + {0x0203,0x27}, + {0x061a,0x02}, + {0x0213,0x64}, + {0x0265,0x01}, + {0x0618,0x05}, + {0x026e,0x74}, + {0x0270,0x02}, + {0x0709,0x00}, + {0x0719,0x00}, + {0x0812,0xdb}, + {0x0822,0x0f}, + {0x0821,0x18}, + {0x0002,0xef}, + {0x0813,0xfb}, + {0x0070,0x88}, + {0x03fe,0x00}, + {0x0106,0x78}, + {0x0136,0x00}, + {0x0181,0xf0}, + {0x0185,0x01}, + {0x0180,0x46}, + {0x0106,0x38}, + {0x010d,0x80}, + {0x010e,0x16}, + {0x0111,0x2c}, + {0x0112,0x02}, + {0x0114,0x03}, + {0x0100,0x09}, + {0x79cf,0x01}, + {0x0219,0x47}, + {0x0054,0x98}, + {0x0076,0x01}, + {0x0052,0x02}, + {0x021a,0x10}, + {0x0430,0x05}, + {0x0431,0x05}, + {0x0432,0x05}, + {0x0433,0x05}, + {0x0434,0x70}, + {0x0435,0x70}, + {0x0436,0x70}, + {0x0437,0x70}, + {0x0004,0x0f}, + {0x0704,0x03}, + {0x071d,0xdc}, + {0x071e,0x05}, + {0x0706,0x02}, + {0x0716,0x02}, + {0x0708,0xc8}, + {0x0718,0xc8}, + {0x071d,0xdc}, + {0x071e,0x05}, + {0x1469,0x80}, +//otp autoload + {0x031f,0x01}, + {0x031f,0x00}, + {0x0a67,0x80}, + {0x0a54,0x0e}, + {0x0a65,0x10}, + {0x0a98,0x04}, + {0x05be,0x00}, + {0x05a9,0x01}, + {0x0089,0x02}, + {0x0aa0,0x00}, + {0x0023,0x00}, + {0x0022,0x00}, + {0x0025,0x00}, + {0x0024,0x00}, + {0x0028,0x0f}, + {0x0029,0x18}, + {0x002a,0x08}, + {0x002b,0x88}, + {0x0317,0x1c}, + {0x0a70,0x03}, + {0x0a82,0x00}, + {0x0a83,0xe0}, + {0x0a71,0x00}, + {0x0a72,0x02}, + {0x0a73,0x60}, + {0x0a75,0x41}, + {0x0a70,0x03}, + {0x0a5a,0x80}, + {REG_DELAY, 0x14}, // sleep 20ms + + {0x0089,0x00}, + {0x05be,0x01}, + {0x0a70,0x00}, + {0x0080,0x02}, + {0x0a67,0x00}, + {0x024b,0x02}, + {0x0220,0x80}, + {0x0058,0x00}, + {0x0059,0x04}, + {REG_NULL, 0x00}, +}; + +//version v1.6 +// +//mclk 24MHz, mipiclk 1004Mbps, wpclk 216MHz, rpclk 200.8MHz +//rowtime 14.81us, vts 2250 +//darksun on, HDR off, fixposition off, DAG off +static const struct regval gc8613_linear10bit_3840x2160_regs[] ={ + {0x03fe,0xf0}, + {0x03fe,0x00}, + {0x03fe,0x10}, + {0x0a38,0x01}, + {0x0a20,0x19}, + {0x061b,0x17}, + {0x061c,0x50}, + {0x061d,0x06}, + {0x061e,0x87}, + {0x061f,0x05}, + {0x0a21,0x10}, + {0x0a31,0xfb}, + {0x0a34,0x40}, + {0x0a35,0x08}, + {0x0a37,0x46}, + {0x0314,0x50}, + {0x0315,0x00}, + {0x031c,0xce}, + {0x0219,0x47}, + {0x0342,0x03}, + {0x0343,0x20}, + {0x0259,0x08}, + {0x025a,0x96}, + {0x0340,0x08}, + {0x0341,0xca}, + {0x0351,0x00}, + {0x0345,0x02}, + {0x0347,0x02}, + {0x0348,0x0f}, + {0x0349,0x18}, + {0x034a,0x08}, + {0x034b,0x88}, + {0x034f,0xf0}, + {0x0094,0x0f}, + {0x0095,0x00}, + {0x0096,0x08}, + {0x0097,0x70}, + {0x0099,0x0c}, + {0x009b,0x0c}, + {0x060c,0x06}, + {0x060e,0x20}, + {0x060f,0x0f}, + {0x070c,0x06}, + {0x070e,0x20}, + {0x070f,0x0f}, + {0x0087,0x50}, + {0x0907,0xd5}, + {0x0909,0x06}, + {0x0902,0x0b}, + {0x0904,0x08}, + {0x0908,0x09}, + {0x0903,0xc5}, + {0x090c,0x09}, + {0x0905,0x10}, + {0x0906,0x00}, + {0x072a,0x7c}, + {0x0724,0x2b}, + {0x0727,0x2b}, + {0x072b,0x1c}, + {0x073e,0x40}, + {0x0078,0x88}, + {0x0618,0x01}, + {0x1466,0x12}, + {0x1468,0x10}, + {0x1467,0x10}, + {0x0709,0x40}, + {0x0719,0x40}, + {0x1469,0x80}, + {0x146a,0xc0}, + {0x146b,0x03}, + {0x1480,0x02}, + {0x1481,0x80}, + {0x1484,0x08}, + {0x1485,0xc0}, + {0x1430,0x80}, + {0x1407,0x10}, + {0x1408,0x16}, + {0x1409,0x03}, + {0x1434,0x04}, + {0x1447,0x75}, + {0x1470,0x10}, + {0x1471,0x13}, + {0x1438,0x00}, + {0x143a,0x00}, + {0x024b,0x02}, + {0x0245,0xc7}, + {0x025b,0x07}, + {0x02bb,0x77}, + {0x0612,0x01}, + {0x0613,0x26}, + {0x0243,0x66}, + {0x0087,0x53}, + {0x0053,0x05}, + {0x0089,0x02}, + {0x0002,0xeb}, + {0x005a,0x0c}, + {0x0040,0x83}, + {0x0075,0x54}, + {0x0205,0x0c}, + {0x0202,0x01}, + {0x0203,0x27}, + {0x061a,0x02}, + {0x03fe,0x00}, + {0x0106,0x78}, + {0x0136,0x03}, + {0x0181,0xf0}, + {0x0185,0x01}, + {0x0180,0x46}, + {0x0106,0x38}, + {0x010d,0xc0}, + {0x010e,0x12}, + {0x0113,0x02}, + {0x0114,0x03}, + {0x0100,0x09}, + {0x0004,0x0f}, + {0x0219,0x47}, + {0x0054,0x98}, + {0x0076,0x01}, + {0x0052,0x02}, + {0x021a,0x10}, + {0x0430,0x21}, + {0x0431,0x21}, + {0x0432,0x21}, + {0x0433,0x21}, + {0x0434,0x61}, + {0x0435,0x61}, + {0x0436,0x61}, + {0x0437,0x61}, + {0x0704,0x03}, + {0x071d,0xdc}, + {0x071e,0x05}, + {0x0706,0x02}, + {0x0716,0x02}, + {0x0708,0xc8}, + {0x0718,0xc8}, + {0x031f,0x01}, + {0x031f,0x00}, + {0x0a67,0x80}, + {0x0a54,0x0e}, + {0x0a65,0x10}, + {0x0a98,0x04}, + {0x05be,0x00}, + {0x05a9,0x01}, + {0x0089,0x02}, + {0x0aa0,0x00}, + {0x0023,0x00}, + {0x0022,0x00}, + {0x0025,0x00}, + {0x0024,0x00}, + {0x0028,0x0f}, + {0x0029,0x18}, + {0x002a,0x08}, + {0x002b,0x88}, + {0x0317,0x1c}, + {0x0a70,0x03}, + {0x0a82,0x00}, + {0x0a83,0xe0}, + {0x0a71,0x00}, + {0x0a72,0x02}, + {0x0a73,0x60}, + {0x0a75,0x41}, + {0x0a70,0x03}, + {0x0a5a,0x80}, + {REG_DELAY, 0x14}, // sleep 20ms + {0x0089,0x02}, + {0x05be,0x01}, + {0x0a70,0x00}, + {0x0080,0x02}, + {0x0a67,0x00}, + {0x024b,0x02}, + {0x0220,0x80}, + {0x0058,0x00}, + {0x0059,0x04}, + {REG_NULL, 0x00}, +}; + +static const struct regval gc8613_liner10bit__1920x1080_90fps_regs[] = { +//version 1.4 +//mclk 27Mhz +//wpclk:359.4375mhz,rpclk:359.4375mhz +//mipi 1002.38Mbps/lane +//vts 1532 +//window 1920 1080 +//row_time 7.25us +//bayer order RGrGbB + {0x03fe, 0xf0}, + {0x03fe, 0x00}, + {0x03fe, 0x10}, + {0x0a38, 0x01}, + {0x0a20, 0x19}, + {0x061b, 0x17}, + {0x061c, 0x44}, + {0x061d, 0x09}, + {0x061e, 0x46}, + {0x061f, 0x04}, + {0x0a21, 0x08}, + {0x0a28, 0x01}, + {0x0a30, 0x01}, + {0x0a31, 0x29}, + {0x0a34, 0x40}, + {0x0a35, 0x08}, + {0x0a37, 0x44}, + {0x0314, 0x70}, + {0x031c, 0xce}, + {0x0219, 0x47}, + {0x0342, 0x02}, + {0x0343, 0x83}, + {0x0259, 0x04}, + {0x025a, 0x00}, + {0x0340, 0x05}, + {0x0341, 0xfc}, + {0x0351, 0x00}, + {0x0345, 0x02}, + {0x0347, 0x02}, + {0x0348, 0x0f}, + {0x0349, 0x10}, //3856 + {0x034a, 0x08}, + {0x034b, 0x88}, //2184 + {0x034f, 0xf0}, + {0x0094, 0x0f}, + {0x0095, 0x00}, + {0x0096, 0x08}, + {0x0097, 0x70}, + {0x0099, 0x09}, + {0x009b, 0x09}, + {0x060c, 0x0a}, + {0x060e, 0x20}, + {0x060f, 0x0f}, + {0x070c, 0x0a}, + {0x070e, 0x20}, + {0x070f, 0x0f}, + {0x0087, 0x50}, + {0x0907, 0xd5}, + {0x0909, 0x06}, + {0x0902, 0x0b}, + {0x0904, 0x08}, + {0x0908, 0x09}, + {0x0903, 0xc5}, + {0x090c, 0x09}, + {0x0905, 0x10}, + {0x0906, 0x00}, + {0x072a, 0x7c}, + {0x0724, 0x2b}, + {0x0727, 0x2b}, + {0x072b, 0x1c}, + {0x073e, 0x40}, + {0x0078, 0x88}, + {0x0618, 0x01}, + {0x1466, 0x12}, + {0x1468, 0x10}, + {0x1467, 0x10}, + {0x0709, 0x40}, + {0x0719, 0x40}, + {0x1469, 0x80}, + {0x146a, 0xc0}, + {0x146b, 0x03}, + {0x1480, 0x02}, + {0x1481, 0x80}, + {0x1484, 0x08}, + {0x1485, 0xc0}, + {0x1430, 0x80}, + {0x1407, 0x10}, + {0x1408, 0x16}, + {0x1409, 0x03}, + {0x1434, 0x04}, + {0x1447, 0x75}, + {0x1470, 0x10}, + {0x1471, 0x13}, + {0x1438, 0x00}, + {0x143a, 0x00}, + {0x024b, 0x02}, + {0x0245, 0xc7}, + {0x025b, 0x07}, + {0x02bb, 0x77}, + {0x0612, 0x01}, + {0x0613, 0x26}, + {0x0243, 0x66}, + {0x0087, 0x53}, + {0x0053, 0x05}, + {0x0089, 0x02}, + {0x0002, 0xeb}, + {0x005a, 0x0c}, + {0x0040, 0x83}, + {0x0075, 0x54}, + {0x0077, 0x08}, + {0x0218, 0x10}, + {0x0205, 0x0c}, + {0x0202, 0x06}, + {0x0203, 0x27}, + {0x061a, 0x02}, + {0x0122, 0x11}, + {0x0123, 0x40}, + {0x0126, 0x0f}, + {0x0129, 0x08},///0x12 + {0x012a, 0x16}, + {0x012b, 0x0f}, + {0x03fe, 0x00}, + {0x0205, 0x0c}, + {0x0202, 0x01}, + {0x0203, 0x27}, + {0x061a, 0x02}, + {0x03fe, 0x00}, + {0x0106, 0x78}, + {0x0136, 0x03}, + {0x0181, 0xf0}, + {0x0185, 0x01}, + {0x0180, 0x46}, + {0x0106, 0x38}, + {0x010d, 0x60}, + {0x010e, 0x09}, + {0x0113, 0x02}, + {0x0114, 0x03}, + {0x0100, 0x09}, + {0x0004, 0x0f}, + {0x0219, 0x47}, + {0x0054, 0x98}, + {0x0076, 0x01}, + {0x0052, 0x02}, + {0x021a, 0x10}, + {0x0430, 0x21}, + {0x0431, 0x21}, + {0x0432, 0x21}, + {0x0433, 0x21}, + {0x0434, 0x61}, + {0x0435, 0x61}, + {0x0436, 0x61}, + {0x0437, 0x61}, + {0x0704, 0x07}, + {0x0706, 0x02}, + {0x0716, 0x02}, + {0x0708, 0xc8}, + {0x0718, 0xc8}, + //otp autoload + {0x031f, 0x01}, + {0x031f, 0x00}, + {0x0a67, 0x80}, + {0x0a54, 0x0e}, + {0x0a65, 0x10}, + {0x0a98, 0x04}, + {0x05be, 0x00}, + {0x05a9, 0x01}, + {0x0089, 0x02}, + {0x0aa0, 0x00}, + {0x0023, 0x00}, + {0x0022, 0x00}, + {0x0025, 0x00}, + {0x0024, 0x00}, + {0x0028, 0x0f}, + {0x0029, 0x18}, + {0x002a, 0x08}, + {0x002b, 0x88}, + {0x0317, 0x1c}, + {0x0a70, 0x03}, + {0x0a82, 0x00}, + {0x0a83, 0xe0}, + {0x0a71, 0x00}, + {0x0a72, 0x02}, + {0x0a73, 0x60}, + {0x0a75, 0x41}, + {0x0a70, 0x03}, + {0x0a5a, 0x80}, + + {REG_DELAY, 0x14},//sleep 20 + + {0x0089, 0x02}, + {0x05be, 0x01}, + {0x0a70, 0x00}, + {0x0080, 0x02}, + {0x0a67, 0x00}, + {0x024b, 0x02}, + {0x0220, 0xcf}, + {REG_NULL, 0x00}, +}; + +static const u32 gain_level_table_nolinear[23] = { + 1024 , + 1184 , + 1440 , + 1680 , + 2016 , + 2272 , + 2624 , + 3200 , + 3824 , + 4544 , + 5456 , + 6512 , + 7824 , + 8512 , + 10112, + 12288, + 15184, + 16768, + 20112, + 24000, + 28192, + 33856, + 0xffffffff, +}; + +static const u32 gain_level_table_linear[27] = { + 1024 , + 1184 , + 1440 , + 1680 , + 2016 , + 2272 , + 2624 , + 3200 , + 3824 , + 4544 , + 5456 , + 6512 , + 7824 , + 8512 , + 10112, + 12288, + 15184, + 16768, + 20112, + 24000, + 28192, + 33856, + 40320, + 48784, + 58688, + 69872, + 0xffffffff, +}; + + +static const u32 reg_val_table_nolinear[22][10] = { + //614 615 225 1467 1468 26e 270 1447 b8 b9 + {0x00, 0x00, 0x00, 0x46, 0x46, 0x74, 0x02, 0x77, 0x01, 0x00}, + {0x90, 0x02, 0x00, 0x47, 0x47, 0x74, 0x02, 0x77, 0x01, 0x0a}, + {0x01, 0x00, 0x00, 0x47, 0x47, 0x77, 0x02, 0x77, 0x01, 0x1a}, + {0x91, 0x02, 0x00, 0x48, 0x48, 0x77, 0x02, 0x77, 0x01, 0x29}, + {0x02, 0x00, 0x00, 0x48, 0x48, 0x79, 0x02, 0x77, 0x01, 0x3e}, + {0x00, 0x00, 0x00, 0x46, 0x46, 0x74, 0x02, 0x75, 0x02, 0x0d}, + {0x90, 0x02, 0x00, 0x47, 0x47, 0x74, 0x02, 0x75, 0x02, 0x24}, + {0x01, 0x00, 0x00, 0x47, 0x47, 0x77, 0x02, 0x75, 0x03, 0x08}, + {0x91, 0x02, 0x00, 0x48, 0x48, 0x79, 0x02, 0x75, 0x03, 0x2e}, + {0x02, 0x00, 0x00, 0x49, 0x49, 0x7a, 0x02, 0x75, 0x04, 0x1b}, + {0x92, 0x02, 0x00, 0x4b, 0x4b, 0x7b, 0x02, 0x75, 0x05, 0x14}, + {0x03, 0x00, 0x00, 0x4c, 0x4c, 0x7c, 0x02, 0x75, 0x06, 0x17}, + {0x93, 0x02, 0x00, 0x4d, 0x4d, 0x7d, 0x02, 0x75, 0x07, 0x29}, + {0x00, 0x00, 0x01, 0x4f, 0x4f, 0x7e, 0x02, 0x75, 0x08, 0x13}, + {0x90, 0x02, 0x01, 0x50, 0x50, 0x7f, 0x02, 0x75, 0x09, 0x38}, + {0x01, 0x00, 0x01, 0x51, 0x51, 0x7f, 0x02, 0x75, 0x0c, 0x00}, + {0x91, 0x02, 0x01, 0x53, 0x53, 0x7f, 0x02, 0x75, 0x0e, 0x35}, + {0x02, 0x00, 0x01, 0x54, 0x54, 0x7f, 0x02, 0x75, 0x10, 0x18}, + {0x92, 0x02, 0x01, 0x56, 0x56, 0x7f, 0x02, 0x75, 0x13, 0x29}, + {0x03, 0x00, 0x01, 0x58, 0x58, 0x7f, 0x02, 0x75, 0x17, 0x1c}, + {0x93, 0x02, 0x01, 0x5a, 0x5a, 0x7f, 0x01, 0x75, 0x1b, 0x22}, + {0x04, 0x00, 0x01, 0x5c, 0x5c, 0x7f, 0x01, 0x75, 0x21, 0x04}, +}; + +static const u32 reg_val_table_linear[26][8] = { +// 0614 0615 225 1467 1468 1447 b8 b9 + {0x00, 0x00, 0x00, 0x0d, 0x0d, 0x77, 0x01, 0x00}, + {0x90, 0x02, 0x00, 0x0e, 0x0e, 0x77, 0x01, 0x0a}, + {0x01, 0x00, 0x00, 0x0e, 0x0e, 0x77, 0x01, 0x1a}, + {0x91, 0x02, 0x00, 0x0f, 0x0f, 0x77, 0x01, 0x29}, + {0x02, 0x00, 0x00, 0x0f, 0x0f, 0x77, 0x01, 0x3e}, + {0x00, 0x00, 0x00, 0x0d, 0x0d, 0x75, 0x02, 0x0d}, + {0x90, 0x02, 0x00, 0x0d, 0x0d, 0x75, 0x02, 0x24}, + {0x01, 0x00, 0x00, 0x0e, 0x0e, 0x75, 0x03, 0x08}, + {0x91, 0x02, 0x00, 0x0e, 0x0e, 0x75, 0x03, 0x2e}, + {0x02, 0x00, 0x00, 0x0f, 0x0f, 0x75, 0x04, 0x1b}, + {0x92, 0x02, 0x00, 0x0f, 0x0f, 0x75, 0x05, 0x14}, + {0x03, 0x00, 0x00, 0x10, 0x10, 0x75, 0x06, 0x17}, + {0x93, 0x02, 0x00, 0x10, 0x10, 0x75, 0x07, 0x29}, + {0x00, 0x00, 0x01, 0x11, 0x11, 0x75, 0x08, 0x13}, + {0x90, 0x02, 0x01, 0x12, 0x12, 0x75, 0x09, 0x38}, + {0x01, 0x00, 0x01, 0x13, 0x13, 0x75, 0x0c, 0x00}, + {0x91, 0x02, 0x01, 0x14, 0x14, 0x75, 0x0e, 0x35}, + {0x02, 0x00, 0x01, 0x15, 0x15, 0x75, 0x10, 0x18}, + {0x92, 0x02, 0x01, 0x16, 0x16, 0x75, 0x13, 0x29}, + {0x03, 0x00, 0x01, 0x17, 0x17, 0x75, 0x17, 0x1c}, + {0x93, 0x02, 0x01, 0x18, 0x18, 0x75, 0x1b, 0x22}, + {0x04, 0x00, 0x01, 0x19, 0x19, 0x75, 0x21, 0x04}, + {0x94, 0x02, 0x01, 0x1b, 0x1b, 0x75, 0x27, 0x18}, + {0x05, 0x00, 0x01, 0x1d, 0x1d, 0x75, 0x2f, 0x29}, + {0x95, 0x02, 0x01, 0x1e, 0x1e, 0x75, 0x39, 0x0b}, + {0x06, 0x00, 0x01, 0x20, 0x20, 0x75, 0x44, 0x0f}, +}; + + +static const struct gc8613_mode supported_modes[] = { +#if GC8613_IS_LINEAR + { + .width = 3840, + .height = 2160, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0127, + .hts_def = 0x0320*8, + .vts_def = 0x08CA, + .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, + .reg_list = gc8613_linear10bit_3840x2160_regs, + .mipi_freq_idx = 0, + .bpp = 10, + .hdr_mode = NO_HDR, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, +#else + { + .width = 3840, + .height = 2160, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0327, + .hts_def = 0x0310*8, + .vts_def = 0x08f8, + .bus_fmt = MEDIA_BUS_FMT_SRGGB12_1X12, + .reg_list = gc8613_nolinear12bit_3840x2160_regs, + .mipi_freq_idx = 0, + .bpp = 12, + .hdr_mode = NO_HDR, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, +#endif + { + .width = 1920, + .height = 1080, + .max_fps = { + .numerator = 10000, + .denominator = 900000, + }, + .exp_def = 0x0127, + .hts_def = 0x0283*3, + .vts_def = 0x05fc, + .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, + .reg_list = gc8613_liner10bit__1920x1080_90fps_regs, + .mipi_freq_idx = 1, + .bpp = 10, + .hdr_mode = NO_HDR, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + +}; + +static const s64 link_freq_items[] = { + GC8613_LINK_FREQ_LINEAR, + GC8613_LINK_FREQ_FPS, +}; + +static const char * const gc8613_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", +}; + +/* Write registers up to 4 at a time */ +static int gc8613_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int gc8613_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + if (regs[i].addr == REG_DELAY) + usleep_range(regs[i].val * 1000, regs[i].val * 2 * 1000); + else + ret = gc8613_write_reg(client, regs[i].addr, + GC8613_REG_VALUE_08BIT, regs[i].val); + } + + return ret; +} + +/* Read registers up to 4 at a time */ +static int gc8613_read_reg(struct i2c_client *client, u16 reg, + unsigned int len, u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int gc8613_s_power(struct v4l2_subdev *sd, int on) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + struct i2c_client *client = gc8613->client; + int ret = 0; + + + mutex_lock(&gc8613->mutex); + + /* If the power state is not modified - no work to do. */ + if (gc8613->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = gc8613_write_array(gc8613->client, gc8613_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + gc8613->power_on = true; + } else { + pm_runtime_put(&client->dev); + gc8613->power_on = false; + } + +unlock_and_return: + mutex_unlock(&gc8613->mutex); + + return ret; +} + +static void gc8613_get_module_inf(struct gc8613 *gc8613, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strscpy(inf->base.sensor, GC8613_NAME, sizeof(inf->base.sensor)); + strscpy(inf->base.module, gc8613->module_name, + sizeof(inf->base.module)); + strscpy(inf->base.lens, gc8613->len_name, sizeof(inf->base.lens)); +} +#if 0 +static int gc8613_get_channel_info(struct gc8613 *gc8613, struct rkmodule_channel_info *ch_info) +{ + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + ch_info->vc = gc8613->cur_mode->vc[ch_info->index]; + ch_info->width = gc8613->cur_mode->width; + ch_info->height = gc8613->cur_mode->height; + ch_info->bus_fmt = gc8613->cur_mode->bus_fmt; + return 0; +} +#endif +static long gc8613_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + struct rkmodule_hdr_cfg *hdr; + const struct gc8613_mode *mode; + + u32 i, h, w; + long ret = 0; + u32 stream = 0; + u64 pixel_rate = 0; +// struct rkmodule_channel_info *ch_info; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + gc8613_get_module_inf(gc8613, (struct rkmodule_inf *)arg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = (struct rkmodule_hdr_cfg *)arg; + hdr->esp.mode = HDR_NORMAL_VC; + hdr->hdr_mode = gc8613->cur_mode->hdr_mode; + break; + case RKMODULE_SET_HDR_CFG: + hdr = (struct rkmodule_hdr_cfg *)arg; + w = gc8613->cur_mode->width; + h = gc8613->cur_mode->height; + for (i = 0; i < gc8613->cfg_num; i++) { + if (w == supported_modes[i].width && + h == supported_modes[i].height && + supported_modes[i].hdr_mode == hdr->hdr_mode) { + gc8613->cur_mode = &supported_modes[i]; + break; + } + } + if (i == gc8613->cfg_num) { + dev_err(&gc8613->client->dev, + "not find hdr mode:%d %dx%d config\n", + hdr->hdr_mode, w, h); + ret = -EINVAL; + } else { + mode = gc8613->cur_mode; + w = gc8613->cur_mode->hts_def - + gc8613->cur_mode->width; + h = gc8613->cur_mode->vts_def - + gc8613->cur_mode->height; + __v4l2_ctrl_modify_range(gc8613->hblank, w, w, 1, w); + __v4l2_ctrl_modify_range(gc8613->vblank, h, + GC8613_VTS_MAX - + gc8613->cur_mode->height, + 1, h); + + +#if 0 + gc8613->cur_link_freq = 0; + gc8613->cur_pixel_rate = GC8613_PIXEL_RATE_LINEAR; + + __v4l2_ctrl_s_ctrl_int64(gc8613->pixel_rate, + gc8613->cur_pixel_rate); + __v4l2_ctrl_s_ctrl(gc8613->link_freq, + gc8613->cur_link_freq); + gc8613->cur_vts = gc8613->cur_mode->vts_def; +#endif + __v4l2_ctrl_s_ctrl(gc8613->link_freq, mode->mipi_freq_idx); + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * GC8613_LANES; + __v4l2_ctrl_s_ctrl_int64(gc8613->pixel_rate, pixel_rate); + + dev_info(&gc8613->client->dev, "sensor mode: %d\n", gc8613->cur_mode->hdr_mode); + } + break; + case RKMODULE_SET_QUICK_STREAM: + stream = *((u32 *)arg); + if (stream) + ret = gc8613_write_reg(gc8613->client, GC8613_REG_CTRL_MODE, + GC8613_REG_VALUE_08BIT, GC8613_MODE_STREAMING); + else + ret = gc8613_write_reg(gc8613->client, GC8613_REG_CTRL_MODE, + GC8613_REG_VALUE_08BIT, GC8613_MODE_SW_STANDBY); + break; + #if 0 + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = gc8613_get_channel_info(gc8613, ch_info); + break; + #endif + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long gc8613_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_hdr_cfg *hdr; + long ret; + u32 stream = 0; +// struct rkmodule_channel_info *ch_info; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = gc8613_ioctl(sd, cmd, inf); + if (!ret) { + ret = copy_to_user(up, inf, sizeof(*inf)); + if (ret) + ret = -EFAULT; + } + kfree(inf); + break; + case RKMODULE_GET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = gc8613_ioctl(sd, cmd, hdr); + if (!ret) { + ret = copy_to_user(up, hdr, sizeof(*hdr)); + if (ret) + ret = -EFAULT; + } + kfree(hdr); + break; + case RKMODULE_SET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(hdr, up, sizeof(*hdr)); + if (!ret) + ret = gc8613_ioctl(sd, cmd, hdr); + else + ret = -EFAULT; + kfree(hdr); + break; + case RKMODULE_SET_QUICK_STREAM: + ret = copy_from_user(&stream, up, sizeof(u32)); + if (!ret) + ret = gc8613_ioctl(sd, cmd, &stream); + else + ret = -EFAULT; + break; + #if 0 + case RKMODULE_GET_CHANNEL_INFO: + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); + if (!ch_info) { + ret = -ENOMEM; + return ret; + } + + ret = gc8613_ioctl(sd, cmd, ch_info); + if (!ret) { + ret = copy_to_user(up, ch_info, sizeof(*ch_info)); + if (ret) + ret = -EFAULT; + } + kfree(ch_info); + break; + #endif + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __gc8613_start_stream(struct gc8613 *gc8613) +{ + int ret; + + ret = gc8613_write_array(gc8613->client, gc8613->cur_mode->reg_list); + if (ret) + return ret; + + /* In case these controls are set before streaming */ + ret = __v4l2_ctrl_handler_setup(&gc8613->ctrl_handler); + if (gc8613->has_init_exp && gc8613->cur_mode->hdr_mode != NO_HDR) { + ret = gc8613_ioctl(&gc8613->subdev, PREISP_CMD_SET_HDRAE_EXP, + &gc8613->init_hdrae_exp); + if (ret) { + dev_err(&gc8613->client->dev, + "init exp fail in hdr mode\n"); + return ret; + } + } + if (ret) + return ret; + + ret |= gc8613_write_reg(gc8613->client, GC8613_REG_CTRL_MODE, + GC8613_REG_VALUE_08BIT, GC8613_MODE_STREAMING); + + return ret; +} + +static int __gc8613_stop_stream(struct gc8613 *gc8613) +{ + gc8613->has_init_exp = false; + return gc8613_write_reg(gc8613->client, GC8613_REG_CTRL_MODE, + GC8613_REG_VALUE_08BIT, GC8613_MODE_SW_STANDBY); +} + +static int gc8613_s_stream(struct v4l2_subdev *sd, int on) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + struct i2c_client *client = gc8613->client; + int ret = 0; + + mutex_lock(&gc8613->mutex); + on = !!on; + if (on == gc8613->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __gc8613_start_stream(gc8613); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __gc8613_stop_stream(gc8613); + pm_runtime_put(&client->dev); + } + + gc8613->streaming = on; + +unlock_and_return: + mutex_unlock(&gc8613->mutex); + + return ret; +} + +static int gc8613_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + const struct gc8613_mode *mode = gc8613->cur_mode; + + fi->interval = mode->max_fps; + + return 0; +} + +static int gc8613_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + + if (code->index != 0) + return -EINVAL; + code->code = gc8613->cur_mode->bus_fmt; + + return 0; +} + +static int gc8613_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + + if (fse->index >= gc8613->cfg_num) + return -EINVAL; + + if (fse->code != supported_modes[0].bus_fmt) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int gc8613_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + + if (fie->index >= gc8613->cfg_num) + return -EINVAL; + + fie->code = supported_modes[fie->index].bus_fmt; + fie->width = supported_modes[fie->index].width; + fie->height = supported_modes[fie->index].height; + fie->interval = supported_modes[fie->index].max_fps; + fie->reserved[0] = supported_modes[fie->index].hdr_mode; + return 0; +} + +static int gc8613_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + const struct gc8613_mode *mode = gc8613->cur_mode; + + mutex_lock(&gc8613->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&gc8613->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->bus_fmt; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&gc8613->mutex); + + return 0; +} + +static int gc8613_get_reso_dist(const struct gc8613_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct gc8613_mode * +gc8613_find_best_fit(struct gc8613 *gc8613, struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < gc8613->cfg_num; i++) { + dist = gc8613_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int gc8613_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + const struct gc8613_mode *mode; + s64 h_blank, vblank_def; + u64 pixel_rate = 0; + + mutex_lock(&gc8613->mutex); + + mode = gc8613_find_best_fit(gc8613, fmt); + fmt->format.code = mode->bus_fmt; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&gc8613->mutex); + return -ENOTTY; +#endif + } else { + gc8613->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(gc8613->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(gc8613->vblank, vblank_def, + GC8613_VTS_MAX - mode->height, + 1, vblank_def); + + __v4l2_ctrl_s_ctrl(gc8613->link_freq, mode->mipi_freq_idx); + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * GC8613_LANES; + __v4l2_ctrl_s_ctrl_int64(gc8613->pixel_rate, pixel_rate); + } + mutex_unlock(&gc8613->mutex); + + return 0; +} +#if 0 +#define DST_WIDTH_3840 3840 +#define DST_HEIGHT_2160 2160 +#define DST_WIDTH_1920 1920 +#define DST_HEIGHT_1080 1080 +/* + * The resolution of the driver configuration needs to be exactly + * the same as the current output resolution of the sensor, + * the input width of the isp needs to be 16 aligned, + * the input height of the isp needs to be 8 aligned. + * Can be cropped to standard resolution by this function, + * otherwise it will crop out strange resolution according + * to the alignment rules. + */ +static int gc8613_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + if (gc8613->cur_mode->width == 3840) { + sel->r.left = CROP_START(gc8613->cur_mode->width, DST_WIDTH_3840); + sel->r.width = DST_WIDTH_3840; + sel->r.top = CROP_START(gc8613->cur_mode->height, DST_HEIGHT_2160); + sel->r.height = DST_HEIGHT_2160; + } else if (gc8613->cur_mode->width == 1920) { + sel->r.left = CROP_START(gc8613->cur_mode->width, DST_WIDTH_1920); + sel->r.width = DST_WIDTH_1920; + sel->r.top = CROP_START(gc8613->cur_mode->height, DST_HEIGHT_1080); + sel->r.height = DST_HEIGHT_1080; + } else { + sel->r.left = CROP_START(gc8613->cur_mode->width, gc8613->cur_mode->width); + sel->r.width = gc8613->cur_mode->width; + sel->r.top = CROP_START(gc8613->cur_mode->height, gc8613->cur_mode->height); + sel->r.height = gc8613->cur_mode->height; + } + return 0; + } + return -EINVAL; +} +#endif + + +static int gc8613_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + const struct gc8613_mode *mode = gc8613->cur_mode; + u32 val = 0; + + val = 1 << (GC8613_LANES - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + if (mode->hdr_mode != NO_HDR) + val |= V4L2_MBUS_CSI2_CHANNEL_1; + if (mode->hdr_mode == HDR_X3) + val |= V4L2_MBUS_CSI2_CHANNEL_2; + config->type = V4L2_MBUS_CSI2_DPHY; + config->flags = val; + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int gc8613_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct gc8613 *gc8613 = to_gc8613(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct gc8613_mode *def_mode = &supported_modes[0]; + + mutex_lock(&gc8613->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = def_mode->bus_fmt; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&gc8613->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops gc8613_internal_ops = { + .open = gc8613_open, +}; +#endif + +static const struct v4l2_subdev_core_ops gc8613_core_ops = { + .s_power = gc8613_s_power, + .ioctl = gc8613_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = gc8613_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops gc8613_video_ops = { + .s_stream = gc8613_s_stream, + .g_frame_interval = gc8613_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops gc8613_pad_ops = { + .enum_mbus_code = gc8613_enum_mbus_code, + .enum_frame_size = gc8613_enum_frame_sizes, + .enum_frame_interval = gc8613_enum_frame_interval, + .get_fmt = gc8613_get_fmt, + .set_fmt = gc8613_set_fmt, + .get_mbus_config = gc8613_g_mbus_config, +// .get_selection = gc8613_get_selection, +}; + +static const struct v4l2_subdev_ops gc8613_subdev_ops = { + .core = &gc8613_core_ops, + .video = &gc8613_video_ops, + .pad = &gc8613_pad_ops, +}; + + +static int gc8613_configure_regulators(struct gc8613 *gc8613) +{ + unsigned int i; + + for (i = 0; i < GC8613_NUM_SUPPLIES; i++) + gc8613->supplies[i].supply = gc8613_supply_names[i]; + + return devm_regulator_bulk_get(&gc8613->client->dev, + GC8613_NUM_SUPPLIES, + gc8613->supplies); +} + +static int gc8613_set_gain_reg(struct gc8613 *gc8613, u32 gain) +{ + const struct gc8613_mode *mode; + int i; + int total; + s64 tol_dig_gain = 0; + mode = gc8613->cur_mode; + + +if (mode->bpp == 10) { +//#if GC8613_IS_LINEAR + if (gain < 64) + gain = 64; + total = ARRAY_SIZE(gain_level_table_linear) - 1; + for (i = 0; i < total; i++) { + if (gain_level_table_linear[i] <= gain && + gain < gain_level_table_linear[i + 1]) + break; + } + if(i == ARRAY_SIZE(gain_level_table_linear) - 1) + i = ARRAY_SIZE(gain_level_table_linear) - 2; + tol_dig_gain = gain * 64 / gain_level_table_linear[i]; + + gc8613_write_reg(gc8613->client, 0x031d, + GC8613_REG_VALUE_08BIT, 0x2d); + + gc8613_write_reg(gc8613->client, 0x0614, + GC8613_REG_VALUE_08BIT, reg_val_table_linear[i][0]); + gc8613_write_reg(gc8613->client, 0x0615, + GC8613_REG_VALUE_08BIT, reg_val_table_linear[i][1]); + + gc8613_write_reg(gc8613->client, 0x031d, + GC8613_REG_VALUE_08BIT, 0x28); + + gc8613_write_reg(gc8613->client, 0x0225, + GC8613_REG_VALUE_08BIT, reg_val_table_linear[i][2]); + gc8613_write_reg(gc8613->client, 0x1467, + GC8613_REG_VALUE_08BIT, reg_val_table_linear[i][3]); + gc8613_write_reg(gc8613->client, 0x1468, + GC8613_REG_VALUE_08BIT, reg_val_table_linear[i][4]); + gc8613_write_reg(gc8613->client, 0x1447, + GC8613_REG_VALUE_08BIT, reg_val_table_linear[i][5]); + gc8613_write_reg(gc8613->client, 0x00b8, + GC8613_REG_VALUE_08BIT, reg_val_table_linear[i][6]); + gc8613_write_reg(gc8613->client, 0x00b9, + GC8613_REG_VALUE_08BIT, reg_val_table_linear[i][7]); + + + gc8613_write_reg(gc8613->client, 0x0064, + GC8613_REG_VALUE_08BIT, (tol_dig_gain >> 6)); + gc8613_write_reg(gc8613->client, 0x0065, + GC8613_REG_VALUE_08BIT, (tol_dig_gain & 0x3f)); + //dev_err(&gc8613->client->dev,"gc8613_set_gain_reg 10 bits bbp:%u\n",mode->bpp); +} +else { +//#else + gain=gain*16; + if (gain < 1024) + gain = 1024; + total = ARRAY_SIZE(gain_level_table_nolinear) - 1; + for (i = 0; i < total; i++) { + if (gain_level_table_nolinear[i] <= gain && + gain < gain_level_table_nolinear[i + 1]) + break; + } + if(i == ARRAY_SIZE(gain_level_table_nolinear) - 1) + i = ARRAY_SIZE(gain_level_table_nolinear) - 2; + tol_dig_gain = gain * 64 / gain_level_table_nolinear[i]; + + gc8613_write_reg(gc8613->client, 0x031d, + GC8613_REG_VALUE_08BIT, 0x2d); + + gc8613_write_reg(gc8613->client, 0x0614, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][0]); + gc8613_write_reg(gc8613->client, 0x0615, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][1]); + + gc8613_write_reg(gc8613->client, 0x026e, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][5]); + gc8613_write_reg(gc8613->client, 0x0270, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][6]); + + gc8613_write_reg(gc8613->client, 0x031d, + GC8613_REG_VALUE_08BIT, 0x28); + + gc8613_write_reg(gc8613->client, 0x0225, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][2]); + gc8613_write_reg(gc8613->client, 0x1467, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][3]); + gc8613_write_reg(gc8613->client, 0x1468, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][4]); + + gc8613_write_reg(gc8613->client, 0x1447, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][7]); + + gc8613_write_reg(gc8613->client, 0x00b8, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][8]); + gc8613_write_reg(gc8613->client, 0x00b9, + GC8613_REG_VALUE_08BIT, reg_val_table_nolinear[i][9]); + + gc8613_write_reg(gc8613->client, 0x0064, + GC8613_REG_VALUE_08BIT, (tol_dig_gain >> 6)); + gc8613_write_reg(gc8613->client, 0x0065, + GC8613_REG_VALUE_08BIT, (tol_dig_gain & 0x3f)); +// dev_err(&gc8613->client->dev,"gc8613_set_gain_reg 12bit bbp:%u\n",mode->bpp); + + } +//#endif + + return 0; + +} + +static int gc8613_enable_test_pattern(struct gc8613 *gc8613, u32 pattern) +{ + return 0; +} + +static int gc8613_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc8613 *gc8613 = container_of(ctrl->handler, + struct gc8613, ctrl_handler); + struct i2c_client *client = gc8613->client; + s64 max; + int ret = 0; + + + /*Propagate change of current control to all related controls*/ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /*Update max exposure while meeting expected vblanking*/ + max = gc8613->cur_mode->height + ctrl->val - 8; + __v4l2_ctrl_modify_range(gc8613->exposure, + gc8613->exposure->minimum, + max, + gc8613->exposure->step, + gc8613->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = gc8613_write_reg(gc8613->client, GC8613_REG_EXPOSURE_H, + GC8613_REG_VALUE_08BIT, + ctrl->val >> 8); + ret |= gc8613_write_reg(gc8613->client, GC8613_REG_EXPOSURE_L, + GC8613_REG_VALUE_08BIT, + ctrl->val & 0xff); + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = gc8613_set_gain_reg(gc8613, ctrl->val); + break; + case V4L2_CID_VBLANK: + gc8613->cur_vts = ctrl->val + gc8613->cur_mode->height; + ret = gc8613_write_reg(gc8613->client, GC8613_REG_VTS_H, + GC8613_REG_VALUE_08BIT, + gc8613->cur_vts >> 8); + ret |= gc8613_write_reg(gc8613->client, GC8613_REG_VTS_L, + GC8613_REG_VALUE_08BIT, + gc8613->cur_vts & 0xff); + break; + case V4L2_CID_TEST_PATTERN: + ret = gc8613_enable_test_pattern(gc8613, ctrl->val); + break; + case V4L2_CID_HFLIP: + + if (ctrl->val) + gc8613->flip |= GC8613_MIRROR_BIT_MASK; + else + gc8613->flip &= ~ GC8613_MIRROR_BIT_MASK; + + switch (gc8613->flip) { + case 0: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 0); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 0); + break; + case 1: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 5); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 0); + break; + case 2: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 2); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 1); + break; + case 3: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 7); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 1); + break; + + default: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 0); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 0); + break; + } + + break; + case V4L2_CID_VFLIP: + if (ctrl->val) + gc8613->flip |= GC8613_FLIP_BIT_MASK; + else + gc8613->flip &= ~ GC8613_FLIP_BIT_MASK; + + switch (gc8613->flip) { + case 0: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 0); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 0); + break; + case 1: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 5); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 0); + break; + case 2: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 2); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 1); + break; + case 3: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 7); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 1); + break; + + default: + ret = gc8613_write_reg(gc8613->client, 0x0063, GC8613_REG_VALUE_08BIT, 0); + ret = gc8613_write_reg(gc8613->client, 0x022c, GC8613_REG_VALUE_08BIT, 0); + break; + } + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + + +static const struct v4l2_ctrl_ops gc8613_ctrl_ops = { + .s_ctrl = gc8613_set_ctrl, +}; + +static int gc8613_initialize_controls(struct gc8613 *gc8613) +{ + const struct gc8613_mode *mode; + struct v4l2_ctrl_handler *handler; + u64 pixel_rate; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &gc8613->ctrl_handler; + mode = gc8613->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 9); + if (ret) + return ret; + handler->lock = &gc8613->mutex; + + gc8613->link_freq = v4l2_ctrl_new_int_menu(handler, NULL, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_items) - 1, 0, + link_freq_items); + __v4l2_ctrl_s_ctrl(gc8613->link_freq, mode->mipi_freq_idx); + + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * GC8613_LANES ; + dev_err(&gc8613->client->dev, " gc8613_LINK_FREQ(%u) gc8613_bpp(%u) pixel_rate(%llu)\n", (u32)link_freq_items[mode->mipi_freq_idx],mode->bpp,pixel_rate); + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + + + gc8613->pixel_rate = v4l2_ctrl_new_std(handler, NULL, + V4L2_CID_PIXEL_RATE, 0, GC8613_MAX_PIXEL_RATE, + 1, pixel_rate); + + h_blank = mode->hts_def - mode->width; + gc8613->cur_vts = mode->vts_def; + gc8613->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (gc8613->hblank) + gc8613->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + gc8613->vblank = v4l2_ctrl_new_std(handler, &gc8613_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + GC8613_VTS_MAX - mode->height, + 1, vblank_def); + + exposure_max = mode->vts_def - 8; + gc8613->exposure = v4l2_ctrl_new_std(handler, &gc8613_ctrl_ops, + V4L2_CID_EXPOSURE, + GC8613_EXPOSURE_MIN, + exposure_max, + GC8613_EXPOSURE_STEP, + mode->exp_def); + + gc8613->anal_gain = v4l2_ctrl_new_std(handler, &gc8613_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + GC8613_GAIN_MIN, + GC8613_GAIN_MAX, + GC8613_GAIN_STEP, + GC8613_GAIN_DEFAULT); + + gc8613->test_pattern = + v4l2_ctrl_new_std_menu_items(handler, + &gc8613_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(gc8613_test_pattern_menu) - 1, + 0, 0, gc8613_test_pattern_menu); + + gc8613->flip = 0; + gc8613->h_flip = v4l2_ctrl_new_std(handler, &gc8613_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + gc8613->v_flip = v4l2_ctrl_new_std(handler, &gc8613_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (handler->error) { + ret = handler->error; + dev_err(&gc8613->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + gc8613->subdev.ctrl_handler = handler; + gc8613->has_init_exp = false; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 gc8613_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, GC8613_XVCLK_FREQ / 1000 / 1000); +} + +static int __gc8613_power_on(struct gc8613 *gc8613) +{ + int ret; + u32 delay_us; + struct device *dev = &gc8613->client->dev; + + if (!IS_ERR_OR_NULL(gc8613->pins_default)) { + ret = pinctrl_select_state(gc8613->pinctrl, + gc8613->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(gc8613->xvclk, GC8613_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (27MHz)\n"); + if (clk_get_rate(gc8613->xvclk) != GC8613_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 27MHz\n"); + ret = clk_prepare_enable(gc8613->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(gc8613->reset_gpio)) + gpiod_set_value_cansleep(gc8613->reset_gpio, 0); + + if (!IS_ERR(gc8613->pwdn_gpio)) + gpiod_set_value_cansleep(gc8613->pwdn_gpio, 0); + + usleep_range(500, 1000); + ret = regulator_bulk_enable(GC8613_NUM_SUPPLIES, gc8613->supplies); + + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(gc8613->pwren_gpio)) + gpiod_set_value_cansleep(gc8613->pwren_gpio, 1); + + usleep_range(1000, 1100); + if (!IS_ERR(gc8613->pwdn_gpio)) + gpiod_set_value_cansleep(gc8613->pwdn_gpio, 1); + usleep_range(100, 150); + if (!IS_ERR(gc8613->reset_gpio)) + gpiod_set_value_cansleep(gc8613->reset_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = gc8613_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(gc8613->xvclk); + + return ret; +} + +static int gc8613_check_sensor_id(struct gc8613 *gc8613, + struct i2c_client *client) +{ + struct device *dev = &gc8613->client->dev; + u16 id = 0; + u32 reg_H = 0; + u32 reg_L = 0; + int ret; + + ret = gc8613_read_reg(client, GC8613_REG_CHIP_ID_H, + GC8613_REG_VALUE_08BIT, ®_H); + ret |= gc8613_read_reg(client, GC8613_REG_CHIP_ID_L, + GC8613_REG_VALUE_08BIT, ®_L); + id = ((reg_H << 8) & 0xff00) | (reg_L & 0xff); + if (!(reg_H == (CHIP_ID >> 8) || reg_L == (CHIP_ID & 0xff))) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + dev_info(dev, "detected gc%04x sensor\n", id); + return 0; +} + +static void __gc8613_power_off(struct gc8613 *gc8613) +{ + int ret; + struct device *dev = &gc8613->client->dev; + + if (!IS_ERR(gc8613->pwdn_gpio)) + gpiod_set_value_cansleep(gc8613->pwdn_gpio, 0); + clk_disable_unprepare(gc8613->xvclk); + if (!IS_ERR(gc8613->reset_gpio)) + gpiod_set_value_cansleep(gc8613->reset_gpio, 0); + if (!IS_ERR_OR_NULL(gc8613->pins_sleep)) { + ret = pinctrl_select_state(gc8613->pinctrl, + gc8613->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + regulator_bulk_disable(GC8613_NUM_SUPPLIES, gc8613->supplies); + if (!IS_ERR(gc8613->pwren_gpio)) + gpiod_set_value_cansleep(gc8613->pwren_gpio, 0); +} + +static int gc8613_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct gc8613 *gc8613; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + u32 i,hdr_mode = 0; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + gc8613 = devm_kzalloc(dev, sizeof(*gc8613), GFP_KERNEL); + if (!gc8613) + return -ENOMEM; + + of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode); + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &gc8613->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &gc8613->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &gc8613->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &gc8613->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + gc8613->client = client; + gc8613->cfg_num = ARRAY_SIZE(supported_modes); + + for (i = 0; i < gc8613->cfg_num; i++) { + if (hdr_mode == supported_modes[i].hdr_mode) { + gc8613->cur_mode = &supported_modes[i]; + break; + } + } + if (i == gc8613->cfg_num) + gc8613->cur_mode = &supported_modes[0]; + + gc8613->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(gc8613->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + gc8613->pwren_gpio = devm_gpiod_get(dev, "pwren", GPIOD_OUT_LOW); + if (IS_ERR(gc8613->pwren_gpio)) + dev_warn(dev, "Failed to get pwren-gpios\n"); + + gc8613->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gc8613->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + gc8613->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(gc8613->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + gc8613->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(gc8613->pinctrl)) { + gc8613->pins_default = + pinctrl_lookup_state(gc8613->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(gc8613->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + gc8613->pins_sleep = + pinctrl_lookup_state(gc8613->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(gc8613->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } else { + dev_err(dev, "no pinctrl\n"); + } + + ret = gc8613_configure_regulators(gc8613); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&gc8613->mutex); + + sd = &gc8613->subdev; + v4l2_i2c_subdev_init(sd, client, &gc8613_subdev_ops); + ret = gc8613_initialize_controls(gc8613); + if (ret) + goto err_destroy_mutex; + + ret = __gc8613_power_on(gc8613); + if (ret) + goto err_free_handler; + + usleep_range(3000, 4000); + + ret = gc8613_check_sensor_id(gc8613, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &gc8613_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + gc8613->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &gc8613->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(gc8613->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + gc8613->module_index, facing, + GC8613_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __gc8613_power_off(gc8613); +err_free_handler: + v4l2_ctrl_handler_free(&gc8613->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&gc8613->mutex); + + return ret; +} + +static int gc8613_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc8613 *gc8613 = to_gc8613(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&gc8613->ctrl_handler); + mutex_destroy(&gc8613->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __gc8613_power_off(gc8613); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +static int gc8613_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc8613 *gc8613 = to_gc8613(sd); + + return __gc8613_power_on(gc8613); +} + +static int gc8613_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc8613 *gc8613 = to_gc8613(sd); + + __gc8613_power_off(gc8613); + + return 0; +} + +static const struct dev_pm_ops gc8613_pm_ops = { + SET_RUNTIME_PM_OPS(gc8613_runtime_suspend, + gc8613_runtime_resume, NULL) +}; + + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id gc8613_of_match[] = { + { .compatible = "galaxycore,gc8613" }, + {}, +}; +MODULE_DEVICE_TABLE(of, gc8613_of_match); +#endif + +static const struct i2c_device_id gc8613_match_id[] = { + { "galaxycore,gc8613", 0 }, + { }, +}; + +static struct i2c_driver gc8613_i2c_driver = { + .driver = { + .name = GC8613_NAME, + .pm = &gc8613_pm_ops, + .of_match_table = of_match_ptr(gc8613_of_match), + }, + .probe = &gc8613_probe, + .remove = &gc8613_remove, + .id_table = gc8613_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&gc8613_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&gc8613_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("galaxycore gc8613 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c index 3c78fe098528d..83d968ab2314a 100644 --- a/drivers/media/i2c/imx415.c +++ b/drivers/media/i2c/imx415.c @@ -58,22 +58,17 @@ #define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN #endif -#define MIPI_FREQ_1188M 1188000000 #define MIPI_FREQ_891M 891000000 #define MIPI_FREQ_446M 446000000 #define MIPI_FREQ_743M 743000000 #define MIPI_FREQ_297M 297000000 #define IMX415_4LANES 4 -#define IMX415_2LANES 2 #define IMX415_MAX_PIXEL_RATE (MIPI_FREQ_891M / 10 * 2 * IMX415_4LANES) #define OF_CAMERA_HDR_MODE "rockchip,camera-hdr-mode" -#define DATA_LANES "rockchip,imx415-data-lanes" -#define OF_CAMERA_CAPTURE_MODE "rockchip,imx415-capture-mode" #define IMX415_XVCLK_FREQ_37M 37125000 -#define IMX415_XVCLK_FREQ_27M 27000000 /* TODO: Get the real chip id from reg */ #define CHIP_ID 0xE0 @@ -147,7 +142,6 @@ #define IMX415_FLIP_REG 0x3030 #define REG_NULL 0xFFFF -#define REG_DELAY 0xFFFE #define IMX415_REG_VALUE_08BIT 1 #define IMX415_REG_VALUE_16BIT 2 @@ -200,7 +194,6 @@ struct imx415_mode { const struct regval *reg_list; u32 hdr_mode; u32 vc[PAD_MAX]; - u32 xvclk; }; struct imx415 { @@ -230,7 +223,6 @@ struct imx415 { bool is_thunderboot; bool is_thunderboot_ng; bool is_first_streamoff; - const struct imx415_mode *supported_modes; const struct imx415_mode *cur_mode; u32 module_index; u32 cfg_num; @@ -240,8 +232,6 @@ struct imx415 { u32 cur_vts; bool has_init_exp; struct preisp_hdrae_exp_s init_hdrae_exp; - u32 lanes; - u32 capture_mode; }; static struct rkmodule_csi_dphy_param dcphy_param = { @@ -755,893 +745,6 @@ static __maybe_unused const struct regval imx415_hdr2_12bit_1932x1096_891M_regs[ {REG_NULL, 0x00}, }; -/* - * Xclk 27Mhz - * 15fps - * CSI-2_2lane - * AD:12bit Output:12bit - * 891Mbps - * Master Mode - * Time 9.988ms Gain:6dB - * All-pixel - */ -static __maybe_unused const struct regval imx415_linear_12bit_3864x2192_891M_regs_2lane[] = { - {0x3008, 0x5D}, - {0x300A, 0x42}, - {0x3028, 0x98}, - {0x3029, 0x08}, - {0x3033, 0x05}, - {0x3050, 0x79}, - {0x3051, 0x07}, - {0x3090, 0x14}, - {0x30C1, 0x00}, - {0x3116, 0x23}, - {0x3118, 0xC6}, - {0x311A, 0xE7}, - {0x311E, 0x23}, - {0x32D4, 0x21}, - {0x32EC, 0xA1}, - {0x344C, 0x2B}, - {0x344D, 0x01}, - {0x344E, 0xED}, - {0x344F, 0x01}, - {0x3450, 0xF6}, - {0x3451, 0x02}, - {0x3452, 0x7F}, - {0x3453, 0x03}, - {0x358A, 0x04}, - {0x35A1, 0x02}, - {0x35EC, 0x27}, - {0x35EE, 0x8D}, - {0x35F0, 0x8D}, - {0x35F2, 0x29}, - {0x36BC, 0x0C}, - {0x36CC, 0x53}, - {0x36CD, 0x00}, - {0x36CE, 0x3C}, - {0x36D0, 0x8C}, - {0x36D1, 0x00}, - {0x36D2, 0x71}, - {0x36D4, 0x3C}, - {0x36D6, 0x53}, - {0x36D7, 0x00}, - {0x36D8, 0x71}, - {0x36DA, 0x8C}, - {0x36DB, 0x00}, - {0x3720, 0x00}, - {0x3724, 0x02}, - {0x3726, 0x02}, - {0x3732, 0x02}, - {0x3734, 0x03}, - {0x3736, 0x03}, - {0x3742, 0x03}, - {0x3862, 0xE0}, - {0x38CC, 0x30}, - {0x38CD, 0x2F}, - {0x395C, 0x0C}, - {0x39A4, 0x07}, - {0x39A8, 0x32}, - {0x39AA, 0x32}, - {0x39AC, 0x32}, - {0x39AE, 0x32}, - {0x39B0, 0x32}, - {0x39B2, 0x2F}, - {0x39B4, 0x2D}, - {0x39B6, 0x28}, - {0x39B8, 0x30}, - {0x39BA, 0x30}, - {0x39BC, 0x30}, - {0x39BE, 0x30}, - {0x39C0, 0x30}, - {0x39C2, 0x2E}, - {0x39C4, 0x2B}, - {0x39C6, 0x25}, - {0x3A42, 0xD1}, - {0x3A4C, 0x77}, - {0x3AE0, 0x02}, - {0x3AEC, 0x0C}, - {0x3B00, 0x2E}, - {0x3B06, 0x29}, - {0x3B98, 0x25}, - {0x3B99, 0x21}, - {0x3B9B, 0x13}, - {0x3B9C, 0x13}, - {0x3B9D, 0x13}, - {0x3B9E, 0x13}, - {0x3BA1, 0x00}, - {0x3BA2, 0x06}, - {0x3BA3, 0x0B}, - {0x3BA4, 0x10}, - {0x3BA5, 0x14}, - {0x3BA6, 0x18}, - {0x3BA7, 0x1A}, - {0x3BA8, 0x1A}, - {0x3BA9, 0x1A}, - {0x3BAC, 0xED}, - {0x3BAD, 0x01}, - {0x3BAE, 0xF6}, - {0x3BAF, 0x02}, - {0x3BB0, 0xA2}, - {0x3BB1, 0x03}, - {0x3BB2, 0xE0}, - {0x3BB3, 0x03}, - {0x3BB4, 0xE0}, - {0x3BB5, 0x03}, - {0x3BB6, 0xE0}, - {0x3BB7, 0x03}, - {0x3BB8, 0xE0}, - {0x3BBA, 0xE0}, - {0x3BBC, 0xDA}, - {0x3BBE, 0x88}, - {0x3BC0, 0x44}, - {0x3BC2, 0x7B}, - {0x3BC4, 0xA2}, - {0x3BC8, 0xBD}, - {0x3BCA, 0xBD}, - {0x4001, 0x01}, - {0x4004, 0xC0}, - {0x4005, 0x06}, - {0x400C, 0x00}, - {0x4018, 0x7F}, - {0x401A, 0x37}, - {0x401C, 0x37}, - {0x401E, 0xF7}, - {0x401F, 0x00}, - {0x4020, 0x3F}, - {0x4022, 0x6F}, - {0x4024, 0x3F}, - {0x4026, 0x5F}, - {0x4028, 0x2F}, - {0x4074, 0x01}, - {0x3002, 0x00}, - //{0x3000, 0x00}, - {REG_DELAY, 0x1E},//wait_ms(30) - {REG_NULL, 0x00}, -}; - -static __maybe_unused const struct regval imx415_linear_10bit_3864x2192_891M_regs_2lane[] = { - {0x3008, 0x7F}, - {0x3031, 0x00}, - {0x3032, 0x00}, - {0x300A, 0x5B}, - {0x3028, 0x98}, - {0x3029, 0x08}, - {0x3033, 0x05}, - {0x3050, 0x79}, - {0x3051, 0x07}, - {0x3090, 0x14}, - {0x30C1, 0x00}, - {0x3116, 0x24}, - {0x3118, 0xC0}, - {0x311A, 0xE0}, - {0x311E, 0x24}, - {0x32D4, 0x21}, - {0x32EC, 0xA1}, - {0x344C, 0x2B}, - {0x344D, 0x01}, - {0x344E, 0xED}, - {0x344F, 0x01}, - {0x3450, 0xF6}, - {0x3451, 0x02}, - {0x3452, 0x7F}, - {0x3453, 0x03}, - {0x358A, 0x04}, - {0x35A1, 0x02}, - {0x35EC, 0x27}, - {0x35EE, 0x8D}, - {0x35F0, 0x8D}, - {0x35F2, 0x29}, - {0x36BC, 0x0C}, - {0x36CC, 0x53}, - {0x36CD, 0x00}, - {0x36CE, 0x3C}, - {0x36D0, 0x8C}, - {0x36D1, 0x00}, - {0x36D2, 0x71}, - {0x36D4, 0x3C}, - {0x36D6, 0x53}, - {0x36D7, 0x00}, - {0x36D8, 0x71}, - {0x36DA, 0x8C}, - {0x36DB, 0x00}, - {0x3720, 0x00}, - {0x3724, 0x02}, - {0x3726, 0x02}, - {0x3732, 0x02}, - {0x3734, 0x03}, - {0x3736, 0x03}, - {0x3742, 0x03}, - {0x3862, 0xE0}, - {0x38CC, 0x30}, - {0x38CD, 0x2F}, - {0x395C, 0x0C}, - {0x39A4, 0x07}, - {0x39A8, 0x32}, - {0x39AA, 0x32}, - {0x39AC, 0x32}, - {0x39AE, 0x32}, - {0x39B0, 0x32}, - {0x39B2, 0x2F}, - {0x39B4, 0x2D}, - {0x39B6, 0x28}, - {0x39B8, 0x30}, - {0x39BA, 0x30}, - {0x39BC, 0x30}, - {0x39BE, 0x30}, - {0x39C0, 0x30}, - {0x39C2, 0x2E}, - {0x39C4, 0x2B}, - {0x39C6, 0x25}, - {0x3A42, 0xD1}, - {0x3A4C, 0x77}, - {0x3AE0, 0x02}, - {0x3AEC, 0x0C}, - {0x3B00, 0x2E}, - {0x3B06, 0x29}, - {0x3B98, 0x25}, - {0x3B99, 0x21}, - {0x3B9B, 0x13}, - {0x3B9C, 0x13}, - {0x3B9D, 0x13}, - {0x3B9E, 0x13}, - {0x3BA1, 0x00}, - {0x3BA2, 0x06}, - {0x3BA3, 0x0B}, - {0x3BA4, 0x10}, - {0x3BA5, 0x14}, - {0x3BA6, 0x18}, - {0x3BA7, 0x1A}, - {0x3BA8, 0x1A}, - {0x3BA9, 0x1A}, - {0x3BAC, 0xED}, - {0x3BAD, 0x01}, - {0x3BAE, 0xF6}, - {0x3BAF, 0x02}, - {0x3BB0, 0xA2}, - {0x3BB1, 0x03}, - {0x3BB2, 0xE0}, - {0x3BB3, 0x03}, - {0x3BB4, 0xE0}, - {0x3BB5, 0x03}, - {0x3BB6, 0xE0}, - {0x3BB7, 0x03}, - {0x3BB8, 0xE0}, - {0x3BBA, 0xE0}, - {0x3BBC, 0xDA}, - {0x3BBE, 0x88}, - {0x3BC0, 0x44}, - {0x3BC2, 0x7B}, - {0x3BC4, 0xA2}, - {0x3BC8, 0xBD}, - {0x3BCA, 0xBD}, - {0x4001, 0x01}, - {0x4004, 0x48}, - {0x4005, 0x09}, - {0x400C, 0x00}, - {0x4018, 0x7F}, - {0x401A, 0x37}, - {0x401C, 0x37}, - {0x401E, 0xF7}, - {0x401F, 0x00}, - {0x4020, 0x3F}, - {0x4022, 0x6F}, - {0x4024, 0x3F}, - {0x4026, 0x5F}, - {0x4028, 0x2F}, - {0x4074, 0x01}, - {0x3002, 0x00}, - //{0x3000, 0x00}, - {REG_DELAY, 0x1E},//wait_ms(30) - {REG_NULL, 0x00}, -}; - - -static __maybe_unused const struct regval imx415_linear_10bit_1920x1080_891M_regs_2lane[] = { - {0x3008, 0x7f}, - {0x300A, 0x5b}, - {0x3034, 0x05}, - // {0x301C, 0x04}, - {0x3020, 0x01}, - {0x3021, 0x01}, - {0x3022, 0x01}, - {0x3024, 0xca}, - {0x3025, 0x08}, - {0x3028, 0x26}, - {0x3029, 0x02}, - {0x3031, 0x00}, - {0x3032, 0x00}, - {0x3033, 0x05}, - {0x3050, 0xC4}, - {0x3090, 0x14}, - {0x30C1, 0x00}, - {0x30D9, 0x02}, - {0x30DA, 0x01}, - {0x3116, 0x24}, - {0x3118, 0xC0}, - {0x311A, 0xE0}, - {0x311E, 0x24}, - {0x32D4, 0x21}, - {0x32EC, 0xA1}, - {0x344C, 0x2B}, - {0x344D, 0x01}, - {0x344E, 0xED}, - {0x344F, 0x01}, - {0x3450, 0xF6}, - {0x3451, 0x02}, - {0x3452, 0x7F}, - {0x3453, 0x03}, - {0x358A, 0x04}, - {0x35A1, 0x02}, - {0x35EC, 0x27}, - {0x35EE, 0x8D}, - {0x35F0, 0x8D}, - {0x35F2, 0x29}, - {0x36BC, 0x0C}, - {0x36CC, 0x53}, - {0x36CD, 0x00}, - {0x36CE, 0x3C}, - {0x36D0, 0x8C}, - {0x36D1, 0x00}, - {0x36D2, 0x71}, - {0x36D4, 0x3C}, - {0x36D6, 0x53}, - {0x36D7, 0x00}, - {0x36D8, 0x71}, - {0x36DA, 0x8C}, - {0x36DB, 0x00}, - {0x3701, 0x00}, - {0x3720, 0x00}, - {0x3724, 0x02}, - {0x3726, 0x02}, - {0x3732, 0x02}, - {0x3734, 0x03}, - {0x3736, 0x03}, - {0x3742, 0x03}, - {0x3862, 0xE0}, - {0x38CC, 0x30}, - {0x38CD, 0x2F}, - {0x395C, 0x0C}, - {0x39A4, 0x07}, - {0x39A8, 0x32}, - {0x39AA, 0x32}, - {0x39AC, 0x32}, - {0x39AE, 0x32}, - {0x39B0, 0x32}, - {0x39B2, 0x2F}, - {0x39B4, 0x2D}, - {0x39B6, 0x28}, - {0x39B8, 0x30}, - {0x39BA, 0x30}, - {0x39BC, 0x30}, - {0x39BE, 0x30}, - {0x39C0, 0x30}, - {0x39C2, 0x2E}, - {0x39C4, 0x2B}, - {0x39C6, 0x25}, - {0x3A42, 0xD1}, - {0x3A4C, 0x77}, - {0x3AE0, 0x02}, - {0x3AEC, 0x0C}, - {0x3B00, 0x2E}, - {0x3B06, 0x29}, - {0x3B98, 0x25}, - {0x3B99, 0x21}, - {0x3B9B, 0x13}, - {0x3B9C, 0x13}, - {0x3B9D, 0x13}, - {0x3B9E, 0x13}, - {0x3BA1, 0x00}, - {0x3BA2, 0x06}, - {0x3BA3, 0x0B}, - {0x3BA4, 0x10}, - {0x3BA5, 0x14}, - {0x3BA6, 0x18}, - {0x3BA7, 0x1A}, - {0x3BA8, 0x1A}, - {0x3BA9, 0x1A}, - {0x3BAC, 0xED}, - {0x3BAD, 0x01}, - {0x3BAE, 0xF6}, - {0x3BAF, 0x02}, - {0x3BB0, 0xA2}, - {0x3BB1, 0x03}, - {0x3BB2, 0xE0}, - {0x3BB3, 0x03}, - {0x3BB4, 0xE0}, - {0x3BB5, 0x03}, - {0x3BB6, 0xE0}, - {0x3BB7, 0x03}, - {0x3BB8, 0xE0}, - {0x3BBA, 0xE0}, - {0x3BBC, 0xDA}, - {0x3BBE, 0x88}, - {0x3BC0, 0x44}, - {0x3BC2, 0x7B}, - {0x3BC4, 0xA2}, - {0x3BC8, 0xBD}, - {0x3BCA, 0xBD}, - {0x4001, 0x01}, - {0x4004, 0x48}, - {0x4005, 0x09}, - {0x400c, 0x00}, - {0x4018, 0xE7}, - {0x401A, 0x37}, - {0x401C, 0x37}, - {0x401E, 0xf7}, - {0x401F, 0x00}, - {0x4020, 0x3f}, - {0x4022, 0x6F}, - {0x4023, 0x00}, - {0x4024, 0x3f}, - {0x4026, 0x5f}, - {0x4028, 0x2F}, - {0x4074, 0x1}, - {0x3002, 0x00}, - //{0x3000, 0x00}, - {REG_DELAY, 0x1E},//wait_ms(30) - {REG_NULL, 0x00}, -}; - - -static __maybe_unused const struct regval imx415_linear_10bit_1920x1080_891M_regs_4lane[] = { - {0x3008, 0x7f}, - {0x300A, 0x5b}, - {0x3034, 0x05}, - // {0x301C, 0x04}, - {0x3020, 0x01}, - {0x3021, 0x01}, - {0x3022, 0x01}, - {0x3024, 0xca}, - {0x3025, 0x08}, - {0x3028, 0x26}, - {0x3029, 0x02}, - {0x3031, 0x00}, - {0x3032, 0x00}, - {0x3033, 0x05}, - {0x3050, 0xC4}, - {0x3090, 0x14}, - {0x30C1, 0x00}, - {0x30D9, 0x02}, - {0x30DA, 0x01}, - {0x3116, 0x24}, - {0x3118, 0xC0}, - {0x311A, 0xE0}, - {0x311E, 0x24}, - {0x32D4, 0x21}, - {0x32EC, 0xA1}, - {0x344C, 0x2B}, - {0x344D, 0x01}, - {0x344E, 0xED}, - {0x344F, 0x01}, - {0x3450, 0xF6}, - {0x3451, 0x02}, - {0x3452, 0x7F}, - {0x3453, 0x03}, - {0x358A, 0x04}, - {0x35A1, 0x02}, - {0x35EC, 0x27}, - {0x35EE, 0x8D}, - {0x35F0, 0x8D}, - {0x35F2, 0x29}, - {0x36BC, 0x0C}, - {0x36CC, 0x53}, - {0x36CD, 0x00}, - {0x36CE, 0x3C}, - {0x36D0, 0x8C}, - {0x36D1, 0x00}, - {0x36D2, 0x71}, - {0x36D4, 0x3C}, - {0x36D6, 0x53}, - {0x36D7, 0x00}, - {0x36D8, 0x71}, - {0x36DA, 0x8C}, - {0x36DB, 0x00}, - {0x3701, 0x00}, - {0x3720, 0x00}, - {0x3724, 0x02}, - {0x3726, 0x02}, - {0x3732, 0x02}, - {0x3734, 0x03}, - {0x3736, 0x03}, - {0x3742, 0x03}, - {0x3862, 0xE0}, - {0x38CC, 0x30}, - {0x38CD, 0x2F}, - {0x395C, 0x0C}, - {0x39A4, 0x07}, - {0x39A8, 0x32}, - {0x39AA, 0x32}, - {0x39AC, 0x32}, - {0x39AE, 0x32}, - {0x39B0, 0x32}, - {0x39B2, 0x2F}, - {0x39B4, 0x2D}, - {0x39B6, 0x28}, - {0x39B8, 0x30}, - {0x39BA, 0x30}, - {0x39BC, 0x30}, - {0x39BE, 0x30}, - {0x39C0, 0x30}, - {0x39C2, 0x2E}, - {0x39C4, 0x2B}, - {0x39C6, 0x25}, - {0x3A42, 0xD1}, - {0x3A4C, 0x77}, - {0x3AE0, 0x02}, - {0x3AEC, 0x0C}, - {0x3B00, 0x2E}, - {0x3B06, 0x29}, - {0x3B98, 0x25}, - {0x3B99, 0x21}, - {0x3B9B, 0x13}, - {0x3B9C, 0x13}, - {0x3B9D, 0x13}, - {0x3B9E, 0x13}, - {0x3BA1, 0x00}, - {0x3BA2, 0x06}, - {0x3BA3, 0x0B}, - {0x3BA4, 0x10}, - {0x3BA5, 0x14}, - {0x3BA6, 0x18}, - {0x3BA7, 0x1A}, - {0x3BA8, 0x1A}, - {0x3BA9, 0x1A}, - {0x3BAC, 0xED}, - {0x3BAD, 0x01}, - {0x3BAE, 0xF6}, - {0x3BAF, 0x02}, - {0x3BB0, 0xA2}, - {0x3BB1, 0x03}, - {0x3BB2, 0xE0}, - {0x3BB3, 0x03}, - {0x3BB4, 0xE0}, - {0x3BB5, 0x03}, - {0x3BB6, 0xE0}, - {0x3BB7, 0x03}, - {0x3BB8, 0xE0}, - {0x3BBA, 0xE0}, - {0x3BBC, 0xDA}, - {0x3BBE, 0x88}, - {0x3BC0, 0x44}, - {0x3BC2, 0x7B}, - {0x3BC4, 0xA2}, - {0x3BC8, 0xBD}, - {0x3BCA, 0xBD}, - {0x4001, 0x03}, - {0x4004, 0x48}, - {0x4005, 0x09}, - {0x400c, 0x00}, - {0x4018, 0xE7}, - {0x401A, 0x37}, - {0x401C, 0x37}, - {0x401E, 0xf7}, - {0x401F, 0x00}, - {0x4020, 0x3f}, - {0x4022, 0x6F}, - {0x4023, 0x00}, - {0x4024, 0x3f}, - {0x4026, 0x5f}, - {0x4028, 0x2F}, - {0x4074, 0x1}, - {0x3002, 0x00}, - //{0x3000, 0x00}, - {REG_DELAY, 0x1E},//wait_ms(30) - {REG_NULL, 0x00}, -}; - -/* - * Xclk 27Mhz - * 90.059fps - * CSI-2_2lane - * AD:10bit Output:12bit - * 2376Mbps - * Master Mode - * Time 9.999ms Gain:6dB - * 2568x1440 2/2-line binning & Window cropping - */ -static __maybe_unused const struct regval imx415_linear_12bit_1284x720_2376M_regs_2lane[] = { - {0x3008, 0x5D}, - {0x300A, 0x42}, - {0x301C, 0x04}, - {0x3020, 0x01}, - {0x3021, 0x01}, - {0x3022, 0x01}, - {0x3024, 0xAB}, - {0x3025, 0x07}, - {0x3028, 0xA4}, - {0x3029, 0x01}, - {0x3031, 0x00}, - {0x3033, 0x00}, - {0x3040, 0x88}, - {0x3041, 0x02}, - {0x3042, 0x08}, - {0x3043, 0x0A}, - {0x3044, 0xF0}, - {0x3045, 0x02}, - {0x3046, 0x40}, - {0x3047, 0x0B}, - {0x3050, 0xC4}, - {0x3090, 0x14}, - {0x30C1, 0x00}, - {0x30D9, 0x02}, - {0x30DA, 0x01}, - {0x3116, 0x23}, - {0x3118, 0x08}, - {0x3119, 0x01}, - {0x311A, 0xE7}, - {0x311E, 0x23}, - {0x32D4, 0x21}, - {0x32EC, 0xA1}, - {0x344C, 0x2B}, - {0x344D, 0x01}, - {0x344E, 0xED}, - {0x344F, 0x01}, - {0x3450, 0xF6}, - {0x3451, 0x02}, - {0x3452, 0x7F}, - {0x3453, 0x03}, - {0x358A, 0x04}, - {0x35A1, 0x02}, - {0x35EC, 0x27}, - {0x35EE, 0x8D}, - {0x35F0, 0x8D}, - {0x35F2, 0x29}, - {0x36BC, 0x0C}, - {0x36CC, 0x53}, - {0x36CD, 0x00}, - {0x36CE, 0x3C}, - {0x36D0, 0x8C}, - {0x36D1, 0x00}, - {0x36D2, 0x71}, - {0x36D4, 0x3C}, - {0x36D6, 0x53}, - {0x36D7, 0x00}, - {0x36D8, 0x71}, - {0x36DA, 0x8C}, - {0x36DB, 0x00}, - {0x3701, 0x00}, - {0x3720, 0x00}, - {0x3724, 0x02}, - {0x3726, 0x02}, - {0x3732, 0x02}, - {0x3734, 0x03}, - {0x3736, 0x03}, - {0x3742, 0x03}, - {0x3862, 0xE0}, - {0x38CC, 0x30}, - {0x38CD, 0x2F}, - {0x395C, 0x0C}, - {0x39A4, 0x07}, - {0x39A8, 0x32}, - {0x39AA, 0x32}, - {0x39AC, 0x32}, - {0x39AE, 0x32}, - {0x39B0, 0x32}, - {0x39B2, 0x2F}, - {0x39B4, 0x2D}, - {0x39B6, 0x28}, - {0x39B8, 0x30}, - {0x39BA, 0x30}, - {0x39BC, 0x30}, - {0x39BE, 0x30}, - {0x39C0, 0x30}, - {0x39C2, 0x2E}, - {0x39C4, 0x2B}, - {0x39C6, 0x25}, - {0x3A42, 0xD1}, - {0x3A4C, 0x77}, - {0x3AE0, 0x02}, - {0x3AEC, 0x0C}, - {0x3B00, 0x2E}, - {0x3B06, 0x29}, - {0x3B98, 0x25}, - {0x3B99, 0x21}, - {0x3B9B, 0x13}, - {0x3B9C, 0x13}, - {0x3B9D, 0x13}, - {0x3B9E, 0x13}, - {0x3BA1, 0x00}, - {0x3BA2, 0x06}, - {0x3BA3, 0x0B}, - {0x3BA4, 0x10}, - {0x3BA5, 0x14}, - {0x3BA6, 0x18}, - {0x3BA7, 0x1A}, - {0x3BA8, 0x1A}, - {0x3BA9, 0x1A}, - {0x3BAC, 0xED}, - {0x3BAD, 0x01}, - {0x3BAE, 0xF6}, - {0x3BAF, 0x02}, - {0x3BB0, 0xA2}, - {0x3BB1, 0x03}, - {0x3BB2, 0xE0}, - {0x3BB3, 0x03}, - {0x3BB4, 0xE0}, - {0x3BB5, 0x03}, - {0x3BB6, 0xE0}, - {0x3BB7, 0x03}, - {0x3BB8, 0xE0}, - {0x3BBA, 0xE0}, - {0x3BBC, 0xDA}, - {0x3BBE, 0x88}, - {0x3BC0, 0x44}, - {0x3BC2, 0x7B}, - {0x3BC4, 0xA2}, - {0x3BC8, 0xBD}, - {0x3BCA, 0xBD}, - {0x4001, 0x01}, - {0x4004, 0xC0}, - {0x4005, 0x06}, - {0x4018, 0xE7}, - {0x401A, 0x8F}, - {0x401C, 0x8F}, - {0x401E, 0x7F}, - {0x401F, 0x02}, - {0x4020, 0x97}, - {0x4022, 0x0F}, - {0x4023, 0x01}, - {0x4024, 0x97}, - {0x4026, 0xF7}, - {0x4028, 0x7F}, - {0x3002, 0x00}, - //{0x3000, 0x00}, - {REG_DELAY, 0x1E},//wait_ms(30) - {REG_NULL, 0x00}, -}; - -static __maybe_unused const struct regval imx415_linear_10bit_1284x720_891M_regs_2lane[] = { - {0x3008, 0x5D}, - {0x300A, 0x42}, - {0x301C, 0x04}, - {0x3020, 0x01}, - {0x3021, 0x01}, - {0x3022, 0x01}, - {0x3024, 0xAB}, - {0x3025, 0x07}, - {0x3028, 0xA4}, - {0x3029, 0x01}, - {0x3031, 0x00}, - {0x3033, 0x00}, - {0x3040, 0x88}, - {0x3041, 0x02}, - {0x3042, 0x08}, - {0x3043, 0x0A}, - {0x3044, 0xF0}, - {0x3045, 0x02}, - {0x3046, 0x40}, - {0x3047, 0x0B}, - {0x3050, 0xC4}, - {0x3090, 0x14}, - {0x30C1, 0x00}, - {0x30D9, 0x02}, - {0x30DA, 0x01}, - {0x3116, 0x23}, - {0x3118, 0x08}, - {0x3119, 0x01}, - {0x311A, 0xE7}, - {0x311E, 0x23}, - {0x32D4, 0x21}, - {0x32EC, 0xA1}, - {0x344C, 0x2B}, - {0x344D, 0x01}, - {0x344E, 0xED}, - {0x344F, 0x01}, - {0x3450, 0xF6}, - {0x3451, 0x02}, - {0x3452, 0x7F}, - {0x3453, 0x03}, - {0x358A, 0x04}, - {0x35A1, 0x02}, - {0x35EC, 0x27}, - {0x35EE, 0x8D}, - {0x35F0, 0x8D}, - {0x35F2, 0x29}, - {0x36BC, 0x0C}, - {0x36CC, 0x53}, - {0x36CD, 0x00}, - {0x36CE, 0x3C}, - {0x36D0, 0x8C}, - {0x36D1, 0x00}, - {0x36D2, 0x71}, - {0x36D4, 0x3C}, - {0x36D6, 0x53}, - {0x36D7, 0x00}, - {0x36D8, 0x71}, - {0x36DA, 0x8C}, - {0x36DB, 0x00}, - {0x3701, 0x00}, - {0x3720, 0x00}, - {0x3724, 0x02}, - {0x3726, 0x02}, - {0x3732, 0x02}, - {0x3734, 0x03}, - {0x3736, 0x03}, - {0x3742, 0x03}, - {0x3862, 0xE0}, - {0x38CC, 0x30}, - {0x38CD, 0x2F}, - {0x395C, 0x0C}, - {0x39A4, 0x07}, - {0x39A8, 0x32}, - {0x39AA, 0x32}, - {0x39AC, 0x32}, - {0x39AE, 0x32}, - {0x39B0, 0x32}, - {0x39B2, 0x2F}, - {0x39B4, 0x2D}, - {0x39B6, 0x28}, - {0x39B8, 0x30}, - {0x39BA, 0x30}, - {0x39BC, 0x30}, - {0x39BE, 0x30}, - {0x39C0, 0x30}, - {0x39C2, 0x2E}, - {0x39C4, 0x2B}, - {0x39C6, 0x25}, - {0x3A42, 0xD1}, - {0x3A4C, 0x77}, - {0x3AE0, 0x02}, - {0x3AEC, 0x0C}, - {0x3B00, 0x2E}, - {0x3B06, 0x29}, - {0x3B98, 0x25}, - {0x3B99, 0x21}, - {0x3B9B, 0x13}, - {0x3B9C, 0x13}, - {0x3B9D, 0x13}, - {0x3B9E, 0x13}, - {0x3BA1, 0x00}, - {0x3BA2, 0x06}, - {0x3BA3, 0x0B}, - {0x3BA4, 0x10}, - {0x3BA5, 0x14}, - {0x3BA6, 0x18}, - {0x3BA7, 0x1A}, - {0x3BA8, 0x1A}, - {0x3BA9, 0x1A}, - {0x3BAC, 0xED}, - {0x3BAD, 0x01}, - {0x3BAE, 0xF6}, - {0x3BAF, 0x02}, - {0x3BB0, 0xA2}, - {0x3BB1, 0x03}, - {0x3BB2, 0xE0}, - {0x3BB3, 0x03}, - {0x3BB4, 0xE0}, - {0x3BB5, 0x03}, - {0x3BB6, 0xE0}, - {0x3BB7, 0x03}, - {0x3BB8, 0xE0}, - {0x3BBA, 0xE0}, - {0x3BBC, 0xDA}, - {0x3BBE, 0x88}, - {0x3BC0, 0x44}, - {0x3BC2, 0x7B}, - {0x3BC4, 0xA2}, - {0x3BC8, 0xBD}, - {0x3BCA, 0xBD}, - {0x4001, 0x01}, - {0x4004, 0xC0}, - {0x4005, 0x06}, - {0x4018, 0xE7}, - {0x401A, 0x8F}, - {0x401C, 0x8F}, - {0x401E, 0x7F}, - {0x401F, 0x02}, - {0x4020, 0x97}, - {0x4022, 0x0F}, - {0x4023, 0x01}, - {0x4024, 0x97}, - {0x4026, 0xF7}, - {0x4028, 0x7F}, - {0x3002, 0x00}, - //{0x3000, 0x00}, - {REG_DELAY, 0x1E},//wait_ms(30) - {REG_NULL, 0x00}, -}; - - /* * The width and height must be configured to be * the same as the current output resolution of the sensor. @@ -1676,29 +779,7 @@ static const struct imx415_mode supported_modes[] = { .mipi_freq_idx = 1, .bpp = 10, .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, - .xvclk = IMX415_XVCLK_FREQ_37M, - }, - { - /* 1H period = (1100 clock) = (1100 * 1 / 74.25MHz) */ - .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, - .width = 1944, - .height = 1096, - .max_fps = { - .numerator = 10000, - .denominator = 600000, - }, - .exp_def = 0x08ca - 0x08, - .hts_def = 0x044c *2, - .vts_def = 0x08ca, - .global_reg_list = NULL, - .reg_list = imx415_linear_10bit_1920x1080_891M_regs_4lane, - .hdr_mode = NO_HDR, - .mipi_freq_idx = 1, - .bpp = 10, - .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, - .xvclk = IMX415_XVCLK_FREQ_37M, }, -#if 0 { .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, .width = 3864, @@ -1723,7 +804,6 @@ static const struct imx415_mode supported_modes[] = { .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_1, .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr2 - .xvclk = IMX415_XVCLK_FREQ_37M, }, { .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, @@ -1749,7 +829,6 @@ static const struct imx415_mode supported_modes[] = { .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr0 .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_2,//S->csi wr2 - .xvclk = IMX415_XVCLK_FREQ_37M, }, { .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, @@ -1775,7 +854,6 @@ static const struct imx415_mode supported_modes[] = { .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr0 .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_2,//S->csi wr2 - .xvclk = IMX415_XVCLK_FREQ_37M, }, { /* 1H period = (1100 clock) = (1100 * 1 / 74.25MHz) */ @@ -1795,7 +873,6 @@ static const struct imx415_mode supported_modes[] = { .mipi_freq_idx = 1, .bpp = 12, .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, - .xvclk = IMX415_XVCLK_FREQ_37M, }, { .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, @@ -1821,7 +898,6 @@ static const struct imx415_mode supported_modes[] = { .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_1, .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr2 - .xvclk = IMX415_XVCLK_FREQ_37M, }, { .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, @@ -1847,7 +923,6 @@ static const struct imx415_mode supported_modes[] = { .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr0 .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_2,//S->csi wr2 - .xvclk = IMX415_XVCLK_FREQ_37M, }, { .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, @@ -1866,7 +941,6 @@ static const struct imx415_mode supported_modes[] = { .mipi_freq_idx = 0, .bpp = 12, .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, - .xvclk = IMX415_XVCLK_FREQ_37M, }, { .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, @@ -1892,51 +966,6 @@ static const struct imx415_mode supported_modes[] = { .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_1, .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr2 - .xvclk = IMX415_XVCLK_FREQ_37M, - }, -#endif -}; - -static const struct imx415_mode supported_modes_2lane[] = { - { - /* 1H period = (1100 clock) = (1100 * 1 / 74.25MHz) */ - .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, - .width = 3864, - .height = 2192, - .max_fps = { - .numerator = 10000, - .denominator = 150000, - }, - .exp_def = 0x08ca - 0x08, - .hts_def = 0x0898 * IMX415_2LANES * 2, - .vts_def = 0x08ca, - .global_reg_list = NULL, - .reg_list = imx415_linear_10bit_3864x2192_891M_regs_2lane, - .hdr_mode = NO_HDR, - .mipi_freq_idx = 1, - .bpp = 10, - .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, - .xvclk = IMX415_XVCLK_FREQ_37M, - }, - { - /* 1H period = (1100 clock) = (1100 * 1 / 74.25MHz) */ - .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, - .width = 1944, - .height = 1096, - .max_fps = { - .numerator = 10000, - .denominator = 600000, - }, - .exp_def = 0x08ca - 0x08, - .hts_def = 0x0800, - .vts_def = 0x08ca, - .global_reg_list = NULL, - .reg_list = imx415_linear_10bit_1920x1080_891M_regs_2lane, - .hdr_mode = NO_HDR, - .mipi_freq_idx = 1, - .bpp = 10, - .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, - .xvclk = IMX415_XVCLK_FREQ_37M, }, }; @@ -1945,7 +974,6 @@ static const s64 link_freq_items[] = { MIPI_FREQ_446M, MIPI_FREQ_743M, MIPI_FREQ_891M, - MIPI_FREQ_1188M, }; /* Write registers up to 4 at a time */ @@ -1982,18 +1010,10 @@ static int imx415_write_array(struct i2c_client *client, { u32 i; int ret = 0; - if (!regs) { - dev_err(&client->dev, "write reg array error\n"); - return ret; - } + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { - if (regs[i].addr == REG_DELAY) { - usleep_range(regs[i].val * 1000, regs[i].val * 1000 + 500); - dev_info(&client->dev, "write reg array, sleep %dms\n", regs[i].val); - } else { - ret = imx415_write_reg(client, regs[i].addr, - IMX415_REG_VALUE_08BIT, regs[i].val); - } + ret = imx415_write_reg(client, regs[i].addr, + IMX415_REG_VALUE_08BIT, regs[i].val); } return ret; } @@ -2050,9 +1070,9 @@ imx415_find_best_fit(struct imx415 *imx415, struct v4l2_subdev_format *fmt) unsigned int i; for (i = 0; i < imx415->cfg_num; i++) { - dist = imx415_get_reso_dist(&imx415->supported_modes[i], framefmt); + dist = imx415_get_reso_dist(&supported_modes[i], framefmt); if ((cur_best_fit_dist == -1 || dist < cur_best_fit_dist) && - imx415->supported_modes[i].bus_fmt == framefmt->code) { + supported_modes[i].bus_fmt == framefmt->code) { cur_best_fit_dist = dist; cur_best_fit = i; } @@ -2060,7 +1080,7 @@ imx415_find_best_fit(struct imx415 *imx415, struct v4l2_subdev_format *fmt) dev_info(&imx415->client->dev, "%s: cur_best_fit(%d)", __func__, cur_best_fit); - return &imx415->supported_modes[cur_best_fit]; + return &supported_modes[cur_best_fit]; } static int __imx415_power_on(struct imx415 *imx415); @@ -2074,8 +1094,8 @@ static void imx415_change_mode(struct imx415 *imx415, const struct imx415_mode * } imx415->cur_mode = mode; imx415->cur_vts = imx415->cur_mode->vts_def; - dev_info(&imx415->client->dev, "set fmt: cur_mode: %dx%d, hdr: %d, bpp: %d\n", - mode->width, mode->height, mode->hdr_mode, mode->bpp); + dev_dbg(&imx415->client->dev, "set fmt: cur_mode: %dx%d, hdr: %d\n", + mode->width, mode->height, mode->hdr_mode); } static int imx415_set_fmt(struct v4l2_subdev *sd, @@ -2086,7 +1106,6 @@ static int imx415_set_fmt(struct v4l2_subdev *sd, const struct imx415_mode *mode; s64 h_blank, vblank_def, vblank_min; u64 pixel_rate = 0; - u8 lanes = imx415->lanes; mutex_lock(&imx415->mutex); @@ -2115,8 +1134,7 @@ static int imx415_set_fmt(struct v4l2_subdev *sd, 1, vblank_def); __v4l2_ctrl_s_ctrl(imx415->vblank, vblank_def); __v4l2_ctrl_s_ctrl(imx415->link_freq, mode->mipi_freq_idx); - pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / - mode->bpp * 2 * lanes; + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * IMX415_4LANES; __v4l2_ctrl_s_ctrl_int64(imx415->pixel_rate, pixel_rate); } @@ -2167,7 +1185,7 @@ static int imx415_enum_mbus_code(struct v4l2_subdev *sd, if (code->index >= imx415->cfg_num) return -EINVAL; - code->code = imx415->supported_modes[code->index].bus_fmt; + code->code = supported_modes[code->index].bus_fmt; return 0; } @@ -2181,13 +1199,13 @@ static int imx415_enum_frame_sizes(struct v4l2_subdev *sd, if (fse->index >= imx415->cfg_num) return -EINVAL; - if (fse->code != imx415->supported_modes[fse->index].bus_fmt) + if (fse->code != supported_modes[fse->index].bus_fmt) return -EINVAL; - fse->min_width = imx415->supported_modes[fse->index].width; - fse->max_width = imx415->supported_modes[fse->index].width; - fse->max_height = imx415->supported_modes[fse->index].height; - fse->min_height = imx415->supported_modes[fse->index].height; + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; return 0; } @@ -2209,9 +1227,8 @@ static int imx415_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, struct imx415 *imx415 = to_imx415(sd); const struct imx415_mode *mode = imx415->cur_mode; u32 val = 0; - u8 lanes = imx415->lanes; - val = 1 << (lanes - 1) | + val = 1 << (IMX415_4LANES - 1) | V4L2_MBUS_CSI2_CHANNEL_0 | V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; if (mode->hdr_mode != NO_HDR) @@ -2665,12 +1682,11 @@ static long imx415_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) struct imx415 *imx415 = to_imx415(sd); struct rkmodule_hdr_cfg *hdr; struct rkmodule_channel_info *ch_info; - u32 h, w, stream; + u32 i, h, w, stream; long ret = 0; const struct imx415_mode *mode; u64 pixel_rate = 0; struct rkmodule_csi_dphy_param *dphy_param; - u8 lanes = imx415->lanes; switch (cmd) { case PREISP_CMD_SET_HDRAE_EXP: @@ -2688,41 +1704,48 @@ static long imx415_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) hdr->hdr_mode = imx415->cur_mode->hdr_mode; break; case RKMODULE_SET_HDR_CFG: + hdr = (struct rkmodule_hdr_cfg *)arg; + w = imx415->cur_mode->width; + h = imx415->cur_mode->height; + for (i = 0; i < imx415->cfg_num; i++) { + if (w == supported_modes[i].width && + h == supported_modes[i].height && + supported_modes[i].hdr_mode == hdr->hdr_mode) { + imx415_change_mode(imx415, &supported_modes[i]); + break; + } + } + if (i == imx415->cfg_num) { + dev_err(&imx415->client->dev, + "not find hdr mode:%d %dx%d config\n", + hdr->hdr_mode, w, h); + ret = -EINVAL; + } else { + mode = imx415->cur_mode; + if (imx415->streaming) { + ret = imx415_write_reg(imx415->client, IMX415_GROUP_HOLD_REG, + IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_START); - imx415_change_mode(imx415, &imx415->supported_modes[imx415->capture_mode]); - - if(imx415->capture_mode == 0) - dev_info(&imx415->client->dev, "capture_mode is 3840x2160\n"); - else if(imx415->capture_mode == 1) - dev_info(&imx415->client->dev, "capture_mode is 1920x1080\n"); - else - dev_info(&imx415->client->dev, "not support capture_mode0\n"); - - mode = imx415->cur_mode; - if (imx415->streaming) { - ret = imx415_write_reg(imx415->client, IMX415_GROUP_HOLD_REG, - IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_START); - - ret |= imx415_write_array(imx415->client, imx415->cur_mode->reg_list); + ret |= imx415_write_array(imx415->client, imx415->cur_mode->reg_list); - ret |= imx415_write_reg(imx415->client, IMX415_GROUP_HOLD_REG, - IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_END); - if (ret) - return ret; + ret |= imx415_write_reg(imx415->client, IMX415_GROUP_HOLD_REG, + IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_END); + if (ret) + return ret; + } + w = mode->hts_def - imx415->cur_mode->width; + h = mode->vts_def - mode->height; + mutex_lock(&imx415->mutex); + __v4l2_ctrl_modify_range(imx415->hblank, w, w, 1, w); + __v4l2_ctrl_modify_range(imx415->vblank, h, + IMX415_VTS_MAX - mode->height, + 1, h); + __v4l2_ctrl_s_ctrl(imx415->link_freq, mode->mipi_freq_idx); + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * IMX415_4LANES; + __v4l2_ctrl_s_ctrl_int64(imx415->pixel_rate, + pixel_rate); + mutex_unlock(&imx415->mutex); } - w = mode->hts_def - imx415->cur_mode->width; - h = mode->vts_def - mode->height; - mutex_lock(&imx415->mutex); - __v4l2_ctrl_modify_range(imx415->hblank, w, w, 1, w); - __v4l2_ctrl_modify_range(imx415->vblank, h, - IMX415_VTS_MAX - mode->height, - 1, h); - __v4l2_ctrl_s_ctrl(imx415->link_freq, mode->mipi_freq_idx); - pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / - mode->bpp * 2 * lanes; - __v4l2_ctrl_s_ctrl_int64(imx415->pixel_rate, - pixel_rate); - mutex_unlock(&imx415->mutex); break; case RKMODULE_SET_QUICK_STREAM: @@ -3022,6 +2045,10 @@ int __imx415_power_on(struct imx415 *imx415) { int ret; struct device *dev = &imx415->client->dev; + + if (imx415->is_thunderboot) + return 0; + if (!IS_ERR_OR_NULL(imx415->pins_default)) { ret = pinctrl_select_state(imx415->pinctrl, imx415->pins_default); @@ -3029,23 +2056,26 @@ int __imx415_power_on(struct imx415 *imx415) dev_err(dev, "could not set pins\n"); } - if (!imx415->is_thunderboot) { - if (!IS_ERR(imx415->power_gpio)) - gpiod_direction_output(imx415->power_gpio, 1); - /* At least 500ns between power raising and XCLR */ - /* fix power on timing if insmod this ko */ - usleep_range(10 * 1000, 20 * 1000); - if (!IS_ERR(imx415->reset_gpio)) - gpiod_direction_output(imx415->reset_gpio, 0); - - /* At least 1us between XCLR and clk */ - /* fix power on timing if insmod this ko */ - usleep_range(10 * 1000, 20 * 1000); + ret = regulator_bulk_enable(IMX415_NUM_SUPPLIES, imx415->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto err_pinctrl; } - ret = clk_set_rate(imx415->xvclk, imx415->cur_mode->xvclk); + if (!IS_ERR(imx415->power_gpio)) + gpiod_direction_output(imx415->power_gpio, 1); + /* At least 500ns between power raising and XCLR */ + /* fix power on timing if insmod this ko */ + usleep_range(10 * 1000, 20 * 1000); + if (!IS_ERR(imx415->reset_gpio)) + gpiod_direction_output(imx415->reset_gpio, 0); + + /* At least 1us between XCLR and clk */ + /* fix power on timing if insmod this ko */ + usleep_range(10 * 1000, 20 * 1000); + ret = clk_set_rate(imx415->xvclk, IMX415_XVCLK_FREQ_37M); if (ret < 0) dev_warn(dev, "Failed to set xvclk rate\n"); - if (clk_get_rate(imx415->xvclk) != imx415->cur_mode->xvclk) + if (clk_get_rate(imx415->xvclk) != IMX415_XVCLK_FREQ_37M) dev_warn(dev, "xvclk mismatched\n"); ret = clk_prepare_enable(imx415->xvclk); if (ret < 0) { @@ -3061,6 +2091,9 @@ int __imx415_power_on(struct imx415 *imx415) err_clk: if (!IS_ERR(imx415->reset_gpio)) gpiod_direction_output(imx415->reset_gpio, 1); + regulator_bulk_disable(IMX415_NUM_SUPPLIES, imx415->supplies); + +err_pinctrl: if (!IS_ERR_OR_NULL(imx415->pins_sleep)) pinctrl_select_state(imx415->pinctrl, imx415->pins_sleep); @@ -3121,7 +2154,7 @@ static int imx415_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) struct imx415 *imx415 = to_imx415(sd); struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, fh->pad, 0); - const struct imx415_mode *def_mode = &imx415->supported_modes[0]; + const struct imx415_mode *def_mode = &supported_modes[0]; mutex_lock(&imx415->mutex); /* Initialize try_fmt */ @@ -3146,11 +2179,11 @@ static int imx415_enum_frame_interval(struct v4l2_subdev *sd, if (fie->index >= imx415->cfg_num) return -EINVAL; - fie->code = imx415->supported_modes[fie->index].bus_fmt; - fie->width = imx415->supported_modes[fie->index].width; - fie->height = imx415->supported_modes[fie->index].height; - fie->interval = imx415->supported_modes[fie->index].max_fps; - fie->reserved[0] = imx415->supported_modes[fie->index].hdr_mode; + fie->code = supported_modes[fie->index].bus_fmt; + fie->width = supported_modes[fie->index].width; + fie->height = supported_modes[fie->index].height; + fie->interval = supported_modes[fie->index].max_fps; + fie->reserved[0] = supported_modes[fie->index].hdr_mode; return 0; } @@ -3159,8 +2192,6 @@ static int imx415_enum_frame_interval(struct v4l2_subdev *sd, #define DST_HEIGHT_2160 2160 #define DST_WIDTH_1920 1920 #define DST_HEIGHT_1080 1080 -#define DST_WIDTH_1280 1280 -#define DST_HEIGHT_720 720 /* * The resolution of the driver configuration needs to be exactly @@ -3188,11 +2219,6 @@ static int imx415_get_selection(struct v4l2_subdev *sd, sel->r.width = DST_WIDTH_1920; sel->r.top = CROP_START(imx415->cur_mode->height, DST_HEIGHT_1080); sel->r.height = DST_HEIGHT_1080; - } else if (imx415->cur_mode->width == 1284) { - sel->r.left = CROP_START(imx415->cur_mode->width, DST_WIDTH_1280); - sel->r.width = DST_WIDTH_1280; - sel->r.top = CROP_START(imx415->cur_mode->height, DST_HEIGHT_720); - sel->r.height = DST_HEIGHT_720; } else { sel->r.left = CROP_START(imx415->cur_mode->width, imx415->cur_mode->width); sel->r.width = imx415->cur_mode->width; @@ -3375,10 +2401,8 @@ static int imx415_initialize_controls(struct imx415 *imx415) struct v4l2_ctrl_handler *handler; s64 exposure_max, vblank_def; u64 pixel_rate; - u64 max_pixel_rate; u32 h_blank; int ret; - u8 lanes = imx415->lanes; handler = &imx415->ctrl_handler; mode = imx415->cur_mode; @@ -3394,10 +2418,9 @@ static int imx415_initialize_controls(struct imx415 *imx415) v4l2_ctrl_s_ctrl(imx415->link_freq, mode->mipi_freq_idx); /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ - pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * lanes; - max_pixel_rate = MIPI_FREQ_1188M / mode->bpp * 2 * lanes; + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * IMX415_4LANES; imx415->pixel_rate = v4l2_ctrl_new_std(handler, NULL, - V4L2_CID_PIXEL_RATE, 0, max_pixel_rate, + V4L2_CID_PIXEL_RATE, 0, IMX415_MAX_PIXEL_RATE, 1, pixel_rate); h_blank = mode->hts_def - mode->width; @@ -3490,7 +2513,7 @@ static int imx415_probe(struct i2c_client *client, struct v4l2_subdev *sd; char facing[2]; int ret; - u32 hdr_mode; + u32 i, hdr_mode = 0; dev_info(dev, "driver version: %02x.%02x.%02x", DRIVER_VERSION >> 16, @@ -3520,32 +2543,16 @@ static int imx415_probe(struct i2c_client *client, dev_warn(dev, " Get hdr mode failed! no hdr default\n"); } imx415->client = client; - - ret = of_property_read_u32(node, DATA_LANES, &imx415->lanes); - if (ret) { - imx415->lanes = 4; - dev_warn(dev, " Get data lanes failed! 4 lanes default\n"); - } - if (imx415->lanes == IMX415_4LANES) { - imx415->supported_modes = supported_modes; - imx415->cfg_num = ARRAY_SIZE(supported_modes); - } else { - imx415->supported_modes = supported_modes_2lane; - imx415->cfg_num = ARRAY_SIZE(supported_modes_2lane); + imx415->cfg_num = ARRAY_SIZE(supported_modes); + for (i = 0; i < imx415->cfg_num; i++) { + if (hdr_mode == supported_modes[i].hdr_mode) { + imx415->cur_mode = &supported_modes[i]; + break; + } } - dev_info(dev, "detect imx415 lane %d\n", - imx415->lanes); imx415->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP); - ret = of_property_read_u32(node, OF_CAMERA_CAPTURE_MODE, &imx415->capture_mode); - if (ret) { - imx415->capture_mode = 0; - dev_warn(dev, " Get capture_mode failed! captrue revolution setting to 3840x2160\n"); - } - - imx415->cur_mode = &imx415->supported_modes[imx415->capture_mode]; - imx415->xvclk = devm_clk_get(dev, "xvclk"); if (IS_ERR(imx415->xvclk)) { dev_err(dev, "Failed to get xvclk\n"); @@ -3703,4 +2710,4 @@ device_initcall_sync(sensor_mod_init); module_exit(sensor_mod_exit); MODULE_DESCRIPTION("Sony imx415 sensor driver"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL v2"); \ No newline at end of file diff --git a/drivers/media/i2c/imx415.c.bak b/drivers/media/i2c/imx415.c.bak new file mode 100644 index 0000000000000..3c78fe098528d --- /dev/null +++ b/drivers/media/i2c/imx415.c.bak @@ -0,0 +1,3706 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * imx415 driver + * + * Copyright (C) 2020 Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X00 first version. + * V0.0X01.0X01 + * 1. fix hdr ae ratio error, + * 0x3260 should be set 0x01 in normal mode, + * should be 0x00 in hdr mode. + * 2. rhs1 should be 4n+1 when set hdr ae. + * V0.0X01.0X02 + * 1. shr0 should be greater than (rsh1 + 9). + * 2. rhs1 should be ceil to 4n + 1. + * V0.0X01.0X03 + * 1. support 12bit HDR DOL3 + * 2. support HDR/Linear quick switch + * V0.0X01.0X04 + * 1. support enum format info by aiq + * V0.0X01.0X05 + * 1. fixed 10bit hdr2/hdr3 frame rate issue + * V0.0X01.0X06 + * 1. support DOL3 10bit 20fps 1485Mbps + * 2. fixed linkfreq error + * V0.0X01.0X07 + * 1. fix set_fmt & ioctl get mode unmatched issue. + * 2. need to set default vblank when change format. + * 3. enum all supported mode mbus_code, not just cur_mode. + * V0.0X01.0X08 + * 1. add dcphy param for hdrx2 mode. + */ + +#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../platform/rockchip/isp/rkisp_tb_helper.h" + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x08) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define MIPI_FREQ_1188M 1188000000 +#define MIPI_FREQ_891M 891000000 +#define MIPI_FREQ_446M 446000000 +#define MIPI_FREQ_743M 743000000 +#define MIPI_FREQ_297M 297000000 + +#define IMX415_4LANES 4 +#define IMX415_2LANES 2 + +#define IMX415_MAX_PIXEL_RATE (MIPI_FREQ_891M / 10 * 2 * IMX415_4LANES) +#define OF_CAMERA_HDR_MODE "rockchip,camera-hdr-mode" +#define DATA_LANES "rockchip,imx415-data-lanes" +#define OF_CAMERA_CAPTURE_MODE "rockchip,imx415-capture-mode" + +#define IMX415_XVCLK_FREQ_37M 37125000 +#define IMX415_XVCLK_FREQ_27M 27000000 + +/* TODO: Get the real chip id from reg */ +#define CHIP_ID 0xE0 +#define IMX415_REG_CHIP_ID 0x311A + +#define IMX415_REG_CTRL_MODE 0x3000 +#define IMX415_MODE_SW_STANDBY BIT(0) +#define IMX415_MODE_STREAMING 0x0 + +#define IMX415_LF_GAIN_REG_H 0x3091 +#define IMX415_LF_GAIN_REG_L 0x3090 + +#define IMX415_SF1_GAIN_REG_H 0x3093 +#define IMX415_SF1_GAIN_REG_L 0x3092 + +#define IMX415_SF2_GAIN_REG_H 0x3095 +#define IMX415_SF2_GAIN_REG_L 0x3094 + +#define IMX415_LF_EXPO_REG_H 0x3052 +#define IMX415_LF_EXPO_REG_M 0x3051 +#define IMX415_LF_EXPO_REG_L 0x3050 + +#define IMX415_SF1_EXPO_REG_H 0x3056 +#define IMX415_SF1_EXPO_REG_M 0x3055 +#define IMX415_SF1_EXPO_REG_L 0x3054 + +#define IMX415_SF2_EXPO_REG_H 0x305A +#define IMX415_SF2_EXPO_REG_M 0x3059 +#define IMX415_SF2_EXPO_REG_L 0x3058 + +#define IMX415_RHS1_REG_H 0x3062 +#define IMX415_RHS1_REG_M 0x3061 +#define IMX415_RHS1_REG_L 0x3060 +#define IMX415_RHS1_DEFAULT 0x004D + +#define IMX415_RHS2_REG_H 0x3066 +#define IMX415_RHS2_REG_M 0x3065 +#define IMX415_RHS2_REG_L 0x3064 +#define IMX415_RHS2_DEFAULT 0x004D + +#define IMX415_EXPOSURE_MIN 4 +#define IMX415_EXPOSURE_STEP 1 +#define IMX415_VTS_MAX 0x7fff + +#define IMX415_GAIN_MIN 0x00 +#define IMX415_GAIN_MAX 0xf0 +#define IMX415_GAIN_STEP 1 +#define IMX415_GAIN_DEFAULT 0x00 + +#define IMX415_FETCH_GAIN_H(VAL) (((VAL) >> 8) & 0x07) +#define IMX415_FETCH_GAIN_L(VAL) ((VAL) & 0xFF) + +#define IMX415_FETCH_EXP_H(VAL) (((VAL) >> 16) & 0x0F) +#define IMX415_FETCH_EXP_M(VAL) (((VAL) >> 8) & 0xFF) +#define IMX415_FETCH_EXP_L(VAL) ((VAL) & 0xFF) + +#define IMX415_FETCH_RHS1_H(VAL) (((VAL) >> 16) & 0x0F) +#define IMX415_FETCH_RHS1_M(VAL) (((VAL) >> 8) & 0xFF) +#define IMX415_FETCH_RHS1_L(VAL) ((VAL) & 0xFF) + +#define IMX415_FETCH_VTS_H(VAL) (((VAL) >> 16) & 0x0F) +#define IMX415_FETCH_VTS_M(VAL) (((VAL) >> 8) & 0xFF) +#define IMX415_FETCH_VTS_L(VAL) ((VAL) & 0xFF) + +#define IMX415_VTS_REG_L 0x3024 +#define IMX415_VTS_REG_M 0x3025 +#define IMX415_VTS_REG_H 0x3026 + +#define IMX415_MIRROR_BIT_MASK BIT(0) +#define IMX415_FLIP_BIT_MASK BIT(1) +#define IMX415_FLIP_REG 0x3030 + +#define REG_NULL 0xFFFF +#define REG_DELAY 0xFFFE + +#define IMX415_REG_VALUE_08BIT 1 +#define IMX415_REG_VALUE_16BIT 2 +#define IMX415_REG_VALUE_24BIT 3 + +#define IMX415_GROUP_HOLD_REG 0x3001 +#define IMX415_GROUP_HOLD_START 0x01 +#define IMX415_GROUP_HOLD_END 0x00 + +/* Basic Readout Lines. Number of necessary readout lines in sensor */ +#define BRL_ALL 2228u +#define BRL_BINNING 1115u +/* Readout timing setting of SEF1(DOL2): RHS1 < 2 * BRL and should be 4n + 1 */ +#define RHS1_MAX_X2(VAL) (((VAL) * 2 - 1) / 4 * 4 + 1) +#define SHR1_MIN_X2 9u + +/* Readout timing setting of SEF1(DOL3): RHS1 < 3 * BRL and should be 6n + 1 */ +#define RHS1_MAX_X3(VAL) (((VAL) * 3 - 1) / 6 * 6 + 1) +#define SHR1_MIN_X3 13u + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define IMX415_NAME "imx415" + +static const char * const imx415_supply_names[] = { + "dvdd", /* Digital core power */ + "dovdd", /* Digital I/O power */ + "avdd", /* Analog power */ +}; + +#define IMX415_NUM_SUPPLIES ARRAY_SIZE(imx415_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +struct imx415_mode { + u32 bus_fmt; + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + u32 mipi_freq_idx; + u32 bpp; + const struct regval *global_reg_list; + const struct regval *reg_list; + u32 hdr_mode; + u32 vc[PAD_MAX]; + u32 xvclk; +}; + +struct imx415 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *power_gpio; + struct regulator_bulk_data supplies[IMX415_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_a_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct mutex mutex; + bool streaming; + bool power_on; + bool is_thunderboot; + bool is_thunderboot_ng; + bool is_first_streamoff; + const struct imx415_mode *supported_modes; + const struct imx415_mode *cur_mode; + u32 module_index; + u32 cfg_num; + const char *module_facing; + const char *module_name; + const char *len_name; + u32 cur_vts; + bool has_init_exp; + struct preisp_hdrae_exp_s init_hdrae_exp; + u32 lanes; + u32 capture_mode; +}; + +static struct rkmodule_csi_dphy_param dcphy_param = { + .vendor = PHY_VENDOR_SAMSUNG, + .lp_vol_ref = 6, + .lp_hys_sw = {3, 0, 0, 0}, + .lp_escclk_pol_sel = {1, 1, 1, 1}, + .skew_data_cal_clk = {0, 3, 3, 3}, + .clk_hs_term_sel = 2, + .data_hs_term_sel = {2, 2, 2, 2}, + .reserved = {0}, +}; + +#define to_imx415(sd) container_of(sd, struct imx415, subdev) + +/* + * Xclk 37.125Mhz + */ +static __maybe_unused const struct regval imx415_global_12bit_3864x2192_regs[] = { + {0x3002, 0x00}, + {0x3008, 0x7F}, + {0x300A, 0x5B}, + {0x30C1, 0x00}, + {0x3031, 0x01}, + {0x3032, 0x01}, + {0x30D9, 0x06}, + {0x3116, 0x24}, + {0x3118, 0xC0}, + {0x311E, 0x24}, + {0x32D4, 0x21}, + {0x32EC, 0xA1}, + {0x3452, 0x7F}, + {0x3453, 0x03}, + {0x358A, 0x04}, + {0x35A1, 0x02}, + {0x36BC, 0x0C}, + {0x36CC, 0x53}, + {0x36CD, 0x00}, + {0x36CE, 0x3C}, + {0x36D0, 0x8C}, + {0x36D1, 0x00}, + {0x36D2, 0x71}, + {0x36D4, 0x3C}, + {0x36D6, 0x53}, + {0x36D7, 0x00}, + {0x36D8, 0x71}, + {0x36DA, 0x8C}, + {0x36DB, 0x00}, + {0x3701, 0x03}, + {0x3724, 0x02}, + {0x3726, 0x02}, + {0x3732, 0x02}, + {0x3734, 0x03}, + {0x3736, 0x03}, + {0x3742, 0x03}, + {0x3862, 0xE0}, + {0x38CC, 0x30}, + {0x38CD, 0x2F}, + {0x395C, 0x0C}, + {0x3A42, 0xD1}, + {0x3A4C, 0x77}, + {0x3AE0, 0x02}, + {0x3AEC, 0x0C}, + {0x3B00, 0x2E}, + {0x3B06, 0x29}, + {0x3B98, 0x25}, + {0x3B99, 0x21}, + {0x3B9B, 0x13}, + {0x3B9C, 0x13}, + {0x3B9D, 0x13}, + {0x3B9E, 0x13}, + {0x3BA1, 0x00}, + {0x3BA2, 0x06}, + {0x3BA3, 0x0B}, + {0x3BA4, 0x10}, + {0x3BA5, 0x14}, + {0x3BA6, 0x18}, + {0x3BA7, 0x1A}, + {0x3BA8, 0x1A}, + {0x3BA9, 0x1A}, + {0x3BAC, 0xED}, + {0x3BAD, 0x01}, + {0x3BAE, 0xF6}, + {0x3BAF, 0x02}, + {0x3BB0, 0xA2}, + {0x3BB1, 0x03}, + {0x3BB2, 0xE0}, + {0x3BB3, 0x03}, + {0x3BB4, 0xE0}, + {0x3BB5, 0x03}, + {0x3BB6, 0xE0}, + {0x3BB7, 0x03}, + {0x3BB8, 0xE0}, + {0x3BBA, 0xE0}, + {0x3BBC, 0xDA}, + {0x3BBE, 0x88}, + {0x3BC0, 0x44}, + {0x3BC2, 0x7B}, + {0x3BC4, 0xA2}, + {0x3BC8, 0xBD}, + {0x3BCA, 0xBD}, + {0x4004, 0x48}, + {0x4005, 0x09}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_linear_12bit_3864x2192_891M_regs[] = { + {0x3020, 0x00}, + {0x3021, 0x00}, + {0x3022, 0x00}, + {0x3024, 0xCA}, + {0x3025, 0x08}, + {0x3028, 0x4C}, + {0x3029, 0x04}, + {0x302C, 0x00}, + {0x302D, 0x00}, + {0x3033, 0x05}, + {0x3050, 0x08}, + {0x3051, 0x00}, + {0x3054, 0x19}, + {0x3058, 0x3E}, + {0x3060, 0x25}, + {0x3064, 0x4A}, + {0x30CF, 0x00}, + {0x3260, 0x01}, + {0x400C, 0x00}, + {0x4018, 0x7F}, + {0x401A, 0x37}, + {0x401C, 0x37}, + {0x401E, 0xF7}, + {0x401F, 0x00}, + {0x4020, 0x3F}, + {0x4022, 0x6F}, + {0x4024, 0x3F}, + {0x4026, 0x5F}, + {0x4028, 0x2F}, + {0x4074, 0x01}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_hdr2_12bit_3864x2192_1782M_regs[] = { + {0x3020, 0x00}, + {0x3021, 0x00}, + {0x3022, 0x00}, + {0x3024, 0xCA}, + {0x3025, 0x08}, + {0x3028, 0x26}, + {0x3029, 0x02}, + {0x302C, 0x01}, + {0x302D, 0x01}, + {0x3033, 0x04}, + {0x3050, 0x90}, + {0x3051, 0x0D}, + {0x3054, 0x09}, + {0x3058, 0x3E}, + {0x3060, 0x4D}, + {0x3064, 0x4A}, + {0x30CF, 0x01}, + {0x3260, 0x00}, + {0x400C, 0x01}, + {0x4018, 0xB7}, + {0x401A, 0x67}, + {0x401C, 0x6F}, + {0x401E, 0xDF}, + {0x401F, 0x01}, + {0x4020, 0x6F}, + {0x4022, 0xCF}, + {0x4024, 0x6F}, + {0x4026, 0xB7}, + {0x4028, 0x5F}, + {0x4074, 0x00}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_hdr3_12bit_3864x2192_1782M_regs[] = { + {0x3020, 0x00}, + {0x3021, 0x00}, + {0x3022, 0x00}, + {0x3024, 0x96}, + {0x3025, 0x06}, + {0x3028, 0x26}, + {0x3029, 0x02}, + {0x302C, 0x01}, + {0x302D, 0x02}, + {0x3033, 0x04}, + {0x3050, 0x14}, + {0x3051, 0x01}, + {0x3054, 0x0D}, + {0x3058, 0x26}, + {0x3060, 0x19}, + {0x3064, 0x32}, + {0x30CF, 0x03}, + {0x3260, 0x00}, + {0x400C, 0x01}, + {0x4018, 0xB7}, + {0x401A, 0x67}, + {0x401C, 0x6F}, + {0x401E, 0xDF}, + {0x401F, 0x01}, + {0x4020, 0x6F}, + {0x4022, 0xCF}, + {0x4024, 0x6F}, + {0x4026, 0xB7}, + {0x4028, 0x5F}, + {0x4074, 0x00}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_global_10bit_3864x2192_regs[] = { + {0x3002, 0x00}, + {0x3008, 0x7F}, + {0x300A, 0x5B}, + {0x3031, 0x00}, + {0x3032, 0x00}, + {0x30C1, 0x00}, + {0x30D9, 0x06}, + {0x3116, 0x24}, + {0x311E, 0x24}, + {0x32D4, 0x21}, + {0x32EC, 0xA1}, + {0x3452, 0x7F}, + {0x3453, 0x03}, + {0x358A, 0x04}, + {0x35A1, 0x02}, + {0x36BC, 0x0C}, + {0x36CC, 0x53}, + {0x36CD, 0x00}, + {0x36CE, 0x3C}, + {0x36D0, 0x8C}, + {0x36D1, 0x00}, + {0x36D2, 0x71}, + {0x36D4, 0x3C}, + {0x36D6, 0x53}, + {0x36D7, 0x00}, + {0x36D8, 0x71}, + {0x36DA, 0x8C}, + {0x36DB, 0x00}, + {0x3701, 0x00}, + {0x3724, 0x02}, + {0x3726, 0x02}, + {0x3732, 0x02}, + {0x3734, 0x03}, + {0x3736, 0x03}, + {0x3742, 0x03}, + {0x3862, 0xE0}, + {0x38CC, 0x30}, + {0x38CD, 0x2F}, + {0x395C, 0x0C}, + {0x3A42, 0xD1}, + {0x3A4C, 0x77}, + {0x3AE0, 0x02}, + {0x3AEC, 0x0C}, + {0x3B00, 0x2E}, + {0x3B06, 0x29}, + {0x3B98, 0x25}, + {0x3B99, 0x21}, + {0x3B9B, 0x13}, + {0x3B9C, 0x13}, + {0x3B9D, 0x13}, + {0x3B9E, 0x13}, + {0x3BA1, 0x00}, + {0x3BA2, 0x06}, + {0x3BA3, 0x0B}, + {0x3BA4, 0x10}, + {0x3BA5, 0x14}, + {0x3BA6, 0x18}, + {0x3BA7, 0x1A}, + {0x3BA8, 0x1A}, + {0x3BA9, 0x1A}, + {0x3BAC, 0xED}, + {0x3BAD, 0x01}, + {0x3BAE, 0xF6}, + {0x3BAF, 0x02}, + {0x3BB0, 0xA2}, + {0x3BB1, 0x03}, + {0x3BB2, 0xE0}, + {0x3BB3, 0x03}, + {0x3BB4, 0xE0}, + {0x3BB5, 0x03}, + {0x3BB6, 0xE0}, + {0x3BB7, 0x03}, + {0x3BB8, 0xE0}, + {0x3BBA, 0xE0}, + {0x3BBC, 0xDA}, + {0x3BBE, 0x88}, + {0x3BC0, 0x44}, + {0x3BC2, 0x7B}, + {0x3BC4, 0xA2}, + {0x3BC8, 0xBD}, + {0x3BCA, 0xBD}, + {0x4004, 0x48}, + {0x4005, 0x09}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_hdr3_10bit_3864x2192_1485M_regs[] = { + {0x3020, 0x00}, + {0x3021, 0x00}, + {0x3022, 0x00}, + {0x3024, 0xBD}, + {0x3025, 0x06}, + {0x3028, 0x1A}, + {0x3029, 0x02}, + {0x302C, 0x01}, + {0x302D, 0x02}, + {0x3033, 0x08}, + {0x3050, 0x90}, + {0x3051, 0x15}, + {0x3054, 0x0D}, + {0x3058, 0xA4}, + {0x3060, 0x97}, + {0x3064, 0xB6}, + {0x30CF, 0x03}, + {0x3118, 0xA0}, + {0x3260, 0x00}, + {0x400C, 0x01}, + {0x4018, 0xA7}, + {0x401A, 0x57}, + {0x401C, 0x5F}, + {0x401E, 0x97}, + {0x401F, 0x01}, + {0x4020, 0x5F}, + {0x4022, 0xAF}, + {0x4024, 0x5F}, + {0x4026, 0x9F}, + {0x4028, 0x4F}, + {0x4074, 0x00}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_hdr3_10bit_3864x2192_1782M_regs[] = { + {0x3020, 0x00}, + {0x3021, 0x00}, + {0x3022, 0x00}, + {0x3024, 0xEA}, + {0x3025, 0x07}, + {0x3028, 0xCA}, + {0x3029, 0x01}, + {0x302C, 0x01}, + {0x302D, 0x02}, + {0x3033, 0x04}, + {0x3050, 0x3E}, + {0x3051, 0x01}, + {0x3054, 0x0D}, + {0x3058, 0x9E}, + {0x3060, 0x91}, + {0x3064, 0xC2}, + {0x30CF, 0x03}, + {0x3118, 0xC0}, + {0x3260, 0x00}, + {0x400C, 0x01}, + {0x4018, 0xB7}, + {0x401A, 0x67}, + {0x401C, 0x6F}, + {0x401E, 0xDF}, + {0x401F, 0x01}, + {0x4020, 0x6F}, + {0x4022, 0xCF}, + {0x4024, 0x6F}, + {0x4026, 0xB7}, + {0x4028, 0x5F}, + {0x4074, 0x00}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_hdr2_10bit_3864x2192_1485M_regs[] = { + {0x3020, 0x00}, + {0x3021, 0x00}, + {0x3022, 0x00}, + {0x3024, 0xFC}, + {0x3025, 0x08}, + {0x3028, 0x1A}, + {0x3029, 0x02}, + {0x302C, 0x01}, + {0x302D, 0x01}, + {0x3033, 0x08}, + {0x3050, 0xA8}, + {0x3051, 0x0D}, + {0x3054, 0x09}, + {0x3058, 0x3E}, + {0x3060, 0x4D}, + {0x3064, 0x4a}, + {0x30CF, 0x01}, + {0x3118, 0xA0}, + {0x3260, 0x00}, + {0x400C, 0x01}, + {0x4018, 0xA7}, + {0x401A, 0x57}, + {0x401C, 0x5F}, + {0x401E, 0x97}, + {0x401F, 0x01}, + {0x4020, 0x5F}, + {0x4022, 0xAF}, + {0x4024, 0x5F}, + {0x4026, 0x9F}, + {0x4028, 0x4F}, + {0x4074, 0x00}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_linear_10bit_3864x2192_891M_regs[] = { + {0x3020, 0x00}, + {0x3021, 0x00}, + {0x3022, 0x00}, + {0x3024, 0xCA}, + {0x3025, 0x08}, + {0x3028, 0x4C}, + {0x3029, 0x04}, + {0x302C, 0x00}, + {0x302D, 0x00}, + {0x3033, 0x05}, + {0x3050, 0x08}, + {0x3051, 0x00}, + {0x3054, 0x19}, + {0x3058, 0x3E}, + {0x3060, 0x25}, + {0x3064, 0x4a}, + {0x30CF, 0x00}, + {0x3118, 0xC0}, + {0x3260, 0x01}, + {0x400C, 0x00}, + {0x4018, 0x7F}, + {0x401A, 0x37}, + {0x401C, 0x37}, + {0x401E, 0xF7}, + {0x401F, 0x00}, + {0x4020, 0x3F}, + {0x4022, 0x6F}, + {0x4024, 0x3F}, + {0x4026, 0x5F}, + {0x4028, 0x2F}, + {0x4074, 0x01}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_linear_12bit_1932x1096_594M_regs[] = { + {0x3020, 0x01}, + {0x3021, 0x01}, + {0x3022, 0x01}, + {0x3024, 0x5D}, + {0x3025, 0x0C}, + {0x3028, 0x0E}, + {0x3029, 0x03}, + {0x302C, 0x00}, + {0x302D, 0x00}, + {0x3031, 0x00}, + {0x3033, 0x07}, + {0x3050, 0x08}, + {0x3051, 0x00}, + {0x3054, 0x19}, + {0x3058, 0x3E}, + {0x3060, 0x25}, + {0x3064, 0x4A}, + {0x30CF, 0x00}, + {0x30D9, 0x02}, + {0x30DA, 0x01}, + {0x3118, 0x80}, + {0x3260, 0x01}, + {0x3701, 0x00}, + {0x400C, 0x00}, + {0x4018, 0x67}, + {0x401A, 0x27}, + {0x401C, 0x27}, + {0x401E, 0xB7}, + {0x401F, 0x00}, + {0x4020, 0x2F}, + {0x4022, 0x4F}, + {0x4024, 0x2F}, + {0x4026, 0x47}, + {0x4028, 0x27}, + {0x4074, 0x01}, + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_hdr2_12bit_1932x1096_891M_regs[] = { + {0x3020, 0x01}, + {0x3021, 0x01}, + {0x3022, 0x01}, + {0x3024, 0xFC}, + {0x3025, 0x08}, + {0x3028, 0x1A}, + {0x3029, 0x02}, + {0x302C, 0x01}, + {0x302D, 0x01}, + {0x3031, 0x00}, + {0x3033, 0x05}, + {0x3050, 0xB8}, + {0x3051, 0x00}, + {0x3054, 0x09}, + {0x3058, 0x3E}, + {0x3060, 0x25}, + {0x3064, 0x4A}, + {0x30CF, 0x01}, + {0x30D9, 0x02}, + {0x30DA, 0x01}, + {0x3118, 0xC0}, + {0x3260, 0x00}, + {0x3701, 0x00}, + {0x400C, 0x00}, + {0x4018, 0xA7}, + {0x401A, 0x57}, + {0x401C, 0x5F}, + {0x401E, 0x97}, + {0x401F, 0x01}, + {0x4020, 0x5F}, + {0x4022, 0xAF}, + {0x4024, 0x5F}, + {0x4026, 0x9F}, + {0x4028, 0x4F}, + {0x4074, 0x01}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 27Mhz + * 15fps + * CSI-2_2lane + * AD:12bit Output:12bit + * 891Mbps + * Master Mode + * Time 9.988ms Gain:6dB + * All-pixel + */ +static __maybe_unused const struct regval imx415_linear_12bit_3864x2192_891M_regs_2lane[] = { + {0x3008, 0x5D}, + {0x300A, 0x42}, + {0x3028, 0x98}, + {0x3029, 0x08}, + {0x3033, 0x05}, + {0x3050, 0x79}, + {0x3051, 0x07}, + {0x3090, 0x14}, + {0x30C1, 0x00}, + {0x3116, 0x23}, + {0x3118, 0xC6}, + {0x311A, 0xE7}, + {0x311E, 0x23}, + {0x32D4, 0x21}, + {0x32EC, 0xA1}, + {0x344C, 0x2B}, + {0x344D, 0x01}, + {0x344E, 0xED}, + {0x344F, 0x01}, + {0x3450, 0xF6}, + {0x3451, 0x02}, + {0x3452, 0x7F}, + {0x3453, 0x03}, + {0x358A, 0x04}, + {0x35A1, 0x02}, + {0x35EC, 0x27}, + {0x35EE, 0x8D}, + {0x35F0, 0x8D}, + {0x35F2, 0x29}, + {0x36BC, 0x0C}, + {0x36CC, 0x53}, + {0x36CD, 0x00}, + {0x36CE, 0x3C}, + {0x36D0, 0x8C}, + {0x36D1, 0x00}, + {0x36D2, 0x71}, + {0x36D4, 0x3C}, + {0x36D6, 0x53}, + {0x36D7, 0x00}, + {0x36D8, 0x71}, + {0x36DA, 0x8C}, + {0x36DB, 0x00}, + {0x3720, 0x00}, + {0x3724, 0x02}, + {0x3726, 0x02}, + {0x3732, 0x02}, + {0x3734, 0x03}, + {0x3736, 0x03}, + {0x3742, 0x03}, + {0x3862, 0xE0}, + {0x38CC, 0x30}, + {0x38CD, 0x2F}, + {0x395C, 0x0C}, + {0x39A4, 0x07}, + {0x39A8, 0x32}, + {0x39AA, 0x32}, + {0x39AC, 0x32}, + {0x39AE, 0x32}, + {0x39B0, 0x32}, + {0x39B2, 0x2F}, + {0x39B4, 0x2D}, + {0x39B6, 0x28}, + {0x39B8, 0x30}, + {0x39BA, 0x30}, + {0x39BC, 0x30}, + {0x39BE, 0x30}, + {0x39C0, 0x30}, + {0x39C2, 0x2E}, + {0x39C4, 0x2B}, + {0x39C6, 0x25}, + {0x3A42, 0xD1}, + {0x3A4C, 0x77}, + {0x3AE0, 0x02}, + {0x3AEC, 0x0C}, + {0x3B00, 0x2E}, + {0x3B06, 0x29}, + {0x3B98, 0x25}, + {0x3B99, 0x21}, + {0x3B9B, 0x13}, + {0x3B9C, 0x13}, + {0x3B9D, 0x13}, + {0x3B9E, 0x13}, + {0x3BA1, 0x00}, + {0x3BA2, 0x06}, + {0x3BA3, 0x0B}, + {0x3BA4, 0x10}, + {0x3BA5, 0x14}, + {0x3BA6, 0x18}, + {0x3BA7, 0x1A}, + {0x3BA8, 0x1A}, + {0x3BA9, 0x1A}, + {0x3BAC, 0xED}, + {0x3BAD, 0x01}, + {0x3BAE, 0xF6}, + {0x3BAF, 0x02}, + {0x3BB0, 0xA2}, + {0x3BB1, 0x03}, + {0x3BB2, 0xE0}, + {0x3BB3, 0x03}, + {0x3BB4, 0xE0}, + {0x3BB5, 0x03}, + {0x3BB6, 0xE0}, + {0x3BB7, 0x03}, + {0x3BB8, 0xE0}, + {0x3BBA, 0xE0}, + {0x3BBC, 0xDA}, + {0x3BBE, 0x88}, + {0x3BC0, 0x44}, + {0x3BC2, 0x7B}, + {0x3BC4, 0xA2}, + {0x3BC8, 0xBD}, + {0x3BCA, 0xBD}, + {0x4001, 0x01}, + {0x4004, 0xC0}, + {0x4005, 0x06}, + {0x400C, 0x00}, + {0x4018, 0x7F}, + {0x401A, 0x37}, + {0x401C, 0x37}, + {0x401E, 0xF7}, + {0x401F, 0x00}, + {0x4020, 0x3F}, + {0x4022, 0x6F}, + {0x4024, 0x3F}, + {0x4026, 0x5F}, + {0x4028, 0x2F}, + {0x4074, 0x01}, + {0x3002, 0x00}, + //{0x3000, 0x00}, + {REG_DELAY, 0x1E},//wait_ms(30) + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_linear_10bit_3864x2192_891M_regs_2lane[] = { + {0x3008, 0x7F}, + {0x3031, 0x00}, + {0x3032, 0x00}, + {0x300A, 0x5B}, + {0x3028, 0x98}, + {0x3029, 0x08}, + {0x3033, 0x05}, + {0x3050, 0x79}, + {0x3051, 0x07}, + {0x3090, 0x14}, + {0x30C1, 0x00}, + {0x3116, 0x24}, + {0x3118, 0xC0}, + {0x311A, 0xE0}, + {0x311E, 0x24}, + {0x32D4, 0x21}, + {0x32EC, 0xA1}, + {0x344C, 0x2B}, + {0x344D, 0x01}, + {0x344E, 0xED}, + {0x344F, 0x01}, + {0x3450, 0xF6}, + {0x3451, 0x02}, + {0x3452, 0x7F}, + {0x3453, 0x03}, + {0x358A, 0x04}, + {0x35A1, 0x02}, + {0x35EC, 0x27}, + {0x35EE, 0x8D}, + {0x35F0, 0x8D}, + {0x35F2, 0x29}, + {0x36BC, 0x0C}, + {0x36CC, 0x53}, + {0x36CD, 0x00}, + {0x36CE, 0x3C}, + {0x36D0, 0x8C}, + {0x36D1, 0x00}, + {0x36D2, 0x71}, + {0x36D4, 0x3C}, + {0x36D6, 0x53}, + {0x36D7, 0x00}, + {0x36D8, 0x71}, + {0x36DA, 0x8C}, + {0x36DB, 0x00}, + {0x3720, 0x00}, + {0x3724, 0x02}, + {0x3726, 0x02}, + {0x3732, 0x02}, + {0x3734, 0x03}, + {0x3736, 0x03}, + {0x3742, 0x03}, + {0x3862, 0xE0}, + {0x38CC, 0x30}, + {0x38CD, 0x2F}, + {0x395C, 0x0C}, + {0x39A4, 0x07}, + {0x39A8, 0x32}, + {0x39AA, 0x32}, + {0x39AC, 0x32}, + {0x39AE, 0x32}, + {0x39B0, 0x32}, + {0x39B2, 0x2F}, + {0x39B4, 0x2D}, + {0x39B6, 0x28}, + {0x39B8, 0x30}, + {0x39BA, 0x30}, + {0x39BC, 0x30}, + {0x39BE, 0x30}, + {0x39C0, 0x30}, + {0x39C2, 0x2E}, + {0x39C4, 0x2B}, + {0x39C6, 0x25}, + {0x3A42, 0xD1}, + {0x3A4C, 0x77}, + {0x3AE0, 0x02}, + {0x3AEC, 0x0C}, + {0x3B00, 0x2E}, + {0x3B06, 0x29}, + {0x3B98, 0x25}, + {0x3B99, 0x21}, + {0x3B9B, 0x13}, + {0x3B9C, 0x13}, + {0x3B9D, 0x13}, + {0x3B9E, 0x13}, + {0x3BA1, 0x00}, + {0x3BA2, 0x06}, + {0x3BA3, 0x0B}, + {0x3BA4, 0x10}, + {0x3BA5, 0x14}, + {0x3BA6, 0x18}, + {0x3BA7, 0x1A}, + {0x3BA8, 0x1A}, + {0x3BA9, 0x1A}, + {0x3BAC, 0xED}, + {0x3BAD, 0x01}, + {0x3BAE, 0xF6}, + {0x3BAF, 0x02}, + {0x3BB0, 0xA2}, + {0x3BB1, 0x03}, + {0x3BB2, 0xE0}, + {0x3BB3, 0x03}, + {0x3BB4, 0xE0}, + {0x3BB5, 0x03}, + {0x3BB6, 0xE0}, + {0x3BB7, 0x03}, + {0x3BB8, 0xE0}, + {0x3BBA, 0xE0}, + {0x3BBC, 0xDA}, + {0x3BBE, 0x88}, + {0x3BC0, 0x44}, + {0x3BC2, 0x7B}, + {0x3BC4, 0xA2}, + {0x3BC8, 0xBD}, + {0x3BCA, 0xBD}, + {0x4001, 0x01}, + {0x4004, 0x48}, + {0x4005, 0x09}, + {0x400C, 0x00}, + {0x4018, 0x7F}, + {0x401A, 0x37}, + {0x401C, 0x37}, + {0x401E, 0xF7}, + {0x401F, 0x00}, + {0x4020, 0x3F}, + {0x4022, 0x6F}, + {0x4024, 0x3F}, + {0x4026, 0x5F}, + {0x4028, 0x2F}, + {0x4074, 0x01}, + {0x3002, 0x00}, + //{0x3000, 0x00}, + {REG_DELAY, 0x1E},//wait_ms(30) + {REG_NULL, 0x00}, +}; + + +static __maybe_unused const struct regval imx415_linear_10bit_1920x1080_891M_regs_2lane[] = { + {0x3008, 0x7f}, + {0x300A, 0x5b}, + {0x3034, 0x05}, + // {0x301C, 0x04}, + {0x3020, 0x01}, + {0x3021, 0x01}, + {0x3022, 0x01}, + {0x3024, 0xca}, + {0x3025, 0x08}, + {0x3028, 0x26}, + {0x3029, 0x02}, + {0x3031, 0x00}, + {0x3032, 0x00}, + {0x3033, 0x05}, + {0x3050, 0xC4}, + {0x3090, 0x14}, + {0x30C1, 0x00}, + {0x30D9, 0x02}, + {0x30DA, 0x01}, + {0x3116, 0x24}, + {0x3118, 0xC0}, + {0x311A, 0xE0}, + {0x311E, 0x24}, + {0x32D4, 0x21}, + {0x32EC, 0xA1}, + {0x344C, 0x2B}, + {0x344D, 0x01}, + {0x344E, 0xED}, + {0x344F, 0x01}, + {0x3450, 0xF6}, + {0x3451, 0x02}, + {0x3452, 0x7F}, + {0x3453, 0x03}, + {0x358A, 0x04}, + {0x35A1, 0x02}, + {0x35EC, 0x27}, + {0x35EE, 0x8D}, + {0x35F0, 0x8D}, + {0x35F2, 0x29}, + {0x36BC, 0x0C}, + {0x36CC, 0x53}, + {0x36CD, 0x00}, + {0x36CE, 0x3C}, + {0x36D0, 0x8C}, + {0x36D1, 0x00}, + {0x36D2, 0x71}, + {0x36D4, 0x3C}, + {0x36D6, 0x53}, + {0x36D7, 0x00}, + {0x36D8, 0x71}, + {0x36DA, 0x8C}, + {0x36DB, 0x00}, + {0x3701, 0x00}, + {0x3720, 0x00}, + {0x3724, 0x02}, + {0x3726, 0x02}, + {0x3732, 0x02}, + {0x3734, 0x03}, + {0x3736, 0x03}, + {0x3742, 0x03}, + {0x3862, 0xE0}, + {0x38CC, 0x30}, + {0x38CD, 0x2F}, + {0x395C, 0x0C}, + {0x39A4, 0x07}, + {0x39A8, 0x32}, + {0x39AA, 0x32}, + {0x39AC, 0x32}, + {0x39AE, 0x32}, + {0x39B0, 0x32}, + {0x39B2, 0x2F}, + {0x39B4, 0x2D}, + {0x39B6, 0x28}, + {0x39B8, 0x30}, + {0x39BA, 0x30}, + {0x39BC, 0x30}, + {0x39BE, 0x30}, + {0x39C0, 0x30}, + {0x39C2, 0x2E}, + {0x39C4, 0x2B}, + {0x39C6, 0x25}, + {0x3A42, 0xD1}, + {0x3A4C, 0x77}, + {0x3AE0, 0x02}, + {0x3AEC, 0x0C}, + {0x3B00, 0x2E}, + {0x3B06, 0x29}, + {0x3B98, 0x25}, + {0x3B99, 0x21}, + {0x3B9B, 0x13}, + {0x3B9C, 0x13}, + {0x3B9D, 0x13}, + {0x3B9E, 0x13}, + {0x3BA1, 0x00}, + {0x3BA2, 0x06}, + {0x3BA3, 0x0B}, + {0x3BA4, 0x10}, + {0x3BA5, 0x14}, + {0x3BA6, 0x18}, + {0x3BA7, 0x1A}, + {0x3BA8, 0x1A}, + {0x3BA9, 0x1A}, + {0x3BAC, 0xED}, + {0x3BAD, 0x01}, + {0x3BAE, 0xF6}, + {0x3BAF, 0x02}, + {0x3BB0, 0xA2}, + {0x3BB1, 0x03}, + {0x3BB2, 0xE0}, + {0x3BB3, 0x03}, + {0x3BB4, 0xE0}, + {0x3BB5, 0x03}, + {0x3BB6, 0xE0}, + {0x3BB7, 0x03}, + {0x3BB8, 0xE0}, + {0x3BBA, 0xE0}, + {0x3BBC, 0xDA}, + {0x3BBE, 0x88}, + {0x3BC0, 0x44}, + {0x3BC2, 0x7B}, + {0x3BC4, 0xA2}, + {0x3BC8, 0xBD}, + {0x3BCA, 0xBD}, + {0x4001, 0x01}, + {0x4004, 0x48}, + {0x4005, 0x09}, + {0x400c, 0x00}, + {0x4018, 0xE7}, + {0x401A, 0x37}, + {0x401C, 0x37}, + {0x401E, 0xf7}, + {0x401F, 0x00}, + {0x4020, 0x3f}, + {0x4022, 0x6F}, + {0x4023, 0x00}, + {0x4024, 0x3f}, + {0x4026, 0x5f}, + {0x4028, 0x2F}, + {0x4074, 0x1}, + {0x3002, 0x00}, + //{0x3000, 0x00}, + {REG_DELAY, 0x1E},//wait_ms(30) + {REG_NULL, 0x00}, +}; + + +static __maybe_unused const struct regval imx415_linear_10bit_1920x1080_891M_regs_4lane[] = { + {0x3008, 0x7f}, + {0x300A, 0x5b}, + {0x3034, 0x05}, + // {0x301C, 0x04}, + {0x3020, 0x01}, + {0x3021, 0x01}, + {0x3022, 0x01}, + {0x3024, 0xca}, + {0x3025, 0x08}, + {0x3028, 0x26}, + {0x3029, 0x02}, + {0x3031, 0x00}, + {0x3032, 0x00}, + {0x3033, 0x05}, + {0x3050, 0xC4}, + {0x3090, 0x14}, + {0x30C1, 0x00}, + {0x30D9, 0x02}, + {0x30DA, 0x01}, + {0x3116, 0x24}, + {0x3118, 0xC0}, + {0x311A, 0xE0}, + {0x311E, 0x24}, + {0x32D4, 0x21}, + {0x32EC, 0xA1}, + {0x344C, 0x2B}, + {0x344D, 0x01}, + {0x344E, 0xED}, + {0x344F, 0x01}, + {0x3450, 0xF6}, + {0x3451, 0x02}, + {0x3452, 0x7F}, + {0x3453, 0x03}, + {0x358A, 0x04}, + {0x35A1, 0x02}, + {0x35EC, 0x27}, + {0x35EE, 0x8D}, + {0x35F0, 0x8D}, + {0x35F2, 0x29}, + {0x36BC, 0x0C}, + {0x36CC, 0x53}, + {0x36CD, 0x00}, + {0x36CE, 0x3C}, + {0x36D0, 0x8C}, + {0x36D1, 0x00}, + {0x36D2, 0x71}, + {0x36D4, 0x3C}, + {0x36D6, 0x53}, + {0x36D7, 0x00}, + {0x36D8, 0x71}, + {0x36DA, 0x8C}, + {0x36DB, 0x00}, + {0x3701, 0x00}, + {0x3720, 0x00}, + {0x3724, 0x02}, + {0x3726, 0x02}, + {0x3732, 0x02}, + {0x3734, 0x03}, + {0x3736, 0x03}, + {0x3742, 0x03}, + {0x3862, 0xE0}, + {0x38CC, 0x30}, + {0x38CD, 0x2F}, + {0x395C, 0x0C}, + {0x39A4, 0x07}, + {0x39A8, 0x32}, + {0x39AA, 0x32}, + {0x39AC, 0x32}, + {0x39AE, 0x32}, + {0x39B0, 0x32}, + {0x39B2, 0x2F}, + {0x39B4, 0x2D}, + {0x39B6, 0x28}, + {0x39B8, 0x30}, + {0x39BA, 0x30}, + {0x39BC, 0x30}, + {0x39BE, 0x30}, + {0x39C0, 0x30}, + {0x39C2, 0x2E}, + {0x39C4, 0x2B}, + {0x39C6, 0x25}, + {0x3A42, 0xD1}, + {0x3A4C, 0x77}, + {0x3AE0, 0x02}, + {0x3AEC, 0x0C}, + {0x3B00, 0x2E}, + {0x3B06, 0x29}, + {0x3B98, 0x25}, + {0x3B99, 0x21}, + {0x3B9B, 0x13}, + {0x3B9C, 0x13}, + {0x3B9D, 0x13}, + {0x3B9E, 0x13}, + {0x3BA1, 0x00}, + {0x3BA2, 0x06}, + {0x3BA3, 0x0B}, + {0x3BA4, 0x10}, + {0x3BA5, 0x14}, + {0x3BA6, 0x18}, + {0x3BA7, 0x1A}, + {0x3BA8, 0x1A}, + {0x3BA9, 0x1A}, + {0x3BAC, 0xED}, + {0x3BAD, 0x01}, + {0x3BAE, 0xF6}, + {0x3BAF, 0x02}, + {0x3BB0, 0xA2}, + {0x3BB1, 0x03}, + {0x3BB2, 0xE0}, + {0x3BB3, 0x03}, + {0x3BB4, 0xE0}, + {0x3BB5, 0x03}, + {0x3BB6, 0xE0}, + {0x3BB7, 0x03}, + {0x3BB8, 0xE0}, + {0x3BBA, 0xE0}, + {0x3BBC, 0xDA}, + {0x3BBE, 0x88}, + {0x3BC0, 0x44}, + {0x3BC2, 0x7B}, + {0x3BC4, 0xA2}, + {0x3BC8, 0xBD}, + {0x3BCA, 0xBD}, + {0x4001, 0x03}, + {0x4004, 0x48}, + {0x4005, 0x09}, + {0x400c, 0x00}, + {0x4018, 0xE7}, + {0x401A, 0x37}, + {0x401C, 0x37}, + {0x401E, 0xf7}, + {0x401F, 0x00}, + {0x4020, 0x3f}, + {0x4022, 0x6F}, + {0x4023, 0x00}, + {0x4024, 0x3f}, + {0x4026, 0x5f}, + {0x4028, 0x2F}, + {0x4074, 0x1}, + {0x3002, 0x00}, + //{0x3000, 0x00}, + {REG_DELAY, 0x1E},//wait_ms(30) + {REG_NULL, 0x00}, +}; + +/* + * Xclk 27Mhz + * 90.059fps + * CSI-2_2lane + * AD:10bit Output:12bit + * 2376Mbps + * Master Mode + * Time 9.999ms Gain:6dB + * 2568x1440 2/2-line binning & Window cropping + */ +static __maybe_unused const struct regval imx415_linear_12bit_1284x720_2376M_regs_2lane[] = { + {0x3008, 0x5D}, + {0x300A, 0x42}, + {0x301C, 0x04}, + {0x3020, 0x01}, + {0x3021, 0x01}, + {0x3022, 0x01}, + {0x3024, 0xAB}, + {0x3025, 0x07}, + {0x3028, 0xA4}, + {0x3029, 0x01}, + {0x3031, 0x00}, + {0x3033, 0x00}, + {0x3040, 0x88}, + {0x3041, 0x02}, + {0x3042, 0x08}, + {0x3043, 0x0A}, + {0x3044, 0xF0}, + {0x3045, 0x02}, + {0x3046, 0x40}, + {0x3047, 0x0B}, + {0x3050, 0xC4}, + {0x3090, 0x14}, + {0x30C1, 0x00}, + {0x30D9, 0x02}, + {0x30DA, 0x01}, + {0x3116, 0x23}, + {0x3118, 0x08}, + {0x3119, 0x01}, + {0x311A, 0xE7}, + {0x311E, 0x23}, + {0x32D4, 0x21}, + {0x32EC, 0xA1}, + {0x344C, 0x2B}, + {0x344D, 0x01}, + {0x344E, 0xED}, + {0x344F, 0x01}, + {0x3450, 0xF6}, + {0x3451, 0x02}, + {0x3452, 0x7F}, + {0x3453, 0x03}, + {0x358A, 0x04}, + {0x35A1, 0x02}, + {0x35EC, 0x27}, + {0x35EE, 0x8D}, + {0x35F0, 0x8D}, + {0x35F2, 0x29}, + {0x36BC, 0x0C}, + {0x36CC, 0x53}, + {0x36CD, 0x00}, + {0x36CE, 0x3C}, + {0x36D0, 0x8C}, + {0x36D1, 0x00}, + {0x36D2, 0x71}, + {0x36D4, 0x3C}, + {0x36D6, 0x53}, + {0x36D7, 0x00}, + {0x36D8, 0x71}, + {0x36DA, 0x8C}, + {0x36DB, 0x00}, + {0x3701, 0x00}, + {0x3720, 0x00}, + {0x3724, 0x02}, + {0x3726, 0x02}, + {0x3732, 0x02}, + {0x3734, 0x03}, + {0x3736, 0x03}, + {0x3742, 0x03}, + {0x3862, 0xE0}, + {0x38CC, 0x30}, + {0x38CD, 0x2F}, + {0x395C, 0x0C}, + {0x39A4, 0x07}, + {0x39A8, 0x32}, + {0x39AA, 0x32}, + {0x39AC, 0x32}, + {0x39AE, 0x32}, + {0x39B0, 0x32}, + {0x39B2, 0x2F}, + {0x39B4, 0x2D}, + {0x39B6, 0x28}, + {0x39B8, 0x30}, + {0x39BA, 0x30}, + {0x39BC, 0x30}, + {0x39BE, 0x30}, + {0x39C0, 0x30}, + {0x39C2, 0x2E}, + {0x39C4, 0x2B}, + {0x39C6, 0x25}, + {0x3A42, 0xD1}, + {0x3A4C, 0x77}, + {0x3AE0, 0x02}, + {0x3AEC, 0x0C}, + {0x3B00, 0x2E}, + {0x3B06, 0x29}, + {0x3B98, 0x25}, + {0x3B99, 0x21}, + {0x3B9B, 0x13}, + {0x3B9C, 0x13}, + {0x3B9D, 0x13}, + {0x3B9E, 0x13}, + {0x3BA1, 0x00}, + {0x3BA2, 0x06}, + {0x3BA3, 0x0B}, + {0x3BA4, 0x10}, + {0x3BA5, 0x14}, + {0x3BA6, 0x18}, + {0x3BA7, 0x1A}, + {0x3BA8, 0x1A}, + {0x3BA9, 0x1A}, + {0x3BAC, 0xED}, + {0x3BAD, 0x01}, + {0x3BAE, 0xF6}, + {0x3BAF, 0x02}, + {0x3BB0, 0xA2}, + {0x3BB1, 0x03}, + {0x3BB2, 0xE0}, + {0x3BB3, 0x03}, + {0x3BB4, 0xE0}, + {0x3BB5, 0x03}, + {0x3BB6, 0xE0}, + {0x3BB7, 0x03}, + {0x3BB8, 0xE0}, + {0x3BBA, 0xE0}, + {0x3BBC, 0xDA}, + {0x3BBE, 0x88}, + {0x3BC0, 0x44}, + {0x3BC2, 0x7B}, + {0x3BC4, 0xA2}, + {0x3BC8, 0xBD}, + {0x3BCA, 0xBD}, + {0x4001, 0x01}, + {0x4004, 0xC0}, + {0x4005, 0x06}, + {0x4018, 0xE7}, + {0x401A, 0x8F}, + {0x401C, 0x8F}, + {0x401E, 0x7F}, + {0x401F, 0x02}, + {0x4020, 0x97}, + {0x4022, 0x0F}, + {0x4023, 0x01}, + {0x4024, 0x97}, + {0x4026, 0xF7}, + {0x4028, 0x7F}, + {0x3002, 0x00}, + //{0x3000, 0x00}, + {REG_DELAY, 0x1E},//wait_ms(30) + {REG_NULL, 0x00}, +}; + +static __maybe_unused const struct regval imx415_linear_10bit_1284x720_891M_regs_2lane[] = { + {0x3008, 0x5D}, + {0x300A, 0x42}, + {0x301C, 0x04}, + {0x3020, 0x01}, + {0x3021, 0x01}, + {0x3022, 0x01}, + {0x3024, 0xAB}, + {0x3025, 0x07}, + {0x3028, 0xA4}, + {0x3029, 0x01}, + {0x3031, 0x00}, + {0x3033, 0x00}, + {0x3040, 0x88}, + {0x3041, 0x02}, + {0x3042, 0x08}, + {0x3043, 0x0A}, + {0x3044, 0xF0}, + {0x3045, 0x02}, + {0x3046, 0x40}, + {0x3047, 0x0B}, + {0x3050, 0xC4}, + {0x3090, 0x14}, + {0x30C1, 0x00}, + {0x30D9, 0x02}, + {0x30DA, 0x01}, + {0x3116, 0x23}, + {0x3118, 0x08}, + {0x3119, 0x01}, + {0x311A, 0xE7}, + {0x311E, 0x23}, + {0x32D4, 0x21}, + {0x32EC, 0xA1}, + {0x344C, 0x2B}, + {0x344D, 0x01}, + {0x344E, 0xED}, + {0x344F, 0x01}, + {0x3450, 0xF6}, + {0x3451, 0x02}, + {0x3452, 0x7F}, + {0x3453, 0x03}, + {0x358A, 0x04}, + {0x35A1, 0x02}, + {0x35EC, 0x27}, + {0x35EE, 0x8D}, + {0x35F0, 0x8D}, + {0x35F2, 0x29}, + {0x36BC, 0x0C}, + {0x36CC, 0x53}, + {0x36CD, 0x00}, + {0x36CE, 0x3C}, + {0x36D0, 0x8C}, + {0x36D1, 0x00}, + {0x36D2, 0x71}, + {0x36D4, 0x3C}, + {0x36D6, 0x53}, + {0x36D7, 0x00}, + {0x36D8, 0x71}, + {0x36DA, 0x8C}, + {0x36DB, 0x00}, + {0x3701, 0x00}, + {0x3720, 0x00}, + {0x3724, 0x02}, + {0x3726, 0x02}, + {0x3732, 0x02}, + {0x3734, 0x03}, + {0x3736, 0x03}, + {0x3742, 0x03}, + {0x3862, 0xE0}, + {0x38CC, 0x30}, + {0x38CD, 0x2F}, + {0x395C, 0x0C}, + {0x39A4, 0x07}, + {0x39A8, 0x32}, + {0x39AA, 0x32}, + {0x39AC, 0x32}, + {0x39AE, 0x32}, + {0x39B0, 0x32}, + {0x39B2, 0x2F}, + {0x39B4, 0x2D}, + {0x39B6, 0x28}, + {0x39B8, 0x30}, + {0x39BA, 0x30}, + {0x39BC, 0x30}, + {0x39BE, 0x30}, + {0x39C0, 0x30}, + {0x39C2, 0x2E}, + {0x39C4, 0x2B}, + {0x39C6, 0x25}, + {0x3A42, 0xD1}, + {0x3A4C, 0x77}, + {0x3AE0, 0x02}, + {0x3AEC, 0x0C}, + {0x3B00, 0x2E}, + {0x3B06, 0x29}, + {0x3B98, 0x25}, + {0x3B99, 0x21}, + {0x3B9B, 0x13}, + {0x3B9C, 0x13}, + {0x3B9D, 0x13}, + {0x3B9E, 0x13}, + {0x3BA1, 0x00}, + {0x3BA2, 0x06}, + {0x3BA3, 0x0B}, + {0x3BA4, 0x10}, + {0x3BA5, 0x14}, + {0x3BA6, 0x18}, + {0x3BA7, 0x1A}, + {0x3BA8, 0x1A}, + {0x3BA9, 0x1A}, + {0x3BAC, 0xED}, + {0x3BAD, 0x01}, + {0x3BAE, 0xF6}, + {0x3BAF, 0x02}, + {0x3BB0, 0xA2}, + {0x3BB1, 0x03}, + {0x3BB2, 0xE0}, + {0x3BB3, 0x03}, + {0x3BB4, 0xE0}, + {0x3BB5, 0x03}, + {0x3BB6, 0xE0}, + {0x3BB7, 0x03}, + {0x3BB8, 0xE0}, + {0x3BBA, 0xE0}, + {0x3BBC, 0xDA}, + {0x3BBE, 0x88}, + {0x3BC0, 0x44}, + {0x3BC2, 0x7B}, + {0x3BC4, 0xA2}, + {0x3BC8, 0xBD}, + {0x3BCA, 0xBD}, + {0x4001, 0x01}, + {0x4004, 0xC0}, + {0x4005, 0x06}, + {0x4018, 0xE7}, + {0x401A, 0x8F}, + {0x401C, 0x8F}, + {0x401E, 0x7F}, + {0x401F, 0x02}, + {0x4020, 0x97}, + {0x4022, 0x0F}, + {0x4023, 0x01}, + {0x4024, 0x97}, + {0x4026, 0xF7}, + {0x4028, 0x7F}, + {0x3002, 0x00}, + //{0x3000, 0x00}, + {REG_DELAY, 0x1E},//wait_ms(30) + {REG_NULL, 0x00}, +}; + + +/* + * The width and height must be configured to be + * the same as the current output resolution of the sensor. + * The input width of the isp needs to be 16 aligned. + * The input height of the isp needs to be 8 aligned. + * If the width or height does not meet the alignment rules, + * you can configure the cropping parameters with the following function to + * crop out the appropriate resolution. + * struct v4l2_subdev_pad_ops { + * .get_selection + * } + */ +static const struct imx415_mode supported_modes[] = { + /* + * frame rate = 1 / (Vtt * 1H) = 1 / (VMAX * 1H) + * VMAX >= (PIX_VWIDTH / 2) + 46 = height + 46 + */ + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 3864, + .height = 2192, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x08ca - 0x08, + .hts_def = 0x044c * IMX415_4LANES * 2, + .vts_def = 0x08ca, + .global_reg_list = imx415_global_10bit_3864x2192_regs, + .reg_list = imx415_linear_10bit_3864x2192_891M_regs, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 1, + .bpp = 10, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + /* 1H period = (1100 clock) = (1100 * 1 / 74.25MHz) */ + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 1944, + .height = 1096, + .max_fps = { + .numerator = 10000, + .denominator = 600000, + }, + .exp_def = 0x08ca - 0x08, + .hts_def = 0x044c *2, + .vts_def = 0x08ca, + .global_reg_list = NULL, + .reg_list = imx415_linear_10bit_1920x1080_891M_regs_4lane, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 1, + .bpp = 10, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = IMX415_XVCLK_FREQ_37M, + }, +#if 0 + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 3864, + .height = 2192, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x08fc * 2 - 0x0da8, + .hts_def = 0x0226 * IMX415_4LANES * 2, + /* + * IMX415 HDR mode T-line is half of Linear mode, + * make vts double to workaround. + */ + .vts_def = 0x08fc * 2, + .global_reg_list = imx415_global_10bit_3864x2192_regs, + .reg_list = imx415_hdr2_10bit_3864x2192_1485M_regs, + .hdr_mode = HDR_X2, + .mipi_freq_idx = 2, + .bpp = 10, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_1, + .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 + .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_1, + .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr2 + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 3864, + .height = 2192, + .max_fps = { + .numerator = 10000, + .denominator = 200000, + }, + .exp_def = 0x13e, + .hts_def = 0x021A * IMX415_4LANES * 2, + /* + * IMX415 HDR mode T-line is half of Linear mode, + * make vts double to workaround. + */ + .vts_def = 0x06BD * 4, + .global_reg_list = imx415_global_10bit_3864x2192_regs, + .reg_list = imx415_hdr3_10bit_3864x2192_1485M_regs, + .hdr_mode = HDR_X3, + .mipi_freq_idx = 2, + .bpp = 10, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_2, + .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr0 + .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 + .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_2,//S->csi wr2 + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 3864, + .height = 2192, + .max_fps = { + .numerator = 10000, + .denominator = 200000, + }, + .exp_def = 0x13e, + .hts_def = 0x01ca * IMX415_4LANES * 2, + /* + * IMX415 HDR mode T-line is half of Linear mode, + * make vts double to workaround. + */ + .vts_def = 0x07ea * 4, + .global_reg_list = imx415_global_10bit_3864x2192_regs, + .reg_list = imx415_hdr3_10bit_3864x2192_1782M_regs, + .hdr_mode = HDR_X3, + .mipi_freq_idx = 3, + .bpp = 10, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_2, + .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr0 + .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 + .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_2,//S->csi wr2 + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + /* 1H period = (1100 clock) = (1100 * 1 / 74.25MHz) */ + .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, + .width = 3864, + .height = 2192, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x08ca - 0x08, + .hts_def = 0x044c * IMX415_4LANES * 2, + .vts_def = 0x08ca, + .global_reg_list = imx415_global_12bit_3864x2192_regs, + .reg_list = imx415_linear_12bit_3864x2192_891M_regs, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 1, + .bpp = 12, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, + .width = 3864, + .height = 2192, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x08CA * 2 - 0x0d90, + .hts_def = 0x0226 * IMX415_4LANES * 2, + /* + * IMX415 HDR mode T-line is half of Linear mode, + * make vts double(that is FSC) to workaround. + */ + .vts_def = 0x08CA * 2, + .global_reg_list = imx415_global_12bit_3864x2192_regs, + .reg_list = imx415_hdr2_12bit_3864x2192_1782M_regs, + .hdr_mode = HDR_X2, + .mipi_freq_idx = 3, + .bpp = 12, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_1, + .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 + .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_1, + .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr2 + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, + .width = 3864, + .height = 2192, + .max_fps = { + .numerator = 10000, + .denominator = 200000, + }, + .exp_def = 0x114, + .hts_def = 0x0226 * IMX415_4LANES * 2, + /* + * IMX415 HDR mode T-line is half of Linear mode, + * make vts double(that is FSC) to workaround. + */ + .vts_def = 0x0696 * 4, + .global_reg_list = imx415_global_12bit_3864x2192_regs, + .reg_list = imx415_hdr3_12bit_3864x2192_1782M_regs, + .hdr_mode = HDR_X3, + .mipi_freq_idx = 3, + .bpp = 12, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_2, + .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr0 + .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 + .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_2,//S->csi wr2 + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, + .width = 1944, + .height = 1097, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x05dc - 0x08, + .hts_def = 0x030e * 3, + .vts_def = 0x0c5d, + .global_reg_list = imx415_global_12bit_3864x2192_regs, + .reg_list = imx415_linear_12bit_1932x1096_594M_regs, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 0, + .bpp = 12, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG12_1X12, + .width = 1944, + .height = 1097, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x08FC / 4, + .hts_def = 0x021A * 4, + /* + * IMX415 HDR mode T-line is half of Linear mode, + * make vts double(that is FSC) to workaround. + */ + .vts_def = 0x08FC * 2, + .global_reg_list = imx415_global_12bit_3864x2192_regs, + .reg_list = imx415_hdr2_12bit_1932x1096_891M_regs, + .hdr_mode = HDR_X2, + .mipi_freq_idx = 1, + .bpp = 12, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_1, + .vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0 + .vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_1, + .vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr2 + .xvclk = IMX415_XVCLK_FREQ_37M, + }, +#endif +}; + +static const struct imx415_mode supported_modes_2lane[] = { + { + /* 1H period = (1100 clock) = (1100 * 1 / 74.25MHz) */ + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 3864, + .height = 2192, + .max_fps = { + .numerator = 10000, + .denominator = 150000, + }, + .exp_def = 0x08ca - 0x08, + .hts_def = 0x0898 * IMX415_2LANES * 2, + .vts_def = 0x08ca, + .global_reg_list = NULL, + .reg_list = imx415_linear_10bit_3864x2192_891M_regs_2lane, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 1, + .bpp = 10, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = IMX415_XVCLK_FREQ_37M, + }, + { + /* 1H period = (1100 clock) = (1100 * 1 / 74.25MHz) */ + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 1944, + .height = 1096, + .max_fps = { + .numerator = 10000, + .denominator = 600000, + }, + .exp_def = 0x08ca - 0x08, + .hts_def = 0x0800, + .vts_def = 0x08ca, + .global_reg_list = NULL, + .reg_list = imx415_linear_10bit_1920x1080_891M_regs_2lane, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 1, + .bpp = 10, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + .xvclk = IMX415_XVCLK_FREQ_37M, + }, +}; + +static const s64 link_freq_items[] = { + MIPI_FREQ_297M, + MIPI_FREQ_446M, + MIPI_FREQ_743M, + MIPI_FREQ_891M, + MIPI_FREQ_1188M, +}; + +/* Write registers up to 4 at a time */ +static int imx415_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int imx415_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + if (!regs) { + dev_err(&client->dev, "write reg array error\n"); + return ret; + } + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + if (regs[i].addr == REG_DELAY) { + usleep_range(regs[i].val * 1000, regs[i].val * 1000 + 500); + dev_info(&client->dev, "write reg array, sleep %dms\n", regs[i].val); + } else { + ret = imx415_write_reg(client, regs[i].addr, + IMX415_REG_VALUE_08BIT, regs[i].val); + } + } + return ret; +} + +/* Read registers up to 4 at a time */ +static int imx415_read_reg(struct i2c_client *client, u16 reg, unsigned int len, + u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int imx415_get_reso_dist(const struct imx415_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct imx415_mode * +imx415_find_best_fit(struct imx415 *imx415, struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < imx415->cfg_num; i++) { + dist = imx415_get_reso_dist(&imx415->supported_modes[i], framefmt); + if ((cur_best_fit_dist == -1 || dist < cur_best_fit_dist) && + imx415->supported_modes[i].bus_fmt == framefmt->code) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + dev_info(&imx415->client->dev, "%s: cur_best_fit(%d)", + __func__, cur_best_fit); + + return &imx415->supported_modes[cur_best_fit]; +} + +static int __imx415_power_on(struct imx415 *imx415); + +static void imx415_change_mode(struct imx415 *imx415, const struct imx415_mode *mode) +{ + if (imx415->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) { + imx415->is_thunderboot = false; + imx415->is_thunderboot_ng = true; + __imx415_power_on(imx415); + } + imx415->cur_mode = mode; + imx415->cur_vts = imx415->cur_mode->vts_def; + dev_info(&imx415->client->dev, "set fmt: cur_mode: %dx%d, hdr: %d, bpp: %d\n", + mode->width, mode->height, mode->hdr_mode, mode->bpp); +} + +static int imx415_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx415 *imx415 = to_imx415(sd); + const struct imx415_mode *mode; + s64 h_blank, vblank_def, vblank_min; + u64 pixel_rate = 0; + u8 lanes = imx415->lanes; + + mutex_lock(&imx415->mutex); + + mode = imx415_find_best_fit(imx415, fmt); + fmt->format.code = mode->bus_fmt; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&imx415->mutex); + return -ENOTTY; +#endif + } else { + imx415_change_mode(imx415, mode); + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(imx415->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + /* VMAX >= (PIX_VWIDTH / 2) + 46 = height + 46 */ + vblank_min = (mode->height + 46) - mode->height; + __v4l2_ctrl_modify_range(imx415->vblank, vblank_min, + IMX415_VTS_MAX - mode->height, + 1, vblank_def); + __v4l2_ctrl_s_ctrl(imx415->vblank, vblank_def); + __v4l2_ctrl_s_ctrl(imx415->link_freq, mode->mipi_freq_idx); + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / + mode->bpp * 2 * lanes; + __v4l2_ctrl_s_ctrl_int64(imx415->pixel_rate, + pixel_rate); + } + dev_info(&imx415->client->dev, "%s: mode->mipi_freq_idx(%d)", + __func__, mode->mipi_freq_idx); + + mutex_unlock(&imx415->mutex); + + return 0; +} + +static int imx415_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx415 *imx415 = to_imx415(sd); + const struct imx415_mode *mode = imx415->cur_mode; + + mutex_lock(&imx415->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&imx415->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->bus_fmt; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR) + fmt->reserved[0] = mode->vc[fmt->pad]; + else + fmt->reserved[0] = mode->vc[PAD0]; + } + mutex_unlock(&imx415->mutex); + + return 0; +} + +static int imx415_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx415 *imx415 = to_imx415(sd); + + if (code->index >= imx415->cfg_num) + return -EINVAL; + + code->code = imx415->supported_modes[code->index].bus_fmt; + + return 0; +} + +static int imx415_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct imx415 *imx415 = to_imx415(sd); + + if (fse->index >= imx415->cfg_num) + return -EINVAL; + + if (fse->code != imx415->supported_modes[fse->index].bus_fmt) + return -EINVAL; + + fse->min_width = imx415->supported_modes[fse->index].width; + fse->max_width = imx415->supported_modes[fse->index].width; + fse->max_height = imx415->supported_modes[fse->index].height; + fse->min_height = imx415->supported_modes[fse->index].height; + + return 0; +} + +static int imx415_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct imx415 *imx415 = to_imx415(sd); + const struct imx415_mode *mode = imx415->cur_mode; + + fi->interval = mode->max_fps; + + return 0; +} + +static int imx415_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct imx415 *imx415 = to_imx415(sd); + const struct imx415_mode *mode = imx415->cur_mode; + u32 val = 0; + u8 lanes = imx415->lanes; + + val = 1 << (lanes - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + if (mode->hdr_mode != NO_HDR) + val |= V4L2_MBUS_CSI2_CHANNEL_1; + if (mode->hdr_mode == HDR_X3) + val |= V4L2_MBUS_CSI2_CHANNEL_2; + config->type = V4L2_MBUS_CSI2_DPHY; + config->flags = val; + + return 0; +} + +static void imx415_get_module_inf(struct imx415 *imx415, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strlcpy(inf->base.sensor, IMX415_NAME, sizeof(inf->base.sensor)); + strlcpy(inf->base.module, imx415->module_name, + sizeof(inf->base.module)); + strlcpy(inf->base.lens, imx415->len_name, sizeof(inf->base.lens)); +} + +static int imx415_set_hdrae_3frame(struct imx415 *imx415, + struct preisp_hdrae_exp_s *ae) +{ + struct i2c_client *client = imx415->client; + u32 l_exp_time, m_exp_time, s_exp_time; + u32 l_a_gain, m_a_gain, s_a_gain; + int shr2, shr1, shr0, rhs2, rhs1 = 0; + int rhs1_change_limit, rhs2_change_limit = 0; + static int rhs1_old = IMX415_RHS1_DEFAULT; + static int rhs2_old = IMX415_RHS2_DEFAULT; + int ret = 0; + u32 fsc; + int rhs1_max = 0; + int shr2_min = 0; + + if (!imx415->has_init_exp && !imx415->streaming) { + imx415->init_hdrae_exp = *ae; + imx415->has_init_exp = true; + dev_dbg(&imx415->client->dev, "imx415 is not streaming, save hdr ae!\n"); + return ret; + } + l_exp_time = ae->long_exp_reg; + m_exp_time = ae->middle_exp_reg; + s_exp_time = ae->short_exp_reg; + l_a_gain = ae->long_gain_reg; + m_a_gain = ae->middle_gain_reg; + s_a_gain = ae->short_gain_reg; + dev_dbg(&client->dev, + "rev exp req: L_exp: 0x%x, 0x%x, M_exp: 0x%x, 0x%x S_exp: 0x%x, 0x%x\n", + l_exp_time, m_exp_time, s_exp_time, + l_a_gain, m_a_gain, s_a_gain); + + ret = imx415_write_reg(client, IMX415_GROUP_HOLD_REG, + IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_START); + /* gain effect n+1 */ + ret |= imx415_write_reg(client, IMX415_LF_GAIN_REG_H, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_H(l_a_gain)); + ret |= imx415_write_reg(client, IMX415_LF_GAIN_REG_L, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_L(l_a_gain)); + ret |= imx415_write_reg(client, IMX415_SF1_GAIN_REG_H, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_H(m_a_gain)); + ret |= imx415_write_reg(client, IMX415_SF1_GAIN_REG_L, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_L(m_a_gain)); + ret |= imx415_write_reg(client, IMX415_SF2_GAIN_REG_H, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_H(s_a_gain)); + ret |= imx415_write_reg(client, IMX415_SF2_GAIN_REG_L, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_L(s_a_gain)); + + /* Restrictions + * FSC = 4 * VMAX and FSC should be 6n; + * exp_l = FSC - SHR0 + Toffset; + * + * SHR0 = FSC - exp_l + Toffset; + * SHR0 <= (FSC -12); + * SHR0 >= RHS2 + 13; + * SHR0 should be 3n; + * + * exp_m = RHS1 - SHR1 + Toffset; + * + * RHS1 < BRL * 3; + * RHS1 <= SHR2 - 13; + * RHS1 >= SHR1 + 12; + * SHR1 >= 13; + * SHR1 <= RHS1 - 12; + * RHS1(n+1) >= RHS1(n) + BRL * 3 -FSC + 3; + * + * SHR1 should be 3n+1 and RHS1 should be 6n+1; + * + * exp_s = RHS2 - SHR2 + Toffset; + * + * RHS2 < BRL * 3 + RHS1; + * RHS2 <= SHR0 - 13; + * RHS2 >= SHR2 + 12; + * SHR2 >= RHS1 + 13; + * SHR2 <= RHS2 - 12; + * RHS1(n+1) >= RHS1(n) + BRL * 3 -FSC + 3; + * + * SHR2 should be 3n+2 and RHS2 should be 6n+2; + */ + + /* The HDR mode vts is double by default to workaround T-line */ + fsc = imx415->cur_vts; + fsc = fsc / 6 * 6; + shr0 = fsc - l_exp_time; + dev_dbg(&client->dev, + "line(%d) shr0 %d, l_exp_time %d, fsc %d\n", + __LINE__, shr0, l_exp_time, fsc); + + rhs1 = (SHR1_MIN_X3 + m_exp_time + 5) / 6 * 6 + 1; + if (imx415->cur_mode->height == 2192) + rhs1_max = RHS1_MAX_X3(BRL_ALL); + else + rhs1_max = RHS1_MAX_X3(BRL_BINNING); + if (rhs1 < 25) + rhs1 = 25; + else if (rhs1 > rhs1_max) + rhs1 = rhs1_max; + dev_dbg(&client->dev, + "line(%d) rhs1 %d, m_exp_time %d rhs1_old %d\n", + __LINE__, rhs1, m_exp_time, rhs1_old); + + //Dynamic adjustment rhs2 must meet the following conditions + if (imx415->cur_mode->height == 2192) + rhs1_change_limit = rhs1_old + 3 * BRL_ALL - fsc + 3; + else + rhs1_change_limit = rhs1_old + 3 * BRL_BINNING - fsc + 3; + rhs1_change_limit = (rhs1_change_limit < 25) ? 25 : rhs1_change_limit; + rhs1_change_limit = (rhs1_change_limit + 5) / 6 * 6 + 1; + if (rhs1_max < rhs1_change_limit) { + dev_err(&client->dev, + "The total exposure limit makes rhs1 max is %d,but old rhs1 limit makes rhs1 min is %d\n", + rhs1_max, rhs1_change_limit); + return -EINVAL; + } + if (rhs1 < rhs1_change_limit) + rhs1 = rhs1_change_limit; + + dev_dbg(&client->dev, + "line(%d) m_exp_time %d rhs1_old %d, rhs1_new %d\n", + __LINE__, m_exp_time, rhs1_old, rhs1); + + rhs1_old = rhs1; + + /* shr1 = rhs1 - s_exp_time */ + if (rhs1 - m_exp_time <= SHR1_MIN_X3) { + shr1 = SHR1_MIN_X3; + m_exp_time = rhs1 - shr1; + } else { + shr1 = rhs1 - m_exp_time; + } + + shr2_min = rhs1 + 13; + rhs2 = (shr2_min + s_exp_time + 5) / 6 * 6 + 2; + if (rhs2 > (shr0 - 13)) + rhs2 = shr0 - 13; + else if (rhs2 < 50) + rhs2 = 50; + dev_dbg(&client->dev, + "line(%d) rhs2 %d, s_exp_time %d, rhs2_old %d\n", + __LINE__, rhs2, s_exp_time, rhs2_old); + + //Dynamic adjustment rhs2 must meet the following conditions + if (imx415->cur_mode->height == 2192) + rhs2_change_limit = rhs2_old + 3 * BRL_ALL - fsc + 3; + else + rhs2_change_limit = rhs2_old + 3 * BRL_BINNING - fsc + 3; + rhs2_change_limit = (rhs2_change_limit < 50) ? 50 : rhs2_change_limit; + rhs2_change_limit = (rhs2_change_limit + 5) / 6 * 6 + 2; + if ((shr0 - 13) < rhs2_change_limit) { + dev_err(&client->dev, + "The total exposure limit makes rhs2 max is %d,but old rhs1 limit makes rhs2 min is %d\n", + shr0 - 13, rhs2_change_limit); + return -EINVAL; + } + if (rhs2 < rhs2_change_limit) + rhs2 = rhs2_change_limit; + + rhs2_old = rhs2; + + /* shr2 = rhs2 - s_exp_time */ + if (rhs2 - s_exp_time <= shr2_min) { + shr2 = shr2_min; + s_exp_time = rhs2 - shr2; + } else { + shr2 = rhs2 - s_exp_time; + } + dev_dbg(&client->dev, + "line(%d) rhs2_new %d, s_exp_time %d shr2 %d, rhs2_change_limit %d\n", + __LINE__, rhs2, s_exp_time, shr2, rhs2_change_limit); + + if (shr0 < rhs2 + 13) + shr0 = rhs2 + 13; + else if (shr0 > fsc - 12) + shr0 = fsc - 12; + + dev_dbg(&client->dev, + "long exposure: l_exp_time=%d, fsc=%d, shr0=%d, l_a_gain=%d\n", + l_exp_time, fsc, shr0, l_a_gain); + dev_dbg(&client->dev, + "middle exposure(SEF1): m_exp_time=%d, rhs1=%d, shr1=%d, m_a_gain=%d\n", + m_exp_time, rhs1, shr1, m_a_gain); + dev_dbg(&client->dev, + "short exposure(SEF2): s_exp_time=%d, rhs2=%d, shr2=%d, s_a_gain=%d\n", + s_exp_time, rhs2, shr2, s_a_gain); + /* time effect n+1 */ + /* write SEF2 exposure RHS2 regs*/ + ret |= imx415_write_reg(client, + IMX415_RHS2_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_L(rhs2)); + ret |= imx415_write_reg(client, + IMX415_RHS2_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_M(rhs2)); + ret |= imx415_write_reg(client, + IMX415_RHS2_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_H(rhs2)); + /* write SEF2 exposure SHR2 regs*/ + ret |= imx415_write_reg(client, + IMX415_SF2_EXPO_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_L(shr2)); + ret |= imx415_write_reg(client, + IMX415_SF2_EXPO_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_M(shr2)); + ret |= imx415_write_reg(client, + IMX415_SF2_EXPO_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_H(shr2)); + /* write SEF1 exposure RHS1 regs*/ + ret |= imx415_write_reg(client, + IMX415_RHS1_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_L(rhs1)); + ret |= imx415_write_reg(client, + IMX415_RHS1_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_M(rhs1)); + ret |= imx415_write_reg(client, + IMX415_RHS1_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_H(rhs1)); + /* write SEF1 exposure SHR1 regs*/ + ret |= imx415_write_reg(client, + IMX415_SF1_EXPO_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_L(shr1)); + ret |= imx415_write_reg(client, + IMX415_SF1_EXPO_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_M(shr1)); + ret |= imx415_write_reg(client, + IMX415_SF1_EXPO_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_H(shr1)); + /* write LF exposure SHR0 regs*/ + ret |= imx415_write_reg(client, + IMX415_LF_EXPO_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_L(shr0)); + ret |= imx415_write_reg(client, + IMX415_LF_EXPO_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_M(shr0)); + ret |= imx415_write_reg(client, + IMX415_LF_EXPO_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_H(shr0)); + + ret |= imx415_write_reg(client, IMX415_GROUP_HOLD_REG, + IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_END); + return ret; +} + +static int imx415_set_hdrae(struct imx415 *imx415, + struct preisp_hdrae_exp_s *ae) +{ + struct i2c_client *client = imx415->client; + u32 l_exp_time, m_exp_time, s_exp_time; + u32 l_a_gain, m_a_gain, s_a_gain; + int shr1, shr0, rhs1, rhs1_max, rhs1_min; + static int rhs1_old = IMX415_RHS1_DEFAULT; + int ret = 0; + u32 fsc; + + if (!imx415->has_init_exp && !imx415->streaming) { + imx415->init_hdrae_exp = *ae; + imx415->has_init_exp = true; + dev_dbg(&imx415->client->dev, "imx415 is not streaming, save hdr ae!\n"); + return ret; + } + l_exp_time = ae->long_exp_reg; + m_exp_time = ae->middle_exp_reg; + s_exp_time = ae->short_exp_reg; + l_a_gain = ae->long_gain_reg; + m_a_gain = ae->middle_gain_reg; + s_a_gain = ae->short_gain_reg; + dev_dbg(&client->dev, + "rev exp req: L_exp: 0x%x, 0x%x, M_exp: 0x%x, 0x%x S_exp: 0x%x, 0x%x\n", + l_exp_time, m_exp_time, s_exp_time, + l_a_gain, m_a_gain, s_a_gain); + + if (imx415->cur_mode->hdr_mode == HDR_X2) { + l_a_gain = m_a_gain; + l_exp_time = m_exp_time; + } + + ret = imx415_write_reg(client, IMX415_GROUP_HOLD_REG, + IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_START); + /* gain effect n+1 */ + ret |= imx415_write_reg(client, IMX415_LF_GAIN_REG_H, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_H(l_a_gain)); + ret |= imx415_write_reg(client, IMX415_LF_GAIN_REG_L, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_L(l_a_gain)); + ret |= imx415_write_reg(client, IMX415_SF1_GAIN_REG_H, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_H(s_a_gain)); + ret |= imx415_write_reg(client, IMX415_SF1_GAIN_REG_L, + IMX415_REG_VALUE_08BIT, IMX415_FETCH_GAIN_L(s_a_gain)); + + /* Restrictions + * FSC = 2 * VMAX and FSC should be 4n; + * exp_l = FSC - SHR0 + Toffset; + * exp_l should be even value; + * + * SHR0 = FSC - exp_l + Toffset; + * SHR0 <= (FSC -8); + * SHR0 >= RHS1 + 9; + * SHR0 should be 2n; + * + * exp_s = RHS1 - SHR1 + Toffset; + * exp_s should be even value; + * + * RHS1 < BRL * 2; + * RHS1 <= SHR0 - 9; + * RHS1 >= SHR1 + 8; + * SHR1 >= 9; + * RHS1(n+1) >= RHS1(n) + BRL * 2 -FSC + 2; + * + * SHR1 should be 2n+1 and RHS1 should be 4n+1; + */ + + /* The HDR mode vts is double by default to workaround T-line */ + fsc = imx415->cur_vts; + shr0 = fsc - l_exp_time; + + if (imx415->cur_mode->height == 2192) { + rhs1_max = min(RHS1_MAX_X2(BRL_ALL), ((shr0 - 9u) / 4 * 4 + 1)); + rhs1_min = max(SHR1_MIN_X2 + 8u, rhs1_old + 2 * BRL_ALL - fsc + 2); + } else { + rhs1_max = min(RHS1_MAX_X2(BRL_BINNING), ((shr0 - 9u) / 4 * 4 + 1)); + rhs1_min = max(SHR1_MIN_X2 + 8u, rhs1_old + 2 * BRL_BINNING - fsc + 2); + } + rhs1_min = (rhs1_min + 3) / 4 * 4 + 1; + rhs1 = (SHR1_MIN_X2 + s_exp_time + 3) / 4 * 4 + 1;/* shall be 4n + 1 */ + dev_dbg(&client->dev, + "line(%d) rhs1 %d, rhs1 min %d rhs1 max %d\n", + __LINE__, rhs1, rhs1_min, rhs1_max); + if (rhs1_max < rhs1_min) { + dev_err(&client->dev, + "The total exposure limit makes rhs1 max is %d,but old rhs1 limit makes rhs1 min is %d\n", + rhs1_max, rhs1_min); + return -EINVAL; + } + rhs1 = clamp(rhs1, rhs1_min, rhs1_max); + dev_dbg(&client->dev, + "line(%d) rhs1 %d, short time %d rhs1_old %d, rhs1_new %d\n", + __LINE__, rhs1, s_exp_time, rhs1_old, rhs1); + + rhs1_old = rhs1; + + /* shr1 = rhs1 - s_exp_time */ + if (rhs1 - s_exp_time <= SHR1_MIN_X2) { + shr1 = SHR1_MIN_X2; + s_exp_time = rhs1 - shr1; + } else { + shr1 = rhs1 - s_exp_time; + } + + if (shr0 < rhs1 + 9) + shr0 = rhs1 + 9; + else if (shr0 > fsc - 8) + shr0 = fsc - 8; + + dev_dbg(&client->dev, + "fsc=%d,RHS1_MAX=%d,SHR1_MIN=%d,rhs1_max=%d\n", + fsc, RHS1_MAX_X2(BRL_ALL), SHR1_MIN_X2, rhs1_max); + dev_dbg(&client->dev, + "l_exp_time=%d,s_exp_time=%d,shr0=%d,shr1=%d,rhs1=%d,l_a_gain=%d,s_a_gain=%d\n", + l_exp_time, s_exp_time, shr0, shr1, rhs1, l_a_gain, s_a_gain); + /* time effect n+2 */ + ret |= imx415_write_reg(client, + IMX415_RHS1_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_L(rhs1)); + ret |= imx415_write_reg(client, + IMX415_RHS1_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_M(rhs1)); + ret |= imx415_write_reg(client, + IMX415_RHS1_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_RHS1_H(rhs1)); + + ret |= imx415_write_reg(client, + IMX415_SF1_EXPO_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_L(shr1)); + ret |= imx415_write_reg(client, + IMX415_SF1_EXPO_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_M(shr1)); + ret |= imx415_write_reg(client, + IMX415_SF1_EXPO_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_H(shr1)); + ret |= imx415_write_reg(client, + IMX415_LF_EXPO_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_L(shr0)); + ret |= imx415_write_reg(client, + IMX415_LF_EXPO_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_M(shr0)); + ret |= imx415_write_reg(client, + IMX415_LF_EXPO_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_H(shr0)); + + ret |= imx415_write_reg(client, IMX415_GROUP_HOLD_REG, + IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_END); + return ret; +} + +static int imx415_get_channel_info(struct imx415 *imx415, struct rkmodule_channel_info *ch_info) +{ + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + ch_info->vc = imx415->cur_mode->vc[ch_info->index]; + ch_info->width = imx415->cur_mode->width; + ch_info->height = imx415->cur_mode->height; + ch_info->bus_fmt = imx415->cur_mode->bus_fmt; + return 0; +} + +static long imx415_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct imx415 *imx415 = to_imx415(sd); + struct rkmodule_hdr_cfg *hdr; + struct rkmodule_channel_info *ch_info; + u32 h, w, stream; + long ret = 0; + const struct imx415_mode *mode; + u64 pixel_rate = 0; + struct rkmodule_csi_dphy_param *dphy_param; + u8 lanes = imx415->lanes; + + switch (cmd) { + case PREISP_CMD_SET_HDRAE_EXP: + if (imx415->cur_mode->hdr_mode == HDR_X2) + ret = imx415_set_hdrae(imx415, arg); + else if (imx415->cur_mode->hdr_mode == HDR_X3) + ret = imx415_set_hdrae_3frame(imx415, arg); + break; + case RKMODULE_GET_MODULE_INFO: + imx415_get_module_inf(imx415, (struct rkmodule_inf *)arg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = (struct rkmodule_hdr_cfg *)arg; + hdr->esp.mode = HDR_NORMAL_VC; + hdr->hdr_mode = imx415->cur_mode->hdr_mode; + break; + case RKMODULE_SET_HDR_CFG: + + imx415_change_mode(imx415, &imx415->supported_modes[imx415->capture_mode]); + + if(imx415->capture_mode == 0) + dev_info(&imx415->client->dev, "capture_mode is 3840x2160\n"); + else if(imx415->capture_mode == 1) + dev_info(&imx415->client->dev, "capture_mode is 1920x1080\n"); + else + dev_info(&imx415->client->dev, "not support capture_mode0\n"); + + mode = imx415->cur_mode; + if (imx415->streaming) { + ret = imx415_write_reg(imx415->client, IMX415_GROUP_HOLD_REG, + IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_START); + + ret |= imx415_write_array(imx415->client, imx415->cur_mode->reg_list); + + ret |= imx415_write_reg(imx415->client, IMX415_GROUP_HOLD_REG, + IMX415_REG_VALUE_08BIT, IMX415_GROUP_HOLD_END); + if (ret) + return ret; + } + w = mode->hts_def - imx415->cur_mode->width; + h = mode->vts_def - mode->height; + mutex_lock(&imx415->mutex); + __v4l2_ctrl_modify_range(imx415->hblank, w, w, 1, w); + __v4l2_ctrl_modify_range(imx415->vblank, h, + IMX415_VTS_MAX - mode->height, + 1, h); + __v4l2_ctrl_s_ctrl(imx415->link_freq, mode->mipi_freq_idx); + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / + mode->bpp * 2 * lanes; + __v4l2_ctrl_s_ctrl_int64(imx415->pixel_rate, + pixel_rate); + mutex_unlock(&imx415->mutex); + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = imx415_write_reg(imx415->client, IMX415_REG_CTRL_MODE, + IMX415_REG_VALUE_08BIT, IMX415_MODE_STREAMING); + else + ret = imx415_write_reg(imx415->client, IMX415_REG_CTRL_MODE, + IMX415_REG_VALUE_08BIT, IMX415_MODE_SW_STANDBY); + break; + case RKMODULE_GET_SONY_BRL: + if (imx415->cur_mode->width == 3864 && imx415->cur_mode->height == 2192) + *((u32 *)arg) = BRL_ALL; + else + *((u32 *)arg) = BRL_BINNING; + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = imx415_get_channel_info(imx415, ch_info); + break; + case RKMODULE_GET_CSI_DPHY_PARAM: + if (imx415->cur_mode->hdr_mode == HDR_X2) { + dphy_param = (struct rkmodule_csi_dphy_param *)arg; + *dphy_param = dcphy_param; + dev_info(&imx415->client->dev, + "get sensor dphy param\n"); + } else + ret = -EINVAL; + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long imx415_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + struct rkmodule_hdr_cfg *hdr; + struct preisp_hdrae_exp_s *hdrae; + struct rkmodule_channel_info *ch_info; + long ret; + u32 stream; + u32 brl = 0; + struct rkmodule_csi_dphy_param *dphy_param; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = imx415_ioctl(sd, cmd, inf); + if (!ret) { + if (copy_to_user(up, inf, sizeof(*inf))) { + kfree(inf); + return -EFAULT; + } + } + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + if (copy_from_user(cfg, up, sizeof(*cfg))) { + kfree(cfg); + return -EFAULT; + } + ret = imx415_ioctl(sd, cmd, cfg); + kfree(cfg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = imx415_ioctl(sd, cmd, hdr); + if (!ret) { + if (copy_to_user(up, hdr, sizeof(*hdr))) { + kfree(hdr); + return -EFAULT; + } + } + kfree(hdr); + break; + case RKMODULE_SET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + if (copy_from_user(hdr, up, sizeof(*hdr))) { + kfree(hdr); + return -EFAULT; + } + ret = imx415_ioctl(sd, cmd, hdr); + kfree(hdr); + break; + case PREISP_CMD_SET_HDRAE_EXP: + hdrae = kzalloc(sizeof(*hdrae), GFP_KERNEL); + if (!hdrae) { + ret = -ENOMEM; + return ret; + } + + if (copy_from_user(hdrae, up, sizeof(*hdrae))) { + kfree(hdrae); + return -EFAULT; + } + ret = imx415_ioctl(sd, cmd, hdrae); + kfree(hdrae); + break; + case RKMODULE_SET_QUICK_STREAM: + if (copy_from_user(&stream, up, sizeof(u32))) + return -EFAULT; + ret = imx415_ioctl(sd, cmd, &stream); + break; + case RKMODULE_GET_SONY_BRL: + ret = imx415_ioctl(sd, cmd, &brl); + if (!ret) { + if (copy_to_user(up, &brl, sizeof(u32))) + return -EFAULT; + } + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); + if (!ch_info) { + ret = -ENOMEM; + return ret; + } + + ret = imx415_ioctl(sd, cmd, ch_info); + if (!ret) { + ret = copy_to_user(up, ch_info, sizeof(*ch_info)); + if (ret) + ret = -EFAULT; + } + kfree(ch_info); + break; + case RKMODULE_GET_CSI_DPHY_PARAM: + dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL); + if (!dphy_param) { + ret = -ENOMEM; + return ret; + } + + ret = imx415_ioctl(sd, cmd, dphy_param); + if (!ret) { + ret = copy_to_user(up, dphy_param, sizeof(*dphy_param)); + if (ret) + ret = -EFAULT; + } + kfree(dphy_param); + break; + + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + + +static int __imx415_start_stream(struct imx415 *imx415) +{ + int ret; + + if (!imx415->is_thunderboot) { + ret = imx415_write_array(imx415->client, imx415->cur_mode->global_reg_list); + if (ret) + return ret; + ret = imx415_write_array(imx415->client, imx415->cur_mode->reg_list); + if (ret) + return ret; + } + + /* In case these controls are set before streaming */ + ret = __v4l2_ctrl_handler_setup(&imx415->ctrl_handler); + if (ret) + return ret; + if (imx415->has_init_exp && imx415->cur_mode->hdr_mode != NO_HDR) { + ret = imx415_ioctl(&imx415->subdev, PREISP_CMD_SET_HDRAE_EXP, + &imx415->init_hdrae_exp); + if (ret) { + dev_err(&imx415->client->dev, + "init exp fail in hdr mode\n"); + return ret; + } + } + return imx415_write_reg(imx415->client, IMX415_REG_CTRL_MODE, + IMX415_REG_VALUE_08BIT, 0); +} + +static int __imx415_stop_stream(struct imx415 *imx415) +{ + imx415->has_init_exp = false; + if (imx415->is_thunderboot) + imx415->is_first_streamoff = true; + return imx415_write_reg(imx415->client, IMX415_REG_CTRL_MODE, + IMX415_REG_VALUE_08BIT, 1); +} + +static int imx415_s_stream(struct v4l2_subdev *sd, int on) +{ + struct imx415 *imx415 = to_imx415(sd); + struct i2c_client *client = imx415->client; + int ret = 0; + + dev_info(&imx415->client->dev, "s_stream: %d. %dx%d, hdr: %d, bpp: %d\n", + on, imx415->cur_mode->width, imx415->cur_mode->height, + imx415->cur_mode->hdr_mode, imx415->cur_mode->bpp); + + mutex_lock(&imx415->mutex); + on = !!on; + if (on == imx415->streaming) + goto unlock_and_return; + + if (on) { + if (imx415->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) { + imx415->is_thunderboot = false; + __imx415_power_on(imx415); + } + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __imx415_start_stream(imx415); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __imx415_stop_stream(imx415); + pm_runtime_put(&client->dev); + } + + imx415->streaming = on; + +unlock_and_return: + mutex_unlock(&imx415->mutex); + + return ret; +} + +static int imx415_s_power(struct v4l2_subdev *sd, int on) +{ + struct imx415 *imx415 = to_imx415(sd); + struct i2c_client *client = imx415->client; + int ret = 0; + + mutex_lock(&imx415->mutex); + + if (imx415->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + imx415->power_on = true; + } else { + pm_runtime_put(&client->dev); + imx415->power_on = false; + } + +unlock_and_return: + mutex_unlock(&imx415->mutex); + + return ret; +} + +int __imx415_power_on(struct imx415 *imx415) +{ + int ret; + struct device *dev = &imx415->client->dev; + if (!IS_ERR_OR_NULL(imx415->pins_default)) { + ret = pinctrl_select_state(imx415->pinctrl, + imx415->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + if (!imx415->is_thunderboot) { + if (!IS_ERR(imx415->power_gpio)) + gpiod_direction_output(imx415->power_gpio, 1); + /* At least 500ns between power raising and XCLR */ + /* fix power on timing if insmod this ko */ + usleep_range(10 * 1000, 20 * 1000); + if (!IS_ERR(imx415->reset_gpio)) + gpiod_direction_output(imx415->reset_gpio, 0); + + /* At least 1us between XCLR and clk */ + /* fix power on timing if insmod this ko */ + usleep_range(10 * 1000, 20 * 1000); + } + ret = clk_set_rate(imx415->xvclk, imx415->cur_mode->xvclk); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate\n"); + if (clk_get_rate(imx415->xvclk) != imx415->cur_mode->xvclk) + dev_warn(dev, "xvclk mismatched\n"); + ret = clk_prepare_enable(imx415->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + goto err_clk; + } + + /* At least 20us between XCLR and I2C communication */ + usleep_range(20*1000, 30*1000); + + return 0; + +err_clk: + if (!IS_ERR(imx415->reset_gpio)) + gpiod_direction_output(imx415->reset_gpio, 1); + if (!IS_ERR_OR_NULL(imx415->pins_sleep)) + pinctrl_select_state(imx415->pinctrl, imx415->pins_sleep); + + return ret; +} + +static void __imx415_power_off(struct imx415 *imx415) +{ + int ret; + struct device *dev = &imx415->client->dev; + + if (imx415->is_thunderboot) { + if (imx415->is_first_streamoff) { + imx415->is_thunderboot = false; + imx415->is_first_streamoff = false; + } else { + return; + } + } + + if (!IS_ERR(imx415->reset_gpio)) + gpiod_direction_output(imx415->reset_gpio, 1); + clk_disable_unprepare(imx415->xvclk); + if (!IS_ERR_OR_NULL(imx415->pins_sleep)) { + ret = pinctrl_select_state(imx415->pinctrl, + imx415->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + if (!IS_ERR(imx415->power_gpio)) + gpiod_direction_output(imx415->power_gpio, 0); + regulator_bulk_disable(IMX415_NUM_SUPPLIES, imx415->supplies); +} + +static int __maybe_unused imx415_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx415 *imx415 = to_imx415(sd); + + return __imx415_power_on(imx415); +} + +static int __maybe_unused imx415_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx415 *imx415 = to_imx415(sd); + + __imx415_power_off(imx415); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int imx415_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct imx415 *imx415 = to_imx415(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct imx415_mode *def_mode = &imx415->supported_modes[0]; + + mutex_lock(&imx415->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = def_mode->bus_fmt; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&imx415->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int imx415_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct imx415 *imx415 = to_imx415(sd); + + if (fie->index >= imx415->cfg_num) + return -EINVAL; + + fie->code = imx415->supported_modes[fie->index].bus_fmt; + fie->width = imx415->supported_modes[fie->index].width; + fie->height = imx415->supported_modes[fie->index].height; + fie->interval = imx415->supported_modes[fie->index].max_fps; + fie->reserved[0] = imx415->supported_modes[fie->index].hdr_mode; + return 0; +} + +#define CROP_START(SRC, DST) (((SRC) - (DST)) / 2 / 4 * 4) +#define DST_WIDTH_3840 3840 +#define DST_HEIGHT_2160 2160 +#define DST_WIDTH_1920 1920 +#define DST_HEIGHT_1080 1080 +#define DST_WIDTH_1280 1280 +#define DST_HEIGHT_720 720 + +/* + * The resolution of the driver configuration needs to be exactly + * the same as the current output resolution of the sensor, + * the input width of the isp needs to be 16 aligned, + * the input height of the isp needs to be 8 aligned. + * Can be cropped to standard resolution by this function, + * otherwise it will crop out strange resolution according + * to the alignment rules. + */ +static int imx415_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct imx415 *imx415 = to_imx415(sd); + + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + if (imx415->cur_mode->width == 3864) { + sel->r.left = CROP_START(imx415->cur_mode->width, DST_WIDTH_3840); + sel->r.width = DST_WIDTH_3840; + sel->r.top = CROP_START(imx415->cur_mode->height, DST_HEIGHT_2160); + sel->r.height = DST_HEIGHT_2160; + } else if (imx415->cur_mode->width == 1944) { + sel->r.left = CROP_START(imx415->cur_mode->width, DST_WIDTH_1920); + sel->r.width = DST_WIDTH_1920; + sel->r.top = CROP_START(imx415->cur_mode->height, DST_HEIGHT_1080); + sel->r.height = DST_HEIGHT_1080; + } else if (imx415->cur_mode->width == 1284) { + sel->r.left = CROP_START(imx415->cur_mode->width, DST_WIDTH_1280); + sel->r.width = DST_WIDTH_1280; + sel->r.top = CROP_START(imx415->cur_mode->height, DST_HEIGHT_720); + sel->r.height = DST_HEIGHT_720; + } else { + sel->r.left = CROP_START(imx415->cur_mode->width, imx415->cur_mode->width); + sel->r.width = imx415->cur_mode->width; + sel->r.top = CROP_START(imx415->cur_mode->height, imx415->cur_mode->height); + sel->r.height = imx415->cur_mode->height; + } + return 0; + } + return -EINVAL; +} + +static const struct dev_pm_ops imx415_pm_ops = { + SET_RUNTIME_PM_OPS(imx415_runtime_suspend, + imx415_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops imx415_internal_ops = { + .open = imx415_open, +}; +#endif + +static const struct v4l2_subdev_core_ops imx415_core_ops = { + .s_power = imx415_s_power, + .ioctl = imx415_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = imx415_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops imx415_video_ops = { + .s_stream = imx415_s_stream, + .g_frame_interval = imx415_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops imx415_pad_ops = { + .enum_mbus_code = imx415_enum_mbus_code, + .enum_frame_size = imx415_enum_frame_sizes, + .enum_frame_interval = imx415_enum_frame_interval, + .get_fmt = imx415_get_fmt, + .set_fmt = imx415_set_fmt, + .get_selection = imx415_get_selection, + .get_mbus_config = imx415_g_mbus_config, +}; + +static const struct v4l2_subdev_ops imx415_subdev_ops = { + .core = &imx415_core_ops, + .video = &imx415_video_ops, + .pad = &imx415_pad_ops, +}; + +static int imx415_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx415 *imx415 = container_of(ctrl->handler, + struct imx415, ctrl_handler); + struct i2c_client *client = imx415->client; + s64 max; + u32 vts = 0, val; + int ret = 0; + u32 shr0 = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + if (imx415->cur_mode->hdr_mode == NO_HDR) { + /* Update max exposure while meeting expected vblanking */ + max = imx415->cur_mode->height + ctrl->val - 8; + __v4l2_ctrl_modify_range(imx415->exposure, + imx415->exposure->minimum, max, + imx415->exposure->step, + imx415->exposure->default_value); + } + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if (imx415->cur_mode->hdr_mode != NO_HDR) + goto ctrl_end; + shr0 = imx415->cur_vts - ctrl->val; + ret = imx415_write_reg(imx415->client, IMX415_LF_EXPO_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_L(shr0)); + ret |= imx415_write_reg(imx415->client, IMX415_LF_EXPO_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_M(shr0)); + ret |= imx415_write_reg(imx415->client, IMX415_LF_EXPO_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_EXP_H(shr0)); + dev_dbg(&client->dev, "set exposure(shr0) %d = cur_vts(%d) - val(%d)\n", + shr0, imx415->cur_vts, ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + if (imx415->cur_mode->hdr_mode != NO_HDR) + goto ctrl_end; + ret = imx415_write_reg(imx415->client, IMX415_LF_GAIN_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_GAIN_H(ctrl->val)); + ret |= imx415_write_reg(imx415->client, IMX415_LF_GAIN_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_GAIN_L(ctrl->val)); + dev_dbg(&client->dev, "set analog gain 0x%x\n", + ctrl->val); + break; + case V4L2_CID_VBLANK: + vts = ctrl->val + imx415->cur_mode->height; + /* + * vts of hdr mode is double to correct T-line calculation. + * Restore before write to reg. + */ + if (imx415->cur_mode->hdr_mode == HDR_X2) { + vts = (vts + 3) / 4 * 4; + imx415->cur_vts = vts; + vts /= 2; + } else if (imx415->cur_mode->hdr_mode == HDR_X3) { + vts = (vts + 11) / 12 * 12; + imx415->cur_vts = vts; + vts /= 4; + } else { + imx415->cur_vts = vts; + } + ret = imx415_write_reg(imx415->client, IMX415_VTS_REG_L, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_VTS_L(vts)); + ret |= imx415_write_reg(imx415->client, IMX415_VTS_REG_M, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_VTS_M(vts)); + ret |= imx415_write_reg(imx415->client, IMX415_VTS_REG_H, + IMX415_REG_VALUE_08BIT, + IMX415_FETCH_VTS_H(vts)); + dev_dbg(&client->dev, "set vblank 0x%x vts %d\n", + ctrl->val, vts); + break; + case V4L2_CID_HFLIP: + ret = imx415_read_reg(imx415->client, IMX415_FLIP_REG, + IMX415_REG_VALUE_08BIT, &val); + if (ret) + break; + if (ctrl->val) + val |= IMX415_MIRROR_BIT_MASK; + else + val &= ~IMX415_MIRROR_BIT_MASK; + ret = imx415_write_reg(imx415->client, IMX415_FLIP_REG, + IMX415_REG_VALUE_08BIT, val); + break; + case V4L2_CID_VFLIP: + ret = imx415_read_reg(imx415->client, IMX415_FLIP_REG, + IMX415_REG_VALUE_08BIT, &val); + if (ret) + break; + if (ctrl->val) + val |= IMX415_FLIP_BIT_MASK; + else + val &= ~IMX415_FLIP_BIT_MASK; + ret = imx415_write_reg(imx415->client, IMX415_FLIP_REG, + IMX415_REG_VALUE_08BIT, val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + +ctrl_end: + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx415_ctrl_ops = { + .s_ctrl = imx415_set_ctrl, +}; + +static int imx415_initialize_controls(struct imx415 *imx415) +{ + const struct imx415_mode *mode; + struct v4l2_ctrl_handler *handler; + s64 exposure_max, vblank_def; + u64 pixel_rate; + u64 max_pixel_rate; + u32 h_blank; + int ret; + u8 lanes = imx415->lanes; + + handler = &imx415->ctrl_handler; + mode = imx415->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); + if (ret) + return ret; + handler->lock = &imx415->mutex; + + imx415->link_freq = v4l2_ctrl_new_int_menu(handler, NULL, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_items) - 1, 0, + link_freq_items); + v4l2_ctrl_s_ctrl(imx415->link_freq, mode->mipi_freq_idx); + + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * lanes; + max_pixel_rate = MIPI_FREQ_1188M / mode->bpp * 2 * lanes; + imx415->pixel_rate = v4l2_ctrl_new_std(handler, NULL, + V4L2_CID_PIXEL_RATE, 0, max_pixel_rate, + 1, pixel_rate); + + h_blank = mode->hts_def - mode->width; + imx415->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (imx415->hblank) + imx415->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + imx415->vblank = v4l2_ctrl_new_std(handler, &imx415_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + IMX415_VTS_MAX - mode->height, + 1, vblank_def); + imx415->cur_vts = mode->vts_def; + + exposure_max = mode->vts_def - 8; + imx415->exposure = v4l2_ctrl_new_std(handler, &imx415_ctrl_ops, + V4L2_CID_EXPOSURE, IMX415_EXPOSURE_MIN, + exposure_max, IMX415_EXPOSURE_STEP, + mode->exp_def); + + imx415->anal_a_gain = v4l2_ctrl_new_std(handler, &imx415_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, IMX415_GAIN_MIN, + IMX415_GAIN_MAX, IMX415_GAIN_STEP, + IMX415_GAIN_DEFAULT); + + v4l2_ctrl_new_std(handler, &imx415_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(handler, &imx415_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (handler->error) { + ret = handler->error; + dev_err(&imx415->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + imx415->subdev.ctrl_handler = handler; + imx415->has_init_exp = false; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int imx415_check_sensor_id(struct imx415 *imx415, + struct i2c_client *client) +{ + struct device *dev = &imx415->client->dev; + u32 id = 0; + int ret; + + if (imx415->is_thunderboot) { + dev_info(dev, "Enable thunderboot mode, skip sensor id check\n"); + return 0; + } + + ret = imx415_read_reg(client, IMX415_REG_CHIP_ID, + IMX415_REG_VALUE_08BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + + dev_info(dev, "Detected imx415 id %06x\n", CHIP_ID); + + return 0; +} + +static int imx415_configure_regulators(struct imx415 *imx415) +{ + unsigned int i; + + for (i = 0; i < IMX415_NUM_SUPPLIES; i++) + imx415->supplies[i].supply = imx415_supply_names[i]; + + return devm_regulator_bulk_get(&imx415->client->dev, + IMX415_NUM_SUPPLIES, + imx415->supplies); +} + +static int imx415_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct imx415 *imx415; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + u32 hdr_mode; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + imx415 = devm_kzalloc(dev, sizeof(*imx415), GFP_KERNEL); + if (!imx415) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &imx415->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &imx415->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &imx415->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &imx415->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + ret = of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode); + if (ret) { + hdr_mode = NO_HDR; + dev_warn(dev, " Get hdr mode failed! no hdr default\n"); + } + imx415->client = client; + + ret = of_property_read_u32(node, DATA_LANES, &imx415->lanes); + if (ret) { + imx415->lanes = 4; + dev_warn(dev, " Get data lanes failed! 4 lanes default\n"); + } + if (imx415->lanes == IMX415_4LANES) { + imx415->supported_modes = supported_modes; + imx415->cfg_num = ARRAY_SIZE(supported_modes); + } else { + imx415->supported_modes = supported_modes_2lane; + imx415->cfg_num = ARRAY_SIZE(supported_modes_2lane); + } + dev_info(dev, "detect imx415 lane %d\n", + imx415->lanes); + + imx415->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP); + + ret = of_property_read_u32(node, OF_CAMERA_CAPTURE_MODE, &imx415->capture_mode); + if (ret) { + imx415->capture_mode = 0; + dev_warn(dev, " Get capture_mode failed! captrue revolution setting to 3840x2160\n"); + } + + imx415->cur_mode = &imx415->supported_modes[imx415->capture_mode]; + + imx415->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(imx415->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + imx415->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS); + if (IS_ERR(imx415->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + imx415->power_gpio = devm_gpiod_get(dev, "power", GPIOD_ASIS); + if (IS_ERR(imx415->power_gpio)) + dev_warn(dev, "Failed to get power-gpios\n"); + imx415->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(imx415->pinctrl)) { + imx415->pins_default = + pinctrl_lookup_state(imx415->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(imx415->pins_default)) + dev_info(dev, "could not get default pinstate\n"); + + imx415->pins_sleep = + pinctrl_lookup_state(imx415->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(imx415->pins_sleep)) + dev_info(dev, "could not get sleep pinstate\n"); + } else { + dev_info(dev, "no pinctrl\n"); + } + + ret = imx415_configure_regulators(imx415); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&imx415->mutex); + + sd = &imx415->subdev; + v4l2_i2c_subdev_init(sd, client, &imx415_subdev_ops); + ret = imx415_initialize_controls(imx415); + if (ret) + goto err_destroy_mutex; + + ret = __imx415_power_on(imx415); + if (ret) + goto err_free_handler; + + ret = imx415_check_sensor_id(imx415, client); + if (ret) + goto err_power_off; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &imx415_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + imx415->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &imx415->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(imx415->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + imx415->module_index, facing, + IMX415_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __imx415_power_off(imx415); +err_free_handler: + v4l2_ctrl_handler_free(&imx415->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&imx415->mutex); + + return ret; +} + +static int imx415_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx415 *imx415 = to_imx415(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&imx415->ctrl_handler); + mutex_destroy(&imx415->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __imx415_power_off(imx415); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id imx415_of_match[] = { + { .compatible = "sony,imx415" }, + {}, +}; +MODULE_DEVICE_TABLE(of, imx415_of_match); +#endif + +static const struct i2c_device_id imx415_match_id[] = { + { "sony,imx415", 0 }, + { }, +}; + +static struct i2c_driver imx415_i2c_driver = { + .driver = { + .name = IMX415_NAME, + .pm = &imx415_pm_ops, + .of_match_table = of_match_ptr(imx415_of_match), + }, + .probe = &imx415_probe, + .remove = &imx415_remove, + .id_table = imx415_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&imx415_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&imx415_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("Sony imx415 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx708.c b/drivers/media/i2c/imx708.c new file mode 100644 index 0000000000000..097f92e1f16e1 --- /dev/null +++ b/drivers/media/i2c/imx708.c @@ -0,0 +1,2121 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * imx708 driver + * + * Copyright (C) 2017 Rockchip Electronics Co., Ltd. + * V0.0X01.0X00 init version. + */ + +//#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "otp_eeprom.h" + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x00) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define IMX708_LINK_FREQ_450MHZ 450000000 // 800Mbps per lane +#define IMX708_LINK_FREQ_447MHZ 447000000 // 1250Mbps per lane +#define IMX708_LINK_FREQ_453MHZ 453000000 + +#define IMX708_LANES 4 + +#define PIXEL_RATE_WITH_848M_10BIT (IMX708_LINK_FREQ_400 * 2 / 10 * 4) +#define PIXEL_RATE_WITH_848M_12BIT (IMX708_LINK_FREQ_400 * 2 / 12 * 4) + +#define IMX708_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x0708 +#define IMX708_REG_CHIP_ID_H 0x0016 +#define IMX708_REG_CHIP_ID_L 0x0017 + +#define IMX708_REG_CTRL_MODE 0x0100 +#define IMX708_MODE_SW_STANDBY 0x0 +#define IMX708_MODE_STREAMING 0x1 + +#define IMX708_REG_EXPOSURE_H 0x0202 +#define IMX708_REG_EXPOSURE_L 0x0203 +#define IMX708_EXPOSURE_OFFSET 48 +#define IMX708_EXPOSURE_MIN 1 +#define IMX708_EXPOSURE_STEP 1 +#define IMX708_EXPOSURE_MAX (IMX708_VTS_MAX - IMX708_EXPOSURE_OFFSET) +#define IMX708_VTS_MAX 0xffff + +#define IMX708_REG_GAIN_H 0x0204 +#define IMX708_REG_GAIN_L 0x0205 +#define IMX708_GAIN_MIN 112 +#define IMX708_GAIN_MAX 960 +#define IMX708_GAIN_STEP 1 +#define IMX708_GAIN_DEFAULT IMX708_GAIN_MIN + +#define IMX708_REG_DGAIN 0x3130 +#define IMX708_DGAIN_MODE BIT(0) +#define IMX708_REG_DGAINGR_H 0x020e +#define IMX708_REG_DGAINGR_L 0x020f +#define IMX708_REG_DGAINR_H 0x0210 +#define IMX708_REG_DGAINR_L 0x0211 +#define IMX708_REG_DGAINB_H 0x0212 +#define IMX708_REG_DGAINB_L 0x0213 +#define IMX708_REG_DGAINGB_H 0x0214 +#define IMX708_REG_DGAINGB_L 0x0215 +#define IMX708_REG_GAIN_GLOBAL_H 0x3ffc +#define IMX708_REG_GAIN_GLOBAL_L 0x3ffd + +#define IMX708_REG_TEST_PATTERN 0x0600 +#define IMX708_TEST_PATTERN_ENABLE 0x1 +#define IMX708_TEST_PATTERN_DISABLE 0x0 + +#define IMX708_REG_VTS_H 0x0340 +#define IMX708_REG_VTS_L 0x0341 + +#define IMX708_FLIP_MIRROR_REG 0x0101 +#define IMX708_MIRROR_BIT_MASK BIT(0) +#define IMX708_FLIP_BIT_MASK BIT(1) + +#define IMX708_FETCH_EXP_H(VAL) (((VAL) >> 8) & 0xFF) +#define IMX708_FETCH_EXP_L(VAL) ((VAL) & 0xFF) + +#define IMX708_FETCH_AGAIN_H(VAL) (((VAL) >> 8) & 0x03) +#define IMX708_FETCH_AGAIN_L(VAL) ((VAL) & 0xFF) + +#define IMX708_FETCH_DGAIN_H(VAL) (((VAL) >> 8) & 0x0F) +#define IMX708_FETCH_DGAIN_L(VAL) ((VAL) & 0xFF) + +#define IMX708_FETCH_RHS1_H(VAL) (((VAL) >> 16) & 0x0F) +#define IMX708_FETCH_RHS1_M(VAL) (((VAL) >> 8) & 0xFF) +#define IMX708_FETCH_RHS1_L(VAL) ((VAL) & 0xFF) + +#define REG_DELAY 0xFFFE +#define REG_NULL 0xFFFF + +#define IMX708_REG_VALUE_08BIT 1 +#define IMX708_REG_VALUE_16BIT 2 +#define IMX708_REG_VALUE_24BIT 3 + +/* HDR exposure ratio (long:med == med:short) */ +#define IMX708_HDR_EXPOSURE_RATIO 4 +#define IMX708_REG_MID_EXPOSURE 0x3116 +#define IMX708_REG_SHT_EXPOSURE 0x0224 +#define IMX708_REG_MID_ANALOG_GAIN 0x3118 +#define IMX708_REG_SHT_ANALOG_GAIN 0x0216 + +#define OF_CAMERA_HDR_MODE "rockchip,camera-hdr-mode" + +#define IMX708_NAME "imx708" + +static const char * const imx708_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define IMX708_NUM_SUPPLIES ARRAY_SIZE(imx708_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +struct other_data { + u32 width; + u32 height; + u32 bus_fmt; + u32 data_type; + u32 data_bit; +}; + +struct imx708_mode { + u32 bus_fmt; + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *global_reg_list; + const struct regval *reg_list; + u32 hdr_mode; + u32 mipi_freq_idx; + const struct other_data *spd; + u32 vc[PAD_MAX]; +}; + +struct imx708 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[IMX708_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *h_flip; + struct v4l2_ctrl *v_flip; + struct v4l2_ctrl *test_pattern; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct mutex mutex; + bool streaming; + bool power_on; + const struct imx708_mode *cur_mode; + u32 cfg_num; + u32 cur_pixel_rate; + u32 cur_link_freq; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + u32 cur_vts; + bool has_init_exp; + struct preisp_hdrae_exp_s init_hdrae_exp; + u8 flip; + struct otp_info *otp; + u32 spd_id; +}; + +#define to_imx708(sd) container_of(sd, struct imx708, subdev) + +/* + *IMX708LQR All-pixel scan CSI-2_4lane 24Mhz + *AD:10bit Output:10bit 1696Mbps Master Mode 30fps + * + */ +static const struct regval imx708_linear_10bit_global_regs[] = { + {0x0100, 0x00}, + {0x0136, 0x18}, + {0x0137, 0x00}, + {0x33F0, 0x02}, + {0x33F1, 0x05}, + {0x3062, 0x00}, + {0x3063, 0x12}, + {0x3068, 0x00}, + {0x3069, 0x12}, + {0x306A, 0x00}, + {0x306B, 0x30}, + {0x3076, 0x00}, + {0x3077, 0x30}, + {0x3078, 0x00}, + {0x3079, 0x30}, + {0x5E54, 0x0C}, + {0x6E44, 0x00}, + {0xB0B6, 0x01}, + {0xE829, 0x00}, + {0xF001, 0x08}, + {0xF003, 0x08}, + {0xF00D, 0x10}, + {0xF00F, 0x10}, + {0xF031, 0x08}, + {0xF033, 0x08}, + {0xF03D, 0x10}, + {0xF03F, 0x10}, + {0x0112, 0x0A}, + {0x0113, 0x0A}, + {0x0114, 0x01}, + {0x0B8E, 0x01}, + {0x0B8F, 0x00}, + {0x0B94, 0x01}, + {0x0B95, 0x00}, + {0x3400, 0x01}, + {0x3478, 0x01}, + {0x3479, 0x1c}, + {0x3091, 0x01}, + {0x3092, 0x00}, + {0x3419, 0x00}, + {0xBCF1, 0x02}, + {0x3094, 0x01}, + {0x3095, 0x01}, + {0x3362, 0x00}, + {0x3363, 0x00}, + {0x3364, 0x00}, + {0x3365, 0x00}, + {0x0138, 0x01}, + + {REG_NULL, 0x00}, +}; + +static const struct regval imx708_linear_10bit_4608x2592_regs[] = { + {0x0342, 0x3D}, + {0x0343, 0x20}, + {0x0340, 0x0A}, + {0x0341, 0x59}, + {0x0344, 0x00}, + {0x0345, 0x00}, + {0x0346, 0x00}, + {0x0347, 0x00}, + {0x0348, 0x11}, + {0x0349, 0xFF}, + {0x034A, 0X0A}, + {0x034B, 0x1F}, + {0x0220, 0x62}, + {0x0222, 0x01}, + {0x0900, 0x00}, + {0x0901, 0x11}, + {0x0902, 0x0A}, + {0x3200, 0x01}, + {0x3201, 0x01}, + {0x32D5, 0x01}, + {0x32D6, 0x00}, + {0x32DB, 0x01}, + {0x32DF, 0x00}, + {0x350C, 0x00}, + {0x350D, 0x00}, + {0x0408, 0x00}, + {0x0409, 0x00}, + {0x040A, 0x00}, + {0x040B, 0x00}, + {0x040C, 0x12}, + {0x040D, 0x00}, + {0x040E, 0x0A}, + {0x040F, 0x20}, + {0x034C, 0x12}, + {0x034D, 0x00}, + {0x034E, 0x0A}, + {0x034F, 0x20}, + {0x0301, 0x05}, + {0x0303, 0x02}, + {0x0305, 0x02}, + {0x0306, 0x00}, + {0x0307, 0x7C}, + {0x030B, 0x02}, + {0x030D, 0x04}, + {0x030E, 0x01}, //Set default MIPI frequency to 450MHz + {0x030F, 0x2c}, + {0x0310, 0x01}, + {0x3CA0, 0x00}, + {0x3CA1, 0x64}, + {0x3CA4, 0x00}, + {0x3CA5, 0x00}, + {0x3CA6, 0x00}, + {0x3CA7, 0x00}, + {0x3CAA, 0x00}, + {0x3CAB, 0x00}, + {0x3CB8, 0x00}, + {0x3CB9, 0x08}, + {0x3CBA, 0x00}, + {0x3CBB, 0x00}, + {0x3CBC, 0x00}, + {0x3CBD, 0x3C}, + {0x3CBE, 0x00}, + {0x3CBF, 0x00}, + {0x0202, 0x0A}, + {0x0203, 0x29}, + {0x0224, 0x01}, + {0x0225, 0xF4}, + {0x3116, 0x01}, + {0x3117, 0xF4}, + {0x0204, 0x00}, + {0x0205, 0x00}, + {0x0216, 0x00}, + {0x0217, 0x00}, + {0x0218, 0x01}, + {0x0219, 0x00}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x3118, 0x00}, + {0x3119, 0x00}, + {0x311A, 0x01}, + {0x311B, 0x00}, + {0x341a, 0x00}, + {0x341b, 0x00}, + {0x341c, 0x00}, + {0x341d, 0x00}, + {0x341e, 0x01}, + {0x341f, 0x20}, + {0x3420, 0x00}, + {0x3421, 0xd8}, + {0x3366, 0x00}, + {0x3367, 0x00}, + {0x3368, 0x00}, + {0x3369, 0x00}, + + {REG_NULL, 0x00}, +}; + +static const struct regval imx708_2x2binned_regs[] = { + {0x0342, 0x1E}, + {0x0343, 0x90}, + {0x0340, 0x05}, + {0x0341, 0x38}, + {0x0344, 0x00}, + {0x0345, 0x00}, + {0x0346, 0x00}, + {0x0347, 0x00}, + {0x0348, 0x11}, + {0x0349, 0xFF}, + {0x034A, 0X0A}, + {0x034B, 0x1F}, + {0x0220, 0x62}, + {0x0222, 0x01}, + {0x0900, 0x01}, + {0x0901, 0x22}, + {0x0902, 0x08}, + {0x3200, 0x41}, + {0x3201, 0x41}, + {0x32D5, 0x00}, + {0x32D6, 0x00}, + {0x32DB, 0x01}, + {0x32DF, 0x00}, + {0x350C, 0x00}, + {0x350D, 0x00}, + {0x0408, 0x00}, + {0x0409, 0x00}, + {0x040A, 0x00}, + {0x040B, 0x00}, + {0x040C, 0x09}, + {0x040D, 0x00}, + {0x040E, 0x05}, + {0x040F, 0x10}, + {0x034C, 0x09}, + {0x034D, 0x00}, + {0x034E, 0x05}, + {0x034F, 0x10}, + {0x0301, 0x05}, + {0x0303, 0x02}, + {0x0305, 0x02}, + {0x0306, 0x00}, + {0x0307, 0x7A}, + {0x030B, 0x02}, + {0x030D, 0x04}, + {0x030E, 0x01}, //Set default MIPI frequency to 450MHz + {0x030F, 0x2c}, + {0x0310, 0x01}, + {0x3CA0, 0x00}, + {0x3CA1, 0x3C}, + {0x3CA4, 0x00}, + {0x3CA5, 0x3C}, + {0x3CA6, 0x00}, + {0x3CA7, 0x00}, + {0x3CAA, 0x00}, + {0x3CAB, 0x00}, + {0x3CB8, 0x00}, + {0x3CB9, 0x1C}, + {0x3CBA, 0x00}, + {0x3CBB, 0x08}, + {0x3CBC, 0x00}, + {0x3CBD, 0x1E}, + {0x3CBE, 0x00}, + {0x3CBF, 0x0A}, + {0x0202, 0x05}, + {0x0203, 0x08}, + {0x0224, 0x01}, + {0x0225, 0xF4}, + {0x3116, 0x01}, + {0x3117, 0xF4}, + {0x0204, 0x00}, + {0x0205, 0x70}, + {0x0216, 0x00}, + {0x0217, 0x70}, + {0x0218, 0x01}, + {0x0219, 0x00}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x3118, 0x00}, + {0x3119, 0x70}, + {0x311A, 0x01}, + {0x311B, 0x00}, + {0x341a, 0x00}, + {0x341b, 0x00}, + {0x341c, 0x00}, + {0x341d, 0x00}, + {0x341e, 0x00}, + {0x341f, 0x90}, + {0x3420, 0x00}, + {0x3421, 0x6c}, + {0x3366, 0x00}, + {0x3367, 0x00}, + {0x3368, 0x00}, + {0x3369, 0x00}, + + {REG_NULL, 0x00}, +}; + +static const struct regval imx708_2x2binned_720p_regs[] = { + {0x0342, 0x14}, + {0x0343, 0x60}, + {0x0340, 0x04}, + {0x0341, 0xB6}, + {0x0344, 0x03}, + {0x0345, 0x00}, + {0x0346, 0x01}, + {0x0347, 0xB0}, + {0x0348, 0x0E}, + {0x0349, 0xFF}, + {0x034A, 0x08}, + {0x034B, 0x6F}, + {0x0220, 0x62}, + {0x0222, 0x01}, + {0x0900, 0x01}, + {0x0901, 0x22}, + {0x0902, 0x08}, + {0x3200, 0x41}, + {0x3201, 0x41}, + {0x32D5, 0x00}, + {0x32D6, 0x00}, + {0x32DB, 0x01}, + {0x32DF, 0x01}, + {0x350C, 0x00}, + {0x350D, 0x00}, + {0x0408, 0x00}, + {0x0409, 0x00}, + {0x040A, 0x00}, + {0x040B, 0x00}, + {0x040C, 0x06}, + {0x040D, 0x00}, + {0x040E, 0x03}, + {0x040F, 0x60}, + {0x034C, 0x06}, + {0x034D, 0x00}, + {0x034E, 0x03}, + {0x034F, 0x60}, + {0x0301, 0x05}, + {0x0303, 0x02}, + {0x0305, 0x02}, + {0x0306, 0x00}, + {0x0307, 0x76}, + {0x030B, 0x02}, + {0x030D, 0x04}, + {0x030E, 0x01}, //Set default MIPI frequency to 450MHz + {0x030F, 0x2c}, + {0x0310, 0x01}, + {0x3CA0, 0x00}, + {0x3CA1, 0x3C}, + {0x3CA4, 0x01}, + {0x3CA5, 0x5E}, + {0x3CA6, 0x00}, + {0x3CA7, 0x00}, + {0x3CAA, 0x00}, + {0x3CAB, 0x00}, + {0x3CB8, 0x00}, + {0x3CB9, 0x0C}, + {0x3CBA, 0x00}, + {0x3CBB, 0x04}, + {0x3CBC, 0x00}, + {0x3CBD, 0x1E}, + {0x3CBE, 0x00}, + {0x3CBF, 0x05}, + {0x0202, 0x04}, + {0x0203, 0x86}, + {0x0224, 0x01}, + {0x0225, 0xF4}, + {0x3116, 0x01}, + {0x3117, 0xF4}, + {0x0204, 0x00}, + {0x0205, 0x70}, + {0x0216, 0x00}, + {0x0217, 0x70}, + {0x0218, 0x01}, + {0x0219, 0x00}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x3118, 0x00}, + {0x3119, 0x70}, + {0x311A, 0x01}, + {0x311B, 0x00}, + {0x341a, 0x00}, + {0x341b, 0x00}, + {0x341c, 0x00}, + {0x341d, 0x00}, + {0x341e, 0x00}, + {0x341f, 0x60}, + {0x3420, 0x00}, + {0x3421, 0x48}, + {0x3366, 0x00}, + {0x3367, 0x00}, + {0x3368, 0x00}, + {0x3369, 0x00}, + + {REG_NULL, 0x00}, +}; + +static const struct regval imx708_hdr_regs[] = { + {0x0342, 0x14}, + {0x0343, 0x60}, + {0x0340, 0x0A}, + {0x0341, 0x5B}, + {0x0344, 0x00}, + {0x0345, 0x00}, + {0x0346, 0x00}, + {0x0347, 0x00}, + {0x0348, 0x11}, + {0x0349, 0xFF}, + {0x034A, 0X0A}, + {0x034B, 0x1F}, + {0x0220, 0x01}, + {0x0222, IMX708_HDR_EXPOSURE_RATIO}, + {0x0900, 0x00}, + {0x0901, 0x11}, + {0x0902, 0x0A}, + {0x3200, 0x01}, + {0x3201, 0x01}, + {0x32D5, 0x00}, + {0x32D6, 0x00}, + {0x32DB, 0x01}, + {0x32DF, 0x00}, + {0x350C, 0x00}, + {0x350D, 0x00}, + {0x0408, 0x00}, + {0x0409, 0x00}, + {0x040A, 0x00}, + {0x040B, 0x00}, + {0x040C, 0x09}, + {0x040D, 0x00}, + {0x040E, 0x05}, + {0x040F, 0x10}, + {0x034C, 0x09}, + {0x034D, 0x00}, + {0x034E, 0x05}, + {0x034F, 0x10}, + {0x0301, 0x05}, + {0x0303, 0x02}, + {0x0305, 0x02}, + {0x0306, 0x00}, + {0x0307, 0xA2}, + {0x030B, 0x02}, + {0x030D, 0x04}, + {0x030E, 0x01}, //Set default MIPI frequency to 450MHz + {0x030F, 0x2c}, + {0x0310, 0x01}, + {0x3CA0, 0x00}, + {0x3CA1, 0x00}, + {0x3CA4, 0x00}, + {0x3CA5, 0x00}, + {0x3CA6, 0x00}, + {0x3CA7, 0x28}, + {0x3CAA, 0x00}, + {0x3CAB, 0x00}, + {0x3CB8, 0x00}, + {0x3CB9, 0x30}, + {0x3CBA, 0x00}, + {0x3CBB, 0x00}, + {0x3CBC, 0x00}, + {0x3CBD, 0x32}, + {0x3CBE, 0x00}, + {0x3CBF, 0x00}, + {0x0202, 0x0A}, + {0x0203, 0x2B}, + {0x0224, 0x0A}, + {0x0225, 0x2B}, + {0x3116, 0x0A}, + {0x3117, 0x2B}, + {0x0204, 0x00}, + {0x0205, 0x00}, + {0x0216, 0x00}, + {0x0217, 0x00}, + {0x0218, 0x01}, + {0x0219, 0x00}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x3118, 0x00}, + {0x3119, 0x00}, + {0x311A, 0x01}, + {0x311B, 0x00}, + {0x341a, 0x00}, + {0x341b, 0x00}, + {0x341c, 0x00}, + {0x341d, 0x00}, + {0x341e, 0x00}, + {0x341f, 0x90}, + {0x3420, 0x00}, + {0x3421, 0x6c}, + {0x3360, 0x01}, + {0x3361, 0x01}, + {0x3366, 0x09}, + {0x3367, 0x00}, + {0x3368, 0x05}, + {0x3369, 0x10}, + + {REG_NULL, 0x00}, +}; + +static const struct imx708_mode supported_modes[] = { + { + .width = 4608, + .height = 2592, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0B00, + .hts_def = 0x3D20, + .vts_def = 0x0A59, + .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, + .global_reg_list = imx708_linear_10bit_global_regs, + .reg_list = imx708_linear_10bit_4608x2592_regs, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 0, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + { + /*2x2Binning*/ + .width = 2304, + .height = 1296, + .max_fps = { + .numerator = 10000, + .denominator = 64100, + }, + .exp_def = 0x0B00, + .hts_def = 0x1E90, + .vts_def = 0x0538, + .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, + .global_reg_list = imx708_linear_10bit_global_regs, + .reg_list = imx708_2x2binned_regs, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 0, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + { + /*2x2 binning 720p*/ + .width = 1536, + .height = 864, + .max_fps = { + .numerator = 10000, + .denominator = 64100, + }, + .exp_def = 0x0B00, + .hts_def = 0x1460, + .vts_def = 0x04B6, + .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, + .global_reg_list = imx708_linear_10bit_global_regs, + .reg_list = imx708_2x2binned_720p_regs, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 0, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + { + .width = 2304, + .height = 1296, + .max_fps = { + .numerator = 10000, + .denominator = 97000, + }, + .exp_def = 0x0B00, + .hts_def = 0x1460, + .vts_def = 0x0A5B, + .bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10, + .global_reg_list = imx708_linear_10bit_global_regs, + .reg_list = imx708_hdr_regs, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 1, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, +}; + +static const s64 link_freq_items[] = { + IMX708_LINK_FREQ_450MHZ, + IMX708_LINK_FREQ_447MHZ, + IMX708_LINK_FREQ_453MHZ, +}; + +static const char * const imx708_test_pattern_menu[] = { + "Disabled", + "Solid color", + "100% color bars", + "Fade to grey color bars", + "PN9" +}; + +/* Write registers up to 4 at a time */ +static int imx708_write_reg(struct i2c_client *client, u16 reg, + int len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int imx708_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + if (unlikely(regs[i].addr == REG_DELAY)) + usleep_range(regs[i].val, regs[i].val * 2); + else + ret = imx708_write_reg(client, regs[i].addr, + IMX708_REG_VALUE_08BIT, + regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int imx708_read_reg(struct i2c_client *client, u16 reg, unsigned int len, + u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret, i; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + for (i = 0; i < 3; i++) { + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret == ARRAY_SIZE(msgs)) + break; + } + if (ret != ARRAY_SIZE(msgs) && i == 3) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int imx708_get_reso_dist(const struct imx708_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct imx708_mode * +imx708_find_best_fit(struct imx708 *imx708, struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < imx708->cfg_num; i++) { + dist = imx708_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int imx708_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx708 *imx708 = to_imx708(sd); + const struct imx708_mode *mode; + s64 h_blank, vblank_def; + u64 pixel_rate = 0; + + mutex_lock(&imx708->mutex); + + mode = imx708_find_best_fit(imx708, fmt); + fmt->format.code = mode->bus_fmt; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&imx708->mutex); + return -ENOTTY; +#endif + } else { + imx708->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(imx708->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(imx708->vblank, vblank_def, + IMX708_VTS_MAX - mode->height, + 1, vblank_def); + + __v4l2_ctrl_s_ctrl(imx708->vblank, vblank_def); + __v4l2_ctrl_s_ctrl(imx708->link_freq, mode->mipi_freq_idx); + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / 10 * 2 * IMX708_LANES; + __v4l2_ctrl_s_ctrl_int64(imx708->pixel_rate, + pixel_rate); + } + + dev_info(&imx708->client->dev, "%s: mode->mipi_freq_idx(%d)", + __func__, mode->mipi_freq_idx); + + mutex_unlock(&imx708->mutex); + + return 0; +} + +static int imx708_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx708 *imx708 = to_imx708(sd); + const struct imx708_mode *mode = imx708->cur_mode; + + mutex_lock(&imx708->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&imx708->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + if (imx708->flip & IMX708_MIRROR_BIT_MASK) { + fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; + if (imx708->flip & IMX708_FLIP_BIT_MASK) + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + } else if (imx708->flip & IMX708_FLIP_BIT_MASK) { + fmt->format.code = MEDIA_BUS_FMT_SGBRG10_1X10; + } else { + fmt->format.code = mode->bus_fmt; + } + fmt->format.field = V4L2_FIELD_NONE; + /* format info: width/height/data type/virctual channel */ + if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR) + fmt->reserved[0] = mode->vc[fmt->pad]; + else + fmt->reserved[0] = mode->vc[PAD0]; + } + mutex_unlock(&imx708->mutex); + + return 0; +} + +static int imx708_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx708 *imx708 = to_imx708(sd); + + if (code->index != 0) + return -EINVAL; + code->code = imx708->cur_mode->bus_fmt; + + return 0; +} + +static int imx708_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct imx708 *imx708 = to_imx708(sd); + + if (fse->index >= imx708->cfg_num) + return -EINVAL; + + if (fse->code != supported_modes[0].bus_fmt) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int imx708_enable_test_pattern(struct imx708 *imx708, u32 pattern) +{ + u32 val; + + if (pattern) + val = (pattern - 1) | IMX708_TEST_PATTERN_ENABLE; + else + val = IMX708_TEST_PATTERN_DISABLE; + + return imx708_write_reg(imx708->client, + IMX708_REG_TEST_PATTERN, + IMX708_REG_VALUE_08BIT, + val); +} + +static int imx708_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct imx708 *imx708 = to_imx708(sd); + const struct imx708_mode *mode = imx708->cur_mode; + + fi->interval = mode->max_fps; + + return 0; +} + +static int imx708_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct imx708 *imx708 = to_imx708(sd); + const struct imx708_mode *mode = imx708->cur_mode; + u32 val = 0; + + if (mode->hdr_mode == NO_HDR) + val = 1 << (IMX708_LANES - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + if (mode->hdr_mode == HDR_X2) + val = 1 << (IMX708_LANES - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | + V4L2_MBUS_CSI2_CHANNEL_1; + + config->type = V4L2_MBUS_CSI2_DPHY; + config->flags = val; + + return 0; +} + +static void imx708_get_otp(struct otp_info *otp, + struct rkmodule_inf *inf) +{ + u32 i, j; + u32 w, h; + + /* awb */ + if (otp->awb_data.flag) { + inf->awb.flag = 1; + inf->awb.r_value = otp->awb_data.r_ratio; + inf->awb.b_value = otp->awb_data.b_ratio; + inf->awb.gr_value = otp->awb_data.g_ratio; + inf->awb.gb_value = 0x0; + + inf->awb.golden_r_value = otp->awb_data.r_golden; + inf->awb.golden_b_value = otp->awb_data.b_golden; + inf->awb.golden_gr_value = otp->awb_data.g_golden; + inf->awb.golden_gb_value = 0x0; + } + + /* lsc */ + if (otp->lsc_data.flag) { + inf->lsc.flag = 1; + inf->lsc.width = otp->basic_data.size.width; + inf->lsc.height = otp->basic_data.size.height; + inf->lsc.table_size = otp->lsc_data.table_size; + + for (i = 0; i < 289; i++) { + inf->lsc.lsc_r[i] = (otp->lsc_data.data[i * 2] << 8) | + otp->lsc_data.data[i * 2 + 1]; + inf->lsc.lsc_gr[i] = (otp->lsc_data.data[i * 2 + 578] << 8) | + otp->lsc_data.data[i * 2 + 579]; + inf->lsc.lsc_gb[i] = (otp->lsc_data.data[i * 2 + 1156] << 8) | + otp->lsc_data.data[i * 2 + 1157]; + inf->lsc.lsc_b[i] = (otp->lsc_data.data[i * 2 + 1734] << 8) | + otp->lsc_data.data[i * 2 + 1735]; + } + } + + /* pdaf */ + if (otp->pdaf_data.flag) { + inf->pdaf.flag = 1; + inf->pdaf.gainmap_width = otp->pdaf_data.gainmap_width; + inf->pdaf.gainmap_height = otp->pdaf_data.gainmap_height; + inf->pdaf.dcc_mode = otp->pdaf_data.dcc_mode; + inf->pdaf.dcc_dir = otp->pdaf_data.dcc_dir; + inf->pdaf.dccmap_width = otp->pdaf_data.dccmap_width; + inf->pdaf.dccmap_height = otp->pdaf_data.dccmap_height; + w = otp->pdaf_data.gainmap_width; + h = otp->pdaf_data.gainmap_height; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + inf->pdaf.gainmap[i * w + j] = + (otp->pdaf_data.gainmap[(i * w + j) * 2] << 8) | + otp->pdaf_data.gainmap[(i * w + j) * 2 + 1]; + } + } + w = otp->pdaf_data.dccmap_width; + h = otp->pdaf_data.dccmap_height; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + inf->pdaf.dccmap[i * w + j] = + (otp->pdaf_data.dccmap[(i * w + j) * 2] << 8) | + otp->pdaf_data.dccmap[(i * w + j) * 2 + 1]; + } + } + } + + /* af */ + if (otp->af_data.flag) { + inf->af.flag = 1; + inf->af.dir_cnt = 1; + inf->af.af_otp[0].vcm_start = otp->af_data.af_inf; + inf->af.af_otp[0].vcm_end = otp->af_data.af_macro; + inf->af.af_otp[0].vcm_dir = 0; + } + +} + +static void imx708_get_module_inf(struct imx708 *imx708, + struct rkmodule_inf *inf) +{ + struct otp_info *otp = imx708->otp; + + memset(inf, 0, sizeof(*inf)); + strscpy(inf->base.sensor, IMX708_NAME, sizeof(inf->base.sensor)); + strscpy(inf->base.module, imx708->module_name, + sizeof(inf->base.module)); + strscpy(inf->base.lens, imx708->len_name, sizeof(inf->base.lens)); + if (otp) + imx708_get_otp(otp, inf); + +} + +static int imx708_get_channel_info(struct imx708 *imx708, struct rkmodule_channel_info *ch_info) +{ + const struct imx708_mode *mode = imx708->cur_mode; + + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + + if (ch_info->index == imx708->spd_id && mode->spd) { + ch_info->vc = V4L2_MBUS_CSI2_CHANNEL_0; + ch_info->width = mode->spd->width; + ch_info->height = mode->spd->height; + ch_info->bus_fmt = mode->spd->bus_fmt; + ch_info->data_type = mode->spd->data_type; + ch_info->data_bit = mode->spd->data_bit; + } else { + ch_info->vc = imx708->cur_mode->vc[ch_info->index]; + ch_info->width = imx708->cur_mode->width; + ch_info->height = imx708->cur_mode->height; + ch_info->bus_fmt = imx708->cur_mode->bus_fmt; + } + return 0; +} + +static long imx708_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct imx708 *imx708 = to_imx708(sd); + struct rkmodule_hdr_cfg *hdr; + struct rkmodule_channel_info *ch_info; + long ret = 0; + u32 i, h, w; + u32 stream = 0; + + switch (cmd) { + case PREISP_CMD_SET_HDRAE_EXP: + break; + case RKMODULE_GET_MODULE_INFO: + imx708_get_module_inf(imx708, (struct rkmodule_inf *)arg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = (struct rkmodule_hdr_cfg *)arg; + hdr->esp.mode = HDR_NORMAL_VC; + hdr->hdr_mode = imx708->cur_mode->hdr_mode; + break; + case RKMODULE_SET_HDR_CFG: + hdr = (struct rkmodule_hdr_cfg *)arg; + w = imx708->cur_mode->width; + h = imx708->cur_mode->height; + for (i = 0; i < imx708->cfg_num; i++) { + if (w == supported_modes[i].width && + h == supported_modes[i].height && + supported_modes[i].hdr_mode == hdr->hdr_mode) { + imx708->cur_mode = &supported_modes[i]; + break; + } + } + if (i == imx708->cfg_num) { + dev_err(&imx708->client->dev, + "not find hdr mode:%d %dx%d config\n", + hdr->hdr_mode, w, h); + ret = -EINVAL; + } else { + w = imx708->cur_mode->hts_def - + imx708->cur_mode->width; + h = imx708->cur_mode->vts_def - + imx708->cur_mode->height; + __v4l2_ctrl_modify_range(imx708->hblank, w, w, 1, w); + __v4l2_ctrl_modify_range(imx708->vblank, h, + IMX708_VTS_MAX - + imx708->cur_mode->height, + 1, h); + + if (imx708->cur_mode->bus_fmt == + MEDIA_BUS_FMT_SRGGB10_1X10) { + imx708->cur_link_freq = 0; + imx708->cur_pixel_rate = + PIXEL_RATE_WITH_848M_10BIT; + } else if (imx708->cur_mode->bus_fmt == + MEDIA_BUS_FMT_SRGGB12_1X12) { + imx708->cur_link_freq = 0; + imx708->cur_pixel_rate = + PIXEL_RATE_WITH_848M_12BIT; + } + + __v4l2_ctrl_s_ctrl_int64(imx708->pixel_rate, + imx708->cur_pixel_rate); + __v4l2_ctrl_s_ctrl(imx708->link_freq, + imx708->cur_link_freq); + } + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = imx708_write_reg(imx708->client, IMX708_REG_CTRL_MODE, + IMX708_REG_VALUE_08BIT, IMX708_MODE_STREAMING); + else + ret = imx708_write_reg(imx708->client, IMX708_REG_CTRL_MODE, + IMX708_REG_VALUE_08BIT, IMX708_MODE_SW_STANDBY); + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = imx708_get_channel_info(imx708, ch_info); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long imx708_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + struct rkmodule_hdr_cfg *hdr; + struct preisp_hdrae_exp_s *hdrae; + struct rkmodule_channel_info *ch_info; + long ret; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = imx708_ioctl(sd, cmd, inf); + if (!ret) { + ret = copy_to_user(up, inf, sizeof(*inf)); + if (ret) + ret = -EFAULT; + } + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = imx708_ioctl(sd, cmd, cfg); + else + ret = -EFAULT; + kfree(cfg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = imx708_ioctl(sd, cmd, hdr); + if (!ret) { + ret = copy_to_user(up, hdr, sizeof(*hdr)); + if (ret) + ret = -EFAULT; + } + kfree(hdr); + break; + case RKMODULE_SET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + ret = copy_from_user(hdr, up, sizeof(*hdr)); + if (!ret) + ret = imx708_ioctl(sd, cmd, hdr); + else + ret = -EFAULT; + kfree(hdr); + break; + case PREISP_CMD_SET_HDRAE_EXP: + hdrae = kzalloc(sizeof(*hdrae), GFP_KERNEL); + if (!hdrae) { + ret = -ENOMEM; + return ret; + } + ret = copy_from_user(hdrae, up, sizeof(*hdrae)); + if (!ret) + ret = imx708_ioctl(sd, cmd, hdrae); + else + ret = -EFAULT; + kfree(hdrae); + break; + case RKMODULE_SET_QUICK_STREAM: + ret = copy_from_user(&stream, up, sizeof(u32)); + if (!ret) + ret = imx708_ioctl(sd, cmd, &stream); + else + ret = -EFAULT; + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); + if (!ch_info) { + ret = -ENOMEM; + return ret; + } + ret = imx708_ioctl(sd, cmd, ch_info); + if (!ret) { + ret = copy_to_user(up, ch_info, sizeof(*ch_info)); + if (ret) + ret = -EFAULT; + } + kfree(ch_info); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int imx708_set_flip(struct imx708 *imx708) +{ + int ret = 0; + u32 val = 0; + + ret = imx708_read_reg(imx708->client, IMX708_FLIP_MIRROR_REG, + IMX708_REG_VALUE_08BIT, &val); + if (imx708->flip & IMX708_MIRROR_BIT_MASK) + val |= IMX708_MIRROR_BIT_MASK; + else + val &= ~IMX708_MIRROR_BIT_MASK; + if (imx708->flip & IMX708_FLIP_BIT_MASK) + val |= IMX708_FLIP_BIT_MASK; + else + val &= ~IMX708_FLIP_BIT_MASK; + ret |= imx708_write_reg(imx708->client, IMX708_FLIP_MIRROR_REG, + IMX708_REG_VALUE_08BIT, val); + + return ret; +} + +static int __imx708_start_stream(struct imx708 *imx708) +{ + int ret; + + ret = imx708_write_array(imx708->client, imx708->cur_mode->global_reg_list); + if (ret) + return ret; + + ret = imx708_write_array(imx708->client, imx708->cur_mode->reg_list); + if (ret) + return ret; + imx708->cur_vts = imx708->cur_mode->vts_def; + /* In case these controls are set before streaming */ + ret = __v4l2_ctrl_handler_setup(&imx708->ctrl_handler); + if (ret) + return ret; + if (imx708->has_init_exp && imx708->cur_mode->hdr_mode != NO_HDR) { + ret = imx708_ioctl(&imx708->subdev, PREISP_CMD_SET_HDRAE_EXP, + &imx708->init_hdrae_exp); + if (ret) { + dev_err(&imx708->client->dev, + "init exp fail in hdr mode\n"); + return ret; + } + } + + imx708_set_flip(imx708); + + return imx708_write_reg(imx708->client, IMX708_REG_CTRL_MODE, + IMX708_REG_VALUE_08BIT, IMX708_MODE_STREAMING); +} + +static int __imx708_stop_stream(struct imx708 *imx708) +{ + return imx708_write_reg(imx708->client, IMX708_REG_CTRL_MODE, + IMX708_REG_VALUE_08BIT, IMX708_MODE_SW_STANDBY); +} + +static int imx708_s_stream(struct v4l2_subdev *sd, int on) +{ + struct imx708 *imx708 = to_imx708(sd); + struct i2c_client *client = imx708->client; + int ret = 0; + + dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on, + imx708->cur_mode->width, + imx708->cur_mode->height, + DIV_ROUND_CLOSEST(imx708->cur_mode->max_fps.denominator, + imx708->cur_mode->max_fps.numerator)); + + mutex_lock(&imx708->mutex); + on = !!on; + if (on == imx708->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __imx708_start_stream(imx708); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __imx708_stop_stream(imx708); + pm_runtime_put(&client->dev); + } + + imx708->streaming = on; + +unlock_and_return: + mutex_unlock(&imx708->mutex); + + return ret; +} + +static int imx708_s_power(struct v4l2_subdev *sd, int on) +{ + struct imx708 *imx708 = to_imx708(sd); + struct i2c_client *client = imx708->client; + int ret = 0; + + mutex_lock(&imx708->mutex); + + /* If the power state is not modified - no work to do. */ + if (imx708->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + imx708->power_on = true; + } else { + pm_runtime_put(&client->dev); + imx708->power_on = false; + } + +unlock_and_return: + mutex_unlock(&imx708->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 imx708_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, IMX708_XVCLK_FREQ / 1000 / 1000); +} + +static int __imx708_power_on(struct imx708 *imx708) +{ + int ret; + u32 delay_us; + struct device *dev = &imx708->client->dev; + + ret = clk_set_rate(imx708->xvclk, IMX708_XVCLK_FREQ); + if (ret < 0) { + dev_err(dev, "Failed to set xvclk rate (24MHz)\n"); + return ret; + } + if (clk_get_rate(imx708->xvclk) != IMX708_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 37.125MHz\n"); + ret = clk_prepare_enable(imx708->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + if (!IS_ERR(imx708->reset_gpio)) + gpiod_set_value_cansleep(imx708->reset_gpio, 0); + + ret = regulator_bulk_enable(IMX708_NUM_SUPPLIES, imx708->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(imx708->reset_gpio)) + gpiod_set_value_cansleep(imx708->reset_gpio, 1); + + /* need wait 8ms to set register */ + usleep_range(8000, 10000); + + if (!IS_ERR(imx708->pwdn_gpio)) + gpiod_set_value_cansleep(imx708->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = imx708_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(imx708->xvclk); + + return ret; +} + +static void __imx708_power_off(struct imx708 *imx708) +{ + + if (!IS_ERR(imx708->pwdn_gpio)) + gpiod_set_value_cansleep(imx708->pwdn_gpio, 0); + clk_disable_unprepare(imx708->xvclk); + if (!IS_ERR(imx708->reset_gpio)) + gpiod_set_value_cansleep(imx708->reset_gpio, 0); + regulator_bulk_disable(IMX708_NUM_SUPPLIES, imx708->supplies); +} + +static int imx708_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx708 *imx708 = to_imx708(sd); + + return __imx708_power_on(imx708); +} + +static int imx708_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx708 *imx708 = to_imx708(sd); + + __imx708_power_off(imx708); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int imx708_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct imx708 *imx708 = to_imx708(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct imx708_mode *def_mode = &supported_modes[0]; + + mutex_lock(&imx708->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = def_mode->bus_fmt; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&imx708->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int imx708_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct imx708 *imx708 = to_imx708(sd); + + if (fie->index >= imx708->cfg_num) + return -EINVAL; + + fie->code = supported_modes[fie->index].bus_fmt; + fie->width = supported_modes[fie->index].width; + fie->height = supported_modes[fie->index].height; + fie->interval = supported_modes[fie->index].max_fps; + fie->reserved[0] = supported_modes[fie->index].hdr_mode; + return 0; +} + +static const struct dev_pm_ops imx708_pm_ops = { + SET_RUNTIME_PM_OPS(imx708_runtime_suspend, + imx708_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops imx708_internal_ops = { + .open = imx708_open, +}; +#endif + +static const struct v4l2_subdev_core_ops imx708_core_ops = { + .s_power = imx708_s_power, + .ioctl = imx708_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = imx708_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops imx708_video_ops = { + .s_stream = imx708_s_stream, + .g_frame_interval = imx708_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops imx708_pad_ops = { + .enum_mbus_code = imx708_enum_mbus_code, + .enum_frame_size = imx708_enum_frame_sizes, + .enum_frame_interval = imx708_enum_frame_interval, + .get_fmt = imx708_get_fmt, + .set_fmt = imx708_set_fmt, + .get_mbus_config = imx708_g_mbus_config, +}; + +static const struct v4l2_subdev_ops imx708_subdev_ops = { + .core = &imx708_core_ops, + .video = &imx708_video_ops, + .pad = &imx708_pad_ops, +}; + +static int imx708_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx708 *imx708 = container_of(ctrl->handler, + struct imx708, ctrl_handler); + struct i2c_client *client = imx708->client; + s64 max; + int ret = 0; + u32 again = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = imx708->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(imx708->exposure, + imx708->exposure->minimum, max, + imx708->exposure->step, + imx708->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + /* 4 least significant bits of expsoure are fractional part */ + ret = imx708_write_reg(imx708->client, + IMX708_REG_EXPOSURE_H, + IMX708_REG_VALUE_08BIT, + IMX708_FETCH_EXP_H(ctrl->val)); + ret |= imx708_write_reg(imx708->client, + IMX708_REG_EXPOSURE_L, + IMX708_REG_VALUE_08BIT, + IMX708_FETCH_EXP_L(ctrl->val)); + dev_dbg(&client->dev, "set exposure 0x%x\n", + ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + /* gain_reg = 1024 - 1024 / gain_ana + * manual multiple 16 to add accuracy: + * then formula change to: + * gain_reg = 1024 - 1024 * 16 / (gain_ana * 16) + */ + if (ctrl->val > 0x400) + ctrl->val = 0x400; + if (ctrl->val < 0x10) + ctrl->val = 0x10; + + again = 1024 - 1024 * 16 / ctrl->val; + ret = imx708_write_reg(imx708->client, IMX708_REG_GAIN_H, + IMX708_REG_VALUE_08BIT, + IMX708_FETCH_AGAIN_H(again)); + ret |= imx708_write_reg(imx708->client, IMX708_REG_GAIN_L, + IMX708_REG_VALUE_08BIT, + IMX708_FETCH_AGAIN_L(again)); + + dev_dbg(&client->dev, "set analog gain 0x%x\n", + ctrl->val); + break; + case V4L2_CID_VBLANK: + ret = imx708_write_reg(imx708->client, + IMX708_REG_VTS_H, + IMX708_REG_VALUE_08BIT, + (ctrl->val + imx708->cur_mode->height) + >> 8); + ret |= imx708_write_reg(imx708->client, + IMX708_REG_VTS_L, + IMX708_REG_VALUE_08BIT, + (ctrl->val + imx708->cur_mode->height) + & 0xff); + imx708->cur_vts = ctrl->val + imx708->cur_mode->height; + + dev_dbg(&client->dev, "set vblank 0x%x\n", + ctrl->val); + break; + case V4L2_CID_HFLIP: + if (ctrl->val) + imx708->flip |= IMX708_MIRROR_BIT_MASK; + else + imx708->flip &= ~IMX708_MIRROR_BIT_MASK; + dev_dbg(&client->dev, "set hflip 0x%x\n", + ctrl->val); + break; + case V4L2_CID_VFLIP: + if (ctrl->val) + imx708->flip |= IMX708_FLIP_BIT_MASK; + else + imx708->flip &= ~IMX708_FLIP_BIT_MASK; + dev_dbg(&client->dev, "set vflip 0x%x\n", + ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + dev_dbg(&client->dev, "set testpattern 0x%x\n", + ctrl->val); + ret = imx708_enable_test_pattern(imx708, ctrl->val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx708_ctrl_ops = { + .s_ctrl = imx708_set_ctrl, +}; + +static int imx708_initialize_controls(struct imx708 *imx708) +{ + const struct imx708_mode *mode; + struct v4l2_ctrl_handler *handler; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + + handler = &imx708->ctrl_handler; + mode = imx708->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 9); + if (ret) + return ret; + handler->lock = &imx708->mutex; + + imx708->link_freq = v4l2_ctrl_new_int_menu(handler, NULL, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_items) - 1, 0, + link_freq_items); + + if (imx708->cur_mode->bus_fmt == MEDIA_BUS_FMT_SRGGB10_1X10) { + imx708->cur_link_freq = 0; + imx708->cur_pixel_rate = PIXEL_RATE_WITH_848M_10BIT; + } else if (imx708->cur_mode->bus_fmt == MEDIA_BUS_FMT_SRGGB12_1X12) { + imx708->cur_link_freq = 0; + imx708->cur_pixel_rate = PIXEL_RATE_WITH_848M_12BIT; + } + + imx708->pixel_rate = v4l2_ctrl_new_std(handler, NULL, + V4L2_CID_PIXEL_RATE, + 0, PIXEL_RATE_WITH_848M_10BIT, + 1, imx708->cur_pixel_rate); + v4l2_ctrl_s_ctrl(imx708->link_freq, + imx708->cur_link_freq); + + h_blank = mode->hts_def - mode->width; + imx708->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (imx708->hblank) + imx708->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + imx708->vblank = v4l2_ctrl_new_std(handler, &imx708_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + IMX708_VTS_MAX - mode->height, + 1, vblank_def); + imx708->cur_vts = mode->vts_def; + exposure_max = mode->vts_def - 4; + imx708->exposure = v4l2_ctrl_new_std(handler, &imx708_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX708_EXPOSURE_MIN, + exposure_max, + IMX708_EXPOSURE_STEP, + mode->exp_def); + imx708->anal_gain = v4l2_ctrl_new_std(handler, &imx708_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + IMX708_GAIN_MIN, + IMX708_GAIN_MAX, + IMX708_GAIN_STEP, + IMX708_GAIN_DEFAULT); + imx708->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &imx708_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx708_test_pattern_menu) - 1, + 0, 0, imx708_test_pattern_menu); + + imx708->h_flip = v4l2_ctrl_new_std(handler, &imx708_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + imx708->v_flip = v4l2_ctrl_new_std(handler, &imx708_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + imx708->flip = 0; + + if (handler->error) { + ret = handler->error; + dev_err(&imx708->client->dev, + "Failed to init controls( %d )\n", ret); + goto err_free_handler; + } + + imx708->subdev.ctrl_handler = handler; + imx708->has_init_exp = false; + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int imx708_check_sensor_id(struct imx708 *imx708, + struct i2c_client *client) +{ + struct device *dev = &imx708->client->dev; + u16 id = 0; + u32 reg_H = 0; + u32 reg_L = 0; + int ret; + + ret = imx708_read_reg(client, IMX708_REG_CHIP_ID_H, + IMX708_REG_VALUE_08BIT, ®_H); + ret |= imx708_read_reg(client, IMX708_REG_CHIP_ID_L, + IMX708_REG_VALUE_08BIT, ®_L); + id = ((reg_H << 8) & 0xff00) | (reg_L & 0xff); + if (!(reg_H == (CHIP_ID >> 8) || reg_L == (CHIP_ID & 0xff))) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + dev_info(dev, "detected imx708 %04x sensor\n", id); + return 0; +} + +static int imx708_configure_regulators(struct imx708 *imx708) +{ + unsigned int i; + + for (i = 0; i < IMX708_NUM_SUPPLIES; i++) + imx708->supplies[i].supply = imx708_supply_names[i]; + + return devm_regulator_bulk_get(&imx708->client->dev, + IMX708_NUM_SUPPLIES, + imx708->supplies); +} + +static int imx708_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct imx708 *imx708; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + u32 i, hdr_mode = 0; + struct device_node *eeprom_ctrl_node; + struct i2c_client *eeprom_ctrl_client; + struct v4l2_subdev *eeprom_ctrl; + struct otp_info *otp_ptr; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + imx708 = devm_kzalloc(dev, sizeof(*imx708), GFP_KERNEL); + if (!imx708) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &imx708->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &imx708->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &imx708->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &imx708->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + ret = of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode); + if (ret) { + hdr_mode = NO_HDR; + dev_warn(dev, " Get hdr mode failed! no hdr default\n"); + } + + imx708->client = client; + imx708->cfg_num = ARRAY_SIZE(supported_modes); + for (i = 0; i < imx708->cfg_num; i++) { + if (hdr_mode == supported_modes[i].hdr_mode) { + imx708->cur_mode = &supported_modes[i]; + break; + } + } + + if (i == imx708->cfg_num) + imx708->cur_mode = &supported_modes[0]; + + imx708->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(imx708->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + imx708->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(imx708->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + imx708->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(imx708->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = of_property_read_u32(node, + "rockchip,spd-id", + &imx708->spd_id); + if (ret != 0) { + imx708->spd_id = PAD_MAX; + dev_err(dev, + "failed get spd_id, will not to use spd\n"); + } + + ret = imx708_configure_regulators(imx708); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&imx708->mutex); + + sd = &imx708->subdev; + v4l2_i2c_subdev_init(sd, client, &imx708_subdev_ops); + + ret = imx708_initialize_controls(imx708); + if (ret) + goto err_destroy_mutex; + + ret = __imx708_power_on(imx708); + if (ret) + goto err_free_handler; + + ret = imx708_check_sensor_id(imx708, client); + if (ret) + goto err_power_off; + eeprom_ctrl_node = of_parse_phandle(node, "eeprom-ctrl", 0); + if (eeprom_ctrl_node) { + eeprom_ctrl_client = + of_find_i2c_device_by_node(eeprom_ctrl_node); + of_node_put(eeprom_ctrl_node); + if (IS_ERR_OR_NULL(eeprom_ctrl_client)) { + dev_err(dev, "can not get node\n"); + goto continue_probe; + } + eeprom_ctrl = i2c_get_clientdata(eeprom_ctrl_client); + if (IS_ERR_OR_NULL(eeprom_ctrl)) { + dev_err(dev, "can not get eeprom i2c client\n"); + } else { + otp_ptr = devm_kzalloc(dev, sizeof(*otp_ptr), GFP_KERNEL); + if (!otp_ptr) + return -ENOMEM; + ret = v4l2_subdev_call(eeprom_ctrl, + core, ioctl, 0, otp_ptr); + if (!ret) { + imx708->otp = otp_ptr; + } else { + imx708->otp = NULL; + devm_kfree(dev, otp_ptr); + } + } + } +continue_probe: + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &imx708_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + imx708->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &imx708->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(imx708->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + imx708->module_index, facing, + IMX708_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __imx708_power_off(imx708); +err_free_handler: + v4l2_ctrl_handler_free(&imx708->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&imx708->mutex); + + return ret; +} + +static int imx708_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx708 *imx708 = to_imx708(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&imx708->ctrl_handler); + mutex_destroy(&imx708->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __imx708_power_off(imx708); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id imx708_of_match[] = { + { .compatible = "sony,imx708" }, + {}, +}; +MODULE_DEVICE_TABLE(of, imx708_of_match); +#endif + +static const struct i2c_device_id imx708_match_id[] = { + { "sony,imx708", 0 }, + { }, +}; + +static struct i2c_driver imx708_i2c_driver = { + .driver = { + .name = IMX708_NAME, + .pm = &imx708_pm_ops, + .of_match_table = of_match_ptr(imx708_of_match), + }, + .probe = &imx708_probe, + .remove = &imx708_remove, + .id_table = imx708_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&imx708_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&imx708_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("Sony imx708 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov50h40.c b/drivers/media/i2c/ov50h40.c new file mode 100644 index 0000000000000..afedc3ea5fde3 --- /dev/null +++ b/drivers/media/i2c/ov50h40.c @@ -0,0 +1,3776 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ov50h40 driver + * + * Copyright (C) 2020 Fuzhou Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X00 first version. + * V0.0X01.0X01 support conversion gain switch. + * V0.0X01.0X02 add debug interface for conversion gain switch. + * V0.0X01.0X03 support enum sensor fmt + * V0.0X01.0X04 add quick stream on/off + * V0.0X01.0X05 support get dcg ratio from sensor + * V0.0X01.0X06 + * 1. fix 8K@12 mipi freq index. + * 2. fix set_fmt & ioctl get mode unmatched issue. + * 3. add debug info. + * V0.0X01.0X07 correct bayer pattern to match register setting + * V0.0X01.0X08 adjust some config for cts. + * 1. only enable 8K@12fps & 4K@30fps setting for use, other for debug. + */ +//#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../platform/rockchip/isp/rkisp_tb_helper.h" +#include +#include "otp_eeprom.h" + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x08) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define MIPI_FREQ_356M 356000000 +#define MIPI_FREQ_384M 384000000 +#define MIPI_FREQ_750M 750000000 +#define MIPI_FREQ_1250M 1250000000 + +#define PIXEL_RATE_WITH_1250M (MIPI_FREQ_1250M / 10 * 4 * 2) + +#define OF_CAMERA_HDR_MODE "rockchip,camera-hdr-mode" + +#define OV50H40_XVCLK_FREQ 19200000 + +#define CHIP_ID 0x564041 +#define OV50H40_REG_CHIP_ID 0x300a + +#define OV50H40_REG_CTRL_MODE 0x0100 +#define OV50H40_MODE_SW_STANDBY 0x0 +#define OV50H40_MODE_STREAMING BIT(0) + +#define OV50H40_EXPOSURE_MIN 4 +#define OV50H40_EXPOSURE_STEP 1 +#define OV50H40_VTS_MAX 0xffff + +#define OV50H40_REG_EXP_LONG_H 0x3500 + +#define OV50H40_REG_AGAIN_LONG_H 0x3508 +#define OV50H40_REG_DGAIN_LONG_H 0x350A +#define OV50H40_GAIN_MIN 0x80 +#define OV50H40_GAIN_MAX 0x7C00 +#define OV50H40_GAIN_STEP 1 +#define OV50H40_GAIN_DEFAULT 0x80 + +#define OV50H40_GROUP_UPDATE_ADDRESS 0x3208 +#define OV50H40_GROUP_UPDATE_START_DATA 0x00 +#define OV50H40_GROUP_UPDATE_END_DATA 0x10 +#define OV50H40_GROUP_UPDATE_END_LAUNCH 0xA0 + +#define OV50H40_SOFTWARE_RESET_REG 0x0103 + +#define OV50H40_FETCH_MSB_BYTE_EXP(VAL) (((VAL) >> 8) & 0xFF) /* 8 Bits */ +#define OV50H40_FETCH_LSB_BYTE_EXP(VAL) ((VAL) & 0xFF) /* 8 Bits */ + +#define OV50H40_FETCH_LSB_GAIN(VAL) (((VAL) << 4) & 0xf0) +#define OV50H40_FETCH_MSB_GAIN(VAL) (((VAL) >> 4) & 0x1f) + +#define OV50H40_REG_TEST_PATTERN 0x50C1 +#define OV50H40_TEST_PATTERN_ENABLE 0x01 +#define OV50H40_TEST_PATTERN_DISABLE 0x0 + +#define OV50H40_REG_VTS 0x380e + +#define REG_NULL 0xFFFF + +#define OV50H40_REG_VALUE_08BIT 1 +#define OV50H40_REG_VALUE_16BIT 2 +#define OV50H40_REG_VALUE_24BIT 3 + +#define OV50H40_LANES 3 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define OV50H40_NAME "ov50h40" + +static const char * const ov50h40_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define OV50H40_NUM_SUPPLIES ARRAY_SIZE(ov50h40_supply_names) + +#define OV50H40_FLIP_REG 0x3820 +#define OV50H40_MIRROR_REG 0x3821 +#define FLIP_BIT_MASK BIT(2) + +struct regval { + u16 addr; + u8 val; +}; + +struct other_data { + u32 width; + u32 height; + u32 bus_fmt; + u32 data_type; + u32 data_bit; +}; + +struct ov50h40_mode { + u32 bus_fmt; + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + u32 mipi_freq_idx; + u32 bpp; + const struct regval *reg_list; + u32 hdr_mode; + const struct other_data *spd; + u32 vc[PAD_MAX]; +}; + +struct ov50h40 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[OV50H40_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *h_flip; + struct v4l2_ctrl *v_flip; + struct mutex mutex; + bool streaming; + bool power_on; + const struct ov50h40_mode *cur_mode; + const struct ov50h40_mode *support_modes; + u32 cfg_num; + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + struct v4l2_fwnode_endpoint bus_cfg; + bool is_thunderboot; + bool is_thunderboot_ng; + bool is_first_streamoff; + struct otp_info *otp; + u32 spd_id; +}; + +#define to_ov50h40(sd) container_of(sd, struct ov50h40, subdev) + +static const struct regval ov50h40_10bit_4096x3072_dphy_30fps_regs[] = { + {REG_NULL, 0x00}, +}; + +static const struct regval ov50h40_10bit_8192x6144_dphy_12fps_regs[] = { + {REG_NULL, 0x00}, +}; + +static const struct regval ov50h40_10bit_4096x3072_cphy_regs[] = { + {0x0103, 0x01}, + {0x0102, 0x01}, + {0x6a03, 0x00}, + {0x0304, 0x02}, + {0x0305, 0xd0}, + {0x0306, 0x03}, + {0x0307, 0x00}, + {0x0308, 0x03}, + {0x0323, 0x12}, + {0x0324, 0x02}, + {0x0325, 0x58}, + {0x0327, 0x09}, + {0x0328, 0x9f}, + {0x0329, 0x01}, + {0x032a, 0x0f}, + {0x032b, 0x09}, + {0x032c, 0x00}, + {0x032e, 0x01}, + {0x0343, 0x02}, + {0x0344, 0x01}, + {0x0345, 0x20}, + {0x0346, 0xdf}, + {0x0347, 0x0f}, + {0x0348, 0x7f}, + {0x0349, 0x0f}, + {0x034a, 0x03}, + {0x034b, 0x02}, + {0x034c, 0x03}, + {0x034d, 0x01}, + {0x034e, 0x01}, + {0x0360, 0x09}, + {0x300d, 0x11}, + {0x300d, 0x11}, + {0x300e, 0x11}, + {0x3012, 0x31}, + {0x3014, 0xf0}, + {0x3015, 0x00}, + {0x3016, 0xf0}, + {0x3017, 0xf0}, + {0x301c, 0x01}, + {0x301d, 0x02}, + {0x301f, 0x98}, + {0x3020, 0x01}, + {0x3025, 0x03}, + {0x3026, 0x80}, + {0x3027, 0x00}, + {0x302c, 0x01}, + {0x302d, 0x00}, + {0x302e, 0x00}, + {0x302f, 0x00}, + {0x3030, 0x03}, + {0x3031, 0x00}, + {0x3044, 0xc2}, + {0x3047, 0x07}, + {0x3102, 0x0d}, + {0x3106, 0x80}, + {0x3400, 0x0c}, + {0x3401, 0x00}, + {0x3406, 0x08}, + {0x3407, 0x08}, + {0x3408, 0x08}, + {0x3409, 0x02}, + {0x340a, 0x03}, + {0x340e, 0x60}, + {0x3420, 0x03}, + {0x3421, 0x08}, + {0x3422, 0x08}, + {0x3423, 0x00}, + {0x3426, 0x15}, + {0x342b, 0x40}, + {0x342c, 0x15}, + {0x342d, 0x01}, + {0x342e, 0x00}, + {0x3500, 0x00}, + {0x3501, 0x00}, + {0x3502, 0x40}, + {0x3504, 0x4c}, + {0x3506, 0x78}, + {0x3507, 0x00}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {0x350a, 0x01}, + {0x350b, 0x00}, + {0x350c, 0x00}, + {0x350d, 0x01}, + {0x350e, 0x00}, + {0x350f, 0x00}, + {0x3519, 0x01}, + {0x351a, 0x71}, + {0x351b, 0x40}, + {0x3540, 0x00}, + {0x3541, 0x00}, + {0x3542, 0x30}, + {0x3544, 0x4c}, + {0x3546, 0x78}, + {0x3548, 0x01}, + {0x3549, 0x00}, + {0x354a, 0x01}, + {0x354b, 0x00}, + {0x354d, 0x01}, + {0x354e, 0x00}, + {0x354f, 0x00}, + {0x3559, 0x01}, + {0x355a, 0x71}, + {0x355b, 0x40}, + {0x3580, 0x00}, + {0x3581, 0x00}, + {0x3582, 0x20}, + {0x3584, 0x4c}, + {0x3586, 0x78}, + {0x3588, 0x01}, + {0x3589, 0x00}, + {0x358a, 0x01}, + {0x358b, 0x00}, + {0x358d, 0x01}, + {0x358e, 0x00}, + {0x358f, 0x00}, + {0x3599, 0x01}, + {0x359a, 0x71}, + {0x359b, 0x40}, + {0x3600, 0xe4}, + {0x3602, 0xe4}, + {0x3603, 0x80}, + {0x3605, 0x38}, + {0x3607, 0x10}, + {0x3608, 0x30}, + {0x3609, 0x80}, + {0x360a, 0xfa}, + {0x360b, 0xc7}, + {0x360c, 0x0f}, + {0x360d, 0xf4}, + {0x360e, 0x2b}, + {0x3610, 0x08}, + {0x3612, 0x00}, + {0x3614, 0x0c}, + {0x3616, 0x8c}, + {0x3617, 0x0d}, + {0x3618, 0xcf}, + {0x3619, 0x44}, + {0x361a, 0x81}, + {0x361b, 0x04}, + {0x361d, 0x1f}, + {0x3622, 0x00}, + {0x3627, 0xa0}, + {0x363b, 0x6a}, + {0x363c, 0x6a}, + {0x3640, 0x00}, + {0x3641, 0x02}, + {0x3643, 0x01}, + {0x3644, 0x00}, + {0x3645, 0x06}, + {0x3646, 0x40}, + {0x3647, 0x01}, + {0x3648, 0x8e}, + {0x364d, 0x10}, + {0x3650, 0xbf}, + {0x3651, 0x00}, + {0x3653, 0x03}, + {0x3657, 0x40}, + {0x3680, 0x00}, + {0x3682, 0x80}, + {0x3683, 0x00}, + {0x3684, 0x01}, + {0x3685, 0x04}, + {0x3688, 0x00}, + {0x3689, 0x88}, + {0x368a, 0x0e}, + {0x368b, 0xef}, + {0x368d, 0x00}, + {0x368e, 0x70}, + {0x3696, 0x41}, + {0x369a, 0x00}, + {0x369f, 0x20}, + {0x36a4, 0x00}, + {0x36a5, 0x00}, + {0x36d0, 0x00}, + {0x36d3, 0x80}, + {0x36d4, 0x00}, + {0x3700, 0x1c}, + {0x3701, 0x13}, + {0x3702, 0x30}, + {0x3703, 0x34}, + {0x3704, 0x03}, + {0x3706, 0x1c}, + {0x3707, 0x04}, + {0x3708, 0x25}, + {0x3709, 0x70}, + {0x370b, 0x3a}, + {0x370c, 0x04}, + {0x3712, 0x01}, + {0x3714, 0xf8}, + {0x3715, 0x00}, + {0x3716, 0x40}, + {0x3720, 0x0b}, + {0x3722, 0x05}, + {0x3724, 0x12}, + {0x372b, 0x00}, + {0x372e, 0x1c}, + {0x372f, 0x13}, + {0x3733, 0x00}, + {0x3735, 0x00}, + {0x373f, 0x00}, + {0x374b, 0x04}, + {0x374c, 0x0c}, + {0x374f, 0x58}, + {0x3754, 0x30}, + {0x3755, 0xb1}, + {0x3756, 0x00}, + {0x3757, 0x30}, + {0x3758, 0x00}, + {0x3759, 0x50}, + {0x375e, 0x00}, + {0x375f, 0x00}, + {0x3760, 0x10}, + {0x3761, 0x30}, + {0x3762, 0x10}, + {0x3763, 0x10}, + {0x3765, 0x20}, + {0x3766, 0x30}, + {0x3767, 0x20}, + {0x3768, 0x00}, + {0x3769, 0x10}, + {0x376a, 0x10}, + {0x376c, 0x00}, + {0x376e, 0x00}, + {0x3770, 0x01}, + {0x3780, 0x5c}, + {0x3782, 0x01}, + {0x378a, 0x01}, + {0x3791, 0x30}, + {0x3793, 0x1c}, + {0x3795, 0x1c}, + {0x3797, 0x8e}, + {0x3799, 0x3a}, + {0x379b, 0x3a}, + {0x379c, 0x01}, + {0x379d, 0x01}, + {0x379f, 0x01}, + {0x37a0, 0x70}, + {0x37a9, 0x01}, + {0x37b2, 0xc8}, + {0x37b7, 0x02}, + {0x37bd, 0x00}, + {0x37c1, 0x1a}, + {0x37c3, 0x1a}, + {0x37ca, 0xc4}, + {0x37cb, 0x02}, + {0x37cc, 0x51}, + {0x37cd, 0x01}, + {0x37d0, 0x00}, + {0x37d4, 0x00}, + {0x37d8, 0x00}, + {0x37d9, 0x08}, + {0x37da, 0x14}, + {0x37db, 0x10}, + {0x37dc, 0x1a}, + {0x37dd, 0x86}, + {0x37e0, 0x68}, + {0x37e3, 0x30}, + {0x37e4, 0xf6}, + {0x37f0, 0x01}, + {0x37f1, 0xe0}, + {0x37f2, 0x24}, + {0x37f6, 0x1a}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x20}, + {0x3805, 0x1f}, + {0x3806, 0x18}, + {0x3807, 0x3f}, + {0x3808, 0x20}, + {0x3809, 0x00}, + {0x380a, 0x18}, + {0x380b, 0x00}, + {0x380c, 0x03}, + {0x380d, 0x00}, + {0x380e, 0x0c}, + {0x380f, 0x80}, + {0x3810, 0x00}, + {0x3811, 0x0f}, + {0x3812, 0x00}, + {0x3813, 0x20}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x381a, 0x0c}, + {0x381b, 0x70}, + {0x381c, 0x01}, + {0x381d, 0x80}, + {0x381f, 0x00}, + {0x3820, 0x40}, + {0x3821, 0x04}, + {0x3822, 0x00}, + {0x3823, 0x04}, + {0x3827, 0x40}, + {0x3828, 0x27}, + {0x382a, 0x80}, + {0x382e, 0x49}, + {0x3830, 0x20}, + {0x3831, 0x10}, + {0x3837, 0x20}, + {0x383f, 0x08}, + {0x3840, 0x00}, + {0x3847, 0x00}, + {0x384a, 0x00}, + {0x384c, 0x03}, + {0x384d, 0x00}, + {0x3858, 0x00}, + {0x3860, 0x00}, + {0x3867, 0x11}, + {0x386a, 0x00}, + {0x386b, 0x00}, + {0x386c, 0x00}, + {0x386d, 0x7c}, + {0x3888, 0x00}, + {0x3889, 0x10}, + {0x388a, 0x00}, + {0x388b, 0x20}, + {0x388c, 0x20}, + {0x388d, 0x00}, + {0x388e, 0x18}, + {0x388f, 0x00}, + {0x3890, 0x11}, + {0x3894, 0x02}, + {0x3895, 0x80}, + {0x3896, 0x00}, + {0x3899, 0x00}, + {0x38a0, 0x00}, + {0x38a1, 0x1d}, + {0x38a2, 0x98}, + {0x38a3, 0x00}, + {0x38a4, 0x1d}, + {0x38a5, 0x98}, + {0x38ac, 0x40}, + {0x38ad, 0x00}, + {0x38ae, 0x00}, + {0x38af, 0x00}, + {0x38b0, 0x00}, + {0x38b1, 0x00}, + {0x38b2, 0x00}, + {0x38b3, 0x00}, + {0x38b4, 0x20}, + {0x38b5, 0x1f}, + {0x38b6, 0x18}, + {0x38b7, 0x1f}, + {0x38b8, 0x20}, + {0x38b9, 0x00}, + {0x38ba, 0x18}, + {0x38bb, 0x00}, + {0x38bc, 0x00}, + {0x38bd, 0x10}, + {0x38be, 0x00}, + {0x38bf, 0x10}, + {0x38c0, 0x11}, + {0x38c1, 0x11}, + {0x38c2, 0x00}, + {0x38c3, 0x00}, + {0x38c4, 0x00}, + {0x38c5, 0x00}, + {0x38c6, 0x11}, + {0x38c7, 0x00}, + {0x38c8, 0x11}, + {0x38c9, 0x00}, + {0x38ca, 0x11}, + {0x38cb, 0x00}, + {0x38cc, 0x11}, + {0x38cd, 0x00}, + {0x38ce, 0x11}, + {0x38cf, 0x00}, + {0x38d1, 0x11}, + {0x38d2, 0x00}, + {0x38d3, 0x00}, + {0x38d4, 0x08}, + {0x38d5, 0x00}, + {0x38d6, 0x08}, + {0x38db, 0x20}, + {0x38dd, 0x10}, + {0x38de, 0x0c}, + {0x38df, 0x20}, + {0x38e0, 0x00}, + {0x38f3, 0x00}, + {0x3900, 0x40}, + {0x3906, 0x24}, + {0x3907, 0x00}, + {0x390a, 0x05}, + {0x3913, 0x0c}, + {0x3918, 0x00}, + {0x3919, 0x15}, + {0x395b, 0x05}, + {0x3982, 0x40}, + {0x398b, 0x00}, + {0x3994, 0x0b}, + {0x3995, 0x30}, + {0x399d, 0x05}, + {0x39a0, 0x0b}, + {0x39dc, 0x01}, + {0x39fb, 0x01}, + {0x39fc, 0x01}, + {0x39fd, 0x06}, + {0x39fe, 0x06}, + {0x3a1d, 0x01}, + {0x3a1e, 0x01}, + {0x3a1f, 0x03}, + {0x3a21, 0x01}, + {0x3a22, 0x06}, + {0x3a23, 0x03}, + {0x3a68, 0x05}, + {0x3a69, 0x20}, + {0x3a6d, 0x50}, + {0x3a78, 0x03}, + {0x3a79, 0x03}, + {0x3a7c, 0x04}, + {0x3a7d, 0x04}, + {0x3a94, 0x04}, + {0x3ab5, 0x00}, + {0x3ab6, 0x01}, + {0x3ab7, 0x01}, + {0x3ab8, 0x01}, + {0x3ab9, 0x01}, + {0x3af2, 0x03}, + {0x3b01, 0x00}, + {0x3b02, 0x00}, + {0x3b16, 0x00}, + {0x3b3d, 0x07}, + {0x3b4a, 0x38}, + {0x3b4b, 0x38}, + {0x3b56, 0x20}, + {0x3b57, 0x21}, + {0x3b58, 0x21}, + {0x3b59, 0x21}, + {0x3b5a, 0x14}, + {0x3b5b, 0x14}, + {0x3b5c, 0x14}, + {0x3b5d, 0x14}, + {0x3b82, 0x14}, + {0x3ba1, 0x20}, + {0x3ba4, 0x77}, + {0x3ba5, 0x77}, + {0x3ba6, 0x00}, + {0x3ba7, 0x00}, + {0x3baa, 0x33}, + {0x3bab, 0x37}, + {0x3bac, 0x77}, + {0x3baf, 0x00}, + {0x3bba, 0x4c}, + {0x3bde, 0x01}, + {0x3be0, 0x30}, + {0x3be7, 0x08}, + {0x3be8, 0x0f}, + {0x3beb, 0x00}, + {0x3bf2, 0x03}, + {0x3bf3, 0x01}, + {0x3bf4, 0x50}, + {0x3bfb, 0x01}, + {0x3bfc, 0x50}, + {0x3bff, 0x08}, + {0x3d84, 0x00}, + {0x3d85, 0x0b}, + {0x3d8c, 0x9b}, + {0x3d8d, 0xa0}, + {0x3daa, 0x00}, + {0x3dab, 0x00}, + {0x3f00, 0x10}, + {0x4008, 0x00}, + {0x4009, 0x02}, + {0x400e, 0x14}, + {0x4010, 0x34}, + {0x4011, 0x01}, + {0x4012, 0x17}, + {0x4015, 0x00}, + {0x4016, 0x1f}, + {0x4017, 0x00}, + {0x4018, 0x0f}, + {0x401a, 0x40}, + {0x401b, 0x04}, + {0x40f8, 0x04}, + {0x40f9, 0x00}, + {0x40fa, 0x02}, + {0x40fb, 0x00}, + {0x4100, 0x00}, + {0x4101, 0x00}, + {0x4102, 0x00}, + {0x4103, 0x00}, + {0x4105, 0x00}, + {0x4288, 0x27}, + {0x4504, 0x80}, + {0x4505, 0x0c}, + {0x4506, 0x01}, + {0x4509, 0x07}, + {0x450c, 0x00}, + {0x450d, 0x30}, + {0x450e, 0x00}, + {0x450f, 0x20}, + {0x4510, 0x00}, + {0x4511, 0x00}, + {0x4512, 0x00}, + {0x4513, 0x00}, + {0x4514, 0x00}, + {0x4515, 0x00}, + {0x4516, 0x00}, + {0x4517, 0x00}, + {0x4518, 0x00}, + {0x4519, 0x00}, + {0x451a, 0x00}, + {0x451b, 0x00}, + {0x451c, 0x00}, + {0x451d, 0x00}, + {0x451e, 0x00}, + {0x451f, 0x00}, + {0x4520, 0x00}, + {0x4521, 0x00}, + {0x4522, 0x00}, + {0x4523, 0x00}, + {0x4524, 0x00}, + {0x4525, 0x00}, + {0x4526, 0x00}, + {0x4527, 0x18}, + {0x4545, 0x00}, + {0x4546, 0x07}, + {0x4547, 0x33}, + {0x4549, 0x00}, + {0x454a, 0x00}, + {0x454b, 0x00}, + {0x454c, 0x00}, + {0x454d, 0x00}, + {0x454e, 0x00}, + {0x454f, 0x00}, + {0x4550, 0x00}, + {0x4551, 0x00}, + {0x4552, 0x00}, + {0x4553, 0x00}, + {0x4554, 0x00}, + {0x4555, 0x00}, + {0x4556, 0x00}, + {0x4557, 0x00}, + {0x4558, 0x00}, + {0x4559, 0x00}, + {0x455a, 0x00}, + {0x455b, 0x00}, + {0x455c, 0x00}, + {0x455d, 0x00}, + {0x455e, 0x00}, + {0x455f, 0x00}, + {0x4560, 0x00}, + {0x4561, 0x00}, + {0x4562, 0x00}, + {0x4563, 0x00}, + {0x4564, 0x00}, + {0x4565, 0x00}, + {0x4580, 0x01}, + {0x4583, 0x00}, + {0x4584, 0x00}, + {0x4585, 0x00}, + {0x4586, 0x00}, + {0x458c, 0x02}, + {0x458d, 0x00}, + {0x458e, 0x00}, + {0x45c0, 0x1c}, + {0x45c1, 0x80}, + {0x45c2, 0x0a}, + {0x45c3, 0x84}, + {0x45c4, 0x10}, + {0x45c5, 0x80}, + {0x45c6, 0x08}, + {0x45c7, 0x00}, + {0x45c8, 0x00}, + {0x45c9, 0x00}, + {0x45ca, 0x00}, + {0x45cb, 0x00}, + {0x45cc, 0x00}, + {0x45cd, 0x07}, + {0x45ce, 0x13}, + {0x45cf, 0x13}, + {0x45d0, 0x13}, + {0x45d2, 0x00}, + {0x45d3, 0x00}, + {0x45d4, 0x00}, + {0x45d5, 0x00}, + {0x45d6, 0x00}, + {0x45d7, 0x00}, + {0x45d8, 0x00}, + {0x45d9, 0x00}, + {0x45da, 0x00}, + {0x45dd, 0x00}, + {0x45de, 0x00}, + {0x45df, 0x00}, + {0x45e0, 0x00}, + {0x45e1, 0x00}, + {0x45e2, 0x00}, + {0x45e3, 0x00}, + {0x45e4, 0x00}, + {0x45e5, 0x00}, + {0x45e7, 0x00}, + {0x4602, 0x00}, + {0x4603, 0x15}, + {0x460b, 0x07}, + {0x4640, 0x01}, + {0x4641, 0x00}, + {0x4643, 0x08}, + {0x4644, 0xe0}, + {0x4645, 0xbf}, + {0x4647, 0x02}, + {0x464a, 0x00}, + {0x464b, 0x00}, + {0x464c, 0x01}, + {0x4680, 0x11}, + {0x4681, 0x80}, + {0x4684, 0x2b}, + {0x4685, 0x17}, + {0x4686, 0x00}, + {0x4687, 0x00}, + {0x4688, 0x00}, + {0x4689, 0x00}, + {0x468e, 0x30}, + {0x468f, 0x00}, + {0x4690, 0x00}, + {0x4691, 0x00}, + {0x4694, 0x04}, + {0x4800, 0x64}, + {0x4802, 0x02}, + {0x4806, 0x40}, + {0x4813, 0x10}, + {0x481b, 0x25}, + {0x4825, 0x32}, + {0x4826, 0x32}, + {0x4829, 0x64}, + {0x4836, 0x32}, + {0x4837, 0x04}, + {0x4840, 0x00}, + {0x4850, 0x42}, + {0x4851, 0xaa}, + {0x4853, 0x10}, + {0x4854, 0x05}, + {0x4855, 0x1c}, + {0x4860, 0x01}, + {0x4861, 0xec}, + {0x4862, 0x3a}, + {0x4883, 0x24}, + {0x4884, 0x11}, + {0x4888, 0x10}, + {0x4889, 0x00}, + {0x4911, 0x00}, + {0x491a, 0x40}, + {0x49f5, 0x00}, + {0x49f8, 0x04}, + {0x49f9, 0x00}, + {0x49fa, 0x02}, + {0x49fb, 0x00}, + {0x4a11, 0x00}, + {0x4a1a, 0x40}, + {0x4af8, 0x04}, + {0x4af9, 0x00}, + {0x4afa, 0x02}, + {0x4afb, 0x00}, + {0x4d00, 0x04}, + {0x4d01, 0x9d}, + {0x4d02, 0xbb}, + {0x4d03, 0x6c}, + {0x4d04, 0xc4}, + {0x4d05, 0x71}, + {0x5000, 0x5b}, + {0x5001, 0x28}, + {0x5002, 0x00}, + {0x5003, 0x0e}, + {0x5004, 0x02}, + {0x5007, 0x06}, + {0x5009, 0x2e}, + {0x5053, 0x05}, + {0x5060, 0x10}, + {0x5069, 0x10}, + {0x506a, 0x20}, + {0x506b, 0x04}, + {0x506c, 0x04}, + {0x506d, 0x0c}, + {0x506e, 0x0c}, + {0x506f, 0x04}, + {0x5070, 0x0c}, + {0x5071, 0x14}, + {0x5072, 0x1c}, + {0x5091, 0x00}, + {0x50c1, 0x00}, + {0x5110, 0x90}, + {0x5111, 0x14}, + {0x5112, 0x9b}, + {0x5113, 0x27}, + {0x5114, 0x01}, + {0x5155, 0x08}, + {0x5156, 0x0c}, + {0x5157, 0x0c}, + {0x5159, 0x08}, + {0x515a, 0x0c}, + {0x515b, 0x0c}, + {0x5180, 0xc0}, + {0x518a, 0x04}, + {0x51d3, 0x0a}, + {0x5251, 0x00}, + {0x5312, 0x00}, + {0x53c1, 0x00}, + {0x5410, 0x90}, + {0x5411, 0x14}, + {0x5412, 0x9b}, + {0x5413, 0x27}, + {0x5455, 0x08}, + {0x5456, 0x0c}, + {0x5457, 0x0c}, + {0x5459, 0x08}, + {0x545a, 0x0c}, + {0x545b, 0x0c}, + {0x5480, 0xc0}, + {0x548a, 0x04}, + {0x56c1, 0x00}, + {0x5710, 0x90}, + {0x5711, 0x14}, + {0x5712, 0x9b}, + {0x5713, 0x27}, + {0x5755, 0x08}, + {0x5756, 0x0c}, + {0x5757, 0x0c}, + {0x5759, 0x08}, + {0x575a, 0x0c}, + {0x575b, 0x0c}, + {0x5780, 0xc0}, + {0x578a, 0x04}, + {0x5853, 0xfe}, + {0x5854, 0xfe}, + {0x5855, 0xfe}, + {0x5856, 0xff}, + {0x5857, 0xff}, + {0x5858, 0xff}, + {0x587b, 0x16}, + {0x58a7, 0x11}, + {0x58c0, 0x3f}, + {0x58fd, 0x0a}, + {0x5925, 0x00}, + {0x5926, 0x00}, + {0x5927, 0x00}, + {0x5928, 0x00}, + {0x5929, 0x00}, + {0x592c, 0x06}, + {0x592d, 0x00}, + {0x592e, 0x03}, + {0x59c2, 0x00}, + {0x59c3, 0xce}, + {0x59c4, 0x01}, + {0x59c5, 0x20}, + {0x59c6, 0x01}, + {0x59c7, 0x91}, + {0x59c8, 0x02}, + {0x59c9, 0x2f}, + {0x59ca, 0x03}, + {0x59cb, 0x0a}, + {0x59cc, 0x04}, + {0x59cd, 0x3d}, + {0x59ce, 0x05}, + {0x59cf, 0xe8}, + {0x59d0, 0x08}, + {0x59d1, 0x3c}, + {0x59d2, 0x0b}, + {0x59d3, 0x7a}, + {0x59d4, 0x0f}, + {0x59d5, 0xff}, + {0x59d6, 0x0f}, + {0x59d7, 0xff}, + {0x59d8, 0x0f}, + {0x59d9, 0xff}, + {0x59da, 0x0f}, + {0x59db, 0xff}, + {0x59ef, 0x5f}, + {0x6901, 0x18}, + {0x6924, 0x00}, + {0x6925, 0x00}, + {0x6926, 0x00}, + {0x6942, 0x00}, + {0x6943, 0x00}, + {0x6944, 0x00}, + {0x694b, 0x00}, + {0x6a20, 0x03}, + {0x6a21, 0x04}, + {0x6a22, 0x00}, + {0x6a53, 0xfe}, + {0x6a54, 0xfe}, + {0x6a55, 0xfe}, + {0x6a56, 0xff}, + {0x6a57, 0xff}, + {0x6a58, 0xff}, + {0x6a7b, 0x16}, + {0x6aa7, 0x11}, + {0x6ac0, 0x3f}, + {0x6afd, 0x0a}, + {0x6b25, 0x00}, + {0x6b26, 0x00}, + {0x6b27, 0x00}, + {0x6b28, 0x00}, + {0x6b29, 0x00}, + {0x6b2c, 0x06}, + {0x6b2d, 0x00}, + {0x6b2e, 0x03}, + {0x6bc2, 0x00}, + {0x6bc3, 0xce}, + {0x6bc4, 0x01}, + {0x6bc5, 0x20}, + {0x6bc6, 0x01}, + {0x6bc7, 0x91}, + {0x6bc8, 0x02}, + {0x6bc9, 0x2f}, + {0x6bca, 0x03}, + {0x6bcb, 0x0a}, + {0x6bcc, 0x04}, + {0x6bcd, 0x3d}, + {0x6bce, 0x05}, + {0x6bcf, 0xe8}, + {0x6bd0, 0x08}, + {0x6bd1, 0x3c}, + {0x6bd2, 0x0b}, + {0x6bd3, 0x7a}, + {0x6bd4, 0x0f}, + {0x6bd5, 0xff}, + {0x6bd6, 0x0f}, + {0x6bd7, 0xff}, + {0x6bd8, 0x0f}, + {0x6bd9, 0xff}, + {0x6bda, 0x0f}, + {0x6bdb, 0xff}, + {0x6bef, 0x5f}, + {0xc200, 0x00}, + {0xc201, 0x00}, + {0xc202, 0x00}, + {0xc203, 0x00}, + {0xc210, 0x00}, + {0xc211, 0x00}, + {0xc212, 0x00}, + {0xc213, 0x00}, + {0xc214, 0x00}, + {0xc230, 0x00}, + {0xc231, 0x00}, + {0xc232, 0x00}, + {0xc233, 0x00}, + {0xc240, 0x00}, + {0xc241, 0x00}, + {0xc242, 0x00}, + {0xc243, 0x00}, + {0xc250, 0x00}, + {0xc251, 0x00}, + {0xc252, 0x00}, + {0xc253, 0x00}, + {0xc260, 0x00}, + {0xc261, 0x00}, + {0xc262, 0x00}, + {0xc263, 0x00}, + {0xc270, 0x00}, + {0xc271, 0x00}, + {0xc272, 0x00}, + {0xc273, 0x00}, + {0xc40e, 0xa0}, + {0xc418, 0x02}, + {0xc42f, 0x00}, + {0xc448, 0x00}, + {0xc44e, 0x03}, + {0xc44f, 0x03}, + {0xc450, 0x04}, + {0xc451, 0x04}, + {0xc46e, 0x01}, + {0xc478, 0x01}, + {0xc49c, 0x00}, + {0xc49d, 0x00}, + {0xc49e, 0x1c}, + {0xc49f, 0x30}, + {0xc4a2, 0x3a}, + {0xc4a3, 0x8e}, + {0xc4b9, 0x09}, + {0xc4bf, 0x01}, + {0xc4c1, 0x07}, + {0xc4c2, 0x07}, + {0xc4c3, 0x77}, + {0xc4c4, 0x77}, + {0xc4d2, 0x38}, + {0xc4d3, 0x38}, + {0xc4d4, 0x38}, + {0xc4d5, 0x38}, + {0xc4e3, 0x14}, + {0xc4e9, 0x20}, + {0xc4f8, 0x01}, + {0xc500, 0x01}, + {0xc506, 0x14}, + {0xc507, 0x02}, + {0xc50b, 0x77}, + {0xc50e, 0x00}, + {0xc50f, 0x00}, + {0xc510, 0x00}, + {0xc511, 0x00}, + {0xc512, 0x00}, + {0xc513, 0x4e}, + {0xc514, 0x4f}, + {0xc515, 0x2a}, + {0xc516, 0x16}, + {0xc517, 0x0b}, + {0xc518, 0x33}, + {0xc519, 0x33}, + {0xc51a, 0x33}, + {0xc51b, 0x33}, + {0xc51c, 0x33}, + {0xc51d, 0x37}, + {0xc51e, 0x37}, + {0xc51f, 0x3a}, + {0xc520, 0x3a}, + {0xc521, 0x3a}, + {0xc52e, 0x0e}, + {0xc52f, 0x0e}, + {0xc530, 0x0e}, + {0xc531, 0x0e}, + {0xc532, 0x0e}, + {0xc533, 0x0e}, + {0xc534, 0x0e}, + {0xc535, 0x0e}, + {0xc53a, 0x0e}, + {0xc53b, 0x0e}, + {0xc53c, 0x0e}, + {0xc53d, 0x0e}, + {0xc53e, 0x0e}, + {0xc53f, 0x0e}, + {0xc540, 0x0e}, + {0xc541, 0x0e}, + {0xc542, 0x0e}, + {0xc543, 0x0e}, + {0xc544, 0x0e}, + {0xc545, 0x0e}, + {0xc546, 0x0e}, + {0xc547, 0x0e}, + {0xc548, 0x0e}, + {0xc549, 0x0e}, + {0xc57d, 0x80}, + {0xc57f, 0x18}, + {0xc580, 0x18}, + {0xc581, 0x18}, + {0xc582, 0x18}, + {0xc583, 0x01}, + {0xc584, 0x01}, + {0xc586, 0x0a}, + {0xc587, 0x18}, + {0xc588, 0x18}, + {0xc589, 0x18}, + {0xc58a, 0x0c}, + {0xc58b, 0x08}, + {0xc58c, 0x04}, + {0xc58e, 0x0a}, + {0xc58f, 0x28}, + {0xc590, 0x28}, + {0xc591, 0x28}, + {0xc592, 0x28}, + {0xc593, 0x04}, + {0xc594, 0x04}, + {0xc597, 0x2c}, + {0xc598, 0x2c}, + {0xc599, 0x2c}, + {0xc59a, 0x28}, + {0xc59b, 0x20}, + {0xc59c, 0x18}, + {0xc5e3, 0x07}, + {0xc5e4, 0x00}, + {0xc5e5, 0x01}, + {0xc5e8, 0x01}, + {0xc5eb, 0x55}, + {0xc5ec, 0x05}, + {0xc624, 0xf8}, + {0xc638, 0x01}, + {0xc639, 0x00}, + {0xc63c, 0x01}, + {0xc63d, 0x00}, + {0xc640, 0x01}, + {0xc641, 0x00}, + {0xc64c, 0x08}, + {0xc64d, 0x08}, + {0xc64e, 0x08}, + {0xc64f, 0x08}, + {0xc650, 0x08}, + {0xc651, 0x08}, + {0xc664, 0x00}, + {0xc66b, 0x00}, + {0xc66c, 0x00}, + {0xc66d, 0x00}, + {0xc66e, 0x01}, + {0xc66f, 0x00}, + {0xc700, 0x80}, + {0xc702, 0x00}, + {0xc703, 0x00}, + {0xc726, 0x03}, + {0xc72b, 0xff}, + {0xc72c, 0xff}, + {0xc72d, 0xff}, + {0xc72f, 0x08}, + {0xc730, 0x00}, + {0xc731, 0x00}, + {0xc732, 0x00}, + {0xc733, 0x00}, + {0xc734, 0x00}, + {0xc735, 0x00}, + {0xc736, 0x01}, + {0xc739, 0x18}, + {0xc73a, 0x49}, + {0xc73b, 0x92}, + {0xc73c, 0x24}, + {0xc73d, 0x00}, + {0xc73e, 0x00}, + {0xc73f, 0x00}, + {0xc740, 0x00}, + {0xc741, 0x00}, + {0xc742, 0x00}, + {0xc743, 0x00}, + {0xc744, 0x00}, + {0xc745, 0x00}, + {0xc746, 0x01}, + {0xc747, 0x04}, + {0xc749, 0x1c}, + {0xc74c, 0x40}, + {0xc74e, 0x00}, + {0xc750, 0x55}, + {0xc751, 0x00}, + {0xc758, 0x40}, + {0xc75b, 0x01}, + {0xc75c, 0x05}, + {0xc765, 0x2a}, + {0xc773, 0x02}, + {0xc774, 0x03}, + {0xc78a, 0x03}, + {0xc78b, 0x04}, + {0xc797, 0x03}, + {0xc798, 0x03}, + {0xc79c, 0x00}, + {0xc79e, 0x01}, + {0xc7a0, 0x12}, + {0xc7a2, 0x01}, + {0xc7a3, 0x01}, + {0xc7a6, 0x02}, + {0xc7a7, 0xff}, + {0xc7a8, 0xff}, + {0xc7a9, 0xff}, + {0xc7aa, 0xff}, + {0xc7ab, 0xff}, + {0xc7ac, 0x02}, + {0xc7ad, 0xff}, + {0xc7ae, 0xff}, + {0xc7af, 0xff}, + {0xc7b0, 0xff}, + {0xc7b1, 0xff}, + {0xc7b2, 0x01}, + {0xc7b3, 0xff}, + {0xc7b4, 0xff}, + {0xc7b5, 0xff}, + {0xc7b6, 0xff}, + {0xc7c3, 0xff}, + {0xc7c4, 0x00}, + {0xc7c5, 0xff}, + {0xc7d9, 0x50}, + {0xc7da, 0xaa}, + {0xc7db, 0x0a}, + {0xc7dc, 0xa0}, + {0xc7e2, 0x01}, + {0xc7e4, 0x01}, + {0xc7e8, 0x12}, + {0xc7fd, 0x12}, + {0xc855, 0x07}, + {0xc8a4, 0x07}, + {0xc95a, 0x77}, + {0xc95b, 0x77}, + {0xc95c, 0x77}, + {0xc95d, 0x77}, + {0xc97b, 0x10}, + {0xc9a8, 0x1c}, + {0xc9b9, 0x28}, + {0xc9be, 0x01}, + {0xc9f3, 0x01}, + {0xc9fe, 0x0a}, + {0xc9ff, 0x0e}, + {0xca00, 0x1a}, + {0xca01, 0x1a}, + {0xca02, 0x1a}, + {0xca02, 0x1a}, + {0xca17, 0x03}, + {0xca18, 0x1a}, + {0xca19, 0x1a}, + {0xca1a, 0x1a}, + {0xca1b, 0x1a}, + {0xca22, 0x12}, + {0xca23, 0x12}, + {0xca24, 0x12}, + {0xca25, 0x12}, + {0xca26, 0x12}, + {0xca31, 0x12}, + {0xca32, 0x12}, + {0xca33, 0x12}, + {0xca34, 0x12}, + {0xca35, 0x12}, + {0xca36, 0x12}, + {0xca37, 0x12}, + {0xca38, 0x12}, + {0xca39, 0x12}, + {0xca3a, 0x12}, + {0xca45, 0x12}, + {0xca46, 0x12}, + {0xca47, 0x12}, + {0xca48, 0x12}, + {0xca49, 0x12}, + {0xcaab, 0x18}, + {0xcaca, 0x0f}, + {0xcada, 0x03}, + {REG_NULL, 0x00}, +}; + +static const struct regval ov50h40_10bit_4096x3072_cphy_30fps_regs[] = { + {0x0304, 0x02}, + {0x0305, 0xd0}, + {0x0327, 0x0e}, + {0x0329, 0x01}, + {0x032c, 0x00}, + {0x0344, 0x01}, + {0x0345, 0x10}, + {0x0360, 0x09}, + {0x3027, 0x00}, + {0x3400, 0x0c}, + {0x3422, 0x08}, + {0x3423, 0x00}, + {0x3506, 0xf8}, + {0x350d, 0x00}, + {0x350e, 0xb2}, + {0x350f, 0x40}, + {0x3546, 0xf8}, + {0x354d, 0x00}, + {0x354e, 0xb2}, + {0x354f, 0x40}, + {0x3586, 0xf8}, + {0x358d, 0x00}, + {0x358e, 0xb2}, + {0x358f, 0x40}, + {0x3609, 0x80}, + {0x360c, 0x4f}, + {0x3610, 0x08}, + {0x3614, 0x10}, + {0x3618, 0xcf}, + {0x3619, 0x40}, + {0x361a, 0x01}, + {0x361d, 0x1f}, + {0x363b, 0x9f}, + {0x363c, 0x6e}, + {0x3640, 0x00}, + {0x3641, 0x02}, + {0x3644, 0x00}, + {0x3645, 0x06}, + {0x3647, 0x01}, + {0x3650, 0xbf}, + {0x3653, 0x03}, + {0x3680, 0x00}, + {0x3682, 0x80}, + {0x3684, 0x01}, + {0x3688, 0x00}, + {0x368a, 0x0e}, + {0x3696, 0x41}, + {0x369a, 0x00}, + {0x36d0, 0x00}, + {0x36d3, 0x40}, + {0x3700, 0x1c}, + {0x3701, 0x13}, + {0x3704, 0x03}, + {0x3706, 0x34}, + {0x3707, 0x04}, + {0x3709, 0x7c}, + {0x370b, 0x94}, + {0x3712, 0x00}, + {0x3714, 0xf2}, + {0x3716, 0x40}, + {0x3722, 0x05}, + {0x3724, 0x08}, + {0x372b, 0x00}, + {0x372e, 0x1c}, + {0x372f, 0x13}, + {0x373f, 0x00}, + {0x374f, 0x58}, + {0x3755, 0x7c}, + {0x3757, 0x7f}, + {0x3759, 0x50}, + {0x375e, 0x0d}, + {0x375f, 0x00}, + {0x3770, 0x04}, + {0x3780, 0x5e}, + {0x3782, 0x01}, + {0x378a, 0x01}, + {0x3791, 0x34}, + {0x3793, 0x1c}, + {0x3795, 0x1c}, + {0x3797, 0x94}, + {0x3799, 0x3a}, + {0x379b, 0x3a}, + {0x379c, 0x01}, + {0x379f, 0x01}, + {0x37a0, 0x9b}, + {0x37a9, 0x01}, + {0x37b2, 0xc8}, + {0x37b7, 0x02}, + {0x37bd, 0x00}, + {0x37c1, 0x1a}, + {0x37c3, 0x1a}, + {0x37cb, 0x02}, + {0x37cd, 0x02}, + {0x37d0, 0x22}, + {0x37d4, 0x00}, + {0x37db, 0x10}, + {0x37dc, 0x1a}, + {0x37e3, 0x30}, + {0x37f0, 0x01}, + {0x37f6, 0x1a}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x20}, + {0x3805, 0x1f}, + {0x3806, 0x18}, + {0x3807, 0x3f}, + {0x3808, 0x10}, + {0x3809, 0x00}, + {0x380a, 0x0c}, + {0x380b, 0x00}, + {0x380c, 0x04}, + {0x380d, 0x4c}, + {0x380e, 0x08}, + {0x380f, 0xe0}, + {0x3810, 0x00}, + {0x3811, 0x07}, + {0x3813, 0x10}, + {0x3815, 0x11}, + {0x3820, 0x46}, + {0x3821, 0x10}, + {0x3822, 0x10}, + {0x3823, 0x04}, + {0x3827, 0x40}, + {0x3828, 0x21}, + {0x3830, 0x20}, + {0x3831, 0x12}, + {0x3837, 0x20}, + {0x383f, 0x08}, + {0x384c, 0x04}, + {0x384d, 0x4c}, + {0x3888, 0x00}, + {0x3889, 0x08}, + {0x388b, 0x10}, + {0x388c, 0x10}, + {0x388d, 0x00}, + {0x388e, 0x0c}, + {0x388f, 0x00}, + {0x3896, 0x00}, + {0x38db, 0x08}, + {0x38dd, 0x04}, + {0x38de, 0x03}, + {0x38df, 0x08}, + {0x3906, 0x24}, + {0x390a, 0x15}, + {0x3919, 0x11}, + {0x3982, 0x40}, + {0x398b, 0x00}, + {0x399d, 0x13}, + {0x39dc, 0x00}, + {0x39fb, 0x01}, + {0x39fc, 0x01}, + {0x39fd, 0x01}, + {0x39fe, 0x01}, + {0x3a1d, 0x01}, + {0x3a1e, 0x01}, + {0x3a21, 0x01}, + {0x3a22, 0x01}, + {0x3a68, 0x13}, + {0x3a69, 0x20}, + {0x3ab6, 0x01}, + {0x3ab7, 0x01}, + {0x3af2, 0x03}, + {0x3b01, 0x1d}, + {0x3b02, 0x00}, + {0x3b3d, 0x07}, + {0x3b4a, 0x00}, + {0x3b4b, 0x00}, + {0x3b56, 0x1f}, + {0x3b57, 0x1f}, + {0x3b58, 0x20}, + {0x3b59, 0x20}, + {0x3b5a, 0x19}, + {0x3b5b, 0x19}, + {0x3b5c, 0x19}, + {0x3b5d, 0x19}, + {0x3b82, 0x19}, + {0x3ba1, 0x1e}, + {0x3ba6, 0x77}, + {0x3ba7, 0x77}, + {0x3baa, 0x33}, + {0x3bab, 0x2f}, + {0x3baf, 0x16}, + {0x3bba, 0x48}, + {0x3bf3, 0x01}, + {0x3bfb, 0x01}, + {0x3bfc, 0x50}, + {0x3bff, 0x08}, + {0x400e, 0x1c}, + {0x4010, 0x34}, + {0x4012, 0x17}, + {0x4015, 0x08}, + {0x4016, 0x17}, + {0x4018, 0x07}, + {0x4506, 0x01}, + {0x4509, 0x07}, + {0x450c, 0x00}, + {0x450d, 0x60}, + {0x4510, 0x03}, + {0x4516, 0x55}, + {0x4517, 0x55}, + {0x4518, 0x55}, + {0x4519, 0x55}, + {0x451a, 0xaa}, + {0x451b, 0xaa}, + {0x451c, 0xaa}, + {0x451d, 0xaa}, + {0x451e, 0xff}, + {0x451f, 0xff}, + {0x4520, 0xff}, + {0x4521, 0xff}, + {0x4522, 0x29}, + {0x4523, 0x08}, + {0x4524, 0xbb}, + {0x4525, 0x0c}, + {0x4545, 0x00}, + {0x4546, 0x03}, + {0x4547, 0x9a}, + {0x4549, 0x00}, + {0x454a, 0x29}, + {0x454b, 0x08}, + {0x454c, 0xbb}, + {0x454d, 0x0c}, + {0x454e, 0x29}, + {0x454f, 0x08}, + {0x4550, 0xbb}, + {0x4551, 0x0c}, + {0x4552, 0x29}, + {0x4553, 0x08}, + {0x4554, 0xbb}, + {0x4555, 0x0c}, + {0x4556, 0x29}, + {0x4557, 0x08}, + {0x4558, 0xbb}, + {0x4559, 0x0c}, + {0x455a, 0x29}, + {0x455b, 0x08}, + {0x455c, 0xbb}, + {0x455d, 0x0c}, + {0x455e, 0x29}, + {0x455f, 0x08}, + {0x4560, 0xbb}, + {0x4561, 0x0c}, + {0x4562, 0x29}, + {0x4563, 0x08}, + {0x4564, 0xbb}, + {0x4565, 0x0c}, + {0x45c0, 0x8e}, + {0x45c1, 0x80}, + {0x45c2, 0x0a}, + {0x45c3, 0x04}, + {0x45c4, 0x13}, + {0x45c5, 0x40}, + {0x45c6, 0x01}, + {0x4602, 0x00}, + {0x4603, 0x15}, + {0x460b, 0x07}, + {0x4640, 0x01}, + {0x4641, 0x00}, + {0x4643, 0x0c}, + {0x4680, 0x11}, + {0x4684, 0x2b}, + {0x468e, 0x30}, + {0x4813, 0x10}, + {0x4836, 0x32}, + {0x4837, 0x04}, + {0x49f5, 0x00}, + {0x5000, 0x2b}, + {0x5001, 0x08}, + {0x5002, 0x00}, + {0x5007, 0x06}, + {0x5009, 0x40}, + {0x5091, 0x00}, + {0x5180, 0xc0}, + {0x5480, 0xc0}, + {0x5780, 0xc0}, + {0x6a03, 0x00}, + {0xc200, 0x00}, + {0xc201, 0x00}, + {0xc202, 0x00}, + {0xc203, 0x00}, + {0xc210, 0x00}, + {0xc211, 0x00}, + {0xc212, 0x00}, + {0xc213, 0x00}, + {0xc214, 0x00}, + {0xc230, 0x00}, + {0xc231, 0x00}, + {0xc232, 0x00}, + {0xc233, 0x00}, + {0xc240, 0x00}, + {0xc241, 0x00}, + {0xc242, 0x00}, + {0xc243, 0x00}, + {0xc250, 0x00}, + {0xc251, 0x00}, + {0xc252, 0x00}, + {0xc253, 0x00}, + {0xc260, 0x00}, + {0xc261, 0x00}, + {0xc262, 0x00}, + {0xc263, 0x00}, + {0xc270, 0x00}, + {0xc271, 0x00}, + {0xc272, 0x00}, + {0xc273, 0x00}, + {0xc40e, 0x00}, + {0xc448, 0x00}, + {0xc46e, 0x01}, + {0xc478, 0x01}, + {0xc49e, 0x34}, + {0xc49f, 0x34}, + {0xc4a2, 0x94}, + {0xc4a3, 0x94}, + {0xc4c1, 0x07}, + {0xc4c2, 0x07}, + {0xc4c3, 0x77}, + {0xc4c4, 0x77}, + {0xc4d2, 0x00}, + {0xc4d3, 0x00}, + {0xc4d4, 0x00}, + {0xc4d5, 0x00}, + {0xc4e3, 0x19}, + {0xc4e9, 0x1e}, + {0xc506, 0x16}, + {0xc50e, 0x1f}, + {0xc50f, 0x1f}, + {0xc510, 0x0f}, + {0xc511, 0x07}, + {0xc512, 0x03}, + {0xc513, 0x4e}, + {0xc514, 0x4e}, + {0xc515, 0x27}, + {0xc516, 0x16}, + {0xc517, 0x0c}, + {0xc518, 0x33}, + {0xc519, 0x33}, + {0xc51a, 0x33}, + {0xc51b, 0x3b}, + {0xc51c, 0x3b}, + {0xc51d, 0x2f}, + {0xc51e, 0x2f}, + {0xc51f, 0x2f}, + {0xc520, 0x2f}, + {0xc521, 0x30}, + {0xc52e, 0x0e}, + {0xc52f, 0x0e}, + {0xc530, 0x0e}, + {0xc531, 0x0e}, + {0xc532, 0x0e}, + {0xc533, 0x0e}, + {0xc534, 0x0e}, + {0xc535, 0x0e}, + {0xc542, 0x0e}, + {0xc543, 0x0e}, + {0xc544, 0x0e}, + {0xc545, 0x0e}, + {0xc546, 0x0e}, + {0xc547, 0x0e}, + {0xc548, 0x0e}, + {0xc549, 0x0e}, + {0xc57d, 0x00}, + {0xc581, 0x18}, + {0xc582, 0x18}, + {0xc583, 0x02}, + {0xc584, 0x01}, + {0xc587, 0x18}, + {0xc589, 0x18}, + {0xc58a, 0x10}, + {0xc58b, 0x08}, + {0xc58c, 0x01}, + {0xc58f, 0x28}, + {0xc590, 0x28}, + {0xc591, 0x28}, + {0xc592, 0x28}, + {0xc593, 0x0a}, + {0xc594, 0x06}, + {0xc597, 0x2e}, + {0xc598, 0x2e}, + {0xc599, 0x2e}, + {0xc59a, 0x18}, + {0xc59b, 0x0e}, + {0xc59c, 0x08}, + {0xc5e4, 0x00}, + {0xc5e5, 0x07}, + {0xc5e8, 0x01}, + {0xc702, 0x10}, + {0xc726, 0x03}, + {0xc72b, 0xff}, + {0xc72c, 0xff}, + {0xc72d, 0xff}, + {0xc72f, 0x08}, + {0xc736, 0x01}, + {0xc739, 0x18}, + {0xc73a, 0xa6}, + {0xc73b, 0x00}, + {0xc73c, 0x00}, + {0xc746, 0x01}, + {0xc747, 0x04}, + {0xc749, 0x1c}, + {0xc75b, 0x01}, + {0xc75c, 0x05}, + {0xc765, 0x2a}, + {0xc773, 0x02}, + {0xc774, 0x03}, + {0xc78a, 0x03}, + {0xc78b, 0x04}, + {0xc798, 0x03}, + {0xc7a2, 0x01}, + {0xc7a6, 0x02}, + {0xc7a7, 0x02}, + {0xc7a8, 0xff}, + {0xc7a9, 0xff}, + {0xc7aa, 0xff}, + {0xc7ac, 0x02}, + {0xc7ad, 0x08}, + {0xc7ae, 0xff}, + {0xc7af, 0xff}, + {0xc7b0, 0xff}, + {0xc7b2, 0x01}, + {0xc7b3, 0x02}, + {0xc7b4, 0xff}, + {0xc7b5, 0xff}, + {0xc7b6, 0xff}, + {0xc7c4, 0x01}, + {0xc7c5, 0x00}, + {0xc7e2, 0x01}, + {0xc855, 0x77}, + {0xc8a4, 0x77}, + {0xc95a, 0x77}, + {0xc95b, 0x77}, + {0xc9b9, 0x18}, + {0xc9fe, 0x0a}, + {0xc9ff, 0x12}, + {0xca00, 0x1a}, + {0xca02, 0x1a}, + {0xca17, 0x04}, + {0xca18, 0x1a}, + {0xca19, 0x1a}, + {0x3501, 0x08}, + {0x3502, 0x00}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {REG_NULL, 0x00}, +}; + +static const struct regval ov50h40_10bit_8192x6144_cphy_30fps_regs[] = { + {0x0304, 0x02}, + {0x0305, 0xd0}, + {0x0327, 0x0e}, + {0x0329, 0x01}, + {0x032c, 0x00}, + {0x0344, 0x01}, + {0x0345, 0x20}, + {0x0360, 0x09}, + {0x3027, 0x00}, + {0x3400, 0x0c}, + {0x3422, 0x08}, + {0x3423, 0x00}, + {0x3506, 0x78}, + {0x350d, 0x01}, + {0x350e, 0x00}, + {0x350f, 0x00}, + {0x3546, 0x78}, + {0x354d, 0x01}, + {0x354e, 0x00}, + {0x354f, 0x00}, + {0x3586, 0x78}, + {0x358d, 0x01}, + {0x358e, 0x00}, + {0x358f, 0x00}, + {0x3609, 0x80}, + {0x360c, 0x0f}, + {0x3610, 0x08}, + {0x3614, 0x0c}, + {0x3618, 0xcf}, + {0x3619, 0x44}, + {0x361a, 0x81}, + {0x361d, 0x1f}, + {0x363b, 0x6a}, + {0x363c, 0x6a}, + {0x3640, 0x00}, + {0x3641, 0x02}, + {0x3644, 0x00}, + {0x3645, 0x06}, + {0x3647, 0x01}, + {0x3650, 0xbf}, + {0x3653, 0x03}, + {0x3680, 0x00}, + {0x3682, 0x80}, + {0x3684, 0x00}, + {0x3688, 0x00}, + {0x368a, 0x0e}, + {0x3696, 0x41}, + {0x369a, 0x00}, + {0x36d0, 0x00}, + {0x36d3, 0x80}, + {0x3700, 0x1c}, + {0x3701, 0x13}, + {0x3704, 0x03}, + {0x3706, 0x1c}, + {0x3707, 0x04}, + {0x3709, 0x70}, + {0x370b, 0x3a}, + {0x3712, 0x01}, + {0x3714, 0xf8}, + {0x3716, 0x40}, + {0x3722, 0x05}, + {0x3724, 0x5d}, + {0x372b, 0x00}, + {0x372e, 0x1c}, + {0x372f, 0x13}, + {0x373f, 0x00}, + {0x374f, 0x58}, + {0x3755, 0xb1}, + {0x3757, 0x30}, + {0x3759, 0x50}, + {0x375e, 0x00}, + {0x375f, 0x00}, + {0x3770, 0x01}, + {0x3780, 0x5c}, + {0x3782, 0x01}, + {0x378a, 0x01}, + {0x3791, 0x30}, + {0x3793, 0x1c}, + {0x3795, 0x1c}, + {0x3797, 0x8e}, + {0x3799, 0x3a}, + {0x379b, 0x3a}, + {0x379c, 0x01}, + {0x379f, 0x01}, + {0x37a0, 0x70}, + {0x37a9, 0x01}, + {0x37b2, 0xc8}, + {0x37b7, 0x02}, + {0x37bd, 0x00}, + {0x37c1, 0x1a}, + {0x37c3, 0x1a}, + {0x37cb, 0x02}, + {0x37cd, 0x01}, + {0x37d0, 0x00}, + {0x37d4, 0x00}, + {0x37db, 0x10}, + {0x37dc, 0x1a}, + {0x37e3, 0x30}, + {0x37f0, 0x01}, + {0x37f6, 0x1a}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x20}, + {0x3805, 0x1f}, + {0x3806, 0x18}, + {0x3807, 0x3f}, + {0x3808, 0x20}, + {0x3809, 0x00}, + {0x380a, 0x18}, + {0x380b, 0x00}, + {0x380c, 0x03}, + {0x380d, 0x06}, + {0x380e, 0x0c}, + {0x380f, 0x96}, + {0x3810, 0x00}, + {0x3811, 0x0f}, + {0x3813, 0x20}, + {0x3815, 0x11}, + {0x3820, 0x44}, + {0x3821, 0x00}, + {0x3822, 0x00}, + {0x3823, 0x04}, + {0x3827, 0x40}, + {0x3828, 0x27}, + {0x3830, 0x20}, + {0x3831, 0x10}, + {0x3837, 0x20}, + {0x383f, 0x08}, + {0x384c, 0x03}, + {0x384d, 0x06}, + {0x3888, 0x00}, + {0x3889, 0x10}, + {0x388b, 0x20}, + {0x388c, 0x20}, + {0x388d, 0x00}, + {0x388e, 0x18}, + {0x388f, 0x00}, + {0x3896, 0x00}, + {0x38db, 0x20}, + {0x38dd, 0x10}, + {0x38de, 0x0c}, + {0x38df, 0x20}, + {0x3906, 0x24}, + {0x390a, 0x05}, + {0x3919, 0x15}, + {0x3982, 0x40}, + {0x398b, 0x00}, + {0x399d, 0x05}, + {0x39dc, 0x01}, + {0x39fb, 0x01}, + {0x39fc, 0x01}, + {0x39fd, 0x06}, + {0x39fe, 0x06}, + {0x3a1d, 0x01}, + {0x3a1e, 0x01}, + {0x3a21, 0x01}, + {0x3a22, 0x06}, + {0x3a68, 0x05}, + {0x3a69, 0x20}, + {0x3ab6, 0x01}, + {0x3ab7, 0x01}, + {0x3af2, 0x03}, + {0x3b01, 0x00}, + {0x3b02, 0x00}, + {0x3b3d, 0x07}, + {0x3b4a, 0x38}, + {0x3b4b, 0x38}, + {0x3b56, 0x20}, + {0x3b57, 0x21}, + {0x3b58, 0x21}, + {0x3b59, 0x21}, + {0x3b5a, 0x14}, + {0x3b5b, 0x14}, + {0x3b5c, 0x14}, + {0x3b5d, 0x14}, + {0x3b82, 0x14}, + {0x3ba1, 0x20}, + {0x3ba6, 0x00}, + {0x3ba7, 0x00}, + {0x3baa, 0x33}, + {0x3bab, 0x37}, + {0x3baf, 0x00}, + {0x3bba, 0x4c}, + {0x3bf3, 0x01}, + {0x3bfb, 0x01}, + {0x3bfc, 0x50}, + {0x3bff, 0x08}, + {0x400e, 0x14}, + {0x4010, 0x34}, + {0x4012, 0x17}, + {0x4015, 0x10}, + {0x4016, 0x2f}, + {0x4018, 0x0f}, + {0x4506, 0x01}, + {0x4509, 0x07}, + {0x450c, 0x00}, + {0x450d, 0x30}, + {0x4510, 0x00}, + {0x4516, 0x00}, + {0x4517, 0x00}, + {0x4518, 0x00}, + {0x4519, 0x00}, + {0x451a, 0x00}, + {0x451b, 0x00}, + {0x451c, 0x00}, + {0x451d, 0x00}, + {0x451e, 0x00}, + {0x451f, 0x00}, + {0x4520, 0x00}, + {0x4521, 0x00}, + {0x4522, 0x00}, + {0x4523, 0x00}, + {0x4524, 0x00}, + {0x4525, 0x00}, + {0x4545, 0x00}, + {0x4546, 0x04}, + {0x4547, 0xcc}, + {0x4549, 0x00}, + {0x454a, 0x00}, + {0x454b, 0x00}, + {0x454c, 0x00}, + {0x454d, 0x00}, + {0x454e, 0x00}, + {0x454f, 0x00}, + {0x4550, 0x00}, + {0x4551, 0x00}, + {0x4552, 0x00}, + {0x4553, 0x00}, + {0x4554, 0x00}, + {0x4555, 0x00}, + {0x4556, 0x00}, + {0x4557, 0x00}, + {0x4558, 0x00}, + {0x4559, 0x00}, + {0x455a, 0x00}, + {0x455b, 0x00}, + {0x455c, 0x00}, + {0x455d, 0x00}, + {0x455e, 0x00}, + {0x455f, 0x00}, + {0x4560, 0x00}, + {0x4561, 0x00}, + {0x4562, 0x00}, + {0x4563, 0x00}, + {0x4564, 0x00}, + {0x4565, 0x00}, + {0x45c0, 0x9c}, + {0x45c1, 0x80}, + {0x45c2, 0x0a}, + {0x45c3, 0x04}, + {0x45c4, 0x13}, + {0x45c5, 0x80}, + {0x45c6, 0x08}, + {0x4602, 0x00}, + {0x4603, 0x15}, + {0x460b, 0x07}, + {0x4640, 0x01}, + {0x4641, 0x00}, + {0x4643, 0x08}, + {0x4680, 0x11}, + {0x4684, 0x2b}, + {0x468e, 0x30}, + {0x4813, 0x10}, + {0x4836, 0x32}, + {0x4837, 0x04}, + {0x49f5, 0x00}, + {0x5000, 0x5b}, + {0x5001, 0x28}, + {0x5002, 0x00}, + {0x5007, 0x06}, + {0x5009, 0x2e}, + {0x5091, 0x00}, + {0x5180, 0xc0}, + {0x5480, 0xc0}, + {0x5780, 0xc0}, + {0x6a03, 0x00}, + {0xc200, 0x00}, + {0xc201, 0x00}, + {0xc202, 0x00}, + {0xc203, 0x00}, + {0xc210, 0x00}, + {0xc211, 0x00}, + {0xc212, 0x00}, + {0xc213, 0x00}, + {0xc214, 0x00}, + {0xc230, 0x00}, + {0xc231, 0x00}, + {0xc232, 0x00}, + {0xc233, 0x00}, + {0xc240, 0x00}, + {0xc241, 0x00}, + {0xc242, 0x00}, + {0xc243, 0x00}, + {0xc250, 0x00}, + {0xc251, 0x00}, + {0xc252, 0x00}, + {0xc253, 0x00}, + {0xc260, 0x00}, + {0xc261, 0x00}, + {0xc262, 0x00}, + {0xc263, 0x00}, + {0xc270, 0x00}, + {0xc271, 0x00}, + {0xc272, 0x00}, + {0xc273, 0x00}, + {0xc40e, 0xa0}, + {0xc448, 0x00}, + {0xc46e, 0x01}, + {0xc478, 0x01}, + {0xc49e, 0x1c}, + {0xc49f, 0x30}, + {0xc4a2, 0x3a}, + {0xc4a3, 0x8e}, + {0xc4c1, 0x07}, + {0xc4c2, 0x07}, + {0xc4c3, 0x77}, + {0xc4c4, 0x77}, + {0xc4d2, 0x38}, + {0xc4d3, 0x38}, + {0xc4d4, 0x38}, + {0xc4d5, 0x38}, + {0xc4e3, 0x14}, + {0xc4e9, 0x20}, + {0xc506, 0x14}, + {0xc50e, 0x00}, + {0xc50f, 0x00}, + {0xc510, 0x00}, + {0xc511, 0x00}, + {0xc512, 0x00}, + {0xc513, 0x4e}, + {0xc514, 0x4f}, + {0xc515, 0x2a}, + {0xc516, 0x16}, + {0xc517, 0x0b}, + {0xc518, 0x33}, + {0xc519, 0x33}, + {0xc51a, 0x33}, + {0xc51b, 0x33}, + {0xc51c, 0x33}, + {0xc51d, 0x37}, + {0xc51e, 0x37}, + {0xc51f, 0x3a}, + {0xc520, 0x3a}, + {0xc521, 0x3a}, + {0xc52e, 0x0e}, + {0xc52f, 0x0e}, + {0xc530, 0x0e}, + {0xc531, 0x0e}, + {0xc532, 0x0e}, + {0xc533, 0x0e}, + {0xc534, 0x0e}, + {0xc535, 0x0e}, + {0xc542, 0x0e}, + {0xc543, 0x0e}, + {0xc544, 0x0e}, + {0xc545, 0x0e}, + {0xc546, 0x0e}, + {0xc547, 0x0e}, + {0xc548, 0x0e}, + {0xc549, 0x0e}, + {0xc57d, 0x80}, + {0xc581, 0x18}, + {0xc582, 0x18}, + {0xc583, 0x01}, + {0xc584, 0x01}, + {0xc587, 0x18}, + {0xc589, 0x18}, + {0xc58a, 0x0c}, + {0xc58b, 0x08}, + {0xc58c, 0x04}, + {0xc58f, 0x28}, + {0xc590, 0x28}, + {0xc591, 0x28}, + {0xc592, 0x28}, + {0xc593, 0x04}, + {0xc594, 0x04}, + {0xc597, 0x2c}, + {0xc598, 0x2c}, + {0xc599, 0x2c}, + {0xc59a, 0x28}, + {0xc59b, 0x20}, + {0xc59c, 0x18}, + {0xc5e4, 0x00}, + {0xc5e5, 0x01}, + {0xc5e8, 0x01}, + {0xc702, 0x00}, + {0xc726, 0x03}, + {0xc72b, 0xff}, + {0xc72c, 0xff}, + {0xc72d, 0xff}, + {0xc72f, 0x08}, + {0xc736, 0x01}, + {0xc739, 0x18}, + {0xc73a, 0x49}, + {0xc73b, 0x92}, + {0xc73c, 0x24}, + {0xc746, 0x01}, + {0xc747, 0x04}, + {0xc749, 0x1c}, + {0xc75b, 0x01}, + {0xc75c, 0x05}, + {0xc765, 0x2a}, + {0xc773, 0x02}, + {0xc774, 0x03}, + {0xc78a, 0x03}, + {0xc78b, 0x04}, + {0xc798, 0x03}, + {0xc7a2, 0x01}, + {0xc7a6, 0x02}, + {0xc7a7, 0xff}, + {0xc7a8, 0xff}, + {0xc7a9, 0xff}, + {0xc7aa, 0xff}, + {0xc7ac, 0x02}, + {0xc7ad, 0xff}, + {0xc7ae, 0xff}, + {0xc7af, 0xff}, + {0xc7b0, 0xff}, + {0xc7b2, 0x01}, + {0xc7b3, 0xff}, + {0xc7b4, 0xff}, + {0xc7b5, 0xff}, + {0xc7b6, 0xff}, + {0xc7c4, 0x00}, + {0xc7c5, 0xff}, + {0xc7e2, 0x01}, + {0xc855, 0x07}, + {0xc8a4, 0x07}, + {0xc95a, 0x77}, + {0xc95b, 0x77}, + {0xc9b9, 0x28}, + {0xc9fe, 0x0a}, + {0xc9ff, 0x0e}, + {0xca00, 0x1a}, + {0xca02, 0x1a}, + {0xca17, 0x03}, + {0xca18, 0x1a}, + {0xca19, 0x1a}, + {0x3501, 0x0c}, + {0x3502, 0x00}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {REG_NULL, 0x00}, +}; + +static const struct other_data ov50h40_spd = { + .width = 4096, + .height = 768, + .bus_fmt = MEDIA_BUS_FMT_SPD_2X8, + .data_type = 0x19, + .data_bit = 10, +}; + +/* + * The width and height must be configured to be + * the same as the current output resolution of the sensor. + * The input width of the isp needs to be 16 aligned. + * The input height of the isp needs to be 8 aligned. + * If the width or height does not meet the alignment rules, + * you can configure the cropping parameters with the following function to + * crop out the appropriate resolution. + * struct v4l2_subdev_pad_ops { + * .get_selection + * } + */ +static const struct ov50h40_mode supported_modes_dphy[] = { + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 4096, + .height = 3072, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0840, + .hts_def = 0x41a * 4, + .vts_def = 0x0c66, + .mipi_freq_idx = 2, + .bpp = 10, + .reg_list = ov50h40_10bit_4096x3072_dphy_30fps_regs, + .hdr_mode = NO_HDR, + .spd = &ov50h40_spd, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 8192, + .height = 6144, + .max_fps = { + .numerator = 10000, + .denominator = 120000, + }, + .exp_def = 0x0240, + .hts_def = 0x9f6 * 4, + .vts_def = 0x0cc3 * 2, + .mipi_freq_idx = 3, + .bpp = 10, + .reg_list = ov50h40_10bit_8192x6144_dphy_12fps_regs, + .hdr_mode = NO_HDR, + .spd = &ov50h40_spd, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, +#ifdef DEBUG + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 4096, + .height = 3072, + .max_fps = { + .numerator = 10000, + .denominator = 150000, + }, + .exp_def = 0x0240, + .hts_def = 0x0834 * 4, + .vts_def = 0x0c66, + .mipi_freq_idx = 1, + .bpp = 10, + .reg_list = ov50h40_10bit_4096x3072_dphy_regs, + .hdr_mode = NO_HDR, + .spd = &ov50h40_spd, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 8192, + .height = 6144, + .max_fps = { + .numerator = 10000, + .denominator = 30000, + }, + .exp_def = 0x0240, + .hts_def = 0x09f6 * 4, + .vts_def = 0x0cc3 * 2, + .mipi_freq_idx = 1, + .bpp = 10, + .reg_list = ov50h40_10bit_8192x6144_dphy_regs, + .hdr_mode = NO_HDR, + .spd = &ov50h40_spd, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 4096, + .height = 3072, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0840, + .hts_def = 0x3e8 * 8, + .vts_def = 0x0d05, + .mipi_freq_idx = 2, + .bpp = 10, + .reg_list = ov50h40_10bit_4096x3072_dphy_30fps_nopd_regs, + .hdr_mode = NO_HDR, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, +#endif +}; + +static const struct ov50h40_mode supported_modes_cphy[] = { + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 4096, + .height = 3072, + .max_fps = { + .numerator = 10000, + .denominator = 150000, + }, + .exp_def = 0x0C00, + .hts_def = 0x044c, + .vts_def = 0x08e0, + .mipi_freq_idx = 0, + .bpp = 10, + .reg_list = ov50h40_10bit_4096x3072_cphy_regs, + .hdr_mode = NO_HDR, + .spd = &ov50h40_spd, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 4096, + .height = 3072, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0c00, + .hts_def = 0x044c, + .vts_def = 0x08e0, + .mipi_freq_idx = 2, + .bpp = 10, + .reg_list = ov50h40_10bit_4096x3072_cphy_30fps_regs, + .hdr_mode = NO_HDR, + .spd = &ov50h40_spd, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10, + .width = 8192, + .height = 6144, + .max_fps = { + .numerator = 10000, + .denominator = 120000, + }, + .exp_def = 0x0c00, + .hts_def = 0x0306, + .vts_def = 0x0c96, + .mipi_freq_idx = 3, + .bpp = 10, + .reg_list = ov50h40_10bit_8192x6144_cphy_30fps_regs, + .hdr_mode = NO_HDR, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, +}; + +static const s64 link_freq_items[] = { + MIPI_FREQ_356M, + MIPI_FREQ_384M, + MIPI_FREQ_750M, + MIPI_FREQ_1250M, +}; + +static const char * const ov50h40_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +static int __ov50h40_power_on(struct ov50h40 *ov50h40); + +/* Write registers up to 4 at a time */ +static int ov50h40_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) { + dev_err(&client->dev, "Failed to write 0x%04x,0x%x\n", reg, val); + return -EIO; + } + return 0; +} + +static int ov50h40_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + ret |= ov50h40_write_reg(client, regs[i].addr, + OV50H40_REG_VALUE_08BIT, regs[i].val); + } + return ret; +} + +/* Read registers up to 4 at a time */ +static int ov50h40_read_reg(struct i2c_client *client, + u16 reg, + unsigned int len, + u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int ov50h40_get_reso_dist(const struct ov50h40_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct ov50h40_mode * +ov50h40_find_best_fit(struct ov50h40 *ov50h40, struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ov50h40->cfg_num; i++) { + dist = ov50h40_get_reso_dist(&ov50h40->support_modes[i], framefmt); + if ((cur_best_fit_dist == -1 || dist < cur_best_fit_dist) && + (ov50h40->support_modes[i].bus_fmt == framefmt->code)) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + dev_info(&ov50h40->client->dev, "%s: cur_best_fit(%d)", + __func__, cur_best_fit); + return &ov50h40->support_modes[cur_best_fit]; +} + +static int ov50h40_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + const struct ov50h40_mode *mode; + s64 h_blank, vblank_def; + u64 pixel_rate = 0; + u32 lane_num = ov50h40->bus_cfg.bus.mipi_csi2.num_data_lanes; + + mutex_lock(&ov50h40->mutex); + + mode = ov50h40_find_best_fit(ov50h40, fmt); + fmt->format.code = mode->bus_fmt; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&ov50h40->mutex); + return -ENOTTY; +#endif + } else { + ov50h40->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(ov50h40->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov50h40->vblank, vblank_def, + OV50H40_VTS_MAX - mode->height, + 1, vblank_def); + __v4l2_ctrl_s_ctrl(ov50h40->vblank, vblank_def); + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * lane_num; + __v4l2_ctrl_s_ctrl_int64(ov50h40->pixel_rate, + pixel_rate); + __v4l2_ctrl_s_ctrl(ov50h40->link_freq, + mode->mipi_freq_idx); + } + dev_info(&ov50h40->client->dev, "%s: mode->mipi_freq_idx(%d)", + __func__, mode->mipi_freq_idx); + + mutex_unlock(&ov50h40->mutex); + + return 0; +} + +static int ov50h40_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + const struct ov50h40_mode *mode = ov50h40->cur_mode; + + mutex_lock(&ov50h40->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&ov50h40->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->bus_fmt; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&ov50h40->mutex); + + return 0; +} + +static int ov50h40_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + + if (code->index != 0) + return -EINVAL; + code->code = ov50h40->cur_mode->bus_fmt; + + return 0; +} + +static int ov50h40_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + + if (fse->index >= ov50h40->cfg_num) + return -EINVAL; + + if (fse->code != ov50h40->support_modes[fse->index].bus_fmt) + return -EINVAL; + + fse->min_width = ov50h40->support_modes[fse->index].width; + fse->max_width = ov50h40->support_modes[fse->index].width; + fse->max_height = ov50h40->support_modes[fse->index].height; + fse->min_height = ov50h40->support_modes[fse->index].height; + + return 0; +} + +static int ov50h40_enable_test_pattern(struct ov50h40 *ov50h40, u32 pattern) +{ + u32 val; + int ret = 0; + + if (pattern) + val = ((pattern - 1) << 4) | OV50H40_TEST_PATTERN_ENABLE; + else + val = OV50H40_TEST_PATTERN_DISABLE; + ret = ov50h40_write_reg(ov50h40->client, OV50H40_REG_TEST_PATTERN, + OV50H40_REG_VALUE_08BIT, val); + return ret; +} + +static int ov50h40_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + const struct ov50h40_mode *mode = ov50h40->cur_mode; + + fi->interval = mode->max_fps; + + return 0; +} + +static int ov50h40_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + u32 lane_num = ov50h40->bus_cfg.bus.mipi_csi2.num_data_lanes; + u32 val = 0; + + val = 1 << (lane_num - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + config->type = ov50h40->bus_cfg.bus_type; + config->flags = val; + + return 0; +} + +static void ov50h40_get_otp(struct otp_info *otp, + struct rkmodule_inf *inf) +{ + u32 i, j; + u32 w, h; + + /* awb */ + if (otp->awb_data.flag) { + inf->awb.flag = 1; + inf->awb.r_value = otp->awb_data.r_ratio; + inf->awb.b_value = otp->awb_data.b_ratio; + inf->awb.gr_value = otp->awb_data.g_ratio; + inf->awb.gb_value = 0x0; + + inf->awb.golden_r_value = otp->awb_data.r_golden; + inf->awb.golden_b_value = otp->awb_data.b_golden; + inf->awb.golden_gr_value = otp->awb_data.g_golden; + inf->awb.golden_gb_value = 0x0; + } + + /* lsc */ + if (otp->lsc_data.flag) { + inf->lsc.flag = 1; + inf->lsc.width = otp->basic_data.size.width; + inf->lsc.height = otp->basic_data.size.height; + inf->lsc.table_size = otp->lsc_data.table_size; + + for (i = 0; i < 289; i++) { + inf->lsc.lsc_r[i] = (otp->lsc_data.data[i * 2] << 8) | + otp->lsc_data.data[i * 2 + 1]; + inf->lsc.lsc_gr[i] = (otp->lsc_data.data[i * 2 + 578] << 8) | + otp->lsc_data.data[i * 2 + 579]; + inf->lsc.lsc_gb[i] = (otp->lsc_data.data[i * 2 + 1156] << 8) | + otp->lsc_data.data[i * 2 + 1157]; + inf->lsc.lsc_b[i] = (otp->lsc_data.data[i * 2 + 1734] << 8) | + otp->lsc_data.data[i * 2 + 1735]; + } + } + + /* pdaf */ + if (otp->pdaf_data.flag) { + inf->pdaf.flag = 1; + inf->pdaf.gainmap_width = otp->pdaf_data.gainmap_width; + inf->pdaf.gainmap_height = otp->pdaf_data.gainmap_height; + inf->pdaf.dcc_mode = otp->pdaf_data.dcc_mode; + inf->pdaf.dcc_dir = otp->pdaf_data.dcc_dir; + inf->pdaf.dccmap_width = otp->pdaf_data.dccmap_width; + inf->pdaf.dccmap_height = otp->pdaf_data.dccmap_height; + w = otp->pdaf_data.gainmap_width; + h = otp->pdaf_data.gainmap_height; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + inf->pdaf.gainmap[i * w + j] = + (otp->pdaf_data.gainmap[(i * w + j) * 2] << 8) | + otp->pdaf_data.gainmap[(i * w + j) * 2 + 1]; + } + } + w = otp->pdaf_data.dccmap_width; + h = otp->pdaf_data.dccmap_height; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + inf->pdaf.dccmap[i * w + j] = + (otp->pdaf_data.dccmap[(i * w + j) * 2] << 8) | + otp->pdaf_data.dccmap[(i * w + j) * 2 + 1]; + } + } + } + + /* af */ + if (otp->af_data.flag) { + inf->af.flag = 1; + inf->af.dir_cnt = 1; + inf->af.af_otp[0].vcm_start = otp->af_data.af_inf; + inf->af.af_otp[0].vcm_end = otp->af_data.af_macro; + inf->af.af_otp[0].vcm_dir = 0; + } + +} + +static void ov50h40_get_module_inf(struct ov50h40 *ov50h40, + struct rkmodule_inf *inf) +{ + struct otp_info *otp = ov50h40->otp; + + memset(inf, 0, sizeof(*inf)); + strscpy(inf->base.sensor, OV50H40_NAME, sizeof(inf->base.sensor)); + strscpy(inf->base.module, ov50h40->module_name, + sizeof(inf->base.module)); + strscpy(inf->base.lens, ov50h40->len_name, sizeof(inf->base.lens)); + if (otp) + ov50h40_get_otp(otp, inf); +} + +static int ov50h40_get_channel_info(struct ov50h40 *ov50h40, struct rkmodule_channel_info *ch_info) +{ + const struct ov50h40_mode *mode = ov50h40->cur_mode; + + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + + if (ch_info->index == ov50h40->spd_id && mode->spd) { + ch_info->vc = V4L2_MBUS_CSI2_CHANNEL_1; + ch_info->width = mode->spd->width; + ch_info->height = mode->spd->height; + ch_info->bus_fmt = mode->spd->bus_fmt; + ch_info->data_type = mode->spd->data_type; + ch_info->data_bit = mode->spd->data_bit; + } else { + ch_info->vc = ov50h40->cur_mode->vc[ch_info->index]; + ch_info->width = ov50h40->cur_mode->width; + ch_info->height = ov50h40->cur_mode->height; + ch_info->bus_fmt = ov50h40->cur_mode->bus_fmt; + } + return 0; +} + +static long ov50h40_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + struct rkmodule_hdr_cfg *hdr_cfg; + struct rkmodule_channel_info *ch_info; + long ret = 0; + u32 i, h, w; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_SET_HDR_CFG: + hdr_cfg = (struct rkmodule_hdr_cfg *)arg; + w = ov50h40->cur_mode->width; + h = ov50h40->cur_mode->height; + for (i = 0; i < ov50h40->cfg_num; i++) { + if (w == ov50h40->support_modes[i].width && + h == ov50h40->support_modes[i].height && + ov50h40->support_modes[i].hdr_mode == hdr_cfg->hdr_mode) { + ov50h40->cur_mode = &ov50h40->support_modes[i]; + break; + } + } + if (i == ov50h40->cfg_num) { + dev_err(&ov50h40->client->dev, + "not find hdr mode:%d %dx%d config\n", + hdr_cfg->hdr_mode, w, h); + ret = -EINVAL; + } else { + w = ov50h40->cur_mode->hts_def - ov50h40->cur_mode->width; + h = ov50h40->cur_mode->vts_def - ov50h40->cur_mode->height; + __v4l2_ctrl_modify_range(ov50h40->hblank, w, w, 1, w); + __v4l2_ctrl_modify_range(ov50h40->vblank, h, + OV50H40_VTS_MAX - ov50h40->cur_mode->height, + 1, h); + dev_info(&ov50h40->client->dev, + "sensor mode: %d\n", + ov50h40->cur_mode->hdr_mode); + } + dev_info(&ov50h40->client->dev, "%s: matched mode index(%d)", + __func__, i); + break; + case RKMODULE_GET_MODULE_INFO: + ov50h40_get_module_inf(ov50h40, (struct rkmodule_inf *)arg); + break; + case RKMODULE_GET_HDR_CFG: + hdr_cfg = (struct rkmodule_hdr_cfg *)arg; + hdr_cfg->esp.mode = HDR_NORMAL_VC; + hdr_cfg->hdr_mode = ov50h40->cur_mode->hdr_mode; + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = ov50h40_write_reg(ov50h40->client, OV50H40_REG_CTRL_MODE, + OV50H40_REG_VALUE_08BIT, OV50H40_MODE_STREAMING); + else + ret = ov50h40_write_reg(ov50h40->client, OV50H40_REG_CTRL_MODE, + OV50H40_REG_VALUE_08BIT, OV50H40_MODE_SW_STANDBY); + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = ov50h40_get_channel_info(ov50h40, ch_info); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long ov50h40_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + struct rkmodule_hdr_cfg *hdr; + struct rkmodule_channel_info *ch_info; + long ret; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = ov50h40_ioctl(sd, cmd, inf); + if (!ret) { + ret = copy_to_user(up, inf, sizeof(*inf)); + if (ret) + ret = -EFAULT; + } + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(cfg, up, sizeof(*cfg)); + if (!ret) + ret = ov50h40_ioctl(sd, cmd, cfg); + else + ret = -EFAULT; + kfree(cfg); + break; + case RKMODULE_GET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = ov50h40_ioctl(sd, cmd, hdr); + if (!ret) { + ret = copy_to_user(up, hdr, sizeof(*hdr)); + if (ret) + ret = -EFAULT; + } + kfree(hdr); + break; + case RKMODULE_SET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(hdr, up, sizeof(*hdr)); + if (!ret) + ret = ov50h40_ioctl(sd, cmd, hdr); + else + ret = -EFAULT; + kfree(hdr); + break; + case RKMODULE_SET_QUICK_STREAM: + ret = copy_from_user(&stream, up, sizeof(u32)); + if (!ret) + ret = ov50h40_ioctl(sd, cmd, &stream); + else + ret = -EFAULT; + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); + if (!ch_info) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(ch_info, up, sizeof(*ch_info)); + if (ret) { + ret = -EFAULT; + return ret; + } + + ret = ov50h40_ioctl(sd, cmd, ch_info); + if (!ret) { + ret = copy_to_user(up, ch_info, sizeof(*ch_info)); + if (ret) + ret = -EFAULT; + } + kfree(ch_info); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __ov50h40_start_stream(struct ov50h40 *ov50h40) +{ + int ret; + + if (!ov50h40->is_thunderboot) { + ret = ov50h40_write_array(ov50h40->client, ov50h40->cur_mode->reg_list); + if (ret) + return ret; + } + + /* In case these controls are set before streaming */ + ret = __v4l2_ctrl_handler_setup(&ov50h40->ctrl_handler); + if (ret) + return ret; + + return ov50h40_write_reg(ov50h40->client, OV50H40_REG_CTRL_MODE, + OV50H40_REG_VALUE_08BIT, OV50H40_MODE_STREAMING); +} + +static int __ov50h40_stop_stream(struct ov50h40 *ov50h40) +{ + if (ov50h40->is_thunderboot) + ov50h40->is_first_streamoff = true; + return ov50h40_write_reg(ov50h40->client, OV50H40_REG_CTRL_MODE, + OV50H40_REG_VALUE_08BIT, OV50H40_MODE_SW_STANDBY); +} + +static int ov50h40_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + struct i2c_client *client = ov50h40->client; + int ret = 0; + + dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on, + ov50h40->cur_mode->width, + ov50h40->cur_mode->height, + DIV_ROUND_CLOSEST(ov50h40->cur_mode->max_fps.denominator, + ov50h40->cur_mode->max_fps.numerator)); + + mutex_lock(&ov50h40->mutex); + on = !!on; + if (on == ov50h40->streaming) + goto unlock_and_return; + + if (on) { + if (ov50h40->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) { + ov50h40->is_thunderboot = false; + __ov50h40_power_on(ov50h40); + } + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __ov50h40_start_stream(ov50h40); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __ov50h40_stop_stream(ov50h40); + pm_runtime_put(&client->dev); + } + + ov50h40->streaming = on; + +unlock_and_return: + mutex_unlock(&ov50h40->mutex); + + return ret; +} + +static int ov50h40_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + struct i2c_client *client = ov50h40->client; + int ret = 0; + + mutex_lock(&ov50h40->mutex); + + /* If the power state is not modified - no work to do. */ + if (ov50h40->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + if (!ov50h40->is_thunderboot) { + ret |= ov50h40_write_reg(ov50h40->client, + OV50H40_SOFTWARE_RESET_REG, + OV50H40_REG_VALUE_08BIT, + 0x01); + usleep_range(100, 200); + } + + ov50h40->power_on = true; + } else { + pm_runtime_put(&client->dev); + ov50h40->power_on = false; + } + +unlock_and_return: + mutex_unlock(&ov50h40->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ov50h40_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, OV50H40_XVCLK_FREQ / 1000 / 1000); +} + +static int __ov50h40_power_on(struct ov50h40 *ov50h40) +{ + int ret; + u32 delay_us; + struct device *dev = &ov50h40->client->dev; + + if (ov50h40->is_thunderboot) + return 0; + + if (!IS_ERR_OR_NULL(ov50h40->pins_default)) { + ret = pinctrl_select_state(ov50h40->pinctrl, + ov50h40->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(ov50h40->xvclk, OV50H40_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(ov50h40->xvclk) != OV50H40_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(ov50h40->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(ov50h40->reset_gpio)) + gpiod_direction_output(ov50h40->reset_gpio, 1); + + ret = regulator_bulk_enable(OV50H40_NUM_SUPPLIES, ov50h40->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(ov50h40->reset_gpio)) + gpiod_direction_output(ov50h40->reset_gpio, 0); + + usleep_range(500, 1000); + if (!IS_ERR(ov50h40->pwdn_gpio)) + gpiod_direction_output(ov50h40->pwdn_gpio, 0); + /* + * There is no need to wait for the delay of RC circuit + * if the reset signal is directly controlled by GPIO. + */ + if (!IS_ERR(ov50h40->reset_gpio)) + usleep_range(8000, 10000); + else + usleep_range(12000, 16000); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = ov50h40_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(ov50h40->xvclk); + + return ret; +} + +static void __ov50h40_power_off(struct ov50h40 *ov50h40) +{ + int ret; + struct device *dev = &ov50h40->client->dev; + + if (ov50h40->is_thunderboot) { + if (ov50h40->is_first_streamoff) { + ov50h40->is_thunderboot = false; + ov50h40->is_first_streamoff = false; + } else { + return; + } + } + + if (!IS_ERR(ov50h40->pwdn_gpio)) + gpiod_direction_output(ov50h40->pwdn_gpio, 1); + + clk_disable_unprepare(ov50h40->xvclk); + + if (!IS_ERR(ov50h40->reset_gpio)) + gpiod_direction_output(ov50h40->reset_gpio, 1); + if (!IS_ERR_OR_NULL(ov50h40->pins_sleep)) { + ret = pinctrl_select_state(ov50h40->pinctrl, + ov50h40->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + + if (ov50h40->is_thunderboot_ng) { + ov50h40->is_thunderboot_ng = false; + } + regulator_bulk_disable(OV50H40_NUM_SUPPLIES, ov50h40->supplies); +} + +static int __maybe_unused ov50h40_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov50h40 *ov50h40 = to_ov50h40(sd); + + return __ov50h40_power_on(ov50h40); +} + +static int __maybe_unused ov50h40_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov50h40 *ov50h40 = to_ov50h40(sd); + + __ov50h40_power_off(ov50h40); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int ov50h40_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct ov50h40_mode *def_mode = &ov50h40->support_modes[0]; + + mutex_lock(&ov50h40->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = def_mode->bus_fmt; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&ov50h40->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int ov50h40_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + + if (fie->index >= ov50h40->cfg_num) + return -EINVAL; + + fie->code = ov50h40->support_modes[fie->index].bus_fmt; + fie->width = ov50h40->support_modes[fie->index].width; + fie->height = ov50h40->support_modes[fie->index].height; + fie->interval = ov50h40->support_modes[fie->index].max_fps; + fie->reserved[0] = ov50h40->support_modes[fie->index].hdr_mode; + return 0; +} +//#define RK356X_TEST +#ifdef RK356X_TEST +#define CROP_START(SRC, DST) (((SRC) - (DST)) / 2 / 4 * 4) +#define DST_WIDTH 4096 +#define DST_HEIGHT 2304 +static int ov50h40_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct ov50h40 *ov50h40 = to_ov50h40(sd); + + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + sel->r.left = CROP_START(ov50h40->cur_mode->width, DST_WIDTH); + sel->r.width = DST_WIDTH; + sel->r.top = CROP_START(ov50h40->cur_mode->height, DST_HEIGHT); + sel->r.height = DST_HEIGHT; + return 0; + } + return -EINVAL; +} +#endif + +static const struct dev_pm_ops ov50h40_pm_ops = { + SET_RUNTIME_PM_OPS(ov50h40_runtime_suspend, + ov50h40_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops ov50h40_internal_ops = { + .open = ov50h40_open, +}; +#endif + +static const struct v4l2_subdev_core_ops ov50h40_core_ops = { + .s_power = ov50h40_s_power, + .ioctl = ov50h40_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov50h40_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops ov50h40_video_ops = { + .s_stream = ov50h40_s_stream, + .g_frame_interval = ov50h40_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops ov50h40_pad_ops = { + .enum_mbus_code = ov50h40_enum_mbus_code, + .enum_frame_size = ov50h40_enum_frame_sizes, + .enum_frame_interval = ov50h40_enum_frame_interval, + .get_fmt = ov50h40_get_fmt, + .set_fmt = ov50h40_set_fmt, +#ifdef RK356X_TEST + .get_selection = ov50h40_get_selection, +#endif + .get_mbus_config = ov50h40_g_mbus_config, +}; + +static const struct v4l2_subdev_ops ov50h40_subdev_ops = { + .core = &ov50h40_core_ops, + .video = &ov50h40_video_ops, + .pad = &ov50h40_pad_ops, +}; + +static int ov50h40_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov50h40 *ov50h40 = container_of(ctrl->handler, + struct ov50h40, ctrl_handler); + struct i2c_client *client = ov50h40->client; + s64 max; + int ret = 0; + u32 again, dgain; + u32 val = 0; + u32 vts = 0; + u32 exp = 0; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + if (ov50h40->cur_mode->height == 6144) + max = ov50h40->cur_mode->height + ctrl->val - 44; + else + max = ov50h40->cur_mode->height + ctrl->val - 22; + __v4l2_ctrl_modify_range(ov50h40->exposure, + ov50h40->exposure->minimum, max, + ov50h40->exposure->step, + ov50h40->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if (ov50h40->cur_mode->height == 6144) + exp = ctrl->val / 2; + else + exp = ctrl->val; + ret = ov50h40_write_reg(ov50h40->client, + OV50H40_REG_EXP_LONG_H, + OV50H40_REG_VALUE_24BIT, + exp); + dev_dbg(&client->dev, "set exposure 0x%x\n", + ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + if (ctrl->val > 1984) { + dgain = ctrl->val * 1024 / 1984; + again = 1984; + } else { + dgain = 1024; + again = ctrl->val; + } + ret = ov50h40_write_reg(ov50h40->client, + OV50H40_REG_AGAIN_LONG_H, + OV50H40_REG_VALUE_16BIT, + (again << 1) & 0x7ffe); + ret |= ov50h40_write_reg(ov50h40->client, + OV50H40_REG_DGAIN_LONG_H, + OV50H40_REG_VALUE_24BIT, + (dgain << 6) & 0xfffc0); + dev_dbg(&client->dev, "set analog gain 0x%x\n", + ctrl->val); + break; + case V4L2_CID_VBLANK: + vts = ctrl->val + ov50h40->cur_mode->height; + if (ov50h40->cur_mode->height == 6144) + vts /= 2; + ret = ov50h40_write_reg(ov50h40->client, OV50H40_REG_VTS, + OV50H40_REG_VALUE_16BIT, + vts); + dev_dbg(&client->dev, "set vblank 0x%x\n", + ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov50h40_enable_test_pattern(ov50h40, ctrl->val); + break; + case V4L2_CID_HFLIP: + ret = ov50h40_read_reg(ov50h40->client, OV50H40_MIRROR_REG, + OV50H40_REG_VALUE_08BIT, + &val); + if (ctrl->val) + val |= FLIP_BIT_MASK; + else + val &= ~FLIP_BIT_MASK; + ret = ov50h40_write_reg(ov50h40->client, OV50H40_MIRROR_REG, + OV50H40_REG_VALUE_08BIT, + val); + break; + case V4L2_CID_VFLIP: + ret = ov50h40_read_reg(ov50h40->client, OV50H40_FLIP_REG, + OV50H40_REG_VALUE_08BIT, + &val); + if (ctrl->val) + val |= FLIP_BIT_MASK; + else + val &= ~FLIP_BIT_MASK; + ret = ov50h40_write_reg(ov50h40->client, OV50H40_FLIP_REG, + OV50H40_REG_VALUE_08BIT, + val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops ov50h40_ctrl_ops = { + .s_ctrl = ov50h40_set_ctrl, +}; + +static int ov50h40_initialize_controls(struct ov50h40 *ov50h40) +{ + const struct ov50h40_mode *mode; + struct v4l2_ctrl_handler *handler; + s64 exposure_max, vblank_def; + u32 h_blank; + int ret; + u64 dst_pixel_rate = 0; + u32 lane_num = ov50h40->bus_cfg.bus.mipi_csi2.num_data_lanes; + + handler = &ov50h40->ctrl_handler; + mode = ov50h40->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 9); + if (ret) + return ret; + handler->lock = &ov50h40->mutex; + + ov50h40->link_freq = v4l2_ctrl_new_int_menu(handler, NULL, + V4L2_CID_LINK_FREQ, + 3, 0, link_freq_items); + + dst_pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * lane_num; + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + ov50h40->pixel_rate = v4l2_ctrl_new_std(handler, NULL, + V4L2_CID_PIXEL_RATE, + 0, PIXEL_RATE_WITH_1250M, + 1, dst_pixel_rate); + + __v4l2_ctrl_s_ctrl(ov50h40->link_freq, + mode->mipi_freq_idx); + + h_blank = mode->hts_def - mode->width; + ov50h40->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (ov50h40->hblank) + ov50h40->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + ov50h40->vblank = v4l2_ctrl_new_std(handler, &ov50h40_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + OV50H40_VTS_MAX - mode->height, + 1, vblank_def); + if (mode->height == 6144) + exposure_max = mode->vts_def - 44; + else + exposure_max = mode->vts_def - 22; + ov50h40->exposure = v4l2_ctrl_new_std(handler, &ov50h40_ctrl_ops, + V4L2_CID_EXPOSURE, OV50H40_EXPOSURE_MIN, + exposure_max, OV50H40_EXPOSURE_STEP, + mode->exp_def); + + ov50h40->anal_gain = v4l2_ctrl_new_std(handler, &ov50h40_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, OV50H40_GAIN_MIN, + OV50H40_GAIN_MAX, OV50H40_GAIN_STEP, + OV50H40_GAIN_DEFAULT); + + ov50h40->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &ov50h40_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov50h40_test_pattern_menu) - 1, + 0, 0, ov50h40_test_pattern_menu); + + ov50h40->h_flip = v4l2_ctrl_new_std(handler, &ov50h40_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + ov50h40->v_flip = v4l2_ctrl_new_std(handler, &ov50h40_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + if (handler->error) { + ret = handler->error; + dev_err(&ov50h40->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + ov50h40->subdev.ctrl_handler = handler; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int ov50h40_check_sensor_id(struct ov50h40 *ov50h40, + struct i2c_client *client) +{ + struct device *dev = &ov50h40->client->dev; + u32 id = 0; + int ret; + + if (ov50h40->is_thunderboot) { + dev_info(dev, "Enable thunderboot mode, skip sensor id check\n"); + return 0; + } + + ret = ov50h40_read_reg(client, OV50H40_REG_CHIP_ID, + OV50H40_REG_VALUE_24BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + + dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID); + + return 0; +} + +static int ov50h40_configure_regulators(struct ov50h40 *ov50h40) +{ + unsigned int i; + + for (i = 0; i < OV50H40_NUM_SUPPLIES; i++) + ov50h40->supplies[i].supply = ov50h40_supply_names[i]; + + return devm_regulator_bulk_get(&ov50h40->client->dev, + OV50H40_NUM_SUPPLIES, + ov50h40->supplies); +} + +static int ov50h40_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct ov50h40 *ov50h40; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + struct device_node *endpoint; + struct device_node *eeprom_ctrl_node; + struct i2c_client *eeprom_ctrl_client; + struct v4l2_subdev *eeprom_ctrl; + struct otp_info *otp_ptr; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + ov50h40 = devm_kzalloc(dev, sizeof(*ov50h40), GFP_KERNEL); + if (!ov50h40) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov50h40->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov50h40->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov50h40->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov50h40->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + ov50h40->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP); + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "Failed to get endpoint\n"); + return -EINVAL; + } + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), + &ov50h40->bus_cfg); + if (ov50h40->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY) { + ov50h40->support_modes = supported_modes_dphy; + ov50h40->cfg_num = ARRAY_SIZE(supported_modes_dphy); + } else { + ov50h40->support_modes = supported_modes_cphy; + ov50h40->cfg_num = ARRAY_SIZE(supported_modes_cphy); + } + + ov50h40->client = client; + ov50h40->cur_mode = &ov50h40->support_modes[0]; + + ov50h40->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ov50h40->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + ov50h40->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS); + if (IS_ERR(ov50h40->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + ov50h40->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_ASIS); + if (IS_ERR(ov50h40->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = of_property_read_u32(node, + "rockchip,spd-id", + &ov50h40->spd_id); + if (ret != 0) { + ov50h40->spd_id = PAD_MAX; + dev_err(dev, + "failed get spd_id, will not to use spd\n"); + } + + ov50h40->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(ov50h40->pinctrl)) { + ov50h40->pins_default = + pinctrl_lookup_state(ov50h40->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(ov50h40->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + ov50h40->pins_sleep = + pinctrl_lookup_state(ov50h40->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(ov50h40->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } else { + dev_err(dev, "no pinctrl\n"); + } + + ret = ov50h40_configure_regulators(ov50h40); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&ov50h40->mutex); + + sd = &ov50h40->subdev; + v4l2_i2c_subdev_init(sd, client, &ov50h40_subdev_ops); + ret = ov50h40_initialize_controls(ov50h40); + if (ret) + goto err_destroy_mutex; + ret = __ov50h40_power_on(ov50h40); + if (ret) + goto err_free_handler; + + ret = ov50h40_check_sensor_id(ov50h40, client); + if (ret) + goto err_power_off; + eeprom_ctrl_node = of_parse_phandle(node, "eeprom-ctrl", 0); + if (eeprom_ctrl_node) { + eeprom_ctrl_client = + of_find_i2c_device_by_node(eeprom_ctrl_node); + of_node_put(eeprom_ctrl_node); + if (IS_ERR_OR_NULL(eeprom_ctrl_client)) { + dev_err(dev, "can not get node\n"); + goto continue_probe; + } + eeprom_ctrl = i2c_get_clientdata(eeprom_ctrl_client); + if (IS_ERR_OR_NULL(eeprom_ctrl)) { + dev_err(dev, "can not get eeprom i2c client\n"); + } else { + otp_ptr = devm_kzalloc(dev, sizeof(*otp_ptr), GFP_KERNEL); + if (!otp_ptr) + return -ENOMEM; + ret = v4l2_subdev_call(eeprom_ctrl, + core, ioctl, 0, otp_ptr); + if (!ret) { + ov50h40->otp = otp_ptr; + } else { + ov50h40->otp = NULL; + devm_kfree(dev, otp_ptr); + } + } + } +continue_probe: + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &ov50h40_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + ov50h40->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov50h40->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(ov50h40->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov50h40->module_index, facing, + OV50H40_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __ov50h40_power_off(ov50h40); +err_free_handler: + v4l2_ctrl_handler_free(&ov50h40->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ov50h40->mutex); + + return ret; +} + +static int ov50h40_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov50h40 *ov50h40 = to_ov50h40(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&ov50h40->ctrl_handler); + mutex_destroy(&ov50h40->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __ov50h40_power_off(ov50h40); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov50h40_of_match[] = { + { .compatible = "ovti,ov50h40" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ov50h40_of_match); +#endif + +static const struct i2c_device_id ov50h40_match_id[] = { + { "ovti,ov50h40", 0 }, + { }, +}; + +static struct i2c_driver ov50h40_i2c_driver = { + .driver = { + .name = OV50H40_NAME, + .pm = &ov50h40_pm_ops, + .of_match_table = of_match_ptr(ov50h40_of_match), + }, + .probe = &ov50h40_probe, + .remove = &ov50h40_remove, + .id_table = ov50h40_match_id, +}; + +#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT +module_i2c_driver(ov50h40_i2c_driver); +#else +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&ov50h40_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&ov50h40_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); +#endif + +MODULE_DESCRIPTION("OmniVision ov50h40 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/rockchip/cif/capture.c b/drivers/media/platform/rockchip/cif/capture.c index 94a2347feb782..8c44b416af946 100644 --- a/drivers/media/platform/rockchip/cif/capture.c +++ b/drivers/media/platform/rockchip/cif/capture.c @@ -5104,8 +5104,11 @@ int rkcif_update_sensor_info(struct rkcif_stream *stream) } stream->cifdev->active_sensor = sensor; + //printf("shingo: sensor = %s. \n", sensor->sd->name); terminal_sensor = &stream->cifdev->terminal_sensor; + //printf("shingo: terminal sensor = %s. \n", terminal_sensor->sd->name); + get_remote_terminal_sensor(stream, &terminal_sensor->sd); if (terminal_sensor->sd) { ret = v4l2_subdev_call(terminal_sensor->sd, pad, get_mbus_config, diff --git a/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c b/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c index 81827de21d9c2..08e562dc8e590 100644 --- a/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c +++ b/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c @@ -491,8 +491,8 @@ static int rockchip_csi2_dphy_fwnode_parse(struct device *dev, return -EINVAL; } - if (vep->bus_type == V4L2_MBUS_CSI2_DPHY) { - config->type = V4L2_MBUS_CSI2_DPHY; + if (vep->bus_type == V4L2_MBUS_CSI2_DPHY || vep->bus_type == V4L2_MBUS_CSI2_CPHY) { + config->type = vep->bus_type; config->flags = vep->bus.mipi_csi2.flags; s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes; } else if (vep->bus_type == V4L2_MBUS_CCP2) { diff --git a/logo.bmp b/logo.bmp index b0fab1583eef2..24c70876c7d00 100644 Binary files a/logo.bmp and b/logo.bmp differ diff --git a/logo_kernel.bmp b/logo_kernel.bmp index b0fab1583eef2..24c70876c7d00 100644 Binary files a/logo_kernel.bmp and b/logo_kernel.bmp differ