vp9: add decoder fence to save time of video pipeline. [2/2]
authorNanxin Qin <nanxin.qin@amlogic.com>
Tue, 21 Apr 2020 07:04:09 +0000 (15:04 +0800)
committerHui Zhang <hui.zhang@amlogic.com>
Fri, 29 May 2020 05:26:56 +0000 (22:26 -0700)
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>
drivers/Makefile
drivers/fake_video_out/Makefile [new file with mode: 0644]
drivers/fake_video_out/fake_video.c [new file with mode: 0644]
drivers/frame_provider/decoder/utils/Makefile
drivers/frame_provider/decoder/utils/vdec.c
drivers/frame_provider/decoder/utils/vdec.h
drivers/frame_provider/decoder/utils/vdec_sync.c [new file with mode: 0644]
drivers/frame_provider/decoder/utils/vdec_sync.h [new file with mode: 0644]
drivers/frame_provider/decoder/vp9/vvp9.c

index 018c8feff897acdfb7aa44422f75b6745bec9825..1acca52ac6774e7d1808180702b0333df14774b2 100644 (file)
@@ -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 (file)
index 0000000..1dd937c
--- /dev/null
@@ -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 (file)
index 0000000..586d350
--- /dev/null
@@ -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>");
+
index 07edbe318e9a1c500644d42aa58fe6a3091045da..c587fd99ff64832b2dd6eea8d3c25d2da9ce7239 100644 (file)
@@ -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
 
index c81f1deb0bd63824c4a8f5d31c9d9de6bb37978a..345d4a24fa786ee82ed96eb6eb3612f55196910e 100644 (file)
@@ -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",
 };
index f99185024b4f98d0f8c6e030e4323cf3d5406b6f..0a526d0addbdd1567c4032a91402e2b8c502093a 100644 (file)
@@ -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 (file)
index 0000000..014b536
--- /dev/null
@@ -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 (file)
index 0000000..a57f0ab
--- /dev/null
@@ -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);
+
index 051736a6d7d5cb5bc665c76c5d56d7ae00f84262..ce898e0b0aeeb0f237b68cf2249227ed1546b13e 100644 (file)
@@ -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);