Contributors: 12
Author Tokens Token Proportion Commits Commit Proportion
Carsten Emde 209 47.83% 1 5.00%
Jani Nikula 95 21.74% 9 45.00%
Bob Paauwe 80 18.31% 1 5.00%
Chris Wilson 15 3.43% 1 5.00%
Gen Zhang 12 2.75% 1 5.00%
Dave Airlie 11 2.52% 1 5.00%
Maxime Ripard 8 1.83% 1 5.00%
Jérôme Glisse 2 0.46% 1 5.00%
Thomas Gleixner 2 0.46% 1 5.00%
Thomas Meyer 1 0.23% 1 5.00%
Valdis Kletnieks 1 0.23% 1 5.00%
Linus Torvalds 1 0.23% 1 5.00%
Total 437 20


// SPDX-License-Identifier: GPL-2.0-or-later
/*
   drm_edid_load.c: use a built-in EDID data set or load it via the firmware
		    interface

   Copyright (C) 2012 Carsten Emde <C.Emde@osadl.org>

*/

#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/platform_device.h>

#include <drm/drm_connector.h>
#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
#include <drm/drm_print.h>

#include "drm_crtc_internal.h"

static char edid_firmware[PATH_MAX];
module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644);
MODULE_PARM_DESC(edid_firmware,
		 "Do not probe monitor, use specified EDID blob from /lib/firmware instead.");

static const struct drm_edid *edid_load(struct drm_connector *connector, const char *name)
{
	const struct firmware *fw = NULL;
	const struct drm_edid *drm_edid;
	int err;

	err = request_firmware(&fw, name, connector->dev->dev);
	if (err) {
		drm_err(connector->dev,
			"[CONNECTOR:%d:%s] Requesting EDID firmware \"%s\" failed (err=%d)\n",
			connector->base.id, connector->name,
			name, err);
		return ERR_PTR(err);
	}

	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Loaded external firmware EDID \"%s\"\n",
		    connector->base.id, connector->name, name);

	drm_edid = drm_edid_alloc(fw->data, fw->size);
	if (!drm_edid_valid(drm_edid)) {
		drm_err(connector->dev, "Invalid firmware EDID \"%s\"\n", name);
		drm_edid_free(drm_edid);
		drm_edid = ERR_PTR(-EINVAL);
	}

	release_firmware(fw);

	return drm_edid;
}

const struct drm_edid *drm_edid_load_firmware(struct drm_connector *connector)
{
	char *edidname, *last, *colon, *fwstr, *edidstr, *fallback = NULL;
	const struct drm_edid *drm_edid;

	if (edid_firmware[0] == '\0')
		return ERR_PTR(-ENOENT);

	/*
	 * If there are multiple edid files specified and separated
	 * by commas, search through the list looking for one that
	 * matches the connector.
	 *
	 * If there's one or more that doesn't specify a connector, keep
	 * the last one found one as a fallback.
	 */
	fwstr = kstrdup(edid_firmware, GFP_KERNEL);
	if (!fwstr)
		return ERR_PTR(-ENOMEM);
	edidstr = fwstr;

	while ((edidname = strsep(&edidstr, ","))) {
		colon = strchr(edidname, ':');
		if (colon != NULL) {
			if (strncmp(connector->name, edidname, colon - edidname))
				continue;
			edidname = colon + 1;
			break;
		}

		if (*edidname != '\0') /* corner case: multiple ',' */
			fallback = edidname;
	}

	if (!edidname) {
		if (!fallback) {
			kfree(fwstr);
			return ERR_PTR(-ENOENT);
		}
		edidname = fallback;
	}

	last = edidname + strlen(edidname) - 1;
	if (*last == '\n')
		*last = '\0';

	drm_edid = edid_load(connector, edidname);

	kfree(fwstr);

	return drm_edid;
}