* [PATCH v3 2/4] drm/panel: ilitek-ili9806e: split core and DSI logic
2026-02-03 7:54 [PATCH v3 0/4] drm/panel: ilitek-ili9806e: add Rocktech RK050HR345-CT106A support Dario Binacchi
2026-02-03 7:54 ` [PATCH v3 1/4] drm/panel: ilitek-ili9806e: rename to specific DSI driver Dario Binacchi
@ 2026-02-03 7:54 ` Dario Binacchi
2026-02-03 16:21 ` kernel test robot
2026-02-03 7:54 ` [PATCH v3 3/4] dt-bindings: ili9806e: add Rocktech RK050HR345-CT106A display Dario Binacchi
` (2 subsequent siblings)
4 siblings, 1 reply; 8+ messages in thread
From: Dario Binacchi @ 2026-02-03 7:54 UTC (permalink / raw)
To: linux-kernel
Cc: linux-amarula, Dario Binacchi, David Airlie, Jessica Zhang,
Maarten Lankhorst, Maxime Ripard, Michael Walle, Neil Armstrong,
Simona Vetter, Thomas Zimmermann, dri-devel
Split the driver to support multiple transport buses. The core logic
(power, GPIO, backlight) is moved to a dedicated core module, while
DSI-specific code is restricted to the DSI module.
Introduce DRM_PANEL_ILITEK_ILI9806E_CORE as a hidden Kconfig symbol
selected by the bus-specific configuration.
Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
---
(no changes since v1)
MAINTAINERS | 2 +-
drivers/gpu/drm/panel/Kconfig | 8 +-
drivers/gpu/drm/panel/Makefile | 1 +
.../drm/panel/panel-ilitek-ili9806e-core.c | 129 +++++++++++++++
.../drm/panel/panel-ilitek-ili9806e-core.h | 15 ++
.../gpu/drm/panel/panel-ilitek-ili9806e-dsi.c | 151 +++++-------------
6 files changed, 195 insertions(+), 111 deletions(-)
create mode 100644 drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c
create mode 100644 drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 46941413ed5c..b856a62666dc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7917,7 +7917,7 @@ F: drivers/gpu/drm/panel/panel-ilitek-ili9805.c
DRM DRIVER FOR ILITEK ILI9806E PANELS
M: Michael Walle <mwalle@kernel.org>
S: Maintained
-F: drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c
+F: drivers/gpu/drm/panel/panel-ilitek-ili9806e-*
DRM DRIVER FOR JADARD JD9365DA-H3 MIPI-DSI LCD PANELS
M: Jagan Teki <jagan@edgeble.ai>
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 692cd474910d..333e981eda96 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -257,11 +257,15 @@ config DRM_PANEL_ILITEK_ILI9805
Say Y if you want to enable support for panels based on the
Ilitek ILI9805 controller.
+config DRM_PANEL_ILITEK_ILI9806E_CORE
+ tristate
+ depends on OF
+ depends on BACKLIGHT_CLASS_DEVICE
+
config DRM_PANEL_ILITEK_ILI9806E_DSI
tristate "Ilitek ILI9806E-based DSI panels"
- depends on OF
depends on DRM_MIPI_DSI
- depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_PANEL_ILITEK_ILI9806E_CORE
help
Say Y if you want to enable support for panels based on the
Ilitek ILI9806E controller using DSI.
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 00071a983242..13034cadb8d8 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_DRM_PANEL_HYDIS_HV101HD1) += panel-hydis-hv101hd1.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_ILI9805) += panel-ilitek-ili9805.o
+obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9806E_CORE) += panel-ilitek-ili9806e-core.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9806E_DSI) += panel-ilitek-ili9806e-dsi.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9881C) += panel-ilitek-ili9881c.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9882T) += panel-ilitek-ili9882t.o
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c
new file mode 100644
index 000000000000..c088685d9d85
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Ilitek ILI9806E core driver.
+ *
+ * Copyright (c) 2026 Amarula Solutions, Dario Binacchi <dario.binacchi@amarulasolutions.com>
+ */
+
+#include <drm/drm_panel.h>
+
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+
+#include "panel-ilitek-ili9806e-core.h"
+
+struct ili9806e {
+ void *transport;
+ struct drm_panel panel;
+
+ struct regulator_bulk_data supplies[2];
+ struct gpio_desc *reset_gpio;
+};
+
+static const char * const regulator_names[] = {
+ "vdd",
+ "vccio",
+};
+
+void *ili9806e_get_transport(struct drm_panel *panel)
+{
+ struct ili9806e *ctx = container_of(panel, struct ili9806e, panel);
+
+ return ctx->transport;
+}
+EXPORT_SYMBOL_GPL(ili9806e_get_transport);
+
+int ili9806e_power_on(struct device *dev)
+{
+ struct ili9806e *ctx = dev_get_drvdata(dev);
+ int ret;
+
+ gpiod_set_value(ctx->reset_gpio, 1);
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ if (ret) {
+ dev_err(dev, "regulator bulk enable failed: %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(10000, 20000);
+ gpiod_set_value(ctx->reset_gpio, 0);
+ usleep_range(10000, 20000);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ili9806e_power_on);
+
+int ili9806e_power_off(struct device *dev)
+{
+ struct ili9806e *ctx = dev_get_drvdata(dev);
+ int ret;
+
+ gpiod_set_value(ctx->reset_gpio, 1);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ if (ret)
+ dev_err(dev, "regulator bulk disable failed: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ili9806e_power_off);
+
+int ili9806e_probe(struct device *dev, void *transport,
+ const struct drm_panel_funcs *funcs,
+ int connector_type)
+{
+ struct ili9806e *ctx;
+ int i, ret;
+
+ ctx = devm_kzalloc(dev, sizeof(struct ili9806e), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, ctx);
+ ctx->transport = transport;
+
+ for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++)
+ ctx->supplies[i].supply = regulator_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
+ ctx->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get regulators\n");
+
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ctx->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
+ "Failed to get reset-gpios\n");
+
+ drm_panel_init(&ctx->panel, dev, funcs, connector_type);
+
+ ret = drm_panel_of_backlight(&ctx->panel);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get backlight\n");
+
+ ctx->panel.prepare_prev_first = true;
+ drm_panel_add(&ctx->panel);
+
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(ili9806e_probe);
+
+void ili9806e_remove(struct device *dev)
+{
+ struct ili9806e *ctx = dev_get_drvdata(dev);
+
+ drm_panel_remove(&ctx->panel);
+}
+EXPORT_SYMBOL_GPL(ili9806e_remove);
+
+MODULE_AUTHOR("Dario Binacchi <dario.binacchi@amarulasolutions.com>");
+MODULE_AUTHOR("Gunnar Dibbern <gunnar.dibbern@lht.dlh.de>");
+MODULE_AUTHOR("Michael Walle <mwalle@kernel.org>");
+MODULE_DESCRIPTION("Ilitek ILI9806E Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.h b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.h
new file mode 100644
index 000000000000..dddece62cf42
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _PANEL_ILITEK_ILI9806E_CORE_H
+#define _PANEL_ILITEK_ILI9806E_CORE_H
+
+void *ili9806e_get_transport(struct drm_panel *panel);
+int ili9806e_power_off(struct device *dev);
+int ili9806e_power_on(struct device *dev);
+
+int ili9806e_probe(struct device *dev, void *transport,
+ const struct drm_panel_funcs *funcs,
+ int connector_type);
+void ili9806e_remove(struct device *dev);
+
+#endif /* _PANEL_ILITEK_ILI9806E_CORE_H */
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c
index c337c4f1a1c7..ecdbed8d4a3a 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c
@@ -1,15 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
-#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
-#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/property.h>
-#include <linux/regulator/consumer.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
@@ -18,7 +15,9 @@
#include <video/mipi_display.h>
-struct panel_desc {
+#include "panel-ilitek-ili9806e-core.h"
+
+struct ili9806e_dsi_panel_desc {
const struct drm_display_mode *display_mode;
unsigned long mode_flags;
enum mipi_dsi_pixel_format format;
@@ -26,60 +25,13 @@ struct panel_desc {
void (*init_sequence)(struct mipi_dsi_multi_context *ctx);
};
-struct ili9806e_panel {
- struct drm_panel panel;
+struct ili9806e_dsi_panel {
struct mipi_dsi_device *dsi;
- struct gpio_desc *reset_gpio;
- struct regulator_bulk_data supplies[2];
- const struct panel_desc *desc;
+ const struct ili9806e_dsi_panel_desc *desc;
enum drm_panel_orientation orientation;
};
-static const char * const regulator_names[] = {
- "vdd",
- "vccio",
-};
-
-static inline struct ili9806e_panel *to_ili9806e_panel(struct drm_panel *panel)
-{
- return container_of(panel, struct ili9806e_panel, panel);
-}
-
-static int ili9806e_power_on(struct ili9806e_panel *ctx)
-{
- struct mipi_dsi_device *dsi = ctx->dsi;
- int ret;
-
- gpiod_set_value(ctx->reset_gpio, 1);
-
- ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
- if (ret < 0) {
- dev_err(&dsi->dev, "regulator bulk enable failed: %d\n", ret);
- return ret;
- }
-
- usleep_range(10000, 20000);
- gpiod_set_value(ctx->reset_gpio, 0);
- usleep_range(10000, 20000);
-
- return 0;
-}
-
-static int ili9806e_power_off(struct ili9806e_panel *ctx)
-{
- struct mipi_dsi_device *dsi = ctx->dsi;
- int ret;
-
- gpiod_set_value(ctx->reset_gpio, 1);
-
- ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
- if (ret)
- dev_err(&dsi->dev, "regulator bulk disable failed: %d\n", ret);
-
- return ret;
-}
-
-static int ili9806e_on(struct ili9806e_panel *ili9806e)
+static int ili9806e_dsi_on(struct ili9806e_dsi_panel *ili9806e)
{
struct mipi_dsi_multi_context ctx = { .dsi = ili9806e->dsi };
@@ -93,7 +45,7 @@ static int ili9806e_on(struct ili9806e_panel *ili9806e)
return ctx.accum_err;
}
-static int ili9806e_off(struct ili9806e_panel *panel)
+static int ili9806e_dsi_off(struct ili9806e_dsi_panel *panel)
{
struct mipi_dsi_multi_context ctx = { .dsi = panel->dsi };
@@ -104,88 +56,75 @@ static int ili9806e_off(struct ili9806e_panel *panel)
return ctx.accum_err;
}
-static int ili9806e_prepare(struct drm_panel *panel)
+static int ili9806e_dsi_prepare(struct drm_panel *panel)
{
- struct ili9806e_panel *ctx = to_ili9806e_panel(panel);
+ struct ili9806e_dsi_panel *ctx = ili9806e_get_transport(panel);
+ struct device *dev = &ctx->dsi->dev;
int ret;
- ret = ili9806e_power_on(ctx);
+ ret = ili9806e_power_on(dev);
if (ret < 0)
return ret;
- ret = ili9806e_on(ctx);
+ ret = ili9806e_dsi_on(ctx);
if (ret < 0) {
- ili9806e_power_off(ctx);
+ ili9806e_power_off(dev);
return ret;
}
return 0;
}
-static int ili9806e_unprepare(struct drm_panel *panel)
+static int ili9806e_dsi_unprepare(struct drm_panel *panel)
{
- struct ili9806e_panel *ctx = to_ili9806e_panel(panel);
- struct mipi_dsi_device *dsi = ctx->dsi;
+ struct ili9806e_dsi_panel *ctx = ili9806e_get_transport(panel);
+ struct device *dev = &ctx->dsi->dev;
int ret;
- ili9806e_off(ctx);
+ ili9806e_dsi_off(ctx);
- ret = ili9806e_power_off(ctx);
+ ret = ili9806e_power_off(dev);
if (ret < 0)
- dev_err(&dsi->dev, "power off failed: %d\n", ret);
+ dev_err(dev, "power off failed: %d\n", ret);
return ret;
}
-static int ili9806e_get_modes(struct drm_panel *panel,
+static int ili9806e_dsi_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
- struct ili9806e_panel *ctx = to_ili9806e_panel(panel);
+ struct ili9806e_dsi_panel *ctx = ili9806e_get_transport(panel);
const struct drm_display_mode *mode = ctx->desc->display_mode;
return drm_connector_helper_get_modes_fixed(connector, mode);
}
-static enum drm_panel_orientation ili9806e_get_orientation(struct drm_panel *panel)
+static enum drm_panel_orientation ili9806e_dsi_get_orientation(struct drm_panel *panel)
{
- struct ili9806e_panel *ctx = to_ili9806e_panel(panel);
+ struct ili9806e_dsi_panel *ctx = ili9806e_get_transport(panel);
return ctx->orientation;
}
-static const struct drm_panel_funcs ili9806e_funcs = {
- .prepare = ili9806e_prepare,
- .unprepare = ili9806e_unprepare,
- .get_modes = ili9806e_get_modes,
- .get_orientation = ili9806e_get_orientation,
+static const struct drm_panel_funcs ili9806e_dsi_funcs = {
+ .prepare = ili9806e_dsi_prepare,
+ .unprepare = ili9806e_dsi_unprepare,
+ .get_modes = ili9806e_dsi_get_modes,
+ .get_orientation = ili9806e_dsi_get_orientation,
};
static int ili9806e_dsi_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
- struct ili9806e_panel *ctx;
- int i, ret;
+ struct ili9806e_dsi_panel *ctx;
+ int ret;
- ctx = devm_drm_panel_alloc(dev, struct ili9806e_panel, panel, &ili9806e_funcs,
- DRM_MODE_CONNECTOR_DSI);
- if (IS_ERR(ctx))
- return PTR_ERR(ctx);
+ ctx = devm_kzalloc(dev, sizeof(struct ili9806e_dsi_panel), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
ctx->desc = device_get_match_data(dev);
- for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++)
- ctx->supplies[i].supply = regulator_names[i];
-
- ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
- ctx->supplies);
- if (ret < 0)
- return ret;
-
- ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
- if (IS_ERR(ctx->reset_gpio))
- return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
- "Failed to get reset-gpios\n");
-
mipi_dsi_set_drvdata(dsi, ctx);
ctx->dsi = dsi;
@@ -197,17 +136,15 @@ static int ili9806e_dsi_probe(struct mipi_dsi_device *dsi)
if (ret)
return dev_err_probe(dev, ret, "Failed to get orientation\n");
- ret = drm_panel_of_backlight(&ctx->panel);
+ ret = ili9806e_probe(dev, ctx, &ili9806e_dsi_funcs,
+ DRM_MODE_CONNECTOR_DSI);
if (ret)
- return dev_err_probe(dev, ret, "Failed to get backlight\n");
-
- ctx->panel.prepare_prev_first = true;
- drm_panel_add(&ctx->panel);
+ return ret;
ret = mipi_dsi_attach(dsi);
if (ret < 0) {
dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
- drm_panel_remove(&ctx->panel);
+ ili9806e_remove(dev);
return ret;
}
@@ -216,10 +153,8 @@ static int ili9806e_dsi_probe(struct mipi_dsi_device *dsi)
static void ili9806e_dsi_remove(struct mipi_dsi_device *dsi)
{
- struct ili9806e_panel *ctx = mipi_dsi_get_drvdata(dsi);
-
mipi_dsi_detach(dsi);
- drm_panel_remove(&ctx->panel);
+ ili9806e_remove(&dsi->dev);
}
static void com35h3p70ulc_init(struct mipi_dsi_multi_context *ctx)
@@ -369,7 +304,7 @@ static const struct drm_display_mode com35h3p70ulc_default_mode = {
.height_mm = 71,
};
-static const struct panel_desc com35h3p70ulc_desc = {
+static const struct ili9806e_dsi_panel_desc com35h3p70ulc_desc = {
.init_sequence = com35h3p70ulc_init,
.display_mode = &com35h3p70ulc_default_mode,
.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
@@ -533,7 +468,7 @@ static const struct drm_display_mode dmt028vghmcmi_1d_default_mode = {
.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
};
-static const struct panel_desc dmt028vghmcmi_1d_desc = {
+static const struct ili9806e_dsi_panel_desc dmt028vghmcmi_1d_desc = {
.init_sequence = dmt028vghmcmi_1d_init,
.display_mode = &dmt028vghmcmi_1d_default_mode,
.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
@@ -542,17 +477,17 @@ static const struct panel_desc dmt028vghmcmi_1d_desc = {
.lanes = 2,
};
-static const struct of_device_id ili9806e_of_match[] = {
+static const struct of_device_id ili9806e_dsi_of_match[] = {
{ .compatible = "densitron,dmt028vghmcmi-1d", .data = &dmt028vghmcmi_1d_desc },
{ .compatible = "ortustech,com35h3p70ulc", .data = &com35h3p70ulc_desc },
{ }
};
-MODULE_DEVICE_TABLE(of, ili9806e_of_match);
+MODULE_DEVICE_TABLE(of, ili9806e_dsi_of_match);
static struct mipi_dsi_driver ili9806e_dsi_driver = {
.driver = {
.name = "ili9806e-dsi",
- .of_match_table = ili9806e_of_match,
+ .of_match_table = ili9806e_dsi_of_match,
},
.probe = ili9806e_dsi_probe,
.remove = ili9806e_dsi_remove,
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v3 4/4] drm/panel: ilitek-ili9806e: add Rocktech RK050HR345-CT106A SPI panel
2026-02-03 7:54 [PATCH v3 0/4] drm/panel: ilitek-ili9806e: add Rocktech RK050HR345-CT106A support Dario Binacchi
` (2 preceding siblings ...)
2026-02-03 7:54 ` [PATCH v3 3/4] dt-bindings: ili9806e: add Rocktech RK050HR345-CT106A display Dario Binacchi
@ 2026-02-03 7:54 ` Dario Binacchi
2026-02-03 17:04 ` kernel test robot
2026-02-04 12:47 ` [PATCH v3 0/4] drm/panel: ilitek-ili9806e: add Rocktech RK050HR345-CT106A support Neil Armstrong
4 siblings, 1 reply; 8+ messages in thread
From: Dario Binacchi @ 2026-02-03 7:54 UTC (permalink / raw)
To: linux-kernel
Cc: linux-amarula, Dario Binacchi, David Airlie, Jessica Zhang,
Maarten Lankhorst, Maxime Ripard, Michael Walle, Neil Armstrong,
Simona Vetter, Thomas Zimmermann, dri-devel
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>
---
Changes in v3:
- Add Reviewed-by tag of Rob Herring
Changes in v2:
- Introduce DRM_PANEL_ILITEK_ILI9806E_CORE hidden kconfig option.
- Split core and DSI logic.
- Restore vdd-supply as required for both DSI and SPI types in the
dt-bindings.
- Dop useless settings in case of rocktech,rk050hr345-ct106a in the
dt-bindings.
MAINTAINERS | 1 +
drivers/gpu/drm/panel/Kconfig | 10 +
drivers/gpu/drm/panel/Makefile | 1 +
.../drm/panel/panel-ilitek-ili9806e-core.c | 31 +-
.../gpu/drm/panel/panel-ilitek-ili9806e-spi.c | 323 ++++++++++++++++++
5 files changed, 353 insertions(+), 13 deletions(-)
create mode 100644 drivers/gpu/drm/panel/panel-ilitek-ili9806e-spi.c
diff --git a/MAINTAINERS b/MAINTAINERS
index b856a62666dc..6aaa32eab636 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7916,6 +7916,7 @@ F: drivers/gpu/drm/panel/panel-ilitek-ili9805.c
DRM DRIVER FOR ILITEK ILI9806E PANELS
M: Michael Walle <mwalle@kernel.org>
+M: Dario Binacchi <dario.binacchi@amarulasolutions.com>
S: Maintained
F: drivers/gpu/drm/panel/panel-ilitek-ili9806e-*
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 333e981eda96..d1cb04e838c5 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -270,6 +270,16 @@ config DRM_PANEL_ILITEK_ILI9806E_DSI
Say Y if you want to enable support for panels based on the
Ilitek ILI9806E controller using DSI.
+config DRM_PANEL_ILITEK_ILI9806E_SPI
+ tristate "Ilitek ILI9806E-based RGB SPI panel"
+ depends on SPI
+ select DRM_PANEL_ILITEK_ILI9806E_CORE
+ select DRM_MIPI_DBI
+ select VIDEOMODE_HELPERS
+ help
+ Say Y if you want to enable support for panels based on the
+ Ilitek ILI9806E controller using SPI.
+
config DRM_PANEL_ILITEK_ILI9881C
tristate "Ilitek ILI9881C-based panels"
depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 13034cadb8d8..3697687651fe 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -28,6 +28,7 @@ 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_ILI9806E_CORE) += panel-ilitek-ili9806e-core.o
obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9806E_DSI) += panel-ilitek-ili9806e-dsi.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_ILI9882T) += panel-ilitek-ili9882t.o
obj-$(CONFIG_DRM_PANEL_INNOLUX_EJ030NA) += panel-innolux-ej030na.o
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c
index c088685d9d85..be2cf1440155 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-core.c
@@ -11,6 +11,7 @@
#include <linux/export.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
@@ -20,15 +21,11 @@ struct ili9806e {
void *transport;
struct drm_panel panel;
+ unsigned int num_supplies;
struct regulator_bulk_data supplies[2];
struct gpio_desc *reset_gpio;
};
-static const char * const regulator_names[] = {
- "vdd",
- "vccio",
-};
-
void *ili9806e_get_transport(struct drm_panel *panel)
{
struct ili9806e *ctx = container_of(panel, struct ili9806e, panel);
@@ -44,7 +41,7 @@ int ili9806e_power_on(struct device *dev)
gpiod_set_value(ctx->reset_gpio, 1);
- ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ ret = regulator_bulk_enable(ctx->num_supplies, ctx->supplies);
if (ret) {
dev_err(dev, "regulator bulk enable failed: %d\n", ret);
return ret;
@@ -65,7 +62,7 @@ int ili9806e_power_off(struct device *dev)
gpiod_set_value(ctx->reset_gpio, 1);
- ret = regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ ret = regulator_bulk_disable(ctx->num_supplies, ctx->supplies);
if (ret)
dev_err(dev, "regulator bulk disable failed: %d\n", ret);
@@ -78,7 +75,8 @@ int ili9806e_probe(struct device *dev, void *transport,
int connector_type)
{
struct ili9806e *ctx;
- int i, ret;
+ bool set_prepare_prev_first = false;
+ int ret;
ctx = devm_kzalloc(dev, sizeof(struct ili9806e), GFP_KERNEL);
if (!ctx)
@@ -87,11 +85,16 @@ int ili9806e_probe(struct device *dev, void *transport,
dev_set_drvdata(dev, ctx);
ctx->transport = transport;
- for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++)
- ctx->supplies[i].supply = regulator_names[i];
+ ctx->supplies[ctx->num_supplies++].supply = "vdd";
+ if (of_device_is_compatible(dev->of_node,
+ "densitron,dmt028vghmcmi-1d") ||
+ of_device_is_compatible(dev->of_node,
+ "ortustech,com35h3p70ulc")) {
+ ctx->supplies[ctx->num_supplies++].supply = "vccio";
+ set_prepare_prev_first = true;
+ }
- ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
- ctx->supplies);
+ ret = devm_regulator_bulk_get(dev, ctx->num_supplies, ctx->supplies);
if (ret)
return dev_err_probe(dev, ret, "failed to get regulators\n");
@@ -106,7 +109,9 @@ int ili9806e_probe(struct device *dev, void *transport,
if (ret)
return dev_err_probe(dev, ret, "Failed to get backlight\n");
- ctx->panel.prepare_prev_first = true;
+ if (set_prepare_prev_first)
+ ctx->panel.prepare_prev_first = true;
+
drm_panel_add(&ctx->panel);
return 0;
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-spi.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-spi.c
new file mode 100644
index 000000000000..9d10b0d28f52
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-spi.c
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SPI interface to the Ilitek ILI9806E panel.
+ *
+ * Copyright (c) 2026 Amarula Solutions, Dario Binacchi <dario.binacchi@amarulasolutions.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/media-bus-format.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include <drm/drm_mipi_dbi.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+
+#include <video/mipi_display.h>
+
+#include "panel-ilitek-ili9806e-core.h"
+
+struct ili9806e_spi_panel {
+ struct spi_device *spi;
+ struct mipi_dbi dbi;
+ const struct ili9806e_spi_panel_desc *desc;
+};
+
+struct ili9806e_spi_panel_desc {
+ const struct drm_display_mode *display_mode;
+ u32 bus_format;
+ u32 bus_flags;
+ void (*init_sequence)(struct ili9806e_spi_panel *ctx);
+};
+
+static int ili9806e_spi_off(struct ili9806e_spi_panel *ctx)
+{
+ struct mipi_dbi *dbi = &ctx->dbi;
+
+ mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF, 0x00);
+ mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE, 0x00);
+
+ return 0;
+}
+
+static int ili9806e_spi_unprepare(struct drm_panel *panel)
+{
+ struct ili9806e_spi_panel *ctx = ili9806e_get_transport(panel);
+ struct device *dev = &ctx->spi->dev;
+ int ret;
+
+ ili9806e_spi_off(ctx);
+
+ ret = ili9806e_power_off(dev);
+ if (ret)
+ dev_err(dev, "power off failed: %d\n", ret);
+
+ return 0;
+}
+
+static int ili9806e_spi_prepare(struct drm_panel *panel)
+{
+ struct ili9806e_spi_panel *ctx = ili9806e_get_transport(panel);
+ struct device *dev = &ctx->spi->dev;
+ int ret;
+
+ ret = ili9806e_power_on(dev);
+ if (ret)
+ return ret;
+
+ if (ctx->desc->init_sequence)
+ ctx->desc->init_sequence(ctx);
+
+ return 0;
+}
+
+static int ili9806e_spi_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct ili9806e_spi_panel *ctx = ili9806e_get_transport(panel);
+ const struct ili9806e_spi_panel_desc *desc = ctx->desc;
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(connector->dev, desc->display_mode);
+ if (!mode)
+ return -ENOMEM;
+
+ drm_mode_set_name(mode);
+
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
+ connector->display_info.bus_flags = desc->bus_flags;
+ drm_display_info_set_bus_formats(&connector->display_info,
+ &desc->bus_format, 1);
+
+ drm_mode_probed_add(connector, mode);
+
+ return 1;
+}
+
+static const struct drm_panel_funcs ili9806e_spi_funcs = {
+ .unprepare = ili9806e_spi_unprepare,
+ .prepare = ili9806e_spi_prepare,
+ .get_modes = ili9806e_spi_get_modes,
+};
+
+static int ili9806e_spi_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct ili9806e_spi_panel *ctx;
+ int err;
+
+ ctx = devm_kzalloc(dev, sizeof(struct ili9806e_spi_panel), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->spi = spi;
+ ctx->desc = device_get_match_data(dev);
+
+ err = mipi_dbi_spi_init(spi, &ctx->dbi, NULL);
+ if (err)
+ return dev_err_probe(dev, err, "MIPI DBI init failed\n");
+
+ return ili9806e_probe(dev, ctx, &ili9806e_spi_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+}
+
+static void ili9806e_spi_remove(struct spi_device *spi)
+{
+ ili9806e_remove(&spi->dev);
+}
+
+static void rk050hr345_ct106a_init(struct ili9806e_spi_panel *ctx)
+{
+ struct mipi_dbi *dbi = &ctx->dbi;
+
+ /* Switch to page 1 */
+ mipi_dbi_command(dbi, 0xff, 0xff, 0x98, 0x06, 0x04, 0x01);
+ /* Interface Settings */
+ mipi_dbi_command(dbi, 0x08, 0x10);
+ mipi_dbi_command(dbi, 0x21, 0x01);
+ /* Panel Settings */
+ mipi_dbi_command(dbi, 0x30, 0x01);
+ mipi_dbi_command(dbi, 0x31, 0x00);
+ /* Power Control */
+ mipi_dbi_command(dbi, 0x40, 0x15);
+ mipi_dbi_command(dbi, 0x41, 0x44);
+ mipi_dbi_command(dbi, 0x42, 0x03);
+ mipi_dbi_command(dbi, 0x43, 0x09);
+ mipi_dbi_command(dbi, 0x44, 0x09);
+ mipi_dbi_command(dbi, 0x50, 0x78);
+ mipi_dbi_command(dbi, 0x51, 0x78);
+ mipi_dbi_command(dbi, 0x52, 0x00);
+ mipi_dbi_command(dbi, 0x53, 0x3a);
+ mipi_dbi_command(dbi, 0x57, 0x50);
+ /* Timing Control */
+ mipi_dbi_command(dbi, 0x60, 0x07);
+ mipi_dbi_command(dbi, 0x61, 0x00);
+ mipi_dbi_command(dbi, 0x62, 0x08);
+ mipi_dbi_command(dbi, 0x63, 0x00);
+ /* Gamma Settings */
+ mipi_dbi_command(dbi, 0xa0, 0x00);
+ mipi_dbi_command(dbi, 0xa1, 0x03);
+ mipi_dbi_command(dbi, 0xa2, 0x0b);
+ mipi_dbi_command(dbi, 0xa3, 0x0f);
+ mipi_dbi_command(dbi, 0xa4, 0x0b);
+ mipi_dbi_command(dbi, 0xa5, 0x1b);
+ mipi_dbi_command(dbi, 0xa6, 0x0a);
+ mipi_dbi_command(dbi, 0xa7, 0x0a);
+ mipi_dbi_command(dbi, 0xa8, 0x02);
+ mipi_dbi_command(dbi, 0xa9, 0x07);
+ mipi_dbi_command(dbi, 0xaa, 0x05);
+ mipi_dbi_command(dbi, 0xab, 0x03);
+ mipi_dbi_command(dbi, 0xac, 0x0e);
+ mipi_dbi_command(dbi, 0xad, 0x32);
+ mipi_dbi_command(dbi, 0xae, 0x2d);
+ mipi_dbi_command(dbi, 0xaf, 0x00);
+ mipi_dbi_command(dbi, 0xc0, 0x00);
+ mipi_dbi_command(dbi, 0xc1, 0x03);
+ mipi_dbi_command(dbi, 0xc2, 0x0e);
+ mipi_dbi_command(dbi, 0xc3, 0x10);
+ mipi_dbi_command(dbi, 0xc4, 0x09);
+ mipi_dbi_command(dbi, 0xc5, 0x17);
+ mipi_dbi_command(dbi, 0xc6, 0x09);
+ mipi_dbi_command(dbi, 0xc7, 0x07);
+ mipi_dbi_command(dbi, 0xc8, 0x04);
+ mipi_dbi_command(dbi, 0xc9, 0x09);
+ mipi_dbi_command(dbi, 0xca, 0x06);
+ mipi_dbi_command(dbi, 0xcb, 0x06);
+ mipi_dbi_command(dbi, 0xcc, 0x0c);
+ mipi_dbi_command(dbi, 0xcd, 0x25);
+ mipi_dbi_command(dbi, 0xce, 0x20);
+ mipi_dbi_command(dbi, 0xcf, 0x00);
+
+ /* Switch to page 6 */
+ mipi_dbi_command(dbi, 0xff, 0xff, 0x98, 0x06, 0x04, 0x06);
+ /* GIP settings */
+ mipi_dbi_command(dbi, 0x00, 0x21);
+ mipi_dbi_command(dbi, 0x01, 0x09);
+ mipi_dbi_command(dbi, 0x02, 0x00);
+ mipi_dbi_command(dbi, 0x03, 0x00);
+ mipi_dbi_command(dbi, 0x04, 0x01);
+ mipi_dbi_command(dbi, 0x05, 0x01);
+ mipi_dbi_command(dbi, 0x06, 0x80);
+ mipi_dbi_command(dbi, 0x07, 0x05);
+ mipi_dbi_command(dbi, 0x08, 0x02);
+ mipi_dbi_command(dbi, 0x09, 0x80);
+ mipi_dbi_command(dbi, 0x0a, 0x00);
+ mipi_dbi_command(dbi, 0x0b, 0x00);
+ mipi_dbi_command(dbi, 0x0c, 0x0a);
+ mipi_dbi_command(dbi, 0x0d, 0x0a);
+ mipi_dbi_command(dbi, 0x0e, 0x00);
+ mipi_dbi_command(dbi, 0x0f, 0x00);
+ mipi_dbi_command(dbi, 0x10, 0xe0);
+ mipi_dbi_command(dbi, 0x11, 0xe4);
+ mipi_dbi_command(dbi, 0x12, 0x04);
+ mipi_dbi_command(dbi, 0x13, 0x00);
+ mipi_dbi_command(dbi, 0x14, 0x00);
+ mipi_dbi_command(dbi, 0x15, 0xc0);
+ mipi_dbi_command(dbi, 0x16, 0x08);
+ mipi_dbi_command(dbi, 0x17, 0x00);
+ mipi_dbi_command(dbi, 0x18, 0x00);
+ mipi_dbi_command(dbi, 0x19, 0x00);
+ mipi_dbi_command(dbi, 0x1a, 0x00);
+ mipi_dbi_command(dbi, 0x1b, 0x00);
+ mipi_dbi_command(dbi, 0x1c, 0x00);
+ mipi_dbi_command(dbi, 0x1d, 0x00);
+ mipi_dbi_command(dbi, 0x20, 0x01);
+ mipi_dbi_command(dbi, 0x21, 0x23);
+ mipi_dbi_command(dbi, 0x22, 0x45);
+ mipi_dbi_command(dbi, 0x23, 0x67);
+ mipi_dbi_command(dbi, 0x24, 0x01);
+ mipi_dbi_command(dbi, 0x25, 0x23);
+ mipi_dbi_command(dbi, 0x26, 0x45);
+ mipi_dbi_command(dbi, 0x27, 0x67);
+ mipi_dbi_command(dbi, 0x30, 0x01);
+ mipi_dbi_command(dbi, 0x31, 0x11);
+ mipi_dbi_command(dbi, 0x32, 0x00);
+ mipi_dbi_command(dbi, 0x33, 0xee);
+ mipi_dbi_command(dbi, 0x34, 0xff);
+ mipi_dbi_command(dbi, 0x35, 0xbb);
+ mipi_dbi_command(dbi, 0x36, 0xca);
+ mipi_dbi_command(dbi, 0x37, 0xdd);
+ mipi_dbi_command(dbi, 0x38, 0xac);
+ mipi_dbi_command(dbi, 0x39, 0x76);
+ mipi_dbi_command(dbi, 0x3a, 0x67);
+ mipi_dbi_command(dbi, 0x3b, 0x22);
+ mipi_dbi_command(dbi, 0x3c, 0x22);
+ mipi_dbi_command(dbi, 0x3d, 0x22);
+ mipi_dbi_command(dbi, 0x3e, 0x22);
+ mipi_dbi_command(dbi, 0x3f, 0x22);
+ mipi_dbi_command(dbi, 0x40, 0x22);
+ mipi_dbi_command(dbi, 0x52, 0x10);
+ mipi_dbi_command(dbi, 0x53, 0x10);
+
+ /* Switch to page 7 */
+ mipi_dbi_command(dbi, 0xff, 0xff, 0x98, 0x06, 0x04, 0x07);
+ mipi_dbi_command(dbi, 0x17, 0x22);
+ mipi_dbi_command(dbi, 0x02, 0x77);
+ mipi_dbi_command(dbi, 0xe1, 0x79);
+ mipi_dbi_command(dbi, 0xb3, 0x10);
+
+ /* Switch to page 0 */
+ mipi_dbi_command(dbi, 0xff, 0xff, 0x98, 0x06, 0x04, 0x00);
+ mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, 0x00); // 0x36
+ mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); // 0x11
+
+ msleep(120);
+
+ mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
+
+ msleep(120);
+}
+
+static const struct drm_display_mode rk050hr345_ct106a_mode = {
+ .width_mm = 62,
+ .height_mm = 110,
+ .clock = 27000,
+ .hdisplay = 480,
+ .hsync_start = 480 + 10,
+ .hsync_end = 480 + 10 + 10,
+ .htotal = 480 + 10 + 10 + 10,
+ .vdisplay = 854,
+ .vsync_start = 854 + 10,
+ .vsync_end = 854 + 10 + 10,
+ .vtotal = 854 + 10 + 10 + 10,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+ .type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER,
+};
+
+static const struct ili9806e_spi_panel_desc rk050hr345_ct106a_desc = {
+ .init_sequence = rk050hr345_ct106a_init,
+ .display_mode = &rk050hr345_ct106a_mode,
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH |
+ DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
+};
+
+static const struct of_device_id ili9806e_spi_of_match[] = {
+ { .compatible = "rocktech,rk050hr345-ct106a", .data = &rk050hr345_ct106a_desc },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ili9806e_spi_of_match);
+
+static const struct spi_device_id ili9806e_spi_ids[] = {
+ { "rk050hr345-ct106a", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(spi, ili9806e_spi_ids);
+
+static struct spi_driver ili9806e_spi_driver = {
+ .driver = {
+ .name = "ili9806e-spi",
+ .of_match_table = ili9806e_spi_of_match,
+ },
+ .probe = ili9806e_spi_probe,
+ .remove = ili9806e_spi_remove,
+ .id_table = ili9806e_spi_ids,
+};
+module_spi_driver(ili9806e_spi_driver);
+
+MODULE_AUTHOR("Dario Binacchi <dario.binacchi@amarulasolutions.com>");
+MODULE_DESCRIPTION("Ilitek ILI9806E LCD SPI Driver");
+MODULE_LICENSE("GPL");
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread