// 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 */ #include #include #include #include #include #include #include #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; }