blob: 1e5593575d232c2cdcf8d9533eace20b64e95da4 [file] [log] [blame]
Thomas Gleixnerfd534e92019-05-23 11:14:39 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Carsten Emdeda0df922012-03-18 22:37:33 +01002/*
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
Carsten Emdeda0df922012-03-18 22:37:33 +01008*/
9
10#include <linux/module.h>
11#include <linux/firmware.h>
David Howells760285e2012-10-02 18:01:07 +010012#include <drm/drmP.h>
13#include <drm/drm_crtc.h>
14#include <drm/drm_crtc_helper.h>
15#include <drm/drm_edid.h>
Carsten Emdeda0df922012-03-18 22:37:33 +010016
17static char edid_firmware[PATH_MAX];
18module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644);
19MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob "
20 "from built-in data or /lib/firmware instead. ");
21
Jani Nikulaac6c35a2017-09-18 21:20:03 +030022/* Use only for backward compatibility with drm_kms_helper.edid_firmware */
23int __drm_set_edid_firmware_path(const char *path)
24{
25 scnprintf(edid_firmware, sizeof(edid_firmware), "%s", path);
26
27 return 0;
28}
29EXPORT_SYMBOL(__drm_set_edid_firmware_path);
30
31/* Use only for backward compatibility with drm_kms_helper.edid_firmware */
32int __drm_get_edid_firmware_path(char *buf, size_t bufsize)
33{
34 return scnprintf(buf, bufsize, "%s", edid_firmware);
35}
36EXPORT_SYMBOL(__drm_get_edid_firmware_path);
37
Daniel Thompson4cbe1bfa2014-05-23 16:01:43 +010038#define GENERIC_EDIDS 6
Ville Syrjäläa5b62372015-08-31 15:09:25 +030039static const char * const generic_edid_name[GENERIC_EDIDS] = {
Daniel Thompson4cbe1bfa2014-05-23 16:01:43 +010040 "edid/800x600.bin",
Carsten Emdeda0df922012-03-18 22:37:33 +010041 "edid/1024x768.bin",
42 "edid/1280x1024.bin",
Carsten Emde8091ee52013-04-06 16:01:34 +000043 "edid/1600x1200.bin",
Carsten Emdeda0df922012-03-18 22:37:33 +010044 "edid/1680x1050.bin",
45 "edid/1920x1080.bin",
46};
47
Chris Wilson9066f832013-10-02 11:12:53 +010048static const u8 generic_edid[GENERIC_EDIDS][128] = {
Carsten Emdeda0df922012-03-18 22:37:33 +010049 {
50 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
51 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
Daniel Thompson4cbe1bfa2014-05-23 16:01:43 +010052 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,
Carsten Emdeda0df922012-03-18 22:37:33 +010070 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,
Carsten Emde8091ee52013-04-06 16:01:34 +0000106 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,
Carsten Emdeda0df922012-03-18 22:37:33 +0100124 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
Chris Wilson9066f832013-10-02 11:12:53 +0100159static 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
Geert Uytterhoevence456e02013-11-19 12:15:05 +0100167static void *edid_load(struct drm_connector *connector, const char *name,
Linus Torvalds496fd152013-07-10 14:21:46 -0700168 const char *connector_name)
Carsten Emdeda0df922012-03-18 22:37:33 +0100169{
Chris Wilson9066f832013-10-02 11:12:53 +0100170 const struct firmware *fw = NULL;
171 const u8 *fwdata;
172 u8 *edid;
173 int fwsize, builtin;
Carsten Emdeda0df922012-03-18 22:37:33 +0100174 int i, valid_extensions = 0;
Jerome Glisse0b2443e2012-08-09 11:25:51 -0400175 bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS);
Carsten Emdeda0df922012-03-18 22:37:33 +0100176
Andy Shevchenko7a5cf522016-03-17 14:22:23 -0700177 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 {
Chris Wilson9066f832013-10-02 11:12:53 +0100182 struct platform_device *pdev;
183 int err;
Carsten Emdeda0df922012-03-18 22:37:33 +0100184
Chris Wilson9066f832013-10-02 11:12:53 +0100185 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 }
Carsten Emdeda0df922012-03-18 22:37:33 +0100191
Chris Wilson9066f832013-10-02 11:12:53 +0100192 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;
Carsten Emdeda0df922012-03-18 22:37:33 +0100201 fwsize = fw->size;
202 }
203
Chris Wilson9066f832013-10-02 11:12:53 +0100204 if (edid_size(fwdata, fwsize) != fwsize) {
Carsten Emdeda0df922012-03-18 22:37:33 +0100205 DRM_ERROR("Size of EDID firmware \"%s\" is invalid "
Chris Wilson9066f832013-10-02 11:12:53 +0100206 "(expected %d, got %d\n", name,
207 edid_size(fwdata, fwsize), (int)fwsize);
208 edid = ERR_PTR(-EINVAL);
209 goto out;
Carsten Emdeda0df922012-03-18 22:37:33 +0100210 }
211
Thomas Meyer8d06cd02013-05-22 21:06:30 +0000212 edid = kmemdup(fwdata, fwsize, GFP_KERNEL);
Carsten Emdeda0df922012-03-18 22:37:33 +0100213 if (edid == NULL) {
Chris Wilson9066f832013-10-02 11:12:53 +0100214 edid = ERR_PTR(-ENOMEM);
215 goto out;
Carsten Emdeda0df922012-03-18 22:37:33 +0100216 }
Carsten Emdeda0df922012-03-18 22:37:33 +0100217
Todd Previte6ba2bd32015-04-21 11:09:41 -0700218 if (!drm_edid_block_valid(edid, 0, print_bad_edid,
219 &connector->edid_corrupt)) {
Jerome Glisse0b2443e2012-08-09 11:25:51 -0400220 connector->bad_edid_counter++;
Carsten Emdeda0df922012-03-18 22:37:33 +0100221 DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ",
222 name);
223 kfree(edid);
Chris Wilson9066f832013-10-02 11:12:53 +0100224 edid = ERR_PTR(-EINVAL);
225 goto out;
Carsten Emdeda0df922012-03-18 22:37:33 +0100226 }
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);
Todd Previte6ba2bd32015-04-21 11:09:41 -0700232 if (drm_edid_block_valid(edid + i * EDID_LENGTH, i,
233 print_bad_edid,
234 NULL))
Carsten Emdeda0df922012-03-18 22:37:33 +0100235 valid_extensions++;
236 }
237
238 if (valid_extensions != edid[0x7e]) {
Chris Wilson9066f832013-10-02 11:12:53 +0100239 u8 *new_edid;
240
Carsten Emdeda0df922012-03-18 22:37:33 +0100241 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;
Chris Wilson9066f832013-10-02 11:12:53 +0100246
Alexey Khoroshilovf7b83b92012-08-07 12:23:06 +0000247 new_edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
Chris Wilson9066f832013-10-02 11:12:53 +0100248 GFP_KERNEL);
249 if (new_edid)
250 edid = new_edid;
Carsten Emdeda0df922012-03-18 22:37:33 +0100251 }
252
Carsten Emdeda0df922012-03-18 22:37:33 +0100253 DRM_INFO("Got %s EDID base block and %d extension%s from "
Andy Shevchenko7a5cf522016-03-17 14:22:23 -0700254 "\"%s\" for connector \"%s\"\n", (builtin >= 0) ? "built-in" :
Carsten Emdeda0df922012-03-18 22:37:33 +0100255 "external", valid_extensions, valid_extensions == 1 ? "" : "s",
256 name, connector_name);
257
Carsten Emdeda0df922012-03-18 22:37:33 +0100258out:
Markus Elfring9084acf2014-11-19 16:33:17 +0100259 release_firmware(fw);
Jani Nikula451023d2012-08-15 09:32:39 +0000260 return edid;
Carsten Emdeda0df922012-03-18 22:37:33 +0100261}
262
Jani Nikula07c2b842017-02-17 17:20:51 +0200263struct edid *drm_load_edid_firmware(struct drm_connector *connector)
Carsten Emdeda0df922012-03-18 22:37:33 +0100264{
Jani Nikula25933822014-06-03 14:56:20 +0300265 const char *connector_name = connector->name;
Bob Paauwe96206e22015-08-27 10:04:13 -0700266 char *edidname, *last, *colon, *fwstr, *edidstr, *fallback = NULL;
Jani Nikula451023d2012-08-15 09:32:39 +0000267 struct edid *edid;
Carsten Emdeda0df922012-03-18 22:37:33 +0100268
Bob Paauwe96206e22015-08-27 10:04:13 -0700269 if (edid_firmware[0] == '\0')
Jani Nikula07c2b842017-02-17 17:20:51 +0200270 return ERR_PTR(-ENOENT);
Carsten Emdeda0df922012-03-18 22:37:33 +0100271
Bob Paauwe96206e22015-08-27 10:04:13 -0700272 /*
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 *
Valdis Kletnieksc67f6952016-05-30 02:26:38 -0400277 * If there's one or more that doesn't specify a connector, keep
Bob Paauwe96206e22015-08-27 10:04:13 -0700278 * 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);
Jani Nikula07c2b842017-02-17 17:20:51 +0200299 return ERR_PTR(-ENOENT);
Bob Paauwe96206e22015-08-27 10:04:13 -0700300 }
301 edidname = fallback;
Carsten Emdeda0df922012-03-18 22:37:33 +0100302 }
303
304 last = edidname + strlen(edidname) - 1;
305 if (*last == '\n')
306 *last = '\0';
307
Geert Uytterhoevence456e02013-11-19 12:15:05 +0100308 edid = edid_load(connector, edidname, connector_name);
Bob Paauwe96206e22015-08-27 10:04:13 -0700309 kfree(fwstr);
310
Jani Nikula07c2b842017-02-17 17:20:51 +0200311 return edid;
Carsten Emdeda0df922012-03-18 22:37:33 +0100312}