blob: 2675551484916a63ff7659b37fb81f7ececd1a1d [file] [log] [blame]
/*
* drivers/amlogic/media/stream_input/parser/esparser.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.
*
*/
#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/amlogic/media/frame_sync/ptsserv.h>
#include <linux/uaccess.h>
#include <linux/atomic.h>
/* #include <mach/am_regs.h> */
#include <linux/delay.h>
#include "../../frame_provider/decoder/utils/vdec.h"
#include <linux/amlogic/media/utils/vdec_reg.h>
#include "../amports/streambuf_reg.h"
#include "../amports/streambuf.h"
#include "esparser.h"
#include "../amports/amports_priv.h"
#include "../amports/thread_rw.h"
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#define SAVE_SCR 0
#define ES_START_CODE_PATTERN 0x00000100
#define ES_START_CODE_MASK 0xffffff00
#define SEARCH_PATTERN_LEN 512
#define ES_PARSER_POP READ_PARSER_REG(PFIFO_DATA)
#define PARSER_WRITE (ES_WRITE | ES_PARSER_START)
#define PARSER_VIDEO (ES_TYPE_VIDEO)
#define PARSER_AUDIO (ES_TYPE_AUDIO)
#define PARSER_SUBPIC (ES_TYPE_SUBTITLE)
#define PARSER_PASSTHROUGH (ES_PASSTHROUGH | ES_PARSER_START)
#define PARSER_AUTOSEARCH (ES_SEARCH | ES_PARSER_START)
#define PARSER_DISCARD (ES_DISCARD | ES_PARSER_START)
#define PARSER_BUSY (ES_PARSER_BUSY)
#define MAX_DRM_PACKAGE_SIZE 0x500000
static unsigned char *search_pattern;
static dma_addr_t search_pattern_map;
static u32 audio_real_wp;
static u32 audio_buf_start;
static u32 audio_buf_end;
static const char esparser_id[] = "esparser-id";
static DECLARE_WAIT_QUEUE_HEAD(wq);
static u32 search_done;
static u32 video_data_parsed;
static u32 audio_data_parsed;
static atomic_t esparser_use_count = ATOMIC_INIT(0);
static DEFINE_MUTEX(esparser_mutex);
static inline u32 get_buf_wp(u32 type)
{
if (type == BUF_TYPE_AUDIO)
return audio_real_wp;
else
return 0;
}
static inline u32 get_buf_start(u32 type)
{
if (type == BUF_TYPE_AUDIO)
return audio_buf_start;
else
return 0;
}
static inline u32 get_buf_end(u32 type)
{
if (type == BUF_TYPE_AUDIO)
return audio_buf_end;
else
return 0;
}
static void set_buf_wp(u32 type, u32 wp)
{
if (type == BUF_TYPE_AUDIO) {
audio_real_wp = wp;
WRITE_AIU_REG(AIU_MEM_AIFIFO_MAN_WP, wp/* & 0xffffff00*/);
}
return;
}
static irqreturn_t esparser_isr(int irq, void *dev_id)
{
u32 int_status = READ_PARSER_REG(PARSER_INT_STATUS);
WRITE_PARSER_REG(PARSER_INT_STATUS, int_status);
if (int_status & PARSER_INTSTAT_SC_FOUND) {
WRITE_PARSER_REG(PFIFO_RD_PTR, 0);
WRITE_PARSER_REG(PFIFO_WR_PTR, 0);
search_done = 1;
wake_up_interruptible(&wq);
}
return IRQ_HANDLED;
}
static inline u32 buf_wp(u32 type)
{
u32 wp;
if ((READ_PARSER_REG(PARSER_ES_CONTROL) & ES_VID_MAN_RD_PTR) == 0) {
wp =
#if 1/* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
(type == BUF_TYPE_HEVC) ? READ_VREG(HEVC_STREAM_WR_PTR) :
#endif
(type == BUF_TYPE_VIDEO) ? READ_VREG(VLD_MEM_VIFIFO_WP) :
(type == BUF_TYPE_AUDIO) ?
READ_AIU_REG(AIU_MEM_AIFIFO_MAN_WP) :
READ_PARSER_REG(PARSER_SUB_START_PTR);
} else {
wp =
#if 1/* MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
(type == BUF_TYPE_HEVC) ? READ_PARSER_REG(PARSER_VIDEO_WP) :
#endif
(type == BUF_TYPE_VIDEO) ? READ_PARSER_REG(PARSER_VIDEO_WP) :
(type == BUF_TYPE_AUDIO) ?
READ_AIU_REG(AIU_MEM_AIFIFO_MAN_WP) :
READ_PARSER_REG(PARSER_SUB_START_PTR);
}
return wp;
}
static int esparser_stbuf_write(struct stream_buf_s *stbuf, const u8 *buf, u32 count)
{
size_t r = count;
const char __user *p = buf;
u32 len = 0;
u32 parser_type;
int ret;
u32 wp;
dma_addr_t dma_addr = 0;
u32 type = stbuf->type;
VDEC_PRINT_FUN_LINENO(__func__, __LINE__);
if (type == BUF_TYPE_HEVC)
parser_type = PARSER_VIDEO;
else if (type == BUF_TYPE_VIDEO)
parser_type = PARSER_VIDEO;
else if (type == BUF_TYPE_AUDIO)
parser_type = PARSER_AUDIO;
else
parser_type = PARSER_SUBPIC;
wp = buf_wp(type);
if (r > 0) {
if (stbuf->is_phybuf)
len = count;
else {
len = min_t(size_t, r, (size_t) FETCHBUF_SIZE);
if (copy_from_user(fetchbuf, p, len))
return -EFAULT;
dma_addr = dma_map_single(
amports_get_dma_device(), fetchbuf,
FETCHBUF_SIZE, DMA_TO_DEVICE);
if (dma_mapping_error(amports_get_dma_device(),
(dma_addr_t) dma_addr))
return -EFAULT;
}
/* wmb(); don't need */
/* reset the Write and read pointer to zero again */
WRITE_PARSER_REG(PFIFO_RD_PTR, 0);
WRITE_PARSER_REG(PFIFO_WR_PTR, 0);
WRITE_PARSER_REG_BITS(PARSER_CONTROL, len, ES_PACK_SIZE_BIT,
ES_PACK_SIZE_WID);
WRITE_PARSER_REG_BITS(PARSER_CONTROL,
parser_type | PARSER_WRITE |
PARSER_AUTOSEARCH, ES_CTRL_BIT,
ES_CTRL_WID);
if (stbuf->is_phybuf) {
u32 buf_32 = (unsigned long)buf & 0xffffffff;
WRITE_PARSER_REG(PARSER_FETCH_ADDR, buf_32);
} else {
WRITE_PARSER_REG(PARSER_FETCH_ADDR, dma_addr);
dma_unmap_single(amports_get_dma_device(), dma_addr,
FETCHBUF_SIZE, DMA_TO_DEVICE);
}
search_done = 0;
if (!(stbuf->drm_flag & TYPE_PATTERN)) {
WRITE_PARSER_REG(PARSER_FETCH_CMD,
(7 << FETCH_ENDIAN) | len);
WRITE_PARSER_REG(PARSER_FETCH_ADDR, search_pattern_map);
WRITE_PARSER_REG(PARSER_FETCH_CMD,
(7 << FETCH_ENDIAN) | SEARCH_PATTERN_LEN);
} else {
WRITE_PARSER_REG(PARSER_FETCH_CMD,
(7 << FETCH_ENDIAN) | (len + 512));
}
ret = wait_event_interruptible_timeout(wq, search_done != 0,
HZ / 5);
if (ret == 0) {
WRITE_PARSER_REG(PARSER_FETCH_CMD, 0);
if (wp == buf_wp(type)) {
/*no data fetched */
return -EAGAIN;
} else {
pr_info("W Timeout, but fetch ok,");
pr_info("type %d len=%d,wpdiff=%d, isphy %x\n",
type, len, wp - buf_wp(type), stbuf->is_phybuf);
}
} else if (ret < 0)
return -ERESTARTSYS;
}
if ((type == BUF_TYPE_VIDEO)
|| (has_hevc_vdec() && (type == BUF_TYPE_HEVC)))
video_data_parsed += len;
else if (type == BUF_TYPE_AUDIO)
audio_data_parsed += len;
threadrw_update_buffer_level(stbuf, len);
VDEC_PRINT_FUN_LINENO(__func__, __LINE__);
return len;
}
static ssize_t _esparser_write(const char __user *buf,
size_t count, struct stream_buf_s *stbuf, int isphybuf)
{
return esparser_stbuf_write(stbuf, buf, count);
}
static ssize_t _esparser_write_s(const char __user *buf,
size_t count, struct stream_buf_s *stbuf)
{
size_t r = count;
const char __user *p = buf;
u32 len = 0;
int ret;
u32 wp, buf_start, buf_end;
u32 type = stbuf->type;
void *vaddr = NULL;
if (type != BUF_TYPE_AUDIO)
BUG();
wp = get_buf_wp(type);
buf_end = get_buf_end(type) + 8;
buf_start = get_buf_start(type);
/*pr_info("write wp 0x%x, count %d, start 0x%x, end 0x%x\n",
* wp, (u32)count, buf_start, buf_end);*/
if (wp + count > buf_end) {
if (wp == buf_end) {
wp = buf_start;
set_buf_wp(type, wp);
return -EAGAIN;
}
vaddr = codec_mm_phys_to_virt(wp);
ret = copy_from_user(vaddr, p, buf_end - wp);
if (ret > 0) {
len += buf_end - wp - ret;
codec_mm_dma_flush(vaddr, len, DMA_TO_DEVICE);
wp += len;
pr_info("copy from user not finished\n");
set_buf_wp(type, wp);
goto end_write;
} else if (ret == 0) {
len += buf_end - wp;
codec_mm_dma_flush(vaddr, len, DMA_TO_DEVICE);
wp = buf_start;
r = count - len;
set_buf_wp(type, wp);
} else {
pr_info("copy from user failed 1\n");
pr_info("w wp 0x%x, count %d, start 0x%x end 0x%x\n",
wp, (u32)count, buf_start, buf_end);
return -EAGAIN;
}
}
vaddr = codec_mm_phys_to_virt(wp);
ret = copy_from_user(vaddr, p + len, r);
if (ret >= 0) {
len += r - ret;
codec_mm_dma_flush(vaddr, r - ret, DMA_TO_DEVICE);
if (ret > 0)
pr_info("copy from user not finished 2\n");
wp += r - ret;
set_buf_wp(type, wp);
} else {
pr_info("copy from user failed 2\n");
return -EAGAIN;
}
end_write:
if (type == BUF_TYPE_AUDIO)
{
audio_data_parsed += len;
threadrw_update_buffer_level(stbuf, len);
}
return len;
}
s32 es_vpts_checkin_us64(struct stream_buf_s *buf, u64 us64)
{
u32 passed;
if (buf->write_thread)
passed = threadrw_dataoffset(buf);
else
passed = video_data_parsed;
return pts_checkin_offset_us64(PTS_TYPE_VIDEO, passed, us64);
}
s32 es_apts_checkin_us64(struct stream_buf_s *buf, u64 us64)
{
u32 passed;
if (buf->write_thread)
passed = threadrw_dataoffset(buf);
else
passed = audio_data_parsed;
return pts_checkin_offset_us64(PTS_TYPE_AUDIO, passed, us64);
}
s32 es_vpts_checkin(struct stream_buf_s *buf, u32 pts)
{
#if 0
if (buf->first_tstamp == INVALID_PTS) {
buf->flag |= BUF_FLAG_FIRST_TSTAMP;
buf->first_tstamp = pts;
return 0;
}
#endif
u32 passed = 0;
mutex_lock(&esparser_mutex);
passed = video_data_parsed + threadrw_buffer_level(buf);
mutex_unlock(&esparser_mutex);
return pts_checkin_offset(PTS_TYPE_VIDEO, passed, pts);
}
s32 es_apts_checkin(struct stream_buf_s *buf, u32 pts)
{
#if 0
if (buf->first_tstamp == INVALID_PTS) {
buf->flag |= BUF_FLAG_FIRST_TSTAMP;
buf->first_tstamp = pts;
return 0;
}
#endif
u32 passed = 0;
mutex_lock(&esparser_mutex);
passed = audio_data_parsed + threadrw_buffer_level(buf);
mutex_unlock(&esparser_mutex);
return pts_checkin_offset(PTS_TYPE_AUDIO, passed, pts);
}
s32 esparser_init(struct stream_buf_s *buf, struct vdec_s *vdec)
{
s32 r = 0;
u32 pts_type;
u32 parser_sub_start_ptr;
u32 parser_sub_end_ptr;
u32 parser_sub_rp;
bool first_use = false;
/* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
VDEC_PRINT_FUN_LINENO(__func__, __LINE__);
if (has_hevc_vdec() && (buf->type == BUF_TYPE_HEVC))
pts_type = PTS_TYPE_HEVC;
else
/* #endif */
if (buf->type == BUF_TYPE_VIDEO)
pts_type = PTS_TYPE_VIDEO;
else if (buf->type == BUF_TYPE_AUDIO)
pts_type = PTS_TYPE_AUDIO;
else if (buf->type == BUF_TYPE_SUBTITLE)
pts_type = PTS_TYPE_MAX;
else
return -EINVAL;
mutex_lock(&esparser_mutex);
parser_sub_start_ptr = READ_PARSER_REG(PARSER_SUB_START_PTR);
parser_sub_end_ptr = READ_PARSER_REG(PARSER_SUB_END_PTR);
parser_sub_rp = READ_PARSER_REG(PARSER_SUB_RP);
buf->flag |= BUF_FLAG_PARSER;
if (atomic_add_return(1, &esparser_use_count) == 1) {
first_use = true;
if (fetchbuf == 0) {
pr_info("%s: no fetchbuf\n", __func__);
r = -ENOMEM;
goto Err_1;
}
if (search_pattern == NULL) {
search_pattern = kcalloc(1,
SEARCH_PATTERN_LEN,
GFP_KERNEL);
if (search_pattern == NULL) {
pr_err("%s: no search_pattern\n", __func__);
r = -ENOMEM;
goto Err_1;
}
/* build a fake start code to get parser interrupt */
search_pattern[0] = 0x00;
search_pattern[1] = 0x00;
search_pattern[2] = 0x01;
search_pattern[3] = 0xff;
search_pattern_map = dma_map_single(
amports_get_dma_device(),
search_pattern,
SEARCH_PATTERN_LEN,
DMA_TO_DEVICE);
}
/* reset PARSER with first esparser_init() call */
WRITE_RESET_REG(RESET1_REGISTER, RESET_PARSER);
/* for recorded file and local play, this can't change the input source*/
/* TS data path */
/*
#ifndef CONFIG_AM_DVB
WRITE_DEMUX_REG(FEC_INPUT_CONTROL, 0);
#else
tsdemux_set_reset_flag();
#endif */
CLEAR_DEMUX_REG_MASK(TS_HIU_CTL, 1 << USE_HI_BSF_INTERFACE);
CLEAR_DEMUX_REG_MASK(TS_HIU_CTL_2, 1 << USE_HI_BSF_INTERFACE);
CLEAR_DEMUX_REG_MASK(TS_HIU_CTL_3, 1 << USE_HI_BSF_INTERFACE);
CLEAR_DEMUX_REG_MASK(TS_FILE_CONFIG, (1 << TS_HIU_ENABLE));
WRITE_PARSER_REG(PARSER_CONFIG,
(10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) |
(1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) |
(16 << PS_CFG_MAX_FETCH_CYCLE_BIT));
WRITE_PARSER_REG(PFIFO_RD_PTR, 0);
WRITE_PARSER_REG(PFIFO_WR_PTR, 0);
WRITE_PARSER_REG(PARSER_SEARCH_PATTERN, ES_START_CODE_PATTERN);
WRITE_PARSER_REG(PARSER_SEARCH_MASK, ES_START_CODE_MASK);
WRITE_PARSER_REG(PARSER_CONFIG,
(10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) |
(1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) |
PS_CFG_STARTCODE_WID_24 |
PS_CFG_PFIFO_ACCESS_WID_8 |
/* single byte pop */
(16 << PS_CFG_MAX_FETCH_CYCLE_BIT));
WRITE_PARSER_REG(PARSER_CONTROL, PARSER_AUTOSEARCH);
}
/* hook stream buffer with PARSER */
if (has_hevc_vdec() && (pts_type == PTS_TYPE_HEVC)) {
WRITE_PARSER_REG(PARSER_VIDEO_START_PTR, vdec->input.start);
WRITE_PARSER_REG(PARSER_VIDEO_END_PTR, vdec->input.start
+ vdec->input.size - 8);
if (vdec_single(vdec)) {
CLEAR_PARSER_REG_MASK(PARSER_ES_CONTROL,
ES_VID_MAN_RD_PTR);
/* set vififo_vbuf_rp_sel=>hevc */
WRITE_VREG(DOS_GEN_CTRL0, 3 << 1);
/* set use_parser_vbuf_wp */
SET_VREG_MASK(HEVC_STREAM_CONTROL,
(1 << 3) | (0 << 4));
/* set stream_fetch_enable */
SET_VREG_MASK(HEVC_STREAM_CONTROL, 1);
if (buf->no_parser) {
/*set endian for non-parser mode */
SET_VREG_MASK(HEVC_STREAM_CONTROL, 7 << 4);
}
/* set stream_buffer_hole with 256 bytes */
SET_VREG_MASK(HEVC_STREAM_FIFO_CTL, (1 << 29));
} else {
SET_PARSER_REG_MASK(PARSER_ES_CONTROL,
ES_VID_MAN_RD_PTR);
WRITE_PARSER_REG(PARSER_VIDEO_WP, vdec->input.start);
WRITE_PARSER_REG(PARSER_VIDEO_RP, vdec->input.start);
}
video_data_parsed = 0;
} else if (pts_type == PTS_TYPE_VIDEO) {
WRITE_PARSER_REG(PARSER_VIDEO_START_PTR,
vdec->input.start);
WRITE_PARSER_REG(PARSER_VIDEO_END_PTR,
vdec->input.start + vdec->input.size - 8);
if (vdec_single(vdec) || (vdec_get_debug_flags() & 0x2)) {
if (vdec_get_debug_flags() & 0x2)
pr_info("%s %d\n", __func__, __LINE__);
CLEAR_PARSER_REG_MASK(PARSER_ES_CONTROL,
ES_VID_MAN_RD_PTR);
WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
CLEAR_VREG_MASK(VLD_MEM_VIFIFO_BUF_CNTL,
MEM_BUFCTRL_INIT);
if (has_hevc_vdec()) {
/* set vififo_vbuf_rp_sel=>vdec */
WRITE_VREG(DOS_GEN_CTRL0, 0);
}
} else {
SET_PARSER_REG_MASK(PARSER_ES_CONTROL,
ES_VID_MAN_RD_PTR);
WRITE_PARSER_REG(PARSER_VIDEO_WP,
vdec->input.start);
WRITE_PARSER_REG(PARSER_VIDEO_RP,
vdec->input.start);
}
video_data_parsed = 0;
} else if (pts_type == PTS_TYPE_AUDIO) {
/* set wp as buffer start */
SET_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL,
MEM_BUFCTRL_MANUAL);
WRITE_AIU_REG(AIU_MEM_AIFIFO_MAN_RP,
READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR));
WRITE_AIU_REG_BITS(AIU_MEM_AIFIFO_CONTROL, 7, 3, 3);
SET_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL,
MEM_BUFCTRL_INIT);
CLEAR_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL,
MEM_BUFCTRL_INIT);
WRITE_AIU_REG(AIU_MEM_AIFIFO_MAN_WP,
READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR));
audio_data_parsed = 0;
audio_buf_start =
READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR);
audio_real_wp = audio_buf_start;
audio_buf_end = READ_AIU_REG(AIU_MEM_AIFIFO_END_PTR);
} else if (buf->type == BUF_TYPE_SUBTITLE) {
WRITE_PARSER_REG(PARSER_SUB_START_PTR,
parser_sub_start_ptr);
WRITE_PARSER_REG(PARSER_SUB_END_PTR,
parser_sub_end_ptr);
WRITE_PARSER_REG(PARSER_SUB_RP, parser_sub_rp);
SET_PARSER_REG_MASK(PARSER_ES_CONTROL,
(7 << ES_SUB_WR_ENDIAN_BIT) |
ES_SUB_MAN_RD_PTR);
}
if (pts_type < PTS_TYPE_MAX) {
r = pts_start(pts_type);
if (r < 0) {
pr_info("esparser_init: pts_start failed\n");
goto Err_1;
}
}
#if 0
if (buf->flag & BUF_FLAG_FIRST_TSTAMP) {
if (buf->type == BUF_TYPE_VIDEO)
es_vpts_checkin(buf, buf->first_tstamp);
else if (buf->type == BUF_TYPE_AUDIO)
es_apts_checkin(buf, buf->first_tstamp);
buf->flag &= ~BUF_FLAG_FIRST_TSTAMP;
}
#endif
if (first_use) {
/*TODO irq */
r = vdec_request_irq(PARSER_IRQ, esparser_isr,
"parser", (void *)esparser_id);
if (r) {
pr_info("esparser_init: irq register failed.\n");
goto Err_2;
}
VDEC_PRINT_FUN_LINENO(__func__, __LINE__);
WRITE_PARSER_REG(PARSER_INT_STATUS, 0xffff);
WRITE_PARSER_REG(PARSER_INT_ENABLE,
PARSER_INTSTAT_SC_FOUND <<
PARSER_INT_HOST_EN_BIT);
}
mutex_unlock(&esparser_mutex);
if (!(vdec_get_debug_flags() & 1) &&
!codec_mm_video_tvp_enabled()) {
int block_size = (buf->type == BUF_TYPE_AUDIO) ?
PAGE_SIZE : PAGE_SIZE << 4;
int buf_num = (buf->type == BUF_TYPE_AUDIO) ?
20 : (2 * SZ_1M)/(PAGE_SIZE << 4);
if (!(buf->type == BUF_TYPE_SUBTITLE))
buf->write_thread = threadrw_alloc(buf_num,
block_size,
esparser_write_ex,
(buf->type == BUF_TYPE_AUDIO) ? 1 : 0);
/*manul mode for audio*/
}
return 0;
Err_2:
pts_stop(pts_type);
Err_1:
atomic_dec(&esparser_use_count);
buf->flag &= ~BUF_FLAG_PARSER;
mutex_unlock(&esparser_mutex);
return r;
}
EXPORT_SYMBOL(esparser_init);
void esparser_audio_reset_s(struct stream_buf_s *buf)
{
ulong flags;
DEFINE_SPINLOCK(lock);
spin_lock_irqsave(&lock, flags);
SET_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_MANUAL);
WRITE_AIU_REG(AIU_MEM_AIFIFO_MAN_RP,
READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR));
WRITE_AIU_REG_BITS(AIU_MEM_AIFIFO_CONTROL, 7, 3, 3);
SET_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
CLEAR_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
WRITE_AIU_REG(AIU_MEM_AIFIFO_MAN_WP,
READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR));
buf->flag |= BUF_FLAG_PARSER;
audio_data_parsed = 0;
audio_real_wp = READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR);
audio_buf_start = READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR);
audio_buf_end = READ_AIU_REG(AIU_MEM_AIFIFO_END_PTR);
spin_unlock_irqrestore(&lock, flags);
return;
}
void esparser_audio_reset(struct stream_buf_s *buf)
{
ulong flags;
DEFINE_SPINLOCK(lock);
spin_lock_irqsave(&lock, flags);
WRITE_PARSER_REG(PARSER_AUDIO_WP,
READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR));
WRITE_PARSER_REG(PARSER_AUDIO_RP,
READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR));
WRITE_PARSER_REG(PARSER_AUDIO_START_PTR,
READ_AIU_REG(AIU_MEM_AIFIFO_START_PTR));
WRITE_PARSER_REG(PARSER_AUDIO_END_PTR,
READ_AIU_REG(AIU_MEM_AIFIFO_END_PTR));
CLEAR_PARSER_REG_MASK(PARSER_ES_CONTROL, ES_AUD_MAN_RD_PTR);
WRITE_AIU_REG(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
CLEAR_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
buf->flag |= BUF_FLAG_PARSER;
audio_data_parsed = 0;
audio_real_wp = 0;
audio_buf_start = 0;
audio_buf_end = 0;
spin_unlock_irqrestore(&lock, flags);
}
void esparser_release(struct stream_buf_s *buf)
{
u32 pts_type;
/* check if esparser_init() is ever called */
if ((buf->flag & BUF_FLAG_PARSER) == 0)
return;
if (atomic_read(&esparser_use_count) == 0) {
pr_info
("[%s:%d]###warning, esparser has been released already\n",
__func__, __LINE__);
return;
}
if (buf->write_thread)
threadrw_release(buf);
if (atomic_dec_and_test(&esparser_use_count)) {
WRITE_PARSER_REG(PARSER_INT_ENABLE, 0);
/*TODO irq */
vdec_free_irq(PARSER_IRQ, (void *)esparser_id);
if (search_pattern) {
dma_unmap_single(amports_get_dma_device(),
search_pattern_map,
SEARCH_PATTERN_LEN, DMA_TO_DEVICE);
kfree(search_pattern);
search_pattern = NULL;
}
}
if (has_hevc_vdec() && (buf->type == BUF_TYPE_HEVC))
pts_type = PTS_TYPE_VIDEO;
else if (buf->type == BUF_TYPE_VIDEO)
pts_type = PTS_TYPE_VIDEO;
else if (buf->type == BUF_TYPE_AUDIO)
pts_type = PTS_TYPE_AUDIO;
else if (buf->type == BUF_TYPE_SUBTITLE) {
buf->flag &= ~BUF_FLAG_PARSER;
return;
} else
return;
buf->flag &= ~BUF_FLAG_PARSER;
pts_stop(pts_type);
}
EXPORT_SYMBOL(esparser_release);
ssize_t drm_write(struct file *file, struct stream_buf_s *stbuf,
const char __user *buf, size_t count)
{
s32 r;
u32 len;
u32 realcount, totalcount;
u32 havewritebytes = 0;
u32 leftcount = 0;
struct drm_info tmpmm;
struct drm_info *drm = &tmpmm;
u32 res = 0;
int drm_flag = 0;
unsigned long realbuf;
if (buf == NULL || count == 0)
return -EINVAL;
if (stbuf->write_thread) {
r = threadrw_flush_buffers(stbuf);
if (r < 0)
pr_info("Warning. drm flush threadrw failed[%d]\n", r);
}
res = copy_from_user(drm, buf, sizeof(struct drm_info));
if (res) {
pr_info("drm kmalloc failed res[%d]\n", res);
return -EFAULT;
}
if ((drm->drm_flag & TYPE_DRMINFO) && (drm->drm_hasesdata == 0)) {
if (drm->drm_pktsize > MAX_DRM_PACKAGE_SIZE) {
pr_err("drm package size is error, size is %u\n", drm->drm_pktsize);
return -EINVAL;
}
/* buf only has drminfo not have esdata; */
realbuf = drm->drm_phy;
realcount = drm->drm_pktsize;
drm_flag = drm->drm_flag;
/* DRM_PRNT("drm_get_rawdata
*onlydrminfo drm->drm_hasesdata[0x%x]
* stbuf->type %d buf[0x%x]\n",
*drm->drm_hasesdata,stbuf->type,buf);
*/
} else if (drm->drm_hasesdata == 1) { /* buf is drminfo+es; */
if (drm->drm_pktsize > MAX_DRM_PACKAGE_SIZE) {
pr_err("drm package size is error, size is %u\n", drm->drm_pktsize);
return -EINVAL;
}
realcount = drm->drm_pktsize;
realbuf = (unsigned long)buf + sizeof(struct drm_info);
drm_flag = 0;
/* DRM_PRNT("drm_get_rawdata
* drminfo+es drm->drm_hasesdata[0x%x]
* stbuf->type %d\n",drm->drm_hasesdata,stbuf->type);
*/
} else { /* buf is hwhead; */
realcount = count;
drm_flag = 0;
realbuf = (unsigned long)buf;
/* DRM_PRNT("drm_get_rawdata
* drm->drm_hasesdata[0x%x]
* len[%d] count[%d] realcout[%d]\n",
* drm->drm_hasesdata,len,count,realcount);
*/
}
len = realcount;
count = realcount;
totalcount = realcount;
stbuf->drm_flag = drm_flag;
stbuf->is_phybuf = drm_flag ? 1 : 0;
while (len > 0) {
if (stbuf->type != BUF_TYPE_SUBTITLE
&& stbuf_space(stbuf) < count) {
/*should not write partial data in drm mode*/
r = stbuf_wait_space(stbuf, count);
if (r < 0)
return r;
if (stbuf_space(stbuf) < count)
return -EAGAIN;
}
len = min_t(u32, len, count);
mutex_lock(&esparser_mutex);
if (stbuf->type != BUF_TYPE_AUDIO)
r = _esparser_write((const char __user *)realbuf, len,
stbuf, drm_flag);
else
r = _esparser_write_s((const char __user *)realbuf, len,
stbuf);
if (r < 0) {
pr_info("drm_write _esparser_write failed [%d]\n", r);
mutex_unlock(&esparser_mutex);
return r;
}
havewritebytes += r;
leftcount = totalcount - havewritebytes;
if (havewritebytes == totalcount) {
mutex_unlock(&esparser_mutex);
break; /* write ok; */
} else if ((len > 0) && (havewritebytes < totalcount)) {
DRM_PRNT
("d writebytes[%d] want[%d] total[%d] real[%d]\n",
havewritebytes, len, totalcount, realcount);
len = len - r; /* write again; */
realbuf = realbuf + r;
} else {
pr_info
("e writebytes[%d] want[%d] total[%d] real[%d]\n",
havewritebytes, len, totalcount, realcount);
}
mutex_unlock(&esparser_mutex);
}
if ((drm->drm_flag & TYPE_DRMINFO) && (drm->drm_hasesdata == 0)) {
havewritebytes = sizeof(struct drm_info);
} else if (drm->drm_hasesdata == 1) {
havewritebytes += sizeof(struct drm_info);
}
return havewritebytes;
}
EXPORT_SYMBOL(drm_write);
/*
*flags:
*1:phy
*2:noblock
*/
ssize_t esparser_write_ex(struct file *file,
struct stream_buf_s *stbuf,
const char __user *buf, size_t count,
int flags)
{
s32 r;
u32 len = count;
if (buf == NULL || count == 0)
return -EINVAL;
/*subtitle have no level to check, */
if (stbuf->type != BUF_TYPE_SUBTITLE && 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;
len = min_t(u32, len, count);
mutex_lock(&esparser_mutex);
if (flags & 1)
stbuf->is_phybuf = true;
if (stbuf->type == BUF_TYPE_AUDIO)
r = _esparser_write_s(buf, len, stbuf);
else
r = _esparser_write(buf, len, stbuf, flags & 1);
mutex_unlock(&esparser_mutex);
return r;
}
ssize_t esparser_write(struct file *file,
struct stream_buf_s *stbuf,
const char __user *buf, size_t count)
{
if (stbuf->write_thread) {
ssize_t ret;
ret = threadrw_write(file, stbuf, buf, count);
if (ret == -EAGAIN) {
u32 a, b;
int vdelay, adelay;
if ((stbuf->type != BUF_TYPE_VIDEO) &&
(stbuf->type != BUF_TYPE_HEVC))
return ret;
if (stbuf->buf_size > (SZ_1M * 30) ||
(threadrw_buffer_size(stbuf) > SZ_1M * 10) ||
!threadrw_support_more_buffers(stbuf))
return ret;
/*only chang buffer for video.*/
vdelay = calculation_stream_delayed_ms(
PTS_TYPE_VIDEO, &a, &b);
adelay = calculation_stream_delayed_ms(
PTS_TYPE_AUDIO, &a, &b);
if ((vdelay > 100 && vdelay < 2000) && /*vdelay valid.*/
((vdelay < 500) ||/*video delay is short!*/
(adelay > 0 && adelay < 1000))/*audio is low.*/
) {
/*on buffer fulled.
*if delay is less than 100ms we think errors,
*And we add more buffer on delay < 2s.
*/
int new_size = 2 * 1024 * 1024;
threadrw_alloc_more_buffer_size(
stbuf, new_size);
}
}
return ret;
}
return esparser_write_ex(file, stbuf, buf, count, 0);
}
EXPORT_SYMBOL(esparser_write);
void esparser_sub_reset(void)
{
ulong flags;
DEFINE_SPINLOCK(lock);
u32 parser_sub_start_ptr;
u32 parser_sub_end_ptr;
spin_lock_irqsave(&lock, flags);
parser_sub_start_ptr = READ_PARSER_REG(PARSER_SUB_START_PTR);
parser_sub_end_ptr = READ_PARSER_REG(PARSER_SUB_END_PTR);
WRITE_PARSER_REG(PARSER_SUB_START_PTR, parser_sub_start_ptr);
WRITE_PARSER_REG(PARSER_SUB_END_PTR, parser_sub_end_ptr);
WRITE_PARSER_REG(PARSER_SUB_RP, parser_sub_start_ptr);
WRITE_PARSER_REG(PARSER_SUB_WP, parser_sub_start_ptr);
SET_PARSER_REG_MASK(PARSER_ES_CONTROL,
(7 << ES_SUB_WR_ENDIAN_BIT) | ES_SUB_MAN_RD_PTR);
spin_unlock_irqrestore(&lock, flags);
}
static int esparser_stbuf_init(struct stream_buf_s *stbuf,
struct vdec_s *vdec)
{
int ret = -1;
ret = stbuf_init(stbuf, vdec);
if (ret)
goto out;
ret = esparser_init(stbuf, vdec);
if (!ret)
stbuf->flag |= BUF_FLAG_IN_USE;
out:
return ret;
}
static void esparser_stbuf_release(struct stream_buf_s *stbuf)
{
esparser_release(stbuf);
stbuf_release(stbuf);
}
static struct stream_buf_ops esparser_stbuf_ops = {
.init = esparser_stbuf_init,
.release = esparser_stbuf_release,
.write = esparser_stbuf_write,
.get_wp = parser_get_wp,
.set_wp = parser_set_wp,
.get_rp = parser_get_rp,
.set_rp = parser_set_rp,
};
struct stream_buf_ops *get_esparser_stbuf_ops(void)
{
return &esparser_stbuf_ops;
}
EXPORT_SYMBOL(get_esparser_stbuf_ops);