1 | // SPDX-License-Identifier: MIT |
2 | |
3 | #include <linux/aperture.h> |
4 | #include <linux/platform_device.h> |
5 | |
6 | #include <drm/drm_aperture.h> |
7 | #include <drm/drm_drv.h> |
8 | #include <drm/drm_print.h> |
9 | |
10 | /** |
11 | * DOC: overview |
12 | * |
13 | * A graphics device might be supported by different drivers, but only one |
14 | * driver can be active at any given time. Many systems load a generic |
15 | * graphics drivers, such as EFI-GOP or VESA, early during the boot process. |
16 | * During later boot stages, they replace the generic driver with a dedicated, |
17 | * hardware-specific driver. To take over the device the dedicated driver |
18 | * first has to remove the generic driver. DRM aperture functions manage |
19 | * ownership of DRM framebuffer memory and hand-over between drivers. |
20 | * |
21 | * DRM drivers should call drm_aperture_remove_conflicting_framebuffers() |
22 | * at the top of their probe function. The function removes any generic |
23 | * driver that is currently associated with the given framebuffer memory. |
24 | * If the framebuffer is located at PCI BAR 0, the rsp code looks as in the |
25 | * example given below. |
26 | * |
27 | * .. code-block:: c |
28 | * |
29 | * static const struct drm_driver example_driver = { |
30 | * ... |
31 | * }; |
32 | * |
33 | * static int remove_conflicting_framebuffers(struct pci_dev *pdev) |
34 | * { |
35 | * resource_size_t base, size; |
36 | * int ret; |
37 | * |
38 | * base = pci_resource_start(pdev, 0); |
39 | * size = pci_resource_len(pdev, 0); |
40 | * |
41 | * return drm_aperture_remove_conflicting_framebuffers(base, size, |
42 | * &example_driver); |
43 | * } |
44 | * |
45 | * static int probe(struct pci_dev *pdev) |
46 | * { |
47 | * int ret; |
48 | * |
49 | * // Remove any generic drivers... |
50 | * ret = remove_conflicting_framebuffers(pdev); |
51 | * if (ret) |
52 | * return ret; |
53 | * |
54 | * // ... and initialize the hardware. |
55 | * ... |
56 | * |
57 | * drm_dev_register(); |
58 | * |
59 | * return 0; |
60 | * } |
61 | * |
62 | * PCI device drivers should call |
63 | * drm_aperture_remove_conflicting_pci_framebuffers() and let it detect the |
64 | * framebuffer apertures automatically. Device drivers without knowledge of |
65 | * the framebuffer's location shall call drm_aperture_remove_framebuffers(), |
66 | * which removes all drivers for known framebuffer. |
67 | * |
68 | * Drivers that are susceptible to being removed by other drivers, such as |
69 | * generic EFI or VESA drivers, have to register themselves as owners of their |
70 | * given framebuffer memory. Ownership of the framebuffer memory is achieved |
71 | * by calling devm_aperture_acquire_from_firmware(). On success, the driver |
72 | * is the owner of the framebuffer range. The function fails if the |
73 | * framebuffer is already owned by another driver. See below for an example. |
74 | * |
75 | * .. code-block:: c |
76 | * |
77 | * static int acquire_framebuffers(struct drm_device *dev, struct platform_device *pdev) |
78 | * { |
79 | * resource_size_t base, size; |
80 | * |
81 | * mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
82 | * if (!mem) |
83 | * return -EINVAL; |
84 | * base = mem->start; |
85 | * size = resource_size(mem); |
86 | * |
87 | * return devm_acquire_aperture_from_firmware(dev, base, size); |
88 | * } |
89 | * |
90 | * static int probe(struct platform_device *pdev) |
91 | * { |
92 | * struct drm_device *dev; |
93 | * int ret; |
94 | * |
95 | * // ... Initialize the device... |
96 | * dev = devm_drm_dev_alloc(); |
97 | * ... |
98 | * |
99 | * // ... and acquire ownership of the framebuffer. |
100 | * ret = acquire_framebuffers(dev, pdev); |
101 | * if (ret) |
102 | * return ret; |
103 | * |
104 | * drm_dev_register(dev, 0); |
105 | * |
106 | * return 0; |
107 | * } |
108 | * |
109 | * The generic driver is now subject to forced removal by other drivers. This |
110 | * only works for platform drivers that support hot unplug. |
111 | * When a driver calls drm_aperture_remove_conflicting_framebuffers() et al. |
112 | * for the registered framebuffer range, the aperture helpers call |
113 | * platform_device_unregister() and the generic driver unloads itself. It |
114 | * may not access the device's registers, framebuffer memory, ROM, etc |
115 | * afterwards. |
116 | */ |
117 | |
118 | /** |
119 | * devm_aperture_acquire_from_firmware - Acquires ownership of a firmware framebuffer |
120 | * on behalf of a DRM driver. |
121 | * @dev: the DRM device to own the framebuffer memory |
122 | * @base: the framebuffer's byte offset in physical memory |
123 | * @size: the framebuffer size in bytes |
124 | * |
125 | * Installs the given device as the new owner of the framebuffer. The function |
126 | * expects the framebuffer to be provided by a platform device that has been |
127 | * set up by firmware. Firmware can be any generic interface, such as EFI, |
128 | * VESA, VGA, etc. If the native hardware driver takes over ownership of the |
129 | * framebuffer range, the firmware state gets lost. Aperture helpers will then |
130 | * unregister the platform device automatically. Acquired apertures are |
131 | * released automatically if the underlying device goes away. |
132 | * |
133 | * The function fails if the framebuffer range, or parts of it, is currently |
134 | * owned by another driver. To evict current owners, callers should use |
135 | * drm_aperture_remove_conflicting_framebuffers() et al. before calling this |
136 | * function. The function also fails if the given device is not a platform |
137 | * device. |
138 | * |
139 | * Returns: |
140 | * 0 on success, or a negative errno value otherwise. |
141 | */ |
142 | int devm_aperture_acquire_from_firmware(struct drm_device *dev, resource_size_t base, |
143 | resource_size_t size) |
144 | { |
145 | struct platform_device *pdev; |
146 | |
147 | if (drm_WARN_ON(dev, !dev_is_platform(dev->dev))) |
148 | return -EINVAL; |
149 | |
150 | pdev = to_platform_device(dev->dev); |
151 | |
152 | return devm_aperture_acquire_for_platform_device(pdev, base, size); |
153 | } |
154 | EXPORT_SYMBOL(devm_aperture_acquire_from_firmware); |
155 | |
156 | /** |
157 | * drm_aperture_remove_conflicting_framebuffers - remove existing framebuffers in the given range |
158 | * @base: the aperture's base address in physical memory |
159 | * @size: aperture size in bytes |
160 | * @req_driver: requesting DRM driver |
161 | * |
162 | * This function removes graphics device drivers which use the memory range described by |
163 | * @base and @size. |
164 | * |
165 | * Returns: |
166 | * 0 on success, or a negative errno code otherwise |
167 | */ |
168 | int drm_aperture_remove_conflicting_framebuffers(resource_size_t base, resource_size_t size, |
169 | const struct drm_driver *req_driver) |
170 | { |
171 | return aperture_remove_conflicting_devices(base, size, name: req_driver->name); |
172 | } |
173 | EXPORT_SYMBOL(drm_aperture_remove_conflicting_framebuffers); |
174 | |
175 | /** |
176 | * drm_aperture_remove_conflicting_pci_framebuffers - remove existing framebuffers for PCI devices |
177 | * @pdev: PCI device |
178 | * @req_driver: requesting DRM driver |
179 | * |
180 | * This function removes graphics device drivers using the memory range configured |
181 | * for any of @pdev's memory bars. The function assumes that a PCI device with |
182 | * shadowed ROM drives a primary display and so kicks out vga16fb. |
183 | * |
184 | * Returns: |
185 | * 0 on success, or a negative errno code otherwise |
186 | */ |
187 | int drm_aperture_remove_conflicting_pci_framebuffers(struct pci_dev *pdev, |
188 | const struct drm_driver *req_driver) |
189 | { |
190 | return aperture_remove_conflicting_pci_devices(pdev, name: req_driver->name); |
191 | } |
192 | EXPORT_SYMBOL(drm_aperture_remove_conflicting_pci_framebuffers); |
193 | |