LCOV - code coverage report
Current view: top level - drivers/gpu/drm - drm_aperture.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 64 0.0 %
Date: 2022-12-09 01:23:36 Functions: 0 7 0.0 %

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

Generated by: LCOV version 1.14