Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

drm/panel: ilitek-ili9806e: add Rocktech RK050HR345-CT106A SPI panel

Add support for the Rocktech RK050HR345-CT106A panel based on the
Ilitek ILI9806E controller using the SPI bus.

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

Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://patch.msgid.link/20260318073346.18041-5-dario.binacchi@amarulasolutions.com

authored by

Dario Binacchi and committed by
Neil Armstrong
a05f2911 7cad20e3

+355 -13
+1
MAINTAINERS
··· 8027 8027 8028 8028 DRM DRIVER FOR ILITEK ILI9806E PANELS 8029 8029 M: Michael Walle <mwalle@kernel.org> 8030 + M: Dario Binacchi <dario.binacchi@amarulasolutions.com> 8030 8031 S: Maintained 8031 8032 F: drivers/gpu/drm/panel/panel-ilitek-ili9806e-* 8032 8033
+12
drivers/gpu/drm/panel/Kconfig
··· 281 281 Say Y if you want to enable support for panels based on the 282 282 Ilitek ILI9806E controller using DSI. 283 283 284 + config DRM_PANEL_ILITEK_ILI9806E_SPI 285 + tristate "Ilitek ILI9806E-based RGB SPI panel" 286 + depends on OF 287 + depends on SPI 288 + depends on BACKLIGHT_CLASS_DEVICE 289 + select DRM_MIPI_DBI 290 + select VIDEOMODE_HELPERS 291 + select DRM_PANEL_ILITEK_ILI9806E_CORE 292 + help 293 + Say Y if you want to enable support for panels based on the 294 + Ilitek ILI9806E controller using SPI. 295 + 284 296 config DRM_PANEL_ILITEK_ILI9881C 285 297 tristate "Ilitek ILI9881C-based panels" 286 298 depends on OF
+1
drivers/gpu/drm/panel/Makefile
··· 29 29 obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9805) += panel-ilitek-ili9805.o 30 30 obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9806E_CORE) += panel-ilitek-ili9806e-core.o 31 31 obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9806E_DSI) += panel-ilitek-ili9806e-dsi.o 32 + obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9806E_SPI) += panel-ilitek-ili9806e-spi.o 32 33 obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o 33 34 obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9882T) += panel-ilitek-ili9882t.o 34 35 obj-$(CONFIG_DRM_PANEL_INNOLUX_EJ030NA) += panel-innolux-ej030na.o
+18 -13
drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c
··· 11 11 #include <linux/export.h> 12 12 #include <linux/gpio/consumer.h> 13 13 #include <linux/module.h> 14 + #include <linux/of.h> 14 15 #include <linux/property.h> 15 16 #include <linux/regulator/consumer.h> 16 17 ··· 21 20 void *transport; 22 21 struct drm_panel panel; 23 22 23 + unsigned int num_supplies; 24 24 struct regulator_bulk_data supplies[2]; 25 25 struct gpio_desc *reset_gpio; 26 - }; 27 - 28 - static const char * const regulator_names[] = { 29 - "vdd", 30 - "vccio", 31 26 }; 32 27 33 28 void *ili9806e_get_transport(struct drm_panel *panel) ··· 41 44 42 45 gpiod_set_value(ctx->reset_gpio, 1); 43 46 44 - ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 47 + ret = regulator_bulk_enable(ctx->num_supplies, ctx->supplies); 45 48 if (ret) { 46 49 dev_err(dev, "regulator bulk enable failed: %d\n", ret); 47 50 return ret; ··· 62 65 63 66 gpiod_set_value(ctx->reset_gpio, 1); 64 67 65 - ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 68 + ret = regulator_bulk_disable(ctx->num_supplies, ctx->supplies); 66 69 if (ret) 67 70 dev_err(dev, "regulator bulk disable failed: %d\n", ret); 68 71 ··· 75 78 int connector_type) 76 79 { 77 80 struct ili9806e *ctx; 78 - int i, ret; 81 + bool set_prepare_prev_first = false; 82 + int ret; 79 83 80 84 ctx = devm_kzalloc(dev, sizeof(struct ili9806e), GFP_KERNEL); 81 85 if (!ctx) ··· 85 87 dev_set_drvdata(dev, ctx); 86 88 ctx->transport = transport; 87 89 88 - for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++) 89 - ctx->supplies[i].supply = regulator_names[i]; 90 + ctx->supplies[ctx->num_supplies++].supply = "vdd"; 91 + if (of_device_is_compatible(dev->of_node, 92 + "densitron,dmt028vghmcmi-1d") || 93 + of_device_is_compatible(dev->of_node, 94 + "ortustech,com35h3p70ulc")) { 95 + ctx->supplies[ctx->num_supplies++].supply = "vccio"; 96 + set_prepare_prev_first = true; 97 + } 90 98 91 - ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), 92 - ctx->supplies); 99 + ret = devm_regulator_bulk_get(dev, ctx->num_supplies, ctx->supplies); 93 100 if (ret) 94 101 return dev_err_probe(dev, ret, "failed to get regulators\n"); 95 102 ··· 109 106 if (ret) 110 107 return dev_err_probe(dev, ret, "Failed to get backlight\n"); 111 108 112 - ctx->panel.prepare_prev_first = true; 109 + if (set_prepare_prev_first) 110 + ctx->panel.prepare_prev_first = true; 111 + 113 112 drm_panel_add(&ctx->panel); 114 113 115 114 return 0;
+323
drivers/gpu/drm/panel/panel-ilitek-ili9806e-spi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * SPI interface to the Ilitek ILI9806E panel. 4 + * 5 + * Copyright (c) 2026 Amarula Solutions, Dario Binacchi <dario.binacchi@amarulasolutions.com> 6 + */ 7 + 8 + #include <linux/delay.h> 9 + #include <linux/device.h> 10 + #include <linux/media-bus-format.h> 11 + #include <linux/module.h> 12 + #include <linux/spi/spi.h> 13 + 14 + #include <drm/drm_mipi_dbi.h> 15 + #include <drm/drm_panel.h> 16 + #include <drm/drm_print.h> 17 + 18 + #include <video/mipi_display.h> 19 + 20 + #include "panel-ilitek-ili9806e-core.h" 21 + 22 + struct ili9806e_spi_panel { 23 + struct spi_device *spi; 24 + struct mipi_dbi dbi; 25 + const struct ili9806e_spi_panel_desc *desc; 26 + }; 27 + 28 + struct ili9806e_spi_panel_desc { 29 + const struct drm_display_mode *display_mode; 30 + u32 bus_format; 31 + u32 bus_flags; 32 + void (*init_sequence)(struct ili9806e_spi_panel *ctx); 33 + }; 34 + 35 + static int ili9806e_spi_off(struct ili9806e_spi_panel *ctx) 36 + { 37 + struct mipi_dbi *dbi = &ctx->dbi; 38 + 39 + mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF, 0x00); 40 + mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE, 0x00); 41 + 42 + return 0; 43 + } 44 + 45 + static int ili9806e_spi_unprepare(struct drm_panel *panel) 46 + { 47 + struct ili9806e_spi_panel *ctx = ili9806e_get_transport(panel); 48 + struct device *dev = &ctx->spi->dev; 49 + int ret; 50 + 51 + ili9806e_spi_off(ctx); 52 + 53 + ret = ili9806e_power_off(dev); 54 + if (ret) 55 + dev_err(dev, "power off failed: %d\n", ret); 56 + 57 + return 0; 58 + } 59 + 60 + static int ili9806e_spi_prepare(struct drm_panel *panel) 61 + { 62 + struct ili9806e_spi_panel *ctx = ili9806e_get_transport(panel); 63 + struct device *dev = &ctx->spi->dev; 64 + int ret; 65 + 66 + ret = ili9806e_power_on(dev); 67 + if (ret) 68 + return ret; 69 + 70 + if (ctx->desc->init_sequence) 71 + ctx->desc->init_sequence(ctx); 72 + 73 + return 0; 74 + } 75 + 76 + static int ili9806e_spi_get_modes(struct drm_panel *panel, 77 + struct drm_connector *connector) 78 + { 79 + struct ili9806e_spi_panel *ctx = ili9806e_get_transport(panel); 80 + const struct ili9806e_spi_panel_desc *desc = ctx->desc; 81 + struct drm_display_mode *mode; 82 + 83 + mode = drm_mode_duplicate(connector->dev, desc->display_mode); 84 + if (!mode) 85 + return -ENOMEM; 86 + 87 + drm_mode_set_name(mode); 88 + 89 + connector->display_info.width_mm = mode->width_mm; 90 + connector->display_info.height_mm = mode->height_mm; 91 + connector->display_info.bus_flags = desc->bus_flags; 92 + drm_display_info_set_bus_formats(&connector->display_info, 93 + &desc->bus_format, 1); 94 + 95 + drm_mode_probed_add(connector, mode); 96 + 97 + return 1; 98 + } 99 + 100 + static const struct drm_panel_funcs ili9806e_spi_funcs = { 101 + .unprepare = ili9806e_spi_unprepare, 102 + .prepare = ili9806e_spi_prepare, 103 + .get_modes = ili9806e_spi_get_modes, 104 + }; 105 + 106 + static int ili9806e_spi_probe(struct spi_device *spi) 107 + { 108 + struct device *dev = &spi->dev; 109 + struct ili9806e_spi_panel *ctx; 110 + int err; 111 + 112 + ctx = devm_kzalloc(dev, sizeof(struct ili9806e_spi_panel), GFP_KERNEL); 113 + if (!ctx) 114 + return -ENOMEM; 115 + 116 + ctx->spi = spi; 117 + ctx->desc = device_get_match_data(dev); 118 + 119 + err = mipi_dbi_spi_init(spi, &ctx->dbi, NULL); 120 + if (err) 121 + return dev_err_probe(dev, err, "MIPI DBI init failed\n"); 122 + 123 + return ili9806e_probe(dev, ctx, &ili9806e_spi_funcs, 124 + DRM_MODE_CONNECTOR_DPI); 125 + } 126 + 127 + static void ili9806e_spi_remove(struct spi_device *spi) 128 + { 129 + ili9806e_remove(&spi->dev); 130 + } 131 + 132 + static void rk050hr345_ct106a_init(struct ili9806e_spi_panel *ctx) 133 + { 134 + struct mipi_dbi *dbi = &ctx->dbi; 135 + 136 + /* Switch to page 1 */ 137 + mipi_dbi_command(dbi, 0xff, 0xff, 0x98, 0x06, 0x04, 0x01); 138 + /* Interface Settings */ 139 + mipi_dbi_command(dbi, 0x08, 0x10); 140 + mipi_dbi_command(dbi, 0x21, 0x01); 141 + /* Panel Settings */ 142 + mipi_dbi_command(dbi, 0x30, 0x01); 143 + mipi_dbi_command(dbi, 0x31, 0x00); 144 + /* Power Control */ 145 + mipi_dbi_command(dbi, 0x40, 0x15); 146 + mipi_dbi_command(dbi, 0x41, 0x44); 147 + mipi_dbi_command(dbi, 0x42, 0x03); 148 + mipi_dbi_command(dbi, 0x43, 0x09); 149 + mipi_dbi_command(dbi, 0x44, 0x09); 150 + mipi_dbi_command(dbi, 0x50, 0x78); 151 + mipi_dbi_command(dbi, 0x51, 0x78); 152 + mipi_dbi_command(dbi, 0x52, 0x00); 153 + mipi_dbi_command(dbi, 0x53, 0x3a); 154 + mipi_dbi_command(dbi, 0x57, 0x50); 155 + /* Timing Control */ 156 + mipi_dbi_command(dbi, 0x60, 0x07); 157 + mipi_dbi_command(dbi, 0x61, 0x00); 158 + mipi_dbi_command(dbi, 0x62, 0x08); 159 + mipi_dbi_command(dbi, 0x63, 0x00); 160 + /* Gamma Settings */ 161 + mipi_dbi_command(dbi, 0xa0, 0x00); 162 + mipi_dbi_command(dbi, 0xa1, 0x03); 163 + mipi_dbi_command(dbi, 0xa2, 0x0b); 164 + mipi_dbi_command(dbi, 0xa3, 0x0f); 165 + mipi_dbi_command(dbi, 0xa4, 0x0b); 166 + mipi_dbi_command(dbi, 0xa5, 0x1b); 167 + mipi_dbi_command(dbi, 0xa6, 0x0a); 168 + mipi_dbi_command(dbi, 0xa7, 0x0a); 169 + mipi_dbi_command(dbi, 0xa8, 0x02); 170 + mipi_dbi_command(dbi, 0xa9, 0x07); 171 + mipi_dbi_command(dbi, 0xaa, 0x05); 172 + mipi_dbi_command(dbi, 0xab, 0x03); 173 + mipi_dbi_command(dbi, 0xac, 0x0e); 174 + mipi_dbi_command(dbi, 0xad, 0x32); 175 + mipi_dbi_command(dbi, 0xae, 0x2d); 176 + mipi_dbi_command(dbi, 0xaf, 0x00); 177 + mipi_dbi_command(dbi, 0xc0, 0x00); 178 + mipi_dbi_command(dbi, 0xc1, 0x03); 179 + mipi_dbi_command(dbi, 0xc2, 0x0e); 180 + mipi_dbi_command(dbi, 0xc3, 0x10); 181 + mipi_dbi_command(dbi, 0xc4, 0x09); 182 + mipi_dbi_command(dbi, 0xc5, 0x17); 183 + mipi_dbi_command(dbi, 0xc6, 0x09); 184 + mipi_dbi_command(dbi, 0xc7, 0x07); 185 + mipi_dbi_command(dbi, 0xc8, 0x04); 186 + mipi_dbi_command(dbi, 0xc9, 0x09); 187 + mipi_dbi_command(dbi, 0xca, 0x06); 188 + mipi_dbi_command(dbi, 0xcb, 0x06); 189 + mipi_dbi_command(dbi, 0xcc, 0x0c); 190 + mipi_dbi_command(dbi, 0xcd, 0x25); 191 + mipi_dbi_command(dbi, 0xce, 0x20); 192 + mipi_dbi_command(dbi, 0xcf, 0x00); 193 + 194 + /* Switch to page 6 */ 195 + mipi_dbi_command(dbi, 0xff, 0xff, 0x98, 0x06, 0x04, 0x06); 196 + /* GIP settings */ 197 + mipi_dbi_command(dbi, 0x00, 0x21); 198 + mipi_dbi_command(dbi, 0x01, 0x09); 199 + mipi_dbi_command(dbi, 0x02, 0x00); 200 + mipi_dbi_command(dbi, 0x03, 0x00); 201 + mipi_dbi_command(dbi, 0x04, 0x01); 202 + mipi_dbi_command(dbi, 0x05, 0x01); 203 + mipi_dbi_command(dbi, 0x06, 0x80); 204 + mipi_dbi_command(dbi, 0x07, 0x05); 205 + mipi_dbi_command(dbi, 0x08, 0x02); 206 + mipi_dbi_command(dbi, 0x09, 0x80); 207 + mipi_dbi_command(dbi, 0x0a, 0x00); 208 + mipi_dbi_command(dbi, 0x0b, 0x00); 209 + mipi_dbi_command(dbi, 0x0c, 0x0a); 210 + mipi_dbi_command(dbi, 0x0d, 0x0a); 211 + mipi_dbi_command(dbi, 0x0e, 0x00); 212 + mipi_dbi_command(dbi, 0x0f, 0x00); 213 + mipi_dbi_command(dbi, 0x10, 0xe0); 214 + mipi_dbi_command(dbi, 0x11, 0xe4); 215 + mipi_dbi_command(dbi, 0x12, 0x04); 216 + mipi_dbi_command(dbi, 0x13, 0x00); 217 + mipi_dbi_command(dbi, 0x14, 0x00); 218 + mipi_dbi_command(dbi, 0x15, 0xc0); 219 + mipi_dbi_command(dbi, 0x16, 0x08); 220 + mipi_dbi_command(dbi, 0x17, 0x00); 221 + mipi_dbi_command(dbi, 0x18, 0x00); 222 + mipi_dbi_command(dbi, 0x19, 0x00); 223 + mipi_dbi_command(dbi, 0x1a, 0x00); 224 + mipi_dbi_command(dbi, 0x1b, 0x00); 225 + mipi_dbi_command(dbi, 0x1c, 0x00); 226 + mipi_dbi_command(dbi, 0x1d, 0x00); 227 + mipi_dbi_command(dbi, 0x20, 0x01); 228 + mipi_dbi_command(dbi, 0x21, 0x23); 229 + mipi_dbi_command(dbi, 0x22, 0x45); 230 + mipi_dbi_command(dbi, 0x23, 0x67); 231 + mipi_dbi_command(dbi, 0x24, 0x01); 232 + mipi_dbi_command(dbi, 0x25, 0x23); 233 + mipi_dbi_command(dbi, 0x26, 0x45); 234 + mipi_dbi_command(dbi, 0x27, 0x67); 235 + mipi_dbi_command(dbi, 0x30, 0x01); 236 + mipi_dbi_command(dbi, 0x31, 0x11); 237 + mipi_dbi_command(dbi, 0x32, 0x00); 238 + mipi_dbi_command(dbi, 0x33, 0xee); 239 + mipi_dbi_command(dbi, 0x34, 0xff); 240 + mipi_dbi_command(dbi, 0x35, 0xbb); 241 + mipi_dbi_command(dbi, 0x36, 0xca); 242 + mipi_dbi_command(dbi, 0x37, 0xdd); 243 + mipi_dbi_command(dbi, 0x38, 0xac); 244 + mipi_dbi_command(dbi, 0x39, 0x76); 245 + mipi_dbi_command(dbi, 0x3a, 0x67); 246 + mipi_dbi_command(dbi, 0x3b, 0x22); 247 + mipi_dbi_command(dbi, 0x3c, 0x22); 248 + mipi_dbi_command(dbi, 0x3d, 0x22); 249 + mipi_dbi_command(dbi, 0x3e, 0x22); 250 + mipi_dbi_command(dbi, 0x3f, 0x22); 251 + mipi_dbi_command(dbi, 0x40, 0x22); 252 + mipi_dbi_command(dbi, 0x52, 0x10); 253 + mipi_dbi_command(dbi, 0x53, 0x10); 254 + 255 + /* Switch to page 7 */ 256 + mipi_dbi_command(dbi, 0xff, 0xff, 0x98, 0x06, 0x04, 0x07); 257 + mipi_dbi_command(dbi, 0x17, 0x22); 258 + mipi_dbi_command(dbi, 0x02, 0x77); 259 + mipi_dbi_command(dbi, 0xe1, 0x79); 260 + mipi_dbi_command(dbi, 0xb3, 0x10); 261 + 262 + /* Switch to page 0 */ 263 + mipi_dbi_command(dbi, 0xff, 0xff, 0x98, 0x06, 0x04, 0x00); 264 + mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, 0x00); // 0x36 265 + mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); // 0x11 266 + 267 + msleep(120); 268 + 269 + mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); 270 + 271 + msleep(120); 272 + } 273 + 274 + static const struct drm_display_mode rk050hr345_ct106a_mode = { 275 + .width_mm = 62, 276 + .height_mm = 110, 277 + .clock = 27000, 278 + .hdisplay = 480, 279 + .hsync_start = 480 + 10, 280 + .hsync_end = 480 + 10 + 10, 281 + .htotal = 480 + 10 + 10 + 10, 282 + .vdisplay = 854, 283 + .vsync_start = 854 + 10, 284 + .vsync_end = 854 + 10 + 10, 285 + .vtotal = 854 + 10 + 10 + 10, 286 + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 287 + .type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER, 288 + }; 289 + 290 + static const struct ili9806e_spi_panel_desc rk050hr345_ct106a_desc = { 291 + .init_sequence = rk050hr345_ct106a_init, 292 + .display_mode = &rk050hr345_ct106a_mode, 293 + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, 294 + .bus_flags = DRM_BUS_FLAG_DE_HIGH | 295 + DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE, 296 + }; 297 + 298 + static const struct of_device_id ili9806e_spi_of_match[] = { 299 + { .compatible = "rocktech,rk050hr345-ct106a", .data = &rk050hr345_ct106a_desc }, 300 + { } 301 + }; 302 + MODULE_DEVICE_TABLE(of, ili9806e_spi_of_match); 303 + 304 + static const struct spi_device_id ili9806e_spi_ids[] = { 305 + { "rk050hr345-ct106a", }, 306 + { /* sentinel */ } 307 + }; 308 + MODULE_DEVICE_TABLE(spi, ili9806e_spi_ids); 309 + 310 + static struct spi_driver ili9806e_spi_driver = { 311 + .driver = { 312 + .name = "ili9806e-spi", 313 + .of_match_table = ili9806e_spi_of_match, 314 + }, 315 + .probe = ili9806e_spi_probe, 316 + .remove = ili9806e_spi_remove, 317 + .id_table = ili9806e_spi_ids, 318 + }; 319 + module_spi_driver(ili9806e_spi_driver); 320 + 321 + MODULE_AUTHOR("Dario Binacchi <dario.binacchi@amarulasolutions.com>"); 322 + MODULE_DESCRIPTION("Ilitek ILI9806E LCD SPI Driver"); 323 + MODULE_LICENSE("GPL");