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:
parent
9b87a5068e
commit
f5e769987f
5 changed files with 188 additions and 26 deletions
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue