aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDima Zavin <dima@android.com>2011-06-24 13:33:00 -0700
committerDima Zavin <dima@android.com>2011-06-24 13:33:00 -0700
commitbd47783bb233d659b668d4154737bb7857e2fb8a (patch)
tree82808bd2624944e14dcf6bc7b6d207d0e8c214db
parentc6b8854fb58cf1e5fa3dff3d30337a3e0711911e (diff)
parent2bf390d33eae05a6a8c44970852b15760d169103 (diff)
downloadkernel_samsung_tuna-bd47783bb233d659b668d4154737bb7857e2fb8a.zip
kernel_samsung_tuna-bd47783bb233d659b668d4154737bb7857e2fb8a.tar.gz
kernel_samsung_tuna-bd47783bb233d659b668d4154737bb7857e2fb8a.tar.bz2
Merge remote branch 'omap/linux-omap-mm-3.0' into linux-omap-3.0
-rw-r--r--arch/arm/mach-omap2/include/mach/tiler.h118
-rw-r--r--drivers/media/video/tiler/Kconfig33
-rw-r--r--drivers/media/video/tiler/Makefile8
-rw-r--r--drivers/media/video/tiler/_tiler.h35
-rw-r--r--drivers/media/video/tiler/tiler-iface.c541
-rw-r--r--drivers/media/video/tiler/tiler-ioctl.c525
-rw-r--r--drivers/media/video/tiler/tiler-main.c294
-rw-r--r--drivers/media/video/tiler/tiler-nv12.c417
-rw-r--r--drivers/media/video/tiler/tiler-reserve.c408
-rw-r--r--drivers/media/video/tiler/tmm-pat.c35
-rw-r--r--drivers/media/video/tiler/tmm.h22
-rw-r--r--drivers/rpmsg/Kconfig1
12 files changed, 1340 insertions, 1097 deletions
diff --git a/arch/arm/mach-omap2/include/mach/tiler.h b/arch/arm/mach-omap2/include/mach/tiler.h
index bbb4108..1bdc6bd 100644
--- a/arch/arm/mach-omap2/include/mach/tiler.h
+++ b/arch/arm/mach-omap2/include/mach/tiler.h
@@ -41,6 +41,7 @@
#define TILER_H
#include <linux/mm.h>
+#include <linux/scatterlist.h>
/*
* ----------------------------- API Definitions -----------------------------
@@ -139,13 +140,10 @@ u32 tiler_virt2phys(u32 usr);
* must be 0) with the tiler block information. 'height' must be 1
* for 1D block.
* @param fmt TILER block format
- * @param align block alignment (default: normally PAGE_SIZE)
- * @param offs block offset
*
* @return error status
*/
-s32 tiler_alloc(struct tiler_block_t *blk, enum tiler_fmt fmt, u32 align,
- u32 offs);
+s32 tiler_alloc(struct tiler_block_t *blk, enum tiler_fmt fmt);
/**
* Reserves a 1D or 2D TILER block area and memory for a set process and group
@@ -155,15 +153,13 @@ s32 tiler_alloc(struct tiler_block_t *blk, enum tiler_fmt fmt, u32 align,
* must be 0) with the tiler block information. 'height' must be 1
* for 1D block.
* @param fmt TILER block format
- * @param align block alignment (default: normally PAGE_SIZE)
- * @param offs block offset
* @param gid group ID
* @param pid process ID
*
* @return error status
*/
-s32 tiler_allocx(struct tiler_block_t *blk, enum tiler_fmt fmt, u32 align,
- u32 offs, u32 gid, pid_t pid);
+s32 tiler_allocx(struct tiler_block_t *blk, enum tiler_fmt fmt,
+ u32 gid, pid_t pid);
/**
* Mmaps a portion of a tiler block to a virtual address. Use this method in
@@ -259,11 +255,8 @@ void tiler_free(struct tiler_block_t *blk);
* @param fmt TILER format
* @param width block width
* @param height block height (must be 1 for 1D)
- * @param align block alignment (default: PAGE_SIZE)
- * @param offs block offset
*/
-void tiler_reserve(u32 n, enum tiler_fmt fmt, u32 width, u32 height, u32 align,
- u32 offs);
+void tiler_reserve(u32 n, enum tiler_fmt fmt, u32 width, u32 height);
/**
* Reserves tiler area for n identical blocks. Use this method to get optimal
@@ -274,13 +267,11 @@ void tiler_reserve(u32 n, enum tiler_fmt fmt, u32 width, u32 height, u32 align,
* @param fmt TILER bit mode
* @param width block width
* @param height block height (must be 1 for 1D)
- * @param align block alignment (default: PAGE_SIZE)
- * @param offs block offset
* @param gid group ID
* @param pid process ID
*/
void tiler_reservex(u32 n, enum tiler_fmt fmt, u32 width, u32 height,
- u32 align, u32 offs, u32 gid, pid_t pid);
+ u32 gid, pid_t pid);
/**
* Reserves tiler area for n identical NV12 blocks for the current process. Use
@@ -290,10 +281,8 @@ void tiler_reservex(u32 n, enum tiler_fmt fmt, u32 width, u32 height,
* @param n number of identical set of blocks
* @param width block width (Y)
* @param height block height (Y)
- * @param align block alignment (default: PAGE_SIZE)
- * @param offs block offset
*/
-void tiler_reserve_nv12(u32 n, u32 width, u32 height, u32 align, u32 offs);
+void tiler_reserve_nv12(u32 n, u32 width, u32 height);
/**
* Reserves tiler area for n identical NV12 blocks. Use this method to get
@@ -303,13 +292,10 @@ void tiler_reserve_nv12(u32 n, u32 width, u32 height, u32 align, u32 offs);
* @param n number of identical set of blocks
* @param width block width (Y)
* @param height block height (Y)
- * @param align block alignment (default: PAGE_SIZE)
- * @param offs block offset
* @param gid group ID
* @param pid process ID
*/
-void tiler_reservex_nv12(u32 n, u32 width, u32 height, u32 align, u32 offs,
- u32 gid, pid_t pid);
+void tiler_reservex_nv12(u32 n, u32 width, u32 height, u32 gid, pid_t pid);
/**
* Create a view based on a tiler address and width and height
@@ -393,9 +379,91 @@ struct tiler_pa_info {
typedef struct mem_info *tiler_blk_handle;
-/* NOTE: this will take ownership pa->mem (will free it) */
+/**
+ * Allocate a 1D area of container space in the Tiler
+ *
+ * @param pa ptr to tiler_pa_info structure
+ *
+ * @return handle Handle to tiler block information. NULL on error.
+ *
+ * NOTE: this will take ownership pa->mem (will free it)
+ *
+ */
tiler_blk_handle tiler_map_1d_block(struct tiler_pa_info *pa);
-void tiler_free_block(tiler_blk_handle block);
+
+/**
+ * Allocate an area of container space in the Tiler
+ *
+ * @param fmt Tiler bpp mode
+ * @param width Width in pixels
+ * @param height Height in pixels
+ * @param ssptr Value of tiler physical address of allocation
+ *
+ * @return handle Handle to tiler block information. NULL on error.
+ *
+ * NOTE: For 1D allocations, specify the full size in the width field, and
+ * specify a height of 1.
+ */
+tiler_blk_handle tiler_alloc_block_area(enum tiler_fmt fmt, u32 width,
+ u32 height, u32 *ssptr);
+
+/**
+ * Free a reserved area in the Tiler
+ *
+ * @param handle Handle to tiler block information
+ *
+ */
+void tiler_free_block_area(tiler_blk_handle block);
+
+/**
+ * Pins a set of physical pages into the Tiler using the area defined in a
+ * handle
+ *
+ * @param handle Handle to tiler block information
+ * @param sg Scatterlist of physical pages
+ * @param nents Number of entries in scatterlist
+ *
+ * @return error status.
+ */
+s32 tiler_pin_block(tiler_blk_handle handle, struct scatterlist *sg, u32 nents);
+
+/**
+ * Unpins a set of physical pages from the Tiler
+ *
+ * @param handle Handle to tiler block information
+ *
+ */
+void tiler_unpin_block(tiler_blk_handle handle);
+
+
+/**
+ * Gives Tiler physical address for a given tiler_blk_handle
+ *
+ * @param handle Handle to tiler block information
+ *
+ * @return phsyical address. NULL on error.
+ */
+u32 tiler_handle_to_phys(tiler_blk_handle handle);
+
+/**
+ * Gives memory requirements for a given container allocation
+ *
+ * @param fmt Tiler bpp mode
+ * @param width Width in pixels
+ * @param height Height in pixels
+ *
+ * @return Number of pages required. On error, returns 0
+ */
+u32 tiler_memsize(enum tiler_fmt fmt, u32 width, u32 height);
+
+/**
+ * Returns virtual stride of a tiler block
+ *
+ * @param handle Handle to tiler block allocation
+ *
+ * @return Size of virtual stride
+ */
+u32 tiler_block_vstride(tiler_blk_handle handle);
/*
* ---------------------------- IOCTL Definitions ----------------------------
@@ -431,8 +499,6 @@ struct tiler_block_info {
u32 id;
u32 key;
u32 group_id;
- u32 align; /* alignment requirements for ssptr */
- u32 offs; /* offset (ssptr & (align - 1) will equal offs) */
u32 ssptr; /* physical address, may not exposed by default */
};
diff --git a/drivers/media/video/tiler/Kconfig b/drivers/media/video/tiler/Kconfig
index 00461eb..202f7f8 100644
--- a/drivers/media/video/tiler/Kconfig
+++ b/drivers/media/video/tiler/Kconfig
@@ -31,21 +31,6 @@ config TILER_GRANULARITY
Supported values are: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
2048, 4096.
-config TILER_ALIGNMENT
- int "Allocation alignment (2^n)"
- range 1 4096
- default 4096
- depends on TI_TILER
- help
- This option sets the default TILER allocation alignment. It can
- be overriden by the tiler.align boot argument.
-
- Must be a 2^n in the range of 1 to 4096; however, it is naturally
- aligned to the TILER granularity.
-
- Supported values are: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
- 2048, 4096.
-
config TILER_CACHE_LIMIT
int "Memory limit to cache free pages in MBytes"
range 0 128
@@ -124,3 +109,21 @@ config TILER_EXPOSE_SSPTR
You can use this flag to see if the userspace is relying on
having access to the SSPtr.
+
+config TILER_ENABLE_NV12
+ bool "Enable NV12 support"
+ default y
+ depends on TI_TILER
+ help
+ This option enables NV12 functionality in the TILER driver.
+
+ If set, nv12 support will be compiled into the driver and APIs
+ will be enabled.
+
+config TILER_ENABLE_USERSPACE
+ bool "Enable userspace API"
+ default y
+ depends on TI_TILER
+ help
+ This option enabled the userspace API. If set, an ioctl interface
+ will be available to users.
diff --git a/drivers/media/video/tiler/Makefile b/drivers/media/video/tiler/Makefile
index aeb0f05..dc5fdaa 100644
--- a/drivers/media/video/tiler/Makefile
+++ b/drivers/media/video/tiler/Makefile
@@ -3,5 +3,13 @@ obj-$(CONFIG_TI_TILER) += tcm/
obj-$(CONFIG_TI_TILER) += tiler.o
tiler-objs = tiler-geom.o tiler-main.o tiler-iface.o tiler-reserve.o tmm-pat.o
+ifdef CONFIG_TILER_ENABLE_NV12
+tiler-objs += tiler-nv12.o
+endif
+
+ifdef CONFIG_TILER_ENABLE_USERSPACE
+tiler-objs += tiler-ioctl.o
+endif
+
obj-$(CONFIG_TI_TILER) += tiler_dmm.o
tiler_dmm-objs = dmm.o
diff --git a/drivers/media/video/tiler/_tiler.h b/drivers/media/video/tiler/_tiler.h
index bd35833..9da70d0 100644
--- a/drivers/media/video/tiler/_tiler.h
+++ b/drivers/media/video/tiler/_tiler.h
@@ -56,6 +56,12 @@ struct process_info {
bool kernel; /* tracking kernel objects */
};
+struct __buf_info {
+ struct list_head by_pid; /* list of buffers per pid */
+ struct tiler_buf_info buf_info;
+ struct mem_info *mi[TILER_MAX_NUM_BLOCKS]; /* blocks */
+};
+
/* per group info (within a process) */
struct gid_info {
struct list_head by_pid; /* other groups */
@@ -104,16 +110,16 @@ struct tiler_geom {
struct tiler_ops {
/* block operations */
s32 (*alloc) (enum tiler_fmt fmt, u32 width, u32 height,
- u32 align, u32 offs, u32 key,
+ u32 key,
u32 gid, struct process_info *pi,
struct mem_info **info);
- s32 (*map) (enum tiler_fmt fmt, u32 width, u32 height,
+ s32 (*pin) (enum tiler_fmt fmt, u32 width, u32 height,
u32 key, u32 gid, struct process_info *pi,
struct mem_info **info, u32 usr_addr);
- void (*reserve_nv12) (u32 n, u32 width, u32 height, u32 align, u32 offs,
+ void (*reserve_nv12) (u32 n, u32 width, u32 height,
u32 gid, struct process_info *pi);
void (*reserve) (u32 n, enum tiler_fmt fmt, u32 width, u32 height,
- u32 align, u32 offs, u32 gid, struct process_info *pi);
+ u32 gid, struct process_info *pi);
void (*unreserve) (u32 gid, struct process_info *pi);
/* block access operations */
@@ -123,10 +129,12 @@ struct tiler_ops {
void (*unlock_free) (struct mem_info *mi, bool free);
s32 (*lay_2d) (enum tiler_fmt fmt, u16 n, u16 w, u16 h, u16 band,
- u16 align, u16 offs, struct gid_info *gi,
+ u16 align, struct gid_info *gi,
struct list_head *pos);
+#ifdef CONFIG_TILER_ENABLE_NV12
s32 (*lay_nv12) (int n, u16 w, u16 w1, u16 h, struct gid_info *gi,
- u8 *p);
+ u8 *p);
+#endif
/* group operations */
struct gid_info * (*get_gi) (struct process_info *pi, u32 gid);
void (*release_gi) (struct gid_info *gi);
@@ -138,8 +146,7 @@ struct tiler_ops {
/* area operations */
s32 (*analize) (enum tiler_fmt fmt, u32 width, u32 height,
- u16 *x_area, u16 *y_area, u16 *band,
- u16 *align, u16 *offs, u16 *in_offs);
+ u16 *x_area, u16 *y_area, u16 *band, u16 *align);
/* process operations */
void (*cleanup) (void);
@@ -151,16 +158,26 @@ struct tiler_ops {
/* additional info */
const struct file_operations *fops;
-
+#ifdef CONFIG_TILER_ENABLE_NV12
bool nv12_packed; /* whether NV12 is packed into same container */
+#endif
u32 page; /* page size */
u32 width; /* container width */
u32 height; /* container height */
+
+ struct mutex mtx; /* mutex for interfaces and ioctls */
};
void tiler_iface_init(struct tiler_ops *tiler);
void tiler_geom_init(struct tiler_ops *tiler);
void tiler_reserve_init(struct tiler_ops *tiler);
+void tiler_nv12_init(struct tiler_ops *tiler);
+u32 tiler_best2pack(u16 o, u16 a, u16 b, u16 w, u16 *n, u16 *_area);
+void tiler_ioctl_init(struct tiler_ops *tiler);
+struct process_info *__get_pi(pid_t pid, bool kernel);
+void _m_unregister_buf(struct __buf_info *_b);
+s32 tiler_notify_event(int event, void *data);
+void _m_free_process_info(struct process_info *pi);
struct process_info *__get_pi(pid_t pid, bool kernel);
diff --git a/drivers/media/video/tiler/tiler-iface.c b/drivers/media/video/tiler/tiler-iface.c
index aa0a118..b7d84d5 100644
--- a/drivers/media/video/tiler/tiler-iface.c
+++ b/drivers/media/video/tiler/tiler-iface.c
@@ -30,152 +30,13 @@
#include "_tiler.h"
static bool security = CONFIG_TILER_SECURITY;
-static bool ssptr_lookup = true;
-static bool offset_lookup = true;
module_param(security, bool, 0644);
MODULE_PARM_DESC(security,
"Separate allocations by different processes into different pages");
-module_param(ssptr_lookup, bool, 0644);
-MODULE_PARM_DESC(ssptr_lookup,
- "Allow looking up a block by ssptr - This is a security risk");
-module_param(offset_lookup, bool, 0644);
-MODULE_PARM_DESC(offset_lookup,
- "Allow looking up a buffer by offset - This is a security risk");
-
-static struct mutex mtx;
+
static struct list_head procs; /* list of process info structs */
static struct tiler_ops *ops; /* shared methods and variables */
-static struct blocking_notifier_head notifier; /* notifier for events */
-
-/*
- * Event notification methods
- * ==========================================================================
- */
-
-static s32 tiler_notify_event(int event, void *data)
-{
- return blocking_notifier_call_chain(&notifier, event, data);
-}
-
-/*
- * Buffer handling methods
- * ==========================================================================
- */
-
-struct __buf_info {
- struct list_head by_pid; /* list of buffers per pid */
- struct tiler_buf_info buf_info;
- struct mem_info *mi[TILER_MAX_NUM_BLOCKS]; /* blocks */
-};
-
-/* check if an offset is used */
-static bool _m_offs_in_use(u32 offs, u32 length, struct process_info *pi)
-{
- struct __buf_info *_b;
- /* have mutex */
- 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 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)) {
- /* use a pseudo-random generator to get a new offset to try */
-
- /* Galois LSF: 20, 17 */
- offs = (offs >> 1) ^ (u32)((0 - (offs & 1u)) & 0x90000);
- }
-
- return offs << PAGE_SHIFT;
-}
-
-/* find and lock a block. process_info is optional */
-static struct mem_info *
-_m_lock_block(u32 key, u32 id, struct process_info *pi) {
- struct gid_info *gi;
- struct mem_info *mi;
-
- /* if process_info is given, look there first */
- if (pi) {
- /* have mutex */
-
- /* find block in process list and free it */
- list_for_each_entry(gi, &pi->groups, by_pid) {
- mi = ops->lock(key, id, gi);
- if (mi)
- return mi;
- }
- }
-
- /* if not found or no process_info given, find block in global list */
- return ops->lock(key, id, NULL);
-}
-
-/* register a buffer */
-static s32 _m_register_buf(struct __buf_info *_b, struct process_info *pi)
-{
- struct mem_info *mi;
- struct tiler_buf_info *b = &_b->buf_info;
- u32 i, num = b->num_blocks, offs;
-
- /* check validity */
- if (num > TILER_MAX_NUM_BLOCKS || num == 0)
- return -EINVAL;
-
- /* find each block */
- b->length = 0;
- for (i = 0; i < num; i++) {
- mi = _m_lock_block(b->blocks[i].key, b->blocks[i].id, pi);
- if (!mi) {
- /* unlock any blocks already found */
- while (i--)
- ops->unlock_free(_b->mi[i], false);
- return -EACCES;
- }
- _b->mi[i] = mi;
-
- /* we don't keep track of ptr and 1D stride so clear them */
- b->blocks[i].ptr = NULL;
- b->blocks[i].stride = 0;
-
- ops->describe(mi, b->blocks + i);
- b->length += tiler_size(&mi->blk);
- }
-
- /* if found all, register buffer */
- offs = _b->mi[0]->blk.phys & ~PAGE_MASK;
- b->offset = _m_get_offs(pi, b->length) + offs;
- b->length -= offs;
-
- /* have mutex */
- list_add(&_b->by_pid, &pi->bufs);
-
- return 0;
-}
-
-/* unregister a buffer */
-static void _m_unregister_buf(struct __buf_info *_b)
-{
- u32 i;
-
- /* unregister */
- list_del(&_b->by_pid);
-
- /* no longer using the blocks */
- for (i = 0; i < _b->buf_info.num_blocks; i++)
- ops->unlock_free(_b->mi[i], false);
-
- kfree(_b);
-}
/*
* process_info handling methods
@@ -196,7 +57,7 @@ struct process_info *__get_pi(pid_t pid, bool kernel)
pid = 0;
/* find process context */
- mutex_lock(&mtx);
+ mutex_lock(&ops->mtx);
list_for_each_entry(pi, &procs, list) {
if (pi->pid == pid && pi->kernel == kernel)
goto done;
@@ -217,7 +78,7 @@ done:
/* increment reference count */
if (pi && !kernel)
pi->refs++;
- mutex_unlock(&mtx);
+ mutex_unlock(&ops->mtx);
return pi;
}
@@ -225,21 +86,22 @@ done:
* Free all info kept by a process: all registered buffers, allocated blocks,
* and unreferenced blocks. Any blocks/areas still referenced will move to the
* orphaned lists to avoid issues if a new process is created with the same pid.
+ *
+ * caller MUST already have mtx
*/
-static void _m_free_process_info(struct process_info *pi)
+void _m_free_process_info(struct process_info *pi)
{
struct gid_info *gi, *gi_;
+#ifdef CONFIG_TILER_ENABLE_USERSPACE
struct __buf_info *_b = NULL, *_b_ = NULL;
- /* have mutex */
-
if (!list_empty(&pi->bufs))
tiler_notify_event(TILER_DEVICE_CLOSE, NULL);
/* unregister all buffers */
list_for_each_entry_safe(_b, _b_, &pi->bufs, by_pid)
_m_unregister_buf(_b);
-
+#endif
BUG_ON(!list_empty(&pi->bufs));
/* free all allocated blocks, and remove unreferenced ones */
@@ -251,366 +113,30 @@ static void _m_free_process_info(struct process_info *pi)
kfree(pi);
}
-/* Free all info kept by all processes. Called on cleanup. */
static void destroy_processes(void)
{
struct process_info *pi, *pi_;
- mutex_lock(&mtx);
+ mutex_lock(&ops->mtx);
list_for_each_entry_safe(pi, pi_, &procs, list)
_m_free_process_info(pi);
BUG_ON(!list_empty(&procs));
- mutex_unlock(&mtx);
+ mutex_unlock(&ops->mtx);
}
-/*
- * File operations (mmap, ioctl, open, close)
- * ==========================================================================
- */
-
-/* mmap tiler buffer into user's virtual space */
-static s32 tiler_mmap(struct file *filp, struct vm_area_struct *vma)
-{
- struct __buf_info *_b;
- struct tiler_buf_info *b = 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);
-
- /* find tiler buffer to mmap */
- mutex_lock(&mtx);
- list_for_each_entry(_b, &pi->bufs, by_pid) {
- /* we support partial mmaping of a whole tiler buffer */
- if (offs >= (_b->buf_info.offset & PAGE_MASK) &&
- offs + size <= PAGE_ALIGN(_b->buf_info.offset +
- _b->buf_info.length)) {
- b = &_b->buf_info;
- break;
- }
- }
- mutex_unlock(&mtx);
-
- /* we use b to detect if we found the bufffer */
- if (!b)
- return -ENXIO;
-
- /* mmap relevant blocks */
- blk_offs = _b->buf_info.offset;
-
- /* start at the beginning of the region */
- mapped_size = 0;
- for (i = 0; i < b->num_blocks; i++, blk_offs += blk_size) {
- blk_size = tiler_size(&_b->mi[i]->blk);
- /* see if tiler block is inside the requested region */
- if (offs >= blk_offs + blk_size || offs + size < blk_offs)
- continue;
- /* get the offset and map size for this particular block */
- map_offs = max(offs, blk_offs) - blk_offs;
- map_size = min(size - mapped_size, blk_size);
-
- /* mmap block */
- if (tiler_mmap_blk(&_b->mi[i]->blk, map_offs, map_size, vma,
- mapped_size))
- return -EAGAIN;
-
- /* update mmap region pointer */
- mapped_size += map_size;
- }
- return 0;
-}
-
-/* ioctl handler */
-static long tiler_ioctl(struct file *filp, u32 cmd, unsigned long arg)
-{
- s32 r;
- void __user *data = (void __user *)arg;
- struct process_info *pi = filp->private_data;
- struct __buf_info *_b;
- struct tiler_buf_info buf_info = {0};
- struct tiler_block_info block_info = {0};
- struct mem_info *mi;
-
- switch (cmd) {
- /* allocate block */
- case TILIOC_GBLK:
- if (copy_from_user(&block_info, data, sizeof(block_info)))
- return -EFAULT;
-
- switch (block_info.fmt) {
- case TILFMT_PAGE:
- r = ops->alloc(block_info.fmt, block_info.dim.len, 1,
- block_info.align, block_info.offs,
- block_info.key, block_info.group_id,
- pi, &mi);
- break;
- case TILFMT_8BIT:
- case TILFMT_16BIT:
- case TILFMT_32BIT:
- r = ops->alloc(block_info.fmt,
- block_info.dim.area.width,
- block_info.dim.area.height,
- block_info.align, block_info.offs,
- block_info.key, block_info.group_id,
- pi, &mi);
- break;
- default:
- return -EINVAL;
- }
- if (r)
- return r;
-
- /* fill out block info */
- if (mi) {
- block_info.ptr = NULL;
- ops->describe(mi, &block_info);
- }
-
- if (copy_to_user(data, &block_info, sizeof(block_info)))
- return -EFAULT;
- break;
- /* free/unmap block */
- case TILIOC_FBLK:
- case TILIOC_UMBLK:
- if (copy_from_user(&block_info, data, sizeof(block_info)))
- return -EFAULT;
-
- /* search current process first, then all processes */
- mutex_lock(&mtx);
- mi = _m_lock_block(block_info.key, block_info.id, pi);
- mutex_unlock(&mtx);
- if (mi)
- ops->unlock_free(mi, true);
-
- /* free always succeeds */
- break;
- /* get physical address */
- case TILIOC_GSSP:
- return tiler_virt2phys(arg);
- break;
- /* map block */
- case TILIOC_MBLK:
- if (copy_from_user(&block_info, data, sizeof(block_info)))
- return -EFAULT;
-
- if (!block_info.ptr)
- return -EFAULT;
-
- r = ops->map(block_info.fmt, block_info.dim.len, 1,
- block_info.key, block_info.group_id, pi,
- &mi, (u32)block_info.ptr);
- if (r)
- return r;
-
- /* fill out block info */
- if (mi)
- ops->describe(mi, &block_info);
-
- if (copy_to_user(data, &block_info, sizeof(block_info)))
- return -EFAULT;
- break;
-#ifndef CONFIG_TILER_SECURE
- /* query buffer information by offset */
- case TILIOC_QBUF:
- if (!offset_lookup)
- return -EPERM;
-
- if (copy_from_user(&buf_info, data, sizeof(buf_info)))
- return -EFAULT;
-
- /* find buffer */
- mutex_lock(&mtx);
- r = -ENOENT;
- /* buffer registration is per process */
- list_for_each_entry(_b, &pi->bufs, by_pid) {
- if (buf_info.offset == _b->buf_info.offset) {
- memcpy(&buf_info, &_b->buf_info,
- sizeof(buf_info));
- r = 0;
- break;
- }
- }
- mutex_unlock(&mtx);
-
- if (r)
- return r;
-
- if (copy_to_user(data, &_b->buf_info, sizeof(_b->buf_info)))
- return -EFAULT;
- break;
-#endif
- /* register buffer */
- case TILIOC_RBUF:
- /* save buffer information */
- _b = kmalloc(sizeof(*_b), GFP_KERNEL);
- if (!_b)
- return -ENOMEM;
- memset(_b, 0, sizeof(*_b));
-
- if (copy_from_user(&_b->buf_info, data, sizeof(_b->buf_info))) {
- kfree(_b);
- return -EFAULT;
- }
-
- mutex_lock(&mtx);
- r = _m_register_buf(_b, pi);
- mutex_unlock(&mtx);
-
- if (r) {
- kfree(_b);
- return -EACCES;
- }
-
- /* undo registration on failure */
- if (copy_to_user(data, &_b->buf_info, sizeof(_b->buf_info))) {
- mutex_lock(&mtx);
- _m_unregister_buf(_b);
- mutex_unlock(&mtx);
- return -EFAULT;
- }
- break;
- /* unregister a buffer */
- case TILIOC_URBUF:
- if (copy_from_user(&buf_info, data, sizeof(buf_info)))
- return -EFAULT;
-
- /* find buffer */
- r = -EFAULT;
- mutex_lock(&mtx);
- /* buffer registration is per process */
- list_for_each_entry(_b, &pi->bufs, by_pid) {
- if (buf_info.offset == _b->buf_info.offset) {
- _m_unregister_buf(_b);
- /* only retrieve buffer length */
- buf_info.length = _b->buf_info.length;
- r = 0;
- break;
- }
- }
- mutex_unlock(&mtx);
-
- if (r)
- return r;
-
- if (copy_to_user(data, &buf_info, sizeof(buf_info)))
- return -EFAULT;
- break;
- /* prereserv blocks */
- case TILIOC_PRBLK:
- if (copy_from_user(&block_info, data, sizeof(block_info)))
- return -EFAULT;
-
- if (block_info.fmt == TILFMT_8AND16)
- ops->reserve_nv12(block_info.key,
- block_info.dim.area.width,
- block_info.dim.area.height,
- block_info.align,
- block_info.offs,
- block_info.group_id, pi);
- else
- ops->reserve(block_info.key,
- block_info.fmt,
- block_info.dim.area.width,
- block_info.dim.area.height,
- block_info.align,
- block_info.offs,
- block_info.group_id, pi);
- break;
- /* unreserve blocks */
- case TILIOC_URBLK:
- ops->unreserve(arg, pi);
- break;
- /* query a tiler block */
- case TILIOC_QBLK:
- if (copy_from_user(&block_info, data, sizeof(block_info)))
- return -EFAULT;
-
- if (block_info.id) {
- /* look up by id if specified */
- mutex_lock(&mtx);
- mi = _m_lock_block(block_info.key, block_info.id, pi);
- mutex_unlock(&mtx);
- } else
-#ifndef CONFIG_TILER_SECURE
- if (ssptr_lookup) {
- /* otherwise, look up by ssptr if allowed */
- mi = ops->lock_by_ssptr(block_info.ssptr);
- } else
-#endif
- return -EPERM;
-
- if (!mi)
- return -EFAULT;
-
- /* we don't keep track of ptr and 1D stride so clear them */
- block_info.ptr = NULL;
- block_info.stride = 0;
-
- ops->describe(mi, &block_info);
- ops->unlock_free(mi, false);
-
- if (copy_to_user(data, &block_info, sizeof(block_info)))
- return -EFAULT;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-/* open tiler driver */
-static s32 tiler_open(struct inode *ip, struct file *filp)
-{
- struct process_info *pi = __get_pi(current->tgid, false);
- if (!pi)
- return -ENOMEM;
-
- filp->private_data = pi;
- return 0;
-}
-
-/* close tiler driver */
-static s32 tiler_release(struct inode *ip, struct file *filp)
-{
- struct process_info *pi = filp->private_data;
-
- mutex_lock(&mtx);
- /* free resources if last device in this process */
- if (0 == --pi->refs)
- _m_free_process_info(pi);
-
- mutex_unlock(&mtx);
-
- return 0;
-}
-
-/* tiler driver file operations */
-static const struct file_operations tiler_fops = {
- .open = tiler_open,
- .unlocked_ioctl = tiler_ioctl,
- .release = tiler_release,
- .mmap = tiler_mmap,
-};
/* initialize tiler interface */
void tiler_iface_init(struct tiler_ops *tiler)
{
ops = tiler;
ops->cleanup = destroy_processes;
- ops->fops = &tiler_fops;
#ifdef CONFIG_TILER_SECURE
security = true;
- offset_lookup = ssptr_lookup = false;
#endif
-
- mutex_init(&mtx);
INIT_LIST_HEAD(&procs);
- BLOCKING_INIT_NOTIFIER_HEAD(&notifier);
}
/*
@@ -639,57 +165,42 @@ u32 tiler_virt2phys(u32 usr)
}
EXPORT_SYMBOL(tiler_virt2phys);
-s32 tiler_reg_notifier(struct notifier_block *nb)
-{
- if (!nb)
- return -EINVAL;
- return blocking_notifier_chain_register(&notifier, nb);
-}
-EXPORT_SYMBOL(tiler_reg_notifier);
-
-s32 tiler_unreg_notifier(struct notifier_block *nb)
-{
- if (!nb)
- return -EINVAL;
- return blocking_notifier_chain_unregister(&notifier, nb);
-}
-EXPORT_SYMBOL(tiler_unreg_notifier);
-
void tiler_reservex(u32 n, enum tiler_fmt fmt, u32 width, u32 height,
- u32 align, u32 offs, u32 gid, pid_t pid)
+ u32 gid, pid_t pid)
{
struct process_info *pi = __get_pi(pid, true);
if (pi)
- ops->reserve(n, fmt, width, height, align, offs, gid, pi);
+ ops->reserve(n, fmt, width, height, gid, pi);
}
EXPORT_SYMBOL(tiler_reservex);
-void tiler_reserve(u32 n, enum tiler_fmt fmt, u32 width, u32 height,
- u32 align, u32 offs)
+void tiler_reserve(u32 n, enum tiler_fmt fmt, u32 width, u32 height)
{
- tiler_reservex(n, fmt, width, height, align, offs, 0, current->tgid);
+ tiler_reservex(n, fmt, width, height, 0, current->tgid);
}
EXPORT_SYMBOL(tiler_reserve);
-void tiler_reservex_nv12(u32 n, u32 width, u32 height, u32 align, u32 offs,
+#ifdef CONFIG_TILER_ENABLE_NV12
+void tiler_reservex_nv12(u32 n, u32 width, u32 height,
u32 gid, pid_t pid)
{
struct process_info *pi = __get_pi(pid, true);
if (pi)
- ops->reserve_nv12(n, width, height, align, offs, gid, pi);
+ ops->reserve_nv12(n, width, height, gid, pi);
}
EXPORT_SYMBOL(tiler_reservex_nv12);
-void tiler_reserve_nv12(u32 n, u32 width, u32 height, u32 align, u32 offs)
+void tiler_reserve_nv12(u32 n, u32 width, u32 height)
{
- tiler_reservex_nv12(n, width, height, align, offs, 0, current->tgid);
+ tiler_reservex_nv12(n, width, height, 0, current->tgid);
}
EXPORT_SYMBOL(tiler_reserve_nv12);
+#endif
s32 tiler_allocx(struct tiler_block_t *blk, enum tiler_fmt fmt,
- u32 align, u32 offs, u32 gid, pid_t pid)
+ u32 gid, pid_t pid)
{
struct mem_info *mi;
struct process_info *pi;
@@ -701,8 +212,7 @@ s32 tiler_allocx(struct tiler_block_t *blk, enum tiler_fmt fmt,
if (!pi)
return -ENOMEM;
- res = ops->alloc(fmt, blk->width, blk->height, align, offs, blk->key,
- gid, pi, &mi);
+ res = ops->alloc(fmt, blk->width, blk->height, blk->key, gid, pi, &mi);
if (mi) {
blk->phys = mi->blk.phys;
blk->id = mi->blk.id;
@@ -711,10 +221,9 @@ s32 tiler_allocx(struct tiler_block_t *blk, enum tiler_fmt fmt,
}
EXPORT_SYMBOL(tiler_allocx);
-s32 tiler_alloc(struct tiler_block_t *blk, enum tiler_fmt fmt,
- u32 align, u32 offs)
+s32 tiler_alloc(struct tiler_block_t *blk, enum tiler_fmt fmt)
{
- return tiler_allocx(blk, fmt, align, offs, 0, current->tgid);
+ return tiler_allocx(blk, fmt, 0, current->tgid);
}
EXPORT_SYMBOL(tiler_alloc);
@@ -731,7 +240,7 @@ s32 tiler_mapx(struct tiler_block_t *blk, enum tiler_fmt fmt, u32 gid,
if (!pi)
return -ENOMEM;
- res = ops->map(fmt, blk->width, blk->height, blk->key, gid, pi, &mi,
+ res = ops->pin(fmt, blk->width, blk->height, blk->key, gid, pi, &mi,
usr_addr);
if (mi) {
blk->phys = mi->blk.phys;
diff --git a/drivers/media/video/tiler/tiler-ioctl.c b/drivers/media/video/tiler/tiler-ioctl.c
new file mode 100644
index 0000000..3cb0abf
--- /dev/null
+++ b/drivers/media/video/tiler/tiler-ioctl.c
@@ -0,0 +1,525 @@
+/*
+ * tiler-ioctl.c
+ *
+ * TILER driver userspace interface functions for TI TILER hardware block.
+ *
+ * Authors: Lajos Molnar <molnar@ti.com>
+ * David Sin <davidsin@ti.com>
+ *
+ * Copyright (C) 2009-2010 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
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/fs.h> /* fops */
+#include <linux/uaccess.h> /* copy_to_user */
+#include <linux/slab.h> /* kmalloc */
+#include <linux/sched.h> /* current */
+#include <linux/mm.h>
+#include <linux/mm_types.h>
+#include <asm/mach/map.h> /* for ioremap_page */
+
+#include "_tiler.h"
+
+static bool ssptr_lookup = true;
+static bool offset_lookup = true;
+
+module_param(ssptr_lookup, bool, 0644);
+MODULE_PARM_DESC(ssptr_lookup,
+ "Allow looking up a block by ssptr - This is a security risk");
+module_param(offset_lookup, bool, 0644);
+MODULE_PARM_DESC(offset_lookup,
+ "Allow looking up a buffer by offset - This is a security risk");
+
+static struct tiler_ops *ops; /* shared methods and variables */
+static struct blocking_notifier_head notifier; /* notifier for events */
+
+/*
+ * Event notification methods
+ * ==========================================================================
+ */
+
+s32 tiler_notify_event(int event, void *data)
+{
+ return blocking_notifier_call_chain(&notifier, event, data);
+}
+
+/*
+ * Buffer handling methods
+ * ==========================================================================
+ */
+
+/* check if an offset is used */
+static bool _m_offs_in_use(u32 offs, u32 length, struct process_info *pi)
+{
+ struct __buf_info *_b;
+ /* have mutex */
+ 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 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)) {
+ /* use a pseudo-random generator to get a new offset to try */
+
+ /* Galois LSF: 20, 17 */
+ offs = (offs >> 1) ^ (u32)((0 - (offs & 1u)) & 0x90000);
+ }
+
+ return offs << PAGE_SHIFT;
+}
+
+/* find and lock a block. process_info is optional */
+static struct mem_info *
+_m_lock_block(u32 key, u32 id, struct process_info *pi) {
+ struct gid_info *gi;
+ struct mem_info *mi;
+
+ /* if process_info is given, look there first */
+ if (pi) {
+ /* have mutex */
+
+ /* find block in process list and free it */
+ list_for_each_entry(gi, &pi->groups, by_pid) {
+ mi = ops->lock(key, id, gi);
+ if (mi)
+ return mi;
+ }
+ }
+
+ /* if not found or no process_info given, find block in global list */
+ return ops->lock(key, id, NULL);
+}
+
+/* register a buffer */
+static s32 _m_register_buf(struct __buf_info *_b, struct process_info *pi)
+{
+ struct mem_info *mi;
+ struct tiler_buf_info *b = &_b->buf_info;
+ u32 i, num = b->num_blocks, offs;
+
+ /* check validity */
+ if (num > TILER_MAX_NUM_BLOCKS || num == 0)
+ return -EINVAL;
+
+ /* find each block */
+ b->length = 0;
+ for (i = 0; i < num; i++) {
+ mi = _m_lock_block(b->blocks[i].key, b->blocks[i].id, pi);
+ if (!mi) {
+ /* unlock any blocks already found */
+ while (i--)
+ ops->unlock_free(_b->mi[i], false);
+ return -EACCES;
+ }
+ _b->mi[i] = mi;
+
+ /* we don't keep track of ptr and 1D stride so clear them */
+ b->blocks[i].ptr = NULL;
+ b->blocks[i].stride = 0;
+
+ ops->describe(mi, b->blocks + i);
+ b->length += tiler_size(&mi->blk);
+ }
+
+ /* if found all, register buffer */
+ offs = _b->mi[0]->blk.phys & ~PAGE_MASK;
+ b->offset = _m_get_offs(pi, b->length) + offs;
+ b->length -= offs;
+
+ /* have mutex */
+ list_add(&_b->by_pid, &pi->bufs);
+
+ return 0;
+}
+
+/* unregister a buffer */
+void _m_unregister_buf(struct __buf_info *_b)
+{
+ u32 i;
+
+ /* unregister */
+ list_del(&_b->by_pid);
+
+ /* no longer using the blocks */
+ for (i = 0; i < _b->buf_info.num_blocks; i++)
+ ops->unlock_free(_b->mi[i], false);
+
+ kfree(_b);
+}
+
+
+/*
+ * File operations (mmap, ioctl, open, close)
+ * ==========================================================================
+ */
+
+/* mmap tiler buffer into user's virtual space */
+static s32 tiler_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct __buf_info *_b;
+ struct tiler_buf_info *b = 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);
+
+ /* find tiler buffer to mmap */
+ mutex_lock(&ops->mtx);
+ list_for_each_entry(_b, &pi->bufs, by_pid) {
+ /* we support partial mmaping of a whole tiler buffer */
+ if (offs >= (_b->buf_info.offset & PAGE_MASK) &&
+ offs + size <= PAGE_ALIGN(_b->buf_info.offset +
+ _b->buf_info.length)) {
+ b = &_b->buf_info;
+ break;
+ }
+ }
+ mutex_unlock(&ops->mtx);
+
+ /* we use b to detect if we found the bufffer */
+ if (!b)
+ return -ENXIO;
+
+ /* mmap relevant blocks */
+ blk_offs = _b->buf_info.offset;
+
+ /* start at the beginning of the region */
+ mapped_size = 0;
+ for (i = 0; i < b->num_blocks; i++, blk_offs += blk_size) {
+ blk_size = tiler_size(&_b->mi[i]->blk);
+ /* see if tiler block is inside the requested region */
+ if (offs >= blk_offs + blk_size || offs + size < blk_offs)
+ continue;
+ /* get the offset and map size for this particular block */
+ map_offs = max(offs, blk_offs) - blk_offs;
+ map_size = min(size - mapped_size, blk_size);
+
+ /* mmap block */
+ if (tiler_mmap_blk(&_b->mi[i]->blk, map_offs, map_size, vma,
+ mapped_size))
+ return -EAGAIN;
+
+ /* update mmap region pointer */
+ mapped_size += map_size;
+ }
+ return 0;
+}
+
+/* ioctl handler */
+static long tiler_ioctl(struct file *filp, u32 cmd, unsigned long arg)
+{
+ s32 r;
+ void __user *data = (void __user *)arg;
+ struct process_info *pi = filp->private_data;
+ struct __buf_info *_b;
+ struct tiler_buf_info buf_info = {0};
+ struct tiler_block_info block_info = {0};
+ struct mem_info *mi;
+
+ switch (cmd) {
+ /* allocate block */
+ case TILIOC_GBLK:
+ if (copy_from_user(&block_info, data, sizeof(block_info)))
+ return -EFAULT;
+
+ switch (block_info.fmt) {
+ case TILFMT_PAGE:
+ r = ops->alloc(block_info.fmt, block_info.dim.len, 1,
+ block_info.key, block_info.group_id,
+ pi, &mi);
+ break;
+ case TILFMT_8BIT:
+ case TILFMT_16BIT:
+ case TILFMT_32BIT:
+ r = ops->alloc(block_info.fmt,
+ block_info.dim.area.width,
+ block_info.dim.area.height,
+ block_info.key, block_info.group_id,
+ pi, &mi);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (r)
+ return r;
+
+ /* fill out block info */
+ if (mi) {
+ block_info.ptr = NULL;
+ ops->describe(mi, &block_info);
+ }
+
+ if (copy_to_user(data, &block_info, sizeof(block_info)))
+ return -EFAULT;
+ break;
+ /* free/unmap block */
+ case TILIOC_FBLK:
+ case TILIOC_UMBLK:
+ if (copy_from_user(&block_info, data, sizeof(block_info)))
+ return -EFAULT;
+
+ /* search current process first, then all processes */
+ mutex_lock(&ops->mtx);
+ mi = _m_lock_block(block_info.key, block_info.id, pi);
+ mutex_unlock(&ops->mtx);
+ if (mi)
+ ops->unlock_free(mi, true);
+
+ /* free always succeeds */
+ break;
+ /* get physical address */
+ case TILIOC_GSSP:
+ return tiler_virt2phys(arg);
+ break;
+ /* map block */
+ case TILIOC_MBLK:
+ if (copy_from_user(&block_info, data, sizeof(block_info)))
+ return -EFAULT;
+
+ if (!block_info.ptr)
+ return -EFAULT;
+
+ r = ops->pin(block_info.fmt, block_info.dim.len, 1,
+ block_info.key, block_info.group_id, pi,
+ &mi, (u32)block_info.ptr);
+ if (r)
+ return r;
+
+ /* fill out block info */
+ if (mi)
+ ops->describe(mi, &block_info);
+
+ if (copy_to_user(data, &block_info, sizeof(block_info)))
+ return -EFAULT;
+ break;
+#ifndef CONFIG_TILER_SECURE
+ /* query buffer information by offset */
+ case TILIOC_QBUF:
+ if (!offset_lookup)
+ return -EPERM;
+
+ if (copy_from_user(&buf_info, data, sizeof(buf_info)))
+ return -EFAULT;
+
+ /* find buffer */
+ mutex_lock(&ops->mtx);
+ r = -ENOENT;
+ /* buffer registration is per process */
+ list_for_each_entry(_b, &pi->bufs, by_pid) {
+ if (buf_info.offset == _b->buf_info.offset) {
+ memcpy(&buf_info, &_b->buf_info,
+ sizeof(buf_info));
+ r = 0;
+ break;
+ }
+ }
+ mutex_unlock(&ops->mtx);
+
+ if (r)
+ return r;
+
+ if (copy_to_user(data, &_b->buf_info, sizeof(_b->buf_info)))
+ return -EFAULT;
+ break;
+#endif
+ /* register buffer */
+ case TILIOC_RBUF:
+ /* save buffer information */
+ _b = kmalloc(sizeof(*_b), GFP_KERNEL);
+ if (!_b)
+ return -ENOMEM;
+ memset(_b, 0, sizeof(*_b));
+
+ if (copy_from_user(&_b->buf_info, data, sizeof(_b->buf_info))) {
+ kfree(_b);
+ return -EFAULT;
+ }
+
+ mutex_lock(&ops->mtx);
+ r = _m_register_buf(_b, pi);
+ mutex_unlock(&ops->mtx);
+
+ if (r) {
+ kfree(_b);
+ return -EACCES;
+ }
+
+ /* undo registration on failure */
+ if (copy_to_user(data, &_b->buf_info, sizeof(_b->buf_info))) {
+ mutex_lock(&ops->mtx);
+ _m_unregister_buf(_b);
+ mutex_unlock(&ops->mtx);
+ return -EFAULT;
+ }
+ break;
+ /* unregister a buffer */
+ case TILIOC_URBUF:
+ if (copy_from_user(&buf_info, data, sizeof(buf_info)))
+ return -EFAULT;
+
+ /* find buffer */
+ r = -EFAULT;
+ mutex_lock(&ops->mtx);
+ /* buffer registration is per process */
+ list_for_each_entry(_b, &pi->bufs, by_pid) {
+ if (buf_info.offset == _b->buf_info.offset) {
+ _m_unregister_buf(_b);
+ /* only retrieve buffer length */
+ buf_info.length = _b->buf_info.length;
+ r = 0;
+ break;
+ }
+ }
+ mutex_unlock(&ops->mtx);
+
+ if (r)
+ return r;
+
+ if (copy_to_user(data, &buf_info, sizeof(buf_info)))
+ return -EFAULT;
+ break;
+ /* prereserv blocks */
+ case TILIOC_PRBLK:
+ if (copy_from_user(&block_info, data, sizeof(block_info)))
+ return -EFAULT;
+
+ if (block_info.fmt == TILFMT_8AND16)
+#ifdef CONFIG_TILER_ENABLE_NV12
+ ops->reserve_nv12(block_info.key,
+ block_info.dim.area.width,
+ block_info.dim.area.height,
+ block_info.group_id, pi);
+#else
+ return -EINVAL;
+#endif
+ else
+ ops->reserve(block_info.key,
+ block_info.fmt,
+ block_info.dim.area.width,
+ block_info.dim.area.height,
+ block_info.group_id, pi);
+ break;
+ /* unreserve blocks */
+ case TILIOC_URBLK:
+ ops->unreserve(arg, pi);
+ break;
+ /* query a tiler block */
+ case TILIOC_QBLK:
+ if (copy_from_user(&block_info, data, sizeof(block_info)))
+ return -EFAULT;
+
+ if (block_info.id) {
+ /* look up by id if specified */
+ mutex_lock(&ops->mtx);
+ mi = _m_lock_block(block_info.key, block_info.id, pi);
+ mutex_unlock(&ops->mtx);
+ } else
+#ifndef CONFIG_TILER_SECURE
+ if (ssptr_lookup) {
+ /* otherwise, look up by ssptr if allowed */
+ mi = ops->lock_by_ssptr(block_info.ssptr);
+ } else
+#endif
+ return -EPERM;
+
+ if (!mi)
+ return -EFAULT;
+
+ /* we don't keep track of ptr and 1D stride so clear them */
+ block_info.ptr = NULL;
+ block_info.stride = 0;
+
+ ops->describe(mi, &block_info);
+ ops->unlock_free(mi, false);
+
+ if (copy_to_user(data, &block_info, sizeof(block_info)))
+ return -EFAULT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* open tiler driver */
+static s32 tiler_open(struct inode *ip, struct file *filp)
+{
+ struct process_info *pi = __get_pi(current->tgid, false);
+ if (!pi)
+ return -ENOMEM;
+
+ filp->private_data = pi;
+ return 0;
+}
+
+/* close tiler driver */
+static s32 tiler_release(struct inode *ip, struct file *filp)
+{
+ struct process_info *pi = filp->private_data;
+
+ mutex_lock(&ops->mtx);
+ /* free resources if last device in this process */
+ if (0 == --pi->refs)
+ _m_free_process_info(pi);
+
+ mutex_unlock(&ops->mtx);
+
+ return 0;
+}
+
+/* tiler driver file operations */
+static const struct file_operations tiler_fops = {
+ .open = tiler_open,
+ .unlocked_ioctl = tiler_ioctl,
+ .release = tiler_release,
+ .mmap = tiler_mmap,
+};
+
+
+void tiler_ioctl_init(struct tiler_ops *tiler)
+{
+ ops = tiler;
+ ops->fops = &tiler_fops;
+
+#ifdef CONFIG_TILER_SECURE
+ offset_lookup = ssptr_lookup = false;
+#endif
+ BLOCKING_INIT_NOTIFIER_HEAD(&notifier);
+}
+
+
+s32 tiler_reg_notifier(struct notifier_block *nb)
+{
+ if (!nb)
+ return -EINVAL;
+ return blocking_notifier_chain_register(&notifier, nb);
+}
+EXPORT_SYMBOL(tiler_reg_notifier);
+
+s32 tiler_unreg_notifier(struct notifier_block *nb)
+{
+ if (!nb)
+ return -EINVAL;
+ return blocking_notifier_chain_unregister(&notifier, nb);
+}
+EXPORT_SYMBOL(tiler_unreg_notifier);
diff --git a/drivers/media/video/tiler/tiler-main.c b/drivers/media/video/tiler/tiler-main.c
index b863cc2..d1d3c6e 100644
--- a/drivers/media/video/tiler/tiler-main.c
+++ b/drivers/media/video/tiler/tiler-main.c
@@ -38,7 +38,6 @@
#include "tcm/tcm-sita.h" /* TCM algorithm */
static bool ssptr_id = CONFIG_TILER_SSPTR_ID;
-static uint default_align = CONFIG_TILER_ALIGNMENT;
static uint granularity = CONFIG_TILER_GRANULARITY;
/*
@@ -48,8 +47,6 @@ static uint granularity = CONFIG_TILER_GRANULARITY;
*/
module_param(ssptr_id, bool, 0444);
MODULE_PARM_DESC(ssptr_id, "Use ssptr as block ID");
-module_param_named(align, default_align, uint, 0644);
-MODULE_PARM_DESC(align, "Default block ssptr alignment");
module_param_named(grain, granularity, uint, 0644);
MODULE_PARM_DESC(grain, "Granularity (bytes)");
@@ -87,8 +84,8 @@ static dma_addr_t dmac_pa;
* TMM connectors
* ==========================================================================
*/
-/* wrapper around tmm_map */
-static s32 refill_pat(struct tmm *tmm, struct tcm_area *area, u32 *ptr)
+/* wrapper around tmm_pin */
+static s32 pin_mem_to_area(struct tmm *tmm, struct tcm_area *area, u32 *ptr)
{
s32 res = 0;
struct pat_area p_area = {0};
@@ -106,7 +103,8 @@ static s32 refill_pat(struct tmm *tmm, struct tcm_area *area, u32 *ptr)
memcpy(dmac_va, ptr, sizeof(*ptr) * tcm_sizeof(slice));
ptr += tcm_sizeof(slice);
- if (tmm_map(tmm, p_area, dmac_pa)) {
+ /* pin memory into DMM */
+ if (tmm_pin(tmm, p_area, dmac_pa)) {
res = -EFAULT;
break;
}
@@ -115,8 +113,8 @@ static s32 refill_pat(struct tmm *tmm, struct tcm_area *area, u32 *ptr)
return res;
}
-/* wrapper around tmm_clear */
-static void clear_pat(struct tmm *tmm, struct tcm_area *area)
+/* wrapper around tmm_unpin */
+static void unpin_mem_from_area(struct tmm *tmm, struct tcm_area *area)
{
struct pat_area p_area = {0};
struct tcm_area slice, area_s;
@@ -127,7 +125,7 @@ static void clear_pat(struct tmm *tmm, struct tcm_area *area)
p_area.x1 = slice.p1.x;
p_area.y1 = slice.p1.y;
- tmm_clear(tmm, p_area);
+ tmm_unpin(tmm, p_area);
}
}
@@ -282,29 +280,23 @@ static inline void _m_area_free(struct area_info *ai)
static s32 __analize_area(enum tiler_fmt fmt, u32 width, u32 height,
u16 *x_area, u16 *y_area, u16 *band,
- u16 *align, u16 *offs, u16 *in_offs)
+ u16 *align)
{
- /* input: width, height is in pixels, align, offs in bytes */
- /* output: x_area, y_area, band, align, offs in slots */
+ /* input: width, height is in pixels */
+ /* output: x_area, y_area, band, align */
/* slot width, height, and row size */
u32 slot_row, min_align;
const struct tiler_geom *g;
+ /* set alignment to page size */
+ *align = PAGE_SIZE;
+
/* width and height must be positive */
if (!width || !height)
return -EINVAL;
- /* align must be 2 power */
- if (*align & (*align - 1))
- return -EINVAL;
-
if (fmt == TILFMT_PAGE) {
- /* adjust size to accomodate offset, only do page alignment */
- *align = PAGE_SIZE;
- *in_offs = *offs & ~PAGE_MASK;
- width += *in_offs;
-
/* for 1D area keep the height (1), width is in tiler slots */
*x_area = DIV_ROUND_UP(width, tiler.page);
*y_area = *band = 1;
@@ -325,33 +317,14 @@ static s32 __analize_area(enum tiler_fmt fmt, u32 width, u32 height,
/* how many slots are can be accessed via one physical page */
*band = PAGE_SIZE / slot_row;
- /* minimum alignment is at least 1 slot. Use default if needed */
+ /* minimum alignment is at least 1 slot */
min_align = max(slot_row, granularity);
- *align = ALIGN(*align ? : default_align, min_align);
-
- /* align must still be 2 power (in case default_align is wrong) */
- if (*align & (*align - 1))
- return -EAGAIN;
-
- /* offset must be multiple of bpp */
- if (*offs & (g->bpp - 1) || *offs >= *align)
- return -EINVAL;
-
- /* round down the offset to the nearest slot size, and increase width
- to allow space for having the correct offset */
- width += (*offs & (min_align - 1)) / g->bpp;
- if (in_offs)
- *in_offs = *offs & (min_align - 1);
- *offs &= ~(min_align - 1);
-
- /* expand width to block size */
- width = ALIGN(width, min_align / g->bpp);
+ *align = ALIGN(*align, min_align);
/* adjust to slots */
*x_area = DIV_ROUND_UP(width, g->slot_w);
*y_area = DIV_ROUND_UP(height, g->slot_h);
*align /= slot_row;
- *offs /= slot_row;
if (*x_area > tiler.width || *y_area > tiler.height)
return -ENOMEM;
@@ -366,7 +339,6 @@ static s32 __analize_area(enum tiler_fmt fmt, u32 width, u32 height,
*
* @param w Width of the block.
* @param align Alignment of the block.
- * @param offs Offset of the block (within alignment)
* @param ai Pointer to area info
* @param next Pointer to the variable where the next block
* will be stored. The block should be inserted
@@ -377,10 +349,10 @@ static s32 __analize_area(enum tiler_fmt fmt, u32 width, u32 height,
*
* (must have mutex)
*/
-static u16 _m_blk_find_fit(u16 w, u16 align, u16 offs,
+static u16 _m_blk_find_fit(u16 w, u16 align,
struct area_info *ai, struct list_head **before)
{
- int x = ai->area.p0.x + w + offs;
+ int x = ai->area.p0.x + w;
struct mem_info *mi;
/* area blocks are sorted by x */
@@ -390,7 +362,7 @@ static u16 _m_blk_find_fit(u16 w, u16 align, u16 offs,
*before = &mi->by_area;
return x;
}
- x = ALIGN(mi->area.p1.x + 1 - offs, align) + w + offs;
+ x = ALIGN(mi->area.p1.x + 1, align) + w;
}
*before = &ai->blocks;
@@ -412,7 +384,7 @@ struct mem_info *_m_add2area(struct mem_info *mi, struct area_info *ai,
return mi;
}
-static struct mem_info *get_2d_area(u16 w, u16 h, u16 align, u16 offs, u16 band,
+static struct mem_info *get_2d_area(u16 w, u16 h, u16 align, u16 band,
struct gid_info *gi, struct tcm *tcm)
{
struct area_info *ai = NULL;
@@ -428,7 +400,7 @@ static struct mem_info *get_2d_area(u16 w, u16 h, u16 align, u16 offs, u16 band,
if (mi->area.tcm == tcm &&
tcm_aheight(mi->area) == h &&
tcm_awidth(mi->area) == w &&
- (mi->area.p0.x & (align - 1)) == offs) {
+ (mi->area.p0.x & (align - 1)) == 0) {
/* this area is already set up */
/* remove from reserved list */
@@ -450,7 +422,7 @@ static struct mem_info *get_2d_area(u16 w, u16 h, u16 align, u16 offs, u16 band,
list_for_each_entry(ai, &gi->areas, by_gid) {
if (ai->area.tcm == tcm &&
tcm_aheight(ai->area) == h) {
- x = _m_blk_find_fit(w, align, offs, ai, &before);
+ x = _m_blk_find_fit(w, align, ai, &before);
if (x) {
_m_add2area(mi, ai, x - w, w, before);
goto done;
@@ -460,10 +432,10 @@ static struct mem_info *get_2d_area(u16 w, u16 h, u16 align, u16 offs, u16 band,
mutex_unlock(&mtx);
/* if no area fit, reserve a new one */
- ai = area_new_m(ALIGN(w + offs, max(band, align)), h,
+ ai = area_new_m(ALIGN(w, max(band, align)), h,
max(band, align), tcm, gi);
if (ai) {
- _m_add2area(mi, ai, ai->area.p0.x + offs, w, &ai->blocks);
+ _m_add2area(mi, ai, ai->area.p0.x, w, &ai->blocks);
} else {
/* clean up */
kfree(mi);
@@ -476,9 +448,9 @@ done:
}
/* layout reserved 2d blocks in a larger area */
-/* NOTE: band, w, h, a(lign), o(ffs) is in slots */
+/* NOTE: band, w, h, a(lign) is in slots */
static s32 lay_2d(enum tiler_fmt fmt, u16 n, u16 w, u16 h, u16 band,
- u16 align, u16 offs, struct gid_info *gi,
+ u16 align, struct gid_info *gi,
struct list_head *pos)
{
u16 x, x0, e = ALIGN(w, align), w_res = (n - 1) * e + w;
@@ -488,15 +460,15 @@ static s32 lay_2d(enum tiler_fmt fmt, u16 n, u16 w, u16 h, u16 band,
printk(KERN_INFO "packing %u %u buffers into %u width\n",
n, w, w_res);
- /* calculate dimensions, band, offs and alignment in slots */
+ /* calculate dimensions, band, and alignment in slots */
/* reserve an area */
- ai = area_new_m(ALIGN(w_res + offs, max(band, align)), h,
+ ai = area_new_m(ALIGN(w_res, max(band, align)), h,
max(band, align), tcm[fmt], gi);
if (!ai)
return -ENOMEM;
/* lay out blocks in the reserved area */
- for (n = 0, x = offs; x < w_res; x += e, n++) {
+ for (n = 0, x = 0; x < w_res; x += e, n++) {
/* reserve a block struct */
mi = kmalloc(sizeof(*mi), GFP_KERNEL);
if (!mi)
@@ -512,6 +484,7 @@ static s32 lay_2d(enum tiler_fmt fmt, u16 n, u16 w, u16 h, u16 band,
return n;
}
+#ifdef CONFIG_TILER_ENABLE_NV12
/* layout reserved nv12 blocks in a larger area */
/* NOTE: area w(idth), w1 (8-bit block width), h(eight) are in slots */
/* p is a pointer to a packing description, which is a list of offsets in
@@ -557,6 +530,7 @@ static s32 lay_nv12(int n, u16 w, u16 w1, u16 h, struct gid_info *gi, u8 *p)
mutex_unlock(&mtx);
return n;
}
+#endif
static void _m_unpin(struct mem_info *mi)
{
@@ -582,7 +556,7 @@ static void _m_unpin(struct mem_info *mi)
kfree(mi->pa.mem);
mi->pa.mem = NULL;
mi->pa.num_pg = 0;
- clear_pat(tmm[tiler_fmt(mi->blk.phys)], &mi->area);
+ unpin_mem_from_area(tmm[tiler_fmt(mi->blk.phys)], &mi->area);
}
/* (must have mutex) free block and any freed areas */
@@ -857,24 +831,21 @@ static void fill_block_info(struct mem_info *i, struct tiler_block_info *blk)
}
blk->id = i->blk.id;
blk->key = i->blk.key;
- blk->offs = i->blk.phys & ~PAGE_MASK;
- blk->align = PAGE_SIZE;
}
/*
* Block operations
* ==========================================================================
*/
-static struct mem_info *__get_area(enum tiler_fmt fmt, u32 width, u32 height,
- u16 align, u16 offs, struct gid_info *gi)
+static struct mem_info *alloc_area(enum tiler_fmt fmt, u32 width, u32 height,
+ struct gid_info *gi)
{
- u16 x, y, band, in_offs = 0;
+ u16 x, y, band, align;
struct mem_info *mi = NULL;
const struct tiler_geom *g = tiler.geom(fmt);
- /* calculate dimensions, band, offs and alignment in slots */
- if (__analize_area(fmt, width, height, &x, &y, &band, &align, &offs,
- &in_offs))
+ /* calculate dimensions, band, and alignment in slots */
+ if (__analize_area(fmt, width, height, &x, &y, &band, &align))
return NULL;
if (fmt == TILFMT_PAGE) {
@@ -893,7 +864,7 @@ static struct mem_info *__get_area(enum tiler_fmt fmt, u32 width, u32 height,
mi->parent = gi;
list_add(&mi->by_area, &gi->onedim);
} else {
- mi = get_2d_area(x, y, align, offs, band, gi, tcm[fmt]);
+ mi = get_2d_area(x, y, align, band, gi, tcm[fmt]);
if (!mi)
return NULL;
@@ -907,20 +878,19 @@ static struct mem_info *__get_area(enum tiler_fmt fmt, u32 width, u32 height,
mutex_unlock(&mtx);
mi->blk.phys = tiler.addr(fmt,
- mi->area.p0.x * g->slot_w, mi->area.p0.y * g->slot_h)
- + in_offs;
+ mi->area.p0.x * g->slot_w, mi->area.p0.y * g->slot_h);
return mi;
}
static struct mem_info *alloc_block_area(enum tiler_fmt fmt, u32 width,
- u32 height, u32 align, u32 offs, u32 key, u32 gid,
+ u32 height, u32 key, u32 gid,
struct process_info *pi)
{
struct mem_info *mi = NULL;
struct gid_info *gi = NULL;
- /* only support up to page alignment */
- if (align > PAGE_SIZE || offs >= (align ? : default_align) || !pi)
+ /* validate parameters */
+ if (!pi)
return ERR_PTR(-EINVAL);
/* get group context */
@@ -932,7 +902,7 @@ static struct mem_info *alloc_block_area(enum tiler_fmt fmt, u32 width,
return ERR_PTR(-ENOMEM);
/* reserve area in tiler container */
- mi = __get_area(fmt, width, height, align, offs, gi);
+ mi = alloc_area(fmt, width, height, gi);
if (!mi) {
mutex_lock(&mtx);
gi->refs--;
@@ -961,7 +931,7 @@ static s32 pin_memory(struct mem_info *mi, struct tiler_pa_info *pa)
struct tcm_area area = mi->area;
/* ensure we can pin */
- if (!tmm_can_map(tmm[fmt]))
+ if (!tmm_can_pin(tmm[fmt]))
return -EINVAL;
/* ensure pages fit into area */
@@ -981,7 +951,7 @@ static s32 pin_memory(struct mem_info *mi, struct tiler_pa_info *pa)
if (fmt == TILFMT_PAGE)
tcm_1d_limit(&area, pa->num_pg);
if (mi->pa.num_pg)
- return refill_pat(tmm[fmt], &area, mi->pa.mem);
+ return pin_mem_to_area(tmm[fmt], &area, mi->pa.mem);
return 0;
}
@@ -1012,34 +982,33 @@ static struct tiler_pa_info *get_new_pa(struct tmm *tmm, u32 num_pg)
}
static s32 alloc_block(enum tiler_fmt fmt, u32 width, u32 height,
- u32 align, u32 offs, u32 key, u32 gid, struct process_info *pi,
+ u32 key, u32 gid, struct process_info *pi,
struct mem_info **info)
{
struct mem_info *mi;
struct tiler_pa_info *pa = NULL;
- s32 res;
+ int res;
*info = NULL;
/* allocate tiler container area */
- mi = alloc_block_area(fmt, width, height, align, offs, key, gid, pi);
+ mi = alloc_block_area(fmt, width, height, key, gid, pi);
if (IS_ERR_OR_NULL(mi))
return mi ? -ENOMEM : PTR_ERR(mi);
- /* allocate and map if mapping is supported */
- if (tmm_can_map(tmm[fmt])) {
- /* allocate back memory */
- pa = get_new_pa(tmm[fmt], tcm_sizeof(mi->area));
- if (!pa)
- goto cleanup;
-
- /* pin memory */
- res = pin_memory(mi, pa);
- free_pa(pa);
- if (res)
- goto cleanup;
+ /* allocate memory */
+ pa = get_new_pa(tmm[fmt], tcm_sizeof(mi->area));
+ if (IS_ERR_OR_NULL(pa)) {
+ res = -ENOMEM;
+ goto cleanup;
}
+ /* pin memory */
+ res = pin_memory(mi, pa);
+ free_pa(pa);
+ if (res)
+ goto cleanup;
+
*info = mi;
return 0;
@@ -1047,7 +1016,36 @@ cleanup:
mutex_lock(&mtx);
_m_free(mi);
mutex_unlock(&mtx);
- return -ENOMEM;
+ return res;
+}
+
+/* gets physical pages from scatterlist */
+static struct tiler_pa_info *scatterlist_to_pa(struct scatterlist *sglist,
+ u32 nents)
+{
+ int i;
+ struct scatterlist *sg;
+ struct tiler_pa_info *pa = NULL;
+ u32 *mem = NULL;
+
+ pa = kzalloc(sizeof(*pa), GFP_KERNEL);
+ if (!pa)
+ return NULL;
+
+ mem = kzalloc(nents * sizeof(*mem), GFP_KERNEL);
+ if (!mem) {
+ kfree(pa);
+ return NULL;
+ }
+
+ /* iterate over scatterlist and build up mem information */
+ for_each_sg(sglist, sg, nents, i)
+ mem[i] = sg_phys(sg);
+
+ pa->mem = mem;
+ pa->memtype = TILER_MEM_USING;
+ pa->num_pg = nents;
+ return pa;
}
/* get physical pages of a user block */
@@ -1056,6 +1054,7 @@ static struct tiler_pa_info *user_block_to_pa(u32 usr_addr, u32 num_pg)
struct task_struct *curr_task = current;
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma = NULL;
+
struct tiler_pa_info *pa = NULL;
struct page *page = NULL;
u32 *mem = NULL, got_pg = 1, i = 0, write;
@@ -1145,7 +1144,8 @@ static struct tiler_pa_info *user_block_to_pa(u32 usr_addr, u32 num_pg)
return pa;
}
-static s32 map_any_block(enum tiler_fmt fmt, u32 width, u32 height,
+/* allocate area from container and pin memory */
+static s32 pin_any_block(enum tiler_fmt fmt, u32 width, u32 height,
u32 key, u32 gid, struct process_info *pi,
struct mem_info **info, struct tiler_pa_info *pa)
{
@@ -1154,16 +1154,12 @@ static s32 map_any_block(enum tiler_fmt fmt, u32 width, u32 height,
*info = NULL;
- /* we only support mapping a user buffer in page mode */
- if (fmt != TILFMT_PAGE)
- goto done;
-
/* check if mapping is supported by tmm */
- if (!tmm_can_map(tmm[fmt]))
+ if (!tmm_can_pin(tmm[fmt]))
goto done;
/* get allocation area */
- mi = alloc_block_area(fmt, width, height, 0, 0, key, gid, pi);
+ mi = alloc_block_area(fmt, width, height, key, gid, pi);
if (IS_ERR_OR_NULL(mi)) {
res = mi ? PTR_ERR(mi) : -ENOMEM;
goto done;
@@ -1185,19 +1181,40 @@ done:
return res;
}
-static s32 map_block(enum tiler_fmt fmt, u32 width, u32 height,
+static s32 pin_block(enum tiler_fmt fmt, u32 width, u32 height,
u32 key, u32 gid, struct process_info *pi,
struct mem_info **info, u32 usr_addr)
{
struct tiler_pa_info *pa = NULL;
+ /* we only support mapping a user buffer in page mode */
+ if (fmt != TILFMT_PAGE)
+ return -ENOMEM;
+
/* get user pages */
pa = user_block_to_pa(usr_addr, DIV_ROUND_UP(width, PAGE_SIZE));
if (IS_ERR_OR_NULL(pa))
return pa ? PTR_ERR(pa) : -ENOMEM;
- return map_any_block(fmt, width, height, key, gid, pi, info, pa);
+ return pin_any_block(fmt, width, height, key, gid, pi, info, pa);
+}
+
+s32 tiler_pin_block(tiler_blk_handle block, struct scatterlist *sg, u32 nents)
+{
+ struct tiler_pa_info *pa = NULL;
+ int res;
+
+ /* get user pages */
+ pa = scatterlist_to_pa(sg, nents);
+ if (IS_ERR_OR_NULL(pa))
+ return pa ? PTR_ERR(pa) : -ENOMEM;
+
+ res = pin_memory(block, pa);
+ free_pa(pa);
+
+ return res;
}
+EXPORT_SYMBOL(tiler_pin_block);
/*
* Driver code
@@ -1212,13 +1229,16 @@ static s32 __init tiler_init(void)
struct tcm_pt div_pt;
struct tcm *sita = NULL;
struct tmm *tmm_pat = NULL;
+ struct pat_area area = {0};
tiler.alloc = alloc_block;
- tiler.map = map_block;
+ tiler.pin = pin_block;
tiler.lock = find_n_lock;
tiler.unlock_free = unlock_n_free;
tiler.lay_2d = lay_2d;
+#ifdef CONFIG_TILER_ENABLE_NV12
tiler.lay_nv12 = lay_nv12;
+#endif
tiler.destroy_group = destroy_group;
tiler.lock_by_ssptr = find_block_by_ssptr;
tiler.describe = fill_block_info;
@@ -1229,12 +1249,18 @@ static s32 __init tiler_init(void)
tiler.analize = __analize_area;
tiler_geom_init(&tiler);
tiler_reserve_init(&tiler);
+
+ mutex_init(&tiler.mtx);
tiler_iface_init(&tiler);
+#ifdef CONFIG_TILER_ENABLE_USERSPACE
+ tiler_ioctl_init(&tiler);
+#endif
+#ifdef CONFIG_TILER_ENABLE_NV12
+ tiler_nv12_init(&tiler);
+#endif
/* check module parameters for correctness */
- if (default_align > PAGE_SIZE ||
- default_align & (default_align - 1) ||
- granularity < 1 || granularity > PAGE_SIZE ||
+ if (granularity < 1 || granularity > PAGE_SIZE ||
granularity & (granularity - 1))
return -EINVAL;
@@ -1258,13 +1284,20 @@ static s32 __init tiler_init(void)
tcm[TILFMT_PAGE] = sita;
/* Allocate tiler memory manager (must have 1 unique TMM per TCM ) */
- tmm_pat = tmm_pat_init(0);
+ tmm_pat = tmm_pat_init(0, dmac_va, dmac_pa);
tmm[TILFMT_8BIT] = tmm_pat;
tmm[TILFMT_16BIT] = tmm_pat;
tmm[TILFMT_32BIT] = tmm_pat;
tmm[TILFMT_PAGE] = tmm_pat;
+ /* Clear out all PAT entries */
+ area.x1 = tiler.width - 1;
+ area.y1 = tiler.height - 1;
+ tmm_unpin(tmm_pat, area);
+
+#ifdef CONFIG_TILER_ENABLE_NV12
tiler.nv12_packed = tcm[TILFMT_8BIT] == tcm[TILFMT_16BIT];
+#endif
tiler_device = kmalloc(sizeof(*tiler_device), GFP_KERNEL);
if (!tiler_device || !sita || !tmm_pat) {
@@ -1364,43 +1397,78 @@ tiler_blk_handle tiler_map_1d_block(struct tiler_pa_info *pa)
{
struct mem_info *mi = NULL;
struct tiler_pa_info *pa_tmp = kmemdup(pa, sizeof(*pa), GFP_KERNEL);
- s32 res = map_any_block(TILFMT_PAGE, pa->num_pg << PAGE_SHIFT, 1, 0, 0,
+ s32 res = pin_any_block(TILFMT_PAGE, pa->num_pg << PAGE_SHIFT, 1, 0, 0,
__get_pi(0, true), &mi, pa_tmp);
return res ? ERR_PTR(res) : mi;
}
EXPORT_SYMBOL(tiler_map_1d_block);
-void tiler_free_block(tiler_blk_handle block)
+void tiler_free_block_area(tiler_blk_handle block)
{
mutex_lock(&mtx);
_m_try_free(block);
mutex_unlock(&mtx);
}
-EXPORT_SYMBOL(tiler_free_block);
+EXPORT_SYMBOL(tiler_free_block_area);
-tiler_blk_handle tiler_alloc_block_area(u32 size)
+tiler_blk_handle tiler_alloc_block_area(enum tiler_fmt fmt, u32 width,
+ u32 height, u32 *ssptr)
{
- return alloc_block_area(TILFMT_PAGE, size >> PAGE_SHIFT, 1, 0, 0, 0, 0,
- __get_pi(0, true));
+ struct mem_info *mi;
+ *ssptr = 0;
+
+ mi = alloc_block_area(fmt, width, height, 0, 0, __get_pi(0, true));
+
+ if (IS_ERR_OR_NULL(mi))
+ goto done;
+
+ *ssptr = mi->blk.phys;
+
+done:
+ return mi;
}
EXPORT_SYMBOL(tiler_alloc_block_area);
-void tiler_unpin_memory(tiler_blk_handle block)
+tiler_blk_handle tiler_alloc_1d_block_area(u32 size)
+{
+ return alloc_block_area(TILFMT_PAGE, size >> PAGE_SHIFT, 1, 0, 0,
+ __get_pi(0, true));
+}
+EXPORT_SYMBOL(tiler_alloc_1d_block_area);
+
+void tiler_unpin_block(tiler_blk_handle block)
{
mutex_lock(&mtx);
_m_unpin(block);
mutex_unlock(&mtx);
}
-EXPORT_SYMBOL(tiler_unpin_memory);
+EXPORT_SYMBOL(tiler_unpin_block);
s32 tiler_pin_memory(tiler_blk_handle block, struct tiler_pa_info *pa)
{
struct tiler_pa_info *pa_tmp = kmemdup(pa, sizeof(*pa), GFP_KERNEL);
- tiler_unpin_memory(block);
+ tiler_unpin_block(block);
return pin_memory(block, pa_tmp);
}
EXPORT_SYMBOL(tiler_pin_memory);
+u32 tiler_memsize(enum tiler_fmt fmt, u32 width, u32 height)
+{
+ u16 x, y, band, align;
+
+ if (tiler.analize(fmt, width, height, &x, &y, &align, &band))
+ return 0;
+ else
+ return x*y;
+}
+EXPORT_SYMBOL(tiler_memsize);
+
+u32 tiler_block_vstride(tiler_blk_handle block)
+{
+ return tiler_vstride(&block->blk);
+}
+EXPORT_SYMBOL(tiler_block_vstride);
+
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Lajos Molnar <molnar@ti.com>");
MODULE_AUTHOR("David Sin <davidsin@ti.com>");
diff --git a/drivers/media/video/tiler/tiler-nv12.c b/drivers/media/video/tiler/tiler-nv12.c
new file mode 100644
index 0000000..e166122
--- /dev/null
+++ b/drivers/media/video/tiler/tiler-nv12.c
@@ -0,0 +1,417 @@
+/*
+ * tiler-nv12.c
+ *
+ * TILER driver NV12 area reservation functions for TI TILER hardware block.
+ *
+ * Author: Lajos Molnar <molnar@ti.com>
+ *
+ * Copyright (C) 2009-2010 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
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "_tiler.h"
+
+static struct tiler_ops *ops; /* shared methods and variables */
+static int band_8;
+static int band_16;
+
+/*
+ * NV12 Reservation Functions
+ *
+ * TILER is designed so that a (w * h) * 8bit area is twice as wide as a
+ * (w/2 * h/2) * 16bit area. Since having pairs of such 8-bit and 16-bit
+ * blocks is a common usecase for TILER, we optimize packing these into a
+ * TILER area.
+ *
+ * During reservation we want to find the most effective packing (most used area
+ * in the smallest overall area)
+ *
+ * We have two algorithms for packing nv12 blocks: either pack 8- and 16-bit
+ * blocks into separate container areas, or pack them together into same area.
+ */
+
+/**
+ * Calculate effectiveness of packing. We weight total area much higher than
+ * packing efficiency to get the smallest overall container use.
+ *
+ * @param w width of one (8-bit) block
+ * @param n buffers in a packing
+ * @param area width of packing area
+ * @param n_total total number of buffers to be packed
+ * @return effectiveness, the higher the better
+ */
+static inline u32 nv12_eff(u16 w, u16 n, u16 area, u16 n_total)
+{
+ return 0x10000000 -
+ /* weigh against total area needed (for all buffers) */
+ /* 64-slots = -2048 */
+ DIV_ROUND_UP(n_total, n) * area * 32 +
+ /* packing efficiency (0 - 1024) */
+ 1024 * n * ((w * 3 + 1) >> 1) / area;
+}
+
+/**
+ * Fallback nv12 packing algorithm: pack 8 and 16 bit block into separate
+ * areas.
+ *
+ * @author a0194118 (7/16/2010)
+ *
+ * @param o desired offset (<a)
+ * @param a desired alignment (>=2)
+ * @param w block width (>0)
+ * @param n number of blocks desired
+ * @param area pointer to store total area needed
+ *
+ * @return number of blocks that can be allocated
+ */
+static u16 nv12_separate(u16 o, u16 a, u16 w, u16 n, u16 *area)
+{
+ tiler_best2pack(o, a, band_8, w, &n, area);
+ tiler_best2pack(o >> 1, a >> 1, band_16, (w + 1) >> 1, &n, area);
+ *area *= 3;
+ return n;
+}
+
+/*
+ * Specialized NV12 Reservation Algorithms
+ *
+ * We use 4 packing methods that pack nv12 blocks into the same area. Together
+ * these 4 methods give the optimal result for most possible input parameters.
+ *
+ * For now we pack into a 64-slot area, so that we don't have to worry about
+ * stride issues (all blocks get 4K stride). For some of the algorithms this
+ * could be true even if the area was 128.
+ */
+
+/**
+ * Packing types are marked using a letter sequence, capital letters denoting
+ * 8-bit blocks, lower case letters denoting corresponding 16-bit blocks.
+ *
+ * All methods have the following parameters. They also define the maximum
+ * number of coordinates that could potentially be packed.
+ *
+ * @param o, a, w, n offset, alignment, width, # of blocks as usual
+ * @param area pointer to store area needed for packing
+ * @param p pointer to store packing coordinates
+ * @return number of blocks that can be packed
+ */
+
+/* Method A: progressive packing: AAAAaaaaBBbbCc into 64-slot area */
+#define MAX_A 21
+static int nv12_A(u16 o, u16 a, u16 w, u16 n, u16 *area, u8 *p)
+{
+ u16 x = o, u, l, m = 0;
+ *area = band_8;
+
+ while (x + w < *area && m < n) {
+ /* current 8bit upper bound (a) is next 8bit lower bound (B) */
+ l = u = (*area + x) >> 1;
+
+ /* pack until upper bound */
+ while (x + w <= u && m < n) {
+ /* save packing */
+ BUG_ON(m + 1 >= MAX_A);
+ *p++ = x;
+ *p++ = l;
+ l = (*area + x + w + 1) >> 1;
+ x = ALIGN(x + w - o, a) + o;
+ m++;
+ }
+ x = ALIGN(l - o, a) + o; /* set new lower bound */
+ }
+ return m;
+}
+
+/* Method -A: regressive packing: cCbbBBaaaaAAAA into 64-slot area */
+static int nv12_revA(u16 o, u16 a, u16 w, u16 n, u16 *area, u8 *p)
+{
+ u16 m;
+
+ /* this is a mirrored packing of method A */
+ n = nv12_A((a - (o + w) % a) % a, a, w, n, area, p);
+
+ /* reverse packing */
+ for (m = 0; m < n; m++) {
+ *p = *area - *p - w;
+ p++;
+ *p = *area - *p - ((w + 1) >> 1);
+ p++;
+ }
+ return n;
+}
+
+/* Method B: simple layout: aAbcBdeCfgDhEFGH */
+#define MAX_B 8
+static int nv12_B(u16 o, u16 a, u16 w, u16 n, u16 *area, u8 *p)
+{
+ u16 e = (o + w) % a; /* end offset */
+ u16 o1 = (o >> 1) % a; /* half offset */
+ u16 e1 = ((o + w + 1) >> 1) % a; /* half end offset */
+ u16 o2 = o1 + (a >> 2); /* 2nd half offset */
+ u16 e2 = e1 + (a >> 2); /* 2nd half end offset */
+ u16 m = 0;
+ *area = band_8;
+
+ /* ensure 16-bit blocks don't overlap 8-bit blocks */
+
+ /* width cannot wrap around alignment, half block must be before block,
+ 2nd half can be before or after */
+ if (w < a && o < e && e1 <= o && (e2 <= o || o2 >= e))
+ while (o + w <= *area && m < n) {
+ BUG_ON(m + 1 >= MAX_B);
+ *p++ = o;
+ *p++ = o >> 1;
+ m++;
+ o += a;
+ }
+ return m;
+}
+
+/* Method C: butterfly layout: AAbbaaBB */
+#define MAX_C 20
+static int nv12_C(u16 o, u16 a, u16 w, u16 n, u16 *area, u8 *p)
+{
+ int m = 0;
+ u16 o2, e = ALIGN(w, a), i = 0, j = 0;
+ *area = band_8;
+ o2 = *area - (a - (o + w) % a) % a; /* end of last possible block */
+
+ m = (min(o2 - 2 * o, 2 * o2 - o - *area) / 3 - w) / e + 1;
+ for (i = j = 0; i < m && j < n; i++, j++) {
+ BUG_ON(j + 1 >= MAX_C);
+ *p++ = o + i * e;
+ *p++ = (o + i * e + *area) >> 1;
+ if (++j < n) {
+ *p++ = o2 - i * e - w;
+ *p++ = (o2 - i * e - w) >> 1;
+ }
+ }
+ return j;
+}
+
+/* Method D: for large allocation: aA or Aa */
+#define MAX_D 1
+static int nv12_D(u16 o, u16 a, u16 w, u16 n, u16 *area, u8 *p)
+{
+ u16 o1, w1 = (w + 1) >> 1, d;
+ *area = ALIGN(o + w, band_8);
+
+ for (d = 0; n > 0 && d + o + w <= *area; d += a) {
+ /* try to fit 16-bit before 8-bit */
+ o1 = ((o + d) % band_8) >> 1;
+ if (o1 + w1 <= o + d) {
+ *p++ = o + d;
+ *p++ = o1;
+ return 1;
+ }
+
+ /* try to fit 16-bit after 8-bit */
+ o1 += ALIGN(d + o + w - o1, band_16);
+ if (o1 + w1 <= *area) {
+ *p++ = o;
+ *p++ = o1;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Umbrella nv12 packing method. This selects the best packings from the above
+ * methods. It also contains hardcoded packings for parameter combinations
+ * that have more efficient packings. This method provides is guaranteed to
+ * provide the optimal packing if 2 <= a <= 64 and w <= 64 and n is large.
+ */
+#define MAX_ANY 21 /* must be MAX(method-MAX-s, hardcoded n-s) */
+static u16 nv12_together(u16 o, u16 a, u16 w, u16 n, u16 *area, u8 *packing)
+{
+ u16 n_best, a_best, n2, a_, o_, w_;
+
+ /* algo results (packings) */
+ u8 pack_A[MAX_A * 2], pack_rA[MAX_A * 2];
+ u8 pack_B[MAX_B * 2], pack_C[MAX_C * 2];
+ u8 pack_D[MAX_D * 2];
+
+ /*
+ * Hardcoded packings. They are sorted by increasing area, and then by
+ * decreasing n. We may not get the best efficiency if less than n
+ * blocks are needed as packings are not necessarily sorted in
+ * increasing order. However, for those n-s one of the other 4 methods
+ * may return the optimal packing.
+ */
+ u8 packings[] = {
+ /* n=9, o=2, w=4, a=4, area=64 */
+ 9, 2, 4, 4, 64,
+ /* 8-bit, 16-bit block coordinate pairs */
+ 2, 33, 6, 35, 10, 37, 14, 39, 18, 41,
+ 46, 23, 50, 25, 54, 27, 58, 29,
+ /* o=0, w=12, a=4, n=3 */
+ 3, 0, 12, 4, 64,
+ 0, 32, 12, 38, 48, 24,
+ /* end */
+ 0
+ }, *p = packings, *p_best = NULL, *p_end;
+ p_end = packings + sizeof(packings) - 1;
+
+ /* see which method gives the best packing */
+
+ /* start with smallest area algorithms A, B & C, stop if we can
+ pack all buffers */
+ n_best = nv12_A(o, a, w, n, area, pack_A);
+ p_best = pack_A;
+ if (n_best < n) {
+ n2 = nv12_revA(o, a, w, n, &a_best, pack_rA);
+ if (n2 > n_best) {
+ n_best = n2;
+ p_best = pack_rA;
+ *area = a_best;
+ }
+ }
+ if (n_best < n) {
+ n2 = nv12_B(o, a, w, n, &a_best, pack_B);
+ if (n2 > n_best) {
+ n_best = n2;
+ p_best = pack_B;
+ *area = a_best;
+ }
+ }
+ if (n_best < n) {
+ n2 = nv12_C(o, a, w, n, &a_best, pack_C);
+ if (n2 > n_best) {
+ n_best = n2;
+ p_best = pack_C;
+ *area = a_best;
+ }
+ }
+
+ /* traverse any special packings */
+ while (*p) {
+ n2 = *p++;
+ o_ = *p++;
+ w_ = *p++;
+ a_ = *p++;
+ /* stop if we already have a better packing */
+ if (n2 < n_best)
+ break;
+
+ /* check if this packing is satisfactory */
+ if (a_ >= a && o + w + ALIGN(o_ - o, a) <= o_ + w_) {
+ *area = *p++;
+ n_best = min(n2, n);
+ p_best = p;
+ break;
+ }
+
+ /* skip to next packing */
+ p += 1 + n2 * 2;
+ }
+
+ /*
+ * If so far unsuccessful, check whether 8 and 16 bit blocks can be
+ * co-packed. This will actually be done in the end by the normal
+ * allocation, but we need to reserve a big-enough area.
+ */
+ if (!n_best) {
+ n_best = nv12_D(o, a, w, n, area, pack_D);
+ p_best = NULL;
+ }
+
+ /* store best packing */
+ if (p_best && n_best) {
+ BUG_ON(n_best > MAX_ANY);
+ memcpy(packing, p_best, n_best * 2 * sizeof(*pack_A));
+ }
+
+ return n_best;
+}
+
+/* reserve nv12 blocks */
+static void reserve_nv12(u32 n, u32 width, u32 height,
+ u32 gid, struct process_info *pi)
+{
+ u16 w, h, band, a, o = 0;
+ struct gid_info *gi;
+ int res = 0, res2, i;
+ u16 n_t, n_s, area_t, area_s;
+ u8 packing[2 * MAX_ANY];
+ struct list_head reserved = LIST_HEAD_INIT(reserved);
+
+ /* Check input parameters for correctness, and support */
+ if (!width || !height || !n ||
+ n > ops->width * ops->height / 2)
+ return;
+
+ /* calculate dimensions, band, and alignment in slots */
+ if (ops->analize(TILFMT_8BIT, width, height, &w, &h, &band, &a))
+ return;
+
+ /* get group context */
+ gi = ops->get_gi(pi, gid);
+ if (!gi)
+ return;
+
+ /* reserve in groups until failed or all is reserved */
+ for (i = 0; i < n && res >= 0; i += res) {
+ /* check packing separately vs together */
+ n_s = nv12_separate(o, a, w, n - i, &area_s);
+ if (ops->nv12_packed)
+ n_t = nv12_together(o, a, w, n - i, &area_t, packing);
+ else
+ n_t = 0;
+
+ /* pack based on better efficiency */
+ res = -1;
+ if (!ops->nv12_packed ||
+ nv12_eff(w, n_s, area_s, n - i) >
+ nv12_eff(w, n_t, area_t, n - i)) {
+
+ /*
+ * Reserve blocks separately into a temporary list, so
+ * that we can free them if unsuccessful. We need to be
+ * able to reserve both 8- and 16-bit blocks as the
+ * offsets of them must match.
+ */
+ res = ops->lay_2d(TILFMT_8BIT, n_s, w, h, band_8, a,
+ gi, &reserved);
+ res2 = ops->lay_2d(TILFMT_16BIT, n_s, (w + 1) >> 1, h,
+ band_16, a >> 1, gi, &reserved);
+
+ if (res2 < 0 || res < 0 || res != res2) {
+ /* clean up */
+ ops->release(&reserved);
+ res = -1;
+ } else {
+ /* add list to reserved */
+ ops->add_reserved(&reserved, gi);
+ }
+ }
+
+ /* if separate packing failed, still try to pack together */
+ if (res < 0 && ops->nv12_packed && n_t) {
+ /* pack together */
+ res = ops->lay_nv12(n_t, area_t, w, h, gi, packing);
+ }
+ }
+
+ ops->release_gi(gi);
+}
+
+/* initialize shared method pointers and global static variables */
+void tiler_nv12_init(struct tiler_ops *tiler)
+{
+ ops = tiler;
+
+ ops->reserve_nv12 = reserve_nv12;
+
+ band_8 = PAGE_SIZE / ops->geom(TILFMT_8BIT)->slot_w
+ / ops->geom(TILFMT_8BIT)->bpp;
+ band_16 = PAGE_SIZE / ops->geom(TILFMT_16BIT)->slot_w
+ / ops->geom(TILFMT_16BIT)->bpp;
+}
diff --git a/drivers/media/video/tiler/tiler-reserve.c b/drivers/media/video/tiler/tiler-reserve.c
index 6715d3d..fbabc6d 100644
--- a/drivers/media/video/tiler/tiler-reserve.c
+++ b/drivers/media/video/tiler/tiler-reserve.c
@@ -19,8 +19,6 @@
#include "_tiler.h"
static struct tiler_ops *ops; /* shared methods and variables */
-static int band_8; /* size of 8-bit band in slots */
-static int band_16; /* size of 16-bit band in slots */
/**
* Calculate the maximum number buffers that can be packed next to each other,
@@ -38,7 +36,7 @@ static int band_16; /* size of 16-bit band in slots */
*
* @return packing efficiency (0-1024)
*/
-static u32 tiler_best2pack(u16 o, u16 a, u16 b, u16 w, u16 *n, u16 *_area)
+u32 tiler_best2pack(u16 o, u16 a, u16 b, u16 w, u16 *n, u16 *_area)
{
u16 m = 0, max_n = *n; /* m is mostly n - 1 */
u16 e = ALIGN(w, a); /* effective width of one block */
@@ -71,393 +69,6 @@ static u32 tiler_best2pack(u16 o, u16 a, u16 b, u16 w, u16 *n, u16 *_area)
return best_eff;
}
-/*
- * NV12 Reservation Functions
- *
- * TILER is designed so that a (w * h) * 8bit area is twice as wide as a
- * (w/2 * h/2) * 16bit area. Since having pairs of such 8-bit and 16-bit
- * blocks is a common usecase for TILER, we optimize packing these into a
- * TILER area.
- *
- * During reservation we want to find the most effective packing (most used area
- * in the smallest overall area)
- *
- * We have two algorithms for packing nv12 blocks: either pack 8- and 16-bit
- * blocks into separate container areas, or pack them together into same area.
- */
-
-/**
- * Calculate effectiveness of packing. We weight total area much higher than
- * packing efficiency to get the smallest overall container use.
- *
- * @param w width of one (8-bit) block
- * @param n buffers in a packing
- * @param area width of packing area
- * @param n_total total number of buffers to be packed
- * @return effectiveness, the higher the better
- */
-static inline u32 nv12_eff(u16 w, u16 n, u16 area, u16 n_total)
-{
- return 0x10000000 -
- /* weigh against total area needed (for all buffers) */
- /* 64-slots = -2048 */
- DIV_ROUND_UP(n_total, n) * area * 32 +
- /* packing efficiency (0 - 1024) */
- 1024 * n * ((w * 3 + 1) >> 1) / area;
-}
-
-/**
- * Fallback nv12 packing algorithm: pack 8 and 16 bit block into separate
- * areas.
- *
- * @author a0194118 (7/16/2010)
- *
- * @param o desired offset (<a)
- * @param a desired alignment (>=2)
- * @param w block width (>0)
- * @param n number of blocks desired
- * @param area pointer to store total area needed
- *
- * @return number of blocks that can be allocated
- */
-static u16 nv12_separate(u16 o, u16 a, u16 w, u16 n, u16 *area)
-{
- tiler_best2pack(o, a, band_8, w, &n, area);
- tiler_best2pack(o >> 1, a >> 1, band_16, (w + 1) >> 1, &n, area);
- *area *= 3;
- return n;
-}
-
-/*
- * Specialized NV12 Reservation Algorithms
- *
- * We use 4 packing methods that pack nv12 blocks into the same area. Together
- * these 4 methods give the optimal result for most possible input parameters.
- *
- * For now we pack into a 64-slot area, so that we don't have to worry about
- * stride issues (all blocks get 4K stride). For some of the algorithms this
- * could be true even if the area was 128.
- */
-
-/**
- * Packing types are marked using a letter sequence, capital letters denoting
- * 8-bit blocks, lower case letters denoting corresponding 16-bit blocks.
- *
- * All methods have the following parameters. They also define the maximum
- * number of coordinates that could potentially be packed.
- *
- * @param o, a, w, n offset, alignment, width, # of blocks as usual
- * @param area pointer to store area needed for packing
- * @param p pointer to store packing coordinates
- * @return number of blocks that can be packed
- */
-
-/* Method A: progressive packing: AAAAaaaaBBbbCc into 64-slot area */
-#define MAX_A 21
-static int nv12_A(u16 o, u16 a, u16 w, u16 n, u16 *area, u8 *p)
-{
- u16 x = o, u, l, m = 0;
- *area = band_8;
-
- while (x + w < *area && m < n) {
- /* current 8bit upper bound (a) is next 8bit lower bound (B) */
- l = u = (*area + x) >> 1;
-
- /* pack until upper bound */
- while (x + w <= u && m < n) {
- /* save packing */
- BUG_ON(m + 1 >= MAX_A);
- *p++ = x;
- *p++ = l;
- l = (*area + x + w + 1) >> 1;
- x = ALIGN(x + w - o, a) + o;
- m++;
- }
- x = ALIGN(l - o, a) + o; /* set new lower bound */
- }
- return m;
-}
-
-/* Method -A: regressive packing: cCbbBBaaaaAAAA into 64-slot area */
-static int nv12_revA(u16 o, u16 a, u16 w, u16 n, u16 *area, u8 *p)
-{
- u16 m;
-
- /* this is a mirrored packing of method A */
- n = nv12_A((a - (o + w) % a) % a, a, w, n, area, p);
-
- /* reverse packing */
- for (m = 0; m < n; m++) {
- *p = *area - *p - w;
- p++;
- *p = *area - *p - ((w + 1) >> 1);
- p++;
- }
- return n;
-}
-
-/* Method B: simple layout: aAbcBdeCfgDhEFGH */
-#define MAX_B 8
-static int nv12_B(u16 o, u16 a, u16 w, u16 n, u16 *area, u8 *p)
-{
- u16 e = (o + w) % a; /* end offset */
- u16 o1 = (o >> 1) % a; /* half offset */
- u16 e1 = ((o + w + 1) >> 1) % a; /* half end offset */
- u16 o2 = o1 + (a >> 2); /* 2nd half offset */
- u16 e2 = e1 + (a >> 2); /* 2nd half end offset */
- u16 m = 0;
- *area = band_8;
-
- /* ensure 16-bit blocks don't overlap 8-bit blocks */
-
- /* width cannot wrap around alignment, half block must be before block,
- 2nd half can be before or after */
- if (w < a && o < e && e1 <= o && (e2 <= o || o2 >= e))
- while (o + w <= *area && m < n) {
- BUG_ON(m + 1 >= MAX_B);
- *p++ = o;
- *p++ = o >> 1;
- m++;
- o += a;
- }
- return m;
-}
-
-/* Method C: butterfly layout: AAbbaaBB */
-#define MAX_C 20
-static int nv12_C(u16 o, u16 a, u16 w, u16 n, u16 *area, u8 *p)
-{
- int m = 0;
- u16 o2, e = ALIGN(w, a), i = 0, j = 0;
- *area = band_8;
- o2 = *area - (a - (o + w) % a) % a; /* end of last possible block */
-
- m = (min(o2 - 2 * o, 2 * o2 - o - *area) / 3 - w) / e + 1;
- for (i = j = 0; i < m && j < n; i++, j++) {
- BUG_ON(j + 1 >= MAX_C);
- *p++ = o + i * e;
- *p++ = (o + i * e + *area) >> 1;
- if (++j < n) {
- *p++ = o2 - i * e - w;
- *p++ = (o2 - i * e - w) >> 1;
- }
- }
- return j;
-}
-
-/* Method D: for large allocation: aA or Aa */
-#define MAX_D 1
-static int nv12_D(u16 o, u16 a, u16 w, u16 n, u16 *area, u8 *p)
-{
- u16 o1, w1 = (w + 1) >> 1, d;
- *area = ALIGN(o + w, band_8);
-
- for (d = 0; n > 0 && d + o + w <= *area; d += a) {
- /* try to fit 16-bit before 8-bit */
- o1 = ((o + d) % band_8) >> 1;
- if (o1 + w1 <= o + d) {
- *p++ = o + d;
- *p++ = o1;
- return 1;
- }
-
- /* try to fit 16-bit after 8-bit */
- o1 += ALIGN(d + o + w - o1, band_16);
- if (o1 + w1 <= *area) {
- *p++ = o;
- *p++ = o1;
- return 1;
- }
- }
- return 0;
-}
-
-/**
- * Umbrella nv12 packing method. This selects the best packings from the above
- * methods. It also contains hardcoded packings for parameter combinations
- * that have more efficient packings. This method provides is guaranteed to
- * provide the optimal packing if 2 <= a <= 64 and w <= 64 and n is large.
- */
-#define MAX_ANY 21 /* must be MAX(method-MAX-s, hardcoded n-s) */
-static u16 nv12_together(u16 o, u16 a, u16 w, u16 n, u16 *area, u8 *packing)
-{
- u16 n_best, a_best, n2, a_, o_, w_;
-
- /* algo results (packings) */
- u8 pack_A[MAX_A * 2], pack_rA[MAX_A * 2];
- u8 pack_B[MAX_B * 2], pack_C[MAX_C * 2];
- u8 pack_D[MAX_D * 2];
-
- /*
- * Hardcoded packings. They are sorted by increasing area, and then by
- * decreasing n. We may not get the best efficiency if less than n
- * blocks are needed as packings are not necessarily sorted in
- * increasing order. However, for those n-s one of the other 4 methods
- * may return the optimal packing.
- */
- u8 packings[] = {
- /* n=9, o=2, w=4, a=4, area=64 */
- 9, 2, 4, 4, 64,
- /* 8-bit, 16-bit block coordinate pairs */
- 2, 33, 6, 35, 10, 37, 14, 39, 18, 41,
- 46, 23, 50, 25, 54, 27, 58, 29,
- /* o=0, w=12, a=4, n=3 */
- 3, 0, 12, 4, 64,
- 0, 32, 12, 38, 48, 24,
- /* end */
- 0
- }, *p = packings, *p_best = NULL, *p_end;
- p_end = packings + sizeof(packings) - 1;
-
- /* see which method gives the best packing */
-
- /* start with smallest area algorithms A, B & C, stop if we can
- pack all buffers */
- n_best = nv12_A(o, a, w, n, area, pack_A);
- p_best = pack_A;
- if (n_best < n) {
- n2 = nv12_revA(o, a, w, n, &a_best, pack_rA);
- if (n2 > n_best) {
- n_best = n2;
- p_best = pack_rA;
- *area = a_best;
- }
- }
- if (n_best < n) {
- n2 = nv12_B(o, a, w, n, &a_best, pack_B);
- if (n2 > n_best) {
- n_best = n2;
- p_best = pack_B;
- *area = a_best;
- }
- }
- if (n_best < n) {
- n2 = nv12_C(o, a, w, n, &a_best, pack_C);
- if (n2 > n_best) {
- n_best = n2;
- p_best = pack_C;
- *area = a_best;
- }
- }
-
- /* traverse any special packings */
- while (*p) {
- n2 = *p++;
- o_ = *p++;
- w_ = *p++;
- a_ = *p++;
- /* stop if we already have a better packing */
- if (n2 < n_best)
- break;
-
- /* check if this packing is satisfactory */
- if (a_ >= a && o + w + ALIGN(o_ - o, a) <= o_ + w_) {
- *area = *p++;
- n_best = min(n2, n);
- p_best = p;
- break;
- }
-
- /* skip to next packing */
- p += 1 + n2 * 2;
- }
-
- /*
- * If so far unsuccessful, check whether 8 and 16 bit blocks can be
- * co-packed. This will actually be done in the end by the normal
- * allocation, but we need to reserve a big-enough area.
- */
- if (!n_best) {
- n_best = nv12_D(o, a, w, n, area, pack_D);
- p_best = NULL;
- }
-
- /* store best packing */
- if (p_best && n_best) {
- BUG_ON(n_best > MAX_ANY);
- memcpy(packing, p_best, n_best * 2 * sizeof(*pack_A));
- }
-
- return n_best;
-}
-
-/* reserve nv12 blocks */
-static void reserve_nv12(u32 n, u32 width, u32 height, u32 align, u32 offs,
- u32 gid, struct process_info *pi)
-{
- u16 w, h, band, a = align, o = offs;
- struct gid_info *gi;
- int res = 0, res2, i;
- u16 n_t, n_s, area_t, area_s;
- u8 packing[2 * MAX_ANY];
- struct list_head reserved = LIST_HEAD_INIT(reserved);
-
- /* adjust alignment to the largest slot width (128 bytes) */
- a = max_t(u16, PAGE_SIZE / min(band_8, band_16), a);
-
- /* Check input parameters for correctness, and support */
- if (!width || !height || !n ||
- offs >= align || offs & 1 ||
- align >= PAGE_SIZE ||
- n > ops->width * ops->height / 2)
- return;
-
- /* calculate dimensions, band, offs and alignment in slots */
- if (ops->analize(TILFMT_8BIT, width, height, &w, &h, &band, &a, &o,
- NULL))
- return;
-
- /* get group context */
- gi = ops->get_gi(pi, gid);
- if (!gi)
- return;
-
- /* reserve in groups until failed or all is reserved */
- for (i = 0; i < n && res >= 0; i += res) {
- /* check packing separately vs together */
- n_s = nv12_separate(o, a, w, n - i, &area_s);
- if (ops->nv12_packed)
- n_t = nv12_together(o, a, w, n - i, &area_t, packing);
- else
- n_t = 0;
-
- /* pack based on better efficiency */
- res = -1;
- if (!ops->nv12_packed ||
- nv12_eff(w, n_s, area_s, n - i) >
- nv12_eff(w, n_t, area_t, n - i)) {
-
- /*
- * Reserve blocks separately into a temporary list, so
- * that we can free them if unsuccessful. We need to be
- * able to reserve both 8- and 16-bit blocks as the
- * offsets of them must match.
- */
- res = ops->lay_2d(TILFMT_8BIT, n_s, w, h, band_8, a, o,
- gi, &reserved);
- res2 = ops->lay_2d(TILFMT_16BIT, n_s, (w + 1) >> 1, h,
- band_16, a >> 1, o >> 1, gi, &reserved);
-
- if (res2 < 0 || res < 0 || res != res2) {
- /* clean up */
- ops->release(&reserved);
- res = -1;
- } else {
- /* add list to reserved */
- ops->add_reserved(&reserved, gi);
- }
- }
-
- /* if separate packing failed, still try to pack together */
- if (res < 0 && ops->nv12_packed && n_t) {
- /* pack together */
- res = ops->lay_nv12(n_t, area_t, w, h, gi, packing);
- }
- }
-
- ops->release_gi(gi);
-}
-
/**
* We also optimize packing regular 2D areas as the auto-packing may result in
* sub-optimal efficiency. This is most pronounced if the area is wider than
@@ -466,17 +77,16 @@ static void reserve_nv12(u32 n, u32 width, u32 height, u32 align, u32 offs,
/* reserve 2d blocks */
static void reserve_blocks(u32 n, enum tiler_fmt fmt, u32 width, u32 height,
- u32 align, u32 offs, u32 gid,
+ u32 gid,
struct process_info *pi)
{
u32 bpt, res = 0, i;
- u16 o = offs, a = align, band, w, h, n_try;
+ u16 a, band, w, h, n_try;
struct gid_info *gi;
const struct tiler_geom *g;
/* Check input parameters for correctness, and support */
if (!width || !height || !n ||
- align > PAGE_SIZE || offs >= align ||
fmt < TILFMT_8BIT || fmt > TILFMT_32BIT)
return;
@@ -489,7 +99,7 @@ static void reserve_blocks(u32 n, enum tiler_fmt fmt, u32 width, u32 height,
* sufficient. Also check for basic area info.
*/
if (width * g->bpp * 2 <= PAGE_SIZE ||
- ops->analize(fmt, width, height, &w, &h, &band, &a, &o, NULL))
+ ops->analize(fmt, width, height, &w, &h, &band, &a))
return;
/* get group id */
@@ -501,12 +111,12 @@ static void reserve_blocks(u32 n, enum tiler_fmt fmt, u32 width, u32 height,
for (i = 0; i < n && res >= 0; i += res + 1) {
/* blocks to allocate in one area */
n_try = min(n - i, ops->width);
- tiler_best2pack(offs, a, band, w, &n_try, NULL);
+ tiler_best2pack(0, a, band, w, &n_try, NULL);
res = -1;
while (n_try > 1) {
/* adjust res so we fail on 0 return value */
- res = ops->lay_2d(fmt, n_try, w, h, band, a, o,
+ res = ops->lay_2d(fmt, n_try, w, h, band, a,
gi, &gi->reserved) - 1;
if (res >= 0)
break;
@@ -539,12 +149,6 @@ void tiler_reserve_init(struct tiler_ops *tiler)
{
ops = tiler;
- ops->reserve_nv12 = reserve_nv12;
ops->reserve = reserve_blocks;
ops->unreserve = unreserve_blocks;
-
- band_8 = PAGE_SIZE / ops->geom(TILFMT_8BIT)->slot_w
- / ops->geom(TILFMT_8BIT)->bpp;
- band_16 = PAGE_SIZE / ops->geom(TILFMT_16BIT)->slot_w
- / ops->geom(TILFMT_16BIT)->bpp;
}
diff --git a/drivers/media/video/tiler/tmm-pat.c b/drivers/media/video/tiler/tmm-pat.c
index e47f43f..2d902f9 100644
--- a/drivers/media/video/tiler/tmm-pat.c
+++ b/drivers/media/video/tiler/tmm-pat.c
@@ -61,6 +61,10 @@ struct fast {
struct dmm_mem {
struct list_head fast_list;
struct dmm *dmm;
+ u32 *dmac_va; /* coherent memory */
+ u32 dmac_pa; /* phys.addr of coherent memory */
+ struct page *dummy_pg; /* dummy page */
+ u32 dummy_pa; /* phys.addr of dummy page */
};
/* read mem values for a param */
@@ -163,6 +167,8 @@ static void tmm_pat_deinit(struct tmm *tmm)
if (--refs == 0)
free_page_cache();
+ __free_page(pvt->dummy_pg);
+
mutex_unlock(&mtx);
}
@@ -243,7 +249,7 @@ static void tmm_pat_free_pages(struct tmm *tmm, u32 *page_list)
mutex_unlock(&mtx);
}
-static s32 tmm_pat_map(struct tmm *tmm, struct pat_area area, u32 page_pa)
+static s32 tmm_pat_pin(struct tmm *tmm, struct pat_area area, u32 page_pa)
{
struct dmm_mem *pvt = (struct dmm_mem *) tmm->pvt;
struct pat pat_desc = {0};
@@ -262,7 +268,20 @@ static s32 tmm_pat_map(struct tmm *tmm, struct pat_area area, u32 page_pa)
return dmm_pat_refill(pvt->dmm, &pat_desc, MANUAL);
}
-struct tmm *tmm_pat_init(u32 pat_id)
+static void tmm_pat_unpin(struct tmm *tmm, struct pat_area area)
+{
+ u16 w = (u8) area.x1 - (u8) area.x0;
+ u16 h = (u8) area.y1 - (u8) area.y0;
+ u16 i = (w + 1) * (h + 1);
+ struct dmm_mem *pvt = (struct dmm_mem *) tmm->pvt;
+
+ while (i--)
+ pvt->dmac_va[i] = pvt->dummy_pa;
+
+ tmm_pat_pin(tmm, area, pvt->dmac_pa);
+}
+
+struct tmm *tmm_pat_init(u32 pat_id, u32 *dmac_va, u32 dmac_pa)
{
struct tmm *tmm = NULL;
struct dmm_mem *pvt = NULL;
@@ -272,9 +291,15 @@ struct tmm *tmm_pat_init(u32 pat_id)
tmm = kmalloc(sizeof(*tmm), GFP_KERNEL);
if (tmm)
pvt = kmalloc(sizeof(*pvt), GFP_KERNEL);
- if (pvt) {
+ if (pvt)
+ pvt->dummy_pg = alloc_page(GFP_KERNEL | GFP_DMA);
+ if (pvt->dummy_pg) {
/* private data */
pvt->dmm = dmm;
+ pvt->dmac_pa = dmac_pa;
+ pvt->dmac_va = dmac_va;
+ pvt->dummy_pa = page_to_phys(pvt->dummy_pg);
+
INIT_LIST_HEAD(&pvt->fast_list);
/* increate tmm_pat references */
@@ -287,8 +312,8 @@ struct tmm *tmm_pat_init(u32 pat_id)
tmm->deinit = tmm_pat_deinit;
tmm->get = tmm_pat_get_pages;
tmm->free = tmm_pat_free_pages;
- tmm->map = tmm_pat_map;
- tmm->clear = NULL; /* not yet supported */
+ tmm->pin = tmm_pat_pin;
+ tmm->unpin = tmm_pat_unpin;
return tmm;
}
diff --git a/drivers/media/video/tiler/tmm.h b/drivers/media/video/tiler/tmm.h
index 9f2a99d..dc1b5b3 100644
--- a/drivers/media/video/tiler/tmm.h
+++ b/drivers/media/video/tiler/tmm.h
@@ -49,8 +49,8 @@ struct tmm {
/* function table */
u32 *(*get) (struct tmm *tmm, u32 num_pages);
void (*free) (struct tmm *tmm, u32 *pages);
- s32 (*map) (struct tmm *tmm, struct pat_area area, u32 page_pa);
- void (*clear) (struct tmm *tmm, struct pat_area area);
+ s32 (*pin) (struct tmm *tmm, struct pat_area area, u32 page_pa);
+ void (*unpin) (struct tmm *tmm, struct pat_area area);
void (*deinit) (struct tmm *tmm);
};
@@ -83,10 +83,10 @@ void tmm_free(struct tmm *tmm, u32 *pages)
* @param list of pages
*/
static inline
-s32 tmm_map(struct tmm *tmm, struct pat_area area, u32 page_pa)
+s32 tmm_pin(struct tmm *tmm, struct pat_area area, u32 page_pa)
{
- if (tmm && tmm->map && tmm->pvt)
- return tmm->map(tmm, area, page_pa);
+ if (tmm && tmm->pin && tmm->pvt)
+ return tmm->pin(tmm, area, page_pa);
return -ENODEV;
}
@@ -95,19 +95,19 @@ s32 tmm_map(struct tmm *tmm, struct pat_area area, u32 page_pa)
* @param area PAT area
*/
static inline
-void tmm_clear(struct tmm *tmm, struct pat_area area)
+void tmm_unpin(struct tmm *tmm, struct pat_area area)
{
- if (tmm && tmm->clear && tmm->pvt)
- tmm->clear(tmm, area);
+ if (tmm && tmm->unpin && tmm->pvt)
+ tmm->unpin(tmm, area);
}
/**
* Checks whether tiler memory manager supports mapping
*/
static inline
-bool tmm_can_map(struct tmm *tmm)
+bool tmm_can_pin(struct tmm *tmm)
{
- return tmm && tmm->map;
+ return tmm && tmm->pin;
}
/**
@@ -125,6 +125,6 @@ void tmm_deinit(struct tmm *tmm)
*
* Initialize TMM for PAT with given id.
*/
-struct tmm *tmm_pat_init(u32 pat_id);
+struct tmm *tmm_pat_init(u32 pat_id, u32 *dmac_va, u32 dmac_pa);
#endif
diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
index 1927ac4..e40c9d8 100644
--- a/drivers/rpmsg/Kconfig
+++ b/drivers/rpmsg/Kconfig
@@ -19,6 +19,7 @@ config RPMSG_OMX
tristate "rpmsg OMX driver"
default y
depends on RPMSG
+ depends on TI_TILER
---help---
An rpmsg driver that exposes OMX API to user space, in order to
allow multimedia applications to offload OMX processing to