Commit f21c8a27 authored by Kuogee Hsieh's avatar Kuogee Hsieh Committed by Rob Clark

drm/msm/dp: handle irq_hpd with sink_count = 0 correctly

irq_hpd interrupt should be handled after dongle plugged in and
before dongle unplugged. Hence irq_hpd interrupt is enabled at
the end of the plugin handle and disabled at the beginning of
unplugged handle. Current irq_hpd with sink_count = 0 is wrongly
handled same as the dongle unplugged which tears down the mainlink
and disables the phy. This patch fixes this problem by only tearing
down the mainlink but keeping phy enabled at irq_hpd with
sink_count = 0 handle so that next irq_hpd with sink_count =1 can be
handled by setup mainlink only. This patch also set dongle into D3
(power off) state at end of handling irq_hpd with sink_count = 0.

Changes in v2:
-- add ctrl->phy_Power_count

Changes in v3:
-- del ctrl->phy_Power_count
-- add phy_power_off to dp_ctrl_off_link_stream()

Changes in v4:
-- return immediately if clock disable failed at dp_ctrl_off_link_stream()

Changes in v5:
-- set dongle to D3 (power off) state at dp_ctrl_off_link_stream()

Changes in v6:
-- add Fixes tag

Fixes: ea9f337c ("drm/msm/dp: reset dp controller only at boot up and pm_resume")
Signed-off-by: default avatarKuogee Hsieh <khsieh@codeaurora.org>
Tested-by: default avatarStephen Boyd <swboyd@chromium.org>
Reviewed-by: default avatarStephen Boyd <swboyd@chromium.org>
Link: https://lore.kernel.org/r/1621635930-30161-1-git-send-email-khsieh@codeaurora.orgSigned-off-by: default avatarRob Clark <robdclark@chromium.org>
parent 665a6961
...@@ -582,10 +582,9 @@ void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog) ...@@ -582,10 +582,9 @@ void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog)
u32 reftimer = dp_read_aux(catalog, REG_DP_DP_HPD_REFTIMER); u32 reftimer = dp_read_aux(catalog, REG_DP_DP_HPD_REFTIMER);
/* enable HPD interrupts */ /* enable HPD plug and unplug interrupts */
dp_catalog_hpd_config_intr(dp_catalog, dp_catalog_hpd_config_intr(dp_catalog,
DP_DP_HPD_PLUG_INT_MASK | DP_DP_IRQ_HPD_INT_MASK DP_DP_HPD_PLUG_INT_MASK | DP_DP_HPD_UNPLUG_INT_MASK, true);
| DP_DP_HPD_UNPLUG_INT_MASK | DP_DP_HPD_REPLUG_INT_MASK, true);
/* Configure REFTIMER and enable it */ /* Configure REFTIMER and enable it */
reftimer |= DP_DP_HPD_REFTIMER_ENABLE; reftimer |= DP_DP_HPD_REFTIMER_ENABLE;
......
...@@ -1809,6 +1809,61 @@ int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl) ...@@ -1809,6 +1809,61 @@ int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl)
return ret; return ret;
} }
int dp_ctrl_off_link_stream(struct dp_ctrl *dp_ctrl)
{
struct dp_ctrl_private *ctrl;
struct dp_io *dp_io;
struct phy *phy;
int ret;
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
dp_io = &ctrl->parser->io;
phy = dp_io->phy;
/* set dongle to D3 (power off) mode */
dp_link_psm_config(ctrl->link, &ctrl->panel->link_info, true);
dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);
ret = dp_power_clk_enable(ctrl->power, DP_STREAM_PM, false);
if (ret) {
DRM_ERROR("Failed to disable pixel clocks. ret=%d\n", ret);
return ret;
}
ret = dp_power_clk_enable(ctrl->power, DP_CTRL_PM, false);
if (ret) {
DRM_ERROR("Failed to disable link clocks. ret=%d\n", ret);
return ret;
}
phy_power_off(phy);
/* aux channel down, reinit phy */
phy_exit(phy);
phy_init(phy);
DRM_DEBUG_DP("DP off link/stream done\n");
return ret;
}
void dp_ctrl_off_phy(struct dp_ctrl *dp_ctrl)
{
struct dp_ctrl_private *ctrl;
struct dp_io *dp_io;
struct phy *phy;
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
dp_io = &ctrl->parser->io;
phy = dp_io->phy;
dp_catalog_ctrl_reset(ctrl->catalog);
phy_exit(phy);
DRM_DEBUG_DP("DP off phy done\n");
}
int dp_ctrl_off(struct dp_ctrl *dp_ctrl) int dp_ctrl_off(struct dp_ctrl *dp_ctrl)
{ {
struct dp_ctrl_private *ctrl; struct dp_ctrl_private *ctrl;
......
...@@ -23,6 +23,8 @@ int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset); ...@@ -23,6 +23,8 @@ int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset);
void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl); void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl);
int dp_ctrl_on_link(struct dp_ctrl *dp_ctrl); int dp_ctrl_on_link(struct dp_ctrl *dp_ctrl);
int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl); int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl);
int dp_ctrl_off_link_stream(struct dp_ctrl *dp_ctrl);
void dp_ctrl_off_phy(struct dp_ctrl *dp_ctrl);
int dp_ctrl_off(struct dp_ctrl *dp_ctrl); int dp_ctrl_off(struct dp_ctrl *dp_ctrl);
void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl); void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl);
void dp_ctrl_isr(struct dp_ctrl *dp_ctrl); void dp_ctrl_isr(struct dp_ctrl *dp_ctrl);
......
...@@ -346,6 +346,12 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) ...@@ -346,6 +346,12 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
dp->dp_display.max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ; dp->dp_display.max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ;
dp->dp_display.max_dp_lanes = dp->parser->max_dp_lanes; dp->dp_display.max_dp_lanes = dp->parser->max_dp_lanes;
/*
* set sink to normal operation mode -- D0
* before dpcd read
*/
dp_link_psm_config(dp->link, &dp->panel->link_info, false);
dp_link_reset_phy_params_vx_px(dp->link); dp_link_reset_phy_params_vx_px(dp->link);
rc = dp_ctrl_on_link(dp->ctrl); rc = dp_ctrl_on_link(dp->ctrl);
if (rc) { if (rc) {
...@@ -414,11 +420,6 @@ static int dp_display_usbpd_configure_cb(struct device *dev) ...@@ -414,11 +420,6 @@ static int dp_display_usbpd_configure_cb(struct device *dev)
dp_display_host_init(dp, false); dp_display_host_init(dp, false);
/*
* set sink to normal operation mode -- D0
* before dpcd read
*/
dp_link_psm_config(dp->link, &dp->panel->link_info, false);
rc = dp_display_process_hpd_high(dp); rc = dp_display_process_hpd_high(dp);
end: end:
return rc; return rc;
...@@ -579,6 +580,10 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data) ...@@ -579,6 +580,10 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data)
dp_add_event(dp, EV_CONNECT_PENDING_TIMEOUT, 0, tout); dp_add_event(dp, EV_CONNECT_PENDING_TIMEOUT, 0, tout);
} }
/* enable HDP irq_hpd/replug interrupt */
dp_catalog_hpd_config_intr(dp->catalog,
DP_DP_IRQ_HPD_INT_MASK | DP_DP_HPD_REPLUG_INT_MASK, true);
mutex_unlock(&dp->event_mutex); mutex_unlock(&dp->event_mutex);
/* uevent will complete connection part */ /* uevent will complete connection part */
...@@ -628,7 +633,26 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data) ...@@ -628,7 +633,26 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
mutex_lock(&dp->event_mutex); mutex_lock(&dp->event_mutex);
state = dp->hpd_state; state = dp->hpd_state;
if (state == ST_DISCONNECT_PENDING || state == ST_DISCONNECTED) {
/* disable irq_hpd/replug interrupts */
dp_catalog_hpd_config_intr(dp->catalog,
DP_DP_IRQ_HPD_INT_MASK | DP_DP_HPD_REPLUG_INT_MASK, false);
/* unplugged, no more irq_hpd handle */
dp_del_event(dp, EV_IRQ_HPD_INT);
if (state == ST_DISCONNECTED) {
/* triggered by irq_hdp with sink_count = 0 */
if (dp->link->sink_count == 0) {
dp_ctrl_off_phy(dp->ctrl);
hpd->hpd_high = 0;
dp->core_initialized = false;
}
mutex_unlock(&dp->event_mutex);
return 0;
}
if (state == ST_DISCONNECT_PENDING) {
mutex_unlock(&dp->event_mutex); mutex_unlock(&dp->event_mutex);
return 0; return 0;
} }
...@@ -642,9 +666,8 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data) ...@@ -642,9 +666,8 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
dp->hpd_state = ST_DISCONNECT_PENDING; dp->hpd_state = ST_DISCONNECT_PENDING;
/* disable HPD plug interrupt until disconnect is done */ /* disable HPD plug interrupts */
dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK, false);
| DP_DP_IRQ_HPD_INT_MASK, false);
hpd->hpd_high = 0; hpd->hpd_high = 0;
...@@ -660,8 +683,8 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data) ...@@ -660,8 +683,8 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)
/* signal the disconnect event early to ensure proper teardown */ /* signal the disconnect event early to ensure proper teardown */
dp_display_handle_plugged_change(g_dp_display, false); dp_display_handle_plugged_change(g_dp_display, false);
dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK | /* enable HDP plug interrupt to prepare for next plugin */
DP_DP_IRQ_HPD_INT_MASK, true); dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK, true);
/* uevent will complete disconnection part */ /* uevent will complete disconnection part */
mutex_unlock(&dp->event_mutex); mutex_unlock(&dp->event_mutex);
...@@ -692,7 +715,7 @@ static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data) ...@@ -692,7 +715,7 @@ static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data)
/* irq_hpd can happen at either connected or disconnected state */ /* irq_hpd can happen at either connected or disconnected state */
state = dp->hpd_state; state = dp->hpd_state;
if (state == ST_DISPLAY_OFF) { if (state == ST_DISPLAY_OFF || state == ST_SUSPENDED) {
mutex_unlock(&dp->event_mutex); mutex_unlock(&dp->event_mutex);
return 0; return 0;
} }
...@@ -910,9 +933,13 @@ static int dp_display_disable(struct dp_display_private *dp, u32 data) ...@@ -910,9 +933,13 @@ static int dp_display_disable(struct dp_display_private *dp, u32 data)
dp_display->audio_enabled = false; dp_display->audio_enabled = false;
dp_ctrl_off(dp->ctrl); /* triggered by irq_hpd with sink_count = 0 */
if (dp->link->sink_count == 0) {
dp->core_initialized = false; dp_ctrl_off_link_stream(dp->ctrl);
} else {
dp_ctrl_off(dp->ctrl);
dp->core_initialized = false;
}
dp_display->power_on = false; dp_display->power_on = false;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment