162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/aperture.h> 462306a36Sopenharmony_ci#include <linux/platform_device.h> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <drm/drm_aperture.h> 762306a36Sopenharmony_ci#include <drm/drm_drv.h> 862306a36Sopenharmony_ci#include <drm/drm_print.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/** 1162306a36Sopenharmony_ci * DOC: overview 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * A graphics device might be supported by different drivers, but only one 1462306a36Sopenharmony_ci * driver can be active at any given time. Many systems load a generic 1562306a36Sopenharmony_ci * graphics drivers, such as EFI-GOP or VESA, early during the boot process. 1662306a36Sopenharmony_ci * During later boot stages, they replace the generic driver with a dedicated, 1762306a36Sopenharmony_ci * hardware-specific driver. To take over the device the dedicated driver 1862306a36Sopenharmony_ci * first has to remove the generic driver. DRM aperture functions manage 1962306a36Sopenharmony_ci * ownership of DRM framebuffer memory and hand-over between drivers. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * DRM drivers should call drm_aperture_remove_conflicting_framebuffers() 2262306a36Sopenharmony_ci * at the top of their probe function. The function removes any generic 2362306a36Sopenharmony_ci * driver that is currently associated with the given framebuffer memory. 2462306a36Sopenharmony_ci * If the framebuffer is located at PCI BAR 0, the rsp code looks as in the 2562306a36Sopenharmony_ci * example given below. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * .. code-block:: c 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * static const struct drm_driver example_driver = { 3062306a36Sopenharmony_ci * ... 3162306a36Sopenharmony_ci * }; 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * static int remove_conflicting_framebuffers(struct pci_dev *pdev) 3462306a36Sopenharmony_ci * { 3562306a36Sopenharmony_ci * resource_size_t base, size; 3662306a36Sopenharmony_ci * int ret; 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * base = pci_resource_start(pdev, 0); 3962306a36Sopenharmony_ci * size = pci_resource_len(pdev, 0); 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * return drm_aperture_remove_conflicting_framebuffers(base, size, 4262306a36Sopenharmony_ci * &example_driver); 4362306a36Sopenharmony_ci * } 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * static int probe(struct pci_dev *pdev) 4662306a36Sopenharmony_ci * { 4762306a36Sopenharmony_ci * int ret; 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * // Remove any generic drivers... 5062306a36Sopenharmony_ci * ret = remove_conflicting_framebuffers(pdev); 5162306a36Sopenharmony_ci * if (ret) 5262306a36Sopenharmony_ci * return ret; 5362306a36Sopenharmony_ci * 5462306a36Sopenharmony_ci * // ... and initialize the hardware. 5562306a36Sopenharmony_ci * ... 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * drm_dev_register(); 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * return 0; 6062306a36Sopenharmony_ci * } 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * PCI device drivers should call 6362306a36Sopenharmony_ci * drm_aperture_remove_conflicting_pci_framebuffers() and let it detect the 6462306a36Sopenharmony_ci * framebuffer apertures automatically. Device drivers without knowledge of 6562306a36Sopenharmony_ci * the framebuffer's location shall call drm_aperture_remove_framebuffers(), 6662306a36Sopenharmony_ci * which removes all drivers for known framebuffer. 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * Drivers that are susceptible to being removed by other drivers, such as 6962306a36Sopenharmony_ci * generic EFI or VESA drivers, have to register themselves as owners of their 7062306a36Sopenharmony_ci * given framebuffer memory. Ownership of the framebuffer memory is achieved 7162306a36Sopenharmony_ci * by calling devm_aperture_acquire_from_firmware(). On success, the driver 7262306a36Sopenharmony_ci * is the owner of the framebuffer range. The function fails if the 7362306a36Sopenharmony_ci * framebuffer is already owned by another driver. See below for an example. 7462306a36Sopenharmony_ci * 7562306a36Sopenharmony_ci * .. code-block:: c 7662306a36Sopenharmony_ci * 7762306a36Sopenharmony_ci * static int acquire_framebuffers(struct drm_device *dev, struct platform_device *pdev) 7862306a36Sopenharmony_ci * { 7962306a36Sopenharmony_ci * resource_size_t base, size; 8062306a36Sopenharmony_ci * 8162306a36Sopenharmony_ci * mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 8262306a36Sopenharmony_ci * if (!mem) 8362306a36Sopenharmony_ci * return -EINVAL; 8462306a36Sopenharmony_ci * base = mem->start; 8562306a36Sopenharmony_ci * size = resource_size(mem); 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * return devm_acquire_aperture_from_firmware(dev, base, size); 8862306a36Sopenharmony_ci * } 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * static int probe(struct platform_device *pdev) 9162306a36Sopenharmony_ci * { 9262306a36Sopenharmony_ci * struct drm_device *dev; 9362306a36Sopenharmony_ci * int ret; 9462306a36Sopenharmony_ci * 9562306a36Sopenharmony_ci * // ... Initialize the device... 9662306a36Sopenharmony_ci * dev = devm_drm_dev_alloc(); 9762306a36Sopenharmony_ci * ... 9862306a36Sopenharmony_ci * 9962306a36Sopenharmony_ci * // ... and acquire ownership of the framebuffer. 10062306a36Sopenharmony_ci * ret = acquire_framebuffers(dev, pdev); 10162306a36Sopenharmony_ci * if (ret) 10262306a36Sopenharmony_ci * return ret; 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * drm_dev_register(dev, 0); 10562306a36Sopenharmony_ci * 10662306a36Sopenharmony_ci * return 0; 10762306a36Sopenharmony_ci * } 10862306a36Sopenharmony_ci * 10962306a36Sopenharmony_ci * The generic driver is now subject to forced removal by other drivers. This 11062306a36Sopenharmony_ci * only works for platform drivers that support hot unplug. 11162306a36Sopenharmony_ci * When a driver calls drm_aperture_remove_conflicting_framebuffers() et al. 11262306a36Sopenharmony_ci * for the registered framebuffer range, the aperture helpers call 11362306a36Sopenharmony_ci * platform_device_unregister() and the generic driver unloads itself. It 11462306a36Sopenharmony_ci * may not access the device's registers, framebuffer memory, ROM, etc 11562306a36Sopenharmony_ci * afterwards. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/** 11962306a36Sopenharmony_ci * devm_aperture_acquire_from_firmware - Acquires ownership of a firmware framebuffer 12062306a36Sopenharmony_ci * on behalf of a DRM driver. 12162306a36Sopenharmony_ci * @dev: the DRM device to own the framebuffer memory 12262306a36Sopenharmony_ci * @base: the framebuffer's byte offset in physical memory 12362306a36Sopenharmony_ci * @size: the framebuffer size in bytes 12462306a36Sopenharmony_ci * 12562306a36Sopenharmony_ci * Installs the given device as the new owner of the framebuffer. The function 12662306a36Sopenharmony_ci * expects the framebuffer to be provided by a platform device that has been 12762306a36Sopenharmony_ci * set up by firmware. Firmware can be any generic interface, such as EFI, 12862306a36Sopenharmony_ci * VESA, VGA, etc. If the native hardware driver takes over ownership of the 12962306a36Sopenharmony_ci * framebuffer range, the firmware state gets lost. Aperture helpers will then 13062306a36Sopenharmony_ci * unregister the platform device automatically. Acquired apertures are 13162306a36Sopenharmony_ci * released automatically if the underlying device goes away. 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * The function fails if the framebuffer range, or parts of it, is currently 13462306a36Sopenharmony_ci * owned by another driver. To evict current owners, callers should use 13562306a36Sopenharmony_ci * drm_aperture_remove_conflicting_framebuffers() et al. before calling this 13662306a36Sopenharmony_ci * function. The function also fails if the given device is not a platform 13762306a36Sopenharmony_ci * device. 13862306a36Sopenharmony_ci * 13962306a36Sopenharmony_ci * Returns: 14062306a36Sopenharmony_ci * 0 on success, or a negative errno value otherwise. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_ciint devm_aperture_acquire_from_firmware(struct drm_device *dev, resource_size_t base, 14362306a36Sopenharmony_ci resource_size_t size) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct platform_device *pdev; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (drm_WARN_ON(dev, !dev_is_platform(dev->dev))) 14862306a36Sopenharmony_ci return -EINVAL; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci pdev = to_platform_device(dev->dev); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return devm_aperture_acquire_for_platform_device(pdev, base, size); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ciEXPORT_SYMBOL(devm_aperture_acquire_from_firmware); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/** 15762306a36Sopenharmony_ci * drm_aperture_remove_conflicting_framebuffers - remove existing framebuffers in the given range 15862306a36Sopenharmony_ci * @base: the aperture's base address in physical memory 15962306a36Sopenharmony_ci * @size: aperture size in bytes 16062306a36Sopenharmony_ci * @req_driver: requesting DRM driver 16162306a36Sopenharmony_ci * 16262306a36Sopenharmony_ci * This function removes graphics device drivers which use the memory range described by 16362306a36Sopenharmony_ci * @base and @size. 16462306a36Sopenharmony_ci * 16562306a36Sopenharmony_ci * Returns: 16662306a36Sopenharmony_ci * 0 on success, or a negative errno code otherwise 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ciint drm_aperture_remove_conflicting_framebuffers(resource_size_t base, resource_size_t size, 16962306a36Sopenharmony_ci const struct drm_driver *req_driver) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci return aperture_remove_conflicting_devices(base, size, req_driver->name); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_aperture_remove_conflicting_framebuffers); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/** 17662306a36Sopenharmony_ci * drm_aperture_remove_conflicting_pci_framebuffers - remove existing framebuffers for PCI devices 17762306a36Sopenharmony_ci * @pdev: PCI device 17862306a36Sopenharmony_ci * @req_driver: requesting DRM driver 17962306a36Sopenharmony_ci * 18062306a36Sopenharmony_ci * This function removes graphics device drivers using the memory range configured 18162306a36Sopenharmony_ci * for any of @pdev's memory bars. The function assumes that a PCI device with 18262306a36Sopenharmony_ci * shadowed ROM drives a primary display and so kicks out vga16fb. 18362306a36Sopenharmony_ci * 18462306a36Sopenharmony_ci * Returns: 18562306a36Sopenharmony_ci * 0 on success, or a negative errno code otherwise 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ciint drm_aperture_remove_conflicting_pci_framebuffers(struct pci_dev *pdev, 18862306a36Sopenharmony_ci const struct drm_driver *req_driver) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci return aperture_remove_conflicting_pci_devices(pdev, req_driver->name); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_aperture_remove_conflicting_pci_framebuffers); 193