From 00338cf8515d82cc22a86faef76f4320609a8607 Mon Sep 17 00:00:00 2001 From: Guochun Huang Date: Mon, 13 May 2024 16:18:55 +0800 Subject: [PATCH 01/17] drm/rockchip: dsi2: enable hs clk once the init cmds be sent by default the host processor to keep the HS serial clock running for the peripheral which may not generate its own clock to complete processing or pipeline movement of received data. Change-Id: If8f08662c4a520f3ae7c37ae57037f1760912b97 Signed-off-by: Guochun Huang --- .../gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c index f9d9dba3a1e9..afdcd5ad6fd4 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c @@ -243,7 +243,6 @@ struct dw_mipi_dsi2 { struct clk *pclk; struct clk *sys_clk; bool phy_enabled; - bool phy_request_clkhs; struct phy *dcphy; union phy_configure_opts phy_opts; @@ -832,10 +831,9 @@ static void dw_mipi_dsi2_pre_enable(struct dw_mipi_dsi2 *dsi2) dw_mipi_dsi2_pre_enable(dsi2->slave); } -static void dw_mipi_dsi2_enable(struct dw_mipi_dsi2 *dsi2) +static void dw_mipi_dsi2_clk_management(struct dw_mipi_dsi2 *dsi2) { - u32 clk_type, mode; - int ret; + u32 clk_type; /* * initial deskew calibration is send after phy_power_on, @@ -848,7 +846,14 @@ static void dw_mipi_dsi2_enable(struct dw_mipi_dsi2 *dsi2) regmap_update_bits(dsi2->regmap, DSI2_PHY_CLK_CFG, CLK_TYPE_MASK, clk_type); +} +static void dw_mipi_dsi2_enable(struct dw_mipi_dsi2 *dsi2) +{ + u32 mode; + int ret; + + dw_mipi_dsi2_clk_management(dsi2); dw_mipi_dsi2_ipi_set(dsi2); if (dsi2->auto_calc_mode) { @@ -1633,18 +1638,9 @@ static ssize_t dw_mipi_dsi2_transfer(struct dw_mipi_dsi2 *dsi2, u32 val; u32 mode; - if (msg->flags & MIPI_DSI_MSG_USE_LPM) { - regmap_update_bits(dsi2->regmap, DSI2_PHY_CLK_CFG, - CLK_TYPE_MASK, dsi2->phy_request_clkhs ? - CONTIUOUS_CLK : NON_CONTINUOUS_CLK); - regmap_update_bits(dsi2->regmap, DSI2_DSI_VID_TX_CFG, - LPDT_DISPLAY_CMD_EN, LPDT_DISPLAY_CMD_EN); - } else { - regmap_update_bits(dsi2->regmap, DSI2_PHY_CLK_CFG, - CLK_TYPE_MASK, CONTIUOUS_CLK); - regmap_update_bits(dsi2->regmap, DSI2_DSI_VID_TX_CFG, - LPDT_DISPLAY_CMD_EN, 0); - } + dw_mipi_dsi2_clk_management(dsi2); + regmap_update_bits(dsi2->regmap, DSI2_DSI_VID_TX_CFG, LPDT_DISPLAY_CMD_EN, + msg->flags & MIPI_DSI_MSG_USE_LPM ? LPDT_DISPLAY_CMD_EN : 0); /* create a packet to the DSI protocol */ ret = mipi_dsi_create_packet(&packet, msg); @@ -1732,9 +1728,6 @@ static int dw_mipi_dsi2_probe(struct platform_device *pdev) dsi2->pdata = of_device_get_match_data(dev); platform_set_drvdata(pdev, dsi2); - if (device_property_read_bool(dev, "phy-request-clkhs")) - dsi2->phy_request_clkhs = true; - if (device_property_read_bool(dev, "auto-calculation-mode")) dsi2->auto_calc_mode = true; From 67fd88dac59f2fc1f4328b036bc77bae190f7eb6 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Wed, 15 May 2024 15:34:39 +0800 Subject: [PATCH 02/17] arm64: dts: rockchip: rk3576: reduce DS of pwm0/pwm2 pins to level2 According to the SI test, reduce the drive strength of pwm0/pwm2 pins to level2 in order to meet the signal metrics. Change-Id: I18b6855efd7f97096f9ee85113ef470627436615 Signed-off-by: Damon Ding --- .../boot/dts/rockchip/rk3576-pinctrl.dtsi | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3576-pinctrl.dtsi b/arch/arm64/boot/dts/rockchip/rk3576-pinctrl.dtsi index 9850c6fde722..0b0851a7e4ea 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-pinctrl.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3576-pinctrl.dtsi @@ -2541,56 +2541,56 @@ pwm0m0_ch0: pwm0m0-ch0 { rockchip,pins = /* pwm0_ch0_m0 */ - <0 RK_PC4 12 &pcfg_pull_none>; + <0 RK_PC4 12 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm0m0_ch1: pwm0m0-ch1 { rockchip,pins = /* pwm0_ch1_m0 */ - <0 RK_PC3 12 &pcfg_pull_none>; + <0 RK_PC3 12 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm0m1_ch0: pwm0m1-ch0 { rockchip,pins = /* pwm0_ch0_m1 */ - <1 RK_PC0 13 &pcfg_pull_none>; + <1 RK_PC0 13 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm0m1_ch1: pwm0m1-ch1 { rockchip,pins = /* pwm0_ch1_m1 */ - <4 RK_PC1 14 &pcfg_pull_none>; + <4 RK_PC1 14 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm0m2_ch0: pwm0m2-ch0 { rockchip,pins = /* pwm0_ch0_m2 */ - <2 RK_PC3 13 &pcfg_pull_none>; + <2 RK_PC3 13 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm0m2_ch1: pwm0m2-ch1 { rockchip,pins = /* pwm0_ch1_m2 */ - <2 RK_PC7 13 &pcfg_pull_none>; + <2 RK_PC7 13 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm0m3_ch0: pwm0m3-ch0 { rockchip,pins = /* pwm0_ch0_m3 */ - <3 RK_PB0 12 &pcfg_pull_none>; + <3 RK_PB0 12 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm0m3_ch1: pwm0m3-ch1 { rockchip,pins = /* pwm0_ch1_m3 */ - <3 RK_PB6 12 &pcfg_pull_none>; + <3 RK_PB6 12 &pcfg_pull_none_drv_level_2>; }; }; @@ -2769,224 +2769,224 @@ pwm2m0_ch0: pwm2m0-ch0 { rockchip,pins = /* pwm2_ch0_m0 */ - <0 RK_PD3 12 &pcfg_pull_none>; + <0 RK_PD3 12 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m0_ch1: pwm2m0-ch1 { rockchip,pins = /* pwm2_ch1_m0 */ - <1 RK_PB3 12 &pcfg_pull_none>; + <1 RK_PB3 12 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m0_ch2: pwm2m0-ch2 { rockchip,pins = /* pwm2_ch2_m0 */ - <2 RK_PA0 14 &pcfg_pull_none>; + <2 RK_PA0 14 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m0_ch3: pwm2m0-ch3 { rockchip,pins = /* pwm2_ch3_m0 */ - <2 RK_PA1 14 &pcfg_pull_none>; + <2 RK_PA1 14 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m0_ch4: pwm2m0-ch4 { rockchip,pins = /* pwm2_ch4_m0 */ - <2 RK_PA4 14 &pcfg_pull_none>; + <2 RK_PA4 14 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m0_ch5: pwm2m0-ch5 { rockchip,pins = /* pwm2_ch5_m0 */ - <4 RK_PA2 13 &pcfg_pull_none>; + <4 RK_PA2 13 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m0_ch6: pwm2m0-ch6 { rockchip,pins = /* pwm2_ch6_m0 */ - <4 RK_PA7 13 &pcfg_pull_none>; + <4 RK_PA7 13 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m0_ch7: pwm2m0-ch7 { rockchip,pins = /* pwm2_ch7_m0 */ - <4 RK_PB3 13 &pcfg_pull_none>; + <4 RK_PB3 13 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m1_ch0: pwm2m1-ch0 { rockchip,pins = /* pwm2_ch0_m1 */ - <4 RK_PC2 14 &pcfg_pull_none>; + <4 RK_PC2 14 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m1_ch1: pwm2m1-ch1 { rockchip,pins = /* pwm2_ch1_m1 */ - <4 RK_PC3 14 &pcfg_pull_none>; + <4 RK_PC3 14 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m1_ch2: pwm2m1-ch2 { rockchip,pins = /* pwm2_ch2_m1 */ - <4 RK_PC6 14 &pcfg_pull_none>; + <4 RK_PC6 14 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m1_ch3: pwm2m1-ch3 { rockchip,pins = /* pwm2_ch3_m1 */ - <4 RK_PC7 14 &pcfg_pull_none>; + <4 RK_PC7 14 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m1_ch4: pwm2m1-ch4 { rockchip,pins = /* pwm2_ch4_m1 */ - <4 RK_PA3 13 &pcfg_pull_none>; + <4 RK_PA3 13 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m1_ch5: pwm2m1-ch5 { rockchip,pins = /* pwm2_ch5_m1 */ - <4 RK_PC5 14 &pcfg_pull_none>; + <4 RK_PC5 14 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m1_ch6: pwm2m1-ch6 { rockchip,pins = /* pwm2_ch6_m1 */ - <4 RK_PC4 14 &pcfg_pull_none>; + <4 RK_PC4 14 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m1_ch7: pwm2m1-ch7 { rockchip,pins = /* pwm2_ch7_m1 */ - <1 RK_PB1 12 &pcfg_pull_none>; + <1 RK_PB1 12 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m2_ch0: pwm2m2-ch0 { rockchip,pins = /* pwm2_ch0_m2 */ - <2 RK_PD0 13 &pcfg_pull_none>; + <2 RK_PD0 13 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m2_ch1: pwm2m2-ch1 { rockchip,pins = /* pwm2_ch1_m2 */ - <2 RK_PD1 13 &pcfg_pull_none>; + <2 RK_PD1 13 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m2_ch2: pwm2m2-ch2 { rockchip,pins = /* pwm2_ch2_m2 */ - <2 RK_PD2 13 &pcfg_pull_none>; + <2 RK_PD2 13 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m2_ch3: pwm2m2-ch3 { rockchip,pins = /* pwm2_ch3_m2 */ - <2 RK_PD3 13 &pcfg_pull_none>; + <2 RK_PD3 13 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m2_ch4: pwm2m2-ch4 { rockchip,pins = /* pwm2_ch4_m2 */ - <2 RK_PD4 13 &pcfg_pull_none>; + <2 RK_PD4 13 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m2_ch5: pwm2m2-ch5 { rockchip,pins = /* pwm2_ch5_m2 */ - <2 RK_PD5 13 &pcfg_pull_none>; + <2 RK_PD5 13 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m2_ch6: pwm2m2-ch6 { rockchip,pins = /* pwm2_ch6_m2 */ - <2 RK_PD6 13 &pcfg_pull_none>; + <2 RK_PD6 13 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m2_ch7: pwm2m2-ch7 { rockchip,pins = /* pwm2_ch7_m2 */ - <2 RK_PD7 13 &pcfg_pull_none>; + <2 RK_PD7 13 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m3_ch0: pwm2m3-ch0 { rockchip,pins = /* pwm2_ch0_m3 */ - <3 RK_PC2 12 &pcfg_pull_none>; + <3 RK_PC2 12 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m3_ch1: pwm2m3-ch1 { rockchip,pins = /* pwm2_ch1_m3 */ - <3 RK_PC3 12 &pcfg_pull_none>; + <3 RK_PC3 12 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m3_ch2: pwm2m3-ch2 { rockchip,pins = /* pwm2_ch2_m3 */ - <3 RK_PC5 12 &pcfg_pull_none>; + <3 RK_PC5 12 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m3_ch3: pwm2m3-ch3 { rockchip,pins = /* pwm2_ch3_m3 */ - <3 RK_PD0 12 &pcfg_pull_none>; + <3 RK_PD0 12 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m3_ch4: pwm2m3-ch4 { rockchip,pins = /* pwm2_ch4_m3 */ - <3 RK_PD2 12 &pcfg_pull_none>; + <3 RK_PD2 12 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m3_ch5: pwm2m3-ch5 { rockchip,pins = /* pwm2_ch5_m3 */ - <3 RK_PD3 12 &pcfg_pull_none>; + <3 RK_PD3 12 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m3_ch6: pwm2m3-ch6 { rockchip,pins = /* pwm2_ch6_m3 */ - <3 RK_PD6 12 &pcfg_pull_none>; + <3 RK_PD6 12 &pcfg_pull_none_drv_level_2>; }; /omit-if-no-ref/ pwm2m3_ch7: pwm2m3-ch7 { rockchip,pins = /* pwm2_ch7_m3 */ - <3 RK_PD7 12 &pcfg_pull_none>; + <3 RK_PD7 12 &pcfg_pull_none_drv_level_2>; }; }; From efba86d9834d71885418577d1c89c9b9aa456e5f Mon Sep 17 00:00:00 2001 From: Algea Cao Date: Tue, 14 May 2024 17:46:45 +0800 Subject: [PATCH 03/17] drm/rockchip: dw_hdmi: Fix some TVS all resolutions are filtered Some TVS edid max_tmds_clock are 0, we think it only support hdmi1.4 resolutions. Fixes: c1dcc6ec1ede ("drm/rockchip: dw_hdmi: Fix hdmi cts hf1-31 failed") Change-Id: Iea8b182d869fb2722701abd77ff65d7f4cf76518 Signed-off-by: Algea Cao --- drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 223f77d167e1..cd647f28a73b 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -1830,8 +1830,14 @@ dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data, hdmi = to_rockchip_hdmi(encoder); if (!hdmi->skip_check_420_mode) { + u32 max_tmds_clock = connector->display_info.max_tmds_clock; + + /* some sinks edid max_tmds_clocks are 0, we think it only support hdmi1.4 */ + if (!connector->display_info.max_tmds_clock) + max_tmds_clock = 340000; + /* edid isn't support yuv420 and max_tmds_clock is less than mode pixel clk */ - if (mode->clock < 600000 && connector->display_info.max_tmds_clock < mode->clock && + if (mode->clock < 600000 && max_tmds_clock < mode->clock && (!drm_mode_is_420(&connector->display_info, mode) || !connector->ycbcr_420_allowed)) return MODE_BAD; @@ -1846,7 +1852,7 @@ dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data, * exceeds the max_tmds_clock of edid. */ if (drm_mode_is_420(&connector->display_info, mode) && - connector->display_info.max_tmds_clock < (mode->clock / 2) && + max_tmds_clock < (mode->clock / 2) && is_hdmi2_mode(mode)) return MODE_BAD; }; From 38e425f2a348e262053d8a753b802d71087445cc Mon Sep 17 00:00:00 2001 From: Chandler Chen Date: Tue, 7 May 2024 09:27:31 +0800 Subject: [PATCH 04/17] video: rockchip: mpp: add vdpu383 int_err_mask Change-Id: Ie9b112f55427b20242148021baf55c054a0f53e5 Signed-off-by: Chandler Chen --- drivers/video/rockchip/mpp/mpp_rkvdec2.c | 4 ++-- drivers/video/rockchip/mpp/mpp_rkvdec2.h | 4 ---- drivers/video/rockchip/mpp/mpp_rkvdec2_link.c | 12 ++++++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/video/rockchip/mpp/mpp_rkvdec2.c b/drivers/video/rockchip/mpp/mpp_rkvdec2.c index 1d8eae761489..f94b23e2401b 100644 --- a/drivers/video/rockchip/mpp/mpp_rkvdec2.c +++ b/drivers/video/rockchip/mpp/mpp_rkvdec2.c @@ -588,6 +588,7 @@ static int rkvdec2_isr(struct mpp_dev *mpp) struct rkvdec2_task *task = NULL; struct mpp_task *mpp_task = mpp->cur_task; struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); + struct rkvdec_link_info *link_info = mpp->var->hw_info->link_info; /* FIXME use a spin lock here */ if (!mpp_task) { @@ -601,8 +602,7 @@ static int rkvdec2_isr(struct mpp_dev *mpp) task->irq_status = mpp->irq_status; mpp_debug(DEBUG_IRQ_STATUS, "irq_status: %08x\n", task->irq_status); - err_mask = RKVDEC_COLMV_REF_ERR_STA | RKVDEC_BUF_EMPTY_STA | - RKVDEC_TIMEOUT_STA | RKVDEC_ERROR_STA; + err_mask = link_info->err_mask; if (err_mask & task->irq_status) { atomic_inc(&mpp->reset_request); if (mpp_debug_unlikely(DEBUG_DUMP_ERR_REG)) { diff --git a/drivers/video/rockchip/mpp/mpp_rkvdec2.h b/drivers/video/rockchip/mpp/mpp_rkvdec2.h index 90a795b39e84..1f91290ba604 100644 --- a/drivers/video/rockchip/mpp/mpp_rkvdec2.h +++ b/drivers/video/rockchip/mpp/mpp_rkvdec2.h @@ -67,10 +67,6 @@ #define RKVDEC_READY_STA BIT(2) #define RKVDEC_IRQ_RAW BIT(1) #define RKVDEC_IRQ BIT(0) -#define RKVDEC_INT_ERROR_MASK (RKVDEC_COLMV_REF_ERR_STA |\ - RKVDEC_BUF_EMPTY_STA |\ - RKVDEC_TIMEOUT_STA |\ - RKVDEC_ERROR_STA) #define RKVDEC_PERF_WORKING_CNT 0x41c /* perf sel reference register */ diff --git a/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c b/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c index 708708e7f350..2d154bab1bb4 100644 --- a/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c +++ b/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c @@ -79,6 +79,7 @@ struct rkvdec_link_info rkvdec_link_v2_hw_info = { }, .irq_base = 0x00, .next_addr_base = 0x1c, + .err_mask = 0xf0, }; /* vdpu34x link hw info for rk356x */ @@ -140,6 +141,7 @@ struct rkvdec_link_info rkvdec_link_rk356x_hw_info = { }, .irq_base = 0x00, .next_addr_base = 0x1c, + .err_mask = 0xf0, }; /* vdpu382 link hw info */ @@ -201,6 +203,7 @@ struct rkvdec_link_info rkvdec_link_vdpu382_hw_info = { }, .irq_base = 0x00, .next_addr_base = 0x1c, + .err_mask = 0xf0, }; /* vdpu383 link hw info */ @@ -1102,7 +1105,7 @@ static void rkvdec2_link_try_dequeue(struct mpp_dev *mpp) mpp_task->session->index, mpp_task->task_index, irq_status, timeout_flag, abort_flag); - if (irq_status & RKVDEC_INT_ERROR_MASK) { + if (irq_status & info->err_mask) { dev_err(mpp->dev, "session %d task %d irq_status %#08x timeout %u abort %u\n", mpp_task->session->index, mpp_task->task_index, @@ -1851,10 +1854,11 @@ irqreturn_t rkvdec2_soft_ccu_irq(int irq, void *param) { struct mpp_dev *mpp = param; u32 irq_status = mpp_read_relaxed(mpp, RKVDEC_REG_INT_EN); + struct rkvdec_link_info *link_info = mpp->var->hw_info->link_info; if (irq_status & RKVDEC_IRQ_RAW) { mpp_debug(DEBUG_IRQ_STATUS, "irq_status=%08x\n", irq_status); - if (irq_status & RKVDEC_INT_ERROR_MASK) { + if (irq_status & link_info->err_mask) { atomic_inc(&mpp->reset_request); atomic_inc(&mpp->queue->reset_request); } @@ -2214,7 +2218,7 @@ static int rkvdec2_hard_ccu_dequeue(struct mpp_taskqueue *queue, mpp_debug(DEBUG_IRQ_CHECK, "session %d task %d w:h[%d %d] err %d irq_status %#x timeout=%u abort=%u iova %08x next %08x ccu[%d %d]\n", mpp_task->session->index, mpp_task->task_index, task->width, - task->height, !!(irq_status & RKVDEC_INT_ERROR_MASK), irq_status, + task->height, !!(irq_status & hw->err_mask), irq_status, timeout_flag, abort_flag, (u32)task->table->iova, ((u32 *)task->table->vaddr)[hw->tb_reg_next], ccu_decoded_num, ccu_total_dec_num); @@ -2251,7 +2255,7 @@ static int rkvdec2_hard_ccu_dequeue(struct mpp_taskqueue *queue, list_del_init(&mpp_task->queue_link); /* Wake up the GET thread */ wake_up(&mpp_task->wait); - if ((irq_status & RKVDEC_INT_ERROR_MASK) || timeout_flag) { + if ((irq_status & hw->err_mask) || timeout_flag) { pr_err("session %d task %d irq_status %#x timeout=%u abort=%u\n", mpp_task->session->index, mpp_task->task_index, irq_status, timeout_flag, abort_flag); From e5c8e1cc2f04b9adb2a45f3207eeb925214daf86 Mon Sep 17 00:00:00 2001 From: Jason Zhang Date: Wed, 8 May 2024 20:30:06 +0800 Subject: [PATCH 05/17] media: i2c: rk628: add attributes for enabling HDMI ARC This patch adds attributes for enabling HDMI ARC. Signed-off-by: Jason Zhang Change-Id: Iec7e1169efc65f298e5b5449e3803151d91c7258 --- drivers/media/i2c/rk628/rk628_csi_v4l2.c | 36 ++++++++++++++++++++++++ drivers/media/i2c/rk628/rk628_hdmirx.c | 23 +++++++++++++++ drivers/media/i2c/rk628/rk628_hdmirx.h | 2 ++ 3 files changed, 61 insertions(+) diff --git a/drivers/media/i2c/rk628/rk628_csi_v4l2.c b/drivers/media/i2c/rk628/rk628_csi_v4l2.c index 5a81de8e14c6..8d33f3b28ae2 100644 --- a/drivers/media/i2c/rk628/rk628_csi_v4l2.c +++ b/drivers/media/i2c/rk628/rk628_csi_v4l2.c @@ -3132,12 +3132,48 @@ static ssize_t audio_present_show(struct device *dev, rk628_hdmirx_audio_present(csi->audio_info) : 0); } +static ssize_t arc_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rk628_csi *csi = dev_get_drvdata(dev); + struct rk_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev); + + if (!hdmirx_dev) + return -EINVAL; + + return snprintf(buf, PAGE_SIZE, "%d\n", + rk628_hdmirx_get_arc_enable(csi->audio_info) ? 1 : 0); +} + +static ssize_t arc_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rk628_csi *csi = dev_get_drvdata(dev); + struct rk_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev); + bool enabled; + int ret; + + if (!hdmirx_dev) + return -EINVAL; + + ret = kstrtobool(buf, &enabled); + if (ret) + return ret; + + rk628_hdmirx_set_arc_enable(csi->audio_info, enabled); + + return count; +} + static DEVICE_ATTR_RO(audio_rate); static DEVICE_ATTR_RO(audio_present); +static DEVICE_ATTR_RW(arc_enable); static struct attribute *rk628_attrs[] = { &dev_attr_audio_rate.attr, &dev_attr_audio_present.attr, + &dev_attr_arc_enable.attr, NULL }; ATTRIBUTE_GROUPS(rk628); diff --git a/drivers/media/i2c/rk628/rk628_hdmirx.c b/drivers/media/i2c/rk628/rk628_hdmirx.c index ef69600a5340..72a0ddada9d2 100644 --- a/drivers/media/i2c/rk628/rk628_hdmirx.c +++ b/drivers/media/i2c/rk628/rk628_hdmirx.c @@ -42,6 +42,7 @@ struct rk628_audioinfo { bool fifo_ints_en; bool ctsn_ints_en; bool audio_present; + bool arc_en; struct device *dev; }; @@ -639,6 +640,28 @@ int rk628_hdmirx_audio_fs(HAUDINFO info) } EXPORT_SYMBOL(rk628_hdmirx_audio_fs); +bool rk628_hdmirx_get_arc_enable(HAUDINFO info) +{ + struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info; + + if (!aif) + return false; + + return aif->arc_en; +} +EXPORT_SYMBOL(rk628_hdmirx_get_arc_enable); + +int rk628_hdmirx_set_arc_enable(HAUDINFO info, bool enabled) +{ + struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info; + + if (!aif) + return false; + + return aif->arc_en = enabled; +} +EXPORT_SYMBOL(rk628_hdmirx_set_arc_enable); + void rk628_hdmirx_audio_i2s_ctrl(HAUDINFO info, bool enable) { struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info; diff --git a/drivers/media/i2c/rk628/rk628_hdmirx.h b/drivers/media/i2c/rk628/rk628_hdmirx.h index 1f0a302a75a8..15538e8ab6bf 100644 --- a/drivers/media/i2c/rk628/rk628_hdmirx.h +++ b/drivers/media/i2c/rk628/rk628_hdmirx.h @@ -504,6 +504,8 @@ void rk628_hdmirx_audio_cancel_work_rate_change(HAUDINFO info, bool sync); bool rk628_hdmirx_audio_present(HAUDINFO info); int rk628_hdmirx_audio_fs(HAUDINFO info); void rk628_hdmirx_audio_i2s_ctrl(HAUDINFO info, bool enable); +bool rk628_hdmirx_get_arc_enable(HAUDINFO info); +int rk628_hdmirx_set_arc_enable(HAUDINFO info, bool enabled); /* for audio isr process */ bool rk628_audio_fifoints_enabled(HAUDINFO info); From 8008e850587436a556223ee4fcb400158f3d876a Mon Sep 17 00:00:00 2001 From: Jason Zhang Date: Wed, 8 May 2024 20:31:17 +0800 Subject: [PATCH 06/17] media: i2c: rk628: add workaround for HDMI ARC The HPD should not be triggered if the HDMI ARC is enabled. Signed-off-by: Jason Zhang Change-Id: If4d74ea9d72a4295a054ab0be45454a0914ab35b --- drivers/media/i2c/rk628/rk628_csi_v4l2.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/i2c/rk628/rk628_csi_v4l2.c b/drivers/media/i2c/rk628/rk628_csi_v4l2.c index 8d33f3b28ae2..9555ad2163b5 100644 --- a/drivers/media/i2c/rk628/rk628_csi_v4l2.c +++ b/drivers/media/i2c/rk628/rk628_csi_v4l2.c @@ -494,6 +494,9 @@ static void rk628_hdmirx_config_all(struct v4l2_subdev *sd) } if (ret < 0 || rk628_hdmirx_scdc_ced_err(csi->rk628)) { + if (rk628_hdmirx_get_arc_enable(csi->audio_info)) + return; + rk628_hdmirx_plugout(sd); csi->lock_fail_time++; v4l2_dbg(1, debug, sd, "%s: lock fail time: %d\n", From b22ce737d2558ee499ed16ac91a356101de4dcac Mon Sep 17 00:00:00 2001 From: Finley Xiao Date: Thu, 9 May 2024 10:10:51 +0800 Subject: [PATCH 07/17] soc: rockchip: csu: Implement rockchip_csu_get_bus() Change-Id: Ibe9d569348b19b4bad1aed3d810a3160aa5c7988 Signed-off-by: Finley Xiao --- drivers/soc/rockchip/rockchip_csu.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/soc/rockchip/rockchip_csu.c b/drivers/soc/rockchip/rockchip_csu.c index 04a6a5b39715..a042b59bcc22 100644 --- a/drivers/soc/rockchip/rockchip_csu.c +++ b/drivers/soc/rockchip/rockchip_csu.c @@ -36,6 +36,21 @@ struct rockchip_csu { static struct rockchip_csu *rk_csu; static DEFINE_MUTEX(csu_lock); +static struct csu_bus *rockchip_csu_get_bus(unsigned int bus_id) +{ + int i; + + if (!rk_csu || !rk_csu->bus) + return NULL; + + for (i = 0; i < rk_csu->bus_cnt; i++) { + if (bus_id == rk_csu->bus[i].id) + return &rk_csu->bus[i]; + } + + return NULL; +} + static int rockchip_csu_sip_config(struct device *dev, u32 bus_id, u32 cfg, u32 enable_msk) { @@ -91,7 +106,7 @@ static int csu_disable(struct csu_clk *clk, bool disable) return 0; if (clk->bus_id >= rk_csu->bus_cnt) return 0; - bus = &rk_csu->bus[clk->bus_id]; + bus = rockchip_csu_get_bus(clk->bus_id); if (!bus) return 0; @@ -138,7 +153,7 @@ int rockchip_csu_set_div(struct csu_clk *clk, unsigned int div) return 0; if (clk->bus_id >= rk_csu->bus_cnt) return 0; - bus = &rk_csu->bus[clk->bus_id]; + bus = rockchip_csu_get_bus(clk->bus_id); if (!bus) return 0; From dfa65b17b167019540b21d01e7464e98151199ed Mon Sep 17 00:00:00 2001 From: Chen Shunqing Date: Wed, 8 May 2024 11:29:08 +0800 Subject: [PATCH 08/17] media: i2c: rk628: fix problem of loop pulling down and up cec hpd Signed-off-by: Chen Shunqing Change-Id: I4fdb6d95269b836d664e3530424f1aaf54fb0a64 --- drivers/media/i2c/rk628/rk628_bt1120_v4l2.c | 2 +- drivers/media/i2c/rk628/rk628_csi_v4l2.c | 2 +- drivers/media/i2c/rk628/rk628_hdmirx.c | 24 ++++++++++++++++++++- drivers/media/i2c/rk628/rk628_hdmirx.h | 2 ++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c b/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c index 5072cc41288a..8f60f377a833 100644 --- a/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c +++ b/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c @@ -513,7 +513,7 @@ static void rk628_hdmirx_hpd_ctrl(struct v4l2_subdev *sd, bool en) HOT_PLUG_DETECT_MASK, HOT_PLUG_DETECT(set_level)); if (bt1120->cec_enable && bt1120->cec) - rk628_hdmirx_cec_hpd(bt1120->cec, en); + rk628_hdmirx_cec_hpd(bt1120->cec, tx_5v_power_present(sd)); } static int rk628_bt1120_s_ctrl_detect_tx_5v(struct v4l2_subdev *sd) diff --git a/drivers/media/i2c/rk628/rk628_csi_v4l2.c b/drivers/media/i2c/rk628/rk628_csi_v4l2.c index 9555ad2163b5..bb4a2d24b84d 100644 --- a/drivers/media/i2c/rk628/rk628_csi_v4l2.c +++ b/drivers/media/i2c/rk628/rk628_csi_v4l2.c @@ -626,7 +626,7 @@ static void rk628_hdmirx_hpd_ctrl(struct v4l2_subdev *sd, bool en) HOT_PLUG_DETECT_MASK, HOT_PLUG_DETECT(set_level)); if (csi->cec_enable && csi->cec) - rk628_hdmirx_cec_hpd(csi->cec, en); + rk628_hdmirx_cec_hpd(csi->cec, tx_5v_power_present(sd)); } diff --git a/drivers/media/i2c/rk628/rk628_hdmirx.c b/drivers/media/i2c/rk628/rk628_hdmirx.c index 72a0ddada9d2..d397324eab4f 100644 --- a/drivers/media/i2c/rk628/rk628_hdmirx.c +++ b/drivers/media/i2c/rk628/rk628_hdmirx.c @@ -1050,6 +1050,17 @@ void rk628_hdmirx_cec_irq(struct rk628 *rk628, struct rk628_hdmirx_cec *cec) } EXPORT_SYMBOL(rk628_hdmirx_cec_irq); +static void rk628_delayed_work_cec(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct rk628_hdmirx_cec *cec = container_of(dwork, struct rk628_hdmirx_cec, + delayed_work_cec); + bool en = rk628_hdmirx_tx_5v_power_detect(cec->rk628->hdmirx_det_gpio); + + cec->cec_hpd = en; + cec_queue_pin_hpd_event(cec->adap, en, ktime_get()); +} + struct rk628_hdmirx_cec *rk628_hdmirx_cec_register(struct rk628 *rk628) { struct rk628_hdmirx_cec *cec; @@ -1091,6 +1102,8 @@ struct rk628_hdmirx_cec *rk628_hdmirx_cec_register(struct rk628 *rk628) /* override the module pointer */ cec->adap->owner = THIS_MODULE; + INIT_DELAYED_WORK(&cec->delayed_work_cec, rk628_delayed_work_cec); + ret = devm_add_action(cec->dev, rk628_hdmirx_cec_del, cec); if (ret) { cec_delete_adapter(cec->adap); @@ -1125,6 +1138,8 @@ struct rk628_hdmirx_cec *rk628_hdmirx_cec_register(struct rk628 *rk628) */ devm_remove_action(cec->dev, rk628_hdmirx_cec_del, cec); + schedule_delayed_work(&cec->delayed_work_cec, msecs_to_jiffies(10000)); + return cec; } EXPORT_SYMBOL(rk628_hdmirx_cec_register); @@ -1144,7 +1159,11 @@ void rk628_hdmirx_cec_hpd(struct rk628_hdmirx_cec *cec, bool en) if (!cec || !cec->adap) return; - cec_queue_pin_hpd_event(cec->adap, en, ktime_get()); + rk628_dbg(cec->rk628, "%s: cec_hpd:%d, en:%d\n", __func__, cec->cec_hpd, en); + if (cec->cec_hpd != en) { + cec->cec_hpd = en; + cec_queue_pin_hpd_event(cec->adap, en, ktime_get()); + } } EXPORT_SYMBOL(rk628_hdmirx_cec_hpd); @@ -1154,6 +1173,9 @@ void rk628_hdmirx_cec_state_reconfiguration(struct rk628 *rk628, unsigned int irqs; u32 val; + /* clk_hdmirx_cec = 32.768k */ + rk628_clk_set_rate(rk628, CGU_CLK_HDMIRX_CEC, 32768); + rk628_i2c_write(rk628, HDMI_RX_CEC_ADDR_L, cec->addresses & 0xff); rk628_i2c_write(rk628, HDMI_RX_CEC_ADDR_H, (cec->addresses >> 8) & 0xff); diff --git a/drivers/media/i2c/rk628/rk628_hdmirx.h b/drivers/media/i2c/rk628/rk628_hdmirx.h index 15538e8ab6bf..f979fa0f5e6c 100644 --- a/drivers/media/i2c/rk628/rk628_hdmirx.h +++ b/drivers/media/i2c/rk628/rk628_hdmirx.h @@ -486,7 +486,9 @@ struct rk628_hdmirx_cec { unsigned int tx_status; bool tx_done; bool rx_done; + bool cec_hpd; struct cec_notifier *notify; + struct delayed_work delayed_work_cec; }; void rk628_hdmirx_set_hdcp(struct rk628 *rk628, struct rk628_hdcp *hdcp, bool en); From e042ab4129d6bcce67354ba783db94fac36b92ee Mon Sep 17 00:00:00 2001 From: Chen Shunqing Date: Wed, 8 May 2024 13:57:15 +0800 Subject: [PATCH 09/17] media: i2c: rk628: fix background not be disabled Signed-off-by: Chen Shunqing Change-Id: I9ac01158cddf71a46c7f91399fdc19da1bb79c92 --- drivers/media/i2c/rk628/rk628_bt1120_v4l2.c | 103 ++++++++----- drivers/media/i2c/rk628/rk628_csi_v4l2.c | 163 +++++++++++++------- 2 files changed, 172 insertions(+), 94 deletions(-) diff --git a/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c b/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c index 8f60f377a833..e6de0e5d3a71 100644 --- a/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c +++ b/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c @@ -251,7 +251,7 @@ static bool tx_5v_power_present(struct v4l2_subdev *sd) struct rk628_bt1120 *bt1120 = to_bt1120(sd); ret = rk628_hdmirx_tx_5v_power_detect(bt1120->plugin_det_gpio); - v4l2_dbg(1, debug, sd, "%s: %d\n", __func__, ret); + v4l2_dbg(2, debug, sd, "%s: %d\n", __func__, ret); return ret; } @@ -464,6 +464,7 @@ static void rk628_delayed_work_res_change(struct work_struct *work) bool plugin; mutex_lock(&bt1120->confctl_mutex); + rk628_set_bg_enable(bt1120->rk628, false); enable_stream(sd, false); bt1120->nosignal = true; bt1120->avi_rcv_rdy = false; @@ -950,16 +951,23 @@ static void rk628_bt1120_clear_hdmirx_interrupts(struct v4l2_subdev *sd) { struct rk628_bt1120 *bt1120 = to_bt1120(sd); - /* clear interrupts */ - rk628_i2c_write(bt1120->rk628, HDMI_RX_MD_ICLR, 0xffffffff); - rk628_i2c_write(bt1120->rk628, HDMI_RX_PDEC_ICLR, 0xffffffff); if (bt1120->rk628->version >= RK628F_VERSION) rk628_i2c_write(bt1120->rk628, GRF_INTR0_CLR_EN, 0x02000200); else rk628_i2c_write(bt1120->rk628, GRF_INTR0_CLR_EN, 0x01000100); } -static int rk628_bt1120_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +static int rk628_is_general_isr(struct rk628_bt1120 *bt1120, u32 md_ints, u32 pdec_ints) +{ + if (rk628_hdmirx_is_signal_change_ists(bt1120->rk628, md_ints, pdec_ints)) + return 1; + if ((pdec_ints & AVI_RCV_ISTS) && !bt1120->avi_rcv_rdy) + return 1; + + return 0; +} + +static int rk628_hdmirx_general_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { struct rk628_bt1120 *bt1120 = to_bt1120(sd); u32 md_ints, pdec_ints, fifo_ints, hact, vact; @@ -972,11 +980,25 @@ static int rk628_bt1120_isr(struct v4l2_subdev *sd, u32 status, bool *handled) return -EINVAL; } + if (!bt1120->vid_ints_en) + return 0; + rk628_i2c_read(bt1120->rk628, GRF_INTR0_STATUS, &int0_status); + if (!(int0_status & (BIT(8) | BIT(9)))) + return 0; + v4l2_dbg(1, debug, sd, "%s: int0 status: 0x%x\n", __func__, int0_status); rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_ISTS, &md_ints); rk628_i2c_read(bt1120->rk628, HDMI_RX_PDEC_ISTS, &pdec_ints); + + /* clear interrupts */ + rk628_i2c_write(bt1120->rk628, HDMI_RX_MD_ICLR, 0xffffffff); + rk628_i2c_write(bt1120->rk628, HDMI_RX_PDEC_ICLR, 0xffffffff); + + if (!rk628_is_general_isr(bt1120, md_ints, pdec_ints)) + return 0; + if (bt1120->rk628->version >= RK628F_VERSION && rk628_hdmirx_is_signal_change_ists(bt1120->rk628, md_ints, pdec_ints)) rk628_set_bg_enable(bt1120->rk628, true); @@ -1003,37 +1025,34 @@ static int rk628_bt1120_isr(struct v4l2_subdev *sd, u32 status, bool *handled) } } } - if (bt1120->vid_ints_en) { - v4l2_dbg(1, debug, sd, "%s: md_ints: %#x, pdec_ints:%#x, plugin: %d\n", - __func__, md_ints, pdec_ints, plugin); + v4l2_dbg(1, debug, sd, "%s: md_ints: %#x, pdec_ints:%#x, plugin: %d\n", + __func__, md_ints, pdec_ints, plugin); - if (rk628_hdmirx_is_signal_change_ists(bt1120->rk628, md_ints, pdec_ints)) { + if (rk628_hdmirx_is_signal_change_ists(bt1120->rk628, md_ints, pdec_ints)) { + rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_HACT_PX, &hact); + rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_VAL, &vact); + v4l2_dbg(1, debug, sd, "%s: HACT:%#x, VACT:%#x\n", + __func__, hact, vact); - rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_HACT_PX, &hact); - rk628_i2c_read(bt1120->rk628, HDMI_RX_MD_VAL, &vact); - v4l2_dbg(1, debug, sd, "%s: HACT:%#x, VACT:%#x\n", - __func__, hact, vact); - - rk628_bt1120_enable_interrupts(sd, false); - if (bt1120->rk628->version < RK628F_VERSION) { - enable_stream(sd, false); - bt1120->nosignal = true; - } - schedule_delayed_work(&bt1120->delayed_work_res_change, HZ / 2); - - v4l2_dbg(1, debug, sd, "%s: hact/vact change, md_ints: %#x\n", - __func__, (u32)(md_ints & (VACT_LIN_ISTS | HACT_PIX_ISTS))); - *handled = true; + rk628_bt1120_enable_interrupts(sd, false); + if (bt1120->rk628->version < RK628F_VERSION) { + enable_stream(sd, false); + bt1120->nosignal = true; } + schedule_delayed_work(&bt1120->delayed_work_res_change, HZ / 2); - if ((pdec_ints & AVI_RCV_ISTS) && plugin && !bt1120->avi_rcv_rdy) { - v4l2_dbg(1, debug, sd, "%s: AVI RCV INT!\n", __func__); - bt1120->avi_rcv_rdy = true; - /* After get the AVI_RCV interrupt state, disable interrupt. */ - rk628_i2c_write(bt1120->rk628, HDMI_RX_PDEC_IEN_CLR, AVI_RCV_ISTS); + v4l2_dbg(1, debug, sd, "%s: hact/vact change, md_ints: %#x\n", + __func__, (u32)(md_ints & (VACT_LIN_ISTS | HACT_PIX_ISTS))); + *handled = true; + } - *handled = true; - } + if ((pdec_ints & AVI_RCV_ISTS) && plugin && !bt1120->avi_rcv_rdy) { + v4l2_dbg(1, debug, sd, "%s: AVI RCV INT!\n", __func__); + bt1120->avi_rcv_rdy = true; + /* After get the AVI_RCV interrupt state, disable interrupt. */ + rk628_i2c_write(bt1120->rk628, HDMI_RX_PDEC_IEN_CLR, AVI_RCV_ISTS); + + *handled = true; } if (*handled != true) v4l2_dbg(1, debug, sd, "%s: unhandled interrupt!\n", __func__); @@ -1041,17 +1060,25 @@ static int rk628_bt1120_isr(struct v4l2_subdev *sd, u32 status, bool *handled) return 0; } +static int rk628_hdmirx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +{ + struct rk628_bt1120 *bt1120 = to_bt1120(sd); + + rk628_hdmirx_general_isr(sd, status, handled); + if (bt1120->cec_enable && bt1120->cec) + rk628_hdmirx_cec_irq(bt1120->rk628, bt1120->cec); + + rk628_bt1120_clear_hdmirx_interrupts(sd); + + return 0; +} + static irqreturn_t rk628_bt1120_irq_handler(int irq, void *dev_id) { struct rk628_bt1120 *bt1120 = dev_id; bool handled = true; - rk628_bt1120_isr(&bt1120->sd, 0, &handled); - - if (bt1120->cec_enable && bt1120->cec) - rk628_hdmirx_cec_irq(bt1120->rk628, bt1120->cec); - - rk628_bt1120_clear_hdmirx_interrupts(&bt1120->sd); + rk628_hdmirx_isr(&bt1120->sd, 0, &handled); return handled ? IRQ_HANDLED : IRQ_NONE; } @@ -1615,7 +1642,7 @@ static const struct v4l2_subdev_internal_ops bt1120_subdev_internal_ops = { #endif static const struct v4l2_subdev_core_ops rk628_bt1120_core_ops = { - .interrupt_service_routine = rk628_bt1120_isr, + .interrupt_service_routine = rk628_hdmirx_isr, .subscribe_event = rk628_bt1120_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, .ioctl = rk628_bt1120_ioctl, diff --git a/drivers/media/i2c/rk628/rk628_csi_v4l2.c b/drivers/media/i2c/rk628/rk628_csi_v4l2.c index bb4a2d24b84d..9828d9c00475 100644 --- a/drivers/media/i2c/rk628/rk628_csi_v4l2.c +++ b/drivers/media/i2c/rk628/rk628_csi_v4l2.c @@ -382,7 +382,7 @@ static bool tx_5v_power_present(struct v4l2_subdev *sd) struct rk628_csi *csi = to_csi(sd); ret = rk628_hdmirx_tx_5v_power_detect(csi->plugin_det_gpio); - v4l2_dbg(1, debug, sd, "%s: %d\n", __func__, ret); + v4l2_dbg(2, debug, sd, "%s: %d\n", __func__, ret); return ret; } @@ -577,6 +577,7 @@ static void rk628_delayed_work_res_change(struct work_struct *work) bool plugin; mutex_lock(&csi->confctl_mutex); + rk628_set_bg_enable(csi->rk628, false); enable_stream(sd, false); csi->nosignal = true; csi->avi_rcv_rdy = false; @@ -1574,9 +1575,6 @@ static void rk628_csi_clear_hdmirx_interrupts(struct v4l2_subdev *sd) struct rk628_csi *csi = to_csi(sd); v4l2_dbg(2, debug, sd, "%s: clear hdmirx ints\n", __func__); - /* clear interrupts */ - rk628_i2c_write(csi->rk628, HDMI_RX_MD_ICLR, 0xffffffff); - rk628_i2c_write(csi->rk628, HDMI_RX_PDEC_ICLR, 0xffffffff); if (csi->rk628->version >= RK628F_VERSION) rk628_i2c_write(csi->rk628, GRF_INTR0_CLR_EN, 0x02000200); else @@ -1647,13 +1645,22 @@ static void rk628_csi_error_process(struct v4l2_subdev *sd) } } -static int rk628_csi_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +static int rk628_is_general_isr(struct rk628_csi *csi, u32 md_ints, u32 pdec_ints) +{ + if (rk628_hdmirx_is_signal_change_ists(csi->rk628, md_ints, pdec_ints)) + return 1; + if ((pdec_ints & AVI_RCV_ISTS) && !csi->avi_rcv_rdy) + return 1; + + return 0; +} + +static int rk628_hdmirx_general_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { struct rk628_csi *csi = to_csi(sd); u32 md_ints = 0x0, pdec_ints = 0x0, fifo_ints, hact, vact; bool plugin; void *audio_info = csi->audio_info; - u32 csi0_raw_ints = 0x0, csi1_raw_ints = 0x0; u32 int0_status; const struct v4l2_event evt_signal_lost = { .type = RK_HDMIRX_V4L2_EVENT_SIGNAL_LOST, @@ -1664,31 +1671,36 @@ static int rk628_csi_isr(struct v4l2_subdev *sd, u32 status, bool *handled) return -EINVAL; } + if (!csi->vid_ints_en) + return 0; + rk628_i2c_read(csi->rk628, GRF_INTR0_STATUS, &int0_status); + if (!(int0_status & (BIT(8) | BIT(9)))) + return 0; + + rk628_i2c_read(csi->rk628, HDMI_RX_MD_ISTS, &md_ints); + rk628_i2c_read(csi->rk628, HDMI_RX_PDEC_ISTS, &pdec_ints); + + /* clear interrupts */ + rk628_i2c_write(csi->rk628, HDMI_RX_MD_ICLR, 0xffffffff); + rk628_i2c_write(csi->rk628, HDMI_RX_PDEC_ICLR, 0xffffffff); + + if (!rk628_is_general_isr(csi, md_ints, pdec_ints)) + return 0; + v4l2_dbg(1, debug, sd, "%s: int0 status: 0x%x\n", __func__, int0_status); - if (int0_status & (BIT(8) | BIT(9))) { - rk628_i2c_read(csi->rk628, HDMI_RX_MD_ISTS, &md_ints); - rk628_i2c_read(csi->rk628, HDMI_RX_PDEC_ISTS, &pdec_ints); - if (csi->rk628->version >= RK628F_VERSION && - rk628_hdmirx_is_signal_change_ists(csi->rk628, md_ints, pdec_ints)) - rk628_set_bg_enable(csi->rk628, true); - } - if ((int0_status & (BIT(6) | BIT(7)))) { - rk628_i2c_read(csi->rk628, CSITX_ERR_INTR_RAW_STATUS_IMD, &csi0_raw_ints); - if (csi->rk628->version >= RK628F_VERSION) - rk628_i2c_read(csi->rk628, CSITX1_ERR_INTR_RAW_STATUS_IMD, &csi1_raw_ints); - rk628_csi_clear_csi_interrupts(sd); - } + if (csi->rk628->version >= RK628F_VERSION && + rk628_hdmirx_is_signal_change_ists(csi->rk628, md_ints, pdec_ints)) + rk628_set_bg_enable(csi->rk628, true); plugin = tx_5v_power_present(sd); if (!plugin) { rk628_csi_enable_interrupts(sd, false); - rk628_csi_enable_csi_interrupts(sd, false); return 0; } - if (csi->rk628->version < RK628F_VERSION && (int0_status & BIT(8))) { + if (csi->rk628->version < RK628F_VERSION) { if (rk628_audio_ctsnints_enabled(audio_info)) { if (pdec_ints & (ACR_N_CHG_ICLR | ACR_CTS_CHG_ICLR)) { rk628_csi_isr_ctsn(audio_info, pdec_ints); @@ -1704,41 +1716,77 @@ static int rk628_csi_isr(struct v4l2_subdev *sd, u32 status, bool *handled) } } } - if (csi->vid_ints_en && (int0_status & (BIT(8) | BIT(9)))) { - v4l2_dbg(1, debug, sd, "%s: md_ints: %#x, pdec_ints:%#x, plugin: %d\n", - __func__, md_ints, pdec_ints, plugin); - if (rk628_hdmirx_is_signal_change_ists(csi->rk628, md_ints, pdec_ints)) { - rk628_i2c_read(csi->rk628, HDMI_RX_MD_HACT_PX, &hact); - rk628_i2c_read(csi->rk628, HDMI_RX_MD_VAL, &vact); - v4l2_dbg(1, debug, sd, "%s: HACT:%#x, VACT:%#x\n", - __func__, hact, vact); + v4l2_dbg(1, debug, sd, "%s: md_ints: %#x, pdec_ints:%#x, plugin: %d\n", + __func__, md_ints, pdec_ints, plugin); - rk628_csi_enable_interrupts(sd, false); - if (csi->rk628->version < RK628F_VERSION) { - enable_stream(sd, false); - csi->nosignal = true; - } - v4l2_event_queue(sd->devnode, &evt_signal_lost); - schedule_delayed_work(&csi->delayed_work_res_change, msecs_to_jiffies(100)); + if (rk628_hdmirx_is_signal_change_ists(csi->rk628, md_ints, pdec_ints)) { + rk628_i2c_read(csi->rk628, HDMI_RX_MD_HACT_PX, &hact); + rk628_i2c_read(csi->rk628, HDMI_RX_MD_VAL, &vact); + v4l2_dbg(1, debug, sd, "%s: HACT:%#x, VACT:%#x\n", + __func__, hact, vact); - v4l2_dbg(1, debug, sd, "%s: hact/vact change, md_ints: %#x\n", - __func__, (u32)(md_ints & (VACT_LIN_ISTS | HACT_PIX_ISTS))); - *handled = true; + rk628_csi_enable_interrupts(sd, false); + if (csi->rk628->version < RK628F_VERSION) { + enable_stream(sd, false); + csi->nosignal = true; } + v4l2_event_queue(sd->devnode, &evt_signal_lost); + schedule_delayed_work(&csi->delayed_work_res_change, msecs_to_jiffies(100)); - if ((pdec_ints & AVI_RCV_ISTS) && plugin && !csi->avi_rcv_rdy) { - v4l2_dbg(1, debug, sd, "%s: AVI RCV INT!\n", __func__); - if (csi->plat_data->tx_mode == DSI_MODE) - enable_stream(sd, false); - csi->avi_rcv_rdy = true; - /* After get the AVI_RCV interrupt state, disable interrupt. */ - rk628_i2c_write(csi->rk628, HDMI_RX_PDEC_IEN_CLR, AVI_RCV_ISTS); - - *handled = true; - } + v4l2_dbg(1, debug, sd, "%s: hact/vact change, md_ints: %#x\n", + __func__, (u32)(md_ints & (VACT_LIN_ISTS | HACT_PIX_ISTS))); + *handled = true; } + if ((pdec_ints & AVI_RCV_ISTS) && plugin && !csi->avi_rcv_rdy) { + v4l2_dbg(1, debug, sd, "%s: AVI RCV INT!\n", __func__); + if (csi->plat_data->tx_mode == DSI_MODE) + enable_stream(sd, false); + csi->avi_rcv_rdy = true; + /* After get the AVI_RCV interrupt state, disable interrupt. */ + rk628_i2c_write(csi->rk628, HDMI_RX_PDEC_IEN_CLR, AVI_RCV_ISTS); + + *handled = true; + } + + if (*handled != true) + v4l2_dbg(1, debug, sd, "%s: unhandled interrupt!\n", __func__); + + return 0; +} + +static int rk628_hdmirx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +{ + struct rk628_csi *csi = to_csi(sd); + + rk628_hdmirx_general_isr(sd, status, handled); + if (csi->cec_enable && csi->cec) + rk628_hdmirx_cec_irq(csi->rk628, csi->cec); + + rk628_csi_clear_hdmirx_interrupts(sd); + + return 0; +} + +static int rk628_csi_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +{ + struct rk628_csi *csi = to_csi(sd); + u32 int0_status; + u32 csi0_raw_ints = 0x0, csi1_raw_ints = 0x0; + + rk628_i2c_read(csi->rk628, GRF_INTR0_STATUS, &int0_status); + if (!(int0_status & (BIT(6) | BIT(7)))) + return 0; + + v4l2_dbg(1, debug, sd, "%s: int0 status: 0x%x\n", __func__, int0_status); + + rk628_i2c_read(csi->rk628, CSITX_ERR_INTR_RAW_STATUS_IMD, &csi0_raw_ints); + if (csi->rk628->version >= RK628F_VERSION) + rk628_i2c_read(csi->rk628, CSITX1_ERR_INTR_RAW_STATUS_IMD, &csi1_raw_ints); + + rk628_csi_clear_csi_interrupts(sd); + if (csi0_raw_ints || csi1_raw_ints) { v4l2_info(sd, "%s: csi interrupt: csi0_raw_ints: 0x%x, csi1_raw_ints: 0x%x!\n", @@ -1753,17 +1801,20 @@ static int rk628_csi_isr(struct v4l2_subdev *sd, u32 status, bool *handled) return 0; } +static int rk628_isr_process(struct v4l2_subdev *sd, u32 status, bool *handled) +{ + rk628_hdmirx_isr(sd, status, handled); + rk628_csi_isr(sd, status, handled); + + return 0; +} + static irqreturn_t rk628_csi_irq_handler(int irq, void *dev_id) { struct rk628_csi *csi = dev_id; bool handled = true; - rk628_csi_isr(&csi->sd, 0, &handled); - - if (csi->cec_enable && csi->cec) - rk628_hdmirx_cec_irq(csi->rk628, csi->cec); - - rk628_csi_clear_hdmirx_interrupts(&csi->sd); + rk628_isr_process(&csi->sd, 0, &handled); return handled ? IRQ_HANDLED : IRQ_NONE; } @@ -2789,7 +2840,7 @@ static long rk628_csi_compat_ioctl32(struct v4l2_subdev *sd, #endif static const struct v4l2_subdev_core_ops rk628_csi_core_ops = { - .interrupt_service_routine = rk628_csi_isr, + .interrupt_service_routine = rk628_isr_process, .subscribe_event = rk628_csi_subscribe_event, .unsubscribe_event = v4l2_event_subdev_unsubscribe, .ioctl = rk628_csi_ioctl, From 05bd68e5d5dae2b02e99bd8aa540a36ff648fd90 Mon Sep 17 00:00:00 2001 From: Weixin Zhou Date: Tue, 14 May 2024 10:15:36 +0800 Subject: [PATCH 10/17] input: touchscreen: fix build error with CONFIG_FB Signed-off-by: Weixin Zhou Change-Id: Id46e4eca111a8216cfade8038486ffcf233a9b6d --- .../focaltech_touch_ft5726/focaltech_core.c | 14 +++++--------- drivers/input/touchscreen/parade/pt_core.c | 6 +++--- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/input/touchscreen/focaltech_touch_ft5726/focaltech_core.c b/drivers/input/touchscreen/focaltech_touch_ft5726/focaltech_core.c index 324100dc0d40..f17fec1df58c 100644 --- a/drivers/input/touchscreen/focaltech_touch_ft5726/focaltech_core.c +++ b/drivers/input/touchscreen/focaltech_touch_ft5726/focaltech_core.c @@ -1432,7 +1432,7 @@ static void fts_resume_work(struct work_struct *work) } #if defined(CONFIG_FB) -static int fb_notifier_callback(struct notifier_block *self, +static int fts_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) { struct fb_event *evdata = data; @@ -1445,7 +1445,7 @@ static int fb_notifier_callback(struct notifier_block *self, return 0; } - if (!(event == FB_EARLY_EVENT_BLANK || event == FB_EVENT_BLANK)) { + if (event != FB_EVENT_BLANK) { FTS_INFO("event(%lu) do not need process\n", event); return 0; } @@ -1454,18 +1454,14 @@ static int fb_notifier_callback(struct notifier_block *self, FTS_INFO("FB event:%lu,blank:%d", event, *blank); switch (*blank) { case FB_BLANK_UNBLANK: - if (FB_EARLY_EVENT_BLANK == event) { - FTS_INFO("resume: event = %lu, not care\n", event); - } else if (FB_EVENT_BLANK == event) { + if (FB_EVENT_BLANK == event) { queue_work(fts_data->ts_workqueue, &fts_data->resume_work); } break; case FB_BLANK_POWERDOWN: - if (FB_EARLY_EVENT_BLANK == event) { + if (FB_EVENT_BLANK == event) { cancel_work_sync(&fts_data->resume_work); fts_ts_suspend(ts_data->dev); - } else if (FB_EVENT_BLANK == event) { - FTS_INFO("suspend: event = %lu, not care\n", event); } break; default: @@ -1766,7 +1762,7 @@ static int fts_ts_probe_entry(struct fts_ts_data *ts_data) #endif #if defined(CONFIG_FB) - ts_data->fb_notif.notifier_call = fb_notifier_callback; + ts_data->fb_notif.notifier_call = fts_fb_notifier_callback; ret = fb_register_client(&ts_data->fb_notif); if (ret) { FTS_ERROR("[FB]Unable to register fb_notifier: %d", ret); diff --git a/drivers/input/touchscreen/parade/pt_core.c b/drivers/input/touchscreen/parade/pt_core.c index b98996ec940b..97a7c452e3ab 100644 --- a/drivers/input/touchscreen/parade/pt_core.c +++ b/drivers/input/touchscreen/parade/pt_core.c @@ -12305,7 +12305,7 @@ static void pt_setup_early_suspend(struct pt_core_data *cd) } #elif defined(CONFIG_FB) /******************************************************************************* - * FUNCTION: fb_notifier_callback + * FUNCTION: pt_fb_notifier_callback * * SUMMARY: Call back function for FrameBuffer notifier to allow to call * resume/suspend attention list. @@ -12318,7 +12318,7 @@ static void pt_setup_early_suspend(struct pt_core_data *cd) * event - event type of fb notifier * *data - pointer to fb_event structure ******************************************************************************/ -static int fb_notifier_callback(struct notifier_block *self, +static int pt_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) { struct pt_core_data *cd = @@ -12370,7 +12370,7 @@ static void pt_setup_fb_notifier(struct pt_core_data *cd) cd->fb_state = FB_ON; - cd->fb_notifier.notifier_call = fb_notifier_callback; + cd->fb_notifier.notifier_call = pt_fb_notifier_callback; rc = fb_register_client(&cd->fb_notifier); if (rc) From 7ab25e6a29d3142d0d5b4490a32ab73a48ed6de3 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Wed, 15 May 2024 10:48:58 +0800 Subject: [PATCH 11/17] drm/rockchip: analogix_dp: fix the max_i2s_channels to 8 According to IC design, the max_i2s_channels is 8 in RK3568/RK3576/RK3588. Change-Id: Ic1bda08c4b8bab72c5cf32ee5ce9aad8c1075ffe Signed-off-by: Damon Ding --- drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index bcb551fc29b2..032aad4ce48f 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -600,7 +600,7 @@ static int rockchip_dp_bind(struct device *dev, struct device *master, .ops = &rockchip_dp_audio_codec_ops, .spdif = 1, .i2s = 1, - .max_i2s_channels = 2, + .max_i2s_channels = 8, }; dp->audio_pdev = From 6becc68bd5178913f9fa344d4b3285f9edeba6a7 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Wed, 15 May 2024 16:51:22 +0800 Subject: [PATCH 12/17] arm64: dts: rockchip: rk3576: add property #sound-dai-cells for edp Change-Id: I5367c1bff1598d54a0785f459f00d7c9147e081e Signed-off-by: Damon Ding --- arch/arm64/boot/dts/rockchip/rk3576.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576.dtsi index b3561c32e3d0..1ff0eace6c78 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi @@ -3158,6 +3158,7 @@ phy-names = "dp"; power-domains = <&power RK3576_PD_VO0>; rockchip,grf = <&vo0_grf>; + #sound-dai-cells = <1>; status = "disabled"; ports { From 84555b3f82b2c0edbd754eb9be581b8cce39d618 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Wed, 15 May 2024 16:52:19 +0800 Subject: [PATCH 13/17] arm64: dts: rockchip: rk3576-evb: add edp sound support Change-Id: If59c59e7320f3011e193308bbb204eb3ed7baad0 Signed-off-by: Damon Ding --- arch/arm64/boot/dts/rockchip/rk3576-evb.dtsi | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3576-evb.dtsi b/arch/arm64/boot/dts/rockchip/rk3576-evb.dtsi index 5d9084586630..8748210d65f5 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-evb.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3576-evb.dtsi @@ -116,6 +116,15 @@ rockchip,jack-det; }; + edp_sound: edp-sound { + status = "disabled"; + compatible = "rockchip,hdmi"; + rockchip,mclk-fs = <128>; + rockchip,card-name = "rockchip-edp"; + rockchip,cpu = <&sai6>; + rockchip,codec = <&edp 0>; + }; + hdmi_sound: hdmi-sound { compatible = "rockchip,hdmi"; rockchip,mclk-fs = <128>; From 0ecd15c4328c8240c722a0d47094fb149dd64f82 Mon Sep 17 00:00:00 2001 From: Shunhua Lan Date: Wed, 10 Apr 2024 21:00:35 +0800 Subject: [PATCH 14/17] media: i2c: rk628: only disable i2s data out only when disabling i2s out Change-Id: I96ae219e1e0c1338120547a43726e759884f0a7c Signed-off-by: Shunhua Lan --- drivers/media/i2c/rk628/rk628_hdmirx.c | 37 ++++++++++++++------------ drivers/media/i2c/rk628/rk628_hdmirx.h | 2 ++ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/drivers/media/i2c/rk628/rk628_hdmirx.c b/drivers/media/i2c/rk628/rk628_hdmirx.c index d397324eab4f..13e9fc2c40eb 100644 --- a/drivers/media/i2c/rk628/rk628_hdmirx.c +++ b/drivers/media/i2c/rk628/rk628_hdmirx.c @@ -39,6 +39,7 @@ struct rk628_audioinfo { bool i2s_enabled_default; bool i2s_enabled; int debug; + bool sample_flat; bool fifo_ints_en; bool ctsn_ints_en; bool audio_present; @@ -466,14 +467,16 @@ static void rk628_csi_delayed_work_audio_v2(struct work_struct *work) } } audio_state->pre_state = fifo_status; - - rk628_i2c_read(rk628, HDMI_RX_AUD_SPARE, &sample_flat); - sample_flat = sample_flat & AUDS_MAS_SAMPLE_FLAT; - if (!sample_flat) - rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_I2S_DATA_OEN_MASK, SW_I2S_DATA_OEN(0)); - else - rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_I2S_DATA_OEN_MASK, SW_I2S_DATA_OEN(1)); - + if (aif->i2s_enabled) { + rk628_i2c_read(rk628, HDMI_RX_AUD_SPARE, &sample_flat); + sample_flat = !!(sample_flat & AUDS_MAS_SAMPLE_FLAT); + if (sample_flat != aif->sample_flat) { + dev_info(rk628->dev, "audio sample flat change to %d\n", sample_flat); + rk628_i2c_write(aif->rk628, HDMI_RX_AUD_SAO_CTRL, I2S_LPCM_BPCUV(0) | I2S_32_16(1) | + (sample_flat ? I2S_DATA_ENABLE_BITS(0xf) : I2S_DATA_ENABLE_BITS(0))); + aif->sample_flat = sample_flat; + } + } exit: schedule_delayed_work(&aif->delayed_work_audio, msecs_to_jiffies(delay)); } @@ -666,17 +669,16 @@ void rk628_hdmirx_audio_i2s_ctrl(HAUDINFO info, bool enable) { struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info; - if (enable == aif->i2s_enabled) + if (enable == aif->i2s_enabled || aif->i2s_enabled_default) return; - if (enable) { + if (enable && !aif->sample_flat) { rk628_i2c_write(aif->rk628, HDMI_RX_AUD_SAO_CTRL, - I2S_LPCM_BPCUV(0) | - I2S_32_16(1)); + I2S_LPCM_BPCUV(0) | I2S_32_16(1) | + I2S_DATA_ENABLE_BITS(0)); } else { rk628_i2c_write(aif->rk628, HDMI_RX_AUD_SAO_CTRL, - I2S_LPCM_BPCUV(0) | - I2S_32_16(1) | - I2S_ENABLE_BITS(0x3f)); + I2S_LPCM_BPCUV(0) | I2S_32_16(1) | + I2S_DATA_ENABLE_BITS(0xf)); } aif->i2s_enabled = enable; } @@ -697,6 +699,7 @@ void rk628_hdmirx_audio_setup(HAUDINFO info) aif->audio_state.init_state = INIT_FIFO_STATE*4; aif->audio_state.fifo_int = false; aif->audio_state.audio_enable = false; + aif->sample_flat = false; aif->fifo_ints_en = false; aif->ctsn_ints_en = false; aif->i2s_enabled = false; @@ -732,8 +735,8 @@ void rk628_hdmirx_audio_setup(HAUDINFO info) AFIF_SUBPACKETS(1)); rk628_i2c_write(aif->rk628, HDMI_RX_AUD_SAO_CTRL, I2S_LPCM_BPCUV(0) | - I2S_32_16(1)| - (aif->i2s_enabled_default ? 0 : I2S_ENABLE_BITS(0x3f))); + I2S_32_16(1) | + (aif->i2s_enabled_default ? 0 : I2S_DATA_ENABLE_BITS(0xf))); aif->i2s_enabled = aif->i2s_enabled_default; rk628_i2c_write(aif->rk628, HDMI_RX_AUD_MUTE_CTRL, APPLY_INT_MUTE(0) | diff --git a/drivers/media/i2c/rk628/rk628_hdmirx.h b/drivers/media/i2c/rk628/rk628_hdmirx.h index f979fa0f5e6c..ffc37dbb0448 100644 --- a/drivers/media/i2c/rk628/rk628_hdmirx.h +++ b/drivers/media/i2c/rk628/rk628_hdmirx.h @@ -188,6 +188,8 @@ #define HDMI_RX_AUD_SAO_CTRL (HDMI_RX_BASE + 0x0260) #define I2S_ENABLE_BITS_MASK GENMASK(10, 5) #define I2S_ENABLE_BITS(x) UPDATE(x, 10, 5) +#define I2S_CLK_ENABLE_BITS(x) UPDATE(x, 10, 9) +#define I2S_DATA_ENABLE_BITS(x) UPDATE(x, 8, 5) #define I2S_LPCM_BPCUV_MASK BIT(11) #define I2S_LPCM_BPCUV(x) UPDATE(x, 11, 11) #define I2S_32_16_MASK BIT(0) From 39c151cdbe7100c751c984208648273c9cc3b9a4 Mon Sep 17 00:00:00 2001 From: Shunhua Lan Date: Wed, 10 Apr 2024 12:02:02 +0800 Subject: [PATCH 15/17] media: i2c: rk628: register a codec for hdmirx audio When the hdmi out device which connect to the rk628 hdmiin stopping audio stream, The i2s clk out by rk628 will be interrupted. Here register a codec to inform alsa the i2s clk is interrupted. Change-Id: I52a43843f64de964bb6415d46d7cefd17a464157 Signed-off-by: Shunhua Lan --- drivers/media/i2c/rk628/rk628_hdmirx.c | 238 +++++++++++++++++++++---- drivers/media/i2c/rk628/rk628_hdmirx.h | 1 + 2 files changed, 209 insertions(+), 30 deletions(-) diff --git a/drivers/media/i2c/rk628/rk628_hdmirx.c b/drivers/media/i2c/rk628/rk628_hdmirx.c index 13e9fc2c40eb..0065a096237c 100644 --- a/drivers/media/i2c/rk628/rk628_hdmirx.c +++ b/drivers/media/i2c/rk628/rk628_hdmirx.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "rk628.h" #include "rk628_combrxphy.h" @@ -19,6 +20,8 @@ #define INIT_FIFO_STATE 64 +#define DEFAULT_AUDIO_CLK 5644800 + struct rk628_audiostate { u32 hdmirx_aud_clkrate; u32 fs_audio; @@ -44,7 +47,15 @@ struct rk628_audioinfo { bool ctsn_ints_en; bool audio_present; bool arc_en; + bool underflow; + bool overflow; + bool startthreshold; + int stablelimit; + int stablecount; struct device *dev; + struct platform_device *pdev; + hdmi_codec_plugged_cb plugged_cb; + struct device *codec_dev; }; struct hdmirx_tmdsclk_cnt { @@ -298,6 +309,9 @@ static void rk628_hdmirx_audio_fifo_init(struct rk628_audioinfo *aif) rk628_i2c_write(aif->rk628, HDMI_RX_AUD_FIFO_CTRL, 0x10001); rk628_i2c_write(aif->rk628, HDMI_RX_AUD_FIFO_CTRL, 0x10000); aif->audio_state.pre_state = aif->audio_state.init_state = INIT_FIFO_STATE*4; + aif->underflow = false; + aif->overflow = false; + aif->startthreshold = false; } static void rk628_hdmirx_audio_fifo_initd(struct rk628_audioinfo *aif) @@ -362,6 +376,25 @@ static void rk628_hdmirx_audio_clk_inc_rate(struct rk628_audioinfo *aif, int dis aif->audio_state.hdmirx_aud_clkrate = hdmirx_aud_clkrate; } +static void rk628_hdmirx_audio_clk_ppm_inc(struct rk628_audioinfo *aif, int ppm) +{ + int delta, rate, inc; + + rate = aif->audio_state.hdmirx_aud_clkrate; + if (ppm < 0) { + ppm = -ppm; + inc = -1; + } else + inc = 1; + delta = div_u64(((uint64_t)rate * ppm + 500000), 1000000); + delta *= inc; + rate += delta; + dev_dbg(aif->dev, "%s: %u to %u(delta:%d)\n", + __func__, aif->audio_state.hdmirx_aud_clkrate, rate, delta); + rk628_clk_set_rate(aif->rk628, CGU_CLK_HDMIRX_AUD, rate); + aif->audio_state.hdmirx_aud_clkrate = rate; +} + static void rk628_hdmirx_audio_set_fs(struct rk628_audioinfo *aif, u32 fs_audio) { u32 hdmirx_aud_clkrate_t = fs_audio*128; @@ -405,6 +438,55 @@ static const char *audio_fifo_err(u32 fifo_status) return "underflow or overflow"; } +static int rk628_hdmirx_audio_clk_adjust(struct rk628_audioinfo *aif, + int total_offset, int single_offset) +{ + int shedule_time = 500; + int ppm = 10; + + if (total_offset > 16 && single_offset > 0) + rk628_hdmirx_audio_clk_ppm_inc(aif, ppm); + else if (total_offset < -16 && single_offset < 0) + rk628_hdmirx_audio_clk_ppm_inc(aif, -ppm); + if (total_offset >= 20) { + shedule_time = 200; + } else if (total_offset >= 50) { + shedule_time = 100; + dev_dbg(aif->dev, "%s: decrease shedule time to %d\n", __func__, shedule_time); + } else if (ppm >= 80) { + shedule_time = 50; + dev_dbg(aif->dev, "%s: decrease shedule time to %d\n", __func__, shedule_time); + } + if (!aif->audio_present) + shedule_time = 50; + return shedule_time; +} + +static void rk628_hdmirx_audio_state_change(struct rk628_audioinfo *aif, bool on) +{ + struct device *dev = aif->rk628->dev; + + if (on) { + if (aif->stablecount < aif->stablelimit) { + aif->stablecount++; + dev_info(dev, "wait for audio stable count %d\n", aif->stablecount); + return; + } + if (!aif->audio_present) { + aif->audio_present = true; + dev_info(dev, "audio on\n"); + rk628_hdmirx_audio_handle_plugged_change(aif, aif->audio_present); + } + } else { + if (aif->audio_present) { + aif->stablecount = 0; + aif->audio_present = false; + dev_info(dev, "audio off\n"); + rk628_hdmirx_audio_handle_plugged_change(aif, aif->audio_present); + } + } +} + static void rk628_csi_delayed_work_audio_v2(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); @@ -414,13 +496,13 @@ static void rk628_csi_delayed_work_audio_v2(struct work_struct *work) struct rk628 *rk628 = aif->rk628; u32 fs_audio, sample_flat; int init_state, pre_state, fifo_status, fifo_ints; + int single_offset, total_offset; unsigned long delay = 500; fs_audio = _rk628_hdmirx_audio_fs(aif); /* read fifo init status */ rk628_i2c_read(rk628, HDMI_RX_AUD_FIFO_ISTS, &fifo_ints); dev_dbg(rk628->dev, "%s: HDMI_RX_AUD_FIFO_ISTS:%#x\r\n", __func__, fifo_ints); - if (fifo_ints & (AFIF_UNDERFL_ISTS | AFIF_OVERFL_ISTS)) { dev_warn(rk628->dev, "%s: audio %s %#x, with fs %svalid %d\n", __func__, audio_fifo_err(fifo_ints), fifo_ints, @@ -428,7 +510,7 @@ static void rk628_csi_delayed_work_audio_v2(struct work_struct *work) if (is_validfs(fs_audio)) rk628_hdmirx_audio_set_fs(aif, fs_audio); rk628_hdmirx_audio_fifo_init(aif); - audio_state->pre_state = 0; + rk628_hdmirx_audio_state_change(aif, 0); goto exit; } @@ -436,9 +518,11 @@ static void rk628_csi_delayed_work_audio_v2(struct work_struct *work) init_state = audio_state->init_state; pre_state = audio_state->pre_state; rk628_i2c_read(rk628, HDMI_RX_AUD_FIFO_FILLSTS1, &fifo_status); + single_offset = fifo_status - pre_state; + total_offset = fifo_status - init_state; dev_dbg(rk628->dev, "%s: HDMI_RX_AUD_FIFO_FILLSTS1:%#x, single offset:%d, total offset:%d\n", - __func__, fifo_status, fifo_status - pre_state, fifo_status - init_state); + __func__, fifo_status, single_offset, total_offset); if (!is_validfs(fs_audio)) { dev_dbg(rk628->dev, "%s: no supported fs(%u), fifo_status %d\n", __func__, fs_audio, fifo_status); @@ -448,23 +532,14 @@ static void rk628_csi_delayed_work_audio_v2(struct work_struct *work) __func__, audio_state->fs_audio, fs_audio); rk628_hdmirx_audio_set_fs(aif, fs_audio); rk628_hdmirx_audio_fifo_init(aif); - audio_state->pre_state = 0; + rk628_hdmirx_audio_state_change(aif, 0); goto exit; } if (fifo_status != 0) { - if (!aif->audio_present) { - dev_info(rk628->dev, "audio on"); - aif->audio_present = true; - } - if (fifo_status - init_state > 16 && fifo_status - pre_state > 0) - rk628_hdmirx_audio_clk_inc_rate(aif, 10); - else if (fifo_status - init_state < -16 && fifo_status - pre_state < 0) - rk628_hdmirx_audio_clk_inc_rate(aif, -10); + rk628_hdmirx_audio_state_change(aif, 1); + delay = rk628_hdmirx_audio_clk_adjust(aif, total_offset, single_offset); } else { - if (aif->audio_present) { - dev_info(rk628->dev, "audio off"); - aif->audio_present = false; - } + rk628_hdmirx_audio_state_change(aif, 0); } audio_state->pre_state = fifo_status; if (aif->i2s_enabled) { @@ -507,10 +582,19 @@ static void rk628_csi_delayed_work_audio(struct work_struct *work) rk628_i2c_read(aif->rk628, HDMI_RX_AUD_FIFO_FILLSTS1, &cur_state); dev_dbg(aif->dev, "%s: HDMI_RX_AUD_FIFO_FILLSTS1:%#x, single offset:%d, total offset:%d\n", __func__, cur_state, cur_state - pre_state, cur_state - init_state); - if (cur_state != 0) - aif->audio_present = true; - else - aif->audio_present = false; + if (cur_state != 0) { + if (!aif->audio_present) { + dev_dbg(aif->dev, "audio on\n"); + aif->audio_present = true; + rk628_hdmirx_audio_handle_plugged_change(aif, 1); + } + } else { + if (aif->audio_present) { + dev_dbg(aif->dev, "audio off\n"); + aif->audio_present = false; + rk628_hdmirx_audio_handle_plugged_change(aif, 0); + } + } if ((cur_state - init_state) > 16 && (cur_state - pre_state) > 0) rk628_hdmirx_audio_clk_inc_rate(aif, 10); @@ -549,23 +633,107 @@ static void rk628_csi_delayed_work_audio_rate_change(struct work_struct *work) if (is_validfs(fs_audio)) rk628_hdmirx_audio_set_fs(aif, fs_audio); rk628_i2c_read(aif->rk628, HDMI_RX_AUD_FIFO_FILLSTS1, &fifo_fillsts); - if (!fifo_fillsts) { + if (!fifo_fillsts) dev_dbg(aif->dev, "%s underflow after overflow\n", __func__); - rk628_hdmirx_audio_fifo_initd(aif); - } else { + else dev_dbg(aif->dev, "%s overflow after underflow\n", __func__); - rk628_hdmirx_audio_fifo_initd(aif); - } + rk628_hdmirx_audio_fifo_initd(aif); + aif->audio_present = false; + rk628_hdmirx_audio_handle_plugged_change(aif, 0); } mutex_unlock(aif->confctl_mutex); } +static int rk628_hdmirx_audio_hw_params(struct device *dev, void *data, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + dev_dbg(dev, "%s\n", __func__); + return 0; +} + +static int rk628_hdmirx_audio_startup(struct device *dev, void *data) +{ + struct rk628_audioinfo *aif = (struct rk628_audioinfo *)data; + + dev_info(dev, "%s: %d\n", __func__, aif->audio_present); + if (aif->audio_present) + return 0; + dev_err(dev, "%s: device is no connected\n", __func__); + return -ENODEV; +} + +static void rk628_hdmirx_audio_shutdown(struct device *dev, void *data) +{ + dev_dbg(dev, "%s\n", __func__); +} + +static int rk628_hdmirx_audio_get_dai_id(struct snd_soc_component *comment, + struct device_node *endpoint) +{ + dev_dbg(comment->dev, "%s\n", __func__); + return 0; +} + +void rk628_hdmirx_audio_handle_plugged_change(HAUDINFO info, bool plugged) +{ + struct rk628_audioinfo *aif = (struct rk628_audioinfo *)info; + + if (aif->plugged_cb && aif->codec_dev) + aif->plugged_cb(aif->codec_dev, plugged); +} + +static int rk628_hdmirx_audio_hook_plugged_cb(struct device *dev, void *data, + hdmi_codec_plugged_cb fn, + struct device *codec_dev) +{ + struct rk628_audioinfo *aif = (struct rk628_audioinfo *)data; + + dev_dbg(dev, "%s\n", __func__); + if (aif->confctl_mutex) + mutex_lock(aif->confctl_mutex); + aif->plugged_cb = fn; + aif->codec_dev = codec_dev; + rk628_hdmirx_audio_handle_plugged_change(aif, aif->audio_present); + if (aif->confctl_mutex) + mutex_unlock(aif->confctl_mutex); + return 0; +} + +static const struct hdmi_codec_ops rk628_hdmirx_audio_codec_ops = { + .hw_params = rk628_hdmirx_audio_hw_params, + .audio_startup = rk628_hdmirx_audio_startup, + .audio_shutdown = rk628_hdmirx_audio_shutdown, + .get_dai_id = rk628_hdmirx_audio_get_dai_id, + .hook_plugged_cb = rk628_hdmirx_audio_hook_plugged_cb +}; + +static int rk628_hdmirx_register_audio_device(struct rk628_audioinfo *aif) +{ + struct hdmi_codec_pdata codec_data = { + .ops = &rk628_hdmirx_audio_codec_ops, + .spdif = 1, + .i2s = 1, + .max_i2s_channels = 8, + .data = aif, + }; + + aif->pdev = platform_device_register_data(aif->dev, + HDMI_CODEC_DRV_NAME, + PLATFORM_DEVID_AUTO, + &codec_data, + sizeof(codec_data)); + + return PTR_ERR_OR_ZERO(aif->pdev); +} + HAUDINFO rk628_hdmirx_audioinfo_alloc(struct device *dev, struct mutex *confctl_mutex, struct rk628 *rk628, bool en) { struct rk628_audioinfo *aif; + int ret; aif = devm_kzalloc(dev, sizeof(*aif), GFP_KERNEL); if (!aif) @@ -581,6 +749,12 @@ HAUDINFO rk628_hdmirx_audioinfo_alloc(struct device *dev, aif->rk628 = rk628; aif->i2s_enabled_default = en; aif->dev = dev; + aif->audio_present = false; + ret = rk628_hdmirx_register_audio_device(aif); + if (ret) { + dev_err(dev, "register audio_driver failed!\n"); + return NULL; + } return aif; } EXPORT_SYMBOL(rk628_hdmirx_audioinfo_alloc); @@ -618,6 +792,8 @@ void rk628_hdmirx_audio_destroy(HAUDINFO info) rk628_hdmirx_audio_cancel_work_audio(aif, true); if (rk628->version < RK628F_VERSION) rk628_hdmirx_audio_cancel_work_rate_change(aif, true); + if (aif->pdev) + platform_device_unregister(aif->pdev); aif->confctl_mutex = NULL; aif->rk628 = NULL; } @@ -703,11 +879,15 @@ void rk628_hdmirx_audio_setup(HAUDINFO info) aif->fifo_ints_en = false; aif->ctsn_ints_en = false; aif->i2s_enabled = false; + aif->underflow = false; + aif->overflow = false; + aif->startthreshold = false; + aif->stablelimit = 0; if (rk628->version >= RK628F_VERSION) rk628_i2c_write(rk628, CRU_MODE_CON00, HIWORD_UPDATE(1, 4, 4)); - rk628_hdmirx_audio_clk_set_rate(aif, 5644800); + rk628_hdmirx_audio_clk_set_rate(aif, DEFAULT_AUDIO_CLK); /* manual aud CTS */ rk628_i2c_write(aif->rk628, HDMI_RX_AUDPLL_GEN_CTS, audio_pll_cts); /* manual aud N */ @@ -723,8 +903,8 @@ void rk628_hdmirx_audio_setup(HAUDINFO info) AFIF_TH_START_MASK | AFIF_TH_MAX_MASK | AFIF_TH_MIN_MASK, - AFIF_TH_START(64) | - AFIF_TH_MAX(8) | + AFIF_TH_START(INIT_FIFO_STATE) | + AFIF_TH_MAX(INIT_FIFO_STATE*2) | AFIF_TH_MIN(8)); /* AUTO_VMUTE */ @@ -752,8 +932,6 @@ void rk628_hdmirx_audio_setup(HAUDINFO info) rk628_i2c_write(aif->rk628, HDMI_RX_AUD_CHEXTR_CTRL, AUD_LAYOUT_CTRL(1)); if (rk628->version >= RK628F_VERSION) { - rk628_i2c_update_bits(aif->rk628, HDMI_RX_DMI_DISABLE_IF, - AUD_ENABLE_MASK, AUD_ENABLE(1)); schedule_delayed_work(&aif->delayed_work_audio, msecs_to_jiffies(1000)); } else { aif->ctsn_ints_en = true; diff --git a/drivers/media/i2c/rk628/rk628_hdmirx.h b/drivers/media/i2c/rk628/rk628_hdmirx.h index ffc37dbb0448..55083a99827d 100644 --- a/drivers/media/i2c/rk628/rk628_hdmirx.h +++ b/drivers/media/i2c/rk628/rk628_hdmirx.h @@ -510,6 +510,7 @@ int rk628_hdmirx_audio_fs(HAUDINFO info); void rk628_hdmirx_audio_i2s_ctrl(HAUDINFO info, bool enable); bool rk628_hdmirx_get_arc_enable(HAUDINFO info); int rk628_hdmirx_set_arc_enable(HAUDINFO info, bool enabled); +void rk628_hdmirx_audio_handle_plugged_change(HAUDINFO info, bool plugged); /* for audio isr process */ bool rk628_audio_fifoints_enabled(HAUDINFO info); From b9aa2b4bb72d21b4b0e86c6043ee2cc15d7433c7 Mon Sep 17 00:00:00 2001 From: Shunhua Lan Date: Wed, 10 Apr 2024 21:08:44 +0800 Subject: [PATCH 16/17] arm64: dts: rockchip: rk3576-evb1-v10-rk628-hdmi2csi: use rockchip hdmi as rk628 hdmiin sound Change-Id: I600ce2121f9da127d79df72100b72cb59b977f37 Signed-off-by: Shunhua Lan --- .../rk3576-evb1-v10-rk628-hdmi2csi.dts | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3576-evb1-v10-rk628-hdmi2csi.dts b/arch/arm64/boot/dts/rockchip/rk3576-evb1-v10-rk628-hdmi2csi.dts index d70ebb39783f..87d0adf7ab70 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-evb1-v10-rk628-hdmi2csi.dts +++ b/arch/arm64/boot/dts/rockchip/rk3576-evb1-v10-rk628-hdmi2csi.dts @@ -23,19 +23,16 @@ status = "okay"; }; - hdmiin-sound { - compatible = "simple-audio-card"; - simple-audio-card,format = "i2s"; - simple-audio-card,name = "rockchip,hdmiin"; - simple-audio-card,bitclock-master = <&dailink0_master>; - simple-audio-card,frame-master = <&dailink0_master>; + hdmiin_sound: hdmiin-sound { status = "okay"; - simple-audio-card,cpu { - sound-dai = <&sai4>; - }; - dailink0_master: simple-audio-card,codec { - sound-dai = <&rk628_dc>; - }; + compatible = "rockchip,hdmi"; + rockchip,mclk-fs = <128>; + rockchip,card-name = "rockchip,rk628hdmiin"; + rockchip,cpu = <&sai4>; + rockchip,codec = <&rk628_csi>; + rockchip,bitclock-master = <&rk628_csi>; + rockchip,frame-master = <&rk628_csi>; + rockchip,jack-det; }; }; @@ -96,6 +93,7 @@ plugin-det-gpios = <&gpio3 RK_PD5 GPIO_ACTIVE_LOW>; continues-clk = <1>; cec-enable; + #sound-dai-cells = <0>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; @@ -174,6 +172,7 @@ pinctrl-0 = <&sai4m2_lrck &sai4m2_sclk &sai4m2_sdi>; + rockchip,sai-rx-wait-time-ms = <50>; status = "okay"; }; From 2ee64657c086046c4d6eb78376e163a158a8a5b7 Mon Sep 17 00:00:00 2001 From: Shunhua Lan Date: Thu, 11 Apr 2024 21:52:40 +0800 Subject: [PATCH 17/17] media: i2c: rk628: implement RK_HDMIRX_V4L2_EVENT_AUDIOINFO event Change-Id: I568ac37ce08ba86a24e1a88b83bb3cb8e878fa03 Signed-off-by: Shunhua Lan --- drivers/media/i2c/rk628/rk628_bt1120_v4l2.c | 3 ++- drivers/media/i2c/rk628/rk628_csi_v4l2.c | 16 +++++++++++++++- drivers/media/i2c/rk628/rk628_hdmirx.c | 7 ++++++- drivers/media/i2c/rk628/rk628_hdmirx.h | 4 +++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c b/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c index e6de0e5d3a71..67c350b811a5 100644 --- a/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c +++ b/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c @@ -2035,7 +2035,8 @@ static int rk628_bt1120_probe(struct i2c_client *client, bt1120->audio_info = rk628_hdmirx_audioinfo_alloc(dev, &bt1120->confctl_mutex, rk628, - bt1120->i2s_enable_default); + bt1120->i2s_enable_default, + NULL); if (!bt1120->audio_info) { v4l2_err(sd, "request audio info fail\n"); goto err_work_queues; diff --git a/drivers/media/i2c/rk628/rk628_csi_v4l2.c b/drivers/media/i2c/rk628/rk628_csi_v4l2.c index 9828d9c00475..2a53a1107c71 100644 --- a/drivers/media/i2c/rk628/rk628_csi_v4l2.c +++ b/drivers/media/i2c/rk628/rk628_csi_v4l2.c @@ -1846,6 +1846,8 @@ static int rk628_csi_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); case RK_HDMIRX_V4L2_EVENT_SIGNAL_LOST: return v4l2_event_subscribe(fh, sub, 0, NULL); + case RK_HDMIRX_V4L2_EVENT_AUDIOINFO: + return v4l2_event_subscribe(fh, sub, 0, NULL); default: return -EINVAL; } @@ -2940,6 +2942,17 @@ static irqreturn_t plugin_detect_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static void rk628_csi_audio_info_cb(struct rk628 *rk628, bool on) +{ + struct i2c_client *client = to_i2c_client(rk628->dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + const struct v4l2_event evt_audio_info = { + .type = RK_HDMIRX_V4L2_EVENT_AUDIOINFO, + }; + v4l2_event_queue(sd->devnode, &evt_audio_info); +} + static int rk628_csi_power_on(struct rk628_csi *csi) { clk_prepare_enable(csi->soc_24M); @@ -3462,7 +3475,8 @@ static int rk628_csi_probe(struct i2c_client *client, csi->audio_info = rk628_hdmirx_audioinfo_alloc(dev, &csi->confctl_mutex, rk628, - csi->i2s_enable_default); + csi->i2s_enable_default, + rk628_csi_audio_info_cb); if (!csi->audio_info) { err = -ENOMEM; v4l2_err(sd, "request audio info fail\n"); diff --git a/drivers/media/i2c/rk628/rk628_hdmirx.c b/drivers/media/i2c/rk628/rk628_hdmirx.c index 0065a096237c..5af2cc8223bb 100644 --- a/drivers/media/i2c/rk628/rk628_hdmirx.c +++ b/drivers/media/i2c/rk628/rk628_hdmirx.c @@ -55,6 +55,7 @@ struct rk628_audioinfo { struct device *dev; struct platform_device *pdev; hdmi_codec_plugged_cb plugged_cb; + rk628_audio_info_cb info_cb; struct device *codec_dev; }; @@ -681,6 +682,8 @@ void rk628_hdmirx_audio_handle_plugged_change(HAUDINFO info, bool plugged) if (aif->plugged_cb && aif->codec_dev) aif->plugged_cb(aif->codec_dev, plugged); + if (aif->info_cb) + aif->info_cb(aif->rk628, plugged); } static int rk628_hdmirx_audio_hook_plugged_cb(struct device *dev, void *data, @@ -730,7 +733,8 @@ static int rk628_hdmirx_register_audio_device(struct rk628_audioinfo *aif) HAUDINFO rk628_hdmirx_audioinfo_alloc(struct device *dev, struct mutex *confctl_mutex, struct rk628 *rk628, - bool en) + bool en, + rk628_audio_info_cb info_cb) { struct rk628_audioinfo *aif; int ret; @@ -750,6 +754,7 @@ HAUDINFO rk628_hdmirx_audioinfo_alloc(struct device *dev, aif->i2s_enabled_default = en; aif->dev = dev; aif->audio_present = false; + aif->info_cb = info_cb; ret = rk628_hdmirx_register_audio_device(aif); if (ret) { dev_err(dev, "register audio_driver failed!\n"); diff --git a/drivers/media/i2c/rk628/rk628_hdmirx.h b/drivers/media/i2c/rk628/rk628_hdmirx.h index 55083a99827d..ed5c4c05ac01 100644 --- a/drivers/media/i2c/rk628/rk628_hdmirx.h +++ b/drivers/media/i2c/rk628/rk628_hdmirx.h @@ -497,10 +497,12 @@ void rk628_hdmirx_set_hdcp(struct rk628 *rk628, struct rk628_hdcp *hdcp, bool en void rk628_hdmirx_controller_setup(struct rk628 *rk628); typedef void *HAUDINFO; +typedef void (*rk628_audio_info_cb)(struct rk628 *rk628, bool on); HAUDINFO rk628_hdmirx_audioinfo_alloc(struct device *dev, struct mutex *confctl_mutex, struct rk628 *rk628, - bool en); + bool en, + rk628_audio_info_cb info_cb); void rk628_hdmirx_audio_destroy(HAUDINFO info); void rk628_hdmirx_audio_setup(HAUDINFO info); void rk628_hdmirx_audio_cancel_work_audio(HAUDINFO info, bool sync);