Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
e3263ab3 | 2 | /* |
8633ef82 | 3 | * Generic System Framebuffers |
e3263ab3 | 4 | * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com> |
e3263ab3 DH |
5 | */ |
6 | ||
7 | /* | |
8633ef82 | 8 | * Simple-Framebuffer support |
e3263ab3 DH |
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 | * | |
8633ef82 | 19 | * If CONFIG_SYSFB_SIMPLEFB is not selected, never register "simple-framebuffer" |
e3263ab3 DH |
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 | |
8633ef82 | 24 | * other OF/DT parsers to create such devices, too. However, they must |
e3263ab3 DH |
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> | |
9eac534d | 32 | #include <linux/pci.h> |
e3263ab3 DH |
33 | #include <linux/platform_data/simplefb.h> |
34 | #include <linux/platform_device.h> | |
35 | #include <linux/screen_info.h> | |
d391c582 | 36 | #include <linux/sysfb.h> |
e3263ab3 | 37 | |
bc824922 JMC |
38 | static struct platform_device *pd; |
39 | static DEFINE_MUTEX(disable_lock); | |
40 | static bool disabled; | |
41 | ||
b49420d6 AD |
42 | static struct device *sysfb_parent_dev(const struct screen_info *si); |
43 | ||
bc824922 JMC |
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 | |
b49420d6 | 57 | * @dev: the device to check if non-NULL |
bc824922 JMC |
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 | */ | |
b49420d6 | 67 | void sysfb_disable(struct device *dev) |
bc824922 | 68 | { |
b49420d6 AD |
69 | struct screen_info *si = &screen_info; |
70 | ||
bc824922 | 71 | mutex_lock(&disable_lock); |
b49420d6 AD |
72 | if (!dev || dev == sysfb_parent_dev(si)) { |
73 | sysfb_unregister(); | |
74 | disabled = true; | |
75 | } | |
bc824922 JMC |
76 | mutex_unlock(&disable_lock); |
77 | } | |
78 | EXPORT_SYMBOL_GPL(sysfb_disable); | |
79 | ||
4e754597 | 80 | #if defined(CONFIG_PCI) |
b49420d6 | 81 | static bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev) |
4e754597 TZ |
82 | { |
83 | /* | |
84 | * TODO: Try to integrate this code into the PCI subsystem | |
85 | */ | |
86 | int ret; | |
87 | u16 command; | |
88 | ||
89 | ret = pci_read_config_word(pdev, PCI_COMMAND, &command); | |
90 | if (ret != PCIBIOS_SUCCESSFUL) | |
91 | return false; | |
92 | if (!(command & PCI_COMMAND_MEMORY)) | |
93 | return false; | |
94 | return true; | |
95 | } | |
96 | #else | |
b49420d6 | 97 | static bool sysfb_pci_dev_is_enabled(struct pci_dev *pdev) |
4e754597 TZ |
98 | { |
99 | return false; | |
100 | } | |
101 | #endif | |
102 | ||
b49420d6 | 103 | static struct device *sysfb_parent_dev(const struct screen_info *si) |
9eac534d TZ |
104 | { |
105 | struct pci_dev *pdev; | |
106 | ||
107 | pdev = screen_info_pci_dev(si); | |
4e754597 | 108 | if (IS_ERR(pdev)) { |
9eac534d | 109 | return ERR_CAST(pdev); |
4e754597 | 110 | } else if (pdev) { |
3285d8f0 TZ |
111 | if (!sysfb_pci_dev_is_enabled(pdev)) { |
112 | pci_dev_put(pdev); | |
4e754597 | 113 | return ERR_PTR(-ENODEV); |
3285d8f0 | 114 | } |
9eac534d | 115 | return &pdev->dev; |
4e754597 | 116 | } |
9eac534d TZ |
117 | |
118 | return NULL; | |
119 | } | |
120 | ||
e3263ab3 DH |
121 | static __init int sysfb_init(void) |
122 | { | |
123 | struct screen_info *si = &screen_info; | |
9eac534d | 124 | struct device *parent; |
e3263ab3 | 125 | struct simplefb_platform_data mode; |
e3263ab3 DH |
126 | const char *name; |
127 | bool compatible; | |
bc824922 JMC |
128 | int ret = 0; |
129 | ||
78aa89d1 TZ |
130 | screen_info_apply_fixups(); |
131 | ||
bc824922 JMC |
132 | mutex_lock(&disable_lock); |
133 | if (disabled) | |
134 | goto unlock_mutex; | |
e3263ab3 | 135 | |
3615c786 HG |
136 | sysfb_apply_efi_quirks(); |
137 | ||
9eac534d | 138 | parent = sysfb_parent_dev(si); |
9fa2679b DC |
139 | if (IS_ERR(parent)) { |
140 | ret = PTR_ERR(parent); | |
4e754597 | 141 | goto unlock_mutex; |
9fa2679b | 142 | } |
9eac534d | 143 | |
e3263ab3 | 144 | /* try to create a simple-framebuffer device */ |
8633ef82 | 145 | compatible = sysfb_parse_mode(si, &mode); |
e3263ab3 | 146 | if (compatible) { |
9eac534d | 147 | pd = sysfb_create_simplefb(si, &mode, parent); |
0949ee75 | 148 | if (!IS_ERR(pd)) |
3285d8f0 | 149 | goto put_device; |
e3263ab3 DH |
150 | } |
151 | ||
152 | /* if the FB is incompatible, create a legacy framebuffer device */ | |
153 | if (si->orig_video_isVGA == VIDEO_TYPE_EFI) | |
154 | name = "efi-framebuffer"; | |
155 | else if (si->orig_video_isVGA == VIDEO_TYPE_VLFB) | |
156 | name = "vesa-framebuffer"; | |
0db5b61e TZ |
157 | else if (si->orig_video_isVGA == VIDEO_TYPE_VGAC) |
158 | name = "vga-framebuffer"; | |
159 | else if (si->orig_video_isVGA == VIDEO_TYPE_EGAC) | |
160 | name = "ega-framebuffer"; | |
e3263ab3 DH |
161 | else |
162 | name = "platform-framebuffer"; | |
163 | ||
8633ef82 | 164 | pd = platform_device_alloc(name, 0); |
bc824922 JMC |
165 | if (!pd) { |
166 | ret = -ENOMEM; | |
3285d8f0 | 167 | goto put_device; |
bc824922 | 168 | } |
8633ef82 | 169 | |
9eac534d TZ |
170 | pd->dev.parent = parent; |
171 | ||
3615c786 | 172 | sysfb_set_efifb_fwnode(pd); |
8633ef82 JMC |
173 | |
174 | ret = platform_device_add_data(pd, si, sizeof(*si)); | |
175 | if (ret) | |
176 | goto err; | |
177 | ||
178 | ret = platform_device_add(pd); | |
179 | if (ret) | |
180 | goto err; | |
181 | ||
3285d8f0 | 182 | goto put_device; |
8633ef82 JMC |
183 | err: |
184 | platform_device_put(pd); | |
3285d8f0 TZ |
185 | put_device: |
186 | put_device(parent); | |
bc824922 JMC |
187 | unlock_mutex: |
188 | mutex_unlock(&disable_lock); | |
8633ef82 | 189 | return ret; |
e3263ab3 DH |
190 | } |
191 | ||
2995e506 | 192 | /* must execute after PCI subsystem for EFI quirks */ |
d1b163aa | 193 | device_initcall(sysfb_init); |