blob: 3b217f276da2edb856b3f0cbb1987684eddb5373 [file] [log] [blame]
/*
* drivers/amlogic/media/stream_input/amports/amstream.c
*
* Copyright (C) 2016 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.
*
*/
#define DEBUG
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <uapi/linux/major.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/amlogic/media/utils/amstream.h>
#include <linux/amlogic/media/utils/vformat.h>
#include <linux/amlogic/media/utils/aformat.h>
#include <linux/amlogic/media/frame_sync/tsync.h>
#include <linux/amlogic/media/frame_sync/ptsserv.h>
#include <linux/amlogic/media/frame_sync/timestamp.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/dma-mapping.h>
#include <linux/dma-contiguous.h>
#include <linux/uaccess.h>
#include <linux/clk.h>
#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
/* #include <mach/mod_gate.h> */
/* #include <mach/power_gate.h> */
#endif
#include "../amports/streambuf.h"
#include "../amports/streambuf_reg.h"
#include "../parser/tsdemux.h"
#include "../parser/psparser.h"
#include "../parser/esparser.h"
#include "../../frame_provider/decoder/utils/vdec.h"
#include "adec.h"
#include "../parser/rmparser.h"
#include "amports_priv.h"
#include <linux/amlogic/media/utils/amports_config.h>
#include <linux/amlogic/media/frame_sync/tsync_pcr.h>
#include "../amports/thread_rw.h"
#include <linux/firmware.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/libfdt_env.h>
#include <linux/of_reserved_mem.h>
#include <linux/reset.h>
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
#endif
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include <linux/amlogic/media/codec_mm/configs.h>
#include "../../frame_provider/decoder/utils/firmware.h"
#include "../../common/chips/chips.h"
#include "../../common/chips/decoder_cpu_ver_info.h"
#include "../subtitle/subtitle.h"
#include "stream_buffer_base.h"
#include "../../frame_provider/decoder/utils/vdec_feature.h"
//#define G12A_BRINGUP_DEBUG
#define CONFIG_AM_VDEC_REAL //DEBUG_TMP
#define DEVICE_NAME "amstream-dev"
#define DRIVER_NAME "amstream"
#define MODULE_NAME "amstream"
#define MAX_AMSTREAM_PORT_NUM ARRAY_SIZE(ports)
u32 amstream_port_num;
u32 amstream_buf_num;
u32 amstream_audio_reset = 0;
#if 0
#if MESON_CPU_TYPE == MESON_CPU_TYPE_MESONG9TV
#define NO_VDEC2_INIT 1
#elif MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD
#define NO_VDEC2_INIT IS_MESON_M8M2_CPU
#endif
#endif
#define NO_VDEC2_INIT 1
#define DEFAULT_VIDEO_BUFFER_SIZE (1024 * 1024 * 3)
#define DEFAULT_VIDEO_BUFFER_SIZE_4K (1024 * 1024 * 6)
#define DEFAULT_VIDEO_BUFFER_SIZE_TVP (1024 * 1024 * 10)
#define DEFAULT_VIDEO_BUFFER_SIZE_4K_TVP (1024 * 1024 * 15)
#define DEFAULT_AUDIO_BUFFER_SIZE (1024*768*2)
#define DEFAULT_SUBTITLE_BUFFER_SIZE (1024*256)
static int def_4k_vstreambuf_sizeM =
(DEFAULT_VIDEO_BUFFER_SIZE_4K >> 20);
static int def_vstreambuf_sizeM =
(DEFAULT_VIDEO_BUFFER_SIZE >> 20);
static int slow_input;
/* #define DATA_DEBUG */
static int use_bufferlevelx10000 = 10000;
static int reset_canuse_buferlevel(int level);
static struct platform_device *amstream_pdev;
struct device *amports_get_dma_device(void)
{
return &amstream_pdev->dev;
}
EXPORT_SYMBOL(amports_get_dma_device);
#ifdef DATA_DEBUG
#include <linux/fs.h>
#define DEBUG_FILE_NAME "/sdcard/debug.tmp"
static struct file *debug_filp;
static loff_t debug_file_pos;
void debug_file_write(const char __user *buf, size_t count)
{
mm_segment_t old_fs;
if (!debug_filp)
return;
old_fs = get_fs();
set_fs(KERNEL_DS);
if (count != vfs_write(debug_filp, buf, count, &debug_file_pos))
pr_err("Failed to write debug file\n");
set_fs(old_fs);
}
#endif
static int amstream_open(struct inode *inode, struct file *file);
static int amstream_release(struct inode *inode, struct file *file);
static long amstream_ioctl(struct file *file, unsigned int cmd, ulong arg);
#ifdef CONFIG_COMPAT
static long amstream_compat_ioctl
(struct file *file, unsigned int cmd, ulong arg);
#endif
static ssize_t amstream_vbuf_write
(struct file *file, const char *buf, size_t count, loff_t *ppos);
static ssize_t amstream_vframe_write
(struct file *file, const char *buf, size_t count, loff_t *ppos);
static ssize_t amstream_abuf_write
(struct file *file, const char *buf, size_t count, loff_t *ppos);
static ssize_t amstream_mpts_write
(struct file *file, const char *buf, size_t count, loff_t *ppos);
static ssize_t amstream_mpps_write
(struct file *file, const char *buf, size_t count, loff_t *ppos);
static ssize_t amstream_sub_read
(struct file *file, char *buf, size_t count, loff_t *ppos);
static ssize_t amstream_sub_write
(struct file *file, const char *buf, size_t count, loff_t *ppos);
static unsigned int amstream_sub_poll
(struct file *file, poll_table *wait_table);
static unsigned int amstream_userdata_poll
(struct file *file, poll_table *wait_table);
static int (*amstream_adec_status)
(struct adec_status *astatus);
#ifdef CONFIG_AM_VDEC_REAL
static ssize_t amstream_mprm_write
(struct file *file, const char *buf, size_t count, loff_t *ppos);
#endif
static const struct file_operations vbuf_fops = {
.owner = THIS_MODULE,
.open = amstream_open,
.release = amstream_release,
.write = amstream_vbuf_write,
.unlocked_ioctl = amstream_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = amstream_compat_ioctl,
#endif
};
static const struct file_operations vframe_fops = {
.owner = THIS_MODULE,
.open = amstream_open,
.release = amstream_release,
.write = amstream_vframe_write,
.unlocked_ioctl = amstream_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = amstream_compat_ioctl,
#endif
};
static const struct file_operations abuf_fops = {
.owner = THIS_MODULE,
.open = amstream_open,
.release = amstream_release,
.write = amstream_abuf_write,
.unlocked_ioctl = amstream_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = amstream_compat_ioctl,
#endif
};
static const struct file_operations mpts_fops = {
.owner = THIS_MODULE,
.open = amstream_open,
.release = amstream_release,
.write = amstream_mpts_write,
.unlocked_ioctl = amstream_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = amstream_compat_ioctl,
#endif
};
static const struct file_operations mpps_fops = {
.owner = THIS_MODULE,
.open = amstream_open,
.release = amstream_release,
.write = amstream_mpps_write,
.unlocked_ioctl = amstream_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = amstream_compat_ioctl,
#endif
};
static const struct file_operations mprm_fops = {
.owner = THIS_MODULE,
.open = amstream_open,
.release = amstream_release,
#ifdef CONFIG_AM_VDEC_REAL
.write = amstream_mprm_write,
#endif
.unlocked_ioctl = amstream_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = amstream_compat_ioctl,
#endif
};
static const struct file_operations sub_fops = {
.owner = THIS_MODULE,
.open = amstream_open,
.release = amstream_release,
.write = amstream_sub_write,
.unlocked_ioctl = amstream_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = amstream_compat_ioctl,
#endif
};
static const struct file_operations sub_read_fops = {
.owner = THIS_MODULE,
.open = amstream_open,
.release = amstream_release,
.read = amstream_sub_read,
.poll = amstream_sub_poll,
.unlocked_ioctl = amstream_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = amstream_compat_ioctl,
#endif
};
static const struct file_operations userdata_fops = {
.owner = THIS_MODULE,
.open = amstream_open,
.release = amstream_release,
.poll = amstream_userdata_poll,
.unlocked_ioctl = amstream_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = amstream_compat_ioctl,
#endif
};
static const struct file_operations amstream_fops = {
.owner = THIS_MODULE,
.open = amstream_open,
.release = amstream_release,
.unlocked_ioctl = amstream_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = amstream_compat_ioctl,
#endif
};
/**************************************************/
static struct audio_info audio_dec_info;
static struct class *amstream_dev_class;
static DEFINE_MUTEX(amstream_mutex);
atomic_t subdata_ready = ATOMIC_INIT(0);
static int sub_type;
static int sub_port_inited;
/* wait queue for poll */
static wait_queue_head_t amstream_sub_wait;
atomic_t userdata_ready = ATOMIC_INIT(0);
static int userdata_length;
static wait_queue_head_t amstream_userdata_wait;
#define USERDATA_FIFO_NUM 1024
static struct userdata_poc_info_t *userdata_poc_info;
static int userdata_poc_ri, userdata_poc_wi;
static int last_read_wi;
/*bit 1 force dual layer
*bit 2 force frame mode
*/
static u32 force_dv_mode;
static DEFINE_MUTEX(userdata_mutex);
static struct stream_port_s ports[] = {
{
.name = "amstream_vbuf",
.type = PORT_TYPE_ES | PORT_TYPE_VIDEO,
.fops = &vbuf_fops,
},
#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
{
.name = "amstream_vbuf_sched",
.type = PORT_TYPE_ES | PORT_TYPE_VIDEO |
PORT_TYPE_DECODER_SCHED,
.fops = &vbuf_fops,
},
{
.name = "amstream_vframe",
.type = PORT_TYPE_ES | PORT_TYPE_VIDEO |
PORT_TYPE_FRAME | PORT_TYPE_DECODER_SCHED,
.fops = &vframe_fops,
},
#endif
{
.name = "amstream_abuf",
.type = PORT_TYPE_ES | PORT_TYPE_AUDIO,
.fops = &abuf_fops,
},
{
.name = "amstream_mpts",
.type = PORT_TYPE_MPTS | PORT_TYPE_VIDEO |
PORT_TYPE_AUDIO | PORT_TYPE_SUB,
.fops = &mpts_fops,
},
#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
{
.name = "amstream_mpts_sched",
.type = PORT_TYPE_MPTS | PORT_TYPE_VIDEO |
PORT_TYPE_AUDIO | PORT_TYPE_SUB |
PORT_TYPE_DECODER_SCHED,
.fops = &mpts_fops,
},
#endif
{
.name = "amstream_mpps",
.type = PORT_TYPE_MPPS | PORT_TYPE_VIDEO |
PORT_TYPE_AUDIO | PORT_TYPE_SUB,
.fops = &mpps_fops,
},
{
.name = "amstream_rm",
.type = PORT_TYPE_RM | PORT_TYPE_VIDEO | PORT_TYPE_AUDIO,
.fops = &mprm_fops,
},
{
.name = "amstream_sub",
.type = PORT_TYPE_SUB,
.fops = &sub_fops,
},
{
.name = "amstream_sub_read",
.type = PORT_TYPE_SUB_RD,
.fops = &sub_read_fops,
},
{
.name = "amstream_userdata",
.type = PORT_TYPE_USERDATA,
.fops = &userdata_fops,
},
{
.name = "amstream_hevc",
.type = PORT_TYPE_ES | PORT_TYPE_VIDEO | PORT_TYPE_HEVC,
.fops = &vbuf_fops,
.vformat = VFORMAT_HEVC,
},
#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
{
.name = "amstream_hevc_frame",
.type = PORT_TYPE_ES | PORT_TYPE_VIDEO | PORT_TYPE_HEVC |
PORT_TYPE_FRAME | PORT_TYPE_DECODER_SCHED,
.fops = &vframe_fops,
.vformat = VFORMAT_HEVC,
},
{
.name = "amstream_hevc_sched",
.type = PORT_TYPE_ES | PORT_TYPE_VIDEO | PORT_TYPE_HEVC |
PORT_TYPE_DECODER_SCHED,
.fops = &vbuf_fops,
.vformat = VFORMAT_HEVC,
},
#ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_DOLBYVISION
{
.name = "amstream_dves_avc",
.type = PORT_TYPE_ES | PORT_TYPE_VIDEO |
PORT_TYPE_DECODER_SCHED | PORT_TYPE_DUALDEC,
.fops = &vbuf_fops,
},
{
.name = "amstream_dves_hevc",
.type = PORT_TYPE_ES | PORT_TYPE_VIDEO | PORT_TYPE_HEVC |
PORT_TYPE_DECODER_SCHED | PORT_TYPE_DUALDEC,
.fops = &vbuf_fops,
.vformat = VFORMAT_HEVC,
},
{
.name = "amstream_dves_avc_frame",
.type = PORT_TYPE_ES | PORT_TYPE_VIDEO | PORT_TYPE_FRAME |
PORT_TYPE_DECODER_SCHED | PORT_TYPE_DUALDEC,
.fops = &vframe_fops,
},
{
.name = "amstream_dves_hevc_frame",
.type = PORT_TYPE_ES | PORT_TYPE_VIDEO | PORT_TYPE_HEVC | PORT_TYPE_FRAME |
PORT_TYPE_DECODER_SCHED | PORT_TYPE_DUALDEC,
.fops = &vframe_fops,
.vformat = VFORMAT_HEVC,
},
{
.name = "amstream_dves_av1",
.type = PORT_TYPE_ES | PORT_TYPE_VIDEO | PORT_TYPE_HEVC | PORT_TYPE_FRAME |
PORT_TYPE_DECODER_SCHED | PORT_TYPE_DUALDEC,
.fops = &vframe_fops,
.vformat = VFORMAT_AV1,
},
#endif
#endif
};
static struct stream_buf_s bufs[BUF_MAX_NUM] = {
{
.reg_base = VLD_MEM_VIFIFO_REG_BASE,
.type = BUF_TYPE_VIDEO,
.buf_start = 0,
.buf_size = DEFAULT_VIDEO_BUFFER_SIZE,
.default_buf_size = DEFAULT_VIDEO_BUFFER_SIZE,
.first_tstamp = INVALID_PTS
},
{
.reg_base = AIU_MEM_AIFIFO_REG_BASE,
.type = BUF_TYPE_AUDIO,
.buf_start = 0,
.buf_size = DEFAULT_AUDIO_BUFFER_SIZE,
.default_buf_size = DEFAULT_AUDIO_BUFFER_SIZE,
.first_tstamp = INVALID_PTS
},
{
.reg_base = 0,
.type = BUF_TYPE_SUBTITLE,
.buf_start = 0,
.buf_size = DEFAULT_SUBTITLE_BUFFER_SIZE,
.default_buf_size = DEFAULT_SUBTITLE_BUFFER_SIZE,
.first_tstamp = INVALID_PTS
},
{
.reg_base = 0,
.type = BUF_TYPE_USERDATA,
.buf_start = 0,
.buf_size = 0,
.first_tstamp = INVALID_PTS
},
{
.reg_base = HEVC_STREAM_REG_BASE,
.type = BUF_TYPE_HEVC,
.buf_start = 0,
.buf_size = DEFAULT_VIDEO_BUFFER_SIZE_4K,
.default_buf_size = DEFAULT_VIDEO_BUFFER_SIZE_4K,
.first_tstamp = INVALID_PTS
},
};
struct stream_buf_s *get_buf_by_type(u32 type)
{
if (PTS_TYPE_VIDEO == type)
return &bufs[BUF_TYPE_VIDEO];
if (PTS_TYPE_AUDIO == type)
return &bufs[BUF_TYPE_AUDIO];
if (has_hevc_vdec()) {
if (PTS_TYPE_HEVC == type)
return &bufs[BUF_TYPE_HEVC];
}
return NULL;
}
void set_sample_rate_info(int arg)
{
audio_dec_info.sample_rate = arg;
audio_dec_info.valid = 1;
}
void set_ch_num_info(int arg)
{
audio_dec_info.channels = arg;
}
struct audio_info *get_audio_info(void)
{
return &audio_dec_info;
}
EXPORT_SYMBOL(get_audio_info);
static void amstream_change_vbufsize(struct port_priv_s *priv,
struct stream_buf_s *pvbuf)
{
if (pvbuf->buf_start != 0 || pvbuf->ext_buf_addr != 0) {
pr_info("streambuf is alloced, buf_start 0x%lx, extbuf 0x%lx\n",
pvbuf->buf_start, pvbuf->ext_buf_addr);
return;
}
if (priv->port->is_4k) {
pvbuf->buf_size = def_4k_vstreambuf_sizeM * SZ_1M;
if (priv->vdec->port_flag & PORT_FLAG_DRM)
pvbuf->buf_size = DEFAULT_VIDEO_BUFFER_SIZE_4K_TVP;
if ((pvbuf->buf_size > 30 * SZ_1M) &&
(codec_mm_get_total_size() < 220 * SZ_1M)) {
/*if less than 250M, used 20M for 4K & 265*/
pvbuf->buf_size = pvbuf->buf_size >> 1;
}
} else if (pvbuf->buf_size > def_vstreambuf_sizeM * SZ_1M) {
if (priv->vdec->port_flag & PORT_FLAG_DRM)
pvbuf->buf_size = DEFAULT_VIDEO_BUFFER_SIZE_TVP;
} else {
pvbuf->buf_size = def_vstreambuf_sizeM * SZ_1M;
if (priv->vdec->port_flag & PORT_FLAG_DRM)
pvbuf->buf_size = DEFAULT_VIDEO_BUFFER_SIZE_TVP;
}
reset_canuse_buferlevel(10000);
}
static bool port_get_inited(struct port_priv_s *priv)
{
struct stream_port_s *port = priv->port;
if (port->type & PORT_TYPE_VIDEO) {
struct vdec_s *vdec = priv->vdec;
return vdec ? vdec->port_flag & PORT_FLAG_INITED : 0;
}
return port->flag & PORT_FLAG_INITED;
}
static void port_set_inited(struct port_priv_s *priv)
{
struct stream_port_s *port = priv->port;
if (port->type & PORT_TYPE_VIDEO) {
struct vdec_s *vdec = priv->vdec;
vdec->port_flag |= PORT_FLAG_INITED;
port->flag |= PORT_FLAG_INITED;
pr_info("vdec->port_flag=0x%x, port_flag=0x%x\n",
vdec->port_flag, port->flag);
} else
port->flag |= PORT_FLAG_INITED;
}
static void video_port_release(struct port_priv_s *priv,
struct stream_buf_s *pbuf, int release_num)
{
struct vdec_s *vdec = priv->vdec;
struct vdec_s *slave = NULL;
if (!vdec)
return;
switch (release_num) {
default:
/*fallthrough*/
case 0: /*release all */
case 3:
if (vdec->slave)
slave = vdec->slave;
vdec_release(vdec);
if (slave)
vdec_release(slave);
priv->vdec = NULL;
/*fallthrough*/
case 1:
;
}
}
static int video_port_init(struct port_priv_s *priv,
struct stream_buf_s *pbuf)
{
int r;
struct stream_port_s *port = priv->port;
struct vdec_s *vdec = priv->vdec;
if ((vdec->port_flag & PORT_FLAG_VFORMAT) == 0) {
pr_err("vformat not set\n");
return -EPERM;
}
if (vdec_dual(vdec) && vdec_secure(vdec) && (vdec->slave)) {
/*copy drm flags for slave dec.*/
vdec->slave->port_flag |= PORT_FLAG_DRM;
}
if (port->vformat == VFORMAT_H264_4K2K ||
(priv->vdec->sys_info->height *
priv->vdec->sys_info->width) > 1920*1088) {
port->is_4k = true;
if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_TXLX
&& port->vformat == VFORMAT_H264) {
vdec_poweron(VDEC_HEVC);
}
} else {
port->is_4k = false;
}
if (port->type & PORT_TYPE_FRAME) {
r = vdec_init(vdec,
(priv->vdec->sys_info->height *
priv->vdec->sys_info->width) > 1920*1088, false);
if (r < 0) {
pr_err("video_port_init %d, vdec_init failed\n",
__LINE__);
return r;
}
#if 0
if (vdec_dual(vdec)) {
if (port->vformat == VFORMAT_AV1) /* av1 dv only single layer */
return 0;
r = vdec_init(vdec->slave,
(priv->vdec->sys_info->height *
priv->vdec->sys_info->width) > 1920*1088);
if (r < 0) {
vdec_release(vdec);
pr_err("video_port_init %d, vdec_init failed\n",
__LINE__);
return r;
}
}
#endif
return 0;
}
amstream_change_vbufsize(priv, pbuf);
if (has_hevc_vdec()) {
if (port->type & PORT_TYPE_MPTS) {
if (pbuf->type == BUF_TYPE_HEVC)
vdec_poweroff(VDEC_1);
else
vdec_poweroff(VDEC_HEVC);
}
}
/* todo: set path based on port flag */
r = vdec_init(vdec,
(priv->vdec->sys_info->height *
priv->vdec->sys_info->width) > 1920*1088, false);
if (r < 0) {
pr_err("video_port_init %d, vdec_init failed\n", __LINE__);
goto err;
}
if (vdec_dual(vdec)) {
r = vdec_init(vdec->slave,
(priv->vdec->sys_info->height *
priv->vdec->sys_info->width) > 1920*1088, false);
if (r < 0) {
pr_err("video_port_init %d, vdec_init failed\n", __LINE__);
goto err;
}
}
return 0;
err:
if (vdec->slave)
vdec_release(vdec->slave);
if (vdec)
vdec_release(vdec);
priv->vdec = NULL;
return r;
}
static void audio_port_release(struct stream_port_s *port,
struct stream_buf_s *pbuf, int release_num)
{
switch (release_num) {
default:
/*fallthrough*/
case 0: /*release all */
/*fallthrough*/
case 4:
esparser_release(pbuf);
/*fallthrough*/
case 3:
adec_release(port->vformat);
/*fallthrough*/
case 2:
stbuf_release(pbuf);
/*fallthrough*/
case 1:
;
}
amstream_audio_reset = 0;
return;
}
static int audio_port_reset(struct stream_port_s *port,
struct stream_buf_s *pbuf)
{
int r;
if ((port->flag & PORT_FLAG_AFORMAT) == 0) {
pr_err("aformat not set\n");
return 0;
}
pr_info("audio port reset, flag:0x%x\n", port->flag);
if ((port->flag & PORT_FLAG_INITED) == 0) {
pr_info("audio port not inited,return\n");
return 0;
}
pr_info("audio_port_reset begin\n");
pts_stop(PTS_TYPE_AUDIO);
stbuf_release(pbuf);
r = stbuf_init(pbuf, NULL);
if (r < 0) {
return r;
}
r = adec_init(port);
if (r < 0) {
audio_port_release(port, pbuf, 2);
return r;
}
if (port->type & PORT_TYPE_ES)
esparser_audio_reset_s(pbuf);
if (port->type & PORT_TYPE_MPTS)
tsdemux_audio_reset();
if (port->type & PORT_TYPE_MPPS)
psparser_audio_reset();
#ifdef CONFIG_AM_VDEC_REAL
if (port->type & PORT_TYPE_RM)
rm_audio_reset();
#endif
pbuf->flag |= BUF_FLAG_IN_USE;
amstream_audio_reset = 1;
r = pts_start(PTS_TYPE_AUDIO);
//clear audio break flag after reset
//tsync_audio_break(0);
pr_info("audio_port_reset done\n");
return r;
}
static int sub_port_reset(struct stream_port_s *port,
struct stream_buf_s *pbuf)
{
int r;
port->flag &= (~PORT_FLAG_INITED);
stbuf_release(pbuf);
r = stbuf_init(pbuf, NULL);
if (r < 0)
return r;
if (port->type & PORT_TYPE_MPTS)
tsdemux_sub_reset();
if (port->type & PORT_TYPE_MPPS)
psparser_sub_reset();
if (port->sid == 0xffff) { /* es sub */
esparser_sub_reset();
pbuf->flag |= BUF_FLAG_PARSER;
}
pbuf->flag |= BUF_FLAG_IN_USE;
port->flag |= PORT_FLAG_INITED;
return 0;
}
static int audio_port_init(struct stream_port_s *port,
struct stream_buf_s *pbuf)
{
int r;
if ((port->flag & PORT_FLAG_AFORMAT) == 0) {
pr_err("aformat not set\n");
return 0;
}
r = stbuf_init(pbuf, NULL);
if (r < 0)
return r;
r = adec_init(port);
if (r < 0) {
audio_port_release(port, pbuf, 2);
return r;
}
if (port->type & PORT_TYPE_ES) {
r = esparser_init(pbuf, NULL);
if (r < 0) {
audio_port_release(port, pbuf, 3);
return r;
}
}
pbuf->flag |= BUF_FLAG_IN_USE;
return 0;
}
static void sub_port_release(struct stream_port_s *port,
struct stream_buf_s *pbuf)
{
if ((port->sid == 0xffff) &&
((port->type & (PORT_TYPE_MPPS | PORT_TYPE_MPTS)) == 0)) {
/* this is es sub */
esparser_release(pbuf);
}
stbuf_release(pbuf);
sub_port_inited = 0;
}
static int sub_port_init(struct stream_port_s *port, struct stream_buf_s *pbuf)
{
int r;
r = stbuf_init(pbuf, NULL);
if (r < 0)
return r;
if ((port->flag & PORT_FLAG_SID) == 0) {
pr_err("subtitle id not set\n");
return 0;
}
if ((port->sid == 0xffff) &&
((port->type & (PORT_TYPE_MPPS | PORT_TYPE_MPTS)) == 0)) {
/* es sub */
r = esparser_init(pbuf, NULL);
if (r < 0) {
sub_port_release(port, pbuf);
return r;
}
}
sub_port_inited = 1;
return 0;
}
static void amstream_user_buffer_init(void)
{
struct stream_buf_s *pubuf = &bufs[BUF_TYPE_USERDATA];
pubuf->buf_size = 0;
pubuf->buf_start = 0;
pubuf->buf_wp = 0;
pubuf->buf_rp = 0;
}
#if 1
/*DDD*/
struct stream_buf_s *get_vbuf(void)
{
return &bufs[BUF_TYPE_VIDEO];
}
EXPORT_SYMBOL(get_vbuf);
#endif
static int amstream_port_init(struct port_priv_s *priv)
{
int r = 0;
struct stream_buf_s *pvbuf = &bufs[BUF_TYPE_VIDEO];
struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO];
struct stream_buf_s *psbuf = &bufs[BUF_TYPE_SUBTITLE];
struct stream_port_s *port = priv->port;
struct vdec_s *vdec = priv->vdec;
r = vdec_resource_checking(vdec);
if (r < 0)
return r;
mutex_lock(&amstream_mutex);
if ((get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_G12A) &&
(get_cpu_major_id() < AM_MESON_CPU_MAJOR_ID_SC2)) {
r = check_efuse_chip(port->vformat);
if (r) {
pr_info("No support video format %d.\n", port->vformat);
mutex_unlock(&amstream_mutex);
return 0;
}
}
/* try to reload the fw.*/
r = video_fw_reload(FW_LOAD_TRY);
if (r)
pr_err("the firmware reload fail.\n");
stbuf_fetch_init();
amstream_user_buffer_init();
if (port_get_inited(priv)) {
mutex_unlock(&amstream_mutex);
return 0;
}
if ((port->type & PORT_TYPE_AUDIO) &&
(port->flag & PORT_FLAG_AFORMAT)) {
r = audio_port_init(port, pabuf);
if (r < 0) {
pr_err("audio_port_init failed\n");
goto error1;
}
}
if ((port->type & PORT_TYPE_VIDEO) &&
(port->flag & PORT_FLAG_VFORMAT)) {
if (vdec_stream_based(vdec)) {
struct stream_buf_ops *ops = NULL;
struct parser_args pars = {
.vid = (port->flag & PORT_FLAG_VID) ? port->vid : 0xffff,
.aid = (port->flag & PORT_FLAG_AID) ? port->aid : 0xffff,
.sid = (port->flag & PORT_FLAG_SID) ? port->sid : 0xffff,
.pcrid = (port->pcr_inited == 1) ? port->pcrid : 0xffff,
};
if (port->type & PORT_TYPE_MPTS) {
ops = get_tsparser_stbuf_ops();
} else if (port->type & PORT_TYPE_MPPS) {
ops = get_psparser_stbuf_ops();
} else {
ops = !vdec_single(vdec) ?
get_stbuf_ops() :
get_esparser_stbuf_ops();
/* def used stbuf with parser if the feature disable. */
if (!is_support_no_parser())
ops = get_esparser_stbuf_ops();
else if (vdec->format == VFORMAT_H264MVC ||
vdec->format == VFORMAT_VC1)
ops = get_stbuf_ops();
}
r = stream_buffer_base_init(&vdec->vbuf, ops, &pars);
if (r) {
mutex_unlock(&priv->mutex);
pr_err("stream buffer base init failed\n");
goto error2;
}
}
mutex_lock(&priv->mutex);
r = video_port_init(priv, &vdec->vbuf);
if (r < 0) {
mutex_unlock(&priv->mutex);
pr_err("video_port_init failed\n");
goto error2;
}
mutex_unlock(&priv->mutex);
}
if ((port->type & PORT_TYPE_MPTS) &&
!(port->flag & PORT_FLAG_VFORMAT)) {
r = tsdemux_init(0xffff,
(port->flag & PORT_FLAG_AID) ? port->aid : 0xffff,
(port->flag & PORT_FLAG_SID) ? port->sid : 0xffff,
(port->pcr_inited == 1) ? port->pcrid : 0xffff,
0, vdec);
if (r < 0) {
pr_err("tsdemux_init failed\n");
goto error3;
}
tsync_pcr_start();
}
if ((port->type & PORT_TYPE_SUB) && (port->flag & PORT_FLAG_SID)) {
r = sub_port_init(port, psbuf);
if (r < 0) {
pr_err("sub_port_init failed\n");
goto error4;
}
}
#ifdef CONFIG_AM_VDEC_REAL
if (port->type & PORT_TYPE_RM) {
rm_set_vasid(
(port->flag & PORT_FLAG_VID) ? port->vid : 0xffff,
(port->flag & PORT_FLAG_AID) ? port->aid : 0xffff);
}
#endif
#if 1 /* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6TVD */
if (!NO_VDEC2_INIT) {
if ((port->type & PORT_TYPE_VIDEO)
&& (port->vformat == VFORMAT_H264_4K2K))
stbuf_vdec2_init(pvbuf);
}
#endif
if ((port->type & PORT_TYPE_VIDEO) &&
(vdec->port_flag & PORT_FLAG_VFORMAT))
/* connect vdec at the end after all HW initialization */
vdec_connect(vdec);
tsync_audio_break(0); /* clear audio break */
set_vsync_pts_inc_mode(0); /* clear video inc */
port_set_inited(priv);
mutex_unlock(&amstream_mutex);
return 0;
/*errors follow here */
error4:
if ((port->type & PORT_TYPE_MPTS) &&
!(port->flag & PORT_FLAG_VFORMAT))
tsdemux_release();
error3:
if ((port->type & PORT_TYPE_VIDEO) &&
(port->flag & PORT_FLAG_VFORMAT))
video_port_release(priv, &priv->vdec->vbuf, 0);
error2:
if ((port->type & PORT_TYPE_AUDIO) &&
(port->flag & PORT_FLAG_AFORMAT))
audio_port_release(port, pabuf, 0);
error1:
mutex_unlock(&amstream_mutex);
return r;
}
static int amstream_port_release(struct port_priv_s *priv)
{
struct stream_port_s *port = priv->port;
struct stream_buf_s *pvbuf = &priv->vdec->vbuf;
struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO];
struct stream_buf_s *psbuf = &bufs[BUF_TYPE_SUBTITLE];
if ((port->type & PORT_TYPE_MPTS) &&
!(port->flag & PORT_FLAG_VFORMAT)) {
tsync_pcr_stop();
tsdemux_release();
}
if ((port->type & PORT_TYPE_MPPS) &&
!(port->flag & PORT_FLAG_VFORMAT)) {
psparser_release();
}
if (port->type & PORT_TYPE_VIDEO) {
video_port_release(priv, pvbuf, 0);
}
if (port->type & PORT_TYPE_AUDIO)
audio_port_release(port, pabuf, 0);
if (port->type & PORT_TYPE_SUB)
sub_port_release(port, psbuf);
port->pcr_inited = 0;
if (!is_mult_inc(port->type) ||
(is_mult_inc(port->type) &&
!is_support_no_parser()))
port->flag = 0;
return 0;
}
static void amstream_change_avid(struct stream_port_s *port)
{
if (port->type & PORT_TYPE_MPTS) {
tsdemux_change_avid(
(port->flag & PORT_FLAG_VID) ? port->vid : 0xffff,
(port->flag & PORT_FLAG_AID) ? port->aid : 0xffff);
}
if (port->type & PORT_TYPE_MPPS) {
psparser_change_avid(
(port->flag & PORT_FLAG_VID) ? port->vid : 0xffff,
(port->flag & PORT_FLAG_AID) ? port->aid : 0xffff);
}
#ifdef CONFIG_AM_VDEC_REAL
if (port->type & PORT_TYPE_RM) {
rm_set_vasid(
(port->flag & PORT_FLAG_VID) ? port->vid : 0xffff,
(port->flag & PORT_FLAG_AID) ? port->aid : 0xffff);
}
#endif
}
static void amstream_change_sid(struct stream_port_s *port)
{
if (port->type & PORT_TYPE_MPTS) {
tsdemux_change_sid(
(port->flag & PORT_FLAG_SID) ? port->sid : 0xffff);
}
if (port->type & PORT_TYPE_MPPS) {
psparser_change_sid(
(port->flag & PORT_FLAG_SID) ? port->sid : 0xffff);
}
}
/**************************************************/
static ssize_t amstream_vbuf_write(struct file *file, const char *buf,
size_t count, loff_t *ppos)
{
struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
struct stream_buf_s *pbuf = &priv->vdec->vbuf;
int r;
if (!(port_get_inited(priv))) {
r = amstream_port_init(priv);
if (r < 0)
return r;
}
if (priv->vdec->port_flag & PORT_FLAG_DRM)
r = drm_write(file, pbuf, buf, count);
else
r = stream_buffer_write(file, pbuf, buf, count);
if (slow_input) {
pr_info("slow_input: es codec write size %x\n", r);
msleep(3000);
}
#ifdef DATA_DEBUG
debug_file_write(buf, r);
#endif
return r;
}
static ssize_t amstream_vframe_write(struct file *file, const char *buf,
size_t count, loff_t *ppos)
{
struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
ssize_t ret;
int wait_max_cnt = 5;
#ifdef DATA_DEBUG
debug_file_write(buf, count);
#endif
do {
ret = vdec_write_vframe(priv->vdec, buf, count);
if (file->f_flags & O_NONBLOCK) {
break;/*alway return for no block mode.*/
} else if (ret == -EAGAIN) {
int level;
level = vdec_input_level(&priv->vdec->input);
if (wait_max_cnt-- < 0)
break;
msleep(20);
}
} while (ret == -EAGAIN);
return ret;
}
static ssize_t amstream_abuf_write(struct file *file, const char *buf,
size_t count, loff_t *ppos)
{
struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
struct stream_port_s *port = priv->port;
struct stream_buf_s *pbuf = &bufs[BUF_TYPE_AUDIO];
int r;
if (!(port_get_inited(priv))) {
r = amstream_port_init(priv);
if (r < 0)
return r;
}
if (port->flag & PORT_FLAG_DRM)
r = drm_write(file, pbuf, buf, count);
else
r = esparser_write(file, pbuf, buf, count);
return r;
}
static ssize_t amstream_mpts_write(struct file *file, const char *buf,
size_t count, loff_t *ppos)
{
struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
struct stream_port_s *port = priv->port;
struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO];
struct stream_buf_s *pvbuf = &priv->vdec->vbuf;
int r = 0;
if (!(port_get_inited(priv))) {
r = amstream_port_init(priv);
if (r < 0)
return r;
}
#ifdef DATA_DEBUG
debug_file_write(buf, count);
#endif
if (port->flag & PORT_FLAG_DRM)
r = drm_tswrite(file, pvbuf, pabuf, buf, count);
else
r = tsdemux_write(file, pvbuf, pabuf, buf, count);
if (slow_input) {
pr_info("slow_input: ts codec write size %x\n", r);
msleep(3000);
}
return r;
}
static ssize_t amstream_mpps_write(struct file *file, const char *buf,
size_t count, loff_t *ppos)
{
struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
struct stream_buf_s *pvbuf = &bufs[BUF_TYPE_VIDEO];
struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO];
int r;
if (!(port_get_inited(priv))) {
r = amstream_port_init(priv);
if (r < 0)
return r;
}
return psparser_write(file, pvbuf, pabuf, buf, count);
}
#ifdef CONFIG_AM_VDEC_REAL
static ssize_t amstream_mprm_write(struct file *file, const char *buf,
size_t count, loff_t *ppos)
{
struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
struct stream_buf_s *pvbuf = &bufs[BUF_TYPE_VIDEO];
struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO];
int r;
if (!(port_get_inited(priv))) {
r = amstream_port_init(priv);
if (r < 0)
return r;
}
return rmparser_write(file, pvbuf, pabuf, buf, count);
}
#endif
static ssize_t amstream_sub_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
u32 sub_rp, sub_wp, sub_start, data_size, res;
struct stream_buf_s *s_buf = &bufs[BUF_TYPE_SUBTITLE];
if (sub_port_inited == 0)
return 0;
sub_rp = stbuf_sub_rp_get();
sub_wp = stbuf_sub_wp_get();
sub_start = stbuf_sub_start_get();
if (sub_wp == sub_rp || sub_rp == 0)
return 0;
/*flush sub buf before read*/
codec_mm_dma_flush(
(void*)codec_mm_phys_to_virt(sub_start),
stbuf_size(s_buf),
DMA_FROM_DEVICE);
if (sub_wp > sub_rp)
data_size = sub_wp - sub_rp;
else
data_size = s_buf->buf_size - sub_rp + sub_wp;
if (data_size > count)
data_size = count;
if (sub_wp < sub_rp) {
int first_num = s_buf->buf_size - (sub_rp - sub_start);
if (data_size <= first_num) {
res = copy_to_user((void *)buf,
(void *)(codec_mm_phys_to_virt(sub_rp)),
data_size);
stbuf_sub_rp_set(sub_rp + data_size - res);
return data_size - res;
} else {
if (first_num > 0) {
res = copy_to_user((void *)buf,
(void *)(codec_mm_phys_to_virt(sub_rp)),
first_num);
stbuf_sub_rp_set(sub_rp + first_num -
res);
return first_num - res;
}
res = copy_to_user((void *)buf,
(void *)(codec_mm_phys_to_virt(sub_start)),
data_size - first_num);
stbuf_sub_rp_set(sub_start + data_size -
first_num - res);
return data_size - first_num - res;
}
} else {
res =
copy_to_user((void *)buf,
(void *)(codec_mm_phys_to_virt(sub_rp)),
data_size);
stbuf_sub_rp_set(sub_rp + data_size - res);
return data_size - res;
}
}
static ssize_t amstream_sub_write(struct file *file, const char *buf,
size_t count, loff_t *ppos)
{
struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
struct stream_buf_s *pbuf = &bufs[BUF_TYPE_SUBTITLE];
int r;
if (!(port_get_inited(priv))) {
r = amstream_port_init(priv);
if (r < 0)
return r;
}
r = esparser_write(file, pbuf, buf, count);
if (r < 0)
return r;
wakeup_sub_poll();
return r;
}
static unsigned int amstream_sub_poll(struct file *file,
poll_table *wait_table)
{
poll_wait(file, &amstream_sub_wait, wait_table);
if (atomic_read(&subdata_ready)) {
atomic_dec(&subdata_ready);
return POLLOUT | POLLWRNORM;
}
return 0;
}
static void set_userdata_poc(struct userdata_poc_info_t poc)
{
userdata_poc_info[userdata_poc_wi] = poc;
userdata_poc_wi++;
if (userdata_poc_wi == USERDATA_FIFO_NUM)
userdata_poc_wi = 0;
}
void init_userdata_fifo(void)
{
userdata_poc_ri = 0;
userdata_poc_wi = 0;
userdata_length = 0;
}
EXPORT_SYMBOL(init_userdata_fifo);
void reset_userdata_fifo(int bInit)
{
struct stream_buf_s *userdata_buf;
int wi, ri;
u32 rp, wp;
mutex_lock(&userdata_mutex);
wi = userdata_poc_wi;
ri = userdata_poc_ri;
userdata_buf = &bufs[BUF_TYPE_USERDATA];
rp = userdata_buf->buf_rp;
wp = userdata_buf->buf_wp;
if (bInit) {
/* decoder reset */
userdata_buf->buf_rp = 0;
userdata_buf->buf_wp = 0;
userdata_poc_ri = 0;
userdata_poc_wi = 0;
} else {
/* just clean fifo buffer */
userdata_buf->buf_rp = userdata_buf->buf_wp;
userdata_poc_ri = userdata_poc_wi;
}
userdata_length = 0;
last_read_wi = userdata_poc_wi;
mutex_unlock(&userdata_mutex);
pr_debug("reset_userdata_fifo, bInit=%d, wi=%d, ri=%d, rp=%d, wp=%d\n",
bInit, wi, ri, rp, wp);
}
EXPORT_SYMBOL(reset_userdata_fifo);
int wakeup_userdata_poll(struct userdata_poc_info_t poc,
int wp,
unsigned long start_phyaddr,
int buf_size,
int data_length)
{
struct stream_buf_s *userdata_buf = &bufs[BUF_TYPE_USERDATA];
mutex_lock(&userdata_mutex);
if (data_length & 0x7)
data_length = (((data_length + 8) >> 3) << 3);
set_userdata_poc(poc);
userdata_buf->buf_start = start_phyaddr;
userdata_buf->buf_wp = wp;
userdata_buf->buf_size = buf_size;
atomic_set(&userdata_ready, 1);
userdata_length += data_length;
mutex_unlock(&userdata_mutex);
wake_up_interruptible(&amstream_userdata_wait);
return userdata_buf->buf_rp;
}
EXPORT_SYMBOL(wakeup_userdata_poll);
void amstream_wakeup_userdata_poll(struct vdec_s *vdec)
{
int i;
st_userdata *userdata = get_vdec_userdata_ctx();
if (vdec == NULL) {
pr_info("Error, invalid vdec instance!\n");
return;
}
mutex_lock(&userdata->mutex);
for (i = 0; i < MAX_USERDATA_CHANNEL_NUM; i++) {
if (userdata->set_id_flag && (userdata->id[i] == vdec->video_id)) {
userdata->ready_flag[i] = 1;
if (vdec_get_debug_flags() & 0x10000000)
pr_info("%s, wakeup! id = %d\n", __func__, vdec->video_id);
break;
} else if (!userdata->set_id_flag) {
if (!userdata->used[0]) {
vdec->video_id = vdec->id;
userdata->id[0] = vdec->id;
userdata->used[0] = 1;
}
if (vdec_get_debug_flags() & 0x10000000)
pr_info("%s[%d] userdata instance %d ready!\n",
__func__, i, userdata->id[i]);
userdata->ready_flag[i] = 1;
break;
}
}
mutex_unlock(&userdata->mutex);
wake_up_interruptible(&userdata->userdata_wait);
}
EXPORT_SYMBOL(amstream_wakeup_userdata_poll);
static unsigned int amstream_userdata_poll(struct file *file,
poll_table *wait_table)
{
int fd_match = 0;
int i;
st_userdata *userdata = get_vdec_userdata_ctx();
poll_wait(file, &userdata->userdata_wait, wait_table);
mutex_lock(&userdata->mutex);
for (i = 0; i < MAX_USERDATA_CHANNEL_NUM; i++) {
if (userdata->id[i] == userdata->video_id && userdata->ready_flag[i] == 1) {
fd_match = 1;
if (vdec_get_debug_flags() & 0x10000000)
pr_info("%s, success! id = %d\n", __func__, userdata->video_id);
break;
}
}
if (fd_match) {
mutex_unlock(&userdata->mutex);
return POLLIN | POLLRDNORM;
}
mutex_unlock(&userdata->mutex);
return 0;
}
static void amstream_userdata_init(void)
{
int i;
st_userdata *userdata = get_vdec_userdata_ctx();
init_waitqueue_head(&userdata->userdata_wait);
mutex_init(&userdata->mutex);
userdata->set_id_flag = 0;
for (i = 0; i < MAX_USERDATA_CHANNEL_NUM; i++) {
userdata->ready_flag[i] = 0;
userdata->id[i] = -1;
userdata->used[i] = 0;
}
return;
}
static int amstream_open(struct inode *inode, struct file *file)
{
s32 i;
struct stream_port_s *s;
struct stream_port_s *port = &ports[iminor(inode)];
struct port_priv_s *priv;
VDEC_PRINT_FUN_LINENO(__func__, __LINE__);
if (vdec_get_debug_flags() & 0x10000000)
pr_info("%s, port type %d\n", __func__, port->type);
#ifdef G12A_BRINGUP_DEBUG
if (vdec_get_debug_flags() & 0xff0000) {
pr_info("%s force open port %d\n",
__func__,
((vdec_get_debug_flags() >> 16) & 0xff) - 1);
port = &ports[((vdec_get_debug_flags() >> 16) & 0xff) - 1];
}
pr_info("%s, port name %s\n", __func__, port->name);
#endif
if (iminor(inode) >= amstream_port_num)
return -ENODEV;
mutex_lock(&amstream_mutex);
if (port->type & PORT_TYPE_VIDEO) {
for (s = &ports[0], i = 0; i < amstream_port_num; i++, s++) {
if ((!is_mult_inc(s->type)) &&
(s->type & PORT_TYPE_VIDEO) &&
(s->flag & PORT_FLAG_IN_USE)) {
mutex_unlock(&amstream_mutex);
return -EBUSY;
}
}
}
if (!is_support_no_parser()) {
if ((port->flag & PORT_FLAG_IN_USE) &&
((port->type & PORT_TYPE_FRAME) == 0)) {
mutex_unlock(&amstream_mutex);
return -EBUSY;
}
}
/* force dv frame mode */
if (force_dv_mode & 0x2) {
port->type |= PORT_TYPE_FRAME;
port->fops = &vframe_fops;
pr_debug("%s, dobly vision force frame mode.\n", __func__);
}
/* esplayer stream mode force dv */
if (force_dv_mode & 0x1)
port->type |= PORT_TYPE_DUALDEC;
/* check other ports conflicts for audio */
for (s = &ports[0], i = 0; i < amstream_port_num; i++, s++) {
if ((s->flag & PORT_FLAG_IN_USE) &&
((port->type) & (s->type) & PORT_TYPE_AUDIO)) {
mutex_unlock(&amstream_mutex);
return -EBUSY;
}
}
priv = kzalloc(sizeof(struct port_priv_s), GFP_KERNEL);
if (priv == NULL) {
mutex_unlock(&amstream_mutex);
return -ENOMEM;
}
mutex_init(&priv->mutex);
priv->port = port;
if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_M6) {
/* TODO: mod gate */
/* switch_mod_gate_by_name("demux", 1); */
amports_switch_gate("demux", 1);
if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_M8) {
/* TODO: clc gate */
/* CLK_GATE_ON(HIU_PARSER_TOP); */
amports_switch_gate("parser_top", 1);
}
if (port->type & PORT_TYPE_VIDEO) {
/* TODO: mod gate */
/* switch_mod_gate_by_name("vdec", 1); */
amports_switch_gate("vdec", 1);
if (has_hevc_vdec()) {
if (port->type &
(PORT_TYPE_MPTS | PORT_TYPE_HEVC))
vdec_poweron(VDEC_HEVC);
if ((port->type & PORT_TYPE_HEVC) == 0)
vdec_poweron(VDEC_1);
} else {
if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_M8)
vdec_poweron(VDEC_1);
}
}
if (port->type & PORT_TYPE_AUDIO) {
/* TODO: mod gate */
/* switch_mod_gate_by_name("audio", 1); */
amports_switch_gate("audio", 1);
}
}
port->vid = 0;
port->aid = 0;
port->sid = 0;
port->pcrid = 0xffff;
file->f_op = port->fops;
file->private_data = priv;
port->flag = PORT_FLAG_IN_USE;
port->pcr_inited = 0;
#ifdef DATA_DEBUG
debug_filp = filp_open(DEBUG_FILE_NAME, O_WRONLY, 0);
if (IS_ERR(debug_filp)) {
pr_err("amstream: open debug file failed\n");
debug_filp = NULL;
}
#endif
mutex_unlock(&amstream_mutex);
if (port->type & PORT_TYPE_VIDEO) {
priv->vdec = vdec_create(port, NULL);
if (priv->vdec == NULL) {
port->flag = 0;
kfree(priv);
pr_err("amstream: vdec creation failed\n");
return -ENOMEM;
}
if (!(port->type & PORT_TYPE_FRAME)) {
if ((port->type & PORT_TYPE_DUALDEC) ||
(vdec_get_debug_flags() & 0x100)) {
priv->vdec->slave = vdec_create(port, priv->vdec);
if (priv->vdec->slave == NULL) {
vdec_release(priv->vdec);
port->flag = 0;
kfree(priv);
pr_err("amstream: sub vdec creation failed\n");
return -ENOMEM;
}
}
}
}
return 0;
}
static int amstream_release(struct inode *inode, struct file *file)
{
struct port_priv_s *priv = file->private_data;
struct stream_port_s *port = priv->port;
struct vdec_s *slave = NULL;
#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
u32 port_flag = 0;
#endif
if (vdec_get_debug_flags() & 0x10000000)
pr_info("%s, port type %d\n", __func__, port->type);
if (iminor(inode) >= amstream_port_num)
return -ENODEV;
mutex_lock(&amstream_mutex);
if (port_get_inited(priv))
amstream_port_release(priv);
if (priv->vdec) {
#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
port_flag = priv->vdec->port_flag;
#endif
if (priv->vdec->slave)
slave = priv->vdec->slave;
vdec_release(priv->vdec);
if (slave)
vdec_release(slave);
priv->vdec = NULL;
}
if ((port->type & (PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)) ==
PORT_TYPE_AUDIO) {
s32 i;
struct stream_port_s *s;
for (s = &ports[0], i = 0; i < amstream_port_num; i++, s++) {
if ((s->flag & PORT_FLAG_IN_USE)
&& (s->type & PORT_TYPE_VIDEO))
break;
}
if (i == amstream_port_num)
timestamp_firstvpts_set(0);
}
if (!is_mult_inc(port->type) ||
(is_mult_inc(port->type) &&
!is_support_no_parser()))
port->flag = 0;
/* timestamp_pcrscr_set(0); */
#ifdef DATA_DEBUG
if (debug_filp) {
filp_close(debug_filp, current->files);
debug_filp = NULL;
debug_file_pos = 0;
}
#endif
if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_M6) {
if (port->type & PORT_TYPE_VIDEO) {
if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_M8) {
#ifndef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
if (has_hevc_vdec())
vdec_poweroff(VDEC_HEVC);
vdec_poweroff(VDEC_1);
#else
if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_TXLX
&& port->vformat == VFORMAT_H264
&& port->is_4k) {
vdec_poweroff(VDEC_HEVC);
}
if ((port->vformat == VFORMAT_HEVC
|| port->vformat == VFORMAT_AVS2
|| port->vformat == VFORMAT_AV1
|| port->vformat == VFORMAT_VP9)) {
vdec_poweroff(VDEC_HEVC);
} else {
vdec_poweroff(VDEC_1);
}
#endif
}
/* TODO: mod gate */
/* switch_mod_gate_by_name("vdec", 0); */
amports_switch_gate("vdec", 0);
}
if (port->type & PORT_TYPE_AUDIO) {
/* TODO: mod gate */
/* switch_mod_gate_by_name("audio", 0); */
/* amports_switch_gate("audio", 0); */
}
if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_M8) {
/* TODO: clc gate */
/* CLK_GATE_OFF(HIU_PARSER_TOP); */
amports_switch_gate("parser_top", 0);
}
/* TODO: mod gate */
/* switch_mod_gate_by_name("demux", 0); */
amports_switch_gate("demux", 0);
}
mutex_destroy(&priv->mutex);
kfree(priv);
mutex_unlock(&amstream_mutex);
return 0;
}
static long amstream_ioctl_get_version(struct port_priv_s *priv,
ulong arg)
{
int version = (AMSTREAM_IOC_VERSION_FIRST & 0xffff) << 16
| (AMSTREAM_IOC_VERSION_SECOND & 0xffff);
put_user(version, (u32 __user *)arg);
return 0;
}
static long amstream_ioctl_get(struct port_priv_s *priv, ulong arg)
{
struct stream_port_s *this = priv->port;
long r = 0;
struct am_ioctl_parm parm;
if (copy_from_user
((void *)&parm, (void *)arg,
sizeof(parm)))
r = -EFAULT;
switch (parm.cmd) {
case AMSTREAM_GET_SUB_LENGTH:
if ((this->type & PORT_TYPE_SUB) ||
(this->type & PORT_TYPE_SUB_RD)) {
u32 sub_wp, sub_rp;
struct stream_buf_s *psbuf = &bufs[BUF_TYPE_SUBTITLE];
int val;
sub_wp = stbuf_sub_wp_get();
sub_rp = stbuf_sub_rp_get();
if (sub_wp == sub_rp)
val = 0;
else if (sub_wp > sub_rp)
val = sub_wp - sub_rp;
else
val = psbuf->buf_size - (sub_rp - sub_wp);
parm.data_32 = val;
} else
r = -EINVAL;
break;
case AMSTREAM_GET_UD_LENGTH:
if (this->type & PORT_TYPE_USERDATA) {
parm.data_32 = userdata_length;
userdata_length = 0;
} else
r = -EINVAL;
break;
case AMSTREAM_GET_APTS_LOOKUP:
if (this->type & PORT_TYPE_AUDIO) {
u32 pts = 0, frame_size, offset;
offset = parm.data_32;
pts_lookup_offset(PTS_TYPE_AUDIO, offset, &pts,
&frame_size, 300);
parm.data_32 = pts;
}
break;
case AMSTREAM_GET_FIRST_APTS_FLAG:
if (this->type & PORT_TYPE_AUDIO) {
parm.data_32 = first_pts_checkin_complete(
PTS_TYPE_AUDIO);
}
break;
case AMSTREAM_GET_APTS:
parm.data_32 = timestamp_apts_get();
break;
case AMSTREAM_GET_VPTS:
parm.data_32 = timestamp_vpts_get();
break;
case AMSTREAM_GET_VPTS_U64:
parm.data_64 = timestamp_vpts_get_u64();
break;
case AMSTREAM_GET_APTS_U64:
parm.data_64 = timestamp_apts_get_u64();
break;
case AMSTREAM_GET_PCRSCR:
//parm.data_32 = timestamp_pcrscr_get();
break;
case AMSTREAM_GET_LAST_CHECKIN_APTS:
parm.data_32 = get_last_checkin_pts(PTS_TYPE_AUDIO);
break;
case AMSTREAM_GET_LAST_CHECKIN_VPTS:
parm.data_32 = get_last_checkin_pts(PTS_TYPE_VIDEO);
break;
case AMSTREAM_GET_LAST_CHECKOUT_APTS:
parm.data_32 = get_last_checkout_pts(PTS_TYPE_AUDIO);
break;
case AMSTREAM_GET_LAST_CHECKOUT_VPTS:
parm.data_32 = get_last_checkout_pts(PTS_TYPE_VIDEO);
break;
case AMSTREAM_GET_SUB_NUM:
parm.data_32 = psparser_get_sub_found_num();
break;
case AMSTREAM_GET_VIDEO_DELAY_LIMIT_MS:
parm.data_32 = bufs[BUF_TYPE_VIDEO].max_buffer_delay_ms;
break;
case AMSTREAM_GET_AUDIO_DELAY_LIMIT_MS:
parm.data_32 = bufs[BUF_TYPE_AUDIO].max_buffer_delay_ms;
break;
case AMSTREAM_GET_VIDEO_CUR_DELAY_MS: {
int delay;
delay = calculation_stream_delayed_ms(
PTS_TYPE_VIDEO, NULL, NULL);
if (delay >= 0)
parm.data_32 = delay;
else
parm.data_32 = 0;
}
break;
case AMSTREAM_GET_AUDIO_CUR_DELAY_MS: {
int delay;
delay = calculation_stream_delayed_ms(
PTS_TYPE_AUDIO, NULL, NULL);
if (delay >= 0)
parm.data_32 = delay;
else
parm.data_32 = 0;
}
break;
case AMSTREAM_GET_AUDIO_AVG_BITRATE_BPS: {
int delay;
u32 avgbps;
delay = calculation_stream_delayed_ms(
PTS_TYPE_AUDIO, NULL, &avgbps);
if (delay >= 0)
parm.data_32 = avgbps;
else
parm.data_32 = 0;
}
break;
case AMSTREAM_GET_VIDEO_AVG_BITRATE_BPS: {
int delay;
u32 avgbps;
delay = calculation_stream_delayed_ms(
PTS_TYPE_VIDEO, NULL, &avgbps);
if (delay >= 0)
parm.data_32 = avgbps;
else
parm.data_32 = 0;
}
break;
case AMSTREAM_GET_ION_ID:
parm.data_32 = priv->vdec->vf_receiver_inst;
break;
case AMSTREAM_GET_NEED_MORE_DATA:
parm.data_32 = vdec_need_more_data(priv->vdec);
break;
case AMSTREAM_GET_FREED_HANDLE:
parm.data_32 = vdec_input_get_freed_handle(priv->vdec);
break;
default:
r = -ENOIOCTLCMD;
break;
}
/* pr_info("parm size:%d\n", sizeof(parm)); */
if (r == 0) {
if (copy_to_user((void *)arg, &parm, sizeof(parm)))
r = -EFAULT;
}
return r;
}
static long amstream_ioctl_set(struct port_priv_s *priv, ulong arg)
{
struct stream_port_s *this = priv->port;
struct am_ioctl_parm parm;
long r = 0;
int i;
st_userdata *userdata = get_vdec_userdata_ctx();
if (copy_from_user
((void *)&parm, (void *)arg,
sizeof(parm)))
r = -EFAULT;
switch (parm.cmd) {
case AMSTREAM_SET_VB_START:
if ((this->type & PORT_TYPE_VIDEO) &&
((priv->vdec->vbuf.flag & BUF_FLAG_IN_USE) == 0)) {
priv->vdec->vbuf.buf_start = parm.data_32;
} else
r = -EINVAL;
break;
case AMSTREAM_SET_VB_SIZE:
if ((this->type & PORT_TYPE_VIDEO) &&
((priv->vdec->vbuf.flag & BUF_FLAG_IN_USE) == 0)) {
if (priv->vdec->vbuf.flag & BUF_FLAG_ALLOC) {
r += stbuf_change_size(
&priv->vdec->vbuf,
parm.data_32,
false);
}
} else if (this->type & PORT_TYPE_FRAME) {
/* todo: frame based set max buffer size */
r = 0;
} else
r = -EINVAL;
break;
case AMSTREAM_SET_AB_START:
if ((this->type & PORT_TYPE_AUDIO) &&
((bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_IN_USE) == 0))
bufs[BUF_TYPE_AUDIO].buf_start = parm.data_32;
else
r = -EINVAL;
break;
case AMSTREAM_SET_AB_SIZE:
if ((this->type & PORT_TYPE_AUDIO) &&
((bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_IN_USE) == 0)) {
if (bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_ALLOC) {
r = stbuf_change_size(
&bufs[BUF_TYPE_AUDIO],
parm.data_32,
false);
}
} else
r = -EINVAL;
break;
case AMSTREAM_SET_VFORMAT:
if ((this->type & PORT_TYPE_VIDEO) &&
(parm.data_vformat < VFORMAT_MAX)) {
this->vformat = parm.data_vformat;
this->flag |= PORT_FLAG_VFORMAT;
vdec_set_format(priv->vdec, this->vformat);
} else
r = -EINVAL;
break;
case AMSTREAM_SET_AFORMAT:
if ((this->type & PORT_TYPE_AUDIO) &&
(parm.data_aformat < AFORMAT_MAX)) {
memset(&audio_dec_info, 0,
sizeof(struct audio_info));
/* for new format,reset the audio info. */
this->aformat = parm.data_aformat;
this->flag |= PORT_FLAG_AFORMAT;
} else
r = -EINVAL;
break;
case AMSTREAM_SET_VID:
if (this->type & PORT_TYPE_VIDEO) {
this->vid = parm.data_32;
this->flag |= PORT_FLAG_VID;
} else
r = -EINVAL;
break;
case AMSTREAM_SET_AID:
if (this->type & PORT_TYPE_AUDIO) {
this->aid = parm.data_32;
this->flag |= PORT_FLAG_AID;
if (port_get_inited(priv)) {
//tsync_audio_break(1);
amstream_change_avid(this);
}
} else
r = -EINVAL;
break;
case AMSTREAM_SET_SID:
if (this->type & PORT_TYPE_SUB) {
this->sid = parm.data_32;
this->flag |= PORT_FLAG_SID;
if (port_get_inited(priv))
amstream_change_sid(this);
} else
r = -EINVAL;
break;
case AMSTREAM_IOC_PCRID:
this->pcrid = parm.data_32;
this->pcr_inited = 1;
pr_err("set pcrid = 0x%x\n", this->pcrid);
break;
case AMSTREAM_SET_ACHANNEL:
if (this->type & PORT_TYPE_AUDIO) {
this->achanl = parm.data_32;
set_ch_num_info(parm.data_32);
} else
r = -EINVAL;
break;
case AMSTREAM_SET_SAMPLERATE:
if (this->type & PORT_TYPE_AUDIO) {
this->asamprate = parm.data_32;
set_sample_rate_info(parm.data_32);
} else
r = -EINVAL;
break;
case AMSTREAM_SET_DATAWIDTH:
if (this->type & PORT_TYPE_AUDIO)
this->adatawidth = parm.data_32;
else
r = -EINVAL;
break;
case AMSTREAM_SET_TSTAMP:
if ((this->type & (PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)) ==
((PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)))
r = -EINVAL;
else if (this->type & PORT_TYPE_FRAME)
r = vdec_set_pts(priv->vdec, parm.data_32);
else if ((this->type & PORT_TYPE_VIDEO) ||
(this->type & PORT_TYPE_HEVC)) {
struct stream_buf_s *vbuf = &priv->vdec->vbuf;
if (vbuf->no_parser) {
pts_checkin_offset(PTS_TYPE_VIDEO,
vbuf->stream_offset, parm.data_32);
} else {
r = es_vpts_checkin(vbuf, parm.data_32);
}
} else if (this->type & PORT_TYPE_AUDIO)
r = es_apts_checkin(&bufs[BUF_TYPE_AUDIO],
parm.data_32);
break;
case AMSTREAM_SET_TSTAMP_US64:
if ((this->type & (PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)) ==
((PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)))
r = -EINVAL;
else {
u64 pts = parm.data_64;
if (this->type & PORT_TYPE_FRAME) {
/*
*todo: check upper layer for decoder handler
* life sequence or multi-tasking management
*/
r = vdec_set_pts64(priv->vdec, pts);
} else if ((this->type & PORT_TYPE_HEVC) ||
(this->type & PORT_TYPE_VIDEO)) {
r = es_vpts_checkin_us64(
&priv->vdec->vbuf, pts);
} else if (this->type & PORT_TYPE_AUDIO) {
r = es_vpts_checkin_us64(
&bufs[BUF_TYPE_AUDIO], pts);
}
}
break;
case AMSTREAM_PORT_INIT:
r = amstream_port_init(priv);
break;
case AMSTREAM_SET_TRICKMODE:
if ((this->type & PORT_TYPE_VIDEO) == 0)
return -EINVAL;
r = vdec_set_trickmode(priv->vdec, parm.data_32);
if (r == -1)
return -ENODEV;
break;
case AMSTREAM_AUDIO_RESET:
if (this->type & PORT_TYPE_AUDIO) {
struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO];
mutex_lock(&amstream_mutex);
r = audio_port_reset(this, pabuf);
mutex_unlock(&amstream_mutex);
} else
r = -EINVAL;
break;
case AMSTREAM_SUB_RESET:
if (this->type & PORT_TYPE_SUB) {
struct stream_buf_s *psbuf = &bufs[BUF_TYPE_SUBTITLE];
r = sub_port_reset(this, psbuf);
} else
r = -EINVAL;
break;
case AMSTREAM_DEC_RESET:
tsync_set_dec_reset();
break;
case AMSTREAM_SET_TS_SKIPBYTE:
tsdemux_set_skipbyte(parm.data_32);
break;
case AMSTREAM_SET_SUB_TYPE:
sub_type = parm.data_32;
break;
case AMSTREAM_SET_PCRSCR:
timestamp_pcrscr_set(parm.data_32);
break;
case AMSTREAM_SET_DEMUX:
tsdemux_set_demux(parm.data_32);
break;
case AMSTREAM_SET_VIDEO_DELAY_LIMIT_MS:
priv->vdec->vbuf.max_buffer_delay_ms = parm.data_32;
break;
case AMSTREAM_SET_AUDIO_DELAY_LIMIT_MS:
bufs[BUF_TYPE_AUDIO].max_buffer_delay_ms = parm.data_32;
break;
case AMSTREAM_SET_DRMMODE:
if (parm.data_32 == 1) {
pr_debug("set drmmode\n");
this->flag |= PORT_FLAG_DRM;
if ((this->type & PORT_TYPE_VIDEO) &&
(priv->vdec))
priv->vdec->port_flag |= PORT_FLAG_DRM;
} else {
this->flag &= (~PORT_FLAG_DRM);
pr_debug("no drmmode\n");
}
break;
case AMSTREAM_SET_APTS: {
unsigned int pts;
pts = parm.data_32;
if (tsync_get_mode() == TSYNC_MODE_PCRMASTER)
tsync_pcr_set_apts(pts);
else
tsync_set_apts(pts);
break;
}
case AMSTREAM_SET_FRAME_BASE_PATH:
if (is_mult_inc(this->type) &&
(parm.frame_base_video_path < FRAME_BASE_PATH_MAX)) {
vdec_set_video_path(priv->vdec, parm.data_32);
} else
r = -EINVAL;
break;
case AMSTREAM_SET_EOS:
if (priv->vdec)
vdec_set_eos(priv->vdec, parm.data_32);
break;
case AMSTREAM_SET_RECEIVE_ID:
if (is_mult_inc(this->type))
vdec_set_receive_id(priv->vdec, parm.data_32);
else
r = -EINVAL;
break;
case AMSTREAM_SET_IS_RESET:
if (priv->vdec)
vdec_set_isreset(priv->vdec, parm.data_32);
break;
case AMSTREAM_SET_DV_META_WITH_EL:
if (priv->vdec) {
vdec_set_dv_metawithel(priv->vdec, parm.data_32);
if (vdec_dual(priv->vdec) && priv->vdec->slave)
vdec_set_dv_metawithel(priv->vdec->slave,
parm.data_32);
}
break;
case AMSTREAM_SET_NO_POWERDOWN:
vdec_set_no_powerdown(parm.data_32);
break;
case AMSTREAM_SET_VIDEO_ID:
priv->vdec->video_id = parm.data_32;
mutex_lock(&userdata->mutex);
for (i = 0;i < MAX_USERDATA_CHANNEL_NUM; i++) {
if (userdata->used[i] == 0) {
userdata->id[i] = priv->vdec->video_id;
userdata->used[i] = 1;
userdata->video_id = priv->vdec->video_id;
userdata->set_id_flag = 1;
break;
}
}
mutex_unlock(&userdata->mutex);
pr_info("AMSTREAM_SET_VIDEO_ID video_id: %d\n", parm.data_32);
break;
default:
r = -ENOIOCTLCMD;
break;
}
return r;
}
static enum E_ASPECT_RATIO get_normalized_aspect_ratio(u32 ratio_control)
{
enum E_ASPECT_RATIO euAspectRatio;
ratio_control = ratio_control >> DISP_RATIO_ASPECT_RATIO_BIT;
switch (ratio_control) {
case 0x8c:
case 0x90:
euAspectRatio = ASPECT_RATIO_16_9;
/*pr_info("ASPECT_RATIO_16_9\n");*/
break;
case 0xbb:
case 0xc0:
euAspectRatio = ASPECT_RATIO_4_3;
/*pr_info("ASPECT_RATIO_4_3\n");*/
break;
default:
euAspectRatio = ASPECT_UNDEFINED;
/*pr_info("ASPECT_UNDEFINED and ratio_control = 0x%x\n",
ratio_control);*/
break;
}
return euAspectRatio;
}
static long amstream_ioctl_get_ex(struct port_priv_s *priv, ulong arg)
{
struct stream_port_s *this = priv->port;
long r = 0;
struct am_ioctl_parm_ex parm;
if (copy_from_user
((void *)&parm, (void *)arg,
sizeof(parm)))
r = -EFAULT;
switch (parm.cmd) {
case AMSTREAM_GET_EX_VB_STATUS:
if (this->type & PORT_TYPE_VIDEO) {
struct am_ioctl_parm_ex *p = &parm;
struct stream_buf_s *buf = NULL;
mutex_lock(&amstream_mutex);
/*
*todo: check upper layer for decoder
* handler lifecycle
*/
if (priv->vdec == NULL) {
r = -EINVAL;
mutex_unlock(&amstream_mutex);
break;
}
if (this->type & PORT_TYPE_FRAME) {
struct vdec_input_status_s status;
r = vdec_input_get_status(&priv->vdec->input,
&status);
if (r == 0) {
p->status.size = status.size;
p->status.data_len = status.data_len;
p->status.free_len = status.free_len;
p->status.read_pointer =
status.read_pointer;
}
mutex_unlock(&amstream_mutex);
break;
}
buf = &priv->vdec->vbuf;
p->status.size = stbuf_canusesize(buf);
p->status.data_len = stbuf_level(buf);
p->status.free_len = stbuf_space(buf);
p->status.read_pointer = stbuf_rp(buf);
mutex_unlock(&amstream_mutex);
} else
r = -EINVAL;
break;
case AMSTREAM_GET_EX_AB_STATUS:
if (this->type & PORT_TYPE_AUDIO) {
struct am_ioctl_parm_ex *p = &parm;
struct stream_buf_s *buf = &bufs[BUF_TYPE_AUDIO];
p->status.size = stbuf_canusesize(buf);
p->status.data_len = stbuf_level(buf);
p->status.free_len = stbuf_space(buf);
p->status.read_pointer = stbuf_rp(buf);
} else
r = -EINVAL;
break;
case AMSTREAM_GET_EX_VDECSTAT:
if ((this->type & PORT_TYPE_VIDEO) == 0) {
pr_err("no video\n");
return -EINVAL;
} else {
struct vdec_info vstatus;
struct am_ioctl_parm_ex *p = &parm;
memset(&vstatus, 0, sizeof(vstatus));
mutex_lock(&priv->mutex);
if (vdec_status(priv->vdec, &vstatus) == -1) {
mutex_unlock(&priv->mutex);
return -ENODEV;
}
mutex_unlock(&priv->mutex);
p->vstatus.width = vstatus.frame_width;
p->vstatus.height = vstatus.frame_height;
p->vstatus.fps = vstatus.frame_rate;
p->vstatus.error_count = vstatus.error_count;
p->vstatus.status = vstatus.status;
p->vstatus.euAspectRatio =
get_normalized_aspect_ratio(
vstatus.ratio_control);
}
break;
case AMSTREAM_GET_EX_ADECSTAT:
if ((this->type & PORT_TYPE_AUDIO) == 0) {
pr_err("no audio\n");
return -EINVAL;
}
if (amstream_adec_status == NULL) {
/*
*pr_err("no amstream_adec_status\n");
*return -ENODEV;
*/
memset(&parm.astatus, 0, sizeof(parm.astatus));
} else {
struct adec_status astatus;
struct am_ioctl_parm_ex *p = &parm;
amstream_adec_status(&astatus);
p->astatus.channels = astatus.channels;
p->astatus.sample_rate = astatus.sample_rate;
p->astatus.resolution = astatus.resolution;
p->astatus.error_count = astatus.error_count;
p->astatus.status = astatus.status;
}
break;
case AMSTREAM_GET_EX_UD_POC:
if (this->type & PORT_TYPE_USERDATA) {
struct userdata_poc_info_t userdata_poc =
userdata_poc_info[userdata_poc_ri];
memcpy(&parm.data_userdata_info,
&userdata_poc,
sizeof(struct userdata_poc_info_t));
userdata_poc_ri++;
if (userdata_poc_ri == USERDATA_FIFO_NUM)
userdata_poc_ri = 0;
} else
r = -EINVAL;
break;
case AMSTREAM_GET_EX_WR_COUNT:
{
struct am_ioctl_parm_ex *p = &parm;
struct vdec_s *vdec = priv->vdec;
mutex_lock(&amstream_mutex);
if (!vdec)
vdec = vdec_get_vdec_by_id(0); //Use id 0 as default
if (vdec && vdec->mvfrm)
p->wr_count = vdec->mvfrm->wr;
else
p->wr_count = 0;
mutex_unlock(&amstream_mutex);
r = 0;
}
break;
default:
r = -ENOIOCTLCMD;
break;
}
/* pr_info("parm size:%zx\n", sizeof(parm)); */
if (r == 0) {
if (copy_to_user((void *)arg, &parm, sizeof(parm)))
r = -EFAULT;
}
return r;
}
static long amstream_ioctl_set_ex(struct port_priv_s *priv, ulong arg)
{
long r = 0;
return r;
}
static long amstream_ioctl_get_ptr(struct port_priv_s *priv, ulong arg)
{
long r = 0;
struct am_ioctl_parm_ptr parm;
if (copy_from_user
((void *)&parm, (void *)arg,
sizeof(parm)))
return -EFAULT;
switch (parm.cmd) {
case AMSTREAM_GET_PTR_SUB_INFO:
{
struct subtitle_info msub_info[MAX_SUB_NUM];
struct subtitle_info *psub_info[MAX_SUB_NUM];
int i;
for (i = 0; i < MAX_SUB_NUM; i++)
psub_info[i] = &msub_info[i];
r = psparser_get_sub_info(psub_info);
if (r == 0) {
memcpy(parm.pdata_sub_info, msub_info,
sizeof(struct subtitle_info)
* MAX_SUB_NUM);
}
}
break;
default:
r = -ENOIOCTLCMD;
break;
}
/* pr_info("parm size:%d\n", sizeof(parm)); */
if (r == 0) {
if (copy_to_user((void *)arg, &parm, sizeof(parm)))
r = -EFAULT;
}
return r;
}
static long amstream_ioctl_set_ptr(struct port_priv_s *priv, ulong arg)
{
struct stream_port_s *this = priv->port;
struct am_ioctl_parm_ptr parm;
long r = 0;
if (copy_from_user
((void *)&parm, (void *)arg,
sizeof(parm))) {
pr_err("[%s]%d, arg err\n", __func__, __LINE__);
r = -EFAULT;
}
switch (parm.cmd) {
case AMSTREAM_SET_PTR_AUDIO_INFO:
if ((this->type & PORT_TYPE_VIDEO)
|| (this->type & PORT_TYPE_AUDIO)) {
if (parm.pdata_audio_info != NULL) {
if (copy_from_user
((void *)&audio_dec_info, (void *)parm.pdata_audio_info,
sizeof(audio_dec_info))) {
pr_err("[%s]%d, arg err\n", __func__, __LINE__);
r = -EFAULT;
}
}
} else
r = -EINVAL;
break;
case AMSTREAM_SET_PTR_CONFIGS:
if (this->type & PORT_TYPE_VIDEO) {
if (!parm.pointer || (parm.len <= 0) ||
(parm.len > PAGE_SIZE)) {
r = -EINVAL;
} else {
r = copy_from_user(priv->vdec->config,
parm.pointer, parm.len);
if (r)
r = -EINVAL;
else
priv->vdec->config_len = parm.len;
}
} else
r = -EINVAL;
break;
case AMSTREAM_SET_PTR_HDR10P_DATA:
if ((this->type & PORT_TYPE_VIDEO) && (this->type & PORT_TYPE_FRAME)) {
if (!parm.pointer || (parm.len <= 0) ||
(parm.len > PAGE_SIZE)) {
r = -EINVAL;
} else {
r = copy_from_user(priv->vdec->hdr10p_data_buf,
parm.pointer, parm.len);
if (r) {
priv->vdec->hdr10p_data_size = 0;
priv->vdec->hdr10p_data_valid = false;
r = -EINVAL;
} else {
priv->vdec->hdr10p_data_size = parm.len;
priv->vdec->hdr10p_data_valid = true;
}
}
} else
r = -EINVAL;
break;
default:
r = -ENOIOCTLCMD;
break;
}
return r;
}
static long amstream_do_ioctl_new(struct port_priv_s *priv,
unsigned int cmd, ulong arg)
{
long r = 0;
struct stream_port_s *this = priv->port;
switch (cmd) {
case AMSTREAM_IOC_GET_VERSION:
r = amstream_ioctl_get_version(priv, arg);
break;
case AMSTREAM_IOC_GET:
r = amstream_ioctl_get(priv, arg);
break;
case AMSTREAM_IOC_SET:
r = amstream_ioctl_set(priv, arg);
break;
case AMSTREAM_IOC_GET_EX:
r = amstream_ioctl_get_ex(priv, arg);
break;
case AMSTREAM_IOC_SET_EX:
r = amstream_ioctl_set_ex(priv, arg);
break;
case AMSTREAM_IOC_GET_PTR:
r = amstream_ioctl_get_ptr(priv, arg);
break;
case AMSTREAM_IOC_SET_PTR:
r = amstream_ioctl_set_ptr(priv, arg);
break;
case AMSTREAM_IOC_SYSINFO:
if (this->type & PORT_TYPE_VIDEO)
r = vdec_set_decinfo(priv->vdec, (void *)arg);
else
r = -EINVAL;
break;
case AMSTREAM_IOC_GET_QOSINFO:
case AMSTREAM_IOC_GET_MVDECINFO:
{
u32 slots = 0;
u32 struct_size = 0;
int vdec_id = 0;
struct vdec_s *vdec = priv->vdec;
struct vframe_counter_s *tmpbuf = kmalloc(QOS_FRAME_NUM *
sizeof(struct vframe_counter_s),GFP_KERNEL);
struct av_param_mvdec_t __user *uarg = (void *)arg;
mutex_lock(&amstream_mutex);
if (!tmpbuf) {
r = -EFAULT;
pr_err("kmalloc vframe_counter_s failed!\n");
mutex_unlock(&amstream_mutex);
break;
}
if (get_user(vdec_id, &uarg->vdec_id) < 0 ||
get_user(struct_size, &uarg->struct_size) < 0) {
r = -EFAULT;
kfree(tmpbuf);
mutex_unlock(&amstream_mutex);
break;
}
if (vdec && !vdec_id) //If vdec_id is > 0, it means user require to use it.
r = 0;//vdec =priv->vdec;//Nothing to do.
else
vdec = vdec_get_vdec_by_id(vdec_id);
if (!vdec) {
r = 0;
kfree(tmpbuf);
mutex_unlock(&amstream_mutex);
break;
}
slots = vdec_get_frame_vdec(vdec, tmpbuf);
if (AMSTREAM_IOC_GET_MVDECINFO == cmd)
put_user(slots, &uarg->slots);
if (slots) {
if (AMSTREAM_IOC_GET_MVDECINFO == cmd) {
if (vdec->mvfrm && copy_to_user((void *)&uarg->comm,
&vdec->mvfrm->comm,
sizeof(struct vframe_comm_s))) {
r = -EFAULT;
kfree(tmpbuf);
mutex_unlock(&amstream_mutex);
break;
}
if (struct_size == sizeof(struct av_param_mvdec_t_old)) {//old struct
struct av_param_mvdec_t_old __user *uarg_old = (void *)arg;
int m;
for (m=0; m<slots; m++)
if (copy_to_user((void *)&uarg_old->minfo[m],
&tmpbuf[m],
sizeof(struct vframe_counter_s_old))) {
r = -EFAULT;
kfree(tmpbuf);
mutex_unlock(&amstream_mutex);
break;
}
} else if (struct_size == sizeof(struct av_param_mvdec_t)) {//new struct
if (copy_to_user((void *)&uarg->minfo[0],
tmpbuf,
slots*sizeof(struct vframe_counter_s))) {
r = -EFAULT;
kfree(tmpbuf);
mutex_unlock(&amstream_mutex);
break;
}
} else {
pr_err("pass in size %u,old struct size %u,current struct size %u\n",
struct_size, (u32)sizeof(struct av_param_mvdec_t_old),(u32)sizeof(struct av_param_mvdec_t));
pr_err("App use another picture size,we haven't support it.\n");
}
}else { //For compatibility, only copy the qos
struct av_param_qosinfo_t __user *uarg = (void *)arg;
int i;
for (i=0; i<slots; i++)
if (copy_to_user((void *)&uarg->vframe_qos[i],
&tmpbuf[i].qos,
sizeof(struct vframe_qos_s))) {
r = -EFAULT;
kfree(tmpbuf);
mutex_unlock(&amstream_mutex);
break;
}
}
} else {
/*Vdec didn't produce item,wait for 10 ms to avoid user application
infinitely calling*/
//msleep(10); let user app handle it.
}
kfree(tmpbuf);
}
mutex_unlock(&amstream_mutex);
break;
case AMSTREAM_IOC_GET_AVINFO:
{
struct av_param_info_t __user *uarg = (void *)arg;
struct av_info_t av_info;
int delay;
u32 avgbps;
if (this->type & PORT_TYPE_VIDEO) {
av_info.first_pic_coming = get_first_pic_coming();
av_info.current_fps = -1;
av_info.vpts = timestamp_vpts_get();
//av_info.vpts_err = tsync_get_vpts_error_num();
av_info.apts = timestamp_apts_get();
//av_info.apts_err = tsync_get_apts_error_num();
av_info.ts_error = get_discontinue_counter();
av_info.first_vpts = timestamp_firstvpts_get();
av_info.toggle_frame_count = get_toggle_frame_count();
delay = calculation_stream_delayed_ms(
PTS_TYPE_VIDEO, NULL, &avgbps);
if (delay >= 0)
av_info.dec_video_bps = avgbps;
else
av_info.dec_video_bps = 0;
}
if (copy_to_user((void *)&uarg->av_info, (void *)&av_info,
sizeof(struct av_info_t)))
r = -EFAULT;
}
break;
default:
r = -ENOIOCTLCMD;
break;
}
return r;
}
static long amstream_do_ioctl_old(struct port_priv_s *priv,
unsigned int cmd, ulong arg)
{
struct stream_port_s *this = priv->port;
long r = 0;
int i;
switch (cmd) {
case AMSTREAM_IOC_VB_START:
if ((this->type & PORT_TYPE_VIDEO) &&
((priv->vdec->vbuf.flag & BUF_FLAG_IN_USE) == 0)) {
priv->vdec->vbuf.buf_start = arg;
} else
r = -EINVAL;
break;
case AMSTREAM_IOC_VB_SIZE:
if ((this->type & PORT_TYPE_VIDEO) &&
((priv->vdec->vbuf.flag & BUF_FLAG_IN_USE) == 0)) {
if (priv->vdec->vbuf.flag & BUF_FLAG_ALLOC) {
r += stbuf_change_size(
&priv->vdec->vbuf,
arg, false);
}
} else
r = -EINVAL;
break;
case AMSTREAM_IOC_AB_START:
if ((this->type & PORT_TYPE_AUDIO) &&
((bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_IN_USE) == 0))
bufs[BUF_TYPE_AUDIO].buf_start = arg;
else
r = -EINVAL;
break;
case AMSTREAM_IOC_AB_SIZE:
if ((this->type & PORT_TYPE_AUDIO) &&
((bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_IN_USE) == 0)) {
if (bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_ALLOC) {
r = stbuf_change_size(
&bufs[BUF_TYPE_AUDIO], arg, false);
}
} else
r = -EINVAL;
break;
case AMSTREAM_IOC_VFORMAT:
if ((this->type & PORT_TYPE_VIDEO) && (arg < VFORMAT_MAX)) {
this->vformat = (enum vformat_e)arg;
this->flag |= PORT_FLAG_VFORMAT;
vdec_set_format(priv->vdec, this->vformat);
} else
r = -EINVAL;
break;
case AMSTREAM_IOC_AFORMAT:
if ((this->type & PORT_TYPE_AUDIO) && (arg < AFORMAT_MAX)) {
memset(&audio_dec_info, 0,
sizeof(struct audio_info));
/* for new format,reset the audio info. */
this->aformat = (enum aformat_e)arg;
this->flag |= PORT_FLAG_AFORMAT;
} else
r = -EINVAL;
break;
case AMSTREAM_IOC_VID:
if (this->type & PORT_TYPE_VIDEO) {
this->vid = (u32) arg;
this->flag |= PORT_FLAG_VID;
} else
r = -EINVAL;
break;
case AMSTREAM_IOC_AID:
if (this->type & PORT_TYPE_AUDIO) {
this->aid = (u32) arg;
this->flag |= PORT_FLAG_AID;
if (port_get_inited(priv)) {
//tsync_audio_break(1);
amstream_change_avid(this);
}
} else
r = -EINVAL;
break;
case AMSTREAM_IOC_SID:
if (this->type & PORT_TYPE_SUB) {
this->sid = (u32) arg;
this->flag |= PORT_FLAG_SID;
if (port_get_inited(priv))
amstream_change_sid(this);
} else
r = -EINVAL;
break;
case AMSTREAM_IOC_PCRID:
this->pcrid = (u32) arg;
this->pcr_inited = 1;
pr_err("set pcrid = 0x%x\n", this->pcrid);
break;
case AMSTREAM_IOC_VB_STATUS:
if (this->type & PORT_TYPE_VIDEO) {
struct am_io_param para;
struct am_io_param *p = &para;
struct stream_buf_s *buf = NULL;
mutex_lock(&amstream_mutex);
/*
*todo: check upper layer for decoder
* handler lifecycle
*/
if (priv->vdec == NULL) {
r = -EINVAL;
mutex_unlock(&amstream_mutex);
break;
}
if (this->type & PORT_TYPE_FRAME) {
struct vdec_input_status_s status;
r = vdec_input_get_status(&priv->vdec->input,
&status);
if (r == 0) {
p->status.size = status.size;
p->status.data_len = status.data_len;
p->status.free_len = status.free_len;
p->status.read_pointer =
status.read_pointer;
if (copy_to_user((void *)arg, p,
sizeof(para)))
r = -EFAULT;
}
mutex_unlock(&amstream_mutex);
break;
}
buf = &priv->vdec->vbuf;
p->status.size = stbuf_canusesize(buf);
p->status.data_len = stbuf_level(buf);
p->status.free_len = stbuf_space(buf);
p->status.read_pointer = stbuf_rp(buf);
if (copy_to_user((void *)arg, p, sizeof(para)))
r = -EFAULT;
mutex_unlock(&amstream_mutex);
return r;
}
r = -EINVAL;
break;
case AMSTREAM_IOC_AB_STATUS:
if (this->type & PORT_TYPE_AUDIO) {
struct am_io_param para;
struct am_io_param *p = &para;
struct stream_buf_s *buf = &bufs[BUF_TYPE_AUDIO];
p->status.size = stbuf_canusesize(buf);
p->status.data_len = stbuf_level(buf);
p->status.free_len = stbuf_space(buf);
p->status.read_pointer = stbuf_rp(buf);
if (copy_to_user((void *)arg, p, sizeof(para)))
r = -EFAULT;
return r;
}
r = -EINVAL;
break;
case AMSTREAM_IOC_SYSINFO:
if (this->type & PORT_TYPE_VIDEO)
r = vdec_set_decinfo(priv->vdec, (void *)arg);
else
r = -EINVAL;
break;
case AMSTREAM_IOC_ACHANNEL:
if (this->type & PORT_TYPE_AUDIO) {
this->achanl = (u32) arg;
set_ch_num_info((u32) arg);
} else
r = -EINVAL;
break;
case AMSTREAM_IOC_SAMPLERATE:
if (this->type & PORT_TYPE_AUDIO) {
this->asamprate = (u32) arg;
set_sample_rate_info((u32) arg);
} else
r = -EINVAL;
break;
case AMSTREAM_IOC_DATAWIDTH:
if (this->type & PORT_TYPE_AUDIO)
this->adatawidth = (u32) arg;
else
r = -EINVAL;
break;
case AMSTREAM_IOC_TSTAMP:
if ((this->type & (PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)) ==
((PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)))
r = -EINVAL;
else if (this->type & PORT_TYPE_FRAME)
r = vdec_set_pts(priv->vdec, arg);
else if ((this->type & PORT_TYPE_VIDEO) ||
(this->type & PORT_TYPE_HEVC)) {
struct stream_buf_s *vbuf = &priv->vdec->vbuf;
if (vbuf->no_parser) {
pts_checkin_offset(PTS_TYPE_VIDEO,
vbuf->stream_offset, arg);
} else {
r = es_vpts_checkin(vbuf, arg);
}
} else if (this->type & PORT_TYPE_AUDIO)
r = es_apts_checkin(&bufs[BUF_TYPE_AUDIO], arg);
break;
case AMSTREAM_IOC_TSTAMP_uS64:
if ((this->type & (PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)) ==
((PORT_TYPE_AUDIO | PORT_TYPE_VIDEO)))
r = -EINVAL;
else {
u64 pts;