This adds a panel driver for the Sitronix st7789v panel based
on the corresponding Linux-6.10 driver.

Signed-off-by: Sascha Hauer <s.ha...@pengutronix.de>
---
 drivers/video/Kconfig                  |   8 +
 drivers/video/Makefile                 |   1 +
 drivers/video/panel-sitronix-st7789v.c | 652 +++++++++++++++++++++++++++++++++
 include/video/vpl.h                    |   1 +
 4 files changed, 662 insertions(+)

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 9e176d3198..e86f511da4 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -186,6 +186,14 @@ config DRIVER_VIDEO_PANEL_ILITEK_ILI9341
          QVGA (240x320) RGB panels. support serial & parallel rgb
          interface.
 
+config DRIVER_VIDEO_PANEL_SITRONIX_ST7789V
+       tristate "Sitronix ST7789V panel"
+       depends on OFTREE && SPI
+       select VIDEO_VPL
+       help
+         Say Y here if you want to enable support for the Sitronix
+         ST7789V controller for 240x320 LCD panels
+
 config DRIVER_VIDEO_PANEL_MIPI_DBI
        tristate "DRM support for MIPI DBI compatible panels"
        depends on OFTREE && SPI
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 9e6a241a33..37a184a7e2 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_DRIVER_VIDEO_SIMPLE_PANEL) += simple-panel.o
 obj-$(CONFIG_DRIVER_VIDEO_MIPI_DBI) += mipi_dbi.o
 obj-$(CONFIG_DRIVER_VIDEO_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o
 obj-$(CONFIG_DRIVER_VIDEO_PANEL_MIPI_DBI) += panel-mipi-dbi.o
+obj-$(CONFIG_DRIVER_VIDEO_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
 
 obj-$(CONFIG_DRIVER_VIDEO_ATMEL) += atmel_lcdfb.o atmel_lcdfb_core.o
 obj-$(CONFIG_DRIVER_VIDEO_ATMEL_HLCD) += atmel_hlcdfb.o atmel_lcdfb_core.o
diff --git a/drivers/video/panel-sitronix-st7789v.c 
b/drivers/video/panel-sitronix-st7789v.c
new file mode 100644
index 0000000000..62443f01d6
--- /dev/null
+++ b/drivers/video/panel-sitronix-st7789v.c
@@ -0,0 +1,652 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017 Free Electrons
+ */
+
+#include <driver.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <regulator.h>
+#include <spi/spi.h>
+#include <fb.h>
+#include <linux/device.h>
+#include <video/vpl.h>
+#include <video/videomode.h>
+#include <video/drm/drm_connector.h>
+#include <video/mipi_display.h>
+#include <video/media-bus-format.h>
+
+#define ST7789V_RAMCTRL_CMD            0xb0
+#define ST7789V_RAMCTRL_RM_RGB                 BIT(4)
+#define ST7789V_RAMCTRL_DM_RGB                 BIT(0)
+#define ST7789V_RAMCTRL_MAGIC                  (3 << 6)
+#define ST7789V_RAMCTRL_EPF(n)                 (((n) & 3) << 4)
+
+#define ST7789V_RGBCTRL_CMD            0xb1
+#define ST7789V_RGBCTRL_WO                     BIT(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_FALLING           BIT(1)
+#define ST7789V_RGBCTRL_DE_LOW                 BIT(0)
+#define ST7789V_RGBCTRL_VBP(n)                 ((n) & 0x7f)
+#define ST7789V_RGBCTRL_HBP(n)                 ((n) & 0x1f)
+
+#define ST7789V_PORCTRL_CMD            0xb2
+#define ST7789V_PORCTRL_IDLE_BP(n)             (((n) & 0xf) << 4)
+#define ST7789V_PORCTRL_IDLE_FP(n)             ((n) & 0xf)
+#define ST7789V_PORCTRL_PARTIAL_BP(n)          (((n) & 0xf) << 4)
+#define ST7789V_PORCTRL_PARTIAL_FP(n)          ((n) & 0xf)
+
+#define ST7789V_GCTRL_CMD              0xb7
+#define ST7789V_GCTRL_VGHS(n)                  (((n) & 7) << 4)
+#define ST7789V_GCTRL_VGLS(n)                  ((n) & 7)
+
+#define ST7789V_VCOMS_CMD              0xbb
+
+#define ST7789V_LCMCTRL_CMD            0xc0
+#define ST7789V_LCMCTRL_XBGR                   BIT(5)
+#define ST7789V_LCMCTRL_XMX                    BIT(3)
+#define ST7789V_LCMCTRL_XMH                    BIT(2)
+
+#define ST7789V_VDVVRHEN_CMD           0xc2
+#define ST7789V_VDVVRHEN_CMDEN                 BIT(0)
+
+#define ST7789V_VRHS_CMD               0xc3
+
+#define ST7789V_VDVS_CMD               0xc4
+
+#define ST7789V_FRCTRL2_CMD            0xc6
+
+#define ST7789V_PWCTRL1_CMD            0xd0
+#define ST7789V_PWCTRL1_MAGIC                  0xa4
+#define ST7789V_PWCTRL1_AVDD(n)                        (((n) & 3) << 6)
+#define ST7789V_PWCTRL1_AVCL(n)                        (((n) & 3) << 4)
+#define ST7789V_PWCTRL1_VDS(n)                 ((n) & 3)
+
+#define ST7789V_PVGAMCTRL_CMD          0xe0
+#define ST7789V_PVGAMCTRL_JP0(n)               (((n) & 3) << 4)
+#define ST7789V_PVGAMCTRL_JP1(n)               (((n) & 3) << 4)
+#define ST7789V_PVGAMCTRL_VP0(n)               ((n) & 0xf)
+#define ST7789V_PVGAMCTRL_VP1(n)               ((n) & 0x3f)
+#define ST7789V_PVGAMCTRL_VP2(n)               ((n) & 0x3f)
+#define ST7789V_PVGAMCTRL_VP4(n)               ((n) & 0x1f)
+#define ST7789V_PVGAMCTRL_VP6(n)               ((n) & 0x1f)
+#define ST7789V_PVGAMCTRL_VP13(n)              ((n) & 0xf)
+#define ST7789V_PVGAMCTRL_VP20(n)              ((n) & 0x7f)
+#define ST7789V_PVGAMCTRL_VP27(n)              ((n) & 7)
+#define ST7789V_PVGAMCTRL_VP36(n)              (((n) & 7) << 4)
+#define ST7789V_PVGAMCTRL_VP43(n)              ((n) & 0x7f)
+#define ST7789V_PVGAMCTRL_VP50(n)              ((n) & 0xf)
+#define ST7789V_PVGAMCTRL_VP57(n)              ((n) & 0x1f)
+#define ST7789V_PVGAMCTRL_VP59(n)              ((n) & 0x1f)
+#define ST7789V_PVGAMCTRL_VP61(n)              ((n) & 0x3f)
+#define ST7789V_PVGAMCTRL_VP62(n)              ((n) & 0x3f)
+#define ST7789V_PVGAMCTRL_VP63(n)              (((n) & 0xf) << 4)
+
+#define ST7789V_NVGAMCTRL_CMD          0xe1
+#define ST7789V_NVGAMCTRL_JN0(n)               (((n) & 3) << 4)
+#define ST7789V_NVGAMCTRL_JN1(n)               (((n) & 3) << 4)
+#define ST7789V_NVGAMCTRL_VN0(n)               ((n) & 0xf)
+#define ST7789V_NVGAMCTRL_VN1(n)               ((n) & 0x3f)
+#define ST7789V_NVGAMCTRL_VN2(n)               ((n) & 0x3f)
+#define ST7789V_NVGAMCTRL_VN4(n)               ((n) & 0x1f)
+#define ST7789V_NVGAMCTRL_VN6(n)               ((n) & 0x1f)
+#define ST7789V_NVGAMCTRL_VN13(n)              ((n) & 0xf)
+#define ST7789V_NVGAMCTRL_VN20(n)              ((n) & 0x7f)
+#define ST7789V_NVGAMCTRL_VN27(n)              ((n) & 7)
+#define ST7789V_NVGAMCTRL_VN36(n)              (((n) & 7) << 4)
+#define ST7789V_NVGAMCTRL_VN43(n)              ((n) & 0x7f)
+#define ST7789V_NVGAMCTRL_VN50(n)              ((n) & 0xf)
+#define ST7789V_NVGAMCTRL_VN57(n)              ((n) & 0x1f)
+#define ST7789V_NVGAMCTRL_VN59(n)              ((n) & 0x1f)
+#define ST7789V_NVGAMCTRL_VN61(n)              ((n) & 0x3f)
+#define ST7789V_NVGAMCTRL_VN62(n)              ((n) & 0x3f)
+#define ST7789V_NVGAMCTRL_VN63(n)              (((n) & 0xf) << 4)
+
+#define ST7789V_TEST(val, func)                        \
+       do {                                    \
+               if ((val = (func)))             \
+                       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;
+       u32 bus_flags;
+       bool invert_mode;
+       bool partial_mode;
+       u16 partial_start;
+       u16 partial_end;
+};
+
+struct st7789v {
+       const struct st7789_panel_info *info;
+       struct spi_device *spi;
+       struct gpio_desc *reset;
+       struct regulator *power;
+       enum drm_panel_orientation orientation;
+       struct device *dev;
+       struct vpl vpl;
+};
+
+enum st7789v_prefix {
+       ST7789V_COMMAND = 0,
+       ST7789V_DATA = 1,
+};
+
+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 st7789v *ctx)
+{
+       const u8 st7789v_ids[ST7789V_IDS_SIZE] = ST7789V_IDS;
+       bool invalid_ids = false;
+       int ret, i;
+       u8 ids[3];
+
+       ret = st7789v_read_data(ctx, MIPI_DCS_GET_DISPLAY_ID, ids, 
ST7789V_IDS_SIZE);
+       if (ret)
+               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,
+       .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 = 61,
+       .height_mm = 103,
+       .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 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 drm_display_mode jt240mhqs_hwt_ek_e3_mode = {
+       .clock = 6000,
+       .hdisplay = 240,
+       .hsync_start = 240 + 28,
+       .hsync_end = 240 + 28 + 10,
+       .htotal = 240 + 28 + 10 + 10,
+       .vdisplay = 280,
+       .vsync_start = 280 + 8,
+       .vsync_end = 280 + 8 + 4,
+       .vtotal = 280 + 8 + 4 + 4,
+       .width_mm = 43,
+       .height_mm = 37,
+       .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 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 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 const struct st7789_panel_info jt240mhqs_hwt_ek_e3_panel = {
+       .mode = &jt240mhqs_hwt_ek_e3_mode,
+       .invert_mode = true,
+       .bus_format = MEDIA_BUS_FMT_RGB666_1X18,
+       .bus_flags = DRM_BUS_FLAG_DE_HIGH |
+                    DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE,
+       .partial_mode = true,
+       .partial_start = 38,
+       .partial_end = 318,
+};
+
+static int st7789v_get_modes(struct st7789v *ctx,
+                            struct display_timings *timings)
+{
+       struct fb_videomode *mode;
+
+       mode = xzalloc(sizeof(*mode));
+
+       drm_display_mode_to_fb_videomode(ctx->info->mode, mode);
+
+       timings->modes = mode;
+       timings->num_modes = 1;
+
+       return 0;
+}
+
+static int st7789v_get_display_info(struct st7789v *ctx,
+                            struct drm_display_info *display_info)
+{
+       display_info->bus_flags = ctx->info->bus_flags;
+       display_info->panel_orientation = ctx->orientation;
+
+       return 0;
+}
+
+static int st7789v_prepare(struct st7789v *ctx)
+{
+       u8 mode, pixel_fmt, polarity;
+       int ret;
+
+       if (!ctx->info->partial_mode)
+               mode = ST7789V_RGBCTRL_WO;
+       else
+               mode = 0;
+
+       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(ctx->dev, "unsupported bus format: %d\n",
+                       ctx->info->bus_format);
+               return -EINVAL;
+       }
+
+       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_FALLING;
+       if (ctx->info->bus_flags & DRM_BUS_FLAG_DE_LOW)
+               polarity |= ST7789V_RGBCTRL_DE_LOW;
+
+       ret = regulator_enable(ctx->power);
+       if (ret)
+               return ret;
+
+       gpiod_set_value(ctx->reset, 1);
+       mdelay(30);
+       gpiod_set_value(ctx->reset, 0);
+       mdelay(120);
+
+       /*
+        * Avoid failing if the IDs are invalid in case the Rx bus width
+        * description is missing.
+        */
+       ret = st7789v_check_id(ctx);
+       if (ret)
+               dev_warn(ctx->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 */
+       mdelay(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)));
+
+       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));
+       }
+
+       if (ctx->info->partial_mode) {
+               u8 area_data[4] = {
+                       (ctx->info->partial_start >> 8) & 0xff,
+                       (ctx->info->partial_start >> 0) & 0xff,
+                       ((ctx->info->partial_end - 1) >> 8) & 0xff,
+                       ((ctx->info->partial_end - 1) >> 0) & 0xff,
+               };
+
+               /* Caution: if userspace ever pushes a mode different from the
+                * expected one (i.e., the one advertised by get_modes), we'll
+                * 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, 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, 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)));
+
+       return 0;
+}
+
+static int st7789v_enable(struct st7789v *ctx)
+{
+       return st7789v_write_command(ctx, MIPI_DCS_SET_DISPLAY_ON);
+}
+
+static int st7789v_disable(struct st7789v *ctx)
+{
+       int ret;
+
+       ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_SET_DISPLAY_OFF));
+
+       return 0;
+}
+
+static int st7789v_unprepare(struct st7789v *ctx)
+{
+       int ret;
+
+       ST7789V_TEST(ret, st7789v_write_command(ctx, 
MIPI_DCS_ENTER_SLEEP_MODE));
+
+       regulator_disable(ctx->power);
+
+       return 0;
+}
+
+static int ili9341_ioctl(struct vpl *vpl, unsigned int port,
+                        unsigned int cmd, void *ptr)
+{       
+       struct st7789v *ctx = container_of(vpl, struct st7789v, vpl);
+
+       switch (cmd) {
+       case VPL_PREPARE:
+               return st7789v_prepare(ctx);
+       case VPL_ENABLE:
+               return st7789v_enable(ctx);
+       case VPL_DISABLE:
+               return st7789v_disable(ctx);
+       case VPL_UNPREPARE:
+               return st7789v_unprepare(ctx);
+       case VPL_GET_VIDEOMODES:
+               return st7789v_get_modes(ctx, ptr);
+       case VPL_GET_BUS_FORMAT:
+               *(u32 *)ptr = ctx->info->bus_format;
+               return 0;
+       case VPL_GET_DISPLAY_INFO:
+               return st7789v_get_display_info(ctx, ptr);
+       default:
+               return 0;
+       }
+}
+
+#define spi_setup(spi) spi->master->setup(spi)
+
+static int st7789v_probe(struct device *dev)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       struct st7789v *ctx;
+       int ret;
+
+       ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       ctx->spi = spi;
+       ctx->dev = dev;
+
+       spi->bits_per_word = 9;
+       ret = spi_setup(spi);
+       if (ret < 0)
+               return dev_err_probe(dev, ret, "Failed to setup spi\n");
+
+       ctx->info = device_get_match_data(&spi->dev);
+
+       ctx->power = 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 = 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");
+
+       ctx->vpl.node = dev->of_node;
+       ctx->vpl.ioctl = ili9341_ioctl;
+
+       return vpl_register(&ctx->vpl);
+}
+
+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 },
+       { .compatible = "jasonic,jt240mhqs-hwt-ek-e3",
+         .data = &jt240mhqs_hwt_ek_e3_panel },
+       { }
+};
+MODULE_DEVICE_TABLE(of, st7789v_of_match);
+
+static struct driver st7789v_driver = {
+       .name = "panel-sitronix-st7789v",
+       .of_compatible = st7789v_of_match,
+       .probe = st7789v_probe,
+};
+device_spi_driver(st7789v_driver);
+
+MODULE_AUTHOR("Maxime Ripard <maxime.rip...@free-electrons.com>");
+MODULE_DESCRIPTION("Sitronix st7789v LCD Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/video/vpl.h b/include/video/vpl.h
index 15711b4701..5ea3d6dcf2 100644
--- a/include/video/vpl.h
+++ b/include/video/vpl.h
@@ -11,6 +11,7 @@
 #define VPL_DISABLE            0x67660004
 #define VPL_GET_VIDEOMODES     0x67660005
 #define VPL_GET_BUS_FORMAT     0x67660006
+#define VPL_GET_DISPLAY_INFO   0x67660007
 
 struct vpl {
        int (*ioctl)(struct vpl *, unsigned int port,

-- 
2.39.5


Reply via email to