| /* |
| * 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 = ¶ |
| 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 = ¶ |
| 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; |
| |
| if (copy_from_user |
| ((void *)&pts, (void *)arg, sizeof(u64))) |
| return -EFAULT; |
| if (this->type & PORT_TYPE_FRAME) { |
| /* |
| *todo: check upper layer for decoder handler |
| * life sequence or multi-tasking management |
| */ |
| if (priv->vdec) |
| r = vdec_set_pts64(priv->vdec, pts); |
| } else if ((this->type & PORT_TYPE_HEVC) || |
| (this->type & PORT_TYPE_VIDEO)) { |
| struct stream_buf_s *vbuf = &priv->vdec->vbuf; |
| if (vbuf->no_parser && !vdec_single(priv->vdec)) { |
| pts_checkin_offset_us64(PTS_TYPE_VIDEO, |
| vbuf->stream_offset, pts); |
| } else { |
| 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_IOC_VDECSTAT: |
| if ((this->type & PORT_TYPE_VIDEO) == 0) |
| return -EINVAL; |
| { |
| struct vdec_info vstatus; |
| struct am_io_param para; |
| struct am_io_param *p = ¶ |
| |
| 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); |
| |
| if (copy_to_user((void *)arg, p, sizeof(para))) |
| r = -EFAULT; |
| return r; |
| } |
| |
| case AMSTREAM_IOC_VDECINFO: |
| if ((this->type & PORT_TYPE_VIDEO) == 0) |
| return -EINVAL; |
| { |
| struct vdec_info vinfo; |
| struct am_io_info para; |
| |
| memset(¶, 0x0, sizeof(struct am_io_info)); |
| |
| mutex_lock(&priv->mutex); |
| if (vdec_status(priv->vdec, &vinfo) == -1) { |
| mutex_unlock(&priv->mutex); |
| return -ENODEV; |
| } |
| mutex_unlock(&priv->mutex); |
| |
| memcpy(¶.vinfo, &vinfo, sizeof(struct vdec_info)); |
| if (copy_to_user((void *)arg, ¶, sizeof(para))) |
| r = -EFAULT; |
| return r; |
| } |
| |
| case AMSTREAM_IOC_ADECSTAT: |
| if ((this->type & PORT_TYPE_AUDIO) == 0) |
| return -EINVAL; |
| if (amstream_adec_status == NULL) |
| return -ENODEV; |
| else { |
| struct adec_status astatus; |
| struct am_io_param para; |
| struct am_io_param *p = ¶ |
| |
| 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; |
| if (copy_to_user((void *)arg, p, sizeof(para))) |
| r = -EFAULT; |
| return r; |
| } |
| case AMSTREAM_IOC_PORT_INIT: |
| r = amstream_port_init(priv); |
| break; |
| |
| case AMSTREAM_IOC_VDEC_RESET: |
| if ((this->type & PORT_TYPE_VIDEO) == 0) |
| return -EINVAL; |
| |
| if (priv->vdec == NULL) |
| return -ENODEV; |
| |
| r = vdec_reset(priv->vdec); |
| break; |
| |
| case AMSTREAM_IOC_TRICKMODE: |
| if ((this->type & PORT_TYPE_VIDEO) == 0) |
| return -EINVAL; |
| r = vdec_set_trickmode(priv->vdec, arg); |
| if (r == -1) |
| return -ENODEV; |
| break; |
| |
| case AMSTREAM_IOC_AUDIO_INFO: |
| if ((this->type & PORT_TYPE_VIDEO) |
| || (this->type & PORT_TYPE_AUDIO)) { |
| if (copy_from_user |
| (&audio_dec_info, (void __user *)arg, |
| sizeof(audio_dec_info))) |
| r = -EFAULT; |
| } else |
| r = -EINVAL; |
| break; |
| |
| case AMSTREAM_IOC_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_IOC_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_IOC_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); |
| put_user(val, (int __user *)arg); |
| } else |
| r = -EINVAL; |
| break; |
| |
| case AMSTREAM_IOC_UD_LENGTH: |
| if (this->type & PORT_TYPE_USERDATA) { |
| /* *((u32 *)arg) = userdata_length; */ |
| put_user(userdata_length, (unsigned long __user *)arg); |
| userdata_length = 0; |
| } else |
| r = -EINVAL; |
| break; |
| |
| case AMSTREAM_IOC_UD_POC: |
| if (this->type & PORT_TYPE_USERDATA) { |
| /* *((u32 *)arg) = userdata_length; */ |
| int ri; |
| #ifdef DEBUG_USER_DATA |
| int wi; |
| #endif |
| int bDataAvail = 0; |
| |
| mutex_lock(&userdata_mutex); |
| if (userdata_poc_wi != userdata_poc_ri) { |
| bDataAvail = 1; |
| ri = userdata_poc_ri; |
| #ifdef DEBUG_USER_DATA |
| wi = userdata_poc_wi; |
| #endif |
| userdata_poc_ri++; |
| if (userdata_poc_ri >= USERDATA_FIFO_NUM) |
| userdata_poc_ri = 0; |
| } |
| mutex_unlock(&userdata_mutex); |
| if (bDataAvail) { |
| int res; |
| struct userdata_poc_info_t userdata_poc = |
| userdata_poc_info[ri]; |
| #ifdef DEBUG_USER_DATA |
| pr_info("read poc: ri=%d, wi=%d, poc=%d, last_wi=%d\n", |
| ri, wi, |
| userdata_poc.poc_number, |
| last_read_wi); |
| #endif |
| res = |
| copy_to_user((unsigned long __user *)arg, |
| &userdata_poc, |
| sizeof(struct userdata_poc_info_t)); |
| if (res < 0) |
| r = -EFAULT; |
| } else { |
| r = -EFAULT; |
| } |
| } else { |
| r = -EINVAL; |
| } |
| break; |
| |
| case AMSTREAM_IOC_UD_BUF_READ: |
| { |
| if (this->type & PORT_TYPE_USERDATA) { |
| struct userdata_param_t param; |
| struct userdata_param_t *p_userdata_param; |
| struct vdec_s *vdec; |
| |
| p_userdata_param = ¶m; |
| if (copy_from_user(p_userdata_param, |
| (void __user *)arg, |
| sizeof(struct userdata_param_t))) { |
| r = -EFAULT; |
| break; |
| } |
| mutex_lock(&amstream_mutex); |
| if (vdec_get_debug_flags() & 0x10000000) |
| pr_info("%s, instance_id = %d\n", __func__, p_userdata_param->instance_id); |
| vdec = vdec_get_vdec_by_video_id(p_userdata_param->instance_id); |
| if (vdec) { |
| if (vdec_read_user_data(vdec, |
| p_userdata_param) == 0) { |
| r = -EFAULT; |
| mutex_unlock(&amstream_mutex); |
| break; |
| } |
| |
| if (copy_to_user((void *)arg, |
| p_userdata_param, |
| sizeof(struct userdata_param_t))) |
| r = -EFAULT; |
| } else |
| r = -EINVAL; |
| mutex_unlock(&amstream_mutex); |
| } |
| } |
| break; |
| |
| case AMSTREAM_IOC_UD_AVAILABLE_VDEC: |
| { |
| unsigned int ready_vdec = 0; |
| u32 ready_flag = 0; |
| st_userdata *userdata = get_vdec_userdata_ctx(); |
| |
| mutex_lock(&userdata->mutex); |
| for (i = 0; i < MAX_USERDATA_CHANNEL_NUM; i++) { |
| if (userdata->video_id == userdata->id[i] && |
| userdata->ready_flag[i] == 1) { |
| ready_vdec = userdata->id[i]; |
| userdata->ready_flag[i] = 0; |
| ready_flag = 1; |
| break; |
| } |
| } |
| if (!ready_flag) { |
| pr_info("instance %d not ready!\n", userdata->video_id); |
| r = -EINVAL; |
| } |
| mutex_unlock(&userdata->mutex); |
| |
| put_user(ready_vdec, (uint32_t __user *)arg); |
| if (vdec_get_debug_flags() & 0x10000000) |
| pr_info("%s, ready_vdec = %u\n", __func__, ready_vdec); |
| } |
| break; |
| |
| case AMSTREAM_IOC_GET_VDEC_ID: |
| if (this->type & PORT_TYPE_VIDEO && priv->vdec) { |
| put_user(priv->vdec->id, (int32_t __user *)arg); |
| } else |
| r = -EINVAL; |
| break; |
| |
| |
| case AMSTREAM_IOC_UD_FLUSH_USERDATA: |
| if (this->type & PORT_TYPE_USERDATA) { |
| struct vdec_s *vdec; |
| int video_id; |
| |
| mutex_lock(&amstream_mutex); |
| get_user(video_id, (int __user *)arg); |
| pr_info("userdata flush id: %d\n", video_id); |
| vdec = vdec_get_vdec_by_video_id(video_id); |
| if (vdec) { |
| vdec_reset_userdata_fifo(vdec, 0); |
| pr_info("reset_userdata_fifo for vdec_id: %d video_id: %d\\n", |
| vdec->id, vdec->video_id); |
| } |
| mutex_unlock(&amstream_mutex); |
| } else |
| r = -EINVAL; |
| break; |
| |
| case AMSTREAM_IOC_SET_DEC_RESET: |
| tsync_set_dec_reset(); |
| break; |
| |
| case AMSTREAM_IOC_TS_SKIPBYTE: |
| if ((int)arg >= 0) |
| tsdemux_set_skipbyte(arg); |
| else |
| r = -EINVAL; |
| break; |
| |
| case AMSTREAM_IOC_SUB_TYPE: |
| sub_type = (int)arg; |
| break; |
| |
| case AMSTREAM_IOC_APTS_LOOKUP: |
| if (this->type & PORT_TYPE_AUDIO) { |
| u32 pts = 0, frame_size, offset; |
| |
| get_user(offset, (unsigned long __user *)arg); |
| pts_lookup_offset(PTS_TYPE_AUDIO, offset, &pts, |
| &frame_size, 300); |
| put_user(pts, (int __user *)arg); |
| } |
| return 0; |
| case GET_FIRST_APTS_FLAG: |
| if (this->type & PORT_TYPE_AUDIO) { |
| put_user(first_pts_checkin_complete(PTS_TYPE_AUDIO), |
| (int __user *)arg); |
| } |
| break; |
| |
| case AMSTREAM_IOC_APTS: |
| put_user(timestamp_apts_get(), (int __user *)arg); |
| break; |
| |
| case AMSTREAM_IOC_VPTS: |
| put_user(timestamp_vpts_get(), (int __user *)arg); |
| break; |
| |
| case AMSTREAM_IOC_PCRSCR: |
| //put_user(timestamp_pcrscr_get(), (int __user *)arg); |
| break; |
| |
| case AMSTREAM_IOC_SET_PCRSCR: |
| timestamp_pcrscr_set(arg); |
| break; |
| case AMSTREAM_IOC_GET_LAST_CHECKIN_APTS: |
| put_user(get_last_checkin_pts(PTS_TYPE_AUDIO), (int *)arg); |
| break; |
| case AMSTREAM_IOC_GET_LAST_CHECKIN_VPTS: |
| put_user(get_last_checkin_pts(PTS_TYPE_VIDEO), (int *)arg); |
| break; |
| case AMSTREAM_IOC_GET_LAST_CHECKOUT_APTS: |
| put_user(get_last_checkout_pts(PTS_TYPE_AUDIO), (int *)arg); |
| break; |
| case AMSTREAM_IOC_GET_LAST_CHECKOUT_VPTS: |
| put_user(get_last_checkout_pts(PTS_TYPE_VIDEO), (int *)arg); |
| break; |
| case AMSTREAM_IOC_SUB_NUM: |
| put_user(psparser_get_sub_found_num(), (int *)arg); |
| break; |
| |
| case AMSTREAM_IOC_SUB_INFO: |
| if (arg > 0) { |
| struct subtitle_info *msub_info = |
| vzalloc(sizeof(struct subtitle_info) * MAX_SUB_NUM); |
| struct subtitle_info **psub_info = |
| vzalloc(sizeof(struct subtitle_info) * MAX_SUB_NUM); |
| int i; |
| |
| if (!msub_info || !psub_info) { |
| r = -ENOMEM; |
| break; |
| } |
| |
| for (i = 0; i < MAX_SUB_NUM; i++) |
| psub_info[i] = &msub_info[i]; |
| |
| r = psparser_get_sub_info(psub_info); |
| |
| if (r == 0) { |
| if (copy_to_user((void __user *)arg, msub_info, |
| sizeof(struct subtitle_info) * MAX_SUB_NUM)) |
| r = -EFAULT; |
| } |
| vfree(msub_info); |
| vfree(psub_info); |
| } |
| break; |
| case AMSTREAM_IOC_SET_DEMUX: |
| tsdemux_set_demux((int)arg); |
| break; |
| case AMSTREAM_IOC_SET_VIDEO_DELAY_LIMIT_MS: |
| priv->vdec->vbuf.max_buffer_delay_ms = (int)arg; |
| break; |
| case AMSTREAM_IOC_SET_AUDIO_DELAY_LIMIT_MS: |
| bufs[BUF_TYPE_AUDIO].max_buffer_delay_ms = (int)arg; |
| break; |
| case AMSTREAM_IOC_GET_VIDEO_DELAY_LIMIT_MS: |
| put_user(priv->vdec->vbuf.max_buffer_delay_ms, (int *)arg); |
| break; |
| case AMSTREAM_IOC_GET_AUDIO_DELAY_LIMIT_MS: |
| put_user(bufs[BUF_TYPE_AUDIO].max_buffer_delay_ms, (int *)arg); |
| break; |
| case AMSTREAM_IOC_GET_VIDEO_CUR_DELAY_MS: { |
| int delay; |
| |
| delay = calculation_stream_delayed_ms( |
| PTS_TYPE_VIDEO, NULL, NULL); |
| if (delay >= 0) |
| put_user(delay, (int *)arg); |
| else |
| put_user(0, (int *)arg); |
| } |
| break; |
| |
| case AMSTREAM_IOC_GET_AUDIO_CUR_DELAY_MS: { |
| int delay; |
| |
| delay = calculation_stream_delayed_ms(PTS_TYPE_AUDIO, NULL, |
| NULL); |
| if (delay >= 0) |
| put_user(delay, (int *)arg); |
| else |
| put_user(0, (int *)arg); |
| } |
| break; |
| case AMSTREAM_IOC_GET_AUDIO_AVG_BITRATE_BPS: { |
| int delay; |
| u32 avgbps; |
| |
| delay = calculation_stream_delayed_ms(PTS_TYPE_AUDIO, NULL, |
| &avgbps); |
| if (delay >= 0) |
| put_user(avgbps, (int *)arg); |
| else |
| put_user(0, (int *)arg); |
| break; |
| } |
| case AMSTREAM_IOC_GET_VIDEO_AVG_BITRATE_BPS: { |
| int delay; |
| u32 avgbps; |
| |
| delay = calculation_stream_delayed_ms(PTS_TYPE_VIDEO, NULL, |
| &avgbps); |
| if (delay >= 0) |
| put_user(avgbps, (int *)arg); |
| else |
| put_user(0, (int *)arg); |
| break; |
| } |
| case AMSTREAM_IOC_SET_DRMMODE: |
| if ((u32) arg == 1) { |
| pr_err("set drmmode, input must be secure buffer\n"); |
| this->flag |= PORT_FLAG_DRM; |
| if ((this->type & PORT_TYPE_VIDEO) && |
| (priv->vdec)) |
| priv->vdec->port_flag |= PORT_FLAG_DRM; |
| } else if ((u32)arg == 2) { |
| pr_err("set drmmode, input must be normal buffer\n"); |
| if ((this->type & PORT_TYPE_VIDEO) && |
| (priv->vdec)) { |
| pr_err("vdec port_flag with drmmode\n"); |
| priv->vdec->port_flag |= PORT_FLAG_DRM; |
| } |
| } else { |
| this->flag &= (~PORT_FLAG_DRM); |
| pr_err("no drmmode\n"); |
| } |
| break; |
| case AMSTREAM_IOC_SET_APTS: { |
| unsigned long pts; |
| |
| if (get_user(pts, (unsigned long __user *)arg)) { |
| pr_err |
| ("Get audio pts from user space fault!\n"); |
| return -EFAULT; |
| } |
| if (tsync_get_mode() == TSYNC_MODE_PCRMASTER) |
| tsync_pcr_set_apts(pts); |
| else |
| tsync_set_apts(pts); |
| break; |
| } |
| case AMSTREAM_IOC_SET_CRC: { |
| struct usr_crc_info_t crc_info; |
| struct vdec_s *vdec; |
| |
| if (copy_from_user(&crc_info, (void __user *)arg, |
| sizeof(struct usr_crc_info_t))) { |
| return -EFAULT; |
| } |
| /* |
| pr_info("id %d, frame %d, y_crc: %08x, uv_crc: %08x\n", crc_info.id, |
| crc_info.pic_num, crc_info.y_crc, crc_info.uv_crc); |
| */ |
| vdec = vdec_get_vdec_by_id(crc_info.id); |
| if (vdec == NULL) |
| return -ENODEV; |
| if (vdec->vfc.cmp_pool == NULL) { |
| vdec->vfc.cmp_pool = |
| vmalloc(USER_CMP_POOL_MAX_SIZE * |
| sizeof(struct usr_crc_info_t)); |
| if (vdec->vfc.cmp_pool == NULL) |
| return -ENOMEM; |
| } |
| if (vdec->vfc.usr_cmp_num >= USER_CMP_POOL_MAX_SIZE) { |
| pr_info("warn: could not write any more, max %d", |
| USER_CMP_POOL_MAX_SIZE); |
| return -EFAULT; |
| } |
| memcpy(&vdec->vfc.cmp_pool[vdec->vfc.usr_cmp_num], &crc_info, |
| sizeof(struct usr_crc_info_t)); |
| vdec->vfc.usr_cmp_num++; |
| break; |
| } |
| case AMSTREAM_IOC_GET_CRC_CMP_RESULT: { |
| int val, vdec_id; |
| struct vdec_s *vdec; |
| |
| if (get_user(val, (int __user *)arg)) { |
| return -EFAULT; |
| } |
| vdec_id = val & 0x00ff; |
| vdec = vdec_get_vdec_by_id(vdec_id); |
| if (vdec == NULL) |
| return -ENODEV; |
| if (val & 0xff00) |
| put_user(vdec->vfc.usr_cmp_num, (int *)arg); |
| else |
| put_user(vdec->vfc.usr_cmp_result, (int *)arg); |
| /* |
| pr_info("amstream get crc32 cmpare num %d result: %d\n", |
| vdec->vfc.usr_cmp_num, vdec->vfc.usr_cmp_result); |
| */ |
| break; |
| } |
| case AMSTREAM_IOC_INIT_EX_STBUF: { |
| struct stream_buffer_metainfo parm; |
| struct stream_buf_s *vbuf = NULL; |
| |
| if (priv->vdec == NULL) { |
| pr_err("init %s, no vdec.\n", __func__); |
| return -EFAULT; |
| } |
| |
| vbuf = &priv->vdec->vbuf; |
| if (vbuf == NULL) { |
| pr_err("init %s, no stbuf.\n", __func__); |
| return -EFAULT; |
| } |
| |
| if (copy_from_user(&parm, (void __user *)arg, |
| sizeof(struct stream_buffer_metainfo))) { |
| return -EFAULT; |
| } |
| stream_buffer_set_ext_buf(vbuf, parm.stbuf_start, |
| parm.stbuf_size, parm.stbuf_flag); |
| break; |
| } |
| case AMSTREAM_IOC_WR_STBUF_META: { |
| struct stream_buffer_metainfo meta; |
| struct stream_buf_s *vbuf = NULL; |
| |
| if (priv->vdec == NULL) { |
| pr_err("write %s, no vdec.\n", __func__); |
| return -EFAULT; |
| } |
| |
| vbuf = &priv->vdec->vbuf; |
| if (vbuf == NULL) { |
| pr_err("write %s, no stbuf.\n", __func__); |
| return -EFAULT; |
| } |
| |
| if (vbuf->ops == NULL) { |
| pr_err("write %s, no ops.\n", __func__); |
| return -EFAULT; |
| } |
| |
| if (copy_from_user(&meta, (void __user *)arg, |
| sizeof(struct stream_buffer_metainfo))) { |
| return -EFAULT; |
| } |
| if (!vbuf->ext_buf_addr) |
| return -ENODEV; |
| |
| stream_buffer_meta_write(vbuf, &meta); |
| break; |
| } |
| case AMSTREAM_IOC_GET_STBUF_STATUS: { |
| struct stream_buffer_status st; |
| struct stream_buf_s *pbuf = NULL; |
| |
| if (priv->vdec == NULL) { |
| pr_err("get status %s, no vdec.\n", __func__); |
| return -EFAULT; |
| } |
| |
| pbuf = &priv->vdec->vbuf; |
| if (pbuf == NULL) { |
| pr_err("get status %s, no stbuf.\n", __func__); |
| return -EFAULT; |
| } |
| |
| if (pbuf->ops == NULL) { |
| pr_err("get status %s, no ops.\n", __func__); |
| return -EFAULT; |
| } |
| |
| st.stbuf_start = pbuf->ext_buf_addr; |
| st.stbuf_size = pbuf->buf_size; |
| st.stbuf_rp = pbuf->ops->get_rp(pbuf); |
| st.stbuf_wp = pbuf->ops->get_wp(pbuf); |
| if (copy_to_user((void __user *)arg, &st, |
| sizeof(struct stream_buffer_status))) { |
| return -EFAULT; |
| } |
| break; |
| } |
| default: |
| r = -ENOIOCTLCMD; |
| break; |
| } |
| |
| return r; |
| } |
| |
| static long amstream_do_ioctl(struct port_priv_s *priv, |
| unsigned int cmd, ulong arg) |
| { |
| long r = 0; |
| |
| switch (cmd) { |
| case AMSTREAM_IOC_GET_VERSION: |
| case AMSTREAM_IOC_GET: |
| case AMSTREAM_IOC_SET: |
| case AMSTREAM_IOC_GET_EX: |
| case AMSTREAM_IOC_SET_EX: |
| case AMSTREAM_IOC_GET_PTR: |
| case AMSTREAM_IOC_SET_PTR: |
| case AMSTREAM_IOC_SYSINFO: |
| case AMSTREAM_IOC_GET_QOSINFO: |
| case AMSTREAM_IOC_GET_MVDECINFO: |
| case AMSTREAM_IOC_GET_AVINFO: |
| r = amstream_do_ioctl_new(priv, cmd, arg); |
| break; |
| default: |
| r = amstream_do_ioctl_old(priv, cmd, arg); |
| break; |
| } |
| if (r != 0) |
| pr_debug("amstream_do_ioctl error :%lx, %x\n", r, cmd); |
| |
| return r; |
| } |
| static long amstream_ioctl(struct file *file, unsigned int cmd, ulong arg) |
| { |
| struct port_priv_s *priv = (struct port_priv_s *)file->private_data; |
| struct stream_port_s *this = priv->port; |
| |
| if (!this) |
| return -ENODEV; |
| |
| return amstream_do_ioctl(priv, cmd, arg); |
| } |
| |
| #ifdef CONFIG_COMPAT |
| struct dec_sysinfo32 { |
| |
| u32 format; |
| |
| u32 width; |
| |
| u32 height; |
| |
| u32 rate; |
| |
| u32 extra; |
| |
| u32 status; |
| |
| u32 ratio; |
| |
| compat_uptr_t param; |
| |
| u64 ratio64; |
| }; |
| |
| struct am_ioctl_parm_ptr32 { |
| union { |
| compat_uptr_t pdata_audio_info; |
| compat_uptr_t pdata_sub_info; |
| compat_uptr_t pointer; |
| char data[8]; |
| }; |
| u32 cmd; |
| u32 len; |
| }; |
| |
| static long amstream_ioc_setget_ptr(struct port_priv_s *priv, |
| unsigned int cmd, struct am_ioctl_parm_ptr32 __user *arg) |
| { |
| struct am_ioctl_parm_ptr __user *data; |
| struct am_ioctl_parm_ptr32 param; |
| int ret; |
| |
| if (copy_from_user(¶m, |
| (void __user *)arg, |
| sizeof(struct am_ioctl_parm_ptr32))) |
| return -EFAULT; |
| |
| data = compat_alloc_user_space(sizeof(*data)); |
| if (!access_ok(data, sizeof(*data))) |
| return -EFAULT; |
| |
| if (put_user(param.cmd, &data->cmd) || |
| put_user(compat_ptr(param.pointer), &data->pointer) || |
| put_user(param.len, &data->len)) |
| return -EFAULT; |
| |
| ret = amstream_do_ioctl(priv, cmd, (unsigned long)data); |
| if (ret < 0) |
| return ret; |
| return 0; |
| |
| } |
| |
| static long amstream_set_sysinfo(struct port_priv_s *priv, |
| struct dec_sysinfo32 __user *arg) |
| { |
| struct dec_sysinfo __user *data; |
| struct dec_sysinfo32 __user *data32 = arg; |
| int ret; |
| struct dec_sysinfo32 param; |
| |
| if (copy_from_user(¶m, |
| (void __user *)arg, |
| sizeof(struct dec_sysinfo32))) |
| return -EFAULT; |
| |
| data = compat_alloc_user_space(sizeof(*data)); |
| if (!access_ok(data, sizeof(*data))) |
| return -EFAULT; |
| if (copy_in_user(data, data32, 7 * sizeof(u32))) |
| return -EFAULT; |
| if (put_user(compat_ptr(param.param), &data->param)) |
| return -EFAULT; |
| if (copy_in_user(&data->ratio64, &data32->ratio64, |
| sizeof(data->ratio64))) |
| return -EFAULT; |
| |
| ret = amstream_do_ioctl(priv, AMSTREAM_IOC_SYSINFO, |
| (unsigned long)data); |
| if (ret < 0) |
| return ret; |
| |
| if (copy_in_user(&arg->format, &data->format, 7 * sizeof(u32)) || |
| copy_in_user(&arg->ratio64, &data->ratio64, |
| sizeof(arg->ratio64))) |
| return -EFAULT; |
| |
| return 0; |
| } |
| |
| |
| struct userdata_param32_t { |
| uint32_t version; |
| uint32_t instance_id; /*input, 0~9*/ |
| uint32_t buf_len; /*input*/ |
| uint32_t data_size; /*output*/ |
| compat_uptr_t pbuf_addr; /*input*/ |
| struct userdata_meta_info_t meta_info; /*output*/ |
| }; |
| |
| |
| static long amstream_ioc_get_userdata(struct port_priv_s *priv, |
| struct userdata_param32_t __user *arg) |
| { |
| struct userdata_param_t __user *data; |
| struct userdata_param32_t __user *data32 = arg; |
| int ret; |
| struct userdata_param32_t param; |
| |
| |
| if (copy_from_user(¶m, |
| (void __user *)arg, |
| sizeof(struct userdata_param32_t))) |
| return -EFAULT; |
| |
| data = compat_alloc_user_space(sizeof(*data)); |
| if (!access_ok(data, sizeof(*data))) |
| return -EFAULT; |
| |
| if (copy_in_user(data, data32, 4 * sizeof(u32))) |
| return -EFAULT; |
| |
| if (copy_in_user(&data->meta_info, &data32->meta_info, |
| sizeof(data->meta_info))) |
| return -EFAULT; |
| |
| if (put_user(compat_ptr(param.pbuf_addr), &data->pbuf_addr)) |
| return -EFAULT; |
| |
| ret = amstream_do_ioctl(priv, AMSTREAM_IOC_UD_BUF_READ, |
| (unsigned long)data); |
| if (ret < 0) |
| return ret; |
| |
| if (copy_in_user(&data32->version, &data->version, 4 * sizeof(u32)) || |
| copy_in_user(&data32->meta_info, &data->meta_info, |
| sizeof(data32->meta_info))) |
| return -EFAULT; |
| |
| return 0; |
| } |
| |
| |
| static long amstream_compat_ioctl(struct file *file, |
| unsigned int cmd, ulong arg) |
| { |
| s32 r = 0; |
| struct port_priv_s *priv = (struct port_priv_s *)file->private_data; |
| |
| switch (cmd) { |
| case AMSTREAM_IOC_GET_VERSION: |
| case AMSTREAM_IOC_GET: |
| case AMSTREAM_IOC_SET: |
| case AMSTREAM_IOC_GET_EX: |
| case AMSTREAM_IOC_SET_EX: |
| return amstream_do_ioctl(priv, cmd, (ulong)compat_ptr(arg)); |
| case AMSTREAM_IOC_GET_PTR: |
| case AMSTREAM_IOC_SET_PTR: |
| return amstream_ioc_setget_ptr(priv, cmd, compat_ptr(arg)); |
| case AMSTREAM_IOC_SYSINFO: |
| return amstream_set_sysinfo(priv, compat_ptr(arg)); |
| case AMSTREAM_IOC_UD_BUF_READ: |
| return amstream_ioc_get_userdata(priv, compat_ptr(arg)); |
| default: |
| return amstream_do_ioctl(priv, cmd, (ulong)compat_ptr(arg)); |
| } |
| |
| return r; |
| } |
| #endif |
| |
| static ssize_t ports_show(struct class *class, struct class_attribute *attr, |
| char *buf) |
| { |
| int i; |
| char *pbuf = buf; |
| struct stream_port_s *p = NULL; |
| |
| for (i = 0; i < amstream_port_num; i++) { |
| p = &ports[i]; |
| /*name */ |
| pbuf += sprintf(pbuf, "%s\t:\n", p->name); |
| /*type */ |
| pbuf += sprintf(pbuf, "\ttype:%d( ", p->type); |
| if (p->type & PORT_TYPE_VIDEO) |
| pbuf += sprintf(pbuf, "%s ", "Video"); |
| if (p->type & PORT_TYPE_AUDIO) |
| pbuf += sprintf(pbuf, "%s ", "Audio"); |
| if (p->type & PORT_TYPE_MPTS) |
| pbuf += sprintf(pbuf, "%s ", "TS"); |
| if (p->type & PORT_TYPE_MPPS) |
| pbuf += sprintf(pbuf, "%s ", "PS"); |
| if (p->type & PORT_TYPE_ES) |
| pbuf += sprintf(pbuf, "%s ", "ES"); |
| if (p->type & PORT_TYPE_RM) |
| pbuf += sprintf(pbuf, "%s ", "RM"); |
| if (p->type & PORT_TYPE_SUB) |
| pbuf += sprintf(pbuf, "%s ", "Subtitle"); |
| if (p->type & PORT_TYPE_SUB_RD) |
| pbuf += sprintf(pbuf, "%s ", "Subtitle_Read"); |
| if (p->type & PORT_TYPE_USERDATA) |
| pbuf += sprintf(pbuf, "%s ", "userdata"); |
| pbuf += sprintf(pbuf, ")\n"); |
| /*flag */ |
| pbuf += sprintf(pbuf, "\tflag:%d( ", p->flag); |
| if (p->flag & PORT_FLAG_IN_USE) |
| pbuf += sprintf(pbuf, "%s ", "Used"); |
| else |
| pbuf += sprintf(pbuf, "%s ", "Unused"); |
| if ((p->type & PORT_TYPE_VIDEO) == 0) { |
| if (p->flag & PORT_FLAG_INITED) |
| pbuf += sprintf(pbuf, "%s ", "inited"); |
| else |
| pbuf += sprintf(pbuf, "%s ", "uninited"); |
| } |
| pbuf += sprintf(pbuf, ")\n"); |
| /*others */ |
| pbuf += sprintf(pbuf, "\tVformat:%d\n", |
| (p->flag & PORT_FLAG_VFORMAT) ? p->vformat : -1); |
| pbuf += sprintf(pbuf, "\tAformat:%d\n", |
| (p->flag & PORT_FLAG_AFORMAT) ? p->aformat : -1); |
| pbuf += sprintf(pbuf, "\tVid:%d\n", |
| (p->flag & PORT_FLAG_VID) ? p->vid : -1); |
| pbuf += sprintf(pbuf, "\tAid:%d\n", |
| (p->flag & PORT_FLAG_AID) ? p->aid : -1); |
| pbuf += sprintf(pbuf, "\tSid:%d\n", |
| (p->flag & PORT_FLAG_SID) ? p->sid : -1); |
| pbuf += sprintf(pbuf, "\tPCRid:%d\n", |
| (p->pcr_inited == 1) ? p->pcrid : -1); |
| pbuf += sprintf(pbuf, "\tachannel:%d\n", p->achanl); |
| pbuf += sprintf(pbuf, "\tasamprate:%d\n", p->asamprate); |
| pbuf += sprintf(pbuf, "\tadatawidth:%d\n\n", p->adatawidth); |
| } |
| return pbuf - buf; |
| } |
| |
| static int show_vbuf_status_cb(struct stream_buf_s *p, char *buf) |
| { |
| char *pbuf = buf; |
| |
| if (!p->buf_start) |
| return 0; |
| /*type */ |
| pbuf += sprintf(pbuf, "Video-%d buffer:", p->id); |
| /*flag */ |
| pbuf += sprintf(pbuf, "\tflag:%d( ", p->flag); |
| if (p->flag & BUF_FLAG_ALLOC) |
| pbuf += sprintf(pbuf, "%s ", "Alloc"); |
| else |
| pbuf += sprintf(pbuf, "%s ", "Unalloc"); |
| if (p->flag & BUF_FLAG_IN_USE) |
| pbuf += sprintf(pbuf, "%s ", "Used"); |
| else |
| pbuf += sprintf(pbuf, "%s ", "Noused"); |
| if (p->flag & BUF_FLAG_PARSER) |
| pbuf += sprintf(pbuf, "%s ", "Parser"); |
| else |
| pbuf += sprintf(pbuf, "%s ", "noParser"); |
| if (p->flag & BUF_FLAG_FIRST_TSTAMP) |
| pbuf += sprintf(pbuf, "%s ", "firststamp"); |
| else |
| pbuf += sprintf(pbuf, "%s ", "nofirststamp"); |
| pbuf += sprintf(pbuf, ")\n"); |
| |
| /*buf stats */ |
| pbuf += sprintf(pbuf, "\tbuf addr:%p\n", (void *)p->buf_start); |
| pbuf += sprintf(pbuf, "\tbuf size:%#x\n", p->buf_size); |
| pbuf += sprintf(pbuf, "\tbuf canusesize:%#x\n", p->canusebuf_size); |
| pbuf += sprintf(pbuf, "\tbuf regbase:%#lx\n", p->reg_base); |
| |
| if (p->reg_base && p->flag & BUF_FLAG_IN_USE) { |
| pbuf += sprintf(pbuf, "\tbuf level:%#x\n", |
| stbuf_level(p)); |
| pbuf += sprintf(pbuf, "\tbuf space:%#x\n", |
| stbuf_space(p)); |
| pbuf += sprintf(pbuf, "\tbuf read pointer:%#x\n", |
| stbuf_rp(p)); |
| } else |
| pbuf += sprintf(pbuf, "\tbuf no used.\n"); |
| |
| return pbuf - buf; |
| } |
| |
| static ssize_t bufs_show(struct class *class, struct class_attribute *attr, |
| char *buf) |
| { |
| int i; |
| char *pbuf = buf; |
| struct stream_buf_s *p = NULL; |
| char buf_type[][12] = { "Video", "Audio", "Subtitle", |
| "UserData", "HEVC" }; |
| |
| for (i = 0; i < amstream_buf_num; i++) { |
| p = &bufs[i]; |
| |
| if (!p->buf_start) |
| continue; |
| |
| /*type */ |
| pbuf += sprintf(pbuf, "%s buffer:", buf_type[p->type]); |
| /*flag */ |
| pbuf += sprintf(pbuf, "\tflag:%d( ", p->flag); |
| if (p->flag & BUF_FLAG_ALLOC) |
| pbuf += sprintf(pbuf, "%s ", "Alloc"); |
| else |
| pbuf += sprintf(pbuf, "%s ", "Unalloc"); |
| if (p->flag & BUF_FLAG_IN_USE) |
| pbuf += sprintf(pbuf, "%s ", "Used"); |
| else |
| pbuf += sprintf(pbuf, "%s ", "Noused"); |
| if (p->flag & BUF_FLAG_PARSER) |
| pbuf += sprintf(pbuf, "%s ", "Parser"); |
| else |
| pbuf += sprintf(pbuf, "%s ", "noParser"); |
| if (p->flag & BUF_FLAG_FIRST_TSTAMP) |
| pbuf += sprintf(pbuf, "%s ", "firststamp"); |
| else |
| pbuf += sprintf(pbuf, "%s ", "nofirststamp"); |
| pbuf += sprintf(pbuf, ")\n"); |
| /*buf stats */ |
| |
| pbuf += sprintf(pbuf, "\tbuf addr:%p\n", (void *)p->buf_start); |
| |
| if (p->type != BUF_TYPE_SUBTITLE) { |
| pbuf += sprintf(pbuf, "\tbuf size:%#x\n", p->buf_size); |
| pbuf += sprintf(pbuf, |
| "\tbuf canusesize:%#x\n", |
| p->canusebuf_size); |
| pbuf += sprintf(pbuf, |
| "\tbuf regbase:%#lx\n", p->reg_base); |
| |
| if (p->reg_base && p->flag & BUF_FLAG_IN_USE) { |
| if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_M6) { |
| /* TODO: mod gate */ |
| /* switch_mod_gate_by_name("vdec", 1);*/ |
| amports_switch_gate("vdec", 1); |
| } |
| pbuf += sprintf(pbuf, "\tbuf level:%#x\n", |
| stbuf_level(p)); |
| pbuf += sprintf(pbuf, "\tbuf space:%#x\n", |
| stbuf_space(p)); |
| pbuf += sprintf(pbuf, |
| "\tbuf read pointer:%#x\n", |
| stbuf_rp(p)); |
| if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_M6) { |
| /* TODO: mod gate */ |
| /* switch_mod_gate_by_name("vdec", 0);*/ |
| amports_switch_gate("vdec", 0); |
| } |
| } else |
| pbuf += sprintf(pbuf, "\tbuf no used.\n"); |
| |
| if (p->type == BUF_TYPE_USERDATA) { |
| pbuf += sprintf(pbuf, |
| "\tbuf write pointer:%#x\n", |
| p->buf_wp); |
| pbuf += sprintf(pbuf, |
| "\tbuf read pointer:%#x\n", |
| p->buf_rp); |
| } |
| } else { |
| u32 sub_wp, sub_rp, data_size; |
| |
| sub_wp = stbuf_sub_wp_get(); |
| sub_rp = stbuf_sub_rp_get(); |
| if (sub_wp >= sub_rp) |
| data_size = sub_wp - sub_rp; |
| else |
| data_size = p->buf_size - sub_rp + sub_wp; |
| pbuf += sprintf(pbuf, "\tbuf size:%#x\n", p->buf_size); |
| pbuf += |
| sprintf(pbuf, "\tbuf canusesize:%#x\n", |
| p->canusebuf_size); |
| pbuf += |
| sprintf(pbuf, "\tbuf start:%#x\n", |
| stbuf_sub_start_get()); |
| pbuf += sprintf(pbuf, |
| "\tbuf write pointer:%#x\n", sub_wp); |
| pbuf += sprintf(pbuf, |
| "\tbuf read pointer:%#x\n", sub_rp); |
| pbuf += sprintf(pbuf, "\tbuf level:%#x\n", data_size); |
| } |
| |
| pbuf += sprintf(pbuf, "\tbuf first_stamp:%#x\n", |
| p->first_tstamp); |
| pbuf += sprintf(pbuf, "\tbuf wcnt:%#x\n\n", p->wcnt); |
| pbuf += sprintf(pbuf, "\tbuf max_buffer_delay_ms:%dms\n", |
| p->max_buffer_delay_ms); |
| |
| if (p->reg_base && p->flag & BUF_FLAG_IN_USE) { |
| int calc_delayms = 0; |
| u32 bitrate = 0, avg_bitrate = 0; |
| |
| calc_delayms = calculation_stream_delayed_ms( |
| (p->type == BUF_TYPE_AUDIO) ? PTS_TYPE_AUDIO : |
| PTS_TYPE_VIDEO, |
| &bitrate, |
| &avg_bitrate); |
| |
| if (calc_delayms >= 0) { |
| pbuf += sprintf(pbuf, |
| "\tbuf current delay:%dms\n", |
| calc_delayms); |
| pbuf += sprintf(pbuf, |
| "\tbuf bitrate latest:%dbps,avg:%dbps\n", |
| bitrate, avg_bitrate); |
| pbuf += sprintf(pbuf, |
| "\tbuf time after last pts:%d ms\n", |
| calculation_stream_ext_delayed_ms |
| ((p->type == BUF_TYPE_AUDIO) ? PTS_TYPE_AUDIO : |
| PTS_TYPE_VIDEO)); |
| |
| pbuf += sprintf(pbuf, |
| "\tbuf time after last write data :%d ms\n", |
| (int)(jiffies_64 - |
| p->last_write_jiffies64) * 1000 / HZ); |
| } |
| } |
| if (p->write_thread) { |
| pbuf += sprintf(pbuf, |
| "\twrite thread:%d/%d,fifo %d:%d,passed:%d\n", |
| threadrw_buffer_level(p), |
| threadrw_buffer_size(p), |
| threadrw_datafifo_len(p), |
| threadrw_freefifo_len(p), |
| threadrw_passed_len(p) |
| ); |
| } |
| } |
| |
| pbuf += show_stream_buffer_status(pbuf, show_vbuf_status_cb); |
| |
| return pbuf - buf; |
| } |
| |
| static ssize_t videobufused_show(struct class *class, |
| struct class_attribute *attr, char *buf) |
| { |
| char *pbuf = buf; |
| struct stream_buf_s *p = NULL; |
| |
| p = &bufs[0]; |
| |
| if (p->flag & BUF_FLAG_IN_USE) |
| pbuf += sprintf(pbuf, "%d ", 1); |
| else |
| pbuf += sprintf(pbuf, "%d ", 0); |
| return 1; |
| } |
| |
| static ssize_t vcodec_profile_show(struct class *class, |
| struct class_attribute *attr, char *buf) |
| { |
| return vcodec_profile_read(buf); |
| } |
| |
| static ssize_t vcodec_feature_show(struct class *class, |
| struct class_attribute *attr, char *buf) |
| { |
| return vcodec_feature_read(buf); |
| } |
| |
| static int reset_canuse_buferlevel(int levelx10000) |
| { |
| int i; |
| struct stream_buf_s *p = NULL; |
| |
| if (levelx10000 >= 0 && levelx10000 <= 10000) |
| use_bufferlevelx10000 = levelx10000; |
| else |
| use_bufferlevelx10000 = 10000; |
| for (i = 0; i < amstream_buf_num; i++) { |
| p = &bufs[i]; |
| p->canusebuf_size = ((p->buf_size / 1024) * |
| use_bufferlevelx10000 / 10000) * 1024; |
| p->canusebuf_size += 1023; |
| p->canusebuf_size &= ~1023; |
| if (p->canusebuf_size > p->buf_size) |
| p->canusebuf_size = p->buf_size; |
| } |
| return 0; |
| } |
| |
| static ssize_t canuse_buferlevel_show(struct class *class, |
| struct class_attribute *attr, char *buf) |
| { |
| ssize_t size = sprintf(buf, |
| "use_bufferlevel=%d/10000[=(set range[ 0~10000])=\n", |
| use_bufferlevelx10000); |
| return size; |
| } |
| |
| static ssize_t canuse_buferlevel_store(struct class *class, |
| struct class_attribute *attr, |
| const char *buf, size_t size) |
| { |
| unsigned int val; |
| ssize_t ret; |
| |
| /*ret = sscanf(buf, "%d", &val);*/ |
| ret = kstrtoint(buf, 0, &val); |
| |
| if (ret != 0) |
| return -EINVAL; |
| (void)val; |
| reset_canuse_buferlevel(val); |
| return size; |
| } |
| |
| static ssize_t max_buffer_delay_ms_store(struct class *class, |
| struct class_attribute *attr, |
| const char *buf, size_t size) |
| { |
| unsigned int val; |
| ssize_t ret; |
| int i; |
| |
| /*ret = sscanf(buf, "%d", &val);*/ |
| ret = kstrtoint(buf, 0, &val); |
| if (ret != 0) |
| return -EINVAL; |
| for (i = 0; i < amstream_buf_num; i++) |
| bufs[i].max_buffer_delay_ms = val; |
| return size; |
| } |
| |
| static ssize_t max_buffer_delay_ms_show(struct class *class, |
| struct class_attribute *attr, |
| char *buf) |
| { |
| ssize_t size = 0; |
| |
| size += sprintf(buf, "%dms video max buffered data delay ms\n", |
| bufs[0].max_buffer_delay_ms); |
| size += sprintf(buf, "%dms audio max buffered data delay ms\n", |
| bufs[1].max_buffer_delay_ms); |
| return size; |
| } |
| |
| static ssize_t reset_audio_port_store(struct class *class, |
| struct class_attribute *attr, |
| const char *buf, size_t size) |
| { |
| unsigned int val = 0; |
| int i; |
| ssize_t ret; |
| struct stream_buf_s *pabuf = &bufs[BUF_TYPE_AUDIO]; |
| struct stream_port_s *this; |
| |
| ret = kstrtoint(buf, 0, &val); |
| if (ret != 0) |
| return -EINVAL; |
| if (val != 1) |
| return -EINVAL; |
| mutex_lock(&amstream_mutex); |
| for (i = 0; i < MAX_AMSTREAM_PORT_NUM; i++) { |
| if (strcmp(ports[i].name, "amstream_mpts") == 0 || |
| strcmp(ports[i].name, "amstream_mpts_sched") == 0) { |
| this = &ports[i]; |
| if ((this->flag & PORT_FLAG_AFORMAT) != 0) { |
| pr_info("audio_port_reset %s\n", ports[i].name); |
| audio_port_reset(this, pabuf); |
| } |
| } |
| } |
| mutex_unlock(&amstream_mutex); |
| return size; |
| } |
| |
| ssize_t dump_stream_show(struct class *class, |
| struct class_attribute *attr, char *buf) |
| { |
| char *p_buf = buf; |
| |
| p_buf += sprintf(p_buf, "\nmdkir -p /data/tmp -m 777;setenforce 0;\n\n"); |
| p_buf += sprintf(p_buf, "video:\n\t echo 0 > /sys/class/amstream/dump_stream;\n"); |
| p_buf += sprintf(p_buf, "hevc :\n\t echo 4 > /sys/class/amstream/dump_stream;\n"); |
| |
| return p_buf - buf; |
| } |
| |
| #define DUMP_STREAM_FILE "/data/tmp/dump_stream.h264" |
| ssize_t dump_stream_store(struct class *class, |
| struct class_attribute *attr, |
| const char *buf, size_t size) |
| { |
| struct stream_buf_s *p_buf; |
| int ret = 0, id = 0; |
| unsigned int stride, remain, level, vmap_size; |
| int write_size; |
| void *stbuf_vaddr; |
| unsigned long offset; |
| struct file *fp; |
| mm_segment_t old_fs; |
| loff_t fpos; |
| |
| ret = sscanf(buf, "%d", &id); |
| if (ret < 0) { |
| pr_info("paser buf id fail, default id = 0\n"); |
| id = 0; |
| } |
| if (id != BUF_TYPE_VIDEO && id != BUF_TYPE_HEVC) { |
| pr_info("buf id out of range, max %d, id %d, set default id 0\n", BUF_MAX_NUM - 1, id); |
| id = 0; |
| } |
| p_buf = get_stream_buffer(id); |
| if (!p_buf) { |
| pr_info("get buf fail, id %d\n", id); |
| return size; |
| } |
| if ((!p_buf->buf_size) || (p_buf->is_secure) || (!(p_buf->flag & BUF_FLAG_IN_USE))) { |
| pr_info("buf size %d, is_secure %d, in_use %d, it can not dump\n", |
| p_buf->buf_size, p_buf->is_secure, (p_buf->flag & BUF_FLAG_IN_USE)); |
| return size; |
| } |
| |
| level = stbuf_level(p_buf); |
| if (!level || level > p_buf->buf_size) { |
| pr_info("stream buf level %d, buf size %d, error return\n", level, p_buf->buf_size); |
| return size; |
| } |
| |
| fp = filp_open(DUMP_STREAM_FILE, O_CREAT | O_RDWR, 0666); |
| if (IS_ERR(fp)) { |
| fp = NULL; |
| pr_info("create dump stream file failed\n"); |
| return size; |
| } |
| |
| offset = p_buf->buf_start; |
| remain = level; |
| stride = SZ_1M; |
| vmap_size = 0; |
| fpos = 0; |
| pr_info("create file success, it will dump from addr 0x%lx, size 0x%x\n", offset, remain); |
| while (remain > 0) { |
| if (remain > stride) |
| vmap_size = stride; |
| else { |
| stride = remain; |
| vmap_size = stride; |
| } |
| |
| stbuf_vaddr = codec_mm_vmap(offset, vmap_size); |
| if (stbuf_vaddr == NULL) { |
| stride >>= 1; |
| pr_info("vmap fail change vmap stide size 0x%x\n", stride); |
| continue; |
| } |
| codec_mm_dma_flush(stbuf_vaddr, vmap_size, DMA_FROM_DEVICE); |
| |
| old_fs = get_fs(); |
| set_fs(KERNEL_DS); |
| write_size = vfs_write(fp, stbuf_vaddr, vmap_size, &fpos); |
| if (write_size < vmap_size) { |
| write_size += vfs_write(fp, stbuf_vaddr + write_size, vmap_size - write_size, &fpos); |
| pr_info("fail write retry, total %d, write %d\n", vmap_size, write_size); |
| if (write_size < vmap_size) { |
| pr_info("retry fail, interrupt dump stream, break\n"); |
| set_fs(old_fs); |
| break; |
| } |
| } |
| set_fs(old_fs); |
| vfs_fsync(fp, 0); |
| pr_info("vmap_size 0x%x dump size 0x%x\n", vmap_size, write_size); |
| |
| offset += vmap_size; |
| remain -= vmap_size; |
| codec_mm_unmap_phyaddr(stbuf_vaddr); |
| } |
| |
| filp_close(fp, current->files); |
| pr_info("dump stream buf end\n"); |
| |
| return size; |
| } |
| |
| static CLASS_ATTR_RO(ports); |
| static CLASS_ATTR_RO(bufs); |
| static CLASS_ATTR_RO(vcodec_profile); |
| static CLASS_ATTR_RO(vcodec_feature); |
| static CLASS_ATTR_RO(videobufused); |
| static CLASS_ATTR_RW(canuse_buferlevel); |
| static CLASS_ATTR_RW(max_buffer_delay_ms); |
| static CLASS_ATTR_WO(reset_audio_port); |
| |
| static struct attribute *amstream_class_attrs[] = { |
| &class_attr_ports.attr, |
| &class_attr_bufs.attr, |
| &class_attr_vcodec_profile.attr, |
| &class_attr_vcodec_feature.attr, |
| &class_attr_videobufused.attr, |
| &class_attr_canuse_buferlevel.attr, |
| &class_attr_max_buffer_delay_ms.attr, |
| &class_attr_reset_audio_port.attr, |
| NULL |
| }; |
| |
| ATTRIBUTE_GROUPS(amstream_class); |
| |
| static struct class amstream_class = { |
| .name = "amstream", |
| .class_groups = amstream_class_groups, |
| }; |
| |
| int amstream_request_firmware_from_sys(const char *file_name, |
| char *buf, int size) |
| { |
| const struct firmware *firmware; |
| int err = 0; |
| struct device *micro_dev; |
| |
| pr_info("try load %s ...", file_name); |
| micro_dev = device_create(&amstream_class, |
| NULL, MKDEV(AMSTREAM_MAJOR, 100), |
| NULL, "videodec"); |
| if (micro_dev == NULL) { |
| pr_err("device_create failed =%d\n", err); |
| return -1; |
| } |
| err = request_firmware(&firmware, file_name, micro_dev); |
| if (err < 0) { |
| pr_err("can't load the %s,err=%d\n", file_name, err); |
| goto error1; |
| } |
| if (firmware->size > size) { |
| pr_err("not enough memory size for audiodsp code\n"); |
| err = -ENOMEM; |
| goto release; |
| } |
| |
| memcpy(buf, (char *)firmware->data, firmware->size); |
| /*mb(); don't need it*/ |
| pr_err("load mcode size=%zd\n mcode name %s\n", firmware->size, |
| file_name); |
| err = firmware->size; |
| release: |
| release_firmware(firmware); |
| error1: |
| device_destroy(&amstream_class, MKDEV(AMSTREAM_MAJOR, 100)); |
| return err; |
| } |
| |
| int videobufused_show_fun(const char *trigger, int id, char *sbuf, int size) |
| { |
| int ret = -1; |
| void *buf, *getbuf = NULL; |
| if (size < PAGE_SIZE) { |
| getbuf = (void *)__get_free_page(GFP_KERNEL); |
| if (!getbuf) |
| return -ENOMEM; |
| buf = getbuf; |
| } else { |
| buf = sbuf; |
| } |
| |
| switch (id) { |
| case 0: |
| ret = videobufused_show(NULL, NULL , buf); |
| break; |
| default: |
| ret = -1; |
| } |
| if (ret > 0 && getbuf != NULL) { |
| ret = min_t(int, ret, size); |
| strncpy(sbuf, buf, ret); |
| } |
| if (getbuf != NULL) |
| free_page((unsigned long)getbuf); |
| return ret; |
| } |
| |
| static struct mconfig amports_configs[] = { |
| MC_PI32("def_4k_vstreambuf_sizeM", &def_4k_vstreambuf_sizeM), |
| MC_PI32("def_vstreambuf_sizeM", &def_vstreambuf_sizeM), |
| MC_PI32("slow_input", &slow_input), |
| MC_FUN_ID("videobufused", videobufused_show_fun, NULL, 0), |
| }; |
| |
| |
| |
| /*static struct resource memobj;*/ |
| static int amstream_probe(struct platform_device *pdev) |
| { |
| int i; |
| int r; |
| struct stream_port_s *st; |
| |
| pr_err("Amlogic A/V streaming port init\n"); |
| |
| amstream_port_num = MAX_AMSTREAM_PORT_NUM; |
| amstream_buf_num = BUF_MAX_NUM; |
| /* |
| * r = of_reserved_mem_device_init(&pdev->dev); |
| * if (r == 0) |
| * pr_info("of probe done"); |
| * else { |
| * r = -ENOMEM; |
| * return r; |
| * } |
| */ |
| r = class_register(&amstream_class); |
| if (r) { |
| pr_err("amstream class create fail.\n"); |
| return r; |
| } |
| |
| r = astream_dev_register(); |
| if (r) |
| return r; |
| |
| r = register_chrdev(AMSTREAM_MAJOR, "amstream", &amstream_fops); |
| if (r < 0) { |
| pr_err("Can't allocate major for amstreaming device\n"); |
| |
| goto error2; |
| } |
| |
| amstream_dev_class = class_create(THIS_MODULE, DEVICE_NAME); |
| |
| for (st = &ports[0], i = 0; i < amstream_port_num; i++, st++) { |
| st->class_dev = device_create(amstream_dev_class, NULL, |
| MKDEV(AMSTREAM_MAJOR, i), NULL, |
| ports[i].name); |
| } |
| |
| amstream_adec_status = NULL; |
| if (tsdemux_class_register() != 0) { |
| r = (-EIO); |
| goto error3; |
| } |
| tsdemux_tsync_func_init(); |
| init_waitqueue_head(&amstream_sub_wait); |
| init_waitqueue_head(&amstream_userdata_wait); |
| reset_canuse_buferlevel(10000); |
| amstream_pdev = pdev; |
| amports_clock_gate_init(&amstream_pdev->dev); |
| |
| /*prealloc fetch buf to avoid no continue buffer later...*/ |
| stbuf_fetch_init(); |
| REG_PATH_CONFIGS("media.amports", amports_configs); |
| |
| amstream_userdata_init(); |
| /* poweroff the decode core because dos can not be reset when reboot */ |
| if (get_cpu_major_id() == AM_MESON_CPU_MAJOR_ID_G12A) |
| vdec_power_reset(); |
| |
| return 0; |
| |
| /* |
| * error4: |
| * tsdemux_class_unregister(); |
| */ |
| error3: |
| for (st = &ports[0], i = 0; i < amstream_port_num; i++, st++) |
| device_destroy(amstream_dev_class, MKDEV(AMSTREAM_MAJOR, i)); |
| class_destroy(amstream_dev_class); |
| error2: |
| unregister_chrdev(AMSTREAM_MAJOR, "amstream"); |
| /* error1: */ |
| astream_dev_unregister(); |
| return r; |
| } |
| |
| static int amstream_remove(struct platform_device *pdev) |
| { |
| int i; |
| struct stream_port_s *st; |
| |
| if (bufs[BUF_TYPE_AUDIO].flag & BUF_FLAG_ALLOC) |
| stbuf_change_size(&bufs[BUF_TYPE_AUDIO], 0, false); |
| stbuf_fetch_release(); |
| tsdemux_class_unregister(); |
| for (st = &ports[0], i = 0; i < amstream_port_num; i++, st++) |
| device_destroy(amstream_dev_class, MKDEV(AMSTREAM_MAJOR, i)); |
| |
| class_destroy(amstream_dev_class); |
| |
| unregister_chrdev(AMSTREAM_MAJOR, "amstream"); |
| |
| class_unregister(&amstream_class); |
| |
| astream_dev_unregister(); |
| |
| amstream_adec_status = NULL; |
| |
| pr_err("Amlogic A/V streaming port release\n"); |
| |
| return 0; |
| } |
| |
| void set_adec_func(int (*adec_func)(struct adec_status *)) |
| { |
| amstream_adec_status = adec_func; |
| } |
| |
| void wakeup_sub_poll(void) |
| { |
| atomic_inc(&subdata_ready); |
| wake_up_interruptible(&amstream_sub_wait); |
| } |
| |
| int get_sub_type(void) |
| { |
| return sub_type; |
| } |
| |
| u32 get_audio_reset(void) |
| { |
| return amstream_audio_reset; |
| } |
| |
| /*get pes buffers */ |
| |
| struct stream_buf_s *get_stream_buffer(int id) |
| { |
| if (id >= BUF_MAX_NUM) |
| return 0; |
| return &bufs[id]; |
| } |
| EXPORT_SYMBOL(get_stream_buffer); |
| static const struct of_device_id amlogic_mesonstream_dt_match[] = { |
| { |
| .compatible = "amlogic, codec, streambuf", |
| }, |
| {}, |
| }; |
| |
| static struct platform_driver amstream_driver = { |
| .probe = amstream_probe, |
| .remove = amstream_remove, |
| .driver = { |
| .owner = THIS_MODULE, |
| .name = "mesonstream", |
| .of_match_table = amlogic_mesonstream_dt_match, |
| } |
| }; |
| |
| static int __init amstream_module_init(void) |
| { |
| if (platform_driver_register(&amstream_driver)) { |
| pr_err("failed to register amstream module\n"); |
| return -ENODEV; |
| } |
| |
| if (subtitle_init()) { |
| pr_err("failed to init subtitle\n"); |
| return -ENODEV; |
| } |
| |
| return 0; |
| } |
| |
| static void __exit amstream_module_exit(void) |
| { |
| platform_driver_unregister(&amstream_driver); |
| subtitle_exit(); |
| } |
| |
| module_init(amstream_module_init); |
| module_exit(amstream_module_exit); |
| |
| module_param(force_dv_mode, uint, 0664); |
| MODULE_PARM_DESC(force_dv_mode, |
| "\n force_dv_mode \n"); |
| |
| module_param(def_4k_vstreambuf_sizeM, uint, 0664); |
| MODULE_PARM_DESC(def_4k_vstreambuf_sizeM, |
| "\nDefault video Stream buf size for 4K MByptes\n"); |
| |
| module_param(def_vstreambuf_sizeM, uint, 0664); |
| MODULE_PARM_DESC(def_vstreambuf_sizeM, |
| "\nDefault video Stream buf size for < 1080p MByptes\n"); |
| |
| module_param(slow_input, uint, 0664); |
| MODULE_PARM_DESC(slow_input, "\n amstream slow_input\n"); |
| |
| |
| MODULE_DESCRIPTION("AMLOGIC streaming port driver"); |
| MODULE_LICENSE("GPL"); |
| MODULE_AUTHOR("Tim Yao <timyao@amlogic.com>"); |