Line data Source code
1 : // SPDX-License-Identifier: MIT
2 :
3 : #include <linux/device.h>
4 : #include <linux/fb.h>
5 : #include <linux/list.h>
6 : #include <linux/mutex.h>
7 : #include <linux/pci.h>
8 : #include <linux/platform_device.h> /* for firmware helpers */
9 : #include <linux/slab.h>
10 : #include <linux/types.h>
11 : #include <linux/vgaarb.h>
12 :
13 : #include <drm/drm_aperture.h>
14 : #include <drm/drm_drv.h>
15 : #include <drm/drm_print.h>
16 :
17 : /**
18 : * DOC: overview
19 : *
20 : * A graphics device might be supported by different drivers, but only one
21 : * driver can be active at any given time. Many systems load a generic
22 : * graphics drivers, such as EFI-GOP or VESA, early during the boot process.
23 : * During later boot stages, they replace the generic driver with a dedicated,
24 : * hardware-specific driver. To take over the device the dedicated driver
25 : * first has to remove the generic driver. DRM aperture functions manage
26 : * ownership of DRM framebuffer memory and hand-over between drivers.
27 : *
28 : * DRM drivers should call drm_aperture_remove_conflicting_framebuffers()
29 : * at the top of their probe function. The function removes any generic
30 : * driver that is currently associated with the given framebuffer memory.
31 : * If the framebuffer is located at PCI BAR 0, the rsp code looks as in the
32 : * example given below.
33 : *
34 : * .. code-block:: c
35 : *
36 : * static const struct drm_driver example_driver = {
37 : * ...
38 : * };
39 : *
40 : * static int remove_conflicting_framebuffers(struct pci_dev *pdev)
41 : * {
42 : * bool primary = false;
43 : * resource_size_t base, size;
44 : * int ret;
45 : *
46 : * base = pci_resource_start(pdev, 0);
47 : * size = pci_resource_len(pdev, 0);
48 : * #ifdef CONFIG_X86
49 : * primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
50 : * #endif
51 : *
52 : * return drm_aperture_remove_conflicting_framebuffers(base, size, primary,
53 : * &example_driver);
54 : * }
55 : *
56 : * static int probe(struct pci_dev *pdev)
57 : * {
58 : * int ret;
59 : *
60 : * // Remove any generic drivers...
61 : * ret = remove_conflicting_framebuffers(pdev);
62 : * if (ret)
63 : * return ret;
64 : *
65 : * // ... and initialize the hardware.
66 : * ...
67 : *
68 : * drm_dev_register();
69 : *
70 : * return 0;
71 : * }
72 : *
73 : * PCI device drivers should call
74 : * drm_aperture_remove_conflicting_pci_framebuffers() and let it detect the
75 : * framebuffer apertures automatically. Device drivers without knowledge of
76 : * the framebuffer's location shall call drm_aperture_remove_framebuffers(),
77 : * which removes all drivers for known framebuffer.
78 : *
79 : * Drivers that are susceptible to being removed by other drivers, such as
80 : * generic EFI or VESA drivers, have to register themselves as owners of their
81 : * given framebuffer memory. Ownership of the framebuffer memory is achieved
82 : * by calling devm_aperture_acquire_from_firmware(). On success, the driver
83 : * is the owner of the framebuffer range. The function fails if the
84 : * framebuffer is already by another driver. See below for an example.
85 : *
86 : * .. code-block:: c
87 : *
88 : * static int acquire_framebuffers(struct drm_device *dev, struct platform_device *pdev)
89 : * {
90 : * resource_size_t base, size;
91 : *
92 : * mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
93 : * if (!mem)
94 : * return -EINVAL;
95 : * base = mem->start;
96 : * size = resource_size(mem);
97 : *
98 : * return devm_acquire_aperture_from_firmware(dev, base, size);
99 : * }
100 : *
101 : * static int probe(struct platform_device *pdev)
102 : * {
103 : * struct drm_device *dev;
104 : * int ret;
105 : *
106 : * // ... Initialize the device...
107 : * dev = devm_drm_dev_alloc();
108 : * ...
109 : *
110 : * // ... and acquire ownership of the framebuffer.
111 : * ret = acquire_framebuffers(dev, pdev);
112 : * if (ret)
113 : * return ret;
114 : *
115 : * drm_dev_register(dev, 0);
116 : *
117 : * return 0;
118 : * }
119 : *
120 : * The generic driver is now subject to forced removal by other drivers. This
121 : * only works for platform drivers that support hot unplug.
122 : * When a driver calls drm_aperture_remove_conflicting_framebuffers() et al
123 : * for the registered framebuffer range, the aperture helpers call
124 : * platform_device_unregister() and the generic driver unloads itself. It
125 : * may not access the device's registers, framebuffer memory, ROM, etc
126 : * afterwards.
127 : */
128 :
129 : struct drm_aperture {
130 : struct drm_device *dev;
131 : resource_size_t base;
132 : resource_size_t size;
133 : struct list_head lh;
134 : void (*detach)(struct drm_device *dev);
135 : };
136 :
137 : static LIST_HEAD(drm_apertures);
138 : static DEFINE_MUTEX(drm_apertures_lock);
139 :
140 : static bool overlap(resource_size_t base1, resource_size_t end1,
141 : resource_size_t base2, resource_size_t end2)
142 : {
143 0 : return (base1 < end2) && (end1 > base2);
144 : }
145 :
146 0 : static void devm_aperture_acquire_release(void *data)
147 : {
148 0 : struct drm_aperture *ap = data;
149 0 : bool detached = !ap->dev;
150 :
151 0 : if (detached)
152 : return;
153 :
154 0 : mutex_lock(&drm_apertures_lock);
155 0 : list_del(&ap->lh);
156 0 : mutex_unlock(&drm_apertures_lock);
157 : }
158 :
159 0 : static int devm_aperture_acquire(struct drm_device *dev,
160 : resource_size_t base, resource_size_t size,
161 : void (*detach)(struct drm_device *))
162 : {
163 0 : size_t end = base + size;
164 : struct list_head *pos;
165 : struct drm_aperture *ap;
166 :
167 0 : mutex_lock(&drm_apertures_lock);
168 :
169 0 : list_for_each(pos, &drm_apertures) {
170 0 : ap = container_of(pos, struct drm_aperture, lh);
171 0 : if (overlap(base, end, ap->base, ap->base + ap->size)) {
172 0 : mutex_unlock(&drm_apertures_lock);
173 0 : return -EBUSY;
174 : }
175 : }
176 :
177 0 : ap = devm_kzalloc(dev->dev, sizeof(*ap), GFP_KERNEL);
178 0 : if (!ap) {
179 0 : mutex_unlock(&drm_apertures_lock);
180 0 : return -ENOMEM;
181 : }
182 :
183 0 : ap->dev = dev;
184 0 : ap->base = base;
185 0 : ap->size = size;
186 0 : ap->detach = detach;
187 0 : INIT_LIST_HEAD(&ap->lh);
188 :
189 0 : list_add(&ap->lh, &drm_apertures);
190 :
191 0 : mutex_unlock(&drm_apertures_lock);
192 :
193 0 : return devm_add_action_or_reset(dev->dev, devm_aperture_acquire_release, ap);
194 : }
195 :
196 0 : static void drm_aperture_detach_firmware(struct drm_device *dev)
197 : {
198 0 : struct platform_device *pdev = to_platform_device(dev->dev);
199 :
200 : /*
201 : * Remove the device from the device hierarchy. This is the right thing
202 : * to do for firmware-based DRM drivers, such as EFI, VESA or VGA. After
203 : * the new driver takes over the hardware, the firmware device's state
204 : * will be lost.
205 : *
206 : * For non-platform devices, a new callback would be required.
207 : *
208 : * If the aperture helpers ever need to handle native drivers, this call
209 : * would only have to unplug the DRM device, so that the hardware device
210 : * stays around after detachment.
211 : */
212 0 : platform_device_unregister(pdev);
213 0 : }
214 :
215 : /**
216 : * devm_aperture_acquire_from_firmware - Acquires ownership of a firmware framebuffer
217 : * on behalf of a DRM driver.
218 : * @dev: the DRM device to own the framebuffer memory
219 : * @base: the framebuffer's byte offset in physical memory
220 : * @size: the framebuffer size in bytes
221 : *
222 : * Installs the given device as the new owner of the framebuffer. The function
223 : * expects the framebuffer to be provided by a platform device that has been
224 : * set up by firmware. Firmware can be any generic interface, such as EFI,
225 : * VESA, VGA, etc. If the native hardware driver takes over ownership of the
226 : * framebuffer range, the firmware state gets lost. Aperture helpers will then
227 : * unregister the platform device automatically. Acquired apertures are
228 : * released automatically if the underlying device goes away.
229 : *
230 : * The function fails if the framebuffer range, or parts of it, is currently
231 : * owned by another driver. To evict current owners, callers should use
232 : * drm_aperture_remove_conflicting_framebuffers() et al. before calling this
233 : * function. The function also fails if the given device is not a platform
234 : * device.
235 : *
236 : * Returns:
237 : * 0 on success, or a negative errno value otherwise.
238 : */
239 0 : int devm_aperture_acquire_from_firmware(struct drm_device *dev, resource_size_t base,
240 : resource_size_t size)
241 : {
242 0 : if (drm_WARN_ON(dev, !dev_is_platform(dev->dev)))
243 : return -EINVAL;
244 :
245 0 : return devm_aperture_acquire(dev, base, size, drm_aperture_detach_firmware);
246 : }
247 : EXPORT_SYMBOL(devm_aperture_acquire_from_firmware);
248 :
249 0 : static void drm_aperture_detach_drivers(resource_size_t base, resource_size_t size)
250 : {
251 0 : resource_size_t end = base + size;
252 : struct list_head *pos, *n;
253 :
254 0 : mutex_lock(&drm_apertures_lock);
255 :
256 0 : list_for_each_safe(pos, n, &drm_apertures) {
257 0 : struct drm_aperture *ap =
258 0 : container_of(pos, struct drm_aperture, lh);
259 0 : struct drm_device *dev = ap->dev;
260 :
261 0 : if (WARN_ON_ONCE(!dev))
262 0 : continue;
263 :
264 0 : if (!overlap(base, end, ap->base, ap->base + ap->size))
265 0 : continue;
266 :
267 0 : ap->dev = NULL; /* detach from device */
268 0 : list_del(&ap->lh);
269 :
270 0 : ap->detach(dev);
271 : }
272 :
273 0 : mutex_unlock(&drm_apertures_lock);
274 0 : }
275 :
276 : /**
277 : * drm_aperture_remove_conflicting_framebuffers - remove existing framebuffers in the given range
278 : * @base: the aperture's base address in physical memory
279 : * @size: aperture size in bytes
280 : * @primary: also kick vga16fb if present
281 : * @req_driver: requesting DRM driver
282 : *
283 : * This function removes graphics device drivers which use memory range described by
284 : * @base and @size.
285 : *
286 : * Returns:
287 : * 0 on success, or a negative errno code otherwise
288 : */
289 0 : int drm_aperture_remove_conflicting_framebuffers(resource_size_t base, resource_size_t size,
290 : bool primary, const struct drm_driver *req_driver)
291 : {
292 : #if IS_REACHABLE(CONFIG_FB)
293 : struct apertures_struct *a;
294 : int ret;
295 :
296 : a = alloc_apertures(1);
297 : if (!a)
298 : return -ENOMEM;
299 :
300 : a->ranges[0].base = base;
301 : a->ranges[0].size = size;
302 :
303 : ret = remove_conflicting_framebuffers(a, req_driver->name, primary);
304 : kfree(a);
305 :
306 : if (ret)
307 : return ret;
308 : #endif
309 :
310 0 : drm_aperture_detach_drivers(base, size);
311 :
312 0 : return 0;
313 : }
314 : EXPORT_SYMBOL(drm_aperture_remove_conflicting_framebuffers);
315 :
316 : /**
317 : * drm_aperture_remove_conflicting_pci_framebuffers - remove existing framebuffers for PCI devices
318 : * @pdev: PCI device
319 : * @req_driver: requesting DRM driver
320 : *
321 : * This function removes graphics device drivers using memory range configured
322 : * for any of @pdev's memory bars. The function assumes that PCI device with
323 : * shadowed ROM drives a primary display and so kicks out vga16fb.
324 : *
325 : * Returns:
326 : * 0 on success, or a negative errno code otherwise
327 : */
328 0 : int drm_aperture_remove_conflicting_pci_framebuffers(struct pci_dev *pdev,
329 : const struct drm_driver *req_driver)
330 : {
331 : resource_size_t base, size;
332 0 : int bar, ret = 0;
333 :
334 0 : for (bar = 0; bar < PCI_STD_NUM_BARS; ++bar) {
335 0 : if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM))
336 0 : continue;
337 0 : base = pci_resource_start(pdev, bar);
338 0 : size = pci_resource_len(pdev, bar);
339 0 : drm_aperture_detach_drivers(base, size);
340 : }
341 :
342 : /*
343 : * WARNING: Apparently we must kick fbdev drivers before vgacon,
344 : * otherwise the vga fbdev driver falls over.
345 : */
346 : #if IS_REACHABLE(CONFIG_FB)
347 : ret = remove_conflicting_pci_framebuffers(pdev, req_driver->name);
348 : #endif
349 : if (ret == 0)
350 0 : ret = vga_remove_vgacon(pdev);
351 0 : return ret;
352 : }
353 : EXPORT_SYMBOL(drm_aperture_remove_conflicting_pci_framebuffers);
|