On Mon,  1 Apr 2019 10:17:19 -0700
Douglas Anderson <diand...@chromium.org> wrote:

> From: Sean Paul <seanp...@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)
> Changes in v4:
>  - Don't add mode from timing if override was specified (Thierry)
>  - Add warning if timing and fixed mode was specified (Thierry)
>  - Don't add fixed mode if timing was specified (Thierry)
>  - Refactor/rename a bit to avoid extra indentation from "if" tests
>  - i should be unsigned (Thierry)
>  - Add annoying WARN_ONs for some cases (Thierry)
>  - Simplify 'No display_timing found' handling (Thierry)
>  - Rename to panel_simple_parse_override_mode() (Thierry)
> Changes in v5:
>  - Added Heiko's Tested-by
> 
> Cc: Doug Anderson <diand...@chromium.org>
> Cc: Eric Anholt <e...@anholt.net>
> Cc: Heiko Stuebner <he...@sntech.de>
> Cc: Jeffy Chen <jeffy.c...@rock-chips.com>
> Cc: Rob Herring <robh...@kernel.org>
> Cc: Stéphane Marchesin <marc...@chromium.org>
> Cc: Thierry Reding <thierry.red...@gmail.com>
> Cc: devicet...@vger.kernel.org
> Cc: dri-devel@lists.freedesktop.org
> Signed-off-by: Sean Paul <seanp...@chromium.org>
> Tested-by: Enric Balletbo i Serra <enric.balle...@collabora.com>
> Signed-off-by: Douglas Anderson <diand...@chromium.org>
> Tested-by: Heiko Stuebner <he...@sntech.de>

Reviewed-by: Boris Brezillon <boris.brezil...@collabora.com>

> ---
> 
>  drivers/gpu/drm/panel/panel-simple.c | 109 +++++++++++++++++++++++++--
>  1 file changed, 104 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/gpu/drm/panel/panel-simple.c 
> b/drivers/gpu/drm/panel/panel-simple.c
> index 9e8218f6a3f2..ad4f4aac2d44 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 {
> @@ -91,6 +92,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)
> @@ -98,16 +101,13 @@ static inline struct panel_simple 
> *to_panel_simple(struct drm_panel *panel)
>       return container_of(panel, struct panel_simple, base);
>  }
>  
> -static int panel_simple_get_fixed_modes(struct panel_simple *panel)
> +static unsigned int panel_simple_get_timings_modes(struct panel_simple 
> *panel)
>  {
>       struct drm_connector *connector = panel->base.connector;
>       struct drm_device *drm = panel->base.drm;
>       struct drm_display_mode *mode;
>       unsigned int i, num = 0;
>  
> -     if (!panel->desc)
> -             return 0;
> -
>       for (i = 0; i < panel->desc->num_timings; i++) {
>               const struct display_timing *dt = &panel->desc->timings[i];
>               struct videomode vm;
> @@ -131,6 +131,16 @@ static int panel_simple_get_fixed_modes(struct 
> panel_simple *panel)
>               num++;
>       }
>  
> +     return num;
> +}
> +
> +static unsigned 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;
> +     unsigned int i, num = 0;
> +
>       for (i = 0; i < panel->desc->num_modes; i++) {
>               const struct drm_display_mode *m = &panel->desc->modes[i];
>  
> @@ -152,6 +162,44 @@ static int panel_simple_get_fixed_modes(struct 
> panel_simple *panel)
>               num++;
>       }
>  
> +     return num;
> +}
> +
> +static int panel_simple_get_non_edid_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 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 = 1;
> +             } else {
> +                     dev_err(drm->dev, "failed to add override mode\n");
> +             }
> +     }
> +
> +     /* Only add timings if override was not there or failed to validate */
> +     if (num == 0 && panel->desc->num_timings)
> +             num = panel_simple_get_timings_modes(panel);
> +
> +     /*
> +      * Only add fixed modes if timings/override added no mode.
> +      *
> +      * We should only ever have either the display timings specified
> +      * or a fixed mode. Anything else is rather bogus.
> +      */
> +     WARN_ON(panel->desc->num_timings && panel->desc->num_modes);
> +     if (num == 0)
> +             num = panel_simple_get_fixed_modes(panel);
> +
>       connector->display_info.bpc = panel->desc->bpc;
>       connector->display_info.width_mm = panel->desc->size.width;
>       connector->display_info.height_mm = panel->desc->size.height;
> @@ -268,7 +316,7 @@ static int panel_simple_get_modes(struct drm_panel *panel)
>       }
>  
>       /* add hard-coded panel modes */
> -     num += panel_simple_get_fixed_modes(p);
> +     num += panel_simple_get_non_edid_modes(p);
>  
>       return num;
>  }
> @@ -299,10 +347,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_parse_override_mode(struct device *dev,
> +                                          struct panel_simple *panel,
> +                                          const struct display_timing *ot)
> +{
> +     const struct panel_desc *desc = panel->desc;
> +     struct videomode vm;
> +     unsigned int i;
> +
> +     if (WARN_ON(desc->num_modes)) {
> +             dev_err(dev, "Reject override mode: panel has a fixed mode\n");
> +             return;
> +     }
> +     if (WARN_ON(!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;
> +             break;
> +     }
> +
> +     if (WARN_ON(!panel->override_mode.type))
> +             dev_err(dev, "Reject override mode: No display_timing found\n");
> +}
> +
>  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);
> @@ -348,6 +444,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_parse_override_mode(dev, panel, &dt);
> +
>       drm_panel_init(&panel->base);
>       panel->base.dev = dev;
>       panel->base.funcs = &panel_simple_funcs;

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to