#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;
} *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
* 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);
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)
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)
{
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;
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)
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;
* 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
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)
for (i = n; i > 0; i--) {
vm_insert_mixed(vma, (unsigned long)vaddr, pfn);
- pfn += usergart[fmt].stride_pfn;
+ 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;
}
return 0;
}
+/* -----------------------------------------------------------------------------
+ * Dumb Buffers
+ */
/**
* omap_gem_dumb_create - create a dumb buffer
return ret;
}
+#ifdef CONFIG_DRM_FBDEV_EMULATION
/* Set scrolling position. This allows us to implement fast scrolling
* for console.
*
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()
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'
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.
}
return omap_obj->vaddr;
}
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Power Management
+ */
#ifdef CONFIG_PM
/* re-pin objects in DMM in resume path: */
}
#endif
+/* -----------------------------------------------------------------------------
+ * DebugFS
+ */
+
#ifdef CONFIG_DEBUG_FS
void omap_gem_describe(struct drm_gem_object *obj, 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;
return ret;
}
+/* -----------------------------------------------------------------------------
+ * Constructor & Destructor
+ */
+
/* don't call directly.. called from GEM core when it is time to actually
* free the object..
*/
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..
*/
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 */
{
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
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
};
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",
}
}
+ 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);
}
*
* Authors: Ravi Ramachandra <r.ramachandra@ti.com>,
* Lajos Molnar <molnar@ti.com>
+ * Andy Gross <andy.gross@ti.com>
*
- * Copyright (C) 2009-2010 Texas Instruments, Inc.
+ * Copyright (C) 2012 Texas Instruments, Inc.
*
* This package is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
*/
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/bitmap.h>
#include <linux/slab.h>
-#include <linux/spinlock.h>
+#include "tcm.h"
-#include "tcm-sita.h"
-
-#define ALIGN_DOWN(value, align) ((value) & ~((align) - 1))
-
-/* Individual selection criteria for different scan areas */
-static s32 CR_L2R_T2B = CR_BIAS_HORIZONTAL;
-static s32 CR_R2L_T2B = CR_DIAGONAL_BALANCE;
-
-/*********************************************
- * TCM API - Sita Implementation
- *********************************************/
-static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u8 align,
- struct tcm_area *area);
-static s32 sita_reserve_1d(struct tcm *tcm, u32 slots, struct tcm_area *area);
-static s32 sita_free(struct tcm *tcm, struct tcm_area *area);
-static void sita_deinit(struct tcm *tcm);
-
-/*********************************************
- * Main Scanner functions
- *********************************************/
-static s32 scan_areas_and_find_fit(struct tcm *tcm, u16 w, u16 h, u16 align,
- struct tcm_area *area);
-
-static s32 scan_l2r_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
- struct tcm_area *field, struct tcm_area *area);
-
-static s32 scan_r2l_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
- struct tcm_area *field, struct tcm_area *area);
-
-static s32 scan_r2l_b2t_one_dim(struct tcm *tcm, u32 num_slots,
- struct tcm_area *field, struct tcm_area *area);
-
-/*********************************************
- * Support Infrastructure Methods
- *********************************************/
-static s32 is_area_free(struct tcm_area ***map, u16 x0, u16 y0, u16 w, u16 h);
-
-static s32 update_candidate(struct tcm *tcm, u16 x0, u16 y0, u16 w, u16 h,
- struct tcm_area *field, s32 criteria,
- struct score *best);
-
-static void get_nearness_factor(struct tcm_area *field,
- struct tcm_area *candidate,
- struct nearness_factor *nf);
-
-static void get_neighbor_stats(struct tcm *tcm, struct tcm_area *area,
- struct neighbor_stats *stat);
-
-static void fill_area(struct tcm *tcm,
- struct tcm_area *area, struct tcm_area *parent);
-
-
-/*********************************************/
-
-/*********************************************
- * Utility Methods
- *********************************************/
-struct tcm *sita_init(u16 width, u16 height, struct tcm_pt *attr)
-{
- struct tcm *tcm;
- struct sita_pvt *pvt;
- struct tcm_area area = {0};
- s32 i;
-
- if (width == 0 || height == 0)
- return NULL;
-
- tcm = kzalloc(sizeof(*tcm), GFP_KERNEL);
- pvt = kzalloc(sizeof(*pvt), GFP_KERNEL);
- if (!tcm || !pvt)
- goto error;
-
- /* Updating the pointers to SiTA implementation APIs */
- tcm->height = height;
- tcm->width = width;
- tcm->reserve_2d = sita_reserve_2d;
- tcm->reserve_1d = sita_reserve_1d;
- tcm->free = sita_free;
- tcm->deinit = sita_deinit;
- tcm->pvt = (void *)pvt;
-
- spin_lock_init(&(pvt->lock));
-
- /* Creating tam map */
- pvt->map = kmalloc(sizeof(*pvt->map) * tcm->width, GFP_KERNEL);
- if (!pvt->map)
- goto error;
-
- for (i = 0; i < tcm->width; i++) {
- pvt->map[i] =
- kmalloc(sizeof(**pvt->map) * tcm->height,
- GFP_KERNEL);
- if (pvt->map[i] == NULL) {
- while (i--)
- kfree(pvt->map[i]);
- kfree(pvt->map);
- goto error;
- }
- }
-
- if (attr && attr->x <= tcm->width && attr->y <= tcm->height) {
- pvt->div_pt.x = attr->x;
- pvt->div_pt.y = attr->y;
-
- } else {
- /* Defaulting to 3:1 ratio on width for 2D area split */
- /* Defaulting to 3:1 ratio on height for 2D and 1D split */
- pvt->div_pt.x = (tcm->width * 3) / 4;
- pvt->div_pt.y = (tcm->height * 3) / 4;
- }
-
- spin_lock(&(pvt->lock));
- assign(&area, 0, 0, width - 1, height - 1);
- fill_area(tcm, &area, NULL);
- spin_unlock(&(pvt->lock));
- return tcm;
-
-error:
- kfree(tcm);
- kfree(pvt);
- return NULL;
-}
-
-static void sita_deinit(struct tcm *tcm)
-{
- struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
- struct tcm_area area = {0};
- s32 i;
-
- area.p1.x = tcm->width - 1;
- area.p1.y = tcm->height - 1;
-
- spin_lock(&(pvt->lock));
- fill_area(tcm, &area, NULL);
- spin_unlock(&(pvt->lock));
-
- for (i = 0; i < tcm->height; i++)
- kfree(pvt->map[i]);
- kfree(pvt->map);
- kfree(pvt);
-}
-
-/**
- * Reserve a 1D area in the container
- *
- * @param num_slots size of 1D area
- * @param area pointer to the area that will be populated with the
- * reserved area
- *
- * @return 0 on success, non-0 error value on failure.
+static unsigned long mask[8];
+/*
+ * pos position in bitmap
+ * w width in slots
+ * h height in slots
+ * map ptr to bitmap
+ * stride slots in a row
*/
-static s32 sita_reserve_1d(struct tcm *tcm, u32 num_slots,
- struct tcm_area *area)
+static void free_slots(unsigned long pos, uint16_t w, uint16_t h,
+ unsigned long *map, uint16_t stride)
{
- s32 ret;
- struct tcm_area field = {0};
- struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+ int i;
- spin_lock(&(pvt->lock));
-
- /* Scanning entire container */
- assign(&field, tcm->width - 1, tcm->height - 1, 0, 0);
-
- ret = scan_r2l_b2t_one_dim(tcm, num_slots, &field, area);
- if (!ret)
- /* update map */
- fill_area(tcm, area, area);
-
- spin_unlock(&(pvt->lock));
- return ret;
+ for (i = 0; i < h; i++, pos += stride)
+ bitmap_clear(map, pos, w);
}
-/**
- * Reserve a 2D area in the container
- *
- * @param w width
- * @param h height
- * @param area pointer to the area that will be populated with the reserved
- * area
- *
- * @return 0 on success, non-0 error value on failure.
+/*
+ * w width in slots
+ * pos ptr to position
+ * map ptr to bitmap
+ * num_bits number of bits in bitmap
*/
-static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u8 align,
- struct tcm_area *area)
+static int r2l_b2t_1d(uint16_t w, unsigned long *pos, unsigned long *map,
+ size_t num_bits)
{
- s32 ret;
- struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+ unsigned long search_count = 0;
+ unsigned long bit;
+ bool area_found = false;
- /* not supporting more than 64 as alignment */
- if (align > 64)
- return -EINVAL;
+ *pos = num_bits - w;
- /* we prefer 1, 32 and 64 as alignment */
- align = align <= 1 ? 1 : align <= 32 ? 32 : 64;
+ while (search_count < num_bits) {
+ bit = find_next_bit(map, num_bits, *pos);
- spin_lock(&(pvt->lock));
- ret = scan_areas_and_find_fit(tcm, w, h, align, area);
- if (!ret)
- /* update map */
- fill_area(tcm, area, area);
+ if (bit - *pos >= w) {
+ /* found a long enough free area */
+ bitmap_set(map, *pos, w);
+ area_found = true;
+ break;
+ }
- spin_unlock(&(pvt->lock));
- return ret;
+ search_count = num_bits - bit + w;
+ *pos = bit - w;
+ }
+
+ return (area_found) ? 0 : -ENOMEM;
}
-/**
- * Unreserve a previously allocated 2D or 1D area
- * @param area area to be freed
- * @return 0 - success
+/*
+ * w = width in slots
+ * h = height in slots
+ * a = align in slots (mask, 2^n-1, 0 is unaligned)
+ * offset = offset in bytes from 4KiB
+ * pos = position in bitmap for buffer
+ * map = bitmap ptr
+ * num_bits = size of bitmap
+ * stride = bits in one row of container
*/
-static s32 sita_free(struct tcm *tcm, struct tcm_area *area)
+static int l2r_t2b(uint16_t w, uint16_t h, uint16_t a, int16_t offset,
+ unsigned long *pos, unsigned long slot_bytes,
+ unsigned long *map, size_t num_bits, size_t slot_stride)
{
- struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
+ int i;
+ unsigned long index;
+ bool area_free;
+ unsigned long slots_per_band = PAGE_SIZE / slot_bytes;
+ unsigned long bit_offset = (offset > 0) ? offset / slot_bytes : 0;
+ unsigned long curr_bit = bit_offset;
+
+ /* reset alignment to 1 if we are matching a specific offset */
+ /* adjust alignment - 1 to get to the format expected in bitmaps */
+ a = (offset > 0) ? 0 : a - 1;
+
+ /* FIXME Return error if slots_per_band > stride */
+
+ while (curr_bit < num_bits) {
+ *pos = bitmap_find_next_zero_area(map, num_bits, curr_bit, w,
+ a);
+
+ /* skip forward if we are not at right offset */
+ if (bit_offset > 0 && (*pos % slots_per_band != bit_offset)) {
+ curr_bit = ALIGN(*pos, slots_per_band) + bit_offset;
+ continue;
+ }
- spin_lock(&(pvt->lock));
+ /* skip forward to next row if we overlap end of row */
+ if ((*pos % slot_stride) + w > slot_stride) {
+ curr_bit = ALIGN(*pos, slot_stride) + bit_offset;
+ continue;
+ }
- /* check that this is in fact an existing area */
- WARN_ON(pvt->map[area->p0.x][area->p0.y] != area ||
- pvt->map[area->p1.x][area->p1.y] != area);
+ /* TODO: Handle overlapping 4K boundaries */
- /* Clear the contents of the associated tiles in the map */
- fill_area(tcm, area, NULL);
+ /* break out of look if we will go past end of container */
+ if ((*pos + slot_stride * h) > num_bits)
+ break;
- spin_unlock(&(pvt->lock));
+ /* generate mask that represents out matching pattern */
+ bitmap_clear(mask, 0, slot_stride);
+ bitmap_set(mask, (*pos % BITS_PER_LONG), w);
- return 0;
-}
-
-/**
- * Note: In general the cordinates in the scan field area relevant to the can
- * sweep directions. The scan origin (e.g. top-left corner) will always be
- * the p0 member of the field. Therfore, for a scan from top-left p0.x <= p1.x
- * and p0.y <= p1.y; whereas, for a scan from bottom-right p1.x <= p0.x and p1.y
- * <= p0.y
- */
+ /* assume the area is free until we find an overlap */
+ area_free = true;
-/**
- * Raster scan horizontally right to left from top to bottom to find a place for
- * a 2D area of given size inside a scan field.
- *
- * @param w width of desired area
- * @param h height of desired area
- * @param align desired area alignment
- * @param area pointer to the area that will be set to the best position
- * @param field area to scan (inclusive)
- *
- * @return 0 on success, non-0 error value on failure.
- */
-static s32 scan_r2l_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
- struct tcm_area *field, struct tcm_area *area)
-{
- s32 x, y;
- s16 start_x, end_x, start_y, end_y, found_x = -1;
- struct tcm_area ***map = ((struct sita_pvt *)tcm->pvt)->map;
- struct score best = {{0}, {0}, {0}, 0};
-
- start_x = field->p0.x;
- end_x = field->p1.x;
- start_y = field->p0.y;
- end_y = field->p1.y;
-
- /* check scan area co-ordinates */
- if (field->p0.x < field->p1.x ||
- field->p1.y < field->p0.y)
- return -EINVAL;
-
- /* check if allocation would fit in scan area */
- if (w > LEN(start_x, end_x) || h > LEN(end_y, start_y))
- return -ENOSPC;
-
- /* adjust start_x and end_y, as allocation would not fit beyond */
- start_x = ALIGN_DOWN(start_x - w + 1, align); /* - 1 to be inclusive */
- end_y = end_y - h + 1;
-
- /* check if allocation would still fit in scan area */
- if (start_x < end_x)
- return -ENOSPC;
-
- /* scan field top-to-bottom, right-to-left */
- for (y = start_y; y <= end_y; y++) {
- for (x = start_x; x >= end_x; x -= align) {
- if (is_area_free(map, x, y, w, h)) {
- found_x = x;
-
- /* update best candidate */
- if (update_candidate(tcm, x, y, w, h, field,
- CR_R2L_T2B, &best))
- goto done;
-
- /* change upper x bound */
- end_x = x + 1;
+ /* check subsequent rows to see if complete area is free */
+ for (i = 1; i < h; i++) {
+ index = *pos / BITS_PER_LONG + i * 8;
+ if (bitmap_intersects(&map[index], mask,
+ (*pos % BITS_PER_LONG) + w)) {
+ area_free = false;
break;
- } else if (map[x][y] && map[x][y]->is2d) {
- /* step over 2D areas */
- x = ALIGN(map[x][y]->p0.x - w + 1, align);
}
}
- /* break if you find a free area shouldering the scan field */
- if (found_x == start_x)
+ if (area_free)
break;
- }
-
- if (!best.a.tcm)
- return -ENOSPC;
-done:
- assign(area, best.a.p0.x, best.a.p0.y, best.a.p1.x, best.a.p1.y);
- return 0;
-}
-
-/**
- * Raster scan horizontally left to right from top to bottom to find a place for
- * a 2D area of given size inside a scan field.
- *
- * @param w width of desired area
- * @param h height of desired area
- * @param align desired area alignment
- * @param area pointer to the area that will be set to the best position
- * @param field area to scan (inclusive)
- *
- * @return 0 on success, non-0 error value on failure.
- */
-static s32 scan_l2r_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
- struct tcm_area *field, struct tcm_area *area)
-{
- s32 x, y;
- s16 start_x, end_x, start_y, end_y, found_x = -1;
- struct tcm_area ***map = ((struct sita_pvt *)tcm->pvt)->map;
- struct score best = {{0}, {0}, {0}, 0};
-
- start_x = field->p0.x;
- end_x = field->p1.x;
- start_y = field->p0.y;
- end_y = field->p1.y;
-
- /* check scan area co-ordinates */
- if (field->p1.x < field->p0.x ||
- field->p1.y < field->p0.y)
- return -EINVAL;
-
- /* check if allocation would fit in scan area */
- if (w > LEN(end_x, start_x) || h > LEN(end_y, start_y))
- return -ENOSPC;
-
- start_x = ALIGN(start_x, align);
-
- /* check if allocation would still fit in scan area */
- if (w > LEN(end_x, start_x))
- return -ENOSPC;
-
- /* adjust end_x and end_y, as allocation would not fit beyond */
- end_x = end_x - w + 1; /* + 1 to be inclusive */
- end_y = end_y - h + 1;
-
- /* scan field top-to-bottom, left-to-right */
- for (y = start_y; y <= end_y; y++) {
- for (x = start_x; x <= end_x; x += align) {
- if (is_area_free(map, x, y, w, h)) {
- found_x = x;
-
- /* update best candidate */
- if (update_candidate(tcm, x, y, w, h, field,
- CR_L2R_T2B, &best))
- goto done;
- /* change upper x bound */
- end_x = x - 1;
- break;
- } else if (map[x][y] && map[x][y]->is2d) {
- /* step over 2D areas */
- x = ALIGN_DOWN(map[x][y]->p1.x, align);
- }
- }
+ /* go forward past this match */
+ if (bit_offset > 0)
+ curr_bit = ALIGN(*pos, slots_per_band) + bit_offset;
+ else
+ curr_bit = *pos + a + 1;
+ }
- /* break if you find a free area shouldering the scan field */
- if (found_x == start_x)
- break;
+ if (area_free) {
+ /* set area as in-use. iterate over rows */
+ for (i = 0, index = *pos; i < h; i++, index += slot_stride)
+ bitmap_set(map, index, w);
}
- if (!best.a.tcm)
- return -ENOSPC;
-done:
- assign(area, best.a.p0.x, best.a.p0.y, best.a.p1.x, best.a.p1.y);
- return 0;
+ return (area_free) ? 0 : -ENOMEM;
}
-/**
- * Raster scan horizontally right to left from bottom to top to find a place
- * for a 1D area of given size inside a scan field.
- *
- * @param num_slots size of desired area
- * @param align desired area alignment
- * @param area pointer to the area that will be set to the best
- * position
- * @param field area to scan (inclusive)
- *
- * @return 0 on success, non-0 error value on failure.
- */
-static s32 scan_r2l_b2t_one_dim(struct tcm *tcm, u32 num_slots,
- struct tcm_area *field, struct tcm_area *area)
+static s32 sita_reserve_1d(struct tcm *tcm, u32 num_slots,
+ struct tcm_area *area)
{
- s32 found = 0;
- s16 x, y;
- struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
- struct tcm_area *p;
-
- /* check scan area co-ordinates */
- if (field->p0.y < field->p1.y)
- return -EINVAL;
-
- /**
- * Currently we only support full width 1D scan field, which makes sense
- * since 1D slot-ordering spans the full container width.
- */
- if (tcm->width != field->p0.x - field->p1.x + 1)
- return -EINVAL;
-
- /* check if allocation would fit in scan area */
- if (num_slots > tcm->width * LEN(field->p0.y, field->p1.y))
- return -ENOSPC;
-
- x = field->p0.x;
- y = field->p0.y;
-
- /* find num_slots consecutive free slots to the left */
- while (found < num_slots) {
- if (y < 0)
- return -ENOSPC;
-
- /* remember bottom-right corner */
- if (found == 0) {
- area->p1.x = x;
- area->p1.y = y;
- }
-
- /* skip busy regions */
- p = pvt->map[x][y];
- if (p) {
- /* move to left of 2D areas, top left of 1D */
- x = p->p0.x;
- if (!p->is2d)
- y = p->p0.y;
-
- /* start over */
- found = 0;
- } else {
- /* count consecutive free slots */
- found++;
- if (found == num_slots)
- break;
- }
-
- /* move to the left */
- if (x == 0)
- y--;
- x = (x ? : tcm->width) - 1;
-
+ unsigned long pos;
+ int ret;
+
+ spin_lock(&(tcm->lock));
+ ret = r2l_b2t_1d(num_slots, &pos, tcm->bitmap, tcm->map_size);
+ if (!ret) {
+ area->p0.x = pos % tcm->width;
+ area->p0.y = pos / tcm->width;
+ area->p1.x = (pos + num_slots - 1) % tcm->width;
+ area->p1.y = (pos + num_slots - 1) / tcm->width;
}
+ spin_unlock(&(tcm->lock));
- /* set top-left corner */
- area->p0.x = x;
- area->p0.y = y;
- return 0;
+ return ret;
}
-/**
- * Find a place for a 2D area of given size inside a scan field based on its
- * alignment needs.
- *
- * @param w width of desired area
- * @param h height of desired area
- * @param align desired area alignment
- * @param area pointer to the area that will be set to the best position
- *
- * @return 0 on success, non-0 error value on failure.
- */
-static s32 scan_areas_and_find_fit(struct tcm *tcm, u16 w, u16 h, u16 align,
- struct tcm_area *area)
+static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u16 align,
+ int16_t offset, uint16_t slot_bytes,
+ struct tcm_area *area)
{
- s32 ret = 0;
- struct tcm_area field = {0};
- u16 boundary_x, boundary_y;
- struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
-
- if (align > 1) {
- /* prefer top-left corner */
- boundary_x = pvt->div_pt.x - 1;
- boundary_y = pvt->div_pt.y - 1;
-
- /* expand width and height if needed */
- if (w > pvt->div_pt.x)
- boundary_x = tcm->width - 1;
- if (h > pvt->div_pt.y)
- boundary_y = tcm->height - 1;
-
- assign(&field, 0, 0, boundary_x, boundary_y);
- ret = scan_l2r_t2b(tcm, w, h, align, &field, area);
-
- /* scan whole container if failed, but do not scan 2x */
- if (ret != 0 && (boundary_x != tcm->width - 1 ||
- boundary_y != tcm->height - 1)) {
- /* scan the entire container if nothing found */
- assign(&field, 0, 0, tcm->width - 1, tcm->height - 1);
- ret = scan_l2r_t2b(tcm, w, h, align, &field, area);
- }
- } else if (align == 1) {
- /* prefer top-right corner */
- boundary_x = pvt->div_pt.x;
- boundary_y = pvt->div_pt.y - 1;
-
- /* expand width and height if needed */
- if (w > (tcm->width - pvt->div_pt.x))
- boundary_x = 0;
- if (h > pvt->div_pt.y)
- boundary_y = tcm->height - 1;
-
- assign(&field, tcm->width - 1, 0, boundary_x, boundary_y);
- ret = scan_r2l_t2b(tcm, w, h, align, &field, area);
-
- /* scan whole container if failed, but do not scan 2x */
- if (ret != 0 && (boundary_x != 0 ||
- boundary_y != tcm->height - 1)) {
- /* scan the entire container if nothing found */
- assign(&field, tcm->width - 1, 0, 0, tcm->height - 1);
- ret = scan_r2l_t2b(tcm, w, h, align, &field,
- area);
- }
+ unsigned long pos;
+ int ret;
+
+ spin_lock(&(tcm->lock));
+ ret = l2r_t2b(w, h, align, offset, &pos, slot_bytes, tcm->bitmap,
+ tcm->map_size, tcm->width);
+
+ if (!ret) {
+ area->p0.x = pos % tcm->width;
+ area->p0.y = pos / tcm->width;
+ area->p1.x = area->p0.x + w - 1;
+ area->p1.y = area->p0.y + h - 1;
}
+ spin_unlock(&(tcm->lock));
return ret;
}
-/* check if an entire area is free */
-static s32 is_area_free(struct tcm_area ***map, u16 x0, u16 y0, u16 w, u16 h)
+static void sita_deinit(struct tcm *tcm)
{
- u16 x = 0, y = 0;
- for (y = y0; y < y0 + h; y++) {
- for (x = x0; x < x0 + w; x++) {
- if (map[x][y])
- return false;
- }
- }
- return true;
+ kfree(tcm);
}
-/* fills an area with a parent tcm_area */
-static void fill_area(struct tcm *tcm, struct tcm_area *area,
- struct tcm_area *parent)
+static s32 sita_free(struct tcm *tcm, struct tcm_area *area)
{
- s32 x, y;
- struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
- struct tcm_area a, a_;
-
- /* set area's tcm; otherwise, enumerator considers it invalid */
- area->tcm = tcm;
-
- tcm_for_each_slice(a, *area, a_) {
- for (x = a.p0.x; x <= a.p1.x; ++x)
- for (y = a.p0.y; y <= a.p1.y; ++y)
- pvt->map[x][y] = parent;
+ unsigned long pos;
+ uint16_t w, h;
+ pos = area->p0.x + area->p0.y * tcm->width;
+ if (area->is2d) {
+ w = area->p1.x - area->p0.x + 1;
+ h = area->p1.y - area->p0.y + 1;
+ } else {
+ w = area->p1.x + area->p1.y * tcm->width - pos + 1;
+ h = 1;
}
+
+ spin_lock(&(tcm->lock));
+ free_slots(pos, w, h, tcm->bitmap, tcm->width);
+ spin_unlock(&(tcm->lock));
+ return 0;
}
-/**
- * Compares a candidate area to the current best area, and if it is a better
- * fit, it updates the best to this one.
- *
- * @param x0, y0, w, h top, left, width, height of candidate area
- * @param field scan field
- * @param criteria scan criteria
- * @param best best candidate and its scores
- *
- * @return 1 (true) if the candidate area is known to be the final best, so no
- * more searching should be performed
- */
-static s32 update_candidate(struct tcm *tcm, u16 x0, u16 y0, u16 w, u16 h,
- struct tcm_area *field, s32 criteria,
- struct score *best)
+struct tcm *sita_init(u16 width, u16 height)
{
- struct score me; /* score for area */
-
- /*
- * NOTE: For horizontal bias we always give the first found, because our
- * scan is horizontal-raster-based and the first candidate will always
- * have the horizontal bias.
- */
- bool first = criteria & CR_BIAS_HORIZONTAL;
-
- assign(&me.a, x0, y0, x0 + w - 1, y0 + h - 1);
-
- /* calculate score for current candidate */
- if (!first) {
- get_neighbor_stats(tcm, &me.a, &me.n);
- me.neighs = me.n.edge + me.n.busy;
- get_nearness_factor(field, &me.a, &me.f);
- }
-
- /* the 1st candidate is always the best */
- if (!best->a.tcm)
- goto better;
+ struct tcm *tcm;
+ size_t map_size = BITS_TO_LONGS(width*height) * sizeof(unsigned long);
- BUG_ON(first);
+ if (width == 0 || height == 0)
+ return NULL;
- /* diagonal balance check */
- if ((criteria & CR_DIAGONAL_BALANCE) &&
- best->neighs <= me.neighs &&
- (best->neighs < me.neighs ||
- /* this implies that neighs and occupied match */
- best->n.busy < me.n.busy ||
- (best->n.busy == me.n.busy &&
- /* check the nearness factor */
- best->f.x + best->f.y > me.f.x + me.f.y)))
- goto better;
+ tcm = kzalloc(sizeof(*tcm) + map_size, GFP_KERNEL);
+ if (!tcm)
+ goto error;
- /* not better, keep going */
- return 0;
+ /* Updating the pointers to SiTA implementation APIs */
+ tcm->height = height;
+ tcm->width = width;
+ tcm->reserve_2d = sita_reserve_2d;
+ tcm->reserve_1d = sita_reserve_1d;
+ tcm->free = sita_free;
+ tcm->deinit = sita_deinit;
-better:
- /* save current area as best */
- memcpy(best, &me, sizeof(me));
- best->a.tcm = tcm;
- return first;
-}
+ spin_lock_init(&tcm->lock);
+ tcm->bitmap = (unsigned long *)(tcm + 1);
+ bitmap_clear(tcm->bitmap, 0, width*height);
-/**
- * Calculate the nearness factor of an area in a search field. The nearness
- * factor is smaller if the area is closer to the search origin.
- */
-static void get_nearness_factor(struct tcm_area *field, struct tcm_area *area,
- struct nearness_factor *nf)
-{
- /**
- * Using signed math as field coordinates may be reversed if
- * search direction is right-to-left or bottom-to-top.
- */
- nf->x = (s32)(area->p0.x - field->p0.x) * 1000 /
- (field->p1.x - field->p0.x);
- nf->y = (s32)(area->p0.y - field->p0.y) * 1000 /
- (field->p1.y - field->p0.y);
-}
+ tcm->map_size = width*height;
-/* get neighbor statistics */
-static void get_neighbor_stats(struct tcm *tcm, struct tcm_area *area,
- struct neighbor_stats *stat)
-{
- s16 x = 0, y = 0;
- struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
-
- /* Clearing any exisiting values */
- memset(stat, 0, sizeof(*stat));
-
- /* process top & bottom edges */
- for (x = area->p0.x; x <= area->p1.x; x++) {
- if (area->p0.y == 0)
- stat->edge++;
- else if (pvt->map[x][area->p0.y - 1])
- stat->busy++;
-
- if (area->p1.y == tcm->height - 1)
- stat->edge++;
- else if (pvt->map[x][area->p1.y + 1])
- stat->busy++;
- }
+ return tcm;
- /* process left & right edges */
- for (y = area->p0.y; y <= area->p1.y; ++y) {
- if (area->p0.x == 0)
- stat->edge++;
- else if (pvt->map[area->p0.x - 1][y])
- stat->busy++;
-
- if (area->p1.x == tcm->width - 1)
- stat->edge++;
- else if (pvt->map[area->p1.x + 1][y])
- stat->busy++;
- }
+error:
+ kfree(tcm);
+ return NULL;
}