blob: b5e06db04dd7005f18a985f7037f0ca81288f7e1 [file] [log] [blame]
/*
* drivers/amlogic/media/stream_input/parser/stream_buffer_base.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/types.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/uaccess.h>
#include <linux/atomic.h>
#include <linux/delay.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include "../../frame_provider/decoder/utils/vdec.h"
#include "amports_priv.h"
#include "stream_buffer_base.h"
#include "thread_rw.h"
#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)
static struct stream_buf_s vdec_buf_def = {
.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
};
static struct stream_buf_s hevc_buf_def = {
.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
};
static struct stream_buf_s *get_def_parms(int f)
{
switch (f) {
case VFORMAT_HEVC:
case VFORMAT_AVS2:
case VFORMAT_AV1:
case VFORMAT_VP9:
return &hevc_buf_def;
default:
return &vdec_buf_def;
}
}
int stream_buffer_base_init(struct stream_buf_s *stbuf,
struct stream_buf_ops *ops,
struct parser_args *pars)
{
struct vdec_s *vdec =
container_of(stbuf, struct vdec_s, vbuf);
struct stream_port_s *port = NULL;
u32 format, width, height;
/* sanity check. */
if (WARN_ON(!stbuf) || WARN_ON(!ops))
return -EINVAL;
port = vdec->port;
format = vdec->port->vformat;
width = vdec->sys_info->width;
height = vdec->sys_info->height;
if (!stbuf->ext_buf_addr) {
memcpy(stbuf, get_def_parms(format),
sizeof(*stbuf));
}
stbuf->id = vdec->id;
stbuf->is_hevc = ((format == VFORMAT_HEVC) ||
(format == VFORMAT_AVS2) ||
(format == VFORMAT_AV1) ||
(format == VFORMAT_VP9));
stbuf->for_4k = ((width * height) >
(1920 * 1088)) ? 1 : 0;
stbuf->is_multi_inst = !vdec_single(vdec);
memcpy(&stbuf->pars, pars, sizeof(*pars));
/* register ops func. */
stbuf->ops = ops;
return 0;
}
EXPORT_SYMBOL(stream_buffer_base_init);
void stream_buffer_set_ext_buf(struct stream_buf_s *stbuf,
ulong addr,
u32 size,
u32 flag)
{
stbuf->ext_buf_addr = addr;
stbuf->buf_size = size;
stbuf->is_secure = ((flag & STBUF_META_FLAG_SECURE) != 0);
stbuf->use_ptsserv = ((flag & STBUF_META_FLAG_PTS_SERV) != 0);
/*
pr_debug("%s, addr %lx, size 0x%x, secure %d\n", __func__,
stbuf->ext_buf_addr, stbuf->buf_size, stbuf->is_secure);
*/
}
EXPORT_SYMBOL(stream_buffer_set_ext_buf);
#ifdef VDEC_FCC_SUPPORT
void fcc_wakeup_jump_back(struct vdec_s *vdec,
struct stream_buffer_metainfo *meta)
{
u32 first_ptr;
u32 round_down_size = 0;
if (fcc_debug_enable())
pr_info("[%d][FCC]: jump_back_done = %d jump_back_flag = %d stbuf_pktaddr = 0x%x size = 0x%x\n",
vdec->id, vdec->jump_back_done, meta->jump_back_flag,
meta->stbuf_pktaddr, meta->stbuf_size);
if (vdec->fcc_mode == FCC_DEC_MODE && vdec->fcc_status != SWITCH_DONE_STATUS) {
mutex_lock(&vdec->jump_back_mutex);
if (meta->jump_back_flag && !vdec->jump_back_done) {
if (vdec->input.target == VDEC_INPUT_TARGET_HEVC)
round_down_size = 0x80;
else if (vdec->input.target == VDEC_INPUT_TARGET_VLD)
round_down_size = 0x100;
if (vdec->vbuf.ext_buf_addr > (meta->stbuf_pktaddr - round_down_size))
first_ptr = vdec->vbuf.ext_buf_addr;
else {
first_ptr = round_down(meta->stbuf_pktaddr, round_down_size);
}
vdec->jump_back_done = 1;
vdec->jump_back_rp = first_ptr;
wake_up_interruptible(&vdec->jump_back_wq);
} else if (!vdec->jump_back_done && !vdec->jump_back_error) {
vdec->jump_back_error = 1;
wake_up_interruptible(&vdec->jump_back_wq);
}
mutex_unlock(&vdec->jump_back_mutex);
}
return;
}
#endif
void stream_buffer_meta_write(struct stream_buf_s *stbuf,
struct stream_buffer_metainfo *meta)
{
u32 wp = stbuf->ops->get_wp(stbuf);
if ((stbuf->stream_offset == 0) &&
/*(wp == stbuf->ext_buf_addr) &&*/ /*For fcc, when switching to dec mode it doesn't meet*/
(meta->stbuf_pktaddr > stbuf->ext_buf_addr)) {
struct vdec_s *vdec = container_of(stbuf, struct vdec_s, vbuf);
u32 first_ptr;
u32 round_down_size = 0;
/*RP max alignment requirement*/
if (vdec->input.target == VDEC_INPUT_TARGET_HEVC)
round_down_size = 0x80;
else if (vdec->input.target == VDEC_INPUT_TARGET_VLD)
round_down_size = 0x100;
if (stbuf->ext_buf_addr > (meta->stbuf_pktaddr - round_down_size))
first_ptr = stbuf->ext_buf_addr;
else {
first_ptr = round_down(meta->stbuf_pktaddr, round_down_size);
pr_info("warn: first packet_wp(%x round_down %x) is not stbuf start addr(%lx)\n",
meta->stbuf_pktaddr, first_ptr, stbuf->ext_buf_addr);
}
stbuf->ops->set_wp(stbuf, first_ptr);
stbuf->ops->set_rp(stbuf, first_ptr);
vdec->input.swap_rp = first_ptr;
if (vdec->slave)
vdec->slave->input.swap_rp = first_ptr;
if (vdec->input.target != VDEC_INPUT_TARGET_HEVC)
stbuf->stream_offset += (meta->stbuf_pktaddr - stbuf->ext_buf_addr);
else
stbuf->stream_offset += (meta->stbuf_pktaddr - first_ptr);
}
if (meta->stbuf_pktaddr + meta->stbuf_pktsize < stbuf->buf_start + stbuf->buf_size)
wp = meta->stbuf_pktaddr + meta->stbuf_pktsize;
else
wp = meta->stbuf_pktaddr + meta->stbuf_pktsize - stbuf->buf_size;
stbuf->ops->set_wp(stbuf, wp);
stbuf->stream_offset += meta->stbuf_pktsize;
#ifdef VDEC_FCC_SUPPORT
fcc_wakeup_jump_back(container_of(stbuf, struct vdec_s, vbuf), meta);
#endif
/*
pr_debug("%s, update wp 0x%x + sz 0x%x --> 0x%x, stream_offset 0x%x\n",
__func__, meta->stbuf_pktaddr, meta->stbuf_pktsize, wp, stbuf->stream_offset);
*/
}
EXPORT_SYMBOL(stream_buffer_meta_write);
ssize_t stream_buffer_write_ex(struct file *file,
struct stream_buf_s *stbuf,
const char __user *buf,
size_t count, int flags)
{
int r;
u32 len = count;
if (buf == NULL || count == 0)
return -EINVAL;
if (stbuf_space(stbuf) < count) {
if ((flags & 2) || ((file != NULL) &&
(file->f_flags & O_NONBLOCK))) {
len = stbuf_space(stbuf);
if (len < 256) /* <1k.do eagain, */
return -EAGAIN;
} else {
len = min(stbuf_canusesize(stbuf) / 8, len);
if (stbuf_space(stbuf) < len) {
r = stbuf_wait_space(stbuf, len);
if (r < 0)
return r;
}
}
}
stbuf->last_write_jiffies64 = jiffies_64;
stbuf->is_phybuf = (flags & 1);
len = min_t(u32, len, count);
r = stbuf->ops->write(stbuf, buf, len);
if (r > 0)
stbuf->stream_offset += r;
return r;
}
EXPORT_SYMBOL(stream_buffer_write_ex);
int stream_buffer_write(struct file *file,
struct stream_buf_s *stbuf,
const char *buf,
size_t count)
{
if (stbuf->write_thread)
return threadrw_write(file, stbuf, buf, count);
else
return stream_buffer_write_ex(file, stbuf, buf, count, 0);
}
EXPORT_SYMBOL(stream_buffer_write);