[PATCH 1/3] drm/panel: simple: Add ability to override typical timing
From: Sean Paul <hidden>
Date: 2018-02-06 16:56:20
Also in:
dri-devel, linux-rockchip
Subsystem:
drm drivers, drm drivers and misc gpu patches, drm panel drivers, open firmware and flattened device tree bindings, the rest · Maintainers:
David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Linus Torvalds
This patch adds the ability to override the typical display timing for a given panel. This is useful for devices which have timing constraints that do not apply across the entire display driver (eg: to avoid crosstalk between panel and digitizer on certain laptops). The rules are as follows: - panel must not specify fixed mode (since the override mode will either be the same as the fixed mode, or we'll be unable to check the bounds of the overried) - panel must specify at least one display_timing range which will be used to ensure the override mode fits within its bounds Cc: Doug Anderson <dianders@chromium.org> Cc: Heiko Stuebner <heiko@sntech.de> Cc: Jeffy Chen <redacted> Cc: Rob Herring <robh+dt@kernel.org> Cc: Stéphane Marchesin <redacted> Cc: Thierry Reding <redacted> Cc: devicetree@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: linux-rockchip@lists.infradead.org Signed-off-by: Sean Paul <redacted> --- .../bindings/display/panel/simple-panel.txt | 20 +++++++ drivers/gpu/drm/panel/panel-simple.c | 69 +++++++++++++++++++++- 2 files changed, 88 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/display/panel/simple-panel.txt b/Documentation/devicetree/bindings/display/panel/simple-panel.txt
index 16d8ff088b7d..590bbff6fc90 100644
--- a/Documentation/devicetree/bindings/display/panel/simple-panel.txt
+++ b/Documentation/devicetree/bindings/display/panel/simple-panel.txt@@ -7,6 +7,14 @@ Optional properties: - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing - enable-gpios: GPIO pin to enable or disable the panel - backlight: phandle of the backlight device attached to the panel +- override-mode: For devices which require a mode which differs from the + display_timing's "typical" mode, and whose restrictions cannot + be applied across the entire platform. The mode must fall + within the bounds of the panel's specified display_timing, and + cannot be used if the panel already specifies a single fixed + mode. The format is specified under "timing subnode" in + display-timing.txt + Example:
@@ -18,4 +26,16 @@ Example: enable-gpios = <&gpio 90 0>; backlight = <&backlight>; + + override-mode { + clock-frequency = <266604720>; + hactive = <2400>; + hfront-porch = <48>; + hback-porch = <84>; + hsync-len = <32>; + vactive = <1600>; + vfront-porch = <3>; + vback-porch = <120>; + vsync-len = <10>; + } };
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 5591984a392b..b774365f3635 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c@@ -34,6 +34,7 @@ #include <drm/drm_panel.h> #include <video/display_timing.h> +#include <video/of_display_timing.h> #include <video/videomode.h> struct panel_desc {
@@ -87,6 +88,8 @@ struct panel_simple { struct i2c_adapter *ddc; struct gpio_desc *enable_gpio; + + struct drm_display_mode override_mode; }; static inline struct panel_simple *to_panel_simple(struct drm_panel *panel)
@@ -99,11 +102,22 @@ static int panel_simple_get_fixed_modes(struct panel_simple *panel) struct drm_connector *connector = panel->base.connector; struct drm_device *drm = panel->base.drm; struct drm_display_mode *mode; + bool has_override = panel->override_mode.type; unsigned int i, num = 0; if (!panel->desc) return 0; + if (has_override) { + mode = drm_mode_duplicate(drm, &panel->override_mode); + if (mode) { + drm_mode_probed_add(connector, mode); + num++; + } else { + dev_err(drm->dev, "failed to add override mode\n"); + } + } + for (i = 0; i < panel->desc->num_timings; i++) { const struct display_timing *dt = &panel->desc->timings[i]; struct videomode vm;
@@ -120,7 +134,7 @@ static int panel_simple_get_fixed_modes(struct panel_simple *panel) mode->type |= DRM_MODE_TYPE_DRIVER; - if (panel->desc->num_timings == 1) + if (panel->desc->num_timings == 1 && !has_override) mode->type |= DRM_MODE_TYPE_PREFERRED; drm_mode_probed_add(connector, mode);
@@ -291,10 +305,58 @@ static const struct drm_panel_funcs panel_simple_funcs = { .get_timings = panel_simple_get_timings, }; +#define PANEL_SIMPLE_BOUNDS_CHECK(to_check, bounds, field) \ + (to_check->field.typ >= bounds->field.min && \ + to_check->field.typ <= bounds->field.max) +static void panel_simple_add_override_mode(struct device *dev, + struct panel_simple *panel, + const struct display_timing *ot) +{ + const struct panel_desc *desc = panel->desc; + struct videomode vm; + int i; + + if (desc->num_modes) { + dev_err(dev, "Reject override mode: panel has a fixed mode\n"); + return; + } + if (!desc->num_timings) { + dev_err(dev, "Reject override mode: no timings specified\n"); + return; + } + + for (i = 0; i < panel->desc->num_timings; i++) { + const struct display_timing *dt = &panel->desc->timings[i]; + + if (!PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, hactive) || + !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, hfront_porch) || + !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, hback_porch) || + !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, hsync_len) || + !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, vactive) || + !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, vfront_porch) || + !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, vback_porch) || + !PANEL_SIMPLE_BOUNDS_CHECK(ot, dt, vsync_len)) + continue; + + if (ot->flags != dt->flags) + continue; + + videomode_from_timing(ot, &vm); + drm_display_mode_from_videomode(&vm, &panel->override_mode); + panel->override_mode.type |= DRM_MODE_TYPE_DRIVER | + DRM_MODE_TYPE_PREFERRED; + return; + } + + dev_err(dev, "Reject override mode: No display_timing found\n"); + return; +} + static int panel_simple_probe(struct device *dev, const struct panel_desc *desc) { struct device_node *backlight, *ddc; struct panel_simple *panel; + struct display_timing override_timing; int err; panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
@@ -338,6 +400,11 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc) } } + err = of_get_display_timing(dev->of_node, "override-mode", + &override_timing); + if (!err) + panel_simple_add_override_mode(dev, panel, &override_timing); + drm_panel_init(&panel->base); panel->base.dev = dev; panel->base.funcs = &panel_simple_funcs;
--
2.16.0.rc1.238.g530d649a79-goog
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel