1 /* 2 * Copyright (c) 2015 Rimvydas Jasinskas 3 * 4 * Simple EDID firmware handling routines derived from 5 * drm_edid.c:drm_do_get_edid() 6 * 7 * Copyright (c) 2007-2008 Intel Corporation 8 * Jesse Barnes <jesse.barnes@intel.com> 9 * Copyright 2010 Red Hat, Inc. 10 * 11 * Permission is hereby granted, free of charge, to any person obtaining a 12 * copy of this software and associated documentation files (the "Software"), 13 * to deal in the Software without restriction, including without limitation 14 * the rights to use, copy, modify, merge, publish, distribute, sub license, 15 * and/or sell copies of the Software, and to permit persons to whom the 16 * Software is furnished to do so, subject to the following conditions: 17 * 18 * The above copyright notice and this permission notice (including the 19 * next paragraph) shall be included in all copies or substantial portions 20 * of the Software. 21 * 22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 25 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 * DEALINGS IN THE SOFTWARE. 29 */ 30 31 #include <drm/drmP.h> 32 #include <drm/drm_edid.h> 33 #include <sys/bus.h> 34 #include <sys/firmware.h> 35 36 #ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE 37 static u8 *do_edid_fw_load(struct drm_connector *connector, const char *fwname, 38 const char *connector_name); 39 40 static char edidfw_tun[256]; 41 TUNABLE_STR("drm.edid_firmware", edidfw_tun, sizeof(edidfw_tun)); 42 43 int drm_load_edid_firmware(struct drm_connector *connector) 44 { 45 const char *connector_name = connector->name; 46 char *cp, *fwname = edidfw_tun; 47 struct edid *fwedid; 48 int ret; 49 50 if (*fwname == '\0') 51 return 0; 52 53 /* Check for connector specifier presence */ 54 if ((cp = strchr(fwname, ':')) != NULL) { 55 /* if connector name doesn't match, we're done */ 56 if (strncmp(connector_name, fwname, cp - fwname)) 57 return 0; 58 fwname = cp + 1; 59 if (*fwname == '\0') 60 return 0; 61 } 62 63 fwedid = (struct edid *)do_edid_fw_load(connector, fwname, connector_name); 64 if (fwedid == NULL) 65 return 0; 66 67 drm_mode_connector_update_edid_property(connector, fwedid); 68 ret = drm_add_edid_modes(connector, fwedid); 69 kfree(fwedid); 70 71 return ret; 72 } 73 74 static u8 * 75 do_edid_fw_load(struct drm_connector *connector, const char *fwname, 76 const char *connector_name) 77 { 78 const struct firmware *fw = NULL; 79 const u8 *fwdata; 80 u8 *block = NULL, *new = NULL; 81 int fwsize, expected; 82 int j, valid_extensions = 0; 83 bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS); 84 85 fw = firmware_get(fwname); 86 87 if (fw == NULL) { 88 DRM_ERROR("Requesting EDID firmware %s failed\n", fwname); 89 return (NULL); 90 } 91 92 fwdata = fw->data; 93 fwsize = fw->datasize; 94 95 if (fwsize < EDID_LENGTH) 96 goto fw_out; 97 98 expected = (fwdata[0x7e] + 1) * EDID_LENGTH; 99 if (expected != fwsize) { 100 DRM_ERROR("Size of EDID firmware %s is invalid: %d vs %d(got)\n", 101 fwname, expected, fwsize); 102 goto fw_out; 103 } 104 105 block = kmalloc(fwsize, M_DRM, GFP_KERNEL); 106 if (block == NULL) { 107 goto fw_out; 108 } 109 memcpy(block, fwdata, fwsize); 110 111 /* now it is safe to release the firmware */ 112 fw_out: 113 fwdata = NULL; 114 if (fw != NULL) { 115 /* 116 * Don't release edid fw right away, useful if / is 117 * still not mounted and/or we performing early kms 118 */ 119 firmware_put(fw, 0); 120 } 121 122 if (block == NULL) 123 return (NULL); 124 125 /* first check the base block */ 126 if (!drm_edid_block_valid(block, 0, print_bad_edid, NULL)) { 127 connector->bad_edid_counter++; 128 DRM_ERROR("EDID firmware %s base block is invalid ", fwname); 129 goto out; 130 } 131 132 DRM_INFO("Got EDID base block from %s for connector %s\n", fwname, connector_name); 133 134 /* if there's no extensions, we're done */ 135 if (block[0x7e] == 0) 136 return block; 137 138 /* XXX then extension blocks */ 139 WARN(1, "Loading EDID firmware with extensions is untested!\n"); 140 141 for (j = 1; j <= block[0x7e]; j++) { 142 /* if we skiped any extension block we have to shuffle good ones */ 143 if (j != valid_extensions + 1) { 144 memcpy(block + (valid_extensions + 1) * EDID_LENGTH, 145 block + (j * EDID_LENGTH), EDID_LENGTH); 146 } 147 if (drm_edid_block_valid(block + j * EDID_LENGTH, j, print_bad_edid, NULL)) { 148 valid_extensions++; 149 } 150 } 151 152 if (valid_extensions != block[0x7e]) { 153 block[EDID_LENGTH-1] += block[0x7e] - valid_extensions; 154 block[0x7e] = valid_extensions; 155 new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH, M_DRM, M_WAITOK); 156 if (new == NULL) 157 goto out; 158 block = new; 159 } 160 161 if (valid_extensions > 0) { 162 DRM_INFO("Got %d extensions in EDID firmware from %s for connector %s\n", 163 valid_extensions, fwname, connector_name); 164 } 165 166 /* if got to here return edid block */ 167 return block; 168 169 out: 170 kfree(block); 171 return (NULL); 172 } 173 174 #endif /* CONFIG_DRM_LOAD_EDID_FIRMWARE */ 175