Merge remote-tracking branch 'asoc/fix/rcar' into asoc-linus
[sfrench/cifs-2.6.git] / drivers / gpu / drm / drm_edid_load.c
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
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 /* Use only for backward compatibility with drm_kms_helper.edid_firmware */
35 int __drm_set_edid_firmware_path(const char *path)
36 {
37         scnprintf(edid_firmware, sizeof(edid_firmware), "%s", path);
38
39         return 0;
40 }
41 EXPORT_SYMBOL(__drm_set_edid_firmware_path);
42
43 /* Use only for backward compatibility with drm_kms_helper.edid_firmware */
44 int __drm_get_edid_firmware_path(char *buf, size_t bufsize)
45 {
46         return scnprintf(buf, bufsize, "%s", edid_firmware);
47 }
48 EXPORT_SYMBOL(__drm_get_edid_firmware_path);
49
50 #define GENERIC_EDIDS 6
51 static const char * const generic_edid_name[GENERIC_EDIDS] = {
52         "edid/800x600.bin",
53         "edid/1024x768.bin",
54         "edid/1280x1024.bin",
55         "edid/1600x1200.bin",
56         "edid/1680x1050.bin",
57         "edid/1920x1080.bin",
58 };
59
60 static const u8 generic_edid[GENERIC_EDIDS][128] = {
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, 0x1b, 0x14, 0x78,
65         0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
66         0x20, 0x50, 0x54, 0x01, 0x00, 0x00, 0x45, 0x40,
67         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
68         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xa0, 0x0f,
69         0x20, 0x00, 0x31, 0x58, 0x1c, 0x20, 0x28, 0x80,
70         0x14, 0x00, 0x15, 0xd0, 0x10, 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, 0x24, 0x26, 0x05, 0x00, 0x0a, 0x20, 0x20,
75         0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
76         0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53,
77         0x56, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xc2,
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, 0x23, 0x1a, 0x78,
83         0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
84         0x20, 0x50, 0x54, 0x00, 0x08, 0x00, 0x61, 0x40,
85         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
86         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x64, 0x19,
87         0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x08, 0x90,
88         0x36, 0x00, 0x63, 0x0a, 0x11, 0x00, 0x00, 0x18,
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, 0x2f, 0x31, 0x07, 0x00, 0x0a, 0x20, 0x20,
93         0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
94         0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x58,
95         0x47, 0x41, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x55,
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, 0x2c, 0x23, 0x78,
101         0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
102         0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0x81, 0x80,
103         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
104         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x2a,
105         0x00, 0x98, 0x51, 0x00, 0x2a, 0x40, 0x30, 0x70,
106         0x13, 0x00, 0xbc, 0x63, 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, 0x3e, 0x40, 0x0b, 0x00, 0x0a, 0x20, 0x20,
111         0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
112         0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53,
113         0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xa0,
114         },
115         {
116         0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
117         0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118         0x05, 0x16, 0x01, 0x03, 0x6d, 0x37, 0x29, 0x78,
119         0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
120         0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xa9, 0x40,
121         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
122         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x48, 0x3f,
123         0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, 0xc0,
124         0x13, 0x00, 0x2b, 0xa0, 0x21, 0x00, 0x00, 0x1e,
125         0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
126         0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
127         0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
128         0x3d, 0x4a, 0x4c, 0x11, 0x00, 0x0a, 0x20, 0x20,
129         0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
130         0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x55,
131         0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0x9d,
132         },
133         {
134         0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
135         0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
136         0x05, 0x16, 0x01, 0x03, 0x6d, 0x2b, 0x1b, 0x78,
137         0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
138         0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xb3, 0x00,
139         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
140         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x21, 0x39,
141         0x90, 0x30, 0x62, 0x1a, 0x27, 0x40, 0x68, 0xb0,
142         0x36, 0x00, 0xb5, 0x11, 0x11, 0x00, 0x00, 0x1e,
143         0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
144         0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
145         0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
146         0x3d, 0x40, 0x42, 0x0f, 0x00, 0x0a, 0x20, 0x20,
147         0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
148         0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x57,
149         0x53, 0x58, 0x47, 0x41, 0x0a, 0x20, 0x00, 0x26,
150         },
151         {
152         0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
153         0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
154         0x05, 0x16, 0x01, 0x03, 0x6d, 0x32, 0x1c, 0x78,
155         0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
156         0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xd1, 0xc0,
157         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
158         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a,
159         0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
160         0x45, 0x00, 0xf4, 0x19, 0x11, 0x00, 0x00, 0x1e,
161         0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
162         0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
163         0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
164         0x3d, 0x42, 0x44, 0x0f, 0x00, 0x0a, 0x20, 0x20,
165         0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
166         0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x46,
167         0x48, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x05,
168         },
169 };
170
171 static int edid_size(const u8 *edid, int data_size)
172 {
173         if (data_size < EDID_LENGTH)
174                 return 0;
175
176         return (edid[0x7e] + 1) * EDID_LENGTH;
177 }
178
179 static void *edid_load(struct drm_connector *connector, const char *name,
180                         const char *connector_name)
181 {
182         const struct firmware *fw = NULL;
183         const u8 *fwdata;
184         u8 *edid;
185         int fwsize, builtin;
186         int i, valid_extensions = 0;
187         bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS);
188
189         builtin = match_string(generic_edid_name, GENERIC_EDIDS, name);
190         if (builtin >= 0) {
191                 fwdata = generic_edid[builtin];
192                 fwsize = sizeof(generic_edid[builtin]);
193         } else {
194                 struct platform_device *pdev;
195                 int err;
196
197                 pdev = platform_device_register_simple(connector_name, -1, NULL, 0);
198                 if (IS_ERR(pdev)) {
199                         DRM_ERROR("Failed to register EDID firmware platform device "
200                                   "for connector \"%s\"\n", connector_name);
201                         return ERR_CAST(pdev);
202                 }
203
204                 err = request_firmware(&fw, name, &pdev->dev);
205                 platform_device_unregister(pdev);
206                 if (err) {
207                         DRM_ERROR("Requesting EDID firmware \"%s\" failed (err=%d)\n",
208                                   name, err);
209                         return ERR_PTR(err);
210                 }
211
212                 fwdata = fw->data;
213                 fwsize = fw->size;
214         }
215
216         if (edid_size(fwdata, fwsize) != fwsize) {
217                 DRM_ERROR("Size of EDID firmware \"%s\" is invalid "
218                           "(expected %d, got %d\n", name,
219                           edid_size(fwdata, fwsize), (int)fwsize);
220                 edid = ERR_PTR(-EINVAL);
221                 goto out;
222         }
223
224         edid = kmemdup(fwdata, fwsize, GFP_KERNEL);
225         if (edid == NULL) {
226                 edid = ERR_PTR(-ENOMEM);
227                 goto out;
228         }
229
230         if (!drm_edid_block_valid(edid, 0, print_bad_edid,
231                                   &connector->edid_corrupt)) {
232                 connector->bad_edid_counter++;
233                 DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ",
234                     name);
235                 kfree(edid);
236                 edid = ERR_PTR(-EINVAL);
237                 goto out;
238         }
239
240         for (i = 1; i <= edid[0x7e]; i++) {
241                 if (i != valid_extensions + 1)
242                         memcpy(edid + (valid_extensions + 1) * EDID_LENGTH,
243                             edid + i * EDID_LENGTH, EDID_LENGTH);
244                 if (drm_edid_block_valid(edid + i * EDID_LENGTH, i,
245                                          print_bad_edid,
246                                          NULL))
247                         valid_extensions++;
248         }
249
250         if (valid_extensions != edid[0x7e]) {
251                 u8 *new_edid;
252
253                 edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions;
254                 DRM_INFO("Found %d valid extensions instead of %d in EDID data "
255                     "\"%s\" for connector \"%s\"\n", valid_extensions,
256                     edid[0x7e], name, connector_name);
257                 edid[0x7e] = valid_extensions;
258
259                 new_edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
260                                     GFP_KERNEL);
261                 if (new_edid)
262                         edid = new_edid;
263         }
264
265         DRM_INFO("Got %s EDID base block and %d extension%s from "
266             "\"%s\" for connector \"%s\"\n", (builtin >= 0) ? "built-in" :
267             "external", valid_extensions, valid_extensions == 1 ? "" : "s",
268             name, connector_name);
269
270 out:
271         release_firmware(fw);
272         return edid;
273 }
274
275 struct edid *drm_load_edid_firmware(struct drm_connector *connector)
276 {
277         const char *connector_name = connector->name;
278         char *edidname, *last, *colon, *fwstr, *edidstr, *fallback = NULL;
279         struct edid *edid;
280
281         if (edid_firmware[0] == '\0')
282                 return ERR_PTR(-ENOENT);
283
284         /*
285          * If there are multiple edid files specified and separated
286          * by commas, search through the list looking for one that
287          * matches the connector.
288          *
289          * If there's one or more that doesn't specify a connector, keep
290          * the last one found one as a fallback.
291          */
292         fwstr = kstrdup(edid_firmware, GFP_KERNEL);
293         edidstr = fwstr;
294
295         while ((edidname = strsep(&edidstr, ","))) {
296                 colon = strchr(edidname, ':');
297                 if (colon != NULL) {
298                         if (strncmp(connector_name, edidname, colon - edidname))
299                                 continue;
300                         edidname = colon + 1;
301                         break;
302                 }
303
304                 if (*edidname != '\0') /* corner case: multiple ',' */
305                         fallback = edidname;
306         }
307
308         if (!edidname) {
309                 if (!fallback) {
310                         kfree(fwstr);
311                         return ERR_PTR(-ENOENT);
312                 }
313                 edidname = fallback;
314         }
315
316         last = edidname + strlen(edidname) - 1;
317         if (*last == '\n')
318                 *last = '\0';
319
320         edid = edid_load(connector, edidname, connector_name);
321         kfree(fwstr);
322
323         return edid;
324 }