xref: /openbmc/linux/drivers/gpu/drm/drm_edid_load.c (revision 63dc02bd)
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;
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)) {
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))
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 		edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
199 		    GFP_KERNEL);
200 		if (edid == NULL) {
201 			err = -ENOMEM;
202 			goto relfw_out;
203 		}
204 	}
205 
206 	connector->display_info.raw_edid = edid;
207 	DRM_INFO("Got %s EDID base block and %d extension%s from "
208 	    "\"%s\" for connector \"%s\"\n", builtin ? "built-in" :
209 	    "external", valid_extensions, valid_extensions == 1 ? "" : "s",
210 	    name, connector_name);
211 
212 relfw_out:
213 	release_firmware(fw);
214 
215 out:
216 	return err;
217 }
218 
219 int drm_load_edid_firmware(struct drm_connector *connector)
220 {
221 	char *connector_name = drm_get_connector_name(connector);
222 	char *edidname = edid_firmware, *last, *colon;
223 	int ret = 0;
224 
225 	if (*edidname == '\0')
226 		return ret;
227 
228 	colon = strchr(edidname, ':');
229 	if (colon != NULL) {
230 		if (strncmp(connector_name, edidname, colon - edidname))
231 			return ret;
232 		edidname = colon + 1;
233 		if (*edidname == '\0')
234 			return ret;
235 	}
236 
237 	last = edidname + strlen(edidname) - 1;
238 	if (*last == '\n')
239 		*last = '\0';
240 
241 	ret = edid_load(connector, edidname, connector_name);
242 	if (ret)
243 		return 0;
244 
245 	drm_mode_connector_update_edid_property(connector,
246 	    (struct edid *) connector->display_info.raw_edid);
247 
248 	return drm_add_edid_modes(connector, (struct edid *)
249 	    connector->display_info.raw_edid);
250 }
251