aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-omap2/include/mach/tiler.h32
-rw-r--r--drivers/media/video/tiler/tiler.c242
2 files changed, 212 insertions, 62 deletions
diff --git a/arch/arm/mach-omap2/include/mach/tiler.h b/arch/arm/mach-omap2/include/mach/tiler.h
index 6f0757b9b24..be29f3ce3e2 100644
--- a/arch/arm/mach-omap2/include/mach/tiler.h
+++ b/arch/arm/mach-omap2/include/mach/tiler.h
@@ -26,6 +26,8 @@
#define TILER_MAX_NUM_BLOCKS 16
+#include <linux/mm.h>
+
#define TILIOC_GBLK _IOWR('z', 100, struct tiler_block_info)
#define TILIOC_FBLK _IOW('z', 101, struct tiler_block_info)
#define TILIOC_GSSP _IOWR('z', 102, u32)
@@ -125,6 +127,36 @@ s32 tiler_allocx(struct tiler_block_t *blk, enum tiler_fmt fmt, u32 align,
u32 offs, u32 gid, pid_t pid);
/**
+ * Mmaps a portion of a tiler block to a virtual address. Use this method in
+ * your driver's mmap function to potentially combine multiple tiler blocks as
+ * one virtual buffer.
+ *
+ * @param blk pointer to tiler block data
+ * @param offs offset from where to map (must be page aligned)
+ * @param size size of area to map (must be page aligned)
+ * @param addr virtual address
+ *
+ * @return error status
+ */
+s32 tiler_mmap_blk(struct tiler_block_t *blk, u32 offs, u32 size,
+ struct vm_area_struct *vma, u32 voffs);
+
+/**
+ * Ioremaps a portion of a tiler block. Use this method in your driver instead
+ * of ioremap to potentially combine multiple tiler blocks as one virtual
+ * buffer.
+ *
+ * @param blk pointer to tiler block data
+ * @param offs offset from where to map (must be page aligned)
+ * @param size size of area to map (must be page aligned)
+ * @param addr virtual address
+ *
+ * @return error status
+ */
+s32 tiler_ioremap_blk(struct tiler_block_t *blk, u32 offs, u32 size, u32 addr,
+ u32 mtype);
+
+/**
* Maps an existing buffer to a 1D or 2D TILER area for the
* current process with group ID 0.
*
diff --git a/drivers/media/video/tiler/tiler.c b/drivers/media/video/tiler/tiler.c
index 0d3aa51efa8..903df67b86f 100644
--- a/drivers/media/video/tiler/tiler.c
+++ b/drivers/media/video/tiler/tiler.c
@@ -32,6 +32,7 @@
#include <linux/pagemap.h> /* page_cache_release() */
#include <linux/slab.h>
+#include <asm/mach/map.h> /* for ioremap_page */
#include <mach/tiler.h>
#include <mach/dmm.h>
#include "../dmm/tmm.h"
@@ -117,7 +118,6 @@ static s32 tiler_major;
static s32 tiler_minor;
static struct tiler_dev *tiler_device;
static struct class *tilerdev_class;
-static u32 id;
static struct mutex mtx;
static struct tcm *tcm[TILER_FORMATS];
static struct tmm *tmm[TILER_FORMATS];
@@ -170,7 +170,6 @@ static inline u32 tiler_size(const struct tiler_block_t *b)
return b->height * tiler_vstride(b);
}
-
/* get process info, and increment refs for device tracking */
static struct process_info *__get_pi(pid_t pid, bool kernel)
{
@@ -557,6 +556,56 @@ static inline bool _m_try_free(struct mem_info *mi)
return _m_chk_ref(mi);
}
+/* check if an id is used */
+static bool _m_id_in_use(u32 id)
+{
+ struct mem_info *mi;
+ list_for_each_entry(mi, &blocks, global)
+ if (mi->blk.id == id)
+ return 1;
+ return 0;
+}
+
+/* check if an offset is used */
+static bool _m_offs_in_use(u32 offs, u32 length, struct process_info *pi)
+{
+ struct __buf_info *_b;
+ list_for_each_entry(_b, &pi->bufs, by_pid)
+ if (_b->buf_info.offset < offs + length &&
+ _b->buf_info.offset + _b->buf_info.length > offs)
+ return 1;
+ return 0;
+}
+
+/* get an id */
+static u32 _m_get_id(void)
+{
+ static u32 id = 0x2d7ae;
+
+ /* ensure noone is using this id */
+ while (_m_id_in_use(id)) {
+ /* Galois LSFR: 32, 22, 2, 1 */
+ id = (id >> 1) ^ (u32)((0 - (id & 1u)) & 0x80200003u);
+ }
+
+ return id;
+}
+
+/* get an offset */
+static u32 _m_get_offs(struct process_info *pi, u32 length)
+{
+ static u32 offs = 0xda7a;
+
+ /* ensure no-one is using this offset */
+ while ((offs << PAGE_SHIFT) + length < length ||
+ _m_offs_in_use(offs << PAGE_SHIFT, length, pi)) {
+ /* Galois LSF: 20, 17 */
+ offs = (offs >> 1) ^ (u32)((0 - (offs & 1u)) & 0x90000);
+ }
+
+ return offs << PAGE_SHIFT;
+}
+
static s32 register_buf(struct __buf_info *_b, struct process_info *pi)
{
struct mem_info *mi = NULL;
@@ -570,11 +619,13 @@ static s32 register_buf(struct __buf_info *_b, struct process_info *pi)
mutex_lock(&mtx);
/* find each block */
+ b->length = 0;
list_for_each_entry(mi, &blocks, global) {
for (i = 0; i < num; i++) {
- if (!_b->mi[i] && mi->blk.phys == b->blocks[i].ssptr) {
+ if (!_b->mi[i] && mi->blk.id == b->blocks[i].id &&
+ mi->blk.key == b->blocks[i].key) {
_b->mi[i] = mi;
-
+ b->length += tiler_size(&mi->blk);
/* quit if found all*/
if (!--remain)
break;
@@ -585,8 +636,7 @@ static s32 register_buf(struct __buf_info *_b, struct process_info *pi)
/* if found all, register buffer */
if (!remain) {
- b->offset = id;
- id += 0x1000;
+ b->offset = _m_get_offs(pi, b->length);
list_add(&_b->by_pid, &pi->bufs);
@@ -821,42 +871,123 @@ static struct mem_info *__get_area(enum tiler_fmt fmt, u32 width, u32 height,
return mi;
}
+s32 tiler_mmap_blk(struct tiler_block_t *blk, u32 offs, u32 size,
+ struct vm_area_struct *vma, u32 voffs)
+{
+ u32 v, p;
+ u32 len; /* area to map */
+
+ /* don't allow mremap */
+ vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
+
+ /* mapping must fit into vma */
+ if (vma->vm_start > vma->vm_start + voffs ||
+ vma->vm_start + voffs > vma->vm_start + voffs + size ||
+ vma->vm_start + voffs + size > vma->vm_end)
+ BUG();
+
+ /* mapping must fit into block */
+ if (offs > offs + size ||
+ offs + size > tiler_size(blk))
+ BUG();
+
+ v = tiler_vstride(blk);
+ p = tiler_pstride(blk);
+
+ /* remap block portion */
+ len = v - (offs % v); /* initial area to map */
+ while (size) {
+ if (len > size)
+ len = size;
+
+ vma->vm_pgoff = (blk->phys + offs) >> PAGE_SHIFT;
+ if (remap_pfn_range(vma, vma->vm_start + voffs, vma->vm_pgoff,
+ len, vma->vm_page_prot))
+ return -EAGAIN;
+ voffs += len;
+ offs += len + p - v;
+ size -= len;
+ len = v; /* subsequent area to map */
+ }
+ return 0;
+}
+EXPORT_SYMBOL(tiler_mmap_blk);
+
+s32 tiler_ioremap_blk(struct tiler_block_t *blk, u32 offs, u32 size,
+ u32 addr, u32 mtype)
+{
+ u32 v, p;
+ u32 len; /* area to map */
+ const struct mem_type *type = get_mem_type(mtype);
+
+ /* mapping must fit into address space */
+ if (addr > addr + size)
+ BUG();
+
+ /* mapping must fit into block */
+ if (offs > offs + size ||
+ offs + size > tiler_size(blk))
+ BUG();
+
+ v = tiler_vstride(blk);
+ p = tiler_pstride(blk);
+
+ /* move offset and address to end */
+ offs += blk->phys + size;
+ addr += size;
+
+ len = v - (offs % v); /* initial area to map */
+ while (size) {
+ while (len && size) {
+ if (ioremap_page(addr - size, offs - size, type))
+ return -EAGAIN;
+ len -= PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ offs += p - v;
+ len = v; /* subsequent area to map */
+ }
+ return 0;
+}
+EXPORT_SYMBOL(tiler_ioremap_blk);
+
static s32 tiler_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct __buf_info *_b = NULL;
struct tiler_buf_info *b = NULL;
- s32 i = 0, j = 0, k = 0, m = 0, p = 0;
- struct list_head *pos = NULL;
+ u32 i, map_offs, map_size, blk_offs, blk_size, mapped_size;
struct process_info *pi = filp->private_data;
+ u32 offs = vma->vm_pgoff << PAGE_SHIFT;
+ u32 size = vma->vm_end - vma->vm_start;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- /* don't allow mremap */
- vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
-
mutex_lock(&mtx);
- list_for_each(pos, &pi->bufs) {
- _b = list_entry(pos, struct __buf_info, by_pid);
- if ((vma->vm_pgoff << PAGE_SHIFT) == _b->buf_info.offset)
+ list_for_each_entry(_b, &pi->bufs, by_pid) {
+ if (offs >= _b->buf_info.offset &&
+ offs + size <= _b->buf_info.offset + _b->buf_info.length) {
+ b = &_b->buf_info;
break;
+ }
}
mutex_unlock(&mtx);
- if (!_b)
+ if (!b)
return -ENXIO;
- b = &_b->buf_info;
-
- for (i = 0; i < b->num_blocks; i++) {
- p = tiler_vstride(&_b->mi[i]->blk);
- for (m = j = 0; j < _b->mi[i]->blk.height; j++) {
- /* map each page of the line */
- vma->vm_pgoff = (b->blocks[i].ssptr + m) >> PAGE_SHIFT;
- if (remap_pfn_range(vma, vma->vm_start + k,
- vma->vm_pgoff, p, vma->vm_page_prot))
- return -EAGAIN;
- k += p;
- m += tiler_pstride(&_b->mi[i]->blk);
- }
+ /* mmap relevant blocks */
+ blk_offs = _b->buf_info.offset;
+ mapped_size = 0;
+ for (i = 0; i < b->num_blocks; i++, blk_offs += blk_size) {
+ blk_size = tiler_size(&_b->mi[i]->blk);
+ if (offs >= blk_offs + blk_size || offs + size < blk_offs)
+ continue;
+ map_offs = max(offs, blk_offs) - blk_offs;
+ map_size = min(size - mapped_size, blk_size);
+ if (tiler_mmap_blk(&_b->mi[i]->blk, map_offs, map_size, vma,
+ mapped_size))
+ return -EAGAIN;
+ mapped_size += map_size;
}
return 0;
}
@@ -939,6 +1070,10 @@ static s32 map_block(enum tiler_fmt fmt, u32 width, u32 height,
mi->blk.width = width;
mi->blk.height = height;
+ mi->blk.key = key;
+ mutex_lock(&mtx);
+ mi->blk.id = _m_get_id();
+ mutex_unlock(&mtx);
mi->usr = usr_addr;
/* allocate pages */
@@ -1047,7 +1182,7 @@ s32 tiler_map(struct tiler_block_t *blk, enum tiler_fmt fmt, u32 usr_addr)
}
EXPORT_SYMBOL(tiler_map);
-s32 free_block(u32 sys_addr, struct process_info *pi)
+s32 free_block(u32 key, u32 id, struct process_info *pi)
{
struct gid_info *gi = NULL;
struct area_info *ai = NULL;
@@ -1058,19 +1193,11 @@ s32 free_block(u32 sys_addr, struct process_info *pi)
/* find block in process list and free it */
list_for_each_entry(gi, &pi->groups, by_pid) {
- /* currently we know if block is 1D or 2D by the address */
- if (TILER_GET_ACC_MODE(sys_addr) == TILFMT_PAGE) {
- list_for_each_entry(mi, &gi->onedim, by_area) {
- if (mi->blk.phys == sys_addr) {
- _m_try_free(mi);
- res = 0;
- goto done;
- }
- }
- } else {
+ {
list_for_each_entry(ai, &gi->areas, by_gid) {
list_for_each_entry(mi, &ai->blocks, by_area) {
- if (mi->blk.phys == sys_addr) {
+ if (mi->blk.key == key &&
+ mi->blk.id == id) {
_m_try_free(mi);
res = 0;
goto done;
@@ -1087,7 +1214,7 @@ done:
return res;
}
-s32 free_block_global(u32 ssptr)
+static s32 free_block_global(u32 key, u32 id)
{
struct mem_info *mi;
s32 res = -ENOENT;
@@ -1096,7 +1223,7 @@ s32 free_block_global(u32 ssptr)
/* find block in global list and free it */
list_for_each_entry(mi, &blocks, global) {
- if (mi->blk.phys == ssptr) {
+ if (mi->blk.key == key && mi->blk.id == id) {
_m_try_free(mi);
res = 0;
break;
@@ -1110,23 +1237,7 @@ s32 free_block_global(u32 ssptr)
s32 tiler_free(struct tiler_block_t *blk)
{
- struct mem_info *mi;
- s32 res = -ENOENT;
-
- mutex_lock(&mtx);
-
- /* find block in global list and free it */
- list_for_each_entry(mi, &blocks, global) {
- if (mi->blk.phys == blk->phys) {
- _m_try_free(mi);
- res = 0;
- break;
- }
- }
- mutex_unlock(&mtx);
-
- /* for debugging, we can set the PAT entries to DMM_LISA_MAP__0 */
- return res;
+ return free_block_global(blk->key, blk->id);
}
EXPORT_SYMBOL(tiler_free);
@@ -1163,6 +1274,8 @@ found:
blk->dim.area.height = i->blk.height;
blk->group_id = ((struct area_info *) i->parent)->gi->gid;
}
+ blk->id = i->blk.id;
+ blk->key = i->blk.key;
blk->offs = i->blk.phys & ~PAGE_MASK;
blk->align = 0;
return 0;
@@ -1218,6 +1331,7 @@ static s32 tiler_ioctl(struct inode *ip, struct file *filp, u32 cmd,
}
if (mi) {
+ block_info.id = mi->blk.id;
block_info.stride = tiler_vstride(&mi->blk);
block_info.ssptr = mi->blk.phys;
}
@@ -1232,8 +1346,8 @@ static s32 tiler_ioctl(struct inode *ip, struct file *filp, u32 cmd,
return -EFAULT;
/* search current process first, then all processes */
- free_block(block_info.ssptr, pi) ?
- free_block_global(block_info.ssptr) : 0;
+ free_block(block_info.key, block_info.id, pi) ?
+ free_block_global(block_info.key, block_info.id) : 0;
/* free always succeeds */
break;
@@ -1270,6 +1384,7 @@ static s32 tiler_ioctl(struct inode *ip, struct file *filp, u32 cmd,
return r;
if (mi) {
+ block_info.id = mi->blk.id;
block_info.stride = tiler_vstride(&mi->blk);
block_info.ssptr = mi->blk.phys;
}
@@ -1390,6 +1505,10 @@ s32 alloc_block(enum tiler_fmt fmt, u32 width, u32 height,
mi->blk.width = width;
mi->blk.height = height;
+ mi->blk.key = key;
+ mutex_lock(&mtx);
+ mi->blk.id = _m_get_id();
+ mutex_unlock(&mtx);
/* allocate and map if mapping is supported */
if (tmm_can_map(TMM(fmt))) {
@@ -1642,7 +1761,6 @@ static s32 __init tiler_init(void)
INIT_LIST_HEAD(&procs);
INIT_LIST_HEAD(&orphan_areas);
INIT_LIST_HEAD(&orphan_onedim);
- id = 0xda7a000;
error:
/* TODO: error handling for device registration */