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