diff mbox series

[7/7] drm/panel: sitronix-st7789v: Check display ID

Message ID 20230609145951.853533-8-miquel.raynal@bootlin.com (mailing list archive)
State New, archived
Headers show
Series drm/panel: sitronix-st7789v: Support ET028013DMA panel | expand

Commit Message

Miquel Raynal June 9, 2023, 2:59 p.m. UTC
A very basic debugging rule when a device is connected for the first
time is to access a read-only register which contains known data in
order to ensure the communication protocol is properly working. This
driver lacked any read helper which is often a critical peace for
fastening bring-ups.

Add a read helper and use it to verify the communication with the panel
is working as soon as possible in order to fail early if this is not the
case.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 .../gpu/drm/panel/panel-sitronix-st7789v.c    | 78 +++++++++++++++++++
 1 file changed, 78 insertions(+)

Comments

Sam Ravnborg June 10, 2023, 8:45 p.m. UTC | #1
Hi Miquel,

On Fri, Jun 09, 2023 at 04:59:51PM +0200, Miquel Raynal wrote:
> A very basic debugging rule when a device is connected for the first
> time is to access a read-only register which contains known data in
> order to ensure the communication protocol is properly working. This
> driver lacked any read helper which is often a critical peace for
> fastening bring-ups.
> 
> Add a read helper and use it to verify the communication with the panel
> is working as soon as possible in order to fail early if this is not the
> case.

The read helper seems like a log of general boiler plate code.
I briefly looked into the use of regmap for the spi communication,
but it did not look like it supported the bit9 stuff.

I did not stare enough to add a reviewd by, but the approach is fine
and it is good to detech issues early.

Acked-by: Sam Ravnborg <sam@ravnborg.org>
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> ---
>  .../gpu/drm/panel/panel-sitronix-st7789v.c    | 78 +++++++++++++++++++
>  1 file changed, 78 insertions(+)
> 
> diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
> index 7de192a3a8aa..fb21d52a7a63 100644
> --- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
> +++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
> @@ -113,6 +113,9 @@
>  			return val;		\
>  	} while (0)
>  
> +#define ST7789V_IDS { 0x85, 0x85, 0x52 }
> +#define ST7789V_IDS_SIZE 3
> +
>  struct st7789v_panel_info {
>  	const struct drm_display_mode *display_mode;
>  	u16 width_mm;
> @@ -165,6 +168,77 @@ static int st7789v_write_data(struct st7789v *ctx, u8 cmd)
>  	return st7789v_spi_write(ctx, ST7789V_DATA, cmd);
>  }
>  
> +static int st7789v_read_data(struct st7789v *ctx, u8 cmd, u8 *buf,
> +			     unsigned int len)
> +{
> +	struct spi_transfer xfer[2] = { };
> +	struct spi_message msg;
> +	u16 txbuf = ((ST7789V_COMMAND & 1) << 8) | cmd;
> +	u16 rxbuf[4] = {};
> +	u8 bit9 = 0;
> +	int ret, i;
> +
> +	switch (len) {
> +	case 1:
> +	case 3:
> +	case 4:
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	spi_message_init(&msg);
> +
> +	xfer[0].tx_buf = &txbuf;
> +	xfer[0].len = sizeof(txbuf);
> +	spi_message_add_tail(&xfer[0], &msg);
> +
> +	xfer[1].rx_buf = rxbuf;
> +	xfer[1].len = len * 2;
> +	spi_message_add_tail(&xfer[1], &msg);
> +
> +	ret = spi_sync(ctx->spi, &msg);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < len; i++) {
> +		buf[i] = rxbuf[i] >> i | (bit9 << (9 - i));
> +		if (i)
> +			bit9 = rxbuf[i] & GENMASK(i - 1, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +static int st7789v_check_id(struct drm_panel *panel)
> +{
> +	const u8 st7789v_ids[ST7789V_IDS_SIZE] = ST7789V_IDS;
> +	struct st7789v *ctx = panel_to_st7789v(panel);
> +	bool invalid_ids = false;
> +	int ret, i;
> +	u8 ids[3];
> +
> +	ret = st7789v_read_data(ctx, MIPI_DCS_GET_DISPLAY_ID, ids, ST7789V_IDS_SIZE);
> +	if (ret) {
> +		dev_err(panel->dev, "Failed to read IDs\n");
> +		return ret;
> +	}
> +
> +	for (i = 0; i < ST7789V_IDS_SIZE; i++) {
> +		if (ids[i] != st7789v_ids[i]) {
> +			invalid_ids = true;
> +			break;
> +		}
> +	}
> +
> +	if (invalid_ids) {
> +		dev_err(panel->dev, "Unrecognized panel IDs");
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
>  static const struct drm_display_mode default_mode = {
>  	.clock = 7000,
>  	.hdisplay = 240,
> @@ -237,6 +311,10 @@ static int st7789v_prepare(struct drm_panel *panel)
>  	gpiod_set_value(ctx->reset, 0);
>  	msleep(120);
>  
> +	ret = st7789v_check_id(panel);
> +	if (ret)
> +		return ret;
> +
>  	ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_EXIT_SLEEP_MODE));
>  
>  	/* We need to wait 120ms after a sleep out command */
> -- 
> 2.34.1
Michael Riesch June 13, 2023, 6:25 a.m. UTC | #2
Hi Miquel,

On 6/9/23 16:59, Miquel Raynal wrote:
> A very basic debugging rule when a device is connected for the first
> time is to access a read-only register which contains known data in
> order to ensure the communication protocol is properly working. This
> driver lacked any read helper which is often a critical peace for
> fastening bring-ups.

I am afraid I don't get the last sentence. s/peace/piece? s/for
fastening/to speed up ? Only guessing here.

Best regards,
Michael

> Add a read helper and use it to verify the communication with the panel
> is working as soon as possible in order to fail early if this is not the
> case.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> ---
>  .../gpu/drm/panel/panel-sitronix-st7789v.c    | 78 +++++++++++++++++++
>  1 file changed, 78 insertions(+)
> 
> diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
> index 7de192a3a8aa..fb21d52a7a63 100644
> --- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
> +++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
> @@ -113,6 +113,9 @@
>  			return val;		\
>  	} while (0)
>  
> +#define ST7789V_IDS { 0x85, 0x85, 0x52 }
> +#define ST7789V_IDS_SIZE 3
> +
>  struct st7789v_panel_info {
>  	const struct drm_display_mode *display_mode;
>  	u16 width_mm;
> @@ -165,6 +168,77 @@ static int st7789v_write_data(struct st7789v *ctx, u8 cmd)
>  	return st7789v_spi_write(ctx, ST7789V_DATA, cmd);
>  }
>  
> +static int st7789v_read_data(struct st7789v *ctx, u8 cmd, u8 *buf,
> +			     unsigned int len)
> +{
> +	struct spi_transfer xfer[2] = { };
> +	struct spi_message msg;
> +	u16 txbuf = ((ST7789V_COMMAND & 1) << 8) | cmd;
> +	u16 rxbuf[4] = {};
> +	u8 bit9 = 0;
> +	int ret, i;
> +
> +	switch (len) {
> +	case 1:
> +	case 3:
> +	case 4:
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	spi_message_init(&msg);
> +
> +	xfer[0].tx_buf = &txbuf;
> +	xfer[0].len = sizeof(txbuf);
> +	spi_message_add_tail(&xfer[0], &msg);
> +
> +	xfer[1].rx_buf = rxbuf;
> +	xfer[1].len = len * 2;
> +	spi_message_add_tail(&xfer[1], &msg);
> +
> +	ret = spi_sync(ctx->spi, &msg);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < len; i++) {
> +		buf[i] = rxbuf[i] >> i | (bit9 << (9 - i));
> +		if (i)
> +			bit9 = rxbuf[i] & GENMASK(i - 1, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +static int st7789v_check_id(struct drm_panel *panel)
> +{
> +	const u8 st7789v_ids[ST7789V_IDS_SIZE] = ST7789V_IDS;
> +	struct st7789v *ctx = panel_to_st7789v(panel);
> +	bool invalid_ids = false;
> +	int ret, i;
> +	u8 ids[3];
> +
> +	ret = st7789v_read_data(ctx, MIPI_DCS_GET_DISPLAY_ID, ids, ST7789V_IDS_SIZE);
> +	if (ret) {
> +		dev_err(panel->dev, "Failed to read IDs\n");
> +		return ret;
> +	}
> +
> +	for (i = 0; i < ST7789V_IDS_SIZE; i++) {
> +		if (ids[i] != st7789v_ids[i]) {
> +			invalid_ids = true;
> +			break;
> +		}
> +	}
> +
> +	if (invalid_ids) {
> +		dev_err(panel->dev, "Unrecognized panel IDs");
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
>  static const struct drm_display_mode default_mode = {
>  	.clock = 7000,
>  	.hdisplay = 240,
> @@ -237,6 +311,10 @@ static int st7789v_prepare(struct drm_panel *panel)
>  	gpiod_set_value(ctx->reset, 0);
>  	msleep(120);
>  
> +	ret = st7789v_check_id(panel);
> +	if (ret)
> +		return ret;
> +
>  	ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_EXIT_SLEEP_MODE));
>  
>  	/* We need to wait 120ms after a sleep out command */
Sebastian Reichel June 14, 2023, 11:27 p.m. UTC | #3
Hi,

On Sat, Jun 10, 2023 at 10:45:25PM +0200, Sam Ravnborg wrote:
> Hi Miquel,
> 
> On Fri, Jun 09, 2023 at 04:59:51PM +0200, Miquel Raynal wrote:
> > A very basic debugging rule when a device is connected for the first
> > time is to access a read-only register which contains known data in
> > order to ensure the communication protocol is properly working. This
> > driver lacked any read helper which is often a critical peace for
> > fastening bring-ups.
> > 
> > Add a read helper and use it to verify the communication with the panel
> > is working as soon as possible in order to fail early if this is not the
> > case.
> 
> The read helper seems like a log of general boiler plate code.
> I briefly looked into the use of regmap for the spi communication,
> but it did not look like it supported the bit9 stuff.
> 
> I did not stare enough to add a reviewd by, but the approach is fine
> and it is good to detech issues early.

The st7789v datasheet describes a setup where SPI is connected
unidirectional (i.e. without MISO). In that case the ID check
will fail.

-- Sebastian

> 
> Acked-by: Sam Ravnborg <sam@ravnborg.org>
> > 
> > Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> > ---
> >  .../gpu/drm/panel/panel-sitronix-st7789v.c    | 78 +++++++++++++++++++
> >  1 file changed, 78 insertions(+)
> > 
> > diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
> > index 7de192a3a8aa..fb21d52a7a63 100644
> > --- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
> > +++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
> > @@ -113,6 +113,9 @@
> >  			return val;		\
> >  	} while (0)
> >  
> > +#define ST7789V_IDS { 0x85, 0x85, 0x52 }
> > +#define ST7789V_IDS_SIZE 3
> > +
> >  struct st7789v_panel_info {
> >  	const struct drm_display_mode *display_mode;
> >  	u16 width_mm;
> > @@ -165,6 +168,77 @@ static int st7789v_write_data(struct st7789v *ctx, u8 cmd)
> >  	return st7789v_spi_write(ctx, ST7789V_DATA, cmd);
> >  }
> >  
> > +static int st7789v_read_data(struct st7789v *ctx, u8 cmd, u8 *buf,
> > +			     unsigned int len)
> > +{
> > +	struct spi_transfer xfer[2] = { };
> > +	struct spi_message msg;
> > +	u16 txbuf = ((ST7789V_COMMAND & 1) << 8) | cmd;
> > +	u16 rxbuf[4] = {};
> > +	u8 bit9 = 0;
> > +	int ret, i;
> > +
> > +	switch (len) {
> > +	case 1:
> > +	case 3:
> > +	case 4:
> > +		break;
> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +
> > +	spi_message_init(&msg);
> > +
> > +	xfer[0].tx_buf = &txbuf;
> > +	xfer[0].len = sizeof(txbuf);
> > +	spi_message_add_tail(&xfer[0], &msg);
> > +
> > +	xfer[1].rx_buf = rxbuf;
> > +	xfer[1].len = len * 2;
> > +	spi_message_add_tail(&xfer[1], &msg);
> > +
> > +	ret = spi_sync(ctx->spi, &msg);
> > +	if (ret)
> > +		return ret;
> > +
> > +	for (i = 0; i < len; i++) {
> > +		buf[i] = rxbuf[i] >> i | (bit9 << (9 - i));
> > +		if (i)
> > +			bit9 = rxbuf[i] & GENMASK(i - 1, 0);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int st7789v_check_id(struct drm_panel *panel)
> > +{
> > +	const u8 st7789v_ids[ST7789V_IDS_SIZE] = ST7789V_IDS;
> > +	struct st7789v *ctx = panel_to_st7789v(panel);
> > +	bool invalid_ids = false;
> > +	int ret, i;
> > +	u8 ids[3];
> > +
> > +	ret = st7789v_read_data(ctx, MIPI_DCS_GET_DISPLAY_ID, ids, ST7789V_IDS_SIZE);
> > +	if (ret) {
> > +		dev_err(panel->dev, "Failed to read IDs\n");
> > +		return ret;
> > +	}
> > +
> > +	for (i = 0; i < ST7789V_IDS_SIZE; i++) {
> > +		if (ids[i] != st7789v_ids[i]) {
> > +			invalid_ids = true;
> > +			break;
> > +		}
> > +	}
> > +
> > +	if (invalid_ids) {
> > +		dev_err(panel->dev, "Unrecognized panel IDs");
> > +		return -EIO;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> >  static const struct drm_display_mode default_mode = {
> >  	.clock = 7000,
> >  	.hdisplay = 240,
> > @@ -237,6 +311,10 @@ static int st7789v_prepare(struct drm_panel *panel)
> >  	gpiod_set_value(ctx->reset, 0);
> >  	msleep(120);
> >  
> > +	ret = st7789v_check_id(panel);
> > +	if (ret)
> > +		return ret;
> > +
> >  	ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_EXIT_SLEEP_MODE));
> >  
> >  	/* We need to wait 120ms after a sleep out command */
> > -- 
> > 2.34.1
Miquel Raynal June 16, 2023, 10:13 a.m. UTC | #4
Hi Sebastian,

sre@kernel.org wrote on Thu, 15 Jun 2023 01:27:24 +0200:

> Hi,
> 
> On Sat, Jun 10, 2023 at 10:45:25PM +0200, Sam Ravnborg wrote:
> > Hi Miquel,
> > 
> > On Fri, Jun 09, 2023 at 04:59:51PM +0200, Miquel Raynal wrote:  
> > > A very basic debugging rule when a device is connected for the first
> > > time is to access a read-only register which contains known data in
> > > order to ensure the communication protocol is properly working. This
> > > driver lacked any read helper which is often a critical peace for
> > > fastening bring-ups.
> > > 
> > > Add a read helper and use it to verify the communication with the panel
> > > is working as soon as possible in order to fail early if this is not the
> > > case.  
> > 
> > The read helper seems like a log of general boiler plate code.
> > I briefly looked into the use of regmap for the spi communication,
> > but it did not look like it supported the bit9 stuff.
> > 
> > I did not stare enough to add a reviewd by, but the approach is fine
> > and it is good to detech issues early.  
> 
> The st7789v datasheet describes a setup where SPI is connected
> unidirectional (i.e. without MISO). In that case the ID check
> will fail.

Right. I'll add a (spi->mode & SPI_NO_RX) check, as the default is to
have both lines, if there is no MISO line, I'd expect it to be
described in the DT, otherwise the description is broken.

Thanks,
Miquèl
Sebastian Reichel June 16, 2023, 1:38 p.m. UTC | #5
Hi,

On Fri, Jun 16, 2023 at 12:13:45PM +0200, Miquel Raynal wrote:
> sre@kernel.org wrote on Thu, 15 Jun 2023 01:27:24 +0200:
> > On Sat, Jun 10, 2023 at 10:45:25PM +0200, Sam Ravnborg wrote:
> > > On Fri, Jun 09, 2023 at 04:59:51PM +0200, Miquel Raynal wrote:  
> > > > A very basic debugging rule when a device is connected for the first
> > > > time is to access a read-only register which contains known data in
> > > > order to ensure the communication protocol is properly working. This
> > > > driver lacked any read helper which is often a critical peace for
> > > > fastening bring-ups.
> > > > 
> > > > Add a read helper and use it to verify the communication with the panel
> > > > is working as soon as possible in order to fail early if this is not the
> > > > case.  
> > > 
> > > The read helper seems like a log of general boiler plate code.
> > > I briefly looked into the use of regmap for the spi communication,
> > > but it did not look like it supported the bit9 stuff.
> > > 
> > > I did not stare enough to add a reviewd by, but the approach is fine
> > > and it is good to detech issues early.  
> > 
> > The st7789v datasheet describes a setup where SPI is connected
> > unidirectional (i.e. without MISO). In that case the ID check
> > will fail.
> 
> Right. I'll add a (spi->mode & SPI_NO_RX) check, as the default is to
> have both lines, if there is no MISO line, I'd expect it to be
> described in the DT, otherwise the description is broken.

Checking for SPI_NO_RX sounds good to me.

-- Sebastian
diff mbox series

Patch

diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
index 7de192a3a8aa..fb21d52a7a63 100644
--- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
+++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
@@ -113,6 +113,9 @@ 
 			return val;		\
 	} while (0)
 
+#define ST7789V_IDS { 0x85, 0x85, 0x52 }
+#define ST7789V_IDS_SIZE 3
+
 struct st7789v_panel_info {
 	const struct drm_display_mode *display_mode;
 	u16 width_mm;
@@ -165,6 +168,77 @@  static int st7789v_write_data(struct st7789v *ctx, u8 cmd)
 	return st7789v_spi_write(ctx, ST7789V_DATA, cmd);
 }
 
+static int st7789v_read_data(struct st7789v *ctx, u8 cmd, u8 *buf,
+			     unsigned int len)
+{
+	struct spi_transfer xfer[2] = { };
+	struct spi_message msg;
+	u16 txbuf = ((ST7789V_COMMAND & 1) << 8) | cmd;
+	u16 rxbuf[4] = {};
+	u8 bit9 = 0;
+	int ret, i;
+
+	switch (len) {
+	case 1:
+	case 3:
+	case 4:
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	spi_message_init(&msg);
+
+	xfer[0].tx_buf = &txbuf;
+	xfer[0].len = sizeof(txbuf);
+	spi_message_add_tail(&xfer[0], &msg);
+
+	xfer[1].rx_buf = rxbuf;
+	xfer[1].len = len * 2;
+	spi_message_add_tail(&xfer[1], &msg);
+
+	ret = spi_sync(ctx->spi, &msg);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < len; i++) {
+		buf[i] = rxbuf[i] >> i | (bit9 << (9 - i));
+		if (i)
+			bit9 = rxbuf[i] & GENMASK(i - 1, 0);
+	}
+
+	return 0;
+}
+
+static int st7789v_check_id(struct drm_panel *panel)
+{
+	const u8 st7789v_ids[ST7789V_IDS_SIZE] = ST7789V_IDS;
+	struct st7789v *ctx = panel_to_st7789v(panel);
+	bool invalid_ids = false;
+	int ret, i;
+	u8 ids[3];
+
+	ret = st7789v_read_data(ctx, MIPI_DCS_GET_DISPLAY_ID, ids, ST7789V_IDS_SIZE);
+	if (ret) {
+		dev_err(panel->dev, "Failed to read IDs\n");
+		return ret;
+	}
+
+	for (i = 0; i < ST7789V_IDS_SIZE; i++) {
+		if (ids[i] != st7789v_ids[i]) {
+			invalid_ids = true;
+			break;
+		}
+	}
+
+	if (invalid_ids) {
+		dev_err(panel->dev, "Unrecognized panel IDs");
+		return -EIO;
+	}
+
+	return 0;
+}
+
 static const struct drm_display_mode default_mode = {
 	.clock = 7000,
 	.hdisplay = 240,
@@ -237,6 +311,10 @@  static int st7789v_prepare(struct drm_panel *panel)
 	gpiod_set_value(ctx->reset, 0);
 	msleep(120);
 
+	ret = st7789v_check_id(panel);
+	if (ret)
+		return ret;
+
 	ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_EXIT_SLEEP_MODE));
 
 	/* We need to wait 120ms after a sleep out command */