[PATCH v3 4/6] drm/panel: simple: Add ability to override typical timing
Enric Balletbo Serra
eballetbo at gmail.com
Mon Feb 19 14:33:52 UTC 2018
Hi,
2018-02-08 18:48 GMT+01:00 Sean Paul <seanpaul at chromium.org>:
> 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
>
> Changes in v2:
> - Parse the full display-timings node (using the native-mode) (Rob)
> Changes in v3:
> - No longer parse display-timings subnode, use panel-timing (Rob)
>
> Cc: Doug Anderson <dianders at chromium.org>
> Cc: Eric Anholt <eric at anholt.net>
> 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
> Signed-off-by: Sean Paul <seanpaul at chromium.org>
> ---
> drivers/gpu/drm/panel/panel-simple.c | 67 +++++++++++++++++++++++++++++++++++-
> 1 file changed, 66 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
> index 5591984a392b..87488392bca1 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 dt;
> int err;
>
> panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
> @@ -338,6 +400,9 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
> }
> }
>
> + if (!of_get_display_timing(dev->of_node, "panel-timing", &dt))
> + panel_simple_add_override_mode(dev, panel, &dt);
> +
> drm_panel_init(&panel->base);
> panel->base.dev = dev;
> panel->base.funcs = &panel_simple_funcs;
> --
Tested on top of linux-next on a Samsung Chromebook Plus.
Tested-by: Enric Balletbo i Serra <enric.balletbo at collabora.com>
> 2.16.0.rc1.238.g530d649a79-goog
>
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
More information about the dri-devel
mailing list