blob: 4e1549c0f01706e70fe1736e1af5bd21d6c3c9e3 [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 DVB driver.
*/
//move to define in Makefile
//#define ENABLE_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/gpio.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/pinctrl/consumer.h>
#include <linux/reset.h>
#include <linux/of_gpio.h>
#include <linux/amlogic/media/utils/amstream.h>
#include <linux/amlogic/cpu_version.h>
#include <linux/clk.h>
#include "c_stb_define.h"
#include "c_stb_regs_define.h"
#include "aml_dvb.h"
#include "aml_dvb_reg.h"
#include "aml_demod_gt.h"
#include "../../../common/media_clock/switch/amports_gate.h"
#define pr_dbg(args...)\
do {\
if (debug_dvb)\
printk(args);\
} while (0)
#define pr_error(fmt, args...) printk("DVB: " fmt, ## args)
#define pr_inf(fmt, args...) printk(fmt, ## args)
MODULE_PARM_DESC(debug_dvb, "\n\t\t Enable dvb debug information");
static int debug_dvb;
module_param(debug_dvb, int, 0644);
#define CARD_NAME "amlogic-dvb-demux"
#define DVB_VERSION "V2.02"
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
MODULE_PARM_DESC(dsc_max, "max number of dsc");
static int dsc_max = DSC_DEV_COUNT;
module_param(dsc_max, int, 0644);
static struct aml_dvb aml_dvb_device;
static struct class aml_stb_class;
static int dmx_reset_all_flag = 0;
#if 0
static struct reset_control *aml_dvb_demux_reset_ctl;
static struct reset_control *aml_dvb_afifo_reset_ctl;
static struct reset_control *aml_dvb_ahbarb0_reset_ctl;
static struct reset_control *aml_dvb_uparsertop_reset_ctl;
#else
/*no used reset ctl,need use clk in 4.9 kernel*/
static struct clk *aml_dvb_demux_clk;
static struct clk *aml_dvb_afifo_clk;
static struct clk *aml_dvb_ahbarb0_clk;
static struct clk *aml_dvb_uparsertop_clk;
#endif
static int aml_tsdemux_reset(void);
static int aml_tsdemux_set_reset_flag(void);
static int aml_tsdemux_request_irq(irq_handler_t handler, void *data);
static int aml_tsdemux_free_irq(void);
static int aml_tsdemux_set_vid(int vpid);
static int aml_tsdemux_set_aid(int apid);
static int aml_tsdemux_set_sid(int spid);
static int aml_tsdemux_set_pcrid(int pcrpid);
static int aml_tsdemux_set_skipbyte(int skipbyte);
static int aml_tsdemux_set_demux(int id);
static unsigned long aml_tsdemux_hwdmx_spin_lock(unsigned long flags);
static int aml_tsdemux_hwdmx_spin_unlock(unsigned long flags);
static struct tsdemux_ops aml_tsdemux_ops = {
.reset = aml_tsdemux_reset,
.set_reset_flag = aml_tsdemux_set_reset_flag,
.request_irq = aml_tsdemux_request_irq,
.free_irq = aml_tsdemux_free_irq,
.set_vid = aml_tsdemux_set_vid,
.set_aid = aml_tsdemux_set_aid,
.set_sid = aml_tsdemux_set_sid,
.set_pcrid = aml_tsdemux_set_pcrid,
.set_skipbyte = aml_tsdemux_set_skipbyte,
.set_demux = aml_tsdemux_set_demux,
.hw_dmx_lock = aml_tsdemux_hwdmx_spin_lock,
.hw_dmx_unlock = aml_tsdemux_hwdmx_spin_unlock
};
long aml_stb_get_base(int id)
{
int newbase = 0;
if (MESON_CPU_MAJOR_ID_TXL < get_cpu_type()
&& MESON_CPU_MAJOR_ID_GXLX != get_cpu_type()) {
newbase = 1;
}
switch (id) {
case ID_STB_CBUS_BASE:
return (newbase) ? 0x1800 : 0x1600;
case ID_SMARTCARD_REG_BASE:
return (newbase) ? 0x9400 : 0x2110;
case ID_ASYNC_FIFO_REG_BASE:
return (newbase) ? 0x2800 : 0x2310;
case ID_ASYNC_FIFO1_REG_BASE:
return 0x9800;
case ID_ASYNC_FIFO2_REG_BASE:
return (newbase) ? 0x2400 : 0x2314;
case ID_RESET_BASE:
return (newbase) ? 0x0400 : 0x1100;
case ID_PARSER_SUB_START_PTR_BASE:
return (newbase) ? 0x3800 : 0x2900;
default:
return 0;
}
return 0;
}
static void aml_dvb_dmx_release(struct aml_dvb *advb, struct aml_dmx *dmx)
{
int i;
pr_inf("[dmx_kpi] %s Enter.\n", __func__);
dvb_net_release(&dmx->dvb_net);
aml_dmx_hw_deinit(dmx);
dmx->demux.dmx.close(&dmx->demux.dmx);
dmx->demux.dmx.remove_frontend(&dmx->demux.dmx, &dmx->mem_fe);
for (i = 0; i < DMX_DEV_COUNT; i++)
dmx->demux.dmx.remove_frontend(&dmx->demux.dmx, &dmx->hw_fe[i]);
dvb_dmxdev_release(&dmx->dmxdev);
dvb_dmx_release(&dmx->demux);
pr_inf("[dmx_kpi] %s Exit.\n", __func__);
}
static int aml_dvb_dmx_init(struct aml_dvb *advb, struct aml_dmx *dmx, int id)
{
int i, ret;
struct resource *res;
char buf[32];
switch (id) {
case 0:
dmx->dmx_irq = INT_DEMUX;
break;
case 1:
dmx->dmx_irq = INT_DEMUX_1;
break;
case 2:
dmx->dmx_irq = INT_DEMUX_2;
break;
}
snprintf(buf, sizeof(buf), "demux%d_irq", id);
res = platform_get_resource_byname(advb->pdev, IORESOURCE_IRQ, buf);
if (res)
dmx->dmx_irq = res->start;
pr_dbg("%s irq num:%d \r\n", buf, dmx->dmx_irq);
dmx->source = -1;
dmx->dump_ts_select = 0;
dmx->dvr_irq = -1;
dmx->demux.dmx.capabilities =
(DMX_TS_FILTERING | DMX_SECTION_FILTERING |
DMX_MEMORY_BASED_FILTERING);
dmx->demux.filternum = dmx->demux.feednum = FILTER_COUNT;
dmx->demux.priv = advb;
dmx->demux.start_feed = aml_dmx_hw_start_feed;
dmx->demux.stop_feed = aml_dmx_hw_stop_feed;
dmx->demux.write_to_decoder = NULL;
ret = dvb_dmx_init(&dmx->demux);
if (ret < 0) {
pr_error("dvb_dmx failed: error %d\n", ret);
goto error_dmx_init;
}
dmx->dmxdev.filternum = dmx->demux.feednum;
dmx->dmxdev.demux = &dmx->demux.dmx;
dmx->dmxdev.capabilities = 0;
ret = dvb_dmxdev_init(&dmx->dmxdev, &advb->dvb_adapter);
if (ret < 0) {
pr_error("dvb_dmxdev_init failed: error %d\n", ret);
goto error_dmxdev_init;
}
for (i = 0; i < DMX_DEV_COUNT; i++) {
int source = i + DMX_FRONTEND_0;
dmx->hw_fe[i].source = source;
ret =
dmx->demux.dmx.add_frontend(&dmx->demux.dmx,
&dmx->hw_fe[i]);
if (ret < 0) {
pr_error("adding hw_frontend to dmx failed: error %d",
ret);
dmx->hw_fe[i].source = 0;
goto error_add_hw_fe;
}
}
dmx->mem_fe.source = DMX_MEMORY_FE;
ret = dmx->demux.dmx.add_frontend(&dmx->demux.dmx, &dmx->mem_fe);
if (ret < 0) {
pr_error("adding mem_frontend to dmx failed: error %d", ret);
goto error_add_mem_fe;
}
ret = dmx->demux.dmx.connect_frontend(&dmx->demux.dmx, &dmx->hw_fe[1]);
if (ret < 0) {
pr_error("connect frontend failed: error %d", ret);
goto error_connect_fe;
}
dmx->id = id;
dmx->aud_chan = -1;
dmx->vid_chan = -1;
dmx->sub_chan = -1;
dmx->pcr_chan = -1;
/*smallsec*/
dmx->smallsec.enable = 0;
dmx->smallsec.bufsize = SS_BUFSIZE_DEF;
dmx->smallsec.dmx = dmx;
/*input timeout*/
dmx->timeout.enable = 1;
dmx->timeout.timeout = DTO_TIMEOUT_DEF;
dmx->timeout.ch_disable = DTO_CHDIS_VAS;
dmx->timeout.match = 1;
dmx->timeout.trigger = 0;
dmx->timeout.dmx = dmx;
/*CRC monitor*/
dmx->crc_check_count = 0;
dmx->crc_check_time = 0;
ret = aml_dmx_hw_init(dmx);
if (ret < 0) {
pr_error("demux hw init error %d", ret);
dmx->id = -1;
goto error_dmx_hw_init;
}
dvb_net_init(&advb->dvb_adapter, &dmx->dvb_net, &dmx->demux.dmx);
return 0;
error_dmx_hw_init:
error_connect_fe:
dmx->demux.dmx.remove_frontend(&dmx->demux.dmx, &dmx->mem_fe);
error_add_mem_fe:
error_add_hw_fe:
for (i = 0; i < DMX_DEV_COUNT; i++) {
if (dmx->hw_fe[i].source)
dmx->demux.dmx.remove_frontend(&dmx->demux.dmx,
&dmx->hw_fe[i]);
}
dvb_dmxdev_release(&dmx->dmxdev);
error_dmxdev_init:
dvb_dmx_release(&dmx->demux);
error_dmx_init:
return ret;
}
struct aml_dvb *aml_get_dvb_device(void)
{
return &aml_dvb_device;
}
EXPORT_SYMBOL(aml_get_dvb_device);
struct dvb_adapter *aml_get_dvb_adapter(void)
{
return &aml_dvb_device.dvb_adapter;
}
EXPORT_SYMBOL(aml_get_dvb_adapter);
static int dvb_dsc_open(struct inode *inode, struct file *file)
{
int err;
err = dvb_generic_open(inode, file);
if (err < 0)
return err;
return 0;
}
static void dsc_channel_alloc(struct aml_dsc *dsc, int id, unsigned int pid)
{
struct aml_dsc_channel *ch = &dsc->channel[id];
ch->used = 1;
ch->work_mode = -1;
ch->id = id;
ch->pid = pid;
ch->set = 0;
ch->dsc = dsc;
ch->mode = -1;
dsc_set_pid(ch, ch->pid);
}
static void dsc_channel_free(struct aml_dsc_channel *ch)
{
if (!ch->used)
return;
ch->used = 0;
dsc_set_pid(ch, 0x1fff);
dsc_release();
ch->pid = 0x1fff;
ch->set = 0;
ch->work_mode = -1;
ch->mode = -1;
}
static void dsc_reset(struct aml_dsc *dsc)
{
int i;
for (i = 0; i < DSC_COUNT; i++)
dsc_channel_free(&dsc->channel[i]);
}
static int get_dsc_key_work_mode(enum ca_cw_type cw_type)
{
int work_mode = DVBCSA_MODE;
switch (cw_type) {
case CA_CW_DVB_CSA_EVEN:
case CA_CW_DVB_CSA_ODD:
work_mode = DVBCSA_MODE;
break;
case CA_CW_AES_EVEN:
case CA_CW_AES_ODD:
case CA_CW_AES_ODD_IV:
case CA_CW_AES_EVEN_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_ODD_IV:
case CA_CW_SM4_EVEN_IV:
work_mode = CIPLUS_MODE;
default:
break;
}
return work_mode;
}
/* Check if there are channels run in previous mode(aes/dvbcsa)
* in dsc0/ciplus
*/
static void dsc_ciplus_switch_check(struct aml_dsc_channel *ch,
enum ca_cw_type cw_type)
{
struct aml_dsc *dsc = ch->dsc;
int work_mode = 0;
struct aml_dsc_channel *pch = NULL;
int i;
work_mode = get_dsc_key_work_mode(cw_type);
if (dsc->work_mode == work_mode)
return;
dsc->work_mode = work_mode;
for (i = 0; i < DSC_COUNT; i++) {
pch = &dsc->channel[i];
if (pch->work_mode != work_mode && pch->work_mode != -1) {
pr_error("Dsc work mode changed,");
pr_error("but there are still some channels");
pr_error("run in different mode\n");
pr_error("mod_pre[%d] -> mod[%d] ch[%d]\n",
pch->work_mode, work_mode, i);
}
}
}
static int dsc_set_cw(struct aml_dsc *dsc, struct ca_descr_ex *d)
{
struct aml_dsc_channel *ch;
if (d->index >= DSC_COUNT)
return -EINVAL;
ch = &dsc->channel[d->index];
switch (d->type) {
case CA_CW_DVB_CSA_EVEN:
case CA_CW_AES_EVEN:
case CA_CW_DES_EVEN:
case CA_CW_SM4_EVEN:
memcpy(ch->even, d->cw, DSC_KEY_SIZE_MAX);
break;
case CA_CW_DVB_CSA_ODD:
case CA_CW_AES_ODD:
case CA_CW_DES_ODD:
case CA_CW_SM4_ODD:
memcpy(ch->odd, d->cw, DSC_KEY_SIZE_MAX);
break;
case CA_CW_AES_EVEN_IV:
case CA_CW_SM4_EVEN_IV:
memcpy(ch->even_iv, d->cw, DSC_KEY_SIZE_MAX);
break;
case CA_CW_AES_ODD_IV:
case CA_CW_SM4_ODD_IV:
memcpy(ch->odd_iv, d->cw, DSC_KEY_SIZE_MAX);
break;
default:
break;
}
ch->set |= (1 << d->type) | (d->flags << 24);
if (d->mode == CA_DSC_IDSA) {
ch->mode = IDSA_MODE;
}
/*do key set*/
dsc_set_key(ch, d->flags, d->type, d->cw);
dsc_ciplus_switch_check(ch, d->type);
return 0;
}
static int dvb_dsc_do_ioctl(struct file *file, unsigned int cmd,
void *parg)
{
struct dvb_device *dvbdev = file->private_data;
struct aml_dsc *dsc = dvbdev->priv;
struct aml_dvb *dvb = dsc->dvb;
struct aml_dsc_channel *ch;
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&dvb->slock, flags);
switch (cmd) {
case CA_RESET:
dsc_reset(dsc);
break;
case CA_GET_CAP: {
ca_caps_t *cap = parg;
cap->slot_num = 1;
cap->slot_type = CA_DESCR;
cap->descr_num = DSC_COUNT;
cap->descr_type = 0;
break;
}
case CA_GET_SLOT_INFO: {
ca_slot_info_t *slot = parg;
slot->num = 1;
slot->type = CA_DESCR;
slot->flags = 0;
break;
}
case CA_GET_DESCR_INFO: {
ca_descr_info_t *descr = parg;
descr->num = DSC_COUNT;
descr->type = 0;
break;
}
case CA_SET_DESCR: {
ca_descr_t *d = parg;
struct ca_descr_ex dex;
dex.index = d->index;
dex.type = d->parity ? CA_CW_DVB_CSA_ODD : CA_CW_DVB_CSA_EVEN;
dex.mode = -1;
dex.flags = 0;
memcpy(dex.cw, d->cw, sizeof(d->cw));
ret = dsc_set_cw(dsc, &dex);
break;
}
case CA_SET_PID: {
ca_pid_t *pi = parg;
int i;
if (pi->index == -1) {
for (i = 0; i < DSC_COUNT; i++) {
ch = &dsc->channel[i];
if (ch->used && (ch->pid == pi->pid)) {
dsc_channel_free(ch);
break;
}
}
} else if ((pi->index >= 0) && (pi->index < DSC_COUNT)) {
ch = &dsc->channel[pi->index];
if (pi->pid < 0x1fff) {
if (!ch->used) {
dsc_channel_alloc(dsc,
pi->index, pi->pid);
}
} else {
if (ch->used)
dsc_channel_free(ch);
}
} else {
ret = -EINVAL;
}
break;
}
case CA_SET_DESCR_EX: {
struct ca_descr_ex *d = parg;
ret = dsc_set_cw(dsc, d);
break;
}
default:
ret = -EINVAL;
break;
}
spin_unlock_irqrestore(&dvb->slock, flags);
return ret;
}
static int dvb_dsc_usercopy(struct file *file,
unsigned int cmd, unsigned long arg,
int (*func)(struct file *file,
unsigned int cmd, void *arg))
{
char sbuf[128];
void *mbuf = NULL;
void *parg = NULL;
int err = -EINVAL;
/* Copy arguments into temp kernel buffer */
switch (_IOC_DIR(cmd)) {
case _IOC_NONE:
/*
* For this command, the pointer is actually an integer
* argument.
*/
parg = (void *) arg;
break;
case _IOC_READ: /* some v4l ioctls are marked wrong ... */
case _IOC_WRITE:
case (_IOC_WRITE | _IOC_READ):
if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
parg = sbuf;
} else {
/* too big to allocate from stack */
mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
if (mbuf == NULL)
return -ENOMEM;
parg = mbuf;
}
err = -EFAULT;
if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
goto out;
break;
}
/* call driver */
err = func(file, cmd, parg);
if (err == -ENOIOCTLCMD)
err = -ENOTTY;
if (err < 0)
goto out;
/* Copy results into user buffer */
switch (_IOC_DIR(cmd)) {
case _IOC_READ:
case (_IOC_WRITE | _IOC_READ):
if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
err = -EFAULT;
break;
}
out:
kfree(mbuf);
return err;
}
static long dvb_dsc_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
return dvb_dsc_usercopy(file, cmd, arg, dvb_dsc_do_ioctl);
}
static int dvb_dsc_release(struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev = file->private_data;
struct aml_dsc *dsc = dvbdev->priv;
struct aml_dvb *dvb = dsc->dvb;
unsigned long flags;
spin_lock_irqsave(&dvb->slock, flags);
dsc_reset(dsc);
spin_unlock_irqrestore(&dvb->slock, flags);
dvb_generic_release(inode, file);
return 0;
}
#ifdef CONFIG_COMPAT
static long dvb_dsc_compat_ioctl(struct file *filp,
unsigned int cmd, unsigned long args)
{
unsigned long ret;
args = (unsigned long)compat_ptr(args);
ret = dvb_dsc_ioctl(filp, cmd, args);
return ret;
}
#endif
static const struct file_operations dvb_dsc_fops = {
.owner = THIS_MODULE,
.read = NULL,
.write = NULL,
.unlocked_ioctl = dvb_dsc_ioctl,
.open = dvb_dsc_open,
.release = dvb_dsc_release,
.poll = NULL,
#ifdef CONFIG_COMPAT
.compat_ioctl = dvb_dsc_compat_ioctl,
#endif
};
static struct dvb_device dvbdev_dsc = {
.priv = NULL,
.users = 1,
.readers = 1,
.writers = 1,
.fops = &dvb_dsc_fops,
};
static int aml_dvb_asyncfifo_init(struct aml_dvb *advb,
struct aml_asyncfifo *asyncfifo, int id)
{
struct resource *res;
char buf[32];
if (id == 0)
asyncfifo->asyncfifo_irq = INT_ASYNC_FIFO_FLUSH;
else if(id == 2)
asyncfifo->asyncfifo_irq = INT_ASYNC_FIFO3_FLUSH;
else
asyncfifo->asyncfifo_irq = INT_ASYNC_FIFO2_FLUSH;
snprintf(buf, sizeof(buf), "dvr%d_irq", id);
res = platform_get_resource_byname(advb->pdev, IORESOURCE_IRQ, buf);
if (res)
asyncfifo->asyncfifo_irq = res->start;
pr_dbg("%s irq num:%d \n", buf, asyncfifo->asyncfifo_irq);
asyncfifo->dvb = advb;
asyncfifo->id = id;
asyncfifo->init = 0;
asyncfifo->flush_size = 256 * 1024;
asyncfifo->secure_enable = 0;
asyncfifo->blk.addr = 0;
asyncfifo->blk.len = 0;
asyncfifo->stored_pages = 0;
return aml_asyncfifo_hw_init(asyncfifo);
}
static void aml_dvb_asyncfifo_release(struct aml_dvb *advb,
struct aml_asyncfifo *asyncfifo)
{
aml_asyncfifo_hw_deinit(asyncfifo);
}
static int aml_dvb_dsc_init(struct aml_dvb *advb,
struct aml_dsc *dsc, int id)
{
int i;
for (i = 0; i < DSC_COUNT; i++) {
dsc->channel[i].id = i;
dsc->channel[i].used = 0;
dsc->channel[i].set = 0;
dsc->channel[i].pid = 0x1fff;
dsc->channel[i].dsc = dsc;
}
dsc->dvb = advb;
dsc->id = id;
dsc->source = -1;
dsc->dst = -1;
/*Register descrambler device */
return dvb_register_device(&advb->dvb_adapter, &dsc->dev,
&dvbdev_dsc, dsc, DVB_DEVICE_CA, 0);
}
static void aml_dvb_dsc_release(struct aml_dvb *advb,
struct aml_dsc *dsc)
{
if (dsc->dev)
dvb_unregister_device(dsc->dev);
dsc->dev = NULL;
}
/*Show the STB input source*/
static ssize_t stb_show_source(struct class *class,
struct class_attribute *attr, char *buf)
{
struct aml_dvb *dvb = &aml_dvb_device;
ssize_t ret = 0;
char *src;
switch (dvb->stb_source) {
case AM_TS_SRC_TS0:
case AM_TS_SRC_S_TS0:
src = "ts0";
break;
case AM_TS_SRC_TS1:
case AM_TS_SRC_S_TS1:
src = "ts1";
break;
case AM_TS_SRC_TS2:
case AM_TS_SRC_S_TS2:
src = "ts2";
break;
case AM_TS_SRC_TS3:
src = "ts3";
break;
case AM_TS_SRC_HIU:
src = "hiu";
break;
case AM_TS_SRC_HIU1:
src = "hiu1";
break;
case AM_TS_SRC_DMX0:
src = "dmx0";
break;
case AM_TS_SRC_DMX1:
src = "dmx1";
break;
case AM_TS_SRC_DMX2:
src = "dmx2";
break;
default:
src = "disable";
break;
}
ret = sprintf(buf, "%s\n", src);
return ret;
}
static ssize_t stb_clear_av(struct class *class,
struct class_attribute *attr, const char *buf,
size_t size)
{
if (!strncmp("1", buf, 1)) {
aml_tsdemux_set_vid(0x1fff);
aml_tsdemux_set_aid(0x1fff);
aml_tsdemux_set_sid(0x1fff);
aml_tsdemux_set_pcrid(0x1fff);
}
return size;
}
static int stb_check_source(const char *buf)
{
struct aml_dvb *dvb = &aml_dvb_device;
int ret = 0;
char *src;
switch (dvb->stb_source) {
case AM_TS_SRC_TS0:
case AM_TS_SRC_S_TS0:
src = "ts0";
break;
case AM_TS_SRC_TS1:
case AM_TS_SRC_S_TS1:
src = "ts1";
break;
case AM_TS_SRC_TS2:
case AM_TS_SRC_S_TS2:
src = "ts2";
break;
case AM_TS_SRC_TS3:
src = "ts3";
break;
case AM_TS_SRC_HIU:
src = "hiu";
break;
case AM_TS_SRC_HIU1:
src = "hiu1";
break;
case AM_TS_SRC_DMX0:
src = "dmx0";
break;
case AM_TS_SRC_DMX1:
src = "dmx1";
break;
case AM_TS_SRC_DMX2:
src = "dmx2";
break;
default:
src = "disable";
break;
}
pr_error("stb_check_source set buf:%s, src:%s\n", buf, src);
ret = strcmp(buf,src);
return ret;
}
/*Set the STB input source*/
static ssize_t stb_store_source(struct class *class,
struct class_attribute *attr, const char *buf,
size_t size)
{
dmx_source_t src = -1;
if (stb_check_source(buf) == 0) {
pr_error("stb_store_source same source \n");
return size;
}
if (!strncmp("ts0", buf, 3))
src = DMX_SOURCE_FRONT0;
else if (!strncmp("ts1", buf, 3))
src = DMX_SOURCE_FRONT1;
else if (!strncmp("ts2", buf, 3))
src = DMX_SOURCE_FRONT2;
else if (!strncmp("ts3", buf, 3))
src = DMX_SOURCE_FRONT3;
else if (!strncmp("hiu1", buf, 4))
src = DMX_SOURCE_DVR1;
else if (!strncmp("hiu", buf, 3))
src = DMX_SOURCE_DVR0;
else if (!strncmp("dmx0", buf, 4))
src = DMX_SOURCE_FRONT0 + 100;
else if (!strncmp("dmx1", buf, 4))
src = DMX_SOURCE_FRONT1 + 100;
else if (!strncmp("dmx2", buf, 4))
src = DMX_SOURCE_FRONT2 + 100;
if (src != -1)
aml_stb_hw_set_source(&aml_dvb_device, src);
return size;
}
static ssize_t show_dmx_reset_all_flag(struct class *class,
struct class_attribute *attr, char *buf)
{
ssize_t ret = 0;
char *src;
if (dmx_reset_all_flag)
src = "1";
else
src = "0";
ret = sprintf(buf, "%s\n", src);
return ret;
}
static ssize_t set_dmx_reset_all_flag(struct class *class,
struct class_attribute *attr, const char *buf,
size_t size)
{
if (!strncmp("0", buf, 1))
dmx_reset_all_flag = 0;
else if (!strncmp("1", buf, 1))
dmx_reset_all_flag = 1;
return size;
}
#define CASE_PREFIX
/*Show the descrambler's input source*/
#define DSC_SOURCE_FUNC_DECL(i) \
static ssize_t dsc##i##_show_source(struct class *class, \
struct class_attribute *attr, char *buf)\
{\
struct aml_dvb *dvb = &aml_dvb_device;\
struct aml_dsc *dsc = &dvb->dsc[i];\
ssize_t ret = 0;\
char *src, *dst;\
switch (dsc->source) {\
CASE_PREFIX case AM_TS_SRC_DMX0:\
src = "dmx0";\
break;\
CASE_PREFIX case AM_TS_SRC_DMX1:\
src = "dmx1";\
break;\
CASE_PREFIX case AM_TS_SRC_DMX2:\
src = "dmx2";\
break;\
CASE_PREFIX default :\
src = "bypass";\
break;\
} \
switch (dsc->dst) {\
CASE_PREFIX case AM_TS_SRC_DMX0:\
dst = "dmx0";\
break;\
CASE_PREFIX case AM_TS_SRC_DMX1:\
dst = "dmx1";\
break;\
CASE_PREFIX case AM_TS_SRC_DMX2:\
dst = "dmx2";\
break;\
CASE_PREFIX default :\
dst = "bypass";\
break;\
} \
ret = sprintf(buf, "%s-%s\n", src, dst);\
return ret;\
} \
static ssize_t dsc##i##_store_source(struct class *class, \
struct class_attribute *attr, const char *buf, size_t size)\
{\
dmx_source_t src = -1, dst = -1;\
\
if (!strncmp("dmx0", buf, 4)) {\
src = DMX_SOURCE_FRONT0 + 100;\
} else if (!strncmp("dmx1", buf, 4)) {\
src = DMX_SOURCE_FRONT1 + 100;\
} else if (!strncmp("dmx2", buf, 4)) {\
src = DMX_SOURCE_FRONT2 + 100;\
} \
if (buf[4] == '-') {\
if (!strncmp("dmx0", buf+5, 4)) {\
dst = DMX_SOURCE_FRONT0 + 100;\
} else if (!strncmp("dmx1", buf+5, 4)) {\
dst = DMX_SOURCE_FRONT1 + 100;\
} else if (!strncmp("dmx2", buf+5, 4)) {\
dst = DMX_SOURCE_FRONT2 + 100;\
} \
} \
else \
dst = src; \
aml_dsc_hw_set_source(&aml_dvb_device.dsc[i], src, dst);\
return size;\
}
/*Show free descramblers count*/
#define DSC_FREE_FUNC_DECL(i) \
static ssize_t dsc##i##_show_free_dscs(struct class *class, \
struct class_attribute *attr, char *buf) \
{ \
struct aml_dvb *dvb = &aml_dvb_device; \
int fid, count; \
ssize_t ret = 0; \
unsigned long flags;\
\
spin_lock_irqsave(&dvb->slock, flags); \
count = 0; \
for (fid = 0; fid < DSC_COUNT; fid++) { \
if (!dvb->dsc[i].channel[fid].used) \
count++; \
} \
spin_unlock_irqrestore(&dvb->slock, flags); \
\
ret = sprintf(buf, "%d\n", count); \
return ret; \
}
#if DSC_DEV_COUNT > 0
DSC_SOURCE_FUNC_DECL(0)
DSC_FREE_FUNC_DECL(0)
#endif
#if DSC_DEV_COUNT > 1
DSC_SOURCE_FUNC_DECL(1)
DSC_FREE_FUNC_DECL(1)
#endif
/*Show the TS output source*/
static ssize_t tso_show_source(struct class *class,
struct class_attribute *attr, char *buf)
{
struct aml_dvb *dvb = &aml_dvb_device;
ssize_t ret = 0;
char *src;
switch (dvb->tso_source) {
case AM_TS_SRC_TS0:
case AM_TS_SRC_S_TS0:
src = "ts0";
break;
case AM_TS_SRC_TS1:
case AM_TS_SRC_S_TS1:
src = "ts1";
break;
case AM_TS_SRC_TS2:
case AM_TS_SRC_S_TS2:
src = "ts2";
break;
case AM_TS_SRC_TS3:
src = "ts3";
break;
case AM_TS_SRC_HIU:
src = "hiu";
break;
case AM_TS_SRC_DMX0:
src = "dmx0";
break;
case AM_TS_SRC_DMX1:
src = "dmx1";
break;
case AM_TS_SRC_DMX2:
src = "dmx2";
break;
default:
src = "default";
break;
}
ret = sprintf(buf, "%s\n", src);
return ret;
}
/*Set the TS output source*/
static ssize_t tso_store_source(struct class *class,
struct class_attribute *attr, const char *buf,
size_t size)
{
dmx_source_t src = -1;
if (!strncmp("ts0", buf, 3))
src = DMX_SOURCE_FRONT0;
else if (!strncmp("ts1", buf, 3))
src = DMX_SOURCE_FRONT1;
else if (!strncmp("ts2", buf, 3))
src = DMX_SOURCE_FRONT2;
else if (!strncmp("ts3", buf, 3))
src = DMX_SOURCE_FRONT3;
else if (!strncmp("hiu", buf, 3))
src = DMX_SOURCE_DVR0;
else if (!strncmp("dmx0", buf, 4))
src = DMX_SOURCE_FRONT0 + 100;
else if (!strncmp("dmx1", buf, 4))
src = DMX_SOURCE_FRONT1 + 100;
else if (!strncmp("dmx2", buf, 4))
src = DMX_SOURCE_FRONT2 + 100;
aml_tso_hw_set_source(&aml_dvb_device, src);
return size;
}
/*Show PCR*/
#define DEMUX_PCR_FUNC_DECL(i) \
static ssize_t demux##i##_show_pcr(struct class *class, \
struct class_attribute *attr, char *buf)\
{\
int f = 0;\
if (i == 0)\
f = READ_MPEG_REG(PCR_DEMUX);\
else if (i == 1)\
f = READ_MPEG_REG(PCR_DEMUX_2);\
else if (i == 2)\
f = READ_MPEG_REG(PCR_DEMUX_3);\
return sprintf(buf, "%08x\n", f);\
}
static int dmx_check_source(int i, const char *buf)
{
struct aml_dvb *dvb = &aml_dvb_device;
struct aml_dmx *dmx = &dvb->dmx[i];
ssize_t ret = 0;
char *src;
switch (dmx->source) {
case AM_TS_SRC_TS0:
case AM_TS_SRC_S_TS0:
src = "ts0";
break;
case AM_TS_SRC_TS1:
case AM_TS_SRC_S_TS1:
src = "ts1";
break;
case AM_TS_SRC_TS2:
case AM_TS_SRC_S_TS2:
src = "ts2";
break;
case AM_TS_SRC_TS3:
src = "ts3";
break;
case AM_TS_SRC_DMX0:
src = "dmx0";
break;
case AM_TS_SRC_DMX1:
src = "dmx1";
break;
case AM_TS_SRC_DMX2:
src = "dmx2";
break;
case AM_TS_SRC_HIU:
src = "hiu";
break;
case AM_TS_SRC_HIU1:
src = "hiu1";
break;
default :
src = "";
break;
}
pr_error("dmx_check_source:set[%s]src[%s]dmx[%d]dmx->source:%d\n", buf, src, i, dmx->source);
ret = strcmp(buf, src);
return ret;
}
/*Show the STB input source*/
#define DEMUX_SOURCE_FUNC_DECL(i) \
static ssize_t demux##i##_show_source(struct class *class, \
struct class_attribute *attr, char *buf)\
{\
struct aml_dvb *dvb = &aml_dvb_device;\
struct aml_dmx *dmx = &dvb->dmx[i];\
ssize_t ret = 0;\
char *src;\
switch (dmx->source) {\
CASE_PREFIX case AM_TS_SRC_TS0:\
CASE_PREFIX case AM_TS_SRC_S_TS0:\
src = "ts0";\
break;\
CASE_PREFIX case AM_TS_SRC_TS1:\
CASE_PREFIX case AM_TS_SRC_S_TS1:\
src = "ts1";\
break;\
CASE_PREFIX case AM_TS_SRC_TS2:\
CASE_PREFIX case AM_TS_SRC_S_TS2:\
src = "ts2";\
break;\
CASE_PREFIX case AM_TS_SRC_TS3:\
src = "ts3";\
break;\
CASE_PREFIX case AM_TS_SRC_DMX0:\
src = "dmx0";\
break;\
CASE_PREFIX case AM_TS_SRC_DMX1:\
src = "dmx1";\
break;\
CASE_PREFIX case AM_TS_SRC_DMX2:\
src = "dmx2";\
break;\
CASE_PREFIX case AM_TS_SRC_HIU:\
src = "hiu";\
break;\
CASE_PREFIX case AM_TS_SRC_HIU1:\
src = "hiu1";\
break;\
CASE_PREFIX default :\
src = "";\
break;\
} \
ret = sprintf(buf, "%s\n", src);\
return ret;\
} \
static ssize_t demux##i##_store_source(struct class *class, \
struct class_attribute *attr, const char *buf, size_t size)\
{\
dmx_source_t src = -1;\
if (dmx_check_source(i, buf) == 0) {\
pr_error("dmx[%d] source is same [%s]\n", i, buf);\
return size;\
}\
if (!strncmp("ts0", buf, 3)) {\
src = DMX_SOURCE_FRONT0;\
} else if (!strncmp("ts1", buf, 3)) {\
src = DMX_SOURCE_FRONT1;\
} else if (!strncmp("ts2", buf, 3)) {\
src = DMX_SOURCE_FRONT2;\
} else if (!strncmp("ts3", buf, 3)) {\
src = DMX_SOURCE_FRONT3;\
} else if (!strncmp("hiu1", buf, 4)) {\
src = DMX_SOURCE_DVR1;\
} else if (!strncmp("hiu", buf, 3)) {\
src = DMX_SOURCE_DVR0;\
} else if (!strncmp("dmx0", buf, 4)) {\
src = DMX_SOURCE_FRONT0_OFFSET;\
} else if (!strncmp("dmx1", buf, 4)) {\
src = DMX_SOURCE_FRONT1_OFFSET;\
} else if (!strncmp("dmx2", buf, 4)) {\
src = DMX_SOURCE_FRONT2_OFFSET;\
} \
if (src != -1) {\
aml_dmx_hw_set_source(aml_dvb_device.dmx[i].dmxdev.demux, src);\
} \
return size;\
}
/*Show free filters count*/
#define DEMUX_FREE_FILTERS_FUNC_DECL(i) \
static ssize_t demux##i##_show_free_filters(struct class *class, \
struct class_attribute *attr, char *buf)\
{\
struct aml_dvb *dvb = &aml_dvb_device;\
struct dvb_demux *dmx = &dvb->dmx[i].demux;\
int fid, count;\
ssize_t ret = 0;\
if (mutex_lock_interruptible(&dmx->mutex)) \
return -ERESTARTSYS; \
count = 0;\
for (fid = 0; fid < dmx->filternum; fid++) {\
if (!dmx->filter[fid].state != DMX_STATE_FREE)\
count++;\
} \
mutex_unlock(&dmx->mutex);\
ret = sprintf(buf, "%d\n", count);\
return ret;\
}
/*Show dmx dev open count*/
#define DEMUX_DEV_USERS_FUNC_DECL(i) \
static ssize_t demux##i##_show_dev_users(struct class *class, \
struct class_attribute *attr, char *buf)\
{\
struct aml_dvb *dvb = &aml_dvb_device;\
struct dvb_demux *dmx = &dvb->dmx[i].demux;\
int count;\
ssize_t ret = 0;\
if (mutex_lock_interruptible(&dmx->mutex)) \
return -ERESTARTSYS; \
count = dvb->dmx[i].dmxdev.dvbdev->users -1;\
mutex_unlock(&dmx->mutex);\
ret = sprintf(buf, "%d\n", count);\
return ret;\
}
static ssize_t demux_state_show(struct class *class,
struct class_attribute *attr, char *buf)
{
struct aml_dvb *dvb = &aml_dvb_device;
int i = 0, r = 0, j = 0;
struct dvb_demux *dmx;
struct aml_dmx *dmx1;
int fid, count;
ssize_t ret = 0;
char *str = NULL;
for (i = 0; i < 3; i++) {
r = sprintf(buf, "#####dmx%d#######\n", i);
buf += r;
ret += r;
dmx = &dvb->dmx[i].demux;
if (mutex_lock_interruptible(&dmx->mutex))
return -ERESTARTSYS;
r = sprintf(buf, "filter:\n");
buf += r;
ret += r;
count = 0;
for (fid = 0; fid < dmx->filternum; fid++) {
if (!dmx->filter[fid].state != DMX_STATE_FREE)
count++;
else {
r = sprintf(buf, "fid:%d, pid:0x%0x, state:%d\n", fid, dmx->filter[fid].feed->pid,
dmx->filter[fid].state);
buf += r;
ret += r;
}
}
r = sprintf(buf, "used filter:%d, free filter:%d\n", (dmx->filternum - count), count);
buf += r;
ret += r;
r = sprintf(buf, "file users:%d\n", dvb->dmx[i].dmxdev.dvbdev->users);
buf += r;
ret += r;
r = sprintf(buf, "chan:\n");
buf += r;
ret += r;
dmx1 = &dvb->dmx[i];
count = 0;
for (j = 0; j < CHANNEL_COUNT; j++) {
if (dmx1->channel[j].used) {
if (dmx1->channel[j].type == DMX_TYPE_TS) {
if (dmx1->channel[j].pes_type == DMX_PES_VIDEO) {
str = "video";
} else if (dmx1->channel[j].pes_type == DMX_PES_AUDIO){
str = "audio";
} else if (dmx1->channel[j].pes_type == DMX_PES_SUBTITLE) {
str = "sub";
} else if (dmx1->channel[j].pes_type == DMX_PES_TELETEXT) {
str = "ttx";
} else if (dmx1->channel[j].pes_type == DMX_PES_TELETEXT) {
str = "other";
}
} else {
str = "sec";
}
count ++;
r = sprintf(buf, "id:%d, type:%s, pid:0x%0x\n", j, str, dmx1->channel[j].pid);
buf += r;
ret += r;
}
}
r = sprintf(buf, "used chan:%d, free chan:%d\n", count, (CHANNEL_COUNT - count));
buf += r;
ret += r;
mutex_unlock(&dmx->mutex);
}
return ret;
}
/*Show filter users count*/
#define DEMUX_FILTER_USERS_FUNC_DECL(i) \
static ssize_t demux##i##_show_filter_users(struct class *class, \
struct class_attribute *attr, char *buf)\
{\
struct aml_dvb *dvb = &aml_dvb_device;\
struct aml_dmx *dmx = &dvb->dmx[i];\
int dmxdevfid, count;\
ssize_t ret = 0;\
unsigned long flags;\
spin_lock_irqsave(&dvb->slock, flags);\
count = 0;\
for (dmxdevfid = 0; dmxdevfid < dmx->dmxdev.filternum; dmxdevfid++) {\
if (dmx->dmxdev.filter[dmxdevfid].state >= \
DMXDEV_STATE_ALLOCATED)\
count++;\
} \
if (count > dmx->demux_filter_user) {\
count = dmx->demux_filter_user;\
} else{\
dmx->demux_filter_user = count;\
} \
spin_unlock_irqrestore(&dvb->slock, flags);\
ret = sprintf(buf, "%d\n", count);\
return ret;\
} \
static ssize_t demux##i##_store_filter_used(struct class *class, \
struct class_attribute *attr, const char *buf, size_t size)\
{\
struct aml_dvb *dvb = &aml_dvb_device;\
struct aml_dmx *dmx = &dvb->dmx[i];\
unsigned long filter_used;\
unsigned long flags;/*char *endp;*/\
/*filter_used = simple_strtol(buf, &endp, 0);*/\
int ret = kstrtol(buf, 0, &filter_used);\
spin_lock_irqsave(&dvb->slock, flags);\
if (ret == 0 && filter_used) {\
if (dmx->demux_filter_user < FILTER_COUNT)\
dmx->demux_filter_user++;\
} else {\
if (dmx->demux_filter_user > 0)\
dmx->demux_filter_user--;\
} \
spin_unlock_irqrestore(&dvb->slock, flags);\
return size;\
}
/*Show ts header*/
#define DEMUX_TS_HEADER_FUNC_DECL(i) \
static ssize_t demux##i##_show_ts_header(struct class *class, \
struct class_attribute *attr, char *buf)\
{\
int hdr = 0;\
if (i == 0)\
hdr = READ_MPEG_REG(TS_HEAD_1);\
else if (i == 1)\
hdr = READ_MPEG_REG(TS_HEAD_1_2);\
else if (i == 2)\
hdr = READ_MPEG_REG(TS_HEAD_1_3);\
return sprintf(buf, "%08x\n", hdr);\
}
/*Show channel activity*/
#define DEMUX_CHANNEL_ACTIVITY_FUNC_DECL(i) \
static ssize_t demux##i##_show_channel_activity(struct class *class, \
struct class_attribute *attr, char *buf)\
{\
int f = 0;\
if (i == 0)\
f = READ_MPEG_REG(DEMUX_CHANNEL_ACTIVITY);\
else if (i == 1)\
f = READ_MPEG_REG(DEMUX_CHANNEL_ACTIVITY_2);\
else if (i == 2)\
f = READ_MPEG_REG(DEMUX_CHANNEL_ACTIVITY_3);\
return sprintf(buf, "%08x\n", f);\
}
#define DEMUX_RESET_FUNC_DECL(i) \
static ssize_t demux##i##_reset_store(struct class *class, \
struct class_attribute *attr, \
const char *buf, size_t size)\
{\
if (!strncmp("1", buf, 1)) { \
struct aml_dvb *dvb = &aml_dvb_device; \
pr_inf("Reset demux["#i"], call dmx_reset_dmx_hw\n"); \
dmx_reset_dmx_id_hw_ex(dvb, i, 0); \
} \
return size; \
}
/*DVR record mode*/
#define DVR_MODE_FUNC_DECL(i) \
static ssize_t dvr##i##_show_mode(struct class *class, \
struct class_attribute *attr, char *buf)\
{\
struct aml_dvb *dvb = &aml_dvb_device;\
struct aml_dmx *dmx = &dvb->dmx[i];\
ssize_t ret = 0;\
char *mode;\
if (dmx->dump_ts_select) {\
mode = "ts";\
} else {\
mode = "pid";\
} \
ret = sprintf(buf, "%s\n", mode);\
return ret;\
} \
static ssize_t dvr##i##_store_mode(struct class *class, \
struct class_attribute *attr, const char *buf, size_t size)\
{\
struct aml_dvb *dvb = &aml_dvb_device;\
struct aml_dmx *dmx = &dvb->dmx[i];\
int dump_ts_select = -1;\
\
if (!strncmp("pid", buf, 3) && dmx->dump_ts_select) {\
dump_ts_select = 0;\
} else if (!strncmp("ts", buf, 2) && !dmx->dump_ts_select) {\
dump_ts_select = 1;\
} \
if (dump_ts_select != -1) {\
aml_dmx_hw_set_dump_ts_select(\
aml_dvb_device.dmx[i].dmxdev.demux, dump_ts_select);\
} \
return size;\
}
#if DMX_DEV_COUNT > 0
DEMUX_PCR_FUNC_DECL(0)
DEMUX_SOURCE_FUNC_DECL(0)
DEMUX_FREE_FILTERS_FUNC_DECL(0)
DEMUX_FILTER_USERS_FUNC_DECL(0)
DEMUX_DEV_USERS_FUNC_DECL(0)
DVR_MODE_FUNC_DECL(0)
DEMUX_TS_HEADER_FUNC_DECL(0)
DEMUX_CHANNEL_ACTIVITY_FUNC_DECL(0)
DEMUX_RESET_FUNC_DECL(0)
#endif
#if DMX_DEV_COUNT > 1
DEMUX_PCR_FUNC_DECL(1)
DEMUX_SOURCE_FUNC_DECL(1)
DEMUX_FREE_FILTERS_FUNC_DECL(1)
DEMUX_FILTER_USERS_FUNC_DECL(1)
DEMUX_DEV_USERS_FUNC_DECL(1)
DVR_MODE_FUNC_DECL(1)
DEMUX_TS_HEADER_FUNC_DECL(1)
DEMUX_CHANNEL_ACTIVITY_FUNC_DECL(1)
DEMUX_RESET_FUNC_DECL(1)
#endif
#if DMX_DEV_COUNT > 2
DEMUX_PCR_FUNC_DECL(2)
DEMUX_SOURCE_FUNC_DECL(2)
DEMUX_FREE_FILTERS_FUNC_DECL(2)
DEMUX_FILTER_USERS_FUNC_DECL(2)
DEMUX_DEV_USERS_FUNC_DECL(2)
DVR_MODE_FUNC_DECL(2)
DEMUX_TS_HEADER_FUNC_DECL(2)
DEMUX_CHANNEL_ACTIVITY_FUNC_DECL(2)
DEMUX_RESET_FUNC_DECL(2)
#endif
/*Show the async fifo source*/
#define ASYNCFIFO_SOURCE_FUNC_DECL(i) \
static ssize_t asyncfifo##i##_show_source(struct class *class, \
struct class_attribute *attr, char *buf)\
{\
struct aml_dvb *dvb = &aml_dvb_device;\
struct aml_asyncfifo *afifo = &dvb->asyncfifo[i];\
ssize_t ret = 0;\
char *src;\
if (dvb->async_fifo_total_count <= i)\
return ret;\
switch (afifo->source) {\
CASE_PREFIX case AM_DMX_0:\
src = "dmx0";\
break;\
CASE_PREFIX case AM_DMX_1:\
src = "dmx1";\
break; \
CASE_PREFIX case AM_DMX_2:\
src = "dmx2";\
break;\
CASE_PREFIX default :\
src = "";\
break;\
} \
ret = sprintf(buf, "%s\n", src);\
return ret;\
} \
static ssize_t asyncfifo##i##_store_source(struct class *class, \
struct class_attribute *attr, const char *buf, size_t size)\
{\
enum aml_dmx_id_t src = -1;\
\
if (aml_dvb_device.async_fifo_total_count <= i)\
return 0;\
if (!strncmp("dmx0", buf, 4)) {\
src = AM_DMX_0;\
} else if (!strncmp("dmx1", buf, 4)) {\
src = AM_DMX_1;\
} else if (!strncmp("dmx2", buf, 4)) {\
src = AM_DMX_2;\
} \
if (src != -1) {\
aml_asyncfifo_hw_set_source(&aml_dvb_device.asyncfifo[i], src);\
} \
return size;\
}
#if ASYNCFIFO_COUNT > 0
ASYNCFIFO_SOURCE_FUNC_DECL(0)
#endif
#if ASYNCFIFO_COUNT > 1
ASYNCFIFO_SOURCE_FUNC_DECL(1)
#endif
#if ASYNCFIFO_COUNT > 2
ASYNCFIFO_SOURCE_FUNC_DECL(2)
#endif
/*Show the async fifo flush size*/
#define ASYNCFIFO_FLUSHSIZE_FUNC_DECL(i) \
static ssize_t asyncfifo##i##_show_flush_size(struct class *class, \
struct class_attribute *attr, char *buf)\
{\
struct aml_dvb *dvb = &aml_dvb_device;\
struct aml_asyncfifo *afifo = &dvb->asyncfifo[i];\
ssize_t ret = 0;\
if (dvb->async_fifo_total_count <= i)\
return ret;\
ret = sprintf(buf, "%d\n", afifo->flush_size);\
return ret;\
} \
static ssize_t asyncfifo##i##_store_flush_size(struct class *class, \
struct class_attribute *attr, \
const char *buf, size_t size)\
{\
struct aml_dvb *dvb = &aml_dvb_device;\
struct aml_asyncfifo *afifo = &dvb->asyncfifo[i];\
/*int fsize = simple_strtol(buf, NULL, 10);*/\
int fsize = 0;\
long value;\
int ret =0;\
if (dvb->async_fifo_total_count <= i)\
return (size_t)0;\
ret = kstrtol(buf, 0, &value);\
if (ret == 0)\
fsize = value;\
if (fsize != afifo->flush_size) {\
afifo->flush_size = fsize;\
aml_asyncfifo_hw_reset(&aml_dvb_device.asyncfifo[i]);\
} \
return size;\
}
#if ASYNCFIFO_COUNT > 0
ASYNCFIFO_FLUSHSIZE_FUNC_DECL(0)
#endif
#if ASYNCFIFO_COUNT > 1
ASYNCFIFO_FLUSHSIZE_FUNC_DECL(1)
#endif
#if ASYNCFIFO_COUNT > 2
ASYNCFIFO_FLUSHSIZE_FUNC_DECL(2)
#endif
/*Show the async fifo secure buffer addr*/
#define ASYNCFIFO_SECUREADDR_FUNC_DECL(i) \
static ssize_t asyncfifo##i##_show_secure_addr(struct class *class, \
struct class_attribute *attr, char *buf)\
{\
struct aml_dvb *dvb = &aml_dvb_device;\
struct aml_asyncfifo *afifo = &dvb->asyncfifo[i];\
ssize_t ret = 0;\
if (dvb->async_fifo_total_count <= i)\
return ret;\
ret = sprintf(buf, "0x%x\n", afifo->blk.addr);\
return ret;\
} \
static ssize_t asyncfifo##i##_store_secure_addr(struct class *class, \
struct class_attribute *attr, \
const char *buf, size_t size)\
{\
struct aml_dvb *dvb = &aml_dvb_device;\
struct aml_asyncfifo *afifo = &dvb->asyncfifo[i];\
unsigned long value;\
int ret=0;\
if (dvb->async_fifo_total_count <= i)\
return (size_t)0;\
ret = kstrtol(buf, 0, &value);\
if (ret == 0 && value != afifo->blk.addr) {\
afifo->blk.addr = value;\
aml_asyncfifo_hw_reset(&aml_dvb_device.asyncfifo[i]);\
} \
return size;\
}
#if ASYNCFIFO_COUNT > 0
ASYNCFIFO_SECUREADDR_FUNC_DECL(0)
#endif
#if ASYNCFIFO_COUNT > 1
ASYNCFIFO_SECUREADDR_FUNC_DECL(1)
#endif
#if ASYNCFIFO_COUNT > 2
ASYNCFIFO_SECUREADDR_FUNC_DECL(2)
#endif
/*Show the async fifo secure buffer size*/
#define ASYNCFIFO_SECUREADDR_SIZE_FUNC_DECL(i) \
static ssize_t asyncfifo##i##_show_secure_addr_size(struct class *class, \
struct class_attribute *attr, char *buf)\
{\
struct aml_dvb *dvb = &aml_dvb_device;\
struct aml_asyncfifo *afifo = &dvb->asyncfifo[i];\
ssize_t ret = 0;\
if (dvb->async_fifo_total_count <= i)\
return ret;\
ret = sprintf(buf, "0x%x\n", afifo->blk.len);\
return ret;\
} \
static ssize_t asyncfifo##i##_store_secure_addr_size(struct class *class, \
struct class_attribute *attr, \
const char *buf, size_t size)\
{\
struct aml_dvb *dvb = &aml_dvb_device;\
struct aml_asyncfifo *afifo = &dvb->asyncfifo[i];\
unsigned long value;\
int ret=0;\
if (dvb->async_fifo_total_count <= i)\
return (size_t)0;\
ret = kstrtol(buf, 0, &value);\
if (ret == 0 && value != afifo->blk.len) {\
afifo->blk.len = value;\
aml_asyncfifo_hw_reset(&aml_dvb_device.asyncfifo[i]);\
} \
return size;\
}
#if ASYNCFIFO_COUNT > 0
ASYNCFIFO_SECUREADDR_SIZE_FUNC_DECL(0)
#endif
#if ASYNCFIFO_COUNT > 1
ASYNCFIFO_SECUREADDR_SIZE_FUNC_DECL(1)
#endif
#if ASYNCFIFO_COUNT > 2
ASYNCFIFO_SECUREADDR_SIZE_FUNC_DECL(2)
#endif
/*Show the async fifo secure enable*/
#define ASYNCFIFO_SECURENABLE_FUNC_DECL(i) \
static ssize_t asyncfifo##i##_show_secure_enable(struct class *class, \
struct class_attribute *attr, char *buf)\
{\
struct aml_dvb *dvb = &aml_dvb_device;\
struct aml_asyncfifo *afifo = &dvb->asyncfifo[i];\
ssize_t ret = 0;\
if (dvb->async_fifo_total_count <= i)\
return ret;\
ret = sprintf(buf, "%d\n", afifo->secure_enable);\
return ret;\
} \
static ssize_t asyncfifo##i##_store_secure_enable(struct class *class, \
struct class_attribute *attr, \
const char *buf, size_t size)\
{\
struct aml_dvb *dvb = &aml_dvb_device;\
struct aml_asyncfifo *afifo = &dvb->asyncfifo[i];\
int enable = 0;\
long value;\
int ret=0;\
if (dvb->async_fifo_total_count <= i)\
return (size_t)0;\
ret = kstrtol(buf, 0, &value);\
if (ret == 0)\
enable = value;\
if (enable != afifo->secure_enable) {\
afifo->secure_enable = enable;\
aml_asyncfifo_hw_reset(&aml_dvb_device.asyncfifo[i]);\
} \
return size;\
}
#if ASYNCFIFO_COUNT > 0
ASYNCFIFO_SECURENABLE_FUNC_DECL(0)
#endif
#if ASYNCFIFO_COUNT > 1
ASYNCFIFO_SECURENABLE_FUNC_DECL(1)
#endif
#if ASYNCFIFO_COUNT > 2
ASYNCFIFO_SECURENABLE_FUNC_DECL(2)
#endif
/*Reset the Demux*/
static ssize_t demux_do_reset(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size)
{
if (!strncmp("1", buf, 1)) {
struct aml_dvb *dvb = &aml_dvb_device;
unsigned long flags;
spin_lock_irqsave(&dvb->slock, flags);
pr_inf("Reset demux, call dmx_reset_hw\n");
dmx_reset_hw_ex(dvb, 0);
spin_unlock_irqrestore(&dvb->slock, flags);
}
return size;
}
/*Show the Video PTS value*/
static ssize_t demux_show_video_pts(struct class *class,
struct class_attribute *attr, char *buf)
{
struct aml_dvb *dvb = &aml_dvb_device;
ssize_t ret = 0;
ret = sprintf(buf, "%u\n", aml_dmx_get_video_pts(dvb));
return ret;
}
/*Show the Audio PTS value*/
static ssize_t demux_show_audio_pts(struct class *class,
struct class_attribute *attr, char *buf)
{
struct aml_dvb *dvb = &aml_dvb_device;
ssize_t ret = 0;
ret = sprintf(buf, "%u\n", aml_dmx_get_audio_pts(dvb));
return ret;
}
/*Show the Video PTS bit32 value*/
static ssize_t demux_show_video_pts_bit32(struct class *class,
struct class_attribute *attr, char *buf)
{
struct aml_dvb *dvb = &aml_dvb_device;
ssize_t ret = 0;
ret = sprintf(buf, "%u\n", aml_dmx_get_video_pts_bit32(dvb));
return ret;
}
/*Show the Audio PTS bit32 value*/
static ssize_t demux_show_audio_pts_bit32(struct class *class,
struct class_attribute *attr, char *buf)
{
struct aml_dvb *dvb = &aml_dvb_device;
ssize_t ret = 0;
ret = sprintf(buf, "%u\n", aml_dmx_get_audio_pts_bit32(dvb));
return ret;
}
/*Show the 33bit Video PTS value*/
static ssize_t demux_show_video_pts_u64(struct class *class,
struct class_attribute *attr, char *buf)
{
struct aml_dvb *dvb = &aml_dvb_device;
ssize_t ret = 0;
u64 pts_val = aml_dmx_get_video_pts(dvb);
pts_val &= 0x00000000FFFFFFFF;
if (aml_dmx_get_video_pts_bit32(dvb)) {
pts_val = pts_val | (1LL<<32);
}
ret = sprintf(buf, "%llu\n", pts_val);
return ret;
}
/*Show the 33bit Audio PTS value*/
static ssize_t demux_show_audio_pts_u64(struct class *class,
struct class_attribute *attr, char *buf)
{
struct aml_dvb *dvb = &aml_dvb_device;
ssize_t ret = 0;
u64 pts_val = aml_dmx_get_audio_pts(dvb);
pts_val &= 0x00000000FFFFFFFF;
if (aml_dmx_get_audio_pts_bit32(dvb)) {
pts_val = pts_val | (1LL<<32);
}
ret = sprintf(buf, "%llu\n", pts_val);
return ret;
}
/*Show the First Video PTS value*/
static ssize_t demux_show_first_video_pts(struct class *class,
struct class_attribute *attr,
char *buf)
{
struct aml_dvb *dvb = &aml_dvb_device;
ssize_t ret = 0;
ret = sprintf(buf, "%u\n", aml_dmx_get_first_video_pts(dvb));
return ret;
}
/*Show the First Audio PTS value*/
static ssize_t demux_show_first_audio_pts(struct class *class,
struct class_attribute *attr,
char *buf)
{
struct aml_dvb *dvb = &aml_dvb_device;
ssize_t ret = 0;
ret = sprintf(buf, "%u\n", aml_dmx_get_first_audio_pts(dvb));
return ret;
}
static ssize_t stb_show_hw_setting(struct class *class,
struct class_attribute *attr, char *buf)
{
int r, total = 0;
int i;
struct aml_dvb *dvb = &aml_dvb_device;
int invert, ctrl;
for (i = 0; i < dvb->ts_in_total_count; i++) {
struct aml_ts_input *ts = &dvb->ts[i];
if (ts->s2p_id != -1)
invert = dvb->s2p[ts->s2p_id].invert;
else
invert = 0;
ctrl = ts->control;
r = sprintf(buf, "ts%d %s control: 0x%x invert: 0x%x\n", i,
ts->mode == AM_TS_DISABLE ? "disable" :
(ts->mode == AM_TS_SERIAL ? "serial" :
"parallel"), ctrl, invert);
buf += r;
total += r;
}
return total;
}
static ssize_t stb_store_hw_setting(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
int id, ctrl, invert, r, mode;
char mname[32];
char pname[32];
unsigned long flags;
struct aml_ts_input *ts;
struct aml_dvb *dvb = &aml_dvb_device;
r = sscanf(buf, "%d %s %x %x", &id, mname, &ctrl, &invert);
if (r != 4)
return -EINVAL;
if (id < 0 || id >= dvb->ts_in_total_count)
return -EINVAL;
if ((mname[0] == 's') || (mname[0] == 'S')) {
sprintf(pname, "s_ts%d", id);
mode = AM_TS_SERIAL;
} else if ((mname[0] == 'p') || (mname[0] == 'P')) {
sprintf(pname, "p_ts%d", id);
mode = AM_TS_PARALLEL;
} else
mode = AM_TS_DISABLE;
spin_lock_irqsave(&dvb->slock, flags);
ts = &dvb->ts[id];
if ((mode == AM_TS_SERIAL) && (ts->mode != AM_TS_SERIAL)) {
int i;
int scnt = 0;
for (i = 0; i < dvb->ts_in_total_count; i++) {
if (dvb->ts[i].s2p_id != -1)
scnt++;
}
if (scnt >= dvb->s2p_total_count)
pr_error("no free s2p\n");
else
ts->s2p_id = scnt;
}
if ((mode != AM_TS_SERIAL) || (ts->s2p_id != -1)) {
if (ts->pinctrl) {
devm_pinctrl_put(ts->pinctrl);
ts->pinctrl = NULL;
}
ts->pinctrl = devm_pinctrl_get_select(&dvb->pdev->dev, pname);
/* if(IS_ERR_VALUE(ts->pinctrl))*/
/* ts->pinctrl = NULL;*/
ts->mode = mode;
ts->control = ctrl;
if (mode == AM_TS_SERIAL)
dvb->s2p[ts->s2p_id].invert = invert;
else
ts->s2p_id = -1;
}
spin_unlock_irqrestore(&dvb->slock, flags);
return count;
}
static struct class_attribute aml_stb_class_attrs[] = {
__ATTR(hw_setting, 0664, stb_show_hw_setting,
stb_store_hw_setting),
__ATTR(source, 0664, stb_show_source,
stb_store_source),
__ATTR(demux_reset_all_flag, 0664, show_dmx_reset_all_flag,
set_dmx_reset_all_flag),
__ATTR(tso_source, 0644, tso_show_source,
tso_store_source),
#define DEMUX_SOURCE_ATTR_PCR(i)\
__ATTR(demux##i##_pcr, 0644, demux##i##_show_pcr, NULL)
#define DEMUX_SOURCE_ATTR_DECL(i)\
__ATTR(demux##i##_source, 0664,\
demux##i##_show_source, demux##i##_store_source)
#define DEMUX_FREE_FILTERS_ATTR_DECL(i)\
__ATTR(demux##i##_free_filters, 0644, \
demux##i##_show_free_filters, NULL)
#define DEMUX_FILTER_USERS_ATTR_DECL(i)\
__ATTR(demux##i##_filter_users, 0644, \
demux##i##_show_filter_users, demux##i##_store_filter_used)
#define DEMUX_DEV_USERS_ATTR_DECL(i)\
__ATTR(demux##i##_dev_users, 0644, \
demux##i##_show_dev_users, NULL)
#define DVR_MODE_ATTR_DECL(i)\
__ATTR(dvr##i##_mode, 0644, dvr##i##_show_mode, \
dvr##i##_store_mode)
#define DEMUX_TS_HEADER_ATTR_DECL(i)\
__ATTR(demux##i##_ts_header, 0644, \
demux##i##_show_ts_header, NULL)
#define DEMUX_CHANNEL_ACTIVITY_ATTR_DECL(i)\
__ATTR(demux##i##_channel_activity, 0644, \
demux##i##_show_channel_activity, NULL)
#define DMX_RESET_ATTR_DECL(i)\
__ATTR(demux##i##_reset, 0644, NULL, \
demux##i##_reset_store)
#if DMX_DEV_COUNT > 0
DEMUX_SOURCE_ATTR_PCR(0),
DEMUX_SOURCE_ATTR_DECL(0),
DEMUX_FREE_FILTERS_ATTR_DECL(0),
DEMUX_FILTER_USERS_ATTR_DECL(0),
DEMUX_DEV_USERS_ATTR_DECL(0),
DVR_MODE_ATTR_DECL(0),
DEMUX_TS_HEADER_ATTR_DECL(0),
DEMUX_CHANNEL_ACTIVITY_ATTR_DECL(0),
DMX_RESET_ATTR_DECL(0),
#endif
#if DMX_DEV_COUNT > 1
DEMUX_SOURCE_ATTR_PCR(1),
DEMUX_SOURCE_ATTR_DECL(1),
DEMUX_FREE_FILTERS_ATTR_DECL(1),
DEMUX_FILTER_USERS_ATTR_DECL(1),
DEMUX_DEV_USERS_ATTR_DECL(1),
DVR_MODE_ATTR_DECL(1),
DEMUX_TS_HEADER_ATTR_DECL(1),
DEMUX_CHANNEL_ACTIVITY_ATTR_DECL(1),
DMX_RESET_ATTR_DECL(1),
#endif
#if DMX_DEV_COUNT > 2
DEMUX_SOURCE_ATTR_PCR(2),
DEMUX_SOURCE_ATTR_DECL(2),
DEMUX_FREE_FILTERS_ATTR_DECL(2),
DEMUX_FILTER_USERS_ATTR_DECL(2),
DEMUX_DEV_USERS_ATTR_DECL(2),
DVR_MODE_ATTR_DECL(2),
DEMUX_TS_HEADER_ATTR_DECL(2),
DEMUX_CHANNEL_ACTIVITY_ATTR_DECL(2),
DMX_RESET_ATTR_DECL(2),
#endif
#define ASYNCFIFO_SOURCE_ATTR_DECL(i)\
__ATTR(asyncfifo##i##_source, 0664, \
asyncfifo##i##_show_source, asyncfifo##i##_store_source)
#define ASYNCFIFO_FLUSHSIZE_ATTR_DECL(i)\
__ATTR(asyncfifo##i##_flush_size, 0664,\
asyncfifo##i##_show_flush_size, \
asyncfifo##i##_store_flush_size)
#define ASYNCFIFO_SECUREADDR_ATTR_DECL(i)\
__ATTR(asyncfifo##i##_secure_addr, S_IRUGO | S_IWUSR | S_IWGRP,\
asyncfifo##i##_show_secure_addr, \
asyncfifo##i##_store_secure_addr)
#define ASYNCFIFO_SECUREADDR_SIZE_ATTR_DECL(i)\
__ATTR(asyncfifo##i##_secure_addr_size, S_IRUGO | S_IWUSR | S_IWGRP,\
asyncfifo##i##_show_secure_addr_size, \
asyncfifo##i##_store_secure_addr_size)
#define ASYNCFIFO_SECURENABLE_ATTR_DECL(i)\
__ATTR(asyncfifo##i##_secure_enable, S_IRUGO | S_IWUSR | S_IWGRP,\
asyncfifo##i##_show_secure_enable, \
asyncfifo##i##_store_secure_enable)
#if ASYNCFIFO_COUNT > 0
ASYNCFIFO_SOURCE_ATTR_DECL(0),
ASYNCFIFO_FLUSHSIZE_ATTR_DECL(0),
ASYNCFIFO_SECUREADDR_ATTR_DECL(0),
ASYNCFIFO_SECUREADDR_SIZE_ATTR_DECL(0),
ASYNCFIFO_SECURENABLE_ATTR_DECL(0),
#endif
#if ASYNCFIFO_COUNT > 1
ASYNCFIFO_SOURCE_ATTR_DECL(1),
ASYNCFIFO_FLUSHSIZE_ATTR_DECL(1),
ASYNCFIFO_SECUREADDR_ATTR_DECL(1),
ASYNCFIFO_SECUREADDR_SIZE_ATTR_DECL(1),
ASYNCFIFO_SECURENABLE_ATTR_DECL(1),
#endif
#if ASYNCFIFO_COUNT > 2
ASYNCFIFO_SOURCE_ATTR_DECL(2),
ASYNCFIFO_FLUSHSIZE_ATTR_DECL(2),
ASYNCFIFO_SECUREADDR_ATTR_DECL(2),
ASYNCFIFO_SECUREADDR_SIZE_ATTR_DECL(2),
ASYNCFIFO_SECURENABLE_ATTR_DECL(2),
#endif
__ATTR(demux_reset, 0644, NULL, demux_do_reset),
__ATTR(video_pts, 0664, demux_show_video_pts,
NULL),
__ATTR(audio_pts, 0664, demux_show_audio_pts,
NULL),
__ATTR(video_pts_bit32, 0644, demux_show_video_pts_bit32, NULL),
__ATTR(audio_pts_bit32, 0644, demux_show_audio_pts_bit32, NULL),
__ATTR(video_pts_u64, 0644, demux_show_video_pts_u64, NULL),
__ATTR(audio_pts_u64, 0644, demux_show_audio_pts_u64, NULL),
__ATTR(first_video_pts, 0644, demux_show_first_video_pts,
NULL),
__ATTR(first_audio_pts, 0644, demux_show_first_audio_pts,
NULL),
__ATTR(clear_av, 0644, NULL, stb_clear_av),
__ATTR(demux_state, 0644, demux_state_show, NULL),
#define DSC_SOURCE_ATTR_DECL(i)\
__ATTR(dsc##i##_source, 0664,\
dsc##i##_show_source, dsc##i##_store_source)
#define DSC_FREE_ATTR_DECL(i) \
__ATTR(dsc##i##_free_dscs, 0644, \
dsc##i##_show_free_dscs, NULL)
#if DSC_DEV_COUNT > 0
DSC_SOURCE_ATTR_DECL(0),
DSC_FREE_ATTR_DECL(0),
#endif
#if DSC_DEV_COUNT > 1
DSC_SOURCE_ATTR_DECL(1),
DSC_FREE_ATTR_DECL(1),
#endif
__ATTR_NULL
};
static struct class aml_stb_class = {
.name = "stb",
.class_attrs = aml_stb_class_attrs,
};
/*
*extern int aml_regist_dmx_class(void);
*extern int aml_unregist_dmx_class(void);
*/
/*
*void afifo_reset(int v)
*{
* if (v)
* reset_control_assert(aml_dvb_afifo_reset_ctl);
* else
* reset_control_deassert(aml_dvb_afifo_reset_ctl);
*}
*/
static int aml_dvb_probe(struct platform_device *pdev)
{
struct aml_dvb *advb;
int i, ret = 0;
struct devio_aml_platform_data *pd_dvb;
pr_inf("probe amlogic dvb driver [%s]\n", DVB_VERSION);
/*switch_mod_gate_by_name("demux", 1); */
#if 0
/*no used reset ctl to set clk*/
aml_dvb_demux_reset_ctl =
devm_reset_control_get(&pdev->dev, "demux");
pr_inf("dmx rst ctl = %p\n", aml_dvb_demux_reset_ctl);
reset_control_deassert(aml_dvb_demux_reset_ctl);
aml_dvb_afifo_reset_ctl =
devm_reset_control_get(&pdev->dev, "asyncfifo");
pr_inf("asyncfifo rst ctl = %p\n", aml_dvb_afifo_reset_ctl);
reset_control_deassert(aml_dvb_afifo_reset_ctl);
aml_dvb_ahbarb0_reset_ctl =
devm_reset_control_get(&pdev->dev, "ahbarb0");
pr_inf("ahbarb0 rst ctl = %p\n", aml_dvb_ahbarb0_reset_ctl);
reset_control_deassert(aml_dvb_ahbarb0_reset_ctl);
aml_dvb_uparsertop_reset_ctl =
devm_reset_control_get(&pdev->dev, "uparsertop");
pr_inf("uparsertop rst ctl = %p\n", aml_dvb_uparsertop_reset_ctl);
reset_control_deassert(aml_dvb_uparsertop_reset_ctl);
#else
if (get_cpu_type() < MESON_CPU_MAJOR_ID_G12A)
{
aml_dvb_demux_clk =
devm_clk_get(&pdev->dev, "demux");
if (IS_ERR_OR_NULL(aml_dvb_demux_clk)) {
dev_err(&pdev->dev, "get demux clk fail\n");
return -1;
}
clk_prepare_enable(aml_dvb_demux_clk);
aml_dvb_afifo_clk =
devm_clk_get(&pdev->dev, "asyncfifo");
if (IS_ERR_OR_NULL(aml_dvb_afifo_clk)) {
dev_err(&pdev->dev, "get asyncfifo clk fail\n");
return -1;
}
clk_prepare_enable(aml_dvb_afifo_clk);
aml_dvb_ahbarb0_clk =
devm_clk_get(&pdev->dev, "ahbarb0");
if (IS_ERR_OR_NULL(aml_dvb_ahbarb0_clk)) {
dev_err(&pdev->dev, "get ahbarb0 clk fail\n");
return -1;
}
clk_prepare_enable(aml_dvb_ahbarb0_clk);
aml_dvb_uparsertop_clk =
devm_clk_get(&pdev->dev, "uparsertop");
if (IS_ERR_OR_NULL(aml_dvb_uparsertop_clk)) {
dev_err(&pdev->dev, "get uparsertop clk fail\n");
return -1;
}
clk_prepare_enable(aml_dvb_uparsertop_clk);
}
else
{
amports_switch_gate("demux", 1);
amports_switch_gate("ahbarb0", 1);
amports_switch_gate("parser_top", 1);
if (get_cpu_type() == MESON_CPU_MAJOR_ID_TL1)
{
aml_dvb_afifo_clk =
devm_clk_get(&pdev->dev, "asyncfifo");
if (IS_ERR_OR_NULL(aml_dvb_afifo_clk)) {
dev_err(&pdev->dev, "get asyncfifo clk fail\n");
return -1;
}
clk_prepare_enable(aml_dvb_afifo_clk);
}
}
#endif
advb = &aml_dvb_device;
memset(advb, 0, sizeof(aml_dvb_device));
spin_lock_init(&advb->slock);
advb->dev = &pdev->dev;
advb->pdev = pdev;
advb->stb_source = -1;
advb->tso_source = -1;
if (get_cpu_type() < MESON_CPU_MAJOR_ID_TL1) {
advb->ts_in_total_count = 3;
advb->s2p_total_count = 2;
advb->async_fifo_total_count = 2;
} else {
advb->ts_in_total_count = 4;
advb->s2p_total_count = 3;
advb->async_fifo_total_count = 3;
}
for (i = 0; i < DMX_DEV_COUNT; i++) {
advb->dmx[i].dmx_irq = -1;
advb->dmx[i].dvr_irq = -1;
}
#ifdef CONFIG_OF
if (pdev->dev.of_node) {
int s2p_id = 0;
char buf[32];
const char *str;
u32 value;
for (i = 0; i < advb->ts_in_total_count; i++) {
advb->ts[i].mode = AM_TS_DISABLE;
advb->ts[i].s2p_id = -1;
advb->ts[i].pinctrl = NULL;
memset(buf, 0, 32);
snprintf(buf, sizeof(buf), "ts%d", i);
ret =
of_property_read_string(pdev->dev.of_node, buf,
&str);
if (!ret) {
if (!strcmp(str, "serial")) {
pr_dbg("%s: serial\n", buf);
if (s2p_id >= advb->s2p_total_count)
pr_error("no free s2p\n");
else {
snprintf(buf, sizeof(buf),
"s_ts%d", i);
advb->ts[i].mode = AM_TS_SERIAL;
advb->ts[i].pinctrl =
devm_pinctrl_get_select
(&pdev->dev, buf);
advb->ts[i].s2p_id = s2p_id;
s2p_id++;
}
} else if (!strcmp(str, "parallel")) {
pr_dbg("%s: parallel\n", buf);
memset(buf, 0, 32);
snprintf(buf, sizeof(buf), "p_ts%d", i);
advb->ts[i].mode = AM_TS_PARALLEL;
advb->ts[i].pinctrl =
devm_pinctrl_get_select(&pdev->dev,
buf);
} else {
advb->ts[i].mode = AM_TS_DISABLE;
advb->ts[i].pinctrl = NULL;
}
/* if(IS_ERR_VALUE(advb->ts[i].pinctrl)) */
/* advb->ts[i].pinctrl = NULL; */
}
memset(buf, 0, 32);
snprintf(buf, sizeof(buf), "ts%d_control", i);
ret =
of_property_read_u32(pdev->dev.of_node, buf,
&value);
if (!ret) {
pr_dbg("%s: 0x%x\n", buf, value);
advb->ts[i].control = value;
} else {
pr_dbg("read error:%s: 0x%x\n", buf, value);
}
if (advb->ts[i].s2p_id != -1) {
memset(buf, 0, 32);
snprintf(buf, sizeof(buf), "ts%d_invert", i);
ret =
of_property_read_u32(pdev->dev.of_node, buf,
&value);
if (!ret) {
pr_dbg("%s: 0x%x\n", buf, value);
advb->s2p[advb->ts[i].s2p_id].invert =
value;
}
}
}
memset(buf, 0, 32);
snprintf(buf, sizeof(buf), "ts_out_invert");
ret =
of_property_read_u32(pdev->dev.of_node, buf,
&value);
if (!ret) {
pr_dbg("%s: 0x%x\n", buf, value);
advb->ts_out_invert = value;
}
}
#endif
pd_dvb = (struct devio_aml_platform_data *)advb->dev->platform_data;
ret =
dvb_register_adapter(&advb->dvb_adapter, CARD_NAME, THIS_MODULE,
advb->dev, adapter_nr);
if (ret < 0)
return ret;
for (i = 0; i < DMX_DEV_COUNT; i++)
advb->dmx[i].id = -1;
for (i = 0; i<DSC_DEV_COUNT; i++)
advb->dsc[i].id = -1;
for (i = 0; i < advb->async_fifo_total_count; i++)
advb->asyncfifo[i].id = -1;
advb->dvb_adapter.priv = advb;
dev_set_drvdata(advb->dev, advb);
for (i = 0; i < DSC_DEV_COUNT; i++) {
ret = aml_dvb_dsc_init(advb, &advb->dsc[i], i);
if (ret < 0)
goto error;
}
for (i = 0; i < DMX_DEV_COUNT; i++) {
ret = aml_dvb_dmx_init(advb, &advb->dmx[i], i);
if (ret < 0)
goto error;
}
/*Init the async fifos */
for (i = 0; i < advb->async_fifo_total_count; i++) {
ret = aml_dvb_asyncfifo_init(advb, &advb->asyncfifo[i], i);
if (ret < 0)
goto error;
}
aml_regist_dmx_class();
if (class_register(&aml_stb_class) < 0) {
pr_error("dvb register class error\n");
goto error;
}
aml_register_parser_mconfig();
#ifdef ENABLE_DEMUX_DRIVER
tsdemux_set_ops(&aml_tsdemux_ops);
#else
tsdemux_set_ops(NULL);
#endif
#if (defined CONFIG_AMLOGIC_DVB_EXTERN)
ret = dvb_extern_register_frontend(&advb->dvb_adapter);
if (ret) {
pr_error("aml register dvb frontend failed\n");
goto error;
}
#endif
return 0;
error:
for (i = 0; i < advb->async_fifo_total_count; i++) {
if (advb->asyncfifo[i].id != -1)
aml_dvb_asyncfifo_release(advb, &advb->asyncfifo[i]);
}
for (i = 0; i < DMX_DEV_COUNT; i++) {
if (advb->dmx[i].id != -1)
aml_dvb_dmx_release(advb, &advb->dmx[i]);
}
for (i = 0; i < DSC_DEV_COUNT; i++) {
if (advb->dsc[i].id != -1)
aml_dvb_dsc_release(advb, &advb->dsc[i]);
}
dvb_unregister_adapter(&advb->dvb_adapter);
return ret;
}
static int aml_dvb_remove(struct platform_device *pdev)
{
struct aml_dvb *advb = (struct aml_dvb *)dev_get_drvdata(&pdev->dev);
int i;
pr_inf("[dmx_kpi] %s Enter.\n", __func__);
#if (defined CONFIG_AMLOGIC_DVB_EXTERN)
dvb_extern_unregister_frontend();
#endif
tsdemux_set_ops(NULL);
aml_unregist_dmx_class();
class_unregister(&aml_stb_class);
for (i = 0; i < advb->async_fifo_total_count; i++) {
if (advb->asyncfifo[i].id != -1)
aml_dvb_asyncfifo_release(advb, &advb->asyncfifo[i]);
}
for (i = 0; i < DMX_DEV_COUNT; i++) {
pr_error("remove demx %d, id is %d\n",i,advb->dmx[i].id);
if (advb->dmx[i].id != -1)
aml_dvb_dmx_release(advb, &advb->dmx[i]);
}
for (i = 0; i < DSC_DEV_COUNT; i++) {
if (advb->dsc[i].id != -1)
aml_dvb_dsc_release(advb, &advb->dsc[i]);
}
dvb_unregister_adapter(&advb->dvb_adapter);
for (i = 0; i < advb->ts_in_total_count; i++) {
if (advb->ts[i].pinctrl && !IS_ERR_VALUE(advb->ts[i].pinctrl))
devm_pinctrl_put(advb->ts[i].pinctrl);
}
/*switch_mod_gate_by_name("demux", 0); */
#if 0
reset_control_assert(aml_dvb_uparsertop_reset_ctl);
reset_control_assert(aml_dvb_ahbarb0_reset_ctl);
reset_control_assert(aml_dvb_afifo_reset_ctl);
reset_control_assert(aml_dvb_demux_reset_ctl);
#else
#if 1
if (get_cpu_type() < MESON_CPU_MAJOR_ID_G12A)
{
clk_disable_unprepare(aml_dvb_uparsertop_clk);
clk_disable_unprepare(aml_dvb_ahbarb0_clk);
clk_disable_unprepare(aml_dvb_afifo_clk);
clk_disable_unprepare(aml_dvb_demux_clk);
}
else
{
amports_switch_gate("demux", 0);
amports_switch_gate("ahbarb0", 0);
amports_switch_gate("parser_top", 0);
if (get_cpu_type() == MESON_CPU_MAJOR_ID_TL1) {
clk_disable_unprepare(aml_dvb_afifo_clk);
}
}
#endif
#endif
pr_inf("[dmx_kpi] %s Exit.\n", __func__);
return 0;
}
static int aml_dvb_suspend(struct platform_device *dev, pm_message_t state)
{
return 0;
}
static int aml_dvb_resume(struct platform_device *dev)
{
struct aml_dvb *dvb = &aml_dvb_device;
int i;
for (i = 0; i < DMX_DEV_COUNT; i++)
dmx_reset_dmx_id_hw_ex(dvb, i, 0);
pr_inf("dvb resume\n");
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id aml_dvb_dt_match[] = {
{
.compatible = "amlogic, dvb-demux",
},
{},
};
#endif /*CONFIG_OF */
static struct platform_driver aml_dvb_driver = {
.probe = aml_dvb_probe,
.remove = aml_dvb_remove,
.suspend = aml_dvb_suspend,
.resume = aml_dvb_resume,
.driver = {
.name = "amlogic-dvb-demux",
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = aml_dvb_dt_match,
#endif
}
};
static int __init aml_dvb_init(void)
{
return platform_driver_register(&aml_dvb_driver);
}
static void __exit aml_dvb_exit(void)
{
pr_inf("aml dvb exit\n");
platform_driver_unregister(&aml_dvb_driver);
}
/*Get the STB source demux*/
static struct aml_dmx *get_stb_dmx(void)
{
struct aml_dvb *dvb = &aml_dvb_device;
struct aml_dmx *dmx = NULL;
int i;
switch (dvb->stb_source) {
case AM_TS_SRC_DMX0:
dmx = &dvb->dmx[0];
break;
case AM_TS_SRC_DMX1:
dmx = &dvb->dmx[1];
break;
case AM_TS_SRC_DMX2:
dmx = &dvb->dmx[2];
break;
default:
for (i = 0; i < DMX_DEV_COUNT; i++) {
dmx = &dvb->dmx[i];
if (dmx->source == dvb->stb_source)
return dmx;
}
break;
}
return dmx;
}
static int aml_tsdemux_reset(void)
{
struct aml_dvb *dvb = &aml_dvb_device;
unsigned long flags;
pr_inf("[dmx_kpi] %s Enter\n", __func__);
spin_lock_irqsave(&dvb->slock, flags);
if (dvb->reset_flag) {
struct aml_dmx *dmx = get_stb_dmx();
dvb->reset_flag = 0;
if (dmx) {
if (dmx_reset_all_flag)
dmx_reset_hw_ex(dvb, 0);
else
dmx_reset_dmx_hw_ex_unlock(dvb, dmx, 0);
}
}
spin_unlock_irqrestore(&dvb->slock, flags);
pr_inf("[dmx_kpi] %s Exit\n", __func__);
return 0;
}
static int aml_tsdemux_set_reset_flag(void)
{
struct aml_dvb *dvb = &aml_dvb_device;
unsigned long flags;
spin_lock_irqsave(&dvb->slock, flags);
dvb->reset_flag = 1;
spin_unlock_irqrestore(&dvb->slock, flags);
return 0;
}
/*Add the amstream irq handler*/
static int aml_tsdemux_request_irq(irq_handler_t handler, void *data)
{
struct aml_dvb *dvb = &aml_dvb_device;
struct aml_dmx *dmx;
unsigned long flags;
spin_lock_irqsave(&dvb->slock, flags);
dmx = get_stb_dmx();
if (dmx) {
dmx->irq_handler = handler;
dmx->irq_data = data;
}
spin_unlock_irqrestore(&dvb->slock, flags);
return 0;
}
/*Free the amstream irq handler*/
static int aml_tsdemux_free_irq(void)
{
struct aml_dvb *dvb = &aml_dvb_device;
struct aml_dmx *dmx;
unsigned long flags;
spin_lock_irqsave(&dvb->slock, flags);
dmx = get_stb_dmx();
if (dmx) {
dmx->irq_handler = NULL;
dmx->irq_data = NULL;
}
spin_unlock_irqrestore(&dvb->slock, flags);
return 0;
}
/*Reset the video PID*/
static int aml_tsdemux_set_vid(int vpid)
{
struct aml_dvb *dvb = &aml_dvb_device;
struct aml_dmx *dmx;
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&dvb->slock, flags);
dmx = get_stb_dmx();
if (dmx) {
if (dmx->vid_chan != -1) {
dmx_free_chan(dmx, dmx->vid_chan);
dmx->vid_chan = -1;
}
if ((vpid >= 0) && (vpid < 0x1FFF)) {
dmx->vid_chan =
dmx_alloc_chan(dmx, DMX_TYPE_TS,
DMX_PES_VIDEO, vpid);
if (dmx->vid_chan == -1)
ret = -1;
}
}
spin_unlock_irqrestore(&dvb->slock, flags);
return ret;
}
/*Reset the audio PID*/
static int aml_tsdemux_set_aid(int apid)
{
struct aml_dvb *dvb = &aml_dvb_device;
struct aml_dmx *dmx;
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&dvb->slock, flags);
dmx = get_stb_dmx();
if (dmx) {
if (dmx->aud_chan != -1) {
dmx_free_chan(dmx, dmx->aud_chan);
dmx->aud_chan = -1;
}
if ((apid >= 0) && (apid < 0x1FFF)) {
dmx->aud_chan =
dmx_alloc_chan(dmx, DMX_TYPE_TS,
DMX_PES_AUDIO, apid);
if (dmx->aud_chan == -1)
ret = -1;
}
}
spin_unlock_irqrestore(&dvb->slock, flags);
return ret;
}
/*Reset the subtitle PID*/
static int aml_tsdemux_set_sid(int spid)
{
struct aml_dvb *dvb = &aml_dvb_device;
struct aml_dmx *dmx;
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&dvb->slock, flags);
dmx = get_stb_dmx();
if (dmx) {
if (dmx->sub_chan != -1) {
dmx_free_chan(dmx, dmx->sub_chan);
dmx->sub_chan = -1;
}
if ((spid >= 0) && (spid < 0x1FFF)) {
dmx->sub_chan = 3;
dmx->sub_chan =
dmx_alloc_chan(dmx, DMX_TYPE_TS,
DMX_PES_SUBTITLE, spid);
if (dmx->sub_chan == -1)
ret = -1;
}
}
spin_unlock_irqrestore(&dvb->slock, flags);
return ret;
}
static int aml_tsdemux_set_pcrid(int pcrpid)
{
struct aml_dvb *dvb = &aml_dvb_device;
struct aml_dmx *dmx;
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&dvb->slock, flags);
dmx = get_stb_dmx();
if (dmx) {
if (dmx->pcr_chan != -1) {
dmx_free_chan(dmx, dmx->pcr_chan);
dmx->pcr_chan = -1;
}
if ((pcrpid >= 0) && (pcrpid < 0x1FFF)) {
dmx->pcr_chan =
dmx_alloc_chan(dmx, DMX_TYPE_TS,
DMX_PES_PCR, pcrpid);
if (dmx->pcr_chan == -1)
ret = -1;
}
}
spin_unlock_irqrestore(&dvb->slock, flags);
return ret;
}
static int aml_tsdemux_set_skipbyte(int skipbyte)
{
struct aml_dvb *dvb = &aml_dvb_device;
unsigned long flags;
spin_lock_irqsave(&dvb->slock, flags);
aml_dmx_set_skipbyte(dvb, skipbyte);
spin_unlock_irqrestore(&dvb->slock, flags);
return 0;
}
static int aml_tsdemux_set_demux(int id)
{
struct aml_dvb *dvb = &aml_dvb_device;
aml_dmx_set_demux(dvb, id);
return 0;
}
static unsigned long aml_tsdemux_hwdmx_spin_lock(unsigned long flags)
{
struct aml_dvb *dvb = &aml_dvb_device;
spin_lock_irqsave(&dvb->slock, flags);
return flags;
}
static int aml_tsdemux_hwdmx_spin_unlock(unsigned long flags)
{
struct aml_dvb *dvb = &aml_dvb_device;
spin_unlock_irqrestore(&dvb->slock, flags);
return 0;
}
module_init(aml_dvb_init);
module_exit(aml_dvb_exit);
MODULE_DESCRIPTION("driver for the AMLogic DVB card");
MODULE_AUTHOR("AMLOGIC");
MODULE_LICENSE("GPL");