Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f5e76998 authored by Aravind Venkateswaran's avatar Aravind Venkateswaran Committed by Gerrit - the friendly Code Review server
Browse files

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: default avatarAravind Venkateswaran <aravindh@codeaurora.org>
Signed-off-by: default avatarTatenda Chipeperekwa <tatendac@codeaurora.org>
parent 9b87a506
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -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;

+17 −0
Original line number Diff line number Diff line
@@ -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);
+133 −24
Original line number Diff line number Diff line
@@ -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;
+29 −0
Original line number Diff line number Diff line
@@ -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;
+8 −0
Original line number Diff line number Diff line
@@ -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);