Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4cf2b281 authored by Thomas Wood's avatar Thomas Wood Committed by Daniel Vetter
Browse files

drm/debugfs: add an "edid_override" file per connector



Add a file to debugfs for each connector to allow the EDID to be
overridden.

v2: Copy ubuf before accessing it and reject invalid length data. (David
    Herrmann)
    Ensure override_edid is reset when a new EDID value is written.
    (David Herrmann)
    Fix the debugfs file permissions. (David Herrmann)

Signed-off-by: default avatarThomas Wood <thomas.wood@intel.com>
Reviewed-by: default avatarAlex Deucher <alexander.deucher@amd.com>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent 30f65707
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -3763,6 +3763,10 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector,
	struct drm_device *dev = connector->dev;
	int ret, size;

	/* ignore requests to set edid when overridden */
	if (connector->override_edid)
		return 0;

	if (connector->edid_blob_ptr)
		drm_property_destroy_blob(dev, connector->edid_blob_ptr);

+68 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@
#include <linux/slab.h>
#include <linux/export.h>
#include <drm/drmP.h>
#include <drm/drm_edid.h>

#if defined(CONFIG_DEBUG_FS)

@@ -304,6 +305,67 @@ static ssize_t connector_write(struct file *file, const char __user *ubuf,
	return len;
}

static int edid_show(struct seq_file *m, void *data)
{
	struct drm_connector *connector = m->private;
	struct drm_property_blob *edid = connector->edid_blob_ptr;

	if (connector->override_edid && edid)
		seq_write(m, edid->data, edid->length);

	return 0;
}

static int edid_open(struct inode *inode, struct file *file)
{
	struct drm_connector *dev = inode->i_private;

	return single_open(file, edid_show, dev);
}

static ssize_t edid_write(struct file *file, const char __user *ubuf,
			  size_t len, loff_t *offp)
{
	struct seq_file *m = file->private_data;
	struct drm_connector *connector = m->private;
	char *buf;
	struct edid *edid;
	int ret;

	buf = memdup_user(ubuf, len);
	if (IS_ERR(buf))
		return PTR_ERR(buf);

	edid = (struct edid *) buf;

	if (len == 5 && !strncmp(buf, "reset", 5)) {
		connector->override_edid = false;
		ret = drm_mode_connector_update_edid_property(connector, NULL);
	} else if (len < EDID_LENGTH ||
		   EDID_LENGTH * (1 + edid->extensions) > len)
		ret = -EINVAL;
	else {
		connector->override_edid = false;
		ret = drm_mode_connector_update_edid_property(connector, edid);
		if (!ret)
			connector->override_edid = true;
	}

	kfree(buf);

	return (ret) ? ret : len;
}

static const struct file_operations drm_edid_fops = {
	.owner = THIS_MODULE,
	.open = edid_open,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
	.write = edid_write
};


static const struct file_operations drm_connector_fops = {
	.owner = THIS_MODULE,
	.open = connector_open,
@@ -333,6 +395,12 @@ int drm_debugfs_connector_add(struct drm_connector *connector)
	if (!ent)
		goto error;

	/* edid */
	ent = debugfs_create_file("edid_override", S_IRUGO | S_IWUSR, root,
				  connector, &drm_edid_fops);
	if (!ent)
		goto error;

	return 0;

error:
+8 −1
Original line number Diff line number Diff line
@@ -130,7 +130,14 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
	count = drm_load_edid_firmware(connector);
	if (count == 0)
#endif
	{
		if (connector->override_edid) {
			struct edid *edid = (struct edid *) connector->edid_blob_ptr->data;

			count = drm_add_edid_modes(connector, edid);
		} else
			count = (*connector_funcs->get_modes)(connector);
	}

	if (count == 0 && connector->status == connector_status_connected)
		count = drm_add_modes_noedid(connector, 1024, 768);
+1 −0
Original line number Diff line number Diff line
@@ -533,6 +533,7 @@ struct drm_connector {

	/* forced on connector */
	enum drm_connector_force force;
	bool override_edid;
	uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER];
	struct drm_encoder *encoder; /* currently active encoder */