xref: /openbmc/linux/drivers/gpu/drm/drm_edid_load.c (revision 8e8e69d6)
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