From 517ffd6317062d563dd46d179c6a9b923e8e7faf Mon Sep 17 00:00:00 2001 From: Zhang Yubing Date: Thu, 25 Jan 2024 16:35:37 +0800 Subject: [PATCH 01/22] drm/rockchip: vop2: dump encdoer name in summary info In most rockchip display interface driver, only one connector attach to a encoder. it can be sured which econder is used by the connector name. For DP MST, a DP MST connector will attach to many DP MST encoders, and more than one DP MST connector can attach to a DP MST encoder. A DP MST encoder's possible crtc can also more than one. So in this case, which DP MST encoder is used can't be get by the crct name or connector name. A encoder name is necessary for DP MST encoder. Change-Id: I57026b25d63cde8f72a6e34dc56e073559659821 Signed-off-by: Zhang Yubing --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 8947214a0e65..2440c2261ac6 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -6331,7 +6331,8 @@ static void vop2_dump_connector_on_crtc(struct drm_crtc *crtc, struct seq_file * drm_connector_list_iter_begin(crtc->dev, &conn_iter); drm_for_each_connector_iter(connector, &conn_iter) { if (crtc->state->connector_mask & drm_connector_mask(connector)) - DEBUG_PRINT(" Connector: %s\n", connector->name); + DEBUG_PRINT(" Connector:%s\tEncoder: %s\n", + connector->name, connector->encoder->name); } drm_connector_list_iter_end(&conn_iter); From 096f12b891173646c2c8d31d72448f67a638beda Mon Sep 17 00:00:00 2001 From: Zhen Chen Date: Wed, 13 Dec 2023 09:33:09 +0800 Subject: [PATCH 02/22] Mali: midgard: replace add_mm_counter() with kbasep_add_mm_counter() The purpose is to eliminate direct references to add_mm_counter(). add_mm_counter() makes use of mm_trace_rss_stat(). While, Android 14.0 GKI prohibits driver modules from referencing mm_trace_rss_stat(). The implementation of kbasep_add_mm_counter() here is derived from bifrost DDK g21. Change-Id: I619e4ecb164e87dcab8f101bae179f316d57360c Signed-off-by: Zhen Chen --- .../gpu/arm/midgard/mali_kbase_mem_linux.c | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/arm/midgard/mali_kbase_mem_linux.c b/drivers/gpu/arm/midgard/mali_kbase_mem_linux.c index a79975c6a0b2..876c01873ca7 100644 --- a/drivers/gpu/arm/midgard/mali_kbase_mem_linux.c +++ b/drivers/gpu/arm/midgard/mali_kbase_mem_linux.c @@ -2364,6 +2364,23 @@ void kbase_vunmap(struct kbase_context *kctx, struct kbase_vmap_struct *map) } KBASE_EXPORT_TEST_API(kbase_vunmap); +static void kbasep_add_mm_counter(struct mm_struct *mm, int member, long value) +{ +#if (KERNEL_VERSION(6, 2, 0) <= LINUX_VERSION_CODE) + /* To avoid the build breakage due to the type change in rss_stat, + * we inline here the equivalent of 'add_mm_counter()' from linux kernel V6.2. + */ + percpu_counter_add(&mm->rss_stat[member], value); +#elif (KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE) + /* To avoid the build breakage due to an unexported kernel symbol 'mm_trace_rss_stat', + * we inline here the equivalent of 'add_mm_counter()' from linux kernel V5.5. + */ + atomic_long_add(value, &mm->rss_stat.count[member]); +#else + add_mm_counter(mm, member, value); +#endif +} + void kbasep_os_process_page_usage_update(struct kbase_context *kctx, int pages) { struct mm_struct *mm; @@ -2373,10 +2390,10 @@ void kbasep_os_process_page_usage_update(struct kbase_context *kctx, int pages) if (mm) { atomic_add(pages, &kctx->nonmapped_pages); #ifdef SPLIT_RSS_COUNTING - add_mm_counter(mm, MM_FILEPAGES, pages); + kbasep_add_mm_counter(mm, MM_FILEPAGES, pages); #else spin_lock(&mm->page_table_lock); - add_mm_counter(mm, MM_FILEPAGES, pages); + kbasep_add_mm_counter(mm, MM_FILEPAGES, pages); spin_unlock(&mm->page_table_lock); #endif } @@ -2401,10 +2418,10 @@ static void kbasep_os_process_page_usage_drain(struct kbase_context *kctx) pages = atomic_xchg(&kctx->nonmapped_pages, 0); #ifdef SPLIT_RSS_COUNTING - add_mm_counter(mm, MM_FILEPAGES, -pages); + kbasep_add_mm_counter(mm, MM_FILEPAGES, -pages); #else spin_lock(&mm->page_table_lock); - add_mm_counter(mm, MM_FILEPAGES, -pages); + kbasep_add_mm_counter(mm, MM_FILEPAGES, -pages); spin_unlock(&mm->page_table_lock); #endif } From c99648df60d3763723de9e443b862da44e8872fe Mon Sep 17 00:00:00 2001 From: Elaine Zhang Date: Tue, 30 Jan 2024 09:17:32 +0800 Subject: [PATCH 03/22] clk: rockchip: add new pll type pll_rk3588_ddr Signed-off-by: Elaine Zhang Change-Id: Ia38a7b5d95a9d5b4c4f27c1adaa310ba4308afbd --- drivers/clk/rockchip/clk-pll.c | 6 +++++- drivers/clk/rockchip/clk.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index 8aa9c30147c9..a109a568199d 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -1409,7 +1409,10 @@ static unsigned long rockchip_rk3588_pll_recalc_rate(struct clk_hw *hw, } rate64 = rate64 >> cur.s; - return (unsigned long)rate64; + if (pll->type == pll_rk3588_ddr) + return (unsigned long)rate64 * 2; + else + return (unsigned long)rate64; } static int rockchip_rk3588_pll_set_params(struct rockchip_clk_pll *pll, @@ -1845,6 +1848,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, #ifdef CONFIG_ROCKCHIP_PLL_RK3588 case pll_rk3588: case pll_rk3588_core: + case pll_rk3588_ddr: if (!pll->rate_table) init.ops = &rockchip_rk3588_pll_clk_norate_ops; else diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index 82345742cb61..8852255098bc 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -452,6 +452,7 @@ enum rockchip_pll_type { pll_rk3399, pll_rk3588, pll_rk3588_core, + pll_rk3588_ddr, }; #define RK3036_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1, \ From 77fc3bec80b5be672e403c884843e2cb3d0fc041 Mon Sep 17 00:00:00 2001 From: Simon Xue Date: Mon, 29 Jan 2024 17:35:28 +0800 Subject: [PATCH 04/22] debugfs: cma: fix bitmap_hex Signed-off-by: Simon Xue Change-Id: I6193bcd72a4761b2e30bb8445c0e7ed1ffe00bf6 --- mm/cma_debug_bitmap_hex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/cma_debug_bitmap_hex.c b/mm/cma_debug_bitmap_hex.c index 3239be097896..bf9f7db28a14 100644 --- a/mm/cma_debug_bitmap_hex.c +++ b/mm/cma_debug_bitmap_hex.c @@ -96,7 +96,7 @@ static int cma_debugfs_add_one(struct cma *cma, struct dentry *root_dentry) struct dentry *tmp; char name[16]; - scnprintf(name, sizeof(name), "cma-%s", cma->name); + scnprintf(name, sizeof(name), "%s", cma->name); tmp = debugfs_lookup(name, root_dentry); if (!tmp) From 084acd7ca3a35bc5a5a93b28db4adb618137be1b Mon Sep 17 00:00:00 2001 From: Ye Zhang Date: Mon, 18 Dec 2023 10:44:30 +0800 Subject: [PATCH 05/22] arm64: dts: rockchip: rk3399: Add dynamic-power-coefficient property for gpu Signed-off-by: Ye Zhang Change-Id: I2b7a70c05b2426b0bab1a67adef8c186bb26f3ab --- arch/arm64/boot/dts/rockchip/rk3399.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index 21a1c8a73511..7fada6f4bc9c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi @@ -2353,10 +2353,11 @@ downdifferential = <10>; status = "disabled"; + dynamic-power-coefficient = <733>; + gpu_power_model: power_model { compatible = "arm,mali-simple-power-model"; static-coefficient = <411000>; - dynamic-coefficient = <733>; ts = <32000 4700 (-80) 2>; thermal-zone = "gpu-thermal"; }; From 7133e80b3c80cea6418e7731b1631e2f95cfa10f Mon Sep 17 00:00:00 2001 From: Ye Zhang Date: Mon, 18 Dec 2023 10:49:16 +0800 Subject: [PATCH 06/22] Mali: midgard: Add support for static power consumption calculation Signed-off-by: Ye Zhang Change-Id: Ic5685241bd7dbe7b5869a623ce25640af7017840 --- .../midgard/backend/gpu/mali_kbase_devfreq.c | 19 +++++++++++++++---- drivers/gpu/arm/midgard/ipa/mali_kbase_ipa.c | 2 ++ .../arm/midgard/ipa/mali_kbase_ipa_simple.c | 6 +++--- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c b/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c index b203dbd8725a..d8fa5a939619 100644 --- a/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c +++ b/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c @@ -343,6 +343,7 @@ int kbase_devfreq_init(struct kbase_device *kbdev) unsigned long opp_rate; unsigned int dyn_power_coeff = 0; int err; + struct device_node *model_dt_node; if (!kbdev->clock) { dev_err(kbdev->dev, "Clock not available for devfreq\n"); @@ -373,10 +374,20 @@ int kbase_devfreq_init(struct kbase_device *kbdev) &ondemand_data.upthreshold); of_property_read_u32(np, "downdifferential", &ondemand_data.downdifferential); - of_property_read_u32(kbdev->dev->of_node, "dynamic-power-coefficient", - &dyn_power_coeff); - if (dyn_power_coeff) - dp->is_cooling_device = true; + + model_dt_node = of_get_compatible_child(np, "arm,mali-simple-power-model"); + if (!model_dt_node) { + err = of_property_read_u32(np, "dynamic-power-coefficient", + &dyn_power_coeff); + if (err) { + dev_err(kbdev->dev, "Couldn't find proper 'dynamic-power-coefficient' in DT\n"); + goto devfreq_add_dev_failed; + } else { + dp->is_cooling_device = true; + } + } else { + of_node_put(model_dt_node); + } kbdev->devfreq = devfreq_add_device(kbdev->dev, dp, "simple_ondemand", &ondemand_data); diff --git a/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa.c b/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa.c index 8e3fa5d436a4..551d914f223f 100644 --- a/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa.c +++ b/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa.c @@ -584,6 +584,8 @@ struct devfreq_cooling_power kbase_ipa_power_model_ops = { #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) .get_static_power = &kbase_get_static_power, .get_dynamic_power = &kbase_get_dynamic_power, +#else + .get_real_power = kbase_get_real_power, #endif }; KBASE_EXPORT_TEST_API(kbase_ipa_power_model_ops); diff --git a/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa_simple.c b/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa_simple.c index da0a4d4a0e7e..47090c53692f 100644 --- a/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa_simple.c +++ b/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa_simple.c @@ -145,9 +145,9 @@ static int add_params(struct kbase_ipa_model *model) if (err) goto end; - err = kbase_ipa_model_add_param_s32(model, "dynamic-coefficient", - &model_data->dynamic_coefficient, - 1, true); + err = of_property_read_u32_array(model->kbdev->dev->of_node, + "dynamic-power-coefficient", + &model_data->dynamic_coefficient, 1); if (err) goto end; From f45da24df9e1160152cbb2a0a6edd73845fdb95b Mon Sep 17 00:00:00 2001 From: Elaine Zhang Date: Mon, 29 Jan 2024 15:39:18 +0800 Subject: [PATCH 07/22] rtc: rockchip: support rtc suspend bypass If rtc 32k used as time for deep sleep, the rtc suspend func bypass do nothing. Signed-off-by: Elaine Zhang Change-Id: I020182bd06ee68da387a141eb55a86f6bb3a0c5b --- drivers/rtc/rtc-rockchip.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-rockchip.c b/drivers/rtc/rtc-rockchip.c index 7175ffef7c7c..237b353c7883 100644 --- a/drivers/rtc/rtc-rockchip.c +++ b/drivers/rtc/rtc-rockchip.c @@ -86,6 +86,7 @@ #define RTC_VREF_INIT 0x40 +#define CLK_32K_ENABLE BIT(5) #define D2A_POR_REG_SEL1 BIT(4) #define D2A_POR_REG_SEL0 BIT(1) @@ -124,6 +125,7 @@ struct rockchip_rtc { unsigned int flag; unsigned int mode; struct delayed_work trim_work; + bool suspend_bypass; }; static unsigned int rockchip_rtc_write(struct regmap *map, @@ -590,6 +592,9 @@ static int rockchip_rtc_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct rockchip_rtc *rtc = dev_get_drvdata(&pdev->dev); + if (rtc->suspend_bypass) + return 0; + if (device_may_wakeup(dev)) enable_irq_wake(rtc->irq); @@ -619,6 +624,9 @@ static int rockchip_rtc_resume(struct device *dev) struct rockchip_rtc *rtc = dev_get_drvdata(&pdev->dev); int ret; + if (rtc->suspend_bypass) + return 0; + if (device_may_wakeup(dev)) disable_irq_wake(rtc->irq); @@ -717,8 +725,10 @@ static int rockchip_rtc_probe(struct platform_device *pdev) "Failed to add clk disable action."); ret = rockchip_rtc_update_bits(rtc->regmap, RTC_VPTAT_TRIM, - D2A_POR_REG_SEL1, - D2A_POR_REG_SEL1); + D2A_POR_REG_SEL1 | + CLK_32K_ENABLE, + D2A_POR_REG_SEL1 | + CLK_32K_ENABLE); if (ret) return dev_err_probe(&pdev->dev, ret, "Failed to write RTC_VPTAT_TRIM\n"); @@ -788,6 +798,10 @@ static int rockchip_rtc_probe(struct platform_device *pdev) "Failed to request alarm IRQ %d\n", rtc->irq); + /* If rtc 32k used as time for deep sleep, the rtc suspend func bypass do nothing. */ + rtc->suspend_bypass = device_property_read_bool(&pdev->dev, + "rockchip,rtc-suspend-bypass"); + INIT_DELAYED_WORK(&rtc->trim_work, rockchip_rtc_compensation_delay_work); rockchip_rtc_trim_start(rtc); From 0196404b4f0b31e581607908bedee5f5b2dde548 Mon Sep 17 00:00:00 2001 From: XiaoDong Huang Date: Mon, 29 Jan 2024 18:28:21 +0800 Subject: [PATCH 08/22] soc: rockchip: pm_config: initialize sleep_config to 0 Signed-off-by: XiaoDong Huang Change-Id: I95697411f67492f1e83f07010248f4cab487d6ed --- drivers/soc/rockchip/rockchip_pm_config.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soc/rockchip/rockchip_pm_config.c b/drivers/soc/rockchip/rockchip_pm_config.c index c319b790ee24..6236c3df02a2 100644 --- a/drivers/soc/rockchip/rockchip_pm_config.c +++ b/drivers/soc/rockchip/rockchip_pm_config.c @@ -431,7 +431,8 @@ static int pm_config_probe(struct platform_device *pdev) sleep_config = devm_kmalloc_array(&pdev->dev, RK_PM_STATE_MAX, - sizeof(*sleep_config), GFP_KERNEL); + sizeof(*sleep_config), + GFP_KERNEL | __GFP_ZERO); if (!sleep_config) return -ENOMEM; From 21f57eaf2d22b6f34f2a0a49ab2b7db390e7db4b Mon Sep 17 00:00:00 2001 From: XiaoDong Huang Date: Tue, 16 Jan 2024 18:08:28 +0800 Subject: [PATCH 09/22] ARM: rockchip: rv1106_pm: improve the accuracy of recovering hptimer Signed-off-by: XiaoDong Huang Change-Id: I5ccc5f5fba0f7696fc2c0563ed715b78b5adf451 --- arch/arm/mach-rockchip/rockchip_hptimer.c | 17 +++++++----- arch/arm/mach-rockchip/rockchip_hptimer.h | 4 +-- arch/arm/mach-rockchip/rv1106_pm.c | 33 ++++++++++++++++------- arch/arm/mach-rockchip/rv1106_pm.h | 3 ++- 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/arch/arm/mach-rockchip/rockchip_hptimer.c b/arch/arm/mach-rockchip/rockchip_hptimer.c index 7cf7e9e46d31..5ca137e5b7bd 100644 --- a/arch/arm/mach-rockchip/rockchip_hptimer.c +++ b/arch/arm/mach-rockchip/rockchip_hptimer.c @@ -92,18 +92,23 @@ static int rk_hptimer_wait_begin_end_valid(void __iomem *base, u64 wait_us) } } -static u64 rk_hptimer_get_soft_adjust_delt_cnt(void __iomem *base) +static u64 rk_hptimer_get_soft_adjust_delt_cnt(void __iomem *base, u32 hf, u32 lf) { u64 begin, end, delt; + u32 tmp; if (rk_hptimer_wait_begin_end_valid(base, HPTIMER_WAIT_MAX_US)) return 0; + /* (T32_24END - T24_32BEGIN + 2) * (T24 - T32) / T32 + 2.5 * T24/T32 + 2 */ begin = (u64)readl_relaxed(base + TIMER_HP_T24_32BEGIN0) | (u64)readl_relaxed(base + TIMER_HP_T24_32BEGIN1) << 32; end = (u64)readl_relaxed(base + TIMER_HP_T32_24END0) | (u64)readl_relaxed(base + TIMER_HP_T32_24END1) << 32; - delt = (end - begin) * T24M_GCD / T32K_GCD; + delt = (end - begin + 2) * (hf - lf); + delt = div_u64(delt, lf); + tmp = (2 * hf + hf / 2) / lf; + delt = delt + tmp + 2; writel_relaxed(0x3, base + TIMER_HP_BEGIN_END_VALID); @@ -167,18 +172,18 @@ int rk_hptimer_wait_mode(void __iomem *base, enum rk_hptimer_mode_t mode) return 0; } -void rk_hptimer_do_soft_adjust(void __iomem *base) +void rk_hptimer_do_soft_adjust(void __iomem *base, u32 hf, u32 lf) { - u64 delt = rk_hptimer_get_soft_adjust_delt_cnt(base); + u64 delt = rk_hptimer_get_soft_adjust_delt_cnt(base, hf, lf); rk_hptimer_soft_adjust_req(base, delt); rk_hptimer_wait_mode(base, RK_HPTIMER_SOFT_ADJUST_MODE); } -void rk_hptimer_do_soft_adjust_no_wait(void __iomem *base) +void rk_hptimer_do_soft_adjust_no_wait(void __iomem *base, u32 hf, u32 lf) { - u64 delt = rk_hptimer_get_soft_adjust_delt_cnt(base); + u64 delt = rk_hptimer_get_soft_adjust_delt_cnt(base, hf, lf); rk_hptimer_soft_adjust_req(base, delt); } diff --git a/arch/arm/mach-rockchip/rockchip_hptimer.h b/arch/arm/mach-rockchip/rockchip_hptimer.h index 4cf6399ba093..eba644d80b2a 100644 --- a/arch/arm/mach-rockchip/rockchip_hptimer.h +++ b/arch/arm/mach-rockchip/rockchip_hptimer.h @@ -16,7 +16,7 @@ int rk_hptimer_is_enabled(void __iomem *base); int rk_hptimer_get_mode(void __iomem *base); u64 rk_hptimer_get_count(void __iomem *base); int rk_hptimer_wait_mode(void __iomem *base, enum rk_hptimer_mode_t mode); -void rk_hptimer_do_soft_adjust(void __iomem *base); -void rk_hptimer_do_soft_adjust_no_wait(void __iomem *base); +void rk_hptimer_do_soft_adjust(void __iomem *base, u32 hf, u32 lf); +void rk_hptimer_do_soft_adjust_no_wait(void __iomem *base, u32 hf, u32 lf); void rk_hptimer_mode_init(void __iomem *base, enum rk_hptimer_mode_t mode); #endif diff --git a/arch/arm/mach-rockchip/rv1106_pm.c b/arch/arm/mach-rockchip/rv1106_pm.c index fbf7eae02596..1b352ffa2a1a 100644 --- a/arch/arm/mach-rockchip/rv1106_pm.c +++ b/arch/arm/mach-rockchip/rv1106_pm.c @@ -55,6 +55,7 @@ struct rv1106_sleep_ddr_data { u32 gpio0a_iomux_l, gpio0a_iomux_h, gpio0a0_pull; u32 gpio0_ddr_l, gpio0_ddr_h; u32 pmu_wkup_int_st, gpio0_int_st; + u32 sleep_clk_freq_hz; }; static struct rv1106_sleep_ddr_data ddr_data; @@ -604,9 +605,9 @@ static void clock_resume(void) static void pvtm_32k_config(int flag) { - int value; - int pvtm_freq_khz, pvtm_div; - int sleep_clk_freq_khz; + u64 value, pvtm_freq_hz; + int pvtm_div; + u32 pvtm_div_freq_hz; ddr_data.pmucru_sel_con7 = readl_relaxed(pmucru_base + RV1106_PMUCRU_CLKSEL_CON(7)); @@ -623,7 +624,7 @@ static void pvtm_32k_config(int flag) pmupvtm_base + RV1106_PVTM_CON(2)); writel_relaxed(RV1106_PVTM_CALC_CNT, pmupvtm_base + RV1106_PVTM_CON(1)); - writel_relaxed(BITS_WITH_WMASK(0, 0x3, PVTM_START), + writel_relaxed(BITS_WITH_WMASK(0, 0x1, PVTM_START), pmupvtm_base + RV1106_PVTM_CON(0)); dsb(); @@ -648,8 +649,11 @@ static void pvtm_32k_config(int flag) ; value = (readl_relaxed(pmupvtm_base + RV1106_PVTM_STATUS(1))); - pvtm_freq_khz = (value * 24000 + RV1106_PVTM_CALC_CNT / 2) / RV1106_PVTM_CALC_CNT; - pvtm_div = (pvtm_freq_khz + 16) / 32 - 1; + pvtm_freq_hz = (value * 24000000 + RV1106_PVTM_CALC_CNT / 2); + pvtm_freq_hz = div_u64(pvtm_freq_hz, RV1106_PVTM_CALC_CNT); + + pvtm_div = ((u32)pvtm_freq_hz + RV1106_PVTM_TARGET_FREQ / 2) / + RV1106_PVTM_TARGET_FREQ - 1; if (pvtm_div > 0xfff) pvtm_div = 0xfff; @@ -660,12 +664,19 @@ static void pvtm_32k_config(int flag) writel_relaxed(BITS_WITH_WMASK(0x2, 0x3, 0), pmucru_base + RV1106_PMUCRU_CLKSEL_CON(7)); - sleep_clk_freq_khz = pvtm_freq_khz / (pvtm_div + 1); + pvtm_div_freq_hz = (u32)pvtm_freq_hz / (pvtm_div + 1); + ddr_data.sleep_clk_freq_hz = pvtm_div_freq_hz; - rkpm_printstr("pvtm real_freq (khz):"); - rkpm_printhex(sleep_clk_freq_khz); + rkpm_printstr("pvtm freq (hz):"); + rkpm_printdec(pvtm_freq_hz); + rkpm_printch('-'); + rkpm_printdec(pvtm_div_freq_hz); rkpm_printch('\n'); } + + rkpm_printstr("sleep freq (hz):"); + rkpm_printdec(ddr_data.sleep_clk_freq_hz); + rkpm_printch('\n'); } static void pvtm_32k_config_restore(void) @@ -674,7 +685,9 @@ static void pvtm_32k_config_restore(void) pmucru_base + RV1106_PMUCRU_CLKSEL_CON(7)); if (rk_hptimer_get_mode(hptimer_base) == RK_HPTIMER_SOFT_ADJUST_MODE) - rk_hptimer_do_soft_adjust_no_wait(hptimer_base); + rk_hptimer_do_soft_adjust_no_wait(hptimer_base, + 24000000, + ddr_data.sleep_clk_freq_hz); } static void ddr_sleep_config(void) diff --git a/arch/arm/mach-rockchip/rv1106_pm.h b/arch/arm/mach-rockchip/rv1106_pm.h index cd07adc1acc6..8144344a1819 100644 --- a/arch/arm/mach-rockchip/rv1106_pm.h +++ b/arch/arm/mach-rockchip/rv1106_pm.h @@ -129,7 +129,8 @@ #define RV1106_PVTM_INTSTS 0x74 #define RV1106_PVTM_STATUS(i) (0x80 + (i) * 4) -#define RV1106_PVTM_CALC_CNT 0x200 +#define RV1106_PVTM_CALC_CNT 24000 +#define RV1106_PVTM_TARGET_FREQ 32768 /* gpio */ #define RV1106_GPIO_SWPORT_DR_L 0x0000 From 6685f6f7f9e13cd70a7bc3197e656298b0ef7f96 Mon Sep 17 00:00:00 2001 From: XiaoDong Huang Date: Wed, 17 Jan 2024 14:21:29 +0800 Subject: [PATCH 10/22] ARM: rockchip: rv1106: sleep: use rtc as 32k source Signed-off-by: XiaoDong Huang Change-Id: I32db2b499f4eabfee1acb2b8a7feb0d85e10456b --- arch/arm/mach-rockchip/rv1106_pm.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-rockchip/rv1106_pm.c b/arch/arm/mach-rockchip/rv1106_pm.c index 1b352ffa2a1a..1d7b30a3ad08 100644 --- a/arch/arm/mach-rockchip/rv1106_pm.c +++ b/arch/arm/mach-rockchip/rv1106_pm.c @@ -603,7 +603,7 @@ static void clock_resume(void) vocru_base + RV1106_VOCRU_GATE_CON(i)); } -static void pvtm_32k_config(int flag) +static void pvtm_32k_config(void) { u64 value, pvtm_freq_hz; int pvtm_div; @@ -612,13 +612,12 @@ static void pvtm_32k_config(int flag) ddr_data.pmucru_sel_con7 = readl_relaxed(pmucru_base + RV1106_PMUCRU_CLKSEL_CON(7)); - if (flag) { - writel_relaxed(BITS_WITH_WMASK(0x1, 0x1, 6), vigrf_base + 0x0); - writel_relaxed(BITS_WITH_WMASK(0x4, 0xf, 0), ioc_base[0] + 0); - writel_relaxed(BITS_WITH_WMASK(0x1, 0x1, 15), + if (slp_cfg->mode_config & RKPM_SLP_32K_EXT) { + writel_relaxed(BITS_WITH_WMASK(0x3, 0x3, 14), pmugrf_base + RV1106_PMUGRF_SOC_CON(1)); writel_relaxed(BITS_WITH_WMASK(0x1, 0x3, 0), pmucru_base + RV1106_PMUCRU_CLKSEL_CON(7)); + ddr_data.sleep_clk_freq_hz = 32768; } else { writel_relaxed(BITS_WITH_WMASK(0, 0x3, 0), pmupvtm_base + RV1106_PVTM_CON(2)); @@ -887,7 +886,7 @@ static void soc_sleep_config(void) rkpm_printch('a'); - pvtm_32k_config(0); + pvtm_32k_config(); rkpm_printch('b'); ddr_sleep_config(); From 5f64a92db216ec88730f515e96859be9bfc4c4b2 Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Mon, 29 Jan 2024 06:48:22 +0000 Subject: [PATCH 11/22] video: rockchip: vehicle: fix bugs of cif get csi_fmt_val Signed-off-by: Jianwei Fan Change-Id: I802fc6a48a2a2177c919f1a09223136e26834c8c --- drivers/video/rockchip/vehicle/vehicle_cif.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/video/rockchip/vehicle/vehicle_cif.c b/drivers/video/rockchip/vehicle/vehicle_cif.c index 96c2bfb95785..ca9f092075fd 100644 --- a/drivers/video/rockchip/vehicle/vehicle_cif.c +++ b/drivers/video/rockchip/vehicle/vehicle_cif.c @@ -2747,9 +2747,8 @@ static int vehicle_cif_csi_channel_init(struct vehicle_cif *cif, return -EINVAL; } } - // channel->fmt_val = fmt->csi_fmt_val; - /* set cif input format yuv422*/ - channel->fmt_val = CSI_WRDDR_TYPE_YUV422; + + channel->fmt_val = fmt->csi_fmt_val; VEHICLE_INFO("%s, LINE=%d, channel->fmt_val = 0x%x, fmt->csi_fmt_val= 0x%x", __func__, __LINE__, channel->fmt_val, fmt->csi_fmt_val); /* @@ -3225,6 +3224,7 @@ static int vehicle_cif_csi2_s_stream(struct vehicle_cif *cif, } else { val |= infmt->csi_yuv_order; } + rkcif_write_reg(cif, get_reg_index_of_id_ctrl0(channel->id), val); cif->state = RKCIF_STATE_STREAMING; } else { @@ -3266,7 +3266,7 @@ static int vehicle_cif_csi2_s_stream_v1(struct vehicle_cif *cif, channel = &cif->channels[0]; if (enable) { - val = CSI_ENABLE_CAPTURE | CSI_DMA_ENABLE | channel->fmt_val | + val = CSI_ENABLE_CAPTURE | CSI_DMA_ENABLE | channel->cmd_mode_en << 26 | CSI_ENABLE_CROP_V1 | channel->id << 8 | channel->data_type << 10; @@ -3275,7 +3275,7 @@ static int vehicle_cif_csi2_s_stream_v1(struct vehicle_cif *cif, VEHICLE_INFO("Input fmt is invalid, use default!\n"); val |= CSI_YUV_INPUT_ORDER_UYVY; } else { - val |= infmt->csi_yuv_order; + val |= infmt->csi_yuv_order | infmt->csi_fmt_val; } if (cfg->output_format == CIF_OUTPUT_FORMAT_420) { From e4bcfb45de6a29592e170beab4a91af726698056 Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Thu, 25 Jan 2024 02:57:10 +0000 Subject: [PATCH 12/22] arm64: dts: rockchip: rk3562-evb: fix image reverse mipi_csi node Signed-off-by: Jianwei Fan Change-Id: I5364d50a99b4388b34631f6a3c43565c6878522c --- arch/arm64/boot/dts/rockchip/rk3562-evb2-image-reverse.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3562-evb2-image-reverse.dtsi b/arch/arm64/boot/dts/rockchip/rk3562-evb2-image-reverse.dtsi index 561128b91645..470e2d53cbb6 100644 --- a/arch/arm64/boot/dts/rockchip/rk3562-evb2-image-reverse.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3562-evb2-image-reverse.dtsi @@ -102,7 +102,7 @@ "srst_csihost_p"; csihost-idx = <0>; rockchip,csi2-dphy = <&csi2_dphy0_hw>; - rockchip,csi2 = <&mipi0_csi2>; + rockchip,csi2 = <&mipi0_csi2_hw>; }; csi2_dphy3 { status = "disabled"; @@ -118,7 +118,7 @@ "srst_csihost_p"; csihost-idx = <2>; rockchip,csi2-dphy = <&csi2_dphy1_hw>; - rockchip,csi2 = <&mipi2_csi2>; + rockchip,csi2 = <&mipi2_csi2_hw>; }; }; From 192370b9558ce0b873abce5cd967be5f087dc068 Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Thu, 25 Jan 2024 02:58:32 +0000 Subject: [PATCH 13/22] arm64: dts: rockchip: rk3588-vehicle-evb: fix image reverse mipi_csi node Signed-off-by: Jianwei Fan Change-Id: Ie14481f72268d0e91446cb489003503713c28e14 --- .../dts/rockchip/rk3588-vehicle-evb-image-reverse.dtsi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-image-reverse.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-image-reverse.dtsi index 9bcce71288f6..fa4255a273c8 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-image-reverse.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-image-reverse.dtsi @@ -90,7 +90,7 @@ reset-names = "srst_csihost_p", "srst_csihost_vicap"; csihost-idx = <0>; - rockchip,csi2 = <&mipi0_csi2>; + rockchip,csi2 = <&mipi0_csi2_hw>; phys = <&mipi_dcphy0>; phy-names = "dcphy"; }; @@ -109,7 +109,7 @@ reset-names = "srst_csihost_p", "srst_csihost_vicap"; csihost-idx = <1>; - rockchip,csi2 = <&mipi1_csi2>; + rockchip,csi2 = <&mipi1_csi2_hw>; phys = <&mipi_dcphy1>; phy-names = "dcphy"; }; @@ -132,7 +132,7 @@ csihost-idx = <2>; rockchip,dphy-grf = <&mipidphy0_grf>; rockchip,csi2-dphy = <&csi2_dphy0_hw>; - rockchip,csi2 = <&mipi2_csi2>; + rockchip,csi2 = <&mipi2_csi2_hw>; }; /* only rk3588 */ csi2_dphy3 { @@ -154,7 +154,7 @@ csihost-idx = <4>; rockchip,dphy-grf = <&mipidphy1_grf>; rockchip,csi2-dphy = <&csi2_dphy1_hw>; - rockchip,csi2 = <&mipi4_csi2>; + rockchip,csi2 = <&mipi4_csi2_hw>; }; rkcif_dvp { status = "disabled"; From 3bc6e98dcc32da7e166aac7fafe6e938533c2b7a Mon Sep 17 00:00:00 2001 From: Sandy Huang Date: Tue, 30 Jan 2024 14:25:33 +0800 Subject: [PATCH 14/22] drm/rockchip: vop2: clearly point out the plane unsupported format modifier Signed-off-by: Sandy Huang Change-Id: I2d247f6ae1f63225629dfc6641bce5525cfb8b20 --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 2440c2261ac6..3307a217a317 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -2275,7 +2275,7 @@ static bool rockchip_vop2_mod_supported(struct drm_plane *plane, u32 format, u64 return true; if (!rockchip_afbc(plane, modifier) && !rockchip_tiled(plane, modifier)) { - DRM_ERROR("Unsupported format modifier 0x%llx\n", modifier); + DRM_ERROR("%s unsupported format modifier 0x%llx\n", plane->name, modifier); return false; } From 17a33dcac274b1fb5f8e53ade82b165e3262cd5a Mon Sep 17 00:00:00 2001 From: Jun Zeng Date: Fri, 29 Dec 2023 17:21:21 +0800 Subject: [PATCH 15/22] arm64: dts: rockchip: rk3588-vehicle-evb: add spi codec reset gpio Change-Id: Ib6d9fbebe978a3393fa42d1d4d441fa1bbb9f599 Signed-off-by: Jun Zeng --- arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v20.dtsi | 3 ++- arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v21.dtsi | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v20.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v20.dtsi index d5621243346a..86b668a8cc28 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v20.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v20.dtsi @@ -274,7 +274,7 @@ rk3308 { rk3308_reset: rk3308-reset { - rockchip,pins = <4 RK_PC6 RK_FUNC_GPIO &pcfg_pull_up>; + rockchip,pins = <4 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>; }; }; }; @@ -331,6 +331,7 @@ #sound-dai-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&rk3308_reset>; + reset-gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_HIGH>; status = "okay"; }; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v21.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v21.dtsi index 3624b81976c2..7413020b1342 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v21.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-vehicle-evb-v21.dtsi @@ -287,7 +287,7 @@ rk3308 { rk3308_reset: rk3308-reset { - rockchip,pins = <4 RK_PC6 RK_FUNC_GPIO &pcfg_pull_up>; + rockchip,pins = <4 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>; }; }; }; @@ -344,6 +344,7 @@ #sound-dai-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&rk3308_reset>; + reset-gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_HIGH>; status = "okay"; }; }; From 91ce62dbce00999e40a46181b16a1f085e26f510 Mon Sep 17 00:00:00 2001 From: Finley Xiao Date: Fri, 19 Jan 2024 15:47:14 +0800 Subject: [PATCH 16/22] soc: rockchip: power-domain: Update gate mask for rk3562 The vepu biu clk depends on vi biu clk, add vi gate mask for pd vepu. The rga biu clk depends on vo biu clk, add vo gate mask for pd rga. Signed-off-by: Finley Xiao Change-Id: Ieaa62a053369e570eb0a11d132fa472c6bd246f4 --- drivers/soc/rockchip/pm_domains.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c index 6e98b2a9ef81..5a60ae717ad4 100644 --- a/drivers/soc/rockchip/pm_domains.c +++ b/drivers/soc/rockchip/pm_domains.c @@ -186,7 +186,7 @@ static void rockchip_pmu_unlock(struct rockchip_pm_domain *pd) .keepon_startup = keepon, \ } -#define DOMAIN_M_C_SD(_name, pwr, status, req, idle, ack, clk, mem, wakeup, keepon) \ +#define DOMAIN_M_G_SD(_name, pwr, status, req, idle, ack, g_mask, mem, wakeup, keepon) \ { \ .name = _name, \ .pwr_w_mask = (pwr) << 16, \ @@ -196,8 +196,8 @@ static void rockchip_pmu_unlock(struct rockchip_pm_domain *pd) .req_mask = (req), \ .idle_mask = (idle), \ .ack_mask = (ack), \ - .clk_ungate_mask = (clk), \ - .clk_ungate_w_mask = (clk) << 16, \ + .clk_ungate_mask = (g_mask), \ + .clk_ungate_w_mask = (g_mask) << 16, \ .mem_num = (mem), \ .active_wakeup = wakeup, \ .keepon_startup = keepon, \ @@ -287,11 +287,11 @@ static void rockchip_pmu_unlock(struct rockchip_pm_domain *pd) #define DOMAIN_RK3528(pwr, req, always, wakeup) \ DOMAIN_M_A(pwr, pwr, req, req, req, always, wakeup, false) -#define DOMAIN_RK3562(name, pwr, req, mem, wakeup) \ - DOMAIN_M_C_SD(name, pwr, pwr, req, req, req, req, mem, wakeup, false) +#define DOMAIN_RK3562(name, pwr, req, g_mask, mem, wakeup) \ + DOMAIN_M_G_SD(name, pwr, pwr, req, req, req, g_mask, mem, wakeup, false) -#define DOMAIN_RK3562_PROTECT(name, pwr, req, mem, wakeup) \ - DOMAIN_M_C_SD(name, pwr, pwr, req, req, req, req, mem, wakeup, true) +#define DOMAIN_RK3562_PROTECT(name, pwr, req, g_mask, mem, wakeup) \ + DOMAIN_M_G_SD(name, pwr, pwr, req, req, req, g_mask, mem, wakeup, true) #define DOMAIN_RK3568(name, pwr, req, wakeup) \ DOMAIN_M(name, pwr, pwr, req, req, req, wakeup, false) @@ -1783,14 +1783,15 @@ static const struct rockchip_domain_info rk3528_pm_domains[] = { }; static const struct rockchip_domain_info rk3562_pm_domains[] = { - [RK3562_PD_GPU] = DOMAIN_RK3562("gpu", BIT(0), BIT(1), 0, false), - [RK3562_PD_NPU] = DOMAIN_RK3562("npu", BIT(1), BIT(2), 0, false), - [RK3562_PD_VDPU] = DOMAIN_RK3562("vdpu", BIT(2), BIT(6), 0, false), - [RK3562_PD_VEPU] = DOMAIN_RK3562("vepu", BIT(3), BIT(7), 0, false), - [RK3562_PD_RGA] = DOMAIN_RK3562("rga", BIT(4), BIT(5), 0, false), - [RK3562_PD_VI] = DOMAIN_RK3562("vi", BIT(5), BIT(3), 0, false), - [RK3562_PD_VO] = DOMAIN_RK3562_PROTECT("vo", BIT(6), BIT(4), 16, false), - [RK3562_PD_PHP] = DOMAIN_RK3562("php", BIT(7), BIT(8), 0, false), + /* name pwr req g_mask mem wakeup */ + [RK3562_PD_GPU] = DOMAIN_RK3562("gpu", BIT(0), BIT(1), BIT(1), 0, false), + [RK3562_PD_NPU] = DOMAIN_RK3562("npu", BIT(1), BIT(2), BIT(2), 0, false), + [RK3562_PD_VDPU] = DOMAIN_RK3562("vdpu", BIT(2), BIT(6), BIT(6), 0, false), + [RK3562_PD_VEPU] = DOMAIN_RK3562("vepu", BIT(3), BIT(7), BIT(7) | BIT(3), 0, false), + [RK3562_PD_RGA] = DOMAIN_RK3562("rga", BIT(4), BIT(5), BIT(5) | BIT(4), 0, false), + [RK3562_PD_VI] = DOMAIN_RK3562("vi", BIT(5), BIT(3), BIT(3), 0, false), + [RK3562_PD_VO] = DOMAIN_RK3562_PROTECT("vo", BIT(6), BIT(4), BIT(4), 16, false), + [RK3562_PD_PHP] = DOMAIN_RK3562("php", BIT(7), BIT(8), BIT(8), 0, false), }; static const struct rockchip_domain_info rk3568_pm_domains[] = { From 1ebaed6daf591978a341b8eeff4788b3d6dc6230 Mon Sep 17 00:00:00 2001 From: Sandy Huang Date: Wed, 3 Jan 2024 17:36:30 +0800 Subject: [PATCH 17/22] drm/rockchip: rgb: register sub dev at rockchip rgb driver At rgb to other display output type product, the connector maybe register at third party drivers, the sub dev register often be forgot, so we add sub dev register at rockchip rgb driver. Signed-off-by: Sandy Huang Change-Id: I3baa051712ac30b63dffa9658df470c12bcb91dc --- drivers/gpu/drm/rockchip/rockchip_rgb.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.c b/drivers/gpu/drm/rockchip/rockchip_rgb.c index a7f2057d349f..1b17e54ed606 100644 --- a/drivers/gpu/drm/rockchip/rockchip_rgb.c +++ b/drivers/gpu/drm/rockchip/rockchip_rgb.c @@ -819,7 +819,8 @@ static int rockchip_rgb_bind(struct device *dev, struct device *master, struct rockchip_rgb *rgb = dev_get_drvdata(dev); struct drm_device *drm_dev = data; struct drm_encoder *encoder = &rgb->encoder; - struct drm_connector *connector; + struct drm_connector *connector = NULL; + struct rockchip_drm_private *private = drm_dev->dev_private; int ret; if (rgb->np_mcu_panel) { @@ -871,8 +872,6 @@ static int rockchip_rgb_bind(struct device *dev, struct device *master, drm_encoder_helper_add(encoder, &rockchip_rgb_encoder_helper_funcs); if (rgb->panel) { - struct rockchip_drm_private *private = drm_dev->dev_private; - connector = &rgb->connector; connector->interlace_allowed = true; ret = drm_connector_init(drm_dev, connector, @@ -894,12 +893,9 @@ static int rockchip_rgb_bind(struct device *dev, struct device *master, "failed to attach encoder: %d\n", ret); goto err_free_connector; } - rgb->sub_dev.connector = &rgb->connector; - rgb->sub_dev.of_node = rgb->dev->of_node; - rgb->sub_dev.loader_protect = rockchip_rgb_encoder_loader_protect; - drm_object_attach_property(&connector->base, private->connector_id_prop, 0); - rockchip_drm_register_sub_dev(&rgb->sub_dev); } else { + struct list_head *connector_list; + rgb->bridge->encoder = encoder; ret = drm_bridge_attach(encoder, rgb->bridge, NULL, 0); if (ret) { @@ -907,6 +903,19 @@ static int rockchip_rgb_bind(struct device *dev, struct device *master, "failed to attach bridge: %d\n", ret); goto err_free_encoder; } + connector_list = &rgb->bridge->dev->mode_config.connector_list; + + list_for_each_entry(connector, connector_list, head) + if (drm_connector_has_possible_encoder(connector, &rgb->encoder)) + break; + } + + if (connector) { + rgb->sub_dev.connector = connector; + rgb->sub_dev.of_node = rgb->dev->of_node; + rgb->sub_dev.loader_protect = rockchip_rgb_encoder_loader_protect; + drm_object_attach_property(&connector->base, private->connector_id_prop, rgb->id); + rockchip_drm_register_sub_dev(&rgb->sub_dev); } return 0; From 8ed148210d8d1d793f79093dbbbdcc87c2479179 Mon Sep 17 00:00:00 2001 From: Sandy Huang Date: Wed, 3 Jan 2024 17:58:57 +0800 Subject: [PATCH 18/22] drm/rockchip: lvds: register sub dev at rockchip lvds driver At lvds to other display output type product, the connector maybe register at third party drivers, the sub dev register often be forgot, so we add sub dev register at rockchip lvds driver. Signed-off-by: Sandy Huang Change-Id: If075041df8ddb1b269c903d092a6263160eff1db --- drivers/gpu/drm/rockchip/rockchip_lvds.c | 28 ++++++++++++++++-------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c index bc21bf48a329..5b74ddf67948 100644 --- a/drivers/gpu/drm/rockchip/rockchip_lvds.c +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c @@ -494,7 +494,8 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, struct rockchip_lvds *lvds = dev_get_drvdata(dev); struct drm_device *drm_dev = data; struct drm_encoder *encoder = &lvds->encoder; - struct drm_connector *connector = &lvds->connector; + struct drm_connector *connector = NULL; + struct rockchip_drm_private *private = drm_dev->dev_private; int ret; /* @@ -522,8 +523,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, drm_encoder_helper_add(encoder, &rockchip_lvds_encoder_helper_funcs); if (lvds->panel) { - struct rockchip_drm_private *private = drm_dev->dev_private; - + connector = &lvds->connector; ret = drm_connector_init(drm_dev, connector, &rockchip_lvds_connector_funcs, DRM_MODE_CONNECTOR_LVDS); @@ -551,19 +551,29 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, "failed to attach encoder: %d\n", ret); goto err_free_connector; } - - lvds->sub_dev.connector = &lvds->connector; - lvds->sub_dev.of_node = lvds->dev->of_node; - lvds->sub_dev.loader_protect = rockchip_lvds_encoder_loader_protect; - rockchip_drm_register_sub_dev(&lvds->sub_dev); - drm_object_attach_property(&connector->base, private->connector_id_prop, 0); } else { + struct list_head *connector_list; + ret = drm_bridge_attach(encoder, lvds->bridge, NULL, 0); if (ret) { DRM_DEV_ERROR(lvds->dev, "failed to attach bridge: %d\n", ret); goto err_free_encoder; } + connector_list = &lvds->bridge->dev->mode_config.connector_list; + + list_for_each_entry(connector, connector_list, head) + if (drm_connector_has_possible_encoder(connector, &lvds->encoder)) + break; + + } + + if (connector) { + lvds->sub_dev.connector = connector; + lvds->sub_dev.of_node = lvds->dev->of_node; + lvds->sub_dev.loader_protect = rockchip_lvds_encoder_loader_protect; + drm_object_attach_property(&connector->base, private->connector_id_prop, lvds->id); + rockchip_drm_register_sub_dev(&lvds->sub_dev); } return 0; From 28a7162d4726172747c41b2ec3b4c1b56434c41a Mon Sep 17 00:00:00 2001 From: Lan Honglin Date: Fri, 19 Jan 2024 14:24:09 +0800 Subject: [PATCH 19/22] media: i2c: add sc831ai sensor driver Change-Id: Ia2ceeb8fcc7f15f9b610583714e3dc03b5e3c8ce Signed-off-by: Lan Honglin --- drivers/media/i2c/Kconfig | 10 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/sc831ai.c | 1863 +++++++++++++++++++++++++++++++++++ 3 files changed, 1874 insertions(+) create mode 100644 drivers/media/i2c/sc831ai.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 0d2ffef356e3..06c22f463328 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -2291,6 +2291,16 @@ config VIDEO_SC830AI This is a Video4Linux2 sensor driver for the SmartSens SC830AI camera. +config VIDEO_SC831AI + tristate "SmartSens SC831AI sensor support" + depends on I2C && VIDEO_V4L2 + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the SmartSens + SC831AI camera. + config VIDEO_SC850SL tristate "SmartSens SC850SL sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 01a38d222d4c..23975ba24d74 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -159,6 +159,7 @@ obj-$(CONFIG_VIDEO_SC501AI) += sc501ai.o obj-$(CONFIG_VIDEO_SC530AI) += sc530ai.o obj-$(CONFIG_VIDEO_SC5336) += sc5336.o obj-$(CONFIG_VIDEO_SC830AI) += sc830ai.o +obj-$(CONFIG_VIDEO_SC831AI) += sc831ai.o obj-$(CONFIG_VIDEO_SC850SL) += sc850sl.o obj-$(CONFIG_VIDEO_SENSOR_ADAPTER) += sensor_adapter.o obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o diff --git a/drivers/media/i2c/sc831ai.c b/drivers/media/i2c/sc831ai.c new file mode 100644 index 000000000000..8d472ead53e1 --- /dev/null +++ b/drivers/media/i2c/sc831ai.c @@ -0,0 +1,1863 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * sc831ai driver + * + * Copyright (C) 2024 Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X01 first version + */ + +//#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../platform/rockchip/isp/rkisp_tb_helper.h" +#include "cam-tb-setup.h" +#include "cam-sleep-wakeup.h" + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x01) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +#define SC831AI_2LANES 2 + +#define MIPI_FREQ_396M 396000000 // 792Mbps + +#define SC831AI_MAX_PIXEL_RATE (MIPI_FREQ_396M / 10 * 2 * SC831AI_2LANES) +#define OF_CAMERA_HDR_MODE "rockchip,camera-hdr-mode" + +#define SC831AI_XVCLK_FREQ 24000000 + +/* TODO: Get the real chip id from reg */ +#define CHIP_ID 0xC170 +#define SC831AI_REG_CHIP_ID 0x3107 + +#define SC831AI_REG_CTRL_MODE 0x0100 +#define SC831AI_MODE_SW_STANDBY 0x0 +#define SC831AI_MODE_STREAMING BIT(0) + +/*expo*/ +#define SC831AI_EXPOSURE_MIN 3 /*okay*/ +#define SC831AI_EXPOSURE_STEP 1 /*okay*/ +#define SC831AI_VTS_MAX 0xffff /*okay*/ + +//long exposure +#define SC831AI_REG_EXP_LONG_H 0x3e00 //[3:0] +#define SC831AI_REG_EXP_LONG_M 0x3e01 //[7:0] +#define SC831AI_REG_EXP_LONG_L 0x3e02 //[7:4] + +//short exposure //for hdr +#define SC831AI_REG_EXP_SF_H 0x3e22 +#define SC831AI_REG_EXP_SF_M 0x3e04 //[7:0] +#define SC831AI_REG_EXP_SF_L 0x3e05 //[7:4] + +#define SC831AI_FETCH_EXP_H(VAL) (((VAL) >> 12) & 0xF) +#define SC831AI_FETCH_EXP_M(VAL) (((VAL) >> 4) & 0xFF) +#define SC831AI_FETCH_EXP_L(VAL) (((VAL) & 0xF) << 4) + +/*gain*/ +//long frame and normal gain reg +#define SC831AI_REG_DGAIN 0x3e06 +#define SC831AI_REG_DGAIN_FINE 0x3e07 +#define SC831AI_REG_AGAIN 0x3e08 +#define SC831AI_REG_GAIN_FINE 0x3e09 + +//short frame gain reg +#define SC831AI_SF_REG_DGAIN 0x3e10 +#define SC831AI_SF_REG_DGAIN_FINE 0x3e11 +#define SC831AI_SF_REG_GAIN_FINE 0x3e12 +#define SC831AI_SF_REG_AGAIN 0x3e13 + +#define SC831AI_GAIN_MIN 64 /* = 1 * 64 */ +#define SC831AI_GAIN_MAX (47351) /* 46.242 * 16 * 64 */ +#define SC831AI_GAIN_STEP 1 +#define SC831AI_GAIN_DEFAULT 64 + +#define SC831AI_REG_VTS 0x320e + +//group hold +#define SC831AI_GROUP_UPDATE_ADDRESS 0x3812 +#define SC831AI_GROUP_UPDATE_START_DATA 0x00 +#define SC831AI_GROUP_UPDATE_LAUNCH 0x30 +#define SC831AI_GROUP_DEALY_CTRL 0x3802 + +#define SC831AI_SOFTWARE_RESET_REG 0x0103 +#define SC831AI_REG_TEST_PATTERN 0x4501 +#define SC831AI_TEST_PATTERN_ENABLE 0x08 + +#define SC831AI_FLIP_REG 0x3221 +#define SC831AI_FLIP_MASK 0x60 +#define SC831AI_MIRROR_MASK 0x06 + +#define REG_NULL 0xFFFF + +#define SC831AI_REG_VALUE_08BIT 1 +#define SC831AI_REG_VALUE_16BIT 2 +#define SC831AI_REG_VALUE_24BIT 3 + +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" + +#define SC831AI_NAME "sc831ai" + +static const char *const sc831ai_supply_names[] = { + "dvdd", // Digital core power + "dovdd", // Digital I/O power + "avdd", // Analog power +}; +#define SC831AI_NUM_SUPPLIES ARRAY_SIZE(sc831ai_supply_names) + +struct regval { + u16 addr; + u8 val; +}; + +struct sc831ai_mode { + u32 bus_fmt; + u32 width; + u32 height; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + u32 mipi_freq_idx; + u32 bpp; + const struct regval *reg_list; + u32 hdr_mode; + u32 vc[PAD_MAX]; +}; + +struct sc831ai { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[SC831AI_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_a_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct mutex mutex; + struct v4l2_fract cur_fps; + bool streaming; + bool power_on; + bool is_thunderboot; + bool is_first_streamoff; + const struct sc831ai_mode *cur_mode; + u32 module_index; + u32 cfg_num; + const char *module_facing; + const char *module_name; + const char *len_name; + u32 cur_vts; + bool has_init_exp; + struct preisp_hdrae_exp_s init_hdrae_exp; + struct cam_sw_info *cam_sw_inf; +}; + +#define to_sc831ai(sd) container_of(sd, struct sc831ai, subdev) + +//Cleaned_0x08_SC831AI_mipi_24Minput_2lane_10bit_792Mbps_3840x2160_15fps.ini +static __maybe_unused const struct regval sc831ai_2lane_linear10bit_3840x2160_regs[] = { + {0x0103, 0x01}, + {0x36e9, 0x80}, + {0x37f9, 0x80}, + {0x3018, 0x3a}, + {0x3019, 0x0c}, + {0x301f, 0x08}, + {0x30b8, 0x44}, + {0x320c, 0x10}, + {0x320d, 0x68}, + {0x320e, 0x08}, + {0x320f, 0xca}, + {0x3301, 0x28}, + {0x3302, 0x10}, + {0x3303, 0x10}, + {0x3304, 0x68}, + {0x3305, 0x00}, + {0x3306, 0x70}, + {0x3307, 0x08}, + {0x3308, 0x14}, + {0x3309, 0x98}, + {0x330a, 0x00}, + {0x330b, 0xf8}, + {0x330c, 0x10}, + {0x330d, 0x08}, + {0x330e, 0x36}, + {0x331e, 0x41}, + {0x331f, 0x71}, + {0x3333, 0x10}, + {0x3334, 0x40}, + {0x334c, 0x10}, + {0x335d, 0x60}, + {0x3364, 0x5e}, + {0x3367, 0x04}, + {0x3390, 0x01}, + {0x3391, 0x03}, + {0x3392, 0x07}, + {0x3393, 0x28}, + {0x3394, 0x4c}, + {0x3395, 0x4c}, + {0x3396, 0x01}, + {0x3397, 0x03}, + {0x3398, 0x07}, + {0x3399, 0x28}, + {0x339a, 0x4c}, + {0x339b, 0x4c}, + {0x339c, 0x4c}, + {0x33ad, 0x24}, + {0x33ae, 0x4e}, + {0x33af, 0x80}, + {0x33b2, 0x50}, + {0x33b3, 0x20}, + {0x33f8, 0x00}, + {0x33f9, 0x88}, + {0x33fa, 0x00}, + {0x33fb, 0x98}, + {0x33fc, 0x43}, + {0x33fd, 0x47}, + {0x349f, 0x03}, + {0x34a6, 0x43}, + {0x34a7, 0x47}, + {0x34a8, 0x20}, + {0x34a9, 0x20}, + {0x34aa, 0x01}, + {0x34ab, 0x10}, + {0x34ac, 0x01}, + {0x34ad, 0x20}, + {0x34f8, 0x43}, + {0x34f9, 0x08}, + {0x3632, 0x64}, + {0x363b, 0x18}, + {0x363c, 0x0e}, + {0x363d, 0x8e}, + {0x363e, 0x6c}, + {0x3654, 0x00}, + {0x3674, 0xb2}, + {0x3675, 0xa4}, + {0x3676, 0x88}, + {0x367c, 0x41}, + {0x367d, 0x43}, + {0x3690, 0x35}, + {0x3691, 0x36}, + {0x3692, 0x66}, + {0x3693, 0x40}, + {0x3694, 0x41}, + {0x3696, 0x85}, + {0x3697, 0x85}, + {0x3698, 0x83}, + {0x3699, 0x92}, + {0x369a, 0x99}, + {0x369b, 0xff}, + {0x369c, 0xff}, + {0x369d, 0xff}, + {0x36a2, 0x40}, + {0x36a3, 0x41}, + {0x36a4, 0x43}, + {0x36a5, 0x47}, + {0x36a6, 0x4f}, + {0x36a7, 0x4f}, + {0x36a8, 0x4f}, + {0x36d0, 0x15}, + {0x36ea, 0x0b}, + {0x36eb, 0x04}, + {0x36ec, 0x43}, + {0x36ed, 0x3a}, + {0x370f, 0x01}, + {0x3727, 0x14}, + {0x37b0, 0x03}, + {0x37b1, 0x8b}, + {0x37b2, 0x8b}, + {0x37b3, 0x41}, + {0x37b4, 0x43}, + {0x37fa, 0xc7}, + {0x37fb, 0x31}, + {0x37fc, 0x10}, + {0x37fd, 0x36}, + {0x3905, 0xad}, + {0x391f, 0x41}, + {0x3e00, 0x01}, + {0x3e01, 0x17}, + {0x3e02, 0x00}, + {0x4509, 0x1a}, + {0x450d, 0x0a}, + {0x5780, 0x66}, + {0x5784, 0x08}, + {0x5785, 0x04}, + {0x5787, 0x0a}, + {0x5788, 0x0a}, + {0x5789, 0x08}, + {0x578a, 0x0a}, + {0x578b, 0x0a}, + {0x578c, 0x08}, + {0x578d, 0x40}, + {0x5790, 0x08}, + {0x5791, 0x04}, + {0x5792, 0x04}, + {0x5793, 0x08}, + {0x5794, 0x04}, + {0x5795, 0x04}, + {0x57a8, 0xd0}, + {0x57aa, 0x2a}, + {0x57ab, 0x7f}, + {0x57ac, 0x00}, + {0x57ad, 0x00}, + {0x36e9, 0x24}, + {0x37f9, 0x53}, + {REG_NULL, 0x00}, +}; + +/* + * The width and height must be configured to be + * the same as the current output resolution of the sensor. + * The input width of the isp needs to be 16 aligned. + * The input height of the isp needs to be 8 aligned. + * If the width or height does not meet the alignment rules, + * you can configure the cropping parameters with the following function to + * crop out the appropriate resolution. + * struct v4l2_subdev_pad_ops { + * .get_selection + * } + */ +static const struct sc831ai_mode supported_modes[] = { + { + .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10, + .width = 3840, + .height = 2160, + .max_fps = { + .numerator = 10000, + .denominator = 150000, + }, + .exp_def = 0x08c0, + .hts_def = 0x1068, + .vts_def = 0x08ca, + .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10, + .reg_list = sc831ai_2lane_linear10bit_3840x2160_regs, + .hdr_mode = NO_HDR, + .mipi_freq_idx = 0, + .bpp = 10, + .vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0, + }, +}; + +static const char *const sc831ai_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bar Type 1", + "Vertical Color Bar Type 2", + "Vertical Color Bar Type 3", + "Vertical Color Bar Type 4" +}; + +static const s64 link_freq_items[] = { + MIPI_FREQ_396M, +}; + +/* Write registers up to 4 at a time */ +static int sc831ai_write_reg(struct i2c_client *client, u16 reg, u32 len, + u32 val) +{ + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; + + if (len > 4) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +static int sc831ai_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + ret = sc831ai_write_reg(client, regs[i].addr, + SC831AI_REG_VALUE_08BIT, regs[i].val); + } + return ret; +} + +/* Read registers up to 4 at a time */ +static int sc831ai_read_reg(struct i2c_client *client, u16 reg, + unsigned int len, u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_be_p[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = be32_to_cpu(data_be); + + return 0; +} + +static int sc831ai_get_reso_dist(const struct sc831ai_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct sc831ai_mode * +sc831ai_find_best_fit(struct sc831ai *sc831ai, struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = sc831ai_get_reso_dist(&supported_modes[i], framefmt); + if ((cur_best_fit_dist == -1 || dist < cur_best_fit_dist) && + supported_modes[i].bus_fmt == framefmt->code) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + dev_info(&sc831ai->client->dev, "%s: cur_best_fit(%d)", __func__, + cur_best_fit); + + return &supported_modes[cur_best_fit]; +} + +static void sc831ai_change_mode(struct sc831ai *sc831ai, + const struct sc831ai_mode *mode) +{ + sc831ai->cur_mode = mode; + sc831ai->cur_vts = sc831ai->cur_mode->vts_def; + dev_info(&sc831ai->client->dev, "set fmt: cur_mode: %dx%d, hdr: %d\n", + mode->width, mode->height, mode->hdr_mode); +} + +static int sc831ai_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct sc831ai *sc831ai = to_sc831ai(sd); + const struct sc831ai_mode *mode; + s64 h_blank, vblank_def; + u64 pixel_rate = 0; + + mutex_lock(&sc831ai->mutex); + + mode = sc831ai_find_best_fit(sc831ai, fmt); + fmt->format.code = mode->bus_fmt; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; +#else + mutex_unlock(&sc831ai->mutex); + return -ENOTTY; +#endif + } else { + sc831ai_change_mode(sc831ai, mode); + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(sc831ai->hblank, h_blank, h_blank, 1, + h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(sc831ai->vblank, vblank_def, + SC831AI_VTS_MAX - mode->height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(sc831ai->link_freq, mode->mipi_freq_idx); + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / + mode->bpp * 2 * SC831AI_2LANES; + __v4l2_ctrl_s_ctrl_int64(sc831ai->pixel_rate, pixel_rate); + sc831ai->cur_fps = mode->max_fps; + sc831ai->cur_vts = mode->vts_def; + } + + mutex_unlock(&sc831ai->mutex); + + return 0; +} + +static int sc831ai_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct sc831ai *sc831ai = to_sc831ai(sd); + const struct sc831ai_mode *mode = sc831ai->cur_mode; + + mutex_lock(&sc831ai->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); +#else + mutex_unlock(&sc831ai->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = mode->bus_fmt; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR) + fmt->reserved[0] = mode->vc[fmt->pad]; + else + fmt->reserved[0] = mode->vc[PAD0]; + } + mutex_unlock(&sc831ai->mutex); + + return 0; +} + +static int sc831ai_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct sc831ai *sc831ai = to_sc831ai(sd); + + if (code->index != 0) + return -EINVAL; + code->code = sc831ai->cur_mode->bus_fmt; + + return 0; +} + +static int sc831ai_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct sc831ai *sc831ai = to_sc831ai(sd); + + if (fse->index >= sc831ai->cfg_num) + return -EINVAL; + + if (fse->code != supported_modes[fse->index].bus_fmt) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int sc831ai_enable_test_pattern(struct sc831ai *sc831ai, u32 pattern) +{ + u32 val = 0; + int ret = 0; + + ret = sc831ai_read_reg(sc831ai->client, SC831AI_REG_TEST_PATTERN, + SC831AI_REG_VALUE_08BIT, &val); + if (pattern) + val |= SC831AI_TEST_PATTERN_ENABLE; + else + val &= ~SC831AI_TEST_PATTERN_ENABLE; + ret |= sc831ai_write_reg(sc831ai->client, SC831AI_REG_TEST_PATTERN, + SC831AI_REG_VALUE_08BIT, val); + return ret; +} + +static int sc831ai_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct sc831ai *sc831ai = to_sc831ai(sd); + const struct sc831ai_mode *mode = sc831ai->cur_mode; + + if (sc831ai->streaming) + fi->interval = sc831ai->cur_fps; + else + fi->interval = mode->max_fps; + + return 0; +} + +static int sc831ai_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct sc831ai *sc831ai = to_sc831ai(sd); + const struct sc831ai_mode *mode = sc831ai->cur_mode; + u32 val = 0; + + if (mode->hdr_mode == NO_HDR) + val = 1 << (SC831AI_2LANES - 1) | V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + if (mode->hdr_mode == HDR_X2) + val = 1 << (SC831AI_2LANES - 1) | V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | + V4L2_MBUS_CSI2_CHANNEL_1; + + config->type = V4L2_MBUS_CSI2_DPHY; + config->flags = val; + + return 0; +} + +static void sc831ai_get_module_inf(struct sc831ai *sc831ai, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strscpy(inf->base.sensor, SC831AI_NAME, sizeof(inf->base.sensor)); + strscpy(inf->base.module, sc831ai->module_name, + sizeof(inf->base.module)); + strscpy(inf->base.lens, sc831ai->len_name, sizeof(inf->base.lens)); +} + +static void sc831ai_get_gain_reg(u32 val, u8 *again_reg, u8 *dgain_reg, + u8 *again_fine_reg, u8 *dgain_fine_reg) +{ + u32 coarse_again = 0, coarse_dgain = 0, fine_again = 0, fine_dgain = 0; + int gain_factor; + + if (val < SC831AI_GAIN_MIN) + val = SC831AI_GAIN_MIN; + else if (val > SC831AI_GAIN_MAX) + val = SC831AI_GAIN_MAX; + + gain_factor = val * 1000 / 64; + if (gain_factor < 1468) { /* 1x ~ 1.468x */ + coarse_again = 0x00; + coarse_dgain = 0x00; + fine_dgain = 0x80; + fine_again = gain_factor * 32 / 1000; + } else if (gain_factor < 2936) { /* 1.438 ~ 2.936x */ + coarse_again = 0x80; + coarse_dgain = 0x00; + fine_dgain = 0x80; + fine_again = gain_factor * 32 / 1468; + } else if (gain_factor < 5872) { /* 2.936x ~ 5.872x */ + coarse_again = 0x81; + coarse_dgain = 0x00; + fine_dgain = 0x80; + fine_again = gain_factor * 32 / 2936; + } else if (gain_factor < 11744) { /* 5.872 ~ 11.744x */ + coarse_again = 0x83; + coarse_dgain = 0x00; + fine_dgain = 0x80; + fine_again = gain_factor * 32 / 5872; + } else if (gain_factor < 23488) { /* 11.744x ~ 23.488x */ + coarse_again = 0x87; + coarse_dgain = 0x00; + fine_dgain = 0x80; + fine_again = gain_factor * 32 / 11744; + } else if (gain_factor <= 46242) { /* 23.488x ~ 46.242x */ + coarse_again = 0x8f; + coarse_dgain = 0x00; + fine_dgain = 0x80; + fine_again = gain_factor * 32 / 23488; + } else if (gain_factor < 46242 * 2) { /* 1.0x ~ 2.0x */ + //open dgain begin max digital gain 16X + coarse_again = 0x8f; + coarse_dgain = 0x00; + fine_again = 0x3f; + fine_dgain = gain_factor * 128 / 46242; + } else if (gain_factor < 46242 * 4) { /* 2.0x ~ 4.0x */ + coarse_again = 0x8f; + coarse_dgain = 0x01; + fine_again = 0x3f; + fine_dgain = gain_factor * 128 / 46242 / 2; + } else if (gain_factor < 46242 * 8) { /* 4.0x ~ 8.0x */ + coarse_again = 0x8f; + coarse_dgain = 0x03; + fine_again = 0x3f; + fine_dgain = gain_factor * 128 / 46242 / 4; + } else if (gain_factor < 46242 * 16) { /* 8.0x ~ 16.0x */ + coarse_again = 0x8f; + coarse_dgain = 0x07; + fine_again = 0x3f; + fine_dgain = gain_factor * 128 / 46242 / 8; + } + *again_reg = coarse_again; + *again_fine_reg = fine_again; + *dgain_reg = coarse_dgain; + *dgain_fine_reg = fine_dgain; +} + +static int sc831ai_get_channel_info(struct sc831ai *sc831ai, + struct rkmodule_channel_info *ch_info) +{ + if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX) + return -EINVAL; + ch_info->vc = sc831ai->cur_mode->vc[ch_info->index]; + ch_info->width = sc831ai->cur_mode->width; + ch_info->height = sc831ai->cur_mode->height; + ch_info->bus_fmt = sc831ai->cur_mode->bus_fmt; + return 0; +} + +static long sc831ai_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct sc831ai *sc831ai = to_sc831ai(sd); + struct rkmodule_hdr_cfg *hdr_cfg; + const struct sc831ai_mode *mode; + struct rkmodule_channel_info *ch_info; + long ret = 0; + u64 pixel_rate = 0; + u32 i, h, w, stream; + + switch (cmd) { + case PREISP_CMD_SET_HDRAE_EXP: + /* + * ret = sc831ai_set_hdrae(sc831ai, arg); + */ + break; + + case RKMODULE_SET_HDR_CFG: + hdr_cfg = (struct rkmodule_hdr_cfg *)arg; + if (sc831ai->streaming) { + ret = sc831ai_write_array(sc831ai->client, + sc831ai->cur_mode->reg_list); + if (ret) + return ret; + } + w = sc831ai->cur_mode->width; + h = sc831ai->cur_mode->height; + for (i = 0; i < sc831ai->cfg_num; i++) { + if (w == supported_modes[i].width && + h == supported_modes[i].height && + supported_modes[i].hdr_mode == hdr_cfg->hdr_mode) { + sc831ai_change_mode(sc831ai, + &supported_modes[i]); + break; + } + } + if (i == sc831ai->cfg_num) { + dev_err(&sc831ai->client->dev, + "not find hdr mode:%d %dx%d config\n", + hdr_cfg->hdr_mode, w, h); + ret = -EINVAL; + } else { + mode = sc831ai->cur_mode; + w = mode->hts_def - mode->width; + h = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(sc831ai->hblank, w, w, 1, w); + __v4l2_ctrl_modify_range(sc831ai->vblank, h, + SC831AI_VTS_MAX - mode->height, + 1, h); + __v4l2_ctrl_s_ctrl(sc831ai->link_freq, + mode->mipi_freq_idx); + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / + mode->bpp * 2 * SC831AI_2LANES; + __v4l2_ctrl_s_ctrl_int64(sc831ai->pixel_rate, + pixel_rate); + sc831ai->cur_fps = mode->max_fps; + sc831ai->cur_vts = mode->vts_def; + dev_info(&sc831ai->client->dev, "sensor mode: %d\n", + mode->hdr_mode); + } + break; + case RKMODULE_GET_MODULE_INFO: + sc831ai_get_module_inf(sc831ai, (struct rkmodule_inf *)arg); + break; + + case RKMODULE_GET_HDR_CFG: + hdr_cfg = (struct rkmodule_hdr_cfg *)arg; + hdr_cfg->esp.mode = HDR_NORMAL_VC; + hdr_cfg->hdr_mode = sc831ai->cur_mode->hdr_mode; + break; + + case RKMODULE_SET_QUICK_STREAM: + stream = *((u32 *)arg); + if (stream) + ret = sc831ai_write_reg(sc831ai->client, + SC831AI_REG_CTRL_MODE, + SC831AI_REG_VALUE_08BIT, + SC831AI_MODE_STREAMING); + else + ret = sc831ai_write_reg(sc831ai->client, + SC831AI_REG_CTRL_MODE, + SC831AI_REG_VALUE_08BIT, + SC831AI_MODE_SW_STANDBY); + break; + + case RKMODULE_GET_CHANNEL_INFO: + ch_info = (struct rkmodule_channel_info *)arg; + ret = sc831ai_get_channel_info(sc831ai, ch_info); + break; + + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long sc831ai_compat_ioctl32(struct v4l2_subdev *sd, unsigned int cmd, + unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *cfg; + struct rkmodule_hdr_cfg *hdr; + struct preisp_hdrae_exp_s *hdrae; + struct rkmodule_channel_info *ch_info; + long ret; + u32 stream; + u32 brl = 0; + struct rkmodule_csi_dphy_param *dphy_param; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = sc831ai_ioctl(sd, cmd, inf); + if (!ret) { + if (copy_to_user(up, inf, sizeof(*inf))) { + kfree(inf); + return -EFAULT; + } + } + kfree(inf); + break; + case RKMODULE_AWB_CFG: + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + ret = -ENOMEM; + return ret; + } + + if (copy_from_user(cfg, up, sizeof(*cfg))) { + kfree(cfg); + return -EFAULT; + } + ret = sc831ai_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 = sc831ai_ioctl(sd, cmd, hdr); + if (!ret) { + if (copy_to_user(up, hdr, sizeof(*hdr))) { + kfree(hdr); + return -EFAULT; + } + } + kfree(hdr); + break; + case RKMODULE_SET_HDR_CFG: + hdr = kzalloc(sizeof(*hdr), GFP_KERNEL); + if (!hdr) { + ret = -ENOMEM; + return ret; + } + + if (copy_from_user(hdr, up, sizeof(*hdr))) { + kfree(hdr); + return -EFAULT; + } + ret = sc831ai_ioctl(sd, cmd, hdr); + kfree(hdr); + break; + case PREISP_CMD_SET_HDRAE_EXP: + hdrae = kzalloc(sizeof(*hdrae), GFP_KERNEL); + if (!hdrae) { + ret = -ENOMEM; + return ret; + } + + if (copy_from_user(hdrae, up, sizeof(*hdrae))) { + kfree(hdrae); + return -EFAULT; + } + ret = sc831ai_ioctl(sd, cmd, hdrae); + kfree(hdrae); + break; + case RKMODULE_SET_QUICK_STREAM: + if (copy_from_user(&stream, up, sizeof(u32))) + return -EFAULT; + ret = sc831ai_ioctl(sd, cmd, &stream); + break; + case RKMODULE_GET_SONY_BRL: + ret = sc831ai_ioctl(sd, cmd, &brl); + if (!ret) { + if (copy_to_user(up, &brl, sizeof(u32))) + return -EFAULT; + } + break; + case RKMODULE_GET_CHANNEL_INFO: + ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); + if (!ch_info) { + ret = -ENOMEM; + return ret; + } + + ret = sc831ai_ioctl(sd, cmd, ch_info); + if (!ret) { + ret = copy_to_user(up, ch_info, sizeof(*ch_info)); + if (ret) + ret = -EFAULT; + } + kfree(ch_info); + break; + case RKMODULE_GET_CSI_DPHY_PARAM: + dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL); + if (!dphy_param) { + ret = -ENOMEM; + return ret; + } + + ret = sc831ai_ioctl(sd, cmd, dphy_param); + if (!ret) { + ret = copy_to_user(up, dphy_param, sizeof(*dphy_param)); + if (ret) + ret = -EFAULT; + } + kfree(dphy_param); + break; + + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __sc831ai_start_stream(struct sc831ai *sc831ai) +{ + int ret; + + if (!sc831ai->is_thunderboot) { + ret = sc831ai_write_array(sc831ai->client, sc831ai->cur_mode->reg_list); + if (ret) + return ret; + + ret = __v4l2_ctrl_handler_setup(&sc831ai->ctrl_handler); + if (ret) + return ret; + /* In case these controls are set before streaming */ + if (sc831ai->has_init_exp && sc831ai->cur_mode->hdr_mode != NO_HDR) { + ret = sc831ai_ioctl(&sc831ai->subdev, PREISP_CMD_SET_HDRAE_EXP, + &sc831ai->init_hdrae_exp); + if (ret) { + dev_err(&sc831ai->client->dev, + "init exp fail in hdr mode\n"); + return ret; + } + } + } + return sc831ai_write_reg(sc831ai->client, SC831AI_REG_CTRL_MODE, + SC831AI_REG_VALUE_08BIT, + SC831AI_MODE_STREAMING); +} + +static int __sc831ai_stop_stream(struct sc831ai *sc831ai) +{ + sc831ai->has_init_exp = false; + if (sc831ai->is_thunderboot) + sc831ai->is_first_streamoff = true; + return sc831ai_write_reg(sc831ai->client, SC831AI_REG_CTRL_MODE, + SC831AI_REG_VALUE_08BIT, + SC831AI_MODE_SW_STANDBY); +} + +static int __sc831ai_power_on(struct sc831ai *sc831ai); +static int sc831ai_s_stream(struct v4l2_subdev *sd, int on) +{ + struct sc831ai *sc831ai = to_sc831ai(sd); + struct i2c_client *client = sc831ai->client; + int ret = 0; + + dev_info(&sc831ai->client->dev, + "s_stream: %d. %dx%d, hdr: %d, bpp: %d\n", on, + sc831ai->cur_mode->width, sc831ai->cur_mode->height, + sc831ai->cur_mode->hdr_mode, sc831ai->cur_mode->bpp); + + mutex_lock(&sc831ai->mutex); + on = !!on; + if (on == sc831ai->streaming) + goto unlock_and_return; + + if (on) { + if (sc831ai->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) { + sc831ai->is_thunderboot = false; + __sc831ai_power_on(sc831ai); + } + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + ret = __sc831ai_start_stream(sc831ai); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + __sc831ai_stop_stream(sc831ai); + pm_runtime_put(&client->dev); + } + + sc831ai->streaming = on; + +unlock_and_return: + mutex_unlock(&sc831ai->mutex); + return ret; +} + +static int sc831ai_s_power(struct v4l2_subdev *sd, int on) +{ + struct sc831ai *sc831ai = to_sc831ai(sd); + struct i2c_client *client = sc831ai->client; + int ret = 0; + + mutex_lock(&sc831ai->mutex); + + /* If the power state is not modified - no work to do. */ + if (sc831ai->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + if (!sc831ai->is_thunderboot) { + ret |= sc831ai_write_reg(sc831ai->client, + SC831AI_SOFTWARE_RESET_REG, + SC831AI_REG_VALUE_08BIT, 0x01); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + usleep_range(100, 200); + } + sc831ai->power_on = true; + } else { + pm_runtime_put(&client->dev); + sc831ai->power_on = false; + } + +unlock_and_return: + mutex_unlock(&sc831ai->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 sc831ai_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, SC831AI_XVCLK_FREQ / 1000 / 1000); +} + +static int __sc831ai_power_on(struct sc831ai *sc831ai) +{ + int ret; + u32 delay_us; + struct device *dev = &sc831ai->client->dev; + + if (!IS_ERR_OR_NULL(sc831ai->pins_default)) { + ret = pinctrl_select_state(sc831ai->pinctrl, + sc831ai->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + ret = clk_set_rate(sc831ai->xvclk, SC831AI_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate 24MHz\n"); + if (clk_get_rate(sc831ai->xvclk) != SC831AI_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched\n"); + ret = clk_prepare_enable(sc831ai->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + goto err_clk; + } + + cam_sw_regulator_bulk_init(sc831ai->cam_sw_inf, SC831AI_NUM_SUPPLIES, sc831ai->supplies); + + if (sc831ai->is_thunderboot) + return 0; + + if (!IS_ERR(sc831ai->reset_gpio)) + gpiod_direction_output(sc831ai->reset_gpio, 0); + + ret = regulator_bulk_enable(SC831AI_NUM_SUPPLIES, sc831ai->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(sc831ai->reset_gpio)) + gpiod_direction_output(sc831ai->reset_gpio, 1); + + usleep_range(5000, 10000); + if (!IS_ERR(sc831ai->pwdn_gpio)) + gpiod_direction_output(sc831ai->pwdn_gpio, 1); + + if (!IS_ERR(sc831ai->reset_gpio)) + usleep_range(6000, 8000); + else + usleep_range(12000, 16000); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = sc831ai_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + return 0; +err_clk: + if (!IS_ERR(sc831ai->reset_gpio)) + gpiod_direction_output(sc831ai->reset_gpio, 0); +disable_clk: + clk_disable_unprepare(sc831ai->xvclk); + + return ret; +} + +static void __sc831ai_power_off(struct sc831ai *sc831ai) +{ + int ret; + struct device *dev = &sc831ai->client->dev; + + clk_disable_unprepare(sc831ai->xvclk); + + if (sc831ai->is_thunderboot) { + if (sc831ai->is_first_streamoff) { + sc831ai->is_thunderboot = false; + sc831ai->is_first_streamoff = false; + } else { + return; + } + } + + if (!IS_ERR(sc831ai->pwdn_gpio)) + gpiod_direction_output(sc831ai->pwdn_gpio, 0); + + if (!IS_ERR(sc831ai->reset_gpio)) + gpiod_direction_output(sc831ai->reset_gpio, 0); + + if (!IS_ERR_OR_NULL(sc831ai->pins_sleep)) { + ret = pinctrl_select_state(sc831ai->pinctrl, + sc831ai->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + regulator_bulk_disable(SC831AI_NUM_SUPPLIES, sc831ai->supplies); +} + +#if IS_REACHABLE(CONFIG_VIDEO_CAM_SLEEP_WAKEUP) +static int __maybe_unused sc831ai_resume(struct device *dev) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct sc831ai *sc831ai = to_sc831ai(sd); + + cam_sw_prepare_wakeup(sc831ai->cam_sw_inf, dev); + + usleep_range(4000, 5000); + cam_sw_write_array(sc831ai->cam_sw_inf); + + if (__v4l2_ctrl_handler_setup(&sc831ai->ctrl_handler)) + dev_err(dev, "__v4l2_ctrl_handler_setup fail!"); + + if (sc831ai->has_init_exp && sc831ai->cur_mode != NO_HDR) { // hdr mode + ret = sc831ai_ioctl(&sc831ai->subdev, PREISP_CMD_SET_HDRAE_EXP, + &sc831ai->cam_sw_inf->hdr_ae); + if (ret) { + dev_err(&sc831ai->client->dev, "set exp fail in hdr mode\n"); + return ret; + } + } + return 0; +} + +static int __maybe_unused sc831ai_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct sc831ai *sc831ai = to_sc831ai(sd); + + cam_sw_write_array_cb_init(sc831ai->cam_sw_inf, client, + (void *)sc831ai->cur_mode->reg_list, + (sensor_write_array)sc831ai_write_array); + cam_sw_prepare_sleep(sc831ai->cam_sw_inf); + + return 0; +} +#else +#define sc831ai_resume NULL +#define sc831ai_suspend NULL +#endif + +static int sc831ai_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct sc831ai *sc831ai = to_sc831ai(sd); + + return __sc831ai_power_on(sc831ai); +} + +static int sc831ai_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct sc831ai *sc831ai = to_sc831ai(sd); + + __sc831ai_power_off(sc831ai); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int sc831ai_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct sc831ai *sc831ai = to_sc831ai(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct sc831ai_mode *def_mode = &supported_modes[0]; + + mutex_lock(&sc831ai->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = def_mode->bus_fmt; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&sc831ai->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int +sc831ai_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct sc831ai *sc831ai = to_sc831ai(sd); + + if (fie->index >= sc831ai->cfg_num) + return -EINVAL; + + fie->code = supported_modes[fie->index].bus_fmt; + fie->width = supported_modes[fie->index].width; + fie->height = supported_modes[fie->index].height; + fie->interval = supported_modes[fie->index].max_fps; + fie->reserved[0] = supported_modes[fie->index].hdr_mode; + return 0; +} + +#define CROP_START(SRC, DST) (((SRC) - (DST)) / 2 / 4 * 4) +#define DST_WIDTH_3840 3840 +#define DST_HEIGHT_2160 2160 +#define DST_WIDTH_1920 1920 +#define DST_HEIGHT_1080 1080 + +static int sc831ai_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct sc831ai *sc831ai = to_sc831ai(sd); + + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + if (sc831ai->cur_mode->width == 3856) { + sel->r.left = CROP_START(sc831ai->cur_mode->width, + DST_WIDTH_3840); + sel->r.width = DST_WIDTH_3840; + sel->r.top = CROP_START(sc831ai->cur_mode->height, + DST_HEIGHT_2160); + sel->r.height = DST_HEIGHT_2160; + } else if (sc831ai->cur_mode->width == 1944) { + sel->r.left = CROP_START(sc831ai->cur_mode->width, + DST_WIDTH_1920); + sel->r.width = DST_WIDTH_1920; + sel->r.top = CROP_START(sc831ai->cur_mode->height, + DST_HEIGHT_1080); + sel->r.height = DST_HEIGHT_1080; + } else { + sel->r.left = CROP_START(sc831ai->cur_mode->width, + sc831ai->cur_mode->width); + sel->r.width = sc831ai->cur_mode->width; + sel->r.top = CROP_START(sc831ai->cur_mode->height, + sc831ai->cur_mode->height); + sel->r.height = sc831ai->cur_mode->height; + } + return 0; + } + return -EINVAL; +} + +static const struct dev_pm_ops sc831ai_pm_ops = { + SET_RUNTIME_PM_OPS(sc831ai_runtime_suspend, + sc831ai_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(sc831ai_suspend, sc831ai_resume) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops sc831ai_internal_ops = { + .open = sc831ai_open, +}; +#endif + +static const struct v4l2_subdev_core_ops sc831ai_core_ops = { + .s_power = sc831ai_s_power, + .ioctl = sc831ai_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = sc831ai_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops sc831ai_video_ops = { + .s_stream = sc831ai_s_stream, + .g_frame_interval = sc831ai_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops sc831ai_pad_ops = { + .enum_mbus_code = sc831ai_enum_mbus_code, + .enum_frame_size = sc831ai_enum_frame_sizes, + .enum_frame_interval = sc831ai_enum_frame_interval, + .get_fmt = sc831ai_get_fmt, + .set_fmt = sc831ai_set_fmt, + .get_selection = sc831ai_get_selection, + .get_mbus_config = sc831ai_g_mbus_config, +}; + +static const struct v4l2_subdev_ops sc831ai_subdev_ops = { + .core = &sc831ai_core_ops, + .video = &sc831ai_video_ops, + .pad = &sc831ai_pad_ops, +}; + +static void sc831ai_modify_fps_info(struct sc831ai *sc831ai) +{ + const struct sc831ai_mode *mode = sc831ai->cur_mode; + + sc831ai->cur_fps.denominator = + mode->max_fps.denominator * sc831ai->cur_vts / mode->vts_def; +} + +static int sc831ai_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct sc831ai *sc831ai = + container_of(ctrl->handler, struct sc831ai, ctrl_handler); + struct i2c_client *client = sc831ai->client; + s64 max; + u8 again, dgain, again_fine, dgain_fine; + int ret = 0; + u32 val; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = sc831ai->cur_mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(sc831ai->exposure, + sc831ai->exposure->minimum, max, + sc831ai->exposure->step, + sc831ai->exposure->default_value); + break; + } + + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if (sc831ai->cur_mode->hdr_mode != NO_HDR) + goto out_ctrl; + val = ctrl->val << 1; + ret = sc831ai_write_reg(sc831ai->client, SC831AI_REG_EXP_LONG_H, + SC831AI_REG_VALUE_08BIT, + SC831AI_FETCH_EXP_H(val)); + ret |= sc831ai_write_reg(sc831ai->client, + SC831AI_REG_EXP_LONG_M, + SC831AI_REG_VALUE_08BIT, + SC831AI_FETCH_EXP_M(val)); + ret |= sc831ai_write_reg(sc831ai->client, + SC831AI_REG_EXP_LONG_L, + SC831AI_REG_VALUE_08BIT, + SC831AI_FETCH_EXP_L(val)); + dev_dbg(&client->dev, "set exposure 0x%x\n", ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + if (sc831ai->cur_mode->hdr_mode != NO_HDR) + goto out_ctrl; + sc831ai_get_gain_reg(ctrl->val, &again, &dgain, &again_fine, + &dgain_fine); + dev_dbg(&client->dev, + "recv_gain:%d set again 0x%x, again_fine 0x%x, set dgain 0x%x, dgain_fine 0x%x\n", + ctrl->val, again, again_fine, dgain, dgain_fine); + ret |= sc831ai_write_reg(sc831ai->client, SC831AI_REG_AGAIN, + SC831AI_REG_VALUE_08BIT, again); + ret |= sc831ai_write_reg(sc831ai->client, SC831AI_REG_GAIN_FINE, + SC831AI_REG_VALUE_08BIT, again_fine); + ret |= sc831ai_write_reg(sc831ai->client, SC831AI_REG_DGAIN, + SC831AI_REG_VALUE_08BIT, dgain); + ret |= sc831ai_write_reg(sc831ai->client, SC831AI_REG_DGAIN_FINE, + SC831AI_REG_VALUE_08BIT, dgain_fine); + break; + case V4L2_CID_VBLANK: + ret = sc831ai_write_reg(sc831ai->client, SC831AI_REG_VTS, + SC831AI_REG_VALUE_16BIT, + ctrl->val + sc831ai->cur_mode->height); + if (!ret) + sc831ai->cur_vts = + ctrl->val + sc831ai->cur_mode->height; + sc831ai_modify_fps_info(sc831ai); + dev_dbg(&client->dev, "set vblank 0x%x\n", ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = sc831ai_enable_test_pattern(sc831ai, ctrl->val); + break; + case V4L2_CID_HFLIP: + ret = sc831ai_read_reg(sc831ai->client, SC831AI_FLIP_REG, + SC831AI_REG_VALUE_08BIT, &val); + if (ret) + break; + if (ctrl->val) + val |= SC831AI_MIRROR_MASK; + else + val &= ~SC831AI_MIRROR_MASK; + ret |= sc831ai_write_reg(sc831ai->client, SC831AI_FLIP_REG, + SC831AI_REG_VALUE_08BIT, val); + break; + case V4L2_CID_VFLIP: + ret = sc831ai_read_reg(sc831ai->client, SC831AI_FLIP_REG, + SC831AI_REG_VALUE_08BIT, &val); + if (ret) + break; + if (ctrl->val) + val |= SC831AI_FLIP_MASK; + else + val &= ~SC831AI_FLIP_MASK; + ret |= sc831ai_write_reg(sc831ai->client, SC831AI_FLIP_REG, + SC831AI_REG_VALUE_08BIT, val); + break; + default: + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); + break; + } + +out_ctrl: + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops sc831ai_ctrl_ops = { + .s_ctrl = sc831ai_set_ctrl, +}; + +static int sc831ai_initialize_controls(struct sc831ai *sc831ai) +{ + const struct sc831ai_mode *mode; + struct v4l2_ctrl_handler *handler; + s64 exposure_max, vblank_def; + u64 pixel_rate = 0; + u32 h_blank; + int ret; + + handler = &sc831ai->ctrl_handler; + mode = sc831ai->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 9); + if (ret) + return ret; + handler->lock = &sc831ai->mutex; + + sc831ai->link_freq = v4l2_ctrl_new_int_menu( + handler, NULL, V4L2_CID_LINK_FREQ, 0, 0, link_freq_items); + v4l2_ctrl_s_ctrl(sc831ai->link_freq, mode->mipi_freq_idx); + + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + pixel_rate = (u32)link_freq_items[mode->mipi_freq_idx] / mode->bpp * 2 * + SC831AI_2LANES; + sc831ai->pixel_rate = + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0, + SC831AI_MAX_PIXEL_RATE, 1, pixel_rate); + + h_blank = mode->hts_def - mode->width; + sc831ai->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); + if (sc831ai->hblank) + sc831ai->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_def = mode->vts_def - mode->height; + sc831ai->vblank = + v4l2_ctrl_new_std(handler, &sc831ai_ctrl_ops, V4L2_CID_VBLANK, + vblank_def, SC831AI_VTS_MAX - mode->height, 1, + vblank_def); + + exposure_max = mode->vts_def - 4; /*vts_def 0x08ca=2250*/ + sc831ai->exposure = + v4l2_ctrl_new_std(handler, &sc831ai_ctrl_ops, V4L2_CID_EXPOSURE, + SC831AI_EXPOSURE_MIN, exposure_max, + SC831AI_EXPOSURE_STEP, + mode->exp_def); /*exp_def 0x08c0=2240*/ + + sc831ai->anal_a_gain = + v4l2_ctrl_new_std(handler, &sc831ai_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, SC831AI_GAIN_MIN, + SC831AI_GAIN_MAX, SC831AI_GAIN_STEP, + SC831AI_GAIN_DEFAULT); + + sc831ai->test_pattern = v4l2_ctrl_new_std_menu_items( + handler, &sc831ai_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(sc831ai_test_pattern_menu) - 1, 0, 0, + sc831ai_test_pattern_menu); + + v4l2_ctrl_new_std(handler, &sc831ai_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, + 0); + v4l2_ctrl_new_std(handler, &sc831ai_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, + 0); + + if (handler->error) { + ret = handler->error; + dev_err(&sc831ai->client->dev, "Failed to init controls(%d)\n", + ret); + goto err_free_handler; + } + + sc831ai->subdev.ctrl_handler = handler; + sc831ai->has_init_exp = false; + sc831ai->cur_fps = mode->max_fps; + sc831ai->cur_vts = mode->vts_def; + + return 0; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static int sc831ai_check_sensor_id(struct sc831ai *sc831ai, + struct i2c_client *client) +{ + struct device *dev = &sc831ai->client->dev; + u32 id = 0; + int ret; + + if (sc831ai->is_thunderboot) { + dev_info(dev, "Enable thunderboot mode, skip sensor id check\n"); + return 0; + } + + ret = sc831ai_read_reg(client, SC831AI_REG_CHIP_ID, + SC831AI_REG_VALUE_16BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } + + dev_info(dev, "Detected sc831ai id %06x\n", CHIP_ID); + + return 0; +} + +static int sc831ai_configure_regulators(struct sc831ai *sc831ai) +{ + unsigned int i; + + for (i = 0; i < SC831AI_NUM_SUPPLIES; i++) + sc831ai->supplies[i].supply = sc831ai_supply_names[i]; + + return devm_regulator_bulk_get(&sc831ai->client->dev, + SC831AI_NUM_SUPPLIES, sc831ai->supplies); +} + +static int sc831ai_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct sc831ai *sc831ai; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + u32 i, hdr_mode = 0; + + dev_info(dev, "driver version: %02x.%02x.%02x", DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, DRIVER_VERSION & 0x00ff); + + sc831ai = devm_kzalloc(dev, sizeof(*sc831ai), GFP_KERNEL); + if (!sc831ai) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &sc831ai->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &sc831ai->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &sc831ai->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &sc831ai->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + ret = of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode); + if (ret) { + hdr_mode = NO_HDR; + dev_warn(dev, " Get hdr mode failed! no hdr default\n"); + } + + sc831ai->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP); + + sc831ai->client = client; + sc831ai->cfg_num = ARRAY_SIZE(supported_modes); + for (i = 0; i < sc831ai->cfg_num; i++) { + if (hdr_mode == supported_modes[i].hdr_mode) { + sc831ai->cur_mode = &supported_modes[i]; + break; + } + } + if (i == ARRAY_SIZE(supported_modes)) + sc831ai->cur_mode = &supported_modes[0]; + + sc831ai->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(sc831ai->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + if (!sc831ai->is_thunderboot) { + sc831ai->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + sc831ai->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + } else { + sc831ai->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS); + sc831ai->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_ASIS); + } + + if (IS_ERR(sc831ai->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios\n"); + + if (IS_ERR(sc831ai->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn_gpios\n"); + + sc831ai->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(sc831ai->pinctrl)) { + sc831ai->pins_default = pinctrl_lookup_state( + sc831ai->pinctrl, OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(sc831ai->pins_default)) + dev_info(dev, "could not get default pinstate\n"); + + sc831ai->pins_sleep = pinctrl_lookup_state( + sc831ai->pinctrl, OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(sc831ai->pins_sleep)) + dev_info(dev, "could not get sleep pinstate\n"); + } else { + dev_info(dev, "no pinctrl\n"); + } + + ret = sc831ai_configure_regulators(sc831ai); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + + mutex_init(&sc831ai->mutex); + + sd = &sc831ai->subdev; + v4l2_i2c_subdev_init(sd, client, &sc831ai_subdev_ops); + ret = sc831ai_initialize_controls(sc831ai); + if (ret) + goto err_destroy_mutex; + + ret = __sc831ai_power_on(sc831ai); + if (ret) + goto err_free_handler; + + ret = sc831ai_check_sensor_id(sc831ai, client); + if (ret) + goto err_power_off; +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &sc831ai_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + sc831ai->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &sc831ai->pad); + if (ret < 0) + goto err_power_off; +#endif + + if (!sc831ai->cam_sw_inf) { + sc831ai->cam_sw_inf = cam_sw_init(); + cam_sw_clk_init(sc831ai->cam_sw_inf, sc831ai->xvclk, SC831AI_XVCLK_FREQ); + cam_sw_reset_pin_init(sc831ai->cam_sw_inf, sc831ai->reset_gpio, 0); + if (!IS_ERR(sc831ai->pwdn_gpio)) + cam_sw_pwdn_pin_init(sc831ai->cam_sw_inf, sc831ai->pwdn_gpio, 1); + } + + memset(facing, 0, sizeof(facing)); + if (strcmp(sc831ai->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + sc831ai->module_index, facing, SC831AI_NAME, + dev_name(sd->dev)); + + ret = v4l2_async_register_subdev_sensor_common(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + if (sc831ai->is_thunderboot) + pm_runtime_get_sync(dev); + else + pm_runtime_idle(dev); + + return 0; + +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __sc831ai_power_off(sc831ai); +err_free_handler: + v4l2_ctrl_handler_free(&sc831ai->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&sc831ai->mutex); + + return ret; +} + +static int sc831ai_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct sc831ai *sc831ai = to_sc831ai(sd); + + v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif + v4l2_ctrl_handler_free(&sc831ai->ctrl_handler); + mutex_destroy(&sc831ai->mutex); + + cam_sw_deinit(sc831ai->cam_sw_inf); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __sc831ai_power_off(sc831ai); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id sc831ai_of_match[] = { + { .compatible = "smartsens,sc831ai" }, + {}, +}; +MODULE_DEVICE_TABLE(of, sc831ai_of_match); +#endif + +static const struct i2c_device_id sc831ai_match_id[] = { + { "smartsens,sc831ai", 0 }, + {}, +}; + +static struct i2c_driver sc831ai_i2c_driver = { + .driver = { + .name = SC831AI_NAME, + .pm = &sc831ai_pm_ops, + .of_match_table = of_match_ptr(sc831ai_of_match), + }, + .probe = &sc831ai_probe, + .remove = &sc831ai_remove, + .id_table = sc831ai_match_id, +}; + +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&sc831ai_i2c_driver); +} + +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&sc831ai_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("smartsens,sc831ai sensor driver"); +MODULE_LICENSE("GPL"); From 68e806406c4ec4736c1b31b33267900f80b12cef Mon Sep 17 00:00:00 2001 From: Xiao Ya peng Date: Mon, 29 Jan 2024 10:40:25 +0800 Subject: [PATCH 20/22] arm64: configs: add rk3308_rkpartybox.config Change-Id: I99a98e338466abfaf98e80b52b36f4772676d420 Signed-off-by: Xiao Ya peng --- arch/arm64/configs/rk3308_rkpartybox.config | 227 ++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 arch/arm64/configs/rk3308_rkpartybox.config diff --git a/arch/arm64/configs/rk3308_rkpartybox.config b/arch/arm64/configs/rk3308_rkpartybox.config new file mode 100644 index 000000000000..f63fa7e9f3a1 --- /dev/null +++ b/arch/arm64/configs/rk3308_rkpartybox.config @@ -0,0 +1,227 @@ +CONFIG_FAT_DEFAULT_IOCHARSET="utf8" +CONFIG_FAT_DEFAULT_UTF8=y +CONFIG_FAT_FS=y +CONFIG_FUSE_FS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_936=y +CONFIG_NLS_CODEPAGE_950=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y +CONFIG_NTFS_FS=y +CONFIG_NTFS_RW=y +CONFIG_PHY_ROCKCHIP_USB=y +CONFIG_SCSI=y +CONFIG_USB=y +CONFIG_USB_CONFIGFS_F_UAC1=y +CONFIG_USB_CONFIGFS_F_UAC2=y +CONFIG_VFAT_FS=y +# CONFIG_APPLE_MFI_FASTCHARGE is not set +# CONFIG_AR5523 is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_ATH9K_HTC is not set +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_SCSI_REQUEST=y +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBFUSB is not set +# CONFIG_BT_HCIBPA10X is not set +# CONFIG_BT_HCIBTUSB is not set +# CONFIG_CARL9170 is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CUSE is not set +# CONFIG_DRM_GM12U320 is not set +# CONFIG_DRM_UDL is not set +CONFIG_DVB_RTL2832_SDR=m +# CONFIG_FB_SMSCUFX is not set +# CONFIG_FB_UDL is not set +CONFIG_FRAME_VECTOR=y +CONFIG_FS_POSIX_ACL=y +# CONFIG_HID_ACCUTOUCH is not set +# CONFIG_HID_APPLEIR is not set +# CONFIG_HID_ASUS is not set +# CONFIG_HID_BETOP_FF is not set +# CONFIG_HID_BIGBEN_FF is not set +# CONFIG_HID_CHICONY is not set +# CONFIG_HID_CORSAIR is not set +# CONFIG_HID_CREATIVE_SB0540 is not set +# CONFIG_HID_ELAN is not set +# CONFIG_HID_ELO is not set +# CONFIG_HID_GT683R is not set +# CONFIG_HID_HOLTEK is not set +# CONFIG_HID_LOGITECH is not set +# CONFIG_HID_MCP2221 is not set +# CONFIG_HID_NTRIG is not set +# CONFIG_HID_PENMOUNT is not set +# CONFIG_HID_PID is not set +# CONFIG_HID_PRODIKEYS is not set +# CONFIG_HID_RETRODE is not set +# CONFIG_HID_ROCCAT is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SONY is not set +# CONFIG_HID_U2FZERO is not set +# CONFIG_HID_UCLOGIC is not set +# CONFIG_HID_WACOM is not set +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_ROBOTFUZZ_OSIF is not set +# CONFIG_I2C_TINY_USB is not set +# CONFIG_INPUT_IMS_PCU is not set +# CONFIG_ISCSI_BOOT_SYSFS is not set +# CONFIG_ISCSI_TCP is not set +# CONFIG_LTE_GDM724X is not set +# CONFIG_MDIO_MVUSB is not set +# CONFIG_MEDIA_USB_SUPPORT is not set +# CONFIG_MFD_DLN2 is not set +# CONFIG_MFD_VIPERBOARD is not set +# CONFIG_MISC_RTSX_USB is not set +# CONFIG_MMC_USHC is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MT7601U is not set +# CONFIG_MT7663U is not set +# CONFIG_MT76x0U is not set +# CONFIG_MT76x2U is not set +# CONFIG_PRISM2_USB is not set +# CONFIG_R8188EU is not set +# CONFIG_R8712U is not set +# CONFIG_RADIO_SHARK is not set +# CONFIG_RADIO_SHARK2 is not set +# CONFIG_RTL8187 is not set +# CONFIG_RTL8192CU is not set +# CONFIG_RTL8XXXU is not set +CONFIG_RTL_CARDS=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_LOGGING is not set +CONFIG_SCSI_LOWLEVEL=y +CONFIG_SCSI_PROC_FS=y +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +# CONFIG_SCSI_UFSHCD is not set +# CONFIG_SCSI_VIRTIO is not set +CONFIG_SG_POOL=y +# CONFIG_SND_BCD2000 is not set +CONFIG_SND_USB=y +# CONFIG_SND_USB_6FIRE is not set +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_CAIAQ is not set +# CONFIG_SND_USB_HIFACE is not set +# CONFIG_SND_USB_POD is not set +# CONFIG_SND_USB_PODHD is not set +# CONFIG_SND_USB_TONEPORT is not set +# CONFIG_SND_USB_UA101 is not set +# CONFIG_SND_USB_VARIAX is not set +# CONFIG_USBIP_CORE is not set +# CONFIG_USBPCWATCHDOG is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set +# CONFIG_USB_APPLEDISPLAY is not set +CONFIG_USB_AUTOSUSPEND_DELAY=2 +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CHAOSKEY is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +CONFIG_USB_DEFAULT_PERSIST=y +# CONFIG_USB_DSBR is not set +# CONFIG_USB_DUMMY_HCD is not set +# CONFIG_USB_DWC2_DUAL_ROLE is not set +# CONFIG_USB_DWC2_HOST is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_EHCI_FSL is not set +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +CONFIG_USB_EHCI_TT_NEWSCHED=y +# CONFIG_USB_EHSET_TEST_FIXTURE is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EZUSB_FX2 is not set +# CONFIG_USB_FEW_INIT_RETRIES is not set +# CONFIG_USB_FOTG210_HCD is not set +# CONFIG_USB_FTDI_ELAN is not set +CONFIG_USB_F_UAC1=y +CONFIG_USB_F_UAC2=y +# CONFIG_USB_HCD_TEST_MODE is not set +CONFIG_USB_HID=y +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_HSIC_USB3503 is not set +# CONFIG_USB_HSIC_USB4604 is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_HUB_USB251XB is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_KEENE is not set +# CONFIG_USB_LAN78XX is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_LEDS_TRIGGER_USBPORT is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LINK_LAYER_TEST is not set +# CONFIG_USB_MA901 is not set +# CONFIG_USB_MAX3421_HCD is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_MR800 is not set +CONFIG_USB_NET_DRIVERS=y +# CONFIG_USB_NET_RNDIS_WLAN is not set +# CONFIG_USB_OHCI_HCD is not set +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_DISABLE_EXTERNAL_HUB is not set +# CONFIG_USB_OTG_PRODUCTLIST is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_RAREMONO is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_RTL8152 is not set +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_SL811_HCD is not set +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_ENE_UB6250 is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_REALTEK is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_TMC is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_UAS is not set +# CONFIG_USB_USBNET is not set +CONFIG_USB_U_AUDIO=y +# CONFIG_USB_WDM is not set +# CONFIG_USB_XHCI_HCD is not set +# CONFIG_USB_YUREX is not set +# CONFIG_USB_ZD1201 is not set +CONFIG_VIDEOBUF2_CORE=m +CONFIG_VIDEOBUF2_MEMOPS=m +CONFIG_VIDEOBUF2_V4L2=m +CONFIG_VIDEOBUF2_VMALLOC=m +# CONFIG_VIRTIO_FS is not set +# CONFIG_VT6656 is not set +# CONFIG_ZD1211RW is not set From 801a58d3aeaae02ee35ad06aabf9c765546feeab Mon Sep 17 00:00:00 2001 From: Sandy Huang Date: Tue, 30 Jan 2024 19:31:03 +0800 Subject: [PATCH 21/22] drm/rockchip: lvds: register sub dev at rockchip lvds driver At lvds to other display output type product, the connector maybe register at third party drivers, the sub dev register often be forgot, so we add sub dev register at rockchip lvds driver. Signed-off-by: Sandy Huang Change-Id: If075041df8ddb1b269c903d092a6263160eff1db --- drivers/gpu/drm/rockchip/rockchip_lvds.c | 27 ++++++++++++++++-------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c index bf92a644f423..cb1eb805ff16 100644 --- a/drivers/gpu/drm/rockchip/rockchip_lvds.c +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c @@ -693,7 +693,8 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, struct rockchip_lvds *lvds = dev_get_drvdata(dev); struct drm_device *drm_dev = data; struct drm_encoder *encoder = &lvds->encoder; - struct drm_connector *connector = &lvds->connector; + struct drm_connector *connector = NULL; + struct rockchip_drm_private *private = drm_dev->dev_private; int ret; /* @@ -721,8 +722,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, drm_encoder_helper_add(encoder, &rockchip_lvds_encoder_helper_funcs); if (lvds->panel) { - struct rockchip_drm_private *private = drm_dev->dev_private; - + connector = &lvds->connector; ret = drm_connector_init(drm_dev, connector, &rockchip_lvds_connector_funcs, DRM_MODE_CONNECTOR_LVDS); @@ -750,19 +750,28 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, "failed to attach encoder: %d\n", ret); goto err_free_connector; } - - lvds->sub_dev.connector = &lvds->connector; - lvds->sub_dev.of_node = lvds->dev->of_node; - lvds->sub_dev.loader_protect = rockchip_lvds_encoder_loader_protect; - rockchip_drm_register_sub_dev(&lvds->sub_dev); - drm_object_attach_property(&connector->base, private->connector_id_prop, 0); } else { + struct list_head *connector_list; + ret = drm_bridge_attach(encoder, lvds->bridge, NULL, 0); if (ret) { DRM_DEV_ERROR(lvds->dev, "failed to attach bridge: %d\n", ret); goto err_free_encoder; } + connector_list = &lvds->bridge->dev->mode_config.connector_list; + + list_for_each_entry(connector, connector_list, head) + if (drm_connector_has_possible_encoder(connector, &lvds->encoder)) + break; + } + + if (connector) { + lvds->sub_dev.connector = connector; + lvds->sub_dev.of_node = lvds->dev->of_node; + lvds->sub_dev.loader_protect = rockchip_lvds_encoder_loader_protect; + drm_object_attach_property(&connector->base, private->connector_id_prop, lvds->id); + rockchip_drm_register_sub_dev(&lvds->sub_dev); } return 0; From 0d60f4f738ffc4b8241f4c71da0d03fda912f4d4 Mon Sep 17 00:00:00 2001 From: Cai YiWei Date: Wed, 31 Jan 2024 16:09:25 +0800 Subject: [PATCH 22/22] media: rockchip: isp: fix isp32 mp buf cfg fix commit e601ffeb040b ("media: rockchip: isp: dvbm buf support from rockit") Change-Id: I0ff6f7842cd7ac4dc75245bf72866d94f106f34c Signed-off-by: Cai YiWei --- drivers/media/platform/rockchip/isp/capture_v32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/rockchip/isp/capture_v32.c b/drivers/media/platform/rockchip/isp/capture_v32.c index 05de8743b707..e33313869ce1 100644 --- a/drivers/media/platform/rockchip/isp/capture_v32.c +++ b/drivers/media/platform/rockchip/isp/capture_v32.c @@ -752,7 +752,7 @@ static int mp_config_mi(struct rkisp_stream *stream) mi_frame_end_int_enable(stream); /* set up first buffer */ - if (dev->cap_dev.wrap_line && stream->dummy_buf.mem_priv) + if (!dev->cap_dev.wrap_line || stream->dummy_buf.mem_priv) mi_frame_end(stream, FRAME_INIT); rkisp_unite_write(dev, stream->config->mi.y_offs_cnt_init, 0, false);