mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-27 17:18:59 +00:00
20ea6adbf1
Build system: x86_64 Build-tested: bcm2708, bcm2709, bcm2710, bcm2711 Run-tested: bcm2708/RPiB+, bcm2709/RPi3B, bcm2710/RPi3B, bcm2711/RPi4B Signed-off-by: Marty Jones <mj8263788@gmail.com> Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
538 lines
15 KiB
Diff
538 lines
15 KiB
Diff
From fa01d26c4572ed40dfe867d3c488abcb1d9a9db4 Mon Sep 17 00:00:00 2001
|
|
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
|
|
Date: Wed, 5 Jan 2022 19:14:48 +0000
|
|
Subject: [PATCH] drm/panel: Add panel driver for Ilitek ILI9806E panel
|
|
|
|
The Ilitek ILI9806E driver is used in the Pimoroni HyperPixel4
|
|
and potentially other displays. Whilst it can support multiple
|
|
interfaces, this driver only accounts for SPI configuration and
|
|
DPI video data.
|
|
|
|
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
|
|
---
|
|
drivers/gpu/drm/panel/Kconfig | 11 +
|
|
drivers/gpu/drm/panel/Makefile | 1 +
|
|
drivers/gpu/drm/panel/panel-ilitek-ili9806e.c | 486 ++++++++++++++++++
|
|
3 files changed, 498 insertions(+)
|
|
create mode 100644 drivers/gpu/drm/panel/panel-ilitek-ili9806e.c
|
|
|
|
--- a/drivers/gpu/drm/panel/Kconfig
|
|
+++ b/drivers/gpu/drm/panel/Kconfig
|
|
@@ -139,6 +139,17 @@ config DRM_PANEL_ILITEK_ILI9341
|
|
QVGA (240x320) RGB panels. support serial & parallel rgb
|
|
interface.
|
|
|
|
+config DRM_PANEL_ILITEK_ILI9806E
|
|
+ tristate "Ilitek ILI9806E-based panels"
|
|
+ depends on OF && SPI
|
|
+ depends on DRM_KMS_HELPER
|
|
+ depends on DRM_KMS_CMA_HELPER
|
|
+ depends on BACKLIGHT_CLASS_DEVICE
|
|
+ select DRM_MIPI_DBI
|
|
+ help
|
|
+ Say Y if you want to enable support for panels based on the
|
|
+ Ilitek ILI9806e controller.
|
|
+
|
|
config DRM_PANEL_ILITEK_ILI9881C
|
|
tristate "Ilitek ILI9881C-based panels"
|
|
depends on OF
|
|
--- a/drivers/gpu/drm/panel/Makefile
|
|
+++ b/drivers/gpu/drm/panel/Makefile
|
|
@@ -12,6 +12,7 @@ obj-$(CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA
|
|
obj-$(CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D) += panel-feiyang-fy07024di26a30d.o
|
|
obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o
|
|
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o
|
|
+obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9806E) += panel-ilitek-ili9806e.o
|
|
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o
|
|
obj-$(CONFIG_DRM_PANEL_INNOLUX_EJ030NA) += panel-innolux-ej030na.o
|
|
obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e.c
|
|
@@ -0,0 +1,486 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-only
|
|
+/*
|
|
+ * Ilitek ILI9806E TFT LCD drm_panel driver.
|
|
+ *
|
|
+ * Copyright (C) 2022 Raspberry Pi Ltd
|
|
+ *
|
|
+ * Derived from drivers/drm/gpu/panel/panel-sitronix-st7789v.c
|
|
+ * Copyright (C) 2017 Free Electrons
|
|
+ */
|
|
+
|
|
+#include <drm/drm_modes.h>
|
|
+#include <drm/drm_panel.h>
|
|
+
|
|
+#include <linux/bitops.h>
|
|
+#include <linux/gpio/consumer.h>
|
|
+#include <linux/media-bus-format.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of_device.h>
|
|
+#include <linux/regmap.h>
|
|
+#include <linux/regulator/consumer.h>
|
|
+#include <linux/spi/spi.h>
|
|
+
|
|
+#include <video/mipi_display.h>
|
|
+#include <video/of_videomode.h>
|
|
+#include <video/videomode.h>
|
|
+
|
|
+struct ili9806 {
|
|
+ struct drm_panel panel;
|
|
+ struct spi_device *spi;
|
|
+ struct gpio_desc *reset;
|
|
+ struct regulator *power;
|
|
+ u32 bus_format;
|
|
+};
|
|
+
|
|
+#define ILI9806_DATA BIT(8)
|
|
+
|
|
+#define ILI9806_MAX_MSG_LEN 6
|
|
+
|
|
+struct ili9806e_msg {
|
|
+ unsigned int len;
|
|
+ u16 msg[ILI9806_MAX_MSG_LEN];
|
|
+};
|
|
+
|
|
+#define ILI9806_SET_PAGE(page) \
|
|
+ { \
|
|
+ .len = 6, \
|
|
+ .msg = { \
|
|
+ 0xFF, \
|
|
+ ILI9806_DATA | 0xFF, \
|
|
+ ILI9806_DATA | 0x98, \
|
|
+ ILI9806_DATA | 0x06, \
|
|
+ ILI9806_DATA | 0x04, \
|
|
+ ILI9806_DATA | (page) \
|
|
+ }, \
|
|
+ }
|
|
+
|
|
+#define ILI9806_SET_REG_PARAM(reg, data) \
|
|
+ { \
|
|
+ .len = 2, \
|
|
+ .msg = { \
|
|
+ (reg), \
|
|
+ ILI9806_DATA | (data), \
|
|
+ }, \
|
|
+ }
|
|
+
|
|
+#define ILI9806_SET_REG(reg) \
|
|
+ { \
|
|
+ .len = 1, \
|
|
+ .msg = { (reg) }, \
|
|
+ }
|
|
+
|
|
+static const struct ili9806e_msg panel_init[] = {
|
|
+ ILI9806_SET_PAGE(1),
|
|
+
|
|
+ /* interface mode
|
|
+ * SEPT_SDIO = 0 (spi interface transfer through SDA pin)
|
|
+ * SDO_STATUS = 1 (always output, but without output tri-state)
|
|
+ */
|
|
+ ILI9806_SET_REG_PARAM(0x08, 0x10),
|
|
+ /* display control
|
|
+ * VSPL = 1 (vertical sync polarity)
|
|
+ * HSPL = 0 (horizontal sync polarity)
|
|
+ * DPL = 0 (PCLK polarity)
|
|
+ * EPL = 1 (data enable polarity)
|
|
+ */
|
|
+ ILI9806_SET_REG_PARAM(0x21, 0x0d),
|
|
+ /* resolution control (0x02 = 480x800) */
|
|
+ ILI9806_SET_REG_PARAM(0x30, 0x02),
|
|
+ /* display inversion control (0x00 = column inversion) */
|
|
+ ILI9806_SET_REG_PARAM(0x31, 0x00),
|
|
+ /* power control
|
|
+ * EXB1T = 0 (internal charge pump)
|
|
+ * EXT_CPCK_SEL = 1 (pump clock control signal = output 2 x waveform)
|
|
+ * BT = 0 (DDVDH / DDVDL voltage = VCI x 2 / VCI x -2)
|
|
+ */
|
|
+ ILI9806_SET_REG_PARAM(0x40, 0x10),
|
|
+ /* power control
|
|
+ * DDVDH_CLP = 5.6 (DDVDH clamp leve)
|
|
+ * DDVDL_CLP = -5.6 (DDVDL clamp leve)
|
|
+ */
|
|
+ ILI9806_SET_REG_PARAM(0x41, 0x55),
|
|
+ /* power control
|
|
+ * VGH_CP = 2DDVDH - DDVDL (step up factor for VGH)
|
|
+ * VGL_CP = DDVDL + VCL - VCIP (step up factor for VGL)
|
|
+ */
|
|
+ ILI9806_SET_REG_PARAM(0x42, 0x02),
|
|
+ /* power control
|
|
+ * VGH_CLPEN = 0 (disable VGH clamp level)
|
|
+ * VGH_CLP = 9 (15.0 VGH clamp level - but this is disabled so not used?)
|
|
+ */
|
|
+ ILI9806_SET_REG_PARAM(0x43, 0x84),
|
|
+ /* power control
|
|
+ * VGL_CLPEN = 0 (disable VGL clamp level)
|
|
+ * VGL_CLP = 9 (-11.0 VGL clamp level - but this is disabled so not used?)
|
|
+ */
|
|
+ ILI9806_SET_REG_PARAM(0x44, 0x84),
|
|
+
|
|
+ /* power control
|
|
+ * VREG1OUT voltage for positive gamma?
|
|
+ */
|
|
+ ILI9806_SET_REG_PARAM(0x50, 0x78),
|
|
+ /* power control
|
|
+ * VREG2OUT voltage for negative gamma?
|
|
+ */
|
|
+ ILI9806_SET_REG_PARAM(0x51, 0x78),
|
|
+
|
|
+ ILI9806_SET_REG_PARAM(0x52, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x53, 0x77),
|
|
+ ILI9806_SET_REG_PARAM(0x57, 0x60),
|
|
+ ILI9806_SET_REG_PARAM(0x60, 0x07),
|
|
+ ILI9806_SET_REG_PARAM(0x61, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x62, 0x08),
|
|
+ ILI9806_SET_REG_PARAM(0x63, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0xA0, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0xA1, 0x07),
|
|
+ ILI9806_SET_REG_PARAM(0xA2, 0x0C),
|
|
+ ILI9806_SET_REG_PARAM(0xA3, 0x0B),
|
|
+ ILI9806_SET_REG_PARAM(0xA4, 0x03),
|
|
+ ILI9806_SET_REG_PARAM(0xA5, 0x07),
|
|
+ ILI9806_SET_REG_PARAM(0xA6, 0x06),
|
|
+ ILI9806_SET_REG_PARAM(0xA7, 0x04),
|
|
+ ILI9806_SET_REG_PARAM(0xA8, 0x08),
|
|
+ ILI9806_SET_REG_PARAM(0xA9, 0x0C),
|
|
+ ILI9806_SET_REG_PARAM(0xAA, 0x13),
|
|
+ ILI9806_SET_REG_PARAM(0xAB, 0x06),
|
|
+ ILI9806_SET_REG_PARAM(0xAC, 0x0D),
|
|
+ ILI9806_SET_REG_PARAM(0xAD, 0x19),
|
|
+ ILI9806_SET_REG_PARAM(0xAE, 0x10),
|
|
+ ILI9806_SET_REG_PARAM(0xAF, 0x00),
|
|
+ /* negative gamma control
|
|
+ * set the gray scale voltage to adjust the gamma characteristics of the panel
|
|
+ */
|
|
+ ILI9806_SET_REG_PARAM(0xC0, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0xC1, 0x07),
|
|
+ ILI9806_SET_REG_PARAM(0xC2, 0x0C),
|
|
+ ILI9806_SET_REG_PARAM(0xC3, 0x0B),
|
|
+ ILI9806_SET_REG_PARAM(0xC4, 0x03),
|
|
+ ILI9806_SET_REG_PARAM(0xC5, 0x07),
|
|
+ ILI9806_SET_REG_PARAM(0xC6, 0x07),
|
|
+ ILI9806_SET_REG_PARAM(0xC7, 0x04),
|
|
+ ILI9806_SET_REG_PARAM(0xC8, 0x08),
|
|
+ ILI9806_SET_REG_PARAM(0xC9, 0x0C),
|
|
+ ILI9806_SET_REG_PARAM(0xCA, 0x13),
|
|
+ ILI9806_SET_REG_PARAM(0xCB, 0x06),
|
|
+ ILI9806_SET_REG_PARAM(0xCC, 0x0D),
|
|
+ ILI9806_SET_REG_PARAM(0xCD, 0x18),
|
|
+ ILI9806_SET_REG_PARAM(0xCE, 0x10),
|
|
+ ILI9806_SET_REG_PARAM(0xCF, 0x00),
|
|
+
|
|
+ ILI9806_SET_PAGE(6),
|
|
+
|
|
+ ILI9806_SET_REG_PARAM(0x00, 0x20),
|
|
+ ILI9806_SET_REG_PARAM(0x01, 0x0A),
|
|
+ ILI9806_SET_REG_PARAM(0x02, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x03, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x04, 0x01),
|
|
+ ILI9806_SET_REG_PARAM(0x05, 0x01),
|
|
+ ILI9806_SET_REG_PARAM(0x06, 0x98),
|
|
+ ILI9806_SET_REG_PARAM(0x07, 0x06),
|
|
+ ILI9806_SET_REG_PARAM(0x08, 0x01),
|
|
+ ILI9806_SET_REG_PARAM(0x09, 0x80),
|
|
+ ILI9806_SET_REG_PARAM(0x0A, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x0B, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x0C, 0x01),
|
|
+ ILI9806_SET_REG_PARAM(0x0D, 0x01),
|
|
+ ILI9806_SET_REG_PARAM(0x0E, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x0F, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x10, 0xF0),
|
|
+ ILI9806_SET_REG_PARAM(0x11, 0xF4),
|
|
+ ILI9806_SET_REG_PARAM(0x12, 0x01),
|
|
+ ILI9806_SET_REG_PARAM(0x13, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x14, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x15, 0xC0),
|
|
+ ILI9806_SET_REG_PARAM(0x16, 0x08),
|
|
+ ILI9806_SET_REG_PARAM(0x17, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x18, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x19, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x1A, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x1B, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x1C, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x1D, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x20, 0x01),
|
|
+ ILI9806_SET_REG_PARAM(0x21, 0x23),
|
|
+ ILI9806_SET_REG_PARAM(0x22, 0x45),
|
|
+ ILI9806_SET_REG_PARAM(0x23, 0x67),
|
|
+ ILI9806_SET_REG_PARAM(0x24, 0x01),
|
|
+ ILI9806_SET_REG_PARAM(0x25, 0x23),
|
|
+ ILI9806_SET_REG_PARAM(0x26, 0x45),
|
|
+ ILI9806_SET_REG_PARAM(0x27, 0x67),
|
|
+ ILI9806_SET_REG_PARAM(0x30, 0x11),
|
|
+ ILI9806_SET_REG_PARAM(0x31, 0x11),
|
|
+ ILI9806_SET_REG_PARAM(0x32, 0x00),
|
|
+ ILI9806_SET_REG_PARAM(0x33, 0xEE),
|
|
+ ILI9806_SET_REG_PARAM(0x34, 0xFF),
|
|
+ ILI9806_SET_REG_PARAM(0x35, 0xBB),
|
|
+ ILI9806_SET_REG_PARAM(0x36, 0xAA),
|
|
+ ILI9806_SET_REG_PARAM(0x37, 0xDD),
|
|
+ ILI9806_SET_REG_PARAM(0x38, 0xCC),
|
|
+ ILI9806_SET_REG_PARAM(0x39, 0x66),
|
|
+ ILI9806_SET_REG_PARAM(0x3A, 0x77),
|
|
+ ILI9806_SET_REG_PARAM(0x3B, 0x22),
|
|
+ ILI9806_SET_REG_PARAM(0x3C, 0x22),
|
|
+ ILI9806_SET_REG_PARAM(0x3D, 0x22),
|
|
+ ILI9806_SET_REG_PARAM(0x3E, 0x22),
|
|
+ ILI9806_SET_REG_PARAM(0x3F, 0x22),
|
|
+ ILI9806_SET_REG_PARAM(0x40, 0x22),
|
|
+ /* register doesn't exist on page 6? */
|
|
+ ILI9806_SET_REG_PARAM(0x52, 0x10),
|
|
+ /* doesn't make sense, not valid according to datasheet */
|
|
+ ILI9806_SET_REG_PARAM(0x53, 0x10),
|
|
+ /* doesn't make sense, not valid according to datasheet */
|
|
+ ILI9806_SET_REG_PARAM(0x54, 0x13),
|
|
+
|
|
+ ILI9806_SET_PAGE(7),
|
|
+
|
|
+ /* enable VREG */
|
|
+ ILI9806_SET_REG_PARAM(0x18, 0x1D),
|
|
+ /* enable VGL_REG */
|
|
+ ILI9806_SET_REG_PARAM(0x17, 0x22),
|
|
+ /* register doesn't exist on page 7? */
|
|
+ ILI9806_SET_REG_PARAM(0x02, 0x77),
|
|
+ /* register doesn't exist on page 7? */
|
|
+ ILI9806_SET_REG_PARAM(0x26, 0xB2),
|
|
+ /* register doesn't exist on page 7? */
|
|
+ ILI9806_SET_REG_PARAM(0xE1, 0x79),
|
|
+
|
|
+ ILI9806_SET_PAGE(0),
|
|
+
|
|
+ ILI9806_SET_REG_PARAM(MIPI_DCS_SET_PIXEL_FORMAT,
|
|
+ MIPI_DCS_PIXEL_FMT_18BIT << 4),
|
|
+ ILI9806_SET_REG_PARAM(MIPI_DCS_SET_TEAR_ON, 0x00),
|
|
+ ILI9806_SET_REG(MIPI_DCS_EXIT_SLEEP_MODE),
|
|
+};
|
|
+
|
|
+#define NUM_INIT_REGS ARRAY_SIZE(panel_init)
|
|
+
|
|
+static inline struct ili9806 *panel_to_ili9806(struct drm_panel *panel)
|
|
+{
|
|
+ return container_of(panel, struct ili9806, panel);
|
|
+}
|
|
+
|
|
+static int ili9806_write_msg(struct ili9806 *ctx, const struct ili9806e_msg *msg)
|
|
+{
|
|
+ struct spi_transfer xfer = { };
|
|
+ struct spi_message spi;
|
|
+ //u16 txbuf[] = { msg->, ILI9806_DATA | data };
|
|
+
|
|
+ spi_message_init(&spi);
|
|
+
|
|
+ xfer.tx_buf = msg->msg;
|
|
+ xfer.bits_per_word = 9;
|
|
+ xfer.len = sizeof(u16) * msg->len;
|
|
+
|
|
+ spi_message_add_tail(&xfer, &spi);
|
|
+ return spi_sync(ctx->spi, &spi);
|
|
+}
|
|
+
|
|
+static int ili9806e_write_msg_list(struct ili9806 *ctx,
|
|
+ const struct ili9806e_msg msgs[],
|
|
+ unsigned int num_msgs)
|
|
+{
|
|
+ int ret, i;
|
|
+
|
|
+ for (i = 0; i < num_msgs; i++) {
|
|
+ ret = ili9806_write_msg(ctx, &msgs[i]);
|
|
+ if (ret)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static const struct drm_display_mode ili9806e_480x800_mode = {
|
|
+ .clock = 32000,
|
|
+ .hdisplay = 480,
|
|
+ .hsync_start = 480 + 10,
|
|
+ .hsync_end = 480 + 10 + 16,
|
|
+ .htotal = 480 + 10 + 16 + 59,
|
|
+ .vdisplay = 800,
|
|
+ .vsync_start = 800 + 15,
|
|
+ .vsync_end = 800 + 15 + 113,
|
|
+ .vtotal = 800 + 15 + 113 + 15,
|
|
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
|
|
+};
|
|
+
|
|
+static int ili9806_get_modes(struct drm_panel *panel,
|
|
+ struct drm_connector *connector)
|
|
+{
|
|
+ struct ili9806 *ctx = panel_to_ili9806(panel);
|
|
+ struct drm_display_mode *mode;
|
|
+
|
|
+ mode = drm_mode_duplicate(connector->dev, &ili9806e_480x800_mode);
|
|
+ if (!mode) {
|
|
+ dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
|
|
+ ili9806e_480x800_mode.hdisplay,
|
|
+ ili9806e_480x800_mode.vdisplay,
|
|
+ drm_mode_vrefresh(&ili9806e_480x800_mode));
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ drm_mode_set_name(mode);
|
|
+
|
|
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
|
|
+ drm_mode_probed_add(connector, mode);
|
|
+
|
|
+ connector->display_info.width_mm = 61;
|
|
+ connector->display_info.height_mm = 103;
|
|
+ drm_display_info_set_bus_formats(&connector->display_info,
|
|
+ &ctx->bus_format, 1);
|
|
+ connector->display_info.bus_flags =
|
|
+ DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int ili9806_prepare(struct drm_panel *panel)
|
|
+{
|
|
+ struct ili9806 *ctx = panel_to_ili9806(panel);
|
|
+ int ret;
|
|
+
|
|
+ ret = regulator_enable(ctx->power);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = ili9806e_write_msg_list(ctx, panel_init, NUM_INIT_REGS);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ili9806_enable(struct drm_panel *panel)
|
|
+{
|
|
+ struct ili9806 *ctx = panel_to_ili9806(panel);
|
|
+ const struct ili9806e_msg msg = ILI9806_SET_REG(MIPI_DCS_SET_DISPLAY_ON);
|
|
+ int ret;
|
|
+
|
|
+ ret = ili9806_write_msg(ctx, &msg);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ili9806_disable(struct drm_panel *panel)
|
|
+{
|
|
+ struct ili9806 *ctx = panel_to_ili9806(panel);
|
|
+ const struct ili9806e_msg msg = ILI9806_SET_REG(MIPI_DCS_SET_DISPLAY_OFF);
|
|
+ int ret;
|
|
+
|
|
+ ret = ili9806_write_msg(ctx, &msg);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ili9806_unprepare(struct drm_panel *panel)
|
|
+{
|
|
+ struct ili9806 *ctx = panel_to_ili9806(panel);
|
|
+ const struct ili9806e_msg msg = ILI9806_SET_REG(MIPI_DCS_ENTER_SLEEP_MODE);
|
|
+ int ret;
|
|
+
|
|
+ ret = ili9806_write_msg(ctx, &msg);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static const struct drm_panel_funcs ili9806_drm_funcs = {
|
|
+ .disable = ili9806_disable,
|
|
+ .enable = ili9806_enable,
|
|
+ .get_modes = ili9806_get_modes,
|
|
+ .prepare = ili9806_prepare,
|
|
+ .unprepare = ili9806_unprepare,
|
|
+};
|
|
+
|
|
+static const struct of_device_id ili9806_of_match[] = {
|
|
+ { .compatible = "txw,txw397017s2",
|
|
+ .data = (void *)MEDIA_BUS_FMT_RGB888_1X24,
|
|
+ }, {
|
|
+ .compatible = "pimoroni,hyperpixel4",
|
|
+ .data = (void *)MEDIA_BUS_FMT_RGB666_1X24_CPADHI,
|
|
+ }, {
|
|
+ .compatible = "ilitek,ili9806e",
|
|
+ .data = (void *)MEDIA_BUS_FMT_RGB888_1X24,
|
|
+ }, {
|
|
+ /* sentinel */
|
|
+ }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, ili9806_of_match);
|
|
+
|
|
+static int ili9806_probe(struct spi_device *spi)
|
|
+{
|
|
+ const struct ili9806e_msg panel_reset[] = {
|
|
+ ILI9806_SET_PAGE(0),
|
|
+ ILI9806_SET_REG_PARAM(0x01, 0x00)
|
|
+ };
|
|
+ const struct of_device_id *id;
|
|
+ struct ili9806 *ctx;
|
|
+ int ret;
|
|
+
|
|
+ ctx = devm_kzalloc(&spi->dev, sizeof(*ctx), GFP_KERNEL);
|
|
+ if (!ctx)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ id = of_match_node(ili9806_of_match, spi->dev.of_node);
|
|
+ if (!id)
|
|
+ return -ENODEV;
|
|
+
|
|
+ ctx->bus_format = (u32)id->data;
|
|
+
|
|
+ spi_set_drvdata(spi, ctx);
|
|
+ ctx->spi = spi;
|
|
+
|
|
+ drm_panel_init(&ctx->panel, &spi->dev, &ili9806_drm_funcs,
|
|
+ DRM_MODE_CONNECTOR_DPI);
|
|
+
|
|
+ ctx->power = devm_regulator_get(&spi->dev, "power");
|
|
+ if (IS_ERR(ctx->power))
|
|
+ return PTR_ERR(ctx->power);
|
|
+
|
|
+ ctx->reset = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_LOW);
|
|
+ if (IS_ERR(ctx->reset)) {
|
|
+ dev_err(&spi->dev, "Couldn't get our reset line\n");
|
|
+ return PTR_ERR(ctx->reset);
|
|
+ }
|
|
+
|
|
+ /* Soft reset */
|
|
+ ili9806e_write_msg_list(ctx, panel_reset, ARRAY_SIZE(panel_reset));
|
|
+ msleep(200);
|
|
+
|
|
+ ret = drm_panel_of_backlight(&ctx->panel);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ drm_panel_add(&ctx->panel);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ili9806_remove(struct spi_device *spi)
|
|
+{
|
|
+ struct ili9806 *ctx = spi_get_drvdata(spi);
|
|
+
|
|
+ drm_panel_remove(&ctx->panel);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct spi_device_id ili9806_ids[] = {
|
|
+ { "txw397017s2", 0 },
|
|
+ { "ili9806e", 0 },
|
|
+ { "hyperpixel4", 0 },
|
|
+ { /* sentinel */ }
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(spi, ili9806_ids);
|
|
+
|
|
+static struct spi_driver ili9806_driver = {
|
|
+ .probe = ili9806_probe,
|
|
+ .remove = ili9806_remove,
|
|
+ .driver = {
|
|
+ .name = "ili9806e",
|
|
+ .of_match_table = ili9806_of_match,
|
|
+ },
|
|
+ .id_table = ili9806_ids,
|
|
+};
|
|
+module_spi_driver(ili9806_driver);
|
|
+
|
|
+MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com>");
|
|
+MODULE_DESCRIPTION("ili9806 LCD panel driver");
|
|
+MODULE_LICENSE("GPL v2");
|