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/module.h> 11 #include <linux/firmware.h> 12 #include <drm/drmP.h> 13 #include <drm/drm_crtc.h> 14 #include <drm/drm_crtc_helper.h> 15 #include <drm/drm_edid.h> 16 17 static char edid_firmware[PATH_MAX]; 18 module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644); 19 MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob " 20 "from built-in data or /lib/firmware instead. "); 21 22 /* Use only for backward compatibility with drm_kms_helper.edid_firmware */ 23 int __drm_set_edid_firmware_path(const char *path) 24 { 25 scnprintf(edid_firmware, sizeof(edid_firmware), "%s", path); 26 27 return 0; 28 } 29 EXPORT_SYMBOL(__drm_set_edid_firmware_path); 30 31 /* Use only for backward compatibility with drm_kms_helper.edid_firmware */ 32 int __drm_get_edid_firmware_path(char *buf, size_t bufsize) 33 { 34 return scnprintf(buf, bufsize, "%s", edid_firmware); 35 } 36 EXPORT_SYMBOL(__drm_get_edid_firmware_path); 37 38 #define GENERIC_EDIDS 6 39 static const char * const generic_edid_name[GENERIC_EDIDS] = { 40 "edid/800x600.bin", 41 "edid/1024x768.bin", 42 "edid/1280x1024.bin", 43 "edid/1600x1200.bin", 44 "edid/1680x1050.bin", 45 "edid/1920x1080.bin", 46 }; 47 48 static const u8 generic_edid[GENERIC_EDIDS][128] = { 49 { 50 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 51 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 52 0x05, 0x16, 0x01, 0x03, 0x6d, 0x1b, 0x14, 0x78, 53 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, 54 0x20, 0x50, 0x54, 0x01, 0x00, 0x00, 0x45, 0x40, 55 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 56 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xa0, 0x0f, 57 0x20, 0x00, 0x31, 0x58, 0x1c, 0x20, 0x28, 0x80, 58 0x14, 0x00, 0x15, 0xd0, 0x10, 0x00, 0x00, 0x1e, 59 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, 60 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, 61 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 62 0x3d, 0x24, 0x26, 0x05, 0x00, 0x0a, 0x20, 0x20, 63 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 64 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53, 65 0x56, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xc2, 66 }, 67 { 68 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 69 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 70 0x05, 0x16, 0x01, 0x03, 0x6d, 0x23, 0x1a, 0x78, 71 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, 72 0x20, 0x50, 0x54, 0x00, 0x08, 0x00, 0x61, 0x40, 73 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 74 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x64, 0x19, 75 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x08, 0x90, 76 0x36, 0x00, 0x63, 0x0a, 0x11, 0x00, 0x00, 0x18, 77 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, 78 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, 79 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 80 0x3d, 0x2f, 0x31, 0x07, 0x00, 0x0a, 0x20, 0x20, 81 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 82 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x58, 83 0x47, 0x41, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x55, 84 }, 85 { 86 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 87 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 88 0x05, 0x16, 0x01, 0x03, 0x6d, 0x2c, 0x23, 0x78, 89 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, 90 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0x81, 0x80, 91 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 92 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x2a, 93 0x00, 0x98, 0x51, 0x00, 0x2a, 0x40, 0x30, 0x70, 94 0x13, 0x00, 0xbc, 0x63, 0x11, 0x00, 0x00, 0x1e, 95 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, 96 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, 97 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 98 0x3d, 0x3e, 0x40, 0x0b, 0x00, 0x0a, 0x20, 0x20, 99 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 100 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53, 101 0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xa0, 102 }, 103 { 104 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 105 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 106 0x05, 0x16, 0x01, 0x03, 0x6d, 0x37, 0x29, 0x78, 107 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, 108 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xa9, 0x40, 109 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 110 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x48, 0x3f, 111 0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, 0xc0, 112 0x13, 0x00, 0x2b, 0xa0, 0x21, 0x00, 0x00, 0x1e, 113 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, 114 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, 115 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 116 0x3d, 0x4a, 0x4c, 0x11, 0x00, 0x0a, 0x20, 0x20, 117 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 118 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x55, 119 0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0x9d, 120 }, 121 { 122 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 123 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 124 0x05, 0x16, 0x01, 0x03, 0x6d, 0x2b, 0x1b, 0x78, 125 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, 126 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xb3, 0x00, 127 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 128 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x21, 0x39, 129 0x90, 0x30, 0x62, 0x1a, 0x27, 0x40, 0x68, 0xb0, 130 0x36, 0x00, 0xb5, 0x11, 0x11, 0x00, 0x00, 0x1e, 131 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, 132 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, 133 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 134 0x3d, 0x40, 0x42, 0x0f, 0x00, 0x0a, 0x20, 0x20, 135 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 136 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x57, 137 0x53, 0x58, 0x47, 0x41, 0x0a, 0x20, 0x00, 0x26, 138 }, 139 { 140 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 141 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 142 0x05, 0x16, 0x01, 0x03, 0x6d, 0x32, 0x1c, 0x78, 143 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, 144 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xd1, 0xc0, 145 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 146 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 147 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, 148 0x45, 0x00, 0xf4, 0x19, 0x11, 0x00, 0x00, 0x1e, 149 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, 150 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, 151 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 152 0x3d, 0x42, 0x44, 0x0f, 0x00, 0x0a, 0x20, 0x20, 153 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 154 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x46, 155 0x48, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x05, 156 }, 157 }; 158 159 static int edid_size(const u8 *edid, int data_size) 160 { 161 if (data_size < EDID_LENGTH) 162 return 0; 163 164 return (edid[0x7e] + 1) * EDID_LENGTH; 165 } 166 167 static void *edid_load(struct drm_connector *connector, const char *name, 168 const char *connector_name) 169 { 170 const struct firmware *fw = NULL; 171 const u8 *fwdata; 172 u8 *edid; 173 int fwsize, builtin; 174 int i, valid_extensions = 0; 175 bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS); 176 177 builtin = match_string(generic_edid_name, GENERIC_EDIDS, name); 178 if (builtin >= 0) { 179 fwdata = generic_edid[builtin]; 180 fwsize = sizeof(generic_edid[builtin]); 181 } else { 182 struct platform_device *pdev; 183 int err; 184 185 pdev = platform_device_register_simple(connector_name, -1, NULL, 0); 186 if (IS_ERR(pdev)) { 187 DRM_ERROR("Failed to register EDID firmware platform device " 188 "for connector \"%s\"\n", connector_name); 189 return ERR_CAST(pdev); 190 } 191 192 err = request_firmware(&fw, name, &pdev->dev); 193 platform_device_unregister(pdev); 194 if (err) { 195 DRM_ERROR("Requesting EDID firmware \"%s\" failed (err=%d)\n", 196 name, err); 197 return ERR_PTR(err); 198 } 199 200 fwdata = fw->data; 201 fwsize = fw->size; 202 } 203 204 if (edid_size(fwdata, fwsize) != fwsize) { 205 DRM_ERROR("Size of EDID firmware \"%s\" is invalid " 206 "(expected %d, got %d\n", name, 207 edid_size(fwdata, fwsize), (int)fwsize); 208 edid = ERR_PTR(-EINVAL); 209 goto out; 210 } 211 212 edid = kmemdup(fwdata, fwsize, GFP_KERNEL); 213 if (edid == NULL) { 214 edid = ERR_PTR(-ENOMEM); 215 goto out; 216 } 217 218 if (!drm_edid_block_valid(edid, 0, print_bad_edid, 219 &connector->edid_corrupt)) { 220 connector->bad_edid_counter++; 221 DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ", 222 name); 223 kfree(edid); 224 edid = ERR_PTR(-EINVAL); 225 goto out; 226 } 227 228 for (i = 1; i <= edid[0x7e]; i++) { 229 if (i != valid_extensions + 1) 230 memcpy(edid + (valid_extensions + 1) * EDID_LENGTH, 231 edid + i * EDID_LENGTH, EDID_LENGTH); 232 if (drm_edid_block_valid(edid + i * EDID_LENGTH, i, 233 print_bad_edid, 234 NULL)) 235 valid_extensions++; 236 } 237 238 if (valid_extensions != edid[0x7e]) { 239 u8 *new_edid; 240 241 edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions; 242 DRM_INFO("Found %d valid extensions instead of %d in EDID data " 243 "\"%s\" for connector \"%s\"\n", valid_extensions, 244 edid[0x7e], name, connector_name); 245 edid[0x7e] = valid_extensions; 246 247 new_edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH, 248 GFP_KERNEL); 249 if (new_edid) 250 edid = new_edid; 251 } 252 253 DRM_INFO("Got %s EDID base block and %d extension%s from " 254 "\"%s\" for connector \"%s\"\n", (builtin >= 0) ? "built-in" : 255 "external", valid_extensions, valid_extensions == 1 ? "" : "s", 256 name, connector_name); 257 258 out: 259 release_firmware(fw); 260 return edid; 261 } 262 263 struct edid *drm_load_edid_firmware(struct drm_connector *connector) 264 { 265 const char *connector_name = connector->name; 266 char *edidname, *last, *colon, *fwstr, *edidstr, *fallback = NULL; 267 struct edid *edid; 268 269 if (edid_firmware[0] == '\0') 270 return ERR_PTR(-ENOENT); 271 272 /* 273 * If there are multiple edid files specified and separated 274 * by commas, search through the list looking for one that 275 * matches the connector. 276 * 277 * If there's one or more that doesn't specify a connector, keep 278 * the last one found one as a fallback. 279 */ 280 fwstr = kstrdup(edid_firmware, GFP_KERNEL); 281 edidstr = fwstr; 282 283 while ((edidname = strsep(&edidstr, ","))) { 284 colon = strchr(edidname, ':'); 285 if (colon != NULL) { 286 if (strncmp(connector_name, edidname, colon - edidname)) 287 continue; 288 edidname = colon + 1; 289 break; 290 } 291 292 if (*edidname != '\0') /* corner case: multiple ',' */ 293 fallback = edidname; 294 } 295 296 if (!edidname) { 297 if (!fallback) { 298 kfree(fwstr); 299 return ERR_PTR(-ENOENT); 300 } 301 edidname = fallback; 302 } 303 304 last = edidname + strlen(edidname) - 1; 305 if (*last == '\n') 306 *last = '\0'; 307 308 edid = edid_load(connector, edidname, connector_name); 309 kfree(fwstr); 310 311 return edid; 312 } 313