Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | // 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; } |