drm/i915/gen8: page directories rework allocation
[deliverable/linux.git] / drivers / gpu / drm / i915 / i915_gem_gtt.c
index f1b9ea61de4bdad515a9f95413ead02f0bb64117..e5770189ebff3bc5a9ef2f2a4bac22dbaa9005e7 100644 (file)
@@ -93,6 +93,9 @@
  */
 
 const struct i915_ggtt_view i915_ggtt_view_normal;
+const struct i915_ggtt_view i915_ggtt_view_rotated = {
+        .type = I915_GGTT_VIEW_ROTATED
+};
 
 static void bdw_setup_private_ppat(struct drm_i915_private *dev_priv);
 static void chv_setup_private_ppat(struct drm_i915_private *dev_priv);
@@ -148,11 +151,11 @@ static void ppgtt_bind_vma(struct i915_vma *vma,
                           u32 flags);
 static void ppgtt_unbind_vma(struct i915_vma *vma);
 
-static inline gen8_gtt_pte_t gen8_pte_encode(dma_addr_t addr,
-                                            enum i915_cache_level level,
-                                            bool valid)
+static inline gen8_pte_t gen8_pte_encode(dma_addr_t addr,
+                                        enum i915_cache_level level,
+                                        bool valid)
 {
-       gen8_gtt_pte_t pte = valid ? _PAGE_PRESENT | _PAGE_RW : 0;
+       gen8_pte_t pte = valid ? _PAGE_PRESENT | _PAGE_RW : 0;
        pte |= addr;
 
        switch (level) {
@@ -170,11 +173,11 @@ static inline gen8_gtt_pte_t gen8_pte_encode(dma_addr_t addr,
        return pte;
 }
 
-static inline gen8_ppgtt_pde_t gen8_pde_encode(struct drm_device *dev,
-                                            dma_addr_t addr,
-                                            enum i915_cache_level level)
+static inline gen8_pde_t gen8_pde_encode(struct drm_device *dev,
+                                         dma_addr_t addr,
+                                         enum i915_cache_level level)
 {
-       gen8_ppgtt_pde_t pde = _PAGE_PRESENT | _PAGE_RW;
+       gen8_pde_t pde = _PAGE_PRESENT | _PAGE_RW;
        pde |= addr;
        if (level != I915_CACHE_NONE)
                pde |= PPAT_CACHED_PDE_INDEX;
@@ -183,11 +186,11 @@ static inline gen8_ppgtt_pde_t gen8_pde_encode(struct drm_device *dev,
        return pde;
 }
 
-static gen6_gtt_pte_t snb_pte_encode(dma_addr_t addr,
-                                    enum i915_cache_level level,
-                                    bool valid, u32 unused)
+static gen6_pte_t snb_pte_encode(dma_addr_t addr,
+                                enum i915_cache_level level,
+                                bool valid, u32 unused)
 {
-       gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
+       gen6_pte_t pte = valid ? GEN6_PTE_VALID : 0;
        pte |= GEN6_PTE_ADDR_ENCODE(addr);
 
        switch (level) {
@@ -205,11 +208,11 @@ static gen6_gtt_pte_t snb_pte_encode(dma_addr_t addr,
        return pte;
 }
 
-static gen6_gtt_pte_t ivb_pte_encode(dma_addr_t addr,
-                                    enum i915_cache_level level,
-                                    bool valid, u32 unused)
+static gen6_pte_t ivb_pte_encode(dma_addr_t addr,
+                                enum i915_cache_level level,
+                                bool valid, u32 unused)
 {
-       gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
+       gen6_pte_t pte = valid ? GEN6_PTE_VALID : 0;
        pte |= GEN6_PTE_ADDR_ENCODE(addr);
 
        switch (level) {
@@ -229,11 +232,11 @@ static gen6_gtt_pte_t ivb_pte_encode(dma_addr_t addr,
        return pte;
 }
 
-static gen6_gtt_pte_t byt_pte_encode(dma_addr_t addr,
-                                    enum i915_cache_level level,
-                                    bool valid, u32 flags)
+static gen6_pte_t byt_pte_encode(dma_addr_t addr,
+                                enum i915_cache_level level,
+                                bool valid, u32 flags)
 {
-       gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
+       gen6_pte_t pte = valid ? GEN6_PTE_VALID : 0;
        pte |= GEN6_PTE_ADDR_ENCODE(addr);
 
        if (!(flags & PTE_READ_ONLY))
@@ -245,11 +248,11 @@ static gen6_gtt_pte_t byt_pte_encode(dma_addr_t addr,
        return pte;
 }
 
-static gen6_gtt_pte_t hsw_pte_encode(dma_addr_t addr,
-                                    enum i915_cache_level level,
-                                    bool valid, u32 unused)
+static gen6_pte_t hsw_pte_encode(dma_addr_t addr,
+                                enum i915_cache_level level,
+                                bool valid, u32 unused)
 {
-       gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
+       gen6_pte_t pte = valid ? GEN6_PTE_VALID : 0;
        pte |= HSW_PTE_ADDR_ENCODE(addr);
 
        if (level != I915_CACHE_NONE)
@@ -258,11 +261,11 @@ static gen6_gtt_pte_t hsw_pte_encode(dma_addr_t addr,
        return pte;
 }
 
-static gen6_gtt_pte_t iris_pte_encode(dma_addr_t addr,
-                                     enum i915_cache_level level,
-                                     bool valid, u32 unused)
+static gen6_pte_t iris_pte_encode(dma_addr_t addr,
+                                 enum i915_cache_level level,
+                                 bool valid, u32 unused)
 {
-       gen6_gtt_pte_t pte = valid ? GEN6_PTE_VALID : 0;
+       gen6_pte_t pte = valid ? GEN6_PTE_VALID : 0;
        pte |= HSW_PTE_ADDR_ENCODE(addr);
 
        switch (level) {
@@ -279,29 +282,109 @@ static gen6_gtt_pte_t iris_pte_encode(dma_addr_t addr,
        return pte;
 }
 
-static void unmap_and_free_pt(struct i915_page_table_entry *pt, struct drm_device *dev)
+#define i915_dma_unmap_single(px, dev) \
+       __i915_dma_unmap_single((px)->daddr, dev)
+
+static inline void __i915_dma_unmap_single(dma_addr_t daddr,
+                                       struct drm_device *dev)
+{
+       struct device *device = &dev->pdev->dev;
+
+       dma_unmap_page(device, daddr, 4096, PCI_DMA_BIDIRECTIONAL);
+}
+
+/**
+ * i915_dma_map_single() - Create a dma mapping for a page table/dir/etc.
+ * @px:        Page table/dir/etc to get a DMA map for
+ * @dev:       drm device
+ *
+ * Page table allocations are unified across all gens. They always require a
+ * single 4k allocation, as well as a DMA mapping. If we keep the structs
+ * symmetric here, the simple macro covers us for every page table type.
+ *
+ * Return: 0 if success.
+ */
+#define i915_dma_map_single(px, dev) \
+       i915_dma_map_page_single((px)->page, (dev), &(px)->daddr)
+
+static inline int i915_dma_map_page_single(struct page *page,
+                                          struct drm_device *dev,
+                                          dma_addr_t *daddr)
+{
+       struct device *device = &dev->pdev->dev;
+
+       *daddr = dma_map_page(device, page, 0, 4096, PCI_DMA_BIDIRECTIONAL);
+       if (dma_mapping_error(device, *daddr))
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void unmap_and_free_pt(struct i915_page_table *pt,
+                              struct drm_device *dev)
 {
        if (WARN_ON(!pt->page))
                return;
+
+       i915_dma_unmap_single(pt, dev);
        __free_page(pt->page);
+       kfree(pt->used_ptes);
        kfree(pt);
 }
 
-static struct i915_page_table_entry *alloc_pt_single(struct drm_device *dev)
+static void gen8_initialize_pt(struct i915_address_space *vm,
+                               struct i915_page_table *pt)
+{
+       gen8_pte_t *pt_vaddr, scratch_pte;
+       int i;
+
+       pt_vaddr = kmap_atomic(pt->page);
+       scratch_pte = gen8_pte_encode(vm->scratch.addr,
+                                     I915_CACHE_LLC, true);
+
+       for (i = 0; i < GEN8_PTES; i++)
+               pt_vaddr[i] = scratch_pte;
+
+       if (!HAS_LLC(vm->dev))
+               drm_clflush_virt_range(pt_vaddr, PAGE_SIZE);
+       kunmap_atomic(pt_vaddr);
+}
+
+static struct i915_page_table *alloc_pt_single(struct drm_device *dev)
 {
-       struct i915_page_table_entry *pt;
+       struct i915_page_table *pt;
+       const size_t count = INTEL_INFO(dev)->gen >= 8 ?
+               GEN8_PTES : GEN6_PTES;
+       int ret = -ENOMEM;
 
        pt = kzalloc(sizeof(*pt), GFP_KERNEL);
        if (!pt)
                return ERR_PTR(-ENOMEM);
 
-       pt->page = alloc_page(GFP_KERNEL | __GFP_ZERO);
-       if (!pt->page) {
-               kfree(pt);
-               return ERR_PTR(-ENOMEM);
-       }
+       pt->used_ptes = kcalloc(BITS_TO_LONGS(count), sizeof(*pt->used_ptes),
+                               GFP_KERNEL);
+
+       if (!pt->used_ptes)
+               goto fail_bitmap;
+
+       pt->page = alloc_page(GFP_KERNEL);
+       if (!pt->page)
+               goto fail_page;
+
+       ret = i915_dma_map_single(pt, dev);
+       if (ret)
+               goto fail_dma;
 
        return pt;
+
+fail_dma:
+       __free_page(pt->page);
+fail_page:
+       kfree(pt->used_ptes);
+fail_bitmap:
+       kfree(pt);
+
+       return ERR_PTR(ret);
 }
 
 /**
@@ -318,17 +401,17 @@ static struct i915_page_table_entry *alloc_pt_single(struct drm_device *dev)
  *
  * Return: 0 if allocation succeeded.
  */
-static int alloc_pt_range(struct i915_page_directory_entry *pd, uint16_t pde, size_t count,
-                 struct drm_device *dev)
+static int alloc_pt_range(struct i915_page_directory *pd, uint16_t pde, size_t count,
+                         struct drm_device *dev)
 {
        int i, ret;
 
        /* 512 is the max page tables per page_directory on any platform. */
-       if (WARN_ON(pde + count > GEN6_PPGTT_PD_ENTRIES))
+       if (WARN_ON(pde + count > I915_PDES))
                return -EINVAL;
 
        for (i = pde; i < pde + count; i++) {
-               struct i915_page_table_entry *pt = alloc_pt_single(dev);
+               struct i915_page_table *pt = alloc_pt_single(dev);
 
                if (IS_ERR(pt)) {
                        ret = PTR_ERR(pt);
@@ -348,7 +431,7 @@ err_out:
        return ret;
 }
 
-static void unmap_and_free_pd(struct i915_page_directory_entry *pd)
+static void unmap_and_free_pd(struct i915_page_directory *pd)
 {
        if (pd->page) {
                __free_page(pd->page);
@@ -356,15 +439,15 @@ static void unmap_and_free_pd(struct i915_page_directory_entry *pd)
        }
 }
 
-static struct i915_page_directory_entry *alloc_pd_single(void)
+static struct i915_page_directory *alloc_pd_single(void)
 {
-       struct i915_page_directory_entry *pd;
+       struct i915_page_directory *pd;
 
        pd = kzalloc(sizeof(*pd), GFP_KERNEL);
        if (!pd)
                return ERR_PTR(-ENOMEM);
 
-       pd->page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+       pd->page = alloc_page(GFP_KERNEL);
        if (!pd->page) {
                kfree(pd);
                return ERR_PTR(-ENOMEM);
@@ -402,7 +485,7 @@ static int gen8_mm_switch(struct i915_hw_ppgtt *ppgtt,
        int i, ret;
 
        /* bit of a hack to find the actual last used pd */
-       int used_pd = ppgtt->num_pd_entries / GEN8_PDES_PER_PAGE;
+       int used_pd = ppgtt->num_pd_entries / I915_PDES;
 
        for (i = used_pd - 1; i >= 0; i--) {
                dma_addr_t addr = ppgtt->pdp.page_directory[i]->daddr;
@@ -421,7 +504,7 @@ static void gen8_ppgtt_clear_range(struct i915_address_space *vm,
 {
        struct i915_hw_ppgtt *ppgtt =
                container_of(vm, struct i915_hw_ppgtt, base);
-       gen8_gtt_pte_t *pt_vaddr, scratch_pte;
+       gen8_pte_t *pt_vaddr, scratch_pte;
        unsigned pdpe = start >> GEN8_PDPE_SHIFT & GEN8_PDPE_MASK;
        unsigned pde = start >> GEN8_PDE_SHIFT & GEN8_PDE_MASK;
        unsigned pte = start >> GEN8_PTE_SHIFT & GEN8_PTE_MASK;
@@ -432,8 +515,8 @@ static void gen8_ppgtt_clear_range(struct i915_address_space *vm,
                                      I915_CACHE_LLC, use_scratch);
 
        while (num_entries) {
-               struct i915_page_directory_entry *pd;
-               struct i915_page_table_entry *pt;
+               struct i915_page_directory *pd;
+               struct i915_page_table *pt;
                struct page *page_table;
 
                if (WARN_ON(!ppgtt->pdp.page_directory[pdpe]))
@@ -452,8 +535,8 @@ static void gen8_ppgtt_clear_range(struct i915_address_space *vm,
                page_table = pt->page;
 
                last_pte = pte + num_entries;
-               if (last_pte > GEN8_PTES_PER_PAGE)
-                       last_pte = GEN8_PTES_PER_PAGE;
+               if (last_pte > GEN8_PTES)
+                       last_pte = GEN8_PTES;
 
                pt_vaddr = kmap_atomic(page_table);
 
@@ -467,7 +550,7 @@ static void gen8_ppgtt_clear_range(struct i915_address_space *vm,
                kunmap_atomic(pt_vaddr);
 
                pte = 0;
-               if (++pde == GEN8_PDES_PER_PAGE) {
+               if (++pde == I915_PDES) {
                        pdpe++;
                        pde = 0;
                }
@@ -481,7 +564,7 @@ static void gen8_ppgtt_insert_entries(struct i915_address_space *vm,
 {
        struct i915_hw_ppgtt *ppgtt =
                container_of(vm, struct i915_hw_ppgtt, base);
-       gen8_gtt_pte_t *pt_vaddr;
+       gen8_pte_t *pt_vaddr;
        unsigned pdpe = start >> GEN8_PDPE_SHIFT & GEN8_PDPE_MASK;
        unsigned pde = start >> GEN8_PDE_SHIFT & GEN8_PDE_MASK;
        unsigned pte = start >> GEN8_PTE_SHIFT & GEN8_PTE_MASK;
@@ -494,8 +577,8 @@ static void gen8_ppgtt_insert_entries(struct i915_address_space *vm,
                        break;
 
                if (pt_vaddr == NULL) {
-                       struct i915_page_directory_entry *pd = ppgtt->pdp.page_directory[pdpe];
-                       struct i915_page_table_entry *pt = pd->page_table[pde];
+                       struct i915_page_directory *pd = ppgtt->pdp.page_directory[pdpe];
+                       struct i915_page_table *pt = pd->page_table[pde];
                        struct page *page_table = pt->page;
 
                        pt_vaddr = kmap_atomic(page_table);
@@ -504,12 +587,12 @@ static void gen8_ppgtt_insert_entries(struct i915_address_space *vm,
                pt_vaddr[pte] =
                        gen8_pte_encode(sg_page_iter_dma_address(&sg_iter),
                                        cache_level, true);
-               if (++pte == GEN8_PTES_PER_PAGE) {
+               if (++pte == GEN8_PTES) {
                        if (!HAS_LLC(ppgtt->base.dev))
                                drm_clflush_virt_range(pt_vaddr, PAGE_SIZE);
                        kunmap_atomic(pt_vaddr);
                        pt_vaddr = NULL;
-                       if (++pde == GEN8_PDES_PER_PAGE) {
+                       if (++pde == I915_PDES) {
                                pdpe++;
                                pde = 0;
                        }
@@ -523,14 +606,44 @@ static void gen8_ppgtt_insert_entries(struct i915_address_space *vm,
        }
 }
 
-static void gen8_free_page_tables(struct i915_page_directory_entry *pd, struct drm_device *dev)
+static void __gen8_do_map_pt(gen8_pde_t * const pde,
+                            struct i915_page_table *pt,
+                            struct drm_device *dev)
+{
+       gen8_pde_t entry =
+               gen8_pde_encode(dev, pt->daddr, I915_CACHE_LLC);
+       *pde = entry;
+}
+
+static void gen8_initialize_pd(struct i915_address_space *vm,
+                              struct i915_page_directory *pd)
+{
+       struct i915_hw_ppgtt *ppgtt =
+                       container_of(vm, struct i915_hw_ppgtt, base);
+       gen8_pde_t *page_directory;
+       struct i915_page_table *pt;
+       int i;
+
+       page_directory = kmap_atomic(pd->page);
+       pt = ppgtt->scratch_pt;
+       for (i = 0; i < I915_PDES; i++)
+               /* Map the PDE to the page table */
+               __gen8_do_map_pt(page_directory + i, pt, vm->dev);
+
+       if (!HAS_LLC(vm->dev))
+               drm_clflush_virt_range(page_directory, PAGE_SIZE);
+
+       kunmap_atomic(page_directory);
+}
+
+static void gen8_free_page_tables(struct i915_page_directory *pd, struct drm_device *dev)
 {
        int i;
 
        if (!pd->page)
                return;
 
-       for (i = 0; i < GEN8_PDES_PER_PAGE; i++) {
+       for (i = 0; i < I915_PDES; i++) {
                if (WARN_ON(!pd->page_table[i]))
                        continue;
 
@@ -550,38 +663,8 @@ static void gen8_ppgtt_free(struct i915_hw_ppgtt *ppgtt)
                gen8_free_page_tables(ppgtt->pdp.page_directory[i], ppgtt->base.dev);
                unmap_and_free_pd(ppgtt->pdp.page_directory[i]);
        }
-}
-
-static void gen8_ppgtt_unmap_pages(struct i915_hw_ppgtt *ppgtt)
-{
-       struct pci_dev *hwdev = ppgtt->base.dev->pdev;
-       int i, j;
-
-       for (i = 0; i < ppgtt->num_pd_pages; i++) {
-               /* TODO: In the future we'll support sparse mappings, so this
-                * will have to change. */
-               if (!ppgtt->pdp.page_directory[i]->daddr)
-                       continue;
 
-               pci_unmap_page(hwdev, ppgtt->pdp.page_directory[i]->daddr, PAGE_SIZE,
-                              PCI_DMA_BIDIRECTIONAL);
-
-               for (j = 0; j < GEN8_PDES_PER_PAGE; j++) {
-                       struct i915_page_directory_entry *pd = ppgtt->pdp.page_directory[i];
-                       struct i915_page_table_entry *pt;
-                       dma_addr_t addr;
-
-                       if (WARN_ON(!pd->page_table[j]))
-                               continue;
-
-                       pt = pd->page_table[j];
-                       addr = pt->daddr;
-
-                       if (addr)
-                               pci_unmap_page(hwdev, addr, PAGE_SIZE,
-                                              PCI_DMA_BIDIRECTIONAL);
-               }
-       }
+       unmap_and_free_pt(ppgtt->scratch_pt, ppgtt->base.dev);
 }
 
 static void gen8_ppgtt_cleanup(struct i915_address_space *vm)
@@ -589,7 +672,6 @@ static void gen8_ppgtt_cleanup(struct i915_address_space *vm)
        struct i915_hw_ppgtt *ppgtt =
                container_of(vm, struct i915_hw_ppgtt, base);
 
-       gen8_ppgtt_unmap_pages(ppgtt);
        gen8_ppgtt_free(ppgtt);
 }
 
@@ -599,7 +681,7 @@ static int gen8_ppgtt_allocate_page_tables(struct i915_hw_ppgtt *ppgtt)
 
        for (i = 0; i < ppgtt->num_pd_pages; i++) {
                ret = alloc_pt_range(ppgtt->pdp.page_directory[i],
-                                    0, GEN8_PDES_PER_PAGE, ppgtt->base.dev);
+                                    0, I915_PDES, ppgtt->base.dev);
                if (ret)
                        goto unwind_out;
        }
@@ -613,25 +695,55 @@ unwind_out:
        return -ENOMEM;
 }
 
-static int gen8_ppgtt_allocate_page_directories(struct i915_hw_ppgtt *ppgtt,
-                                               const int max_pdp)
+static int gen8_ppgtt_alloc_page_directories(struct i915_page_directory_pointer *pdp,
+                                    uint64_t start,
+                                    uint64_t length)
 {
-       int i;
+       struct i915_hw_ppgtt *ppgtt =
+               container_of(pdp, struct i915_hw_ppgtt, pdp);
+       struct i915_page_directory *unused;
+       uint64_t temp;
+       uint32_t pdpe;
+
+       /* FIXME: PPGTT container_of won't work for 64b */
+       WARN_ON((start + length) > 0x800000000ULL);
+
+       gen8_for_each_pdpe(unused, pdp, start, length, temp, pdpe) {
+               WARN_ON(unused);
+               pdp->page_directory[pdpe] = alloc_pd_single();
+               if (IS_ERR(ppgtt->pdp.page_directory[pdpe]))
+                       goto unwind_out;
+
+               gen8_initialize_pd(&ppgtt->base,
+                                  ppgtt->pdp.page_directory[pdpe]);
+               ppgtt->num_pd_pages++;
+       }
 
-       for (i = 0; i < max_pdp; i++) {
-               ppgtt->pdp.page_directory[i] = alloc_pd_single();
-               if (IS_ERR(ppgtt->pdp.page_directory[i]))
+       /* XXX: Still alloc all page directories in systems with less than
+        * 4GB of memory. This won't be needed after a subsequent patch.
+        */
+       while (ppgtt->num_pd_pages < GEN8_LEGACY_PDPES) {
+               ppgtt->pdp.page_directory[ppgtt->num_pd_pages] = alloc_pd_single();
+               if (IS_ERR(ppgtt->pdp.page_directory[ppgtt->num_pd_pages]))
                        goto unwind_out;
+
+               gen8_initialize_pd(&ppgtt->base,
+                                  ppgtt->pdp.page_directory[ppgtt->num_pd_pages]);
+               pdpe++;
+               ppgtt->num_pd_pages++;
        }
 
-       ppgtt->num_pd_pages = max_pdp;
        BUG_ON(ppgtt->num_pd_pages > GEN8_LEGACY_PDPES);
 
        return 0;
 
 unwind_out:
-       while (i--)
-               unmap_and_free_pd(ppgtt->pdp.page_directory[i]);
+       while (pdpe--) {
+               unmap_and_free_pd(ppgtt->pdp.page_directory[pdpe]);
+               ppgtt->num_pd_pages--;
+       }
+
+       WARN_ON(ppgtt->num_pd_pages);
 
        return -ENOMEM;
 }
@@ -641,7 +753,8 @@ static int gen8_ppgtt_alloc(struct i915_hw_ppgtt *ppgtt,
 {
        int ret;
 
-       ret = gen8_ppgtt_allocate_page_directories(ppgtt, max_pdp);
+       ret = gen8_ppgtt_alloc_page_directories(&ppgtt->pdp, ppgtt->base.start,
+                                       ppgtt->base.total);
        if (ret)
                return ret;
 
@@ -649,7 +762,7 @@ static int gen8_ppgtt_alloc(struct i915_hw_ppgtt *ppgtt,
        if (ret)
                goto err_out;
 
-       ppgtt->num_pd_entries = max_pdp * GEN8_PDES_PER_PAGE;
+       ppgtt->num_pd_entries = max_pdp * I915_PDES;
 
        return 0;
 
@@ -682,11 +795,13 @@ static int gen8_ppgtt_setup_page_tables(struct i915_hw_ppgtt *ppgtt,
                                        const int pt)
 {
        dma_addr_t pt_addr;
-       struct i915_page_directory_entry *pdir = ppgtt->pdp.page_directory[pd];
-       struct i915_page_table_entry *ptab = pdir->page_table[pt];
+       struct i915_page_directory *pdir = ppgtt->pdp.page_directory[pd];
+       struct i915_page_table *ptab = pdir->page_table[pt];
        struct page *p = ptab->page;
        int ret;
 
+       gen8_initialize_pt(&ppgtt->base, ptab);
+
        pt_addr = pci_map_page(ppgtt->base.dev->pdev,
                               p, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
        ret = pci_dma_mapping_error(ppgtt->base.dev->pdev, pt_addr);
@@ -698,7 +813,7 @@ static int gen8_ppgtt_setup_page_tables(struct i915_hw_ppgtt *ppgtt,
        return 0;
 }
 
-/**
+/*
  * GEN8 legacy ppgtt programming is accomplished through a max 4 PDP registers
  * with a net effect resembling a 2-level page table in normal x86 terms. Each
  * PDP represents 1GB of memory 4 * 512 * 512 * 4096 = 4GB legacy 32b address
@@ -711,12 +826,23 @@ static int gen8_ppgtt_setup_page_tables(struct i915_hw_ppgtt *ppgtt,
 static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt, uint64_t size)
 {
        const int max_pdp = DIV_ROUND_UP(size, 1 << 30);
-       const int min_pt_pages = GEN8_PDES_PER_PAGE * max_pdp;
+       const int min_pt_pages = I915_PDES * max_pdp;
        int i, j, ret;
 
        if (size % (1<<30))
                DRM_INFO("Pages will be wasted unless GTT size (%llu) is divisible by 1GB\n", size);
 
+       ppgtt->base.start = 0;
+       /* This is the area that we advertise as usable for the caller */
+       ppgtt->base.total = max_pdp * I915_PDES * GEN8_PTES * PAGE_SIZE;
+       WARN_ON(ppgtt->base.total == 0);
+
+       ppgtt->scratch_pt = alloc_pt_single(ppgtt->base.dev);
+       if (IS_ERR(ppgtt->scratch_pt))
+               return PTR_ERR(ppgtt->scratch_pt);
+
+       gen8_initialize_pt(&ppgtt->base, ppgtt->scratch_pt);
+
        /* 1. Do all our allocations for page directories and page tables.
         * We allocate more than was asked so that we can point the unused parts
         * to valid entries that point to scratch page. Dynamic page tables
@@ -734,7 +860,7 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt, uint64_t size)
                if (ret)
                        goto bail;
 
-               for (j = 0; j < GEN8_PDES_PER_PAGE; j++) {
+               for (j = 0; j < I915_PDES; j++) {
                        ret = gen8_ppgtt_setup_page_tables(ppgtt, i, j);
                        if (ret)
                                goto bail;
@@ -742,7 +868,7 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt, uint64_t size)
        }
 
        /*
-        * 3. Map all the page directory entires to point to the page tables
+        * 3. Map all the page directory entries to point to the page tables
         * we've allocated.
         *
         * For now, the PPGTT helper functions all require that the PDEs are
@@ -750,11 +876,11 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt, uint64_t size)
         * will never need to touch the PDEs again.
         */
        for (i = 0; i < GEN8_LEGACY_PDPES; i++) {
-               struct i915_page_directory_entry *pd = ppgtt->pdp.page_directory[i];
-               gen8_ppgtt_pde_t *pd_vaddr;
+               struct i915_page_directory *pd = ppgtt->pdp.page_directory[i];
+               gen8_pde_t *pd_vaddr;
                pd_vaddr = kmap_atomic(ppgtt->pdp.page_directory[i]->page);
-               for (j = 0; j < GEN8_PDES_PER_PAGE; j++) {
-                       struct i915_page_table_entry *pt = pd->page_table[j];
+               for (j = 0; j < I915_PDES; j++) {
+                       struct i915_page_table *pt = pd->page_table[j];
                        dma_addr_t addr = pt->daddr;
                        pd_vaddr[j] = gen8_pde_encode(ppgtt->base.dev, addr,
                                                      I915_CACHE_LLC);
@@ -768,14 +894,10 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt, uint64_t size)
        ppgtt->base.clear_range = gen8_ppgtt_clear_range;
        ppgtt->base.insert_entries = gen8_ppgtt_insert_entries;
        ppgtt->base.cleanup = gen8_ppgtt_cleanup;
-       ppgtt->base.start = 0;
-
-       /* This is the area that we advertise as usable for the caller */
-       ppgtt->base.total = max_pdp * GEN8_PDES_PER_PAGE * GEN8_PTES_PER_PAGE * PAGE_SIZE;
 
        /* Set all ptes to a valid scratch page. Also above requested space */
        ppgtt->base.clear_range(&ppgtt->base, 0,
-                               ppgtt->num_pd_pages * GEN8_PTES_PER_PAGE * PAGE_SIZE,
+                               ppgtt->num_pd_pages * GEN8_PTES * PAGE_SIZE,
                                true);
 
        DRM_DEBUG_DRIVER("Allocated %d pages for page directories (%d wasted)\n",
@@ -786,7 +908,6 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt, uint64_t size)
        return 0;
 
 bail:
-       gen8_ppgtt_unmap_pages(ppgtt);
        gen8_ppgtt_free(ppgtt);
        return ret;
 }
@@ -795,22 +916,22 @@ static void gen6_dump_ppgtt(struct i915_hw_ppgtt *ppgtt, struct seq_file *m)
 {
        struct drm_i915_private *dev_priv = ppgtt->base.dev->dev_private;
        struct i915_address_space *vm = &ppgtt->base;
-       gen6_gtt_pte_t __iomem *pd_addr;
-       gen6_gtt_pte_t scratch_pte;
+       gen6_pte_t __iomem *pd_addr;
+       gen6_pte_t scratch_pte;
        uint32_t pd_entry;
        int pte, pde;
 
        scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, true, 0);
 
-       pd_addr = (gen6_gtt_pte_t __iomem *)dev_priv->gtt.gsm +
-               ppgtt->pd.pd_offset / sizeof(gen6_gtt_pte_t);
+       pd_addr = (gen6_pte_t __iomem *)dev_priv->gtt.gsm +
+               ppgtt->pd.pd_offset / sizeof(gen6_pte_t);
 
        seq_printf(m, "  VM %p (pd_offset %x-%x):\n", vm,
                   ppgtt->pd.pd_offset,
                   ppgtt->pd.pd_offset + ppgtt->num_pd_entries);
        for (pde = 0; pde < ppgtt->num_pd_entries; pde++) {
                u32 expected;
-               gen6_gtt_pte_t *pt_vaddr;
+               gen6_pte_t *pt_vaddr;
                dma_addr_t pt_addr = ppgtt->pd.page_table[pde]->daddr;
                pd_entry = readl(pd_addr + pde);
                expected = (GEN6_PDE_ADDR_ENCODE(pt_addr) | GEN6_PDE_VALID);
@@ -823,9 +944,9 @@ static void gen6_dump_ppgtt(struct i915_hw_ppgtt *ppgtt, struct seq_file *m)
                seq_printf(m, "\tPDE: %x\n", pd_entry);
 
                pt_vaddr = kmap_atomic(ppgtt->pd.page_table[pde]->page);
-               for (pte = 0; pte < I915_PPGTT_PT_ENTRIES; pte+=4) {
+               for (pte = 0; pte < GEN6_PTES; pte+=4) {
                        unsigned long va =
-                               (pde * PAGE_SIZE * I915_PPGTT_PT_ENTRIES) +
+                               (pde * PAGE_SIZE * GEN6_PTES) +
                                (pte * PAGE_SIZE);
                        int i;
                        bool found = false;
@@ -848,26 +969,36 @@ static void gen6_dump_ppgtt(struct i915_hw_ppgtt *ppgtt, struct seq_file *m)
        }
 }
 
-static void gen6_write_pdes(struct i915_hw_ppgtt *ppgtt)
+/* Write pde (index) from the page directory @pd to the page table @pt */
+static void gen6_write_pde(struct i915_page_directory *pd,
+                           const int pde, struct i915_page_table *pt)
 {
-       struct drm_i915_private *dev_priv = ppgtt->base.dev->dev_private;
-       gen6_gtt_pte_t __iomem *pd_addr;
-       uint32_t pd_entry;
-       int i;
+       /* Caller needs to make sure the write completes if necessary */
+       struct i915_hw_ppgtt *ppgtt =
+               container_of(pd, struct i915_hw_ppgtt, pd);
+       u32 pd_entry;
 
-       WARN_ON(ppgtt->pd.pd_offset & 0x3f);
-       pd_addr = (gen6_gtt_pte_t __iomem*)dev_priv->gtt.gsm +
-               ppgtt->pd.pd_offset / sizeof(gen6_gtt_pte_t);
-       for (i = 0; i < ppgtt->num_pd_entries; i++) {
-               dma_addr_t pt_addr;
+       pd_entry = GEN6_PDE_ADDR_ENCODE(pt->daddr);
+       pd_entry |= GEN6_PDE_VALID;
 
-               pt_addr = ppgtt->pd.page_table[i]->daddr;
-               pd_entry = GEN6_PDE_ADDR_ENCODE(pt_addr);
-               pd_entry |= GEN6_PDE_VALID;
+       writel(pd_entry, ppgtt->pd_addr + pde);
+}
 
-               writel(pd_entry, pd_addr + i);
-       }
-       readl(pd_addr);
+/* Write all the page tables found in the ppgtt structure to incrementing page
+ * directories. */
+static void gen6_write_page_range(struct drm_i915_private *dev_priv,
+                                 struct i915_page_directory *pd,
+                                 uint32_t start, uint32_t length)
+{
+       struct i915_page_table *pt;
+       uint32_t pde, temp;
+
+       gen6_for_each_pde(pt, pd, start, length, temp, pde)
+               gen6_write_pde(pd, pde, pt);
+
+       /* Make sure write is complete before other code can use this page
+        * table. Also require for WC mapped PTEs */
+       readl(dev_priv->gtt.gsm);
 }
 
 static uint32_t get_pd_offset(struct i915_hw_ppgtt *ppgtt)
@@ -1023,19 +1154,19 @@ static void gen6_ppgtt_clear_range(struct i915_address_space *vm,
 {
        struct i915_hw_ppgtt *ppgtt =
                container_of(vm, struct i915_hw_ppgtt, base);
-       gen6_gtt_pte_t *pt_vaddr, scratch_pte;
+       gen6_pte_t *pt_vaddr, scratch_pte;
        unsigned first_entry = start >> PAGE_SHIFT;
        unsigned num_entries = length >> PAGE_SHIFT;
-       unsigned act_pt = first_entry / I915_PPGTT_PT_ENTRIES;
-       unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
+       unsigned act_pt = first_entry / GEN6_PTES;
+       unsigned first_pte = first_entry % GEN6_PTES;
        unsigned last_pte, i;
 
        scratch_pte = vm->pte_encode(vm->scratch.addr, I915_CACHE_LLC, true, 0);
 
        while (num_entries) {
                last_pte = first_pte + num_entries;
-               if (last_pte > I915_PPGTT_PT_ENTRIES)
-                       last_pte = I915_PPGTT_PT_ENTRIES;
+               if (last_pte > GEN6_PTES)
+                       last_pte = GEN6_PTES;
 
                pt_vaddr = kmap_atomic(ppgtt->pd.page_table[act_pt]->page);
 
@@ -1057,10 +1188,10 @@ static void gen6_ppgtt_insert_entries(struct i915_address_space *vm,
 {
        struct i915_hw_ppgtt *ppgtt =
                container_of(vm, struct i915_hw_ppgtt, base);
-       gen6_gtt_pte_t *pt_vaddr;
+       gen6_pte_t *pt_vaddr;
        unsigned first_entry = start >> PAGE_SHIFT;
-       unsigned act_pt = first_entry / I915_PPGTT_PT_ENTRIES;
-       unsigned act_pte = first_entry % I915_PPGTT_PT_ENTRIES;
+       unsigned act_pt = first_entry / GEN6_PTES;
+       unsigned act_pte = first_entry % GEN6_PTES;
        struct sg_page_iter sg_iter;
 
        pt_vaddr = NULL;
@@ -1072,7 +1203,7 @@ static void gen6_ppgtt_insert_entries(struct i915_address_space *vm,
                        vm->pte_encode(sg_page_iter_dma_address(&sg_iter),
                                       cache_level, true, flags);
 
-               if (++act_pte == I915_PPGTT_PT_ENTRIES) {
+               if (++act_pte == GEN6_PTES) {
                        kunmap_atomic(pt_vaddr);
                        pt_vaddr = NULL;
                        act_pt++;
@@ -1083,23 +1214,133 @@ static void gen6_ppgtt_insert_entries(struct i915_address_space *vm,
                kunmap_atomic(pt_vaddr);
 }
 
-static void gen6_ppgtt_unmap_pages(struct i915_hw_ppgtt *ppgtt)
+/* PDE TLBs are a pain invalidate pre GEN8. It requires a context reload. If we
+ * are switching between contexts with the same LRCA, we also must do a force
+ * restore.
+ */
+static inline void mark_tlbs_dirty(struct i915_hw_ppgtt *ppgtt)
 {
+       /* If current vm != vm, */
+       ppgtt->pd_dirty_rings = INTEL_INFO(ppgtt->base.dev)->ring_mask;
+}
+
+static void gen6_initialize_pt(struct i915_address_space *vm,
+               struct i915_page_table *pt)
+{
+       gen6_pte_t *pt_vaddr, scratch_pte;
        int i;
 
-       for (i = 0; i < ppgtt->num_pd_entries; i++)
-               pci_unmap_page(ppgtt->base.dev->pdev,
-                              ppgtt->pd.page_table[i]->daddr,
-                              4096, PCI_DMA_BIDIRECTIONAL);
+       WARN_ON(vm->scratch.addr == 0);
+
+       scratch_pte = vm->pte_encode(vm->scratch.addr,
+                       I915_CACHE_LLC, true, 0);
+
+       pt_vaddr = kmap_atomic(pt->page);
+
+       for (i = 0; i < GEN6_PTES; i++)
+               pt_vaddr[i] = scratch_pte;
+
+       kunmap_atomic(pt_vaddr);
+}
+
+static int gen6_alloc_va_range(struct i915_address_space *vm,
+                              uint64_t start, uint64_t length)
+{
+       DECLARE_BITMAP(new_page_tables, I915_PDES);
+       struct drm_device *dev = vm->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct i915_hw_ppgtt *ppgtt =
+                               container_of(vm, struct i915_hw_ppgtt, base);
+       struct i915_page_table *pt;
+       const uint32_t start_save = start, length_save = length;
+       uint32_t pde, temp;
+       int ret;
+
+       WARN_ON(upper_32_bits(start));
+
+       bitmap_zero(new_page_tables, I915_PDES);
+
+       /* The allocation is done in two stages so that we can bail out with
+        * minimal amount of pain. The first stage finds new page tables that
+        * need allocation. The second stage marks use ptes within the page
+        * tables.
+        */
+       gen6_for_each_pde(pt, &ppgtt->pd, start, length, temp, pde) {
+               if (pt != ppgtt->scratch_pt) {
+                       WARN_ON(bitmap_empty(pt->used_ptes, GEN6_PTES));
+                       continue;
+               }
+
+               /* We've already allocated a page table */
+               WARN_ON(!bitmap_empty(pt->used_ptes, GEN6_PTES));
+
+               pt = alloc_pt_single(dev);
+               if (IS_ERR(pt)) {
+                       ret = PTR_ERR(pt);
+                       goto unwind_out;
+               }
+
+               gen6_initialize_pt(vm, pt);
+
+               ppgtt->pd.page_table[pde] = pt;
+               set_bit(pde, new_page_tables);
+               trace_i915_page_table_entry_alloc(vm, pde, start, GEN6_PDE_SHIFT);
+       }
+
+       start = start_save;
+       length = length_save;
+
+       gen6_for_each_pde(pt, &ppgtt->pd, start, length, temp, pde) {
+               DECLARE_BITMAP(tmp_bitmap, GEN6_PTES);
+
+               bitmap_zero(tmp_bitmap, GEN6_PTES);
+               bitmap_set(tmp_bitmap, gen6_pte_index(start),
+                          gen6_pte_count(start, length));
+
+               if (test_and_clear_bit(pde, new_page_tables))
+                       gen6_write_pde(&ppgtt->pd, pde, pt);
+
+               trace_i915_page_table_entry_map(vm, pde, pt,
+                                        gen6_pte_index(start),
+                                        gen6_pte_count(start, length),
+                                        GEN6_PTES);
+               bitmap_or(pt->used_ptes, tmp_bitmap, pt->used_ptes,
+                               GEN6_PTES);
+       }
+
+       WARN_ON(!bitmap_empty(new_page_tables, I915_PDES));
+
+       /* Make sure write is complete before other code can use this page
+        * table. Also require for WC mapped PTEs */
+       readl(dev_priv->gtt.gsm);
+
+       mark_tlbs_dirty(ppgtt);
+       return 0;
+
+unwind_out:
+       for_each_set_bit(pde, new_page_tables, I915_PDES) {
+               struct i915_page_table *pt = ppgtt->pd.page_table[pde];
+
+               ppgtt->pd.page_table[pde] = ppgtt->scratch_pt;
+               unmap_and_free_pt(pt, vm->dev);
+       }
+
+       mark_tlbs_dirty(ppgtt);
+       return ret;
 }
 
 static void gen6_ppgtt_free(struct i915_hw_ppgtt *ppgtt)
 {
        int i;
 
-       for (i = 0; i < ppgtt->num_pd_entries; i++)
-               unmap_and_free_pt(ppgtt->pd.page_table[i], ppgtt->base.dev);
+       for (i = 0; i < ppgtt->num_pd_entries; i++) {
+               struct i915_page_table *pt = ppgtt->pd.page_table[i];
 
+               if (pt != ppgtt->scratch_pt)
+                       unmap_and_free_pt(ppgtt->pd.page_table[i], ppgtt->base.dev);
+       }
+
+       unmap_and_free_pt(ppgtt->scratch_pt, ppgtt->base.dev);
        unmap_and_free_pd(&ppgtt->pd);
 }
 
@@ -1110,7 +1351,6 @@ static void gen6_ppgtt_cleanup(struct i915_address_space *vm)
 
        drm_mm_remove_node(&ppgtt->node);
 
-       gen6_ppgtt_unmap_pages(ppgtt);
        gen6_ppgtt_free(ppgtt);
 }
 
@@ -1126,6 +1366,12 @@ static int gen6_ppgtt_allocate_page_directories(struct i915_hw_ppgtt *ppgtt)
         * size. We allocate at the top of the GTT to avoid fragmentation.
         */
        BUG_ON(!drm_mm_initialized(&dev_priv->gtt.base.mm));
+       ppgtt->scratch_pt = alloc_pt_single(ppgtt->base.dev);
+       if (IS_ERR(ppgtt->scratch_pt))
+               return PTR_ERR(ppgtt->scratch_pt);
+
+       gen6_initialize_pt(&ppgtt->base, ppgtt->scratch_pt);
+
 alloc:
        ret = drm_mm_insert_node_in_range_generic(&dev_priv->gtt.base.mm,
                                                  &ppgtt->node, GEN6_PD_SIZE,
@@ -1139,66 +1385,43 @@ alloc:
                                               0, dev_priv->gtt.base.total,
                                               0);
                if (ret)
-                       return ret;
+                       goto err_out;
 
                retried = true;
                goto alloc;
        }
 
        if (ret)
-               return ret;
+               goto err_out;
+
 
        if (ppgtt->node.start < dev_priv->gtt.mappable_end)
                DRM_DEBUG("Forced to use aperture for PDEs\n");
 
-       ppgtt->num_pd_entries = GEN6_PPGTT_PD_ENTRIES;
+       ppgtt->num_pd_entries = I915_PDES;
        return 0;
+
+err_out:
+       unmap_and_free_pt(ppgtt->scratch_pt, ppgtt->base.dev);
+       return ret;
 }
 
 static int gen6_ppgtt_alloc(struct i915_hw_ppgtt *ppgtt)
 {
-       int ret;
-
-       ret = gen6_ppgtt_allocate_page_directories(ppgtt);
-       if (ret)
-               return ret;
-
-       ret = alloc_pt_range(&ppgtt->pd, 0, ppgtt->num_pd_entries,
-                       ppgtt->base.dev);
-
-       if (ret) {
-               drm_mm_remove_node(&ppgtt->node);
-               return ret;
-       }
-
-       return 0;
+       return gen6_ppgtt_allocate_page_directories(ppgtt);
 }
 
-static int gen6_ppgtt_setup_page_tables(struct i915_hw_ppgtt *ppgtt)
+static void gen6_scratch_va_range(struct i915_hw_ppgtt *ppgtt,
+                                 uint64_t start, uint64_t length)
 {
-       struct drm_device *dev = ppgtt->base.dev;
-       int i;
-
-       for (i = 0; i < ppgtt->num_pd_entries; i++) {
-               struct page *page;
-               dma_addr_t pt_addr;
+       struct i915_page_table *unused;
+       uint32_t pde, temp;
 
-               page = ppgtt->pd.page_table[i]->page;
-               pt_addr = pci_map_page(dev->pdev, page, 0, 4096,
-                                      PCI_DMA_BIDIRECTIONAL);
-
-               if (pci_dma_mapping_error(dev->pdev, pt_addr)) {
-                       gen6_ppgtt_unmap_pages(ppgtt);
-                       return -EIO;
-               }
-
-               ppgtt->pd.page_table[i]->daddr = pt_addr;
-       }
-
-       return 0;
+       gen6_for_each_pde(unused, &ppgtt->pd, start, length, temp, pde)
+               ppgtt->pd.page_table[pde] = ppgtt->scratch_pt;
 }
 
-static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
+static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt, bool aliasing)
 {
        struct drm_device *dev = ppgtt->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1221,36 +1444,50 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
        if (ret)
                return ret;
 
-       ret = gen6_ppgtt_setup_page_tables(ppgtt);
-       if (ret) {
-               gen6_ppgtt_free(ppgtt);
-               return ret;
+       if (aliasing) {
+               /* preallocate all pts */
+               ret = alloc_pt_range(&ppgtt->pd, 0, ppgtt->num_pd_entries,
+                               ppgtt->base.dev);
+
+               if (ret) {
+                       gen6_ppgtt_cleanup(&ppgtt->base);
+                       return ret;
+               }
        }
 
+       ppgtt->base.allocate_va_range = gen6_alloc_va_range;
        ppgtt->base.clear_range = gen6_ppgtt_clear_range;
        ppgtt->base.insert_entries = gen6_ppgtt_insert_entries;
        ppgtt->base.cleanup = gen6_ppgtt_cleanup;
        ppgtt->base.start = 0;
-       ppgtt->base.total = ppgtt->num_pd_entries * I915_PPGTT_PT_ENTRIES * PAGE_SIZE;
+       ppgtt->base.total = ppgtt->num_pd_entries * GEN6_PTES * PAGE_SIZE;
        ppgtt->debug_dump = gen6_dump_ppgtt;
 
        ppgtt->pd.pd_offset =
-               ppgtt->node.start / PAGE_SIZE * sizeof(gen6_gtt_pte_t);
+               ppgtt->node.start / PAGE_SIZE * sizeof(gen6_pte_t);
+
+       ppgtt->pd_addr = (gen6_pte_t __iomem *)dev_priv->gtt.gsm +
+               ppgtt->pd.pd_offset / sizeof(gen6_pte_t);
+
+       if (aliasing)
+               ppgtt->base.clear_range(&ppgtt->base, 0, ppgtt->base.total, true);
+       else
+               gen6_scratch_va_range(ppgtt, 0, ppgtt->base.total);
 
-       ppgtt->base.clear_range(&ppgtt->base, 0, ppgtt->base.total, true);
+       gen6_write_page_range(dev_priv, &ppgtt->pd, 0, ppgtt->base.total);
 
        DRM_DEBUG_DRIVER("Allocated pde space (%lldM) at GTT entry: %llx\n",
                         ppgtt->node.size >> 20,
                         ppgtt->node.start / PAGE_SIZE);
 
-       gen6_write_pdes(ppgtt);
        DRM_DEBUG("Adding PPGTT at offset %x\n",
                  ppgtt->pd.pd_offset << 10);
 
        return 0;
 }
 
-static int __hw_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt)
+static int __hw_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt,
+               bool aliasing)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
@@ -1258,7 +1495,7 @@ static int __hw_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt)
        ppgtt->base.scratch = dev_priv->gtt.base.scratch;
 
        if (INTEL_INFO(dev)->gen < 8)
-               return gen6_ppgtt_init(ppgtt);
+               return gen6_ppgtt_init(ppgtt, aliasing);
        else
                return gen8_ppgtt_init(ppgtt, dev_priv->gtt.base.total);
 }
@@ -1267,7 +1504,7 @@ int i915_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt)
        struct drm_i915_private *dev_priv = dev->dev_private;
        int ret = 0;
 
-       ret = __hw_ppgtt_init(dev, ppgtt);
+       ret = __hw_ppgtt_init(dev, ppgtt, false);
        if (ret == 0) {
                kref_init(&ppgtt->ref);
                drm_mm_init(&ppgtt->base.mm, ppgtt->base.start,
@@ -1514,15 +1751,20 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
                return;
        }
 
-       list_for_each_entry(vm, &dev_priv->vm_list, global_link) {
-               /* TODO: Perhaps it shouldn't be gen6 specific */
-               if (i915_is_ggtt(vm)) {
-                       if (dev_priv->mm.aliasing_ppgtt)
-                               gen6_write_pdes(dev_priv->mm.aliasing_ppgtt);
-                       continue;
-               }
+       if (USES_PPGTT(dev)) {
+               list_for_each_entry(vm, &dev_priv->vm_list, global_link) {
+                       /* TODO: Perhaps it shouldn't be gen6 specific */
 
-               gen6_write_pdes(container_of(vm, struct i915_hw_ppgtt, base));
+                       struct i915_hw_ppgtt *ppgtt =
+                                       container_of(vm, struct i915_hw_ppgtt,
+                                                    base);
+
+                       if (i915_is_ggtt(vm))
+                               ppgtt = dev_priv->mm.aliasing_ppgtt;
+
+                       gen6_write_page_range(dev_priv, &ppgtt->pd,
+                                             0, ppgtt->base.total);
+               }
        }
 
        i915_ggtt_flush(dev_priv);
@@ -1541,7 +1783,7 @@ int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj)
        return 0;
 }
 
-static inline void gen8_set_pte(void __iomem *addr, gen8_gtt_pte_t pte)
+static inline void gen8_set_pte(void __iomem *addr, gen8_pte_t pte)
 {
 #ifdef writeq
        writeq(pte, addr);
@@ -1558,8 +1800,8 @@ static void gen8_ggtt_insert_entries(struct i915_address_space *vm,
 {
        struct drm_i915_private *dev_priv = vm->dev->dev_private;
        unsigned first_entry = start >> PAGE_SHIFT;
-       gen8_gtt_pte_t __iomem *gtt_entries =
-               (gen8_gtt_pte_t __iomem *)dev_priv->gtt.gsm + first_entry;
+       gen8_pte_t __iomem *gtt_entries =
+               (gen8_pte_t __iomem *)dev_priv->gtt.gsm + first_entry;
        int i = 0;
        struct sg_page_iter sg_iter;
        dma_addr_t addr = 0; /* shut up gcc */
@@ -1604,8 +1846,8 @@ static void gen6_ggtt_insert_entries(struct i915_address_space *vm,
 {
        struct drm_i915_private *dev_priv = vm->dev->dev_private;
        unsigned first_entry = start >> PAGE_SHIFT;
-       gen6_gtt_pte_t __iomem *gtt_entries =
-               (gen6_gtt_pte_t __iomem *)dev_priv->gtt.gsm + first_entry;
+       gen6_pte_t __iomem *gtt_entries =
+               (gen6_pte_t __iomem *)dev_priv->gtt.gsm + first_entry;
        int i = 0;
        struct sg_page_iter sg_iter;
        dma_addr_t addr = 0;
@@ -1643,8 +1885,8 @@ static void gen8_ggtt_clear_range(struct i915_address_space *vm,
        struct drm_i915_private *dev_priv = vm->dev->dev_private;
        unsigned first_entry = start >> PAGE_SHIFT;
        unsigned num_entries = length >> PAGE_SHIFT;
-       gen8_gtt_pte_t scratch_pte, __iomem *gtt_base =
-               (gen8_gtt_pte_t __iomem *) dev_priv->gtt.gsm + first_entry;
+       gen8_pte_t scratch_pte, __iomem *gtt_base =
+               (gen8_pte_t __iomem *) dev_priv->gtt.gsm + first_entry;
        const int max_entries = gtt_total_entries(dev_priv->gtt) - first_entry;
        int i;
 
@@ -1669,8 +1911,8 @@ static void gen6_ggtt_clear_range(struct i915_address_space *vm,
        struct drm_i915_private *dev_priv = vm->dev->dev_private;
        unsigned first_entry = start >> PAGE_SHIFT;
        unsigned num_entries = length >> PAGE_SHIFT;
-       gen6_gtt_pte_t scratch_pte, __iomem *gtt_base =
-               (gen6_gtt_pte_t __iomem *) dev_priv->gtt.gsm + first_entry;
+       gen6_pte_t scratch_pte, __iomem *gtt_base =
+               (gen6_pte_t __iomem *) dev_priv->gtt.gsm + first_entry;
        const int max_entries = gtt_total_entries(dev_priv->gtt) - first_entry;
        int i;
 
@@ -1897,9 +2139,11 @@ static int i915_gem_setup_global_gtt(struct drm_device *dev,
                if (!ppgtt)
                        return -ENOMEM;
 
-               ret = __hw_ppgtt_init(dev, ppgtt);
-               if (ret != 0)
+               ret = __hw_ppgtt_init(dev, ppgtt, true);
+               if (ret) {
+                       kfree(ppgtt);
                        return ret;
+               }
 
                dev_priv->mm.aliasing_ppgtt = ppgtt;
        }
@@ -2185,7 +2429,7 @@ static int gen8_gmch_probe(struct drm_device *dev,
                gtt_size = gen8_get_total_gtt_size(snb_gmch_ctl);
        }
 
-       *gtt_total = (gtt_size / sizeof(gen8_gtt_pte_t)) << PAGE_SHIFT;
+       *gtt_total = (gtt_size / sizeof(gen8_pte_t)) << PAGE_SHIFT;
 
        if (IS_CHERRYVIEW(dev))
                chv_setup_private_ppat(dev_priv);
@@ -2230,7 +2474,7 @@ static int gen6_gmch_probe(struct drm_device *dev,
        *stolen = gen6_get_stolen_size(snb_gmch_ctl);
 
        gtt_size = gen6_get_total_gtt_size(snb_gmch_ctl);
-       *gtt_total = (gtt_size / sizeof(gen6_gtt_pte_t)) << PAGE_SHIFT;
+       *gtt_total = (gtt_size / sizeof(gen6_pte_t)) << PAGE_SHIFT;
 
        ret = ggtt_probe_common(dev, gtt_size);
 
@@ -2340,12 +2584,13 @@ __i915_gem_vma_create(struct drm_i915_gem_object *obj,
                      struct i915_address_space *vm,
                      const struct i915_ggtt_view *ggtt_view)
 {
-       struct i915_vma *vma = kzalloc(sizeof(*vma), GFP_KERNEL);
-       if (vma == NULL)
-               return ERR_PTR(-ENOMEM);
+       struct i915_vma *vma;
 
        if (WARN_ON(i915_is_ggtt(vm) != !!ggtt_view))
                return ERR_PTR(-EINVAL);
+       vma = kzalloc(sizeof(*vma), GFP_KERNEL);
+       if (vma == NULL)
+               return ERR_PTR(-ENOMEM);
 
        INIT_LIST_HEAD(&vma->vma_link);
        INIT_LIST_HEAD(&vma->mm_list);
@@ -2413,15 +2658,119 @@ i915_gem_obj_lookup_or_create_ggtt_vma(struct drm_i915_gem_object *obj,
 
 }
 
+static void
+rotate_pages(dma_addr_t *in, unsigned int width, unsigned int height,
+            struct sg_table *st)
+{
+       unsigned int column, row;
+       unsigned int src_idx;
+       struct scatterlist *sg = st->sgl;
+
+       st->nents = 0;
+
+       for (column = 0; column < width; column++) {
+               src_idx = width * (height - 1) + column;
+               for (row = 0; row < height; row++) {
+                       st->nents++;
+                       /* We don't need the pages, but need to initialize
+                        * the entries so the sg list can be happily traversed.
+                        * The only thing we need are DMA addresses.
+                        */
+                       sg_set_page(sg, NULL, PAGE_SIZE, 0);
+                       sg_dma_address(sg) = in[src_idx];
+                       sg_dma_len(sg) = PAGE_SIZE;
+                       sg = sg_next(sg);
+                       src_idx -= width;
+               }
+       }
+}
 
-static inline
-int i915_get_ggtt_vma_pages(struct i915_vma *vma)
+static struct sg_table *
+intel_rotate_fb_obj_pages(struct i915_ggtt_view *ggtt_view,
+                         struct drm_i915_gem_object *obj)
 {
+       struct drm_device *dev = obj->base.dev;
+       struct intel_rotation_info *rot_info = &ggtt_view->rotation_info;
+       unsigned long size, pages, rot_pages;
+       struct sg_page_iter sg_iter;
+       unsigned long i;
+       dma_addr_t *page_addr_list;
+       struct sg_table *st;
+       unsigned int tile_pitch, tile_height;
+       unsigned int width_pages, height_pages;
+       int ret = -ENOMEM;
+
+       pages = obj->base.size / PAGE_SIZE;
+
+       /* Calculate tiling geometry. */
+       tile_height = intel_tile_height(dev, rot_info->pixel_format,
+                                       rot_info->fb_modifier);
+       tile_pitch = PAGE_SIZE / tile_height;
+       width_pages = DIV_ROUND_UP(rot_info->pitch, tile_pitch);
+       height_pages = DIV_ROUND_UP(rot_info->height, tile_height);
+       rot_pages = width_pages * height_pages;
+       size = rot_pages * PAGE_SIZE;
+
+       /* Allocate a temporary list of source pages for random access. */
+       page_addr_list = drm_malloc_ab(pages, sizeof(dma_addr_t));
+       if (!page_addr_list)
+               return ERR_PTR(ret);
+
+       /* Allocate target SG list. */
+       st = kmalloc(sizeof(*st), GFP_KERNEL);
+       if (!st)
+               goto err_st_alloc;
+
+       ret = sg_alloc_table(st, rot_pages, GFP_KERNEL);
+       if (ret)
+               goto err_sg_alloc;
+
+       /* Populate source page list from the object. */
+       i = 0;
+       for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, 0) {
+               page_addr_list[i] = sg_page_iter_dma_address(&sg_iter);
+               i++;
+       }
+
+       /* Rotate the pages. */
+       rotate_pages(page_addr_list, width_pages, height_pages, st);
+
+       DRM_DEBUG_KMS(
+                     "Created rotated page mapping for object size %lu (pitch=%u, height=%u, pixel_format=0x%x, %ux%u tiles, %lu pages).\n",
+                     size, rot_info->pitch, rot_info->height,
+                     rot_info->pixel_format, width_pages, height_pages,
+                     rot_pages);
+
+       drm_free_large(page_addr_list);
+
+       return st;
+
+err_sg_alloc:
+       kfree(st);
+err_st_alloc:
+       drm_free_large(page_addr_list);
+
+       DRM_DEBUG_KMS(
+                     "Failed to create rotated mapping for object size %lu! (%d) (pitch=%u, height=%u, pixel_format=0x%x, %ux%u tiles, %lu pages)\n",
+                     size, ret, rot_info->pitch, rot_info->height,
+                     rot_info->pixel_format, width_pages, height_pages,
+                     rot_pages);
+       return ERR_PTR(ret);
+}
+
+static inline int
+i915_get_ggtt_vma_pages(struct i915_vma *vma)
+{
+       int ret = 0;
+
        if (vma->ggtt_view.pages)
                return 0;
 
        if (vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL)
                vma->ggtt_view.pages = vma->obj->pages;
+       else if (vma->ggtt_view.type == I915_GGTT_VIEW_ROTATED)
+               vma->ggtt_view.pages =
+                       intel_rotate_fb_obj_pages(&vma->ggtt_view, vma->obj);
        else
                WARN_ONCE(1, "GGTT view %u not implemented!\n",
                          vma->ggtt_view.type);
@@ -2429,10 +2778,15 @@ int i915_get_ggtt_vma_pages(struct i915_vma *vma)
        if (!vma->ggtt_view.pages) {
                DRM_ERROR("Failed to get pages for GGTT view type %u!\n",
                          vma->ggtt_view.type);
-               return -EINVAL;
+               ret = -EINVAL;
+       } else if (IS_ERR(vma->ggtt_view.pages)) {
+               ret = PTR_ERR(vma->ggtt_view.pages);
+               vma->ggtt_view.pages = NULL;
+               DRM_ERROR("Failed to get pages for VMA view type %u (%d)!\n",
+                         vma->ggtt_view.type, ret);
        }
 
-       return 0;
+       return ret;
 }
 
 /**
This page took 0.066435 seconds and 5 git commands to generate.