|
We have an issue when the KMS EDID is either not valid or does not
accuratly describe the attached monitor. This happens with broken monitors and also with KVM devices which often lie about the attached monitor when you request EDID and you are not being displayed. In these situations it would be nice to be able to take corrective action. Before KMS took over display initialisation the X server allowed the user to specifify an alternative EDID which it would use instead of the one loaded from the monitor. The patch attempts to provide the kernel component of such a mechanism in the KMS world. This patch allows us to specify new EDID files which may then overwrite the auto detected EDID in KMS. As loading an EDID is potentially dangerous (indeed with old monitors you might load up and EDID which causes us to blow up the monitor) we do not allow arbitrary files to be loaded, we limit the files to those found in the firmware directory edid/. The load is triggered by echoing the name of a replacement edid file into the existing edid file in /sys. This will use the firmware loader to load the new binary data, and then switch to it. It is envisioned that this would be utilised by some udev support. For example you could perhaps use system board ids to trigger automatic loading, or perhaps the md5sum of the existing EDID where this is always known to be wrong. This patch is a prerequisite for this kind of support. This fix is not yet upstream, I am going to be tackling that next. However it is a very self contained change, and is primarily intended to be used by userspace support which we will define such that if the interface changes it will be hidden from the user; I do not intend to guarentee this interface API. I am considering this a bug fix for those broken EDID scenarios which we used to be able to fix in X and no longer can. Finally some history, I have taken this patch and sanitised it for our use, particularly limiting its reach to the edid directory in the firmware directories. This is both for safety and also as it would allow that directory to be shipped in a separate package from the X team in the future. Proposing for Precise (post Beta 2 please). -apw Carsten Emde (1): drm/edid: allow to load edid firmware drivers/gpu/drm/drm_crtc_helper.c | 7 ++++ drivers/gpu/drm/drm_edid.c | 5 +++ drivers/gpu/drm/drm_sysfs.c | 66 ++++++++++++++++++++++++++++++++++++- include/drm/drm_crtc.h | 1 + 4 files changed, 78 insertions(+), 1 deletions(-) -- 1.7.9.1 -- kernel-team mailing list [hidden email] https://lists.ubuntu.com/mailman/listinfo/kernel-team |
|
From: Carsten Emde <[hidden email]>
Use the firmware interface to load binary EDID data from a file and use them to assign monitor data to a video connector. EDID data is located in the edid/ directory within the existing firmware directories. [[hidden email]: follow the changes to the sysfs interfaces] [[hidden email]: fix the memory allocation so we use the real buffer] [[hidden email]: add a firware prefix edid/ to limit what may be loaded] Signed-off-by: Carsten Emde <[hidden email]> Signed-off-by: Andy Whitcroft <[hidden email]> --- drivers/gpu/drm/drm_crtc_helper.c | 7 ++++ drivers/gpu/drm/drm_edid.c | 5 +++ drivers/gpu/drm/drm_sysfs.c | 66 ++++++++++++++++++++++++++++++++++++- include/drm/drm_crtc.h | 1 + 4 files changed, 78 insertions(+), 1 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index d2619d7..8dd1680 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -94,6 +94,13 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, drm_get_connector_name(connector)); + + if (connector->edid_pinned) { + list_for_each_entry(mode, &connector->modes, head) + count++; + return count; + } + /* set all modes to the unverified state */ list_for_each_entry_safe(mode, t, &connector->modes, head) mode->status = MODE_UNVERIFIED; diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index fb6c26c..1944fd5 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -385,6 +385,11 @@ struct edid *drm_get_edid(struct drm_connector *connector, { struct edid *edid = NULL; + if (connector->edid_pinned) { + edid = (struct edid *) connector->display_info.raw_edid; + return edid; + } + if (drm_probe_ddc(adapter)) edid = (struct edid *)drm_do_get_edid(connector, adapter); diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 0f9ef9b..c5c875d 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -17,9 +17,11 @@ #include <linux/gfp.h> #include <linux/err.h> #include <linux/export.h> +#include <linux/firmware.h> #include "drm_sysfs.h" #include "drm_core.h" +#include "drm_edid.h" #include "drmP.h" #define to_drm_minor(d) container_of(d, struct drm_minor, kdev) @@ -228,6 +230,67 @@ static ssize_t edid_show(struct file *filp, struct kobject *kobj, return count; } +#define EDID_FIRMWARE_PREFIX "edid/" + +static ssize_t edid_store(struct file *file, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, size_t count) +{ + struct device *connector_dev = container_of(kobj, struct device, kobj); + struct drm_connector *connector = to_drm_connector(connector_dev); + const struct firmware *fw; + unsigned char *edid; + size_t size = EDID_LENGTH; + int status; + char *filename, *cr; + + if (count == 0 || *buf == '\n' || *buf == '\0') { + connector->edid_pinned = 0; + return count; + } + + filename = kmalloc(sizeof(EDID_FIRMWARE_PREFIX) + count, GFP_KERNEL); + if (!filename) + return -ENOMEM; + strcpy(filename, EDID_FIRMWARE_PREFIX); + memcpy(filename + sizeof(EDID_FIRMWARE_PREFIX) - 1, buf, count); + filename[sizeof(EDID_FIRMWARE_PREFIX) + count] = '\0'; + + cr = strchr(filename, '\n'); + if (cr) + *cr = '\0'; + + status = request_firmware(&fw, filename, connector_dev); + kfree(filename); + if (status) + return status; + + if (fw->size != size) { + release_firmware(fw); + return -EINVAL; + } + + edid = kmalloc(size, GFP_KERNEL); + if (edid == NULL) { + release_firmware(fw); + return -ENOMEM; + } + + memcpy(edid, fw->data, size); + + drm_mode_connector_update_edid_property(connector, + (struct edid *) edid); + drm_add_edid_modes(connector, (struct edid *) edid); + drm_mode_connector_list_update(connector); + drm_mode_sort(&connector->modes); + + connector->display_info.raw_edid = edid; + connector->edid_pinned = 1; + + release_firmware(fw); + + return count; +} + static ssize_t modes_show(struct device *device, struct device_attribute *attr, char *buf) @@ -341,9 +404,10 @@ static struct device_attribute connector_attrs_opt1[] = { static struct bin_attribute edid_attr = { .attr.name = "edid", - .attr.mode = 0444, + .attr.mode = 0644, .size = 0, .read = edid_show, + .write = edid_store, }; /** diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 8020798..96d6e18 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -509,6 +509,7 @@ struct drm_connector { struct list_head user_modes; struct drm_property_blob *edid_blob_ptr; + int edid_pinned; u32 property_ids[DRM_CONNECTOR_MAX_PROPERTY]; uint64_t property_values[DRM_CONNECTOR_MAX_PROPERTY]; -- 1.7.9.1 -- kernel-team mailing list [hidden email] https://lists.ubuntu.com/mailman/listinfo/kernel-team |
|
In reply to this post by Andy Whitcroft-3
Ok IGNORE this for now, it seems upstream has _just_ merged an
alternative patch set for this very thing. If we pull anything it needs to be interface compatible with that. -apw -- kernel-team mailing list [hidden email] https://lists.ubuntu.com/mailman/listinfo/kernel-team |
| Powered by Nabble | Edit this page |
