drm/i915: Determine the stolen memory base address on gen2
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / i915 / i915_gem_stolen.c
index 69eebc678f015ae4e30bf1f372fb48d94d29ecb2..cdacf3f5b77a571086024deae067bf32edc3a988 100644 (file)
@@ -30,6 +30,9 @@
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
 
+#define KB(x) ((x) * 1024)
+#define MB(x) (KB(x) * 1024)
+
 /*
  * The BIOS typically reserves some of the system's memory for the exclusive
  * use of the integrated graphics. This memory is no longer available for
@@ -91,24 +94,91 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
        /* Almost universally we can find the Graphics Base of Stolen Memory
         * at offset 0x5c in the igfx configuration space. On a few (desktop)
         * machines this is also mirrored in the bridge device at different
-        * locations, or in the MCHBAR. On gen2, the layout is again slightly
-        * different with the Graphics Segment immediately following Top of
-        * Memory (or Top of Usable DRAM). Note it appears that TOUD is only
-        * reported by 865g, so we just use the top of memory as determined
-        * by the e820 probe.
+        * locations, or in the MCHBAR.
+        *
+        * On 865 we just check the TOUD register.
+        *
+        * On 830/845/85x the stolen memory base isn't available in any
+        * register. We need to calculate it as TOM-TSEG_SIZE-stolen_size.
         *
-        * XXX However gen2 requires an unavailable symbol.
         */
        base = 0;
        if (INTEL_INFO(dev)->gen >= 3) {
                /* Read Graphics Base of Stolen Memory directly */
                pci_read_config_dword(dev->pdev, 0x5c, &base);
                base &= ~((1<<20) - 1);
-       } else { /* GEN2 */
-#if 0
-               /* Stolen is immediately above Top of Memory */
-               base = max_low_pfn_mapped << PAGE_SHIFT;
-#endif
+       } else if (IS_I865G(dev)) {
+               u16 toud = 0;
+
+               /*
+                * FIXME is the graphics stolen memory region
+                * always at TOUD? Ie. is it always the last
+                * one to be allocated by the BIOS?
+                */
+               pci_bus_read_config_word(dev->pdev->bus, PCI_DEVFN(0, 0),
+                                        I865_TOUD, &toud);
+
+               base = toud << 16;
+       } else if (IS_I85X(dev)) {
+               u32 tseg_size = 0;
+               u32 tom;
+               u8 tmp;
+
+               pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0),
+                                        I85X_ESMRAMC, &tmp);
+
+               if (tmp & TSEG_ENABLE)
+                       tseg_size = MB(1);
+
+               pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 1),
+                                        I85X_DRB3, &tmp);
+               tom = tmp * MB(32);
+
+               base = tom - tseg_size - dev_priv->gtt.stolen_size;
+       } else if (IS_845G(dev)) {
+               u32 tseg_size = 0;
+               u32 tom;
+               u8 tmp;
+
+               pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0),
+                                        I845_ESMRAMC, &tmp);
+
+               if (tmp & TSEG_ENABLE) {
+                       switch (tmp & I845_TSEG_SIZE_MASK) {
+                       case I845_TSEG_SIZE_512K:
+                               tseg_size = KB(512);
+                               break;
+                       case I845_TSEG_SIZE_1M:
+                               tseg_size = MB(1);
+                               break;
+                       }
+               }
+
+               pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0),
+                                        I830_DRB3, &tmp);
+               tom = tmp * MB(32);
+
+               base = tom - tseg_size - dev_priv->gtt.stolen_size;
+       } else if (IS_I830(dev)) {
+               u32 tseg_size = 0;
+               u32 tom;
+               u8 tmp;
+
+               pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0),
+                                        I830_ESMRAMC, &tmp);
+
+               if (tmp & TSEG_ENABLE) {
+                       if (tmp & I830_TSEG_SIZE_1M)
+                               tseg_size = MB(1);
+                       else
+                               tseg_size = KB(512);
+               }
+
+               pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0),
+                                        I830_DRB3, &tmp);
+               tom = tmp * MB(32);
+
+               base = tom - tseg_size - dev_priv->gtt.stolen_size;
        }
 
        if (base == 0)