[RFC v2 8/8] drm/tinydrm: Add support for several Adafruit TFT displays
Noralf Trønnes
noralf at tronnes.org
Fri Apr 8 17:05:10 UTC 2016
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
More information about the dri-devel
mailing list