[FFmpeg-devel] [PATCH] libavcodec/cedrus264: h264 hardware encoding for Allwinner H2/H3
daniel.kucera at gmail.com
daniel.kucera at gmail.com
Sat May 26 16:51:17 EEST 2018
From: Daniel Kucera <daniel.kucera at gmail.com>
Signed-off-by: Daniel Kucera <daniel.kucera at gmail.com>
---
libavcodec/Makefile | 1 +
libavcodec/allcodecs.c | 1 +
libavcodec/arm/sunxi/cedar_ve.h | 79 ++++++
libavcodec/arm/sunxi/ion.h | 377 ++++++++++++++++++++++++++
libavcodec/arm/sunxi/ion_sunxi.h | 108 ++++++++
libavcodec/arm/sunxi/ve.c | 552 +++++++++++++++++++++++++++++++++++++++
libavcodec/arm/sunxi/ve.h | 193 ++++++++++++++
libavcodec/cedrus264.c | 402 ++++++++++++++++++++++++++++
8 files changed, 1713 insertions(+)
create mode 100644 libavcodec/arm/sunxi/cedar_ve.h
create mode 100644 libavcodec/arm/sunxi/ion.h
create mode 100644 libavcodec/arm/sunxi/ion_sunxi.h
create mode 100755 libavcodec/arm/sunxi/ve.c
create mode 100755 libavcodec/arm/sunxi/ve.h
create mode 100755 libavcodec/cedrus264.c
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 3ab071a..f3821af 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -987,6 +987,7 @@ OBJS-$(CONFIG_LIBX265_ENCODER) += libx265.o
OBJS-$(CONFIG_LIBXAVS_ENCODER) += libxavs.o
OBJS-$(CONFIG_LIBXVID_ENCODER) += libxvid.o
OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER) += libzvbi-teletextdec.o ass.o
+OBJS-$(CONFIG_CEDRUS264_ENCODER) += cedrus264.o arm/sunxi/ve.o
# parsers
OBJS-$(CONFIG_AAC_LATM_PARSER) += latm_parser.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 7b7a8c7..a153576 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -66,6 +66,7 @@ extern AVCodec ff_c93_decoder;
extern AVCodec ff_cavs_decoder;
extern AVCodec ff_cdgraphics_decoder;
extern AVCodec ff_cdxl_decoder;
+extern AVCodec ff_cedrus264_encoder;
extern AVCodec ff_cfhd_decoder;
extern AVCodec ff_cinepak_encoder;
extern AVCodec ff_cinepak_decoder;
diff --git a/libavcodec/arm/sunxi/cedar_ve.h b/libavcodec/arm/sunxi/cedar_ve.h
new file mode 100644
index 0000000..92faf59
--- /dev/null
+++ b/libavcodec/arm/sunxi/cedar_ve.h
@@ -0,0 +1,79 @@
+#ifndef _CEDAR_VE_H_
+#define _CEDAR_VE_H_
+
+enum IOCTL_CMD {
+ IOCTL_UNKOWN = 0x100,
+ IOCTL_GET_ENV_INFO,
+ IOCTL_WAIT_VE_DE,
+ IOCTL_WAIT_VE_EN,
+ IOCTL_RESET_VE,
+ IOCTL_ENABLE_VE,
+ IOCTL_DISABLE_VE,
+ IOCTL_SET_VE_FREQ,
+
+ IOCTL_CONFIG_AVS2 = 0x200,
+ IOCTL_GETVALUE_AVS2 ,
+ IOCTL_PAUSE_AVS2 ,
+ IOCTL_START_AVS2 ,
+ IOCTL_RESET_AVS2 ,
+ IOCTL_ADJUST_AVS2,
+ IOCTL_ENGINE_REQ,
+ IOCTL_ENGINE_REL,
+ IOCTL_ENGINE_CHECK_DELAY,
+ IOCTL_GET_IC_VER,
+ IOCTL_ADJUST_AVS2_ABS,
+ IOCTL_FLUSH_CACHE,
+ IOCTL_SET_REFCOUNT,
+
+ IOCTL_READ_REG = 0x300,
+ IOCTL_WRITE_REG,
+
+ IOCTL_SET_VOL = 0x400,
+
+#if defined CONFIG_ARCH_SUN8IW8P1
+ IOCTL_WAIT_JPEG_DEC = 0x500,
+#endif
+};
+
+struct cedarv_env_infomation{
+ unsigned int phymem_start;
+ int phymem_total_size;
+ unsigned int address_macc;
+};
+
+struct cedarv_cache_range{
+ long start;
+ long end;
+};
+
+/*struct __cedarv_task {
+ int task_prio;
+ int ID;
+ unsigned long timeout;
+ unsigned int frametime;
+ unsigned int block_mode;
+};
+
+struct cedarv_engine_task {
+ struct __cedarv_task t;
+ struct list_head list;
+ struct task_struct *task_handle;
+ unsigned int status;
+ unsigned int running;
+ unsigned int is_first_task;
+};
+
+struct cedarv_engine_task_info {
+ int task_prio;
+ unsigned int frametime;
+ unsigned int total_time;
+};*/
+
+struct cedarv_regop {
+ unsigned int addr;
+ unsigned int value;
+};
+/*--------------------------------------------------------------------------------*/
+
+
+#endif
diff --git a/libavcodec/arm/sunxi/ion.h b/libavcodec/arm/sunxi/ion.h
new file mode 100644
index 0000000..e777c69
--- /dev/null
+++ b/libavcodec/arm/sunxi/ion.h
@@ -0,0 +1,377 @@
+/*
+ * include/linux/ion.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_ION_H
+#define _LINUX_ION_H
+
+#include <stdlib.h>
+#include <linux/types.h>
+
+struct ion_handle;
+/**
+ * enum ion_heap_types - list of all possible types of heaps
+ * @ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc
+ * @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc
+ * @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved
+ * carveout heap, allocations are physically
+ * contiguous
+ * @ION_HEAP_TYPE_DMA: memory allocated via DMA API
+ * @ION_NUM_HEAPS: helper for iterating over heaps, a bit mask
+ * is used to identify the heaps, so only 32
+ * total heap types are supported
+ */
+enum ion_heap_type {
+ ION_HEAP_TYPE_SYSTEM,
+ ION_HEAP_TYPE_SYSTEM_CONTIG,
+ ION_HEAP_TYPE_CARVEOUT,
+ ION_HEAP_TYPE_CHUNK,
+ ION_HEAP_TYPE_DMA,
+ ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always
+ are at the end of this enum */
+ ION_NUM_HEAPS = 16,
+};
+
+#define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM)
+#define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG)
+#define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT)
+#define ION_HEAP_TYPE_DMA_MASK (1 << ION_HEAP_TYPE_DMA)
+
+#define ION_NUM_HEAP_IDS sizeof(unsigned int) * 8
+
+/**
+ * allocation flags - the lower 16 bits are used by core ion, the upper 16
+ * bits are reserved for use by the heaps themselves.
+ */
+#define ION_FLAG_CACHED 1 /* mappings of this buffer should be
+ cached, ion will do cache
+ maintenance when the buffer is
+ mapped for dma */
+#define ION_FLAG_CACHED_NEEDS_SYNC 2 /* mappings of this buffer will created
+ at mmap time, if this is set
+ caches must be managed manually */
+
+#ifdef __KERNEL__
+struct ion_device;
+struct ion_heap;
+struct ion_mapper;
+struct ion_client;
+struct ion_buffer;
+
+/* This should be removed some day when phys_addr_t's are fully
+ plumbed in the kernel, and all instances of ion_phys_addr_t should
+ be converted to phys_addr_t. For the time being many kernel interfaces
+ do not accept phys_addr_t's that would have to */
+#define ion_phys_addr_t unsigned long
+
+/**
+ * struct ion_platform_heap - defines a heap in the given platform
+ * @type: type of the heap from ion_heap_type enum
+ * @id: unique identifier for heap. When allocating higher numbers
+ * will be allocated from first. At allocation these are passed
+ * as a bit mask and therefore can not exceed ION_NUM_HEAP_IDS.
+ * @name: used for debug purposes
+ * @base: base address of heap in physical memory if applicable
+ * @size: size of the heap in bytes if applicable
+ * @align: required alignment in physical memory if applicable
+ * @priv: private info passed from the board file
+ *
+ * Provided by the board file.
+ */
+struct ion_platform_heap {
+ enum ion_heap_type type;
+ unsigned int id;
+ const char *name;
+ ion_phys_addr_t base;
+ size_t size;
+ ion_phys_addr_t align;
+ void *priv;
+};
+
+/**
+ * struct ion_platform_data - array of platform heaps passed from board file
+ * @nr: number of structures in the array
+ * @heaps: array of platform_heap structions
+ *
+ * Provided by the board file in the form of platform data to a platform device.
+ */
+struct ion_platform_data {
+ int nr;
+ struct ion_platform_heap heaps[];
+};
+
+/**
+ * ion_reserve() - reserve memory for ion heaps if applicable
+ * @data: platform data specifying starting physical address and
+ * size
+ *
+ * Calls memblock reserve to set aside memory for heaps that are
+ * located at specific memory addresses or of specfic sizes not
+ * managed by the kernel
+ */
+void ion_reserve(struct ion_platform_data *data);
+
+/**
+ * ion_client_create() - allocate a client and returns it
+ * @dev: the global ion device
+ * @heap_type_mask: mask of heaps this client can allocate from
+ * @name: used for debugging
+ */
+struct ion_client *ion_client_create(struct ion_device *dev,
+ const char *name);
+
+/**
+ * ion_client_destroy() - free's a client and all it's handles
+ * @client: the client
+ *
+ * Free the provided client and all it's resources including
+ * any handles it is holding.
+ */
+void ion_client_destroy(struct ion_client *client);
+
+/**
+ * ion_alloc - allocate ion memory
+ * @client: the client
+ * @len: size of the allocation
+ * @align: requested allocation alignment, lots of hardware blocks
+ * have alignment requirements of some kind
+ * @heap_id_mask: mask of heaps to allocate from, if multiple bits are set
+ * heaps will be tried in order from highest to lowest
+ * id
+ * @flags: heap flags, the low 16 bits are consumed by ion, the
+ * high 16 bits are passed on to the respective heap and
+ * can be heap custom
+ *
+ * Allocate memory in one of the heaps provided in heap mask and return
+ * an opaque handle to it.
+ */
+struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
+ size_t align, unsigned int heap_id_mask,
+ unsigned int flags);
+
+/**
+ * ion_free - free a handle
+ * @client: the client
+ * @handle: the handle to free
+ *
+ * Free the provided handle.
+ */
+void ion_free(struct ion_client *client, struct ion_handle *handle);
+
+/**
+ * ion_phys - returns the physical address and len of a handle
+ * @client: the client
+ * @handle: the handle
+ * @addr: a pointer to put the address in
+ * @len: a pointer to put the length in
+ *
+ * This function queries the heap for a particular handle to get the
+ * handle's physical address. It't output is only correct if
+ * a heap returns physically contiguous memory -- in other cases
+ * this api should not be implemented -- ion_sg_table should be used
+ * instead. Returns -EINVAL if the handle is invalid. This has
+ * no implications on the reference counting of the handle --
+ * the returned value may not be valid if the caller is not
+ * holding a reference.
+ */
+int ion_phys(struct ion_client *client, struct ion_handle *handle,
+ ion_phys_addr_t *addr, size_t *len);
+
+/**
+ * ion_map_dma - return an sg_table describing a handle
+ * @client: the client
+ * @handle: the handle
+ *
+ * This function returns the sg_table describing
+ * a particular ion handle.
+ */
+struct sg_table *ion_sg_table(struct ion_client *client,
+ struct ion_handle *handle);
+
+/**
+ * ion_map_kernel - create mapping for the given handle
+ * @client: the client
+ * @handle: handle to map
+ *
+ * Map the given handle into the kernel and return a kernel address that
+ * can be used to access this address.
+ */
+void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle);
+
+/**
+ * ion_unmap_kernel() - destroy a kernel mapping for a handle
+ * @client: the client
+ * @handle: handle to unmap
+ */
+void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle);
+
+/**
+ * ion_share_dma_buf() - share buffer as dma-buf
+ * @client: the client
+ * @handle: the handle
+ */
+struct dma_buf *ion_share_dma_buf(struct ion_client *client,
+ struct ion_handle *handle);
+
+/**
+ * ion_share_dma_buf_fd() - given an ion client, create a dma-buf fd
+ * @client: the client
+ * @handle: the handle
+ */
+int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle);
+
+/**
+ * ion_import_dma_buf() - given an dma-buf fd from the ion exporter get handle
+ * @client: the client
+ * @fd: the dma-buf fd
+ *
+ * Given an dma-buf fd that was allocated through ion via ion_share_dma_buf,
+ * import that fd and return a handle representing it. If a dma-buf from
+ * another exporter is passed in this function will return ERR_PTR(-EINVAL)
+ */
+struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd);
+
+#endif /* __KERNEL__ */
+
+/**
+ * DOC: Ion Userspace API
+ *
+ * create a client by opening /dev/ion
+ * most operations handled via following ioctls
+ *
+ */
+
+/**
+ * struct ion_allocation_data - metadata passed from userspace for allocations
+ * @len: size of the allocation
+ * @align: required alignment of the allocation
+ * @heap_id_mask: mask of heap ids to allocate from
+ * @flags: flags passed to heap
+ * @handle: pointer that will be populated with a cookie to use to
+ * refer to this allocation
+ *
+ * Provided by userspace as an argument to the ioctl
+ */
+struct ion_allocation_data {
+ size_t len;
+ size_t align;
+ unsigned int heap_id_mask;
+ unsigned int flags;
+ struct ion_handle *handle;
+};
+
+/**
+ * struct ion_fd_data - metadata passed to/from userspace for a handle/fd pair
+ * @handle: a handle
+ * @fd: a file descriptor representing that handle
+ *
+ * For ION_IOC_SHARE or ION_IOC_MAP userspace populates the handle field with
+ * the handle returned from ion alloc, and the kernel returns the file
+ * descriptor to share or map in the fd field. For ION_IOC_IMPORT, userspace
+ * provides the file descriptor and the kernel returns the handle.
+ */
+struct ion_fd_data {
+ struct ion_handle *handle;
+ int fd;
+};
+
+/**
+ * struct ion_handle_data - a handle passed to/from the kernel
+ * @handle: a handle
+ */
+struct ion_handle_data {
+ struct ion_handle *handle;
+};
+
+/**
+ * struct ion_custom_data - metadata passed to/from userspace for a custom ioctl
+ * @cmd: the custom ioctl function to call
+ * @arg: additional data to pass to the custom ioctl, typically a user
+ * pointer to a predefined structure
+ *
+ * This works just like the regular cmd and arg fields of an ioctl.
+ */
+struct ion_custom_data {
+ unsigned int cmd;
+ unsigned long arg;
+};
+
+#define ION_IOC_MAGIC 'I'
+
+/**
+ * DOC: ION_IOC_ALLOC - allocate memory
+ *
+ * Takes an ion_allocation_data struct and returns it with the handle field
+ * populated with the opaque handle for the allocation.
+ */
+#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \
+ struct ion_allocation_data)
+
+/**
+ * DOC: ION_IOC_FREE - free memory
+ *
+ * Takes an ion_handle_data struct and frees the handle.
+ */
+#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
+
+/**
+ * DOC: ION_IOC_MAP - get a file descriptor to mmap
+ *
+ * Takes an ion_fd_data struct with the handle field populated with a valid
+ * opaque handle. Returns the struct with the fd field set to a file
+ * descriptor open in the current address space. This file descriptor
+ * can then be used as an argument to mmap.
+ */
+#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation
+ *
+ * Takes an ion_fd_data struct with the handle field populated with a valid
+ * opaque handle. Returns the struct with the fd field set to a file
+ * descriptor open in the current address space. This file descriptor
+ * can then be passed to another process. The corresponding opaque handle can
+ * be retrieved via ION_IOC_IMPORT.
+ */
+#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_IMPORT - imports a shared file descriptor
+ *
+ * Takes an ion_fd_data struct with the fd field populated with a valid file
+ * descriptor obtained from ION_IOC_SHARE and returns the struct with the handle
+ * filed set to the corresponding opaque handle.
+ */
+#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_SYNC - syncs a shared file descriptors to memory
+ *
+ * Deprecated in favor of using the dma_buf api's correctly (syncing
+ * will happend automatically when the buffer is mapped to a device).
+ * If necessary should be used after touching a cached buffer from the cpu,
+ * this will make the buffer in memory coherent.
+ */
+#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_CUSTOM - call architecture specific ion ioctl
+ *
+ * Takes the argument of the architecture specific ioctl to call and
+ * passes appropriate userdata for that ioctl
+ */
+#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data)
+
+#endif /* _LINUX_ION_H */
diff --git a/libavcodec/arm/sunxi/ion_sunxi.h b/libavcodec/arm/sunxi/ion_sunxi.h
new file mode 100644
index 0000000..d7468ae
--- /dev/null
+++ b/libavcodec/arm/sunxi/ion_sunxi.h
@@ -0,0 +1,108 @@
+/*
+ * include/linux/ion_sunxi.h
+ *
+ * Copyright(c) 2013-2015 Allwinnertech Co., Ltd.
+ * http://www.allwinnertech.com
+ *
+ * Author: liugang <liugang at allwinnertech.com>
+ *
+ * sunxi ion header file
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __ION_SUNXI_H
+#define __ION_SUNXI_H
+
+#define ION_HEAP_TYPE_SUNXI_START (ION_HEAP_TYPE_CUSTOM + 1)
+#define ION_HEAP_TYPE_SECURE (ION_HEAP_TYPE_SUNXI_START)
+
+typedef struct {
+ long start;
+ long end;
+}sunxi_cache_range;
+
+typedef struct {
+ void *handle;
+ unsigned long phys_addr;
+ unsigned long size;
+}sunxi_phys_data;
+
+#define DMA_BUF_MAXCNT 8
+
+typedef struct {
+ unsigned int src_va;
+ unsigned int src_pa;
+ unsigned int dst_va;
+ unsigned int dst_pa;
+ unsigned int size;
+}dma_buf_item;
+
+typedef struct {
+ int multi_dma;
+ unsigned int cnt;
+ dma_buf_item item[DMA_BUF_MAXCNT];
+}dma_buf_group;
+
+#define ION_IOC_SUNXI_FLUSH_RANGE 5
+#define ION_IOC_SUNXI_FLUSH_ALL 6
+#define ION_IOC_SUNXI_PHYS_ADDR 7
+#define ION_IOC_SUNXI_DMA_COPY 8
+#define ION_IOC_SUNXI_DUMP 9
+
+#ifdef __KERNEL__
+
+int flush_clean_user_range(long start, long end);
+int flush_user_range(long start, long end);
+void flush_dcache_all(void);
+
+/**
+ * sunxi_buf_alloc - alloc phys contigous memory in SUNXI platform.
+ * @size: size in bytes to allocate.
+ * @paddr: store the start phys address allocated.
+ *
+ * return the start virtual address, or 0 if failed.
+ */
+void *sunxi_buf_alloc(unsigned int size, unsigned int *paddr);
+/**
+ * sunxi_buf_free - free buffer allocated by sunxi_buf_alloc.
+ * @vaddr: the kernel virt addr of the area.
+ * @paddr: the start phys addr of the area.
+ * @size: size in bytes of the area.
+ */
+void sunxi_buf_free(void *vaddr, unsigned int paddr, unsigned int size);
+/**
+ * sunxi_alloc_phys - alloc phys contigous memory in SUNXI platform.
+ * @size: size in bytes to allocate.
+ *
+ * return the start phys addr, or 0 if failed.
+ */
+u32 sunxi_alloc_phys(size_t size);
+/**
+ * sunxi_free_phys - free phys contigous memory allocted by sunxi_alloc_phys.
+ * @paddr: the start phys addr of the area.
+ * @size: size in bytes of the area.
+ */
+void sunxi_free_phys(u32 paddr, size_t size);
+/**
+ * sunxi_map_kernel - map phys contigous memory to kernel virtual space.
+ * @paddr: the start phys addr of the area.
+ * @size: size in bytes of the area.
+ *
+ * return the start virt addr which is in vmalloc space, or NULL if failed.
+ */
+void *sunxi_map_kernel(unsigned int paddr, unsigned int size);
+/**
+ * sunxi_unmap_kernel - unmap phys contigous memory from kernel space.
+ * @vaddr: the kernel virt addr of the area.
+ * @paddr: the start phys addr of the area.
+ * @size: size in bytes of the area.
+ */
+void sunxi_unmap_kernel(void *vaddr, unsigned int paddr, unsigned int size);
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/libavcodec/arm/sunxi/ve.c b/libavcodec/arm/sunxi/ve.c
new file mode 100755
index 0000000..b44b17e
--- /dev/null
+++ b/libavcodec/arm/sunxi/ve.c
@@ -0,0 +1,552 @@
+/*
+ * Copyright (c) 2013-2014 Jens Kuske <jenskuske at gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/file.h>
+#include "ve.h"
+#include "ion.h"
+#include "ion_sunxi.h"
+#include "cedar_ve.h"
+
+#define LOCKFILE "/tmp/cedar_dev.lck"
+#define DEVICE "/dev/cedar_dev"
+#define PAGE_OFFSET (0xc0000000) // from kernel
+#define PAGE_SIZE (4096)
+
+#define typeof __typeof__
+
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+static int fd = -1, lockfd = -1;
+//void memlist_add(struct ve_mem *mem);
+//struct mem_list *memlist_find(struct ve_mem *mem);
+//int memlist_del(struct ve_mem *mem);
+//void memlist_del_all();
+
+struct memchunk_t
+{
+ struct ve_mem mem;
+ struct memchunk_t *next;
+};
+
+struct ion_mem
+{
+ struct ion_handle *handle;
+ int fd;
+ struct ve_mem mem;
+};
+
+static struct
+{
+ int fd;
+ int ion_fd;
+ void *regs;
+ int version;
+ struct memchunk_t first_memchunk;
+ pthread_rwlock_t memory_lock;
+ pthread_mutex_t device_lock;
+} ve = { .fd = -1, .ion_fd = -1, .memory_lock = PTHREAD_RWLOCK_INITIALIZER, .device_lock = PTHREAD_MUTEX_INITIALIZER };
+
+
+struct mem_list {
+ struct ve_mem *mem;
+ struct mem_list *next;
+} *memlist = NULL;
+
+static void memlist_add(struct ve_mem *mem) {
+ struct mem_list *m, *k;
+ if (memlist == NULL) {
+ memlist = (struct mem_list *)malloc(sizeof (struct mem_list));
+ memlist->mem = mem;
+ memlist->next = NULL;
+ return;
+ }
+ m = k = memlist;
+ while (m) {
+ k = m;
+ m = m->next;
+ }
+ m = (struct mem_list *)malloc(sizeof (struct mem_list));
+ m->mem = mem;
+ m->next = NULL;
+ k->next = m;
+}
+
+static struct mem_list *memlist_find(struct ve_mem *mem) {
+ struct mem_list *m = memlist;
+
+ while (m) {
+ if (m->mem == mem) return m;
+ m = m->next;
+ }
+ return NULL;
+}
+
+static int memlist_del(struct ve_mem *mem) {
+ struct mem_list *m = memlist;
+ struct mem_list *prev = m;
+
+ while (m) {
+ if (m->mem == mem) {
+ prev->next = m->next;
+ free(m);
+ return 0;
+ }
+ prev = m;
+ m = m->next;
+ }
+ return -1;
+}
+
+static void memlist_del_all(void) {
+ struct mem_list *m = memlist;
+
+ while (m) {
+ struct mem_list *k = m;
+ m = m->next;
+ free(k);
+ }
+ memlist = NULL;
+}
+
+int ve_open(void)
+{
+ if (ve.fd != -1)
+ return 0;
+
+ struct cedarv_env_infomation info;
+
+ ve.fd = open(DEVICE, O_RDWR);
+ if (ve.fd == -1)
+ return 0;
+
+ if (ioctl(ve.fd, IOCTL_GET_ENV_INFO, (void *)(&info)) == -1)
+ goto close;
+
+ ve.regs = mmap(NULL, 0x800, PROT_READ | PROT_WRITE, MAP_SHARED, ve.fd, info.address_macc);
+ if (ve.regs == MAP_FAILED)
+ goto close;
+
+ ve.first_memchunk.mem.phys = info.phymem_start - PAGE_OFFSET;
+ ve.first_memchunk.mem.size = info.phymem_total_size;
+
+ if (ve.first_memchunk.mem.size == 0)
+ {
+ ve.ion_fd = open("/dev/ion", O_RDONLY);
+ if (ve.ion_fd == -1)
+ goto unmap;
+ }
+
+ ioctl(ve.fd, IOCTL_ENGINE_REQ, 0);
+ ioctl(ve.fd, IOCTL_ENABLE_VE, 0);
+ ioctl(ve.fd, IOCTL_SET_VE_FREQ, 320);
+ ioctl(ve.fd, IOCTL_RESET_VE, 0);
+
+ writel(0x00130007, ve.regs + VE_CTRL);
+
+ ve.version = readl(ve.regs + VE_VERSION) >> 16;
+ printf("[VDPAU SUNXI] VE version 0x%04x opened.\n", ve.version);
+
+ return 1;
+
+unmap:
+ munmap(ve.regs, 0x800);
+close:
+ close(ve.fd);
+ ve.fd = -1;
+ return 0;
+}
+
+int ve_lock(void) {
+ /* We must lock another file than /dev/cedar_dev,
+ * because opening the device already do problems.
+ * (If device is opened, it will be closed at program exit and ve
+ * interrupt will be disabled, also if anohter process was using it) */
+ if(lockfd == -1) lockfd = open(LOCKFILE, O_CREAT | O_RDWR, 0666);
+ if(lockfd == -1) return 0;
+ if(flock(lockfd, LOCK_EX | LOCK_NB) < 0) return 0;
+ return 1;
+}
+
+void ve_unlock(void) {
+ if (lockfd == -1) return;
+ flock(lockfd, LOCK_UN);
+ close(lockfd);
+ lockfd = -1;
+ /* Don't try to unlink file, it causes race conditions. */
+}
+
+void ve_close(void)
+{
+ if (ve.fd == -1)
+ return;
+
+ ioctl(ve.fd, IOCTL_DISABLE_VE, 0);
+ ioctl(ve.fd, IOCTL_ENGINE_REL, 0);
+
+ munmap(ve.regs, 0x800);
+ ve.regs = NULL;
+
+ if (ve.ion_fd != -1)
+ close(ve.ion_fd);
+
+ close(ve.fd);
+ ve.fd = -1;
+}
+
+int ve_get_version(void)
+{
+ return ve.version;
+}
+
+int ve_wait(int timeout)
+{
+ if (ve.fd == -1)
+ return 0;
+ if (ve_get_version() >= 0x1633)
+ return ioctl(ve.fd, IOCTL_WAIT_VE_EN, timeout);
+ else
+ return ioctl(ve.fd, IOCTL_WAIT_VE_DE, timeout);
+}
+
+void *ve_get(int engine, uint32_t flags)
+{
+ if (pthread_mutex_lock(&ve.device_lock))
+ return NULL;
+ if (ve_get_version() >= 0x1633)
+ writel(0x001300C0 | (engine & 0xf) | (flags & ~0xf), ve.regs + VE_CTRL);
+ else
+ writel(0x00130000 | (engine & 0xf) | (flags & ~0xf), ve.regs + VE_CTRL);
+
+ return ve.regs;
+}
+
+void ve_put(void)
+{
+ writel(0x00130007, ve.regs + VE_CTRL);
+ pthread_mutex_unlock(&ve.device_lock);
+}
+
+static struct ve_mem *ion_malloc(int size)
+{
+ struct ion_mem *imem = calloc(1, sizeof(struct ion_mem));
+ if (!imem)
+ {
+ perror("calloc ion_buffer failed");
+ return NULL;
+ }
+
+ struct ion_allocation_data alloc = {
+ .len = size,
+ .align = 4096,
+ .heap_id_mask = ION_HEAP_TYPE_DMA,
+ .flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC,
+ };
+
+ if (ioctl(ve.ion_fd, ION_IOC_ALLOC, &alloc))
+ {
+ perror("ION_IOC_ALLOC failed");
+ free(imem);
+ return NULL;
+ }
+
+ imem->handle = alloc.handle;
+ imem->mem.size = size;
+
+ struct ion_fd_data map = {
+ .handle = imem->handle,
+ };
+
+ if (ioctl(ve.ion_fd, ION_IOC_MAP, &map))
+ {
+ perror("ION_IOC_MAP failed");
+ free(imem);
+ return NULL;
+ }
+
+ imem->fd = map.fd;
+
+ imem->mem.virt = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, imem->fd, 0);
+ if (imem->mem.virt == MAP_FAILED)
+ {
+ perror("mmap failed");
+ return NULL;
+ }
+
+ sunxi_phys_data phys = {
+ .handle = imem->handle,
+ };
+
+ struct ion_custom_data custom = {
+ .cmd = ION_IOC_SUNXI_PHYS_ADDR,
+ .arg = (unsigned long)(&phys),
+ };
+
+ if (ioctl(ve.ion_fd, ION_IOC_CUSTOM, &custom))
+ {
+ perror("ION_IOC_CUSTOM(SUNXI_PHYS_ADDR) failed");
+ free(imem);
+ return NULL;
+ }
+
+ imem->mem.phys = phys.phys_addr - 0x40000000;
+
+ memlist_add(&imem->mem);
+
+ return &imem->mem;
+}
+
+struct ve_mem *ve_malloc(int size)
+{
+ if (ve.fd == -1)
+ return NULL;
+
+ if (ve.ion_fd != -1)
+ return ion_malloc(size);
+
+ if (pthread_rwlock_wrlock(&ve.memory_lock))
+ return NULL;
+
+ void *addr = NULL;
+ struct ve_mem *ret = NULL;
+
+ size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+ struct memchunk_t *c, *best_chunk = NULL;
+ for (c = &ve.first_memchunk; c != NULL; c = c->next)
+ {
+ if(c->mem.virt == NULL && c->mem.size >= size)
+ {
+ if (best_chunk == NULL || c->mem.size < best_chunk->mem.size)
+ best_chunk = c;
+
+ if (c->mem.size == size)
+ break;
+ }
+ }
+
+ if (!best_chunk)
+ goto out;
+
+ int left_size = best_chunk->mem.size - size;
+
+ addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ve.fd, best_chunk->mem.phys + PAGE_OFFSET);
+ if (addr == MAP_FAILED)
+ {
+ ret = NULL;
+ goto out;
+ }
+
+ best_chunk->mem.virt = addr;
+ best_chunk->mem.size = size;
+
+ if (left_size > 0)
+ {
+ c = malloc(sizeof(struct memchunk_t));
+ c->mem.phys = best_chunk->mem.phys + size;
+ c->mem.size = left_size;
+ c->mem.virt = NULL;
+ c->next = best_chunk->next;
+ best_chunk->next = c;
+ }
+
+ ret = &best_chunk->mem;
+out:
+ pthread_rwlock_unlock(&ve.memory_lock);
+ return ret;
+}
+
+static void ion_free(struct ve_mem *mem)
+{
+ if (ve.ion_fd == -1 || !mem)
+ return;
+
+ struct ion_mem *imem = container_of(mem, struct ion_mem, mem);
+
+ if (munmap(mem->virt, mem->size))
+ {
+ perror("munmap failed");
+ return;
+ }
+
+ memlist_del(mem);
+
+ close(imem->fd);
+
+ struct ion_handle_data handle = {
+ .handle = imem->handle,
+ };
+
+ if (ioctl(ve.ion_fd, ION_IOC_FREE, &handle))
+ {
+ perror("ION_IOC_FREE failed");
+ free(imem);
+ return;
+ }
+}
+
+void ve_free(struct ve_mem *mem)
+{
+ if (ve.fd == -1)
+ return;
+
+ if (mem == NULL)
+ return;
+
+ if (ve.ion_fd != -1)
+ ion_free(mem);
+
+ if (pthread_rwlock_wrlock(&ve.memory_lock))
+ return;
+
+ struct memchunk_t *c;
+ for (c = &ve.first_memchunk; c != NULL; c = c->next)
+ {
+ if (&c->mem == mem)
+ {
+ munmap(c->mem.virt, c->mem.size);
+ c->mem.virt = NULL;
+ break;
+ }
+ }
+
+ for (c = &ve.first_memchunk; c != NULL; c = c->next)
+ {
+ if (c->mem.virt == NULL)
+ {
+ while (c->next != NULL && c->next->mem.virt == NULL)
+ {
+ struct memchunk_t *n = c->next;
+ c->mem.size += n->mem.size;
+ c->next = n->next;
+ free(n);
+ }
+ }
+ }
+
+ pthread_rwlock_unlock(&ve.memory_lock);
+}
+
+uint32_t ve_virt2phys(void *ptr)
+{
+ uint32_t addr = 0;
+
+ if (ve.fd == -1)
+ return 0;
+
+ if (ve.ion_fd != -1) {
+
+ struct mem_list *m = memlist;
+
+ while (m) {
+ struct ve_mem *mem = m->mem;
+ if (!mem) {
+ m = m->next;
+ continue;
+ }
+
+ //printf("c->mem: virt 0x%08X, phys 0x%08X, ptr 0x%08X\n", (unsigned int)mem->virt, mem->phys, (unsigned int)ptr);
+ if (mem->virt == NULL)
+ continue;
+
+ if (mem->virt == ptr)
+ {
+ addr = mem->phys;
+ break;
+ }
+ else if (ptr > mem->virt && ptr < (mem->virt + mem->size))
+ {
+ addr = mem->phys + (ptr - mem->virt);
+ break;
+ }
+ m = m->next;
+ }
+ return addr;
+ }
+
+
+ //if (pthread_rwlock_rdlock(&ve.memory_lock))
+ // return 0;
+
+
+
+ struct memchunk_t *c;
+ for (c = &ve.first_memchunk; c != NULL; c = c->next)
+ {
+ printf("c->mem: virt 0x%08X, phys 0x%08X, ptr 0x%08X\n", (unsigned int)c->mem.virt, c->mem.phys, (unsigned int)ptr);
+ if (c->mem.virt == NULL)
+ continue;
+
+ if (c->mem.virt == ptr)
+ {
+ addr = c->mem.phys;
+ break;
+ }
+ else if (ptr > c->mem.virt && ptr < (c->mem.virt + c->mem.size))
+ {
+ addr = c->mem.phys + (ptr - c->mem.virt);
+ break;
+ }
+ }
+
+ //pthread_rwlock_unlock(&ve.memory_lock);
+ return addr;
+}
+
+
+void ve_flush_cache(struct ve_mem *mem)
+{
+ if (ve.fd == -1)
+ return;
+
+ if (ve.ion_fd != -1)
+ {
+ sunxi_cache_range range = {
+ .start = (long)mem->virt,
+ .end = (long)mem->virt + mem->size,
+ };
+
+ struct ion_custom_data cache = {
+ .cmd = ION_IOC_SUNXI_FLUSH_RANGE,
+ .arg = (unsigned long)(&range),
+ };
+
+ if (ioctl(ve.ion_fd, ION_IOC_CUSTOM, &cache))
+ perror("ION_IOC_CUSTOM(SUNXI_FLUSH_RANGE) failed");
+ }
+ else
+ {
+ struct cedarv_cache_range range =
+ {
+ .start = (int)mem->virt,
+ .end = (int)mem->virt + mem->size
+ };
+
+ ioctl(ve.fd, IOCTL_FLUSH_CACHE, (void*)(&range));
+ }
+}
diff --git a/libavcodec/arm/sunxi/ve.h b/libavcodec/arm/sunxi/ve.h
new file mode 100755
index 0000000..c5dcc72
--- /dev/null
+++ b/libavcodec/arm/sunxi/ve.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2013 Jens Kuske <jenskuske at gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __VE_H__
+#define __VE_H__
+
+#include <stdint.h>
+
+int ve_lock(void);
+void ve_unlock(void);
+int ve_open(void);
+void ve_close(void);
+int ve_get_version(void);
+int ve_wait(int timeout);
+void *ve_get(int engine, uint32_t flags);
+void ve_put(void);
+
+struct ve_mem
+{
+ void *virt;
+ uint32_t phys;
+ int size;
+};
+
+struct ve_mem *ve_malloc(int size);
+void ve_free(struct ve_mem *mem);
+void ve_flush_cache(struct ve_mem *mem);
+uint32_t ve_virt2phys(void *ptr);
+
+static inline void writeb(uint8_t val, void *addr)
+{
+ *((volatile uint8_t *)addr) = val;
+}
+
+static inline void writel(uint32_t val, void *addr)
+{
+ *((volatile uint32_t *)addr) = val;
+}
+
+static inline uint32_t readl(void *addr)
+{
+ return *((volatile uint32_t *) addr);
+}
+
+#define VE_ENGINE_MPEG 0x0
+#define VE_ENGINE_H264 0x1
+#define VE_ENGINE_HEVC 0x4
+#define VE_ENGINE_AVC 0xb
+
+#define VE_CTRL 0x000
+#define VE_EXTRA_OUT_FMT_OFFSET 0x0e8
+#define VE_VERSION 0x0f0
+
+#define VE_MPEG_PIC_HDR 0x100
+#define VE_MPEG_VOP_HDR 0x104
+#define VE_MPEG_SIZE 0x108
+#define VE_MPEG_FRAME_SIZE 0x10c
+#define VE_MPEG_MBA 0x110
+#define VE_MPEG_CTRL 0x114
+#define VE_MPEG_TRIGGER 0x118
+#define VE_MPEG_STATUS 0x11c
+#define VE_MPEG_TRBTRD_FIELD 0x120
+#define VE_MPEG_TRBTRD_FRAME 0x124
+#define VE_MPEG_VLD_ADDR 0x128
+#define VE_MPEG_VLD_OFFSET 0x12c
+#define VE_MPEG_VLD_LEN 0x130
+#define VE_MPEG_VLD_END 0x134
+#define VE_MPEG_MBH_ADDR 0x138
+#define VE_MPEG_DCAC_ADDR 0x13c
+#define VE_MPEG_NCF_ADDR 0x144
+#define VE_MPEG_REC_LUMA 0x148
+#define VE_MPEG_REC_CHROMA 0x14c
+#define VE_MPEG_FWD_LUMA 0x150
+#define VE_MPEG_FWD_CHROMA 0x154
+#define VE_MPEG_BACK_LUMA 0x158
+#define VE_MPEG_BACK_CHROMA 0x15c
+#define VE_MPEG_IQ_MIN_INPUT 0x180
+#define VE_MPEG_QP_INPUT 0x184
+
+#define VE_MPEG_ROT_LUMA 0x1cc
+#define VE_MPEG_ROT_CHROMA 0x1d0
+#define VE_MPEG_SDROT_CTRL 0x1d4
+
+#define VE_H264_FRAME_SIZE 0x200
+#define VE_H264_PIC_HDR 0x204
+#define VE_H264_SLICE_HDR 0x208
+#define VE_H264_SLICE_HDR2 0x20c
+#define VE_H264_PRED_WEIGHT 0x210
+#define VE_H264_QP_PARAM 0x21c
+#define VE_H264_CTRL 0x220
+#define VE_H264_TRIGGER 0x224
+#define VE_H264_STATUS 0x228
+#define VE_H264_CUR_MB_NUM 0x22c
+#define VE_H264_VLD_ADDR 0x230
+#define VE_H264_VLD_OFFSET 0x234
+#define VE_H264_VLD_LEN 0x238
+#define VE_H264_VLD_END 0x23c
+#define VE_H264_SDROT_CTRL 0x240
+#define VE_H264_SDROT_LUMA 0x244
+#define VE_H264_SDROT_CHROMA 0x248
+#define VE_H264_OUTPUT_FRAME_IDX 0x24c
+#define VE_H264_EXTRA_BUFFER1 0x250
+#define VE_H264_EXTRA_BUFFER2 0x254
+#define VE_H264_BASIC_BITS 0x2dc
+#define VE_H264_RAM_WRITE_PTR 0x2e0
+#define VE_H264_RAM_WRITE_DATA 0x2e4
+
+#define VE_SRAM_H264_PRED_WEIGHT_TABLE 0x000
+#define VE_SRAM_H264_FRAMEBUFFER_LIST 0x400
+#define VE_SRAM_H264_REF_LIST0 0x640
+#define VE_SRAM_H264_REF_LIST1 0x664
+#define VE_SRAM_H264_SCALING_LISTS 0x800
+
+#define VE_HEVC_NAL_HDR 0x500
+#define VE_HEVC_SPS 0x504
+#define VE_HEVC_PIC_SIZE 0x508
+#define VE_HEVC_PCM_HDR 0x50c
+#define VE_HEVC_PPS0 0x510
+#define VE_HEVC_PPS1 0x514
+#define VE_HEVC_SLICE_HDR0 0x520
+#define VE_HEVC_SLICE_HDR1 0x524
+#define VE_HEVC_SLICE_HDR2 0x528
+#define VE_HEVC_CTB_ADDR 0x52c
+#define VE_HEVC_CTRL 0x530
+#define VE_HEVC_TRIG 0x534
+#define VE_HEVC_STATUS 0x538
+#define VE_HEVC_CTU_NUM 0x53c
+#define VE_HEVC_BITS_ADDR 0x540
+#define VE_HEVC_BITS_OFFSET 0x544
+#define VE_HEVC_BITS_LEN 0x548
+#define VE_HEVC_BITS_END_ADDR 0x54c
+#define VE_HEVC_REC_BUF_IDX 0x55c
+#define VE_HEVC_NEIGHBOR_INFO_ADDR 0x560
+#define VE_HEVC_TILE_LIST_ADDR 0x564
+#define VE_HEVC_TILE_START_CTB 0x568
+#define VE_HEVC_TILE_END_CTB 0x56c
+#define VE_HEVC_BITS_DATA 0x5dc
+#define VE_HEVC_SRAM_ADDR 0x5e0
+#define VE_HEVC_SRAM_DATA 0x5e4
+
+#define VE_SRAM_HEVC_PRED_WEIGHT_LUMA_L0 0x000
+#define VE_SRAM_HEVC_PRED_WEIGHT_CHROMA_L0 0x020
+#define VE_SRAM_HEVC_PRED_WEIGHT_LUMA_L1 0x060
+#define VE_SRAM_HEVC_PRED_WEIGHT_CHROMA_L1 0x080
+#define VE_SRAM_HEVG_PIC_LIST 0x400
+#define VE_SRAM_HEVC_REF_PIC_LIST0 0xc00
+#define VE_SRAM_HEVC_REF_PIC_LIST1 0xc10
+
+#define VE_ISP_INPUT_SIZE 0xa00
+#define VE_ISP_INPUT_STRIDE 0xa04
+#define VE_ISP_CTRL 0xa08
+#define VE_ISP_INPUT_LUMA 0xa78
+#define VE_ISP_INPUT_CHROMA 0xa7c
+
+#define VE_AVC_PARAM 0xb04
+#define VE_AVC_QP 0xb08
+#define VE_AVC_MOTION_EST 0xb10
+#define VE_AVC_CTRL 0xb14
+#define VE_AVC_TRIGGER 0xb18
+#define VE_AVC_STATUS 0xb1c
+#define VE_AVC_BASIC_BITS 0xb20
+#define VE_AVC_UNK_BUF 0xb60
+#define VE_AVC_VLE_ADDR 0xb80
+#define VE_AVC_VLE_END 0xb84
+#define VE_AVC_VLE_OFFSET 0xb88
+#define VE_AVC_VLE_MAX 0xb8c
+#define VE_AVC_VLE_LENGTH 0xb90
+#define VE_AVC_REF_LUMA 0xba0
+#define VE_AVC_REF_CHROMA 0xba4
+#define VE_AVC_REC_LUMA 0xbb0
+#define VE_AVC_REC_CHROMA 0xbb4
+#define VE_AVC_REF_SLUMA 0xbb8
+#define VE_AVC_REC_SLUMA 0xbbc
+#define VE_AVC_MB_INFO 0xbc0
+
+
+#endif
diff --git a/libavcodec/cedrus264.c b/libavcodec/cedrus264.c
new file mode 100755
index 0000000..5e62361
--- /dev/null
+++ b/libavcodec/cedrus264.c
@@ -0,0 +1,402 @@
+/*
+ * Cedrus 264 Video Encoder
+ * Copyright (c) 2014 Julien Folly
+ *
+ * This file is part of Libav.
+ *
+ * Libav is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * Libav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Cedrus 264 Encoder
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "libavutil/internal.h"
+#include "libavutil/opt.h"
+#include "libavutil/mem.h"
+#include "libavutil/pixdesc.h"
+#include "avcodec.h"
+#include "internal.h"
+
+#include "arm/sunxi/ve.h"
+
+/* byte stream utils from:
+ * https://github.com/jemk/cedrus/tree/master/h264enc
+ */
+static void put_bits(void* regs, uint32_t x, int num)
+{
+ writel(x, (uint8_t *)regs + VE_AVC_BASIC_BITS);
+ writel(0x1 | ((num & 0x1f) << 8), (uint8_t *)regs + VE_AVC_TRIGGER);
+ // again the problem, how to check for finish?
+}
+
+static void put_ue(void* regs, uint32_t x)
+{
+ x++;
+ put_bits(regs, x, (32 - __builtin_clz(x)) * 2 - 1);
+}
+
+static void put_se(void* regs, int x)
+{
+ x = 2 * x - 1;
+ x ^= (x >> 31);
+ put_ue(regs, x);
+}
+
+static void put_start_code(void* regs)
+{
+ uint32_t tmp = readl((uint8_t *)regs + VE_AVC_PARAM);
+
+ /* Disable emulation_prevention_three_byte */
+ writel(tmp | (0x1 << 31), (uint8_t *)regs + VE_AVC_PARAM);
+
+ put_bits(regs, 0, 31);
+ put_bits(regs, 1, 1);
+
+ writel(tmp, (uint8_t *)regs + VE_AVC_PARAM);
+}
+
+static void put_rbsp_trailing_bits(void* regs)
+{
+ unsigned int cur_bs_len = readl((uint8_t *)regs + VE_AVC_VLE_LENGTH);
+
+ int num_zero_bits = 8 - ((cur_bs_len + 1) & 0x7);
+ put_bits(regs, 1 << num_zero_bits, num_zero_bits + 1);
+}
+
+static void put_seq_parameter_set(void* regs, int width, int height)
+{
+ put_bits(regs, 3 << 5 | 7 << 0, 8); // NAL Header
+ put_bits(regs, 77, 8); // profile_idc
+ put_bits(regs, 0x0, 8); // constraints
+ put_bits(regs, 4 * 10 + 1, 8); // level_idc
+ put_ue(regs, 0); // seq_parameter_set_id
+
+ put_ue(regs, 0); // log2_max_frame_num_minus4
+ put_ue(regs, 0); // pic_order_cnt_type
+ // if (pic_order_cnt_type == 0)
+ put_ue(regs, 4); // log2_max_pic_order_cnt_lsb_minus4
+
+ put_ue(regs, 1); // max_num_ref_frames
+ put_bits(regs, 0, 1); // gaps_in_frame_num_value_allowed_flag
+
+ put_ue(regs, width - 1); // pic_width_in_mbs_minus1
+ put_ue(regs, height - 1); // pic_height_in_map_units_minus1
+
+ put_bits(regs, 1, 1); // frame_mbs_only_flag
+ // if (!frame_mbs_only_flag)
+
+ put_bits(regs, 1, 1); // direct_8x8_inference_flag
+ put_bits(regs, 0, 1); // frame_cropping_flag
+ // if (frame_cropping_flag)
+
+ put_bits(regs, 0, 1); // vui_parameters_present_flag
+ // if (vui_parameters_present_flag)
+}
+
+static void put_pic_parameter_set(void *regs, int qp_minus30)
+{
+ put_bits(regs, 3 << 5 | 8 << 0, 8); // NAL Header
+ put_ue(regs, 0); // pic_parameter_set_id
+ put_ue(regs, 0); // seq_parameter_set_id
+ put_bits(regs, 1, 1); // entropy_coding_mode_flag
+ put_bits(regs, 0, 1); // bottom_field_pic_order_in_frame_present_flag
+ put_ue(regs, 0); // num_slice_groups_minus1
+ // if (num_slice_groups_minus1 > 0)
+
+ put_ue(regs, 0); // num_ref_idx_l0_default_active_minus1
+ put_ue(regs, 0); // num_ref_idx_l1_default_active_minus1
+ put_bits(regs, 0, 1); // weighted_pred_flag
+ put_bits(regs, 0, 2); // weighted_bipred_idc
+ //put_se(regs, 0); // pic_init_qp_minus26 (minus slice_qp_delta)
+ //put_se(regs, 0); // pic_init_qs_minus26
+ put_se(regs, qp_minus30); // pic_init_qp_minus26 (minus slice_qp_delta)
+ put_se(regs, qp_minus30); // pic_init_qs_minus26
+ put_se(regs, 4); // chroma_qp_index_offset
+ put_bits(regs, 1, 1); // deblocking_filter_control_present_flag
+ put_bits(regs, 0, 1); // constrained_intra_pred_flag
+ put_bits(regs, 0, 1); // redundant_pic_cnt_present_flag
+}
+
+static void put_slice_header(void* regs)
+{
+ put_bits(regs, 3 << 5 | 5 << 0, 8); // NAL Header
+
+ put_ue(regs, 0); // first_mb_in_slice
+ put_ue(regs, 2); // slice_type
+ put_ue(regs, 0); // pic_parameter_set_id
+ put_bits(regs, 0, 4); // frame_num
+
+ // if (IdrPicFlag)
+ put_ue(regs, 0); // idr_pic_id
+
+ // if (pic_order_cnt_type == 0)
+ put_bits(regs, 0, 8); // pic_order_cnt_lsb
+
+ // dec_ref_pic_marking
+ put_bits(regs, 0, 1); // no_output_of_prior_pics_flag
+ put_bits(regs, 0, 1); // long_term_reference_flag
+
+ put_se(regs, 4); // slice_qp_delta
+
+ // if (deblocking_filter_control_present_flag)
+ put_ue(regs, 0); // disable_deblocking_filter_idc
+ // if (disable_deblocking_filter_idc != 1)
+ put_se(regs, 0); // slice_alpha_c0_offset_div2
+ put_se(regs, 0); // slice_beta_offset_div2
+}
+
+static void put_aud(void* regs)
+{
+ put_bits(regs, 0 << 5 | 9 << 0, 8); // NAL Header
+
+ put_bits(regs, 7, 3); // primary_pic_type
+}
+
+#define CEDAR_OUTPUT_BUF_SIZE 1*1024*1024
+typedef struct cedrus264Context {
+ AVClass *class;
+ uint8_t *ve_regs;
+ struct ve_mem *input_buf, *output_buf, *reconstruct_buf, *small_luma_buf, *mb_info_buf;
+ unsigned int tile_w, tile_w2, tile_h, tile_h2, mb_w, mb_h, plane_size, frame_size;
+ unsigned int frame_num;
+ int qp, vewait;
+} cedrus264Context;
+
+static av_cold int cedrus264_encode_init(AVCodecContext *avctx)
+{
+ cedrus264Context *c4 = avctx->priv_data;
+
+ /* Check pixel format */
+ if(avctx->pix_fmt != AV_PIX_FMT_NV12){
+ av_log(avctx, AV_LOG_FATAL, "Unsupported pixel format (use -pix_fmt nv12)!\n");
+ return AVERROR(EINVAL);
+ }
+
+ /* Check width */
+ if(avctx->width % 32 != 0){
+ av_log(avctx, AV_LOG_FATAL, "Input width is not a multiple of 32!\n");
+ return AVERROR(EINVAL);
+ }
+
+ /* Check if VE is available */
+ while(!ve_lock()){
+ if (c4->vewait <= 0){
+ av_log(avctx, AV_LOG_ERROR, "VE in use!\n");
+ return AVERROR(ENOMEM);
+ }
+ av_log(avctx, AV_LOG_INFO, "VE in use, wait %i seconds.\r", c4->vewait--);
+ sleep(1);
+ }
+
+ /* Open VE */
+ if(!ve_open()){
+ av_log(avctx, AV_LOG_ERROR, "VE Open error.\n");
+ return AVERROR(ENOMEM);
+ }
+
+
+ /* Compute tile, macroblock and plane size */
+ c4->tile_w = (avctx->width + 31) & ~31;
+ c4->tile_w2 = (avctx->width / 2 + 31) & ~31;
+ c4->tile_h = (avctx->height + 31) & ~31;
+ c4->tile_h2 = (avctx->height / 2 + 31) & ~31;
+ c4->mb_w = (avctx->width + 15) / 16;
+ c4->mb_h = (avctx->height + 15) / 16;
+ c4->plane_size = c4->mb_w * 16 * c4->mb_h * 16;
+ c4->frame_size = c4->plane_size + c4->plane_size / 2;
+
+ /* Alloc buffers */
+ c4->input_buf = ve_malloc(c4->frame_size);
+ c4->output_buf = ve_malloc(CEDAR_OUTPUT_BUF_SIZE);
+ c4->reconstruct_buf = ve_malloc(c4->tile_w * c4->tile_h + c4->tile_w * c4->tile_h2);
+ c4->small_luma_buf = ve_malloc(c4->tile_w2 * c4->tile_h2);
+ c4->mb_info_buf = ve_malloc(0x1000);
+ if(!c4->input_buf || !c4->output_buf || !c4->reconstruct_buf || !c4->small_luma_buf || !c4->mb_info_buf){
+ av_log(avctx, AV_LOG_FATAL, "Cannot allocate frame.\n");
+ return AVERROR(ENOMEM);
+ }
+
+ /* Activate AVC engine */
+ c4->ve_regs = ve_get(VE_ENGINE_AVC, 0);
+
+ /* ---- Part to put in cedrus264_encode if engine is used by multiple process (Need to be checked) */
+
+ /* Input size */
+ writel(c4->mb_w << 16, c4->ve_regs + VE_ISP_INPUT_STRIDE);
+ writel((c4->mb_w << 16) | (c4->mb_h << 0), c4->ve_regs + VE_ISP_INPUT_SIZE);
+
+ /* Input buffer */
+ writel(c4->input_buf->phys, c4->ve_regs + VE_ISP_INPUT_LUMA);
+ writel(c4->input_buf->phys + c4->plane_size, c4->ve_regs + VE_ISP_INPUT_CHROMA);
+
+ /* Reference output */
+ writel(c4->reconstruct_buf->phys, c4->ve_regs + VE_AVC_REC_LUMA);
+ writel(c4->reconstruct_buf->phys + c4->tile_w * c4->tile_h, c4->ve_regs + VE_AVC_REC_CHROMA);
+ writel(c4->small_luma_buf->phys, c4->ve_regs + VE_AVC_REC_SLUMA);
+ writel(c4->mb_info_buf->phys, c4->ve_regs + VE_AVC_MB_INFO);
+
+ /* Encoding parameters */
+ writel(0x00000100, c4->ve_regs + VE_AVC_PARAM);
+ writel(0x00040000 | (c4->qp<<8) | c4->qp, c4->ve_regs + VE_AVC_QP);
+ //writel(0x00041e1e, c4->ve_regs + VE_AVC_QP); // Fixed QP=30
+ writel(0x00000104, c4->ve_regs + VE_AVC_MOTION_EST);
+
+ /* ---- Part end ---- */
+
+ /* Alloc Frame */
+ avctx->coded_frame = av_frame_alloc();
+ if(!avctx->coded_frame){
+ av_log(avctx, AV_LOG_FATAL, "Cannot allocate frame.\n");
+ return AVERROR(ENOMEM);
+ }
+
+ /* Init variables */
+ c4->frame_num = 0;
+ avctx->coded_frame->quality = c4->qp * FF_QP2LAMBDA;
+
+ return 0;
+}
+
+static int cedrus264_encode(AVCodecContext *avctx, AVPacket *pkt,
+ const AVFrame *frame, int *got_packet)
+{
+ cedrus264Context *c4 = avctx->priv_data;
+ unsigned int size;
+ int result;
+
+ /* Copy data */
+ result = avpicture_layout((const AVPicture *)frame, AV_PIX_FMT_NV12,
+ avctx->width, avctx->height, c4->input_buf->virt, c4->frame_size);
+ if(result < 0){
+ av_log(avctx, AV_LOG_ERROR, "Input buffer too small.\n");
+ return AVERROR(ENOMEM);
+ }
+ ve_flush_cache(c4->input_buf);
+
+ /* flush output buffer, otherwise we might read old cached data */
+ ve_flush_cache(c4->output_buf);
+
+ /* Set output buffer */
+ writel(0x0, c4->ve_regs + VE_AVC_VLE_OFFSET);
+ writel(c4->output_buf->phys, c4->ve_regs + VE_AVC_VLE_ADDR);
+ writel(c4->output_buf->phys + CEDAR_OUTPUT_BUF_SIZE - 1, c4->ve_regs + VE_AVC_VLE_END);
+
+ writel(0x04000000, c4->ve_regs + 0xb8c); // ???
+
+ put_start_code(c4->ve_regs);
+ put_aud(c4->ve_regs);
+ put_rbsp_trailing_bits(c4->ve_regs);
+
+ if (c4->frame_num == 0)
+ {
+ put_start_code(c4->ve_regs);
+ put_seq_parameter_set(c4->ve_regs, c4->mb_w, c4->mb_h);
+ put_rbsp_trailing_bits(c4->ve_regs);
+
+ put_start_code(c4->ve_regs);
+ put_pic_parameter_set(c4->ve_regs, c4->qp - 30);
+ put_rbsp_trailing_bits(c4->ve_regs);
+ }
+
+ put_start_code(c4->ve_regs);
+ put_slice_header(c4->ve_regs);
+
+ writel(readl(c4->ve_regs + VE_AVC_CTRL) | 0xf, c4->ve_regs + VE_AVC_CTRL);
+ writel(readl(c4->ve_regs + VE_AVC_STATUS) | 0x7, c4->ve_regs + VE_AVC_STATUS);
+
+ writel(0x8, c4->ve_regs + VE_AVC_TRIGGER);
+ ve_wait(1);
+
+ writel(readl(c4->ve_regs + VE_AVC_STATUS), c4->ve_regs + VE_AVC_STATUS);
+
+ size = readl(c4->ve_regs + VE_AVC_VLE_LENGTH) / 8;
+ if(size > 0){
+ if ((result = ff_alloc_packet(pkt, size)) < 0){
+ av_log(avctx, AV_LOG_ERROR, "Packet allocation error.\n");
+ return result;
+ }
+ memcpy(pkt->data, c4->output_buf->virt, size);
+
+ pkt->pts = pkt->dts = frame->pts - ff_samples_to_time_base(avctx, avctx->delay);
+ pkt->flags |= AV_PKT_FLAG_KEY;
+ *got_packet = 1;
+ }else *got_packet = 0;
+
+ c4->frame_num++;
+
+ return 0;
+}
+
+static av_cold int cedrus264_close(AVCodecContext *avctx)
+{
+ cedrus264Context *c4 = avctx->priv_data;
+
+ /* Close AVC engine */
+ ve_put();
+
+ /* Free buffers */
+ ve_free(c4->input_buf);
+ ve_free(c4->output_buf);
+ ve_free(c4->reconstruct_buf);
+ ve_free(c4->small_luma_buf);
+ ve_free(c4->mb_info_buf);
+
+ /* Disable and close VE */
+ ve_close();
+ ve_unlock();
+
+ /* Free Frame */
+ av_frame_free(&avctx->coded_frame);
+
+ return 0;
+}
+
+#define OFFSET(x) offsetof(cedrus264Context, x)
+#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ /* Quality range form 0 to 51 not working, good is between 2 and 47 */
+ { "qp", "Constant quantization parameter rate control method", OFFSET(qp), AV_OPT_TYPE_INT, { .i64 = 30 }, 2, 47, VE },
+ { "vewait", "Time to wait if the VE is busy (default 0)", OFFSET(vewait), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE },
+ { NULL },
+};
+
+static const AVClass cedrus264_class = {
+ .class_name = "cedrus264",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVCodec ff_cedrus264_encoder = {
+ .name = "cedrus264",
+ .long_name = NULL_IF_CONFIG_SMALL("Cedrus H.264 Encoder"),
+ .type = AVMEDIA_TYPE_VIDEO,
+ .id = AV_CODEC_ID_H264,
+ .priv_data_size = sizeof(cedrus264Context),
+ .init = cedrus264_encode_init,
+ .encode2 = cedrus264_encode,
+ .close = cedrus264_close,
+ .priv_class = &cedrus264_class,
+};
--
2.7.4
More information about the ffmpeg-devel
mailing list