Merge branch 'for-3.5-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj...
[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 "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, 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                 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;
224
225         if (*edidname == '\0')
226                 return 0;
227
228         colon = strchr(edidname, ':');
229         if (colon != NULL) {
230                 if (strncmp(connector_name, edidname, colon - edidname))
231                         return 0;
232                 edidname = colon + 1;
233                 if (*edidname == '\0')
234                         return 0;
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 }