From: Esteban Urrutia <[email protected]>

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 <[email protected]>
---
 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 <[email protected]>
+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 <[email protected]>
 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 <[email protected]>
+ */
+
+#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 <[email protected]>");
+MODULE_DESCRIPTION("DRM driver for Novatek NT37703 based MIPI DSI panels");
+MODULE_LICENSE("GPL");

-- 
2.55.0



Reply via email to