diff options
author | Dima Zavin <dima@android.com> | 2011-06-24 13:33:00 -0700 |
---|---|---|
committer | Dima Zavin <dima@android.com> | 2011-06-24 13:33:00 -0700 |
commit | bd47783bb233d659b668d4154737bb7857e2fb8a (patch) | |
tree | 82808bd2624944e14dcf6bc7b6d207d0e8c214db | |
parent | c6b8854fb58cf1e5fa3dff3d30337a3e0711911e (diff) | |
parent | 2bf390d33eae05a6a8c44970852b15760d169103 (diff) | |
download | kernel_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.h | 118 | ||||
-rw-r--r-- | drivers/media/video/tiler/Kconfig | 33 | ||||
-rw-r--r-- | drivers/media/video/tiler/Makefile | 8 | ||||
-rw-r--r-- | drivers/media/video/tiler/_tiler.h | 35 | ||||
-rw-r--r-- | drivers/media/video/tiler/tiler-iface.c | 541 | ||||
-rw-r--r-- | drivers/media/video/tiler/tiler-ioctl.c | 525 | ||||
-rw-r--r-- | drivers/media/video/tiler/tiler-main.c | 294 | ||||
-rw-r--r-- | drivers/media/video/tiler/tiler-nv12.c | 417 | ||||
-rw-r--r-- | drivers/media/video/tiler/tiler-reserve.c | 408 | ||||
-rw-r--r-- | drivers/media/video/tiler/tmm-pat.c | 35 | ||||
-rw-r--r-- | drivers/media/video/tiler/tmm.h | 22 | ||||
-rw-r--r-- | drivers/rpmsg/Kconfig | 1 |
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(¬ifier, 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(¬ifier); } /* @@ -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(¬ifier, nb); -} -EXPORT_SYMBOL(tiler_reg_notifier); - -s32 tiler_unreg_notifier(struct notifier_block *nb) -{ - if (!nb) - return -EINVAL; - return blocking_notifier_chain_unregister(¬ifier, 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(¬ifier, 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(¬ifier); +} + + +s32 tiler_reg_notifier(struct notifier_block *nb) +{ + if (!nb) + return -EINVAL; + return blocking_notifier_chain_register(¬ifier, nb); +} +EXPORT_SYMBOL(tiler_reg_notifier); + +s32 tiler_unreg_notifier(struct notifier_block *nb) +{ + if (!nb) + return -EINVAL; + return blocking_notifier_chain_unregister(¬ifier, 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 |