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