drm_edid_load.c 8.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
   drm_edid_load.c: use a built-in EDID data set or load it via the firmware
		    interface

   Copyright (C) 2012 Carsten Emde <C.Emde@osadl.org>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
*/

#include <linux/module.h>
#include <linux/firmware.h>
24 25 26 27
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
28 29 30 31 32 33

static char edid_firmware[PATH_MAX];
module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644);
MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob "
	"from built-in data or /lib/firmware instead. ");

34
#define GENERIC_EDIDS 5
35
static const char *generic_edid_name[GENERIC_EDIDS] = {
36 37
	"edid/1024x768.bin",
	"edid/1280x1024.bin",
38
	"edid/1600x1200.bin",
39 40 41 42
	"edid/1680x1050.bin",
	"edid/1920x1080.bin",
};

43
static const u8 generic_edid[GENERIC_EDIDS][128] = {
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
	{
	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x05, 0x16, 0x01, 0x03, 0x6d, 0x23, 0x1a, 0x78,
	0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
	0x20, 0x50, 0x54, 0x00, 0x08, 0x00, 0x61, 0x40,
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x64, 0x19,
	0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x08, 0x90,
	0x36, 0x00, 0x63, 0x0a, 0x11, 0x00, 0x00, 0x18,
	0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
	0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
	0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
	0x3d, 0x2f, 0x31, 0x07, 0x00, 0x0a, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
	0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x58,
	0x47, 0x41, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x55,
	},
	{
	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x05, 0x16, 0x01, 0x03, 0x6d, 0x2c, 0x23, 0x78,
	0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
	0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0x81, 0x80,
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x2a,
	0x00, 0x98, 0x51, 0x00, 0x2a, 0x40, 0x30, 0x70,
	0x13, 0x00, 0xbc, 0x63, 0x11, 0x00, 0x00, 0x1e,
	0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
	0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
	0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
	0x3d, 0x3e, 0x40, 0x0b, 0x00, 0x0a, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
	0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53,
	0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xa0,
	},
	{
	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
	0x05, 0x16, 0x01, 0x03, 0x6d, 0x37, 0x29, 0x78,
	0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
	0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xa9, 0x40,
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x48, 0x3f,
	0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, 0xc0,
	0x13, 0x00, 0x2b, 0xa0, 0x21, 0x00, 0x00, 0x1e,
	0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
	0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
	0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
	0x3d, 0x4a, 0x4c, 0x11, 0x00, 0x0a, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
	0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x55,
	0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0x9d,
	},
	{
	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
	0x05, 0x16, 0x01, 0x03, 0x6d, 0x2b, 0x1b, 0x78,
	0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
	0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xb3, 0x00,
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x21, 0x39,
	0x90, 0x30, 0x62, 0x1a, 0x27, 0x40, 0x68, 0xb0,
	0x36, 0x00, 0xb5, 0x11, 0x11, 0x00, 0x00, 0x1e,
	0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
	0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
	0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
	0x3d, 0x40, 0x42, 0x0f, 0x00, 0x0a, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
	0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x57,
	0x53, 0x58, 0x47, 0x41, 0x0a, 0x20, 0x00, 0x26,
	},
	{
	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
	0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x05, 0x16, 0x01, 0x03, 0x6d, 0x32, 0x1c, 0x78,
	0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
	0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xd1, 0xc0,
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a,
	0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
	0x45, 0x00, 0xf4, 0x19, 0x11, 0x00, 0x00, 0x1e,
	0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
	0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
	0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
	0x3d, 0x42, 0x44, 0x0f, 0x00, 0x0a, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
	0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x46,
	0x48, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x05,
	},
};

136 137 138 139 140 141 142 143
static int edid_size(const u8 *edid, int data_size)
{
	if (data_size < EDID_LENGTH)
		return 0;

	return (edid[0x7e] + 1) * EDID_LENGTH;
}

144 145
static u8 *edid_load(struct drm_connector *connector, const char *name,
			const char *connector_name)
146
{
147 148 149 150
	const struct firmware *fw = NULL;
	const u8 *fwdata;
	u8 *edid;
	int fwsize, builtin;
151
	int i, valid_extensions = 0;
152
	bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS);
153

154 155 156
	builtin = 0;
	for (i = 0; i < GENERIC_EDIDS; i++) {
		if (strcmp(name, generic_edid_name[i]) == 0) {
157 158
			fwdata = generic_edid[i];
			fwsize = sizeof(generic_edid[i]);
159 160
			builtin = 1;
			break;
161 162
		}
	}
163 164 165
	if (!builtin) {
		struct platform_device *pdev;
		int err;
166

167 168 169 170 171 172 173 174 175 176 177 178 179 180
		pdev = platform_device_register_simple(connector_name, -1, NULL, 0);
		if (IS_ERR(pdev)) {
			DRM_ERROR("Failed to register EDID firmware platform device "
				  "for connector \"%s\"\n", connector_name);
			return ERR_CAST(pdev);
		}

		err = request_firmware(&fw, name, &pdev->dev);
		platform_device_unregister(pdev);
		if (err) {
			DRM_ERROR("Requesting EDID firmware \"%s\" failed (err=%d)\n",
				  name, err);
			return ERR_PTR(err);
		}
181

182
		fwdata = fw->data;
183 184 185
		fwsize = fw->size;
	}

186
	if (edid_size(fwdata, fwsize) != fwsize) {
187
		DRM_ERROR("Size of EDID firmware \"%s\" is invalid "
188 189 190 191
			  "(expected %d, got %d\n", name,
			  edid_size(fwdata, fwsize), (int)fwsize);
		edid = ERR_PTR(-EINVAL);
		goto out;
192 193
	}

T
Thomas Meyer 已提交
194
	edid = kmemdup(fwdata, fwsize, GFP_KERNEL);
195
	if (edid == NULL) {
196 197
		edid = ERR_PTR(-ENOMEM);
		goto out;
198 199
	}

200 201
	if (!drm_edid_block_valid(edid, 0, print_bad_edid)) {
		connector->bad_edid_counter++;
202 203 204
		DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ",
		    name);
		kfree(edid);
205 206
		edid = ERR_PTR(-EINVAL);
		goto out;
207 208 209 210 211 212
	}

	for (i = 1; i <= edid[0x7e]; i++) {
		if (i != valid_extensions + 1)
			memcpy(edid + (valid_extensions + 1) * EDID_LENGTH,
			    edid + i * EDID_LENGTH, EDID_LENGTH);
213
		if (drm_edid_block_valid(edid + i * EDID_LENGTH, i, print_bad_edid))
214 215 216 217
			valid_extensions++;
	}

	if (valid_extensions != edid[0x7e]) {
218 219
		u8 *new_edid;

220 221 222 223 224
		edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions;
		DRM_INFO("Found %d valid extensions instead of %d in EDID data "
		    "\"%s\" for connector \"%s\"\n", valid_extensions,
		    edid[0x7e], name, connector_name);
		edid[0x7e] = valid_extensions;
225

226
		new_edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
227 228 229
				    GFP_KERNEL);
		if (new_edid)
			edid = new_edid;
230 231 232 233 234 235 236 237
	}

	DRM_INFO("Got %s EDID base block and %d extension%s from "
	    "\"%s\" for connector \"%s\"\n", builtin ? "built-in" :
	    "external", valid_extensions, valid_extensions == 1 ? "" : "s",
	    name, connector_name);

out:
238 239
	if (fw)
		release_firmware(fw);
240
	return edid;
241 242 243 244
}

int drm_load_edid_firmware(struct drm_connector *connector)
{
245
	const char *connector_name = drm_get_connector_name(connector);
246
	char *edidname = edid_firmware, *last, *colon;
247
	int ret;
248
	struct edid *edid;
249 250

	if (*edidname == '\0')
251
		return 0;
252 253 254 255

	colon = strchr(edidname, ':');
	if (colon != NULL) {
		if (strncmp(connector_name, edidname, colon - edidname))
256
			return 0;
257 258
		edidname = colon + 1;
		if (*edidname == '\0')
259
			return 0;
260 261 262 263 264 265
	}

	last = edidname + strlen(edidname) - 1;
	if (*last == '\n')
		*last = '\0';

266 267
	edid = (struct edid *) edid_load(connector, edidname, connector_name);
	if (IS_ERR_OR_NULL(edid))
268 269
		return 0;

270 271 272
	drm_mode_connector_update_edid_property(connector, edid);
	ret = drm_add_edid_modes(connector, edid);
	kfree(edid);
273

274
	return ret;
275
}