diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.c b/hw/xfree86/drivers/modesetting/drmmode_display.c index 67b225ca5..30254653b 100644 --- a/hw/xfree86/drivers/modesetting/drmmode_display.c +++ b/hw/xfree86/drivers/modesetting/drmmode_display.c @@ -2896,42 +2896,90 @@ drmmode_output_add_gtf_modes(xf86OutputPtr output, DisplayModePtr Modes) return xf86ModesAdd(Modes, m); } +static inline void +drmmode_clear_edid_cache(drmmode_output_private_ptr drmmode_output) +{ + /* Free old cached data */ + if (drmmode_output->cached_edid) { + free(drmmode_output->cached_edid); + drmmode_output->cached_edid = NULL; + } + + if (drmmode_output->cached_edid_data) { + free(drmmode_output->cached_edid_data); + drmmode_output->cached_edid_data = NULL; + drmmode_output->cached_edid_length = 0; + } +} + static DisplayModePtr drmmode_output_get_modes(xf86OutputPtr output) { drmmode_output_private_ptr drmmode_output = output->driver_private; drmModeConnectorPtr koutput = drmmode_output->mode_output; drmmode_ptr drmmode = drmmode_output->drmmode; + Bool edid_changed = FALSE; int i; DisplayModePtr Modes = NULL, Mode; xf86MonPtr mon = NULL; + drmModePropertyBlobPtr current_blob; if (!koutput) return NULL; + /* Free previous EDID blob from last call */ drmModeFreePropertyBlob(drmmode_output->edid_blob); + drmmode_output->edid_blob = NULL; + + current_blob = koutput_get_prop_blob(drmmode->fd, koutput, "EDID"); + if (current_blob) { + /* Check if EDID actually changed by comparing raw data */ + if (drmmode_output->cached_edid_data && + current_blob->length == drmmode_output->cached_edid_length && + memcmp(current_blob->data, drmmode_output->cached_edid_data, + current_blob->length) == 0) { + mon = drmmode_output->cached_edid; + drmmode_output->edid_blob = current_blob; + } else { + edid_changed = TRUE; + + mon = xf86InterpretEDID(output->scrn->scrnIndex, + current_blob->data); + if (mon && current_blob->length > 128) + mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA; + + /* Free old cached data */ + drmmode_clear_edid_cache(drmmode_output); + + /* Cache the new EDID and its raw data */ + drmmode_output->cached_edid = mon; + drmmode_output->cached_edid_data = malloc(current_blob->length); + if (drmmode_output->cached_edid_data) { + memcpy(drmmode_output->cached_edid_data, + current_blob->data, + current_blob->length); + drmmode_output->cached_edid_length = current_blob->length; + } else { + drmmode_output->cached_edid_length = 0; + } - /* look for an EDID property */ - drmmode_output->edid_blob = - koutput_get_prop_blob(drmmode->fd, koutput, "EDID"); + drmmode_output->edid_blob = current_blob; + } + } else { + return NULL; + } - if (drmmode_output->edid_blob) { - mon = xf86InterpretEDID(output->scrn->scrnIndex, - drmmode_output->edid_blob->data); - if (mon && drmmode_output->edid_blob->length > 128) - mon->flags |= MONITOR_EDID_COMPLETE_RAWDATA; + /* Only call xf86OutputSetEDID if EDID actually changed */ + if (edid_changed) { + xf86OutputSetEDID(output, mon); } - xf86OutputSetEDID(output, mon); drmmode_output_attach_tile(output); - /* modes should already be available */ for (i = 0; i < koutput->count_modes; i++) { Mode = XNFalloc(sizeof(DisplayModeRec)); - drmmode_ConvertFromKMode(output->scrn, &koutput->modes[i], Mode); Modes = xf86ModesAdd(Modes, Mode); - } return drmmode_output_add_gtf_modes(output, Modes); @@ -2946,6 +2994,8 @@ drmmode_output_destroy(xf86OutputPtr output) drmModeFreePropertyBlob(drmmode_output->edid_blob); drmModeFreePropertyBlob(drmmode_output->tile_blob); + drmmode_clear_edid_cache(drmmode_output); + for (i = 0; i < drmmode_output->num_props; i++) { drmModeFreeProperty(drmmode_output->props[i].mode_prop); free(drmmode_output->props[i].atoms); diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.h b/hw/xfree86/drivers/modesetting/drmmode_display.h index b020592f3..0965bc40e 100644 --- a/hw/xfree86/drivers/modesetting/drmmode_display.h +++ b/hw/xfree86/drivers/modesetting/drmmode_display.h @@ -266,6 +266,10 @@ typedef struct { xf86CrtcPtr current_crtc; Atom ctm_atom; struct drm_color_ctm ctm; + + xf86MonPtr cached_edid; /* Cached parsed EDID */ + void* cached_edid_data; /* Cached raw EDID bytes */ + uint32_t cached_edid_length; /* Length of cached raw data */ } drmmode_output_private_rec, *drmmode_output_private_ptr; typedef struct {