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