Add support for Adafruit MIPI DBI compatible SPI displays:
2.8" PiTFT 320x240 TFT+Touchscreen for Raspberry Pi (#1601)

Signed-off-by: Noralf Trønnes <noralf at tronnes.org>
---
 drivers/gpu/drm/tinydrm/Kconfig        |   9 ++
 drivers/gpu/drm/tinydrm/Makefile       |   3 +
 drivers/gpu/drm/tinydrm/adafruit-tft.c | 257 +++++++++++++++++++++++++++++++++
 include/drm/tinydrm/ili9340.h          |  47 ++++++
 4 files changed, 316 insertions(+)
 create mode 100644 drivers/gpu/drm/tinydrm/adafruit-tft.c
 create mode 100644 include/drm/tinydrm/ili9340.h

diff --git a/drivers/gpu/drm/tinydrm/Kconfig b/drivers/gpu/drm/tinydrm/Kconfig
index 8c41eee..621be2f 100644
--- a/drivers/gpu/drm/tinydrm/Kconfig
+++ b/drivers/gpu/drm/tinydrm/Kconfig
@@ -13,4 +13,13 @@ menuconfig DRM_TINYDRM
 config TINYDRM_MIPI_DBI
        tristate

+config TINYDRM_ADAFRUIT_TFT
+       tristate "DRM driver for Adafruit SPI TFT displays"
+       depends on DRM_TINYDRM && SPI
+       select LCDREG_SPI
+       select TINYDRM_MIPI_DBI
+       help
+         DRM driver for the following Adafruit displays:
+           2.8" PiTFT 320x240 for Raspberry Pi - ILI9340 (#1601)
+
 source "drivers/gpu/drm/tinydrm/lcdreg/Kconfig"
diff --git a/drivers/gpu/drm/tinydrm/Makefile b/drivers/gpu/drm/tinydrm/Makefile
index 35ba822..3c00201 100644
--- a/drivers/gpu/drm/tinydrm/Makefile
+++ b/drivers/gpu/drm/tinydrm/Makefile
@@ -3,3 +3,6 @@ obj-$(CONFIG_LCDREG)                    += lcdreg/

 # Controllers
 obj-$(CONFIG_TINYDRM_MIPI_DBI)         += mipi-dbi.o
+
+# Displays
+obj-$(CONFIG_TINYDRM_ADAFRUIT_TFT)     += adafruit-tft.o
diff --git a/drivers/gpu/drm/tinydrm/adafruit-tft.c 
b/drivers/gpu/drm/tinydrm/adafruit-tft.c
new file mode 100644
index 0000000..20da98d
--- /dev/null
+++ b/drivers/gpu/drm/tinydrm/adafruit-tft.c
@@ -0,0 +1,257 @@
+/*
+ * DRM driver for Adafruit MIPI compatible SPI TFT displays
+ *
+ * Copyright 2016 Noralf Trønnes
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <drm/tinydrm/ili9340.h>
+#include <drm/tinydrm/lcdreg-spi.h>
+#include <drm/tinydrm/mipi-dbi.h>
+#include <drm/tinydrm/tinydrm.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <video/mipi_display.h>
+
+enum adafruit_tft_displays {
+       ADAFRUIT_1601 = 1601,
+       ADAFRUIT_797 = 797,
+       ADAFRUIT_358 = 358,
+};
+
+static u32 adafruit_tft_get_rotation(struct device *dev)
+{
+       u32 rotation = 0;
+
+       device_property_read_u32(dev, "rotation", &rotation);
+
+       return rotation;
+}
+
+static int adafruit_tft_1601_panel_prepare(struct drm_panel *panel)
+{
+       struct tinydrm_device *tdev = tinydrm_from_panel(panel);
+       struct lcdreg *reg = tdev->lcdreg;
+       u8 addr_mode;
+       int ret;
+
+       dev_dbg(tdev->base->dev, "%s\n", __func__);
+
+       if (tdev->regulator) {
+               ret = regulator_enable(tdev->regulator);
+               if (ret) {
+                       dev_err(tdev->base->dev,
+                               "Failed to enable regulator %d\n", ret);
+                       return ret;
+               }
+       }
+
+       mipi_dbi_debug_dump_regs(reg);
+
+       /* Avoid flicker by skipping setup if the bootloader has done it */
+       if (mipi_dbi_display_is_on(reg))
+               return 0;
+
+       lcdreg_reset(reg);
+       ret = lcdreg_writereg(reg, MIPI_DCS_SOFT_RESET);
+       if (ret) {
+               dev_err(tdev->base->dev, "Error writing lcdreg %d\n", ret);
+               return ret;
+       }
+
+       msleep(20);
+
+       /* Undocumented registers */
+       lcdreg_writereg(reg, 0xEF, 0x03, 0x80, 0x02);
+       lcdreg_writereg(reg, 0xCF, 0x00, 0xC1, 0x30);
+       lcdreg_writereg(reg, 0xED, 0x64, 0x03, 0x12, 0x81);
+       lcdreg_writereg(reg, 0xE8, 0x85, 0x00, 0x78);
+       lcdreg_writereg(reg, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02);
+       lcdreg_writereg(reg, 0xF7, 0x20);
+       lcdreg_writereg(reg, 0xEA, 0x00, 0x00);
+
+       lcdreg_writereg(reg, ILI9340_PWCTRL1, 0x23);
+       lcdreg_writereg(reg, ILI9340_PWCTRL2, 0x10);
+       lcdreg_writereg(reg, ILI9340_VMCTRL1, 0x3e, 0x28);
+       lcdreg_writereg(reg, ILI9340_VMCTRL2, 0x86);
+
+       lcdreg_writereg(reg, MIPI_DCS_SET_PIXEL_FORMAT, 0x55);
+       lcdreg_writereg(reg, ILI9340_FRMCTR1, 0x00, 0x18);
+       lcdreg_writereg(reg, ILI9340_DISCTRL, 0x08, 0x82, 0x27);
+
+       /* 3Gamma Function Disable */
+       lcdreg_writereg(reg, 0xF2, 0x00);
+
+       lcdreg_writereg(reg, MIPI_DCS_SET_GAMMA_CURVE, 0x01);
+       lcdreg_writereg(reg, ILI9340_PGAMCTRL,
+                       0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1,
+                       0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00);
+       lcdreg_writereg(reg, ILI9340_NGAMCTRL,
+                       0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1,
+                       0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F);
+
+       switch (adafruit_tft_get_rotation(reg->dev)) {
+       default:
+               addr_mode = ILI9340_MADCTL_MV | ILI9340_MADCTL_MY |
+                           ILI9340_MADCTL_MX;
+               break;
+       case 90:
+               addr_mode = ILI9340_MADCTL_MY;
+               break;
+       case 180:
+               addr_mode = ILI9340_MADCTL_MV;
+               break;
+       case 270:
+               addr_mode = ILI9340_MADCTL_MX;
+               break;
+       }
+       addr_mode |= ILI9340_MADCTL_BGR;
+       lcdreg_writereg(reg, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
+
+       lcdreg_writereg(reg, MIPI_DCS_EXIT_SLEEP_MODE);
+       msleep(120);
+       lcdreg_writereg(reg, MIPI_DCS_SET_DISPLAY_ON);
+
+       mipi_dbi_debug_dump_regs(reg);
+
+       return 0;
+}
+
+struct drm_panel_funcs adafruit_tft_1601_funcs = {
+       .get_modes = tinydrm_panel_get_modes,
+       .prepare = adafruit_tft_1601_panel_prepare,
+       .unprepare = mipi_dbi_panel_unprepare,
+       .enable = tinydrm_panel_enable_backlight,
+       .disable = tinydrm_panel_disable_backlight,
+};
+
+static const struct of_device_id adafruit_tft_of_match[] = {
+       { .compatible = "adafruit,tft1601", .data = (void *)ADAFRUIT_1601 },
+       { .compatible = "adafruit,tft797",  .data = (void *)ADAFRUIT_797 },
+       { .compatible = "adafruit,tft358",  .data = (void *)ADAFRUIT_358 },
+       {},
+};
+MODULE_DEVICE_TABLE(of, adafruit_tft_of_match);
+
+static const struct spi_device_id adafruit_tft_id[] = {
+       { "tft1601", ADAFRUIT_1601 },
+       { "tft797",  ADAFRUIT_797 },
+       { "tft358",  ADAFRUIT_358 },
+       { },
+};
+MODULE_DEVICE_TABLE(spi, adafruit_tft_id);
+
+TINYDRM_DRM_DRIVER(adafruit_tft, "adafruit-tft", "Adafruit TFT", "20160317");
+
+static int adafruit_tft_probe(struct spi_device *spi)
+{
+       const struct of_device_id *of_id;
+       struct lcdreg_spi_config cfg = {
+               .mode = LCDREG_SPI_4WIRE,
+       };
+       struct device *dev = &spi->dev;
+       struct tinydrm_device *tdev;
+       bool readable = false;
+       struct lcdreg *reg;
+       int id, ret;
+
+       of_id = of_match_device(adafruit_tft_of_match, dev);
+       if (of_id) {
+               id = (int)of_id->data;
+       } else {
+               const struct spi_device_id *spi_id = spi_get_device_id(spi);
+
+               if (!spi_id)
+                       return -EINVAL;
+
+               id = spi_id->driver_data;
+       }
+
+       tdev = devm_kzalloc(dev, sizeof(*tdev), GFP_KERNEL);
+       if (!tdev)
+               return -ENOMEM;
+
+       tdev->backlight = tinydrm_of_find_backlight(dev);
+       if (IS_ERR(tdev->backlight))
+               return PTR_ERR(tdev->backlight);
+
+       tdev->regulator = devm_regulator_get_optional(dev, "power");
+       if (IS_ERR(tdev->regulator)) {
+               if (PTR_ERR(tdev->regulator) != -ENODEV)
+                       return PTR_ERR(tdev->regulator);
+               tdev->regulator = NULL;
+       }
+
+       switch (id) {
+       case ADAFRUIT_1601:
+               readable = true;
+               cfg.mode = LCDREG_SPI_4WIRE;
+               tdev->width = 320;
+               tdev->height = 240;
+               tdev->panel.funcs = &adafruit_tft_1601_funcs;
+               break;
+       case ADAFRUIT_797:
+               cfg.mode = LCDREG_SPI_3WIRE;
+               tdev->width = 176;
+               tdev->height = 220;
+               /* TODO: tdev->panel.funcs = &adafruit_tft_797_funcs*/
+               break;
+       case ADAFRUIT_358:
+               tdev->width = 128;
+               tdev->height = 160;
+               /* TODO: tdev->panel.funcs = &adafruit_tft_358_funcs */
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       DRM_DEBUG_DRIVER("rotation = %u\n", adafruit_tft_get_rotation(dev));
+       switch (adafruit_tft_get_rotation(dev)) {
+       case 90:
+       case 270:
+               swap(tdev->width, tdev->height);
+               break;
+       }
+
+       reg = devm_lcdreg_spi_init(spi, &cfg);
+       if (IS_ERR(reg))
+               return PTR_ERR(reg);
+
+       reg->readable = readable;
+       tdev->lcdreg = reg;
+       ret = mipi_dbi_init(dev, tdev);
+       if (ret)
+               return ret;
+
+       /* TODO: Make configurable */
+       tdev->fbdefio_delay_ms = 40;
+
+       spi_set_drvdata(spi, tdev);
+
+       return devm_tinydrm_register(dev, tdev, &adafruit_tft);
+}
+
+static struct spi_driver adafruit_tft_spi_driver = {
+       .driver = {
+               .name = "adafruit-tft",
+               .owner = THIS_MODULE,
+               .of_match_table = adafruit_tft_of_match,
+               .pm = &tinydrm_simple_pm_ops,
+       },
+       .id_table = adafruit_tft_id,
+       .probe = adafruit_tft_probe,
+       .shutdown = tinydrm_spi_shutdown,
+};
+module_spi_driver(adafruit_tft_spi_driver);
+
+MODULE_DESCRIPTION("Adafruit MIPI compatible SPI displays");
+MODULE_AUTHOR("Noralf Trønnes");
+MODULE_LICENSE("GPL");
diff --git a/include/drm/tinydrm/ili9340.h b/include/drm/tinydrm/ili9340.h
new file mode 100644
index 0000000..c851c41
--- /dev/null
+++ b/include/drm/tinydrm/ili9340.h
@@ -0,0 +1,47 @@
+/*
+ * ILI9340 LCD controller
+ *
+ * Copyright 2016 Noralf Trønnes
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __LINUX_ILI9340_H
+#define __LINUX_ILI9340_H
+
+#define ILI9340_FRMCTR1    0xB1
+#define ILI9340_FRMCTR2    0xB2
+#define ILI9340_FRMCTR3    0xB3
+#define ILI9340_INVTR      0xB4
+#define ILI9340_DISCTRL    0xB6
+
+#define ILI9340_PWCTRL1    0xC0
+#define ILI9340_PWCTRL2    0xC1
+#define ILI9340_PWCTRL3    0xC2
+#define ILI9340_PWCTRL4    0xC3
+#define ILI9340_PWCTRL5    0xC4
+#define ILI9340_VMCTRL1    0xC5
+#define ILI9340_VMCTRL2    0xC7
+
+#define ILI9340_RDID1      0xDA
+#define ILI9340_RDID2      0xDB
+#define ILI9340_RDID3      0xDC
+#define ILI9340_RDID4      0xDD
+
+#define ILI9340_PGAMCTRL   0xE0
+#define ILI9340_NGAMCTRL   0xE1
+
+#define ILI9340_IFCTL      0xF6
+
+#define ILI9340_MADCTL_MH  BIT(2)
+#define ILI9340_MADCTL_BGR BIT(3)
+#define ILI9340_MADCTL_ML  BIT(4)
+#define ILI9340_MADCTL_MV  BIT(5)
+#define ILI9340_MADCTL_MX  BIT(6)
+#define ILI9340_MADCTL_MY  BIT(7)
+
+
+#endif /* __LINUX_ILI9340_H */
-- 
2.2.2

Reply via email to