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