Please report boards and AW SoC types with TS0 AND TS1 inputs available and in which GPIO multiplex mode.
I'm modifying script for CSI -> TS now and test init of the driver built in kernel.
Signed-off-by: Thomas Schorpp <
thomas....@gmail.com>
---
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig
index f6e40b3..19de663 100644
--- a/drivers/media/dvb/Kconfig
+++ b/drivers/media/dvb/Kconfig
@@ -84,6 +84,10 @@ comment "Supported ddbridge ('Octopus') Adapters"
depends on DVB_CORE && PCI && I2C
source "drivers/media/dvb/ddbridge/Kconfig"
+comment "Supported SoC TS Capture Controllers"
+ depends on DVB_CORE
+ source "drivers/media/dvb/soc/Kconfig"
+
comment "Supported DVB Frontends"
depends on DVB_CORE
source "drivers/media/dvb/frontends/Kconfig"
diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile
index b2cefe6..053abd3 100644
--- a/drivers/media/dvb/Makefile
+++ b/drivers/media/dvb/Makefile
@@ -16,6 +16,7 @@ obj-y := dvb-core/ \
pt1/ \
mantis/ \
ngene/ \
- ddbridge/
+ ddbridge/ \
+ soc/
obj-$(CONFIG_DVB_FIREDTV) += firewire/
diff --git a/drivers/media/dvb/soc/Kconfig b/drivers/media/dvb/soc/Kconfig
new file mode 100644
index 0000000..d795da3
--- /dev/null
+++ b/drivers/media/dvb/soc/Kconfig
@@ -0,0 +1,60 @@
+# TODO:
+# 1. Selective TS- Ports/Controller(s?) config, auto-disable CSI drivers in Kconfig.
+# 2. Auto- Select tuner pll/sec/lnb drivers,
+# but there may be modules around with other/unsupported PLL- chips,
+# so let user choose them.
+# 3. Support for DVB-S2/T2/C2/ATSC(?) receivers (Me not before SDTV gets off the air.)
+# 4. CI+ CA Support (In Your dreams).
+
+config DVB_TSC_SUNXI
+ bool "TS Controller Driver for Allwinner A10/A20 TSC"
+ default n
+ depends on DVB_CORE && PLAT_SUNXI
+
+if DVB_TSC_SUNXI
+
+config TS0_SUN4I
+ tristate "TS0 TS capture & demux input for sun4i/5i/7i, A10/A13/A20"
+ default n
+# depends on ARCH_SUN4I || ARCH_SUN5I || ARCH_SUN7I
+ select VIDEOBUF_DMA_CONTIG
+
+config TS1_SUN7I
+ tristate "TS1 TS capture & demux input for sun7i/sunxi, A20"
+ default n
+# depends on ARCH_SUN7I
+ select VIDEOBUF_DMA_CONTIG
+
+menu "Supported attached DVB/ATSC Receiver modules"
+
+config TSC_CU1216
+ tristate "Philips CU1216 DVB-C Receiver"
+ depends on I2C && DVB_FE_CUSTOMISE
+ select DVB_TDA10021
+ ---help---
+ I2C/TS Philips CU1216 and Clones TDA10021 based DVB-C Receiver Module
+
+config TSC_CU1216_MKIII
+ tristate "Philips CU1216-MKIII DVB-C Receiver"
+ depends on I2C && DVB_FE_CUSTOMISE
+ select DVB_TDA10023
+ ---help---
+ I2C/TS Philips CU1216-MKIII and Clones TDA10023 based DVB-C Receiver Module
+
+config TSC_SHARP_BS2F7_Z0_95
+ tristate "Sharp BS2F7(V/H)Z0(2/3)95 DVB-S Receiver"
+ depends on I2C && DVB_FE_CUSTOMISE
+ select DVB_STV0288
+ ---help---
+ I2C/TS Sharp BS2F7(V/H)Z0(2/3)95 STV0288 DVB-S Receiver Module
+
+config TSC_BSBE1
+ tristate "BSBE1 DVB-C -Receiver"
+ depends on I2C && DVB_FE_CUSTOMISE
+ select DVB_STV0299
+ ---help---
+ I2C/TS "BSBE1-502A" DVB-S Receiver Extensions Module (Found on TT FF/HPE Nexus dvb-s pci)
+
+endmenu
+endif
+
diff --git a/drivers/media/dvb/soc/Makefile b/drivers/media/dvb/soc/Makefile
new file mode 100644
index 0000000..1cbaedc
--- /dev/null
+++ b/drivers/media/dvb/soc/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_TS0_SUN4I) += tscdrv.o
+#obj-$(CONFIG_TS1_SUN7I) += tscdrv.o
+
diff --git a/drivers/media/dvb/soc/dvb_drv_sun7i.h b/drivers/media/dvb/soc/dvb_drv_sun7i.h
new file mode 100644
index 0000000..d756767
--- /dev/null
+++ b/drivers/media/dvb/soc/dvb_drv_sun7i.h
@@ -0,0 +1,106 @@
+#ifndef DRIVER_INTERFACE_H
+#define DRIVER_INTERFACE_H
+
+#include<linux/ioctl.h>
+
+
+//define iocltl command
+#define TSCDEV_IOC_MAGIC 't'
+
+#define TSCDEV_ENABLE_INT _IO(TSCDEV_IOC_MAGIC, 0)
+#define TSCDEV_DISABLE_INT _IO(TSCDEV_IOC_MAGIC, 1)
+#define TSCDEV_GET_PHYSICS _IO(TSCDEV_IOC_MAGIC, 2)
+#define TSCDEV_RELEASE_SEM _IO(TSCDEV_IOC_MAGIC, 3)
+#define TSCDEV_WAIT_INT _IOR(TSCDEV_IOC_MAGIC, 4, unsigned long)
+#define TSCDEV_ENABLE_CLK _IOW(TSCDEV_IOC_MAGIC, 5, unsigned long)
+#define TSCDEV_DISABLE_CLK _IOW(TSCDEV_IOC_MAGIC, 6, unsigned long)
+#define TSCDEV_PUT_CLK _IOW(TSCDEV_IOC_MAGIC, 7, unsigned long)
+#define TSCDEV_SET_CLK_FREQ _IOW(TSCDEV_IOC_MAGIC, 8, unsigned long)
+#define TSCDEV_GET_CLK _IOWR(TSCDEV_IOC_MAGIC, 9, unsigned long)
+#define TSCDEV_GET_CLK_FREQ _IOWR(TSCDEV_IOC_MAGIC, 10, unsigned long)
+#define TSCDEV_SET_SRC_CLK_FREQ _IOWR(TSCDEV_IOC_MAGIC, 11, unsigned long)
+#define TSCDEV_FOR_TEST _IO(TSCDEV_IOC_MAGIC, 12)
+
+
+#define TSCDEV_IOC_MAXNR (12)
+
+
+struct intrstatus {
+ unsigned int port0chan;
+ unsigned int port0pcr;
+ unsigned int port1chan;
+ unsigned int port1pcr;
+};
+
+/*
+ * define struct for clock parameters
+ */
+#define CLK_NAME_LEN (32)
+struct clk_para {
+ char clk_name[CLK_NAME_LEN];
+ unsigned int handle;
+ int clk_rate;
+};
+
+/*
+ * define values of channel information
+ */
+#define TSF_INTR_THRESHOLD (1) // defines when to interrupt
+//0:1/2 buffer size 1:1/4 buffer size
+//2:1/8 buffer size 3:1/16 buffer size
+#define VIDEO_NOTIFY_PKT_NUM (1024)
+#define AUDIO_NOTIFY_PKT_NUM (64)
+#define SUBTITLE_NOTIFY_PKT_NUM (64)
+#define SECTION_NOTIFY_PKT_NUM (64)
+#define TS_DATA_NOTIFY_PKT_NUM (64)
+
+/*
+ * define channel max numbers and base offset
+ */
+#define VIDEO_CHAN_BASE (0)
+#define MAX_VIDEO_CHAN (1)
+
+#define AUDIO_CHAN_BASE (VIDEO_CHAN_BASE + MAX_VIDEO_CHAN)
+#define MAX_AUDIO_CHAN (1)
+
+#define SUBTITLE_CHAN_BASE (AUDIO_CHAN_BASE + MAX_AUDIO_CHAN)
+#define MAX_SUBTITLE_CHAN (1)
+
+#define SECTION_CHAN_BASE (SUBTITLE_CHAN_BASE + MAX_SUBTITLE_CHAN)
+#define MAX_SECTION_CHAN (12)
+
+#define TS_DATA_CHAN_BASE (SECTION_CHAN_BASE + MAX_SECTION_CHAN)
+#define MAX_TS_DATA_CHAN (15)
+
+/*
+ * define address of Registers
+ */
+#define REGS_pBASE (0x01C04000)
+
+// registers offset
+#define TSC_REGS_OFFSET (0x00 )
+#define TSG_REGS_OFFSET (0x40 )
+#define TSF0_REGS_OFFSET (0x80 )
+#define TSF1_REGS_OFFSET (0x100 )
+
+#define TSF0_PCR_STATUS_OFFSET (0x88 )
+#define TSF0_CHAN_STATUS_OFFSET (0x98 )
+#define TSF1_PCR_STATUS_OFFSET (0x108 )
+#define TSF1_CHAN_STATUS_OFFSET (0x118 )
+
+//start address
+#define TSC_REGS_pBASE (REGS_pBASE + 0x00 )
+#define TSG_REGS_pBASE (REGS_pBASE + 0x40 )
+#define TSF0_REGS_pBASE (REGS_pBASE + 0x80 )
+#define TSF1_REGS_pBASE (REGS_pBASE + 0x100)
+
+#define REGS_BASE REGS_pBASE
+#define TSC_REGS_BASE TSC_REGS_pBASE
+#define TSG_REGS_BASE TSG_REGS_pBASE
+#define TSF0_REGS_BASE TSF0_REGS_pBASE
+#define TSF1_REGS_BASE TSF1_REGS_pBASE
+
+//register size
+#define REGS_SIZE (0x1000)
+
+#endif
diff --git a/drivers/media/dvb/soc/tscdrv.c b/drivers/media/dvb/soc/tscdrv.c
new file mode 100644
index 0000000..9c40d70
--- /dev/null
+++ b/drivers/media/dvb/soc/tscdrv.c
@@ -0,0 +1,764 @@
+/*
+ * drivers/media/video/tsc/tscdrv.c
+ * (C) Copyright 2010-2015
+ * Allwinner Technology Co., Ltd. <
www.allwinnertech.com>
+ * csjamesdeng <
csjam...@allwinnertech.com>
+ * (C) 2013
thomas....@gmail.com
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/semaphore.h>
+#include <linux/fs.h>
+#include <linux/dma-mapping.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/cdev.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/preempt.h>
+#include <linux/rmap.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/ioctl.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/jiffies.h>
+#include <linux/gpio.h>
+
+#include <asm-generic/gpio.h>
+#include <asm/uaccess.h>
+#include <asm/memory.h>
+#include <asm/unistd.h>
+#include <asm-generic/int-ll64.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/page.h>
+
+#include <plat/sys_config.h>
+#include <mach/gpio.h>
+#include <mach/clock.h>
+#include <mach/system.h>
+#include <mach/hardware.h>
+
+#include "tscdrv.h"
+
+
+static struct tsc_dev *tsc_devp = NULL;
+
+static DECLARE_WAIT_QUEUE_HEAD(wait_proc);
+
+/*
+ * unmap/release iomem
+ */
+static void tsiomem_unregister(struct tsc_dev *devp)
+{
+ /* Release io mem */
+ if (devp->regs) {
+ release_resource(devp->regs);
+ devp->regs = NULL;
+ }
+ iounmap(devp->regsaddr);
+ devp->regsaddr = NULL;
+}
+
+/*
+ * ioremap and request iomem
+ */
+static int register_tsiomem(struct tsc_dev *devp)
+{
+ char *addr;
+ int ret;
+ struct resource *res;
+
+ ret = check_mem_region(REGS_BASE, REGS_SIZE);
+ TSC_DBG("%s: check_mem_region return: %d\n", __func__, ret);
+ res = request_mem_region(REGS_BASE, REGS_SIZE, "ts regs");
+ if (res == NULL) {
+ TSC_ERR("%s: cannot reserve region for register\n", __func__);
+ goto err;
+ }
+ devp->regs = res;
+
+ addr = ioremap(REGS_BASE, REGS_SIZE);
+ if (!addr) {
+ TSC_ERR("%s: cannot map region for register\n", __func__);
+ goto err;
+ }
+
+ devp->regsaddr = addr;
+ TSC_DBG("%s: devp->regsaddr: 0x%08x\n", __func__, (unsigned int)devp->regsaddr);
+
+ return 0;
+
+err:
+ if (devp->regs) {
+ release_resource(devp->regs);
+ devp->regs = NULL;
+ }
+ return -1;
+}
+
+/*
+ * interrupt service routine
+ * To wake up wait queue
+ */
+static irqreturn_t tscdriverinterrupt(int irq, void *dev_id)
+{
+ tsc_devp->intstatus.port0pcr = ioread32((void *)(tsc_devp->regsaddr + 0x88));
+ tsc_devp->intstatus.port0chan = ioread32((void *)(tsc_devp->regsaddr + 0x98));
+ tsc_devp->intstatus.port1pcr = ioread32((void *)(tsc_devp->regsaddr + 0x108));
+ tsc_devp->intstatus.port1chan = ioread32((void *)(tsc_devp->regsaddr + 0x118));
+
+ iowrite32(tsc_devp->intstatus.port0pcr, (void *)(tsc_devp->regsaddr + 0x88));
+ iowrite32(tsc_devp->intstatus.port0chan, (void *)(tsc_devp->regsaddr + 0x98));
+ iowrite32(tsc_devp->intstatus.port1pcr, (void *)(tsc_devp->regsaddr + 0x108));
+ iowrite32(tsc_devp->intstatus.port1chan, (void *)(tsc_devp->regsaddr + 0x118));
+ tsc_devp->irq_flag = 1;
+
+ wake_up_interruptible(&wait_proc);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * poll operateion for wait for TS irq
+ */
+unsigned int tscdev_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ int mask = 0;
+ struct tsc_dev *devp = filp->private_data;
+
+ poll_wait(filp, &devp->wq, wait);
+
+ if (devp->irq_flag == 1) {
+ mask |= POLLIN | POLLRDNORM;
+ }
+
+ return mask;
+}
+
+/*
+ * ioctl function
+ */
+long tscdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ long ret;
+ spinlock_t *lock;
+ struct tsc_dev *devp;
+ struct clk_para temp_clk;
+ struct clk *clk_hdle;
+ struct intrstatus statusdata;
+
+ ret = 0;
+ devp = filp->private_data;
+ lock = &devp->lock;
+
+ if (_IOC_TYPE(cmd) != TSCDEV_IOC_MAGIC)
+ return -EINVAL;
+ if (_IOC_NR(cmd) > TSCDEV_IOC_MAXNR)
+ return -EINVAL;
+
+ switch (cmd) {
+ case TSCDEV_WAIT_INT:
+ ret = wait_event_interruptible_timeout(wait_proc, devp->irq_flag, HZ * 1);
+ if (!ret && !devp->irq_flag) {
+ //case: wait timeout.
+ TSC_ERR("%s: wait timeout\n", __func__);
+ memset(&statusdata, 0, sizeof(statusdata));
+ } else {
+ //case: interrupt occured.
+ devp->irq_flag = 0;
+ statusdata.port0chan = devp->intstatus.port0chan;
+ statusdata.port0pcr = devp->intstatus.port0pcr;
+ statusdata.port1chan = devp->intstatus.port1chan;
+ statusdata.port1pcr = devp->intstatus.port1pcr;
+ }
+
+ //copy status data to user
+ if (copy_to_user((struct intrstatus *)arg, &(devp->intstatus),
+ sizeof(struct intrstatus))) {
+ return -EFAULT;
+ }
+
+ break;
+
+ case TSCDEV_GET_PHYSICS:
+ return virt_to_phys((void *)devp->pmem);
+
+ case TSCDEV_ENABLE_INT:
+ enable_irq(devp->irq);
+ break;
+
+ case TSCDEV_DISABLE_INT:
+ tsc_devp->irq_flag = 1;
+ wake_up_interruptible(&wait_proc);
+ disable_irq(devp->irq);
+ break;
+
+ case TSCDEV_RELEASE_SEM:
+ tsc_devp->irq_flag = 1;
+ wake_up_interruptible(&wait_proc);
+ break;
+
+ case TSCDEV_GET_CLK:
+ if (copy_from_user(&temp_clk, (struct clk_para __user *)arg,
+ sizeof(struct clk_para))) {
+ TSC_ERR("%s: get clk error\n", __func__);
+ return -EFAULT;
+ }
+ clk_hdle = clk_get(devp->dev, temp_clk.clk_name);
+
+ if (copy_to_user((char *) & ((struct clk_para *)arg)->handle,
+ &clk_hdle, 4)) {
+ TSC_ERR("%s: get clk error\n", __func__);
+ return -EFAULT;
+ }
+ break;
+
+ case TSCDEV_PUT_CLK:
+ if (copy_from_user(&temp_clk, (struct clk_para __user *)arg,
+ sizeof(struct clk_para))) {
+ TSC_ERR("%s: put clk error\n", __func__);
+ return -EFAULT;
+ }
+ clk_put((struct clk *)temp_clk.handle);
+
+ break;
+
+ case TSCDEV_ENABLE_CLK:
+ if (copy_from_user(&temp_clk, (struct clk_para __user *)arg,
+ sizeof(struct clk_para))) {
+ TSC_ERR("%s: enable clk error\n", __func__);
+ return -EFAULT;
+ }
+
+ clk_enable((struct clk *)temp_clk.handle);
+
+ break;
+
+ case TSCDEV_DISABLE_CLK:
+ if (copy_from_user(&temp_clk, (struct clk_para __user *)arg,
+ sizeof(struct clk_para))) {
+ TSC_ERR("%s: disable clk error\n", __func__);
+ return -EFAULT;
+ }
+
+ clk_disable((struct clk *)temp_clk.handle);
+
+ break;
+
+ case TSCDEV_GET_CLK_FREQ:
+ if (copy_from_user(&temp_clk, (struct clk_para __user *)arg,
+ sizeof(struct clk_para))) {
+ TSC_ERR("%s: get clk freq error\n", __func__);
+ return -EFAULT;
+ }
+ temp_clk.clk_rate = clk_get_rate((struct clk *)temp_clk.handle);
+
+ if (copy_to_user((char *) & ((struct clk_para *)arg)->clk_rate,
+ &temp_clk.clk_rate, 4)) {
+ TSC_ERR("%s: get clk freq error\n", __func__);
+ return -EFAULT;
+ }
+ break;
+
+ case TSCDEV_SET_SRC_CLK_FREQ:
+ break;
+
+ case TSCDEV_SET_CLK_FREQ:
+ if (copy_from_user(&temp_clk, (struct clk_para __user *)arg,
+ sizeof(struct clk_para))) {
+ TSC_ERR("%s: set clk error\n", __func__);
+ return -EFAULT;
+ }
+
+ clk_set_rate((struct clk *)temp_clk.handle, temp_clk.clk_rate);
+
+ break;
+
+ default:
+ TSC_ERR("%s: invalid cmd\n", __func__);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int tscdev_open(struct inode *inode, struct file *filp)
+{
+ int ret;
+ struct tsc_dev *devp;
+ struct clk_para tmp_clk;
+
+ devp = container_of(inode->i_cdev, struct tsc_dev, cdev);
+ filp->private_data = devp;
+
+ if (down_interruptible(&devp->sem)) {
+ return -ERESTARTSYS;
+ }
+
+ //open ahb clock
+ strcpy(tmp_clk.clk_name, "ahb_ts");
+ tsc_devp->ahb_clk = clk_get(tsc_devp->dev, tmp_clk.clk_name);
+ if (!tsc_devp->ahb_clk || IS_ERR(tsc_devp->ahb_clk)) {
+ TSC_ERR("%s: get ahb_ts clk failed\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (clk_enable(tsc_devp->ahb_clk) < 0) {
+ TSC_ERR("%s: enable ahb_ts clk error\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ TSC_INF("%s: ahb_ts clk rate: 0x%lx\n", __func__, clk_get_rate(tsc_devp->ahb_clk));
+
+ //open sdram clock
+ strcpy(tmp_clk.clk_name, "sdram_ts");
+ tsc_devp->sdram_clk = clk_get(tsc_devp->dev, tmp_clk.clk_name);
+ if (!tsc_devp->sdram_clk || IS_ERR(tsc_devp->sdram_clk)) {
+ TSC_ERR("%s: get sdram ts clk failed\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (clk_enable(tsc_devp->sdram_clk) < 0) {
+ TSC_ERR("%s: enable sdram ts clk error\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ //open ts clock
+ strcpy(tmp_clk.clk_name, "ts");
+ tsc_devp->tsc_clk = clk_get(tsc_devp->dev, tmp_clk.clk_name);
+ if (!tsc_devp->tsc_clk || IS_ERR(tsc_devp->tsc_clk)) {
+ TSC_ERR("%s: get tsc clk failed\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (clk_enable(tsc_devp->tsc_clk) < 0) {
+ TSC_ERR("%s: enable tsc clk error\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ strcpy(tmp_clk.clk_name, "sdram_pll_p");
+ tsc_devp->pll5_clk = clk_get(tsc_devp->dev, tmp_clk.clk_name);
+ if (!tsc_devp->pll5_clk || IS_ERR(tsc_devp->pll5_clk)) {
+ TSC_ERR("%s: get pll5 clk failed\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ clk_set_parent(tsc_devp->tsc_clk, tsc_devp->pll5_clk);
+
+ //set ts clock rate
+ tmp_clk.clk_rate = clk_get_rate(tsc_devp->pll5_clk);
+ TSC_INF("%s: parent clock rate %d\n", __func__, tmp_clk.clk_rate);
+ tmp_clk.clk_rate /= 2;
+ TSC_INF("%s: clock rate %d\n", __func__, tmp_clk.clk_rate);
+ if (clk_set_rate(tsc_devp->tsc_clk, tmp_clk.clk_rate) < 0) {
+ TSC_INF("%s: set clk rate error\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ tmp_clk.clk_rate = clk_get_rate(tsc_devp->tsc_clk);
+ TSC_INF("%s: clock rate %d", __func__, tmp_clk.clk_rate);
+
+ /* init other resource here */
+ devp->irq_flag = 0;
+
+ up(&devp->sem);
+
+ nonseekable_open(inode, filp);
+
+ return 0;
+
+err:
+ if (devp->tsc_clk) {
+ clk_disable(tsc_devp->tsc_clk);
+ devp->tsc_clk = NULL;
+ }
+ if (devp->ahb_clk) {
+ clk_disable(tsc_devp->ahb_clk);
+ devp->ahb_clk = NULL;
+ }
+ if (devp->sdram_clk) {
+ clk_disable(tsc_devp->sdram_clk);
+ devp->sdram_clk = NULL;
+ }
+
+ return ret;
+}
+
+static int tscdev_release(struct inode *inode, struct file *filp)
+{
+ struct tsc_dev *devp;
+
+ devp = filp->private_data;
+
+ if (down_interruptible(&devp->sem)) {
+ return -ERESTARTSYS;
+ }
+
+ if (devp->tsc_clk) {
+ clk_disable(tsc_devp->tsc_clk);
+ devp->tsc_clk = NULL;
+ }
+ if (devp->ahb_clk) {
+ clk_disable(tsc_devp->ahb_clk);
+ devp->ahb_clk = NULL;
+ }
+ if (devp->sdram_clk) {
+ clk_disable(tsc_devp->sdram_clk);
+ devp->sdram_clk = NULL;
+ }
+
+ /* release other resource here */
+ devp->irq_flag = 1;
+
+ up(&devp->sem);
+
+ return 0;
+}
+
+void tscdev_vma_open(struct vm_area_struct *vma)
+{
+ TSC_DBG("%s\n", __func__);
+}
+
+void tscdev_vma_close(struct vm_area_struct *vma)
+{
+ TSC_DBG("%s\n", __func__);
+}
+
+static struct vm_operations_struct tscdev_remap_vm_ops = {
+ .open = tscdev_vma_open,
+ .close = tscdev_vma_close,
+};
+
+static int tscdev_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ unsigned int mypfn;
+ unsigned int psize;
+ unsigned int offset;
+ unsigned int physics;
+ unsigned int vmsize;
+ unsigned int regsaddr ;
+ struct tsc_dev *devp = filp->private_data;
+
+ regsaddr = (unsigned int)devp->regsaddr ;
+ offset = vma->vm_pgoff << PAGE_SHIFT;
+ vmsize = vma->vm_end - vma->vm_start;
+ psize = devp->gsize - offset;
+ if (vmsize <= PAGE_SIZE) { // if mmap registers, vmsize must be less than PAGE_SIZE
+ physics = __pa(regsaddr);
+ mypfn = REGS_BASE >> 12;
+
+ vma->vm_flags |= VM_RESERVED | VM_IO;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ if (remap_pfn_range(vma, vma->vm_start, mypfn, vmsize, vma->vm_page_prot)) {
+ return -EAGAIN;
+ }
+ } else {
+ physics = __pa(devp->pmem);
+ mypfn = physics >> PAGE_SHIFT;
+ if (vmsize > psize)
+ return -ENXIO;
+
+ vma->vm_flags |= VM_RESERVED ;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ if (remap_pfn_range(vma, vma->vm_start, mypfn, vmsize, vma->vm_page_prot)) {
+ return -EAGAIN;
+ }
+ }
+
+ vma->vm_ops = &tscdev_remap_vm_ops;
+ tscdev_vma_open(vma);
+
+ return 0;
+}
+
+static unsigned p_gpio_handler = 0;
+
+static unsigned int request_tsc_pio(void)
+{
+ int cnt;
+ user_gpio_set_t gpio_list;
+
+ cnt = script_parser_mainkey_get_gpio_count("tsc_para");
+ if (!script_parser_mainkey_get_gpio_cfg("tsc_para", &gpio_list, cnt)) {
+ TSC_ERR("%s: get tsc pio para failed\n", __func__);
+ return -1;
+ }
+
+ if (!(p_gpio_handler = sunxi_gpio_request_array(&gpio_list, cnt))) {
+ TSC_INF("%s: request tsc pio failed, maybe error\n", __func__);
+ goto err;
+ }
+ return 0;
+
+err:
+ gpio_release(p_gpio_handler, 1);
+ return -1;
+}
+
+static void release_tsc_pio(void)
+{
+ gpio_release(p_gpio_handler, 1);
+}
+
+static struct file_operations tscdev_fops = {
+ .owner = THIS_MODULE,
+ .mmap = tscdev_mmap,
+ .poll = tscdev_poll,
+ .open = tscdev_open,
+ .release = tscdev_release,
+ .llseek = no_llseek,
+ .unlocked_ioctl = tscdev_ioctl,
+};
+
+static int size_multiply_arr[4] = {2, 4, 8, 16};
+static int tsc_used = 0;
+
+static int __init tscdev_init(void)
+{
+ int ret;
+ int devno;
+ unsigned int videobufsize;
+ unsigned int audiobufsize;
+ unsigned int subtitlebufsize;
+ unsigned int sectionbufsize;
+ unsigned int tsdatabufsize;
+ unsigned int pcrbufsize;
+ unsigned int chan32bufsize;
+ unsigned int sizemultiply;
+ char *viraddr;
+ char *pbuf;
+ int gsize;
+ dev_t dev = 0;
+ int *val = NULL;
+
+ if(!(script_parser_fetch_ex("tsc_para", "tsc_used", val,
+ (script_parser_value_type_t*)SCRIPT_PARSER_VALUE_TYPE_SINGLE_WORD, 1))) {
+ TSC_ERR("%s: get tsc_used failed\n", __func__);
+ return 0;
+ }
+ if (*val != 1) {
+ TSC_INF("%s: tsc driver is disabled\n", __func__);
+ return 0;
+ }
+ tsc_used = *val;
+
+ tsc_devp = kmalloc(sizeof(struct tsc_dev), GFP_KERNEL);
+ if (tsc_devp == NULL) {
+ TSC_ERR("%s: malloc memory for tsc device error\n", __func__);
+ return -ENOMEM;
+ }
+ memset(tsc_devp, 0, sizeof(struct tsc_dev));
+
+ tsc_devp->ts_dev_major = TSCDEV_MAJOR;
+ tsc_devp->ts_dev_minor = TSCDEV_MINOR;
+
+ /* register char device */
+ dev = MKDEV(tsc_devp->ts_dev_major, tsc_devp->ts_dev_minor);
+ if (tsc_devp->ts_dev_major) {
+ ret = register_chrdev_region(dev, 1, "tsc_dev");
+ } else {
+ ret = alloc_chrdev_region(&dev, tsc_devp->ts_dev_minor, 1, "tsc_dev");
+ tsc_devp->ts_dev_major = MAJOR(dev);
+ tsc_devp->ts_dev_minor = MINOR(dev);
+ }
+ if (ret < 0) {
+ TSC_ERR("%s: can't get major %d", __func__, tsc_devp->ts_dev_major);
+ goto err1;
+ }
+
+ /* calculate total buffer size of filters */
+ sizemultiply = size_multiply_arr[TSF_INTR_THRESHOLD];
+ videobufsize = (VIDEO_NOTIFY_PKT_NUM * 188 * sizemultiply + 0x3ff)& ~0x3ff;
+ audiobufsize = (AUDIO_NOTIFY_PKT_NUM * 188 * sizemultiply + 0x3ff)& ~0x3ff;
+ subtitlebufsize = (SUBTITLE_NOTIFY_PKT_NUM * 188 * sizemultiply + 0x3ff)& ~0x3ff;
+ sectionbufsize = (SECTION_NOTIFY_PKT_NUM * 188 * sizemultiply + 0x3ff)& ~0x3ff;
+ tsdatabufsize = (TS_DATA_NOTIFY_PKT_NUM * 188 * sizemultiply + 0x3ff)& ~0x3ff;
+ pcrbufsize = (1 * 188 * sizemultiply + 0x3ff) & ~0x3ff;;
+ chan32bufsize = (1 * 188 * sizemultiply + 0x3ff) & ~0x3ff;;
+
+ gsize = videobufsize * MAX_VIDEO_CHAN +
+ audiobufsize * MAX_AUDIO_CHAN +
+ subtitlebufsize * MAX_SUBTITLE_CHAN +
+ sectionbufsize * MAX_SECTION_CHAN +
+ tsdatabufsize * MAX_TS_DATA_CHAN +
+ pcrbufsize +
+ chan32bufsize;
+
+ /* alloc contiguous buffer */
+ gsize = (gsize + 0xfff) & ~0xfff;
+ TSC_INF("%s: kmalloc memory, size: 0x%x\n", __func__, gsize);
+ pbuf = (char *)kmalloc(gsize, GFP_DMA | GFP_KERNEL);
+ if (!pbuf) {
+ TSC_ERR("%s: allocate memory failed\n", __func__);
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ /* set pages reversed */
+ for (viraddr = pbuf; viraddr < pbuf + gsize; viraddr += PAGE_SIZE) {
+ SetPageReserved(virt_to_page(viraddr));
+ }
+
+ tsc_devp->pmem = pbuf;
+ tsc_devp->gsize = gsize;
+
+ sema_init(&tsc_devp->sem, 1);
+ init_waitqueue_head(&tsc_devp->wq);
+
+ /* request TS pio */
+ if (request_tsc_pio()) {
+ TSC_ERR("%s: request tsc pio failed\n", __func__);
+ ret = -EINVAL;
+ goto err2;
+ }
+
+ /* request TS irq */
+ ret = request_irq(TS_IRQ_NO, tscdriverinterrupt, 0, "tsc_dev", NULL);
+ if (ret < 0) {
+ TSC_ERR("%s: request irq error\n", __func__);
+ ret = -EINVAL;
+ goto err3;
+ }
+ tsc_devp->irq = TS_IRQ_NO;
+
+ /* create char device */
+ devno = MKDEV(tsc_devp->ts_dev_major, tsc_devp->ts_dev_minor);
+ cdev_init(&tsc_devp->cdev, &tscdev_fops);
+ tsc_devp->cdev.owner = THIS_MODULE;
+ tsc_devp->cdev.ops = &tscdev_fops;
+ ret = cdev_add(&tsc_devp->cdev, devno, 1);
+ if (ret) {
+ TSC_ERR("%s: add tsc char device error\n", __func__);
+ ret = -EINVAL;
+ goto err4;
+ }
+
+ tsc_devp->class = class_create(THIS_MODULE, "tsc_dev");
+ if (IS_ERR(tsc_devp->class)) {
+ TSC_ERR("%s: create tsc_dev class failed\n", __func__);
+ ret = -EINVAL;
+ goto err5;
+ }
+
+ tsc_devp->dev = device_create(tsc_devp->class, NULL, devno, NULL, "tsc_dev");
+ if (IS_ERR(tsc_devp->dev)) {
+ TSC_ERR("%s: create tsc_dev device failed\n", __func__);
+ ret = -EINVAL;
+ goto err6;
+ }
+
+ if (register_tsiomem(tsc_devp)) {
+ TSC_ERR("%s: register ts io memory error\n", __func__);
+ ret = -EINVAL;
+ goto err7;
+ }
+
+ return 0;
+
+err7:
+ device_destroy(tsc_devp->class, dev);
+err6:
+ class_destroy(tsc_devp->class);
+err5:
+ cdev_del(&tsc_devp->cdev);
+err4:
+ free_irq(TS_IRQ_NO, NULL);
+err3:
+ release_tsc_pio();
+err2:
+ if (tsc_devp->pmem) {
+ /* release reserved pages */
+ for (viraddr = pbuf; viraddr < pbuf + gsize; viraddr += PAGE_SIZE) {
+ ClearPageReserved(virt_to_page(viraddr));
+ }
+ kfree(pbuf);
+ pbuf = NULL;
+ }
+err1:
+ unregister_chrdev_region(dev, 1);
+ if (tsc_devp) {
+ kfree(tsc_devp);
+ }
+
+ return ret;
+}
+module_init(tscdev_init);
+
+static void __exit tscdev_exit(void)
+{
+ dev_t dev;
+ char *viraddr;
+ char *pbuf;
+ int gsize;
+
+ if (tsc_used != 1) {
+ TSC_INF("%s: tsc driver is disabled\n", __func__);
+ return;
+ }
+
+ if (tsc_devp == NULL) {
+ TSC_ERR("%s: invalid tsc_devp\n", __func__);
+ return;
+ }
+
+ /* unregister iomem and iounmap */
+ tsiomem_unregister(tsc_devp);
+
+ dev = MKDEV(tsc_devp->ts_dev_major, tsc_devp->ts_dev_minor);
+ device_destroy(tsc_devp->class, dev);
+ class_destroy(tsc_devp->class);
+ cdev_del(&tsc_devp->cdev);
+
+ /* release ts irq */
+ free_irq(TS_IRQ_NO, NULL);
+
+ /* release ts pin */
+ release_tsc_pio();
+
+ pbuf = tsc_devp->pmem;
+ gsize = tsc_devp->gsize;
+ if (tsc_devp->pmem) {
+ /* release reserved pages */
+ for (viraddr = pbuf; viraddr < pbuf + gsize; viraddr += PAGE_SIZE) {
+ ClearPageReserved(virt_to_page(viraddr));
+ }
+ kfree(pbuf);
+ pbuf = NULL;
+ }
+
+ unregister_chrdev_region(dev, 1);
+
+ kfree(tsc_devp);
+}
+module_exit(tscdev_exit);
+
+MODULE_AUTHOR("Soft-Allwinner");
+MODULE_DESCRIPTION("Allwinner Transport Stream Controller (TSC) driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/media/dvb/soc/tscdrv.h b/drivers/media/dvb/soc/tscdrv.h
new file mode 100644
index 0000000..b34a15c
--- /dev/null
+++ b/drivers/media/dvb/soc/tscdrv.h
@@ -0,0 +1,74 @@
+/*
+ * drivers/media/video/tsc/tscdrv.c
+ * (C) Copyright 2010-2015
+ * Allwinner Technology Co., Ltd. <
www.allwinnertech.com>
+ * csjamesdeng <
csjam...@allwinnertech.com>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __TSC_DRV_H__
+#define __TSC_DRV_H__
+
+#include "dvb_drv_sun7i.h"
+
+#define DRV_VERSION "0.0.3alpha" //version
+#define TS_IRQ_NO (35) //interrupt number,
+
+#ifndef TSCDEV_MAJOR
+#define TSCDEV_MAJOR (225)
+#endif
+
+#ifndef TSCDEV_MINOR
+#define TSCDEV_MINOR (0)
+#endif
+
+struct tsc_dev {
+ struct cdev cdev; /* char device struct */
+ struct device *dev; /* ptr to class device struct */
+ struct class *class; /* class for auto create device node */
+ struct semaphore sem; /* mutual exclusion semaphore */
+ spinlock_t lock; /* spinlock to protect ioclt access */
+ wait_queue_head_t wq; /* wait queue for poll ops */
+
+ struct resource *regs; /* registers resource */
+ char *regsaddr ; /* registers address */
+
+ unsigned int irq; /* tsc driver irq number */
+ unsigned int irq_flag; /* flag of tsc driver irq generated */
+
+ int ts_dev_major;
+ int ts_dev_minor;
+
+ struct clk *pll5_clk;
+ struct clk *sdram_clk; /* sdram clock */
+ struct clk *tsc_clk; /* ts clock */
+ struct clk *ahb_clk; /* ahb clock */
+
+ char *pmem; /* memory address */
+ unsigned int gsize; /* memory size */
+
+ struct intrstatus intstatus; /* save interrupt status */
+};
+
+#define TSC_DEBUG_LEVEL 3
+
+#if (TSC_DEBUG_LEVEL == 1)
+ #define TSC_DBG(format,args...) do {} while (0)
+ #define TSC_INF(format,args...) do {} while (0)
+ #define TSC_ERR(format,args...) printk(KERN_ERR "[tsc-err] "format,##args)
+#elif (TSC_DEBUG_LEVEL == 2)
+ #define TSC_DBG(format,args...) do {} while (0)
+ #define TSC_INF(format,args...) printk(KERN_INFO"[tsc-inf] "format,##args)
+ #define TSC_ERR(format,args...) printk(KERN_ERR "[tsc-err] "format,##args)
+#elif (TSC_DEBUG_LEVEL == 3)
+ #define TSC_DBG(format,args...) printk(KERN_INFO"[tsc-dbg] "format,##args)
+ #define TSC_INF(format,args...) printk(KERN_INFO"[tsc-inf] "format,##args)
+ #define TSC_ERR(format,args...) printk(KERN_ERR "[tsc-err] "format,##args)
+#endif
+
+#endif
--