summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAjay Singh Parmar <aparmar@codeaurora.org>2016-10-26 15:59:21 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2016-10-27 15:18:22 -0700
commitdec2c9c98d4315290da22136f63e7ac561aefa74 (patch)
tree3aeba0b3def501198cc4f4f4f665a9b6f104b451
parentdc10995a1b0c5ee71ae0221d72018eeac72bf30a (diff)
msm: mdss: dp: add support for link retraining at lower link rate
Add support to restart link training at a lower link rate if the training has failed at the current rate in Clock Recovery phase or has reached the maximum number of retries in the Channel Equalization phase. Change-Id: Ic7ac0b7ac19d19577d4d1223c8638f17ad9d78af Signed-off-by: Ajay Singh Parmar <aparmar@codeaurora.org>
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c109
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c89
-rw-r--r--drivers/video/fbdev/msm/msm_ext_display.c8
3 files changed, 120 insertions, 86 deletions
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c
index dbaf3247e8bb..391d4c24eb7f 100644
--- a/drivers/video/fbdev/msm/mdss_dp.c
+++ b/drivers/video/fbdev/msm/mdss_dp.c
@@ -57,6 +57,10 @@ static u32 supported_modes[] = {
HDMI_VFRMT_4096x2160p60_256_135, HDMI_EVFRMT_4096x2160p24_16_9
};
+static int mdss_dp_off_irq(struct mdss_dp_drv_pdata *dp_drv);
+static void mdss_dp_mainlink_push_idle(struct mdss_panel_data *pdata);
+static inline void mdss_dp_link_retraining(struct mdss_dp_drv_pdata *dp);
+
static void mdss_dp_put_dt_clk_data(struct device *dev,
struct dss_module_power *module_power)
{
@@ -1156,19 +1160,27 @@ static void mdss_dp_configure_source_params(struct mdss_dp_drv_pdata *dp,
*
* Initiates training of the DP main link and checks the state of the main
* link after the training is complete.
+ *
+ * Return: error code. -EINVAL if any invalid data or -EAGAIN if retraining
+ * is required.
*/
-static void mdss_dp_train_main_link(struct mdss_dp_drv_pdata *dp)
+static int mdss_dp_train_main_link(struct mdss_dp_drv_pdata *dp)
{
+ int ret = 0;
int ready = 0;
pr_debug("enter\n");
+ ret = mdss_dp_link_train(dp);
+ if (ret)
+ goto end;
- mdss_dp_link_train(dp);
mdss_dp_wait4train(dp);
ready = mdss_dp_mainlink_ready(dp, BIT(0));
pr_debug("main link %s\n", ready ? "READY" : "NOT READY");
+end:
+ return ret;
}
static int mdss_dp_on_irq(struct mdss_dp_drv_pdata *dp_drv)
@@ -1178,33 +1190,43 @@ static int mdss_dp_on_irq(struct mdss_dp_drv_pdata *dp_drv)
struct lane_mapping ln_map;
/* wait until link training is completed */
- mutex_lock(&dp_drv->train_mutex);
-
pr_debug("enter\n");
- orientation = usbpd_get_plug_orientation(dp_drv->pd);
- pr_debug("plug orientation = %d\n", orientation);
+ do {
+ if (ret == -EAGAIN) {
+ mdss_dp_mainlink_push_idle(&dp_drv->panel_data);
+ mdss_dp_off_irq(dp_drv);
+ }
- ret = mdss_dp_get_lane_mapping(dp_drv, orientation, &ln_map);
- if (ret)
- goto exit;
+ mutex_lock(&dp_drv->train_mutex);
- mdss_dp_phy_share_lane_config(&dp_drv->phy_io,
- orientation, dp_drv->dpcd.max_lane_count);
+ orientation = usbpd_get_plug_orientation(dp_drv->pd);
+ pr_debug("plug orientation = %d\n", orientation);
- ret = mdss_dp_enable_mainlink_clocks(dp_drv);
- if (ret)
- goto exit;
+ ret = mdss_dp_get_lane_mapping(dp_drv, orientation, &ln_map);
+ if (ret)
+ goto exit;
- mdss_dp_mainlink_reset(&dp_drv->ctrl_io);
+ mdss_dp_phy_share_lane_config(&dp_drv->phy_io,
+ orientation, dp_drv->dpcd.max_lane_count);
- reinit_completion(&dp_drv->idle_comp);
+ ret = mdss_dp_enable_mainlink_clocks(dp_drv);
+ if (ret)
+ goto exit;
- mdss_dp_configure_source_params(dp_drv, &ln_map);
+ mdss_dp_mainlink_reset(&dp_drv->ctrl_io);
- mdss_dp_train_main_link(dp_drv);
+ reinit_completion(&dp_drv->idle_comp);
+
+ mdss_dp_configure_source_params(dp_drv, &ln_map);
+
+ dp_drv->power_on = true;
+
+ ret = mdss_dp_train_main_link(dp_drv);
+
+ mutex_unlock(&dp_drv->train_mutex);
+ } while (ret == -EAGAIN);
- dp_drv->power_on = true;
pr_debug("end\n");
exit:
@@ -1273,7 +1295,14 @@ int mdss_dp_on_hpd(struct mdss_dp_drv_pdata *dp_drv)
mdss_dp_configure_source_params(dp_drv, &ln_map);
link_training:
- mdss_dp_train_main_link(dp_drv);
+ dp_drv->power_on = true;
+
+ if (-EAGAIN == mdss_dp_train_main_link(dp_drv)) {
+ mutex_unlock(&dp_drv->train_mutex);
+
+ mdss_dp_link_retraining(dp_drv);
+ return 0;
+ }
dp_drv->cont_splash = 0;
@@ -1419,18 +1448,23 @@ int mdss_dp_off(struct mdss_panel_data *pdata)
return mdss_dp_off_hpd(dp);
}
-static void mdss_dp_send_cable_notification(
+static int mdss_dp_send_cable_notification(
struct mdss_dp_drv_pdata *dp, int val)
{
+ int ret = 0;
if (!dp) {
DEV_ERR("%s: invalid input\n", __func__);
- return;
+ ret = -EINVAL;
+ goto end;
}
if (dp && dp->ext_audio_data.intf_ops.hpd)
- dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev,
+ ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev,
dp->ext_audio_data.type, val);
+
+end:
+ return ret;
}
static void mdss_dp_audio_codec_wait(struct mdss_dp_drv_pdata *dp)
@@ -1450,18 +1484,22 @@ static void mdss_dp_audio_codec_wait(struct mdss_dp_drv_pdata *dp)
dp->wait_for_audio_comp = false;
}
-static void mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, bool enable)
+static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, bool enable)
{
+ int notified = 0;
+
if (enable) {
- mdss_dp_send_cable_notification(dp, enable);
+ notified = mdss_dp_send_cable_notification(dp, enable);
} else {
mdss_dp_set_audio_switch_node(dp, enable);
mdss_dp_audio_codec_wait(dp);
- mdss_dp_send_cable_notification(dp, enable);
+ notified = mdss_dp_send_cable_notification(dp, enable);
}
pr_debug("notify state %s done\n",
enable ? "ENABLE" : "DISABLE");
+
+ return notified;
}
@@ -2336,15 +2374,20 @@ static int mdss_dp_hpd_irq_notify_clients(struct mdss_dp_drv_pdata *dp)
int ret = 0;
if (dp->hpd_irq_toggled) {
- mdss_dp_notify_clients(dp, false);
dp->hpd_irq_clients_notified = true;
- reinit_completion(&dp->irq_comp);
- ret = wait_for_completion_timeout(&dp->irq_comp,
- irq_comp_timeout);
- if (ret <= 0) {
- pr_warn("irq_comp timed out\n");
- return -EINVAL;
+ ret = mdss_dp_notify_clients(dp, false);
+
+ if (!IS_ERR_VALUE(ret) && ret) {
+ reinit_completion(&dp->irq_comp);
+ ret = wait_for_completion_timeout(&dp->irq_comp,
+ irq_comp_timeout);
+ if (ret <= 0) {
+ pr_warn("irq_comp timed out\n");
+ ret = -EINVAL;
+ } else {
+ ret = 0;
+ }
}
}
diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c
index 25f726a4c05f..9014e3a02d21 100644
--- a/drivers/video/fbdev/msm/mdss_dp_aux.c
+++ b/drivers/video/fbdev/msm/mdss_dp_aux.c
@@ -801,6 +801,8 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
cap = &ep->dpcd;
bp = rp->data;
+ memset(cap, 0, sizeof(*cap));
+
data = *bp++; /* byte 0 */
cap->major = (data >> 4) & 0x0f;
cap->minor = data & 0x0f;
@@ -819,8 +821,13 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
if (data & BIT(7))
cap->enhanced_frame++;
- if (data & 0x40)
+ if (data & 0x40) {
cap->flags |= DPCD_TPS3;
+ pr_debug("pattern 3 supported\n");
+ } else {
+ pr_debug("pattern 3 not supported\n");
+ }
+
data &= 0x0f;
cap->max_lane_count = data;
if (--rlen <= 0)
@@ -886,7 +893,7 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
data = *bp++; /* byte 9 */
cap->rx_port0_buf_size = (data + 1) * 32;
- pr_debug("lane_buf_size=%d", cap->rx_port0_buf_size);
+ pr_debug("lane_buf_size=%d\n", cap->rx_port0_buf_size);
if (--rlen <= 0)
return;
@@ -1456,6 +1463,7 @@ static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep)
int tries, old_v_level;
int ret = 0;
int usleep_time;
+ int const maximum_retries = 5;
pr_debug("Entered++");
@@ -1483,7 +1491,7 @@ static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep)
if (old_v_level == ep->v_level) {
tries++;
- if (tries >= 5) {
+ if (tries >= maximum_retries) {
ret = -1;
break; /* quit */
}
@@ -1505,6 +1513,7 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep)
int ret = 0;
int usleep_time;
char pattern;
+ int const maximum_retries = 5;
pr_debug("Entered++");
@@ -1530,7 +1539,7 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep)
}
tries++;
- if (tries > 4) {
+ if (tries > maximum_retries) {
ret = -1;
break;
}
@@ -1543,47 +1552,27 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep)
static int dp_link_rate_down_shift(struct mdss_dp_drv_pdata *ep)
{
- u32 prate, lrate;
- int rate, lane, max_lane;
- int changed = 0;
-
- rate = ep->link_rate;
- lane = ep->lane_cnt;
- max_lane = ep->dpcd.max_lane_count;
-
- prate = ep->pixel_rate;
- prate /= 1000; /* avoid using 64 biits */
- prate *= ep->bpp;
- prate /= 8; /* byte */
-
- if (rate > DP_LINK_RATE_162 && rate <= DP_LINK_RATE_MAX) {
- rate -= 4; /* reduce rate */
- changed++;
- }
-
- if (changed) {
- if (lane >= 1 && lane < max_lane)
- lane <<= 1; /* increase lane */
+ int ret = 0;
- lrate = 270000000; /* 270M */
- lrate /= 1000; /* avoid using 64 bits */
- lrate *= rate;
- lrate /= 10; /* byte, 10 bits --> 8 bits */
- lrate *= lane;
+ if (!ep)
+ return -EINVAL;
- pr_debug("new lrate=%u prate=%u rate=%d lane=%d p=%d b=%d\n",
- lrate, prate, rate, lane, ep->pixel_rate, ep->bpp);
+ switch (ep->link_rate) {
+ case DP_LINK_RATE_540:
+ ep->link_rate = DP_LINK_RATE_270;
+ break;
+ case DP_LINK_RATE_270:
+ ep->link_rate = DP_LINK_RATE_162;
+ break;
+ case DP_LINK_RATE_162:
+ default:
+ ret = -EINVAL;
+ break;
+ };
- if (lrate > prate) {
- ep->link_rate = rate;
- ep->lane_cnt = lane;
- pr_debug("new rate=%d %d\n", rate, lane);
- return 0;
- }
- }
+ pr_debug("new rate=%d\n", ep->link_rate);
- /* add calculation later */
- return -EINVAL;
+ return ret;
}
int mdss_dp_aux_set_sink_power_state(struct mdss_dp_drv_pdata *ep, char state)
@@ -1620,7 +1609,6 @@ int mdss_dp_link_train(struct mdss_dp_drv_pdata *dp)
mdss_dp_aux_set_sink_power_state(dp, SINK_POWER_ON);
-train_start:
dp->v_level = 0; /* start from default level */
dp->p_level = 0;
mdss_dp_config_ctrl(dp);
@@ -1630,11 +1618,12 @@ train_start:
ret = dp_start_link_train_1(dp);
if (ret < 0) {
- if (dp_link_rate_down_shift(dp) == 0) {
- goto train_start;
+ if (!dp_link_rate_down_shift(dp)) {
+ pr_debug("retry with lower rate\n");
+ return -EAGAIN;
} else {
pr_err("Training 1 failed\n");
- ret = -1;
+ ret = -EINVAL;
goto clear;
}
}
@@ -1643,21 +1632,21 @@ train_start:
ret = dp_start_link_train_2(dp);
if (ret < 0) {
- if (dp_link_rate_down_shift(dp) == 0) {
- goto train_start;
+ if (!dp_link_rate_down_shift(dp)) {
+ pr_debug("retry with lower rate\n");
+ return -EAGAIN;
} else {
pr_err("Training 2 failed\n");
- ret = -1;
+ ret = -EINVAL;
goto clear;
}
}
pr_debug("Training 2 completed successfully\n");
-
clear:
dp_clear_training_pattern(dp);
- if (ret != -1) {
+ if (ret != -EINVAL) {
mdss_dp_setup_tr_unit(&dp->ctrl_io, dp->link_rate,
dp->lane_cnt, dp->vic);
mdss_dp_state_ctrl(&dp->ctrl_io, ST_SEND_VIDEO);
diff --git a/drivers/video/fbdev/msm/msm_ext_display.c b/drivers/video/fbdev/msm/msm_ext_display.c
index f6c6548bdaa5..36a5530cb761 100644
--- a/drivers/video/fbdev/msm/msm_ext_display.c
+++ b/drivers/video/fbdev/msm/msm_ext_display.c
@@ -313,14 +313,14 @@ static void msm_ext_disp_remove_intf_data(struct msm_ext_disp *ext_disp,
}
}
-static void msm_ext_disp_send_cable_notification(struct msm_ext_disp *ext_disp,
+static int msm_ext_disp_send_cable_notification(struct msm_ext_disp *ext_disp,
enum msm_ext_disp_cable_state new_state)
{
int state = EXT_DISPLAY_CABLE_STATE_MAX;
if (!ext_disp) {
pr_err("Invalid params\n");
- return;
+ return -EINVAL;
}
state = ext_disp->hdmi_sdev.state;
@@ -330,6 +330,8 @@ static void msm_ext_disp_send_cable_notification(struct msm_ext_disp *ext_disp,
ext_disp->hdmi_sdev.state == state ?
"is same" : "switched to",
ext_disp->hdmi_sdev.state);
+
+ return ext_disp->hdmi_sdev.state == state ? 0 : 1;
}
static int msm_ext_disp_hpd(struct platform_device *pdev,
@@ -398,7 +400,7 @@ static int msm_ext_disp_hpd(struct platform_device *pdev,
ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX;
}
- msm_ext_disp_send_cable_notification(ext_disp, state);
+ ret = msm_ext_disp_send_cable_notification(ext_disp, state);
pr_debug("Hpd (%d) for display (%s)\n", state,
msm_ext_disp_name(type));