[PATCH v2] drm/mcde: Retry DSI read/write transactions

Stephan Gerhold stephan at gerhold.net
Sat Aug 15 08:08:13 UTC 2020


On Fri, Aug 14, 2020 at 09:44:51PM +0200, Linus Walleij wrote:
> The vendor driver makes a few retries on read DSI
> transactions, something that is needed especially in
> case of read (such as reading the panel MTP ID) while
> the panel is running in video mode. This happens on
> the Samsung s6e63m0 panel on the Golden device.
> 
> Retry reads and writes alike three times.
> 
> Cc: Stephan Gerhold <stephan at gerhold.net>
> Signed-off-by: Linus Walleij <linus.walleij at linaro.org>
> ---
> ChangeLog v1->v2:
> - Retry three times.
> - Only retry the actual command transmission like the vendor
>   driver does, no need to set up all registers and do checks
>   all over. Break out a part of the mcde_dsi_host_transfer()
>   function to achieve this.
> ---
>  drivers/gpu/drm/mcde/mcde_dsi.c | 158 +++++++++++++++++++-------------
>  1 file changed, 92 insertions(+), 66 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
> index 4ce8cc5f0be2..b3c5d3cbda92 100644
> --- a/drivers/gpu/drm/mcde/mcde_dsi.c
> +++ b/drivers/gpu/drm/mcde/mcde_dsi.c
> @@ -208,79 +208,16 @@ static int mcde_dsi_host_detach(struct mipi_dsi_host *host,
>  	 (type == MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM) || \
>  	 (type == MIPI_DSI_DCS_READ))
>  
> -static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host,
> -				      const struct mipi_dsi_msg *msg)
> +static int mcde_dsi_execute_transfer(struct mcde_dsi *d,
> +				     const struct mipi_dsi_msg *msg)
>  {
> -	struct mcde_dsi *d = host_to_mcde_dsi(host);
>  	const u32 loop_delay_us = 10; /* us */
> -	const u8 *tx = msg->tx_buf;
>  	u32 loop_counter;
>  	size_t txlen = msg->tx_len;
>  	size_t rxlen = msg->rx_len;
> +	int i;
>  	u32 val;
>  	int ret;
> -	int i;
> -
> -	if (txlen > 16) {
> -		dev_err(d->dev,
> -			"dunno how to write more than 16 bytes yet\n");
> -		return -EIO;
> -	}
> -	if (rxlen > 4) {
> -		dev_err(d->dev,
> -			"dunno how to read more than 4 bytes yet\n");
> -		return -EIO;
> -	}
> -
> -	dev_dbg(d->dev,
> -		"message to channel %d, write %zd bytes read %zd bytes\n",
> -		msg->channel, txlen, rxlen);
> -
> -	/* Command "nature" */
> -	if (MCDE_DSI_HOST_IS_READ(msg->type))
> -		/* MCTL_MAIN_DATA_CTL already set up */
> -		val = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_READ;
> -	else
> -		val = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_WRITE;
> -	/*
> -	 * More than 2 bytes will not fit in a single packet, so it's
> -	 * time to set the "long not short" bit. One byte is used by
> -	 * the MIPI DCS command leaving just one byte for the payload
> -	 * in a short package.
> -	 */
> -	if (mipi_dsi_packet_format_is_long(msg->type))
> -		val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT;
> -	val |= 0 << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID_SHIFT;
> -	val |= txlen << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE_SHIFT;
> -	val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN;
> -	val |= msg->type << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SHIFT;
> -	writel(val, d->regs + DSI_DIRECT_CMD_MAIN_SETTINGS);
> -
> -	/* MIPI DCS command is part of the data */
> -	if (txlen > 0) {
> -		val = 0;
> -		for (i = 0; i < 4 && i < txlen; i++)
> -			val |= tx[i] << (i * 8);
> -	}
> -	writel(val, d->regs + DSI_DIRECT_CMD_WRDAT0);
> -	if (txlen > 4) {
> -		val = 0;
> -		for (i = 0; i < 4 && (i + 4) < txlen; i++)
> -			val |= tx[i + 4] << (i * 8);
> -		writel(val, d->regs + DSI_DIRECT_CMD_WRDAT1);
> -	}
> -	if (txlen > 8) {
> -		val = 0;
> -		for (i = 0; i < 4 && (i + 8) < txlen; i++)
> -			val |= tx[i + 8] << (i * 8);
> -		writel(val, d->regs + DSI_DIRECT_CMD_WRDAT2);
> -	}
> -	if (txlen > 12) {
> -		val = 0;
> -		for (i = 0; i < 4 && (i + 12) < txlen; i++)
> -			val |= tx[i + 12] << (i * 8);
> -		writel(val, d->regs + DSI_DIRECT_CMD_WRDAT3);
> -	}
>  
>  	writel(~0, d->regs + DSI_DIRECT_CMD_STS_CLR);
>  	writel(~0, d->regs + DSI_CMD_MODE_STS_CLR);
> @@ -297,6 +234,7 @@ static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host,
>  			usleep_range(loop_delay_us, (loop_delay_us * 3) / 2);
>  		if (!loop_counter) {
>  			dev_err(d->dev, "DSI read timeout!\n");
> +			/* Set exit code and retry */
>  			return -ETIME;
>  		}
>  	} else {
> @@ -307,6 +245,7 @@ static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host,
>  			usleep_range(loop_delay_us, (loop_delay_us * 3) / 2);
>  
>  		if (!loop_counter) {
> +			/* Set exit code and retry */
>  			dev_err(d->dev, "DSI write timeout!\n");
>  			return -ETIME;
>  		}
> @@ -348,6 +287,93 @@ static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host,
>  		ret = rdsz;
>  	}
>  
> +	/* Successful transmission */
> +	return ret;
> +}
> +
> +static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host,
> +				      const struct mipi_dsi_msg *msg)
> +{
> +	struct mcde_dsi *d = host_to_mcde_dsi(host);
> +	const u8 *tx = msg->tx_buf;
> +	size_t txlen = msg->tx_len;
> +	size_t rxlen = msg->rx_len;
> +	unsigned int retries = 0;
> +	u32 val;
> +	int ret;
> +	int i;
> +
> +	if (txlen > 16) {
> +		dev_err(d->dev,
> +			"dunno how to write more than 16 bytes yet\n");
> +		return -EIO;
> +	}
> +	if (rxlen > 4) {
> +		dev_err(d->dev,
> +			"dunno how to read more than 4 bytes yet\n");
> +		return -EIO;
> +	}
> +
> +	dev_dbg(d->dev,
> +		"message to channel %d, write %zd bytes read %zd bytes\n",
> +		msg->channel, txlen, rxlen);
> +
> +	/* Command "nature" */
> +	if (MCDE_DSI_HOST_IS_READ(msg->type))
> +		/* MCTL_MAIN_DATA_CTL already set up */
> +		val = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_READ;
> +	else
> +		val = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_WRITE;
> +	/*
> +	 * More than 2 bytes will not fit in a single packet, so it's
> +	 * time to set the "long not short" bit. One byte is used by
> +	 * the MIPI DCS command leaving just one byte for the payload
> +	 * in a short package.
> +	 */
> +	if (mipi_dsi_packet_format_is_long(msg->type))
> +		val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT;
> +	val |= 0 << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID_SHIFT;
> +	val |= txlen << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE_SHIFT;
> +	val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN;
> +	val |= msg->type << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SHIFT;
> +	writel(val, d->regs + DSI_DIRECT_CMD_MAIN_SETTINGS);
> +
> +	/* MIPI DCS command is part of the data */
> +	if (txlen > 0) {
> +		val = 0;
> +		for (i = 0; i < 4 && i < txlen; i++)
> +			val |= tx[i] << (i * 8);
> +	}
> +	writel(val, d->regs + DSI_DIRECT_CMD_WRDAT0);
> +	if (txlen > 4) {
> +		val = 0;
> +		for (i = 0; i < 4 && (i + 4) < txlen; i++)
> +			val |= tx[i + 4] << (i * 8);
> +		writel(val, d->regs + DSI_DIRECT_CMD_WRDAT1);
> +	}
> +	if (txlen > 8) {
> +		val = 0;
> +		for (i = 0; i < 4 && (i + 8) < txlen; i++)
> +			val |= tx[i + 8] << (i * 8);
> +		writel(val, d->regs + DSI_DIRECT_CMD_WRDAT2);
> +	}
> +	if (txlen > 12) {
> +		val = 0;
> +		for (i = 0; i < 4 && (i + 12) < txlen; i++)
> +			val |= tx[i + 12] << (i * 8);
> +		writel(val, d->regs + DSI_DIRECT_CMD_WRDAT3);
> +	}
> +
> +	while (retries < 3) {
> +		ret = mcde_dsi_execute_transfer(d, msg);
> +		if (ret >= 0)
> +			break;
> +		retries++;
> +	}

It might be a bit nicer to write this as a for loop, i.e.

	for (retries = 0; retries < 3; retries++) {
		ret = mcde_dsi_execute_transfer(d, msg);
		if (ret >= 0)
			break;
	}

But I guess it does not make much of a difference here.

Just a thought, looks good to me otherwise!
Reviewed-by: Stephan Gerhold <stephan at gerhold.net>

Thanks!
Stephan

> +	if (ret < 0 && retries)
> +		dev_err(d->dev, "gave up after %d retries\n", retries);
> +
> +	/* Clear any errors */
>  	writel(~0, d->regs + DSI_DIRECT_CMD_STS_CLR);
>  	writel(~0, d->regs + DSI_CMD_MODE_STS_CLR);
>  
> -- 
> 2.26.2
> 


More information about the dri-devel mailing list