blob: 69ec60c12c962710c1d77d789c68278af380524c [file] [log] [blame]
/*
* drivers/amlogic/media/frame_provider/decoder/utils/vdec.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/spinlock.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/semaphore.h>
#include <linux/sched/rt.h>
#include <linux/interrupt.h>
#include <linux/amlogic/media/utils/vformat.h>
#include <linux/amlogic/iomap.h>
#include <linux/amlogic/media/canvas/canvas.h>
#include <linux/amlogic/media/vfm/vframe.h>
#include <linux/amlogic/media/vfm/vframe_provider.h>
#include <linux/amlogic/media/vfm/vframe_receiver.h>
#include <linux/amlogic/media/video_sink/ionvideo_ext.h>
#ifdef CONFIG_AMLOGIC_V4L_VIDEO3
#include <linux/amlogic/media/video_sink/v4lvideo_ext.h>
#endif
#include <linux/amlogic/media/vfm/vfm_ext.h>
/*for VDEC_DEBUG_SUPPORT*/
#include <linux/time.h>
#include <linux/amlogic/media/utils/vdec_reg.h>
#include "../../../stream_input/amports/streambuf.h"
#include "vdec.h"
#include "vdec_trace.h"
#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
#include "vdec_profile.h"
#endif
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/libfdt_env.h>
#include <linux/of_reserved_mem.h>
#include <linux/dma-contiguous.h>
#include <linux/cma.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/dma-contiguous.h>
#include "../../../stream_input/amports/amports_priv.h"
#include <linux/amlogic/media/utils/amports_config.h>
#include "../utils/amvdec.h"
#include "vdec_input.h"
#include "../../../common/media_clock/clk/clk.h"
#include <linux/reset.h>
#include <linux/amlogic/cpu_version.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include <linux/amlogic/media/video_sink/video_keeper.h>
#include <linux/amlogic/media/codec_mm/configs.h>
#include <linux/amlogic/media/frame_sync/ptsserv.h>
#include "secprot.h"
#include "../../../common/chips/decoder_cpu_ver_info.h"
#include "frame_check.h"
#ifdef CONFIG_AMLOGIC_POWER
#include <linux/amlogic/power_ctrl.h>
#endif
#include <dt-bindings/power/sc2-pd.h>
#include <linux/amlogic/pwr_ctrl.h>
#include <linux/of_device.h>
#include "vdec_power_ctrl.h"
static DEFINE_MUTEX(vdec_mutex);
#define MC_SIZE (4096 * 4)
#define CMA_ALLOC_SIZE SZ_64M
#define MEM_NAME "vdec_prealloc"
static int inited_vcodec_num;
#define jiffies_ms div64_u64(get_jiffies_64() * 1000, HZ)
static int poweron_clock_level;
static int debug_vdetect = 0;
static int keep_vdec_mem;
static unsigned int debug_trace_num = 16 * 20;
static int step_mode;
static unsigned int clk_config;
#ifdef VDEC_FCC_SUPPORT
static int fcc_debug;
static void vdec_fcc_jump_back(struct vdec_s *vdec);
#endif
/*
* 0x1 : sched_priority to MAX_RT_PRIO -1.
* 0x2 : always reload firmware.
* 0x4 : vdec canvas debug enable
* 0x100: enable vdec fence.
*/
#define VDEC_DBG_SCHED_PRIO (0x1)
#define VDEC_DBG_ALWAYS_LOAD_FW (0x2)
#define VDEC_DBG_CANVAS_STATUS (0x4)
#define VDEC_DBG_ENABLE_FENCE (0x100)
#define FRAME_BASE_PATH_DI_V4LVIDEO_0 (29)
#define FRAME_BASE_PATH_DI_V4LVIDEO_1 (30)
#define FRAME_BASE_PATH_DI_V4LVIDEO_2 (31)
u32 debug = VDEC_DBG_ALWAYS_LOAD_FW;
EXPORT_SYMBOL(debug);
int hevc_max_reset_count;
EXPORT_SYMBOL(hevc_max_reset_count);
int no_powerdown;
EXPORT_SYMBOL(no_powerdown);
static int parallel_decode = 1;
static int fps_detection;
static int fps_clear;
static int force_nosecure_even_drm;
static int disable_switch_single_to_mult;
static DEFINE_SPINLOCK(vdec_spin_lock);
#define HEVC_TEST_LIMIT 100
#define GXBB_REV_A_MINOR 0xA
#define PRINT_FRAME_INFO 1
#define DISABLE_FRAME_INFO 2
#define RESET7_REGISTER_LEVEL 0x1127
#define P_RESETCTRL_RESET5_LEVEL 0x15
static int frameinfo_flag = 0;
static int v4lvideo_add_di = 1;
static int max_di_instance = 2;
static int max_supported_di_instance = 4;
//static int path_debug = 0;
static int enable_mvdec_info = 1;
int decode_underflow = 0;
static int enable_stream_mode_multi_dec;
#define CANVAS_MAX_SIZE (AMVDEC_CANVAS_MAX1 - AMVDEC_CANVAS_START_INDEX + 1 + AMVDEC_CANVAS_MAX2 + 1)
extern void vframe_rate_uevent(int duration);
struct am_reg {
char *name;
int offset;
};
struct vdec_isr_context_s {
int index;
int irq;
irq_handler_t dev_isr;
irq_handler_t dev_threaded_isr;
void *dev_id;
struct vdec_s *vdec;
};
struct decode_fps_s {
u32 frame_count;
u64 start_timestamp;
u64 last_timestamp;
u32 fps;
};
struct vdec_core_s {
struct list_head connected_vdec_list;
spinlock_t lock;
spinlock_t canvas_lock;
spinlock_t fps_lock;
spinlock_t input_lock;
struct ida ida;
atomic_t vdec_nr;
struct vdec_s *vfm_vdec;
struct vdec_s *active_vdec;
struct vdec_s *active_hevc;
struct vdec_s *hint_fr_vdec;
struct platform_device *vdec_core_platform_device;
struct device *cma_dev;
struct semaphore sem;
struct task_struct *thread;
struct workqueue_struct *vdec_core_wq;
unsigned long sched_mask;
struct vdec_isr_context_s isr_context[VDEC_IRQ_MAX];
int power_ref_count[VDEC_MAX];
struct vdec_s *last_vdec;
int parallel_dec;
unsigned long power_ref_mask;
int vdec_combine_flag;
struct decode_fps_s decode_fps[MAX_INSTANCE_MUN];
unsigned long buff_flag;
unsigned long stream_buff_flag;
struct power_manager_s *pm;
u32 vdec_resouce_status;
};
struct canvas_status_s {
int type;
int canvas_used_flag;
int id;
};
static struct vdec_core_s *vdec_core;
static const char * const vdec_status_string[] = {
"VDEC_STATUS_UNINITIALIZED",
"VDEC_STATUS_DISCONNECTED",
"VDEC_STATUS_CONNECTED",
"VDEC_STATUS_ACTIVE"
};
/*
bit [28] enable print
bit [23:16] etc
bit [15:12]
none 0 and not 0x1: force single
none 0 and 0x1: force multi
bit [8]
1: force dual
bit [3]
1: use mavs for single mode
bit [2]
1: force vfm path for frame mode
bit [1]
1: force esparser auto mode
bit [0]
1: disable audo manual mode ??
*/
static int debugflags;
static char vfm_path[VDEC_MAP_NAME_SIZE] = {"disable"};
static const char vfm_path_node[][VDEC_MAP_NAME_SIZE] =
{
"video_render.0",
"video_render.1",
"amvideo",
"videopip",
"deinterlace",
"dimulti.1",
"amlvideo",
"aml_video.1",
"amlvideo2.0",
"amlvideo2.1",
"ppmgr",
"ionvideo",
"ionvideo.1",
"ionvideo.2",
"ionvideo.3",
"ionvideo.4",
"ionvideo.5",
"ionvideo.6",
"ionvideo.7",
"ionvideo.8",
"videosync.0",
"v4lvideo.0",
"v4lvideo.1",
"v4lvideo.2",
"v4lvideo.3",
"v4lvideo.4",
"v4lvideo.5",
"v4lvideo.6",
"v4lvideo.7",
"v4lvideo.8",
"fake-amvideo",
"disable",
"reserved",
};
static struct canvas_status_s canvas_stat[AMVDEC_CANVAS_MAX1 - AMVDEC_CANVAS_START_INDEX + 1 + AMVDEC_CANVAS_MAX2 + 1];
int vdec_get_debug_flags(void)
{
return debugflags;
}
EXPORT_SYMBOL(vdec_get_debug_flags);
void VDEC_PRINT_FUN_LINENO(const char *fun, int line)
{
if (debugflags & 0x10000000)
pr_info("%s, %d\n", fun, line);
}
EXPORT_SYMBOL(VDEC_PRINT_FUN_LINENO);
bool is_support_no_parser(void)
{
if ((enable_stream_mode_multi_dec) ||
(get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_SC2))
return true;
return false;
}
EXPORT_SYMBOL(is_support_no_parser);
unsigned char is_mult_inc(unsigned int type)
{
unsigned char ret = 0;
if (vdec_get_debug_flags() & 0xf000)
ret = (vdec_get_debug_flags() & 0x1000)
? 1 : 0;
else if (type & PORT_TYPE_DECODER_SCHED)
ret = 1;
return ret;
}
EXPORT_SYMBOL(is_mult_inc);
static const bool cores_with_input[VDEC_MAX] = {
true, /* VDEC_1 */
false, /* VDEC_HCODEC */
false, /* VDEC_2 */
true, /* VDEC_HEVC / VDEC_HEVC_FRONT */
false, /* VDEC_HEVC_BACK */
};
static const int cores_int[VDEC_MAX] = {
VDEC_IRQ_1,
VDEC_IRQ_2,
VDEC_IRQ_0,
VDEC_IRQ_0,
VDEC_IRQ_HEVC_BACK
};
unsigned long vdec_canvas_lock(struct vdec_core_s *core)
{
unsigned long flags;
spin_lock_irqsave(&core->canvas_lock, flags);
return flags;
}
void vdec_canvas_unlock(struct vdec_core_s *core, unsigned long flags)
{
spin_unlock_irqrestore(&core->canvas_lock, flags);
}
unsigned long vdec_fps_lock(struct vdec_core_s *core)
{
unsigned long flags;
spin_lock_irqsave(&core->fps_lock, flags);
return flags;
}
void vdec_fps_unlock(struct vdec_core_s *core, unsigned long flags)
{
spin_unlock_irqrestore(&core->fps_lock, flags);
}
unsigned long vdec_core_lock(struct vdec_core_s *core)
{
unsigned long flags;
spin_lock_irqsave(&core->lock, flags);
return flags;
}
void vdec_core_unlock(struct vdec_core_s *core, unsigned long flags)
{
spin_unlock_irqrestore(&core->lock, flags);
}
unsigned long vdec_inputbuff_lock(struct vdec_core_s *core)
{
unsigned long flags;
spin_lock_irqsave(&core->input_lock, flags);
return flags;
}
void vdec_inputbuff_unlock(struct vdec_core_s *core, unsigned long flags)
{
spin_unlock_irqrestore(&core->input_lock, flags);
}
static bool vdec_is_input_frame_empty(struct vdec_s *vdec) {
struct vdec_core_s *core = vdec_core;
bool ret;
unsigned long flags;
flags = vdec_inputbuff_lock(core);
ret = !(vdec->core_mask & core->buff_flag);
vdec_inputbuff_unlock(core, flags);
return ret;
}
static void vdec_up(struct vdec_s *vdec)
{
struct vdec_core_s *core = vdec_core;
if (debug & 8)
pr_info("vdec_up, id:%d\n", vdec->id);
up(&core->sem);
}
static u64 vdec_get_us_time_system(void)
{
struct timeval tv;
do_gettimeofday(&tv);
return div64_u64(timeval_to_ns(&tv), 1000);
}
static void vdec_fps_clear(int id)
{
if (id >= MAX_INSTANCE_MUN)
return;
vdec_core->decode_fps[id].frame_count = 0;
vdec_core->decode_fps[id].start_timestamp = 0;
vdec_core->decode_fps[id].last_timestamp = 0;
vdec_core->decode_fps[id].fps = 0;
}
static void vdec_fps_clearall(void)
{
int i;
for (i = 0; i < MAX_INSTANCE_MUN; i++) {
vdec_core->decode_fps[i].frame_count = 0;
vdec_core->decode_fps[i].start_timestamp = 0;
vdec_core->decode_fps[i].last_timestamp = 0;
vdec_core->decode_fps[i].fps = 0;
}
}
static void vdec_fps_detec(int id)
{
unsigned long flags;
if (fps_detection == 0)
return;
if (id >= MAX_INSTANCE_MUN)
return;
flags = vdec_fps_lock(vdec_core);
if (fps_clear == 1) {
vdec_fps_clearall();
fps_clear = 0;
}
vdec_core->decode_fps[id].frame_count++;
if (vdec_core->decode_fps[id].frame_count == 1) {
vdec_core->decode_fps[id].start_timestamp =
vdec_get_us_time_system();
vdec_core->decode_fps[id].last_timestamp =
vdec_core->decode_fps[id].start_timestamp;
} else {
vdec_core->decode_fps[id].last_timestamp =
vdec_get_us_time_system();
vdec_core->decode_fps[id].fps =
(u32)div_u64(((u64)(vdec_core->decode_fps[id].frame_count) *
10000000000),
(vdec_core->decode_fps[id].last_timestamp -
vdec_core->decode_fps[id].start_timestamp));
}
vdec_fps_unlock(vdec_core, flags);
}
static int get_canvas(unsigned int index, unsigned int base)
{
int start;
int canvas_index = index * base;
int ret;
if ((base > 4) || (base == 0))
return -1;
if ((AMVDEC_CANVAS_START_INDEX + canvas_index + base - 1)
<= AMVDEC_CANVAS_MAX1) {
start = AMVDEC_CANVAS_START_INDEX + base * index;
} else {
canvas_index -= (AMVDEC_CANVAS_MAX1 -
AMVDEC_CANVAS_START_INDEX + 1) / base * base;
if (canvas_index <= AMVDEC_CANVAS_MAX2)
start = canvas_index / base;
else
return -1;
}
if (base == 1) {
ret = start;
} else if (base == 2) {
ret = ((start + 1) << 16) | ((start + 1) << 8) | start;
} else if (base == 3) {
ret = ((start + 2) << 16) | ((start + 1) << 8) | start;
} else if (base == 4) {
ret = (((start + 3) << 24) | (start + 2) << 16) |
((start + 1) << 8) | start;
}
return ret;
}
static int get_canvas_ex(int type, int id)
{
int i;
unsigned long flags;
flags = vdec_canvas_lock(vdec_core);
for (i = 0; i < CANVAS_MAX_SIZE; i++) {
if ((canvas_stat[i].type == type) &&
(canvas_stat[i].id & (1 << id)) == 0) {
canvas_stat[i].canvas_used_flag++;
canvas_stat[i].id |= (1 << id);
if (debug & 4)
pr_debug("get used canvas %d\n", i);
vdec_canvas_unlock(vdec_core, flags);
if (i < AMVDEC_CANVAS_MAX2 + 1)
return i;
else
return (i + AMVDEC_CANVAS_START_INDEX - AMVDEC_CANVAS_MAX2 - 1);
}
}
for (i = 0; i < CANVAS_MAX_SIZE; i++) {
if (canvas_stat[i].type == 0) {
canvas_stat[i].type = type;
canvas_stat[i].canvas_used_flag = 1;
canvas_stat[i].id = (1 << id);
if (debug & 4) {
pr_debug("get canvas %d\n", i);
pr_debug("canvas_used_flag %d\n",
canvas_stat[i].canvas_used_flag);
pr_debug("canvas_stat[i].id %d\n",
canvas_stat[i].id);
}
vdec_canvas_unlock(vdec_core, flags);
if (i < AMVDEC_CANVAS_MAX2 + 1)
return i;
else
return (i + AMVDEC_CANVAS_START_INDEX - AMVDEC_CANVAS_MAX2 - 1);
}
}
vdec_canvas_unlock(vdec_core, flags);
pr_info("cannot get canvas\n");
return -1;
}
static void free_canvas_ex(int index, int id)
{
unsigned long flags;
int offset;
flags = vdec_canvas_lock(vdec_core);
if (index >= 0 &&
index < AMVDEC_CANVAS_MAX2 + 1)
offset = index;
else if ((index >= AMVDEC_CANVAS_START_INDEX) &&
(index <= AMVDEC_CANVAS_MAX1))
offset = index + AMVDEC_CANVAS_MAX2 + 1 - AMVDEC_CANVAS_START_INDEX;
else {
vdec_canvas_unlock(vdec_core, flags);
return;
}
if ((canvas_stat[offset].canvas_used_flag > 0) &&
(canvas_stat[offset].id & (1 << id))) {
canvas_stat[offset].canvas_used_flag--;
canvas_stat[offset].id &= ~(1 << id);
if (canvas_stat[offset].canvas_used_flag == 0) {
canvas_stat[offset].type = 0;
canvas_stat[offset].id = 0;
}
if (debug & 4) {
pr_debug("free index %d used_flag %d, type = %d, id = %d\n",
offset,
canvas_stat[offset].canvas_used_flag,
canvas_stat[offset].type,
canvas_stat[offset].id);
}
}
vdec_canvas_unlock(vdec_core, flags);
return;
}
static void vdec_dmc_pipeline_reset(void)
{
/*
* bit15: vdec_piple
* bit14: hevc_dmc_piple
* bit13: hevcf_dmc_pipl
* bit12: wave420_dmc_pipl
* bit11: hcodec_dmc_pipl
*/
WRITE_RESET_REG(RESET7_REGISTER,
(1 << 15) | (1 << 14) | (1 << 13) |
(1 << 12) | (1 << 11));
}
static void vdec_stop_armrisc(int hw)
{
ulong timeout = jiffies + HZ;
if (hw == VDEC_INPUT_TARGET_VLD) {
WRITE_VREG(MPSR, 0);
WRITE_VREG(CPSR, 0);
while (READ_VREG(IMEM_DMA_CTRL) & 0x8000) {
if (time_after(jiffies, timeout))
break;
}
timeout = jiffies + HZ;
while (READ_VREG(LMEM_DMA_CTRL) & 0x8000) {
if (time_after(jiffies, timeout))
break;
}
} else if (hw == VDEC_INPUT_TARGET_HEVC) {
WRITE_VREG(HEVC_MPSR, 0);
WRITE_VREG(HEVC_CPSR, 0);
while (READ_VREG(HEVC_IMEM_DMA_CTRL) & 0x8000) {
if (time_after(jiffies, timeout))
break;
}
timeout = jiffies + HZ/10;
while (READ_VREG(HEVC_LMEM_DMA_CTRL) & 0x8000) {
if (time_after(jiffies, timeout))
break;
}
}
}
static void vdec_disable_DMC(struct vdec_s *vdec)
{
/*close first,then wait pedding end,timing suggestion from vlsi*/
struct vdec_input_s *input = &vdec->input;
unsigned long flags;
unsigned int mask = 0;
if (input->target == VDEC_INPUT_TARGET_VLD) {
mask = (1 << 13);
if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_G12A)
mask = (1 << 21);
} else if (input->target == VDEC_INPUT_TARGET_HEVC) {
mask = (1 << 4); /*hevc*/
if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_G12A)
mask |= (1 << 8); /*hevcb */
}
/* need to stop armrisc. */
if (!IS_ERR_OR_NULL(vdec->dev))
vdec_stop_armrisc(input->target);
spin_lock_irqsave(&vdec_spin_lock, flags);
codec_dmcbus_write(DMC_REQ_CTRL,
codec_dmcbus_read(DMC_REQ_CTRL) & ~mask);
spin_unlock_irqrestore(&vdec_spin_lock, flags);
if (is_cpu_tm2_revb() ||
(get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_SC2)) {
while (!(codec_dmcbus_read(TM2_REVB_DMC_CHAN_STS)
& mask))
;
} else {
while (!(codec_dmcbus_read(DMC_CHAN_STS)
& mask))
;
}
pr_debug("%s input->target= 0x%x\n", __func__, input->target);
}
static void vdec_enable_DMC(struct vdec_s *vdec)
{
struct vdec_input_s *input = &vdec->input;
unsigned long flags;
unsigned int mask = 0;
if (input->target == VDEC_INPUT_TARGET_VLD) {
mask = (1 << 13);
if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_G12A)
mask = (1 << 21);
} else if (input->target == VDEC_INPUT_TARGET_HEVC) {
mask = (1 << 4); /*hevc*/
if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_G12A)
mask |= (1 << 8); /*hevcb */
}
/*must to be reset the dmc pipeline if it's g12b.*/
if (get_cpu_type() == AM_MESON_CPU_MAJOR_ID_G12B)
vdec_dmc_pipeline_reset();
spin_lock_irqsave(&vdec_spin_lock, flags);
codec_dmcbus_write(DMC_REQ_CTRL,
codec_dmcbus_read(DMC_REQ_CTRL) | mask);
spin_unlock_irqrestore(&vdec_spin_lock, flags);
pr_debug("%s input->target= 0x%x\n", __func__, input->target);
}
static int vdec_get_hw_type(int value)
{
int type;
switch (value) {
case VFORMAT_HEVC:
case VFORMAT_VP9:
case VFORMAT_AVS2:
case VFORMAT_AV1:
type = CORE_MASK_HEVC;
break;
case VFORMAT_MPEG12:
case VFORMAT_MPEG4:
case VFORMAT_H264:
case VFORMAT_MJPEG:
case VFORMAT_REAL:
case VFORMAT_JPEG:
case VFORMAT_VC1:
case VFORMAT_AVS:
case VFORMAT_YUV:
case VFORMAT_H264MVC:
case VFORMAT_H264_4K2K:
case VFORMAT_H264_ENC:
case VFORMAT_JPEG_ENC:
type = CORE_MASK_VDEC_1;
break;
default:
type = -1;
}
return type;
}
static void vdec_save_active_hw(struct vdec_s *vdec)
{
int type;
type = vdec_get_hw_type(vdec->port->vformat);
if (type == CORE_MASK_HEVC) {
vdec_core->active_hevc = vdec;
} else if (type == CORE_MASK_VDEC_1) {
vdec_core->active_vdec = vdec;
} else {
pr_info("save_active_fw wrong\n");
}
}
static void vdec_update_buff_status(void)
{
struct vdec_core_s *core = vdec_core;
unsigned long flags;
struct vdec_s *vdec;
flags = vdec_inputbuff_lock(core);
core->buff_flag = 0;
core->stream_buff_flag = 0;
list_for_each_entry(vdec, &core->connected_vdec_list, list) {
struct vdec_input_s *input = &vdec->input;
if (input_frame_based(input)) {
if (input->have_frame_num || input->eos)
core->buff_flag |= vdec->core_mask;
} else if (input_stream_based(input)) {
core->stream_buff_flag |= vdec->core_mask;
}
/* slave el pre_decode_level wp update */
if ((is_support_no_parser()) && (vdec->slave)) {
STBUF_WRITE(&vdec->slave->vbuf, set_wp,
STBUF_READ(&vdec->vbuf, get_wp));
}
}
vdec_inputbuff_unlock(core, flags);
}
#if 0
void vdec_update_streambuff_status(void)
{
struct vdec_core_s *core = vdec_core;
struct vdec_s *vdec;
/* check streaming prepare level threshold if not EOS */
list_for_each_entry(vdec, &core->connected_vdec_list, list) {
struct vdec_input_s *input = &vdec->input;
if (input && input_stream_based(input) && !input->eos &&
(vdec->need_more_data & VDEC_NEED_MORE_DATA)) {
u32 rp, wp, level;
rp = STBUF_READ(&vdec->vbuf, get_rp);
wp = STBUF_READ(&vdec->vbuf, get_wp);
if (wp < rp)
level = input->size + wp - rp;
else
level = wp - rp;
if ((level < input->prepare_level) &&
(pts_get_rec_num(PTS_TYPE_VIDEO,
vdec->input.total_rd_count) < 2)) {
break;
} else if (level > input->prepare_level) {
vdec->need_more_data &= ~VDEC_NEED_MORE_DATA;
if (debug & 8)
pr_info("vdec_flush_streambuff_status up\n");
vdec_up(vdec);
}
break;
}
}
}
EXPORT_SYMBOL(vdec_update_streambuff_status);
#endif
int vdec_status(struct vdec_s *vdec, struct vdec_info *vstatus)
{
if (vdec && vdec->dec_status &&
((vdec->status == VDEC_STATUS_CONNECTED ||
vdec->status == VDEC_STATUS_ACTIVE)))
return vdec->dec_status(vdec, vstatus);
return 0;
}
EXPORT_SYMBOL(vdec_status);
int vdec_set_trickmode(struct vdec_s *vdec, unsigned long trickmode)
{
int r;
if (vdec->set_trickmode) {
r = vdec->set_trickmode(vdec, trickmode);
if ((r == 0) && (vdec->slave) && (vdec->slave->set_trickmode))
r = vdec->slave->set_trickmode(vdec->slave,
trickmode);
return r;
}
return -1;
}
EXPORT_SYMBOL(vdec_set_trickmode);
int vdec_set_isreset(struct vdec_s *vdec, int isreset)
{
vdec->is_reset = isreset;
pr_info("is_reset=%d\n", isreset);
if (vdec->set_isreset)
return vdec->set_isreset(vdec, isreset);
return 0;
}
EXPORT_SYMBOL(vdec_set_isreset);
int vdec_set_dv_metawithel(struct vdec_s *vdec, int isdvmetawithel)
{
vdec->dolby_meta_with_el = isdvmetawithel;
pr_info("isdvmetawithel=%d\n", isdvmetawithel);
return 0;
}
EXPORT_SYMBOL(vdec_set_dv_metawithel);
void vdec_set_no_powerdown(int flag)
{
no_powerdown = flag;
pr_info("no_powerdown=%d\n", no_powerdown);
return;
}
EXPORT_SYMBOL(vdec_set_no_powerdown);
void vdec_count_info(struct vdec_info *vs, unsigned int err,
unsigned int offset)
{
if (err)
vs->error_frame_count++;
if (offset) {
if (0 == vs->frame_count) {
vs->offset = 0;
vs->samp_cnt = 0;
}
vs->frame_data = offset > vs->total_data ?
offset - vs->total_data : vs->total_data - offset;
vs->total_data = offset;
if (vs->samp_cnt < 96000 * 2) { /* 2s */
if (0 == vs->samp_cnt)
vs->offset = offset;
vs->samp_cnt += vs->frame_dur;
} else {
vs->bit_rate = (offset - vs->offset) / 2;
/*pr_info("bitrate : %u\n",vs->bit_rate);*/
vs->samp_cnt = 0;
}
vs->frame_count++;
}
/*pr_info("size : %u, offset : %u, dur : %u, cnt : %u\n",
vs->offset,offset,vs->frame_dur,vs->samp_cnt);*/
return;
}
EXPORT_SYMBOL(vdec_count_info);
int vdec_is_support_4k(void)
{
return !is_meson_gxl_package_805X();
}
EXPORT_SYMBOL(vdec_is_support_4k);
/*
* clk_config:
*0:default
*1:no gp0_pll;
*2:always used gp0_pll;
*>=10:fixed n M clk;
*== 100 , 100M clks;
*/
unsigned int get_vdec_clk_config_settings(void)
{
return clk_config;
}
void update_vdec_clk_config_settings(unsigned int config)
{
clk_config = config;
}
EXPORT_SYMBOL(update_vdec_clk_config_settings);
struct device *get_codec_cma_device(void)
{
return vdec_core->cma_dev;
}
#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
static const char * const vdec_device_name[] = {
"amvdec_mpeg12", "ammvdec_mpeg12",
"amvdec_mpeg4", "ammvdec_mpeg4",
"amvdec_h264", "ammvdec_h264",
"amvdec_mjpeg", "ammvdec_mjpeg",
"amvdec_real", "ammvdec_real",
"amjpegdec", "ammjpegdec",
"amvdec_vc1", "ammvdec_vc1",
"amvdec_avs", "ammvdec_avs",
"amvdec_yuv", "ammvdec_yuv",
"amvdec_h264mvc", "ammvdec_h264mvc",
"amvdec_h264_4k2k", "ammvdec_h264_4k2k",
"amvdec_h265", "ammvdec_h265",
"amvenc_avc", "amvenc_avc",
"jpegenc", "jpegenc",
"amvdec_vp9", "ammvdec_vp9",
"amvdec_avs2", "ammvdec_avs2",
"amvdec_av1", "ammvdec_av1",
};
#else
static const char * const vdec_device_name[] = {
"amvdec_mpeg12",
"amvdec_mpeg4",
"amvdec_h264",
"amvdec_mjpeg",
"amvdec_real",
"amjpegdec",
"amvdec_vc1",
"amvdec_avs",
"amvdec_yuv",
"amvdec_h264mvc",
"amvdec_h264_4k2k",
"amvdec_h265",
"amvenc_avc",
"jpegenc",
"amvdec_vp9",
"amvdec_avs2",
"amvdec_av1"
};
#endif
/*
* Only support time sliced decoding for frame based input,
* so legacy decoder can exist with time sliced decoder.
*/
static const char *get_dev_name(bool use_legacy_vdec, int format)
{
#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
if (use_legacy_vdec && (debugflags & 0x8) == 0)
return vdec_device_name[format * 2];
else
return vdec_device_name[format * 2 + 1];
#else
return vdec_device_name[format];
#endif
}
#ifdef VDEC_DEBUG_SUPPORT
static u64 get_current_clk(void)
{
/*struct timespec xtime = current_kernel_time();
u64 usec = xtime.tv_sec * 1000000;
usec += xtime.tv_nsec / 1000;
*/
u64 usec = sched_clock();
return usec;
}
static void inc_profi_count(unsigned long mask, u32 *count)
{
enum vdec_type_e type;
for (type = VDEC_1; type < VDEC_MAX; type++) {
if (mask & (1 << type))
count[type]++;
}
}
static void update_profi_clk_run(struct vdec_s *vdec,
unsigned long mask, u64 clk)
{
enum vdec_type_e type;
for (type = VDEC_1; type < VDEC_MAX; type++) {
if (mask & (1 << type)) {
vdec->start_run_clk[type] = clk;
if (vdec->profile_start_clk[type] == 0)
vdec->profile_start_clk[type] = clk;
vdec->total_clk[type] = clk
- vdec->profile_start_clk[type];
/*pr_info("set start_run_clk %ld\n",
vdec->start_run_clk);*/
}
}
}
static void update_profi_clk_stop(struct vdec_s *vdec,
unsigned long mask, u64 clk)
{
enum vdec_type_e type;
for (type = VDEC_1; type < VDEC_MAX; type++) {
if (mask & (1 << type)) {
if (vdec->start_run_clk[type] == 0)
pr_info("error, start_run_clk[%d] not set\n", type);
/*pr_info("update run_clk type %d, %ld, %ld, %ld\n",
type,
clk,
vdec->start_run_clk[type],
vdec->run_clk[type]);*/
vdec->run_clk[type] +=
(clk - vdec->start_run_clk[type]);
}
}
}
#endif
int vdec_set_decinfo(struct vdec_s *vdec, struct dec_sysinfo *p)
{
if (copy_from_user((void *)&vdec->sys_info_store, (void *)p,
sizeof(struct dec_sysinfo)))
return -EFAULT;
/* force switch to mult instance if supports this profile. */
if ((vdec->type == VDEC_TYPE_SINGLE) &&
!disable_switch_single_to_mult) {
const char *str = NULL;
char fmt[16] = {0};
str = strchr(get_dev_name(false, vdec->format), '_');
if (!str)
return -1;
sprintf(fmt, "m%s", ++str);
if (is_support_profile(fmt) &&
vdec->sys_info->format != VIDEO_DEC_FORMAT_H263 &&
vdec->format != VFORMAT_AV1)
vdec->type = VDEC_TYPE_STREAM_PARSER;
}
return 0;
}
EXPORT_SYMBOL(vdec_set_decinfo);
/* construct vdec strcture */
struct vdec_s *vdec_create(struct stream_port_s *port,
struct vdec_s *master)
{
struct vdec_s *vdec;
int type = VDEC_TYPE_SINGLE;
int id;
if (is_mult_inc(port->type))
type = (port->type & PORT_TYPE_FRAME) ?
VDEC_TYPE_FRAME_BLOCK :
VDEC_TYPE_STREAM_PARSER;
id = ida_simple_get(&vdec_core->ida,
0, MAX_INSTANCE_MUN, GFP_KERNEL);
if (id < 0) {
pr_info("vdec_create request id failed!ret =%d\n", id);
return NULL;
}
vdec = vzalloc(sizeof(struct vdec_s));
/* TBD */
if (vdec) {
vdec->magic = 0x43454456;
vdec->id = -1;
vdec->type = type;
vdec->port = port;
vdec->sys_info = &vdec->sys_info_store;
INIT_LIST_HEAD(&vdec->list);
atomic_inc(&vdec_core->vdec_nr);
#ifdef CONFIG_AMLOGIC_V4L_VIDEO3
v4lvideo_dec_count_increase();
#endif
vdec->id = id;
vdec_input_init(&vdec->input, vdec);
vdec->input.vdec_is_input_frame_empty = vdec_is_input_frame_empty;
vdec->input.vdec_up = vdec_up;
if (master) {
vdec->master = master;
master->slave = vdec;
master->sched = 1;
}
if (enable_mvdec_info) {
vdec->mvfrm = (struct vdec_frames_s *)
vzalloc(sizeof(struct vdec_frames_s));
if (!vdec->mvfrm)
pr_err("vzalloc: vdec_frames_s failed\n");
}
}
pr_debug("vdec_create instance %p, total %d, PM: %s\n", vdec,
atomic_read(&vdec_core->vdec_nr),
get_pm_name(vdec_core->pm->pm_type));
//trace_vdec_create(vdec); /*DEBUG_TMP*/
return vdec;
}
EXPORT_SYMBOL(vdec_create);
int vdec_set_format(struct vdec_s *vdec, int format)
{
vdec->format = format;
vdec->port_flag |= PORT_FLAG_VFORMAT;
if (vdec->slave) {
vdec->slave->format = format;
vdec->slave->port_flag |= PORT_FLAG_VFORMAT;
}
//trace_vdec_set_format(vdec, format);/*DEBUG_TMP*/
return 0;
}
EXPORT_SYMBOL(vdec_set_format);
int vdec_set_pts(struct vdec_s *vdec, u32 pts)
{
vdec->pts = pts;
vdec->pts64 = div64_u64((u64)pts * 100, 9);
vdec->pts_valid = true;
//trace_vdec_set_pts(vdec, (u64)pts);/*DEBUG_TMP*/
return 0;
}
EXPORT_SYMBOL(vdec_set_pts);
void vdec_set_timestamp(struct vdec_s *vdec, u64 timestamp)
{
vdec->timestamp = timestamp;
vdec->timestamp_valid = true;
}
EXPORT_SYMBOL(vdec_set_timestamp);
int vdec_set_pts64(struct vdec_s *vdec, u64 pts64)
{
vdec->pts64 = pts64;
vdec->pts = (u32)div64_u64(pts64 * 9, 100);
vdec->pts_valid = true;
//trace_vdec_set_pts64(vdec, pts64);/*DEBUG_TMP*/
return 0;
}
EXPORT_SYMBOL(vdec_set_pts64);
int vdec_get_status(struct vdec_s *vdec)
{
return vdec->status;
}
EXPORT_SYMBOL(vdec_get_status);
int vdec_get_frame_num(struct vdec_s *vdec)
{
return vdec->input.have_frame_num;
}
EXPORT_SYMBOL(vdec_get_frame_num);
void vdec_set_status(struct vdec_s *vdec, int status)
{
//trace_vdec_set_status(vdec, status);/*DEBUG_TMP*/
vdec->status = status;
}
EXPORT_SYMBOL(vdec_set_status);
void vdec_set_next_status(struct vdec_s *vdec, int status)
{
//trace_vdec_set_next_status(vdec, status);/*DEBUG_TMP*/
vdec->next_status = status;
}
EXPORT_SYMBOL(vdec_set_next_status);
int vdec_set_video_path(struct vdec_s *vdec, int video_path)
{
vdec->frame_base_video_path = video_path;
return 0;
}
EXPORT_SYMBOL(vdec_set_video_path);
int vdec_set_receive_id(struct vdec_s *vdec, int receive_id)
{
vdec->vf_receiver_inst = receive_id;
return 0;
}
EXPORT_SYMBOL(vdec_set_receive_id);
/* add frame data to input chain */
int vdec_write_vframe(struct vdec_s *vdec, const char *buf, size_t count)
{
return vdec_input_add_frame(&vdec->input, buf, count);
}
EXPORT_SYMBOL(vdec_write_vframe);
int vdec_write_vframe_with_dma(struct vdec_s *vdec,
ulong addr, size_t count, u32 handle, chunk_free free, void* priv)
{
return vdec_input_add_frame_with_dma(&vdec->input,
addr, count, handle, free, priv);
}
EXPORT_SYMBOL(vdec_write_vframe_with_dma);
/* add a work queue thread for vdec*/
void vdec_schedule_work(struct work_struct *work)
{
if (vdec_core->vdec_core_wq)
queue_work(vdec_core->vdec_core_wq, work);
else
schedule_work(work);
}
EXPORT_SYMBOL(vdec_schedule_work);
static struct vdec_s *vdec_get_associate(struct vdec_s *vdec)
{
if (vdec->master)
return vdec->master;
else if (vdec->slave)
return vdec->slave;
return NULL;
}
static void vdec_sync_input_read(struct vdec_s *vdec)
{
if (!vdec_stream_based(vdec))
return;
if (vdec_dual(vdec)) {
u32 me, other;
if (vdec->input.target == VDEC_INPUT_TARGET_VLD) {
me = READ_VREG(VLD_MEM_VIFIFO_WRAP_COUNT);
other =
vdec_get_associate(vdec)->input.stream_cookie;
if (me > other)
return;
else if (me == other) {
me = READ_VREG(VLD_MEM_VIFIFO_RP);
other =
vdec_get_associate(vdec)->input.swap_rp;
if (me > other) {
STBUF_WRITE(&vdec->vbuf, set_rp,
vdec_get_associate(vdec)->input.swap_rp);
return;
}
}
STBUF_WRITE(&vdec->vbuf, set_rp,
READ_VREG(VLD_MEM_VIFIFO_RP));
} else if (vdec->input.target == VDEC_INPUT_TARGET_HEVC) {
me = READ_VREG(HEVC_SHIFT_BYTE_COUNT);
if (((me & 0x80000000) == 0) &&
(vdec->input.streaming_rp & 0x80000000))
me += 1ULL << 32;
other = vdec_get_associate(vdec)->input.streaming_rp;
if (me > other) {
STBUF_WRITE(&vdec->vbuf, set_rp,
vdec_get_associate(vdec)->input.swap_rp);
return;
}
STBUF_WRITE(&vdec->vbuf, set_rp,
READ_VREG(HEVC_STREAM_RD_PTR));
}
} else if (vdec->input.target == VDEC_INPUT_TARGET_VLD) {
STBUF_WRITE(&vdec->vbuf, set_rp,
READ_VREG(VLD_MEM_VIFIFO_RP));
} else if (vdec->input.target == VDEC_INPUT_TARGET_HEVC) {
STBUF_WRITE(&vdec->vbuf, set_rp,
READ_VREG(HEVC_STREAM_RD_PTR));
}
}
static void vdec_sync_input_write(struct vdec_s *vdec)
{
if (!vdec_stream_based(vdec))
return;
if (vdec->input.target == VDEC_INPUT_TARGET_VLD) {
if (is_support_no_parser()) {
if (!vdec->master) {
WRITE_VREG(VLD_MEM_VIFIFO_WP,
STBUF_READ(&vdec->vbuf, get_wp));
} else {
STBUF_WRITE(&vdec->vbuf, set_wp,
STBUF_READ(&vdec->master->vbuf, get_wp));
}
} else {
WRITE_VREG(VLD_MEM_VIFIFO_WP,
STBUF_READ(&vdec->vbuf, get_wp));
}
} else if (vdec->input.target == VDEC_INPUT_TARGET_HEVC) {
if (is_support_no_parser()) {
if (!vdec->master) {
WRITE_VREG(HEVC_STREAM_WR_PTR,
STBUF_READ(&vdec->vbuf, get_wp));
} else {
STBUF_WRITE(&vdec->vbuf, set_wp,
STBUF_READ(&vdec->master->vbuf, get_wp));
}
} else {
WRITE_VREG(HEVC_STREAM_WR_PTR,
STBUF_READ(&vdec->vbuf, get_wp));
}
}
}
void vdec_stream_skip_data(struct vdec_s *vdec, int skip_size)
{
u32 rp_set;
struct vdec_input_s *input = &vdec->input;
u32 rp = 0, wp = 0, level;
rp = STBUF_READ(&vdec->vbuf, get_rp);
wp = STBUF_READ(&vdec->vbuf, get_wp);
if (wp > rp)
level = wp - rp;
else
level = wp + vdec->input.size - rp ;
if (level <= skip_size) {
pr_err("skip size is error, buffer level = 0x%x, skip size = 0x%x\n", level, skip_size);
return;
}
if (wp >= rp) {
rp_set = rp + skip_size;
}
else if ((rp + skip_size) < (input->start + input->size)) {
rp_set = rp + skip_size;
} else {
rp_set = rp + skip_size - input->size;
input->stream_cookie++;
}
if (vdec->format == VFORMAT_H264)
SET_VREG_MASK(POWER_CTL_VLD,
(1 << 9));
WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0);
/* restore read side */
WRITE_VREG(VLD_MEM_SWAP_ADDR,
input->swap_page_phys);
WRITE_VREG(VLD_MEM_SWAP_CTL, 1);
while (READ_VREG(VLD_MEM_SWAP_CTL) & (1<<7))
;
WRITE_VREG(VLD_MEM_SWAP_CTL, 0);
WRITE_VREG(VLD_MEM_VIFIFO_CURR_PTR,
rp_set);
WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 1);
WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0);
STBUF_WRITE(&vdec->vbuf, set_rp,
rp_set);
WRITE_VREG(VLD_MEM_SWAP_ADDR,
input->swap_page_phys);
WRITE_VREG(VLD_MEM_SWAP_CTL, 3);
while (READ_VREG(VLD_MEM_SWAP_CTL) & (1<<7))
;
WRITE_VREG(VLD_MEM_SWAP_CTL, 0);
}
EXPORT_SYMBOL(vdec_stream_skip_data);
/*
*get next frame from input chain
*/
/*
*THE VLD_FIFO is 512 bytes and Video buffer level
* empty interrupt is set to 0x80 bytes threshold
*/
#define VLD_PADDING_SIZE 1024
#define HEVC_PADDING_SIZE (1024*16)
int vdec_prepare_input(struct vdec_s *vdec, struct vframe_chunk_s **p)
{
struct vdec_input_s *input = &vdec->input;
struct vframe_chunk_s *chunk = NULL;
struct vframe_block_list_s *block = NULL;
int dummy;
/* full reset to HW input */
if (input->target == VDEC_INPUT_TARGET_VLD) {
WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0);
/* reset VLD fifo for all vdec */
WRITE_VREG(DOS_SW_RESET0, (1<<5) | (1<<4) | (1<<3));
WRITE_VREG(DOS_SW_RESET0, 0);
if (get_cpu_major_id() < AM_MESON_CPU_MAJOR_ID_SC2)
dummy = READ_RESET_REG(RESET0_REGISTER);
WRITE_VREG(POWER_CTL_VLD, 1 << 4);
} else if (input->target == VDEC_INPUT_TARGET_HEVC) {
#if 0
/*move to driver*/
if (input_frame_based(input))
WRITE_VREG(HEVC_STREAM_CONTROL, 0);
/*
* 2: assist
* 3: parser
* 4: parser_state
* 8: dblk
* 11:mcpu
* 12:ccpu
* 13:ddr
* 14:iqit
* 15:ipp
* 17:qdct
* 18:mpred
* 19:sao
* 24:hevc_afifo
*/
WRITE_VREG(DOS_SW_RESET3,
(1<<3)|(1<<4)|(1<<8)|(1<<11)|(1<<12)|(1<<14)|(1<<15)|
(1<<17)|(1<<18)|(1<<19));
WRITE_VREG(DOS_SW_RESET3, 0);
#endif
}
/*
*setup HW decoder input buffer (VLD context)
* based on input->type and input->target
*/
if (input_frame_based(input)) {
chunk = vdec_input_next_chunk(&vdec->input);
if (chunk == NULL) {
*p = NULL;
return -1;
}
block = chunk->block;
if (input->target == VDEC_INPUT_TARGET_VLD) {
WRITE_VREG(VLD_MEM_VIFIFO_START_PTR, block->start);
WRITE_VREG(VLD_MEM_VIFIFO_END_PTR, block->start +
block->size - 8);
WRITE_VREG(VLD_MEM_VIFIFO_CURR_PTR,
round_down(block->start + chunk->offset,
VDEC_FIFO_ALIGN));
WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 1);
WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0);
/* set to manual mode */
WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, 2);
WRITE_VREG(VLD_MEM_VIFIFO_RP,
round_down(block->start + chunk->offset,
VDEC_FIFO_ALIGN));
dummy = chunk->offset + chunk->size +
VLD_PADDING_SIZE;
if (dummy >= block->size)
dummy -= block->size;
WRITE_VREG(VLD_MEM_VIFIFO_WP,
round_down(block->start + dummy,
VDEC_FIFO_ALIGN));
WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, 3);
WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, 2);
WRITE_VREG(VLD_MEM_VIFIFO_CONTROL,
(0x11 << 16) | (1<<10) | (7<<3));
} else if (input->target == VDEC_INPUT_TARGET_HEVC) {
WRITE_VREG(HEVC_STREAM_START_ADDR, block->start);
WRITE_VREG(HEVC_STREAM_END_ADDR, block->start +
block->size);
WRITE_VREG(HEVC_STREAM_RD_PTR, block->start +
chunk->offset);
dummy = chunk->offset + chunk->size +
HEVC_PADDING_SIZE;
if (dummy >= block->size)
dummy -= block->size;
WRITE_VREG(HEVC_STREAM_WR_PTR,
round_down(block->start + dummy,
VDEC_FIFO_ALIGN));
/* set endian */
SET_VREG_MASK(HEVC_STREAM_CONTROL, 7 << 4);
}
*p = chunk;
return chunk->size;
} else {
/* stream based */
u32 rp = 0, wp = 0, fifo_len = 0, first_set_rp = 0;
int size;
bool swap_valid = input->swap_valid;
unsigned long swap_page_phys = input->swap_page_phys;
if (vdec_dual(vdec) &&
((vdec->flag & VDEC_FLAG_SELF_INPUT_CONTEXT) == 0)) {
/* keep using previous input context */
struct vdec_s *master = (vdec->slave) ?
vdec : vdec->master;
if (master->input.last_swap_slave) {
swap_valid = master->slave->input.swap_valid;
swap_page_phys =
master->slave->input.swap_page_phys;
} else {
swap_valid = master->input.swap_valid;
swap_page_phys = master->input.swap_page_phys;
}
}
if (swap_valid) {
if (input->target == VDEC_INPUT_TARGET_VLD) {
if (vdec->format == VFORMAT_H264)
SET_VREG_MASK(POWER_CTL_VLD,
(1 << 9));
WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0);
/* restore read side */
WRITE_VREG(VLD_MEM_SWAP_ADDR,
swap_page_phys);
WRITE_VREG(VLD_MEM_SWAP_CTL, 1);
while (READ_VREG(VLD_MEM_SWAP_CTL) & (1<<7))
;
WRITE_VREG(VLD_MEM_SWAP_CTL, 0);
#ifdef VDEC_FCC_SUPPORT
vdec_fcc_jump_back(vdec);
#endif
/* restore wrap count */
WRITE_VREG(VLD_MEM_VIFIFO_WRAP_COUNT,
input->stream_cookie);
rp = READ_VREG(VLD_MEM_VIFIFO_RP);
fifo_len = READ_VREG(VLD_MEM_VIFIFO_LEVEL);
/* enable */
WRITE_VREG(VLD_MEM_VIFIFO_CONTROL,
(0x11 << 16) | (1<<10));
if (vdec->vbuf.no_parser)
SET_VREG_MASK(VLD_MEM_VIFIFO_CONTROL,
7 << 3);
/* sync with front end */
vdec_sync_input_read(vdec);
vdec_sync_input_write(vdec);
wp = READ_VREG(VLD_MEM_VIFIFO_WP);
} else if (input->target == VDEC_INPUT_TARGET_HEVC) {
SET_VREG_MASK(HEVC_STREAM_CONTROL, 1);
/* restore read side */
WRITE_VREG(HEVC_STREAM_SWAP_ADDR,
swap_page_phys);
WRITE_VREG(HEVC_STREAM_SWAP_CTRL, 1);
while (READ_VREG(HEVC_STREAM_SWAP_CTRL)
& (1<<7))
;
WRITE_VREG(HEVC_STREAM_SWAP_CTRL, 0);
#ifdef VDEC_FCC_SUPPORT
vdec_fcc_jump_back(vdec);
#endif
/* restore stream offset */
WRITE_VREG(HEVC_SHIFT_BYTE_COUNT,
input->stream_cookie);
rp = READ_VREG(HEVC_STREAM_RD_PTR);
fifo_len = (READ_VREG(HEVC_STREAM_FIFO_CTL)
>> 16) & 0x7f;
/* enable */
/* sync with front end */
vdec_sync_input_read(vdec);
vdec_sync_input_write(vdec);
wp = READ_VREG(HEVC_STREAM_WR_PTR);
if (vdec->vbuf.no_parser)
SET_VREG_MASK(HEVC_STREAM_CONTROL,
7 << 4);
/*pr_info("vdec: restore context\r\n");*/
}
} else {
if (vdec->vbuf.ext_buf_addr)
first_set_rp = STBUF_READ(&vdec->vbuf, get_rp);
else {
if (vdec->discard_start_data_flag)
first_set_rp = STBUF_READ(&vdec->vbuf, get_rp);
else
first_set_rp = input->start;
}
if (input->target == VDEC_INPUT_TARGET_VLD) {
WRITE_VREG(VLD_MEM_VIFIFO_START_PTR,
input->start);
WRITE_VREG(VLD_MEM_VIFIFO_END_PTR,
input->start + input->size - 8);
WRITE_VREG(VLD_MEM_VIFIFO_CURR_PTR,
first_set_rp);
WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 1);
WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0);
/* set to manual mode */
WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, 2);
WRITE_VREG(VLD_MEM_VIFIFO_RP, first_set_rp);
WRITE_VREG(VLD_MEM_VIFIFO_WP,
STBUF_READ(&vdec->vbuf, get_wp));
rp = READ_VREG(VLD_MEM_VIFIFO_RP);
/* enable */
WRITE_VREG(VLD_MEM_VIFIFO_CONTROL,
(0x11 << 16) | (1<<10));
if (vdec->vbuf.no_parser)
SET_VREG_MASK(VLD_MEM_VIFIFO_CONTROL,
7 << 3);
wp = READ_VREG(VLD_MEM_VIFIFO_WP);
} else if (input->target == VDEC_INPUT_TARGET_HEVC) {
WRITE_VREG(HEVC_STREAM_START_ADDR,
input->start);
WRITE_VREG(HEVC_STREAM_END_ADDR,
input->start + input->size);
WRITE_VREG(HEVC_STREAM_RD_PTR,
first_set_rp);
WRITE_VREG(HEVC_STREAM_WR_PTR,
STBUF_READ(&vdec->vbuf, get_wp));
rp = READ_VREG(HEVC_STREAM_RD_PTR);
wp = READ_VREG(HEVC_STREAM_WR_PTR);
fifo_len = (READ_VREG(HEVC_STREAM_FIFO_CTL)
>> 16) & 0x7f;
if (vdec->vbuf.no_parser)
SET_VREG_MASK(HEVC_STREAM_CONTROL,
7 << 4);
/* enable */
}
}
*p = NULL;
if (wp >= rp)
size = wp - rp + fifo_len;
else
size = wp + input->size - rp + fifo_len;
if (size < 0) {
pr_info("%s error: input->size %x wp %x rp %x fifo_len %x => size %x\r\n",
__func__, input->size, wp, rp, fifo_len, size);
size = 0;
}
return size;
}
}
EXPORT_SYMBOL(vdec_prepare_input);
void vdec_enable_input(struct vdec_s *vdec)
{
struct vdec_input_s *input = &vdec->input;
if (vdec->status != VDEC_STATUS_ACTIVE)
return;
if (input->target == VDEC_INPUT_TARGET_VLD)
SET_VREG_MASK(VLD_MEM_VIFIFO_CONTROL, (1<<2) | (1<<1));
else if (input->target == VDEC_INPUT_TARGET_HEVC) {
SET_VREG_MASK(HEVC_STREAM_CONTROL, 1);
if (vdec_stream_based(vdec)) {
if (vdec->vbuf.no_parser)
/*set endian for non-parser mode. */
SET_VREG_MASK(HEVC_STREAM_CONTROL, 7 << 4);
else
CLEAR_VREG_MASK(HEVC_STREAM_CONTROL, 7 << 4);
} else
SET_VREG_MASK(HEVC_STREAM_CONTROL, 7 << 4);
SET_VREG_MASK(HEVC_STREAM_FIFO_CTL, (1<<29));
}
}
EXPORT_SYMBOL(vdec_enable_input);
int vdec_set_input_buffer(struct vdec_s *vdec, u32 start, u32 size)
{
int r = vdec_input_set_buffer(&vdec->input, start, size);
if (r)
return r;
if (vdec->slave)
r = vdec_input_set_buffer(&vdec->slave->input, start, size);
return r;
}
EXPORT_SYMBOL(vdec_set_input_buffer);
/*
* vdec_eos returns the possibility that there are
* more input can be used by decoder through vdec_prepare_input
* Note: this function should be called prior to vdec_vframe_dirty
* by decoder driver to determine if EOS happens for stream based
* decoding when there is no sufficient data for a frame
*/
bool vdec_has_more_input(struct vdec_s *vdec)
{
struct vdec_input_s *input = &vdec->input;
if (!input->eos)
return true;
if (input_frame_based(input))
return vdec_input_next_input_chunk(input) != NULL;
else {
if (input->target == VDEC_INPUT_TARGET_VLD)
return READ_VREG(VLD_MEM_VIFIFO_WP) !=
STBUF_READ(&vdec->vbuf, get_wp);
else {
return (READ_VREG(HEVC_STREAM_WR_PTR) & ~0x3) !=
(STBUF_READ(&vdec->vbuf, get_wp) & ~0x3);
}
}
}
EXPORT_SYMBOL(vdec_has_more_input);
void vdec_set_prepare_level(struct vdec_s *vdec, int level)
{
vdec->input.prepare_level = level;
}
EXPORT_SYMBOL(vdec_set_prepare_level);
void vdec_set_flag(struct vdec_s *vdec, u32 flag)
{
vdec->flag = flag;
}
EXPORT_SYMBOL(vdec_set_flag);
void vdec_set_eos(struct vdec_s *vdec, bool eos)
{
struct vdec_core_s *core = vdec_core;
vdec->input.eos = eos;
if (vdec->slave)
vdec->slave->input.eos = eos;
up(&core->sem);
}
EXPORT_SYMBOL(vdec_set_eos);
#ifdef VDEC_DEBUG_SUPPORT
void vdec_set_step_mode(void)
{
step_mode = 0x1ff;
}
EXPORT_SYMBOL(vdec_set_step_mode);
#endif
void vdec_set_next_sched(struct vdec_s *vdec, struct vdec_s *next_vdec)
{
if (vdec && next_vdec) {
vdec->sched = 0;
next_vdec->sched = 1;
}
}
EXPORT_SYMBOL(vdec_set_next_sched);
/*
* Swap Context: S0 S1 S2 S3 S4
* Sample sequence: M S M M S
* Master Context: S0 S0 S2 S3 S3
* Slave context: NA S1 S1 S2 S4
* ^
* ^
* ^
* the tricky part
* If there are back to back decoding of master or slave
* then the context of the counter part should be updated
* with current decoder. In this example, S1 should be
* updated to S2.
* This is done by swap the swap_page and related info
* between two layers.
*/
static void vdec_borrow_input_context(struct vdec_s *vdec)
{
struct page *swap_page;
unsigned long swap_page_phys;
struct vdec_input_s *me;
struct vdec_input_s *other;
if (!vdec_dual(vdec))
return;
me = &vdec->input;
other = &vdec_get_associate(vdec)->input;
/* swap the swap_context, borrow counter part's
* swap context storage and update all related info.
* After vdec_vframe_dirty, vdec_save_input_context
* will be called to update current vdec's
* swap context
*/
swap_page = other->swap_page;
other->swap_page = me->swap_page;
me->swap_page = swap_page;
swap_page_phys = other->swap_page_phys;
other->swap_page_phys = me->swap_page_phys;
me->swap_page_phys = swap_page_phys;
other->swap_rp = me->swap_rp;
other->streaming_rp = me->streaming_rp;
other->stream_cookie = me->stream_cookie;
other->swap_valid = me->swap_valid;
}
void vdec_vframe_dirty(struct vdec_s *vdec, struct vframe_chunk_s *chunk)
{
if (chunk)
chunk->flag |= VFRAME_CHUNK_FLAG_CONSUMED;
if (vdec_stream_based(vdec)) {
vdec->input.swap_needed = true;
if (vdec_dual(vdec)) {
vdec_get_associate(vdec)->input.dirty_count = 0;
vdec->input.dirty_count++;
if (vdec->input.dirty_count > 1) {
vdec->input.dirty_count = 1;
vdec_borrow_input_context(vdec);
}
}
/* for stream based mode, we update read and write pointer
* also in case decoder wants to keep working on decoding
* for more frames while input front end has more data
*/
vdec_sync_input_read(vdec);
vdec_sync_input_write(vdec);
vdec->need_more_data |= VDEC_NEED_MORE_DATA_DIRTY;
vdec->need_more_data &= ~VDEC_NEED_MORE_DATA;
}
}
EXPORT_SYMBOL(vdec_vframe_dirty);
bool vdec_need_more_data(struct vdec_s *vdec)
{
if (vdec_stream_based(vdec))
return vdec->need_more_data & VDEC_NEED_MORE_DATA;
return false;
}
EXPORT_SYMBOL(vdec_need_more_data);
void hevc_wait_ddr(void)
{
unsigned long flags;
unsigned int mask = 0;
mask = 1 << 4; /* hevc */
if (get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_G12A)
mask |= (1 << 8); /* hevcb */
spin_lock_irqsave(&vdec_spin_lock, flags);
codec_dmcbus_write(DMC_REQ_CTRL,
codec_dmcbus_read(DMC_REQ_CTRL) & ~mask);
spin_unlock_irqrestore(&vdec_spin_lock, flags);
if (is_cpu_tm2_revb() ||
(get_cpu_major_id() >= AM_MESON_CPU_MAJOR_ID_SC2)) {
while (!(codec_dmcbus_read(TM2_REVB_DMC_CHAN_STS)
& mask))
;
} else {
while (!(codec_dmcbus_read(DMC_CHAN_STS)
& mask))
;
}
}
void vdec_save_input_context(struct vdec_s *vdec)
{
struct vdec_input_s *input = &vdec->input;
#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
vdec_profile(vdec, VDEC_PROFILE_EVENT_SAVE_INPUT);
#endif
if (input->target == VDEC_INPUT_TARGET_VLD)
WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 1<<15);
if (input_stream_based(input) && (input->swap_needed)) {
if (input->target == VDEC_INPUT_TARGET_VLD) {
WRITE_VREG(VLD_MEM_SWAP_ADDR,
input->swap_page_phys);
WRITE_VREG(VLD_MEM_SWAP_CTL, 3);
while (READ_VREG(VLD_MEM_SWAP_CTL) & (1<<7))
;
WRITE_VREG(VLD_MEM_SWAP_CTL, 0);
vdec->input.stream_cookie =
READ_VREG(VLD_MEM_VIFIFO_WRAP_COUNT);
vdec->input.swap_rp =
READ_VREG(VLD_MEM_VIFIFO_RP);
vdec->input.total_rd_count =
(u64)vdec->input.stream_cookie *
vdec->input.size + vdec->input.swap_rp -
READ_VREG(VLD_MEM_VIFIFO_BYTES_AVAIL);
} else if (input->target == VDEC_INPUT_TARGET_HEVC) {
WRITE_VREG(HEVC_STREAM_SWAP_ADDR,
input->swap_page_phys);
WRITE_VREG(HEVC_STREAM_SWAP_CTRL, 3);
while (READ_VREG(HEVC_STREAM_SWAP_CTRL) & (1<<7))
;
WRITE_VREG(HEVC_STREAM_SWAP_CTRL, 0);
vdec->input.stream_cookie =
READ_VREG(HEVC_SHIFT_BYTE_COUNT);
vdec->input.swap_rp =
READ_VREG(HEVC_STREAM_RD_PTR);
if (((vdec->input.stream_cookie & 0x80000000) == 0) &&
(vdec->input.streaming_rp & 0x80000000))
vdec->input.streaming_rp += 1ULL << 32;
vdec->input.streaming_rp &= 0xffffffffULL << 32;
vdec->input.streaming_rp |= vdec->input.stream_cookie;
vdec->input.total_rd_count = vdec->input.streaming_rp;
hevc_wait_ddr();
}
input->swap_valid = true;
input->swap_needed = false;
/*pr_info("vdec: save context\r\n");*/
vdec_sync_input_read(vdec);
if (vdec_dual(vdec)) {
struct vdec_s *master = (vdec->slave) ?
vdec : vdec->master;
master->input.last_swap_slave = (master->slave == vdec);
/* pr_info("master->input.last_swap_slave = %d\n",
master->input.last_swap_slave); */
}
}
}
EXPORT_SYMBOL(vdec_save_input_context);
void vdec_clean_input(struct vdec_s *vdec)
{
struct vdec_input_s *input = &vdec->input;
while (!list_empty(&input->vframe_chunk_list)) {
struct vframe_chunk_s *chunk =
vdec_input_next_chunk(input);
if (chunk && (chunk->flag & VFRAME_CHUNK_FLAG_CONSUMED))
vdec_input_release_chunk(input, chunk);
else
break;
}
vdec_save_input_context(vdec);
}
EXPORT_SYMBOL(vdec_clean_input);
static int vdec_input_read_restore(struct vdec_s *vdec)
{
struct vdec_input_s *input = &vdec->input;
if (!vdec_stream_based(vdec))
return 0;
if (!input->swap_valid) {
if (input->target == VDEC_INPUT_TARGET_VLD) {
WRITE_VREG(VLD_MEM_VIFIFO_START_PTR,
input->start);
WRITE_VREG(VLD_MEM_VIFIFO_END_PTR,
input->start + input->size - 8);
WRITE_VREG(VLD_MEM_VIFIFO_CURR_PTR,
input->start);
WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 1);
WRITE_VREG(VLD_MEM_VIFIFO_CONTROL, 0);
/* set to manual mode */
WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, 2);
WRITE_VREG(VLD_MEM_VIFIFO_RP, input->start);
} else if (input->target == VDEC_INPUT_TARGET_HEVC) {
WRITE_VREG(HEVC_STREAM_START_ADDR,
input->start);
WRITE_VREG(HEVC_STREAM_END_ADDR,
input->start + input->size);
WRITE_VREG(HEVC_STREAM_RD_PTR,
input->start);
}
return 0;
}
if (input->target == VDEC_INPUT_TARGET_VLD) {
/* restore read side */
WRITE_VREG(VLD_MEM_SWAP_ADDR,
input->swap_page_phys);
/*swap active*/
WRITE_VREG(VLD_MEM_SWAP_CTL, 1);
/*wait swap busy*/
while (READ_VREG(VLD_MEM_SWAP_CTL) & (1<<7))
;
WRITE_VREG(VLD_MEM_SWAP_CTL, 0);
} else if (input->target == VDEC_INPUT_TARGET_HEVC) {
/* restore read side */
WRITE_VREG(HEVC_STREAM_SWAP_ADDR,
input->swap_page_phys);
WRITE_VREG(HEVC_STREAM_SWAP_CTRL, 1);
while (READ_VREG(HEVC_STREAM_SWAP_CTRL)
& (1<<7))
;
WRITE_VREG(HEVC_STREAM_SWAP_CTRL, 0);
}
return 0;
}
int vdec_sync_input(struct vdec_s *vdec)
{
struct vdec_input_s *input = &vdec->input;
u32 rp = 0, wp = 0, fifo_len = 0;
int size;
vdec_input_read_restore(vdec);
vdec_sync_input_read(vdec);
vdec_sync_input_write(vdec);
if (input->target == VDEC_INPUT_TARGET_VLD) {
rp = READ_VREG(VLD_MEM_VIFIFO_RP);
wp = READ_VREG(VLD_MEM_VIFIFO_WP);
} else if (input->target == VDEC_INPUT_TARGET_HEVC) {
rp = READ_VREG(HEVC_STREAM_RD_PTR);
wp = READ_VREG(HEVC_STREAM_WR_PTR);
fifo_len = (READ_VREG(HEVC_STREAM_FIFO_CTL)
>> 16) & 0x7f;
}
if (wp >= rp)
size = wp - rp + fifo_len;
else
size = wp + input->size - rp + fifo_len;
if (size < 0) {
pr_info("%s error: input->size %x wp %x rp %x fifo_len %x => size %x\r\n",
__func__, input->size, wp, rp, fifo_len, size);
size = 0;
}
return size;
}
EXPORT_SYMBOL(vdec_sync_input);
const char *vdec_status_str(struct vdec_s *vdec)
{
if (vdec->status < 0)
return "INVALID";
return vdec->status < ARRAY_SIZE(vdec_status_string) ?
vdec_status_string[vdec->status] : "INVALID";
}
const char *vdec_type_str(struct vdec_s *vdec)
{
switch (vdec->type) {
case VDEC_TYPE_SINGLE:
return "VDEC_TYPE_SINGLE";
case VDEC_TYPE_STREAM_PARSER:
return "VDEC_TYPE_STREAM_PARSER";
case VDEC_TYPE_FRAME_BLOCK:
return "VDEC_TYPE_FRAME_BLOCK";
case VDEC_TYPE_FRAME_CIRCULAR:
return "VDEC_TYPE_FRAME_CIRCULAR";
default:
return "VDEC_TYPE_INVALID";
}
}
const char *vdec_device_name_str(struct vdec_s *vdec)
{
return vdec_device_name[vdec->format * 2 + 1];
}
EXPORT_SYMBOL(vdec_device_name_str);
void walk_vdec_core_list(char *s)
{
struct vdec_s *vdec;
struct vdec_core_s *core = vdec_core;
unsigned long flags;
pr_info("%s --->\n", s);
flags = vdec_core_lock(vdec_core);
if (list_empty(&core->connected_vdec_list)) {
pr_info("connected vdec list empty\n");
} else {
list_for_each_entry(vdec, &core->connected_vdec_list, list) {
pr_info("\tvdec (%p), status = %s\n", vdec,
vdec_status_str(vdec));
}
}
vdec_core_unlock(vdec_core, flags);
}
EXPORT_SYMBOL(walk_vdec_core_list);
/* insert vdec to vdec_core for scheduling,
* for dual running decoders, connect/disconnect always runs in pairs
*/
int vdec_connect(struct vdec_s *vdec)
{
unsigned long flags;
//trace_vdec_connect(vdec);/*DEBUG_TMP*/
if (vdec->status != VDEC_STATUS_DISCONNECTED)
return 0;
vdec_set_status(vdec, VDEC_STATUS_CONNECTED);
vdec_set_next_status(vdec, VDEC_STATUS_CONNECTED);
init_completion(&vdec->inactive_done);
if (vdec->slave) {
vdec_set_status(vdec->slave, VDEC_STATUS_CONNECTED);
vdec_set_next_status(vdec->slave, VDEC_STATUS_CONNECTED);
init_completion(&vdec->slave->inactive_done);
}
flags = vdec_core_lock(vdec_core);
list_add_tail(&vdec->list, &vdec_core->connected_vdec_list);
if (vdec->slave) {
list_add_tail(&vdec->slave->list,
&vdec_core->connected_vdec_list);
}
vdec_core_unlock(vdec_core, flags);
up(&vdec_core->sem);
return 0;
}
EXPORT_SYMBOL(vdec_connect);
/* remove vdec from vdec_core scheduling */
int vdec_disconnect(struct vdec_s *vdec)
{
#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
vdec_profile(vdec, VDEC_PROFILE_EVENT_DISCONNECT);
#endif
//trace_vdec_disconnect(vdec);/*DEBUG_TMP*/
if ((vdec->status != VDEC_STATUS_CONNECTED) &&
(vdec->status != VDEC_STATUS_ACTIVE)) {
return 0;
}
mutex_lock(&vdec_mutex);
/*
*when a vdec is under the management of scheduler
* the status change will only be from vdec_core_thread
*/
vdec_set_next_status(vdec, VDEC_STATUS_DISCONNECTED);
if (vdec->slave)
vdec_set_next_status(vdec->slave, VDEC_STATUS_DISCONNECTED);
else if (vdec->master)
vdec_set_next_status(vdec->master, VDEC_STATUS_DISCONNECTED);
mutex_unlock(&vdec_mutex);
up(&vdec_core->sem);
if(!wait_for_completion_timeout(&vdec->inactive_done,
msecs_to_jiffies(2000)))
goto discon_timeout;
if (vdec->slave) {
if(!wait_for_completion_timeout(&vdec->slave->inactive_done,
msecs_to_jiffies(2000)))
goto discon_timeout;
} else if (vdec->master) {
if(!wait_for_completion_timeout(&vdec->master->inactive_done,
msecs_to_jiffies(2000)))
goto discon_timeout;
}
return 0;
discon_timeout:
pr_err("%s timeout!!! status: 0x%x force it to 2\n", __func__, vdec->status);
vdec_set_status(vdec, VDEC_STATUS_CONNECTED);
return 0;
}
EXPORT_SYMBOL(vdec_disconnect);
/* release vdec structure */
int vdec_destroy(struct vdec_s *vdec)
{
//trace_vdec_destroy(vdec);/*DEBUG_TMP*/
vdec_input_release(&vdec->input);
#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
vdec_profile_flush(vdec);
#endif
ida_simple_remove(&vdec_core->ida, vdec->id);
if (vdec->mvfrm)
vfree(vdec->mvfrm);
vfree(vdec);
#ifdef CONFIG_AMLOGIC_V4L_VIDEO3
v4lvideo_dec_count_decrease();
#endif
atomic_dec(&vdec_core->vdec_nr);
return 0;
}
EXPORT_SYMBOL(vdec_destroy);
static bool is_tunnel_pipeline(u32 pl)
{
return ((pl & BIT(FRAME_BASE_PATH_DTV_TUNNEL_MODE)) ||
(pl & BIT(FRAME_BASE_PATH_AMLVIDEO_AMVIDEO))) ?
true : false;
}
static bool is_nontunnel_pipeline(u32 pl)
{
return (pl & BIT(FRAME_BASE_PATH_DI_V4LVIDEO)) ? true : false;
}
static bool is_v4lvideo_already_used(u32 pre, int vf_receiver_inst)
{
if (vf_receiver_inst == 0) {
if (pre & BIT(FRAME_BASE_PATH_DI_V4LVIDEO_0)) {
return true;
}
} else if (vf_receiver_inst == 1) {
if (pre & BIT(FRAME_BASE_PATH_DI_V4LVIDEO_1)) {
return true;
}
} else if (vf_receiver_inst == 2) {
if (pre & BIT(FRAME_BASE_PATH_DI_V4LVIDEO_2)) {
return true;
}
}
return false;
}
static bool is_res_locked(u32 pre, u32 cur, int vf_receiver_inst)
{
if (is_tunnel_pipeline(pre)) {
if (is_tunnel_pipeline(cur)) {
return true;
}
} else if (is_nontunnel_pipeline(cur)) {
if (is_v4lvideo_already_used(pre,vf_receiver_inst)) {
return true;
}
}
return false;
}
int vdec_resource_checking(struct vdec_s *vdec)
{
/*
* If it is the single instance that the pipeline of DTV used,
* then have to check that the resources which is belong tunnel
* pipeline these are being released.
*/
ulong expires = jiffies + msecs_to_jiffies(2000);
while (is_res_locked(vdec_core->vdec_resouce_status,
BIT(vdec->frame_base_video_path),
vdec->vf_receiver_inst)) {
if (time_after(jiffies, expires)) {
pr_err("wait vdec resource timeout.\n");
return -EBUSY;
}
schedule();
}
return 0;
}
EXPORT_SYMBOL(vdec_resource_checking);
/*
*register vdec_device
* create output, vfm or create ionvideo output
*/
s32 vdec_init(struct vdec_s *vdec, int is_4k)
{
int r = 0;
struct vdec_s *p = vdec;
const char *dev_name;
int id = PLATFORM_DEVID_AUTO;/*if have used my self*/
int max_di_count = max_di_instance;
if (vdec_stream_based(vdec))
max_di_count = max_supported_di_instance;
if (is_res_locked(vdec_core->vdec_resouce_status,
BIT(vdec->frame_base_video_path),
vdec->vf_receiver_inst))
return -EBUSY;
//pr_err("%s [pid=%d,tgid=%d]\n", __func__, current->pid, current->tgid);
dev_name = get_dev_name(vdec_single(vdec), vdec->format);
if (dev_name == NULL)
return -ENODEV;
pr_info("vdec_init, dev_name:%s, vdec_type=%s id = %d\n",
dev_name, vdec_type_str(vdec), vdec->id);
snprintf(vdec->name, sizeof(vdec->name),
"vdec-%d", vdec->id);
snprintf(vdec->dec_spend_time, sizeof(vdec->dec_spend_time),
"%s-dec_spend_time", vdec->name);
snprintf(vdec->dec_spend_time_ave, sizeof(vdec->dec_spend_time_ave),
"%s-dec_spend_time_ave", vdec->name);
/*
*todo: VFM patch control should be configurable,
* for now all stream based input uses default VFM path.
*/
if (!is_support_no_parser()) {
if (vdec_stream_based(vdec) && !vdec_dual(vdec)) {
if (vdec_core->vfm_vdec == NULL) {
pr_debug("vdec_init set vfm decoder %p\n", vdec);
vdec_core->vfm_vdec = vdec;
} else {
pr_info("vdec_init vfm path busy.\n");
return -EBUSY;
}
}
}
mutex_lock(&vdec_mutex);
inited_vcodec_num++;
mutex_unlock(&vdec_mutex);
vdec_input_set_type(&vdec->input, vdec->type,
(vdec->format == VFORMAT_HEVC ||
vdec->format == VFORMAT_AVS2 ||
vdec->format == VFORMAT_VP9 ||
vdec->format == VFORMAT_AV1
) ?
VDEC_INPUT_TARGET_HEVC :
VDEC_INPUT_TARGET_VLD);
if (vdec_single(vdec) || (vdec_get_debug_flags() & 0x2))
vdec_enable_DMC(vdec);
p->cma_dev = vdec_core->cma_dev;
p->get_canvas = get_canvas;
p->get_canvas_ex = get_canvas_ex;
p->free_canvas_ex = free_canvas_ex;
p->vdec_fps_detec = vdec_fps_detec;
/* todo */
if (!vdec_dual(vdec)) {
p->use_vfm_path =
(is_support_no_parser()) ?
vdec_single(vdec) :
vdec_stream_based(vdec);
}
if (debugflags & 0x4)
p->use_vfm_path = 1;
/* vdec_dev_reg.flag = 0; */
if (vdec->id >= 0)
id = vdec->id;
p->parallel_dec = parallel_decode;
vdec_core->parallel_dec = parallel_decode;
vdec->canvas_mode = CANVAS_BLKMODE_32X32;
#ifdef FRAME_CHECK
vdec_frame_check_init(vdec);
#endif
/* stream buffer init. */
if (vdec->vbuf.ops && !vdec->master) {
r = vdec->vbuf.ops->init(&vdec->vbuf, vdec);
if (r) {
pr_err("%s stream buffer init err (%d)\n", dev_name, r);
mutex_lock(&vdec_mutex);
inited_vcodec_num--;
mutex_unlock(&vdec_mutex);
goto error;
}
if (vdec->slave) {
memcpy(&vdec->slave->vbuf, &vdec->vbuf,
sizeof(vdec->vbuf));
}
}
p->dev = platform_device_register_data(
&vdec_core->vdec_core_platform_device->dev,
dev_name,
id,
&p, sizeof(struct vdec_s *));
if (IS_ERR(p->dev)) {
r = PTR_ERR(p->dev);
pr_err("vdec: Decoder device %s register failed (%d)\n",
dev_name, r);
mutex_lock(&vdec_mutex);
inited_vcodec_num--;
mutex_unlock(&vdec_mutex);
goto error;
} else if (!p->dev->dev.driver) {
pr_info("vdec: Decoder device %s driver probe failed.\n",
dev_name);
r = -ENODEV;
goto error;
}
if ((p->type == VDEC_TYPE_FRAME_BLOCK) && (p->run == NULL)) {
r = -ENODEV;
pr_err("vdec: Decoder device not handled (%s)\n", dev_name);
mutex_lock(&vdec_mutex);
inited_vcodec_num--;
mutex_unlock(&vdec_mutex);
goto error;
}
if (p->use_vfm_path) {
vdec->vf_receiver_inst = -1;
vdec->vfm_map_id[0] = 0;
} else if (!vdec_dual(vdec)) {
/* create IONVIDEO instance and connect decoder's
* vf_provider interface to it
*/
if (!is_support_no_parser()) {
if (p->type != VDEC_TYPE_FRAME_BLOCK) {
r = -ENODEV;
pr_err("vdec: Incorrect decoder type\n");
mutex_lock(&vdec_mutex);
inited_vcodec_num--;
mutex_unlock(&vdec_mutex);
goto error;
}
}
if (strncmp("disable", vfm_path, strlen("disable"))) {
snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE,
"%s %s", vdec->vf_provider_name, vfm_path);
snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE,
"vdec-map-%d", vdec->id);
} else if (p->frame_base_video_path == FRAME_BASE_PATH_IONVIDEO) {
#if 1
r = ionvideo_assign_map(&vdec->vf_receiver_name,
&vdec->vf_receiver_inst);
#else
/*
* temporarily just use decoder instance ID as iondriver ID
* to solve OMX iondriver instance number check time sequence
* only the limitation is we can NOT mix different video
* decoders since same ID will be used for different decoder
* formats.
*/
vdec->vf_receiver_inst = p->dev->id;
r = ionvideo_assign_map(&vdec->vf_receiver_name,
&vdec->vf_receiver_inst);
#endif
if (r < 0) {
pr_err("IonVideo frame receiver allocation failed.\n");
mutex_lock(&vdec_mutex);
inited_vcodec_num--;
mutex_unlock(&vdec_mutex);
goto error;
}
snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE,
"%s %s", vdec->vf_provider_name,
vdec->vf_receiver_name);
snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE,
"vdec-map-%d", vdec->id);
} else if (p->frame_base_video_path ==
FRAME_BASE_PATH_AMLVIDEO_AMVIDEO) {
if (vdec_secure(vdec)) {
snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE,
"%s %s", vdec->vf_provider_name,
"amlvideo amvideo");
} else {
if (debug_vdetect)
snprintf(vdec->vfm_map_chain,
VDEC_MAP_NAME_SIZE,
"%s vdetect.0 %s",
vdec->vf_provider_name,
"amlvideo ppmgr deinterlace amvideo");
else
snprintf(vdec->vfm_map_chain,
VDEC_MAP_NAME_SIZE, "%s %s",
vdec->vf_provider_name,
"amlvideo ppmgr deinterlace amvideo");
}
snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE,
"vdec-map-%d", vdec->id);
} else if (p->frame_base_video_path ==
FRAME_BASE_PATH_AMLVIDEO1_AMVIDEO2) {
snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE,
"%s %s", vdec->vf_provider_name,
"aml_video.1 videosync.0 videopip");
snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE,
"vdec-map-%d", vdec->id);
} else if (p->frame_base_video_path == FRAME_BASE_PATH_V4L_OSD) {
snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE,
"%s %s", vdec->vf_provider_name,
vdec->vf_receiver_name);
snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE,
"vdec-map-%d", vdec->id);
} else if (p->frame_base_video_path == FRAME_BASE_PATH_TUNNEL_MODE) {
snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE,
"%s %s", vdec->vf_provider_name,
"amvideo");
snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE,
"vdec-map-%d", vdec->id);
} else if (p->frame_base_video_path == FRAME_BASE_PATH_PIP_TUNNEL_MODE) {
snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE,
"%s %s", vdec->vf_provider_name,
"videosync.0 videopip");
snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE,
"vdec-map-%d", vdec->id);
} else if (p->frame_base_video_path == FRAME_BASE_PATH_V4L_VIDEO) {
snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE,
"%s %s %s", vdec->vf_provider_name,
vdec->vf_receiver_name, "amvideo");
snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE,
"vdec-map-%d", vdec->id);
} else if (p->frame_base_video_path ==
FRAME_BASE_PATH_DI_V4LVIDEO) {
#ifdef CONFIG_AMLOGIC_V4L_VIDEO3
r = v4lvideo_assign_map(&vdec->vf_receiver_name,
&vdec->vf_receiver_inst);
#else
r = -1;
#endif
if (r < 0) {
pr_err("V4lVideo frame receiver allocation failed.\n");
mutex_lock(&vdec_mutex);
inited_vcodec_num--;
mutex_unlock(&vdec_mutex);
goto error;
}
if (!v4lvideo_add_di)
snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE,
"%s %s", vdec->vf_provider_name,
vdec->vf_receiver_name);
else {
if ((vdec->vf_receiver_inst == 0)
&& (max_di_count > 0))
if (max_di_count == 1)
snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE,
"%s %s %s", vdec->vf_provider_name,
"deinterlace",
vdec->vf_receiver_name);
else
snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE,
"%s %s %s", vdec->vf_provider_name,
"dimulti.1",
vdec->vf_receiver_name);
else if ((vdec->vf_receiver_inst <
max_di_count) &&
(vdec->vf_receiver_inst == 1))
snprintf(vdec->vfm_map_chain,
VDEC_MAP_NAME_SIZE,
"%s %s %s",
vdec->vf_provider_name,
"deinterlace",
vdec->vf_receiver_name);
else if (vdec->vf_receiver_inst <
max_di_count)
snprintf(vdec->vfm_map_chain,
VDEC_MAP_NAME_SIZE,
"%s %s%d %s",
vdec->vf_provider_name,
"dimulti.",
vdec->vf_receiver_inst,
vdec->vf_receiver_name);
else
snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE,
"%s %s", vdec->vf_provider_name,
vdec->vf_receiver_name);
}
snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE,
"vdec-map-%d", vdec->id);
} else if (p->frame_base_video_path ==
FRAME_BASE_PATH_V4LVIDEO) {
#ifdef CONFIG_AMLOGIC_V4L_VIDEO3
r = v4lvideo_assign_map(&vdec->vf_receiver_name,
&vdec->vf_receiver_inst);
#else
r = -1;
#endif
if (r < 0) {
pr_err("V4lVideo frame receiver allocation failed.\n");
mutex_lock(&vdec_mutex);
inited_vcodec_num--;
mutex_unlock(&vdec_mutex);
goto error;
}
snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE,
"%s %s", vdec->vf_provider_name,
vdec->vf_receiver_name);
snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE,
"vdec-map-%d", vdec->id);
} else if (p->frame_base_video_path ==
FRAME_BASE_PATH_DTV_TUNNEL_MODE) {
snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE,
"%s deinterlace %s", vdec->vf_provider_name,
"amvideo");
snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE,
"vdec-map-%d", vdec->id);
} else if (p->frame_base_video_path ==
FRAME_BASE_PATH_AMLVIDEO_FENCE) {
snprintf(vdec->vfm_map_chain, VDEC_MAP_NAME_SIZE,
"%s %s", vdec->vf_provider_name,
"amlvideo amvideo");
snprintf(vdec->vfm_map_id, VDEC_MAP_NAME_SIZE,
"vdec-map-%d", vdec->id);
}
if (vfm_map_add(vdec->vfm_map_id,
vdec->vfm_map_chain) < 0) {
r = -ENOMEM;
pr_err("Decoder pipeline map creation failed %s.\n",
vdec->vfm_map_id);
vdec->vfm_map_id[0] = 0;
mutex_lock(&vdec_mutex);
inited_vcodec_num--;
mutex_unlock(&vdec_mutex);
goto error;
}
pr_debug("vfm map %s created\n", vdec->vfm_map_id);
/*
*assume IONVIDEO driver already have a few vframe_receiver
* registered.
* 1. Call iondriver function to allocate a IONVIDEO path and
* provide receiver's name and receiver op.
* 2. Get decoder driver's provider name from driver instance
* 3. vfm_map_add(name, "<decoder provider name>
* <iondriver receiver name>"), e.g.
* vfm_map_add("vdec_ion_map_0", "mpeg4_0 iondriver_1");
* 4. vf_reg_provider and vf_reg_receiver
* Note: the decoder provider's op uses vdec as op_arg
* the iondriver receiver's op uses iondev device as
* op_arg
*/
}
if (!vdec_single(vdec)) {
vf_reg_provider(&p->vframe_provider);
vf_notify_receiver(p->vf_provider_name,
VFRAME_EVENT_PROVIDER_START,
vdec);
if (vdec_core->hint_fr_vdec == NULL)
vdec_core->hint_fr_vdec = vdec;
if (vdec_core->hint_fr_vdec == vdec) {
if (p->sys_info->rate != 0) {
if (!vdec->is_reset) {
vf_notify_receiver(p->vf_provider_name,
VFRAME_EVENT_PROVIDER_FR_HINT,
(void *)
((unsigned long)
p->sys_info->rate));
vdec->fr_hint_state = VDEC_HINTED;
}
} else {
vdec->fr_hint_state = VDEC_NEED_HINT;
}
}
}
p->dolby_meta_with_el = 0;
pr_debug("vdec_init, vf_provider_name = %s, b %d\n",
p->vf_provider_name, is_cpu_tm2_revb());
mutex_lock(&vdec_mutex);
vdec_core->vdec_resouce_status |= BIT(p->frame_base_video_path);
if (p->frame_base_video_path == FRAME_BASE_PATH_DI_V4LVIDEO) {
if (p->vf_receiver_inst == 0) {
vdec_core->vdec_resouce_status |= BIT(FRAME_BASE_PATH_DI_V4LVIDEO_0);
} else if (p->vf_receiver_inst == 1) {
vdec_core->vdec_resouce_status |= BIT(FRAME_BASE_PATH_DI_V4LVIDEO_1);
} else if (p->vf_receiver_inst == 2) {
vdec_core->vdec_resouce_status |= BIT(FRAME_BASE_PATH_DI_V4LVIDEO_2);
}
}
mutex_unlock(&vdec_mutex);
vdec_input_prepare_bufs(/*prepared buffer for fast playing.*/
&vdec->input,
vdec->sys_info->width,
vdec->sys_info->height);
/* vdec is now ready to be active */
vdec_set_status(vdec, VDEC_STATUS_DISCONNECTED);
#ifdef VDEC_FCC_SUPPORT
init_waitqueue_head(&vdec->jump_back_wq);
mutex_init(&vdec->jump_back_mutex);
vdec->fcc_mode = FCC_BUTT;
vdec->fcc_status = STATUS_BUTT;
#endif
return 0;
error:
return r;
}
EXPORT_SYMBOL(vdec_init);
/*
*Remove the vdec after timeout happens both in vdec_disconnect
*and platform_device_unregister. Then after, we can release the vdec.
*/
static void vdec_connect_list_force_clear(struct vdec_core_s *core, struct vdec_s *v_ref)
{
struct vdec_s *vdec, *tmp;
unsigned long flags;
flags = vdec_core_lock(core);
list_for_each_entry_safe(vdec, tmp,
&core->connected_vdec_list, list) {
if ((vdec->status == VDEC_STATUS_DISCONNECTED) &&
(vdec == v_ref)) {
pr_err("%s, vdec = %p, active vdec = %p\n",
__func__, vdec, core->active_vdec);
if (v_ref->active_mask)
core->sched_mask &= ~v_ref->active_mask;
if (core->active_vdec == v_ref)
core->active_vdec = NULL;
if (core->active_hevc == v_ref)
core->active_hevc = NULL;
if (core->last_vdec == v_ref)
core->last_vdec = NULL;
list_del(&vdec->list);
}
}
vdec_core_unlock(core, flags);
}
/* vdec_create/init/release/destroy are applied to both dual running decoders
*/
void vdec_release(struct vdec_s *vdec)
{
u32 wcount = 0;
//trace_vdec_release(vdec);/*DEBUG_TMP*/
#ifdef VDEC_DEBUG_SUPPORT
if (step_mode) {
pr_info("VDEC_DEBUG: in step_mode, wait release\n");
while (step_mode)
udelay(10);
pr_info("VDEC_DEBUG: step_mode is clear\n");
}
#endif
/* When release, userspace systemctl need this duration 0 event */
vframe_rate_uevent(0);
vdec_disconnect(vdec);
if (vdec->vframe_provider.name) {
if (!vdec_single(vdec)) {
if (vdec_core->hint_fr_vdec == vdec
&& vdec->fr_hint_state == VDEC_HINTED)
vf_notify_receiver(
vdec->vf_provider_name,
VFRAME_EVENT_PROVIDER_FR_END_HINT,
NULL);
vdec->fr_hint_state = VDEC_NO_NEED_HINT;
}
vf_unreg_provider(&vdec->vframe_provider);
}
if (vdec_core->vfm_vdec == vdec)
vdec_core->vfm_vdec = NULL;
if (vdec_core->hint_fr_vdec == vdec)
vdec_core->hint_fr_vdec = NULL;
if (vdec->vf_receiver_inst >= 0) {
if (vdec->vfm_map_id[0]) {
vfm_map_remove(vdec->vfm_map_id);
vdec->vfm_map_id[0] = 0;
}
}
while (vdec->irq_cnt > vdec->irq_thread_cnt) {
if ((wcount & 0x1f) == 0)
pr_debug("%s vdec[%lx]: %lld > %lld, loop %u times\n",__func__, (unsigned long)vdec,
vdec->irq_cnt,vdec->irq_thread_cnt, wcount);
/*
* Wait at most 2000 ms.
* In suspend scenario, the system may disable thread_fn,
* thus can NOT always waiting the thread_fn happen
*/
if (++wcount > 1000)
break;
usleep_range(1000, 2000);
}
#ifdef FRAME_CHECK
vdec_frame_check_exit(vdec);
#endif
vdec_fps_clear(vdec->id);
if (atomic_read(&vdec_core->vdec_nr) == 1)
vdec_disable_DMC(vdec);
platform_device_unregister(vdec->dev);
/*Check if the vdec still in connected list, if yes, delete it*/
vdec_connect_list_force_clear(vdec_core, vdec);
if (vdec->vbuf.ops && !vdec->master)
vdec->vbuf.ops->release(&vdec->vbuf);
pr_debug("vdec_release instance %p, total %d id = %d\n", vdec,
atomic_read(&vdec_core->vdec_nr), vdec->id);
mutex_lock(&vdec_mutex);
if (vdec->frame_base_video_path == FRAME_BASE_PATH_DI_V4LVIDEO) {
if (vdec->vf_receiver_inst == 0) {
vdec_core->vdec_resouce_status &= ~BIT(FRAME_BASE_PATH_DI_V4LVIDEO_0);
} else if (vdec->vf_receiver_inst == 1) {
vdec_core->vdec_resouce_status &= ~BIT(FRAME_BASE_PATH_DI_V4LVIDEO_1);
} else if (vdec->vf_receiver_inst == 2) {
vdec_core->vdec_resouce_status &= ~BIT(FRAME_BASE_PATH_DI_V4LVIDEO_2);
}
}
vdec_core->vdec_resouce_status &= ~BIT(vdec->frame_base_video_path);
mutex_unlock(&vdec_mutex);
vdec_destroy(vdec);
mutex_lock(&vdec_mutex);
inited_vcodec_num--;
mutex_unlock(&vdec_mutex);
}
EXPORT_SYMBOL(vdec_release);
/* For dual running decoders, vdec_reset is only called with master vdec.
*/
int vdec_reset(struct vdec_s *vdec)
{
//trace_vdec_reset(vdec); /*DEBUG_TMP*/
vdec_disconnect(vdec);
if (vdec->vframe_provider.name)
vf_unreg_provider(&vdec->vframe_provider);
if ((vdec->slave) && (vdec->slave->vframe_provider.name))
vf_unreg_provider(&vdec->slave->vframe_provider);
if (vdec->reset) {
vdec->reset(vdec);
if (vdec->slave)
vdec->slave->reset(vdec->slave);
}
vdec->mc_loaded = 0;/*clear for reload firmware*/
vdec_input_release(&vdec->input);
vdec_input_init(&vdec->input, vdec);
vdec_input_prepare_bufs(&vdec->input, vdec->sys_info->width,
vdec->sys_info->height);
vf_reg_provider(&vdec->vframe_provider);
vf_notify_receiver(vdec->vf_provider_name,
VFRAME_EVENT_PROVIDER_START, vdec);
if (vdec->slave) {
vf_reg_provider(&vdec->slave->vframe_provider);
vf_notify_receiver(vdec->slave->vf_provider_name,
VFRAME_EVENT_PROVIDER_START, vdec->slave);
vdec->slave->mc_loaded = 0;/*clear for reload firmware*/
}
vdec_connect(vdec);
return 0;
}
EXPORT_SYMBOL(vdec_reset);
int vdec_v4l2_reset(struct vdec_s *vdec, int flag)
{
//trace_vdec_reset(vdec); /*DEBUG_TMP*/
pr_debug("vdec_v4l2_reset %d\n", flag);
vdec_disconnect(vdec);
if (flag != 2) {
if (vdec->vframe_provider.name)
vf_unreg_provider(&vdec->vframe_provider);
if ((vdec->slave) && (vdec->slave->vframe_provider.name))
vf_unreg_provider(&vdec->slave->vframe_provider);
if (vdec->reset) {
vdec->reset(vdec);
if (vdec->slave)
vdec->slave->reset(vdec->slave);
}
vdec->mc_loaded = 0;/*clear for reload firmware*/
vdec_input_release(&vdec->input);
vdec_input_init(&vdec->input, vdec);
vdec_input_prepare_bufs(&vdec->input, vdec->sys_info->width,
vdec->sys_info->height);
vf_reg_provider(&vdec->vframe_provider);
vf_notify_receiver(vdec->vf_provider_name,
VFRAME_EVENT_PROVIDER_START, vdec);
if (vdec->slave) {
vf_reg_provider(&vdec->slave->vframe_provider);
vf_notify_receiver(vdec->slave->vf_provider_name,
VFRAME_EVENT_PROVIDER_START, vdec->slave);
vdec->slave->mc_loaded = 0;/*clear for reload firmware*/
}
} else {
if (vdec->reset) {
vdec->reset(vdec);
if (vdec->slave)
vdec->slave->reset(vdec->slave);
}
}
vdec_connect(vdec);
vdec_frame_check_init(vdec);
return 0;
}
EXPORT_SYMBOL(vdec_v4l2_reset);
void vdec_free_cmabuf(void)
{
mutex_lock(&vdec_mutex);
/*if (inited_vcodec_num > 0) {
mutex_unlock(&vdec_mutex);
return;
}*/
mutex_unlock(&vdec_mutex);
}
void vdec_core_request(struct vdec_s *vdec, unsigned long mask)
{
vdec->core_mask |= mask;
if (vdec->slave)
vdec->slave->core_mask |= mask;
if (vdec_core->parallel_dec == 1) {
if (mask & CORE_MASK_COMBINE)
vdec_core->vdec_combine_flag++;
}
}
EXPORT_SYMBOL(vdec_core_request);
int vdec_core_release(struct vdec_s *vdec, unsigned long mask)
{
vdec->core_mask &= ~mask;
if (vdec->slave)
vdec->slave->core_mask &= ~mask;
if (vdec_core->parallel_dec == 1) {
if (mask & CORE_MASK_COMBINE)
vdec_core->vdec_combine_flag--;
}
return 0;
}
EXPORT_SYMBOL(vdec_core_release);
bool vdec_core_with_input(unsigned long mask)
{
enum vdec_type_e type;
for (type = VDEC_1; type < VDEC_MAX; type++) {
if ((mask & (1 << type)) && cores_with_input[type])
return true;
}
return false;
}
void vdec_core_finish_run(struct vdec_s *vdec, unsigned long mask)
{
unsigned long i;
unsigned long t = mask;
mutex_lock(&vdec_mutex);
while (t) {
i = __ffs(t);
clear_bit(i, &vdec->active_mask);
t &= ~(1 << i);
}
if (vdec->active_mask == 0)
vdec_set_status(vdec, VDEC_STATUS_CONNECTED);
mutex_unlock(&vdec_mutex);
}
EXPORT_SYMBOL(vdec_core_finish_run);
/*
* find what core resources are available for vdec
*/
static unsigned long vdec_schedule_mask(struct vdec_s *vdec,
unsigned long active_mask)
{
unsigned long mask = vdec->core_mask &
~CORE_MASK_COMBINE;
if (vdec->core_mask & CORE_MASK_COMBINE) {
/* combined cores must be granted together */
if ((mask & ~active_mask) == mask)
return mask;
else
return 0;
} else
return mask & ~vdec->sched_mask & ~active_mask;
}