drm/i915/gen8: page directories rework allocation
[deliverable/linux.git] / drivers / gpu / drm / i915 / i915_gem_gtt.c
index 645c3636c5712eacd19673385dbd42cd06e16f86..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);
@@ -311,10 +314,13 @@ static inline int i915_dma_map_page_single(struct page *page,
        struct device *device = &dev->pdev->dev;
 
        *daddr = dma_map_page(device, page, 0, 4096, PCI_DMA_BIDIRECTIONAL);
-       return dma_mapping_error(device, *daddr);
+       if (dma_mapping_error(device, *daddr))
+               return -ENOMEM;
+
+       return 0;
 }
 
-static void unmap_and_free_pt(struct i915_page_table_entry *pt,
+static void unmap_and_free_pt(struct i915_page_table *pt,
                               struct drm_device *dev)
 {
        if (WARN_ON(!pt->page))
@@ -326,9 +332,27 @@ static void unmap_and_free_pt(struct i915_page_table_entry *pt,
        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)
 {
-       struct i915_page_table_entry *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 *pt;
        const size_t count = INTEL_INFO(dev)->gen >= 8 ?
                GEN8_PTES : GEN6_PTES;
        int ret = -ENOMEM;
@@ -343,7 +367,7 @@ static struct i915_page_table_entry *alloc_pt_single(struct drm_device *dev)
        if (!pt->used_ptes)
                goto fail_bitmap;
 
-       pt->page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+       pt->page = alloc_page(GFP_KERNEL);
        if (!pt->page)
                goto fail_page;
 
@@ -377,8 +401,8 @@ fail_bitmap:
  *
  * 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;
 
@@ -387,7 +411,7 @@ static int alloc_pt_range(struct i915_page_directory_entry *pd, uint16_t pde, si
                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);
@@ -407,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);
@@ -415,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);
@@ -491,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]))
@@ -553,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);
@@ -582,7 +606,37 @@ 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;
 
@@ -609,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 < I915_PDES; 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)
@@ -648,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);
 }
 
@@ -672,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;
 
-       for (i = 0; i < max_pdp; i++) {
-               ppgtt->pdp.page_directory[i] = alloc_pd_single();
-               if (IS_ERR(ppgtt->pdp.page_directory[i]))
+               gen8_initialize_pd(&ppgtt->base,
+                                  ppgtt->pdp.page_directory[pdpe]);
+               ppgtt->num_pd_pages++;
+       }
+
+       /* 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;
 }
@@ -700,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;
 
@@ -741,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);
@@ -776,6 +832,17 @@ static int gen8_ppgtt_init(struct i915_hw_ppgtt *ppgtt, uint64_t size)
        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
@@ -801,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
@@ -809,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];
+               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 < I915_PDES; j++) {
-                       struct i915_page_table_entry *pt = pd->page_table[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);
@@ -827,10 +894,6 @@ 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 * I915_PDES * GEN8_PTES * PAGE_SIZE;
 
        /* Set all ptes to a valid scratch page. Also above requested space */
        ppgtt->base.clear_range(&ppgtt->base, 0,
@@ -845,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;
 }
@@ -908,8 +970,8 @@ static void gen6_dump_ppgtt(struct i915_hw_ppgtt *ppgtt, struct seq_file *m)
 }
 
 /* Write pde (index) from the page directory @pd to the page table @pt */
-static void gen6_write_pde(struct i915_page_directory_entry *pd,
-                           const int pde, struct i915_page_table_entry *pt)
+static void gen6_write_pde(struct i915_page_directory *pd,
+                           const int pde, struct i915_page_table *pt)
 {
        /* Caller needs to make sure the write completes if necessary */
        struct i915_hw_ppgtt *ppgtt =
@@ -925,10 +987,10 @@ static void gen6_write_pde(struct i915_page_directory_entry *pd,
 /* 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_entry *pd,
+                                 struct i915_page_directory *pd,
                                  uint32_t start, uint32_t length)
 {
-       struct i915_page_table_entry *pt;
+       struct i915_page_table *pt;
        uint32_t pde, temp;
 
        gen6_for_each_pde(pt, pd, start, length, temp, pde)
@@ -1152,16 +1214,6 @@ 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)
-{
-       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);
-}
-
 /* 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.
@@ -1172,13 +1224,71 @@ static inline void mark_tlbs_dirty(struct i915_hw_ppgtt *ppgtt)
        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;
+
+       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_entry *pt;
+       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);
@@ -1187,21 +1297,50 @@ static int gen6_alloc_va_range(struct i915_address_space *vm,
                bitmap_set(tmp_bitmap, gen6_pte_index(start),
                           gen6_pte_count(start, length));
 
-               bitmap_or(pt->used_ptes, pt->used_ptes, tmp_bitmap,
+               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);
 }
 
@@ -1212,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);
 }
 
@@ -1228,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,
@@ -1258,29 +1402,26 @@ alloc:
        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);
+       return gen6_ppgtt_allocate_page_directories(ppgtt);
+}
 
-       if (ret) {
-               drm_mm_remove_node(&ppgtt->node);
-               return ret;
-       }
+static void gen6_scratch_va_range(struct i915_hw_ppgtt *ppgtt,
+                                 uint64_t start, uint64_t length)
+{
+       struct i915_page_table *unused;
+       uint32_t pde, temp;
 
-       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;
@@ -1303,6 +1444,17 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
        if (ret)
                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;
@@ -1317,7 +1469,10 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
        ppgtt->pd_addr = (gen6_pte_t __iomem *)dev_priv->gtt.gsm +
                ppgtt->pd.pd_offset / sizeof(gen6_pte_t);
 
-       ppgtt->base.clear_range(&ppgtt->base, 0, ppgtt->base.total, true);
+       if (aliasing)
+               ppgtt->base.clear_range(&ppgtt->base, 0, ppgtt->base.total, true);
+       else
+               gen6_scratch_va_range(ppgtt, 0, ppgtt->base.total);
 
        gen6_write_page_range(dev_priv, &ppgtt->pd, 0, ppgtt->base.total);
 
@@ -1331,7 +1486,8 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
        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;
 
@@ -1339,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);
 }
@@ -1348,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,
@@ -1983,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;
        }
@@ -2500,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);
@@ -2516,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.058085 seconds and 5 git commands to generate.