]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/gpu/drm/drm_edid_load.c
Merge tag 'libnvdimm-fixes-5.2-rc2' of git://git.kernel.org/pub/scm/linux/kernel...
[linux.git] / drivers / gpu / drm / drm_edid_load.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3    drm_edid_load.c: use a built-in EDID data set or load it via the firmware
4                     interface
5
6    Copyright (C) 2012 Carsten Emde <C.Emde@osadl.org>
7
8 */
9
10 #include <linux/module.h>
11 #include <linux/firmware.h>
12 #include <drm/drmP.h>
13 #include <drm/drm_crtc.h>
14 #include <drm/drm_crtc_helper.h>
15 #include <drm/drm_edid.h>
16
17 static char edid_firmware[PATH_MAX];
18 module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644);
19 MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob "
20         "from built-in data or /lib/firmware instead. ");
21
22 /* Use only for backward compatibility with drm_kms_helper.edid_firmware */
23 int __drm_set_edid_firmware_path(const char *path)
24 {
25         scnprintf(edid_firmware, sizeof(edid_firmware), "%s", path);
26
27         return 0;
28 }
29 EXPORT_SYMBOL(__drm_set_edid_firmware_path);
30
31 /* Use only for backward compatibility with drm_kms_helper.edid_firmware */
32 int __drm_get_edid_firmware_path(char *buf, size_t bufsize)
33 {
34         return scnprintf(buf, bufsize, "%s", edid_firmware);
35 }
36 EXPORT_SYMBOL(__drm_get_edid_firmware_path);
37
38 #define GENERIC_EDIDS 6
39 static const char * const generic_edid_name[GENERIC_EDIDS] = {
40         "edid/800x600.bin",
41         "edid/1024x768.bin",
42         "edid/1280x1024.bin",
43         "edid/1600x1200.bin",
44         "edid/1680x1050.bin",
45         "edid/1920x1080.bin",
46 };
47
48 static const u8 generic_edid[GENERIC_EDIDS][128] = {
49         {
50         0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
51         0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
52         0x05, 0x16, 0x01, 0x03, 0x6d, 0x1b, 0x14, 0x78,
53         0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
54         0x20, 0x50, 0x54, 0x01, 0x00, 0x00, 0x45, 0x40,
55         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
56         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xa0, 0x0f,
57         0x20, 0x00, 0x31, 0x58, 0x1c, 0x20, 0x28, 0x80,
58         0x14, 0x00, 0x15, 0xd0, 0x10, 0x00, 0x00, 0x1e,
59         0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
60         0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
61         0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
62         0x3d, 0x24, 0x26, 0x05, 0x00, 0x0a, 0x20, 0x20,
63         0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
64         0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53,
65         0x56, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xc2,
66         },
67         {
68         0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
69         0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
70         0x05, 0x16, 0x01, 0x03, 0x6d, 0x23, 0x1a, 0x78,
71         0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
72         0x20, 0x50, 0x54, 0x00, 0x08, 0x00, 0x61, 0x40,
73         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
74         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x64, 0x19,
75         0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x08, 0x90,
76         0x36, 0x00, 0x63, 0x0a, 0x11, 0x00, 0x00, 0x18,
77         0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
78         0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
79         0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
80         0x3d, 0x2f, 0x31, 0x07, 0x00, 0x0a, 0x20, 0x20,
81         0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
82         0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x58,
83         0x47, 0x41, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x55,
84         },
85         {
86         0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
87         0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
88         0x05, 0x16, 0x01, 0x03, 0x6d, 0x2c, 0x23, 0x78,
89         0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
90         0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0x81, 0x80,
91         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
92         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x2a,
93         0x00, 0x98, 0x51, 0x00, 0x2a, 0x40, 0x30, 0x70,
94         0x13, 0x00, 0xbc, 0x63, 0x11, 0x00, 0x00, 0x1e,
95         0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
96         0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
97         0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
98         0x3d, 0x3e, 0x40, 0x0b, 0x00, 0x0a, 0x20, 0x20,
99         0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
100         0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53,
101         0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xa0,
102         },
103         {
104         0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
105         0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106         0x05, 0x16, 0x01, 0x03, 0x6d, 0x37, 0x29, 0x78,
107         0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
108         0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xa9, 0x40,
109         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
110         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x48, 0x3f,
111         0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, 0xc0,
112         0x13, 0x00, 0x2b, 0xa0, 0x21, 0x00, 0x00, 0x1e,
113         0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
114         0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
115         0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
116         0x3d, 0x4a, 0x4c, 0x11, 0x00, 0x0a, 0x20, 0x20,
117         0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
118         0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x55,
119         0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0x9d,
120         },
121         {
122         0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
123         0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124         0x05, 0x16, 0x01, 0x03, 0x6d, 0x2b, 0x1b, 0x78,
125         0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
126         0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xb3, 0x00,
127         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
128         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x21, 0x39,
129         0x90, 0x30, 0x62, 0x1a, 0x27, 0x40, 0x68, 0xb0,
130         0x36, 0x00, 0xb5, 0x11, 0x11, 0x00, 0x00, 0x1e,
131         0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
132         0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
133         0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
134         0x3d, 0x40, 0x42, 0x0f, 0x00, 0x0a, 0x20, 0x20,
135         0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
136         0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x57,
137         0x53, 0x58, 0x47, 0x41, 0x0a, 0x20, 0x00, 0x26,
138         },
139         {
140         0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
141         0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
142         0x05, 0x16, 0x01, 0x03, 0x6d, 0x32, 0x1c, 0x78,
143         0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
144         0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xd1, 0xc0,
145         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
146         0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a,
147         0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
148         0x45, 0x00, 0xf4, 0x19, 0x11, 0x00, 0x00, 0x1e,
149         0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
150         0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
151         0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
152         0x3d, 0x42, 0x44, 0x0f, 0x00, 0x0a, 0x20, 0x20,
153         0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
154         0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x46,
155         0x48, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x05,
156         },
157 };
158
159 static int edid_size(const u8 *edid, int data_size)
160 {
161         if (data_size < EDID_LENGTH)
162                 return 0;
163
164         return (edid[0x7e] + 1) * EDID_LENGTH;
165 }
166
167 static void *edid_load(struct drm_connector *connector, const char *name,
168                         const char *connector_name)
169 {
170         const struct firmware *fw = NULL;
171         const u8 *fwdata;
172         u8 *edid;
173         int fwsize, builtin;
174         int i, valid_extensions = 0;
175         bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS);
176
177         builtin = match_string(generic_edid_name, GENERIC_EDIDS, name);
178         if (builtin >= 0) {
179                 fwdata = generic_edid[builtin];
180                 fwsize = sizeof(generic_edid[builtin]);
181         } else {
182                 struct platform_device *pdev;
183                 int err;
184
185                 pdev = platform_device_register_simple(connector_name, -1, NULL, 0);
186                 if (IS_ERR(pdev)) {
187                         DRM_ERROR("Failed to register EDID firmware platform device "
188                                   "for connector \"%s\"\n", connector_name);
189                         return ERR_CAST(pdev);
190                 }
191
192                 err = request_firmware(&fw, name, &pdev->dev);
193                 platform_device_unregister(pdev);
194                 if (err) {
195                         DRM_ERROR("Requesting EDID firmware \"%s\" failed (err=%d)\n",
196                                   name, err);
197                         return ERR_PTR(err);
198                 }
199
200                 fwdata = fw->data;
201                 fwsize = fw->size;
202         }
203
204         if (edid_size(fwdata, fwsize) != fwsize) {
205                 DRM_ERROR("Size of EDID firmware \"%s\" is invalid "
206                           "(expected %d, got %d\n", name,
207                           edid_size(fwdata, fwsize), (int)fwsize);
208                 edid = ERR_PTR(-EINVAL);
209                 goto out;
210         }
211
212         edid = kmemdup(fwdata, fwsize, GFP_KERNEL);
213         if (edid == NULL) {
214                 edid = ERR_PTR(-ENOMEM);
215                 goto out;
216         }
217
218         if (!drm_edid_block_valid(edid, 0, print_bad_edid,
219                                   &connector->edid_corrupt)) {
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,
233                                          print_bad_edid,
234                                          NULL))
235                         valid_extensions++;
236         }
237
238         if (valid_extensions != edid[0x7e]) {
239                 u8 *new_edid;
240
241                 edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions;
242                 DRM_INFO("Found %d valid extensions instead of %d in EDID data "
243                     "\"%s\" for connector \"%s\"\n", valid_extensions,
244                     edid[0x7e], name, connector_name);
245                 edid[0x7e] = valid_extensions;
246
247                 new_edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
248                                     GFP_KERNEL);
249                 if (new_edid)
250                         edid = new_edid;
251         }
252
253         DRM_INFO("Got %s EDID base block and %d extension%s from "
254             "\"%s\" for connector \"%s\"\n", (builtin >= 0) ? "built-in" :
255             "external", valid_extensions, valid_extensions == 1 ? "" : "s",
256             name, connector_name);
257
258 out:
259         release_firmware(fw);
260         return edid;
261 }
262
263 struct edid *drm_load_edid_firmware(struct drm_connector *connector)
264 {
265         const char *connector_name = connector->name;
266         char *edidname, *last, *colon, *fwstr, *edidstr, *fallback = NULL;
267         struct edid *edid;
268
269         if (edid_firmware[0] == '\0')
270                 return ERR_PTR(-ENOENT);
271
272         /*
273          * If there are multiple edid files specified and separated
274          * by commas, search through the list looking for one that
275          * matches the connector.
276          *
277          * If there's one or more that doesn't specify a connector, keep
278          * the last one found one as a fallback.
279          */
280         fwstr = kstrdup(edid_firmware, GFP_KERNEL);
281         edidstr = fwstr;
282
283         while ((edidname = strsep(&edidstr, ","))) {
284                 colon = strchr(edidname, ':');
285                 if (colon != NULL) {
286                         if (strncmp(connector_name, edidname, colon - edidname))
287                                 continue;
288                         edidname = colon + 1;
289                         break;
290                 }
291
292                 if (*edidname != '\0') /* corner case: multiple ',' */
293                         fallback = edidname;
294         }
295
296         if (!edidname) {
297                 if (!fallback) {
298                         kfree(fwstr);
299                         return ERR_PTR(-ENOENT);
300                 }
301                 edidname = fallback;
302         }
303
304         last = edidname + strlen(edidname) - 1;
305         if (*last == '\n')
306                 *last = '\0';
307
308         edid = edid_load(connector, edidname, connector_name);
309         kfree(fwstr);
310
311         return edid;
312 }