summaryrefslogtreecommitdiff
authorNanxin Qin <nanxin.qin@amlogic.com>2020-04-21 07:04:09 (GMT)
committer Hui Zhang <hui.zhang@amlogic.com>2020-05-29 05:26:56 (GMT)
commit677ab50da749119274325f12855b0f3ac0cfb88b (patch)
tree745a2bd261c5900c31a1891d65d2fb1fd1ef86b0
parenta1ce75ba81641dfbaffd42c9ed333c6a69be8ca1 (diff)
downloadmedia_modules-677ab50da749119274325f12855b0f3ac0cfb88b.zip
media_modules-677ab50da749119274325f12855b0f3ac0cfb88b.tar.gz
media_modules-677ab50da749119274325f12855b0f3ac0cfb88b.tar.bz2
vp9: add decoder fence to save time of video pipeline. [2/2]
PD#SWPL-26955 Problem: Reduce 1 frame delay in Sabrina's video pipeline Solution: add decoder fence to save time of video pipeline this patch of optimized is on decode side. Verify: u212 Change-Id: I6c0ef14a76534e19968fa4a5c18eda30a79936e6 Signed-off-by: Nanxin Qin <nanxin.qin@amlogic.com>
Diffstat
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/fake_video_out/Makefile1
-rw-r--r--drivers/fake_video_out/fake_video.c221
-rw-r--r--drivers/frame_provider/decoder/utils/Makefile1
-rw-r--r--drivers/frame_provider/decoder/utils/vdec.c19
-rw-r--r--drivers/frame_provider/decoder/utils/vdec.h2
-rw-r--r--drivers/frame_provider/decoder/utils/vdec_sync.c377
-rw-r--r--drivers/frame_provider/decoder/utils/vdec_sync.h70
-rw-r--r--drivers/frame_provider/decoder/vp9/vvp9.c142
9 files changed, 825 insertions, 9 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 018c8fe..1acca52 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -3,3 +3,4 @@ obj-y += frame_provider/
obj-y += frame_sink/
obj-y += stream_input/
obj-y += amvdec_ports/
+obj-y += fake_video_out/
diff --git a/drivers/fake_video_out/Makefile b/drivers/fake_video_out/Makefile
new file mode 100644
index 0000000..1dd937c
--- a/dev/null
+++ b/drivers/fake_video_out/Makefile
@@ -0,0 +1 @@
+obj-m += fake_video.o
diff --git a/drivers/fake_video_out/fake_video.c b/drivers/fake_video_out/fake_video.c
new file mode 100644
index 0000000..586d350
--- a/dev/null
+++ b/drivers/fake_video_out/fake_video.c
@@ -0,0 +1,221 @@
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/ctype.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-contiguous.h>
+#include <linux/sched.h>
+#include <linux/amlogic/media/vfm/vframe.h>
+#include <linux/amlogic/media/vfm/vframe_provider.h>
+#include <linux/amlogic/media/vfm/vframe_receiver.h>
+#include <linux/amlogic/major.h>
+#include "../frame_provider/decoder/utils/vdec.h"
+#include <linux/delay.h>
+
+#define RECEIVER_NAME "fake-amvideo"
+#define DEVICE_NAME "fake-amvideo"
+
+static struct device *amvideo_dev = NULL;
+struct work_struct worker;
+
+static struct vframe_receiver_s fake_video_vf_recv;
+static int video_receiver_event_fun(int type, void *data, void *);
+static const struct vframe_receiver_op_s fake_video_vf_receiver = {
+ .event_cb = video_receiver_event_fun
+};
+
+static struct vframe_s *video_vf_peek(void)
+{
+ return vf_peek(RECEIVER_NAME);
+}
+
+static struct vframe_s *video_vf_get(void)
+{
+ struct vframe_s *vf = NULL;
+
+ vf = vf_get(RECEIVER_NAME);
+
+ if (vf) {
+ atomic_set(&vf->use_cnt, 1);
+ /*pr_err("Get vframe w: %d, h: %d, fence: %lx, idx: %d\n",
+ vf->width, vf->height, (ulong)vf->fence, vf->index & 0xff);*/
+ }
+
+ return vf;
+}
+
+static void video_vf_put(struct vframe_s *vf)
+{
+ struct vframe_provider_s *vfp = vf_get_provider(RECEIVER_NAME);
+
+ if (vfp && vf && atomic_dec_and_test(&vf->use_cnt)) {
+ vf_put(vf, RECEIVER_NAME);
+ }
+}
+
+static int video_receiver_event_fun(int type, void *data, void *private_data)
+{
+ switch (type) {
+ case VFRAME_EVENT_PROVIDER_UNREG: {
+ break;
+ }
+ case VFRAME_EVENT_PROVIDER_START: {
+ break;
+ }
+ case VFRAME_EVENT_PROVIDER_QUREY_STATE: {
+ break;
+ }
+ case VFRAME_EVENT_PROVIDER_VFRAME_READY: {
+ vdec_schedule_work(&worker);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+void displayer_worker(struct work_struct *work)
+{
+ struct vframe_s *vf = NULL;
+
+ if (video_vf_peek()) {
+ vf = video_vf_get();
+ if (!vf) {
+ pr_err("receiver vf err.\n");
+ return;
+ }
+
+ /* add delay to simulation pipeline time*/
+ udelay(2000);
+
+ if (vf->fence) {
+ /* fence waiting until frame ready. */
+ vdec_fence_wait(vf->fence, 2000);
+
+ if (vdec_fence_status_get(vf->fence) == 1) {
+ pr_info("[VDEC-FENCE]: Display, idx: %d, dec cost: %lld\n",
+ vf->index & 0xff, local_clock() - get_sync_pt(vf->fence)->timestamp);
+ } else {
+ pr_err("[VDEC-FENCE]: Display invalid, fence status err.\n");
+ }
+ }
+
+ video_vf_put(vf);
+ }
+}
+
+static int amvideo_open(struct inode *inode, struct file *file)
+{
+ file->private_data = NULL;
+ return 0;
+}
+
+static int amvideo_release(struct inode *inode, struct file *file)
+{
+ file->private_data = NULL;
+ return 0;
+}
+
+static long amvideo_ioctl(struct file *file, unsigned int cmd, ulong arg)
+{
+ file->private_data = NULL;
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+static long amvideo_compat_ioctl(struct file *file, unsigned int cmd, ulong arg)
+{
+ file->private_data = NULL;
+ return 0;
+}
+#endif
+
+static const struct file_operations amvideo_fops = {
+ .owner = THIS_MODULE,
+ .open = amvideo_open,
+ .release = amvideo_release,
+ .unlocked_ioctl = amvideo_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = amvideo_compat_ioctl,
+#endif
+ //.poll = amvideo_poll,
+};
+
+static struct class amvideo_class = {
+ .name = "fake_video",
+};
+
+#define FAKEVIDEO_MAJOR (21 + (AML_BASE))
+
+static int __init fake_video_init(void)
+{
+ int ret = 0;
+
+ ret = class_register(&amvideo_class);
+ if (ret) {
+ pr_err("create video class fail.\n");
+ return 0;
+ }
+
+
+ /* create video device */
+ ret = register_chrdev(FAKEVIDEO_MAJOR, DEVICE_NAME, &amvideo_fops);
+ if (ret < 0) {
+ pr_err("Can't register major for amvideo device\n");
+ goto err1;
+ }
+
+ amvideo_dev = device_create(&amvideo_class, NULL,
+ MKDEV(FAKEVIDEO_MAJOR, 0), NULL, DEVICE_NAME);
+ if (IS_ERR(amvideo_dev)) {
+ pr_err("Can't create amvideo device\n");
+ goto err;
+ }
+
+ vf_receiver_init(&fake_video_vf_recv, RECEIVER_NAME,
+ &fake_video_vf_receiver, NULL);
+ vf_reg_receiver(&fake_video_vf_recv);
+
+
+ INIT_WORK(&worker, displayer_worker);
+
+ return 0;
+
+err:
+ unregister_chrdev(FAKEVIDEO_MAJOR, DEVICE_NAME);
+err1:
+ class_unregister(&amvideo_class);
+
+ return ret;
+}
+
+static void __exit fake_video_exit(void)
+{
+ cancel_work_sync(&worker);
+
+ vf_unreg_receiver(&fake_video_vf_recv);
+
+ device_destroy(&amvideo_class, MKDEV(FAKEVIDEO_MAJOR, 0));
+ unregister_chrdev(FAKEVIDEO_MAJOR, DEVICE_NAME);
+ class_unregister(&amvideo_class);
+
+}
+
+
+module_init(fake_video_init);
+module_exit(fake_video_exit);
+
+
+MODULE_DESCRIPTION("AMLOGIC fake video output driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nanxin Qin <nanxin.qin@amlogic.com>");
+
diff --git a/drivers/frame_provider/decoder/utils/Makefile b/drivers/frame_provider/decoder/utils/Makefile
index 07edbe3..c587fd9 100644
--- a/drivers/frame_provider/decoder/utils/Makefile
+++ b/drivers/frame_provider/decoder/utils/Makefile
@@ -5,4 +5,5 @@ decoder_common-objs += config_parser.o secprot.o vdec_profile.o
decoder_common-objs += amstream_profile.o
decoder_common-objs += frame_check.o amlogic_fbc_hook.o
decoder_common-objs += vdec_v4l2_buffer_ops.o
+decoder_common-objs += vdec_sync.o
diff --git a/drivers/frame_provider/decoder/utils/vdec.c b/drivers/frame_provider/decoder/utils/vdec.c
index c81f1de..345d4a2 100644
--- a/drivers/frame_provider/decoder/utils/vdec.c
+++ b/drivers/frame_provider/decoder/utils/vdec.c
@@ -91,12 +91,20 @@ static int keep_vdec_mem;
static unsigned int debug_trace_num = 16 * 20;
static int step_mode;
static unsigned int clk_config;
+
/*
- &1: sched_priority to MAX_RT_PRIO -1.
- &2: always reload firmware.
- &4: vdec canvas debug enable
- */
-static unsigned int debug = 2;
+ * 0x1 : sched_priority to MAX_RT_PRIO -1.
+ * 0x2 : always reload firmware.
+ * 0x4 : vdec canvas debug enable
+ * 0x100: enable vdec fence.
+ */
+#define VDEC_DBG_SCHED_PRIO (0x1)
+#define VDEC_DBG_ALWAYS_LOAD_FW (0x2)
+#define VDEC_DBG_CANVAS_STATUS (0x4)
+#define VDEC_DBG_ENABLE_FENCE (0x100)
+
+u32 debug;
+EXPORT_SYMBOL(debug);
static int hevc_max_reset_count;
@@ -250,6 +258,7 @@ static const char vfm_path_node[][VDEC_MAP_NAME_SIZE] =
"v4lvideo.6",
"v4lvideo.7",
"v4lvideo.8",
+ "fake-amvideo",
"disable",
"reserved",
};
diff --git a/drivers/frame_provider/decoder/utils/vdec.h b/drivers/frame_provider/decoder/utils/vdec.h
index f991850..0a526d0 100644
--- a/drivers/frame_provider/decoder/utils/vdec.h
+++ b/drivers/frame_provider/decoder/utils/vdec.h
@@ -36,6 +36,7 @@
#include "vdec_input.h"
#include "frame_check.h"
+#include "vdec_sync.h"
s32 vdec_dev_register(void);
s32 vdec_dev_unregister(void);
@@ -273,6 +274,7 @@ struct vdec_s {
atomic_t inrelease;
int parallel_dec;
struct vdec_frames_s *mvfrm;
+ struct vdec_sync sync;
};
/* common decoder vframe provider name to use default vfm path */
diff --git a/drivers/frame_provider/decoder/utils/vdec_sync.c b/drivers/frame_provider/decoder/utils/vdec_sync.c
new file mode 100644
index 0000000..014b536
--- a/dev/null
+++ b/drivers/frame_provider/decoder/utils/vdec_sync.c
@@ -0,0 +1,377 @@
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/sync_file.h>
+#include "vdec_sync.h"
+
+#define VDEC_DBG_ENABLE_FENCE (0x100)
+
+extern u32 debug;
+
+static const struct fence_ops timeline_fence_ops;
+static inline struct sync_pt *fence_to_sync_pt(struct fence *fence)
+{
+ if (fence->ops != &timeline_fence_ops)
+ return NULL;
+ return container_of(fence, struct sync_pt, fence);
+}
+
+/**
+ * sync_timeline_create() - creates a sync object
+ * @name: sync_timeline name
+ *
+ * Creates a new sync_timeline. Returns the sync_timeline object or NULL in
+ * case of error.
+ */
+static struct sync_timeline *sync_timeline_create(const char *name)
+{
+ struct sync_timeline *obj;
+
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (!obj)
+ return NULL;
+
+ kref_init(&obj->kref);
+ obj->context = fence_context_alloc(1);
+ strlcpy(obj->name, name, sizeof(obj->name));
+ INIT_LIST_HEAD(&obj->active_list_head);
+ INIT_LIST_HEAD(&obj->pt_list);
+ spin_lock_init(&obj->lock);
+
+ return obj;
+}
+
+static void sync_timeline_free(struct kref *kref)
+{
+ struct sync_timeline *obj =
+ container_of(kref, struct sync_timeline, kref);
+
+ pr_info("[VDEC-FENCE] free timeline: %lx\n", (ulong) obj);
+
+ kfree(obj);
+}
+
+static void sync_timeline_get(struct sync_timeline *obj)
+{
+ kref_get(&obj->kref);
+}
+
+static void sync_timeline_put(struct sync_timeline *obj)
+{
+ kref_put(&obj->kref, sync_timeline_free);
+}
+
+static const char *timeline_fence_get_driver_name(struct fence *fence)
+{
+ struct sync_timeline *parent = fence_parent(fence);
+
+ return parent->name;
+}
+
+static const char *timeline_fence_get_timeline_name(struct fence *fence)
+{
+ struct sync_timeline *parent = fence_parent(fence);
+
+ return parent->name;
+}
+
+static void timeline_fence_release(struct fence *fence)
+{
+ struct sync_pt *pt = fence_to_sync_pt(fence);
+ struct sync_timeline *parent = fence_parent(fence);
+ unsigned long flags;
+
+ /*pr_info("[VDEC-FENCE] release fence: %lx\n", (ulong) fence);*/
+
+ spin_lock_irqsave(fence->lock, flags);
+ list_del(&pt->link);
+ if (!list_empty(&pt->active_list))
+ list_del(&pt->active_list);
+ spin_unlock_irqrestore(fence->lock, flags);
+ sync_timeline_put(parent);
+ fence_free(fence);
+}
+
+static bool timeline_fence_signaled(struct fence *fence)
+{
+ struct sync_timeline *parent = fence_parent(fence);
+ struct sync_pt *pt = get_sync_pt(fence);
+
+ if (__fence_is_later(fence->seqno, parent->value))
+ return false;
+
+ if (pt->timestamp > parent->timestamp)
+ return false;
+
+ return true;
+}
+
+static bool timeline_fence_enable_signaling(struct fence *fence)
+{
+ struct sync_pt *pt = container_of(fence, struct sync_pt, fence);
+ struct sync_timeline *parent = fence_parent(fence);
+
+ if (timeline_fence_signaled(fence))
+ return false;
+
+ list_add_tail(&pt->active_list, &parent->active_list_head);
+ return true;
+}
+
+static void timeline_fence_disable_signaling(struct fence *fence)
+{
+ struct sync_pt *pt = container_of(fence, struct sync_pt, fence);
+
+ list_del_init(&pt->active_list);
+}
+
+static void timeline_fence_value_str(struct fence *fence,
+ char *str, int size)
+{
+ snprintf(str, size, "%d", fence->seqno);
+}
+
+static void timeline_fence_timeline_value_str(struct fence *fence,
+ char *str, int size)
+{
+ struct sync_timeline *parent = fence_parent(fence);
+
+ snprintf(str, size, "%d", parent->value);
+}
+
+static const struct fence_ops timeline_fence_ops = {
+ .get_driver_name = timeline_fence_get_driver_name,
+ .get_timeline_name = timeline_fence_get_timeline_name,
+ .enable_signaling = timeline_fence_enable_signaling,
+ .disable_signaling = timeline_fence_disable_signaling,
+ .signaled = timeline_fence_signaled,
+ .wait = fence_default_wait,
+ .release = timeline_fence_release,
+ .fence_value_str = timeline_fence_value_str,
+ .timeline_value_str = timeline_fence_timeline_value_str,
+};
+
+/**
+ * sync_timeline_signal() - signal a status change on a sync_timeline
+ * @obj: sync_timeline to signal
+ * @inc: num to increment on timeline->value
+ *
+ * A sync implementation should call this any time one of it's fences
+ * has signaled or has an error condition.
+ */
+static void sync_timeline_signal(struct sync_timeline *obj, unsigned int inc)
+{
+ struct sync_pt *pt, *next;
+ unsigned long flags;
+
+ spin_lock_irqsave(&obj->lock, flags);
+ obj->value += inc;
+ list_for_each_entry_safe(pt, next, &obj->active_list_head,
+ active_list) {
+ if (fence_is_signaled_locked(&pt->fence))
+ list_del_init(&pt->active_list);
+ }
+ spin_unlock_irqrestore(&obj->lock, flags);
+}
+
+/**
+ * sync_pt_create() - creates a sync pt
+ * @parent: fence's parent sync_timeline
+ * @inc: value of the fence
+ *
+ * Creates a new sync_pt as a child of @parent. @size bytes will be
+ * allocated allowing for implementation specific data to be kept after
+ * the generic sync_timeline struct. Returns the sync_pt object or
+ * NULL in case of error.
+ */
+static struct sync_pt *sync_pt_create(struct sync_timeline *obj,
+ unsigned int value)
+{
+ struct sync_pt *pt;
+ unsigned long flags;
+
+ pt = kzalloc(sizeof(*pt), GFP_KERNEL);
+ if (!pt)
+ return NULL;
+ spin_lock_irqsave(&obj->lock, flags);
+ sync_timeline_get(obj);
+ fence_init(&pt->fence, &timeline_fence_ops, &obj->lock,
+ obj->context, value);
+ list_add_tail(&pt->link, &obj->pt_list);
+ INIT_LIST_HEAD(&pt->active_list);
+ spin_unlock_irqrestore(&obj->lock, flags);
+
+ return pt;
+}
+
+static void sync_pt_free(struct sync_timeline *obj,
+ struct sync_pt *pt)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&obj->lock, flags);
+ list_del(&pt->link);
+ sync_timeline_put(obj);
+ spin_unlock_irqrestore(&obj->lock, flags);
+ kfree(pt);
+ pt = NULL;
+}
+
+static int timeline_create_fence(struct vdec_sync *sync, int usage,
+ struct fence **fence, int *fd, u32 value)
+{
+ int ret;
+ struct sync_pt *pt;
+ struct sync_file *sync_file;
+ struct sync_timeline *obj = sync->timeline;
+
+ if (obj == NULL)
+ return -EPERM;
+
+ pt = sync_pt_create(obj, value);
+ if (!pt) {
+ return -ENOMEM;
+ }
+
+ if (usage == FENCE_USE_FOR_APP) {
+ *fd = get_unused_fd_flags(O_CLOEXEC);
+ if (*fd < 0) {
+ return -EBADF;
+ goto err;
+ }
+
+ sync_file = sync_file_create(&pt->fence);
+ if (!sync_file) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ fd_install(*fd, sync_file->file);
+
+ /* decreases refcnt. */
+ fence_put(&pt->fence);
+ }
+
+ *fence = &pt->fence;
+
+ pt->timestamp = local_clock();
+
+ if (debug & VDEC_DBG_ENABLE_FENCE)
+ pr_info("[VDEC-FENCE]: create fence: %lx, fd: %d, ref: %d, usage: %d\n",
+ (ulong) &pt->fence, *fd, atomic_read(&pt->fence.refcount.refcount), usage);
+ return 0;
+err:
+ put_unused_fd(*fd);
+ if (pt)
+ sync_pt_free(obj, pt);
+
+ return ret;
+}
+
+struct fence *vdec_fence_get(int fd)
+{
+ return sync_file_get_fence(fd);
+}
+EXPORT_SYMBOL(vdec_fence_get);
+
+void vdec_fence_put(struct fence *fence)
+{
+ if (debug & VDEC_DBG_ENABLE_FENCE)
+ pr_info("[VDEC-FENCE]: the fence cost time: %lld ns\n",
+ local_clock() - get_sync_pt(fence)->timestamp);
+ fence_put(fence);
+}
+EXPORT_SYMBOL(vdec_fence_put);
+
+int vdec_fence_wait(struct fence *fence, long timeout)
+{
+ if (debug & VDEC_DBG_ENABLE_FENCE)
+ pr_info("[VDEC-FENCE]: wait fence %lx.\n", (ulong) fence);
+
+ return fence_wait_timeout(fence, false, timeout);
+}
+EXPORT_SYMBOL(vdec_fence_wait);
+
+void vdec_timeline_create(struct vdec_sync *sync, u8 *name)
+{
+ snprintf(sync->name, sizeof(sync->name), "%s", name);
+
+ sync->timeline = (void *)sync_timeline_create(sync->name);
+ if (sync->timeline)
+ pr_info("[VDEC-FENCE]: create timeline %lx, name: %s\n",
+ (ulong) sync->timeline, sync->name);
+ else
+ pr_err("[VDEC-FENCE]: create timeline faild.\n");
+}
+EXPORT_SYMBOL(vdec_timeline_create);
+
+int vdec_timeline_create_fence(struct vdec_sync *sync)
+{
+ struct sync_timeline *obj = sync->timeline;
+ u32 value = 0;
+
+ if (obj == NULL)
+ return -EPERM;
+
+ value = obj->value + 1;
+
+ return timeline_create_fence(sync,
+ sync->usage,
+ &sync->fence,
+ &sync->fd,
+ value);
+}
+EXPORT_SYMBOL(vdec_timeline_create_fence);
+
+void vdec_timeline_increase(struct vdec_sync *sync, u32 value)
+{
+ struct sync_timeline *obj = sync->timeline;
+
+ if (obj == NULL)
+ return;
+
+ obj->timestamp = local_clock();
+
+ if (debug & VDEC_DBG_ENABLE_FENCE)
+ pr_info("[VDEC-FENCE]: update timeline %d.\n",
+ obj->value + value);
+
+ sync_timeline_signal(obj, value);
+}
+EXPORT_SYMBOL(vdec_timeline_increase);
+
+void vdec_timeline_put(struct vdec_sync *sync)
+{
+ struct sync_timeline *obj = sync->timeline;
+
+ sync_timeline_put(obj);
+}
+EXPORT_SYMBOL(vdec_timeline_put);
+
+void vdec_fence_status_set(struct fence *fence, int status)
+{
+ fence->error = status;
+}
+EXPORT_SYMBOL(vdec_fence_status_set);
+
+int vdec_fence_status_get(struct fence *fence)
+{
+ return fence_get_status(fence);
+}
+EXPORT_SYMBOL(vdec_fence_status_get);
+
+bool check_objs_all_signaled(struct vdec_sync *sync)
+{
+ struct sync_timeline *obj = sync->timeline;
+ bool ret = false;
+ ulong flags;
+
+ spin_lock_irqsave(&obj->lock, flags);
+ ret = list_empty(&obj->active_list_head);
+ spin_unlock_irqrestore(&obj->lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(check_objs_all_signaled);
+
diff --git a/drivers/frame_provider/decoder/utils/vdec_sync.h b/drivers/frame_provider/decoder/utils/vdec_sync.h
new file mode 100644
index 0000000..a57f0ab
--- a/dev/null
+++ b/drivers/frame_provider/decoder/utils/vdec_sync.h
@@ -0,0 +1,70 @@
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <linux/spinlock.h>
+#include <linux/fence.h>
+#include <linux/sync_file.h>
+#include <uapi/linux/sync_file.h>
+
+#define FENCE_USE_FOR_DRIVER (0)
+#define FENCE_USE_FOR_APP (1)
+
+struct sync_timeline {
+ struct kref kref;
+ char name[32];
+
+ /* protected by lock */
+ u64 context;
+ u32 value;
+
+ struct list_head active_list_head;
+ struct list_head pt_list;
+ spinlock_t lock;
+
+ u64 timestamp;
+};
+
+struct sync_pt {
+ struct fence fence;
+ struct list_head link;
+ struct list_head active_list;
+ u64 timestamp;
+};
+
+struct vdec_sync {
+ u8 name[32];
+ void *timeline;
+ int usage;
+ int fd;
+ struct fence *fence;
+};
+
+static inline struct sync_timeline *fence_parent(struct fence *fence)
+{
+ return container_of(fence->lock, struct sync_timeline, lock);
+}
+
+static inline struct sync_pt *get_sync_pt(struct fence *fence)
+{
+ return container_of(fence, struct sync_pt, fence);
+}
+
+struct fence *vdec_fence_get(int fd);
+
+void vdec_fence_put(struct fence *fence);
+
+int vdec_fence_wait(struct fence *fence, long timeout);
+
+void vdec_timeline_create(struct vdec_sync *sync, u8 *name);
+
+int vdec_timeline_create_fence(struct vdec_sync *sync);
+
+void vdec_timeline_increase(struct vdec_sync *sync, u32 value);
+
+void vdec_timeline_put(struct vdec_sync *sync);
+
+int vdec_fence_status_get(struct fence *fence);
+
+void vdec_fence_status_set(struct fence *fence, int status);
+
+bool check_objs_all_signaled(struct vdec_sync *sync);
+
diff --git a/drivers/frame_provider/decoder/vp9/vvp9.c b/drivers/frame_provider/decoder/vp9/vvp9.c
index 051736a..ce898e0 100644
--- a/drivers/frame_provider/decoder/vp9/vvp9.c
+++ b/drivers/frame_provider/decoder/vp9/vvp9.c
@@ -398,6 +398,15 @@ static u32 udebug_pause_decode_idx;
static u32 without_display_mode;
+/*
+ *[3:0] 0: default use config from omx.
+ * 1: force enable fence.
+ * 2: disable fence.
+ *[7:4] 0: fence use for driver.
+ * 1: fence fd use for app.
+ */
+static u32 force_config_fence;
+
#define DEBUG_REG
#ifdef DEBUG_REG
void WRITE_VREG_DBG2(unsigned int adr, unsigned int val)
@@ -587,6 +596,9 @@ struct PIC_BUFFER_CONFIG_s {
u32 hw_decode_time;
u32 frame_size2; // For frame base mode
bool vframe_bound;
+
+ /* vdec sync. */
+ struct fence *fence;
} PIC_BUFFER_CONFIG;
enum BITSTREAM_PROFILE {
@@ -1201,6 +1213,8 @@ struct VP9Decoder_s {
union param_u vp9_param;
int sidebind_type;
int sidebind_channel_id;
+ bool enable_fence;
+ int fence_usage;
};
static int vp9_print(struct VP9Decoder_s *pbi,
@@ -1753,6 +1767,7 @@ int vp9_alloc_mmu(
cur_mmu_4k_number, pic_width, pic_height);
return -1;
}
+
return decoder_mmu_box_alloc_idx(
pbi->mmu_box,
cur_buf_idx,
@@ -2441,6 +2456,7 @@ int vp9_bufmgr_process(struct VP9Decoder_s *pbi, union param_u *params)
pr_info("get_free_fb error\r\n");
return -1;
}
+
#ifndef MV_USE_FIXED_BUF
#ifdef SUPPORT_FB_DECODING
if (pbi->used_stage_buf_num == 0) {
@@ -2523,6 +2539,7 @@ int vp9_bufmgr_process(struct VP9Decoder_s *pbi, union param_u *params)
}
cm->frame_type = params->p.frame_type;
cm->show_frame = params->p.show_frame;
+ cm->bit_depth = params->p.bit_depth;
cm->error_resilient_mode = params->p.error_resilient_mode;
@@ -2713,6 +2730,31 @@ int vp9_bufmgr_process(struct VP9Decoder_s *pbi, union param_u *params)
*(" last_intra_only %d last_show_frame %d last_frame_type %d)\n",
*cm->last_intra_only, cm->last_show_frame, cm->last_frame_type);
*/
+
+ if (pbi->enable_fence && cm->show_frame) {
+ struct PIC_BUFFER_CONFIG_s *pic = &cm->cur_frame->buf;
+ struct vdec_s *vdec = hw_to_vdec(pbi);
+
+ /* create fence for each buffers. */
+ ret = vdec_timeline_create_fence(&vdec->sync);
+ if (ret < 0)
+ return ret;
+
+ pic->fence = vdec->sync.fence;
+ pic->bit_depth = cm->bit_depth;
+ pic->slice_type = cm->frame_type;
+ pic->stream_offset = pbi->pre_stream_offset;
+
+ if (pbi->chunk) {
+ pic->pts = pbi->chunk->pts;
+ pic->pts64 = pbi->chunk->pts64;
+ pic->timestamp = pbi->chunk->timestamp;
+ }
+
+ /* post video vframe. */
+ prepare_display_buf(pbi, pic);
+ }
+
return 0;
}
@@ -2842,6 +2884,7 @@ int vp9_bufmgr_init(struct VP9Decoder_s *pbi, struct BuffInfo_s *buf_spec_i,
int vp9_bufmgr_postproc(struct VP9Decoder_s *pbi)
{
+ struct vdec_s *vdec = hw_to_vdec(pbi);
struct VP9_Common_s *cm = &pbi->common;
struct PIC_BUFFER_CONFIG_s sd;
@@ -2865,7 +2908,14 @@ int vp9_bufmgr_postproc(struct VP9Decoder_s *pbi)
if (vp9_get_raw_frame(pbi, &sd) == 0) {
/*pr_info("Display frame index %d\r\n", sd.index);*/
sd.stream_offset = pbi->pre_stream_offset;
- prepare_display_buf(pbi, &sd);
+
+ if (pbi->enable_fence) {
+ /* notify signal to wake up wq of fence. */
+ vdec_timeline_increase(&vdec->sync, 1);
+ } else {
+ prepare_display_buf(pbi, &sd);
+ }
+
pbi->pre_stream_offset = READ_VREG(HEVC_SHIFT_BYTE_COUNT);
}
@@ -6901,6 +6951,11 @@ static void vvp9_vf_put(struct vframe_s *vf, void *op_arg)
if (vf == (&pbi->vframe_dummy))
return;
+ if (pbi->enable_fence && vf->fence) {
+ vdec_fence_put(vf->fence);
+ vf->fence = NULL;
+ }
+
kfifo_put(&pbi->newframe_q, (const struct vframe_s *)vf);
pbi->vf_put_count++;
if (index < pbi->used_buf_num) {
@@ -7127,6 +7182,12 @@ static int prepare_display_buf(struct VP9Decoder_s *pbi,
}
}
+ if (pbi->enable_fence) {
+ /* fill fence information. */
+ if (pbi->fence_usage == FENCE_USE_FOR_DRIVER)
+ vf->fence = pic_config->fence;
+ }
+
#ifdef MULTI_INSTANCE_SUPPORT
if (vdec_frame_based(pvdec)) {
vf->pts = pic_config->pts;
@@ -7341,6 +7402,7 @@ static int prepare_display_buf(struct VP9Decoder_s *pbi,
vf->duration = 0;
}
update_vf_memhandle(pbi, vf, pic_config);
+
if (!(pic_config->y_crop_width == 196
&& pic_config->y_crop_height == 196
&& (debug & VP9_DEBUG_NO_TRIGGER_FRAME) == 0
@@ -9539,6 +9601,36 @@ static int amvdec_vp9_probe(struct platform_device *pdev)
return 0;
}
+static void vdec_fence_release(struct VP9Decoder_s *pbi,
+ struct vdec_sync *sync)
+{
+ ulong expires;
+ int i;
+
+ /* notify signal to wake up all fences. */
+ vdec_timeline_increase(sync, VF_POOL_SIZE);
+
+ expires = jiffies + msecs_to_jiffies(2000);
+ while (!check_objs_all_signaled(sync)) {
+ if (time_after(jiffies, expires)) {
+ pr_err("wait fence signaled timeout.\n");
+ break;
+ }
+ }
+
+ for (i = 0; i < VF_POOL_SIZE; i++) {
+ struct vframe_s *vf = &pbi->vfpool[i];
+
+ if (vf->fence) {
+ vdec_fence_put(vf->fence);
+ vf->fence = NULL;
+ }
+ }
+
+ /* decreases refcnt of timeline. */
+ vdec_timeline_put(sync);
+}
+
static int amvdec_vp9_remove(struct platform_device *pdev)
{
struct VP9Decoder_s *pbi = gHevc;
@@ -9569,6 +9661,9 @@ static int amvdec_vp9_remove(struct platform_device *pdev)
#endif
mem_map_mode = 0;
+ if (pbi->enable_fence)
+ vdec_fence_release(pbi, &vdec->sync);
+
vfree(pbi);
mutex_unlock(&vvp9_mutex);
@@ -10650,6 +10745,16 @@ static int ammvdec_vp9_probe(struct platform_device *pdev)
"parm_v4l_canvas_mem_mode",
&config_val) == 0)
pbi->mem_map_mode = config_val;
+
+ if (get_config_int(pdata->config,
+ "parm_enable_fence",
+ &config_val) == 0)
+ pbi->enable_fence = config_val;
+
+ if (get_config_int(pdata->config,
+ "parm_fence_usage",
+ &config_val) == 0)
+ pbi->fence_usage = config_val;
#endif
if (get_config_int(pdata->config, "HDRStaticInfo",
&vf_dp.present_flag) == 0
@@ -10698,6 +10803,10 @@ static int ammvdec_vp9_probe(struct platform_device *pdev)
pbi->double_write_mode = double_write_mode;
}
+ if (no_head & 0x10) {
+ pbi->no_head = (no_head & 0xf);
+ }
+
if (!pbi->is_used_v4l) {
pbi->mem_map_mode = mem_map_mode;
}
@@ -10707,6 +10816,20 @@ static int ammvdec_vp9_probe(struct platform_device *pdev)
pbi->max_pic_w, pbi->max_pic_h);
return -1;
}
+
+ if (force_config_fence) {
+ pbi->enable_fence = true;
+ pbi->fence_usage =
+ (force_config_fence >> 4) & 0xf;
+ if (force_config_fence & 0x2)
+ pbi->enable_fence = false;
+ vp9_print(pbi, 0, "enable fence: %d, fence usage: %d\n",
+ pbi->enable_fence, pbi->fence_usage);
+ }
+
+ if (pbi->enable_fence)
+ pdata->sync.usage = pbi->fence_usage;
+
pbi->mmu_enable = 1;
video_signal_type = pbi->video_signal_type;
@@ -10789,6 +10912,12 @@ static int ammvdec_vp9_probe(struct platform_device *pdev)
| CORE_MASK_COMBINE);
#endif
pbi->pic_list_init_done2 = true;
+
+ if (pbi->enable_fence) {
+ /* creat timeline. */
+ vdec_timeline_create(&pdata->sync, DRIVER_NAME);
+ }
+
return 0;
}
@@ -10826,6 +10955,8 @@ static int ammvdec_vp9_remove(struct platform_device *pdev)
}
}
+ if (pbi->enable_fence)
+ vdec_fence_release(pbi, &vdec->sync);
#ifdef DEBUG_PTS
pr_info("pts missed %ld, pts hit %ld, duration %d\n",
@@ -10932,7 +11063,7 @@ static int __init amvdec_vp9_driver_init_module(void)
if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_SM1) {
amvdec_vp9_profile.profile =
- "8k, 10bit, dwrite, compressed, no_head";
+ "8k, 10bit, dwrite, compressed";
} else if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_GXL
/*&& get_cpu_major_id() != MESON_CPU_MAJOR_ID_GXLX*/
&& get_cpu_major_id() != AM_MESON_CPU_MAJOR_ID_TXL) {
@@ -10946,10 +11077,10 @@ static int __init amvdec_vp9_driver_init_module(void)
} else {
if (vdec_is_support_4k())
amvdec_vp9_profile.profile =
- "4k, 10bit, dwrite, compressed, no_head";
+ "4k, 10bit, dwrite, compressed";
else
amvdec_vp9_profile.profile =
- "10bit, dwrite, compressed, no_head";
+ "10bit, dwrite, compressed";
}
} else {
@@ -11152,6 +11283,9 @@ MODULE_PARM_DESC(udebug_pause_decode_idx, "\n udebug_pause_decode_idx\n");
module_param(without_display_mode, uint, 0664);
MODULE_PARM_DESC(without_display_mode, "\n without_display_mode\n");
+module_param(force_config_fence, uint, 0664);
+MODULE_PARM_DESC(force_config_fence, "\n force enable fence\n");
+
module_init(amvdec_vp9_driver_init_module);
module_exit(amvdec_vp9_driver_remove_module);