msm: mdss: dp: retry failed AUX transactions

Retry AUX read/write transactions that have failed either due
to the AUX controller hardware indicating an error via the ISR,
or due to a software based timeout while waiting for transaction
completion. The transaction retry strategy is as follows: first
repeat the transaction using the same PHY AUX settings, and then
retry the transaction using updated PHY AUX settings if repeating
the transaction has failed.

CRs-Fixed: 2006096
Change-Id: Id9c3c7ae1ab320540545b9c178d947a3cd023079
Signed-off-by: Aravind Venkateswaran <aravindh@codeaurora.org>
Signed-off-by: Tatenda Chipeperekwa <tatendac@codeaurora.org>
This commit is contained in:
Aravind Venkateswaran 2017-02-18 21:54:54 -08:00 committed by Gerrit - the friendly Code Review server
parent 9b87a5068e
commit f5e769987f
5 changed files with 188 additions and 26 deletions

View file

@ -2938,8 +2938,6 @@ static void mdss_dp_mainlink_push_idle(struct mdss_panel_data *pdata)
/* wait until link training is completed */
mutex_lock(&dp_drv->train_mutex);
mdss_dp_aux_set_sink_power_state(dp_drv, SINK_POWER_OFF);
reinit_completion(&dp_drv->idle_comp);
mdss_dp_state_ctrl(&dp_drv->ctrl_io, ST_PUSH_IDLE);
if (!wait_for_completion_timeout(&dp_drv->idle_comp,
@ -3339,6 +3337,7 @@ irqreturn_t dp_isr(int irq, void *ptr)
spin_lock(&dp->lock);
isr1 = dp_read(base + DP_INTR_STATUS);
isr2 = dp_read(base + DP_INTR_STATUS2);
pr_debug("isr1=0x%08x, isr2=0x%08x\n", isr1, isr2);
mask1 = isr1 & dp->mask1;

View file

@ -508,6 +508,23 @@ static inline char *mdss_dp_phy_aux_config_type_to_string(u32 cfg_type)
}
}
enum dp_aux_transaction {
DP_AUX_WRITE,
DP_AUX_READ
};
static inline char *mdss_dp_aux_transaction_to_string(u32 transaction)
{
switch (transaction) {
case DP_AUX_WRITE:
return DP_ENUM_STR(DP_AUX_WRITE);
case DP_AUX_READ:
return DP_ENUM_STR(DP_AUX_READ);
default:
return "unknown";
}
}
struct mdss_dp_drv_pdata {
/* device driver */
int (*on) (struct mdss_panel_data *pdata);

View file

@ -231,7 +231,12 @@ static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep,
len = dp_cmd_fifo_tx(&ep->txp, ep->base);
wait_for_completion_timeout(&ep->aux_comp, HZ/4);
if (!wait_for_completion_timeout(&ep->aux_comp, HZ/4)) {
pr_err("aux write timeout\n");
ep->aux_error_num = EDP_AUX_ERR_TOUT;
/* Reset the AUX controller state machine */
mdss_dp_aux_reset(&ep->ctrl_io);
}
if (ep->aux_error_num == EDP_AUX_ERR_NONE)
ret = len;
@ -243,13 +248,6 @@ static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep,
return ret;
}
int dp_aux_write(void *ep, struct edp_cmd *cmd)
{
int rc = dp_aux_write_cmds(ep, cmd);
return rc < 0 ? -EINVAL : 0;
}
static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep,
struct edp_cmd *cmds)
{
@ -287,7 +285,14 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep,
dp_cmd_fifo_tx(tp, ep->base);
wait_for_completion_timeout(&ep->aux_comp, HZ/4);
if (!wait_for_completion_timeout(&ep->aux_comp, HZ/4)) {
pr_err("aux read timeout\n");
ep->aux_error_num = EDP_AUX_ERR_TOUT;
/* Reset the AUX controller state machine */
mdss_dp_aux_reset(&ep->ctrl_io);
ret = ep->aux_error_num;
goto end;
}
if (ep->aux_error_num == EDP_AUX_ERR_NONE) {
ret = dp_cmd_fifo_rx(rp, len, ep->base);
@ -299,19 +304,13 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep,
ret = ep->aux_error_num;
}
end:
ep->aux_cmd_busy = 0;
mutex_unlock(&ep->aux_mutex);
return ret;
}
int dp_aux_read(void *ep, struct edp_cmd *cmds)
{
int rc = dp_aux_read_cmds(ep, cmds);
return rc < 0 ? -EINVAL : 0;
}
void dp_aux_native_handler(struct mdss_dp_drv_pdata *ep, u32 isr)
{
pr_debug("isr=0x%08x\n", isr);
@ -335,6 +334,7 @@ void dp_aux_native_handler(struct mdss_dp_drv_pdata *ep, u32 isr)
void dp_aux_i2c_handler(struct mdss_dp_drv_pdata *ep, u32 isr)
{
pr_debug("isr=0x%08x\n", isr);
if (isr & EDP_INTR_AUX_I2C_DONE) {
if (isr & (EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER))
ep->aux_error_num = EDP_AUX_ERR_NACK;
@ -362,8 +362,70 @@ void dp_aux_i2c_handler(struct mdss_dp_drv_pdata *ep, u32 isr)
complete(&ep->aux_comp);
}
static int dp_aux_write_buf(struct mdss_dp_drv_pdata *ep, u32 addr,
char *buf, int len, int i2c)
static int dp_aux_rw_cmds_retry(struct mdss_dp_drv_pdata *dp,
struct edp_cmd *cmd, enum dp_aux_transaction transaction)
{
int const retry_count = 5;
int adjust_count = 0;
int i;
u32 aux_cfg1_config_count;
int ret;
aux_cfg1_config_count = mdss_dp_phy_aux_get_config_cnt(dp,
PHY_AUX_CFG1);
retry:
i = 0;
ret = 0;
do {
struct edp_cmd cmd1 = *cmd;
dp->aux_error_num = EDP_AUX_ERR_NONE;
pr_debug("Trying %s, iteration count: %d\n",
mdss_dp_aux_transaction_to_string(transaction),
i + 1);
if (transaction == DP_AUX_READ)
ret = dp_aux_read_cmds(dp, &cmd1);
else if (transaction == DP_AUX_WRITE)
ret = dp_aux_write_cmds(dp, &cmd1);
i++;
} while ((i < retry_count) && (ret < 0));
if (ret >= 0) /* rw success */
goto end;
if (adjust_count >= aux_cfg1_config_count) {
pr_err("PHY_AUX_CONFIG1 calibration failed\n");
goto end;
}
/* Adjust AUX configuration and retry */
pr_debug("AUX failure (%d), adjust AUX settings\n", ret);
mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1);
adjust_count++;
goto retry;
end:
return ret;
}
/**
* dp_aux_write_buf_retry() - send a AUX write command
* @dp: display port driver data
* @addr: AUX address (in hex) to write the command to
* @buf: the buffer containing the actual payload
* @len: the length of the buffer @buf
* @i2c: indicates if it is an i2c-over-aux transaction
* @retry: specifies if retries should be attempted upon failures
*
* Send an AUX write command with the specified payload over the AUX
* channel. This function can send both native AUX command or an
* i2c-over-AUX command. In addition, if specified, it can also retry
* when failures are detected. The retry logic would adjust AUX PHY
* parameters on the fly.
*/
static int dp_aux_write_buf_retry(struct mdss_dp_drv_pdata *dp, u32 addr,
char *buf, int len, int i2c, bool retry)
{
struct edp_cmd cmd;
@ -374,11 +436,42 @@ static int dp_aux_write_buf(struct mdss_dp_drv_pdata *ep, u32 addr,
cmd.len = len & 0x0ff;
cmd.next = 0;
return dp_aux_write_cmds(ep, &cmd);
if (retry)
return dp_aux_rw_cmds_retry(dp, &cmd, DP_AUX_WRITE);
else
return dp_aux_write_cmds(dp, &cmd);
}
static int dp_aux_read_buf(struct mdss_dp_drv_pdata *ep, u32 addr,
int len, int i2c)
static int dp_aux_write_buf(struct mdss_dp_drv_pdata *dp, u32 addr,
char *buf, int len, int i2c)
{
return dp_aux_write_buf_retry(dp, addr, buf, len, i2c, true);
}
int dp_aux_write(void *dp, struct edp_cmd *cmd)
{
int rc = dp_aux_write_cmds(dp, cmd);
return rc < 0 ? -EINVAL : 0;
}
/**
* dp_aux_read_buf_retry() - send a AUX read command
* @dp: display port driver data
* @addr: AUX address (in hex) to write the command to
* @buf: the buffer containing the actual payload
* @len: the length of the buffer @buf
* @i2c: indicates if it is an i2c-over-aux transaction
* @retry: specifies if retries should be attempted upon failures
*
* Send an AUX write command with the specified payload over the AUX
* channel. This function can send both native AUX command or an
* i2c-over-AUX command. In addition, if specified, it can also retry
* when failures are detected. The retry logic would adjust AUX PHY
* parameters on the fly.
*/
static int dp_aux_read_buf_retry(struct mdss_dp_drv_pdata *dp, u32 addr,
int len, int i2c, bool retry)
{
struct edp_cmd cmd = {0};
@ -389,7 +482,23 @@ static int dp_aux_read_buf(struct mdss_dp_drv_pdata *ep, u32 addr,
cmd.len = len & 0x0ff;
cmd.next = 0;
return dp_aux_read_cmds(ep, &cmd);
if (retry)
return dp_aux_rw_cmds_retry(dp, &cmd, DP_AUX_READ);
else
return dp_aux_read_cmds(dp, &cmd);
}
static int dp_aux_read_buf(struct mdss_dp_drv_pdata *dp, u32 addr,
int len, int i2c)
{
return dp_aux_read_buf_retry(dp, addr, len, i2c, true);
}
int dp_aux_read(void *dp, struct edp_cmd *cmds)
{
int rc = dp_aux_read_cmds(dp, cmds);
return rc < 0 ? -EINVAL : 0;
}
/*
@ -787,9 +896,9 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp)
dp_sink_parse_test_request(dp);
do {
rlen = dp_aux_read_buf(dp, EDID_START_ADDRESS +
rlen = dp_aux_read_buf_retry(dp, EDID_START_ADDRESS +
(blk_num * EDID_BLOCK_SIZE),
EDID_BLOCK_SIZE, 1);
EDID_BLOCK_SIZE, 1, false);
if (rlen != EDID_BLOCK_SIZE) {
pr_err("Read failed. rlen=%d\n", rlen);
continue;

View file

@ -871,6 +871,35 @@ void mdss_dp_aux_set_limits(struct dss_io_data *ctrl_io)
writel_relaxed(max_aux_limits, ctrl_io->base + DP_AUX_LIMITS);
}
void mdss_dp_phy_aux_update_config(struct mdss_dp_drv_pdata *dp,
enum dp_phy_aux_config_type config_type)
{
u32 new_index;
struct dss_io_data *phy_io = &dp->phy_io;
struct mdss_dp_phy_cfg *cfg = mdss_dp_phy_aux_get_config(dp,
config_type);
if (!cfg) {
pr_err("invalid config type %s",
mdss_dp_phy_aux_config_type_to_string(config_type));
return;
}
new_index = (cfg->current_index + 1) % cfg->cfg_cnt;
pr_debug("Updating %s from 0x%08x to 0x%08x\n",
mdss_dp_phy_aux_config_type_to_string(config_type),
cfg->lut[cfg->current_index], cfg->lut[new_index]);
writel_relaxed(cfg->lut[new_index], phy_io->base + cfg->offset);
cfg->current_index = new_index;
/* Make sure the new HW configuration takes effect */
wmb();
/* Reset the AUX controller before any subsequent transactions */
mdss_dp_aux_reset(&dp->ctrl_io);
}
void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, char *l_map)
{
u8 bits_per_lane = 2;

View file

@ -280,6 +280,12 @@ static inline struct mdss_dp_phy_cfg *mdss_dp_phy_aux_get_config(
return &dp->aux_cfg[cfg_type];
}
static inline u32 mdss_dp_phy_aux_get_config_cnt(
struct mdss_dp_drv_pdata *dp, enum dp_phy_aux_config_type cfg_type)
{
return dp->aux_cfg[cfg_type].cfg_cnt;
}
void mdss_dp_aux_set_limits(struct dss_io_data *ctrl_io);
int dp_aux_read(void *ep, struct edp_cmd *cmds);
int dp_aux_write(void *ep, struct edp_cmd *cmd);
@ -296,6 +302,8 @@ void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io, u8 link_rate,
u8 ln_cnt, u32 res, struct mdss_panel_info *pinfo);
void mdss_dp_config_misc(struct mdss_dp_drv_pdata *dp, u32 bd, u32 cc);
void mdss_dp_phy_aux_setup(struct mdss_dp_drv_pdata *dp);
void mdss_dp_phy_aux_update_config(struct mdss_dp_drv_pdata *dp,
enum dp_phy_aux_config_type config_type);
void mdss_dp_hpd_configure(struct dss_io_data *ctrl_io, bool enable);
void mdss_dp_aux_ctrl(struct dss_io_data *ctrl_io, bool enable);
void mdss_dp_mainlink_ctrl(struct dss_io_data *ctrl_io, bool enable);