1/*
2   drm_edid_load.c: use a built-in EDID data set or load it via the firmware
3		    interface
4
5   Copyright (C) 2012 Carsten Emde <C.Emde@osadl.org>
6
7   This program is free software; you can redistribute it and/or
8   modify it under the terms of the GNU General Public License
9   as published by the Free Software Foundation; either version 2
10   of the License, or (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
20*/
21
22#include <linux/module.h>
23#include <linux/firmware.h>
24#include <drm/drmP.h>
25#include <drm/drm_crtc.h>
26#include <drm/drm_crtc_helper.h>
27#include <drm/drm_edid.h>
28
29static char edid_firmware[PATH_MAX];
30module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644);
31MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob "
32	"from built-in data or /lib/firmware instead. ");
33
34#define GENERIC_EDIDS 6
35static const char *generic_edid_name[GENERIC_EDIDS] = {
36	"edid/800x600.bin",
37	"edid/1024x768.bin",
38	"edid/1280x1024.bin",
39	"edid/1600x1200.bin",
40	"edid/1680x1050.bin",
41	"edid/1920x1080.bin",
42};
43
44static const u8 generic_edid[GENERIC_EDIDS][128] = {
45	{
46	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
47	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
48	0x05, 0x16, 0x01, 0x03, 0x6d, 0x1b, 0x14, 0x78,
49	0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
50	0x20, 0x50, 0x54, 0x01, 0x00, 0x00, 0x45, 0x40,
51	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
52	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xa0, 0x0f,
53	0x20, 0x00, 0x31, 0x58, 0x1c, 0x20, 0x28, 0x80,
54	0x14, 0x00, 0x15, 0xd0, 0x10, 0x00, 0x00, 0x1e,
55	0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
56	0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
57	0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
58	0x3d, 0x24, 0x26, 0x05, 0x00, 0x0a, 0x20, 0x20,
59	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
60	0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53,
61	0x56, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xc2,
62	},
63	{
64	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
65	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
66	0x05, 0x16, 0x01, 0x03, 0x6d, 0x23, 0x1a, 0x78,
67	0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
68	0x20, 0x50, 0x54, 0x00, 0x08, 0x00, 0x61, 0x40,
69	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
70	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x64, 0x19,
71	0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x08, 0x90,
72	0x36, 0x00, 0x63, 0x0a, 0x11, 0x00, 0x00, 0x18,
73	0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
74	0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
75	0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
76	0x3d, 0x2f, 0x31, 0x07, 0x00, 0x0a, 0x20, 0x20,
77	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
78	0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x58,
79	0x47, 0x41, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x55,
80	},
81	{
82	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
83	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
84	0x05, 0x16, 0x01, 0x03, 0x6d, 0x2c, 0x23, 0x78,
85	0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
86	0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0x81, 0x80,
87	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
88	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x2a,
89	0x00, 0x98, 0x51, 0x00, 0x2a, 0x40, 0x30, 0x70,
90	0x13, 0x00, 0xbc, 0x63, 0x11, 0x00, 0x00, 0x1e,
91	0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
92	0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
93	0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
94	0x3d, 0x3e, 0x40, 0x0b, 0x00, 0x0a, 0x20, 0x20,
95	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
96	0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53,
97	0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xa0,
98	},
99	{
100	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
101	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
102	0x05, 0x16, 0x01, 0x03, 0x6d, 0x37, 0x29, 0x78,
103	0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
104	0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xa9, 0x40,
105	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
106	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x48, 0x3f,
107	0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, 0xc0,
108	0x13, 0x00, 0x2b, 0xa0, 0x21, 0x00, 0x00, 0x1e,
109	0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
110	0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
111	0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
112	0x3d, 0x4a, 0x4c, 0x11, 0x00, 0x0a, 0x20, 0x20,
113	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
114	0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x55,
115	0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0x9d,
116	},
117	{
118	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
119	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
120	0x05, 0x16, 0x01, 0x03, 0x6d, 0x2b, 0x1b, 0x78,
121	0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
122	0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xb3, 0x00,
123	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
124	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x21, 0x39,
125	0x90, 0x30, 0x62, 0x1a, 0x27, 0x40, 0x68, 0xb0,
126	0x36, 0x00, 0xb5, 0x11, 0x11, 0x00, 0x00, 0x1e,
127	0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
128	0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
129	0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
130	0x3d, 0x40, 0x42, 0x0f, 0x00, 0x0a, 0x20, 0x20,
131	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
132	0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x57,
133	0x53, 0x58, 0x47, 0x41, 0x0a, 0x20, 0x00, 0x26,
134	},
135	{
136	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
137	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
138	0x05, 0x16, 0x01, 0x03, 0x6d, 0x32, 0x1c, 0x78,
139	0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
140	0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xd1, 0xc0,
141	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
142	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a,
143	0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
144	0x45, 0x00, 0xf4, 0x19, 0x11, 0x00, 0x00, 0x1e,
145	0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
146	0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
147	0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
148	0x3d, 0x42, 0x44, 0x0f, 0x00, 0x0a, 0x20, 0x20,
149	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
150	0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x46,
151	0x48, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x05,
152	},
153};
154
155static int edid_size(const u8 *edid, int data_size)
156{
157	if (data_size < EDID_LENGTH)
158		return 0;
159
160	return (edid[0x7e] + 1) * EDID_LENGTH;
161}
162
163static void *edid_load(struct drm_connector *connector, const char *name,
164			const char *connector_name)
165{
166	const struct firmware *fw = NULL;
167	const u8 *fwdata;
168	u8 *edid;
169	int fwsize, builtin;
170	int i, valid_extensions = 0;
171	bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS);
172
173	builtin = 0;
174	for (i = 0; i < GENERIC_EDIDS; i++) {
175		if (strcmp(name, generic_edid_name[i]) == 0) {
176			fwdata = generic_edid[i];
177			fwsize = sizeof(generic_edid[i]);
178			builtin = 1;
179			break;
180		}
181	}
182	if (!builtin) {
183		struct platform_device *pdev;
184		int err;
185
186		pdev = platform_device_register_simple(connector_name, -1, NULL, 0);
187		if (IS_ERR(pdev)) {
188			DRM_ERROR("Failed to register EDID firmware platform device "
189				  "for connector \"%s\"\n", connector_name);
190			return ERR_CAST(pdev);
191		}
192
193		err = request_firmware(&fw, name, &pdev->dev);
194		platform_device_unregister(pdev);
195		if (err) {
196			DRM_ERROR("Requesting EDID firmware \"%s\" failed (err=%d)\n",
197				  name, err);
198			return ERR_PTR(err);
199		}
200
201		fwdata = fw->data;
202		fwsize = fw->size;
203	}
204
205	if (edid_size(fwdata, fwsize) != fwsize) {
206		DRM_ERROR("Size of EDID firmware \"%s\" is invalid "
207			  "(expected %d, got %d\n", name,
208			  edid_size(fwdata, fwsize), (int)fwsize);
209		edid = ERR_PTR(-EINVAL);
210		goto out;
211	}
212
213	edid = kmemdup(fwdata, fwsize, GFP_KERNEL);
214	if (edid == NULL) {
215		edid = ERR_PTR(-ENOMEM);
216		goto out;
217	}
218
219	if (!drm_edid_block_valid(edid, 0, print_bad_edid)) {
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, print_bad_edid))
233			valid_extensions++;
234	}
235
236	if (valid_extensions != edid[0x7e]) {
237		u8 *new_edid;
238
239		edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions;
240		DRM_INFO("Found %d valid extensions instead of %d in EDID data "
241		    "\"%s\" for connector \"%s\"\n", valid_extensions,
242		    edid[0x7e], name, connector_name);
243		edid[0x7e] = valid_extensions;
244
245		new_edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
246				    GFP_KERNEL);
247		if (new_edid)
248			edid = new_edid;
249	}
250
251	DRM_INFO("Got %s EDID base block and %d extension%s from "
252	    "\"%s\" for connector \"%s\"\n", builtin ? "built-in" :
253	    "external", valid_extensions, valid_extensions == 1 ? "" : "s",
254	    name, connector_name);
255
256out:
257	release_firmware(fw);
258	return edid;
259}
260
261int drm_load_edid_firmware(struct drm_connector *connector)
262{
263	const char *connector_name = connector->name;
264	char *edidname = edid_firmware, *last, *colon;
265	int ret;
266	struct edid *edid;
267
268	if (*edidname == '\0')
269		return 0;
270
271	colon = strchr(edidname, ':');
272	if (colon != NULL) {
273		if (strncmp(connector_name, edidname, colon - edidname))
274			return 0;
275		edidname = colon + 1;
276		if (*edidname == '\0')
277			return 0;
278	}
279
280	last = edidname + strlen(edidname) - 1;
281	if (*last == '\n')
282		*last = '\0';
283
284	edid = edid_load(connector, edidname, connector_name);
285	if (IS_ERR_OR_NULL(edid))
286		return 0;
287
288	drm_mode_connector_update_edid_property(connector, edid);
289	ret = drm_add_edid_modes(connector, edid);
290	drm_edid_to_eld(connector, edid);
291	kfree(edid);
292
293	return ret;
294}
295