Skip to contentSkip to conversation

Add Inanbo T28CP45TN89 panel support

PATCH Superseded Sebastian Reichel 3 participants 12 messages Mar 17, 2023 – Mar 31, 2023

Subsystems (4)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

cover Add Inanbo T28CP45TN89 panel support

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.

The series conflicts with [0] ("Add timing override to sitronix,st7789v"). I can rebase, but did not want to delay getting some review feedback :)

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

-- Sebastian

Sebastian Reichel (7):
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: prepare for additional panels drm/panel: sitronix-st7789v: add Inanbo T28CP45TN89 support

 .../display/panel/sitronix,st7789v.yaml       |   5 +-
 .../devicetree/bindings/vendor-prefixes.yaml  |   2 +
 .../gpu/drm/panel/panel-sitronix-st7789v.c    | 251 +++++++++++++++---
 3 files changed, 213 insertions(+), 45 deletions(-)
Signature · 1 line
2.39.2
#1 lore raw

PATCH 1/7 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 ed64e06ecca4..33e1d65cf4b2 100644
@@ -609,6 +609,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
   "^inet-tek,.*":
Signature · 1 line
2.39.2
#3 lore raw

PATCH 2/7 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 d984b59daa4a..4fc86f96b00f 100644
@@ -15,7 +15,9 @@ allOf:
 
 properties:
   compatible:
-    const: sitronix,st7789v
+    enum:
+      - sitronix,st7789v
+      - inanbo,t28cp45tn89-v17
 
   reg: true
   reset-gpios: true
@@ -29,7 +31,6 @@ properties:
 required:
   - compatible
   - reg
-  - reset-gpios
   - power-supply
 
 unevaluatedProperties: false
Signature · 1 line
2.39.2
#4 lore raw

Re: [PATCHv1 2/7] dt-bindings: display: st7789v: add Inanbo T28CP45TN89

Quoted 3 lines · Sebastian Reichel

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

Please wrap commit message according to Linux coding style / submission process (neither too early nor over the limit):
https://elixir.bootlin.com/linux/v5.18-rc4/source/Documentation/process/submitting-patches.rst#L586

Quoted 11 lines
---
 .../devicetree/bindings/display/panel/sitronix,st7789v.yaml  | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
Documentation/devicetree/bindings/display/panel/sitronix,st7789v.yaml +0 -0
index d984b59daa4a..4fc86f96b00f 100644

Documentation/devicetree/bindings/display/panel/sitronix,st7789v.yaml allOf: · line 20

Quoted 7 lines
Documentation/devicetree/bindings/display/panel/sitronix,st7789v.yaml +3 -1
@@ -15,7 +15,9 @@ allOf:
 
 properties:
   compatible:
-    const: sitronix,st7789v
+    enum:
+      - sitronix,st7789v
+      - inanbo,t28cp45tn89-v17

Keep them ordered by name.

Best regards,
Krzysztof

#5 lore raw

PATCH 3/7 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.39.2
#6 lore raw

Re: [PATCHv1 3/7] drm/panel: sitronix-st7789v: add SPI ID table

Hi Sebastian,

Quoted 12 lines · 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 +0 -0
index bbc4569cbcdc..e4d8dea1db36 100644

drivers/gpu/drm/panel/panel-sitronix-st7789v.c static void st7789v_remove(struct spi_device *spi) · line 398

Quoted 5 lines
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +2 -0
@@ -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" },

Minor suggestion: The format

static const struct spi_device_id st7789v_spi_id[] = {
	{
		.name = "st7789v",
	},
	{ }
};

is more verbose, but can be extended easily.

drivers/gpu/drm/panel/panel-sitronix-st7789v.c static void st7789v_remove(struct spi_device *spi) · line 405

Quoted 7 lines
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +4 -0
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, st7789v_spi_id);
+
 static const struct of_device_id st7789v_of_match[] = {
 	{ .compatible = "sitronix,st7789v" },
 	{ }

The same holds for this structure here (you may want to consider this when adding the .data field in patch 6/7.

Best regards,
Michael

Quoted 8 lines
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +1 -0
@@ -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,
#7 lore raw

PATCH 4/7 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.39.2
#8 lore raw

PATCH 5/7 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.39.2
#9 lore raw

PATCH 6/7 drm/panel: sitronix-st7789v: prepare for additional panels

In reply to Sebastian Reichel

Prepare the driver for additional panels with different initialization.

---
 .../gpu/drm/panel/panel-sitronix-st7789v.c    | 110 +++++++++++-------
 1 file changed, 67 insertions(+), 43 deletions(-)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +67 -43
index 1d43b8cc1647..a62a2f5737e4 100644
@@ -108,8 +108,16 @@
 			return val;		\
 	} while (0)
 
+struct st7789v;
+
+struct st7789_panel_info {
+	const struct drm_display_mode *mode;
+	int (*init_sequence)(struct st7789v *ctx);
+};
+
 struct st7789v {
 	struct drm_panel panel;
+	const struct st7789_panel_info *info;
 	struct spi_device *spi;
 	struct gpio_desc *reset;
 	struct regulator *power;
@@ -162,51 +170,13 @@ 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 int st7789v_get_modes(struct drm_panel *panel,
-			     struct drm_connector *connector)
-{
-	struct drm_display_mode *mode;
-
-	mode = drm_mode_duplicate(connector->dev, &default_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));
-		return -ENOMEM;
-	}
-
-	drm_mode_set_name(mode);
-
-	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;
-
-	return 1;
-}
-
-static int st7789v_prepare(struct drm_panel *panel)
-{
-	struct st7789v *ctx = panel_to_st7789v(panel);
+static int init_sequence_default(struct st7789v *ctx) {
 	int ret;
 
-	ret = regulator_enable(ctx->power);
-	if (ret)
-		return ret;
-
-	gpiod_set_value(ctx->reset, 1);
-	msleep(30);
-	gpiod_set_value(ctx->reset, 0);
-	msleep(120);
-
-	ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_EXIT_SLEEP_MODE));
-
-	/* We need to wait 120ms after a sleep out command */
-	msleep(120);
-
 	ST7789V_TEST(ret, st7789v_write_command(ctx,
 						MIPI_DCS_SET_ADDRESS_MODE));
 	ST7789V_TEST(ret, st7789v_write_data(ctx, 0));
@@ -313,6 +283,58 @@ static int st7789v_prepare(struct drm_panel *panel)
 	return 0;
 }
 
+struct st7789_panel_info default_panel = {
+	.mode = &default_mode,
+	.init_sequence = init_sequence_default,
+};
+
+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, ctx->info->mode);
+	if (!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;
+	}
+
+	drm_mode_set_name(mode);
+
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+	drm_mode_probed_add(connector, mode);
+
+	connector->display_info.width_mm = ctx->info->mode->width_mm;
+	connector->display_info.height_mm = ctx->info->mode->height_mm;
+
+	return 1;
+}
+
+static int st7789v_prepare(struct drm_panel *panel)
+{
+	struct st7789v *ctx = panel_to_st7789v(panel);
+	int ret;
+
+	ret = regulator_enable(ctx->power);
+	if (ret)
+		return ret;
+
+	gpiod_set_value(ctx->reset, 1);
+	msleep(30);
+	gpiod_set_value(ctx->reset, 0);
+	msleep(120);
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_EXIT_SLEEP_MODE));
+
+	/* We need to wait 120ms after a sleep out command */
+	msleep(120);
+
+	return ctx->info->init_sequence(ctx);
+}
+
 static int st7789v_enable(struct drm_panel *panel)
 {
 	struct st7789v *ctx = panel_to_st7789v(panel);
@@ -362,6 +384,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, &spi->dev, &st7789v_drm_funcs,
 		       DRM_MODE_CONNECTOR_DPI);
 
@@ -392,13 +416,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.39.2
#10 lore raw

PATCH 7/7 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    | 137 ++++++++++++++++++
 1 file changed, 137 insertions(+)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +137 -0
index a62a2f5737e4..90f70eb84f11 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>
@@ -113,6 +114,8 @@ struct st7789v;
 struct st7789_panel_info {
 	const struct drm_display_mode *mode;
 	int (*init_sequence)(struct st7789v *ctx);
+	unsigned int bpc;
+	u32 bus_format;
 };
 
 struct st7789v {
@@ -174,6 +177,20 @@ static const struct drm_display_mode default_mode = {
 	.height_mm = 103,
 };
 
+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,
+};
+
 static int init_sequence_default(struct st7789v *ctx) {
 	int ret;
 
@@ -283,11 +300,125 @@ static int init_sequence_default(struct st7789v *ctx) {
 	return 0;
 }
 
+static int init_sequence_t28cp45tn89(struct st7789v *ctx) {
+	int ret;
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx,
+						MIPI_DCS_SET_ADDRESS_MODE));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, 0));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx,
+						MIPI_DCS_SET_PIXEL_FORMAT));
+	ST7789V_TEST(ret, st7789v_write_data(ctx,
+					     (MIPI_DCS_PIXEL_FMT_16BIT << 4) |
+					     (MIPI_DCS_PIXEL_FMT_16BIT)));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_PORCTRL_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, 0xc));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, 0xc));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, 0));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PORCTRL_IDLE_BP(3) |
+					     ST7789V_PORCTRL_IDLE_FP(3)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx,
+					     ST7789V_PORCTRL_PARTIAL_BP(3) |
+					     ST7789V_PORCTRL_PARTIAL_FP(3)));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_GCTRL_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_GCTRL_VGLS(5) |
+					     ST7789V_GCTRL_VGHS(3)));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_VCOMS_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, 0x2b));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_LCMCTRL_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_LCMCTRL_XMH |
+					     ST7789V_LCMCTRL_XMX |
+					     ST7789V_LCMCTRL_XBGR));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_VDVVRHEN_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_VDVVRHEN_CMDEN));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_VRHS_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, 0xf));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_VDVS_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, 0x20));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_FRCTRL2_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, 0xf));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_PWCTRL1_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PWCTRL1_MAGIC));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PWCTRL1_AVDD(2) |
+					     ST7789V_PWCTRL1_AVCL(2) |
+					     ST7789V_PWCTRL1_VDS(1)));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_PVGAMCTRL_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP63(0xd)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP1(0xca)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP2(0xe)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP4(8)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP6(9)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP13(7)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP20(0x2d)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP27(0xb) |
+					     ST7789V_PVGAMCTRL_VP36(3)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP43(0x3d)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_JP1(3) |
+					     ST7789V_PVGAMCTRL_VP50(4)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP57(0xa)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP59(0xa)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP61(0x1b)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP62(0x28)));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_NVGAMCTRL_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN63(0xd)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN1(0xca)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN2(0xf)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN4(8)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN6(8)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN13(7)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN20(0x2e)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN27(0xc) |
+					     ST7789V_NVGAMCTRL_VN36(5)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN43(0x40)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_JN1(3) |
+					     ST7789V_NVGAMCTRL_VN50(4)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN57(9)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN59(0xb)));
+	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_EXIT_INVERT_MODE));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_RAMCTRL_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RAMCTRL_DM_RGB |
+					     ST7789V_RAMCTRL_RM_RGB));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RAMCTRL_EPF(3) |
+					     ST7789V_RAMCTRL_MAGIC));
+
+	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_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_VBP(8)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_HBP(20)));
+
+	return 0;
+}
+
 struct st7789_panel_info default_panel = {
 	.mode = &default_mode,
 	.init_sequence = init_sequence_default,
 };
 
+struct st7789_panel_info t28cp45tn89_panel = {
+	.mode = &t28cp45tn89_mode,
+	.init_sequence = init_sequence_t28cp45tn89,
+	.bpc = 6,
+	.bus_format = MEDIA_BUS_FMT_RGB565_1X16,
+};
+
 static int st7789v_get_modes(struct drm_panel *panel,
 			     struct drm_connector *connector)
 {
@@ -307,8 +438,12 @@ 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 = ctx->info->bpc;
 	connector->display_info.width_mm = ctx->info->mode->width_mm;
 	connector->display_info.height_mm = ctx->info->mode->height_mm;
+	if (ctx->info->bus_format)
+		drm_display_info_set_bus_formats(&connector->display_info,
+						 &ctx->info->bus_format, 1);
 
 	return 1;
 }
@@ -417,12 +552,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.39.2
#11 lore raw

Re: [PATCHv1 7/7] drm/panel: sitronix-st7789v: add Inanbo T28CP45TN89 support

Hi Sebastian,

Thanks for your work and for the beautiful timing :-)

Quoted 5 lines · 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.

I can spot only a few differences:

- bits per pixel: 18 (RGB666) vs. 16 (RGB565)
- invert mode
- sync/clk signal polarity

The init sequences are largely the same, which leads to vast code duplication. Instead, the st7789v_prepare could be adjusted to consider the st7789v_panel_info and apply the required settings accordingly.

For example, the polarities could be embedded into the drm_display_mode structure...

Quoted 18 lines
---
 .../gpu/drm/panel/panel-sitronix-st7789v.c    | 137 ++++++++++++++++++
 1 file changed, 137 insertions(+)
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +1 -0
index a62a2f5737e4..90f70eb84f11 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>

drivers/gpu/drm/panel/panel-sitronix-st7789v.c struct st7789v; · line 118

Quoted 5 lines
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +2 -0
@@ -113,6 +114,8 @@ struct st7789v;
 struct st7789_panel_info {
 	const struct drm_display_mode *mode;
 	int (*init_sequence)(struct st7789v *ctx);
+	unsigned int bpc;
+	u32 bus_format;

... and here you introduce fields for the bits per pixel. Just a field for the invert mode is missing.

BTW, I would introduce these fields in the previous patch. This patch should be only about filling out the already existing fields for the new panel.

Quoted 151 lines
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +128 -0
 };
 
 struct st7789v {
@@ -174,6 +177,20 @@ static const struct drm_display_mode default_mode = {
 	.height_mm = 103,
 };
 
+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,
+};
+
 static int init_sequence_default(struct st7789v *ctx) {
 	int ret;
 
@@ -283,11 +300,125 @@ static int init_sequence_default(struct st7789v *ctx) {
 	return 0;
 }
 
+static int init_sequence_t28cp45tn89(struct st7789v *ctx) {
+	int ret;
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx,
+						MIPI_DCS_SET_ADDRESS_MODE));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, 0));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx,
+						MIPI_DCS_SET_PIXEL_FORMAT));
+	ST7789V_TEST(ret, st7789v_write_data(ctx,
+					     (MIPI_DCS_PIXEL_FMT_16BIT << 4) |
+					     (MIPI_DCS_PIXEL_FMT_16BIT)));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_PORCTRL_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, 0xc));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, 0xc));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, 0));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PORCTRL_IDLE_BP(3) |
+					     ST7789V_PORCTRL_IDLE_FP(3)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx,
+					     ST7789V_PORCTRL_PARTIAL_BP(3) |
+					     ST7789V_PORCTRL_PARTIAL_FP(3)));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_GCTRL_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_GCTRL_VGLS(5) |
+					     ST7789V_GCTRL_VGHS(3)));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_VCOMS_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, 0x2b));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_LCMCTRL_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_LCMCTRL_XMH |
+					     ST7789V_LCMCTRL_XMX |
+					     ST7789V_LCMCTRL_XBGR));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_VDVVRHEN_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_VDVVRHEN_CMDEN));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_VRHS_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, 0xf));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_VDVS_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, 0x20));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_FRCTRL2_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, 0xf));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_PWCTRL1_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PWCTRL1_MAGIC));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PWCTRL1_AVDD(2) |
+					     ST7789V_PWCTRL1_AVCL(2) |
+					     ST7789V_PWCTRL1_VDS(1)));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_PVGAMCTRL_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP63(0xd)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP1(0xca)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP2(0xe)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP4(8)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP6(9)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP13(7)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP20(0x2d)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP27(0xb) |
+					     ST7789V_PVGAMCTRL_VP36(3)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP43(0x3d)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_JP1(3) |
+					     ST7789V_PVGAMCTRL_VP50(4)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP57(0xa)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP59(0xa)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP61(0x1b)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_PVGAMCTRL_VP62(0x28)));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_NVGAMCTRL_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN63(0xd)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN1(0xca)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN2(0xf)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN4(8)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN6(8)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN13(7)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN20(0x2e)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN27(0xc) |
+					     ST7789V_NVGAMCTRL_VN36(5)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN43(0x40)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_JN1(3) |
+					     ST7789V_NVGAMCTRL_VN50(4)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN57(9)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN59(0xb)));
+	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_EXIT_INVERT_MODE));
+
+	ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_RAMCTRL_CMD));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RAMCTRL_DM_RGB |
+					     ST7789V_RAMCTRL_RM_RGB));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RAMCTRL_EPF(3) |
+					     ST7789V_RAMCTRL_MAGIC));
+
+	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_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_VBP(8)));
+	ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_HBP(20)));
+
+	return 0;
+}
+
 struct st7789_panel_info default_panel = {
 	.mode = &default_mode,
 	.init_sequence = init_sequence_default,
 };
 
+struct st7789_panel_info t28cp45tn89_panel = {
+	.mode = &t28cp45tn89_mode,
+	.init_sequence = init_sequence_t28cp45tn89,
+	.bpc = 6,
+	.bus_format = MEDIA_BUS_FMT_RGB565_1X16,
+};
+
 static int st7789v_get_modes(struct drm_panel *panel,
 			     struct drm_connector *connector)
 {

drivers/gpu/drm/panel/panel-sitronix-st7789v.c static int st7789v_get_modes(struct drm_panel *panel, · line 446

Quoted 9 lines
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +4 -0
@@ -307,8 +438,12 @@ 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 = ctx->info->bpc;
 	connector->display_info.width_mm = ctx->info->mode->width_mm;
 	connector->display_info.height_mm = ctx->info->mode->height_mm;
+	if (ctx->info->bus_format)
+		drm_display_info_set_bus_formats(&connector->display_info,
+						 &ctx->info->bus_format, 1);

This should also be in a previous patch.

Best regards,
Michael

Quoted 18 lines
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +2 -0
 	return 1;
 }
@@ -417,12 +552,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);