mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 10:00:17 +00:00
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>
This commit is contained in:
committed by
Dom Cobley
parent
5e64a6f033
commit
72a36feda1
@@ -266,6 +266,17 @@ config DRM_PANEL_ILITEK_ILI9806E
|
|||||||
Say Y if you want to enable support for panels based on the
|
Say Y if you want to enable support for panels based on the
|
||||||
Ilitek ILI9806E controller.
|
Ilitek ILI9806E controller.
|
||||||
|
|
||||||
|
config DRM_PANEL_ILITEK_ILI9806E_SPI
|
||||||
|
tristate "Ilitek ILI9806E-based panels on SPI/DPI"
|
||||||
|
depends on OF && SPI
|
||||||
|
select DRM_KMS_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 using DPI for video and SPI for
|
||||||
|
configuration.
|
||||||
|
|
||||||
config DRM_PANEL_ILITEK_ILI9881C
|
config DRM_PANEL_ILITEK_ILI9881C
|
||||||
tristate "Ilitek ILI9881C-based panels"
|
tristate "Ilitek ILI9881C-based panels"
|
||||||
depends on OF
|
depends on OF
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ 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_ILI9341) += panel-ilitek-ili9341.o
|
||||||
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9805) += panel-ilitek-ili9805.o
|
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9805) += panel-ilitek-ili9805.o
|
||||||
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9806E) += panel-ilitek-ili9806e.o
|
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9806E) += panel-ilitek-ili9806e.o
|
||||||
|
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9806E_SPI) += panel-ilitek-ili9806e-spi.o
|
||||||
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o
|
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o
|
||||||
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9882T) += panel-ilitek-ili9882t.o
|
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9882T) += panel-ilitek-ili9882t.o
|
||||||
obj-$(CONFIG_DRM_PANEL_INNOLUX_EJ030NA) += panel-innolux-ej030na.o
|
obj-$(CONFIG_DRM_PANEL_INNOLUX_EJ030NA) += panel-innolux-ej030na.o
|
||||||
|
|||||||
479
drivers/gpu/drm/panel/panel-ilitek-ili9806e-spi.c
Normal file
479
drivers/gpu/drm/panel/panel-ilitek-ili9806e-spi.c
Normal file
@@ -0,0 +1,479 @@
|
|||||||
|
// 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.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)
|
||||||
|
};
|
||||||
|
struct ili9806 *ctx;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ctx = devm_kzalloc(&spi->dev, sizeof(*ctx), GFP_KERNEL);
|
||||||
|
if (!ctx)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ctx->bus_format = (u32)(uintptr_t)of_device_get_match_data(&spi->dev);
|
||||||
|
|
||||||
|
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 void ili9806_remove(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
struct ili9806 *ctx = spi_get_drvdata(spi);
|
||||||
|
|
||||||
|
drm_panel_remove(&ctx->panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
Reference in New Issue
Block a user