* [PATCH 2/2] drm/panel: Add driver for Novatek NT37703 based panels
2026-08-11 5:52 [PATCH 0/2] Add panel driver for Novatek NT37703 Esteban Urrutia via B4 Relay
2026-08-11 5:52 ` [PATCH 1/2] dt-bindings: display: panel: Add bindings " Esteban Urrutia via B4 Relay
@ 2026-08-11 5:52 ` Esteban Urrutia via B4 Relay
2026-08-11 14:28 ` Uwe Kleine-König
1 sibling, 1 reply; 6+ messages in thread
From: Esteban Urrutia via B4 Relay @ 2026-08-11 5:52 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: dri-devel, devicetree, linux-kernel, phone-devel, Esteban Urrutia
From: Esteban Urrutia <esteuwu@proton.me>
Add driver for panels based on this DDIC.
Initial support is added for the Tianma "v2" panel (no panel model) found
in the Motorola ThinkPhone by motorola.
Signed-off-by: Esteban Urrutia <esteuwu@proton.me>
---
MAINTAINERS | 6 +
drivers/gpu/drm/panel/Kconfig | 13 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-novatek-nt37703.c | 467 ++++++++++++++++++++++++++
4 files changed, 487 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index da14a58df9e8..e2bb50a8b1af 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8339,6 +8339,12 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/panel/novatek,nt36672a.yaml
F: drivers/gpu/drm/panel/panel-novatek-nt36672a.c
+DRM DRIVER FOR NOVATEK NT37703 PANELS
+M: Esteban Urrutia <esteuwu@proton.me>
+S: Maintained
+F: Documentation/devicetree/bindings/display/panel/novatek,nt37703.yaml
+F: drivers/gpu/drm/panel/panel-novatek-nt37703.c
+
DRM DRIVER FOR NOVATEK NT37801 PANELS
M: Krzysztof Kozlowski <krzk@kernel.org>
S: Maintained
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index cbdf7b8f7f7a..9cd74ceef44e 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -706,6 +706,19 @@ config DRM_PANEL_NOVATEK_NT37700F
Say Y here if you want to enable support for Novatek NT37700F DSI
panel module. The panel has a resolution of 1080x2160.
+config DRM_PANEL_NOVATEK_NT37703
+ tristate "Novatek NT37703 DSI panel"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_DISPLAY_DSC_HELPER
+ select DRM_DISPLAY_HELPER
+ help
+ Say Y or M here if you want to enable support for panels based
+ on the Novatek NT37703 display driver IC, such as Tianma
+ panels found in some units of the Motorola ThinkPhone by
+ motorola.
+
config DRM_PANEL_NOVATEK_NT37801
tristate "Novatek NT37801/NT37810 AMOLED DSI panel"
depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 3b523cf37833..4a69b2657c5b 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36536) += panel-novatek-nt36536.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_NT37700F) += panel-novatek-nt37700f.o
+obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37703) += panel-novatek-nt37703.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37801) += panel-novatek-nt37801.o
obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o
obj-$(CONFIG_DRM_PANEL_MANTIX_MLAF057WE51) += panel-mantix-mlaf057we51.o
diff --git a/drivers/gpu/drm/panel/panel-novatek-nt37703.c b/drivers/gpu/drm/panel/panel-novatek-nt37703.c
new file mode 100644
index 000000000000..5858bf12a310
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-novatek-nt37703.c
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Novatek NT37703 based MIPI DSI panels
+ *
+ * Multiple panel support based on Novatek NT36523 driver
+ * Per panel DSC support based on Ilitek ILI9882T driver
+ *
+ * Copyright (c) 2026 Esteban Urrutia <esteuwu@proton.me>
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/display/drm_dsc.h>
+#include <drm/display/drm_dsc_helper.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+#define DSC_BPG_OFFSET(x) ((u8) ((x) & DSC_RANGE_BPG_OFFSET_MASK))
+#define NT37703_DCS_SWITCH_PAGE 0xf0
+
+#define nt37703_cmd_test_on(ctx) mipi_dsi_dcs_write_seq_multi((ctx), 0xff, 0xaa, 0x55, \
+ 0xa5, 0x80)
+#define nt37703_switch_page(ctx, page) mipi_dsi_dcs_write_seq_multi((ctx), \
+ NT37703_DCS_SWITCH_PAGE, \
+ 0x55, 0xaa, 0x52, 0x08, (page))
+
+struct panel_info {
+ struct drm_panel panel;
+ const struct panel_desc *desc;
+ struct mipi_dsi_device *dsi;
+ struct drm_dsc_config dsc;
+ struct regulator_bulk_data *supplies;
+ struct gpio_desc *reset_gpio;
+};
+
+struct panel_desc {
+ unsigned int width_mm;
+ unsigned int height_mm;
+
+ unsigned int lanes;
+ unsigned long mode_flags;
+ enum mipi_dsi_pixel_format format;
+
+ const struct drm_display_mode *modes;
+ unsigned int num_modes;
+ int (*init_sequence)(struct panel_info *pinfo);
+ const struct drm_dsc_config *dsc;
+};
+
+/*
+ * These parameters are applied when using DSC 1.1 and when the DSC configuration specifies 10 b.p.c
+ * and 8 b.p.p
+ */
+static const struct drm_dsc_rc_range_parameters nt37703_rc_range_params[DSC_NUM_BUF_RANGES] = {
+ { 0, 8, DSC_BPG_OFFSET(2) },
+ { 4, 8, DSC_BPG_OFFSET(0) },
+ { 5, 9, DSC_BPG_OFFSET(0) },
+ { 5, 10, DSC_BPG_OFFSET(-2) },
+ { 7, 11, DSC_BPG_OFFSET(-4) },
+ { 7, 11, DSC_BPG_OFFSET(-6) },
+ { 7, 11, DSC_BPG_OFFSET(-8) },
+ { 7, 12, DSC_BPG_OFFSET(-8) },
+ { 7, 13, DSC_BPG_OFFSET(-8) },
+ { 8, 14, DSC_BPG_OFFSET(-10) },
+ { 9, 14, DSC_BPG_OFFSET(-10) },
+ { 9, 15, DSC_BPG_OFFSET(-10) },
+ { 9, 15, DSC_BPG_OFFSET(-12) },
+ { 12, 16, DSC_BPG_OFFSET(-12) },
+ { 16, 17, DSC_BPG_OFFSET(-12) },
+};
+
+static const struct regulator_bulk_data nt37703_supplies[] = {
+ { .supply = "avdd" },
+ { .supply = "elvdd" },
+ { .supply = "elvss" },
+ { .supply = "vci" },
+ { .supply = "vdd" },
+ { .supply = "vddi" },
+};
+
+static inline struct panel_info *to_panel_info(struct drm_panel *panel)
+{
+ return container_of_const(panel, struct panel_info, panel);
+}
+
+static void nt37703_reset(struct panel_info *pinfo)
+{
+ gpiod_set_value_cansleep(pinfo->reset_gpio, 0);
+ usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(pinfo->reset_gpio, 1);
+ usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(pinfo->reset_gpio, 0);
+ usleep_range(10000, 11000);
+}
+
+static int nt37703_off(struct panel_info *pinfo)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->dsi };
+
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ return dsi_ctx.accum_err;
+}
+
+static int nt37703_prepare(struct drm_panel *panel)
+{
+ struct panel_info *pinfo = to_panel_info(panel);
+ struct device *dev = &pinfo->dsi->dev;
+ struct drm_dsc_picture_parameter_set pps;
+ int ret;
+ int i;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(nt37703_supplies), pinfo->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ nt37703_reset(pinfo);
+
+ ret = pinfo->desc->init_sequence(pinfo);
+ if (ret < 0) {
+ dev_err(dev, "Failed to initialize panel: %d\n", ret);
+ gpiod_set_value_cansleep(pinfo->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(nt37703_supplies), pinfo->supplies);
+ return ret;
+ }
+
+ /*
+ * For some reason this DDIC uses different parameters for the RC ranges under certain DSC
+ * configurations.
+ * Not applying them results in heavy visual glitches, so do so if all conditions are met.
+ */
+ if (pinfo->dsc.dsc_version_major == 1 && pinfo->dsc.dsc_version_minor == 1 &&
+ pinfo->dsc.bits_per_component == 10 && pinfo->dsc.bits_per_pixel == 8 << 4) {
+ for (i = 0; i < DSC_NUM_BUF_RANGES; i++) {
+ pinfo->dsc.rc_range_params[i].range_min_qp =
+ nt37703_rc_range_params[i].range_min_qp;
+ pinfo->dsc.rc_range_params[i].range_max_qp =
+ nt37703_rc_range_params[i].range_max_qp;
+ pinfo->dsc.rc_range_params[i].range_bpg_offset =
+ nt37703_rc_range_params[i].range_bpg_offset;
+ }
+ }
+
+ /*
+ * Given drm_dsc_setup_rc_params() inconditionally overrides certain members in pinfo->dsc
+ * that may have been set in pinfo->desc->dsc, override these members once more with what's
+ * found in pinfo->desc->dsc.
+ */
+ if (pinfo->desc->dsc->first_line_bpg_offset)
+ pinfo->dsc.first_line_bpg_offset = pinfo->desc->dsc->first_line_bpg_offset;
+
+ drm_dsc_pps_payload_pack(&pps, &pinfo->dsc);
+
+ ret = mipi_dsi_picture_parameter_set(pinfo->dsi, &pps);
+ if (ret < 0) {
+ dev_err(panel->dev, "Failed to transmit PPS: %d\n", ret);
+ return ret;
+ }
+
+ ret = mipi_dsi_compression_mode(pinfo->dsi, true);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable compression mode: %d\n", ret);
+ return ret;
+ }
+
+ msleep(28);
+
+ return 0;
+}
+
+static int nt37703_unprepare(struct drm_panel *panel)
+{
+ struct panel_info *pinfo = to_panel_info(panel);
+ struct device *dev = &pinfo->dsi->dev;
+ int ret;
+
+ ret = nt37703_off(pinfo);
+ if (ret < 0)
+ dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
+
+ gpiod_set_value_cansleep(pinfo->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(nt37703_supplies), pinfo->supplies);
+
+ return 0;
+}
+
+static int nt37703_get_modes(struct drm_panel *panel, struct drm_connector *connector)
+{
+ struct panel_info *pinfo = to_panel_info(panel);
+ int i;
+
+ for (i = 0; i < pinfo->desc->num_modes; i++) {
+ const struct drm_display_mode *m = &pinfo->desc->modes[i];
+ struct drm_display_mode *mode;
+
+ mode = drm_mode_duplicate(connector->dev, m);
+ if (!mode) {
+ dev_err(panel->dev, "Failed to add mode %ux%u@%u\n", m->hdisplay,
+ m->vdisplay, drm_mode_vrefresh(m));
+ return -ENOMEM;
+ }
+
+ mode->type = DRM_MODE_TYPE_DRIVER;
+ if (i == 0)
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(connector, mode);
+ }
+
+ connector->display_info.width_mm = pinfo->desc->width_mm;
+ connector->display_info.height_mm = pinfo->desc->height_mm;
+
+ return pinfo->desc->num_modes;
+}
+
+static const struct drm_panel_funcs nt37703_panel_funcs = {
+ .prepare = nt37703_prepare,
+ .unprepare = nt37703_unprepare,
+ .get_modes = nt37703_get_modes,
+};
+
+static int nt37703_bl_update_status(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ u16 brightness = backlight_get_brightness(bl);
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
+ if (ret < 0)
+ return ret;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ return 0;
+}
+
+static int nt37703_bl_get_brightness(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ u16 brightness;
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
+ if (ret < 0)
+ return ret;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ return brightness;
+}
+
+static const struct backlight_ops nt37703_bl_ops = {
+ .update_status = nt37703_bl_update_status,
+ .get_brightness = nt37703_bl_get_brightness,
+};
+
+static struct backlight_device *nt37703_create_backlight(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ const struct backlight_properties props = {
+ .type = BACKLIGHT_RAW,
+ .brightness = 2047,
+ .max_brightness = 4095,
+ };
+
+ return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, &nt37703_bl_ops,
+ &props);
+}
+
+static const struct drm_display_mode bronco_tianma_modes[] = {
+ {
+ .clock = (1080 + 16 + 4 + 16) * (2400 + 44 + 2 + 14) * 144 / 1000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 16,
+ .hsync_end = 1080 + 16 + 4,
+ .htotal = 1080 + 16 + 4 + 16,
+ .vdisplay = 2400,
+ .vsync_start = 2400 + 44,
+ .vsync_end = 2400 + 44 + 2,
+ .vtotal = 2400 + 44 + 2 + 14,
+ },
+};
+
+static int bronco_tianma_v2_init_sequence(struct panel_info *pinfo)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->dsi };
+
+ nt37703_switch_page(&dsi_ctx, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb5, 0x29);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb5, 0x2b, 0x1b, 0x00, 0x32);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x88, 0x01, 0x02, 0x1b, 0x08, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x87, 0x20);
+ mipi_dsi_dcs_set_column_address_multi(&dsi_ctx, 0, 1080 - 1);
+ mipi_dsi_dcs_set_page_address_multi(&dsi_ctx, 0, 2400 - 1);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x90, 0x03, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
+ nt37703_cmd_test_on(&dsi_ctx);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x15);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf8, 0x01, 0xe1);
+ nt37703_cmd_test_on(&dsi_ctx);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf4, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0xaa, 0x55, 0xa5, 0x81);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x13);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf9, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0xaa, 0x55, 0xa5, 0x81);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x3c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf5, 0x81);
+ mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+ mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0x0000);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2f, 0x00);
+ nt37703_switch_page(&dsi_ctx, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc7, 0x47);
+ nt37703_switch_page(&dsi_ctx, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0xc4);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb2, 0x09, 0x84, 0x09, 0x84, 0x09, 0x84, 0x16,
+ 0xd6);
+ nt37703_switch_page(&dsi_ctx, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x33);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc6, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00,
+ 0x18);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2f, 0x83);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8b, 0x00, 0x00);
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+
+ return dsi_ctx.accum_err;
+}
+
+static const struct drm_dsc_config bronco_tianma_dsc = {
+ .dsc_version_major = 1,
+ .dsc_version_minor = 1,
+ .slice_height = 20,
+ .slice_width = 540,
+ .slice_count = 2, /* 1080 / slice_width */
+ .bits_per_component = 10,
+ .bits_per_pixel = 8 << 4, /* 4 fractional bits */
+ .block_pred_enable = true,
+ .first_line_bpg_offset = 13,
+};
+
+static const struct panel_desc bronco_tianma_v2_desc = {
+ .modes = bronco_tianma_modes,
+ .num_modes = ARRAY_SIZE(bronco_tianma_modes),
+ .width_mm = 68,
+ .height_mm = 152,
+ .lanes = 4,
+ .format = MIPI_DSI_FMT_RGB101010,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_NO_EOT_PACKET |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM |
+ MIPI_DSI_MODE_DSC_ALL_SLICES_IN_PKT;
+ .init_sequence = bronco_tianma_v2_init_sequence,
+ .dsc = &bronco_tianma_dsc,
+};
+
+static int nt37703_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct panel_info *pinfo;
+ int ret;
+
+ pinfo = devm_drm_panel_alloc(dev, struct panel_info, panel, &nt37703_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(pinfo))
+ return PTR_ERR(pinfo);
+
+ pinfo->desc = of_device_get_match_data(dev);
+ if (IS_ERR(pinfo->desc))
+ return PTR_ERR(pinfo->desc);
+
+ ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(nt37703_supplies), nt37703_supplies,
+ &pinfo->supplies);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+ pinfo->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(pinfo->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(pinfo->reset_gpio),
+ "Failed to get reset-gpios\n");
+
+ pinfo->dsi = dsi;
+ mipi_dsi_set_drvdata(dsi, pinfo);
+
+ dsi->lanes = pinfo->desc->lanes;
+ dsi->format = pinfo->desc->format;
+ dsi->mode_flags = pinfo->desc->mode_flags;
+
+ pinfo->panel.prepare_prev_first = true;
+
+ pinfo->panel.backlight = nt37703_create_backlight(dsi);
+ if (IS_ERR(pinfo->panel.backlight))
+ return dev_err_probe(dev, PTR_ERR(pinfo->panel.backlight),
+ "Failed to create backlight\n");
+
+ drm_panel_add(&pinfo->panel);
+
+ /* This panel only supports DSC; unconditionally enable it */
+ pinfo->dsc = *(pinfo->desc->dsc);
+ dsi->dsc = &pinfo->dsc;
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0) {
+ drm_panel_remove(&pinfo->panel);
+ return dev_err_probe(dev, ret, "Failed to attach to DSI host\n");
+ }
+
+ return 0;
+}
+
+static void nt37703_remove(struct mipi_dsi_device *dsi)
+{
+ struct panel_info *pinfo = mipi_dsi_get_drvdata(dsi);
+ int ret;
+
+ ret = mipi_dsi_detach(dsi);
+ if (ret < 0)
+ dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
+
+ drm_panel_remove(&pinfo->panel);
+}
+
+static const struct of_device_id nt37703_of_match[] = {
+ {
+ .compatible = "motorola,bronco-tianma-v2-nt37703",
+ .data = &bronco_tianma_v2_desc,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, nt37703_of_match);
+
+static struct mipi_dsi_driver nt37703_driver = {
+ .probe = nt37703_probe,
+ .remove = nt37703_remove,
+ .driver = {
+ .name = "panel-novatek-nt37703",
+ .of_match_table = nt37703_of_match,
+ },
+};
+module_mipi_dsi_driver(nt37703_driver);
+
+MODULE_AUTHOR("Esteban Urrutia <esteuwu@proton.me>");
+MODULE_DESCRIPTION("DRM driver for Novatek NT37703 based MIPI DSI panels");
+MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread