blob: e0e9b1645f7c1b4f5454ed043b322cda9d9ca906 [file] [log] [blame]
/*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* 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.
*
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Description:
*/
#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>
#include <linux/kthread.h>
#define RECEIVER_NAME "fake-amvideo"
#define DEVICE_NAME "fake-amvideo"
#define TUNNEL_MODE (0)
#define NON_TUNNEL_MODE (1)
static u32 display_mode = TUNNEL_MODE;
module_param(display_mode, uint, 0664);
MODULE_PARM_DESC(display_mode, "\n display_mode\n");
static struct device *amvideo_dev = NULL;
struct timer_list timer;
struct task_struct *task;
bool thread_stop = false;
atomic_t frame_count;
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: {
atomic_set(&frame_count, 0);
pr_info("VFRAME_EVENT_PROVIDER_UNREG\n");
break;
}
case VFRAME_EVENT_PROVIDER_START: {
pr_info("VFRAME_EVENT_PROVIDER_START\n");
break;
}
case VFRAME_EVENT_PROVIDER_QUREY_STATE: {
break;
}
case VFRAME_EVENT_PROVIDER_VFRAME_READY: {
pr_info("VFRAME_EVENT_PROVIDER_VFRAME_READY\n");
atomic_inc(&frame_count);
break;
}
default:
break;
}
return 0;
}
static void display_timer_func(unsigned long arg)
{
struct vframe_s *vf = NULL;
if (display_mode != TUNNEL_MODE)
goto out;
vf = video_vf_peek();
if (!vf) {
goto out;
}
if (vf->fence) {
if (vdec_fence_status_get(vf->fence) == 1) {
pr_info("[VDEC-FENCE]: Display, idx: %d\n",
vf->index & 0xff);
vf = video_vf_get();
video_vf_put(vf);
} else {
pr_err("[VDEC-FENCE]: Display invalid, fence status err.\n");
}
}
out:
mod_timer(&timer, jiffies + msecs_to_jiffies(16));
}
static int display_thread(void *data)
{
struct vframe_s *vf = NULL;
for (;;) {
if (thread_stop)
break;
if ((atomic_read(&frame_count) == 0) ||
display_mode != NON_TUNNEL_MODE)
continue;
if (video_vf_peek()) {
vf = video_vf_get();
if (!vf) {
pr_err("receiver vf err.\n");
break;
}
atomic_dec(&frame_count);
if (vf->fence) {
u64 timestamp = local_clock();
/* fence waiting until frame ready. */
vdec_fence_wait(vf->fence, msecs_to_jiffies(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() - timestamp);
} else {
pr_err("[VDEC-FENCE]: Display invalid, fence status err.\n");
}
}
video_vf_put(vf);
}
msleep(16);
}
return 0;
}
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 (23 + (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);
atomic_set(&frame_count, 0);
init_timer(&timer);
//timer.data = 0;
timer.function = display_timer_func;
timer.expires = jiffies + HZ;
add_timer(&timer);
thread_stop = false;
task = kthread_run(display_thread, NULL, "aml-%s", "fake-video-thread");
if (IS_ERR(task)) {
ret = PTR_ERR(task);
pr_err("Failed to creat display thread, ret: %d.\n", ret);
goto err;
}
return 0;
err:
unregister_chrdev(FAKEVIDEO_MAJOR, DEVICE_NAME);
err1:
class_unregister(&amvideo_class);
return ret;
}
static void __exit fake_video_exit(void)
{
thread_stop = true;
kthread_stop(task);
del_timer_sync(&timer);
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>");