drm/panel: simple: Add ability to override typical timing
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 Changes in v6: - Rebased to drm-misc next - Added tags Cc: Doug Anderson <dianders@chromium.org> Cc: Eric Anholt <eric@anholt.net> Cc: Heiko Stuebner <heiko@sntech.de> Cc: Jeffy Chen <jeffy.chen@rock-chips.com> Cc: Rob Herring <robh+dt@kernel.org> Cc: Stéphane Marchesin <marcheu@chromium.org> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: devicetree@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Signed-off-by: Sean Paul <seanpaul@chromium.org> Tested-by: Enric Balletbo i Serra <enric.balletbo@collabora.com> Signed-off-by: Douglas Anderson <dianders@chromium.org> Tested-by: Heiko Stuebner <heiko@sntech.de> Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com> Acked-by: Thierry Reding <thierry.reding@gmail.com> Signed-off-by: Sam Ravnborg <sam@ravnborg.org> Link: https://patchwork.freedesktop.org/patch/msgid/20190711203455.125667-2-dianders@chromium.org
This commit is contained in:
parent
a4e7e98e90
commit
b8a2948fa2
@ -30,6 +30,7 @@
|
|||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
|
|
||||||
#include <video/display_timing.h>
|
#include <video/display_timing.h>
|
||||||
|
#include <video/of_display_timing.h>
|
||||||
#include <video/videomode.h>
|
#include <video/videomode.h>
|
||||||
|
|
||||||
#include <drm/drm_crtc.h>
|
#include <drm/drm_crtc.h>
|
||||||
@ -92,6 +93,8 @@ struct panel_simple {
|
|||||||
struct i2c_adapter *ddc;
|
struct i2c_adapter *ddc;
|
||||||
|
|
||||||
struct gpio_desc *enable_gpio;
|
struct gpio_desc *enable_gpio;
|
||||||
|
|
||||||
|
struct drm_display_mode override_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct panel_simple *to_panel_simple(struct drm_panel *panel)
|
static inline struct panel_simple *to_panel_simple(struct drm_panel *panel)
|
||||||
@ -99,16 +102,13 @@ static inline struct panel_simple *to_panel_simple(struct drm_panel *panel)
|
|||||||
return container_of(panel, struct panel_simple, base);
|
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_connector *connector = panel->base.connector;
|
||||||
struct drm_device *drm = panel->base.drm;
|
struct drm_device *drm = panel->base.drm;
|
||||||
struct drm_display_mode *mode;
|
struct drm_display_mode *mode;
|
||||||
unsigned int i, num = 0;
|
unsigned int i, num = 0;
|
||||||
|
|
||||||
if (!panel->desc)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (i = 0; i < panel->desc->num_timings; i++) {
|
for (i = 0; i < panel->desc->num_timings; i++) {
|
||||||
const struct display_timing *dt = &panel->desc->timings[i];
|
const struct display_timing *dt = &panel->desc->timings[i];
|
||||||
struct videomode vm;
|
struct videomode vm;
|
||||||
@ -132,6 +132,16 @@ static int panel_simple_get_fixed_modes(struct panel_simple *panel)
|
|||||||
num++;
|
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++) {
|
for (i = 0; i < panel->desc->num_modes; i++) {
|
||||||
const struct drm_display_mode *m = &panel->desc->modes[i];
|
const struct drm_display_mode *m = &panel->desc->modes[i];
|
||||||
|
|
||||||
@ -153,6 +163,44 @@ static int panel_simple_get_fixed_modes(struct panel_simple *panel)
|
|||||||
num++;
|
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.bpc = panel->desc->bpc;
|
||||||
connector->display_info.width_mm = panel->desc->size.width;
|
connector->display_info.width_mm = panel->desc->size.width;
|
||||||
connector->display_info.height_mm = panel->desc->size.height;
|
connector->display_info.height_mm = panel->desc->size.height;
|
||||||
@ -269,7 +317,7 @@ static int panel_simple_get_modes(struct drm_panel *panel)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* add hard-coded panel modes */
|
/* add hard-coded panel modes */
|
||||||
num += panel_simple_get_fixed_modes(p);
|
num += panel_simple_get_non_edid_modes(p);
|
||||||
|
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
@ -300,10 +348,58 @@ static const struct drm_panel_funcs panel_simple_funcs = {
|
|||||||
.get_timings = panel_simple_get_timings,
|
.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)
|
static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
|
||||||
{
|
{
|
||||||
struct device_node *backlight, *ddc;
|
struct device_node *backlight, *ddc;
|
||||||
struct panel_simple *panel;
|
struct panel_simple *panel;
|
||||||
|
struct display_timing dt;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
|
panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
|
||||||
@ -349,6 +445,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);
|
drm_panel_init(&panel->base);
|
||||||
panel->base.dev = dev;
|
panel->base.dev = dev;
|
||||||
panel->base.funcs = &panel_simple_funcs;
|
panel->base.funcs = &panel_simple_funcs;
|
||||||
|
Loading…
Reference in New Issue
Block a user