@@ -7,6 +7,7 @@
*
**************************************************************************/
+#include <linux/aperture.h>
#include <linux/cpu.h>
#include <linux/module.h>
#include <linux/notifier.h>
@@ -19,7 +20,6 @@
#include <acpi/video.h>
#include <drm/drm.h>
-#include <drm/drm_aperture.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_file.h>
@@ -416,25 +416,45 @@ static int psb_driver_load(struct drm_device *dev, unsigned long flags)
return ret;
}
-static int psb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+/*
+ * Hardware for gma500 is a hybrid device, which both acts as a PCI
+ * device (for legacy vga functionality) but also more like an
+ * integrated display on a SoC where the framebuffer simply
+ * resides in main memory and not in a special PCI bar (that
+ * internally redirects to a stolen range of main memory) like all
+ * other integrated PCI display devices implement it.
+ *
+ * To catch all cases we need to remove conflicting firmware devices
+ * for the stolen system memory and for the VGA functionality. As we
+ * currently cannot easily find the framebuffer's location in stolen
+ * memory, we remove all framebuffers here.
+ *
+ * TODO: Refactor psb_driver_load() to map vdc_reg earlier. Then
+ * we might be able to read the framebuffer range from the
+ * device.
+ */
+static int gma_remove_conflicting_framebuffers(struct pci_dev *pdev,
+ const struct drm_driver *req_driver)
{
- struct drm_psb_private *dev_priv;
- struct drm_device *dev;
+ resource_size_t base = 0;
+ resource_size_t size = U32_MAX; /* 4 GiB HW limit */
+ const char *name = req_driver->name;
int ret;
- /*
- * We cannot yet easily find the framebuffer's location in memory. So
- * remove all framebuffers here. Note that we still want the pci special
- * handling to kick out vgacon.
- *
- * TODO: Refactor psb_driver_load() to map vdc_reg earlier. Then we
- * might be able to read the framebuffer range from the device.
- */
- ret = drm_aperture_remove_framebuffers(&driver);
+ ret = aperture_remove_conflicting_devices(base, size, name);
if (ret)
return ret;
- ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, &driver);
+ return __aperture_remove_legacy_vga_devices(pdev);
+}
+
+static int psb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct drm_psb_private *dev_priv;
+ struct drm_device *dev;
+ int ret;
+
+ ret = gma_remove_conflicting_framebuffers(pdev, &driver);
if (ret)
return ret;
@@ -301,6 +301,37 @@ int aperture_remove_conflicting_devices(resource_size_t base, resource_size_t si
}
EXPORT_SYMBOL(aperture_remove_conflicting_devices);
+/**
+ * __aperture_remove_legacy_vga_devices - remove legacy VGA devices of a PCI devices
+ * @pdev: PCI device
+ *
+ * This function removes VGA devices provided by @pdev, such as a VGA
+ * framebuffer or a console. This is useful if you have a VGA-compatible
+ * PCI graphics device with framebuffers in non-BAR locations. Drivers
+ * should acquire ownership of those memory areas and afterwards call
+ * this helper to release remaining VGA devices.
+ *
+ * If your hardware has its framebuffers accessible via PCI BARS, use
+ * aperture_remove_conflicting_pci_devices() instead. The function will
+ * release any VGA devices automatically.
+ *
+ * WARNING: Apparently we must remove graphics drivers before calling
+ * this helper. Otherwise the vga fbdev driver falls over if
+ * we have vgacon configured.
+ *
+ * Returns:
+ * 0 on success, or a negative errno code otherwise
+ */
+int __aperture_remove_legacy_vga_devices(struct pci_dev *pdev)
+{
+ /* VGA framebuffer */
+ aperture_detach_devices(VGA_FB_PHYS_BASE, VGA_FB_PHYS_SIZE);
+
+ /* VGA textmode console */
+ return vga_remove_vgacon(pdev);
+}
+EXPORT_SYMBOL(__aperture_remove_legacy_vga_devices);
+
/**
* aperture_remove_conflicting_pci_devices - remove existing framebuffers for PCI devices
* @pdev: PCI device
@@ -317,7 +348,7 @@ int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, const char *na
{
bool primary = false;
resource_size_t base, size;
- int bar, ret;
+ int bar, ret = 0;
if (pdev == vga_default_device())
primary = true;
@@ -334,24 +365,15 @@ int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, const char *na
aperture_detach_devices(base, size);
}
- if (primary) {
- /*
- * If this is the primary adapter, there could be a VGA device
- * that consumes the VGA framebuffer I/O range. Remove this
- * device as well.
- */
- aperture_detach_devices(VGA_FB_PHYS_BASE, VGA_FB_PHYS_SIZE);
-
- /*
- * WARNING: Apparently we must kick fbdev drivers before vgacon,
- * otherwise the vga fbdev driver falls over.
- */
- ret = vga_remove_vgacon(pdev);
- if (ret)
- return ret;
- }
+ /*
+ * If this is the primary adapter, there could be a VGA device
+ * that consumes the VGA framebuffer I/O range. Remove this
+ * device as well.
+ */
+ if (primary)
+ ret = __aperture_remove_legacy_vga_devices(pdev);
- return 0;
+ return ret;
}
EXPORT_SYMBOL(aperture_remove_conflicting_pci_devices);
@@ -16,6 +16,8 @@ int devm_aperture_acquire_for_platform_device(struct platform_device *pdev,
int aperture_remove_conflicting_devices(resource_size_t base, resource_size_t size,
const char *name);
+int __aperture_remove_legacy_vga_devices(struct pci_dev *pdev);
+
int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, const char *name);
#else
static inline int devm_aperture_acquire_for_platform_device(struct platform_device *pdev,
@@ -31,6 +33,11 @@ static inline int aperture_remove_conflicting_devices(resource_size_t base, reso
return 0;
}
+static inline int __aperture_remove_legacy_vga_devices(struct pci_dev *pdev)
+{
+ return 0;
+}
+
static inline int aperture_remove_conflicting_pci_devices(struct pci_dev *pdev, const char *name)
{
return 0;