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