xref: /openbmc/linux/drivers/gpu/drm/drm_edid_load.c (revision 2a598d0b)
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 		int err;
176 
177 		err = request_firmware(&fw, name, connector->dev->dev);
178 		if (err) {
179 			drm_err(connector->dev,
180 				"[CONNECTOR:%d:%s] Requesting EDID firmware \"%s\" failed (err=%d)\n",
181 				connector->base.id, connector->name,
182 				name, err);
183 			return ERR_PTR(err);
184 		}
185 
186 		fwdata = fw->data;
187 		fwsize = fw->size;
188 	}
189 
190 	drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Loaded %s firmware EDID \"%s\"\n",
191 		    connector->base.id, connector->name,
192 		    builtin >= 0 ? "built-in" : "external", name);
193 
194 	drm_edid = drm_edid_alloc(fwdata, fwsize);
195 	if (!drm_edid_valid(drm_edid)) {
196 		drm_err(connector->dev, "Invalid firmware EDID \"%s\"\n", name);
197 		drm_edid_free(drm_edid);
198 		drm_edid = ERR_PTR(-EINVAL);
199 	}
200 
201 	release_firmware(fw);
202 
203 	return drm_edid;
204 }
205 
206 const struct drm_edid *drm_edid_load_firmware(struct drm_connector *connector)
207 {
208 	char *edidname, *last, *colon, *fwstr, *edidstr, *fallback = NULL;
209 	const struct drm_edid *drm_edid;
210 
211 	if (edid_firmware[0] == '\0')
212 		return ERR_PTR(-ENOENT);
213 
214 	/*
215 	 * If there are multiple edid files specified and separated
216 	 * by commas, search through the list looking for one that
217 	 * matches the connector.
218 	 *
219 	 * If there's one or more that doesn't specify a connector, keep
220 	 * the last one found one as a fallback.
221 	 */
222 	fwstr = kstrdup(edid_firmware, GFP_KERNEL);
223 	if (!fwstr)
224 		return ERR_PTR(-ENOMEM);
225 	edidstr = fwstr;
226 
227 	while ((edidname = strsep(&edidstr, ","))) {
228 		colon = strchr(edidname, ':');
229 		if (colon != NULL) {
230 			if (strncmp(connector->name, edidname, colon - edidname))
231 				continue;
232 			edidname = colon + 1;
233 			break;
234 		}
235 
236 		if (*edidname != '\0') /* corner case: multiple ',' */
237 			fallback = edidname;
238 	}
239 
240 	if (!edidname) {
241 		if (!fallback) {
242 			kfree(fwstr);
243 			return ERR_PTR(-ENOENT);
244 		}
245 		edidname = fallback;
246 	}
247 
248 	last = edidname + strlen(edidname) - 1;
249 	if (*last == '\n')
250 		*last = '\0';
251 
252 	drm_edid = edid_load(connector, edidname);
253 
254 	kfree(fwstr);
255 
256 	return drm_edid;
257 }
258