Skip to contentSkip to conversation

Add Inanbo T28CP45TN89 panel support

PATCH Superseded Sebastian Reichel 4 participants 17 messages Apr 22, 2023 – Jul 12, 2023

Subsystems (4)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

v2 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.

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

-- Sebastian

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       |   5 +-
 .../devicetree/bindings/vendor-prefixes.yaml  |   2 +
 .../gpu/drm/panel/panel-sitronix-st7789v.c    | 148 ++++++++++++++----
 3 files changed, 120 insertions(+), 35 deletions(-)
Signature · 1 line
2.39.2
#1 lore raw

PATCH v2 1/13 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
#2 lore raw

PATCH v2 2/13 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..7c5e4313db1d 100644
@@ -15,7 +15,9 @@ allOf:
 
 properties:
   compatible:
-    const: sitronix,st7789v
+    enum:
+      - inanbo,t28cp45tn89-v17
+      - sitronix,st7789v
 
   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
maintainer Acked-by on v2 2/13
#3 lore raw

Re: [PATCH v2 02/13] dt-bindings: display: st7789v: add Inanbo T28CP45TN89

Quoted 8 lines · 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(-)
#4 lore raw

PATCH v2 3/13 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
#5 lore raw

PATCH v2 4/13 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
#6 lore raw

PATCH v2 5/13 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
#7 lore raw

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

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

PATCH v2 8/13 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..29c2a91f8299 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,
 };
 
+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.39.2
Reviewed-by on v2 8/13
#10 lore raw

Re: [PATCH v2 08/13] drm/panel: sitronix-st7789v: avoid hardcoding mode info

Hi Sebastian,

Thanks for the v2 of your series. Looks great!

One nitpick though: you seem to wrap the patch message lines at ~50 characters sometimes, which is awfully short.

Another comment below:

Quoted 29 lines · 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 +5 -0
index a6d6155ef45c..29c2a91f8299 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;

drivers/gpu/drm/panel/panel-sitronix-st7789v.c static const struct drm_display_mode default_mode = { · line 170

Quoted 6 lines
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +3 -0
@@ -160,16 +165,21 @@ static const struct drm_display_mode default_mode = {
 	.vtotal = 320 + 8 + 4 + 4,
 };
 
+struct st7789_panel_info default_panel = {
+	.mode = &default_mode,
+};

Shouldn't this be "static const struct st7789_panel_info default_panel"?

(Same holds for "struct st7789_panel_info t28cp45tn89_panel" in patch 13/13.)

With the comments above addressed, feel free to add my

to the whole v3 of your series.

Thanks and best regards,
Michael

Quoted 44 lines
drivers/gpu/drm/panel/panel-sitronix-st7789v.c +10 -6
+
 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);
#11 lore raw

PATCH v2 9/13 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 29c2a91f8299..0e1839e6da73 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,
 };
 
 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.39.2
#12 lore raw

PATCH v2 10/13 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 0e1839e6da73..6d155c6262f3 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 = {
 
 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.39.2
#13 lore raw

PATCH v2 11/13 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 6d155c6262f3..158623a8dfb7 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 = {
 
 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.39.2
#14 lore raw

PATCH v2 12/13 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 158623a8dfb7..f3b1bf7ef996 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,
 };
 
 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.39.2
#15 lore raw

PATCH v2 13/13 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 f3b1bf7ef996..172c6c1fc090 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,
+};
+
 struct st7789_panel_info default_panel = {
 	.mode = &default_mode,
 	.invert_mode = true,
@@ -181,6 +196,14 @@ struct st7789_panel_info default_panel = {
 		     DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE,
 };
 
+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.39.2
#16 lore raw
In reply to Sebastian Reichel

Hi Sebastian,

+ Thomas

[email protected] wrote on Sat, 22 Apr 2023 22:49:59 +0200:

Quoted 5 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 haven't seen another version for this series so I assume it is still the one to look at. As you already know, I also want to add support for a panel based on the st7789 display controller. As discussed, I rebased my changes on top of yours as you actually sent them upstream much earlier than I did.

As a single minor comment was made to this version of the series, I would like to know if you wanted to send a new version soon? Or if it would make sense to send a bigger series with all our common patches in one single shot (mine should apply cleanly without further work on yours). Let me know how we can move forward.

For the record, here are my patches.

Thanks a lot,
Miquèl

Quoted 29 lines

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

-- Sebastian

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       |   5 +-
 .../devicetree/bindings/vendor-prefixes.yaml  |   2 +
 .../gpu/drm/panel/panel-sitronix-st7789v.c    | 148 ++++++++++++++----
 3 files changed, 120 insertions(+), 35 deletions(-)