Hi, 2018-02-08 18:48 GMT+01:00 Sean Paul <seanpaul@xxxxxxxxxxxx>: > 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@xxxxxxxxxxxx> > Cc: Eric Anholt <eric@xxxxxxxxxx> > 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 > Signed-off-by: Sean Paul <seanpaul@xxxxxxxxxxxx> > --- > 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@xxxxxxxxxxxxx> > 2.16.0.rc1.238.g530d649a79-goog > > _______________________________________________ > dri-devel mailing list > dri-devel@xxxxxxxxxxxxxxxxxxxxx > https://lists.freedesktop.org/mailman/listinfo/dri-devel _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel