1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 drm_edid_load.c: use a built-in EDID data set or load it via the firmware 4 interface 5 6 Copyright (C) 2012 Carsten Emde <C.Emde@osadl.org> 7 8 */ 9 10 #include <linux/firmware.h> 11 #include <linux/module.h> 12 #include <linux/platform_device.h> 13 14 #include <drm/drm_connector.h> 15 #include <drm/drm_drv.h> 16 #include <drm/drm_edid.h> 17 #include <drm/drm_print.h> 18 19 #include "drm_crtc_internal.h" 20 21 static char edid_firmware[PATH_MAX]; 22 module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644); 23 MODULE_PARM_DESC(edid_firmware, 24 "Do not probe monitor, use specified EDID blob from /lib/firmware instead."); 25 26 static const struct drm_edid *edid_load(struct drm_connector *connector, const char *name) 27 { 28 const struct firmware *fw = NULL; 29 const struct drm_edid *drm_edid; 30 int err; 31 32 err = request_firmware(&fw, name, connector->dev->dev); 33 if (err) { 34 drm_err(connector->dev, 35 "[CONNECTOR:%d:%s] Requesting EDID firmware \"%s\" failed (err=%d)\n", 36 connector->base.id, connector->name, 37 name, err); 38 return ERR_PTR(err); 39 } 40 41 drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Loaded external firmware EDID \"%s\"\n", 42 connector->base.id, connector->name, name); 43 44 drm_edid = drm_edid_alloc(fw->data, fw->size); 45 if (!drm_edid_valid(drm_edid)) { 46 drm_err(connector->dev, "Invalid firmware EDID \"%s\"\n", name); 47 drm_edid_free(drm_edid); 48 drm_edid = ERR_PTR(-EINVAL); 49 } 50 51 release_firmware(fw); 52 53 return drm_edid; 54 } 55 56 const struct drm_edid *drm_edid_load_firmware(struct drm_connector *connector) 57 { 58 char *edidname, *last, *colon, *fwstr, *edidstr, *fallback = NULL; 59 const struct drm_edid *drm_edid; 60 61 if (edid_firmware[0] == '\0') 62 return ERR_PTR(-ENOENT); 63 64 /* 65 * If there are multiple edid files specified and separated 66 * by commas, search through the list looking for one that 67 * matches the connector. 68 * 69 * If there's one or more that doesn't specify a connector, keep 70 * the last one found one as a fallback. 71 */ 72 fwstr = kstrdup(edid_firmware, GFP_KERNEL); 73 if (!fwstr) 74 return ERR_PTR(-ENOMEM); 75 edidstr = fwstr; 76 77 while ((edidname = strsep(&edidstr, ","))) { 78 colon = strchr(edidname, ':'); 79 if (colon != NULL) { 80 if (strncmp(connector->name, edidname, colon - edidname)) 81 continue; 82 edidname = colon + 1; 83 break; 84 } 85 86 if (*edidname != '\0') /* corner case: multiple ',' */ 87 fallback = edidname; 88 } 89 90 if (!edidname) { 91 if (!fallback) { 92 kfree(fwstr); 93 return ERR_PTR(-ENOENT); 94 } 95 edidname = fallback; 96 } 97 98 last = edidname + strlen(edidname) - 1; 99 if (*last == '\n') 100 *last = '\0'; 101 102 drm_edid = edid_load(connector, edidname); 103 104 kfree(fwstr); 105 106 return drm_edid; 107 } 108