summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c3
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.h17
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c157
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.c29
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_util.h8
5 files changed, 188 insertions, 26 deletions
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c
index 8b8605977cb8..7360f512ea20 100644
--- a/drivers/video/fbdev/msm/mdss_dp.c
+++ b/drivers/video/fbdev/msm/mdss_dp.c
@@ -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;
diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h
index 3f94b507399e..47982d4846e0 100644
--- a/drivers/video/fbdev/msm/mdss_dp.h
+++ b/drivers/video/fbdev/msm/mdss_dp.h
@@ -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);
diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c
index b53bd079fd72..671ca8270e4c 100644
--- a/drivers/video/fbdev/msm/mdss_dp_aux.c
+++ b/drivers/video/fbdev/msm/mdss_dp_aux.c
@@ -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;
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c
index e1983dc28bcf..02212c061279 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.c
+++ b/drivers/video/fbdev/msm/mdss_dp_util.c
@@ -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;
diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h
index 5f85786510e5..4c93e48e97dc 100644
--- a/drivers/video/fbdev/msm/mdss_dp_util.h
+++ b/drivers/video/fbdev/msm/mdss_dp_util.h
@@ -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);