Skip to contentSkip to conversation

Sitronix ST7789V improvements

PATCH Applied Sebastian Reichel 3 participants 24 messages Jul 14, 2023 – Aug 1, 2023

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

v3 cover Sitronix ST7789V improvements

Hi,

This adds panel support for Inanbo T28CP45TN89, which I found inside of a handheld thermal camera. The panel is based on the st7789v controller. All information is based on reverse engineering. I also appended the series from Miquel Raynal adding EDT ET028013DMA panel support, so that I could easily test it with my SPI_NO_RX setup. They are slightly different due to rebasing.

Changes since PATCHv2:
* https://lore.kernel.org/all/[email protected]/
* https://lore.kernel.org/all/[email protected]/
* Add Rob Herring's R-b for the DT binding
* Make panel info "static const"
* Add Michael Riesch's R-b to all my patches
* Rebase to 6.5-rc1
* Append Miquel's series

Changes since PATCHv1:
* https://lore.kernel.org/all/[email protected]/
* Apply DT binding changes requested by Krzysztof Kozlowski and his Ack
* I changed the driver patches to avoid code duplication and splitted the code a bit more

Greetings,

-- Sebastian

Miquel Raynal (6):
dt-bindings: display: st7789v: Add the edt,et028013dma panel
compatible
dt-bindings: display: st7789v: bound the number of Rx data lines drm/panel: sitronix-st7789v: Use 9 bits per spi word by default drm/panel: sitronix-st7789v: Clarify a definition drm/panel: sitronix-st7789v: Add EDT ET028013DMA panel support drm/panel: sitronix-st7789v: Check display ID

Sebastian Reichel (13):
dt-bindings: vendor-prefixes: add Inanbo
dt-bindings: display: st7789v: add Inanbo T28CP45TN89 drm/panel: sitronix-st7789v: add SPI ID table drm/panel: sitronix-st7789v: remove unused constants drm/panel: sitronix-st7789v: make reset GPIO optional drm/panel: sitronix-st7789v: simplify st7789v_spi_write drm/panel: sitronix-st7789v: improve error handling drm/panel: sitronix-st7789v: avoid hardcoding mode info drm/panel: sitronix-st7789v: avoid hardcoding panel size drm/panel: sitronix-st7789v: add media bus format drm/panel: sitronix-st7789v: avoid hardcoding invert mode drm/panel: sitronix-st7789v: avoid hardcoding polarity info drm/panel: sitronix-st7789v: add Inanbo T28CP45TN89 support

 .../display/panel/sitronix,st7789v.yaml       |  10 +-
 .../devicetree/bindings/vendor-prefixes.yaml  |   2 +
 .../gpu/drm/panel/panel-sitronix-st7789v.c    | 262 +++++++++++++++---
 3 files changed, 237 insertions(+), 37 deletions(-)
Signature · 1 line
2.40.1
#1 lore raw

PATCH v3 1/19 dt-bindings: vendor-prefixes: add Inanbo

Shenzhen INANBO Electronic Technology Co., Ltd. manufacturers TFT/OLED LCD panels.

---
 Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
 1 file changed, 2 insertions(+)
Documentation/devicetree/bindings/vendor-prefixes.yaml +2 -0
index af60bf1a6664..1e2e51401dc5 100644
@@ -617,6 +617,8 @@ patternProperties:
     description: Imagination Technologies Ltd.
   "^imi,.*":
     description: Integrated Micro-Electronics Inc.
+  "^inanbo,.*":
+    description: Shenzhen INANBO Electronic Technology Co., Ltd.
   "^incircuit,.*":
     description: In-Circuit GmbH
   "^indiedroid,.*":
Signature · 1 line
2.40.1
#2 lore raw

PATCH v3 2/19 dt-bindings: display: st7789v: add Inanbo T28CP45TN89

In reply to Sebastian Reichel

Add compatible value for Inanbo t28cp45tn89 and make reset GPIO non mandatory, since it might not be connected to the CPU.

---
 .../devicetree/bindings/display/panel/sitronix,st7789v.yaml  | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
Documentation/devicetree/bindings/display/panel/sitronix,st7789v.yaml +3 -2
index fa6556363cca..75e935f0547b 100644
@@ -15,7 +15,9 @@ allOf:
 
 properties:
   compatible:
-    const: sitronix,st7789v
+    enum:
+      - inanbo,t28cp45tn89-v17
+      - sitronix,st7789v
 
   reg: true
   reset-gpios: true
@@ -33,7 +35,6 @@ properties:
 required:
   - compatible
   - reg
-  - reset-gpios
   - power-supply
 
 unevaluatedProperties: false
Signature · 1 line
2.40.1
#3 lore raw

PATCH v3 3/19 drm/panel: sitronix-st7789v: add SPI ID table

In reply to Sebastian Reichel

SPI device drivers should also have a SPI ID table.

---
 drivers/gpu/drm/panel/panel-sitronix-st7789v.c | 7 +++++++
 1 file changed, 7 insertions(+)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +7 -0
index bbc4569cbcdc..e4d8dea1db36 100644
@@ -394,6 +394,12 @@ static void st7789v_remove(struct spi_device *spi)
 	drm_panel_remove(&ctx->panel);
 }
 
+static const struct spi_device_id st7789v_spi_id[] = {
+	{ "st7789v" },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, st7789v_spi_id);
+
 static const struct of_device_id st7789v_of_match[] = {
 	{ .compatible = "sitronix,st7789v" },
 	{ }
@@ -403,6 +409,7 @@ MODULE_DEVICE_TABLE(of, st7789v_of_match);
 static struct spi_driver st7789v_driver = {
 	.probe = st7789v_probe,
 	.remove = st7789v_remove,
+	.id_table = st7789v_spi_id,
 	.driver = {
 		.name = "st7789v",
 		.of_match_table = st7789v_of_match,
Signature · 1 line
2.40.1
#4 lore raw

PATCH v3 4/19 drm/panel: sitronix-st7789v: remove unused constants

In reply to Sebastian Reichel

ST7789V_COLMOD_RGB_FMT_18BITS and ST7789V_COLMOD_CTRL_FMT_18BITS are unused in favour of MIPI_DCS_PIXEL_FMT_18BIT, remove them.

---
 drivers/gpu/drm/panel/panel-sitronix-st7789v.c | 3 ---
 1 file changed, 3 deletions(-)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +0 -3
index e4d8dea1db36..f7566551b5e2 100644
@@ -15,9 +15,6 @@
 #include <drm/drm_modes.h>
 #include <drm/drm_panel.h>
 
-#define ST7789V_COLMOD_RGB_FMT_18BITS		(6 << 4)
-#define ST7789V_COLMOD_CTRL_FMT_18BITS		(6 << 0)
-
 #define ST7789V_RAMCTRL_CMD		0xb0
 #define ST7789V_RAMCTRL_RM_RGB			BIT(4)
 #define ST7789V_RAMCTRL_DM_RGB			BIT(0)
Signature · 1 line
2.40.1
#5 lore raw

PATCH v3 5/19 drm/panel: sitronix-st7789v: make reset GPIO optional

In reply to Sebastian Reichel

The reset pin might not be software controllable from the SoC, so make it optional.

---
 drivers/gpu/drm/panel/panel-sitronix-st7789v.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +1 -1
index f7566551b5e2..1d43b8cc1647 100644
@@ -369,7 +369,7 @@ static int st7789v_probe(struct spi_device *spi)
 	if (IS_ERR(ctx->power))
 		return PTR_ERR(ctx->power);
 
-	ctx->reset = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW);
+	ctx->reset = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_LOW);
 	if (IS_ERR(ctx->reset)) {
 		dev_err(&spi->dev, "Couldn't get our reset line\n");
 		return PTR_ERR(ctx->reset);
Signature · 1 line
2.40.1
#6 lore raw

PATCH v3 6/19 drm/panel: sitronix-st7789v: simplify st7789v_spi_write

In reply to Sebastian Reichel

st7789v_spi_write initializes a message with just
a single transfer, spi_sync_transfer can be used
for that.

---
 drivers/gpu/drm/panel/panel-sitronix-st7789v.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +1 -5
index 1d43b8cc1647..6290bd49d055 100644
@@ -129,17 +129,13 @@ static int st7789v_spi_write(struct st7789v *ctx, enum st7789v_prefix prefix,
 			     u8 data)
 {
 	struct spi_transfer xfer = { };
-	struct spi_message msg;
 	u16 txbuf = ((prefix & 1) << 8) | data;
 
-	spi_message_init(&msg);
-
 	xfer.tx_buf = &txbuf;
 	xfer.bits_per_word = 9;
 	xfer.len = sizeof(txbuf);
 
-	spi_message_add_tail(&xfer, &msg);
-	return spi_sync(ctx->spi, &msg);
+	return spi_sync_transfer(ctx->spi, &xfer, 1);
 }
 
 static int st7789v_write_command(struct st7789v *ctx, u8 cmd)
Signature · 1 line
2.40.1
#7 lore raw

PATCH v3 7/19 drm/panel: sitronix-st7789v: improve error handling

In reply to Sebastian Reichel

Improve error handling in the probe routine, so that probe defer errors are captured in /sys/kernel/debug/devices_deferred

---
 .../gpu/drm/panel/panel-sitronix-st7789v.c    | 23 ++++++++++---------
 1 file changed, 12 insertions(+), 11 deletions(-)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +12 -11
index 6290bd49d055..a6d6155ef45c 100644
@@ -348,32 +348,33 @@ static const struct drm_panel_funcs st7789v_drm_funcs = {
 
 static int st7789v_probe(struct spi_device *spi)
 {
+	struct device *dev = &spi->dev;
 	struct st7789v *ctx;
 	int ret;
 
-	ctx = devm_kzalloc(&spi->dev, sizeof(*ctx), GFP_KERNEL);
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
 	if (!ctx)
 		return -ENOMEM;
 
 	spi_set_drvdata(spi, ctx);
 	ctx->spi = spi;
 
-	drm_panel_init(&ctx->panel, &spi->dev, &st7789v_drm_funcs,
+	drm_panel_init(&ctx->panel, dev, &st7789v_drm_funcs,
 		       DRM_MODE_CONNECTOR_DPI);
 
-	ctx->power = devm_regulator_get(&spi->dev, "power");
-	if (IS_ERR(ctx->power))
-		return PTR_ERR(ctx->power);
+	ctx->power = devm_regulator_get(dev, "power");
+	ret = PTR_ERR_OR_ZERO(ctx->power);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get regulator\n");
 
-	ctx->reset = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_LOW);
-	if (IS_ERR(ctx->reset)) {
-		dev_err(&spi->dev, "Couldn't get our reset line\n");
-		return PTR_ERR(ctx->reset);
-	}
+	ctx->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+	ret = PTR_ERR_OR_ZERO(ctx->reset);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get reset line\n");
 
 	ret = drm_panel_of_backlight(&ctx->panel);
 	if (ret)
-		return ret;
+		return dev_err_probe(dev, ret, "Failed to get backlight\n");
 
 	drm_panel_add(&ctx->panel);
 
Signature · 1 line
2.40.1
#8 lore raw

PATCH v3 8/19 drm/panel: sitronix-st7789v: avoid hardcoding mode info

In reply to Sebastian Reichel

Avoid hard-coding the default_mode and supply it from match data. One additional layer of abstraction has been introduced, which will be needed for specifying other panel information (e.g. bus flags) in the next steps.

---
 .../gpu/drm/panel/panel-sitronix-st7789v.c    | 24 ++++++++++++++-----
 1 file changed, 18 insertions(+), 6 deletions(-)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +18 -6
index a6d6155ef45c..24c5bec6ce31 100644
@@ -108,8 +108,13 @@
 			return val;		\
 	} while (0)
 
+struct st7789_panel_info {
+	const struct drm_display_mode *mode;
+};
+
 struct st7789v {
 	struct drm_panel panel;
+	const struct st7789_panel_info *info;
 	struct spi_device *spi;
 	struct gpio_desc *reset;
 	struct regulator *power;
@@ -160,16 +165,21 @@ static const struct drm_display_mode default_mode = {
 	.vtotal = 320 + 8 + 4 + 4,
 };
 
+static const struct st7789_panel_info default_panel = {
+	.mode = &default_mode,
+};
+
 static int st7789v_get_modes(struct drm_panel *panel,
 			     struct drm_connector *connector)
 {
+	struct st7789v *ctx = panel_to_st7789v(panel);
 	struct drm_display_mode *mode;
 
-	mode = drm_mode_duplicate(connector->dev, &default_mode);
+	mode = drm_mode_duplicate(connector->dev, ctx->info->mode);
 	if (!mode) {
-		dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
-			default_mode.hdisplay, default_mode.vdisplay,
-			drm_mode_vrefresh(&default_mode));
+		dev_err(panel->dev, "failed to add mode %ux%u@%u\n",
+			ctx->info->mode->hdisplay, ctx->info->mode->vdisplay,
+			drm_mode_vrefresh(ctx->info->mode));
 		return -ENOMEM;
 	}
 
@@ -359,6 +369,8 @@ static int st7789v_probe(struct spi_device *spi)
 	spi_set_drvdata(spi, ctx);
 	ctx->spi = spi;
 
+	ctx->info = device_get_match_data(&spi->dev);
+
 	drm_panel_init(&ctx->panel, dev, &st7789v_drm_funcs,
 		       DRM_MODE_CONNECTOR_DPI);
 
@@ -389,13 +401,13 @@ static void st7789v_remove(struct spi_device *spi)
 }
 
 static const struct spi_device_id st7789v_spi_id[] = {
-	{ "st7789v" },
+	{ "st7789v", (unsigned long) &default_panel },
 	{ }
 };
 MODULE_DEVICE_TABLE(spi, st7789v_spi_id);
 
 static const struct of_device_id st7789v_of_match[] = {
-	{ .compatible = "sitronix,st7789v" },
+	{ .compatible = "sitronix,st7789v", .data = &default_panel },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, st7789v_of_match);
Signature · 1 line
2.40.1
#9 lore raw

PATCH v3 9/19 drm/panel: sitronix-st7789v: avoid hardcoding panel size

In reply to Sebastian Reichel

Move the panel size information to the mode struct, so that different panel sizes can be specified depending on the panel type.

---
 drivers/gpu/drm/panel/panel-sitronix-st7789v.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +4 -2
index 24c5bec6ce31..aa11a1dc0752 100644
@@ -163,6 +163,8 @@ static const struct drm_display_mode default_mode = {
 	.vsync_start = 320 + 8,
 	.vsync_end = 320 + 8 + 4,
 	.vtotal = 320 + 8 + 4 + 4,
+	.width_mm = 61,
+	.height_mm = 103,
 };
 
 static const struct st7789_panel_info default_panel = {
@@ -188,8 +190,8 @@ static int st7789v_get_modes(struct drm_panel *panel,
 	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 	drm_mode_probed_add(connector, mode);
 
-	connector->display_info.width_mm = 61;
-	connector->display_info.height_mm = 103;
+	connector->display_info.width_mm = ctx->info->mode->width_mm;
+	connector->display_info.height_mm = ctx->info->mode->height_mm;
 
 	return 1;
 }
Signature · 1 line
2.40.1
#10 lore raw

PATCH v3 10/19 drm/panel: sitronix-st7789v: add media bus format

In reply to Sebastian Reichel

Add support for describing the media bus format in the panel configuration and expose that to userspace. Since both supported formats (RGB565 and RGB666) are using 6 bits per color also hardcode that information.

---
 .../gpu/drm/panel/panel-sitronix-st7789v.c    | 26 ++++++++++++++++---
 1 file changed, 23 insertions(+), 3 deletions(-)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +23 -3
index aa11a1dc0752..d05d7a9caefc 100644
@@ -10,6 +10,7 @@
 #include <linux/spi/spi.h>
 
 #include <video/mipi_display.h>
+#include <linux/media-bus-format.h>
 
 #include <drm/drm_device.h>
 #include <drm/drm_modes.h>
@@ -110,6 +111,7 @@
 
 struct st7789_panel_info {
 	const struct drm_display_mode *mode;
+	u32 bus_format;
 };
 
 struct st7789v {
@@ -169,6 +171,7 @@ static const struct drm_display_mode default_mode = {
 
 static const struct st7789_panel_info default_panel = {
 	.mode = &default_mode,
+	.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
 };
 
 static int st7789v_get_modes(struct drm_panel *panel,
@@ -190,8 +193,11 @@ static int st7789v_get_modes(struct drm_panel *panel,
 	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 	drm_mode_probed_add(connector, mode);
 
+	connector->display_info.bpc = 6;
 	connector->display_info.width_mm = ctx->info->mode->width_mm;
 	connector->display_info.height_mm = ctx->info->mode->height_mm;
+	drm_display_info_set_bus_formats(&connector->display_info,
+					 &ctx->info->bus_format, 1);
 
 	return 1;
 }
@@ -199,8 +205,24 @@ static int st7789v_get_modes(struct drm_panel *panel,
 static int st7789v_prepare(struct drm_panel *panel)
 {
 	struct st7789v *ctx = panel_to_st7789v(panel);
+	u8 pixel_fmt;
 	int ret;
 
+	switch (ctx->info->bus_format) {
+	case MEDIA_BUS_FMT_RGB666_1X18:
+		pixel_fmt = MIPI_DCS_PIXEL_FMT_18BIT;
+		break;
+	case MEDIA_BUS_FMT_RGB565_1X16:
+		pixel_fmt = MIPI_DCS_PIXEL_FMT_16BIT;
+		break;
+	default:
+		dev_err(panel->dev, "unsupported bus format: %d\n",
+			ctx->info->bus_format);
+		return -EINVAL;
+	}
+
+	pixel_fmt = (pixel_fmt << 4) | pixel_fmt;
+
 	ret = regulator_enable(ctx->power);
 	if (ret)
 		return ret;
@@ -221,9 +243,7 @@ static int st7789v_prepare(struct drm_panel *panel)
 
 	ST7789V_TEST(ret, st7789v_write_command(ctx,
 						MIPI_DCS_SET_PIXEL_FORMAT));
-	ST7789V_TEST(ret, st7789v_write_data(ctx,
-					     (MIPI_DCS_PIXEL_FMT_18BIT << 4) |
-					     (MIPI_DCS_PIXEL_FMT_18BIT)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, pixel_fmt));
 
 	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_PORCTRL_CMD));
 	ST7789V_TEST(ret, st7789v_write_data(ctx, 0xc));
Signature · 1 line
2.40.1
#11 lore raw

PATCH v3 11/19 drm/panel: sitronix-st7789v: avoid hardcoding invert mode

In reply to Sebastian Reichel

While the default panel uses invert mode, some panels require non-invert mode instead.

---
 drivers/gpu/drm/panel/panel-sitronix-st7789v.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +9 -1
index d05d7a9caefc..ee84d7a9019e 100644
@@ -112,6 +112,7 @@
 struct st7789_panel_info {
 	const struct drm_display_mode *mode;
 	u32 bus_format;
+	bool invert_mode;
 };
 
 struct st7789v {
@@ -171,6 +172,7 @@ static const struct drm_display_mode default_mode = {
 
 static const struct st7789_panel_info default_panel = {
 	.mode = &default_mode,
+	.invert_mode = true,
 	.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
 };
 
@@ -321,7 +323,13 @@ static int st7789v_prepare(struct drm_panel *panel)
 	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN61(0x1b)));
 	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN62(0x28)));
 
-	ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_ENTER_INVERT_MODE));
+	if (ctx->info->invert_mode) {
+		ST7789V_TEST(ret, st7789v_write_command(ctx,
+						MIPI_DCS_ENTER_INVERT_MODE));
+	} else {
+		ST7789V_TEST(ret, st7789v_write_command(ctx,
+						MIPI_DCS_EXIT_INVERT_MODE));
+	}
 
 	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_RAMCTRL_CMD));
 	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RAMCTRL_DM_RGB |
Signature · 1 line
2.40.1
#12 lore raw

PATCH v3 12/19 drm/panel: sitronix-st7789v: avoid hardcoding polarity info

In reply to Sebastian Reichel

Add polarity information via mode and bus flags, so that they are no longer hardcoded and forward the information to the DRM stack. This is required for adding panels with different settings.

---
 .../gpu/drm/panel/panel-sitronix-st7789v.c    | 22 +++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +18 -4
index ee84d7a9019e..94c805c79d05 100644
@@ -28,6 +28,7 @@
 #define ST7789V_RGBCTRL_VSYNC_HIGH		BIT(3)
 #define ST7789V_RGBCTRL_HSYNC_HIGH		BIT(2)
 #define ST7789V_RGBCTRL_PCLK_HIGH		BIT(1)
+#define ST7789V_RGBCTRL_DE_LOW			BIT(0)
 #define ST7789V_RGBCTRL_VBP(n)			((n) & 0x7f)
 #define ST7789V_RGBCTRL_HBP(n)			((n) & 0x1f)
 
@@ -112,6 +113,7 @@
 struct st7789_panel_info {
 	const struct drm_display_mode *mode;
 	u32 bus_format;
+	u32 bus_flags;
 	bool invert_mode;
 };
 
@@ -168,12 +170,15 @@ static const struct drm_display_mode default_mode = {
 	.vtotal = 320 + 8 + 4 + 4,
 	.width_mm = 61,
 	.height_mm = 103,
+	.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
 };
 
 static const struct st7789_panel_info default_panel = {
 	.mode = &default_mode,
 	.invert_mode = true,
 	.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
+	.bus_flags = DRM_BUS_FLAG_DE_HIGH |
+		     DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE,
 };
 
 static int st7789v_get_modes(struct drm_panel *panel,
@@ -198,6 +203,7 @@ static int st7789v_get_modes(struct drm_panel *panel,
 	connector->display_info.bpc = 6;
 	connector->display_info.width_mm = ctx->info->mode->width_mm;
 	connector->display_info.height_mm = ctx->info->mode->height_mm;
+	connector->display_info.bus_flags = ctx->info->bus_flags;
 	drm_display_info_set_bus_formats(&connector->display_info,
 					 &ctx->info->bus_format, 1);
 
@@ -207,7 +213,7 @@ static int st7789v_get_modes(struct drm_panel *panel,
 static int st7789v_prepare(struct drm_panel *panel)
 {
 	struct st7789v *ctx = panel_to_st7789v(panel);
-	u8 pixel_fmt;
+	u8 pixel_fmt, polarity;
 	int ret;
 
 	switch (ctx->info->bus_format) {
@@ -225,6 +231,16 @@ static int st7789v_prepare(struct drm_panel *panel)
 
 	pixel_fmt = (pixel_fmt << 4) | pixel_fmt;
 
+	polarity = 0;
+	if (ctx->info->mode->flags & DRM_MODE_FLAG_PVSYNC)
+		polarity |= ST7789V_RGBCTRL_VSYNC_HIGH;
+	if (ctx->info->mode->flags & DRM_MODE_FLAG_PHSYNC)
+		polarity |= ST7789V_RGBCTRL_HSYNC_HIGH;
+	if (ctx->info->bus_flags & DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE)
+		polarity |= ST7789V_RGBCTRL_PCLK_HIGH;
+	if (ctx->info->bus_flags & DRM_BUS_FLAG_DE_LOW)
+		polarity |= ST7789V_RGBCTRL_DE_LOW;
+
 	ret = regulator_enable(ctx->power);
 	if (ret)
 		return ret;
@@ -340,9 +356,7 @@ static int st7789v_prepare(struct drm_panel *panel)
 	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_RGBCTRL_CMD));
 	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_WO |
 					     ST7789V_RGBCTRL_RCM(2) |
-					     ST7789V_RGBCTRL_VSYNC_HIGH |
-					     ST7789V_RGBCTRL_HSYNC_HIGH |
-					     ST7789V_RGBCTRL_PCLK_HIGH));
+					     polarity));
 	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_VBP(8)));
 	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_HBP(20)));
 
Signature · 1 line
2.40.1
#13 lore raw

PATCH v3 13/19 drm/panel: sitronix-st7789v: add Inanbo T28CP45TN89 support

In reply to Sebastian Reichel

UNI-T UTi260b has a Inanbo T28CP45TN89 v17 panel. I could not find proper documentation for the panel apart from a technical drawing, but according to the vendor U-Boot it is based on a Sitronix st7789v chip. I generated the init sequence by modifying the default one until proper graphics output has been seen on the device.

---
 .../gpu/drm/panel/panel-sitronix-st7789v.c    | 25 +++++++++++++++++++
 1 file changed, 25 insertions(+)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +25 -0
index 94c805c79d05..55e475471f43 100644
@@ -173,6 +173,21 @@ static const struct drm_display_mode default_mode = {
 	.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
 };
 
+static const struct drm_display_mode t28cp45tn89_mode = {
+	.clock = 6008,
+	.hdisplay = 240,
+	.hsync_start = 240 + 38,
+	.hsync_end = 240 + 38 + 10,
+	.htotal = 240 + 38 + 10 + 10,
+	.vdisplay = 320,
+	.vsync_start = 320 + 8,
+	.vsync_end = 320 + 8 + 4,
+	.vtotal = 320 + 8 + 4 + 4,
+	.width_mm = 43,
+	.height_mm = 57,
+	.flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC,
+};
+
 static const struct st7789_panel_info default_panel = {
 	.mode = &default_mode,
 	.invert_mode = true,
@@ -181,6 +196,14 @@ static const struct st7789_panel_info default_panel = {
 		     DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE,
 };
 
+static const struct st7789_panel_info t28cp45tn89_panel = {
+	.mode = &t28cp45tn89_mode,
+	.invert_mode = false,
+	.bus_format = MEDIA_BUS_FMT_RGB565_1X16,
+	.bus_flags = DRM_BUS_FLAG_DE_HIGH |
+		     DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
+};
+
 static int st7789v_get_modes(struct drm_panel *panel,
 			     struct drm_connector *connector)
 {
@@ -446,12 +469,14 @@ static void st7789v_remove(struct spi_device *spi)
 
 static const struct spi_device_id st7789v_spi_id[] = {
 	{ "st7789v", (unsigned long) &default_panel },
+	{ "t28cp45tn89-v17", (unsigned long) &t28cp45tn89_panel },
 	{ }
 };
 MODULE_DEVICE_TABLE(spi, st7789v_spi_id);
 
 static const struct of_device_id st7789v_of_match[] = {
 	{ .compatible = "sitronix,st7789v", .data = &default_panel },
+	{ .compatible = "inanbo,t28cp45tn89-v17", .data = &t28cp45tn89_panel },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, st7789v_of_match);
Signature · 1 line
2.40.1
#14 lore raw

PATCH v3 14/19 dt-bindings: display: st7789v: Add the edt,et028013dma panel compatible

In reply to Sebastian Reichel

From: Miquel Raynal <[email protected]>

The ST7789V LCD controller is also embedded in the ET028013DMA panel. Add a compatible string to describe this other panel.

---
 .../devicetree/bindings/display/panel/sitronix,st7789v.yaml      | 1 +
 1 file changed, 1 insertion(+)
Documentation/devicetree/bindings/display/panel/sitronix,st7789v.yaml +1 -0
index 75e935f0547b..9f4157b02b84 100644
@@ -16,6 +16,7 @@ allOf:
 properties:
   compatible:
     enum:
+      - edt,et028013dma
       - inanbo,t28cp45tn89-v17
       - sitronix,st7789v
 
Signature · 1 line
2.40.1
#15 lore raw

PATCH v3 15/19 dt-bindings: display: st7789v: bound the number of Rx data lines

In reply to Sebastian Reichel

From: Miquel Raynal <[email protected]>

The ST7789V LCD controller supports regular SPI wiring, as well as no Rx data line at all. The operating system needs to know whether it can read registers from the device or not. Let's detail this specific design possibility by bounding the spi-rx-bus-width property.

---
 .../devicetree/bindings/display/panel/sitronix,st7789v.yaml   | 4 ++++
 1 file changed, 4 insertions(+)
Documentation/devicetree/bindings/display/panel/sitronix,st7789v.yaml +4 -0
index 9f4157b02b84..905c064cd106 100644
@@ -29,6 +29,10 @@ properties:
   spi-cpha: true
   spi-cpol: true
 
+  spi-rx-bus-width:
+    minimum: 0
+    maximum: 1
+
   dc-gpios:
     maxItems: 1
     description: DCX pin, Display data/command selection pin in parallel interface
Signature · 1 line
2.40.1
#16 lore raw

PATCH v3 16/19 drm/panel: sitronix-st7789v: Use 9 bits per spi word by default

In reply to Sebastian Reichel

From: Miquel Raynal <[email protected]>

The Sitronix controller expects 9-bit words, provide this as default at probe time rather than specifying this in each and every access.

---
 drivers/gpu/drm/panel/panel-sitronix-st7789v.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +5 -1
index 55e475471f43..33bdf9ee4085 100644
@@ -142,7 +142,6 @@ static int st7789v_spi_write(struct st7789v *ctx, enum st7789v_prefix prefix,
 	u16 txbuf = ((prefix & 1) << 8) | data;
 
 	xfer.tx_buf = &txbuf;
-	xfer.bits_per_word = 9;
 	xfer.len = sizeof(txbuf);
 
 	return spi_sync_transfer(ctx->spi, &xfer, 1);
@@ -436,6 +435,11 @@ static int st7789v_probe(struct spi_device *spi)
 	spi_set_drvdata(spi, ctx);
 	ctx->spi = spi;
 
+	spi->bits_per_word = 9;
+	ret = spi_setup(spi);
+	if (ret < 0)
+		return dev_err_probe(&spi->dev, ret, "Failed to setup spi\n");
+
 	ctx->info = device_get_match_data(&spi->dev);
 
 	drm_panel_init(&ctx->panel, dev, &st7789v_drm_funcs,
Signature · 1 line
2.40.1
#17 lore raw

PATCH v3 17/19 drm/panel: sitronix-st7789v: Clarify a definition

In reply to Sebastian Reichel

From: Miquel Raynal <[email protected]>

The Sitronix datasheet explains BIT(1) of the RGBCTRL register as the DOTCLK/PCLK edge used to sample the data lines:

“0” The data is input on the positive edge of DOTCLK “1” The data is input on the negative edge of DOTCLK

IOW, this bit implies a falling edge and not a high state. Correct the definition to ease the comparison with the datasheet.

---
 drivers/gpu/drm/panel/panel-sitronix-st7789v.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +2 -2
index 33bdf9ee4085..3d24690e891b 100644
@@ -27,7 +27,7 @@
 #define ST7789V_RGBCTRL_RCM(n)			(((n) & 3) << 5)
 #define ST7789V_RGBCTRL_VSYNC_HIGH		BIT(3)
 #define ST7789V_RGBCTRL_HSYNC_HIGH		BIT(2)
-#define ST7789V_RGBCTRL_PCLK_HIGH		BIT(1)
+#define ST7789V_RGBCTRL_PCLK_FALLING		BIT(1)
 #define ST7789V_RGBCTRL_DE_LOW			BIT(0)
 #define ST7789V_RGBCTRL_VBP(n)			((n) & 0x7f)
 #define ST7789V_RGBCTRL_HBP(n)			((n) & 0x1f)
@@ -259,7 +259,7 @@ static int st7789v_prepare(struct drm_panel *panel)
 	if (ctx->info->mode->flags & DRM_MODE_FLAG_PHSYNC)
 		polarity |= ST7789V_RGBCTRL_HSYNC_HIGH;
 	if (ctx->info->bus_flags & DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE)
-		polarity |= ST7789V_RGBCTRL_PCLK_HIGH;
+		polarity |= ST7789V_RGBCTRL_PCLK_FALLING;
 	if (ctx->info->bus_flags & DRM_BUS_FLAG_DE_LOW)
 		polarity |= ST7789V_RGBCTRL_DE_LOW;
 
Signature · 1 line
2.40.1
#18 lore raw

PATCH v3 18/19 drm/panel: sitronix-st7789v: Add EDT ET028013DMA panel support

In reply to Sebastian Reichel

From: Miquel Raynal <[email protected]>

This panel from Emerging Display Technologies Corporation features an ST7789V2 LCD controller panel inside which is almost identical to what the Sitronix panel driver supports.

In practice, the module physical size is specific, and experiments show that the display will malfunction if any of the following situation occurs:
* Pixel clock is above 3MHz
* Pixel clock is not inverted
I could not properly identify the reasons behind these failures, scope captures show valid input signals.

---
 .../gpu/drm/panel/panel-sitronix-st7789v.c    | 25 +++++++++++++++++++
 1 file changed, 25 insertions(+)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +25 -0
index 3d24690e891b..47387f127e05 100644
@@ -187,6 +187,21 @@ static const struct drm_display_mode t28cp45tn89_mode = {
 	.flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC,
 };
 
+static const struct drm_display_mode et028013dma_mode = {
+	.clock = 3000,
+	.hdisplay = 240,
+	.hsync_start = 240 + 38,
+	.hsync_end = 240 + 38 + 10,
+	.htotal = 240 + 38 + 10 + 10,
+	.vdisplay = 320,
+	.vsync_start = 320 + 8,
+	.vsync_end = 320 + 8 + 4,
+	.vtotal = 320 + 8 + 4 + 4,
+	.width_mm = 43,
+	.height_mm = 58,
+	.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
+};
+
 static const struct st7789_panel_info default_panel = {
 	.mode = &default_mode,
 	.invert_mode = true,
@@ -203,6 +218,14 @@ static const struct st7789_panel_info t28cp45tn89_panel = {
 		     DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
 };
 
+static const struct st7789_panel_info et028013dma_panel = {
+	.mode = &et028013dma_mode,
+	.invert_mode = true,
+	.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
+	.bus_flags = DRM_BUS_FLAG_DE_HIGH |
+		     DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
+};
+
 static int st7789v_get_modes(struct drm_panel *panel,
 			     struct drm_connector *connector)
 {
@@ -474,6 +497,7 @@ static void st7789v_remove(struct spi_device *spi)
 static const struct spi_device_id st7789v_spi_id[] = {
 	{ "st7789v", (unsigned long) &default_panel },
 	{ "t28cp45tn89-v17", (unsigned long) &t28cp45tn89_panel },
+	{ "et028013dma", (unsigned long) &et028013dma_panel },
 	{ }
 };
 MODULE_DEVICE_TABLE(spi, st7789v_spi_id);
@@ -481,6 +505,7 @@ MODULE_DEVICE_TABLE(spi, st7789v_spi_id);
 static const struct of_device_id st7789v_of_match[] = {
 	{ .compatible = "sitronix,st7789v", .data = &default_panel },
 	{ .compatible = "inanbo,t28cp45tn89-v17", .data = &t28cp45tn89_panel },
+	{ .compatible = "edt,et028013dma", .data = &et028013dma_panel },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, st7789v_of_match);
Signature · 1 line
2.40.1
#19 lore raw

PATCH v3 19/19 drm/panel: sitronix-st7789v: Check display ID

In reply to Sebastian Reichel

From: Miquel Raynal <[email protected]>

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 piece for speeding-up 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 inform the user early if this is not the case.

As this panel may work with no MISO line, the check is discarded in this case. Upon error, we do not fail probing but just warn the user, in case the DT description would be lacking the Rx bus width (which is likely on old descriptions) in order to avoid breaking existing devices.

---
 .../gpu/drm/panel/panel-sitronix-st7789v.c    | 81 +++++++++++++++++++
 1 file changed, 81 insertions(+)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +81 -0
index 47387f127e05..dc010d87a9ef 100644
@@ -110,6 +110,9 @@
 			return val;		\
 	} while (0)
 
+#define ST7789V_IDS { 0x85, 0x85, 0x52 }
+#define ST7789V_IDS_SIZE 3
+
 struct st7789_panel_info {
 	const struct drm_display_mode *mode;
 	u32 bus_format;
@@ -157,6 +160,76 @@ 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];
+
+	if (ctx->spi->mode & SPI_NO_RX)
+		return 0;
+
+	ret = st7789v_read_data(ctx, MIPI_DCS_GET_DISPLAY_ID, ids, ST7789V_IDS_SIZE);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ST7789V_IDS_SIZE; i++) {
+		if (ids[i] != st7789v_ids[i]) {
+			invalid_ids = true;
+			break;
+		}
+	}
+
+	if (invalid_ids)
+		return -EIO;
+
+	return 0;
+}
+
 static const struct drm_display_mode default_mode = {
 	.clock = 7000,
 	.hdisplay = 240,
@@ -295,6 +368,14 @@ static int st7789v_prepare(struct drm_panel *panel)
 	gpiod_set_value(ctx->reset, 0);
 	msleep(120);
 
+	/*
+	 * Avoid failing if the IDs are invalid in case the Rx bus width
+	 * description is missing.
+	 */
+	ret = st7789v_check_id(panel);
+	if (ret)
+		dev_warn(panel->dev, "Unrecognized panel IDs");
+
 	ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_EXIT_SLEEP_MODE));
 
 	/* We need to wait 120ms after a sleep out command */
Signature · 1 line
2.40.1
#20 lore raw
In reply to Sebastian Reichel

Hi Sebastian,

+ Thomas

[email protected] wrote on Fri, 14 Jul 2023 03:37:37 +0200:

Quoted 8 lines

Hi,

This adds panel support for Inanbo T28CP45TN89, which I found inside of a handheld thermal camera. The panel is based on the st7789v controller. All information is based on reverse engineering. I also appended the series from Miquel Raynal adding EDT ET028013DMA panel support, so that I could easily test it with my SPI_NO_RX setup. They are slightly different due to rebasing.

Thanks a lot! I'll continue following the series and provide my help when required.

Cheers,
Miquèl

Quoted 49 lines

Changes since PATCHv2:
* https://lore.kernel.org/all/[email protected]/
* https://lore.kernel.org/all/[email protected]/
* Add Rob Herring's R-b for the DT binding
* Make panel info "static const"
* Add Michael Riesch's R-b to all my patches
* Rebase to 6.5-rc1
* Append Miquel's series

Changes since PATCHv1:
* https://lore.kernel.org/all/[email protected]/
* Apply DT binding changes requested by Krzysztof Kozlowski and his Ack
* I changed the driver patches to avoid code duplication and splitted the code a bit more

Greetings,

-- Sebastian

Miquel Raynal (6):
dt-bindings: display: st7789v: Add the edt,et028013dma panel
compatible
dt-bindings: display: st7789v: bound the number of Rx data lines drm/panel: sitronix-st7789v: Use 9 bits per spi word by default drm/panel: sitronix-st7789v: Clarify a definition drm/panel: sitronix-st7789v: Add EDT ET028013DMA panel support drm/panel: sitronix-st7789v: Check display ID

Sebastian Reichel (13):
dt-bindings: vendor-prefixes: add Inanbo
dt-bindings: display: st7789v: add Inanbo T28CP45TN89 drm/panel: sitronix-st7789v: add SPI ID table drm/panel: sitronix-st7789v: remove unused constants drm/panel: sitronix-st7789v: make reset GPIO optional drm/panel: sitronix-st7789v: simplify st7789v_spi_write drm/panel: sitronix-st7789v: improve error handling drm/panel: sitronix-st7789v: avoid hardcoding mode info drm/panel: sitronix-st7789v: avoid hardcoding panel size drm/panel: sitronix-st7789v: add media bus format drm/panel: sitronix-st7789v: avoid hardcoding invert mode drm/panel: sitronix-st7789v: avoid hardcoding polarity info drm/panel: sitronix-st7789v: add Inanbo T28CP45TN89 support

 .../display/panel/sitronix,st7789v.yaml       |  10 +-
 .../devicetree/bindings/vendor-prefixes.yaml  |   2 +
 .../gpu/drm/panel/panel-sitronix-st7789v.c    | 262 +++++++++++++++---
 3 files changed, 237 insertions(+), 37 deletions(-)
#21 lore raw
In reply to Sebastian Reichel

Hi,

Each patch in the series has at least one Reviewed-by tag and the DT binding changes have an Acked-by from at least one of the DT binding maintainers. It has been pending on the mailing list for two weeks now, so can it be applied?

P.S.: Looks like I missed Neil in the original recipients list. Sorry about that. If you are not subscribed to dri-devel and need the full thread it's available here:

https://lore.kernel.org/all/[email protected]/

Greetings,

-- Sebastian

maintainer
#22 lore raw

Hi Sebastian,

Quoted 10 lines · Sebastian Reichel

Hi,

Each patch in the series has at least one Reviewed-by tag and the DT binding changes have an Acked-by from at least one of the DT binding maintainers. It has been pending on the mailing list for two weeks now, so can it be applied?

P.S.: Looks like I missed Neil in the original recipients list. Sorry about that. If you are not subscribed to dri-devel and need the full thread it's available here:

Indeed I wasn't in CC and missed the thread.

Quoted 2 lines

https://lore.kernel.org/all/[email protected]/

Applying it now,

Thanks.
Neil

Quoted 4 lines

Greetings,

-- Sebastian

maintainer Applied
#23 lore raw
In reply to Sebastian Reichel

Hi,

Quoted 8 lines · Sebastian Reichel

This adds panel support for Inanbo T28CP45TN89, which I found inside of a handheld thermal camera. The panel is based on the st7789v controller. All information is based on reverse engineering. I also appended the series from Miquel Raynal adding EDT ET028013DMA panel support, so that I could easily test it with my SPI_NO_RX setup. They are slightly different due to rebasing.

[...]

Thanks, Applied to https://anongit.freedesktop.org/git/drm/drm-misc.git (drm-misc-next)

[01/19] dt-bindings: vendor-prefixes: add Inanbo

        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=b93e0e203e27492a2277169e05ac59afb9bf7fcd
[02/19] dt-bindings: display: st7789v: add Inanbo T28CP45TN89
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=ff984a81cf601a68ba99a9c3264145f4d931783d
[03/19] drm/panel: sitronix-st7789v: add SPI ID table
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=11649154ec46f1c7f7c58bac22e2c5927ca6b6a2
[04/19] drm/panel: sitronix-st7789v: remove unused constants
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=c2974f43b1237e0c985760156bc3ca4dccbb5243
[05/19] drm/panel: sitronix-st7789v: make reset GPIO optional
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=b6b65e45e09a2e940e48722fa0bfdf16e6f4edf8
[06/19] drm/panel: sitronix-st7789v: simplify st7789v_spi_write
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=fbad26dcb657830e59ba2ca5eaba6be0019b97f9
[07/19] drm/panel: sitronix-st7789v: improve error handling
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=bc2aa99b2306bc9d91586bc9187bfef4e61d3882
[08/19] drm/panel: sitronix-st7789v: avoid hardcoding mode info
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=9b4454fa2528c617b5986517c9c73e50e30d237d
[09/19] drm/panel: sitronix-st7789v: avoid hardcoding panel size
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=4098d1867f27de2443c33e116b064ad3082aecb9
[10/19] drm/panel: sitronix-st7789v: add media bus format
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=a4b563b1d19dea9de366f81cae6342d80b663a45
[11/19] drm/panel: sitronix-st7789v: avoid hardcoding invert mode
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=7a6288726cf6bc0fa1bca0f24922a06425b84bf1
[12/19] drm/panel: sitronix-st7789v: avoid hardcoding polarity info
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=e4572f99f8a7dfd8a081c9135943ab82abe6f692
[13/19] drm/panel: sitronix-st7789v: add Inanbo T28CP45TN89 support
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=a411558cc14309073616e72d259083602585b296
[14/19] dt-bindings: display: st7789v: Add the edt,et028013dma panel compatible
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=893cfba7c56aa3fed34935b6fbc14a008c3b8172
[15/19] dt-bindings: display: st7789v: bound the number of Rx data lines
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=9943981aa3ab7841186827fce2177279c766b6df
[16/19] drm/panel: sitronix-st7789v: Use 9 bits per spi word by default
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=6b00e72e4bee08048379a6365251b195b8a946d1
[17/19] drm/panel: sitronix-st7789v: Clarify a definition
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=a368b40836e7fc4f24dbb0fcfb9dedcde1dcaa38
[18/19] drm/panel: sitronix-st7789v: Add EDT ET028013DMA panel support
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=71f739082160b5e4def3a7083dc25583cc195d04
[19/19] drm/panel: sitronix-st7789v: Check display ID
        https://cgit.freedesktop.org/drm/drm-misc/commit/?id=290cdd7959a734a0ef20ec096af7810177c4b9f8
Signature · 1 line
Neil