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