@@ -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;
@@ -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);
@@ -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,
};
/**
@@ -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];