diff options
Diffstat (limited to 'drivers/gpu/drm/omapdrm/omap_gem.c')
-rw-r--r-- | drivers/gpu/drm/omapdrm/omap_gem.c | 311 |
1 files changed, 176 insertions, 135 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index 7ed08fdc4c42..8495a1a4b617 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -19,30 +19,22 @@ #include <linux/shmem_fs.h> #include <linux/spinlock.h> +#include <linux/pfn_t.h> #include <drm/drm_vma_manager.h> #include "omap_drv.h" #include "omap_dmm_tiler.h" -/* remove these once drm core helpers are merged */ -struct page **_drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask); -void _drm_gem_put_pages(struct drm_gem_object *obj, struct page **pages, - bool dirty, bool accessed); -int _drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size); - /* * GEM buffer object implementation. */ -#define to_omap_bo(x) container_of(x, struct omap_gem_object, base) - /* note: we use upper 8 bits of flags for driver-internal flags: */ -#define OMAP_BO_DMA 0x01000000 /* actually is physically contiguous */ +#define OMAP_BO_DMA 0x01000000 /* actually is physically contiguous */ #define OMAP_BO_EXT_SYNC 0x02000000 /* externally allocated sync object */ #define OMAP_BO_EXT_MEM 0x04000000 /* externally allocated memory */ - struct omap_gem_object { struct drm_gem_object base; @@ -119,8 +111,7 @@ struct omap_gem_object { } *sync; }; -static int get_pages(struct drm_gem_object *obj, struct page ***pages); -static uint64_t mmap_offset(struct drm_gem_object *obj); +#define to_omap_bo(x) container_of(x, struct omap_gem_object, base) /* To deal with userspace mmap'ings of 2d tiled buffers, which (a) are * not necessarily pinned in TILER all the time, and (b) when they are @@ -134,27 +125,69 @@ static uint64_t mmap_offset(struct drm_gem_object *obj); * for later.. */ #define NUM_USERGART_ENTRIES 2 -struct usergart_entry { +struct omap_drm_usergart_entry { struct tiler_block *block; /* the reserved tiler block */ dma_addr_t paddr; struct drm_gem_object *obj; /* the current pinned obj */ pgoff_t obj_pgoff; /* page offset of obj currently mapped in */ }; -static struct { - struct usergart_entry entry[NUM_USERGART_ENTRIES]; + +struct omap_drm_usergart { + struct omap_drm_usergart_entry entry[NUM_USERGART_ENTRIES]; int height; /* height in rows */ int height_shift; /* ilog2(height in rows) */ int slot_shift; /* ilog2(width per slot) */ int stride_pfn; /* stride in pages */ int last; /* index of last used entry */ -} *usergart; +}; + +/* ----------------------------------------------------------------------------- + * Helpers + */ + +/** get mmap offset */ +static uint64_t mmap_offset(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + int ret; + size_t size; + + WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + + /* Make it mmapable */ + size = omap_gem_mmap_size(obj); + ret = drm_gem_create_mmap_offset_size(obj, size); + if (ret) { + dev_err(dev->dev, "could not allocate mmap offset\n"); + return 0; + } + + return drm_vma_node_offset_addr(&obj->vma_node); +} + +/* GEM objects can either be allocated from contiguous memory (in which + * case obj->filp==NULL), or w/ shmem backing (obj->filp!=NULL). But non + * contiguous buffers can be remapped in TILER/DMM if they need to be + * contiguous... but we don't do this all the time to reduce pressure + * on TILER/DMM space when we know at allocation time that the buffer + * will need to be scanned out. + */ +static inline bool is_shmem(struct drm_gem_object *obj) +{ + return obj->filp != NULL; +} + +/* ----------------------------------------------------------------------------- + * Eviction + */ static void evict_entry(struct drm_gem_object *obj, - enum tiler_fmt fmt, struct usergart_entry *entry) + enum tiler_fmt fmt, struct omap_drm_usergart_entry *entry) { struct omap_gem_object *omap_obj = to_omap_bo(obj); - int n = usergart[fmt].height; + struct omap_drm_private *priv = obj->dev->dev_private; + int n = priv->usergart[fmt].height; size_t size = PAGE_SIZE * n; loff_t off = mmap_offset(obj) + (entry->obj_pgoff << PAGE_SHIFT); @@ -180,46 +213,25 @@ static void evict_entry(struct drm_gem_object *obj, static void evict(struct drm_gem_object *obj) { struct omap_gem_object *omap_obj = to_omap_bo(obj); + struct omap_drm_private *priv = obj->dev->dev_private; if (omap_obj->flags & OMAP_BO_TILED) { enum tiler_fmt fmt = gem2fmt(omap_obj->flags); int i; - if (!usergart) - return; - for (i = 0; i < NUM_USERGART_ENTRIES; i++) { - struct usergart_entry *entry = &usergart[fmt].entry[i]; + struct omap_drm_usergart_entry *entry = + &priv->usergart[fmt].entry[i]; + if (entry->obj == obj) evict_entry(obj, fmt, entry); } } } -/* GEM objects can either be allocated from contiguous memory (in which - * case obj->filp==NULL), or w/ shmem backing (obj->filp!=NULL). But non - * contiguous buffers can be remapped in TILER/DMM if they need to be - * contiguous... but we don't do this all the time to reduce pressure - * on TILER/DMM space when we know at allocation time that the buffer - * will need to be scanned out. - */ -static inline bool is_shmem(struct drm_gem_object *obj) -{ - return obj->filp != NULL; -} - -/** - * shmem buffers that are mapped cached can simulate coherency via using - * page faulting to keep track of dirty pages +/* ----------------------------------------------------------------------------- + * Page Management */ -static inline bool is_cached_coherent(struct drm_gem_object *obj) -{ - struct omap_gem_object *omap_obj = to_omap_bo(obj); - return is_shmem(obj) && - ((omap_obj->flags & OMAP_BO_CACHE_MASK) == OMAP_BO_CACHED); -} - -static DEFINE_SPINLOCK(sync_lock); /** ensure backing pages are allocated */ static int omap_gem_attach_pages(struct drm_gem_object *obj) @@ -272,6 +284,28 @@ free_pages: return ret; } +/* acquire pages when needed (for example, for DMA where physically + * contiguous buffer is not required + */ +static int get_pages(struct drm_gem_object *obj, struct page ***pages) +{ + struct omap_gem_object *omap_obj = to_omap_bo(obj); + int ret = 0; + + if (is_shmem(obj) && !omap_obj->pages) { + ret = omap_gem_attach_pages(obj); + if (ret) { + dev_err(obj->dev->dev, "could not attach pages\n"); + return ret; + } + } + + /* TODO: even phys-contig.. we should have a list of pages? */ + *pages = omap_obj->pages; + + return 0; +} + /** release backing pages */ static void omap_gem_detach_pages(struct drm_gem_object *obj) { @@ -301,26 +335,6 @@ uint32_t omap_gem_flags(struct drm_gem_object *obj) return to_omap_bo(obj)->flags; } -/** get mmap offset */ -static uint64_t mmap_offset(struct drm_gem_object *obj) -{ - struct drm_device *dev = obj->dev; - int ret; - size_t size; - - WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - - /* Make it mmapable */ - size = omap_gem_mmap_size(obj); - ret = drm_gem_create_mmap_offset_size(obj, size); - if (ret) { - dev_err(dev->dev, "could not allocate mmap offset\n"); - return 0; - } - - return drm_vma_node_offset_addr(&obj->vma_node); -} - uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj) { uint64_t offset; @@ -362,6 +376,10 @@ int omap_gem_tiled_size(struct drm_gem_object *obj, uint16_t *w, uint16_t *h) return -EINVAL; } +/* ----------------------------------------------------------------------------- + * Fault Handling + */ + /* Normal handling for the case of faulting in non-tiled buffers */ static int fault_1d(struct drm_gem_object *obj, struct vm_area_struct *vma, struct vm_fault *vmf) @@ -385,7 +403,8 @@ static int fault_1d(struct drm_gem_object *obj, VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address, pfn, pfn << PAGE_SHIFT); - return vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn); + return vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, + __pfn_to_pfn_t(pfn, PFN_DEV)); } /* Special handling for the case of faulting in 2d tiled buffers */ @@ -393,7 +412,8 @@ static int fault_2d(struct drm_gem_object *obj, struct vm_area_struct *vma, struct vm_fault *vmf) { struct omap_gem_object *omap_obj = to_omap_bo(obj); - struct usergart_entry *entry; + struct omap_drm_private *priv = obj->dev->dev_private; + struct omap_drm_usergart_entry *entry; enum tiler_fmt fmt = gem2fmt(omap_obj->flags); struct page *pages[64]; /* XXX is this too much to have on stack? */ unsigned long pfn; @@ -406,8 +426,8 @@ static int fault_2d(struct drm_gem_object *obj, * 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; + const int n = priv->usergart[fmt].height; + const int n_shift = priv->usergart[fmt].height_shift; /* * If buffer width in bytes > PAGE_SIZE then the virtual stride is @@ -428,11 +448,11 @@ static int fault_2d(struct drm_gem_object *obj, base_pgoff = round_down(pgoff, m << n_shift); /* figure out buffer width in slots */ - slots = omap_obj->width >> usergart[fmt].slot_shift; + slots = omap_obj->width >> priv->usergart[fmt].slot_shift; vaddr = vmf->virtual_address - ((pgoff - base_pgoff) << PAGE_SHIFT); - entry = &usergart[fmt].entry[usergart[fmt].last]; + entry = &priv->usergart[fmt].entry[priv->usergart[fmt].last]; /* evict previous buffer using this usergart entry, if any: */ if (entry->obj) @@ -478,13 +498,15 @@ static int fault_2d(struct drm_gem_object *obj, pfn, pfn << PAGE_SHIFT); for (i = n; i > 0; i--) { - vm_insert_mixed(vma, (unsigned long)vaddr, pfn); - pfn += usergart[fmt].stride_pfn; + vm_insert_mixed(vma, (unsigned long)vaddr, + __pfn_to_pfn_t(pfn, PFN_DEV)); + pfn += priv->usergart[fmt].stride_pfn; vaddr += PAGE_SIZE * m; } /* simple round-robin: */ - usergart[fmt].last = (usergart[fmt].last + 1) % NUM_USERGART_ENTRIES; + priv->usergart[fmt].last = (priv->usergart[fmt].last + 1) + % NUM_USERGART_ENTRIES; return 0; } @@ -596,6 +618,9 @@ int omap_gem_mmap_obj(struct drm_gem_object *obj, return 0; } +/* ----------------------------------------------------------------------------- + * Dumb Buffers + */ /** * omap_gem_dumb_create - create a dumb buffer @@ -653,6 +678,7 @@ fail: return ret; } +#ifdef CONFIG_DRM_FBDEV_EMULATION /* Set scrolling position. This allows us to implement fast scrolling * for console. * @@ -689,6 +715,22 @@ fail: return ret; } +#endif + +/* ----------------------------------------------------------------------------- + * Memory Management & DMA Sync + */ + +/** + * shmem buffers that are mapped cached can simulate coherency via using + * page faulting to keep track of dirty pages + */ +static inline bool is_cached_coherent(struct drm_gem_object *obj) +{ + struct omap_gem_object *omap_obj = to_omap_bo(obj); + return is_shmem(obj) && + ((omap_obj->flags & OMAP_BO_CACHE_MASK) == OMAP_BO_CACHED); +} /* Sync the buffer for CPU access.. note pages should already be * attached, ie. omap_gem_get_pages() @@ -865,28 +907,6 @@ int omap_gem_tiled_stride(struct drm_gem_object *obj, uint32_t orient) return ret; } -/* acquire pages when needed (for example, for DMA where physically - * contiguous buffer is not required - */ -static int get_pages(struct drm_gem_object *obj, struct page ***pages) -{ - struct omap_gem_object *omap_obj = to_omap_bo(obj); - int ret = 0; - - if (is_shmem(obj) && !omap_obj->pages) { - ret = omap_gem_attach_pages(obj); - if (ret) { - dev_err(obj->dev->dev, "could not attach pages\n"); - return ret; - } - } - - /* TODO: even phys-contig.. we should have a list of pages? */ - *pages = omap_obj->pages; - - return 0; -} - /* if !remap, and we don't have pages backing, then fail, rather than * increasing the pin count (which we don't really do yet anyways, * because we don't support swapping pages back out). And 'remap' @@ -924,6 +944,7 @@ int omap_gem_put_pages(struct drm_gem_object *obj) return 0; } +#ifdef CONFIG_DRM_FBDEV_EMULATION /* Get kernel virtual address for CPU access.. this more or less only * exists for omap_fbdev. This should be called with struct_mutex * held. @@ -942,6 +963,11 @@ void *omap_gem_vaddr(struct drm_gem_object *obj) } return omap_obj->vaddr; } +#endif + +/* ----------------------------------------------------------------------------- + * Power Management + */ #ifdef CONFIG_PM /* re-pin objects in DMM in resume path: */ @@ -971,6 +997,10 @@ int omap_gem_resume(struct device *dev) } #endif +/* ----------------------------------------------------------------------------- + * DebugFS + */ + #ifdef CONFIG_DEBUG_FS void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m) { @@ -1017,9 +1047,12 @@ void omap_gem_describe_objects(struct list_head *list, struct seq_file *m) } #endif -/* Buffer Synchronization: +/* ----------------------------------------------------------------------------- + * Buffer Synchronization */ +static DEFINE_SPINLOCK(sync_lock); + struct omap_gem_sync_waiter { struct list_head list; struct omap_gem_object *omap_obj; @@ -1265,6 +1298,10 @@ unlock: return ret; } +/* ----------------------------------------------------------------------------- + * Constructor & Destructor + */ + /* don't call directly.. called from GEM core when it is time to actually * free the object.. */ @@ -1282,8 +1319,6 @@ void omap_gem_free_object(struct drm_gem_object *obj) list_del(&omap_obj->mm_list); spin_unlock(&priv->list_lock); - drm_gem_free_mmap_offset(obj); - /* this means the object is still pinned.. which really should * not happen. I think.. */ @@ -1308,31 +1343,7 @@ void omap_gem_free_object(struct drm_gem_object *obj) drm_gem_object_release(obj); - kfree(obj); -} - -/* convenience method to construct a GEM buffer object, and userspace handle */ -int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file, - union omap_gem_size gsize, uint32_t flags, uint32_t *handle) -{ - struct drm_gem_object *obj; - int ret; - - obj = omap_gem_new(dev, gsize, flags); - if (!obj) - return -ENOMEM; - - ret = drm_gem_handle_create(file, obj, handle); - if (ret) { - drm_gem_object_release(obj); - kfree(obj); /* TODO isn't there a dtor to call? just copying i915 */ - return ret; - } - - /* drop reference from allocate - handle holds it now */ - drm_gem_object_unreference_unlocked(obj); - - return 0; + kfree(omap_obj); } /* GEM buffer object constructor */ @@ -1341,15 +1352,15 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev, { struct omap_drm_private *priv = dev->dev_private; struct omap_gem_object *omap_obj; - struct drm_gem_object *obj = NULL; + struct drm_gem_object *obj; struct address_space *mapping; size_t size; int ret; if (flags & OMAP_BO_TILED) { - if (!usergart) { + if (!priv->usergart) { dev_err(dev->dev, "Tiled buffers require DMM\n"); - goto fail; + return NULL; } /* tiled buffers are always shmem paged backed.. when they are @@ -1420,16 +1431,42 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev, return obj; fail: - if (obj) + omap_gem_free_object(obj); + return NULL; +} + +/* convenience method to construct a GEM buffer object, and userspace handle */ +int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file, + union omap_gem_size gsize, uint32_t flags, uint32_t *handle) +{ + struct drm_gem_object *obj; + int ret; + + obj = omap_gem_new(dev, gsize, flags); + if (!obj) + return -ENOMEM; + + ret = drm_gem_handle_create(file, obj, handle); + if (ret) { omap_gem_free_object(obj); + return ret; + } - return NULL; + /* drop reference from allocate - handle holds it now */ + drm_gem_object_unreference_unlocked(obj); + + return 0; } -/* init/cleanup.. if DMM is used, we need to set some stuff up.. */ +/* ----------------------------------------------------------------------------- + * Init & Cleanup + */ + +/* If DMM is used, we need to set some stuff up.. */ void omap_gem_init(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; + struct omap_drm_usergart *usergart; const enum tiler_fmt fmts[] = { TILFMT_8BIT, TILFMT_16BIT, TILFMT_32BIT }; @@ -1458,10 +1495,11 @@ void omap_gem_init(struct drm_device *dev) usergart[i].stride_pfn = tiler_stride(fmts[i], 0) >> PAGE_SHIFT; usergart[i].slot_shift = ilog2((PAGE_SIZE / h) >> i); for (j = 0; j < NUM_USERGART_ENTRIES; j++) { - struct usergart_entry *entry = &usergart[i].entry[j]; - struct tiler_block *block = - tiler_reserve_2d(fmts[i], w, h, - PAGE_SIZE); + struct omap_drm_usergart_entry *entry; + struct tiler_block *block; + + entry = &usergart[i].entry[j]; + block = tiler_reserve_2d(fmts[i], w, h, PAGE_SIZE); if (IS_ERR(block)) { dev_err(dev->dev, "reserve failed: %d, %d, %ld\n", @@ -1477,13 +1515,16 @@ void omap_gem_init(struct drm_device *dev) } } + priv->usergart = usergart; priv->has_dmm = true; } void omap_gem_deinit(struct drm_device *dev) { + struct omap_drm_private *priv = dev->dev_private; + /* I believe we can rely on there being no more outstanding GEM * objects which could depend on usergart/dmm at this point. */ - kfree(usergart); + kfree(priv->usergart); } |