Part-of: <https://gitlab.postmarketos.org/postmarketOS/pmaports/-/merge_requests/8412>
669 lines
24 KiB
Diff
669 lines
24 KiB
Diff
From 2004321a59de1889325505efbf3af46762d368af Mon Sep 17 00:00:00 2001
|
|
From: Arseniy Velikanov <me@adomerle.pw>
|
|
Date: Wed, 11 Feb 2026 21:05:40 +0700
|
|
Subject: [PATCH 06/12] drm/panel: Add support for Novatek NT36532 panel
|
|
|
|
Add a driver for panels using the Novatek NT36532 Display Driver IC,
|
|
including support for the CSOT panel used in the Xiaomi Pad 6 tablets.
|
|
|
|
Co-developed-by: Ryo Kenrie Wongso <developer@damillora.com>
|
|
Signed-off-by: Ryo Kenrie Wongso <developer@damillora.com>
|
|
---
|
|
drivers/gpu/drm/panel/Kconfig | 10 +
|
|
drivers/gpu/drm/panel/Makefile | 1 +
|
|
drivers/gpu/drm/panel/panel-novatek-nt36532.c | 609 ++++++++++++++++++
|
|
3 files changed, 620 insertions(+)
|
|
create mode 100644 drivers/gpu/drm/panel/panel-novatek-nt36532.c
|
|
|
|
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
|
|
index 307152ad7759..97a6ea3829bb 100644
|
|
--- a/drivers/gpu/drm/panel/Kconfig
|
|
+++ b/drivers/gpu/drm/panel/Kconfig
|
|
@@ -533,6 +533,16 @@ config DRM_PANEL_NOVATEK_NT36523
|
|
around the Novatek NT36523 display controller, such as some
|
|
Boe panels used in Xiaomi Mi Pad 5 and 5 Pro tablets.
|
|
|
|
+config DRM_PANEL_NOVATEK_NT36532
|
|
+ tristate "Novatek NT36532 panel driver"
|
|
+ depends on OF
|
|
+ depends on DRM_MIPI_DSI
|
|
+ depends on BACKLIGHT_CLASS_DEVICE
|
|
+ help
|
|
+ Say Y here if you want to enable support for the panels built
|
|
+ around the Novatek NT36532 display controller, such as some
|
|
+ panels used in Xiaomi Pad 6 tablets.
|
|
+
|
|
config DRM_PANEL_NOVATEK_NT36672A
|
|
tristate "Novatek NT36672A DSI panel"
|
|
depends on OF
|
|
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
|
|
index aeffaa95666d..e1511dba37a7 100644
|
|
--- a/drivers/gpu/drm/panel/Makefile
|
|
+++ b/drivers/gpu/drm/panel/Makefile
|
|
@@ -52,6 +52,7 @@ obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35510) += panel-novatek-nt35510.o
|
|
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35560) += panel-novatek-nt35560.o
|
|
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35950) += panel-novatek-nt35950.o
|
|
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36523) += panel-novatek-nt36523.o
|
|
+obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36532) += panel-novatek-nt36532.o
|
|
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672A) += panel-novatek-nt36672a.o
|
|
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672E) += panel-novatek-nt36672e.o
|
|
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37801) += panel-novatek-nt37801.o
|
|
diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36532.c b/drivers/gpu/drm/panel/panel-novatek-nt36532.c
|
|
new file mode 100644
|
|
index 000000000000..cf0c92766f89
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/panel/panel-novatek-nt36532.c
|
|
@@ -0,0 +1,609 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-only
|
|
+// Copyright (c) 2024 Luka Panio <lukapanio@gmail.com>
|
|
+
|
|
+#include <linux/backlight.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/gpio/consumer.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/of_device.h>
|
|
+#include <linux/of_graph.h>
|
|
+#include <linux/regulator/consumer.h>
|
|
+
|
|
+#include <video/mipi_display.h>
|
|
+
|
|
+#include <drm/drm_mipi_dsi.h>
|
|
+#include <drm/drm_modes.h>
|
|
+#include <drm/drm_panel.h>
|
|
+#include <drm/drm_probe_helper.h>
|
|
+#include <drm/display/drm_dsc.h>
|
|
+#include <drm/display/drm_dsc_helper.h>
|
|
+
|
|
+struct nt36532 {
|
|
+ struct drm_panel panel;
|
|
+ struct mipi_dsi_device *dsi[2];
|
|
+ const struct panel_desc *desc;
|
|
+ struct regulator_bulk_data supplies[3];
|
|
+ struct gpio_desc *reset_gpio;
|
|
+};
|
|
+
|
|
+static inline struct nt36532 *
|
|
+to_nt36532(struct drm_panel *panel)
|
|
+{
|
|
+ return container_of(panel, struct nt36532, panel);
|
|
+}
|
|
+
|
|
+struct panel_desc {
|
|
+ unsigned int lanes;
|
|
+ enum mipi_dsi_pixel_format format;
|
|
+ unsigned long mode_flags;
|
|
+
|
|
+ const struct drm_display_mode display_mode;
|
|
+
|
|
+ const struct mipi_dsi_device_info dsi_info;
|
|
+
|
|
+ struct drm_dsc_config * const dsc_cfg;
|
|
+
|
|
+ int (*init_sequence)(struct nt36532 *ctx);
|
|
+};
|
|
+
|
|
+static void nt36532_reset(struct nt36532 *ctx)
|
|
+{
|
|
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
|
|
+ usleep_range(12000, 13000);
|
|
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
|
|
+ usleep_range(12000, 13000);
|
|
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
|
|
+ usleep_range(12000, 13000);
|
|
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
|
|
+ usleep_range(12000, 13000);
|
|
+}
|
|
+
|
|
+static int pipa_init_sequence(struct nt36532 *ctx)
|
|
+{
|
|
+ struct mipi_dsi_device *dsi= ctx->dsi[0];
|
|
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
|
|
+
|
|
+ struct device *dev = &dsi->dev;
|
|
+ int ret;
|
|
+
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x27);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd0, 0x31);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd1, 0x20);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xd2, 0x38);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xde, 0x43);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xdf, 0x02);
|
|
+
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x23);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_NOP, 0x80);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SOFT_RESET, 0x84);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x2d);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x00);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x03);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0x2a);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0xd0);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x16);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_DISPLAY_ON, 0x0a);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_PARTIAL_ROWS, 0xff);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_PARTIAL_COLUMNS, 0xfe);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0xfd);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_SCROLL_AREA, 0xfb);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_TEAR_OFF, 0xf8);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_TEAR_ON, 0xf5);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_ADDRESS_MODE, 0xf3);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_SCROLL_START, 0xf2);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_EXIT_IDLE_MODE, 0xf2);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_ENTER_IDLE_MODE, 0xf2);
|
|
+
|
|
+ ret = mipi_dsi_dcs_set_pixel_format(dsi, 0xef);
|
|
+ if (ret < 0) {
|
|
+ dev_err(dev, "Failed to set pixel format: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3b, 0xec);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_3D_CONTROL, 0xe9);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_GET_3D_CONTROL, 0xe5);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_VSYNC_TIMING, 0xe5);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0xe5);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_COLUMN_ADDRESS, 0x13);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_GET_SCANLINE, 0xff);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0xf4);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0xe7);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0xda);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0xcd);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4a, 0xc0);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4b, 0xb3);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4c, 0xb1);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4d, 0xb1);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4e, 0xb1);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4f, 0x95);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x79);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0x5c);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_GET_DISPLAY_BRIGHTNESS, 0x58);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x58);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_GET_CONTROL_DISPLAY, 0x58);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_PAGE_ADDRESS, 0x0e);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0xff);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0xfb);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5a, 0xf7);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0xf3);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5c, 0xef);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0xe3);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0xd8);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0xd6);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0xd6);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0xd6);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0xc8);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0xb7);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0xaa);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0xa8);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0xa8);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0xa8);
|
|
+
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x20);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x02);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0x72);
|
|
+
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x22);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9f, 0x57);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x1f, 0x1f, 0x1f, 0x1f);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb1, 0x4d, 0x4d, 0x4d, 0x4d);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb2, 0x1e, 0x1e, 0x1e, 0x1e);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb3, 0x6c, 0x6c, 0x6c, 0x6c);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb4, 0x1f, 0x1f, 0x1f, 0x1f);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb5, 0x44, 0x44, 0x44, 0x44);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb8, 0x00);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb9, 0x6e);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xba, 0x6e);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbb, 0x6e);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbe, 0x0b);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbf, 0x6e);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc1, 0x6e);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc3, 0x6e);
|
|
+
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x23);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xba, 0x7a, 0x5d);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbb, 0x77, 0x60);
|
|
+
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x24);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x80);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x92, 0x45, 0x00, 0xc0);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xdb, 0x33);
|
|
+
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x25);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_GET_ERROR_COUNT_ON_DSI, 0x00);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x09);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x16);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_COLUMN_ADDRESS, 0x09);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_PAGE_ADDRESS, 0x16);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x0b);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc5, 0x1e);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf6, 0x02);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf7, 0x48);
|
|
+
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x26);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x75);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x10, 0x12, 0x12, 0x12);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0xc5, 0xa3, 0xa3, 0xa3);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0x0f, 0x11, 0x11, 0x11);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0xe8, 0xc6, 0xc6, 0xc6);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1e, 0x45);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1f, 0x45);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2a, 0x10, 0x12, 0x12, 0x12);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0xc0, 0x9e, 0x9e, 0x9e);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2f, 0x0b);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0x45);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0x45);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0x22);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0x92);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x78);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x96);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x78);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x06);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x45);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_VSYNC_TIMING, 0x47);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x47);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x47);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x0b);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0x47);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x47);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4a, 0x47);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x84, 0x16, 0x16, 0x16);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x85, 0x26, 0x26, 0x26);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8b, 0xaa);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x99, 0x26, 0x26, 0x26, 0x26);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9a, 0xb6, 0xb6, 0xb6, 0xb6);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9b, 0x25, 0x25, 0x25, 0x25);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9c, 0xd4, 0xd4, 0xd4, 0xd4);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9d, 0x26, 0x26, 0x26, 0x26);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9e, 0xac, 0xac, 0xac, 0xac);
|
|
+
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x27);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0xc1);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x01);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x3c, 0x0d);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0a, 0xeb, 0x40);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x4b, 0x00);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0xeb, 0x40);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x4b, 0x00);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x11);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0xf0);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x02);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x29);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7e, 0x4e);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0xc9, 0x0c);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x3d, 0x00);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0xcb, 0xc0);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x84, 0x92, 0x00);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x85, 0x36, 0x00);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x86, 0xcb, 0xc0);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x88, 0x92, 0x00);
|
|
+
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x2a);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x09);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1e, 0x09);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1f, 0x09);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xa3, 0x00);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc5, 0x09);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc6, 0x16);
|
|
+
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x10);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfb, 0x01);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb3, 0x40);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb2, 0x91);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x90, 0x03);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x91,
|
|
+ 0x89, 0x28, 0x00, 0x14, 0xd2, 0x00, 0x01, 0xf4,
|
|
+ 0x01, 0xab, 0x00, 0x06, 0x05, 0x7a, 0x06, 0x1a);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x92, 0x10, 0xf0);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x00);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3b, 0x03, 0xd8, 0x1a, 0x0a, 0x0a, 0x00);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x0f, 0xff);
|
|
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x24);
|
|
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
|
|
+ if (ret < 0) {
|
|
+ dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ msleep(70);
|
|
+
|
|
+ ret = mipi_dsi_dcs_set_display_on(dsi);
|
|
+ if (ret < 0) {
|
|
+ dev_err(dev, "Failed to set display on: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ msleep(40);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct drm_display_mode pipa_display_mode = {
|
|
+ .clock = (1800 + 200 + 4 + 92) * (2880 + 26 + 2 + 214) * 120 / 1000,
|
|
+ .hdisplay = 1800,
|
|
+ .hsync_start = 1800 + 200,
|
|
+ .hsync_end = 1800 + 200 + 4,
|
|
+ .htotal = 1800 + 200 + 4 + 92,
|
|
+ .vdisplay = 2880,
|
|
+ .vsync_start = 2880 + 26,
|
|
+ .vsync_end = 2880 + 26 + 2,
|
|
+ .vtotal = 2880 + 26 + 2 + 214,
|
|
+ .width_mm = 148,
|
|
+ .height_mm = 237,
|
|
+ .type = DRM_MODE_TYPE_DRIVER,
|
|
+};
|
|
+
|
|
+static struct drm_dsc_config pipa_dsc_cfg = {
|
|
+ .dsc_version_major = 1,
|
|
+ .dsc_version_minor = 1,
|
|
+ .slice_height = 20,
|
|
+ .slice_width = 450,
|
|
+ .slice_count = 2,
|
|
+ .bits_per_component = 8,
|
|
+ .bits_per_pixel = 8 << 4,
|
|
+ .block_pred_enable = true,
|
|
+};
|
|
+
|
|
+static const struct panel_desc pipa_csot_desc = {
|
|
+ .dsi_info = {
|
|
+ .type = "csot-pipa",
|
|
+ .channel = 0,
|
|
+ .node = NULL,
|
|
+ },
|
|
+ .lanes = 4,
|
|
+ .format = MIPI_DSI_FMT_RGB888,
|
|
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
|
|
+ .display_mode = pipa_display_mode,
|
|
+ .dsc_cfg = &pipa_dsc_cfg,
|
|
+ .init_sequence = pipa_init_sequence,
|
|
+};
|
|
+static const struct panel_desc pipa_tianma_desc = {
|
|
+ .dsi_info = {
|
|
+ .type = "tianma-pipa",
|
|
+ .channel = 0,
|
|
+ .node = NULL,
|
|
+ },
|
|
+ .lanes = 4,
|
|
+ .format = MIPI_DSI_FMT_RGB888,
|
|
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
|
|
+ .display_mode = pipa_display_mode,
|
|
+ .dsc_cfg = &pipa_dsc_cfg,
|
|
+ .init_sequence = pipa_init_sequence,
|
|
+};
|
|
+
|
|
+static int nt36532_off(struct nt36532 *ctx)
|
|
+{
|
|
+ struct mipi_dsi_device *dsi = ctx->dsi[0];
|
|
+ struct device *dev = &dsi->dev;
|
|
+ int ret;
|
|
+
|
|
+ ret = mipi_dsi_dcs_set_display_off(dsi);
|
|
+ if (ret < 0) {
|
|
+ dev_err(dev, "Failed to set display off: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ msleep(20);
|
|
+
|
|
+ ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
|
|
+ if (ret < 0) {
|
|
+ dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ msleep(120);
|
|
+
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int nt36532_prepare(struct drm_panel *panel)
|
|
+{
|
|
+ struct nt36532 *ctx = to_nt36532(panel);
|
|
+ struct mipi_dsi_device *dsi = ctx->dsi[0];
|
|
+ struct device *dev = &dsi->dev;
|
|
+ struct drm_dsc_picture_parameter_set pps;
|
|
+ int ret;
|
|
+
|
|
+ ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
|
+ if (ret < 0) {
|
|
+ dev_err(dev, "Failed to enable regulators: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ msleep(120);
|
|
+
|
|
+ nt36532_reset(ctx);
|
|
+
|
|
+ msleep(120);
|
|
+
|
|
+ ret = ctx->desc->init_sequence(ctx);
|
|
+ if (ret < 0) {
|
|
+ regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
|
+ dev_err(panel->dev, "failed to initialize panel: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ drm_dsc_pps_payload_pack(&pps, ctx->desc->dsc_cfg);
|
|
+
|
|
+ ret = mipi_dsi_picture_parameter_set(dsi, &pps);
|
|
+ if (ret < 0) {
|
|
+ dev_err(panel->dev, "failed to transmit PPS: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = mipi_dsi_compression_mode(dsi, true);
|
|
+ if (ret < 0) {
|
|
+ dev_err(dev, "failed to enable compression mode: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ msleep(120);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int nt36532_enable(struct drm_panel *panel)
|
|
+{
|
|
+ struct nt36532 *ctx = to_nt36532(panel);
|
|
+ struct drm_dsc_picture_parameter_set pps;
|
|
+ struct mipi_dsi_device *dsi = ctx->dsi[0];
|
|
+ struct device *dev = &dsi->dev;
|
|
+ int ret;
|
|
+
|
|
+ ret = mipi_dsi_dcs_set_display_on(dsi);
|
|
+ if (ret < 0) {
|
|
+ dev_err(dev, "Failed to set display on: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ usleep_range(10000, 11000);
|
|
+
|
|
+ drm_dsc_pps_payload_pack(&pps, ctx->desc->dsc_cfg);
|
|
+
|
|
+ print_hex_dump(KERN_INFO, "DSC:", DUMP_PREFIX_NONE, 16,
|
|
+ 1, (void *)&pps, sizeof(pps), false);
|
|
+
|
|
+ ret = mipi_dsi_picture_parameter_set(dsi, &pps);
|
|
+ if (ret < 0) {
|
|
+ dev_err(panel->dev, "failed to transmit PPS: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ msleep(28);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int nt36532_disable(struct drm_panel *panel)
|
|
+{
|
|
+ struct nt36532 *ctx = to_nt36532(panel);
|
|
+ struct device *dev = &ctx->dsi[0]->dev;
|
|
+ int ret;
|
|
+
|
|
+ ret = nt36532_off(ctx);
|
|
+ if (ret < 0)
|
|
+ dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int nt36532_unprepare(struct drm_panel *panel)
|
|
+{
|
|
+ struct nt36532 *ctx = to_nt36532(panel);
|
|
+
|
|
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
|
|
+ regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int nt36532_get_modes(struct drm_panel *panel,
|
|
+ struct drm_connector *connector)
|
|
+{
|
|
+ struct nt36532 *ctx = to_nt36532(panel);
|
|
+ const struct panel_desc *desc = ctx->desc;
|
|
+ return drm_connector_helper_get_modes_fixed(connector, &desc->display_mode);
|
|
+}
|
|
+
|
|
+static const struct drm_panel_funcs nt36532_panel_funcs = {
|
|
+ .prepare = nt36532_prepare,
|
|
+ .enable = nt36532_enable,
|
|
+ .disable = nt36532_disable,
|
|
+ .unprepare = nt36532_unprepare,
|
|
+ .get_modes = nt36532_get_modes,
|
|
+};
|
|
+
|
|
+static int nt36532_probe(struct mipi_dsi_device *dsi)
|
|
+{
|
|
+ struct mipi_dsi_host *dsi_sec_host;
|
|
+ struct nt36532 *ctx;
|
|
+ struct device *dev = &dsi->dev;
|
|
+ struct device_node *dsi_sec;
|
|
+ int ret, i;
|
|
+
|
|
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
|
+ if (!ctx)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ctx->supplies[0].supply = "vddio";
|
|
+ ctx->supplies[1].supply = "dvddbuck";
|
|
+ ctx->supplies[2].supply = "dvddldo";
|
|
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
|
|
+ ctx->supplies);
|
|
+ if (ret < 0)
|
|
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
|
|
+
|
|
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
|
|
+ if (IS_ERR(ctx->reset_gpio))
|
|
+ return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
|
|
+ "Failed to get reset-gpios\n");
|
|
+
|
|
+ ctx->desc = of_device_get_match_data(dev);
|
|
+ if (!ctx->desc)
|
|
+ return -ENODEV;
|
|
+
|
|
+ dsi_sec = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);
|
|
+
|
|
+ if (dsi_sec) {
|
|
+ const struct mipi_dsi_device_info info = { "nt36532", 0,
|
|
+ dsi_sec };
|
|
+
|
|
+ dev_notice(dev, "Using Dual-DSI with OF node `%s`\n", dsi_sec->name);
|
|
+
|
|
+ dsi_sec_host = of_find_mipi_dsi_host_by_node(dsi_sec);
|
|
+ of_node_put(dsi_sec);
|
|
+ if (!dsi_sec_host) {
|
|
+ return dev_err_probe(dev, -EPROBE_DEFER,
|
|
+ "Cannot get secondary DSI host\n");
|
|
+ }
|
|
+
|
|
+ ctx->dsi[1] =
|
|
+ devm_mipi_dsi_device_register_full(dev, dsi_sec_host, &info);
|
|
+ if (IS_ERR(ctx->dsi[1])) {
|
|
+ return dev_err_probe(dev, PTR_ERR(ctx->dsi[1]),
|
|
+ "Cannot get secondary DSI node\n");
|
|
+ }
|
|
+
|
|
+ mipi_dsi_set_drvdata(ctx->dsi[1], ctx);
|
|
+ } else {
|
|
+ dev_notice(dev, "Using Single-DSI\n");
|
|
+ }
|
|
+
|
|
+ ctx->dsi[0] = dsi;
|
|
+ mipi_dsi_set_drvdata(dsi, ctx);
|
|
+
|
|
+ drm_panel_init(&ctx->panel, dev, &nt36532_panel_funcs,
|
|
+ DRM_MODE_CONNECTOR_DSI);
|
|
+ ctx->panel.prepare_prev_first = true;
|
|
+
|
|
+ ret = drm_panel_of_backlight(&ctx->panel);
|
|
+ if (ret)
|
|
+ return dev_err_probe(dev, ret, "Failed to get backlight\n");
|
|
+
|
|
+ drm_panel_add(&ctx->panel);
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) {
|
|
+ if (!ctx->dsi[i])
|
|
+ continue;
|
|
+
|
|
+ /* This panel only supports DSC; unconditionally enable it */
|
|
+ ctx->dsi[i]->dsc = ctx->desc->dsc_cfg;
|
|
+ ctx->dsi[i]->dsc_slice_per_pkt = 2;
|
|
+
|
|
+ ctx->dsi[i]->lanes = ctx->desc->lanes;
|
|
+ ctx->dsi[i]->format = ctx->desc->format;
|
|
+ ctx->dsi[i]->mode_flags = ctx->desc->mode_flags;
|
|
+
|
|
+ ret = devm_mipi_dsi_attach(dev, ctx->dsi[i]);
|
|
+ if (ret < 0) {
|
|
+ drm_panel_remove(&ctx->panel);
|
|
+ return dev_err_probe(dev, ret,
|
|
+ "Failed to attach to DSI%d\n", i);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void nt36532_remove(struct mipi_dsi_device *dsi)
|
|
+{
|
|
+ struct nt36532 *ctx = mipi_dsi_get_drvdata(dsi);
|
|
+
|
|
+ drm_panel_remove(&ctx->panel);
|
|
+}
|
|
+
|
|
+static const struct of_device_id nt36532_of_match[] = {
|
|
+ {
|
|
+ .compatible = "xiaomi,pipa-tianma-nt36532",
|
|
+ .data = &pipa_tianma_desc,
|
|
+ },
|
|
+ {
|
|
+ .compatible = "xiaomi,pipa-csot-nt36532",
|
|
+ .data = &pipa_csot_desc,
|
|
+ },
|
|
+ {},
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, nt36532_of_match);
|
|
+
|
|
+static struct mipi_dsi_driver nt36532_driver = {
|
|
+ .probe = nt36532_probe,
|
|
+ .remove = nt36532_remove,
|
|
+ .driver = {
|
|
+ .name = "panel-novatek-nt36532",
|
|
+ .of_match_table = nt36532_of_match,
|
|
+ },
|
|
+};
|
|
+module_mipi_dsi_driver(nt36532_driver);
|
|
+
|
|
+MODULE_AUTHOR("Luka Panio <luakapnio@gmail.com>");
|
|
+MODULE_DESCRIPTION("DRM panel driver for the Novatek NT36532 Driver-IC");
|
|
+MODULE_LICENSE("GPL");
|
|
--
|
|
2.52.0
|
|
|