Skip to content

Commit

Permalink
drm: Update edid-derived drm_display_info fields at edid property set…
Browse files Browse the repository at this point in the history
… [v2]

There are a set of values in the drm_display_info structure for each
connector which hold information derived from EDID. These are computed
in drm_add_display_info. Before this patch, that was only called in
drm_add_edid_modes. This meant that they were only set when EDID was
present and never reset when EDID was not, as happened when the
display was disconnected.

One of these fields, non_desktop, is used from
drm_mode_connector_update_edid_property, the function responsible for
assigning the new edid value to the application-visible property.

Various drivers call these two functions (drm_add_edid_modes and
drm_mode_connector_update_edid_property) in different orders. This
means that even when EDID is present, the drm_display_info fields may
not have been computed at the time that
drm_mode_connector_update_edid_property used the non_desktop value to
set the non_desktop property.

I've added a public function (drm_reset_display_info) that resets the
drm_display_info field values to default values and then made the
drm_add_display_info function public. These two functions are now
called directly from drm_mode_connector_update_edid_property so that
the drm_display_info fields are always computed from the current EDID
information before being used in that function.

This means that the drm_display_info values are often computed twice,
once when the EDID property it set and a second time when EDID is used
to compute modes for the device. The alternative would be to uniformly
ensure that the values were computed once before being used, which
would require that all drivers reliably invoke the two paths in the
same order. The computation is inexpensive enough that it seems more
maintainable in the long term to simply compute them in both paths.

The API to drm_add_display_info has been changed so that it no longer
takes the set of edid-based quirks as a parameter. Rather, it now
computes those quirks itself and returns them for further use by
drm_add_edid_modes.

This patch also includes a number of 'const' additions caused by
drm_mode_connector_update_edid_property taking a 'const struct edid *'
parameter and wanting to pass that along to drm_add_display_info.

v2: after review by Daniel Vetter <daniel.vetter@ffwll.ch>

	Removed EXPORT_SYMBOL_GPL for drm_reset_display_info and
	drm_add_display_info.

	Added FIXME in drm_mode_connector_update_edid_property about
	potentially merging that with drm_add_edid_modes to avoid
	the need for two driver calls.

Signed-off-by: Keith Packard <keithp@keithp.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20171213084427.31199-1-keithp@keithp.com
(danvet: cherry picked from commit 12a889bf4bca ("drm: rework delayed
connector cleanup in connector_iter") from drm-misc-next since
functional conflict with changes in -next and we need to make sure
both have the right version and nothing gets lost.)
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
  • Loading branch information
Keith Packard authored and Daniel Vetter committed Dec 13, 2017
1 parent babc811 commit 4b4df57
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 14 deletions.
13 changes: 13 additions & 0 deletions drivers/gpu/drm/drm_connector.c
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,19 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector,
if (edid)
size = EDID_LENGTH * (1 + edid->extensions);

/* Set the display info, using edid if available, otherwise
* reseting the values to defaults. This duplicates the work
* done in drm_add_edid_modes, but that function is not
* consistently called before this one in all drivers and the
* computation is cheap enough that it seems better to
* duplicate it rather than attempt to ensure some arbitrary
* ordering of calls.
*/
if (edid)
drm_add_display_info(connector, edid);
else
drm_reset_display_info(connector);

drm_object_property_set_value(&connector->base,
dev->mode_config.non_desktop_property,
connector->display_info.non_desktop);
Expand Down
52 changes: 38 additions & 14 deletions drivers/gpu/drm/drm_edid.c
Original file line number Diff line number Diff line change
Expand Up @@ -1731,7 +1731,7 @@ EXPORT_SYMBOL(drm_edid_duplicate);
*
* Returns true if @vendor is in @edid, false otherwise
*/
static bool edid_vendor(struct edid *edid, const char *vendor)
static bool edid_vendor(const struct edid *edid, const char *vendor)
{
char edid_vendor[3];

Expand All @@ -1749,7 +1749,7 @@ static bool edid_vendor(struct edid *edid, const char *vendor)
*
* This tells subsequent routines what fixes they need to apply.
*/
static u32 edid_get_quirks(struct edid *edid)
static u32 edid_get_quirks(const struct edid *edid)
{
const struct edid_quirk *quirk;
int i;
Expand Down Expand Up @@ -2813,7 +2813,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
/*
* Search EDID for CEA extension block.
*/
static u8 *drm_find_edid_extension(struct edid *edid, int ext_id)
static u8 *drm_find_edid_extension(const struct edid *edid, int ext_id)
{
u8 *edid_ext = NULL;
int i;
Expand All @@ -2835,12 +2835,12 @@ static u8 *drm_find_edid_extension(struct edid *edid, int ext_id)
return edid_ext;
}

static u8 *drm_find_cea_extension(struct edid *edid)
static u8 *drm_find_cea_extension(const struct edid *edid)
{
return drm_find_edid_extension(edid, CEA_EXT);
}

static u8 *drm_find_displayid_extension(struct edid *edid)
static u8 *drm_find_displayid_extension(const struct edid *edid)
{
return drm_find_edid_extension(edid, DISPLAYID_EXT);
}
Expand Down Expand Up @@ -4363,7 +4363,7 @@ drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db)
}

static void drm_parse_cea_ext(struct drm_connector *connector,
struct edid *edid)
const struct edid *edid)
{
struct drm_display_info *info = &connector->display_info;
const u8 *edid_ext;
Expand Down Expand Up @@ -4397,11 +4397,33 @@ static void drm_parse_cea_ext(struct drm_connector *connector,
}
}

static void drm_add_display_info(struct drm_connector *connector,
struct edid *edid, u32 quirks)
/* A connector has no EDID information, so we've got no EDID to compute quirks from. Reset
* all of the values which would have been set from EDID
*/
void
drm_reset_display_info(struct drm_connector *connector)
{
struct drm_display_info *info = &connector->display_info;

info->width_mm = 0;
info->height_mm = 0;

info->bpc = 0;
info->color_formats = 0;
info->cea_rev = 0;
info->max_tmds_clock = 0;
info->dvi_dual = false;

info->non_desktop = 0;
}
EXPORT_SYMBOL_GPL(drm_reset_display_info);

u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid)
{
struct drm_display_info *info = &connector->display_info;

u32 quirks = edid_get_quirks(edid);

info->width_mm = edid->width_cm * 10;
info->height_mm = edid->height_cm * 10;

Expand All @@ -4414,11 +4436,13 @@ static void drm_add_display_info(struct drm_connector *connector,

info->non_desktop = !!(quirks & EDID_QUIRK_NON_DESKTOP);

DRM_DEBUG_KMS("non_desktop set to %d\n", info->non_desktop);

if (edid->revision < 3)
return;
return quirks;

if (!(edid->input & DRM_EDID_INPUT_DIGITAL))
return;
return quirks;

drm_parse_cea_ext(connector, edid);

Expand All @@ -4438,7 +4462,7 @@ static void drm_add_display_info(struct drm_connector *connector,

/* Only defined for 1.4 with digital displays */
if (edid->revision < 4)
return;
return quirks;

switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) {
case DRM_EDID_DIGITAL_DEPTH_6:
Expand Down Expand Up @@ -4473,7 +4497,9 @@ static void drm_add_display_info(struct drm_connector *connector,
info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422)
info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
return quirks;
}
EXPORT_SYMBOL_GPL(drm_add_display_info);

static int validate_displayid(u8 *displayid, int length, int idx)
{
Expand Down Expand Up @@ -4627,14 +4653,12 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
return 0;
}

quirks = edid_get_quirks(edid);

/*
* CEA-861-F adds ycbcr capability map block, for HDMI 2.0 sinks.
* To avoid multiple parsing of same block, lets parse that map
* from sink info, before parsing CEA modes.
*/
drm_add_display_info(connector, edid, quirks);
quirks = drm_add_display_info(connector, edid);

/*
* EDID spec says modes should be preferred in this order:
Expand Down
2 changes: 2 additions & 0 deletions include/drm/drm_edid.h
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,8 @@ struct edid *drm_get_edid(struct drm_connector *connector,
struct edid *drm_get_edid_switcheroo(struct drm_connector *connector,
struct i2c_adapter *adapter);
struct edid *drm_edid_duplicate(const struct edid *edid);
void drm_reset_display_info(struct drm_connector *connector);
u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid);
int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);

u8 drm_match_cea_mode(const struct drm_display_mode *to_match);
Expand Down

0 comments on commit 4b4df57

Please sign in to comment.