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