disp: msm: dp: link training enhancements
Update the link training process along with the AUX communications during link training as per hardware recommendations. Update the pre-emphasis and swing values for active lanes only instead of all lanes. During link training, update pre-emphasis and swing values in hardware first and then update sink. CRs-Fixed: 2458753 Change-Id: Ie05c9d6508b0c564b194032ae4ebb1bc5550e7b8 Signed-off-by: Ajay Singh Parmar <aparmar@codeaurora.org>
This commit is contained in:
191
msm/dp/dp_ctrl.c
191
msm/dp/dp_ctrl.c
@@ -41,6 +41,8 @@
|
|||||||
#define MR_LINK_CUSTOM80 0x200
|
#define MR_LINK_CUSTOM80 0x200
|
||||||
#define MR_LINK_TRAINING4 0x40
|
#define MR_LINK_TRAINING4 0x40
|
||||||
|
|
||||||
|
#define DP_MAX_LANES 4
|
||||||
|
|
||||||
struct dp_mst_ch_slot_info {
|
struct dp_mst_ch_slot_info {
|
||||||
u32 start_slot;
|
u32 start_slot;
|
||||||
u32 tot_slots;
|
u32 tot_slots;
|
||||||
@@ -182,35 +184,40 @@ static void dp_ctrl_wait4video_ready(struct dp_ctrl_private *ctrl)
|
|||||||
pr_warn("SEND_VIDEO time out\n");
|
pr_warn("SEND_VIDEO time out\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dp_ctrl_update_sink_vx_px(struct dp_ctrl_private *ctrl,
|
static int dp_ctrl_update_sink_vx_px(struct dp_ctrl_private *ctrl)
|
||||||
u32 voltage_level, u32 pre_emphasis_level)
|
|
||||||
{
|
{
|
||||||
int i;
|
int i, ret;
|
||||||
u8 buf[4];
|
u8 buf[DP_MAX_LANES];
|
||||||
|
u8 v_level = ctrl->link->phy_params.v_level;
|
||||||
|
u8 p_level = ctrl->link->phy_params.p_level;
|
||||||
|
u8 size = min_t(u8, sizeof(buf), ctrl->link->link_params.lane_count);
|
||||||
u32 max_level_reached = 0;
|
u32 max_level_reached = 0;
|
||||||
|
|
||||||
if (voltage_level == DP_LINK_VOLTAGE_MAX) {
|
if (v_level == DP_LINK_VOLTAGE_MAX) {
|
||||||
pr_debug("max. voltage swing level reached %d\n",
|
pr_debug("max voltage swing level reached %d\n", v_level);
|
||||||
voltage_level);
|
max_level_reached |= DP_TRAIN_MAX_SWING_REACHED;
|
||||||
max_level_reached |= BIT(2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pre_emphasis_level == DP_LINK_PRE_EMPHASIS_MAX) {
|
if (p_level == DP_LINK_PRE_EMPHASIS_MAX) {
|
||||||
pr_debug("max. pre-emphasis level reached %d\n",
|
pr_debug("max pre-emphasis level reached %d\n", p_level);
|
||||||
pre_emphasis_level);
|
max_level_reached |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
|
||||||
max_level_reached |= BIT(5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pre_emphasis_level <<= 3;
|
p_level <<= DP_TRAIN_PRE_EMPHASIS_SHIFT;
|
||||||
|
|
||||||
for (i = 0; i < 4; i++)
|
for (i = 0; i < size; i++)
|
||||||
buf[i] = voltage_level | pre_emphasis_level | max_level_reached;
|
buf[i] = v_level | p_level | max_level_reached;
|
||||||
|
|
||||||
pr_debug("sink: p|v=0x%x\n", voltage_level | pre_emphasis_level);
|
pr_debug("lanes: %d, swing: 0x%x, pre-emp: 0x%x\n",
|
||||||
return drm_dp_dpcd_write(ctrl->aux->drm_aux, 0x103, buf, 4);
|
size, v_level, p_level);
|
||||||
|
|
||||||
|
ret = drm_dp_dpcd_write(ctrl->aux->drm_aux,
|
||||||
|
DP_TRAINING_LANE0_SET, buf, size);
|
||||||
|
|
||||||
|
return ret <= 0 ? -EINVAL : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dp_ctrl_update_vx_px(struct dp_ctrl_private *ctrl)
|
static void dp_ctrl_update_hw_vx_px(struct dp_ctrl_private *ctrl)
|
||||||
{
|
{
|
||||||
struct dp_link *link = ctrl->link;
|
struct dp_link *link = ctrl->link;
|
||||||
bool high = false;
|
bool high = false;
|
||||||
@@ -221,21 +228,22 @@ static int dp_ctrl_update_vx_px(struct dp_ctrl_private *ctrl)
|
|||||||
|
|
||||||
ctrl->catalog->update_vx_px(ctrl->catalog,
|
ctrl->catalog->update_vx_px(ctrl->catalog,
|
||||||
link->phy_params.v_level, link->phy_params.p_level, high);
|
link->phy_params.v_level, link->phy_params.p_level, high);
|
||||||
|
|
||||||
return dp_ctrl_update_sink_vx_px(ctrl, link->phy_params.v_level,
|
|
||||||
link->phy_params.p_level);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dp_ctrl_train_pattern_set(struct dp_ctrl_private *ctrl,
|
static int dp_ctrl_update_sink_pattern(struct dp_ctrl_private *ctrl, u8 pattern)
|
||||||
u8 pattern)
|
|
||||||
{
|
{
|
||||||
u8 buf[4];
|
u8 buf = pattern;
|
||||||
|
int ret;
|
||||||
|
|
||||||
pr_debug("sink: pattern=%x\n", pattern);
|
pr_debug("sink: pattern=%x\n", pattern);
|
||||||
|
|
||||||
buf[0] = pattern;
|
if (pattern && pattern != DP_TRAINING_PATTERN_4)
|
||||||
return drm_dp_dpcd_write(ctrl->aux->drm_aux,
|
buf |= DP_LINK_SCRAMBLING_DISABLE;
|
||||||
DP_TRAINING_PATTERN_SET, buf, 1);
|
|
||||||
|
ret = drm_dp_dpcd_writeb(ctrl->aux->drm_aux,
|
||||||
|
DP_TRAINING_PATTERN_SET, buf);
|
||||||
|
|
||||||
|
return ret <= 0 ? -EINVAL : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dp_ctrl_read_link_status(struct dp_ctrl_private *ctrl,
|
static int dp_ctrl_read_link_status(struct dp_ctrl_private *ctrl,
|
||||||
@@ -287,10 +295,11 @@ static int dp_ctrl_lane_count_down_shift(struct dp_ctrl_private *ctrl)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl)
|
static int dp_ctrl_link_training_1(struct dp_ctrl_private *ctrl)
|
||||||
{
|
{
|
||||||
int tries, old_v_level, ret = 0;
|
int tries, old_v_level, ret = -EINVAL;
|
||||||
u8 link_status[DP_LINK_STATUS_SIZE];
|
u8 link_status[DP_LINK_STATUS_SIZE];
|
||||||
|
u8 pattern = 0;
|
||||||
int const maximum_retries = 5;
|
int const maximum_retries = 5;
|
||||||
|
|
||||||
ctrl->aux->state &= ~DP_STATE_TRAIN_1_FAILED;
|
ctrl->aux->state &= ~DP_STATE_TRAIN_1_FAILED;
|
||||||
@@ -301,42 +310,42 @@ static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl)
|
|||||||
/* Make sure to clear the current pattern before starting a new one */
|
/* Make sure to clear the current pattern before starting a new one */
|
||||||
wmb();
|
wmb();
|
||||||
|
|
||||||
ctrl->catalog->set_pattern(ctrl->catalog, 0x01);
|
|
||||||
ret = dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_1 |
|
|
||||||
DP_LINK_SCRAMBLING_DISABLE); /* train_1 */
|
|
||||||
if (ret <= 0) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = dp_ctrl_update_vx_px(ctrl);
|
|
||||||
if (ret <= 0) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
tries = 0;
|
tries = 0;
|
||||||
old_v_level = ctrl->link->phy_params.v_level;
|
old_v_level = ctrl->link->phy_params.v_level;
|
||||||
while (1) {
|
while (!atomic_read(&ctrl->aborted)) {
|
||||||
if (atomic_read(&ctrl->aborted)) {
|
/* update hardware with current swing/pre-emp values */
|
||||||
ret = -EINVAL;
|
dp_ctrl_update_hw_vx_px(ctrl);
|
||||||
break;
|
|
||||||
|
if (!pattern) {
|
||||||
|
pattern = DP_TRAINING_PATTERN_1;
|
||||||
|
|
||||||
|
ctrl->catalog->set_pattern(ctrl->catalog, pattern);
|
||||||
|
|
||||||
|
/* update sink with current settings */
|
||||||
|
ret = dp_ctrl_update_sink_pattern(ctrl, pattern);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = dp_ctrl_update_sink_vx_px(ctrl);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
|
||||||
drm_dp_link_train_clock_recovery_delay(ctrl->panel->dpcd);
|
drm_dp_link_train_clock_recovery_delay(ctrl->panel->dpcd);
|
||||||
|
|
||||||
ret = dp_ctrl_read_link_status(ctrl, link_status);
|
ret = dp_ctrl_read_link_status(ctrl, link_status);
|
||||||
if (ret)
|
if (ret)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (drm_dp_clock_recovery_ok(link_status,
|
if (!drm_dp_clock_recovery_ok(link_status,
|
||||||
ctrl->link->link_params.lane_count)) {
|
ctrl->link->link_params.lane_count))
|
||||||
|
ret = -EINVAL;
|
||||||
|
else
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
if (ctrl->link->phy_params.v_level == DP_LINK_VOLTAGE_MAX) {
|
if (ctrl->link->phy_params.v_level == DP_LINK_VOLTAGE_MAX) {
|
||||||
pr_err_ratelimited("max v_level reached\n");
|
pr_err_ratelimited("max v_level reached\n");
|
||||||
ret = -EAGAIN;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,13 +364,8 @@ static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl)
|
|||||||
pr_debug("clock recovery not done, adjusting vx px\n");
|
pr_debug("clock recovery not done, adjusting vx px\n");
|
||||||
|
|
||||||
ctrl->link->adjust_levels(ctrl->link, link_status);
|
ctrl->link->adjust_levels(ctrl->link, link_status);
|
||||||
ret = dp_ctrl_update_vx_px(ctrl);
|
|
||||||
if (ret <= 0) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
end:
|
|
||||||
ctrl->aux->state &= ~DP_STATE_TRAIN_1_STARTED;
|
ctrl->aux->state &= ~DP_STATE_TRAIN_1_STARTED;
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
@@ -400,14 +404,14 @@ static int dp_ctrl_link_rate_down_shift(struct dp_ctrl_private *ctrl)
|
|||||||
|
|
||||||
static void dp_ctrl_clear_training_pattern(struct dp_ctrl_private *ctrl)
|
static void dp_ctrl_clear_training_pattern(struct dp_ctrl_private *ctrl)
|
||||||
{
|
{
|
||||||
dp_ctrl_train_pattern_set(ctrl, 0);
|
dp_ctrl_update_sink_pattern(ctrl, 0);
|
||||||
drm_dp_link_train_channel_eq_delay(ctrl->panel->dpcd);
|
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_training_2(struct dp_ctrl_private *ctrl)
|
||||||
{
|
{
|
||||||
int tries = 0, ret = 0;
|
int tries = 0, ret = -EINVAL;
|
||||||
char pattern;
|
u8 dpcd_pattern, pattern = 0;
|
||||||
int const maximum_retries = 5;
|
int const maximum_retries = 5;
|
||||||
u8 link_status[DP_LINK_STATUS_SIZE];
|
u8 link_status[DP_LINK_STATUS_SIZE];
|
||||||
|
|
||||||
@@ -420,32 +424,30 @@ static int dp_ctrl_link_training_2(struct dp_ctrl_private *ctrl)
|
|||||||
wmb();
|
wmb();
|
||||||
|
|
||||||
if (drm_dp_tps3_supported(ctrl->panel->dpcd))
|
if (drm_dp_tps3_supported(ctrl->panel->dpcd))
|
||||||
pattern = DP_TRAINING_PATTERN_3;
|
dpcd_pattern = DP_TRAINING_PATTERN_3;
|
||||||
else
|
else
|
||||||
pattern = DP_TRAINING_PATTERN_2;
|
dpcd_pattern = DP_TRAINING_PATTERN_2;
|
||||||
|
|
||||||
ret = dp_ctrl_update_vx_px(ctrl);
|
while (!atomic_read(&ctrl->aborted)) {
|
||||||
if (ret <= 0) {
|
/* update hardware with current swing/pre-emp values */
|
||||||
ret = -EINVAL;
|
dp_ctrl_update_hw_vx_px(ctrl);
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pattern != DP_TRAINING_PATTERN_4)
|
if (!pattern) {
|
||||||
pattern |= DP_LINK_SCRAMBLING_DISABLE;
|
pattern = dpcd_pattern;
|
||||||
|
|
||||||
ctrl->catalog->set_pattern(ctrl->catalog, pattern);
|
/* program hw to send pattern */
|
||||||
ret = dp_ctrl_train_pattern_set(ctrl, pattern);
|
ctrl->catalog->set_pattern(ctrl->catalog, pattern);
|
||||||
if (ret <= 0) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
/* update sink with current pattern */
|
||||||
if (atomic_read(&ctrl->aborted)) {
|
ret = dp_ctrl_update_sink_pattern(ctrl, pattern);
|
||||||
ret = -EINVAL;
|
if (ret)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = dp_ctrl_update_sink_vx_px(ctrl);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
|
||||||
drm_dp_link_train_channel_eq_delay(ctrl->panel->dpcd);
|
drm_dp_link_train_channel_eq_delay(ctrl->panel->dpcd);
|
||||||
|
|
||||||
ret = dp_ctrl_read_link_status(ctrl, link_status);
|
ret = dp_ctrl_read_link_status(ctrl, link_status);
|
||||||
@@ -459,8 +461,10 @@ static int dp_ctrl_link_training_2(struct dp_ctrl_private *ctrl)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (drm_dp_channel_eq_ok(link_status,
|
if (!drm_dp_channel_eq_ok(link_status,
|
||||||
ctrl->link->link_params.lane_count))
|
ctrl->link->link_params.lane_count))
|
||||||
|
ret = -EINVAL;
|
||||||
|
else
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (tries > maximum_retries) {
|
if (tries > maximum_retries) {
|
||||||
@@ -470,13 +474,8 @@ static int dp_ctrl_link_training_2(struct dp_ctrl_private *ctrl)
|
|||||||
tries++;
|
tries++;
|
||||||
|
|
||||||
ctrl->link->adjust_levels(ctrl->link, link_status);
|
ctrl->link->adjust_levels(ctrl->link, link_status);
|
||||||
ret = dp_ctrl_update_vx_px(ctrl);
|
}
|
||||||
if (ret <= 0) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (1);
|
|
||||||
end:
|
|
||||||
ctrl->aux->state &= ~DP_STATE_TRAIN_2_STARTED;
|
ctrl->aux->state &= ~DP_STATE_TRAIN_2_STARTED;
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
@@ -489,7 +488,7 @@ end:
|
|||||||
static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
|
static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
u8 encoding = 0x1;
|
u8 const encoding = 0x1, downspread = 0x00;
|
||||||
struct drm_dp_link link_info = {0};
|
struct drm_dp_link link_info = {0};
|
||||||
|
|
||||||
ctrl->link->phy_params.p_level = 0;
|
ctrl->link->phy_params.p_level = 0;
|
||||||
@@ -504,14 +503,21 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
ret = drm_dp_dpcd_write(ctrl->aux->drm_aux,
|
ret = drm_dp_dpcd_writeb(ctrl->aux->drm_aux,
|
||||||
DP_MAIN_LINK_CHANNEL_CODING_SET, &encoding, 1);
|
DP_DOWNSPREAD_CTRL, downspread);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = dp_ctrl_link_train_1(ctrl);
|
ret = drm_dp_dpcd_writeb(ctrl->aux->drm_aux,
|
||||||
|
DP_MAIN_LINK_CHANNEL_CODING_SET, encoding);
|
||||||
|
if (ret <= 0) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dp_ctrl_link_training_1(ctrl);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("link training #1 failed\n");
|
pr_err("link training #1 failed\n");
|
||||||
goto end;
|
goto end;
|
||||||
@@ -862,8 +868,9 @@ static void dp_ctrl_send_phy_test_pattern(struct dp_ctrl_private *ctrl)
|
|||||||
u32 pattern_sent = 0x0;
|
u32 pattern_sent = 0x0;
|
||||||
u32 pattern_requested = ctrl->link->phy_params.phy_test_pattern_sel;
|
u32 pattern_requested = ctrl->link->phy_params.phy_test_pattern_sel;
|
||||||
|
|
||||||
dp_ctrl_update_vx_px(ctrl);
|
dp_ctrl_update_hw_vx_px(ctrl);
|
||||||
ctrl->catalog->send_phy_pattern(ctrl->catalog, pattern_requested);
|
ctrl->catalog->send_phy_pattern(ctrl->catalog, pattern_requested);
|
||||||
|
dp_ctrl_update_sink_vx_px(ctrl);
|
||||||
ctrl->link->send_test_response(ctrl->link);
|
ctrl->link->send_test_response(ctrl->link);
|
||||||
|
|
||||||
pattern_sent = ctrl->catalog->read_phy_pattern(ctrl->catalog);
|
pattern_sent = ctrl->catalog->read_phy_pattern(ctrl->catalog);
|
||||||
|
@@ -1426,6 +1426,10 @@ static int dp_link_adjust_levels(struct dp_link *dp_link, u8 *link_status)
|
|||||||
&& (dp_link->phy_params.v_level == DP_LINK_VOLTAGE_LEVEL_2))
|
&& (dp_link->phy_params.v_level == DP_LINK_VOLTAGE_LEVEL_2))
|
||||||
dp_link->phy_params.p_level = DP_LINK_PRE_EMPHASIS_LEVEL_1;
|
dp_link->phy_params.p_level = DP_LINK_PRE_EMPHASIS_LEVEL_1;
|
||||||
|
|
||||||
|
if ((dp_link->phy_params.p_level > DP_LINK_PRE_EMPHASIS_LEVEL_2)
|
||||||
|
&& (dp_link->phy_params.v_level == DP_LINK_VOLTAGE_LEVEL_1))
|
||||||
|
dp_link->phy_params.p_level = DP_LINK_PRE_EMPHASIS_LEVEL_2;
|
||||||
|
|
||||||
pr_debug("Set (VxPx): %x%x\n",
|
pr_debug("Set (VxPx): %x%x\n",
|
||||||
dp_link->phy_params.v_level, dp_link->phy_params.p_level);
|
dp_link->phy_params.v_level, dp_link->phy_params.p_level);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user