[RFC 5/5] drm/tinydrm: Add support for several Adafruit TFT displays

Noralf Trønnes noralf at tronnes.org
Wed Mar 16 13:34:19 UTC 2016


Add support for Adafruit MIPI DBI compatible SPI displays:
1.8" Color TFT LCD display - ST7735R (#358)
2.2" Color TFT LCD display - HX8340BN, 9-bit (#797)
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        |  11 ++
 drivers/gpu/drm/tinydrm/Makefile       |   3 +
 drivers/gpu/drm/tinydrm/adafruit-tft.c | 256 +++++++++++++++++++++++++++++++++
 include/drm/tinydrm/ili9340.h          |  85 +++++++++++
 4 files changed, 355 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 a7929e0..649f311 100644
--- a/drivers/gpu/drm/tinydrm/Kconfig
+++ b/drivers/gpu/drm/tinydrm/Kconfig
@@ -13,4 +13,15 @@ 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)
+	    2.2" Color TFT LCD display - HX8340BN, 9-bit mode (#797)
+	    1.8" Color TFT LCD display - ST7735R (#358)
+
 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..09049f7
--- /dev/null
+++ b/drivers/gpu/drm/tinydrm/adafruit-tft.c
@@ -0,0 +1,256 @@
+#define DEBUG
+
+/*
+ * 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, ILI9340_SWRESET);
+	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, ILI9340_PIXSET, 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, ILI9340_GAMSET, 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, ILI9340_SLPOUT);
+	msleep(120);
+	lcdreg_writereg(reg, ILI9340_DISPON);
+
+	mipi_dbi_debug_dump_regs(reg);
+
+	return 0;
+}
+
+struct drm_panel_funcs adafruit_tft_1601_funcs = {
+	.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);
+
+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->deferred->defer_ms = 40;
+
+	spi_set_drvdata(spi, tdev);
+
+	return devm_tinydrm_register(dev, tdev);
+}
+
+static struct spi_driver adafruit_tft_spi_driver = {
+	.driver = {
+		.name = "ada-mipifb",
+		.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..ecd2122
--- /dev/null
+++ b/include/drm/tinydrm/ili9340.h
@@ -0,0 +1,85 @@
+/*
+ * 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_NOP        0x00
+#define ILI9340_SWRESET    0x01
+#define ILI9340_RDDIDIF    0x04
+#define ILI9340_RDDST      0x09
+#define ILI9340_RDDPM      0x0A
+#define ILI9340_RDDMADCTL  0x0B
+#define ILI9340_RDDCOLMOD  0x0C
+#define ILI9340_RDDIM      0x0D
+#define ILI9340_RDDSM      0x0E
+#define ILI9340_RDDSDR     0x0F
+
+#define ILI9340_SLPIN      0x10
+#define ILI9340_SLPOUT     0x11
+#define ILI9340_PTLON      0x12
+#define ILI9340_NORON      0x13
+
+#define ILI9340_DINVOFF    0x20
+#define ILI9340_DINVON     0x21
+#define ILI9340_GAMSET     0x26
+#define ILI9340_DISPOFF    0x28
+#define ILI9340_DISPON     0x29
+#define ILI9340_CASET      0x2A
+#define ILI9340_PASET      0x2B
+#define ILI9340_RAMWR      0x2C
+#define ILI9340_RGBSET     0x2D
+#define ILI9340_RAMRD      0x2E
+
+#define ILI9340_PLTAR      0x30
+#define ILI9340_VSCRDEF    0x33
+#define ILI9340_TEOFF      0x34
+#define ILI9340_TEON       0x35
+#define ILI9340_MADCTL     0x36
+#define ILI9340_VSCRSADD   0x37
+#define ILI9340_IDMOFF     0x38
+#define ILI9340_IDMON      0x39
+#define ILI9340_PIXSET     0x3A
+
+#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