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@xxxxxxxxxxxx> Cc: Heiko Stuebner <heiko@xxxxxxxxx> Cc: Jeffy Chen <jeffy.chen@xxxxxxxxxxxxxx> Cc: Rob Herring <robh+dt@xxxxxxxxxx> Cc: Stéphane Marchesin <marcheu@xxxxxxxxxxxx> Cc: Thierry Reding <thierry.reding@xxxxxxxxx> Cc: devicetree@xxxxxxxxxxxxxxx Cc: dri-devel@xxxxxxxxxxxxxxxxxxxxx Cc: linux-rockchip@xxxxxxxxxxxxxxxxxxx Signed-off-by: Sean Paul <seanpaul@xxxxxxxxxxxx> --- .../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 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html