blob: c2678793ba71c61803075cdb882b2f383cba716f [file] [log] [blame]
/*
* drivers/amlogic/media/stream_input/parser/streambuf.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/timer.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/amlogic/media/frame_sync/ptsserv.h>
#include <linux/amlogic/media/utils/vformat.h>
#include <linux/amlogic/iomap.h>
#include <asm/cacheflush.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
/* #include <mach/am_regs.h> */
#include <linux/amlogic/media/utils/vdec_reg.h>
#include "../../frame_provider/decoder/utils/vdec.h"
#include "streambuf_reg.h"
#include "streambuf.h"
#include <linux/amlogic/media/utils/amports_config.h>
#include "../amports/amports_priv.h"
#include <linux/dma-mapping.h>
#include <linux/dma-contiguous.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#define STBUF_SIZE (64*1024)
#define STBUF_WAIT_INTERVAL (HZ/100)
#define MEM_NAME "streambuf"
void *fetchbuf = 0;
static s32 _stbuf_alloc(struct stream_buf_s *buf, bool is_secure)
{
if (buf->buf_size == 0)
return -ENOBUFS;
while (buf->buf_start == 0) {
int flags = CODEC_MM_FLAGS_DMA;
buf->buf_page_num = PAGE_ALIGN(buf->buf_size) / PAGE_SIZE;
if (buf->type == BUF_TYPE_SUBTITLE)
flags = CODEC_MM_FLAGS_DMA_CPU;
/*
*if 4k,
*used cma first,for less mem fragments.
*/
if (((buf->type == BUF_TYPE_HEVC) ||
(buf->type == BUF_TYPE_VIDEO)) &&
buf->for_4k)
flags |= CODEC_MM_FLAGS_CMA_FIRST;
if (buf->buf_size > 20 * 1024 * 1024)
flags |= CODEC_MM_FLAGS_CMA_FIRST;
if ((buf->type == BUF_TYPE_HEVC) ||
(buf->type == BUF_TYPE_VIDEO)) {
flags |= CODEC_MM_FLAGS_FOR_VDECODER;
} else if (buf->type == BUF_TYPE_AUDIO) {
flags |= CODEC_MM_FLAGS_FOR_ADECODER;
flags |= CODEC_MM_FLAGS_DMA_CPU;
}
if (is_secure)
flags |= CODEC_MM_FLAGS_TVP;
buf->buf_start = codec_mm_alloc_for_dma(MEM_NAME,
buf->buf_page_num, 4+PAGE_SHIFT, flags);
if (!buf->buf_start) {
int is_video = (buf->type == BUF_TYPE_HEVC) ||
(buf->type == BUF_TYPE_VIDEO);
if (is_video && buf->buf_size >= 9 * SZ_1M) {/*min 6M*/
int old_size = buf->buf_size;
buf->buf_size =
PAGE_ALIGN(buf->buf_size * 2/3);
pr_info("%s stbuf alloced size = %d failed try small %d size\n",
(buf->type == BUF_TYPE_HEVC) ? "HEVC" :
(buf->type == BUF_TYPE_VIDEO) ? "Video" :
(buf->type == BUF_TYPE_AUDIO) ? "Audio" :
"Subtitle", old_size, buf->buf_size);
continue;
}
pr_info("%s stbuf alloced size = %d failed\n",
(buf->type == BUF_TYPE_HEVC) ? "HEVC" :
(buf->type == BUF_TYPE_VIDEO) ? "Video" :
(buf->type == BUF_TYPE_AUDIO) ? "Audio" :
"Subtitle", buf->buf_size);
return -ENOMEM;
}
buf->is_secure = is_secure;
pr_debug("%s stbuf alloced at %p, secure = %d, size = %d\n",
(buf->type == BUF_TYPE_HEVC) ? "HEVC" :
(buf->type == BUF_TYPE_VIDEO) ? "Video" :
(buf->type == BUF_TYPE_AUDIO) ? "Audio" :
"Subtitle", (void *)buf->buf_start,
buf->is_secure,
buf->buf_size);
}
buf->canusebuf_size = buf->buf_size;
buf->flag |= BUF_FLAG_ALLOC;
return 0;
}
int stbuf_change_size(struct stream_buf_s *buf, int size, bool is_secure)
{
unsigned long old_buf;
int old_size, old_pagenum;
int ret;
pr_info("buffersize=%d,%d,start=%p, secure=%d\n", size, buf->buf_size,
(void *)buf->buf_start, is_secure);
if (buf->buf_size == size && buf->buf_start != 0)
return 0;
old_buf = buf->buf_start;
old_size = buf->buf_size;
old_pagenum = buf->buf_page_num;
buf->buf_start = 0;
buf->buf_size = size;
ret = size;
if (size == 0 ||
_stbuf_alloc(buf, is_secure) == 0) {
/*
* size=0:We only free the old memory;
* alloc ok,changed to new buffer
*/
if (old_buf != 0) {
codec_mm_free_for_dma(MEM_NAME, old_buf);
}
if (size == 0)
buf->is_secure = false;
pr_info("changed the (%d) buffer size from %d to %d\n",
buf->type, old_size, size);
return 0;
} else {
/* alloc failed */
buf->buf_start = old_buf;
buf->buf_size = old_size;
buf->buf_page_num = old_pagenum;
pr_info("changed the (%d) buffer size from %d to %d,failed\n",
buf->type, old_size, size);
}
return ret;
}
int stbuf_fetch_init(void)
{
if (NULL != fetchbuf)
return 0;
fetchbuf = (void *)__get_free_pages(GFP_KERNEL,
get_order(FETCHBUF_SIZE));
if (!fetchbuf) {
pr_info("%s: Can not allocate fetch working buffer\n",
__func__);
return -ENOMEM;
}
return 0;
}
EXPORT_SYMBOL(stbuf_fetch_init);
void stbuf_fetch_release(void)
{
if (0 && fetchbuf) {
/* always don't free.for safe alloc/free*/
free_pages((unsigned long)fetchbuf, get_order(FETCHBUF_SIZE));
fetchbuf = 0;
}
}
static void _stbuf_timer_func(struct timer_list *arg)
{
struct stream_buf_s *p = (struct stream_buf_s *)arg;
if (stbuf_space(p) < p->wcnt) {
p->timer.expires = jiffies + STBUF_WAIT_INTERVAL;
add_timer(&p->timer);
} else
wake_up_interruptible(&p->wq);
}
u32 stbuf_level(struct stream_buf_s *buf)
{
if ((buf->type == BUF_TYPE_HEVC) || (buf->type == BUF_TYPE_VIDEO)) {
if (buf->no_parser) {
int level = buf->buf_wp - buf->buf_rp;
if (level < 0)
level += buf->buf_size;
return level;
} else {
if (READ_PARSER_REG(PARSER_ES_CONTROL) & 1) {
int level = READ_PARSER_REG(PARSER_VIDEO_WP) -
READ_PARSER_REG(PARSER_VIDEO_RP);
if (level < 0)
level += READ_PARSER_REG(PARSER_VIDEO_END_PTR) -
READ_PARSER_REG(PARSER_VIDEO_START_PTR) + 8;
return (u32)level;
} else
return (buf->type == BUF_TYPE_HEVC) ?
READ_VREG(HEVC_STREAM_LEVEL) :
_READ_ST_REG(LEVEL);
}
}
return _READ_ST_REG(LEVEL);
}
u32 stbuf_rp(struct stream_buf_s *buf)
{
if ((buf->type == BUF_TYPE_HEVC) || (buf->type == BUF_TYPE_VIDEO)) {
if (buf->no_parser)
return buf->buf_rp;
else {
if (READ_PARSER_REG(PARSER_ES_CONTROL) & 1)
return READ_PARSER_REG(PARSER_VIDEO_RP);
else
return (buf->type == BUF_TYPE_HEVC) ?
READ_VREG(HEVC_STREAM_RD_PTR) :
_READ_ST_REG(RP);
}
}
return _READ_ST_REG(RP);
}
u32 stbuf_space(struct stream_buf_s *buf)
{
/* reserved space for safe write,
* the parser fifo size is 1024byts, so reserve it
*/
int size;
size = buf->canusebuf_size - stbuf_level(buf);
if (buf->canusebuf_size >= buf->buf_size / 2) {
/* old reversed value,tobe full, reversed only... */
size = size - 6 * 1024;
}
if (!buf->no_parser) {
if ((buf->type == BUF_TYPE_VIDEO)
|| (has_hevc_vdec() && buf->type == BUF_TYPE_HEVC))
size -= READ_PARSER_REG(PARSER_VIDEO_HOLE);
}
return size > 0 ? size : 0;
}
u32 stbuf_size(struct stream_buf_s *buf)
{
return buf->buf_size;
}
u32 stbuf_canusesize(struct stream_buf_s *buf)
{
return buf->canusebuf_size;
}
s32 stbuf_init(struct stream_buf_s *buf, struct vdec_s *vdec)
{
s32 r;
u32 dummy;
u32 addr32;
VDEC_PRINT_FUN_LINENO(__func__, __LINE__);
if (!buf->buf_start) {
r = _stbuf_alloc(buf, (vdec) ?
vdec->port_flag & PORT_FLAG_DRM : 0);
if (r < 0)
return r;
}
addr32 = buf->buf_start & 0xffffffff;
buf->use_ptsserv = true;
init_waitqueue_head(&buf->wq);
/*
* For multidec, do not touch HW stream buffers during port
* init and release.
*/
if ((buf->type == BUF_TYPE_VIDEO) || (buf->type == BUF_TYPE_HEVC)) {
if (vdec) {
if (vdec_stream_based(vdec))
vdec_set_input_buffer(vdec, addr32,
buf->buf_size);
else
return vdec_set_input_buffer(vdec, addr32,
buf->buf_size);
}
}
buf->write_thread = 0;
if (((vdec && !vdec_single(vdec)) || (buf->is_multi_inst)) &&
(vdec_get_debug_flags() & 0x2) == 0)
return 0;
if (has_hevc_vdec() && buf->type == BUF_TYPE_HEVC) {
CLEAR_VREG_MASK(HEVC_STREAM_CONTROL, 1);
WRITE_VREG(HEVC_STREAM_START_ADDR, addr32);
WRITE_VREG(HEVC_STREAM_END_ADDR, addr32 + buf->buf_size);
WRITE_VREG(HEVC_STREAM_RD_PTR, addr32);
WRITE_VREG(HEVC_STREAM_WR_PTR, addr32);
return 0;
}
if (buf->type == BUF_TYPE_VIDEO) {
VDEC_PRINT_FUN_LINENO(__func__, __LINE__);
_WRITE_ST_REG(CONTROL, 0);
/* reset VLD before setting all pointers */
WRITE_VREG(VLD_MEM_VIFIFO_WRAP_COUNT, 0);
/*TODO: only > m6*/
#if 1/* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
WRITE_VREG(DOS_SW_RESET0, (1 << 4));
WRITE_VREG(DOS_SW_RESET0, 0);
#else
WRITE_RESET_REG(RESET0_REGISTER, RESET_VLD);
#endif
dummy = READ_RESET_REG(RESET0_REGISTER);
WRITE_VREG(POWER_CTL_VLD, 1 << 4);
} else if (buf->type == BUF_TYPE_AUDIO) {
_WRITE_ST_REG(CONTROL, 0);
WRITE_AIU_REG(AIU_AIFIFO_GBIT, 0x80);
}
if (buf->type == BUF_TYPE_SUBTITLE) {
WRITE_PARSER_REG(PARSER_SUB_RP, addr32);
WRITE_PARSER_REG(PARSER_SUB_START_PTR, addr32);
WRITE_PARSER_REG(PARSER_SUB_END_PTR,
addr32 + buf->buf_size - 8);
return 0;
}
_WRITE_ST_REG(START_PTR, addr32);
_WRITE_ST_REG(CURR_PTR, addr32);
_WRITE_ST_REG(END_PTR, addr32 + buf->buf_size - 8);
_SET_ST_REG_MASK(CONTROL, MEM_BUFCTRL_INIT);
_CLR_ST_REG_MASK(CONTROL, MEM_BUFCTRL_INIT);
_WRITE_ST_REG(BUF_CTRL, MEM_BUFCTRL_MANUAL);
_WRITE_ST_REG(WP, addr32);
_SET_ST_REG_MASK(BUF_CTRL, MEM_BUFCTRL_INIT);
_CLR_ST_REG_MASK(BUF_CTRL, MEM_BUFCTRL_INIT);
_SET_ST_REG_MASK(CONTROL,
(0x11 << 16) | MEM_FILL_ON_LEVEL | MEM_CTRL_FILL_EN |
MEM_CTRL_EMPTY_EN);
if (buf->no_parser)
_SET_ST_REG_MASK(CONTROL, 7 << 3);
return 0;
}
EXPORT_SYMBOL(stbuf_init);
void stbuf_vdec2_init(struct stream_buf_s *buf)
{
_WRITE_VDEC2_ST_REG(CONTROL, 0);
_WRITE_VDEC2_ST_REG(START_PTR, _READ_ST_REG(START_PTR));
_WRITE_VDEC2_ST_REG(END_PTR, _READ_ST_REG(END_PTR));
_WRITE_VDEC2_ST_REG(CURR_PTR, _READ_ST_REG(CURR_PTR));
_WRITE_VDEC2_ST_REG(CONTROL, MEM_FILL_ON_LEVEL | MEM_BUFCTRL_INIT);
_WRITE_VDEC2_ST_REG(CONTROL, MEM_FILL_ON_LEVEL);
_WRITE_VDEC2_ST_REG(BUF_CTRL, MEM_BUFCTRL_INIT);
_WRITE_VDEC2_ST_REG(BUF_CTRL, 0);
_WRITE_VDEC2_ST_REG(CONTROL,
(0x11 << 16) | MEM_FILL_ON_LEVEL | MEM_CTRL_FILL_EN
| MEM_CTRL_EMPTY_EN);
}
s32 stbuf_wait_space(struct stream_buf_s *stream_buf, size_t count)
{
struct stream_buf_s *p = stream_buf;
long time_out = 200;
p->wcnt = count;
timer_setup(&p->timer, _stbuf_timer_func, (ulong) p);
mod_timer(&p->timer, jiffies + STBUF_WAIT_INTERVAL);
if (wait_event_interruptible_timeout
(p->wq, stbuf_space(p) >= count,
msecs_to_jiffies(time_out)) == 0) {
del_timer_sync(&p->timer);
return -EAGAIN;
}
del_timer_sync(&p->timer);
return 0;
}
void stbuf_release(struct stream_buf_s *buf)
{
int r;
buf->first_tstamp = INVALID_PTS;
if (!buf->ext_buf_addr) {
r = stbuf_init(buf, NULL);/* reinit buffer */
if (r < 0)
pr_err("stbuf_release %d, stbuf_init failed\n", __LINE__);
}
if (buf->flag & BUF_FLAG_ALLOC && buf->buf_start) {
codec_mm_free_for_dma(MEM_NAME, buf->buf_start);
buf->flag &= ~BUF_FLAG_ALLOC;
buf->buf_start = 0;
buf->is_secure = false;
}
buf->flag &= ~BUF_FLAG_IN_USE;
}
EXPORT_SYMBOL(stbuf_release);
u32 stbuf_sub_rp_get(void)
{
return READ_PARSER_REG(PARSER_SUB_RP);
}
void stbuf_sub_rp_set(unsigned int sub_rp)
{
WRITE_PARSER_REG(PARSER_SUB_RP, sub_rp);
return;
}
u32 stbuf_sub_wp_get(void)
{
return READ_PARSER_REG(PARSER_SUB_WP);
}
u32 stbuf_sub_start_get(void)
{
return READ_PARSER_REG(PARSER_SUB_START_PTR);
}
u32 parser_get_wp(struct stream_buf_s *vb)
{
return READ_PARSER_REG(PARSER_VIDEO_WP);
}
EXPORT_SYMBOL(parser_get_wp);
void parser_set_wp(struct stream_buf_s *vb, u32 val)
{
WRITE_PARSER_REG(PARSER_VIDEO_WP, val);
}
EXPORT_SYMBOL(parser_set_wp);
u32 parser_get_rp(struct stream_buf_s *vb)
{
return READ_PARSER_REG(PARSER_VIDEO_RP);
}
EXPORT_SYMBOL(parser_get_rp);
void parser_set_rp(struct stream_buf_s *vb, u32 val)
{
WRITE_PARSER_REG(PARSER_VIDEO_RP, val);
}
EXPORT_SYMBOL(parser_set_rp);