Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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 5
 35static const char *generic_edid_name[GENERIC_EDIDS] = {
 36	"edid/1024x768.bin",
 37	"edid/1280x1024.bin",
 38	"edid/1600x1200.bin",
 39	"edid/1680x1050.bin",
 40	"edid/1920x1080.bin",
 41};
 42
 43static const u8 generic_edid[GENERIC_EDIDS][128] = {
 44	{
 45	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
 46	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 47	0x05, 0x16, 0x01, 0x03, 0x6d, 0x23, 0x1a, 0x78,
 48	0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
 49	0x20, 0x50, 0x54, 0x00, 0x08, 0x00, 0x61, 0x40,
 50	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
 51	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x64, 0x19,
 52	0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x08, 0x90,
 53	0x36, 0x00, 0x63, 0x0a, 0x11, 0x00, 0x00, 0x18,
 54	0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
 55	0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
 56	0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
 57	0x3d, 0x2f, 0x31, 0x07, 0x00, 0x0a, 0x20, 0x20,
 58	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
 59	0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x58,
 60	0x47, 0x41, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x55,
 61	},
 62	{
 63	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
 64	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 65	0x05, 0x16, 0x01, 0x03, 0x6d, 0x2c, 0x23, 0x78,
 66	0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
 67	0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0x81, 0x80,
 68	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
 69	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x2a,
 70	0x00, 0x98, 0x51, 0x00, 0x2a, 0x40, 0x30, 0x70,
 71	0x13, 0x00, 0xbc, 0x63, 0x11, 0x00, 0x00, 0x1e,
 72	0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
 73	0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
 74	0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
 75	0x3d, 0x3e, 0x40, 0x0b, 0x00, 0x0a, 0x20, 0x20,
 76	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
 77	0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53,
 78	0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xa0,
 79	},
 80	{
 81	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
 82	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 83	0x05, 0x16, 0x01, 0x03, 0x6d, 0x37, 0x29, 0x78,
 84	0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
 85	0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xa9, 0x40,
 86	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
 87	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x48, 0x3f,
 88	0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, 0xc0,
 89	0x13, 0x00, 0x2b, 0xa0, 0x21, 0x00, 0x00, 0x1e,
 90	0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
 91	0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
 92	0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
 93	0x3d, 0x4a, 0x4c, 0x11, 0x00, 0x0a, 0x20, 0x20,
 94	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
 95	0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x55,
 96	0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0x9d,
 97	},
 98	{
 99	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
100	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
101	0x05, 0x16, 0x01, 0x03, 0x6d, 0x2b, 0x1b, 0x78,
102	0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
103	0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xb3, 0x00,
104	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
105	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x21, 0x39,
106	0x90, 0x30, 0x62, 0x1a, 0x27, 0x40, 0x68, 0xb0,
107	0x36, 0x00, 0xb5, 0x11, 0x11, 0x00, 0x00, 0x1e,
108	0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
109	0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
110	0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
111	0x3d, 0x40, 0x42, 0x0f, 0x00, 0x0a, 0x20, 0x20,
112	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
113	0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x57,
114	0x53, 0x58, 0x47, 0x41, 0x0a, 0x20, 0x00, 0x26,
115	},
116	{
117	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
118	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
119	0x05, 0x16, 0x01, 0x03, 0x6d, 0x32, 0x1c, 0x78,
120	0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
121	0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xd1, 0xc0,
122	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
123	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a,
124	0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
125	0x45, 0x00, 0xf4, 0x19, 0x11, 0x00, 0x00, 0x1e,
126	0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
127	0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
128	0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
129	0x3d, 0x42, 0x44, 0x0f, 0x00, 0x0a, 0x20, 0x20,
130	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
131	0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x46,
132	0x48, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x05,
133	},
134};
135
136static int edid_size(const u8 *edid, int data_size)
137{
138	if (data_size < EDID_LENGTH)
139		return 0;
140
141	return (edid[0x7e] + 1) * EDID_LENGTH;
142}
143
144static void *edid_load(struct drm_connector *connector, const char *name,
145			const char *connector_name)
146{
147	const struct firmware *fw = NULL;
148	const u8 *fwdata;
149	u8 *edid;
150	int fwsize, builtin;
151	int i, valid_extensions = 0;
152	bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS);
153
154	builtin = 0;
155	for (i = 0; i < GENERIC_EDIDS; i++) {
156		if (strcmp(name, generic_edid_name[i]) == 0) {
157			fwdata = generic_edid[i];
158			fwsize = sizeof(generic_edid[i]);
159			builtin = 1;
160			break;
161		}
162	}
163	if (!builtin) {
164		struct platform_device *pdev;
165		int err;
166
167		pdev = platform_device_register_simple(connector_name, -1, NULL, 0);
168		if (IS_ERR(pdev)) {
169			DRM_ERROR("Failed to register EDID firmware platform device "
170				  "for connector \"%s\"\n", connector_name);
171			return ERR_CAST(pdev);
172		}
173
174		err = request_firmware(&fw, name, &pdev->dev);
175		platform_device_unregister(pdev);
176		if (err) {
177			DRM_ERROR("Requesting EDID firmware \"%s\" failed (err=%d)\n",
178				  name, err);
179			return ERR_PTR(err);
180		}
181
182		fwdata = fw->data;
183		fwsize = fw->size;
184	}
185
186	if (edid_size(fwdata, fwsize) != fwsize) {
187		DRM_ERROR("Size of EDID firmware \"%s\" is invalid "
188			  "(expected %d, got %d\n", name,
189			  edid_size(fwdata, fwsize), (int)fwsize);
190		edid = ERR_PTR(-EINVAL);
191		goto out;
192	}
193
194	edid = kmemdup(fwdata, fwsize, GFP_KERNEL);
195	if (edid == NULL) {
196		edid = ERR_PTR(-ENOMEM);
197		goto out;
198	}
199
200	if (!drm_edid_block_valid(edid, 0, print_bad_edid)) {
201		connector->bad_edid_counter++;
202		DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ",
203		    name);
204		kfree(edid);
205		edid = ERR_PTR(-EINVAL);
206		goto out;
207	}
208
209	for (i = 1; i <= edid[0x7e]; i++) {
210		if (i != valid_extensions + 1)
211			memcpy(edid + (valid_extensions + 1) * EDID_LENGTH,
212			    edid + i * EDID_LENGTH, EDID_LENGTH);
213		if (drm_edid_block_valid(edid + i * EDID_LENGTH, i, print_bad_edid))
214			valid_extensions++;
215	}
216
217	if (valid_extensions != edid[0x7e]) {
218		u8 *new_edid;
219
220		edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions;
221		DRM_INFO("Found %d valid extensions instead of %d in EDID data "
222		    "\"%s\" for connector \"%s\"\n", valid_extensions,
223		    edid[0x7e], name, connector_name);
224		edid[0x7e] = valid_extensions;
225
226		new_edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
227				    GFP_KERNEL);
228		if (new_edid)
229			edid = new_edid;
230	}
231
232	DRM_INFO("Got %s EDID base block and %d extension%s from "
233	    "\"%s\" for connector \"%s\"\n", builtin ? "built-in" :
234	    "external", valid_extensions, valid_extensions == 1 ? "" : "s",
235	    name, connector_name);
236
237out:
238	if (fw)
239		release_firmware(fw);
240	return edid;
241}
242
243int drm_load_edid_firmware(struct drm_connector *connector)
244{
245	const char *connector_name = drm_get_connector_name(connector);
246	char *edidname = edid_firmware, *last, *colon;
247	int ret;
248	struct edid *edid;
249
250	if (*edidname == '\0')
251		return 0;
252
253	colon = strchr(edidname, ':');
254	if (colon != NULL) {
255		if (strncmp(connector_name, edidname, colon - edidname))
256			return 0;
257		edidname = colon + 1;
258		if (*edidname == '\0')
259			return 0;
260	}
261
262	last = edidname + strlen(edidname) - 1;
263	if (*last == '\n')
264		*last = '\0';
265
266	edid = edid_load(connector, edidname, connector_name);
267	if (IS_ERR_OR_NULL(edid))
268		return 0;
269
270	drm_mode_connector_update_edid_property(connector, edid);
271	ret = drm_add_edid_modes(connector, edid);
272	kfree(edid);
273
274	return ret;
275}