staging: drm/omap: mmap of tiled buffers with stride >4kb
authorRob Clark <rob@ti.com>
Mon, 5 Mar 2012 16:48:40 +0000 (10:48 -0600)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 7 Mar 2012 21:38:08 +0000 (13:38 -0800)
Deal with the case of buffers with virtual stride larger than one
page in fault_2d().

Signed-off-by: Rob Clark <rob@ti.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/omapdrm/omap_gem.c

index 5abd294ab2ef5a35b0b75b87d497a69656381aef..921f058cc6a4d66c32ed2c6d7b9f228b9e4fe953 100644 (file)
@@ -153,10 +153,23 @@ static void evict_entry(struct drm_gem_object *obj,
                enum tiler_fmt fmt, struct usergart_entry *entry)
 {
        if (obj->dev->dev_mapping) {
-               size_t size = PAGE_SIZE * usergart[fmt].height;
+               struct omap_gem_object *omap_obj = to_omap_bo(obj);
+               int n = usergart[fmt].height;
+               size_t size = PAGE_SIZE * n;
                loff_t off = mmap_offset(obj) +
                                (entry->obj_pgoff << PAGE_SHIFT);
-               unmap_mapping_range(obj->dev->dev_mapping, off, size, 1);
+               const int m = 1 + ((omap_obj->width << fmt) / PAGE_SIZE);
+               if (m > 1) {
+                       int i;
+                       /* if stride > than PAGE_SIZE then sparse mapping: */
+                       for (i = n; i > 0; i--) {
+                               unmap_mapping_range(obj->dev->dev_mapping,
+                                               off, PAGE_SIZE, 1);
+                               off += PAGE_SIZE * m;
+                       }
+               } else {
+                       unmap_mapping_range(obj->dev->dev_mapping, off, size, 1);
+               }
        }
 
        entry->obj = NULL;
@@ -342,26 +355,39 @@ static int fault_2d(struct drm_gem_object *obj,
        void __user *vaddr;
        int i, ret, slots;
 
-       if (!usergart)
-               return -EFAULT;
-
-       /* TODO: this fxn might need a bit tweaking to deal w/ tiled buffers
-        * that are wider than 4kb
+       /*
+        * Note the height of the slot is also equal to the number of pages
+        * that need to be mapped in to fill 4kb wide CPU page.  If the slot
+        * height is 64, then 64 pages fill a 4kb wide by 64 row region.
+        */
+       const int n = usergart[fmt].height;
+       const int n_shift = usergart[fmt].height_shift;
+
+       /*
+        * If buffer width in bytes > PAGE_SIZE then the virtual stride is
+        * rounded up to next multiple of PAGE_SIZE.. this need to be taken
+        * into account in some of the math, so figure out virtual stride
+        * in pages
         */
+       const int m = 1 + ((omap_obj->width << fmt) / PAGE_SIZE);
 
        /* We don't use vmf->pgoff since that has the fake offset: */
        pgoff = ((unsigned long)vmf->virtual_address -
                        vma->vm_start) >> PAGE_SHIFT;
 
-       /* actual address we start mapping at is rounded down to previous slot
+       /*
+        * Actual address we start mapping at is rounded down to previous slot
         * boundary in the y direction:
         */
-       base_pgoff = round_down(pgoff, usergart[fmt].height);
-       vaddr = vmf->virtual_address - ((pgoff - base_pgoff) << PAGE_SHIFT);
-       entry = &usergart[fmt].entry[usergart[fmt].last];
+       base_pgoff = round_down(pgoff, m << n_shift);
 
+       /* figure out buffer width in slots */
        slots = omap_obj->width >> usergart[fmt].slot_shift;
 
+       vaddr = vmf->virtual_address - ((pgoff - base_pgoff) << PAGE_SHIFT);
+
+       entry = &usergart[fmt].entry[usergart[fmt].last];
+
        /* evict previous buffer using this usergart entry, if any: */
        if (entry->obj)
                evict_entry(entry->obj, fmt, entry);
@@ -369,23 +395,30 @@ static int fault_2d(struct drm_gem_object *obj,
        entry->obj = obj;
        entry->obj_pgoff = base_pgoff;
 
-       /* now convert base_pgoff to phys offset from virt offset:
-        */
-       base_pgoff = (base_pgoff >> usergart[fmt].height_shift) * slots;
-
-       /* map in pages.  Note the height of the slot is also equal to the
-        * number of pages that need to be mapped in to fill 4kb wide CPU page.
-        * If the height is 64, then 64 pages fill a 4kb wide by 64 row region.
-        * Beyond the valid pixel part of the buffer, we set pages[i] to NULL to
-        * get a dummy page mapped in.. if someone reads/writes it they will get
-        * random/undefined content, but at least it won't be corrupting
-        * whatever other random page used to be mapped in, or other undefined
-        * behavior.
+       /* now convert base_pgoff to phys offset from virt offset: */
+       base_pgoff = (base_pgoff >> n_shift) * slots;
+
+       /* for wider-than 4k.. figure out which part of the slot-row we want: */
+       if (m > 1) {
+               int off = pgoff % m;
+               entry->obj_pgoff += off;
+               base_pgoff /= m;
+               slots = min(slots - (off << n_shift), n);
+               base_pgoff += off << n_shift;
+               vaddr += off << PAGE_SHIFT;
+       }
+
+       /*
+        * Map in pages. Beyond the valid pixel part of the buffer, we set
+        * pages[i] to NULL to get a dummy page mapped in.. if someone
+        * reads/writes it they will get random/undefined content, but at
+        * least it won't be corrupting whatever other random page used to
+        * be mapped in, or other undefined behavior.
         */
        memcpy(pages, &omap_obj->pages[base_pgoff],
                        sizeof(struct page *) * slots);
        memset(pages + slots, 0,
-                       sizeof(struct page *) * (usergart[fmt].height - slots));
+                       sizeof(struct page *) * (n - slots));
 
        ret = tiler_pin(entry->block, pages, ARRAY_SIZE(pages), 0, true);
        if (ret) {
@@ -393,16 +426,15 @@ static int fault_2d(struct drm_gem_object *obj,
                return ret;
        }
 
-       i = usergart[fmt].height;
        pfn = entry->paddr >> PAGE_SHIFT;
 
        VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address,
                        pfn, pfn << PAGE_SHIFT);
 
-       while (i--) {
+       for (i = n; i > 0; i--) {
                vm_insert_mixed(vma, (unsigned long)vaddr, pfn);
                pfn += usergart[fmt].stride_pfn;
-               vaddr += PAGE_SIZE;
+               vaddr += PAGE_SIZE * m;
        }
 
        /* simple round-robin: */