blob: 7d9d7def8085ad4363b1aa39555df3eb54adeba1 [file] [log] [blame]
/*
* Copyright (C) 2017 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Description:
*/
/*
* AMLOGIC demux driver.
*/
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/spinlock.h>
#include <linux/fcntl.h>
#include <asm/irq.h>
#include <linux/uaccess.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <asm/cacheflush.h>
#include <linux/dma-mapping.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/vmalloc.h>
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include <linux/amlogic/media/codec_mm/configs.h>
#include "../../amports/streambuf.h"
#include "c_stb_define.h"
#include "c_stb_regs_define.h"
#include "aml_dvb.h"
#include "aml_dvb_reg.h"
#include <linux/sched/signal.h>
#define ENABLE_SEC_BUFF_WATCHDOG
#define USE_AHB_MODE
#define PR_ERROR_SPEED_LIMIT
#define pr_dbg_flag(_f, _args...)\
do {\
if (debug_dmx&(_f))\
printk(_args);\
} while (0)
#define pr_dbg_irq_flag(_f, _args...)\
do {\
if (debug_irq&(_f))\
printk(_args);\
} while (0)
#define pr_dbg(args...) pr_dbg_flag(0x1, args)
#define pr_dbg_irq(args...)pr_dbg_irq_flag(0x1, args)
#define pr_dbg_irq_dvr(args...)pr_dbg_irq_flag(0x2, args)
#define pr_dbg_sf(args...) pr_dbg_flag(0x4, args)
#define pr_dbg_irq_sf(args...) pr_dbg_irq_flag(0x4, args)
#define pr_dbg_ss(args...) pr_dbg_flag(0x8, args)
#define pr_dbg_irq_ss(args...) pr_dbg_irq_flag(0x8, args)
#define pr_dbg_irq_pes(args...) pr_dbg_irq_flag(0x10, args)
#define pr_dbg_irq_sub(args...) pr_dbg_irq_flag(0x20, args)
#ifdef PR_ERROR_SPEED_LIMIT
static u32 last_pr_error_time;
#define pr_error(fmt, _args...)\
do {\
u32 diff = jiffies_to_msecs(jiffies - last_pr_error_time);\
if (!last_pr_error_time || diff > 50) {\
pr_err("DVB:" fmt, ## _args);\
last_pr_error_time = jiffies;\
} \
} while (0)
#else
#define pr_error(fmt, args...) pr_err("DVB: " fmt, ## args)
#endif
#define pr_inf(fmt, args...) printk("DVB: " fmt, ## args)
#define dump(b, l) \
do { \
int i; \
printk("dump: "); \
for (i = 0; i < (l); i++) {\
if (!(i&0xf)) \
printk("\n\t"); \
printk("%02x ", *(((unsigned char *)(b))+i)); \
} \
printk("\n"); \
} while (0)
MODULE_PARM_DESC(debug_dmx, "\n\t\t Enable demux debug information");
static int debug_dmx;
module_param(debug_dmx, int, 0644);
MODULE_PARM_DESC(debug_irq, "\n\t\t Enable demux IRQ debug information");
static int debug_irq;
module_param(debug_irq, int, 0644);
MODULE_PARM_DESC(disable_dsc, "\n\t\t Disable discrambler");
static int disable_dsc;
module_param(disable_dsc, int, 0644);
MODULE_PARM_DESC(enable_sec_monitor, "\n\t\t Enable sec monitor default is enable");
static int enable_sec_monitor = 2;
module_param(enable_sec_monitor, int, 0644);
/*For old version kernel */
#ifndef MESON_CPU_MAJOR_ID_GXL
#define MESON_CPU_MAJOR_ID_GXL 0x21
#endif
static int npidtypes = CHANNEL_COUNT;
#define MOD_PARAM_DECLARE_CHANPIDS_TYPES(_dmx) \
MODULE_PARM_DESC(debug_dmx##_dmx##_chanpids_types, "\n\t\t pids types of dmx channels"); \
static short debug_dmx##_dmx##_chanpids_types[CHANNEL_COUNT] = \
{[0 ... (CHANNEL_COUNT - 1)] = -1}; \
module_param_array(debug_dmx##_dmx##_chanpids_types, short, &npidtypes, 0444)
MOD_PARAM_DECLARE_CHANPIDS_TYPES(0);
MOD_PARAM_DECLARE_CHANPIDS_TYPES(1);
MOD_PARAM_DECLARE_CHANPIDS_TYPES(2);
#define set_debug_dmx_chanpids_types(_dmx, _idx, _type)\
do { \
if ((_dmx) == 0) \
debug_dmx0_chanpids_types[(_idx)] = (_type); \
else if ((_dmx) == 1) \
debug_dmx1_chanpids_types[(_idx)] = (_type); \
else if ((_dmx) == 2) \
debug_dmx2_chanpids_types[(_idx)] = (_type); \
} while (0)
static int npids = CHANNEL_COUNT;
#define MOD_PARAM_DECLARE_CHANPIDS(_dmx) \
MODULE_PARM_DESC(debug_dmx##_dmx##_chanpids, "\n\t\t pids of dmx channels"); \
static short debug_dmx##_dmx##_chanpids[CHANNEL_COUNT] = \
{[0 ... (CHANNEL_COUNT - 1)] = -1}; \
module_param_array(debug_dmx##_dmx##_chanpids, short, &npids, 0444)
#define CIPLUS_OUTPUT_AUTO 8
static int ciplus_out_sel = CIPLUS_OUTPUT_AUTO;
static int ciplus_out_auto_mode = 1;
static u32 ciplus = 0;
#define CIPLUS_OUT_SEL 28
#define CIPLUS_IN_SEL 26
MOD_PARAM_DECLARE_CHANPIDS(0);
MOD_PARAM_DECLARE_CHANPIDS(1);
MOD_PARAM_DECLARE_CHANPIDS(2);
#define set_debug_dmx_chanpids(_dmx, _idx, _pid)\
do { \
if ((_dmx) == 0) \
debug_dmx0_chanpids[(_idx)] = (_pid); \
else if ((_dmx) == 1) \
debug_dmx1_chanpids[(_idx)] = (_pid); \
else if ((_dmx) == 2) \
debug_dmx2_chanpids[(_idx)] = (_pid); \
if (_pid == -1) \
set_debug_dmx_chanpids_types(_dmx, _idx, -1); \
} while (0)
MODULE_PARM_DESC(debug_sf_user, "\n\t\t only for sf mode check");
static int debug_sf_user;
module_param(debug_sf_user, int, 0444);
MODULE_PARM_DESC(force_sec_sf, "\n\t\t force sf mode for sec filter");
static int force_sec_sf;
module_param(force_sec_sf, int, 0644);
MODULE_PARM_DESC(force_pes_sf, "\n\t\t force sf mode for pes filter");
static int force_pes_sf;
module_param(force_pes_sf, int, 0644);
MODULE_PARM_DESC(use_of_sop, "\n\t\t Enable use of sop input");
static int use_of_sop;
module_param(use_of_sop, int, 0644);
/*
As the default value of unused channel's PID_TYPE is 0x7,
if we use PID_TYPE(RECORDER_STREAM:0x7) for recording channel,
the data with the pid which assigned in unused channel's setting will be captured also.
To avoid the high bitrate(exists) of this pid's data flood the buffers in the data path,
which will causes the record data corruption, and bad picture decoded.
*/
MODULE_PARM_DESC(g_chan_def_pid, "\n\t\t default pid for unused channel");
static int g_chan_def_pid = 0x1FFE;
module_param(g_chan_def_pid, int, 0644);
/*#define CIPLUS_KEY0 0x16f8
#define CIPLUS_KEY1 0x16f9
#define CIPLUS_KEY2 0x16fa
#define CIPLUS_KEY3 0x16fb
#define CIPLUS_KEY_WR 0x16fc
#define CIPLUS_CONFIG 0x16fd
#define CIPLUS_ENDIAN 0x16fe*/
static u32 old_stb_top_config;
static u32 old_fec_input_control;
static int have_old_stb_top_config = 1;
static int have_old_fec_input_control = 1;
static long pes_off_pre[DMX_DEV_COUNT];
static void
dmx_write_reg(int r, u32 v)
{
u32 oldv, mask;
if (disable_dsc) {
if (r == STB_TOP_CONFIG) {
if (have_old_stb_top_config) {
oldv = old_stb_top_config;
have_old_stb_top_config = 0;
} else {
oldv = READ_MPEG_REG(STB_TOP_CONFIG);
}
mask = (1<<7)|(1<<15)|(3<<26)|(7<<28);
v &= ~mask;
v |= (oldv & mask);
} else if (r == FEC_INPUT_CONTROL) {
if (have_old_fec_input_control) {
oldv = old_fec_input_control;
have_old_fec_input_control = 0;
} else {
oldv = READ_MPEG_REG(FEC_INPUT_CONTROL);
}
mask = (1<<15);
v &= ~mask;
v |= (oldv & mask);
} else if ((r == RESET1_REGISTER) || (r == RESET3_REGISTER)) {
if (!have_old_stb_top_config) {
have_old_stb_top_config = 1;
old_stb_top_config =
READ_MPEG_REG(STB_TOP_CONFIG);
}
if (!have_old_fec_input_control) {
have_old_fec_input_control = 1;
old_fec_input_control =
READ_MPEG_REG(FEC_INPUT_CONTROL);
}
} else if ((r == TS_PL_PID_INDEX) || (r == TS_PL_PID_DATA)
|| (r == COMM_DESC_KEY0)
|| (r == COMM_DESC_KEY1)
|| (r == COMM_DESC_KEY_RW)
|| (r == CIPLUS_KEY0)
|| (r == CIPLUS_KEY1)
|| (r == CIPLUS_KEY2)
|| (r == CIPLUS_KEY3)
|| (r == CIPLUS_KEY_WR)
|| (r == CIPLUS_CONFIG)
|| (r == CIPLUS_ENDIAN)) {
return;
}
}
WRITE_MPEG_REG(r, v);
}
#undef WRITE_MPEG_REG
#define WRITE_MPEG_REG(r, v) dmx_write_reg(r, v)
#define DMX_READ_REG(i, r)\
((i)?((i == 1)?READ_MPEG_REG(r##_2) :\
READ_MPEG_REG(r##_3)) : READ_MPEG_REG(r))
#define DMX_WRITE_REG(i, r, d)\
do {\
if (i == 1) {\
WRITE_MPEG_REG(r##_2, d);\
} else if (i == 2) {\
WRITE_MPEG_REG(r##_3, d);\
} \
else {\
WRITE_MPEG_REG(r, d);\
} \
} while (0)
#define READ_PERI_REG READ_CBUS_REG
#define WRITE_PERI_REG WRITE_CBUS_REG
#define READ_ASYNC_FIFO_REG(i, r)\
((i) ? ((i-1)?READ_PERI_REG(ASYNC_FIFO1_##r):\
READ_PERI_REG(ASYNC_FIFO2_##r)) : READ_PERI_REG(ASYNC_FIFO_##r))
#define WRITE_ASYNC_FIFO_REG(i, r, d)\
do {\
if (i == 2) {\
WRITE_PERI_REG(ASYNC_FIFO1_##r, d);\
} else if (i == 0) {\
WRITE_PERI_REG(ASYNC_FIFO_##r, d);\
} else {\
WRITE_PERI_REG(ASYNC_FIFO2_##r, d);\
} \
} while (0)
#define CLEAR_ASYNC_FIFO_REG_MASK(i, reg, mask) \
WRITE_ASYNC_FIFO_REG(i, reg, \
(READ_ASYNC_FIFO_REG(i, reg)&(~(mask))))
#define DVR_FEED(f) \
((f) && ((f)->type == DMX_TYPE_TS) && \
(((f)->ts_type & (TS_PACKET | TS_DEMUX)) == TS_PACKET))
#define MOD_PARAM_DECLARE_CHANREC(_dmx) \
MODULE_PARM_DESC(dmx##_dmx##_chanrec_enable, \
"\n\t\t record by channel, one time use in the beginning"); \
static int dmx##_dmx##_chanrec_enable; \
module_param(dmx##_dmx##_chanrec_enable, int, 0644); \
MODULE_PARM_DESC(dmx##_dmx##_chanrec, "\n\t\t record channels bits"); \
static int dmx##_dmx##_chanrec; \
module_param(dmx##_dmx##_chanrec, int, 0644)
MOD_PARAM_DECLARE_CHANREC(0);
MOD_PARAM_DECLARE_CHANREC(1);
MOD_PARAM_DECLARE_CHANREC(2);
#define MOD_PARAM_DECLARE_CHANPROC(_dmx) \
MODULE_PARM_DESC(dmx##_dmx##_chanproc_enable, "channel further processing"); \
static int dmx##_dmx##_chanproc_enable; \
module_param(dmx##_dmx##_chanproc_enable, int, 0644); \
MODULE_PARM_DESC(dmx##_dmx##_chanproc, "further process channels bits"); \
static int dmx##_dmx##_chanproc; \
module_param(dmx##_dmx##_chanproc, int, 0644)
MOD_PARAM_DECLARE_CHANPROC(0);
MOD_PARAM_DECLARE_CHANPROC(1);
MOD_PARAM_DECLARE_CHANPROC(2);
#define DMX_CH_OP_CHANREC 0
#define DMX_CH_OP_CHANPROC 1
static inline int _setbit(int v, int b) { return v|(1<<b); }
static inline int _clrbit(int v, int b) { return v&~(1<<b); }
static inline int _set(int v, int b) { return b; }
static int dsc_set_csa_key(struct aml_dsc_channel *ch, int flags,
enum ca_cw_type type, u8 *key);
static int dsc_set_aes_des_sm4_key(struct aml_dsc_channel *ch, int flags,
enum ca_cw_type type, u8 *key);
static void aml_ci_plus_disable(void);
static void am_ci_plus_set_output(struct aml_dsc_channel *ch);
static int set_subtitle_pes_buffer(struct aml_dmx *dmx);
static void dmxn_op_chan(int dmx, int ch, int(*op)(int, int), int ch_op)
{
int enable_0, enable_1, enable_2;
int *set_0, *set_1, *set_2;
int reg;
if (ch_op == DMX_CH_OP_CHANREC) {
enable_0 = dmx0_chanrec_enable;
enable_1 = dmx1_chanrec_enable;
enable_2 = dmx2_chanrec_enable;
set_0 = &dmx0_chanrec;
set_1 = &dmx1_chanrec;
set_2 = &dmx2_chanrec;
reg = DEMUX_CHAN_RECORD_EN;
} else if (ch_op == DMX_CH_OP_CHANPROC) {
enable_0 = dmx0_chanproc_enable;
enable_1 = dmx1_chanproc_enable;
enable_2 = dmx2_chanproc_enable;
set_0 = &dmx0_chanproc;
set_1 = &dmx1_chanproc;
set_2 = &dmx2_chanproc;
reg = DEMUX_CHAN_PROCESS_EN;
} else {
return;
}
if (dmx == 0) {
if (enable_0) {
*set_0 = op(*set_0, ch);
WRITE_MPEG_REG(reg+DEMUX_1_OFFSET, *set_0);
}
} else if (dmx == 1) {
if (enable_1) {
*set_1 = op(*set_1, ch);
WRITE_MPEG_REG(reg+DEMUX_2_OFFSET, *set_1);
}
} else if (dmx == 2) {
if (enable_2) {
*set_2 = op(*set_2, ch);
WRITE_MPEG_REG(reg+DEMUX_3_OFFSET, *set_2);
}
}
}
#define dmx_add_recchan(_dmx, _chid) \
do { \
pr_dbg("dmx[%d]_add_recchan[%d]\n", _dmx, _chid); \
dmxn_op_chan(_dmx, _chid, _setbit, DMX_CH_OP_CHANREC); \
} while (0)
#define dmx_rm_recchan(_dmx, _chid) \
do { \
pr_dbg("dmx[%d]_rm_recchan[%ld]\n", _dmx, _chid); \
dmxn_op_chan(_dmx, _chid, _clrbit, DMX_CH_OP_CHANREC); \
} while (0)
#define dmx_set_recchan(_dmx, _chs) \
do { \
pr_dbg("dmx[%d]_set_recchan[%d]\n", _dmx, _chs); \
dmxn_op_chan(_dmx, _chs, _set, DMX_CH_OP_CHANREC); \
} while (0)
#define dmx_add_procchan(_dmx, _chid) \
do { \
pr_dbg("dmx[%d]_add_procchan[%d]\n", _dmx, _chid); \
dmxn_op_chan(_dmx, _chid, _setbit, DMX_CH_OP_CHANPROC); \
} while (0)
#define dmx_rm_procchan(_dmx, _chid) \
do { \
pr_dbg("dmx[%d]_rm_procchan[%ld]\n", _dmx, _chid); \
dmxn_op_chan(_dmx, _chid, _clrbit, DMX_CH_OP_CHANPROC); \
} while (0)
#define dmx_set_procchan(_dmx, _chs) \
do { \
pr_dbg("dmx[%d]_set_procchan[%d]\n", _dmx, _chs); \
dmxn_op_chan(_dmx, _chs, _set, DMX_CH_OP_CHANPROC); \
} while (0)
#define NO_SUB
#define SUB_BUF_DMX
#define SUB_PARSER
#ifndef SUB_BUF_DMX
#undef SUB_PARSER
#endif
#define SUB_BUF_SHARED
#define PES_BUF_SHARED
#define SYS_CHAN_COUNT (4)
#define SEC_GRP_LEN_0 (0xc)
#define SEC_GRP_LEN_1 (0xc)
#define SEC_GRP_LEN_2 (0xc)
#define SEC_GRP_LEN_3 (0xc)
#define LARGE_SEC_BUFF_MASK 0xFFFFFFFF
#define LARGE_SEC_BUFF_COUNT 32
#define WATCHDOG_TIMER 250
#define ASYNCFIFO_BUFFER_SIZE_DEFAULT (512*1024)
#define DEMUX_INT_MASK\
((0<<(AUDIO_SPLICING_POINT)) |\
(0<<(VIDEO_SPLICING_POINT)) |\
(1<<(OTHER_PES_READY)) |\
(1<<(PCR_READY)) |\
(1<<(SUB_PES_READY)) |\
(1<<(SECTION_BUFFER_READY)) |\
(0<<(OM_CMD_READ_PENDING)) |\
(1<<(TS_ERROR_PIN)) |\
(1<<(NEW_PDTS_READY)) |\
(0<<(DUPLICATED_PACKET)) |\
(0<<(DIS_CONTINUITY_PACKET)))
#define TS_SRC_MAX 3
/*Reset the demux device*/
#define RESET_DEMUX2 (1<<15)
#define RESET_DEMUX1 (1<<14)
#define RESET_DEMUX0 (1<<13)
#define RESET_S2P1 (1<<12)
#define RESET_S2P0 (1<<11)
#define RESET_DES (1<<10)
#define RESET_TOP (1<<9)
static int dmx_remove_feed(struct aml_dmx *dmx, struct dvb_demux_feed *feed);
static void reset_async_fifos(struct aml_dvb *dvb);
static int dmx_add_feed(struct aml_dmx *dmx, struct dvb_demux_feed *feed);
static int dmx_smallsec_set(struct aml_smallsec *ss, int enable, int bufsize,
int force);
static int dmx_timeout_set(struct aml_dmxtimeout *dto, int enable,
int timeout, int ch_dis, int nomatch,
int force);
/*Audio & Video PTS value*/
static u32 video_pts = 0;
static u32 audio_pts = 0;
static u32 video_pts_bit32 = 0;
static u32 audio_pts_bit32 = 0;
static u32 first_video_pts = 0;
static u32 first_audio_pts = 0;
static int demux_skipbyte;
static int tsfile_clkdiv = 5;
static int asyncfifo_buf_len = ASYNCFIFO_BUFFER_SIZE_DEFAULT;
#define SF_DMX_ID 2
#define SF_AFIFO_ID 1
#define sf_dmx_sf(_dmx) \
(((_dmx)->id == SF_DMX_ID) \
&& ((struct aml_dvb *)(_dmx)->demux.priv)->swfilter.user)
#define sf_afifo_sf(_afifo) \
(((_afifo)->id == SF_AFIFO_ID) && (_afifo)->dvb->swfilter.user)
#define dmx_get_dev(dmx) (((struct aml_dvb *)((dmx)->demux.priv))->dev)
#define asyncfifo_get_dev(afifo) ((afifo)->dvb->dev)
int dmx_phyreg_access(unsigned int reg, unsigned int writeval,
unsigned int *readval)
{
void __iomem *vaddr;
reg = round_down(reg, 0x3);
vaddr = ioremap(reg, 0x4);
if (!vaddr)
return -ENOMEM;
if (readval)
*readval = readl_relaxed(vaddr);
else
writel_relaxed(writeval, vaddr);
iounmap(vaddr);
return 0;
}
/*Section buffer watchdog*/
static void section_buffer_watchdog_func(struct timer_list * timer)
{
struct aml_dvb *dvb = from_timer(dvb,timer,watchdog_timer);
struct aml_dmx *dmx;
u32 section_busy32 = 0, om_cmd_status32 = 0,
demux_channel_activity32 = 0;
u16 demux_int_status1 = 0;
u32 device_no = 0;
u32 filter_number = 0;
u32 i = 0;
unsigned long flags;
spin_lock_irqsave(&dvb->slock, flags);
for (device_no = 0; device_no < DMX_DEV_COUNT; device_no++) {
dmx = &dvb->dmx[device_no];
if (dvb->dmx_watchdog_disable[device_no])
continue;
if (!dmx->init)
continue;
om_cmd_status32 =
DMX_READ_REG(device_no, OM_CMD_STATUS);
demux_channel_activity32 =
DMX_READ_REG(device_no, DEMUX_CHANNEL_ACTIVITY);
section_busy32 =
DMX_READ_REG(device_no, SEC_BUFF_BUSY);
if (om_cmd_status32 & 0x8fc2) {
/* bit 15:12 -- om_cmd_count (read only) */
/* bit 11:9 -- overflow_count */
/* bit 11:9 -- om_cmd_wr_ptr(read only) */
/* bit 8:6 -- om_overwrite_count */
/* bit 8:6 -- om_cmd_rd_ptr(read only) */
/* bit 5:3 -- type_stb_om_w_rd(read only) */
/* bit 2 -- unit_start_stb_om_w_rd(read only) */
/* bit 1 -- om_cmd_overflow(read only) */
/* bit 0 -- om_cmd_pending(read) */
/* bit 0 -- om_cmd_read_finished(write) */
/*BUG: If the recoder is running, return */
if (!dmx->record) {
/* OM status is wrong */
dmx->om_status_error_count++;
pr_dbg("demux om status \n"
"%04x\t%03x\t%03x\t%03x\t%01x\t%01x\t"
"%x\t%x\tdmx%d:status:0x%xerr_cnt:%d-%d\n",
(om_cmd_status32 >> 12) & 0xf,
(om_cmd_status32 >> 9) & 0x7,
(om_cmd_status32 >> 6) & 0x7,
(om_cmd_status32 >> 3) & 0x7,
(om_cmd_status32 >> 2) & 0x1,
(om_cmd_status32 >> 1) & 0x1,
demux_channel_activity32, section_busy32,
dmx->id, om_cmd_status32, dmx->om_status_error_count, enable_sec_monitor);
if (enable_sec_monitor &&
dmx->om_status_error_count > enable_sec_monitor) {
/*Reset the demux */
dmx_reset_dmx_hw_ex_unlock(dvb, dmx, 0);
/* Reset the error count */
dmx->om_status_error_count = 0;
goto end;
}
}
} else {
/* OM status is correct, reset the error count */
dmx->om_status_error_count = 0;
}
section_busy32 =
DMX_READ_REG(device_no, SEC_BUFF_BUSY);
if (LARGE_SEC_BUFF_MASK ==
(section_busy32 & LARGE_SEC_BUFF_MASK)) {
/*All the largest section buffers occupied,
* clear buffers
*/
DMX_WRITE_REG(device_no,
SEC_BUFF_READY, section_busy32);
} else {
for (i = 0; i < SEC_BUF_COUNT; i++) {
if (!(section_busy32 & (1 << i)))
continue;
DMX_WRITE_REG(device_no, SEC_BUFF_NUMBER, i);
filter_number = DMX_READ_REG(device_no,
SEC_BUFF_NUMBER);
filter_number >>= 8;
if ((filter_number >= FILTER_COUNT)
/* >=31, do not handle this case */
|| ((filter_number < FILTER_COUNT)
&& dmx->filter[filter_number].used))
section_busy32 &= ~(1 << i);
}
if (section_busy32 & (dmx->smallsec.enable ?
0x7FFFFFFF :
LARGE_SEC_BUFF_MASK)) {
/*Clear invalid buffers */
DMX_WRITE_REG(device_no,
SEC_BUFF_READY,
section_busy32);
pr_error("clear invalid buffer 0x%x\n",
section_busy32);
}
#if 0
section_busy32 = 0x7fffffff;
for (i = 0; i < SEC_BUF_BUSY_SIZE; i++) {
dmx->section_busy[i] = (
(i == SEC_BUF_BUSY_SIZE - 1) ?
DMX_READ_REG(device_no, SEC_BUFF_BUSY) :
dmx->section_busy[i + 1]);
section_busy32 &= dmx->section_busy[i];
}
/*count the number of '1' bits */
i = section_busy32;
i = (i & 0x55555555) + ((i & 0xaaaaaaaa) >> 1);
i = (i & 0x33333333) + ((i & 0xcccccccc) >> 2);
i = (i & 0x0f0f0f0f) + ((i & 0xf0f0f0f0) >> 4);
i = (i & 0x00ff00ff) + ((i & 0xff00ff00) >> 8);
i = (i & 0x0000ffff) + ((i & 0xffff0000) >> 16);
if (i > LARGE_SEC_BUFF_COUNT) {
/*too long some of the section
* buffers are being processed
*/
DMX_WRITE_REG(device_no, SEC_BUFF_READY,
section_busy32);
}
#endif
}
demux_int_status1 =
DMX_READ_REG(device_no, STB_INT_STATUS) & 0xfff7;
if (demux_int_status1 & (1 << TS_ERROR_PIN)) {
DMX_WRITE_REG(device_no,
STB_INT_STATUS,
(1 << TS_ERROR_PIN));
}
}
end:
spin_unlock_irqrestore(&dvb->slock, flags);
#ifdef ENABLE_SEC_BUFF_WATCHDOG
mod_timer(&dvb->watchdog_timer,
jiffies + msecs_to_jiffies(WATCHDOG_TIMER));
#endif
}
static inline int sec_filter_match(struct aml_dmx *dmx, struct aml_filter *f,
u8 *p)
{
int b;
u8 neq = 0;
if (!f->used || !dmx->channel[f->chan_id].used)
return 0;
for (b = 0; b < FILTER_LEN; b++) {
u8 xor = p[b] ^ f->value[b];
if (xor & f->maskandmode[b])
return 0;
if (xor & f->maskandnotmode[b])
neq = 1;
}
if (f->neq && !neq)
return 0;
return 1;
}
static void trigger_crc_monitor(struct aml_dmx *dmx)
{
if (!dmx->crc_check_time) {
dmx->crc_check_time = jiffies;
dmx->crc_check_count = 0;
}
if (dmx->crc_check_count > 100) {
if (jiffies_to_msecs(jiffies - dmx->crc_check_time) <= 1000) {
struct aml_dvb *dvb = (struct aml_dvb *)dmx->demux.priv;
pr_error("Too many crc fail (%d crc fail in %d ms)!\n",
dmx->crc_check_count,
jiffies_to_msecs(jiffies - dmx->crc_check_time)
);
dmx_reset_dmx_hw_ex_unlock(dvb, dmx, 0);
}
dmx->crc_check_time = 0;
}
dmx->crc_check_count++;
}
static int section_crc(struct aml_dmx *dmx, struct aml_filter *f, u8 *p)
{
int sec_len = (((p[1] & 0xF) << 8) | p[2]) + 3;
struct dvb_demux_feed *feed = dmx->channel[f->chan_id].feed;
if (feed->feed.sec.check_crc) {
struct dvb_demux *demux = feed->demux;
struct dmx_section_feed *sec = &feed->feed.sec;
int section_syntax_indicator;
section_syntax_indicator = ((p[1] & 0x80) != 0);
sec->seclen = sec_len;
sec->crc_val = ~0;
if (demux->check_crc32(feed, p, sec_len)) {
pr_error("section CRC check failed! pid[%d]\n", feed->pid);
#if 0
{
int i;
pr_error("sec:[%#lx:%#lx:%#lx-%#lx:%#lx:%#lx-%#lx:%#lx:%#lx]\n",
dmx->sec_cnt[0], dmx->sec_cnt_match[0], dmx->sec_cnt_crc_fail[0],
dmx->sec_cnt[1], dmx->sec_cnt_match[1], dmx->sec_cnt_crc_fail[1],
dmx->sec_cnt[2], dmx->sec_cnt_match[2], dmx->sec_cnt_crc_fail[2]);
pr_error("bad sec[%d]:\n", sec_len);
/*
* if (sec_len > 256)
* sec_len = 256;
* for (i = 0; i < sec_len; i++) {
* pr_err("%02x ", p[i]);
* if (!((i + 1) % 16))
* pr_err("\n");
* }
*/
}
#endif
trigger_crc_monitor(dmx);
return 0;
}
#if 0
int i;
for (i = 0; i < sec_len; i++) {
pr_dbg("%02x ", p[i]);
if (!((i + 1) % 16))
pr_dbg("\n");
}
pr_dbg("\nsection data\n");
#endif
}
return 1;
}
static void section_notify(struct aml_dmx *dmx, struct aml_filter *f, u8 *p)
{
int sec_len = (((p[1] & 0xF) << 8) | p[2]) + 3;
struct dvb_demux_feed *feed = dmx->channel[f->chan_id].feed;
if (feed && feed->cb.sec)
feed->cb.sec(p, sec_len, NULL, 0, f->filter,0);
}
static void hardware_match_section(struct aml_dmx *dmx,
u16 sec_num, u16 buf_num)
{
u8 *p = (u8 *) dmx->sec_buf[buf_num].addr;
struct aml_filter *f;
int chid, i;
int need_crc = 1;
if (sec_num >= FILTER_COUNT) {
pr_dbg("sec_num invalid: %d\n", sec_num);
return;
}
dma_sync_single_for_cpu(dmx_get_dev(dmx),
dmx->sec_pages_map + (buf_num << 0x0c),
(1 << 0x0c), DMA_FROM_DEVICE);
f = &dmx->filter[sec_num];
chid = f->chan_id;
dmx->sec_cnt[SEC_CNT_HW]++;
for (i = 0; i < FILTER_COUNT; i++) {
f = &dmx->filter[i];
if (f->chan_id != chid)
continue;
if (sec_filter_match(dmx, f, p)) {
if (need_crc) {
dmx->sec_cnt_match[SEC_CNT_HW]++;
if (!section_crc(dmx, f, p)) {
dmx->sec_cnt_crc_fail[SEC_CNT_HW]++;
return;
}
need_crc = 0;
}
section_notify(dmx, f, p);
}
}
}
static void software_match_section(struct aml_dmx *dmx, u16 buf_num)
{
u8 *p = (u8 *) dmx->sec_buf[buf_num].addr;
struct aml_filter *f, *fmatch = NULL;
int i, fid = -1;
dma_sync_single_for_cpu(dmx_get_dev(dmx),
dmx->sec_pages_map + (buf_num << 0x0c),
(1 << 0x0c), DMA_FROM_DEVICE);
dmx->sec_cnt[SEC_CNT_SW]++;
for (i = 0; i < FILTER_COUNT; i++) {
f = &dmx->filter[i];
if (sec_filter_match(dmx, f, p)) {
pr_dbg("[software match]filter %d match, pid %d\n",
i, dmx->channel[f->chan_id].pid);
if (!fmatch) {
fmatch = f;
fid = i;
} else {
pr_error("[sw match]Muli-filter match this\n"
"section, will skip this section\n");
return;
}
}
}
if (fmatch) {
pr_dbg("[software match]dispatch\n"
"section to filter %d pid %d\n",
fid, dmx->channel[fmatch->chan_id].pid);
dmx->sec_cnt_match[SEC_CNT_SW]++;
if (section_crc(dmx, fmatch, p))
section_notify(dmx, fmatch, p);
else
dmx->sec_cnt_crc_fail[SEC_CNT_SW]++;
} else {
pr_dbg("[software match]this section do not\n"
"match any filter!!!\n");
}
}
static int _rbuf_write(struct dvb_ringbuffer *buf, const u8 *src, size_t len)
{
ssize_t free;
if (!len)
return 0;
if (!buf->data)
return 0;
free = dvb_ringbuffer_free(buf);
if (len > free) {
pr_error("sf: buffer overflow\n");
return -EOVERFLOW;
}
return dvb_ringbuffer_write(buf, src, len);
}
static int _rbuf_filter_pkts(struct dvb_ringbuffer *rb,
u8 *wrapbuf,
void (*swfilter_packets)(struct dvb_demux *demux,
const u8 *buf,
size_t count),
struct dvb_demux *demux)
{
ssize_t len1 = 0;
ssize_t len2 = 0;
size_t off;
size_t count;
size_t size;
if (debug_irq & 0x4)
dump(&rb->data[rb->pread], (debug_irq & 0xFFF00) >> 8);
/*
* rb|====--------===[0x47]====|
* ^ ^
* wr rd
*/
len1 = rb->pwrite - rb->pread;
if (len1 < 0) {
len1 = rb->size - rb->pread;
len2 = rb->pwrite;
}
for (off = 0; off < len1; off++) {
if (rb->data[rb->pread + off] == 0x47)
break;
}
if (off)
pr_dbg_irq_sf("off ->|%zd\n", off);
len1 -= off;
rb->pread = (rb->pread + off) % rb->size;
count = len1 / 188;
if (count) {
pr_dbg_irq_sf("pkt >> 1[%zd<->%zd]\n", rb->pread, rb->pwrite);
swfilter_packets(demux, rb->data + rb->pread, count);
size = count * 188;
len1 -= size;
rb->pread += size;
}
if (len2 && len1 && ((len1 + len2) > 188)) {
pr_dbg_irq_sf("pkt >> 2[%zd<->%zd]\n", rb->pread, rb->pwrite);
size = 188 - len1;
memcpy(wrapbuf, rb->data + rb->pread, len1);
memcpy(wrapbuf + len1, rb->data, size);
swfilter_packets(demux, wrapbuf, 1);
rb->pread = size;
len2 -= size;
}
if (len2) {
pr_dbg_irq_sf("pkt >> 3[%zd<->%zd]\n", rb->pread, rb->pwrite);
count = len2 / 188;
if (count) {
swfilter_packets(demux, rb->data + rb->pread, count);
rb->pread += count * 188;
}
}
return 0;
}
static void smallsection_match_section(struct aml_dmx *dmx, u8 *p, u16 sec_num)
{
struct aml_filter *f;
int chid, i;
int need_crc = 1;
if (sec_num >= FILTER_COUNT) {
pr_dbg("sec_num invalid: %d\n", sec_num);
return;
}
f = &dmx->filter[sec_num];
chid = f->chan_id;
dmx->sec_cnt[SEC_CNT_SS]++;
for (i = 0; i < FILTER_COUNT; i++) {
f = &dmx->filter[i];
if (f->chan_id != chid)
continue;
if (sec_filter_match(dmx, f, p)) {
if (need_crc) {
dmx->sec_cnt_match[SEC_CNT_SS]++;
if (!section_crc(dmx, f, p)) {
dmx->sec_cnt_crc_fail[SEC_CNT_SS]++;
return;
}
need_crc = 0;
}
section_notify(dmx, f, p);
}
}
}
static void process_smallsection(struct aml_dmx *dmx)
{
u32 v, wr, rd;
u32 data32;
struct aml_smallsec *ss = &dmx->smallsec;
v = DMX_READ_REG(dmx->id, DEMUX_SMALL_SEC_CTL);
wr = (v >> 8) & 0xff;
rd = (v >> 16) & 0xff;
if (rd != wr) {
int n1 = wr - rd,
n2 = 0,
max = (ss->bufsize>>8);
int i;
u8 *p;
int sec_len;
pr_dbg_irq_ss("secbuf[31] ctrl:0x%x\n", v);
if (n1 < 0) {
n1 = max - rd;
n2 = wr;
}
if (n1) {
pr_dbg_irq_ss("n1:%d\n", n1);
dma_sync_single_for_cpu(dmx_get_dev(dmx),
ss->buf_map+(rd<<8),
n1<<8,
DMA_FROM_DEVICE);
for (i = 0; i < n1; i++) {
p = (u8 *)ss->buf+((rd+i)<<8);
sec_len = (((p[1] & 0xF) << 8) | p[2]) + 3;
smallsection_match_section(dmx, p,
*(p+sec_len+1));
}
}
if (n2) {
pr_dbg_irq_ss("n2:%d\n", n2);
dma_sync_single_for_cpu(dmx_get_dev(dmx),
ss->buf_map,
n2<<8,
DMA_FROM_DEVICE);
for (i = 0; i < n2; i++) {
p = (u8 *)ss->buf+(i<<8);
sec_len = (((p[1] & 0xF) << 8) | p[2]) + 3;
smallsection_match_section(dmx, p,
*(p+sec_len+1));
}
}
rd = wr;
data32 = (DMX_READ_REG(dmx->id, DEMUX_SMALL_SEC_CTL)
& 0xff00ffff)
| (rd << 16);
DMX_WRITE_REG(dmx->id, DEMUX_SMALL_SEC_CTL, data32);
}
}
static void process_section(struct aml_dmx *dmx)
{
u32 ready, i, sec_busy;
u16 sec_num;
/*pr_dbg("section\n"); */
ready = DMX_READ_REG(dmx->id, SEC_BUFF_READY);
if (ready) {
#ifdef USE_AHB_MODE
/* WRITE_ISA_REG(AHB_BRIDGE_CTRL1,
* READ_ISA_REG (AHB_BRIDGE_CTRL1) | (1 << 31));
*/
/* WRITE_ISA_REG(AHB_BRIDGE_CTRL1,
* READ_ISA_REG (AHB_BRIDGE_CTRL1) & (~ (1 << 31)));
*/
#endif
if ((ready & (1<<31)) && dmx->smallsec.enable) {
u32 v, wr, rd;
v = DMX_READ_REG(dmx->id, DEMUX_SMALL_SEC_CTL);
wr = (v >> 8) & 0xff;
rd = (v >> 16) & 0xff;
if ((wr < rd) && (5 > (rd - wr)))
pr_error("warning: small ss buf [w%dr%d]\n",
wr, rd);
pr_dbg_irq_ss("ss>%x\n",
DMX_READ_REG(dmx->id, DEMUX_SMALL_SEC_CTL));
process_smallsection(dmx);
/*tasklet_hi_schedule(&dmx->dmx_tasklet);*/
/*tasklet_schedule(&dmx->dmx_tasklet);*/
DMX_WRITE_REG(dmx->id, SEC_BUFF_READY, (1<<31));
return;
}
for (i = 0; i < SEC_BUF_COUNT; i++) {
if (!(ready & (1 << i)))
continue;
/* get section busy */
sec_busy = DMX_READ_REG(dmx->id, SEC_BUFF_BUSY);
/* get filter number */
DMX_WRITE_REG(dmx->id, SEC_BUFF_NUMBER, i);
sec_num = (DMX_READ_REG(dmx->id, SEC_BUFF_NUMBER) >> 8);
/*
* sec_buf_watchdog_count dispatch:
* byte0 -- always busy=0 's watchdog count
* byte1 -- always busy=1 & filter_num=31 's
* watchdog count
*/
/* sec_busy is not set, check busy=0 watchdog count */
if (!(sec_busy & (1 << i))) {
/* clear other wd count of this buffer */
dmx->sec_buf_watchdog_count[i] &= 0x000000ff;
dmx->sec_buf_watchdog_count[i] += 0x1;
pr_dbg("bit%d ready=1, busy=0,\n"
"sec_num=%d for %d times\n",
i, sec_num,
dmx->sec_buf_watchdog_count[i]);
if (dmx->sec_buf_watchdog_count[i] >= 5) {
pr_dbg("busy=0 reach the max count,\n"
"try software match.\n");
software_match_section(dmx, i);
dmx->sec_buf_watchdog_count[i] = 0;
DMX_WRITE_REG(dmx->id, SEC_BUFF_READY,
(1 << i));
}
continue;
}
/* filter_num == 31 && busy == 1,check watchdog count */
if (sec_num >= FILTER_COUNT) {
/* clear other wd count of this buffer */
dmx->sec_buf_watchdog_count[i] &= 0x0000ff00;
dmx->sec_buf_watchdog_count[i] += 0x100;
pr_dbg("bit%d ready=1,busy=1,\n"
"sec_num=%d for %d times\n",
i, sec_num,
dmx->sec_buf_watchdog_count[i] >> 8);
if (dmx->sec_buf_watchdog_count[i] >= 0x500) {
pr_dbg("busy=1&filter_num=31\n"
" reach the max count, clear\n"
" the buf ready & busy!\n");
software_match_section(dmx, i);
dmx->sec_buf_watchdog_count[i] = 0;
DMX_WRITE_REG(dmx->id,
SEC_BUFF_READY,
(1 << i));
DMX_WRITE_REG(dmx->id,
SEC_BUFF_BUSY,
(1 << i));
}
continue;
}
/* now, ready & busy are both set and
* filter number is valid
*/
if (dmx->sec_buf_watchdog_count[i] != 0)
dmx->sec_buf_watchdog_count[i] = 0;
/* process this section */
hardware_match_section(dmx, sec_num, i);
/* clear the ready & busy bit */
DMX_WRITE_REG(dmx->id, SEC_BUFF_READY, (1 << i));
DMX_WRITE_REG(dmx->id, SEC_BUFF_BUSY, (1 << i));
}
}
}
#ifdef NO_SUB
static void process_sub(struct aml_dmx *dmx)
{
u32 rd_ptr = 0;
u32 wr_ptr = READ_MPEG_REG(PARSER_SUB_WP);
u32 start_ptr = READ_MPEG_REG(PARSER_SUB_START_PTR);
u32 end_ptr = READ_MPEG_REG(PARSER_SUB_END_PTR);
u32 buffer1 = 0, buffer2 = 0;
u8 *buffer1_virt = 0, *buffer2_virt = 0;
u32 len1 = 0, len2 = 0;
if (!dmx->sub_buf_base_virt)
return;
rd_ptr = READ_MPEG_REG(PARSER_SUB_RP);
if (!rd_ptr)
return;
if (rd_ptr > wr_ptr) {
len1 = end_ptr - rd_ptr + 8;
buffer1 = rd_ptr;
len2 = wr_ptr - start_ptr;
buffer2 = start_ptr;
rd_ptr = start_ptr + len2;
} else if (rd_ptr < wr_ptr) {
len1 = wr_ptr - rd_ptr;
buffer1 = rd_ptr;
rd_ptr += len1;
len2 = 0;
} else if (rd_ptr == wr_ptr) {
pr_dbg("sub no data\n");
}
if (buffer1 && len1)
#ifdef SUB_BUF_DMX
buffer1_virt = (void *)dmx->sub_pages + (buffer1 - start_ptr);
#else
buffer1_virt = (void *)dmx->sub_buf_base_virt + (buffer1 - start_ptr);
#endif
if (buffer2 && len2)
#ifdef SUB_BUF_DMX
buffer2_virt = (void *)dmx->sub_pages + (buffer2 - start_ptr);
#else
buffer2_virt = (void *)dmx->sub_buf_base_virt + (buffer2 - start_ptr);
#endif
pr_dbg_irq_sub("sub: rd_ptr:%x buf1:%x len1:%d buf2:%x len2:%d\n",
rd_ptr, buffer1, len1, buffer2, len2);
pr_dbg_irq_sub("sub: buf1_virt:%p buf2_virt:%p\n",
buffer1_virt, buffer2_virt);
if (len1)
dma_sync_single_for_cpu(dmx_get_dev(dmx),
(dma_addr_t) buffer1, len1,
DMA_FROM_DEVICE);
if (len2)
dma_sync_single_for_cpu(dmx_get_dev(dmx),
(dma_addr_t) buffer2, len2,
DMA_FROM_DEVICE);
if (dmx->channel[2].used) {
if (dmx->channel[2].feed && dmx->channel[2].feed->cb.ts &&
((buffer1_virt != NULL && len1 !=0 ) || (buffer2_virt != NULL && len2 != 0)))
{
dmx->channel[2].feed->cb.ts(buffer1_virt, len1,
buffer2_virt, len2,
&dmx->channel[2].feed->feed.ts,0);
}
}
WRITE_MPEG_REG(PARSER_SUB_RP, rd_ptr);
}
#endif
static void process_pes(struct aml_dmx *dmx)
{
long off, off_pre = pes_off_pre[dmx->id];
u8 *buffer1 = 0, *buffer2 = 0;
u8 *buffer1_phys = 0, *buffer2_phys = 0;
u32 len1 = 0, len2 = 0;
int i = 1;
off = (DMX_READ_REG(dmx->id, OTHER_WR_PTR) << 3);
pr_dbg_irq_pes("[%d]WR:0x%x PES WR:0x%x\n", dmx->id,
DMX_READ_REG(dmx->id, OTHER_WR_PTR),
DMX_READ_REG(dmx->id, OB_PES_WR_PTR));
buffer1 = (u8 *)(dmx->pes_pages + off_pre);
pr_dbg_irq_pes("[%d]PES WR[%02x %02x %02x %02x %02x %02x %02x %02x",
dmx->id,
buffer1[0], buffer1[1], buffer1[2], buffer1[3],
buffer1[4], buffer1[5], buffer1[6], buffer1[7]);
pr_dbg_irq_pes(" %02x %02x %02x %02x %02x %02x %02x %02x]\n",
buffer1[8], buffer1[9], buffer1[10], buffer1[11],
buffer1[12], buffer1[13], buffer1[14], buffer1[15]);
if (off > off_pre) {
len1 = off-off_pre;
buffer1 = (unsigned char *)(dmx->pes_pages + off_pre);
} else if (off < off_pre) {
len1 = dmx->pes_buf_len-off_pre;
buffer1 = (unsigned char *)(dmx->pes_pages + off_pre);
len2 = off;
buffer2 = (unsigned char *)dmx->pes_pages;
} else if (off == off_pre) {
pr_dbg("pes no data\n");
}
pes_off_pre[dmx->id] = off;
if (len1) {
buffer1_phys = (unsigned char *)virt_to_phys(buffer1);
dma_sync_single_for_cpu(dmx_get_dev(dmx),
(dma_addr_t)buffer1_phys, len1, DMA_FROM_DEVICE);
}
if (len2) {
buffer2_phys = (unsigned char *)virt_to_phys(buffer2);
dma_sync_single_for_cpu(dmx_get_dev(dmx),
(dma_addr_t)buffer2_phys, len2, DMA_FROM_DEVICE);
}
if (len1 || len2) {
struct aml_channel *ch;
for (i = 0; i < CHANNEL_COUNT; i++) {
ch = &dmx->channel[i];
if (ch->used && ch->feed
&& (ch->feed->type == DMX_TYPE_TS)) {
if (ch->feed->ts_type & TS_PAYLOAD_ONLY) {
ch->feed->cb.ts(buffer1,
len1, buffer2, len2,
&ch->feed->feed.ts,0);
}
}
}
}
}
static void process_om_read(struct aml_dmx *dmx)
{
unsigned int i;
unsigned short om_cmd_status_data_0 = 0;
unsigned short om_cmd_status_data_1 = 0;
/* unsigned short om_cmd_status_data_2 = 0;*/
unsigned short om_cmd_data_out = 0;
om_cmd_status_data_0 = DMX_READ_REG(dmx->id, OM_CMD_STATUS);
om_cmd_status_data_1 = DMX_READ_REG(dmx->id, OM_CMD_DATA);
/* om_cmd_status_data_2 = DMX_READ_REG(dmx->id, OM_CMD_DATA2);*/
if (om_cmd_status_data_0 & 1) {
DMX_WRITE_REG(dmx->id, OM_DATA_RD_ADDR,
(1 << 15) | ((om_cmd_status_data_1 & 0xff) << 2));
for (i = 0; i < (((om_cmd_status_data_1 >> 7) & 0x1fc) >> 1);
i++) {
om_cmd_data_out = DMX_READ_REG(dmx->id, OM_DATA_RD);
}
om_cmd_data_out = DMX_READ_REG(dmx->id, OM_DATA_RD_ADDR);
DMX_WRITE_REG(dmx->id, OM_DATA_RD_ADDR, 0);
DMX_WRITE_REG(dmx->id, OM_CMD_STATUS, 1);
}
}
static void dmx_irq_bh_handler(unsigned long arg)
{
struct aml_dmx *dmx = (struct aml_dmx *)arg;
#if 0
u32 status;
status = DMX_READ_REG(dmx->id, STB_INT_STATUS);
if (status)
DMX_WRITE_REG(dmx->id, STB_INT_STATUS, status);
#endif
process_smallsection(dmx);
}
static irqreturn_t dmx_irq_handler(int irq_number, void *para)
{
struct aml_dmx *dmx = (struct aml_dmx *)para;
struct aml_dvb *dvb = aml_get_dvb_device();
u32 status;
unsigned long flags;
spin_lock_irqsave(&dvb->slock, flags);
status = DMX_READ_REG(dmx->id, STB_INT_STATUS);
if (!status)
goto irq_handled;
pr_dbg_irq("demux %d irq status: 0x%08x\n", dmx->id, status);
if (status & (1 << SECTION_BUFFER_READY))
process_section(dmx);
#ifdef NO_SUB
if (status & (1 << SUB_PES_READY)) {
/*If the subtitle is set by tsdemux,
*do not parser in demux driver.
*/
if (dmx->sub_chan == -1)
process_sub(dmx);
}
#endif
if (status & (1 << OTHER_PES_READY))
process_pes(dmx);
if (status & (1 << OM_CMD_READ_PENDING))
process_om_read(dmx);
/*
*if (status & (1 << DUPLICATED_PACKET)) {
*}
*if (status & (1 << DIS_CONTINUITY_PACKET)) {
*}
*if (status & (1 << VIDEO_SPLICING_POINT)) {
*}
*if (status & (1 << AUDIO_SPLICING_POINT)) {
*}
*/
if (status & (1 << TS_ERROR_PIN))
pr_error("TS_ERROR_PIN\n");
if (status & (1 << NEW_PDTS_READY)) {
u32 pdts_status = DMX_READ_REG(dmx->id, STB_PTS_DTS_STATUS);
if (pdts_status & (1 << VIDEO_PTS_READY)) {
video_pts = DMX_READ_REG(dmx->id, VIDEO_PTS_DEMUX);
video_pts_bit32 =
(pdts_status & (1 << VIDEO_PTS_BIT32)) ? 1 : 0;
if (!first_video_pts
|| 0 > (int)(video_pts - first_video_pts))
first_video_pts = video_pts;
}
if (pdts_status & (1 << AUDIO_PTS_READY)) {
audio_pts = DMX_READ_REG(dmx->id, AUDIO_PTS_DEMUX);
audio_pts_bit32 =
(pdts_status & (1 << AUDIO_PTS_BIT32)) ? 1 : 0;
if (!first_audio_pts
|| 0 > (int)(audio_pts - first_audio_pts))
first_audio_pts = audio_pts;
}
}
if (dmx->irq_handler)
dmx->irq_handler(dmx->dmx_irq, (void *)(long)dmx->id);
DMX_WRITE_REG(dmx->id, STB_INT_STATUS, status);
/*tasklet_schedule(&dmx->dmx_tasklet);*/
{
if (!dmx->int_check_time) {
dmx->int_check_time = jiffies;
dmx->int_check_count = 0;
}
if (jiffies_to_msecs(jiffies - dmx->int_check_time) >= 100
|| dmx->int_check_count > 1000) {
if (dmx->int_check_count > 1000) {
struct aml_dvb *dvb =
(struct aml_dvb *)dmx->demux.priv;
pr_error("Too many irq (%d irq in %d ms)!\n",
dmx->int_check_count,
jiffies_to_msecs(jiffies -
dmx->int_check_time));
if (dmx->fe && !dmx->in_tune)
DMX_WRITE_REG(dmx->id, STB_INT_MASK, 0);
dmx_reset_hw_ex(dvb, 0);
}
dmx->int_check_time = 0;
}
dmx->int_check_count++;
if (dmx->in_tune) {
dmx->error_check++;
if (dmx->error_check > 200)
DMX_WRITE_REG(dmx->id, STB_INT_MASK, 0);
}
}
irq_handled:
spin_unlock_irqrestore(&dvb->slock, flags);
return IRQ_HANDLED;
}
static inline int dmx_get_order(unsigned long size)
{
int order;
order = -1;
do {
size >>= 1;
order++;
} while (size);
return order;
}
static inline int dmx_get_afifo_size(struct aml_asyncfifo *afifo)
{
return afifo->secure_enable && afifo->blk.len ? afifo->blk.len : asyncfifo_buf_len;
}
static void dvr_process_channel(struct aml_asyncfifo *afifo,
struct aml_channel *channel,
u32 total, u32 size,
struct aml_swfilter *sf)
{
int cnt;
int ret = 0;
struct aml_dvr_block blk;
if (afifo->buf_read > afifo->buf_toggle) {
cnt = total - afifo->buf_read;
if (!(afifo->secure_enable && afifo->blk.addr)) {
dma_sync_single_for_cpu(asyncfifo_get_dev(afifo),
afifo->pages_map+afifo->buf_read*size,
cnt*size,
DMA_FROM_DEVICE);
if (sf)
ret = _rbuf_write(&sf->rbuf,
(u8 *)afifo->pages+afifo->buf_read*size,
cnt*size);
else
channel->dvr_feed->cb.ts(
(u8 *)afifo->pages+afifo->buf_read*size,
cnt*size, NULL, 0,
&channel->dvr_feed->feed.ts,0);
} else {
blk.addr = afifo->blk.addr+afifo->buf_read*size;
blk.len = cnt*size;
if (sf)
ret = _rbuf_write(&sf->rbuf,
(u8 *)afifo->pages+afifo->buf_read*size,
cnt*size);
else {
channel->dvr_feed->cb.ts(
(u8 *)&blk,
sizeof(struct aml_dvr_block),
NULL, 0,
&channel->dvr_feed->feed.ts,0);
}
}
afifo->buf_read = 0;
}
if (afifo->buf_toggle > afifo->buf_read) {
cnt = afifo->buf_toggle - afifo->buf_read;
if (!(afifo->secure_enable && afifo->blk.addr)) {
dma_sync_single_for_cpu(asyncfifo_get_dev(afifo),
afifo->pages_map+afifo->buf_read*size,
cnt*size,
DMA_FROM_DEVICE);
if (sf) {
if (ret >= 0)
ret = _rbuf_write(&sf->rbuf,
(u8 *)afifo->pages+afifo->buf_read*size,
cnt*size);
} else {
channel->dvr_feed->cb.ts(
(u8 *)afifo->pages+afifo->buf_read*size,
cnt*size, NULL, 0,
&channel->dvr_feed->feed.ts,0);
}
} else {
blk.addr = afifo->blk.addr+afifo->buf_read*size;
blk.len = cnt*size;
if (sf)
ret = _rbuf_write(&sf->rbuf,
(u8 *)afifo->pages+afifo->buf_read*size,
cnt*size);
else {
channel->dvr_feed->cb.ts(
(u8 *)&blk,
sizeof(struct aml_dvr_block),
NULL, 0,
&channel->dvr_feed->feed.ts,0);
}
}
afifo->buf_read = afifo->buf_toggle;
}
if (sf && ret > 0) {
_rbuf_filter_pkts(&sf->rbuf, sf->wrapbuf,
dvb_dmx_swfilter_packets,
channel->dvr_feed->demux);
} else if (sf && ret <= 0)
pr_error("sf rbuf write error[%d]\n", ret);
else
pr_dbg_irq_dvr("write data to dvr\n");
}
static uint32_t last_afifo_time = 0;
static void dvr_irq_bh_handler(unsigned long arg)
{
struct aml_asyncfifo *afifo = (struct aml_asyncfifo *)arg;
struct aml_dvb *dvb = afifo->dvb;
struct aml_dmx *dmx;
u32 size, total;
int i, factor;
unsigned long flags;
pr_dbg_irq_dvr("async fifo %d irq, interval:%d ms, %d data\n", afifo->id,
jiffies_to_msecs(jiffies - last_afifo_time), afifo->flush_size);
spin_lock_irqsave(&dvb->slock, flags);
if (dvb && afifo->source >= AM_DMX_0 && afifo->source < AM_DMX_MAX) {
dmx = &dvb->dmx[afifo->source];
// pr_inf("async fifo %d irq, source:%d\n", afifo->id,afifo->source);
if (dmx->init && dmx->record) {
struct aml_swfilter *sf = &dvb->swfilter;
int issf = 0;
total = afifo->buf_len / afifo->flush_size;
factor = dmx_get_order(total);
size = afifo->buf_len >> factor;
if (sf->user && (sf->afifo == afifo))
issf = 1;
for (i = 0; i < CHANNEL_COUNT; i++) {
if (dmx->channel[i].used
&& dmx->channel[i].dvr_feed) {
dvr_process_channel(afifo,
&dmx->channel[i],
total,
size,
issf?sf:NULL);
break;
}
}
}
}
spin_unlock_irqrestore(&dvb->slock, flags);
last_afifo_time = jiffies;
}
static irqreturn_t dvr_irq_handler(int irq_number, void *para)
{
struct aml_asyncfifo *afifo = (struct aml_asyncfifo *)para;
int factor = dmx_get_order(afifo->buf_len / afifo->flush_size);
afifo->buf_toggle++;
afifo->buf_toggle %= (1 << factor);
tasklet_schedule(&afifo->asyncfifo_tasklet);
return IRQ_HANDLED;
}
/*Enable the STB*/
static void stb_enable(struct aml_dvb *dvb)
{
int out_src, des_in, en_des, fec_clk, hiu, dec_clk_en;
int src, tso_src, i;
u32 fec_s0, fec_s1,fec_s2;
u32 invert0, invert1, invert2;
u32 data;
switch (dvb->stb_source) {
case AM_TS_SRC_DMX0:
src = dvb->dmx[0].source;
break;
case AM_TS_SRC_DMX1:
src = dvb->dmx[1].source;
break;
case AM_TS_SRC_DMX2:
src = dvb->dmx[2].source;
break;
default:
src = dvb->stb_source;
break;
}
switch (src) {
case AM_TS_SRC_TS0:
fec_clk = tsfile_clkdiv;
hiu = 0;
break;
case AM_TS_SRC_TS1:
fec_clk = tsfile_clkdiv;
hiu = 0;
break;
case AM_TS_SRC_TS2:
fec_clk = tsfile_clkdiv;
hiu = 0;
break;
case AM_TS_SRC_TS3:
fec_clk = tsfile_clkdiv;
hiu = 0;
break;
case AM_TS_SRC_S_TS0:
fec_clk = tsfile_clkdiv;
hiu = 0;
break;
case AM_TS_SRC_S_TS1:
fec_clk = tsfile_clkdiv;
hiu = 0;
break;
case AM_TS_SRC_S_TS2:
fec_clk = tsfile_clkdiv;
hiu = 0;
break;
case AM_TS_SRC_HIU:
fec_clk = tsfile_clkdiv;
hiu = 1;
break;
case AM_TS_SRC_HIU1:
fec_clk = tsfile_clkdiv;
hiu = 1;
break;
default:
fec_clk = 0;
hiu = 0;
break;
}
switch (dvb->dsc[0].source) {
case AM_TS_SRC_DMX0:
des_in = 0;
en_des = 1;
dec_clk_en = 1;
break;
case AM_TS_SRC_DMX1:
des_in = 1;
en_des = 1;
dec_clk_en = 1;
break;
case AM_TS_SRC_DMX2:
des_in = 2;
en_des = 1;
dec_clk_en = 1;
break;
default:
des_in = 0;
en_des = 0;
dec_clk_en = 0;
break;
}
switch (dvb->tso_source) {
case AM_TS_SRC_DMX0:
tso_src = dvb->dmx[0].source;
break;
case AM_TS_SRC_DMX1:
tso_src = dvb->dmx[1].source;
break;
case AM_TS_SRC_DMX2:
tso_src = dvb->dmx[2].source;
break;
default:
tso_src = dvb->tso_source;
break;
}
switch (tso_src) {
case AM_TS_SRC_TS0:
out_src = 0;
break;
case AM_TS_SRC_TS1:
out_src = 1;
break;
case AM_TS_SRC_TS2:
out_src = 2;
break;
case AM_TS_SRC_TS3:
out_src = 3;
break;
case AM_TS_SRC_S_TS0:
out_src = 6;
break;
case AM_TS_SRC_S_TS1:
out_src = 5;
break;
case AM_TS_SRC_S_TS2:
out_src = 4;
break;
case AM_TS_SRC_HIU:
out_src = 7;
break;
default:
out_src = 0;
break;
}
pr_dbg("[stb]src: %d, dsc1in: %d, tso: %d\n", src, des_in, out_src);
fec_s0 = 0;
fec_s1 = 0;
fec_s2 = 0;
invert0 = 0;
invert1 = 0;
invert2 = 0;
for (i = 0; i < dvb->ts_in_total_count; i++) {
if (dvb->ts[i].s2p_id == 0)
fec_s0 = i;
else if (dvb->ts[i].s2p_id == 1)
fec_s1 = i;
else if (dvb->ts[i].s2p_id == 2)
fec_s2 = i;
}
invert0 = dvb->s2p[0].invert;
invert1 = dvb->s2p[1].invert;
WRITE_MPEG_REG(STB_TOP_CONFIG,
(invert1 << INVERT_S2P1_FEC_CLK) |
(fec_s1 << S2P1_FEC_SERIAL_SEL) |
(out_src << TS_OUTPUT_SOURCE) |
(des_in << DES_INPUT_SEL) |
(en_des << ENABLE_DES_PL) |
(dec_clk_en << ENABLE_DES_PL_CLK) |
(invert0 << INVERT_S2P0_FEC_CLK) |
(fec_s0 << S2P0_FEC_SERIAL_SEL)|
(ciplus));
ciplus = 0;
if (get_cpu_type() >= MESON_CPU_MAJOR_ID_TL1) {
invert2 = dvb->s2p[2].invert;
WRITE_MPEG_REG(STB_S2P2_CONFIG,
(invert2 << INVERT_S2P2_FEC_CLK) |
(fec_s2 << S2P2_FEC_SERIAL_SEL));
}
if (dvb->reset_flag)
hiu = 0;
/* invert ts out clk,add ci model need add this*/
if (dvb->ts_out_invert) {
printk("ts out invert ---\r\n");
data = READ_MPEG_REG(TS_TOP_CONFIG);
data |= 1 << TS_OUT_CLK_INVERT;
WRITE_MPEG_REG(TS_TOP_CONFIG, data);
}
if (src == AM_TS_SRC_HIU1) {
WRITE_MPEG_REG(TS_HIU1_CONFIG,
(demux_skipbyte << FILE_M2TS_SKIP_BYTES_HIU1) |
(hiu << TS_HIU_ENABLE_HIU1) |
(fec_clk << FEC_CLK_DIV_HIU1) |
(0xBB << TS_PACKAGE_LENGTH_SUB_1_HIU1) |
(0x47 << FEC_SYNC_BYTE_HIU1));
} else {
/* invert ts out clk end */
WRITE_MPEG_REG(TS_FILE_CONFIG,
(demux_skipbyte << 16) |
(6 << DES_OUT_DLY) |
(3 << TRANSPORT_SCRAMBLING_CONTROL_ODD) |
(3 << TRANSPORT_SCRAMBLING_CONTROL_ODD_2) |
(hiu << TS_HIU_ENABLE) | (fec_clk << FEC_FILE_CLK_DIV));
}
}
int dsc_set_pid(struct aml_dsc_channel *ch, int pid)
{
struct aml_dsc *dsc = ch->dsc;
int is_dsc2 = (dsc->id == 1) ? 1 : 0;
u32 data;
WRITE_MPEG_REG(TS_PL_PID_INDEX,
((ch->id & 0x0f) >> 1)+(is_dsc2 ? 4 : 0));
data = READ_MPEG_REG(TS_PL_PID_DATA);
if (ch->id & 1) {
data &= 0xFFFF0000;
data |= pid & 0x1fff;
if (!ch->used)
data |= 1 << PID_MATCH_DISABLE_LOW;
} else {
data &= 0xFFFF;
data |= (pid & 0x1fff) << 16;
if (!ch->used)
data |= 1 << PID_MATCH_DISABLE_HIGH;
}
WRITE_MPEG_REG(TS_PL_PID_INDEX,
((ch->id & 0x0f) >> 1)+(is_dsc2 ? 4 : 0));
WRITE_MPEG_REG(TS_PL_PID_DATA, data);
WRITE_MPEG_REG(TS_PL_PID_INDEX, 0);
if (ch->used)
pr_dbg("set DSC %d ch %d PID %d\n", dsc->id, ch->id, pid);
else
pr_dbg("disable DSC %d ch %d\n", dsc->id, ch->id);
return 0;
}
int dsc_get_pid(struct aml_dsc_channel *ch, int *pid)
{
struct aml_dsc *dsc = ch->dsc;
int is_dsc2 = (dsc->id == 1) ? 1 : 0;
u32 data;
WRITE_MPEG_REG(TS_PL_PID_INDEX,
((ch->id & 0x0f) >> 1)+(is_dsc2 ? 4 : 0));
data = READ_MPEG_REG(TS_PL_PID_DATA);
if (ch->id & 1) {
*pid = data & 0x1fff;
} else {
*pid = (data >> 16) & 0x1fff;
}
/*pr_dbg("%s,get DSC %d ch %d PID %d\n", __FUNCTION__,dsc->id, ch->id, *pid);*/
return 0;
}
int dsc_set_key(struct aml_dsc_channel *ch, int flags, enum ca_cw_type type,
u8 *key)
{
/*struct aml_dsc *dsc = ch->dsc;*/
int ret = -1;
switch (type) {
case CA_CW_DVB_CSA_EVEN:
case CA_CW_DVB_CSA_ODD:
aml_ci_plus_disable();
ret = dsc_set_csa_key(ch, flags, type, key);
if (ret != 0)
goto END;
/* Different with old mode, do change */
if (ch->work_mode == CIPLUS_MODE || ch->work_mode == -1) {
if (ch->work_mode == -1)
pr_inf("dsc[%d:%d] enable\n",
ch->dsc->id, ch->id);
else
pr_inf("dsc[%d:%d] enable (from ciplus)\n",
ch->dsc->id, ch->id);
ch->mode = ECB_MODE;
ch->work_mode = DVBCSA_MODE;
}
break;
case CA_CW_AES_EVEN:
case CA_CW_AES_ODD:
case CA_CW_AES_EVEN_IV:
case CA_CW_AES_ODD_IV:
case CA_CW_DES_EVEN:
case CA_CW_DES_ODD:
case CA_CW_SM4_EVEN:
case CA_CW_SM4_ODD:
case CA_CW_SM4_EVEN_IV:
case CA_CW_SM4_ODD_IV:
ret = dsc_set_aes_des_sm4_key(ch, flags, type, key);
if (ret != 0)
goto END;
am_ci_plus_set_output(ch);
/* Different with old mode, do change */
if (ch->work_mode == DVBCSA_MODE || ch->work_mode == -1) {
if (ch->work_mode == -1)
pr_inf("dsc[%d:%d] ciplus enable\n",
ch->dsc->id, ch->id);
else
pr_inf("dsc[%d:%d] ciplus enable (from dsc)\n",
ch->dsc->id, ch->id);
ch->work_mode = CIPLUS_MODE;
}
break;
default:
break;
}
END:
return ret;
}
int dsc_set_keys(struct aml_dsc_channel *ch)
{
int types = ch->set & 0xFFFFFF;
int flag = (ch->set >> 24) & 0xFF;
int i;
u8 *k;
int ret = 0;
for (i = 0; i < CA_CW_TYPE_MAX; i++) {
if (types & (1 << i)) {
k = NULL;
switch (i) {
case CA_CW_DVB_CSA_EVEN:
case CA_CW_AES_EVEN:
case CA_CW_DES_EVEN:
case CA_CW_SM4_EVEN:
k = ch->even;
break;
case CA_CW_DVB_CSA_ODD:
case CA_CW_AES_ODD:
case CA_CW_DES_ODD:
case CA_CW_SM4_ODD:
k = ch->odd;
break;
case CA_CW_AES_EVEN_IV:
case CA_CW_SM4_EVEN_IV:
k = ch->even_iv;
break;
case CA_CW_AES_ODD_IV:
case CA_CW_SM4_ODD_IV:
k = ch->odd_iv;
break;
default:
break;
}
/*
if (k)
pr_inf("dsc ch:%d flag:%d type:%d\n", ch->id, flag, i);
*/
if (k)
ret = dsc_set_key(ch, flag,
i,
k);
}
}
return 0;
}
static int dsc_set_csa_key(struct aml_dsc_channel *ch, int flags,
enum ca_cw_type type, u8 *key)
{
struct aml_dsc *dsc = ch->dsc;
int is_dsc2 = (dsc->id == 1) ? 1 : 0;
u16 k0, k1, k2, k3;
u32 key0, key1;
int reg;
if (flags & DSC_FROM_KL) {
k0 = k1 = k2 = k3 = 0;
/*dummy write to check if kl not working*/
key0 = key1 = 0;
WRITE_MPEG_REG(COMM_DESC_KEY0, key0);
WRITE_MPEG_REG(COMM_DESC_KEY1, key1);
/*tdes? :*/
if (get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB) {
WRITE_MPEG_REG(COMM_DESC_KEY_RW,
/* (type ? (1 << 6) : (1 << 5)) | */
((1 << 5)) |
((ch->id + type * DSC_COUNT)+
(is_dsc2 ? 16 : 0)));
}
if (get_cpu_type() == MESON_CPU_MAJOR_ID_GXL ||
get_cpu_type() == MESON_CPU_MAJOR_ID_GXM) {
pr_info("do kl..\n");
WRITE_MPEG_REG(COMM_DESC_KEY_RW,
(type ? (1 << 6) : (1 << 5)) | (1<<7) |
((ch->id + type * DSC_COUNT)+
(is_dsc2 ? 16 : 0)));
}
reg = (type ? (1 << 6) : (1 << 5)) |
((ch->id + type * DSC_COUNT)+
(is_dsc2 ? 16 : 0));
} else {
k0 = (key[0] << 8) | key[1];
k1 = (key[2] << 8) | key[3];
k2 = (key[4] << 8) | key[5];
k3 = (key[6] << 8) | key[7];
key0 = (k0 << 16) | k1;
key1 = (k2 << 16) | k3;
WRITE_MPEG_REG(COMM_DESC_KEY0, key0);
WRITE_MPEG_REG(COMM_DESC_KEY1, key1);
reg = (ch->id + type * DSC_COUNT)+(is_dsc2 ? 16 : 0);
WRITE_MPEG_REG(COMM_DESC_KEY_RW, reg);
}
return 0;
}
/************************* AES DESC************************************/
/*#define STB_TOP_CONFIG 0x16f0
#define CIPLUS_KEY0 0x16f8
#define CIPLUS_KEY1 0x16f9
#define CIPLUS_KEY2 0x16fa
#define CIPLUS_KEY3 0x16fb
#define CIPLUS_KEY_WR 0x16fc
#define CIPLUS_CONFIG 0x16fd
#define CIPLUS_ENDIAN 0x16fe*/
#define ENABLE_DEC_PL 7
#define ENABLE_DES_PL_CLK 15
#define KEY_WR_AES_IV_B 5
#define KEY_WR_AES_IV_A 4
#define KEY_WR_AES_B 3
#define KEY_WR_AES_A 2
#define KEY_WR_DES_B 1
#define KEY_WR_DES_A 0
#define IDSA_MODE_BIT 31
#define SM4_MODE 30
#define DES2_KEY_ENDIAN 25
#define DES2_IN_ENDIAN 21
#define DES2_CFG 6
#define DES2_EN 5
#define CNTL_ENABLE 3
#define AES_CBC_DISABLE 2
#define AES_EN 1
#define DES_EN 0
#define AES_IV_ENDIAN 28
#define AES_MSG_OUT_ENDIAN 24
#define AES_MSG_IN_ENDIAN 20
#define AES_KEY_ENDIAN 16
#define DES_MSG_OUT_ENDIAN 8
#define DES_MSG_IN_ENDIAN 4
#define DES_KEY_ENDIAN 0
#define ALGO_AES 0
#define ALGO_SM4 1
#define ALGO_DES 2
#if 0
static void aml_ci_plus_set_stb(void)
{
unsigned int data;
/* data = READ_MPEG_REG(FEC_INPUT_CONTROL); */
/* data |= (0<<FEC_SEL); */
/* data |= (1<<FEC_CORE_SEL); */
/* data |= (1<<FEC_INPUT_FEC_CLK);
* local playback will not work if set this
*/
/* WRITE_MPEG_REG(FEC_INPUT_CONTROL, data); */
data = READ_MPEG_REG(STB_TOP_CONFIG);
WRITE_MPEG_REG(STB_TOP_CONFIG, data |
(0 << CIPLUS_IN_SEL) | (0 << CIPLUS_OUT_SEL));
data = READ_MPEG_REG(STB_TOP_CONFIG);
/* data |= (1<<ENABLE_DEC_PL); bit 7 --
* enable_des_pl, this step was set in dsc_enable
*/
/*bit 15 -- enable_des_pl_clk*/
/* data |= (1<<ENABLE_DES_PL_CLK); */
data |= (1<<CIPLUS_OUT_SEL);/*bit 28 -- ciplus_out_sel from ciplus*/
WRITE_MPEG_REG(STB_TOP_CONFIG, data);
data = READ_MPEG_REG(STB_TOP_CONFIG);
}
#endif
/*
* param:
* key:
* 16bytes IV key
* type:
* AM_DSC_KEY_TYPE_AES_ODD IV odd key
* AM_DSC_KEY_TYPE_AES_EVEN IV even key
*/
void aml_ci_plus_set_iv(struct aml_dsc_channel *ch, enum ca_cw_type type,
u8 *key)
{
unsigned int k0, k1, k2, k3;
k3 = (key[0] << 24) | (key[1] << 16) | (key[2] << 8) | key[3];
k2 = (key[4] << 24) | (key[5] << 16) | (key[6] << 8) | key[7];
k1 = (key[8] << 24) | (key[9] << 16) | (key[10] << 8) | key[11];
k0 = (key[12] << 24) | (key[13] << 16) | (key[14] << 8) | key[15];
if (type == CA_CW_AES_EVEN_IV ||
type == CA_CW_SM4_EVEN_IV) {
WRITE_MPEG_REG(CIPLUS_KEY0, k0);
WRITE_MPEG_REG(CIPLUS_KEY1, k1);
WRITE_MPEG_REG(CIPLUS_KEY2, k2);
WRITE_MPEG_REG(CIPLUS_KEY3, k3);
WRITE_MPEG_REG(CIPLUS_KEY_WR,
(ch->id << 9) | (1<<KEY_WR_AES_IV_A));
} else if (type == CA_CW_AES_ODD_IV ||
type == CA_CW_SM4_ODD_IV) {
WRITE_MPEG_REG(CIPLUS_KEY0, k0);
WRITE_MPEG_REG(CIPLUS_KEY1, k1);
WRITE_MPEG_REG(CIPLUS_KEY2, k2);
WRITE_MPEG_REG(CIPLUS_KEY3, k3);
WRITE_MPEG_REG(CIPLUS_KEY_WR,
(ch->id << 9) | (1<<KEY_WR_AES_IV_B));
}
}
/*
* Param:
* key_endian
* S905D 7 for kl 0 for set key directly
* mode
* 0 for ebc
* 1 for cbc
*/
static void aml_ci_plus_config(int key_endian, int mode, int algo)
{
unsigned int data;
unsigned int idsa_mode = 0;
unsigned int sm4_mode = 0;
unsigned int cbc_disable = 0;
unsigned int des_enable = 0;
unsigned int aes_enable = 0;
unsigned int des2_key_endian = 0;
unsigned int des2_in_endian = 0;
unsigned int des2_cfg = 0;
unsigned int des2_enable = 0;
pr_dbg("%s mode:%d,alog:%d\n",__FUNCTION__,mode,algo);
if (get_cpu_type() < MESON_CPU_MAJOR_ID_SM1) {
WRITE_MPEG_REG(CIPLUS_ENDIAN,
(15 << AES_MSG_OUT_ENDIAN)
| (15 << AES_MSG_IN_ENDIAN)
| (key_endian << AES_KEY_ENDIAN)
|
(15 << DES_MSG_OUT_ENDIAN)
| (15 << DES_MSG_IN_ENDIAN)
| (key_endian << DES_KEY_ENDIAN)
);
} else if (algo == ALGO_DES){
WRITE_MPEG_REG(CIPLUS_ENDIAN,
(15 << AES_IV_ENDIAN)
| (7 << AES_MSG_OUT_ENDIAN)
| (15 << AES_MSG_IN_ENDIAN)
| (15 << AES_KEY_ENDIAN)
);
pr_inf("CIPLUS_ENDIAN is 0x%x\n", READ_MPEG_REG(CIPLUS_ENDIAN));
} else {
WRITE_MPEG_REG(CIPLUS_ENDIAN, 0);
}
data = READ_MPEG_REG(CIPLUS_ENDIAN);
if (algo == ALGO_SM4) {
sm4_mode = 1;
} else if (algo == ALGO_AES){
aes_enable = 1;
} else {
if (get_cpu_type() < MESON_CPU_MAJOR_ID_SM1) {
des_enable = 1;
} else {
des2_key_endian = 8;
des2_in_endian = 8;
des2_cfg = 2;
des2_enable = 1;
aes_enable = 1;
}
}
if (mode == IDSA_MODE) {
idsa_mode = 1;
cbc_disable = 0;
} else if (mode == CBC_MODE) {
cbc_disable = 0;
} else {
cbc_disable = 1;
}
pr_dbg("idsa_mode:%d sm4_mode:%d cbc_disable:%d aes_enable:%d des_enable:%d\n", \
idsa_mode,sm4_mode,cbc_disable,aes_enable,des_enable);
data = (idsa_mode << IDSA_MODE_BIT) |
(sm4_mode << SM4_MODE ) |
(des2_key_endian << DES2_KEY_ENDIAN) |
(des2_in_endian << DES2_IN_ENDIAN) |
(des2_cfg << DES2_CFG) |
(des2_enable << DES2_EN) |
(cbc_disable << AES_CBC_DISABLE) |
/*1 << AES_CBC_DISABLE : ECB
*0 << AES_CBC_DISABLE : CBC
*/
(1 << CNTL_ENABLE) |
(aes_enable << AES_EN) |
(des_enable << DES_EN);
WRITE_MPEG_REG(CIPLUS_CONFIG, data);
data = READ_MPEG_REG(CIPLUS_CONFIG);
pr_dbg("CIPLUS_CONFIG is 0x%x\n",data);
}
static void set_fec_core_sel (struct aml_dvb *dvb)
{
int i;
for (i = 0; i < DMX_DEV_COUNT; i ++) {
int set = 0;
u32 ctrl = DMX_READ_REG(i, FEC_INPUT_CONTROL);
if ((dvb->dsc[0].dst != -1) && (dvb->dsc[0].dst - AM_TS_SRC_DMX0 == i)) {
set = 1;
} else if ((dvb->dsc[1].dst != -1) && (dvb->dsc[1].dst - AM_TS_SRC_DMX0 == i)) {
set = 1;
} else {
u32 cfg = READ_MPEG_REG(CIPLUS_CONFIG);
if (cfg & (1 << CNTL_ENABLE)) {
if (!ciplus_out_auto_mode) {
if (ciplus_out_sel & (1 << i))
set = 1;
}
}
}
if (set) {
ctrl |= (1 << FEC_CORE_SEL);
} else {
ctrl &= ~(1 << FEC_CORE_SEL);
}
DMX_WRITE_REG(i, FEC_INPUT_CONTROL, ctrl);
}
}
/*
* Set output to demux set.
*/
static void am_ci_plus_set_output(struct aml_dsc_channel *ch)
{
struct aml_dsc *dsc = ch->dsc;
struct aml_dvb *dvb = dsc->dvb;
u32 data;
u32 in = 0, out = 0;
int set = 0;
if (dsc->id != 0) {
pr_error("Ciplus set output can only work at dsc0 device\n");
return;
}
switch (dsc->source) {
case AM_TS_SRC_DMX0:
in = 0;
break;
case AM_TS_SRC_DMX1:
in = 1;
break;
case AM_TS_SRC_DMX2:
in = 2;
break;
default:
break;
}
if (ciplus_out_auto_mode == 1) {
switch (dsc->dst) {
case AM_TS_SRC_DMX0:
out = 1;
break;
case AM_TS_SRC_DMX1:
out = 2;
break;
case AM_TS_SRC_DMX2:
out = 4;
break;
default:
break;
}
set = 1;
ciplus_out_sel = out;
} else if (ciplus_out_sel >= 0 && ciplus_out_sel <= 7) {
set = 1;
out = ciplus_out_sel;
} else {
pr_error("dsc ciplus out config is invalid\n");
}
if (set) {
/* Set ciplus input source ,
* output set 0 means no output. ---> need confirm.
* if output set 0 still affects dsc output, we need to disable
* ciplus module.
*/
data = READ_MPEG_REG(STB_TOP_CONFIG);
data &= ~(3<<CIPLUS_IN_SEL);
data |= in << CIPLUS_IN_SEL;
data &= ~(7<<CIPLUS_OUT_SEL);
data |= out << CIPLUS_OUT_SEL;
WRITE_MPEG_REG(STB_TOP_CONFIG, data);
pr_inf("dsc ciplus in[%x] out[%x] %s\n", in, out,
(ciplus_out_auto_mode) ? "" : "force");
set_fec_core_sel(dvb);
}
}
#if 0
/*
* Ciplus output has high priority,
* disable it's output will let dsc output go.
*/
static void aml_ci_plus_disable_output(void)
{
u32 data = 0;
data = READ_MPEG_REG(STB_TOP_CONFIG);
WRITE_MPEG_REG(STB_TOP_CONFIG, data &
~(7 << CIPLUS_OUT_SEL));
}
static void aml_ci_plus_enable(void)
{
u32 data = 0;
data = READ_MPEG_REG(STB_TOP_CONFIG);
WRITE_MPEG_REG(CIPLUS_CONFIG,
(1 << CNTL_ENABLE)
| (1 << AES_EN)
| (1 << DES_EN));
}
#endif
static void aml_ci_plus_disable(void)
{
u32 data = 0;
WRITE_MPEG_REG(CIPLUS_CONFIG, 0);
data = READ_MPEG_REG(STB_TOP_CONFIG);
WRITE_MPEG_REG(STB_TOP_CONFIG, data &
~((1 << CIPLUS_IN_SEL) | (7 << CIPLUS_OUT_SEL)));
}
static int dsc_set_aes_des_sm4_key(struct aml_dsc_channel *ch, int flags,
enum ca_cw_type type, u8 *key)
{
unsigned int k0, k1, k2, k3;
int iv = 0, aes = 0, des = 0;
int ab_iv = 0, ab_aes = 0, ab_des = 0;
int from_kl = flags & CA_CW_FROM_KL;
int algo = 0;
if (!from_kl) {
if (get_cpu_type() < MESON_CPU_MAJOR_ID_SM1) {
k3 = (key[0] << 24) | (key[1] << 16) | (key[2] << 8) | key[3];
k2 = (key[4] << 24) | (key[5] << 16) | (key[6] << 8) | key[7];
k1 = (key[8] << 24) | (key[9] << 16) | (key[10] << 8) | key[11];
k0 = (key[12] << 24) | (key[13] << 16)
| (key[14] << 8) | key[15];
} else {
k0 = (key[0]) | (key[1] << 8) | (key[2] << 16) | (key[3] << 24);
k1 = (key[4]) | (key[5] << 8) | (key[6] << 16) | (key[7] << 24);
k2 = (key[8]) | (key[9] << 8) | (key[10] << 16)| (key[11] << 24);
k3 = (key[12])| (key[13] << 8)| (key[14] << 16)| (key[15] << 24);
}
} else
k0 = k1 = k2 = k3 = 0;
switch (type) {
case CA_CW_AES_EVEN:
case CA_CW_SM4_EVEN:
ab_aes = (from_kl) ? 0x2 : 0x1;
if (ch->mode == -1)
ch->mode = ECB_MODE;
aes = 1;
if (type == CA_CW_AES_EVEN)
algo = ALGO_AES;
else
algo = ALGO_SM4;
break;
case CA_CW_AES_ODD:
case CA_CW_SM4_ODD:
ab_aes = (from_kl) ? 0x1 : 0x2;
if (ch->mode == -1)
ch->mode = ECB_MODE;
aes = 1;
if (type == CA_CW_AES_ODD)
algo = ALGO_AES;
else
algo = ALGO_SM4;
break;
case CA_CW_AES_EVEN_IV:
case CA_CW_SM4_EVEN_IV:
ab_iv = 0x1;
if (ch->mode == -1)
ch->mode = CBC_MODE;
iv = 1;
if (type == CA_CW_AES_EVEN_IV)
algo = ALGO_AES;
else
algo = ALGO_SM4;
break;
case CA_CW_AES_ODD_IV:
case CA_CW_SM4_ODD_IV:
ab_iv = 0x2;
if (ch->mode == -1)
ch->mode = CBC_MODE;
iv = 1;
if (type == CA_CW_AES_ODD_IV)
algo = ALGO_AES;
else
algo = ALGO_SM4;
break;
case CA_CW_DES_EVEN:
if (get_cpu_type() < MESON_CPU_MAJOR_ID_SM1) {
ab_des = 0x1;
} else {
ab_aes = 0x1;
}
ch->mode = ECB_MODE;
des = 1;
algo = ALGO_DES;
break;
case CA_CW_DES_ODD:
if (get_cpu_type() < MESON_CPU_MAJOR_ID_SM1) {
ab_des = 0x2;
} else {
ab_aes = 0x2;
}
ch->mode = ECB_MODE;
algo = ALGO_DES;
des = 1;
break;
default:
break;
}
/* Set endian and cbc/ecb mode */
if (from_kl)
aml_ci_plus_config(7, ch->mode, algo);
else
aml_ci_plus_config(0, ch->mode, algo);
/* Write keys to work */
if (iv || aes) {
WRITE_MPEG_REG(CIPLUS_KEY0, k0);
WRITE_MPEG_REG(CIPLUS_KEY1, k1);
WRITE_MPEG_REG(CIPLUS_KEY2, k2);
WRITE_MPEG_REG(CIPLUS_KEY3, k3);
} else {/*des*/
WRITE_MPEG_REG(CIPLUS_KEY0, k2);
WRITE_MPEG_REG(CIPLUS_KEY1, k3);
WRITE_MPEG_REG(CIPLUS_KEY2, 0);
WRITE_MPEG_REG(CIPLUS_KEY3, 0);
}
WRITE_MPEG_REG(CIPLUS_KEY_WR,
(ch->id << 9) |
/* bit[11:9] the key of index,
need match PID index*/
((from_kl && des) ? (1 << 8) : 0) |
/* bit[8] des key use cw[127:64]*/
(0 << 7) | /* bit[7] aes iv use cw*/
((from_kl && (aes || des)) ? (1 << 6) : 0) |
/* bit[6] aes/des key use cw*/
/* bit[5] write AES IV B value*/
(ab_iv << 4) | /* bit[4] write AES IV A value*/
/* bit[3] write AES B key*/
(ab_aes << 2) | /* bit[2] write AES A key*/
/* bit[1] write DES B key*/
(ab_des)); /* bit[0] write DES A key*/
/*
pr_inf("k:%08x:%08x:%08x:%08x kl:%d aes:%d des:%d ab_iv:%d ab_aes:%d ab_des:%d id:%d mod:%d\n",
k0, k1, k2, k3,
from_kl, aes, des, ab_iv, ab_aes, ab_des, ch->id, ch->aes_mode);
*/
return 0;
}
void dsc_release(void)
{
//aml_ci_plus_disable();
}
/************************* AES DESC************************************/
void set_ciplus_input_source(struct aml_dsc *dsc)
{
u32 data;
u32 in = 0;
if (dsc->id != 0) {
pr_error("Ciplus set output can only work at dsc0 device\n");
return;
}
switch (dsc->source) {
case AM_TS_SRC_DMX0:
in = 0;
break;
case AM_TS_SRC_DMX1:
in = 1;
break;
case AM_TS_SRC_DMX2:
in = 2;
break;
default:
break;
}
if (ciplus_out_auto_mode == 1) {
/* Set ciplus input source */
data = READ_MPEG_REG(STB_TOP_CONFIG);
data &= ~(3<<CIPLUS_IN_SEL);
data |= in << CIPLUS_IN_SEL;
WRITE_MPEG_REG(STB_TOP_CONFIG, data);
pr_inf("dsc ciplus in[%x]\n", in);
}
}
int dsc_enable(struct aml_dsc *dsc, int enable)
{
if (dsc->id == 0) {
WRITE_MPEG_REG(STB_TOP_CONFIG,
READ_MPEG_REG(STB_TOP_CONFIG) &
~((0x11 << DES_INPUT_SEL)|
(1 << ENABLE_DES_PL)|
(1 << ENABLE_DES_PL_CLK)));
} else if (dsc->id == 1) {
WRITE_MPEG_REG(COMM_DESC_2_CTL, 0);
}
return 0;
}
/*Set section buffer*/
static int dmx_alloc_sec_buffer(struct aml_dmx *dmx)
{
unsigned long base;
unsigned long grp_addr[SEC_BUF_GRP_COUNT];
int grp_len[SEC_BUF_GRP_COUNT];
int i;
if (dmx->sec_pages)
return 0;
grp_len[0] = (1 << SEC_GRP_LEN_0) * 8;
grp_len[1] = (1 << SEC_GRP_LEN_1) * 8;
grp_len[2] = (1 << SEC_GRP_LEN_2) * 8;
grp_len[3] = (1 << SEC_GRP_LEN_3) * 8;
dmx->sec_total_len = grp_len[0] + grp_len[1] + grp_len[2] + grp_len[3];
dmx->sec_pages =
__get_free_pages(GFP_KERNEL, get_order(dmx->sec_total_len));
if (!dmx->sec_pages) {
pr_error("cannot allocate section buffer %d bytes %d order\n",
dmx->sec_total_len, get_order(dmx->sec_total_len));
return -1;
}
dmx->sec_pages_map =
dma_map_single(dmx_get_dev(dmx), (void *)dmx->sec_pages,
dmx->sec_total_len, DMA_FROM_DEVICE);
grp_addr[0] = dmx->sec_pages_map;
grp_addr[1] = grp_addr[0] + grp_len[0];
grp_addr[2] = grp_addr[1] + grp_len[1];
grp_addr[3] = grp_addr[2] + grp_len[2];
dmx->sec_buf[0].addr = dmx->sec_pages;
dmx->sec_buf[0].len = grp_len[0] / 8;
for (i = 1; i < SEC_BUF_COUNT; i++) {
dmx->sec_buf[i].addr =
dmx->sec_buf[i - 1].addr + dmx->sec_buf[i - 1].len;
dmx->sec_buf[i].len = grp_len[i / 8] / 8;
}
base = grp_addr[0] & 0xFFFF0000;
DMX_WRITE_REG(dmx->id, SEC_BUFF_BASE, base >> 16);
DMX_WRITE_REG(dmx->id, SEC_BUFF_01_START,
(((grp_addr[0] - base) >> 8) << 16) |
((grp_addr[1] - base) >> 8));
DMX_WRITE_REG(dmx->id, SEC_BUFF_23_START,
(((grp_addr[2] - base) >> 8) << 16) |
((grp_addr[3] - base) >> 8));
DMX_WRITE_REG(dmx->id, SEC_BUFF_SIZE,
SEC_GRP_LEN_0 |
(SEC_GRP_LEN_1 << 4) |
(SEC_GRP_LEN_2 << 8) |
(SEC_GRP_LEN_3 << 12));
return 0;
}
#ifdef NO_SUB
/*Set subtitle buffer*/
static int dmx_alloc_sub_buffer(struct aml_dvb *dvb, struct aml_dmx *dmx)
{
#ifdef SUB_BUF_DMX
unsigned long addr;
if (dmx->sub_pages)
return 0;
/*check if use shared buf*/
if (dvb->sub_pages) {
dmx->sub_pages = dvb->sub_pages;
dmx->sub_buf_len = dvb->sub_buf_len;
dmx->sub_pages_map = dvb->sub_pages_map;
goto end_alloc;
}
dmx->sub_buf_len = 64 * 1024;
dmx->sub_pages =
__get_free_pages(GFP_KERNEL, get_order(dmx->sub_buf_len));
if (!dmx->sub_pages) {
pr_error("cannot allocate subtitle buffer\n");
return -1;
}
dmx->sub_pages_map =
dma_map_single(dmx_get_dev(dmx), (void *)dmx->sub_pages,
dmx->sub_buf_len, DMA_FROM_DEVICE);
end_alloc:
addr = virt_to_phys((void *)dmx->sub_pages);
#ifndef SUB_PARSER
DMX_WRITE_REG(dmx->id, SB_START, addr >> 12);
DMX_WRITE_REG(dmx->id, SB_LAST_ADDR, (dmx->sub_buf_len >> 3) - 1);
#endif
if (dmx->sub_pages != dvb->sub_pages) {
pr_dbg("sub buff: (%d) %lx %x\n",
dmx->id, addr, dmx->sub_buf_len);
}
#endif
return 0;
}
#ifdef SUB_BUF_SHARED
static int dmx_alloc_sub_buffer_shared(struct aml_dvb *dvb)
{
#ifdef SUB_BUF_DMX
if (dvb->sub_pages)
return 0;
dvb->sub_buf_len = 64 * 1024;
dvb->sub_pages =
__get_free_pages(GFP_KERNEL, get_order(dvb->sub_buf_len));
if (!dvb->sub_pages) {
pr_error("cannot allocate subtitle buffer\n");
return -1;
}
dvb->sub_pages_map =
dma_map_single(dvb->dev, (void *)dvb->sub_pages,
dvb->sub_buf_len, DMA_FROM_DEVICE);
pr_dbg("sub buff shared: %lx %x\n",
(unsigned long)virt_to_phys((void *)dvb->sub_pages),
dvb->sub_buf_len);
#endif
return 0;
}
#endif
#endif /*NO_SUB */
/*Set PES buffer*/
static int dmx_alloc_pes_buffer(struct aml_dvb *dvb, struct aml_dmx *dmx)
{
unsigned long addr;
if (dmx->pes_pages)
return 0;
/*check if use shared buf*/
if (dvb->pes_pages) {
dmx->pes_pages = dvb->pes_pages;
dmx->pes_buf_len = dvb->pes_buf_len;
dmx->pes_pages_map = dvb->pes_pages_map;
goto end_alloc;
}
dmx->pes_buf_len = 64 * 1024;
dmx->pes_pages =
__get_free_pages(GFP_KERNEL, get_order(dmx->pes_buf_len));
if (!dmx->pes_pages) {
pr_error("cannot allocate pes buffer\n");
return -1;
}
dmx->pes_pages_map =
dma_map_single(dmx_get_dev(dmx), (void *)dmx->pes_pages,
dmx->pes_buf_len, DMA_FROM_DEVICE);
end_alloc:
addr = virt_to_phys((void *)dmx->pes_pages);
DMX_WRITE_REG(dmx->id, OB_START, addr >> 12);
DMX_WRITE_REG(dmx->id, OB_LAST_ADDR, (dmx->pes_buf_len >> 3) - 1);
if (dmx->pes_pages != dvb->pes_pages) {
pr_dbg("pes buff: (%d) %lx %x\n",
dmx->id, addr, dmx->pes_buf_len);
}
return 0;
}
#ifdef PES_BUF_SHARED
static int dmx_alloc_pes_buffer_shared(struct aml_dvb *dvb)
{
if (dvb->pes_pages)
return 0;
dvb->pes_buf_len = 64 * 1024;
dvb->pes_pages =
__get_free_pages(GFP_KERNEL, get_order(dvb->pes_buf_len));
if (!dvb->pes_pages) {
pr_error("cannot allocate pes buffer\n");
return -1;
}
dvb->pes_pages_map =
dma_map_single(dvb->dev, (void *)dvb->pes_pages,
dvb->pes_buf_len, DMA_FROM_DEVICE);
pr_dbg("pes buff shared: %lx %x\n",
(unsigned long)virt_to_phys((void *)dvb->pes_pages),
dvb->pes_buf_len);
return 0;
}
#endif
/*Allocate ASYNC FIFO Buffer*/
static unsigned long asyncfifo_alloc_buffer(struct aml_asyncfifo *afifo, int len)
{
if (!afifo->stored_pages) {
afifo->stored_pages = __get_free_pages(GFP_KERNEL, get_order(len));
}
if (!afifo->stored_pages) {
pr_error("cannot allocate async fifo buffer\n");
return 0;
}
return afifo->stored_pages;
}
static void asyncfifo_free_buffer(unsigned long buf, int len)
{
//free_pages(buf, get_order(len));
}
static int asyncfifo_set_buffer(struct aml_asyncfifo *afifo,
int len, unsigned long buf)
{
if (afifo->pages)
return -1;
afifo->buf_toggle = 0;
afifo->buf_read = 0;
afifo->buf_len = dmx_get_afifo_size(afifo);
pr_dbg("async fifo %d buf %lu buf size %d, flush size %d, secure_enable %d, blk.addr %u\n",
afifo->id, buf, afifo->buf_len, afifo->flush_size, afifo->secure_enable, afifo->blk.addr);
if ((afifo->flush_size <= 0)
|| (afifo->flush_size > (len>>1))) {
afifo->flush_size = len>>1;
} else if (afifo->flush_size < 128) {
afifo->flush_size = 128;
} else {
int fsize;
for (fsize = 128; fsize < (len>>1); fsize <<= 1) {
if (fsize >= afifo->flush_size)
break;
}
afifo->flush_size = fsize;
}
afifo->pages = buf;
if (!afifo->pages)
return -1;
afifo->pages_map = dma_map_single(asyncfifo_get_dev(afifo),
(void *)afifo->pages, len, DMA_FROM_DEVICE);
return 0;
}
static void asyncfifo_put_buffer(struct aml_asyncfifo *afifo)
{
if (afifo->pages) {
dma_unmap_single(asyncfifo_get_dev(afifo),
afifo->pages_map, asyncfifo_buf_len, DMA_FROM_DEVICE);
asyncfifo_free_buffer(afifo->pages, asyncfifo_buf_len);
afifo->pages_map = 0;
afifo->pages = 0;
}
}
int async_fifo_init(struct aml_asyncfifo *afifo, int initirq,
int buf_len, unsigned long buf)
{
int ret = 0;
int irq;
if (afifo->init)
return -1;
afifo->source = AM_DMX_MAX;
afifo->pages = 0;
afifo->buf_toggle = 0;
afifo->buf_read = 0;
afifo->buf_len = 0;
if (afifo->asyncfifo_irq == -1) {
pr_error("no irq for ASYNC_FIFO%d\n", afifo->id);
/*Do not return error*/
return -1;
}
tasklet_init(&afifo->asyncfifo_tasklet,
dvr_irq_bh_handler, (unsigned long)afifo);
if (initirq)
irq = request_irq(afifo->asyncfifo_irq, dvr_irq_handler,
IRQF_SHARED|IRQF_TRIGGER_RISING,
"dvr irq", afifo);
else
enable_irq(afifo->asyncfifo_irq);
/*alloc buffer*/
ret = asyncfifo_set_buffer(afifo, buf_len, buf);
afifo->init = 1;
return ret;
}
int async_fifo_deinit(struct aml_asyncfifo *afifo, int freeirq)
{
struct aml_dvb *dvb = afifo->dvb;
unsigned long flags;
if (!afifo->init)
return 0;
spin_lock_irqsave(&dvb->slock, flags);
CLEAR_ASYNC_FIFO_REG_MASK(afifo->id, REG1, 1 << ASYNC_FIFO_FLUSH_EN);
CLEAR_ASYNC_FIFO_REG_MASK(afifo->id, REG2, 1 << ASYNC_FIFO_FILL_EN);
spin_unlock_irqrestore(&dvb->slock, flags);
asyncfifo_put_buffer(afifo);
afifo->source = AM_DMX_MAX;
afifo->buf_toggle = 0;
afifo->buf_read = 0;
afifo->buf_len = 0;
if (afifo->asyncfifo_irq != -1) {
if (freeirq)
free_irq(afifo->asyncfifo_irq, afifo);
else
disable_irq(afifo->asyncfifo_irq);
}
tasklet_kill(&afifo->asyncfifo_tasklet);
afifo->init = 0;
return 0;
}
static int _dmx_smallsec_enable(struct aml_smallsec *ss, int bufsize)
{
if (!ss->buf) {
ss->buf = __get_free_pages(GFP_KERNEL,
get_order(bufsize));
if (!ss->buf) {
pr_error("cannot allocate smallsec buffer\n"
"%d bytes %d order\n",
bufsize, get_order(bufsize));
return -1;
}
ss->buf_map = dma_map_single(dmx_get_dev(ss->dmx),
(void *)ss->buf,
bufsize, DMA_FROM_DEVICE);
}
DMX_WRITE_REG(ss->dmx->id, DEMUX_SMALL_SEC_ADDR,
ss->buf_map);
DMX_WRITE_REG(ss->dmx->id, DEMUX_SMALL_SEC_CTL,
((((bufsize>>8)-1)&0xff)<<24) |
(1<<1) |/*enable reset the wr ptr*/
(1<<0));
ss->bufsize = bufsize;
ss->enable = 1;
pr_inf("demux%d smallsec buf start: %lx, size: %d\n",
ss->dmx->id, ss->buf, ss->bufsize);
return 0;
}
static int _dmx_smallsec_disable(struct aml_smallsec *ss)
{
DMX_WRITE_REG(ss->dmx->id, DEMUX_SMALL_SEC_CTL, 0);
if (ss->buf) {
dma_unmap_single(dmx_get_dev(ss->dmx), ss->buf_map,
ss->bufsize, DMA_FROM_DEVICE);
free_pages(ss->buf, get_order(ss->bufsize));
ss->buf = 0;
ss->buf_map = 0;
}
ss->enable = 0;
pr_inf("demux%d smallsec buf disable\n", ss->dmx->id);
return 0;
}
static int dmx_smallsec_set(struct aml_smallsec *ss, int enable, int bufsize,
int force)
{
if (!enable) {/*disable*/
if (ss->enable || force)
_dmx_smallsec_disable(ss);
} else {/*enable*/
if (bufsize < 0)
bufsize = SS_BUFSIZE_DEF;
else if (!bufsize)
bufsize = ss->bufsize;
else {
/*unit:FF max:FF00*/
bufsize &= ~0xFF;
bufsize &= 0x1FF00;
}
if ((ss->enable && (bufsize != ss->bufsize)) || force)
_dmx_smallsec_disable(ss);
if (!ss->enable)
_dmx_smallsec_enable(ss, bufsize);
}
return 0;
}
static int _dmx_timeout_enable(struct aml_dmxtimeout *dto, int timeout,
int ch_dis, int match)
{
DMX_WRITE_REG(dto->dmx->id, DEMUX_INPUT_TIMEOUT_C, ch_dis);
DMX_WRITE_REG(dto->dmx->id, DEMUX_INPUT_TIMEOUT,
((!!match)<<31) |
(timeout&0x7fffffff));
dto->ch_disable = ch_dis;
dto->match = match;
dto->timeout = timeout;
dto->trigger = 0;
dto->enable = 1;
pr_inf("demux%d timeout enable:timeout(%d),ch(0x%x),match(%d)\n",
dto->dmx->id, dto->timeout, dto->ch_disable, dto->match);
return 0;
}
static int _dmx_timeout_disable(struct aml_dmxtimeout *dto)
{
DMX_WRITE_REG(dto->dmx->id, DEMUX_INPUT_TIMEOUT, 0);
dto->enable = 0;
dto->trigger = 0;
pr_inf("demux%d timeout disable\n", dto->dmx->id);
return 0;
}
static int dmx_timeout_set(struct aml_dmxtimeout *dto, int enable,
int timeout, int ch_dis, int match,
int force)
{
if (!enable) {/*disable*/
if (dto->enable || force)
_dmx_timeout_disable(dto);
} else {/*enable*/
if (timeout < 0) {
timeout = DTO_TIMEOUT_DEF;
ch_dis = DTO_CHDIS_VAS;
match = dto->match;
} else if (!timeout) {
timeout = dto->timeout;
ch_dis = dto->ch_disable;
match = dto->match;
}
if ((dto->enable && (timeout != dto->timeout))
|| force)
_dmx_timeout_disable(dto);
if (!dto->enable)
_dmx_timeout_enable(dto, timeout, ch_dis, match);
}
return 0;
}
/*Initialize the registers*/
static int dmx_init(struct aml_dmx *dmx)
{
struct aml_dvb *dvb = (struct aml_dvb *)dmx->demux.priv;
int irq;
int ret = 0;
char buf[32];
u32 value = 0;
if (dmx->init)
return 0;
pr_dbg("[dmx_kpi] %s Enter\n", __func__);
memset(buf, 0, 32);
snprintf(buf, sizeof(buf), "asyncfifo_buf_len");
ret = of_property_read_u32(dvb->pdev->dev.of_node, buf, &value);
if (!ret) {
pr_inf("%s: 0x%x\n", buf, value);
asyncfifo_buf_len = value;
}
/*Register irq handlers */
if (dmx->dmx_irq != -1) {
pr_dbg("request irq\n");
tasklet_init(&dmx->dmx_tasklet,
dmx_irq_bh_handler,
(unsigned long)dmx);
irq = request_irq(dmx->dmx_irq, dmx_irq_handler,
IRQF_SHARED|IRQF_TRIGGER_RISING,
"dmx irq", dmx);
}
/*Allocate buffer */
if (dmx_alloc_sec_buffer(dmx) < 0)
return -1;
#ifdef NO_SUB
#ifdef SUB_BUF_SHARED
if (dmx_alloc_sub_buffer_shared(dvb) < 0)
return -1;
#endif
if (dmx_alloc_sub_buffer(dvb, dmx) < 0)
return -1;
#endif
#ifdef PES_BUF_SHARED
if (dmx_alloc_pes_buffer_shared(dvb) < 0)
return -1;
#endif
if (dmx_alloc_pes_buffer(dvb, dmx) < 0)
return -1;
/*Reset the hardware */
if (!dvb->dmx_init) {
timer_setup(&dvb->watchdog_timer, section_buffer_watchdog_func,0);
#ifdef ENABLE_SEC_BUFF_WATCHDOG
mod_timer(&dvb->watchdog_timer,jiffies + msecs_to_jiffies(WATCHDOG_TIMER));
#endif
dmx_reset_hw(dvb);
}
dvb->dmx_init++;
memset(dmx->sec_buf_watchdog_count, 0,
sizeof(dmx->sec_buf_watchdog_count));
dmx->om_status_error_count = 0;
dmx->init = 1;
pr_dbg("[dmx_kpi] %s Exit\n", __func__);
return 0;
}
/*Release the resource*/
static int dmx_deinit(struct aml_dmx *dmx)
{
struct aml_dvb *dvb = (struct aml_dvb *)dmx->demux.priv;
pr_dbg("[dmx_kpi] %s Enter\n", __func__);
if (!dmx->init)
return 0;
DMX_WRITE_REG(dmx->id, DEMUX_CONTROL, 0);
dvb->dmx_init--;
/*Reset the hardware */
if (!dvb->dmx_init) {
dmx_reset_hw(dvb);
#ifdef ENABLE_SEC_BUFF_WATCHDOG
del_timer_sync(&dvb->watchdog_timer);
#endif
}
if (dmx->sec_pages) {
dma_unmap_single(dmx_get_dev(dmx), dmx->sec_pages_map,
dmx->sec_total_len, DMA_FROM_DEVICE);
free_pages(dmx->sec_pages, get_order(dmx->sec_total_len));
dmx->sec_pages = 0;
dmx->sec_pages_map = 0;
}
#ifdef NO_SUB
#ifdef SUB_BUF_DMX
#ifdef SUB_BUF_SHARED
if (dvb->sub_pages) {
dma_unmap_single(dvb->dev, dvb->sub_pages_map,
dvb->sub_buf_len, DMA_FROM_DEVICE);
free_pages(dvb->sub_pages, get_order(dvb->sub_buf_len));
dvb->sub_pages = 0;
}
dmx->sub_pages = 0;
#else
if (dmx->sub_pages) {
dma_unmap_single(dmx_get_dev(dmx), dmx->sub_pages_map,
dmx->sub_buf_len, DMA_FROM_DEVICE);
free_pages(dmx->sub_pages, get_order(dmx->sub_buf_len));
dmx->sub_pages = 0;
}
#endif
#endif
#endif
#ifdef PES_BUF_SHARED
if (dvb->pes_pages) {
dma_unmap_single(dvb->dev, dvb->pes_pages_map,
dvb->pes_buf_len, DMA_FROM_DEVICE);
free_pages(dvb->pes_pages, get_order(dvb->pes_buf_len));
dvb->pes_pages = 0;
}
dmx->pes_pages = 0;
#else
if (dmx->pes_pages) {
dma_unmap_single(dmx_get_dev(dmx), dmx->pes_pages_map,
dmx->pes_buf_len, DMA_FROM_DEVICE);
free_pages(dmx->pes_pages, get_order(dmx->pes_buf_len));
dmx->pes_pages = 0;
}
#endif
if (dmx->dmx_irq != -1) {
free_irq(dmx->dmx_irq, dmx);
tasklet_kill(&dmx->dmx_tasklet);
}
dmx->init = 0;
pr_dbg("[dmx_kpi] %s Exit\n", __func__);
return 0;
}
/*Check the record flag*/
static int dmx_get_record_flag(struct aml_dmx *dmx)
{
int i, linked = 0, record_flag = 0;
struct aml_dvb *dvb = (struct aml_dvb *)dmx->demux.priv;
/*Check whether a async fifo connected to this dmx */
for (i = 0; i < dvb->async_fifo_total_count; i++) {
if (!dvb->asyncfifo[i].init)
continue;
if ((dvb->asyncfifo[i].source == dmx->id)
/*&& !(dvb->swfilter.user && (i==SF_AFIFO_ID)) */
/*sf mode reserved */
) {
linked = 1;
break;
}
}
for (i = 0; i < CHANNEL_COUNT; i++) {
if (dmx->channel[i].used && dmx->channel[i].dvr_feed) {
if (!dmx->record) {
pr_error("dmx_get_record_flag set record dmx->id: %d\n", dmx->id);
dmx->record = 1;
if (linked) {
/*A new record will start,
* must reset the async fifos for
* linking the right demux
*/
reset_async_fifos(dvb);
}
}
if (linked)
record_flag = 1;
goto find_done;
}
}
if (dmx->record) {
pr_error("dmx_get_record_flag clear record dmx->id: %d\n", dmx->id);
dmx->record = 0;
if (linked) {
/*A record will stop, reset the async fifos
*for linking the right demux
*/
reset_async_fifos(dvb);
}
}
find_done:
return record_flag;
}
static void dmx_cascade_set(int cur_dmx, int source) {
int fec_sel_demux = 0;
int data;
switch (source) {
case AM_TS_SRC_DMX0:
case AM_TS_SRC_DMX1:
case AM_TS_SRC_DMX2:
fec_sel_demux = source -AM_TS_SRC_DMX0;
break;
default:
fec_sel_demux = cur_dmx;
break;
}
data = READ_MPEG_REG(TS_TOP_CONFIG1);
data &= ~(0x3 << (cur_dmx*2));
data |= (fec_sel_demux << (cur_dmx*2));
WRITE_MPEG_REG(TS_TOP_CONFIG1,data);
pr_dbg("%s id:%d, source:%d data:0x%0x\n",__FUNCTION__,cur_dmx,fec_sel_demux,data);
}
/*Enable the demux device*/
static int dmx_enable(struct aml_dmx *dmx)
{
struct aml_dvb *dvb = (struct aml_dvb *)dmx->demux.priv;
int fec_sel, hi_bsf, fec_ctrl, record;
int fec_core_sel = 0;
int set_stb = 0, fec_s = 0;
int s2p_id;
u32 invert0 = 0, invert1 = 0, invert2 = 0, fec_s0 = 0, fec_s1 = 0, fec_s2 = 0;
u32 use_sop = 0;
int i = 0;
record = dmx_get_record_flag(dmx);
if (use_of_sop == 1) {
use_sop = 1;
pr_inf("dmx use of sop input\r\n");
}
switch (dmx->source) {
case AM_TS_SRC_TS0:
fec_sel = 0;
fec_ctrl = dvb->ts[0].control;
record = record ? 1 : 0;
break;
case AM_TS_SRC_TS1:
fec_sel = 1;
fec_ctrl = dvb->ts[1].control;
record = record ? 1 : 0;
break;
case AM_TS_SRC_TS2:
fec_sel = 2;
fec_ctrl = dvb->ts[2].control;
record = record ? 1 : 0;
break;
case AM_TS_SRC_TS3:
fec_sel = 3;
fec_ctrl = dvb->ts[3].control;
record = record ? 1 : 0;
break;
case AM_TS_SRC_S_TS0:
case AM_TS_SRC_S_TS1:
case AM_TS_SRC_S_TS2:
s2p_id = 0;
fec_ctrl = 0;
if (dmx->source == AM_TS_SRC_S_TS0) {
s2p_id = 0;
} else if (dmx->source == AM_TS_SRC_S_TS1) {
s2p_id = 1;
} else if (dmx->source == AM_TS_SRC_S_TS2) {
s2p_id = 2;
}
for (i = 0; i < dvb->s2p_total_count; i++) {
if (dvb->ts[i].s2p_id == s2p_id) {
fec_ctrl = dvb->ts[i].control;
}
}
//fec_sel = (s2p_id == 1) ? 5 : 6;
fec_sel = 6 - s2p_id;
record = record ? 1 : 0;
set_stb = 1;
fec_s = dmx->source - AM_TS_SRC_S_TS0;
break;
case AM_TS_SRC_HIU:
fec_sel = 7;
fec_ctrl = 0;
/*
support record in HIU mode
record = 0;
*/
break;
case AM_TS_SRC_HIU1:
fec_sel = 8;
fec_ctrl = 0;
/*
support record in HIU mode
record = 0;
*/
break;
case AM_TS_SRC_DMX0:
case AM_TS_SRC_DMX1:
case AM_TS_SRC_DMX2:
fec_sel = -1;
fec_ctrl = 0;
record = record ? 1 : 0;
break;
default:
fec_sel = 0;
fec_ctrl = 0;
record = 0;
break;
}
if (dmx->channel[0].used || dmx->channel[1].used) {
hi_bsf = 1;
if (fec_sel == 8) {
hi_bsf = 2; /*hi_bsf select hiu1*/
}
}else {
hi_bsf = 0;
}
if ((dvb->dsc[0].dst != -1)
&& ((dvb->dsc[0].dst - AM_TS_SRC_DMX0) == dmx->id))
fec_core_sel = 1;
if ((dvb->dsc[1].dst != -1)
&& ((dvb->dsc[1].dst - AM_TS_SRC_DMX0) == dmx->id)) {
int des_in, des_out, en_des = 0;
switch (dvb->dsc[1].source) {
case AM_TS_SRC_DMX0:
des_in = 0;
en_des = 1;
break;
case AM_TS_SRC_DMX1:
des_in = 1;
en_des = 1;
break;
case AM_TS_SRC_DMX2:
des_in = 2;
en_des = 1;
break;
default:
des_in = 0;
en_des = 0;
break;
}
switch (dvb->dsc[1].dst) {
case AM_TS_SRC_DMX0:
des_out = 1;
break;
case AM_TS_SRC_DMX1:
des_out = 2;
break;
case AM_TS_SRC_DMX2:
des_out = 4;
break;
default:
des_out = 0;
break;
}
if (!des_out)
en_des = 0;
WRITE_MPEG_REG(COMM_DESC_2_CTL,
(6 << 8) |/*des_out_dly_2*/
((!!en_des) << 6) |/* des_pl_clk_2*/
((!!en_des) << 5) |/* des_pl_2*/
(des_out << 2) |/*use_des_2*/
(des_in)/*des_i_sel_2*/
);
fec_core_sel = 1;
pr_dbg("dsc2 ctrl: 0x%x\n", READ_MPEG_REG(COMM_DESC_2_CTL));
}
pr_dbg("[dmx-%d]src: %d, rec: %d, hi_bsf: %d, dsc: %d\n",
dmx->id, dmx->source, record, hi_bsf, fec_core_sel);
if (dmx->chan_count) {
if (set_stb) {
u32 v = READ_MPEG_REG(STB_TOP_CONFIG);
int i;
for (i = 0; i < dvb->ts_in_total_count; i++) {
if (dvb->ts[i].s2p_id == 0)
fec_s0 = i;
else if (dvb->ts[i].s2p_id == 1)
fec_s1 = i;
else if (dvb->ts[i].s2p_id == 2)
fec_s2 = i;
}
invert0 = dvb->s2p[0].invert;
invert1 = dvb->s2p[1].invert;
v &= ~((0x3 << S2P0_FEC_SERIAL_SEL) |
(0x1f << INVERT_S2P0_FEC_CLK) |
(0x3 << S2P1_FEC_SERIAL_SEL) |
(0x1f << INVERT_S2P1_FEC_CLK));
v |= (fec_s0 << S2P0_FEC_SERIAL_SEL) |
(invert0 << INVERT_S2P0_FEC_CLK) |
(fec_s1 << S2P1_FEC_SERIAL_SEL) |
(invert1 << INVERT_S2P1_FEC_CLK);
WRITE_MPEG_REG(STB_TOP_CONFIG, v);
if (get_cpu_type() >= MESON_CPU_MAJOR_ID_TL1) {
invert2 = dvb->s2p[2].invert;
//add s2p2 config
v = READ_MPEG_REG(STB_S2P2_CONFIG);
v &= ~((0x3 << S2P2_FEC_SERIAL_SEL) |
(0x1f << INVERT_S2P2_FEC_CLK));
v |= (fec_s2 << S2P2_FEC_SERIAL_SEL) |
(invert2 << INVERT_S2P2_FEC_CLK);
WRITE_MPEG_REG(STB_S2P2_CONFIG, v);
}
}
/*Initialize the registers */
DMX_WRITE_REG(dmx->id, STB_INT_MASK, DEMUX_INT_MASK);
DMX_WRITE_REG(dmx->id, DEMUX_MEM_REQ_EN,
#ifdef USE_AHB_MODE
(1 << SECTION_AHB_DMA_EN) |
(0 << SUB_AHB_DMA_EN) |
(1 << OTHER_PES_AHB_DMA_EN) |
#endif
(1 << SECTION_PACKET) |
(1 << VIDEO_PACKET) |
(1 << AUDIO_PACKET) |
(1 << SUB_PACKET) |
(1 << SCR_ONLY_PACKET) |
(1 << OTHER_PES_PACKET));
DMX_WRITE_REG(dmx->id, PES_STRONG_SYNC, 0x1234);
DMX_WRITE_REG(dmx->id, DEMUX_ENDIAN,
(1<<SEPERATE_ENDIAN) |
(0<<OTHER_PES_ENDIAN) |
(7<<SCR_ENDIAN) |
(7<<SUB_ENDIAN) |
(7<<AUDIO_ENDIAN) |
(7<<VIDEO_ENDIAN) |
(7 << OTHER_ENDIAN) |
(7 << BYPASS_ENDIAN) | (0 << SECTION_ENDIAN));
if (fec_sel != 8) {
DMX_WRITE_REG(dmx->id, TS_HIU_CTL,
// (0 << LAST_BURST_THRESHOLD) |
(hi_bsf << USE_HI_BSF_INTERFACE));
} else {
DMX_WRITE_REG(dmx->id, TS_HIU_CTL,
(1 << PDTS_WR_SEL) |
(hi_bsf << USE_HI_BSF_INTERFACE));
}
if (!fec_core_sel) {
u32 cfg = READ_MPEG_REG(CIPLUS_CONFIG);
if (cfg & (1 << CNTL_ENABLE)) {
if (!ciplus_out_auto_mode) {
int mask = 1 << dmx->id;
if (ciplus_out_sel & mask)
fec_core_sel = 1;
}
}
}
if (fec_sel == -1) {
dmx_cascade_set(dmx->id,dmx->source);
DMX_WRITE_REG(dmx->id, FEC_INPUT_CONTROL,
(fec_core_sel << FEC_CORE_SEL) |
(0 << FEC_SEL) | (fec_ctrl << 0));
} else {
dmx_cascade_set(dmx->id,dmx->source);
if (fec_sel != 8) {
DMX_WRITE_REG(dmx->id, FEC_INPUT_CONTROL,
(fec_core_sel << FEC_CORE_SEL) |
(fec_sel << FEC_SEL) | (fec_ctrl << 0));
} else {
DMX_WRITE_REG(dmx->id, FEC_INPUT_CONTROL,
(fec_core_sel << FEC_CORE_SEL) |
(1 << FEC_SEL_3BIT) | (fec_ctrl << 0));
}
}
DMX_WRITE_REG(dmx->id, STB_OM_CTL,
(0x40 << MAX_OM_DMA_COUNT) |
(0x7f << LAST_OM_ADDR));
/*RECORDER_STREAM depends on video2*/
/*VIDEO_STREAM_ID: video2_stream_id (bit[31:16])*/
/*DEMUX_CONTROL:
bit[25] video2_en
bit[24:22] video2_type*/
#define VIDEO2_FOR_RECORDER_STREAM (1 << 25 | 7 << 22)
DMX_WRITE_REG(dmx->id, VIDEO_STREAM_ID,
((record) ? 0xFFFF0000 : 0));
DMX_WRITE_REG(dmx->id, DEMUX_CONTROL,
(0 << BYPASS_USE_RECODER_PATH) |
(0 << INSERT_AUDIO_PES_STRONG_SYNC) |
(0 << INSERT_VIDEO_PES_STRONG_SYNC) |
(0 << OTHER_INT_AT_PES_BEGINING) |
(0 << DISCARD_AV_PACKAGE) |
((!!dmx->dump_ts_select) << TS_RECORDER_SELECT) |
(record << TS_RECORDER_ENABLE) |
(1 << KEEP_DUPLICATE_PACKAGE) |
(1 << SECTION_END_WITH_TABLE_ID) |
(1 << ENABLE_FREE_CLK_FEC_DATA_VALID) |
(1 << ENABLE_FREE_CLK_STB_REG) |
(1 << STB_DEMUX_ENABLE) |
(use_sop << NOT_USE_OF_SOP_INPUT) |
((record)? VIDEO2_FOR_RECORDER_STREAM : 0));
pr_dbg("dmx control[%#x]\n",
DMX_READ_REG(dmx->id, DEMUX_CONTROL));
} else {
DMX_WRITE_REG(dmx->id, STB_INT_MASK, 0);
/* if disable FEC_INPUT_CONTROL, background and unattended record will fail */
// DMX_WRITE_REG(dmx->id, FEC_INPUT_CONTROL, 0);
DMX_WRITE_REG(dmx->id, DEMUX_CONTROL, 0);
//dmx not used, but it can cascade for other dmx
if ((dmx->source == AM_TS_SRC_DMX0 ||
dmx->source == AM_TS_SRC_DMX1 ||
dmx->source == AM_TS_SRC_DMX2 ) &&
(dmx->id != dmx->source-AM_TS_SRC_DMX0))
dmx_cascade_set(dmx->id,dmx->source);
}
return 0;
}
static int dmx_set_misc(struct aml_dmx *dmx, int hi_bsf, int en_dsc)
{
if (hi_bsf >= 0) {
DMX_WRITE_REG(dmx->id, TS_HIU_CTL,
hi_bsf ?
(DMX_READ_REG(dmx->id, TS_HIU_CTL) |
(1 << USE_HI_BSF_INTERFACE))
:
(DMX_READ_REG(dmx->id, TS_HIU_CTL) &
(~(1 << USE_HI_BSF_INTERFACE))));
}
if (en_dsc >= 0) {
DMX_WRITE_REG(dmx->id, FEC_INPUT_CONTROL,
en_dsc ?
(DMX_READ_REG(dmx->id, FEC_INPUT_CONTROL) |
(1 << FEC_CORE_SEL))
:
(DMX_READ_REG(dmx->id, FEC_INPUT_CONTROL) &
(~(1 << FEC_CORE_SEL))));
}
return 0;
}
static int dmx_set_misc_id(struct aml_dvb *dvb, int id, int hi_bsf, int en_dsc)
{
return dmx_set_misc(&dvb->dmx[id], hi_bsf, en_dsc);
}
/*Get the channel's ID by its PID*/
static int dmx_get_chan(struct aml_dmx *dmx, int pid)
{
int id;
for (id = 0; id < CHANNEL_COUNT; id++) {
if (dmx->channel[id].used && dmx->channel[id].pid == pid)
return id;
}
return -1;
}
/*Get the channel's target*/
static u32 dmx_get_chan_target(struct aml_dmx *dmx, int cid)
{
u32 type;
if (!dmx->channel[cid].used)
return (0x7 << PID_TYPE) | g_chan_def_pid;
if (dmx->channel[cid].type == DMX_TYPE_SEC) {
type = SECTION_PACKET;
} else {
switch (dmx->channel[cid].pes_type) {
case DMX_PES_AUDIO:
type = AUDIO_PACKET;
break;
case DMX_PES_VIDEO:
type = VIDEO_PACKET;
break;
case DMX_PES_SUBTITLE:
case DMX_PES_TELETEXT:
type = SUB_PACKET;
break;
case DMX_PES_PCR:
type = SCR_ONLY_PACKET;
break;
case DMX_PES_AUDIO3:
type = OTHER_PES_PACKET;
break;
default:
type = RECORDER_STREAM;
break;
}
}
dmx->channel[cid].pkt_type = type;
pr_dbg("chan target: %x %x\n", type, dmx->channel[cid].pid);
return (type << PID_TYPE) | dmx->channel[cid].pid;
}
/*Get the advance value of the channel*/
static inline u32 dmx_get_chan_advance(struct aml_dmx *dmx, int cid)
{
return 0;
}
/*Set the channel registers*/
static int dmx_set_chan_regs(struct aml_dmx *dmx, int cid)
{
u32 data, addr, advance, max;
pr_dbg("set channel (id:%d PID:0x%x) registers\n", cid,
dmx->channel[cid].pid);
while (DMX_READ_REG(dmx->id, FM_WR_ADDR) & 0x8000)
udelay(1);
if (cid & 1) {
data =
(dmx_get_chan_target(dmx, cid - 1) << 16) |
dmx_get_chan_target(dmx, cid);
advance =
(dmx_get_chan_advance(dmx, cid) << 8) |
dmx_get_chan_advance(dmx, cid - 1);
if (dmx->channel[cid - 1].used)
set_debug_dmx_chanpids_types(dmx->id, cid - 1,
dmx->channel[cid - 1].pkt_type);
} else {
data =
(dmx_get_chan_target(dmx, cid) << 16) |
dmx_get_chan_target(dmx, cid + 1);
advance =
(dmx_get_chan_advance(dmx, cid + 1) << 8) |
dmx_get_chan_advance(dmx, cid);
if (dmx->channel[cid + 1].used)
set_debug_dmx_chanpids_types(dmx->id, cid + 1,
dmx->channel[cid + 1].pkt_type);
}
addr = cid >> 1;
DMX_WRITE_REG(dmx->id, FM_WR_DATA, data);
DMX_WRITE_REG(dmx->id, FM_WR_ADDR, (advance << 16) | 0x8000 | addr);
pr_dbg("write fm %x:%x\n", (advance << 16) | 0x8000 | addr, data);
for (max = CHANNEL_COUNT - 1; max > 0; max--) {
if (dmx->channel[max].used)
break;
}
data = DMX_READ_REG(dmx->id, MAX_FM_COMP_ADDR) & 0xF0;
DMX_WRITE_REG(dmx->id, MAX_FM_COMP_ADDR, data | (max >> 1));
pr_dbg("write fm comp %x\n", data | (max >> 1));
if (DMX_READ_REG(dmx->id, OM_CMD_STATUS) & 0x8e00) {
pr_error("warning: send cmd %x\n",
DMX_READ_REG(dmx->id, OM_CMD_STATUS));
}
if (cid == 0) {
video_pts = 0;
first_video_pts = 0;
}
else if (cid == 1) {
audio_pts = 0;
first_audio_pts = 0;
}
if (dmx->channel[cid].used)
set_debug_dmx_chanpids_types(dmx->id, cid,
dmx->channel[cid].pkt_type);
return 0;
}
/*Get the filter target*/
static int dmx_get_filter_target(struct aml_dmx *dmx, int fid, u32 *target,
u8 *advance)
{
struct dmx_section_filter *filter;
struct aml_filter *f;
int i, cid, neq_bytes;
fid = fid & 0xFFFF;
f = &dmx->filter[fid];
if (!f->used) {
target[0] = 0x1fff;
advance[0] = 0;
for (i = 1; i < FILTER_LEN; i++) {
target[i] = 0x9fff;
advance[i] = 0;
}
return 0;
}
cid = f->chan_id;
filter = f->filter;
neq_bytes = 0;
if (filter->filter_mode[0] != 0xFF) {
neq_bytes = 2;
} else {
for (i = 3; i < FILTER_LEN; i++) {
if (filter->filter_mode[i] != 0xFF)
neq_bytes++;
}
}
f->neq = 0;
for (i = 0; i < FILTER_LEN; i++) {
u8 value = filter->filter_value[i];
u8 mask = filter->filter_mask[i];
u8 mode = filter->filter_mode[i];
u8 mb, mb1, nb, v, t, adv = 0;
if (!i) {
mb = 1;
mb1 = 1;
v = 0;
if ((mode == 0xFF) && mask) {
t = mask & 0xF0;
if (t) {
mb1 = 0;
adv |= t^0xF0;
}
v |= (value & 0xF0) | adv;
t = mask & 0x0F;
if (t) {
mb = 0;
adv |= t^0x0F;
}
v |= (value & 0x0F) | adv;
}
target[i] = (mb << SECTION_FIRSTBYTE_MASKLOW) |
(mb1 << SECTION_FIRSTBYTE_MASKHIGH) |
(0 << SECTION_FIRSTBYTE_DISABLE_PID_CHECK) |
(cid << SECTION_FIRSTBYTE_PID_INDEX) | v;
advance[i] = adv;
} else {
if (i < 3) {
value = 0;
mask = 0;
mode = 0xff;
}
mb = 1;
nb = 0;
v = 0;
if ((i >= 3) && mask) {
if (mode == 0xFF) {
mb = 0;
nb = 0;
adv = mask ^ 0xFF;
v = value | adv;
} else {
if (neq_bytes == 1) {
mb = 0;
nb = 1;
adv = mask ^ 0xFF;
v = value & ~adv;
}
}
}
target[i] = (mb << SECTION_RESTBYTE_MASK) |
(nb << SECTION_RESTBYTE_MASK_EQ) |
(0 << SECTION_RESTBYTE_DISABLE_PID_CHECK) |
(cid << SECTION_RESTBYTE_PID_INDEX) | v;
advance[i] = adv;
}
f->value[i] = value;
f->maskandmode[i] = mask & mode;
f->maskandnotmode[i] = mask & ~mode;
if (f->maskandnotmode[i])
f->neq = 1;
}
return 0;
}
/*Set the filter registers*/
static int dmx_set_filter_regs(struct aml_dmx *dmx, int fid)
{
u32 t1[FILTER_LEN], t2[FILTER_LEN];
u8 advance1[FILTER_LEN], advance2[FILTER_LEN];
u32 addr, data, max, adv;
int i;
pr_dbg("set filter (id:%d) registers\n", fid);
if (fid & 1) {
dmx_get_filter_target(dmx, fid - 1, t1, advance1);
dmx_get_filter_target(dmx, fid, t2, advance2);
} else {
dmx_get_filter_target(dmx, fid, t1, advance1);
dmx_get_filter_target(dmx, fid + 1, t2, advance2);
}
for (i = 0; i < FILTER_LEN; i++) {
while (DMX_READ_REG(dmx->id, FM_WR_ADDR) & 0x8000)
udelay(1);
data = (t1[i] << 16) | t2[i];
addr = (fid >> 1) | ((i + 1) << 4);
adv = (advance1[i] << 8) | advance2[i];
DMX_WRITE_REG(dmx->id, FM_WR_DATA, data);
DMX_WRITE_REG(dmx->id, FM_WR_ADDR, (adv << 16) | 0x8000 | addr);
pr_dbg("write fm %x:%x\n", (adv << 16) | 0x8000 | addr, data);
}
for (max = FILTER_COUNT - 1; max > 0; max--) {
if (dmx->filter[max].used)
break;
}
data = DMX_READ_REG(dmx->id, MAX_FM_COMP_ADDR) & 0xF;
DMX_WRITE_REG(dmx->id, MAX_FM_COMP_ADDR, data | ((max >> 1) << 4));
pr_dbg("write fm comp %x\n", data | ((max >> 1) << 4));
if (DMX_READ_REG(dmx->id, OM_CMD_STATUS) & 0x8e00) {
pr_error("error send cmd %x\n",
DMX_READ_REG(dmx->id, OM_CMD_STATUS));
}
return 0;
}
/*Clear the filter's buffer*/
static void dmx_clear_filter_buffer(struct aml_dmx *dmx, int fid)
{
u32 section_busy32 = DMX_READ_REG(dmx->id, SEC_BUFF_READY);
u32 filter_number;
int i;
if (!section_busy32)
return;
for (i = 0; i < SEC_BUF_COUNT; i++) {
if (section_busy32 & (1 << i)) {
DMX_WRITE_REG(dmx->id, SEC_BUFF_NUMBER, i);
filter_number =
(DMX_READ_REG(dmx->id, SEC_BUFF_NUMBER) >> 8);
if (filter_number != fid)
section_busy32 &= ~(1 << i);
}
}
if (section_busy32)
DMX_WRITE_REG(dmx->id, SEC_BUFF_READY, section_busy32);
}
static void async_fifo_disable(struct aml_asyncfifo *afifo)
{
pr_inf("AF(%d) disable asyncfifo\n", afifo->id);
CLEAR_ASYNC_FIFO_REG_MASK(afifo->id, REG1, 1 << ASYNC_FIFO_FLUSH_EN);
CLEAR_ASYNC_FIFO_REG_MASK(afifo->id, REG2, 1 << ASYNC_FIFO_FILL_EN);
if (READ_ASYNC_FIFO_REG(afifo->id, REG2) & (1 << ASYNC_FIFO_FILL_EN)
|| READ_ASYNC_FIFO_REG(afifo->id, REG1)
& (1 << ASYNC_FIFO_FLUSH_EN)) {
pr_error("disable failed\n");
} else
pr_inf("disable ok\n");
afifo->buf_toggle = 0;
afifo->buf_read = 0;
}
static void async_fifo_set_regs(struct aml_asyncfifo *afifo, int source_val)
{
u32 start_addr = (afifo->secure_enable && afifo->blk.addr)?
afifo->blk.addr : virt_to_phys((void *)afifo->pages);
u32 size = afifo->buf_len;
u32 flush_size = afifo->flush_size;
int factor = dmx_get_order(size / flush_size);
u32 old_size, new_size, old_factor, new_factor;
int old_src, old_en;
old_en = READ_ASYNC_FIFO_REG(afifo->id, REG2)
& (1 << ASYNC_FIFO_FILL_EN);
old_src =
(READ_ASYNC_FIFO_REG(afifo->id, REG2) >> ASYNC_FIFO_SOURCE_LSB)
& 3;
new_size = (size >> 7) & 0x7fff;
old_size =
(READ_ASYNC_FIFO_REG(afifo->id, REG1)
>> ASYNC_FIFO_FLUSH_CNT_LSB)
& 0x7fff;
old_factor =
(READ_ASYNC_FIFO_REG(afifo->id, REG3)
>> ASYNC_FLUSH_SIZE_IRQ_LSB)
& 0x7fff;
new_factor = ((size >> (factor + 7)) - 1) & 0x7fff;
pr_inf("AF(%d) [%s] src:0x%x->0x%x size:0x%x->0x%x factor:0x%x->0x%x\n",
afifo->id,
old_en? "on" : "off",
old_src, source_val,
old_size, new_size,
old_factor, new_factor);
if (old_en
&& (old_src == source_val)
&& (new_size == old_size)
&& (old_factor == new_factor))
return;
if (old_en) {
if ((old_size == new_size)
&& (old_factor == new_factor)) {
/*only source changed, do not reset all*/
/* Connect the DEMUX to ASYNC_FIFO */
WRITE_ASYNC_FIFO_REG(afifo->id, REG2,
(READ_ASYNC_FIFO_REG(afifo->id, REG2)
& ~(0x3 << ASYNC_FIFO_SOURCE_LSB))
| (source_val << ASYNC_FIFO_SOURCE_LSB));
return;
} else {
/*Dynamic change setting is not supported,
*should disable it first.
*Data discontinue will be here.
*/
async_fifo_disable(afifo);
}
}
pr_inf("ASYNC FIFO id=%d, link to DMX%d, start_addr %x, buf_size %d,"
"source value 0x%x, factor %d\n",
afifo->id, afifo->source, start_addr, size, source_val, factor);
/* Destination address */
WRITE_ASYNC_FIFO_REG(afifo->id, REG0, start_addr);
/* Setup flush parameters */
WRITE_ASYNC_FIFO_REG(afifo->id, REG1,
(0 << ASYNC_FIFO_TO_HIU) |
(0 << ASYNC_FIFO_FLUSH) |
/* don't flush the path */
(1 << ASYNC_FIFO_RESET) |
/* reset the path */
(1 << ASYNC_FIFO_WRAP_EN) |
/* wrap enable */
(0 << ASYNC_FIFO_FLUSH_EN) |
/* disable the flush path */
/*(0x3 << ASYNC_FIFO_FLUSH_CNT_LSB);
* flush 3 x 32 32-bit words
*/
/*(0x7fff << ASYNC_FIFO_FLUSH_CNT_LSB);
* flush 4MBytes of data
*/
(((size >> 7) & 0x7fff) << ASYNC_FIFO_FLUSH_CNT_LSB));
/* number of 128-byte blocks to flush */
/* clear the reset signal */
WRITE_ASYNC_FIFO_REG(afifo->id, REG1,
READ_ASYNC_FIFO_REG(afifo->id,
REG1) & ~(1 << ASYNC_FIFO_RESET));
/* Enable flush */
WRITE_ASYNC_FIFO_REG(afifo->id, REG1,
READ_ASYNC_FIFO_REG(afifo->id,
REG1) | (1 << ASYNC_FIFO_FLUSH_EN));
/*Setup Fill parameters */
WRITE_ASYNC_FIFO_REG(afifo->id, REG2,
(1 << ASYNC_FIFO_ENDIAN_LSB) |
(0 << ASYNC_FIFO_FILL_EN) |
/* disable fill path to reset fill path */
/*(96 << ASYNC_FIFO_FILL_CNT_LSB);
*3 x 32 32-bit words
*/
(0 << ASYNC_FIFO_FILL_CNT_LSB));
/* forever FILL; */
WRITE_ASYNC_FIFO_REG(afifo->id, REG2,
READ_ASYNC_FIFO_REG(afifo->id, REG2) |
(1 << ASYNC_FIFO_FILL_EN));/*Enable fill path*/
/* generate flush interrupt */
WRITE_ASYNC_FIFO_REG(afifo->id, REG3,
(READ_ASYNC_FIFO_REG(afifo->id, REG3) & 0xffff0000) |
((((size >> (factor + 7)) - 1) & 0x7fff) <<
ASYNC_FLUSH_SIZE_IRQ_LSB));
/* Connect the STB DEMUX to ASYNC_FIFO */
WRITE_ASYNC_FIFO_REG(afifo->id, REG2,
READ_ASYNC_FIFO_REG(afifo->id, REG2) |
(source_val << ASYNC_FIFO_SOURCE_LSB));
}
/*Reset the ASYNC FIFOS when a ASYNC FIFO connect to a different DMX*/
static void reset_async_fifos(struct aml_dvb *dvb)
{
struct aml_asyncfifo *low_dmx_fifo = NULL;
struct aml_asyncfifo *high_dmx_fifo = NULL;
struct aml_asyncfifo *highest_dmx_fifo = NULL;
int i, j;
int record_enable;
struct aml_asyncfifo *afifo = NULL;
for (j = 0; j < DMX_DEV_COUNT; j++) {
if (!dvb->dmx[j].init)
continue;
record_enable = 0;
for (i = 0; i < dvb->async_fifo_total_count; i++) {
afifo = &dvb->asyncfifo[i];
if (!afifo->init)
continue;
if (!dvb->dmx[j].record
|| !(dvb->dmx[j].id == afifo->source))
continue;
/*This dmx is linked to the async fifo,
*Enable the TS_RECORDER_ENABLE
*/
record_enable = 1;
if (!low_dmx_fifo) {
low_dmx_fifo = afifo;
} else if (low_dmx_fifo->source >
afifo->source) {
if (!high_dmx_fifo)
high_dmx_fifo = low_dmx_fifo;
else {
highest_dmx_fifo = high_dmx_fifo;
high_dmx_fifo = low_dmx_fifo;
}
low_dmx_fifo = afifo;
} else if (low_dmx_fifo->source < afifo->source) {
if (!high_dmx_fifo)
high_dmx_fifo = afifo;
else {
if (high_dmx_fifo->source >
afifo->source) {
highest_dmx_fifo =
high_dmx_fifo;
high_dmx_fifo = afifo;
} else {
highest_dmx_fifo = afifo;
}
}
}
break;
}
pr_inf("Set DMX%d TS_RECORDER_ENABLE to %d\n", dvb->dmx[j].id,
record_enable ? 1 : 0);
if (record_enable) {
int old_en =
DMX_READ_REG(dvb->dmx[j].id, DEMUX_CONTROL)
& (1 << TS_RECORDER_ENABLE);
if (!old_en) {
DMX_WRITE_REG(dvb->dmx[j].id, DEMUX_CONTROL,
DMX_READ_REG(dvb->dmx[j].id,
DEMUX_CONTROL)
| (1 << TS_RECORDER_ENABLE));
}
} else {
int old_en =
DMX_READ_REG(dvb->dmx[j].id, DEMUX_CONTROL)
& (1 << TS_RECORDER_ENABLE);
if (old_en) {
DMX_WRITE_REG(dvb->dmx[j].id, DEMUX_CONTROL,
DMX_READ_REG(dvb->dmx[j].id,
DEMUX_CONTROL)
& (~(1 << TS_RECORDER_ENABLE)));
}
}
}
pr_inf("reset ASYNC FIFOs\n");
for (i = 0; i < dvb->async_fifo_total_count; i++) {
int old;
afifo = &dvb->asyncfifo[i];
if (!afifo->init)
continue;
old = READ_ASYNC_FIFO_REG(afifo->id, REG2)
& (1 << ASYNC_FIFO_FILL_EN);
if (old
&& (afifo != low_dmx_fifo)
&& (afifo != high_dmx_fifo)
&& (afifo != highest_dmx_fifo))
async_fifo_disable(afifo);
}
/*Set the async fifo regs */
if (low_dmx_fifo) {
async_fifo_set_regs(low_dmx_fifo, 0x3);
if (high_dmx_fifo) {
async_fifo_set_regs(high_dmx_fifo, 0x2);
if (highest_dmx_fifo)
async_fifo_set_regs(highest_dmx_fifo, 0x0);
}
}
}
/*Reset the demux device*/
void dmx_reset_hw(struct aml_dvb *dvb)
{
dmx_reset_hw_ex(dvb, 1);
}
/*Reset the demux device*/
void dmx_reset_hw_ex(struct aml_dvb *dvb, int reset_irq)
{
int id, times;
u32 pcr_num[DMX_DEV_COUNT];
u32 pcr_reg[DMX_DEV_COUNT];
pr_dbg("[dmx_kpi] demux reset begin\n");
for (id = 0; id < DMX_DEV_COUNT; id++) {
if (!dvb->dmx[id].init)
continue;
pcr_reg[id] = DMX_READ_REG(id, PCR90K_CTL);
pcr_num[id] = DMX_READ_REG(id, ASSIGN_PID_NUMBER);
pr_dbg("reset demux, pcr_regs[%d]:0x%x, pcr_num[%d]:0x%x\n", id, pcr_reg[id], id, pcr_num[id]);
if (reset_irq) {
if (dvb->dmx[id].dmx_irq != -1)
disable_irq(dvb->dmx[id].dmx_irq);
if (dvb->dmx[id].dvr_irq != -1)
disable_irq(dvb->dmx[id].dvr_irq);
}
}
#ifdef ENABLE_SEC_BUFF_WATCHDOG
if (reset_irq)
del_timer_sync(&dvb->watchdog_timer);
#endif
/*RESET_TOP will clear the dsc pid , save all dsc pid that setting in TA*/
for (id = 0; id < DSC_DEV_COUNT; id++) {
struct aml_dsc *dsc = &dvb->dsc[id];
int n;
for (n = 0; n < DSC_COUNT; n++) {
struct aml_dsc_channel *ch = &dsc->channel[n];
/*if(ch->used)*/
{
ch->id = n;
dsc_get_pid(ch,&ch->pid);
}
}
}
/*{
u32 data;
data = READ_MPEG_REG(STB_TOP_CONFIG);
ciplus = 0x7C000000 & data;
}*/
WRITE_MPEG_REG(RESET1_REGISTER, RESET_DEMUXSTB);
/*WRITE_MPEG_REG(RESET3_REGISTER, RESET_DEMUX2|RESET_DEMUX1|RESET_DEMUX0|RESET_S2P1|RESET_S2P0|RESET_TOP);*/
for (id = 0; id < DMX_DEV_COUNT; id++) {
times = 0;
while (times++ < 1000000) {
if (!(DMX_READ_REG(id, OM_CMD_STATUS) & 0x01))
break;
}
}
WRITE_MPEG_REG(STB_TOP_CONFIG, 0);
WRITE_MPEG_REG(STB_S2P2_CONFIG, 0);
for (id = 0; id < DMX_DEV_COUNT; id++) {
u32 version, data;
if (!dvb->dmx[id].init)
continue;
if (reset_irq) {
if (dvb->dmx[id].dmx_irq != -1)
enable_irq(dvb->dmx[id].dmx_irq);
if (dvb->dmx[id].dvr_irq != -1)
enable_irq(dvb->dmx[id].dvr_irq);
}
DMX_WRITE_REG(id, DEMUX_CONTROL, 0x0000);
version = DMX_READ_REG(id, STB_VERSION);
DMX_WRITE_REG(id, STB_TEST_REG, version);
pr_dbg("STB %d hardware version : %d\n", id, version);
DMX_WRITE_REG(id, STB_TEST_REG, 0x5550);
data = DMX_READ_REG(id, STB_TEST_REG);
if (data != 0x5550)
pr_error("STB %d register access failed\n", id);
DMX_WRITE_REG(id, STB_TEST_REG, 0xaaa0);
data = DMX_READ_REG(id, STB_TEST_REG);
if (data != 0xaaa0)
pr_error("STB %d register access failed\n", id);
DMX_WRITE_REG(id, MAX_FM_COMP_ADDR, 0x0000);
DMX_WRITE_REG(id, STB_INT_MASK, 0);
DMX_WRITE_REG(id, STB_INT_STATUS, 0xffff);
DMX_WRITE_REG(id, FEC_INPUT_CONTROL, 0);
}
stb_enable(dvb);
for (id = 0; id < DMX_DEV_COUNT; id++) {
struct aml_dmx *dmx = &dvb->dmx[id];
int n;
unsigned long addr;
unsigned long base;
unsigned long grp_addr[SEC_BUF_GRP_COUNT];
int grp_len[SEC_BUF_GRP_COUNT];
if (!dvb->dmx[id].init)
continue;
if (dmx->sec_pages) {
grp_len[0] = (1 << SEC_GRP_LEN_0) * 8;
grp_len[1] = (1 << SEC_GRP_LEN_1) * 8;
grp_len[2] = (1 << SEC_GRP_LEN_2) * 8;
grp_len[3] = (1 << SEC_GRP_LEN_3) * 8;
grp_addr[0] = virt_to_phys((void *)dmx->sec_pages);
grp_addr[1] = grp_addr[0] + grp_len[0];
grp_addr[2] = grp_addr[1] + grp_len[1];
grp_addr[3] = grp_addr[2] + grp_len[2];
base = grp_addr[0] & 0xFFFF0000;
DMX_WRITE_REG(dmx->id, SEC_BUFF_BASE, base >> 16);
DMX_WRITE_REG(dmx->id, SEC_BUFF_01_START,
(((grp_addr[0] - base) >> 8) << 16) |
((grp_addr[1] - base) >> 8));
DMX_WRITE_REG(dmx->id, SEC_BUFF_23_START,
(((grp_addr[2] - base) >> 8) << 16) |
((grp_addr[3] - base) >> 8));
DMX_WRITE_REG(dmx->id, SEC_BUFF_SIZE,
SEC_GRP_LEN_0 |
(SEC_GRP_LEN_1 << 4) |
(SEC_GRP_LEN_2 << 8) |
(SEC_GRP_LEN_3 << 12));
}
#ifdef NO_SUB
#ifndef SUB_PARSER
if (dmx->sub_pages) {
addr = virt_to_phys((void *)dmx->sub_pages);
DMX_WRITE_REG(dmx->id, SB_START, addr >> 12);
DMX_WRITE_REG(dmx->id, SB_LAST_ADDR,
(dmx->sub_buf_len >> 3) - 1);
}
#endif
#endif
if (dmx->pes_pages) {
addr = virt_to_phys((void *)dmx->pes_pages);
DMX_WRITE_REG(dmx->id, OB_START, addr >> 12);
DMX_WRITE_REG(dmx->id, OB_LAST_ADDR,
(dmx->pes_buf_len >> 3) - 1);
}
for (n = 0; n < CHANNEL_COUNT; n++) {
/*struct aml_channel *chan = &dmx->channel[n];*/
/*if (chan->used)*/
{
#ifdef NO_SUB
#ifdef SUB_PARSER
/*
check if subtitle channel was running,
the parser will be used in amstream also,
take care of the buff ptr.
*/
u32 v = dmx_get_chan_target(dmx, n);
if (v != 0xFFFF &&
(v & (0x7 << PID_TYPE))
== (SUB_PACKET << PID_TYPE))
set_subtitle_pes_buffer(dmx);
#endif
#endif
{
u32 v = dmx_get_chan_target(dmx, n);
if (v != 0xFFFF &&
(v & (0x7 << PID_TYPE))
==
(OTHER_PES_PACKET << PID_TYPE))
pes_off_pre[dmx->id] = 0;
}
dmx_set_chan_regs(dmx, n);
}
}
for (n = 0; n < FILTER_COUNT; n++) {
struct aml_filter *filter = &dmx->filter[n];
if (filter->used)
dmx_set_filter_regs(dmx, n);
}
dmx_enable(&dvb->dmx[id]);
dmx_smallsec_set(&dmx->smallsec,
dmx->smallsec.enable,
dmx->smallsec.bufsize,
1);
dmx_timeout_set(&dmx->timeout,
dmx->timeout.enable,
dmx->timeout.timeout,
dmx->timeout.ch_disable,
dmx->timeout.match,
1);
DMX_WRITE_REG(id, ASSIGN_PID_NUMBER, pcr_num[id]);
DMX_WRITE_REG(id, PCR90K_CTL, pcr_reg[id]);
}
for (id = 0; id < DSC_DEV_COUNT; id++) {
struct aml_dsc *dsc = &dvb->dsc[id];
int n;
for (n = 0; n < DSC_COUNT; n++) {
int flag = 0;
struct aml_dsc_channel *ch = &dsc->channel[n];
/*if(ch->used)*/
{
ch->work_mode = -1;
//if ta setting pid, used will 0
if (ch->pid != 0x1fff && !ch->used) {
flag = 1;
ch->used = 1;
}
dsc_set_pid(ch, ch->pid);
if (flag)
ch->used = 0;
dsc_set_keys(ch);
}
}
}
#ifdef ENABLE_SEC_BUFF_WATCHDOG
if (reset_irq) {
mod_timer(&dvb->watchdog_timer,
jiffies + msecs_to_jiffies(WATCHDOG_TIMER));
}
#endif
pr_dbg("[dmx_kpi] demux reset end\n");
}
/*Reset the individual demux*/
void dmx_reset_dmx_hw_ex_unlock(struct aml_dvb *dvb, struct aml_dmx *dmx,
int reset_irq)
{
u32 pcr_num = 0;
u32 pcr_regs = 0;
{
if (!dmx->init)
return;
pcr_regs = DMX_READ_REG(dmx->id, PCR90K_CTL);
pcr_num = DMX_READ_REG(dmx->id, ASSIGN_PID_NUMBER);
pr_dbg("reset demux, pcr_regs:0x%x, pcr_num:0x%x\n", pcr_regs, pcr_num);
}
{
if (!dmx->init)
return;
if (reset_irq) {
if (dmx->dmx_irq != -1)
disable_irq(dmx->dmx_irq);
if (dmx->dvr_irq != -1)
disable_irq(dmx->dvr_irq);
}
}
#ifdef ENABLE_SEC_BUFF_WATCHDOG
if (reset_irq) {
/*del_timer_sync(&dvb->watchdog_timer); */
dvb->dmx_watchdog_disable[dmx->id] = 1;
}
#endif
/*{
u32 data;
data = READ_MPEG_REG(STB_TOP_CONFIG);
ciplus = 0x7C000000 & data;
}*/
pr_error("dmx_reset_dmx_hw_ex_unlock into\n");
WRITE_MPEG_REG(RESET3_REGISTER,
(dmx->id) ? ((dmx->id ==
1) ? RESET_DEMUX1 : RESET_DEMUX2) :
RESET_DEMUX0);
WRITE_MPEG_REG(RESET3_REGISTER, RESET_DES);
{
int times;
times = 0;
while (times++ < 1000000) {
if (!(DMX_READ_REG(dmx->id, OM_CMD_STATUS) & 0x01))
break;
}
}
/*WRITE_MPEG_REG(STB_TOP_CONFIG, 0); */
{
u32 version, data;
if (!dmx->init)
return;
if (reset_irq) {
if (dmx->dmx_irq != -1)
enable_irq(dmx->dmx_irq);
if (dmx->dvr_irq != -1)
enable_irq(dmx->dvr_irq);
}
DMX_WRITE_REG(dmx->id, DEMUX_CONTROL, 0x0000);
version = DMX_READ_REG(dmx->id, STB_VERSION);
DMX_WRITE_REG(dmx->id, STB_TEST_REG, version);
pr_dbg("STB %d hardware version : %d\n", dmx->id, version);
DMX_WRITE_REG(dmx->id, STB_TEST_REG, 0x5550);
data = DMX_READ_REG(dmx->id, STB_TEST_REG);
if (data != 0x5550)
pr_error("STB %d register access failed\n", dmx->id);
DMX_WRITE_REG(dmx->id, STB_TEST_REG, 0xaaa0);
data = DMX_READ_REG(dmx->id, STB_TEST_REG);
if (data != 0xaaa0)
pr_error("STB %d register access failed\n", dmx->id);
DMX_WRITE_REG(dmx->id, MAX_FM_COMP_ADDR, 0x0000);
DMX_WRITE_REG(dmx->id, STB_INT_MASK, 0);
DMX_WRITE_REG(dmx->id, STB_INT_STATUS, 0xffff);
DMX_WRITE_REG(dmx->id, FEC_INPUT_CONTROL, 0);
}
stb_enable(dvb);
{
int n;
unsigned long addr;
unsigned long base;
unsigned long grp_addr[SEC_BUF_GRP_COUNT];
int grp_len[SEC_BUF_GRP_COUNT];
if (!dmx->init)
return;
if (dmx->sec_pages) {
grp_len[0] = (1 << SEC_GRP_LEN_0) * 8;
grp_len[1] = (1 << SEC_GRP_LEN_1) * 8;
grp_len[2] = (1 << SEC_GRP_LEN_2) * 8;
grp_len[3] = (1 << SEC_GRP_LEN_3) * 8;
grp_addr[0] = virt_to_phys((void *)dmx->sec_pages);
grp_addr[1] = grp_addr[0] + grp_len[0];
grp_addr[2] = grp_addr[1] + grp_len[1];
grp_addr[3] = grp_addr[2] + grp_len[2];
base = grp_addr[0] & 0xFFFF0000;
DMX_WRITE_REG(dmx->id, SEC_BUFF_BASE, base >> 16);
DMX_WRITE_REG(dmx->id, SEC_BUFF_01_START,
(((grp_addr[0] - base) >> 8) << 16) |
((grp_addr[1] - base) >> 8));
DMX_WRITE_REG(dmx->id, SEC_BUFF_23_START,
(((grp_addr[2] - base) >> 8) << 16) |
((grp_addr[3] - base) >> 8));
DMX_WRITE_REG(dmx->id, SEC_BUFF_SIZE,
SEC_GRP_LEN_0 |
(SEC_GRP_LEN_1 << 4) |
(SEC_GRP_LEN_2 << 8) |
(SEC_GRP_LEN_3 << 12));
}
#ifdef NO_SUB
#ifndef SUB_PARSER
if (dmx->sub_pages) {
addr = virt_to_phys((void *)dmx->sub_pages);
DMX_WRITE_REG(dmx->id, SB_START, addr >> 12);
DMX_WRITE_REG(dmx->id, SB_LAST_ADDR,
(dmx->sub_buf_len >> 3) - 1);
}
#endif
#endif
if (dmx->pes_pages) {
addr = virt_to_phys((void *)dmx->pes_pages);
DMX_WRITE_REG(dmx->id, OB_START, addr >> 12);
DMX_WRITE_REG(dmx->id, OB_LAST_ADDR,
(dmx->pes_buf_len >> 3) - 1);
}
for (n = 0; n < CHANNEL_COUNT; n++) {
/*struct aml_channel *chan = &dmx->channel[n];*/
/*if (chan->used)*/
{
#ifdef NO_SUB
#ifdef SUB_PARSER
/*
check if subtitle channel was running,
the parser will be used in amstream also,
take care of the buff ptr.
*/
u32 v = dmx_get_chan_target(dmx, n);
if (v != 0xFFFF &&
(v & (0x7 << PID_TYPE))
== (SUB_PACKET << PID_TYPE))
set_subtitle_pes_buffer(dmx);
#endif
#endif
{
u32 v = dmx_get_chan_target(dmx, n);
if (v != 0xFFFF &&
(v & (0x7 << PID_TYPE))
==
(OTHER_PES_PACKET << PID_TYPE))
pes_off_pre[dmx->id] = 0;
}
dmx_set_chan_regs(dmx, n);
}
}
for (n = 0; n < FILTER_COUNT; n++) {
struct aml_filter *filter = &dmx->filter[n];
if (filter->used)
dmx_set_filter_regs(dmx, n);
}
for (n = 0; n < SEC_CNT_MAX; n++) {
dmx->sec_cnt[n] = 0;
dmx->sec_cnt_match[n] = 0;
dmx->sec_cnt_crc_fail[n] = 0;
}
dmx_enable(dmx);
dmx_smallsec_set(&dmx->smallsec,
dmx->smallsec.enable,
dmx->smallsec.bufsize,
1);
dmx_timeout_set(&dmx->timeout,
dmx->timeout.enable,
dmx->timeout.timeout,
dmx->timeout.ch_disable,
dmx->timeout.match,
1);
}
{
int id;
for (id = 0; id < DSC_DEV_COUNT; id++) {
struct aml_dsc *dsc = &dvb->dsc[id];
int n;
for (n = 0; n < DSC_COUNT; n++) {
int flag = 0;
struct aml_dsc_channel *ch = &dsc->channel[n];
/*if(ch->used)*/ {
ch->id = n;
ch->work_mode = -1;
dsc_get_pid(ch,&ch->pid);
if (ch->pid != 0x1fff && !ch->used) {
flag = 1;
ch->used = 1;
}
dsc_set_pid(ch, ch->pid);
if (flag)
ch->used = 0;
dsc_set_keys(ch);
}
}
}
}
#ifdef ENABLE_SEC_BUFF_WATCHDOG
if (reset_irq) {
/*mod_timer(&dvb->watchdog_timer,
*jiffies+msecs_to_jiffies(WATCHDOG_TIMER));
*/
dvb->dmx_watchdog_disable[dmx->id] = 0;
}
#endif
{
DMX_WRITE_REG(dmx->id, ASSIGN_PID_NUMBER, pcr_num);
DMX_WRITE_REG(dmx->id, PCR90K_CTL, pcr_regs);
}
}
void dmx_reset_dmx_id_hw_ex_unlock(struct aml_dvb *dvb, int id, int reset_irq)
{
dmx_reset_dmx_hw_ex_unlock(dvb, &dvb->dmx[id], reset_irq);
}
void dmx_reset_dmx_hw_ex(struct aml_dvb *dvb, struct aml_dmx *dmx,
int reset_irq)
{
unsigned long flags;
spin_lock_irqsave(&dvb->slock, flags);
dmx_reset_dmx_hw_ex_unlock(dvb, dmx, reset_irq);
spin_unlock_irqrestore(&dvb->slock, flags);
}
void dmx_reset_dmx_id_hw_ex(struct aml_dvb *dvb, int id, int reset_irq)
{
unsigned long flags;
spin_lock_irqsave(&dvb->slock, flags);
dmx_reset_dmx_id_hw_ex_unlock(dvb, id, reset_irq);
spin_unlock_irqrestore(&dvb->slock, flags);
}
void dmx_reset_dmx_hw(struct aml_dvb *dvb, int id)
{
dmx_reset_dmx_id_hw_ex(dvb, id, 1);
}
/*Allocate subtitle pes buffer*/
#if 0
static int alloc_subtitle_pes_buffer(struct aml_dmx *dmx)
{
int start_ptr = 0;
struct stream_buf_s *sbuff = 0;
u32 phy_addr;
start_ptr = READ_MPEG_REG(PARSER_SUB_START_PTR);
if (start_ptr) {
WRITE_MPEG_REG(PARSER_SUB_RP, start_ptr);
goto exit;
}
sbuff = get_stream_buffer(BUF_TYPE_SUBTITLE);
if (sbuff) {
if (sbuff->flag & BUF_FLAG_IOMEM)
phy_addr = sbuff->buf_start;
else
phy_addr = virt_to_phys((void *)sbuff->buf_start);
WRITE_MPEG_REG(PARSER_SUB_RP, phy_addr);
WRITE_MPEG_REG(PARSER_SUB_START_PTR, phy_addr);
WRITE_MPEG_REG(PARSER_SUB_END_PTR,
phy_addr + sbuff->buf_size - 8);
pr_dbg("pes buff=:%x %x\n", phy_addr, sbuff->buf_size);
} else
pr_dbg("Error stream buffer\n");
exit:
return 0;
}
#endif
static int set_subtitle_pes_buffer(struct aml_dmx *dmx)
{
#ifdef SUB_PARSER
if (dmx->sub_chan == -1) {
unsigned long addr = virt_to_phys((void *)dmx->sub_pages);
WRITE_MPEG_REG(PARSER_SUB_RP, addr);
WRITE_MPEG_REG(PARSER_SUB_START_PTR, addr);
WRITE_MPEG_REG(PARSER_SUB_END_PTR, addr + dmx->sub_buf_len - 8);
pr_inf("set sub buff: (%d) %lx %x\n", dmx->id, addr, dmx->sub_buf_len);
}
#endif
return 0;
}
int dmx_get_sub_buffer(unsigned long *base, unsigned long *virt)
{
#ifndef SUB_BUF_DMX
unsigned long s = READ_MPEG_REG(PARSER_SUB_START_PTR);
if (base)
*base = s;
if (virt)
*virt = (unsigned long)codec_mm_phys_to_virt(s);
#endif
return 0;
}
int dmx_init_sub_buffer(struct aml_dmx *dmx, unsigned long base, unsigned long virt)
{
#ifndef SUB_BUF_DMX
dmx->sub_buf_base = base;
pr_inf("sub buf base: 0x%lx\n", dmx->sub_buf_base);
dmx->sub_buf_base_virt = (u8 *)virt;
pr_inf("sub buf base virt: 0x%p\n", dmx->sub_buf_base_virt);
#endif
return 0;
}
static int check_dvr_for_raw_channel(struct aml_dmx *dmx, int ch)
{
switch (ch) {
case 0:
case 1: return 1;
case 2: return dmx->sub_chan != -1 ? 1 : 0;
case 3: return dmx->pcr_chan != -1 ? 1 : 0;
default: return 0;
}
return 0;
}
/*Allocate a new channel*/
int dmx_alloc_chan(struct aml_dmx *dmx, int type, int pes_type, int pid)
{
int id = -1;
int ret;
if (type == DMX_TYPE_TS) {
switch (pes_type) {
case DMX_PES_VIDEO:
if (!dmx->channel[0].used)
id = 0;
break;
case DMX_PES_AUDIO:
if (!dmx->channel[1].used)
id = 1;
break;
case DMX_PES_SUBTITLE:
case DMX_PES_TELETEXT:
if (!dmx->channel[2].used)
id = 2;
//alloc_subtitle_pes_buffer(dmx);
set_subtitle_pes_buffer(dmx);
break;
case DMX_PES_PCR:
if (!dmx->channel[3].used)
id = 3;
break;
case DMX_PES_AUDIO3:
pes_off_pre[dmx->id] = 0;
{
int i;
for (i = SYS_CHAN_COUNT;
i < CHANNEL_COUNT; i++) {
if (!dmx->channel[i].used) {
id = i;
break;
}
}
}
break;
case DMX_PES_OTHER:
{
int i;
for (i = SYS_CHAN_COUNT;
i < CHANNEL_COUNT; i++) {
if (!dmx->channel[i].used) {
id = i;
break;
}
}
}
break;
default:
break;
}
} else {
int i;
for (i = SYS_CHAN_COUNT; i < CHANNEL_COUNT; i++) {
if (!dmx->channel[i].used) {
id = i;
break;
}
}
}
if (id == -1) {
pr_error("too many channels\n");
return -1;
}
pr_dbg("allocate channel(id:%d-%d PID:0x%x)\n", dmx->id, id, pid);
if (check_dvr_for_raw_channel(dmx, id)) {
ret = dmx_get_chan(dmx, pid);
if (ret >= 0 && DVR_FEED(dmx->channel[ret].feed)) {
pr_dbg("raw ch fix: dmx:%d: ch[%d(dvr)] -> ch[%d]\n",
dmx->id, ret, id);
dmx_remove_feed(dmx, dmx->channel[ret].feed);
dmx->channel[id].dvr_feed = dmx->channel[ret].feed;
dmx->channel[id].dvr_feed->priv = (void *)(long)id;
} else {
dmx->channel[id].dvr_feed = NULL;
}
}
dmx->channel[id].type = type;
dmx->channel[id].pes_type = pes_type;
dmx->channel[id].pid = pid;
dmx->channel[id].used = 1;
dmx->channel[id].filter_count = 0;
dmx_set_chan_regs(dmx, id);
set_debug_dmx_chanpids(dmx->id, id, pid);
dmx->chan_count++;
dmx_enable(dmx);
return id;
}
/*Free a channel*/
void dmx_free_chan(struct aml_dmx *dmx, int cid)
{
pr_dbg("free channel(id:%d-%d PID:0x%x)\n", dmx->id, cid, dmx->channel[cid].pid);
dmx->channel[cid].used = 0;
dmx->channel[cid].pid = 0x1fff;
dmx_set_chan_regs(dmx, cid);
if (cid == 2) {
u32 parser_sub_start_ptr;
parser_sub_start_ptr = READ_MPEG_REG(PARSER_SUB_START_PTR);
WRITE_MPEG_REG(PARSER_SUB_RP, parser_sub_start_ptr);
WRITE_MPEG_REG(PARSER_SUB_WP, parser_sub_start_ptr);
}
set_debug_dmx_chanpids(dmx->id, cid, -1);
dmx->chan_count--;
dmx_enable(dmx);
/*Special pes type channel, check its dvr feed */
if (check_dvr_for_raw_channel(dmx, cid)
&& dmx->channel[cid].dvr_feed) {
/*start the dvr feed */
pr_dbg("raw ch fix: dmx:%d: ch[%d] -> ch[(dvr)]\n",
dmx->id, cid);
dmx_add_feed(dmx, dmx->channel[cid].dvr_feed);
dmx->channel[cid].dvr_feed = NULL;
}
}
/*Add a section*/
static int dmx_chan_add_filter(struct aml_dmx *dmx, int cid,
struct dvb_demux_filter *filter)
{
int id = -1;
int i;
for (i = 0; i < FILTER_COUNT; i++) {
if (!dmx->filter[i].used) {
id = i;
break;
}
}
if (id == -1) {
pr_error("too many filters\n");
return -1;
}
pr_dbg("channel(id:%d PID:0x%x) add filter(id:%d)\n", cid,
filter->feed->pid, id);
dmx->filter[id].chan_id = cid;
dmx->filter[id].used = 1;
dmx->filter[id].filter = (struct dmx_section_filter *)filter;
dmx->channel[cid].filter_count++;
dmx_set_filter_regs(dmx, id);
return id;
}
static void dmx_remove_filter(struct aml_dmx *dmx, int cid, int fid)
{
pr_dbg("channel(id:%d PID:0x%x) remove filter(id:%d)\n", cid,
dmx->channel[cid].pid, fid);
dmx->filter[fid].used = 0;
dmx->channel[cid].filter_count--;
dmx_set_filter_regs(dmx, fid);
dmx_clear_filter_buffer(dmx, fid);
}
static int sf_add_feed(struct aml_dmx *src_dmx, struct dvb_demux_feed *feed)
{
int ret = 0;
struct aml_dvb *dvb = (struct aml_dvb *)src_dmx->demux.priv;
struct aml_swfilter *sf = &dvb->swfilter;
pr_dbg_sf("sf add pid[%d]\n", feed->pid);
/*init sf */
if (!sf->user) {
void *mem;
mem = vmalloc(SF_BUFFER_SIZE);
if (!mem) {
ret = -ENOMEM;
goto fail;
}
dvb_ringbuffer_init(&sf->rbuf, mem, SF_BUFFER_SIZE);
sf->dmx = &dvb->dmx[SF_DMX_ID];
sf->afifo = &dvb->asyncfifo[SF_AFIFO_ID];
sf->dmx->source = src_dmx->source;
sf->afifo->source = sf->dmx->id;
sf->track_dmx = src_dmx->id;
/*sf->afifo->flush_size = 188*10; */
pr_dbg_sf("init sf mode.\n");
} else if (sf->dmx->source != src_dmx->source) {
pr_error(" pid=%d[src:%d] already used with sfdmx%d[src:%d]\n",
feed->pid, src_dmx->source, sf->dmx->id,
sf->dmx->source);
ret = -EBUSY;
goto fail;
}
/*setup feed */
ret = dmx_get_chan(sf->dmx, feed->pid);
if (ret >= 0) {
pr_error(" pid=%d[dmx:%d] already used [dmx:%d].\n",
feed->pid, src_dmx->id,
((struct aml_dmx *)sf->dmx->channel[ret].feed->
demux)->id);
ret = -EBUSY;
goto fail;
}
ret =
dmx_alloc_chan(sf->dmx, DMX_TYPE_TS, DMX_PES_OTHER,
feed->pid);
if (ret < 0) {
pr_error(" %s: alloc chan error, ret=%d\n", __func__, ret);
ret = -EBUSY;
goto fail;
}
sf->dmx->channel[ret].feed = feed;
feed->priv = (void *)(long)ret;
sf->dmx->channel[ret].dvr_feed = feed;
sf->user++;
debug_sf_user = sf->user;
dmx_enable(sf->dmx);
return 0;
fail:
feed->priv = (void *)-1;
return ret;
}
static int sf_remove_feed(struct aml_dmx *src_dmx, struct dvb_demux_feed *feed)
{
int ret;
struct aml_dvb *dvb = (struct aml_dvb *)src_dmx->demux.priv;
struct aml_swfilter *sf = &dvb->swfilter;
if (!sf->user || (sf->dmx->source != src_dmx->source))
return 0;
/*add fail, no need to remove*/
if (((long)feed->priv) < 0)
return 0;
ret = dmx_get_chan(sf->dmx, feed->pid);
if (ret < 0)
return 0;
pr_dbg_sf("sf remove pid[%d]\n", feed->pid);
dmx_free_chan(sf->dmx, (long)feed->priv);
sf->dmx->channel[ret].feed = NULL;
sf->dmx->channel[ret].dvr_feed = NULL;
sf->user--;
debug_sf_user = sf->user;
if (!sf->user) {
sf->dmx->source = -1;
sf->afifo->source = AM_DMX_MAX;
sf->track_dmx = -1;
/*sf->afifo->flush_size = sf->afifo->buf_len>>1; */
if (sf->rbuf.data) {
void *mem = sf->rbuf.data;
sf->rbuf.data = NULL;
vfree(mem);
}
pr_dbg_sf("exit sf mode.\n");
}
return 0;
}
static int sf_feed_sf(struct aml_dmx *dmx, struct dvb_demux_feed *feed,
int add_not_remove)
{
int sf = 0;
if (sf_dmx_sf(dmx)) {
pr_error("%s: demux %d is in sf mode\n", __func__, dmx->id);
return -EINVAL;
}
switch (feed->type) {
case DMX_TYPE_TS:{
struct dmxdev_filter *dmxdevfilter =
feed->feed.ts.priv;
if (!DVR_FEED(feed)) {
if (dmxdevfilter->params.pes.
flags & DMX_USE_SWFILTER)
sf = 1;
if (force_pes_sf)
sf = 1;
}
}
break;
case DMX_TYPE_SEC:{
struct dvb_demux_filter *filter;
for (filter = feed->filter; filter;
filter = filter->next) {
struct dmxdev_filter *dmxdevfilter =
filter->filter.priv;
if (dmxdevfilter->params.sec.
flags & DMX_USE_SWFILTER)
sf = 1;
if (add_not_remove)
filter->hw_handle = (u16)-1;
}
if (force_sec_sf)
sf = 1;
}
break;
}
return sf ? 0 : 1;
}
static int sf_check_feed(struct aml_dmx *dmx, struct dvb_demux_feed *feed,
int add_not_remove)
{
int ret = 0;
ret = sf_feed_sf(dmx, feed, add_not_remove);
if (ret)
return ret;
pr_dbg_sf("%s [pid:%d] %s\n",
(feed->type == DMX_TYPE_TS) ? "DMX_TYPE_TS" : "DMX_TYPE_SEC",
feed->pid, add_not_remove ? "-> sf mode" : "sf mode ->");
if (add_not_remove)
ret = sf_add_feed(dmx, feed);
else
ret = sf_remove_feed(dmx, feed);
if (ret < 0) {
pr_error("sf %s feed fail[%d]\n",
add_not_remove ? "add" : "remove", ret);
}
return ret;
}
static int dmx_add_feed(struct aml_dmx *dmx, struct dvb_demux_feed *feed)
{
int id, ret = 0;
struct dvb_demux_filter *filter;
struct dvb_demux_feed *dfeed = NULL;
int sf_ret = 0; /*<0:error, =0:sf_on, >0:sf_off */
sf_ret = sf_check_feed(dmx, feed, 1/*SF_FEED_OP_ADD */);
if (sf_ret < 0)
return sf_ret;
switch (feed->type) {
case DMX_TYPE_TS:
pr_dbg("add feed ts: pid:%d-0x%x, (%p)\n",
dmx->id, feed->pid, feed);
ret = dmx_get_chan(dmx, feed->pid);
if (ret >= 0) {
if (DVR_FEED(dmx->channel[ret].feed)) {
if (DVR_FEED(feed)) {
/*dvr feed already work */
pr_error("PID %d already used(DVR)\n",
feed->pid);
ret = -EBUSY;
goto fail;
}
if (sf_ret) {
/*if sf_on, we do not reset the
*previous dvr feed, just load the pes
*feed on the sf, a diffrent data path.
*/
dfeed = dmx->channel[ret].feed;
dmx_remove_feed(dmx, dfeed);
}
} else {
if (DVR_FEED(feed)
&& (!dmx->channel[ret].dvr_feed)) {
/*just store the dvr_feed */
dmx->channel[ret].dvr_feed = feed;
feed->priv = (void *)(long)ret;
if (!dmx->record)
dmx_enable(dmx);
dmx_add_recchan(dmx->id, ret);
return 0;
}
{
pr_error("PID %d already used\n",
feed->pid);
ret = -EBUSY;
goto fail;
}
}
}
if (sf_ret) { /*not sf feed. */
ret =
dmx_alloc_chan(dmx, feed->type,
feed->pes_type, feed->pid);
if (ret < 0) {
pr_dbg("%s: alloc chan error, ret=%d\n",
__func__, ret);
ret = -EBUSY;
goto fail;
}
dmx->channel[ret].feed = feed;
feed->priv = (void *)(long)ret;
dmx->channel[ret].dvr_feed = NULL;
}
/*dvr */
if (DVR_FEED(feed)) {
dmx->channel[ret].dvr_feed = feed;
feed->priv = (void *)(long)ret;
if (!dmx->record)
dmx_enable(dmx);
dmx_add_recchan(dmx->id, ret);
} else if (dfeed && sf_ret) {
dmx->channel[ret].dvr_feed = dfeed;
dfeed->priv = (void *)(long)ret;
if (!dmx->record)
dmx_enable(dmx);
dmx_add_recchan(dmx->id, ret);
}
break;
case DMX_TYPE_SEC:
pr_dbg("add feed sec: pid:%d-0x%x, (%p)\n",
dmx->id, feed->pid, feed);
ret = dmx_get_chan(dmx, feed->pid);
if (ret >= 0) {
if (DVR_FEED(dmx->channel[ret].feed)) {
if (sf_ret) {
/*if sf_on, we do not reset the
*previous dvr feed, just load the pes
*feed on the sf,a diffrent data path.
*/
dfeed = dmx->channel[ret].feed;
dmx_remove_feed(dmx, dfeed);
}
} else {
pr_error("PID %d already used\n", feed->pid);
ret = -EBUSY;
goto fail;
}
}
if (sf_ret) { /*not sf feed. */
id = dmx_alloc_chan(dmx, feed->type,
feed->pes_type, feed->pid);
if (id < 0) {
pr_dbg("%s: alloc chan error, ret=%d\n",
__func__, id);
ret = -EBUSY;
goto fail;
}
for (filter = feed->filter; filter;
filter = filter->next) {
ret = dmx_chan_add_filter(dmx, id, filter);
if (ret >= 0)
filter->hw_handle = ret;
else
filter->hw_handle = (u16)-1;
}
dmx->channel[id].feed = feed;
feed->priv = (void *)(long)id;
dmx->channel[id].dvr_feed = NULL;
if (dfeed) {
dmx->channel[id].dvr_feed = dfeed;
dfeed->priv = (void *)(long)id;
if (!dmx->record)
dmx_enable(dmx);
dmx_add_recchan(dmx->id, id);
}
}
break;
default:
return -EINVAL;
}
dmx->feed_count++;
return 0;
fail:
feed->priv = (void *)-1;
return ret;
}
static int dmx_remove_feed(struct aml_dmx *dmx, struct dvb_demux_feed *feed)
{
struct dvb_demux_filter *filter;
struct dvb_demux_feed *dfeed = NULL;
int sf_ret = 0; /*<0:error, =0:sf_on, >0:sf_off */
/*add fail, no need to remove*/
if (((long)feed->priv) < 0)
return 0;
sf_ret = sf_check_feed(dmx, feed, 0/*SF_FEED_OP_RM */);
if (sf_ret <= 0)
return sf_ret;
switch (feed->type) {
case DMX_TYPE_TS:
pr_dbg("rm feed ts: pid:%d-0x%x, %p\n",
dmx->id, feed->pid, feed);
if (dmx->channel[(long)feed->priv].feed ==
dmx->channel[(long)feed->priv].dvr_feed) {
dmx_rm_recchan(dmx->id, (long)feed->priv);
dmx_free_chan(dmx, (long)feed->priv);
} else {
if (feed == dmx->channel[(long)feed->priv].feed) {
dfeed = dmx->channel[(long)feed->priv].dvr_feed;
dmx_rm_recchan(dmx->id, (long)feed->priv);
dmx_free_chan(dmx, (long)feed->priv);
if (dfeed) {
/*start the dvr feed */
dmx_add_feed(dmx, dfeed);
}
} else if (feed ==
dmx->channel[(long)feed->priv].dvr_feed) {
/*just remove the dvr_feed */
dmx->channel[(long)feed->priv].dvr_feed = NULL;
dmx_rm_recchan(dmx->id, (long)feed->priv);
if (dmx->record)
dmx_enable(dmx);
} else {
/*This must never happen */
pr_error("%s: unknown feed\n", __func__);
return -EINVAL;
}
}
break;
case DMX_TYPE_SEC:
pr_dbg("rm feed sec: pid:%d-0x%x, %p\n",
dmx->id, feed->pid, feed);
for (filter = feed->filter; filter; filter = filter->next) {
if (filter->hw_handle != (u16)-1)
dmx_remove_filter(dmx, (long)feed->priv,
(int)filter->hw_handle);
}
dfeed = dmx->channel[(long)feed->priv].dvr_feed;
dmx_rm_recchan(dmx->id, (long)feed->priv);
dmx_free_chan(dmx, (long)feed->priv);
if (dfeed) {
/*start the dvr feed */
dmx_add_feed(dmx, dfeed);
}
break;
default:
return -EINVAL;
}
dmx->feed_count--;
return 0;
}
int aml_dmx_hw_init(struct aml_dmx *dmx)
{
/*
*struct aml_dvb *dvb = (struct aml_dvb *)dmx->demux.priv;
*unsigned long flags;
*/
int ret;
/*Demux initialize */
/*spin_lock_irqsave(&dvb->slock, flags);*/
ret = dmx_init(dmx);
/*spin_unlock_irqrestore(&dvb->slock, flags);*/
return ret;
}
int aml_dmx_hw_deinit(struct aml_dmx *dmx)
{
struct aml_dvb *dvb = (struct aml_dvb *)dmx->demux.priv;
unsigned long flags;
int ret;
spin_lock_irqsave(&dvb->slock, flags);
ret = dmx_deinit(dmx);
spin_unlock_irqrestore(&dvb->slock, flags);
return ret;
}
/*extern void afifo_reset(int v);*/
int aml_asyncfifo_hw_init(struct aml_asyncfifo *afifo)
{
/*
* struct aml_dvb *dvb = afifo->dvb;
* unsigned long flags;
*/
int ret;
int len = asyncfifo_buf_len;
unsigned long buf = asyncfifo_alloc_buffer(afifo, len);
if (!buf)
return -1;
/*Async FIFO initialize*/
/*
* spin_lock_irqsave(&dvb->slock, flags);
*/
/*
*#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8
* CLK_GATE_ON(ASYNC_FIFO);
*#endif
*/
/*afifo_reset(0);*/
WRITE_MPEG_REG(RESET6_REGISTER, (1<<11)|(1<<12));
ret = async_fifo_init(afifo, 1, len, buf);
/*
* spin_unlock_irqrestore(&dvb->slock, flags);
*/
if (ret < 0)
asyncfifo_free_buffer(buf, len);
return ret;
}
int aml_asyncfifo_hw_deinit(struct aml_asyncfifo *afifo)
{
int ret;
ret = async_fifo_deinit(afifo, 1);
/*
*#if MESON_CPU_TYPE >= MESON_CPU_TYPE_MESON8
* CLK_GATE_OFF(ASYNC_FIFO);
*#endif
*/
/*afifo_reset(1);*/
return ret;
}
int aml_asyncfifo_hw_reset(struct aml_asyncfifo *afifo)
{
struct aml_dvb *dvb = afifo->dvb;
unsigned long flags;
int ret, src = -1;
unsigned long buf = 0;
int len = asyncfifo_buf_len;
buf = asyncfifo_alloc_buffer(afifo, len);
if (!buf)
return -1;
if (afifo->init) {
src = afifo->source;
async_fifo_deinit(afifo, 0);
}
spin_lock_irqsave(&dvb->slock, flags);
ret = async_fifo_init(afifo, 0, len, buf);
/* restore the source */
if (src != -1)
afifo->source = src;
if ((ret == 0) && afifo->dvb)
reset_async_fifos(afifo->dvb);
spin_unlock_irqrestore(&dvb->slock, flags);
if (ret < 0)
asyncfifo_free_buffer(buf, len);
return ret;
}
int aml_dmx_hw_start_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct aml_dmx *dmx = (struct aml_dmx *)dvbdmxfeed->demux;
struct aml_dvb *dvb = (struct aml_dvb *)dmx->demux.priv;
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&dvb->slock, flags);
ret = dmx_add_feed(dmx, dvbdmxfeed);
spin_unlock_irqrestore(&dvb->slock, flags);
/*handle errors silently*/
if (ret != 0)
ret = 0;
return ret;
}
int aml_dmx_hw_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct aml_dmx *dmx = (struct aml_dmx *)dvbdmxfeed->demux;
struct aml_dvb *dvb = (struct aml_dvb *)dmx->demux.priv;
unsigned long flags;
spin_lock_irqsave(&dvb->slock, flags);
dmx_remove_feed(dmx, dvbdmxfeed);
spin_unlock_irqrestore(&dvb->slock, flags);
return 0;
}
int sf_dmx_track_source(struct aml_dmx *dmx)
{
struct aml_dvb *dvb = (struct aml_dvb *)dmx->demux.priv;
struct aml_swfilter *sf = &dvb->swfilter;
if (sf->user && (dmx->id == sf->track_dmx)) {
pr_dbg_sf("tracking dmx src [%d -> %d]\n",
sf->dmx->source, dmx->source);
sf->dmx->source = dmx->source;
dmx_reset_dmx_hw_ex_unlock(dvb, sf->dmx, 0);
}
return 0;
}
int aml_dmx_hw_set_source(struct dmx_demux *demux, dmx_source_t src)
{
struct aml_dmx *dmx = (struct aml_dmx *)demux;
struct aml_dvb *dvb = (struct aml_dvb *)dmx->demux.priv;
int ret = 0;
int hw_src;
unsigned long flags;
if (sf_dmx_sf(dmx)) {
pr_error("%s: demux %d is in sf mode\n", __func__, dmx->id);
return -EINVAL;
}
spin_lock_irqsave(&dvb->slock, flags);
hw_src = dmx->source;
switch (src) {
case DMX_SOURCE_FRONT0:
hw_src =
(dvb->ts[0].mode ==
AM_TS_SERIAL) ? (dvb->ts[0].s2p_id+AM_TS_SRC_S_TS0) : AM_TS_SRC_TS0;
break;
case DMX_SOURCE_FRONT1:
hw_src =
(dvb->ts[1].mode ==
AM_TS_SERIAL) ? (dvb->ts[1].s2p_id+AM_TS_SRC_S_TS0) : AM_TS_SRC_TS1;
break;
case DMX_SOURCE_FRONT2:
hw_src =
(dvb->ts[2].mode ==
AM_TS_SERIAL) ? (dvb->ts[2].s2p_id+AM_TS_SRC_S_TS0) : AM_TS_SRC_TS2;
break;
case DMX_SOURCE_FRONT3:
hw_src =
(dvb->ts[3].mode ==
AM_TS_SERIAL) ? (dvb->ts[3].s2p_id+AM_TS_SRC_S_TS0) : AM_TS_SRC_TS3;
break;
case DMX_SOURCE_DVR0:
hw_src = AM_TS_SRC_HIU;
break;
case DMX_SOURCE_DVR1:
hw_src = AM_TS_SRC_HIU1;
break;
case DMX_SOURCE_FRONT0_OFFSET:
hw_src = AM_TS_SRC_DMX0;
break;
case DMX_SOURCE_FRONT1_OFFSET:
hw_src = AM_TS_SRC_DMX1;
break;
case DMX_SOURCE_FRONT2_OFFSET:
hw_src = AM_TS_SRC_DMX2;
break;
default:
pr_error("illegal demux source %d\n", src);
ret = -EINVAL;
break;
}
if (hw_src != dmx->source) {
dmx->source = hw_src;
dmx_reset_dmx_hw_ex_unlock(dvb, dmx, 0);
sf_dmx_track_source(dmx);
}
spin_unlock_irqrestore(&dvb->slock, flags);
return ret;
}
#define IS_SRC_DMX(_src) ((_src) >= AM_TS_SRC_DMX0 && (_src) <= AM_TS_SRC_DMX2)
int aml_stb_hw_set_source(struct aml_dvb *dvb, dmx_source_t src)
{
unsigned long flags;
int hw_src;
int ret;
ret = 0;
spin_lock_irqsave(&dvb->slock, flags);
hw_src = dvb->stb_source;
switch (src) {
case DMX_SOURCE_FRONT0:
hw_src =
(dvb->ts[0].mode ==
AM_TS_SERIAL) ? (dvb->ts[0].s2p_id+AM_TS_SRC_S_TS0) : AM_TS_SRC_TS0;
break;
case DMX_SOURCE_FRONT1:
hw_src =
(dvb->ts[1].mode ==
AM_TS_SERIAL) ? (dvb->ts[1].s2p_id+AM_TS_SRC_S_TS0) : AM_TS_SRC_TS1;
break;
case DMX_SOURCE_FRONT2:
hw_src =
(dvb->ts[2].mode ==
AM_TS_SERIAL) ? (dvb->ts[2].s2p_id+AM_TS_SRC_S_TS0) : AM_TS_SRC_TS2;
break;
case DMX_SOURCE_FRONT3:
hw_src =
(dvb->ts[3].mode ==
AM_TS_SERIAL) ? (dvb->ts[3].s2p_id+AM_TS_SRC_S_TS0) : AM_TS_SRC_TS3;
break;
case DMX_SOURCE_DVR0:
hw_src = AM_TS_SRC_HIU;
break;
case DMX_SOURCE_DVR1:
hw_src = AM_TS_SRC_HIU1;
break;
case DMX_SOURCE_FRONT0_OFFSET:
hw_src = AM_TS_SRC_DMX0;
break;
case DMX_SOURCE_FRONT1_OFFSET:
hw_src = AM_TS_SRC_DMX1;
break;
case DMX_SOURCE_FRONT2_OFFSET:
hw_src = AM_TS_SRC_DMX2;
break;
default:
pr_error("illegal demux source %d\n", src);
ret = -EINVAL;
break;
}
if (dvb->stb_source != hw_src) {
int old_source = dvb->stb_source;
dvb->stb_source = hw_src;
if (IS_SRC_DMX(old_source)) {
dmx_set_misc_id(dvb,
(old_source - AM_TS_SRC_DMX0), 0, -1);
} else {
/*which dmx for av-play is unknown,
*can't avoid reset-all
*/
dmx_reset_hw_ex(dvb, 0);
}
if (IS_SRC_DMX(dvb->stb_source)) {
dmx_set_misc_id(dvb,
(dvb->stb_source - AM_TS_SRC_DMX0), 1, -1);
/*dmx_reset_dmx_id_hw_ex_unlock
* (dvb, (dvb->stb_source-AM_TS_SRC_DMX0), 0);
*/
} else {
/*which dmx for av-play is unknown,
*can't avoid reset-all
*/
dmx_reset_hw_ex(dvb, 0);
}
}
spin_unlock_irqrestore(&dvb->slock, flags);
return ret;
}
int aml_dsc_hw_set_source(struct aml_dsc *dsc,
dmx_source_t src, dmx_source_t dst)
{
struct aml_dvb *dvb = dsc->dvb;
int ret = 0;
unsigned long flags;
int hw_src = -1, hw_dst = -1, org_src = -1, org_dst = -1;
int src_reset = 0, dst_reset = 0;
spin_lock_irqsave(&dvb->slock, flags);
hw_src = dsc->source;
hw_dst = dsc->dst;
switch (src) {
case DMX_SOURCE_FRONT0_OFFSET:
hw_src = AM_TS_SRC_DMX0;
break;
case DMX_SOURCE_FRONT1_OFFSET:
hw_src = AM_TS_SRC_DMX1;
break;
case DMX_SOURCE_FRONT2_OFFSET:
hw_src = AM_TS_SRC_DMX2;
break;
default:
hw_src = -1;
break;
}
switch (dst) {
case DMX_SOURCE_FRONT0_OFFSET:
hw_dst = AM_TS_SRC_DMX0;
break;
case DMX_SOURCE_FRONT1_OFFSET:
hw_dst = AM_TS_SRC_DMX1;
break;
case DMX_SOURCE_FRONT2_OFFSET:
hw_dst = AM_TS_SRC_DMX2;
break;
default:
hw_dst = -1;
break;
}
if (hw_src != dsc->source) {
org_src = dsc->source;
dsc->source = hw_src;
src_reset = 1;
}
if (hw_dst != dsc->dst) {
org_dst = dsc->dst;
dsc->dst = hw_dst;
dst_reset = 1;
}
if (src_reset) {
pr_inf("dsc%d source changed: %d -> %d\n",
dsc->id, org_src, hw_src);
if (org_src != -1) {
pr_inf("reset dmx%d\n", (org_src - AM_TS_SRC_DMX0));
dmx_reset_dmx_id_hw_ex_unlock(dvb,
(org_src - AM_TS_SRC_DMX0), 0);
}
if (hw_src != -1) {
pr_inf("reset dmx%d\n", (hw_src - AM_TS_SRC_DMX0));
dmx_reset_dmx_id_hw_ex_unlock(dvb,
(hw_src - AM_TS_SRC_DMX0), 0);
} else
dsc_enable(dsc, 0);
}
if (dst_reset) {
pr_inf("dsc%d dest changed: %d -> %d\n",
dsc->id, org_dst, hw_dst);
if (((!src_reset) && (org_dst != -1)) ||
(src_reset && (org_dst != -1) &&
(org_dst != org_src) && (org_dst != hw_src))) {
pr_inf("reset dmx%d\n", (org_dst - AM_TS_SRC_DMX0));
dmx_reset_dmx_id_hw_ex_unlock(dvb,
(org_dst - AM_TS_SRC_DMX0), 0);
}
if (((!src_reset) && (hw_dst != -1)) ||
(src_reset && (hw_dst != -1)
&& (hw_dst != org_src) && (hw_dst != hw_src))) {
pr_inf("reset dmx%d\n", (hw_dst - AM_TS_SRC_DMX0));
dmx_reset_dmx_id_hw_ex_unlock(dvb,
(hw_dst - AM_TS_SRC_DMX0), 0);
}
if (hw_dst == -1)
dsc_enable(dsc, 0);
}
if (src_reset && dst_reset) {
set_ciplus_input_source(dsc);
}
spin_unlock_irqrestore(&dvb->slock, flags);
return ret;
}
int aml_tso_hw_set_source(struct aml_dvb *dvb, dmx_source_t src)
{
int ret = 0;
unsigned long flags;
int hw_src;
spin_lock_irqsave(&dvb->slock, flags);
hw_src = dvb->tso_source;
switch (src) {
case DMX_SOURCE_FRONT0:
hw_src = (dvb->ts[0].mode == AM_TS_SERIAL)
? (dvb->ts[0].s2p_id+AM_TS_SRC_S_TS0) : AM_TS_SRC_TS0;
break;
case DMX_SOURCE_FRONT1:
hw_src = (dvb->ts[1].mode == AM_TS_SERIAL)
? (dvb->ts[1].s2p_id+AM_TS_SRC_S_TS0) : AM_TS_SRC_TS1;
break;
case DMX_SOURCE_FRONT2:
hw_src = (dvb->ts[2].mode == AM_TS_SERIAL)
? (dvb->ts[2].s2p_id+AM_TS_SRC_S_TS0) : AM_TS_SRC_TS2;
break;
case DMX_SOURCE_FRONT3:
hw_src = (dvb->ts[3].mode == AM_TS_SERIAL)
? (dvb->ts[3].s2p_id+AM_TS_SRC_S_TS0) : AM_TS_SRC_TS3;
break;
case DMX_SOURCE_DVR0:
hw_src = AM_TS_SRC_HIU;
break;
case DMX_SOURCE_FRONT0 + 100:
hw_src = AM_TS_SRC_DMX0;
break;
case DMX_SOURCE_FRONT1 + 100:
hw_src = AM_TS_SRC_DMX1;
break;
case DMX_SOURCE_FRONT2 + 100:
hw_src = AM_TS_SRC_DMX2;
break;
default:
hw_src = -1;
ret = -EINVAL;
break;
}
if (hw_src != dvb->tso_source) {
dvb->tso_source = hw_src;
stb_enable(dvb);
}
spin_unlock_irqrestore(&dvb->slock, flags);
return ret;
}
int aml_asyncfifo_hw_set_source(struct aml_asyncfifo *afifo,
enum aml_dmx_id_t src)
{
struct aml_dvb *dvb = afifo->dvb;
int ret = -1;
unsigned long flags;
if (sf_afifo_sf(afifo)) {
pr_error("%s: afifo %d is in sf mode\n", __func__, afifo->id);
return -EINVAL;
}
spin_lock_irqsave(&dvb->slock, flags);
pr_dbg("asyncfifo %d set source %d->%d",
afifo->id, afifo->source, src);
switch (src) {
case AM_DMX_0:
case AM_DMX_1:
case AM_DMX_2:
if (afifo->source != src) {
afifo->source = src;
ret = 0;
}
break;
default:
pr_error("illegal async fifo source %d\n", src);
ret = -EINVAL;
break;
}
if (ret == 0 && afifo->dvb)
reset_async_fifos(afifo->dvb);
spin_unlock_irqrestore(&dvb->slock, flags);
return ret;
}
int aml_dmx_hw_set_dump_ts_select(struct dmx_demux *demux, int dump_ts_select)
{
struct aml_dmx *dmx = (struct aml_dmx *)demux;
struct aml_dvb *dvb = (struct aml_dvb *)dmx->demux.priv;
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&dvb->slock, flags);
dump_ts_select = !!dump_ts_select;
if (dmx->dump_ts_select != dump_ts_select) {
dmx->dump_ts_select = dump_ts_select;
dmx_reset_dmx_hw_ex_unlock(dvb, dmx, 0);
}
spin_unlock_irqrestore(&dvb->slock, flags);
return ret;
}
u32 aml_dmx_get_video_pts(struct aml_dvb *dvb)
{
unsigned long flags;
u32 pts;
spin_lock_irqsave(&dvb->slock, flags);
pts = video_pts;
spin_unlock_irqrestore(&dvb->slock, flags);
return pts;
}
u32 aml_dmx_get_audio_pts(struct aml_dvb *dvb)
{
unsigned long flags;
u32 pts;
spin_lock_irqsave(&dvb->slock, flags);
pts = audio_pts;
spin_unlock_irqrestore(&dvb->slock, flags);
return pts;
}
u32 aml_dmx_get_video_pts_bit32(struct aml_dvb *dvb)
{
unsigned long flags;
u32 bit32;
spin_lock_irqsave(&dvb->slock, flags);
bit32 = video_pts_bit32;
spin_unlock_irqrestore(&dvb->slock, flags);
return bit32;
}
u32 aml_dmx_get_audio_pts_bit32(struct aml_dvb *dvb)
{
unsigned long flags;
u32 bit32;
spin_lock_irqsave(&dvb->slock, flags);
bit32 = audio_pts_bit32;
spin_unlock_irqrestore(&dvb->slock, flags);
return bit32;
}
u32 aml_dmx_get_first_video_pts(struct aml_dvb *dvb)
{
unsigned long flags;
u32 pts;
spin_lock_irqsave(&dvb->slock, flags);
pts = first_video_pts;
spin_unlock_irqrestore(&dvb->slock, flags);
return pts;
}
u32 aml_dmx_get_first_audio_pts(struct aml_dvb *dvb)
{
unsigned long flags;
u32 pts;
spin_lock_irqsave(&dvb->slock, flags);
pts = first_audio_pts;
spin_unlock_irqrestore(&dvb->slock, flags);
return pts;
}
int aml_dmx_set_skipbyte(struct aml_dvb *dvb, int skipbyte)
{
if (demux_skipbyte != skipbyte) {
pr_dbg("set skip byte %d\n", skipbyte);
demux_skipbyte = skipbyte;
dmx_reset_hw_ex(dvb, 0);
}
return 0;
}
int aml_dmx_set_demux(struct aml_dvb *dvb, int id)
{
aml_stb_hw_set_source(dvb, DMX_SOURCE_DVR0);
if (id < DMX_DEV_COUNT) {
struct aml_dmx *dmx = &dvb->dmx[id];
aml_dmx_hw_set_source((struct dmx_demux *)dmx,
DMX_SOURCE_DVR0);
}
return 0;
}
int _set_tsfile_clkdiv(struct aml_dvb *dvb, int clkdiv)
{
if (tsfile_clkdiv != clkdiv) {
pr_dbg("set ts file clock div %d\n", clkdiv);
tsfile_clkdiv = clkdiv;
dmx_reset_hw(dvb);
}
return 0;
}
static ssize_t tsfile_clkdiv_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size)
{
/*int div = (int)simple_strtol(buf, NULL, 10);*/
long div;
if (kstrtol(buf, 0, &div) == 0)
_set_tsfile_clkdiv(aml_get_dvb_device(), (int)div);
return size;
}
static ssize_t tsfile_clkdiv_show(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t ret;
ret = sprintf(buf, "%d\n", tsfile_clkdiv);
return ret;
}
static int dmx_id;
static ssize_t dmx_smallsec_show(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t ret;
struct aml_dvb *dvb = aml_get_dvb_device();
ret = sprintf(buf, "%d:%d\n", dvb->dmx[dmx_id].smallsec.enable,
dvb->dmx[dmx_id].smallsec.bufsize);
return ret;
}
static ssize_t dmx_smallsec_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size)
{
int i, e, s = 0, f = 0;
struct aml_dvb *dvb = aml_get_dvb_device();
i = sscanf(buf, "%d:%i:%d", &e, &s, &f);
if (i <= 0)
return size;
dmx_smallsec_set(&dvb->dmx[dmx_id].smallsec, e, s, f);
return size;
}
static ssize_t dmx_timeout_show(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t ret;
struct aml_dvb *dvb = aml_get_dvb_device();
ret = sprintf(buf, "%d:%d:0x%x:%d:%d\n",
dvb->dmx[dmx_id].timeout.enable,
dvb->dmx[dmx_id].timeout.timeout,
dvb->dmx[dmx_id].timeout.ch_disable,
dvb->dmx[dmx_id].timeout.match,
(DMX_READ_REG(dmx_id, STB_INT_STATUS)&(1<<INPUT_TIME_OUT)) ?
1 : 0);
DMX_WRITE_REG(dmx_id, STB_INT_STATUS, (1<<INPUT_TIME_OUT));
return ret;
}
static ssize_t dmx_timeout_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size)
{
int i, e, t = 0, c = 0, m = 0, f = 0;
struct aml_dvb *dvb = aml_get_dvb_device();
i = sscanf(buf, "%d:%i:%i:%d:%d", &e, &t, &c, &m, &f);
if (i <= 0)
return size;
dmx_timeout_set(&dvb->dmx[dmx_id].timeout, e, t, c, m, f);
return size;
}
#define DEMUX_SCAMBLE_FUNC_DECL(i) \
static ssize_t demux##i##_scramble_show(struct class *class, \
struct class_attribute *attr, char *buf)\
{\
int data = 0;\
int aflag = 0;\
int vflag = 0;\
ssize_t ret = 0;\
data = DMX_READ_REG(i, DEMUX_SCRAMBLING_STATE);\
if ((data & 0x01) == 0x01) \
vflag = 1;\
if ((data & 0x02) == 0x02) \
aflag = 1;\
ret = sprintf(buf, "%d %d\n", vflag, aflag);\
return ret;\
}
#if DMX_DEV_COUNT > 0
DEMUX_SCAMBLE_FUNC_DECL(0)
#endif
#if DMX_DEV_COUNT > 1
DEMUX_SCAMBLE_FUNC_DECL(1)
#endif
#if DMX_DEV_COUNT > 2
DEMUX_SCAMBLE_FUNC_DECL(2)
#endif
static ssize_t ciplus_output_ctrl_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
int ret;
char *out = "none";
pr_inf("output demux use 3 bit to indicate.\n");
pr_inf("1bit:demux0 2bit:demux1 3bit:demux2\n");
switch (ciplus_out_sel) {
case 1:
out = "dmx0";
break;
case 2:
out = "dmx1";
break;
case 4:
out = "dmx2";
break;
default:
break;
}
ret = sprintf(buf, "%s 0x%x %s\n",
out,
ciplus_out_sel,
(ciplus_out_auto_mode) ? "" : "(force)");
return ret;
}
static ssize_t ciplus_output_ctrl_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size)
{
struct aml_dvb *dvb = aml_get_dvb_device();
int i, tmp;
u32 top_cfg, ci_cfg;
i = kstrtoint(buf, -1, &tmp);
if (tmp > 8 || tmp < 0)
pr_error("Invalid output set\n");
else if (tmp == 8) {
ciplus_out_auto_mode = 1;
ciplus_out_sel = -1;
pr_error("Auto set output mode enable\n");
} else {
ciplus_out_auto_mode = 0;
ciplus_out_sel = tmp;
pr_error("Auto set output mode disable\n");
}
top_cfg = READ_MPEG_REG(STB_TOP_CONFIG);
ci_cfg = READ_MPEG_REG(CIPLUS_CONFIG);
if (ci_cfg & (1 << CNTL_ENABLE)) {
int out = 0;
if (ciplus_out_auto_mode) {
if (dvb->dsc[0].source != -1)
out = 1 << (dvb->dsc[0].source - AM_TS_SRC_DMX0);
} else {
out = ciplus_out_sel;
}
top_cfg &= ~(7<<CIPLUS_OUT_SEL);
top_cfg |= (out<<CIPLUS_OUT_SEL);
WRITE_MPEG_REG(STB_TOP_CONFIG, top_cfg);
set_fec_core_sel(dvb);
}
return size;
}
static ssize_t reset_fec_input_ctrl_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
return 0;
}
static ssize_t reset_fec_input_ctrl_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size)
{
u32 v;
v = READ_MPEG_REG(FEC_INPUT_CONTROL);
v &= ~(1<<11);
WRITE_MPEG_REG(FEC_INPUT_CONTROL, v);
pr_dbg("reset FEC_INPUT_CONTROL to %x\n", v);
return size;
}
static ssize_t register_addr_show(struct class *class,
struct class_attribute *attr,
char *buf);
static ssize_t register_addr_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size);
static ssize_t dmx_id_show(struct class *class,
struct class_attribute *attr, char *buf);
static ssize_t dmx_id_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size);
static ssize_t register_value_show(struct class *class,
struct class_attribute *attr,
char *buf);
static ssize_t register_value_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size);
static ssize_t dmx_sec_statistics_show(struct class *class,
struct class_attribute *attr,
char *buf);
static int reg_addr;
static CLASS_ATTR_RW(dmx_id);
static CLASS_ATTR_RW(register_addr);
static CLASS_ATTR_RW(register_value);
static CLASS_ATTR_RW(tsfile_clkdiv);
#define DEMUX_SCAMBLE_ATTR_DECL(i)\
CLASS_ATTR_RO(demux##i##_scramble);
#if DMX_DEV_COUNT > 0
DEMUX_SCAMBLE_ATTR_DECL(0);
#endif
#if DMX_DEV_COUNT > 1
DEMUX_SCAMBLE_ATTR_DECL(1);
#endif
#if DMX_DEV_COUNT > 2
DEMUX_SCAMBLE_ATTR_DECL(2);
#endif
static CLASS_ATTR_RW(dmx_smallsec);
static CLASS_ATTR_RW(dmx_timeout);
static CLASS_ATTR_RW(reset_fec_input_ctrl);
static CLASS_ATTR_RW(ciplus_output_ctrl);
static CLASS_ATTR_RO(dmx_sec_statistics);
#define DMX_ATTR(name) &class_attr_##name.attr
static struct attribute *aml_dmx_class_attrs[] = {
DMX_ATTR(dmx_id),
DMX_ATTR(register_addr),
DMX_ATTR(register_value),
DMX_ATTR(tsfile_clkdiv),
DMX_ATTR(dmx_smallsec),
DMX_ATTR(dmx_timeout),
DMX_ATTR(reset_fec_input_ctrl),
DMX_ATTR(ciplus_output_ctrl),
DMX_ATTR(dmx_sec_statistics),
#define DEMUX_SCRAMBLE(i) \
DMX_ATTR(demux##i##_scramble)
#if DMX_DEV_COUNT > 0
DEMUX_SCRAMBLE(0),
#endif
#if DMX_DEV_COUNT > 1
DEMUX_SCRAMBLE(1),
#endif
#if DMX_DEV_COUNT > 2
DEMUX_SCRAMBLE(2),
#endif
NULL,
};
ATTRIBUTE_GROUPS(aml_dmx_class);
static struct class aml_dmx_class = {
.name = "dmx",
.class_groups = aml_dmx_class_groups,
};
static ssize_t dmx_id_show(struct class *class,
struct class_attribute *attr, char *buf)
{
int ret;
ret = sprintf(buf, "%d\n", dmx_id);
return ret;
}
static ssize_t dmx_id_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size)
{
int id = 0;
long value = 0;
if (kstrtol(buf, 0, &value) == 0)
id = (int)value;
/*id = simple_strtol(buf, 0, 16);*/
if (id < 0 || id > 2)
pr_dbg("dmx id must 0 ~2\n");
else
dmx_id = id;
return size;
}
static ssize_t register_addr_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
int ret;
ret = sprintf(buf, "%x\n", reg_addr);
return ret;
}
static ssize_t register_addr_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size)
{
int addr = 0;
/*addr = simple_strtol(buf, 0, 16);*/
long value = 0;
if (kstrtol(buf, 0, &value) == 0)
addr = (int)value;
reg_addr = addr;
return size;
}
static ssize_t register_value_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
int ret, value;
value = READ_MPEG_REG(reg_addr);
ret = sprintf(buf, "%x\n", value);
return ret;
}
static ssize_t register_value_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size)
{
int value = 0;
/*value = simple_strtol(buf, 0, 16);*/
long val = 0;
if (kstrtol(buf, 0, &val) == 0)
value = (int)val;
WRITE_MPEG_REG(reg_addr, value);
return size;
}
static ssize_t dmx_sec_statistics_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
ssize_t ret;
char tmp[128];
struct aml_dvb *dvb = aml_get_dvb_device();
ret = sprintf(tmp, "[hw]%#lx:%#lx:%#lx\n[sw]%#lx:%#lx:%#lx\n",
dvb->dmx[dmx_id].sec_cnt[SEC_CNT_HW],
dvb->dmx[dmx_id].sec_cnt_match[SEC_CNT_HW],
dvb->dmx[dmx_id].sec_cnt_crc_fail[SEC_CNT_HW],
dvb->dmx[dmx_id].sec_cnt[SEC_CNT_SW],
dvb->dmx[dmx_id].sec_cnt_match[SEC_CNT_SW],
dvb->dmx[dmx_id].sec_cnt_crc_fail[SEC_CNT_SW]);
ret = sprintf(buf, "%s[ss]%#lx:%#lx:%#lx\n",
tmp,
dvb->dmx[dmx_id].sec_cnt[SEC_CNT_SS],
dvb->dmx[dmx_id].sec_cnt_match[SEC_CNT_SS],
dvb->dmx[dmx_id].sec_cnt_crc_fail[SEC_CNT_SS]);
return ret;
}
int aml_regist_dmx_class(void)
{
if (class_register(&aml_dmx_class) < 0)
pr_error("register class error\n");
return 0;
}
int aml_unregist_dmx_class(void)
{
class_unregister(&aml_dmx_class);
return 0;
}
static struct mconfig parser_configs[] = {
MC_PU32("video_pts", &video_pts),
MC_PU32("audio_pts", &audio_pts),
MC_PU32("video_pts_bit32", &video_pts_bit32),
MC_PU32("audio_pts_bit32", &audio_pts_bit32),
MC_PU32("first_video_pts", &first_video_pts),
MC_PU32("first_audio_pts", &first_audio_pts),
};
void aml_register_parser_mconfig(void)
{
REG_PATH_CONFIGS("media.parser", parser_configs);
}