Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[firefly-linux-kernel-4.4.55.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 #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 u8 *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         bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS);
127
128         pdev = platform_device_register_simple(connector_name, -1, NULL, 0);
129         if (IS_ERR(pdev)) {
130                 DRM_ERROR("Failed to register EDID firmware platform device "
131                     "for connector \"%s\"\n", connector_name);
132                 err = -EINVAL;
133                 goto out;
134         }
135
136         err = request_firmware(&fw, name, &pdev->dev);
137         platform_device_unregister(pdev);
138
139         if (err) {
140                 i = 0;
141                 while (i < GENERIC_EDIDS && strcmp(name, generic_edid_name[i]))
142                         i++;
143                 if (i < GENERIC_EDIDS) {
144                         err = 0;
145                         builtin = 1;
146                         fwdata = generic_edid[i];
147                         fwsize = sizeof(generic_edid[i]);
148                 }
149         }
150
151         if (err) {
152                 DRM_ERROR("Requesting EDID firmware \"%s\" failed (err=%d)\n",
153                     name, err);
154                 goto out;
155         }
156
157         if (fwdata == NULL) {
158                 fwdata = (u8 *) fw->data;
159                 fwsize = fw->size;
160         }
161
162         expected = (fwdata[0x7e] + 1) * EDID_LENGTH;
163         if (expected != fwsize) {
164                 DRM_ERROR("Size of EDID firmware \"%s\" is invalid "
165                     "(expected %d, got %d)\n", name, expected, (int) fwsize);
166                 err = -EINVAL;
167                 goto relfw_out;
168         }
169
170         edid = kmalloc(fwsize, GFP_KERNEL);
171         if (edid == NULL) {
172                 err = -ENOMEM;
173                 goto relfw_out;
174         }
175         memcpy(edid, fwdata, fwsize);
176
177         if (!drm_edid_block_valid(edid, 0, print_bad_edid)) {
178                 connector->bad_edid_counter++;
179                 DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ",
180                     name);
181                 kfree(edid);
182                 err = -EINVAL;
183                 goto relfw_out;
184         }
185
186         for (i = 1; i <= edid[0x7e]; i++) {
187                 if (i != valid_extensions + 1)
188                         memcpy(edid + (valid_extensions + 1) * EDID_LENGTH,
189                             edid + i * EDID_LENGTH, EDID_LENGTH);
190                 if (drm_edid_block_valid(edid + i * EDID_LENGTH, i, print_bad_edid))
191                         valid_extensions++;
192         }
193
194         if (valid_extensions != edid[0x7e]) {
195                 edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions;
196                 DRM_INFO("Found %d valid extensions instead of %d in EDID data "
197                     "\"%s\" for connector \"%s\"\n", valid_extensions,
198                     edid[0x7e], name, connector_name);
199                 edid[0x7e] = valid_extensions;
200                 new_edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
201                     GFP_KERNEL);
202                 if (new_edid == NULL) {
203                         err = -ENOMEM;
204                         kfree(edid);
205                         goto relfw_out;
206                 }
207                 edid = new_edid;
208         }
209
210         DRM_INFO("Got %s EDID base block and %d extension%s from "
211             "\"%s\" for connector \"%s\"\n", builtin ? "built-in" :
212             "external", valid_extensions, valid_extensions == 1 ? "" : "s",
213             name, connector_name);
214
215 relfw_out:
216         release_firmware(fw);
217
218 out:
219         if (err)
220                 return ERR_PTR(err);
221
222         return edid;
223 }
224
225 int drm_load_edid_firmware(struct drm_connector *connector)
226 {
227         char *connector_name = drm_get_connector_name(connector);
228         char *edidname = edid_firmware, *last, *colon;
229         int ret;
230         struct edid *edid;
231
232         if (*edidname == '\0')
233                 return 0;
234
235         colon = strchr(edidname, ':');
236         if (colon != NULL) {
237                 if (strncmp(connector_name, edidname, colon - edidname))
238                         return 0;
239                 edidname = colon + 1;
240                 if (*edidname == '\0')
241                         return 0;
242         }
243
244         last = edidname + strlen(edidname) - 1;
245         if (*last == '\n')
246                 *last = '\0';
247
248         edid = (struct edid *) edid_load(connector, edidname, connector_name);
249         if (IS_ERR_OR_NULL(edid))
250                 return 0;
251
252         drm_mode_connector_update_edid_property(connector, edid);
253         ret = drm_add_edid_modes(connector, edid);
254         kfree(edid);
255
256         return ret;
257 }