From 97e3972e9c7b3288522680f792cadade7c2b3732 Mon Sep 17 00:00:00 2001 From: Ye Zhang Date: Wed, 23 Jul 2025 16:25:08 +0800 Subject: [PATCH 01/11] pinctrl: rockchip: Correctly support rk3308/rk3308b/rk3308bs Signed-off-by: Ye Zhang Change-Id: Ie9e101a28289193515c782736b0d154e0344cef8 --- drivers/pinctrl/pinctrl-rockchip.c | 111 ++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c index 1b9e48d529c1..b632b86b5120 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -615,6 +615,80 @@ static struct rockchip_mux_recalced_data rk3128_mux_recalced_data[] = { }; static struct rockchip_mux_recalced_data rk3308_mux_recalced_data[] = { + { + /* gpio1b6_sel */ + .num = 1, + .pin = 14, + .reg = 0x28, + .bit = 12, + .mask = 0xf + }, { + /* gpio1b7_sel */ + .num = 1, + .pin = 15, + .reg = 0x2c, + .bit = 0, + .mask = 0x3 + }, { + /* gpio1c2_sel */ + .num = 1, + .pin = 18, + .reg = 0x30, + .bit = 4, + .mask = 0xf + }, { + /* gpio1c3_sel */ + .num = 1, + .pin = 19, + .reg = 0x30, + .bit = 8, + .mask = 0xf + }, { + /* gpio1c4_sel */ + .num = 1, + .pin = 20, + .reg = 0x30, + .bit = 12, + .mask = 0xf + }, { + /* gpio1c5_sel */ + .num = 1, + .pin = 21, + .reg = 0x34, + .bit = 0, + .mask = 0xf + }, { + /* gpio1c6_sel */ + .num = 1, + .pin = 22, + .reg = 0x34, + .bit = 4, + .mask = 0xf + }, { + /* gpio1c7_sel */ + .num = 1, + .pin = 23, + .reg = 0x34, + .bit = 8, + .mask = 0xf + }, { + /* gpio3b4_sel */ + .num = 3, + .pin = 12, + .reg = 0x68, + .bit = 8, + .mask = 0xf + }, { + /* gpio3b5_sel */ + .num = 3, + .pin = 13, + .reg = 0x68, + .bit = 12, + .mask = 0xf + }, +}; + +static struct rockchip_mux_recalced_data rk3308b_mux_recalced_data[] = { { /* gpio1b6_sel */ .num = 1, @@ -1001,6 +1075,35 @@ static struct rockchip_mux_route_data rk3308_mux_route_data[] = { RK_MUXROUTE_SAME(2, RK_PA4, 3, 0x600, BIT(16 + 2) | BIT(2)), /* pdm-clkm-m2 */ }; +static struct rockchip_mux_route_data rk3308b_mux_route_data[] = { + RK_MUXROUTE_SAME(0, RK_PC3, 1, 0x314, BIT(16 + 0) | BIT(0)), /* rtc_clk */ + RK_MUXROUTE_SAME(1, RK_PC6, 2, 0x314, BIT(16 + 2) | BIT(16 + 3)), /* uart2_rxm0 */ + RK_MUXROUTE_SAME(4, RK_PD2, 2, 0x314, BIT(16 + 2) | BIT(16 + 3) | BIT(2)), /* uart2_rxm1 */ + RK_MUXROUTE_SAME(0, RK_PB7, 2, 0x608, BIT(16 + 8) | BIT(16 + 9)), /* i2c3_sdam0 */ + RK_MUXROUTE_SAME(3, RK_PB4, 2, 0x608, BIT(16 + 8) | BIT(16 + 9) | BIT(8)), /* i2c3_sdam1 */ + RK_MUXROUTE_SAME(2, RK_PA0, 3, 0x608, BIT(16 + 8) | BIT(16 + 9) | BIT(9)), /* i2c3_sdam2 */ + RK_MUXROUTE_SAME(1, RK_PA3, 2, 0x308, BIT(16 + 3)), /* i2s-8ch-1-sclktxm0 */ + RK_MUXROUTE_SAME(1, RK_PA4, 2, 0x308, BIT(16 + 3)), /* i2s-8ch-1-sclkrxm0 */ + RK_MUXROUTE_SAME(1, RK_PB5, 2, 0x308, BIT(16 + 3) | BIT(3)), /* i2s-8ch-1-sclktxm1 */ + RK_MUXROUTE_SAME(1, RK_PB6, 2, 0x308, BIT(16 + 3) | BIT(3)), /* i2s-8ch-1-sclkrxm1 */ + RK_MUXROUTE_SAME(1, RK_PA4, 3, 0x308, BIT(16 + 12) | BIT(16 + 13)), /* pdm-clkm0 */ + RK_MUXROUTE_SAME(1, RK_PB6, 4, 0x308, BIT(16 + 12) | BIT(16 + 13) | BIT(12)), /* pdm-clkm1 */ + RK_MUXROUTE_SAME(2, RK_PA6, 2, 0x308, BIT(16 + 12) | BIT(16 + 13) | BIT(13)), /* pdm-clkm2 */ + RK_MUXROUTE_SAME(2, RK_PA4, 3, 0x600, BIT(16 + 2) | BIT(2)), /* pdm-clkm-m2 */ + RK_MUXROUTE_SAME(3, RK_PB2, 3, 0x314, BIT(16 + 9)), /* spi1_miso */ + RK_MUXROUTE_SAME(2, RK_PA4, 2, 0x314, BIT(16 + 9) | BIT(9)), /* spi1_miso_m1 */ + RK_MUXROUTE_SAME(0, RK_PB3, 3, 0x314, BIT(16 + 10) | BIT(16 + 11)), /* owire_m0 */ + RK_MUXROUTE_SAME(1, RK_PC6, 7, 0x314, BIT(16 + 10) | BIT(16 + 11) | BIT(10)), /* owire_m1 */ + RK_MUXROUTE_SAME(2, RK_PA2, 5, 0x314, BIT(16 + 10) | BIT(16 + 11) | BIT(11)), /* owire_m2 */ + RK_MUXROUTE_SAME(0, RK_PB3, 2, 0x314, BIT(16 + 12) | BIT(16 + 13)), /* can_rxd_m0 */ + RK_MUXROUTE_SAME(1, RK_PC6, 5, 0x314, BIT(16 + 12) | BIT(16 + 13) | BIT(12)), /* can_rxd_m1 */ + RK_MUXROUTE_SAME(2, RK_PA2, 4, 0x314, BIT(16 + 12) | BIT(16 + 13) | BIT(13)), /* can_rxd_m2 */ + RK_MUXROUTE_SAME(1, RK_PC4, 3, 0x314, BIT(16 + 14)), /* mac_rxd0_m0 */ + RK_MUXROUTE_SAME(4, RK_PA2, 2, 0x314, BIT(16 + 14) | BIT(14)), /* mac_rxd0_m1 */ + RK_MUXROUTE_SAME(3, RK_PB4, 4, 0x314, BIT(16 + 15)), /* uart3_rx */ + RK_MUXROUTE_SAME(0, RK_PC1, 3, 0x314, BIT(16 + 15) | BIT(15)), /* uart3_rx_m1 */ +}; + static struct rockchip_mux_route_data rk3328_mux_route_data[] = { RK_MUXROUTE_SAME(1, RK_PA1, 2, 0x50, BIT(16) | BIT(16 + 1)), /* uart2dbg_rxm0 */ RK_MUXROUTE_SAME(2, RK_PA1, 1, 0x50, BIT(16) | BIT(16 + 1) | BIT(0)), /* uart2dbg_rxm1 */ @@ -4853,6 +4956,12 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data( match = of_match_node(rockchip_pinctrl_dt_match, node); ctrl = (struct rockchip_pin_ctrl *)match->data; + if (IS_ENABLED(CONFIG_CPU_RK3308) && (soc_is_rk3308b() || soc_is_rk3308bs())) { + ctrl->iomux_recalced = rk3308b_mux_recalced_data; + ctrl->niomux_recalced = ARRAY_SIZE(rk3308b_mux_recalced_data); + ctrl->iomux_routes = rk3308b_mux_route_data; + ctrl->niomux_routes = ARRAY_SIZE(rk3308b_mux_route_data); + } if (IS_ENABLED(CONFIG_CPU_RK3308) && soc_is_rk3308bs()) ctrl->pin_banks = rk3308bs_pin_banks; if (IS_ENABLED(CONFIG_CPU_PX30) && soc_is_px30s()) @@ -5117,7 +5226,7 @@ static int rockchip_pinctrl_probe(struct platform_device *pdev) /* try to find the optional reference to the rmio syscon */ info->regmap_rmio = syscon_regmap_lookup_by_phandle_optional(np, "rockchip,rmio"); - if (IS_ENABLED(CONFIG_CPU_RK3308) && ctrl->type == RK3308) { + if (IS_ENABLED(CONFIG_CPU_RK3308) && (soc_is_rk3308b() || soc_is_rk3308bs())) { ret = rk3308_soc_data_init(info); if (ret) return ret; From b2df67f15d6fadd7b528659bca17d2433d4fe5df Mon Sep 17 00:00:00 2001 From: Ye Zhang Date: Fri, 25 Jul 2025 16:37:03 +0800 Subject: [PATCH 02/11] soc: rockchip: opp_select: avoid duplicate of_find_property Only searches rockchip,pvtpll-table when bin-specific property is absent. Signed-off-by: Ye Zhang Change-Id: Ic728e39851cdd8c32970d81249cf295a3b8d6aeb --- drivers/soc/rockchip/rockchip_opp_select.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/soc/rockchip/rockchip_opp_select.c b/drivers/soc/rockchip/rockchip_opp_select.c index 40898c39c85a..53d80acaea8e 100644 --- a/drivers/soc/rockchip/rockchip_opp_select.c +++ b/drivers/soc/rockchip/rockchip_opp_select.c @@ -1411,10 +1411,10 @@ static void rockchip_init_pvtpll_table(struct device *dev, "rockchip,pvtpll-table-B%d", info->bin); prop = of_find_property(np, prop_name, NULL); } - if (!prop) + if (!prop) { sprintf(prop_name, "rockchip,pvtpll-table"); - - prop = of_find_property(np, prop_name, NULL); + prop = of_find_property(np, prop_name, NULL); + } if (!prop) goto out; From 9f6237f0868cf6cad69727bbdc1481d461c23173 Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Thu, 3 Jul 2025 15:32:17 +0800 Subject: [PATCH 03/11] media: i2c: ov13b10: add ov13b10 sensor driver Change-Id: I428d778ce7c4e853ea9e5728ec8e0cbb04c7735f Signed-off-by: Jianwei Fan Signed-off-by: Wang Panzhenzhuan --- drivers/media/i2c/ov13b10.c | 2669 ++++++++++++++++++++--------------- 1 file changed, 1504 insertions(+), 1165 deletions(-) diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c index 368a3c2bfe34..863a3b746c03 100644 --- a/drivers/media/i2c/ov13b10.c +++ b/drivers/media/i2c/ov13b10.c @@ -1,128 +1,459 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2021 Intel Corporation. +/* + * ov13b10 driver + * + * Copyright (C) 2025 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 fix default output 3840x2160 issue + */ -#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 "otp_eeprom.h" + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x05) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define OV13B10_LINK_FREQ_1120MHZ 560000000 //1120MHz +#define OV13B10_PIXEL_RATE (OV13B10_LINK_FREQ_1120MHZ / 10 * 2 * 4) + +#define OV13B10_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x00560d +#define OV13B10_REG_CHIP_ID 0x300a + +#define OV13B10_REG_CTRL_MODE 0x0100 +#define OV13B10_MODE_SW_STANDBY 0x0 +#define OV13B10_MODE_STREAMING BIT(0) + +#define OV13B10_REG_EXPOSURE_H 0x3500 +#define OV13B10_REG_EXPOSURE_M 0x3501 +#define OV13B10_REG_EXPOSURE_L 0x3502 +#define OV13B10_EXPOSURE_MIN 4 +#define OV13B10_EXPOSURE_STEP 1 +#define OV13B10_VTS_MAX 0x7fff + +#define OV13B10_REG_GAIN_H 0x3508 +#define OV13B10_REG_GAIN_L 0x3509 +#define OV13B10_GAIN_H_MASK 0x0f +#define OV13B10_GAIN_H_SHIFT 8 +#define OV13B10_GAIN_L_MASK 0xff +#define OV13B10_GAIN_MIN 0x100 +#define OV13B10_GAIN_MAX 0x1f7f +#define OV13B10_GAIN_STEP 1 +#define OV13B10_GAIN_DEFAULT 0x100 + +#define OV13B10_REG_TEST_PATTERN 0x5080 +#define OV13B10_TEST_PATTERN_ENABLE 0x80 +#define OV13B10_TEST_PATTERN_DISABLE 0x0 + +#define OV13B10_REG_VTS 0x380e + +#define REG_NULL 0xFFFF #define OV13B10_REG_VALUE_08BIT 1 #define OV13B10_REG_VALUE_16BIT 2 #define OV13B10_REG_VALUE_24BIT 3 -#define OV13B10_REG_MODE_SELECT 0x0100 -#define OV13B10_MODE_STANDBY 0x00 -#define OV13B10_MODE_STREAMING 0x01 +#define OV13B10_LANES 4 +#define OV13B10_BITS_PER_SAMPLE 10 -#define OV13B10_REG_SOFTWARE_RST 0x0103 -#define OV13B10_SOFTWARE_RST 0x01 +#define OV13B10_CHIP_REVISION_REG 0x302A +#define OV13B10_R1A 0xb1 +#define OV13B10_R2A 0xb2 -/* Chip ID */ -#define OV13B10_REG_CHIP_ID 0x300a -#define OV13B10_CHIP_ID 0x560d42 +#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" -/* V_TIMING internal */ -#define OV13B10_REG_VTS 0x380e -#define OV13B10_VTS_30FPS 0x0c7c -#define OV13B10_VTS_60FPS 0x063e -#define OV13B10_VTS_MAX 0x7fff +#define OV13B10_NAME "ov13b10" -/* HBLANK control - read only */ -#define OV13B10_PPL_560MHZ 4704 +#define OV13B10_REG_DGAIN_H 0x350A +#define OV13B10_REG_DGAIN_M 0x350B +#define OV13B10_REG_DGAIN_L 0x350C -/* Exposure control */ -#define OV13B10_REG_EXPOSURE 0x3500 -#define OV13B10_EXPOSURE_MIN 4 -#define OV13B10_EXPOSURE_STEP 1 -#define OV13B10_EXPOSURE_DEFAULT 0x40 +static const struct regval *ov13b10_global_regs; -/* Analog gain control */ -#define OV13B10_REG_ANALOG_GAIN 0x3508 -#define OV13B10_ANA_GAIN_MIN 0x80 -#define OV13B10_ANA_GAIN_MAX 0x07c0 -#define OV13B10_ANA_GAIN_STEP 1 -#define OV13B10_ANA_GAIN_DEFAULT 0x80 +/* usb supply power */ +static const char * const ov13b10_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; -/* Digital gain control */ -#define OV13B10_REG_DGTL_GAIN_H 0x350a -#define OV13B10_REG_DGTL_GAIN_M 0x350b -#define OV13B10_REG_DGTL_GAIN_L 0x350c +#define OV13B10_NUM_SUPPLIES ARRAY_SIZE(ov13b10_supply_names) -#define OV13B10_DGTL_GAIN_MIN 1024 /* Min = 1 X */ -#define OV13B10_DGTL_GAIN_MAX (4096 - 1) /* Max = 4 X */ -#define OV13B10_DGTL_GAIN_DEFAULT 2560 /* Default gain = 2.5 X */ -#define OV13B10_DGTL_GAIN_STEP 1 /* Each step = 1/1024 */ - -#define OV13B10_DGTL_GAIN_L_SHIFT 6 -#define OV13B10_DGTL_GAIN_L_MASK 0x3 -#define OV13B10_DGTL_GAIN_M_SHIFT 2 -#define OV13B10_DGTL_GAIN_M_MASK 0xff -#define OV13B10_DGTL_GAIN_H_SHIFT 10 -#define OV13B10_DGTL_GAIN_H_MASK 0x3 - -/* Test Pattern Control */ -#define OV13B10_REG_TEST_PATTERN 0x5080 -#define OV13B10_TEST_PATTERN_ENABLE BIT(7) -#define OV13B10_TEST_PATTERN_MASK 0xf3 -#define OV13B10_TEST_PATTERN_BAR_SHIFT 2 - -/* Flip Control */ -#define OV13B10_REG_FORMAT1 0x3820 -#define OV13B10_REG_FORMAT2 0x3821 - -/* Horizontal Window Offset */ -#define OV13B10_REG_H_WIN_OFFSET 0x3811 - -/* Vertical Window Offset */ -#define OV13B10_REG_V_WIN_OFFSET 0x3813 - -struct ov13b10_reg { - u16 address; +struct regval { + u16 addr; u8 val; }; -struct ov13b10_reg_list { - u32 num_of_regs; - const struct ov13b10_reg *regs; -}; - -/* Link frequency config */ -struct ov13b10_link_freq_config { - u32 pixels_per_line; - - /* registers for this link frequency */ - struct ov13b10_reg_list reg_list; -}; - -/* Mode : resolution and related config&values */ struct ov13b10_mode { - /* Frame width */ u32 width; - /* Frame height */ u32 height; - - /* V-timing */ + struct v4l2_fract max_fps; + u32 hts_def; u32 vts_def; - u32 vts_min; - - /* Index of Link frequency config to be used */ - u32 link_freq_index; - /* Default register values */ - struct ov13b10_reg_list reg_list; + u32 exp_def; + u32 hdr_mode; + const struct regval *reg_list; + u32 vc[PAD_MAX]; }; -/* 4208x3120 needs 1120Mbps/lane, 4 lanes */ -static const struct ov13b10_reg mipi_data_rate_1120mbps[] = { +struct ov13b10 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *power_gpio; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct gpio_desc *camled_gpio; + struct regulator_bulk_data supplies[OV13B10_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct mutex mutex; + bool streaming; + bool power_on; + const struct ov13b10_mode *cur_mode; + u32 cfg_num; + u32 module_index; + u32 cur_width; + const char *module_facing; + const char *module_name; + const char *len_name; + struct otp_info *otp; +}; + +#define to_ov13b10(sd) container_of(sd, struct ov13b10, subdev) + +/* + * Xclk 24Mhz + */ +static const struct regval ov13b10_global_regs_r1a[] = { {0x0103, 0x01}, - {0x0303, 0x04}, - {0x0305, 0xaf}, + {0x0303, 0x01}, + {0x0305, 0x46}, {0x0321, 0x00}, {0x0323, 0x04}, {0x0324, 0x01}, - {0x0325, 0xa4}, + {0x0325, 0x50}, + {0x0326, 0x81}, + {0x0327, 0x04}, + {0x3012, 0x07}, + {0x3013, 0x32}, + {0x3107, 0x23}, + {0x3501, 0x0c}, + {0x3502, 0x10}, + {0x3504, 0x08}, + {0x3508, 0x07}, + {0x3509, 0xf0}, + {0x3600, 0x16}, + {0x3601, 0x54}, + {0x3612, 0x4e}, + {0x3620, 0x00}, + {0x3621, 0x68}, + {0x3622, 0x66}, + {0x3623, 0x03}, + {0x3662, 0x92}, + {0x3666, 0xbb}, + {0x3667, 0x44}, + {0x366e, 0xff}, + {0x366f, 0xf3}, + {0x3675, 0x44}, + {0x3676, 0x00}, + {0x367f, 0xe9}, + {0x3681, 0x1f}, + {0x3682, 0x1f}, + {0x3683, 0x0b}, + {0x3684, 0x0b}, + {0x3704, 0x0f}, + {0x3706, 0x40}, + {0x3708, 0x3b}, + {0x3709, 0x72}, + {0x370b, 0xa2}, + {0x3714, 0x24}, + {0x371a, 0x3e}, + {0x3725, 0x42}, + {0x3739, 0x12}, + {0x3767, 0x00}, + {0x377a, 0x0d}, + {0x3789, 0x18}, + {0x3790, 0x40}, + {0x3791, 0xa2}, + {0x37c2, 0x04}, + {0x37c3, 0xf1}, + {0x37d9, 0x0c}, + {0x37da, 0x02}, + {0x37dc, 0x02}, + {0x37e1, 0x04}, + {0x37e2, 0x0a}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x08}, + {0x3804, 0x10}, + {0x3805, 0x8f}, + {0x3806, 0x0c}, + {0x3807, 0x47}, + {0x3808, 0x10}, + {0x3809, 0x70}, + {0x380a, 0x0c}, + {0x380b, 0x30}, + {0x380c, 0x04}, + {0x380d, 0x98}, + {0x380e, 0x0c}, + {0x380f, 0x7c}, + {0x3811, 0x0f}, + {0x3813, 0x08}, + {0x3814, 0x01}, + {0x3815, 0x01}, + {0x3816, 0x01}, + {0x3817, 0x01}, + {0x381f, 0x08}, + {0x3820, 0x88}, + {0x3821, 0x00}, + {0x382e, 0xe6}, + {0x3c80, 0x00}, + {0x3c87, 0x01}, + {0x3c8c, 0x19}, + {0x3c8d, 0x1c}, + {0x3ca0, 0x00}, + {0x3ca1, 0x00}, + {0x3ca2, 0x00}, + {0x3ca3, 0x00}, + {0x3ca4, 0x50}, + {0x3ca5, 0x11}, + {0x3ca6, 0x01}, + {0x3ca7, 0x00}, + {0x3ca8, 0x00}, + {0x4008, 0x02}, + {0x4009, 0x0f}, + {0x400a, 0x01}, + {0x400b, 0x19}, + {0x4019, 0x04}, + {0x401a, 0x58}, + {0x4032, 0x1e}, + {0x4050, 0x02}, + {0x4051, 0x09}, + {0x405e, 0x00}, + {0x4066, 0x02}, + {0x4501, 0x00}, + {0x4502, 0x04}, + {0x4505, 0x00}, + {0x4800, 0x64}, + {0x481b, 0x3e}, + {0x481f, 0x30}, + {0x4825, 0x34}, + {0x4837, 0x0e}, + {0x484b, 0x01}, + {0x4883, 0x02}, + {0x5000, 0xff}, + {0x5001, 0x0f}, + {0x5045, 0x20}, + {0x5046, 0x20}, + {0x5047, 0xa4}, + {0x5048, 0x20}, + {0x5049, 0xa4}, + {0x380c, 0x04}, + {0x380d, 0x90}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {0x0305, 0x32}, + {0x4837, 0x14}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + */ +static const struct regval ov13b10_global_regs_r2a[] = { + {0x0103, 0x01}, + {0x0303, 0x01}, + {0x0305, 0x46}, + {0x0321, 0x00}, + {0x0323, 0x04}, + {0x0324, 0x01}, + {0x0325, 0x50}, + {0x0326, 0x81}, + {0x0327, 0x04}, + {0x3012, 0x07}, + {0x3013, 0x32}, + {0x3107, 0x23}, + {0x3501, 0x0c}, + {0x3502, 0x10}, + {0x3504, 0x08}, + {0x3508, 0x07}, + {0x3509, 0xf0}, + {0x3600, 0x16}, + {0x3601, 0x54}, + {0x3612, 0x4e}, + {0x3620, 0x00}, + {0x3621, 0x68}, + {0x3622, 0x66}, + {0x3623, 0x03}, + {0x3662, 0x92}, + {0x3666, 0xbb}, + {0x3667, 0x44}, + {0x366e, 0xff}, + {0x366f, 0xf3}, + {0x3675, 0x44}, + {0x3676, 0x00}, + {0x367f, 0xe9}, + {0x3681, 0x1f}, + {0x3682, 0x1f}, + {0x3683, 0x0b}, + {0x3684, 0x0b}, + {0x3704, 0x0f}, + {0x3706, 0x40}, + {0x3708, 0x3b}, + {0x3709, 0x72}, + {0x370b, 0xa2}, + {0x3714, 0x24}, + {0x371a, 0x3e}, + {0x3725, 0x42}, + {0x3739, 0x12}, + {0x3767, 0x00}, + {0x377a, 0x0d}, + {0x3789, 0x18}, + {0x3790, 0x40}, + {0x3791, 0xa2}, + {0x37c2, 0x04}, + {0x37c3, 0xf1}, + {0x37d9, 0x0c}, + {0x37da, 0x02}, + {0x37dc, 0x02}, + {0x37e1, 0x04}, + {0x37e2, 0x0a}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x08}, + {0x3804, 0x10}, + {0x3805, 0x8f}, + {0x3806, 0x0c}, + {0x3807, 0x47}, + {0x3808, 0x10}, + {0x3809, 0x70}, + {0x380a, 0x0c}, + {0x380b, 0x30}, + {0x380c, 0x04}, + {0x380d, 0x98}, + {0x380e, 0x0c}, + {0x380f, 0x7c}, + {0x3811, 0x0f}, + {0x3813, 0x08}, + {0x3814, 0x01}, + {0x3815, 0x01}, + {0x3816, 0x01}, + {0x3817, 0x01}, + {0x381f, 0x08}, + {0x3820, 0x88}, + {0x3821, 0x00}, + {0x382e, 0xe6}, + {0x3c80, 0x00}, + {0x3c87, 0x01}, + {0x3c8c, 0x19}, + {0x3c8d, 0x1c}, + {0x3ca0, 0x00}, + {0x3ca1, 0x00}, + {0x3ca2, 0x00}, + {0x3ca3, 0x00}, + {0x3ca4, 0x50}, + {0x3ca5, 0x11}, + {0x3ca6, 0x01}, + {0x3ca7, 0x00}, + {0x3ca8, 0x00}, + {0x4008, 0x02}, + {0x4009, 0x0f}, + {0x400a, 0x01}, + {0x400b, 0x19}, + {0x4019, 0x04}, + {0x401a, 0x58}, + {0x4032, 0x1e}, + {0x4050, 0x02}, + {0x4051, 0x09}, + {0x405e, 0x00}, + {0x4066, 0x02}, + {0x4501, 0x00}, + {0x4502, 0x04}, + {0x4505, 0x00}, + {0x4800, 0x64}, + {0x481b, 0x3e}, + {0x481f, 0x30}, + {0x4825, 0x34}, + {0x4837, 0x0e}, + {0x484b, 0x01}, + {0x4883, 0x02}, + {0x5000, 0xff}, + {0x5001, 0x0f}, + {0x5045, 0x20}, + {0x5046, 0x20}, + {0x5047, 0xa4}, + {0x5048, 0x20}, + {0x5049, 0xa4}, + {0x380c, 0x04}, + {0x380d, 0x90}, + {0x3508, 0x01}, + {0x3509, 0x00}, + {0x0305, 0x32}, + {0x4837, 0x14}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * max_framerate 30fps + * mipi_datarate per lane 600Mbps + */ +static const struct regval ov13b10_4208x3120_regs[] = { + {0x0103, 0x01}, + {0x0303, 0x01}, + {0x0305, 0x46}, + {0x0321, 0x00}, + {0x0323, 0x04}, + {0x0324, 0x01}, + {0x0325, 0x50}, {0x0326, 0x81}, {0x0327, 0x04}, {0x3012, 0x07}, @@ -190,7 +521,7 @@ static const struct ov13b10_reg mipi_data_rate_1120mbps[] = { {0x380e, 0x0c}, {0x380f, 0x7c}, {0x3811, 0x0f}, - {0x3813, 0x09}, + {0x3813, 0x08}, {0x3814, 0x01}, {0x3815, 0x01}, {0x3816, 0x01}, @@ -243,227 +574,29 @@ static const struct ov13b10_reg mipi_data_rate_1120mbps[] = { {0x5047, 0xa4}, {0x5048, 0x20}, {0x5049, 0xa4}, - {0x0100, 0x01}, + {REG_NULL, 0x00}, }; -static const struct ov13b10_reg mode_4208x3120_regs[] = { - {0x0305, 0xaf}, - {0x3501, 0x0c}, - {0x3662, 0x92}, - {0x3714, 0x24}, - {0x3739, 0x12}, - {0x37c2, 0x04}, - {0x37d9, 0x0c}, - {0x37e2, 0x0a}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x08}, - {0x3804, 0x10}, - {0x3805, 0x8f}, - {0x3806, 0x0c}, - {0x3807, 0x47}, - {0x3808, 0x10}, - {0x3809, 0x70}, - {0x380a, 0x0c}, - {0x380b, 0x30}, - {0x380c, 0x04}, - {0x380d, 0x98}, - {0x380e, 0x0c}, - {0x380f, 0x7c}, - {0x3810, 0x00}, - {0x3811, 0x0f}, - {0x3812, 0x00}, - {0x3813, 0x09}, - {0x3814, 0x01}, - {0x3816, 0x01}, - {0x3820, 0x88}, - {0x3c8c, 0x19}, - {0x4008, 0x02}, - {0x4009, 0x0f}, - {0x4050, 0x02}, - {0x4051, 0x09}, - {0x4501, 0x00}, - {0x4505, 0x00}, - {0x4837, 0x0e}, - {0x5000, 0xff}, - {0x5001, 0x0f}, + +static const struct ov13b10_mode supported_modes[] = { +{ + .width = 4208, + .height = 3120, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0200, + .hts_def = 0x0498 * 4, + .vts_def = 0x0c7c, + .reg_list = ov13b10_4208x3120_regs, + .hdr_mode = NO_HDR, + .vc[PAD0] = 0, + }, }; -static const struct ov13b10_reg mode_4160x3120_regs[] = { - {0x0305, 0xaf}, - {0x3501, 0x0c}, - {0x3662, 0x92}, - {0x3714, 0x24}, - {0x3739, 0x12}, - {0x37c2, 0x04}, - {0x37d9, 0x0c}, - {0x37e2, 0x0a}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x08}, - {0x3804, 0x10}, - {0x3805, 0x8f}, - {0x3806, 0x0c}, - {0x3807, 0x47}, - {0x3808, 0x10}, - {0x3809, 0x40}, - {0x380a, 0x0c}, - {0x380b, 0x30}, - {0x380c, 0x04}, - {0x380d, 0x98}, - {0x380e, 0x0c}, - {0x380f, 0x7c}, - {0x3810, 0x00}, - {0x3811, 0x27}, - {0x3812, 0x00}, - {0x3813, 0x09}, - {0x3814, 0x01}, - {0x3816, 0x01}, - {0x3820, 0x88}, - {0x3c8c, 0x19}, - {0x4008, 0x02}, - {0x4009, 0x0f}, - {0x4050, 0x02}, - {0x4051, 0x09}, - {0x4501, 0x00}, - {0x4505, 0x00}, - {0x4837, 0x0e}, - {0x5000, 0xff}, - {0x5001, 0x0f}, -}; - -static const struct ov13b10_reg mode_4160x2340_regs[] = { - {0x0305, 0xaf}, - {0x3501, 0x0c}, - {0x3662, 0x92}, - {0x3714, 0x24}, - {0x3739, 0x12}, - {0x37c2, 0x04}, - {0x37d9, 0x0c}, - {0x37e2, 0x0a}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x08}, - {0x3804, 0x10}, - {0x3805, 0x8f}, - {0x3806, 0x0c}, - {0x3807, 0x47}, - {0x3808, 0x10}, - {0x3809, 0x40}, - {0x380a, 0x09}, - {0x380b, 0x24}, - {0x380c, 0x04}, - {0x380d, 0x98}, - {0x380e, 0x0c}, - {0x380f, 0x7c}, - {0x3810, 0x00}, - {0x3811, 0x27}, - {0x3812, 0x01}, - {0x3813, 0x8f}, - {0x3814, 0x01}, - {0x3816, 0x01}, - {0x3820, 0x88}, - {0x3c8c, 0x19}, - {0x4008, 0x02}, - {0x4009, 0x0f}, - {0x4050, 0x02}, - {0x4051, 0x09}, - {0x4501, 0x00}, - {0x4505, 0x00}, - {0x4837, 0x0e}, - {0x5000, 0xff}, - {0x5001, 0x0f}, -}; - -static const struct ov13b10_reg mode_2104x1560_regs[] = { - {0x0305, 0xaf}, - {0x3501, 0x06}, - {0x3662, 0x88}, - {0x3714, 0x28}, - {0x3739, 0x10}, - {0x37c2, 0x14}, - {0x37d9, 0x06}, - {0x37e2, 0x0c}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x08}, - {0x3804, 0x10}, - {0x3805, 0x8f}, - {0x3806, 0x0c}, - {0x3807, 0x47}, - {0x3808, 0x08}, - {0x3809, 0x38}, - {0x380a, 0x06}, - {0x380b, 0x18}, - {0x380c, 0x04}, - {0x380d, 0x98}, - {0x380e, 0x06}, - {0x380f, 0x3e}, - {0x3810, 0x00}, - {0x3811, 0x07}, - {0x3812, 0x00}, - {0x3813, 0x05}, - {0x3814, 0x03}, - {0x3816, 0x03}, - {0x3820, 0x8b}, - {0x3c8c, 0x18}, - {0x4008, 0x00}, - {0x4009, 0x05}, - {0x4050, 0x00}, - {0x4051, 0x05}, - {0x4501, 0x08}, - {0x4505, 0x00}, - {0x4837, 0x0e}, - {0x5000, 0xfd}, - {0x5001, 0x0d}, -}; - -static const struct ov13b10_reg mode_2080x1170_regs[] = { - {0x0305, 0xaf}, - {0x3501, 0x06}, - {0x3662, 0x88}, - {0x3714, 0x28}, - {0x3739, 0x10}, - {0x37c2, 0x14}, - {0x37d9, 0x06}, - {0x37e2, 0x0c}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x08}, - {0x3804, 0x10}, - {0x3805, 0x8f}, - {0x3806, 0x0c}, - {0x3807, 0x47}, - {0x3808, 0x08}, - {0x3809, 0x20}, - {0x380a, 0x04}, - {0x380b, 0x92}, - {0x380c, 0x04}, - {0x380d, 0x98}, - {0x380e, 0x06}, - {0x380f, 0x3e}, - {0x3810, 0x00}, - {0x3811, 0x13}, - {0x3812, 0x00}, - {0x3813, 0xc9}, - {0x3814, 0x03}, - {0x3816, 0x03}, - {0x3820, 0x8b}, - {0x3c8c, 0x18}, - {0x4008, 0x00}, - {0x4009, 0x05}, - {0x4050, 0x00}, - {0x4051, 0x05}, - {0x4501, 0x08}, - {0x4505, 0x00}, - {0x4837, 0x0e}, - {0x5000, 0xfd}, - {0x5001, 0x0d}, +static const s64 link_freq_menu_items[] = { + OV13B10_LINK_FREQ_1120MHZ, }; static const char * const ov13b10_test_pattern_menu[] = { @@ -474,142 +607,63 @@ static const char * const ov13b10_test_pattern_menu[] = { "Vertical Color Bar Type 4" }; -/* Configurations for supported link frequencies */ -#define OV13B10_LINK_FREQ_560MHZ 560000000ULL -#define OV13B10_LINK_FREQ_INDEX_0 0 - -#define OV13B10_EXT_CLK 19200000 -#define OV13B10_DATA_LANES 4 - -/* - * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample - * data rate => double data rate; number of lanes => 4; bits per pixel => 10 - */ -static u64 link_freq_to_pixel_rate(u64 f) +/* Write registers up to 4 at a time */ +static int ov13b10_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) { - f *= 2 * OV13B10_DATA_LANES; - do_div(f, 10); - - return f; -} - -/* Menu items for LINK_FREQ V4L2 control */ -static const s64 link_freq_menu_items[] = { - OV13B10_LINK_FREQ_560MHZ -}; - -/* Link frequency configs */ -static const struct ov13b10_link_freq_config - link_freq_configs[] = { - { - .pixels_per_line = OV13B10_PPL_560MHZ, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mipi_data_rate_1120mbps), - .regs = mipi_data_rate_1120mbps, - } - } -}; - -/* Mode configs */ -static const struct ov13b10_mode supported_modes[] = { - { - .width = 4208, - .height = 3120, - .vts_def = OV13B10_VTS_30FPS, - .vts_min = OV13B10_VTS_30FPS, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_4208x3120_regs), - .regs = mode_4208x3120_regs, - }, - .link_freq_index = OV13B10_LINK_FREQ_INDEX_0, - }, - { - .width = 4160, - .height = 3120, - .vts_def = OV13B10_VTS_30FPS, - .vts_min = OV13B10_VTS_30FPS, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_4160x3120_regs), - .regs = mode_4160x3120_regs, - }, - .link_freq_index = OV13B10_LINK_FREQ_INDEX_0, - }, - { - .width = 4160, - .height = 2340, - .vts_def = OV13B10_VTS_30FPS, - .vts_min = OV13B10_VTS_30FPS, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_4160x2340_regs), - .regs = mode_4160x2340_regs, - }, - .link_freq_index = OV13B10_LINK_FREQ_INDEX_0, - }, - { - .width = 2104, - .height = 1560, - .vts_def = OV13B10_VTS_60FPS, - .vts_min = OV13B10_VTS_60FPS, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_2104x1560_regs), - .regs = mode_2104x1560_regs, - }, - .link_freq_index = OV13B10_LINK_FREQ_INDEX_0, - }, - { - .width = 2080, - .height = 1170, - .vts_def = OV13B10_VTS_60FPS, - .vts_min = OV13B10_VTS_60FPS, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_2080x1170_regs), - .regs = mode_2080x1170_regs, - }, - .link_freq_index = OV13B10_LINK_FREQ_INDEX_0, - } -}; - -struct ov13b10 { - struct v4l2_subdev sd; - struct media_pad pad; - - struct v4l2_ctrl_handler ctrl_handler; - /* V4L2 Controls */ - struct v4l2_ctrl *link_freq; - struct v4l2_ctrl *pixel_rate; - struct v4l2_ctrl *vblank; - struct v4l2_ctrl *hblank; - struct v4l2_ctrl *exposure; - - /* Current mode */ - const struct ov13b10_mode *cur_mode; - - /* Mutex for serialized access */ - struct mutex mutex; - - /* Streaming on/off */ - bool streaming; - - /* True if the device has been identified */ - bool identified; -}; - -#define to_ov13b10(_sd) container_of(_sd, struct ov13b10, sd) - -/* Read registers up to 4 at a time */ -static int ov13b10_read_reg(struct ov13b10 *ov13b, - u16 reg, u32 len, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); - struct i2c_msg msgs[2]; - u8 *data_be_p; - int ret; - __be32 data_be = 0; - __be16 reg_addr_be = cpu_to_be16(reg); + u32 buf_i = 0; + u32 val_i = 0; + u8 buf[6]; + u8 *val_p = NULL; + __be32 val_be = 0; 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 ov13b10_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i = 0; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = ov13b10_write_reg(client, regs[i].addr, + OV13B10_REG_VALUE_08BIT, + regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int ov13b10_read_reg(struct i2c_client *client, u16 reg, + unsigned int len, u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p = NULL; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret = 0; + + if (len > 4 || !len) + return -EINVAL; + data_be_p = (u8 *)&data_be; /* Write register address */ msgs[0].addr = client->addr; @@ -632,259 +686,804 @@ static int ov13b10_read_reg(struct ov13b10 *ov13b, return 0; } -/* Write registers up to 4 at a time */ -static int ov13b10_write_reg(struct ov13b10 *ov13b, - u16 reg, u32 len, u32 __val) +static int ov13b10_get_reso_dist(const struct ov13b10_mode *mode, + struct v4l2_mbus_framefmt *framefmt) { - struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); - int buf_i, val_i; - u8 buf[6], *val_p; - __be32 val; - - if (len > 4) - return -EINVAL; - - buf[0] = reg >> 8; - buf[1] = reg & 0xff; - - val = cpu_to_be32(__val); - val_p = (u8 *)&val; - 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; + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); } -/* Write a list of registers */ -static int ov13b10_write_regs(struct ov13b10 *ov13b, - const struct ov13b10_reg *regs, u32 len) +static const struct ov13b10_mode * +ov13b10_find_best_fit(struct ov13b10 *ov13b10, struct v4l2_subdev_format *fmt) { - struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); - int ret; - u32 i; + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist = 0; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i = 0; - for (i = 0; i < len; i++) { - ret = ov13b10_write_reg(ov13b, regs[i].address, 1, - regs[i].val); - if (ret) { - dev_err_ratelimited(&client->dev, - "Failed to write reg 0x%4.4x. error = %d\n", - regs[i].address, ret); - - return ret; + for (i = 0; i < ov13b10->cfg_num; i++) { + dist = ov13b10_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 0; + return &supported_modes[cur_best_fit]; } -static int ov13b10_write_reg_list(struct ov13b10 *ov13b, - const struct ov13b10_reg_list *r_list) +static int ov13b10_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) { - return ov13b10_write_regs(ov13b, r_list->regs, r_list->num_of_regs); -} + struct ov13b10 *ov13b10 = to_ov13b10(sd); + const struct ov13b10_mode *mode; + s64 h_blank, vblank_def = 0; -/* Open sub-device */ -static int ov13b10_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - const struct ov13b10_mode *default_mode = &supported_modes[0]; - struct ov13b10 *ov13b = to_ov13b10(sd); - struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, - fh->state, - 0); + mutex_lock(&ov13b10->mutex); - mutex_lock(&ov13b->mutex); + mode = ov13b10_find_best_fit(ov13b10, fmt); + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; +#else + mutex_unlock(&ov13b10->mutex); + return -ENOTTY; +#endif + } else { + ov13b10->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(ov13b10->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov13b10->vblank, vblank_def, + OV13B10_VTS_MAX - mode->height, + 1, vblank_def); + } - /* Initialize try_fmt */ - try_fmt->width = default_mode->width; - try_fmt->height = default_mode->height; - try_fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; - try_fmt->field = V4L2_FIELD_NONE; - - /* No crop or compose */ - mutex_unlock(&ov13b->mutex); + mutex_unlock(&ov13b10->mutex); return 0; } -static int ov13b10_update_digital_gain(struct ov13b10 *ov13b, u32 d_gain) +static int ov13b10_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) { - int ret; - u32 val; + struct ov13b10 *ov13b10 = to_ov13b10(sd); + const struct ov13b10_mode *mode = ov13b10->cur_mode; - /* - * 0x350C[7:6], 0x350B[7:0], 0x350A[1:0] - */ + mutex_lock(&ov13b10->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); +#else + mutex_unlock(&ov13b10->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&ov13b10->mutex); - val = (d_gain & OV13B10_DGTL_GAIN_L_MASK) << OV13B10_DGTL_GAIN_L_SHIFT; - ret = ov13b10_write_reg(ov13b, OV13B10_REG_DGTL_GAIN_L, - OV13B10_REG_VALUE_08BIT, val); - if (ret) - return ret; + return 0; +} - val = (d_gain >> OV13B10_DGTL_GAIN_M_SHIFT) & OV13B10_DGTL_GAIN_M_MASK; - ret = ov13b10_write_reg(ov13b, OV13B10_REG_DGTL_GAIN_M, - OV13B10_REG_VALUE_08BIT, val); - if (ret) - return ret; +static int ov13b10_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ - val = (d_gain >> OV13B10_DGTL_GAIN_H_SHIFT) & OV13B10_DGTL_GAIN_H_MASK; - ret = ov13b10_write_reg(ov13b, OV13B10_REG_DGTL_GAIN_H, - OV13B10_REG_VALUE_08BIT, val); + if (code->index != 0) + return -EINVAL; + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + + return 0; +} + +static int ov13b10_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct ov13b10 *ov13b10 = to_ov13b10(sd); + + if (fse->index >= ov13b10->cfg_num) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10) + 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 ov13b10_enable_test_pattern(struct ov13b10 *ov13b10, u32 pattern) +{ + u32 val = 0; + + if (pattern) + val = (pattern - 1) | OV13B10_TEST_PATTERN_ENABLE; + else + val = OV13B10_TEST_PATTERN_DISABLE; + + return ov13b10_write_reg(ov13b10->client, + OV13B10_REG_TEST_PATTERN, + OV13B10_REG_VALUE_08BIT, + val); +} + +static int ov13b10_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov13b10 *ov13b10 = to_ov13b10(sd); + const struct ov13b10_mode *mode = ov13b10->cur_mode; + + mutex_lock(&ov13b10->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&ov13b10->mutex); + + return 0; +} + +static void ov13b10_get_otp(struct otp_info *otp, + struct rkmodule_inf *inf) +{ + u32 i; + + if (otp->total_checksum) { + /* 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]; + } + } + } +} + +static int ov13b10_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_config *config) +{ + config->type = V4L2_MBUS_CSI2_DPHY; + config->bus.mipi_csi2.num_data_lanes = OV13B10_LANES; + + return 0; +} + +static void ov13b10_get_module_inf(struct ov13b10 *ov13b10, + struct rkmodule_inf *inf) +{ + struct otp_info *otp = ov13b10->otp; + + memset(inf, 0, sizeof(*inf)); + strscpy(inf->base.sensor, OV13B10_NAME, sizeof(inf->base.sensor)); + strscpy(inf->base.module, ov13b10->module_name, + sizeof(inf->base.module)); + strscpy(inf->base.lens, ov13b10->len_name, sizeof(inf->base.lens)); + if (otp) + ov13b10_get_otp(otp, inf); +} + +static long ov13b10_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ov13b10 *ov13b10 = to_ov13b10(sd); + struct rkmodule_hdr_cfg *hdr; + long ret = 0; + u32 stream = 0; + u32 i = 0; + u32 h = 0; + u32 w = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + ov13b10_get_module_inf(ov13b10, (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 = ov13b10->cur_mode->hdr_mode; + break; + case RKMODULE_SET_HDR_CFG: + hdr = (struct rkmodule_hdr_cfg *)arg; + w = ov13b10->cur_mode->width; + h = ov13b10->cur_mode->height; + for (i = 0; i < ov13b10->cfg_num; i++) { + if (w == supported_modes[i].width && + h == supported_modes[i].height && + supported_modes[i].hdr_mode == hdr->hdr_mode) { + ov13b10->cur_mode = &supported_modes[i]; + break; + } + } + if (i == ov13b10->cfg_num) { + dev_err(&ov13b10->client->dev, + "not find hdr mode:%d %dx%d config\n", + hdr->hdr_mode, w, h); + ret = -EINVAL; + } else { + w = ov13b10->cur_mode->hts_def - + ov13b10->cur_mode->width; + h = ov13b10->cur_mode->vts_def - + ov13b10->cur_mode->height; + __v4l2_ctrl_modify_range(ov13b10->hblank, w, w, 1, w); + __v4l2_ctrl_modify_range(ov13b10->vblank, h, + OV13B10_VTS_MAX - + ov13b10->cur_mode->height, + 1, h); + } + break; + case PREISP_CMD_SET_HDRAE_EXP: + break; + case RKMODULE_SET_QUICK_STREAM: + stream = *((u32 *)arg); + if (stream) + ret = ov13b10_write_reg(ov13b10->client, + OV13B10_REG_CTRL_MODE, + OV13B10_REG_VALUE_08BIT, + OV13B10_MODE_STREAMING); + else + ret = ov13b10_write_reg(ov13b10->client, + OV13B10_REG_CTRL_MODE, + OV13B10_REG_VALUE_08BIT, + OV13B10_MODE_SW_STANDBY); + break; + default: + ret = -ENOIOCTLCMD; + break; + } return ret; } -static int ov13b10_enable_test_pattern(struct ov13b10 *ov13b, u32 pattern) +#ifdef CONFIG_COMPAT +static long ov13b10_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) { - int ret; - u32 val; + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf = NULL; + struct rkmodule_awb_cfg *cfg = NULL; + struct rkmodule_hdr_cfg *hdr; + struct preisp_hdrae_exp_s *hdrae; + long ret = 0; + u32 stream = 0; - ret = ov13b10_read_reg(ov13b, OV13B10_REG_TEST_PATTERN, - OV13B10_REG_VALUE_08BIT, &val); - if (ret) - return ret; + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } - if (pattern) { - val &= OV13B10_TEST_PATTERN_MASK; - val |= ((pattern - 1) << OV13B10_TEST_PATTERN_BAR_SHIFT) | - OV13B10_TEST_PATTERN_ENABLE; - } else { - val &= ~OV13B10_TEST_PATTERN_ENABLE; + ret = ov13b10_ioctl(sd, cmd, inf); + if (!ret) { + ret = copy_to_user(up, inf, sizeof(*inf)); + if (ret) { + kfree(inf); + return -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) { + kfree(cfg); + return -EFAULT; + } + ret = ov13b10_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 = ov13b10_ioctl(sd, cmd, hdr); + if (!ret) { + ret = copy_to_user(up, hdr, sizeof(*hdr)); + if (ret) { + kfree(hdr); + return -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) { + kfree(hdr); + return -EFAULT; + } + ret = ov13b10_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; + } + + ret = copy_from_user(hdrae, up, sizeof(*hdrae)); + if (ret) { + kfree(hdrae); + return -EFAULT; + } + ret = ov13b10_ioctl(sd, cmd, hdrae); + kfree(hdrae); + break; + case RKMODULE_SET_QUICK_STREAM: + ret = copy_from_user(&stream, up, sizeof(u32)); + if (ret) + return -EFAULT; + ret = ov13b10_ioctl(sd, cmd, &stream); + break; + default: + ret = -ENOIOCTLCMD; + break; } - return ov13b10_write_reg(ov13b, OV13B10_REG_TEST_PATTERN, - OV13B10_REG_VALUE_08BIT, val); + return ret; } +#endif -static int ov13b10_set_ctrl_hflip(struct ov13b10 *ov13b, u32 ctrl_val) +static int __ov13b10_start_stream(struct ov13b10 *ov13b10) { - int ret; - u32 val; + int ret = 0; - ret = ov13b10_read_reg(ov13b, OV13B10_REG_FORMAT1, - OV13B10_REG_VALUE_08BIT, &val); + ret = ov13b10_write_array(ov13b10->client, ov13b10->cur_mode->reg_list); if (ret) return ret; - ret = ov13b10_write_reg(ov13b, OV13B10_REG_FORMAT1, - OV13B10_REG_VALUE_08BIT, - ctrl_val ? val & ~BIT(3) : val); - + /* In case these controls are set before streaming */ + ret = __v4l2_ctrl_handler_setup(&ov13b10->ctrl_handler); if (ret) return ret; - ret = ov13b10_read_reg(ov13b, OV13B10_REG_H_WIN_OFFSET, - OV13B10_REG_VALUE_08BIT, &val); - if (ret) - return ret; - - /* - * Applying cropping offset to reverse the change of Bayer order - * after mirroring image - */ - return ov13b10_write_reg(ov13b, OV13B10_REG_H_WIN_OFFSET, + return ov13b10_write_reg(ov13b10->client, + OV13B10_REG_CTRL_MODE, OV13B10_REG_VALUE_08BIT, - ctrl_val ? ++val : val); + OV13B10_MODE_STREAMING); } -static int ov13b10_set_ctrl_vflip(struct ov13b10 *ov13b, u32 ctrl_val) +static int __ov13b10_stop_stream(struct ov13b10 *ov13b10) { - int ret; - u32 val; - - ret = ov13b10_read_reg(ov13b, OV13B10_REG_FORMAT1, - OV13B10_REG_VALUE_08BIT, &val); - if (ret) - return ret; - - ret = ov13b10_write_reg(ov13b, OV13B10_REG_FORMAT1, - OV13B10_REG_VALUE_08BIT, - ctrl_val ? val | BIT(4) | BIT(5) : val); - - if (ret) - return ret; - - ret = ov13b10_read_reg(ov13b, OV13B10_REG_V_WIN_OFFSET, - OV13B10_REG_VALUE_08BIT, &val); - if (ret) - return ret; - - /* - * Applying cropping offset to reverse the change of Bayer order - * after flipping image - */ - return ov13b10_write_reg(ov13b, OV13B10_REG_V_WIN_OFFSET, + return ov13b10_write_reg(ov13b10->client, + OV13B10_REG_CTRL_MODE, OV13B10_REG_VALUE_08BIT, - ctrl_val ? --val : val); + OV13B10_MODE_SW_STANDBY); } +static int ov13b10_s_stream(struct v4l2_subdev *sd, int on) +{ + int ret = 0; + struct ov13b10 *ov13b10 = to_ov13b10(sd); + struct i2c_client *client = ov13b10->client; + + mutex_lock(&ov13b10->mutex); + on = !!on; + if (on == ov13b10->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 = __ov13b10_start_stream(ov13b10); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __ov13b10_stop_stream(ov13b10); + pm_runtime_put(&client->dev); + } + + ov13b10->streaming = on; + +unlock_and_return: + mutex_unlock(&ov13b10->mutex); + + return ret; +} + +static int ov13b10_s_power(struct v4l2_subdev *sd, int on) +{ + int ret = 0; + struct ov13b10 *ov13b10 = to_ov13b10(sd); + struct i2c_client *client = ov13b10->client; + + mutex_lock(&ov13b10->mutex); + + /* If the power state is not modified - no work to do. */ + if (ov13b10->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; + } + + ov13b10->power_on = true; + } else { + pm_runtime_put(&client->dev); + ov13b10->power_on = false; + } + +unlock_and_return: + mutex_unlock(&ov13b10->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ov13b10_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, OV13B10_XVCLK_FREQ / 1000 / 1000); +} + +static int __ov13b10_power_on(struct ov13b10 *ov13b10) +{ + int ret = 0; + u32 delay_us = 0; + struct device *dev = &ov13b10->client->dev; + + if (!IS_ERR(ov13b10->power_gpio)) + gpiod_set_value_cansleep(ov13b10->power_gpio, 1); + + usleep_range(1000, 2000); + + if (!IS_ERR_OR_NULL(ov13b10->pins_default)) { + ret = pinctrl_select_state(ov13b10->pinctrl, + ov13b10->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(ov13b10->xvclk, OV13B10_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(ov13b10->xvclk) != OV13B10_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(ov13b10->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + if (!IS_ERR(ov13b10->reset_gpio)) + gpiod_set_value_cansleep(ov13b10->reset_gpio, 0); + + ret = regulator_bulk_enable(OV13B10_NUM_SUPPLIES, ov13b10->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(ov13b10->reset_gpio)) + gpiod_set_value_cansleep(ov13b10->reset_gpio, 1); + + usleep_range(500, 1000); + if (!IS_ERR(ov13b10->pwdn_gpio)) + gpiod_set_value_cansleep(ov13b10->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = ov13b10_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(ov13b10->xvclk); + + return ret; +} + +static void __ov13b10_power_off(struct ov13b10 *ov13b10) +{ + int ret = 0; + struct device *dev = &ov13b10->client->dev; + + if (!IS_ERR(ov13b10->pwdn_gpio)) + gpiod_set_value_cansleep(ov13b10->pwdn_gpio, 0); + clk_disable_unprepare(ov13b10->xvclk); + if (!IS_ERR(ov13b10->reset_gpio)) + gpiod_set_value_cansleep(ov13b10->reset_gpio, 0); + + if (!IS_ERR_OR_NULL(ov13b10->pins_sleep)) { + ret = pinctrl_select_state(ov13b10->pinctrl, + ov13b10->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + if (!IS_ERR(ov13b10->power_gpio)) + gpiod_set_value_cansleep(ov13b10->power_gpio, 0); + + regulator_bulk_disable(OV13B10_NUM_SUPPLIES, ov13b10->supplies); +} + +static int ov13b10_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov13b10 *ov13b10 = to_ov13b10(sd); + + return __ov13b10_power_on(ov13b10); +} + +static int ov13b10_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov13b10 *ov13b10 = to_ov13b10(sd); + + __ov13b10_power_off(ov13b10); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int ov13b10_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov13b10 *ov13b10 = to_ov13b10(sd); + struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, fh->state, 0); + const struct ov13b10_mode *def_mode = &supported_modes[0]; + + mutex_lock(&ov13b10->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&ov13b10->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int ov13b10_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct ov13b10 *ov13b10 = to_ov13b10(sd); + + if (fie->index >= ov13b10->cfg_num) + return -EINVAL; + + fie->code = MEDIA_BUS_FMT_SBGGR10_1X10; + 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; +} + + +#define CROP_START(SRC, DST) (((SRC) - (DST)) / 2 / 4 * 4) +#define DST_WIDTH 3840 +#define DST_HEIGHT 2160 + +/* + * 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 ov13b10_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct ov13b10 *ov13b10 = to_ov13b10(sd); + + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + if (ov13b10->cur_mode->width == 4208) { + sel->r.left = CROP_START(ov13b10->cur_mode->width, 4208); + sel->r.width = 4208; + sel->r.top = CROP_START(ov13b10->cur_mode->height, 3120); + sel->r.height = 3120; + } else { + sel->r.left = CROP_START(ov13b10->cur_mode->width, DST_WIDTH); + sel->r.width = DST_WIDTH; + sel->r.top = CROP_START(ov13b10->cur_mode->height, DST_HEIGHT); + sel->r.height = DST_HEIGHT; + } + return 0; + } + return -EINVAL; +} + +static const struct dev_pm_ops ov13b10_pm_ops = { + SET_RUNTIME_PM_OPS(ov13b10_runtime_suspend, + ov13b10_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops ov13b10_internal_ops = { + .open = ov13b10_open, +}; +#endif + +static const struct v4l2_subdev_core_ops ov13b10_core_ops = { + .s_power = ov13b10_s_power, + .ioctl = ov13b10_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov13b10_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops ov13b10_video_ops = { + .s_stream = ov13b10_s_stream, + .g_frame_interval = ov13b10_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops ov13b10_pad_ops = { + .enum_mbus_code = ov13b10_enum_mbus_code, + .enum_frame_size = ov13b10_enum_frame_sizes, + .enum_frame_interval = ov13b10_enum_frame_interval, + .get_fmt = ov13b10_get_fmt, + .set_fmt = ov13b10_set_fmt, + .get_selection = ov13b10_get_selection, + .get_mbus_config = ov13b10_g_mbus_config, +}; + +static const struct v4l2_subdev_ops ov13b10_subdev_ops = { + .core = &ov13b10_core_ops, + .video = &ov13b10_video_ops, + .pad = &ov13b10_pad_ops, +}; + static int ov13b10_set_ctrl(struct v4l2_ctrl *ctrl) { - struct ov13b10 *ov13b = container_of(ctrl->handler, - struct ov13b10, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); - s64 max; - int ret; + struct ov13b10 *ov13b10 = container_of(ctrl->handler, + struct ov13b10, ctrl_handler); + struct i2c_client *client = ov13b10->client; + s64 max = 0; + int ret = 0; + u32 again = 0; + u32 dgain = 0; /* Propagate change of current control to all related controls */ switch (ctrl->id) { case V4L2_CID_VBLANK: /* Update max exposure while meeting expected vblanking */ - max = ov13b->cur_mode->height + ctrl->val - 8; - __v4l2_ctrl_modify_range(ov13b->exposure, - ov13b->exposure->minimum, - max, ov13b->exposure->step, max); + max = ov13b10->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(ov13b10->exposure, + ov13b10->exposure->minimum, max, + ov13b10->exposure->step, + ov13b10->exposure->default_value); break; } - /* - * Applying V4L2 control value only happens - * when power is up for streaming - */ - if (!pm_runtime_get_if_in_use(&client->dev)) + if (pm_runtime_get(&client->dev) <= 0) return 0; - ret = 0; switch (ctrl->id) { - case V4L2_CID_ANALOGUE_GAIN: - ret = ov13b10_write_reg(ov13b, OV13B10_REG_ANALOG_GAIN, - OV13B10_REG_VALUE_16BIT, - ctrl->val << 1); - break; - case V4L2_CID_DIGITAL_GAIN: - ret = ov13b10_update_digital_gain(ov13b, ctrl->val); - break; case V4L2_CID_EXPOSURE: - ret = ov13b10_write_reg(ov13b, OV13B10_REG_EXPOSURE, - OV13B10_REG_VALUE_24BIT, - ctrl->val); + /* 4 least significant bits of expsoure are fractional part */ + ret = ov13b10_write_reg(ov13b10->client, + OV13B10_REG_EXPOSURE_H, + OV13B10_REG_VALUE_08BIT, + (ctrl->val >> 16) & 0xFF); + ret |= ov13b10_write_reg(ov13b10->client, + OV13B10_REG_EXPOSURE_M, + OV13B10_REG_VALUE_08BIT, + (ctrl->val >> 8) & 0xFF); + ret |= ov13b10_write_reg(ov13b10->client, + OV13B10_REG_EXPOSURE_L, + OV13B10_REG_VALUE_08BIT, + ctrl->val & 0xFF); + break; + case V4L2_CID_ANALOGUE_GAIN: + if (ctrl->val > 0xF80) { + again = 0xF80; + dgain = ctrl->val - 0xF80; + if (dgain < 0x400) + dgain = 0x400; //1024,1x dgain + else if (dgain > 0xFFF) + dgain = 0xFFF; //max dgain=3.999x + } else { + dgain = 1024; + if (ctrl->val < 0x100) + again = 0x100; + else + again = ctrl->val; + } + + ret = ov13b10_write_reg(ov13b10->client, + OV13B10_REG_GAIN_H, + OV13B10_REG_VALUE_08BIT, + (again >> OV13B10_GAIN_H_SHIFT) & + OV13B10_GAIN_H_MASK); + ret |= ov13b10_write_reg(ov13b10->client, + OV13B10_REG_GAIN_L, + OV13B10_REG_VALUE_08BIT, + again & OV13B10_GAIN_L_MASK); + ret |= ov13b10_write_reg(ov13b10->client, + OV13B10_REG_DGAIN_H, + OV13B10_REG_VALUE_08BIT, + (dgain >> 10) & 0x03); + ret |= ov13b10_write_reg(ov13b10->client, + OV13B10_REG_DGAIN_M, + OV13B10_REG_VALUE_08BIT, + dgain >> 2 & 0xff); + ret |= ov13b10_write_reg(ov13b10->client, + OV13B10_REG_DGAIN_L, + OV13B10_REG_VALUE_08BIT, + (dgain & 0x03) << 6 & 0xC0); break; case V4L2_CID_VBLANK: - ret = ov13b10_write_reg(ov13b, OV13B10_REG_VTS, + ret = ov13b10_write_reg(ov13b10->client, + OV13B10_REG_VTS, OV13B10_REG_VALUE_16BIT, - ov13b->cur_mode->height - + ctrl->val); + ctrl->val + ov13b10->cur_mode->height); break; case V4L2_CID_TEST_PATTERN: - ret = ov13b10_enable_test_pattern(ov13b, ctrl->val); - break; - case V4L2_CID_HFLIP: - ov13b10_set_ctrl_hflip(ov13b, ctrl->val); - break; - case V4L2_CID_VFLIP: - ov13b10_set_ctrl_vflip(ov13b, ctrl->val); + ret = ov13b10_enable_test_pattern(ov13b10, ctrl->val); break; default: - dev_info(&client->dev, - "ctrl(id:0x%x,val:0x%x) is not handled\n", - ctrl->id, ctrl->val); + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); break; } @@ -897,574 +1496,299 @@ static const struct v4l2_ctrl_ops ov13b10_ctrl_ops = { .s_ctrl = ov13b10_set_ctrl, }; -static int ov13b10_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) +static int ov13b10_initialize_controls(struct ov13b10 *ov13b10) { - /* Only one bayer order(GRBG) is supported */ - if (code->index > 0) - return -EINVAL; - - code->code = MEDIA_BUS_FMT_SGRBG10_1X10; - - return 0; -} - -static int ov13b10_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - if (fse->index >= ARRAY_SIZE(supported_modes)) - return -EINVAL; - - if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) - return -EINVAL; - - fse->min_width = supported_modes[fse->index].width; - fse->max_width = fse->min_width; - fse->min_height = supported_modes[fse->index].height; - fse->max_height = fse->min_height; - - return 0; -} - -static void ov13b10_update_pad_format(const struct ov13b10_mode *mode, - struct v4l2_subdev_format *fmt) -{ - fmt->format.width = mode->width; - fmt->format.height = mode->height; - fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; - fmt->format.field = V4L2_FIELD_NONE; -} - -static int ov13b10_do_get_pad_format(struct ov13b10 *ov13b, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct v4l2_mbus_framefmt *framefmt; - struct v4l2_subdev *sd = &ov13b->sd; - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); - fmt->format = *framefmt; - } else { - ov13b10_update_pad_format(ov13b->cur_mode, fmt); - } - - return 0; -} - -static int ov13b10_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct ov13b10 *ov13b = to_ov13b10(sd); - int ret; - - mutex_lock(&ov13b->mutex); - ret = ov13b10_do_get_pad_format(ov13b, sd_state, fmt); - mutex_unlock(&ov13b->mutex); - - return ret; -} - -static int -ov13b10_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct ov13b10 *ov13b = to_ov13b10(sd); const struct ov13b10_mode *mode; - struct v4l2_mbus_framefmt *framefmt; - s32 vblank_def; - s32 vblank_min; - s64 h_blank; - s64 pixel_rate; - s64 link_freq; - - mutex_lock(&ov13b->mutex); - - /* Only one raw bayer(GRBG) order is supported */ - if (fmt->format.code != MEDIA_BUS_FMT_SGRBG10_1X10) - fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; - - mode = v4l2_find_nearest_size(supported_modes, - ARRAY_SIZE(supported_modes), - width, height, - fmt->format.width, fmt->format.height); - ov13b10_update_pad_format(mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); - *framefmt = fmt->format; - } else { - ov13b->cur_mode = mode; - __v4l2_ctrl_s_ctrl(ov13b->link_freq, mode->link_freq_index); - link_freq = link_freq_menu_items[mode->link_freq_index]; - pixel_rate = link_freq_to_pixel_rate(link_freq); - __v4l2_ctrl_s_ctrl_int64(ov13b->pixel_rate, pixel_rate); - - /* Update limits and set FPS to default */ - vblank_def = ov13b->cur_mode->vts_def - - ov13b->cur_mode->height; - vblank_min = ov13b->cur_mode->vts_min - - ov13b->cur_mode->height; - __v4l2_ctrl_modify_range(ov13b->vblank, vblank_min, - OV13B10_VTS_MAX - - ov13b->cur_mode->height, - 1, - vblank_def); - __v4l2_ctrl_s_ctrl(ov13b->vblank, vblank_def); - h_blank = - link_freq_configs[mode->link_freq_index].pixels_per_line - - ov13b->cur_mode->width; - __v4l2_ctrl_modify_range(ov13b->hblank, h_blank, - h_blank, 1, h_blank); - } - - mutex_unlock(&ov13b->mutex); - - return 0; -} - -/* Verify chip ID */ -static int ov13b10_identify_module(struct ov13b10 *ov13b) -{ - struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); - int ret; - u32 val; - - if (ov13b->identified) - return 0; - - ret = ov13b10_read_reg(ov13b, OV13B10_REG_CHIP_ID, - OV13B10_REG_VALUE_24BIT, &val); - if (ret) - return ret; - - if (val != OV13B10_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x\n", - OV13B10_CHIP_ID, val); - return -EIO; - } - - ov13b->identified = true; - - return 0; -} - -static int ov13b10_start_streaming(struct ov13b10 *ov13b) -{ - struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); - const struct ov13b10_reg_list *reg_list; - int ret, link_freq_index; - - ret = ov13b10_identify_module(ov13b); - if (ret) - return ret; - - /* Get out of from software reset */ - ret = ov13b10_write_reg(ov13b, OV13B10_REG_SOFTWARE_RST, - OV13B10_REG_VALUE_08BIT, OV13B10_SOFTWARE_RST); - if (ret) { - dev_err(&client->dev, "%s failed to set powerup registers\n", - __func__); - return ret; - } - - link_freq_index = ov13b->cur_mode->link_freq_index; - reg_list = &link_freq_configs[link_freq_index].reg_list; - ret = ov13b10_write_reg_list(ov13b, reg_list); - if (ret) { - dev_err(&client->dev, "%s failed to set plls\n", __func__); - return ret; - } - - /* Apply default values of current mode */ - reg_list = &ov13b->cur_mode->reg_list; - ret = ov13b10_write_reg_list(ov13b, reg_list); - if (ret) { - dev_err(&client->dev, "%s failed to set mode\n", __func__); - return ret; - } - - /* Apply customized values from user */ - ret = __v4l2_ctrl_handler_setup(ov13b->sd.ctrl_handler); - if (ret) - return ret; - - return ov13b10_write_reg(ov13b, OV13B10_REG_MODE_SELECT, - OV13B10_REG_VALUE_08BIT, - OV13B10_MODE_STREAMING); -} - -/* Stop streaming */ -static int ov13b10_stop_streaming(struct ov13b10 *ov13b) -{ - return ov13b10_write_reg(ov13b, OV13B10_REG_MODE_SELECT, - OV13B10_REG_VALUE_08BIT, OV13B10_MODE_STANDBY); -} - -static int ov13b10_set_stream(struct v4l2_subdev *sd, int enable) -{ - struct ov13b10 *ov13b = to_ov13b10(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl = NULL; + s64 exposure_max = 0; + s64 vblank_def = 0; + u32 h_blank = 0; int ret = 0; - mutex_lock(&ov13b->mutex); - if (ov13b->streaming == enable) { - mutex_unlock(&ov13b->mutex); - return 0; - } - - if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); - if (ret < 0) - goto err_unlock; - - /* - * Apply default & customized values - * and then start streaming. - */ - ret = ov13b10_start_streaming(ov13b); - if (ret) - goto err_rpm_put; - } else { - ov13b10_stop_streaming(ov13b); - pm_runtime_put(&client->dev); - } - - ov13b->streaming = enable; - mutex_unlock(&ov13b->mutex); - - return ret; - -err_rpm_put: - pm_runtime_put(&client->dev); -err_unlock: - mutex_unlock(&ov13b->mutex); - - return ret; -} - -static int __maybe_unused ov13b10_suspend(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct ov13b10 *ov13b = to_ov13b10(sd); - - if (ov13b->streaming) - ov13b10_stop_streaming(ov13b); - - return 0; -} - -static int __maybe_unused ov13b10_resume(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct ov13b10 *ov13b = to_ov13b10(sd); - int ret; - - if (ov13b->streaming) { - ret = ov13b10_start_streaming(ov13b); - if (ret) - goto error; - } - - return 0; - -error: - ov13b10_stop_streaming(ov13b); - ov13b->streaming = false; - return ret; -} - -static const struct v4l2_subdev_video_ops ov13b10_video_ops = { - .s_stream = ov13b10_set_stream, -}; - -static const struct v4l2_subdev_pad_ops ov13b10_pad_ops = { - .enum_mbus_code = ov13b10_enum_mbus_code, - .get_fmt = ov13b10_get_pad_format, - .set_fmt = ov13b10_set_pad_format, - .enum_frame_size = ov13b10_enum_frame_size, -}; - -static const struct v4l2_subdev_ops ov13b10_subdev_ops = { - .video = &ov13b10_video_ops, - .pad = &ov13b10_pad_ops, -}; - -static const struct media_entity_operations ov13b10_subdev_entity_ops = { - .link_validate = v4l2_subdev_link_validate, -}; - -static const struct v4l2_subdev_internal_ops ov13b10_internal_ops = { - .open = ov13b10_open, -}; - -/* Initialize control handlers */ -static int ov13b10_init_controls(struct ov13b10 *ov13b) -{ - struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); - struct v4l2_fwnode_device_properties props; - struct v4l2_ctrl_handler *ctrl_hdlr; - s64 exposure_max; - s64 vblank_def; - s64 vblank_min; - s64 hblank; - s64 pixel_rate_min; - s64 pixel_rate_max; - const struct ov13b10_mode *mode; - u32 max; - int ret; - - ctrl_hdlr = &ov13b->ctrl_handler; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10); + handler = &ov13b10->ctrl_handler; + mode = ov13b10->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); if (ret) return ret; + handler->lock = &ov13b10->mutex; - mutex_init(&ov13b->mutex); - ctrl_hdlr->lock = &ov13b->mutex; - max = ARRAY_SIZE(link_freq_menu_items) - 1; - ov13b->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, - &ov13b10_ctrl_ops, - V4L2_CID_LINK_FREQ, - max, - 0, - link_freq_menu_items); - if (ov13b->link_freq) - ov13b->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + 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; - pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]); - pixel_rate_min = 0; - /* By default, PIXEL_RATE is read only */ - ov13b->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov13b10_ctrl_ops, - V4L2_CID_PIXEL_RATE, - pixel_rate_min, pixel_rate_max, - 1, pixel_rate_max); + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, OV13B10_PIXEL_RATE, 1, OV13B10_PIXEL_RATE); + + h_blank = mode->hts_def - mode->width; + ov13b10->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (ov13b10->hblank) + ov13b10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; - mode = ov13b->cur_mode; vblank_def = mode->vts_def - mode->height; - vblank_min = mode->vts_min - mode->height; - ov13b->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov13b10_ctrl_ops, - V4L2_CID_VBLANK, - vblank_min, - OV13B10_VTS_MAX - mode->height, 1, - vblank_def); + ov13b10->vblank = v4l2_ctrl_new_std(handler, &ov13b10_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + OV13B10_VTS_MAX - mode->height, + 1, vblank_def); - hblank = link_freq_configs[mode->link_freq_index].pixels_per_line - - mode->width; - ov13b->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov13b10_ctrl_ops, - V4L2_CID_HBLANK, - hblank, hblank, 1, hblank); - if (ov13b->hblank) - ov13b->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + exposure_max = mode->vts_def - 4; + ov13b10->exposure = v4l2_ctrl_new_std(handler, &ov13b10_ctrl_ops, + V4L2_CID_EXPOSURE, OV13B10_EXPOSURE_MIN, + exposure_max, OV13B10_EXPOSURE_STEP, + mode->exp_def); - exposure_max = mode->vts_def - 8; - ov13b->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov13b10_ctrl_ops, - V4L2_CID_EXPOSURE, - OV13B10_EXPOSURE_MIN, - exposure_max, OV13B10_EXPOSURE_STEP, - exposure_max); + ov13b10->anal_gain = v4l2_ctrl_new_std(handler, &ov13b10_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, OV13B10_GAIN_MIN, + OV13B10_GAIN_MAX, OV13B10_GAIN_STEP, + OV13B10_GAIN_DEFAULT); - v4l2_ctrl_new_std(ctrl_hdlr, &ov13b10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, - OV13B10_ANA_GAIN_MIN, OV13B10_ANA_GAIN_MAX, - OV13B10_ANA_GAIN_STEP, OV13B10_ANA_GAIN_DEFAULT); + ov13b10->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &ov13b10_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov13b10_test_pattern_menu) - 1, + 0, 0, ov13b10_test_pattern_menu); - /* Digital gain */ - v4l2_ctrl_new_std(ctrl_hdlr, &ov13b10_ctrl_ops, V4L2_CID_DIGITAL_GAIN, - OV13B10_DGTL_GAIN_MIN, OV13B10_DGTL_GAIN_MAX, - OV13B10_DGTL_GAIN_STEP, OV13B10_DGTL_GAIN_DEFAULT); - - v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov13b10_ctrl_ops, - V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(ov13b10_test_pattern_menu) - 1, - 0, 0, ov13b10_test_pattern_menu); - - v4l2_ctrl_new_std(ctrl_hdlr, &ov13b10_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(ctrl_hdlr, &ov13b10_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - - if (ctrl_hdlr->error) { - ret = ctrl_hdlr->error; - dev_err(&client->dev, "%s control init failed (%d)\n", - __func__, ret); - goto error; + if (handler->error) { + ret = handler->error; + dev_err(&ov13b10->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; } - ret = v4l2_fwnode_device_parse(&client->dev, &props); - if (ret) - goto error; - - ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov13b10_ctrl_ops, - &props); - if (ret) - goto error; - - ov13b->sd.ctrl_handler = ctrl_hdlr; + ov13b10->subdev.ctrl_handler = handler; return 0; -error: - v4l2_ctrl_handler_free(ctrl_hdlr); - mutex_destroy(&ov13b->mutex); +err_free_handler: + v4l2_ctrl_handler_free(handler); return ret; } -static void ov13b10_free_controls(struct ov13b10 *ov13b) +static int ov13b10_check_sensor_id(struct ov13b10 *ov13b10, + struct i2c_client *client) { - v4l2_ctrl_handler_free(ov13b->sd.ctrl_handler); - mutex_destroy(&ov13b->mutex); -} + struct device *dev = &ov13b10->client->dev; + u32 id = 0; + int ret = 0; -static int ov13b10_check_hwcfg(struct device *dev) -{ - struct v4l2_fwnode_endpoint bus_cfg = { - .bus_type = V4L2_MBUS_CSI2_DPHY - }; - struct fwnode_handle *ep; - struct fwnode_handle *fwnode = dev_fwnode(dev); - unsigned int i, j; - int ret; - u32 ext_clk; + ret = ov13b10_read_reg(client, OV13B10_REG_CHIP_ID, + OV13B10_REG_VALUE_16BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } - if (!fwnode) - return -ENXIO; - - ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", - &ext_clk); + ret = ov13b10_read_reg(client, OV13B10_CHIP_REVISION_REG, + OV13B10_REG_VALUE_08BIT, &id); if (ret) { - dev_err(dev, "can't get clock frequency"); + dev_err(dev, "Read chip revision register error\n"); return ret; } - if (ext_clk != OV13B10_EXT_CLK) { - dev_err(dev, "external clock %d is not supported", - ext_clk); + if (id == OV13B10_R2A) + ov13b10_global_regs = ov13b10_global_regs_r2a; + else + ov13b10_global_regs = ov13b10_global_regs_r1a; + dev_info(dev, "Detected OV%06x sensor, REVISION 0x%x\n", CHIP_ID, id); + + return 0; +} + +static int ov13b10_configure_regulators(struct ov13b10 *ov13b10) +{ + unsigned int i = 0; + + for (i = 0; i < OV13B10_NUM_SUPPLIES; i++) + ov13b10->supplies[i].supply = ov13b10_supply_names[i]; + + return devm_regulator_bulk_get(&ov13b10->client->dev, + OV13B10_NUM_SUPPLIES, + ov13b10->supplies); +} + +static int ov13b10_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct ov13b10 *ov13b10; + struct v4l2_subdev *sd; + char facing[2]; + int ret = 0; + struct device_node *eeprom_ctrl_node; + struct i2c_client *eeprom_ctrl_client; + struct v4l2_subdev *eeprom_ctrl; + struct otp_info *otp_ptr; + u32 i, hdr_mode = 0; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + ov13b10 = devm_kzalloc(dev, sizeof(*ov13b10), GFP_KERNEL); + if (!ov13b10) + return -ENOMEM; + + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov13b10->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov13b10->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov13b10->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov13b10->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); return -EINVAL; } - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); - if (!ep) - return -ENXIO; - - ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); - fwnode_handle_put(ep); - if (ret) - return ret; - - if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV13B10_DATA_LANES) { - dev_err(dev, "number of CSI2 data lanes %d is not supported", - bus_cfg.bus.mipi_csi2.num_data_lanes); - ret = -EINVAL; - goto out_err; - } - - if (!bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "no link frequencies defined"); - ret = -EINVAL; - goto out_err; - } - - for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { - for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { - if (link_freq_menu_items[i] == - bus_cfg.link_frequencies[j]) - break; - } - - if (j == bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "no link frequency %lld supported", - link_freq_menu_items[i]); - ret = -EINVAL; - goto out_err; - } - } - -out_err: - v4l2_fwnode_endpoint_free(&bus_cfg); - - return ret; -} - -static int ov13b10_probe(struct i2c_client *client) -{ - struct ov13b10 *ov13b; - bool full_power; - int ret; - - /* Check HW config */ - ret = ov13b10_check_hwcfg(&client->dev); + ret = of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode); if (ret) { - dev_err(&client->dev, "failed to check hwcfg: %d", ret); + hdr_mode = NO_HDR; + dev_warn(dev, " Get hdr mode failed! no hdr default\n"); + } + ov13b10->client = client; + ov13b10->cfg_num = ARRAY_SIZE(supported_modes); + for (i = 0; i < ov13b10->cfg_num; i++) { + if (hdr_mode == supported_modes[i].hdr_mode) { + ov13b10->cur_mode = &supported_modes[i]; + break; + } + } + if (i == ov13b10->cfg_num) + ov13b10->cur_mode = &supported_modes[0]; + + ov13b10->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ov13b10->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + ov13b10->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(ov13b10->power_gpio)) + dev_warn(dev, "Failed to get power-gpios, maybe no use\n"); + + ov13b10->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov13b10->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + ov13b10->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(ov13b10->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = ov13b10_configure_regulators(ov13b10); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); return ret; } - ov13b = devm_kzalloc(&client->dev, sizeof(*ov13b), GFP_KERNEL); - if (!ov13b) - return -ENOMEM; + ov13b10->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(ov13b10->pinctrl)) { + ov13b10->pins_default = + pinctrl_lookup_state(ov13b10->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(ov13b10->pins_default)) + dev_err(dev, "could not get default pinstate\n"); - /* Initialize subdev */ - v4l2_i2c_subdev_init(&ov13b->sd, client, &ov13b10_subdev_ops); + ov13b10->pins_sleep = + pinctrl_lookup_state(ov13b10->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(ov13b10->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } - full_power = acpi_dev_state_d0(&client->dev); - if (full_power) { - /* Check module identity */ - ret = ov13b10_identify_module(ov13b); - if (ret) { - dev_err(&client->dev, "failed to find sensor: %d\n", ret); - return ret; + mutex_init(&ov13b10->mutex); + + sd = &ov13b10->subdev; + v4l2_i2c_subdev_init(sd, client, &ov13b10_subdev_ops); + ret = ov13b10_initialize_controls(ov13b10); + if (ret) + goto err_destroy_mutex; + + ret = __ov13b10_power_on(ov13b10); + if (ret) + goto err_free_handler; + + ret = ov13b10_check_sensor_id(ov13b10, 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) { + ov13b10->otp = otp_ptr; + } else { + ov13b10->otp = NULL; + devm_kfree(dev, otp_ptr); + } } } - /* Set default mode to max resolution */ - ov13b->cur_mode = &supported_modes[0]; +continue_probe: - ret = ov13b10_init_controls(ov13b); - if (ret) - return ret; - - /* Initialize subdev */ - ov13b->sd.internal_ops = &ov13b10_internal_ops; - ov13b->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - ov13b->sd.entity.ops = &ov13b10_subdev_entity_ops; - ov13b->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - - /* Initialize source pad */ - ov13b->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&ov13b->sd.entity, 1, &ov13b->pad); - if (ret) { - dev_err(&client->dev, "%s failed:%d\n", __func__, ret); - goto error_handler_free; - } - - - /* - * Device is already turned on by i2c-core with ACPI domain PM. - * Enable runtime PM and turn off the device. - */ - /* Set the device's state to active if it's in D0 state. */ - if (full_power) - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); - - ret = v4l2_async_register_subdev_sensor(&ov13b->sd); +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &ov13b10_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + ov13b10->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov13b10->pad); if (ret < 0) - goto error_media_entity_runtime_pm; + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(ov13b10->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov13b10->module_index, facing, + OV13B10_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); return 0; -error_media_entity_runtime_pm: - pm_runtime_disable(&client->dev); - if (full_power) - pm_runtime_set_suspended(&client->dev); - media_entity_cleanup(&ov13b->sd.entity); - -error_handler_free: - ov13b10_free_controls(ov13b); - dev_err(&client->dev, "%s failed:%d\n", __func__, ret); +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __ov13b10_power_off(ov13b10); +err_free_handler: + v4l2_ctrl_handler_free(&ov13b10->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ov13b10->mutex); return ret; } @@ -1472,42 +1796,57 @@ error_handler_free: static void ov13b10_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ov13b10 *ov13b = to_ov13b10(sd); + struct ov13b10 *ov13b10 = to_ov13b10(sd); v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&sd->entity); - ov13b10_free_controls(ov13b); +#endif + v4l2_ctrl_handler_free(&ov13b10->ctrl_handler); + mutex_destroy(&ov13b10->mutex); pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __ov13b10_power_off(ov13b10); pm_runtime_set_suspended(&client->dev); } -static const struct dev_pm_ops ov13b10_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(ov13b10_suspend, ov13b10_resume) +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov13b10_of_match[] = { + { .compatible = "ovti,ov13b10" }, + {}, }; - -#ifdef CONFIG_ACPI -static const struct acpi_device_id ov13b10_acpi_ids[] = { - {"OVTIDB10"}, - { /* sentinel */ } -}; - -MODULE_DEVICE_TABLE(acpi, ov13b10_acpi_ids); +MODULE_DEVICE_TABLE(of, ov13b10_of_match); #endif +static const struct i2c_device_id ov13b10_match_id[] = { + { "ovti,ov13b10", 0 }, + { }, +}; + static struct i2c_driver ov13b10_i2c_driver = { .driver = { - .name = "ov13b10", + .name = OV13B10_NAME, .pm = &ov13b10_pm_ops, - .acpi_match_table = ACPI_PTR(ov13b10_acpi_ids), + .of_match_table = of_match_ptr(ov13b10_of_match), }, - .probe_new = ov13b10_probe, - .remove = ov13b10_remove, - .flags = I2C_DRV_ACPI_WAIVE_D0_PROBE, + .probe = ov13b10_probe, + .remove = ov13b10_remove, + .id_table = ov13b10_match_id, }; -module_i2c_driver(ov13b10_i2c_driver); +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&ov13b10_i2c_driver); +} -MODULE_AUTHOR("Kao, Arec "); -MODULE_DESCRIPTION("Omnivision ov13b10 sensor driver"); +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&ov13b10_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("OmniVision ov13b10 sensor driver"); MODULE_LICENSE("GPL v2"); From dbcc3c130c5028da4a9f24e32d52898193c2d52f Mon Sep 17 00:00:00 2001 From: Algea Cao Date: Mon, 28 Jul 2025 11:14:29 +0800 Subject: [PATCH 04/11] drm/rockchip: dw_hdmi: Do not enable DSC when the DSC compression ratio is below 0.375. If the DSC mode with a compression rate lower than 0.375 is to be supported, the dclk clock source of the VOP bound to HDMI must be a CRU PLL that supports fractional frequency division. However, in most scenarios, HDMI is unable to be assigned such a PLL. So in this scenario, instead of enabling DSC, we switch to YUV420 format. Change-Id: I450cdd5857e4384894651ed063fac152a8d9bb0f Signed-off-by: Algea Cao --- drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 41 +++++++++++++++------ 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index f77b349f5187..dd2aa4004cf5 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -1036,23 +1036,36 @@ rockchip_hdmi_find_by_id(struct device_driver *drv, unsigned int id) return dev_get_drvdata(dev); } +static bool rockchip_hdmi_check_dsc_rate_supported(struct rockchip_hdmi *hdmi, + u64 tmdsclk, u8 bpp) +{ + u64 data_rate, dsc_rate; + u64 frl_rate, dsc_frl_rate; + + frl_rate = (u64)hdmi->hdmi21_data.max_lanes * + hdmi->hdmi21_data.max_frl_rate_per_lane * 1000000000; + dsc_frl_rate = (u64)hdmi->hdmi21_data.dsc_cap.max_lanes * + hdmi->hdmi21_data.dsc_cap.max_frl_rate_per_lane * 1000000000; + data_rate = (u64)tmdsclk * bpp; + data_rate = DIV_ROUND_UP_ULL(data_rate * 18, 16); + /* compression ratio needs to be greater than 0.375. */ + dsc_rate = DIV_ROUND_UP_ULL(data_rate * 9, 24); + + if ((data_rate > frl_rate) && (dsc_rate > dsc_frl_rate)) + return true; + + return false; +} + static bool rockchip_hdmi_if_dsc_enable(struct rockchip_hdmi *hdmi, unsigned int tmdsclk) { - u64 data_rate; - u64 frl_rate = (u64)hdmi->link_cfg.frl_lanes * hdmi->link_cfg.rate_per_lane * 1000000; u8 bpp = hdmi_bus_fmt_color_depth(hdmi->bus_format) * 3; /* rk3588 dsc can't support yuv420/422 dsc */ if (hdmi_bus_fmt_is_yuv420(hdmi->bus_format) || hdmi_bus_fmt_is_yuv422(hdmi->bus_format)) return false; - data_rate = (u64)tmdsclk * bpp; - data_rate = DIV_ROUND_UP_ULL(data_rate * 18, 16); - - if (data_rate > frl_rate) - return true; - - return false; + return rockchip_hdmi_check_dsc_rate_supported(hdmi, tmdsclk, bpp); } static void hdmi_select_link_config(struct rockchip_hdmi *hdmi, @@ -1092,7 +1105,7 @@ static void hdmi_select_link_config(struct rockchip_hdmi *hdmi, max_dsc_rate_per_lane = hdmi->hdmi21_data.dsc_cap.max_frl_rate_per_lane; - if (rockchip_hdmi_if_dsc_enable(hdmi, tmdsclk)) { + if (rockchip_hdmi_if_dsc_enable(hdmi, tmdsclk * 1000)) { hdmi->link_cfg.dsc_mode = true; hdmi->link_cfg.frl_lanes = max_dsc_lanes; hdmi->link_cfg.rate_per_lane = max_dsc_rate_per_lane; @@ -2419,6 +2432,7 @@ dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state, bool support_dc = false; bool sink_is_hdmi = true; bool yuv422_out = false; + bool dsc_rate_supported; u32 max_tmds_clock = info->max_tmds_clock; int output_eotf; @@ -2537,9 +2551,14 @@ dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state, DRM_MODE_FLAG_3D_FRAME_PACKING) pixclock *= 2; + tmdsclock = hdmi_get_tmdsclock(hdmi, mode.clock * 1000); + dsc_rate_supported = + rockchip_hdmi_check_dsc_rate_supported(hdmi, tmdsclock, color_depth * 3); + if (drm_mode_is_420_only(info, &mode) || (hdmi->is_hdmi_qp && mode.clock > 1188000 && - (*color_format == RK_IF_FORMAT_YCBCR422 || hdmi->force_disable_dsc))) + (*color_format == RK_IF_FORMAT_YCBCR422 || hdmi->force_disable_dsc || + !dsc_rate_supported))) *color_format = RK_IF_FORMAT_YCBCR420; if (!sink_is_hdmi) { From b30177fbce0be1ac74f3755b13816d26ae7bf3fe Mon Sep 17 00:00:00 2001 From: Johan Jonker Date: Thu, 22 Dec 2022 15:26:57 +0100 Subject: [PATCH 05/11] UPSTREAM: dt-bindings: display: bridge: convert analogix_dp.txt to yaml Convert analogix_dp.txt to yaml for use as common document. Changed: Relexed requirements Change-Id: I7d667c53d51b79eeb79b8ea03002bfc696454094 Signed-off-by: Johan Jonker Reviewed-by: Rob Herring Signed-off-by: Heiko Stuebner Signed-off-by: Damon Ding Link: https://patchwork.freedesktop.org/patch/msgid/489e7bd3-fa26-885f-4104-8b0b29aa4f2b@gmail.com (cherry picked from commit 440112adadd3e273d2a2ff6e5d4b969ee03fda47) --- .../bindings/display/bridge/analogix,dp.yaml | 63 +++++++++++++++++++ .../bindings/display/bridge/analogix_dp.txt | 57 ----------------- .../bindings/display/exynos/exynos_dp.txt | 2 +- 3 files changed, 64 insertions(+), 58 deletions(-) create mode 100644 Documentation/devicetree/bindings/display/bridge/analogix,dp.yaml delete mode 100644 Documentation/devicetree/bindings/display/bridge/analogix_dp.txt diff --git a/Documentation/devicetree/bindings/display/bridge/analogix,dp.yaml b/Documentation/devicetree/bindings/display/bridge/analogix,dp.yaml new file mode 100644 index 000000000000..c9b06885cc63 --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/analogix,dp.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/analogix,dp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analogix Display Port bridge + +maintainers: + - Rob Herring + +properties: + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: true + + clock-names: true + + phys: true + + phy-names: + const: dp + + force-hpd: + description: + Indicate driver need force hpd when hpd detect failed, this + is used for some eDP screen which don not have a hpd signal. + + hpd-gpios: + description: + Hotplug detect GPIO. + Indicates which GPIO should be used for hotplug detection + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: + Input node to receive pixel data. + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: + Port node with one endpoint connected to a dp-connector node. + + required: + - port@0 + - port@1 + +required: + - reg + - interrupts + - clock-names + - clocks + - ports + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt b/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt deleted file mode 100644 index a548f404033c..000000000000 --- a/Documentation/devicetree/bindings/display/bridge/analogix_dp.txt +++ /dev/null @@ -1,57 +0,0 @@ -Analogix Display Port bridge bindings - -Required properties for dp-controller: - -compatible: - platform specific such as: - * "samsung,exynos5-dp" - * "rockchip,rk3288-dp" - * "rockchip,rk3399-edp" - -reg: - physical base address of the controller and length - of memory mapped region. - -interrupts: - interrupt combiner values. - -clocks: - from common clock binding: handle to dp clock. - -clock-names: - from common clock binding: Shall be "dp". - -phys: - from general PHY binding: the phandle for the PHY device. - -phy-names: - from general PHY binding: Should be "dp". - -Optional properties for dp-controller: - -analogix,video-bist-enable: - Enable video bist pattern for DP_TX debugging. - -force-hpd: - Indicate driver need force hpd when hpd detect failed, this - is used for some eDP screen which don't have hpd signal. - -hpd-gpios: - Hotplug detect GPIO. - Indicates which GPIO should be used for hotplug detection - -panel-self-test: - Enable optional LCD Panel Self Test. - -port@[X]: SoC specific port nodes with endpoint definitions as defined - in Documentation/devicetree/bindings/media/video-interfaces.txt, - please refer to the SoC specific binding document: - * Documentation/devicetree/bindings/display/exynos/exynos_dp.txt - * Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt - -support-psr: - Enable Source's PSR capability. - -[1]: Documentation/devicetree/bindings/media/video-interfaces.txt -------------------------------------------------------------------------------- - -Example: - - dp-controller { - compatible = "samsung,exynos5-dp"; - reg = <0x145b0000 0x10000>; - interrupts = <10 3>; - interrupt-parent = <&combiner>; - clocks = <&clock 342>; - clock-names = "dp"; - - phys = <&dp_phy>; - phy-names = "dp"; - }; diff --git a/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt b/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt index 9b6cba3f82af..3a401590320f 100644 --- a/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt +++ b/Documentation/devicetree/bindings/display/exynos/exynos_dp.txt @@ -50,7 +50,7 @@ Optional properties for dp-controller: Documentation/devicetree/bindings/display/panel/display-timing.txt For the below properties, please refer to Analogix DP binding document: - * Documentation/devicetree/bindings/display/bridge/analogix_dp.txt + * Documentation/devicetree/bindings/display/bridge/analogix,dp.yaml -phys (required) -phy-names (required) -hpd-gpios (optional) From 57fc3e6d41045abd9710064a6cc8b5149abc0c22 Mon Sep 17 00:00:00 2001 From: Johan Jonker Date: Thu, 22 Dec 2022 15:27:35 +0100 Subject: [PATCH 06/11] UPSTREAM: dt-bindings: display: rockchip: convert analogix_dp-rockchip.txt to yaml Convert analogix_dp-rockchip.txt to yaml. Changed: Add power-domains property File name Change-Id: Ibd1493c5b35697e37a7b22b454dbafc3e035ab9e Signed-off-by: Johan Jonker Reviewed-by: Rob Herring Signed-off-by: Heiko Stuebner Signed-off-by: Damon Ding Link: https://patchwork.freedesktop.org/patch/msgid/88a5a9e3-9bc8-5966-22ec-5bdb1fa7a5b1@gmail.com (cherry picked from commit 9bb35d4c3230803345d5218676a7325b26823209) --- .../display/rockchip/analogix_dp-rockchip.txt | 98 ----------------- .../rockchip/rockchip,analogix-dp.yaml | 103 ++++++++++++++++++ 2 files changed, 103 insertions(+), 98 deletions(-) delete mode 100644 Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt create mode 100644 Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml diff --git a/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt deleted file mode 100644 index 43561584c13a..000000000000 --- a/Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt +++ /dev/null @@ -1,98 +0,0 @@ -Rockchip RK3288 specific extensions to the Analogix Display Port -================================ - -Required properties: -- compatible: "rockchip,rk3288-dp", - "rockchip,rk3399-edp"; - -- reg: physical base address of the controller and length - -- clocks: from common clock binding: handle to dp clock. - of memory mapped region. - -- clock-names: from common clock binding: - Required elements: "dp" "pclk" - -- resets: Must contain an entry for each entry in reset-names. - See ../reset/reset.txt for details. - -- pinctrl-names: Names corresponding to the chip hotplug pinctrl states. -- pinctrl-0: pin-control mode. should be <&edp_hpd> - -- reset-names: Must include the name "dp" - -- rockchip,grf: this soc should set GRF regs, so need get grf here. - -- ports: there are 2 port nodes with endpoint definitions as defined in - Documentation/devicetree/bindings/media/video-interfaces.txt. - Port 0: contained 2 endpoints, connecting to the output of vop. - Port 1: contained 1 endpoint, connecting to the input of panel. - -Optional property for different chips: -- clocks: from common clock binding: handle to grf_vio clock. - -- clock-names: from common clock binding: - Required elements: "grf" - -For the below properties, please refer to Analogix DP binding document: - * Documentation/devicetree/bindings/display/bridge/analogix_dp.txt -- phys (required) -- phy-names (required) -- hpd-gpios (optional) -- force-hpd (optional) -------------------------------------------------------------------------------- - -Example: - dp-controller: dp@ff970000 { - compatible = "rockchip,rk3288-dp"; - reg = <0xff970000 0x4000>; - interrupts = ; - clocks = <&cru SCLK_EDP>, <&cru PCLK_EDP_CTRL>; - clock-names = "dp", "pclk"; - phys = <&dp_phy>; - phy-names = "dp"; - - rockchip,grf = <&grf>; - resets = <&cru 111>; - reset-names = "dp"; - - pinctrl-names = "default"; - pinctrl-0 = <&edp_hpd>; - - - ports { - #address-cells = <1>; - #size-cells = <0>; - edp_in: port@0 { - reg = <0>; - #address-cells = <1>; - #size-cells = <0>; - edp_in_vopb: endpoint@0 { - reg = <0>; - remote-endpoint = <&vopb_out_edp>; - }; - edp_in_vopl: endpoint@1 { - reg = <1>; - remote-endpoint = <&vopl_out_edp>; - }; - }; - - edp_out: port@1 { - reg = <1>; - #address-cells = <1>; - #size-cells = <0>; - edp_out_panel: endpoint { - reg = <0>; - remote-endpoint = <&panel_in_edp> - }; - }; - }; - }; - - pinctrl { - edp { - edp_hpd: edp-hpd { - rockchip,pins = <7 11 RK_FUNC_2 &pcfg_pull_none>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml new file mode 100644 index 000000000000..60dedf9b2be7 --- /dev/null +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml @@ -0,0 +1,103 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/rockchip/rockchip,analogix-dp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip specific extensions to the Analogix Display Port + +maintainers: + - Sandy Huang + - Heiko Stuebner + +properties: + compatible: + enum: + - rockchip,rk3288-dp + - rockchip,rk3399-edp + + clocks: + minItems: 2 + maxItems: 3 + + clock-names: + minItems: 2 + items: + - const: dp + - const: pclk + - const: grf + + power-domains: + maxItems: 1 + + resets: + maxItems: 1 + + reset-names: + const: dp + + rockchip,grf: + $ref: /schemas/types.yaml#/definitions/phandle + description: + This SoC makes use of GRF regs. + +required: + - compatible + - clocks + - clock-names + - resets + - reset-names + - rockchip,grf + +allOf: + - $ref: /schemas/display/bridge/analogix,dp.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + dp@ff970000 { + compatible = "rockchip,rk3288-dp"; + reg = <0xff970000 0x4000>; + interrupts = ; + clocks = <&cru SCLK_EDP>, <&cru PCLK_EDP_CTRL>; + clock-names = "dp", "pclk"; + phys = <&dp_phy>; + phy-names = "dp"; + resets = <&cru 111>; + reset-names = "dp"; + rockchip,grf = <&grf>; + pinctrl-0 = <&edp_hpd>; + pinctrl-names = "default"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + edp_in: port@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + edp_in_vopb: endpoint@0 { + reg = <0>; + remote-endpoint = <&vopb_out_edp>; + }; + edp_in_vopl: endpoint@1 { + reg = <1>; + remote-endpoint = <&vopl_out_edp>; + }; + }; + + edp_out: port@1 { + reg = <1>; + + edp_out_panel: endpoint { + remote-endpoint = <&panel_in_edp>; + }; + }; + }; + }; From a73b31a8da976fc218440cbbffa4cb11cfb50190 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Mon, 10 Mar 2025 18:41:10 +0800 Subject: [PATCH 07/11] UPSTREAM: dt-bindings: display: rockchip: analogix-dp: Add support for RK3588 Compared with RK3288/RK3399, the HBR2 link rate support is the main improvement of RK3588 eDP TX controller, and there are also two independent eDP display interfaces on RK3588 Soc. The newly added 'apb' reset is to ensure the APB bus of eDP controller works well on the RK3588 SoC. Reviewed-by: Rob Herring (Arm) Change-Id: If3864613762898624ba39ad0395516a4ebb02732 Signed-off-by: Damon Ding Link: https://lore.kernel.org/r/20250310104114.2608063-10-damon.ding@rock-chips.com Signed-off-by: Dmitry Baryshkov (cherry picked from commit f855146263b14abadd8d5bd0e280e54fbab3bd18) --- .../rockchip/rockchip,analogix-dp.yaml | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml index 60dedf9b2be7..48dc76328426 100644 --- a/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml @@ -15,6 +15,7 @@ properties: enum: - rockchip,rk3288-dp - rockchip,rk3399-edp + - rockchip,rk3588-edp clocks: minItems: 2 @@ -31,10 +32,14 @@ properties: maxItems: 1 resets: - maxItems: 1 + minItems: 1 + maxItems: 2 reset-names: - const: dp + minItems: 1 + items: + - const: dp + - const: apb rockchip,grf: $ref: /schemas/types.yaml#/definitions/phandle @@ -52,6 +57,19 @@ required: allOf: - $ref: /schemas/display/bridge/analogix,dp.yaml# + - if: + properties: + compatible: + contains: + enum: + - rockchip,rk3588-edp + then: + properties: + resets: + minItems: 2 + reset-names: + minItems: 2 + unevaluatedProperties: false examples: From 39843b4ec3177b1102245004b2c4fb3fdd1d1288 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Fri, 18 Jul 2025 16:52:09 +0800 Subject: [PATCH 08/11] dt-bindings: display: rockchip: analogix-dp: Add support for RK3576 The RK3576 eDP TX controller is the same as that of RK3588. Change-Id: I3f32329866bc70f6f26132eb583f520e39f53594 Signed-off-by: Damon Ding --- .../bindings/display/rockchip/rockchip,analogix-dp.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml index 48dc76328426..15658c81a5d1 100644 --- a/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml @@ -15,6 +15,7 @@ properties: enum: - rockchip,rk3288-dp - rockchip,rk3399-edp + - rockchip,rk3576-edp - rockchip,rk3588-edp clocks: @@ -62,6 +63,7 @@ allOf: compatible: contains: enum: + - rockchip,rk3576-edp - rockchip,rk3588-edp then: properties: From d191490eb5d008c168b3fb7f89583da65af3d8ee Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Tue, 22 Jul 2025 15:08:27 +0800 Subject: [PATCH 09/11] dt-bindings: display: rockchip: analogix-dp: Add compatible for RK3568 The RK3568 eDP TX controller is almost the same as that of RK3399. It supports RBR/HBR with 1/2/4 lanes and the max supported resolution is 2560x1600p60. The slight difference with RK3399 is the newly added 'apb' reset, which is just like that of RK3588/RK3576. Change-Id: Ifd5bc2d8f337b794a6d2983b689d2bd2271d78c2 Signed-off-by: Damon Ding --- .../bindings/display/rockchip/rockchip,analogix-dp.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml index 15658c81a5d1..3ba863c4fdd6 100644 --- a/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml @@ -15,6 +15,7 @@ properties: enum: - rockchip,rk3288-dp - rockchip,rk3399-edp + - rockchip,rk3568-edp - rockchip,rk3576-edp - rockchip,rk3588-edp @@ -63,6 +64,7 @@ allOf: compatible: contains: enum: + - rockchip,rk3568-edp - rockchip,rk3576-edp - rockchip,rk3588-edp then: From c516e523a235942baeca4c83c0ed907e889ad037 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Tue, 22 Jul 2025 11:20:59 +0800 Subject: [PATCH 10/11] dt-bindings: display: rockchip: analogix-dp: Add properties for dual-channel/split modes After converting analogix_dp.txt to yaml, the descriptions of properties for dual-channel and split modes, which have been already supported, should be added synchronously. Change-Id: I8a66ef3ed8c469eca0c9d6e06a827c1f1a8d58a1 Signed-off-by: Damon Ding --- .../rockchip/rockchip,analogix-dp.yaml | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml index 3ba863c4fdd6..e4761ea6e3f4 100644 --- a/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml @@ -43,11 +43,52 @@ properties: - const: dp - const: apb + rockchip,data-swap: + description: + Indicate whether to enable data swap function for split mode or dual + channel mode. It can be helpful to deal with the reversed hardware + design issue in left and right display interfaces. + + rockchip,dual-channel: + description: + Indicate whether to enable dual channel mode, which horizontally + splits a single video port's output to drive one displays with + identical interfaces and consistent display timing. + | | ---> | eDP0 | \ | | + | Video Port | --> | Panel | + | | ---> | eDP1 | / | | + + rockchip,dual-connector-split: + description: + Indicate whether to enable dual connector split mode, which horizontally + splits a single video port's output to drive two displays with different + interfaces and consistent display timing. + | | ---> | eDP0 | ---> | | | + | Video Port | | Panel0(Left) | Panel1(right) | + | | ---> | MIPI1 | | | | + ----------------------------------^ + rockchip,grf: $ref: /schemas/types.yaml#/definitions/phandle description: This SoC makes use of GRF regs. + rockchip,left-display: + description: + Assign the specific interface for the left display in dual connector + split mode. It can be helpful to deal with the reversed hardware + design issue in left and right display interfaces. + + rockchip,split-mode: + description: + Indicate whether to enable split mode, which horizontally splits + a single video port's output to drive two displays with identical + interfaces and consistent display timing. + | | ---> | eDP0 | ---> | | | + | Video Port | | Panel0(Left) | Panel1(right) | + | | ---> | eDP1 | | | | + ---------------------------------^ + required: - compatible - clocks From 72d923c5f8afbe8f4a89eb24d44ee9f35fcbf297 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Fri, 18 Jul 2025 17:15:39 +0800 Subject: [PATCH 11/11] dt-bindings: display: rockchip: analogix-dp: Add property rockchip,disable-psr Since the PSR feature can help to reduce the power consumption, the Source device, which can support PSR function, should enable PSR if the PSR capability of Sink device is detected. If the user truly does not want to enable PSR function or the Panel has something wrong with it, the property 'rockchip,disable-psr' will be helpful. Change-Id: I03b3c83c7c88ea3fc3ccd447e5c5da49e16f22a9 Signed-off-by: Damon Ding --- .../bindings/display/rockchip/rockchip,analogix-dp.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml index e4761ea6e3f4..367285bfcc56 100644 --- a/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml @@ -49,6 +49,11 @@ properties: channel mode. It can be helpful to deal with the reversed hardware design issue in left and right display interfaces. + rockchip,disable-psr: + description: + Indicate whether to disable PSR function when the PSR + capability of Sink device is detected. + rockchip,dual-channel: description: Indicate whether to enable dual channel mode, which horizontally