blob: 81c94c04be07ec37d25e5b594c90bd9af67c451c [file] [log] [blame]
/*
* drivers/amlogic/media/stream_input/parser/tsdemux.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/dma-mapping.h>
#include <linux/amlogic/media/frame_sync/ptsserv.h>
#include <linux/amlogic/media/frame_sync/tsync.h>
#include <linux/amlogic/media/utils/amstream.h>
#include <linux/amlogic/media/vfm/vframe_provider.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/uaccess.h>
/* #include <mach/am_regs.h> */
#include <linux/clk.h>
/* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
/* #include <mach/mod_gate.h> */
/* #endif */
#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 <linux/amlogic/media/utils/amports_config.h>
#include <linux/amlogic/media/frame_sync/tsync_pcr.h>
#include "tsdemux.h"
#include <linux/reset.h>
#include "../amports/amports_priv.h"
#define MAX_DRM_PACKAGE_SIZE 0x500000
MODULE_PARM_DESC(reset_demux_enable, "\n\t\t Reset demux enable");
static int reset_demux_enable = 0;
module_param(reset_demux_enable, int, 0644);
static const char tsdemux_fetch_id[] = "tsdemux-fetch-id";
static const char tsdemux_irq_id[] = "tsdemux-irq-id";
static u32 curr_pcr_num = 0xffff;
static u32 curr_vid_id = 0xffff;
static u32 curr_aud_id = 0xffff;
static u32 curr_sub_id = 0xffff;
static u32 curr_pcr_id = 0xffff;
static DECLARE_WAIT_QUEUE_HEAD(wq);
static u32 fetch_done;
static u32 discontinued_counter;
static u32 first_pcr;
static u8 pcrscr_valid;
static u8 pcraudio_valid;
static u8 pcrvideo_valid;
static u8 pcr_init_flag;
static int demux_skipbyte;
static struct tsdemux_ops *demux_ops;
static DEFINE_SPINLOCK(demux_ops_lock);
static int enable_demux_driver(void)
{
return demux_ops ? 1 : 0;
}
void tsdemux_set_ops(struct tsdemux_ops *ops)
{
unsigned long flags;
spin_lock_irqsave(&demux_ops_lock, flags);
demux_ops = ops;
spin_unlock_irqrestore(&demux_ops_lock, flags);
}
EXPORT_SYMBOL(tsdemux_set_ops);
int tsdemux_set_reset_flag_ext(void)
{
int r = 0;
if (demux_ops && demux_ops->set_reset_flag)
r = demux_ops->set_reset_flag();
return r;
}
int tsdemux_set_reset_flag(void)
{
unsigned long flags;
int r;
spin_lock_irqsave(&demux_ops_lock, flags);
r = tsdemux_set_reset_flag_ext();
spin_unlock_irqrestore(&demux_ops_lock, flags);
return r;
}
static int tsdemux_reset(void)
{
unsigned long flags;
int r = 0;
spin_lock_irqsave(&demux_ops_lock, flags);
if (demux_ops && demux_ops->reset) {
tsdemux_set_reset_flag_ext();
r = demux_ops->reset();
}
spin_unlock_irqrestore(&demux_ops_lock, flags);
return r;
}
static int tsdemux_request_irq(irq_handler_t handler, void *data)
{
unsigned long flags;
int r = 0;
spin_lock_irqsave(&demux_ops_lock, flags);
if (demux_ops && demux_ops->request_irq)
r = demux_ops->request_irq(handler, data);
spin_unlock_irqrestore(&demux_ops_lock, flags);
return r;
}
static int tsdemux_free_irq(void)
{
unsigned long flags;
int r = 0;
spin_lock_irqsave(&demux_ops_lock, flags);
if (demux_ops && demux_ops->free_irq)
r = demux_ops->free_irq();
spin_unlock_irqrestore(&demux_ops_lock, flags);
return r;
}
static int tsdemux_set_vid(int vpid)
{
unsigned long flags;
int r = 0;
spin_lock_irqsave(&demux_ops_lock, flags);
if (demux_ops && demux_ops->set_vid)
r = demux_ops->set_vid(vpid);
spin_unlock_irqrestore(&demux_ops_lock, flags);
return r;
}
static int tsdemux_set_aid(int apid)
{
unsigned long flags;
int r = 0;
spin_lock_irqsave(&demux_ops_lock, flags);
if (demux_ops && demux_ops->set_aid)
r = demux_ops->set_aid(apid);
spin_unlock_irqrestore(&demux_ops_lock, flags);
return r;
}
static int tsdemux_set_sid(int spid)
{
unsigned long flags;
int r = 0;
spin_lock_irqsave(&demux_ops_lock, flags);
if (demux_ops && demux_ops->set_sid)
r = demux_ops->set_sid(spid);
spin_unlock_irqrestore(&demux_ops_lock, flags);
return r;
}
static int tsdemux_set_pcrid(int pcrpid)
{
unsigned long flags;
int r = 0;
spin_lock_irqsave(&demux_ops_lock, flags);
if (demux_ops && demux_ops->set_pcrid)
r = demux_ops->set_pcrid(pcrpid);
spin_unlock_irqrestore(&demux_ops_lock, flags);
return r;
}
static int tsdemux_set_skip_byte(int skipbyte)
{
unsigned long flags;
int r = 0;
spin_lock_irqsave(&demux_ops_lock, flags);
if (demux_ops && demux_ops->set_skipbyte)
r = demux_ops->set_skipbyte(skipbyte);
spin_unlock_irqrestore(&demux_ops_lock, flags);
return r;
}
static int tsdemux_config(void)
{
return 0;
}
static void tsdemux_pcr_set(unsigned int pcr);
/*TODO irq*/
/* bit 15 ---------------*/
/* bit 12 --VIDEO_PTS[32]*/
/* bit 0 ---------------*/
/*Read the 13th bit of STB_PTS_DTS_STATUS register
correspond to the highest bit of video pts*/
static irqreturn_t tsdemux_isr(int irq, void *dev_id)
{
u32 int_status = 0;
int id = (long)dev_id;
if (!enable_demux_driver()) {
int_status = READ_DEMUX_REG(STB_INT_STATUS);
} else {
if (id == 0)
int_status = READ_DEMUX_REG(STB_INT_STATUS);
else if (id == 1)
int_status = READ_DEMUX_REG(STB_INT_STATUS_2);
else if (id == 2)
int_status = READ_DEMUX_REG(STB_INT_STATUS_3);
}
if (int_status & (1 << NEW_PDTS_READY)) {
if (!enable_demux_driver()) {
u32 pdts_status = READ_DEMUX_REG(STB_PTS_DTS_STATUS);
u64 vpts;
vpts = READ_MPEG_REG(VIDEO_PTS_DEMUX);
vpts &= 0x00000000FFFFFFFF;
if (pdts_status & 0x1000) {
vpts = vpts | (1LL<<32);
}
if (pdts_status & (1 << VIDEO_PTS_READY))
pts_checkin_wrptr_pts33(PTS_TYPE_VIDEO,
READ_DEMUX_REG(VIDEO_PDTS_WR_PTR),
vpts);
if (pdts_status & (1 << AUDIO_PTS_READY))
pts_checkin_wrptr(PTS_TYPE_AUDIO,
READ_DEMUX_REG(AUDIO_PDTS_WR_PTR),
READ_DEMUX_REG(AUDIO_PTS_DEMUX));
WRITE_DEMUX_REG(STB_PTS_DTS_STATUS, pdts_status);
} else {
#define DMX_READ_REG(i, r)\
((i) ? ((i == 1) ? READ_DEMUX_REG(r##_2) : \
READ_DEMUX_REG(r##_3)) : READ_DEMUX_REG(r))
u64 vpts;
u32 pdts_status = DMX_READ_REG(id, STB_PTS_DTS_STATUS);
vpts = DMX_READ_REG(id, VIDEO_PTS_DEMUX);
vpts &= 0x00000000FFFFFFFF;
if (pdts_status & 0x1000) {
vpts = vpts | (1LL<<32);
}
if (pdts_status & (1 << VIDEO_PTS_READY))
pts_checkin_wrptr_pts33(PTS_TYPE_VIDEO,
DMX_READ_REG(id, VIDEO_PDTS_WR_PTR),
vpts);
if (pdts_status & (1 << AUDIO_PTS_READY))
pts_checkin_wrptr(PTS_TYPE_AUDIO,
DMX_READ_REG(id, AUDIO_PDTS_WR_PTR),
DMX_READ_REG(id, AUDIO_PTS_DEMUX));
if (id == 1)
WRITE_DEMUX_REG(STB_PTS_DTS_STATUS_2,
pdts_status);
else if (id == 2)
WRITE_DEMUX_REG(STB_PTS_DTS_STATUS_3,
pdts_status);
else
WRITE_DEMUX_REG(STB_PTS_DTS_STATUS,
pdts_status);
}
}
if (int_status & (1 << DIS_CONTINUITY_PACKET)) {
discontinued_counter++;
/* pr_info("discontinued counter=%d\n",discontinued_counter); */
}
if (int_status & (1 << SUB_PES_READY)) {
/* TODO: put data to somewhere */
/* pr_info("subtitle pes ready\n"); */
wakeup_sub_poll();
}
if (int_status & (1<<PCR_READY)) {
unsigned int pcr_pts = 0xffffffff;
pcr_pts = DMX_READ_REG(id, PCR_DEMUX);
tsdemux_pcr_set(pcr_pts);
}
if (!enable_demux_driver())
WRITE_DEMUX_REG(STB_INT_STATUS, int_status);
return IRQ_HANDLED;
}
static irqreturn_t parser_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_FETCH_CMD) {
fetch_done = 1;
wake_up_interruptible(&wq);
}
return IRQ_HANDLED;
}
static ssize_t _tsdemux_write(const char __user *buf, size_t count,
int isphybuf)
{
size_t r = count;
const char __user *p = buf;
u32 len;
int ret;
dma_addr_t dma_addr = 0;
if (r > 0) {
if (isphybuf)
len = count;
else {
len = min_t(size_t, r, 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))
return -EFAULT;
}
fetch_done = 0;
wmb(); /* Ensure fetchbuf contents visible */
if (isphybuf) {
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);
}
WRITE_PARSER_REG(PARSER_FETCH_CMD, (7 << FETCH_ENDIAN) | len);
ret =
wait_event_interruptible_timeout(wq, fetch_done != 0,
HZ / 2);
if (ret == 0) {
WRITE_PARSER_REG(PARSER_FETCH_CMD, 0);
pr_info("write timeout, retry\n");
return -EAGAIN;
} else if (ret < 0)
return -ERESTARTSYS;
p += len;
r -= len;
}
return count - r;
}
#define PCR_EN 12
static int reset_pcr_regs(void)
{
u32 pcr_num;
u32 pcr_regs = 0;
if (curr_pcr_id >= 0x1FFF)
return 0;
/* set paramater to fetch pcr */
pcr_num = 0;
if (curr_pcr_id == curr_vid_id)
pcr_num = 0;
else if (curr_pcr_id == curr_aud_id)
pcr_num = 1;
else if (curr_pcr_id == curr_sub_id)
pcr_num = 2;
else
pcr_num = 3;
if (pcr_num != curr_pcr_num) {
u32 clk_unit = 0;
u32 clk_81 = 0;
struct clk *clk;
//clk = clk_get(NULL,"clk81");
clk= devm_clk_get(amports_get_dma_device(),"clk_81");
if (IS_ERR(clk) || clk == 0) {
pr_info("[%s:%d] error clock\n", __func__, __LINE__);
return 0;
}
clk_81 = clk_get_rate(clk);
clk_unit = clk_81 / 90000;
pr_info("[%s:%d] clk_81 = %x clk_unit =%x\n", __func__,
__LINE__, clk_81, clk_unit);
pcr_regs = 1 << PCR_EN | clk_unit;
pr_info("[tsdemux_init] the set pcr_regs =%x\n", pcr_regs);
if (READ_DEMUX_REG(TS_HIU_CTL_2) & 0x80) {
WRITE_DEMUX_REG(PCR90K_CTL_2, pcr_regs);
WRITE_DEMUX_REG(ASSIGN_PID_NUMBER_2, pcr_num);
pr_info("[tsdemux_init] To use device 2,pcr_num=%d\n",
pcr_num);
pr_info("tsdemux_init] the read pcr_regs= %x\n",
READ_DEMUX_REG(PCR90K_CTL_2));
} else if (READ_DEMUX_REG(TS_HIU_CTL_3) & 0x80) {
WRITE_DEMUX_REG(PCR90K_CTL_3, pcr_regs);
WRITE_DEMUX_REG(ASSIGN_PID_NUMBER_3, pcr_num);
pr_info("[tsdemux_init] To use device 3,pcr_num=%d\n",
pcr_num);
pr_info("tsdemux_init] the read pcr_regs= %x\n",
READ_DEMUX_REG(PCR90K_CTL_3));
} else {
WRITE_DEMUX_REG(PCR90K_CTL, pcr_regs);
WRITE_DEMUX_REG(ASSIGN_PID_NUMBER, pcr_num);
pr_info("[tsdemux_init] To use device 1,pcr_num=%d\n",
pcr_num);
pr_info("tsdemux_init] the read pcr_regs= %x\n",
READ_DEMUX_REG(PCR90K_CTL));
}
curr_pcr_num = pcr_num;
}
return 1;
}
s32 tsdemux_init(u32 vid, u32 aid, u32 sid, u32 pcrid, bool is_hevc,
struct vdec_s *vdec)
{
s32 r;
u32 parser_sub_start_ptr;
u32 parser_sub_end_ptr;
u32 parser_sub_rp;
pcrvideo_valid = 0;
pcraudio_valid = 0;
pcr_init_flag = 0;
/* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
/*TODO clk */
/*
*switch_mod_gate_by_type(MOD_DEMUX, 1);
*/
/* #endif */
amports_switch_gate("demux", 1);
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);
WRITE_RESET_REG(RESET1_REGISTER, RESET_PARSER);
if (enable_demux_driver()) {
tsdemux_reset();
} else {
WRITE_RESET_REG(RESET1_REGISTER, RESET_PARSER | RESET_DEMUXSTB);
WRITE_DEMUX_REG(STB_TOP_CONFIG, 0);
WRITE_DEMUX_REG(DEMUX_CONTROL, 0);
}
/* set PID filter */
pr_info
("tsdemux video_pid = 0x%x, audio_pid = 0x%x,",
vid, aid);
pr_info
("sub_pid = 0x%x, pcrid = 0x%x\n",
sid, pcrid);
if (!enable_demux_driver()) {
WRITE_DEMUX_REG(FM_WR_DATA,
(((vid < 0x1fff)
? (vid & 0x1fff) | (VIDEO_PACKET << 13)
: 0xffff) << 16)
| ((aid < 0x1fff)
? (aid & 0x1fff) | (AUDIO_PACKET << 13)
: 0xffff));
WRITE_DEMUX_REG(FM_WR_ADDR, 0x8000);
while (READ_DEMUX_REG(FM_WR_ADDR) & 0x8000)
;
WRITE_DEMUX_REG(FM_WR_DATA,
(((sid < 0x1fff)
? (sid & 0x1fff) | (SUB_PACKET << 13)
: 0xffff) << 16)
| 0xffff);
WRITE_DEMUX_REG(FM_WR_ADDR, 0x8001);
while (READ_DEMUX_REG(FM_WR_ADDR) & 0x8000)
;
WRITE_DEMUX_REG(MAX_FM_COMP_ADDR, 1);
WRITE_DEMUX_REG(STB_INT_MASK, 0);
WRITE_DEMUX_REG(STB_INT_STATUS, 0xffff);
/* TS data path */
WRITE_DEMUX_REG(FEC_INPUT_CONTROL, 0x7000);
WRITE_DEMUX_REG(DEMUX_MEM_REQ_EN,
(1 << VIDEO_PACKET) |
(1 << AUDIO_PACKET) | (1 << SUB_PACKET));
WRITE_DEMUX_REG(DEMUX_ENDIAN,
(7 << OTHER_ENDIAN) |
(7 << BYPASS_ENDIAN) | (0 << SECTION_ENDIAN));
WRITE_DEMUX_REG(TS_HIU_CTL, 1 << USE_HI_BSF_INTERFACE);
WRITE_DEMUX_REG(TS_FILE_CONFIG,
(demux_skipbyte << 16) |
(6 << DES_OUT_DLY) |
(3 << TRANSPORT_SCRAMBLING_CONTROL_ODD) |
(1 << TS_HIU_ENABLE) | (4 << FEC_FILE_CLK_DIV));
/* enable TS demux */
WRITE_DEMUX_REG(DEMUX_CONTROL,
(1 << STB_DEMUX_ENABLE) |
(1 << KEEP_DUPLICATE_PACKAGE));
}
if (fetchbuf == 0) {
pr_info("%s: no fetchbuf\n", __func__);
return -ENOMEM;
}
/* hook stream buffer with PARSER */
if (has_hevc_vdec() && is_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);
/* 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);
}
} else {
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);
WRITE_VREG(VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
CLEAR_VREG_MASK(VLD_MEM_VIFIFO_BUF_CNTL,
MEM_BUFCTRL_INIT);
/* set vififo_vbuf_rp_sel=>vdec */
if (has_hevc_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);
}
}
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_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_AIU_REG(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
CLEAR_AIU_REG_MASK(AIU_MEM_AIFIFO_BUF_CNTL, MEM_BUFCTRL_INIT);
if (!enable_demux_driver() || ((sid > 0) && (sid < 0x1fff))) {
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 MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
if (vid != 0xffff) {
if (has_hevc_vdec())
r = pts_start((is_hevc) ? PTS_TYPE_HEVC : PTS_TYPE_VIDEO);
else
/* #endif */
r = pts_start(PTS_TYPE_VIDEO);
if ((r < 0) && (r != -EBUSY)) {
pr_info("Video pts start failed.(%d)\n", r);
goto err1;
}
}
if (aid != 0xffff) {
r = pts_start(PTS_TYPE_AUDIO);
if ((r < 0) && (r != -EBUSY)) {
pr_info("Audio pts start failed.(%d)\n", r);
goto err2;
}
}
/*TODO irq */
r = vdec_request_irq(PARSER_IRQ, parser_isr,
"tsdemux-fetch", (void *)tsdemux_fetch_id);
if (r)
goto err3;
WRITE_PARSER_REG(PARSER_INT_STATUS, 0xffff);
WRITE_PARSER_REG(PARSER_INT_ENABLE,
PARSER_INTSTAT_FETCH_CMD << PARSER_INT_HOST_EN_BIT);
WRITE_PARSER_REG(PARSER_VIDEO_HOLE, 0x400);
WRITE_PARSER_REG(PARSER_AUDIO_HOLE, 0x400);
discontinued_counter = 0;
if (!enable_demux_driver()) {
/*TODO irq */
r = vdec_request_irq(DEMUX_IRQ, tsdemux_isr,
"tsdemux-irq", (void *)tsdemux_irq_id);
WRITE_DEMUX_REG(STB_INT_MASK, (1 << SUB_PES_READY)
| (1 << NEW_PDTS_READY)
| (1 << DIS_CONTINUITY_PACKET));
if (r)
goto err4;
} else {
tsdemux_config();
tsdemux_request_irq(tsdemux_isr, (void *)tsdemux_irq_id);
if (vid < 0x1FFF) {
curr_vid_id = vid;
tsdemux_set_vid(vid);
pcrvideo_valid = 1;
}
if (aid < 0x1FFF) {
curr_aud_id = aid;
tsdemux_set_aid(aid);
pcraudio_valid = 1;
}
if (sid < 0x1FFF) {
curr_sub_id = sid;
tsdemux_set_sid(sid);
}
curr_pcr_id = pcrid;
pcrscr_valid = reset_pcr_regs();
if ((pcrid < 0x1FFF) && (pcrid != vid) && (pcrid != aid)
&& (pcrid != sid))
tsdemux_set_pcrid(pcrid);
}
first_pcr = 0;
return 0;
err4:
/*TODO irq */
if (!enable_demux_driver())
vdec_free_irq(PARSER_IRQ, (void *)tsdemux_fetch_id);
err3:
pts_stop(PTS_TYPE_AUDIO);
err2:
/* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8 */
if (has_hevc_vdec())
pts_stop((is_hevc) ? PTS_TYPE_HEVC : PTS_TYPE_VIDEO);
else
/* #endif */
pts_stop(PTS_TYPE_VIDEO);
err1:
pr_info("TS Demux init failed.\n");
return -ENOENT;
}
void tsdemux_release(void)
{
pcrscr_valid = 0;
first_pcr = 0;
pcr_init_flag = 0;
WRITE_PARSER_REG(PARSER_INT_ENABLE, 0);
WRITE_PARSER_REG(PARSER_VIDEO_HOLE, 0);
WRITE_PARSER_REG(PARSER_AUDIO_HOLE, 0);
/*TODO irq */
vdec_free_irq(PARSER_IRQ, (void *)tsdemux_fetch_id);
if (!enable_demux_driver()) {
WRITE_DEMUX_REG(STB_INT_MASK, 0);
/*TODO irq */
vdec_free_irq(DEMUX_IRQ, (void *)tsdemux_irq_id);
} else {
tsdemux_set_aid(0xffff);
tsdemux_set_vid(0xffff);
tsdemux_set_sid(0xffff);
tsdemux_set_pcrid(0xffff);
tsdemux_free_irq();
curr_vid_id = 0xffff;
curr_aud_id = 0xffff;
curr_sub_id = 0xffff;
curr_pcr_id = 0xffff;
curr_pcr_num = 0xffff;
}
pts_stop(PTS_TYPE_VIDEO);
pts_stop(PTS_TYPE_AUDIO);
WRITE_RESET_REG(RESET1_REGISTER, RESET_PARSER);
#ifdef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
SET_PARSER_REG_MASK(PARSER_ES_CONTROL, ES_VID_MAN_RD_PTR);
WRITE_PARSER_REG(PARSER_VIDEO_WP, 0);
WRITE_PARSER_REG(PARSER_VIDEO_RP, 0);
#endif
if (enable_demux_driver())
tsdemux_reset();
/* #if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON6 */
/*TODO clk */
/*
*switch_mod_gate_by_type(MOD_DEMUX, 0);
*/
/* #endif */
amports_switch_gate("demux", 0);
}
EXPORT_SYMBOL(tsdemux_release);
static int limited_delay_check(struct file *file,
struct stream_buf_s *vbuf,
struct stream_buf_s *abuf,
const char __user *buf, size_t count)
{
struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
struct stream_port_s *port = priv->port;
int write_size;
if (!((port->flag & PORT_FLAG_VID) &&
(port->flag & PORT_FLAG_AID))) {
struct stream_buf_s *buf =
(port->flag & PORT_FLAG_VID) ? vbuf : abuf;
return min_t(int, count, stbuf_space(buf));
}
if (vbuf->max_buffer_delay_ms > 0 && abuf->max_buffer_delay_ms > 0 &&
stbuf_level(vbuf) > 1024 && stbuf_level(abuf) > 256) {
int vdelay =
calculation_stream_delayed_ms(PTS_TYPE_VIDEO,
NULL, NULL);
int adelay =
calculation_stream_delayed_ms(PTS_TYPE_AUDIO,
NULL, NULL);
/*max wait 100ms,if timeout,try again top level. */
int maxretry = 10;
/*too big delay,do wait now. */
/*if noblock mode,don't do wait. */
if (!(file->f_flags & O_NONBLOCK)) {
while (vdelay > vbuf->max_buffer_delay_ms
&& adelay > abuf->max_buffer_delay_ms
&& maxretry-- > 0) {
msleep(20);
vdelay =
calculation_stream_delayed_ms
(PTS_TYPE_VIDEO, NULL, NULL);
adelay =
calculation_stream_delayed_ms
(PTS_TYPE_AUDIO, NULL, NULL);
}
}
if (vdelay > vbuf->max_buffer_delay_ms
&& adelay > abuf->max_buffer_delay_ms)
return 0;
}
write_size = min(stbuf_space(vbuf), stbuf_space(abuf));
write_size = min_t(int, count, write_size);
return write_size;
}
ssize_t drm_tswrite(struct file *file,
struct stream_buf_s *vbuf,
struct stream_buf_s *abuf,
const char __user *buf, size_t count)
{
s32 r;
u32 realcount = count;
u32 havewritebytes = 0;
struct drm_info tmpmm;
struct drm_info *drm = &tmpmm;
u32 res = 0;
int isphybuf = 0;
unsigned long realbuf;
struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
struct stream_port_s *port = priv->port;
size_t wait_size, write_size;
if (buf == NULL || count == 0)
return -EINVAL;
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_level == DRM_LEVEL1) {
/* buf only has drminfo not have esdata; */
if (drm->drm_pktsize <= MAX_DRM_PACKAGE_SIZE)
realcount = drm->drm_pktsize;
else {
pr_err("drm package size is error, size is %u\n", drm->drm_pktsize);
return -EINVAL;
}
realbuf = drm->drm_phy;
isphybuf = 1;
} else
realbuf = (unsigned long)buf;
/* pr_info("drm->drm_flag = 0x%x,realcount = %d , buf = 0x%x ",*/
/*drm->drm_flag,realcount, buf); */
count = realcount;
while (count > 0) {
if ((stbuf_space(vbuf) < count) ||
(stbuf_space(abuf) < count)) {
if (file->f_flags & O_NONBLOCK) {
int v_stbuf_space = stbuf_space(vbuf);
int a_stbuf_space = stbuf_space(abuf);
write_size = min(v_stbuf_space, a_stbuf_space);
/*have 188 bytes,write now., */
if (write_size <= 188)
return -EAGAIN;
} else {
wait_size =
min(stbuf_canusesize(vbuf) / 8,
stbuf_canusesize(abuf) / 4);
if ((port->flag & PORT_FLAG_VID)
&& (stbuf_space(vbuf) < wait_size)) {
r = stbuf_wait_space(vbuf, wait_size);
if (r < 0) {
if (r != -EAGAIN)
pr_info
("write no space--- ");
if (r != -EAGAIN)
pr_info
("no space,%d--%d,r-%d\n",
stbuf_space(vbuf),
stbuf_space(abuf), r);
return r;
}
}
if ((port->flag & PORT_FLAG_AID)
&& (stbuf_space(abuf) < wait_size)) {
r = stbuf_wait_space(abuf, wait_size);
if (r < 0) {
pr_info
("write no stbuf_wait_space--");
pr_info
("no space,%d--%d,r-%d\n",
stbuf_space(vbuf),
stbuf_space(abuf), r);
return r;
}
}
}
}
if ((port->flag & PORT_FLAG_VID) &&
(port->flag & PORT_FLAG_AID)) {
write_size = min(stbuf_space(vbuf), stbuf_space(abuf));
write_size = min(count, write_size);
} else {
struct stream_buf_s *buf =
(port->flag & PORT_FLAG_VID) ? vbuf : abuf;
write_size = min_t(int, count, stbuf_space(buf));
}
/* pr_info("write_size = %d,count = %d,\n",*/
/*write_size, count); */
if (write_size > 0) {
r = _tsdemux_write((const char __user *)realbuf + havewritebytes,
write_size, isphybuf);
if (r < 0) {
if (r != -EAGAIN)
pr_info
("vspace %d--aspace %d,r-%d\n",
stbuf_space(vbuf),
stbuf_space(abuf), r);
return r;
}
}
else
return -EAGAIN;
havewritebytes += r;
/* pr_info("havewritebytes = %d, r = %d,\n",*/
/*havewritebytes, r); */
if (havewritebytes == realcount)
break; /* write ok; */
else if (havewritebytes > realcount)
pr_info(" error ! write too much havewritebytes = %u, r = %u\n",
(u32)havewritebytes,(u32)realcount);
count -= r;
}
return havewritebytes;
}
ssize_t tsdemux_write(struct file *file,
struct stream_buf_s *vbuf,
struct stream_buf_s *abuf,
const char __user *buf, size_t count)
{
s32 r;
struct port_priv_s *priv = (struct port_priv_s *)file->private_data;
struct stream_port_s *port = priv->port;
size_t wait_size, write_size;
if ((stbuf_space(vbuf) < count) || (stbuf_space(abuf) < count)) {
if (file->f_flags & O_NONBLOCK) {
write_size = min(stbuf_space(vbuf), stbuf_space(abuf));
if (write_size <= 188) /*have 188 bytes,write now., */
return -EAGAIN;
} else {
wait_size =
min(stbuf_canusesize(vbuf) / 8,
stbuf_canusesize(abuf) / 4);
if ((port->flag & PORT_FLAG_VID)
&& (stbuf_space(vbuf) < wait_size)) {
r = stbuf_wait_space(vbuf, wait_size);
if (r < 0) {
/* pr_info("write no space--- ");
* pr_info("no space,%d--%d,r-%d\n",
* stbuf_space(vbuf),
* stbuf_space(abuf),r);
*/
return r;
}
}
if ((port->flag & PORT_FLAG_AID)
&& (stbuf_space(abuf) < wait_size)) {
r = stbuf_wait_space(abuf, wait_size);
if (r < 0) {
/* pr_info("write no stbuf_wait_space")'
* pr_info{"---no space,%d--%d,r-%d\n",
* stbuf_space(vbuf),
* stbuf_space(abuf),r);
*/
return r;
}
}
}
}
vbuf->last_write_jiffies64 = jiffies_64;
abuf->last_write_jiffies64 = jiffies_64;
write_size = limited_delay_check(file, vbuf, abuf, buf, count);
if (write_size > 0)
return _tsdemux_write(buf, write_size, 0);
else
return -EAGAIN;
}
int get_discontinue_counter(void)
{
return discontinued_counter;
}
EXPORT_SYMBOL(get_discontinue_counter);
static ssize_t discontinue_counter_show(struct class *class,
struct class_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", discontinued_counter);
}
static CLASS_ATTR_RO(discontinue_counter);
static struct attribute *tsdemux_class_attrs[] = {
&class_attr_discontinue_counter.attr,
NULL
};
ATTRIBUTE_GROUPS(tsdemux_class);
static struct class tsdemux_class = {
.name = "tsdemux",
.class_groups = tsdemux_class_groups,
};
int tsdemux_class_register(void)
{
int r = class_register(&tsdemux_class);
if (r < 0)
pr_info("register tsdemux class error!\n");
discontinued_counter = 0;
return r;
}
void tsdemux_class_unregister(void)
{
class_unregister(&tsdemux_class);
}
void tsdemux_change_avid(unsigned int vid, unsigned int aid)
{
if (!enable_demux_driver()) {
WRITE_DEMUX_REG(FM_WR_DATA,
(((vid & 0x1fff) | (VIDEO_PACKET << 13)) << 16)
| ((aid & 0x1fff) | (AUDIO_PACKET << 13)));
WRITE_DEMUX_REG(FM_WR_ADDR, 0x8000);
while (READ_DEMUX_REG(FM_WR_ADDR) & 0x8000)
;
} else {
if (curr_vid_id != vid) {
tsdemux_set_vid(vid);
curr_vid_id = vid;
}
if (curr_aud_id != aid) {
tsdemux_set_aid(aid);
curr_aud_id = aid;
}
reset_pcr_regs();
}
}
void tsdemux_change_sid(unsigned int sid)
{
if (!enable_demux_driver()) {
WRITE_DEMUX_REG(FM_WR_DATA,
(((sid & 0x1fff) | (SUB_PACKET << 13)) << 16)
| 0xffff);
WRITE_DEMUX_REG(FM_WR_ADDR, 0x8001);
while (READ_DEMUX_REG(FM_WR_ADDR) & 0x8000)
;
} else {
curr_sub_id = sid;
tsdemux_set_sid(sid);
reset_pcr_regs();
}
}
void tsdemux_audio_reset(void)
{
ulong flags;
unsigned long xflags = 0;
spin_lock_irqsave(&demux_ops_lock, flags);
if (demux_ops && demux_ops->hw_dmx_lock)
xflags = demux_ops->hw_dmx_lock(xflags);
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);
if (demux_ops && demux_ops->hw_dmx_unlock)
demux_ops->hw_dmx_unlock(xflags);
spin_unlock_irqrestore(&demux_ops_lock, flags);
if (reset_demux_enable == 1)
tsdemux_reset();
}
void tsdemux_sub_reset(void)
{
ulong flags;
u32 parser_sub_start_ptr;
u32 parser_sub_end_ptr;
unsigned long xflags = 0;
spin_lock_irqsave(&demux_ops_lock, flags);
if (demux_ops && demux_ops->hw_dmx_lock)
xflags = demux_ops->hw_dmx_lock(xflags);
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);
if (demux_ops && demux_ops->hw_dmx_unlock)
demux_ops->hw_dmx_unlock(xflags);
spin_unlock_irqrestore(&demux_ops_lock, flags);
}
void tsdemux_set_skipbyte(int skipbyte)
{
if (!enable_demux_driver())
demux_skipbyte = skipbyte;
else
tsdemux_set_skip_byte(skipbyte);
}
void tsdemux_set_demux(int dev)
{
if (enable_demux_driver()) {
unsigned long flags;
int r = 0;
spin_lock_irqsave(&demux_ops_lock, flags);
if (demux_ops && demux_ops->set_demux)
r = demux_ops->set_demux(dev);
spin_unlock_irqrestore(&demux_ops_lock, flags);
}
}
u32 tsdemux_pcrscr_get(void)
{
u32 pcr = 0;
if (pcrscr_valid == 0)
return 0;
if (READ_DEMUX_REG(TS_HIU_CTL_2) & 0x80)
pcr = READ_DEMUX_REG(PCR_DEMUX_2);
else if (READ_DEMUX_REG(TS_HIU_CTL_3) & 0x80)
pcr = READ_DEMUX_REG(PCR_DEMUX_3);
else
pcr = READ_DEMUX_REG(PCR_DEMUX);
if (first_pcr == 0)
first_pcr = pcr;
return pcr;
}
u32 tsdemux_first_pcrscr_get(void)
{
if (pcrscr_valid == 0)
return 0;
if (first_pcr == 0) {
u32 pcr;
if (READ_DEMUX_REG(TS_HIU_CTL_2) & 0x80)
pcr = READ_DEMUX_REG(PCR_DEMUX_2);
else if (READ_DEMUX_REG(TS_HIU_CTL_3) & 0x80)
pcr = READ_DEMUX_REG(PCR_DEMUX_3);
else
pcr = READ_DEMUX_REG(PCR_DEMUX);
first_pcr = pcr;
/* pr_info("set first_pcr = 0x%x\n", pcr); */
}
return first_pcr;
}
u8 tsdemux_pcrscr_valid(void)
{
return pcrscr_valid;
}
u8 tsdemux_pcraudio_valid(void)
{
return pcraudio_valid;
}
u8 tsdemux_pcrvideo_valid(void)
{
return pcrvideo_valid;
}
void tsdemux_pcr_set(unsigned int pcr)
{
if (pcr_init_flag == 0) {
/*timestamp_pcrscr_set(pcr);
timestamp_pcrscr_enable(1);*/
pcr_init_flag = 1;
}
}
void tsdemux_tsync_func_init(void)
{
register_tsync_callbackfunc(
TSYNC_PCRSCR_VALID, (void *)(tsdemux_pcrscr_valid));
register_tsync_callbackfunc(
TSYNC_PCRSCR_GET, (void *)(tsdemux_pcrscr_get));
register_tsync_callbackfunc(
TSYNC_FIRST_PCRSCR_GET, (void *)(tsdemux_first_pcrscr_get));
register_tsync_callbackfunc(
TSYNC_PCRAUDIO_VALID, (void *)(tsdemux_pcraudio_valid));
register_tsync_callbackfunc(
TSYNC_PCRVIDEO_VALID, (void *)(tsdemux_pcrvideo_valid));
register_tsync_callbackfunc(
TSYNC_BUF_BY_BYTE, (void *)(get_buf_by_type));
register_tsync_callbackfunc(
TSYNC_STBUF_LEVEL, (void *)(stbuf_level));
register_tsync_callbackfunc(
TSYNC_STBUF_SPACE, (void *)(stbuf_space));
register_tsync_callbackfunc(
TSYNC_STBUF_SIZE, (void *)(stbuf_size));
}
static int tsparser_stbuf_init(struct stream_buf_s *stbuf,
struct vdec_s *vdec)
{
int ret = -1;
ret = stbuf_init(stbuf, vdec);
if (ret)
goto out;
ret = tsdemux_init(stbuf->pars.vid,
stbuf->pars.aid,
stbuf->pars.sid,
stbuf->pars.pcrid,
stbuf->is_hevc,
vdec);
if (ret)
goto out;
tsync_pcr_start();
stbuf->flag |= BUF_FLAG_IN_USE;
out:
return ret;
}
static void tsparser_stbuf_release(struct stream_buf_s *stbuf)
{
tsync_pcr_stop();
tsdemux_release();
stbuf_release(stbuf);
}
static struct stream_buf_ops tsparser_stbuf_ops = {
.init = tsparser_stbuf_init,
.release = tsparser_stbuf_release,
.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_tsparser_stbuf_ops(void)
{
return &tsparser_stbuf_ops;
}
EXPORT_SYMBOL(get_tsparser_stbuf_ops);