Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/drm/drm_edid_load.c
26444 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
drm_edid_load.c: use a built-in EDID data set or load it via the firmware
4
interface
5
6
Copyright (C) 2012 Carsten Emde <[email protected]>
7
8
*/
9
10
#include <linux/firmware.h>
11
#include <linux/module.h>
12
#include <linux/platform_device.h>
13
14
#include <drm/drm_connector.h>
15
#include <drm/drm_drv.h>
16
#include <drm/drm_edid.h>
17
#include <drm/drm_print.h>
18
19
#include "drm_crtc_internal.h"
20
21
static char edid_firmware[PATH_MAX];
22
module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644);
23
MODULE_PARM_DESC(edid_firmware,
24
"Do not probe monitor, use specified EDID blob from /lib/firmware instead.");
25
26
static const struct drm_edid *edid_load(struct drm_connector *connector, const char *name)
27
{
28
const struct firmware *fw = NULL;
29
const struct drm_edid *drm_edid;
30
int err;
31
32
err = request_firmware(&fw, name, connector->dev->dev);
33
if (err) {
34
drm_err(connector->dev,
35
"[CONNECTOR:%d:%s] Requesting EDID firmware \"%s\" failed (err=%d)\n",
36
connector->base.id, connector->name,
37
name, err);
38
return ERR_PTR(err);
39
}
40
41
drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Loaded external firmware EDID \"%s\"\n",
42
connector->base.id, connector->name, name);
43
44
drm_edid = drm_edid_alloc(fw->data, fw->size);
45
if (!drm_edid_valid(drm_edid)) {
46
drm_err(connector->dev, "Invalid firmware EDID \"%s\"\n", name);
47
drm_edid_free(drm_edid);
48
drm_edid = ERR_PTR(-EINVAL);
49
}
50
51
release_firmware(fw);
52
53
return drm_edid;
54
}
55
56
const struct drm_edid *drm_edid_load_firmware(struct drm_connector *connector)
57
{
58
char *edidname, *last, *colon, *fwstr, *edidstr, *fallback = NULL;
59
const struct drm_edid *drm_edid;
60
61
if (edid_firmware[0] == '\0')
62
return ERR_PTR(-ENOENT);
63
64
/*
65
* If there are multiple edid files specified and separated
66
* by commas, search through the list looking for one that
67
* matches the connector.
68
*
69
* If there's one or more that doesn't specify a connector, keep
70
* the last one found one as a fallback.
71
*/
72
fwstr = kstrdup(edid_firmware, GFP_KERNEL);
73
if (!fwstr)
74
return ERR_PTR(-ENOMEM);
75
edidstr = fwstr;
76
77
while ((edidname = strsep(&edidstr, ","))) {
78
colon = strchr(edidname, ':');
79
if (colon != NULL) {
80
if (strncmp(connector->name, edidname, colon - edidname))
81
continue;
82
edidname = colon + 1;
83
break;
84
}
85
86
if (*edidname != '\0') /* corner case: multiple ',' */
87
fallback = edidname;
88
}
89
90
if (!edidname) {
91
if (!fallback) {
92
kfree(fwstr);
93
return ERR_PTR(-ENOENT);
94
}
95
edidname = fallback;
96
}
97
98
last = edidname + strlen(edidname) - 1;
99
if (*last == '\n')
100
*last = '\0';
101
102
drm_edid = edid_load(connector, edidname);
103
104
kfree(fwstr);
105
106
return drm_edid;
107
}
108
109