18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci drm_edid_load.c: use a built-in EDID data set or load it via the firmware 48c2ecf20Sopenharmony_ci interface 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci Copyright (C) 2012 Carsten Emde <C.Emde@osadl.org> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci*/ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/firmware.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 158c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h> 168c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 178c2ecf20Sopenharmony_ci#include <drm/drm_edid.h> 188c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic char edid_firmware[PATH_MAX]; 218c2ecf20Sopenharmony_cimodule_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644); 228c2ecf20Sopenharmony_ciMODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob " 238c2ecf20Sopenharmony_ci "from built-in data or /lib/firmware instead. "); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Use only for backward compatibility with drm_kms_helper.edid_firmware */ 268c2ecf20Sopenharmony_ciint __drm_set_edid_firmware_path(const char *path) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci scnprintf(edid_firmware, sizeof(edid_firmware), "%s", path); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci return 0; 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__drm_set_edid_firmware_path); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* Use only for backward compatibility with drm_kms_helper.edid_firmware */ 358c2ecf20Sopenharmony_ciint __drm_get_edid_firmware_path(char *buf, size_t bufsize) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci return scnprintf(buf, bufsize, "%s", edid_firmware); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__drm_get_edid_firmware_path); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define GENERIC_EDIDS 6 428c2ecf20Sopenharmony_cistatic const char * const generic_edid_name[GENERIC_EDIDS] = { 438c2ecf20Sopenharmony_ci "edid/800x600.bin", 448c2ecf20Sopenharmony_ci "edid/1024x768.bin", 458c2ecf20Sopenharmony_ci "edid/1280x1024.bin", 468c2ecf20Sopenharmony_ci "edid/1600x1200.bin", 478c2ecf20Sopenharmony_ci "edid/1680x1050.bin", 488c2ecf20Sopenharmony_ci "edid/1920x1080.bin", 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic const u8 generic_edid[GENERIC_EDIDS][128] = { 528c2ecf20Sopenharmony_ci { 538c2ecf20Sopenharmony_ci 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 548c2ecf20Sopenharmony_ci 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 558c2ecf20Sopenharmony_ci 0x05, 0x16, 0x01, 0x03, 0x6d, 0x1b, 0x14, 0x78, 568c2ecf20Sopenharmony_ci 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, 578c2ecf20Sopenharmony_ci 0x20, 0x50, 0x54, 0x01, 0x00, 0x00, 0x45, 0x40, 588c2ecf20Sopenharmony_ci 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 598c2ecf20Sopenharmony_ci 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xa0, 0x0f, 608c2ecf20Sopenharmony_ci 0x20, 0x00, 0x31, 0x58, 0x1c, 0x20, 0x28, 0x80, 618c2ecf20Sopenharmony_ci 0x14, 0x00, 0x15, 0xd0, 0x10, 0x00, 0x00, 0x1e, 628c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, 638c2ecf20Sopenharmony_ci 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, 648c2ecf20Sopenharmony_ci 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 658c2ecf20Sopenharmony_ci 0x3d, 0x24, 0x26, 0x05, 0x00, 0x0a, 0x20, 0x20, 668c2ecf20Sopenharmony_ci 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 678c2ecf20Sopenharmony_ci 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53, 688c2ecf20Sopenharmony_ci 0x56, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xc2, 698c2ecf20Sopenharmony_ci }, 708c2ecf20Sopenharmony_ci { 718c2ecf20Sopenharmony_ci 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 728c2ecf20Sopenharmony_ci 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 738c2ecf20Sopenharmony_ci 0x05, 0x16, 0x01, 0x03, 0x6d, 0x23, 0x1a, 0x78, 748c2ecf20Sopenharmony_ci 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, 758c2ecf20Sopenharmony_ci 0x20, 0x50, 0x54, 0x00, 0x08, 0x00, 0x61, 0x40, 768c2ecf20Sopenharmony_ci 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 778c2ecf20Sopenharmony_ci 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x64, 0x19, 788c2ecf20Sopenharmony_ci 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x08, 0x90, 798c2ecf20Sopenharmony_ci 0x36, 0x00, 0x63, 0x0a, 0x11, 0x00, 0x00, 0x18, 808c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, 818c2ecf20Sopenharmony_ci 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, 828c2ecf20Sopenharmony_ci 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 838c2ecf20Sopenharmony_ci 0x3d, 0x2f, 0x31, 0x07, 0x00, 0x0a, 0x20, 0x20, 848c2ecf20Sopenharmony_ci 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 858c2ecf20Sopenharmony_ci 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x58, 868c2ecf20Sopenharmony_ci 0x47, 0x41, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x55, 878c2ecf20Sopenharmony_ci }, 888c2ecf20Sopenharmony_ci { 898c2ecf20Sopenharmony_ci 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 908c2ecf20Sopenharmony_ci 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 918c2ecf20Sopenharmony_ci 0x05, 0x16, 0x01, 0x03, 0x6d, 0x2c, 0x23, 0x78, 928c2ecf20Sopenharmony_ci 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, 938c2ecf20Sopenharmony_ci 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0x81, 0x80, 948c2ecf20Sopenharmony_ci 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 958c2ecf20Sopenharmony_ci 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x2a, 968c2ecf20Sopenharmony_ci 0x00, 0x98, 0x51, 0x00, 0x2a, 0x40, 0x30, 0x70, 978c2ecf20Sopenharmony_ci 0x13, 0x00, 0xbc, 0x63, 0x11, 0x00, 0x00, 0x1e, 988c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, 998c2ecf20Sopenharmony_ci 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, 1008c2ecf20Sopenharmony_ci 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 1018c2ecf20Sopenharmony_ci 0x3d, 0x3e, 0x40, 0x0b, 0x00, 0x0a, 0x20, 0x20, 1028c2ecf20Sopenharmony_ci 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 1038c2ecf20Sopenharmony_ci 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53, 1048c2ecf20Sopenharmony_ci 0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xa0, 1058c2ecf20Sopenharmony_ci }, 1068c2ecf20Sopenharmony_ci { 1078c2ecf20Sopenharmony_ci 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 1088c2ecf20Sopenharmony_ci 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1098c2ecf20Sopenharmony_ci 0x05, 0x16, 0x01, 0x03, 0x6d, 0x37, 0x29, 0x78, 1108c2ecf20Sopenharmony_ci 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, 1118c2ecf20Sopenharmony_ci 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xa9, 0x40, 1128c2ecf20Sopenharmony_ci 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 1138c2ecf20Sopenharmony_ci 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x48, 0x3f, 1148c2ecf20Sopenharmony_ci 0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, 0xc0, 1158c2ecf20Sopenharmony_ci 0x13, 0x00, 0x2b, 0xa0, 0x21, 0x00, 0x00, 0x1e, 1168c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, 1178c2ecf20Sopenharmony_ci 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, 1188c2ecf20Sopenharmony_ci 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 1198c2ecf20Sopenharmony_ci 0x3d, 0x4a, 0x4c, 0x11, 0x00, 0x0a, 0x20, 0x20, 1208c2ecf20Sopenharmony_ci 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 1218c2ecf20Sopenharmony_ci 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x55, 1228c2ecf20Sopenharmony_ci 0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0x9d, 1238c2ecf20Sopenharmony_ci }, 1248c2ecf20Sopenharmony_ci { 1258c2ecf20Sopenharmony_ci 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 1268c2ecf20Sopenharmony_ci 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1278c2ecf20Sopenharmony_ci 0x05, 0x16, 0x01, 0x03, 0x6d, 0x2b, 0x1b, 0x78, 1288c2ecf20Sopenharmony_ci 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, 1298c2ecf20Sopenharmony_ci 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xb3, 0x00, 1308c2ecf20Sopenharmony_ci 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 1318c2ecf20Sopenharmony_ci 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x21, 0x39, 1328c2ecf20Sopenharmony_ci 0x90, 0x30, 0x62, 0x1a, 0x27, 0x40, 0x68, 0xb0, 1338c2ecf20Sopenharmony_ci 0x36, 0x00, 0xb5, 0x11, 0x11, 0x00, 0x00, 0x1e, 1348c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, 1358c2ecf20Sopenharmony_ci 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, 1368c2ecf20Sopenharmony_ci 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 1378c2ecf20Sopenharmony_ci 0x3d, 0x40, 0x42, 0x0f, 0x00, 0x0a, 0x20, 0x20, 1388c2ecf20Sopenharmony_ci 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 1398c2ecf20Sopenharmony_ci 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x57, 1408c2ecf20Sopenharmony_ci 0x53, 0x58, 0x47, 0x41, 0x0a, 0x20, 0x00, 0x26, 1418c2ecf20Sopenharmony_ci }, 1428c2ecf20Sopenharmony_ci { 1438c2ecf20Sopenharmony_ci 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 1448c2ecf20Sopenharmony_ci 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1458c2ecf20Sopenharmony_ci 0x05, 0x16, 0x01, 0x03, 0x6d, 0x32, 0x1c, 0x78, 1468c2ecf20Sopenharmony_ci 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, 1478c2ecf20Sopenharmony_ci 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xd1, 0xc0, 1488c2ecf20Sopenharmony_ci 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 1498c2ecf20Sopenharmony_ci 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 1508c2ecf20Sopenharmony_ci 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, 1518c2ecf20Sopenharmony_ci 0x45, 0x00, 0xf4, 0x19, 0x11, 0x00, 0x00, 0x1e, 1528c2ecf20Sopenharmony_ci 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, 1538c2ecf20Sopenharmony_ci 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, 1548c2ecf20Sopenharmony_ci 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, 1558c2ecf20Sopenharmony_ci 0x3d, 0x42, 0x44, 0x0f, 0x00, 0x0a, 0x20, 0x20, 1568c2ecf20Sopenharmony_ci 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 1578c2ecf20Sopenharmony_ci 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x46, 1588c2ecf20Sopenharmony_ci 0x48, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x05, 1598c2ecf20Sopenharmony_ci }, 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int edid_size(const u8 *edid, int data_size) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci if (data_size < EDID_LENGTH) 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return (edid[0x7e] + 1) * EDID_LENGTH; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic void *edid_load(struct drm_connector *connector, const char *name, 1718c2ecf20Sopenharmony_ci const char *connector_name) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci const struct firmware *fw = NULL; 1748c2ecf20Sopenharmony_ci const u8 *fwdata; 1758c2ecf20Sopenharmony_ci u8 *edid; 1768c2ecf20Sopenharmony_ci int fwsize, builtin; 1778c2ecf20Sopenharmony_ci int i, valid_extensions = 0; 1788c2ecf20Sopenharmony_ci bool print_bad_edid = !connector->bad_edid_counter || drm_debug_enabled(DRM_UT_KMS); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci builtin = match_string(generic_edid_name, GENERIC_EDIDS, name); 1818c2ecf20Sopenharmony_ci if (builtin >= 0) { 1828c2ecf20Sopenharmony_ci fwdata = generic_edid[builtin]; 1838c2ecf20Sopenharmony_ci fwsize = sizeof(generic_edid[builtin]); 1848c2ecf20Sopenharmony_ci } else { 1858c2ecf20Sopenharmony_ci struct platform_device *pdev; 1868c2ecf20Sopenharmony_ci int err; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci pdev = platform_device_register_simple(connector_name, -1, NULL, 0); 1898c2ecf20Sopenharmony_ci if (IS_ERR(pdev)) { 1908c2ecf20Sopenharmony_ci DRM_ERROR("Failed to register EDID firmware platform device " 1918c2ecf20Sopenharmony_ci "for connector \"%s\"\n", connector_name); 1928c2ecf20Sopenharmony_ci return ERR_CAST(pdev); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci err = request_firmware(&fw, name, &pdev->dev); 1968c2ecf20Sopenharmony_ci platform_device_unregister(pdev); 1978c2ecf20Sopenharmony_ci if (err) { 1988c2ecf20Sopenharmony_ci DRM_ERROR("Requesting EDID firmware \"%s\" failed (err=%d)\n", 1998c2ecf20Sopenharmony_ci name, err); 2008c2ecf20Sopenharmony_ci return ERR_PTR(err); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci fwdata = fw->data; 2048c2ecf20Sopenharmony_ci fwsize = fw->size; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (edid_size(fwdata, fwsize) != fwsize) { 2088c2ecf20Sopenharmony_ci DRM_ERROR("Size of EDID firmware \"%s\" is invalid " 2098c2ecf20Sopenharmony_ci "(expected %d, got %d\n", name, 2108c2ecf20Sopenharmony_ci edid_size(fwdata, fwsize), (int)fwsize); 2118c2ecf20Sopenharmony_ci edid = ERR_PTR(-EINVAL); 2128c2ecf20Sopenharmony_ci goto out; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci edid = kmemdup(fwdata, fwsize, GFP_KERNEL); 2168c2ecf20Sopenharmony_ci if (edid == NULL) { 2178c2ecf20Sopenharmony_ci edid = ERR_PTR(-ENOMEM); 2188c2ecf20Sopenharmony_ci goto out; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (!drm_edid_block_valid(edid, 0, print_bad_edid, 2228c2ecf20Sopenharmony_ci &connector->edid_corrupt)) { 2238c2ecf20Sopenharmony_ci connector->bad_edid_counter++; 2248c2ecf20Sopenharmony_ci DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ", 2258c2ecf20Sopenharmony_ci name); 2268c2ecf20Sopenharmony_ci kfree(edid); 2278c2ecf20Sopenharmony_ci edid = ERR_PTR(-EINVAL); 2288c2ecf20Sopenharmony_ci goto out; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci for (i = 1; i <= edid[0x7e]; i++) { 2328c2ecf20Sopenharmony_ci if (i != valid_extensions + 1) 2338c2ecf20Sopenharmony_ci memcpy(edid + (valid_extensions + 1) * EDID_LENGTH, 2348c2ecf20Sopenharmony_ci edid + i * EDID_LENGTH, EDID_LENGTH); 2358c2ecf20Sopenharmony_ci if (drm_edid_block_valid(edid + i * EDID_LENGTH, i, 2368c2ecf20Sopenharmony_ci print_bad_edid, 2378c2ecf20Sopenharmony_ci NULL)) 2388c2ecf20Sopenharmony_ci valid_extensions++; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (valid_extensions != edid[0x7e]) { 2428c2ecf20Sopenharmony_ci u8 *new_edid; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions; 2458c2ecf20Sopenharmony_ci DRM_INFO("Found %d valid extensions instead of %d in EDID data " 2468c2ecf20Sopenharmony_ci "\"%s\" for connector \"%s\"\n", valid_extensions, 2478c2ecf20Sopenharmony_ci edid[0x7e], name, connector_name); 2488c2ecf20Sopenharmony_ci edid[0x7e] = valid_extensions; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci new_edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH, 2518c2ecf20Sopenharmony_ci GFP_KERNEL); 2528c2ecf20Sopenharmony_ci if (new_edid) 2538c2ecf20Sopenharmony_ci edid = new_edid; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci DRM_INFO("Got %s EDID base block and %d extension%s from " 2578c2ecf20Sopenharmony_ci "\"%s\" for connector \"%s\"\n", (builtin >= 0) ? "built-in" : 2588c2ecf20Sopenharmony_ci "external", valid_extensions, valid_extensions == 1 ? "" : "s", 2598c2ecf20Sopenharmony_ci name, connector_name); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ciout: 2628c2ecf20Sopenharmony_ci release_firmware(fw); 2638c2ecf20Sopenharmony_ci return edid; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistruct edid *drm_load_edid_firmware(struct drm_connector *connector) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci const char *connector_name = connector->name; 2698c2ecf20Sopenharmony_ci char *edidname, *last, *colon, *fwstr, *edidstr, *fallback = NULL; 2708c2ecf20Sopenharmony_ci struct edid *edid; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (edid_firmware[0] == '\0') 2738c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* 2768c2ecf20Sopenharmony_ci * If there are multiple edid files specified and separated 2778c2ecf20Sopenharmony_ci * by commas, search through the list looking for one that 2788c2ecf20Sopenharmony_ci * matches the connector. 2798c2ecf20Sopenharmony_ci * 2808c2ecf20Sopenharmony_ci * If there's one or more that doesn't specify a connector, keep 2818c2ecf20Sopenharmony_ci * the last one found one as a fallback. 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci fwstr = kstrdup(edid_firmware, GFP_KERNEL); 2848c2ecf20Sopenharmony_ci if (!fwstr) 2858c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2868c2ecf20Sopenharmony_ci edidstr = fwstr; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci while ((edidname = strsep(&edidstr, ","))) { 2898c2ecf20Sopenharmony_ci colon = strchr(edidname, ':'); 2908c2ecf20Sopenharmony_ci if (colon != NULL) { 2918c2ecf20Sopenharmony_ci if (strncmp(connector_name, edidname, colon - edidname)) 2928c2ecf20Sopenharmony_ci continue; 2938c2ecf20Sopenharmony_ci edidname = colon + 1; 2948c2ecf20Sopenharmony_ci break; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (*edidname != '\0') /* corner case: multiple ',' */ 2988c2ecf20Sopenharmony_ci fallback = edidname; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (!edidname) { 3028c2ecf20Sopenharmony_ci if (!fallback) { 3038c2ecf20Sopenharmony_ci kfree(fwstr); 3048c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci edidname = fallback; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci last = edidname + strlen(edidname) - 1; 3108c2ecf20Sopenharmony_ci if (*last == '\n') 3118c2ecf20Sopenharmony_ci *last = '\0'; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci edid = edid_load(connector, edidname, connector_name); 3148c2ecf20Sopenharmony_ci kfree(fwstr); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return edid; 3178c2ecf20Sopenharmony_ci} 318