Parse device tree for panel-timing and allow to override the typical
timing. This requires the timing to be defined as display_timing instead
of drm_display_mode.

Signed-off-by: Gerald Loacker <gerald.loac...@wolfvision.net>
---
 .../gpu/drm/panel/panel-sitronix-st7789v.c    | 174 +++++++++++++++---
 1 file changed, 144 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c 
b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
index 1ca04585aff2..ebde8a70deee 100644
--- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
+++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
@@ -10,6 +10,9 @@
 #include <linux/regulator/consumer.h>
 #include <linux/spi/spi.h>
 
+#include <video/display_timing.h>
+#include <video/of_display_timing.h>
+#include <video/videomode.h>
 #include <video/mipi_display.h>
 
 #include <drm/drm_device.h>
@@ -28,6 +31,8 @@
 #define ST7789V_RGBCTRL_CMD            0xb1
 #define ST7789V_RGBCTRL_WO                     BIT(7)
 #define ST7789V_RGBCTRL_RCM(n)                 (((n) & 3) << 5)
+#define ST7789V_RGBCTRL_RCM_DE                 2
+#define ST7789V_RGBCTRL_RCM_HV                 3
 #define ST7789V_RGBCTRL_VSYNC_HIGH             BIT(3)
 #define ST7789V_RGBCTRL_HSYNC_HIGH             BIT(2)
 #define ST7789V_RGBCTRL_PCLK_HIGH              BIT(1)
@@ -117,6 +122,7 @@ struct st7789v {
        struct spi_device *spi;
        struct gpio_desc *reset;
        struct regulator *power;
+       struct drm_display_mode override_mode;
        enum drm_panel_orientation orientation;
 };
 
@@ -157,39 +163,84 @@ static int st7789v_write_data(struct st7789v *ctx, u8 cmd)
        return st7789v_spi_write(ctx, ST7789V_DATA, cmd);
 }
 
-static const struct drm_display_mode default_mode = {
-       .clock = 7000,
-       .hdisplay = 240,
-       .hsync_start = 240 + 38,
-       .hsync_end = 240 + 38 + 10,
-       .htotal = 240 + 38 + 10 + 10,
-       .vdisplay = 320,
-       .vsync_start = 320 + 8,
-       .vsync_end = 320 + 8 + 4,
-       .vtotal = 320 + 8 + 4 + 4,
-       .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
+static const struct display_timing st7789v_timing = {
+       .pixelclock = { 1, 7000000, 8333333 },
+       .hactive = { 240, 240, 240 },
+       .hfront_porch = { 2, 38, 500 },
+       .hback_porch = { 4, 10, 21 },
+       .hsync_len = { 2, 10, 10 },
+       .vactive = { 320, 320, 320 },
+       .vfront_porch = { 8, 8, 500 },
+       .vback_porch = { 4, 4, 117 },
+       .vsync_len = { 4, 4, 10 },
+       .flags = DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH |
+                DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_POSEDGE |
+                DISPLAY_FLAGS_SYNC_POSEDGE,
 };
 
-static int st7789v_get_modes(struct drm_panel *panel,
-                            struct drm_connector *connector)
+struct panel_desc {
+       u32 bus_format;
+       u32 bus_flags;
+};
+
+static struct panel_desc st7789v_desc = {
+       .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE,
+       .bus_format = MEDIA_BUS_FMT_RGB666_1X18,
+};
+
+static unsigned int st7789v_get_timings_modes(struct drm_panel *panel,
+                                             struct drm_connector *connector)
 {
-       struct st7789v *ctx = panel_to_st7789v(panel);
        struct drm_display_mode *mode;
-       u32 bus_format = MEDIA_BUS_FMT_RGB666_1X18;
 
-       mode = drm_mode_duplicate(connector->dev, &default_mode);
+       const struct display_timing *dt = &st7789v_timing;
+       struct videomode vm;
+
+       videomode_from_timing(dt, &vm);
+       mode = drm_mode_create(connector->dev);
        if (!mode) {
-               dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
-                       default_mode.hdisplay, default_mode.vdisplay,
-                       drm_mode_vrefresh(&default_mode));
-               return -ENOMEM;
+               dev_err(panel->dev, "failed to add timing %ux%ux\n",
+                       dt->hactive.typ, dt->vactive.typ);
+               return 0;
        }
 
-       drm_mode_set_name(mode);
+       drm_display_mode_from_videomode(&vm, mode);
+
+       mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
 
-       mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
        drm_mode_probed_add(connector, mode);
 
+       return 1;
+}
+
+static int st7789v_get_modes(struct drm_panel *panel,
+                            struct drm_connector *connector)
+{
+       struct st7789v *ctx = panel_to_st7789v(panel);
+       struct drm_display_mode *mode;
+       bool has_override = ctx->override_mode.type;
+       u32 bus_format = MEDIA_BUS_FMT_RGB666_1X18;
+       unsigned int num;
+
+       if (has_override) {
+               mode = drm_mode_duplicate(connector->dev, &ctx->override_mode);
+               if (!mode) {
+                       dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
+                               ctx->override_mode.hdisplay,
+                               ctx->override_mode.vdisplay,
+                               drm_mode_vrefresh(&ctx->override_mode));
+                       return 0;
+               }
+
+               drm_mode_set_name(mode);
+
+               mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+               drm_mode_probed_add(connector, mode);
+               num = 1;
+       } else {
+               num = st7789v_get_timings_modes(panel, connector);
+       }
+
        connector->display_info.width_mm = 61;
        connector->display_info.height_mm = 103;
 
@@ -204,7 +255,7 @@ static int st7789v_get_modes(struct drm_panel *panel,
         */
        drm_connector_set_panel_orientation(connector, ctx->orientation);
 
-       return 1;
+       return num;
 }
 
 static enum drm_panel_orientation st7789v_get_orientation(struct drm_panel *p)
@@ -217,6 +268,8 @@ static enum drm_panel_orientation 
st7789v_get_orientation(struct drm_panel *p)
 static int st7789v_prepare(struct drm_panel *panel)
 {
        struct st7789v *ctx = panel_to_st7789v(panel);
+       struct drm_display_mode mode = ctx->override_mode;
+       u16 hbp, vbp, rcm, hsync = 0, vsync = 0, pclk = 0;
        int ret;
 
        ret = regulator_enable(ctx->power);
@@ -327,15 +380,29 @@ static int st7789v_prepare(struct drm_panel *panel)
        ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RAMCTRL_EPF(3) |
                                             ST7789V_RAMCTRL_MAGIC));
 
+       if ((st7789v_desc.bus_flags & DRM_BUS_FLAG_DE_HIGH) ||
+           (st7789v_desc.bus_flags & DRM_BUS_FLAG_DE_LOW))
+               rcm = ST7789V_RGBCTRL_RCM_DE;
+       else
+               rcm = ST7789V_RGBCTRL_RCM_HV;
+
+       if (st7789v_desc.bus_flags & DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE)
+               pclk = ST7789V_RGBCTRL_PCLK_HIGH;
+
+       if (mode.flags & DRM_MODE_FLAG_PHSYNC)
+               hsync = ST7789V_RGBCTRL_HSYNC_HIGH;
+
+       if (mode.flags & DRM_MODE_FLAG_PVSYNC)
+               vsync = ST7789V_RGBCTRL_VSYNC_HIGH;
+
+       vbp = mode.vtotal - mode.vsync_start;
+       hbp = mode.htotal - mode.hsync_start;
        ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_RGBCTRL_CMD));
        ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_WO |
-                                            ST7789V_RGBCTRL_RCM(2) |
-                                            ST7789V_RGBCTRL_VSYNC_HIGH |
-                                            ST7789V_RGBCTRL_HSYNC_HIGH |
-                                            ST7789V_RGBCTRL_PCLK_HIGH));
-       ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_VBP(8)));
-       ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_HBP(20)));
-
+                                            ST7789V_RGBCTRL_RCM(rcm) |
+                                            vsync | hsync | pclk));
+       ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_VBP(vbp)));
+       ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_HBP(hbp)));
        return 0;
 }
 
@@ -377,8 +444,50 @@ static const struct drm_panel_funcs st7789v_drm_funcs = {
        .unprepare = st7789v_unprepare,
 };
 
+static void st7789v_set_videomode(struct device *dev, struct st7789v *ctx,
+                                 const struct display_timing *dt)
+{
+       struct videomode vm;
+
+       videomode_from_timing(dt, &vm);
+       drm_display_mode_from_videomode(&vm, &ctx->override_mode);
+       ctx->override_mode.type |= DRM_MODE_TYPE_DRIVER |
+                                  DRM_MODE_TYPE_PREFERRED;
+
+       drm_bus_flags_from_videomode(&vm, &st7789v_desc.bus_flags);
+}
+
+#define ST7789V_BOUNDS_CHECK(to_check, bounds, field) \
+       (to_check->field.typ >= bounds->field.min &&  \
+        to_check->field.typ <= bounds->field.max)
+static void st7789v_parse_panel_timing_node(struct device *dev,
+                                           struct st7789v *ctx,
+                                           const struct display_timing *ot)
+{
+       const struct display_timing *dt;
+       bool ot_timing_valid = true;
+
+       dt = &st7789v_timing;
+
+       if (!ST7789V_BOUNDS_CHECK(ot, dt, hactive) ||
+           !ST7789V_BOUNDS_CHECK(ot, dt, hfront_porch) ||
+           !ST7789V_BOUNDS_CHECK(ot, dt, hback_porch) ||
+           !ST7789V_BOUNDS_CHECK(ot, dt, hsync_len) ||
+           !ST7789V_BOUNDS_CHECK(ot, dt, vactive) ||
+           !ST7789V_BOUNDS_CHECK(ot, dt, vfront_porch) ||
+           !ST7789V_BOUNDS_CHECK(ot, dt, vback_porch) ||
+           !ST7789V_BOUNDS_CHECK(ot, dt, vsync_len))
+               ot_timing_valid = false;
+
+       if (ot_timing_valid)
+               dt = ot;
+
+       st7789v_set_videomode(dev, ctx, dt);
+}
+
 static int st7789v_probe(struct spi_device *spi)
 {
+       struct display_timing dt;
        struct st7789v *ctx;
        int ret;
 
@@ -408,6 +517,11 @@ static int st7789v_probe(struct spi_device *spi)
 
        of_drm_get_panel_orientation(spi->dev.of_node, &ctx->orientation);
 
+       if (!of_get_display_timing(spi->dev.of_node, "panel-timing", &dt))
+               st7789v_parse_panel_timing_node(&spi->dev, ctx, &dt);
+       else
+               st7789v_set_videomode(&spi->dev, ctx, &st7789v_timing);
+
        drm_panel_add(&ctx->panel);
 
        return 0;
-- 
2.37.2

Reply via email to