Dave, >> Some monitors and, more often, kvm switches do not provide working >> EDID data. KMS based X configurations in recent server versions no >> longer allow to override the screen resolution which makes it >> impossible to correctly adapt the screen resolution to the monitor >> in use. This patch allows to define the fallback screen resolution >> to something else than the default 1024x768. > Does video=800x600 not work for you? I did a lot more tests; apparently, things are even more difficult than expected. These are my observations (all with Radeon X1950 and radeon driver): - On the monitor side: In addition to the known quirks, the EDID data may not be accessible or erroneous. The original patch only solved the situation of unavailable EDID data. A frequent reason of erroneous or unavailable EDID data is the use of suboptimally designed KVM switches that are unable to probe the connected monitor and send no or fantasy EDID data. - On the Xorg side: Older radeon drivers recognize the options "IgnoreEDID" and "NoDDC". Using this trick, Xorg can be forced to disregard any EDID data and use the resolution as specified in xorg.conf. Less old radeon drivers still recognize the option "IgnoreEDID", but this does not work any longer, since the option "NoDDC" has been abandoned. Such drivers rely on correct EDID data. Current radeon drivers should have the option "CustomEDID" to override the probed EDID data (at least this is what the manual says), but I couldn't get it working. The log file insists on saying that the option "CustomEDID" is not used. These drivers also rely on correct EDID data. My conclusion is, that the right place to solve the situation is in the kernel. The attached patch makes the sysfs edid entry writable. If the name of an existing binary EDID data file in /lib/firmware is supplied such as cd /sys/devices/pci0000:00/0000:00:07.0/0000:05:00.0/drm/card0 echo dvi_0.bin >card0-DVI-I-1/edid the related connector will be set to these EDID data. Any probing is then disabled. To restore the previous behavior and to re-enable monitor probing, an empty string or a single line delimiter must be sent: echo >card0-DVI-I-1/edid I have tested the patch successfully in a number of different configurations. When userspace setup tools such as gnome-display-properties are used, the name and resolution data of the provided (fake) EDID data are correctly decoded. When compared to the various X hacks I tried, I find this solution better, since it solves the problem at a place where it belongs. This is in line with the observation that EDID related functionality appears to fade out of X drivers. Does this make sense? Carsten.
Use the firmware interface to load binary EDID data from a file and use them to assign monitor data to a video connector. Signed-off-by: Carsten Emde <C.Emde@xxxxxxxxx> --- drivers/gpu/drm/drm_crtc_helper.c | 7 ++++ drivers/gpu/drm/drm_edid.c | 5 +++ drivers/gpu/drm/drm_sysfs.c | 63 +++++++++++++++++++++++++++++++++++++- include/drm/drm_crtc.h | 1 4 files changed, 75 insertions(+), 1 deletion(-) Index: linux-2.6.34/drivers/gpu/drm/drm_crtc_helper.c =================================================================== --- linux-2.6.34.orig/drivers/gpu/drm/drm_crtc_helper.c +++ linux-2.6.34/drivers/gpu/drm/drm_crtc_helper.c @@ -87,6 +87,13 @@ int drm_helper_probe_single_connector_mo int mode_flags = 0; DRM_DEBUG_KMS("%s\n", 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; Index: linux-2.6.34/drivers/gpu/drm/drm_edid.c =================================================================== --- linux-2.6.34.orig/drivers/gpu/drm/drm_edid.c +++ linux-2.6.34/drivers/gpu/drm/drm_edid.c @@ -1214,6 +1214,11 @@ struct edid *drm_get_edid(struct drm_con int ret; struct edid *edid; + if (connector->edid_pinned) { + edid = (struct edid *) connector->display_info.raw_edid; + goto end; + } + edid = kmalloc(EDID_LENGTH * (DRM_MAX_EDID_EXT_NUM + 1), GFP_KERNEL); if (edid == NULL) { Index: linux-2.6.34/drivers/gpu/drm/drm_sysfs.c =================================================================== --- linux-2.6.34.orig/drivers/gpu/drm/drm_sysfs.c +++ linux-2.6.34/drivers/gpu/drm/drm_sysfs.c @@ -16,9 +16,11 @@ #include <linux/kdev_t.h> #include <linux/gfp.h> #include <linux/err.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) @@ -219,6 +221,64 @@ static ssize_t edid_show(struct kobject return count; } +static ssize_t edid_store(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(count+1, GFP_KERNEL); + if (!filename) + return -ENOMEM; + memcpy(filename, buf, count); + filename[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; + } + + edid = (unsigned char *) fw->data; + + 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) @@ -332,9 +392,10 @@ static struct device_attribute connector static struct bin_attribute edid_attr = { .attr.name = "edid", - .attr.mode = 0444, + .attr.mode = 0644, .size = 128, .read = edid_show, + .write = edid_store, }; /** Index: linux-2.6.34/include/drm/drm_crtc.h =================================================================== --- linux-2.6.34.orig/include/drm/drm_crtc.h +++ linux-2.6.34/include/drm/drm_crtc.h @@ -508,6 +508,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];
_______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel