Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/aperture: Add infrastructure for aperture ownership

Platform devices might operate on firmware framebuffers, such as VESA or
EFI. Before a native driver for the graphics hardware can take over the
device, it has to remove any platform driver that operates on the firmware
framebuffer. Aperture helpers provide the infrastructure for platform
drivers to acquire firmware framebuffers, and for native drivers to remove
them later on.

It works similar to the related fbdev mechanism. During initialization, the
platform driver acquires the firmware framebuffer's I/O memory and provides
a callback to be removed. The native driver later uses this information to
remove any platform driver for it's framebuffer I/O memory.

The aperture removal code is integrated into the existing code for removing
conflicting framebuffers, so native drivers use it automatically.

v5:
* fix build error introduced by rebasing v4
* fix typo in documentation
v4:
* hide detach callback in implementation (Daniel)
* documentation fixes
v3:
* rebase onto existing aperture infrastructure
* release aperture from list during detach; fix dangling apertures
* don't export struct drm_aperture
* document struct drm_aperture_funcs
v2:
* rename plaform helpers to aperture helpers
* tie to device lifetime with devm_ functions
* removed unsued remove() callback
* rename kickout to detach
* make struct drm_aperture private
* rebase onto existing drm_aperture.h header file
* use MIT license only for simplicity
* documentation

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Acked-by: Maxime Ripard <maxime@cerno.tech>
Tested-by: nerdopolis <bluescreen_avenger@verizon.net>
Link: https://patchwork.freedesktop.org/patch/msgid/20210430105840.30515-4-tzimmermann@suse.de

+221 -4
+217 -4
drivers/gpu/drm/drm_aperture.c
··· 1 1 // SPDX-License-Identifier: MIT 2 2 3 + #include <linux/device.h> 3 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> 4 11 #include <linux/vgaarb.h> 5 12 6 13 #include <drm/drm_aperture.h> 14 + #include <drm/drm_drv.h> 15 + #include <drm/drm_print.h> 7 16 8 17 /** 9 18 * DOC: overview ··· 71 62 * framebuffer apertures automatically. Device drivers without knowledge of 72 63 * the framebuffer's location shall call drm_aperture_remove_framebuffers(), 73 64 * which removes all drivers for known framebuffer. 65 + * 66 + * Drivers that are susceptible to being removed by other drivers, such as 67 + * generic EFI or VESA drivers, have to register themselves as owners of their 68 + * given framebuffer memory. Ownership of the framebuffer memory is achived 69 + * by calling devm_aperture_acquire_from_firmware(). On success, the driver 70 + * is the owner of the framebuffer range. The function fails if the 71 + * framebuffer is already by another driver. See below for an example. 72 + * 73 + * .. code-block:: c 74 + * 75 + * static int acquire_framebuffers(struct drm_device *dev, struct platform_device *pdev) 76 + * { 77 + * resource_size_t base, size; 78 + * 79 + * mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 80 + * if (!mem) 81 + * return -EINVAL; 82 + * base = mem->start; 83 + * size = resource_size(mem); 84 + * 85 + * return devm_acquire_aperture_from_firmware(dev, base, size); 86 + * } 87 + * 88 + * static int probe(struct platform_device *pdev) 89 + * { 90 + * struct drm_device *dev; 91 + * int ret; 92 + * 93 + * // ... Initialize the device... 94 + * dev = devm_drm_dev_alloc(); 95 + * ... 96 + * 97 + * // ... and acquire ownership of the framebuffer. 98 + * ret = acquire_framebuffers(dev, pdev); 99 + * if (ret) 100 + * return ret; 101 + * 102 + * drm_dev_register(dev, 0); 103 + * 104 + * return 0; 105 + * } 106 + * 107 + * The generic driver is now subject to forced removal by other drivers. This 108 + * only works for platform drivers that support hot unplug. 109 + * When a driver calls drm_aperture_remove_conflicting_framebuffers() et al 110 + * for the registered framebuffer range, the aperture helpers call 111 + * platform_device_unregister() and the generic driver unloads itself. It 112 + * may not access the device's registers, framebuffer memory, ROM, etc 113 + * afterwards. 74 114 */ 115 + 116 + struct drm_aperture { 117 + struct drm_device *dev; 118 + resource_size_t base; 119 + resource_size_t size; 120 + struct list_head lh; 121 + void (*detach)(struct drm_device *dev); 122 + }; 123 + 124 + static LIST_HEAD(drm_apertures); 125 + static DEFINE_MUTEX(drm_apertures_lock); 126 + 127 + static bool overlap(resource_size_t base1, resource_size_t end1, 128 + resource_size_t base2, resource_size_t end2) 129 + { 130 + return (base1 < end2) && (end1 > base2); 131 + } 132 + 133 + static void devm_aperture_acquire_release(void *data) 134 + { 135 + struct drm_aperture *ap = data; 136 + bool detached = !ap->dev; 137 + 138 + if (detached) 139 + return; 140 + 141 + mutex_lock(&drm_apertures_lock); 142 + list_del(&ap->lh); 143 + mutex_unlock(&drm_apertures_lock); 144 + } 145 + 146 + static int devm_aperture_acquire(struct drm_device *dev, 147 + resource_size_t base, resource_size_t size, 148 + void (*detach)(struct drm_device *)) 149 + { 150 + size_t end = base + size; 151 + struct list_head *pos; 152 + struct drm_aperture *ap; 153 + 154 + mutex_lock(&drm_apertures_lock); 155 + 156 + list_for_each(pos, &drm_apertures) { 157 + ap = container_of(pos, struct drm_aperture, lh); 158 + if (overlap(base, end, ap->base, ap->base + ap->size)) 159 + return -EBUSY; 160 + } 161 + 162 + ap = devm_kzalloc(dev->dev, sizeof(*ap), GFP_KERNEL); 163 + if (!ap) 164 + return -ENOMEM; 165 + 166 + ap->dev = dev; 167 + ap->base = base; 168 + ap->size = size; 169 + ap->detach = detach; 170 + INIT_LIST_HEAD(&ap->lh); 171 + 172 + list_add(&ap->lh, &drm_apertures); 173 + 174 + mutex_unlock(&drm_apertures_lock); 175 + 176 + return devm_add_action_or_reset(dev->dev, devm_aperture_acquire_release, ap); 177 + } 178 + 179 + static void drm_aperture_detach_firmware(struct drm_device *dev) 180 + { 181 + struct platform_device *pdev = to_platform_device(dev->dev); 182 + 183 + /* 184 + * Remove the device from the device hierarchy. This is the right thing 185 + * to do for firmware-based DRM drivers, such as EFI, VESA or VGA. After 186 + * the new driver takes over the hardware, the firmware device's state 187 + * will be lost. 188 + * 189 + * For non-platform devices, a new callback would be required. 190 + * 191 + * If the aperture helpers ever need to handle native drivers, this call 192 + * would only have to unplug the DRM device, so that the hardware device 193 + * stays around after detachment. 194 + */ 195 + platform_device_unregister(pdev); 196 + } 197 + 198 + /** 199 + * devm_aperture_acquire_from_firmware - Acquires ownership of a firmware framebuffer 200 + * on behalf of a DRM driver. 201 + * @dev: the DRM device to own the framebuffer memory 202 + * @base: the framebuffer's byte offset in physical memory 203 + * @size: the framebuffer size in bytes 204 + * 205 + * Installs the given device as the new owner of the framebuffer. The function 206 + * expects the framebuffer to be provided by a platform device that has been 207 + * set up by firmware. Firmware can be any generic interface, such as EFI, 208 + * VESA, VGA, etc. If the native hardware driver takes over ownership of the 209 + * framebuffer range, the firmware state gets lost. Aperture helpers will then 210 + * unregister the platform device automatically. Acquired apertures are 211 + * released automatically if the underlying device goes away. 212 + * 213 + * The function fails if the framebuffer range, or parts of it, is currently 214 + * owned by another driver. To evict current owners, callers should use 215 + * drm_aperture_remove_conflicting_framebuffers() et al. before calling this 216 + * function. The function also fails if the given device is not a platform 217 + * device. 218 + * 219 + * Returns: 220 + * 0 on success, or a negative errno value otherwise. 221 + */ 222 + int devm_aperture_acquire_from_firmware(struct drm_device *dev, resource_size_t base, 223 + resource_size_t size) 224 + { 225 + if (drm_WARN_ON(dev, !dev_is_platform(dev->dev))) 226 + return -EINVAL; 227 + 228 + return devm_aperture_acquire(dev, base, size, drm_aperture_detach_firmware); 229 + } 230 + EXPORT_SYMBOL(devm_aperture_acquire_from_firmware); 231 + 232 + static void drm_aperture_detach_drivers(resource_size_t base, resource_size_t size) 233 + { 234 + resource_size_t end = base + size; 235 + struct list_head *pos, *n; 236 + 237 + mutex_lock(&drm_apertures_lock); 238 + 239 + list_for_each_safe(pos, n, &drm_apertures) { 240 + struct drm_aperture *ap = 241 + container_of(pos, struct drm_aperture, lh); 242 + struct drm_device *dev = ap->dev; 243 + 244 + if (WARN_ON_ONCE(!dev)) 245 + continue; 246 + 247 + if (!overlap(base, end, ap->base, ap->base + ap->size)) 248 + continue; 249 + 250 + ap->dev = NULL; /* detach from device */ 251 + list_del(&ap->lh); 252 + 253 + ap->detach(dev); 254 + } 255 + 256 + mutex_unlock(&drm_apertures_lock); 257 + } 75 258 76 259 /** 77 260 * drm_aperture_remove_conflicting_framebuffers - remove existing framebuffers in the given range ··· 295 94 ret = remove_conflicting_framebuffers(a, name, primary); 296 95 kfree(a); 297 96 298 - return ret; 299 - #else 300 - return 0; 97 + if (ret) 98 + return ret; 301 99 #endif 100 + 101 + drm_aperture_detach_drivers(base, size); 102 + 103 + return 0; 302 104 } 303 105 EXPORT_SYMBOL(drm_aperture_remove_conflicting_framebuffers); 304 106 ··· 319 115 */ 320 116 int drm_aperture_remove_conflicting_pci_framebuffers(struct pci_dev *pdev, const char *name) 321 117 { 322 - int ret = 0; 118 + resource_size_t base, size; 119 + int bar, ret = 0; 120 + 121 + for (bar = 0; bar < PCI_STD_NUM_BARS; ++bar) { 122 + if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) 123 + continue; 124 + base = pci_resource_start(pdev, bar); 125 + size = pci_resource_len(pdev, bar); 126 + drm_aperture_detach_drivers(base, size); 127 + } 323 128 324 129 /* 325 130 * WARNING: Apparently we must kick fbdev drivers before vgacon,
+4
include/drm/drm_aperture.h
··· 5 5 6 6 #include <linux/types.h> 7 7 8 + struct drm_device; 8 9 struct pci_dev; 10 + 11 + int devm_aperture_acquire_from_firmware(struct drm_device *dev, resource_size_t base, 12 + resource_size_t size); 9 13 10 14 int drm_aperture_remove_conflicting_framebuffers(resource_size_t base, resource_size_t size, 11 15 bool primary, const char *name);