[PATCH 1/3] drm/panel: simple: Add ability to override typical timing
Sean Paul
seanpaul at chromium.org
Tue Feb 6 08:56:20 PST 2018
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 at chromium.org>
Cc: Heiko Stuebner <heiko at sntech.de>
Cc: Jeffy Chen <jeffy.chen at rock-chips.com>
Cc: Rob Herring <robh+dt at kernel.org>
Cc: Stéphane Marchesin <marcheu at chromium.org>
Cc: Thierry Reding <thierry.reding at gmail.com>
Cc: devicetree at vger.kernel.org
Cc: dri-devel at lists.freedesktop.org
Cc: linux-rockchip at lists.infradead.org
Signed-off-by: Sean Paul <seanpaul at chromium.org>
---
.../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
More information about the Linux-rockchip
mailing list