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)