summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorKuogee Hsieh <khsieh@codeaurora.org>2020-09-11 20:36:42 +0000
committerRob Clark <robdclark@chromium.org>2020-09-15 10:54:34 -0700
commit8ede2ecc3e5ee327923f6e3cfe52761ce73607d1 (patch)
tree2c36060e4f032d16e90e1480f790c359b06f5350 /drivers
parent220b856a3d3742a22831cb6cd94e48133a58d30e (diff)
drm/msm/dp: Add DP compliance tests on Snapdragon Chipsets
add event thread to execute events serially from event queue. Also timeout mode is supported which allow an event be deferred to be executed at later time. Both link and phy compliant tests had been done successfully. Changes in v2: -- Fix potential deadlock by removing redundant connect_mutex -- Check and enable link clock during modeset -- Drop unused code and fix function prototypes. -- set sink power to normal operation state (D0) before DPCD read Changes in v3: -- push idle pattern at main link before timing generator off -- add timeout handles for both connect and disconnect Changes in v4: -- add ST_SUSPEND_PENDING to handles suspend/modeset test operations -- clear dp phy aux interrupt status when ERR_DPPHY_AUX error -- send segment addr during edid read -- clear bpp depth before MISC register write Changes in v5: -- add ST_SUSPENDED to fix crash at resume Changes in v6: -- at msm_dp_display_enable() do not return until resume_done to avoid kms commit timeout Signed-off-by: Kuogee Hsieh <khsieh@codeaurora.org> Signed-off-by: Rob Clark <robdclark@chromium.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c6
-rw-r--r--drivers/gpu/drm/msm/dp/dp_aux.c1
-rw-r--r--drivers/gpu/drm/msm/dp/dp_catalog.c77
-rw-r--r--drivers/gpu/drm/msm/dp/dp_ctrl.c370
-rw-r--r--drivers/gpu/drm/msm/dp/dp_ctrl.h3
-rw-r--r--drivers/gpu/drm/msm/dp/dp_display.c828
-rw-r--r--drivers/gpu/drm/msm/dp/dp_display.h1
-rw-r--r--drivers/gpu/drm/msm/dp/dp_drm.c4
-rw-r--r--drivers/gpu/drm/msm/dp/dp_hpd.c2
-rw-r--r--drivers/gpu/drm/msm/dp/dp_hpd.h1
-rw-r--r--drivers/gpu/drm/msm/dp/dp_link.c22
-rw-r--r--drivers/gpu/drm/msm/dp/dp_panel.c78
-rw-r--r--drivers/gpu/drm/msm/dp/dp_panel.h9
-rw-r--r--drivers/gpu/drm/msm/dp/dp_parser.c42
-rw-r--r--drivers/gpu/drm/msm/dp/dp_parser.h2
-rw-r--r--drivers/gpu/drm/msm/dp/dp_power.c46
-rw-r--r--drivers/gpu/drm/msm/dp/dp_power.h13
-rw-r--r--drivers/gpu/drm/msm/dp/dp_reg.h1
-rw-r--r--drivers/gpu/drm/msm/msm_drv.h1
19 files changed, 1050 insertions, 457 deletions
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index 1d12e7cd11fe..702a29040958 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -1225,6 +1225,11 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
/* wait for idle */
dpu_encoder_wait_for_event(drm_enc, MSM_ENC_TX_COMPLETE);
+ if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && priv->dp) {
+ if (msm_dp_display_pre_disable(priv->dp, drm_enc))
+ DPU_ERROR_ENC(dpu_enc, "dp display push idle failed\n");
+ }
+
dpu_encoder_resource_control(drm_enc, DPU_ENC_RC_EVENT_PRE_STOP);
for (i = 0; i < dpu_enc->num_phys_encs; i++) {
@@ -1234,6 +1239,7 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
phys->ops.disable(phys);
}
+
/* after phys waits for frame-done, should be no more frames pending */
if (atomic_xchg(&dpu_enc->frame_done_timeout_ms, 0)) {
DPU_ERROR("enc%d timeout pending\n", drm_enc->base.id);
diff --git a/drivers/gpu/drm/msm/dp/dp_aux.c b/drivers/gpu/drm/msm/dp/dp_aux.c
index 6bf3a5712968..d742b4d870b3 100644
--- a/drivers/gpu/drm/msm/dp/dp_aux.c
+++ b/drivers/gpu/drm/msm/dp/dp_aux.c
@@ -384,6 +384,7 @@ static ssize_t dp_aux_transfer(struct drm_dp_aux *dp_aux,
PHY_AUX_CFG1);
dp_catalog_aux_reset(aux->catalog);
}
+ usleep_range(400, 500); /* at least 400us to next try */
goto unlock_exit;
}
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index c16072630d40..67abb90953e4 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -536,16 +536,21 @@ void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog *dp_catalog,
* To make sure link reg writes happens before other operation,
* dp_write_link() function uses writel()
*/
- dp_write_link(catalog, REG_DP_MAINLINK_CTRL,
- DP_MAINLINK_FB_BOUNDARY_SEL);
- dp_write_link(catalog, REG_DP_MAINLINK_CTRL,
- DP_MAINLINK_FB_BOUNDARY_SEL |
- DP_MAINLINK_CTRL_RESET);
- dp_write_link(catalog, REG_DP_MAINLINK_CTRL,
+ mainlink_ctrl = dp_read_link(catalog, REG_DP_MAINLINK_CTRL);
+
+ mainlink_ctrl &= ~(DP_MAINLINK_CTRL_RESET |
+ DP_MAINLINK_CTRL_ENABLE);
+ dp_write_link(catalog, REG_DP_MAINLINK_CTRL, mainlink_ctrl);
+
+ mainlink_ctrl |= DP_MAINLINK_CTRL_RESET;
+ dp_write_link(catalog, REG_DP_MAINLINK_CTRL, mainlink_ctrl);
+
+ mainlink_ctrl &= ~DP_MAINLINK_CTRL_RESET;
+ dp_write_link(catalog, REG_DP_MAINLINK_CTRL, mainlink_ctrl);
+
+ mainlink_ctrl |= (DP_MAINLINK_CTRL_ENABLE |
DP_MAINLINK_FB_BOUNDARY_SEL);
- dp_write_link(catalog, REG_DP_MAINLINK_CTRL,
- DP_MAINLINK_FB_BOUNDARY_SEL |
- DP_MAINLINK_CTRL_ENABLE);
+ dp_write_link(catalog, REG_DP_MAINLINK_CTRL, mainlink_ctrl);
} else {
mainlink_ctrl = dp_read_link(catalog, REG_DP_MAINLINK_CTRL);
mainlink_ctrl &= ~DP_MAINLINK_CTRL_ENABLE;
@@ -644,7 +649,7 @@ int dp_catalog_ctrl_set_pattern(struct dp_catalog *dp_catalog,
bit = BIT(pattern - 1);
DRM_DEBUG_DP("hw: bit=%d train=%d\n", bit, pattern);
- dp_write_link(catalog, REG_DP_STATE_CTRL, bit);
+ dp_catalog_ctrl_state_ctrl(dp_catalog, bit);
bit = BIT(pattern - 1) << DP_MAINLINK_READY_LINK_TRAINING_SHIFT;
@@ -769,7 +774,7 @@ void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog)
/* enable HPD interrupts */
dp_catalog_hpd_config_intr(dp_catalog,
DP_DP_HPD_PLUG_INT_MASK | DP_DP_IRQ_HPD_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 */
reftimer |= DP_DP_HPD_REFTIMER_ENABLE;
@@ -881,15 +886,27 @@ void dp_catalog_ctrl_send_phy_pattern(struct dp_catalog *dp_catalog,
dp_write_link(catalog, REG_DP_STATE_CTRL, 0x0);
switch (pattern) {
- case DP_LINK_QUAL_PATTERN_D10_2:
+ case DP_PHY_TEST_PATTERN_D10_2:
dp_write_link(catalog, REG_DP_STATE_CTRL,
DP_STATE_CTRL_LINK_TRAINING_PATTERN1);
- return;
- case DP_LINK_QUAL_PATTERN_PRBS7:
+ break;
+ case DP_PHY_TEST_PATTERN_ERROR_COUNT:
+ value &= ~(1 << 16);
+ dp_write_link(catalog, REG_DP_HBR2_COMPLIANCE_SCRAMBLER_RESET,
+ value);
+ value |= SCRAMBLER_RESET_COUNT_VALUE;
+ dp_write_link(catalog, REG_DP_HBR2_COMPLIANCE_SCRAMBLER_RESET,
+ value);
+ dp_write_link(catalog, REG_DP_MAINLINK_LEVELS,
+ DP_MAINLINK_SAFE_TO_EXIT_LEVEL_2);
+ dp_write_link(catalog, REG_DP_STATE_CTRL,
+ DP_STATE_CTRL_LINK_SYMBOL_ERR_MEASURE);
+ break;
+ case DP_PHY_TEST_PATTERN_PRBS7:
dp_write_link(catalog, REG_DP_STATE_CTRL,
DP_STATE_CTRL_LINK_PRBS7);
- return;
- case DP_LINK_QUAL_PATTERN_80BIT_CUSTOM:
+ break;
+ case DP_PHY_TEST_PATTERN_80BIT_CUSTOM:
dp_write_link(catalog, REG_DP_STATE_CTRL,
DP_STATE_CTRL_LINK_TEST_CUSTOM_PATTERN);
/* 00111110000011111000001111100000 */
@@ -901,14 +918,15 @@ void dp_catalog_ctrl_send_phy_pattern(struct dp_catalog *dp_catalog,
/* 1111100000111110 */
dp_write_link(catalog, REG_DP_TEST_80BIT_CUSTOM_PATTERN_REG2,
0x0000F83E);
- return;
- case DP_LINK_QUAL_PATTERN_HBR2_EYE:
- case DP_LINK_QUAL_PATTERN_ERROR_RATE:
- value &= ~DP_HBR2_ERM_PATTERN;
- if (pattern == DP_LINK_QUAL_PATTERN_HBR2_EYE)
- value = DP_HBR2_ERM_PATTERN;
+ break;
+ case DP_PHY_TEST_PATTERN_CP2520:
+ value = dp_read_link(catalog, REG_DP_MAINLINK_CTRL);
+ value &= ~DP_MAINLINK_CTRL_SW_BYPASS_SCRAMBLER;
+ dp_write_link(catalog, REG_DP_MAINLINK_CTRL, value);
+
+ value = DP_HBR2_ERM_PATTERN;
dp_write_link(catalog, REG_DP_HBR2_COMPLIANCE_SCRAMBLER_RESET,
- value);
+ value);
value |= SCRAMBLER_RESET_COUNT_VALUE;
dp_write_link(catalog, REG_DP_HBR2_COMPLIANCE_SCRAMBLER_RESET,
value);
@@ -916,10 +934,19 @@ void dp_catalog_ctrl_send_phy_pattern(struct dp_catalog *dp_catalog,
DP_MAINLINK_SAFE_TO_EXIT_LEVEL_2);
dp_write_link(catalog, REG_DP_STATE_CTRL,
DP_STATE_CTRL_LINK_SYMBOL_ERR_MEASURE);
- return;
+ value = dp_read_link(catalog, REG_DP_MAINLINK_CTRL);
+ value |= DP_MAINLINK_CTRL_ENABLE;
+ dp_write_link(catalog, REG_DP_MAINLINK_CTRL, value);
+ break;
+ case DP_PHY_TEST_PATTERN_SEL_MASK:
+ dp_write_link(catalog, REG_DP_MAINLINK_CTRL,
+ DP_MAINLINK_CTRL_ENABLE);
+ dp_write_link(catalog, REG_DP_STATE_CTRL,
+ DP_STATE_CTRL_LINK_TRAINING_PATTERN4);
+ break;
default:
DRM_DEBUG_DP("No valid test pattern requested:0x%x\n", pattern);
- return;
+ break;
}
}
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index ae07e43b541b..70b0e06953f6 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -26,6 +26,13 @@
#define MR_LINK_SYMBOL_ERM 0x80
#define MR_LINK_PRBS7 0x100
#define MR_LINK_CUSTOM80 0x200
+#define MR_LINK_TRAINING4 0x40
+
+enum {
+ DP_TRAINING_NONE,
+ DP_TRAINING_1,
+ DP_TRAINING_2,
+};
struct dp_tu_calc_input {
u64 lclk; /* 162, 270, 540 and 810 */
@@ -58,7 +65,6 @@ struct dp_vc_tu_mapping_table {
struct dp_ctrl_private {
struct dp_ctrl dp_ctrl;
-
struct device *dev;
struct drm_dp_aux *aux;
struct dp_panel *panel;
@@ -68,10 +74,16 @@ struct dp_ctrl_private {
struct dp_catalog *catalog;
struct completion idle_comp;
- struct mutex push_idle_mutex;
struct completion video_comp;
};
+struct dp_cr_status {
+ u8 lane_0_1;
+ u8 lane_2_3;
+};
+
+#define DP_LANE0_1_CR_DONE 0x11
+
static int dp_aux_link_configure(struct drm_dp_aux *aux,
struct dp_link_info *link)
{
@@ -97,8 +109,6 @@ void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl)
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
- mutex_lock(&ctrl->push_idle_mutex);
-
reinit_completion(&ctrl->idle_comp);
dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_PUSH_IDLE);
@@ -106,7 +116,6 @@ void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl)
IDLE_PATTERN_COMPLETION_TIMEOUT_JIFFIES))
pr_warn("PUSH_IDLE pattern timedout\n");
- mutex_unlock(&ctrl->push_idle_mutex);
pr_debug("mainlink off done\n");
}
@@ -979,7 +988,7 @@ static int dp_ctrl_wait4video_ready(struct dp_ctrl_private *ctrl)
if (!wait_for_completion_timeout(&ctrl->video_comp,
WAIT_FOR_VIDEO_READY_TIMEOUT_JIFFIES)) {
- DRM_ERROR("Link Train timedout\n");
+ DRM_ERROR("wait4video timedout\n");
ret = -ETIMEDOUT;
}
return ret;
@@ -1000,13 +1009,13 @@ static int dp_ctrl_update_vx_px(struct dp_ctrl_private *ctrl)
if (ret)
return ret;
- if (voltage_swing_level > DP_TRAIN_VOLTAGE_SWING_MAX) {
+ if (voltage_swing_level >= DP_TRAIN_VOLTAGE_SWING_MAX) {
DRM_DEBUG_DP("max. voltage swing level reached %d\n",
voltage_swing_level);
max_level_reached |= DP_TRAIN_MAX_SWING_REACHED;
}
- if (pre_emphasis_level == DP_TRAIN_PRE_EMPHASIS_MAX) {
+ if (pre_emphasis_level >= DP_TRAIN_PRE_EMPHASIS_MAX) {
DRM_DEBUG_DP("max. pre-emphasis level reached %d\n",
pre_emphasis_level);
max_level_reached |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
@@ -1038,8 +1047,11 @@ static bool dp_ctrl_train_pattern_set(struct dp_ctrl_private *ctrl,
DRM_DEBUG_DP("sink: pattern=%x\n", pattern);
buf = pattern;
- ret = drm_dp_dpcd_writeb(ctrl->aux,
- DP_TRAINING_PATTERN_SET, buf);
+
+ if (pattern && pattern != DP_TRAINING_PATTERN_4)
+ buf |= DP_LINK_SCRAMBLING_DISABLE;
+
+ ret = drm_dp_dpcd_writeb(ctrl->aux, DP_TRAINING_PATTERN_SET, buf);
return ret == 1;
}
@@ -1065,19 +1077,23 @@ static int dp_ctrl_read_link_status(struct dp_ctrl_private *ctrl,
return -ETIMEDOUT;
}
-static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl)
+static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl,
+ struct dp_cr_status *cr, int *training_step)
{
int tries, old_v_level, ret = 0;
u8 link_status[DP_LINK_STATUS_SIZE];
- int const maximum_retries = 5;
+ int const maximum_retries = 4;
dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0);
+ *training_step = DP_TRAINING_1;
+
ret = dp_catalog_ctrl_set_pattern(ctrl->catalog, DP_TRAINING_PATTERN_1);
if (ret)
return ret;
dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_1 |
DP_LINK_SCRAMBLING_DISABLE);
+
ret = dp_ctrl_update_vx_px(ctrl);
if (ret)
return ret;
@@ -1091,12 +1107,15 @@ static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl)
if (ret)
return ret;
+ cr->lane_0_1 = link_status[0];
+ cr->lane_2_3 = link_status[1];
+
if (drm_dp_clock_recovery_ok(link_status,
ctrl->link->link_params.num_lanes)) {
- return ret;
+ return 0;
}
- if (ctrl->link->phy_params.v_level >
+ if (ctrl->link->phy_params.v_level >=
DP_TRAIN_VOLTAGE_SWING_MAX) {
DRM_ERROR_RATELIMITED("max v_level reached\n");
return -EAGAIN;
@@ -1119,8 +1138,10 @@ static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl)
return -ETIMEDOUT;
}
-static void dp_ctrl_link_rate_down_shift(struct dp_ctrl_private *ctrl)
+static int dp_ctrl_link_rate_down_shift(struct dp_ctrl_private *ctrl)
{
+ int ret = 0;
+
switch (ctrl->link->link_params.rate) {
case 810000:
ctrl->link->link_params.rate = 540000;
@@ -1129,13 +1150,33 @@ static void dp_ctrl_link_rate_down_shift(struct dp_ctrl_private *ctrl)
ctrl->link->link_params.rate = 270000;
break;
case 270000:
+ ctrl->link->link_params.rate = 162000;
+ break;
case 162000:
default:
- ctrl->link->link_params.rate = 162000;
+ ret = -EINVAL;
break;
};
- DRM_DEBUG_DP("new rate=0x%x\n", ctrl->link->link_params.rate);
+ if (!ret)
+ DRM_DEBUG_DP("new rate=0x%x\n", ctrl->link->link_params.rate);
+
+ return ret;
+}
+
+static int dp_ctrl_link_lane_down_shift(struct dp_ctrl_private *ctrl)
+{
+
+ if (ctrl->link->link_params.num_lanes == 1)
+ return -1;
+
+ ctrl->link->link_params.num_lanes /= 2;
+ ctrl->link->link_params.rate = ctrl->panel->link_info.rate;
+
+ ctrl->link->phy_params.p_level = 0;
+ ctrl->link->phy_params.v_level = 0;
+
+ return 0;
}
static void dp_ctrl_clear_training_pattern(struct dp_ctrl_private *ctrl)
@@ -1144,7 +1185,8 @@ static void dp_ctrl_clear_training_pattern(struct dp_ctrl_private *ctrl)
drm_dp_link_train_channel_eq_delay(ctrl->panel->dpcd);
}
-static int dp_ctrl_link_training_2(struct dp_ctrl_private *ctrl)
+static int dp_ctrl_link_train_2(struct dp_ctrl_private *ctrl,
+ struct dp_cr_status *cr, int *training_step)
{
int tries = 0, ret = 0;
char pattern;
@@ -1153,6 +1195,8 @@ static int dp_ctrl_link_training_2(struct dp_ctrl_private *ctrl)
dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0);
+ *training_step = DP_TRAINING_2;
+
if (drm_dp_tps3_supported(ctrl->panel->dpcd))
pattern = DP_TRAINING_PATTERN_3;
else
@@ -1174,10 +1218,13 @@ static int dp_ctrl_link_training_2(struct dp_ctrl_private *ctrl)
ret = dp_ctrl_read_link_status(ctrl, link_status);
if (ret)
return ret;
+ cr->lane_0_1 = link_status[0];
+ cr->lane_2_3 = link_status[1];
if (drm_dp_channel_eq_ok(link_status,
- ctrl->link->link_params.num_lanes))
- return ret;
+ ctrl->link->link_params.num_lanes)) {
+ return 0;
+ }
dp_link_adjust_levels(ctrl->link, link_status);
ret = dp_ctrl_update_vx_px(ctrl);
@@ -1189,15 +1236,15 @@ static int dp_ctrl_link_training_2(struct dp_ctrl_private *ctrl)
return -ETIMEDOUT;
}
-static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
+static int dp_ctrl_reinitialize_mainlink(struct dp_ctrl_private *ctrl);
+
+static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl,
+ struct dp_cr_status *cr, int *training_step)
{
int ret = 0;
u8 encoding = DP_SET_ANSI_8B10B;
struct dp_link_info link_info = {0};
- ctrl->link->phy_params.p_level = 0;
- ctrl->link->phy_params.v_level = 0;
-
dp_ctrl_config_ctrl(ctrl);
link_info.num_lanes = ctrl->link->link_params.num_lanes;
@@ -1208,7 +1255,7 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
drm_dp_dpcd_write(ctrl->aux, DP_MAIN_LINK_CHANNEL_CODING_SET,
&encoding, 1);
- ret = dp_ctrl_link_train_1(ctrl);
+ ret = dp_ctrl_link_train_1(ctrl, cr, training_step);
if (ret) {
DRM_ERROR("link training #1 failed. ret=%d\n", ret);
goto end;
@@ -1217,7 +1264,7 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
/* print success info as this is a result of user initiated action */
DRM_DEBUG_DP("link training #1 successful\n");
- ret = dp_ctrl_link_training_2(ctrl);
+ ret = dp_ctrl_link_train_2(ctrl, cr, training_step);
if (ret) {
DRM_ERROR("link training #2 failed. ret=%d\n", ret);
goto end;
@@ -1229,58 +1276,36 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
end:
dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0);
- dp_ctrl_clear_training_pattern(ctrl);
return ret;
}
-static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl, bool train)
+static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl,
+ struct dp_cr_status *cr, int *training_step)
{
- bool mainlink_ready = false;
int ret = 0;
dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, true);
- ret = dp_link_psm_config(ctrl->link, &ctrl->panel->link_info, false);
- if (ret)
- return ret;
-
if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN)
return ret;
- if (train) {
- /*
- * As part of previous calls, DP controller state might have
- * transitioned to PUSH_IDLE. In order to start transmitting
- * a link training pattern, we have to first do soft reset.
- */
- dp_catalog_ctrl_reset(ctrl->catalog);
-
- ret = dp_ctrl_link_train(ctrl);
- if (ret)
- return ret;
- }
-
/*
- * Set up transfer unit values and set controller state to send
- * video.
+ * As part of previous calls, DP controller state might have
+ * transitioned to PUSH_IDLE. In order to start transmitting
+ * a link training pattern, we have to first do soft reset.
*/
- dp_ctrl_setup_tr_unit(ctrl);
- dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_SEND_VIDEO);
+ dp_catalog_ctrl_reset(ctrl->catalog);
- ret = dp_ctrl_wait4video_ready(ctrl);
- if (ret)
- return ret;
+ ret = dp_ctrl_link_train(ctrl, cr, training_step);
- mainlink_ready = dp_catalog_ctrl_mainlink_ready(ctrl->catalog);
- DRM_DEBUG_DP("mainlink %s\n", mainlink_ready ? "READY" : "NOT READY");
return ret;
}
static void dp_ctrl_set_clock_rate(struct dp_ctrl_private *ctrl,
- char *name, u32 rate)
+ enum dp_pm_type module, char *name, u32 rate)
{
- u32 num = ctrl->parser->mp[DP_CTRL_PM].num_clk;
- struct dss_clk *cfg = ctrl->parser->mp[DP_CTRL_PM].clk_config;
+ u32 num = ctrl->parser->mp[module].num_clk;
+ struct dss_clk *cfg = ctrl->parser->mp[module].clk_config;
while (num && strcmp(cfg->clk_name, name)) {
num--;
@@ -1302,16 +1327,33 @@ static int dp_ctrl_enable_mainlink_clocks(struct dp_ctrl_private *ctrl)
dp_power_set_link_clk_parent(ctrl->power);
- dp_ctrl_set_clock_rate(ctrl, "ctrl_link",
+ dp_ctrl_set_clock_rate(ctrl, DP_CTRL_PM, "ctrl_link",
ctrl->link->link_params.rate);
- dp_ctrl_set_clock_rate(ctrl, "stream_pixel",
- ctrl->dp_ctrl.pixel_rate);
-
ret = dp_power_clk_enable(ctrl->power, DP_CTRL_PM, true);
if (ret)
DRM_ERROR("Unable to start link clocks. ret=%d\n", ret);
+ DRM_DEBUG_DP("link rate=%d pixel_clk=%d\n",
+ ctrl->link->link_params.rate, ctrl->dp_ctrl.pixel_rate);
+
+ return ret;
+}
+
+static int dp_ctrl_enable_stream_clocks(struct dp_ctrl_private *ctrl)
+{
+ int ret = 0;
+
+ dp_ctrl_set_clock_rate(ctrl, DP_STREAM_PM, "stream_pixel",
+ ctrl->dp_ctrl.pixel_rate);
+
+ ret = dp_power_clk_enable(ctrl->power, DP_STREAM_PM, true);
+ if (ret)
+ DRM_ERROR("Unabled to start pixel clocks. ret=%d\n", ret);
+
+ DRM_DEBUG_DP("link rate=%d pixel_clk=%d\n",
+ ctrl->link->link_params.rate, ctrl->dp_ctrl.pixel_rate);
+
return ret;
}
@@ -1401,37 +1443,30 @@ static int dp_ctrl_reinitialize_mainlink(struct dp_ctrl_private *ctrl)
return ret;
}
- dp_ctrl_configure_source_params(ctrl);
- dp_catalog_ctrl_config_msa(ctrl->catalog,
- ctrl->link->link_params.rate,
- ctrl->dp_ctrl.pixel_rate, dp_ctrl_use_fixed_nvid(ctrl));
- reinit_completion(&ctrl->idle_comp);
-
return ret;
}
static int dp_ctrl_link_maintenance(struct dp_ctrl_private *ctrl)
{
int ret = 0;
- int tries;
+ struct dp_cr_status cr;
+ int training_step = DP_TRAINING_NONE;
dp_ctrl_push_idle(&ctrl->dp_ctrl);
dp_catalog_ctrl_reset(ctrl->catalog);
ctrl->dp_ctrl.pixel_rate = ctrl->panel->dp_mode.drm_mode.clock;
- for (tries = 0; tries < 10; tries++) {
- ret = dp_ctrl_reinitialize_mainlink(ctrl);
- if (ret) {
- DRM_ERROR("Failed to reinitialize mainlink. ret=%d\n",
- ret);
- break;
- }
+ ret = dp_ctrl_setup_main_link(ctrl, &cr, &training_step);
+ if (ret)
+ goto end;
- ret = dp_ctrl_setup_main_link(ctrl, true);
- if (ret == -EAGAIN) /* try with lower link rate */
- dp_ctrl_link_rate_down_shift(ctrl);
- }
+ dp_ctrl_clear_training_pattern(ctrl);
+
+ dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_SEND_VIDEO);
+
+ ret = dp_ctrl_wait4video_ready(ctrl);
+end:
return ret;
}
@@ -1444,22 +1479,22 @@ static int dp_ctrl_process_phy_test_request(struct dp_ctrl_private *ctrl)
return ret;
}
- dp_ctrl_push_idle(&ctrl->dp_ctrl);
/*
* The global reset will need DP link related clocks to be
* running. Add the global reset just before disabling the
* link clocks and core clocks.
*/
- dp_catalog_ctrl_reset(ctrl->catalog);
ret = dp_ctrl_off(&ctrl->dp_ctrl);
if (ret) {
DRM_ERROR("failed to disable DP controller\n");
return ret;
}
- ret = dp_ctrl_on(&ctrl->dp_ctrl);
- if (ret)
- DRM_ERROR("failed to enable DP controller\n");
+ ret = dp_ctrl_on_link(&ctrl->dp_ctrl);
+ if (!ret)
+ ret = dp_ctrl_on_stream(&ctrl->dp_ctrl);
+ else
+ DRM_ERROR("failed to enable DP link controller\n");
return ret;
}
@@ -1479,27 +1514,33 @@ static bool dp_ctrl_send_phy_test_pattern(struct dp_ctrl_private *ctrl)
return false;
}
dp_catalog_ctrl_send_phy_pattern(ctrl->catalog, pattern_requested);
+ dp_ctrl_update_vx_px(ctrl);
dp_link_send_test_response(ctrl->link);
pattern_sent = dp_catalog_ctrl_read_phy_pattern(ctrl->catalog);
switch (pattern_sent) {
case MR_LINK_TRAINING1:
- success = pattern_requested ==
- DP_LINK_QUAL_PATTERN_D10_2;
+ success = (pattern_requested ==
+ DP_PHY_TEST_PATTERN_D10_2);
break;
case MR_LINK_SYMBOL_ERM:
- success = (pattern_requested ==
- DP_LINK_QUAL_PATTERN_ERROR_RATE)
- || (pattern_requested ==
- DP_LINK_QUAL_PATTERN_HBR2_EYE);
+ success = ((pattern_requested ==
+ DP_PHY_TEST_PATTERN_ERROR_COUNT) ||
+ (pattern_requested ==
+ DP_PHY_TEST_PATTERN_CP2520));
break;
case MR_LINK_PRBS7:
- success = pattern_requested == DP_LINK_QUAL_PATTERN_PRBS7;
+ success = (pattern_requested ==
+ DP_PHY_TEST_PATTERN_PRBS7);
break;
case MR_LINK_CUSTOM80:
- success = pattern_requested ==
- DP_LINK_QUAL_PATTERN_80BIT_CUSTOM;
+ success = (pattern_requested ==
+ DP_PHY_TEST_PATTERN_80BIT_CUSTOM);
+ break;
+ case MR_LINK_TRAINING4:
+ success = (pattern_requested ==
+ DP_PHY_TEST_PATTERN_SEL_MASK);
break;
default:
success = false;
@@ -1531,12 +1572,12 @@ void dp_ctrl_handle_sink_request(struct dp_ctrl *dp_ctrl)
}
}
- if (sink_request & DP_LINK_STATUS_UPDATED)
+ if (sink_request & DP_LINK_STATUS_UPDATED) {
if (dp_ctrl_link_maintenance(ctrl)) {
- DRM_ERROR("LM failed: STATUS_UPDATED\n");
+ DRM_ERROR("LM failed: TEST_LINK_TRAINING\n");
return;
}
-
+ }
if (sink_request & DP_TEST_LINK_TRAINING) {
dp_link_send_test_response(ctrl->link);
@@ -1547,13 +1588,15 @@ void dp_ctrl_handle_sink_request(struct dp_ctrl *dp_ctrl)
}
}
-int dp_ctrl_on(struct dp_ctrl *dp_ctrl)
+int dp_ctrl_on_link(struct dp_ctrl *dp_ctrl)
{
int rc = 0;
struct dp_ctrl_private *ctrl;
u32 rate = 0;
- u32 link_train_max_retries = 10;
+ int link_train_max_retries = 5;
u32 const phy_cts_pixel_clk_khz = 148500;
+ struct dp_cr_status cr;
+ unsigned int training_step;
if (!dp_ctrl)
return -EINVAL;
@@ -1587,6 +1630,9 @@ int dp_ctrl_on(struct dp_ctrl *dp_ctrl)
if (rc)
return rc;
+ ctrl->link->phy_params.p_level = 0;
+ ctrl->link->phy_params.v_level = 0;
+
while (--link_train_max_retries &&
!atomic_read(&ctrl->dp_ctrl.aborted)) {
rc = dp_ctrl_reinitialize_mainlink(ctrl);
@@ -1595,19 +1641,125 @@ int dp_ctrl_on(struct dp_ctrl *dp_ctrl)
rc);
break;
}
- rc = dp_ctrl_setup_main_link(ctrl, true);
- if (!rc)
+
+ training_step = DP_TRAINING_NONE;
+ rc = dp_ctrl_setup_main_link(ctrl, &cr, &training_step);
+ if (rc == 0) {
+ /* training completed successfully */
break;
- /* try with lower link rate */
- dp_ctrl_link_rate_down_shift(ctrl);
+ } else if (training_step == DP_TRAINING_1) {
+ /* link train_1 failed */
+ rc = dp_ctrl_link_rate_down_shift(ctrl);
+ if (rc < 0) { /* already in RBR = 1.6G */
+ if (cr.lane_0_1 & DP_LANE0_1_CR_DONE) {
+ /*
+ * some lanes are ready,
+ * reduce lane number
+ */
+ rc = dp_ctrl_link_lane_down_shift(ctrl);
+ if (rc < 0) { /* lane == 1 already */
+ /* end with failure */
+ break;
+ }
+ } else {
+ /* end with failure */
+ break; /* lane == 1 already */
+ }
+ }
+ } else if (training_step == DP_TRAINING_2) {
+ /* link train_2 failed, lower lane rate */
+ rc = dp_ctrl_link_lane_down_shift(ctrl);
+ if (rc < 0) {
+ /* end with failure */
+ break; /* lane == 1 already */
+ }
+ }
}
if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN)
- dp_ctrl_send_phy_test_pattern(ctrl);
+ return rc;
+
+ /* stop txing train pattern */
+ dp_ctrl_clear_training_pattern(ctrl);
+
+ /*
+ * keep transmitting idle pattern until video ready
+ * to avoid main link from loss of sync
+ */
+ if (rc == 0) /* link train successfully */
+ dp_ctrl_push_idle(dp_ctrl);
return rc;
}
+int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl)
+{
+ u32 rate = 0;
+ int ret = 0;
+ bool mainlink_ready = false;
+ struct dp_ctrl_private *ctrl;
+
+ if (!dp_ctrl)
+ return -EINVAL;
+
+ ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+ rate = ctrl->panel->link_info.rate;
+
+ ctrl->link->link_params.rate = rate;
+ ctrl->link->link_params.num_lanes = ctrl->panel->link_info.num_lanes;
+ ctrl->dp_ctrl.pixel_rate = ctrl->panel->dp_mode.drm_mode.clock;
+
+ DRM_DEBUG_DP("rate=%d, num_lanes=%d, pixel_rate=%d\n",
+ ctrl->link->link_params.rate,
+ ctrl->link->link_params.num_lanes, ctrl->dp_ctrl.pixel_rate);
+
+ if (!dp_power_clk_status(ctrl->power, DP_CTRL_PM)) { /* link clk is off */
+ ret = dp_ctrl_enable_mainlink_clocks(ctrl);
+ if (ret) {
+ DRM_ERROR("Failed to start link clocks. ret=%d\n", ret);
+ goto end;
+ }
+ }
+
+ ret = dp_ctrl_enable_stream_clocks(ctrl);
+ if (ret) {
+ DRM_ERROR("Failed to start pixel clocks. ret=%d\n", ret);
+ goto end;
+ }
+
+ if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
+ dp_ctrl_send_phy_test_pattern(ctrl);
+ return 0;
+ }
+
+ /*
+ * Set up transfer unit values and set controller state to send
+ * video.
+ */
+ dp_ctrl_configure_source_params(ctrl);
+
+ dp_catalog_ctrl_config_msa(ctrl->catalog,
+ ctrl->link->link_params.rate,
+ ctrl->dp_ctrl.pixel_rate, dp_ctrl_use_fixed_nvid(ctrl));
+
+ reinit_completion(&ctrl->video_comp);
+
+ dp_ctrl_setup_tr_unit(ctrl);
+
+ dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_SEND_VIDEO);
+
+ ret = dp_ctrl_wait4video_ready(ctrl);
+ if (ret)
+ return ret;
+
+ mainlink_ready = dp_catalog_ctrl_mainlink_ready(ctrl->catalog);
+ DRM_DEBUG_DP("mainlink %s\n", mainlink_ready ? "READY" : "NOT READY");
+
+end:
+ return ret;
+}
+
int dp_ctrl_off(struct dp_ctrl *dp_ctrl)
{
struct dp_ctrl_private *ctrl;
@@ -1619,11 +1771,16 @@ int dp_ctrl_off(struct dp_ctrl *dp_ctrl)
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);
+
dp_catalog_ctrl_reset(ctrl->catalog);
+
+ ret = dp_power_clk_enable(ctrl->power, DP_STREAM_PM, false);
+ if (ret)
+ DRM_ERROR("Failed to disable pixel clocks. ret=%d\n", ret);
+
ret = dp_power_clk_enable(ctrl->power, DP_CTRL_PM, false);
if (ret) {
- DRM_ERROR("Failed to disable clocks. ret=%d\n", ret);
- return ret;
+ DRM_ERROR("Failed to disable link clocks. ret=%d\n", ret);
}
DRM_DEBUG_DP("DP off done\n");
@@ -1674,7 +1831,6 @@ struct dp_ctrl *dp_ctrl_get(struct device *dev, struct dp_link *link,
init_completion(&ctrl->idle_comp);
init_completion(&ctrl->video_comp);
- mutex_init(&ctrl->push_idle_mutex);
/* in parameters */
ctrl->parser = parser;
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index 94713a0daff0..f60ba93c8678 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -21,7 +21,8 @@ struct dp_ctrl {
int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip);
void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl);
-int dp_ctrl_on(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_off(struct dp_ctrl *dp_ctrl);
void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl);
void dp_ctrl_isr(struct dp_ctrl *dp_ctrl);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 925c89720a16..18ca5eac8e20 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -9,6 +9,7 @@
#include <linux/debugfs.h>
#include <linux/component.h>
#include <linux/of_irq.h>
+#include <linux/delay.h>
#include "msm_drv.h"
#include "msm_kms.h"
@@ -28,6 +29,52 @@
static struct msm_dp *g_dp_display;
#define HPD_STRING_SIZE 30
+enum {
+ ISR_DISCONNECTED,
+ ISR_CONNECT_PENDING,
+ ISR_CONNECTED,
+ ISR_HPD_REPLUG_COUNT,
+ ISR_IRQ_HPD_PULSE_COUNT,
+ ISR_HPD_LO_GLITH_COUNT,
+};
+
+/* event thread connection state */
+enum {
+ ST_DISCONNECTED,
+ ST_CONNECT_PENDING,
+ ST_CONNECTED,
+ ST_DISCONNECT_PENDING,
+ ST_SUSPEND_PENDING,
+ ST_SUSPENDED,
+};
+
+enum {
+ EV_NO_EVENT,
+ /* hpd events */
+ EV_HPD_INIT_SETUP,
+ EV_HPD_PLUG_INT,
+ EV_IRQ_HPD_INT,
+ EV_HPD_REPLUG_INT,
+ EV_HPD_UNPLUG_INT,
+ EV_USER_NOTIFICATION,
+ EV_CONNECT_PENDING_TIMEOUT,
+ EV_DISCONNECT_PENDING_TIMEOUT,
+};
+
+#define EVENT_TIMEOUT (HZ/10) /* 100ms */
+#define DP_EVENT_Q_MAX 8
+
+#define DP_TIMEOUT_5_SECOND (5000/EVENT_TIMEOUT)
+#define DP_TIMEOUT_NONE 0
+
+#define WAIT_FOR_RESUME_TIMEOUT_JIFFIES (HZ / 2)
+
+struct dp_event {
+ u32 event_id;
+ u32 data;
+ u32 delay;
+};