Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/sysfb.c
26378 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Generic System Framebuffers
4
* Copyright (c) 2012-2013 David Herrmann <[email protected]>
5
*/
6
7
/*
8
* Simple-Framebuffer support
9
* Create a platform-device for any available boot framebuffer. The
10
* simple-framebuffer platform device is already available on DT systems, so
11
* this module parses the global "screen_info" object and creates a suitable
12
* platform device compatible with the "simple-framebuffer" DT object. If
13
* the framebuffer is incompatible, we instead create a legacy
14
* "vesa-framebuffer", "efi-framebuffer" or "platform-framebuffer" device and
15
* pass the screen_info as platform_data. This allows legacy drivers
16
* to pick these devices up without messing with simple-framebuffer drivers.
17
* The global "screen_info" is still valid at all times.
18
*
19
* If CONFIG_SYSFB_SIMPLEFB is not selected, never register "simple-framebuffer"
20
* platform devices, but only use legacy framebuffer devices for
21
* backwards compatibility.
22
*
23
* TODO: We set the dev_id field of all platform-devices to 0. This allows
24
* other OF/DT parsers to create such devices, too. However, they must
25
* start at offset 1 for this to work.
26
*/
27
28
#include <linux/err.h>
29
#include <linux/init.h>
30
#include <linux/kernel.h>
31
#include <linux/mm.h>
32
#include <linux/pci.h>
33
#include <linux/platform_data/simplefb.h>
34
#include <linux/platform_device.h>
35
#include <linux/screen_info.h>
36
#include <linux/sysfb.h>
37
38
static struct platform_device *pd;
39
static DEFINE_MUTEX(disable_lock);
40
static bool disabled;
41
42
static struct device *sysfb_parent_dev(const struct screen_info *si);
43
44
static bool sysfb_unregister(void)
45
{
46
if (IS_ERR_OR_NULL(pd))
47
return false;
48
49
platform_device_unregister(pd);
50
pd = NULL;
51
52
return true;
53
}
54
55
/**
56
* sysfb_disable() - disable the Generic System Framebuffers support
57
* @dev: the device to check if non-NULL
58
*
59
* This disables the registration of system framebuffer devices that match the
60
* generic drivers that make use of the system framebuffer set up by firmware.
61
*
62
* It also unregisters a device if this was already registered by sysfb_init().
63
*
64
* Context: The function can sleep. A @disable_lock mutex is acquired to serialize
65
* against sysfb_init(), that registers a system framebuffer device.
66
*/
67
void sysfb_disable(struct device *dev)
68
{
69
struct screen_info *si = &screen_info;
70
struct device *parent;
71
72
mutex_lock(&disable_lock);
73
parent = sysfb_parent_dev(si);
74
if (!dev || !parent || dev == parent) {
75
sysfb_unregister();
76
disabled = true;
77
}
78
mutex_unlock(&disable_lock);
79
}
80
EXPORT_SYMBOL_GPL(sysfb_disable);
81
82
/**
83
* sysfb_handles_screen_info() - reports if sysfb handles the global screen_info
84
*
85
* Callers can use sysfb_handles_screen_info() to determine whether the Generic
86
* System Framebuffers (sysfb) can handle the global screen_info data structure
87
* or not. Drivers might need this information to know if they have to setup the
88
* system framebuffer, or if they have to delegate this action to sysfb instead.
89
*
90
* Returns:
91
* True if sysfb handles the global screen_info data structure.
92
*/
93
bool sysfb_handles_screen_info(void)
94
{
95
const struct screen_info *si = &screen_info;
96
97
return !!screen_info_video_type(si);
98
}
99
EXPORT_SYMBOL_GPL(sysfb_handles_screen_info);
100
101
#if defined(CONFIG_PCI)
102
static bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev)
103
{
104
/*
105
* TODO: Try to integrate this code into the PCI subsystem
106
*/
107
int ret;
108
u16 command;
109
110
ret = pci_read_config_word(pdev, PCI_COMMAND, &command);
111
if (ret != PCIBIOS_SUCCESSFUL)
112
return false;
113
if (!(command & PCI_COMMAND_MEMORY))
114
return false;
115
return true;
116
}
117
#else
118
static bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev)
119
{
120
return false;
121
}
122
#endif
123
124
static struct device *sysfb_parent_dev(const struct screen_info *si)
125
{
126
struct pci_dev *pdev;
127
128
pdev = screen_info_pci_dev(si);
129
if (IS_ERR(pdev)) {
130
return ERR_CAST(pdev);
131
} else if (pdev) {
132
if (!sysfb_pci_dev_is_enabled(pdev)) {
133
pci_dev_put(pdev);
134
return ERR_PTR(-ENODEV);
135
}
136
return &pdev->dev;
137
}
138
139
return NULL;
140
}
141
142
static __init int sysfb_init(void)
143
{
144
struct screen_info *si = &screen_info;
145
struct device *parent;
146
unsigned int type;
147
struct simplefb_platform_data mode;
148
const char *name;
149
bool compatible;
150
int ret = 0;
151
152
screen_info_apply_fixups();
153
154
mutex_lock(&disable_lock);
155
if (disabled)
156
goto unlock_mutex;
157
158
sysfb_apply_efi_quirks();
159
160
parent = sysfb_parent_dev(si);
161
if (IS_ERR(parent)) {
162
ret = PTR_ERR(parent);
163
goto unlock_mutex;
164
}
165
166
/* try to create a simple-framebuffer device */
167
compatible = sysfb_parse_mode(si, &mode);
168
if (compatible) {
169
pd = sysfb_create_simplefb(si, &mode, parent);
170
if (!IS_ERR(pd))
171
goto put_device;
172
}
173
174
type = screen_info_video_type(si);
175
176
/* if the FB is incompatible, create a legacy framebuffer device */
177
switch (type) {
178
case VIDEO_TYPE_EGAC:
179
name = "ega-framebuffer";
180
break;
181
case VIDEO_TYPE_VGAC:
182
name = "vga-framebuffer";
183
break;
184
case VIDEO_TYPE_VLFB:
185
name = "vesa-framebuffer";
186
break;
187
case VIDEO_TYPE_EFI:
188
name = "efi-framebuffer";
189
break;
190
default:
191
name = "platform-framebuffer";
192
break;
193
}
194
195
pd = platform_device_alloc(name, 0);
196
if (!pd) {
197
ret = -ENOMEM;
198
goto put_device;
199
}
200
201
pd->dev.parent = parent;
202
203
sysfb_set_efifb_fwnode(pd);
204
205
ret = platform_device_add_data(pd, si, sizeof(*si));
206
if (ret)
207
goto err;
208
209
ret = platform_device_add(pd);
210
if (ret)
211
goto err;
212
213
goto put_device;
214
err:
215
platform_device_put(pd);
216
put_device:
217
put_device(parent);
218
unlock_mutex:
219
mutex_unlock(&disable_lock);
220
return ret;
221
}
222
223
/* must execute after PCI subsystem for EFI quirks */
224
device_initcall(sysfb_init);
225
226