summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorEric Nelson <eric.nelson@boundarydevices.com>2013-02-22 18:50:43 -0700
committerEric Nelson <eric.nelson@boundarydevices.com>2013-09-03 14:12:26 -0700
commit45ddf45f67072ea2ac2e2a9f561be602bc1609cb (patch)
treefd1fc36b99e6724f48051282c5cef846738bfb82 /drivers
parent4b6121db76250f74ad26fb5e7d80f97ef00128fe (diff)
Add tw686x-0.1.5 from Techwell/Intersil package
Diffstat (limited to 'drivers')
-rw-r--r--drivers/pci/pcie/Kconfig7
-rw-r--r--drivers/pci/pcie/Makefile2
-rw-r--r--drivers/pci/pcie/tw6869/Makefile8
-rw-r--r--drivers/pci/pcie/tw6869/i2c-sw.c321
-rw-r--r--drivers/pci/pcie/tw6869/i2c-sw.h60
-rw-r--r--drivers/pci/pcie/tw6869/tw686x-alsa.c658
-rw-r--r--drivers/pci/pcie/tw6869/tw686x-audio.c300
-rw-r--r--drivers/pci/pcie/tw6869/tw686x-core.c578
-rw-r--r--drivers/pci/pcie/tw6869/tw686x-device.c1172
-rw-r--r--drivers/pci/pcie/tw6869/tw686x-device.h56
-rw-r--r--drivers/pci/pcie/tw6869/tw686x-i2c.c213
-rw-r--r--drivers/pci/pcie/tw6869/tw686x-reg.h593
-rw-r--r--drivers/pci/pcie/tw6869/tw686x-video.c1670
-rw-r--r--drivers/pci/pcie/tw6869/tw686x.h415
-rw-r--r--drivers/pci/pcie/tw6869/videobuf-dma-contig-tw.c897
-rw-r--r--drivers/pci/pcie/tw6869/videobuf-dma-contig-tw.h46
16 files changed, 6996 insertions, 0 deletions
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
index dc29348264c6..41860efad080 100644
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -58,3 +58,10 @@ config PCIEASPM_DEBUG
config PCIE_PME
def_bool y
depends on PCIEPORTBUS && PM_RUNTIME && EXPERIMENTAL && ACPI
+
+
+config PCIE_TW6869
+ tristate "Techwell/Intersil TW6869"
+ select VIDEOBUF_DMA_CONTIG
+ select VIDEOBUF_DMA_SG
+ depends on PCIEPORTBUS && PM_RUNTIME
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
index 00c62df5a9fc..1ffa63c0e492 100644
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -14,3 +14,5 @@ obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
obj-$(CONFIG_PCIEAER) += aer/
obj-$(CONFIG_PCIE_PME) += pme.o
+
+obj-$(CONFIG_PCIE_TW6869) += tw6869/
diff --git a/drivers/pci/pcie/tw6869/Makefile b/drivers/pci/pcie/tw6869/Makefile
new file mode 100644
index 000000000000..268a69bd7141
--- /dev/null
+++ b/drivers/pci/pcie/tw6869/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for Techwell/Intersil TW6869
+#
+
+tw6869-objs := tw686x-core.o tw686x-video.o tw686x-device.o \
+ tw686x-alsa.o videobuf-dma-contig-tw.o tw686x-audio.o
+
+obj-m += tw6869.o
diff --git a/drivers/pci/pcie/tw6869/i2c-sw.c b/drivers/pci/pcie/tw6869/i2c-sw.c
new file mode 100644
index 000000000000..40007e4d19b7
--- /dev/null
+++ b/drivers/pci/pcie/tw6869/i2c-sw.c
@@ -0,0 +1,321 @@
+/*
+ * Driver for Techwell TW6864/68 based DVR cards
+ *
+ * (c) 2009-10 liran <jlee@techwellinc.com.cn> [Techwell China]
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include "i2c-sw.h"
+
+static struct timeval i2csw_tv_begin;
+
+//--------------------------------------------------------
+// 获得时钟
+//--------------------------------------------------------
+static u32 tick_count( void )
+{
+ struct timeval tv_now;
+ u64 tv_diff = 0;
+
+ do_gettimeofday( &tv_now );
+ tv_diff = 1000000 * ( tv_now.tv_sec - i2csw_tv_begin.tv_sec ) +
+ tv_now.tv_usec - i2csw_tv_begin.tv_usec;
+
+ return (((u32)tv_diff)/1000);
+}
+
+static void i2csw_bit_delay( struct i2c_sw *i2c )
+{
+ volatile u32 i;
+ for ( i = i2c->cycle; i > 0; i-- ) ;
+}
+
+static bool i2csw_start(struct i2c_sw *i2c)
+{
+ // SendStart - send an I2c start
+ // i.e. SDA 1 -> 0 with SCL = 1
+
+ if ( !i2c->s_sda( i2c, LevelHi ) ) { i2c->err_code = I2CERR_SDA; return false; }
+ if ( !i2c->s_scl( i2c, LevelHi ) ) { i2c->err_code = I2CERR_SCL; return false; }
+ if ( !i2c->s_sda( i2c, LevelLow ) ) { i2c->err_code = I2CERR_SDA; return false; }
+ if ( !i2c->s_scl( i2c, LevelLow ) ) { i2c->err_code = I2CERR_SCL; return false; }
+ i2c->err_code = I2CERR_OK;
+
+ return true;
+}
+
+static bool i2csw_stop(struct i2c_sw *i2c)
+{
+ // SendStop - sends an I2C stop, releasing the bus.
+ // i.e. SDA 0 -> 1 with SCL = 1
+
+ if ( !i2c->s_scl( i2c, LevelLow ) ) { i2c->err_code = I2CERR_SCL; return false; }
+ if ( !i2c->s_sda( i2c, LevelLow ) ) { i2c->err_code = I2CERR_SDA; return false; }
+ if ( !i2c->s_scl( i2c, LevelHi ) ) { i2c->err_code = I2CERR_SCL; return false; }
+ if ( !i2c->s_sda( i2c, LevelHi ) ) { i2c->err_code = I2CERR_SDA; return false; }
+ i2c->err_code = I2CERR_OK;
+
+ return true;
+}
+
+static bool i2csw_send_ack( struct i2c_sw *i2c )
+{
+ // Generate ACK signal
+ // i.e. SDA = 0 with SCL = 1
+
+ if ( !i2c->s_scl( i2c, LevelLow ) ) { i2c->err_code = I2CERR_SCL; return false; }
+ if ( !i2c->s_sda( i2c, LevelLow ) ) { i2c->err_code = I2CERR_SDA; return false; }
+ if ( !i2c->s_scl( i2c, LevelHi ) ) { i2c->err_code = I2CERR_SCL; return false; }
+ if ( !i2c->s_scl( i2c, LevelLow ) ) { i2c->err_code = I2CERR_SCL; return false; }
+ if ( !i2c->s_sda( i2c, LevelHi ) ) { i2c->err_code = I2CERR_SDA; return false; }
+ i2c->err_code = I2CERR_OK;
+
+ return true;
+}
+
+static bool i2csw_send_nack( struct i2c_sw *i2c )
+{
+ // Generate NACK signal
+ // i.e. SDA = 1 with SCL = 1
+
+ if ( !i2c->s_scl( i2c, LevelLow ) ) { i2c->err_code = I2CERR_SCL; return false; }
+ if ( !i2c->s_sda( i2c, LevelHi ) ) { i2c->err_code = I2CERR_SDA; return false; }
+ if ( !i2c->s_scl( i2c, LevelHi ) ) { i2c->err_code = I2CERR_SCL; return false; }
+ if ( !i2c->s_scl( i2c, LevelLow ) ) { i2c->err_code = I2CERR_SCL; return false; }
+ if ( !i2c->s_sda( i2c, LevelLow ) ) { i2c->err_code = I2CERR_SDA; return false; }
+ i2c->err_code = I2CERR_OK;
+
+ return true;
+}
+
+static bool i2csw_wait_ack( struct i2c_sw *i2c )
+{
+ u32 startTime;
+ u32 maxWait = 500; // 500 mSec
+
+ if ( !i2c->s_scl( i2c, LevelLow ) ){ i2c->err_code = I2CERR_SCL; return false; }
+ if ( !i2c->s_sda( i2c, LevelHi ) ) { i2c->err_code = I2CERR_SDA; return false; }
+
+ // loop until either ACK or timeout
+ startTime = tick_count();
+
+ while (1)
+ {
+ // SDA pin == 0 means the slave ACK'd
+ if ( i2c->g_sda( i2c ) == 0 )
+ {
+ if ( !i2c->s_scl( i2c, LevelHi ) ) { i2c->err_code = I2CERR_SCL; return false; }
+ if ( !i2c->s_scl( i2c, LevelLow ) ){ i2c->err_code = I2CERR_SCL; return false; }
+ i2c->err_code = I2CERR_OK;
+ return true;
+ }
+
+ // timeout?
+ if ( tick_count() - startTime > (u32)maxWait )
+ {
+ i2csw_stop(i2c);
+ i2c->err_code = I2CERR_TIMEOUT;
+ return false;
+ }
+ } // while
+
+ return true;
+}
+
+static bool i2csw_read(struct i2c_sw *i2c, u8 *value)
+{
+ u8 mask;
+
+ *value = 0x00;
+
+ // read 8 bits from I2c into Accumulator
+ for(mask = 0x80; mask > 0; mask = (u8)(mask >> 1)) {
+ if ( !i2c->s_scl( i2c, LevelLow ) ) { i2c->err_code = I2CERR_SCL; return false; }
+ if ( !i2c->s_scl( i2c, LevelHi ) ) { i2c->err_code = I2CERR_SCL; return false; }
+ if ( i2c->g_sda( i2c ) == LevelHi ){ *value=(u8)( *value | mask ); }// set the bit
+ }
+ i2c->err_code = I2CERR_OK;
+
+ return true;
+}
+
+static bool i2csw_write(struct i2c_sw *i2c, u8 value)
+{
+ u8 mask;
+
+ // generate bit patterns by setting SCL and SDA lines
+ for(mask = 0x80; mask > 0; mask = (u8)(mask >> 1)) {
+ if(!i2c->s_scl( i2c,LevelLow)) {i2c->err_code = I2CERR_SCL; return false;}
+
+ // Send one data bit.
+ if(value & mask) { // Put data bit on pin.
+ if(!i2c->s_sda( i2c,LevelHi)) {i2c->err_code = I2CERR_SDA; return false;}
+ }
+ else if(!i2c->s_sda( i2c,LevelLow)) {i2c->err_code = I2CERR_SDA; return false;}
+
+ if(!i2c->s_scl( i2c,LevelHi)) {i2c->err_code = I2CERR_SCL; return false;}
+ }
+
+ return i2csw_wait_ack(i2c);
+}
+
+bool i2csw_init(struct i2c_sw* i2c, long freq)
+{
+ u32 elapsed = 0L;
+ u32 start;
+ volatile u32 i;
+
+ if( !i2c ) {
+ return false;
+ }
+
+ i2c->cycle = 10000L; // use a large number to start
+
+ while(elapsed < 5) // loop until delay is long enough for calculation
+ {
+ i2c->cycle *= 10;
+ start = tick_count();
+
+ for(i = i2c->cycle; i > 0; i--) ;
+ elapsed = tick_count() - start;
+ }
+
+ if(freq > 1)
+ i2c->cycle = i2c->cycle / elapsed * 1000L / freq / 2;
+
+ spin_lock_init(&i2c->slock);
+
+ i2c->err_code = I2CERR_OK;
+
+ return true;
+}
+
+bool i2csw_s_scl(struct i2c_sw *i2c, TLevel scl)
+{
+ // loop until SCL really changes or timeout
+ u32 maxWait = 500; // 500 mSec
+ u32 startTime = tick_count();
+
+ while(1)
+ {
+ // has SCL changed yet?
+ if(i2c->g_scl(i2c) == scl)
+ break;
+
+ // timeout?
+ if(tick_count() - startTime > (u32)maxWait)
+ {
+ i2c->err_code = I2CERR_TIMEOUT;
+ return false;
+ }
+ }
+
+ i2csw_bit_delay(i2c);
+ i2c->err_code = I2CERR_OK;
+
+ return true;
+}
+
+bool i2csw_s_sda(struct i2c_sw *i2c, TLevel sda)
+{
+ i2csw_bit_delay(i2c);
+ i2c->err_code = I2CERR_OK;
+
+ return true;
+}
+
+//--------------------------------------------------------
+// 写数据
+//--------------------------------------------------------
+bool i2csw_write_bytes(struct i2c_sw *i2c, u8* data, int length)
+{
+ int i;
+
+ spin_lock( &i2c->slock );
+
+ if( i2csw_start(i2c) ) {
+ for(i = 0; i < length;i++) {
+ if(!i2csw_write(i2c, *(data+i))) {
+ break;
+ }
+ }
+ }
+
+ i2csw_stop(i2c);
+
+ spin_unlock( &i2c->slock );
+
+ return (i2c->err_code==I2CERR_OK);
+}
+
+//--------------------------------------------------------
+// 写数据
+//--------------------------------------------------------
+bool i2csw_write_bytes_nostop(struct i2c_sw *i2c, u8* data, int length)
+{
+ int i;
+
+ spin_lock( &i2c->slock );
+
+ if( i2csw_start(i2c) ) {
+ for(i = 0; i < length;i++) {
+ if(!i2csw_write(i2c, *(data+i))) {
+ break;
+ }
+ }
+ }
+
+ spin_unlock( &i2c->slock );
+
+ return (i2c->err_code==I2CERR_OK);
+}
+
+//--------------------------------------------------------
+// 读数据
+//--------------------------------------------------------
+bool i2csw_read_bytes(struct i2c_sw *i2c, u8 *data_w, int length_w, u8 *data_r, int length_r)
+{
+ int i;
+
+ spin_lock( &i2c->slock );
+
+ if( i2csw_start(i2c) ) {
+ for(i = 0; i < length_w;i++) {
+ if(!i2csw_write(i2c, *(data_w+i))) {
+ break;
+ }
+ }
+
+ if(i >= length_w) {
+ for(i = 0; i < length_r; i++) {
+ if(!i2csw_read(i2c, data_r+i)) {
+ break;
+ }
+ if(i < length_r - 1) {
+ i2csw_send_ack(i2c);
+ }
+ }
+ i2csw_send_nack(i2c);
+ }
+ }
+
+ spin_unlock( &i2c->slock );
+
+ return (i2c->err_code==I2CERR_OK);
+}
diff --git a/drivers/pci/pcie/tw6869/i2c-sw.h b/drivers/pci/pcie/tw6869/i2c-sw.h
new file mode 100644
index 000000000000..cbb140279fc3
--- /dev/null
+++ b/drivers/pci/pcie/tw6869/i2c-sw.h
@@ -0,0 +1,60 @@
+/*
+ * Driver for Techwell TW6864/68 based DVR cards
+ *
+ * (c) 2009-10 liran <jlee@techwellinc.com.cn> [Techwell China]
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define I2C_WRITE 0 // write operation
+#define I2C_READ 1 // read operation
+
+#define I2CDIV_MAX 15 // I2C maximum divider
+
+#define TIMEOUT 500 // timeout value (500ms) to wait for an I2C operation
+ // passing 3 values to I2C takes ~450ms
+
+typedef enum { LevelLow, LevelHi } TLevel;
+enum {
+ I2CERR_OK = 0, // no error
+ I2CERR_INIT = 1, // error in initialization
+ I2CERR_MODE = 2, // invalid I2C mode (must be either HW or SW)
+ I2CERR_NOACK = 3, // no ACK received from slave
+ I2CERR_TIMEOUT = 4, // timeout error
+ I2CERR_SCL = 5, // unable to change SCL line
+ I2CERR_SDA = 6, // unable to change SDA line
+ I2CERR_NODEVICE = 9, // the address is error -- for tuner
+};
+
+struct i2c_sw {
+ void* chip;
+ u32 cycle; // software control of frequency
+ int err_code;
+ spinlock_t slock;
+
+ bool (*s_scl)(struct i2c_sw *i2c, TLevel);
+ bool (*s_sda)(struct i2c_sw *i2c, TLevel);
+ int (*g_scl)(struct i2c_sw *i2c);
+ int (*g_sda)(struct i2c_sw *i2c);
+};
+
+bool i2csw_s_scl(struct i2c_sw *i2c, TLevel scl);
+bool i2csw_s_sda(struct i2c_sw *i2c, TLevel sda);
+
+bool i2csw_init(struct i2c_sw* i2c, long freq);
+bool i2csw_write_bytes(struct i2c_sw *i2c, u8* data, int length);
+bool i2csw_write_bytes_nostop(struct i2c_sw *i2c, u8* data, int length);
+bool i2csw_read_bytes(struct i2c_sw *i2c, u8 *data_w, int length_w, u8 *data_r, int length_r);
diff --git a/drivers/pci/pcie/tw6869/tw686x-alsa.c b/drivers/pci/pcie/tw6869/tw686x-alsa.c
new file mode 100644
index 000000000000..0863aa6cf40a
--- /dev/null
+++ b/drivers/pci/pcie/tw6869/tw686x-alsa.c
@@ -0,0 +1,658 @@
+/*
+ *
+ * Driver for Intersil|Techwell TW6869 based DVR cards
+ *
+ * (c) 2011-12 liran <jli11@intersil.com> [Intersil|Techwell China]
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Thanks to yiliang for variable audio packet length and more audio
+ * formats support.
+ */
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <linux/module.h>
+
+#include "tw686x.h"
+#include "tw686x-device.h"
+#include "tw686x-reg.h"
+
+MODULE_DESCRIPTION("alsa driver module for tw6869 based DVR cards");
+MODULE_AUTHOR("liran <jli11@intersil.com>");
+MODULE_LICENSE("GPL");
+
+static long tw686x_audio_nr = 0;
+
+/*
+ * PCM structure
+ */
+
+typedef struct snd_card_tw686x_pcm {
+ struct tw686x_adev *dev;
+
+ spinlock_t lock;
+
+ struct snd_pcm_substream *substream;
+} snd_card_tw686x_pcm_t;
+
+
+/*
+ * tw686x audio DMA IRQ handler
+ *
+ * Called whenever we get an tw686x audio interrupt
+ * Handles shifting between the 2 buffers, manages the read counters,
+ * and notifies ALSA when periods elapse
+ *
+ */
+
+static void tw686x_alsa_advance_one_period(struct tw686x_adev *dev)
+{
+ dev->read_offset = dev->period_idx * dev->blksize;
+ dev->period_idx ++;
+ if (dev->period_idx == dev->blocks)
+ dev->period_idx = 0;
+
+ // configure the next DMA buffer
+ // period_idx - 1, just transferred,
+ // period_idx, being transferred
+ // period_idx + 1, next buffer
+ dev->dma_blk += dev->blksize;
+ if (dev->period_idx + 1 == dev->blocks)
+ dev->dma_blk -= dev->bufsize;
+}
+
+void tw686x_alsa_irq(struct tw686x_adev *dev, u32 dma_status, u32 pb_status)
+{
+ int pb = (pb_status>>(dev->channel_id+TW686X_AUDIO_BEGIN)) & 1;
+
+ //daprintk(DPRT_LEVEL0, dev, "%s(%d)\n", __func__, pb);
+
+ /* next block addr */
+
+ if(dev->running)
+ {
+ /* update status & wake waiting readers */
+ tw686x_alsa_advance_one_period(dev);
+
+ if (dev->pb_flag != pb)
+ {
+ daprintk(1, dev, "pb flag mismatch %d\n", pb);
+
+ tw686x_dev_set_adma_buffer (dev, dev->dma_blk, dev->pb_flag);
+ tw686x_alsa_advance_one_period(dev);
+ dev->pb_flag = 1 - dev->pb_flag;
+
+ snd_pcm_period_elapsed(dev->substream);
+ }
+
+ tw686x_dev_set_adma_buffer (dev, dev->dma_blk, dev->pb_flag);
+ dev->pb_flag = 1 - dev->pb_flag;
+
+ snd_pcm_period_elapsed(dev->substream);
+ }
+}
+
+int tw686x_alsa_resetdma(struct tw686x_chip *chip, u32 dma_cmd)
+{
+ struct tw686x_adev *dev = NULL;
+ int i=0;
+
+ for(i=0; i<chip->aud_count; i++) {
+ if(dma_cmd & (1<<(TW686X_AUDIO_BEGIN+i))) {
+ dev = chip->aud_dev[i];
+ dev->pb_flag = 0;
+ tw686x_dev_run_audio( dev, true );
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * ALSA capture trigger
+ *
+ * - One of the ALSA capture callbacks.
+ *
+ * Called whenever a capture is started or stopped. Must be defined,
+ * but there's nothing we want to do here
+ *
+ */
+
+static int snd_card_tw686x_capture_trigger(struct snd_pcm_substream * substream,
+ int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_card_tw686x_pcm_t *pcm = runtime->private_data;
+ struct tw686x_adev *dev=pcm->dev;
+ int err = 0;
+ unsigned long flags;
+
+ daprintk(DPRT_LEVEL0, dev, "%s(%d)\n", __func__, (cmd == SNDRV_PCM_TRIGGER_START));
+
+//liran for test spin_lock(dev->slock); //!!! this lock may cause dead lock
+ spin_lock_irqsave(dev->slock, flags);
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ /* start dma */
+ dev->pb_flag = 0;
+
+ //将音频格式设置挪到这里
+ tw686x_dev_set_audio(dev->chip, runtime->rate, runtime->sample_bits, runtime->channels, dev->blksize);
+
+ tw686x_dev_run_audio(dev, true);
+ dev->running = true;
+ } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ /* stop dma */
+ tw686x_dev_run_audio(dev, false);
+ dev->running = false;
+ } else {
+ err = -EINVAL;
+ }
+ spin_unlock_irqrestore(dev->slock, flags);
+//liran for test spin_unlock(dev->slock);
+
+ return err;
+}
+
+/*
+ * DMA buffer initialization
+ *
+ * Uses V4L functions to initialize the DMA. Shouldn't be necessary in
+ * ALSA, but I was unable to use ALSA's own DMA, and had to force the
+ * usage of V4L's
+ *
+ * - Copied verbatim from saa7134-oss.
+ *
+ */
+
+static int dsp_buffer_init(struct tw686x_adev *dev)
+{
+ int err;
+
+ BUG_ON(!dev->bufsize);
+
+ videobuf_dma_init(&dev->dma);
+ err = videobuf_dma_init_kernel(&dev->dma, PCI_DMA_FROMDEVICE,
+ (dev->bufsize + PAGE_SIZE) >> PAGE_SHIFT);
+ return err;
+}
+
+/*
+ * DMA buffer release
+ *
+ * Called after closing the device, during snd_card_tw686x_capture_close
+ *
+ */
+
+static int dsp_buffer_free(struct tw686x_adev *dev)
+{
+ BUG_ON(!dev->blksize);
+
+ videobuf_dma_free(&dev->dma);
+
+ dev->blocks = 0;
+ dev->blksize = 0;
+ dev->bufsize = 0;
+
+ return 0;
+}
+
+/*
+ * ALSA PCM preparation
+ *
+ * - One of the ALSA capture callbacks.
+ *
+ * Called right after the capture device is opened, this function configures
+ * the buffer using the previously defined functions, allocates the memory,
+ * sets up the hardware registers, and then starts the DMA. When this function
+ * returns, the audio should be flowing.
+ *
+ */
+
+static int snd_card_tw686x_capture_prepare(struct snd_pcm_substream * substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_card_tw686x_t *card_tw686x = snd_pcm_substream_chip(substream);
+ struct tw686x_adev *dev = card_tw686x->dev;
+ snd_card_tw686x_pcm_t *pcm = runtime->private_data;
+
+ daprintk(DPRT_LEVEL0, dev, "%s(%d, %d, %d)\n", __func__, runtime->rate, runtime->channels, runtime->sample_bits);
+
+ pcm->dev->substream = substream;
+
+ //liran:: moved this to trigger function, to avoid the audio format changed by system when some channels are capturing.
+ //tw686x_dev_set_audio(dev->chip, runtime->rate, runtime->sample_bits, runtime->channels, dev->blksize);
+
+ dev->rate = runtime->rate;
+
+ return 0;
+}
+
+/*
+ * ALSA pointer fetching
+ *
+ * - One of the ALSA capture callbacks.
+ *
+ * Called whenever a period elapses, it must return the current hardware
+ * position of the buffer.
+ * Also resets the read counter used to prevent overruns
+ *
+ */
+
+static snd_pcm_uframes_t
+snd_card_tw686x_capture_pointer(struct snd_pcm_substream * substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_card_tw686x_pcm_t *pcm = runtime->private_data;
+ struct tw686x_adev *dev=pcm->dev;
+
+ //daprintk(DPRT_LEVEL0, dev, "%s(%d)\n", __func__, dev->read_offset);
+
+ return bytes_to_frames(runtime, dev->read_offset);
+}
+
+/*
+ * ALSA hardware capabilities definition
+ */
+
+static struct snd_pcm_hardware snd_card_tw686x_capture =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = 4096,
+ .period_bytes_min = 256,
+ .period_bytes_max = 1024,
+ .periods_min = 4,
+ .periods_max = 8,
+};
+
+static void snd_card_tw686x_runtime_free(struct snd_pcm_runtime *runtime)
+{
+ snd_card_tw686x_pcm_t *pcm = runtime->private_data;
+ struct tw686x_adev *dev = pcm->dev;
+
+ daprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ kfree(pcm);
+}
+
+
+/*
+ * ALSA hardware params
+ *
+ * - One of the ALSA capture callbacks.
+ *
+ * Called on initialization, right before the PCM preparation
+ *
+ */
+
+static int snd_card_tw686x_hw_params(struct snd_pcm_substream * substream,
+ struct snd_pcm_hw_params * hw_params)
+{
+ snd_card_tw686x_t *card_tw686x = snd_pcm_substream_chip(substream);
+ struct tw686x_adev *dev = card_tw686x->dev;
+ unsigned int period_size, periods;
+ struct scatterlist *list;
+ int err;
+
+ period_size = params_period_bytes(hw_params);
+ periods = params_periods(hw_params);
+
+ if (period_size < 0x100 || period_size > 0x10000)
+ return -EINVAL;
+ if (periods < 2)
+ return -EINVAL;
+ if (period_size * periods > 1024 * 1024)
+ return -EINVAL;
+
+ daprintk(DPRT_LEVEL0, dev, "%s(bufsize=%d)\n", __func__, period_size * periods);
+
+ if (dev->blocks == periods &&
+ dev->blksize == period_size)
+ return 0;
+
+ /* release the old buffer */
+ if (substream->runtime->dma_area) {
+#if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
+ videobuf_dma_unmap(&dev->chip->pci->dev, &dev->dma);
+#else
+ videobuf_sg_dma_unmap(&dev->chip->pci->dev, &dev->dma);
+#endif
+ dsp_buffer_free(dev);
+ substream->runtime->dma_area = NULL;
+ }
+
+ dev->pb_flag = 0;
+ dev->period_idx = 0;
+ dev->blocks = periods;
+ dev->blksize = period_size;
+ dev->bufsize = period_size * periods;
+ dev->running = false;
+
+ err = dsp_buffer_init(dev);
+ if (0 != err) {
+ dev->blocks = 0;
+ dev->blksize = 0;
+ dev->bufsize = 0;
+ return err;
+ }
+
+#if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
+ if (0 != (err = videobuf_dma_map(&dev->chip->pci->dev, &dev->dma))) {
+#else
+ if (0 != (err = videobuf_sg_dma_map(&dev->chip->pci->dev, &dev->dma))) {
+#endif
+ dsp_buffer_free(dev);
+ return err;
+ }
+
+ list = dev->dma.sglist;
+
+ daprintk(DPRT_LEVEL0, dev, "%s_%d: period_size %d, periods %d, sglen %d.\n",
+ __func__,
+ dev->channel_id,
+ period_size,
+ periods,
+ dev->dma.sglen);
+
+ dev->dma_blk = cpu_to_le32(sg_dma_address(list) - list->offset);
+ tw686x_dev_set_adma_buffer (dev, dev->dma_blk, 0);
+
+ dev->dma_blk += dev->blksize;
+ tw686x_dev_set_adma_buffer (dev, dev->dma_blk, 1);
+
+ /* I should be able to use runtime->dma_addr in the control
+ byte, but it doesn't work. So I allocate the DMA using the
+ V4L functions, and force ALSA to use that as the DMA area */
+
+#if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
+ substream->runtime->dma_area = dev->dma.vaddr;
+#else
+ substream->runtime->dma_area = dev->dma.vmalloc;
+#endif
+ substream->runtime->dma_bytes = dev->bufsize;
+ substream->runtime->dma_addr = 0;
+
+ return 0;
+}
+
+/*
+ * ALSA hardware release
+ *
+ * - One of the ALSA capture callbacks.
+ *
+ * Called after closing the device, but before snd_card_tw686x_capture_close
+ * It stops the DMA audio and releases the buffers.
+ *
+ */
+
+static int snd_card_tw686x_hw_free(struct snd_pcm_substream * substream)
+{
+ snd_card_tw686x_t *card_tw686x = snd_pcm_substream_chip(substream);
+ struct tw686x_adev *dev;
+
+ dev = card_tw686x->dev;
+
+ daprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ if (substream->runtime->dma_area) {
+#if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
+ videobuf_dma_unmap(&dev->chip->pci->dev, &dev->dma);
+#else
+ videobuf_sg_dma_unmap(&dev->chip->pci->dev, &dev->dma);
+#endif
+ dsp_buffer_free(dev);
+ substream->runtime->dma_area = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * ALSA capture finish
+ *
+ * - One of the ALSA capture callbacks.
+ *
+ * Called after closing the device.
+ *
+ */
+
+static int snd_card_tw686x_capture_close(struct snd_pcm_substream * substream)
+{
+ snd_card_tw686x_t *card_tw686x = snd_pcm_substream_chip(substream);
+ struct tw686x_adev *dev = card_tw686x->dev;
+
+ daprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ if (card_tw686x->mute_was_on) {
+ }
+ return 0;
+}
+
+/*
+ * ALSA capture start
+ *
+ * - One of the ALSA capture callbacks.
+ *
+ * Called when opening the device. It creates and populates the PCM
+ * structure
+ *
+ */
+
+static int snd_card_tw686x_capture_open(struct snd_pcm_substream * substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_card_tw686x_pcm_t *pcm;
+ snd_card_tw686x_t *card_tw686x = snd_pcm_substream_chip(substream);
+ struct tw686x_adev *dev;
+ int err;
+
+ if (!card_tw686x) {
+ printk(KERN_ERR "BUG: tw686x can't find device struct."
+ " Can't proceed with open\n");
+ return -ENODEV;
+ }
+ dev = card_tw686x->dev;
+
+ daprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ mutex_lock(&dev->lock);
+
+ dev->read_offset = 0;
+
+ mutex_unlock(&dev->lock);
+
+ pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
+ if (pcm == NULL)
+ return -ENOMEM;
+
+ pcm->dev=dev;
+
+ spin_lock_init(&pcm->lock);
+
+ pcm->substream = substream;
+ runtime->private_data = pcm;
+ runtime->private_free = snd_card_tw686x_runtime_free;
+ runtime->hw = snd_card_tw686x_capture;
+
+ err = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIODS, 2);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/*
+ * page callback (needed for mmap)
+ */
+
+static struct page *snd_card_tw686x_page(struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ void *pageptr = substream->runtime->dma_area + offset;
+
+ snd_card_tw686x_t *card_tw686x = snd_pcm_substream_chip(substream);
+ struct tw686x_adev *dev = card_tw686x->dev;
+
+ daprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ return vmalloc_to_page(pageptr);
+}
+
+/*
+ * ALSA capture callbacks definition
+ */
+
+static struct snd_pcm_ops snd_card_tw686x_capture_ops = {
+ .open = snd_card_tw686x_capture_open,
+ .close = snd_card_tw686x_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_card_tw686x_hw_params,
+ .hw_free = snd_card_tw686x_hw_free,
+ .prepare = snd_card_tw686x_capture_prepare,
+ .trigger = snd_card_tw686x_capture_trigger,
+ .pointer = snd_card_tw686x_capture_pointer,
+ .page = snd_card_tw686x_page,
+};
+
+/*
+ * ALSA PCM setup
+ *
+ * Called when initializing the board. Sets up the name and hooks up
+ * the callbacks
+ *
+ */
+
+static int snd_card_tw686x_pcm(snd_card_tw686x_t *card_tw686x, long device)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ daprintk(DPRT_LEVEL0, card_tw686x->dev, "%s()\n", __func__);
+
+ if ((err = snd_pcm_new(card_tw686x->card, "TW6869 PCM", device, 0, 1, &pcm)) < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_tw686x_capture_ops);
+ pcm->private_data = card_tw686x;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "TW6869 PCM");
+
+ return 0;
+}
+
+static void snd_tw686x_free(struct snd_card * card)
+{
+ snd_card_tw686x_t *card_tw686x = (snd_card_tw686x_t*)card->private_data;
+ struct tw686x_adev* dev = card_tw686x->dev;
+
+ daprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+}
+
+/*
+ * ALSA initialization
+ *
+ * Called by the init routine, once for each tw686x device present,
+ * it creates the basic structures and registers the ALSA devices
+ *
+ */
+
+int tw686x_alsa_create(struct tw686x_adev *dev)
+{
+
+ struct snd_card *card = NULL;
+ snd_card_tw686x_t *card_tw686x;
+ int err;
+
+ daprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ if(tw686x_audio_nr > (SNDRV_CARDS-2))
+ return -ENODEV;
+
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+ err = snd_card_create(tw686x_audio_nr+1, NULL, THIS_MODULE,
+ sizeof(snd_card_tw686x_t), &card);
+ if (err < 0)
+ return err;
+#else
+ card = snd_card_new(-2, NULL, THIS_MODULE, sizeof(snd_card_tw686x_t));
+ if (card == NULL)
+ return -ENOMEM;
+#endif
+
+ strcpy(card->driver, "TW6869");
+
+ /* Card "creation" */
+
+ card->private_free = snd_tw686x_free;
+ card_tw686x = (snd_card_tw686x_t *) card->private_data;
+
+ spin_lock_init(&card_tw686x->lock);
+
+ card_tw686x->dev = dev;
+ card_tw686x->card= card;
+ dev->card = card_tw686x;
+
+ mutex_init(&dev->lock);
+
+ if ((err = snd_card_tw686x_pcm(card_tw686x, 0)) < 0)
+ goto __nodev;
+
+ snd_card_set_dev(card, &dev->chip->pci->dev);
+
+ /* End of "creation" */
+
+ strcpy(card->shortname, "TW6869");
+ sprintf(card->longname, "%s at 0x%p irq %d",
+ dev->chip->name, dev->chip->bmmio, dev->chip->pci->irq);
+
+ daprintk(1, dev, "alsa: %s registered as card %d\n",card->longname,dev->channel_id);
+
+ if ((err = snd_card_register(card)) == 0) {
+ tw686x_audio_nr++;
+ return 0;
+ }
+
+__nodev:
+ snd_card_free(card);
+ return err;
+}
+
+int tw686x_alsa_free(struct tw686x_adev *dev)
+{
+ if(dev->card) {
+ snd_card_free(dev->card->card);
+ dev->card = NULL;
+ }
+
+ tw686x_audio_nr--;
+
+ return 1;
+}
diff --git a/drivers/pci/pcie/tw6869/tw686x-audio.c b/drivers/pci/pcie/tw6869/tw686x-audio.c
new file mode 100644
index 000000000000..2c2df98f5a91
--- /dev/null
+++ b/drivers/pci/pcie/tw6869/tw686x-audio.c
@@ -0,0 +1,300 @@
+/*
+ *
+ * Driver for Intersil|Techwell TW6869 based DVR cards
+ *
+ * (c) 2011-12 liran <jli11@intersil.com> [Intersil|Techwell China]
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Thanks to yiliang for variable audio packet length and more audio
+ * formats support.
+ */
+
+#include <linux/module.h>
+#include "tw686x.h"
+#include "tw686x-device.h"
+#include "tw686x-reg.h"
+
+#define TW686X_AUDIO_PERIOD_SIZE 4096
+
+MODULE_DESCRIPTION("audio driver module for tw6869 based DVR cards");
+MODULE_AUTHOR("liran <jli11@intersil.com>");
+MODULE_LICENSE("GPL");
+
+static int tw686x_audio_buffer_init(struct tw686x_adev *dev)
+{
+ int err;
+
+ BUG_ON(!dev->bufsize);
+
+ videobuf_dma_init(&dev->dma);
+ err = videobuf_dma_init_kernel(&dev->dma, PCI_DMA_FROMDEVICE,
+ (dev->bufsize + PAGE_SIZE) >> PAGE_SHIFT);
+ return err;
+}
+
+/*
+ * DMA buffer release
+ *
+ * Called after closing the device, during snd_card_tw686x_capture_close
+ *
+ */
+
+static int tw686x_audio_buffer_free(struct tw686x_adev *dev)
+{
+ BUG_ON(!dev->blksize);
+
+ videobuf_dma_free(&dev->dma);
+
+ dev->blocks = 0;
+ dev->blksize = 0;
+ dev->bufsize = 0;
+
+ return 0;
+}
+
+//static
+int tw686x_audio_trigger(struct tw686x_adev *dev, int cmd)
+{
+ unsigned int dma_addr;
+ struct scatterlist *list;
+
+ daprintk(DPRT_LEVEL0, dev, "%s(%d)\n", __func__, cmd);
+
+ if(cmd)
+ {
+ dev->pb_flag = 0;
+
+ list = dev->dma.sglist + dev->dma_blk;
+
+ dma_addr = cpu_to_le32(sg_dma_address(list) - list->offset);
+ tw686x_dev_set_adma_buffer (dev, dma_addr, 0);
+
+ dev->dma_blk++;
+ if(dev->dma_blk >= dev->blocks)
+ {
+ dev->dma_blk = 0;
+ }
+
+ list = dev->dma.sglist + dev->dma_blk;
+ dma_addr = cpu_to_le32(sg_dma_address(list) - list->offset);
+ tw686x_dev_set_adma_buffer (dev, dma_addr, 1);
+
+ dev->dma_blk++;
+ if(dev->dma_blk >= dev->blocks)
+ {
+ dev->dma_blk = 0;
+ }
+ }
+
+ /* start dma */
+ dev->substream = (struct snd_pcm_substream*)(NULL+cmd);
+ tw686x_dev_run_audio(dev, cmd>0);
+
+ return 0;
+}
+
+static void tw686x_audio_advance_one_period(struct tw686x_adev *dev)
+{
+ dev->dma_blk++;
+ if(dev->dma_blk >= dev->blocks)
+ dev->dma_blk = 0;
+
+ dev->period_idx++;
+ if(dev->period_idx >= dev->blocks)
+ {
+ tw686x_audio_trigger(dev, 0);
+ }
+}
+
+void tw686x_audio_irq(struct tw686x_adev *dev, u32 dma_status, u32 pb_status)
+{
+ int pb = (pb_status>>(dev->channel_id+TW686X_AUDIO_BEGIN)) & 1;
+ unsigned int dma_addr;
+ struct scatterlist *list;
+
+ daprintk(DPRT_LEVEL0, dev, "%s(%d)\n", __func__, pb);
+
+ /* next block addr */
+
+ /* update status & wake waiting readers */
+ tw686x_audio_advance_one_period(dev);
+
+ list = dev->dma.sglist + dev->dma_blk;
+ dma_addr = cpu_to_le32(sg_dma_address(list) - list->offset);
+
+ if (dev->pb_flag != pb)
+ {
+ daprintk(1, dev, "pb flag mismatch\n");
+
+ tw686x_dev_set_adma_buffer (dev, dma_addr, dev->pb_flag);
+ tw686x_audio_advance_one_period(dev);
+ dev->pb_flag = 1 - dev->pb_flag;
+
+ list = dev->dma.sglist + dev->dma_blk;
+ dma_addr = cpu_to_le32(sg_dma_address(list) - list->offset);
+
+ //snd_pcm_period_elapsed(dev->substream);
+ }
+
+ tw686x_dev_set_adma_buffer (dev, dma_addr, dev->pb_flag);
+ dev->pb_flag = 1 - dev->pb_flag;
+
+ //snd_pcm_period_elapsed(dev->substream);
+}
+
+int tw686x_audio_param(struct tw686x_adev *dev, struct tw686x_aparam* ap)
+{
+ unsigned long flags;
+ daprintk(DPRT_LEVEL0, dev, "%s(%d %d %d %d)\n", __func__, ap->sample_rate, ap->sample_bits, ap->channels, ap->blksize);
+
+ spin_lock_irqsave(dev->slock, flags);
+
+ dev->period_idx = 0;
+ dev->read_offset = 0;
+ dev->substream = NULL;
+ dev->dma_blk = 0;
+
+ if(ap->blksize < 0)
+ {
+ tw686x_audio_trigger(dev, 0);
+ }
+ else
+ {
+ ap->blksize = min(ap->blksize, TW686X_AUDIO_PERIOD_SIZE);
+ tw686x_dev_set_audio(dev->chip, ap->sample_rate, ap->sample_bits, ap->channels, ap->blksize);
+ dev->blksize = ap->blksize;
+ }
+
+ spin_unlock_irqrestore(dev->slock, flags);
+
+ return 0;
+}
+
+int tw686x_audio_data(struct tw686x_adev *dev, struct tw686x_adata *ad)
+{
+ unsigned long flags;
+ struct scatterlist *list;
+
+ spin_lock_irqsave(dev->slock, flags);
+
+ if(dev->period_idx > 0)
+ {
+ list = dev->dma.sglist + dev->read_offset;
+ ad->len = min(ad->len, (int)dev->blksize);
+ memcpy(ad->data, sg_virt(list), ad->len);
+
+ dev->period_idx--;
+ dev->read_offset ++;
+ if (dev->read_offset >= dev->blocks)
+ dev->read_offset = 0;
+ }
+ else
+ {
+ ad->len = 0;
+ }
+ if((dev->period_idx<(dev->blocks-1)) && (dev->substream==NULL))
+ {
+ tw686x_audio_trigger(dev, 1);
+ }
+
+ spin_unlock_irqrestore(dev->slock, flags);
+
+ return 0;
+}
+
+int tw686x_audio_resetdma(struct tw686x_chip *chip, u32 dma_cmd)
+{
+#if(0)
+ struct tw686x_adev *dev = NULL;
+ int i=0;
+
+ for(i=0; i<chip->aud_count; i++) {
+ if(dma_cmd & (1<<(TW686X_AUDIO_BEGIN+i))) {
+ dev = chip->aud_dev[i];
+ dev->period_idx = 0;
+ dev->read_offset = 0;
+ dev->substream = NULL;
+ dev->dma_blk = 0;
+ tw686x_audio_trigger( dev, 1 );
+ }
+ }
+#endif
+ return 0;
+}
+
+int tw686x_audio_create(struct tw686x_adev *dev)
+{
+ unsigned int period_size, periods;
+ int err;
+
+ daprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ mutex_init(&dev->lock);
+
+ period_size = TW686X_AUDIO_PERIOD_SIZE; //固定为4096
+ periods = 4; //最小为2
+
+ daprintk(DPRT_LEVEL0, dev, "%s(bufsize=%d)\n", __func__, period_size * periods);
+
+ dev->blocks = periods;
+ dev->blksize = period_size;
+ dev->bufsize = period_size * periods;
+ dev->period_idx = 0;
+ dev->read_offset = 0;
+ dev->substream = NULL;
+ dev->card = NULL;
+
+ err = tw686x_audio_buffer_init(dev);
+ if (0 != err) {
+ dev->blocks = 0;
+ dev->blksize = 0;
+ dev->bufsize = 0;
+ return err;
+ }
+
+#if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
+ if (0 != (err = videobuf_dma_map(&dev->chip->pci->dev, &dev->dma))) {
+#else
+ if (0 != (err = videobuf_sg_dma_map(&dev->chip->pci->dev, &dev->dma))) {
+#endif
+ tw686x_audio_buffer_free(dev);
+ return err;
+ }
+
+ daprintk(DPRT_LEVEL0, dev, "%s_%d: period_size %d, periods %d, sglen %d.\n",
+ __func__,
+ dev->channel_id,
+ period_size,
+ periods,
+ dev->dma.sglen);
+
+ return 0;
+}
+
+int tw686x_audio_free(struct tw686x_adev *dev)
+{
+ if (dev->substream != NULL) {
+#if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
+ videobuf_dma_unmap(&dev->chip->pci->dev, &dev->dma);
+#else
+ videobuf_sg_dma_unmap(&dev->chip->pci->dev, &dev->dma);
+#endif
+ tw686x_audio_buffer_free(dev);
+ dev->substream = NULL;
+ }
+
+ return 0;
+}
diff --git a/drivers/pci/pcie/tw6869/tw686x-core.c b/drivers/pci/pcie/tw6869/tw686x-core.c
new file mode 100644
index 000000000000..61a358769871
--- /dev/null
+++ b/drivers/pci/pcie/tw6869/tw686x-core.c
@@ -0,0 +1,578 @@
+/*
+ *
+ * Driver for Techwell TW6864/68 based DVR cards
+ *
+ * (c) 2009-10 liran <jlee@techwellinc.com.cn> [Techwell China]
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/div64.h>
+
+#include "tw686x-reg.h"
+#include "tw686x.h"
+#include "tw686x-device.h"
+
+MODULE_DESCRIPTION("Driver for tw6864/68 based DVR cards");
+MODULE_AUTHOR("liran<jli11@intersil.com>");
+MODULE_LICENSE("GPL");
+
+unsigned int tw686x_debug;
+module_param(tw686x_debug, int, 0644);
+MODULE_PARM_DESC(tw686x_debug, "enable debug messages");
+
+unsigned int tw686x_dmamode = TW686X_DMA_MODE_BLOCK;
+module_param(tw686x_dmamode, int, 0644);
+MODULE_PARM_DESC(tw686x_dmamode, "tw686x dma mode");
+
+unsigned int tw686x_audio = 1;
+module_param(tw686x_audio, int, 0644);
+MODULE_PARM_DESC(tw686x_audio, "tw686x audio enable");
+
+unsigned int tw686x_restart_timer = 1;
+module_param(tw686x_restart_timer, int, 0644);
+MODULE_PARM_DESC(tw686x_restart_timer, "tw686x dma restart timer enable");
+
+unsigned int tw686x_bcsi = 0;
+module_param(tw686x_bcsi, int, 0644);
+MODULE_PARM_DESC(tw686x_bcsi, "tw686x driver for BCSI enable");
+
+//zxx_20120712 added for TW6865
+unsigned int tw686x_6865 = 0;
+module_param(tw686x_6865, int, 0644);
+MODULE_PARM_DESC(tw686x_6865, "tw6865 mode");
+
+static unsigned int tw686x_chipcount = 0;
+
+DEFINE_MUTEX(tw686x_chiplist_lock);
+LIST_HEAD(tw686x_chiplist);
+
+void tw686x_dmabuf_free(struct tw686x_chip *chip,
+ struct tw686x_dmabuf *buf)
+{
+ struct pci_dev *pci = chip->pci;
+
+ if (NULL == buf->cpu)
+ return;
+ pci_free_consistent(pci, buf->size, buf->cpu, buf->dma);
+
+ dprintk(DPRT_LEVEL0, chip, " dmamem free dma=%lx cpu=%p size=%d\n",
+ (unsigned long)buf->dma, buf->cpu, buf->size);
+
+ memset(buf,0,sizeof(*buf));
+}
+
+int tw686x_dmabuf_alloc(struct tw686x_chip *chip,
+ struct tw686x_dmabuf *buf,
+ unsigned int size)
+{
+ struct pci_dev *pci = chip->pci;
+ __le32 *cpu;
+ dma_addr_t dma = 0;
+
+ if (NULL != buf->cpu && buf->size < size)
+ tw686x_dmabuf_free(chip,buf);
+ if (NULL == buf->cpu) {
+ size = PAGE_ALIGN(size);
+ cpu = pci_alloc_consistent(pci, size, &dma);
+ if (NULL == cpu) {
+ dprintk(DPRT_LEVEL0, chip, "dmamem alloc failed size=%d\n", size);
+ return -ENOMEM;
+ }
+ buf->cpu = cpu;
+ buf->dma = dma;
+ buf->size = size;
+ dprintk(DPRT_LEVEL0, chip, "dmamem alloc dma=%lx cpu=%p size=%d\n",
+ (unsigned long)dma, cpu, size);
+ }
+ memset(buf->cpu,0,buf->size);
+ return 0;
+}
+
+static void tw686x_shutdown(struct tw686x_chip *chip)
+{
+ /* Disable Video DMA */
+ /* Disable Audio activity */
+ /* Disable Interrupts */
+
+ tw686x_dev_uninit( chip );
+}
+
+static void tw686x_reset(struct tw686x_chip *chip)
+{
+ dprintk(1, chip, "%s()\n", __func__);
+
+ tw686x_dev_uninit( chip );
+ tw686x_dev_init( chip );
+}
+
+static int get_resources(struct tw686x_chip *chip)
+{
+ if (request_mem_region(pci_resource_start(chip->pci, 0),
+ pci_resource_len(chip->pci, 0),
+ chip->name))
+ return 0;
+
+ printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx\n",
+ chip->name, (unsigned long long)pci_resource_start(chip->pci, 0));
+
+ return -EBUSY;
+}
+
+static void tw686x_assist_timeout(unsigned long data)
+{
+ struct tw686x_chip* chip = (struct tw686x_chip*)data;
+ int i=0;
+
+ u32 tmp = tw_read(TW6864_R_DMA_CMD);
+ u32 tmp1 = tw_read(TW6864_R_DMA_CHANNEL_ENABLE);
+ dprintk(DPRT_LEVEL0, chip, "%s(), %x %x %x\n", __func__, tmp, tmp1, chip->dma_restart);
+
+ if(chip->dma_restart > 0)
+ {
+ unsigned long flags;
+ struct tw686x_vdev* dev;
+ struct tw686x_adev* adev;
+
+ tw_write(TW6864_R_DMA_CMD, 0);
+
+ spin_lock_irqsave(&chip->slock, flags);
+ for(i=0; i<chip->vid_count; i++) {
+ dev = chip->vid_dev[i];
+ if(chip->dma_restart & (1<<i)) {
+ dev->dma_started = false;
+ dev->pb_next = 0;
+ dev->pb_curr = 0;
+ tw686x_video_start(dev);
+ }
+ }
+ if(CaptureFLV)
+ {
+ for(i=0; i<chip->aud_count; i++) {
+ if(chip->dma_restart & (1<<(TW686X_AUDIO_BEGIN+i))) {
+ adev = chip->aud_dev[i];
+ if(adev->running)
+ {
+ adev->period_idx = 0;
+ adev->read_offset = 0;
+ adev->substream = NULL;
+ adev->dma_blk = 0;
+ tw686x_audio_trigger( adev, 1 );
+ }
+ }
+ }
+ }
+ else
+ {
+ for(i=0; i<chip->aud_count; i++) {
+ if(chip->dma_restart & (1<<(TW686X_AUDIO_BEGIN+i))) {
+ adev = chip->aud_dev[i];
+ if(adev->running)
+ {
+ adev->pb_flag = 0;
+ tw686x_dev_run_audio(adev, true);
+ }
+ }
+ }
+ }
+ chip->dma_restart = 0;
+ spin_unlock_irqrestore(&chip->slock, flags);
+ }
+#if(0)
+ else
+ {
+ for(i=0; i<chip->vid_count; i++) {
+ tw686x_video_status(chip->vid_dev[i]);
+ }
+ }
+
+ mod_timer(&chip->tm_assist, jiffies+BUFFER_TIMEOUT*2);
+#endif
+}
+
+static int tw686x_setup(struct tw686x_chip *chip)
+{
+ struct tw686x_vdev *vdev;
+ struct tw686x_adev *adev;
+ int i = 0;
+
+ chip->nr = tw686x_chipcount++;
+ sprintf(chip->name, "tw6869[%d]", chip->nr);
+
+ mutex_lock(&tw686x_chiplist_lock);
+ list_add_tail(&chip->chiplist, &tw686x_chiplist);
+ mutex_unlock(&tw686x_chiplist_lock);
+
+ if (get_resources(chip) < 0) {
+ printk(KERN_ERR "%s No more PCIe resources for "
+ "subsystem: %04x:%04x\n",
+ chip->name, chip->pci->subsystem_vendor,
+ chip->pci->subsystem_device);
+
+ tw686x_chipcount--;
+ return -ENODEV;
+ }
+
+ /* PCIe stuff */
+ chip->bmmio = ioremap(pci_resource_start(chip->pci, 0),
+ pci_resource_len(chip->pci, 0));
+
+ printk(KERN_INFO "%s: subsystem: %04x:%04x\n",
+ chip->name, chip->pci->subsystem_vendor,
+ chip->pci->subsystem_device);
+
+ //zxx_20120712 added for TW6865
+ int nSubDevNum = TW686X_DECODER_COUNT;
+ if (tw686x_6865) {
+ nSubDevNum = TW686X_DECODER_COUNT / 2;
+ }
+
+ chip->vid_count = nSubDevNum;
+
+ /* init hardware */
+ tw686x_reset(chip);
+ chip->aud_count = chip->vid_count;
+
+ for( i=0; i<chip->vid_count; i++ ) {
+ vdev = kzalloc(sizeof(struct tw686x_vdev), GFP_KERNEL);
+ if (NULL == vdev) {
+ return -ENOMEM;
+ }
+
+ vdev->chip = chip;
+ vdev->channel_id = i;
+ vdev->slock= &chip->slock;
+ chip->vid_dev[i]= vdev;
+
+ if (tw686x_video_register(vdev) < 0) {
+ printk(KERN_ERR "%s: %s() Failed to register "
+ "video adapters %d\n", chip->name, __func__, i);
+ return -ENOMEM;
+ }
+
+ if( tw686x_dmabuf_alloc(chip, &vdev->dma_desc[TW686X_DMA_DESC_P], TW686X_DMA_DESC_LEN)<0 ) {
+ printk(KERN_INFO "%s: allocate dma p-buffer descriptor memory failed\n", chip->name);
+ return -ENOMEM;
+ }
+ if( tw686x_dmabuf_alloc(chip, &vdev->dma_desc[TW686X_DMA_DESC_B], TW686X_DMA_DESC_LEN)<0 ) {
+ printk(KERN_INFO "%s: allocate dma b-buffer descriptor memory failed\n", chip->name);
+ return -ENOMEM;
+ }
+ tw686x_dev_set_pbdesc(vdev);
+ }
+
+ if(tw686x_audio) {
+ for( i=0; i<chip->aud_count; i++ ) {
+ adev = kzalloc(sizeof(struct tw686x_adev), GFP_KERNEL);
+ if (NULL == adev) {
+ return -ENOMEM;
+ }
+
+ adev->chip = chip;
+ adev->channel_id = i;
+ adev->slock= &chip->slock;
+ chip->aud_dev[i]= adev;
+
+ if(CaptureFLV)
+ {
+ if (tw686x_audio_create(adev) < 0) {
+ printk(KERN_ERR "%s: %s() Failed to create "
+ "audio adapters %d\n", chip->name, __func__, i);
+ kfree( adev );
+ chip->aud_dev[i] = NULL;
+ break;
+ }
+ }
+ else
+ {
+ if (tw686x_alsa_create(adev) < 0) {
+ printk(KERN_ERR "%s: %s() Failed to create "
+ "audio adapters %d\n", chip->name, __func__, i);
+ kfree( adev );
+ chip->aud_dev[i] = NULL;
+ break;
+ }
+ }
+ }
+ }
+
+ if(chip->vid_count > 0) {
+ chip->tm_assist.function = tw686x_assist_timeout;
+ chip->tm_assist.data = (unsigned long)chip;
+ init_timer(&chip->tm_assist);
+ //mod_timer(&chip->tm_assist, jiffies+BUFFER_TIMEOUT*2);
+ }
+ chip->dma_restart = 0;
+
+ return 0;
+}
+
+static void tw686x_unregister(struct tw686x_chip *chip)
+{
+ int i=0;
+
+ release_mem_region(pci_resource_start(chip->pci, 0),
+ pci_resource_len(chip->pci, 0));
+
+ if(chip->vid_count > 0) {
+ chip->dma_restart = 0;
+ del_timer(&chip->tm_assist);
+ }
+
+ for( i=0; i<chip->vid_count; i++ ) {
+ if(chip->vid_dev[i]) {
+ tw686x_video_unregister( chip->vid_dev[i] );
+ tw686x_dmabuf_free( chip, &chip->vid_dev[i]->dma_desc[TW686X_DMA_DESC_P] );
+ tw686x_dmabuf_free( chip, &chip->vid_dev[i]->dma_desc[TW686X_DMA_DESC_B] );
+ kfree( chip->vid_dev[i] );
+ chip->vid_dev[i] = NULL;
+ }
+ }
+
+ for( i=0; i<chip->aud_count; i++ ) {
+ if(chip->aud_dev[i]) {
+ if(CaptureFLV)
+ {
+ tw686x_audio_free( chip->aud_dev[i] );
+ }
+ else
+ {
+ tw686x_alsa_free( chip->aud_dev[i] );
+ }
+ kfree( chip->aud_dev[i] );
+ chip->aud_dev[i] = NULL;
+ }
+ }
+
+ iounmap(chip->bmmio);
+}
+
+static irqreturn_t tw686x_irq(int irq, void *chip_id)
+{
+ struct tw686x_chip *chip = chip_id;
+ struct tw686x_vdev *vdev = NULL;
+ struct tw686x_adev *adev = NULL;
+ int handled = 0;
+ int i;
+ u32 dma_status, pb_status, dma_error, dma_cmd;
+
+ dma_status = tw_read( TW6864_R_DMA_INT_STATUS );
+ pb_status = tw_read( TW6864_R_DMA_PB_STATUS );
+ dma_error = tw_read( TW6864_R_DMA_INT_ERROR );
+ dma_cmd = tw_read( TW6864_R_DMA_CMD );
+
+ if( (dma_cmd & TW6864_R_DMA_CMD_RESETALL) && (dma_status || dma_error) ) {
+ spin_lock(&chip->slock);
+
+ for( i=0; i<chip->vid_count; i++ ) {
+ if( (dma_status & TW6864_R_DMA_INT_STATUS_DMA(i))/* || (dma_error & TW6864_R_DMA_VINT_FIFO(i))*/ ) {
+ vdev = chip->vid_dev[i];
+ tw686x_video_irq( vdev, dma_status, pb_status );
+ }
+ else if( dma_error & TW6864_R_DMA_VINT_FIFO(i) ) {
+ vdev = chip->vid_dev[i];
+ tw686x_dev_run( vdev, false );
+ //dvprintk(DPRT_LEVEL0, vdev, "irq %x %x\n", dma_status, dma_error);
+ }
+ }
+ for( i=0; i<chip->aud_count; i++ ) {
+ if( dma_status & TW6864_R_DMA_INT_STATUS_DMA(i+TW686X_AUDIO_BEGIN) ) {
+ adev = chip->aud_dev[i];
+#if(CaptureFLV)
+ tw686x_audio_irq( adev, dma_status, pb_status );
+#else
+ tw686x_alsa_irq( adev, dma_status, pb_status );
+#endif
+ }
+ }
+
+ if( dma_status & TW6864_R_DMA_INT_STATUS_TOUT )
+ dprintk(DPRT_LEVEL0, chip, "irq timeout %x %x %x\n", dma_status, dma_error, dma_cmd);
+
+ if( dma_status & TW6864_R_DMA_INT_STATUS_TOUT )
+ {
+ //DMA time out
+ tw686x_dev_setdma(chip, 0);
+ tw686x_video_resetdma( chip, dma_cmd );
+#if(CaptureFLV)
+ //tw686x_audio_resetdma( chip, dma_cmd );
+#else
+ //tw686x_alsa_resetdma( chip, dma_cmd );
+#endif
+ }
+
+ handled = 1;
+ spin_unlock(&chip->slock);
+ }
+
+ return IRQ_RETVAL(handled);
+}
+
+static int __devinit tw686x_initchip(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ struct tw686x_chip *chip;
+ int err;
+ u8 pci_rev,pci_lat;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (NULL == chip)
+ return -ENOMEM;
+
+ spin_lock_init(&chip->slock);
+
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+ err = v4l2_device_register(&pci_dev->dev, &chip->v4l2_dev);
+ if (err < 0)
+ goto fail_free;
+#endif
+
+ /* pci init */
+ chip->pci = pci_dev;
+ if (pci_enable_device(pci_dev)) {
+ err = -EIO;
+ goto fail_unreg;
+ }
+
+ if (tw686x_setup(chip) < 0) {
+ err = -EINVAL;
+ goto fail_unreg;
+ }
+
+ /* print pci info */
+ pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &pci_rev);
+ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_lat);
+ printk(KERN_INFO "%s: found at %s, bus: %d, rev: %d, irq: %d, "
+ "latency: %d, mmio: 0x%llx\n", chip->name,
+ pci_name(pci_dev), pci_dev->bus->number, pci_rev, pci_dev->irq, pci_lat,
+ (unsigned long long)pci_resource_start(pci_dev, 0));
+
+ pci_set_master(pci_dev);
+ if (!pci_dma_supported(pci_dev, 0xffffffff)) {
+ printk("%s: Oops: no 32bit PCI DMA ???\n", chip->name);
+ err = -EIO;
+ goto fail_irq;
+ }
+
+ err = request_irq(pci_dev->irq, tw686x_irq,
+ IRQF_SHARED | IRQF_DISABLED, chip->name, chip);
+ if (err < 0) {
+ printk(KERN_ERR "%s: can't get IRQ %d\n",
+ chip->name, pci_dev->irq);
+ goto fail_irq;
+ }
+
+#if(LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,30))
+ pci_set_drvdata(pci_dev, chip);
+#endif
+
+ return 0;
+
+fail_irq:
+ tw686x_unregister(chip);
+fail_unreg:
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+ v4l2_device_unregister(&chip->v4l2_dev);
+fail_free:
+#endif
+ kfree(chip);
+ return err;
+}
+
+static void __devexit tw686x_finichip(struct pci_dev *pci_dev)
+{
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct tw686x_chip *chip = to_tw686x(v4l2_dev);
+#else
+ struct tw686x_chip *chip = pci_get_drvdata(pci_dev);
+#endif
+
+ tw686x_shutdown(chip);
+
+ pci_disable_device(pci_dev);
+#if(LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,30))
+ pci_set_drvdata(pci_dev, NULL);
+#endif
+
+ /* unregister stuff */
+ free_irq(pci_dev->irq, chip);
+
+ mutex_lock(&tw686x_chiplist_lock);
+ list_del(&chip->chiplist);
+ mutex_unlock(&tw686x_chiplist_lock);
+
+ tw686x_unregister(chip);
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+ v4l2_device_unregister(v4l2_dev);
+#endif
+ kfree(chip);
+}
+
+static struct pci_device_id tw686x_pci_tbl[] = {
+ {
+ /* TW6869 */
+ .vendor = 0x1797,
+ .device = 0x6869,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ }, {
+ /* --- end of list --- */
+ }
+};
+MODULE_DEVICE_TABLE(pci, tw686x_pci_tbl);
+
+static struct pci_driver tw686x_pci_driver = {
+ .name = "tw6869",
+ .id_table = tw686x_pci_tbl,
+ .probe = tw686x_initchip,
+ .remove = __devexit_p(tw686x_finichip),
+ /* TODO */
+ .suspend = NULL,
+ .resume = NULL,
+};
+
+static int tw686x_init(void)
+{
+ printk(KERN_INFO "tw6869 driver version %d.%d.%d loaded\n",
+ (TW686X_VERSION_CODE >> 16) & 0xff,
+ (TW686X_VERSION_CODE >> 8) & 0xff,
+ TW686X_VERSION_CODE & 0xff);
+ printk(KERN_INFO "tw6869 driver dma_mode=%x, debug=%x\n", tw686x_dmamode, tw686x_debug);
+
+ return pci_register_driver(&tw686x_pci_driver);
+}
+
+static void tw686x_fini(void)
+{
+ pci_unregister_driver(&tw686x_pci_driver);
+
+ printk(KERN_INFO "tw6869 driver version %d.%d.%d unloaded\n",
+ (TW686X_VERSION_CODE >> 16) & 0xff,
+ (TW686X_VERSION_CODE >> 8) & 0xff,
+ TW686X_VERSION_CODE & 0xff);
+}
+
+module_init(tw686x_init);
+module_exit(tw686x_fini);
diff --git a/drivers/pci/pcie/tw6869/tw686x-device.c b/drivers/pci/pcie/tw6869/tw686x-device.c
new file mode 100644
index 000000000000..5c2c064c86df
--- /dev/null
+++ b/drivers/pci/pcie/tw6869/tw686x-device.c
@@ -0,0 +1,1172 @@
+/*
+ * Driver for Techwell TW6864/68 based DVR cards
+ *
+ * (c) 2009-10 liran <jlee@techwellinc.com.cn> [Techwell China]
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <asm/div64.h>
+
+#include "tw686x.h"
+#include "tw686x-reg.h"
+#include "tw686x-device.h"
+
+int tw686x_dev_init( struct tw686x_chip *chip )
+{
+ u32 tmp;
+ int i = 0;
+ u32 page_table_p[TW6864_MAX_NUM_SG_DMA] = {
+ TW6869_R_DMA_PAGE_TABLE0, TW6869_R_DMA_PAGE_TABLE2, TW6869_R_DMA_PAGE_TABLE4, TW6869_R_DMA_PAGE_TABLE6,
+ TW6869_R_DMA_PAGE_TABLE8, TW6869_R_DMA_PAGE_TABLEA, TW6869_R_DMA_PAGE_TABLEC, TW6869_R_DMA_PAGE_TABLEE
+ };
+ u32 page_table_b[TW6864_MAX_NUM_SG_DMA] = {
+ TW6869_R_DMA_PAGE_TABLE1, TW6869_R_DMA_PAGE_TABLE3, TW6869_R_DMA_PAGE_TABLE5, TW6869_R_DMA_PAGE_TABLE7,
+ TW6869_R_DMA_PAGE_TABLE9, TW6869_R_DMA_PAGE_TABLEB, TW6869_R_DMA_PAGE_TABLED, TW6869_R_DMA_PAGE_TABLEF
+ };
+
+ dprintk(DPRT_LEVEL0, chip, "%s()\n", __func__);
+
+ if( pci_read_config_dword( chip->pci, 0x04, &tmp) == 0) {
+ tmp |= 7;
+ pci_write_config_dword( chip->pci, 0x04, tmp);
+ }
+
+ if( pci_read_config_dword( chip->pci, 0x78, &tmp) == 0) {
+ tmp = (0x8<<5) | (tmp & 0xFFFFFE1F); //8=128B, 9=256B, A=512B, B=1024B
+ pci_write_config_dword( chip->pci, 0x78, tmp);
+ }
+
+ //Software Reset
+ tw_write(TW6864_R_SYS_SOFT_RST, 0x01);
+ tw_write(TW6864_R_SYS_SOFT_RST, 0x0F);
+
+ for(i=0;i<TW6864_MAX_NUM_SG_DMA;i++) {
+ //S&G, Common Buffer DMA memory setup
+ tw_write(page_table_p[i], 0x00000000);
+ tw_write(page_table_b[i], 0x00000000);
+ }
+
+ for(tmp=0; tmp<TW6864_MAX_NUM_DATA_DMA; tmp++) {
+ tw_write(TW6864_R_CH8to15_CONFIG_REG_P(tmp),0);
+ tw_write(TW6864_R_CH8to15_CONFIG_REG_B(tmp),0);
+ }
+
+ if( pci_read_config_dword(chip->pci, 0x04, &tmp) == 0) {
+ dprintk(DPRT_LEVEL0, chip, "enable INTx\n");
+ tmp &= 0xFFFFFBFF; // Enable INTx
+ pci_write_config_dword(chip->pci, 0x04, tmp);
+ }
+
+ if( pci_read_config_dword(chip->pci, 0x50, &tmp) == 0) { //CFG_MSI_CAP = 0x50
+ tmp &= 0xFFFEFFFF; //Disable MSI
+ pci_write_config_dword( chip->pci, 0x50, tmp );
+ }
+
+ if( pci_read_config_dword(chip->pci, 0xAC, &tmp) == 0) { //CFG_MSIX_CAP = 0xAC
+ tmp &= 0x7FFFFFFF; //Disable MSI-X
+ pci_write_config_dword(chip->pci, 0xAC, tmp);
+ }
+
+ tw_write(TW6864_R_AVSRST,0x3F); //Reset Internal video decoders
+ tw_write(TW6864_R_DMA_CMD, 0); //Reset all DMA channels
+ tw_write(TW6864_R_DMA_CHANNEL_ENABLE, 0); //Disable all DMA channels
+ tw_write(TW6864_R_DMA_INT_REF, 0x4c4b6/2);//0x4c4b6); //Set INT interval for 2.0ms CPU占用率100%时,这个值设大了可能会造成DMA timeout
+
+ tmp = 0x00FF0004; //(TW6864_MAXSWITCH>1) ? 0x00000004 : 0x00FF0004;
+ tw_write(TW6864_R_DMA_CONFIG, tmp); //enable vsync reset dma_operator fifo,INTx, MSI disabled,
+ //little endian, bad/vsync protection on
+
+ tw_write(TW6864_R_DMA_CHANNEL_TIMEOUT, 0x3FFFFFFF);//0x14FF0FF0);//0x140c8b08);
+
+ tmp = 0x15DC; //512*(2*125)/(27*720/858)
+ tw_write(TW6864_R_PHASE_REF_CONFIG, tmp);
+
+ tmp = 1;
+ tmp |= TW6864_R_VIDEO_CTRL1_STANDARDALL; //pal, 4 D1 TDM format, 108MHz
+ tw_write(TW6864_R_VIDEO_CTRL1, tmp);
+
+ //set gpio mode
+ tmp = tw_read(TW6869_R_PIN_CFG_CTRL);
+ tmp&= ~0x03;
+ tmp|= 0x02;
+ tw_write(TW6869_R_PIN_CFG_CTRL,tmp);
+
+ tmp = 0x40a0;
+ tw_write(TW6864_R_CSR_REG,tmp);
+
+ tmp = TW6864_R_VIDEO_CTRL2_GENRSTALL | (TW6864_VIDEO_GEN_PATTERNS <<8) | TW6864_R_VIDEO_CTRL2_GENALL;
+ tw_write(TW6864_R_VIDEO_CTRL2, tmp);
+
+ tmp = (TW6864_AUDIO_DMA_LENGTH << 19) |
+ (TW6864_AUDIO_SAMP_RATE_INT(TW6864_AUDIO_SAMPLE_RATE)<<5) |
+ (TW6864_AUDIO_GEN_PATTERN << 4) |
+ (TW6864_AUDIO_GEN_MIX_SEL <<1 ) |
+ TW6864_AUDIO_GEN;
+ tw_write(TW6864_R_AUDIO_CTRL1, tmp);
+
+ tmp = (u32)TW6864_AUDIO_SAMP_RATE_EXT(TW6864_AUDIO_SAMPLE_RATE);
+ tw_write(TW6864_R_AUDIO_CTRL2, tmp);
+
+ tmp = 0x40;
+ if(TW6864_AUDIO_SAMPLE_BIT == 8) {
+ tmp |= BIT(8);
+ }
+ tw_write(TW6864_R_AUDIO_CTRL3, tmp);
+
+ //Show Blue background if no signal
+ tw_write(TW6864_R_MISC_CTRLII1, 0xE7);
+
+ tmp = 0x40;
+ tw_write(TW6864_R_AUDIO_GAIN_0, tmp);
+ tw_write(TW6864_R_AUDIO_GAIN_1, tmp);
+ tw_write(TW6864_R_AUDIO_GAIN_2, tmp);
+ tw_write(TW6864_R_AUDIO_GAIN_3, tmp);
+ tw_write(TW6869_R_AUDIO_GAIN_4, tmp);
+ tw_write(TW6869_R_AUDIO_GAIN_5, tmp);
+ tw_write(TW6869_R_AUDIO_GAIN_6, tmp);
+ tw_write(TW6869_R_AUDIO_GAIN_7, tmp);
+
+ tw_write(TW6864_R_VERTICAL_CTRL,0x00); //0x26 will cause ch0 and ch1 have dma_error.
+ tw_write(TW6864_R_LOOP_CTRL, 0xa5);
+ tw_write(TW6864_R_GPIO_REG, 0xFFFF0000);
+ tw_write(TW6864_R_HFLT1, 0x00);//0xBB); //NOTE: 这个会影响CIF的清晰度,针对CIF和4CIF需分别设置
+ tw_write(TW6864_R_HFLT2, 0x00);//0xBB);
+
+ return 0;
+}
+
+void tw686x_dev_uninit( struct tw686x_chip *chip )
+{
+ int i = 0;
+ u32 page_table_p[TW6864_MAX_NUM_SG_DMA] = {
+ TW6869_R_DMA_PAGE_TABLE0, TW6869_R_DMA_PAGE_TABLE2, TW6869_R_DMA_PAGE_TABLE4, TW6869_R_DMA_PAGE_TABLE6,
+ TW6869_R_DMA_PAGE_TABLE8, TW6869_R_DMA_PAGE_TABLEA, TW6869_R_DMA_PAGE_TABLEC, TW6869_R_DMA_PAGE_TABLEE
+ };
+ u32 page_table_b[TW6864_MAX_NUM_SG_DMA] = {
+ TW6869_R_DMA_PAGE_TABLE1, TW6869_R_DMA_PAGE_TABLE3, TW6869_R_DMA_PAGE_TABLE5, TW6869_R_DMA_PAGE_TABLE7,
+ TW6869_R_DMA_PAGE_TABLE9, TW6869_R_DMA_PAGE_TABLEB, TW6869_R_DMA_PAGE_TABLED, TW6869_R_DMA_PAGE_TABLEF
+ };
+
+ dprintk(DPRT_LEVEL0, chip, "%s()\n", __func__);
+
+ tw_write(TW6864_R_DMA_CMD, 0); //Reset all DMA channels
+ tw_write(TW6864_R_DMA_CHANNEL_ENABLE, 0); //Disable all DMA channels
+
+ for(i=0; i<TW6864_MAX_NUM_SG_DMA; i++) {
+ tw_write(TW6864_R_VC_CTRL_REG(i), 0x00000000);
+ tw_write(page_table_p[i], 0x00000000);
+ tw_write(page_table_b[i], 0x00000000);
+ }
+}
+
+void tw686x_dev_setdma( struct tw686x_chip *chip, u32 dma_cmd)
+{
+ u32 dma_en = dma_cmd;
+
+ if(dma_cmd != 0) {
+ dma_en |= (1<<31);
+ }
+
+ tw_write(TW6864_R_DMA_CHANNEL_ENABLE, dma_en);
+ //tw_write(TW6864_R_DMA_CMD, dma_cmd);
+}
+
+int tw686x_dev_set_mux(struct tw686x_vdev *dev, unsigned int input)
+{
+ struct tw686x_chip *chip = dev->chip;
+ int ch = dev->channel_id;
+ u32 uConfig = tw_read(TW6864_R_CH0to7_CONFIG_REG(ch));
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(%d)\n", __func__, (int)input);
+
+ uConfig &= ~(3 << 30);
+ uConfig |= (input&3) << 30;
+ tw_write(TW6864_R_CH0to7_CONFIG_REG(ch), uConfig);
+
+ return 0;
+}
+
+void tw686x_dev_set_standard(struct tw686x_vdev *dev, v4l2_std_id value, bool b_auto)
+{
+ struct tw686x_chip *chip = dev->chip;
+ int ch = dev->channel_id;
+ u32 tmp = tw_read(TW6864_R_VIDEO_CTRL1);
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ tmp &= ~TW6864_R_VIDEO_CTRL1_STANDARD(ch);
+ if(IS_PAL(value)) {
+ tmp |= TW6864_R_VIDEO_CTRL1_STANDARD(ch); //pal, 4 D1 TDM format, 108MHz
+ }
+
+ tw_write(TW6864_R_VIDEO_CTRL1, tmp);
+
+ if(ch < 4) {
+ if(b_auto) {
+ tmp = 0x07;
+ }
+ else {
+ tmp = tw_read(TW6864_R_CHANNEL(TW6864_R_STANDARD_0, ch));
+ tmp&= 0xF8;
+ tmp|= IS_PAL(value) ? 1 : 0;
+ }
+ tw_write(TW6864_R_CHANNEL(TW6864_R_STANDARD_0, ch), tmp);
+ }
+ else {
+ ch -= 4;
+ if(b_auto) {
+ tmp = 0x07;
+ }
+ else {
+ tmp = tw_read(TW6864_R_CHANNEL(TW6869_R_STANDARD_4, ch));
+ tmp&= 0xF8;
+ tmp|= IS_PAL(value) ? 1 : 0;
+ }
+ tw_write(TW6864_R_CHANNEL(TW6869_R_STANDARD_4, ch), tmp);
+ }
+}
+
+static u32 tw686x_dev_map_pixelformat(u32 fourcc)
+{
+ switch(fourcc) {
+ case V4L2_PIX_FMT_YUYV :
+ fourcc = (TW6864_VIDEO_FORMAT_YUYV & 7);
+ break;
+ case V4L2_PIX_FMT_UYVY :
+ fourcc = (TW6864_VIDEO_FORMAT_UYVY & 7);
+ break;
+ case V4L2_PIX_FMT_YVU420 :
+ fourcc = (TW6864_VIDEO_FORMAT_YUV420 & 7);
+ break;
+ case V4L2_PIX_FMT_YUV411P :
+ fourcc = (TW6864_VIDEO_FORMAT_Y411 & 7);
+ break;
+ case V4L2_PIX_FMT_Y41P :
+ fourcc = (TW6864_VIDEO_FORMAT_Y41P & 7);
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ fourcc = (TW6864_VIDEO_FORMAT_RGB565 & 7);
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ fourcc = (TW6864_VIDEO_FORMAT_RGB555 & 7);
+ break;
+ default:
+ fourcc = (TW6864_VIDEO_FORMAT_YUYV & 7);
+ }
+
+ return fourcc;
+}
+
+void tw686x_dev_set_pixelformat(struct tw686x_vdev *dev, u32 fourcc)
+{
+ struct tw686x_chip *chip = dev->chip;
+ int ch = dev->channel_id;
+ u32 uConfig = tw_read(TW6864_R_CH0to7_CONFIG_REG(ch));
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ uConfig &= ~(7 << 20);
+ uConfig |= tw686x_dev_map_pixelformat(fourcc) << 20;
+ tw_write(TW6864_R_CH0to7_CONFIG_REG(ch), uConfig);
+}
+
+void tw686x_dev_set_size(struct tw686x_vdev *dev, int width, int height)
+{
+ struct tw686x_chip *chip = dev->chip;
+ int ch = dev->channel_id;
+ u32 tmp, tmp1, tmp2;
+ int maxWidth = TW686X_REMOVE_BLACKSTRIPE ? 720 : 704;
+ bool isPal = IS_PAL(dev->tvnorm);
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(%d, %d)\n", __func__, width, height);
+
+ //SH_Scaler
+ //setup for Black stripe remover
+ tmp1 = width-12; //EndPos
+ tmp2 = 4; //StartPos
+ tmp = (tmp1 - tmp2)*(1<<16)/width;
+ tmp = (tmp2 & 0x1F) | ((tmp1 & 0x3FF) << 5) | (tmp <<15);
+ tmp |= TW686X_REMOVE_BLACKSTRIPE;
+ tw_write(TW6864_R_SHSCALER_REG(ch), tmp);
+
+ tmp = (u32)TW686X_VIDEO_SIZE(width,height);
+ tmp1 = tmp2 = tmp;
+ tw_write(TW6864_R_VIDEO_SIZE_REGA, tmp);
+ tw_write(TW6864_R_VIDEO_SIZE_REG(ch), tmp); //for Rev.B or later only
+
+ //H/V Scale
+ tmp1 &= 0x7FF;
+ tmp2 = (tmp2>>16)&0x1FF; //V
+ tmp1 = (maxWidth*256)/tmp1; //H
+ tmp2 = ((isPal?288:240)*256)/tmp2;
+ tmp = tmp1 & 0xFF;
+
+ if(ch < 4) {
+ tw_write(TW6864_R_CHANNEL(TW6864_R_HSCALE_LO_0,ch), tmp);
+
+ tmp = tmp2 & 0xFF;
+ tw_write(TW6864_R_CHANNEL(TW6864_R_VSCALE_LO_0,ch), tmp);
+
+ tmp = (((tmp2 >> 8)& 0xF) << 4) | ( (tmp1>>8) & 0xF );
+ tw_write(TW6864_R_CHANNEL(TW6864_R_VHSCALE_HI_0,ch), tmp);
+
+ if(tmp1 > 1) {
+ if(width==352) { tmp = 0x01; }
+ else if(width==176) { tmp = 0x02; }
+ else { tmp = 0x00; }
+ }
+ else { tmp = 0x0B; }
+ tmp1 = tw_read(TW6864_R_HFLT1 + (ch/2)*4);
+ tmp1&= ~(0x0F << ((ch%2)*4));
+ tmp1|= ((tmp&0x0F) << ((ch%2)*4));
+ tw_write(TW6864_R_HFLT1 + (ch/2)*4, tmp1);
+
+ if(isPal) {
+ tw_write(TW6864_R_CHANNEL(TW6864_R_VDELAY_0,ch), 0x17);
+ tw_write(TW6864_R_CHANNEL(TW6864_R_HDELAY_0,ch), TW686X_REMOVE_BLACKSTRIPE?0x0A:0x00E);
+ }
+ else {
+ tw_write(TW6864_R_CHANNEL(TW6864_R_VDELAY_0,ch), 0x14);
+ tw_write(TW6864_R_CHANNEL(TW6864_R_HDELAY_0,ch), 0x0E);//TW6864_REMOVE_BLACKSTRIPE?0x0A:0x00C);
+ }
+ tw_write(TW6864_R_CHANNEL(TW6864_R_F2_CNT_0,ch), 0x00);
+ }
+ else {
+ ch -= 4;
+ tw_write(TW6864_R_CHANNEL(TW6869_R_HSCALE_LO_4,ch),tmp);
+
+ tmp = tmp2 & 0xFF;
+ tw_write(TW6864_R_CHANNEL(TW6869_R_VSCALE_LO_4,ch),tmp);
+
+ tmp = (((tmp2 >> 8)& 0xF) << 4) | ( (tmp1>>8) & 0xF );
+ tw_write(TW6864_R_CHANNEL(TW6869_R_VHSCALE_HI_4,ch),tmp);
+
+ if(tmp1 > 1) {
+ if(width==352) { tmp = 0x01; }
+ else if(width==176) { tmp = 0x02; }
+ else { tmp = 0x00; }
+ }
+ else { tmp = 0x0B; }
+ tmp1 = tw_read(TW6869_R_HFLT1 + (ch/2)*4);
+ tmp1&= ~(0x0F << ((ch%2)*4));
+ tmp1|= ((tmp&0x0F) << ((ch%2)*4));
+ tw_write(TW6869_R_HFLT1 + (ch/2)*4, tmp1);
+
+ if(isPal) {
+ tw_write(TW6864_R_CHANNEL(TW6869_R_VDELAY_4,ch), 0x17);
+ tw_write(TW6864_R_CHANNEL(TW6869_R_HDELAY_4,ch), TW686X_REMOVE_BLACKSTRIPE?0x0A:0x0E);
+ }
+ else {
+ tw_write(TW6864_R_CHANNEL(TW6869_R_VDELAY_4,ch), 0x14);
+ tw_write(TW6864_R_CHANNEL(TW6869_R_HDELAY_4,ch), 0x0E);//TW6864_REMOVE_BLACKSTRIPE?0x0A:0x00C);
+ }
+ tw_write(TW6864_R_CHANNEL(TW6869_R_F2_CNT_4,ch), 0x00);
+ }
+}
+
+void tw686x_dev_set_size2(struct tw686x_vdev *dev, int width, int height)
+{
+ struct tw686x_chip *chip = dev->chip;
+ int ch = dev->channel_id;
+ u32 tmp, tmp1, tmp2;
+ int maxWidth = TW686X_REMOVE_BLACKSTRIPE ? 720 : 704;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(%d, %d)\n", __func__, width, height);
+
+ tmp = (u32)TW686X_VIDEO_SIZE_F2(width,height);
+ tmp1 = tmp2 = tmp;
+ tw_write(TW6864_R_VIDEO_SIZE_REG_F2(ch), tmp); //for Rev.B or later only
+
+ //H/V Scale
+ tmp1&= 0x7FF;
+ tmp2 = (tmp2>>16)&0x1FF; //V
+ tmp1 = (maxWidth*256)/tmp1; //H
+ tmp2 = ((IS_PAL(dev->tvnorm)?288:240)*256)/tmp2;
+ tmp = tmp1 & 0xFF;
+
+ if(ch < 4) {
+ tw_write(TW6864_R_CHANNEL(TW6864_R_HSCALE_LO_F2_0,ch), tmp);
+
+ tmp = tmp2 & 0xFF;
+ tw_write(TW6864_R_CHANNEL(TW6864_R_VSCALE_LO_F2_0,ch), tmp);
+
+ tmp = (((tmp2 >> 8)& 0xF) << 4) | ( (tmp1>>8) & 0xF );
+ tw_write(TW6864_R_CHANNEL(TW6864_R_VHSCALE_HI_F2_0,ch), tmp);
+
+ if(IS_PAL(dev->tvnorm)) {
+ tw_write(TW6864_R_CHANNEL(TW6864_R_VDELAY_F2_0,ch), 0x17);
+ tw_write(TW6864_R_CHANNEL(TW6864_R_HDELAY_F2_0,ch), TW686X_REMOVE_BLACKSTRIPE?0x0A:0x00E);
+ }
+ else {
+ tw_write(TW6864_R_CHANNEL(TW6864_R_VDELAY_F2_0,ch), 0x14);
+ tw_write(TW6864_R_CHANNEL(TW6864_R_HDELAY_F2_0,ch), 0x0E);//TW686X_REMOVE_BLACKSTRIPE?0x0A:0x00C);
+ }
+
+ tmp = tw_read(TW6864_R_CHANNEL(TW6864_R_CROPPING_0,ch));
+ tw_write(TW6864_R_CHANNEL(TW6864_R_CROPPING_F2_0,ch), tmp);
+
+ tmp = tw_read(TW6864_R_CHANNEL(TW6864_R_HACTIVE_0,ch));
+ tw_write(TW6864_R_CHANNEL(TW6864_R_HACTIVE_F2_0,ch), tmp);
+
+ tmp = tw_read(TW6864_R_CHANNEL(TW6864_R_VACTIVE_0,ch));
+ tw_write(TW6864_R_CHANNEL(TW6864_R_VACTIVE_F2_0,ch), tmp);
+
+ tw_write(TW6864_R_CHANNEL(TW6864_R_F2_CNT_0,ch), 0x1);
+ }
+ else
+ {
+ ch -= 4;
+ tw_write(TW6864_R_CHANNEL(TW6869_R_HSCALE_LO_F2_4,ch), tmp);
+
+ tmp = tmp2 & 0xFF;
+ tw_write(TW6864_R_CHANNEL(TW6869_R_VSCALE_LO_F2_4,ch), tmp);
+
+ tmp = (((tmp2 >> 8)& 0xF) << 4) | ( (tmp1>>8) & 0xF );
+ tw_write(TW6864_R_CHANNEL(TW6869_R_VHSCALE_HI_F2_4,ch), tmp);
+
+ if(IS_PAL(dev->tvnorm)) {
+ tw_write(TW6864_R_CHANNEL(TW6869_R_VDELAY_F2_4,ch), 0x17);
+ tw_write(TW6864_R_CHANNEL(TW6869_R_HDELAY_F2_4,ch), TW686X_REMOVE_BLACKSTRIPE?0x0A:0x00E);
+ }
+ else {
+ tw_write(TW6864_R_CHANNEL(TW6869_R_VDELAY_F2_4,ch), 0x14);
+ tw_write(TW6864_R_CHANNEL(TW6869_R_HDELAY_F2_4,ch), 0x0E);//TW686X_REMOVE_BLACKSTRIPE?0x0A:0x00C);
+ }
+
+ tmp = tw_read(TW6864_R_CHANNEL(TW6869_R_CROPPING_4,ch));
+ tw_write(TW6864_R_CHANNEL(TW6869_R_CROPPING_F2_4,ch), tmp);
+
+ tmp = tw_read(TW6864_R_CHANNEL(TW6869_R_HACTIVE_4,ch));
+ tw_write(TW6864_R_CHANNEL(TW6869_R_HACTIVE_F2_4,ch), tmp);
+
+ tmp = tw_read(TW6864_R_CHANNEL(TW6869_R_VACTIVE_4,ch));
+ tw_write(TW6864_R_CHANNEL(TW6869_R_VACTIVE_F2_4,ch), tmp);
+
+ tw_write(TW6864_R_CHANNEL(TW6869_R_F2_CNT_4,ch), 0x1);
+ }
+
+ tmp = tw_read(TW6864_R_VIDEO_SIZE_REG(ch));
+ tmp |= (1 << 30);
+ tw_write(TW6864_R_VIDEO_SIZE_REG(ch), tmp);
+}
+
+void tw686x_dev_set_vdelay(struct tw686x_vdev *dev, int value)
+{
+ struct tw686x_chip *chip = dev->chip;
+ int ch = dev->channel_id;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ if(ch < 4) {
+ tw_write(TW6864_R_CHANNEL(TW6864_R_VDELAY_0,ch), (u32)value);
+ tw_write(TW6864_R_CHANNEL(TW6864_R_VDELAY_F2_0,ch), (u32)value);
+ }
+ else {
+ ch -= 4;
+ tw_write(TW6864_R_CHANNEL(TW6869_R_VDELAY_4,ch), (u32)value);
+ tw_write(TW6864_R_CHANNEL(TW6869_R_VDELAY_F2_4,ch), (u32)value);
+ }
+}
+
+void tw686x_dev_set_bright(struct tw686x_vdev *dev, int val)
+{
+ struct tw686x_chip *chip = dev->chip;
+ int ch = dev->channel_id;
+
+ //if(1) { val = 0x00; }
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ if(ch<4) {
+ tw_write(TW6864_R_BRIGHT(ch), val);
+ }
+ else {
+ ch -= 4;
+ tw_write(TW6864_R_CHANNEL(TW6869_R_BRIGHT_4,ch), val);
+ }
+}
+
+void tw686x_dev_set_contrast(struct tw686x_vdev *dev, int val)
+{
+ struct tw686x_chip *chip = dev->chip;
+ int ch = dev->channel_id;
+
+ //if(1) { val = 0x64; }
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ if(ch<4) {
+ tw_write(TW6864_R_CONTRAST(ch), val);
+ }
+ else {
+ ch -= 4;
+ tw_write(TW6864_R_CHANNEL(TW6869_R_CONTRAST_4,ch), val);
+ }
+}
+
+void tw686x_dev_set_hue(struct tw686x_vdev *dev, int val)
+{
+ struct tw686x_chip *chip = dev->chip;
+ int ch = dev->channel_id;
+
+ //if(1) { val = 0; }
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ if(ch<4) {
+ tw_write(TW6864_R_HUE(ch), val);
+ }
+ else {
+ ch -= 4;
+ tw_write(TW6864_R_CHANNEL(TW6869_R_HUE_4,ch), val);
+ }
+}
+
+void tw686x_dev_set_saturation(struct tw686x_vdev *dev, int val)
+{
+ struct tw686x_chip *chip = dev->chip;
+ int ch = dev->channel_id;
+
+ //if(1) { val = 0x80; }
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ if(ch<4) {
+ tw_write(TW6864_R_SAT_U(ch), val);
+ tw_write(TW6864_R_SAT_V(ch), val);
+ }
+ else {
+ ch -= 4;
+ tw_write(TW6864_R_CHANNEL(TW6869_R_SAT_U_4,ch), val);
+ tw_write(TW6864_R_CHANNEL(TW6869_R_SAT_V_4,ch), val);
+ }
+}
+
+void tw686x_dev_set_sharpness(struct tw686x_vdev *dev, int val)
+{
+ struct tw686x_chip *chip = dev->chip;
+ int ch = dev->channel_id;
+
+ val &= 0x0F;
+ val |= 0x10;//0xA0;
+
+ //if(1) { val = 0x11; }
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ if(ch<4) {
+ tw_write(TW6864_R_SHARPNESS(ch), val);
+ }
+ else {
+ ch -= 4;
+ tw_write(TW6864_R_CHANNEL(TW6869_R_SHARPNESS_4,ch), val);
+ }
+}
+
+u32 tw686x_dev_get_decoderstatus(struct tw686x_vdev *dev)
+{
+ struct tw686x_chip *chip = dev->chip;
+ int ch = dev->channel_id;
+
+ if(ch < 4) {
+ return tw_read(TW6864_R_CHANNEL(TW6864_R_VSTATUS_0, ch));
+ }
+
+ return tw_read(TW6864_R_CHANNEL(TW6869_R_VSTATUS_4, ch-4));
+}
+
+int tw686x_dev_get_signal(struct tw686x_vdev *dev)
+{
+ u32 vstatus = tw686x_dev_get_decoderstatus( dev );
+
+ //dvprintk(DPRT_LEVEL0, dev, "%s() vstatus=%x\n", __func__, vstatus);
+
+ return (!(vstatus&BIT(7)) && (vstatus&BIT(6)));
+}
+
+u32 tw686x_dev_map_fieldrate(int nFieldRate, bool bNTSC)
+{
+ u32 uRate = 0;
+
+ if(!bNTSC) {
+ switch(nFieldRate) {
+ case 0:
+ uRate = 0;
+ break;
+ case 1:
+ case 2:
+ uRate = 0x00000001;
+ break;
+ case 3:
+ uRate = 0x00004001;//0x10001000;
+ break;
+ case 4:
+ case 5:
+ uRate = 0x00104001;//0x10010010;
+ break;
+ case 6:
+ case 7:
+ uRate = 0x00404041;//0x10101010;
+ break;
+ case 8:
+ case 9:
+ uRate = 0x01041041;//0x11011010;
+ break;
+ case 10:
+ uRate = 0x01104411;//0x11101110;
+ break;
+ case 11:
+ case 12:
+ uRate = 0x01111111;//0x11111110;
+ break;
+ case 13:
+ case 14:
+ uRate = 0x04444445;//0x11111111;
+ break;
+ case 15:
+ uRate = 0x04511445;//0x15111111;
+ break;
+ case 16:
+ case 17:
+ uRate = 0x05145145;//0x15111511;
+ break;
+ case 18:
+ case 19:
+ uRate = 0x05151515;//0x15151511;
+ break;
+ case 20:
+ uRate = 0x05515455;//0x15151515;
+ break;
+ case 21:
+ case 22:
+ uRate = 0x05551555;//0x15515515;
+ break;
+ case 23:
+ case 24:
+ uRate = 0x05555555;//0x15551555;
+ break;
+ default:
+ uRate = 0x15555555;
+ break;
+ }
+ }
+ else {
+ switch(nFieldRate) {
+ case 0:
+ uRate = 0;
+ break;
+ case 1:
+ case 2:
+ case 3:
+ uRate = 0x00000001;
+ break;
+ case 4:
+ case 5:
+ uRate = 0x00004001;//0x10001000;
+ break;
+ case 6:
+ case 7:
+ uRate = 0x00104001;//0x10010010;
+ break;
+ case 8:
+ case 9:
+ uRate = 0x00404041;//0x10101010;
+ break;
+ case 10:
+ case 11:
+ uRate = 0x01041041;//0x11011010;
+ break;
+ case 12:
+ case 13:
+ uRate = 0x01104411;//0x11101110;
+ break;
+ case 14:
+ case 15:
+ uRate = 0x01111111;//0x11111110;
+ break;
+ case 16:
+ case 17:
+ uRate = 0x04444445;//0x11111111;
+ break;
+ case 18:
+ case 19:
+ uRate = 0x04511445;//0x15111111;
+ break;
+ case 20:
+ case 21:
+ uRate = 0x05145145;//0x15111511;
+ break;
+ case 22:
+ case 23:
+ uRate = 0x05151515;//0x15151511;
+ break;
+ case 24:
+ case 25:
+ uRate = 0x05515455;//0x15151515;
+ break;
+ case 26:
+ case 27:
+ uRate = 0x05551555;//0x15551555;
+ break;
+ case 28:
+ case 29:
+ uRate = 0x05555555;//0x15551555;
+ break;
+ default:
+ uRate = 0x15555555;
+ break;
+ }
+ }
+
+ return uRate;
+}
+
+u32 tw686x_dev_map_framerate(int nFieldRate1, int nFieldRate2, bool bNTSC, bool b6864)
+{
+ u32 uRate = 0;
+
+ if(nFieldRate1 == 0) {
+ uRate = tw686x_dev_map_fieldrate(nFieldRate2, bNTSC);
+ }
+ else if(nFieldRate2 == 0) {
+ uRate = (tw686x_dev_map_fieldrate(nFieldRate1, bNTSC) << 1);
+ }
+ else {
+ if(b6864) {
+ uRate = bNTSC ? 30 : 25;
+ if(nFieldRate1>(int)uRate) nFieldRate1 = (int)uRate;
+ if(nFieldRate2>(int)uRate) nFieldRate2 = (int)uRate;
+ if(uRate==nFieldRate1) {
+ uRate = tw686x_dev_map_fieldrate(nFieldRate1, bNTSC) |
+ (tw686x_dev_map_fieldrate(nFieldRate2, bNTSC) << 1);
+ }
+ else {
+ uRate = (tw686x_dev_map_fieldrate(nFieldRate1, bNTSC)<<1) |
+ (tw686x_dev_map_fieldrate(nFieldRate2, bNTSC)<<2);
+ }
+ }
+ else {
+ uRate = tw686x_dev_map_fieldrate(nFieldRate1, bNTSC) |
+ (tw686x_dev_map_fieldrate(nFieldRate2, bNTSC) << 1);
+ }
+ }
+ if(uRate > 0) {
+ uRate |= 0x80000000;
+ }
+
+ return uRate;
+}
+
+void tw686x_dev_set_framerate(struct tw686x_vdev *dev, int val, int field)
+{
+ struct tw686x_chip *chip = dev->chip;
+ int ch = dev->channel_id;
+ u32 uRate = 0;
+
+ if(field == V4L2_FIELD_TOP) {
+ uRate = tw686x_dev_map_framerate(val, 0, !IS_PAL(dev->tvnorm), false);
+ }
+ else if(field == V4L2_FIELD_BOTTOM) {
+ uRate = tw686x_dev_map_framerate(0,val, !IS_PAL(dev->tvnorm), false);
+ }
+ else {
+ uRate = tw686x_dev_map_framerate(val, val, !IS_PAL(dev->tvnorm), true);
+ }
+
+ tw_write(TW6864_R_DROP_FIELD_REG(ch), uRate); //0x95555555 for even only, 0xd5555555 for odd only
+}
+
+//----------------------------------------------
+// 运行或者停止DMA program
+//----------------------------------------------
+void tw686x_dev_run(struct tw686x_vdev *dev, bool b_run)
+{
+ struct tw686x_chip *chip = dev->chip;
+ int ch = dev->channel_id;
+ u32 tmp = tw_read(TW6864_R_DMA_CMD);
+ u32 tmp1 = tw_read(TW6864_R_DMA_CHANNEL_ENABLE);
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(%d %d)\n", __func__, ch, b_run);
+
+ if(b_run) {
+ tmp |= (1<<31);
+ tmp |= (1<<ch);
+ tmp1|= (1<<ch);
+ }
+ else {
+ if(!(tmp&(1<<ch)) && !(tmp1&(1<<ch))) {
+ return;
+ }
+ tmp &= ~(1<<ch);
+ tmp1&= ~(1<<ch);
+ if( tmp1 == 0 ) {
+ tmp = 0;
+ }
+ }
+
+ tw_write(TW6864_R_DMA_CHANNEL_ENABLE, tmp1);
+ tw_write(TW6864_R_DMA_CMD, tmp);
+}
+
+void tw686x_dev_set_dma(struct tw686x_vdev *dev, int width, int height, int linewidth)
+{
+ struct tw686x_dmadesc * dma_desc;
+ struct tw686x_chip *chip = dev->chip;
+ int ch = dev->channel_id;
+ bool is4CIF = IS_PAL(dev->tvnorm) ? (height>288) : (height>240);
+ int nHeight = !is4CIF ? height : height/2;
+ u32 uTemp, uConfig;
+ int linepitch = linewidth;
+ int idx_begin = ch*TW686X_DMA_DESC_UNIT;
+ int idx_end = (ch+1)*TW686X_DMA_DESC_UNIT-1;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(%d) is4CIF=%d\n", __func__, ch, is4CIF);
+
+ /*
+ * 0 -- scatter gather, 2 -- frame block mode, 3 -- field block mode
+ */
+ uTemp = (!IS_TW686X_DMA_MODE_BLOCK) ? 0 : (!is4CIF ? 3 : 2);
+
+ uConfig = tw_read( TW6864_R_PHASE_REF_CONFIG );
+ uConfig &= ~(3<<(16+(ch<<1)));
+ uConfig |= (uTemp<<(16+(ch<<1)));
+ tw_write( TW6864_R_PHASE_REF_CONFIG, uConfig );
+
+ uConfig = tw_read(TW6864_R_CH0to7_CONFIG_REG(ch));
+ uConfig = (uConfig & ~0x1FF) | (idx_begin&0x1FF); //m_StartIdx
+ uConfig = (uConfig & ~(0x3FF<<10)) | ((idx_end&0x3FF)<<10); //m_EndIdx
+ uConfig = (uConfig & ~(1<<23)) | ((false&1)<<23); //m_bHorizontalDecimate
+ uConfig = (uConfig & ~(1<<24)) | ((false&1)<<24); //m_bVerticalDecimate
+ uConfig = (uConfig & ~(3<<25)) | ((0&3)<<25); //m_nDropChannelNum
+ uConfig = (uConfig & ~(1<<27)) | ((1&1)<<27); //m_bDropMasterOrSlave //((((ch&1)?FALSE:TRUE)&1)<<27)
+ uConfig = (uConfig & ~(1<<28)) | ((false&1)<<28); //m_bDropField //((bDropField&1)<<28)
+ uConfig = (uConfig & ~(1<<29)) | ((false&1)<<29); //m_bDropOddOrEven
+ //uConfig = (uConfig & ~(3<<30)) | ((0&3)<<30); //m_nCurVideoChannelNum -- mux
+
+ tw_write(TW6864_R_CH0to7_CONFIG_REG(ch), uConfig);
+ tw_write(TW6864_R_DROP_FIELD_REG(ch), 0); //0x95555555 for even only, 0xd5555555 for odd only
+
+ if( !IS_TW686X_DMA_MODE_BLOCK ) {
+#define DMA_STATUS_HOST_NOT_AVAIABLE 0
+ dma_desc = TW686X_DMA_DESC(dev, P);
+ for( uTemp=0; uTemp<TW686X_DMA_DESC_UNIT; uTemp++ ) {
+ dma_desc[uTemp].ctrl = (DMA_STATUS_HOST_NOT_AVAIABLE&3)<<30;
+ }
+ dma_desc = TW686X_DMA_DESC(dev, B);
+ for(uTemp=0; uTemp<TW686X_DMA_DESC_UNIT; uTemp++) {
+ dma_desc[uTemp].ctrl = (DMA_STATUS_HOST_NOT_AVAIABLE&3)<<30;
+ }
+ }
+ else {
+ tw_write( TW6864_R_BDMA(TW6864_R_BDMA_ADDR_P_0, ch), 0 );
+ tw_write( TW6864_R_BDMA(TW6864_R_BDMA_ADDR_B_0, ch), 0 );
+ tw_write( TW6864_R_BDMA(TW6864_R_BDMA_WHP_0, ch),
+ (linewidth&0x7FF) | //line width
+ ((linepitch&0x7FF)<<11) | //pitch
+ ((nHeight&0x7FF)<<22) //height
+ );
+ tw_write( TW6864_R_BDMA(TW6864_R_BDMA_ADDR_P_F2_0, ch), 0 );
+ tw_write( TW6864_R_BDMA(TW6864_R_BDMA_ADDR_B_F2_0, ch), 0 );
+ tw_write( TW6864_R_BDMA(TW6864_R_BDMA_WHP_F2_0, ch),
+ (linewidth&0x7FF) | //line width
+ ((linepitch&0x7FF)<<11) | //pitch
+ ((nHeight&0x7FF)<<22) //height
+ );
+ }
+
+ //field 2
+ if(!is4CIF) {
+ tw686x_dev_set_size(dev, width, nHeight);
+ tw686x_dev_set_size2(dev, width, nHeight);
+ }
+ else {
+ tw686x_dev_set_size2(dev, width, nHeight);
+ tw686x_dev_set_size(dev, width, nHeight);
+ }
+}
+
+int tw686x_dev_set_dmabuf(struct tw686x_vdev *dev, int nbuf, struct tw686x_buf *buf)
+{
+ struct tw686x_chip *chip = dev->chip;
+ int ch = dev->channel_id;
+ dma_addr_t dma_addr = buf ? videobuf_to_dma_contig_isl(&buf->vb) : 0;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(n_buf=%d, buf=%d 0x%08x)\n", __func__, nbuf, buf->vb.i, (u32)dma_addr);
+
+ if( V4L2_FIELD_HAS_BOTH(dev->field) ) {
+ switch( nbuf ) {
+ case 0:
+ tw_write( TW6864_R_BDMA(TW6864_R_BDMA_ADDR_P_0, ch), (u32)dma_addr );
+ break;
+ case 1:
+ tw_write( TW6864_R_BDMA(TW6864_R_BDMA_ADDR_B_0, ch), (u32)dma_addr );
+ break;
+ }
+ }
+ else {
+ switch( nbuf ) {
+ case 0:
+ tw_write( TW6864_R_BDMA(TW6864_R_BDMA_ADDR_P_0, ch), (u32)dma_addr );
+ break;
+ case 1:
+ tw_write( TW6864_R_BDMA(TW6864_R_BDMA_ADDR_P_F2_0, ch), (u32)dma_addr );
+ break;
+ case 2:
+ tw_write( TW6864_R_BDMA(TW6864_R_BDMA_ADDR_B_0, ch), (u32)dma_addr );
+ break;
+ case 3:
+ tw_write( TW6864_R_BDMA(TW6864_R_BDMA_ADDR_B_F2_0, ch), (u32)dma_addr );
+ break;
+ }
+ }
+
+ return 0;
+}
+
+#define TW686X_DISABLE_DESCRIPTOR(desc) \
+{ \
+ (desc)->ctrl &= 0x3FFFFFFF; \
+ (desc)->ctrl |= 0x20000000; \
+}
+
+#define TW686X_FILL_DESCRIPTOR(desc, phy_addr, byte_len, field_start, next_start, new_frame, check_point, status) \
+{ \
+ (desc)->addr = phy_addr; \
+ (desc)->ctrl = ((status & 0x3) << 30) | \
+ ((new_frame&1)<<29) | \
+ ((next_start & 0xFF) << 21) | \
+ ((field_start & 0x7F) << 14) | \
+ ((check_point&1)<<13) | \
+ (byte_len & 0x1FFF); \
+}
+
+#define DMA_STATUS_HOST_READY 1
+
+int tw686x_dev_set_dmadesc(struct tw686x_vdev *dev, int nbuf, struct tw686x_buf *buf)
+{
+ int fld_start = false;
+ int i, j, bytes_to;
+ int offset=0, n_desc=1;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+ struct tw686x_dmadesc *desc;
+ u32 phy_addr = 0;
+ int fld_num = V4L2_FIELD_HAS_BOTH(buf->vb.field) + 1;
+ int fld_cnt = 0;
+
+ n_desc += (dev->pb_next==0);
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(%d %d), %d %p %p %p, %x\n", __func__, nbuf, buf->vb.i,
+ n_desc, TW686X_DMA_DESC(dev, P), TW686X_DMA_DESC(dev, B), buf->sg, buf->sg_offset);
+
+ for( j=0; j<n_desc; j++ ) {
+ if((buf->sg==NULL) || (buf->sg_bytes_pb==buf->vb.size)) {
+ buf->sg = dma->sglist;
+ buf->sg_offset = 0;
+ buf->sg_bytes_to = buf->vb.size;
+ fld_start = true;
+ fld_cnt = 0;
+ offset= 0;
+ }
+ else {
+ offset = buf->sg_offset;
+ }
+ bytes_to= buf->sg_bytes_pb/fld_num;
+ desc = (nbuf==0) ? TW686X_DMA_DESC(dev, P) : TW686X_DMA_DESC(dev, B);
+ nbuf = (nbuf + 1) % dev->buf_needs;
+ for(i=0; i<TW686X_DMA_DESC_UNIT; i++) {
+ if(bytes_to == 0) {
+ TW686X_DISABLE_DESCRIPTOR(&desc[i]);
+ }
+ else {
+ while (offset && offset >= sg_dma_len(buf->sg)) {
+ offset -= sg_dma_len(buf->sg);
+ buf->sg++;
+ }
+ phy_addr = cpu_to_le32(sg_dma_address(buf->sg)+offset);
+ if(offset && (offset<TW686X_DMA_DESC_LEN)) {
+ offset = min(TW686X_DMA_DESC_LEN-offset, bytes_to);
+ TW686X_FILL_DESCRIPTOR(&desc[i], phy_addr, offset, 0,
+ 0, fld_start, 1, DMA_STATUS_HOST_READY);
+ bytes_to-= offset;
+ offset = TW686X_DMA_DESC_LEN;
+ //dvprintk(1, dev, "%s+(%x %x)\n", (nbuf==0)?"B":"P", desc[i].ctrl, phy_addr);
+ fld_start = false;
+ }
+ else if(bytes_to > TW686X_DMA_DESC_LEN) {
+ TW686X_FILL_DESCRIPTOR(&desc[i], phy_addr, TW686X_DMA_DESC_LEN, 0,
+ 0, fld_start, 1, DMA_STATUS_HOST_READY);
+ bytes_to-= TW686X_DMA_DESC_LEN;
+ offset += TW686X_DMA_DESC_LEN;
+ //dvprintk(1, dev, "%s (%x %x)\n", (nbuf==0)?"B":"P", desc[i].ctrl, phy_addr);
+ fld_start = false;
+ }
+ else {
+ TW686X_FILL_DESCRIPTOR(&desc[i], phy_addr, bytes_to, 0,
+ 0, fld_start, 1, DMA_STATUS_HOST_READY);
+ offset += bytes_to;
+ bytes_to = 0;
+ //dvprintk(1, dev, "%s-(%x %x)\n", (nbuf==0)?"B":"P", desc[i].ctrl, phy_addr);
+ //dvprintk(1, dev, "field %d/%d %s-(%x %x)\n", fld_cnt, fld_num, (nbuf==0)?"B":"P", desc[i].ctrl, phy_addr);
+
+ fld_cnt++;
+ if(fld_cnt < fld_num)
+ {
+ fld_start = true;
+ bytes_to = buf->sg_bytes_pb/fld_num;
+ }
+ }
+ }
+ }
+ buf->sg_offset = offset;
+ }
+
+ return 0;
+}
+
+//-------------------------------------------------------------------
+// 允许GPIO输出
+//-------------------------------------------------------------------
+bool tw686x_dev_enable_gpiooutput(struct tw686x_chip *chip, bool enable, int gpio, int bits)
+{
+ u32 mask= ((1 << bits) - 1) << (gpio+16);
+ u32 old = tw_read(TW6864_R_GPIO_REG);
+ tw_write(TW6864_R_GPIO_REG, enable ? (old&~mask) : (old|mask));
+
+ dprintk(DPRT_LEVEL0, chip, "%s()\n", __func__);
+
+ return true;
+}
+
+//-------------------------------------------------------------------
+// 输出GPIO电平
+//-------------------------------------------------------------------
+bool tw686x_dev_set_gpiobits(struct tw686x_chip *chip, int gpio, int bits, u32* value)
+{
+ u32 mask= ((1 << bits) - 1) << gpio;
+ u32 old = tw_read(TW6864_R_GPIO_REG) & ~mask;
+ tw_write(TW6864_R_GPIO_REG, old|((*value<<gpio) & 0xFFFF0000));
+
+ dprintk(DPRT_LEVEL0, chip, "%s()\n", __func__);
+
+ return true;
+}
+
+//-------------------------------------------------------------------
+// 获取GPIO电平
+//-------------------------------------------------------------------
+bool tw686x_dev_get_gpiobits(struct tw686x_chip *chip, int gpio, int bits, u32* pValue)
+{
+ *pValue = (((tw_read(TW6864_R_GPIO_REG) & 0xFFFF0000) & (((1 << bits) - 1) << gpio)) >> gpio);
+
+ dprintk(DPRT_LEVEL0, chip, "%s()\n", __func__);
+
+ return true;
+}
+
+int tw686x_dev_set_audio(struct tw686x_chip *chip, int samplerate, int bits, int channels, int blksize)
+{
+ u32 tmp1, tmp2, tmp3;
+
+ tmp1 = tw_read(TW6864_R_AUDIO_CTRL1);
+
+ tmp1 &= 0x0000001F;
+ tmp1 |= (125000000/samplerate) << 5;
+ tmp1 |= blksize << 19;
+
+ tw_write(TW6864_R_AUDIO_CTRL1, tmp1);
+
+ tmp2 = ((125000000 / samplerate) << 16) +
+ ((125000000 % samplerate) << 16)/samplerate;
+
+ tw_write(TW6864_R_AUDIO_CTRL2, tmp2);
+
+ tmp3 = tw_read(TW6864_R_AUDIO_CTRL3);
+ tmp3 &= ~BIT(8);
+ tmp3 |= (bits==8) ? BIT(8) : 0;
+ tw_write(TW6864_R_AUDIO_CTRL3, tmp3);
+
+ return 0;
+}
+
+int tw686x_dev_set_adma_buffer(struct tw686x_adev *dev, u32 buf_addr, int pb)
+{
+ struct tw686x_chip *chip = dev->chip;
+
+ if(pb == 0) {
+ tw_write(TW6864_R_CH8to15_CONFIG_REG_P(dev->channel_id), buf_addr);
+ }
+ else {
+ tw_write(TW6864_R_CH8to15_CONFIG_REG_B(dev->channel_id), buf_addr);
+ }
+
+ //daprintk(DPRT_LEVEL0, dev, "%s(%x %d)\n", __func__, buf_addr, pb);
+
+ return 0;
+}
+
+//----------------------------------------------
+// 运行或者停止audio DMA program
+//----------------------------------------------
+void tw686x_dev_run_audio(struct tw686x_adev *dev, bool b_run)
+{
+ struct tw686x_chip *chip = dev->chip;
+ int ch = dev->channel_id+TW686X_AUDIO_BEGIN;
+ u32 tmp = tw_read(TW6864_R_DMA_CMD);
+ u32 tmp1 = tw_read(TW6864_R_DMA_CHANNEL_ENABLE);
+
+ daprintk(DPRT_LEVEL0, dev, "%s(%d %d)\n", __func__, ch, b_run);
+
+ if(b_run) {
+ tmp |= (1<<31);
+ tmp |= (1<<ch);
+ tmp1|= (1<<ch);
+ }
+ else {
+ if(!(tmp&(1<<ch)) && !(tmp1&(1<<ch))) {
+ return;
+ }
+ tmp &= ~(1<<ch);
+ tmp1&= ~(1<<ch);
+ if( tmp1 == 0 ) {
+ tmp = 0;
+ }
+ }
+
+ tw_write(TW6864_R_DMA_CHANNEL_ENABLE, tmp1);
+ tw_write(TW6864_R_DMA_CMD, tmp);
+}
+
+//----------------------------------------------
+// Set dma p/b buffer desc
+//----------------------------------------------
+void tw686x_dev_set_pbdesc(struct tw686x_vdev *dev)
+{
+ struct tw686x_chip *chip = dev->chip;
+ u32 page_table_p[TW6864_MAX_NUM_SG_DMA] = {
+ TW6869_R_DMA_PAGE_TABLE0, TW6869_R_DMA_PAGE_TABLE2, TW6869_R_DMA_PAGE_TABLE4, TW6869_R_DMA_PAGE_TABLE6,
+ TW6869_R_DMA_PAGE_TABLE8, TW6869_R_DMA_PAGE_TABLEA, TW6869_R_DMA_PAGE_TABLEC, TW6869_R_DMA_PAGE_TABLEE
+ };
+ u32 page_table_b[TW6864_MAX_NUM_SG_DMA] = {
+ TW6869_R_DMA_PAGE_TABLE1, TW6869_R_DMA_PAGE_TABLE3, TW6869_R_DMA_PAGE_TABLE5, TW6869_R_DMA_PAGE_TABLE7,
+ TW6869_R_DMA_PAGE_TABLE9, TW6869_R_DMA_PAGE_TABLEB, TW6869_R_DMA_PAGE_TABLED, TW6869_R_DMA_PAGE_TABLEF
+ };
+
+ tw_write(page_table_p[dev->channel_id], (u32)dev->dma_desc[TW686X_DMA_DESC_P].dma);
+ tw_write(page_table_b[dev->channel_id], (u32)dev->dma_desc[TW686X_DMA_DESC_B].dma);
+}
diff --git a/drivers/pci/pcie/tw6869/tw686x-device.h b/drivers/pci/pcie/tw6869/tw686x-device.h
new file mode 100644
index 000000000000..202cd46c7d15
--- /dev/null
+++ b/drivers/pci/pcie/tw6869/tw686x-device.h
@@ -0,0 +1,56 @@
+/*
+ * Driver for Techwell TW6864/68 based DVR cards
+ *
+ * (c) 2009-10 liran <jlee@techwellinc.com.cn> [Techwell China]
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef TW686X_DEVICE_H_INCLUDED
+#define TW686X_DEVICE_H_INCLUDED
+
+int tw686x_dev_init( struct tw686x_chip *chip );
+int tw686x_dev_init_286x( struct tw686x_chip *chip );
+void tw686x_dev_uninit( struct tw686x_chip *chip );
+void tw686x_dev_setdma( struct tw686x_chip *chip, u32 dma_cmd);
+int tw686x_dev_set_mux( struct tw686x_vdev *dev, unsigned int input );
+void tw686x_dev_set_standard( struct tw686x_vdev *dev, v4l2_std_id value, bool b_auto );
+void tw686x_dev_set_pixelformat( struct tw686x_vdev *dev, u32 fourcc );
+void tw686x_dev_set_size(struct tw686x_vdev *dev, int width, int height);
+void tw686x_dev_set_size2(struct tw686x_vdev *dev, int width, int height);
+void tw686x_dev_set_vdelay(struct tw686x_vdev *dev, int value);
+void tw686x_dev_set_bright(struct tw686x_vdev *dev, int val);
+void tw686x_dev_set_contrast(struct tw686x_vdev *dev, int val);
+void tw686x_dev_set_hue(struct tw686x_vdev *dev, int val);
+void tw686x_dev_set_saturation(struct tw686x_vdev *dev, int val);
+void tw686x_dev_set_sharpness(struct tw686x_vdev *dev, int val);
+void tw686x_dev_set_framerate(struct tw686x_vdev *dev, int val, int field);
+u32 tw686x_dev_get_decoderstatus(struct tw686x_vdev *dev);
+int tw686x_dev_get_signal(struct tw686x_vdev *dev);
+void tw686x_dev_run(struct tw686x_vdev *dev, bool b_run);
+void tw686x_dev_set_dma(struct tw686x_vdev *dev, int width, int height, int linewidth);
+int tw686x_dev_set_dmabuf(struct tw686x_vdev *dev, int nbuf, struct tw686x_buf *buf);
+void tw686x_dev_set_pbdesc(struct tw686x_vdev *dev);
+int tw686x_dev_set_dmadesc(struct tw686x_vdev *dev, int nbuf, struct tw686x_buf *buf);
+
+bool tw686x_dev_enable_gpiooutput(struct tw686x_chip *chip, bool enable, int gpio, int bits);
+bool tw686x_dev_set_gpiobits(struct tw686x_chip *chip, int gpio, int bits, u32* value);
+bool tw686x_dev_get_gpiobits(struct tw686x_chip *chip, int gpio, int bits, u32* pValue);
+int tw686x_dev_set_audio(struct tw686x_chip *chip, int samplerate, int bits, int channels, int blksize);
+int tw686x_dev_set_adma_buffer(struct tw686x_adev *dev, u32 buf_addr, int pb);
+void tw686x_dev_run_audio(struct tw686x_adev *dev, bool b_run);
+
+#endif // TW686X_DEVICE_H_INCLUDED
diff --git a/drivers/pci/pcie/tw6869/tw686x-i2c.c b/drivers/pci/pcie/tw6869/tw686x-i2c.c
new file mode 100644
index 000000000000..9cc0cb300ecf
--- /dev/null
+++ b/drivers/pci/pcie/tw6869/tw686x-i2c.c
@@ -0,0 +1,213 @@
+/*
+ * Driver for Techwell TW6864/68 based DVR cards
+ *
+ * (c) 2009-10 liran <jlee@techwellinc.com.cn> [Techwell China]
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+
+#include "tw686x.h"
+#include "tw686x-reg.h"
+
+#define TW6864_I2C_SCLK_BIT BIT(0)
+#define TW6864_I2C_SDA_DATA_BIT BIT(1)
+#define TW6864_I2C_SCLK_CTRL_BIT BIT(16) //1:Input,0=Output
+#define TW6864_I2C_SDA_CTRL_BIT BIT(17) //1:Input,0=Output
+
+static bool tw686x_i2c_s_scl( struct i2c_sw *i2c, TLevel scl )
+{
+ struct tw686x_chip *chip = (struct tw686x_chip *)i2c->chip;
+ struct tw686x_i2c *i2c_686x = &chip->i2c_686x;
+
+ i2c_686x->control_word &= ~TW6864_I2C_SCLK_CTRL_BIT;
+ if(scl == LevelHi) {
+ i2c_686x->control_word |= TW6864_I2C_SCLK_BIT;
+ }
+ else {
+ i2c_686x->control_word &= ~TW6864_I2C_SCLK_BIT;
+ }
+
+ tw_write(TW6864_R_GPIO_REG, i2c_686x->control_word);
+
+ return i2csw_s_scl(&i2c_686x->i2c, scl);
+}
+
+static bool tw686x_i2c_s_sda( struct i2c_sw *i2c, TLevel sda )
+{
+ struct tw686x_chip *chip = (struct tw686x_chip *)i2c->chip;
+ struct tw686x_i2c *i2c_686x = &chip->i2c_686x;
+
+ i2c_686x->control_word &= ~TW6864_I2C_SDA_CTRL_BIT;
+ if(sda == LevelHi) {
+ i2c_686x->control_word |= TW6864_I2C_SDA_DATA_BIT;
+ }
+ else {
+ i2c_686x->control_word &= ~TW6864_I2C_SDA_DATA_BIT;
+ }
+
+ tw_write(TW6864_R_GPIO_REG, i2c_686x->control_word);
+
+ return i2csw_s_sda(&i2c_686x->i2c, sda);
+}
+
+static int tw686x_i2c_g_scl( struct i2c_sw * i2c )
+{
+ struct tw686x_chip *chip = (struct tw686x_chip *)i2c->chip;
+
+ return (tw_read(TW6864_R_GPIO_REG) & TW6864_I2C_SCLK_BIT);
+}
+
+static int tw686x_i2c_g_sda( struct i2c_sw * i2c )
+{
+ struct tw686x_chip *chip = (struct tw686x_chip *)i2c->chip;
+ struct tw686x_i2c *i2c_686x = &chip->i2c_686x;
+
+ i2c_686x->control_word |= TW6864_I2C_SDA_CTRL_BIT;
+ tw_write(TW6864_R_GPIO_REG, i2c_686x->control_word);
+
+ return ((tw_read(TW6864_R_GPIO_REG) & TW6864_I2C_SDA_DATA_BIT)>>1);
+}
+
+static int i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct tw686x_chip *chip = i2c_adap->algo_data;
+ struct i2c_sw *i2c = &chip->i2c_686x.i2c;
+ int i, retval = 0;
+
+ dprintk(1, chip, "%s(num = %d)\n", __func__, num);
+
+ for (i = 0 ; i < num; i++) {
+ dprintk(1, chip, "%s(num = %d) addr = 0x%02x len = 0x%x\n",
+ __func__, num, msgs[i].addr, msgs[i].len);
+ if (msgs[i].flags & I2C_M_RD) {
+ /* read */
+ retval = i2csw_read_bytes(i2c, (u8*)&msgs[i].addr, 1, msgs[i].buf, msgs[i].len);
+ } else {
+ /* write */
+ retval = i2csw_write_bytes(i2c, msgs[i].buf, msgs[i].len);
+ }
+ if (!retval) {
+ retval = -i2c->err_code;
+ goto err;
+ }
+ }
+ return num;
+
+ err:
+ return retval;
+}
+
+static u32 tw686x_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm tw686x_i2c_algo_template = {
+ .master_xfer = i2c_xfer,
+ .functionality = tw686x_functionality,
+};
+
+static struct i2c_adapter tw686x_i2c_adap_template = {
+ .name = "tw6869",
+ .owner = THIS_MODULE,
+/* .id = I2C_SW_TW686X, */
+ .algo = &tw686x_i2c_algo_template,
+};
+
+static struct i2c_client tw686x_client_template = {
+ .name = "tw6869 internal",
+};
+
+static struct i2c_sw tw686x_i2csw_template = {
+ .s_scl = tw686x_i2c_s_scl,
+ .s_sda = tw686x_i2c_s_sda,
+ .g_scl = tw686x_i2c_g_scl,
+ .g_sda = tw686x_i2c_g_sda,
+};
+
+static int tw686x_i2c_init( struct tw686x_chip *chip )
+{
+ struct tw686x_i2c *i2c_686x = &chip->i2c_686x;
+
+ i2c_686x->i2c = tw686x_i2csw_template;
+ i2c_686x->i2c.chip = chip;
+
+ i2c_686x->control_word = tw_read(TW6864_R_GPIO_REG);
+ i2c_686x->control_word &= ~TW6864_I2C_SCLK_CTRL_BIT;
+ i2c_686x->control_word &= ~TW6864_I2C_SDA_CTRL_BIT;
+ i2c_686x->control_word |= TW6864_I2C_SCLK_BIT;
+ i2c_686x->control_word |= TW6864_I2C_SDA_DATA_BIT;
+ tw_write(TW6864_R_GPIO_REG, i2c_686x->control_word);
+
+ i2csw_init( &i2c_686x->i2c, 400000);//2000000);
+
+ return 0;
+}
+
+/* init + register i2c algo-bit adapter */
+int tw686x_i2c_register(struct tw686x_chip *chip)
+{
+// u8 buf;
+// struct i2c_board_info info;
+// struct i2c_client *c;
+// const unsigned short addr_list[] = {
+// TW2864_R_I2C_ADDR0, I2C_CLIENT_END
+// };
+
+ dprintk(1, chip, "%s()\n", __func__);
+
+ chip->i2c_adap = tw686x_i2c_adap_template;
+ chip->i2c_adap.dev.parent = &chip->pci->dev;
+ strcpy(chip->i2c_adap.name, chip->name);
+ chip->i2c_adap.algo_data = chip;
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+ i2c_set_adapdata(&chip->i2c_adap, &chip->v4l2_dev);
+#else
+ i2c_set_adapdata(&chip->i2c_adap, chip);
+#endif
+ i2c_add_adapter(&chip->i2c_adap);
+
+ chip->i2c_client = tw686x_client_template;
+ chip->i2c_client.adapter = &chip->i2c_adap;
+ chip->i2c_client.addr = TW2864_R_I2C_ADDR0;
+
+ tw686x_i2c_init( chip );
+
+// if( i2c_master_recv(&chip->i2c_client, &buf, 0)<0 ) {
+// printk("%s: no tw286x found\n", chip->name);
+// }
+// else {
+// printk("%s: found tw286x\n", chip->name);
+// }
+//
+// memset(&info, 0, sizeof(struct i2c_board_info));
+// strlcpy(info.type, "tw286x", I2C_NAME_SIZE);
+// c = i2c_new_probed_device(&chip->i2c_adap, &info, addr_list);
+// dprintk(1, chip, "%s() client=%p\n", __func__, c);
+
+ return 0;
+}
+
+int tw686x_i2c_unregister(struct tw686x_chip* chip)
+{
+ i2c_del_adapter(&chip->i2c_adap);
+ return 0;
+}
diff --git a/drivers/pci/pcie/tw6869/tw686x-reg.h b/drivers/pci/pcie/tw6869/tw686x-reg.h
new file mode 100644
index 000000000000..57bacd1d4c91
--- /dev/null
+++ b/drivers/pci/pcie/tw6869/tw686x-reg.h
@@ -0,0 +1,593 @@
+/*
+ * Driver for Techwell TW6864/68 based DVR cards
+ *
+ * (c) 2009-10 liran <jlee@techwellinc.com.cn> [Techwell China]
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef TW686XREG_H_INCLUDED
+#define TW686XREG_H_INCLUDED
+
+//#define BIT(x) (1<<(x))
+
+//////////////////////////////////////////////////
+//NO NOT CHANGE
+// Register definitions
+// IMPORTANT: These defines are DWORD-based.
+#define TW6864_R_DMA_INT_STATUS 0x000 //0x00 //RO
+#define TW6864_R_DMA_INT_STATUS_ERR(ch) (BIT(ch)<<24)
+#define TW6864_R_DMA_INT_STATUS_ERRALL 0xFF000000
+#define TW6864_R_DMA_INT_STATUS_TOUT BIT(17)
+#define TW6864_R_DMA_INT_STATUS_DMA(ch) BIT(ch)
+#define TW6864_R_DMA_INT_STATUS_DMAALL 0x1FFFF
+#define TW6864_R_DMA_PB_STATUS 0x004 //0x01 //RO
+#define TW6864_R_DMA_CMD 0x008 //0x02
+#define TW6864_R_DMA_CMD_TRIG BIT(31)
+#define TW6864_R_DMA_CMD_RESET(ch) BIT(ch)
+#define TW6864_R_DMA_CMD_RESETALL 0x01FFFF
+#define TW6864_R_DMA_INT_ERROR 0x00C //0x03
+#define TW6864_R_DMA_VINT_FIFO(ch) (BIT(ch)<<24)
+#define TW6864_R_DMA_VINT_PTR(ch) (BIT(ch)<<16)
+#define TW6864_R_DMA_XINT_ERROR(ch) BIT(ch)
+#define TW6864_R_VIDEO_CHID 0x010 //0x04
+#define TW6864_R_VIDEO_PARSER_STATUS 0x014 //0x05
+#define TW6864_R_SYS_SOFT_RST 0x018 //0x06
+
+#define TW6864_R_DMA_PAGE_TABLE0 0x020 //0x08 //RW
+#define TW6864_R_DMA_PAGE_TABLE1 0x024 //0x09
+#define TW6864_R_DMA_CHANNEL_ENABLE 0x028 //0x0a
+#define TW6864_R_DMA_CONFIG 0x02C //0x0b
+#define TW6864_R_DMA_INT_REF 0x030 //0x0c
+#define TW6864_R_DMA_CHANNEL_TIMEOUT 0x034 //0x0d
+
+#define TW6864_R_DMA_CH0_CONFIG 0x040 //0x10 //DMA_CH0_CONFIG ~ DMA_CH7_CONFIG are continusly
+#define TW6864_R_DMA_CH1_CONFIG 0x044 //0x11
+#define TW6864_R_DMA_CH2_CONFIG 0x048 //0x12
+#define TW6864_R_DMA_CH3_CONFIG 0x04C //0x13
+#define TW6864_R_DMA_CH4_CONFIG 0x050 //0x14
+#define TW6864_R_DMA_CH5_CONFIG 0x054 //0x15
+#define TW6864_R_DMA_CH6_CONFIG 0x058 //0x16
+#define TW6864_R_DMA_CH7_CONFIG 0x05C //0x17
+#define TW6864_R_DMA_CH8_CONFIG_P 0x060 //0x18 // DMA_CH8_CONFIG_P ~ DMA_CH10_CONFIG_B are continusly
+#define TW6864_R_DMA_CH8_CONFIG_B 0x064 //0x19
+#define TW6864_R_DMA_CH9_CONFIG_P 0x068 //0x1A
+#define TW6864_R_DMA_CH9_CONFIG_B 0x06C //0x1B
+#define TW6864_R_DMA_CHA_CONFIG_P 0x070 //0x1C
+#define TW6864_R_DMA_CHA_CONFIG_B 0x074 //0x1D
+#define TW6864_R_DMA_CHB_CONFIG_P 0x078 //0x1E
+#define TW6864_R_DMA_CHB_CONFIG_B 0x07C //0x1F
+#define TW6864_R_DMA_CHC_CONFIG_P 0x080 //0x20
+#define TW6864_R_DMA_CHC_CONFIG_B 0x084 //0x21
+#define TW6864_R_DMA_CHD_CONFIG_P 0x088 //0x22
+#define TW6864_R_DMA_CHD_CONFIG_B 0x08C //0x23
+#define TW6864_R_DMA_CHE_CONFIG_P 0x090 //0x24
+#define TW6864_R_DMA_CHE_CONFIG_B 0x094 //0x25
+#define TW6864_R_DMA_CHF_CONFIG_P 0x098 //0x26
+#define TW6864_R_DMA_CHF_CONFIG_B 0x09C //0x27
+#define TW6864_R_DMA_CH10_CONFIG_P 0x0A0 //0x28
+#define TW6864_R_DMA_CH10_CONFIG_B 0x0A4 //0x29
+#define TW6864_R_CH0to7_CONFIG_REG(idx) (TW6864_R_DMA_CH0_CONFIG+((idx)*4))
+#define TW6864_R_CH8to15_CONFIG_REG_P(idx) (TW6864_R_DMA_CH8_CONFIG_P+((idx)*8))
+#define TW6864_R_CH8to15_CONFIG_REG_B(idx) (TW6864_R_DMA_CH8_CONFIG_B+((idx)*8))
+
+#define TW6864_R_VIDEO_CTRL1 0x0A8 //0x2A
+#define TW6864_R_VIDEO_CTRL1_STANDARD(ch) (BIT(ch)<<13)
+#define TW6864_R_VIDEO_CTRL1_STANDARDALL (0xFF<<13)
+#define TW6864_R_VIDEO_CTRL2 0x0AC //0x2B
+#define TW6864_R_VIDEO_CTRL2_GENRST(ch) (BIT(ch)<<16)
+#define TW6864_R_VIDEO_CTRL2_GENRSTALL 0xFF0000
+#define TW6864_R_VIDEO_CTRL2_PATTERNS(ch) (BIT(ch)<<8)
+#define TW6864_R_VIDEO_CTRL2_PATTERNSALL 0xFF00
+#define TW6864_R_VIDEO_CTRL2_GEN(ch) BIT(ch)
+#define TW6864_R_VIDEO_CTRL2_GENALL 0xFF
+#define TW6864_R_AUDIO_CTRL1 0x0B0 //0x2C
+#define TW6864_R_AUDIO_CTRL2 0x0B4 //0x2D
+#define TW6864_R_PHASE_REF_CONFIG 0x0B8 //0x2E
+#define TW6864_R_GPIO_REG 0x0BC //0x2F
+
+#define TW6864_R_INTL_HBAR0_CTRL 0x0C0 //0x30
+#define TW6864_R_INTL_HBAR1_CTRL 0x0C4 //0x31
+#define TW6864_R_INTL_HBAR2_CTRL 0x0C8 //0x32
+#define TW6864_R_INTL_HBAR3_CTRL 0x0CC //0x33
+#define TW6864_R_INTL_HBAR4_CTRL 0x0D0 //0x34
+#define TW6864_R_INTL_HBAR5_CTRL 0x0D4 //0x35
+#define TW6864_R_INTL_HBAR6_CTRL 0x0D8 //0x36
+#define TW6864_R_INTL_HBAR7_CTRL 0x0DC //0x37
+
+#define TW6864_R_AUDIO_CTRL3 0x0E0 //0x38
+#define TW6864_R_DROP_FIELD_REG0 0x0E4 //0x39
+#define TW6864_R_DROP_FIELD_REG1 0x0E8 //0x3A
+#define TW6864_R_DROP_FIELD_REG2 0x0EC //0x3B
+#define TW6864_R_DROP_FIELD_REG3 0x0F0 //0x3C
+#define TW6864_R_DROP_FIELD_REG4 0x0F4 //0x3D
+#define TW6864_R_DROP_FIELD_REG5 0x0F8 //0x3E
+#define TW6864_R_DROP_FIELD_REG6 0x0FC //0x3F
+#define TW6864_R_DROP_FIELD_REG7 0x100 //0x40
+#define TW6864_R_VIDEO_SIZE_REGA 0x104 //0x41 //Rev.A only
+#define TW6864_R_SHSCALER_REG0 0x108 //0x42
+#define TW6864_R_SHSCALER_REG1 0x10C //0x43
+#define TW6864_R_SHSCALER_REG2 0x110 //0x44
+#define TW6864_R_SHSCALER_REG3 0x114 //0x45
+#define TW6864_R_SHSCALER_REG4 0x118 //0x46
+#define TW6864_R_SHSCALER_REG5 0x11C //0x47
+#define TW6864_R_SHSCALER_REG6 0x120 //0x48
+#define TW6864_R_SHSCALER_REG7 0x124 //0x49
+#define TW6864_R_VIDEO_SIZE_REG0 0x128 //0x4A //Rev.B or later
+#define TW6864_R_VIDEO_SIZE_REG1 0x12C //0x4B
+#define TW6864_R_VIDEO_SIZE_REG2 0x130 //0x4C
+#define TW6864_R_VIDEO_SIZE_REG3 0x134 //0x4D
+#define TW6864_R_VIDEO_SIZE_REG4 0x138 //0x4E
+#define TW6864_R_VIDEO_SIZE_REG5 0x13C //0x4F
+#define TW6864_R_VIDEO_SIZE_REG6 0x140 //0x50
+#define TW6864_R_VIDEO_SIZE_REG7 0x144 //0x51
+#define TW6864_R_VIDEO_SIZE_REG0_F2 0x148 //0x52 //Rev.B or later
+#define TW6864_R_VIDEO_SIZE_REG1_F2 0x14C //0x53
+#define TW6864_R_VIDEO_SIZE_REG2_F2 0x150 //0x54
+#define TW6864_R_VIDEO_SIZE_REG3_F2 0x154 //0x55
+#define TW6864_R_VIDEO_SIZE_REG4_F2 0x158 //0x56
+#define TW6864_R_VIDEO_SIZE_REG5_F2 0x15C //0x57
+#define TW6864_R_VIDEO_SIZE_REG6_F2 0x160 //0x58
+#define TW6864_R_VIDEO_SIZE_REG7_F2 0x164 //0x59
+#define TW6864_R_VC_CTRL_REG0 0x1C0 //0x70
+#define TW6864_R_VC_CTRL_REG1 0x1C4 //0x71
+#define TW6864_R_VC_CTRL_REG2 0x1C8 //0x72
+#define TW6864_R_VC_CTRL_REG3 0x1D0 //0x73
+#define TW6864_R_VC_CTRL_REG4 0x1D4 //0x74
+#define TW6864_R_VC_CTRL_REG5 0x1D8 //0x75
+#define TW6864_R_VC_CTRL_REG6 0x1DC //0x76
+#define TW6864_R_VC_CTRL_REG7 0x1E0 //0x77
+#define TW6864_R_BDMA_ADDR_P_0 0x200 //0x80 //0x80 ~ 0xBF,Rev.B or later
+#define TW6864_R_BDMA_WHP_0 0x204 //0x81
+#define TW6864_R_BDMA_ADDR_B_0 0x208 //0x82
+#define TW6864_R_BDMA_ADDR_P_F2_0 0x210 //0x84
+#define TW6864_R_BDMA_WHP_F2_0 0x214 //0x85
+#define TW6864_R_BDMA_ADDR_B_F2_0 0x218 //0x86
+#define TW6864_R_CSR_REG 0x3F4 //0xFD
+#define TW6864_R_EP_REG_ADDR 0x3F8 //0xFE
+#define TW6864_R_EP_REG_DATA 0x3FC //0xFF
+#define TW6864_R_DROP_FIELD_REG(idx) (TW6864_R_DROP_FIELD_REG0+((idx)*4))
+#define TW6864_R_SHSCALER_REG(idx) (TW6864_R_SHSCALER_REG0+((idx)*4))
+#define TW6864_R_VIDEO_SIZE_REG(idx) (TW6864_R_VIDEO_SIZE_REG0+((idx)*4))
+#define TW6864_R_VIDEO_SIZE_REG_F2(idx) (TW6864_R_VIDEO_SIZE_REG0_F2+((idx)*4))
+#define TW6864_R_VC_CTRL_REG(idx) (TW6864_R_VC_CTRL_REG0+((idx)*4))
+#define TW6864_R_BDMA(reg, idx) ((reg)+(idx)*0x20)
+
+//////////////////////////////////////////////////
+// external 2864 registers definitions
+#define TW2864_R_I2C_ADDR0 0x50
+#define TW2864_R_I2C_ADDR1 0x52
+#define TW2864_R_VSTATUS_0 0x00
+#define TW2864_R_VSTATUS_1 0x10
+#define TW2864_R_VSTATUS_2 0x20
+#define TW2864_R_VSTATUS_3 0x30
+#define TW2864_R_VDELAY_0 0x08
+#define TW2864_R_VDELAY_1 0x18
+#define TW2864_R_VDELAY_2 0x28
+#define TW2864_R_VDELAY_3 0x38
+#define TW2864_R_HDELAY_0 0x0A
+#define TW2864_R_HDELAY_1 0x1A
+#define TW2864_R_HDELAY_2 0x2A
+#define TW2864_R_HDELAY_3 0x3A
+#define TW2864_R_STANDARD_0 0x0E
+#define TW2864_R_STANDARD_1 0x1E
+#define TW2864_R_STANDARD_2 0x2E
+#define TW2864_R_STANDARD_3 0x3E
+#define TW2864_R_CLK_SEL_2865 0x61 //specfic to TW2865
+#define TW2864_R_VERTICAL_SHARPNESS 0x85
+#define TW2864_R_VERTICAL_CTRL 0x8F
+#define TW2864_R_LOOP_CTRL 0x95
+#define TW2864_R_MISC_CTRLII1 0x96
+#define TW2864_R_MISC_CTRLII2 0x97
+#define TW2864_R_MPP_MODE0 0xC8
+#define TW2864_R_MPP_MODE1 0xC9
+#define TW2864_R_NOVID 0x9E
+#define TW2864_R_CLKODEL 0x9F
+#define TW2864_R_HFLT1 0xA8
+#define TW2864_R_HFLT2 0xA9
+#define TW2864_R_VIDEO_OUT_CTRL 0xCA
+#define TW2864_R_SMD 0xCF
+#define TW2864_R_MASTER_CTRL 0xDB
+#define TW2864_R_AUDIO_GAIN0 0xD0
+#define TW2864_R_AUDIO_GAIN1 0xD1
+#define TW2864_R_NUM_ADUIOR 0xD2
+#define TW2864_R_SEQ_ADUIOR 0xD7
+#define TW2864_R_MIX_OUT_SEL 0xE0
+#define TW2864_R_VSCALE_LO_0 0xE4
+#define TW2864_R_VHSCALE_HI_0 0xE5
+#define TW2864_R_HSCALE_LO_0 0xE6
+#define TW2864_R_VSCALE_LO_1 0xE7
+#define TW2864_R_VHSCALE_HI_1 0xE8
+#define TW2864_R_HSCALE_LO_1 0xE9
+#define TW2864_R_VSCALE_LO_2 0xEA
+#define TW2864_R_VHSCALE_HI_2 0xEB
+#define TW2864_R_HSCALE_LO_2 0xEC
+#define TW2864_R_VSCALE_LO_3 0xED
+#define TW2864_R_VHSCALE_HI_3 0xEE
+#define TW2864_R_HSCALE_LO_3 0xEF
+#define TW2864_R_VIDEO_MISC 0xF9
+#define TW2864_R_CLKOCTRL 0xFA
+#define TW2864_R_CLKOPOL 0xFB
+#define TW2864_R_AVDET_ENA 0xFC
+#define TW2864_R_AVDET_STATE 0xFD
+#define TW2864_R_DEV_REV 0xFE
+#define TW2864_R_DEV_REV_LO 0xFF
+#define TW2864_R_CHANNEL(reg, ch) ((reg)+(ch)*0x10)
+
+//0x01 0x11 0x21 0x31 BRIGHT BRIGHTNESS
+#define TW2864_R_BRIGHT_0 0x01
+#define TW2864_R_BRIGHT_1 0x11
+#define TW2864_R_BRIGHT_2 0x21
+#define TW2864_R_BRIGHT_3 0x31
+
+//0x02 0x12 0x22 0x32 CONTRAST CONTRAST
+#define TW2864_R_CONTRAST_0 0x02
+#define TW2864_R_CONTRAST_1 0x12
+#define TW2864_R_CONTRAST_2 0x22
+#define TW2864_R_CONTRAST_3 0x32
+
+//0x03 0x13 0x23 0x33 SHARPNESS SCURVE VSF CTI SHARPNESS
+#define TW2864_R_SHARPNESS_0 0x03
+#define TW2864_R_SHARPNESS_1 0x13
+#define TW2864_R_SHARPNESS_2 0x23
+#define TW2864_R_SHARPNESS_3 0x33
+
+//0x04 0x14 0x24 0x34 SAT_U SAT_U
+#define TW2864_R_SAT_U_0 0x04
+#define TW2864_R_SAT_U_1 0x14
+#define TW2864_R_SAT_U_2 0x24
+#define TW2864_R_SAT_U_3 0x34
+
+//0x05 0x15 0x25 0x35 SAT_V SAT_V
+#define TW2864_R_SAT_V_0 0x05
+#define TW2864_R_SAT_V_1 0x15
+#define TW2864_R_SAT_V_2 0x25
+#define TW2864_R_SAT_V_3 0x35
+
+//0x06 0x16 0x26 0x36 HUE HUE
+#define TW2864_R_HUE_0 0x06
+#define TW2864_R_HUE_1 0x16
+#define TW2864_R_HUE_2 0x26
+#define TW2864_R_HUE_3 0x36
+
+//////////////////////////////////////////////////
+// internal 2864 registers definitions
+#define TW6864_R_VSTATUS_0 0x400 //0x100
+#define TW6864_R_VSTATUS_1 0x440 //0x110
+#define TW6864_R_VSTATUS_2 0x480 //0x120
+#define TW6864_R_VSTATUS_3 0x4C0 //0x130
+#define TW6864_R_BRIGHT_0 0x404 //0x101
+#define TW6864_R_BRIGHT_1 0x444 //0x111
+#define TW6864_R_BRIGHT_2 0x484 //0x121
+#define TW6864_R_BRIGHT_3 0x4C4 //0x131
+#define TW6864_R_CONTRAST_0 0x408 //0x102
+#define TW6864_R_CONTRAST_1 0x448 //0x112
+#define TW6864_R_CONTRAST_2 0x488 //0x122
+#define TW6864_R_CONTRAST_3 0x4C8 //0x132
+#define TW6864_R_SHARPNESS_0 0x40C //0x103
+#define TW6864_R_SHARPNESS_1 0x44C //0x113
+#define TW6864_R_SHARPNESS_2 0x48C //0x123
+#define TW6864_R_SHARPNESS_3 0x4CC //0x133
+#define TW6864_R_SAT_U_0 0x410 //0x104
+#define TW6864_R_SAT_U_1 0x450 //0x114
+#define TW6864_R_SAT_U_2 0x490 //0x124
+#define TW6864_R_SAT_U_3 0x4D0 //0x134
+#define TW6864_R_SAT_V_0 0x414 //0x105
+#define TW6864_R_SAT_V_1 0x454 //0x115
+#define TW6864_R_SAT_V_2 0x494 //0x125
+#define TW6864_R_SAT_V_3 0x4D4 //0x135
+#define TW6864_R_HUE_0 0x418 //0x106
+#define TW6864_R_HUE_1 0x458 //0x116
+#define TW6864_R_HUE_2 0x498 //0x126
+#define TW6864_R_HUE_3 0x4D8 //0x136
+#define TW6864_R_CROPPING_0 0x41C //0x107
+#define TW6864_R_CROPPING_1 0x45C //0x117
+#define TW6864_R_CROPPING_2 0x49C //0x127
+#define TW6864_R_CROPPING_3 0x4DC //0x137
+#define TW6864_R_VDELAY_0 0x420 //0x108
+#define TW6864_R_VDELAY_1 0x460 //0x118
+#define TW6864_R_VDELAY_2 0x4A0 //0x128
+#define TW6864_R_VDELAY_3 0x4E0 //0x138
+#define TW6864_R_VACTIVE_0 0x424 //0x109
+#define TW6864_R_VACTIVE_1 0x464 //0x119
+#define TW6864_R_VACTIVE_2 0x4A4 //0x129
+#define TW6864_R_VACTIVE_3 0x4E4 //0x139
+#define TW6864_R_HDELAY_0 0x428 //0x10A
+#define TW6864_R_HDELAY_1 0x468 //0x11A
+#define TW6864_R_HDELAY_2 0x4A8 //0x12A
+#define TW6864_R_HDELAY_3 0x4E8 //0x13A
+#define TW6864_R_HACTIVE_0 0x42C //0x10B
+#define TW6864_R_HACTIVE_1 0x46C //0x11B
+#define TW6864_R_HACTIVE_2 0x4AC //0x12B
+#define TW6864_R_HACTIVE_3 0x4EC //0x13B
+#define TW6864_R_MACROVISION_0 0x430 //0x10C
+#define TW6864_R_MACROVISION_1 0x470 //0x11C
+#define TW6864_R_MACROVISION_2 0x4B0 //0x12C
+#define TW6864_R_MACROVISION_3 0x4F0 //0x13C
+#define TW6864_R_CHIPSTATUSII_0 0x434 //0x10D
+#define TW6864_R_CHIPSTATUSII_1 0x474 //0x11D
+#define TW6864_R_CHIPSTATUSII_2 0x4B4 //0x12D
+#define TW6864_R_CHIPSTATUSII_3 0x4F4 //0x13D
+#define TW6864_R_STANDARD_0 0x438 //0x10E
+#define TW6864_R_STANDARD_1 0x478 //0x11E
+#define TW6864_R_STANDARD_2 0x4B8 //0x12E
+#define TW6864_R_STANDARD_3 0x4F8 //0x13E
+#define TW6864_R_STDDETECT_0 0x43C //0x10F
+#define TW6864_R_STDDETECT_1 0x47C //0x11F
+#define TW6864_R_STDDETECT_2 0x4BC //0x12F
+#define TW6864_R_STDDETECT_3 0x4FC //0x13F
+#define TW6864_R_VSCALE_LO_0 0x510 //0x144
+#define TW6864_R_VSCALE_LO_1 0x550 //0x154
+#define TW6864_R_VSCALE_LO_2 0x590 //0x164
+#define TW6864_R_VSCALE_LO_3 0x5D0 //0x174
+#define TW6864_R_VHSCALE_HI_0 0x514 //0x145
+#define TW6864_R_VHSCALE_HI_1 0x554 //0x155
+#define TW6864_R_VHSCALE_HI_2 0x594 //0x165
+#define TW6864_R_VHSCALE_HI_3 0x5D4 //0x175
+#define TW6864_R_HSCALE_LO_0 0x518 //0x146
+#define TW6864_R_HSCALE_LO_1 0x558 //0x156
+#define TW6864_R_HSCALE_LO_2 0x598 //0x166
+#define TW6864_R_HSCALE_LO_3 0x5D8 //0x176
+#define TW6864_R_CROPPING_F2_0 0x51C //0x147
+#define TW6864_R_CROPPING_F2_1 0x55C //0x157
+#define TW6864_R_CROPPING_F2_2 0x59C //0x167
+#define TW6864_R_CROPPING_F2_3 0x5DC //0x177
+#define TW6864_R_VDELAY_F2_0 0x520 //0x148
+#define TW6864_R_VDELAY_F2_1 0x560 //0x158
+#define TW6864_R_VDELAY_F2_2 0x5A0 //0x168
+#define TW6864_R_VDELAY_F2_3 0x5E0 //0x178
+#define TW6864_R_VACTIVE_F2_0 0x524 //0x149
+#define TW6864_R_VACTIVE_F2_1 0x564 //0x159
+#define TW6864_R_VACTIVE_F2_2 0x5A4 //0x169
+#define TW6864_R_VACTIVE_F2_3 0x5E4 //0x179
+#define TW6864_R_HDELAY_F2_0 0x528 //0x14A
+#define TW6864_R_HDELAY_F2_1 0x568 //0x15A
+#define TW6864_R_HDELAY_F2_2 0x5A8 //0x16A
+#define TW6864_R_HDELAY_F2_3 0x5E8 //0x17A
+#define TW6864_R_HACTIVE_F2_0 0x52C //0x14B
+#define TW6864_R_HACTIVE_F2_1 0x56C //0x15B
+#define TW6864_R_HACTIVE_F2_2 0x5AC //0x16B
+#define TW6864_R_HACTIVE_F2_3 0x5EC //0x17B
+#define TW6864_R_VSCALE_LO_F2_0 0x530 //0x14C
+#define TW6864_R_VSCALE_LO_F2_1 0x570 //0x15C
+#define TW6864_R_VSCALE_LO_F2_2 0x5B0 //0x16C
+#define TW6864_R_VSCALE_LO_F2_3 0x5F0 //0x17C
+#define TW6864_R_VHSCALE_HI_F2_0 0x534 //0x14D
+#define TW6864_R_VHSCALE_HI_F2_1 0x574 //0x15D
+#define TW6864_R_VHSCALE_HI_F2_2 0x5B4 //0x16D
+#define TW6864_R_VHSCALE_HI_F2_3 0x5F4 //0x17D
+#define TW6864_R_HSCALE_LO_F2_0 0x538 //0x14E
+#define TW6864_R_HSCALE_LO_F2_1 0x578 //0x15E
+#define TW6864_R_HSCALE_LO_F2_2 0x5B8 //0x16E
+#define TW6864_R_HSCALE_LO_F2_3 0x5F8 //0x17E
+#define TW6864_R_F2_CNT_0 0x53C //0x14F
+#define TW6864_R_F2_CNT_1 0x57C //0x15F
+#define TW6864_R_F2_CNT_2 0x5BC //0x16F
+#define TW6864_R_F2_CNT_3 0x5FC //0x17F
+#define TW6864_R_AVSRST 0x600 //0x180
+#define TW6864_R_COLORKILL_HY 0x610 //0x184
+#define TW6864_R_VERTICAL_CTRL 0x63C //0x18F
+#define TW6864_R_LOOP_CTRL 0x654 //0x195
+#define TW6864_R_MISC_CTRLII1 0x658 //0x196
+#define TW6864_R_MISC_CTRLII2 0x65C //0x197
+#define TW6864_R_HFLT1 0x6A0 //0x1A8
+#define TW6864_R_HFLT2 0x6A4 //0x1A9
+#define TW6864_R_AUDIO_GAIN_0 0x740 //0x1D0
+#define TW6864_R_AUDIO_GAIN_1 0x744 //0x1D1
+#define TW6864_R_AUDIO_GAIN_2 0x748 //0x1D2
+#define TW6864_R_AUDIO_GAIN_3 0x74C //0x1D3
+
+#define TW6864_R_VSTATUS(ch) (TW6864_R_VSTATUS_0 + (ch)*0x40)
+#define TW6864_R_BRIGHT(ch) (TW6864_R_BRIGHT_0 + (ch)*0x40)
+#define TW6864_R_CONTRAST(ch) (TW6864_R_CONTRAST_0 + (ch)*0x40)
+#define TW6864_R_SHARPNESS(ch) (TW6864_R_SHARPNESS_0 + (ch)*0x40)
+#define TW6864_R_SAT_U(ch) (TW6864_R_SAT_U_0 + (ch)*0x40)
+#define TW6864_R_SAT_V(ch) (TW6864_R_SAT_V_0 + (ch)*0x40)
+#define TW6864_R_HUE(ch) (TW6864_R_HUE_0 + (ch)*0x40)
+#define TW6864_R_CROPPING(ch) (TW6864_R_CROPPING_0 + (ch)*0x40)
+#define TW6864_R_VDELAY(ch) (TW6864_R_VDELAY_0 + (ch)*0x40)
+#define TW6864_R_VACTIVE(ch) (TW6864_R_VACTIVE_0 + (ch)*0x40)
+#define TW6864_R_HDELAY(ch) (TW6864_R_HDELAY_0 + (ch)*0x40)
+#define TW6864_R_HACTIVE(ch) (TW6864_R_HACTIVE_0 + (ch)*0x40)
+#define TW6864_R_CROPPING_F2(ch) (TW6864_R_CROPPING_F2_0 + (ch)*0x40)
+#define TW6864_R_VDELAY_F2(ch) (TW6864_R_VDELAY_F2_0 + (ch)*0x40)
+#define TW6864_R_VACTIVE_F2(ch) (TW6864_R_VACTIVE_F2_0 + (ch)*0x40)
+#define TW6864_R_HDELAY_F2(ch) (TW6864_R_HDELAY_F2_0 + (ch)*0x40)
+#define TW6864_R_HACTIVE_F2(ch) (TW6864_R_HACTIVE_F2_0 + (ch)*0x40)
+#define TW6864_R_MACROVISION(ch) (TW6864_R_MACROVISION_0 + (ch)*0x40)
+#define TW6864_R_CHIPSTATUSII(ch) (TW6864_R_CHIPSTATUSII_0 + (ch)*0x40)
+#define TW6864_R_STANDARD(ch) (TW6864_R_STANDARD_0 + (ch)*0x40)
+#define TW6864_R_STDDETECT(ch) (TW6864_R_STDDETECT_0 + (ch)*0x40)
+#define TW6864_R_CHANNEL(reg, ch) ((reg)+(ch)*0x40)
+
+//////////////////////////////////////////////////
+//NO NOT CHANGE
+//These 3 definitions are matched with hardware
+#define TW6864_MAX_NUM_SG_DMA 8
+#define TW6864_MAX_NUM_DATA_DMA 9
+#define TW6864_MAX_NUM_DMA (TW6864_MAX_NUM_SG_DMA+TW6864_MAX_NUM_DATA_DMA)
+
+#define TW6864_VIDEO_GEN 0xFF //1=External,0=internal
+#define TW6864_VIDEO_GEN_PATTERNS 0x00 //0=ColorBar, 1=SEQ DATA , 8-bit
+#define TW6864_VIDEO_FORMAT_UYVY 0
+#define TW6864_VIDEO_FORMAT_YUV420 1
+#define TW6864_VIDEO_FORMAT_Y411 2 //Use VIDEO_FORMAT_IYU1 instead, LEAD does not accept this, even they are the same
+#define TW6864_VIDEO_FORMAT_Y41P 3
+#define TW6864_VIDEO_FORMAT_RGB555 4
+#define TW6864_VIDEO_FORMAT_RGB565 5
+#define TW6864_VIDEO_FORMAT_IYU1 0xA //same as VIDEO_FORMAT_Y411, m_nVideoFormat will be masked by 0x7
+#define TW6864_VIDEO_FORMAT_YUYV 6 //YUYV=YUY2, only available for Rev.B or later
+#define TW6864_VIDEO_FORMAT_UYVY_FRAME 0x8 //VideoInfoHeader2
+
+#define CaptureFLV 0
+
+#define TW6864_AUDIO_DMA_LENGTH 4096 //in bytes, should not > PAGE_SIZE(4096)
+#define TW6864_AUDIO_GEN 1 //1=External,0=internal
+#define TW6864_AUDIO_GEN_PATTERN 0 //0=WAVE,1=SEQ DATA , 1-bit
+#define TW6864_AUDIO_GEN_MIX_SEL 0 //(0~7)
+#define TW6864_AUDIO_SAMPLE_RATE 8 //in KHz
+#define TW6864_AUDIO_SAMPLE_BIT (CaptureFLV*8+8) //8bit or 16bit
+#define TW6864_AUDIO_SAMP_RATE_INT(rate) (125000/(rate))
+#define TW6864_AUDIO_SAMP_RATE_EXT(rate) ((((unsigned long long)125000)<<16)/(rate))
+
+//for TW6869
+//#define FPGA
+
+#define TW6869_R_MD_CONF0 0x180 //0x60 //TW6869 or later, 0x60 ~ 0x67
+#define TW6869_R_MD_INIT0 0x1A0 //0x68 //TW6869 or later, 0x68 ~ 0x6F
+#define TW6869_R_MD_MAP0 0x1C0 //0x70 //TW6869 or later, 0x70 ~ 0x77
+
+#define TW6869_R_DMA_PAGE_TABLE0 TW6864_R_DMA_PAGE_TABLE0 //0x020 //0x08 //RW
+#define TW6869_R_DMA_PAGE_TABLE1 TW6864_R_DMA_PAGE_TABLE1 //0x024 //0x09
+#define TW6869_R_DMA_PAGE_TABLE2 0x340 //0xD0
+#define TW6869_R_DMA_PAGE_TABLE3 0x344 //0xD1
+#define TW6869_R_DMA_PAGE_TABLE4 0x348 //0xD2
+#define TW6869_R_DMA_PAGE_TABLE5 0x34C //0xD3
+#define TW6869_R_DMA_PAGE_TABLE6 0x350 //0xD4
+#define TW6869_R_DMA_PAGE_TABLE7 0x354 //0xD5
+#define TW6869_R_DMA_PAGE_TABLE8 0x358 //0xD6
+#define TW6869_R_DMA_PAGE_TABLE9 0x35C //0xD7
+#define TW6869_R_DMA_PAGE_TABLEA 0x360 //0xD8
+#define TW6869_R_DMA_PAGE_TABLEB 0x364 //0xD9
+#define TW6869_R_DMA_PAGE_TABLEC 0x368 //0xDA
+#define TW6869_R_DMA_PAGE_TABLED 0x36C //0xDB
+#define TW6869_R_DMA_PAGE_TABLEE 0x370 //0xDC
+#define TW6869_R_DMA_PAGE_TABLEF 0x374 //0xDD
+#define TW6869_R_GPIO_REG_1 0x380 //0xE0
+#define TW6869_R_GPIO_REG_2 0x384 //0xE1
+#define TW6869_R_PIN_CFG_CTRL 0x3EC //0xFB
+#define TW6869_R_VSTATUS_4 0x800 //0x200
+#define TW6869_R_VSTATUS_5 0x840 //0x210
+#define TW6869_R_VSTATUS_6 0x880 //0x220
+#define TW6869_R_VSTATUS_7 0x8C0 //0x230
+#define TW6869_R_BRIGHT_4 0x804 //0x201
+#define TW6869_R_BRIGHT_5 0x844 //0x211
+#define TW6869_R_BRIGHT_6 0x884 //0x221
+#define TW6869_R_BRIGHT_7 0x8C4 //0x231
+#define TW6869_R_CONTRAST_4 0x808 //0x202
+#define TW6869_R_CONTRAST_5 0x848 //0x212
+#define TW6869_R_CONTRAST_6 0x888 //0x222
+#define TW6869_R_CONTRAST_7 0x8C8 //0x232
+#define TW6869_R_SHARPNESS_4 0x80C //0x203
+#define TW6869_R_SHARPNESS_5 0x84C //0x213
+#define TW6869_R_SHARPNESS_6 0x88C //0x223
+#define TW6869_R_SHARPNESS_7 0x8CC //0x233
+#define TW6869_R_SAT_U_4 0x810 //0x204
+#define TW6869_R_SAT_U_5 0x850 //0x214
+#define TW6869_R_SAT_U_6 0x890 //0x224
+#define TW6869_R_SAT_U_7 0x8D0 //0x234
+#define TW6869_R_SAT_V_4 0x814 //0x205
+#define TW6869_R_SAT_V_5 0x854 //0x215
+#define TW6869_R_SAT_V_6 0x894 //0x225
+#define TW6869_R_SAT_V_7 0x8D4 //0x235
+#define TW6869_R_HUE_4 0x818 //0x206
+#define TW6869_R_HUE_5 0x858 //0x216
+#define TW6869_R_HUE_6 0x898 //0x226
+#define TW6869_R_HUE_7 0x8D8 //0x236
+#define TW6869_R_CROPPING_4 0x81C //0x207
+#define TW6869_R_CROPPING_5 0x85C //0x217
+#define TW6869_R_CROPPING_6 0x89C //0x227
+#define TW6869_R_CROPPING_7 0x8DC //0x237
+#define TW6869_R_VDELAY_4 0x820 //0x208
+#define TW6869_R_VDELAY_5 0x860 //0x218
+#define TW6869_R_VDELAY_6 0x8A0 //0x228
+#define TW6869_R_VDELAY_7 0x8E0 //0x238
+#define TW6869_R_VACTIVE_4 0x824 //0x209
+#define TW6869_R_VACTIVE_5 0x864 //0x219
+#define TW6869_R_VACTIVE_6 0x8A4 //0x229
+#define TW6869_R_VACTIVE_7 0x8E4 //0x239
+#define TW6869_R_HDELAY_4 0x828 //0x20A
+#define TW6869_R_HDELAY_5 0x868 //0x21A
+#define TW6869_R_HDELAY_6 0x8A8 //0x22A
+#define TW6869_R_HDELAY_7 0x8E8 //0x23A
+#define TW6869_R_HACTIVE_4 0x82C //0x20B
+#define TW6869_R_HACTIVE_5 0x86C //0x21B
+#define TW6869_R_HACTIVE_6 0x8AC //0x22B
+#define TW6869_R_HACTIVE_7 0x8EC //0x23B
+#define TW6869_R_MACROVISION_4 0x830 //0x20C
+#define TW6869_R_MACROVISION_5 0x870 //0x21C
+#define TW6869_R_MACROVISION_6 0x8B0 //0x22C
+#define TW6869_R_MACROVISION_7 0x8F0 //0x23C
+#define TW6869_R_CHIPSTATUSII_4 0x834 //0x20D
+#define TW6869_R_CHIPSTATUSII_5 0x874 //0x21D
+#define TW6869_R_CHIPSTATUSII_6 0x8B4 //0x22D
+#define TW6869_R_CHIPSTATUSII_7 0x8F4 //0x23D
+#define TW6869_R_STANDARD_4 0x838 //0x20E
+#define TW6869_R_STANDARD_5 0x878 //0x21E
+#define TW6869_R_STANDARD_6 0x8B8 //0x22E
+#define TW6869_R_STANDARD_7 0x8F8 //0x23E
+#define TW6869_R_STDDETECT_4 0x83C //0x20F
+#define TW6869_R_STDDETECT_5 0x87C //0x21F
+#define TW6869_R_STDDETECT_6 0x8BC //0x22F
+#define TW6869_R_STDDETECT_7 0x8FC //0x23F
+#define TW6869_R_VSCALE_LO_4 0x910 //0x244
+#define TW6869_R_VSCALE_LO_5 0x950 //0x254
+#define TW6869_R_VSCALE_LO_6 0x990 //0x264
+#define TW6869_R_VSCALE_LO_7 0x9D0 //0x274
+#define TW6869_R_VHSCALE_HI_4 0x914 //0x245
+#define TW6869_R_VHSCALE_HI_5 0x954 //0x255
+#define TW6869_R_VHSCALE_HI_6 0x994 //0x265
+#define TW6869_R_VHSCALE_HI_7 0x9D4 //0x275
+#define TW6869_R_HSCALE_LO_4 0x918 //0x246
+#define TW6869_R_HSCALE_LO_5 0x958 //0x256
+#define TW6869_R_HSCALE_LO_6 0x998 //0x266
+#define TW6869_R_HSCALE_LO_7 0x9D8 //0x276
+#define TW6869_R_CROPPING_F2_4 0x91C //0x247
+#define TW6869_R_CROPPING_F2_5 0x95C //0x257
+#define TW6869_R_CROPPING_F2_6 0x99C //0x267
+#define TW6869_R_CROPPING_F2_7 0x9DC //0x277
+#define TW6869_R_VDELAY_F2_4 0x920 //0x248
+#define TW6869_R_VDELAY_F2_5 0x960 //0x258
+#define TW6869_R_VDELAY_F2_6 0x9A0 //0x268
+#define TW6869_R_VDELAY_F2_7 0x9E0 //0x278
+#define TW6869_R_VACTIVE_F2_4 0x924 //0x249
+#define TW6869_R_VACTIVE_F2_5 0x964 //0x259
+#define TW6869_R_VACTIVE_F2_6 0x9A4 //0x269
+#define TW6869_R_VACTIVE_F2_7 0x9E4 //0x279
+#define TW6869_R_HDELAY_F2_4 0x928 //0x24A
+#define TW6869_R_HDELAY_F2_5 0x968 //0x25A
+#define TW6869_R_HDELAY_F2_6 0x9A8 //0x26A
+#define TW6869_R_HDELAY_F2_7 0x9E8 //0x27A
+#define TW6869_R_HACTIVE_F2_4 0x92C //0x24B
+#define TW6869_R_HACTIVE_F2_5 0x96C //0x25B
+#define TW6869_R_HACTIVE_F2_6 0x9AC //0x26B
+#define TW6869_R_HACTIVE_F2_7 0x9EC //0x27B
+#define TW6869_R_VSCALE_LO_F2_4 0x930 //0x24C
+#define TW6869_R_VSCALE_LO_F2_5 0x970 //0x25C
+#define TW6869_R_VSCALE_LO_F2_6 0x9B0 //0x26C
+#define TW6869_R_VSCALE_LO_F2_7 0x9F0 //0x27C
+#define TW6869_R_VHSCALE_HI_F2_4 0x934 //0x24D
+#define TW6869_R_VHSCALE_HI_F2_5 0x974 //0x25D
+#define TW6869_R_VHSCALE_HI_F2_6 0x9B4 //0x26D
+#define TW6869_R_VHSCALE_HI_F2_7 0x9F4 //0x27D
+#define TW6869_R_HSCALE_LO_F2_4 0x938 //0x24E
+#define TW6869_R_HSCALE_LO_F2_5 0x978 //0x25E
+#define TW6869_R_HSCALE_LO_F2_6 0x9B8 //0x26E
+#define TW6869_R_HSCALE_LO_F2_7 0x9F8 //0x27E
+#define TW6869_R_F2_CNT_4 0x93C //0x24F
+#define TW6869_R_F2_CNT_5 0x97C //0x25F
+#define TW6869_R_F2_CNT_6 0x9BC //0x26F
+#define TW6869_R_F2_CNT_7 0x9FC //0x27F
+#define TW6869_R_VERTICAL_CTRL 0xA3C //0x28F
+#define TW6869_R_LOOP_CTRL 0xA54 //0x295
+#define TW6869_R_HFLT1 0xAA0 //0x2A8
+#define TW6869_R_HFLT2 0xAA4 //0x2A9
+#define TW6869_R_AUDIO_GAIN_4 0xB40 //0x2D0
+#define TW6869_R_AUDIO_GAIN_5 0xB44 //0x2D1
+#define TW6869_R_AUDIO_GAIN_6 0xB48 //0x2D2
+#define TW6869_R_AUDIO_GAIN_7 0xB4C //0x2D3
+
+#endif // TW686XREG_H_INCLUDED
diff --git a/drivers/pci/pcie/tw6869/tw686x-video.c b/drivers/pci/pcie/tw6869/tw686x-video.c
new file mode 100644
index 000000000000..41eacf10a49d
--- /dev/null
+++ b/drivers/pci/pcie/tw6869/tw686x-video.c
@@ -0,0 +1,1670 @@
+/*
+ * Driver for Intersil|Techwell TW6869 based DVR cards
+ *
+ * (c) 2011-12 liran <jli11@intersil.com> [Intersil|Techwell China]
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/list.h>
+#include <linux/pagemap.h>
+#include <linux/module.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+#include "tw686x.h"
+#include "tw686x-device.h"
+#include "tw686x-reg.h"
+
+MODULE_DESCRIPTION("v4l2 driver module for tw686x based DVR cards");
+MODULE_AUTHOR("liran <jli11@intersil.com>");
+MODULE_LICENSE("GPL");
+
+static int tw686x_vidcount = 0;
+
+/* ------------------------------------------------------------------- */
+/* static data */
+
+#define FORMAT_FLAGS_PACKED 0x01
+
+static struct tw686x_fmt formats[] = {
+ {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "15 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "16 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:1:1, packed, Y41P",
+ .fourcc = V4L2_PIX_FMT_Y41P,
+ .depth = 12,
+ .flags = FORMAT_FLAGS_PACKED,
+ },
+};
+
+static const struct v4l2_queryctrl no_ctrl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+static const struct v4l2_queryctrl video_ctrls[] = {
+ /* --- video --- */
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .minimum = -128,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 20,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 100,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 128,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .minimum = -128,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+};
+static const unsigned int CTRLS = ARRAY_SIZE(video_ctrls);
+
+static const struct v4l2_queryctrl* ctrl_by_id(unsigned int id)
+{
+ unsigned int i;
+
+ for (i = 0; i < CTRLS; i++)
+ if (video_ctrls[i].id == id)
+ return video_ctrls+i;
+ return NULL;
+}
+
+static struct tw686x_fmt *format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++)
+ if (formats[i].fourcc == fourcc)
+ return formats+i;
+
+ printk(KERN_ERR "%s(0x%08x) NOT FOUND\n", __func__, fourcc);
+ return NULL;
+}
+
+/* ----------------------------------------------------------------------- */
+/* resource management */
+
+static int res_get(struct tw686x_vdev *dev, struct tw686x_fh *fh, unsigned int bit)
+{
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ mutex_lock(&dev->lock);
+ if (dev->resources & bit) {
+ /* no, someone else uses it */
+ mutex_unlock(&dev->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->resources |= bit;
+ dvprintk(DPRT_LEVEL0, dev, "res: get %d\n",bit);
+ mutex_unlock(&dev->lock);
+
+ return 1;
+}
+
+static int res_check(struct tw686x_fh *fh, unsigned int bit)
+{
+ return (fh->resources & bit);
+}
+
+static int res_locked(struct tw686x_vdev *dev, unsigned int bit)
+{
+ return (dev->resources & bit);
+}
+
+static
+void res_free(struct tw686x_vdev *dev, struct tw686x_fh *fh, unsigned int bits)
+{
+ BUG_ON((fh->resources & bits) != bits);
+
+ mutex_lock(&dev->lock);
+ fh->resources &= ~bits;
+ dev->resources &= ~bits;
+ dvprintk(DPRT_LEVEL0, dev, "res: put %d\n",bits);
+ mutex_unlock(&dev->lock);
+}
+
+void tw686x_video_start(struct tw686x_vdev* dev)
+{
+ struct tw686x_dmaqueue *q = &dev->video_q;
+ struct tw686x_buf *buf;
+ struct list_head *item, *item_temp;
+ bool b_active = false;
+
+ //dvprintk(1, dev, "%s()\n", __func__);
+
+ if( !res_locked(dev, RESOURCE_VIDEO) ) {
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); //主要目的是在没有中断产生时,停止采集后释放所有buffer,否则可能streamoff死等
+ return;
+ }
+
+ list_for_each(item, &q->active) {
+ buf = list_entry(item, struct tw686x_buf, active);
+ b_active = IS_TW686X_DMA_MODE_BLOCK ?
+ (dev->pb_next < dev->buf_needs) : (dev->pb_next<=buf->sg_ofo) ;
+ if( b_active ) {
+ buf->activate( dev, buf, dev->pb_curr, true );
+ }
+ dev->pb_next++;
+ }
+
+ list_for_each(item, &q->queued) {
+ buf = list_entry(item, struct tw686x_buf, vb.queue);
+ b_active = IS_TW686X_DMA_MODE_BLOCK ?
+ (dev->pb_next < dev->buf_needs) : (dev->pb_next<=buf->sg_ofo) ;
+ if( b_active ) {
+ item_temp = item;
+ item = item->prev;
+ list_del(item_temp);
+ buf->activate( dev, buf, dev->pb_curr, false );
+ }
+ dev->pb_next++;
+ }
+
+ if(dev->pb_next > 0) {
+ tw686x_dev_run( dev, true );
+ dev->dma_started = true;
+ }
+}
+
+static void tw686x_video_restart_timeout(unsigned long data)
+{
+ struct tw686x_vdev* dev = (struct tw686x_vdev*)data;
+ unsigned long flags;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ spin_lock_irqsave(dev->slock, flags);
+ dev->dma_started = false;
+ dev->pb_next = 0;
+ dev->pb_curr = 0;
+ tw686x_video_start(dev);
+ spin_unlock_irqrestore(dev->slock, flags);
+}
+
+int tw686x_video_restart(struct tw686x_vdev *dev, bool b_framerate)
+{
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+// assert_spin_locked(dev->slock);
+
+ tw686x_dev_run( dev, false );
+ dev->dma_started = false;
+ dev->pb_next = 0;
+ dev->pb_curr = 0;
+
+ if(b_framerate) {
+ tw686x_dev_set_framerate(dev, dev->framerate, dev->field);
+ }
+
+ if(tw686x_restart_timer) {
+ mod_timer(&dev->tm_restart, jiffies+(BUFFER_TIMEOUT/50));
+ }
+ else {
+ tw686x_video_start(dev);
+ }
+
+ return 0;
+}
+
+static int tw686x_set_tvnorm(struct tw686x_vdev *dev, v4l2_std_id norm)
+{
+ dvprintk(DPRT_LEVEL0, dev, "%s(norm = 0x%08x) name: [%s]\n",
+ __func__,
+ (unsigned int)norm,
+ v4l2_norm_to_name(norm));
+
+ mutex_lock(&dev->lock);
+ dev->tvnorm = norm;
+ tw686x_dev_set_standard( dev, norm, true );
+
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+ call_all(dev->chip, core, s_std, norm);
+#endif
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+static int tw686x_buffer_next(struct tw686x_vdev *dev,
+ struct tw686x_dmaqueue *q, int dma_pb)
+{
+ struct tw686x_buf *buf;
+ int ret = -EINVAL;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(%d)\n", __func__, dev->pb_next);
+
+// assert_spin_locked(dev->slock);
+ if (!list_empty(&q->queued)) {
+ /* activate next one from queue */
+ buf = list_entry(q->queued.next,struct tw686x_buf,vb.queue);
+ list_del(&buf->vb.queue);
+ ret = buf->activate(dev, buf, dma_pb, false);
+ }
+ else {
+ dvprintk(DPRT_LEVEL0, dev, "buffer_next %p\n",NULL);
+ }
+
+ return ret;
+}
+
+static bool tw686x_buffer_wakeup(struct tw686x_vdev *dev, struct tw686x_dmaqueue *q, u32 state)
+{
+ struct tw686x_buf *buf;
+ bool ret = false;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(%d)\n", __func__, state);
+
+// assert_spin_locked(dev->slock);
+ if( !list_empty(&q->active) ) {
+ /* finish current buffer */
+ buf = list_entry(q->active.next, struct tw686x_buf, active);
+ if( IS_TW686X_DMA_MODE_BLOCK || (state==VIDEOBUF_ERROR) ) {
+ buf->vb.state = state;
+ do_gettimeofday(&buf->vb.ts);
+ list_del(&buf->active);
+ wake_up(&buf->vb.done);
+ dev->pb_next--;
+ ret = true;
+ }
+ else {
+ buf->sg_bytes_to -= buf->sg_bytes_pb;
+ if( buf->sg_bytes_to>buf->sg_bytes_pb ) {
+ tw686x_dev_set_dmadesc( dev, dev->pb_curr, buf );
+ }
+ else if( buf->sg_bytes_to<=0 ) {
+ buf->vb.state = state;
+ do_gettimeofday(&buf->vb.ts);
+ list_del(&buf->active);
+ wake_up(&buf->vb.done);
+ dev->pb_next--;
+ if( !list_empty(&q->active) ) {
+ /* finish current buffer */
+ //notice!!! 这个地方与buffer_active里边同时开启的话,会造成dma descriptor重复填充
+ //buf = list_entry(q->active.next, struct tw686x_buf, active);
+ //tw686x_dev_set_dmadesc( dev, dev->pb_curr, buf );
+ }
+ else {
+ ret = true;
+ }
+ }
+ else {
+ ret = true;
+ }
+ }
+ dvprintk(DPRT_LEVEL0, dev, "wake up buffer %d, %d\n", buf->vb.i, buf->vb.state);
+ }
+ if( list_empty(&q->active) ) {
+ del_timer(&q->timeout);
+ }
+
+ return ret;
+}
+
+static int tw686x_buffer_cancel(struct tw686x_vdev *dev, struct tw686x_dmaqueue *q, u32 state)
+{
+ struct tw686x_buf *buf;
+ int ret = -EINVAL;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(%d)\n", __func__, dev->pb_next);
+
+// assert_spin_locked(dev->slock);
+ if( !list_empty(&q->queued) ) {
+ /* finish current buffer */
+ buf = list_entry(q->queued.next, struct tw686x_buf, vb.queue);
+ buf->vb.state = state;
+ do_gettimeofday(&buf->vb.ts);
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+ dev->pb_next--;
+ ret = true;
+ }
+
+ return ret;
+}
+
+static void tw686x_buffer_timeout(unsigned long data)
+{
+ struct tw686x_vdev *dev = (struct tw686x_vdev *)data;
+ struct tw686x_dmaqueue *q = &dev->video_q;
+ unsigned long flags;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ spin_lock_irqsave(dev->slock, flags);
+
+#if(1)
+ if( !res_locked(dev, RESOURCE_VIDEO) ) {
+ while (!list_empty(&q->active)) {
+ tw686x_buffer_wakeup( dev, q, VIDEOBUF_ERROR );
+ }
+ while (!list_empty(&q->queued)) {
+ tw686x_buffer_cancel( dev, q, VIDEOBUF_ERROR );
+ }
+ }
+ else {
+ tw686x_video_restart( dev, false );
+ }
+#else
+{
+ int pb_status = dev->vstatus;
+ if( !res_locked(dev, RESOURCE_VIDEO) ) {
+ while (!list_empty(&q->active)) {
+ tw686x_buffer_wakeup( dev, q, VIDEOBUF_ERROR );
+ }
+ while (!list_empty(&q->queued)) {
+ tw686x_buffer_cancel( dev, q, VIDEOBUF_ERROR );
+ }
+ pb_status = 0;
+ }
+ else {
+ if( !IS_TW686X_DMA_MODE_BLOCK ) {
+ dev->pb_curr = pb_status;
+ pb_status = !pb_status;
+ }
+ if( tw686x_buffer_wakeup(dev, &dev->video_q, VIDEOBUF_DONE) &&
+ (dev->pb_next>=dev->buf_needs) ) {
+ tw686x_buffer_next(dev, &dev->video_q, dev->pb_curr);
+ }
+ else if( dev->pb_next<=0 ) {
+ tw686x_dev_run( dev, false );
+ dev->dma_started = false;
+ dev->pb_next = 0;
+ dev->pb_curr = 0;
+ }
+ mod_timer(&q->timeout, jiffies+(BUFFER_TIMEOUT/20));
+ }
+ dev->vstatus = pb_status;
+}
+#endif
+
+ spin_unlock_irqrestore(dev->slock, flags);
+}
+
+static int buffer_activate(struct tw686x_vdev *dev, struct tw686x_buf *buf,
+ int dma_pb, bool queued)
+{
+ struct tw686x_dmaqueue *q = &dev->video_q;
+
+ dvprintk(DPRT_LEVEL0, dev, "activate buf=%d\n", buf->vb.i);
+
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ if(!queued) {
+ list_add_tail(&buf->active, &q->active);
+ }
+
+ if( IS_TW686X_DMA_MODE_BLOCK ) {
+ tw686x_dev_set_dmabuf(dev, dma_pb, buf);
+ }
+ else {
+ buf->sg = NULL;
+ buf->sg_offset = 0;
+ buf->sg_bytes_to = 0;
+ //if((dev->pb_next==0) || (buf->sg_ofo==0)) //notice!!! 这里可能造成dmadesc重复填充,需要再仔细斟酌
+ tw686x_dev_set_dmadesc(dev, dma_pb, buf);
+ }
+ dev->pb_curr = (dev->pb_curr + 1) % dev->buf_needs;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+
+ return 0;
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct videobuf_dmabuf *dma;
+ struct tw686x_fh *fh = vq->priv_data;
+ struct tw686x_vdev *dev = fh->dev;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(%d) state=%d\n", __func__, vb->i, vb->state);
+
+ BUG_ON(in_interrupt());
+
+#if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
+ videobuf_waiton(vq, vb, 0, 0);
+#else
+ videobuf_waiton(vb, 0, 0);
+#endif
+
+ if( IS_TW686X_DMA_MODE_BLOCK ) {
+ videobuf_dma_contig_free_isl(vq, vb);
+ }
+ else {
+ dma = videobuf_to_dma(vb);
+#if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
+ videobuf_dma_unmap(vq->dev, dma);
+#else
+ videobuf_dma_unmap(vq, dma);
+#endif
+ videobuf_dma_free(dma);
+ }
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct tw686x_fh *fh = vq->priv_data;
+ struct tw686x_vdev *dev = fh->dev;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(%d)\n", __func__, *count);
+
+ *size = (fh->fmt->depth*fh->width*fh->height) >> 3;
+
+ if (!*count || (*count > TW686X_BUFFER_COUNT)) {
+ *count = TW686X_BUFFER_COUNT;
+ }
+
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct tw686x_fh *fh = q->priv_data;
+ struct tw686x_vdev *dev = fh->dev;
+ struct tw686x_buf *buf= container_of(vb, struct tw686x_buf, vb);
+ int rc;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(%d)\n", __func__, vb->i);
+
+ BUG_ON(NULL == fh->fmt);
+
+ if (fh->width < TW686X_MIN_WIDTH || fh->width > norm_maxw(dev->tvnorm) ||
+ fh->height < TW686X_MIN_HEIGHT || fh->height > norm_maxh(dev->tvnorm))
+ return -EINVAL;
+
+ buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+ if(buf->vb.size <= TW686X_SG_PBLEN) {
+ buf->sg_ofo = 1;
+ buf->sg_bytes_pb = buf->vb.size;
+ }
+ else {
+ buf->sg_ofo = 0;
+ buf->sg_bytes_pb = buf->vb.size / ((buf->vb.size<=TW686X_SG_PBLEN*2) ? 2 : 4);
+ }
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ if (buf->fmt != fh->fmt ||
+ buf->vb.width != fh->width ||
+ buf->vb.height != fh->height ||
+ buf->vb.field != field) {
+ buf->fmt = fh->fmt;
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.field = field;
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (0 != rc)
+ goto fail;
+ }
+
+ dvprintk(DPRT_LEVEL0, dev, "[%p/%d] buffer_prepare - %dx%d %dbpp \"%s\"\n",
+ buf, buf->vb.i,
+ fh->width, fh->height, fh->fmt->depth, fh->fmt->name);
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ buf->activate = buffer_activate;
+
+ return 0;
+
+ fail:
+ buffer_release(q, vb);
+ return rc;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct tw686x_buf *buf = container_of(vb, struct tw686x_buf, vb);
+ struct tw686x_fh *fh = vq->priv_data;
+ struct tw686x_vdev *dev= fh->dev;
+ struct tw686x_dmaqueue *q = &dev->video_q;
+ bool b_active = false;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(%d)\n", __func__, vb->i);
+
+// assert_spin_locked(dev->slock);
+
+ b_active = IS_TW686X_DMA_MODE_BLOCK ?
+ (dev->pb_next < dev->buf_needs) : (dev->pb_next<=buf->sg_ofo) ;
+ if( b_active ) {
+ buf->activate( dev, buf, dev->pb_curr, false );
+ if( !dev->dma_started ) {
+ tw686x_dev_run( dev, true );
+ dev->dma_started = true;
+ }
+ }
+ else {
+ list_add_tail(&buf->vb.queue, &q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ }
+ dev->pb_next++;
+}
+
+static struct videobuf_queue_ops tw686x_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+static int video_open(struct file *file)
+{
+ struct tw686x_chip *chip;
+ struct tw686x_vdev *h=video_drvdata(file);
+ struct tw686x_vdev *dev = NULL;
+ struct tw686x_fh *fh;
+ struct list_head *list;
+ enum v4l2_buf_type type = 0;
+ int i = 0;
+
+ mutex_lock(&tw686x_chiplist_lock);
+ list_for_each(list, &tw686x_chiplist) {
+ chip = list_entry(list, struct tw686x_chip, chiplist);
+ for( i=0; i<chip->vid_count; i++ ) {
+ if (chip->vid_dev[i] == h) {
+ dev = h;
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ }
+ }
+ }
+ mutex_unlock(&tw686x_chiplist_lock);
+ if (NULL == dev) {
+ return -ENODEV;
+ }
+ chip = dev->chip;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s() minor=%d type=%s\n", __func__,
+ dev->video_dev->minor, v4l2_type_names[type]);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh) {
+ return -ENOMEM;
+ }
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->type = type;
+ fh->width = TW686X_DEFAULT_WIDTH;
+ fh->height = TW686X_DEFAULT_HEIGHT;
+ fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV);
+ fh->bytesperline = fh->width*2;
+ fh->ctl_bright = 0x00;
+ fh->ctl_contrast= 0x64;
+ fh->ctl_hue = 0x00;
+ fh->ctl_saturation = 0x80;
+ //v4l2_prio_open(&dev->prio,&fh->prio);
+
+ #if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
+ if( IS_TW686X_DMA_MODE_BLOCK ) {
+ videobuf_queue_dma_contig_init_isl(&fh->vidq, &tw686x_video_qops, &chip->pci->dev,
+ dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct tw686x_buf),
+ fh, NULL);
+ }
+ else {
+ videobuf_queue_sg_init(&fh->vidq, &tw686x_video_qops,
+ &chip->pci->dev, dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct tw686x_buf),
+ fh, NULL);
+ }
+#else
+ if( IS_TW686X_DMA_MODE_BLOCK ) {
+ videobuf_queue_dma_contig_init_isl(&fh->vidq, &tw686x_video_qops, &chip->pci->dev,
+ dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct tw686x_buf),
+ fh);
+ }
+ else {
+ videobuf_queue_sg_init(&fh->vidq, &tw686x_video_qops,
+ &chip->pci->dev, dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct tw686x_buf),
+ fh);
+ }
+#endif
+
+ return 0;
+}
+
+static ssize_t video_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct tw686x_fh *fh = file->private_data;
+ struct tw686x_vdev *dev= fh->dev;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ if (res_locked(dev,RESOURCE_VIDEO))
+ return -EBUSY;
+
+ return videobuf_read_one(&fh->vidq, data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int video_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct tw686x_fh *fh = file->private_data;
+ struct tw686x_buf *buf= NULL;
+ unsigned int rc = POLLERR;
+ struct tw686x_vdev *dev= fh->dev;
+
+ mutex_lock(&fh->vidq.vb_lock);
+
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ /* streaming capture */
+ if (list_empty(&fh->vidq.stream))
+ goto done;
+ buf = list_entry(fh->vidq.stream.next,
+ struct tw686x_buf, vb.stream);
+ } else {
+ /* read() capture */
+ buf = (struct tw686x_buf *)fh->vidq.read_buf;
+ if (NULL == buf)
+ goto done;
+ }
+
+ poll_wait(file, &buf->vb.done, wait);
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ rc = POLLIN|POLLRDNORM;
+ else
+ rc = 0;
+
+done:
+ mutex_unlock(&fh->vidq.vb_lock);
+ dvprintk(DPRT_LEVEL0, dev, "%s() return %d\n", __func__, rc);
+ return rc;
+}
+
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+static int video_release(struct file *file)
+#else
+static int video_release(struct inode *inode, struct file *file)
+#endif
+{
+ struct tw686x_fh *fh = file->private_data;
+ struct tw686x_vdev *dev = fh->dev;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ /* stop video capture */
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ res_free(dev,fh,RESOURCE_VIDEO);
+ tw686x_dev_run(dev, false);
+ videobuf_streamoff(&fh->vidq);
+ }
+
+ if (fh->vidq.read_buf) {
+ buffer_release(&fh->vidq, fh->vidq.read_buf);
+ kfree(fh->vidq.read_buf);
+ }
+
+ videobuf_mmap_free(&fh->vidq);
+ file->private_data = NULL;
+ kfree(fh);
+
+ return 0;
+}
+
+static int video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct tw686x_fh *fh = file->private_data;
+ struct tw686x_vdev *dev= fh->dev;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ return videobuf_mmap_mapper(&fh->vidq, vma);
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO IOCTLS */
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->vidq.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+ struct tw686x_fmt *fmt;
+ enum v4l2_field field;
+ unsigned int maxw, maxh;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(fourcc=0x%x, field=%d, %dx%d)\n", __func__,
+ f->fmt.pix.pixelformat, f->fmt.pix.field, f->fmt.pix.width, f->fmt.pix.height);
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ maxw = norm_maxw(dev->tvnorm);
+ maxh = norm_maxh(dev->tvnorm);
+ if(f->fmt.pix.width == -1) {
+ f->fmt.pix.width = fh->width;
+ }
+ if(f->fmt.pix.height == -1) {
+ f->fmt.pix.height = fh->height;
+ }
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ f->fmt.pix.field = field;
+ f->fmt.pix.width &= ~0x0F;
+ f->fmt.pix.height&= ~0x0F;
+
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+ v4l_bound_align_image(&f->fmt.pix.width, TW686X_MIN_WIDTH, maxw, 2,
+ &f->fmt.pix.height, TW686X_MIN_HEIGHT, maxh, 0, 0);
+#else
+ if (f->fmt.pix.height < TW686X_MIN_HEIGHT)
+ f->fmt.pix.height = TW686X_MIN_HEIGHT;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+ if (f->fmt.pix.width < TW686X_MIN_WIDTH)
+ f->fmt.pix.width = TW686X_MIN_WIDTH;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ f->fmt.pix.width &= ~0x03;
+#endif
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+ int err;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ err = vidioc_try_fmt_vid_cap(file, priv, f);
+ if (0 != err)
+ return err;
+
+ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ fh->bytesperline = f->fmt.pix.bytesperline;
+ fh->vidq.field = f->fmt.pix.field;
+ dev->field = fh->vidq.field;
+ if( IS_TW686X_DMA_MODE_BLOCK ) {
+ dev->buf_needs = (V4L2_FIELD_HAS_BOTH(dev->field) ? 2 : 4);
+ }
+ else {
+ dev->buf_needs = 2;
+ }
+
+ dvprintk(2, dev, "%s() width=%d height=%d field=%d\n", __func__,
+ fh->width, fh->height, fh->vidq.field);
+
+ tw686x_dev_set_pixelformat( dev, f->fmt.pix.pixelformat );
+
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+#if(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35))
+ call_all(dev->chip, video, s_fmt, f);
+#endif
+#endif
+
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct tw686x_vdev *dev = ((struct tw686x_fh *)priv)->dev;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ sprintf(cap->driver, "%s", "tw6869");
+ sprintf(cap->card, "%s", "TW6869 PC-DVR");
+ sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->chip->pci));
+ cap->version = TW686X_VERSION_CODE;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(%d)\n", __func__, f->index);
+
+ if (unlikely(f->index >= ARRAY_SIZE(formats)))
+ return -EINVAL;
+
+ strlcpy(f->description, formats[f->index].name,
+ sizeof(f->description));
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+static int vidiocgmbuf(struct file *file, void *priv,
+ struct video_mbuf *mbuf)
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ return 0;
+}
+#endif
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ return videobuf_reqbufs(&fh->vidq, p);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ return videobuf_querybuf(&fh->vidq, p);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ return videobuf_qbuf(&fh->vidq, p);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ return videobuf_dqbuf(&fh->vidq, p,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+
+ dvprintk(1, dev, "%s()\n", __func__);
+
+ if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
+ return -EINVAL;
+ if (unlikely(i != fh->type))
+ return -EINVAL;
+ if (unlikely(!res_get(dev, fh, RESOURCE_VIDEO)))
+ return -EBUSY;
+
+ dev->pb_curr = 0;
+ dev->pb_next = 0;
+ dev->dma_started = false;
+ dev->chip->daemon_nr = 0;
+ dev->chip->daemon_tor= 0;
+ do_gettimeofday( &dev->chip->daemon_tv );
+ if( dev->framerate==0 ) {
+ dev->framerate = TW686X_MAX_FRAMERATE(dev->tvnorm);
+ }
+
+ tw686x_dev_set_dma( dev, fh->width, fh->height, (fh->width*fh->fmt->depth)>>3 );
+ tw686x_dev_set_framerate( dev, dev->framerate, fh->vidq.field );
+
+ return videobuf_streamon(&fh->vidq);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+
+ dvprintk(1, dev, "%s(%d)\n", __func__, fh->vidq.streaming);
+
+ if (!res_check(fh, RESOURCE_VIDEO))
+ return 0;
+
+ res_free(dev, fh, RESOURCE_VIDEO);
+
+ if( IS_TW686X_DMA_MODE_BLOCK && !fh->vidq.streaming) {
+ return 0;
+ }
+
+ return videobuf_streamoff(&fh->vidq);
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorms)
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev = fh->dev;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ *tvnorms = dev->tvnorm;
+
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id* id)
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev = fh->dev;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(%08x)\n", __func__, (u32)*id);
+
+ tw686x_set_tvnorm(dev, *id);
+ if( dev->framerate==0 ) {
+ dev->framerate = TW686X_MAX_FRAMERATE(*id);
+ }
+
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ unsigned int n;
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+ u32 vstatus = 0;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(%d-%d)\n", __func__, i->index, dev->input);
+
+ n = i->index;
+ if(tw686x_bcsi) {
+ n -= 1;
+ }
+
+ if(dev->channel_id<TW686X_DECODER_COUNT) {
+ if (n >= TW6864_MUX_COUNT)
+ return -EINVAL;
+ }
+ else {
+ return -EINVAL;
+ }
+
+ memset(i, 0, sizeof(*i));
+ i->index = n;
+ if(tw686x_bcsi) {
+ i->index += 1;
+ }
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ sprintf(i->name, "Composite%d", n);
+ i->std = TW686X_NORMS;
+ if(n == dev->input) {
+ vstatus = dev->vstatus;
+ if (0 != (vstatus & BIT(7)))
+ i->status |= V4L2_IN_ST_NO_SIGNAL;
+ if (0 == (vstatus & BIT(6)))
+ i->status |= V4L2_IN_ST_NO_H_LOCK;
+ if (0 != (vstatus & BIT(1)))
+ i->status |= V4L2_IN_ST_NO_COLOR;
+ }
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+
+ *i = dev->input;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s() returns %d\n", __func__, *i);
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(%d)\n", __func__, i);
+
+ if( dev->channel_id<TW686X_DECODER_COUNT ) {
+ if (i >= TW6864_MUX_COUNT) {
+ dvprintk(1, dev, "%s(%d) -EINVAL\n", __func__, i);
+ return -EINVAL;
+ }
+ }
+ else if (i >= TW2865_MUX_COUNT) {
+ dvprintk(1, dev, "%s(%d) -EINVAL\n", __func__, i);
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->lock);
+ dev->input = i;
+ tw686x_dev_set_mux(dev, i);
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+ const struct v4l2_queryctrl *ctrl;
+
+ dvprintk(DPRT_LEVEL0, dev, "%s()\n", __func__);
+
+ if ((c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1))
+ return -EINVAL;
+ ctrl = ctrl_by_id(c->id);
+ *c = (NULL != ctrl) ? *ctrl : no_ctrl;
+
+ return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *c)
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+ const struct v4l2_queryctrl* ctrl;
+
+ ctrl = ctrl_by_id(c->id);
+ if (NULL == ctrl)
+ return -EINVAL;
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->value = fh->ctl_bright;
+ break;
+ case V4L2_CID_HUE:
+ c->value = fh->ctl_hue;
+ break;
+ case V4L2_CID_CONTRAST:
+ c->value = fh->ctl_contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ c->value = fh->ctl_saturation;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dvprintk(DPRT_LEVEL0, dev, "%s(id=%x) return %d\n", __func__, c->id, c->value);
+
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *c)
+{
+ const struct v4l2_queryctrl* ctrl;
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+
+ mutex_lock(&dev->lock);
+ ctrl = ctrl_by_id(c->id);
+ if (NULL == ctrl) {
+ mutex_unlock(&dev->lock);
+ return -EINVAL;
+ }
+
+ dvprintk(DPRT_LEVEL0, dev, "set_control name=%s val=%d\n",ctrl->name,c->value);
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ case V4L2_CTRL_TYPE_MENU:
+ case V4L2_CTRL_TYPE_INTEGER:
+ if (c->value < ctrl->minimum)
+ c->value = ctrl->minimum;
+ if (c->value > ctrl->maximum)
+ c->value = ctrl->maximum;
+ break;
+ default:
+ /* nothing */;
+ };
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ fh->ctl_bright = c->value;
+ tw686x_dev_set_bright( dev, fh->ctl_bright );
+ break;
+ case V4L2_CID_HUE:
+ fh->ctl_hue = c->value;
+ tw686x_dev_set_hue( dev, fh->ctl_hue );
+ break;
+ case V4L2_CID_CONTRAST:
+ fh->ctl_contrast = c->value;
+ tw686x_dev_set_contrast( dev, fh->ctl_contrast );
+ break;
+ case V4L2_CID_SATURATION:
+ fh->ctl_saturation = c->value;
+ tw686x_dev_set_saturation( dev, fh->ctl_saturation );
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+static int vidioc_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+#else
+static int vidioc_g_register(struct file *file, void *priv,
+ struct v4l2_register *reg)
+#endif
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+ struct tw686x_chip *chip = dev->chip;
+
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+#else
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+#endif
+
+ if( reg->reg >= 0x800 ) {
+ return -EINVAL;
+ }
+
+ reg->reg &= ~0x03;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ call_all(dev, core, g_register, reg);
+#else
+ reg->val = tw_read( reg->reg );
+#endif
+
+ //dvprintk(1, dev, "%s(0x%08x) = 0x%08x\n", __func__, (u32)reg->reg, (u32)reg->val);
+
+ return 0;
+}
+
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+static int vidioc_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+#else
+static int vidioc_s_register(struct file *file, void *priv,
+ struct v4l2_register *reg)
+#endif
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+ struct tw686x_chip *chip = dev->chip;
+
+ //dvprintk(1, dev, "%s(0x%08x, 0x%08x)\n", __func__, (u32)reg->reg, (u32)reg->val);
+
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+#else
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+#endif
+
+ if( reg->reg >= 0x800 ) {
+ return -EINVAL;
+ }
+
+ reg->reg &= ~0x03;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ call_all(dev, core, s_register, reg);
+#else
+ tw_write( reg->reg, reg->val );
+#endif
+
+ return 0;
+}
+
+/* For other private ioctls */
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,38))
+static long vidioc_tw686x(struct file *file, void *priv,
+ bool valid_prio, int cmd, void *arg)
+#elif(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+static long vidioc_tw686x(struct file *file, void *priv,
+ int cmd, void *arg)
+#else
+static int vidioc_tw686x(struct file *file, void *priv,
+ int cmd, void *arg)
+#endif
+{
+ struct tw686x_fh *fh = priv;
+ struct tw686x_vdev *dev= fh->dev;
+ long ret = -EINVAL;
+
+ //dvprintk(1, dev, "%s()\n", __func__);
+
+ switch( cmd ) {
+ case TW686X_S_REGISTER:
+ ret = vidioc_s_register( file, priv, arg );
+ break;
+ case TW686X_G_REGISTER:
+ ret = vidioc_g_register( file, priv, arg );
+ break;
+ case TW686X_S_FRAMERATE:
+ dev->framerate = *(long*)arg;
+ tw686x_dev_set_framerate( dev, dev->framerate, fh->vidq.field );
+ ret = 0;
+ break;
+ case TW686X_G_FRAMERATE:
+ *(long*)arg = dev->framerate;
+ ret = 0;
+ break;
+ case TW686X_G_SIGNAL:
+ *(long*)arg = tw686x_dev_get_signal( dev );
+ ret = 0;
+ break;
+#if(CaptureFLV)
+ case TW686X_S_AUDIO:
+ ret = tw686x_audio_param( dev->chip->aud_dev[dev->channel_id], (struct tw686x_aparam*)arg);
+ break;
+ case TW686X_G_AUDIO:
+ ret = tw686x_audio_data( dev->chip->aud_dev[dev->channel_id], (struct tw686x_adata*)arg);
+ break;
+#endif
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+int tw686x_video_resetdma(struct tw686x_chip *chip, u32 dma_cmd)
+{
+#if(TW6869_RESETDMA)
+ chip->dma_restart = dma_cmd;
+ mod_timer(&chip->tm_assist, jiffies+(BUFFER_TIMEOUT/25));
+#else
+ struct tw686x_vdev *dev = NULL;
+ int i=0;
+
+ for(i=0; i<chip->vid_count; i++) {
+ if(dma_cmd & (1<<i)) {
+ dev = chip->vid_dev[i];
+ tw686x_video_restart( dev, false );
+ }
+ }
+#endif
+
+ return 0;
+}
+
+int tw686x_video_daemon(struct tw686x_chip *chip, u32 dma_error)
+{
+ int i, should_dropdown = 0;
+ struct tw686x_vdev *dev = NULL;
+ struct timeval tv_now;
+ long long tv_diff = 0;
+ long ms_diff = 0;
+
+ do_gettimeofday( &tv_now );
+ tv_diff = 1000000 * ( tv_now.tv_sec - chip->daemon_tv.tv_sec ) +
+ tv_now.tv_usec - chip->daemon_tv.tv_usec;
+ ms_diff = (long)tv_diff;
+ ms_diff/= 1000;
+
+ for( i=0; i<chip->vid_count; i++ ) {
+ if( dma_error&TW6864_R_DMA_VINT_FIFO(i) ) {
+ chip->daemon_nr++;
+ }
+ }
+
+ if( chip->daemon_nr >= TW686X_DAEMON_THRESHOLD ) {
+ chip->daemon_tor++;
+ chip->daemon_nr = 0;
+ }
+
+ if(ms_diff >= TW686X_DAEMON_PERIOD) {
+ should_dropdown = (chip->daemon_tor >= TW686X_DAEMON_TOLERANCE);
+ chip->daemon_tv = tv_now;
+ chip->daemon_tor= 0;;
+ chip->daemon_nr = 0;
+ }
+
+ if( !should_dropdown ) {
+ for( i=0; i<chip->vid_count; i++ ) {
+ dev = chip->vid_dev[i];
+ if( dma_error&TW6864_R_DMA_VINT_FIFO(i) ) {
+ tw686x_video_restart( dev, false );
+ }
+ }
+ }
+ else {
+ for( i=0; i<chip->vid_count; i++ ) {
+ dev = chip->vid_dev[i];
+ if(dev->framerate > TW686X_DAEMON_MIN) {
+ dev->framerate--;
+ tw686x_video_restart( dev, true );
+ }
+ else if( dma_error&TW6864_R_DMA_VINT_FIFO(i) ) {
+ tw686x_video_restart( dev, false );
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------- */
+
+int tw686x_video_irq(struct tw686x_vdev *dev, u32 dma_status, u32 pb_status)
+{
+/*liran for debug
+ struct tw686x_chip *chip = dev->chip;
+
+ dvprintk(DPRT_LEVEL0, dev, "irq buf=0x%08x\n", ibuf==0 ?
+ tw_read(TW6864_R_BDMA(TW6864_R_BDMA_ADDR_P_0, dev->channel_id)) :
+ tw_read(TW6864_R_BDMA(TW6864_R_BDMA_ADDR_B_0, dev->channel_id)));
+*/
+
+ if( !res_locked(dev, RESOURCE_VIDEO) ) {
+ tw686x_dev_run( dev, false );
+ while (!list_empty(&dev->video_q.active)) {
+ tw686x_buffer_wakeup( dev, &dev->video_q, VIDEOBUF_ERROR );
+ }
+ while (!list_empty(&dev->video_q.queued)) {
+ tw686x_buffer_cancel( dev, &dev->video_q, VIDEOBUF_ERROR );
+ }
+ }
+ else {
+ if( !IS_TW686X_DMA_MODE_BLOCK ) {
+ dev->pb_curr = (pb_status >> dev->channel_id) & 1;
+ }
+ if( tw686x_buffer_wakeup(dev, &dev->video_q, VIDEOBUF_DONE) ) {
+ }
+ if(dev->pb_next>0) {
+ tw686x_buffer_next(dev, &dev->video_q, dev->pb_curr);
+ }
+ else {
+ tw686x_dev_run( dev, false );
+ dev->dma_started = false;
+ dev->pb_next = 0;
+ dev->pb_curr = 0;
+ }
+ }
+
+ return 0;
+}
+
+int tw686x_video_status(struct tw686x_vdev *dev)
+{
+ dev->vstatus = tw686x_dev_get_decoderstatus(dev);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------- */
+/* exported stuff */
+
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+static const struct v4l2_file_operations video_fops = {
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .read = video_read,
+ .poll = video_poll,
+ .mmap = video_mmap,
+ .ioctl = video_ioctl2,
+};
+#else
+static const struct file_operations video_fops = {
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .read = video_read,
+ .poll = video_poll,
+ .mmap = video_mmap,
+ .ioctl = video_ioctl2,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+#endif
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+ .vidiocgmbuf = vidiocgmbuf,
+#endif
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+ .vidioc_default = vidioc_tw686x,
+};
+
+static struct video_device tw686x_video_template = {
+ .name = "tw6869-video",
+ .fops = &video_fops,
+ .minor = -1,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = TW686X_NORMS,
+ .current_norm = V4L2_STD_NTSC,
+};
+
+int tw686x_video_register(struct tw686x_vdev *dev)
+{
+ int err;
+ struct video_device *vfd;
+
+ dvprintk(1, dev, "%s()\n", __func__);
+
+ mutex_init(&dev->lock);
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->video_q.active);
+ INIT_LIST_HEAD(&dev->video_q.queued);
+ dev->video_q.timeout.function = tw686x_buffer_timeout;
+ dev->video_q.timeout.data = (unsigned long)dev;
+ init_timer(&dev->video_q.timeout);
+ dev->tm_restart.function = tw686x_video_restart_timeout;
+ dev->tm_restart.data = (unsigned long)dev;
+ init_timer(&dev->tm_restart);
+
+ dev->tvnorm = TW686X_DEFAULT_STANDARD;
+ dev->field = V4L2_FIELD_TOP;
+
+ /* create v4l devices */
+ vfd = video_device_alloc();
+ if (vfd) {
+ *vfd = tw686x_video_template;
+ vfd->minor = -1;
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+ vfd->v4l2_dev = &dev->chip->v4l2_dev;
+#endif
+ vfd->release = video_device_release;
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s %d",
+ dev->chip->name, "video", dev->channel_id);
+ video_set_drvdata(vfd, dev);
+ }
+
+ /* register v4l devices */
+ dev->video_dev = vfd;
+ err = video_register_device(dev->video_dev, VFL_TYPE_GRABBER, tw686x_vidcount++);
+ if (err < 0) {
+ printk(KERN_INFO "%s: can't register video device %d\n", dev->chip->name, tw686x_vidcount);
+ goto fail_unreg;
+ }
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+ printk(KERN_INFO "%s: registered device video %d [v4l2]\n",
+ dev->chip->name, dev->video_dev->num);
+#else
+ printk(KERN_INFO "%s: registered device video %d [v4l2]\n",
+ dev->chip->name, dev->video_dev->minor & 0x1f);
+#endif
+
+ /* initial device configuration */
+ tw686x_set_tvnorm(dev, dev->tvnorm);
+ tw686x_dev_set_mux(dev, 0);
+
+ return 0;
+
+fail_unreg:
+ tw686x_video_unregister(dev);
+ return err;
+}
+
+void tw686x_video_unregister(struct tw686x_vdev *dev)
+{
+ dvprintk(1, dev, "%s()\n", __func__);
+
+ if (dev->video_dev) {
+ if (-1 != dev->video_dev->minor)
+ video_unregister_device(dev->video_dev);
+ else
+ video_device_release(dev->video_dev);
+ dev->video_dev = NULL;
+ }
+}
diff --git a/drivers/pci/pcie/tw6869/tw686x.h b/drivers/pci/pcie/tw6869/tw686x.h
new file mode 100644
index 000000000000..6a7f58b58e8f
--- /dev/null
+++ b/drivers/pci/pcie/tw6869/tw686x.h
@@ -0,0 +1,415 @@
+/*
+ * Driver for Techwell TW6864/68 based DVR cards
+ *
+ * (c) 2009-10 liran <jlee@techwellinc.com.cn> [Techwell China]
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/videodev2.h>
+#include <linux/kdev_t.h>
+#include <linux/input.h>
+#include <linux/notifier.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+
+#include <asm/io.h>
+#include <linux/version.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+#include <media/v4l2-device.h>
+#endif
+#include <media/videobuf-dma-sg.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "videobuf-dma-contig-tw.h"
+
+#define TW686X_VERSION_CODE KERNEL_VERSION(0, 1, 4)
+#define TW6869_RESETDMA 1
+
+#include "i2c-sw.h"
+
+#define I2C_SW_TW686X 0x0A0000 /* TW686X video decoder bus */
+
+extern unsigned int tw686x_bcsi; /* for BCSI */
+extern unsigned int tw686x_restart_timer; /* dma restart timer */
+extern unsigned int tw686x_dmamode;
+extern unsigned int tw686x_debug;
+extern unsigned int tw686x_6865; //zxx_20120712 added for TW6865
+#define dprintk(level, chip, fmt, arg...)\
+ do { if (tw686x_debug >= level)\
+ printk(KERN_DEBUG "%s: " fmt, (chip)->name, ## arg);\
+ } while (0)
+#define dvprintk(level, dev, fmt, arg...)\
+ do { if (tw686x_debug >= level)\
+ printk(KERN_DEBUG "%s[v-%d]: " fmt, ((dev)->chip)->name, (dev)->channel_id, ## arg);\
+ } while (0)
+#define daprintk(level, dev, fmt, arg...)\
+ do { if (tw686x_debug >= level)\
+ printk(KERN_DEBUG "%s[a-%d]: " fmt, ((dev)->chip)->name, (dev)->channel_id, ## arg);\
+ } while (0)
+
+#define dvcount(level, dev, cnt, cnt_out) \
+{ \
+ if(tw686x_debug >= level) { \
+ static struct timeval _tv_begin_##cnt = {0,0}; \
+ static long _cnt_##cnt = 0; \
+ struct timeval _tv_now_##cnt; \
+ long long _tv_diff_##cnt = 0; \
+ long _ms_diff_##cnt = 0; \
+ do_gettimeofday( &_tv_now_##cnt ); \
+ _tv_diff_##cnt = 1000000 * ( _tv_now_##cnt.tv_sec - _tv_begin_##cnt.tv_sec ) + \
+ _tv_now_##cnt.tv_usec - _tv_begin_##cnt.tv_usec; \
+ _ms_diff_##cnt = (long)_tv_diff_##cnt; \
+ _ms_diff_##cnt /= 1000; \
+ _cnt_##cnt++; \
+ if(_ms_diff_##cnt >= 1000) { \
+ dvprintk(1, dev, "%s-%d %d\n", cnt_out, (int)_cnt_##cnt, (int)_ms_diff_##cnt); \
+ _tv_begin_##cnt = _tv_now_##cnt; \
+ _cnt_##cnt = 0; \
+ } \
+ } \
+}
+
+#define DPRT_LEVEL0 11
+
+#define RESOURCE_VIDEO 2
+#define BUFFER_TIMEOUT (HZ) /* 0.5 seconds */
+
+/* ioctl for tw686x */
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+#define TW686X_S_REGISTER _IOWR('V', (BASE_VIDIOC_PRIVATE + 1), struct v4l2_dbg_register)
+#define TW686X_G_REGISTER _IOWR('V', (BASE_VIDIOC_PRIVATE + 2), struct v4l2_dbg_register)
+#else
+#define TW686X_S_REGISTER _IOWR('V', (BASE_VIDIOC_PRIVATE + 1), struct v4l2_register)
+#define TW686X_G_REGISTER _IOWR('V', (BASE_VIDIOC_PRIVATE + 2), struct v4l2_register)
+#endif
+#define TW686X_S_FRAMERATE _IOWR('V', (BASE_VIDIOC_PRIVATE + 3), long)
+#define TW686X_G_FRAMERATE _IOWR('V', (BASE_VIDIOC_PRIVATE + 4), long)
+#define TW686X_G_SIGNAL _IOWR('V', (BASE_VIDIOC_PRIVATE + 5), long)
+
+#define TW686X_S_AUDIO _IOWR('V', (BASE_VIDIOC_PRIVATE + 6), struct tw686x_aparam)
+#define TW686X_G_AUDIO _IOWR('V', (BASE_VIDIOC_PRIVATE + 7), struct tw686x_adata)
+
+#define TW686X_DAEMON_PERIOD 2000 /*2s*/
+#define TW686X_DAEMON_THRESHOLD 16 /*2s*/
+#define TW686X_DAEMON_TOLERANCE 2 /*2 times*/
+#define TW686X_DAEMON_MIN 15 /*min frame rate*/
+
+#define TW686X_DMA_MODE_SG 0 /* 0--scatter gather, 1--block dma*/
+#define TW686X_DMA_MODE_BLOCK 1
+#define IS_TW686X_DMA_MODE_BLOCK (tw686x_dmamode & TW686X_DMA_MODE_BLOCK)
+#define TW686X_DMA_DESC_LEN 4096
+#define TW686X_DMA_DESC_P 0
+#define TW686X_DMA_DESC_B 1
+#define TW686X_DMA_DESC_NUM 2
+#define TW686X_DMA_DESC_UNIT 512
+#define TW686X_DMA_DESC_UNIT_LEN (TW686X_DMA_DESC_UNIT*8)
+#define TW686X_DMA_DESC(dev, pb) ((struct tw686x_dmadesc*)((unsigned long)(dev)->dma_desc[TW686X_DMA_DESC_##pb].cpu))
+#define TW686X_SG_PBLEN (TW686X_DMA_DESC_UNIT*TW686X_DMA_DESC_LEN)
+
+#define TW686X_DECODER_COUNT 8
+#define TW6864_MUX_COUNT 1
+#define TW2865_MUX_COUNT 1
+#define TW2864_MUX_COUNT 1
+#define TW686X_VIDEO_COUNT 8
+#define TW686X_AUDIO_COUNT 8
+#define TW686X_AUDIO_BEGIN 8
+#define TW686X_BUFFER_COUNT 9
+#define TW686X_DEFAULT_STANDARD V4L2_STD_NTSC
+#define TW686X_DEFAULT_WIDTH 704
+#define TW686X_DEFAULT_HEIGHT 480
+#define TW686X_REMOVE_BLACKSTRIPE 0 //(1 << 31)
+#define TW686X_VIDEO_SIZE(w,h) ((w) | ((h)<<16) | (1<<31))
+#define TW686X_VIDEO_SIZE_F2(w,h) ((w) | ((h)<<16)) //Field2 size, Available only for Rev.B or later
+#define TW686X_MAX_FRAMERATE(std) (IS_PAL(std) ? 25 : 30)
+#define TW686X_MIN_WIDTH 160
+#define TW686X_MIN_HEIGHT 120
+
+/* Currently unsupported by the driver: PAL/H, NTSC/Kr*/
+#define TW686X_NORMS (V4L2_STD_NTSC | V4L2_STD_PAL)
+
+#define IS_PAL( std ) ( (std) & V4L2_STD_PAL )
+
+struct tw686x_fmt {
+ char *name;
+ u32 fourcc; /* v4l2 format id */
+ int depth;
+ int flags;
+};
+
+struct tw686x_dmadesc {
+ u32 ctrl; //DO NOT CHANGE SEQUENCE, and it is valid for little endian target PC only
+ u32 addr;
+};
+
+struct tw686x_fh {
+ struct tw686x_vdev *dev;
+ enum v4l2_buf_type type;
+ u32 resources;
+ struct videobuf_queue vidq;
+
+ /* video capture */
+ struct tw686x_fmt *fmt;
+ int width;
+ int height;
+ int bytesperline;
+ int ctl_bright;
+ int ctl_contrast;
+ int ctl_hue;
+ int ctl_saturation;
+};
+
+/* buffer for one video frame */
+struct tw686x_buf {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ /* tw686x specific */
+ struct tw686x_fmt* fmt;
+ struct list_head active;
+
+ int sg_bytes_to;
+ int sg_bytes_pb;
+ struct scatterlist *sg;
+ int sg_offset;
+ int sg_ofo; /*one p/b buffer for one frame*/
+
+ int (*activate)(struct tw686x_vdev *dev, struct tw686x_buf *buf,
+ int dma_pb, bool queued);
+};
+
+struct tw686x_dmaqueue {
+ struct list_head active;
+ struct list_head queued;
+ struct timer_list timeout;
+};
+
+/* buffer for dma */
+struct tw686x_dmabuf {
+ unsigned int size;
+ __le32 *cpu;
+ dma_addr_t dma;
+ struct vm_area_struct *vma;
+};
+
+struct tw686x_i2c {
+ struct i2c_sw i2c;
+ u32 control_word;
+};
+
+/*
+ * audio main chip structure
+ */
+typedef struct snd_card_tw686x {
+ struct snd_card *card;
+// struct pci_dev *pci;
+ struct tw686x_adev *dev;
+
+ u16 mute_was_on;
+
+ spinlock_t lock;
+} snd_card_tw686x_t;
+
+struct tw686x_chip {
+ struct list_head chiplist;
+ spinlock_t slock;
+
+ /* pci i/o */
+ char name[32];
+ int nr;
+ u32 dma_restart;
+
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+ struct v4l2_device v4l2_dev;
+#endif
+ struct pci_dev *pci;
+ __u8 __iomem *bmmio;
+
+ /* i2c i/o */
+ struct i2c_adapter i2c_adap;
+ struct i2c_client i2c_client;
+ struct tw686x_i2c i2c_686x;
+
+ struct tw686x_vdev *vid_dev[TW686X_VIDEO_COUNT];
+ int vid_count;
+ struct timeval daemon_tv;
+ int daemon_nr;
+ int daemon_tor;
+
+ struct tw686x_adev *aud_dev[TW686X_AUDIO_COUNT];
+ int aud_count;
+
+ //assist timer, for video decoder status checking
+ struct timer_list tm_assist;
+};
+
+/* global device status */
+struct tw686x_vdev {
+ struct tw686x_chip* chip;
+
+ struct list_head devlist;
+ struct mutex lock;
+ spinlock_t* slock;
+ struct v4l2_subdev *sd_tw286x;
+
+ /* various device info */
+ unsigned int resources;
+ struct video_device *video_dev;
+ struct tw686x_dmaqueue video_q;
+ struct tw686x_dmabuf dma_vb[TW686X_BUFFER_COUNT];
+ struct tw686x_dmabuf dma_desc[TW686X_DMA_DESC_NUM];
+ struct timer_list tm_restart;
+
+ /* various v4l controls */
+ v4l2_std_id tvnorm;
+ unsigned int input;
+
+ /* other global state info */
+ u32 vstatus;
+
+ /* for dma buffer queue */
+ int buf_needs;
+ int pb_next;
+ int pb_curr;
+ int dma_started;
+ int channel_id;
+ int field;
+ int framerate;
+};
+
+/* dmasound dsp status */
+struct tw686x_adev {
+ struct tw686x_chip* chip;
+ int channel_id;
+ snd_card_tw686x_t * card;
+
+ struct mutex lock;
+ spinlock_t* slock;
+ int minor_dsp;
+ unsigned int users_dsp;
+
+ /* dsp */
+ unsigned int rate;
+ unsigned int blocks;
+ unsigned int blksize;
+ unsigned int bufsize;
+ struct videobuf_dmabuf dma;
+ unsigned int read_offset;
+ struct snd_pcm_substream *substream;
+
+ unsigned int dma_blk;
+ unsigned int pb_flag;
+ unsigned int period_idx;
+ bool running;
+};
+
+struct tw686x_aparam {
+ int sample_rate;
+ int sample_bits;
+ int channels;
+ int blksize;
+};
+
+struct tw686x_adata {
+ unsigned char data[4092];
+ int len;
+};
+
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30))
+static inline struct tw686x_chip *to_tw686x(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct tw686x_chip, v4l2_dev);
+}
+#endif
+
+/* ----------------------------------------------------------- */
+
+#define tw_read(reg) readl(chip->bmmio + (reg))
+#define tw_write(reg, value) writel((value), chip->bmmio + (reg))
+
+#define tw_andor(reg, mask, value) \
+ writel((readl(chip->bmmio+(reg)) & ~(mask)) |\
+ ((value) & (mask)), chip->bmmio+(reg))
+
+#define tw_set(reg, bit) tw_andor((reg), (bit), (bit))
+#define tw_clear(reg, bit) tw_andor((reg), (bit), 0)
+
+#define call_all(dev, o, f, args...) \
+ v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
+
+extern struct mutex tw686x_chiplist_lock;
+extern struct list_head tw686x_chiplist;
+
+/* ----------------------------------------------------------- */
+/* tw686x-core.c */
+int tw686x_dmabuf_alloc(struct tw686x_chip *chip,
+ struct tw686x_dmabuf *buf, unsigned int size);
+void tw686x_dmabuf_free(struct tw686x_chip *chip, struct tw686x_dmabuf *buf);
+
+/* ----------------------------------------------------------- */
+/* tw686x-video.c */
+/* Video */
+extern int tw686x_video_register(struct tw686x_vdev *dev);
+extern void tw686x_video_unregister(struct tw686x_vdev *dev);
+extern int tw686x_video_irq(struct tw686x_vdev *dev, u32 dma_status, u32 pb_status);
+extern int tw686x_video_daemon(struct tw686x_chip *chip, u32 dma_error);
+extern int tw686x_video_status(struct tw686x_vdev *dev);
+extern int tw686x_video_resetdma(struct tw686x_chip* chip, u32 dma_cmd);
+extern int tw686x_video_restart(struct tw686x_vdev *dev, bool b_framerate);
+extern void tw686x_video_start(struct tw686x_vdev* dev);
+
+/* ----------------------------------------------------------- */
+/* tw686x-alsa.c */
+/* Audio */
+extern int tw686x_alsa_create(struct tw686x_adev *dev);
+extern int tw686x_alsa_free(struct tw686x_adev *dev);
+extern void tw686x_alsa_irq(struct tw686x_adev *dev, u32 dma_status, u32 pb_status);
+extern int tw686x_alsa_resetdma(struct tw686x_chip *chip, u32 dma_cmd);
+
+/* tw686x-i2c.c */
+extern int tw686x_i2c_register(struct tw686x_chip *chip);
+extern int tw686x_i2c_unregister(struct tw686x_chip *chip);
+
+/* ----------------------------------------------------------- */
+/* tw686x-audio.c */
+/* Audio */
+extern int tw686x_audio_create(struct tw686x_adev *dev);
+extern int tw686x_audio_free(struct tw686x_adev *dev);
+extern void tw686x_audio_irq(struct tw686x_adev *dev, u32 dma_status, u32 pb_status);
+extern int tw686x_audio_param(struct tw686x_adev *dev, struct tw686x_aparam* ap);
+extern int tw686x_audio_data(struct tw686x_adev *dev, struct tw686x_adata* ap);
+extern int tw686x_audio_resetdma(struct tw686x_chip *chip, u32 dma_cmd);
+extern int tw686x_audio_trigger(struct tw686x_adev *dev, int cmd);
+
+/* ----------------------------------------------------------- */
+/* tv norms */
+
+static unsigned int inline norm_maxw(v4l2_std_id norm)
+{
+ //return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768;
+ return 704;
+}
+
+static unsigned int inline norm_maxh(v4l2_std_id norm)
+{
+ return (norm & V4L2_STD_625_50) ? 576 : 480;
+}
diff --git a/drivers/pci/pcie/tw6869/videobuf-dma-contig-tw.c b/drivers/pci/pcie/tw6869/videobuf-dma-contig-tw.c
new file mode 100644
index 000000000000..1de92c0067cb
--- /dev/null
+++ b/drivers/pci/pcie/tw6869/videobuf-dma-contig-tw.c
@@ -0,0 +1,897 @@
+/*
+ * helper functions for physically contiguous capture buffers
+ *
+ * The functions support hardware lacking scatter gather support
+ * (i.e. the buffers must be linear in physical memory)
+ *
+ * Copyright (c) 2008 Magnus Damm
+ *
+ * Based on videobuf-vmalloc.c,
+ * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
+ *
+ * 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
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/dma-mapping.h>
+#include <linux/version.h>
+
+#if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include "videobuf-dma-contig-tw.h"
+
+struct videobuf_dma_contig_memory {
+ u32 magic;
+ void *vaddr;
+ dma_addr_t dma_handle;
+ unsigned long size;
+};
+
+#define MAGIC_DC_MEM 0x0733ac61
+#define MAGIC_CHECK(is, should) \
+ if (unlikely((is) != (should))) { \
+ pr_err("magic mismatch: %x expected %x\n", (is), (should)); \
+ BUG(); \
+ }
+
+static void
+videobuf_vm_open(struct vm_area_struct *vma)
+{
+ struct videobuf_mapping *map = vma->vm_private_data;
+
+ dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n",
+ map, map->count, vma->vm_start, vma->vm_end);
+
+ map->count++;
+}
+
+static void videobuf_vm_close(struct vm_area_struct *vma)
+{
+ struct videobuf_mapping *map = vma->vm_private_data;
+ struct videobuf_queue *q = map->q;
+ int i;
+
+ dev_dbg(q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n",
+ map, map->count, vma->vm_start, vma->vm_end);
+
+ map->count--;
+ if (0 == map->count) {
+ struct videobuf_dma_contig_memory *mem;
+
+ dev_dbg(q->dev, "munmap %p q=%p\n", map, q);
+ videobuf_queue_lock(q);
+
+ /* We need first to cancel streams, before unmapping */
+ if (q->streaming)
+ videobuf_queue_cancel(q);
+
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+
+ if (q->bufs[i]->map != map)
+ continue;
+
+ mem = q->bufs[i]->priv;
+ if (mem) {
+ /* This callback is called only if kernel has
+ allocated memory and this memory is mmapped.
+ In this case, memory should be freed,
+ in order to do memory unmap.
+ */
+
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ /* vfree is not atomic - can't be
+ called with IRQ's disabled
+ */
+ dev_dbg(q->dev, "buf[%d] freeing %p\n",
+ i, mem->vaddr);
+
+ dma_free_coherent(q->dev, mem->size,
+ mem->vaddr, mem->dma_handle);
+ mem->vaddr = NULL;
+ }
+
+ q->bufs[i]->map = NULL;
+ q->bufs[i]->baddr = 0;
+ }
+
+ kfree(map);
+
+ videobuf_queue_unlock(q);
+ }
+}
+
+static const struct vm_operations_struct videobuf_vm_ops = {
+ .open = videobuf_vm_open,
+ .close = videobuf_vm_close,
+};
+
+/**
+ * videobuf_dma_contig_user_put() - reset pointer to user space buffer
+ * @mem: per-buffer private videobuf-dma-contig data
+ *
+ * This function resets the user space pointer
+ */
+static void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem)
+{
+ mem->dma_handle = 0;
+ mem->size = 0;
+}
+
+/**
+ * videobuf_dma_contig_user_get() - setup user space memory pointer
+ * @mem: per-buffer private videobuf-dma-contig data
+ * @vb: video buffer to map
+ *
+ * This function validates and sets up a pointer to user space memory.
+ * Only physically contiguous pfn-mapped memory is accepted.
+ *
+ * Returns 0 if successful.
+ */
+static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem,
+ struct videobuf_buffer *vb)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ unsigned long prev_pfn, this_pfn;
+ unsigned long pages_done, user_address;
+ unsigned int offset;
+ int ret;
+
+ offset = vb->baddr & ~PAGE_MASK;
+ mem->size = PAGE_ALIGN(vb->size + offset);
+ ret = -EINVAL;
+
+ down_read(&mm->mmap_sem);
+
+ vma = find_vma(mm, vb->baddr);
+ if (!vma)
+ goto out_up;
+
+ if ((vb->baddr + mem->size) > vma->vm_end)
+ goto out_up;
+
+ pages_done = 0;
+ prev_pfn = 0; /* kill warning */
+ user_address = vb->baddr;
+
+ while (pages_done < (mem->size >> PAGE_SHIFT)) {
+ ret = follow_pfn(vma, user_address, &this_pfn);
+ if (ret)
+ break;
+
+ if (pages_done == 0)
+ mem->dma_handle = (this_pfn << PAGE_SHIFT) + offset;
+ else if (this_pfn != (prev_pfn + 1))
+ ret = -EFAULT;
+
+ if (ret)
+ break;
+
+ prev_pfn = this_pfn;
+ user_address += PAGE_SIZE;
+ pages_done++;
+ }
+
+ out_up:
+ up_read(&current->mm->mmap_sem);
+
+ return ret;
+}
+
+static struct videobuf_buffer *__videobuf_alloc_vb(size_t size)
+{
+ struct videobuf_dma_contig_memory *mem;
+ struct videobuf_buffer *vb;
+
+ vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
+ if (vb) {
+ mem = vb->priv = ((char *)vb) + size;
+ mem->magic = MAGIC_DC_MEM;
+ }
+
+ return vb;
+}
+
+static void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
+{
+ struct videobuf_dma_contig_memory *mem = buf->priv;
+
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ return mem->vaddr;
+}
+
+static int __videobuf_iolock(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ struct v4l2_framebuffer *fbuf)
+{
+ struct videobuf_dma_contig_memory *mem = vb->priv;
+
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ switch (vb->memory) {
+ case V4L2_MEMORY_MMAP:
+ dev_dbg(q->dev, "%s memory method MMAP\n", __func__);
+
+ /* All handling should be done by __videobuf_mmap_mapper() */
+ if (!mem->vaddr) {
+ dev_err(q->dev, "memory is not alloced/mmapped.\n");
+ return -EINVAL;
+ }
+ break;
+ case V4L2_MEMORY_USERPTR:
+ dev_dbg(q->dev, "%s memory method USERPTR\n", __func__);
+
+ /* handle pointer from user space */
+ if (vb->baddr)
+ return videobuf_dma_contig_user_get(mem, vb);
+
+ /* allocate memory for the read() method */
+ mem->size = PAGE_ALIGN(vb->size);
+ mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
+ &mem->dma_handle, GFP_KERNEL);
+ if (!mem->vaddr) {
+ dev_err(q->dev, "dma_alloc_coherent %ld failed\n",
+ mem->size);
+ return -ENOMEM;
+ }
+
+ dev_dbg(q->dev, "dma_alloc_coherent data is at %p (%ld)\n",
+ mem->vaddr, mem->size);
+ break;
+ case V4L2_MEMORY_OVERLAY:
+ default:
+ dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __videobuf_mmap_mapper(struct videobuf_queue *q,
+ struct videobuf_buffer *buf,
+ struct vm_area_struct *vma)
+{
+ struct videobuf_dma_contig_memory *mem;
+ struct videobuf_mapping *map;
+ int retval;
+ unsigned long size;
+
+ dev_dbg(q->dev, "%s\n", __func__);
+
+ /* create mapping + update buffer list */
+ map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
+ if (!map)
+ return -ENOMEM;
+
+ buf->map = map;
+ map->q = q;
+
+ buf->baddr = vma->vm_start;
+
+ mem = buf->priv;
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ mem->size = PAGE_ALIGN(buf->bsize);
+ mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
+ &mem->dma_handle, GFP_KERNEL);
+ if (!mem->vaddr) {
+ dev_err(q->dev, "dma_alloc_coherent size %ld failed\n",
+ mem->size);
+ goto error;
+ }
+ dev_dbg(q->dev, "dma_alloc_coherent data is at addr %p (size %ld)\n",
+ mem->vaddr, mem->size);
+
+ /* Try to remap memory */
+
+ size = vma->vm_end - vma->vm_start;
+ size = (size < mem->size) ? size : mem->size;
+
+ //vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); //notice!!!这里加上后会造成上层读内存CPU占用狂高
+ retval = remap_pfn_range(vma, vma->vm_start,
+ mem->dma_handle >> PAGE_SHIFT,
+ size, vma->vm_page_prot);
+ if (retval) {
+ dev_err(q->dev, "mmap: remap failed with error %d. ", retval);
+ dma_free_coherent(q->dev, mem->size,
+ mem->vaddr, mem->dma_handle);
+ goto error;
+ }
+
+ vma->vm_ops = &videobuf_vm_ops;
+ vma->vm_flags |= VM_DONTEXPAND;
+ vma->vm_private_data = map;
+
+ dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
+ map, q, vma->vm_start, vma->vm_end,
+ (long int)buf->bsize,
+ vma->vm_pgoff, buf->i);
+
+ videobuf_vm_open(vma);
+
+ return 0;
+
+error:
+ kfree(map);
+ return -ENOMEM;
+}
+
+static struct videobuf_qtype_ops qops = {
+ .magic = MAGIC_QTYPE_OPS,
+
+ .alloc_vb = __videobuf_alloc_vb,
+ .iolock = __videobuf_iolock,
+ .mmap_mapper = __videobuf_mmap_mapper,
+ .vaddr = __videobuf_to_vaddr,
+};
+
+void videobuf_queue_dma_contig_init_isl(struct videobuf_queue *q,
+ const struct videobuf_queue_ops *ops,
+ struct device *dev,
+ spinlock_t *irqlock,
+ enum v4l2_buf_type type,
+ enum v4l2_field field,
+ unsigned int msize,
+ void *priv,
+ struct mutex *ext_lock)
+{
+ videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
+ priv, &qops, ext_lock);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init_isl);
+
+dma_addr_t videobuf_to_dma_contig_isl(struct videobuf_buffer *buf)
+{
+ struct videobuf_dma_contig_memory *mem = buf->priv;
+
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ return mem->dma_handle;
+}
+EXPORT_SYMBOL_GPL(videobuf_to_dma_contig_isl);
+
+void* videobuf_to_vaddr_isl(struct videobuf_buffer *buf)
+{
+ struct videobuf_dma_contig_memory *mem = buf->priv;
+
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ return mem->vaddr;
+}
+EXPORT_SYMBOL_GPL(videobuf_to_vaddr_isl);
+
+void videobuf_dma_contig_free_isl(struct videobuf_queue *q,
+ struct videobuf_buffer *buf)
+{
+ struct videobuf_dma_contig_memory *mem = buf->priv;
+
+ /* mmapped memory can't be freed here, otherwise mmapped region
+ would be released, while still needed. In this case, the memory
+ release should happen inside videobuf_vm_close().
+ So, it should free memory only if the memory were allocated for
+ read() operation.
+ */
+ if (buf->memory != V4L2_MEMORY_USERPTR)
+ return;
+
+ if (!mem)
+ return;
+
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ /* handle user space pointer case */
+ if (buf->baddr) {
+ videobuf_dma_contig_user_put(mem);
+ return;
+ }
+
+ /* read() method */
+ if (mem->vaddr) {
+ dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle);
+ mem->vaddr = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_contig_free_isl);
+#else
+
+#include "videobuf-dma-contig-tw.h"
+
+struct videobuf_dma_contig_memory {
+ u32 magic;
+ void *vaddr;
+ dma_addr_t dma_handle;
+ unsigned long size;
+ int is_userptr;
+};
+
+#define MAGIC_DC_MEM 0x0733ac61
+#define MAGIC_CHECK(is, should) \
+ if (unlikely((is) != (should))) { \
+ pr_err("magic mismatch: %x expected %x\n", (is), (should)); \
+ BUG(); \
+ }
+
+static void
+videobuf_vm_open(struct vm_area_struct *vma)
+{
+ struct videobuf_mapping *map = vma->vm_private_data;
+
+ dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n",
+ map, map->count, vma->vm_start, vma->vm_end);
+
+ map->count++;
+}
+
+static void videobuf_vm_close(struct vm_area_struct *vma)
+{
+ struct videobuf_mapping *map = vma->vm_private_data;
+ struct videobuf_queue *q = map->q;
+ int i;
+
+ dev_dbg(map->q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n",
+ map, map->count, vma->vm_start, vma->vm_end);
+
+ map->count--;
+ if (0 == map->count) {
+ struct videobuf_dma_contig_memory *mem;
+
+ dev_dbg(map->q->dev, "munmap %p q=%p\n", map, q);
+ mutex_lock(&q->vb_lock);
+
+ /* We need first to cancel streams, before unmapping */
+ if (q->streaming)
+ videobuf_queue_cancel(q);
+
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+
+ if (q->bufs[i]->map != map)
+ continue;
+
+ mem = q->bufs[i]->priv;
+ if (mem) {
+ /* This callback is called only if kernel has
+ allocated memory and this memory is mmapped.
+ In this case, memory should be freed,
+ in order to do memory unmap.
+ */
+
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ /* vfree is not atomic - can't be
+ called with IRQ's disabled
+ */
+ dev_dbg(map->q->dev, "buf[%d] freeing %p\n",
+ i, mem->vaddr);
+
+ dma_free_coherent(q->dev, mem->size,
+ mem->vaddr, mem->dma_handle);
+ mem->vaddr = NULL;
+ }
+
+ q->bufs[i]->map = NULL;
+ q->bufs[i]->baddr = 0;
+ }
+
+ kfree(map);
+
+ mutex_unlock(&q->vb_lock);
+ }
+}
+
+static struct vm_operations_struct videobuf_vm_ops = {
+ .open = videobuf_vm_open,
+ .close = videobuf_vm_close,
+};
+
+/**
+ * videobuf_dma_contig_user_put() - reset pointer to user space buffer
+ * @mem: per-buffer private videobuf-dma-contig data
+ *
+ * This function resets the user space pointer
+ */
+static void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem)
+{
+ mem->is_userptr = 0;
+ mem->dma_handle = 0;
+ mem->size = 0;
+}
+
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,32))
+/**
+ * videobuf_dma_contig_user_get() - setup user space memory pointer
+ * @mem: per-buffer private videobuf-dma-contig data
+ * @vb: video buffer to map
+ *
+ * This function validates and sets up a pointer to user space memory.
+ * Only physically contiguous pfn-mapped memory is accepted.
+ *
+ * Returns 0 if successful.
+ */
+static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem,
+ struct videobuf_buffer *vb)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ unsigned long prev_pfn, this_pfn;
+ unsigned long pages_done, user_address;
+ int ret;
+
+ mem->size = PAGE_ALIGN(vb->size);
+ mem->is_userptr = 0;
+ ret = -EINVAL;
+
+ down_read(&mm->mmap_sem);
+
+ vma = find_vma(mm, vb->baddr);
+ if (!vma)
+ goto out_up;
+
+ if ((vb->baddr + mem->size) > vma->vm_end)
+ goto out_up;
+
+ pages_done = 0;
+ prev_pfn = 0; /* kill warning */
+ user_address = vb->baddr;
+
+ while (pages_done < (mem->size >> PAGE_SHIFT)) {
+ ret = follow_pfn(vma, user_address, &this_pfn);
+ if (ret)
+ break;
+
+ if (pages_done == 0)
+ mem->dma_handle = this_pfn << PAGE_SHIFT;
+ else if (this_pfn != (prev_pfn + 1))
+ ret = -EFAULT;
+
+ if (ret)
+ break;
+
+ prev_pfn = this_pfn;
+ user_address += PAGE_SIZE;
+ pages_done++;
+ }
+
+ if (!ret)
+ mem->is_userptr = 1;
+
+ out_up:
+ up_read(&current->mm->mmap_sem);
+
+ return ret;
+}
+#endif
+
+static void *__videobuf_alloc(size_t size)
+{
+ struct videobuf_dma_contig_memory *mem;
+ struct videobuf_buffer *vb;
+
+ vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
+ if (vb) {
+ mem = vb->priv = ((char *)vb) + size;
+ mem->magic = MAGIC_DC_MEM;
+ }
+
+ return vb;
+}
+
+static void *__videobuf_to_vmalloc(struct videobuf_buffer *buf)
+{
+ struct videobuf_dma_contig_memory *mem = buf->priv;
+
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ return mem->vaddr;
+}
+
+static int __videobuf_iolock(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ struct v4l2_framebuffer *fbuf)
+{
+ struct videobuf_dma_contig_memory *mem = vb->priv;
+
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ switch (vb->memory) {
+ case V4L2_MEMORY_MMAP:
+ dev_dbg(q->dev, "%s memory method MMAP\n", __func__);
+
+ /* All handling should be done by __videobuf_mmap_mapper() */
+ if (!mem->vaddr) {
+ dev_err(q->dev, "memory is not alloced/mmapped.\n");
+ return -EINVAL;
+ }
+ break;
+ case V4L2_MEMORY_USERPTR:
+ dev_dbg(q->dev, "%s memory method USERPTR\n", __func__);
+
+ /* handle pointer from user space */
+ if (vb->baddr)
+#if(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,32))
+ return videobuf_dma_contig_user_get(mem, vb);
+#else
+ return -EINVAL;
+#endif
+
+ /* allocate memory for the read() method */
+ mem->size = PAGE_ALIGN(vb->size);
+ mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
+ &mem->dma_handle, GFP_KERNEL);
+ if (!mem->vaddr) {
+ dev_err(q->dev, "dma_alloc_coherent %ld failed\n",
+ mem->size);
+ return -ENOMEM;
+ }
+
+ dev_dbg(q->dev, "dma_alloc_coherent data is at %p (%ld)\n",
+ mem->vaddr, mem->size);
+ break;
+ case V4L2_MEMORY_OVERLAY:
+ default:
+ dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __videobuf_mmap_free(struct videobuf_queue *q)
+{
+ unsigned int i;
+
+ dev_dbg(q->dev, "%s\n", __func__);
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (q->bufs[i] && q->bufs[i]->map)
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int __videobuf_mmap_mapper(struct videobuf_queue *q,
+ struct vm_area_struct *vma)
+{
+ struct videobuf_dma_contig_memory *mem;
+ struct videobuf_mapping *map;
+ unsigned int first;
+ int retval;
+ unsigned long size, offset = vma->vm_pgoff << PAGE_SHIFT;
+
+ dev_dbg(q->dev, "%s\n", __func__);
+ if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED))
+ return -EINVAL;
+
+ /* look for first buffer to map */
+ for (first = 0; first < VIDEO_MAX_FRAME; first++) {
+ if (!q->bufs[first])
+ continue;
+
+ if (V4L2_MEMORY_MMAP != q->bufs[first]->memory)
+ continue;
+ if (q->bufs[first]->boff == offset)
+ break;
+ }
+ if (VIDEO_MAX_FRAME == first) {
+ dev_dbg(q->dev, "invalid user space offset [offset=0x%lx]\n",
+ offset);
+ return -EINVAL;
+ }
+
+ /* create mapping + update buffer list */
+ map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
+ if (!map)
+ return -ENOMEM;
+
+ q->bufs[first]->map = map;
+ map->start = vma->vm_start;
+ map->end = vma->vm_end;
+ map->q = q;
+
+ q->bufs[first]->baddr = vma->vm_start;
+
+ mem = q->bufs[first]->priv;
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ mem->size = PAGE_ALIGN(q->bufs[first]->bsize);
+ mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
+ &mem->dma_handle, GFP_KERNEL);
+ if (!mem->vaddr) {
+ dev_err(q->dev, "dma_alloc_coherent size %ld failed\n",
+ mem->size);
+ goto error;
+ }
+ dev_dbg(q->dev, "dma_alloc_coherent data is at addr %p (size %ld)\n",
+ mem->vaddr, mem->size);
+
+ /* Try to remap memory */
+
+ size = vma->vm_end - vma->vm_start;
+ size = (size < mem->size) ? size : mem->size;
+
+ //vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); //notice!!!这里加上后会造成上层读内存CPU占用狂高
+ retval = remap_pfn_range(vma, vma->vm_start,
+ mem->dma_handle >> PAGE_SHIFT,
+ size, vma->vm_page_prot);
+ if (retval) {
+ dev_err(q->dev, "mmap: remap failed with error %d. ", retval);
+ dma_free_coherent(q->dev, mem->size,
+ mem->vaddr, mem->dma_handle);
+ goto error;
+ }
+
+ vma->vm_ops = &videobuf_vm_ops;
+ vma->vm_flags |= VM_DONTEXPAND;
+ vma->vm_private_data = map;
+
+ dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
+ map, q, vma->vm_start, vma->vm_end,
+ (long int) q->bufs[first]->bsize,
+ vma->vm_pgoff, first);
+
+ videobuf_vm_open(vma);
+
+ return 0;
+
+error:
+ kfree(map);
+ return -ENOMEM;
+}
+
+static int __videobuf_copy_to_user(struct videobuf_queue *q,
+ char __user *data, size_t count,
+ int nonblocking)
+{
+ struct videobuf_dma_contig_memory *mem = q->read_buf->priv;
+ void *vaddr;
+
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+ BUG_ON(!mem->vaddr);
+
+ /* copy to userspace */
+ if (count > q->read_buf->size - q->read_off)
+ count = q->read_buf->size - q->read_off;
+
+ vaddr = mem->vaddr;
+
+ if (copy_to_user(data, vaddr + q->read_off, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static int __videobuf_copy_stream(struct videobuf_queue *q,
+ char __user *data, size_t count, size_t pos,
+ int vbihack, int nonblocking)
+{
+ unsigned int *fc;
+ struct videobuf_dma_contig_memory *mem = q->read_buf->priv;
+
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ if (vbihack) {
+ /* dirty, undocumented hack -- pass the frame counter
+ * within the last four bytes of each vbi data block.
+ * We need that one to maintain backward compatibility
+ * to all vbi decoding software out there ... */
+ fc = (unsigned int *)mem->vaddr;
+ fc += (q->read_buf->size >> 2) - 1;
+ *fc = q->read_buf->field_count >> 1;
+ dev_dbg(q->dev, "vbihack: %d\n", *fc);
+ }
+
+ /* copy stuff using the common method */
+ count = __videobuf_copy_to_user(q, data, count, nonblocking);
+
+ if ((count == -EFAULT) && (pos == 0))
+ return -EFAULT;
+
+ return count;
+}
+
+static struct videobuf_qtype_ops qops = {
+ .magic = MAGIC_QTYPE_OPS,
+
+ .alloc = __videobuf_alloc,
+ .iolock = __videobuf_iolock,
+ .mmap_free = __videobuf_mmap_free,
+ .mmap_mapper = __videobuf_mmap_mapper,
+ .video_copy_to_user = __videobuf_copy_to_user,
+ .copy_stream = __videobuf_copy_stream,
+ .vmalloc = __videobuf_to_vmalloc,
+};
+
+void videobuf_queue_dma_contig_init_isl(struct videobuf_queue *q,
+ struct videobuf_queue_ops *ops,
+ struct device *dev,
+ spinlock_t *irqlock,
+ enum v4l2_buf_type type,
+ enum v4l2_field field,
+ unsigned int msize,
+ void *priv)
+{
+ videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
+ priv, &qops);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init_isl);
+
+dma_addr_t videobuf_to_dma_contig_isl(struct videobuf_buffer *buf)
+{
+ struct videobuf_dma_contig_memory *mem = buf->priv;
+
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ return mem->dma_handle;
+}
+EXPORT_SYMBOL_GPL(videobuf_to_dma_contig_isl);
+
+void* videobuf_to_vaddr_isl(struct videobuf_buffer *buf)
+{
+ struct videobuf_dma_contig_memory *mem = buf->priv;
+
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ return mem->vaddr;
+}
+EXPORT_SYMBOL_GPL(videobuf_to_vaddr_isl);
+
+void videobuf_dma_contig_free_isl(struct videobuf_queue *q,
+ struct videobuf_buffer *buf)
+{
+ struct videobuf_dma_contig_memory *mem = buf->priv;
+
+ /* mmapped memory can't be freed here, otherwise mmapped region
+ would be released, while still needed. In this case, the memory
+ release should happen inside videobuf_vm_close().
+ So, it should free memory only if the memory were allocated for
+ read() operation.
+ */
+ if (buf->memory != V4L2_MEMORY_USERPTR)
+ return;
+
+ if (!mem)
+ return;
+
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ /* handle user space pointer case */
+ if (buf->baddr) {
+ videobuf_dma_contig_user_put(mem);
+ return;
+ }
+
+ /* read() method */
+ dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle);
+ mem->vaddr = NULL;
+}
+EXPORT_SYMBOL_GPL(videobuf_dma_contig_free_isl);
+#endif
+
+MODULE_DESCRIPTION("helper module to manage video4linux dma contig buffers");
+MODULE_AUTHOR("Magnus Damm");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pci/pcie/tw6869/videobuf-dma-contig-tw.h b/drivers/pci/pcie/tw6869/videobuf-dma-contig-tw.h
new file mode 100644
index 000000000000..1d4df8d54bbe
--- /dev/null
+++ b/drivers/pci/pcie/tw6869/videobuf-dma-contig-tw.h
@@ -0,0 +1,46 @@
+/*
+ * helper functions for physically contiguous capture buffers
+ *
+ * The functions support hardware lacking scatter gather support
+ * (i.e. the buffers must be linear in physical memory)
+ *
+ * Copyright (c) 2008 Magnus Damm
+ *
+ * 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
+ */
+#ifndef _VIDEOBUF_DMA_CONTIG_TW_H
+#define _VIDEOBUF_DMA_CONTIG_TW_H
+
+#include <linux/dma-mapping.h>
+#include <media/videobuf-core.h>
+#include <linux/version.h>
+
+#if(LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
+void videobuf_queue_dma_contig_init_isl(struct videobuf_queue *q,
+ const struct videobuf_queue_ops *ops,
+ struct device *dev,
+ spinlock_t *irqlock,
+ enum v4l2_buf_type type,
+ enum v4l2_field field,
+ unsigned int msize,
+ void *priv,
+ struct mutex *ext_lock);
+#else
+void videobuf_queue_dma_contig_init_isl(struct videobuf_queue *q,
+ struct videobuf_queue_ops *ops,
+ struct device *dev,
+ spinlock_t *irqlock,
+ enum v4l2_buf_type type,
+ enum v4l2_field field,
+ unsigned int msize,
+ void *priv);
+#endif
+
+dma_addr_t videobuf_to_dma_contig_isl(struct videobuf_buffer *buf);
+void* videobuf_to_vaddr_isl(struct videobuf_buffer *buf);
+void videobuf_dma_contig_free_isl(struct videobuf_queue *q,
+ struct videobuf_buffer *buf);
+
+#endif /* _VIDEOBUF_DMA_CONTIG_TW_H */