From 61632fe66ba74549a6a606222706e7b2ffb90571 Mon Sep 17 00:00:00 2001 From: Chen Shunqing Date: Fri, 26 Jul 2024 10:08:14 +0800 Subject: [PATCH] media: i2c: rk628: reset hdmirx when HDMI mode change to DVI mode Signed-off-by: Chen Shunqing Change-Id: Iee807f52bd2a3a78b51ad7dfec6d4bd010ad8299 --- drivers/media/i2c/rk628/rk628_bt1120_v4l2.c | 68 ++++++++++++++++++--- drivers/media/i2c/rk628/rk628_csi_v4l2.c | 66 +++++++++++++++++--- drivers/media/i2c/rk628/rk628_hdmirx.h | 6 ++ 3 files changed, 121 insertions(+), 19 deletions(-) diff --git a/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c b/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c index 316e3f89d001..cfce5413fe02 100644 --- a/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c +++ b/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c @@ -100,6 +100,7 @@ struct rk628_bt1120 { bool vid_ints_en; bool dual_edge; bool cec_enable; + bool dvi_mode; struct rk628_hdmirx_cec *cec; struct rk628_hdcp hdcp; bool i2s_enable_default; @@ -323,12 +324,25 @@ static int rk628_bt1120_get_detected_timings(struct v4l2_subdev *sd, return ret; } +static void rk628_bt1120_hdmirx_reset(struct v4l2_subdev *sd) +{ + struct rk628_bt1120 *bt1120 = to_bt1120(sd); + + disable_irq(bt1120->plugin_irq); + disable_irq(bt1120->hdmirx_irq); + rk628_hdmirx_controller_reset(bt1120->rk628); + bt1120->hdcp.hdcp_start = false; + enable_irq(bt1120->plugin_irq); + enable_irq(bt1120->hdmirx_irq); +} + static void rk628_hdmirx_plugout(struct v4l2_subdev *sd) { struct rk628_bt1120 *bt1120 = to_bt1120(sd); enable_stream(sd, false); bt1120->nosignal = true; + bt1120->hdcp.hdcp_start = false; rk628_bt1120_enable_interrupts(sd, false); cancel_delayed_work(&bt1120->delayed_work_res_change); rk628_hdmirx_audio_cancel_work_audio(bt1120->audio_info, true); @@ -343,16 +357,21 @@ static void rk628_hdmirx_config_all(struct v4l2_subdev *sd) struct rk628_bt1120 *bt1120 = to_bt1120(sd); ret = rk628_hdmirx_phy_setup(sd); - if (ret >= 0 && !rk628_hdmirx_scdc_ced_err(bt1120->rk628)) { + if (ret == LOCK_OK && !rk628_hdmirx_scdc_ced_err(bt1120->rk628)) { ret = rk628_bt1120_format_change(sd); - if (!ret) { + if (ret == LOCK_OK) { bt1120->lock_fail_time = 0; bt1120->nosignal = false; return; } } - if (ret < 0 || rk628_hdmirx_scdc_ced_err(bt1120->rk628)) { + if (ret == LOCK_RESET || rk628_hdmirx_scdc_ced_err(bt1120->rk628)) { + rk628_bt1120_hdmirx_reset(sd); + rk628_hdmirx_hpd_ctrl(sd, true); + schedule_delayed_work(&bt1120->delayed_work_enable_hotplug, + msecs_to_jiffies(100)); + } else if (ret == LOCK_FAIL) { rk628_hdmirx_plugout(sd); bt1120->lock_fail_time++; v4l2_dbg(1, debug, sd, "%s: lock fail time: %d\n", @@ -360,7 +379,7 @@ static void rk628_hdmirx_config_all(struct v4l2_subdev *sd) delay = 800 + 800 * ((bt1120->lock_fail_time + 1) % 2); schedule_delayed_work(&bt1120->delayed_work_enable_hotplug, msecs_to_jiffies(delay)); - } + } } static void rk628_hdmirx_reset_regfile(struct v4l2_subdev *sd) @@ -480,7 +499,7 @@ static void rk628_delayed_work_res_change(struct work_struct *work) rk628_hdmirx_audio_cancel_work_audio(bt1120->audio_info, true); rk628_hdmirx_verisyno_phy_power_off(bt1120->rk628); schedule_delayed_work(&bt1120->delayed_work_enable_hotplug, - msecs_to_jiffies(1100)); + msecs_to_jiffies(100)); } else { rk628_bt1120_enable_interrupts(sd, false); enable_stream(sd, false); @@ -768,6 +787,27 @@ static bool rk628_rcv_supported_res(struct v4l2_subdev *sd, u32 width, } } +static int rk628_hdmirx_dvi_mode_reset(struct v4l2_subdev *sd) +{ + u32 val, avi_pb; + struct rk628_bt1120 *bt1120 = to_bt1120(sd); + + rk628_i2c_read(bt1120->rk628, HDMI_RX_PDEC_STS, &val); + if (val & DVI_DET) { + rk628_i2c_read(bt1120->rk628, HDMI_RX_PDEC_AVI_PB, &avi_pb); + if (avi_pb && !bt1120->dvi_mode) { + bt1120->dvi_mode = true; + v4l2_info(sd, "%s HDMI to DVI hdmirx ctrl reset!\n", __func__); + return -1; + } + bt1120->dvi_mode = true; + } else { + bt1120->dvi_mode = false; + } + + return 0; +} + static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd) { u32 i, cnt, val; @@ -806,9 +846,15 @@ static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd) if (bt1120->rk628->version < RK628F_VERSION && (val & DVI_DET)) dev_info(bt1120->dev, "DVI mode detected\n"); + if ((status & 0xfff) >= 0xf00) { + msleep(50); + if (rk628_hdmirx_dvi_mode_reset(sd)) + return LOCK_RESET; + } + if (!tx_5v_power_present(sd)) { v4l2_info(sd, "HDMI pull out, return!\n"); - return -1; + return LOCK_FAIL; } if (cnt >= 15) @@ -829,9 +875,9 @@ static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd) } if (i == RXPHY_CFG_MAX_TIMES) - return -1; + return LOCK_FAIL; - return 0; + return LOCK_OK; } static void rk628_bt1120_initial(struct v4l2_subdev *sd) { @@ -908,7 +954,7 @@ static int rk628_bt1120_format_change(struct v4l2_subdev *sd) ret = rk628_bt1120_get_detected_timings(sd, &timings); if (ret) { v4l2_dbg(1, debug, sd, "%s: get timing fail\n", __func__); - return ret; + return LOCK_FAIL; } if (!v4l2_match_dv_timings(&bt1120->timings, &timings, 0, false)) { /* automatically set timing rather than set by userspace */ @@ -921,7 +967,7 @@ static int rk628_bt1120_format_change(struct v4l2_subdev *sd) if (sd->devnode) v4l2_subdev_notify_event(sd, &rk628_bt1120_ev_fmt); - return 0; + return LOCK_OK; } static void rk628_bt1120_enable_interrupts(struct v4l2_subdev *sd, bool en) @@ -1070,9 +1116,11 @@ static int rk628_hdmirx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { struct rk628_bt1120 *bt1120 = to_bt1120(sd); + mutex_lock(&bt1120->rk628->rst_lock); rk628_hdmirx_general_isr(sd, status, handled); if (bt1120->cec_enable && bt1120->cec) rk628_hdmirx_cec_irq(bt1120->rk628, bt1120->cec); + mutex_unlock(&bt1120->rk628->rst_lock); rk628_bt1120_clear_hdmirx_interrupts(sd); diff --git a/drivers/media/i2c/rk628/rk628_csi_v4l2.c b/drivers/media/i2c/rk628/rk628_csi_v4l2.c index 543b9d27e1f1..9f1802bd3d21 100644 --- a/drivers/media/i2c/rk628/rk628_csi_v4l2.c +++ b/drivers/media/i2c/rk628/rk628_csi_v4l2.c @@ -125,6 +125,7 @@ struct rk628_csi { bool vid_ints_en; bool continues_clk; bool cec_enable; + bool dvi_mode; struct rk628_hdmirx_cec *cec; struct rk628_hdcp hdcp; bool i2s_enable_default; @@ -474,12 +475,25 @@ static int rk628_csi_get_detected_timings(struct v4l2_subdev *sd, } +static void rk628_csi_hdmirx_reset(struct v4l2_subdev *sd) +{ + struct rk628_csi *csi = to_csi(sd); + + disable_irq(csi->plugin_irq); + disable_irq(csi->hdmirx_irq); + rk628_hdmirx_controller_reset(csi->rk628); + csi->hdcp.hdcp_start = false; + enable_irq(csi->plugin_irq); + enable_irq(csi->hdmirx_irq); +} + static void rk628_hdmirx_plugout(struct v4l2_subdev *sd) { struct rk628_csi *csi = to_csi(sd); enable_stream(sd, false); csi->nosignal = true; + csi->hdcp.hdcp_start = false; rk628_csi_enable_interrupts(sd, false); cancel_delayed_work(&csi->delayed_work_res_change); rk628_hdmirx_audio_cancel_work_audio(csi->audio_info, true); @@ -494,16 +508,21 @@ static void rk628_hdmirx_config_all(struct v4l2_subdev *sd) struct rk628_csi *csi = to_csi(sd); ret = rk628_hdmirx_phy_setup(sd); - if (ret >= 0 && !rk628_hdmirx_scdc_ced_err(csi->rk628)) { + if (ret == LOCK_OK && !rk628_hdmirx_scdc_ced_err(csi->rk628)) { ret = rk628_csi_format_change(sd); - if (!ret) { + if (ret == LOCK_OK) { csi->lock_fail_time = 0; csi->nosignal = false; return; } } - if (ret < 0 || rk628_hdmirx_scdc_ced_err(csi->rk628)) { + if (ret == LOCK_RESET || rk628_hdmirx_scdc_ced_err(csi->rk628)) { + rk628_csi_hdmirx_reset(sd); + rk628_hdmirx_hpd_ctrl(sd, true); + schedule_delayed_work(&csi->delayed_work_enable_hotplug, + msecs_to_jiffies(100)); + } else if (ret == LOCK_FAIL) { if (rk628_hdmirx_get_arc_enable(csi->audio_info)) return; @@ -601,7 +620,7 @@ static void rk628_delayed_work_res_change(struct work_struct *work) rk628_hdmirx_audio_cancel_work_audio(csi->audio_info, true); rk628_hdmirx_verisyno_phy_power_off(csi->rk628); schedule_delayed_work(&csi->delayed_work_enable_hotplug, - msecs_to_jiffies(1100)); + msecs_to_jiffies(100)); } else { rk628_hdmirx_audio_cancel_work_audio(csi->audio_info, true); rk628_hdmirx_inno_phy_power_off(sd); @@ -1269,6 +1288,27 @@ static bool rk628_rcv_supported_res(struct v4l2_subdev *sd, u32 width, } } +static int rk628_hdmirx_dvi_mode_reset(struct v4l2_subdev *sd) +{ + u32 val, avi_pb; + struct rk628_csi *csi = to_csi(sd); + + rk628_i2c_read(csi->rk628, HDMI_RX_PDEC_STS, &val); + if (val & DVI_DET) { + rk628_i2c_read(csi->rk628, HDMI_RX_PDEC_AVI_PB, &avi_pb); + if (avi_pb && !csi->dvi_mode) { + csi->dvi_mode = true; + v4l2_info(sd, "%s HDMI to DVI hdmirx ctrl reset!\n", __func__); + return -1; + } + csi->dvi_mode = true; + } else { + csi->dvi_mode = false; + } + + return 0; +} + static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd) { u32 i, cnt, val; @@ -1307,9 +1347,15 @@ static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd) if (csi->rk628->version < RK628F_VERSION && (val & DVI_DET)) dev_info(csi->dev, "DVI mode detected\n"); + if ((status & 0xfff) >= 0xf00) { + msleep(50); + if (rk628_hdmirx_dvi_mode_reset(sd)) + return LOCK_RESET; + } + if (!tx_5v_power_present(sd)) { v4l2_info(sd, "HDMI pull out, return!\n"); - return -1; + return LOCK_FAIL; } if (cnt >= 15) @@ -1330,9 +1376,9 @@ static int rk628_hdmirx_phy_setup(struct v4l2_subdev *sd) } if (i == RXPHY_CFG_MAX_TIMES) - return -1; + return LOCK_FAIL; - return 0; + return LOCK_OK; } static void rk628_csi_initial(struct v4l2_subdev *sd) @@ -1439,7 +1485,7 @@ static int rk628_csi_format_change(struct v4l2_subdev *sd) ret = rk628_csi_get_detected_timings(sd, &timings); if (ret) { v4l2_dbg(1, debug, sd, "%s: get timing fail\n", __func__); - return ret; + return LOCK_FAIL; } if (!v4l2_match_dv_timings(&csi->timings, &timings, 0, false)) { /* automatically set timing rather than set by userspace */ @@ -1452,7 +1498,7 @@ static int rk628_csi_format_change(struct v4l2_subdev *sd) if (sd->devnode) v4l2_subdev_notify_event(sd, &rk628_csi_ev_fmt); - return 0; + return LOCK_OK; } #if IS_REACHABLE(CONFIG_VIDEO_ROCKCHIP_CIF) @@ -1774,9 +1820,11 @@ static int rk628_hdmirx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { struct rk628_csi *csi = to_csi(sd); + mutex_lock(&csi->rk628->rst_lock); rk628_hdmirx_general_isr(sd, status, handled); if (csi->cec_enable && csi->cec) rk628_hdmirx_cec_irq(csi->rk628, csi->cec); + mutex_unlock(&csi->rk628->rst_lock); rk628_csi_clear_hdmirx_interrupts(sd); diff --git a/drivers/media/i2c/rk628/rk628_hdmirx.h b/drivers/media/i2c/rk628/rk628_hdmirx.h index a54507e25257..095178ff6cd3 100644 --- a/drivers/media/i2c/rk628/rk628_hdmirx.h +++ b/drivers/media/i2c/rk628/rk628_hdmirx.h @@ -471,6 +471,12 @@ enum bus_format { BUS_FMT_UNKNOWN, }; +enum lock_status { + LOCK_OK = 0, + LOCK_FAIL = 1, + LOCK_RESET = 2, +}; + struct hdcp_keys { u8 KSV[HDCP_KEY_KSV_SIZE]; u8 devicekey[HDCP_PRIVATE_KEY_SIZE];