* [RFC PATCH v3 2/3] drm/panel: sitronix-st7789v: Convert to mipi_dbi
2026-02-21 7:13 [RFC PATCH v3 0/3] drm/panel: sitronix-st7789v: Convert to mipi_dbi and add tinydrm Archit Anant
2026-02-21 7:13 ` [RFC PATCH v3 1/3] drm/mipi-dbi: Provide option to invert reset GPIO logic Archit Anant
@ 2026-02-21 7:13 ` Archit Anant
2026-02-21 7:13 ` [RFC PATCH v3 3/3] drm/panel: sitronix-st7789v: add standalone tinydrm support Archit Anant
2 siblings, 0 replies; 4+ messages in thread
From: Archit Anant @ 2026-02-21 7:13 UTC (permalink / raw)
To: neil.armstrong, jesszhan0024, maarten.lankhorst, mripard,
tzimmermann
Cc: sebastian.reichel, gerald.loacker, michael.riesch, miquel.raynal,
wens, airlied, simona, architanant5, dri-devel, linux-kernel
From: Chen-Yu Tsai <wens@kernel.org>
The wire protocol of the ST7789V is basically MIPI DBI. Switch to the
mipi_dbi helpers to reduce some code. This also ends up adding support
for 8-bit D/C mode. The reset logic in the mipi_dbi helpers is also
used.
While at it, also clean up st7789v_check_id() to use ST7789V_IDS_SIZE
to declare the ids array size and sizeof(ids) where the size is
needed.
Signed-off-by: Chen-Yu Tsai <wens@kernel.org>
---
drivers/gpu/drm/panel/Kconfig | 1 +
.../gpu/drm/panel/panel-sitronix-st7789v.c | 306 +++++++-----------
2 files changed, 110 insertions(+), 197 deletions(-)
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 307152ad7759..64325874d3e2 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -1011,6 +1011,7 @@ config DRM_PANEL_SITRONIX_ST7789V
tristate "Sitronix ST7789V panel"
depends on OF && SPI
depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_MIPI_DBI
help
Say Y here if you want to enable support for the Sitronix
ST7789V controller for 240x320 LCD panels
diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
index d5f821d6b23c..b77e616f2994 100644
--- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
+++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
@@ -14,6 +14,7 @@
#include <drm/drm_device.h>
#include <drm/drm_modes.h>
+#include <drm/drm_mipi_dbi.h>
#include <drm/drm_panel.h>
#define ST7789V_RAMCTRL_CMD 0xb0
@@ -125,9 +126,9 @@ struct st7789_panel_info {
struct st7789v {
struct drm_panel panel;
+ struct mipi_dbi dbi;
const struct st7789_panel_info *info;
struct spi_device *spi;
- struct gpio_desc *reset;
struct regulator *power;
enum drm_panel_orientation orientation;
};
@@ -142,86 +143,23 @@ static inline struct st7789v *panel_to_st7789v(struct drm_panel *panel)
return container_of(panel, struct st7789v, panel);
}
-static int st7789v_spi_write(struct st7789v *ctx, enum st7789v_prefix prefix,
- u8 data)
-{
- struct spi_transfer xfer = { };
- u16 txbuf = ((prefix & 1) << 8) | data;
-
- xfer.tx_buf = &txbuf;
- xfer.len = sizeof(txbuf);
-
- return spi_sync_transfer(ctx->spi, &xfer, 1);
-}
-
-static int st7789v_write_command(struct st7789v *ctx, u8 cmd)
-{
- return st7789v_spi_write(ctx, ST7789V_COMMAND, cmd);
-}
-
-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];
+ u8 ids[ST7789V_IDS_SIZE];
if (ctx->spi->mode & SPI_NO_RX)
return 0;
- ret = st7789v_read_data(ctx, MIPI_DCS_GET_DISPLAY_ID, ids, ST7789V_IDS_SIZE);
+ ret = mipi_dbi_command_stackbuf(&ctx->dbi, MIPI_DCS_GET_DISPLAY_ID,
+ ids, sizeof(ids));
if (ret)
return ret;
- for (i = 0; i < ST7789V_IDS_SIZE; i++) {
+ for (i = 0; i < sizeof(ids); i++) {
if (ids[i] != st7789v_ids[i]) {
invalid_ids = true;
break;
@@ -379,6 +317,7 @@ static enum drm_panel_orientation st7789v_get_orientation(struct drm_panel *p)
static int st7789v_prepare(struct drm_panel *panel)
{
struct st7789v *ctx = panel_to_st7789v(panel);
+ struct mipi_dbi *dbi = &ctx->dbi;
u8 mode, pixel_fmt, polarity;
int ret;
@@ -416,10 +355,7 @@ static int st7789v_prepare(struct drm_panel *panel)
if (ret)
return ret;
- gpiod_set_value(ctx->reset, 1);
- msleep(30);
- gpiod_set_value(ctx->reset, 0);
- msleep(120);
+ mipi_dbi_hw_reset(&ctx->dbi);
/*
* Avoid failing if the IDs are invalid in case the Rx bus width
@@ -429,101 +365,81 @@ static int st7789v_prepare(struct drm_panel *panel)
if (ret)
dev_warn(panel->dev, "Unrecognized panel IDs");
- ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_EXIT_SLEEP_MODE));
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, 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));
-
- ST7789V_TEST(ret, st7789v_write_command(ctx,
- MIPI_DCS_SET_PIXEL_FORMAT));
- 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));
- 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, mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, 0));
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, pixel_fmt));
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_PORCTRL_CMD, 0xc, 0xc, 0,
+ ST7789V_PORCTRL_IDLE_BP(3) |
+ ST7789V_PORCTRL_IDLE_FP(3),
+ ST7789V_PORCTRL_PARTIAL_BP(3) |
+ ST7789V_PORCTRL_PARTIAL_FP(3)));
+
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_GCTRL_CMD,
+ ST7789V_GCTRL_VGLS(5) |
+ ST7789V_GCTRL_VGHS(3)));
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_VCOMS_CMD, 0x2b));
+
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_LCMCTRL_CMD,
+ ST7789V_LCMCTRL_XMH |
+ ST7789V_LCMCTRL_XMX |
+ ST7789V_LCMCTRL_XBGR));
+
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_VDVVRHEN_CMD,
+ ST7789V_VDVVRHEN_CMDEN));
+
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_VRHS_CMD, 0xf));
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_VDVS_CMD, 0x20));
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_FRCTRL2_CMD, 0xf));
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_PWCTRL1_CMD,
+ ST7789V_PWCTRL1_MAGIC,
+ ST7789V_PWCTRL1_AVDD(2) |
+ ST7789V_PWCTRL1_AVCL(2) |
+ ST7789V_PWCTRL1_VDS(1)));
+
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_PVGAMCTRL_CMD,
+ ST7789V_PVGAMCTRL_VP63(0xd),
+ ST7789V_PVGAMCTRL_VP1(0xca),
+ ST7789V_PVGAMCTRL_VP2(0xe),
+ ST7789V_PVGAMCTRL_VP4(8),
+ ST7789V_PVGAMCTRL_VP6(9),
+ ST7789V_PVGAMCTRL_VP13(7),
+ ST7789V_PVGAMCTRL_VP20(0x2d),
+ ST7789V_PVGAMCTRL_VP27(0xb) |
+ ST7789V_PVGAMCTRL_VP36(3),
+ ST7789V_PVGAMCTRL_VP43(0x3d),
+ ST7789V_PVGAMCTRL_JP1(3) |
+ ST7789V_PVGAMCTRL_VP50(4),
+ ST7789V_PVGAMCTRL_VP57(0xa),
+ ST7789V_PVGAMCTRL_VP59(0xa),
+ ST7789V_PVGAMCTRL_VP61(0x1b),
+ ST7789V_PVGAMCTRL_VP62(0x28)));
+
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_NVGAMCTRL_CMD,
+ ST7789V_NVGAMCTRL_VN63(0xd),
+ ST7789V_NVGAMCTRL_VN1(0xca),
+ ST7789V_NVGAMCTRL_VN2(0xf),
+ ST7789V_NVGAMCTRL_VN4(8),
+ ST7789V_NVGAMCTRL_VN6(8),
+ ST7789V_NVGAMCTRL_VN13(7),
+ ST7789V_NVGAMCTRL_VN20(0x2e),
+ ST7789V_NVGAMCTRL_VN27(0xc) |
+ ST7789V_NVGAMCTRL_VN36(5),
+ ST7789V_NVGAMCTRL_VN43(0x40),
+ ST7789V_NVGAMCTRL_JN1(3) |
+ ST7789V_NVGAMCTRL_VN50(4),
+ ST7789V_NVGAMCTRL_VN57(9),
+ ST7789V_NVGAMCTRL_VN59(0xb),
+ ST7789V_NVGAMCTRL_VN61(0x1b),
+ ST7789V_NVGAMCTRL_VN62(0x28)));
if (ctx->info->invert_mode) {
- ST7789V_TEST(ret, st7789v_write_command(ctx,
- MIPI_DCS_ENTER_INVERT_MODE));
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, MIPI_DCS_ENTER_INVERT_MODE));
} else {
- ST7789V_TEST(ret, st7789v_write_command(ctx,
- MIPI_DCS_EXIT_INVERT_MODE));
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, MIPI_DCS_EXIT_INVERT_MODE));
}
if (ctx->info->partial_mode) {
@@ -539,36 +455,27 @@ static int st7789v_prepare(struct drm_panel *panel)
* add margins.
*/
- ST7789V_TEST(ret, st7789v_write_command(
- ctx, MIPI_DCS_ENTER_PARTIAL_MODE));
-
- ST7789V_TEST(ret, st7789v_write_command(
- ctx, MIPI_DCS_SET_PAGE_ADDRESS));
- ST7789V_TEST(ret, st7789v_write_data(ctx, area_data[0]));
- ST7789V_TEST(ret, st7789v_write_data(ctx, area_data[1]));
- ST7789V_TEST(ret, st7789v_write_data(ctx, area_data[2]));
- ST7789V_TEST(ret, st7789v_write_data(ctx, area_data[3]));
-
- ST7789V_TEST(ret, st7789v_write_command(
- ctx, MIPI_DCS_SET_PARTIAL_ROWS));
- ST7789V_TEST(ret, st7789v_write_data(ctx, area_data[0]));
- ST7789V_TEST(ret, st7789v_write_data(ctx, area_data[1]));
- ST7789V_TEST(ret, st7789v_write_data(ctx, area_data[2]));
- ST7789V_TEST(ret, st7789v_write_data(ctx, area_data[3]));
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, MIPI_DCS_ENTER_PARTIAL_MODE));
+
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, MIPI_DCS_SET_PAGE_ADDRESS,
+ area_data[0], area_data[1],
+ area_data[2], area_data[3]));
+
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, MIPI_DCS_SET_PARTIAL_ROWS,
+ area_data[0], area_data[1],
+ area_data[2], area_data[3]));
}
- 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, mipi_dbi_command(dbi, ST7789V_RAMCTRL_CMD,
+ ST7789V_RAMCTRL_DM_RGB |
+ ST7789V_RAMCTRL_RM_RGB,
+ ST7789V_RAMCTRL_EPF(3) |
+ ST7789V_RAMCTRL_MAGIC));
- ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_RGBCTRL_CMD));
- ST7789V_TEST(ret, st7789v_write_data(ctx, mode |
- ST7789V_RGBCTRL_RCM(2) |
- polarity));
- ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_VBP(8)));
- ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_HBP(20)));
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_RGBCTRL_CMD,
+ mode | ST7789V_RGBCTRL_RCM(2) | polarity,
+ ST7789V_RGBCTRL_VBP(8),
+ ST7789V_RGBCTRL_HBP(20)));
return 0;
}
@@ -577,7 +484,7 @@ static int st7789v_enable(struct drm_panel *panel)
{
struct st7789v *ctx = panel_to_st7789v(panel);
- return st7789v_write_command(ctx, MIPI_DCS_SET_DISPLAY_ON);
+ return mipi_dbi_command(&ctx->dbi, MIPI_DCS_SET_DISPLAY_ON);
}
static int st7789v_disable(struct drm_panel *panel)
@@ -585,7 +492,7 @@ static int st7789v_disable(struct drm_panel *panel)
struct st7789v *ctx = panel_to_st7789v(panel);
int ret;
- ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_SET_DISPLAY_OFF));
+ ST7789V_TEST(ret, mipi_dbi_command(&ctx->dbi, MIPI_DCS_SET_DISPLAY_OFF));
return 0;
}
@@ -595,7 +502,7 @@ static int st7789v_unprepare(struct drm_panel *panel)
struct st7789v *ctx = panel_to_st7789v(panel);
int ret;
- ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_ENTER_SLEEP_MODE));
+ ST7789V_TEST(ret, mipi_dbi_command(&ctx->dbi, MIPI_DCS_ENTER_SLEEP_MODE));
regulator_disable(ctx->power);
@@ -615,6 +522,7 @@ static int st7789v_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct st7789v *ctx;
+ struct gpio_desc *dc;
int ret;
ctx = devm_drm_panel_alloc(dev, struct st7789v, panel,
@@ -625,11 +533,6 @@ 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);
ctx->power = devm_regulator_get(dev, "power");
@@ -637,11 +540,16 @@ static int st7789v_probe(struct spi_device *spi)
if (ret)
return dev_err_probe(dev, ret, "Failed to get regulator\n");
- ctx->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
- ret = PTR_ERR_OR_ZERO(ctx->reset);
+ ctx->dbi.reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ ret = PTR_ERR_OR_ZERO(ctx->dbi.reset);
if (ret)
return dev_err_probe(dev, ret, "Failed to get reset line\n");
+ dc = devm_gpiod_get_optional(&spi->dev, "dc", GPIOD_OUT_LOW);
+ ret = PTR_ERR_OR_ZERO(dc);
+ if (ret)
+ return dev_err_probe(&spi->dev, ret, "Failed to get GPIO for D/C\n");
+
ret = drm_panel_of_backlight(&ctx->panel);
if (ret)
return dev_err_probe(dev, ret, "Failed to get backlight\n");
@@ -650,6 +558,10 @@ static int st7789v_probe(struct spi_device *spi)
if (ret)
return dev_err_probe(&spi->dev, ret, "Failed to get orientation\n");
+ ret = mipi_dbi_spi_init(spi, &ctx->dbi, dc);
+ if (ret)
+ return dev_err_probe(&spi->dev, ret, "Failed to init MIPI DBI\n");
+
drm_panel_add(&ctx->panel);
return 0;
--
2.39.5
^ permalink raw reply related [flat|nested] 4+ messages in thread* [RFC PATCH v3 3/3] drm/panel: sitronix-st7789v: add standalone tinydrm support
2026-02-21 7:13 [RFC PATCH v3 0/3] drm/panel: sitronix-st7789v: Convert to mipi_dbi and add tinydrm Archit Anant
2026-02-21 7:13 ` [RFC PATCH v3 1/3] drm/mipi-dbi: Provide option to invert reset GPIO logic Archit Anant
2026-02-21 7:13 ` [RFC PATCH v3 2/3] drm/panel: sitronix-st7789v: Convert to mipi_dbi Archit Anant
@ 2026-02-21 7:13 ` Archit Anant
2 siblings, 0 replies; 4+ messages in thread
From: Archit Anant @ 2026-02-21 7:13 UTC (permalink / raw)
To: neil.armstrong, jesszhan0024, maarten.lankhorst, mripard,
tzimmermann
Cc: sebastian.reichel, gerald.loacker, michael.riesch, miquel.raynal,
wens, airlied, simona, architanant5, dri-devel, linux-kernel
The current panel-sitronix-st7789v driver functions only as a DRM
panel, requiring an external display controller to operate. However,
the majority of ST7789V displays in the hobbyist ecosystem are used as
standalone screens connected via 8-bit SPI.
Building on the mipi_dbi conversion in the previous patches, this commit
introduces drm_simple_display_pipe support. This allows the driver to
register as a full-featured DRM device (tinydrm) when a master display
controller is not present, effectively providing a modern replacement
for the legacy staging fbtft driver.
Additionally, this patch ports support for the HannStar HSD20-IPS panel
from the staging driver. This is implemented via a new 'is_ips' flag in
the panel info structure, which selects the optimized PORCTRL and GCTRL
settings required by that specific glass panel.
This hybrid architecture ensures the driver is suitable for both
high-performance SoC RGB interfaces and simple SPI-based embedded
systems.
Signed-off-by: Archit Anant <architanant5@gmail.com>
---
.../gpu/drm/panel/panel-sitronix-st7789v.c | 153 +++++++++++++++---
1 file changed, 127 insertions(+), 26 deletions(-)
diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
index b77e616f2994..a07568edc701 100644
--- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
+++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
@@ -12,9 +12,14 @@
#include <video/mipi_display.h>
#include <linux/media-bus-format.h>
+#include <drm/clients/drm_client_setup.h>
+#include <drm/drm_atomic_helper.h>
#include <drm/drm_device.h>
-#include <drm/drm_modes.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fbdev_dma.h>
+#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_mipi_dbi.h>
+#include <drm/drm_modes.h>
#include <drm/drm_panel.h>
#define ST7789V_RAMCTRL_CMD 0xb0
@@ -107,7 +112,8 @@
#define ST7789V_TEST(val, func) \
do { \
- if ((val = (func))) \
+ val = (func); \
+ if (val) \
return val; \
} while (0)
@@ -120,13 +126,14 @@ struct st7789_panel_info {
u32 bus_flags;
bool invert_mode;
bool partial_mode;
+ bool is_ips;
u16 partial_start;
u16 partial_end;
};
struct st7789v {
struct drm_panel panel;
- struct mipi_dbi dbi;
+ struct mipi_dbi_dev dbidev;
const struct st7789_panel_info *info;
struct spi_device *spi;
struct regulator *power;
@@ -154,7 +161,7 @@ static int st7789v_check_id(struct drm_panel *panel)
if (ctx->spi->mode & SPI_NO_RX)
return 0;
- ret = mipi_dbi_command_stackbuf(&ctx->dbi, MIPI_DCS_GET_DISPLAY_ID,
+ ret = mipi_dbi_command_stackbuf(&ctx->dbidev.dbi, MIPI_DCS_GET_DISPLAY_ID,
ids, sizeof(ids));
if (ret)
return ret;
@@ -237,6 +244,14 @@ static const struct drm_display_mode jt240mhqs_hwt_ek_e3_mode = {
.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
};
+/*
+ * The HSD20_IPS panel is used in SPI mode via the staging driver.
+ * Timings are handled internally by the controller configuration.
+ */
+static const struct drm_display_mode hsd20_ips_mode = {
+ DRM_SIMPLE_MODE(240, 320, 31, 41),
+};
+
static const struct st7789_panel_info default_panel = {
.mode = &default_mode,
.invert_mode = true,
@@ -272,6 +287,14 @@ static const struct st7789_panel_info jt240mhqs_hwt_ek_e3_panel = {
.partial_end = 318,
};
+static const struct st7789_panel_info hsd20_ips_panel = {
+ .mode = &hsd20_ips_mode,
+ .invert_mode = true,
+ .is_ips = true,
+ .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)
{
@@ -317,7 +340,7 @@ static enum drm_panel_orientation st7789v_get_orientation(struct drm_panel *p)
static int st7789v_prepare(struct drm_panel *panel)
{
struct st7789v *ctx = panel_to_st7789v(panel);
- struct mipi_dbi *dbi = &ctx->dbi;
+ struct mipi_dbi *dbi = &ctx->dbidev.dbi;
u8 mode, pixel_fmt, polarity;
int ret;
@@ -355,7 +378,7 @@ static int st7789v_prepare(struct drm_panel *panel)
if (ret)
return ret;
- mipi_dbi_hw_reset(&ctx->dbi);
+ mipi_dbi_hw_reset(&ctx->dbidev.dbi);
/*
* Avoid failing if the IDs are invalid in case the Rx bus width
@@ -372,15 +395,24 @@ static int st7789v_prepare(struct drm_panel *panel)
ST7789V_TEST(ret, mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, 0));
ST7789V_TEST(ret, mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, pixel_fmt));
- ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_PORCTRL_CMD, 0xc, 0xc, 0,
- ST7789V_PORCTRL_IDLE_BP(3) |
- ST7789V_PORCTRL_IDLE_FP(3),
- ST7789V_PORCTRL_PARTIAL_BP(3) |
- ST7789V_PORCTRL_PARTIAL_FP(3)));
-
- ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_GCTRL_CMD,
- ST7789V_GCTRL_VGLS(5) |
- ST7789V_GCTRL_VGHS(3)));
+
+ if (ctx->info->is_ips) {
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_PORCTRL_CMD,
+ 0x05, 0x05, 0x00, 0x33, 0x33));
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_GCTRL_CMD,
+ 0x75));
+ } else {
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_PORCTRL_CMD, 0xc, 0xc, 0,
+ ST7789V_PORCTRL_IDLE_BP(3) |
+ ST7789V_PORCTRL_IDLE_FP(3),
+ ST7789V_PORCTRL_PARTIAL_BP(3) |
+ ST7789V_PORCTRL_PARTIAL_FP(3)));
+
+ ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_GCTRL_CMD,
+ ST7789V_GCTRL_VGLS(5) |
+ ST7789V_GCTRL_VGHS(3)));
+ }
+
ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_VCOMS_CMD, 0x2b));
ST7789V_TEST(ret, mipi_dbi_command(dbi, ST7789V_LCMCTRL_CMD,
@@ -436,11 +468,10 @@ static int st7789v_prepare(struct drm_panel *panel)
ST7789V_NVGAMCTRL_VN61(0x1b),
ST7789V_NVGAMCTRL_VN62(0x28)));
- if (ctx->info->invert_mode) {
+ if (ctx->info->invert_mode)
ST7789V_TEST(ret, mipi_dbi_command(dbi, MIPI_DCS_ENTER_INVERT_MODE));
- } else {
+ else
ST7789V_TEST(ret, mipi_dbi_command(dbi, MIPI_DCS_EXIT_INVERT_MODE));
- }
if (ctx->info->partial_mode) {
u8 area_data[4] = {
@@ -484,7 +515,7 @@ static int st7789v_enable(struct drm_panel *panel)
{
struct st7789v *ctx = panel_to_st7789v(panel);
- return mipi_dbi_command(&ctx->dbi, MIPI_DCS_SET_DISPLAY_ON);
+ return mipi_dbi_command(&ctx->dbidev.dbi, MIPI_DCS_SET_DISPLAY_ON);
}
static int st7789v_disable(struct drm_panel *panel)
@@ -492,7 +523,7 @@ static int st7789v_disable(struct drm_panel *panel)
struct st7789v *ctx = panel_to_st7789v(panel);
int ret;
- ST7789V_TEST(ret, mipi_dbi_command(&ctx->dbi, MIPI_DCS_SET_DISPLAY_OFF));
+ ST7789V_TEST(ret, mipi_dbi_command(&ctx->dbidev.dbi, MIPI_DCS_SET_DISPLAY_OFF));
return 0;
}
@@ -502,7 +533,7 @@ static int st7789v_unprepare(struct drm_panel *panel)
struct st7789v *ctx = panel_to_st7789v(panel);
int ret;
- ST7789V_TEST(ret, mipi_dbi_command(&ctx->dbi, MIPI_DCS_ENTER_SLEEP_MODE));
+ ST7789V_TEST(ret, mipi_dbi_command(&ctx->dbidev.dbi, MIPI_DCS_ENTER_SLEEP_MODE));
regulator_disable(ctx->power);
@@ -518,6 +549,52 @@ static const struct drm_panel_funcs st7789v_drm_funcs = {
.unprepare = st7789v_unprepare,
};
+static void st7789v_pipe_enable(struct drm_simple_display_pipe *pipe,
+ struct drm_crtc_state *crtc_state,
+ struct drm_plane_state *plane_state)
+{
+ struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
+ struct st7789v *ctx = container_of(dbidev, struct st7789v, dbidev);
+ int ret, idx;
+
+ if (!drm_dev_enter(pipe->crtc.dev, &idx))
+ return;
+
+ ret = st7789v_prepare(&ctx->panel);
+ if (ret)
+ goto out;
+
+ ret = st7789v_enable(&ctx->panel);
+ if (ret)
+ goto out_unprepare;
+
+ mipi_dbi_enable_flush(dbidev, crtc_state, plane_state);
+ goto out;
+
+out_unprepare:
+ st7789v_unprepare(&ctx->panel);
+out:
+ drm_dev_exit(idx);
+}
+
+static const struct drm_simple_display_pipe_funcs st7789v_pipe_funcs = {
+ DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS(st7789v_pipe_enable),
+};
+
+DEFINE_DRM_GEM_DMA_FOPS(st7789v_fops);
+
+static const struct drm_driver st7789v_drm_driver = {
+ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
+ .fops = &st7789v_fops,
+ DRM_GEM_DMA_DRIVER_OPS_VMAP,
+ DRM_FBDEV_DMA_DRIVER_OPS,
+ .debugfs_init = mipi_dbi_debugfs_init,
+ .name = "st7789v",
+ .desc = "Sitronix ST7789V",
+ .major = 1,
+ .minor = 0,
+};
+
static int st7789v_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
@@ -525,10 +602,11 @@ static int st7789v_probe(struct spi_device *spi)
struct gpio_desc *dc;
int ret;
- ctx = devm_drm_panel_alloc(dev, struct st7789v, panel,
- &st7789v_drm_funcs, DRM_MODE_CONNECTOR_DPI);
+ ctx = devm_drm_dev_alloc(dev, &st7789v_drm_driver, struct st7789v,
+ dbidev.drm);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
+ drm_panel_init(&ctx->panel, dev, &st7789v_drm_funcs, DRM_MODE_CONNECTOR_DPI);
spi_set_drvdata(spi, ctx);
ctx->spi = spi;
@@ -540,8 +618,8 @@ static int st7789v_probe(struct spi_device *spi)
if (ret)
return dev_err_probe(dev, ret, "Failed to get regulator\n");
- ctx->dbi.reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
- ret = PTR_ERR_OR_ZERO(ctx->dbi.reset);
+ ctx->dbidev.dbi.reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ ret = PTR_ERR_OR_ZERO(ctx->dbidev.dbi.reset);
if (ret)
return dev_err_probe(dev, ret, "Failed to get reset line\n");
@@ -558,12 +636,31 @@ static int st7789v_probe(struct spi_device *spi)
if (ret)
return dev_err_probe(&spi->dev, ret, "Failed to get orientation\n");
- ret = mipi_dbi_spi_init(spi, &ctx->dbi, dc);
+ ret = mipi_dbi_spi_init(spi, &ctx->dbidev.dbi, dc);
if (ret)
return dev_err_probe(&spi->dev, ret, "Failed to init MIPI DBI\n");
drm_panel_add(&ctx->panel);
+ ctx->dbidev.dbi.invert_reset = true;
+
+ ret = mipi_dbi_dev_init(&ctx->dbidev, &st7789v_pipe_funcs, ctx->info->mode,
+ ctx->orientation);
+ if (ret) {
+ drm_panel_remove(&ctx->panel);
+ return ret;
+ }
+
+ drm_mode_config_reset(&ctx->dbidev.drm);
+
+ ret = drm_dev_register(&ctx->dbidev.drm, 0);
+
+ if (ret) {
+ drm_panel_remove(&ctx->panel);
+ return ret;
+ }
+ drm_client_setup(&ctx->dbidev.drm, NULL);
+
return 0;
}
@@ -571,6 +668,8 @@ static void st7789v_remove(struct spi_device *spi)
{
struct st7789v *ctx = spi_get_drvdata(spi);
+ drm_dev_unplug(&ctx->dbidev.drm);
+ drm_atomic_helper_shutdown(&ctx->dbidev.drm);
drm_panel_remove(&ctx->panel);
}
@@ -579,6 +678,7 @@ static const struct spi_device_id st7789v_spi_id[] = {
{ "t28cp45tn89-v17", (unsigned long) &t28cp45tn89_panel },
{ "et028013dma", (unsigned long) &et028013dma_panel },
{ "jt240mhqs-hwt-ek-e3", (unsigned long) &jt240mhqs_hwt_ek_e3_panel },
+ { "hsd20-ips", (unsigned long) &hsd20_ips_panel},
{ }
};
MODULE_DEVICE_TABLE(spi, st7789v_spi_id);
@@ -589,6 +689,7 @@ static const struct of_device_id st7789v_of_match[] = {
{ .compatible = "edt,et028013dma", .data = &et028013dma_panel },
{ .compatible = "jasonic,jt240mhqs-hwt-ek-e3",
.data = &jt240mhqs_hwt_ek_e3_panel },
+ { .compatible = "hannstar,hsd20-ips", .data = &hsd20_ips_panel },
{ }
};
MODULE_DEVICE_TABLE(of, st7789v_of_match);
--
2.39.5
^ permalink raw reply related [flat|nested] 4+ messages in thread