drm/sitronix: move tiny Sitronix drivers to their own subdir
authorMarcus Folkesson <marcus.folkesson@gmail.com>
Mon, 12 May 2025 07:15:11 +0000 (09:15 +0200)
committerJavier Martinez Canillas <javierm@redhat.com>
Mon, 12 May 2025 08:26:40 +0000 (10:26 +0200)
We start to have support many Sitronix displays in the tiny directory,
and we expect more to come.

Move them to their own subdirectory.

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Suggested-by: Javier Martinez Canillas <javierm@redhat.com>
Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/r/20250512-sitronix-v3-1-bbf6cc413698@gmail.com
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
13 files changed:
MAINTAINERS
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/sitronix/Kconfig [new file with mode: 0644]
drivers/gpu/drm/sitronix/Makefile [new file with mode: 0644]
drivers/gpu/drm/sitronix/st7571-i2c.c [new file with mode: 0644]
drivers/gpu/drm/sitronix/st7586.c [new file with mode: 0644]
drivers/gpu/drm/sitronix/st7735r.c [new file with mode: 0644]
drivers/gpu/drm/tiny/Kconfig
drivers/gpu/drm/tiny/Makefile
drivers/gpu/drm/tiny/st7571-i2c.c [deleted file]
drivers/gpu/drm/tiny/st7586.c [deleted file]
drivers/gpu/drm/tiny/st7735r.c [deleted file]

index bcc4944a69916182d5001ba0bebb6fe32134a9ce..fe9773af465a867987dae6277d71820c0a7011a3 100644 (file)
@@ -7695,13 +7695,13 @@ M:      David Lechner <david@lechnology.com>
 S:     Maintained
 T:     git https://gitlab.freedesktop.org/drm/misc/kernel.git
 F:     Documentation/devicetree/bindings/display/sitronix,st7586.txt
-F:     drivers/gpu/drm/tiny/st7586.c
+F:     drivers/gpu/drm/sitronix/st7586.c
 
 DRM DRIVER FOR SITRONIX ST7571 PANELS
 M:     Marcus Folkesson <marcus.folkesson@gmail.com>
 S:     Maintained
 F:     Documentation/devicetree/bindings/display/sitronix,st7571.yaml
-F:     drivers/gpu/drm/tiny/st7571-i2c.c
+F:     drivers/gpu/drm/sitronix/st7571-i2c.c
 
 DRM DRIVER FOR SITRONIX ST7701 PANELS
 M:     Jagan Teki <jagan@amarulasolutions.com>
@@ -7722,7 +7722,7 @@ M:        David Lechner <david@lechnology.com>
 S:     Maintained
 T:     git https://gitlab.freedesktop.org/drm/misc/kernel.git
 F:     Documentation/devicetree/bindings/display/sitronix,st7735r.yaml
-F:     drivers/gpu/drm/tiny/st7735r.c
+F:     drivers/gpu/drm/sitronix/st7735r.c
 
 DRM DRIVER FOR SOLOMON SSD130X OLED DISPLAYS
 M:     Javier Martinez Canillas <javierm@redhat.com>
index 5088698182d34e11b30ffacbffd8a6612fc0967b..9488fc01bca3a689f207b8ba75fcb142cc643463 100644 (file)
@@ -385,6 +385,8 @@ source "drivers/gpu/drm/xlnx/Kconfig"
 
 source "drivers/gpu/drm/gud/Kconfig"
 
+source "drivers/gpu/drm/sitronix/Kconfig"
+
 source "drivers/gpu/drm/solomon/Kconfig"
 
 source "drivers/gpu/drm/sprd/Kconfig"
index b5d5561bbe5fd72f3915e6a52f325fdb79c7981e..70510620f29c874e376c795fb05d426a0faae05c 100644 (file)
@@ -221,6 +221,7 @@ obj-$(CONFIG_DRM_TIDSS) += tidss/
 obj-y                  += xlnx/
 obj-y                  += gud/
 obj-$(CONFIG_DRM_HYPERV) += hyperv/
+obj-y                  += sitronix/
 obj-y                  += solomon/
 obj-$(CONFIG_DRM_SPRD) += sprd/
 obj-$(CONFIG_DRM_LOONGSON) += loongson/
diff --git a/drivers/gpu/drm/sitronix/Kconfig b/drivers/gpu/drm/sitronix/Kconfig
new file mode 100644 (file)
index 0000000..c069d0d
--- /dev/null
@@ -0,0 +1,51 @@
+config DRM_ST7571_I2C
+       tristate "DRM support for Sitronix ST7571 display panels (I2C)"
+       depends on DRM && I2C && MMU
+       select DRM_CLIENT_SELECTION
+       select DRM_GEM_SHMEM_HELPER
+       select DRM_KMS_HELPER
+       select REGMAP_I2C
+       help
+         DRM driver for Sitronix ST7571 panels controlled over I2C.
+
+         if M is selected the module will be called st7571-i2c.
+
+config TINYDRM_ST7586
+       tristate
+       default n
+
+config DRM_ST7586
+       tristate "DRM support for Sitronix ST7586 display panels"
+       depends on DRM && SPI
+       select DRM_CLIENT_SELECTION
+       select DRM_KMS_HELPER
+       select DRM_GEM_DMA_HELPER
+       select DRM_MIPI_DBI
+       default TINYDRM_ST7586
+       help
+         DRM driver for the following Sitronix ST7586 panels:
+         * LEGO MINDSTORMS EV3
+
+         If M is selected the module will be called st7586.
+
+config TINYDRM_ST7735R
+       tristate
+       default n
+
+config DRM_ST7735R
+       tristate "DRM support for Sitronix ST7715R/ST7735R display panels"
+       depends on DRM && SPI
+       select DRM_CLIENT_SELECTION
+       select DRM_KMS_HELPER
+       select DRM_GEM_DMA_HELPER
+       select DRM_MIPI_DBI
+       select BACKLIGHT_CLASS_DEVICE
+       default TINYDRM_ST7735R
+       help
+         DRM driver for Sitronix ST7715R/ST7735R with one of the following
+         LCDs:
+         * Jianda JD-T18003-T01 1.8" 128x160 TFT
+         * Okaya RH128128T 1.44" 128x128 TFT
+
+         If M is selected the module will be called st7735r.
+
diff --git a/drivers/gpu/drm/sitronix/Makefile b/drivers/gpu/drm/sitronix/Makefile
new file mode 100644 (file)
index 0000000..bd139e5
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DRM_ST7571_I2C)           += st7571-i2c.o
+obj-$(CONFIG_DRM_ST7586)               += st7586.o
+obj-$(CONFIG_DRM_ST7735R)              += st7735r.o
diff --git a/drivers/gpu/drm/sitronix/st7571-i2c.c b/drivers/gpu/drm/sitronix/st7571-i2c.c
new file mode 100644 (file)
index 0000000..eec8468
--- /dev/null
@@ -0,0 +1,1000 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Sitronix ST7571, a 4 level gray scale dot matrix LCD controller
+ *
+ * Copyright (C) 2025 Marcus Folkesson <marcus.folkesson@gmail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include <drm/clients/drm_client_setup.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fbdev_shmem.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_module.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_probe_helper.h>
+
+#include <video/display_timing.h>
+#include <video/of_display_timing.h>
+
+#define ST7571_COMMAND_MODE                    (0x00)
+#define ST7571_DATA_MODE                       (0x40)
+
+/* Normal mode command set */
+#define ST7571_DISPLAY_OFF                     (0xae)
+#define ST7571_DISPLAY_ON                      (0xaf)
+#define ST7571_OSC_ON                          (0xab)
+#define ST7571_SET_COLUMN_LSB(c)               (0x00 | FIELD_PREP(GENMASK(3, 0), (c)))
+#define ST7571_SET_COLUMN_MSB(c)               (0x10 | FIELD_PREP(GENMASK(2, 0), (c) >> 4))
+#define ST7571_SET_COM0_LSB(x)                 (FIELD_PREP(GENMASK(6, 0), (x)))
+#define ST7571_SET_COM0_MSB                    (0x44)
+#define ST7571_SET_COM_SCAN_DIR(d)             (0xc0 | FIELD_PREP(GENMASK(3, 3), (d)))
+#define ST7571_SET_CONTRAST_LSB(c)             (FIELD_PREP(GENMASK(5, 0), (c)))
+#define ST7571_SET_CONTRAST_MSB                        (0x81)
+#define ST7571_SET_DISPLAY_DUTY_LSB(d)         (FIELD_PREP(GENMASK(7, 0), (d)))
+#define ST7571_SET_DISPLAY_DUTY_MSB            (0x48)
+#define ST7571_SET_ENTIRE_DISPLAY_ON(p)                (0xa4 | FIELD_PREP(GENMASK(0, 0), (p)))
+#define ST7571_SET_LCD_BIAS(b)                 (0x50 | FIELD_PREP(GENMASK(2, 0), (b)))
+#define ST7571_SET_MODE_LSB(m)                 (FIELD_PREP(GENMASK(7, 2), (m)))
+#define ST7571_SET_MODE_MSB                    (0x38)
+#define ST7571_SET_PAGE(p)                     (0xb0 | FIELD_PREP(GENMASK(3, 0), (p)))
+#define ST7571_SET_POWER(p)                    (0x28 | FIELD_PREP(GENMASK(2, 0), (p)))
+#define ST7571_SET_REGULATOR_REG(r)            (0x20 | FIELD_PREP(GENMASK(2, 0), (r)))
+#define ST7571_SET_REVERSE(r)                  (0xa6 | FIELD_PREP(GENMASK(0, 0), (r)))
+#define ST7571_SET_SEG_SCAN_DIR(d)             (0xa0 | FIELD_PREP(GENMASK(0, 0), (d)))
+#define ST7571_SET_START_LINE_LSB(l)           (FIELD_PREP(GENMASK(6, 0), (l)))
+#define ST7571_SET_START_LINE_MSB              (0x40)
+
+/* Extension command set 3 */
+#define ST7571_COMMAND_SET_3                   (0x7b)
+#define ST7571_SET_COLOR_MODE(c)               (0x10 | FIELD_PREP(GENMASK(0, 0), (c)))
+#define ST7571_COMMAND_SET_NORMAL              (0x00)
+
+#define ST7571_PAGE_HEIGHT 8
+
+#define DRIVER_NAME "st7571"
+#define DRIVER_DESC "ST7571 DRM driver"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+
+enum st7571_color_mode {
+       ST7571_COLOR_MODE_GRAY = 0,
+       ST7571_COLOR_MODE_BLACKWHITE = 1,
+};
+
+struct st7571_device;
+
+struct st7571_panel_constraints {
+       u32 min_nlines;
+       u32 max_nlines;
+       u32 min_ncols;
+       u32 max_ncols;
+       bool support_grayscale;
+};
+
+struct st7571_panel_data {
+       int (*init)(struct st7571_device *st7571);
+       struct st7571_panel_constraints constraints;
+};
+
+struct st7571_panel_format {
+       void (*prepare_buffer)(struct st7571_device *st7571,
+                              const struct iosys_map *vmap,
+                              struct drm_framebuffer *fb,
+                              struct drm_rect *rect,
+                              struct drm_format_conv_state *fmtcnv_state);
+       int (*update_rect)(struct drm_framebuffer *fb, struct drm_rect *rect);
+       enum st7571_color_mode mode;
+       const u8 nformats;
+       const u32 formats[];
+};
+
+struct st7571_device {
+       struct drm_device dev;
+
+       struct drm_plane primary_plane;
+       struct drm_crtc crtc;
+       struct drm_encoder encoder;
+       struct drm_connector connector;
+
+       struct drm_display_mode mode;
+
+       const struct st7571_panel_format *pformat;
+       const struct st7571_panel_data *pdata;
+       struct i2c_client *client;
+       struct gpio_desc *reset;
+       struct regmap *regmap;
+
+       /*
+        * Depending on the hardware design, the acknowledge signal may be hard to
+        * recognize as a valid logic "0" level.
+        * Therefor, ignore NAK if possible to stay compatible with most hardware designs
+        * and off-the-shelf panels out there.
+        *
+        * From section 6.4 MICROPOCESSOR INTERFACE section in the datasheet:
+        *
+        * "By connecting SDA_OUT to SDA_IN externally, the SDA line becomes fully
+        * I2C interface compatible.
+        * Separating acknowledge-output from serial data
+        * input is advantageous for chip-on-glass (COG) applications. In COG
+        * applications, the ITO resistance and the pull-up resistor will form a
+        * voltage  divider, which affects acknowledge-signal level. Larger ITO
+        * resistance will raise the acknowledged-signal level and system cannot
+        * recognize this level as a valid logic “0” level. By separating SDA_IN from
+        * SDA_OUT, the IC can be used in a mode that ignores the acknowledge-bit.
+        * For applications which check acknowledge-bit, it is necessary to minimize
+        * the ITO resistance of the SDA_OUT trace to guarantee a valid low level."
+        *
+        */
+       bool ignore_nak;
+
+       bool grayscale;
+       u32 height_mm;
+       u32 width_mm;
+       u32 startline;
+       u32 nlines;
+       u32 ncols;
+       u32 bpp;
+
+       /* Intermediate buffer in LCD friendly format */
+       u8 *hwbuf;
+
+       /* Row of (transformed) pixels ready to be written to the display */
+       u8 *row;
+};
+
+static inline struct st7571_device *drm_to_st7571(struct drm_device *dev)
+{
+       return container_of(dev, struct st7571_device, dev);
+}
+
+static int st7571_regmap_write(void *context, const void *data, size_t count)
+{
+       struct i2c_client *client = context;
+       struct st7571_device *st7571 = i2c_get_clientdata(client);
+       int ret;
+
+       struct i2c_msg msg = {
+               .addr = st7571->client->addr,
+               .flags = st7571->ignore_nak ? I2C_M_IGNORE_NAK : 0,
+               .len = count,
+               .buf = (u8 *)data
+       };
+
+       ret = i2c_transfer(st7571->client->adapter, &msg, 1);
+
+       /*
+        * Unfortunately, there is no way to check if the transfer failed because of
+        * a NAK or something else as I2C bus drivers use different return values for NAK.
+        *
+        * However, if the transfer fails and ignore_nak is set, we know it is an error.
+        */
+       if (ret < 0 && st7571->ignore_nak)
+               return ret;
+
+       return 0;
+}
+
+/* The st7571 driver does not read registers but regmap expects a .read */
+static int st7571_regmap_read(void *context, const void *reg_buf,
+                             size_t reg_size, void *val_buf, size_t val_size)
+{
+       return -EOPNOTSUPP;
+}
+
+static int st7571_send_command_list(struct st7571_device *st7571,
+                                   const u8 *cmd_list, size_t len)
+{
+       int ret;
+
+       for (int i = 0; i < len; i++) {
+               ret = regmap_write(st7571->regmap, ST7571_COMMAND_MODE, cmd_list[i]);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ret;
+}
+
+static inline u8 st7571_transform_xy(const char *p, int x, int y)
+{
+       int xrest = x % 8;
+       u8 result = 0;
+
+       /*
+        * Transforms an (x, y) pixel coordinate into a vertical 8-bit
+        * column from the framebuffer. It calculates the corresponding byte in the
+        * framebuffer, extracts the bit at the given x position across 8 consecutive
+        * rows, and packs those bits into a single byte.
+        *
+        * Return an 8-bit value representing a vertical column of pixels.
+        */
+       x = x / 8;
+       y = (y / 8) * 8;
+
+       for (int i = 0; i < 8; i++) {
+               int row_idx = y + i;
+               u8 byte = p[row_idx * 16 + x];
+               u8 bit = (byte >> xrest) & 1;
+
+               result |= (bit << i);
+       }
+
+       return result;
+}
+
+static int st7571_set_position(struct st7571_device *st7571, int x, int y)
+{
+       u8 cmd_list[] = {
+               ST7571_SET_COLUMN_LSB(x),
+               ST7571_SET_COLUMN_MSB(x),
+               ST7571_SET_PAGE(y / ST7571_PAGE_HEIGHT),
+       };
+
+       return st7571_send_command_list(st7571, cmd_list, ARRAY_SIZE(cmd_list));
+}
+
+static int st7571_fb_clear_screen(struct st7571_device *st7571)
+{
+       u32 npixels = st7571->ncols * round_up(st7571->nlines, ST7571_PAGE_HEIGHT) * st7571->bpp;
+       char pixelvalue = 0x00;
+
+       for (int i = 0; i < npixels; i++)
+               regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, &pixelvalue, 1);
+
+       return 0;
+}
+
+static void st7571_prepare_buffer_monochrome(struct st7571_device *st7571,
+                                            const struct iosys_map *vmap,
+                                            struct drm_framebuffer *fb,
+                                            struct drm_rect *rect,
+                                            struct drm_format_conv_state *fmtcnv_state)
+{
+       unsigned int dst_pitch;
+       struct iosys_map dst;
+       u32 size;
+
+       switch (fb->format->format) {
+       case DRM_FORMAT_XRGB8888:
+               dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8);
+               iosys_map_set_vaddr(&dst, st7571->hwbuf);
+
+               drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
+               break;
+
+       case DRM_FORMAT_R1:
+               size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
+               memcpy(st7571->hwbuf, vmap->vaddr, size);
+               break;
+       }
+}
+
+static void st7571_prepare_buffer_grayscale(struct st7571_device *st7571,
+                                           const struct iosys_map *vmap,
+                                           struct drm_framebuffer *fb,
+                                           struct drm_rect *rect,
+                                           struct drm_format_conv_state *fmtcnv_state)
+{
+       u32 size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
+       unsigned int dst_pitch;
+       struct iosys_map dst;
+
+       switch (fb->format->format) {
+       case DRM_FORMAT_XRGB8888: /* Only support XRGB8888 in monochrome mode */
+               dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8);
+               iosys_map_set_vaddr(&dst, st7571->hwbuf);
+
+               drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
+               break;
+
+       case DRM_FORMAT_R1:
+               size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
+               memcpy(st7571->hwbuf, vmap->vaddr, size);
+               break;
+
+       case DRM_FORMAT_R2:
+               size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 4;
+               memcpy(st7571->hwbuf, vmap->vaddr, size);
+               break;
+       };
+}
+
+static int st7571_fb_update_rect_monochrome(struct drm_framebuffer *fb, struct drm_rect *rect)
+{
+       struct st7571_device *st7571 = drm_to_st7571(fb->dev);
+       char *row = st7571->row;
+
+       /* Align y to display page boundaries */
+       rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT);
+       rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines);
+
+       for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) {
+               for (int x = rect->x1; x < rect->x2; x++)
+                       row[x] = st7571_transform_xy(st7571->hwbuf, x, y);
+
+               st7571_set_position(st7571, rect->x1, y);
+
+               /* TODO: Investige why we can't write multiple bytes at once */
+               for (int x = rect->x1; x < rect->x2; x++)
+                       regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
+       }
+
+       return 0;
+}
+
+static int st7571_fb_update_rect_grayscale(struct drm_framebuffer *fb, struct drm_rect *rect)
+{
+       struct st7571_device *st7571 = drm_to_st7571(fb->dev);
+       u32 format = fb->format->format;
+       char *row = st7571->row;
+       int x1;
+       int x2;
+
+       /* Align y to display page boundaries */
+       rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT);
+       rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines);
+
+       switch (format) {
+       case DRM_FORMAT_XRGB8888:
+               /* Threated as monochrome (R1) */
+               fallthrough;
+       case DRM_FORMAT_R1:
+               x1 = rect->x1;
+               x2 = rect->x2;
+               break;
+       case DRM_FORMAT_R2:
+               x1 = rect->x1 * 2;
+               x2 = rect->x2 * 2;
+               break;
+       }
+
+       for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) {
+               for (int x = x1; x < x2; x++)
+                       row[x] = st7571_transform_xy(st7571->hwbuf, x, y);
+
+               st7571_set_position(st7571, rect->x1, y);
+
+               /* TODO: Investige why we can't write multiple bytes at once */
+               for (int x = x1; x < x2; x++) {
+                       regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
+
+                       /*
+                        * As the display supports grayscale, all pixels must be written as two bits
+                        * even if the format is monochrome.
+                        *
+                        * The bit values maps to the following grayscale:
+                        * 0 0 = White
+                        * 0 1 = Light gray
+                        * 1 0 = Dark gray
+                        * 1 1 = Black
+                        *
+                        * For monochrome formats, write the same value twice to get
+                        * either a black or white pixel.
+                        */
+                       if (format == DRM_FORMAT_R1 || format == DRM_FORMAT_XRGB8888)
+                               regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
+               }
+       }
+
+       return 0;
+}
+
+static int st7571_connector_get_modes(struct drm_connector *conn)
+{
+       struct st7571_device *st7571 = drm_to_st7571(conn->dev);
+
+       return drm_connector_helper_get_modes_fixed(conn, &st7571->mode);
+}
+
+static const struct drm_connector_helper_funcs st7571_connector_helper_funcs = {
+       .get_modes = st7571_connector_get_modes,
+};
+
+static const struct st7571_panel_format st7571_monochrome = {
+       .prepare_buffer = st7571_prepare_buffer_monochrome,
+       .update_rect = st7571_fb_update_rect_monochrome,
+       .mode = ST7571_COLOR_MODE_BLACKWHITE,
+       .formats = {
+               DRM_FORMAT_XRGB8888,
+               DRM_FORMAT_R1,
+       },
+       .nformats = 2,
+};
+
+static const struct st7571_panel_format st7571_grayscale = {
+       .prepare_buffer = st7571_prepare_buffer_grayscale,
+       .update_rect = st7571_fb_update_rect_grayscale,
+       .mode = ST7571_COLOR_MODE_GRAY,
+       .formats = {
+               DRM_FORMAT_XRGB8888,
+               DRM_FORMAT_R1,
+               DRM_FORMAT_R2,
+       },
+       .nformats = 3,
+};
+
+static const u64 st7571_primary_plane_fmtmods[] = {
+       DRM_FORMAT_MOD_LINEAR,
+       DRM_FORMAT_MOD_INVALID
+};
+
+static int st7571_primary_plane_helper_atomic_check(struct drm_plane *plane,
+                                                   struct drm_atomic_state *state)
+{
+       struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
+       struct drm_crtc *new_crtc = new_plane_state->crtc;
+       struct drm_crtc_state *new_crtc_state = NULL;
+
+       if (new_crtc)
+               new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
+
+       return drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
+                                                  DRM_PLANE_NO_SCALING,
+                                                  DRM_PLANE_NO_SCALING,
+                                                  false, false);
+}
+
+static void st7571_primary_plane_helper_atomic_update(struct drm_plane *plane,
+                                                     struct drm_atomic_state *state)
+{
+       struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
+       struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
+       struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
+       struct drm_framebuffer *fb = plane_state->fb;
+       struct drm_atomic_helper_damage_iter iter;
+       struct drm_device *dev = plane->dev;
+       struct drm_rect damage;
+       struct st7571_device *st7571 = drm_to_st7571(plane->dev);
+       int ret, idx;
+
+       if (!fb)
+               return; /* no framebuffer; plane is disabled */
+
+       ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
+       if (ret)
+               return;
+
+       if (!drm_dev_enter(dev, &idx))
+               goto out_drm_gem_fb_end_cpu_access;
+
+       drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
+       drm_atomic_for_each_plane_damage(&iter, &damage) {
+               st7571->pformat->prepare_buffer(st7571,
+                                               &shadow_plane_state->data[0],
+                                               fb, &damage,
+                                               &shadow_plane_state->fmtcnv_state);
+
+               st7571->pformat->update_rect(fb, &damage);
+       }
+
+       drm_dev_exit(idx);
+
+out_drm_gem_fb_end_cpu_access:
+       drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
+}
+
+static void st7571_primary_plane_helper_atomic_disable(struct drm_plane *plane,
+                                                      struct drm_atomic_state *state)
+{
+       struct drm_device *dev = plane->dev;
+       struct st7571_device *st7571 = drm_to_st7571(plane->dev);
+       int idx;
+
+       if (!drm_dev_enter(dev, &idx))
+               return;
+
+       st7571_fb_clear_screen(st7571);
+       drm_dev_exit(idx);
+}
+
+static const struct drm_plane_helper_funcs st7571_primary_plane_helper_funcs = {
+       DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
+       .atomic_check = st7571_primary_plane_helper_atomic_check,
+       .atomic_update = st7571_primary_plane_helper_atomic_update,
+       .atomic_disable = st7571_primary_plane_helper_atomic_disable,
+};
+
+static const struct drm_plane_funcs st7571_primary_plane_funcs = {
+       .update_plane = drm_atomic_helper_update_plane,
+       .disable_plane = drm_atomic_helper_disable_plane,
+       .destroy = drm_plane_cleanup,
+       DRM_GEM_SHADOW_PLANE_FUNCS,
+};
+
+/*
+ * CRTC
+ */
+
+static enum drm_mode_status st7571_crtc_mode_valid(struct drm_crtc *crtc,
+                                                  const struct drm_display_mode *mode)
+{
+       struct st7571_device *st7571 = drm_to_st7571(crtc->dev);
+
+       return drm_crtc_helper_mode_valid_fixed(crtc, mode, &st7571->mode);
+}
+
+static const struct drm_crtc_helper_funcs st7571_crtc_helper_funcs = {
+       .atomic_check = drm_crtc_helper_atomic_check,
+       .mode_valid = st7571_crtc_mode_valid,
+};
+
+static const struct drm_crtc_funcs st7571_crtc_funcs = {
+       .reset = drm_atomic_helper_crtc_reset,
+       .destroy = drm_crtc_cleanup,
+       .set_config = drm_atomic_helper_set_config,
+       .page_flip = drm_atomic_helper_page_flip,
+       .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+/*
+ * Encoder
+ */
+
+static void ssd130x_encoder_atomic_enable(struct drm_encoder *encoder,
+                                         struct drm_atomic_state *state)
+{
+       struct drm_device *drm = encoder->dev;
+       struct st7571_device *st7571 = drm_to_st7571(drm);
+       u8 command = ST7571_DISPLAY_ON;
+       int ret;
+
+       ret = st7571->pdata->init(st7571);
+       if (ret)
+               return;
+
+       st7571_send_command_list(st7571, &command, 1);
+}
+
+static void ssd130x_encoder_atomic_disable(struct drm_encoder *encoder,
+                                          struct drm_atomic_state *state)
+{
+       struct drm_device *drm = encoder->dev;
+       struct st7571_device *st7571 = drm_to_st7571(drm);
+       u8 command = ST7571_DISPLAY_OFF;
+
+       st7571_send_command_list(st7571, &command, 1);
+}
+
+static const struct drm_encoder_funcs st7571_encoder_funcs = {
+       .destroy = drm_encoder_cleanup,
+
+};
+
+static const struct drm_encoder_helper_funcs st7571_encoder_helper_funcs = {
+       .atomic_enable = ssd130x_encoder_atomic_enable,
+       .atomic_disable = ssd130x_encoder_atomic_disable,
+};
+
+/*
+ * Connector
+ */
+
+static const struct drm_connector_funcs st7571_connector_funcs = {
+       .reset = drm_atomic_helper_connector_reset,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .destroy = drm_connector_cleanup,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static const struct drm_mode_config_funcs st7571_mode_config_funcs = {
+       .fb_create = drm_gem_fb_create_with_dirty,
+       .atomic_check = drm_atomic_helper_check,
+       .atomic_commit = drm_atomic_helper_commit,
+};
+
+static struct drm_display_mode st7571_mode(struct st7571_device *st7571)
+{
+       struct drm_display_mode mode = {
+               DRM_SIMPLE_MODE(st7571->ncols, st7571->nlines,
+                               st7571->width_mm, st7571->height_mm),
+       };
+
+       return mode;
+}
+
+static int st7571_mode_config_init(struct st7571_device *st7571)
+{
+       struct drm_device *dev = &st7571->dev;
+       const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints;
+       int ret;
+
+       ret = drmm_mode_config_init(dev);
+       if (ret)
+               return ret;
+
+       dev->mode_config.min_width = constraints->min_ncols;
+       dev->mode_config.min_height = constraints->min_nlines;
+       dev->mode_config.max_width = constraints->max_ncols;
+       dev->mode_config.max_height = constraints->max_nlines;
+       dev->mode_config.preferred_depth = 24;
+       dev->mode_config.funcs = &st7571_mode_config_funcs;
+
+       return 0;
+}
+
+static int st7571_plane_init(struct st7571_device *st7571,
+                            const struct st7571_panel_format *pformat)
+{
+       struct drm_plane *primary_plane = &st7571->primary_plane;
+       struct drm_device *dev = &st7571->dev;
+       int ret;
+
+       ret = drm_universal_plane_init(dev, primary_plane, 0,
+                                      &st7571_primary_plane_funcs,
+                                      pformat->formats,
+                                      pformat->nformats,
+                                      st7571_primary_plane_fmtmods,
+                                      DRM_PLANE_TYPE_PRIMARY, NULL);
+       if (ret)
+               return ret;
+
+       drm_plane_helper_add(primary_plane, &st7571_primary_plane_helper_funcs);
+       drm_plane_enable_fb_damage_clips(primary_plane);
+
+       return 0;
+}
+
+static int st7571_crtc_init(struct st7571_device *st7571)
+{
+       struct drm_plane *primary_plane = &st7571->primary_plane;
+       struct drm_crtc *crtc = &st7571->crtc;
+       struct drm_device *dev = &st7571->dev;
+       int ret;
+
+       ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
+                                       &st7571_crtc_funcs, NULL);
+       if (ret)
+               return ret;
+
+       drm_crtc_helper_add(crtc, &st7571_crtc_helper_funcs);
+
+       return 0;
+}
+
+static int st7571_encoder_init(struct st7571_device *st7571)
+{
+       struct drm_encoder *encoder = &st7571->encoder;
+       struct drm_crtc *crtc = &st7571->crtc;
+       struct drm_device *dev = &st7571->dev;
+       int ret;
+
+       ret = drm_encoder_init(dev, encoder, &st7571_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL);
+       if (ret)
+               return ret;
+
+       drm_encoder_helper_add(encoder, &st7571_encoder_helper_funcs);
+
+       encoder->possible_crtcs = drm_crtc_mask(crtc);
+
+       return 0;
+}
+
+static int st7571_connector_init(struct st7571_device *st7571)
+{
+       struct drm_connector *connector = &st7571->connector;
+       struct drm_encoder *encoder = &st7571->encoder;
+       struct drm_device *dev = &st7571->dev;
+       int ret;
+
+       ret = drm_connector_init(dev, connector, &st7571_connector_funcs,
+                                DRM_MODE_CONNECTOR_Unknown);
+       if (ret)
+               return ret;
+
+       drm_connector_helper_add(connector, &st7571_connector_helper_funcs);
+
+       return drm_connector_attach_encoder(connector, encoder);
+}
+
+DEFINE_DRM_GEM_FOPS(st7571_fops);
+
+static const struct drm_driver st7571_driver = {
+       .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
+
+       .name            = DRIVER_NAME,
+       .desc            = DRIVER_DESC,
+       .major           = DRIVER_MAJOR,
+       .minor           = DRIVER_MINOR,
+
+       .fops            = &st7571_fops,
+       DRM_GEM_SHMEM_DRIVER_OPS,
+       DRM_FBDEV_SHMEM_DRIVER_OPS,
+};
+
+static const struct regmap_bus st7571_regmap_bus = {
+       .read = st7571_regmap_read,
+       .write = st7571_regmap_write,
+};
+
+static const struct regmap_config st7571_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .use_single_write = true,
+};
+
+static int st7571_validate_parameters(struct st7571_device *st7571)
+{
+       struct device *dev = st7571->dev.dev;
+       const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints;
+
+       if (st7571->width_mm  == 0) {
+               dev_err(dev, "Invalid panel width\n");
+               return -EINVAL;
+       }
+
+       if (st7571->height_mm == 0) {
+               dev_err(dev, "Invalid panel height\n");
+               return -EINVAL;
+       }
+
+       if (st7571->nlines < constraints->min_nlines ||
+           st7571->nlines > constraints->max_nlines) {
+               dev_err(dev, "Invalid timing configuration.\n");
+               return -EINVAL;
+       }
+
+       if (st7571->startline + st7571->nlines > constraints->max_nlines) {
+               dev_err(dev, "Invalid timing configuration.\n");
+               return -EINVAL;
+       }
+
+       if (st7571->ncols < constraints->min_ncols ||
+           st7571->ncols > constraints->max_ncols) {
+               dev_err(dev, "Invalid timing configuration.\n");
+               return -EINVAL;
+       }
+
+       if (st7571->grayscale && !constraints->support_grayscale) {
+               dev_err(dev, "Grayscale not supported\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int st7571_parse_dt(struct st7571_device *st7571)
+{
+       struct device *dev = &st7571->client->dev;
+       struct device_node *np = dev->of_node;
+       struct display_timing dt;
+       int ret;
+
+       ret = of_get_display_timing(np, "panel-timing", &dt);
+       if (ret) {
+               dev_err(dev, "Failed to get display timing from DT\n");
+               return ret;
+       }
+
+       of_property_read_u32(np, "width-mm", &st7571->width_mm);
+       of_property_read_u32(np, "height-mm", &st7571->height_mm);
+       st7571->grayscale = of_property_read_bool(np, "sitronix,grayscale");
+
+       if (st7571->grayscale) {
+               st7571->pformat = &st7571_grayscale;
+               st7571->bpp = 2;
+       } else {
+               st7571->pformat = &st7571_monochrome;
+               st7571->bpp = 1;
+       }
+
+       st7571->startline = dt.vfront_porch.typ;
+       st7571->nlines = dt.vactive.typ;
+       st7571->ncols = dt.hactive.typ;
+
+       st7571->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+       if (IS_ERR(st7571->reset))
+               return PTR_ERR(st7571->reset);
+
+       return 0;
+}
+
+static void st7571_reset(struct st7571_device *st7571)
+{
+       gpiod_set_value_cansleep(st7571->reset, 1);
+       fsleep(20);
+       gpiod_set_value_cansleep(st7571->reset, 0);
+}
+
+static int st7571_lcd_init(struct st7571_device *st7571)
+{
+       /*
+        * Most of the initialization sequence is taken directly from the
+        * referential initial code in the ST7571 datasheet.
+        */
+       u8 commands[] = {
+               ST7571_DISPLAY_OFF,
+
+               ST7571_SET_MODE_MSB,
+               ST7571_SET_MODE_LSB(0x2e),
+
+               ST7571_SET_SEG_SCAN_DIR(0),
+               ST7571_SET_COM_SCAN_DIR(1),
+
+               ST7571_SET_COM0_MSB,
+               ST7571_SET_COM0_LSB(0x00),
+
+               ST7571_SET_START_LINE_MSB,
+               ST7571_SET_START_LINE_LSB(st7571->startline),
+
+               ST7571_OSC_ON,
+               ST7571_SET_REGULATOR_REG(5),
+               ST7571_SET_CONTRAST_MSB,
+               ST7571_SET_CONTRAST_LSB(0x33),
+               ST7571_SET_LCD_BIAS(0x04),
+               ST7571_SET_DISPLAY_DUTY_MSB,
+               ST7571_SET_DISPLAY_DUTY_LSB(st7571->nlines),
+
+               ST7571_SET_POWER(0x4),  /* Power Control, VC: ON, VR: OFF, VF: OFF */
+               ST7571_SET_POWER(0x6),  /* Power Control, VC: ON, VR: ON, VF: OFF */
+               ST7571_SET_POWER(0x7),  /* Power Control, VC: ON, VR: ON, VF: ON */
+
+               ST7571_COMMAND_SET_3,
+               ST7571_SET_COLOR_MODE(st7571->pformat->mode),
+               ST7571_COMMAND_SET_NORMAL,
+
+               ST7571_SET_REVERSE(0),
+               ST7571_SET_ENTIRE_DISPLAY_ON(0),
+       };
+
+       /* Perform a reset before initializing the controller */
+       st7571_reset(st7571);
+
+       return st7571_send_command_list(st7571, commands, ARRAY_SIZE(commands));
+}
+
+static int st7571_probe(struct i2c_client *client)
+{
+       struct st7571_device *st7571;
+       struct drm_device *dev;
+       int ret;
+
+       st7571 = devm_drm_dev_alloc(&client->dev, &st7571_driver,
+                                   struct st7571_device, dev);
+       if (IS_ERR(st7571))
+               return PTR_ERR(st7571);
+
+       dev = &st7571->dev;
+       st7571->client = client;
+       i2c_set_clientdata(client, st7571);
+       st7571->pdata = device_get_match_data(&client->dev);
+
+       ret = st7571_parse_dt(st7571);
+       if (ret)
+               return ret;
+
+       ret = st7571_validate_parameters(st7571);
+       if (ret)
+               return ret;
+
+       st7571->mode = st7571_mode(st7571);
+
+       /*
+        * The hardware design could make it hard to detect a NAK on the I2C bus.
+        * If the adapter does not support protocol mangling do
+        * not set the I2C_M_IGNORE_NAK flag at the expense * of possible
+        * cruft in the logs.
+        */
+       if (i2c_check_functionality(client->adapter, I2C_FUNC_PROTOCOL_MANGLING))
+               st7571->ignore_nak = true;
+
+       st7571->regmap = devm_regmap_init(&client->dev, &st7571_regmap_bus,
+                                         client, &st7571_regmap_config);
+       if (IS_ERR(st7571->regmap)) {
+               return dev_err_probe(&client->dev, PTR_ERR(st7571->regmap),
+                                    "Failed to initialize regmap\n");
+       }
+
+       st7571->hwbuf = devm_kzalloc(&client->dev,
+                                    (st7571->nlines * st7571->ncols * st7571->bpp) / 8,
+                                    GFP_KERNEL);
+       if (!st7571->hwbuf)
+               return -ENOMEM;
+
+       st7571->row = devm_kzalloc(&client->dev,
+                                  (st7571->ncols * st7571->bpp),
+                                  GFP_KERNEL);
+       if (!st7571->row)
+               return -ENOMEM;
+
+       ret = st7571_mode_config_init(st7571);
+       if (ret)
+               return dev_err_probe(&client->dev, ret,
+                                    "Failed to initialize mode config\n");
+
+       ret = st7571_plane_init(st7571, st7571->pformat);
+       if (ret)
+               return dev_err_probe(&client->dev, ret,
+                                    "Failed to initialize primary plane\n");
+
+       ret = st7571_crtc_init(st7571);
+       if (ret < 0)
+               return dev_err_probe(&client->dev, ret,
+                                    "Failed to initialize CRTC\n");
+
+       ret = st7571_encoder_init(st7571);
+       if (ret < 0)
+               return dev_err_probe(&client->dev, ret,
+                                    "Failed to initialize encoder\n");
+
+       ret = st7571_connector_init(st7571);
+       if (ret < 0)
+               return dev_err_probe(&client->dev, ret,
+                                    "Failed to initialize connector\n");
+
+       drm_mode_config_reset(dev);
+
+       ret = drm_dev_register(dev, 0);
+       if (ret)
+               return dev_err_probe(&client->dev, ret,
+                                    "Failed to register DRM device\n");
+
+       drm_client_setup(dev, NULL);
+       return 0;
+}
+
+static void st7571_remove(struct i2c_client *client)
+{
+       struct st7571_device *st7571 = i2c_get_clientdata(client);
+
+       drm_dev_unplug(&st7571->dev);
+}
+
+struct st7571_panel_data st7571_config = {
+       .init = st7571_lcd_init,
+       .constraints = {
+               .min_nlines = 1,
+               .max_nlines = 128,
+               .min_ncols = 128,
+               .max_ncols = 128,
+               .support_grayscale = true,
+       },
+};
+
+static const struct of_device_id st7571_of_match[] = {
+       { .compatible = "sitronix,st7571", .data = &st7571_config },
+       {},
+};
+MODULE_DEVICE_TABLE(of, st7571_of_match);
+
+static const struct i2c_device_id st7571_id[] = {
+       { "st7571", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, st7571_id);
+
+static struct i2c_driver st7571_i2c_driver = {
+       .driver = {
+               .name = "st7571",
+               .of_match_table = st7571_of_match,
+       },
+       .probe = st7571_probe,
+       .remove = st7571_remove,
+       .id_table = st7571_id,
+};
+
+module_i2c_driver(st7571_i2c_driver);
+
+MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
+MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sitronix/st7586.c b/drivers/gpu/drm/sitronix/st7586.c
new file mode 100644 (file)
index 0000000..a29672d
--- /dev/null
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * DRM driver for Sitronix ST7586 panels
+ *
+ * Copyright 2017 David Lechner <david@lechnology.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/spi/spi.h>
+#include <video/mipi_display.h>
+
+#include <drm/clients/drm_client_setup.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_fbdev_dma.h>
+#include <drm/drm_format_helper.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_mipi_dbi.h>
+#include <drm/drm_rect.h>
+
+/* controller-specific commands */
+#define ST7586_DISP_MODE_GRAY  0x38
+#define ST7586_DISP_MODE_MONO  0x39
+#define ST7586_ENABLE_DDRAM    0x3a
+#define ST7586_SET_DISP_DUTY   0xb0
+#define ST7586_SET_PART_DISP   0xb4
+#define ST7586_SET_NLINE_INV   0xb5
+#define ST7586_SET_VOP         0xc0
+#define ST7586_SET_BIAS_SYSTEM 0xc3
+#define ST7586_SET_BOOST_LEVEL 0xc4
+#define ST7586_SET_VOP_OFFSET  0xc7
+#define ST7586_ENABLE_ANALOG   0xd0
+#define ST7586_AUTO_READ_CTRL  0xd7
+#define ST7586_OTP_RW_CTRL     0xe0
+#define ST7586_OTP_CTRL_OUT    0xe1
+#define ST7586_OTP_READ                0xe3
+
+#define ST7586_DISP_CTRL_MX    BIT(6)
+#define ST7586_DISP_CTRL_MY    BIT(7)
+
+/*
+ * The ST7586 controller has an unusual pixel format where 2bpp grayscale is
+ * packed 3 pixels per byte with the first two pixels using 3 bits and the 3rd
+ * pixel using only 2 bits.
+ *
+ * |  D7  |  D6  |  D5  ||      |      || 2bpp |
+ * | (D4) | (D3) | (D2) ||  D1  |  D0  || GRAY |
+ * +------+------+------++------+------++------+
+ * |  1   |  1   |  1   ||  1   |  1   || 0  0 | black
+ * |  1   |  0   |  0   ||  1   |  0   || 0  1 | dark gray
+ * |  0   |  1   |  0   ||  0   |  1   || 1  0 | light gray
+ * |  0   |  0   |  0   ||  0   |  0   || 1  1 | white
+ */
+
+static const u8 st7586_lookup[] = { 0x7, 0x4, 0x2, 0x0 };
+
+static void st7586_xrgb8888_to_gray332(u8 *dst, void *vaddr,
+                                      struct drm_framebuffer *fb,
+                                      struct drm_rect *clip,
+                                      struct drm_format_conv_state *fmtcnv_state)
+{
+       size_t len = (clip->x2 - clip->x1) * (clip->y2 - clip->y1);
+       unsigned int x, y;
+       u8 *src, *buf, val;
+       struct iosys_map dst_map, vmap;
+
+       buf = kmalloc(len, GFP_KERNEL);
+       if (!buf)
+               return;
+
+       iosys_map_set_vaddr(&dst_map, buf);
+       iosys_map_set_vaddr(&vmap, vaddr);
+       drm_fb_xrgb8888_to_gray8(&dst_map, NULL, &vmap, fb, clip, fmtcnv_state);
+       src = buf;
+
+       for (y = clip->y1; y < clip->y2; y++) {
+               for (x = clip->x1; x < clip->x2; x += 3) {
+                       val = st7586_lookup[*src++ >> 6] << 5;
+                       val |= st7586_lookup[*src++ >> 6] << 2;
+                       val |= st7586_lookup[*src++ >> 6] >> 1;
+                       *dst++ = val;
+               }
+       }
+
+       kfree(buf);
+}
+
+static int st7586_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuffer *fb,
+                          struct drm_rect *clip, struct drm_format_conv_state *fmtcnv_state)
+{
+       int ret;
+
+       ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
+       if (ret)
+               return ret;
+
+       st7586_xrgb8888_to_gray332(dst, src->vaddr, fb, clip, fmtcnv_state);
+
+       drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
+
+       return 0;
+}
+
+static void st7586_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb,
+                           struct drm_rect *rect, struct drm_format_conv_state *fmtcnv_state)
+{
+       struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev);
+       struct mipi_dbi *dbi = &dbidev->dbi;
+       int start, end, ret = 0;
+
+       /* 3 pixels per byte, so grow clip to nearest multiple of 3 */
+       rect->x1 = rounddown(rect->x1, 3);
+       rect->x2 = roundup(rect->x2, 3);
+
+       DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
+
+       ret = st7586_buf_copy(dbidev->tx_buf, src, fb, rect, fmtcnv_state);
+       if (ret)
+               goto err_msg;
+
+       /* Pixels are packed 3 per byte */
+       start = rect->x1 / 3;
+       end = rect->x2 / 3;
+
+       mipi_dbi_command(dbi, MIPI_DCS_SET_COLUMN_ADDRESS,
+                        (start >> 8) & 0xFF, start & 0xFF,
+                        (end >> 8) & 0xFF, (end - 1) & 0xFF);
+       mipi_dbi_command(dbi, MIPI_DCS_SET_PAGE_ADDRESS,
+                        (rect->y1 >> 8) & 0xFF, rect->y1 & 0xFF,
+                        (rect->y2 >> 8) & 0xFF, (rect->y2 - 1) & 0xFF);
+
+       ret = mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START,
+                                  (u8 *)dbidev->tx_buf,
+                                  (end - start) * (rect->y2 - rect->y1));
+err_msg:
+       if (ret)
+               dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
+}
+
+static void st7586_pipe_update(struct drm_simple_display_pipe *pipe,
+                              struct drm_plane_state *old_state)
+{
+       struct drm_plane_state *state = pipe->plane.state;
+       struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
+       struct drm_framebuffer *fb = state->fb;
+       struct drm_rect rect;
+       int idx;
+
+       if (!pipe->crtc.state->active)
+               return;
+
+       if (!drm_dev_enter(fb->dev, &idx))
+               return;
+
+       if (drm_atomic_helper_damage_merged(old_state, state, &rect))
+               st7586_fb_dirty(&shadow_plane_state->data[0], fb, &rect,
+                               &shadow_plane_state->fmtcnv_state);
+
+       drm_dev_exit(idx);
+}
+
+static void st7586_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 drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
+       struct drm_framebuffer *fb = plane_state->fb;
+       struct mipi_dbi *dbi = &dbidev->dbi;
+       struct drm_rect rect = {
+               .x1 = 0,
+               .x2 = fb->width,
+               .y1 = 0,
+               .y2 = fb->height,
+       };
+       int idx, ret;
+       u8 addr_mode;
+
+       if (!drm_dev_enter(pipe->crtc.dev, &idx))
+               return;
+
+       DRM_DEBUG_KMS("\n");
+
+       ret = mipi_dbi_poweron_reset(dbidev);
+       if (ret)
+               goto out_exit;
+
+       mipi_dbi_command(dbi, ST7586_AUTO_READ_CTRL, 0x9f);
+       mipi_dbi_command(dbi, ST7586_OTP_RW_CTRL, 0x00);
+
+       msleep(10);
+
+       mipi_dbi_command(dbi, ST7586_OTP_READ);
+
+       msleep(20);
+
+       mipi_dbi_command(dbi, ST7586_OTP_CTRL_OUT);
+       mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
+       mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
+
+       msleep(50);
+
+       mipi_dbi_command(dbi, ST7586_SET_VOP_OFFSET, 0x00);
+       mipi_dbi_command(dbi, ST7586_SET_VOP, 0xe3, 0x00);
+       mipi_dbi_command(dbi, ST7586_SET_BIAS_SYSTEM, 0x02);
+       mipi_dbi_command(dbi, ST7586_SET_BOOST_LEVEL, 0x04);
+       mipi_dbi_command(dbi, ST7586_ENABLE_ANALOG, 0x1d);
+       mipi_dbi_command(dbi, ST7586_SET_NLINE_INV, 0x00);
+       mipi_dbi_command(dbi, ST7586_DISP_MODE_GRAY);
+       mipi_dbi_command(dbi, ST7586_ENABLE_DDRAM, 0x02);
+
+       switch (dbidev->rotation) {
+       default:
+               addr_mode = 0x00;
+               break;
+       case 90:
+               addr_mode = ST7586_DISP_CTRL_MY;
+               break;
+       case 180:
+               addr_mode = ST7586_DISP_CTRL_MX | ST7586_DISP_CTRL_MY;
+               break;
+       case 270:
+               addr_mode = ST7586_DISP_CTRL_MX;
+               break;
+       }
+       mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
+
+       mipi_dbi_command(dbi, ST7586_SET_DISP_DUTY, 0x7f);
+       mipi_dbi_command(dbi, ST7586_SET_PART_DISP, 0xa0);
+       mipi_dbi_command(dbi, MIPI_DCS_SET_PARTIAL_ROWS, 0x00, 0x00, 0x00, 0x77);
+       mipi_dbi_command(dbi, MIPI_DCS_EXIT_INVERT_MODE);
+
+       msleep(100);
+
+       st7586_fb_dirty(&shadow_plane_state->data[0], fb, &rect,
+                       &shadow_plane_state->fmtcnv_state);
+
+       mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
+out_exit:
+       drm_dev_exit(idx);
+}
+
+static void st7586_pipe_disable(struct drm_simple_display_pipe *pipe)
+{
+       struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
+
+       /*
+        * This callback is not protected by drm_dev_enter/exit since we want to
+        * turn off the display on regular driver unload. It's highly unlikely
+        * that the underlying SPI controller is gone should this be called after
+        * unplug.
+        */
+
+       DRM_DEBUG_KMS("\n");
+
+       mipi_dbi_command(&dbidev->dbi, MIPI_DCS_SET_DISPLAY_OFF);
+}
+
+static const u32 st7586_formats[] = {
+       DRM_FORMAT_XRGB8888,
+};
+
+static const struct drm_simple_display_pipe_funcs st7586_pipe_funcs = {
+       .mode_valid     = mipi_dbi_pipe_mode_valid,
+       .enable         = st7586_pipe_enable,
+       .disable        = st7586_pipe_disable,
+       .update         = st7586_pipe_update,
+       .begin_fb_access = mipi_dbi_pipe_begin_fb_access,
+       .end_fb_access  = mipi_dbi_pipe_end_fb_access,
+       .reset_plane    = mipi_dbi_pipe_reset_plane,
+       .duplicate_plane_state = mipi_dbi_pipe_duplicate_plane_state,
+       .destroy_plane_state = mipi_dbi_pipe_destroy_plane_state,
+};
+
+static const struct drm_display_mode st7586_mode = {
+       DRM_SIMPLE_MODE(178, 128, 37, 27),
+};
+
+DEFINE_DRM_GEM_DMA_FOPS(st7586_fops);
+
+static const struct drm_driver st7586_driver = {
+       .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
+       .fops                   = &st7586_fops,
+       DRM_GEM_DMA_DRIVER_OPS_VMAP,
+       DRM_FBDEV_DMA_DRIVER_OPS,
+       .debugfs_init           = mipi_dbi_debugfs_init,
+       .name                   = "st7586",
+       .desc                   = "Sitronix ST7586",
+       .major                  = 1,
+       .minor                  = 0,
+};
+
+static const struct of_device_id st7586_of_match[] = {
+       { .compatible = "lego,ev3-lcd" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, st7586_of_match);
+
+static const struct spi_device_id st7586_id[] = {
+       { "ev3-lcd", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(spi, st7586_id);
+
+static int st7586_probe(struct spi_device *spi)
+{
+       struct device *dev = &spi->dev;
+       struct mipi_dbi_dev *dbidev;
+       struct drm_device *drm;
+       struct mipi_dbi *dbi;
+       struct gpio_desc *a0;
+       u32 rotation = 0;
+       size_t bufsize;
+       int ret;
+
+       dbidev = devm_drm_dev_alloc(dev, &st7586_driver,
+                                   struct mipi_dbi_dev, drm);
+       if (IS_ERR(dbidev))
+               return PTR_ERR(dbidev);
+
+       dbi = &dbidev->dbi;
+       drm = &dbidev->drm;
+
+       bufsize = (st7586_mode.vdisplay + 2) / 3 * st7586_mode.hdisplay;
+
+       dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+       if (IS_ERR(dbi->reset))
+               return dev_err_probe(dev, PTR_ERR(dbi->reset), "Failed to get GPIO 'reset'\n");
+
+       a0 = devm_gpiod_get(dev, "a0", GPIOD_OUT_LOW);
+       if (IS_ERR(a0))
+               return dev_err_probe(dev, PTR_ERR(a0), "Failed to get GPIO 'a0'\n");
+
+       device_property_read_u32(dev, "rotation", &rotation);
+
+       ret = mipi_dbi_spi_init(spi, dbi, a0);
+       if (ret)
+               return ret;
+
+       /* Cannot read from this controller via SPI */
+       dbi->read_commands = NULL;
+
+       ret = mipi_dbi_dev_init_with_formats(dbidev, &st7586_pipe_funcs,
+                                            st7586_formats, ARRAY_SIZE(st7586_formats),
+                                            &st7586_mode, rotation, bufsize);
+       if (ret)
+               return ret;
+
+       /*
+        * we are using 8-bit data, so we are not actually swapping anything,
+        * but setting mipi->swap_bytes makes mipi_dbi_typec3_command() do the
+        * right thing and not use 16-bit transfers (which results in swapped
+        * bytes on little-endian systems and causes out of order data to be
+        * sent to the display).
+        */
+       dbi->swap_bytes = true;
+
+       drm_mode_config_reset(drm);
+
+       ret = drm_dev_register(drm, 0);
+       if (ret)
+               return ret;
+
+       spi_set_drvdata(spi, drm);
+
+       drm_client_setup(drm, NULL);
+
+       return 0;
+}
+
+static void st7586_remove(struct spi_device *spi)
+{
+       struct drm_device *drm = spi_get_drvdata(spi);
+
+       drm_dev_unplug(drm);
+       drm_atomic_helper_shutdown(drm);
+}
+
+static void st7586_shutdown(struct spi_device *spi)
+{
+       drm_atomic_helper_shutdown(spi_get_drvdata(spi));
+}
+
+static struct spi_driver st7586_spi_driver = {
+       .driver = {
+               .name = "st7586",
+               .of_match_table = st7586_of_match,
+       },
+       .id_table = st7586_id,
+       .probe = st7586_probe,
+       .remove = st7586_remove,
+       .shutdown = st7586_shutdown,
+};
+module_spi_driver(st7586_spi_driver);
+
+MODULE_DESCRIPTION("Sitronix ST7586 DRM driver");
+MODULE_AUTHOR("David Lechner <david@lechnology.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sitronix/st7735r.c b/drivers/gpu/drm/sitronix/st7735r.c
new file mode 100644 (file)
index 0000000..1d60f6e
--- /dev/null
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * DRM driver for display panels connected to a Sitronix ST7715R or ST7735R
+ * display controller in SPI mode.
+ *
+ * Copyright 2017 David Lechner <david@lechnology.com>
+ * Copyright (C) 2019 Glider bvba
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/dma-buf.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/spi/spi.h>
+#include <video/mipi_display.h>
+
+#include <drm/clients/drm_client_setup.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fbdev_dma.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_mipi_dbi.h>
+
+#define ST7735R_FRMCTR1                0xb1
+#define ST7735R_FRMCTR2                0xb2
+#define ST7735R_FRMCTR3                0xb3
+#define ST7735R_INVCTR         0xb4
+#define ST7735R_PWCTR1         0xc0
+#define ST7735R_PWCTR2         0xc1
+#define ST7735R_PWCTR3         0xc2
+#define ST7735R_PWCTR4         0xc3
+#define ST7735R_PWCTR5         0xc4
+#define ST7735R_VMCTR1         0xc5
+#define ST7735R_GAMCTRP1       0xe0
+#define ST7735R_GAMCTRN1       0xe1
+
+#define ST7735R_MY     BIT(7)
+#define ST7735R_MX     BIT(6)
+#define ST7735R_MV     BIT(5)
+#define ST7735R_RGB    BIT(3)
+
+struct st7735r_cfg {
+       const struct drm_display_mode mode;
+       unsigned int left_offset;
+       unsigned int top_offset;
+       unsigned int write_only:1;
+       unsigned int rgb:1;             /* RGB (vs. BGR) */
+};
+
+struct st7735r_priv {
+       struct mipi_dbi_dev dbidev;     /* Must be first for .release() */
+       const struct st7735r_cfg *cfg;
+};
+
+static void st7735r_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 st7735r_priv *priv = container_of(dbidev, struct st7735r_priv,
+                                                dbidev);
+       struct mipi_dbi *dbi = &dbidev->dbi;
+       int ret, idx;
+       u8 addr_mode;
+
+       if (!drm_dev_enter(pipe->crtc.dev, &idx))
+               return;
+
+       DRM_DEBUG_KMS("\n");
+
+       ret = mipi_dbi_poweron_reset(dbidev);
+       if (ret)
+               goto out_exit;
+
+       msleep(150);
+
+       mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
+       msleep(500);
+
+       mipi_dbi_command(dbi, ST7735R_FRMCTR1, 0x01, 0x2c, 0x2d);
+       mipi_dbi_command(dbi, ST7735R_FRMCTR2, 0x01, 0x2c, 0x2d);
+       mipi_dbi_command(dbi, ST7735R_FRMCTR3, 0x01, 0x2c, 0x2d, 0x01, 0x2c,
+                        0x2d);
+       mipi_dbi_command(dbi, ST7735R_INVCTR, 0x07);
+       mipi_dbi_command(dbi, ST7735R_PWCTR1, 0xa2, 0x02, 0x84);
+       mipi_dbi_command(dbi, ST7735R_PWCTR2, 0xc5);
+       mipi_dbi_command(dbi, ST7735R_PWCTR3, 0x0a, 0x00);
+       mipi_dbi_command(dbi, ST7735R_PWCTR4, 0x8a, 0x2a);
+       mipi_dbi_command(dbi, ST7735R_PWCTR5, 0x8a, 0xee);
+       mipi_dbi_command(dbi, ST7735R_VMCTR1, 0x0e);
+       mipi_dbi_command(dbi, MIPI_DCS_EXIT_INVERT_MODE);
+       switch (dbidev->rotation) {
+       default:
+               addr_mode = ST7735R_MX | ST7735R_MY;
+               break;
+       case 90:
+               addr_mode = ST7735R_MX | ST7735R_MV;
+               break;
+       case 180:
+               addr_mode = 0;
+               break;
+       case 270:
+               addr_mode = ST7735R_MY | ST7735R_MV;
+               break;
+       }
+
+       if (priv->cfg->rgb)
+               addr_mode |= ST7735R_RGB;
+
+       mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
+       mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT,
+                        MIPI_DCS_PIXEL_FMT_16BIT);
+       mipi_dbi_command(dbi, ST7735R_GAMCTRP1, 0x02, 0x1c, 0x07, 0x12, 0x37,
+                        0x32, 0x29, 0x2d, 0x29, 0x25, 0x2b, 0x39, 0x00, 0x01,
+                        0x03, 0x10);
+       mipi_dbi_command(dbi, ST7735R_GAMCTRN1, 0x03, 0x1d, 0x07, 0x06, 0x2e,
+                        0x2c, 0x29, 0x2d, 0x2e, 0x2e, 0x37, 0x3f, 0x00, 0x00,
+                        0x02, 0x10);
+       mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
+
+       msleep(100);
+
+       mipi_dbi_command(dbi, MIPI_DCS_ENTER_NORMAL_MODE);
+
+       msleep(20);
+
+       mipi_dbi_enable_flush(dbidev, crtc_state, plane_state);
+out_exit:
+       drm_dev_exit(idx);
+}
+
+static const struct drm_simple_display_pipe_funcs st7735r_pipe_funcs = {
+       DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS(st7735r_pipe_enable),
+};
+
+static const struct st7735r_cfg jd_t18003_t01_cfg = {
+       .mode           = { DRM_SIMPLE_MODE(128, 160, 28, 35) },
+       /* Cannot read from Adafruit 1.8" display via SPI */
+       .write_only     = true,
+};
+
+static const struct st7735r_cfg rh128128t_cfg = {
+       .mode           = { DRM_SIMPLE_MODE(128, 128, 25, 26) },
+       .left_offset    = 2,
+       .top_offset     = 3,
+       .rgb            = true,
+};
+
+DEFINE_DRM_GEM_DMA_FOPS(st7735r_fops);
+
+static const struct drm_driver st7735r_driver = {
+       .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
+       .fops                   = &st7735r_fops,
+       DRM_GEM_DMA_DRIVER_OPS_VMAP,
+       DRM_FBDEV_DMA_DRIVER_OPS,
+       .debugfs_init           = mipi_dbi_debugfs_init,
+       .name                   = "st7735r",
+       .desc                   = "Sitronix ST7735R",
+       .major                  = 1,
+       .minor                  = 0,
+};
+
+static const struct of_device_id st7735r_of_match[] = {
+       { .compatible = "jianda,jd-t18003-t01", .data = &jd_t18003_t01_cfg },
+       { .compatible = "okaya,rh128128t", .data = &rh128128t_cfg },
+       { },
+};
+MODULE_DEVICE_TABLE(of, st7735r_of_match);
+
+static const struct spi_device_id st7735r_id[] = {
+       { "jd-t18003-t01", (uintptr_t)&jd_t18003_t01_cfg },
+       { "rh128128t", (uintptr_t)&rh128128t_cfg },
+       { },
+};
+MODULE_DEVICE_TABLE(spi, st7735r_id);
+
+static int st7735r_probe(struct spi_device *spi)
+{
+       struct device *dev = &spi->dev;
+       const struct st7735r_cfg *cfg;
+       struct mipi_dbi_dev *dbidev;
+       struct st7735r_priv *priv;
+       struct drm_device *drm;
+       struct mipi_dbi *dbi;
+       struct gpio_desc *dc;
+       u32 rotation = 0;
+       int ret;
+
+       cfg = device_get_match_data(&spi->dev);
+       if (!cfg)
+               cfg = (void *)spi_get_device_id(spi)->driver_data;
+
+       priv = devm_drm_dev_alloc(dev, &st7735r_driver,
+                                 struct st7735r_priv, dbidev.drm);
+       if (IS_ERR(priv))
+               return PTR_ERR(priv);
+
+       dbidev = &priv->dbidev;
+       priv->cfg = cfg;
+
+       dbi = &dbidev->dbi;
+       drm = &dbidev->drm;
+
+       dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+       if (IS_ERR(dbi->reset))
+               return dev_err_probe(dev, PTR_ERR(dbi->reset), "Failed to get GPIO 'reset'\n");
+
+       dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW);
+       if (IS_ERR(dc))
+               return dev_err_probe(dev, PTR_ERR(dc), "Failed to get GPIO 'dc'\n");
+
+       dbidev->backlight = devm_of_find_backlight(dev);
+       if (IS_ERR(dbidev->backlight))
+               return PTR_ERR(dbidev->backlight);
+
+       device_property_read_u32(dev, "rotation", &rotation);
+
+       ret = mipi_dbi_spi_init(spi, dbi, dc);
+       if (ret)
+               return ret;
+
+       if (cfg->write_only)
+               dbi->read_commands = NULL;
+
+       dbidev->left_offset = cfg->left_offset;
+       dbidev->top_offset = cfg->top_offset;
+
+       ret = mipi_dbi_dev_init(dbidev, &st7735r_pipe_funcs, &cfg->mode,
+                               rotation);
+       if (ret)
+               return ret;
+
+       drm_mode_config_reset(drm);
+
+       ret = drm_dev_register(drm, 0);
+       if (ret)
+               return ret;
+
+       spi_set_drvdata(spi, drm);
+
+       drm_client_setup(drm, NULL);
+
+       return 0;
+}
+
+static void st7735r_remove(struct spi_device *spi)
+{
+       struct drm_device *drm = spi_get_drvdata(spi);
+
+       drm_dev_unplug(drm);
+       drm_atomic_helper_shutdown(drm);
+}
+
+static void st7735r_shutdown(struct spi_device *spi)
+{
+       drm_atomic_helper_shutdown(spi_get_drvdata(spi));
+}
+
+static struct spi_driver st7735r_spi_driver = {
+       .driver = {
+               .name = "st7735r",
+               .of_match_table = st7735r_of_match,
+       },
+       .id_table = st7735r_id,
+       .probe = st7735r_probe,
+       .remove = st7735r_remove,
+       .shutdown = st7735r_shutdown,
+};
+module_spi_driver(st7735r_spi_driver);
+
+MODULE_DESCRIPTION("Sitronix ST7735R DRM driver");
+MODULE_AUTHOR("David Lechner <david@lechnology.com>");
+MODULE_LICENSE("GPL");
index daa1adbb1b43325d644ae13f3cabfc1bb01ff4d8..6d1b3e2cb3fbd8630864824ae985897b9d8095c7 100644 (file)
@@ -199,44 +199,3 @@ config TINYDRM_SHARP_MEMORY
          * 4.40" Sharp Memory LCD (LS044Q7DH01)
 
          If M is selected the module will be called sharp_memory.
-
-config TINYDRM_ST7586
-       tristate "DRM support for Sitronix ST7586 display panels"
-       depends on DRM && SPI
-       select DRM_CLIENT_SELECTION
-       select DRM_KMS_HELPER
-       select DRM_GEM_DMA_HELPER
-       select DRM_MIPI_DBI
-       help
-         DRM driver for the following Sitronix ST7586 panels:
-         * LEGO MINDSTORMS EV3
-
-         If M is selected the module will be called st7586.
-
-config DRM_ST7571_I2C
-       tristate "DRM support for Sitronix ST7571 display panels (I2C)"
-       depends on DRM && I2C && MMU
-       select DRM_CLIENT_SELECTION
-       select DRM_GEM_SHMEM_HELPER
-       select DRM_KMS_HELPER
-       select REGMAP_I2C
-       help
-         DRM driver for Sitronix ST7571 panels controlled over I2C.
-
-         if M is selected the module will be called st7571-i2c.
-
-config TINYDRM_ST7735R
-       tristate "DRM support for Sitronix ST7715R/ST7735R display panels"
-       depends on DRM && SPI
-       select DRM_CLIENT_SELECTION
-       select DRM_KMS_HELPER
-       select DRM_GEM_DMA_HELPER
-       select DRM_MIPI_DBI
-       select BACKLIGHT_CLASS_DEVICE
-       help
-         DRM driver for Sitronix ST7715R/ST7735R with one of the following
-         LCDs:
-         * Jianda JD-T18003-T01 1.8" 128x160 TFT
-         * Okaya RH128128T 1.44" 128x128 TFT
-
-         If M is selected the module will be called st7735r.
index 0151590db5cbd80aebde0629afd03f47b83c3045..4a9ff61ec25420e2c0a648c04eaab7ca25dd5407 100644 (file)
@@ -6,7 +6,6 @@ obj-$(CONFIG_DRM_BOCHS)                 += bochs.o
 obj-$(CONFIG_DRM_CIRRUS_QEMU)          += cirrus-qemu.o
 obj-$(CONFIG_DRM_GM12U320)             += gm12u320.o
 obj-$(CONFIG_DRM_PANEL_MIPI_DBI)       += panel-mipi-dbi.o
-obj-$(CONFIG_DRM_ST7571_I2C)           += st7571-i2c.o
 obj-$(CONFIG_TINYDRM_HX8357D)          += hx8357d.o
 obj-$(CONFIG_TINYDRM_ILI9163)          += ili9163.o
 obj-$(CONFIG_TINYDRM_ILI9225)          += ili9225.o
@@ -15,5 +14,3 @@ obj-$(CONFIG_TINYDRM_ILI9486)         += ili9486.o
 obj-$(CONFIG_TINYDRM_MI0283QT)         += mi0283qt.o
 obj-$(CONFIG_TINYDRM_REPAPER)          += repaper.o
 obj-$(CONFIG_TINYDRM_SHARP_MEMORY)     += sharp-memory.o
-obj-$(CONFIG_TINYDRM_ST7586)           += st7586.o
-obj-$(CONFIG_TINYDRM_ST7735R)          += st7735r.o
diff --git a/drivers/gpu/drm/tiny/st7571-i2c.c b/drivers/gpu/drm/tiny/st7571-i2c.c
deleted file mode 100644 (file)
index eec8468..0000000
+++ /dev/null
@@ -1,1000 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Driver for Sitronix ST7571, a 4 level gray scale dot matrix LCD controller
- *
- * Copyright (C) 2025 Marcus Folkesson <marcus.folkesson@gmail.com>
- */
-
-#include <linux/bitfield.h>
-#include <linux/delay.h>
-#include <linux/gpio/consumer.h>
-#include <linux/i2c.h>
-#include <linux/module.h>
-#include <linux/regmap.h>
-
-#include <drm/clients/drm_client_setup.h>
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_connector.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_damage_helper.h>
-#include <drm/drm_drv.h>
-#include <drm/drm_encoder.h>
-#include <drm/drm_fb_helper.h>
-#include <drm/drm_fbdev_shmem.h>
-#include <drm/drm_fourcc.h>
-#include <drm/drm_framebuffer.h>
-#include <drm/drm_gem_atomic_helper.h>
-#include <drm/drm_gem_framebuffer_helper.h>
-#include <drm/drm_gem_shmem_helper.h>
-#include <drm/drm_modeset_helper_vtables.h>
-#include <drm/drm_module.h>
-#include <drm/drm_plane.h>
-#include <drm/drm_probe_helper.h>
-
-#include <video/display_timing.h>
-#include <video/of_display_timing.h>
-
-#define ST7571_COMMAND_MODE                    (0x00)
-#define ST7571_DATA_MODE                       (0x40)
-
-/* Normal mode command set */
-#define ST7571_DISPLAY_OFF                     (0xae)
-#define ST7571_DISPLAY_ON                      (0xaf)
-#define ST7571_OSC_ON                          (0xab)
-#define ST7571_SET_COLUMN_LSB(c)               (0x00 | FIELD_PREP(GENMASK(3, 0), (c)))
-#define ST7571_SET_COLUMN_MSB(c)               (0x10 | FIELD_PREP(GENMASK(2, 0), (c) >> 4))
-#define ST7571_SET_COM0_LSB(x)                 (FIELD_PREP(GENMASK(6, 0), (x)))
-#define ST7571_SET_COM0_MSB                    (0x44)
-#define ST7571_SET_COM_SCAN_DIR(d)             (0xc0 | FIELD_PREP(GENMASK(3, 3), (d)))
-#define ST7571_SET_CONTRAST_LSB(c)             (FIELD_PREP(GENMASK(5, 0), (c)))
-#define ST7571_SET_CONTRAST_MSB                        (0x81)
-#define ST7571_SET_DISPLAY_DUTY_LSB(d)         (FIELD_PREP(GENMASK(7, 0), (d)))
-#define ST7571_SET_DISPLAY_DUTY_MSB            (0x48)
-#define ST7571_SET_ENTIRE_DISPLAY_ON(p)                (0xa4 | FIELD_PREP(GENMASK(0, 0), (p)))
-#define ST7571_SET_LCD_BIAS(b)                 (0x50 | FIELD_PREP(GENMASK(2, 0), (b)))
-#define ST7571_SET_MODE_LSB(m)                 (FIELD_PREP(GENMASK(7, 2), (m)))
-#define ST7571_SET_MODE_MSB                    (0x38)
-#define ST7571_SET_PAGE(p)                     (0xb0 | FIELD_PREP(GENMASK(3, 0), (p)))
-#define ST7571_SET_POWER(p)                    (0x28 | FIELD_PREP(GENMASK(2, 0), (p)))
-#define ST7571_SET_REGULATOR_REG(r)            (0x20 | FIELD_PREP(GENMASK(2, 0), (r)))
-#define ST7571_SET_REVERSE(r)                  (0xa6 | FIELD_PREP(GENMASK(0, 0), (r)))
-#define ST7571_SET_SEG_SCAN_DIR(d)             (0xa0 | FIELD_PREP(GENMASK(0, 0), (d)))
-#define ST7571_SET_START_LINE_LSB(l)           (FIELD_PREP(GENMASK(6, 0), (l)))
-#define ST7571_SET_START_LINE_MSB              (0x40)
-
-/* Extension command set 3 */
-#define ST7571_COMMAND_SET_3                   (0x7b)
-#define ST7571_SET_COLOR_MODE(c)               (0x10 | FIELD_PREP(GENMASK(0, 0), (c)))
-#define ST7571_COMMAND_SET_NORMAL              (0x00)
-
-#define ST7571_PAGE_HEIGHT 8
-
-#define DRIVER_NAME "st7571"
-#define DRIVER_DESC "ST7571 DRM driver"
-#define DRIVER_MAJOR 1
-#define DRIVER_MINOR 0
-
-enum st7571_color_mode {
-       ST7571_COLOR_MODE_GRAY = 0,
-       ST7571_COLOR_MODE_BLACKWHITE = 1,
-};
-
-struct st7571_device;
-
-struct st7571_panel_constraints {
-       u32 min_nlines;
-       u32 max_nlines;
-       u32 min_ncols;
-       u32 max_ncols;
-       bool support_grayscale;
-};
-
-struct st7571_panel_data {
-       int (*init)(struct st7571_device *st7571);
-       struct st7571_panel_constraints constraints;
-};
-
-struct st7571_panel_format {
-       void (*prepare_buffer)(struct st7571_device *st7571,
-                              const struct iosys_map *vmap,
-                              struct drm_framebuffer *fb,
-                              struct drm_rect *rect,
-                              struct drm_format_conv_state *fmtcnv_state);
-       int (*update_rect)(struct drm_framebuffer *fb, struct drm_rect *rect);
-       enum st7571_color_mode mode;
-       const u8 nformats;
-       const u32 formats[];
-};
-
-struct st7571_device {
-       struct drm_device dev;
-
-       struct drm_plane primary_plane;
-       struct drm_crtc crtc;
-       struct drm_encoder encoder;
-       struct drm_connector connector;
-
-       struct drm_display_mode mode;
-
-       const struct st7571_panel_format *pformat;
-       const struct st7571_panel_data *pdata;
-       struct i2c_client *client;
-       struct gpio_desc *reset;
-       struct regmap *regmap;
-
-       /*
-        * Depending on the hardware design, the acknowledge signal may be hard to
-        * recognize as a valid logic "0" level.
-        * Therefor, ignore NAK if possible to stay compatible with most hardware designs
-        * and off-the-shelf panels out there.
-        *
-        * From section 6.4 MICROPOCESSOR INTERFACE section in the datasheet:
-        *
-        * "By connecting SDA_OUT to SDA_IN externally, the SDA line becomes fully
-        * I2C interface compatible.
-        * Separating acknowledge-output from serial data
-        * input is advantageous for chip-on-glass (COG) applications. In COG
-        * applications, the ITO resistance and the pull-up resistor will form a
-        * voltage  divider, which affects acknowledge-signal level. Larger ITO
-        * resistance will raise the acknowledged-signal level and system cannot
-        * recognize this level as a valid logic “0” level. By separating SDA_IN from
-        * SDA_OUT, the IC can be used in a mode that ignores the acknowledge-bit.
-        * For applications which check acknowledge-bit, it is necessary to minimize
-        * the ITO resistance of the SDA_OUT trace to guarantee a valid low level."
-        *
-        */
-       bool ignore_nak;
-
-       bool grayscale;
-       u32 height_mm;
-       u32 width_mm;
-       u32 startline;
-       u32 nlines;
-       u32 ncols;
-       u32 bpp;
-
-       /* Intermediate buffer in LCD friendly format */
-       u8 *hwbuf;
-
-       /* Row of (transformed) pixels ready to be written to the display */
-       u8 *row;
-};
-
-static inline struct st7571_device *drm_to_st7571(struct drm_device *dev)
-{
-       return container_of(dev, struct st7571_device, dev);
-}
-
-static int st7571_regmap_write(void *context, const void *data, size_t count)
-{
-       struct i2c_client *client = context;
-       struct st7571_device *st7571 = i2c_get_clientdata(client);
-       int ret;
-
-       struct i2c_msg msg = {
-               .addr = st7571->client->addr,
-               .flags = st7571->ignore_nak ? I2C_M_IGNORE_NAK : 0,
-               .len = count,
-               .buf = (u8 *)data
-       };
-
-       ret = i2c_transfer(st7571->client->adapter, &msg, 1);
-
-       /*
-        * Unfortunately, there is no way to check if the transfer failed because of
-        * a NAK or something else as I2C bus drivers use different return values for NAK.
-        *
-        * However, if the transfer fails and ignore_nak is set, we know it is an error.
-        */
-       if (ret < 0 && st7571->ignore_nak)
-               return ret;
-
-       return 0;
-}
-
-/* The st7571 driver does not read registers but regmap expects a .read */
-static int st7571_regmap_read(void *context, const void *reg_buf,
-                             size_t reg_size, void *val_buf, size_t val_size)
-{
-       return -EOPNOTSUPP;
-}
-
-static int st7571_send_command_list(struct st7571_device *st7571,
-                                   const u8 *cmd_list, size_t len)
-{
-       int ret;
-
-       for (int i = 0; i < len; i++) {
-               ret = regmap_write(st7571->regmap, ST7571_COMMAND_MODE, cmd_list[i]);
-               if (ret < 0)
-                       return ret;
-       }
-
-       return ret;
-}
-
-static inline u8 st7571_transform_xy(const char *p, int x, int y)
-{
-       int xrest = x % 8;
-       u8 result = 0;
-
-       /*
-        * Transforms an (x, y) pixel coordinate into a vertical 8-bit
-        * column from the framebuffer. It calculates the corresponding byte in the
-        * framebuffer, extracts the bit at the given x position across 8 consecutive
-        * rows, and packs those bits into a single byte.
-        *
-        * Return an 8-bit value representing a vertical column of pixels.
-        */
-       x = x / 8;
-       y = (y / 8) * 8;
-
-       for (int i = 0; i < 8; i++) {
-               int row_idx = y + i;
-               u8 byte = p[row_idx * 16 + x];
-               u8 bit = (byte >> xrest) & 1;
-
-               result |= (bit << i);
-       }
-
-       return result;
-}
-
-static int st7571_set_position(struct st7571_device *st7571, int x, int y)
-{
-       u8 cmd_list[] = {
-               ST7571_SET_COLUMN_LSB(x),
-               ST7571_SET_COLUMN_MSB(x),
-               ST7571_SET_PAGE(y / ST7571_PAGE_HEIGHT),
-       };
-
-       return st7571_send_command_list(st7571, cmd_list, ARRAY_SIZE(cmd_list));
-}
-
-static int st7571_fb_clear_screen(struct st7571_device *st7571)
-{
-       u32 npixels = st7571->ncols * round_up(st7571->nlines, ST7571_PAGE_HEIGHT) * st7571->bpp;
-       char pixelvalue = 0x00;
-
-       for (int i = 0; i < npixels; i++)
-               regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, &pixelvalue, 1);
-
-       return 0;
-}
-
-static void st7571_prepare_buffer_monochrome(struct st7571_device *st7571,
-                                            const struct iosys_map *vmap,
-                                            struct drm_framebuffer *fb,
-                                            struct drm_rect *rect,
-                                            struct drm_format_conv_state *fmtcnv_state)
-{
-       unsigned int dst_pitch;
-       struct iosys_map dst;
-       u32 size;
-
-       switch (fb->format->format) {
-       case DRM_FORMAT_XRGB8888:
-               dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8);
-               iosys_map_set_vaddr(&dst, st7571->hwbuf);
-
-               drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
-               break;
-
-       case DRM_FORMAT_R1:
-               size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
-               memcpy(st7571->hwbuf, vmap->vaddr, size);
-               break;
-       }
-}
-
-static void st7571_prepare_buffer_grayscale(struct st7571_device *st7571,
-                                           const struct iosys_map *vmap,
-                                           struct drm_framebuffer *fb,
-                                           struct drm_rect *rect,
-                                           struct drm_format_conv_state *fmtcnv_state)
-{
-       u32 size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
-       unsigned int dst_pitch;
-       struct iosys_map dst;
-
-       switch (fb->format->format) {
-       case DRM_FORMAT_XRGB8888: /* Only support XRGB8888 in monochrome mode */
-               dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8);
-               iosys_map_set_vaddr(&dst, st7571->hwbuf);
-
-               drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
-               break;
-
-       case DRM_FORMAT_R1:
-               size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
-               memcpy(st7571->hwbuf, vmap->vaddr, size);
-               break;
-
-       case DRM_FORMAT_R2:
-               size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 4;
-               memcpy(st7571->hwbuf, vmap->vaddr, size);
-               break;
-       };
-}
-
-static int st7571_fb_update_rect_monochrome(struct drm_framebuffer *fb, struct drm_rect *rect)
-{
-       struct st7571_device *st7571 = drm_to_st7571(fb->dev);
-       char *row = st7571->row;
-
-       /* Align y to display page boundaries */
-       rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT);
-       rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines);
-
-       for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) {
-               for (int x = rect->x1; x < rect->x2; x++)
-                       row[x] = st7571_transform_xy(st7571->hwbuf, x, y);
-
-               st7571_set_position(st7571, rect->x1, y);
-
-               /* TODO: Investige why we can't write multiple bytes at once */
-               for (int x = rect->x1; x < rect->x2; x++)
-                       regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
-       }
-
-       return 0;
-}
-
-static int st7571_fb_update_rect_grayscale(struct drm_framebuffer *fb, struct drm_rect *rect)
-{
-       struct st7571_device *st7571 = drm_to_st7571(fb->dev);
-       u32 format = fb->format->format;
-       char *row = st7571->row;
-       int x1;
-       int x2;
-
-       /* Align y to display page boundaries */
-       rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT);
-       rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines);
-
-       switch (format) {
-       case DRM_FORMAT_XRGB8888:
-               /* Threated as monochrome (R1) */
-               fallthrough;
-       case DRM_FORMAT_R1:
-               x1 = rect->x1;
-               x2 = rect->x2;
-               break;
-       case DRM_FORMAT_R2:
-               x1 = rect->x1 * 2;
-               x2 = rect->x2 * 2;
-               break;
-       }
-
-       for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) {
-               for (int x = x1; x < x2; x++)
-                       row[x] = st7571_transform_xy(st7571->hwbuf, x, y);
-
-               st7571_set_position(st7571, rect->x1, y);
-
-               /* TODO: Investige why we can't write multiple bytes at once */
-               for (int x = x1; x < x2; x++) {
-                       regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
-
-                       /*
-                        * As the display supports grayscale, all pixels must be written as two bits
-                        * even if the format is monochrome.
-                        *
-                        * The bit values maps to the following grayscale:
-                        * 0 0 = White
-                        * 0 1 = Light gray
-                        * 1 0 = Dark gray
-                        * 1 1 = Black
-                        *
-                        * For monochrome formats, write the same value twice to get
-                        * either a black or white pixel.
-                        */
-                       if (format == DRM_FORMAT_R1 || format == DRM_FORMAT_XRGB8888)
-                               regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
-               }
-       }
-
-       return 0;
-}
-
-static int st7571_connector_get_modes(struct drm_connector *conn)
-{
-       struct st7571_device *st7571 = drm_to_st7571(conn->dev);
-
-       return drm_connector_helper_get_modes_fixed(conn, &st7571->mode);
-}
-
-static const struct drm_connector_helper_funcs st7571_connector_helper_funcs = {
-       .get_modes = st7571_connector_get_modes,
-};
-
-static const struct st7571_panel_format st7571_monochrome = {
-       .prepare_buffer = st7571_prepare_buffer_monochrome,
-       .update_rect = st7571_fb_update_rect_monochrome,
-       .mode = ST7571_COLOR_MODE_BLACKWHITE,
-       .formats = {
-               DRM_FORMAT_XRGB8888,
-               DRM_FORMAT_R1,
-       },
-       .nformats = 2,
-};
-
-static const struct st7571_panel_format st7571_grayscale = {
-       .prepare_buffer = st7571_prepare_buffer_grayscale,
-       .update_rect = st7571_fb_update_rect_grayscale,
-       .mode = ST7571_COLOR_MODE_GRAY,
-       .formats = {
-               DRM_FORMAT_XRGB8888,
-               DRM_FORMAT_R1,
-               DRM_FORMAT_R2,
-       },
-       .nformats = 3,
-};
-
-static const u64 st7571_primary_plane_fmtmods[] = {
-       DRM_FORMAT_MOD_LINEAR,
-       DRM_FORMAT_MOD_INVALID
-};
-
-static int st7571_primary_plane_helper_atomic_check(struct drm_plane *plane,
-                                                   struct drm_atomic_state *state)
-{
-       struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
-       struct drm_crtc *new_crtc = new_plane_state->crtc;
-       struct drm_crtc_state *new_crtc_state = NULL;
-
-       if (new_crtc)
-               new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
-
-       return drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
-                                                  DRM_PLANE_NO_SCALING,
-                                                  DRM_PLANE_NO_SCALING,
-                                                  false, false);
-}
-
-static void st7571_primary_plane_helper_atomic_update(struct drm_plane *plane,
-                                                     struct drm_atomic_state *state)
-{
-       struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
-       struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
-       struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
-       struct drm_framebuffer *fb = plane_state->fb;
-       struct drm_atomic_helper_damage_iter iter;
-       struct drm_device *dev = plane->dev;
-       struct drm_rect damage;
-       struct st7571_device *st7571 = drm_to_st7571(plane->dev);
-       int ret, idx;
-
-       if (!fb)
-               return; /* no framebuffer; plane is disabled */
-
-       ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
-       if (ret)
-               return;
-
-       if (!drm_dev_enter(dev, &idx))
-               goto out_drm_gem_fb_end_cpu_access;
-
-       drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
-       drm_atomic_for_each_plane_damage(&iter, &damage) {
-               st7571->pformat->prepare_buffer(st7571,
-                                               &shadow_plane_state->data[0],
-                                               fb, &damage,
-                                               &shadow_plane_state->fmtcnv_state);
-
-               st7571->pformat->update_rect(fb, &damage);
-       }
-
-       drm_dev_exit(idx);
-
-out_drm_gem_fb_end_cpu_access:
-       drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
-}
-
-static void st7571_primary_plane_helper_atomic_disable(struct drm_plane *plane,
-                                                      struct drm_atomic_state *state)
-{
-       struct drm_device *dev = plane->dev;
-       struct st7571_device *st7571 = drm_to_st7571(plane->dev);
-       int idx;
-
-       if (!drm_dev_enter(dev, &idx))
-               return;
-
-       st7571_fb_clear_screen(st7571);
-       drm_dev_exit(idx);
-}
-
-static const struct drm_plane_helper_funcs st7571_primary_plane_helper_funcs = {
-       DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
-       .atomic_check = st7571_primary_plane_helper_atomic_check,
-       .atomic_update = st7571_primary_plane_helper_atomic_update,
-       .atomic_disable = st7571_primary_plane_helper_atomic_disable,
-};
-
-static const struct drm_plane_funcs st7571_primary_plane_funcs = {
-       .update_plane = drm_atomic_helper_update_plane,
-       .disable_plane = drm_atomic_helper_disable_plane,
-       .destroy = drm_plane_cleanup,
-       DRM_GEM_SHADOW_PLANE_FUNCS,
-};
-
-/*
- * CRTC
- */
-
-static enum drm_mode_status st7571_crtc_mode_valid(struct drm_crtc *crtc,
-                                                  const struct drm_display_mode *mode)
-{
-       struct st7571_device *st7571 = drm_to_st7571(crtc->dev);
-
-       return drm_crtc_helper_mode_valid_fixed(crtc, mode, &st7571->mode);
-}
-
-static const struct drm_crtc_helper_funcs st7571_crtc_helper_funcs = {
-       .atomic_check = drm_crtc_helper_atomic_check,
-       .mode_valid = st7571_crtc_mode_valid,
-};
-
-static const struct drm_crtc_funcs st7571_crtc_funcs = {
-       .reset = drm_atomic_helper_crtc_reset,
-       .destroy = drm_crtc_cleanup,
-       .set_config = drm_atomic_helper_set_config,
-       .page_flip = drm_atomic_helper_page_flip,
-       .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
-       .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
-};
-
-/*
- * Encoder
- */
-
-static void ssd130x_encoder_atomic_enable(struct drm_encoder *encoder,
-                                         struct drm_atomic_state *state)
-{
-       struct drm_device *drm = encoder->dev;
-       struct st7571_device *st7571 = drm_to_st7571(drm);
-       u8 command = ST7571_DISPLAY_ON;
-       int ret;
-
-       ret = st7571->pdata->init(st7571);
-       if (ret)
-               return;
-
-       st7571_send_command_list(st7571, &command, 1);
-}
-
-static void ssd130x_encoder_atomic_disable(struct drm_encoder *encoder,
-                                          struct drm_atomic_state *state)
-{
-       struct drm_device *drm = encoder->dev;
-       struct st7571_device *st7571 = drm_to_st7571(drm);
-       u8 command = ST7571_DISPLAY_OFF;
-
-       st7571_send_command_list(st7571, &command, 1);
-}
-
-static const struct drm_encoder_funcs st7571_encoder_funcs = {
-       .destroy = drm_encoder_cleanup,
-
-};
-
-static const struct drm_encoder_helper_funcs st7571_encoder_helper_funcs = {
-       .atomic_enable = ssd130x_encoder_atomic_enable,
-       .atomic_disable = ssd130x_encoder_atomic_disable,
-};
-
-/*
- * Connector
- */
-
-static const struct drm_connector_funcs st7571_connector_funcs = {
-       .reset = drm_atomic_helper_connector_reset,
-       .fill_modes = drm_helper_probe_single_connector_modes,
-       .destroy = drm_connector_cleanup,
-       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
-       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
-static const struct drm_mode_config_funcs st7571_mode_config_funcs = {
-       .fb_create = drm_gem_fb_create_with_dirty,
-       .atomic_check = drm_atomic_helper_check,
-       .atomic_commit = drm_atomic_helper_commit,
-};
-
-static struct drm_display_mode st7571_mode(struct st7571_device *st7571)
-{
-       struct drm_display_mode mode = {
-               DRM_SIMPLE_MODE(st7571->ncols, st7571->nlines,
-                               st7571->width_mm, st7571->height_mm),
-       };
-
-       return mode;
-}
-
-static int st7571_mode_config_init(struct st7571_device *st7571)
-{
-       struct drm_device *dev = &st7571->dev;
-       const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints;
-       int ret;
-
-       ret = drmm_mode_config_init(dev);
-       if (ret)
-               return ret;
-
-       dev->mode_config.min_width = constraints->min_ncols;
-       dev->mode_config.min_height = constraints->min_nlines;
-       dev->mode_config.max_width = constraints->max_ncols;
-       dev->mode_config.max_height = constraints->max_nlines;
-       dev->mode_config.preferred_depth = 24;
-       dev->mode_config.funcs = &st7571_mode_config_funcs;
-
-       return 0;
-}
-
-static int st7571_plane_init(struct st7571_device *st7571,
-                            const struct st7571_panel_format *pformat)
-{
-       struct drm_plane *primary_plane = &st7571->primary_plane;
-       struct drm_device *dev = &st7571->dev;
-       int ret;
-
-       ret = drm_universal_plane_init(dev, primary_plane, 0,
-                                      &st7571_primary_plane_funcs,
-                                      pformat->formats,
-                                      pformat->nformats,
-                                      st7571_primary_plane_fmtmods,
-                                      DRM_PLANE_TYPE_PRIMARY, NULL);
-       if (ret)
-               return ret;
-
-       drm_plane_helper_add(primary_plane, &st7571_primary_plane_helper_funcs);
-       drm_plane_enable_fb_damage_clips(primary_plane);
-
-       return 0;
-}
-
-static int st7571_crtc_init(struct st7571_device *st7571)
-{
-       struct drm_plane *primary_plane = &st7571->primary_plane;
-       struct drm_crtc *crtc = &st7571->crtc;
-       struct drm_device *dev = &st7571->dev;
-       int ret;
-
-       ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
-                                       &st7571_crtc_funcs, NULL);
-       if (ret)
-               return ret;
-
-       drm_crtc_helper_add(crtc, &st7571_crtc_helper_funcs);
-
-       return 0;
-}
-
-static int st7571_encoder_init(struct st7571_device *st7571)
-{
-       struct drm_encoder *encoder = &st7571->encoder;
-       struct drm_crtc *crtc = &st7571->crtc;
-       struct drm_device *dev = &st7571->dev;
-       int ret;
-
-       ret = drm_encoder_init(dev, encoder, &st7571_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL);
-       if (ret)
-               return ret;
-
-       drm_encoder_helper_add(encoder, &st7571_encoder_helper_funcs);
-
-       encoder->possible_crtcs = drm_crtc_mask(crtc);
-
-       return 0;
-}
-
-static int st7571_connector_init(struct st7571_device *st7571)
-{
-       struct drm_connector *connector = &st7571->connector;
-       struct drm_encoder *encoder = &st7571->encoder;
-       struct drm_device *dev = &st7571->dev;
-       int ret;
-
-       ret = drm_connector_init(dev, connector, &st7571_connector_funcs,
-                                DRM_MODE_CONNECTOR_Unknown);
-       if (ret)
-               return ret;
-
-       drm_connector_helper_add(connector, &st7571_connector_helper_funcs);
-
-       return drm_connector_attach_encoder(connector, encoder);
-}
-
-DEFINE_DRM_GEM_FOPS(st7571_fops);
-
-static const struct drm_driver st7571_driver = {
-       .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
-
-       .name            = DRIVER_NAME,
-       .desc            = DRIVER_DESC,
-       .major           = DRIVER_MAJOR,
-       .minor           = DRIVER_MINOR,
-
-       .fops            = &st7571_fops,
-       DRM_GEM_SHMEM_DRIVER_OPS,
-       DRM_FBDEV_SHMEM_DRIVER_OPS,
-};
-
-static const struct regmap_bus st7571_regmap_bus = {
-       .read = st7571_regmap_read,
-       .write = st7571_regmap_write,
-};
-
-static const struct regmap_config st7571_regmap_config = {
-       .reg_bits = 8,
-       .val_bits = 8,
-       .use_single_write = true,
-};
-
-static int st7571_validate_parameters(struct st7571_device *st7571)
-{
-       struct device *dev = st7571->dev.dev;
-       const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints;
-
-       if (st7571->width_mm  == 0) {
-               dev_err(dev, "Invalid panel width\n");
-               return -EINVAL;
-       }
-
-       if (st7571->height_mm == 0) {
-               dev_err(dev, "Invalid panel height\n");
-               return -EINVAL;
-       }
-
-       if (st7571->nlines < constraints->min_nlines ||
-           st7571->nlines > constraints->max_nlines) {
-               dev_err(dev, "Invalid timing configuration.\n");
-               return -EINVAL;
-       }
-
-       if (st7571->startline + st7571->nlines > constraints->max_nlines) {
-               dev_err(dev, "Invalid timing configuration.\n");
-               return -EINVAL;
-       }
-
-       if (st7571->ncols < constraints->min_ncols ||
-           st7571->ncols > constraints->max_ncols) {
-               dev_err(dev, "Invalid timing configuration.\n");
-               return -EINVAL;
-       }
-
-       if (st7571->grayscale && !constraints->support_grayscale) {
-               dev_err(dev, "Grayscale not supported\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int st7571_parse_dt(struct st7571_device *st7571)
-{
-       struct device *dev = &st7571->client->dev;
-       struct device_node *np = dev->of_node;
-       struct display_timing dt;
-       int ret;
-
-       ret = of_get_display_timing(np, "panel-timing", &dt);
-       if (ret) {
-               dev_err(dev, "Failed to get display timing from DT\n");
-               return ret;
-       }
-
-       of_property_read_u32(np, "width-mm", &st7571->width_mm);
-       of_property_read_u32(np, "height-mm", &st7571->height_mm);
-       st7571->grayscale = of_property_read_bool(np, "sitronix,grayscale");
-
-       if (st7571->grayscale) {
-               st7571->pformat = &st7571_grayscale;
-               st7571->bpp = 2;
-       } else {
-               st7571->pformat = &st7571_monochrome;
-               st7571->bpp = 1;
-       }
-
-       st7571->startline = dt.vfront_porch.typ;
-       st7571->nlines = dt.vactive.typ;
-       st7571->ncols = dt.hactive.typ;
-
-       st7571->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
-       if (IS_ERR(st7571->reset))
-               return PTR_ERR(st7571->reset);
-
-       return 0;
-}
-
-static void st7571_reset(struct st7571_device *st7571)
-{
-       gpiod_set_value_cansleep(st7571->reset, 1);
-       fsleep(20);
-       gpiod_set_value_cansleep(st7571->reset, 0);
-}
-
-static int st7571_lcd_init(struct st7571_device *st7571)
-{
-       /*
-        * Most of the initialization sequence is taken directly from the
-        * referential initial code in the ST7571 datasheet.
-        */
-       u8 commands[] = {
-               ST7571_DISPLAY_OFF,
-
-               ST7571_SET_MODE_MSB,
-               ST7571_SET_MODE_LSB(0x2e),
-
-               ST7571_SET_SEG_SCAN_DIR(0),
-               ST7571_SET_COM_SCAN_DIR(1),
-
-               ST7571_SET_COM0_MSB,
-               ST7571_SET_COM0_LSB(0x00),
-
-               ST7571_SET_START_LINE_MSB,
-               ST7571_SET_START_LINE_LSB(st7571->startline),
-
-               ST7571_OSC_ON,
-               ST7571_SET_REGULATOR_REG(5),
-               ST7571_SET_CONTRAST_MSB,
-               ST7571_SET_CONTRAST_LSB(0x33),
-               ST7571_SET_LCD_BIAS(0x04),
-               ST7571_SET_DISPLAY_DUTY_MSB,
-               ST7571_SET_DISPLAY_DUTY_LSB(st7571->nlines),
-
-               ST7571_SET_POWER(0x4),  /* Power Control, VC: ON, VR: OFF, VF: OFF */
-               ST7571_SET_POWER(0x6),  /* Power Control, VC: ON, VR: ON, VF: OFF */
-               ST7571_SET_POWER(0x7),  /* Power Control, VC: ON, VR: ON, VF: ON */
-
-               ST7571_COMMAND_SET_3,
-               ST7571_SET_COLOR_MODE(st7571->pformat->mode),
-               ST7571_COMMAND_SET_NORMAL,
-
-               ST7571_SET_REVERSE(0),
-               ST7571_SET_ENTIRE_DISPLAY_ON(0),
-       };
-
-       /* Perform a reset before initializing the controller */
-       st7571_reset(st7571);
-
-       return st7571_send_command_list(st7571, commands, ARRAY_SIZE(commands));
-}
-
-static int st7571_probe(struct i2c_client *client)
-{
-       struct st7571_device *st7571;
-       struct drm_device *dev;
-       int ret;
-
-       st7571 = devm_drm_dev_alloc(&client->dev, &st7571_driver,
-                                   struct st7571_device, dev);
-       if (IS_ERR(st7571))
-               return PTR_ERR(st7571);
-
-       dev = &st7571->dev;
-       st7571->client = client;
-       i2c_set_clientdata(client, st7571);
-       st7571->pdata = device_get_match_data(&client->dev);
-
-       ret = st7571_parse_dt(st7571);
-       if (ret)
-               return ret;
-
-       ret = st7571_validate_parameters(st7571);
-       if (ret)
-               return ret;
-
-       st7571->mode = st7571_mode(st7571);
-
-       /*
-        * The hardware design could make it hard to detect a NAK on the I2C bus.
-        * If the adapter does not support protocol mangling do
-        * not set the I2C_M_IGNORE_NAK flag at the expense * of possible
-        * cruft in the logs.
-        */
-       if (i2c_check_functionality(client->adapter, I2C_FUNC_PROTOCOL_MANGLING))
-               st7571->ignore_nak = true;
-
-       st7571->regmap = devm_regmap_init(&client->dev, &st7571_regmap_bus,
-                                         client, &st7571_regmap_config);
-       if (IS_ERR(st7571->regmap)) {
-               return dev_err_probe(&client->dev, PTR_ERR(st7571->regmap),
-                                    "Failed to initialize regmap\n");
-       }
-
-       st7571->hwbuf = devm_kzalloc(&client->dev,
-                                    (st7571->nlines * st7571->ncols * st7571->bpp) / 8,
-                                    GFP_KERNEL);
-       if (!st7571->hwbuf)
-               return -ENOMEM;
-
-       st7571->row = devm_kzalloc(&client->dev,
-                                  (st7571->ncols * st7571->bpp),
-                                  GFP_KERNEL);
-       if (!st7571->row)
-               return -ENOMEM;
-
-       ret = st7571_mode_config_init(st7571);
-       if (ret)
-               return dev_err_probe(&client->dev, ret,
-                                    "Failed to initialize mode config\n");
-
-       ret = st7571_plane_init(st7571, st7571->pformat);
-       if (ret)
-               return dev_err_probe(&client->dev, ret,
-                                    "Failed to initialize primary plane\n");
-
-       ret = st7571_crtc_init(st7571);
-       if (ret < 0)
-               return dev_err_probe(&client->dev, ret,
-                                    "Failed to initialize CRTC\n");
-
-       ret = st7571_encoder_init(st7571);
-       if (ret < 0)
-               return dev_err_probe(&client->dev, ret,
-                                    "Failed to initialize encoder\n");
-
-       ret = st7571_connector_init(st7571);
-       if (ret < 0)
-               return dev_err_probe(&client->dev, ret,
-                                    "Failed to initialize connector\n");
-
-       drm_mode_config_reset(dev);
-
-       ret = drm_dev_register(dev, 0);
-       if (ret)
-               return dev_err_probe(&client->dev, ret,
-                                    "Failed to register DRM device\n");
-
-       drm_client_setup(dev, NULL);
-       return 0;
-}
-
-static void st7571_remove(struct i2c_client *client)
-{
-       struct st7571_device *st7571 = i2c_get_clientdata(client);
-
-       drm_dev_unplug(&st7571->dev);
-}
-
-struct st7571_panel_data st7571_config = {
-       .init = st7571_lcd_init,
-       .constraints = {
-               .min_nlines = 1,
-               .max_nlines = 128,
-               .min_ncols = 128,
-               .max_ncols = 128,
-               .support_grayscale = true,
-       },
-};
-
-static const struct of_device_id st7571_of_match[] = {
-       { .compatible = "sitronix,st7571", .data = &st7571_config },
-       {},
-};
-MODULE_DEVICE_TABLE(of, st7571_of_match);
-
-static const struct i2c_device_id st7571_id[] = {
-       { "st7571", 0 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, st7571_id);
-
-static struct i2c_driver st7571_i2c_driver = {
-       .driver = {
-               .name = "st7571",
-               .of_match_table = st7571_of_match,
-       },
-       .probe = st7571_probe,
-       .remove = st7571_remove,
-       .id_table = st7571_id,
-};
-
-module_i2c_driver(st7571_i2c_driver);
-
-MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
-MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/tiny/st7586.c b/drivers/gpu/drm/tiny/st7586.c
deleted file mode 100644 (file)
index a29672d..0000000
+++ /dev/null
@@ -1,407 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * DRM driver for Sitronix ST7586 panels
- *
- * Copyright 2017 David Lechner <david@lechnology.com>
- */
-
-#include <linux/delay.h>
-#include <linux/gpio/consumer.h>
-#include <linux/module.h>
-#include <linux/property.h>
-#include <linux/spi/spi.h>
-#include <video/mipi_display.h>
-
-#include <drm/clients/drm_client_setup.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_damage_helper.h>
-#include <drm/drm_drv.h>
-#include <drm/drm_fb_dma_helper.h>
-#include <drm/drm_fbdev_dma.h>
-#include <drm/drm_format_helper.h>
-#include <drm/drm_framebuffer.h>
-#include <drm/drm_gem_atomic_helper.h>
-#include <drm/drm_gem_dma_helper.h>
-#include <drm/drm_gem_framebuffer_helper.h>
-#include <drm/drm_managed.h>
-#include <drm/drm_mipi_dbi.h>
-#include <drm/drm_rect.h>
-
-/* controller-specific commands */
-#define ST7586_DISP_MODE_GRAY  0x38
-#define ST7586_DISP_MODE_MONO  0x39
-#define ST7586_ENABLE_DDRAM    0x3a
-#define ST7586_SET_DISP_DUTY   0xb0
-#define ST7586_SET_PART_DISP   0xb4
-#define ST7586_SET_NLINE_INV   0xb5
-#define ST7586_SET_VOP         0xc0
-#define ST7586_SET_BIAS_SYSTEM 0xc3
-#define ST7586_SET_BOOST_LEVEL 0xc4
-#define ST7586_SET_VOP_OFFSET  0xc7
-#define ST7586_ENABLE_ANALOG   0xd0
-#define ST7586_AUTO_READ_CTRL  0xd7
-#define ST7586_OTP_RW_CTRL     0xe0
-#define ST7586_OTP_CTRL_OUT    0xe1
-#define ST7586_OTP_READ                0xe3
-
-#define ST7586_DISP_CTRL_MX    BIT(6)
-#define ST7586_DISP_CTRL_MY    BIT(7)
-
-/*
- * The ST7586 controller has an unusual pixel format where 2bpp grayscale is
- * packed 3 pixels per byte with the first two pixels using 3 bits and the 3rd
- * pixel using only 2 bits.
- *
- * |  D7  |  D6  |  D5  ||      |      || 2bpp |
- * | (D4) | (D3) | (D2) ||  D1  |  D0  || GRAY |
- * +------+------+------++------+------++------+
- * |  1   |  1   |  1   ||  1   |  1   || 0  0 | black
- * |  1   |  0   |  0   ||  1   |  0   || 0  1 | dark gray
- * |  0   |  1   |  0   ||  0   |  1   || 1  0 | light gray
- * |  0   |  0   |  0   ||  0   |  0   || 1  1 | white
- */
-
-static const u8 st7586_lookup[] = { 0x7, 0x4, 0x2, 0x0 };
-
-static void st7586_xrgb8888_to_gray332(u8 *dst, void *vaddr,
-                                      struct drm_framebuffer *fb,
-                                      struct drm_rect *clip,
-                                      struct drm_format_conv_state *fmtcnv_state)
-{
-       size_t len = (clip->x2 - clip->x1) * (clip->y2 - clip->y1);
-       unsigned int x, y;
-       u8 *src, *buf, val;
-       struct iosys_map dst_map, vmap;
-
-       buf = kmalloc(len, GFP_KERNEL);
-       if (!buf)
-               return;
-
-       iosys_map_set_vaddr(&dst_map, buf);
-       iosys_map_set_vaddr(&vmap, vaddr);
-       drm_fb_xrgb8888_to_gray8(&dst_map, NULL, &vmap, fb, clip, fmtcnv_state);
-       src = buf;
-
-       for (y = clip->y1; y < clip->y2; y++) {
-               for (x = clip->x1; x < clip->x2; x += 3) {
-                       val = st7586_lookup[*src++ >> 6] << 5;
-                       val |= st7586_lookup[*src++ >> 6] << 2;
-                       val |= st7586_lookup[*src++ >> 6] >> 1;
-                       *dst++ = val;
-               }
-       }
-
-       kfree(buf);
-}
-
-static int st7586_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuffer *fb,
-                          struct drm_rect *clip, struct drm_format_conv_state *fmtcnv_state)
-{
-       int ret;
-
-       ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
-       if (ret)
-               return ret;
-
-       st7586_xrgb8888_to_gray332(dst, src->vaddr, fb, clip, fmtcnv_state);
-
-       drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
-
-       return 0;
-}
-
-static void st7586_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb,
-                           struct drm_rect *rect, struct drm_format_conv_state *fmtcnv_state)
-{
-       struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev);
-       struct mipi_dbi *dbi = &dbidev->dbi;
-       int start, end, ret = 0;
-
-       /* 3 pixels per byte, so grow clip to nearest multiple of 3 */
-       rect->x1 = rounddown(rect->x1, 3);
-       rect->x2 = roundup(rect->x2, 3);
-
-       DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
-
-       ret = st7586_buf_copy(dbidev->tx_buf, src, fb, rect, fmtcnv_state);
-       if (ret)
-               goto err_msg;
-
-       /* Pixels are packed 3 per byte */
-       start = rect->x1 / 3;
-       end = rect->x2 / 3;
-
-       mipi_dbi_command(dbi, MIPI_DCS_SET_COLUMN_ADDRESS,
-                        (start >> 8) & 0xFF, start & 0xFF,
-                        (end >> 8) & 0xFF, (end - 1) & 0xFF);
-       mipi_dbi_command(dbi, MIPI_DCS_SET_PAGE_ADDRESS,
-                        (rect->y1 >> 8) & 0xFF, rect->y1 & 0xFF,
-                        (rect->y2 >> 8) & 0xFF, (rect->y2 - 1) & 0xFF);
-
-       ret = mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START,
-                                  (u8 *)dbidev->tx_buf,
-                                  (end - start) * (rect->y2 - rect->y1));
-err_msg:
-       if (ret)
-               dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
-}
-
-static void st7586_pipe_update(struct drm_simple_display_pipe *pipe,
-                              struct drm_plane_state *old_state)
-{
-       struct drm_plane_state *state = pipe->plane.state;
-       struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
-       struct drm_framebuffer *fb = state->fb;
-       struct drm_rect rect;
-       int idx;
-
-       if (!pipe->crtc.state->active)
-               return;
-
-       if (!drm_dev_enter(fb->dev, &idx))
-               return;
-
-       if (drm_atomic_helper_damage_merged(old_state, state, &rect))
-               st7586_fb_dirty(&shadow_plane_state->data[0], fb, &rect,
-                               &shadow_plane_state->fmtcnv_state);
-
-       drm_dev_exit(idx);
-}
-
-static void st7586_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 drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
-       struct drm_framebuffer *fb = plane_state->fb;
-       struct mipi_dbi *dbi = &dbidev->dbi;
-       struct drm_rect rect = {
-               .x1 = 0,
-               .x2 = fb->width,
-               .y1 = 0,
-               .y2 = fb->height,
-       };
-       int idx, ret;
-       u8 addr_mode;
-
-       if (!drm_dev_enter(pipe->crtc.dev, &idx))
-               return;
-
-       DRM_DEBUG_KMS("\n");
-
-       ret = mipi_dbi_poweron_reset(dbidev);
-       if (ret)
-               goto out_exit;
-
-       mipi_dbi_command(dbi, ST7586_AUTO_READ_CTRL, 0x9f);
-       mipi_dbi_command(dbi, ST7586_OTP_RW_CTRL, 0x00);
-
-       msleep(10);
-
-       mipi_dbi_command(dbi, ST7586_OTP_READ);
-
-       msleep(20);
-
-       mipi_dbi_command(dbi, ST7586_OTP_CTRL_OUT);
-       mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
-       mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
-
-       msleep(50);
-
-       mipi_dbi_command(dbi, ST7586_SET_VOP_OFFSET, 0x00);
-       mipi_dbi_command(dbi, ST7586_SET_VOP, 0xe3, 0x00);
-       mipi_dbi_command(dbi, ST7586_SET_BIAS_SYSTEM, 0x02);
-       mipi_dbi_command(dbi, ST7586_SET_BOOST_LEVEL, 0x04);
-       mipi_dbi_command(dbi, ST7586_ENABLE_ANALOG, 0x1d);
-       mipi_dbi_command(dbi, ST7586_SET_NLINE_INV, 0x00);
-       mipi_dbi_command(dbi, ST7586_DISP_MODE_GRAY);
-       mipi_dbi_command(dbi, ST7586_ENABLE_DDRAM, 0x02);
-
-       switch (dbidev->rotation) {
-       default:
-               addr_mode = 0x00;
-               break;
-       case 90:
-               addr_mode = ST7586_DISP_CTRL_MY;
-               break;
-       case 180:
-               addr_mode = ST7586_DISP_CTRL_MX | ST7586_DISP_CTRL_MY;
-               break;
-       case 270:
-               addr_mode = ST7586_DISP_CTRL_MX;
-               break;
-       }
-       mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
-
-       mipi_dbi_command(dbi, ST7586_SET_DISP_DUTY, 0x7f);
-       mipi_dbi_command(dbi, ST7586_SET_PART_DISP, 0xa0);
-       mipi_dbi_command(dbi, MIPI_DCS_SET_PARTIAL_ROWS, 0x00, 0x00, 0x00, 0x77);
-       mipi_dbi_command(dbi, MIPI_DCS_EXIT_INVERT_MODE);
-
-       msleep(100);
-
-       st7586_fb_dirty(&shadow_plane_state->data[0], fb, &rect,
-                       &shadow_plane_state->fmtcnv_state);
-
-       mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
-out_exit:
-       drm_dev_exit(idx);
-}
-
-static void st7586_pipe_disable(struct drm_simple_display_pipe *pipe)
-{
-       struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
-
-       /*
-        * This callback is not protected by drm_dev_enter/exit since we want to
-        * turn off the display on regular driver unload. It's highly unlikely
-        * that the underlying SPI controller is gone should this be called after
-        * unplug.
-        */
-
-       DRM_DEBUG_KMS("\n");
-
-       mipi_dbi_command(&dbidev->dbi, MIPI_DCS_SET_DISPLAY_OFF);
-}
-
-static const u32 st7586_formats[] = {
-       DRM_FORMAT_XRGB8888,
-};
-
-static const struct drm_simple_display_pipe_funcs st7586_pipe_funcs = {
-       .mode_valid     = mipi_dbi_pipe_mode_valid,
-       .enable         = st7586_pipe_enable,
-       .disable        = st7586_pipe_disable,
-       .update         = st7586_pipe_update,
-       .begin_fb_access = mipi_dbi_pipe_begin_fb_access,
-       .end_fb_access  = mipi_dbi_pipe_end_fb_access,
-       .reset_plane    = mipi_dbi_pipe_reset_plane,
-       .duplicate_plane_state = mipi_dbi_pipe_duplicate_plane_state,
-       .destroy_plane_state = mipi_dbi_pipe_destroy_plane_state,
-};
-
-static const struct drm_display_mode st7586_mode = {
-       DRM_SIMPLE_MODE(178, 128, 37, 27),
-};
-
-DEFINE_DRM_GEM_DMA_FOPS(st7586_fops);
-
-static const struct drm_driver st7586_driver = {
-       .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
-       .fops                   = &st7586_fops,
-       DRM_GEM_DMA_DRIVER_OPS_VMAP,
-       DRM_FBDEV_DMA_DRIVER_OPS,
-       .debugfs_init           = mipi_dbi_debugfs_init,
-       .name                   = "st7586",
-       .desc                   = "Sitronix ST7586",
-       .major                  = 1,
-       .minor                  = 0,
-};
-
-static const struct of_device_id st7586_of_match[] = {
-       { .compatible = "lego,ev3-lcd" },
-       {},
-};
-MODULE_DEVICE_TABLE(of, st7586_of_match);
-
-static const struct spi_device_id st7586_id[] = {
-       { "ev3-lcd", 0 },
-       { },
-};
-MODULE_DEVICE_TABLE(spi, st7586_id);
-
-static int st7586_probe(struct spi_device *spi)
-{
-       struct device *dev = &spi->dev;
-       struct mipi_dbi_dev *dbidev;
-       struct drm_device *drm;
-       struct mipi_dbi *dbi;
-       struct gpio_desc *a0;
-       u32 rotation = 0;
-       size_t bufsize;
-       int ret;
-
-       dbidev = devm_drm_dev_alloc(dev, &st7586_driver,
-                                   struct mipi_dbi_dev, drm);
-       if (IS_ERR(dbidev))
-               return PTR_ERR(dbidev);
-
-       dbi = &dbidev->dbi;
-       drm = &dbidev->drm;
-
-       bufsize = (st7586_mode.vdisplay + 2) / 3 * st7586_mode.hdisplay;
-
-       dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
-       if (IS_ERR(dbi->reset))
-               return dev_err_probe(dev, PTR_ERR(dbi->reset), "Failed to get GPIO 'reset'\n");
-
-       a0 = devm_gpiod_get(dev, "a0", GPIOD_OUT_LOW);
-       if (IS_ERR(a0))
-               return dev_err_probe(dev, PTR_ERR(a0), "Failed to get GPIO 'a0'\n");
-
-       device_property_read_u32(dev, "rotation", &rotation);
-
-       ret = mipi_dbi_spi_init(spi, dbi, a0);
-       if (ret)
-               return ret;
-
-       /* Cannot read from this controller via SPI */
-       dbi->read_commands = NULL;
-
-       ret = mipi_dbi_dev_init_with_formats(dbidev, &st7586_pipe_funcs,
-                                            st7586_formats, ARRAY_SIZE(st7586_formats),
-                                            &st7586_mode, rotation, bufsize);
-       if (ret)
-               return ret;
-
-       /*
-        * we are using 8-bit data, so we are not actually swapping anything,
-        * but setting mipi->swap_bytes makes mipi_dbi_typec3_command() do the
-        * right thing and not use 16-bit transfers (which results in swapped
-        * bytes on little-endian systems and causes out of order data to be
-        * sent to the display).
-        */
-       dbi->swap_bytes = true;
-
-       drm_mode_config_reset(drm);
-
-       ret = drm_dev_register(drm, 0);
-       if (ret)
-               return ret;
-
-       spi_set_drvdata(spi, drm);
-
-       drm_client_setup(drm, NULL);
-
-       return 0;
-}
-
-static void st7586_remove(struct spi_device *spi)
-{
-       struct drm_device *drm = spi_get_drvdata(spi);
-
-       drm_dev_unplug(drm);
-       drm_atomic_helper_shutdown(drm);
-}
-
-static void st7586_shutdown(struct spi_device *spi)
-{
-       drm_atomic_helper_shutdown(spi_get_drvdata(spi));
-}
-
-static struct spi_driver st7586_spi_driver = {
-       .driver = {
-               .name = "st7586",
-               .of_match_table = st7586_of_match,
-       },
-       .id_table = st7586_id,
-       .probe = st7586_probe,
-       .remove = st7586_remove,
-       .shutdown = st7586_shutdown,
-};
-module_spi_driver(st7586_spi_driver);
-
-MODULE_DESCRIPTION("Sitronix ST7586 DRM driver");
-MODULE_AUTHOR("David Lechner <david@lechnology.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/tiny/st7735r.c b/drivers/gpu/drm/tiny/st7735r.c
deleted file mode 100644 (file)
index 1d60f6e..0000000
+++ /dev/null
@@ -1,277 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * DRM driver for display panels connected to a Sitronix ST7715R or ST7735R
- * display controller in SPI mode.
- *
- * Copyright 2017 David Lechner <david@lechnology.com>
- * Copyright (C) 2019 Glider bvba
- */
-
-#include <linux/backlight.h>
-#include <linux/delay.h>
-#include <linux/dma-buf.h>
-#include <linux/gpio/consumer.h>
-#include <linux/module.h>
-#include <linux/property.h>
-#include <linux/spi/spi.h>
-#include <video/mipi_display.h>
-
-#include <drm/clients/drm_client_setup.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_drv.h>
-#include <drm/drm_fbdev_dma.h>
-#include <drm/drm_gem_atomic_helper.h>
-#include <drm/drm_gem_dma_helper.h>
-#include <drm/drm_managed.h>
-#include <drm/drm_mipi_dbi.h>
-
-#define ST7735R_FRMCTR1                0xb1
-#define ST7735R_FRMCTR2                0xb2
-#define ST7735R_FRMCTR3                0xb3
-#define ST7735R_INVCTR         0xb4
-#define ST7735R_PWCTR1         0xc0
-#define ST7735R_PWCTR2         0xc1
-#define ST7735R_PWCTR3         0xc2
-#define ST7735R_PWCTR4         0xc3
-#define ST7735R_PWCTR5         0xc4
-#define ST7735R_VMCTR1         0xc5
-#define ST7735R_GAMCTRP1       0xe0
-#define ST7735R_GAMCTRN1       0xe1
-
-#define ST7735R_MY     BIT(7)
-#define ST7735R_MX     BIT(6)
-#define ST7735R_MV     BIT(5)
-#define ST7735R_RGB    BIT(3)
-
-struct st7735r_cfg {
-       const struct drm_display_mode mode;
-       unsigned int left_offset;
-       unsigned int top_offset;
-       unsigned int write_only:1;
-       unsigned int rgb:1;             /* RGB (vs. BGR) */
-};
-
-struct st7735r_priv {
-       struct mipi_dbi_dev dbidev;     /* Must be first for .release() */
-       const struct st7735r_cfg *cfg;
-};
-
-static void st7735r_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 st7735r_priv *priv = container_of(dbidev, struct st7735r_priv,
-                                                dbidev);
-       struct mipi_dbi *dbi = &dbidev->dbi;
-       int ret, idx;
-       u8 addr_mode;
-
-       if (!drm_dev_enter(pipe->crtc.dev, &idx))
-               return;
-
-       DRM_DEBUG_KMS("\n");
-
-       ret = mipi_dbi_poweron_reset(dbidev);
-       if (ret)
-               goto out_exit;
-
-       msleep(150);
-
-       mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
-       msleep(500);
-
-       mipi_dbi_command(dbi, ST7735R_FRMCTR1, 0x01, 0x2c, 0x2d);
-       mipi_dbi_command(dbi, ST7735R_FRMCTR2, 0x01, 0x2c, 0x2d);
-       mipi_dbi_command(dbi, ST7735R_FRMCTR3, 0x01, 0x2c, 0x2d, 0x01, 0x2c,
-                        0x2d);
-       mipi_dbi_command(dbi, ST7735R_INVCTR, 0x07);
-       mipi_dbi_command(dbi, ST7735R_PWCTR1, 0xa2, 0x02, 0x84);
-       mipi_dbi_command(dbi, ST7735R_PWCTR2, 0xc5);
-       mipi_dbi_command(dbi, ST7735R_PWCTR3, 0x0a, 0x00);
-       mipi_dbi_command(dbi, ST7735R_PWCTR4, 0x8a, 0x2a);
-       mipi_dbi_command(dbi, ST7735R_PWCTR5, 0x8a, 0xee);
-       mipi_dbi_command(dbi, ST7735R_VMCTR1, 0x0e);
-       mipi_dbi_command(dbi, MIPI_DCS_EXIT_INVERT_MODE);
-       switch (dbidev->rotation) {
-       default:
-               addr_mode = ST7735R_MX | ST7735R_MY;
-               break;
-       case 90:
-               addr_mode = ST7735R_MX | ST7735R_MV;
-               break;
-       case 180:
-               addr_mode = 0;
-               break;
-       case 270:
-               addr_mode = ST7735R_MY | ST7735R_MV;
-               break;
-       }
-
-       if (priv->cfg->rgb)
-               addr_mode |= ST7735R_RGB;
-
-       mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
-       mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT,
-                        MIPI_DCS_PIXEL_FMT_16BIT);
-       mipi_dbi_command(dbi, ST7735R_GAMCTRP1, 0x02, 0x1c, 0x07, 0x12, 0x37,
-                        0x32, 0x29, 0x2d, 0x29, 0x25, 0x2b, 0x39, 0x00, 0x01,
-                        0x03, 0x10);
-       mipi_dbi_command(dbi, ST7735R_GAMCTRN1, 0x03, 0x1d, 0x07, 0x06, 0x2e,
-                        0x2c, 0x29, 0x2d, 0x2e, 0x2e, 0x37, 0x3f, 0x00, 0x00,
-                        0x02, 0x10);
-       mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
-
-       msleep(100);
-
-       mipi_dbi_command(dbi, MIPI_DCS_ENTER_NORMAL_MODE);
-
-       msleep(20);
-
-       mipi_dbi_enable_flush(dbidev, crtc_state, plane_state);
-out_exit:
-       drm_dev_exit(idx);
-}
-
-static const struct drm_simple_display_pipe_funcs st7735r_pipe_funcs = {
-       DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS(st7735r_pipe_enable),
-};
-
-static const struct st7735r_cfg jd_t18003_t01_cfg = {
-       .mode           = { DRM_SIMPLE_MODE(128, 160, 28, 35) },
-       /* Cannot read from Adafruit 1.8" display via SPI */
-       .write_only     = true,
-};
-
-static const struct st7735r_cfg rh128128t_cfg = {
-       .mode           = { DRM_SIMPLE_MODE(128, 128, 25, 26) },
-       .left_offset    = 2,
-       .top_offset     = 3,
-       .rgb            = true,
-};
-
-DEFINE_DRM_GEM_DMA_FOPS(st7735r_fops);
-
-static const struct drm_driver st7735r_driver = {
-       .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
-       .fops                   = &st7735r_fops,
-       DRM_GEM_DMA_DRIVER_OPS_VMAP,
-       DRM_FBDEV_DMA_DRIVER_OPS,
-       .debugfs_init           = mipi_dbi_debugfs_init,
-       .name                   = "st7735r",
-       .desc                   = "Sitronix ST7735R",
-       .major                  = 1,
-       .minor                  = 0,
-};
-
-static const struct of_device_id st7735r_of_match[] = {
-       { .compatible = "jianda,jd-t18003-t01", .data = &jd_t18003_t01_cfg },
-       { .compatible = "okaya,rh128128t", .data = &rh128128t_cfg },
-       { },
-};
-MODULE_DEVICE_TABLE(of, st7735r_of_match);
-
-static const struct spi_device_id st7735r_id[] = {
-       { "jd-t18003-t01", (uintptr_t)&jd_t18003_t01_cfg },
-       { "rh128128t", (uintptr_t)&rh128128t_cfg },
-       { },
-};
-MODULE_DEVICE_TABLE(spi, st7735r_id);
-
-static int st7735r_probe(struct spi_device *spi)
-{
-       struct device *dev = &spi->dev;
-       const struct st7735r_cfg *cfg;
-       struct mipi_dbi_dev *dbidev;
-       struct st7735r_priv *priv;
-       struct drm_device *drm;
-       struct mipi_dbi *dbi;
-       struct gpio_desc *dc;
-       u32 rotation = 0;
-       int ret;
-
-       cfg = device_get_match_data(&spi->dev);
-       if (!cfg)
-               cfg = (void *)spi_get_device_id(spi)->driver_data;
-
-       priv = devm_drm_dev_alloc(dev, &st7735r_driver,
-                                 struct st7735r_priv, dbidev.drm);
-       if (IS_ERR(priv))
-               return PTR_ERR(priv);
-
-       dbidev = &priv->dbidev;
-       priv->cfg = cfg;
-
-       dbi = &dbidev->dbi;
-       drm = &dbidev->drm;
-
-       dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
-       if (IS_ERR(dbi->reset))
-               return dev_err_probe(dev, PTR_ERR(dbi->reset), "Failed to get GPIO 'reset'\n");
-
-       dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW);
-       if (IS_ERR(dc))
-               return dev_err_probe(dev, PTR_ERR(dc), "Failed to get GPIO 'dc'\n");
-
-       dbidev->backlight = devm_of_find_backlight(dev);
-       if (IS_ERR(dbidev->backlight))
-               return PTR_ERR(dbidev->backlight);
-
-       device_property_read_u32(dev, "rotation", &rotation);
-
-       ret = mipi_dbi_spi_init(spi, dbi, dc);
-       if (ret)
-               return ret;
-
-       if (cfg->write_only)
-               dbi->read_commands = NULL;
-
-       dbidev->left_offset = cfg->left_offset;
-       dbidev->top_offset = cfg->top_offset;
-
-       ret = mipi_dbi_dev_init(dbidev, &st7735r_pipe_funcs, &cfg->mode,
-                               rotation);
-       if (ret)
-               return ret;
-
-       drm_mode_config_reset(drm);
-
-       ret = drm_dev_register(drm, 0);
-       if (ret)
-               return ret;
-
-       spi_set_drvdata(spi, drm);
-
-       drm_client_setup(drm, NULL);
-
-       return 0;
-}
-
-static void st7735r_remove(struct spi_device *spi)
-{
-       struct drm_device *drm = spi_get_drvdata(spi);
-
-       drm_dev_unplug(drm);
-       drm_atomic_helper_shutdown(drm);
-}
-
-static void st7735r_shutdown(struct spi_device *spi)
-{
-       drm_atomic_helper_shutdown(spi_get_drvdata(spi));
-}
-
-static struct spi_driver st7735r_spi_driver = {
-       .driver = {
-               .name = "st7735r",
-               .of_match_table = st7735r_of_match,
-       },
-       .id_table = st7735r_id,
-       .probe = st7735r_probe,
-       .remove = st7735r_remove,
-       .shutdown = st7735r_shutdown,
-};
-module_spi_driver(st7735r_spi_driver);
-
-MODULE_DESCRIPTION("Sitronix ST7735R DRM driver");
-MODULE_AUTHOR("David Lechner <david@lechnology.com>");
-MODULE_LICENSE("GPL");