author | Nanxin 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) |
commit | 677ab50da749119274325f12855b0f3ac0cfb88b (patch) | |
tree | 745a2bd261c5900c31a1891d65d2fb1fd1ef86b0 | |
parent | a1ce75ba81641dfbaffd42c9ed333c6a69be8ca1 (diff) | |
download | media_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>
-rw-r--r-- | drivers/Makefile | 1 | ||||
-rw-r--r-- | drivers/fake_video_out/Makefile | 1 | ||||
-rw-r--r-- | drivers/fake_video_out/fake_video.c | 221 | ||||
-rw-r--r-- | drivers/frame_provider/decoder/utils/Makefile | 1 | ||||
-rw-r--r-- | drivers/frame_provider/decoder/utils/vdec.c | 19 | ||||
-rw-r--r-- | drivers/frame_provider/decoder/utils/vdec.h | 2 | ||||
-rw-r--r-- | drivers/frame_provider/decoder/utils/vdec_sync.c | 377 | ||||
-rw-r--r-- | drivers/frame_provider/decoder/utils/vdec_sync.h | 70 | ||||
-rw-r--r-- | drivers/frame_provider/decoder/vp9/vvp9.c | 142 |
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); |