diff mbox

[10/10] staging: drm/omap: mmap of tiled buffers with stride >4kb

Message ID 1330966120-28582-11-git-send-email-rob.clark@linaro.org
State Accepted
Commit e559895ae99fb99ecb91d5ad9e0b742ad881b341
Headers show

Commit Message

Rob Clark March 5, 2012, 4:48 p.m. UTC
From: Rob Clark <rob@ti.com>

Deal with the case of buffers with virtual stride larger than one
page in fault_2d().

Signed-off-by: Rob Clark <rob@ti.com>
---
 drivers/staging/omapdrm/omap_gem.c |   86 ++++++++++++++++++++++++-----------
 1 files changed, 59 insertions(+), 27 deletions(-)
diff mbox

Patch

diff --git a/drivers/staging/omapdrm/omap_gem.c b/drivers/staging/omapdrm/omap_gem.c
index 5abd294..921f058 100644
--- a/drivers/staging/omapdrm/omap_gem.c
+++ b/drivers/staging/omapdrm/omap_gem.c
@@ -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: */