[8/8] video: support Rocktech RK050HR345-CT106A panel

Message ID 20260219175130.2839234-9-dario.binacchi@amarulasolutions.com
State New
Headers show
Series
  • video: support Rocktech RK050HR345-CT106A panel
Related show

Commit Message

Dario Binacchi Feb. 19, 2026, 5:51 p.m. UTC
Add support for the Rocktech RK050HR345-CT106A RGB panel. This model
uses an Ilitek ILI9806E controller over the SPI bus for initialization
and register configuration only.

The driver is designed to be easily extensible to support other panels
with different sequences and timings by providing a specific descriptor
structure for each model.

Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
---

 drivers/video/Kconfig           |   8 +
 drivers/video/Makefile          |   1 +
 drivers/video/ilitek-ili9806e.c | 354 ++++++++++++++++++++++++++++++++
 3 files changed, 363 insertions(+)
 create mode 100644 drivers/video/ilitek-ili9806e.c

Comments

Tom Rini Feb. 19, 2026, 6:07 p.m. UTC | #1
On Thu, Feb 19, 2026 at 06:51:19PM +0100, Dario Binacchi wrote:

> Add support for the Rocktech RK050HR345-CT106A RGB panel. This model
> uses an Ilitek ILI9806E controller over the SPI bus for initialization
> and register configuration only.
> 
> The driver is designed to be easily extensible to support other panels
> with different sequences and timings by providing a specific descriptor
> structure for each model.
> 
> Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
> ---
> 
>  drivers/video/Kconfig           |   8 +
>  drivers/video/Makefile          |   1 +
>  drivers/video/ilitek-ili9806e.c | 354 ++++++++++++++++++++++++++++++++
>  3 files changed, 363 insertions(+)
>  create mode 100644 drivers/video/ilitek-ili9806e.c
> 
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index 08c9b0207884..c78d6540855a 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -552,6 +552,14 @@ config VIDEO_LCD_HIMAX_HX8394
>  	Say Y here if you want to enable support for Himax HX8394
>  	dsi 4dl panel.
>  
> +config VIDEO_LCD_ILITEK_ILI9806E
> +	tristate "Ilitek ILI9806E-based panels"

"tristate" is wrong for U-Boot, it should be bool.

Patch

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 08c9b0207884..c78d6540855a 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -552,6 +552,14 @@  config VIDEO_LCD_HIMAX_HX8394
 	Say Y here if you want to enable support for Himax HX8394
 	dsi 4dl panel.
 
+config VIDEO_LCD_ILITEK_ILI9806E
+	tristate "Ilitek ILI9806E-based panels"
+	depends on PANEL && BACKLIGHT
+	help
+	  Say Y here if you want to enable support for panels base on
+	  the Ilitek ILI9806E controller. Currently only the DBI panel
+	  is implemented.
+
 config VIDEO_LCD_MOT
 	tristate "Atrix 4G and Droid X2 540x960 DSI video mode panel"
 	depends on PANEL && BACKLIGHT
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 984768ea156d..74846a8106e4 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -61,6 +61,7 @@  obj-$(CONFIG_VIDEO_LCD_ENDEAVORU) += endeavoru-panel.o
 obj-$(CONFIG_VIDEO_LCD_HIMAX_HX8394) += himax-hx8394.o
 obj-$(CONFIG_VIDEO_LCD_HITACHI_TX10D07VM0BAA) += hitachi-tx10d07vm0baa.o
 obj-$(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.o
+obj-$(CONFIG_VIDEO_LCD_ILITEK_ILI9806E) += ilitek-ili9806e.o
 obj-$(CONFIG_VIDEO_LCD_LG_LD070WX3) += lg-ld070wx3.o
 obj-$(CONFIG_VIDEO_LCD_LG_LH400WV3) += lg-lh400wv3-sd04.o
 obj-$(CONFIG_VIDEO_LCD_MOT) += mot-panel.o
diff --git a/drivers/video/ilitek-ili9806e.c b/drivers/video/ilitek-ili9806e.c
new file mode 100644
index 000000000000..e1b27e12dde5
--- /dev/null
+++ b/drivers/video/ilitek-ili9806e.c
@@ -0,0 +1,354 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 Amarula Solutions, Dario Binacchi <dario.binacchi@amarulasolutions.com>
+ */
+
+#include <backlight.h>
+#include <dm.h>
+#include <mipi_display.h>
+#include <panel.h>
+#include <spi.h>
+#include <asm/gpio.h>
+#include <dm/device_compat.h>
+#include <linux/delay.h>
+#include <power/regulator.h>
+
+struct ilitek_ili9806e_priv {
+	struct udevice *vdd;
+	struct udevice *backlight;
+	struct gpio_desc reset_gpio;
+	const struct ilitek_ili9806e_desc *desc;
+};
+
+struct ilitek_ili9806e_desc {
+	const struct display_timing timing;
+	void (*init_sequence)(struct udevice *dev);
+};
+
+static int ilitek_ili9806e_dcs_write(struct udevice *dev, u8 cmd, const u8 *seq, int len)
+{
+	u16 data[16];
+	int i, ret;
+
+	if ((len + 1) > ARRAY_SIZE(data)) {
+		dev_err(dev, "Command length (%d) exceeds buffer size (%lu)\n",
+			len + 1, ARRAY_SIZE(data));
+		return -EMSGSIZE;
+	}
+
+	data[0] = cmd;
+	if (len) {
+		for (i = 0; i < len; i++)
+			data[i + 1] = seq[i] | 0x0100;
+	}
+
+	ret = dm_spi_xfer(dev, (len + 1) * 8 * sizeof(u16), data, NULL,
+			  SPI_XFER_ONCE);
+	return 0;
+}
+
+#define ilitek_ili9806e_dcs_write_seq(dev, cmd, seq...)		\
+({								\
+	static const u8 b[] = { seq };				\
+	ilitek_ili9806e_dcs_write(dev, cmd, b, ARRAY_SIZE(b));	\
+})
+
+static int ilitek_ili9806e_enable_backlight(struct udevice *dev)
+{
+	struct ilitek_ili9806e_priv *priv = dev_get_priv(dev);
+	const struct ilitek_ili9806e_desc *desc = priv->desc;
+
+	desc->init_sequence(dev);
+
+	return panel_set_backlight(dev, BACKLIGHT_DEFAULT);
+}
+
+static int ilitek_ili9806e_set_backlight(struct udevice *dev, int percent)
+{
+	struct ilitek_ili9806e_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = backlight_enable(priv->backlight);
+	if (ret) {
+		dev_err(dev, "Cannot enable backlight\n");
+		return ret;
+	}
+
+	ret = backlight_set_brightness(priv->backlight, percent);
+	if (ret)
+		dev_err(dev, "Cannot set backlight brightness\n");
+
+	return ret;
+}
+
+static int ilitek_ili9806e_get_display_timing(struct udevice *dev,
+					      struct display_timing *timing)
+{
+	struct ilitek_ili9806e_priv *priv = dev_get_priv(dev);
+
+	memcpy(timing, &priv->desc->timing, sizeof(*timing));
+	return 0;
+}
+
+static int ilitek_ili9806e_of_to_plat(struct udevice *dev)
+{
+	struct ilitek_ili9806e_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	if (CONFIG_IS_ENABLED(DM_REGULATOR)) {
+		ret = device_get_supply_regulator(dev, "vdd-supply", &priv->vdd);
+		if (ret) {
+			dev_err(dev, "Cannot get vdd supply\n");
+			return ret;
+		}
+	}
+
+	ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
+					   "backlight", &priv->backlight);
+	if (ret) {
+		dev_err(dev, "Cannot get backlight\n");
+		return ret;
+	}
+
+	ret = gpio_request_by_name(dev, "reset-gpios", 0,
+				   &priv->reset_gpio, GPIOD_IS_OUT);
+	if (ret) {
+		dev_err(dev, "Cannot get reset GPIO\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ilitek_ili9806e_hw_init(struct udevice *dev)
+{
+	struct ilitek_ili9806e_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = dm_gpio_set_value(&priv->reset_gpio, 1);
+	if (ret) {
+		dev_err(dev, "Cannot enter reset\n");
+		return ret;
+	}
+
+	ret = regulator_set_enable_if_allowed(priv->vdd, 1);
+	if (ret) {
+		dev_err(dev, "Cannot enable vdd-supply\n");
+		return ret;
+	}
+
+	mdelay(20);
+
+	ret = dm_gpio_set_value(&priv->reset_gpio, 0);
+	if (ret) {
+		dev_err(dev, "Cannot exit reset\n");
+		return ret;
+	}
+
+	mdelay(20);
+
+	return 0;
+}
+
+static int ilitek_ili9806e_probe(struct udevice *dev)
+{
+	struct ilitek_ili9806e_priv *priv = dev_get_priv(dev);
+	struct spi_slave *slave = dev_get_parent_priv(dev);
+	int ret;
+
+	ret = spi_set_wordlen(slave, 9);
+	if (ret) {
+		dev_err(dev, "Cannot set SPI.bits_per_word\n");
+		return ret;
+	}
+
+	ret = spi_claim_bus(slave);
+	if (ret) {
+		dev_err(dev, "Cannot get SPI bus\n");
+		return ret;
+	}
+
+	priv->desc = (struct ilitek_ili9806e_desc *)dev_get_driver_data(dev);
+	return ilitek_ili9806e_hw_init(dev);
+}
+
+static const struct panel_ops ilitek_ili9806e_ops = {
+	.enable_backlight = ilitek_ili9806e_enable_backlight,
+	.set_backlight = ilitek_ili9806e_set_backlight,
+	.get_display_timing = ilitek_ili9806e_get_display_timing,
+};
+
+static void rk050hr345_ct106a_init(struct udevice *dev)
+{
+	/* Switch to page 1 */
+	ilitek_ili9806e_dcs_write_seq(dev, 0xff, 0xff, 0x98, 0x06, 0x04, 0x01);
+	/* Interface Settings */
+	ilitek_ili9806e_dcs_write_seq(dev, 0x08, 0x10);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x21, 0x01);
+	/* Panel Settings */
+	ilitek_ili9806e_dcs_write_seq(dev, 0x30, 0x01);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x31, 0x00);
+	/* Power Control */
+	ilitek_ili9806e_dcs_write_seq(dev, 0x40, 0x15);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x41, 0x44);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x42, 0x03);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x43, 0x09);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x44, 0x09);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x50, 0x78);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x51, 0x78);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x52, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x53, 0x3a);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x57, 0x50);
+	/* Timing Control */
+	ilitek_ili9806e_dcs_write_seq(dev, 0x60, 0x07);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x61, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x62, 0x08);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x63, 0x00);
+	/* Gamma Settings */
+	ilitek_ili9806e_dcs_write_seq(dev, 0xa0, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xa1, 0x03);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xa2, 0x0b);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xa3, 0x0f);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xa4, 0x0b);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xa5, 0x1b);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xa6, 0x0a);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xa7, 0x0a);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xa8, 0x02);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xa9, 0x07);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xaa, 0x05);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xab, 0x03);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xac, 0x0e);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xad, 0x32);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xae, 0x2d);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xaf, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xc0, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xc1, 0x03);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xc2, 0x0e);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xc3, 0x10);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xc4, 0x09);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xc5, 0x17);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xc6, 0x09);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xc7, 0x07);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xc8, 0x04);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xc9, 0x09);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xca, 0x06);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xcb, 0x06);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xcc, 0x0c);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xcd, 0x25);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xce, 0x20);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xcf, 0x00);
+
+	/* Switch to page 6 */
+	ilitek_ili9806e_dcs_write_seq(dev, 0xff, 0xff, 0x98, 0x06, 0x04, 0x06);
+	/* GIP settings */
+	ilitek_ili9806e_dcs_write_seq(dev, 0x00, 0x21);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x01, 0x09);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x02, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x03, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x04, 0x01);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x05, 0x01);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x06, 0x80);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x07, 0x05);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x08, 0x02);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x09, 0x80);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x0a, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x0b, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x0c, 0x0a);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x0d, 0x0a);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x0e, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x0f, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x10, 0xe0);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x11, 0xe4);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x12, 0x04);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x13, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x14, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x15, 0xc0);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x16, 0x08);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x17, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x18, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x19, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x1a, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x1b, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x1c, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x1d, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x20, 0x01);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x21, 0x23);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x22, 0x45);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x23, 0x67);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x24, 0x01);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x25, 0x23);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x26, 0x45);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x27, 0x67);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x30, 0x01);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x31, 0x11);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x32, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x33, 0xee);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x34, 0xff);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x35, 0xbb);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x36, 0xca);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x37, 0xdd);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x38, 0xac);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x39, 0x76);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x3a, 0x67);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x3b, 0x22);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x3c, 0x22);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x3d, 0x22);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x3e, 0x22);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x3f, 0x22);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x40, 0x22);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x52, 0x10);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x53, 0x10);
+
+	/* Switch to page 7 */
+	ilitek_ili9806e_dcs_write_seq(dev, 0xff, 0xff, 0x98, 0x06, 0x04, 0x07);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x17, 0x22);
+	ilitek_ili9806e_dcs_write_seq(dev, 0x02, 0x77);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xe1, 0x79);
+	ilitek_ili9806e_dcs_write_seq(dev, 0xb3, 0x10);
+
+	/* Switch to page 0 */
+	ilitek_ili9806e_dcs_write_seq(dev, 0xff, 0xff, 0x98, 0x06, 0x04, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
+	ilitek_ili9806e_dcs_write_seq(dev, MIPI_DCS_EXIT_SLEEP_MODE);
+
+	mdelay(120);
+
+	ilitek_ili9806e_dcs_write_seq(dev, MIPI_DCS_SET_DISPLAY_ON);
+
+	mdelay(120);
+}
+
+static const struct ilitek_ili9806e_desc rk050hr345_ct106a_desc = {
+	.timing = {
+		.pixelclock.typ = 27000000,
+		.hactive.typ = 480,
+		.hfront_porch.typ = 10,
+		.hback_porch.typ = 10,
+		.hsync_len.typ = 10,
+		.vactive.typ = 854,
+		.vfront_porch.typ = 10,
+		.vback_porch.typ = 10,
+		.vsync_len.typ = 10,
+		.flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW,
+	},
+	.init_sequence = rk050hr345_ct106a_init,
+};
+
+static const struct udevice_id ilitek_ili9806e_ids[] = {
+	{
+		.compatible = "rocktech,rk050hr345-ct106a",
+		.data = (ulong)&rk050hr345_ct106a_desc,
+	},
+	{ }
+};
+
+U_BOOT_DRIVER(ilitek_ili9806e) = {
+	.name		= "ilitek_ili9806e",
+	.id		= UCLASS_PANEL,
+	.of_match	= ilitek_ili9806e_ids,
+	.ops		= &ilitek_ili9806e_ops,
+	.of_to_plat	= ilitek_ili9806e_of_to_plat,
+	.probe		= ilitek_ili9806e_probe,
+	.priv_auto	= sizeof(struct ilitek_ili9806e_priv),
+	.flags		= DM_FLAG_PRE_RELOC,
+};