From: YİĞİTCAN KAVAKLI <[email protected]>

Add support for the 1080x2340 Tianma video mode panel with Novatek
NT36672A driver IC found in the Xiaomi Redmi Note 8 (ginkgo / SM6125).

Changes:
- Add 1080x2340 display mode timings (183.025 MHz pixel clock)
- Add verified DCS initialization sequences for pages 0x25, 0x20, 0x24,
  0x26, and user command page 0x10
- Add standard DRM panel .enable and .disable callbacks to coordinate
  display on/off commands with active DSI video stream
- Set prepare_prev_first = true for host controller synchronization
- Optimize power-on reset sequence to nominal 10ms timings

Signed-off-by: YİĞİTCAN KAVAKLI <[email protected]>
---
 drivers/gpu/drm/panel/panel-novatek-nt36672a.c | 141 ++++++++++++++++++++-----
 1 file changed, 117 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36672a.c 
b/drivers/gpu/drm/panel/panel-novatek-nt36672a.c
index 
464d9ce47d87dc857c3a565a63156ac79ddeb30c..b69213790441f204016895a391b58f676762b1aa
 100644
--- a/drivers/gpu/drm/panel/panel-novatek-nt36672a.c
+++ b/drivers/gpu/drm/panel/panel-novatek-nt36672a.c
@@ -34,7 +34,7 @@ static const char * const nt36672a_regulator_names[] = {
        "vddneg",
 };
 
-static unsigned long const nt36672a_regulator_enable_loads[] = {
+static const unsigned long nt36672a_regulator_enable_loads[] = {
        62000,
        100000,
        100000
@@ -92,18 +92,12 @@ static int nt36672a_panel_unprepare(struct drm_panel *panel)
        if (pinfo->desc->send_deinit_cmds)
                pinfo->desc->send_deinit_cmds(&dsi_ctx);
 
-       /* Reset error to continue with display off even if send_cmds failed */
+       /* Reset error to continue power-down even if send_cmds failed */
        dsi_ctx.accum_err = 0;
-       mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
-       /* Reset error to continue power-down even if display off failed */
-       dsi_ctx.accum_err = 0;
-
-       /* 120ms delay required here as per DCS spec */
-       msleep(120);
 
        mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
 
-       /* 0x3C = 60ms delay */
+       /* 60ms delay as per DCS spec */
        msleep(60);
 
        nt36672a_panel_power_off(panel);
@@ -120,15 +114,14 @@ static int nt36672a_panel_power_on(struct nt36672a_panel 
*pinfo)
                return ret;
 
        /*
-        * As per downstream kernel, Reset sequence of Tianma FHD panel 
requires the panel to
-        * be out of reset for 10ms, followed by being held in reset for 10ms. 
But for Android
-        * AOSP, we needed to bump it upto 200ms otherwise we get white screen 
sometimes.
-        * FIXME: Try to reduce this 200ms to a lesser value.
+        * Reset sequence: 10ms out of reset, 10ms held in reset, 10ms out of 
reset.
         */
-       gpiod_set_value(pinfo->reset_gpio, 1);
-       msleep(200);
-       gpiod_set_value(pinfo->reset_gpio, 0);
-       msleep(200);
+       gpiod_set_value_cansleep(pinfo->reset_gpio, 0);
+       usleep_range(10000, 11000);
+       gpiod_set_value_cansleep(pinfo->reset_gpio, 1);
+       usleep_range(10000, 11000);
+       gpiod_set_value_cansleep(pinfo->reset_gpio, 0);
+       usleep_range(10000, 11000);
 
        return 0;
 }
@@ -146,19 +139,41 @@ static int nt36672a_panel_prepare(struct drm_panel *panel)
 
        mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
 
-       /* 0x46 = 70 ms delay */
-       mipi_dsi_msleep(&dsi_ctx, 70);
-
-       mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+       /* 80 ms delay as per DCS spec / downstream timing */
+       msleep(80);
 
        /* Send rest of the init cmds */
        if (pinfo->desc->send_init_cmds_2)
                pinfo->desc->send_init_cmds_2(&dsi_ctx);
 
-       mipi_dsi_msleep(&dsi_ctx, 120);
-
        if (dsi_ctx.accum_err < 0)
-               gpiod_set_value(pinfo->reset_gpio, 0);
+               gpiod_set_value_cansleep(pinfo->reset_gpio, 0);
+
+       return dsi_ctx.accum_err;
+}
+
+static int nt36672a_panel_enable(struct drm_panel *panel)
+{
+       struct nt36672a_panel *pinfo = to_nt36672a_panel(panel);
+       struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->link };
+
+       mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+
+       /* Frame settling delay */
+       msleep(20);
+
+       return dsi_ctx.accum_err;
+}
+
+static int nt36672a_panel_disable(struct drm_panel *panel)
+{
+       struct nt36672a_panel *pinfo = to_nt36672a_panel(panel);
+       struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->link };
+
+       mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+
+       /* 120ms delay as per DCS spec */
+       msleep(120);
 
        return dsi_ctx.accum_err;
 }
@@ -189,6 +204,8 @@ static int nt36672a_panel_get_modes(struct drm_panel *panel,
 static const struct drm_panel_funcs panel_funcs = {
        .unprepare = nt36672a_panel_unprepare,
        .prepare = nt36672a_panel_prepare,
+       .enable = nt36672a_panel_enable,
+       .disable = nt36672a_panel_disable,
        .get_modes = nt36672a_panel_get_modes,
 };
 
@@ -404,6 +421,79 @@ static const struct nt36672a_panel_desc 
tianma_fhd_video_panel_desc = {
        .send_deinit_cmds = tianma_fhd_video_send_deinit_cmds,
 };
 
+static void tianma_ginkgo_send_init_cmds(struct mipi_dsi_multi_context 
*dsi_ctx)
+{
+       /* Page 0x25 init */
+       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, 0x18, 0x96);
+       mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x05, 0x04);
+
+       /* Page 0x20 init */
+       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, 0x78, 0x01);
+
+       /* Page 0x24 init */
+       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, 0x82, 0x13);
+       mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x84, 0x31);
+       mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x88, 0x13);
+       mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x8a, 0x31);
+       mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x8e, 0xe4);
+       mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x8f, 0x01);
+       mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x90, 0x80);
+
+       /* Page 0x26 init */
+       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, 0xa9, 0x12);
+       mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xaa, 0x10);
+       mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xae, 0x8a);
+
+       /* Page 0x10 (User command page) */
+       mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x10);
+}
+
+static void tianma_ginkgo_send_post_on_cmds(struct mipi_dsi_multi_context 
*dsi_ctx)
+{
+       mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb0, 0x01);
+       mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x35, 0x00);
+       mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x68, 0x04, 0x03);
+       mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x51, 0xb8);
+       mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x53, 0x2c);
+       mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x55, 0x00);
+}
+
+static const struct drm_display_mode tianma_ginkgo_fhd_video_mode = {
+       .clock          = 183025,
+
+       .hdisplay       = 1080,
+       .hsync_start    = 1080 + 90,
+       .hsync_end      = 1080 + 90 + 2,
+       .htotal         = 1080 + 90 + 2 + 120,
+
+       .vdisplay       = 2340,
+       .vsync_start    = 2340 + 10,
+       .vsync_end      = 2340 + 10 + 3,
+       .vtotal         = 2340 + 10 + 3 + 8,
+
+       .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static const struct nt36672a_panel_desc tianma_ginkgo_fhd_video_panel_desc = {
+       .display_mode = &tianma_ginkgo_fhd_video_mode,
+       .panel_name = "tianma-ginkgo-fhd-video",
+       .width_mm = 67,
+       .height_mm = 145,
+       .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM,
+       .format = MIPI_DSI_FMT_RGB888,
+       .lanes = 4,
+       .send_init_cmds_1 = tianma_ginkgo_send_init_cmds,
+       .send_init_cmds_2 = tianma_ginkgo_send_post_on_cmds,
+};
+
 static int nt36672a_panel_add(struct nt36672a_panel *pinfo)
 {
        struct device *dev = &pinfo->link->dev;
@@ -428,6 +518,8 @@ static int nt36672a_panel_add(struct nt36672a_panel *pinfo)
        if (ret)
                return dev_err_probe(dev, ret, "Failed to get backlight\n");
 
+       pinfo->base.prepare_prev_first = true;
+
        drm_panel_add(&pinfo->base);
 
        return 0;
@@ -481,6 +573,7 @@ static void nt36672a_panel_remove(struct mipi_dsi_device 
*dsi)
 
 static const struct of_device_id tianma_fhd_video_of_match[] = {
        { .compatible = "tianma,fhd-video", .data = 
&tianma_fhd_video_panel_desc },
+       { .compatible = "tianma,fhd-video-ginkgo", .data = 
&tianma_ginkgo_fhd_video_panel_desc },
        { },
 };
 MODULE_DEVICE_TABLE(of, tianma_fhd_video_of_match);

-- 
2.47.3


Reply via email to