[RFC] Re: [PATCH 0/1] drm-set-default-noedid-res.patch

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Linux DRI Users]     [Linux Intel Graphics]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux