xref: /dragonfly/sys/dev/drm/drm_edid_load.c (revision a85cb24f)
1 /*
2  * Copyright (c) 2015 Rimvydas Jasinskas
3  *
4  * Simple EDID firmware handling routines derived from
5  * drm_edid.c:drm_do_get_edid()
6  *
7  * Copyright (c) 2007-2008 Intel Corporation
8  *   Jesse Barnes <jesse.barnes@intel.com>
9  * Copyright 2010 Red Hat, Inc.
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sub license,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice (including the
19  * next paragraph) shall be included in all copies or substantial portions
20  * of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  */
30 
31 #include <drm/drmP.h>
32 #include <drm/drm_edid.h>
33 #include <sys/bus.h>
34 #include <sys/firmware.h>
35 
36 #ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
37 static u8 *do_edid_fw_load(struct drm_connector *connector, const char *fwname,
38 			   const char *connector_name);
39 
40 static char edidfw_tun[256];
41 TUNABLE_STR("drm.edid_firmware", edidfw_tun, sizeof(edidfw_tun));
42 
drm_load_edid_firmware(struct drm_connector * connector)43 struct edid *drm_load_edid_firmware(struct drm_connector *connector)
44 {
45 	const char *connector_name = connector->name;
46 	char *cp, *fwname = edidfw_tun;
47 	struct edid *fwedid;
48 
49 	if (*fwname == '\0')
50 		return ERR_PTR(-ENOENT);
51 
52 	/* Check for connector specifier presence */
53 	if ((cp = strchr(fwname, ':')) != NULL) {
54 		/* if connector name doesn't match, we're done */
55 		if (strncmp(connector_name, fwname, cp - fwname))
56 			return ERR_PTR(-ENOENT);
57 		fwname = cp + 1;
58 		if (*fwname == '\0')
59 			return ERR_PTR(-ENOENT);
60 	}
61 
62 	fwedid = (struct edid *)do_edid_fw_load(connector, fwname, connector_name);
63 
64 	return fwedid;
65 }
66 
67 static u8 *
do_edid_fw_load(struct drm_connector * connector,const char * fwname,const char * connector_name)68 do_edid_fw_load(struct drm_connector *connector, const char *fwname,
69 			const char *connector_name)
70 {
71 	const struct firmware *fw = NULL;
72 	const u8 *fwdata;
73 	u8 *block = NULL, *new = NULL;
74 	int fwsize, expected;
75 	int j, valid_extensions = 0;
76 	bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS);
77 
78 	fw = firmware_get(fwname);
79 
80 	if (fw == NULL) {
81 		DRM_ERROR("Requesting EDID firmware %s failed\n", fwname);
82 		return (NULL);
83 	}
84 
85 	fwdata = fw->data;
86 	fwsize = fw->datasize;
87 
88 	if (fwsize < EDID_LENGTH)
89 		goto fw_out;
90 
91 	expected = (fwdata[0x7e] + 1) * EDID_LENGTH;
92 	if (expected != fwsize) {
93 		DRM_ERROR("Size of EDID firmware %s is invalid: %d vs %d(got)\n",
94 			  fwname, expected, fwsize);
95 		goto fw_out;
96 	}
97 
98 	block = kmalloc(fwsize, M_DRM, GFP_KERNEL);
99 	if (block == NULL) {
100 		goto fw_out;
101 	}
102 	memcpy(block, fwdata, fwsize);
103 
104 	/* now it is safe to release the firmware */
105 fw_out:
106 	fwdata = NULL;
107 	if (fw != NULL) {
108 		/*
109 		 * Don't release edid fw right away, useful if / is
110 		 * still not mounted and/or we performing early kms
111 		 */
112 		firmware_put(fw, 0);
113 	}
114 
115 	if (block == NULL)
116 		return (NULL);
117 
118 	/* first check the base block */
119 	if (!drm_edid_block_valid(block, 0, print_bad_edid, NULL)) {
120 		connector->bad_edid_counter++;
121 		DRM_ERROR("EDID firmware %s base block is invalid ", fwname);
122 		goto out;
123 	}
124 
125 	DRM_INFO("Got EDID base block from %s for connector %s\n", fwname, connector_name);
126 
127 	/* if there's no extensions, we're done */
128 	if (block[0x7e] == 0)
129 		return block;
130 
131 	/* XXX then extension blocks */
132 	WARN(1, "Loading EDID firmware with extensions is untested!\n");
133 
134 	for (j = 1; j <= block[0x7e]; j++) {
135 		/* if we skiped any extension block we have to shuffle good ones */
136 		if (j != valid_extensions + 1) {
137 			memcpy(block + (valid_extensions + 1) * EDID_LENGTH,
138 			       block + (j * EDID_LENGTH), EDID_LENGTH);
139 		}
140 		if (drm_edid_block_valid(block + j * EDID_LENGTH, j, print_bad_edid, NULL)) {
141 			valid_extensions++;
142 		}
143 	}
144 
145 	if (valid_extensions != block[0x7e]) {
146 		block[EDID_LENGTH-1] += block[0x7e] - valid_extensions;
147 		block[0x7e] = valid_extensions;
148 		new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH, M_DRM, M_WAITOK);
149 		if (new == NULL)
150 			goto out;
151 		block = new;
152 	}
153 
154 	if (valid_extensions > 0) {
155 		DRM_INFO("Got %d extensions in EDID firmware from %s for connector %s\n",
156 			 valid_extensions, fwname, connector_name);
157 	}
158 
159 	/* if got to here return edid block */
160 	return block;
161 
162 out:
163 	kfree(block);
164 	return (NULL);
165 }
166 
167 #endif /* CONFIG_DRM_LOAD_EDID_FIRMWARE */
168