devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/2] drm/panel: initial support for the Ortustech COM35H3P70ULC
@ 2024-06-26 14:44 Michael Walle
  2024-06-26 14:44 ` [PATCH v2 1/2] dt-bindings: display: panel: add Ilitek ili9806e panel controller Michael Walle
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Michael Walle @ 2024-06-26 14:44 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Daniel Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Michael Walle,
	Dmitry Baryshkov
  Cc: dri-devel, devicetree, linux-kernel, Gunnar Dibbern

Add initial support for the 480x640 DSI panel from Ortustech. The
panel uses an Ilitek ILI9806E panel driver IC.

v2:
 - use drm_connector_helper_get_modes_fixed(), thanks Dmitry.
 - slight header files cleanup

Michael Walle (2):
  dt-bindings: display: panel: add Ilitek ili9806e panel controller
  drm/panel: add Ilitek ILI9806E panel driver

 .../display/panel/ilitek,ili9806e.yaml        |  63 +++
 MAINTAINERS                                   |   5 +
 drivers/gpu/drm/panel/Kconfig                 |   9 +
 drivers/gpu/drm/panel/Makefile                |   1 +
 drivers/gpu/drm/panel/panel-ilitek-ili9806e.c | 402 ++++++++++++++++++
 5 files changed, 480 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/ilitek,ili9806e.yaml
 create mode 100644 drivers/gpu/drm/panel/panel-ilitek-ili9806e.c

-- 
2.39.2


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v2 1/2] dt-bindings: display: panel: add Ilitek ili9806e panel controller
  2024-06-26 14:44 [PATCH v2 0/2] drm/panel: initial support for the Ortustech COM35H3P70ULC Michael Walle
@ 2024-06-26 14:44 ` Michael Walle
  2024-06-26 14:44 ` [PATCH v2 2/2] drm/panel: add Ilitek ILI9806E panel driver Michael Walle
  2024-06-26 16:45 ` [PATCH v2 0/2] drm/panel: initial support for the Ortustech COM35H3P70ULC Neil Armstrong
  2 siblings, 0 replies; 6+ messages in thread
From: Michael Walle @ 2024-06-26 14:44 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Daniel Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Michael Walle,
	Dmitry Baryshkov
  Cc: dri-devel, devicetree, linux-kernel, Gunnar Dibbern, Conor Dooley

Add the device tree binding for the Ilitek ILI9806E controller which can
be found on the Ortustech COME35H3P70ULC DSI display panel.

There are no peculiarities except for two different power signals.

Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Michael Walle <mwalle@kernel.org>
---
 .../display/panel/ilitek,ili9806e.yaml        | 63 +++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/ilitek,ili9806e.yaml

diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,ili9806e.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,ili9806e.yaml
new file mode 100644
index 000000000000..cfd7cc9c8725
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili9806e.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/ilitek,ili9806e.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ilitek ILI9806E based MIPI-DSI panels
+
+maintainers:
+  - Michael Walle <mwalle@kernel.org>
+
+allOf:
+  - $ref: panel-common.yaml#
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - ortustech,com35h3p70ulc
+      - const: ilitek,ili9806e
+
+  reg:
+    maxItems: 1
+
+  vdd-supply: true
+  vccio-supply: true
+
+required:
+  - compatible
+  - reg
+  - vdd-supply
+  - vccio-supply
+  - reset-gpios
+  - backlight
+  - port
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    dsi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        panel@0 {
+            compatible = "ortustech,com35h3p70ulc", "ilitek,ili9806e";
+            reg = <0>;
+            vdd-supply = <&reg_vdd_panel>;
+            vccio-supply = <&reg_vccio_panel>;
+            reset-gpios = <&gpio3 6 GPIO_ACTIVE_LOW>;
+            backlight = <&backlight>;
+
+            port {
+                panel_in: endpoint {
+                    remote-endpoint = <&dsi_out>;
+                };
+            };
+        };
+    };
+
+...
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v2 2/2] drm/panel: add Ilitek ILI9806E panel driver
  2024-06-26 14:44 [PATCH v2 0/2] drm/panel: initial support for the Ortustech COM35H3P70ULC Michael Walle
  2024-06-26 14:44 ` [PATCH v2 1/2] dt-bindings: display: panel: add Ilitek ili9806e panel controller Michael Walle
@ 2024-06-26 14:44 ` Michael Walle
  2024-06-26 16:36   ` Neil Armstrong
  2024-06-26 20:50   ` Jessica Zhang
  2024-06-26 16:45 ` [PATCH v2 0/2] drm/panel: initial support for the Ortustech COM35H3P70ULC Neil Armstrong
  2 siblings, 2 replies; 6+ messages in thread
From: Michael Walle @ 2024-06-26 14:44 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Daniel Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Michael Walle,
	Dmitry Baryshkov
  Cc: dri-devel, devicetree, linux-kernel, Gunnar Dibbern

The Ortustech COM35H3P70ULC panel is based on the ILI9806E DSI display
controller.

Co-developed-by: Gunnar Dibbern <gunnar.dibbern@lht.dlh.de>
Signed-off-by: Gunnar Dibbern <gunnar.dibbern@lht.dlh.de>
Signed-off-by: Michael Walle <mwalle@kernel.org>
---
 MAINTAINERS                                   |   5 +
 drivers/gpu/drm/panel/Kconfig                 |   9 +
 drivers/gpu/drm/panel/Makefile                |   1 +
 drivers/gpu/drm/panel/panel-ilitek-ili9806e.c | 402 ++++++++++++++++++
 4 files changed, 417 insertions(+)
 create mode 100644 drivers/gpu/drm/panel/panel-ilitek-ili9806e.c

diff --git a/MAINTAINERS b/MAINTAINERS
index e2d8fdda1737..61352f26f2d9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7003,6 +7003,11 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/display/panel/ilitek,ili9805.yaml
 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.c
+
 DRM DRIVER FOR JADARD JD9365DA-H3 MIPI-DSI LCD PANELS
 M:	Jagan Teki <jagan@edgeble.ai>
 S:	Maintained
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index bf4eadfe21cb..904a928bc60e 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -205,6 +205,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
+	tristate "Ilitek ILI9806E-based panels"
+	depends on OF
+	depends on DRM_MIPI_DSI
+	depends on BACKLIGHT_CLASS_DEVICE
+	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
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 051b75b3df7b..12ce91416849 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.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) += panel-ilitek-ili9806e.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.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e.c
new file mode 100644
index 000000000000..e4a44cd26c4d
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e.c
@@ -0,0 +1,402 @@
+// 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>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+#include <video/mipi_display.h>
+
+struct panel_desc {
+	const struct drm_display_mode *display_mode;
+	unsigned long mode_flags;
+	enum mipi_dsi_pixel_format format;
+	unsigned int lanes;
+	void (*init_sequence)(struct mipi_dsi_multi_context *ctx);
+};
+
+struct ili9806e_panel {
+	struct drm_panel panel;
+	struct mipi_dsi_device *dsi;
+	struct gpio_desc *reset_gpio;
+	struct regulator_bulk_data supplies[2];
+	const struct 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)
+{
+	struct mipi_dsi_multi_context ctx = { .dsi = ili9806e->dsi };
+
+	if (ili9806e->desc->init_sequence)
+		ili9806e->desc->init_sequence(&ctx);
+
+	mipi_dsi_dcs_exit_sleep_mode_multi(&ctx);
+	mipi_dsi_msleep(&ctx, 120);
+	mipi_dsi_dcs_set_display_on_multi(&ctx);
+
+	return ctx.accum_err;
+}
+
+static int ili9806e_off(struct ili9806e_panel *panel)
+{
+	struct mipi_dsi_multi_context ctx = { .dsi = panel->dsi };
+
+	mipi_dsi_dcs_set_display_off_multi(&ctx);
+	mipi_dsi_dcs_enter_sleep_mode_multi(&ctx);
+	mipi_dsi_msleep(&ctx, 120);
+
+	return ctx.accum_err;
+}
+
+static int ili9806e_prepare(struct drm_panel *panel)
+{
+	struct ili9806e_panel *ctx = to_ili9806e_panel(panel);
+	int ret;
+
+	ret = ili9806e_power_on(ctx);
+	if (ret < 0)
+		return ret;
+
+	ret = ili9806e_on(ctx);
+	if (ret < 0) {
+		ili9806e_power_off(ctx);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ili9806e_unprepare(struct drm_panel *panel)
+{
+	struct ili9806e_panel *ctx = to_ili9806e_panel(panel);
+	struct mipi_dsi_device *dsi = ctx->dsi;
+	int ret;
+
+	ili9806e_off(ctx);
+
+	ret = ili9806e_power_off(ctx);
+	if (ret < 0)
+		dev_err(&dsi->dev, "power off failed: %d\n", ret);
+
+	return ret;
+}
+
+static int ili9806e_get_modes(struct drm_panel *panel,
+			      struct drm_connector *connector)
+{
+	struct ili9806e_panel *ctx = to_ili9806e_panel(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)
+{
+	struct ili9806e_panel *ctx = to_ili9806e_panel(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 int ili9806e_dsi_probe(struct mipi_dsi_device *dsi)
+{
+	struct device *dev = &dsi->dev;
+	struct ili9806e_panel *ctx;
+	int i, ret;
+
+	ctx = devm_kzalloc(dev, sizeof(*ctx), 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;
+
+	dsi->mode_flags = ctx->desc->mode_flags;
+	dsi->format = ctx->desc->format;
+	dsi->lanes = ctx->desc->lanes;
+
+	drm_panel_init(&ctx->panel, dev, &ili9806e_funcs,
+		       DRM_MODE_CONNECTOR_DSI);
+
+	ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get orientation\n");
+
+	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);
+
+	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);
+		return ret;
+	}
+
+	return 0;
+}
+
+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);
+}
+
+static void com35h3p70ulc_init(struct mipi_dsi_multi_context *ctx)
+{
+	/* Switch to page 1 */
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xff, 0xff, 0x98, 0x06, 0x04, 0x01);
+	/* Interface Settings */
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x08, 0x18);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x21, 0x01);
+	/* Panel Settings */
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x30, 0x03);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x31, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x60, 0x0d);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x61, 0x08);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x62, 0x08);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x63, 0x09);
+	/* Power Control */
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x40, 0x30);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x41, 0x44);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x42, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x43, 0x89);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x44, 0x8e);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x45, 0xd9);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x46, 0x33);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x47, 0x33);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x50, 0x90);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x51, 0x90);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x56, 0x00);
+	/* Gamma Settings */
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xa0, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xa1, 0x0c);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xa2, 0x13);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xa3, 0x0f);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xa4, 0x0a);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xa5, 0x0d);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xa6, 0x0c);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xa7, 0x0b);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xa8, 0x01);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xa9, 0x06);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xaa, 0x15);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xab, 0x07);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xac, 0x12);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xad, 0x28);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xae, 0x20);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xaf, 0x14);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xc0, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xc1, 0x0c);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xc2, 0x13);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xc3, 0x0f);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xc4, 0x09);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xc5, 0x0d);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xc6, 0x0c);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xc7, 0x0b);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xc8, 0x01);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xc9, 0x06);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xca, 0x14);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xcb, 0x07);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xcc, 0x0f);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xcd, 0x21);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xce, 0x17);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xcf, 0x0a);
+
+	/* Switch to page 7 */
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xff, 0xff, 0x98, 0x06, 0x04, 0x07);
+	/* Power Control */
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x06, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x18, 0x1d);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x17, 0x32);
+
+	/* Switch to page 6 */
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xff, 0xff, 0x98, 0x06, 0x04, 0x06);
+	/* GIP settings */
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x00, 0x20);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x01, 0x02);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x02, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x03, 0x02);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x04, 0x01);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x05, 0x01);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x06, 0x88);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x07, 0x04);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x08, 0x03);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x09, 0x80);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x0a, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x0b, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x0c, 0x01);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x0d, 0x01);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x0e, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x0f, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x10, 0x55);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x11, 0x50);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x12, 0x01);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x13, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x14, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x15, 0x43);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x16, 0x0b);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x17, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x18, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x19, 0x10);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x1a, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x1b, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x1c, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x1d, 0x00);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x20, 0x01);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x21, 0x23);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x22, 0x45);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x23, 0x67);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x24, 0x01);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x25, 0x23);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x26, 0x45);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x27, 0x67);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x30, 0x02);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x31, 0x22);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x32, 0x22);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x33, 0x88);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x34, 0xaa);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x35, 0xbb);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x36, 0x66);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x37, 0x22);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x38, 0x22);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x39, 0x22);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x3a, 0x22);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x3b, 0x22);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x3c, 0x22);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x3d, 0x22);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x3e, 0x22);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x3f, 0x22);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x40, 0x22);
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x53, 0x12);
+
+	/* Switch to page 0 */
+	mipi_dsi_dcs_write_seq_multi(ctx, 0xff, 0xff, 0x98, 0x06, 0x04, 0x00);
+	/* Interface Pixel format */
+	mipi_dsi_dcs_write_seq_multi(ctx, 0x3a, 0x60);
+};
+
+static const struct drm_display_mode com35h3p70ulc_default_mode = {
+	.clock = 22400,
+	.hdisplay = 480,
+	.hsync_start = 480 + 16,
+	.hsync_end = 480 + 16 + 16,
+	.htotal = 480 + 16 + 16 + 16,
+	.vdisplay = 640,
+	.vsync_start = 640 + 52,
+	.vsync_end = 640 + 52 + 4,
+	.vtotal = 640 + 52 + 4 + 16,
+	.width_mm = 53,
+	.height_mm = 71,
+};
+
+static const struct panel_desc com35h3p70ulc_desc = {
+	.init_sequence = com35h3p70ulc_init,
+	.display_mode = &com35h3p70ulc_default_mode,
+	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+		      MIPI_DSI_MODE_LPM,
+	.format = MIPI_DSI_FMT_RGB888,
+	.lanes = 2,
+};
+
+static const struct of_device_id ili9806e_of_match[] = {
+	{ .compatible = "ortustech,com35h3p70ulc", .data = &com35h3p70ulc_desc },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ili9806e_of_match);
+
+static struct mipi_dsi_driver ili9806e_dsi_driver = {
+	.driver = {
+		.name = "ili9806e-dsi",
+		.of_match_table = ili9806e_of_match,
+	},
+	.probe = ili9806e_dsi_probe,
+	.remove = ili9806e_dsi_remove,
+};
+module_mipi_dsi_driver(ili9806e_dsi_driver);
+
+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");
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 2/2] drm/panel: add Ilitek ILI9806E panel driver
  2024-06-26 14:44 ` [PATCH v2 2/2] drm/panel: add Ilitek ILI9806E panel driver Michael Walle
@ 2024-06-26 16:36   ` Neil Armstrong
  2024-06-26 20:50   ` Jessica Zhang
  1 sibling, 0 replies; 6+ messages in thread
From: Neil Armstrong @ 2024-06-26 16:36 UTC (permalink / raw)
  To: Michael Walle, Jessica Zhang, David Airlie, Daniel Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Dmitry Baryshkov
  Cc: dri-devel, devicetree, linux-kernel, Gunnar Dibbern

On 26/06/2024 16:44, Michael Walle wrote:
> The Ortustech COM35H3P70ULC panel is based on the ILI9806E DSI display
> controller.
> 
> Co-developed-by: Gunnar Dibbern <gunnar.dibbern@lht.dlh.de>
> Signed-off-by: Gunnar Dibbern <gunnar.dibbern@lht.dlh.de>
> Signed-off-by: Michael Walle <mwalle@kernel.org>
> ---
>   MAINTAINERS                                   |   5 +
>   drivers/gpu/drm/panel/Kconfig                 |   9 +
>   drivers/gpu/drm/panel/Makefile                |   1 +
>   drivers/gpu/drm/panel/panel-ilitek-ili9806e.c | 402 ++++++++++++++++++
>   4 files changed, 417 insertions(+)
>   create mode 100644 drivers/gpu/drm/panel/panel-ilitek-ili9806e.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index e2d8fdda1737..61352f26f2d9 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7003,6 +7003,11 @@ S:	Maintained
>   F:	Documentation/devicetree/bindings/display/panel/ilitek,ili9805.yaml
>   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.c
> +
>   DRM DRIVER FOR JADARD JD9365DA-H3 MIPI-DSI LCD PANELS
>   M:	Jagan Teki <jagan@edgeble.ai>
>   S:	Maintained
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index bf4eadfe21cb..904a928bc60e 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -205,6 +205,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
> +	tristate "Ilitek ILI9806E-based panels"
> +	depends on OF
> +	depends on DRM_MIPI_DSI
> +	depends on BACKLIGHT_CLASS_DEVICE
> +	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
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index 051b75b3df7b..12ce91416849 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -21,6 +21,7 @@ obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.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) += panel-ilitek-ili9806e.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.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e.c
> new file mode 100644
> index 000000000000..e4a44cd26c4d
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e.c
> @@ -0,0 +1,402 @@
> +// 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>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_probe_helper.h>
> +
> +#include <video/mipi_display.h>
> +
> +struct panel_desc {
> +	const struct drm_display_mode *display_mode;
> +	unsigned long mode_flags;
> +	enum mipi_dsi_pixel_format format;
> +	unsigned int lanes;
> +	void (*init_sequence)(struct mipi_dsi_multi_context *ctx);
> +};
> +
> +struct ili9806e_panel {
> +	struct drm_panel panel;
> +	struct mipi_dsi_device *dsi;
> +	struct gpio_desc *reset_gpio;
> +	struct regulator_bulk_data supplies[2];
> +	const struct 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)
> +{
> +	struct mipi_dsi_multi_context ctx = { .dsi = ili9806e->dsi };
> +
> +	if (ili9806e->desc->init_sequence)
> +		ili9806e->desc->init_sequence(&ctx);
> +
> +	mipi_dsi_dcs_exit_sleep_mode_multi(&ctx);
> +	mipi_dsi_msleep(&ctx, 120);
> +	mipi_dsi_dcs_set_display_on_multi(&ctx);
> +
> +	return ctx.accum_err;
> +}
> +
> +static int ili9806e_off(struct ili9806e_panel *panel)
> +{
> +	struct mipi_dsi_multi_context ctx = { .dsi = panel->dsi };
> +
> +	mipi_dsi_dcs_set_display_off_multi(&ctx);
> +	mipi_dsi_dcs_enter_sleep_mode_multi(&ctx);
> +	mipi_dsi_msleep(&ctx, 120);
> +
> +	return ctx.accum_err;
> +}
> +
> +static int ili9806e_prepare(struct drm_panel *panel)
> +{
> +	struct ili9806e_panel *ctx = to_ili9806e_panel(panel);
> +	int ret;
> +
> +	ret = ili9806e_power_on(ctx);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = ili9806e_on(ctx);
> +	if (ret < 0) {
> +		ili9806e_power_off(ctx);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ili9806e_unprepare(struct drm_panel *panel)
> +{
> +	struct ili9806e_panel *ctx = to_ili9806e_panel(panel);
> +	struct mipi_dsi_device *dsi = ctx->dsi;
> +	int ret;
> +
> +	ili9806e_off(ctx);
> +
> +	ret = ili9806e_power_off(ctx);
> +	if (ret < 0)
> +		dev_err(&dsi->dev, "power off failed: %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static int ili9806e_get_modes(struct drm_panel *panel,
> +			      struct drm_connector *connector)
> +{
> +	struct ili9806e_panel *ctx = to_ili9806e_panel(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)
> +{
> +	struct ili9806e_panel *ctx = to_ili9806e_panel(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 int ili9806e_dsi_probe(struct mipi_dsi_device *dsi)
> +{
> +	struct device *dev = &dsi->dev;
> +	struct ili9806e_panel *ctx;
> +	int i, ret;
> +
> +	ctx = devm_kzalloc(dev, sizeof(*ctx), 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;
> +
> +	dsi->mode_flags = ctx->desc->mode_flags;
> +	dsi->format = ctx->desc->format;
> +	dsi->lanes = ctx->desc->lanes;
> +
> +	drm_panel_init(&ctx->panel, dev, &ili9806e_funcs,
> +		       DRM_MODE_CONNECTOR_DSI);
> +
> +	ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to get orientation\n");
> +
> +	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);
> +
> +	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);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +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);
> +}
> +
> +static void com35h3p70ulc_init(struct mipi_dsi_multi_context *ctx)
> +{
> +	/* Switch to page 1 */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xff, 0xff, 0x98, 0x06, 0x04, 0x01);
> +	/* Interface Settings */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x08, 0x18);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x21, 0x01);
> +	/* Panel Settings */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x30, 0x03);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x31, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x60, 0x0d);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x61, 0x08);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x62, 0x08);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x63, 0x09);
> +	/* Power Control */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x40, 0x30);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x41, 0x44);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x42, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x43, 0x89);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x44, 0x8e);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x45, 0xd9);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x46, 0x33);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x47, 0x33);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x50, 0x90);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x51, 0x90);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x56, 0x00);
> +	/* Gamma Settings */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa0, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa1, 0x0c);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa2, 0x13);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa3, 0x0f);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa4, 0x0a);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa5, 0x0d);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa6, 0x0c);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa7, 0x0b);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa8, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa9, 0x06);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xaa, 0x15);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xab, 0x07);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xac, 0x12);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xad, 0x28);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xae, 0x20);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xaf, 0x14);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc0, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc1, 0x0c);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc2, 0x13);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc3, 0x0f);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc4, 0x09);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc5, 0x0d);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc6, 0x0c);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc7, 0x0b);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc8, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc9, 0x06);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xca, 0x14);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xcb, 0x07);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xcc, 0x0f);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xcd, 0x21);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xce, 0x17);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xcf, 0x0a);
> +
> +	/* Switch to page 7 */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xff, 0xff, 0x98, 0x06, 0x04, 0x07);
> +	/* Power Control */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x06, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x18, 0x1d);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x17, 0x32);
> +
> +	/* Switch to page 6 */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xff, 0xff, 0x98, 0x06, 0x04, 0x06);
> +	/* GIP settings */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x00, 0x20);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x01, 0x02);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x02, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x03, 0x02);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x04, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x05, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x06, 0x88);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x07, 0x04);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x08, 0x03);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x09, 0x80);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x0a, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x0b, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x0c, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x0d, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x0e, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x0f, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x10, 0x55);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x11, 0x50);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x12, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x13, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x14, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x15, 0x43);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x16, 0x0b);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x17, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x18, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x19, 0x10);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x1a, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x1b, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x1c, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x1d, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x20, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x21, 0x23);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x22, 0x45);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x23, 0x67);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x24, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x25, 0x23);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x26, 0x45);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x27, 0x67);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x30, 0x02);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x31, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x32, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x33, 0x88);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x34, 0xaa);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x35, 0xbb);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x36, 0x66);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x37, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x38, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x39, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x3a, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x3b, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x3c, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x3d, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x3e, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x3f, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x40, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x53, 0x12);
> +
> +	/* Switch to page 0 */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xff, 0xff, 0x98, 0x06, 0x04, 0x00);
> +	/* Interface Pixel format */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x3a, 0x60);
> +};
> +
> +static const struct drm_display_mode com35h3p70ulc_default_mode = {
> +	.clock = 22400,
> +	.hdisplay = 480,
> +	.hsync_start = 480 + 16,
> +	.hsync_end = 480 + 16 + 16,
> +	.htotal = 480 + 16 + 16 + 16,
> +	.vdisplay = 640,
> +	.vsync_start = 640 + 52,
> +	.vsync_end = 640 + 52 + 4,
> +	.vtotal = 640 + 52 + 4 + 16,
> +	.width_mm = 53,
> +	.height_mm = 71,
> +};
> +
> +static const struct panel_desc com35h3p70ulc_desc = {
> +	.init_sequence = com35h3p70ulc_init,
> +	.display_mode = &com35h3p70ulc_default_mode,
> +	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
> +		      MIPI_DSI_MODE_LPM,
> +	.format = MIPI_DSI_FMT_RGB888,
> +	.lanes = 2,
> +};
> +
> +static const struct of_device_id ili9806e_of_match[] = {
> +	{ .compatible = "ortustech,com35h3p70ulc", .data = &com35h3p70ulc_desc },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, ili9806e_of_match);
> +
> +static struct mipi_dsi_driver ili9806e_dsi_driver = {
> +	.driver = {
> +		.name = "ili9806e-dsi",
> +		.of_match_table = ili9806e_of_match,
> +	},
> +	.probe = ili9806e_dsi_probe,
> +	.remove = ili9806e_dsi_remove,
> +};
> +module_mipi_dsi_driver(ili9806e_dsi_driver);
> +
> +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");

Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 0/2] drm/panel: initial support for the Ortustech COM35H3P70ULC
  2024-06-26 14:44 [PATCH v2 0/2] drm/panel: initial support for the Ortustech COM35H3P70ULC Michael Walle
  2024-06-26 14:44 ` [PATCH v2 1/2] dt-bindings: display: panel: add Ilitek ili9806e panel controller Michael Walle
  2024-06-26 14:44 ` [PATCH v2 2/2] drm/panel: add Ilitek ILI9806E panel driver Michael Walle
@ 2024-06-26 16:45 ` Neil Armstrong
  2 siblings, 0 replies; 6+ messages in thread
From: Neil Armstrong @ 2024-06-26 16:45 UTC (permalink / raw)
  To: Jessica Zhang, David Airlie, Daniel Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Dmitry Baryshkov,
	Michael Walle
  Cc: dri-devel, devicetree, linux-kernel, Gunnar Dibbern

Hi,

On Wed, 26 Jun 2024 16:44:31 +0200, Michael Walle wrote:
> Add initial support for the 480x640 DSI panel from Ortustech. The
> panel uses an Ilitek ILI9806E panel driver IC.
> 
> v2:
>  - use drm_connector_helper_get_modes_fixed(), thanks Dmitry.
>  - slight header files cleanup
> 
> [...]

Thanks, Applied to https://gitlab.freedesktop.org/drm/misc/kernel.git (drm-misc-next)

[1/2] dt-bindings: display: panel: add Ilitek ili9806e panel controller
      https://gitlab.freedesktop.org/drm/misc/kernel/-/commit/b7a0c0e9d80756da52d5c88f24b5253a08108724
[2/2] drm/panel: add Ilitek ILI9806E panel driver
      https://gitlab.freedesktop.org/drm/misc/kernel/-/commit/baf272bac637d3275bb83c17ac849b44a4590655

-- 
Neil


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 2/2] drm/panel: add Ilitek ILI9806E panel driver
  2024-06-26 14:44 ` [PATCH v2 2/2] drm/panel: add Ilitek ILI9806E panel driver Michael Walle
  2024-06-26 16:36   ` Neil Armstrong
@ 2024-06-26 20:50   ` Jessica Zhang
  1 sibling, 0 replies; 6+ messages in thread
From: Jessica Zhang @ 2024-06-26 20:50 UTC (permalink / raw)
  To: Michael Walle, Neil Armstrong, David Airlie, Daniel Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Dmitry Baryshkov
  Cc: dri-devel, devicetree, linux-kernel, Gunnar Dibbern



On 6/26/2024 7:44 AM, Michael Walle wrote:
> The Ortustech COM35H3P70ULC panel is based on the ILI9806E DSI display
> controller.
> 
> Co-developed-by: Gunnar Dibbern <gunnar.dibbern@lht.dlh.de>
> Signed-off-by: Gunnar Dibbern <gunnar.dibbern@lht.dlh.de>
> Signed-off-by: Michael Walle <mwalle@kernel.org>

Reviewed-by: Jessica Zhang <quic_jesszhan@quicinc.com>

> ---
>   MAINTAINERS                                   |   5 +
>   drivers/gpu/drm/panel/Kconfig                 |   9 +
>   drivers/gpu/drm/panel/Makefile                |   1 +
>   drivers/gpu/drm/panel/panel-ilitek-ili9806e.c | 402 ++++++++++++++++++
>   4 files changed, 417 insertions(+)
>   create mode 100644 drivers/gpu/drm/panel/panel-ilitek-ili9806e.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index e2d8fdda1737..61352f26f2d9 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7003,6 +7003,11 @@ S:	Maintained
>   F:	Documentation/devicetree/bindings/display/panel/ilitek,ili9805.yaml
>   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.c
> +
>   DRM DRIVER FOR JADARD JD9365DA-H3 MIPI-DSI LCD PANELS
>   M:	Jagan Teki <jagan@edgeble.ai>
>   S:	Maintained
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index bf4eadfe21cb..904a928bc60e 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -205,6 +205,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
> +	tristate "Ilitek ILI9806E-based panels"
> +	depends on OF
> +	depends on DRM_MIPI_DSI
> +	depends on BACKLIGHT_CLASS_DEVICE
> +	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
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index 051b75b3df7b..12ce91416849 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -21,6 +21,7 @@ obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.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) += panel-ilitek-ili9806e.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.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e.c
> new file mode 100644
> index 000000000000..e4a44cd26c4d
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e.c
> @@ -0,0 +1,402 @@
> +// 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>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_probe_helper.h>
> +
> +#include <video/mipi_display.h>
> +
> +struct panel_desc {
> +	const struct drm_display_mode *display_mode;
> +	unsigned long mode_flags;
> +	enum mipi_dsi_pixel_format format;
> +	unsigned int lanes;
> +	void (*init_sequence)(struct mipi_dsi_multi_context *ctx);
> +};
> +
> +struct ili9806e_panel {
> +	struct drm_panel panel;
> +	struct mipi_dsi_device *dsi;
> +	struct gpio_desc *reset_gpio;
> +	struct regulator_bulk_data supplies[2];
> +	const struct 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)
> +{
> +	struct mipi_dsi_multi_context ctx = { .dsi = ili9806e->dsi };
> +
> +	if (ili9806e->desc->init_sequence)
> +		ili9806e->desc->init_sequence(&ctx);
> +
> +	mipi_dsi_dcs_exit_sleep_mode_multi(&ctx);
> +	mipi_dsi_msleep(&ctx, 120);
> +	mipi_dsi_dcs_set_display_on_multi(&ctx);
> +
> +	return ctx.accum_err;
> +}
> +
> +static int ili9806e_off(struct ili9806e_panel *panel)
> +{
> +	struct mipi_dsi_multi_context ctx = { .dsi = panel->dsi };
> +
> +	mipi_dsi_dcs_set_display_off_multi(&ctx);
> +	mipi_dsi_dcs_enter_sleep_mode_multi(&ctx);
> +	mipi_dsi_msleep(&ctx, 120);
> +
> +	return ctx.accum_err;
> +}
> +
> +static int ili9806e_prepare(struct drm_panel *panel)
> +{
> +	struct ili9806e_panel *ctx = to_ili9806e_panel(panel);
> +	int ret;
> +
> +	ret = ili9806e_power_on(ctx);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = ili9806e_on(ctx);
> +	if (ret < 0) {
> +		ili9806e_power_off(ctx);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ili9806e_unprepare(struct drm_panel *panel)
> +{
> +	struct ili9806e_panel *ctx = to_ili9806e_panel(panel);
> +	struct mipi_dsi_device *dsi = ctx->dsi;
> +	int ret;
> +
> +	ili9806e_off(ctx);
> +
> +	ret = ili9806e_power_off(ctx);
> +	if (ret < 0)
> +		dev_err(&dsi->dev, "power off failed: %d\n", ret);
> +
> +	return ret;
> +}
> +
> +static int ili9806e_get_modes(struct drm_panel *panel,
> +			      struct drm_connector *connector)
> +{
> +	struct ili9806e_panel *ctx = to_ili9806e_panel(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)
> +{
> +	struct ili9806e_panel *ctx = to_ili9806e_panel(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 int ili9806e_dsi_probe(struct mipi_dsi_device *dsi)
> +{
> +	struct device *dev = &dsi->dev;
> +	struct ili9806e_panel *ctx;
> +	int i, ret;
> +
> +	ctx = devm_kzalloc(dev, sizeof(*ctx), 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;
> +
> +	dsi->mode_flags = ctx->desc->mode_flags;
> +	dsi->format = ctx->desc->format;
> +	dsi->lanes = ctx->desc->lanes;
> +
> +	drm_panel_init(&ctx->panel, dev, &ili9806e_funcs,
> +		       DRM_MODE_CONNECTOR_DSI);
> +
> +	ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to get orientation\n");
> +
> +	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);
> +
> +	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);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +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);
> +}
> +
> +static void com35h3p70ulc_init(struct mipi_dsi_multi_context *ctx)
> +{
> +	/* Switch to page 1 */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xff, 0xff, 0x98, 0x06, 0x04, 0x01);
> +	/* Interface Settings */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x08, 0x18);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x21, 0x01);
> +	/* Panel Settings */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x30, 0x03);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x31, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x60, 0x0d);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x61, 0x08);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x62, 0x08);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x63, 0x09);
> +	/* Power Control */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x40, 0x30);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x41, 0x44);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x42, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x43, 0x89);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x44, 0x8e);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x45, 0xd9);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x46, 0x33);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x47, 0x33);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x50, 0x90);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x51, 0x90);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x56, 0x00);
> +	/* Gamma Settings */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa0, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa1, 0x0c);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa2, 0x13);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa3, 0x0f);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa4, 0x0a);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa5, 0x0d);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa6, 0x0c);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa7, 0x0b);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa8, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xa9, 0x06);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xaa, 0x15);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xab, 0x07);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xac, 0x12);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xad, 0x28);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xae, 0x20);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xaf, 0x14);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc0, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc1, 0x0c);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc2, 0x13);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc3, 0x0f);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc4, 0x09);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc5, 0x0d);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc6, 0x0c);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc7, 0x0b);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc8, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xc9, 0x06);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xca, 0x14);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xcb, 0x07);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xcc, 0x0f);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xcd, 0x21);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xce, 0x17);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xcf, 0x0a);
> +
> +	/* Switch to page 7 */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xff, 0xff, 0x98, 0x06, 0x04, 0x07);
> +	/* Power Control */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x06, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x18, 0x1d);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x17, 0x32);
> +
> +	/* Switch to page 6 */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xff, 0xff, 0x98, 0x06, 0x04, 0x06);
> +	/* GIP settings */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x00, 0x20);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x01, 0x02);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x02, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x03, 0x02);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x04, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x05, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x06, 0x88);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x07, 0x04);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x08, 0x03);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x09, 0x80);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x0a, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x0b, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x0c, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x0d, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x0e, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x0f, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x10, 0x55);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x11, 0x50);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x12, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x13, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x14, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x15, 0x43);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x16, 0x0b);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x17, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x18, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x19, 0x10);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x1a, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x1b, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x1c, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x1d, 0x00);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x20, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x21, 0x23);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x22, 0x45);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x23, 0x67);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x24, 0x01);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x25, 0x23);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x26, 0x45);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x27, 0x67);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x30, 0x02);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x31, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x32, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x33, 0x88);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x34, 0xaa);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x35, 0xbb);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x36, 0x66);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x37, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x38, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x39, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x3a, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x3b, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x3c, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x3d, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x3e, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x3f, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x40, 0x22);
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x53, 0x12);
> +
> +	/* Switch to page 0 */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0xff, 0xff, 0x98, 0x06, 0x04, 0x00);
> +	/* Interface Pixel format */
> +	mipi_dsi_dcs_write_seq_multi(ctx, 0x3a, 0x60);
> +};
> +
> +static const struct drm_display_mode com35h3p70ulc_default_mode = {
> +	.clock = 22400,
> +	.hdisplay = 480,
> +	.hsync_start = 480 + 16,
> +	.hsync_end = 480 + 16 + 16,
> +	.htotal = 480 + 16 + 16 + 16,
> +	.vdisplay = 640,
> +	.vsync_start = 640 + 52,
> +	.vsync_end = 640 + 52 + 4,
> +	.vtotal = 640 + 52 + 4 + 16,
> +	.width_mm = 53,
> +	.height_mm = 71,
> +};
> +
> +static const struct panel_desc com35h3p70ulc_desc = {
> +	.init_sequence = com35h3p70ulc_init,
> +	.display_mode = &com35h3p70ulc_default_mode,
> +	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
> +		      MIPI_DSI_MODE_LPM,
> +	.format = MIPI_DSI_FMT_RGB888,
> +	.lanes = 2,
> +};
> +
> +static const struct of_device_id ili9806e_of_match[] = {
> +	{ .compatible = "ortustech,com35h3p70ulc", .data = &com35h3p70ulc_desc },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, ili9806e_of_match);
> +
> +static struct mipi_dsi_driver ili9806e_dsi_driver = {
> +	.driver = {
> +		.name = "ili9806e-dsi",
> +		.of_match_table = ili9806e_of_match,
> +	},
> +	.probe = ili9806e_dsi_probe,
> +	.remove = ili9806e_dsi_remove,
> +};
> +module_mipi_dsi_driver(ili9806e_dsi_driver);
> +
> +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");
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2024-06-26 20:51 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-06-26 14:44 [PATCH v2 0/2] drm/panel: initial support for the Ortustech COM35H3P70ULC Michael Walle
2024-06-26 14:44 ` [PATCH v2 1/2] dt-bindings: display: panel: add Ilitek ili9806e panel controller Michael Walle
2024-06-26 14:44 ` [PATCH v2 2/2] drm/panel: add Ilitek ILI9806E panel driver Michael Walle
2024-06-26 16:36   ` Neil Armstrong
2024-06-26 20:50   ` Jessica Zhang
2024-06-26 16:45 ` [PATCH v2 0/2] drm/panel: initial support for the Ortustech COM35H3P70ULC Neil Armstrong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).