Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: MIT
0002 
0003 #include <linux/aperture.h>
0004 #include <linux/device.h>
0005 #include <linux/fb.h> /* for old fbdev helpers */
0006 #include <linux/list.h>
0007 #include <linux/mutex.h>
0008 #include <linux/pci.h>
0009 #include <linux/platform_device.h>
0010 #include <linux/slab.h>
0011 #include <linux/types.h>
0012 #include <linux/vgaarb.h>
0013 
0014 /**
0015  * DOC: overview
0016  *
0017  * A graphics device might be supported by different drivers, but only one
0018  * driver can be active at any given time. Many systems load a generic
0019  * graphics drivers, such as EFI-GOP or VESA, early during the boot process.
0020  * During later boot stages, they replace the generic driver with a dedicated,
0021  * hardware-specific driver. To take over the device the dedicated driver
0022  * first has to remove the generic driver. Aperture functions manage
0023  * ownership of framebuffer memory and hand-over between drivers.
0024  *
0025  * Graphics drivers should call aperture_remove_conflicting_devices()
0026  * at the top of their probe function. The function removes any generic
0027  * driver that is currently associated with the given framebuffer memory.
0028  * An example for a graphics device on the platform bus is shown below.
0029  *
0030  * .. code-block:: c
0031  *
0032  *  static int example_probe(struct platform_device *pdev)
0033  *  {
0034  *      struct resource *mem;
0035  *      resource_size_t base, size;
0036  *      int ret;
0037  *
0038  *      mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0039  *      if (!mem)
0040  *          return -ENODEV;
0041  *      base = mem->start;
0042  *      size = resource_size(mem);
0043  *
0044  *      ret = aperture_remove_conflicting_devices(base, size, false, "example");
0045  *      if (ret)
0046  *          return ret;
0047  *
0048  *      // Initialize the hardware
0049  *      ...
0050  *
0051  *      return 0;
0052  *  }
0053  *
0054  *  static const struct platform_driver example_driver = {
0055  *      .probe = example_probe,
0056  *      ...
0057  *  };
0058  *
0059  * The given example reads the platform device's I/O-memory range from the
0060  * device instance. An active framebuffer will be located within this range.
0061  * The call to aperture_remove_conflicting_devices() releases drivers that
0062  * have previously claimed ownership of the range and are currently driving
0063  * output on the framebuffer. If successful, the new driver can take over
0064  * the device.
0065  *
0066  * While the given example uses a platform device, the aperture helpers work
0067  * with every bus that has an addressable framebuffer. In the case of PCI,
0068  * device drivers can also call aperture_remove_conflicting_pci_devices() and
0069  * let the function detect the apertures automatically. Device drivers without
0070  * knowledge of the framebuffer's location can call
0071  * aperture_remove_all_conflicting_devices(), which removes all known devices.
0072  *
0073  * Drivers that are susceptible to being removed by other drivers, such as
0074  * generic EFI or VESA drivers, have to register themselves as owners of their
0075  * framebuffer apertures. Ownership of the framebuffer memory is achieved
0076  * by calling devm_aperture_acquire_for_platform_device(). If successful, the
0077  * driveris the owner of the framebuffer range. The function fails if the
0078  * framebuffer is already owned by another driver. See below for an example.
0079  *
0080  * .. code-block:: c
0081  *
0082  *  static int generic_probe(struct platform_device *pdev)
0083  *  {
0084  *      struct resource *mem;
0085  *      resource_size_t base, size;
0086  *
0087  *      mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0088  *      if (!mem)
0089  *          return -ENODEV;
0090  *      base = mem->start;
0091  *      size = resource_size(mem);
0092  *
0093  *      ret = devm_aperture_acquire_for_platform_device(pdev, base, size);
0094  *      if (ret)
0095  *          return ret;
0096  *
0097  *      // Initialize the hardware
0098  *      ...
0099  *
0100  *      return 0;
0101  *  }
0102  *
0103  *  static int generic_remove(struct platform_device *)
0104  *  {
0105  *      // Hot-unplug the device
0106  *      ...
0107  *
0108  *      return 0;
0109  *  }
0110  *
0111  *  static const struct platform_driver generic_driver = {
0112  *      .probe = generic_probe,
0113  *      .remove = generic_remove,
0114  *      ...
0115  *  };
0116  *
0117  * The similar to the previous example, the generic driver claims ownership
0118  * of the framebuffer memory from its probe function. This will fail if the
0119  * memory range, or parts of it, is already owned by another driver.
0120  *
0121  * If successful, the generic driver is now subject to forced removal by
0122  * another driver. This only works for platform drivers that support hot
0123  * unplugging. When a driver calls aperture_remove_conflicting_devices()
0124  * et al for the registered framebuffer range, the aperture helpers call
0125  * platform_device_unregister() and the generic driver unloads itself. The
0126  * generic driver also has to provide a remove function to make this work.
0127  * Once hot unplugged fro mhardware, it may not access the device's
0128  * registers, framebuffer memory, ROM, etc afterwards.
0129  */
0130 
0131 struct aperture_range {
0132     struct device *dev;
0133     resource_size_t base;
0134     resource_size_t size;
0135     struct list_head lh;
0136     void (*detach)(struct device *dev);
0137 };
0138 
0139 static LIST_HEAD(apertures);
0140 static DEFINE_MUTEX(apertures_lock);
0141 
0142 static bool overlap(resource_size_t base1, resource_size_t end1,
0143             resource_size_t base2, resource_size_t end2)
0144 {
0145     return (base1 < end2) && (end1 > base2);
0146 }
0147 
0148 static void devm_aperture_acquire_release(void *data)
0149 {
0150     struct aperture_range *ap = data;
0151     bool detached = !ap->dev;
0152 
0153     if (detached)
0154         return;
0155 
0156     mutex_lock(&apertures_lock);
0157     list_del(&ap->lh);
0158     mutex_unlock(&apertures_lock);
0159 }
0160 
0161 static int devm_aperture_acquire(struct device *dev,
0162                  resource_size_t base, resource_size_t size,
0163                  void (*detach)(struct device *))
0164 {
0165     size_t end = base + size;
0166     struct list_head *pos;
0167     struct aperture_range *ap;
0168 
0169     mutex_lock(&apertures_lock);
0170 
0171     list_for_each(pos, &apertures) {
0172         ap = container_of(pos, struct aperture_range, lh);
0173         if (overlap(base, end, ap->base, ap->base + ap->size)) {
0174             mutex_unlock(&apertures_lock);
0175             return -EBUSY;
0176         }
0177     }
0178 
0179     ap = devm_kzalloc(dev, sizeof(*ap), GFP_KERNEL);
0180     if (!ap) {
0181         mutex_unlock(&apertures_lock);
0182         return -ENOMEM;
0183     }
0184 
0185     ap->dev = dev;
0186     ap->base = base;
0187     ap->size = size;
0188     ap->detach = detach;
0189     INIT_LIST_HEAD(&ap->lh);
0190 
0191     list_add(&ap->lh, &apertures);
0192 
0193     mutex_unlock(&apertures_lock);
0194 
0195     return devm_add_action_or_reset(dev, devm_aperture_acquire_release, ap);
0196 }
0197 
0198 static void aperture_detach_platform_device(struct device *dev)
0199 {
0200     struct platform_device *pdev = to_platform_device(dev);
0201 
0202     /*
0203      * Remove the device from the device hierarchy. This is the right thing
0204      * to do for firmware-based DRM drivers, such as EFI, VESA or VGA. After
0205      * the new driver takes over the hardware, the firmware device's state
0206      * will be lost.
0207      *
0208      * For non-platform devices, a new callback would be required.
0209      *
0210      * If the aperture helpers ever need to handle native drivers, this call
0211      * would only have to unplug the DRM device, so that the hardware device
0212      * stays around after detachment.
0213      */
0214     platform_device_unregister(pdev);
0215 }
0216 
0217 /**
0218  * devm_aperture_acquire_for_platform_device - Acquires ownership of an aperture
0219  *                                             on behalf of a platform device.
0220  * @pdev:   the platform device to own the aperture
0221  * @base:   the aperture's byte offset in physical memory
0222  * @size:   the aperture size in bytes
0223  *
0224  * Installs the given device as the new owner of the aperture. The function
0225  * expects the aperture to be provided by a platform device. If another
0226  * driver takes over ownership of the aperture, aperture helpers will then
0227  * unregister the platform device automatically. All acquired apertures are
0228  * released automatically when the underlying device goes away.
0229  *
0230  * The function fails if the aperture, or parts of it, is currently
0231  * owned by another device. To evict current owners, callers should use
0232  * remove_conflicting_devices() et al. before calling this function.
0233  *
0234  * Returns:
0235  * 0 on success, or a negative errno value otherwise.
0236  */
0237 int devm_aperture_acquire_for_platform_device(struct platform_device *pdev,
0238                           resource_size_t base,
0239                           resource_size_t size)
0240 {
0241     return devm_aperture_acquire(&pdev->dev, base, size, aperture_detach_platform_device);
0242 }
0243 EXPORT_SYMBOL(devm_aperture_acquire_for_platform_device);
0244 
0245 static void aperture_detach_devices(resource_size_t base, resource_size_t size)
0246 {
0247     resource_size_t end = base + size;
0248     struct list_head *pos, *n;
0249 
0250     mutex_lock(&apertures_lock);
0251 
0252     list_for_each_safe(pos, n, &apertures) {
0253         struct aperture_range *ap = container_of(pos, struct aperture_range, lh);
0254         struct device *dev = ap->dev;
0255 
0256         if (WARN_ON_ONCE(!dev))
0257             continue;
0258 
0259         if (!overlap(base, end, ap->base, ap->base + ap->size))
0260             continue;
0261 
0262         ap->dev = NULL; /* detach from device */
0263         list_del(&ap->lh);
0264 
0265         ap->detach(dev);
0266     }
0267 
0268     mutex_unlock(&apertures_lock);
0269 }
0270 
0271 /**
0272  * aperture_remove_conflicting_devices - remove devices in the given range
0273  * @base: the aperture's base address in physical memory
0274  * @size: aperture size in bytes
0275  * @primary: also kick vga16fb if present; only relevant for VGA devices
0276  * @name: a descriptive name of the requesting driver
0277  *
0278  * This function removes devices that own apertures within @base and @size.
0279  *
0280  * Returns:
0281  * 0 on success, or a negative errno code otherwise
0282  */
0283 int aperture_remove_conflicting_devices(resource_size_t base, resource_size_t size,
0284                     bool primary, const char *name)
0285 {
0286 #if IS_REACHABLE(CONFIG_FB)
0287     struct apertures_struct *a;
0288     int ret;
0289 
0290     a = alloc_apertures(1);
0291     if (!a)
0292         return -ENOMEM;
0293 
0294     a->ranges[0].base = base;
0295     a->ranges[0].size = size;
0296 
0297     ret = remove_conflicting_framebuffers(a, name, primary);
0298     kfree(a);
0299 
0300     if (ret)
0301         return ret;
0302 #endif
0303 
0304     aperture_detach_devices(base, size);
0305 
0306     return 0;
0307 }
0308 EXPORT_SYMBOL(aperture_remove_conflicting_devices);
0309 
0310 /**
0311  * aperture_remove_conflicting_pci_devices - remove existing framebuffers for PCI devices
0312  * @pdev: PCI device
0313  * @name: a descriptive name of the requesting driver
0314  *
0315  * This function removes devices that own apertures within any of @pdev's
0316  * memory bars. The function assumes that PCI device with shadowed ROM
0317  * drives a primary display and therefore kicks out vga16fb as well.
0318  *
0319  * Returns:
0320  * 0 on success, or a negative errno code otherwise
0321  */
0322 int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, const char *name)
0323 {
0324     resource_size_t base, size;
0325     int bar, ret;
0326 
0327     /*
0328      * WARNING: Apparently we must kick fbdev drivers before vgacon,
0329      * otherwise the vga fbdev driver falls over.
0330      */
0331 #if IS_REACHABLE(CONFIG_FB)
0332     ret = remove_conflicting_pci_framebuffers(pdev, name);
0333     if (ret)
0334         return ret;
0335 #endif
0336     ret = vga_remove_vgacon(pdev);
0337     if (ret)
0338         return ret;
0339 
0340     for (bar = 0; bar < PCI_STD_NUM_BARS; ++bar) {
0341         if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM))
0342             continue;
0343         base = pci_resource_start(pdev, bar);
0344         size = pci_resource_len(pdev, bar);
0345         aperture_detach_devices(base, size);
0346     }
0347 
0348     return 0;
0349 
0350 }
0351 EXPORT_SYMBOL(aperture_remove_conflicting_pci_devices);