Merge "msm: mdss: dp: fix handling of link training mutex"

This commit is contained in:
Linux Build Service Account 2016-12-23 03:55:01 -08:00 committed by Gerrit - the friendly Code Review server
commit 1cb457387d
5 changed files with 323 additions and 25 deletions

View file

@ -1211,16 +1211,20 @@ static int mdss_dp_on_irq(struct mdss_dp_drv_pdata *dp_drv)
ret = mdss_dp_get_lane_mapping(dp_drv, dp_drv->orientation,
&ln_map);
if (ret)
if (ret) {
mutex_unlock(&dp_drv->train_mutex);
goto exit;
}
mdss_dp_phy_share_lane_config(&dp_drv->phy_io,
dp_drv->orientation,
dp_drv->dpcd.max_lane_count);
ret = mdss_dp_enable_mainlink_clocks(dp_drv);
if (ret)
if (ret) {
mutex_unlock(&dp_drv->train_mutex);
goto exit;
}
mdss_dp_mainlink_reset(&dp_drv->ctrl_io);
@ -1238,7 +1242,6 @@ static int mdss_dp_on_irq(struct mdss_dp_drv_pdata *dp_drv)
pr_debug("end\n");
exit:
mutex_unlock(&dp_drv->train_mutex);
return ret;
}
@ -1351,9 +1354,16 @@ static inline bool mdss_dp_is_link_training_requested(
return (dp->test_data.test_requested == TEST_LINK_TRAINING);
}
static inline bool mdss_dp_is_phy_test_pattern_requested(
struct mdss_dp_drv_pdata *dp)
{
return (dp->test_data.test_requested == PHY_TEST_PATTERN);
}
static inline bool mdss_dp_soft_hpd_reset(struct mdss_dp_drv_pdata *dp)
{
return mdss_dp_is_link_training_requested(dp) &&
return (mdss_dp_is_link_training_requested(dp) ||
mdss_dp_is_phy_test_pattern_requested(dp)) &&
dp->alt_mode.dp_status.hpd_irq;
}
@ -2500,6 +2510,57 @@ static int mdss_dp_process_link_training_request(struct mdss_dp_drv_pdata *dp)
return 0;
}
/**
* mdss_dp_process_phy_test_pattern_request() - process new phy test requests
* @dp: Display Port Driver data
*
* This function will handle new phy test pattern requests that are initiated
* by the sink. The function will return 0 if a phy test pattern has been
* processed, otherwise it will return -EINVAL.
*/
static int mdss_dp_process_phy_test_pattern_request(
struct mdss_dp_drv_pdata *dp)
{
u32 test_link_rate = 0, test_lane_count = 0;
if (!mdss_dp_is_phy_test_pattern_requested(dp))
return -EINVAL;
mdss_dp_send_test_response(dp);
test_link_rate = dp->test_data.test_link_rate;
test_lane_count = dp->test_data.test_lane_count;
pr_info("%s link rate = 0x%x, lane count = 0x%x\n",
mdss_dp_get_test_name(TEST_LINK_TRAINING),
test_link_rate, test_lane_count);
/**
* Retrain the mainlink if there is a change in link rate or lane
* count.
*/
if (mdss_dp_aux_is_link_rate_valid(test_link_rate) &&
mdss_dp_aux_is_lane_count_valid(test_lane_count) &&
((dp->dpcd.max_lane_count != test_lane_count) ||
(dp->link_rate != test_link_rate))) {
pr_info("updated link rate or lane count, retraining.\n");
dp->dpcd.max_lane_count = dp->test_data.test_lane_count;
dp->link_rate = dp->test_data.test_link_rate;
mdss_dp_link_retraining(dp);
}
mdss_dp_config_ctrl(dp);
mdss_dp_aux_update_voltage_and_pre_emphasis_lvl(dp);
mdss_dp_phy_send_test_pattern(dp);
return 0;
}
/**
* mdss_dp_process_downstream_port_status_change() - process port status changes
* @dp: Display Port Driver data
@ -2548,6 +2609,9 @@ static int mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp)
if (!ret)
goto exit;
ret = mdss_dp_process_phy_test_pattern_request(dp);
if (!ret)
goto exit;
pr_debug("done\n");
exit:
mdss_dp_reset_test_data(dp);

View file

@ -268,6 +268,7 @@ struct dpcd_test_request {
u32 test_requested;
u32 test_link_rate;
u32 test_lane_count;
u32 phy_test_pattern_sel;
u32 response;
};
@ -509,6 +510,51 @@ enum dp_lane_count {
DP_LANE_COUNT_4 = 4,
};
enum phy_test_pattern {
PHY_TEST_PATTERN_NONE,
PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING,
PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT,
PHY_TEST_PATTERN_PRBS7,
PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN,
PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN,
};
static inline char *mdss_dp_get_phy_test_pattern(u32 phy_test_pattern_sel)
{
switch (phy_test_pattern_sel) {
case PHY_TEST_PATTERN_NONE:
return DP_ENUM_STR(PHY_TEST_PATTERN_NONE);
case PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING:
return DP_ENUM_STR(PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING);
case PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
return DP_ENUM_STR(PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT);
case PHY_TEST_PATTERN_PRBS7:
return DP_ENUM_STR(PHY_TEST_PATTERN_PRBS7);
case PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN:
return DP_ENUM_STR(PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN);
case PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN:
return DP_ENUM_STR(PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN);
default:
return "unknown";
}
}
static inline bool mdss_dp_is_phy_test_pattern_supported(
u32 phy_test_pattern_sel)
{
switch (phy_test_pattern_sel) {
case PHY_TEST_PATTERN_NONE:
case PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING:
case PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
case PHY_TEST_PATTERN_PRBS7:
case PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN:
case PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN:
return true;
default:
return false;
}
}
enum dp_aux_error {
EDP_AUX_ERR_NONE = 0,
EDP_AUX_ERR_ADDR = -1,
@ -561,7 +607,7 @@ static inline char *mdss_dp_get_test_response(u32 test_response)
enum test_type {
UNKNOWN_TEST = 0,
TEST_LINK_TRAINING = BIT(0),
TEST_PATTERN = BIT(1),
PHY_TEST_PATTERN = BIT(3),
TEST_EDID_READ = BIT(2),
};
@ -569,7 +615,7 @@ static inline char *mdss_dp_get_test_name(u32 test_requested)
{
switch (test_requested) {
case TEST_LINK_TRAINING: return DP_ENUM_STR(TEST_LINK_TRAINING);
case TEST_PATTERN: return DP_ENUM_STR(TEST_PATTERN);
case PHY_TEST_PATTERN: return DP_ENUM_STR(PHY_TEST_PATTERN);
case TEST_EDID_READ: return DP_ENUM_STR(TEST_EDID_READ);
default: return "unknown";
}
@ -640,5 +686,10 @@ void *mdss_dp_get_hdcp_data(struct device *dev);
int mdss_dp_hdcp2p2_init(struct mdss_dp_drv_pdata *dp_drv);
bool mdss_dp_aux_clock_recovery_done(struct mdss_dp_drv_pdata *ep);
bool mdss_dp_aux_channel_eq_done(struct mdss_dp_drv_pdata *ep);
bool mdss_dp_aux_is_link_rate_valid(u32 link_rate);
bool mdss_dp_aux_is_lane_count_valid(u32 lane_count);
int mdss_dp_aux_link_status_read(struct mdss_dp_drv_pdata *ep, int len);
void mdss_dp_aux_update_voltage_and_pre_emphasis_lvl(
struct mdss_dp_drv_pdata *dp);
#endif /* MDSS_DP_H */

View file

@ -537,8 +537,9 @@ char mdss_dp_gen_link_clk(struct mdss_panel_info *pinfo, char lane_cnt)
else if (min_link_rate <= DP_LINK_RATE_540)
calc_link_rate = DP_LINK_RATE_540;
else {
pr_err("link_rate = %d is unsupported\n", min_link_rate);
calc_link_rate = 0;
/* Cap the link rate to the max supported rate */
pr_debug("link_rate = %d is unsupported\n", min_link_rate);
calc_link_rate = DP_LINK_RATE_540;
}
return calc_link_rate;
@ -960,7 +961,7 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
dp_sink_parse_sink_count(ep);
}
static int dp_link_status_read(struct mdss_dp_drv_pdata *ep, int len)
int mdss_dp_aux_link_status_read(struct mdss_dp_drv_pdata *ep, int len)
{
char *bp;
char data;
@ -1031,12 +1032,12 @@ void mdss_dp_aux_send_test_response(struct mdss_dp_drv_pdata *dp)
}
/**
* dp_is_link_rate_valid() - validates the link rate
* mdss_dp_aux_is_link_rate_valid() - validates the link rate
* @lane_rate: link rate requested by the sink
*
* Returns true if the requested link rate is supported.
*/
static bool dp_is_link_rate_valid(u32 link_rate)
bool mdss_dp_aux_is_link_rate_valid(u32 link_rate)
{
return (link_rate == DP_LINK_RATE_162) ||
(link_rate == DP_LINK_RATE_270) ||
@ -1044,12 +1045,12 @@ static bool dp_is_link_rate_valid(u32 link_rate)
}
/**
* dp_is_lane_count_valid() - validates the lane count
* mdss_dp_aux_is_lane_count_valid() - validates the lane count
* @lane_count: lane count requested by the sink
*
* Returns true if the requested lane count is supported.
*/
static bool dp_is_lane_count_valid(u32 lane_count)
bool mdss_dp_aux_is_lane_count_valid(u32 lane_count)
{
return (lane_count == DP_LANE_COUNT_1) ||
(lane_count == DP_LANE_COUNT_2) ||
@ -1086,7 +1087,7 @@ static int dp_parse_link_training_params(struct mdss_dp_drv_pdata *ep)
bp = rp->data;
data = *bp++;
if (!dp_is_link_rate_valid(data)) {
if (!mdss_dp_aux_is_link_rate_valid(data)) {
pr_err("invalid link rate = 0x%x\n", data);
ret = -EINVAL;
goto exit;
@ -1108,7 +1109,7 @@ static int dp_parse_link_training_params(struct mdss_dp_drv_pdata *ep)
data = *bp++;
data &= 0x1F;
if (!dp_is_lane_count_valid(data)) {
if (!mdss_dp_aux_is_lane_count_valid(data)) {
pr_err("invalid lane count = 0x%x\n", data);
ret = -EINVAL;
goto exit;
@ -1156,6 +1157,53 @@ static void dp_sink_parse_sink_count(struct mdss_dp_drv_pdata *ep)
ep->sink_count.count, ep->sink_count.cp_ready);
}
/**
* dp_parse_phy_test_params() - parses the phy test parameters
* @ep: Display Port Driver data
*
* Parses the DPCD (Byte 0x248) for the DP PHY test pattern that is being
* requested.
*/
static int dp_parse_phy_test_params(struct mdss_dp_drv_pdata *ep)
{
char *bp;
char data;
struct edp_buf *rp;
int rlen;
int const param_len = 0x1;
int const phy_test_pattern_addr = 0x248;
int const dpcd_version_1_2 = 0x12;
int ret = 0;
rlen = dp_aux_read_buf(ep, phy_test_pattern_addr, param_len, 0);
if (rlen < param_len) {
pr_err("failed to read phy test pattern\n");
ret = -EINVAL;
goto end;
}
rp = &ep->rxp;
bp = rp->data;
data = *bp++;
if (ep->dpcd.major == dpcd_version_1_2)
data = data & 0x7;
else
data = data & 0x3;
ep->test_data.phy_test_pattern_sel = data;
pr_debug("phy_test_pattern_sel = %s\n",
mdss_dp_get_phy_test_pattern(data));
if (!mdss_dp_is_phy_test_pattern_supported(data))
ret = -EINVAL;
end:
return ret;
}
/**
* dp_is_test_supported() - checks if test requested by sink is supported
* @test_requested: test requested by the sink
@ -1165,7 +1213,8 @@ static void dp_sink_parse_sink_count(struct mdss_dp_drv_pdata *ep)
static bool dp_is_test_supported(u32 test_requested)
{
return (test_requested == TEST_LINK_TRAINING) ||
(test_requested == TEST_EDID_READ);
(test_requested == TEST_EDID_READ) ||
(test_requested == PHY_TEST_PATTERN);
}
/**
@ -1229,8 +1278,19 @@ static void dp_sink_parse_test_request(struct mdss_dp_drv_pdata *ep)
pr_debug("%s requested\n", mdss_dp_get_test_name(data));
ep->test_data.test_requested = data;
if (ep->test_data.test_requested == TEST_LINK_TRAINING)
switch (ep->test_data.test_requested) {
case PHY_TEST_PATTERN:
ret = dp_parse_phy_test_params(ep);
if (ret)
break;
case TEST_LINK_TRAINING:
ret = dp_parse_link_training_params(ep);
break;
default:
pr_debug("test 0x%x not supported\n",
ep->test_data.test_requested);
return;
}
/**
* Send a TEST_ACK if all test parameters are valid, otherwise send
@ -1453,7 +1513,8 @@ char vm_voltage_swing[4][4] = {
{0x1E, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */
};
static void dp_voltage_pre_emphasise_set(struct mdss_dp_drv_pdata *dp)
static void dp_aux_set_voltage_and_pre_emphasis_lvl(
struct mdss_dp_drv_pdata *dp)
{
u32 value0 = 0;
u32 value1 = 0;
@ -1488,6 +1549,38 @@ static void dp_voltage_pre_emphasise_set(struct mdss_dp_drv_pdata *dp)
}
/**
* mdss_dp_aux_update_voltage_and_pre_emphasis_lvl() - updates DP PHY settings
* @ep: Display Port Driver data
*
* Updates the DP PHY with the requested voltage swing and pre-emphasis
* levels if they are different from the current settings.
*/
void mdss_dp_aux_update_voltage_and_pre_emphasis_lvl(
struct mdss_dp_drv_pdata *dp)
{
int const num_bytes = 6;
struct dpcd_link_status *status = &dp->link_status;
/* Read link status for updated voltage and pre-emphasis levels. */
mdss_dp_aux_link_status_read(dp, num_bytes);
pr_info("Current: v_level = %d, p_level = %d\n",
dp->v_level, dp->p_level);
pr_info("Requested: v_level = %d, p_level = %d\n",
status->req_voltage_swing[0],
status->req_pre_emphasis[0]);
if ((status->req_voltage_swing[0] != dp->v_level) ||
(status->req_pre_emphasis[0] != dp->p_level)) {
dp->v_level = status->req_voltage_swing[0];
dp->p_level = status->req_pre_emphasis[0];
dp_aux_set_voltage_and_pre_emphasis_lvl(dp);
}
pr_debug("end\n");
}
static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep)
{
int tries, old_v_level;
@ -1500,7 +1593,7 @@ static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep)
dp_host_train_set(ep, 0x01); /* train_1 */
dp_cap_lane_rate_set(ep);
dp_train_pattern_set_write(ep, 0x21); /* train_1 */
dp_voltage_pre_emphasise_set(ep);
dp_aux_set_voltage_and_pre_emphasis_lvl(ep);
tries = 0;
old_v_level = ep->v_level;
@ -1508,7 +1601,7 @@ static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep)
usleep_time = ep->dpcd.training_read_interval;
usleep_range(usleep_time, usleep_time);
dp_link_status_read(ep, 6);
mdss_dp_aux_link_status_read(ep, 6);
if (mdss_dp_aux_clock_recovery_done(ep)) {
ret = 0;
break;
@ -1531,7 +1624,7 @@ static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep)
}
dp_sink_train_set_adjust(ep);
dp_voltage_pre_emphasise_set(ep);
dp_aux_set_voltage_and_pre_emphasis_lvl(ep);
}
return ret;
@ -1555,13 +1648,13 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep)
dp_train_pattern_set_write(ep, pattern | 0x20);/* train_2 */
do {
dp_voltage_pre_emphasise_set(ep);
dp_aux_set_voltage_and_pre_emphasis_lvl(ep);
dp_host_train_set(ep, pattern);
usleep_time = ep->dpcd.training_read_interval;
usleep_range(usleep_time, usleep_time);
dp_link_status_read(ep, 6);
mdss_dp_aux_link_status_read(ep, 6);
if (mdss_dp_aux_channel_eq_done(ep)) {
ret = 0;
@ -1698,7 +1791,7 @@ void mdss_dp_aux_parse_sink_status_field(struct mdss_dp_drv_pdata *ep)
{
dp_sink_parse_sink_count(ep);
dp_sink_parse_test_request(ep);
dp_link_status_read(ep, 6);
mdss_dp_aux_link_status_read(ep, 6);
}
int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *ep)
@ -1706,7 +1799,7 @@ int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *ep)
struct dpcd_link_status *sp;
int ret = 0; /* not sync */
ret = dp_link_status_read(ep, 6);
ret = mdss_dp_aux_link_status_read(ep, 6);
if (ret) {
sp = &ep->link_status;

View file

@ -892,3 +892,87 @@ void mdss_dp_audio_enable(struct dss_io_data *ctrl_io, bool enable)
writel_relaxed(audio_ctrl, ctrl_io->base + MMSS_DP_AUDIO_CFG);
}
/**
* mdss_dp_phy_send_test_pattern() - sends the requested PHY test pattern
* @ep: Display Port Driver data
*
* Updates the DP controller state and sends the requested PHY test pattern
* to the sink.
*/
void mdss_dp_phy_send_test_pattern(struct mdss_dp_drv_pdata *dp)
{
struct dss_io_data *io = &dp->ctrl_io;
u32 phy_test_pattern_sel = dp->test_data.phy_test_pattern_sel;
u32 value = 0x0;
if (!mdss_dp_is_phy_test_pattern_supported(phy_test_pattern_sel)) {
pr_err("test pattern 0x%x not supported\n",
phy_test_pattern_sel);
return;
}
/* Disable mainlink */
writel_relaxed(0x0, io->base + DP_MAINLINK_CTRL);
/* Reset mainlink */
mdss_dp_mainlink_reset(io);
/* Enable mainlink */
writel_relaxed(0x0, io->base + DP_MAINLINK_CTRL);
/* Initialize DP state control */
mdss_dp_state_ctrl(io, 0x00);
pr_debug("phy_test_pattern_sel = %s\n",
mdss_dp_get_phy_test_pattern(phy_test_pattern_sel));
switch (phy_test_pattern_sel) {
case PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING:
mdss_dp_state_ctrl(io, BIT(0));
break;
case PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
value = readl_relaxed(io->base +
DP_HBR2_COMPLIANCE_SCRAMBLER_RESET);
value &= ~(1 << 16);
writel_relaxed(value, io->base +
DP_HBR2_COMPLIANCE_SCRAMBLER_RESET);
value |= 0xFC;
writel_relaxed(value, io->base +
DP_HBR2_COMPLIANCE_SCRAMBLER_RESET);
writel_relaxed(0x2, io->base + DP_MAINLINK_LEVELS);
mdss_dp_state_ctrl(io, BIT(4));
break;
case PHY_TEST_PATTERN_PRBS7:
mdss_dp_state_ctrl(io, BIT(5));
break;
case PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN:
mdss_dp_state_ctrl(io, BIT(6));
/* 00111110000011111000001111100000 */
writel_relaxed(0x3E0F83E0, io->base +
DP_TEST_80BIT_CUSTOM_PATTERN_REG0);
/* 00001111100000111110000011111000 */
writel_relaxed(0x0F83E0F8, io->base +
DP_TEST_80BIT_CUSTOM_PATTERN_REG1);
/* 1111100000111110 */
writel_relaxed(0x0000F83E, io->base +
DP_TEST_80BIT_CUSTOM_PATTERN_REG2);
break;
case PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN:
value = readl_relaxed(io->base +
DP_HBR2_COMPLIANCE_SCRAMBLER_RESET);
value |= BIT(16);
writel_relaxed(value, io->base +
DP_HBR2_COMPLIANCE_SCRAMBLER_RESET);
value |= 0xFC;
writel_relaxed(value, io->base +
DP_HBR2_COMPLIANCE_SCRAMBLER_RESET);
writel_relaxed(0x2, io->base + DP_MAINLINK_LEVELS);
mdss_dp_state_ctrl(io, BIT(4));
break;
default:
pr_debug("No valid test pattern requested: 0x%x\n",
phy_test_pattern_sel);
return;
}
}

View file

@ -60,6 +60,11 @@
#define DP_MAINLINK_LEVELS (0x00000444)
#define DP_TU (0x0000044C)
#define DP_HBR2_COMPLIANCE_SCRAMBLER_RESET (0x00000454)
#define DP_TEST_80BIT_CUSTOM_PATTERN_REG0 (0x000004C0)
#define DP_TEST_80BIT_CUSTOM_PATTERN_REG1 (0x000004C4)
#define DP_TEST_80BIT_CUSTOM_PATTERN_REG2 (0x000004C8)
#define MMSS_DP_AUDIO_TIMING_GEN (0x00000480)
#define MMSS_DP_AUDIO_TIMING_RBR_32 (0x00000484)
#define MMSS_DP_AUDIO_TIMING_HBR_32 (0x00000488)
@ -315,5 +320,6 @@ void mdss_dp_audio_set_sample_rate(struct dss_io_data *ctrl_io,
void mdss_dp_set_safe_to_exit_level(struct dss_io_data *ctrl_io,
uint32_t lane_cnt);
int mdss_dp_aux_read_rx_status(struct mdss_dp_drv_pdata *dp, u8 *rx_status);
void mdss_dp_phy_send_test_pattern(struct mdss_dp_drv_pdata *dp);
#endif /* __DP_UTIL_H__ */