blob: ad3498756ef989c2aa7b94b89c7641abd903e902 [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"
#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);
/*#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)
/*Section buffer watchdog*/
static void section_buffer_watchdog_func(unsigned long arg)
{
struct aml_dvb *dvb = (struct aml_dvb *)arg;
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);
}
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);
}
}
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);
}
}
}
}
}
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);
} 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);
}
}
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);
}
} 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);
}
}
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:
am_ci_plus_set_output(ch);
ret = dsc_set_aes_des_sm4_key(ch, flags, type, key);
if (ret != 0)
goto END;
/* 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);
}
/*
* Set output to demux set.
*/
static void am_ci_plus_set_output(struct aml_dsc_channel *ch)
{
struct aml_dsc *dsc = ch->dsc;
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");
}
}
#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 {/*en