ipfw: Add icmpcodes support.
[dragonfly.git] / sys / dev / drm / drm_edid_load.c
blob48792752b3b0c2520e8005267dcadb53e527d852
1 /*
2 * Copyright (c) 2015 Rimvydas Jasinskas
4 * Simple EDID firmware handling routines derived from
5 * drm_edid.c:drm_do_get_edid()
7 * Copyright (c) 2007-2008 Intel Corporation
8 * Jesse Barnes <jesse.barnes@intel.com>
9 * Copyright 2010 Red Hat, Inc.
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:
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.
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.
31 #include <drm/drmP.h>
32 #include <drm/drm_edid.h>
33 #include <sys/bus.h>
34 #include <sys/firmware.h>
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);
40 static char edidfw_tun[256];
41 TUNABLE_STR("drm.edid_firmware", edidfw_tun, sizeof(edidfw_tun));
43 int drm_load_edid_firmware(struct drm_connector *connector)
45 const char *connector_name = connector->name;
46 char *cp, *fwname = edidfw_tun;
47 struct edid *fwedid;
48 int ret;
50 if (*fwname == '\0')
51 return 0;
53 /* Check for connector specifier presence */
54 if ((cp = strchr(fwname, ':')) != NULL) {
55 /* if connector name doesn't match, we're done */
56 if (strncmp(connector_name, fwname, cp - fwname))
57 return 0;
58 fwname = cp + 1;
59 if (*fwname == '\0')
60 return 0;
63 fwedid = (struct edid *)do_edid_fw_load(connector, fwname, connector_name);
64 if (fwedid == NULL)
65 return 0;
67 drm_mode_connector_update_edid_property(connector, fwedid);
68 ret = drm_add_edid_modes(connector, fwedid);
69 kfree(fwedid);
71 return ret;
74 static u8 *
75 do_edid_fw_load(struct drm_connector *connector, const char *fwname,
76 const char *connector_name)
78 const struct firmware *fw = NULL;
79 const u8 *fwdata;
80 u8 *block = NULL, *new = NULL;
81 int fwsize, expected;
82 int j, valid_extensions = 0;
83 bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS);
85 fw = firmware_get(fwname);
87 if (fw == NULL) {
88 DRM_ERROR("Requesting EDID firmware %s failed\n", fwname);
89 return (NULL);
92 fwdata = fw->data;
93 fwsize = fw->datasize;
95 if (fwsize < EDID_LENGTH)
96 goto fw_out;
98 expected = (fwdata[0x7e] + 1) * EDID_LENGTH;
99 if (expected != fwsize) {
100 DRM_ERROR("Size of EDID firmware %s is invalid: %d vs %d(got)\n",
101 fwname, expected, fwsize);
102 goto fw_out;
105 block = kmalloc(fwsize, M_DRM, GFP_KERNEL);
106 if (block == NULL) {
107 goto fw_out;
109 memcpy(block, fwdata, fwsize);
111 /* now it is safe to release the firmware */
112 fw_out:
113 fwdata = NULL;
114 if (fw != NULL) {
116 * Don't release edid fw right away, useful if / is
117 * still not mounted and/or we performing early kms
119 firmware_put(fw, 0);
122 if (block == NULL)
123 return (NULL);
125 /* first check the base block */
126 if (!drm_edid_block_valid(block, 0, print_bad_edid, NULL)) {
127 connector->bad_edid_counter++;
128 DRM_ERROR("EDID firmware %s base block is invalid ", fwname);
129 goto out;
132 DRM_INFO("Got EDID base block from %s for connector %s\n", fwname, connector_name);
134 /* if there's no extensions, we're done */
135 if (block[0x7e] == 0)
136 return block;
138 /* XXX then extension blocks */
139 WARN(1, "Loading EDID firmware with extensions is untested!\n");
141 for (j = 1; j <= block[0x7e]; j++) {
142 /* if we skiped any extension block we have to shuffle good ones */
143 if (j != valid_extensions + 1) {
144 memcpy(block + (valid_extensions + 1) * EDID_LENGTH,
145 block + (j * EDID_LENGTH), EDID_LENGTH);
147 if (drm_edid_block_valid(block + j * EDID_LENGTH, j, print_bad_edid, NULL)) {
148 valid_extensions++;
152 if (valid_extensions != block[0x7e]) {
153 block[EDID_LENGTH-1] += block[0x7e] - valid_extensions;
154 block[0x7e] = valid_extensions;
155 new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH, M_DRM, M_WAITOK);
156 if (new == NULL)
157 goto out;
158 block = new;
161 if (valid_extensions > 0) {
162 DRM_INFO("Got %d extensions in EDID firmware from %s for connector %s\n",
163 valid_extensions, fwname, connector_name);
166 /* if got to here return edid block */
167 return block;
169 out:
170 kfree(block);
171 return (NULL);
174 #endif /* CONFIG_DRM_LOAD_EDID_FIRMWARE */