summaryrefslogtreecommitdiffstats
path: root/drivers/media/pci/bt8xx
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-10-07 17:49:05 +0900
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-07 17:49:05 +0900
commit0b8e74c6f44094189dbe78baf4101acc7570c6af (patch)
tree6440561d09fb71ba5928664604ec92f29940be6b /drivers/media/pci/bt8xx
parent7f60ba388f5b9dd8b0da463b394412dace3ab814 (diff)
parentbd0d10498826ed150da5e4c45baf8b9c7088fb71 (diff)
downloadop-kernel-dev-0b8e74c6f44094189dbe78baf4101acc7570c6af.zip
op-kernel-dev-0b8e74c6f44094189dbe78baf4101acc7570c6af.tar.gz
Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab: "The first part of the media updates for Kernel 3.7. This series contain: - A major tree renaming patch series: now, drivers are organized internally by their used bus, instead of by V4L2 and/or DVB API, providing a cleaner driver location for hybrid drivers that implement both APIs, and allowing to cleanup the Kconfig items and make them more intuitive for the end user; - Media Kernel developers are typically very lazy with their duties of keeping the MAINTAINERS entries for their drivers updated. As now the tree is more organized, we're doing an effort to add/update those entries for the drivers that aren't currently orphan; - Several DVB USB drivers got moved to a new DVB USB v2 core; the new core fixes several bugs (as the existing one that got bitroted). Now, suspend/resume finally started to work fine (at least with some devices - we should expect more work with regards to it); - added multistream support for DVB-T2, and unified the API for DVB-S2 and ISDB-S. Backward binary support is preserved; - as usual, a few new drivers, some V4L2 core improvements and lots of drivers improvements and fixes. There are some points to notice on this series: 1) you should expect a trivial merge conflict on your tree, with the removal of Documentation/feature-removal-schedule.txt: this series would be adding two additional entries there. I opted to not rebase it due to this recent change; 2) With regards to the PCTV 520e udev-related breakage, I opted to fix it in a way that the patches can be backported to 3.5 even without your firmware fix patch. This way, Greg doesn't need to rush backporting your patch (as there are still the firmware cache and firmware path customization issues to be addressed there). I'll send later a patch (likely after the end of the merge window) reverting the rest of the DRX-K async firmware request, fully restoring its original behaviour to allow media drivers to initialize everything serialized as before for 3.7 and upper. 3) I'm planning to work on this weekend to test the DMABUF patches for V4L2. The patches are on my queue for several Kernel cycles, but, up to now, there is/was no way to test the series locally. I have some concerns about this particular changeset with regards to security issues, and with regards to the replacement of the old VIDIOC_OVERLAY ioctl's that is broken on modern systems, due to GPU drivers change. The Overlay API allows direct PCI2PCI transfers from a media capture card into the GPU framebuffer, but its API is crappy. Also, the only existing X11 driver that implements it requires a XV extension that is not available anymore on modern drivers. The DMABUF can do the same thing, but with it is promising to be a properly-designed API. If I can successfully test this series and be happy with it, I should be asking you to pull them next week." * 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (717 commits) em28xx: regression fix: use DRX-K sync firmware requests on em28xx drxk: allow loading firmware synchrousnously em28xx: Make all em28xx extensions to be initialized asynchronously [media] tda18271: properly report read errors in tda18271_get_id [media] tda18271: delay IR & RF calibration until init() if delay_cal is set [media] MAINTAINERS: add Michael Krufky as tda827x maintainer [media] MAINTAINERS: add Michael Krufky as tda8290 maintainer [media] MAINTAINERS: add Michael Krufky as cxusb maintainer [media] MAINTAINERS: add Michael Krufky as lg2160 maintainer [media] MAINTAINERS: add Michael Krufky as lgdt3305 maintainer [media] MAINTAINERS: add Michael Krufky as mxl111sf maintainer [media] MAINTAINERS: add Michael Krufky as mxl5007t maintainer [media] MAINTAINERS: add Michael Krufky as tda18271 maintainer [media] s5p-tv: Report only multi-plane capabilities in vidioc_querycap [media] s5p-mfc: Fix misplaced return statement in s5p_mfc_suspend() [media] exynos-gsc: Add missing static storage class specifiers [media] exynos-gsc: Remove <linux/version.h> header file inclusion [media] s5p-fimc: Fix incorrect condition in fimc_lite_reqbufs() [media] s5p-tv: Fix potential NULL pointer dereference error [media] s5k6aa: Fix possible NULL pointer dereference ...
Diffstat (limited to 'drivers/media/pci/bt8xx')
-rw-r--r--drivers/media/pci/bt8xx/Kconfig43
-rw-r--r--drivers/media/pci/bt8xx/Makefile11
-rw-r--r--drivers/media/pci/bt8xx/bt848.h369
-rw-r--r--drivers/media/pci/bt8xx/bt878.c609
-rw-r--r--drivers/media/pci/bt8xx/bt878.h159
-rw-r--r--drivers/media/pci/bt8xx/bttv-audio-hook.c382
-rw-r--r--drivers/media/pci/bt8xx/bttv-audio-hook.h23
-rw-r--r--drivers/media/pci/bt8xx/bttv-cards.c4895
-rw-r--r--drivers/media/pci/bt8xx/bttv-driver.c4630
-rw-r--r--drivers/media/pci/bt8xx/bttv-gpio.c189
-rw-r--r--drivers/media/pci/bt8xx/bttv-i2c.c397
-rw-r--r--drivers/media/pci/bt8xx/bttv-if.c121
-rw-r--r--drivers/media/pci/bt8xx/bttv-input.c589
-rw-r--r--drivers/media/pci/bt8xx/bttv-risc.c909
-rw-r--r--drivers/media/pci/bt8xx/bttv-vbi.c459
-rw-r--r--drivers/media/pci/bt8xx/bttv.h376
-rw-r--r--drivers/media/pci/bt8xx/bttvp.h535
-rw-r--r--drivers/media/pci/bt8xx/dst.c1873
-rw-r--r--drivers/media/pci/bt8xx/dst_ca.c726
-rw-r--r--drivers/media/pci/bt8xx/dst_ca.h58
-rw-r--r--drivers/media/pci/bt8xx/dst_common.h182
-rw-r--r--drivers/media/pci/bt8xx/dst_priv.h35
-rw-r--r--drivers/media/pci/bt8xx/dvb-bt8xx.c975
-rw-r--r--drivers/media/pci/bt8xx/dvb-bt8xx.h63
24 files changed, 18608 insertions, 0 deletions
diff --git a/drivers/media/pci/bt8xx/Kconfig b/drivers/media/pci/bt8xx/Kconfig
new file mode 100644
index 0000000..61d09e0
--- /dev/null
+++ b/drivers/media/pci/bt8xx/Kconfig
@@ -0,0 +1,43 @@
+config VIDEO_BT848
+ tristate "BT848 Video For Linux"
+ depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L2
+ select I2C_ALGOBIT
+ select VIDEO_BTCX
+ select VIDEOBUF_DMA_SG
+ depends on RC_CORE
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEO_MSP3400 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_TVAUDIO if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_TDA7432 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_SAA6588 if MEDIA_SUBDRV_AUTOSELECT
+ ---help---
+ Support for BT848 based frame grabber/overlay boards. This includes
+ the Miro, Hauppauge and STB boards. Please read the material in
+ <file:Documentation/video4linux/bttv/> for more information.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bttv.
+
+config DVB_BT8XX
+ tristate "DVB/ATSC Support for bt878 based TV cards"
+ depends on DVB_CORE && PCI && I2C && VIDEO_BT848
+ select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_SP887X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_NXT6000 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CX24110 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_OR51211 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for PCI cards based on the Bt8xx PCI bridge. Examples are
+ the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards,
+ the pcHDTV HD2000 cards, the DViCO FusionHDTV Lite cards, and
+ some AVerMedia cards.
+
+ Since these cards have no MPEG decoder onboard, they transmit
+ only compressed MPEG data over the PCI bus, so you need
+ an external software decoder to watch TV on your computer.
+
+ Say Y if you own such a device and want to use it.
diff --git a/drivers/media/pci/bt8xx/Makefile b/drivers/media/pci/bt8xx/Makefile
new file mode 100644
index 0000000..5f06597
--- /dev/null
+++ b/drivers/media/pci/bt8xx/Makefile
@@ -0,0 +1,11 @@
+bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \
+ bttv-risc.o bttv-vbi.o bttv-i2c.o bttv-gpio.o \
+ bttv-input.o bttv-audio-hook.o
+
+obj-$(CONFIG_VIDEO_BT848) += bttv.o
+obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/tuners
diff --git a/drivers/media/pci/bt8xx/bt848.h b/drivers/media/pci/bt8xx/bt848.h
new file mode 100644
index 0000000..c37e6ac
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bt848.h
@@ -0,0 +1,369 @@
+/*
+ bt848.h - Bt848 register offsets
+
+ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+
+ 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 _BT848_H_
+#define _BT848_H_
+
+#ifndef PCI_VENDOR_ID_BROOKTREE
+#define PCI_VENDOR_ID_BROOKTREE 0x109e
+#endif
+#ifndef PCI_DEVICE_ID_BT848
+#define PCI_DEVICE_ID_BT848 0x350
+#endif
+#ifndef PCI_DEVICE_ID_BT849
+#define PCI_DEVICE_ID_BT849 0x351
+#endif
+#ifndef PCI_DEVICE_ID_FUSION879
+#define PCI_DEVICE_ID_FUSION879 0x36c
+#endif
+
+#ifndef PCI_DEVICE_ID_BT878
+#define PCI_DEVICE_ID_BT878 0x36e
+#endif
+#ifndef PCI_DEVICE_ID_BT879
+#define PCI_DEVICE_ID_BT879 0x36f
+#endif
+
+/* Brooktree 848 registers */
+
+#define BT848_DSTATUS 0x000
+#define BT848_DSTATUS_PRES (1<<7)
+#define BT848_DSTATUS_HLOC (1<<6)
+#define BT848_DSTATUS_FIELD (1<<5)
+#define BT848_DSTATUS_NUML (1<<4)
+#define BT848_DSTATUS_CSEL (1<<3)
+#define BT848_DSTATUS_PLOCK (1<<2)
+#define BT848_DSTATUS_LOF (1<<1)
+#define BT848_DSTATUS_COF (1<<0)
+
+#define BT848_IFORM 0x004
+#define BT848_IFORM_HACTIVE (1<<7)
+#define BT848_IFORM_MUXSEL (3<<5)
+#define BT848_IFORM_MUX0 (2<<5)
+#define BT848_IFORM_MUX1 (3<<5)
+#define BT848_IFORM_MUX2 (1<<5)
+#define BT848_IFORM_XTSEL (3<<3)
+#define BT848_IFORM_XT0 (1<<3)
+#define BT848_IFORM_XT1 (2<<3)
+#define BT848_IFORM_XTAUTO (3<<3)
+#define BT848_IFORM_XTBOTH (3<<3)
+#define BT848_IFORM_NTSC 1
+#define BT848_IFORM_NTSC_J 2
+#define BT848_IFORM_PAL_BDGHI 3
+#define BT848_IFORM_PAL_M 4
+#define BT848_IFORM_PAL_N 5
+#define BT848_IFORM_SECAM 6
+#define BT848_IFORM_PAL_NC 7
+#define BT848_IFORM_AUTO 0
+#define BT848_IFORM_NORM 7
+
+#define BT848_TDEC 0x008
+#define BT848_TDEC_DEC_FIELD (1<<7)
+#define BT848_TDEC_FLDALIGN (1<<6)
+#define BT848_TDEC_DEC_RAT (0x1f)
+
+#define BT848_E_CROP 0x00C
+#define BT848_O_CROP 0x08C
+
+#define BT848_E_VDELAY_LO 0x010
+#define BT848_O_VDELAY_LO 0x090
+
+#define BT848_E_VACTIVE_LO 0x014
+#define BT848_O_VACTIVE_LO 0x094
+
+#define BT848_E_HDELAY_LO 0x018
+#define BT848_O_HDELAY_LO 0x098
+
+#define BT848_E_HACTIVE_LO 0x01C
+#define BT848_O_HACTIVE_LO 0x09C
+
+#define BT848_E_HSCALE_HI 0x020
+#define BT848_O_HSCALE_HI 0x0A0
+
+#define BT848_E_HSCALE_LO 0x024
+#define BT848_O_HSCALE_LO 0x0A4
+
+#define BT848_BRIGHT 0x028
+
+#define BT848_E_CONTROL 0x02C
+#define BT848_O_CONTROL 0x0AC
+#define BT848_CONTROL_LNOTCH (1<<7)
+#define BT848_CONTROL_COMP (1<<6)
+#define BT848_CONTROL_LDEC (1<<5)
+#define BT848_CONTROL_CBSENSE (1<<4)
+#define BT848_CONTROL_CON_MSB (1<<2)
+#define BT848_CONTROL_SAT_U_MSB (1<<1)
+#define BT848_CONTROL_SAT_V_MSB (1<<0)
+
+#define BT848_CONTRAST_LO 0x030
+#define BT848_SAT_U_LO 0x034
+#define BT848_SAT_V_LO 0x038
+#define BT848_HUE 0x03C
+
+#define BT848_E_SCLOOP 0x040
+#define BT848_O_SCLOOP 0x0C0
+#define BT848_SCLOOP_CAGC (1<<6)
+#define BT848_SCLOOP_CKILL (1<<5)
+#define BT848_SCLOOP_HFILT_AUTO (0<<3)
+#define BT848_SCLOOP_HFILT_CIF (1<<3)
+#define BT848_SCLOOP_HFILT_QCIF (2<<3)
+#define BT848_SCLOOP_HFILT_ICON (3<<3)
+
+#define BT848_SCLOOP_PEAK (1<<7)
+#define BT848_SCLOOP_HFILT_MINP (1<<3)
+#define BT848_SCLOOP_HFILT_MEDP (2<<3)
+#define BT848_SCLOOP_HFILT_MAXP (3<<3)
+
+
+#define BT848_OFORM 0x048
+#define BT848_OFORM_RANGE (1<<7)
+#define BT848_OFORM_CORE0 (0<<5)
+#define BT848_OFORM_CORE8 (1<<5)
+#define BT848_OFORM_CORE16 (2<<5)
+#define BT848_OFORM_CORE32 (3<<5)
+
+#define BT848_E_VSCALE_HI 0x04C
+#define BT848_O_VSCALE_HI 0x0CC
+#define BT848_VSCALE_YCOMB (1<<7)
+#define BT848_VSCALE_COMB (1<<6)
+#define BT848_VSCALE_INT (1<<5)
+#define BT848_VSCALE_HI 15
+
+#define BT848_E_VSCALE_LO 0x050
+#define BT848_O_VSCALE_LO 0x0D0
+#define BT848_TEST 0x054
+#define BT848_ADELAY 0x060
+#define BT848_BDELAY 0x064
+
+#define BT848_ADC 0x068
+#define BT848_ADC_RESERVED (2<<6)
+#define BT848_ADC_SYNC_T (1<<5)
+#define BT848_ADC_AGC_EN (1<<4)
+#define BT848_ADC_CLK_SLEEP (1<<3)
+#define BT848_ADC_Y_SLEEP (1<<2)
+#define BT848_ADC_C_SLEEP (1<<1)
+#define BT848_ADC_CRUSH (1<<0)
+
+#define BT848_WC_UP 0x044
+#define BT848_WC_DOWN 0x078
+
+#define BT848_E_VTC 0x06C
+#define BT848_O_VTC 0x0EC
+#define BT848_VTC_HSFMT (1<<7)
+#define BT848_VTC_VFILT_2TAP 0
+#define BT848_VTC_VFILT_3TAP 1
+#define BT848_VTC_VFILT_4TAP 2
+#define BT848_VTC_VFILT_5TAP 3
+
+#define BT848_SRESET 0x07C
+
+#define BT848_COLOR_FMT 0x0D4
+#define BT848_COLOR_FMT_O_RGB32 (0<<4)
+#define BT848_COLOR_FMT_O_RGB24 (1<<4)
+#define BT848_COLOR_FMT_O_RGB16 (2<<4)
+#define BT848_COLOR_FMT_O_RGB15 (3<<4)
+#define BT848_COLOR_FMT_O_YUY2 (4<<4)
+#define BT848_COLOR_FMT_O_BtYUV (5<<4)
+#define BT848_COLOR_FMT_O_Y8 (6<<4)
+#define BT848_COLOR_FMT_O_RGB8 (7<<4)
+#define BT848_COLOR_FMT_O_YCrCb422 (8<<4)
+#define BT848_COLOR_FMT_O_YCrCb411 (9<<4)
+#define BT848_COLOR_FMT_O_RAW (14<<4)
+#define BT848_COLOR_FMT_E_RGB32 0
+#define BT848_COLOR_FMT_E_RGB24 1
+#define BT848_COLOR_FMT_E_RGB16 2
+#define BT848_COLOR_FMT_E_RGB15 3
+#define BT848_COLOR_FMT_E_YUY2 4
+#define BT848_COLOR_FMT_E_BtYUV 5
+#define BT848_COLOR_FMT_E_Y8 6
+#define BT848_COLOR_FMT_E_RGB8 7
+#define BT848_COLOR_FMT_E_YCrCb422 8
+#define BT848_COLOR_FMT_E_YCrCb411 9
+#define BT848_COLOR_FMT_E_RAW 14
+
+#define BT848_COLOR_FMT_RGB32 0x00
+#define BT848_COLOR_FMT_RGB24 0x11
+#define BT848_COLOR_FMT_RGB16 0x22
+#define BT848_COLOR_FMT_RGB15 0x33
+#define BT848_COLOR_FMT_YUY2 0x44
+#define BT848_COLOR_FMT_BtYUV 0x55
+#define BT848_COLOR_FMT_Y8 0x66
+#define BT848_COLOR_FMT_RGB8 0x77
+#define BT848_COLOR_FMT_YCrCb422 0x88
+#define BT848_COLOR_FMT_YCrCb411 0x99
+#define BT848_COLOR_FMT_RAW 0xee
+
+#define BT848_VTOTAL_LO 0xB0
+#define BT848_VTOTAL_HI 0xB4
+
+#define BT848_COLOR_CTL 0x0D8
+#define BT848_COLOR_CTL_EXT_FRMRATE (1<<7)
+#define BT848_COLOR_CTL_COLOR_BARS (1<<6)
+#define BT848_COLOR_CTL_RGB_DED (1<<5)
+#define BT848_COLOR_CTL_GAMMA (1<<4)
+#define BT848_COLOR_CTL_WSWAP_ODD (1<<3)
+#define BT848_COLOR_CTL_WSWAP_EVEN (1<<2)
+#define BT848_COLOR_CTL_BSWAP_ODD (1<<1)
+#define BT848_COLOR_CTL_BSWAP_EVEN (1<<0)
+
+#define BT848_CAP_CTL 0x0DC
+#define BT848_CAP_CTL_DITH_FRAME (1<<4)
+#define BT848_CAP_CTL_CAPTURE_VBI_ODD (1<<3)
+#define BT848_CAP_CTL_CAPTURE_VBI_EVEN (1<<2)
+#define BT848_CAP_CTL_CAPTURE_ODD (1<<1)
+#define BT848_CAP_CTL_CAPTURE_EVEN (1<<0)
+
+#define BT848_VBI_PACK_SIZE 0x0E0
+
+#define BT848_VBI_PACK_DEL 0x0E4
+#define BT848_VBI_PACK_DEL_VBI_HDELAY 0xfc
+#define BT848_VBI_PACK_DEL_EXT_FRAME 2
+#define BT848_VBI_PACK_DEL_VBI_PKT_HI 1
+
+
+#define BT848_INT_STAT 0x100
+#define BT848_INT_MASK 0x104
+
+#define BT848_INT_ETBF (1<<23)
+
+#define BT848_INT_RISCS (0xf<<28)
+#define BT848_INT_RISC_EN (1<<27)
+#define BT848_INT_RACK (1<<25)
+#define BT848_INT_FIELD (1<<24)
+#define BT848_INT_SCERR (1<<19)
+#define BT848_INT_OCERR (1<<18)
+#define BT848_INT_PABORT (1<<17)
+#define BT848_INT_RIPERR (1<<16)
+#define BT848_INT_PPERR (1<<15)
+#define BT848_INT_FDSR (1<<14)
+#define BT848_INT_FTRGT (1<<13)
+#define BT848_INT_FBUS (1<<12)
+#define BT848_INT_RISCI (1<<11)
+#define BT848_INT_GPINT (1<<9)
+#define BT848_INT_I2CDONE (1<<8)
+#define BT848_INT_VPRES (1<<5)
+#define BT848_INT_HLOCK (1<<4)
+#define BT848_INT_OFLOW (1<<3)
+#define BT848_INT_HSYNC (1<<2)
+#define BT848_INT_VSYNC (1<<1)
+#define BT848_INT_FMTCHG (1<<0)
+
+
+#define BT848_GPIO_DMA_CTL 0x10C
+#define BT848_GPIO_DMA_CTL_GPINTC (1<<15)
+#define BT848_GPIO_DMA_CTL_GPINTI (1<<14)
+#define BT848_GPIO_DMA_CTL_GPWEC (1<<13)
+#define BT848_GPIO_DMA_CTL_GPIOMODE (3<<11)
+#define BT848_GPIO_DMA_CTL_GPCLKMODE (1<<10)
+#define BT848_GPIO_DMA_CTL_PLTP23_4 (0<<6)
+#define BT848_GPIO_DMA_CTL_PLTP23_8 (1<<6)
+#define BT848_GPIO_DMA_CTL_PLTP23_16 (2<<6)
+#define BT848_GPIO_DMA_CTL_PLTP23_32 (3<<6)
+#define BT848_GPIO_DMA_CTL_PLTP1_4 (0<<4)
+#define BT848_GPIO_DMA_CTL_PLTP1_8 (1<<4)
+#define BT848_GPIO_DMA_CTL_PLTP1_16 (2<<4)
+#define BT848_GPIO_DMA_CTL_PLTP1_32 (3<<4)
+#define BT848_GPIO_DMA_CTL_PKTP_4 (0<<2)
+#define BT848_GPIO_DMA_CTL_PKTP_8 (1<<2)
+#define BT848_GPIO_DMA_CTL_PKTP_16 (2<<2)
+#define BT848_GPIO_DMA_CTL_PKTP_32 (3<<2)
+#define BT848_GPIO_DMA_CTL_RISC_ENABLE (1<<1)
+#define BT848_GPIO_DMA_CTL_FIFO_ENABLE (1<<0)
+
+#define BT848_I2C 0x110
+#define BT878_I2C_MODE (1<<7)
+#define BT878_I2C_RATE (1<<6)
+#define BT878_I2C_NOSTOP (1<<5)
+#define BT878_I2C_NOSTART (1<<4)
+#define BT848_I2C_DIV (0xf<<4)
+#define BT848_I2C_SYNC (1<<3)
+#define BT848_I2C_W3B (1<<2)
+#define BT848_I2C_SCL (1<<1)
+#define BT848_I2C_SDA (1<<0)
+
+#define BT848_RISC_STRT_ADD 0x114
+#define BT848_GPIO_OUT_EN 0x118
+#define BT848_GPIO_REG_INP 0x11C
+#define BT848_RISC_COUNT 0x120
+#define BT848_GPIO_DATA 0x200
+
+
+/* Bt848 RISC commands */
+
+/* only for the SYNC RISC command */
+#define BT848_FIFO_STATUS_FM1 0x06
+#define BT848_FIFO_STATUS_FM3 0x0e
+#define BT848_FIFO_STATUS_SOL 0x02
+#define BT848_FIFO_STATUS_EOL4 0x01
+#define BT848_FIFO_STATUS_EOL3 0x0d
+#define BT848_FIFO_STATUS_EOL2 0x09
+#define BT848_FIFO_STATUS_EOL1 0x05
+#define BT848_FIFO_STATUS_VRE 0x04
+#define BT848_FIFO_STATUS_VRO 0x0c
+#define BT848_FIFO_STATUS_PXV 0x00
+
+#define BT848_RISC_RESYNC (1<<15)
+
+/* WRITE and SKIP */
+/* disable which bytes of each DWORD */
+#define BT848_RISC_BYTE0 (1U<<12)
+#define BT848_RISC_BYTE1 (1U<<13)
+#define BT848_RISC_BYTE2 (1U<<14)
+#define BT848_RISC_BYTE3 (1U<<15)
+#define BT848_RISC_BYTE_ALL (0x0fU<<12)
+#define BT848_RISC_BYTE_NONE 0
+/* cause RISCI */
+#define BT848_RISC_IRQ (1U<<24)
+/* RISC command is last one in this line */
+#define BT848_RISC_EOL (1U<<26)
+/* RISC command is first one in this line */
+#define BT848_RISC_SOL (1U<<27)
+
+#define BT848_RISC_WRITE (0x01U<<28)
+#define BT848_RISC_SKIP (0x02U<<28)
+#define BT848_RISC_WRITEC (0x05U<<28)
+#define BT848_RISC_JUMP (0x07U<<28)
+#define BT848_RISC_SYNC (0x08U<<28)
+
+#define BT848_RISC_WRITE123 (0x09U<<28)
+#define BT848_RISC_SKIP123 (0x0aU<<28)
+#define BT848_RISC_WRITE1S23 (0x0bU<<28)
+
+
+/* Bt848A and higher only !! */
+#define BT848_TGLB 0x080
+#define BT848_TGCTRL 0x084
+#define BT848_FCAP 0x0E8
+#define BT848_PLL_F_LO 0x0F0
+#define BT848_PLL_F_HI 0x0F4
+
+#define BT848_PLL_XCI 0x0F8
+#define BT848_PLL_X (1<<7)
+#define BT848_PLL_C (1<<6)
+
+#define BT848_DVSIF 0x0FC
+
+/* Bt878 register */
+
+#define BT878_DEVCTRL 0x40
+#define BT878_EN_TBFX 0x02
+#define BT878_EN_VSFX 0x04
+
+#endif
diff --git a/drivers/media/pci/bt8xx/bt878.c b/drivers/media/pci/bt8xx/bt878.c
new file mode 100644
index 0000000..b34fa95
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bt878.c
@@ -0,0 +1,609 @@
+/*
+ * bt878.c: part of the driver for the Pinnacle PCTV Sat DVB PCI card
+ *
+ * Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de>
+ *
+ * large parts based on the bttv driver
+ * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@metzlerbros.de)
+ * & Marcus Metzler (mocm@metzlerbros.de)
+ * (c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/kmod.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "bt878.h"
+#include "dst_priv.h"
+
+
+/**************************************/
+/* Miscellaneous utility definitions */
+/**************************************/
+
+static unsigned int bt878_verbose = 1;
+static unsigned int bt878_debug;
+
+module_param_named(verbose, bt878_verbose, int, 0444);
+MODULE_PARM_DESC(verbose,
+ "verbose startup messages, default is 1 (yes)");
+module_param_named(debug, bt878_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging, default is 0 (off).");
+
+int bt878_num;
+struct bt878 bt878[BT878_MAX];
+
+EXPORT_SYMBOL(bt878_num);
+EXPORT_SYMBOL(bt878);
+
+#define btwrite(dat,adr) bmtwrite((dat), (bt->bt878_mem+(adr)))
+#define btread(adr) bmtread(bt->bt878_mem+(adr))
+
+#define btand(dat,adr) btwrite((dat) & btread(adr), adr)
+#define btor(dat,adr) btwrite((dat) | btread(adr), adr)
+#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
+
+#if defined(dprintk)
+#undef dprintk
+#endif
+#define dprintk(fmt, arg...) \
+ do { \
+ if (bt878_debug) \
+ printk(KERN_DEBUG fmt, ##arg); \
+ } while (0)
+
+static void bt878_mem_free(struct bt878 *bt)
+{
+ if (bt->buf_cpu) {
+ pci_free_consistent(bt->dev, bt->buf_size, bt->buf_cpu,
+ bt->buf_dma);
+ bt->buf_cpu = NULL;
+ }
+
+ if (bt->risc_cpu) {
+ pci_free_consistent(bt->dev, bt->risc_size, bt->risc_cpu,
+ bt->risc_dma);
+ bt->risc_cpu = NULL;
+ }
+}
+
+static int bt878_mem_alloc(struct bt878 *bt)
+{
+ if (!bt->buf_cpu) {
+ bt->buf_size = 128 * 1024;
+
+ bt->buf_cpu =
+ pci_alloc_consistent(bt->dev, bt->buf_size,
+ &bt->buf_dma);
+
+ if (!bt->buf_cpu)
+ return -ENOMEM;
+
+ memset(bt->buf_cpu, 0, bt->buf_size);
+ }
+
+ if (!bt->risc_cpu) {
+ bt->risc_size = PAGE_SIZE;
+ bt->risc_cpu =
+ pci_alloc_consistent(bt->dev, bt->risc_size,
+ &bt->risc_dma);
+
+ if (!bt->risc_cpu) {
+ bt878_mem_free(bt);
+ return -ENOMEM;
+ }
+
+ memset(bt->risc_cpu, 0, bt->risc_size);
+ }
+
+ return 0;
+}
+
+/* RISC instructions */
+#define RISC_WRITE (0x01 << 28)
+#define RISC_JUMP (0x07 << 28)
+#define RISC_SYNC (0x08 << 28)
+
+/* RISC bits */
+#define RISC_WR_SOL (1 << 27)
+#define RISC_WR_EOL (1 << 26)
+#define RISC_IRQ (1 << 24)
+#define RISC_STATUS(status) ((((~status) & 0x0F) << 20) | ((status & 0x0F) << 16))
+#define RISC_SYNC_RESYNC (1 << 15)
+#define RISC_SYNC_FM1 0x06
+#define RISC_SYNC_VRO 0x0C
+
+#define RISC_FLUSH() bt->risc_pos = 0
+#define RISC_INSTR(instr) bt->risc_cpu[bt->risc_pos++] = cpu_to_le32(instr)
+
+static int bt878_make_risc(struct bt878 *bt)
+{
+ bt->block_bytes = bt->buf_size >> 4;
+ bt->block_count = 1 << 4;
+ bt->line_bytes = bt->block_bytes;
+ bt->line_count = bt->block_count;
+
+ while (bt->line_bytes > 4095) {
+ bt->line_bytes >>= 1;
+ bt->line_count <<= 1;
+ }
+
+ if (bt->line_count > 255) {
+ printk(KERN_ERR "bt878: buffer size error!\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+static void bt878_risc_program(struct bt878 *bt, u32 op_sync_orin)
+{
+ u32 buf_pos = 0;
+ u32 line;
+
+ RISC_FLUSH();
+ RISC_INSTR(RISC_SYNC | RISC_SYNC_FM1 | op_sync_orin);
+ RISC_INSTR(0);
+
+ dprintk("bt878: risc len lines %u, bytes per line %u\n",
+ bt->line_count, bt->line_bytes);
+ for (line = 0; line < bt->line_count; line++) {
+ // At the beginning of every block we issue an IRQ with previous (finished) block number set
+ if (!(buf_pos % bt->block_bytes))
+ RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL |
+ RISC_IRQ |
+ RISC_STATUS(((buf_pos /
+ bt->block_bytes) +
+ (bt->block_count -
+ 1)) %
+ bt->block_count) | bt->
+ line_bytes);
+ else
+ RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL |
+ bt->line_bytes);
+ RISC_INSTR(bt->buf_dma + buf_pos);
+ buf_pos += bt->line_bytes;
+ }
+
+ RISC_INSTR(RISC_SYNC | op_sync_orin | RISC_SYNC_VRO);
+ RISC_INSTR(0);
+
+ RISC_INSTR(RISC_JUMP);
+ RISC_INSTR(bt->risc_dma);
+
+ btwrite((bt->line_count << 16) | bt->line_bytes, BT878_APACK_LEN);
+}
+
+/*****************************/
+/* Start/Stop grabbing funcs */
+/*****************************/
+
+void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin,
+ u32 irq_err_ignore)
+{
+ u32 int_mask;
+
+ dprintk("bt878 debug: bt878_start (ctl=%8.8x)\n", controlreg);
+ /* complete the writing of the risc dma program now we have
+ * the card specifics
+ */
+ bt878_risc_program(bt, op_sync_orin);
+ controlreg &= ~0x1f;
+ controlreg |= 0x1b;
+
+ btwrite(bt->risc_dma, BT878_ARISC_START);
+
+ /* original int mask had :
+ * 6 2 8 4 0
+ * 1111 1111 1000 0000 0000
+ * SCERR|OCERR|PABORT|RIPERR|FDSR|FTRGT|FBUS|RISCI
+ * Hacked for DST to:
+ * SCERR | OCERR | FDSR | FTRGT | FBUS | RISCI
+ */
+ int_mask = BT878_ASCERR | BT878_AOCERR | BT878_APABORT |
+ BT878_ARIPERR | BT878_APPERR | BT878_AFDSR | BT878_AFTRGT |
+ BT878_AFBUS | BT878_ARISCI;
+
+
+ /* ignore pesky bits */
+ int_mask &= ~irq_err_ignore;
+
+ btwrite(int_mask, BT878_AINT_MASK);
+ btwrite(controlreg, BT878_AGPIO_DMA_CTL);
+}
+
+void bt878_stop(struct bt878 *bt)
+{
+ u32 stat;
+ int i = 0;
+
+ dprintk("bt878 debug: bt878_stop\n");
+
+ btwrite(0, BT878_AINT_MASK);
+ btand(~0x13, BT878_AGPIO_DMA_CTL);
+
+ do {
+ stat = btread(BT878_AINT_STAT);
+ if (!(stat & BT878_ARISC_EN))
+ break;
+ i++;
+ } while (i < 500);
+
+ dprintk("bt878(%d) debug: bt878_stop, i=%d, stat=0x%8.8x\n",
+ bt->nr, i, stat);
+}
+
+EXPORT_SYMBOL(bt878_start);
+EXPORT_SYMBOL(bt878_stop);
+
+/*****************************/
+/* Interrupt service routine */
+/*****************************/
+
+static irqreturn_t bt878_irq(int irq, void *dev_id)
+{
+ u32 stat, astat, mask;
+ int count;
+ struct bt878 *bt;
+
+ bt = (struct bt878 *) dev_id;
+
+ count = 0;
+ while (1) {
+ stat = btread(BT878_AINT_STAT);
+ mask = btread(BT878_AINT_MASK);
+ if (!(astat = (stat & mask)))
+ return IRQ_NONE; /* this interrupt is not for me */
+/* dprintk("bt878(%d) debug: irq count %d, stat 0x%8.8x, mask 0x%8.8x\n",bt->nr,count,stat,mask); */
+ btwrite(astat, BT878_AINT_STAT); /* try to clear interrupt condition */
+
+
+ if (astat & (BT878_ASCERR | BT878_AOCERR)) {
+ if (bt878_verbose) {
+ printk(KERN_INFO
+ "bt878(%d): irq%s%s risc_pc=%08x\n",
+ bt->nr,
+ (astat & BT878_ASCERR) ? " SCERR" :
+ "",
+ (astat & BT878_AOCERR) ? " OCERR" :
+ "", btread(BT878_ARISC_PC));
+ }
+ }
+ if (astat & (BT878_APABORT | BT878_ARIPERR | BT878_APPERR)) {
+ if (bt878_verbose) {
+ printk(KERN_INFO
+ "bt878(%d): irq%s%s%s risc_pc=%08x\n",
+ bt->nr,
+ (astat & BT878_APABORT) ? " PABORT" :
+ "",
+ (astat & BT878_ARIPERR) ? " RIPERR" :
+ "",
+ (astat & BT878_APPERR) ? " PPERR" :
+ "", btread(BT878_ARISC_PC));
+ }
+ }
+ if (astat & (BT878_AFDSR | BT878_AFTRGT | BT878_AFBUS)) {
+ if (bt878_verbose) {
+ printk(KERN_INFO
+ "bt878(%d): irq%s%s%s risc_pc=%08x\n",
+ bt->nr,
+ (astat & BT878_AFDSR) ? " FDSR" : "",
+ (astat & BT878_AFTRGT) ? " FTRGT" :
+ "",
+ (astat & BT878_AFBUS) ? " FBUS" : "",
+ btread(BT878_ARISC_PC));
+ }
+ }
+ if (astat & BT878_ARISCI) {
+ bt->finished_block = (stat & BT878_ARISCS) >> 28;
+ tasklet_schedule(&bt->tasklet);
+ break;
+ }
+ count++;
+ if (count > 20) {
+ btwrite(0, BT878_AINT_MASK);
+ printk(KERN_ERR
+ "bt878(%d): IRQ lockup, cleared int mask\n",
+ bt->nr);
+ break;
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+int
+bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp)
+{
+ int retval;
+
+ retval = 0;
+ if (mutex_lock_interruptible(&bt->gpio_lock))
+ return -ERESTARTSYS;
+ /* special gpio signal */
+ switch (cmd) {
+ case DST_IG_ENABLE:
+ // dprintk("dvb_bt8xx: dst enable mask 0x%02x enb 0x%02x \n", mp->dstg.enb.mask, mp->dstg.enb.enable);
+ retval = bttv_gpio_enable(bt->bttv_nr,
+ mp->enb.mask,
+ mp->enb.enable);
+ break;
+ case DST_IG_WRITE:
+ // dprintk("dvb_bt8xx: dst write gpio mask 0x%02x out 0x%02x\n", mp->dstg.outp.mask, mp->dstg.outp.highvals);
+ retval = bttv_write_gpio(bt->bttv_nr,
+ mp->outp.mask,
+ mp->outp.highvals);
+
+ break;
+ case DST_IG_READ:
+ /* read */
+ retval = bttv_read_gpio(bt->bttv_nr, &mp->rd.value);
+ // dprintk("dvb_bt8xx: dst read gpio 0x%02x\n", (unsigned)mp->dstg.rd.value);
+ break;
+ case DST_IG_TS:
+ /* Set packet size */
+ bt->TS_Size = mp->psize;
+ break;
+
+ default:
+ retval = -EINVAL;
+ break;
+ }
+ mutex_unlock(&bt->gpio_lock);
+ return retval;
+}
+
+EXPORT_SYMBOL(bt878_device_control);
+
+#define BROOKTREE_878_DEVICE(vend, dev, name) \
+ { \
+ .vendor = PCI_VENDOR_ID_BROOKTREE, \
+ .device = PCI_DEVICE_ID_BROOKTREE_878, \
+ .subvendor = (vend), .subdevice = (dev), \
+ .driver_data = (unsigned long) name \
+ }
+
+static struct pci_device_id bt878_pci_tbl[] __devinitdata = {
+ BROOKTREE_878_DEVICE(0x0071, 0x0101, "Nebula Electronics DigiTV"),
+ BROOKTREE_878_DEVICE(0x1461, 0x0761, "AverMedia AverTV DVB-T 761"),
+ BROOKTREE_878_DEVICE(0x11bd, 0x001c, "Pinnacle PCTV Sat"),
+ BROOKTREE_878_DEVICE(0x11bd, 0x0026, "Pinnacle PCTV SAT CI"),
+ BROOKTREE_878_DEVICE(0x1822, 0x0001, "Twinhan VisionPlus DVB"),
+ BROOKTREE_878_DEVICE(0x270f, 0xfc00,
+ "ChainTech digitop DST-1000 DVB-S"),
+ BROOKTREE_878_DEVICE(0x1461, 0x0771, "AVermedia AverTV DVB-T 771"),
+ BROOKTREE_878_DEVICE(0x18ac, 0xdb10, "DViCO FusionHDTV DVB-T Lite"),
+ BROOKTREE_878_DEVICE(0x18ac, 0xdb11, "Ultraview DVB-T Lite"),
+ BROOKTREE_878_DEVICE(0x18ac, 0xd500, "DViCO FusionHDTV 5 Lite"),
+ BROOKTREE_878_DEVICE(0x7063, 0x2000, "pcHDTV HD-2000 TV"),
+ BROOKTREE_878_DEVICE(0x1822, 0x0026, "DNTV Live! Mini"),
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, bt878_pci_tbl);
+
+static const char * __devinit card_name(const struct pci_device_id *id)
+{
+ return id->driver_data ? (const char *)id->driver_data : "Unknown";
+}
+
+/***********************/
+/* PCI device handling */
+/***********************/
+
+static int __devinit bt878_probe(struct pci_dev *dev,
+ const struct pci_device_id *pci_id)
+{
+ int result = 0;
+ unsigned char lat;
+ struct bt878 *bt;
+#if defined(__powerpc__)
+ unsigned int cmd;
+#endif
+ unsigned int cardid;
+
+ printk(KERN_INFO "bt878: Bt878 AUDIO function found (%d).\n",
+ bt878_num);
+ if (bt878_num >= BT878_MAX) {
+ printk(KERN_ERR "bt878: Too many devices inserted\n");
+ result = -ENOMEM;
+ goto fail0;
+ }
+ if (pci_enable_device(dev))
+ return -EIO;
+
+ cardid = dev->subsystem_device << 16;
+ cardid |= dev->subsystem_vendor;
+
+ printk(KERN_INFO "%s: card id=[0x%x],[ %s ] has DVB functions.\n",
+ __func__, cardid, card_name(pci_id));
+
+ bt = &bt878[bt878_num];
+ bt->dev = dev;
+ bt->nr = bt878_num;
+ bt->shutdown = 0;
+
+ bt->id = dev->device;
+ bt->irq = dev->irq;
+ bt->bt878_adr = pci_resource_start(dev, 0);
+ if (!request_mem_region(pci_resource_start(dev, 0),
+ pci_resource_len(dev, 0), "bt878")) {
+ result = -EBUSY;
+ goto fail0;
+ }
+
+ bt->revision = dev->revision;
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+
+
+ printk(KERN_INFO "bt878(%d): Bt%x (rev %d) at %02x:%02x.%x, ",
+ bt878_num, bt->id, bt->revision, dev->bus->number,
+ PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+ printk("irq: %d, latency: %d, memory: 0x%lx\n",
+ bt->irq, lat, bt->bt878_adr);
+
+
+#if defined(__powerpc__)
+ /* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
+ /* response on cards with no firmware is not enabled by OF */
+ pci_read_config_dword(dev, PCI_COMMAND, &cmd);
+ cmd = (cmd | PCI_COMMAND_MEMORY);
+ pci_write_config_dword(dev, PCI_COMMAND, cmd);
+#endif
+
+#ifdef __sparc__
+ bt->bt878_mem = (unsigned char *) bt->bt878_adr;
+#else
+ bt->bt878_mem = ioremap(bt->bt878_adr, 0x1000);
+#endif
+
+ /* clear interrupt mask */
+ btwrite(0, BT848_INT_MASK);
+
+ result = request_irq(bt->irq, bt878_irq,
+ IRQF_SHARED | IRQF_DISABLED, "bt878",
+ (void *) bt);
+ if (result == -EINVAL) {
+ printk(KERN_ERR "bt878(%d): Bad irq number or handler\n",
+ bt878_num);
+ goto fail1;
+ }
+ if (result == -EBUSY) {
+ printk(KERN_ERR
+ "bt878(%d): IRQ %d busy, change your PnP config in BIOS\n",
+ bt878_num, bt->irq);
+ goto fail1;
+ }
+ if (result < 0)
+ goto fail1;
+
+ pci_set_master(dev);
+ pci_set_drvdata(dev, bt);
+
+ if ((result = bt878_mem_alloc(bt))) {
+ printk(KERN_ERR "bt878: failed to allocate memory!\n");
+ goto fail2;
+ }
+
+ bt878_make_risc(bt);
+ btwrite(0, BT878_AINT_MASK);
+ bt878_num++;
+
+ return 0;
+
+ fail2:
+ free_irq(bt->irq, bt);
+ fail1:
+ release_mem_region(pci_resource_start(bt->dev, 0),
+ pci_resource_len(bt->dev, 0));
+ fail0:
+ pci_disable_device(dev);
+ return result;
+}
+
+static void __devexit bt878_remove(struct pci_dev *pci_dev)
+{
+ u8 command;
+ struct bt878 *bt = pci_get_drvdata(pci_dev);
+
+ if (bt878_verbose)
+ printk(KERN_INFO "bt878(%d): unloading\n", bt->nr);
+
+ /* turn off all capturing, DMA and IRQs */
+ btand(~0x13, BT878_AGPIO_DMA_CTL);
+
+ /* first disable interrupts before unmapping the memory! */
+ btwrite(0, BT878_AINT_MASK);
+ btwrite(~0U, BT878_AINT_STAT);
+
+ /* disable PCI bus-mastering */
+ pci_read_config_byte(bt->dev, PCI_COMMAND, &command);
+ /* Should this be &=~ ?? */
+ command &= ~PCI_COMMAND_MASTER;
+ pci_write_config_byte(bt->dev, PCI_COMMAND, command);
+
+ free_irq(bt->irq, bt);
+ printk(KERN_DEBUG "bt878_mem: 0x%p.\n", bt->bt878_mem);
+ if (bt->bt878_mem)
+ iounmap(bt->bt878_mem);
+
+ release_mem_region(pci_resource_start(bt->dev, 0),
+ pci_resource_len(bt->dev, 0));
+ /* wake up any waiting processes
+ because shutdown flag is set, no new processes (in this queue)
+ are expected
+ */
+ bt->shutdown = 1;
+ bt878_mem_free(bt);
+
+ pci_set_drvdata(pci_dev, NULL);
+ pci_disable_device(pci_dev);
+ return;
+}
+
+static struct pci_driver bt878_pci_driver = {
+ .name = "bt878",
+ .id_table = bt878_pci_tbl,
+ .probe = bt878_probe,
+ .remove = __devexit_p(bt878_remove),
+};
+
+/*******************************/
+/* Module management functions */
+/*******************************/
+
+static int __init bt878_init_module(void)
+{
+ bt878_num = 0;
+
+ printk(KERN_INFO "bt878: AUDIO driver version %d.%d.%d loaded\n",
+ (BT878_VERSION_CODE >> 16) & 0xff,
+ (BT878_VERSION_CODE >> 8) & 0xff,
+ BT878_VERSION_CODE & 0xff);
+
+ return pci_register_driver(&bt878_pci_driver);
+}
+
+static void __exit bt878_cleanup_module(void)
+{
+ pci_unregister_driver(&bt878_pci_driver);
+}
+
+module_init(bt878_init_module);
+module_exit(bt878_cleanup_module);
+
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/bt878.h b/drivers/media/pci/bt8xx/bt878.h
new file mode 100644
index 0000000..d19b592
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bt878.h
@@ -0,0 +1,159 @@
+/*
+ bt878.h - Bt878 audio module (register offsets)
+
+ Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de>
+
+ 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 _BT878_H_
+#define _BT878_H_
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+
+#include "bt848.h"
+#include "bttv.h"
+
+#define BT878_VERSION_CODE 0x000000
+
+#define BT878_AINT_STAT 0x100
+#define BT878_ARISCS (0xf<<28)
+#define BT878_ARISC_EN (1<<27)
+#define BT878_ASCERR (1<<19)
+#define BT878_AOCERR (1<<18)
+#define BT878_APABORT (1<<17)
+#define BT878_ARIPERR (1<<16)
+#define BT878_APPERR (1<<15)
+#define BT878_AFDSR (1<<14)
+#define BT878_AFTRGT (1<<13)
+#define BT878_AFBUS (1<<12)
+#define BT878_ARISCI (1<<11)
+#define BT878_AOFLOW (1<<3)
+
+#define BT878_AINT_MASK 0x104
+
+#define BT878_AGPIO_DMA_CTL 0x10c
+#define BT878_A_GAIN (0xf<<28)
+#define BT878_A_G2X (1<<27)
+#define BT878_A_PWRDN (1<<26)
+#define BT878_A_SEL (3<<24)
+#define BT878_DA_SCE (1<<23)
+#define BT878_DA_LRI (1<<22)
+#define BT878_DA_MLB (1<<21)
+#define BT878_DA_LRD (0x1f<<16)
+#define BT878_DA_DPM (1<<15)
+#define BT878_DA_SBR (1<<14)
+#define BT878_DA_ES2 (1<<13)
+#define BT878_DA_LMT (1<<12)
+#define BT878_DA_SDR (0xf<<8)
+#define BT878_DA_IOM (3<<6)
+#define BT878_DA_APP (1<<5)
+#define BT878_ACAP_EN (1<<4)
+#define BT878_PKTP (3<<2)
+#define BT878_RISC_EN (1<<1)
+#define BT878_FIFO_EN 1
+
+#define BT878_APACK_LEN 0x110
+#define BT878_AFP_LEN (0xff<<16)
+#define BT878_ALP_LEN 0xfff
+
+#define BT878_ARISC_START 0x114
+
+#define BT878_ARISC_PC 0x120
+
+/* BT878 FUNCTION 0 REGISTERS */
+#define BT878_GPIO_DMA_CTL 0x10c
+
+/* Interrupt register */
+#define BT878_INT_STAT 0x100
+#define BT878_INT_MASK 0x104
+#define BT878_I2CRACK (1<<25)
+#define BT878_I2CDONE (1<<8)
+
+#define BT878_MAX 4
+
+#define BT878_RISC_SYNC_MASK (1 << 15)
+
+
+#define BTTV_BOARD_UNKNOWN 0x00
+#define BTTV_BOARD_PINNACLESAT 0x5e
+#define BTTV_BOARD_NEBULA_DIGITV 0x68
+#define BTTV_BOARD_PC_HDTV 0x70
+#define BTTV_BOARD_TWINHAN_DST 0x71
+#define BTTV_BOARD_AVDVBT_771 0x7b
+#define BTTV_BOARD_AVDVBT_761 0x7c
+#define BTTV_BOARD_DVICO_DVBT_LITE 0x80
+#define BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE 0x87
+
+extern int bt878_num;
+
+struct bt878 {
+ struct mutex gpio_lock;
+ unsigned int nr;
+ unsigned int bttv_nr;
+ struct i2c_adapter *adapter;
+ struct pci_dev *dev;
+ unsigned int id;
+ unsigned int TS_Size;
+ unsigned char revision;
+ unsigned int irq;
+ unsigned long bt878_adr;
+ volatile void __iomem *bt878_mem; /* function 1 */
+
+ volatile u32 finished_block;
+ volatile u32 last_block;
+ u32 block_count;
+ u32 block_bytes;
+ u32 line_bytes;
+ u32 line_count;
+
+ u32 buf_size;
+ u8 *buf_cpu;
+ dma_addr_t buf_dma;
+
+ u32 risc_size;
+ __le32 *risc_cpu;
+ dma_addr_t risc_dma;
+ u32 risc_pos;
+
+ struct tasklet_struct tasklet;
+ int shutdown;
+};
+
+extern struct bt878 bt878[BT878_MAX];
+
+void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin,
+ u32 irq_err_ignore);
+void bt878_stop(struct bt878 *bt);
+
+#if defined(__powerpc__) /* big-endian */
+static inline void io_st_le32(volatile unsigned __iomem *addr, unsigned val)
+{
+ st_le32(addr, val);
+ eieio();
+}
+
+#define bmtwrite(dat,adr) io_st_le32((adr),(dat))
+#define bmtread(adr) ld_le32((adr))
+#else
+#define bmtwrite(dat,adr) writel((dat), (adr))
+#define bmtread(adr) readl(adr)
+#endif
+
+#endif
diff --git a/drivers/media/pci/bt8xx/bttv-audio-hook.c b/drivers/media/pci/bt8xx/bttv-audio-hook.c
new file mode 100644
index 0000000..2364d16
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-audio-hook.c
@@ -0,0 +1,382 @@
+/*
+ * Handlers for board audio hooks, splitted from bttv-cards
+ *
+ * Copyright (c) 2006 Mauro Carvalho Chehab (mchehab@infradead.org)
+ * This code is placed under the terms of the GNU General Public License
+ */
+
+#include "bttv-audio-hook.h"
+
+#include <linux/delay.h>
+
+/* ----------------------------------------------------------------------- */
+/* winview */
+
+void winview_volume(struct bttv *btv, __u16 volume)
+{
+ /* PT2254A programming Jon Tombs, jon@gte.esi.us.es */
+ int bits_out, loops, vol, data;
+
+ /* 32 levels logarithmic */
+ vol = 32 - ((volume>>11));
+ /* units */
+ bits_out = (PT2254_DBS_IN_2>>(vol%5));
+ /* tens */
+ bits_out |= (PT2254_DBS_IN_10>>(vol/5));
+ bits_out |= PT2254_L_CHANNEL | PT2254_R_CHANNEL;
+ data = gpio_read();
+ data &= ~(WINVIEW_PT2254_CLK| WINVIEW_PT2254_DATA|
+ WINVIEW_PT2254_STROBE);
+ for (loops = 17; loops >= 0 ; loops--) {
+ if (bits_out & (1<<loops))
+ data |= WINVIEW_PT2254_DATA;
+ else
+ data &= ~WINVIEW_PT2254_DATA;
+ gpio_write(data);
+ udelay(5);
+ data |= WINVIEW_PT2254_CLK;
+ gpio_write(data);
+ udelay(5);
+ data &= ~WINVIEW_PT2254_CLK;
+ gpio_write(data);
+ }
+ data |= WINVIEW_PT2254_STROBE;
+ data &= ~WINVIEW_PT2254_DATA;
+ gpio_write(data);
+ udelay(10);
+ data &= ~WINVIEW_PT2254_STROBE;
+ gpio_write(data);
+}
+
+/* ----------------------------------------------------------------------- */
+/* mono/stereo control for various cards (which don't use i2c chips but */
+/* connect something to the GPIO pins */
+
+void gvbctv3pci_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int con = 0;
+
+ if (set) {
+ gpio_inout(0x300, 0x300);
+ if (t->audmode & V4L2_TUNER_MODE_LANG1)
+ con = 0x000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2)
+ con = 0x300;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ con = 0x200;
+/* if (t->audmode & V4L2_TUNER_MODE_MONO)
+ * con = 0x100; */
+ gpio_bits(0x300, con);
+ } else {
+ t->audmode = V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+void gvbctv5pci_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int val, con;
+
+ if (btv->radio_user)
+ return;
+
+ val = gpio_read();
+ if (set) {
+ con = 0x000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) {
+ if (t->audmode & V4L2_TUNER_MODE_LANG1) {
+ /* LANG1 + LANG2 */
+ con = 0x100;
+ }
+ else {
+ /* LANG2 */
+ con = 0x300;
+ }
+ }
+ if (con != (val & 0x300)) {
+ gpio_bits(0x300, con);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"gvbctv5pci");
+ }
+ } else {
+ switch (val & 0x70) {
+ case 0x10:
+ t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ break;
+ case 0x30:
+ t->rxsubchans = V4L2_TUNER_SUB_LANG2;
+ break;
+ case 0x50:
+ t->rxsubchans = V4L2_TUNER_SUB_LANG1;
+ break;
+ case 0x60:
+ t->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ break;
+ case 0x70:
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ break;
+ default:
+ t->rxsubchans = V4L2_TUNER_SUB_MONO |
+ V4L2_TUNER_SUB_STEREO |
+ V4L2_TUNER_SUB_LANG1 |
+ V4L2_TUNER_SUB_LANG2;
+ }
+ t->audmode = V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * Mario Medina Nussbaum <medisoft@alohabbs.org.mx>
+ * I discover that on BT848_GPIO_DATA address a byte 0xcce enable stereo,
+ * 0xdde enables mono and 0xccd enables sap
+ *
+ * Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * P.S.: At least mask in line above is wrong - GPIO pins 3,2 select
+ * input/output sound connection, so both must be set for output mode.
+ *
+ * Looks like it's needed only for the "tvphone", the "tvphone 98"
+ * handles this with a tda9840
+ *
+ */
+
+void avermedia_tvphone_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ int val = 0;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */
+ val = 0x02;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ val = 0x01;
+ if (val) {
+ gpio_bits(0x03,val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"avermedia");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1;
+ return;
+ }
+}
+
+
+void avermedia_tv_stereo_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ int val = 0;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */
+ val = 0x01;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO) /* STEREO */
+ val = 0x02;
+ btaor(val, ~0x03, BT848_GPIO_DATA);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"avermedia");
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ return;
+ }
+}
+
+/* Lifetec 9415 handling */
+
+void lt9415_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ int val = 0;
+
+ if (gpio_read() & 0x4000) {
+ t->audmode = V4L2_TUNER_MODE_MONO;
+ return;
+ }
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* A2 SAP */
+ val = 0x0080;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO) /* A2 stereo */
+ val = 0x0880;
+ if ((t->audmode & V4L2_TUNER_MODE_LANG1) ||
+ (t->audmode & V4L2_TUNER_MODE_MONO))
+ val = 0;
+ gpio_bits(0x0880, val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"lt9415");
+ } else {
+ /* autodetect doesn't work with this card :-( */
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ return;
+ }
+}
+
+/* TDA9821 on TerraTV+ Bt848, Bt878 */
+void terratv_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int con = 0;
+
+ if (set) {
+ gpio_inout(0x180000,0x180000);
+ if (t->audmode & V4L2_TUNER_MODE_LANG2)
+ con = 0x080000;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ con = 0x180000;
+ gpio_bits(0x180000, con);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"terratv");
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+
+void winfast2000_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned long val = 0;
+
+ if (set) {
+ /*btor (0xc32000, BT848_GPIO_OUT_EN);*/
+ if (t->audmode & V4L2_TUNER_MODE_MONO) /* Mono */
+ val = 0x420000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG1) /* Mono */
+ val = 0x420000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */
+ val = 0x410000;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO) /* Stereo */
+ val = 0x020000;
+ if (val) {
+ gpio_bits(0x430000, val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"winfast2000");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * Dariusz Kowalewski <darekk@automex.pl>
+ * sound control for Prolink PV-BT878P+9B (PixelView PlayTV Pro FM+NICAM
+ * revision 9B has on-board TDA9874A sound decoder).
+ *
+ * Note: There are card variants without tda9874a. Forcing the "stereo sound route"
+ * will mute this cards.
+ */
+void pvbt878p9b_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int val = 0;
+
+ if (btv->radio_user)
+ return;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_MONO) {
+ val = 0x01;
+ }
+ if ((t->audmode & (V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2))
+ || (t->audmode & V4L2_TUNER_MODE_STEREO)) {
+ val = 0x02;
+ }
+ if (val) {
+ gpio_bits(0x03,val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"pvbt878p9b");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * Dariusz Kowalewski <darekk@automex.pl>
+ * sound control for FlyVideo 2000S (with tda9874 decoder)
+ * based on pvbt878p9b_audio() - this is not tested, please fix!!!
+ */
+void fv2000s_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int val = 0xffff;
+
+ if (btv->radio_user)
+ return;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_MONO) {
+ val = 0x0000;
+ }
+ if ((t->audmode & (V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2))
+ || (t->audmode & V4L2_TUNER_MODE_STEREO)) {
+ val = 0x1080; /*-dk-???: 0x0880, 0x0080, 0x1800 ... */
+ }
+ if (val != 0xffff) {
+ gpio_bits(0x1800, val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"fv2000s");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * sound control for Canopus WinDVR PCI
+ * Masaki Suzuki <masaki@btree.org>
+ */
+void windvr_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned long val = 0;
+
+ if (set) {
+ if (t->audmode & V4L2_TUNER_MODE_MONO)
+ val = 0x040000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG1)
+ val = 0;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2)
+ val = 0x100000;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ val = 0;
+ if (val) {
+ gpio_bits(0x140000, val);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"windvr");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
+
+/*
+ * sound control for AD-TVK503
+ * Hiroshi Takekawa <sian@big.or.jp>
+ */
+void adtvk503_audio(struct bttv *btv, struct v4l2_tuner *t, int set)
+{
+ unsigned int con = 0xffffff;
+
+ /* btaor(0x1e0000, ~0x1e0000, BT848_GPIO_OUT_EN); */
+
+ if (set) {
+ /* btor(***, BT848_GPIO_OUT_EN); */
+ if (t->audmode & V4L2_TUNER_MODE_LANG1)
+ con = 0x00000000;
+ if (t->audmode & V4L2_TUNER_MODE_LANG2)
+ con = 0x00180000;
+ if (t->audmode & V4L2_TUNER_MODE_STEREO)
+ con = 0x00000000;
+ if (t->audmode & V4L2_TUNER_MODE_MONO)
+ con = 0x00060000;
+ if (con != 0xffffff) {
+ gpio_bits(0x1e0000,con);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv, "adtvk503");
+ }
+ } else {
+ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ }
+}
diff --git a/drivers/media/pci/bt8xx/bttv-audio-hook.h b/drivers/media/pci/bt8xx/bttv-audio-hook.h
new file mode 100644
index 0000000..159d07a
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-audio-hook.h
@@ -0,0 +1,23 @@
+/*
+ * Handlers for board audio hooks, splitted from bttv-cards
+ *
+ * Copyright (c) 2006 Mauro Carvalho Chehab (mchehab@infradead.org)
+ * This code is placed under the terms of the GNU General Public License
+ */
+
+#include "bttvp.h"
+
+void winview_volume (struct bttv *btv, __u16 volume);
+
+void lt9415_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void avermedia_tvphone_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void avermedia_tv_stereo_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void terratv_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void gvbctv3pci_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void gvbctv5pci_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void winfast2000_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void pvbt878p9b_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void fv2000s_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void windvr_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+void adtvk503_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+
diff --git a/drivers/media/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c
new file mode 100644
index 0000000..38952fa
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-cards.c
@@ -0,0 +1,4895 @@
+/*
+
+ bttv-cards.c
+
+ this file has configuration informations - card-specific stuff
+ like the big tvcards array for the most part
+
+ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ & Marcus Metzler (mocm@thp.uni-koeln.de)
+ (c) 1999-2001 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+
+ 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/firmware.h>
+#include <net/checksum.h>
+
+#include <asm/unaligned.h>
+#include <asm/io.h>
+
+#include "bttvp.h"
+#include <media/v4l2-common.h>
+#include <media/tvaudio.h>
+#include "bttv-audio-hook.h"
+
+/* fwd decl */
+static void boot_msp34xx(struct bttv *btv, int pin);
+static void hauppauge_eeprom(struct bttv *btv);
+static void avermedia_eeprom(struct bttv *btv);
+static void osprey_eeprom(struct bttv *btv, const u8 ee[256]);
+static void modtec_eeprom(struct bttv *btv);
+static void init_PXC200(struct bttv *btv);
+static void init_RTV24(struct bttv *btv);
+
+static void rv605_muxsel(struct bttv *btv, unsigned int input);
+static void eagle_muxsel(struct bttv *btv, unsigned int input);
+static void xguard_muxsel(struct bttv *btv, unsigned int input);
+static void ivc120_muxsel(struct bttv *btv, unsigned int input);
+static void gvc1100_muxsel(struct bttv *btv, unsigned int input);
+
+static void PXC200_muxsel(struct bttv *btv, unsigned int input);
+
+static void picolo_tetra_muxsel(struct bttv *btv, unsigned int input);
+static void picolo_tetra_init(struct bttv *btv);
+
+static void tibetCS16_muxsel(struct bttv *btv, unsigned int input);
+static void tibetCS16_init(struct bttv *btv);
+
+static void kodicom4400r_muxsel(struct bttv *btv, unsigned int input);
+static void kodicom4400r_init(struct bttv *btv);
+
+static void sigmaSLC_muxsel(struct bttv *btv, unsigned int input);
+static void sigmaSQ_muxsel(struct bttv *btv, unsigned int input);
+
+static void geovision_muxsel(struct bttv *btv, unsigned int input);
+
+static void phytec_muxsel(struct bttv *btv, unsigned int input);
+
+static void gv800s_muxsel(struct bttv *btv, unsigned int input);
+static void gv800s_init(struct bttv *btv);
+
+static void td3116_muxsel(struct bttv *btv, unsigned int input);
+
+static int terratec_active_radio_upgrade(struct bttv *btv);
+static int tea5757_read(struct bttv *btv);
+static int tea5757_write(struct bttv *btv, int value);
+static void identify_by_eeprom(struct bttv *btv,
+ unsigned char eeprom_data[256]);
+static int __devinit pvr_boot(struct bttv *btv);
+
+/* config variables */
+static unsigned int triton1;
+static unsigned int vsfx;
+static unsigned int latency = UNSET;
+int no_overlay=-1;
+
+static unsigned int card[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int pll[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int tuner[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int svhs[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int remote[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int audiodev[BTTV_MAX];
+static unsigned int saa6588[BTTV_MAX];
+static struct bttv *master[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = NULL };
+static unsigned int autoload = UNSET;
+static unsigned int gpiomask = UNSET;
+static unsigned int audioall = UNSET;
+static unsigned int audiomux[5] = { [ 0 ... 4 ] = UNSET };
+
+/* insmod options */
+module_param(triton1, int, 0444);
+module_param(vsfx, int, 0444);
+module_param(no_overlay, int, 0444);
+module_param(latency, int, 0444);
+module_param(gpiomask, int, 0444);
+module_param(audioall, int, 0444);
+module_param(autoload, int, 0444);
+
+module_param_array(card, int, NULL, 0444);
+module_param_array(pll, int, NULL, 0444);
+module_param_array(tuner, int, NULL, 0444);
+module_param_array(svhs, int, NULL, 0444);
+module_param_array(remote, int, NULL, 0444);
+module_param_array(audiodev, int, NULL, 0444);
+module_param_array(audiomux, int, NULL, 0444);
+
+MODULE_PARM_DESC(triton1,"set ETBF pci config bit "
+ "[enable bug compatibility for triton1 + others]");
+MODULE_PARM_DESC(vsfx,"set VSFX pci config bit "
+ "[yet another chipset flaw workaround]");
+MODULE_PARM_DESC(latency,"pci latency timer");
+MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list");
+MODULE_PARM_DESC(pll,"specify installed crystal (0=none, 28=28 MHz, 35=35 MHz)");
+MODULE_PARM_DESC(tuner,"specify installed tuner type");
+MODULE_PARM_DESC(autoload, "obsolete option, please do not use anymore");
+MODULE_PARM_DESC(audiodev, "specify audio device:\n"
+ "\t\t-1 = no audio\n"
+ "\t\t 0 = autodetect (default)\n"
+ "\t\t 1 = msp3400\n"
+ "\t\t 2 = tda7432\n"
+ "\t\t 3 = tvaudio");
+MODULE_PARM_DESC(saa6588, "if 1, then load the saa6588 RDS module, default (0) is to use the card definition.");
+MODULE_PARM_DESC(no_overlay,"allow override overlay default (0 disables, 1 enables)"
+ " [some VIA/SIS chipsets are known to have problem with overlay]");
+
+/* ----------------------------------------------------------------------- */
+/* list of card IDs for bt878+ cards */
+
+static struct CARD {
+ unsigned id;
+ int cardnr;
+ char *name;
+} cards[] __devinitdata = {
+ { 0x13eb0070, BTTV_BOARD_HAUPPAUGE878, "Hauppauge WinTV" },
+ { 0x39000070, BTTV_BOARD_HAUPPAUGE878, "Hauppauge WinTV-D" },
+ { 0x45000070, BTTV_BOARD_HAUPPAUGEPVR, "Hauppauge WinTV/PVR" },
+ { 0xff000070, BTTV_BOARD_OSPREY1x0, "Osprey-100" },
+ { 0xff010070, BTTV_BOARD_OSPREY2x0_SVID,"Osprey-200" },
+ { 0xff020070, BTTV_BOARD_OSPREY500, "Osprey-500" },
+ { 0xff030070, BTTV_BOARD_OSPREY2000, "Osprey-2000" },
+ { 0xff040070, BTTV_BOARD_OSPREY540, "Osprey-540" },
+ { 0xff070070, BTTV_BOARD_OSPREY440, "Osprey-440" },
+
+ { 0x00011002, BTTV_BOARD_ATI_TVWONDER, "ATI TV Wonder" },
+ { 0x00031002, BTTV_BOARD_ATI_TVWONDERVE,"ATI TV Wonder/VE" },
+
+ { 0x6606107d, BTTV_BOARD_WINFAST2000, "Leadtek WinFast TV 2000" },
+ { 0x6607107d, BTTV_BOARD_WINFASTVC100, "Leadtek WinFast VC 100" },
+ { 0x6609107d, BTTV_BOARD_WINFAST2000, "Leadtek TV 2000 XP" },
+ { 0x263610b4, BTTV_BOARD_STB2, "STB TV PCI FM, Gateway P/N 6000704" },
+ { 0x264510b4, BTTV_BOARD_STB2, "STB TV PCI FM, Gateway P/N 6000704" },
+ { 0x402010fc, BTTV_BOARD_GVBCTV3PCI, "I-O Data Co. GV-BCTV3/PCI" },
+ { 0x405010fc, BTTV_BOARD_GVBCTV4PCI, "I-O Data Co. GV-BCTV4/PCI" },
+ { 0x407010fc, BTTV_BOARD_GVBCTV5PCI, "I-O Data Co. GV-BCTV5/PCI" },
+ { 0xd01810fc, BTTV_BOARD_GVBCTV5PCI, "I-O Data Co. GV-BCTV5/PCI" },
+
+ { 0x001211bd, BTTV_BOARD_PINNACLE, "Pinnacle PCTV" },
+ /* some cards ship with byteswapped IDs ... */
+ { 0x1200bd11, BTTV_BOARD_PINNACLE, "Pinnacle PCTV [bswap]" },
+ { 0xff00bd11, BTTV_BOARD_PINNACLE, "Pinnacle PCTV [bswap]" },
+ /* this seems to happen as well ... */
+ { 0xff1211bd, BTTV_BOARD_PINNACLE, "Pinnacle PCTV" },
+
+ { 0x3000121a, BTTV_BOARD_VOODOOTV_200, "3Dfx VoodooTV 200" },
+ { 0x263710b4, BTTV_BOARD_VOODOOTV_FM, "3Dfx VoodooTV FM" },
+ { 0x3060121a, BTTV_BOARD_STB2, "3Dfx VoodooTV 100/ STB OEM" },
+
+ { 0x3000144f, BTTV_BOARD_MAGICTVIEW063, "(Askey Magic/others) TView99 CPH06x" },
+ { 0xa005144f, BTTV_BOARD_MAGICTVIEW063, "CPH06X TView99-Card" },
+ { 0x3002144f, BTTV_BOARD_MAGICTVIEW061, "(Askey Magic/others) TView99 CPH05x" },
+ { 0x3005144f, BTTV_BOARD_MAGICTVIEW061, "(Askey Magic/others) TView99 CPH061/06L (T1/LC)" },
+ { 0x5000144f, BTTV_BOARD_MAGICTVIEW061, "Askey CPH050" },
+ { 0x300014ff, BTTV_BOARD_MAGICTVIEW061, "TView 99 (CPH061)" },
+ { 0x300214ff, BTTV_BOARD_PHOEBE_TVMAS, "Phoebe TV Master (CPH060)" },
+
+ { 0x00011461, BTTV_BOARD_AVPHONE98, "AVerMedia TVPhone98" },
+ { 0x00021461, BTTV_BOARD_AVERMEDIA98, "AVermedia TVCapture 98" },
+ { 0x00031461, BTTV_BOARD_AVPHONE98, "AVerMedia TVPhone98" },
+ { 0x00041461, BTTV_BOARD_AVERMEDIA98, "AVerMedia TVCapture 98" },
+ { 0x03001461, BTTV_BOARD_AVERMEDIA98, "VDOMATE TV TUNER CARD" },
+
+ { 0x1117153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue (Philips PAL B/G)" },
+ { 0x1118153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue (Temic PAL B/G)" },
+ { 0x1119153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue (Philips PAL I)" },
+ { 0x111a153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue (Temic PAL I)" },
+
+ { 0x1123153b, BTTV_BOARD_TERRATVRADIO, "Terratec TV Radio+" },
+ { 0x1127153b, BTTV_BOARD_TERRATV, "Terratec TV+ (V1.05)" },
+ /* clashes with FlyVideo
+ *{ 0x18521852, BTTV_BOARD_TERRATV, "Terratec TV+ (V1.10)" }, */
+ { 0x1134153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue (LR102)" },
+ { 0x1135153b, BTTV_BOARD_TERRATVALUER, "Terratec TValue Radio" }, /* LR102 */
+ { 0x5018153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue" }, /* ?? */
+ { 0xff3b153b, BTTV_BOARD_TERRATVALUER, "Terratec TValue Radio" }, /* ?? */
+
+ { 0x400015b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV" },
+ { 0x400a15b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV" },
+ { 0x400d15b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" },
+ { 0x401015b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" },
+ { 0x401615b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" },
+
+ { 0x1430aa00, BTTV_BOARD_PV143, "Provideo PV143A" },
+ { 0x1431aa00, BTTV_BOARD_PV143, "Provideo PV143B" },
+ { 0x1432aa00, BTTV_BOARD_PV143, "Provideo PV143C" },
+ { 0x1433aa00, BTTV_BOARD_PV143, "Provideo PV143D" },
+ { 0x1433aa03, BTTV_BOARD_PV143, "Security Eyes" },
+
+ { 0x1460aa00, BTTV_BOARD_PV150, "Provideo PV150A-1" },
+ { 0x1461aa01, BTTV_BOARD_PV150, "Provideo PV150A-2" },
+ { 0x1462aa02, BTTV_BOARD_PV150, "Provideo PV150A-3" },
+ { 0x1463aa03, BTTV_BOARD_PV150, "Provideo PV150A-4" },
+
+ { 0x1464aa04, BTTV_BOARD_PV150, "Provideo PV150B-1" },
+ { 0x1465aa05, BTTV_BOARD_PV150, "Provideo PV150B-2" },
+ { 0x1466aa06, BTTV_BOARD_PV150, "Provideo PV150B-3" },
+ { 0x1467aa07, BTTV_BOARD_PV150, "Provideo PV150B-4" },
+
+ { 0xa132ff00, BTTV_BOARD_IVC100, "IVC-100" },
+ { 0xa1550000, BTTV_BOARD_IVC200, "IVC-200" },
+ { 0xa1550001, BTTV_BOARD_IVC200, "IVC-200" },
+ { 0xa1550002, BTTV_BOARD_IVC200, "IVC-200" },
+ { 0xa1550003, BTTV_BOARD_IVC200, "IVC-200" },
+ { 0xa1550100, BTTV_BOARD_IVC200, "IVC-200G" },
+ { 0xa1550101, BTTV_BOARD_IVC200, "IVC-200G" },
+ { 0xa1550102, BTTV_BOARD_IVC200, "IVC-200G" },
+ { 0xa1550103, BTTV_BOARD_IVC200, "IVC-200G" },
+ { 0xa1550800, BTTV_BOARD_IVC200, "IVC-200" },
+ { 0xa1550801, BTTV_BOARD_IVC200, "IVC-200" },
+ { 0xa1550802, BTTV_BOARD_IVC200, "IVC-200" },
+ { 0xa1550803, BTTV_BOARD_IVC200, "IVC-200" },
+ { 0xa182ff00, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff01, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff02, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff03, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff04, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff05, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff06, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff07, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff08, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff09, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff0a, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff0b, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff0c, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff0d, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff0e, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff0f, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xf0500000, BTTV_BOARD_IVCE8784, "IVCE-8784" },
+ { 0xf0500001, BTTV_BOARD_IVCE8784, "IVCE-8784" },
+ { 0xf0500002, BTTV_BOARD_IVCE8784, "IVCE-8784" },
+ { 0xf0500003, BTTV_BOARD_IVCE8784, "IVCE-8784" },
+
+ { 0x41424344, BTTV_BOARD_GRANDTEC, "GrandTec Multi Capture" },
+ { 0x01020304, BTTV_BOARD_XGUARD, "Grandtec Grand X-Guard" },
+
+ { 0x18501851, BTTV_BOARD_CHRONOS_VS2, "FlyVideo 98 (LR50)/ Chronos Video Shuttle II" },
+ { 0xa0501851, BTTV_BOARD_CHRONOS_VS2, "FlyVideo 98 (LR50)/ Chronos Video Shuttle II" },
+ { 0x18511851, BTTV_BOARD_FLYVIDEO98EZ, "FlyVideo 98EZ (LR51)/ CyberMail AV" },
+ { 0x18521852, BTTV_BOARD_TYPHOON_TVIEW, "FlyVideo 98FM (LR50)/ Typhoon TView TV/FM Tuner" },
+ { 0x41a0a051, BTTV_BOARD_FLYVIDEO_98FM, "Lifeview FlyVideo 98 LR50 Rev Q" },
+ { 0x18501f7f, BTTV_BOARD_FLYVIDEO_98, "Lifeview Flyvideo 98" },
+
+ { 0x010115cb, BTTV_BOARD_GMV1, "AG GMV1" },
+ { 0x010114c7, BTTV_BOARD_MODTEC_205, "Modular Technology MM201/MM202/MM205/MM210/MM215 PCTV" },
+
+ { 0x10b42636, BTTV_BOARD_HAUPPAUGE878, "STB ???" },
+ { 0x217d6606, BTTV_BOARD_WINFAST2000, "Leadtek WinFast TV 2000" },
+ { 0xfff6f6ff, BTTV_BOARD_WINFAST2000, "Leadtek WinFast TV 2000" },
+ { 0x03116000, BTTV_BOARD_SENSORAY311_611, "Sensoray 311" },
+ { 0x06116000, BTTV_BOARD_SENSORAY311_611, "Sensoray 611" },
+ { 0x00790e11, BTTV_BOARD_WINDVR, "Canopus WinDVR PCI" },
+ { 0xa0fca1a0, BTTV_BOARD_ZOLTRIX, "Face to Face Tvmax" },
+ { 0x82b2aa6a, BTTV_BOARD_SIMUS_GVC1100, "SIMUS GVC1100" },
+ { 0x146caa0c, BTTV_BOARD_PV951, "ituner spectra8" },
+ { 0x200a1295, BTTV_BOARD_PXC200, "ImageNation PXC200A" },
+
+ { 0x40111554, BTTV_BOARD_PV_BT878P_9B, "Prolink Pixelview PV-BT" },
+ { 0x17de0a01, BTTV_BOARD_KWORLD, "Mecer TV/FM/Video Tuner" },
+
+ { 0x01051805, BTTV_BOARD_PICOLO_TETRA_CHIP, "Picolo Tetra Chip #1" },
+ { 0x01061805, BTTV_BOARD_PICOLO_TETRA_CHIP, "Picolo Tetra Chip #2" },
+ { 0x01071805, BTTV_BOARD_PICOLO_TETRA_CHIP, "Picolo Tetra Chip #3" },
+ { 0x01081805, BTTV_BOARD_PICOLO_TETRA_CHIP, "Picolo Tetra Chip #4" },
+
+ { 0x15409511, BTTV_BOARD_ACORP_Y878F, "Acorp Y878F" },
+
+ { 0x53534149, BTTV_BOARD_SSAI_SECURITY, "SSAI Security Video Interface" },
+ { 0x5353414a, BTTV_BOARD_SSAI_ULTRASOUND, "SSAI Ultrasound Video Interface" },
+
+ /* likely broken, vendor id doesn't match the other magic views ...
+ * { 0xa0fca04f, BTTV_BOARD_MAGICTVIEW063, "Guillemot Maxi TV Video 3" }, */
+
+ /* Duplicate PCI ID, reconfigure for this board during the eeprom read.
+ * { 0x13eb0070, BTTV_BOARD_HAUPPAUGE_IMPACTVCB, "Hauppauge ImpactVCB" }, */
+
+ { 0x109e036e, BTTV_BOARD_CONCEPTRONIC_CTVFMI2, "Conceptronic CTVFMi v2"},
+
+ /* DVB cards (using pci function .1 for mpeg data xfer) */
+ { 0x001c11bd, BTTV_BOARD_PINNACLESAT, "Pinnacle PCTV Sat" },
+ { 0x01010071, BTTV_BOARD_NEBULA_DIGITV, "Nebula Electronics DigiTV" },
+ { 0x20007063, BTTV_BOARD_PC_HDTV, "pcHDTV HD-2000 TV"},
+ { 0x002611bd, BTTV_BOARD_TWINHAN_DST, "Pinnacle PCTV SAT CI" },
+ { 0x00011822, BTTV_BOARD_TWINHAN_DST, "Twinhan VisionPlus DVB" },
+ { 0xfc00270f, BTTV_BOARD_TWINHAN_DST, "ChainTech digitop DST-1000 DVB-S" },
+ { 0x07711461, BTTV_BOARD_AVDVBT_771, "AVermedia AverTV DVB-T 771" },
+ { 0x07611461, BTTV_BOARD_AVDVBT_761, "AverMedia AverTV DVB-T 761" },
+ { 0xdb1018ac, BTTV_BOARD_DVICO_DVBT_LITE, "DViCO FusionHDTV DVB-T Lite" },
+ { 0xdb1118ac, BTTV_BOARD_DVICO_DVBT_LITE, "Ultraview DVB-T Lite" },
+ { 0xd50018ac, BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE, "DViCO FusionHDTV 5 Lite" },
+ { 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini "},
+ { 0xd200dbc0, BTTV_BOARD_DVICO_FUSIONHDTV_2, "DViCO FusionHDTV 2" },
+ { 0x763c008a, BTTV_BOARD_GEOVISION_GV600, "GeoVision GV-600" },
+ { 0x18011000, BTTV_BOARD_ENLTV_FM_2, "Encore ENL TV-FM-2" },
+ { 0x763d800a, BTTV_BOARD_GEOVISION_GV800S, "GeoVision GV-800(S) (master)" },
+ { 0x763d800b, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" },
+ { 0x763d800c, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" },
+ { 0x763d800d, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" },
+
+ { 0x15401830, BTTV_BOARD_PV183, "Provideo PV183-1" },
+ { 0x15401831, BTTV_BOARD_PV183, "Provideo PV183-2" },
+ { 0x15401832, BTTV_BOARD_PV183, "Provideo PV183-3" },
+ { 0x15401833, BTTV_BOARD_PV183, "Provideo PV183-4" },
+ { 0x15401834, BTTV_BOARD_PV183, "Provideo PV183-5" },
+ { 0x15401835, BTTV_BOARD_PV183, "Provideo PV183-6" },
+ { 0x15401836, BTTV_BOARD_PV183, "Provideo PV183-7" },
+ { 0x15401837, BTTV_BOARD_PV183, "Provideo PV183-8" },
+ { 0x3116f200, BTTV_BOARD_TVT_TD3116, "Tongwei Video Technology TD-3116" },
+ { 0x02280279, BTTV_BOARD_APOSONIC_WDVR, "Aposonic W-DVR" },
+ { 0, -1, NULL }
+};
+
+/* ----------------------------------------------------------------------- */
+/* array with description for bt848 / bt878 tv/grabber cards */
+
+struct tvcard bttv_tvcards[] = {
+ /* ---- card 0x00 ---------------------------------- */
+ [BTTV_BOARD_UNKNOWN] = {
+ .name = " *** UNKNOWN/GENERIC *** ",
+ .video_inputs = 4,
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MIRO] = {
+ .name = "MIRO PCTV",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 15,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 0, 0, 0 },
+ .gpiomute = 10,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_HAUPPAUGE] = {
+ .name = "Hauppauge (bt848)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_STB] = {
+ .name = "STB, Gateway P/N 6000699 (bt848)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 4, 0, 2, 3 },
+ .gpiomute = 1,
+ .no_msp34xx = 1,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ },
+
+ /* ---- card 0x04 ---------------------------------- */
+ [BTTV_BOARD_INTEL] = {
+ .name = "Intel Create and Share PCI/ Smart Video Recorder III",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = 2,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0 },
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_DIAMOND] = {
+ .name = "Diamond DTV2000",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 3,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0, 1, 0, 1 },
+ .gpiomute = 3,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_AVERMEDIA] = {
+ .name = "AVerMedia TVPhone",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomask = 0x0f,
+ .gpiomux = { 0x0c, 0x04, 0x08, 0x04 },
+ /* 0x04 for some cards ?? */
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= avermedia_tvphone_audio,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_MATRIX_VISION] = {
+ .name = "MATRIX-Vision MV-Delta",
+ .video_inputs = 5,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 0, 0),
+ .gpiomux = { 0 },
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x08 ---------------------------------- */
+ [BTTV_BOARD_FLYVIDEO] = {
+ .name = "Lifeview FlyVideo II (Bt848) LR26 / MAXI TV Video PCI2 LR26",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xc00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0xc00, 0x800, 0x400 },
+ .gpiomute = 0xc00,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TURBOTV] = {
+ .name = "IMS/IXmicro TurboTV",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 3,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 1, 2, 3 },
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_HAUPPAUGE878] = {
+ .name = "Hauppauge (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x0f, /* old: 7 */
+ .muxsel = MUXSEL(2, 0, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MIROPRO] = {
+ .name = "MIRO PCTV pro",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x3014f,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x20001,0x10001, 0, 0 },
+ .gpiomute = 10,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x0c ---------------------------------- */
+ [BTTV_BOARD_ADSTECH_TV] = {
+ .name = "ADS Technologies Channel Surfer TV (bt848)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 15,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 13, 14, 11, 7 },
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_AVERMEDIA98] = {
+ .name = "AVerMedia TVCapture 98",
+ .video_inputs = 3,
+ /* .audio_inputs= 4, */
+ .svhs = 2,
+ .gpiomask = 15,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 13, 14, 11, 7 },
+ .msp34xx_alt = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= avermedia_tv_stereo_audio,
+ .no_gpioirq = 1,
+ },
+ [BTTV_BOARD_VHX] = {
+ .name = "Aimslab Video Highway Xtreme (VHX)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 2, 1, 3 }, /* old: {0, 1, 2, 3, 4} */
+ .gpiomute = 4,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ZOLTRIX] = {
+ .name = "Zoltrix TV-Max",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 15,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 1, 0 },
+ .gpiomute = 10,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x10 ---------------------------------- */
+ [BTTV_BOARD_PIXVIEWPLAYTV] = {
+ .name = "Prolink Pixelview PlayTV (bt878)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x01fe00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ /* 2003-10-20 by "Anton A. Arapov" <arapov@mail.ru> */
+ .gpiomux = { 0x001e00, 0, 0x018000, 0x014000 },
+ .gpiomute = 0x002000,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_WINVIEW_601] = {
+ .name = "Leadtek WinView 601",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x8300f8,
+ .muxsel = MUXSEL(2, 3, 1, 1, 0),
+ .gpiomux = { 0x4fa007,0xcfa007,0xcfa007,0xcfa007 },
+ .gpiomute = 0xcfa007,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .volume_gpio = winview_volume,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_AVEC_INTERCAP] = {
+ .name = "AVEC Intercapture",
+ .video_inputs = 3,
+ /* .audio_inputs= 2, */
+ .svhs = 2,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 0, 0, 0 },
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_LIFE_FLYKIT] = {
+ .name = "Lifeview FlyVideo II EZ /FlyKit LR38 Bt848 (capture only)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x8dff00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0 },
+ .no_msp34xx = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x14 ---------------------------------- */
+ [BTTV_BOARD_CEI_RAFFLES] = {
+ .name = "CEI Raffles Card",
+ .video_inputs = 3,
+ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_CONFERENCETV] = {
+ .name = "Lifeview FlyVideo 98/ Lucky Star Image World ConferenceTV LR50",
+ .video_inputs = 4,
+ /* .audio_inputs= 2, tuner, line in */
+ .svhs = 2,
+ .gpiomask = 0x1800,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_PHOEBE_TVMAS] = {
+ .name = "Askey CPH050/ Phoebe Tv Master + FM",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xc00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 0x800, 0x400 },
+ .gpiomute = 0xc00,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MODTEC_205] = {
+ .name = "Modular Technology MM201/MM202/MM205/MM210/MM215 PCTV, bt878",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .has_dig_in = 1,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 3, 0), /* input 2 is digital */
+ /* .digital_mode= DIGITAL_MODE_CAMERA, */
+ .gpiomux = { 0, 0, 0, 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ALPS_TSBB5_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x18 ---------------------------------- */
+ [BTTV_BOARD_MAGICTVIEW061] = {
+ .name = "Askey CPH05X/06X (bt878) [many vendors]",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xe00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = {0x400, 0x400, 0x400, 0x400 },
+ .gpiomute = 0xc00,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ .has_radio = 1, /* not every card has radio */
+ },
+ [BTTV_BOARD_VOBIS_BOOSTAR] = {
+ .name = "Terratec TerraTV+ Version 1.0 (Bt848)/ Terra TValue Version 1.0/ Vobis TV-Boostar",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x1f0fff,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x20000, 0x30000, 0x10000, 0 },
+ .gpiomute = 0x40000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= terratv_audio,
+ },
+ [BTTV_BOARD_HAUPPAUG_WCAM] = {
+ .name = "Hauppauge WinCam newer (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 0, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MAXI] = {
+ .name = "Lifeview FlyVideo 98/ MAXI TV Video PCI2 LR50",
+ .video_inputs = 4,
+ /* .audio_inputs= 2, */
+ .svhs = 2,
+ .gpiomask = 0x1800,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_SECAM,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x1c ---------------------------------- */
+ [BTTV_BOARD_TERRATV] = {
+ .name = "Terratec TerraTV+ Version 1.1 (bt878)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x1f0fff,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x20000, 0x30000, 0x10000, 0x00000 },
+ .gpiomute = 0x40000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= terratv_audio,
+ /* GPIO wiring:
+ External 20 pin connector (for Active Radio Upgrade board)
+ gpio00: i2c-sda
+ gpio01: i2c-scl
+ gpio02: om5610-data
+ gpio03: om5610-clk
+ gpio04: om5610-wre
+ gpio05: om5610-stereo
+ gpio06: rds6588-davn
+ gpio07: Pin 7 n.c.
+ gpio08: nIOW
+ gpio09+10: nIOR, nSEL ?? (bt878)
+ gpio09: nIOR (bt848)
+ gpio10: nSEL (bt848)
+ Sound Routing:
+ gpio16: u2-A0 (1st 4052bt)
+ gpio17: u2-A1
+ gpio18: u2-nEN
+ gpio19: u4-A0 (2nd 4052)
+ gpio20: u4-A1
+ u4-nEN - GND
+ Btspy:
+ 00000 : Cdrom (internal audio input)
+ 10000 : ext. Video audio input
+ 20000 : TV Mono
+ a0000 : TV Mono/2
+ 1a0000 : TV Stereo
+ 30000 : Radio
+ 40000 : Mute
+ */
+
+ },
+ [BTTV_BOARD_PXC200] = {
+ /* Jannik Fritsch <jannik@techfak.uni-bielefeld.de> */
+ .name = "Imagenation PXC200",
+ .video_inputs = 5,
+ /* .audio_inputs= 1, */
+ .svhs = 1, /* was: 4 */
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 0, 0),
+ .gpiomux = { 0 },
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .muxsel_hook = PXC200_muxsel,
+
+ },
+ [BTTV_BOARD_FLYVIDEO_98] = {
+ .name = "Lifeview FlyVideo 98 LR50",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x1800, /* 0x8dfe00 */
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x0800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_IPROTV] = {
+ .name = "Formac iProTV, Formac ProTV I (bt848)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 1,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 0, 0, 0 },
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x20 ---------------------------------- */
+ [BTTV_BOARD_INTEL_C_S_PCI] = {
+ .name = "Intel Create and Share PCI/ Smart Video Recorder III",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = 2,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0 },
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TERRATVALUE] = {
+ .name = "Terratec TerraTValue Version Bt878",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xffff00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x500, 0, 0x300, 0x900 },
+ .gpiomute = 0x900,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_WINFAST2000] = {
+ .name = "Leadtek WinFast 2000/ WinFast 2000 XP",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ /* TV, CVid, SVid, CVid over SVid connector */
+ .muxsel = MUXSEL(2, 3, 1, 1, 0),
+ /* Alexander Varakin <avarakin@hotmail.com> [stereo version] */
+ .gpiomask = 0xb33000,
+ .gpiomux = { 0x122000,0x1000,0x0000,0x620000 },
+ .gpiomute = 0x800000,
+ /* Audio Routing for "WinFast 2000 XP" (no tv stereo !)
+ gpio23 -- hef4052:nEnable (0x800000)
+ gpio12 -- hef4052:A1
+ gpio13 -- hef4052:A0
+ 0x0000: external audio
+ 0x1000: FM
+ 0x2000: TV
+ 0x3000: n.c.
+ Note: There exists another variant "Winfast 2000" with tv stereo !?
+ Note: eeprom only contains FF and pci subsystem id 107d:6606
+ */
+ .pll = PLL_28,
+ .has_radio = 1,
+ .tuner_type = TUNER_PHILIPS_PAL, /* default for now, gpio reads BFFF06 for Pal bg+dk */
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= winfast2000_audio,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_CHRONOS_VS2] = {
+ .name = "Lifeview FlyVideo 98 LR50 / Chronos Video Shuttle II",
+ .video_inputs = 4,
+ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 0x1800,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x24 ---------------------------------- */
+ [BTTV_BOARD_TYPHOON_TVIEW] = {
+ .name = "Lifeview FlyVideo 98FM LR50 / Typhoon TView TV/FM Tuner",
+ .video_inputs = 4,
+ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 0x1800,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_PXELVWPLTVPRO] = {
+ .name = "Prolink PixelView PlayTV pro",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xff,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x21, 0x20, 0x24, 0x2c },
+ .gpiomute = 0x29,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MAGICTVIEW063] = {
+ .name = "Askey CPH06X TView99",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x551e00,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0x551400, 0x551200, 0, 0 },
+ .gpiomute = 0x551c00,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_PINNACLE] = {
+ .name = "Pinnacle PCTV Studio/Rave",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x03000F,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 0xd0001, 0, 0 },
+ .gpiomute = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x28 ---------------------------------- */
+ [BTTV_BOARD_STB2] = {
+ .name = "STB TV PCI FM, Gateway P/N 6000704 (bt878), 3Dfx VoodooTV 100",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 4, 0, 2, 3 },
+ .gpiomute = 1,
+ .no_msp34xx = 1,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_AVPHONE98] = {
+ .name = "AVerMedia TVPhone 98",
+ .video_inputs = 3,
+ /* .audio_inputs= 4, */
+ .svhs = 2,
+ .gpiomask = 15,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 13, 4, 11, 7 },
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 1,
+ .audio_mode_gpio= avermedia_tvphone_audio,
+ },
+ [BTTV_BOARD_PV951] = {
+ .name = "ProVideo PV951", /* pic16c54 */
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 0, 0},
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ONAIR_TV] = {
+ .name = "Little OnAir TV",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xe00b,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0xff9ff6, 0xff9ff6, 0xff1ff7, 0 },
+ .gpiomute = 0xff3ffc,
+ .no_msp34xx = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x2c ---------------------------------- */
+ [BTTV_BOARD_SIGMA_TVII_FM] = {
+ .name = "Sigma TVII-FM",
+ .video_inputs = 2,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .gpiomask = 3,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 1, 0, 2 },
+ .gpiomute = 3,
+ .no_msp34xx = 1,
+ .pll = PLL_NONE,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MATRIX_VISION2] = {
+ .name = "MATRIX-Vision MV-Delta 2",
+ .video_inputs = 5,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 0, 0),
+ .gpiomux = { 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ZOLTRIX_GENIE] = {
+ .name = "Zoltrix Genie TV/FM",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xbcf03f,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0xbc803f, 0xbc903f, 0xbcb03f, 0 },
+ .gpiomute = 0xbcb03f,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_4039FR5_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TERRATVRADIO] = {
+ .name = "Terratec TV/Radio+",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x70000,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x20000, 0x30000, 0x10000, 0 },
+ .gpiomute = 0x40000,
+ .no_msp34xx = 1,
+ .pll = PLL_35,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 1,
+ },
+
+ /* ---- card 0x30 ---------------------------------- */
+ [BTTV_BOARD_DYNALINK] = {
+ .name = "Askey CPH03x/ Dynalink Magic TView",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 15,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = {2,0,0,0 },
+ .gpiomute = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_GVBCTV3PCI] = {
+ .name = "IODATA GV-BCTV3/PCI",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x010f00,
+ .muxsel = MUXSEL(2, 3, 0, 0),
+ .gpiomux = {0x10000, 0, 0x10000, 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ALPS_TSHC6_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= gvbctv3pci_audio,
+ },
+ [BTTV_BOARD_PXELVWPLTVPAK] = {
+ .name = "Prolink PV-BT878P+4E / PixelView PlayTV PAK / Lenco MXTV-9578 CP",
+ .video_inputs = 5,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .has_dig_in = 1,
+ .gpiomask = 0xAA0000,
+ .muxsel = MUXSEL(2, 3, 1, 1, 0), /* in 4 is digital */
+ /* .digital_mode= DIGITAL_MODE_CAMERA, */
+ .gpiomux = { 0x20000, 0, 0x80000, 0x80000 },
+ .gpiomute = 0xa8000,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ /* GPIO wiring: (different from Rev.4C !)
+ GPIO17: U4.A0 (first hef4052bt)
+ GPIO19: U4.A1
+ GPIO20: U5.A1 (second hef4052bt)
+ GPIO21: U4.nEN
+ GPIO22: BT832 Reset Line
+ GPIO23: A5,A0, U5,nEN
+ Note: At i2c=0x8a is a Bt832 chip, which changes to 0x88 after being reset via GPIO22
+ */
+ },
+ [BTTV_BOARD_EAGLE] = {
+ .name = "Eagle Wireless Capricorn2 (bt878A)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 0, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .pll = PLL_28,
+ .tuner_type = UNSET /* TUNER_ALPS_TMDH2_NTSC */,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x34 ---------------------------------- */
+ [BTTV_BOARD_PINNACLEPRO] = {
+ /* David Härdeman <david@2gen.com> */
+ .name = "Pinnacle PCTV Studio Pro",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 0x03000F,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 0xd0001, 0, 0 },
+ .gpiomute = 10,
+ /* sound path (5 sources):
+ MUX1 (mask 0x03), Enable Pin 0x08 (0=enable, 1=disable)
+ 0= ext. Audio IN
+ 1= from MUX2
+ 2= Mono TV sound from Tuner
+ 3= not connected
+ MUX2 (mask 0x30000):
+ 0,2,3= from MSP34xx
+ 1= FM stereo Radio from Tuner */
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TVIEW_RDS_FM] = {
+ /* Claas Langbehn <claas@bigfoot.com>,
+ Sven Grothklags <sven@upb.de> */
+ .name = "Typhoon TView RDS + FM Stereo / KNC1 TV Station RDS",
+ .video_inputs = 4,
+ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 0x1c,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 0x10, 8 },
+ .gpiomute = 4,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_LIFETEC_9415] = {
+ /* Tim Röstermundt <rosterm@uni-muenster.de>
+ in de.comp.os.unix.linux.hardware:
+ options bttv card=0 pll=1 radio=1 gpiomask=0x18e0
+ gpiomux =0x44c71f,0x44d71f,0,0x44d71f,0x44dfff
+ options tuner type=5 */
+ .name = "Lifeview FlyVideo 2000 /FlyVideo A2/ Lifetec LT 9415 TV [LR90]",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x18e0,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x0000,0x0800,0x1000,0x1000 },
+ .gpiomute = 0x18e0,
+ /* For cards with tda9820/tda9821:
+ 0x0000: Tuner normal stereo
+ 0x0080: Tuner A2 SAP (second audio program = Zweikanalton)
+ 0x0880: Tuner A2 stereo */
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_BESTBUY_EASYTV] = {
+ /* Miguel Angel Alvarez <maacruz@navegalia.com>
+ old Easy TV BT848 version (model CPH031) */
+ .name = "Askey CPH031/ BESTBUY Easy TV",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xF,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 2, 0, 0, 0 },
+ .gpiomute = 10,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x38 ---------------------------------- */
+ [BTTV_BOARD_FLYVIDEO_98FM] = {
+ /* Gordon Heydon <gjheydon@bigfoot.com ('98) */
+ .name = "Lifeview FlyVideo 98FM LR50",
+ .video_inputs = 4,
+ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 0x1800,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+ /* This is the ultimate cheapo capture card
+ * just a BT848A on a small PCB!
+ * Steve Hosgood <steve@equiinet.com> */
+ [BTTV_BOARD_GRANDTEC] = {
+ .name = "GrandTec 'Grand Video Capture' (Bt848)",
+ .video_inputs = 2,
+ /* .audio_inputs= 0, */
+ .svhs = 1,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(3, 1),
+ .gpiomux = { 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_35,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ASKEY_CPH060] = {
+ /* Daniel Herrington <daniel.herrington@home.com> */
+ .name = "Askey CPH060/ Phoebe TV Master Only (No FM)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xe00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x400, 0x400, 0x400, 0x400 },
+ .gpiomute = 0x800,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_4036FY5_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ASKEY_CPH03X] = {
+ /* Matti Mottus <mottus@physic.ut.ee> */
+ .name = "Askey CPH03x TV Capturer",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x03000F,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 2, 0, 0, 0 },
+ .gpiomute = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ },
+
+ /* ---- card 0x3c ---------------------------------- */
+ [BTTV_BOARD_MM100PCTV] = {
+ /* Philip Blundell <philb@gnu.org> */
+ .name = "Modular Technology MM100PCTV",
+ .video_inputs = 2,
+ /* .audio_inputs= 2, */
+ .svhs = NO_SVHS,
+ .gpiomask = 11,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 0, 0, 1 },
+ .gpiomute = 8,
+ .pll = PLL_35,
+ .tuner_type = TUNER_TEMIC_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_GMV1] = {
+ /* Adrian Cox <adrian@humboldt.co.uk */
+ .name = "AG Electronics GMV1",
+ .video_inputs = 2,
+ /* .audio_inputs= 0, */
+ .svhs = 1,
+ .gpiomask = 0xF,
+ .muxsel = MUXSEL(2, 2),
+ .gpiomux = { },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_BESTBUY_EASYTV2] = {
+ /* Miguel Angel Alvarez <maacruz@navegalia.com>
+ new Easy TV BT878 version (model CPH061)
+ special thanks to Informatica Mieres for providing the card */
+ .name = "Askey CPH061/ BESTBUY Easy TV (bt878)",
+ .video_inputs = 3,
+ /* .audio_inputs= 2, */
+ .svhs = 2,
+ .gpiomask = 0xFF,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 1, 0, 4, 4 },
+ .gpiomute = 9,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ATI_TVWONDER] = {
+ /* Lukas Gebauer <geby@volny.cz> */
+ .name = "ATI TV-Wonder",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xf03f,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0xbffe, 0, 0xbfff, 0 },
+ .gpiomute = 0xbffe,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_4006FN5_MULTI_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x40 ---------------------------------- */
+ [BTTV_BOARD_ATI_TVWONDERVE] = {
+ /* Lukas Gebauer <geby@volny.cz> */
+ .name = "ATI TV-Wonder VE",
+ .video_inputs = 2,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .gpiomask = 1,
+ .muxsel = MUXSEL(2, 3, 0, 1),
+ .gpiomux = { 0, 0, 1, 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_4006FN5_MULTI_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_FLYVIDEO2000] = {
+ /* DeeJay <deejay@westel900.net (2000S) */
+ .name = "Lifeview FlyVideo 2000S LR90",
+ .video_inputs = 3,
+ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 0x18e0,
+ .muxsel = MUXSEL(2, 3, 0, 1),
+ /* Radio changed from 1e80 to 0x800 to make
+ FlyVideo2000S in .hu happy (gm)*/
+ /* -dk-???: set mute=0x1800 for tda9874h daughterboard */
+ .gpiomux = { 0x0000,0x0800,0x1000,0x1000 },
+ .gpiomute = 0x1800,
+ .audio_mode_gpio= fv2000s_audio,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TERRATVALUER] = {
+ .name = "Terratec TValueRadio",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xffff00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x500, 0x500, 0x300, 0x900 },
+ .gpiomute = 0x900,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_GVBCTV4PCI] = {
+ /* TANAKA Kei <peg00625@nifty.com> */
+ .name = "IODATA GV-BCTV4/PCI",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x010f00,
+ .muxsel = MUXSEL(2, 3, 0, 0),
+ .gpiomux = {0x10000, 0, 0x10000, 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_SHARP_2U5JF5540_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= gvbctv3pci_audio,
+ },
+
+ /* ---- card 0x44 ---------------------------------- */
+ [BTTV_BOARD_VOODOOTV_FM] = {
+ .name = "3Dfx VoodooTV FM (Euro)",
+ /* try "insmod msp3400 simple=0" if you have
+ * sound problems with this card. */
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x4f8a00,
+ /* 0x100000: 1=MSP enabled (0=disable again)
+ * 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC) */
+ .gpiomux = {0x947fff, 0x987fff,0x947fff,0x947fff },
+ .gpiomute = 0x947fff,
+ /* tvtuner, radio, external,internal, mute, stereo
+ * tuner, Composit, SVid, Composit-on-Svid-adapter */
+ .muxsel = MUXSEL(2, 3, 0, 1),
+ .tuner_type = TUNER_MT2032,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_VOODOOTV_200] = {
+ .name = "VoodooTV 200 (USA)",
+ /* try "insmod msp3400 simple=0" if you have
+ * sound problems with this card. */
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x4f8a00,
+ /* 0x100000: 1=MSP enabled (0=disable again)
+ * 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC) */
+ .gpiomux = {0x947fff, 0x987fff,0x947fff,0x947fff },
+ .gpiomute = 0x947fff,
+ /* tvtuner, radio, external,internal, mute, stereo
+ * tuner, Composit, SVid, Composit-on-Svid-adapter */
+ .muxsel = MUXSEL(2, 3, 0, 1),
+ .tuner_type = TUNER_MT2032,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_AIMMS] = {
+ /* Philip Blundell <pb@nexus.co.uk> */
+ .name = "Active Imaging AIMMS",
+ .video_inputs = 1,
+ /* .audio_inputs= 0, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .muxsel = MUXSEL(2),
+ .gpiomask = 0
+ },
+ [BTTV_BOARD_PV_BT878P_PLUS] = {
+ /* Tomasz Pyra <hellfire@sedez.iq.pl> */
+ .name = "Prolink Pixelview PV-BT878P+ (Rev.4C,8E)",
+ .video_inputs = 3,
+ /* .audio_inputs= 4, */
+ .svhs = 2,
+ .gpiomask = 15,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 11, 7 }, /* TV and Radio with same GPIO ! */
+ .gpiomute = 13,
+ .pll = PLL_28,
+ .tuner_type = TUNER_LG_PAL_I_FM,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ /* GPIO wiring:
+ GPIO0: U4.A0 (hef4052bt)
+ GPIO1: U4.A1
+ GPIO2: U4.A1 (second hef4052bt)
+ GPIO3: U4.nEN, U5.A0, A5.nEN
+ GPIO8-15: vrd866b ?
+ */
+ },
+ [BTTV_BOARD_FLYVIDEO98EZ] = {
+ .name = "Lifeview FlyVideo 98EZ (capture only) LR51",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = 2,
+ /* AV1, AV2, SVHS, CVid adapter on SVHS */
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .pll = PLL_28,
+ .no_msp34xx = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x48 ---------------------------------- */
+ [BTTV_BOARD_PV_BT878P_9B] = {
+ /* Dariusz Kowalewski <darekk@automex.pl> */
+ .name = "Prolink Pixelview PV-BT878P+9B (PlayTV Pro rev.9B FM+NICAM)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x3f,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x01, 0x00, 0x03, 0x03 },
+ .gpiomute = 0x09,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= pvbt878p9b_audio, /* Note: not all cards have stereo */
+ .has_radio = 1, /* Note: not all cards have radio */
+ .has_remote = 1,
+ /* GPIO wiring:
+ GPIO0: A0 hef4052
+ GPIO1: A1 hef4052
+ GPIO3: nEN hef4052
+ GPIO8-15: vrd866b
+ GPIO20,22,23: R30,R29,R28
+ */
+ },
+ [BTTV_BOARD_SENSORAY311_611] = {
+ /* Clay Kunz <ckunz@mail.arc.nasa.gov> */
+ /* you must jumper JP5 for the 311 card (PC/104+) to work */
+ .name = "Sensoray 311/611",
+ .video_inputs = 5,
+ /* .audio_inputs= 0, */
+ .svhs = 4,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 0, 0),
+ .gpiomux = { 0 },
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_RV605] = {
+ /* Miguel Freitas <miguel@cetuc.puc-rio.br> */
+ .name = "RemoteVision MX (RV605)",
+ .video_inputs = 16,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x00,
+ .gpiomask2 = 0x07ff,
+ .muxsel = MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3),
+ .no_msp34xx = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .muxsel_hook = rv605_muxsel,
+ },
+ [BTTV_BOARD_POWERCLR_MTV878] = {
+ .name = "Powercolor MTV878/ MTV878R/ MTV878F",
+ .video_inputs = 3,
+ /* .audio_inputs= 2, */
+ .svhs = 2,
+ .gpiomask = 0x1C800F, /* Bit0-2: Audio select, 8-12:remote control 14:remote valid 15:remote reset */
+ .muxsel = MUXSEL(2, 1, 1),
+ .gpiomux = { 0, 1, 2, 2 },
+ .gpiomute = 4,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ },
+
+ /* ---- card 0x4c ---------------------------------- */
+ [BTTV_BOARD_WINDVR] = {
+ /* Masaki Suzuki <masaki@btree.org> */
+ .name = "Canopus WinDVR PCI (COMPAQ Presario 3524JP, 5112JP)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x140007,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= windvr_audio,
+ },
+ [BTTV_BOARD_GRANDTEC_MULTI] = {
+ .name = "GrandTec Multi Capture Card (Bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_KWORLD] = {
+ .name = "Jetway TV/Capture JW-TV878-FBK, Kworld KW-TV878RF",
+ .video_inputs = 4,
+ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 7,
+ /* Tuner, SVid, SVHS, SVid to SVHS connector */
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 4, 4 },/* Yes, this tuner uses the same audio output for TV and FM radio!
+ * This card lacks external Audio In, so we mute it on Ext. & Int.
+ * The PCB can take a sbx1637/sbx1673, wiring unknown.
+ * This card lacks PCI subsystem ID, sigh.
+ * gpiomux =1: lower volume, 2+3: mute
+ * btwincap uses 0x80000/0x80003
+ */
+ .gpiomute = 4,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ /* Samsung TCPA9095PC27A (BG+DK), philips compatible, w/FM, stereo and
+ radio signal strength indicators work fine. */
+ .has_radio = 1,
+ /* GPIO Info:
+ GPIO0,1: HEF4052 A0,A1
+ GPIO2: HEF4052 nENABLE
+ GPIO3-7: n.c.
+ GPIO8-13: IRDC357 data0-5 (data6 n.c. ?) [chip not present on my card]
+ GPIO14,15: ??
+ GPIO16-21: n.c.
+ GPIO22,23: ??
+ ?? : mtu8b56ep microcontroller for IR (GPIO wiring unknown)*/
+ },
+ [BTTV_BOARD_DSP_TCVIDEO] = {
+ /* Arthur Tetzlaff-Deas, DSP Design Ltd <software@dspdesign.com> */
+ .name = "DSP Design TCVIDEO",
+ .video_inputs = 4,
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x50 ---------------------------------- */
+ [BTTV_BOARD_HAUPPAUGEPVR] = {
+ .name = "Hauppauge WinTV PVR",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 0, 1, 1),
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+
+ .gpiomask = 7,
+ .gpiomux = {7},
+ },
+ [BTTV_BOARD_GVBCTV5PCI] = {
+ .name = "IODATA GV-BCTV5/PCI",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x0f0f80,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = {0x030000, 0x010000, 0, 0 },
+ .gpiomute = 0x020000,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= gvbctv5pci_audio,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_OSPREY1x0] = {
+ .name = "Osprey 100/150 (878)", /* 0x1(2|3)-45C6-C1 */
+ .video_inputs = 4, /* id-inputs-clock */
+ /* .audio_inputs= 0, */
+ .svhs = 3,
+ .muxsel = MUXSEL(3, 2, 0, 1),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ [BTTV_BOARD_OSPREY1x0_848] = {
+ .name = "Osprey 100/150 (848)", /* 0x04-54C0-C1 & older boards */
+ .video_inputs = 3,
+ /* .audio_inputs= 0, */
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 3, 1),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+
+ /* ---- card 0x54 ---------------------------------- */
+ [BTTV_BOARD_OSPREY101_848] = {
+ .name = "Osprey 101 (848)", /* 0x05-40C0-C1 */
+ .video_inputs = 2,
+ /* .audio_inputs= 0, */
+ .svhs = 1,
+ .muxsel = MUXSEL(3, 1),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ [BTTV_BOARD_OSPREY1x1] = {
+ .name = "Osprey 101/151", /* 0x1(4|5)-0004-C4 */
+ .video_inputs = 1,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(0),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ [BTTV_BOARD_OSPREY1x1_SVID] = {
+ .name = "Osprey 101/151 w/ svid", /* 0x(16|17|20)-00C4-C1 */
+ .video_inputs = 2,
+ /* .audio_inputs= 0, */
+ .svhs = 1,
+ .muxsel = MUXSEL(0, 1),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ [BTTV_BOARD_OSPREY2xx] = {
+ .name = "Osprey 200/201/250/251", /* 0x1(8|9|E|F)-0004-C4 */
+ .video_inputs = 1,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(0),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+
+ /* ---- card 0x58 ---------------------------------- */
+ [BTTV_BOARD_OSPREY2x0_SVID] = {
+ .name = "Osprey 200/250", /* 0x1(A|B)-00C4-C1 */
+ .video_inputs = 2,
+ /* .audio_inputs= 1, */
+ .svhs = 1,
+ .muxsel = MUXSEL(0, 1),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ [BTTV_BOARD_OSPREY2x0] = {
+ .name = "Osprey 210/220/230", /* 0x1(A|B)-04C0-C1 */
+ .video_inputs = 2,
+ /* .audio_inputs= 1, */
+ .svhs = 1,
+ .muxsel = MUXSEL(2, 3),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ [BTTV_BOARD_OSPREY500] = {
+ .name = "Osprey 500", /* 500 */
+ .video_inputs = 2,
+ /* .audio_inputs= 1, */
+ .svhs = 1,
+ .muxsel = MUXSEL(2, 3),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ [BTTV_BOARD_OSPREY540] = {
+ .name = "Osprey 540", /* 540 */
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+
+ /* ---- card 0x5C ---------------------------------- */
+ [BTTV_BOARD_OSPREY2000] = {
+ .name = "Osprey 2000", /* 2000 */
+ .video_inputs = 2,
+ /* .audio_inputs= 1, */
+ .svhs = 1,
+ .muxsel = MUXSEL(2, 3),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1, /* must avoid, conflicts with the bt860 */
+ },
+ [BTTV_BOARD_IDS_EAGLE] = {
+ /* M G Berberich <berberic@forwiss.uni-passau.de> */
+ .name = "IDS Eagle",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 2, 2, 2),
+ .muxsel_hook = eagle_muxsel,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ },
+ [BTTV_BOARD_PINNACLESAT] = {
+ .name = "Pinnacle PCTV Sat",
+ .video_inputs = 2,
+ /* .audio_inputs= 0, */
+ .svhs = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .muxsel = MUXSEL(3, 1),
+ .pll = PLL_28,
+ .no_gpioirq = 1,
+ .has_dvb = 1,
+ },
+ [BTTV_BOARD_FORMAC_PROTV] = {
+ .name = "Formac ProTV II (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 2,
+ /* TV, Comp1, Composite over SVID con, SVID */
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 2, 0, 0 },
+ .pll = PLL_28,
+ .has_radio = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ /* sound routing:
+ GPIO=0x00,0x01,0x03: mute (?)
+ 0x02: both TV and radio (tuner: FM1216/I)
+ The card has onboard audio connectors labeled "cdrom" and "board",
+ not soldered here, though unknown wiring.
+ Card lacks: external audio in, pci subsystem id.
+ */
+ },
+
+ /* ---- card 0x60 ---------------------------------- */
+ [BTTV_BOARD_MACHTV] = {
+ .name = "MachTV",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 3},
+ .gpiomute = 4,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ },
+ [BTTV_BOARD_EURESYS_PICOLO] = {
+ .name = "Euresys Picolo",
+ .video_inputs = 3,
+ /* .audio_inputs= 0, */
+ .svhs = 2,
+ .gpiomask = 0,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .muxsel = MUXSEL(2, 0, 1),
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_PV150] = {
+ /* Luc Van Hoeylandt <luc@e-magic.be> */
+ .name = "ProVideo PV150", /* 0x4f */
+ .video_inputs = 2,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3),
+ .gpiomux = { 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_AD_TVK503] = {
+ /* Hiroshi Takekawa <sian@big.or.jp> */
+ /* This card lacks subsystem ID */
+ .name = "AD-TVK503", /* 0x63 */
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x001e8007,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ /* Tuner, Radio, external, internal, off, on */
+ .gpiomux = { 0x08, 0x0f, 0x0a, 0x08 },
+ .gpiomute = 0x0f,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ .audio_mode_gpio= adtvk503_audio,
+ },
+
+ /* ---- card 0x64 ---------------------------------- */
+ [BTTV_BOARD_HERCULES_SM_TV] = {
+ .name = "Hercules Smart TV Stereo",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ /* Notes:
+ - card lacks subsystem ID
+ - stereo variant w/ daughter board with tda9874a @0xb0
+ - Audio Routing:
+ always from tda9874 independent of GPIO (?)
+ external line in: unknown
+ - Other chips: em78p156elp @ 0x96 (probably IR remote control)
+ hef4053 (instead 4052) for unknown function
+ */
+ },
+ [BTTV_BOARD_PACETV] = {
+ .name = "Pace TV & Radio Card",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ /* Tuner, CVid, SVid, CVid over SVid connector */
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomask = 0,
+ .no_tda7432 = 1,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 1,
+ .pll = PLL_28,
+ /* Bt878, Bt832, FI1246 tuner; no pci subsystem id
+ only internal line out: (4pin header) RGGL
+ Radio must be decoded by msp3410d (not routed through)*/
+ /*
+ .digital_mode = DIGITAL_MODE_CAMERA, todo!
+ */
+ },
+ [BTTV_BOARD_IVC200] = {
+ /* Chris Willing <chris@vislab.usyd.edu.au> */
+ .name = "IVC-200",
+ .video_inputs = 1,
+ /* .audio_inputs= 0, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS,
+ .gpiomask = 0xdf,
+ .muxsel = MUXSEL(2),
+ .pll = PLL_28,
+ },
+ [BTTV_BOARD_IVCE8784] = {
+ .name = "IVCE-8784",
+ .video_inputs = 1,
+ /* .audio_inputs= 0, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS,
+ .gpiomask = 0xdf,
+ .muxsel = MUXSEL(2),
+ .pll = PLL_28,
+ },
+ [BTTV_BOARD_XGUARD] = {
+ .name = "Grand X-Guard / Trust 814PCI",
+ .video_inputs = 16,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .gpiomask2 = 0xff,
+ .muxsel = MUXSEL(2,2,2,2, 3,3,3,3, 1,1,1,1, 0,0,0,0),
+ .muxsel_hook = xguard_muxsel,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .pll = PLL_28,
+ },
+
+ /* ---- card 0x68 ---------------------------------- */
+ [BTTV_BOARD_NEBULA_DIGITV] = {
+ .name = "Nebula Electronics DigiTV",
+ .video_inputs = 1,
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .has_dvb = 1,
+ .has_remote = 1,
+ .gpiomask = 0x1b,
+ .no_gpioirq = 1,
+ },
+ [BTTV_BOARD_PV143] = {
+ /* Jorge Boncompte - DTI2 <jorge@dti2.net> */
+ .name = "ProVideo PV143",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD009X1_VD011_MINIDIN] = {
+ /* M.Klahr@phytec.de */
+ .name = "PHYTEC VD-009-X1 VD-011 MiniDIN (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = 3,
+ .gpiomask = 0x00,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD009X1_VD011_COMBI] = {
+ .name = "PHYTEC VD-009-X1 VD-011 Combi (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = 3,
+ .gpiomask = 0x00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x6c ---------------------------------- */
+ [BTTV_BOARD_VD009_MINIDIN] = {
+ .name = "PHYTEC VD-009 MiniDIN (bt878)",
+ .video_inputs = 10,
+ /* .audio_inputs= 0, */
+ .svhs = 9,
+ .gpiomask = 0x00,
+ .gpiomask2 = 0x03, /* used for external vodeo mux */
+ .muxsel = MUXSEL(2, 2, 2, 2, 3, 3, 3, 3, 1, 0),
+ .muxsel_hook = phytec_muxsel,
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD009_COMBI] = {
+ .name = "PHYTEC VD-009 Combi (bt878)",
+ .video_inputs = 10,
+ /* .audio_inputs= 0, */
+ .svhs = 9,
+ .gpiomask = 0x00,
+ .gpiomask2 = 0x03, /* used for external vodeo mux */
+ .muxsel = MUXSEL(2, 2, 2, 2, 3, 3, 3, 3, 1, 1),
+ .muxsel_hook = phytec_muxsel,
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_IVC100] = {
+ .name = "IVC-100",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS,
+ .gpiomask = 0xdf,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .pll = PLL_28,
+ },
+ [BTTV_BOARD_IVC120] = {
+ /* IVC-120G - Alan Garfield <alan@fromorbit.com> */
+ .name = "IVC-120G",
+ .video_inputs = 16,
+ /* .audio_inputs= 0, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS, /* card has no svhs */
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .gpiomask = 0x00,
+ .muxsel = MUXSEL(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
+ .muxsel_hook = ivc120_muxsel,
+ .pll = PLL_28,
+ },
+
+ /* ---- card 0x70 ---------------------------------- */
+ [BTTV_BOARD_PC_HDTV] = {
+ .name = "pcHDTV HD-2000 TV",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .tuner_type = TUNER_PHILIPS_FCV1236D,
+ .tuner_addr = ADDR_UNSET,
+ .has_dvb = 1,
+ },
+ [BTTV_BOARD_TWINHAN_DST] = {
+ .name = "Twinhan DST + clones",
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_video = 1,
+ .has_dvb = 1,
+ },
+ [BTTV_BOARD_WINFASTVC100] = {
+ .name = "Winfast VC100",
+ .video_inputs = 3,
+ /* .audio_inputs= 0, */
+ .svhs = 1,
+ /* Vid In, SVid In, Vid over SVid in connector */
+ .muxsel = MUXSEL(3, 1, 1, 3),
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ },
+ [BTTV_BOARD_TEV560] = {
+ .name = "Teppro TEV-560/InterVision IV-560",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 3,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 1, 1, 1 },
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_35,
+ },
+
+ /* ---- card 0x74 ---------------------------------- */
+ [BTTV_BOARD_SIMUS_GVC1100] = {
+ .name = "SIMUS GVC1100",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .muxsel = MUXSEL(2, 2, 2, 2),
+ .gpiomask = 0x3F,
+ .muxsel_hook = gvc1100_muxsel,
+ },
+ [BTTV_BOARD_NGSTV_PLUS] = {
+ /* Carlos Silva r3pek@r3pek.homelinux.org || card 0x75 */
+ .name = "NGS NGSTV+",
+ .video_inputs = 3,
+ .svhs = 2,
+ .gpiomask = 0x008007,
+ .muxsel = MUXSEL(2, 3, 0, 0),
+ .gpiomux = { 0, 0, 0, 0 },
+ .gpiomute = 0x000003,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_LMLBT4] = {
+ /* http://linuxmedialabs.com */
+ .name = "LMLBT4",
+ .video_inputs = 4, /* IN1,IN2,IN3,IN4 */
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TEKRAM_M205] = {
+ /* Helmroos Harri <harri.helmroos@pp.inet.fi> */
+ .name = "Tekram M205 PRO",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = 2,
+ .gpiomask = 0x68,
+ .muxsel = MUXSEL(2, 3, 1),
+ .gpiomux = { 0x68, 0x68, 0x61, 0x61 },
+ .pll = PLL_28,
+ },
+
+ /* ---- card 0x78 ---------------------------------- */
+ [BTTV_BOARD_CONTVFMI] = {
+ /* Javier Cendan Ares <jcendan@lycos.es> */
+ /* bt878 TV + FM without subsystem ID */
+ .name = "Conceptronic CONTVFMi",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x008007,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 2 },
+ .gpiomute = 3,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_PICOLO_TETRA_CHIP] = {
+ /*Eric DEBIEF <debief@telemsa.com>*/
+ /*EURESYS Picolo Tetra : 4 Conexant Fusion 878A, no audio, video input set with analog multiplexers GPIO controlled*/
+ /* adds picolo_tetra_muxsel(), picolo_tetra_init(), the following declaration strucure, and #define BTTV_BOARD_PICOLO_TETRA_CHIP*/
+ /*0x79 in bttv.h*/
+ .name = "Euresys Picolo Tetra",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0,
+ .gpiomask2 = 0x3C<<16,/*Set the GPIO[18]->GPIO[21] as output pin.==> drive the video inputs through analog multiplexers*/
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ /*878A input is always MUX0, see above.*/
+ .muxsel = MUXSEL(2, 2, 2, 2),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .pll = PLL_28,
+ .muxsel_hook = picolo_tetra_muxsel,/*Required as it doesn't follow the classic input selection policy*/
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_SPIRIT_TV] = {
+ /* Spirit TV Tuner from http://spiritmodems.com.au */
+ /* Stafford Goodsell <surge@goliath.homeunix.org> */
+ .name = "Spirit TV Tuner",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x0000000f,
+ .muxsel = MUXSEL(2, 1, 1),
+ .gpiomux = { 0x02, 0x00, 0x00, 0x00 },
+ .tuner_type = TUNER_TEMIC_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ },
+ [BTTV_BOARD_AVDVBT_771] = {
+ /* Wolfram Joost <wojo@frokaschwei.de> */
+ .name = "AVerMedia AVerTV DVB-T 771",
+ .video_inputs = 2,
+ .svhs = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .muxsel = MUXSEL(3, 3),
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .pll = PLL_28,
+ .has_dvb = 1,
+ .no_gpioirq = 1,
+ .has_remote = 1,
+ },
+ /* ---- card 0x7c ---------------------------------- */
+ [BTTV_BOARD_AVDVBT_761] = {
+ /* Matt Jesson <dvb@jesson.eclipse.co.uk> */
+ /* Based on the Nebula card data - added remote and new card number - BTTV_BOARD_AVDVBT_761, see also ir-kbd-gpio.c */
+ .name = "AverMedia AverTV DVB-T 761",
+ .video_inputs = 2,
+ .svhs = 1,
+ .muxsel = MUXSEL(3, 1, 2, 0), /* Comp0, S-Video, ?, ? */
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .has_dvb = 1,
+ .no_gpioirq = 1,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_MATRIX_VISIONSQ] = {
+ /* andre.schwarz@matrix-vision.de */
+ .name = "MATRIX Vision Sigma-SQ",
+ .video_inputs = 16,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x0,
+ .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3),
+ .muxsel_hook = sigmaSQ_muxsel,
+ .gpiomux = { 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MATRIX_VISIONSLC] = {
+ /* andre.schwarz@matrix-vision.de */
+ .name = "MATRIX Vision Sigma-SLC",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x0,
+ .muxsel = MUXSEL(2, 2, 2, 2),
+ .muxsel_hook = sigmaSLC_muxsel,
+ .gpiomux = { 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ /* BTTV_BOARD_APAC_VIEWCOMP */
+ [BTTV_BOARD_APAC_VIEWCOMP] = {
+ /* Attila Kondoros <attila.kondoros@chello.hu> */
+ /* bt878 TV + FM 0x00000000 subsystem ID */
+ .name = "APAC Viewcomp 878(AMAX)",
+ .video_inputs = 2,
+ /* .audio_inputs= 1, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0xFF,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 0, 0, 0 },
+ .gpiomute = 10,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1, /* miniremote works, see ir-kbd-gpio.c */
+ .has_radio = 1, /* not every card has radio */
+ },
+
+ /* ---- card 0x80 ---------------------------------- */
+ [BTTV_BOARD_DVICO_DVBT_LITE] = {
+ /* Chris Pascoe <c.pascoe@itee.uq.edu.au> */
+ .name = "DViCO FusionHDTV DVB-T Lite",
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .pll = PLL_28,
+ .no_video = 1,
+ .has_dvb = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VGEAR_MYVCD] = {
+ /* Steven <photon38@pchome.com.tw> */
+ .name = "V-Gear MyVCD",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x3f,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = {0x31, 0x31, 0x31, 0x31 },
+ .gpiomute = 0x31,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 0,
+ },
+ [BTTV_BOARD_SUPER_TV] = {
+ /* Rick C <cryptdragoon@gmail.com> */
+ .name = "Super TV Tuner",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ .gpiomask = 0x008007,
+ .gpiomux = { 0, 0x000001,0,0 },
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_TIBET_CS16] = {
+ /* Chris Fanning <video4linux@haydon.net> */
+ .name = "Tibet Systems 'Progress DVR' CS16",
+ .video_inputs = 16,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2),
+ .pll = PLL_28,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .muxsel_hook = tibetCS16_muxsel,
+ },
+ [BTTV_BOARD_KODICOM_4400R] = {
+ /* Bill Brack <wbrack@mmm.com.hk> */
+ /*
+ * Note that, because of the card's wiring, the "master"
+ * BT878A chip (i.e. the one which controls the analog switch
+ * and must use this card type) is the 2nd one detected. The
+ * other 3 chips should use card type 0x85, whose description
+ * follows this one. There is a EEPROM on the card (which is
+ * connected to the I2C of one of those other chips), but is
+ * not currently handled. There is also a facility for a
+ * "monitor", which is also not currently implemented.
+ */
+ .name = "Kodicom 4400R (master)",
+ .video_inputs = 16,
+ /* .audio_inputs= 0, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS,
+ /* GPIO bits 0-9 used for analog switch:
+ * 00 - 03: camera selector
+ * 04 - 06: channel (controller) selector
+ * 07: data (1->on, 0->off)
+ * 08: strobe
+ * 09: reset
+ * bit 16 is input from sync separator for the channel
+ */
+ .gpiomask = 0x0003ff,
+ .no_gpioirq = 1,
+ .muxsel = MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3),
+ .pll = PLL_28,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .muxsel_hook = kodicom4400r_muxsel,
+ },
+ [BTTV_BOARD_KODICOM_4400R_SL] = {
+ /* Bill Brack <wbrack@mmm.com.hk> */
+ /* Note that, for reasons unknown, the "master" BT878A chip (i.e. the
+ * one which controls the analog switch, and must use the card type)
+ * is the 2nd one detected. The other 3 chips should use this card
+ * type
+ */
+ .name = "Kodicom 4400R (slave)",
+ .video_inputs = 16,
+ /* .audio_inputs= 0, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS,
+ .gpiomask = 0x010000,
+ .no_gpioirq = 1,
+ .muxsel = MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3),
+ .pll = PLL_28,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .muxsel_hook = kodicom4400r_muxsel,
+ },
+ /* ---- card 0x86---------------------------------- */
+ [BTTV_BOARD_ADLINK_RTV24] = {
+ /* Michael Henson <mhenson@clarityvi.com> */
+ /* Adlink RTV24 with special unlock codes */
+ .name = "Adlink RTV24",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ },
+ /* ---- card 0x87---------------------------------- */
+ [BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE] = {
+ /* Michael Krufky <mkrufky@m1k.net> */
+ .name = "DViCO FusionHDTV 5 Lite",
+ .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H064F */
+ .tuner_addr = ADDR_UNSET,
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 3, 1),
+ .gpiomask = 0x00e00007,
+ .gpiomux = { 0x00400005, 0, 0x00000001, 0 },
+ .gpiomute = 0x00c00007,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .has_dvb = 1,
+ },
+ /* ---- card 0x88---------------------------------- */
+ [BTTV_BOARD_ACORP_Y878F] = {
+ /* Mauro Carvalho Chehab <mchehab@infradead.org> */
+ .name = "Acorp Y878F",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x01fe00,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x001e00, 0, 0x018000, 0x014000 },
+ .gpiomute = 0x002000,
+ .pll = PLL_28,
+ .tuner_type = TUNER_YMEC_TVF66T5_B_DFF,
+ .tuner_addr = 0xc1 >>1,
+ .has_radio = 1,
+ },
+ /* ---- card 0x89 ---------------------------------- */
+ [BTTV_BOARD_CONCEPTRONIC_CTVFMI2] = {
+ .name = "Conceptronic CTVFMi v2",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x001c0007,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 2 },
+ .gpiomute = 3,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TENA_9533_DI,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ .has_radio = 1,
+ },
+ /* ---- card 0x8a ---------------------------------- */
+ [BTTV_BOARD_PV_BT878P_2E] = {
+ .name = "Prolink Pixelview PV-BT878P+ (Rev.2E)",
+ .video_inputs = 5,
+ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .has_dig_in = 1,
+ .gpiomask = 0x01fe00,
+ .muxsel = MUXSEL(2, 3, 1, 1, 0), /* in 4 is digital */
+ /* .digital_mode= DIGITAL_MODE_CAMERA, */
+ .gpiomux = { 0x00400, 0x10400, 0x04400, 0x80000 },
+ .gpiomute = 0x12400,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_LG_PAL_FM,
+ .tuner_addr = ADDR_UNSET,
+ .has_remote = 1,
+ },
+ /* ---- card 0x8b ---------------------------------- */
+ [BTTV_BOARD_PV_M4900] = {
+ /* Sérgio Fortier <sergiofortier@yahoo.com.br> */
+ .name = "Prolink PixelView PlayTV MPEG2 PV-M4900",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x3f,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x21, 0x20, 0x24, 0x2c },
+ .gpiomute = 0x29,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_YMEC_TVF_5533MF,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 1,
+ .has_remote = 1,
+ },
+ /* ---- card 0x8c ---------------------------------- */
+ /* Has four Bt878 chips behind a PCI bridge, each chip has:
+ one external BNC composite input (mux 2)
+ three internal composite inputs (unknown muxes)
+ an 18-bit stereo A/D (CS5331A), which has:
+ one external stereo unblanced (RCA) audio connection
+ one (or 3?) internal stereo balanced (XLR) audio connection
+ input is selected via gpio to a 14052B mux
+ (mask=0x300, unbal=0x000, bal=0x100, ??=0x200,0x300)
+ gain is controlled via an X9221A chip on the I2C bus @0x28
+ sample rate is controlled via gpio to an MK1413S
+ (mask=0x3, 32kHz=0x0, 44.1kHz=0x1, 48kHz=0x2, ??=0x3)
+ There is neither a tuner nor an svideo input. */
+ [BTTV_BOARD_OSPREY440] = {
+ .name = "Osprey 440",
+ .video_inputs = 4,
+ /* .audio_inputs= 2, */
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(2, 3, 0, 1), /* 3,0,1 are guesses */
+ .gpiomask = 0x303,
+ .gpiomute = 0x000, /* int + 32kHz */
+ .gpiomux = { 0, 0, 0x000, 0x100},
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ /* ---- card 0x8d ---------------------------------- */
+ [BTTV_BOARD_ASOUND_SKYEYE] = {
+ .name = "Asound Skyeye PCTV",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 15,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 0, 0, 0 },
+ .gpiomute = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+ },
+ /* ---- card 0x8e ---------------------------------- */
+ [BTTV_BOARD_SABRENT_TVFM] = {
+ .name = "Sabrent TV-FM (bttv version)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x108007,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 100000, 100002, 100002, 100000 },
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TNF_5335MF,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 1,
+ },
+ /* ---- card 0x8f ---------------------------------- */
+ [BTTV_BOARD_HAUPPAUGE_IMPACTVCB] = {
+ .name = "Hauppauge ImpactVCB (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x0f, /* old: 7 */
+ .muxsel = MUXSEL(0, 1, 3, 2), /* Composite 0-3 */
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MACHTV_MAGICTV] = {
+ /* Julian Calaby <julian.calaby@gmail.com>
+ * Slightly different from original MachTV definition (0x60)
+
+ * FIXME: RegSpy says gpiomask should be "0x001c800f", but it
+ * stuffs up remote chip. Bug is a pin on the jaecs is not set
+ * properly (methinks) causing no keyup bits being set */
+
+ .name = "MagicTV", /* rebranded MachTV */
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .tuner_type = TUNER_TEMIC_4009FR5_PAL,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_SSAI_SECURITY] = {
+ .name = "SSAI Security Video Interface",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(0, 1, 2, 3),
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_SSAI_ULTRASOUND] = {
+ .name = "SSAI Ultrasound Video Interface",
+ .video_inputs = 2,
+ /* .audio_inputs= 0, */
+ .svhs = 1,
+ .muxsel = MUXSEL(2, 0, 1, 3),
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ /* ---- card 0x94---------------------------------- */
+ [BTTV_BOARD_DVICO_FUSIONHDTV_2] = {
+ .name = "DViCO FusionHDTV 2",
+ .tuner_type = TUNER_PHILIPS_FCV1236D,
+ .tuner_addr = ADDR_UNSET,
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .muxsel = MUXSEL(2, 3, 1),
+ .gpiomask = 0x00e00007,
+ .gpiomux = { 0x00400005, 0, 0x00000001, 0 },
+ .gpiomute = 0x00c00007,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ },
+ /* ---- card 0x95---------------------------------- */
+ [BTTV_BOARD_TYPHOON_TVTUNERPCI] = {
+ .name = "Typhoon TV-Tuner PCI (50684)",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x3014f,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x20001,0x10001, 0, 0 },
+ .gpiomute = 10,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_GEOVISION_GV600] = {
+ /* emhn@usb.ve */
+ .name = "Geovision GV-600",
+ .video_inputs = 16,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x0,
+ .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2),
+ .muxsel_hook = geovision_muxsel,
+ .gpiomux = { 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_KOZUMI_KTV_01C] = {
+ /* Mauro Lacy <mauro@lacy.com.ar>
+ * Based on MagicTV and Conceptronic CONTVFMi */
+
+ .name = "Kozumi KTV-01C",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x008007,
+ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 2 }, /* CONTVFMi */
+ .gpiomute = 3, /* CONTVFMi */
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* TCL MK3 */
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_ENLTV_FM_2] = {
+ /* Encore TV Tuner Pro ENL TV-FM-2
+ Mauro Carvalho Chehab <mchehab@infradead.org */
+ .name = "Encore ENL TV-FM-2",
+ .video_inputs = 3,
+ /* .audio_inputs= 1, */
+ .svhs = 2,
+ /* bit 6 -> IR disabled
+ bit 18/17 = 00 -> mute
+ 01 -> enable external audio input
+ 10 -> internal audio input (mono?)
+ 11 -> internal audio input
+ */
+ .gpiomask = 0x060040,
+ .muxsel = MUXSEL(2, 3, 3),
+ .gpiomux = { 0x60000, 0x60000, 0x20000, 0x20000 },
+ .gpiomute = 0,
+ .tuner_type = TUNER_TCL_MF02GIP_5N,
+ .tuner_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_VD012] = {
+ /* D.Heer@Phytec.de */
+ .name = "PHYTEC VD-012 (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0x00,
+ .muxsel = MUXSEL(0, 2, 3, 1),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD012_X1] = {
+ /* D.Heer@Phytec.de */
+ .name = "PHYTEC VD-012-X1 (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = 3,
+ .gpiomask = 0x00,
+ .muxsel = MUXSEL(2, 3, 1),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD012_X2] = {
+ /* D.Heer@Phytec.de */
+ .name = "PHYTEC VD-012-X2 (bt878)",
+ .video_inputs = 4,
+ /* .audio_inputs= 0, */
+ .svhs = 3,
+ .gpiomask = 0x00,
+ .muxsel = MUXSEL(3, 2, 1),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_GEOVISION_GV800S] = {
+ /* Bruno Christo <bchristo@inf.ufsm.br>
+ *
+ * GeoVision GV-800(S) has 4 Conexant Fusion 878A:
+ * 1 audio input per BT878A = 4 audio inputs
+ * 4 video inputs per BT878A = 16 video inputs
+ * This is the first BT878A chip of the GV-800(S). It's the
+ * "master" chip and it controls the video inputs through an
+ * analog multiplexer (a CD22M3494) via some GPIO pins. The
+ * slaves should use card type 0x9e (following this one).
+ * There is a EEPROM on the card which is currently not handled.
+ * The audio input is not working yet.
+ */
+ .name = "Geovision GV-800(S) (master)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS,
+ .gpiomask = 0xf107f,
+ .no_gpioirq = 1,
+ .muxsel = MUXSEL(2, 2, 2, 2),
+ .pll = PLL_28,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .muxsel_hook = gv800s_muxsel,
+ },
+ [BTTV_BOARD_GEOVISION_GV800S_SL] = {
+ /* Bruno Christo <bchristo@inf.ufsm.br>
+ *
+ * GeoVision GV-800(S) has 4 Conexant Fusion 878A:
+ * 1 audio input per BT878A = 4 audio inputs
+ * 4 video inputs per BT878A = 16 video inputs
+ * The 3 other BT878A chips are "slave" chips of the GV-800(S)
+ * and should use this card type.
+ * The audio input is not working yet.
+ */
+ .name = "Geovision GV-800(S) (slave)",
+ .video_inputs = 4,
+ /* .audio_inputs= 1, */
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ .svhs = NO_SVHS,
+ .gpiomask = 0x00,
+ .no_gpioirq = 1,
+ .muxsel = MUXSEL(2, 2, 2, 2),
+ .pll = PLL_28,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+ .muxsel_hook = gv800s_muxsel,
+ },
+ [BTTV_BOARD_PV183] = {
+ .name = "ProVideo PV183", /* 0x9f */
+ .video_inputs = 2,
+ /* .audio_inputs= 0, */
+ .svhs = NO_SVHS,
+ .gpiomask = 0,
+ .muxsel = MUXSEL(2, 3),
+ .gpiomux = { 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TVT_TD3116] = {
+ .name = "Tongwei Video Technology TD-3116",
+ .video_inputs = 16,
+ .gpiomask = 0xc00ff,
+ .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2),
+ .muxsel_hook = td3116_muxsel,
+ .svhs = NO_SVHS,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ABSENT,
+ },
+ [BTTV_BOARD_APOSONIC_WDVR] = {
+ .name = "Aposonic W-DVR",
+ .video_inputs = 4,
+ .svhs = NO_SVHS,
+ .muxsel = MUXSEL(2, 3, 1, 0),
+ .tuner_type = TUNER_ABSENT,
+ },
+
+};
+
+static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards);
+
+/* ----------------------------------------------------------------------- */
+
+static unsigned char eeprom_data[256];
+
+/*
+ * identify card
+ */
+void __devinit bttv_idcard(struct bttv *btv)
+{
+ unsigned int gpiobits;
+ int i,type;
+
+ /* read PCI subsystem ID */
+ btv->cardid = btv->c.pci->subsystem_device << 16;
+ btv->cardid |= btv->c.pci->subsystem_vendor;
+
+ if (0 != btv->cardid && 0xffffffff != btv->cardid) {
+ /* look for the card */
+ for (type = -1, i = 0; cards[i].id != 0; i++)
+ if (cards[i].id == btv->cardid)
+ type = i;
+
+ if (type != -1) {
+ /* found it */
+ pr_info("%d: detected: %s [card=%d], PCI subsystem ID is %04x:%04x\n",
+ btv->c.nr, cards[type].name, cards[type].cardnr,
+ btv->cardid & 0xffff,
+ (btv->cardid >> 16) & 0xffff);
+ btv->c.type = cards[type].cardnr;
+ } else {
+ /* 404 */
+ pr_info("%d: subsystem: %04x:%04x (UNKNOWN)\n",
+ btv->c.nr, btv->cardid & 0xffff,
+ (btv->cardid >> 16) & 0xffff);
+ pr_debug("please mail id, board name and the correct card= insmod option to linux-media@vger.kernel.org\n");
+ }
+ }
+
+ /* let the user override the autodetected type */
+ if (card[btv->c.nr] < bttv_num_tvcards)
+ btv->c.type=card[btv->c.nr];
+
+ /* print which card config we are using */
+ pr_info("%d: using: %s [card=%d,%s]\n",
+ btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type,
+ card[btv->c.nr] < bttv_num_tvcards
+ ? "insmod option" : "autodetected");
+
+ /* overwrite gpio stuff ?? */
+ if (UNSET == audioall && UNSET == audiomux[0])
+ return;
+
+ if (UNSET != audiomux[0]) {
+ gpiobits = 0;
+ for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) {
+ bttv_tvcards[btv->c.type].gpiomux[i] = audiomux[i];
+ gpiobits |= audiomux[i];
+ }
+ } else {
+ gpiobits = audioall;
+ for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) {
+ bttv_tvcards[btv->c.type].gpiomux[i] = audioall;
+ }
+ }
+ bttv_tvcards[btv->c.type].gpiomask = (UNSET != gpiomask) ? gpiomask : gpiobits;
+ pr_info("%d: gpio config override: mask=0x%x, mux=",
+ btv->c.nr, bttv_tvcards[btv->c.type].gpiomask);
+ for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) {
+ pr_cont("%s0x%x",
+ i ? "," : "", bttv_tvcards[btv->c.type].gpiomux[i]);
+ }
+ pr_cont("\n");
+}
+
+/*
+ * (most) board specific initialisations goes here
+ */
+
+/* Some Modular Technology cards have an eeprom, but no subsystem ID */
+static void identify_by_eeprom(struct bttv *btv, unsigned char eeprom_data[256])
+{
+ int type = -1;
+
+ if (0 == strncmp(eeprom_data,"GET MM20xPCTV",13))
+ type = BTTV_BOARD_MODTEC_205;
+ else if (0 == strncmp(eeprom_data+20,"Picolo",7))
+ type = BTTV_BOARD_EURESYS_PICOLO;
+ else if (eeprom_data[0] == 0x84 && eeprom_data[2]== 0)
+ type = BTTV_BOARD_HAUPPAUGE; /* old bt848 */
+
+ if (-1 != type) {
+ btv->c.type = type;
+ pr_info("%d: detected by eeprom: %s [card=%d]\n",
+ btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type);
+ }
+}
+
+static void flyvideo_gpio(struct bttv *btv)
+{
+ int gpio, has_remote, has_radio, is_capture_only;
+ int is_lr90, has_tda9820_tda9821;
+ int tuner_type = UNSET, ttype;
+
+ gpio_inout(0xffffff, 0);
+ udelay(8); /* without this we would see the 0x1800 mask */
+ gpio = gpio_read();
+ /* FIXME: must restore OUR_EN ??? */
+
+ /* all cards provide GPIO info, some have an additional eeprom
+ * LR50: GPIO coding can be found lower right CP1 .. CP9
+ * CP9=GPIO23 .. CP1=GPIO15; when OPEN, the corresponding GPIO reads 1.
+ * GPIO14-12: n.c.
+ * LR90: GP9=GPIO23 .. GP1=GPIO15 (right above the bt878)
+
+ * lowest 3 bytes are remote control codes (no handshake needed)
+ * xxxFFF: No remote control chip soldered
+ * xxxF00(LR26/LR50), xxxFE0(LR90): Remote control chip (LVA001 or CF45) soldered
+ * Note: Some bits are Audio_Mask !
+ */
+ ttype = (gpio & 0x0f0000) >> 16;
+ switch (ttype) {
+ case 0x0:
+ tuner_type = 2; /* NTSC, e.g. TPI8NSR11P */
+ break;
+ case 0x2:
+ tuner_type = 39; /* LG NTSC (newer TAPC series) TAPC-H701P */
+ break;
+ case 0x4:
+ tuner_type = 5; /* Philips PAL TPI8PSB02P, TPI8PSB12P, TPI8PSB12D or FI1216, FM1216 */
+ break;
+ case 0x6:
+ tuner_type = 37; /* LG PAL (newer TAPC series) TAPC-G702P */
+ break;
+ case 0xC:
+ tuner_type = 3; /* Philips SECAM(+PAL) FQ1216ME or FI1216MF */
+ break;
+ default:
+ pr_info("%d: FlyVideo_gpio: unknown tuner type\n", btv->c.nr);
+ break;
+ }
+
+ has_remote = gpio & 0x800000;
+ has_radio = gpio & 0x400000;
+ /* unknown 0x200000;
+ * unknown2 0x100000; */
+ is_capture_only = !(gpio & 0x008000); /* GPIO15 */
+ has_tda9820_tda9821 = !(gpio & 0x004000);
+ is_lr90 = !(gpio & 0x002000); /* else LR26/LR50 (LR38/LR51 f. capture only) */
+ /*
+ * gpio & 0x001000 output bit for audio routing */
+
+ if (is_capture_only)
+ tuner_type = TUNER_ABSENT; /* No tuner present */
+
+ pr_info("%d: FlyVideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n",
+ btv->c.nr, has_radio ? "yes" : "no",
+ has_remote ? "yes" : "no", tuner_type, gpio);
+ pr_info("%d: FlyVideo LR90=%s tda9821/tda9820=%s capture_only=%s\n",
+ btv->c.nr, is_lr90 ? "yes" : "no",
+ has_tda9820_tda9821 ? "yes" : "no",
+ is_capture_only ? "yes" : "no");
+
+ if (tuner_type != UNSET) /* only set if known tuner autodetected, else let insmod option through */
+ btv->tuner_type = tuner_type;
+ btv->has_radio = has_radio;
+
+ /* LR90 Audio Routing is done by 2 hef4052, so Audio_Mask has 4 bits: 0x001c80
+ * LR26/LR50 only has 1 hef4052, Audio_Mask 0x000c00
+ * Audio options: from tuner, from tda9821/tda9821(mono,stereo,sap), from tda9874, ext., mute */
+ if (has_tda9820_tda9821)
+ btv->audio_mode_gpio = lt9415_audio;
+ /* todo: if(has_tda9874) btv->audio_mode_gpio = fv2000s_audio; */
+}
+
+static int miro_tunermap[] = { 0,6,2,3, 4,5,6,0, 3,0,4,5, 5,2,16,1,
+ 14,2,17,1, 4,1,4,3, 1,2,16,1, 4,4,4,4 };
+static int miro_fmtuner[] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1,
+ 1,1,1,1, 1,1,1,0, 0,0,0,0, 0,1,0,0 };
+
+static void miro_pinnacle_gpio(struct bttv *btv)
+{
+ int id,msp,gpio;
+ char *info;
+
+ gpio_inout(0xffffff, 0);
+ gpio = gpio_read();
+ id = ((gpio>>10) & 63) -1;
+ msp = bttv_I2CRead(btv, I2C_ADDR_MSP3400, "MSP34xx");
+ if (id < 32) {
+ btv->tuner_type = miro_tunermap[id];
+ if (0 == (gpio & 0x20)) {
+ btv->has_radio = 1;
+ if (!miro_fmtuner[id]) {
+ btv->has_matchbox = 1;
+ btv->mbox_we = (1<<6);
+ btv->mbox_most = (1<<7);
+ btv->mbox_clk = (1<<8);
+ btv->mbox_data = (1<<9);
+ btv->mbox_mask = (1<<6)|(1<<7)|(1<<8)|(1<<9);
+ }
+ } else {
+ btv->has_radio = 0;
+ }
+ if (-1 != msp) {
+ if (btv->c.type == BTTV_BOARD_MIRO)
+ btv->c.type = BTTV_BOARD_MIROPRO;
+ if (btv->c.type == BTTV_BOARD_PINNACLE)
+ btv->c.type = BTTV_BOARD_PINNACLEPRO;
+ }
+ pr_info("%d: miro: id=%d tuner=%d radio=%s stereo=%s\n",
+ btv->c.nr, id+1, btv->tuner_type,
+ !btv->has_radio ? "no" :
+ (btv->has_matchbox ? "matchbox" : "fmtuner"),
+ (-1 == msp) ? "no" : "yes");
+ } else {
+ /* new cards with microtune tuner */
+ id = 63 - id;
+ btv->has_radio = 0;
+ switch (id) {
+ case 1:
+ info = "PAL / mono";
+ btv->tda9887_conf = TDA9887_INTERCARRIER;
+ break;
+ case 2:
+ info = "PAL+SECAM / stereo";
+ btv->has_radio = 1;
+ btv->tda9887_conf = TDA9887_QSS;
+ break;
+ case 3:
+ info = "NTSC / stereo";
+ btv->has_radio = 1;
+ btv->tda9887_conf = TDA9887_QSS;
+ break;
+ case 4:
+ info = "PAL+SECAM / mono";
+ btv->tda9887_conf = TDA9887_QSS;
+ break;
+ case 5:
+ info = "NTSC / mono";
+ btv->tda9887_conf = TDA9887_INTERCARRIER;
+ break;
+ case 6:
+ info = "NTSC / stereo";
+ btv->tda9887_conf = TDA9887_INTERCARRIER;
+ break;
+ case 7:
+ info = "PAL / stereo";
+ btv->tda9887_conf = TDA9887_INTERCARRIER;
+ break;
+ default:
+ info = "oops: unknown card";
+ break;
+ }
+ if (-1 != msp)
+ btv->c.type = BTTV_BOARD_PINNACLEPRO;
+ pr_info("%d: pinnacle/mt: id=%d info=\"%s\" radio=%s\n",
+ btv->c.nr, id, info, btv->has_radio ? "yes" : "no");
+ btv->tuner_type = TUNER_MT2032;
+ }
+}
+
+/* GPIO21 L: Buffer aktiv, H: Buffer inaktiv */
+#define LM1882_SYNC_DRIVE 0x200000L
+
+static void init_ids_eagle(struct bttv *btv)
+{
+ gpio_inout(0xffffff,0xFFFF37);
+ gpio_write(0x200020);
+
+ /* flash strobe inverter ?! */
+ gpio_write(0x200024);
+
+ /* switch sync drive off */
+ gpio_bits(LM1882_SYNC_DRIVE,LM1882_SYNC_DRIVE);
+
+ /* set BT848 muxel to 2 */
+ btaor((2)<<5, ~(2<<5), BT848_IFORM);
+}
+
+/* Muxsel helper for the IDS Eagle.
+ * the eagles does not use the standard muxsel-bits but
+ * has its own multiplexer */
+static void eagle_muxsel(struct bttv *btv, unsigned int input)
+{
+ gpio_bits(3, input & 3);
+
+ /* composite */
+ /* set chroma ADC to sleep */
+ btor(BT848_ADC_C_SLEEP, BT848_ADC);
+ /* set to composite video */
+ btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
+ btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
+
+ /* switch sync drive off */
+ gpio_bits(LM1882_SYNC_DRIVE,LM1882_SYNC_DRIVE);
+}
+
+static void gvc1100_muxsel(struct bttv *btv, unsigned int input)
+{
+ static const int masks[] = {0x30, 0x01, 0x12, 0x23};
+ gpio_write(masks[input%4]);
+}
+
+/* LMLBT4x initialization - to allow access to GPIO bits for sensors input and
+ alarms output
+
+ GPIObit | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ assignment | TI | O3|INx| O2| O1|IN4|IN3|IN2|IN1| | |
+
+ IN - sensor inputs, INx - sensor inputs and TI XORed together
+ O1,O2,O3 - alarm outputs (relays)
+
+ OUT ENABLE 1 1 0 . 1 1 0 0 . 0 0 0 0 = 0x6C0
+
+*/
+
+static void init_lmlbt4x(struct bttv *btv)
+{
+ pr_debug("LMLBT4x init\n");
+ btwrite(0x000000, BT848_GPIO_REG_INP);
+ gpio_inout(0xffffff, 0x0006C0);
+ gpio_write(0x000000);
+}
+
+static void sigmaSQ_muxsel(struct bttv *btv, unsigned int input)
+{
+ unsigned int inmux = input % 8;
+ gpio_inout( 0xf, 0xf );
+ gpio_bits( 0xf, inmux );
+}
+
+static void sigmaSLC_muxsel(struct bttv *btv, unsigned int input)
+{
+ unsigned int inmux = input % 4;
+ gpio_inout( 3<<9, 3<<9 );
+ gpio_bits( 3<<9, inmux<<9 );
+}
+
+static void geovision_muxsel(struct bttv *btv, unsigned int input)
+{
+ unsigned int inmux = input % 16;
+ gpio_inout(0xf, 0xf);
+ gpio_bits(0xf, inmux);
+}
+
+/*
+ * The TD3116 has 2 74HC4051 muxes wired to the MUX0 input of a bt878.
+ * The first 74HC4051 has the lower 8 inputs, the second one the higher 8.
+ * The muxes are controlled via a 74HC373 latch which is connected to
+ * GPIOs 0-7. GPIO 18 is connected to the LE signal of the latch.
+ * Q0 of the latch is connected to the Enable (~E) input of the first
+ * 74HC4051. Q1 - Q3 are connected to S0 - S2 of the same 74HC4051.
+ * Q4 - Q7 are connected to the second 74HC4051 in the same way.
+ */
+
+static void td3116_latch_value(struct bttv *btv, u32 value)
+{
+ gpio_bits((1<<18) | 0xff, value);
+ gpio_bits((1<<18) | 0xff, (1<<18) | value);
+ udelay(1);
+ gpio_bits((1<<18) | 0xff, value);
+}
+
+static void td3116_muxsel(struct bttv *btv, unsigned int input)
+{
+ u32 value;
+ u32 highbit;
+
+ highbit = (input & 0x8) >> 3 ;
+
+ /* Disable outputs and set value in the mux */
+ value = 0x11; /* Disable outputs */
+ value |= ((input & 0x7) << 1) << (4 * highbit);
+ td3116_latch_value(btv, value);
+
+ /* Enable the correct output */
+ value &= ~0x11;
+ value |= ((highbit ^ 0x1) << 4) | highbit;
+ td3116_latch_value(btv, value);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void bttv_reset_audio(struct bttv *btv)
+{
+ /*
+ * BT878A has a audio-reset register.
+ * 1. This register is an audio reset function but it is in
+ * function-0 (video capture) address space.
+ * 2. It is enough to do this once per power-up of the card.
+ * 3. There is a typo in the Conexant doc -- it is not at
+ * 0x5B, but at 0x058. (B is an odd-number, obviously a typo!).
+ * --//Shrikumar 030609
+ */
+ if (btv->id != 878)
+ return;
+
+ if (bttv_debug)
+ pr_debug("%d: BT878A ARESET\n", btv->c.nr);
+ btwrite((1<<7), 0x058);
+ udelay(10);
+ btwrite( 0, 0x058);
+}
+
+/* initialization part one -- before registering i2c bus */
+void __devinit bttv_init_card1(struct bttv *btv)
+{
+ switch (btv->c.type) {
+ case BTTV_BOARD_HAUPPAUGE:
+ case BTTV_BOARD_HAUPPAUGE878:
+ boot_msp34xx(btv,5);
+ break;
+ case BTTV_BOARD_VOODOOTV_200:
+ case BTTV_BOARD_VOODOOTV_FM:
+ boot_msp34xx(btv,20);
+ break;
+ case BTTV_BOARD_AVERMEDIA98:
+ boot_msp34xx(btv,11);
+ break;
+ case BTTV_BOARD_HAUPPAUGEPVR:
+ pvr_boot(btv);
+ break;
+ case BTTV_BOARD_TWINHAN_DST:
+ case BTTV_BOARD_AVDVBT_771:
+ case BTTV_BOARD_PINNACLESAT:
+ btv->use_i2c_hw = 1;
+ break;
+ case BTTV_BOARD_ADLINK_RTV24:
+ init_RTV24( btv );
+ break;
+
+ }
+ if (!bttv_tvcards[btv->c.type].has_dvb)
+ bttv_reset_audio(btv);
+}
+
+/* initialization part two -- after registering i2c bus */
+void __devinit bttv_init_card2(struct bttv *btv)
+{
+ btv->tuner_type = UNSET;
+
+ if (BTTV_BOARD_UNKNOWN == btv->c.type) {
+ bttv_readee(btv,eeprom_data,0xa0);
+ identify_by_eeprom(btv,eeprom_data);
+ }
+
+ switch (btv->c.type) {
+ case BTTV_BOARD_MIRO:
+ case BTTV_BOARD_MIROPRO:
+ case BTTV_BOARD_PINNACLE:
+ case BTTV_BOARD_PINNACLEPRO:
+ /* miro/pinnacle */
+ miro_pinnacle_gpio(btv);
+ break;
+ case BTTV_BOARD_FLYVIDEO_98:
+ case BTTV_BOARD_MAXI:
+ case BTTV_BOARD_LIFE_FLYKIT:
+ case BTTV_BOARD_FLYVIDEO:
+ case BTTV_BOARD_TYPHOON_TVIEW:
+ case BTTV_BOARD_CHRONOS_VS2:
+ case BTTV_BOARD_FLYVIDEO_98FM:
+ case BTTV_BOARD_FLYVIDEO2000:
+ case BTTV_BOARD_FLYVIDEO98EZ:
+ case BTTV_BOARD_CONFERENCETV:
+ case BTTV_BOARD_LIFETEC_9415:
+ flyvideo_gpio(btv);
+ break;
+ case BTTV_BOARD_HAUPPAUGE:
+ case BTTV_BOARD_HAUPPAUGE878:
+ case BTTV_BOARD_HAUPPAUGEPVR:
+ /* pick up some config infos from the eeprom */
+ bttv_readee(btv,eeprom_data,0xa0);
+ hauppauge_eeprom(btv);
+ break;
+ case BTTV_BOARD_AVERMEDIA98:
+ case BTTV_BOARD_AVPHONE98:
+ bttv_readee(btv,eeprom_data,0xa0);
+ avermedia_eeprom(btv);
+ break;
+ case BTTV_BOARD_PXC200:
+ init_PXC200(btv);
+ break;
+ case BTTV_BOARD_PICOLO_TETRA_CHIP:
+ picolo_tetra_init(btv);
+ break;
+ case BTTV_BOARD_VHX:
+ btv->has_radio = 1;
+ btv->has_matchbox = 1;
+ btv->mbox_we = 0x20;
+ btv->mbox_most = 0;
+ btv->mbox_clk = 0x08;
+ btv->mbox_data = 0x10;
+ btv->mbox_mask = 0x38;
+ break;
+ case BTTV_BOARD_VOBIS_BOOSTAR:
+ case BTTV_BOARD_TERRATV:
+ terratec_active_radio_upgrade(btv);
+ break;
+ case BTTV_BOARD_MAGICTVIEW061:
+ if (btv->cardid == 0x3002144f) {
+ btv->has_radio=1;
+ pr_info("%d: radio detected by subsystem id (CPH05x)\n",
+ btv->c.nr);
+ }
+ break;
+ case BTTV_BOARD_STB2:
+ if (btv->cardid == 0x3060121a) {
+ /* Fix up entry for 3DFX VoodooTV 100,
+ which is an OEM STB card variant. */
+ btv->has_radio=0;
+ btv->tuner_type=TUNER_TEMIC_NTSC;
+ }
+ break;
+ case BTTV_BOARD_OSPREY1x0:
+ case BTTV_BOARD_OSPREY1x0_848:
+ case BTTV_BOARD_OSPREY101_848:
+ case BTTV_BOARD_OSPREY1x1:
+ case BTTV_BOARD_OSPREY1x1_SVID:
+ case BTTV_BOARD_OSPREY2xx:
+ case BTTV_BOARD_OSPREY2x0_SVID:
+ case BTTV_BOARD_OSPREY2x0:
+ case BTTV_BOARD_OSPREY440:
+ case BTTV_BOARD_OSPREY500:
+ case BTTV_BOARD_OSPREY540:
+ case BTTV_BOARD_OSPREY2000:
+ bttv_readee(btv,eeprom_data,0xa0);
+ osprey_eeprom(btv, eeprom_data);
+ break;
+ case BTTV_BOARD_IDS_EAGLE:
+ init_ids_eagle(btv);
+ break;
+ case BTTV_BOARD_MODTEC_205:
+ bttv_readee(btv,eeprom_data,0xa0);
+ modtec_eeprom(btv);
+ break;
+ case BTTV_BOARD_LMLBT4:
+ init_lmlbt4x(btv);
+ break;
+ case BTTV_BOARD_TIBET_CS16:
+ tibetCS16_init(btv);
+ break;
+ case BTTV_BOARD_KODICOM_4400R:
+ kodicom4400r_init(btv);
+ break;
+ case BTTV_BOARD_GEOVISION_GV800S:
+ gv800s_init(btv);
+ break;
+ }
+
+ /* pll configuration */
+ if (!(btv->id==848 && btv->revision==0x11)) {
+ /* defaults from card list */
+ if (PLL_28 == bttv_tvcards[btv->c.type].pll) {
+ btv->pll.pll_ifreq=28636363;
+ btv->pll.pll_crystal=BT848_IFORM_XT0;
+ }
+ if (PLL_35 == bttv_tvcards[btv->c.type].pll) {
+ btv->pll.pll_ifreq=35468950;
+ btv->pll.pll_crystal=BT848_IFORM_XT1;
+ }
+ /* insmod options can override */
+ switch (pll[btv->c.nr]) {
+ case 0: /* none */
+ btv->pll.pll_crystal = 0;
+ btv->pll.pll_ifreq = 0;
+ btv->pll.pll_ofreq = 0;
+ break;
+ case 1: /* 28 MHz */
+ case 28:
+ btv->pll.pll_ifreq = 28636363;
+ btv->pll.pll_ofreq = 0;
+ btv->pll.pll_crystal = BT848_IFORM_XT0;
+ break;
+ case 2: /* 35 MHz */
+ case 35:
+ btv->pll.pll_ifreq = 35468950;
+ btv->pll.pll_ofreq = 0;
+ btv->pll.pll_crystal = BT848_IFORM_XT1;
+ break;
+ }
+ }
+ btv->pll.pll_current = -1;
+
+ /* tuner configuration (from card list / autodetect / insmod option) */
+ if (UNSET != bttv_tvcards[btv->c.type].tuner_type)
+ if (UNSET == btv->tuner_type)
+ btv->tuner_type = bttv_tvcards[btv->c.type].tuner_type;
+ if (UNSET != tuner[btv->c.nr])
+ btv->tuner_type = tuner[btv->c.nr];
+
+ if (btv->tuner_type == TUNER_ABSENT)
+ pr_info("%d: tuner absent\n", btv->c.nr);
+ else if (btv->tuner_type == UNSET)
+ pr_warn("%d: tuner type unset\n", btv->c.nr);
+ else
+ pr_info("%d: tuner type=%d\n", btv->c.nr, btv->tuner_type);
+
+ if (autoload != UNSET) {
+ pr_warn("%d: the autoload option is obsolete\n", btv->c.nr);
+ pr_warn("%d: use option msp3400, tda7432 or tvaudio to override which audio module should be used\n",
+ btv->c.nr);
+ }
+
+ if (UNSET == btv->tuner_type)
+ btv->tuner_type = TUNER_ABSENT;
+
+ btv->dig = bttv_tvcards[btv->c.type].has_dig_in ?
+ bttv_tvcards[btv->c.type].video_inputs - 1 : UNSET;
+ btv->svhs = bttv_tvcards[btv->c.type].svhs == NO_SVHS ?
+ UNSET : bttv_tvcards[btv->c.type].svhs;
+ if (svhs[btv->c.nr] != UNSET)
+ btv->svhs = svhs[btv->c.nr];
+ if (remote[btv->c.nr] != UNSET)
+ btv->has_remote = remote[btv->c.nr];
+
+ if (bttv_tvcards[btv->c.type].has_radio)
+ btv->has_radio = 1;
+ if (bttv_tvcards[btv->c.type].has_remote)
+ btv->has_remote = 1;
+ if (!bttv_tvcards[btv->c.type].no_gpioirq)
+ btv->gpioirq = 1;
+ if (bttv_tvcards[btv->c.type].volume_gpio)
+ btv->volume_gpio = bttv_tvcards[btv->c.type].volume_gpio;
+ if (bttv_tvcards[btv->c.type].audio_mode_gpio)
+ btv->audio_mode_gpio = bttv_tvcards[btv->c.type].audio_mode_gpio;
+
+ if (btv->tuner_type == TUNER_ABSENT)
+ return; /* no tuner or related drivers to load */
+
+ if (btv->has_saa6588 || saa6588[btv->c.nr]) {
+ /* Probe for RDS receiver chip */
+ static const unsigned short addrs[] = {
+ 0x20 >> 1,
+ 0x22 >> 1,
+ I2C_CLIENT_END
+ };
+ struct v4l2_subdev *sd;
+
+ sd = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "saa6588", 0, addrs);
+ btv->has_saa6588 = (sd != NULL);
+ }
+
+ /* try to detect audio/fader chips */
+
+ /* First check if the user specified the audio chip via a module
+ option. */
+
+ switch (audiodev[btv->c.nr]) {
+ case -1:
+ return; /* do not load any audio module */
+
+ case 0: /* autodetect */
+ break;
+
+ case 1: {
+ /* The user specified that we should probe for msp3400 */
+ static const unsigned short addrs[] = {
+ I2C_ADDR_MSP3400 >> 1,
+ I2C_ADDR_MSP3400_ALT >> 1,
+ I2C_CLIENT_END
+ };
+
+ btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "msp3400", 0, addrs);
+ if (btv->sd_msp34xx)
+ return;
+ goto no_audio;
+ }
+
+ case 2: {
+ /* The user specified that we should probe for tda7432 */
+ static const unsigned short addrs[] = {
+ I2C_ADDR_TDA7432 >> 1,
+ I2C_CLIENT_END
+ };
+
+ if (v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "tda7432", 0, addrs))
+ return;
+ goto no_audio;
+ }
+
+ case 3: {
+ /* The user specified that we should probe for tvaudio */
+ btv->sd_tvaudio = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "tvaudio", 0, tvaudio_addrs());
+ if (btv->sd_tvaudio)
+ return;
+ goto no_audio;
+ }
+
+ default:
+ pr_warn("%d: unknown audiodev value!\n", btv->c.nr);
+ return;
+ }
+
+ /* There were no overrides, so now we try to discover this through the
+ card definition */
+
+ /* probe for msp3400 first: this driver can detect whether or not
+ it really is a msp3400, so it will return NULL when the device
+ found is really something else (e.g. a tea6300). */
+ if (!bttv_tvcards[btv->c.type].no_msp34xx) {
+ btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "msp3400",
+ 0, I2C_ADDRS(I2C_ADDR_MSP3400 >> 1));
+ } else if (bttv_tvcards[btv->c.type].msp34xx_alt) {
+ btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "msp3400",
+ 0, I2C_ADDRS(I2C_ADDR_MSP3400_ALT >> 1));
+ }
+
+ /* If we found a msp34xx, then we're done. */
+ if (btv->sd_msp34xx)
+ return;
+
+ /* it might also be a tda7432. */
+ if (!bttv_tvcards[btv->c.type].no_tda7432) {
+ static const unsigned short addrs[] = {
+ I2C_ADDR_TDA7432 >> 1,
+ I2C_CLIENT_END
+ };
+
+ if (v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "tda7432", 0, addrs))
+ return;
+ }
+
+ /* Now see if we can find one of the tvaudio devices. */
+ btv->sd_tvaudio = v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "tvaudio", 0, tvaudio_addrs());
+ if (btv->sd_tvaudio)
+ return;
+
+no_audio:
+ pr_warn("%d: audio absent, no audio device found!\n", btv->c.nr);
+}
+
+
+/* initialize the tuner */
+void __devinit bttv_init_tuner(struct bttv *btv)
+{
+ int addr = ADDR_UNSET;
+
+ if (ADDR_UNSET != bttv_tvcards[btv->c.type].tuner_addr)
+ addr = bttv_tvcards[btv->c.type].tuner_addr;
+
+ if (btv->tuner_type != TUNER_ABSENT) {
+ struct tuner_setup tun_setup;
+
+ /* Load tuner module before issuing tuner config call! */
+ if (btv->has_radio)
+ v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "tuner",
+ 0, v4l2_i2c_tuner_addrs(ADDRS_RADIO));
+ v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "tuner",
+ 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
+ v4l2_i2c_new_subdev(&btv->c.v4l2_dev,
+ &btv->c.i2c_adap, "tuner",
+ 0, v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD));
+
+ tun_setup.mode_mask = T_ANALOG_TV;
+ tun_setup.type = btv->tuner_type;
+ tun_setup.addr = addr;
+
+ if (btv->has_radio)
+ tun_setup.mode_mask |= T_RADIO;
+
+ bttv_call_all(btv, tuner, s_type_addr, &tun_setup);
+ }
+
+ if (btv->tda9887_conf) {
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &btv->tda9887_conf;
+
+ bttv_call_all(btv, tuner, s_config, &tda9887_cfg);
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void modtec_eeprom(struct bttv *btv)
+{
+ if( strncmp(&(eeprom_data[0x1e]),"Temic 4066 FY5",14) ==0) {
+ btv->tuner_type=TUNER_TEMIC_4066FY5_PAL_I;
+ pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n",
+ btv->c.nr, &eeprom_data[0x1e]);
+ } else if (strncmp(&(eeprom_data[0x1e]),"Alps TSBB5",10) ==0) {
+ btv->tuner_type=TUNER_ALPS_TSBB5_PAL_I;
+ pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n",
+ btv->c.nr, &eeprom_data[0x1e]);
+ } else if (strncmp(&(eeprom_data[0x1e]),"Philips FM1246",14) ==0) {
+ btv->tuner_type=TUNER_PHILIPS_NTSC;
+ pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n",
+ btv->c.nr, &eeprom_data[0x1e]);
+ } else {
+ pr_info("%d: Modtec: Unknown TunerString: %s\n",
+ btv->c.nr, &eeprom_data[0x1e]);
+ }
+}
+
+static void __devinit hauppauge_eeprom(struct bttv *btv)
+{
+ struct tveeprom tv;
+
+ tveeprom_hauppauge_analog(&btv->i2c_client, &tv, eeprom_data);
+ btv->tuner_type = tv.tuner_type;
+ btv->has_radio = tv.has_radio;
+
+ pr_info("%d: Hauppauge eeprom indicates model#%d\n",
+ btv->c.nr, tv.model);
+
+ /*
+ * Some of the 878 boards have duplicate PCI IDs. Switch the board
+ * type based on model #.
+ */
+ if(tv.model == 64900) {
+ pr_info("%d: Switching board type from %s to %s\n",
+ btv->c.nr,
+ bttv_tvcards[btv->c.type].name,
+ bttv_tvcards[BTTV_BOARD_HAUPPAUGE_IMPACTVCB].name);
+ btv->c.type = BTTV_BOARD_HAUPPAUGE_IMPACTVCB;
+ }
+
+ /* The 61334 needs the msp3410 to do the radio demod to get sound */
+ if (tv.model == 61334)
+ btv->radio_uses_msp_demodulator = 1;
+}
+
+static int terratec_active_radio_upgrade(struct bttv *btv)
+{
+ int freq;
+
+ btv->has_radio = 1;
+ btv->has_matchbox = 1;
+ btv->mbox_we = 0x10;
+ btv->mbox_most = 0x20;
+ btv->mbox_clk = 0x08;
+ btv->mbox_data = 0x04;
+ btv->mbox_mask = 0x3c;
+
+ btv->mbox_iow = 1 << 8;
+ btv->mbox_ior = 1 << 9;
+ btv->mbox_csel = 1 << 10;
+
+ freq=88000/62.5;
+ tea5757_write(btv, 5 * freq + 0x358); /* write 0x1ed8 */
+ if (0x1ed8 == tea5757_read(btv)) {
+ pr_info("%d: Terratec Active Radio Upgrade found\n", btv->c.nr);
+ btv->has_radio = 1;
+ btv->has_saa6588 = 1;
+ btv->has_matchbox = 1;
+ } else {
+ btv->has_radio = 0;
+ btv->has_matchbox = 0;
+ }
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * minimal bootstrap for the WinTV/PVR -- upload altera firmware.
+ *
+ * The hcwamc.rbf firmware file is on the Hauppauge driver CD. Have
+ * a look at Pvr/pvr45xxx.EXE (self-extracting zip archive, can be
+ * unpacked with unzip).
+ */
+#define PVR_GPIO_DELAY 10
+
+#define BTTV_ALT_DATA 0x000001
+#define BTTV_ALT_DCLK 0x100000
+#define BTTV_ALT_NCONFIG 0x800000
+
+static int __devinit pvr_altera_load(struct bttv *btv, const u8 *micro,
+ u32 microlen)
+{
+ u32 n;
+ u8 bits;
+ int i;
+
+ gpio_inout(0xffffff,BTTV_ALT_DATA|BTTV_ALT_DCLK|BTTV_ALT_NCONFIG);
+ gpio_write(0);
+ udelay(PVR_GPIO_DELAY);
+
+ gpio_write(BTTV_ALT_NCONFIG);
+ udelay(PVR_GPIO_DELAY);
+
+ for (n = 0; n < microlen; n++) {
+ bits = micro[n];
+ for (i = 0 ; i < 8 ; i++) {
+ gpio_bits(BTTV_ALT_DCLK,0);
+ if (bits & 0x01)
+ gpio_bits(BTTV_ALT_DATA,BTTV_ALT_DATA);
+ else
+ gpio_bits(BTTV_ALT_DATA,0);
+ gpio_bits(BTTV_ALT_DCLK,BTTV_ALT_DCLK);
+ bits >>= 1;
+ }
+ }
+ gpio_bits(BTTV_ALT_DCLK,0);
+ udelay(PVR_GPIO_DELAY);
+
+ /* begin Altera init loop (Not necessary,but doesn't hurt) */
+ for (i = 0 ; i < 30 ; i++) {
+ gpio_bits(BTTV_ALT_DCLK,0);
+ gpio_bits(BTTV_ALT_DCLK,BTTV_ALT_DCLK);
+ }
+ gpio_bits(BTTV_ALT_DCLK,0);
+ return 0;
+}
+
+static int __devinit pvr_boot(struct bttv *btv)
+{
+ const struct firmware *fw_entry;
+ int rc;
+
+ rc = request_firmware(&fw_entry, "hcwamc.rbf", &btv->c.pci->dev);
+ if (rc != 0) {
+ pr_warn("%d: no altera firmware [via hotplug]\n", btv->c.nr);
+ return rc;
+ }
+ rc = pvr_altera_load(btv, fw_entry->data, fw_entry->size);
+ pr_info("%d: altera firmware upload %s\n",
+ btv->c.nr, (rc < 0) ? "failed" : "ok");
+ release_firmware(fw_entry);
+ return rc;
+}
+
+/* ----------------------------------------------------------------------- */
+/* some osprey specific stuff */
+
+static void __devinit osprey_eeprom(struct bttv *btv, const u8 ee[256])
+{
+ int i;
+ u32 serial = 0;
+ int cardid = -1;
+
+ /* This code will nevery actually get called in this case.... */
+ if (btv->c.type == BTTV_BOARD_UNKNOWN) {
+ /* this might be an antique... check for MMAC label in eeprom */
+ if (!strncmp(ee, "MMAC", 4)) {
+ u8 checksum = 0;
+ for (i = 0; i < 21; i++)
+ checksum += ee[i];
+ if (checksum != ee[21])
+ return;
+ cardid = BTTV_BOARD_OSPREY1x0_848;
+ for (i = 12; i < 21; i++)
+ serial *= 10, serial += ee[i] - '0';
+ }
+ } else {
+ unsigned short type;
+
+ for (i = 4*16; i < 8*16; i += 16) {
+ u16 checksum = ip_compute_csum(ee + i, 16);
+
+ if ((checksum&0xff) + (checksum>>8) == 0xff)
+ break;
+ }
+ if (i >= 8*16)
+ return;
+ ee += i;
+
+ /* found a valid descriptor */
+ type = get_unaligned_be16((__be16 *)(ee+4));
+
+ switch(type) {
+ /* 848 based */
+ case 0x0004:
+ cardid = BTTV_BOARD_OSPREY1x0_848;
+ break;
+ case 0x0005:
+ cardid = BTTV_BOARD_OSPREY101_848;
+ break;
+
+ /* 878 based */
+ case 0x0012:
+ case 0x0013:
+ cardid = BTTV_BOARD_OSPREY1x0;
+ break;
+ case 0x0014:
+ case 0x0015:
+ cardid = BTTV_BOARD_OSPREY1x1;
+ break;
+ case 0x0016:
+ case 0x0017:
+ case 0x0020:
+ cardid = BTTV_BOARD_OSPREY1x1_SVID;
+ break;
+ case 0x0018:
+ case 0x0019:
+ case 0x001E:
+ case 0x001F:
+ cardid = BTTV_BOARD_OSPREY2xx;
+ break;
+ case 0x001A:
+ case 0x001B:
+ cardid = BTTV_BOARD_OSPREY2x0_SVID;
+ break;
+ case 0x0040:
+ cardid = BTTV_BOARD_OSPREY500;
+ break;
+ case 0x0050:
+ case 0x0056:
+ cardid = BTTV_BOARD_OSPREY540;
+ /* bttv_osprey_540_init(btv); */
+ break;
+ case 0x0060:
+ case 0x0070:
+ case 0x00A0:
+ cardid = BTTV_BOARD_OSPREY2x0;
+ /* enable output on select control lines */
+ gpio_inout(0xffffff,0x000303);
+ break;
+ case 0x00D8:
+ cardid = BTTV_BOARD_OSPREY440;
+ break;
+ default:
+ /* unknown...leave generic, but get serial # */
+ pr_info("%d: osprey eeprom: unknown card type 0x%04x\n",
+ btv->c.nr, type);
+ break;
+ }
+ serial = get_unaligned_be32((__be32 *)(ee+6));
+ }
+
+ pr_info("%d: osprey eeprom: card=%d '%s' serial=%u\n",
+ btv->c.nr, cardid,
+ cardid > 0 ? bttv_tvcards[cardid].name : "Unknown", serial);
+
+ if (cardid<0 || btv->c.type == cardid)
+ return;
+
+ /* card type isn't set correctly */
+ if (card[btv->c.nr] < bttv_num_tvcards) {
+ pr_warn("%d: osprey eeprom: Not overriding user specified card type\n",
+ btv->c.nr);
+ } else {
+ pr_info("%d: osprey eeprom: Changing card type from %d to %d\n",
+ btv->c.nr, btv->c.type, cardid);
+ btv->c.type = cardid;
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+/* AVermedia specific stuff, from bktr_card.c */
+
+static int tuner_0_table[] = {
+ TUNER_PHILIPS_NTSC, TUNER_PHILIPS_PAL /* PAL-BG*/,
+ TUNER_PHILIPS_PAL, TUNER_PHILIPS_PAL /* PAL-I*/,
+ TUNER_PHILIPS_PAL, TUNER_PHILIPS_PAL,
+ TUNER_PHILIPS_SECAM, TUNER_PHILIPS_SECAM,
+ TUNER_PHILIPS_SECAM, TUNER_PHILIPS_PAL,
+ TUNER_PHILIPS_FM1216ME_MK3 };
+
+static int tuner_1_table[] = {
+ TUNER_TEMIC_NTSC, TUNER_TEMIC_PAL,
+ TUNER_TEMIC_PAL, TUNER_TEMIC_PAL,
+ TUNER_TEMIC_PAL, TUNER_TEMIC_PAL,
+ TUNER_TEMIC_4012FY5, TUNER_TEMIC_4012FY5, /* TUNER_TEMIC_SECAM */
+ TUNER_TEMIC_4012FY5, TUNER_TEMIC_PAL};
+
+static void __devinit avermedia_eeprom(struct bttv *btv)
+{
+ int tuner_make, tuner_tv_fm, tuner_format, tuner_type = 0;
+
+ tuner_make = (eeprom_data[0x41] & 0x7);
+ tuner_tv_fm = (eeprom_data[0x41] & 0x18) >> 3;
+ tuner_format = (eeprom_data[0x42] & 0xf0) >> 4;
+ btv->has_remote = (eeprom_data[0x42] & 0x01);
+
+ if (tuner_make == 0 || tuner_make == 2)
+ if (tuner_format <= 0x0a)
+ tuner_type = tuner_0_table[tuner_format];
+ if (tuner_make == 1)
+ if (tuner_format <= 9)
+ tuner_type = tuner_1_table[tuner_format];
+
+ if (tuner_make == 4)
+ if (tuner_format == 0x09)
+ tuner_type = TUNER_LG_NTSC_NEW_TAPC; /* TAPC-G702P */
+
+ pr_info("%d: Avermedia eeprom[0x%02x%02x]: tuner=",
+ btv->c.nr, eeprom_data[0x41], eeprom_data[0x42]);
+ if (tuner_type) {
+ btv->tuner_type = tuner_type;
+ pr_cont("%d", tuner_type);
+ } else
+ pr_cont("Unknown type");
+ pr_cont(" radio:%s remote control:%s\n",
+ tuner_tv_fm ? "yes" : "no",
+ btv->has_remote ? "yes" : "no");
+}
+
+/*
+ * For Voodoo TV/FM and Voodoo 200. These cards' tuners use a TDA9880
+ * analog demod, which is not I2C controlled like the newer and more common
+ * TDA9887 series. Instead is has two tri-state input pins, S0 and S1,
+ * that control the IF for the video and audio. Apparently, bttv GPIO
+ * 0x10000 is connected to S0. S0 low selects a 38.9 MHz VIF for B/G/D/K/I
+ * (i.e., PAL) while high selects 45.75 MHz for M/N (i.e., NTSC).
+ */
+u32 bttv_tda9880_setnorm(struct bttv *btv, u32 gpiobits)
+{
+
+ if (btv->audio == TVAUDIO_INPUT_TUNER) {
+ if (bttv_tvnorms[btv->tvnorm].v4l2_id & V4L2_STD_MN)
+ gpiobits |= 0x10000;
+ else
+ gpiobits &= ~0x10000;
+ }
+
+ gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpiobits);
+ return gpiobits;
+}
+
+
+/*
+ * reset/enable the MSP on some Hauppauge cards
+ * Thanks to Kyösti Mälkki (kmalkki@cc.hut.fi)!
+ *
+ * Hauppauge: pin 5
+ * Voodoo: pin 20
+ */
+static void __devinit boot_msp34xx(struct bttv *btv, int pin)
+{
+ int mask = (1 << pin);
+
+ gpio_inout(mask,mask);
+ gpio_bits(mask,0);
+ mdelay(2);
+ udelay(500);
+ gpio_bits(mask,mask);
+
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"msp34xx");
+ if (bttv_verbose)
+ pr_info("%d: Hauppauge/Voodoo msp34xx: reset line init [%d]\n",
+ btv->c.nr, pin);
+}
+
+/* ----------------------------------------------------------------------- */
+/* Imagenation L-Model PXC200 Framegrabber */
+/* This is basically the same procedure as
+ * used by Alessandro Rubini in his pxc200
+ * driver, but using BTTV functions */
+
+static void __devinit init_PXC200(struct bttv *btv)
+{
+ static int vals[] __devinitdata = { 0x08, 0x09, 0x0a, 0x0b, 0x0d, 0x0d,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x00 };
+ unsigned int i;
+ int tmp;
+ u32 val;
+
+ /* Initialise GPIO-connevted stuff */
+ gpio_inout(0xffffff, (1<<13));
+ gpio_write(0);
+ udelay(3);
+ gpio_write(1<<13);
+ /* GPIO inputs are pulled up, so no need to drive
+ * reset pin any longer */
+ gpio_bits(0xffffff, 0);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"pxc200");
+
+ /* we could/should try and reset/control the AD pots? but
+ right now we simply turned off the crushing. Without
+ this the AGC drifts drifts
+ remember the EN is reverse logic -->
+ setting BT848_ADC_AGC_EN disable the AGC
+ tboult@eecs.lehigh.edu
+ */
+
+ btwrite(BT848_ADC_RESERVED|BT848_ADC_AGC_EN, BT848_ADC);
+
+ /* Initialise MAX517 DAC */
+ pr_info("Setting DAC reference voltage level ...\n");
+ bttv_I2CWrite(btv,0x5E,0,0x80,1);
+
+ /* Initialise 12C508 PIC */
+ /* The I2CWrite and I2CRead commmands are actually to the
+ * same chips - but the R/W bit is included in the address
+ * argument so the numbers are different */
+
+
+ pr_info("Initialising 12C508 PIC chip ...\n");
+
+ /* First of all, enable the clock line. This is used in the PXC200-F */
+ val = btread(BT848_GPIO_DMA_CTL);
+ val |= BT848_GPIO_DMA_CTL_GPCLKMODE;
+ btwrite(val, BT848_GPIO_DMA_CTL);
+
+ /* Then, push to 0 the reset pin long enough to reset the *
+ * device same as above for the reset line, but not the same
+ * value sent to the GPIO-connected stuff
+ * which one is the good one? */
+ gpio_inout(0xffffff,(1<<2));
+ gpio_write(0);
+ udelay(10);
+ gpio_write(1<<2);
+
+ for (i = 0; i < ARRAY_SIZE(vals); i++) {
+ tmp=bttv_I2CWrite(btv,0x1E,0,vals[i],1);
+ if (tmp != -1) {
+ pr_info("I2C Write(%2.2x) = %i\nI2C Read () = %2.2x\n\n",
+ vals[i],tmp,bttv_I2CRead(btv,0x1F,NULL));
+ }
+ }
+
+ pr_info("PXC200 Initialised\n");
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+/*
+ * The Adlink RTV-24 (aka Angelo) has some special initialisation to unlock
+ * it. This apparently involves the following procedure for each 878 chip:
+ *
+ * 1) write 0x00C3FEFF to the GPIO_OUT_EN register
+ *
+ * 2) write to GPIO_DATA
+ * - 0x0E
+ * - sleep 1ms
+ * - 0x10 + 0x0E
+ * - sleep 10ms
+ * - 0x0E
+ * read from GPIO_DATA into buf (uint_32)
+ * - if ( data>>18 & 0x01 != 0) || ( buf>>19 & 0x01 != 1 )
+ * error. ERROR_CPLD_Check_Failed stop.
+ *
+ * 3) write to GPIO_DATA
+ * - write 0x4400 + 0x0E
+ * - sleep 10ms
+ * - write 0x4410 + 0x0E
+ * - sleep 1ms
+ * - write 0x0E
+ * read from GPIO_DATA into buf (uint_32)
+ * - if ( buf>>18 & 0x01 ) || ( buf>>19 & 0x01 != 0 )
+ * error. ERROR_CPLD_Check_Failed.
+ */
+/* ----------------------------------------------------------------------- */
+static void
+init_RTV24 (struct bttv *btv)
+{
+ uint32_t dataRead = 0;
+ long watchdog_value = 0x0E;
+
+ pr_info("%d: Adlink RTV-24 initialisation in progress ...\n",
+ btv->c.nr);
+
+ btwrite (0x00c3feff, BT848_GPIO_OUT_EN);
+
+ btwrite (0 + watchdog_value, BT848_GPIO_DATA);
+ msleep (1);
+ btwrite (0x10 + watchdog_value, BT848_GPIO_DATA);
+ msleep (10);
+ btwrite (0 + watchdog_value, BT848_GPIO_DATA);
+
+ dataRead = btread (BT848_GPIO_DATA);
+
+ if ((((dataRead >> 18) & 0x01) != 0) || (((dataRead >> 19) & 0x01) != 1)) {
+ pr_info("%d: Adlink RTV-24 initialisation(1) ERROR_CPLD_Check_Failed (read %d)\n",
+ btv->c.nr, dataRead);
+ }
+
+ btwrite (0x4400 + watchdog_value, BT848_GPIO_DATA);
+ msleep (10);
+ btwrite (0x4410 + watchdog_value, BT848_GPIO_DATA);
+ msleep (1);
+ btwrite (watchdog_value, BT848_GPIO_DATA);
+ msleep (1);
+ dataRead = btread (BT848_GPIO_DATA);
+
+ if ((((dataRead >> 18) & 0x01) != 0) || (((dataRead >> 19) & 0x01) != 0)) {
+ pr_info("%d: Adlink RTV-24 initialisation(2) ERROR_CPLD_Check_Failed (read %d)\n",
+ btv->c.nr, dataRead);
+
+ return;
+ }
+
+ pr_info("%d: Adlink RTV-24 initialisation complete\n", btv->c.nr);
+}
+
+
+
+/* ----------------------------------------------------------------------- */
+/* Miro Pro radio stuff -- the tea5757 is connected to some GPIO ports */
+/*
+ * Copyright (c) 1999 Csaba Halasz <qgehali@uni-miskolc.hu>
+ * This code is placed under the terms of the GNU General Public License
+ *
+ * Brutally hacked by Dan Sheridan <dan.sheridan@contact.org.uk> djs52 8/3/00
+ */
+
+static void bus_low(struct bttv *btv, int bit)
+{
+ if (btv->mbox_ior) {
+ gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+ btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
+ udelay(5);
+ }
+
+ gpio_bits(bit,0);
+ udelay(5);
+
+ if (btv->mbox_ior) {
+ gpio_bits(btv->mbox_iow | btv->mbox_csel, 0);
+ udelay(5);
+ }
+}
+
+static void bus_high(struct bttv *btv, int bit)
+{
+ if (btv->mbox_ior) {
+ gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+ btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
+ udelay(5);
+ }
+
+ gpio_bits(bit,bit);
+ udelay(5);
+
+ if (btv->mbox_ior) {
+ gpio_bits(btv->mbox_iow | btv->mbox_csel, 0);
+ udelay(5);
+ }
+}
+
+static int bus_in(struct bttv *btv, int bit)
+{
+ if (btv->mbox_ior) {
+ gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+ btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
+ udelay(5);
+
+ gpio_bits(btv->mbox_iow | btv->mbox_csel, 0);
+ udelay(5);
+ }
+ return gpio_read() & (bit);
+}
+
+/* TEA5757 register bits */
+#define TEA_FREQ 0:14
+#define TEA_BUFFER 15:15
+
+#define TEA_SIGNAL_STRENGTH 16:17
+
+#define TEA_PORT1 18:18
+#define TEA_PORT0 19:19
+
+#define TEA_BAND 20:21
+#define TEA_BAND_FM 0
+#define TEA_BAND_MW 1
+#define TEA_BAND_LW 2
+#define TEA_BAND_SW 3
+
+#define TEA_MONO 22:22
+#define TEA_ALLOW_STEREO 0
+#define TEA_FORCE_MONO 1
+
+#define TEA_SEARCH_DIRECTION 23:23
+#define TEA_SEARCH_DOWN 0
+#define TEA_SEARCH_UP 1
+
+#define TEA_STATUS 24:24
+#define TEA_STATUS_TUNED 0
+#define TEA_STATUS_SEARCHING 1
+
+/* Low-level stuff */
+static int tea5757_read(struct bttv *btv)
+{
+ unsigned long timeout;
+ int value = 0;
+ int i;
+
+ /* better safe than sorry */
+ gpio_inout(btv->mbox_mask, btv->mbox_clk | btv->mbox_we);
+
+ if (btv->mbox_ior) {
+ gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+ btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
+ udelay(5);
+ }
+
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"tea5757 read");
+
+ bus_low(btv,btv->mbox_we);
+ bus_low(btv,btv->mbox_clk);
+
+ udelay(10);
+ timeout= jiffies + msecs_to_jiffies(1000);
+
+ /* wait for DATA line to go low; error if it doesn't */
+ while (bus_in(btv,btv->mbox_data) && time_before(jiffies, timeout))
+ schedule();
+ if (bus_in(btv,btv->mbox_data)) {
+ pr_warn("%d: tea5757: read timeout\n", btv->c.nr);
+ return -1;
+ }
+
+ dprintk("%d: tea5757:", btv->c.nr);
+ for (i = 0; i < 24; i++) {
+ udelay(5);
+ bus_high(btv,btv->mbox_clk);
+ udelay(5);
+ dprintk_cont("%c",
+ bus_in(btv, btv->mbox_most) == 0 ? 'T' : '-');
+ bus_low(btv,btv->mbox_clk);
+ value <<= 1;
+ value |= (bus_in(btv,btv->mbox_data) == 0)?0:1; /* MSB first */
+ dprintk_cont("%c",
+ bus_in(btv, btv->mbox_most) == 0 ? 'S' : 'M');
+ }
+ dprintk_cont("\n");
+ dprintk("%d: tea5757: read 0x%X\n", btv->c.nr, value);
+ return value;
+}
+
+static int tea5757_write(struct bttv *btv, int value)
+{
+ int i;
+ int reg = value;
+
+ gpio_inout(btv->mbox_mask, btv->mbox_clk | btv->mbox_we | btv->mbox_data);
+
+ if (btv->mbox_ior) {
+ gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+ btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
+ udelay(5);
+ }
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"tea5757 write");
+
+ dprintk("%d: tea5757: write 0x%X\n", btv->c.nr, value);
+ bus_low(btv,btv->mbox_clk);
+ bus_high(btv,btv->mbox_we);
+ for (i = 0; i < 25; i++) {
+ if (reg & 0x1000000)
+ bus_high(btv,btv->mbox_data);
+ else
+ bus_low(btv,btv->mbox_data);
+ reg <<= 1;
+ bus_high(btv,btv->mbox_clk);
+ udelay(10);
+ bus_low(btv,btv->mbox_clk);
+ udelay(10);
+ }
+ bus_low(btv,btv->mbox_we); /* unmute !!! */
+ return 0;
+}
+
+void tea5757_set_freq(struct bttv *btv, unsigned short freq)
+{
+ dprintk("tea5757_set_freq %d\n",freq);
+ tea5757_write(btv, 5 * freq + 0x358); /* add 10.7MHz (see docs) */
+}
+
+/* RemoteVision MX (rv605) muxsel helper [Miguel Freitas]
+ *
+ * This is needed because rv605 don't use a normal multiplex, but a crosspoint
+ * switch instead (CD22M3494E). This IC can have multiple active connections
+ * between Xn (input) and Yn (output) pins. We need to clear any existing
+ * connection prior to establish a new one, pulsing the STROBE pin.
+ *
+ * The board hardwire Y0 (xpoint) to MUX1 and MUXOUT to Yin.
+ * GPIO pins are wired as:
+ * GPIO[0:3] - AX[0:3] (xpoint) - P1[0:3] (microcontroller)
+ * GPIO[4:6] - AY[0:2] (xpoint) - P1[4:6] (microcontroller)
+ * GPIO[7] - DATA (xpoint) - P1[7] (microcontroller)
+ * GPIO[8] - - P3[5] (microcontroller)
+ * GPIO[9] - RESET (xpoint) - P3[6] (microcontroller)
+ * GPIO[10] - STROBE (xpoint) - P3[7] (microcontroller)
+ * GPINTR - - P3[4] (microcontroller)
+ *
+ * The microcontroller is a 80C32 like. It should be possible to change xpoint
+ * configuration either directly (as we are doing) or using the microcontroller
+ * which is also wired to I2C interface. I have no further info on the
+ * microcontroller features, one would need to disassembly the firmware.
+ * note: the vendor refused to give any information on this product, all
+ * that stuff was found using a multimeter! :)
+ */
+static void rv605_muxsel(struct bttv *btv, unsigned int input)
+{
+ static const u8 muxgpio[] = { 0x3, 0x1, 0x2, 0x4, 0xf, 0x7, 0xe, 0x0,
+ 0xd, 0xb, 0xc, 0x6, 0x9, 0x5, 0x8, 0xa };
+
+ gpio_bits(0x07f, muxgpio[input]);
+
+ /* reset all conections */
+ gpio_bits(0x200,0x200);
+ mdelay(1);
+ gpio_bits(0x200,0x000);
+ mdelay(1);
+
+ /* create a new connection */
+ gpio_bits(0x480,0x480);
+ mdelay(1);
+ gpio_bits(0x480,0x080);
+ mdelay(1);
+}
+
+/* Tibet Systems 'Progress DVR' CS16 muxsel helper [Chris Fanning]
+ *
+ * The CS16 (available on eBay cheap) is a PCI board with four Fusion
+ * 878A chips, a PCI bridge, an Atmel microcontroller, four sync separator
+ * chips, ten eight input analog multiplexors, a not chip and a few
+ * other components.
+ *
+ * 16 inputs on a secondary bracket are provided and can be selected
+ * from each of the four capture chips. Two of the eight input
+ * multiplexors are used to select from any of the 16 input signals.
+ *
+ * Unsupported hardware capabilities:
+ * . A video output monitor on the secondary bracket can be selected from
+ * one of the 878A chips.
+ * . Another passthrough but I haven't spent any time investigating it.
+ * . Digital I/O (logic level connected to GPIO) is available from an
+ * onboard header.
+ *
+ * The on chip input mux should always be set to 2.
+ * GPIO[16:19] - Video input selection
+ * GPIO[0:3] - Video output monitor select (only available from one 878A)
+ * GPIO[?:?] - Digital I/O.
+ *
+ * There is an ATMEL microcontroller with an 8031 core on board. I have not
+ * determined what function (if any) it provides. With the microcontroller
+ * and sync separator chips a guess is that it might have to do with video
+ * switching and maybe some digital I/O.
+ */
+static void tibetCS16_muxsel(struct bttv *btv, unsigned int input)
+{
+ /* video mux */
+ gpio_bits(0x0f0000, input << 16);
+}
+
+static void tibetCS16_init(struct bttv *btv)
+{
+ /* enable gpio bits, mask obtained via btSpy */
+ gpio_inout(0xffffff, 0x0f7fff);
+ gpio_write(0x0f7fff);
+}
+
+/*
+ * The following routines for the Kodicom-4400r get a little mind-twisting.
+ * There is a "master" controller and three "slave" controllers, together
+ * an analog switch which connects any of 16 cameras to any of the BT87A's.
+ * The analog switch is controlled by the "master", but the detection order
+ * of the four BT878A chips is in an order which I just don't understand.
+ * The "master" is actually the second controller to be detected. The
+ * logic on the board uses logical numbers for the 4 controllers, but
+ * those numbers are different from the detection sequence. When working
+ * with the analog switch, we need to "map" from the detection sequence
+ * over to the board's logical controller number. This mapping sequence
+ * is {3, 0, 2, 1}, i.e. the first controller to be detected is logical
+ * unit 3, the second (which is the master) is logical unit 0, etc.
+ * We need to maintain the status of the analog switch (which of the 16
+ * cameras is connected to which of the 4 controllers). Rather than
+ * add to the bttv structure for this, we use the data reserved for
+ * the mbox (unused for this card type).
+ */
+
+/*
+ * First a routine to set the analog switch, which controls which camera
+ * is routed to which controller. The switch comprises an X-address
+ * (gpio bits 0-3, representing the camera, ranging from 0-15), and a
+ * Y-address (gpio bits 4-6, representing the controller, ranging from 0-3).
+ * A data value (gpio bit 7) of '1' enables the switch, and '0' disables
+ * the switch. A STROBE bit (gpio bit 8) latches the data value into the
+ * specified address. The idea is to set the address and data, then bring
+ * STROBE high, and finally bring STROBE back to low.
+ */
+static void kodicom4400r_write(struct bttv *btv,
+ unsigned char xaddr,
+ unsigned char yaddr,
+ unsigned char data) {
+ unsigned int udata;
+
+ udata = (data << 7) | ((yaddr&3) << 4) | (xaddr&0xf);
+ gpio_bits(0x1ff, udata); /* write ADDR and DAT */
+ gpio_bits(0x1ff, udata | (1 << 8)); /* strobe high */
+ gpio_bits(0x1ff, udata); /* strobe low */
+}
+
+/*
+ * Next the mux select. Both the "master" and "slave" 'cards' (controllers)
+ * use this routine. The routine finds the "master" for the card, maps
+ * the controller number from the detected position over to the logical
+ * number, writes the appropriate data to the analog switch, and housekeeps
+ * the local copy of the switch information. The parameter 'input' is the
+ * requested camera number (0 - 15).
+ */
+static void kodicom4400r_muxsel(struct bttv *btv, unsigned int input)
+{
+ char *sw_status;
+ int xaddr, yaddr;
+ struct bttv *mctlr;
+ static unsigned char map[4] = {3, 0, 2, 1};
+
+ mctlr = master[btv->c.nr];
+ if (mctlr == NULL) { /* ignore if master not yet detected */
+ return;
+ }
+ yaddr = (btv->c.nr - mctlr->c.nr + 1) & 3; /* the '&' is for safety */
+ yaddr = map[yaddr];
+ sw_status = (char *)(&mctlr->mbox_we);
+ xaddr = input & 0xf;
+ /* Check if the controller/camera pair has changed, else ignore */
+ if (sw_status[yaddr] != xaddr)
+ {
+ /* "open" the old switch, "close" the new one, save the new */
+ kodicom4400r_write(mctlr, sw_status[yaddr], yaddr, 0);
+ sw_status[yaddr] = xaddr;
+ kodicom4400r_write(mctlr, xaddr, yaddr, 1);
+ }
+}
+
+/*
+ * During initialisation, we need to reset the analog switch. We
+ * also preset the switch to map the 4 connectors on the card to the
+ * *user's* (see above description of kodicom4400r_muxsel) channels
+ * 0 through 3
+ */
+static void kodicom4400r_init(struct bttv *btv)
+{
+ char *sw_status = (char *)(&btv->mbox_we);
+ int ix;
+
+ gpio_inout(0x0003ff, 0x0003ff);
+ gpio_write(1 << 9); /* reset MUX */
+ gpio_write(0);
+ /* Preset camera 0 to the 4 controllers */
+ for (ix = 0; ix < 4; ix++) {
+ sw_status[ix] = ix;
+ kodicom4400r_write(btv, ix, ix, 1);
+ }
+ /*
+ * Since this is the "master", we need to set up the
+ * other three controller chips' pointers to this structure
+ * for later use in the muxsel routine.
+ */
+ if ((btv->c.nr<1) || (btv->c.nr>BTTV_MAX-3))
+ return;
+ master[btv->c.nr-1] = btv;
+ master[btv->c.nr] = btv;
+ master[btv->c.nr+1] = btv;
+ master[btv->c.nr+2] = btv;
+}
+
+/* The Grandtec X-Guard framegrabber card uses two Dual 4-channel
+ * video multiplexers to provide up to 16 video inputs. These
+ * multiplexers are controlled by the lower 8 GPIO pins of the
+ * bt878. The multiplexers probably Pericom PI5V331Q or similar.
+
+ * xxx0 is pin xxx of multiplexer U5,
+ * yyy1 is pin yyy of multiplexer U2
+ */
+#define ENA0 0x01
+#define ENB0 0x02
+#define ENA1 0x04
+#define ENB1 0x08
+
+#define IN10 0x10
+#define IN00 0x20
+#define IN11 0x40
+#define IN01 0x80
+
+static void xguard_muxsel(struct bttv *btv, unsigned int input)
+{
+ static const int masks[] = {
+ ENB0, ENB0|IN00, ENB0|IN10, ENB0|IN00|IN10,
+ ENA0, ENA0|IN00, ENA0|IN10, ENA0|IN00|IN10,
+ ENB1, ENB1|IN01, ENB1|IN11, ENB1|IN01|IN11,
+ ENA1, ENA1|IN01, ENA1|IN11, ENA1|IN01|IN11,
+ };
+ gpio_write(masks[input%16]);
+}
+static void picolo_tetra_init(struct bttv *btv)
+{
+ /*This is the video input redirection fonctionality : I DID NOT USED IT. */
+ btwrite (0x08<<16,BT848_GPIO_DATA);/*GPIO[19] [==> 4053 B+C] set to 1 */
+ btwrite (0x04<<16,BT848_GPIO_DATA);/*GPIO[18] [==> 4053 A] set to 1*/
+}
+static void picolo_tetra_muxsel (struct bttv* btv, unsigned int input)
+{
+
+ dprintk("%d : picolo_tetra_muxsel => input = %d\n", btv->c.nr, input);
+ /*Just set the right path in the analog multiplexers : channel 1 -> 4 ==> Analog Mux ==> MUX0*/
+ /*GPIO[20]&GPIO[21] used to choose the right input*/
+ btwrite (input<<20,BT848_GPIO_DATA);
+
+}
+
+/*
+ * ivc120_muxsel [Added by Alan Garfield <alan@fromorbit.com>]
+ *
+ * The IVC120G security card has 4 i2c controlled TDA8540 matrix
+ * swichers to provide 16 channels to MUX0. The TDA8540's have
+ * 4 independent outputs and as such the IVC120G also has the
+ * optional "Monitor Out" bus. This allows the card to be looking
+ * at one input while the monitor is looking at another.
+ *
+ * Since I've couldn't be bothered figuring out how to add an
+ * independent muxsel for the monitor bus, I've just set it to
+ * whatever the card is looking at.
+ *
+ * OUT0 of the TDA8540's is connected to MUX0 (0x03)
+ * OUT1 of the TDA8540's is connected to "Monitor Out" (0x0C)
+ *
+ * TDA8540_ALT3 IN0-3 = Channel 13 - 16 (0x03)
+ * TDA8540_ALT4 IN0-3 = Channel 1 - 4 (0x03)
+ * TDA8540_ALT5 IN0-3 = Channel 5 - 8 (0x03)
+ * TDA8540_ALT6 IN0-3 = Channel 9 - 12 (0x03)
+ *
+ */
+
+/* All 7 possible sub-ids for the TDA8540 Matrix Switcher */
+#define I2C_TDA8540 0x90
+#define I2C_TDA8540_ALT1 0x92
+#define I2C_TDA8540_ALT2 0x94
+#define I2C_TDA8540_ALT3 0x96
+#define I2C_TDA8540_ALT4 0x98
+#define I2C_TDA8540_ALT5 0x9a
+#define I2C_TDA8540_ALT6 0x9c
+
+static void ivc120_muxsel(struct bttv *btv, unsigned int input)
+{
+ /* Simple maths */
+ int key = input % 4;
+ int matrix = input / 4;
+
+ dprintk("%d: ivc120_muxsel: Input - %02d | TDA - %02d | In - %02d\n",
+ btv->c.nr, input, matrix, key);
+
+ /* Handles the input selection on the TDA8540's */
+ bttv_I2CWrite(btv, I2C_TDA8540_ALT3, 0x00,
+ ((matrix == 3) ? (key | key << 2) : 0x00), 1);
+ bttv_I2CWrite(btv, I2C_TDA8540_ALT4, 0x00,
+ ((matrix == 0) ? (key | key << 2) : 0x00), 1);
+ bttv_I2CWrite(btv, I2C_TDA8540_ALT5, 0x00,
+ ((matrix == 1) ? (key | key << 2) : 0x00), 1);
+ bttv_I2CWrite(btv, I2C_TDA8540_ALT6, 0x00,
+ ((matrix == 2) ? (key | key << 2) : 0x00), 1);
+
+ /* Handles the output enables on the TDA8540's */
+ bttv_I2CWrite(btv, I2C_TDA8540_ALT3, 0x02,
+ ((matrix == 3) ? 0x03 : 0x00), 1); /* 13 - 16 */
+ bttv_I2CWrite(btv, I2C_TDA8540_ALT4, 0x02,
+ ((matrix == 0) ? 0x03 : 0x00), 1); /* 1-4 */
+ bttv_I2CWrite(btv, I2C_TDA8540_ALT5, 0x02,
+ ((matrix == 1) ? 0x03 : 0x00), 1); /* 5-8 */
+ bttv_I2CWrite(btv, I2C_TDA8540_ALT6, 0x02,
+ ((matrix == 2) ? 0x03 : 0x00), 1); /* 9-12 */
+
+ /* 878's MUX0 is already selected for input via muxsel values */
+}
+
+
+/* PXC200 muxsel helper
+ * luke@syseng.anu.edu.au
+ * another transplant
+ * from Alessandro Rubini (rubini@linux.it)
+ *
+ * There are 4 kinds of cards:
+ * PXC200L which is bt848
+ * PXC200F which is bt848 with PIC controlling mux
+ * PXC200AL which is bt878
+ * PXC200AF which is bt878 with PIC controlling mux
+ */
+#define PX_CFG_PXC200F 0x01
+#define PX_FLAG_PXC200A 0x00001000 /* a pxc200A is bt-878 based */
+#define PX_I2C_PIC 0x0f
+#define PX_PXC200A_CARDID 0x200a1295
+#define PX_I2C_CMD_CFG 0x00
+
+static void PXC200_muxsel(struct bttv *btv, unsigned int input)
+{
+ int rc;
+ long mux;
+ int bitmask;
+ unsigned char buf[2];
+
+ /* Read PIC config to determine if this is a PXC200F */
+ /* PX_I2C_CMD_CFG*/
+ buf[0]=0;
+ buf[1]=0;
+ rc=bttv_I2CWrite(btv,(PX_I2C_PIC<<1),buf[0],buf[1],1);
+ if (rc) {
+ pr_debug("%d: PXC200_muxsel: pic cfg write failed:%d\n",
+ btv->c.nr, rc);
+ /* not PXC ? do nothing */
+ return;
+ }
+
+ rc=bttv_I2CRead(btv,(PX_I2C_PIC<<1),NULL);
+ if (!(rc & PX_CFG_PXC200F)) {
+ pr_debug("%d: PXC200_muxsel: not PXC200F rc:%d\n",
+ btv->c.nr, rc);
+ return;
+ }
+
+
+ /* The multiplexer in the 200F is handled by the GPIO port */
+ /* get correct mapping between inputs */
+ /* mux = bttv_tvcards[btv->type].muxsel[input] & 3; */
+ /* ** not needed!? */
+ mux = input;
+
+ /* make sure output pins are enabled */
+ /* bitmask=0x30f; */
+ bitmask=0x302;
+ /* check whether we have a PXC200A */
+ if (btv->cardid == PX_PXC200A_CARDID) {
+ bitmask ^= 0x180; /* use 7 and 9, not 8 and 9 */
+ bitmask |= 7<<4; /* the DAC */
+ }
+ btwrite(bitmask, BT848_GPIO_OUT_EN);
+
+ bitmask = btread(BT848_GPIO_DATA);
+ if (btv->cardid == PX_PXC200A_CARDID)
+ bitmask = (bitmask & ~0x280) | ((mux & 2) << 8) | ((mux & 1) << 7);
+ else /* older device */
+ bitmask = (bitmask & ~0x300) | ((mux & 3) << 8);
+ btwrite(bitmask,BT848_GPIO_DATA);
+
+ /*
+ * Was "to be safe, set the bt848 to input 0"
+ * Actually, since it's ok at load time, better not messing
+ * with these bits (on PXC200AF you need to set mux 2 here)
+ *
+ * needed because bttv-driver sets mux before calling this function
+ */
+ if (btv->cardid == PX_PXC200A_CARDID)
+ btaor(2<<5, ~BT848_IFORM_MUXSEL, BT848_IFORM);
+ else /* older device */
+ btand(~BT848_IFORM_MUXSEL,BT848_IFORM);
+
+ pr_debug("%d: setting input channel to:%d\n", btv->c.nr, (int)mux);
+}
+
+static void phytec_muxsel(struct bttv *btv, unsigned int input)
+{
+ unsigned int mux = input % 4;
+
+ if (input == btv->svhs)
+ mux = 0;
+
+ gpio_bits(0x3, mux);
+}
+
+/*
+ * GeoVision GV-800(S) functions
+ * Bruno Christo <bchristo@inf.ufsm.br>
+*/
+
+/* This is a function to control the analog switch, which determines which
+ * camera is routed to which controller. The switch comprises an X-address
+ * (gpio bits 0-3, representing the camera, ranging from 0-15), and a
+ * Y-address (gpio bits 4-6, representing the controller, ranging from 0-3).
+ * A data value (gpio bit 18) of '1' enables the switch, and '0' disables
+ * the switch. A STROBE bit (gpio bit 17) latches the data value into the
+ * specified address. There is also a chip select (gpio bit 16).
+ * The idea is to set the address and chip select together, bring
+ * STROBE high, write the data, and finally bring STROBE back to low.
+ */
+static void gv800s_write(struct bttv *btv,
+ unsigned char xaddr,
+ unsigned char yaddr,
+ unsigned char data) {
+ /* On the "master" 878A:
+ * GPIO bits 0-9 are used for the analog switch:
+ * 00 - 03: camera selector
+ * 04 - 06: 878A (controller) selector
+ * 16: cselect
+ * 17: strobe
+ * 18: data (1->on, 0->off)
+ * 19: reset
+ */
+ const u32 ADDRESS = ((xaddr&0xf) | (yaddr&3)<<4);
+ const u32 CSELECT = 1<<16;
+ const u32 STROBE = 1<<17;
+ const u32 DATA = data<<18;
+
+ gpio_bits(0x1007f, ADDRESS | CSELECT); /* write ADDRESS and CSELECT */
+ gpio_bits(0x20000, STROBE); /* STROBE high */
+ gpio_bits(0x40000, DATA); /* write DATA */
+ gpio_bits(0x20000, ~STROBE); /* STROBE low */
+}
+
+/*
+ * GeoVision GV-800(S) muxsel
+ *
+ * Each of the 4 cards (controllers) use this function.
+ * The controller using this function selects the input through the GPIO pins
+ * of the "master" card. A pointer to this card is stored in master[btv->c.nr].
+ *
+ * The parameter 'input' is the requested camera number (0-4) on the controller.
+ * The map array has the address of each input. Note that the addresses in the
+ * array are in the sequence the original GeoVision driver uses, that is, set
+ * every controller to input 0, then to input 1, 2, 3, repeat. This means that
+ * the physical "camera 1" connector corresponds to controller 0 input 0,
+ * "camera 2" corresponds to controller 1 input 0, and so on.
+ *
+ * After getting the input address, the function then writes the appropriate
+ * data to the analog switch, and housekeeps the local copy of the switch
+ * information.
+ */
+static void gv800s_muxsel(struct bttv *btv, unsigned int input)
+{
+ struct bttv *mctlr;
+ char *sw_status;
+ int xaddr, yaddr;
+ static unsigned int map[4][4] = { { 0x0, 0x4, 0xa, 0x6 },
+ { 0x1, 0x5, 0xb, 0x7 },
+ { 0x2, 0x8, 0xc, 0xe },
+ { 0x3, 0x9, 0xd, 0xf } };
+ input = input%4;
+ mctlr = master[btv->c.nr];
+ if (mctlr == NULL) {
+ /* do nothing until the "master" is detected */
+ return;
+ }
+ yaddr = (btv->c.nr - mctlr->c.nr) & 3;
+ sw_status = (char *)(&mctlr->mbox_we);
+ xaddr = map[yaddr][input] & 0xf;
+
+ /* Check if the controller/camera pair has changed, ignore otherwise */
+ if (sw_status[yaddr] != xaddr) {
+ /* disable the old switch, enable the new one and save status */
+ gv800s_write(mctlr, sw_status[yaddr], yaddr, 0);
+ sw_status[yaddr] = xaddr;
+ gv800s_write(mctlr, xaddr, yaddr, 1);
+ }
+}
+
+/* GeoVision GV-800(S) "master" chip init */
+static void gv800s_init(struct bttv *btv)
+{
+ char *sw_status = (char *)(&btv->mbox_we);
+ int ix;
+
+ gpio_inout(0xf107f, 0xf107f);
+ gpio_write(1<<19); /* reset the analog MUX */
+ gpio_write(0);
+
+ /* Preset camera 0 to the 4 controllers */
+ for (ix = 0; ix < 4; ix++) {
+ sw_status[ix] = ix;
+ gv800s_write(btv, ix, ix, 1);
+ }
+
+ /* Inputs on the "master" controller need this brightness fix */
+ bttv_I2CWrite(btv, 0x18, 0x5, 0x90, 1);
+
+ if (btv->c.nr > BTTV_MAX-4)
+ return;
+ /*
+ * Store the "master" controller pointer in the master
+ * array for later use in the muxsel function.
+ */
+ master[btv->c.nr] = btv;
+ master[btv->c.nr+1] = btv;
+ master[btv->c.nr+2] = btv;
+ master[btv->c.nr+3] = btv;
+}
+
+/* ----------------------------------------------------------------------- */
+/* motherboard chipset specific stuff */
+
+void __init bttv_check_chipset(void)
+{
+ int pcipci_fail = 0;
+ struct pci_dev *dev = NULL;
+
+ if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) /* should check if target is AGP */
+ pcipci_fail = 1;
+ if (pci_pci_problems & (PCIPCI_TRITON|PCIPCI_NATOMA|PCIPCI_VIAETBF))
+ triton1 = 1;
+ if (pci_pci_problems & PCIPCI_VSFX)
+ vsfx = 1;
+#ifdef PCIPCI_ALIMAGIK
+ if (pci_pci_problems & PCIPCI_ALIMAGIK)
+ latency = 0x0A;
+#endif
+
+
+ /* print warnings about any quirks found */
+ if (triton1)
+ pr_info("Host bridge needs ETBF enabled\n");
+ if (vsfx)
+ pr_info("Host bridge needs VSFX enabled\n");
+ if (pcipci_fail) {
+ pr_info("bttv and your chipset may not work together\n");
+ if (!no_overlay) {
+ pr_info("overlay will be disabled\n");
+ no_overlay = 1;
+ } else {
+ pr_info("overlay forced. Use this option at your own risk.\n");
+ }
+ }
+ if (UNSET != latency)
+ pr_info("pci latency fixup [%d]\n", latency);
+ while ((dev = pci_get_device(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82441, dev))) {
+ unsigned char b;
+ pci_read_config_byte(dev, 0x53, &b);
+ if (bttv_debug)
+ pr_info("Host bridge: 82441FX Natoma, bufcon=0x%02x\n",
+ b);
+ }
+}
+
+int __devinit bttv_handle_chipset(struct bttv *btv)
+{
+ unsigned char command;
+
+ if (!triton1 && !vsfx && UNSET == latency)
+ return 0;
+
+ if (bttv_verbose) {
+ if (triton1)
+ pr_info("%d: enabling ETBF (430FX/VP3 compatibility)\n",
+ btv->c.nr);
+ if (vsfx && btv->id >= 878)
+ pr_info("%d: enabling VSFX\n", btv->c.nr);
+ if (UNSET != latency)
+ pr_info("%d: setting pci timer to %d\n",
+ btv->c.nr, latency);
+ }
+
+ if (btv->id < 878) {
+ /* bt848 (mis)uses a bit in the irq mask for etbf */
+ if (triton1)
+ btv->triton1 = BT848_INT_ETBF;
+ } else {
+ /* bt878 has a bit in the pci config space for it */
+ pci_read_config_byte(btv->c.pci, BT878_DEVCTRL, &command);
+ if (triton1)
+ command |= BT878_EN_TBFX;
+ if (vsfx)
+ command |= BT878_EN_VSFX;
+ pci_write_config_byte(btv->c.pci, BT878_DEVCTRL, command);
+ }
+ if (UNSET != latency)
+ pci_write_config_byte(btv->c.pci, PCI_LATENCY_TIMER, latency);
+ return 0;
+}
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c
new file mode 100644
index 0000000..b68918c
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-driver.c
@@ -0,0 +1,4630 @@
+/*
+
+ bttv - Bt848 frame grabber driver
+
+ Copyright (C) 1996,97,98 Ralph Metzler <rjkm@thp.uni-koeln.de>
+ & Marcus Metzler <mocm@thp.uni-koeln.de>
+ (c) 1999-2002 Gerd Knorr <kraxel@bytesex.org>
+
+ some v4l2 code lines are taken from Justin's bttv2 driver which is
+ (c) 2000 Justin Schoeman <justin@suntiger.ee.up.ac.za>
+
+ V4L1 removal from:
+ (c) 2005-2006 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
+
+ Fixes to be fully V4L2 compliant by
+ (c) 2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+
+ Cropping and overscan support
+ Copyright (C) 2005, 2006 Michael H. Schimek <mschimek@gmx.at>
+ Sponsored by OPQ Systems AB
+
+ 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/kdev_t.h>
+#include "bttvp.h"
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/tvaudio.h>
+#include <media/msp3400.h>
+
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+
+#include <media/saa6588.h>
+
+#define BTTV_VERSION "0.9.19"
+
+unsigned int bttv_num; /* number of Bt848s in use */
+struct bttv *bttvs[BTTV_MAX];
+
+unsigned int bttv_debug;
+unsigned int bttv_verbose = 1;
+unsigned int bttv_gpio;
+
+/* config variables */
+#ifdef __BIG_ENDIAN
+static unsigned int bigendian=1;
+#else
+static unsigned int bigendian;
+#endif
+static unsigned int radio[BTTV_MAX];
+static unsigned int irq_debug;
+static unsigned int gbuffers = 8;
+static unsigned int gbufsize = 0x208000;
+static unsigned int reset_crop = 1;
+
+static int video_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+static int radio_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+static int vbi_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+static int debug_latency;
+static int disable_ir;
+
+static unsigned int fdsr;
+
+/* options */
+static unsigned int combfilter;
+static unsigned int lumafilter;
+static unsigned int automute = 1;
+static unsigned int chroma_agc;
+static unsigned int adc_crush = 1;
+static unsigned int whitecrush_upper = 0xCF;
+static unsigned int whitecrush_lower = 0x7F;
+static unsigned int vcr_hack;
+static unsigned int irq_iswitch;
+static unsigned int uv_ratio = 50;
+static unsigned int full_luma_range;
+static unsigned int coring;
+
+/* API features (turn on/off stuff for testing) */
+static unsigned int v4l2 = 1;
+
+/* insmod args */
+module_param(bttv_verbose, int, 0644);
+module_param(bttv_gpio, int, 0644);
+module_param(bttv_debug, int, 0644);
+module_param(irq_debug, int, 0644);
+module_param(debug_latency, int, 0644);
+module_param(disable_ir, int, 0444);
+
+module_param(fdsr, int, 0444);
+module_param(gbuffers, int, 0444);
+module_param(gbufsize, int, 0444);
+module_param(reset_crop, int, 0444);
+
+module_param(v4l2, int, 0644);
+module_param(bigendian, int, 0644);
+module_param(irq_iswitch, int, 0644);
+module_param(combfilter, int, 0444);
+module_param(lumafilter, int, 0444);
+module_param(automute, int, 0444);
+module_param(chroma_agc, int, 0444);
+module_param(adc_crush, int, 0444);
+module_param(whitecrush_upper, int, 0444);
+module_param(whitecrush_lower, int, 0444);
+module_param(vcr_hack, int, 0444);
+module_param(uv_ratio, int, 0444);
+module_param(full_luma_range, int, 0444);
+module_param(coring, int, 0444);
+
+module_param_array(radio, int, NULL, 0444);
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+module_param_array(vbi_nr, int, NULL, 0444);
+
+MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)");
+MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian");
+MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)");
+MODULE_PARM_DESC(bttv_gpio,"log gpio changes, default is 0 (no)");
+MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)");
+MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)");
+MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
+MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8");
+MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000");
+MODULE_PARM_DESC(reset_crop,"reset cropping parameters at open(), default "
+ "is 1 (yes) for compatibility with older applications");
+MODULE_PARM_DESC(automute,"mute audio on bad/missing video signal, default is 1 (yes)");
+MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)");
+MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)");
+MODULE_PARM_DESC(whitecrush_upper,"sets the white crush upper value, default is 207");
+MODULE_PARM_DESC(whitecrush_lower,"sets the white crush lower value, default is 127");
+MODULE_PARM_DESC(vcr_hack,"enables the VCR hack (improves synch on poor VCR tapes), default is 0 (no)");
+MODULE_PARM_DESC(irq_iswitch,"switch inputs in irq handler");
+MODULE_PARM_DESC(uv_ratio,"ratio between u and v gains, default is 50");
+MODULE_PARM_DESC(full_luma_range,"use the full luma range, default is 0 (no)");
+MODULE_PARM_DESC(coring,"set the luma coring level, default is 0 (no)");
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
+
+MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards");
+MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(BTTV_VERSION);
+
+/* ----------------------------------------------------------------------- */
+/* sysfs */
+
+static ssize_t show_card(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vfd = container_of(cd, struct video_device, dev);
+ struct bttv *btv = video_get_drvdata(vfd);
+ return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET);
+}
+static DEVICE_ATTR(card, S_IRUGO, show_card, NULL);
+
+/* ----------------------------------------------------------------------- */
+/* dvb auto-load setup */
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+ request_module("dvb-bt8xx");
+}
+
+static void request_modules(struct bttv *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_modules(struct bttv *dev)
+{
+ flush_work(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#define flush_request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+
+/* ----------------------------------------------------------------------- */
+/* static data */
+
+/* special timing tables from conexant... */
+static u8 SRAM_Table[][60] =
+{
+ /* PAL digital input over GPIO[7:0] */
+ {
+ 45, // 45 bytes following
+ 0x36,0x11,0x01,0x00,0x90,0x02,0x05,0x10,0x04,0x16,
+ 0x12,0x05,0x11,0x00,0x04,0x12,0xC0,0x00,0x31,0x00,
+ 0x06,0x51,0x08,0x03,0x89,0x08,0x07,0xC0,0x44,0x00,
+ 0x81,0x01,0x01,0xA9,0x0D,0x02,0x02,0x50,0x03,0x37,
+ 0x37,0x00,0xAF,0x21,0x00
+ },
+ /* NTSC digital input over GPIO[7:0] */
+ {
+ 51, // 51 bytes following
+ 0x0C,0xC0,0x00,0x00,0x90,0x02,0x03,0x10,0x03,0x06,
+ 0x10,0x04,0x12,0x12,0x05,0x02,0x13,0x04,0x19,0x00,
+ 0x04,0x39,0x00,0x06,0x59,0x08,0x03,0x83,0x08,0x07,
+ 0x03,0x50,0x00,0xC0,0x40,0x00,0x86,0x01,0x01,0xA6,
+ 0x0D,0x02,0x03,0x11,0x01,0x05,0x37,0x00,0xAC,0x21,
+ 0x00,
+ },
+ // TGB_NTSC392 // quartzsight
+ // This table has been modified to be used for Fusion Rev D
+ {
+ 0x2A, // size of table = 42
+ 0x06, 0x08, 0x04, 0x0a, 0xc0, 0x00, 0x18, 0x08, 0x03, 0x24,
+ 0x08, 0x07, 0x02, 0x90, 0x02, 0x08, 0x10, 0x04, 0x0c, 0x10,
+ 0x05, 0x2c, 0x11, 0x04, 0x55, 0x48, 0x00, 0x05, 0x50, 0x00,
+ 0xbf, 0x0c, 0x02, 0x2f, 0x3d, 0x00, 0x2f, 0x3f, 0x00, 0xc3,
+ 0x20, 0x00
+ }
+};
+
+/* minhdelayx1 first video pixel we can capture on a line and
+ hdelayx1 start of active video, both relative to rising edge of
+ /HRESET pulse (0H) in 1 / fCLKx1.
+ swidth width of active video and
+ totalwidth total line width, both in 1 / fCLKx1.
+ sqwidth total line width in square pixels.
+ vdelay start of active video in 2 * field lines relative to
+ trailing edge of /VRESET pulse (VDELAY register).
+ sheight height of active video in 2 * field lines.
+ videostart0 ITU-R frame line number of the line corresponding
+ to vdelay in the first field. */
+#define CROPCAP(minhdelayx1, hdelayx1, swidth, totalwidth, sqwidth, \
+ vdelay, sheight, videostart0) \
+ .cropcap.bounds.left = minhdelayx1, \
+ /* * 2 because vertically we count field lines times two, */ \
+ /* e.g. 23 * 2 to 23 * 2 + 576 in PAL-BGHI defrect. */ \
+ .cropcap.bounds.top = (videostart0) * 2 - (vdelay) + MIN_VDELAY, \
+ /* 4 is a safety margin at the end of the line. */ \
+ .cropcap.bounds.width = (totalwidth) - (minhdelayx1) - 4, \
+ .cropcap.bounds.height = (sheight) + (vdelay) - MIN_VDELAY, \
+ .cropcap.defrect.left = hdelayx1, \
+ .cropcap.defrect.top = (videostart0) * 2, \
+ .cropcap.defrect.width = swidth, \
+ .cropcap.defrect.height = sheight, \
+ .cropcap.pixelaspect.numerator = totalwidth, \
+ .cropcap.pixelaspect.denominator = sqwidth,
+
+const struct bttv_tvnorm bttv_tvnorms[] = {
+ /* PAL-BDGHI */
+ /* max. active video is actually 922, but 924 is divisible by 4 and 3! */
+ /* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */
+ {
+ .v4l2_id = V4L2_STD_PAL,
+ .name = "PAL",
+ .Fsc = 35468950,
+ .swidth = 924,
+ .sheight = 576,
+ .totalwidth = 1135,
+ .adelay = 0x7f,
+ .bdelay = 0x72,
+ .iform = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+ .scaledtwidth = 1135,
+ .hdelayx1 = 186,
+ .hactivex1 = 924,
+ .vdelay = 0x20,
+ .vbipack = 255, /* min (2048 / 4, 0x1ff) & 0xff */
+ .sram = 0,
+ /* ITU-R frame line number of the first VBI line
+ we can capture, of the first and second field.
+ The last line is determined by cropcap.bounds. */
+ .vbistart = { 7, 320 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 186,
+ /* Should be (768 * 1135 + 944 / 2) / 944.
+ cropcap.defrect is used for image width
+ checks, so we keep the old value 924. */
+ /* swidth */ 924,
+ /* totalwidth */ 1135,
+ /* sqwidth */ 944,
+ /* vdelay */ 0x20,
+ /* sheight */ 576,
+ /* videostart0 */ 23)
+ /* bt878 (and bt848?) can capture another
+ line below active video. */
+ .cropcap.bounds.height = (576 + 2) + 0x20 - 2,
+ },{
+ .v4l2_id = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
+ .name = "NTSC",
+ .Fsc = 28636363,
+ .swidth = 768,
+ .sheight = 480,
+ .totalwidth = 910,
+ .adelay = 0x68,
+ .bdelay = 0x5d,
+ .iform = (BT848_IFORM_NTSC|BT848_IFORM_XT0),
+ .scaledtwidth = 910,
+ .hdelayx1 = 128,
+ .hactivex1 = 910,
+ .vdelay = 0x1a,
+ .vbipack = 144, /* min (1600 / 4, 0x1ff) & 0xff */
+ .sram = 1,
+ .vbistart = { 10, 273 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 128,
+ /* Should be (640 * 910 + 780 / 2) / 780? */
+ /* swidth */ 768,
+ /* totalwidth */ 910,
+ /* sqwidth */ 780,
+ /* vdelay */ 0x1a,
+ /* sheight */ 480,
+ /* videostart0 */ 23)
+ },{
+ .v4l2_id = V4L2_STD_SECAM,
+ .name = "SECAM",
+ .Fsc = 35468950,
+ .swidth = 924,
+ .sheight = 576,
+ .totalwidth = 1135,
+ .adelay = 0x7f,
+ .bdelay = 0xb0,
+ .iform = (BT848_IFORM_SECAM|BT848_IFORM_XT1),
+ .scaledtwidth = 1135,
+ .hdelayx1 = 186,
+ .hactivex1 = 922,
+ .vdelay = 0x20,
+ .vbipack = 255,
+ .sram = 0, /* like PAL, correct? */
+ .vbistart = { 7, 320 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 186,
+ /* swidth */ 924,
+ /* totalwidth */ 1135,
+ /* sqwidth */ 944,
+ /* vdelay */ 0x20,
+ /* sheight */ 576,
+ /* videostart0 */ 23)
+ },{
+ .v4l2_id = V4L2_STD_PAL_Nc,
+ .name = "PAL-Nc",
+ .Fsc = 28636363,
+ .swidth = 640,
+ .sheight = 576,
+ .totalwidth = 910,
+ .adelay = 0x68,
+ .bdelay = 0x5d,
+ .iform = (BT848_IFORM_PAL_NC|BT848_IFORM_XT0),
+ .scaledtwidth = 780,
+ .hdelayx1 = 130,
+ .hactivex1 = 734,
+ .vdelay = 0x1a,
+ .vbipack = 144,
+ .sram = -1,
+ .vbistart = { 7, 320 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 130,
+ /* swidth */ (640 * 910 + 780 / 2) / 780,
+ /* totalwidth */ 910,
+ /* sqwidth */ 780,
+ /* vdelay */ 0x1a,
+ /* sheight */ 576,
+ /* videostart0 */ 23)
+ },{
+ .v4l2_id = V4L2_STD_PAL_M,
+ .name = "PAL-M",
+ .Fsc = 28636363,
+ .swidth = 640,
+ .sheight = 480,
+ .totalwidth = 910,
+ .adelay = 0x68,
+ .bdelay = 0x5d,
+ .iform = (BT848_IFORM_PAL_M|BT848_IFORM_XT0),
+ .scaledtwidth = 780,
+ .hdelayx1 = 135,
+ .hactivex1 = 754,
+ .vdelay = 0x1a,
+ .vbipack = 144,
+ .sram = -1,
+ .vbistart = { 10, 273 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 135,
+ /* swidth */ (640 * 910 + 780 / 2) / 780,
+ /* totalwidth */ 910,
+ /* sqwidth */ 780,
+ /* vdelay */ 0x1a,
+ /* sheight */ 480,
+ /* videostart0 */ 23)
+ },{
+ .v4l2_id = V4L2_STD_PAL_N,
+ .name = "PAL-N",
+ .Fsc = 35468950,
+ .swidth = 768,
+ .sheight = 576,
+ .totalwidth = 1135,
+ .adelay = 0x7f,
+ .bdelay = 0x72,
+ .iform = (BT848_IFORM_PAL_N|BT848_IFORM_XT1),
+ .scaledtwidth = 944,
+ .hdelayx1 = 186,
+ .hactivex1 = 922,
+ .vdelay = 0x20,
+ .vbipack = 144,
+ .sram = -1,
+ .vbistart = { 7, 320 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 186,
+ /* swidth */ (768 * 1135 + 944 / 2) / 944,
+ /* totalwidth */ 1135,
+ /* sqwidth */ 944,
+ /* vdelay */ 0x20,
+ /* sheight */ 576,
+ /* videostart0 */ 23)
+ },{
+ .v4l2_id = V4L2_STD_NTSC_M_JP,
+ .name = "NTSC-JP",
+ .Fsc = 28636363,
+ .swidth = 640,
+ .sheight = 480,
+ .totalwidth = 910,
+ .adelay = 0x68,
+ .bdelay = 0x5d,
+ .iform = (BT848_IFORM_NTSC_J|BT848_IFORM_XT0),
+ .scaledtwidth = 780,
+ .hdelayx1 = 135,
+ .hactivex1 = 754,
+ .vdelay = 0x16,
+ .vbipack = 144,
+ .sram = -1,
+ .vbistart = { 10, 273 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 135,
+ /* swidth */ (640 * 910 + 780 / 2) / 780,
+ /* totalwidth */ 910,
+ /* sqwidth */ 780,
+ /* vdelay */ 0x16,
+ /* sheight */ 480,
+ /* videostart0 */ 23)
+ },{
+ /* that one hopefully works with the strange timing
+ * which video recorders produce when playing a NTSC
+ * tape on a PAL TV ... */
+ .v4l2_id = V4L2_STD_PAL_60,
+ .name = "PAL-60",
+ .Fsc = 35468950,
+ .swidth = 924,
+ .sheight = 480,
+ .totalwidth = 1135,
+ .adelay = 0x7f,
+ .bdelay = 0x72,
+ .iform = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+ .scaledtwidth = 1135,
+ .hdelayx1 = 186,
+ .hactivex1 = 924,
+ .vdelay = 0x1a,
+ .vbipack = 255,
+ .vtotal = 524,
+ .sram = -1,
+ .vbistart = { 10, 273 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 186,
+ /* swidth */ 924,
+ /* totalwidth */ 1135,
+ /* sqwidth */ 944,
+ /* vdelay */ 0x1a,
+ /* sheight */ 480,
+ /* videostart0 */ 23)
+ }
+};
+static const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms);
+
+/* ----------------------------------------------------------------------- */
+/* bttv format list
+ packed pixel formats must come first */
+static const struct bttv_format formats[] = {
+ {
+ .name = "8 bpp, gray",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .btformat = BT848_COLOR_FMT_Y8,
+ .depth = 8,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "8 bpp, dithered color",
+ .fourcc = V4L2_PIX_FMT_HI240,
+ .btformat = BT848_COLOR_FMT_RGB8,
+ .depth = 8,
+ .flags = FORMAT_FLAGS_PACKED | FORMAT_FLAGS_DITHER,
+ },{
+ .name = "15 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .btformat = BT848_COLOR_FMT_RGB15,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "15 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .btformat = BT848_COLOR_FMT_RGB15,
+ .btswap = 0x03, /* byteswap */
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "16 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .btformat = BT848_COLOR_FMT_RGB16,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "16 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .btformat = BT848_COLOR_FMT_RGB16,
+ .btswap = 0x03, /* byteswap */
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "24 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .btformat = BT848_COLOR_FMT_RGB24,
+ .depth = 24,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "32 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .btformat = BT848_COLOR_FMT_RGB32,
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "32 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .btformat = BT848_COLOR_FMT_RGB32,
+ .btswap = 0x0f, /* byte+word swap */
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .btformat = BT848_COLOR_FMT_YUY2,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .btformat = BT848_COLOR_FMT_YUY2,
+ .btswap = 0x03, /* byteswap */
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "4:2:2, planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .btformat = BT848_COLOR_FMT_YCrCb422,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .hshift = 1,
+ .vshift = 0,
+ },{
+ .name = "4:2:0, planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .btformat = BT848_COLOR_FMT_YCrCb422,
+ .depth = 12,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .hshift = 1,
+ .vshift = 1,
+ },{
+ .name = "4:2:0, planar, Y-Cr-Cb",
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .btformat = BT848_COLOR_FMT_YCrCb422,
+ .depth = 12,
+ .flags = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
+ .hshift = 1,
+ .vshift = 1,
+ },{
+ .name = "4:1:1, planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV411P,
+ .btformat = BT848_COLOR_FMT_YCrCb411,
+ .depth = 12,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .hshift = 2,
+ .vshift = 0,
+ },{
+ .name = "4:1:0, planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV410,
+ .btformat = BT848_COLOR_FMT_YCrCb411,
+ .depth = 9,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .hshift = 2,
+ .vshift = 2,
+ },{
+ .name = "4:1:0, planar, Y-Cr-Cb",
+ .fourcc = V4L2_PIX_FMT_YVU410,
+ .btformat = BT848_COLOR_FMT_YCrCb411,
+ .depth = 9,
+ .flags = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
+ .hshift = 2,
+ .vshift = 2,
+ },{
+ .name = "raw scanlines",
+ .fourcc = -1,
+ .btformat = BT848_COLOR_FMT_RAW,
+ .depth = 8,
+ .flags = FORMAT_FLAGS_RAW,
+ }
+};
+static const unsigned int FORMATS = ARRAY_SIZE(formats);
+
+/* ----------------------------------------------------------------------- */
+
+#define V4L2_CID_PRIVATE_CHROMA_AGC (V4L2_CID_PRIVATE_BASE + 0)
+#define V4L2_CID_PRIVATE_COMBFILTER (V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_PRIVATE_LUMAFILTER (V4L2_CID_PRIVATE_BASE + 3)
+#define V4L2_CID_PRIVATE_AGC_CRUSH (V4L2_CID_PRIVATE_BASE + 4)
+#define V4L2_CID_PRIVATE_VCR_HACK (V4L2_CID_PRIVATE_BASE + 5)
+#define V4L2_CID_PRIVATE_WHITECRUSH_UPPER (V4L2_CID_PRIVATE_BASE + 6)
+#define V4L2_CID_PRIVATE_WHITECRUSH_LOWER (V4L2_CID_PRIVATE_BASE + 7)
+#define V4L2_CID_PRIVATE_UV_RATIO (V4L2_CID_PRIVATE_BASE + 8)
+#define V4L2_CID_PRIVATE_FULL_LUMA_RANGE (V4L2_CID_PRIVATE_BASE + 9)
+#define V4L2_CID_PRIVATE_CORING (V4L2_CID_PRIVATE_BASE + 10)
+#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 11)
+
+static const struct v4l2_queryctrl no_ctl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+static const struct v4l2_queryctrl bttv_ctls[] = {
+ /* --- video --- */
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 256,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 128,
+ .default_value = 27648,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 128,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 256,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ /* --- audio --- */
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535/100,
+ .default_value = 65535,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_AUDIO_BALANCE,
+ .name = "Balance",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535/100,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_AUDIO_BASS,
+ .name = "Bass",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535/100,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_AUDIO_TREBLE,
+ .name = "Treble",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535/100,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ /* --- private --- */
+ {
+ .id = V4L2_CID_PRIVATE_CHROMA_AGC,
+ .name = "chroma agc",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_COMBFILTER,
+ .name = "combfilter",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_AUTOMUTE,
+ .name = "automute",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_LUMAFILTER,
+ .name = "luma decimation filter",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_AGC_CRUSH,
+ .name = "agc crush",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_VCR_HACK,
+ .name = "vcr hack",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_WHITECRUSH_UPPER,
+ .name = "whitecrush upper",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 0xCF,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_PRIVATE_WHITECRUSH_LOWER,
+ .name = "whitecrush lower",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 0x7F,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_PRIVATE_UV_RATIO,
+ .name = "uv ratio",
+ .minimum = 0,
+ .maximum = 100,
+ .step = 1,
+ .default_value = 50,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_PRIVATE_FULL_LUMA_RANGE,
+ .name = "full luma range",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_CORING,
+ .name = "coring",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }
+
+
+
+};
+
+static const struct v4l2_queryctrl *ctrl_by_id(int id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bttv_ctls); i++)
+ if (bttv_ctls[i].id == id)
+ return bttv_ctls+i;
+
+ return NULL;
+}
+
+/* ----------------------------------------------------------------------- */
+/* resource management */
+
+/*
+ RESOURCE_ allocated by freed by
+
+ VIDEO_READ bttv_read 1) bttv_read 2)
+
+ VIDEO_STREAM VIDIOC_STREAMON VIDIOC_STREAMOFF
+ VIDIOC_QBUF 1) bttv_release
+ VIDIOCMCAPTURE 1)
+
+ OVERLAY VIDIOCCAPTURE on VIDIOCCAPTURE off
+ VIDIOC_OVERLAY on VIDIOC_OVERLAY off
+ 3) bttv_release
+
+ VBI VIDIOC_STREAMON VIDIOC_STREAMOFF
+ VIDIOC_QBUF 1) bttv_release
+ bttv_read, bttv_poll 1) 4)
+
+ 1) The resource must be allocated when we enter buffer prepare functions
+ and remain allocated while buffers are in the DMA queue.
+ 2) This is a single frame read.
+ 3) VIDIOC_S_FBUF and VIDIOC_S_FMT (OVERLAY) still work when
+ RESOURCE_OVERLAY is allocated.
+ 4) This is a continuous read, implies VIDIOC_STREAMON.
+
+ Note this driver permits video input and standard changes regardless if
+ resources are allocated.
+*/
+
+#define VBI_RESOURCES (RESOURCE_VBI)
+#define VIDEO_RESOURCES (RESOURCE_VIDEO_READ | \
+ RESOURCE_VIDEO_STREAM | \
+ RESOURCE_OVERLAY)
+
+static
+int check_alloc_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bit)
+{
+ int xbits; /* mutual exclusive resources */
+
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ xbits = bit;
+ if (bit & (RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM))
+ xbits |= RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM;
+
+ /* is it free? */
+ if (btv->resources & xbits) {
+ /* no, someone else uses it */
+ goto fail;
+ }
+
+ if ((bit & VIDEO_RESOURCES)
+ && 0 == (btv->resources & VIDEO_RESOURCES)) {
+ /* Do crop - use current, don't - use default parameters. */
+ __s32 top = btv->crop[!!fh->do_crop].rect.top;
+
+ if (btv->vbi_end > top)
+ goto fail;
+
+ /* We cannot capture the same line as video and VBI data.
+ Claim scan lines crop[].rect.top to bottom. */
+ btv->crop_start = top;
+ } else if (bit & VBI_RESOURCES) {
+ __s32 end = fh->vbi_fmt.end;
+
+ if (end > btv->crop_start)
+ goto fail;
+
+ /* Claim scan lines above fh->vbi_fmt.end. */
+ btv->vbi_end = end;
+ }
+
+ /* it's free, grab it */
+ fh->resources |= bit;
+ btv->resources |= bit;
+ return 1;
+
+ fail:
+ return 0;
+}
+
+static
+int check_btres(struct bttv_fh *fh, int bit)
+{
+ return (fh->resources & bit);
+}
+
+static
+int locked_btres(struct bttv *btv, int bit)
+{
+ return (btv->resources & bit);
+}
+
+/* Call with btv->lock down. */
+static void
+disclaim_vbi_lines(struct bttv *btv)
+{
+ btv->vbi_end = 0;
+}
+
+/* Call with btv->lock down. */
+static void
+disclaim_video_lines(struct bttv *btv)
+{
+ const struct bttv_tvnorm *tvnorm;
+ u8 crop;
+
+ tvnorm = &bttv_tvnorms[btv->tvnorm];
+ btv->crop_start = tvnorm->cropcap.bounds.top
+ + tvnorm->cropcap.bounds.height;
+
+ /* VBI capturing ends at VDELAY, start of video capturing, no
+ matter how many lines the VBI RISC program expects. When video
+ capturing is off, it shall no longer "preempt" VBI capturing,
+ so we set VDELAY to maximum. */
+ crop = btread(BT848_E_CROP) | 0xc0;
+ btwrite(crop, BT848_E_CROP);
+ btwrite(0xfe, BT848_E_VDELAY_LO);
+ btwrite(crop, BT848_O_CROP);
+ btwrite(0xfe, BT848_O_VDELAY_LO);
+}
+
+static
+void free_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bits)
+{
+ if ((fh->resources & bits) != bits) {
+ /* trying to free resources not allocated by us ... */
+ pr_err("BUG! (btres)\n");
+ }
+ fh->resources &= ~bits;
+ btv->resources &= ~bits;
+
+ bits = btv->resources;
+
+ if (0 == (bits & VIDEO_RESOURCES))
+ disclaim_video_lines(btv);
+
+ if (0 == (bits & VBI_RESOURCES))
+ disclaim_vbi_lines(btv);
+}
+
+/* ----------------------------------------------------------------------- */
+/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC */
+
+/* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C
+ PLL_X = Reference pre-divider (0=1, 1=2)
+ PLL_C = Post divider (0=6, 1=4)
+ PLL_I = Integer input
+ PLL_F = Fractional input
+
+ F_input = 28.636363 MHz:
+ PAL (CLKx2 = 35.46895 MHz): PLL_X = 1, PLL_I = 0x0E, PLL_F = 0xDCF9, PLL_C = 0
+*/
+
+static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout)
+{
+ unsigned char fl, fh, fi;
+
+ /* prevent overflows */
+ fin/=4;
+ fout/=4;
+
+ fout*=12;
+ fi=fout/fin;
+
+ fout=(fout%fin)*256;
+ fh=fout/fin;
+
+ fout=(fout%fin)*256;
+ fl=fout/fin;
+
+ btwrite(fl, BT848_PLL_F_LO);
+ btwrite(fh, BT848_PLL_F_HI);
+ btwrite(fi|BT848_PLL_X, BT848_PLL_XCI);
+}
+
+static void set_pll(struct bttv *btv)
+{
+ int i;
+
+ if (!btv->pll.pll_crystal)
+ return;
+
+ if (btv->pll.pll_ofreq == btv->pll.pll_current) {
+ dprintk("%d: PLL: no change required\n", btv->c.nr);
+ return;
+ }
+
+ if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) {
+ /* no PLL needed */
+ if (btv->pll.pll_current == 0)
+ return;
+ if (bttv_verbose)
+ pr_info("%d: PLL can sleep, using XTAL (%d)\n",
+ btv->c.nr, btv->pll.pll_ifreq);
+ btwrite(0x00,BT848_TGCTRL);
+ btwrite(0x00,BT848_PLL_XCI);
+ btv->pll.pll_current = 0;
+ return;
+ }
+
+ if (bttv_verbose)
+ pr_info("%d: Setting PLL: %d => %d (needs up to 100ms)\n",
+ btv->c.nr,
+ btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+ set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+
+ for (i=0; i<10; i++) {
+ /* Let other people run while the PLL stabilizes */
+ msleep(10);
+
+ if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) {
+ btwrite(0,BT848_DSTATUS);
+ } else {
+ btwrite(0x08,BT848_TGCTRL);
+ btv->pll.pll_current = btv->pll.pll_ofreq;
+ if (bttv_verbose)
+ pr_info("PLL set ok\n");
+ return;
+ }
+ }
+ btv->pll.pll_current = -1;
+ if (bttv_verbose)
+ pr_info("Setting PLL failed\n");
+ return;
+}
+
+/* used to switch between the bt848's analog/digital video capture modes */
+static void bt848A_set_timing(struct bttv *btv)
+{
+ int i, len;
+ int table_idx = bttv_tvnorms[btv->tvnorm].sram;
+ int fsc = bttv_tvnorms[btv->tvnorm].Fsc;
+
+ if (btv->input == btv->dig) {
+ dprintk("%d: load digital timing table (table_idx=%d)\n",
+ btv->c.nr,table_idx);
+
+ /* timing change...reset timing generator address */
+ btwrite(0x00, BT848_TGCTRL);
+ btwrite(0x02, BT848_TGCTRL);
+ btwrite(0x00, BT848_TGCTRL);
+
+ len=SRAM_Table[table_idx][0];
+ for(i = 1; i <= len; i++)
+ btwrite(SRAM_Table[table_idx][i],BT848_TGLB);
+ btv->pll.pll_ofreq = 27000000;
+
+ set_pll(btv);
+ btwrite(0x11, BT848_TGCTRL);
+ btwrite(0x41, BT848_DVSIF);
+ } else {
+ btv->pll.pll_ofreq = fsc;
+ set_pll(btv);
+ btwrite(0x0, BT848_DVSIF);
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void bt848_bright(struct bttv *btv, int bright)
+{
+ int value;
+
+ // printk("set bright: %d\n", bright); // DEBUG
+ btv->bright = bright;
+
+ /* We want -128 to 127 we get 0-65535 */
+ value = (bright >> 8) - 128;
+ btwrite(value & 0xff, BT848_BRIGHT);
+}
+
+static void bt848_hue(struct bttv *btv, int hue)
+{
+ int value;
+
+ btv->hue = hue;
+
+ /* -128 to 127 */
+ value = (hue >> 8) - 128;
+ btwrite(value & 0xff, BT848_HUE);
+}
+
+static void bt848_contrast(struct bttv *btv, int cont)
+{
+ int value,hibit;
+
+ btv->contrast = cont;
+
+ /* 0-511 */
+ value = (cont >> 7);
+ hibit = (value >> 6) & 4;
+ btwrite(value & 0xff, BT848_CONTRAST_LO);
+ btaor(hibit, ~4, BT848_E_CONTROL);
+ btaor(hibit, ~4, BT848_O_CONTROL);
+}
+
+static void bt848_sat(struct bttv *btv, int color)
+{
+ int val_u,val_v,hibits;
+
+ btv->saturation = color;
+
+ /* 0-511 for the color */
+ val_u = ((color * btv->opt_uv_ratio) / 50) >> 7;
+ val_v = (((color * (100 - btv->opt_uv_ratio) / 50) >>7)*180L)/254;
+ hibits = (val_u >> 7) & 2;
+ hibits |= (val_v >> 8) & 1;
+ btwrite(val_u & 0xff, BT848_SAT_U_LO);
+ btwrite(val_v & 0xff, BT848_SAT_V_LO);
+ btaor(hibits, ~3, BT848_E_CONTROL);
+ btaor(hibits, ~3, BT848_O_CONTROL);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int
+video_mux(struct bttv *btv, unsigned int input)
+{
+ int mux,mask2;
+
+ if (input >= bttv_tvcards[btv->c.type].video_inputs)
+ return -EINVAL;
+
+ /* needed by RemoteVideo MX */
+ mask2 = bttv_tvcards[btv->c.type].gpiomask2;
+ if (mask2)
+ gpio_inout(mask2,mask2);
+
+ if (input == btv->svhs) {
+ btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
+ btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
+ } else {
+ btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
+ btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
+ }
+ mux = bttv_muxsel(btv, input);
+ btaor(mux<<5, ~(3<<5), BT848_IFORM);
+ dprintk("%d: video mux: input=%d mux=%d\n", btv->c.nr, input, mux);
+
+ /* card specific hook */
+ if(bttv_tvcards[btv->c.type].muxsel_hook)
+ bttv_tvcards[btv->c.type].muxsel_hook (btv, input);
+ return 0;
+}
+
+static char *audio_modes[] = {
+ "audio: tuner", "audio: radio", "audio: extern",
+ "audio: intern", "audio: mute"
+};
+
+static int
+audio_mux(struct bttv *btv, int input, int mute)
+{
+ int gpio_val, signal;
+ struct v4l2_control ctrl;
+
+ gpio_inout(bttv_tvcards[btv->c.type].gpiomask,
+ bttv_tvcards[btv->c.type].gpiomask);
+ signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC;
+
+ btv->mute = mute;
+ btv->audio = input;
+
+ /* automute */
+ mute = mute || (btv->opt_automute && !signal && !btv->radio_user);
+
+ if (mute)
+ gpio_val = bttv_tvcards[btv->c.type].gpiomute;
+ else
+ gpio_val = bttv_tvcards[btv->c.type].gpiomux[input];
+
+ switch (btv->c.type) {
+ case BTTV_BOARD_VOODOOTV_FM:
+ case BTTV_BOARD_VOODOOTV_200:
+ gpio_val = bttv_tda9880_setnorm(btv, gpio_val);
+ break;
+
+ default:
+ gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val);
+ }
+
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv, audio_modes[mute ? 4 : input]);
+ if (in_interrupt())
+ return 0;
+
+ ctrl.id = V4L2_CID_AUDIO_MUTE;
+ ctrl.value = btv->mute;
+ bttv_call_all(btv, core, s_ctrl, &ctrl);
+ if (btv->sd_msp34xx) {
+ u32 in;
+
+ /* Note: the inputs tuner/radio/extern/intern are translated
+ to msp routings. This assumes common behavior for all msp3400
+ based TV cards. When this assumption fails, then the
+ specific MSP routing must be added to the card table.
+ For now this is sufficient. */
+ switch (input) {
+ case TVAUDIO_INPUT_RADIO:
+ /* Some boards need the msp do to the radio demod */
+ if (btv->radio_uses_msp_demodulator) {
+ in = MSP_INPUT_DEFAULT;
+ break;
+ }
+ in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+ break;
+ case TVAUDIO_INPUT_EXTERN:
+ in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+ break;
+ case TVAUDIO_INPUT_INTERN:
+ /* Yes, this is the same input as for RADIO. I doubt
+ if this is ever used. The only board with an INTERN
+ input is the BTTV_BOARD_AVERMEDIA98. I wonder how
+ that was tested. My guess is that the whole INTERN
+ input does not work. */
+ in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+ break;
+ case TVAUDIO_INPUT_TUNER:
+ default:
+ /* This is the only card that uses TUNER2, and afaik,
+ is the only difference between the VOODOOTV_FM
+ and VOODOOTV_200 */
+ if (btv->c.type == BTTV_BOARD_VOODOOTV_200)
+ in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER2, \
+ MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER);
+ else
+ in = MSP_INPUT_DEFAULT;
+ break;
+ }
+ v4l2_subdev_call(btv->sd_msp34xx, audio, s_routing,
+ in, MSP_OUTPUT_DEFAULT, 0);
+ }
+ if (btv->sd_tvaudio) {
+ v4l2_subdev_call(btv->sd_tvaudio, audio, s_routing,
+ input, 0, 0);
+ }
+ return 0;
+}
+
+static inline int
+audio_mute(struct bttv *btv, int mute)
+{
+ return audio_mux(btv, btv->audio, mute);
+}
+
+static inline int
+audio_input(struct bttv *btv, int input)
+{
+ return audio_mux(btv, input, btv->mute);
+}
+
+static void
+bttv_crop_calc_limits(struct bttv_crop *c)
+{
+ /* Scale factor min. 1:1, max. 16:1. Min. image size
+ 48 x 32. Scaled width must be a multiple of 4. */
+
+ if (1) {
+ /* For bug compatibility with VIDIOCGCAP and image
+ size checks in earlier driver versions. */
+ c->min_scaled_width = 48;
+ c->min_scaled_height = 32;
+ } else {
+ c->min_scaled_width =
+ (max(48, c->rect.width >> 4) + 3) & ~3;
+ c->min_scaled_height =
+ max(32, c->rect.height >> 4);
+ }
+
+ c->max_scaled_width = c->rect.width & ~3;
+ c->max_scaled_height = c->rect.height;
+}
+
+static void
+bttv_crop_reset(struct bttv_crop *c, unsigned int norm)
+{
+ c->rect = bttv_tvnorms[norm].cropcap.defrect;
+ bttv_crop_calc_limits(c);
+}
+
+/* Call with btv->lock down. */
+static int
+set_tvnorm(struct bttv *btv, unsigned int norm)
+{
+ const struct bttv_tvnorm *tvnorm;
+ v4l2_std_id id;
+
+ BUG_ON(norm >= BTTV_TVNORMS);
+ BUG_ON(btv->tvnorm >= BTTV_TVNORMS);
+
+ tvnorm = &bttv_tvnorms[norm];
+
+ if (memcmp(&bttv_tvnorms[btv->tvnorm].cropcap, &tvnorm->cropcap,
+ sizeof (tvnorm->cropcap))) {
+ bttv_crop_reset(&btv->crop[0], norm);
+ btv->crop[1] = btv->crop[0]; /* current = default */
+
+ if (0 == (btv->resources & VIDEO_RESOURCES)) {
+ btv->crop_start = tvnorm->cropcap.bounds.top
+ + tvnorm->cropcap.bounds.height;
+ }
+ }
+
+ btv->tvnorm = norm;
+
+ btwrite(tvnorm->adelay, BT848_ADELAY);
+ btwrite(tvnorm->bdelay, BT848_BDELAY);
+ btaor(tvnorm->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH),
+ BT848_IFORM);
+ btwrite(tvnorm->vbipack, BT848_VBI_PACK_SIZE);
+ btwrite(1, BT848_VBI_PACK_DEL);
+ bt848A_set_timing(btv);
+
+ switch (btv->c.type) {
+ case BTTV_BOARD_VOODOOTV_FM:
+ case BTTV_BOARD_VOODOOTV_200:
+ bttv_tda9880_setnorm(btv, gpio_read());
+ break;
+ }
+ id = tvnorm->v4l2_id;
+ bttv_call_all(btv, core, s_std, id);
+
+ return 0;
+}
+
+/* Call with btv->lock down. */
+static void
+set_input(struct bttv *btv, unsigned int input, unsigned int norm)
+{
+ unsigned long flags;
+
+ btv->input = input;
+ if (irq_iswitch) {
+ spin_lock_irqsave(&btv->s_lock,flags);
+ if (btv->curr.frame_irq) {
+ /* active capture -> delayed input switch */
+ btv->new_input = input;
+ } else {
+ video_mux(btv,input);
+ }
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+ } else {
+ video_mux(btv,input);
+ }
+ audio_input(btv, (btv->tuner_type != TUNER_ABSENT && input == 0) ?
+ TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN);
+ set_tvnorm(btv, norm);
+}
+
+static void init_irqreg(struct bttv *btv)
+{
+ /* clear status */
+ btwrite(0xfffffUL, BT848_INT_STAT);
+
+ if (bttv_tvcards[btv->c.type].no_video) {
+ /* i2c only */
+ btwrite(BT848_INT_I2CDONE,
+ BT848_INT_MASK);
+ } else {
+ /* full video */
+ btwrite((btv->triton1) |
+ (btv->gpioirq ? BT848_INT_GPINT : 0) |
+ BT848_INT_SCERR |
+ (fdsr ? BT848_INT_FDSR : 0) |
+ BT848_INT_RISCI | BT848_INT_OCERR |
+ BT848_INT_FMTCHG|BT848_INT_HLOCK|
+ BT848_INT_I2CDONE,
+ BT848_INT_MASK);
+ }
+}
+
+static void init_bt848(struct bttv *btv)
+{
+ int val;
+
+ if (bttv_tvcards[btv->c.type].no_video) {
+ /* very basic init only */
+ init_irqreg(btv);
+ return;
+ }
+
+ btwrite(0x00, BT848_CAP_CTL);
+ btwrite(BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL);
+ btwrite(BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, BT848_IFORM);
+
+ /* set planar and packed mode trigger points and */
+ /* set rising edge of inverted GPINTR pin as irq trigger */
+ btwrite(BT848_GPIO_DMA_CTL_PKTP_32|
+ BT848_GPIO_DMA_CTL_PLTP1_16|
+ BT848_GPIO_DMA_CTL_PLTP23_16|
+ BT848_GPIO_DMA_CTL_GPINTC|
+ BT848_GPIO_DMA_CTL_GPINTI,
+ BT848_GPIO_DMA_CTL);
+
+ val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
+ btwrite(val, BT848_E_SCLOOP);
+ btwrite(val, BT848_O_SCLOOP);
+
+ btwrite(0x20, BT848_E_VSCALE_HI);
+ btwrite(0x20, BT848_O_VSCALE_HI);
+ btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+ BT848_ADC);
+
+ btwrite(whitecrush_upper, BT848_WC_UP);
+ btwrite(whitecrush_lower, BT848_WC_DOWN);
+
+ if (btv->opt_lumafilter) {
+ btwrite(0, BT848_E_CONTROL);
+ btwrite(0, BT848_O_CONTROL);
+ } else {
+ btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+ btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+ }
+
+ bt848_bright(btv, btv->bright);
+ bt848_hue(btv, btv->hue);
+ bt848_contrast(btv, btv->contrast);
+ bt848_sat(btv, btv->saturation);
+
+ /* interrupt */
+ init_irqreg(btv);
+}
+
+static void bttv_reinit_bt848(struct bttv *btv)
+{
+ unsigned long flags;
+
+ if (bttv_verbose)
+ pr_info("%d: reset, reinitialize\n", btv->c.nr);
+ spin_lock_irqsave(&btv->s_lock,flags);
+ btv->errors=0;
+ bttv_set_dma(btv,0);
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+
+ init_bt848(btv);
+ btv->pll.pll_current = -1;
+ set_input(btv, btv->input, btv->tvnorm);
+}
+
+static int bttv_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *c)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->value = btv->bright;
+ break;
+ case V4L2_CID_HUE:
+ c->value = btv->hue;
+ break;
+ case V4L2_CID_CONTRAST:
+ c->value = btv->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ c->value = btv->saturation;
+ break;
+
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ bttv_call_all(btv, core, g_ctrl, c);
+ break;
+
+ case V4L2_CID_PRIVATE_CHROMA_AGC:
+ c->value = btv->opt_chroma_agc;
+ break;
+ case V4L2_CID_PRIVATE_COMBFILTER:
+ c->value = btv->opt_combfilter;
+ break;
+ case V4L2_CID_PRIVATE_LUMAFILTER:
+ c->value = btv->opt_lumafilter;
+ break;
+ case V4L2_CID_PRIVATE_AUTOMUTE:
+ c->value = btv->opt_automute;
+ break;
+ case V4L2_CID_PRIVATE_AGC_CRUSH:
+ c->value = btv->opt_adc_crush;
+ break;
+ case V4L2_CID_PRIVATE_VCR_HACK:
+ c->value = btv->opt_vcr_hack;
+ break;
+ case V4L2_CID_PRIVATE_WHITECRUSH_UPPER:
+ c->value = btv->opt_whitecrush_upper;
+ break;
+ case V4L2_CID_PRIVATE_WHITECRUSH_LOWER:
+ c->value = btv->opt_whitecrush_lower;
+ break;
+ case V4L2_CID_PRIVATE_UV_RATIO:
+ c->value = btv->opt_uv_ratio;
+ break;
+ case V4L2_CID_PRIVATE_FULL_LUMA_RANGE:
+ c->value = btv->opt_full_luma_range;
+ break;
+ case V4L2_CID_PRIVATE_CORING:
+ c->value = btv->opt_coring;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int bttv_s_ctrl(struct file *file, void *f,
+ struct v4l2_control *c)
+{
+ int err;
+ int val;
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ err = v4l2_prio_check(&btv->prio, fh->prio);
+ if (0 != err)
+ return err;
+
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ bt848_bright(btv, c->value);
+ break;
+ case V4L2_CID_HUE:
+ bt848_hue(btv, c->value);
+ break;
+ case V4L2_CID_CONTRAST:
+ bt848_contrast(btv, c->value);
+ break;
+ case V4L2_CID_SATURATION:
+ bt848_sat(btv, c->value);
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ audio_mute(btv, c->value);
+ /* fall through */
+ case V4L2_CID_AUDIO_VOLUME:
+ if (btv->volume_gpio)
+ btv->volume_gpio(btv, c->value);
+
+ bttv_call_all(btv, core, s_ctrl, c);
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ bttv_call_all(btv, core, s_ctrl, c);
+ break;
+
+ case V4L2_CID_PRIVATE_CHROMA_AGC:
+ btv->opt_chroma_agc = c->value;
+ val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
+ btwrite(val, BT848_E_SCLOOP);
+ btwrite(val, BT848_O_SCLOOP);
+ break;
+ case V4L2_CID_PRIVATE_COMBFILTER:
+ btv->opt_combfilter = c->value;
+ break;
+ case V4L2_CID_PRIVATE_LUMAFILTER:
+ btv->opt_lumafilter = c->value;
+ if (btv->opt_lumafilter) {
+ btand(~BT848_CONTROL_LDEC, BT848_E_CONTROL);
+ btand(~BT848_CONTROL_LDEC, BT848_O_CONTROL);
+ } else {
+ btor(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+ btor(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+ }
+ break;
+ case V4L2_CID_PRIVATE_AUTOMUTE:
+ btv->opt_automute = c->value;
+ break;
+ case V4L2_CID_PRIVATE_AGC_CRUSH:
+ btv->opt_adc_crush = c->value;
+ btwrite(BT848_ADC_RESERVED |
+ (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+ BT848_ADC);
+ break;
+ case V4L2_CID_PRIVATE_VCR_HACK:
+ btv->opt_vcr_hack = c->value;
+ break;
+ case V4L2_CID_PRIVATE_WHITECRUSH_UPPER:
+ btv->opt_whitecrush_upper = c->value;
+ btwrite(c->value, BT848_WC_UP);
+ break;
+ case V4L2_CID_PRIVATE_WHITECRUSH_LOWER:
+ btv->opt_whitecrush_lower = c->value;
+ btwrite(c->value, BT848_WC_DOWN);
+ break;
+ case V4L2_CID_PRIVATE_UV_RATIO:
+ btv->opt_uv_ratio = c->value;
+ bt848_sat(btv, btv->saturation);
+ break;
+ case V4L2_CID_PRIVATE_FULL_LUMA_RANGE:
+ btv->opt_full_luma_range = c->value;
+ btaor((c->value<<7), ~BT848_OFORM_RANGE, BT848_OFORM);
+ break;
+ case V4L2_CID_PRIVATE_CORING:
+ btv->opt_coring = c->value;
+ btaor((c->value<<5), ~BT848_OFORM_CORE32, BT848_OFORM);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+void bttv_gpio_tracking(struct bttv *btv, char *comment)
+{
+ unsigned int outbits, data;
+ outbits = btread(BT848_GPIO_OUT_EN);
+ data = btread(BT848_GPIO_DATA);
+ pr_debug("%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
+ btv->c.nr, outbits, data & outbits, data & ~outbits, comment);
+}
+
+static void bttv_field_count(struct bttv *btv)
+{
+ int need_count = 0;
+
+ if (btv->users)
+ need_count++;
+
+ if (need_count) {
+ /* start field counter */
+ btor(BT848_INT_VSYNC,BT848_INT_MASK);
+ } else {
+ /* stop field counter */
+ btand(~BT848_INT_VSYNC,BT848_INT_MASK);
+ btv->field_count = 0;
+ }
+}
+
+static const struct bttv_format*
+format_by_fourcc(int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < FORMATS; i++) {
+ if (-1 == formats[i].fourcc)
+ continue;
+ if (formats[i].fourcc == fourcc)
+ return formats+i;
+ }
+ return NULL;
+}
+
+/* ----------------------------------------------------------------------- */
+/* misc helpers */
+
+static int
+bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
+ struct bttv_buffer *new)
+{
+ struct bttv_buffer *old;
+ unsigned long flags;
+ int retval = 0;
+
+ dprintk("switch_overlay: enter [new=%p]\n", new);
+ if (new)
+ new->vb.state = VIDEOBUF_DONE;
+ spin_lock_irqsave(&btv->s_lock,flags);
+ old = btv->screen;
+ btv->screen = new;
+ btv->loop_irq |= 1;
+ bttv_set_dma(btv, 0x03);
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+ if (NULL != old) {
+ dprintk("switch_overlay: old=%p state is %d\n",
+ old, old->vb.state);
+ bttv_dma_free(&fh->cap,btv, old);
+ kfree(old);
+ }
+ if (NULL == new)
+ free_btres_lock(btv,fh,RESOURCE_OVERLAY);
+ dprintk("switch_overlay: done\n");
+ return retval;
+}
+
+/* ----------------------------------------------------------------------- */
+/* video4linux (1) interface */
+
+static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv,
+ struct bttv_buffer *buf,
+ const struct bttv_format *fmt,
+ unsigned int width, unsigned int height,
+ enum v4l2_field field)
+{
+ struct bttv_fh *fh = q->priv_data;
+ int redo_dma_risc = 0;
+ struct bttv_crop c;
+ int norm;
+ int rc;
+
+ /* check settings */
+ if (NULL == fmt)
+ return -EINVAL;
+ if (fmt->btformat == BT848_COLOR_FMT_RAW) {
+ width = RAW_BPL;
+ height = RAW_LINES*2;
+ if (width*height > buf->vb.bsize)
+ return -EINVAL;
+ buf->vb.size = buf->vb.bsize;
+
+ /* Make sure tvnorm and vbi_end remain consistent
+ until we're done. */
+
+ norm = btv->tvnorm;
+
+ /* In this mode capturing always starts at defrect.top
+ (default VDELAY), ignoring cropping parameters. */
+ if (btv->vbi_end > bttv_tvnorms[norm].cropcap.defrect.top) {
+ return -EINVAL;
+ }
+
+ c.rect = bttv_tvnorms[norm].cropcap.defrect;
+ } else {
+ norm = btv->tvnorm;
+ c = btv->crop[!!fh->do_crop];
+
+ if (width < c.min_scaled_width ||
+ width > c.max_scaled_width ||
+ height < c.min_scaled_height)
+ return -EINVAL;
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_ALTERNATE:
+ /* btv->crop counts frame lines. Max. scale
+ factor is 16:1 for frames, 8:1 for fields. */
+ if (height * 2 > c.max_scaled_height)
+ return -EINVAL;
+ break;
+
+ default:
+ if (height > c.max_scaled_height)
+ return -EINVAL;
+ break;
+ }
+
+ buf->vb.size = (width * height * fmt->depth) >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+ }
+
+ /* alloc + fill struct bttv_buffer (if changed) */
+ if (buf->vb.width != width || buf->vb.height != height ||
+ buf->vb.field != field ||
+ buf->tvnorm != norm || buf->fmt != fmt ||
+ buf->crop.top != c.rect.top ||
+ buf->crop.left != c.rect.left ||
+ buf->crop.width != c.rect.width ||
+ buf->crop.height != c.rect.height) {
+ buf->vb.width = width;
+ buf->vb.height = height;
+ buf->vb.field = field;
+ buf->tvnorm = norm;
+ buf->fmt = fmt;
+ buf->crop = c.rect;
+ redo_dma_risc = 1;
+ }
+
+ /* alloc risc memory */
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ redo_dma_risc = 1;
+ if (0 != (rc = videobuf_iolock(q,&buf->vb,&btv->fbuf)))
+ goto fail;
+ }
+
+ if (redo_dma_risc)
+ if (0 != (rc = bttv_buffer_risc(btv,buf)))
+ goto fail;
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+ fail:
+ bttv_dma_free(q,btv,buf);
+ return rc;
+}
+
+static int
+buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct bttv_fh *fh = q->priv_data;
+
+ *size = fh->fmt->depth*fh->width*fh->height >> 3;
+ if (0 == *count)
+ *count = gbuffers;
+ if (*size * *count > gbuffers * gbufsize)
+ *count = (gbuffers * gbufsize) / *size;
+ return 0;
+}
+
+static int
+buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+ struct bttv_fh *fh = q->priv_data;
+
+ return bttv_prepare_buffer(q,fh->btv, buf, fh->fmt,
+ fh->width, fh->height, field);
+}
+
+static void
+buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+ struct bttv_fh *fh = q->priv_data;
+ struct bttv *btv = fh->btv;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue,&btv->capture);
+ if (!btv->curr.frame_irq) {
+ btv->loop_irq |= 1;
+ bttv_set_dma(btv, 0x03);
+ }
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+ struct bttv_fh *fh = q->priv_data;
+
+ bttv_dma_free(q,fh->btv,buf);
+}
+
+static struct videobuf_queue_ops bttv_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+static int bttv_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ unsigned int i;
+ int err;
+
+ err = v4l2_prio_check(&btv->prio, fh->prio);
+ if (err)
+ goto err;
+
+ for (i = 0; i < BTTV_TVNORMS; i++)
+ if (*id & bttv_tvnorms[i].v4l2_id)
+ break;
+ if (i == BTTV_TVNORMS) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ set_tvnorm(btv, i);
+
+err:
+
+ return err;
+}
+
+static int bttv_querystd(struct file *file, void *f, v4l2_std_id *id)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML)
+ *id = V4L2_STD_625_50;
+ else
+ *id = V4L2_STD_525_60;
+ return 0;
+}
+
+static int bttv_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int rc = 0;
+
+ if (i->index >= bttv_tvcards[btv->c.type].video_inputs) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ i->audioset = 1;
+
+ if (btv->tuner_type != TUNER_ABSENT && i->index == 0) {
+ sprintf(i->name, "Television");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->tuner = 0;
+ } else if (i->index == btv->svhs) {
+ sprintf(i->name, "S-Video");
+ } else {
+ sprintf(i->name, "Composite%d", i->index);
+ }
+
+ if (i->index == btv->input) {
+ __u32 dstatus = btread(BT848_DSTATUS);
+ if (0 == (dstatus & BT848_DSTATUS_PRES))
+ i->status |= V4L2_IN_ST_NO_SIGNAL;
+ if (0 == (dstatus & BT848_DSTATUS_HLOC))
+ i->status |= V4L2_IN_ST_NO_H_LOCK;
+ }
+
+ i->std = BTTV_NORMS;
+
+err:
+
+ return rc;
+}
+
+static int bttv_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ *i = btv->input;
+
+ return 0;
+}
+
+static int bttv_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ int err;
+
+ err = v4l2_prio_check(&btv->prio, fh->prio);
+ if (unlikely(err))
+ goto err;
+
+ if (i > bttv_tvcards[btv->c.type].video_inputs) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ set_input(btv, i, btv->tvnorm);
+
+err:
+ return 0;
+}
+
+static int bttv_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int err;
+
+ if (unlikely(0 != t->index))
+ return -EINVAL;
+
+ if (unlikely(btv->tuner_type == TUNER_ABSENT)) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ err = v4l2_prio_check(&btv->prio, fh->prio);
+ if (unlikely(err))
+ goto err;
+
+ bttv_call_all(btv, tuner, s_tuner, t);
+
+ if (btv->audio_mode_gpio)
+ btv->audio_mode_gpio(btv, t, 1);
+
+err:
+
+ return 0;
+}
+
+static int bttv_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ f->type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = btv->freq;
+
+ return 0;
+}
+
+static int bttv_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int err;
+
+ if (unlikely(f->tuner != 0))
+ return -EINVAL;
+
+ err = v4l2_prio_check(&btv->prio, fh->prio);
+ if (unlikely(err))
+ goto err;
+
+ if (unlikely(f->type != (btv->radio_user
+ ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV))) {
+ err = -EINVAL;
+ goto err;
+ }
+ btv->freq = f->frequency;
+ bttv_call_all(btv, tuner, s_frequency, f);
+ if (btv->has_matchbox && btv->radio_user)
+ tea5757_set_freq(btv, btv->freq);
+err:
+
+ return 0;
+}
+
+static int bttv_log_status(struct file *file, void *f)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ bttv_call_all(btv, core, log_status);
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int bttv_g_register(struct file *file, void *f,
+ struct v4l2_dbg_register *reg)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+
+ /* bt848 has a 12-bit register space */
+ reg->reg &= 0xfff;
+ reg->val = btread(reg->reg);
+ reg->size = 1;
+
+ return 0;
+}
+
+static int bttv_s_register(struct file *file, void *f,
+ struct v4l2_dbg_register *reg)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+
+ /* bt848 has a 12-bit register space */
+ reg->reg &= 0xfff;
+ btwrite(reg->val, reg->reg);
+
+ return 0;
+}
+#endif
+
+/* Given cropping boundaries b and the scaled width and height of a
+ single field or frame, which must not exceed hardware limits, this
+ function adjusts the cropping parameters c. */
+static void
+bttv_crop_adjust (struct bttv_crop * c,
+ const struct v4l2_rect * b,
+ __s32 width,
+ __s32 height,
+ enum v4l2_field field)
+{
+ __s32 frame_height = height << !V4L2_FIELD_HAS_BOTH(field);
+ __s32 max_left;
+ __s32 max_top;
+
+ if (width < c->min_scaled_width) {
+ /* Max. hor. scale factor 16:1. */
+ c->rect.width = width * 16;
+ } else if (width > c->max_scaled_width) {
+ /* Min. hor. scale factor 1:1. */
+ c->rect.width = width;
+
+ max_left = b->left + b->width - width;
+ max_left = min(max_left, (__s32) MAX_HDELAY);
+ if (c->rect.left > max_left)
+ c->rect.left = max_left;
+ }
+
+ if (height < c->min_scaled_height) {
+ /* Max. vert. scale factor 16:1, single fields 8:1. */
+ c->rect.height = height * 16;
+ } else if (frame_height > c->max_scaled_height) {
+ /* Min. vert. scale factor 1:1.
+ Top and height count field lines times two. */
+ c->rect.height = (frame_height + 1) & ~1;
+
+ max_top = b->top + b->height - c->rect.height;
+ if (c->rect.top > max_top)
+ c->rect.top = max_top;
+ }
+
+ bttv_crop_calc_limits(c);
+}
+
+/* Returns an error if scaling to a frame or single field with the given
+ width and height is not possible with the current cropping parameters
+ and width aligned according to width_mask. If adjust_size is TRUE the
+ function may adjust the width and/or height instead, rounding width
+ to (width + width_bias) & width_mask. If adjust_crop is TRUE it may
+ also adjust the current cropping parameters to get closer to the
+ desired image size. */
+static int
+limit_scaled_size_lock (struct bttv_fh * fh,
+ __s32 * width,
+ __s32 * height,
+ enum v4l2_field field,
+ unsigned int width_mask,
+ unsigned int width_bias,
+ int adjust_size,
+ int adjust_crop)
+{
+ struct bttv *btv = fh->btv;
+ const struct v4l2_rect *b;
+ struct bttv_crop *c;
+ __s32 min_width;
+ __s32 min_height;
+ __s32 max_width;
+ __s32 max_height;
+ int rc;
+
+ BUG_ON((int) width_mask >= 0 ||
+ width_bias >= (unsigned int) -width_mask);
+
+ /* Make sure tvnorm, vbi_end and the current cropping parameters
+ remain consistent until we're done. */
+
+ b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
+
+ /* Do crop - use current, don't - use default parameters. */
+ c = &btv->crop[!!fh->do_crop];
+
+ if (fh->do_crop
+ && adjust_size
+ && adjust_crop
+ && !locked_btres(btv, VIDEO_RESOURCES)) {
+ min_width = 48;
+ min_height = 32;
+
+ /* We cannot scale up. When the scaled image is larger
+ than crop.rect we adjust the crop.rect as required
+ by the V4L2 spec, hence cropcap.bounds are our limit. */
+ max_width = min(b->width, (__s32) MAX_HACTIVE);
+ max_height = b->height;
+
+ /* We cannot capture the same line as video and VBI data.
+ Note btv->vbi_end is really a minimum, see
+ bttv_vbi_try_fmt(). */
+ if (btv->vbi_end > b->top) {
+ max_height -= btv->vbi_end - b->top;
+ rc = -EBUSY;
+ if (min_height > max_height)
+ goto fail;
+ }
+ } else {
+ rc = -EBUSY;
+ if (btv->vbi_end > c->rect.top)
+ goto fail;
+
+ min_width = c->min_scaled_width;
+ min_height = c->min_scaled_height;
+ max_width = c->max_scaled_width;
+ max_height = c->max_scaled_height;
+
+ adjust_crop = 0;
+ }
+
+ min_width = (min_width - width_mask - 1) & width_mask;
+ max_width = max_width & width_mask;
+
+ /* Max. scale factor is 16:1 for frames, 8:1 for fields. */
+ min_height = min_height;
+ /* Min. scale factor is 1:1. */
+ max_height >>= !V4L2_FIELD_HAS_BOTH(field);
+
+ if (adjust_size) {
+ *width = clamp(*width, min_width, max_width);
+ *height = clamp(*height, min_height, max_height);
+
+ /* Round after clamping to avoid overflow. */
+ *width = (*width + width_bias) & width_mask;
+
+ if (adjust_crop) {
+ bttv_crop_adjust(c, b, *width, *height, field);
+
+ if (btv->vbi_end > c->rect.top) {
+ /* Move the crop window out of the way. */
+ c->rect.top = btv->vbi_end;
+ }
+ }
+ } else {
+ rc = -EINVAL;
+ if (*width < min_width ||
+ *height < min_height ||
+ *width > max_width ||
+ *height > max_height ||
+ 0 != (*width & ~width_mask))
+ goto fail;
+ }
+
+ rc = 0; /* success */
+
+ fail:
+
+ return rc;
+}
+
+/* Returns an error if the given overlay window dimensions are not
+ possible with the current cropping parameters. If adjust_size is
+ TRUE the function may adjust the window width and/or height
+ instead, however it always rounds the horizontal position and
+ width as btcx_align() does. If adjust_crop is TRUE the function
+ may also adjust the current cropping parameters to get closer
+ to the desired window size. */
+static int
+verify_window_lock (struct bttv_fh * fh,
+ struct v4l2_window * win,
+ int adjust_size,
+ int adjust_crop)
+{
+ enum v4l2_field field;
+ unsigned int width_mask;
+ int rc;
+
+ if (win->w.width < 48 || win->w.height < 32)
+ return -EINVAL;
+ if (win->clipcount > 2048)
+ return -EINVAL;
+
+ field = win->field;
+
+ if (V4L2_FIELD_ANY == field) {
+ __s32 height2;
+
+ height2 = fh->btv->crop[!!fh->do_crop].rect.height >> 1;
+ field = (win->w.height > height2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_TOP;
+ }
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* 4-byte alignment. */
+ if (NULL == fh->ovfmt)
+ return -EINVAL;
+ width_mask = ~0;
+ switch (fh->ovfmt->depth) {
+ case 8:
+ case 24:
+ width_mask = ~3;
+ break;
+ case 16:
+ width_mask = ~1;
+ break;
+ case 32:
+ break;
+ default:
+ BUG();
+ }
+
+ win->w.width -= win->w.left & ~width_mask;
+ win->w.left = (win->w.left - width_mask - 1) & width_mask;
+
+ rc = limit_scaled_size_lock(fh, &win->w.width, &win->w.height,
+ field, width_mask,
+ /* width_bias: round down */ 0,
+ adjust_size, adjust_crop);
+ if (0 != rc)
+ return rc;
+
+ win->field = field;
+ return 0;
+}
+
+static int setup_window_lock(struct bttv_fh *fh, struct bttv *btv,
+ struct v4l2_window *win, int fixup)
+{
+ struct v4l2_clip *clips = NULL;
+ int n,size,retval = 0;
+
+ if (NULL == fh->ovfmt)
+ return -EINVAL;
+ if (!(fh->ovfmt->flags & FORMAT_FLAGS_PACKED))
+ return -EINVAL;
+ retval = verify_window_lock(fh, win,
+ /* adjust_size */ fixup,
+ /* adjust_crop */ fixup);
+ if (0 != retval)
+ return retval;
+
+ /* copy clips -- luckily v4l1 + v4l2 are binary
+ compatible here ...*/
+ n = win->clipcount;
+ size = sizeof(*clips)*(n+4);
+ clips = kmalloc(size,GFP_KERNEL);
+ if (NULL == clips)
+ return -ENOMEM;
+ if (n > 0) {
+ if (copy_from_user(clips,win->clips,sizeof(struct v4l2_clip)*n)) {
+ kfree(clips);
+ return -EFAULT;
+ }
+ }
+
+ /* clip against screen */
+ if (NULL != btv->fbuf.base)
+ n = btcx_screen_clips(btv->fbuf.fmt.width, btv->fbuf.fmt.height,
+ &win->w, clips, n);
+ btcx_sort_clips(clips,n);
+
+ /* 4-byte alignments */
+ switch (fh->ovfmt->depth) {
+ case 8:
+ case 24:
+ btcx_align(&win->w, clips, n, 3);
+ break;
+ case 16:
+ btcx_align(&win->w, clips, n, 1);
+ break;
+ case 32:
+ /* no alignment fixups needed */
+ break;
+ default:
+ BUG();
+ }
+
+ kfree(fh->ov.clips);
+ fh->ov.clips = clips;
+ fh->ov.nclips = n;
+
+ fh->ov.w = win->w;
+ fh->ov.field = win->field;
+ fh->ov.setup_ok = 1;
+
+ btv->init.ov.w.width = win->w.width;
+ btv->init.ov.w.height = win->w.height;
+ btv->init.ov.field = win->field;
+
+ /* update overlay if needed */
+ retval = 0;
+ if (check_btres(fh, RESOURCE_OVERLAY)) {
+ struct bttv_buffer *new;
+
+ new = videobuf_sg_alloc(sizeof(*new));
+ new->crop = btv->crop[!!fh->do_crop].rect;
+ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+ retval = bttv_switch_overlay(btv,fh,new);
+ }
+ return retval;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct videobuf_queue* bttv_queue(struct bttv_fh *fh)
+{
+ struct videobuf_queue* q = NULL;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ q = &fh->cap;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ q = &fh->vbi;
+ break;
+ default:
+ BUG();
+ }
+ return q;
+}
+
+static int bttv_resource(struct bttv_fh *fh)
+{
+ int res = 0;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ res = RESOURCE_VIDEO_STREAM;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ res = RESOURCE_VBI;
+ break;
+ default:
+ BUG();
+ }
+ return res;
+}
+
+static int bttv_switch_type(struct bttv_fh *fh, enum v4l2_buf_type type)
+{
+ struct videobuf_queue *q = bttv_queue(fh);
+ int res = bttv_resource(fh);
+
+ if (check_btres(fh,res))
+ return -EBUSY;
+ if (videobuf_queue_is_busy(q))
+ return -EBUSY;
+ fh->type = type;
+ return 0;
+}
+
+static void
+pix_format_set_size (struct v4l2_pix_format * f,
+ const struct bttv_format * fmt,
+ unsigned int width,
+ unsigned int height)
+{
+ f->width = width;
+ f->height = height;
+
+ if (fmt->flags & FORMAT_FLAGS_PLANAR) {
+ f->bytesperline = width; /* Y plane */
+ f->sizeimage = (width * height * fmt->depth) >> 3;
+ } else {
+ f->bytesperline = (width * fmt->depth) >> 3;
+ f->sizeimage = height * f->bytesperline;
+ }
+}
+
+static int bttv_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct bttv_fh *fh = priv;
+
+ pix_format_set_size(&f->fmt.pix, fh->fmt,
+ fh->width, fh->height);
+ f->fmt.pix.field = fh->cap.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+
+ return 0;
+}
+
+static int bttv_g_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct bttv_fh *fh = priv;
+
+ f->fmt.win.w = fh->ov.w;
+ f->fmt.win.field = fh->ov.field;
+
+ return 0;
+}
+
+static int bttv_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ const struct bttv_format *fmt;
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ enum v4l2_field field;
+ __s32 width, height;
+ int rc;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+
+ if (V4L2_FIELD_ANY == field) {
+ __s32 height2;
+
+ height2 = btv->crop[!!fh->do_crop].rect.height >> 1;
+ field = (f->fmt.pix.height > height2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+
+ if (V4L2_FIELD_SEQ_BT == field)
+ field = V4L2_FIELD_SEQ_TB;
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_ALTERNATE:
+ case V4L2_FIELD_INTERLACED:
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ if (fmt->flags & FORMAT_FLAGS_PLANAR)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ width = f->fmt.pix.width;
+ height = f->fmt.pix.height;
+
+ rc = limit_scaled_size_lock(fh, &width, &height, field,
+ /* width_mask: 4 pixels */ ~3,
+ /* width_bias: nearest */ 2,
+ /* adjust_size */ 1,
+ /* adjust_crop */ 0);
+ if (0 != rc)
+ return rc;
+
+ /* update data for the application */
+ f->fmt.pix.field = field;
+ pix_format_set_size(&f->fmt.pix, fmt, width, height);
+
+ return 0;
+}
+
+static int bttv_try_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct bttv_fh *fh = priv;
+
+ return verify_window_lock(fh, &f->fmt.win,
+ /* adjust_size */ 1,
+ /* adjust_crop */ 0);
+}
+
+static int bttv_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int retval;
+ const struct bttv_format *fmt;
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ __s32 width, height;
+ enum v4l2_field field;
+
+ retval = bttv_switch_type(fh, f->type);
+ if (0 != retval)
+ return retval;
+
+ retval = bttv_try_fmt_vid_cap(file, priv, f);
+ if (0 != retval)
+ return retval;
+
+ width = f->fmt.pix.width;
+ height = f->fmt.pix.height;
+ field = f->fmt.pix.field;
+
+ retval = limit_scaled_size_lock(fh, &width, &height, f->fmt.pix.field,
+ /* width_mask: 4 pixels */ ~3,
+ /* width_bias: nearest */ 2,
+ /* adjust_size */ 1,
+ /* adjust_crop */ 1);
+ if (0 != retval)
+ return retval;
+
+ f->fmt.pix.field = field;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+
+ /* update our state informations */
+ fh->fmt = fmt;
+ fh->cap.field = f->fmt.pix.field;
+ fh->cap.last = V4L2_FIELD_NONE;
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ btv->init.fmt = fmt;
+ btv->init.width = f->fmt.pix.width;
+ btv->init.height = f->fmt.pix.height;
+
+ return 0;
+}
+
+static int bttv_s_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (no_overlay > 0) {
+ pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
+ }
+
+ return setup_window_lock(fh, btv, &f->fmt.win, 1);
+}
+
+static int bttv_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (0 == v4l2)
+ return -EINVAL;
+
+ strlcpy(cap->driver, "bttv", sizeof(cap->driver));
+ strlcpy(cap->card, btv->video_dev->name, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "PCI:%s", pci_name(btv->c.pci));
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VBI_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ if (no_overlay <= 0)
+ cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
+
+ /*
+ * No need to lock here: those vars are initialized during board
+ * probe and remains untouched during the rest of the driver lifecycle
+ */
+ if (btv->has_saa6588)
+ cap->capabilities |= V4L2_CAP_RDS_CAPTURE;
+ if (btv->tuner_type != TUNER_ABSENT)
+ cap->capabilities |= V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int bttv_enum_fmt_cap_ovr(struct v4l2_fmtdesc *f)
+{
+ int index = -1, i;
+
+ for (i = 0; i < FORMATS; i++) {
+ if (formats[i].fourcc != -1)
+ index++;
+ if ((unsigned int)index == f->index)
+ break;
+ }
+ if (FORMATS == i)
+ return -EINVAL;
+
+ f->pixelformat = formats[i].fourcc;
+ strlcpy(f->description, formats[i].name, sizeof(f->description));
+
+ return i;
+}
+
+static int bttv_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ int rc = bttv_enum_fmt_cap_ovr(f);
+
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static int bttv_enum_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ int rc;
+
+ if (no_overlay > 0) {
+ pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
+ }
+
+ rc = bttv_enum_fmt_cap_ovr(f);
+
+ if (rc < 0)
+ return rc;
+
+ if (!(formats[rc].flags & FORMAT_FLAGS_PACKED))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int bttv_g_fbuf(struct file *file, void *f,
+ struct v4l2_framebuffer *fb)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ *fb = btv->fbuf;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ if (fh->ovfmt)
+ fb->fmt.pixelformat = fh->ovfmt->fourcc;
+ return 0;
+}
+
+static int bttv_overlay(struct file *file, void *f, unsigned int on)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ struct bttv_buffer *new;
+ int retval = 0;
+
+ if (on) {
+ /* verify args */
+ if (unlikely(!btv->fbuf.base)) {
+ return -EINVAL;
+ }
+ if (unlikely(!fh->ov.setup_ok)) {
+ dprintk("%d: overlay: !setup_ok\n", btv->c.nr);
+ retval = -EINVAL;
+ }
+ if (retval)
+ return retval;
+ }
+
+ if (!check_alloc_btres_lock(btv, fh, RESOURCE_OVERLAY))
+ return -EBUSY;
+
+ if (on) {
+ fh->ov.tvnorm = btv->tvnorm;
+ new = videobuf_sg_alloc(sizeof(*new));
+ new->crop = btv->crop[!!fh->do_crop].rect;
+ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+ } else {
+ new = NULL;
+ }
+
+ /* switch over */
+ retval = bttv_switch_overlay(btv, fh, new);
+ return retval;
+}
+
+static int bttv_s_fbuf(struct file *file, void *f,
+ const struct v4l2_framebuffer *fb)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ const struct bttv_format *fmt;
+ int retval;
+
+ if (!capable(CAP_SYS_ADMIN) &&
+ !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* check args */
+ fmt = format_by_fourcc(fb->fmt.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+ if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
+ return -EINVAL;
+
+ retval = -EINVAL;
+ if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+ __s32 width = fb->fmt.width;
+ __s32 height = fb->fmt.height;
+
+ retval = limit_scaled_size_lock(fh, &width, &height,
+ V4L2_FIELD_INTERLACED,
+ /* width_mask */ ~3,
+ /* width_bias */ 2,
+ /* adjust_size */ 0,
+ /* adjust_crop */ 0);
+ if (0 != retval)
+ return retval;
+ }
+
+ /* ok, accept it */
+ btv->fbuf.base = fb->base;
+ btv->fbuf.fmt.width = fb->fmt.width;
+ btv->fbuf.fmt.height = fb->fmt.height;
+ if (0 != fb->fmt.bytesperline)
+ btv->fbuf.fmt.bytesperline = fb->fmt.bytesperline;
+ else
+ btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fmt->depth/8;
+
+ retval = 0;
+ fh->ovfmt = fmt;
+ btv->init.ovfmt = fmt;
+ if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+ fh->ov.w.left = 0;
+ fh->ov.w.top = 0;
+ fh->ov.w.width = fb->fmt.width;
+ fh->ov.w.height = fb->fmt.height;
+ btv->init.ov.w.width = fb->fmt.width;
+ btv->init.ov.w.height = fb->fmt.height;
+ kfree(fh->ov.clips);
+ fh->ov.clips = NULL;
+ fh->ov.nclips = 0;
+
+ if (check_btres(fh, RESOURCE_OVERLAY)) {
+ struct bttv_buffer *new;
+
+ new = videobuf_sg_alloc(sizeof(*new));
+ new->crop = btv->crop[!!fh->do_crop].rect;
+ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+ retval = bttv_switch_overlay(btv, fh, new);
+ }
+ }
+ return retval;
+}
+
+static int bttv_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct bttv_fh *fh = priv;
+ return videobuf_reqbufs(bttv_queue(fh), p);
+}
+
+static int bttv_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct bttv_fh *fh = priv;
+ return videobuf_querybuf(bttv_queue(fh), b);
+}
+
+static int bttv_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int res = bttv_resource(fh);
+
+ if (!check_alloc_btres_lock(btv, fh, res))
+ return -EBUSY;
+
+ return videobuf_qbuf(bttv_queue(fh), b);
+}
+
+static int bttv_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct bttv_fh *fh = priv;
+ return videobuf_dqbuf(bttv_queue(fh), b,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int bttv_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int res = bttv_resource(fh);
+
+ if (!check_alloc_btres_lock(btv, fh, res))
+ return -EBUSY;
+ return videobuf_streamon(bttv_queue(fh));
+}
+
+
+static int bttv_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int retval;
+ int res = bttv_resource(fh);
+
+
+ retval = videobuf_streamoff(bttv_queue(fh));
+ if (retval < 0)
+ return retval;
+ free_btres_lock(btv, fh, res);
+ return 0;
+}
+
+static int bttv_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ const struct v4l2_queryctrl *ctrl;
+
+ if ((c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1) &&
+ (c->id < V4L2_CID_PRIVATE_BASE ||
+ c->id >= V4L2_CID_PRIVATE_LASTP1))
+ return -EINVAL;
+
+ if (!btv->volume_gpio && (c->id == V4L2_CID_AUDIO_VOLUME))
+ *c = no_ctl;
+ else {
+ ctrl = ctrl_by_id(c->id);
+
+ *c = (NULL != ctrl) ? *ctrl : no_ctl;
+ }
+
+ return 0;
+}
+
+static int bttv_g_parm(struct file *file, void *f,
+ struct v4l2_streamparm *parm)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ v4l2_video_std_frame_period(bttv_tvnorms[btv->tvnorm].v4l2_id,
+ &parm->parm.capture.timeperframe);
+
+ return 0;
+}
+
+static int bttv_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (btv->tuner_type == TUNER_ABSENT)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ bttv_call_all(btv, tuner, g_tuner, t);
+ strcpy(t->name, "Television");
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->type = V4L2_TUNER_ANALOG_TV;
+ if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
+ t->signal = 0xffff;
+
+ if (btv->audio_mode_gpio)
+ btv->audio_mode_gpio(btv, t, 0);
+
+ return 0;
+}
+
+static int bttv_g_priority(struct file *file, void *f, enum v4l2_priority *p)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ *p = v4l2_prio_max(&btv->prio);
+
+ return 0;
+}
+
+static int bttv_s_priority(struct file *file, void *f,
+ enum v4l2_priority prio)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ int rc;
+
+ rc = v4l2_prio_change(&btv->prio, &fh->prio, prio);
+
+ return rc;
+}
+
+static int bttv_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cap)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+
+ *cap = bttv_tvnorms[btv->tvnorm].cropcap;
+
+ return 0;
+}
+
+static int bttv_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+
+ /* No fh->do_crop = 1; because btv->crop[1] may be
+ inconsistent with fh->width or fh->height and apps
+ do not expect a change here. */
+
+ crop->c = btv->crop[!!fh->do_crop].rect;
+
+ return 0;
+}
+
+static int bttv_s_crop(struct file *file, void *f, const struct v4l2_crop *crop)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ const struct v4l2_rect *b;
+ int retval;
+ struct bttv_crop c;
+ __s32 b_left;
+ __s32 b_top;
+ __s32 b_right;
+ __s32 b_bottom;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+
+ /* Make sure tvnorm, vbi_end and the current cropping
+ parameters remain consistent until we're done. Note
+ read() may change vbi_end in check_alloc_btres_lock(). */
+ retval = v4l2_prio_check(&btv->prio, fh->prio);
+ if (0 != retval) {
+ return retval;
+ }
+
+ retval = -EBUSY;
+
+ if (locked_btres(fh->btv, VIDEO_RESOURCES)) {
+ return retval;
+ }
+
+ b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
+
+ b_left = b->left;
+ b_right = b_left + b->width;
+ b_bottom = b->top + b->height;
+
+ b_top = max(b->top, btv->vbi_end);
+ if (b_top + 32 >= b_bottom) {
+ return retval;
+ }
+
+ /* Min. scaled size 48 x 32. */
+ c.rect.left = clamp_t(s32, crop->c.left, b_left, b_right - 48);
+ c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY);
+
+ c.rect.width = clamp_t(s32, crop->c.width,
+ 48, b_right - c.rect.left);
+
+ c.rect.top = clamp_t(s32, crop->c.top, b_top, b_bottom - 32);
+ /* Top and height must be a multiple of two. */
+ c.rect.top = (c.rect.top + 1) & ~1;
+
+ c.rect.height = clamp_t(s32, crop->c.height,
+ 32, b_bottom - c.rect.top);
+ c.rect.height = (c.rect.height + 1) & ~1;
+
+ bttv_crop_calc_limits(&c);
+
+ btv->crop[1] = c;
+
+ fh->do_crop = 1;
+
+ if (fh->width < c.min_scaled_width) {
+ fh->width = c.min_scaled_width;
+ btv->init.width = c.min_scaled_width;
+ } else if (fh->width > c.max_scaled_width) {
+ fh->width = c.max_scaled_width;
+ btv->init.width = c.max_scaled_width;
+ }
+
+ if (fh->height < c.min_scaled_height) {
+ fh->height = c.min_scaled_height;
+ btv->init.height = c.min_scaled_height;
+ } else if (fh->height > c.max_scaled_height) {
+ fh->height = c.max_scaled_height;
+ btv->init.height = c.max_scaled_height;
+ }
+
+ return 0;
+}
+
+static int bttv_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ strcpy(a->name, "audio");
+ return 0;
+}
+
+static int bttv_s_audio(struct file *file, void *priv, const struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ return 0;
+}
+
+static ssize_t bttv_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct bttv_fh *fh = file->private_data;
+ int retval = 0;
+
+ if (fh->btv->errors)
+ bttv_reinit_bt848(fh->btv);
+ dprintk("%d: read count=%d type=%s\n",
+ fh->btv->c.nr, (int)count, v4l2_type_names[fh->type]);
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (!check_alloc_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ)) {
+ /* VIDEO_READ in use by another fh,
+ or VIDEO_STREAM by any fh. */
+ return -EBUSY;
+ }
+ retval = videobuf_read_one(&fh->cap, data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ free_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ);
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI))
+ return -EBUSY;
+ retval = videobuf_read_stream(&fh->vbi, data, count, ppos, 1,
+ file->f_flags & O_NONBLOCK);
+ break;
+ default:
+ BUG();
+ }
+ return retval;
+}
+
+static unsigned int bttv_poll(struct file *file, poll_table *wait)
+{
+ struct bttv_fh *fh = file->private_data;
+ struct bttv_buffer *buf;
+ enum v4l2_field field;
+ unsigned int rc = POLLERR;
+
+ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+ if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI))
+ return POLLERR;
+ return videobuf_poll_stream(file, &fh->vbi, wait);
+ }
+
+ if (check_btres(fh,RESOURCE_VIDEO_STREAM)) {
+ /* streaming capture */
+ if (list_empty(&fh->cap.stream))
+ goto err;
+ buf = list_entry(fh->cap.stream.next,struct bttv_buffer,vb.stream);
+ } else {
+ /* read() capture */
+ if (NULL == fh->cap.read_buf) {
+ /* need to capture a new frame */
+ if (locked_btres(fh->btv,RESOURCE_VIDEO_STREAM))
+ goto err;
+ fh->cap.read_buf = videobuf_sg_alloc(fh->cap.msize);
+ if (NULL == fh->cap.read_buf)
+ goto err;
+ fh->cap.read_buf->memory = V4L2_MEMORY_USERPTR;
+ field = videobuf_next_field(&fh->cap);
+ if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,field)) {
+ kfree (fh->cap.read_buf);
+ fh->cap.read_buf = NULL;
+ goto err;
+ }
+ fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf);
+ fh->cap.read_off = 0;
+ }
+ buf = (struct bttv_buffer*)fh->cap.read_buf;
+ }
+
+ poll_wait(file, &buf->vb.done, wait);
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ rc = POLLIN|POLLRDNORM;
+ else
+ rc = 0;
+err:
+ return rc;
+}
+
+static int bttv_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct bttv *btv = video_drvdata(file);
+ struct bttv_fh *fh;
+ enum v4l2_buf_type type = 0;
+
+ dprintk("open dev=%s\n", video_device_node_name(vdev));
+
+ if (vdev->vfl_type == VFL_TYPE_GRABBER) {
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ } else if (vdev->vfl_type == VFL_TYPE_VBI) {
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ } else {
+ WARN_ON(1);
+ return -ENODEV;
+ }
+
+ dprintk("%d: open called (type=%s)\n",
+ btv->c.nr, v4l2_type_names[type]);
+
+ /* allocate per filehandle data */
+ fh = kmalloc(sizeof(*fh), GFP_KERNEL);
+ if (unlikely(!fh))
+ return -ENOMEM;
+ file->private_data = fh;
+
+ *fh = btv->init;
+
+ fh->type = type;
+ fh->ov.setup_ok = 0;
+
+ v4l2_prio_open(&btv->prio, &fh->prio);
+
+ videobuf_queue_sg_init(&fh->cap, &bttv_video_qops,
+ &btv->c.pci->dev, &btv->s_lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct bttv_buffer),
+ fh, &btv->lock);
+ videobuf_queue_sg_init(&fh->vbi, &bttv_vbi_qops,
+ &btv->c.pci->dev, &btv->s_lock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct bttv_buffer),
+ fh, &btv->lock);
+ set_tvnorm(btv,btv->tvnorm);
+ set_input(btv, btv->input, btv->tvnorm);
+
+ btv->users++;
+
+ /* The V4L2 spec requires one global set of cropping parameters
+ which only change on request. These are stored in btv->crop[1].
+ However for compatibility with V4L apps and cropping unaware
+ V4L2 apps we now reset the cropping parameters as seen through
+ this fh, which is to say VIDIOC_G_CROP and scaling limit checks
+ will use btv->crop[0], the default cropping parameters for the
+ current video standard, and VIDIOC_S_FMT will not implicitely
+ change the cropping parameters until VIDIOC_S_CROP has been
+ called. */
+ fh->do_crop = !reset_crop; /* module parameter */
+
+ /* Likewise there should be one global set of VBI capture
+ parameters, but for compatibility with V4L apps and earlier
+ driver versions each fh has its own parameters. */
+ bttv_vbi_fmt_reset(&fh->vbi_fmt, btv->tvnorm);
+
+ bttv_field_count(btv);
+ return 0;
+}
+
+static int bttv_release(struct file *file)
+{
+ struct bttv_fh *fh = file->private_data;
+ struct bttv *btv = fh->btv;
+
+ /* turn off overlay */
+ if (check_btres(fh, RESOURCE_OVERLAY))
+ bttv_switch_overlay(btv,fh,NULL);
+
+ /* stop video capture */
+ if (check_btres(fh, RESOURCE_VIDEO_STREAM)) {
+ videobuf_streamoff(&fh->cap);
+ free_btres_lock(btv,fh,RESOURCE_VIDEO_STREAM);
+ }
+ if (fh->cap.read_buf) {
+ buffer_release(&fh->cap,fh->cap.read_buf);
+ kfree(fh->cap.read_buf);
+ }
+ if (check_btres(fh, RESOURCE_VIDEO_READ)) {
+ free_btres_lock(btv, fh, RESOURCE_VIDEO_READ);
+ }
+
+ /* stop vbi capture */
+ if (check_btres(fh, RESOURCE_VBI)) {
+ videobuf_stop(&fh->vbi);
+ free_btres_lock(btv,fh,RESOURCE_VBI);
+ }
+
+ /* free stuff */
+
+ videobuf_mmap_free(&fh->cap);
+ videobuf_mmap_free(&fh->vbi);
+ v4l2_prio_close(&btv->prio, fh->prio);
+ file->private_data = NULL;
+ kfree(fh);
+
+ btv->users--;
+ bttv_field_count(btv);
+
+ if (!btv->users)
+ audio_mute(btv, 1);
+
+ return 0;
+}
+
+static int
+bttv_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct bttv_fh *fh = file->private_data;
+
+ dprintk("%d: mmap type=%s 0x%lx+%ld\n",
+ fh->btv->c.nr, v4l2_type_names[fh->type],
+ vma->vm_start, vma->vm_end - vma->vm_start);
+ return videobuf_mmap_mapper(bttv_queue(fh),vma);
+}
+
+static const struct v4l2_file_operations bttv_fops =
+{
+ .owner = THIS_MODULE,
+ .open = bttv_open,
+ .release = bttv_release,
+ .unlocked_ioctl = video_ioctl2,
+ .read = bttv_read,
+ .mmap = bttv_mmap,
+ .poll = bttv_poll,
+};
+
+static const struct v4l2_ioctl_ops bttv_ioctl_ops = {
+ .vidioc_querycap = bttv_querycap,
+ .vidioc_enum_fmt_vid_cap = bttv_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = bttv_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = bttv_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = bttv_s_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_overlay = bttv_enum_fmt_vid_overlay,
+ .vidioc_g_fmt_vid_overlay = bttv_g_fmt_vid_overlay,
+ .vidioc_try_fmt_vid_overlay = bttv_try_fmt_vid_overlay,
+ .vidioc_s_fmt_vid_overlay = bttv_s_fmt_vid_overlay,
+ .vidioc_g_fmt_vbi_cap = bttv_g_fmt_vbi_cap,
+ .vidioc_try_fmt_vbi_cap = bttv_try_fmt_vbi_cap,
+ .vidioc_s_fmt_vbi_cap = bttv_s_fmt_vbi_cap,
+ .vidioc_g_audio = bttv_g_audio,
+ .vidioc_s_audio = bttv_s_audio,
+ .vidioc_cropcap = bttv_cropcap,
+ .vidioc_reqbufs = bttv_reqbufs,
+ .vidioc_querybuf = bttv_querybuf,
+ .vidioc_qbuf = bttv_qbuf,
+ .vidioc_dqbuf = bttv_dqbuf,
+ .vidioc_s_std = bttv_s_std,
+ .vidioc_enum_input = bttv_enum_input,
+ .vidioc_g_input = bttv_g_input,
+ .vidioc_s_input = bttv_s_input,
+ .vidioc_queryctrl = bttv_queryctrl,
+ .vidioc_g_ctrl = bttv_g_ctrl,
+ .vidioc_s_ctrl = bttv_s_ctrl,
+ .vidioc_streamon = bttv_streamon,
+ .vidioc_streamoff = bttv_streamoff,
+ .vidioc_g_tuner = bttv_g_tuner,
+ .vidioc_s_tuner = bttv_s_tuner,
+ .vidioc_g_crop = bttv_g_crop,
+ .vidioc_s_crop = bttv_s_crop,
+ .vidioc_g_fbuf = bttv_g_fbuf,
+ .vidioc_s_fbuf = bttv_s_fbuf,
+ .vidioc_overlay = bttv_overlay,
+ .vidioc_g_priority = bttv_g_priority,
+ .vidioc_s_priority = bttv_s_priority,
+ .vidioc_g_parm = bttv_g_parm,
+ .vidioc_g_frequency = bttv_g_frequency,
+ .vidioc_s_frequency = bttv_s_frequency,
+ .vidioc_log_status = bttv_log_status,
+ .vidioc_querystd = bttv_querystd,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = bttv_g_register,
+ .vidioc_s_register = bttv_s_register,
+#endif
+};
+
+static struct video_device bttv_video_template = {
+ .fops = &bttv_fops,
+ .ioctl_ops = &bttv_ioctl_ops,
+ .tvnorms = BTTV_NORMS,
+ .current_norm = V4L2_STD_PAL,
+};
+
+/* ----------------------------------------------------------------------- */
+/* radio interface */
+
+static int radio_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct bttv *btv = video_drvdata(file);
+ struct bttv_fh *fh;
+
+ dprintk("open dev=%s\n", video_device_node_name(vdev));
+
+ dprintk("%d: open called (radio)\n", btv->c.nr);
+
+ /* allocate per filehandle data */
+ fh = kmalloc(sizeof(*fh), GFP_KERNEL);
+ if (unlikely(!fh))
+ return -ENOMEM;
+ file->private_data = fh;
+ *fh = btv->init;
+
+ v4l2_prio_open(&btv->prio, &fh->prio);
+
+ btv->radio_user++;
+
+ bttv_call_all(btv, tuner, s_radio);
+ audio_input(btv,TVAUDIO_INPUT_RADIO);
+
+ return 0;
+}
+
+static int radio_release(struct file *file)
+{
+ struct bttv_fh *fh = file->private_data;
+ struct bttv *btv = fh->btv;
+ struct saa6588_command cmd;
+
+ v4l2_prio_close(&btv->prio, fh->prio);
+ file->private_data = NULL;
+ kfree(fh);
+
+ btv->radio_user--;
+
+ bttv_call_all(btv, core, ioctl, SAA6588_CMD_CLOSE, &cmd);
+
+ return 0;
+}
+
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ strcpy(cap->driver, "bttv");
+ strlcpy(cap->card, btv->radio_dev->name, sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(btv->c.pci));
+ cap->capabilities = V4L2_CAP_TUNER;
+
+ return 0;
+}
+
+static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (btv->tuner_type == TUNER_ABSENT)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+ bttv_call_all(btv, tuner, g_tuner, t);
+
+ if (btv->audio_mode_gpio)
+ btv->audio_mode_gpio(btv, t, 0);
+
+ return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+
+ return 0;
+}
+
+static int radio_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ bttv_call_all(btv, tuner, s_tuner, t);
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *priv,
+ const struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int radio_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (unlikely(i))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ const struct v4l2_queryctrl *ctrl;
+
+ if (c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+
+ if (c->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl = ctrl_by_id(c->id);
+ *c = *ctrl;
+ } else
+ *c = no_ctl;
+
+ return 0;
+}
+
+static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static ssize_t radio_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct bttv_fh *fh = file->private_data;
+ struct bttv *btv = fh->btv;
+ struct saa6588_command cmd;
+ cmd.block_count = count/3;
+ cmd.buffer = data;
+ cmd.instance = file;
+ cmd.result = -ENODEV;
+
+ bttv_call_all(btv, core, ioctl, SAA6588_CMD_READ, &cmd);
+
+ return cmd.result;
+}
+
+static unsigned int radio_poll(struct file *file, poll_table *wait)
+{
+ struct bttv_fh *fh = file->private_data;
+ struct bttv *btv = fh->btv;
+ struct saa6588_command cmd;
+ cmd.instance = file;
+ cmd.event_list = wait;
+ cmd.result = -ENODEV;
+ bttv_call_all(btv, core, ioctl, SAA6588_CMD_POLL, &cmd);
+
+ return cmd.result;
+}
+
+static const struct v4l2_file_operations radio_fops =
+{
+ .owner = THIS_MODULE,
+ .open = radio_open,
+ .read = radio_read,
+ .release = radio_release,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = radio_poll,
+};
+
+static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_s_std = radio_s_std,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_input = radio_g_input,
+ .vidioc_g_ctrl = bttv_g_ctrl,
+ .vidioc_s_ctrl = bttv_s_ctrl,
+ .vidioc_g_frequency = bttv_g_frequency,
+ .vidioc_s_frequency = bttv_s_frequency,
+};
+
+static struct video_device radio_template = {
+ .fops = &radio_fops,
+ .ioctl_ops = &radio_ioctl_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+/* some debug code */
+
+static int bttv_risc_decode(u32 risc)
+{
+ static char *instr[16] = {
+ [ BT848_RISC_WRITE >> 28 ] = "write",
+ [ BT848_RISC_SKIP >> 28 ] = "skip",
+ [ BT848_RISC_WRITEC >> 28 ] = "writec",
+ [ BT848_RISC_JUMP >> 28 ] = "jump",
+ [ BT848_RISC_SYNC >> 28 ] = "sync",
+ [ BT848_RISC_WRITE123 >> 28 ] = "write123",
+ [ BT848_RISC_SKIP123 >> 28 ] = "skip123",
+ [ BT848_RISC_WRITE1S23 >> 28 ] = "write1s23",
+ };
+ static int incr[16] = {
+ [ BT848_RISC_WRITE >> 28 ] = 2,
+ [ BT848_RISC_JUMP >> 28 ] = 2,
+ [ BT848_RISC_SYNC >> 28 ] = 2,
+ [ BT848_RISC_WRITE123 >> 28 ] = 5,
+ [ BT848_RISC_SKIP123 >> 28 ] = 2,
+ [ BT848_RISC_WRITE1S23 >> 28 ] = 3,
+ };
+ static char *bits[] = {
+ "be0", "be1", "be2", "be3/resync",
+ "set0", "set1", "set2", "set3",
+ "clr0", "clr1", "clr2", "clr3",
+ "irq", "res", "eol", "sol",
+ };
+ int i;
+
+ pr_cont("0x%08x [ %s", risc,
+ instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
+ for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
+ if (risc & (1 << (i + 12)))
+ pr_cont(" %s", bits[i]);
+ pr_cont(" count=%d ]\n", risc & 0xfff);
+ return incr[risc >> 28] ? incr[risc >> 28] : 1;
+}
+
+static void bttv_risc_disasm(struct bttv *btv,
+ struct btcx_riscmem *risc)
+{
+ unsigned int i,j,n;
+
+ pr_info("%s: risc disasm: %p [dma=0x%08lx]\n",
+ btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma);
+ for (i = 0; i < (risc->size >> 2); i += n) {
+ pr_info("%s: 0x%lx: ",
+ btv->c.v4l2_dev.name,
+ (unsigned long)(risc->dma + (i<<2)));
+ n = bttv_risc_decode(le32_to_cpu(risc->cpu[i]));
+ for (j = 1; j < n; j++)
+ pr_info("%s: 0x%lx: 0x%08x [ arg #%d ]\n",
+ btv->c.v4l2_dev.name,
+ (unsigned long)(risc->dma + ((i+j)<<2)),
+ risc->cpu[i+j], j);
+ if (0 == risc->cpu[i])
+ break;
+ }
+}
+
+static void bttv_print_riscaddr(struct bttv *btv)
+{
+ pr_info(" main: %08llx\n", (unsigned long long)btv->main.dma);
+ pr_info(" vbi : o=%08llx e=%08llx\n",
+ btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0,
+ btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0);
+ pr_info(" cap : o=%08llx e=%08llx\n",
+ btv->curr.top
+ ? (unsigned long long)btv->curr.top->top.dma : 0,
+ btv->curr.bottom
+ ? (unsigned long long)btv->curr.bottom->bottom.dma : 0);
+ pr_info(" scr : o=%08llx e=%08llx\n",
+ btv->screen ? (unsigned long long)btv->screen->top.dma : 0,
+ btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0);
+ bttv_risc_disasm(btv, &btv->main);
+}
+
+/* ----------------------------------------------------------------------- */
+/* irq handler */
+
+static char *irq_name[] = {
+ "FMTCHG", // format change detected (525 vs. 625)
+ "VSYNC", // vertical sync (new field)
+ "HSYNC", // horizontal sync
+ "OFLOW", // chroma/luma AGC overflow
+ "HLOCK", // horizontal lock changed
+ "VPRES", // video presence changed
+ "6", "7",
+ "I2CDONE", // hw irc operation finished
+ "GPINT", // gpio port triggered irq
+ "10",
+ "RISCI", // risc instruction triggered irq
+ "FBUS", // pixel data fifo dropped data (high pci bus latencies)
+ "FTRGT", // pixel data fifo overrun
+ "FDSR", // fifo data stream resyncronisation
+ "PPERR", // parity error (data transfer)
+ "RIPERR", // parity error (read risc instructions)
+ "PABORT", // pci abort
+ "OCERR", // risc instruction error
+ "SCERR", // syncronisation error
+};
+
+static void bttv_print_irqbits(u32 print, u32 mark)
+{
+ unsigned int i;
+
+ pr_cont("bits:");
+ for (i = 0; i < ARRAY_SIZE(irq_name); i++) {
+ if (print & (1 << i))
+ pr_cont(" %s", irq_name[i]);
+ if (mark & (1 << i))
+ pr_cont("*");
+ }
+}
+
+static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc)
+{
+ pr_warn("%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n",
+ btv->c.nr,
+ (unsigned long)btv->main.dma,
+ (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_VBI+1]),
+ (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_FIELD+1]),
+ (unsigned long)rc);
+
+ if (0 == (btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC)) {
+ pr_notice("%d: Oh, there (temporarily?) is no input signal. "
+ "Ok, then this is harmless, don't worry ;)\n",
+ btv->c.nr);
+ return;
+ }
+ pr_notice("%d: Uhm. Looks like we have unusual high IRQ latencies\n",
+ btv->c.nr);
+ pr_notice("%d: Lets try to catch the culpit red-handed ...\n",
+ btv->c.nr);
+ dump_stack();
+}
+
+static int
+bttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set)
+{
+ struct bttv_buffer *item;
+
+ memset(set,0,sizeof(*set));
+
+ /* capture request ? */
+ if (!list_empty(&btv->capture)) {
+ set->frame_irq = 1;
+ item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+ if (V4L2_FIELD_HAS_TOP(item->vb.field))
+ set->top = item;
+ if (V4L2_FIELD_HAS_BOTTOM(item->vb.field))
+ set->bottom = item;
+
+ /* capture request for other field ? */
+ if (!V4L2_FIELD_HAS_BOTH(item->vb.field) &&
+ (item->vb.queue.next != &btv->capture)) {
+ item = list_entry(item->vb.queue.next, struct bttv_buffer, vb.queue);
+ /* Mike Isely <isely@pobox.com> - Only check
+ * and set up the bottom field in the logic
+ * below. Don't ever do the top field. This
+ * of course means that if we set up the
+ * bottom field in the above code that we'll
+ * actually skip a field. But that's OK.
+ * Having processed only a single buffer this
+ * time, then the next time around the first
+ * available buffer should be for a top field.
+ * That will then cause us here to set up a
+ * top then a bottom field in the normal way.
+ * The alternative to this understanding is
+ * that we set up the second available buffer
+ * as a top field, but that's out of order
+ * since this driver always processes the top
+ * field first - the effect will be the two
+ * buffers being returned in the wrong order,
+ * with the second buffer also being delayed
+ * by one field time (owing to the fifo nature
+ * of videobuf). Worse still, we'll be stuck
+ * doing fields out of order now every time
+ * until something else causes a field to be
+ * dropped. By effectively forcing a field to
+ * drop this way then we always get back into
+ * sync within a single frame time. (Out of
+ * order fields can screw up deinterlacing
+ * algorithms.) */
+ if (!V4L2_FIELD_HAS_BOTH(item->vb.field)) {
+ if (NULL == set->bottom &&
+ V4L2_FIELD_BOTTOM == item->vb.field) {
+ set->bottom = item;
+ }
+ if (NULL != set->top && NULL != set->bottom)
+ set->top_irq = 2;
+ }
+ }
+ }
+
+ /* screen overlay ? */
+ if (NULL != btv->screen) {
+ if (V4L2_FIELD_HAS_BOTH(btv->screen->vb.field)) {
+ if (NULL == set->top && NULL == set->bottom) {
+ set->top = btv->screen;
+ set->bottom = btv->screen;
+ }
+ } else {
+ if (V4L2_FIELD_TOP == btv->screen->vb.field &&
+ NULL == set->top) {
+ set->top = btv->screen;
+ }
+ if (V4L2_FIELD_BOTTOM == btv->screen->vb.field &&
+ NULL == set->bottom) {
+ set->bottom = btv->screen;
+ }
+ }
+ }
+
+ dprintk("%d: next set: top=%p bottom=%p [screen=%p,irq=%d,%d]\n",
+ btv->c.nr, set->top, set->bottom,
+ btv->screen, set->frame_irq, set->top_irq);
+ return 0;
+}
+
+static void
+bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup,
+ struct bttv_buffer_set *curr, unsigned int state)
+{
+ struct timeval ts;
+
+ do_gettimeofday(&ts);
+
+ if (wakeup->top == wakeup->bottom) {
+ if (NULL != wakeup->top && curr->top != wakeup->top) {
+ if (irq_debug > 1)
+ pr_debug("%d: wakeup: both=%p\n",
+ btv->c.nr, wakeup->top);
+ wakeup->top->vb.ts = ts;
+ wakeup->top->vb.field_count = btv->field_count;
+ wakeup->top->vb.state = state;
+ wake_up(&wakeup->top->vb.done);
+ }
+ } else {
+ if (NULL != wakeup->top && curr->top != wakeup->top) {
+ if (irq_debug > 1)
+ pr_debug("%d: wakeup: top=%p\n",
+ btv->c.nr, wakeup->top);
+ wakeup->top->vb.ts = ts;
+ wakeup->top->vb.field_count = btv->field_count;
+ wakeup->top->vb.state = state;
+ wake_up(&wakeup->top->vb.done);
+ }
+ if (NULL != wakeup->bottom && curr->bottom != wakeup->bottom) {
+ if (irq_debug > 1)
+ pr_debug("%d: wakeup: bottom=%p\n",
+ btv->c.nr, wakeup->bottom);
+ wakeup->bottom->vb.ts = ts;
+ wakeup->bottom->vb.field_count = btv->field_count;
+ wakeup->bottom->vb.state = state;
+ wake_up(&wakeup->bottom->vb.done);
+ }
+ }
+}
+
+static void
+bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup,
+ unsigned int state)
+{
+ struct timeval ts;
+
+ if (NULL == wakeup)
+ return;
+
+ do_gettimeofday(&ts);
+ wakeup->vb.ts = ts;
+ wakeup->vb.field_count = btv->field_count;
+ wakeup->vb.state = state;
+ wake_up(&wakeup->vb.done);
+}
+
+static void bttv_irq_timeout(unsigned long data)
+{
+ struct bttv *btv = (struct bttv *)data;
+ struct bttv_buffer_set old,new;
+ struct bttv_buffer *ovbi;
+ struct bttv_buffer *item;
+ unsigned long flags;
+
+ if (bttv_verbose) {
+ pr_info("%d: timeout: drop=%d irq=%d/%d, risc=%08x, ",
+ btv->c.nr, btv->framedrop, btv->irq_me, btv->irq_total,
+ btread(BT848_RISC_COUNT));
+ bttv_print_irqbits(btread(BT848_INT_STAT),0);
+ pr_cont("\n");
+ }
+
+ spin_lock_irqsave(&btv->s_lock,flags);
+
+ /* deactivate stuff */
+ memset(&new,0,sizeof(new));
+ old = btv->curr;
+ ovbi = btv->cvbi;
+ btv->curr = new;
+ btv->cvbi = NULL;
+ btv->loop_irq = 0;
+ bttv_buffer_activate_video(btv, &new);
+ bttv_buffer_activate_vbi(btv, NULL);
+ bttv_set_dma(btv, 0);
+
+ /* wake up */
+ bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_ERROR);
+ bttv_irq_wakeup_vbi(btv, ovbi, VIDEOBUF_ERROR);
+
+ /* cancel all outstanding capture / vbi requests */
+ while (!list_empty(&btv->capture)) {
+ item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+ list_del(&item->vb.queue);
+ item->vb.state = VIDEOBUF_ERROR;
+ wake_up(&item->vb.done);
+ }
+ while (!list_empty(&btv->vcapture)) {
+ item = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
+ list_del(&item->vb.queue);
+ item->vb.state = VIDEOBUF_ERROR;
+ wake_up(&item->vb.done);
+ }
+
+ btv->errors++;
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+}
+
+static void
+bttv_irq_wakeup_top(struct bttv *btv)
+{
+ struct bttv_buffer *wakeup = btv->curr.top;
+
+ if (NULL == wakeup)
+ return;
+
+ spin_lock(&btv->s_lock);
+ btv->curr.top_irq = 0;
+ btv->curr.top = NULL;
+ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
+
+ do_gettimeofday(&wakeup->vb.ts);
+ wakeup->vb.field_count = btv->field_count;
+ wakeup->vb.state = VIDEOBUF_DONE;
+ wake_up(&wakeup->vb.done);
+ spin_unlock(&btv->s_lock);
+}
+
+static inline int is_active(struct btcx_riscmem *risc, u32 rc)
+{
+ if (rc < risc->dma)
+ return 0;
+ if (rc > risc->dma + risc->size)
+ return 0;
+ return 1;
+}
+
+static void
+bttv_irq_switch_video(struct bttv *btv)
+{
+ struct bttv_buffer_set new;
+ struct bttv_buffer_set old;
+ dma_addr_t rc;
+
+ spin_lock(&btv->s_lock);
+
+ /* new buffer set */
+ bttv_irq_next_video(btv, &new);
+ rc = btread(BT848_RISC_COUNT);
+ if ((btv->curr.top && is_active(&btv->curr.top->top, rc)) ||
+ (btv->curr.bottom && is_active(&btv->curr.bottom->bottom, rc))) {
+ btv->framedrop++;
+ if (debug_latency)
+ bttv_irq_debug_low_latency(btv, rc);
+ spin_unlock(&btv->s_lock);
+ return;
+ }
+
+ /* switch over */
+ old = btv->curr;
+ btv->curr = new;
+ btv->loop_irq &= ~1;
+ bttv_buffer_activate_video(btv, &new);
+ bttv_set_dma(btv, 0);
+
+ /* switch input */
+ if (UNSET != btv->new_input) {
+ video_mux(btv,btv->new_input);
+ btv->new_input = UNSET;
+ }
+
+ /* wake up finished buffers */
+ bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_DONE);
+ spin_unlock(&btv->s_lock);
+}
+
+static void
+bttv_irq_switch_vbi(struct bttv *btv)
+{
+ struct bttv_buffer *new = NULL;
+ struct bttv_buffer *old;
+ u32 rc;
+
+ spin_lock(&btv->s_lock);
+
+ if (!list_empty(&btv->vcapture))
+ new = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
+ old = btv->cvbi;
+
+ rc = btread(BT848_RISC_COUNT);
+ if (NULL != old && (is_active(&old->top, rc) ||
+ is_active(&old->bottom, rc))) {
+ btv->framedrop++;
+ if (debug_latency)
+ bttv_irq_debug_low_latency(btv, rc);
+ spin_unlock(&btv->s_lock);
+ return;
+ }
+
+ /* switch */
+ btv->cvbi = new;
+ btv->loop_irq &= ~4;
+ bttv_buffer_activate_vbi(btv, new);
+ bttv_set_dma(btv, 0);
+
+ bttv_irq_wakeup_vbi(btv, old, VIDEOBUF_DONE);
+ spin_unlock(&btv->s_lock);
+}
+
+static irqreturn_t bttv_irq(int irq, void *dev_id)
+{
+ u32 stat,astat;
+ u32 dstat;
+ int count;
+ struct bttv *btv;
+ int handled = 0;
+
+ btv=(struct bttv *)dev_id;
+
+ count=0;
+ while (1) {
+ /* get/clear interrupt status bits */
+ stat=btread(BT848_INT_STAT);
+ astat=stat&btread(BT848_INT_MASK);
+ if (!astat)
+ break;
+ handled = 1;
+ btwrite(stat,BT848_INT_STAT);
+
+ /* get device status bits */
+ dstat=btread(BT848_DSTATUS);
+
+ if (irq_debug) {
+ pr_debug("%d: irq loop=%d fc=%d riscs=%x, riscc=%08x, ",
+ btv->c.nr, count, btv->field_count,
+ stat>>28, btread(BT848_RISC_COUNT));
+ bttv_print_irqbits(stat,astat);
+ if (stat & BT848_INT_HLOCK)
+ pr_cont(" HLOC => %s",
+ dstat & BT848_DSTATUS_HLOC
+ ? "yes" : "no");
+ if (stat & BT848_INT_VPRES)
+ pr_cont(" PRES => %s",
+ dstat & BT848_DSTATUS_PRES
+ ? "yes" : "no");
+ if (stat & BT848_INT_FMTCHG)
+ pr_cont(" NUML => %s",
+ dstat & BT848_DSTATUS_NUML
+ ? "625" : "525");
+ pr_cont("\n");
+ }
+
+ if (astat&BT848_INT_VSYNC)
+ btv->field_count++;
+
+ if ((astat & BT848_INT_GPINT) && btv->remote) {
+ bttv_input_irq(btv);
+ }
+
+ if (astat & BT848_INT_I2CDONE) {
+ btv->i2c_done = stat;
+ wake_up(&btv->i2c_queue);
+ }
+
+ if ((astat & BT848_INT_RISCI) && (stat & (4<<28)))
+ bttv_irq_switch_vbi(btv);
+
+ if ((astat & BT848_INT_RISCI) && (stat & (2<<28)))
+ bttv_irq_wakeup_top(btv);
+
+ if ((astat & BT848_INT_RISCI) && (stat & (1<<28)))
+ bttv_irq_switch_video(btv);
+
+ if ((astat & BT848_INT_HLOCK) && btv->opt_automute)
+ audio_mute(btv, btv->mute); /* trigger automute */
+
+ if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) {
+ pr_info("%d: %s%s @ %08x,",
+ btv->c.nr,
+ (astat & BT848_INT_SCERR) ? "SCERR" : "",
+ (astat & BT848_INT_OCERR) ? "OCERR" : "",
+ btread(BT848_RISC_COUNT));
+ bttv_print_irqbits(stat,astat);
+ pr_cont("\n");
+ if (bttv_debug)
+ bttv_print_riscaddr(btv);
+ }
+ if (fdsr && astat & BT848_INT_FDSR) {
+ pr_info("%d: FDSR @ %08x\n",
+ btv->c.nr, btread(BT848_RISC_COUNT));
+ if (bttv_debug)
+ bttv_print_riscaddr(btv);
+ }
+
+ count++;
+ if (count > 4) {
+
+ if (count > 8 || !(astat & BT848_INT_GPINT)) {
+ btwrite(0, BT848_INT_MASK);
+
+ pr_err("%d: IRQ lockup, cleared int mask [",
+ btv->c.nr);
+ } else {
+ pr_err("%d: IRQ lockup, clearing GPINT from int mask [",
+ btv->c.nr);
+
+ btwrite(btread(BT848_INT_MASK) & (-1 ^ BT848_INT_GPINT),
+ BT848_INT_MASK);
+ };
+
+ bttv_print_irqbits(stat,astat);
+
+ pr_cont("]\n");
+ }
+ }
+ btv->irq_total++;
+ if (handled)
+ btv->irq_me++;
+ return IRQ_RETVAL(handled);
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* initialitation */
+
+static struct video_device *vdev_init(struct bttv *btv,
+ const struct video_device *template,
+ const char *type_name)
+{
+ struct video_device *vfd;
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ vfd->v4l2_dev = &btv->c.v4l2_dev;
+ vfd->release = video_device_release;
+ vfd->debug = bttv_debug;
+ video_set_drvdata(vfd, btv);
+ snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)",
+ btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "",
+ type_name, bttv_tvcards[btv->c.type].name);
+ return vfd;
+}
+
+static void bttv_unregister_video(struct bttv *btv)
+{
+ if (btv->video_dev) {
+ if (video_is_registered(btv->video_dev))
+ video_unregister_device(btv->video_dev);
+ else
+ video_device_release(btv->video_dev);
+ btv->video_dev = NULL;
+ }
+ if (btv->vbi_dev) {
+ if (video_is_registered(btv->vbi_dev))
+ video_unregister_device(btv->vbi_dev);
+ else
+ video_device_release(btv->vbi_dev);
+ btv->vbi_dev = NULL;
+ }
+ if (btv->radio_dev) {
+ if (video_is_registered(btv->radio_dev))
+ video_unregister_device(btv->radio_dev);
+ else
+ video_device_release(btv->radio_dev);
+ btv->radio_dev = NULL;
+ }
+}
+
+/* register video4linux devices */
+static int __devinit bttv_register_video(struct bttv *btv)
+{
+ if (no_overlay > 0)
+ pr_notice("Overlay support disabled\n");
+
+ /* video */
+ btv->video_dev = vdev_init(btv, &bttv_video_template, "video");
+
+ if (NULL == btv->video_dev)
+ goto err;
+ if (video_register_device(btv->video_dev, VFL_TYPE_GRABBER,
+ video_nr[btv->c.nr]) < 0)
+ goto err;
+ pr_info("%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->video_dev));
+ if (device_create_file(&btv->video_dev->dev,
+ &dev_attr_card)<0) {
+ pr_err("%d: device_create_file 'card' failed\n", btv->c.nr);
+ goto err;
+ }
+
+ /* vbi */
+ btv->vbi_dev = vdev_init(btv, &bttv_video_template, "vbi");
+
+ if (NULL == btv->vbi_dev)
+ goto err;
+ if (video_register_device(btv->vbi_dev, VFL_TYPE_VBI,
+ vbi_nr[btv->c.nr]) < 0)
+ goto err;
+ pr_info("%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->vbi_dev));
+
+ if (!btv->has_radio)
+ return 0;
+ /* radio */
+ btv->radio_dev = vdev_init(btv, &radio_template, "radio");
+ if (NULL == btv->radio_dev)
+ goto err;
+ if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,
+ radio_nr[btv->c.nr]) < 0)
+ goto err;
+ pr_info("%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->radio_dev));
+
+ /* all done */
+ return 0;
+
+ err:
+ bttv_unregister_video(btv);
+ return -1;
+}
+
+
+/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
+/* response on cards with no firmware is not enabled by OF */
+static void pci_set_command(struct pci_dev *dev)
+{
+#if defined(__powerpc__)
+ unsigned int cmd;
+
+ pci_read_config_dword(dev, PCI_COMMAND, &cmd);
+ cmd = (cmd | PCI_COMMAND_MEMORY );
+ pci_write_config_dword(dev, PCI_COMMAND, cmd);
+#endif
+}
+
+static int __devinit bttv_probe(struct pci_dev *dev,
+ const struct pci_device_id *pci_id)
+{
+ int result;
+ unsigned char lat;
+ struct bttv *btv;
+
+ if (bttv_num == BTTV_MAX)
+ return -ENOMEM;
+ pr_info("Bt8xx card found (%d)\n", bttv_num);
+ bttvs[bttv_num] = btv = kzalloc(sizeof(*btv), GFP_KERNEL);
+ if (btv == NULL) {
+ pr_err("out of memory\n");
+ return -ENOMEM;
+ }
+ btv->c.nr = bttv_num;
+ snprintf(btv->c.v4l2_dev.name, sizeof(btv->c.v4l2_dev.name),
+ "bttv%d", btv->c.nr);
+
+ /* initialize structs / fill in defaults */
+ mutex_init(&btv->lock);
+ spin_lock_init(&btv->s_lock);
+ spin_lock_init(&btv->gpio_lock);
+ init_waitqueue_head(&btv->i2c_queue);
+ INIT_LIST_HEAD(&btv->c.subs);
+ INIT_LIST_HEAD(&btv->capture);
+ INIT_LIST_HEAD(&btv->vcapture);
+ v4l2_prio_init(&btv->prio);
+
+ init_timer(&btv->timeout);
+ btv->timeout.function = bttv_irq_timeout;
+ btv->timeout.data = (unsigned long)btv;
+
+ btv->i2c_rc = -1;
+ btv->tuner_type = UNSET;
+ btv->new_input = UNSET;
+ btv->has_radio=radio[btv->c.nr];
+
+ /* pci stuff (init, get irq/mmio, ... */
+ btv->c.pci = dev;
+ btv->id = dev->device;
+ if (pci_enable_device(dev)) {
+ pr_warn("%d: Can't enable device\n", btv->c.nr);
+ return -EIO;
+ }
+ if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) {
+ pr_warn("%d: No suitable DMA available\n", btv->c.nr);
+ return -EIO;
+ }
+ if (!request_mem_region(pci_resource_start(dev,0),
+ pci_resource_len(dev,0),
+ btv->c.v4l2_dev.name)) {
+ pr_warn("%d: can't request iomem (0x%llx)\n",
+ btv->c.nr,
+ (unsigned long long)pci_resource_start(dev, 0));
+ return -EBUSY;
+ }
+ pci_set_master(dev);
+ pci_set_command(dev);
+
+ result = v4l2_device_register(&dev->dev, &btv->c.v4l2_dev);
+ if (result < 0) {
+ pr_warn("%d: v4l2_device_register() failed\n", btv->c.nr);
+ goto fail0;
+ }
+
+ btv->revision = dev->revision;
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+ pr_info("%d: Bt%d (rev %d) at %s, irq: %d, latency: %d, mmio: 0x%llx\n",
+ bttv_num, btv->id, btv->revision, pci_name(dev),
+ btv->c.pci->irq, lat,
+ (unsigned long long)pci_resource_start(dev, 0));
+ schedule();
+
+ btv->bt848_mmio = ioremap(pci_resource_start(dev, 0), 0x1000);
+ if (NULL == btv->bt848_mmio) {
+ pr_err("%d: ioremap() failed\n", btv->c.nr);
+ result = -EIO;
+ goto fail1;
+ }
+
+ /* identify card */
+ bttv_idcard(btv);
+
+ /* disable irqs, register irq handler */
+ btwrite(0, BT848_INT_MASK);
+ result = request_irq(btv->c.pci->irq, bttv_irq,
+ IRQF_SHARED | IRQF_DISABLED, btv->c.v4l2_dev.name, (void *)btv);
+ if (result < 0) {
+ pr_err("%d: can't get IRQ %d\n",
+ bttv_num, btv->c.pci->irq);
+ goto fail1;
+ }
+
+ if (0 != bttv_handle_chipset(btv)) {
+ result = -EIO;
+ goto fail2;
+ }
+
+ /* init options from insmod args */
+ btv->opt_combfilter = combfilter;
+ btv->opt_lumafilter = lumafilter;
+ btv->opt_automute = automute;
+ btv->opt_chroma_agc = chroma_agc;
+ btv->opt_adc_crush = adc_crush;
+ btv->opt_vcr_hack = vcr_hack;
+ btv->opt_whitecrush_upper = whitecrush_upper;
+ btv->opt_whitecrush_lower = whitecrush_lower;
+ btv->opt_uv_ratio = uv_ratio;
+ btv->opt_full_luma_range = full_luma_range;
+ btv->opt_coring = coring;
+
+ /* fill struct bttv with some useful defaults */
+ btv->init.btv = btv;
+ btv->init.ov.w.width = 320;
+ btv->init.ov.w.height = 240;
+ btv->init.fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+ btv->init.width = 320;
+ btv->init.height = 240;
+ btv->input = 0;
+
+ /* initialize hardware */
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"pre-init");
+
+ bttv_risc_init_main(btv);
+ init_bt848(btv);
+
+ /* gpio */
+ btwrite(0x00, BT848_GPIO_REG_INP);
+ btwrite(0x00, BT848_GPIO_OUT_EN);
+ if (bttv_verbose)
+ bttv_gpio_tracking(btv,"init");
+
+ /* needs to be done before i2c is registered */
+ bttv_init_card1(btv);
+
+ /* register i2c + gpio */
+ init_bttv_i2c(btv);
+
+ /* some card-specific stuff (needs working i2c) */
+ bttv_init_card2(btv);
+ bttv_init_tuner(btv);
+ init_irqreg(btv);
+
+ /* register video4linux + input */
+ if (!bttv_tvcards[btv->c.type].no_video) {
+ bttv_register_video(btv);
+ bt848_bright(btv,32768);
+ bt848_contrast(btv, 27648);
+ bt848_hue(btv,32768);
+ bt848_sat(btv,32768);
+ audio_mute(btv, 1);
+ set_input(btv, 0, btv->tvnorm);
+ bttv_crop_reset(&btv->crop[0], btv->tvnorm);
+ btv->crop[1] = btv->crop[0]; /* current = default */
+ disclaim_vbi_lines(btv);
+ disclaim_video_lines(btv);
+ }
+
+ /* add subdevices and autoload dvb-bt8xx if needed */
+ if (bttv_tvcards[btv->c.type].has_dvb) {
+ bttv_sub_add_device(&btv->c, "dvb");
+ request_modules(btv);
+ }
+
+ if (!disable_ir) {
+ init_bttv_i2c_ir(btv);
+ bttv_input_init(btv);
+ }
+
+ /* everything is fine */
+ bttv_num++;
+ return 0;
+
+fail2:
+ free_irq(btv->c.pci->irq,btv);
+
+fail1:
+ v4l2_device_unregister(&btv->c.v4l2_dev);
+
+fail0:
+ if (btv->bt848_mmio)
+ iounmap(btv->bt848_mmio);
+ release_mem_region(pci_resource_start(btv->c.pci,0),
+ pci_resource_len(btv->c.pci,0));
+ return result;
+}
+
+static void __devexit bttv_remove(struct pci_dev *pci_dev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct bttv *btv = to_bttv(v4l2_dev);
+
+ if (bttv_verbose)
+ pr_info("%d: unloading\n", btv->c.nr);
+
+ if (bttv_tvcards[btv->c.type].has_dvb)
+ flush_request_modules(btv);
+
+ /* shutdown everything (DMA+IRQs) */
+ btand(~15, BT848_GPIO_DMA_CTL);
+ btwrite(0, BT848_INT_MASK);
+ btwrite(~0x0, BT848_INT_STAT);
+ btwrite(0x0, BT848_GPIO_OUT_EN);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"cleanup");
+
+ /* tell gpio modules we are leaving ... */
+ btv->shutdown=1;
+ bttv_input_fini(btv);
+ bttv_sub_del_devices(&btv->c);
+
+ /* unregister i2c_bus + input */
+ fini_bttv_i2c(btv);
+
+ /* unregister video4linux */
+ bttv_unregister_video(btv);
+
+ /* free allocated memory */
+ btcx_riscmem_free(btv->c.pci,&btv->main);
+
+ /* free ressources */
+ free_irq(btv->c.pci->irq,btv);
+ iounmap(btv->bt848_mmio);
+ release_mem_region(pci_resource_start(btv->c.pci,0),
+ pci_resource_len(btv->c.pci,0));
+
+ v4l2_device_unregister(&btv->c.v4l2_dev);
+ bttvs[btv->c.nr] = NULL;
+ kfree(btv);
+
+ return;
+}
+
+#ifdef CONFIG_PM
+static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct bttv *btv = to_bttv(v4l2_dev);
+ struct bttv_buffer_set idle;
+ unsigned long flags;
+
+ dprintk("%d: suspend %d\n", btv->c.nr, state.event);
+
+ /* stop dma + irqs */
+ spin_lock_irqsave(&btv->s_lock,flags);
+ memset(&idle, 0, sizeof(idle));
+ btv->state.video = btv->curr;
+ btv->state.vbi = btv->cvbi;
+ btv->state.loop_irq = btv->loop_irq;
+ btv->curr = idle;
+ btv->loop_irq = 0;
+ bttv_buffer_activate_video(btv, &idle);
+ bttv_buffer_activate_vbi(btv, NULL);
+ bttv_set_dma(btv, 0);
+ btwrite(0, BT848_INT_MASK);
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+
+ /* save bt878 state */
+ btv->state.gpio_enable = btread(BT848_GPIO_OUT_EN);
+ btv->state.gpio_data = gpio_read();
+
+ /* save pci state */
+ pci_save_state(pci_dev);
+ if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) {
+ pci_disable_device(pci_dev);
+ btv->state.disabled = 1;
+ }
+ return 0;
+}
+
+static int bttv_resume(struct pci_dev *pci_dev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct bttv *btv = to_bttv(v4l2_dev);
+ unsigned long flags;
+ int err;
+
+ dprintk("%d: resume\n", btv->c.nr);
+
+ /* restore pci state */
+ if (btv->state.disabled) {
+ err=pci_enable_device(pci_dev);
+ if (err) {
+ pr_warn("%d: Can't enable device\n", btv->c.nr);
+ return err;
+ }
+ btv->state.disabled = 0;
+ }
+ err=pci_set_power_state(pci_dev, PCI_D0);
+ if (err) {
+ pci_disable_device(pci_dev);
+ pr_warn("%d: Can't enable device\n", btv->c.nr);
+ btv->state.disabled = 1;
+ return err;
+ }
+
+ pci_restore_state(pci_dev);
+
+ /* restore bt878 state */
+ bttv_reinit_bt848(btv);
+ gpio_inout(0xffffff, btv->state.gpio_enable);
+ gpio_write(btv->state.gpio_data);
+
+ /* restart dma */
+ spin_lock_irqsave(&btv->s_lock,flags);
+ btv->curr = btv->state.video;
+ btv->cvbi = btv->state.vbi;
+ btv->loop_irq = btv->state.loop_irq;
+ bttv_buffer_activate_video(btv, &btv->curr);
+ bttv_buffer_activate_vbi(btv, btv->cvbi);
+ bttv_set_dma(btv, 0);
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+ return 0;
+}
+#endif
+
+static struct pci_device_id bttv_pci_tbl[] = {
+ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848), 0},
+ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT849), 0},
+ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT878), 0},
+ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT879), 0},
+ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_FUSION879), 0},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, bttv_pci_tbl);
+
+static struct pci_driver bttv_pci_driver = {
+ .name = "bttv",
+ .id_table = bttv_pci_tbl,
+ .probe = bttv_probe,
+ .remove = __devexit_p(bttv_remove),
+#ifdef CONFIG_PM
+ .suspend = bttv_suspend,
+ .resume = bttv_resume,
+#endif
+};
+
+static int __init bttv_init_module(void)
+{
+ int ret;
+
+ bttv_num = 0;
+
+ pr_info("driver version %s loaded\n", BTTV_VERSION);
+ if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
+ gbuffers = 2;
+ if (gbufsize > BTTV_MAX_FBUF)
+ gbufsize = BTTV_MAX_FBUF;
+ gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
+ if (bttv_verbose)
+ pr_info("using %d buffers with %dk (%d pages) each for capture\n",
+ gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT);
+
+ bttv_check_chipset();
+
+ ret = bus_register(&bttv_sub_bus_type);
+ if (ret < 0) {
+ pr_warn("bus_register error: %d\n", ret);
+ return ret;
+ }
+ ret = pci_register_driver(&bttv_pci_driver);
+ if (ret < 0)
+ bus_unregister(&bttv_sub_bus_type);
+
+ return ret;
+}
+
+static void __exit bttv_cleanup_module(void)
+{
+ pci_unregister_driver(&bttv_pci_driver);
+ bus_unregister(&bttv_sub_bus_type);
+}
+
+module_init(bttv_init_module);
+module_exit(bttv_cleanup_module);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/bttv-gpio.c b/drivers/media/pci/bt8xx/bttv-gpio.c
new file mode 100644
index 0000000..922e823
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-gpio.c
@@ -0,0 +1,189 @@
+/*
+
+ bttv-gpio.c -- gpio sub drivers
+
+ sysfs-based sub driver interface for bttv
+ mainly intended for gpio access
+
+
+ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ & Marcus Metzler (mocm@thp.uni-koeln.de)
+ (c) 1999-2003 Gerd Knorr <kraxel@bytesex.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 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+
+#include "bttvp.h"
+
+/* ----------------------------------------------------------------------- */
+/* internal: the bttv "bus" */
+
+static int bttv_sub_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct bttv_sub_driver *sub = to_bttv_sub_drv(drv);
+ int len = strlen(sub->wanted);
+
+ if (0 == strncmp(dev_name(dev), sub->wanted, len))
+ return 1;
+ return 0;
+}
+
+static int bttv_sub_probe(struct device *dev)
+{
+ struct bttv_sub_device *sdev = to_bttv_sub_dev(dev);
+ struct bttv_sub_driver *sub = to_bttv_sub_drv(dev->driver);
+
+ return sub->probe ? sub->probe(sdev) : -ENODEV;
+}
+
+static int bttv_sub_remove(struct device *dev)
+{
+ struct bttv_sub_device *sdev = to_bttv_sub_dev(dev);
+ struct bttv_sub_driver *sub = to_bttv_sub_drv(dev->driver);
+
+ if (sub->remove)
+ sub->remove(sdev);
+ return 0;
+}
+
+struct bus_type bttv_sub_bus_type = {
+ .name = "bttv-sub",
+ .match = &bttv_sub_bus_match,
+ .probe = bttv_sub_probe,
+ .remove = bttv_sub_remove,
+};
+
+static void release_sub_device(struct device *dev)
+{
+ struct bttv_sub_device *sub = to_bttv_sub_dev(dev);
+ kfree(sub);
+}
+
+int bttv_sub_add_device(struct bttv_core *core, char *name)
+{
+ struct bttv_sub_device *sub;
+ int err;
+
+ sub = kzalloc(sizeof(*sub),GFP_KERNEL);
+ if (NULL == sub)
+ return -ENOMEM;
+
+ sub->core = core;
+ sub->dev.parent = &core->pci->dev;
+ sub->dev.bus = &bttv_sub_bus_type;
+ sub->dev.release = release_sub_device;
+ dev_set_name(&sub->dev, "%s%d", name, core->nr);
+
+ err = device_register(&sub->dev);
+ if (0 != err) {
+ kfree(sub);
+ return err;
+ }
+ pr_info("%d: add subdevice \"%s\"\n", core->nr, dev_name(&sub->dev));
+ list_add_tail(&sub->list,&core->subs);
+ return 0;
+}
+
+int bttv_sub_del_devices(struct bttv_core *core)
+{
+ struct bttv_sub_device *sub, *save;
+
+ list_for_each_entry_safe(sub, save, &core->subs, list) {
+ list_del(&sub->list);
+ device_unregister(&sub->dev);
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+/* external: sub-driver register/unregister */
+
+int bttv_sub_register(struct bttv_sub_driver *sub, char *wanted)
+{
+ sub->drv.bus = &bttv_sub_bus_type;
+ snprintf(sub->wanted,sizeof(sub->wanted),"%s",wanted);
+ return driver_register(&sub->drv);
+}
+EXPORT_SYMBOL(bttv_sub_register);
+
+int bttv_sub_unregister(struct bttv_sub_driver *sub)
+{
+ driver_unregister(&sub->drv);
+ return 0;
+}
+EXPORT_SYMBOL(bttv_sub_unregister);
+
+/* ----------------------------------------------------------------------- */
+/* external: gpio access functions */
+
+void bttv_gpio_inout(struct bttv_core *core, u32 mask, u32 outbits)
+{
+ struct bttv *btv = container_of(core, struct bttv, c);
+ unsigned long flags;
+ u32 data;
+
+ spin_lock_irqsave(&btv->gpio_lock,flags);
+ data = btread(BT848_GPIO_OUT_EN);
+ data = data & ~mask;
+ data = data | (mask & outbits);
+ btwrite(data,BT848_GPIO_OUT_EN);
+ spin_unlock_irqrestore(&btv->gpio_lock,flags);
+}
+
+u32 bttv_gpio_read(struct bttv_core *core)
+{
+ struct bttv *btv = container_of(core, struct bttv, c);
+ u32 value;
+
+ value = btread(BT848_GPIO_DATA);
+ return value;
+}
+
+void bttv_gpio_write(struct bttv_core *core, u32 value)
+{
+ struct bttv *btv = container_of(core, struct bttv, c);
+
+ btwrite(value,BT848_GPIO_DATA);
+}
+
+void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits)
+{
+ struct bttv *btv = container_of(core, struct bttv, c);
+ unsigned long flags;
+ u32 data;
+
+ spin_lock_irqsave(&btv->gpio_lock,flags);
+ data = btread(BT848_GPIO_DATA);
+ data = data & ~mask;
+ data = data | (mask & bits);
+ btwrite(data,BT848_GPIO_DATA);
+ spin_unlock_irqrestore(&btv->gpio_lock,flags);
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/bttv-i2c.c b/drivers/media/pci/bt8xx/bttv-i2c.c
new file mode 100644
index 0000000..580c8e6
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-i2c.c
@@ -0,0 +1,397 @@
+/*
+
+ bttv-i2c.c -- all the i2c code is here
+
+ bttv - Bt848 frame grabber driver
+
+ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ & Marcus Metzler (mocm@thp.uni-koeln.de)
+ (c) 1999-2003 Gerd Knorr <kraxel@bytesex.org>
+
+ (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org>
+ - Multituner support and i2c address binding
+
+ 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "bttvp.h"
+#include <media/v4l2-common.h>
+#include <linux/jiffies.h>
+#include <asm/io.h>
+
+static int i2c_debug;
+static int i2c_hw;
+static int i2c_scan;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug, "configure i2c debug level");
+module_param(i2c_hw, int, 0444);
+MODULE_PARM_DESC(i2c_hw,"force use of hardware i2c support, "
+ "instead of software bitbang");
+module_param(i2c_scan, int, 0444);
+MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
+
+static unsigned int i2c_udelay = 5;
+module_param(i2c_udelay, int, 0444);
+MODULE_PARM_DESC(i2c_udelay,"soft i2c delay at insmod time, in usecs "
+ "(should be 5 or higher). Lower value means higher bus speed.");
+
+/* ----------------------------------------------------------------------- */
+/* I2C functions - bitbanging adapter (software i2c) */
+
+static void bttv_bit_setscl(void *data, int state)
+{
+ struct bttv *btv = (struct bttv*)data;
+
+ if (state)
+ btv->i2c_state |= 0x02;
+ else
+ btv->i2c_state &= ~0x02;
+ btwrite(btv->i2c_state, BT848_I2C);
+ btread(BT848_I2C);
+}
+
+static void bttv_bit_setsda(void *data, int state)
+{
+ struct bttv *btv = (struct bttv*)data;
+
+ if (state)
+ btv->i2c_state |= 0x01;
+ else
+ btv->i2c_state &= ~0x01;
+ btwrite(btv->i2c_state, BT848_I2C);
+ btread(BT848_I2C);
+}
+
+static int bttv_bit_getscl(void *data)
+{
+ struct bttv *btv = (struct bttv*)data;
+ int state;
+
+ state = btread(BT848_I2C) & 0x02 ? 1 : 0;
+ return state;
+}
+
+static int bttv_bit_getsda(void *data)
+{
+ struct bttv *btv = (struct bttv*)data;
+ int state;
+
+ state = btread(BT848_I2C) & 0x01;
+ return state;
+}
+
+static struct i2c_algo_bit_data __devinitdata bttv_i2c_algo_bit_template = {
+ .setsda = bttv_bit_setsda,
+ .setscl = bttv_bit_setscl,
+ .getsda = bttv_bit_getsda,
+ .getscl = bttv_bit_getscl,
+ .udelay = 16,
+ .timeout = 200,
+};
+
+/* ----------------------------------------------------------------------- */
+/* I2C functions - hardware i2c */
+
+static u32 functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL;
+}
+
+static int
+bttv_i2c_wait_done(struct bttv *btv)
+{
+ int rc = 0;
+
+ /* timeout */
+ if (wait_event_interruptible_timeout(btv->i2c_queue,
+ btv->i2c_done, msecs_to_jiffies(85)) == -ERESTARTSYS)
+ rc = -EIO;
+
+ if (btv->i2c_done & BT848_INT_RACK)
+ rc = 1;
+ btv->i2c_done = 0;
+ return rc;
+}
+
+#define I2C_HW (BT878_I2C_MODE | BT848_I2C_SYNC |\
+ BT848_I2C_SCL | BT848_I2C_SDA)
+
+static int
+bttv_i2c_sendbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
+{
+ u32 xmit;
+ int retval,cnt;
+
+ /* sanity checks */
+ if (0 == msg->len)
+ return -EINVAL;
+
+ /* start, address + first byte */
+ xmit = (msg->addr << 25) | (msg->buf[0] << 16) | I2C_HW;
+ if (msg->len > 1 || !last)
+ xmit |= BT878_I2C_NOSTOP;
+ btwrite(xmit, BT848_I2C);
+ retval = bttv_i2c_wait_done(btv);
+ if (retval < 0)
+ goto err;
+ if (retval == 0)
+ goto eio;
+ if (i2c_debug) {
+ pr_cont(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
+ }
+
+ for (cnt = 1; cnt < msg->len; cnt++ ) {
+ /* following bytes */
+ xmit = (msg->buf[cnt] << 24) | I2C_HW | BT878_I2C_NOSTART;
+ if (cnt < msg->len-1 || !last)
+ xmit |= BT878_I2C_NOSTOP;
+ btwrite(xmit, BT848_I2C);
+ retval = bttv_i2c_wait_done(btv);
+ if (retval < 0)
+ goto err;
+ if (retval == 0)
+ goto eio;
+ if (i2c_debug)
+ pr_cont(" %02x", msg->buf[cnt]);
+ }
+ if (!(xmit & BT878_I2C_NOSTOP))
+ pr_cont(">\n");
+ return msg->len;
+
+ eio:
+ retval = -EIO;
+ err:
+ if (i2c_debug)
+ pr_cont(" ERR: %d\n",retval);
+ return retval;
+}
+
+static int
+bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
+{
+ u32 xmit;
+ u32 cnt;
+ int retval;
+
+ for (cnt = 0; cnt < msg->len; cnt++) {
+ xmit = (msg->addr << 25) | (1 << 24) | I2C_HW;
+ if (cnt < msg->len-1)
+ xmit |= BT848_I2C_W3B;
+ if (cnt < msg->len-1 || !last)
+ xmit |= BT878_I2C_NOSTOP;
+ if (cnt)
+ xmit |= BT878_I2C_NOSTART;
+
+ if (i2c_debug) {
+ if (!(xmit & BT878_I2C_NOSTART))
+ pr_cont(" <R %02x", (msg->addr << 1) +1);
+ }
+
+ btwrite(xmit, BT848_I2C);
+ retval = bttv_i2c_wait_done(btv);
+ if (retval < 0)
+ goto err;
+ if (retval == 0)
+ goto eio;
+ msg->buf[cnt] = ((u32)btread(BT848_I2C) >> 8) & 0xff;
+ if (i2c_debug) {
+ pr_cont(" =%02x", msg->buf[cnt]);
+ }
+ if (i2c_debug && !(xmit & BT878_I2C_NOSTOP))
+ pr_cont(" >\n");
+ }
+
+
+ return msg->len;
+
+ eio:
+ retval = -EIO;
+ err:
+ if (i2c_debug)
+ pr_cont(" ERR: %d\n",retval);
+ return retval;
+}
+
+static int bttv_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
+{
+ struct v4l2_device *v4l2_dev = i2c_get_adapdata(i2c_adap);
+ struct bttv *btv = to_bttv(v4l2_dev);
+ int retval = 0;
+ int i;
+
+ if (i2c_debug)
+ pr_debug("bt-i2c:");
+
+ btwrite(BT848_INT_I2CDONE|BT848_INT_RACK, BT848_INT_STAT);
+ for (i = 0 ; i < num; i++) {
+ if (msgs[i].flags & I2C_M_RD) {
+ /* read */
+ retval = bttv_i2c_readbytes(btv, &msgs[i], i+1 == num);
+ if (retval < 0)
+ goto err;
+ } else {
+ /* write */
+ retval = bttv_i2c_sendbytes(btv, &msgs[i], i+1 == num);
+ if (retval < 0)
+ goto err;
+ }
+ }
+ return num;
+
+ err:
+ return retval;
+}
+
+static const struct i2c_algorithm bttv_algo = {
+ .master_xfer = bttv_i2c_xfer,
+ .functionality = functionality,
+};
+
+/* ----------------------------------------------------------------------- */
+/* I2C functions - common stuff */
+
+/* read I2C */
+int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for)
+{
+ unsigned char buffer = 0;
+
+ if (0 != btv->i2c_rc)
+ return -1;
+ if (bttv_verbose && NULL != probe_for)
+ pr_info("%d: i2c: checking for %s @ 0x%02x... ",
+ btv->c.nr, probe_for, addr);
+ btv->i2c_client.addr = addr >> 1;
+ if (1 != i2c_master_recv(&btv->i2c_client, &buffer, 1)) {
+ if (NULL != probe_for) {
+ if (bttv_verbose)
+ pr_cont("not found\n");
+ } else
+ pr_warn("%d: i2c read 0x%x: error\n",
+ btv->c.nr, addr);
+ return -1;
+ }
+ if (bttv_verbose && NULL != probe_for)
+ pr_cont("found\n");
+ return buffer;
+}
+
+/* write I2C */
+int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1,
+ unsigned char b2, int both)
+{
+ unsigned char buffer[2];
+ int bytes = both ? 2 : 1;
+
+ if (0 != btv->i2c_rc)
+ return -1;
+ btv->i2c_client.addr = addr >> 1;
+ buffer[0] = b1;
+ buffer[1] = b2;
+ if (bytes != i2c_master_send(&btv->i2c_client, buffer, bytes))
+ return -1;
+ return 0;
+}
+
+/* read EEPROM content */
+void __devinit bttv_readee(struct bttv *btv, unsigned char *eedata, int addr)
+{
+ memset(eedata, 0, 256);
+ if (0 != btv->i2c_rc)
+ return;
+ btv->i2c_client.addr = addr >> 1;
+ tveeprom_read(&btv->i2c_client, eedata, 256);
+}
+
+static char *i2c_devs[128] = {
+ [ 0x1c >> 1 ] = "lgdt330x",
+ [ 0x30 >> 1 ] = "IR (hauppauge)",
+ [ 0x80 >> 1 ] = "msp34xx",
+ [ 0x86 >> 1 ] = "tda9887",
+ [ 0xa0 >> 1 ] = "eeprom",
+ [ 0xc0 >> 1 ] = "tuner (analog)",
+ [ 0xc2 >> 1 ] = "tuner (analog)",
+};
+
+static void do_i2c_scan(char *name, struct i2c_client *c)
+{
+ unsigned char buf;
+ int i,rc;
+
+ for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) {
+ c->addr = i;
+ rc = i2c_master_recv(c,&buf,0);
+ if (rc < 0)
+ continue;
+ pr_info("%s: i2c scan: found device @ 0x%x [%s]\n",
+ name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+ }
+}
+
+/* init + register i2c adapter */
+int __devinit init_bttv_i2c(struct bttv *btv)
+{
+ strlcpy(btv->i2c_client.name, "bttv internal", I2C_NAME_SIZE);
+
+ if (i2c_hw)
+ btv->use_i2c_hw = 1;
+ if (btv->use_i2c_hw) {
+ /* bt878 */
+ strlcpy(btv->c.i2c_adap.name, "bt878",
+ sizeof(btv->c.i2c_adap.name));
+ btv->c.i2c_adap.algo = &bttv_algo;
+ } else {
+ /* bt848 */
+ /* Prevents usage of invalid delay values */
+ if (i2c_udelay<5)
+ i2c_udelay=5;
+
+ strlcpy(btv->c.i2c_adap.name, "bttv",
+ sizeof(btv->c.i2c_adap.name));
+ memcpy(&btv->i2c_algo, &bttv_i2c_algo_bit_template,
+ sizeof(bttv_i2c_algo_bit_template));
+ btv->i2c_algo.udelay = i2c_udelay;
+ btv->i2c_algo.data = btv;
+ btv->c.i2c_adap.algo_data = &btv->i2c_algo;
+ }
+ btv->c.i2c_adap.owner = THIS_MODULE;
+
+ btv->c.i2c_adap.dev.parent = &btv->c.pci->dev;
+ snprintf(btv->c.i2c_adap.name, sizeof(btv->c.i2c_adap.name),
+ "bt%d #%d [%s]", btv->id, btv->c.nr,
+ btv->use_i2c_hw ? "hw" : "sw");
+
+ i2c_set_adapdata(&btv->c.i2c_adap, &btv->c.v4l2_dev);
+ btv->i2c_client.adapter = &btv->c.i2c_adap;
+
+
+ if (btv->use_i2c_hw) {
+ btv->i2c_rc = i2c_add_adapter(&btv->c.i2c_adap);
+ } else {
+ bttv_bit_setscl(btv,1);
+ bttv_bit_setsda(btv,1);
+ btv->i2c_rc = i2c_bit_add_bus(&btv->c.i2c_adap);
+ }
+ if (0 == btv->i2c_rc && i2c_scan)
+ do_i2c_scan(btv->c.v4l2_dev.name, &btv->i2c_client);
+
+ return btv->i2c_rc;
+}
diff --git a/drivers/media/pci/bt8xx/bttv-if.c b/drivers/media/pci/bt8xx/bttv-if.c
new file mode 100644
index 0000000..a6a540d
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-if.c
@@ -0,0 +1,121 @@
+/*
+
+ bttv-if.c -- old gpio interface to other kernel modules
+ don't use in new code, will go away in 2.7
+ have a look at bttv-gpio.c instead.
+
+ bttv - Bt848 frame grabber driver
+
+ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ & Marcus Metzler (mocm@thp.uni-koeln.de)
+ (c) 1999-2003 Gerd Knorr <kraxel@bytesex.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 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/init.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include "bttvp.h"
+
+EXPORT_SYMBOL(bttv_get_pcidev);
+EXPORT_SYMBOL(bttv_gpio_enable);
+EXPORT_SYMBOL(bttv_read_gpio);
+EXPORT_SYMBOL(bttv_write_gpio);
+
+/* ----------------------------------------------------------------------- */
+/* Exported functions - for other modules which want to access the */
+/* gpio ports (IR for example) */
+/* see bttv.h for comments */
+
+struct pci_dev* bttv_get_pcidev(unsigned int card)
+{
+ if (card >= bttv_num)
+ return NULL;
+ if (!bttvs[card])
+ return NULL;
+
+ return bttvs[card]->c.pci;
+}
+
+
+int bttv_gpio_enable(unsigned int card, unsigned long mask, unsigned long data)
+{
+ struct bttv *btv;
+
+ if (card >= bttv_num) {
+ return -EINVAL;
+ }
+
+ btv = bttvs[card];
+ if (!btv)
+ return -ENODEV;
+
+ gpio_inout(mask,data);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"extern enable");
+ return 0;
+}
+
+int bttv_read_gpio(unsigned int card, unsigned long *data)
+{
+ struct bttv *btv;
+
+ if (card >= bttv_num) {
+ return -EINVAL;
+ }
+
+ btv = bttvs[card];
+ if (!btv)
+ return -ENODEV;
+
+ if(btv->shutdown) {
+ return -ENODEV;
+ }
+
+/* prior setting BT848_GPIO_REG_INP is (probably) not needed
+ because we set direct input on init */
+ *data = gpio_read();
+ return 0;
+}
+
+int bttv_write_gpio(unsigned int card, unsigned long mask, unsigned long data)
+{
+ struct bttv *btv;
+
+ if (card >= bttv_num) {
+ return -EINVAL;
+ }
+
+ btv = bttvs[card];
+ if (!btv)
+ return -ENODEV;
+
+/* prior setting BT848_GPIO_REG_INP is (probably) not needed
+ because direct input is set on init */
+ gpio_bits(mask,data);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"extern write");
+ return 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c
new file mode 100644
index 0000000..ef4c7cd
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-input.c
@@ -0,0 +1,589 @@
+/*
+ *
+ * Copyright (c) 2003 Gerd Knorr
+ * Copyright (c) 2003 Pavel Machek
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#include "bttv.h"
+#include "bttvp.h"
+
+
+static int ir_debug;
+module_param(ir_debug, int, 0644);
+
+static int ir_rc5_remote_gap = 885;
+module_param(ir_rc5_remote_gap, int, 0644);
+
+#undef dprintk
+#define dprintk(fmt, ...) \
+do { \
+ if (ir_debug >= 1) \
+ pr_info(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define DEVNAME "bttv-input"
+
+#define MODULE_NAME "bttv"
+
+/* ---------------------------------------------------------------------- */
+
+static void ir_handle_key(struct bttv *btv)
+{
+ struct bttv_ir *ir = btv->remote;
+ u32 gpio,data;
+
+ /* read gpio value */
+ gpio = bttv_gpio_read(&btv->c);
+ if (ir->polling) {
+ if (ir->last_gpio == gpio)
+ return;
+ ir->last_gpio = gpio;
+ }
+
+ /* extract data */
+ data = ir_extract_bits(gpio, ir->mask_keycode);
+ dprintk("irq gpio=0x%x code=%d | %s%s%s\n",
+ gpio, data,
+ ir->polling ? "poll" : "irq",
+ (gpio & ir->mask_keydown) ? " down" : "",
+ (gpio & ir->mask_keyup) ? " up" : "");
+
+ if ((ir->mask_keydown && (gpio & ir->mask_keydown)) ||
+ (ir->mask_keyup && !(gpio & ir->mask_keyup))) {
+ rc_keydown_notimeout(ir->dev, data, 0);
+ } else {
+ /* HACK: Probably, ir->mask_keydown is missing
+ for this board */
+ if (btv->c.type == BTTV_BOARD_WINFAST2000)
+ rc_keydown_notimeout(ir->dev, data, 0);
+
+ rc_keyup(ir->dev);
+ }
+}
+
+static void ir_enltv_handle_key(struct bttv *btv)
+{
+ struct bttv_ir *ir = btv->remote;
+ u32 gpio, data, keyup;
+
+ /* read gpio value */
+ gpio = bttv_gpio_read(&btv->c);
+
+ /* extract data */
+ data = ir_extract_bits(gpio, ir->mask_keycode);
+
+ /* Check if it is keyup */
+ keyup = (gpio & ir->mask_keyup) ? 1 << 31 : 0;
+
+ if ((ir->last_gpio & 0x7f) != data) {
+ dprintk("gpio=0x%x code=%d | %s\n",
+ gpio, data,
+ (gpio & ir->mask_keyup) ? " up" : "up/down");
+
+ rc_keydown_notimeout(ir->dev, data, 0);
+ if (keyup)
+ rc_keyup(ir->dev);
+ } else {
+ if ((ir->last_gpio & 1 << 31) == keyup)
+ return;
+
+ dprintk("(cnt) gpio=0x%x code=%d | %s\n",
+ gpio, data,
+ (gpio & ir->mask_keyup) ? " up" : "down");
+
+ if (keyup)
+ rc_keyup(ir->dev);
+ else
+ rc_keydown_notimeout(ir->dev, data, 0);
+ }
+
+ ir->last_gpio = data | keyup;
+}
+
+static int bttv_rc5_irq(struct bttv *btv);
+
+void bttv_input_irq(struct bttv *btv)
+{
+ struct bttv_ir *ir = btv->remote;
+
+ if (ir->rc5_gpio)
+ bttv_rc5_irq(btv);
+ else if (!ir->polling)
+ ir_handle_key(btv);
+}
+
+static void bttv_input_timer(unsigned long data)
+{
+ struct bttv *btv = (struct bttv*)data;
+ struct bttv_ir *ir = btv->remote;
+
+ if (btv->c.type == BTTV_BOARD_ENLTV_FM_2)
+ ir_enltv_handle_key(btv);
+ else
+ ir_handle_key(btv);
+ mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
+}
+
+/*
+ * FIXME: Nebula digi uses the legacy way to decode RC5, instead of relying
+ * on the rc-core way. As we need to be sure that both IRQ transitions are
+ * properly triggered, Better to touch it only with this hardware for
+ * testing.
+ */
+
+#define RC5_START(x) (((x) >> 12) & 3)
+#define RC5_TOGGLE(x) (((x) >> 11) & 1)
+#define RC5_ADDR(x) (((x) >> 6) & 31)
+#define RC5_INSTR(x) ((x) & 63)
+
+/* decode raw bit pattern to RC5 code */
+static u32 bttv_rc5_decode(unsigned int code)
+{
+ unsigned int org_code = code;
+ unsigned int pair;
+ unsigned int rc5 = 0;
+ int i;
+
+ for (i = 0; i < 14; ++i) {
+ pair = code & 0x3;
+ code >>= 2;
+
+ rc5 <<= 1;
+ switch (pair) {
+ case 0:
+ case 2:
+ break;
+ case 1:
+ rc5 |= 1;
+ break;
+ case 3:
+ dprintk("rc5_decode(%x) bad code\n",
+ org_code);
+ return 0;
+ }
+ }
+ dprintk("code=%x, rc5=%x, start=%x, toggle=%x, address=%x, "
+ "instr=%x\n", rc5, org_code, RC5_START(rc5),
+ RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5));
+ return rc5;
+}
+
+static void bttv_rc5_timer_end(unsigned long data)
+{
+ struct bttv_ir *ir = (struct bttv_ir *)data;
+ struct timeval tv;
+ u32 gap;
+ u32 rc5 = 0;
+
+ /* get time */
+ do_gettimeofday(&tv);
+
+ /* avoid overflow with gap >1s */
+ if (tv.tv_sec - ir->base_time.tv_sec > 1) {
+ gap = 200000;
+ } else {
+ gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) +
+ tv.tv_usec - ir->base_time.tv_usec;
+ }
+
+ /* signal we're ready to start a new code */
+ ir->active = false;
+
+ /* Allow some timer jitter (RC5 is ~24ms anyway so this is ok) */
+ if (gap < 28000) {
+ dprintk("spurious timer_end\n");
+ return;
+ }
+
+ if (ir->last_bit < 20) {
+ /* ignore spurious codes (caused by light/other remotes) */
+ dprintk("short code: %x\n", ir->code);
+ } else {
+ ir->code = (ir->code << ir->shift_by) | 1;
+ rc5 = bttv_rc5_decode(ir->code);
+
+ /* two start bits? */
+ if (RC5_START(rc5) != ir->start) {
+ pr_info(DEVNAME ":"
+ " rc5 start bits invalid: %u\n", RC5_START(rc5));
+
+ /* right address? */
+ } else if (RC5_ADDR(rc5) == ir->addr) {
+ u32 toggle = RC5_TOGGLE(rc5);
+ u32 instr = RC5_INSTR(rc5);
+
+ /* Good code */
+ rc_keydown(ir->dev, instr, toggle);
+ dprintk("instruction %x, toggle %x\n",
+ instr, toggle);
+ }
+ }
+}
+
+static int bttv_rc5_irq(struct bttv *btv)
+{
+ struct bttv_ir *ir = btv->remote;
+ struct timeval tv;
+ u32 gpio;
+ u32 gap;
+ unsigned long current_jiffies;
+
+ /* read gpio port */
+ gpio = bttv_gpio_read(&btv->c);
+
+ /* get time of bit */
+ current_jiffies = jiffies;
+ do_gettimeofday(&tv);
+
+ /* avoid overflow with gap >1s */
+ if (tv.tv_sec - ir->base_time.tv_sec > 1) {
+ gap = 200000;
+ } else {
+ gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) +
+ tv.tv_usec - ir->base_time.tv_usec;
+ }
+
+ dprintk("RC5 IRQ: gap %d us for %s\n",
+ gap, (gpio & 0x20) ? "mark" : "space");
+
+ /* remote IRQ? */
+ if (!(gpio & 0x20))
+ return 0;
+
+ /* active code => add bit */
+ if (ir->active) {
+ /* only if in the code (otherwise spurious IRQ or timer
+ late) */
+ if (ir->last_bit < 28) {
+ ir->last_bit = (gap - ir_rc5_remote_gap / 2) /
+ ir_rc5_remote_gap;
+ ir->code |= 1 << ir->last_bit;
+ }
+ /* starting new code */
+ } else {
+ ir->active = true;
+ ir->code = 0;
+ ir->base_time = tv;
+ ir->last_bit = 0;
+
+ mod_timer(&ir->timer, current_jiffies + msecs_to_jiffies(30));
+ }
+
+ /* toggle GPIO pin 4 to reset the irq */
+ bttv_gpio_write(&btv->c, gpio & ~(1 << 4));
+ bttv_gpio_write(&btv->c, gpio | (1 << 4));
+ return 1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void bttv_ir_start(struct bttv *btv, struct bttv_ir *ir)
+{
+ if (ir->polling) {
+ setup_timer(&ir->timer, bttv_input_timer, (unsigned long)btv);
+ ir->timer.expires = jiffies + msecs_to_jiffies(1000);
+ add_timer(&ir->timer);
+ } else if (ir->rc5_gpio) {
+ /* set timer_end for code completion */
+ setup_timer(&ir->timer, bttv_rc5_timer_end, (unsigned long)ir);
+ ir->shift_by = 1;
+ ir->start = 3;
+ ir->addr = 0x0;
+ ir->rc5_remote_gap = ir_rc5_remote_gap;
+ }
+}
+
+static void bttv_ir_stop(struct bttv *btv)
+{
+ if (btv->remote->polling)
+ del_timer_sync(&btv->remote->timer);
+
+ if (btv->remote->rc5_gpio) {
+ u32 gpio;
+
+ del_timer_sync(&btv->remote->timer);
+
+ gpio = bttv_gpio_read(&btv->c);
+ bttv_gpio_write(&btv->c, gpio & ~(1 << 4));
+ }
+}
+
+/*
+ * Get_key functions used by I2C remotes
+ */
+
+static int get_key_pv951(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char b;
+
+ /* poll IR chip */
+ if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ dprintk("read error\n");
+ return -EIO;
+ }
+
+ /* ignore 0xaa */
+ if (b==0xaa)
+ return 0;
+ dprintk("key %02x\n", b);
+
+ /*
+ * NOTE:
+ * lirc_i2c maps the pv951 code as:
+ * addr = 0x61D6
+ * cmd = bit_reverse (b)
+ * So, it seems that this device uses NEC extended
+ * I decided to not fix the table, due to two reasons:
+ * 1) Without the actual device, this is only a guess;
+ * 2) As the addr is not reported via I2C, nor can be changed,
+ * the device is bound to the vendor-provided RC.
+ */
+
+ *ir_key = b;
+ *ir_raw = b;
+ return 1;
+}
+
+/* Instantiate the I2C IR receiver device, if present */
+void __devinit init_bttv_i2c_ir(struct bttv *btv)
+{
+ const unsigned short addr_list[] = {
+ 0x1a, 0x18, 0x64, 0x30, 0x71,
+ I2C_CLIENT_END
+ };
+ struct i2c_board_info info;
+
+ if (0 != btv->i2c_rc)
+ return;
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ memset(&btv->init_data, 0, sizeof(btv->init_data));
+ strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+
+ switch (btv->c.type) {
+ case BTTV_BOARD_PV951:
+ btv->init_data.name = "PV951";
+ btv->init_data.get_key = get_key_pv951;
+ btv->init_data.ir_codes = RC_MAP_PV951;
+ info.addr = 0x4b;
+ break;
+ default:
+ /*
+ * The external IR receiver is at i2c address 0x34 (0x35 for
+ * reads). Future Hauppauge cards will have an internal
+ * receiver at 0x30 (0x31 for reads). In theory, both can be
+ * fitted, and Hauppauge suggest an external overrides an
+ * internal.
+ * That's why we probe 0x1a (~0x34) first. CB
+ */
+
+ i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list, NULL);
+ return;
+ }
+
+ if (btv->init_data.name)
+ info.platform_data = &btv->init_data;
+ i2c_new_device(&btv->c.i2c_adap, &info);
+
+ return;
+}
+
+int __devexit fini_bttv_i2c(struct bttv *btv)
+{
+ if (0 != btv->i2c_rc)
+ return 0;
+
+ return i2c_del_adapter(&btv->c.i2c_adap);
+}
+
+int bttv_input_init(struct bttv *btv)
+{
+ struct bttv_ir *ir;
+ char *ir_codes = NULL;
+ struct rc_dev *rc;
+ int err = -ENOMEM;
+
+ if (!btv->has_remote)
+ return -ENODEV;
+
+ ir = kzalloc(sizeof(*ir),GFP_KERNEL);
+ rc = rc_allocate_device();
+ if (!ir || !rc)
+ goto err_out_free;
+
+ /* detect & configure */
+ switch (btv->c.type) {
+ case BTTV_BOARD_AVERMEDIA:
+ case BTTV_BOARD_AVPHONE98:
+ case BTTV_BOARD_AVERMEDIA98:
+ ir_codes = RC_MAP_AVERMEDIA;
+ ir->mask_keycode = 0xf88000;
+ ir->mask_keydown = 0x010000;
+ ir->polling = 50; // ms
+ break;
+
+ case BTTV_BOARD_AVDVBT_761:
+ case BTTV_BOARD_AVDVBT_771:
+ ir_codes = RC_MAP_AVERMEDIA_DVBT;
+ ir->mask_keycode = 0x0f00c0;
+ ir->mask_keydown = 0x000020;
+ ir->polling = 50; // ms
+ break;
+
+ case BTTV_BOARD_PXELVWPLTVPAK:
+ ir_codes = RC_MAP_PIXELVIEW;
+ ir->mask_keycode = 0x003e00;
+ ir->mask_keyup = 0x010000;
+ ir->polling = 50; // ms
+ break;
+ case BTTV_BOARD_PV_M4900:
+ case BTTV_BOARD_PV_BT878P_9B:
+ case BTTV_BOARD_PV_BT878P_PLUS:
+ ir_codes = RC_MAP_PIXELVIEW;
+ ir->mask_keycode = 0x001f00;
+ ir->mask_keyup = 0x008000;
+ ir->polling = 50; // ms
+ break;
+
+ case BTTV_BOARD_WINFAST2000:
+ ir_codes = RC_MAP_WINFAST;
+ ir->mask_keycode = 0x1f8;
+ break;
+ case BTTV_BOARD_MAGICTVIEW061:
+ case BTTV_BOARD_MAGICTVIEW063:
+ ir_codes = RC_MAP_WINFAST;
+ ir->mask_keycode = 0x0008e000;
+ ir->mask_keydown = 0x00200000;
+ break;
+ case BTTV_BOARD_APAC_VIEWCOMP:
+ ir_codes = RC_MAP_APAC_VIEWCOMP;
+ ir->mask_keycode = 0x001f00;
+ ir->mask_keyup = 0x008000;
+ ir->polling = 50; // ms
+ break;
+ case BTTV_BOARD_ASKEY_CPH03X:
+ case BTTV_BOARD_CONCEPTRONIC_CTVFMI2:
+ case BTTV_BOARD_CONTVFMI:
+ ir_codes = RC_MAP_PIXELVIEW;
+ ir->mask_keycode = 0x001F00;
+ ir->mask_keyup = 0x006000;
+ ir->polling = 50; // ms
+ break;
+ case BTTV_BOARD_NEBULA_DIGITV:
+ ir_codes = RC_MAP_NEBULA;
+ ir->rc5_gpio = true;
+ break;
+ case BTTV_BOARD_MACHTV_MAGICTV:
+ ir_codes = RC_MAP_APAC_VIEWCOMP;
+ ir->mask_keycode = 0x001F00;
+ ir->mask_keyup = 0x004000;
+ ir->polling = 50; /* ms */
+ break;
+ case BTTV_BOARD_KOZUMI_KTV_01C:
+ ir_codes = RC_MAP_PCTV_SEDNA;
+ ir->mask_keycode = 0x001f00;
+ ir->mask_keyup = 0x006000;
+ ir->polling = 50; /* ms */
+ break;
+ case BTTV_BOARD_ENLTV_FM_2:
+ ir_codes = RC_MAP_ENCORE_ENLTV2;
+ ir->mask_keycode = 0x00fd00;
+ ir->mask_keyup = 0x000080;
+ ir->polling = 1; /* ms */
+ ir->last_gpio = ir_extract_bits(bttv_gpio_read(&btv->c),
+ ir->mask_keycode);
+ break;
+ }
+ if (NULL == ir_codes) {
+ dprintk("Ooops: IR config error [card=%d]\n", btv->c.type);
+ err = -ENODEV;
+ goto err_out_free;
+ }
+
+ if (ir->rc5_gpio) {
+ u32 gpio;
+ /* enable remote irq */
+ bttv_gpio_inout(&btv->c, (1 << 4), 1 << 4);
+ gpio = bttv_gpio_read(&btv->c);
+ bttv_gpio_write(&btv->c, gpio & ~(1 << 4));
+ bttv_gpio_write(&btv->c, gpio | (1 << 4));
+ } else {
+ /* init hardware-specific stuff */
+ bttv_gpio_inout(&btv->c, ir->mask_keycode | ir->mask_keydown, 0);
+ }
+
+ /* init input device */
+ ir->dev = rc;
+
+ snprintf(ir->name, sizeof(ir->name), "bttv IR (card=%d)",
+ btv->c.type);
+ snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
+ pci_name(btv->c.pci));
+
+ rc->input_name = ir->name;
+ rc->input_phys = ir->phys;
+ rc->input_id.bustype = BUS_PCI;
+ rc->input_id.version = 1;
+ if (btv->c.pci->subsystem_vendor) {
+ rc->input_id.vendor = btv->c.pci->subsystem_vendor;
+ rc->input_id.product = btv->c.pci->subsystem_device;
+ } else {
+ rc->input_id.vendor = btv->c.pci->vendor;
+ rc->input_id.product = btv->c.pci->device;
+ }
+ rc->dev.parent = &btv->c.pci->dev;
+ rc->map_name = ir_codes;
+ rc->driver_name = MODULE_NAME;
+
+ btv->remote = ir;
+ bttv_ir_start(btv, ir);
+
+ /* all done */
+ err = rc_register_device(rc);
+ if (err)
+ goto err_out_stop;
+
+ return 0;
+
+ err_out_stop:
+ bttv_ir_stop(btv);
+ btv->remote = NULL;
+ err_out_free:
+ rc_free_device(rc);
+ kfree(ir);
+ return err;
+}
+
+void bttv_input_fini(struct bttv *btv)
+{
+ if (btv->remote == NULL)
+ return;
+
+ bttv_ir_stop(btv);
+ rc_unregister_device(btv->remote->dev);
+ kfree(btv->remote);
+ btv->remote = NULL;
+}
diff --git a/drivers/media/pci/bt8xx/bttv-risc.c b/drivers/media/pci/bt8xx/bttv-risc.c
new file mode 100644
index 0000000..82cc47d
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-risc.c
@@ -0,0 +1,909 @@
+/*
+
+ bttv-risc.c -- interfaces to other kernel modules
+
+ bttv risc code handling
+ - memory management
+ - generation
+
+ (c) 2000-2003 Gerd Knorr <kraxel@bytesex.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 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <media/v4l2-ioctl.h>
+
+#include "bttvp.h"
+
+#define VCR_HACK_LINES 4
+
+/* ---------------------------------------------------------- */
+/* risc code generators */
+
+int
+bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int offset, unsigned int bpl,
+ unsigned int padding, unsigned int skip_lines,
+ unsigned int store_lines)
+{
+ u32 instructions,line,todo;
+ struct scatterlist *sg;
+ __le32 *rp;
+ int rc;
+
+ /* estimate risc mem: worst case is one write per page border +
+ one write per scan line + sync + jump (all 2 dwords). padding
+ can cause next bpl to start close to a page border. First DMA
+ region may be smaller than PAGE_SIZE */
+ instructions = skip_lines * 4;
+ instructions += (1 + ((bpl + padding) * store_lines)
+ / PAGE_SIZE + store_lines) * 8;
+ instructions += 2 * 8;
+ if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions)) < 0)
+ return rc;
+
+ /* sync instruction */
+ rp = risc->cpu;
+ *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
+ *(rp++) = cpu_to_le32(0);
+
+ while (skip_lines-- > 0) {
+ *(rp++) = cpu_to_le32(BT848_RISC_SKIP | BT848_RISC_SOL |
+ BT848_RISC_EOL | bpl);
+ }
+
+ /* scan lines */
+ sg = sglist;
+ for (line = 0; line < store_lines; line++) {
+ if ((btv->opt_vcr_hack) &&
+ (line >= (store_lines - VCR_HACK_LINES)))
+ continue;
+ while (offset && offset >= sg_dma_len(sg)) {
+ offset -= sg_dma_len(sg);
+ sg++;
+ }
+ if (bpl <= sg_dma_len(sg)-offset) {
+ /* fits into current chunk */
+ *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|
+ BT848_RISC_EOL|bpl);
+ *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
+ offset+=bpl;
+ } else {
+ /* scanline needs to be splitted */
+ todo = bpl;
+ *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|
+ (sg_dma_len(sg)-offset));
+ *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
+ todo -= (sg_dma_len(sg)-offset);
+ offset = 0;
+ sg++;
+ while (todo > sg_dma_len(sg)) {
+ *(rp++)=cpu_to_le32(BT848_RISC_WRITE|
+ sg_dma_len(sg));
+ *(rp++)=cpu_to_le32(sg_dma_address(sg));
+ todo -= sg_dma_len(sg);
+ sg++;
+ }
+ *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_EOL|
+ todo);
+ *(rp++)=cpu_to_le32(sg_dma_address(sg));
+ offset += todo;
+ }
+ offset += padding;
+ }
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+ return 0;
+}
+
+static int
+bttv_risc_planar(struct bttv *btv, struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int yoffset, unsigned int ybpl,
+ unsigned int ypadding, unsigned int ylines,
+ unsigned int uoffset, unsigned int voffset,
+ unsigned int hshift, unsigned int vshift,
+ unsigned int cpadding)
+{
+ unsigned int instructions,line,todo,ylen,chroma;
+ __le32 *rp;
+ u32 ri;
+ struct scatterlist *ysg;
+ struct scatterlist *usg;
+ struct scatterlist *vsg;
+ int topfield = (0 == yoffset);
+ int rc;
+
+ /* estimate risc mem: worst case is one write per page border +
+ one write per scan line (5 dwords)
+ plus sync + jump (2 dwords) */
+ instructions = ((3 + (ybpl + ypadding) * ylines * 2)
+ / PAGE_SIZE) + ylines;
+ instructions += 2;
+ if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*4*5)) < 0)
+ return rc;
+
+ /* sync instruction */
+ rp = risc->cpu;
+ *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3);
+ *(rp++) = cpu_to_le32(0);
+
+ /* scan lines */
+ ysg = sglist;
+ usg = sglist;
+ vsg = sglist;
+ for (line = 0; line < ylines; line++) {
+ if ((btv->opt_vcr_hack) &&
+ (line >= (ylines - VCR_HACK_LINES)))
+ continue;
+ switch (vshift) {
+ case 0:
+ chroma = 1;
+ break;
+ case 1:
+ if (topfield)
+ chroma = ((line & 1) == 0);
+ else
+ chroma = ((line & 1) == 1);
+ break;
+ case 2:
+ if (topfield)
+ chroma = ((line & 3) == 0);
+ else
+ chroma = ((line & 3) == 2);
+ break;
+ default:
+ chroma = 0;
+ break;
+ }
+
+ for (todo = ybpl; todo > 0; todo -= ylen) {
+ /* go to next sg entry if needed */
+ while (yoffset && yoffset >= sg_dma_len(ysg)) {
+ yoffset -= sg_dma_len(ysg);
+ ysg++;
+ }
+ while (uoffset && uoffset >= sg_dma_len(usg)) {
+ uoffset -= sg_dma_len(usg);
+ usg++;
+ }
+ while (voffset && voffset >= sg_dma_len(vsg)) {
+ voffset -= sg_dma_len(vsg);
+ vsg++;
+ }
+
+ /* calculate max number of bytes we can write */
+ ylen = todo;
+ if (yoffset + ylen > sg_dma_len(ysg))
+ ylen = sg_dma_len(ysg) - yoffset;
+ if (chroma) {
+ if (uoffset + (ylen>>hshift) > sg_dma_len(usg))
+ ylen = (sg_dma_len(usg) - uoffset) << hshift;
+ if (voffset + (ylen>>hshift) > sg_dma_len(vsg))
+ ylen = (sg_dma_len(vsg) - voffset) << hshift;
+ ri = BT848_RISC_WRITE123;
+ } else {
+ ri = BT848_RISC_WRITE1S23;
+ }
+ if (ybpl == todo)
+ ri |= BT848_RISC_SOL;
+ if (ylen == todo)
+ ri |= BT848_RISC_EOL;
+
+ /* write risc instruction */
+ *(rp++)=cpu_to_le32(ri | ylen);
+ *(rp++)=cpu_to_le32(((ylen >> hshift) << 16) |
+ (ylen >> hshift));
+ *(rp++)=cpu_to_le32(sg_dma_address(ysg)+yoffset);
+ yoffset += ylen;
+ if (chroma) {
+ *(rp++)=cpu_to_le32(sg_dma_address(usg)+uoffset);
+ uoffset += ylen >> hshift;
+ *(rp++)=cpu_to_le32(sg_dma_address(vsg)+voffset);
+ voffset += ylen >> hshift;
+ }
+ }
+ yoffset += ypadding;
+ if (chroma) {
+ uoffset += cpadding;
+ voffset += cpadding;
+ }
+ }
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+ return 0;
+}
+
+static int
+bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc,
+ const struct bttv_format *fmt, struct bttv_overlay *ov,
+ int skip_even, int skip_odd)
+{
+ int dwords, rc, line, maxy, start, end;
+ unsigned skip, nskips;
+ struct btcx_skiplist *skips;
+ __le32 *rp;
+ u32 ri,ra;
+ u32 addr;
+
+ /* skip list for window clipping */
+ if (NULL == (skips = kmalloc(sizeof(*skips) * ov->nclips,GFP_KERNEL)))
+ return -ENOMEM;
+
+ /* estimate risc mem: worst case is (1.5*clip+1) * lines instructions
+ + sync + jump (all 2 dwords) */
+ dwords = (3 * ov->nclips + 2) *
+ ((skip_even || skip_odd) ? (ov->w.height+1)>>1 : ov->w.height);
+ dwords += 4;
+ if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,dwords*4)) < 0) {
+ kfree(skips);
+ return rc;
+ }
+
+ /* sync instruction */
+ rp = risc->cpu;
+ *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
+ *(rp++) = cpu_to_le32(0);
+
+ addr = (unsigned long)btv->fbuf.base;
+ addr += btv->fbuf.fmt.bytesperline * ov->w.top;
+ addr += (fmt->depth >> 3) * ov->w.left;
+
+ /* scan lines */
+ for (maxy = -1, line = 0; line < ov->w.height;
+ line++, addr += btv->fbuf.fmt.bytesperline) {
+ if ((btv->opt_vcr_hack) &&
+ (line >= (ov->w.height - VCR_HACK_LINES)))
+ continue;
+ if ((line%2) == 0 && skip_even)
+ continue;
+ if ((line%2) == 1 && skip_odd)
+ continue;
+
+ /* calculate clipping */
+ if (line > maxy)
+ btcx_calc_skips(line, ov->w.width, &maxy,
+ skips, &nskips, ov->clips, ov->nclips);
+
+ /* write out risc code */
+ for (start = 0, skip = 0; start < ov->w.width; start = end) {
+ if (skip >= nskips) {
+ ri = BT848_RISC_WRITE;
+ end = ov->w.width;
+ } else if (start < skips[skip].start) {
+ ri = BT848_RISC_WRITE;
+ end = skips[skip].start;
+ } else {
+ ri = BT848_RISC_SKIP;
+ end = skips[skip].end;
+ skip++;
+ }
+ if (BT848_RISC_WRITE == ri)
+ ra = addr + (fmt->depth>>3)*start;
+ else
+ ra = 0;
+
+ if (0 == start)
+ ri |= BT848_RISC_SOL;
+ if (ov->w.width == end)
+ ri |= BT848_RISC_EOL;
+ ri |= (fmt->depth>>3) * (end-start);
+
+ *(rp++)=cpu_to_le32(ri);
+ if (0 != ra)
+ *(rp++)=cpu_to_le32(ra);
+ }
+ }
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size);
+ kfree(skips);
+ return 0;
+}
+
+/* ---------------------------------------------------------- */
+
+static void
+bttv_calc_geo_old(struct bttv *btv, struct bttv_geometry *geo,
+ int width, int height, int interleaved,
+ const struct bttv_tvnorm *tvnorm)
+{
+ u32 xsf, sr;
+ int vdelay;
+
+ int swidth = tvnorm->swidth;
+ int totalwidth = tvnorm->totalwidth;
+ int scaledtwidth = tvnorm->scaledtwidth;
+
+ if (btv->input == btv->dig) {
+ swidth = 720;
+ totalwidth = 858;
+ scaledtwidth = 858;
+ }
+
+ vdelay = tvnorm->vdelay;
+
+ xsf = (width*scaledtwidth)/swidth;
+ geo->hscale = ((totalwidth*4096UL)/xsf-4096);
+ geo->hdelay = tvnorm->hdelayx1;
+ geo->hdelay = (geo->hdelay*width)/swidth;
+ geo->hdelay &= 0x3fe;
+ sr = ((tvnorm->sheight >> (interleaved?0:1))*512)/height - 512;
+ geo->vscale = (0x10000UL-sr) & 0x1fff;
+ geo->crop = ((width>>8)&0x03) | ((geo->hdelay>>6)&0x0c) |
+ ((tvnorm->sheight>>4)&0x30) | ((vdelay>>2)&0xc0);
+ geo->vscale |= interleaved ? (BT848_VSCALE_INT<<8) : 0;
+ geo->vdelay = vdelay;
+ geo->width = width;
+ geo->sheight = tvnorm->sheight;
+ geo->vtotal = tvnorm->vtotal;
+
+ if (btv->opt_combfilter) {
+ geo->vtc = (width < 193) ? 2 : ((width < 385) ? 1 : 0);
+ geo->comb = (width < 769) ? 1 : 0;
+ } else {
+ geo->vtc = 0;
+ geo->comb = 0;
+ }
+}
+
+static void
+bttv_calc_geo (struct bttv * btv,
+ struct bttv_geometry * geo,
+ unsigned int width,
+ unsigned int height,
+ int both_fields,
+ const struct bttv_tvnorm * tvnorm,
+ const struct v4l2_rect * crop)
+{
+ unsigned int c_width;
+ unsigned int c_height;
+ u32 sr;
+
+ if ((crop->left == tvnorm->cropcap.defrect.left
+ && crop->top == tvnorm->cropcap.defrect.top
+ && crop->width == tvnorm->cropcap.defrect.width
+ && crop->height == tvnorm->cropcap.defrect.height
+ && width <= tvnorm->swidth /* see PAL-Nc et al */)
+ || btv->input == btv->dig) {
+ bttv_calc_geo_old(btv, geo, width, height,
+ both_fields, tvnorm);
+ return;
+ }
+
+ /* For bug compatibility the image size checks permit scale
+ factors > 16. See bttv_crop_calc_limits(). */
+ c_width = min((unsigned int) crop->width, width * 16);
+ c_height = min((unsigned int) crop->height, height * 16);
+
+ geo->width = width;
+ geo->hscale = (c_width * 4096U + (width >> 1)) / width - 4096;
+ /* Even to store Cb first, odd for Cr. */
+ geo->hdelay = ((crop->left * width + c_width) / c_width) & ~1;
+
+ geo->sheight = c_height;
+ geo->vdelay = crop->top - tvnorm->cropcap.bounds.top + MIN_VDELAY;
+ sr = c_height >> !both_fields;
+ sr = (sr * 512U + (height >> 1)) / height - 512;
+ geo->vscale = (0x10000UL - sr) & 0x1fff;
+ geo->vscale |= both_fields ? (BT848_VSCALE_INT << 8) : 0;
+ geo->vtotal = tvnorm->vtotal;
+
+ geo->crop = (((geo->width >> 8) & 0x03) |
+ ((geo->hdelay >> 6) & 0x0c) |
+ ((geo->sheight >> 4) & 0x30) |
+ ((geo->vdelay >> 2) & 0xc0));
+
+ if (btv->opt_combfilter) {
+ geo->vtc = (width < 193) ? 2 : ((width < 385) ? 1 : 0);
+ geo->comb = (width < 769) ? 1 : 0;
+ } else {
+ geo->vtc = 0;
+ geo->comb = 0;
+ }
+}
+
+static void
+bttv_apply_geo(struct bttv *btv, struct bttv_geometry *geo, int odd)
+{
+ int off = odd ? 0x80 : 0x00;
+
+ if (geo->comb)
+ btor(BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
+ else
+ btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
+
+ btwrite(geo->vtc, BT848_E_VTC+off);
+ btwrite(geo->hscale >> 8, BT848_E_HSCALE_HI+off);
+ btwrite(geo->hscale & 0xff, BT848_E_HSCALE_LO+off);
+ btaor((geo->vscale>>8), 0xe0, BT848_E_VSCALE_HI+off);
+ btwrite(geo->vscale & 0xff, BT848_E_VSCALE_LO+off);
+ btwrite(geo->width & 0xff, BT848_E_HACTIVE_LO+off);
+ btwrite(geo->hdelay & 0xff, BT848_E_HDELAY_LO+off);
+ btwrite(geo->sheight & 0xff, BT848_E_VACTIVE_LO+off);
+ btwrite(geo->vdelay & 0xff, BT848_E_VDELAY_LO+off);
+ btwrite(geo->crop, BT848_E_CROP+off);
+ btwrite(geo->vtotal>>8, BT848_VTOTAL_HI);
+ btwrite(geo->vtotal & 0xff, BT848_VTOTAL_LO);
+}
+
+/* ---------------------------------------------------------- */
+/* risc group / risc main loop / dma management */
+
+void
+bttv_set_dma(struct bttv *btv, int override)
+{
+ unsigned long cmd;
+ int capctl;
+
+ btv->cap_ctl = 0;
+ if (NULL != btv->curr.top) btv->cap_ctl |= 0x02;
+ if (NULL != btv->curr.bottom) btv->cap_ctl |= 0x01;
+ if (NULL != btv->cvbi) btv->cap_ctl |= 0x0c;
+
+ capctl = 0;
+ capctl |= (btv->cap_ctl & 0x03) ? 0x03 : 0x00; /* capture */
+ capctl |= (btv->cap_ctl & 0x0c) ? 0x0c : 0x00; /* vbi data */
+ capctl |= override;
+
+ d2printk("%d: capctl=%x lirq=%d top=%08llx/%08llx even=%08llx/%08llx\n",
+ btv->c.nr,capctl,btv->loop_irq,
+ btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0,
+ btv->curr.top ? (unsigned long long)btv->curr.top->top.dma : 0,
+ btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0,
+ btv->curr.bottom ? (unsigned long long)btv->curr.bottom->bottom.dma : 0);
+
+ cmd = BT848_RISC_JUMP;
+ if (btv->loop_irq) {
+ cmd |= BT848_RISC_IRQ;
+ cmd |= (btv->loop_irq & 0x0f) << 16;
+ cmd |= (~btv->loop_irq & 0x0f) << 20;
+ }
+ if (btv->curr.frame_irq || btv->loop_irq || btv->cvbi) {
+ mod_timer(&btv->timeout, jiffies+BTTV_TIMEOUT);
+ } else {
+ del_timer(&btv->timeout);
+ }
+ btv->main.cpu[RISC_SLOT_LOOP] = cpu_to_le32(cmd);
+
+ btaor(capctl, ~0x0f, BT848_CAP_CTL);
+ if (capctl) {
+ if (btv->dma_on)
+ return;
+ btwrite(btv->main.dma, BT848_RISC_STRT_ADD);
+ btor(3, BT848_GPIO_DMA_CTL);
+ btv->dma_on = 1;
+ } else {
+ if (!btv->dma_on)
+ return;
+ btand(~3, BT848_GPIO_DMA_CTL);
+ btv->dma_on = 0;
+ }
+ return;
+}
+
+int
+bttv_risc_init_main(struct bttv *btv)
+{
+ int rc;
+
+ if ((rc = btcx_riscmem_alloc(btv->c.pci,&btv->main,PAGE_SIZE)) < 0)
+ return rc;
+ dprintk("%d: risc main @ %08llx\n",
+ btv->c.nr, (unsigned long long)btv->main.dma);
+
+ btv->main.cpu[0] = cpu_to_le32(BT848_RISC_SYNC | BT848_RISC_RESYNC |
+ BT848_FIFO_STATUS_VRE);
+ btv->main.cpu[1] = cpu_to_le32(0);
+ btv->main.cpu[2] = cpu_to_le32(BT848_RISC_JUMP);
+ btv->main.cpu[3] = cpu_to_le32(btv->main.dma + (4<<2));
+
+ /* top field */
+ btv->main.cpu[4] = cpu_to_le32(BT848_RISC_JUMP);
+ btv->main.cpu[5] = cpu_to_le32(btv->main.dma + (6<<2));
+ btv->main.cpu[6] = cpu_to_le32(BT848_RISC_JUMP);
+ btv->main.cpu[7] = cpu_to_le32(btv->main.dma + (8<<2));
+
+ btv->main.cpu[8] = cpu_to_le32(BT848_RISC_SYNC | BT848_RISC_RESYNC |
+ BT848_FIFO_STATUS_VRO);
+ btv->main.cpu[9] = cpu_to_le32(0);
+
+ /* bottom field */
+ btv->main.cpu[10] = cpu_to_le32(BT848_RISC_JUMP);
+ btv->main.cpu[11] = cpu_to_le32(btv->main.dma + (12<<2));
+ btv->main.cpu[12] = cpu_to_le32(BT848_RISC_JUMP);
+ btv->main.cpu[13] = cpu_to_le32(btv->main.dma + (14<<2));
+
+ /* jump back to top field */
+ btv->main.cpu[14] = cpu_to_le32(BT848_RISC_JUMP);
+ btv->main.cpu[15] = cpu_to_le32(btv->main.dma + (0<<2));
+
+ return 0;
+}
+
+int
+bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc,
+ int irqflags)
+{
+ unsigned long cmd;
+ unsigned long next = btv->main.dma + ((slot+2) << 2);
+
+ if (NULL == risc) {
+ d2printk("%d: risc=%p slot[%d]=NULL\n", btv->c.nr, risc, slot);
+ btv->main.cpu[slot+1] = cpu_to_le32(next);
+ } else {
+ d2printk("%d: risc=%p slot[%d]=%08llx irq=%d\n",
+ btv->c.nr, risc, slot,
+ (unsigned long long)risc->dma, irqflags);
+ cmd = BT848_RISC_JUMP;
+ if (irqflags) {
+ cmd |= BT848_RISC_IRQ;
+ cmd |= (irqflags & 0x0f) << 16;
+ cmd |= (~irqflags & 0x0f) << 20;
+ }
+ risc->jmp[0] = cpu_to_le32(cmd);
+ risc->jmp[1] = cpu_to_le32(next);
+ btv->main.cpu[slot+1] = cpu_to_le32(risc->dma);
+ }
+ return 0;
+}
+
+void
+bttv_dma_free(struct videobuf_queue *q,struct bttv *btv, struct bttv_buffer *buf)
+{
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+ BUG_ON(in_interrupt());
+ videobuf_waiton(q, &buf->vb, 0, 0);
+ videobuf_dma_unmap(q->dev, dma);
+ videobuf_dma_free(dma);
+ btcx_riscmem_free(btv->c.pci,&buf->bottom);
+ btcx_riscmem_free(btv->c.pci,&buf->top);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+int
+bttv_buffer_activate_vbi(struct bttv *btv,
+ struct bttv_buffer *vbi)
+{
+ struct btcx_riscmem *top;
+ struct btcx_riscmem *bottom;
+ int top_irq_flags;
+ int bottom_irq_flags;
+
+ top = NULL;
+ bottom = NULL;
+ top_irq_flags = 0;
+ bottom_irq_flags = 0;
+
+ if (vbi) {
+ unsigned int crop, vdelay;
+
+ vbi->vb.state = VIDEOBUF_ACTIVE;
+ list_del(&vbi->vb.queue);
+
+ /* VDELAY is start of video, end of VBI capturing. */
+ crop = btread(BT848_E_CROP);
+ vdelay = btread(BT848_E_VDELAY_LO) + ((crop & 0xc0) << 2);
+
+ if (vbi->geo.vdelay > vdelay) {
+ vdelay = vbi->geo.vdelay & 0xfe;
+ crop = (crop & 0x3f) | ((vbi->geo.vdelay >> 2) & 0xc0);
+
+ btwrite(vdelay, BT848_E_VDELAY_LO);
+ btwrite(crop, BT848_E_CROP);
+ btwrite(vdelay, BT848_O_VDELAY_LO);
+ btwrite(crop, BT848_O_CROP);
+ }
+
+ if (vbi->vbi_count[0] > 0) {
+ top = &vbi->top;
+ top_irq_flags = 4;
+ }
+
+ if (vbi->vbi_count[1] > 0) {
+ top_irq_flags = 0;
+ bottom = &vbi->bottom;
+ bottom_irq_flags = 4;
+ }
+ }
+
+ bttv_risc_hook(btv, RISC_SLOT_O_VBI, top, top_irq_flags);
+ bttv_risc_hook(btv, RISC_SLOT_E_VBI, bottom, bottom_irq_flags);
+
+ return 0;
+}
+
+int
+bttv_buffer_activate_video(struct bttv *btv,
+ struct bttv_buffer_set *set)
+{
+ /* video capture */
+ if (NULL != set->top && NULL != set->bottom) {
+ if (set->top == set->bottom) {
+ set->top->vb.state = VIDEOBUF_ACTIVE;
+ if (set->top->vb.queue.next)
+ list_del(&set->top->vb.queue);
+ } else {
+ set->top->vb.state = VIDEOBUF_ACTIVE;
+ set->bottom->vb.state = VIDEOBUF_ACTIVE;
+ if (set->top->vb.queue.next)
+ list_del(&set->top->vb.queue);
+ if (set->bottom->vb.queue.next)
+ list_del(&set->bottom->vb.queue);
+ }
+ bttv_apply_geo(btv, &set->top->geo, 1);
+ bttv_apply_geo(btv, &set->bottom->geo,0);
+ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &set->top->top,
+ set->top_irq);
+ bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &set->bottom->bottom,
+ set->frame_irq);
+ btaor((set->top->btformat & 0xf0) | (set->bottom->btformat & 0x0f),
+ ~0xff, BT848_COLOR_FMT);
+ btaor((set->top->btswap & 0x0a) | (set->bottom->btswap & 0x05),
+ ~0x0f, BT848_COLOR_CTL);
+ } else if (NULL != set->top) {
+ set->top->vb.state = VIDEOBUF_ACTIVE;
+ if (set->top->vb.queue.next)
+ list_del(&set->top->vb.queue);
+ bttv_apply_geo(btv, &set->top->geo,1);
+ bttv_apply_geo(btv, &set->top->geo,0);
+ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &set->top->top,
+ set->frame_irq);
+ bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL, 0);
+ btaor(set->top->btformat & 0xff, ~0xff, BT848_COLOR_FMT);
+ btaor(set->top->btswap & 0x0f, ~0x0f, BT848_COLOR_CTL);
+ } else if (NULL != set->bottom) {
+ set->bottom->vb.state = VIDEOBUF_ACTIVE;
+ if (set->bottom->vb.queue.next)
+ list_del(&set->bottom->vb.queue);
+ bttv_apply_geo(btv, &set->bottom->geo,1);
+ bttv_apply_geo(btv, &set->bottom->geo,0);
+ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
+ bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &set->bottom->bottom,
+ set->frame_irq);
+ btaor(set->bottom->btformat & 0xff, ~0xff, BT848_COLOR_FMT);
+ btaor(set->bottom->btswap & 0x0f, ~0x0f, BT848_COLOR_CTL);
+ } else {
+ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
+ bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL, 0);
+ }
+ return 0;
+}
+
+/* ---------------------------------------------------------- */
+
+/* calculate geometry, build risc code */
+int
+bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf)
+{
+ const struct bttv_tvnorm *tvnorm = bttv_tvnorms + buf->tvnorm;
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+ dprintk("%d: buffer field: %s format: %s size: %dx%d\n",
+ btv->c.nr, v4l2_field_names[buf->vb.field],
+ buf->fmt->name, buf->vb.width, buf->vb.height);
+
+ /* packed pixel modes */
+ if (buf->fmt->flags & FORMAT_FLAGS_PACKED) {
+ int bpl = (buf->fmt->depth >> 3) * buf->vb.width;
+ int bpf = bpl * (buf->vb.height >> 1);
+
+ bttv_calc_geo(btv,&buf->geo,buf->vb.width,buf->vb.height,
+ V4L2_FIELD_HAS_BOTH(buf->vb.field),
+ tvnorm,&buf->crop);
+
+ switch (buf->vb.field) {
+ case V4L2_FIELD_TOP:
+ bttv_risc_packed(btv,&buf->top,dma->sglist,
+ /* offset */ 0,bpl,
+ /* padding */ 0,/* skip_lines */ 0,
+ buf->vb.height);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ bttv_risc_packed(btv,&buf->bottom,dma->sglist,
+ 0,bpl,0,0,buf->vb.height);
+ break;
+ case V4L2_FIELD_INTERLACED:
+ bttv_risc_packed(btv,&buf->top,dma->sglist,
+ 0,bpl,bpl,0,buf->vb.height >> 1);
+ bttv_risc_packed(btv,&buf->bottom,dma->sglist,
+ bpl,bpl,bpl,0,buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ bttv_risc_packed(btv,&buf->top,dma->sglist,
+ 0,bpl,0,0,buf->vb.height >> 1);
+ bttv_risc_packed(btv,&buf->bottom,dma->sglist,
+ bpf,bpl,0,0,buf->vb.height >> 1);
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ /* planar modes */
+ if (buf->fmt->flags & FORMAT_FLAGS_PLANAR) {
+ int uoffset, voffset;
+ int ypadding, cpadding, lines;
+
+ /* calculate chroma offsets */
+ uoffset = buf->vb.width * buf->vb.height;
+ voffset = buf->vb.width * buf->vb.height;
+ if (buf->fmt->flags & FORMAT_FLAGS_CrCb) {
+ /* Y-Cr-Cb plane order */
+ uoffset >>= buf->fmt->hshift;
+ uoffset >>= buf->fmt->vshift;
+ uoffset += voffset;
+ } else {
+ /* Y-Cb-Cr plane order */
+ voffset >>= buf->fmt->hshift;
+ voffset >>= buf->fmt->vshift;
+ voffset += uoffset;
+ }
+
+ switch (buf->vb.field) {
+ case V4L2_FIELD_TOP:
+ bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+ buf->vb.height,/* both_fields */ 0,
+ tvnorm,&buf->crop);
+ bttv_risc_planar(btv, &buf->top, dma->sglist,
+ 0,buf->vb.width,0,buf->vb.height,
+ uoffset,voffset,buf->fmt->hshift,
+ buf->fmt->vshift,0);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+ buf->vb.height,0,
+ tvnorm,&buf->crop);
+ bttv_risc_planar(btv, &buf->bottom, dma->sglist,
+ 0,buf->vb.width,0,buf->vb.height,
+ uoffset,voffset,buf->fmt->hshift,
+ buf->fmt->vshift,0);
+ break;
+ case V4L2_FIELD_INTERLACED:
+ bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+ buf->vb.height,1,
+ tvnorm,&buf->crop);
+ lines = buf->vb.height >> 1;
+ ypadding = buf->vb.width;
+ cpadding = buf->vb.width >> buf->fmt->hshift;
+ bttv_risc_planar(btv,&buf->top,
+ dma->sglist,
+ 0,buf->vb.width,ypadding,lines,
+ uoffset,voffset,
+ buf->fmt->hshift,
+ buf->fmt->vshift,
+ cpadding);
+ bttv_risc_planar(btv,&buf->bottom,
+ dma->sglist,
+ ypadding,buf->vb.width,ypadding,lines,
+ uoffset+cpadding,
+ voffset+cpadding,
+ buf->fmt->hshift,
+ buf->fmt->vshift,
+ cpadding);
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+ buf->vb.height,1,
+ tvnorm,&buf->crop);
+ lines = buf->vb.height >> 1;
+ ypadding = buf->vb.width;
+ cpadding = buf->vb.width >> buf->fmt->hshift;
+ bttv_risc_planar(btv,&buf->top,
+ dma->sglist,
+ 0,buf->vb.width,0,lines,
+ uoffset >> 1,
+ voffset >> 1,
+ buf->fmt->hshift,
+ buf->fmt->vshift,
+ 0);
+ bttv_risc_planar(btv,&buf->bottom,
+ dma->sglist,
+ lines * ypadding,buf->vb.width,0,lines,
+ lines * ypadding + (uoffset >> 1),
+ lines * ypadding + (voffset >> 1),
+ buf->fmt->hshift,
+ buf->fmt->vshift,
+ 0);
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ /* raw data */
+ if (buf->fmt->flags & FORMAT_FLAGS_RAW) {
+ /* build risc code */
+ buf->vb.field = V4L2_FIELD_SEQ_TB;
+ bttv_calc_geo(btv,&buf->geo,tvnorm->swidth,tvnorm->sheight,
+ 1,tvnorm,&buf->crop);
+ bttv_risc_packed(btv, &buf->top, dma->sglist,
+ /* offset */ 0, RAW_BPL, /* padding */ 0,
+ /* skip_lines */ 0, RAW_LINES);
+ bttv_risc_packed(btv, &buf->bottom, dma->sglist,
+ buf->vb.size/2 , RAW_BPL, 0, 0, RAW_LINES);
+ }
+
+ /* copy format info */
+ buf->btformat = buf->fmt->btformat;
+ buf->btswap = buf->fmt->btswap;
+ return 0;
+}
+
+/* ---------------------------------------------------------- */
+
+/* calculate geometry, build risc code */
+int
+bttv_overlay_risc(struct bttv *btv,
+ struct bttv_overlay *ov,
+ const struct bttv_format *fmt,
+ struct bttv_buffer *buf)
+{
+ /* check interleave, bottom+top fields */
+ dprintk("%d: overlay fields: %s format: %s size: %dx%d\n",
+ btv->c.nr, v4l2_field_names[buf->vb.field],
+ fmt->name, ov->w.width, ov->w.height);
+
+ /* calculate geometry */
+ bttv_calc_geo(btv,&buf->geo,ov->w.width,ov->w.height,
+ V4L2_FIELD_HAS_BOTH(ov->field),
+ &bttv_tvnorms[ov->tvnorm],&buf->crop);
+
+ /* build risc code */
+ switch (ov->field) {
+ case V4L2_FIELD_TOP:
+ bttv_risc_overlay(btv, &buf->top, fmt, ov, 0, 0);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 0, 0);
+ break;
+ case V4L2_FIELD_INTERLACED:
+ bttv_risc_overlay(btv, &buf->top, fmt, ov, 0, 1);
+ bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 1, 0);
+ break;
+ default:
+ BUG();
+ }
+
+ /* copy format info */
+ buf->btformat = fmt->btformat;
+ buf->btswap = fmt->btswap;
+ buf->vb.field = ov->field;
+ return 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/bttv-vbi.c b/drivers/media/pci/bt8xx/bttv-vbi.c
new file mode 100644
index 0000000..b433267
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-vbi.c
@@ -0,0 +1,459 @@
+/*
+
+ bttv - Bt848 frame grabber driver
+ vbi interface
+
+ (c) 2002 Gerd Knorr <kraxel@bytesex.org>
+
+ Copyright (C) 2005, 2006 Michael H. Schimek <mschimek@gmx.at>
+ Sponsored by OPQ Systems AB
+
+ 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/kdev_t.h>
+#include <media/v4l2-ioctl.h>
+#include <asm/io.h>
+#include "bttvp.h"
+
+/* Offset from line sync pulse leading edge (0H) to start of VBI capture,
+ in fCLKx2 pixels. According to the datasheet, VBI capture starts
+ VBI_HDELAY fCLKx1 pixels from the tailing edgeof /HRESET, and /HRESET
+ is 64 fCLKx1 pixels wide. VBI_HDELAY is set to 0, so this should be
+ (64 + 0) * 2 = 128 fCLKx2 pixels. But it's not! The datasheet is
+ Just Plain Wrong. The real value appears to be different for
+ different revisions of the bt8x8 chips, and to be affected by the
+ horizontal scaling factor. Experimentally, the value is measured
+ to be about 244. */
+#define VBI_OFFSET 244
+
+/* 2048 for compatibility with earlier driver versions. The driver
+ really stores 1024 + tvnorm->vbipack * 4 samples per line in the
+ buffer. Note tvnorm->vbipack is <= 0xFF (limit of VBIPACK_LO + HI
+ is 0x1FF DWORDs) and VBI read()s store a frame counter in the last
+ four bytes of the VBI image. */
+#define VBI_BPL 2048
+
+/* Compatibility. */
+#define VBI_DEFLINES 16
+
+static unsigned int vbibufs = 4;
+static unsigned int vbi_debug;
+
+module_param(vbibufs, int, 0444);
+module_param(vbi_debug, int, 0644);
+MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32, default 4");
+MODULE_PARM_DESC(vbi_debug,"vbi code debug messages, default is 0 (no)");
+
+#ifdef dprintk
+# undef dprintk
+#endif
+#define dprintk(fmt, ...) \
+do { \
+ if (vbi_debug) \
+ pr_debug("%d: " fmt, btv->c.nr, ##__VA_ARGS__); \
+} while (0)
+
+#define IMAGE_SIZE(fmt) \
+ (((fmt)->count[0] + (fmt)->count[1]) * (fmt)->samples_per_line)
+
+/* ----------------------------------------------------------------------- */
+/* vbi risc code + mm */
+
+static int vbi_buffer_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned int *size)
+{
+ struct bttv_fh *fh = q->priv_data;
+ struct bttv *btv = fh->btv;
+
+ if (0 == *count)
+ *count = vbibufs;
+
+ *size = IMAGE_SIZE(&fh->vbi_fmt.fmt);
+
+ dprintk("setup: samples=%u start=%d,%d count=%u,%u\n",
+ fh->vbi_fmt.fmt.samples_per_line,
+ fh->vbi_fmt.fmt.start[0],
+ fh->vbi_fmt.fmt.start[1],
+ fh->vbi_fmt.fmt.count[0],
+ fh->vbi_fmt.fmt.count[1]);
+
+ return 0;
+}
+
+static int vbi_buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct bttv_fh *fh = q->priv_data;
+ struct bttv *btv = fh->btv;
+ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+ const struct bttv_tvnorm *tvnorm;
+ unsigned int skip_lines0, skip_lines1, min_vdelay;
+ int redo_dma_risc;
+ int rc;
+
+ buf->vb.size = IMAGE_SIZE(&fh->vbi_fmt.fmt);
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ tvnorm = fh->vbi_fmt.tvnorm;
+
+ /* There's no VBI_VDELAY register, RISC must skip the lines
+ we don't want. With default parameters we skip zero lines
+ as earlier driver versions did. The driver permits video
+ standard changes while capturing, so we use vbi_fmt.tvnorm
+ instead of btv->tvnorm to skip zero lines after video
+ standard changes as well. */
+
+ skip_lines0 = 0;
+ skip_lines1 = 0;
+
+ if (fh->vbi_fmt.fmt.count[0] > 0)
+ skip_lines0 = max(0, (fh->vbi_fmt.fmt.start[0]
+ - tvnorm->vbistart[0]));
+ if (fh->vbi_fmt.fmt.count[1] > 0)
+ skip_lines1 = max(0, (fh->vbi_fmt.fmt.start[1]
+ - tvnorm->vbistart[1]));
+
+ redo_dma_risc = 0;
+
+ if (buf->vbi_skip[0] != skip_lines0 ||
+ buf->vbi_skip[1] != skip_lines1 ||
+ buf->vbi_count[0] != fh->vbi_fmt.fmt.count[0] ||
+ buf->vbi_count[1] != fh->vbi_fmt.fmt.count[1]) {
+ buf->vbi_skip[0] = skip_lines0;
+ buf->vbi_skip[1] = skip_lines1;
+ buf->vbi_count[0] = fh->vbi_fmt.fmt.count[0];
+ buf->vbi_count[1] = fh->vbi_fmt.fmt.count[1];
+ redo_dma_risc = 1;
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ redo_dma_risc = 1;
+ if (0 != (rc = videobuf_iolock(q, &buf->vb, NULL)))
+ goto fail;
+ }
+
+ if (redo_dma_risc) {
+ unsigned int bpl, padding, offset;
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+ bpl = 2044; /* max. vbipack */
+ padding = VBI_BPL - bpl;
+
+ if (fh->vbi_fmt.fmt.count[0] > 0) {
+ rc = bttv_risc_packed(btv, &buf->top,
+ dma->sglist,
+ /* offset */ 0, bpl,
+ padding, skip_lines0,
+ fh->vbi_fmt.fmt.count[0]);
+ if (0 != rc)
+ goto fail;
+ }
+
+ if (fh->vbi_fmt.fmt.count[1] > 0) {
+ offset = fh->vbi_fmt.fmt.count[0] * VBI_BPL;
+
+ rc = bttv_risc_packed(btv, &buf->bottom,
+ dma->sglist,
+ offset, bpl,
+ padding, skip_lines1,
+ fh->vbi_fmt.fmt.count[1]);
+ if (0 != rc)
+ goto fail;
+ }
+ }
+
+ /* VBI capturing ends at VDELAY, start of video capturing,
+ no matter where the RISC program ends. VDELAY minimum is 2,
+ bounds.top is the corresponding first field line number
+ times two. VDELAY counts half field lines. */
+ min_vdelay = MIN_VDELAY;
+ if (fh->vbi_fmt.end >= tvnorm->cropcap.bounds.top)
+ min_vdelay += fh->vbi_fmt.end - tvnorm->cropcap.bounds.top;
+
+ /* For bttv_buffer_activate_vbi(). */
+ buf->geo.vdelay = min_vdelay;
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ buf->vb.field = field;
+ dprintk("buf prepare %p: top=%p bottom=%p field=%s\n",
+ vb, &buf->top, &buf->bottom,
+ v4l2_field_names[buf->vb.field]);
+ return 0;
+
+ fail:
+ bttv_dma_free(q,btv,buf);
+ return rc;
+}
+
+static void
+vbi_buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct bttv_fh *fh = q->priv_data;
+ struct bttv *btv = fh->btv;
+ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+
+ dprintk("queue %p\n",vb);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue,&btv->vcapture);
+ if (NULL == btv->cvbi) {
+ fh->btv->loop_irq |= 4;
+ bttv_set_dma(btv,0x0c);
+ }
+}
+
+static void vbi_buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct bttv_fh *fh = q->priv_data;
+ struct bttv *btv = fh->btv;
+ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+
+ dprintk("free %p\n",vb);
+ bttv_dma_free(q,fh->btv,buf);
+}
+
+struct videobuf_queue_ops bttv_vbi_qops = {
+ .buf_setup = vbi_buffer_setup,
+ .buf_prepare = vbi_buffer_prepare,
+ .buf_queue = vbi_buffer_queue,
+ .buf_release = vbi_buffer_release,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int try_fmt(struct v4l2_vbi_format *f, const struct bttv_tvnorm *tvnorm,
+ __s32 crop_start)
+{
+ __s32 min_start, max_start, max_end, f2_offset;
+ unsigned int i;
+
+ /* For compatibility with earlier driver versions we must pretend
+ the VBI and video capture window may overlap. In reality RISC
+ magic aborts VBI capturing at the first line of video capturing,
+ leaving the rest of the buffer unchanged, usually all zero.
+ VBI capturing must always start before video capturing. >> 1
+ because cropping counts field lines times two. */
+ min_start = tvnorm->vbistart[0];
+ max_start = (crop_start >> 1) - 1;
+ max_end = (tvnorm->cropcap.bounds.top
+ + tvnorm->cropcap.bounds.height) >> 1;
+
+ if (min_start > max_start)
+ return -EBUSY;
+
+ BUG_ON(max_start >= max_end);
+
+ f->sampling_rate = tvnorm->Fsc;
+ f->samples_per_line = VBI_BPL;
+ f->sample_format = V4L2_PIX_FMT_GREY;
+ f->offset = VBI_OFFSET;
+
+ f2_offset = tvnorm->vbistart[1] - tvnorm->vbistart[0];
+
+ for (i = 0; i < 2; ++i) {
+ if (0 == f->count[i]) {
+ /* No data from this field. We leave f->start[i]
+ alone because VIDIOCSVBIFMT is w/o and EINVALs
+ when a driver does not support exactly the
+ requested parameters. */
+ } else {
+ s64 start, count;
+
+ start = clamp(f->start[i], min_start, max_start);
+ /* s64 to prevent overflow. */
+ count = (s64) f->start[i] + f->count[i] - start;
+ f->start[i] = start;
+ f->count[i] = clamp(count, (s64) 1,
+ max_end - start);
+ }
+
+ min_start += f2_offset;
+ max_start += f2_offset;
+ max_end += f2_offset;
+ }
+
+ if (0 == (f->count[0] | f->count[1])) {
+ /* As in earlier driver versions. */
+ f->start[0] = tvnorm->vbistart[0];
+ f->start[1] = tvnorm->vbistart[1];
+ f->count[0] = 1;
+ f->count[1] = 1;
+ }
+
+ f->flags = 0;
+
+ f->reserved[0] = 0;
+ f->reserved[1] = 0;
+
+ return 0;
+}
+
+int bttv_try_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ const struct bttv_tvnorm *tvnorm;
+ __s32 crop_start;
+
+ mutex_lock(&btv->lock);
+
+ tvnorm = &bttv_tvnorms[btv->tvnorm];
+ crop_start = btv->crop_start;
+
+ mutex_unlock(&btv->lock);
+
+ return try_fmt(&frt->fmt.vbi, tvnorm, crop_start);
+}
+
+
+int bttv_s_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ const struct bttv_tvnorm *tvnorm;
+ __s32 start1, end;
+ int rc;
+
+ mutex_lock(&btv->lock);
+
+ rc = -EBUSY;
+ if (fh->resources & RESOURCE_VBI)
+ goto fail;
+
+ tvnorm = &bttv_tvnorms[btv->tvnorm];
+
+ rc = try_fmt(&frt->fmt.vbi, tvnorm, btv->crop_start);
+ if (0 != rc)
+ goto fail;
+
+ start1 = frt->fmt.vbi.start[1] - tvnorm->vbistart[1] +
+ tvnorm->vbistart[0];
+
+ /* First possible line of video capturing. Should be
+ max(f->start[0] + f->count[0], start1 + f->count[1]) * 2
+ when capturing both fields. But for compatibility we must
+ pretend the VBI and video capture window may overlap,
+ so end = start + 1, the lowest possible value, times two
+ because vbi_fmt.end counts field lines times two. */
+ end = max(frt->fmt.vbi.start[0], start1) * 2 + 2;
+
+ mutex_lock(&fh->vbi.vb_lock);
+
+ fh->vbi_fmt.fmt = frt->fmt.vbi;
+ fh->vbi_fmt.tvnorm = tvnorm;
+ fh->vbi_fmt.end = end;
+
+ mutex_unlock(&fh->vbi.vb_lock);
+
+ rc = 0;
+
+ fail:
+ mutex_unlock(&btv->lock);
+
+ return rc;
+}
+
+
+int bttv_g_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt)
+{
+ struct bttv_fh *fh = f;
+ const struct bttv_tvnorm *tvnorm;
+
+ frt->fmt.vbi = fh->vbi_fmt.fmt;
+
+ tvnorm = &bttv_tvnorms[fh->btv->tvnorm];
+
+ if (tvnorm != fh->vbi_fmt.tvnorm) {
+ __s32 max_end;
+ unsigned int i;
+
+ /* As in vbi_buffer_prepare() this imitates the
+ behaviour of earlier driver versions after video
+ standard changes, with default parameters anyway. */
+
+ max_end = (tvnorm->cropcap.bounds.top
+ + tvnorm->cropcap.bounds.height) >> 1;
+
+ frt->fmt.vbi.sampling_rate = tvnorm->Fsc;
+
+ for (i = 0; i < 2; ++i) {
+ __s32 new_start;
+
+ new_start = frt->fmt.vbi.start[i]
+ + tvnorm->vbistart[i]
+ - fh->vbi_fmt.tvnorm->vbistart[i];
+
+ frt->fmt.vbi.start[i] = min(new_start, max_end - 1);
+ frt->fmt.vbi.count[i] =
+ min((__s32) frt->fmt.vbi.count[i],
+ max_end - frt->fmt.vbi.start[i]);
+
+ max_end += tvnorm->vbistart[1]
+ - tvnorm->vbistart[0];
+ }
+ }
+ return 0;
+}
+
+void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, unsigned int norm)
+{
+ const struct bttv_tvnorm *tvnorm;
+ unsigned int real_samples_per_line;
+ unsigned int real_count;
+
+ tvnorm = &bttv_tvnorms[norm];
+
+ f->fmt.sampling_rate = tvnorm->Fsc;
+ f->fmt.samples_per_line = VBI_BPL;
+ f->fmt.sample_format = V4L2_PIX_FMT_GREY;
+ f->fmt.offset = VBI_OFFSET;
+ f->fmt.start[0] = tvnorm->vbistart[0];
+ f->fmt.start[1] = tvnorm->vbistart[1];
+ f->fmt.count[0] = VBI_DEFLINES;
+ f->fmt.count[1] = VBI_DEFLINES;
+ f->fmt.flags = 0;
+ f->fmt.reserved[0] = 0;
+ f->fmt.reserved[1] = 0;
+
+ /* For compatibility the buffer size must be 2 * VBI_DEFLINES *
+ VBI_BPL regardless of the current video standard. */
+ real_samples_per_line = 1024 + tvnorm->vbipack * 4;
+ real_count = ((tvnorm->cropcap.defrect.top >> 1)
+ - tvnorm->vbistart[0]);
+
+ BUG_ON(real_samples_per_line > VBI_BPL);
+ BUG_ON(real_count > VBI_DEFLINES);
+
+ f->tvnorm = tvnorm;
+
+ /* See bttv_vbi_fmt_set(). */
+ f->end = tvnorm->vbistart[0] * 2 + 2;
+}
+
+/* ----------------------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/bttv.h b/drivers/media/pci/bt8xx/bttv.h
new file mode 100644
index 0000000..79a1124
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv.h
@@ -0,0 +1,376 @@
+/*
+ *
+ * bttv - Bt848 frame grabber driver
+ *
+ * card ID's and external interfaces of the bttv driver
+ * basically stuff needed by other drivers (i2c, lirc, ...)
+ * and is supported not to change much over time.
+ *
+ * Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ * (c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ *
+ */
+
+#ifndef _BTTV_H_
+#define _BTTV_H_
+
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <media/v4l2-device.h>
+#include <media/i2c-addr.h>
+#include <media/tuner.h>
+
+/* ---------------------------------------------------------- */
+/* exported by bttv-cards.c */
+
+#define BTTV_BOARD_UNKNOWN 0x00
+#define BTTV_BOARD_MIRO 0x01
+#define BTTV_BOARD_HAUPPAUGE 0x02
+#define BTTV_BOARD_STB 0x03
+#define BTTV_BOARD_INTEL 0x04
+#define BTTV_BOARD_DIAMOND 0x05
+#define BTTV_BOARD_AVERMEDIA 0x06
+#define BTTV_BOARD_MATRIX_VISION 0x07
+#define BTTV_BOARD_FLYVIDEO 0x08
+#define BTTV_BOARD_TURBOTV 0x09
+#define BTTV_BOARD_HAUPPAUGE878 0x0a
+#define BTTV_BOARD_MIROPRO 0x0b
+#define BTTV_BOARD_ADSTECH_TV 0x0c
+#define BTTV_BOARD_AVERMEDIA98 0x0d
+#define BTTV_BOARD_VHX 0x0e
+#define BTTV_BOARD_ZOLTRIX 0x0f
+#define BTTV_BOARD_PIXVIEWPLAYTV 0x10
+#define BTTV_BOARD_WINVIEW_601 0x11
+#define BTTV_BOARD_AVEC_INTERCAP 0x12
+#define BTTV_BOARD_LIFE_FLYKIT 0x13
+#define BTTV_BOARD_CEI_RAFFLES 0x14
+#define BTTV_BOARD_CONFERENCETV 0x15
+#define BTTV_BOARD_PHOEBE_TVMAS 0x16
+#define BTTV_BOARD_MODTEC_205 0x17
+#define BTTV_BOARD_MAGICTVIEW061 0x18
+#define BTTV_BOARD_VOBIS_BOOSTAR 0x19
+#define BTTV_BOARD_HAUPPAUG_WCAM 0x1a
+#define BTTV_BOARD_MAXI 0x1b
+#define BTTV_BOARD_TERRATV 0x1c
+#define BTTV_BOARD_PXC200 0x1d
+#define BTTV_BOARD_FLYVIDEO_98 0x1e
+#define BTTV_BOARD_IPROTV 0x1f
+#define BTTV_BOARD_INTEL_C_S_PCI 0x20
+#define BTTV_BOARD_TERRATVALUE 0x21
+#define BTTV_BOARD_WINFAST2000 0x22
+#define BTTV_BOARD_CHRONOS_VS2 0x23
+#define BTTV_BOARD_TYPHOON_TVIEW 0x24
+#define BTTV_BOARD_PXELVWPLTVPRO 0x25
+#define BTTV_BOARD_MAGICTVIEW063 0x26
+#define BTTV_BOARD_PINNACLE 0x27
+#define BTTV_BOARD_STB2 0x28
+#define BTTV_BOARD_AVPHONE98 0x29
+#define BTTV_BOARD_PV951 0x2a
+#define BTTV_BOARD_ONAIR_TV 0x2b
+#define BTTV_BOARD_SIGMA_TVII_FM 0x2c
+#define BTTV_BOARD_MATRIX_VISION2 0x2d
+#define BTTV_BOARD_ZOLTRIX_GENIE 0x2e
+#define BTTV_BOARD_TERRATVRADIO 0x2f
+#define BTTV_BOARD_DYNALINK 0x30
+#define BTTV_BOARD_GVBCTV3PCI 0x31
+#define BTTV_BOARD_PXELVWPLTVPAK 0x32
+#define BTTV_BOARD_EAGLE 0x33
+#define BTTV_BOARD_PINNACLEPRO 0x34
+#define BTTV_BOARD_TVIEW_RDS_FM 0x35
+#define BTTV_BOARD_LIFETEC_9415 0x36
+#define BTTV_BOARD_BESTBUY_EASYTV 0x37
+#define BTTV_BOARD_FLYVIDEO_98FM 0x38
+#define BTTV_BOARD_GRANDTEC 0x39
+#define BTTV_BOARD_ASKEY_CPH060 0x3a
+#define BTTV_BOARD_ASKEY_CPH03X 0x3b
+#define BTTV_BOARD_MM100PCTV 0x3c
+#define BTTV_BOARD_GMV1 0x3d
+#define BTTV_BOARD_BESTBUY_EASYTV2 0x3e
+#define BTTV_BOARD_ATI_TVWONDER 0x3f
+#define BTTV_BOARD_ATI_TVWONDERVE 0x40
+#define BTTV_BOARD_FLYVIDEO2000 0x41
+#define BTTV_BOARD_TERRATVALUER 0x42
+#define BTTV_BOARD_GVBCTV4PCI 0x43
+#define BTTV_BOARD_VOODOOTV_FM 0x44
+#define BTTV_BOARD_AIMMS 0x45
+#define BTTV_BOARD_PV_BT878P_PLUS 0x46
+#define BTTV_BOARD_FLYVIDEO98EZ 0x47
+#define BTTV_BOARD_PV_BT878P_9B 0x48
+#define BTTV_BOARD_SENSORAY311_611 0x49
+#define BTTV_BOARD_RV605 0x4a
+#define BTTV_BOARD_POWERCLR_MTV878 0x4b
+#define BTTV_BOARD_WINDVR 0x4c
+#define BTTV_BOARD_GRANDTEC_MULTI 0x4d
+#define BTTV_BOARD_KWORLD 0x4e
+#define BTTV_BOARD_DSP_TCVIDEO 0x4f
+#define BTTV_BOARD_HAUPPAUGEPVR 0x50
+#define BTTV_BOARD_GVBCTV5PCI 0x51
+#define BTTV_BOARD_OSPREY1x0 0x52
+#define BTTV_BOARD_OSPREY1x0_848 0x53
+#define BTTV_BOARD_OSPREY101_848 0x54
+#define BTTV_BOARD_OSPREY1x1 0x55
+#define BTTV_BOARD_OSPREY1x1_SVID 0x56
+#define BTTV_BOARD_OSPREY2xx 0x57
+#define BTTV_BOARD_OSPREY2x0_SVID 0x58
+#define BTTV_BOARD_OSPREY2x0 0x59
+#define BTTV_BOARD_OSPREY500 0x5a
+#define BTTV_BOARD_OSPREY540 0x5b
+#define BTTV_BOARD_OSPREY2000 0x5c
+#define BTTV_BOARD_IDS_EAGLE 0x5d
+#define BTTV_BOARD_PINNACLESAT 0x5e
+#define BTTV_BOARD_FORMAC_PROTV 0x5f
+#define BTTV_BOARD_MACHTV 0x60
+#define BTTV_BOARD_EURESYS_PICOLO 0x61
+#define BTTV_BOARD_PV150 0x62
+#define BTTV_BOARD_AD_TVK503 0x63
+#define BTTV_BOARD_HERCULES_SM_TV 0x64
+#define BTTV_BOARD_PACETV 0x65
+#define BTTV_BOARD_IVC200 0x66
+#define BTTV_BOARD_XGUARD 0x67
+#define BTTV_BOARD_NEBULA_DIGITV 0x68
+#define BTTV_BOARD_PV143 0x69
+#define BTTV_BOARD_VD009X1_VD011_MINIDIN 0x6a
+#define BTTV_BOARD_VD009X1_VD011_COMBI 0x6b
+#define BTTV_BOARD_VD009_MINIDIN 0x6c
+#define BTTV_BOARD_VD009_COMBI 0x6d
+#define BTTV_BOARD_IVC100 0x6e
+#define BTTV_BOARD_IVC120 0x6f
+#define BTTV_BOARD_PC_HDTV 0x70
+#define BTTV_BOARD_TWINHAN_DST 0x71
+#define BTTV_BOARD_WINFASTVC100 0x72
+#define BTTV_BOARD_TEV560 0x73
+#define BTTV_BOARD_SIMUS_GVC1100 0x74
+#define BTTV_BOARD_NGSTV_PLUS 0x75
+#define BTTV_BOARD_LMLBT4 0x76
+#define BTTV_BOARD_TEKRAM_M205 0x77
+#define BTTV_BOARD_CONTVFMI 0x78
+#define BTTV_BOARD_PICOLO_TETRA_CHIP 0x79
+#define BTTV_BOARD_SPIRIT_TV 0x7a
+#define BTTV_BOARD_AVDVBT_771 0x7b
+#define BTTV_BOARD_AVDVBT_761 0x7c
+#define BTTV_BOARD_MATRIX_VISIONSQ 0x7d
+#define BTTV_BOARD_MATRIX_VISIONSLC 0x7e
+#define BTTV_BOARD_APAC_VIEWCOMP 0x7f
+#define BTTV_BOARD_DVICO_DVBT_LITE 0x80
+#define BTTV_BOARD_VGEAR_MYVCD 0x81
+#define BTTV_BOARD_SUPER_TV 0x82
+#define BTTV_BOARD_TIBET_CS16 0x83
+#define BTTV_BOARD_KODICOM_4400R 0x84
+#define BTTV_BOARD_KODICOM_4400R_SL 0x85
+#define BTTV_BOARD_ADLINK_RTV24 0x86
+#define BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE 0x87
+#define BTTV_BOARD_ACORP_Y878F 0x88
+#define BTTV_BOARD_CONCEPTRONIC_CTVFMI2 0x89
+#define BTTV_BOARD_PV_BT878P_2E 0x8a
+#define BTTV_BOARD_PV_M4900 0x8b
+#define BTTV_BOARD_OSPREY440 0x8c
+#define BTTV_BOARD_ASOUND_SKYEYE 0x8d
+#define BTTV_BOARD_SABRENT_TVFM 0x8e
+#define BTTV_BOARD_HAUPPAUGE_IMPACTVCB 0x8f
+#define BTTV_BOARD_MACHTV_MAGICTV 0x90
+#define BTTV_BOARD_SSAI_SECURITY 0x91
+#define BTTV_BOARD_SSAI_ULTRASOUND 0x92
+#define BTTV_BOARD_VOODOOTV_200 0x93
+#define BTTV_BOARD_DVICO_FUSIONHDTV_2 0x94
+#define BTTV_BOARD_TYPHOON_TVTUNERPCI 0x95
+#define BTTV_BOARD_GEOVISION_GV600 0x96
+#define BTTV_BOARD_KOZUMI_KTV_01C 0x97
+#define BTTV_BOARD_ENLTV_FM_2 0x98
+#define BTTV_BOARD_VD012 0x99
+#define BTTV_BOARD_VD012_X1 0x9a
+#define BTTV_BOARD_VD012_X2 0x9b
+#define BTTV_BOARD_IVCE8784 0x9c
+#define BTTV_BOARD_GEOVISION_GV800S 0x9d
+#define BTTV_BOARD_GEOVISION_GV800S_SL 0x9e
+#define BTTV_BOARD_PV183 0x9f
+#define BTTV_BOARD_TVT_TD3116 0xa0
+#define BTTV_BOARD_APOSONIC_WDVR 0xa1
+
+/* more card-specific defines */
+#define PT2254_L_CHANNEL 0x10
+#define PT2254_R_CHANNEL 0x08
+#define PT2254_DBS_IN_2 0x400
+#define PT2254_DBS_IN_10 0x20000
+#define WINVIEW_PT2254_CLK 0x40
+#define WINVIEW_PT2254_DATA 0x20
+#define WINVIEW_PT2254_STROBE 0x80
+
+struct bttv_core {
+ /* device structs */
+ struct v4l2_device v4l2_dev;
+ struct pci_dev *pci;
+ struct i2c_adapter i2c_adap;
+ struct list_head subs; /* struct bttv_sub_device */
+
+ /* device config */
+ unsigned int nr; /* dev nr (for printk("bttv%d: ..."); */
+ unsigned int type; /* card type (pointer into tvcards[]) */
+};
+
+struct bttv;
+
+struct tvcard {
+ char *name;
+ void (*volume_gpio)(struct bttv *btv, __u16 volume);
+ void (*audio_mode_gpio)(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+ void (*muxsel_hook)(struct bttv *btv, unsigned int input);
+
+ /* MUX bits for each input, two bits per input starting with the LSB */
+ u32 muxsel; /* Use MUXSEL() to set */
+
+ u32 gpiomask;
+ u32 gpiomux[4]; /* Tuner, Radio, external, internal */
+ u32 gpiomute; /* GPIO mute setting */
+ u32 gpiomask2; /* GPIO MUX mask */
+
+ unsigned int tuner_type;
+ u8 tuner_addr;
+ u8 video_inputs; /* Number of inputs */
+ unsigned int svhs:4; /* Which input is s-video */
+#define NO_SVHS 15
+ unsigned int pll:2;
+#define PLL_NONE 0
+#define PLL_28 1
+#define PLL_35 2
+
+ /* i2c audio flags */
+ unsigned int no_msp34xx:1;
+ unsigned int no_tda7432:1;
+ unsigned int msp34xx_alt:1;
+ /* Note: currently no card definition needs to mark the presence
+ of a RDS saa6588 chip. If this is ever needed, then add a new
+ 'has_saa6588' bit here. */
+
+ unsigned int no_video:1; /* video pci function is unused */
+ unsigned int has_dvb:1;
+ unsigned int has_remote:1;
+ unsigned int has_radio:1;
+ unsigned int has_dig_in:1; /* Has digital input (always last input) */
+ unsigned int no_gpioirq:1;
+};
+
+extern struct tvcard bttv_tvcards[];
+
+/*
+ * This bit of cpp voodoo is used to create a macro with a variable number of
+ * arguments (1 to 16). It will pack each argument into a word two bits at a
+ * time. It can't be a function because it needs to be compile time constant to
+ * initialize structures. Since each argument must fit in two bits, it's ok
+ * that they are changed to octal. One should not use hex number, macros, or
+ * anything else with this macro. Just use plain integers from 0 to 3.
+ */
+#define _MUXSELf(a) 0##a << 30
+#define _MUXSELe(a, b...) 0##a << 28 | _MUXSELf(b)
+#define _MUXSELd(a, b...) 0##a << 26 | _MUXSELe(b)
+#define _MUXSELc(a, b...) 0##a << 24 | _MUXSELd(b)
+#define _MUXSELb(a, b...) 0##a << 22 | _MUXSELc(b)
+#define _MUXSELa(a, b...) 0##a << 20 | _MUXSELb(b)
+#define _MUXSEL9(a, b...) 0##a << 18 | _MUXSELa(b)
+#define _MUXSEL8(a, b...) 0##a << 16 | _MUXSEL9(b)
+#define _MUXSEL7(a, b...) 0##a << 14 | _MUXSEL8(b)
+#define _MUXSEL6(a, b...) 0##a << 12 | _MUXSEL7(b)
+#define _MUXSEL5(a, b...) 0##a << 10 | _MUXSEL6(b)
+#define _MUXSEL4(a, b...) 0##a << 8 | _MUXSEL5(b)
+#define _MUXSEL3(a, b...) 0##a << 6 | _MUXSEL4(b)
+#define _MUXSEL2(a, b...) 0##a << 4 | _MUXSEL3(b)
+#define _MUXSEL1(a, b...) 0##a << 2 | _MUXSEL2(b)
+#define MUXSEL(a, b...) (a | _MUXSEL1(b))
+
+/* identification / initialization of the card */
+extern void bttv_idcard(struct bttv *btv);
+extern void bttv_init_card1(struct bttv *btv);
+extern void bttv_init_card2(struct bttv *btv);
+extern void bttv_init_tuner(struct bttv *btv);
+
+/* card-specific funtions */
+extern void tea5757_set_freq(struct bttv *btv, unsigned short freq);
+extern u32 bttv_tda9880_setnorm(struct bttv *btv, u32 gpiobits);
+
+/* extra tweaks for some chipsets */
+extern void bttv_check_chipset(void);
+extern int bttv_handle_chipset(struct bttv *btv);
+
+/* ---------------------------------------------------------- */
+/* exported by bttv-if.c */
+
+/* this obsolete -- please use the sysfs-based
+ interface below for new code */
+
+extern struct pci_dev* bttv_get_pcidev(unsigned int card);
+
+/* sets GPOE register (BT848_GPIO_OUT_EN) to new value:
+ data | (current_GPOE_value & ~mask)
+ returns negative value if error occurred
+*/
+extern int bttv_gpio_enable(unsigned int card,
+ unsigned long mask, unsigned long data);
+
+/* fills data with GPDATA register contents
+ returns negative value if error occurred
+*/
+extern int bttv_read_gpio(unsigned int card, unsigned long *data);
+
+/* sets GPDATA register to new value:
+ (data & mask) | (current_GPDATA_value & ~mask)
+ returns negative value if error occurred
+*/
+extern int bttv_write_gpio(unsigned int card,
+ unsigned long mask, unsigned long data);
+
+
+
+
+/* ---------------------------------------------------------- */
+/* sysfs/driver-moded based gpio access interface */
+
+struct bttv_sub_device {
+ struct device dev;
+ struct bttv_core *core;
+ struct list_head list;
+};
+#define to_bttv_sub_dev(x) container_of((x), struct bttv_sub_device, dev)
+
+struct bttv_sub_driver {
+ struct device_driver drv;
+ char wanted[20];
+ int (*probe)(struct bttv_sub_device *sub);
+ void (*remove)(struct bttv_sub_device *sub);
+};
+#define to_bttv_sub_drv(x) container_of((x), struct bttv_sub_driver, drv)
+
+int bttv_sub_register(struct bttv_sub_driver *drv, char *wanted);
+int bttv_sub_unregister(struct bttv_sub_driver *drv);
+
+/* gpio access functions */
+void bttv_gpio_inout(struct bttv_core *core, u32 mask, u32 outbits);
+u32 bttv_gpio_read(struct bttv_core *core);
+void bttv_gpio_write(struct bttv_core *core, u32 value);
+void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits);
+
+#define gpio_inout(mask,bits) bttv_gpio_inout(&btv->c, mask, bits)
+#define gpio_read() bttv_gpio_read(&btv->c)
+#define gpio_write(value) bttv_gpio_write(&btv->c, value)
+#define gpio_bits(mask,bits) bttv_gpio_bits(&btv->c, mask, bits)
+
+
+/* ---------------------------------------------------------- */
+/* i2c */
+
+#define bttv_call_all(btv, o, f, args...) \
+ v4l2_device_call_all(&btv->c.v4l2_dev, 0, o, f, ##args)
+
+extern int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for);
+extern int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1,
+ unsigned char b2, int both);
+extern void bttv_readee(struct bttv *btv, unsigned char *eedata, int addr);
+
+extern int bttv_input_init(struct bttv *dev);
+extern void bttv_input_fini(struct bttv *dev);
+extern void bttv_input_irq(struct bttv *dev);
+
+#endif /* _BTTV_H_ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/bttvp.h b/drivers/media/pci/bt8xx/bttvp.h
new file mode 100644
index 0000000..70fd4f23
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttvp.h
@@ -0,0 +1,535 @@
+/*
+
+ bttv - Bt848 frame grabber driver
+
+ bttv's *private* header file -- nobody other than bttv itself
+ should ever include this file.
+
+ (c) 2000-2002 Gerd Knorr <kraxel@bytesex.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 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 _BTTVP_H_
+#define _BTTVP_H_
+
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/pci.h>
+#include <linux/input.h>
+#include <linux/mutex.h>
+#include <linux/scatterlist.h>
+#include <asm/io.h>
+#include <media/v4l2-common.h>
+#include <linux/device.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/tveeprom.h>
+#include <media/rc-core.h>
+#include <media/ir-kbd-i2c.h>
+
+#include "bt848.h"
+#include "bttv.h"
+#include "btcx-risc.h"
+
+#ifdef __KERNEL__
+
+#define FORMAT_FLAGS_DITHER 0x01
+#define FORMAT_FLAGS_PACKED 0x02
+#define FORMAT_FLAGS_PLANAR 0x04
+#define FORMAT_FLAGS_RAW 0x08
+#define FORMAT_FLAGS_CrCb 0x10
+
+#define RISC_SLOT_O_VBI 4
+#define RISC_SLOT_O_FIELD 6
+#define RISC_SLOT_E_VBI 10
+#define RISC_SLOT_E_FIELD 12
+#define RISC_SLOT_LOOP 14
+
+#define RESOURCE_OVERLAY 1
+#define RESOURCE_VIDEO_STREAM 2
+#define RESOURCE_VBI 4
+#define RESOURCE_VIDEO_READ 8
+
+#define RAW_LINES 640
+#define RAW_BPL 1024
+
+#define UNSET (-1U)
+
+/* Min. value in VDELAY register. */
+#define MIN_VDELAY 2
+/* Even to get Cb first, odd for Cr. */
+#define MAX_HDELAY (0x3FF & -2)
+/* Limits scaled width, which must be a multiple of 4. */
+#define MAX_HACTIVE (0x3FF & -4)
+
+#define BTTV_NORMS (\
+ V4L2_STD_PAL | V4L2_STD_PAL_N | \
+ V4L2_STD_PAL_Nc | V4L2_STD_SECAM | \
+ V4L2_STD_NTSC | V4L2_STD_PAL_M | \
+ V4L2_STD_PAL_60)
+/* ---------------------------------------------------------- */
+
+struct bttv_tvnorm {
+ int v4l2_id;
+ char *name;
+ u32 Fsc;
+ u16 swidth, sheight; /* scaled standard width, height */
+ u16 totalwidth;
+ u8 adelay, bdelay, iform;
+ u32 scaledtwidth;
+ u16 hdelayx1, hactivex1;
+ u16 vdelay;
+ u8 vbipack;
+ u16 vtotal;
+ int sram;
+ /* ITU-R frame line number of the first VBI line we can
+ capture, of the first and second field. The last possible line
+ is determined by cropcap.bounds. */
+ u16 vbistart[2];
+ /* Horizontally this counts fCLKx1 samples following the leading
+ edge of the horizontal sync pulse, vertically ITU-R frame line
+ numbers of the first field times two (2, 4, 6, ... 524 or 624). */
+ struct v4l2_cropcap cropcap;
+};
+extern const struct bttv_tvnorm bttv_tvnorms[];
+
+struct bttv_format {
+ char *name;
+ int fourcc; /* video4linux 2 */
+ int btformat; /* BT848_COLOR_FMT_* */
+ int btswap; /* BT848_COLOR_CTL_* */
+ int depth; /* bit/pixel */
+ int flags;
+ int hshift,vshift; /* for planar modes */
+};
+
+struct bttv_ir {
+ struct rc_dev *dev;
+ struct timer_list timer;
+
+ char name[32];
+ char phys[32];
+
+ /* Usual gpio signalling */
+ u32 mask_keycode;
+ u32 mask_keydown;
+ u32 mask_keyup;
+ u32 polling;
+ u32 last_gpio;
+ int shift_by;
+ int start; // What should RC5_START() be
+ int addr; // What RC5_ADDR() should be.
+ int rc5_remote_gap;
+
+ /* RC5 gpio */
+ bool rc5_gpio; /* Is RC5 legacy GPIO enabled? */
+ u32 last_bit; /* last raw bit seen */
+ u32 code; /* raw code under construction */
+ struct timeval base_time; /* time of last seen code */
+ bool active; /* building raw code */
+};
+
+
+/* ---------------------------------------------------------- */
+
+struct bttv_geometry {
+ u8 vtc,crop,comb;
+ u16 width,hscale,hdelay;
+ u16 sheight,vscale,vdelay,vtotal;
+};
+
+struct bttv_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ /* bttv specific */
+ const struct bttv_format *fmt;
+ unsigned int tvnorm;
+ int btformat;
+ int btswap;
+ struct bttv_geometry geo;
+ struct btcx_riscmem top;
+ struct btcx_riscmem bottom;
+ struct v4l2_rect crop;
+ unsigned int vbi_skip[2];
+ unsigned int vbi_count[2];
+};
+
+struct bttv_buffer_set {
+ struct bttv_buffer *top; /* top field buffer */
+ struct bttv_buffer *bottom; /* bottom field buffer */
+ unsigned int top_irq;
+ unsigned int frame_irq;
+};
+
+struct bttv_overlay {
+ unsigned int tvnorm;
+ struct v4l2_rect w;
+ enum v4l2_field field;
+ struct v4l2_clip *clips;
+ int nclips;
+ int setup_ok;
+};
+
+struct bttv_vbi_fmt {
+ struct v4l2_vbi_format fmt;
+
+ /* fmt.start[] and count[] refer to this video standard. */
+ const struct bttv_tvnorm *tvnorm;
+
+ /* Earliest possible start of video capturing with this
+ v4l2_vbi_format, in struct bttv_crop.rect units. */
+ __s32 end;
+};
+
+/* bttv-vbi.c */
+void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, unsigned int norm);
+
+struct bttv_crop {
+ /* A cropping rectangle in struct bttv_tvnorm.cropcap units. */
+ struct v4l2_rect rect;
+
+ /* Scaled image size limits with this crop rect. Divide
+ max_height, but not min_height, by two when capturing
+ single fields. See also bttv_crop_reset() and
+ bttv_crop_adjust() in bttv-driver.c. */
+ __s32 min_scaled_width;
+ __s32 min_scaled_height;
+ __s32 max_scaled_width;
+ __s32 max_scaled_height;
+};
+
+struct bttv_fh {
+ struct bttv *btv;
+ int resources;
+#ifdef VIDIOC_G_PRIORITY
+ enum v4l2_priority prio;
+#endif
+ enum v4l2_buf_type type;
+
+ /* video capture */
+ struct videobuf_queue cap;
+ const struct bttv_format *fmt;
+ int width;
+ int height;
+
+ /* video overlay */
+ const struct bttv_format *ovfmt;
+ struct bttv_overlay ov;
+
+ /* Application called VIDIOC_S_CROP. */
+ int do_crop;
+
+ /* vbi capture */
+ struct videobuf_queue vbi;
+ /* Current VBI capture window as seen through this fh (cannot
+ be global for compatibility with earlier drivers). Protected
+ by struct bttv.lock and struct bttv_fh.vbi.lock. */
+ struct bttv_vbi_fmt vbi_fmt;
+};
+
+/* ---------------------------------------------------------- */
+/* bttv-risc.c */
+
+/* risc code generators - capture */
+int bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int offset, unsigned int bpl,
+ unsigned int pitch, unsigned int skip_lines,
+ unsigned int store_lines);
+
+/* control dma register + risc main loop */
+void bttv_set_dma(struct bttv *btv, int override);
+int bttv_risc_init_main(struct bttv *btv);
+int bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc,
+ int irqflags);
+
+/* capture buffer handling */
+int bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf);
+int bttv_buffer_activate_video(struct bttv *btv,
+ struct bttv_buffer_set *set);
+int bttv_buffer_activate_vbi(struct bttv *btv,
+ struct bttv_buffer *vbi);
+void bttv_dma_free(struct videobuf_queue *q, struct bttv *btv,
+ struct bttv_buffer *buf);
+
+/* overlay handling */
+int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov,
+ const struct bttv_format *fmt,
+ struct bttv_buffer *buf);
+
+
+/* ---------------------------------------------------------- */
+/* bttv-vbi.c */
+
+int bttv_try_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f);
+int bttv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f);
+int bttv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f);
+
+extern struct videobuf_queue_ops bttv_vbi_qops;
+
+/* ---------------------------------------------------------- */
+/* bttv-gpio.c */
+
+extern struct bus_type bttv_sub_bus_type;
+int bttv_sub_add_device(struct bttv_core *core, char *name);
+int bttv_sub_del_devices(struct bttv_core *core);
+
+/* ---------------------------------------------------------- */
+/* bttv-cards.c */
+
+extern int no_overlay;
+
+/* ---------------------------------------------------------- */
+/* bttv-input.c */
+
+extern void init_bttv_i2c_ir(struct bttv *btv);
+extern int fini_bttv_i2c(struct bttv *btv);
+
+/* ---------------------------------------------------------- */
+/* bttv-driver.c */
+
+/* insmod options */
+extern unsigned int bttv_verbose;
+extern unsigned int bttv_debug;
+extern unsigned int bttv_gpio;
+extern void bttv_gpio_tracking(struct bttv *btv, char *comment);
+extern int init_bttv_i2c(struct bttv *btv);
+
+#define dprintk(fmt, ...) \
+do { \
+ if (bttv_debug >= 1) \
+ pr_debug(fmt, ##__VA_ARGS__); \
+} while (0)
+#define dprintk_cont(fmt, ...) \
+do { \
+ if (bttv_debug >= 1) \
+ pr_cont(fmt, ##__VA_ARGS__); \
+} while (0)
+#define d2printk(fmt, ...) \
+do { \
+ if (bttv_debug >= 2) \
+ printk(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define BTTV_MAX_FBUF 0x208000
+#define BTTV_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
+#define BTTV_FREE_IDLE msecs_to_jiffies(1000) /* one second */
+
+
+struct bttv_pll_info {
+ unsigned int pll_ifreq; /* PLL input frequency */
+ unsigned int pll_ofreq; /* PLL output frequency */
+ unsigned int pll_crystal; /* Crystal used for input */
+ unsigned int pll_current; /* Currently programmed ofreq */
+};
+
+/* for gpio-connected remote control */
+struct bttv_input {
+ struct input_dev *dev;
+ char name[32];
+ char phys[32];
+ u32 mask_keycode;
+ u32 mask_keydown;
+};
+
+struct bttv_suspend_state {
+ u32 gpio_enable;
+ u32 gpio_data;
+ int disabled;
+ int loop_irq;
+ struct bttv_buffer_set video;
+ struct bttv_buffer *vbi;
+};
+
+struct bttv {
+ struct bttv_core c;
+
+ /* pci device config */
+ unsigned short id;
+ unsigned char revision;
+ unsigned char __iomem *bt848_mmio; /* pointer to mmio */
+
+ /* card configuration info */
+ unsigned int cardid; /* pci subsystem id (bt878 based ones) */
+ unsigned int tuner_type; /* tuner chip type */
+ unsigned int tda9887_conf;
+ unsigned int svhs, dig;
+ unsigned int has_saa6588:1;
+ struct bttv_pll_info pll;
+ int triton1;
+ int gpioirq;
+
+ int use_i2c_hw;
+
+ /* old gpio interface */
+ int shutdown;
+
+ void (*volume_gpio)(struct bttv *btv, __u16 volume);
+ void (*audio_mode_gpio)(struct bttv *btv, struct v4l2_tuner *tuner, int set);
+
+ /* new gpio interface */
+ spinlock_t gpio_lock;
+
+ /* i2c layer */
+ struct i2c_algo_bit_data i2c_algo;
+ struct i2c_client i2c_client;
+ int i2c_state, i2c_rc;
+ int i2c_done;
+ wait_queue_head_t i2c_queue;
+ struct v4l2_subdev *sd_msp34xx;
+ struct v4l2_subdev *sd_tvaudio;
+
+ /* video4linux (1) */
+ struct video_device *video_dev;
+ struct video_device *radio_dev;
+ struct video_device *vbi_dev;
+
+ /* infrared remote */
+ int has_remote;
+ struct bttv_ir *remote;
+
+ /* I2C remote data */
+ struct IR_i2c_init_data init_data;
+
+ /* locking */
+ spinlock_t s_lock;
+ struct mutex lock;
+ int resources;
+#ifdef VIDIOC_G_PRIORITY
+ struct v4l2_prio_state prio;
+#endif
+
+ /* video state */
+ unsigned int input;
+ unsigned int audio;
+ unsigned int mute;
+ unsigned long freq;
+ unsigned int tvnorm;
+ int hue, contrast, bright, saturation;
+ struct v4l2_framebuffer fbuf;
+ unsigned int field_count;
+
+ /* various options */
+ int opt_combfilter;
+ int opt_lumafilter;
+ int opt_automute;
+ int opt_chroma_agc;
+ int opt_adc_crush;
+ int opt_vcr_hack;
+ int opt_whitecrush_upper;
+ int opt_whitecrush_lower;
+ int opt_uv_ratio;
+ int opt_full_luma_range;
+ int opt_coring;
+
+ /* radio data/state */
+ int has_radio;
+ int radio_user;
+ int radio_uses_msp_demodulator;
+
+ /* miro/pinnacle + Aimslab VHX
+ philips matchbox (tea5757 radio tuner) support */
+ int has_matchbox;
+ int mbox_we;
+ int mbox_data;
+ int mbox_clk;
+ int mbox_most;
+ int mbox_mask;
+
+ /* ISA stuff (Terratec Active Radio Upgrade) */
+ int mbox_ior;
+ int mbox_iow;
+ int mbox_csel;
+
+ /* risc memory management data
+ - must acquire s_lock before changing these
+ - only the irq handler is supported to touch top + bottom + vcurr */
+ struct btcx_riscmem main;
+ struct bttv_buffer *screen; /* overlay */
+ struct list_head capture; /* video capture queue */
+ struct list_head vcapture; /* vbi capture queue */
+ struct bttv_buffer_set curr; /* active buffers */
+ struct bttv_buffer *cvbi; /* active vbi buffer */
+ int loop_irq;
+ int new_input;
+
+ unsigned long cap_ctl;
+ unsigned long dma_on;
+ struct timer_list timeout;
+ struct bttv_suspend_state state;
+
+ /* stats */
+ unsigned int errors;
+ unsigned int framedrop;
+ unsigned int irq_total;
+ unsigned int irq_me;
+
+ unsigned int users;
+ struct bttv_fh init;
+
+ /* used to make dvb-bt8xx autoloadable */
+ struct work_struct request_module_wk;
+
+ /* Default (0) and current (1) video capturing and overlay
+ cropping parameters in bttv_tvnorm.cropcap units. Protected
+ by bttv.lock. */
+ struct bttv_crop crop[2];
+
+ /* Earliest possible start of video capturing in
+ bttv_tvnorm.cropcap line units. Set by check_alloc_btres()
+ and free_btres(). Protected by bttv.lock. */
+ __s32 vbi_end;
+
+ /* Latest possible end of VBI capturing (= crop[x].rect.top when
+ VIDEO_RESOURCES are locked). Set by check_alloc_btres()
+ and free_btres(). Protected by bttv.lock. */
+ __s32 crop_start;
+};
+
+static inline struct bttv *to_bttv(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct bttv, c.v4l2_dev);
+}
+
+/* our devices */
+#define BTTV_MAX 32
+extern unsigned int bttv_num;
+extern struct bttv *bttvs[BTTV_MAX];
+
+static inline unsigned int bttv_muxsel(const struct bttv *btv,
+ unsigned int input)
+{
+ return (bttv_tvcards[btv->c.type].muxsel >> (input * 2)) & 3;
+}
+
+#endif
+
+#define btwrite(dat,adr) writel((dat), btv->bt848_mmio+(adr))
+#define btread(adr) readl(btv->bt848_mmio+(adr))
+
+#define btand(dat,adr) btwrite((dat) & btread(adr), adr)
+#define btor(dat,adr) btwrite((dat) | btread(adr), adr)
+#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
+
+#endif /* _BTTVP_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/bt8xx/dst.c b/drivers/media/pci/bt8xx/dst.c
new file mode 100644
index 0000000..430b3eb
--- /dev/null
+++ b/drivers/media/pci/bt8xx/dst.c
@@ -0,0 +1,1873 @@
+/*
+ Frontend/Card driver for TwinHan DST Frontend
+ Copyright (C) 2003 Jamie Honan
+ Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <asm/div64.h>
+#include "dvb_frontend.h"
+#include "dst_priv.h"
+#include "dst_common.h"
+
+static unsigned int verbose = 1;
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)");
+
+static unsigned int dst_addons;
+module_param(dst_addons, int, 0644);
+MODULE_PARM_DESC(dst_addons, "CA daughterboard, default is 0 (No addons)");
+
+static unsigned int dst_algo;
+module_param(dst_algo, int, 0644);
+MODULE_PARM_DESC(dst_algo, "tuning algo: default is 0=(SW), 1=(HW)");
+
+#define HAS_LOCK 1
+#define ATTEMPT_TUNE 2
+#define HAS_POWER 4
+
+#define DST_ERROR 0
+#define DST_NOTICE 1
+#define DST_INFO 2
+#define DST_DEBUG 3
+
+#define dprintk(x, y, z, format, arg...) do { \
+ if (z) { \
+ if ((x > DST_ERROR) && (x > y)) \
+ printk(KERN_ERR "dst(%d) %s: " format "\n", \
+ state->bt->nr, __func__ , ##arg); \
+ else if ((x > DST_NOTICE) && (x > y)) \
+ printk(KERN_NOTICE "dst(%d) %s: " format "\n", \
+ state->bt->nr, __func__ , ##arg); \
+ else if ((x > DST_INFO) && (x > y)) \
+ printk(KERN_INFO "dst(%d) %s: " format "\n", \
+ state->bt->nr, __func__ , ##arg); \
+ else if ((x > DST_DEBUG) && (x > y)) \
+ printk(KERN_DEBUG "dst(%d) %s: " format "\n", \
+ state->bt->nr, __func__ , ##arg); \
+ } else { \
+ if (x > y) \
+ printk(format, ##arg); \
+ } \
+} while(0)
+
+static int dst_command(struct dst_state *state, u8 *data, u8 len);
+
+static void dst_packsize(struct dst_state *state, int psize)
+{
+ union dst_gpio_packet bits;
+
+ bits.psize = psize;
+ bt878_device_control(state->bt, DST_IG_TS, &bits);
+}
+
+static int dst_gpio_outb(struct dst_state *state, u32 mask, u32 enbb,
+ u32 outhigh, int delay)
+{
+ union dst_gpio_packet enb;
+ union dst_gpio_packet bits;
+ int err;
+
+ enb.enb.mask = mask;
+ enb.enb.enable = enbb;
+
+ dprintk(verbose, DST_INFO, 1, "mask=[%04x], enbb=[%04x], outhigh=[%04x]", mask, enbb, outhigh);
+ if ((err = bt878_device_control(state->bt, DST_IG_ENABLE, &enb)) < 0) {
+ dprintk(verbose, DST_INFO, 1, "dst_gpio_enb error (err == %i, mask == %02x, enb == %02x)", err, mask, enbb);
+ return -EREMOTEIO;
+ }
+ udelay(1000);
+ /* because complete disabling means no output, no need to do output packet */
+ if (enbb == 0)
+ return 0;
+ if (delay)
+ msleep(10);
+ bits.outp.mask = enbb;
+ bits.outp.highvals = outhigh;
+ if ((err = bt878_device_control(state->bt, DST_IG_WRITE, &bits)) < 0) {
+ dprintk(verbose, DST_INFO, 1, "dst_gpio_outb error (err == %i, enbb == %02x, outhigh == %02x)", err, enbb, outhigh);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static int dst_gpio_inb(struct dst_state *state, u8 *result)
+{
+ union dst_gpio_packet rd_packet;
+ int err;
+
+ *result = 0;
+ if ((err = bt878_device_control(state->bt, DST_IG_READ, &rd_packet)) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "dst_gpio_inb error (err == %i)", err);
+ return -EREMOTEIO;
+ }
+ *result = (u8) rd_packet.rd.value;
+
+ return 0;
+}
+
+int rdc_reset_state(struct dst_state *state)
+{
+ dprintk(verbose, DST_INFO, 1, "Resetting state machine");
+ if (dst_gpio_outb(state, RDC_8820_INT, RDC_8820_INT, 0, NO_DELAY) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !");
+ return -1;
+ }
+ msleep(10);
+ if (dst_gpio_outb(state, RDC_8820_INT, RDC_8820_INT, RDC_8820_INT, NO_DELAY) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !");
+ msleep(10);
+ return -1;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(rdc_reset_state);
+
+static int rdc_8820_reset(struct dst_state *state)
+{
+ dprintk(verbose, DST_DEBUG, 1, "Resetting DST");
+ if (dst_gpio_outb(state, RDC_8820_RESET, RDC_8820_RESET, 0, NO_DELAY) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !");
+ return -1;
+ }
+ udelay(1000);
+ if (dst_gpio_outb(state, RDC_8820_RESET, RDC_8820_RESET, RDC_8820_RESET, DELAY) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int dst_pio_enable(struct dst_state *state)
+{
+ if (dst_gpio_outb(state, ~0, RDC_8820_PIO_0_ENABLE, 0, NO_DELAY) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !");
+ return -1;
+ }
+ udelay(1000);
+
+ return 0;
+}
+
+int dst_pio_disable(struct dst_state *state)
+{
+ if (dst_gpio_outb(state, ~0, RDC_8820_PIO_0_DISABLE, RDC_8820_PIO_0_DISABLE, NO_DELAY) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !");
+ return -1;
+ }
+ if (state->type_flags & DST_TYPE_HAS_FW_1)
+ udelay(1000);
+
+ return 0;
+}
+EXPORT_SYMBOL(dst_pio_disable);
+
+int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode)
+{
+ u8 reply;
+ int i;
+
+ for (i = 0; i < 200; i++) {
+ if (dst_gpio_inb(state, &reply) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "dst_gpio_inb ERROR !");
+ return -1;
+ }
+ if ((reply & RDC_8820_PIO_0_ENABLE) == 0) {
+ dprintk(verbose, DST_INFO, 1, "dst wait ready after %d", i);
+ return 1;
+ }
+ msleep(10);
+ }
+ dprintk(verbose, DST_NOTICE, 1, "dst wait NOT ready after %d", i);
+
+ return 0;
+}
+EXPORT_SYMBOL(dst_wait_dst_ready);
+
+int dst_error_recovery(struct dst_state *state)
+{
+ dprintk(verbose, DST_NOTICE, 1, "Trying to return from previous errors.");
+ dst_pio_disable(state);
+ msleep(10);
+ dst_pio_enable(state);
+ msleep(10);
+
+ return 0;
+}
+EXPORT_SYMBOL(dst_error_recovery);
+
+int dst_error_bailout(struct dst_state *state)
+{
+ dprintk(verbose, DST_INFO, 1, "Trying to bailout from previous error.");
+ rdc_8820_reset(state);
+ dst_pio_disable(state);
+ msleep(10);
+
+ return 0;
+}
+EXPORT_SYMBOL(dst_error_bailout);
+
+int dst_comm_init(struct dst_state *state)
+{
+ dprintk(verbose, DST_INFO, 1, "Initializing DST.");
+ if ((dst_pio_enable(state)) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "PIO Enable Failed");
+ return -1;
+ }
+ if ((rdc_reset_state(state)) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "RDC 8820 State RESET Failed.");
+ return -1;
+ }
+ if (state->type_flags & DST_TYPE_HAS_FW_1)
+ msleep(100);
+ else
+ msleep(5);
+
+ return 0;
+}
+EXPORT_SYMBOL(dst_comm_init);
+
+int write_dst(struct dst_state *state, u8 *data, u8 len)
+{
+ struct i2c_msg msg = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = data,
+ .len = len
+ };
+
+ int err;
+ u8 cnt, i;
+
+ dprintk(verbose, DST_NOTICE, 0, "writing [ ");
+ for (i = 0; i < len; i++)
+ dprintk(verbose, DST_NOTICE, 0, "%02x ", data[i]);
+ dprintk(verbose, DST_NOTICE, 0, "]\n");
+
+ for (cnt = 0; cnt < 2; cnt++) {
+ if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) {
+ dprintk(verbose, DST_INFO, 1, "_write_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)", err, len, data[0]);
+ dst_error_recovery(state);
+ continue;
+ } else
+ break;
+ }
+ if (cnt >= 2) {
+ dprintk(verbose, DST_INFO, 1, "RDC 8820 RESET");
+ dst_error_bailout(state);
+
+ return -1;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(write_dst);
+
+int read_dst(struct dst_state *state, u8 *ret, u8 len)
+{
+ struct i2c_msg msg = {
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = ret,
+ .len = len
+ };
+
+ int err;
+ int cnt;
+
+ for (cnt = 0; cnt < 2; cnt++) {
+ if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) {
+ dprintk(verbose, DST_INFO, 1, "read_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)", err, len, ret[0]);
+ dst_error_recovery(state);
+ continue;
+ } else
+ break;
+ }
+ if (cnt >= 2) {
+ dprintk(verbose, DST_INFO, 1, "RDC 8820 RESET");
+ dst_error_bailout(state);
+
+ return -1;
+ }
+ dprintk(verbose, DST_DEBUG, 1, "reply is 0x%x", ret[0]);
+ for (err = 1; err < len; err++)
+ dprintk(verbose, DST_DEBUG, 0, " 0x%x", ret[err]);
+ if (err > 1)
+ dprintk(verbose, DST_DEBUG, 0, "\n");
+
+ return 0;
+}
+EXPORT_SYMBOL(read_dst);
+
+static int dst_set_polarization(struct dst_state *state)
+{
+ switch (state->voltage) {
+ case SEC_VOLTAGE_13: /* Vertical */
+ dprintk(verbose, DST_INFO, 1, "Polarization=[Vertical]");
+ state->tx_tuna[8] &= ~0x40;
+ break;
+ case SEC_VOLTAGE_18: /* Horizontal */
+ dprintk(verbose, DST_INFO, 1, "Polarization=[Horizontal]");
+ state->tx_tuna[8] |= 0x40;
+ break;
+ case SEC_VOLTAGE_OFF:
+ break;
+ }
+
+ return 0;
+}
+
+static int dst_set_freq(struct dst_state *state, u32 freq)
+{
+ state->frequency = freq;
+ dprintk(verbose, DST_INFO, 1, "set Frequency %u", freq);
+
+ if (state->dst_type == DST_TYPE_IS_SAT) {
+ freq = freq / 1000;
+ if (freq < 950 || freq > 2150)
+ return -EINVAL;
+ state->tx_tuna[2] = (freq >> 8);
+ state->tx_tuna[3] = (u8) freq;
+ state->tx_tuna[4] = 0x01;
+ state->tx_tuna[8] &= ~0x04;
+ if (state->type_flags & DST_TYPE_HAS_OBS_REGS) {
+ if (freq < 1531)
+ state->tx_tuna[8] |= 0x04;
+ }
+ } else if (state->dst_type == DST_TYPE_IS_TERR) {
+ freq = freq / 1000;
+ if (freq < 137000 || freq > 858000)
+ return -EINVAL;
+ state->tx_tuna[2] = (freq >> 16) & 0xff;
+ state->tx_tuna[3] = (freq >> 8) & 0xff;
+ state->tx_tuna[4] = (u8) freq;
+ } else if (state->dst_type == DST_TYPE_IS_CABLE) {
+ freq = freq / 1000;
+ state->tx_tuna[2] = (freq >> 16) & 0xff;
+ state->tx_tuna[3] = (freq >> 8) & 0xff;
+ state->tx_tuna[4] = (u8) freq;
+ } else if (state->dst_type == DST_TYPE_IS_ATSC) {
+ freq = freq / 1000;
+ if (freq < 51000 || freq > 858000)
+ return -EINVAL;
+ state->tx_tuna[2] = (freq >> 16) & 0xff;
+ state->tx_tuna[3] = (freq >> 8) & 0xff;
+ state->tx_tuna[4] = (u8) freq;
+ state->tx_tuna[5] = 0x00; /* ATSC */
+ state->tx_tuna[6] = 0x00;
+ if (state->dst_hw_cap & DST_TYPE_HAS_ANALOG)
+ state->tx_tuna[7] = 0x00; /* Digital */
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int dst_set_bandwidth(struct dst_state *state, u32 bandwidth)
+{
+ state->bandwidth = bandwidth;
+
+ if (state->dst_type != DST_TYPE_IS_TERR)
+ return -EOPNOTSUPP;
+
+ switch (bandwidth) {
+ case 6000000:
+ if (state->dst_hw_cap & DST_TYPE_HAS_CA)
+ state->tx_tuna[7] = 0x06;
+ else {
+ state->tx_tuna[6] = 0x06;
+ state->tx_tuna[7] = 0x00;
+ }
+ break;
+ case 7000000:
+ if (state->dst_hw_cap & DST_TYPE_HAS_CA)
+ state->tx_tuna[7] = 0x07;
+ else {
+ state->tx_tuna[6] = 0x07;
+ state->tx_tuna[7] = 0x00;
+ }
+ break;
+ case 8000000:
+ if (state->dst_hw_cap & DST_TYPE_HAS_CA)
+ state->tx_tuna[7] = 0x08;
+ else {
+ state->tx_tuna[6] = 0x08;
+ state->tx_tuna[7] = 0x00;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dst_set_inversion(struct dst_state *state, fe_spectral_inversion_t inversion)
+{
+ state->inversion = inversion;
+ switch (inversion) {
+ case INVERSION_OFF: /* Inversion = Normal */
+ state->tx_tuna[8] &= ~0x80;
+ break;
+ case INVERSION_ON:
+ state->tx_tuna[8] |= 0x80;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dst_set_fec(struct dst_state *state, fe_code_rate_t fec)
+{
+ state->fec = fec;
+ return 0;
+}
+
+static fe_code_rate_t dst_get_fec(struct dst_state *state)
+{
+ return state->fec;
+}
+
+static int dst_set_symbolrate(struct dst_state *state, u32 srate)
+{
+ u32 symcalc;
+ u64 sval;
+
+ state->symbol_rate = srate;
+ if (state->dst_type == DST_TYPE_IS_TERR) {
+ return -EOPNOTSUPP;
+ }
+ dprintk(verbose, DST_INFO, 1, "set symrate %u", srate);
+ srate /= 1000;
+ if (state->dst_type == DST_TYPE_IS_SAT) {
+ if (state->type_flags & DST_TYPE_HAS_SYMDIV) {
+ sval = srate;
+ sval <<= 20;
+ do_div(sval, 88000);
+ symcalc = (u32) sval;
+ dprintk(verbose, DST_INFO, 1, "set symcalc %u", symcalc);
+ state->tx_tuna[5] = (u8) (symcalc >> 12);
+ state->tx_tuna[6] = (u8) (symcalc >> 4);
+ state->tx_tuna[7] = (u8) (symcalc << 4);
+ } else {
+ state->tx_tuna[5] = (u8) (srate >> 16) & 0x7f;
+ state->tx_tuna[6] = (u8) (srate >> 8);
+ state->tx_tuna[7] = (u8) srate;
+ }
+ state->tx_tuna[8] &= ~0x20;
+ if (state->type_flags & DST_TYPE_HAS_OBS_REGS) {
+ if (srate > 8000)
+ state->tx_tuna[8] |= 0x20;
+ }
+ } else if (state->dst_type == DST_TYPE_IS_CABLE) {
+ dprintk(verbose, DST_DEBUG, 1, "%s", state->fw_name);
+ if (!strncmp(state->fw_name, "DCTNEW", 6)) {
+ state->tx_tuna[5] = (u8) (srate >> 8);
+ state->tx_tuna[6] = (u8) srate;
+ state->tx_tuna[7] = 0x00;
+ } else if (!strncmp(state->fw_name, "DCT-CI", 6)) {
+ state->tx_tuna[5] = 0x00;
+ state->tx_tuna[6] = (u8) (srate >> 8);
+ state->tx_tuna[7] = (u8) srate;
+ }
+ }
+ return 0;
+}
+
+static int dst_set_modulation(struct dst_state *state, fe_modulation_t modulation)
+{
+ if (state->dst_type != DST_TYPE_IS_CABLE)
+ return -EOPNOTSUPP;
+
+ state->modulation = modulation;
+ switch (modulation) {
+ case QAM_16:
+ state->tx_tuna[8] = 0x10;
+ break;
+ case QAM_32:
+ state->tx_tuna[8] = 0x20;
+ break;
+ case QAM_64:
+ state->tx_tuna[8] = 0x40;
+ break;
+ case QAM_128:
+ state->tx_tuna[8] = 0x80;
+ break;
+ case QAM_256:
+ if (!strncmp(state->fw_name, "DCTNEW", 6))
+ state->tx_tuna[8] = 0xff;
+ else if (!strncmp(state->fw_name, "DCT-CI", 6))
+ state->tx_tuna[8] = 0x00;
+ break;
+ case QPSK:
+ case QAM_AUTO:
+ case VSB_8:
+ case VSB_16:
+ default:
+ return -EINVAL;
+
+ }
+
+ return 0;
+}
+
+static fe_modulation_t dst_get_modulation(struct dst_state *state)
+{
+ return state->modulation;
+}
+
+
+u8 dst_check_sum(u8 *buf, u32 len)
+{
+ u32 i;
+ u8 val = 0;
+ if (!len)
+ return 0;
+ for (i = 0; i < len; i++) {
+ val += buf[i];
+ }
+ return ((~val) + 1);
+}
+EXPORT_SYMBOL(dst_check_sum);
+
+static void dst_type_flags_print(struct dst_state *state)
+{
+ u32 type_flags = state->type_flags;
+
+ dprintk(verbose, DST_ERROR, 0, "DST type flags :");
+ if (type_flags & DST_TYPE_HAS_TS188)
+ dprintk(verbose, DST_ERROR, 0, " 0x%x newtuner", DST_TYPE_HAS_TS188);
+ if (type_flags & DST_TYPE_HAS_NEWTUNE_2)
+ dprintk(verbose, DST_ERROR, 0, " 0x%x newtuner 2", DST_TYPE_HAS_NEWTUNE_2);
+ if (type_flags & DST_TYPE_HAS_TS204)
+ dprintk(verbose, DST_ERROR, 0, " 0x%x ts204", DST_TYPE_HAS_TS204);
+ if (type_flags & DST_TYPE_HAS_VLF)
+ dprintk(verbose, DST_ERROR, 0, " 0x%x VLF", DST_TYPE_HAS_VLF);
+ if (type_flags & DST_TYPE_HAS_SYMDIV)
+ dprintk(verbose, DST_ERROR, 0, " 0x%x symdiv", DST_TYPE_HAS_SYMDIV);
+ if (type_flags & DST_TYPE_HAS_FW_1)
+ dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 1", DST_TYPE_HAS_FW_1);
+ if (type_flags & DST_TYPE_HAS_FW_2)
+ dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 2", DST_TYPE_HAS_FW_2);
+ if (type_flags & DST_TYPE_HAS_FW_3)
+ dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 3", DST_TYPE_HAS_FW_3);
+ dprintk(verbose, DST_ERROR, 0, "\n");
+}
+
+
+static int dst_type_print(struct dst_state *state, u8 type)
+{
+ char *otype;
+ switch (type) {
+ case DST_TYPE_IS_SAT:
+ otype = "satellite";
+ break;
+
+ case DST_TYPE_IS_TERR:
+ otype = "terrestrial";
+ break;
+
+ case DST_TYPE_IS_CABLE:
+ otype = "cable";
+ break;
+
+ case DST_TYPE_IS_ATSC:
+ otype = "atsc";
+ break;
+
+ default:
+ dprintk(verbose, DST_INFO, 1, "invalid dst type %d", type);
+ return -EINVAL;
+ }
+ dprintk(verbose, DST_INFO, 1, "DST type: %s", otype);
+
+ return 0;
+}
+
+static struct tuner_types tuner_list[] = {
+ {
+ .tuner_type = TUNER_TYPE_L64724,
+ .tuner_name = "L 64724",
+ .board_name = "UNKNOWN",
+ .fw_name = "UNKNOWN"
+ },
+
+ {
+ .tuner_type = TUNER_TYPE_STV0299,
+ .tuner_name = "STV 0299",
+ .board_name = "VP1020",
+ .fw_name = "DST-MOT"
+ },
+
+ {
+ .tuner_type = TUNER_TYPE_STV0299,
+ .tuner_name = "STV 0299",
+ .board_name = "VP1020",
+ .fw_name = "DST-03T"
+ },
+
+ {
+ .tuner_type = TUNER_TYPE_MB86A15,
+ .tuner_name = "MB 86A15",
+ .board_name = "VP1022",
+ .fw_name = "DST-03T"
+ },
+
+ {
+ .tuner_type = TUNER_TYPE_MB86A15,
+ .tuner_name = "MB 86A15",
+ .board_name = "VP1025",
+ .fw_name = "DST-03T"
+ },
+
+ {
+ .tuner_type = TUNER_TYPE_STV0299,
+ .tuner_name = "STV 0299",
+ .board_name = "VP1030",
+ .fw_name = "DST-CI"
+ },
+
+ {
+ .tuner_type = TUNER_TYPE_STV0299,
+ .tuner_name = "STV 0299",
+ .board_name = "VP1030",
+ .fw_name = "DSTMCI"
+ },
+
+ {
+ .tuner_type = TUNER_TYPE_UNKNOWN,
+ .tuner_name = "UNKNOWN",
+ .board_name = "VP2021",
+ .fw_name = "DCTNEW"
+ },
+
+ {
+ .tuner_type = TUNER_TYPE_UNKNOWN,
+ .tuner_name = "UNKNOWN",
+ .board_name = "VP2030",
+ .fw_name = "DCT-CI"
+ },
+
+ {
+ .tuner_type = TUNER_TYPE_UNKNOWN,
+ .tuner_name = "UNKNOWN",
+ .board_name = "VP2031",
+ .fw_name = "DCT-CI"
+ },
+
+ {
+ .tuner_type = TUNER_TYPE_UNKNOWN,
+ .tuner_name = "UNKNOWN",
+ .board_name = "VP2040",
+ .fw_name = "DCT-CI"
+ },
+
+ {
+ .tuner_type = TUNER_TYPE_UNKNOWN,
+ .tuner_name = "UNKNOWN",
+ .board_name = "VP3020",
+ .fw_name = "DTTFTA"
+ },
+
+ {
+ .tuner_type = TUNER_TYPE_UNKNOWN,
+ .tuner_name = "UNKNOWN",
+ .board_name = "VP3021",
+ .fw_name = "DTTFTA"
+ },
+
+ {
+ .tuner_type = TUNER_TYPE_TDA10046,
+ .tuner_name = "TDA10046",
+ .board_name = "VP3040",
+ .fw_name = "DTT-CI"
+ },
+
+ {
+ .tuner_type = TUNER_TYPE_UNKNOWN,
+ .tuner_name = "UNKNOWN",
+ .board_name = "VP3051",
+ .fw_name = "DTTNXT"
+ },
+
+ {
+ .tuner_type = TUNER_TYPE_NXT200x,
+ .tuner_name = "NXT200x",
+ .board_name = "VP3220",
+ .fw_name = "ATSCDI"
+ },
+
+ {
+ .tuner_type = TUNER_TYPE_NXT200x,
+ .tuner_name = "NXT200x",
+ .board_name = "VP3250",
+ .fw_name = "ATSCAD"
+ },
+};
+
+/*
+ Known cards list
+ Satellite
+ -------------------
+ 200103A
+ VP-1020 DST-MOT LG(old), TS=188
+
+ VP-1020 DST-03T LG(new), TS=204
+ VP-1022 DST-03T LG(new), TS=204
+ VP-1025 DST-03T LG(new), TS=204
+
+ VP-1030 DSTMCI, LG(new), TS=188
+ VP-1032 DSTMCI, LG(new), TS=188
+
+ Cable
+ -------------------
+ VP-2030 DCT-CI, Samsung, TS=204
+ VP-2021 DCT-CI, Unknown, TS=204
+ VP-2031 DCT-CI, Philips, TS=188
+ VP-2040 DCT-CI, Philips, TS=188, with CA daughter board
+ VP-2040 DCT-CI, Philips, TS=204, without CA daughter board
+
+ Terrestrial
+ -------------------
+ VP-3050 DTTNXT TS=188
+ VP-3040 DTT-CI, Philips, TS=188
+ VP-3040 DTT-CI, Philips, TS=204
+
+ ATSC
+ -------------------
+ VP-3220 ATSCDI, TS=188
+ VP-3250 ATSCAD, TS=188
+
+*/
+
+static struct dst_types dst_tlist[] = {
+ {
+ .device_id = "200103A",
+ .offset = 0,
+ .dst_type = DST_TYPE_IS_SAT,
+ .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1 | DST_TYPE_HAS_OBS_REGS,
+ .dst_feature = 0,
+ .tuner_type = 0
+ }, /* obsolete */
+
+ {
+ .device_id = "DST-020",
+ .offset = 0,
+ .dst_type = DST_TYPE_IS_SAT,
+ .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1,
+ .dst_feature = 0,
+ .tuner_type = 0
+ }, /* obsolete */
+
+ {
+ .device_id = "DST-030",
+ .offset = 0,
+ .dst_type = DST_TYPE_IS_SAT,
+ .type_flags = DST_TYPE_HAS_TS204 | DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_1,
+ .dst_feature = 0,
+ .tuner_type = 0
+ }, /* obsolete */
+
+ {
+ .device_id = "DST-03T",
+ .offset = 0,
+ .dst_type = DST_TYPE_IS_SAT,
+ .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_TS204 | DST_TYPE_HAS_FW_2,
+ .dst_feature = DST_TYPE_HAS_DISEQC3 | DST_TYPE_HAS_DISEQC4 | DST_TYPE_HAS_DISEQC5
+ | DST_TYPE_HAS_MAC | DST_TYPE_HAS_MOTO,
+ .tuner_type = TUNER_TYPE_MULTI
+ },
+
+ {
+ .device_id = "DST-MOT",
+ .offset = 0,
+ .dst_type = DST_TYPE_IS_SAT,
+ .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1,
+ .dst_feature = 0,
+ .tuner_type = 0
+ }, /* obsolete */
+
+ {
+ .device_id = "DST-CI",
+ .offset = 1,
+ .dst_type = DST_TYPE_IS_SAT,
+ .type_flags = DST_TYPE_HAS_TS204 | DST_TYPE_HAS_FW_1,
+ .dst_feature = DST_TYPE_HAS_CA,
+ .tuner_type = 0
+ }, /* An OEM board */
+
+ {
+ .device_id = "DSTMCI",
+ .offset = 1,
+ .dst_type = DST_TYPE_IS_SAT,
+ .type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_FW_BUILD | DST_TYPE_HAS_INC_COUNT | DST_TYPE_HAS_VLF,
+ .dst_feature = DST_TYPE_HAS_CA | DST_TYPE_HAS_DISEQC3 | DST_TYPE_HAS_DISEQC4
+ | DST_TYPE_HAS_MOTO | DST_TYPE_HAS_MAC,
+ .tuner_type = TUNER_TYPE_MULTI
+ },
+
+ {
+ .device_id = "DSTFCI",
+ .offset = 1,
+ .dst_type = DST_TYPE_IS_SAT,
+ .type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_1,
+ .dst_feature = 0,
+ .tuner_type = 0
+ }, /* unknown to vendor */
+
+ {
+ .device_id = "DCT-CI",
+ .offset = 1,
+ .dst_type = DST_TYPE_IS_CABLE,
+ .type_flags = DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_FW_1 | DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_VLF,
+ .dst_feature = DST_TYPE_HAS_CA,
+ .tuner_type = 0
+ },
+
+ {
+ .device_id = "DCTNEW",
+ .offset = 1,
+ .dst_type = DST_TYPE_IS_CABLE,
+ .type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_3 | DST_TYPE_HAS_FW_BUILD | DST_TYPE_HAS_MULTI_FE,
+ .dst_feature = 0,
+ .tuner_type = 0
+ },
+
+ {
+ .device_id = "DTT-CI",
+ .offset = 1,
+ .dst_type = DST_TYPE_IS_TERR,
+ .type_flags = DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_VLF,
+ .dst_feature = DST_TYPE_HAS_CA,
+ .tuner_type = 0
+ },
+
+ {
+ .device_id = "DTTDIG",
+ .offset = 1,
+ .dst_type = DST_TYPE_IS_TERR,
+ .type_flags = DST_TYPE_HAS_FW_2,
+ .dst_feature = 0,
+ .tuner_type = 0
+ },
+
+ {
+ .device_id = "DTTNXT",
+ .offset = 1,
+ .dst_type = DST_TYPE_IS_TERR,
+ .type_flags = DST_TYPE_HAS_FW_2,
+ .dst_feature = DST_TYPE_HAS_ANALOG,
+ .tuner_type = 0
+ },
+
+ {
+ .device_id = "ATSCDI",
+ .offset = 1,
+ .dst_type = DST_TYPE_IS_ATSC,
+ .type_flags = DST_TYPE_HAS_FW_2,
+ .dst_feature = 0,
+ .tuner_type = 0
+ },
+
+ {
+ .device_id = "ATSCAD",
+ .offset = 1,
+ .dst_type = DST_TYPE_IS_ATSC,
+ .type_flags = DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_FW_BUILD,
+ .dst_feature = DST_TYPE_HAS_MAC | DST_TYPE_HAS_ANALOG,
+ .tuner_type = 0
+ },
+
+ { }
+
+};
+
+static int dst_get_mac(struct dst_state *state)
+{
+ u8 get_mac[] = { 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ get_mac[7] = dst_check_sum(get_mac, 7);
+ if (dst_command(state, get_mac, 8) < 0) {
+ dprintk(verbose, DST_INFO, 1, "Unsupported Command");
+ return -1;
+ }
+ memset(&state->mac_address, '\0', 8);
+ memcpy(&state->mac_address, &state->rxbuffer, 6);
+ dprintk(verbose, DST_ERROR, 1, "MAC Address=[%pM]", state->mac_address);
+
+ return 0;
+}
+
+static int dst_fw_ver(struct dst_state *state)
+{
+ u8 get_ver[] = { 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ get_ver[7] = dst_check_sum(get_ver, 7);
+ if (dst_command(state, get_ver, 8) < 0) {
+ dprintk(verbose, DST_INFO, 1, "Unsupported Command");
+ return -1;
+ }
+ memcpy(&state->fw_version, &state->rxbuffer, 8);
+ dprintk(verbose, DST_ERROR, 1, "Firmware Ver = %x.%x Build = %02x, on %x:%x, %x-%x-20%02x",
+ state->fw_version[0] >> 4, state->fw_version[0] & 0x0f,
+ state->fw_version[1],
+ state->fw_version[5], state->fw_version[6],
+ state->fw_version[4], state->fw_version[3], state->fw_version[2]);
+
+ return 0;
+}
+
+static int dst_card_type(struct dst_state *state)
+{
+ int j;
+ struct tuner_types *p_tuner_list = NULL;
+
+ u8 get_type[] = { 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ get_type[7] = dst_check_sum(get_type, 7);
+ if (dst_command(state, get_type, 8) < 0) {
+ dprintk(verbose, DST_INFO, 1, "Unsupported Command");
+ return -1;
+ }
+ memset(&state->card_info, '\0', 8);
+ memcpy(&state->card_info, &state->rxbuffer, 7);
+ dprintk(verbose, DST_ERROR, 1, "Device Model=[%s]", &state->card_info[0]);
+
+ for (j = 0, p_tuner_list = tuner_list; j < ARRAY_SIZE(tuner_list); j++, p_tuner_list++) {
+ if (!strcmp(&state->card_info[0], p_tuner_list->board_name)) {
+ state->tuner_type = p_tuner_list->tuner_type;
+ dprintk(verbose, DST_ERROR, 1, "DST has [%s] tuner, tuner type=[%d]",
+ p_tuner_list->tuner_name, p_tuner_list->tuner_type);
+ }
+ }
+
+ return 0;
+}
+
+static int dst_get_vendor(struct dst_state *state)
+{
+ u8 get_vendor[] = { 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ get_vendor[7] = dst_check_sum(get_vendor, 7);
+ if (dst_command(state, get_vendor, 8) < 0) {
+ dprintk(verbose, DST_INFO, 1, "Unsupported Command");
+ return -1;
+ }
+ memset(&state->vendor, '\0', 8);
+ memcpy(&state->vendor, &state->rxbuffer, 7);
+ dprintk(verbose, DST_ERROR, 1, "Vendor=[%s]", &state->vendor[0]);
+
+ return 0;
+}
+
+static void debug_dst_buffer(struct dst_state *state)
+{
+ int i;
+
+ if (verbose > 2) {
+ printk("%s: [", __func__);
+ for (i = 0; i < 8; i++)
+ printk(" %02x", state->rxbuffer[i]);
+ printk("]\n");
+ }
+}
+
+static int dst_check_stv0299(struct dst_state *state)
+{
+ u8 check_stv0299[] = { 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ check_stv0299[7] = dst_check_sum(check_stv0299, 7);
+ if (dst_command(state, check_stv0299, 8) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "Cmd=[0x04] failed");
+ return -1;
+ }
+ debug_dst_buffer(state);
+
+ if (memcmp(&check_stv0299, &state->rxbuffer, 8)) {
+ dprintk(verbose, DST_ERROR, 1, "Found a STV0299 NIM");
+ state->tuner_type = TUNER_TYPE_STV0299;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int dst_check_mb86a15(struct dst_state *state)
+{
+ u8 check_mb86a15[] = { 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ check_mb86a15[7] = dst_check_sum(check_mb86a15, 7);
+ if (dst_command(state, check_mb86a15, 8) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "Cmd=[0x10], failed");
+ return -1;
+ }
+ debug_dst_buffer(state);
+
+ if (memcmp(&check_mb86a15, &state->rxbuffer, 8) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "Found a MB86A15 NIM");
+ state->tuner_type = TUNER_TYPE_MB86A15;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int dst_get_tuner_info(struct dst_state *state)
+{
+ u8 get_tuner_1[] = { 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ u8 get_tuner_2[] = { 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ get_tuner_1[7] = dst_check_sum(get_tuner_1, 7);
+ get_tuner_2[7] = dst_check_sum(get_tuner_2, 7);
+ dprintk(verbose, DST_ERROR, 1, "DST TYpe = MULTI FE");
+ if (state->type_flags & DST_TYPE_HAS_MULTI_FE) {
+ if (dst_command(state, get_tuner_1, 8) < 0) {
+ dprintk(verbose, DST_INFO, 1, "Cmd=[0x13], Unsupported");
+ goto force;
+ }
+ } else {
+ if (dst_command(state, get_tuner_2, 8) < 0) {
+ dprintk(verbose, DST_INFO, 1, "Cmd=[0xb], Unsupported");
+ goto force;
+ }
+ }
+ memcpy(&state->board_info, &state->rxbuffer, 8);
+ if (state->type_flags & DST_TYPE_HAS_MULTI_FE) {
+ dprintk(verbose, DST_ERROR, 1, "DST type has TS=188");
+ }
+ if (state->board_info[0] == 0xbc) {
+ if (state->dst_type != DST_TYPE_IS_ATSC)
+ state->type_flags |= DST_TYPE_HAS_TS188;
+ else
+ state->type_flags |= DST_TYPE_HAS_NEWTUNE_2;
+
+ if (state->board_info[1] == 0x01) {
+ state->dst_hw_cap |= DST_TYPE_HAS_DBOARD;
+ dprintk(verbose, DST_ERROR, 1, "DST has Daughterboard");
+ }
+ }
+
+ return 0;
+force:
+ if (!strncmp(state->fw_name, "DCT-CI", 6)) {
+ state->type_flags |= DST_TYPE_HAS_TS204;
+ dprintk(verbose, DST_ERROR, 1, "Forcing [%s] to TS188", state->fw_name);
+ }
+
+ return -1;
+}
+
+static int dst_get_device_id(struct dst_state *state)
+{
+ u8 reply;
+
+ int i, j;
+ struct dst_types *p_dst_type = NULL;
+ struct tuner_types *p_tuner_list = NULL;
+
+ u8 use_dst_type = 0;
+ u32 use_type_flags = 0;
+
+ static u8 device_type[8] = {0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff};
+
+ state->tuner_type = 0;
+ device_type[7] = dst_check_sum(device_type, 7);
+
+ if (write_dst(state, device_type, FIXED_COMM))
+ return -1; /* Write failed */
+ if ((dst_pio_disable(state)) < 0)
+ return -1;
+ if (read_dst(state, &reply, GET_ACK))
+ return -1; /* Read failure */
+ if (reply != ACK) {
+ dprintk(verbose, DST_INFO, 1, "Write not Acknowledged! [Reply=0x%02x]", reply);
+ return -1; /* Unack'd write */
+ }
+ if (!dst_wait_dst_ready(state, DEVICE_INIT))
+ return -1; /* DST not ready yet */
+ if (read_dst(state, state->rxbuffer, FIXED_COMM))
+ return -1;
+
+ dst_pio_disable(state);
+ if (state->rxbuffer[7] != dst_check_sum(state->rxbuffer, 7)) {
+ dprintk(verbose, DST_INFO, 1, "Checksum failure!");
+ return -1; /* Checksum failure */
+ }
+ state->rxbuffer[7] = '\0';
+
+ for (i = 0, p_dst_type = dst_tlist; i < ARRAY_SIZE(dst_tlist); i++, p_dst_type++) {
+ if (!strncmp (&state->rxbuffer[p_dst_type->offset], p_dst_type->device_id, strlen (p_dst_type->device_id))) {
+ use_type_flags = p_dst_type->type_flags;
+ use_dst_type = p_dst_type->dst_type;
+
+ /* Card capabilities */
+ state->dst_hw_cap = p_dst_type->dst_feature;
+ dprintk(verbose, DST_ERROR, 1, "Recognise [%s]", p_dst_type->device_id);
+ strncpy(&state->fw_name[0], p_dst_type->device_id, 6);
+ /* Multiple tuners */
+ if (p_dst_type->tuner_type & TUNER_TYPE_MULTI) {
+ switch (use_dst_type) {
+ case DST_TYPE_IS_SAT:
+ /* STV0299 check */
+ if (dst_check_stv0299(state) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "Unsupported");
+ state->tuner_type = TUNER_TYPE_MB86A15;
+ }
+ break;
+ default:
+ break;
+ }
+ if (dst_check_mb86a15(state) < 0)
+ dprintk(verbose, DST_ERROR, 1, "Unsupported");
+ /* Single tuner */
+ } else {
+ state->tuner_type = p_dst_type->tuner_type;
+ }
+ for (j = 0, p_tuner_list = tuner_list; j < ARRAY_SIZE(tuner_list); j++, p_tuner_list++) {
+ if (!(strncmp(p_dst_type->device_id, p_tuner_list->fw_name, 7)) &&
+ p_tuner_list->tuner_type == state->tuner_type) {
+ dprintk(verbose, DST_ERROR, 1, "[%s] has a [%s]",
+ p_dst_type->device_id, p_tuner_list->tuner_name);
+ }
+ }
+ break;
+ }
+ }
+
+ if (i >= ARRAY_SIZE(dst_tlist)) {
+ dprintk(verbose, DST_ERROR, 1, "Unable to recognize %s or %s", &state->rxbuffer[0], &state->rxbuffer[1]);
+ dprintk(verbose, DST_ERROR, 1, "please email linux-dvb@linuxtv.org with this type in");
+ use_dst_type = DST_TYPE_IS_SAT;
+ use_type_flags = DST_TYPE_HAS_SYMDIV;
+ }
+ dst_type_print(state, use_dst_type);
+ state->type_flags = use_type_flags;
+ state->dst_type = use_dst_type;
+ dst_type_flags_print(state);
+
+ return 0;
+}
+
+static int dst_probe(struct dst_state *state)
+{
+ mutex_init(&state->dst_mutex);
+ if (dst_addons & DST_TYPE_HAS_CA) {
+ if ((rdc_8820_reset(state)) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "RDC 8820 RESET Failed.");
+ return -1;
+ }
+ msleep(4000);
+ } else {
+ msleep(100);
+ }
+ if ((dst_comm_init(state)) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "DST Initialization Failed.");
+ return -1;
+ }
+ msleep(100);
+ if (dst_get_device_id(state) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "unknown device.");
+ return -1;
+ }
+ if (dst_get_mac(state) < 0) {
+ dprintk(verbose, DST_INFO, 1, "MAC: Unsupported command");
+ }
+ if ((state->type_flags & DST_TYPE_HAS_MULTI_FE) || (state->type_flags & DST_TYPE_HAS_FW_BUILD)) {
+ if (dst_get_tuner_info(state) < 0)
+ dprintk(verbose, DST_INFO, 1, "Tuner: Unsupported command");
+ }
+ if (state->type_flags & DST_TYPE_HAS_TS204) {
+ dst_packsize(state, 204);
+ }
+ if (state->type_flags & DST_TYPE_HAS_FW_BUILD) {
+ if (dst_fw_ver(state) < 0) {
+ dprintk(verbose, DST_INFO, 1, "FW: Unsupported command");
+ return 0;
+ }
+ if (dst_card_type(state) < 0) {
+ dprintk(verbose, DST_INFO, 1, "Card: Unsupported command");
+ return 0;
+ }
+ if (dst_get_vendor(state) < 0) {
+ dprintk(verbose, DST_INFO, 1, "Vendor: Unsupported command");
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int dst_command(struct dst_state *state, u8 *data, u8 len)
+{
+ u8 reply;
+
+ mutex_lock(&state->dst_mutex);
+ if ((dst_comm_init(state)) < 0) {
+ dprintk(verbose, DST_NOTICE, 1, "DST Communication Initialization Failed.");
+ goto error;
+ }
+ if (write_dst(state, data, len)) {
+ dprintk(verbose, DST_INFO, 1, "Trying to recover.. ");
+ if ((dst_error_recovery(state)) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "Recovery Failed.");
+ goto error;
+ }
+ goto error;
+ }
+ if ((dst_pio_disable(state)) < 0) {
+ dprintk(verbose, DST_ERROR, 1, "PIO Disable Failed.");
+ goto error;
+ }
+ if (state->type_flags & DST_TYPE_HAS_FW_1)
+ mdelay(3);
+ if (read_dst(state, &reply, GET_ACK)) {
+ dprintk(verbose, DST_DEBUG, 1, "Trying to recover.. ");
+ if ((dst_error_recovery(state)) < 0) {
+ dprintk(verbose, DST_INFO, 1, "Recovery Failed.");
+ goto error;
+ }
+ goto error;
+ }
+ if (reply != ACK) {
+ dprintk(verbose, DST_INFO, 1, "write not acknowledged 0x%02x ", reply);
+ goto error;
+ }
+ if (len >= 2 && data[0] == 0 && (data[1] == 1 || data[1] == 3))
+ goto error;
+ if (state->type_flags & DST_TYPE_HAS_FW_1)
+ mdelay(3);
+ else
+ udelay(2000);
+ if (!dst_wait_dst_ready(state, NO_DELAY))
+ goto error;
+ if (read_dst(state, state->rxbuffer, FIXED_COMM)) {
+ dprintk(verbose, DST_DEBUG, 1, "Trying to recover.. ");
+ if ((dst_error_recovery(state)) < 0) {
+ dprintk(verbose, DST_INFO, 1, "Recovery failed.");
+ goto error;
+ }
+ goto error;
+ }
+ if (state->rxbuffer[7] != dst_check_sum(state->rxbuffer, 7)) {
+ dprintk(verbose, DST_INFO, 1, "checksum failure");
+ goto error;
+ }
+ mutex_unlock(&state->dst_mutex);
+ return 0;
+
+error:
+ mutex_unlock(&state->dst_mutex);
+ return -EIO;
+
+}
+
+static int dst_get_signal(struct dst_state *state)
+{
+ int retval;
+ u8 get_signal[] = { 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb };
+ //dprintk("%s: Getting Signal strength and other parameters\n", __func__);
+ if ((state->diseq_flags & ATTEMPT_TUNE) == 0) {
+ state->decode_lock = state->decode_strength = state->decode_snr = 0;
+ return 0;
+ }
+ if (0 == (state->diseq_flags & HAS_LOCK)) {
+ state->decode_lock = state->decode_strength = state->decode_snr = 0;
+ return 0;
+ }
+ if (time_after_eq(jiffies, state->cur_jiff + (HZ / 5))) {
+ retval = dst_command(state, get_signal, 8);
+ if (retval < 0)
+ return retval;
+ if (state->dst_type == DST_TYPE_IS_SAT) {
+ state->decode_lock = ((state->rxbuffer[6] & 0x10) == 0) ? 1 : 0;
+ state->decode_strength = state->rxbuffer[5] << 8;
+ state->decode_snr = state->rxbuffer[2] << 8 | state->rxbuffer[3];
+ } else if ((state->dst_type == DST_TYPE_IS_TERR) || (state->dst_type == DST_TYPE_IS_CABLE)) {
+ state->decode_lock = (state->rxbuffer[1]) ? 1 : 0;
+ state->decode_strength = state->rxbuffer[4] << 8;
+ state->decode_snr = state->rxbuffer[3] << 8;
+ } else if (state->dst_type == DST_TYPE_IS_ATSC) {
+ state->decode_lock = (state->rxbuffer[6] == 0x00) ? 1 : 0;
+ state->decode_strength = state->rxbuffer[4] << 8;
+ state->decode_snr = state->rxbuffer[2] << 8 | state->rxbuffer[3];
+ }
+ state->cur_jiff = jiffies;
+ }
+ return 0;
+}
+
+static int dst_tone_power_cmd(struct dst_state *state)
+{
+ u8 paket[8] = { 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 };
+
+ if (state->dst_type != DST_TYPE_IS_SAT)
+ return -EOPNOTSUPP;
+ paket[4] = state->tx_tuna[4];
+ paket[2] = state->tx_tuna[2];
+ paket[3] = state->tx_tuna[3];
+ paket[7] = dst_check_sum (paket, 7);
+ return dst_command(state, paket, 8);
+}
+
+static int dst_get_tuna(struct dst_state *state)
+{
+ int retval;
+
+ if ((state->diseq_flags & ATTEMPT_TUNE) == 0)
+ return 0;
+ state->diseq_flags &= ~(HAS_LOCK);
+ if (!dst_wait_dst_ready(state, NO_DELAY))
+ return -EIO;
+ if ((state->type_flags & DST_TYPE_HAS_VLF) &&
+ !(state->dst_type == DST_TYPE_IS_ATSC))
+
+ retval = read_dst(state, state->rx_tuna, 10);
+ else
+ retval = read_dst(state, &state->rx_tuna[2], FIXED_COMM);
+ if (retval < 0) {
+ dprintk(verbose, DST_DEBUG, 1, "read not successful");
+ return retval;
+ }
+ if ((state->type_flags & DST_TYPE_HAS_VLF) &&
+ !(state->dst_type == DST_TYPE_IS_ATSC)) {
+
+ if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[0], 9)) {
+ dprintk(verbose, DST_INFO, 1, "checksum failure ? ");
+ return -EIO;
+ }
+ } else {
+ if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[2], 7)) {
+ dprintk(verbose, DST_INFO, 1, "checksum failure? ");
+ return -EIO;
+ }
+ }
+ if (state->rx_tuna[2] == 0 && state->rx_tuna[3] == 0)
+ return 0;
+ if (state->dst_type == DST_TYPE_IS_SAT) {
+ state->decode_freq = ((state->rx_tuna[2] & 0x7f) << 8) + state->rx_tuna[3];
+ } else {
+ state->decode_freq = ((state->rx_tuna[2] & 0x7f) << 16) + (state->rx_tuna[3] << 8) + state->rx_tuna[4];
+ }
+ state->decode_freq = state->decode_freq * 1000;
+ state->decode_lock = 1;
+ state->diseq_flags |= HAS_LOCK;
+
+ return 1;
+}
+
+static int dst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage);
+
+static int dst_write_tuna(struct dvb_frontend *fe)
+{
+ struct dst_state *state = fe->demodulator_priv;
+ int retval;
+ u8 reply;
+
+ dprintk(verbose, DST_INFO, 1, "type_flags 0x%x ", state->type_flags);
+ state->decode_freq = 0;
+ state->decode_lock = state->decode_strength = state->decode_snr = 0;
+ if (state->dst_type == DST_TYPE_IS_SAT) {
+ if (!(state->diseq_flags & HAS_POWER))
+ dst_set_voltage(fe, SEC_VOLTAGE_13);
+ }
+ state->diseq_flags &= ~(HAS_LOCK | ATTEMPT_TUNE);
+ mutex_lock(&state->dst_mutex);
+ if ((dst_comm_init(state)) < 0) {
+ dprintk(verbose, DST_DEBUG, 1, "DST Communication initialization failed.");
+ goto error;
+ }
+// if (state->type_flags & DST_TYPE_HAS_NEWTUNE) {
+ if ((state->type_flags & DST_TYPE_HAS_VLF) &&
+ (!(state->dst_type == DST_TYPE_IS_ATSC))) {
+
+ state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[0], 9);
+ retval = write_dst(state, &state->tx_tuna[0], 10);
+ } else {
+ state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[2], 7);
+ retval = write_dst(state, &state->tx_tuna[2], FIXED_COMM);
+ }
+ if (retval < 0) {
+ dst_pio_disable(state);
+ dprintk(verbose, DST_DEBUG, 1, "write not successful");
+ goto werr;
+ }
+ if ((dst_pio_disable(state)) < 0) {
+ dprintk(verbose, DST_DEBUG, 1, "DST PIO disable failed !");
+ goto error;
+ }
+ if ((read_dst(state, &reply, GET_ACK) < 0)) {
+ dprintk(verbose, DST_DEBUG, 1, "read verify not successful.");
+ goto error;
+ }
+ if (reply != ACK) {
+ dprintk(verbose, DST_DEBUG, 1, "write not acknowledged 0x%02x ", reply);
+ goto error;
+ }
+ state->diseq_flags |= ATTEMPT_TUNE;
+ retval = dst_get_tuna(state);
+werr:
+ mutex_unlock(&state->dst_mutex);
+ return retval;
+
+error:
+ mutex_unlock(&state->dst_mutex);
+ return -EIO;
+}
+
+/*
+ * line22k0 0x00, 0x09, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00
+ * line22k1 0x00, 0x09, 0x01, 0xff, 0x01, 0x00, 0x00, 0x00
+ * line22k2 0x00, 0x09, 0x02, 0xff, 0x01, 0x00, 0x00, 0x00
+ * tone 0x00, 0x09, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00
+ * data 0x00, 0x09, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00
+ * power_off 0x00, 0x09, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00
+ * power_on 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00
+ * Diseqc 1 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec
+ * Diseqc 2 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf4, 0xe8
+ * Diseqc 3 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf8, 0xe4
+ * Diseqc 4 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xfc, 0xe0
+ */
+
+static int dst_set_diseqc(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd)
+{
+ struct dst_state *state = fe->demodulator_priv;
+ u8 paket[8] = { 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec };
+
+ if (state->dst_type != DST_TYPE_IS_SAT)
+ return -EOPNOTSUPP;
+ if (cmd->msg_len > 0 && cmd->msg_len < 5)
+ memcpy(&paket[3], cmd->msg, cmd->msg_len);
+ else if (cmd->msg_len == 5 && state->dst_hw_cap & DST_TYPE_HAS_DISEQC5)
+ memcpy(&paket[2], cmd->msg, cmd->msg_len);
+ else
+ return -EINVAL;
+ paket[7] = dst_check_sum(&paket[0], 7);
+ return dst_command(state, paket, 8);
+}
+
+static int dst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ int need_cmd, retval = 0;
+ struct dst_state *state = fe->demodulator_priv;
+
+ state->voltage = voltage;
+ if (state->dst_type != DST_TYPE_IS_SAT)
+ return -EOPNOTSUPP;
+
+ need_cmd = 0;
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ case SEC_VOLTAGE_18:
+ if ((state->diseq_flags & HAS_POWER) == 0)
+ need_cmd = 1;
+ state->diseq_flags |= HAS_POWER;
+ state->tx_tuna[4] = 0x01;
+ break;
+ case SEC_VOLTAGE_OFF:
+ need_cmd = 1;
+ state->diseq_flags &= ~(HAS_POWER | HAS_LOCK | ATTEMPT_TUNE);
+ state->tx_tuna[4] = 0x00;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (need_cmd)
+ retval = dst_tone_power_cmd(state);
+
+ return retval;
+}
+
+static int dst_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+ struct dst_state *state = fe->demodulator_priv;
+
+ state->tone = tone;
+ if (state->dst_type != DST_TYPE_IS_SAT)
+ return -EOPNOTSUPP;
+
+ switch (tone) {
+ case SEC_TONE_OFF:
+ if (state->type_flags & DST_TYPE_HAS_OBS_REGS)
+ state->tx_tuna[2] = 0x00;
+ else
+ state->tx_tuna[2] = 0xff;
+ break;
+
+ case SEC_TONE_ON:
+ state->tx_tuna[2] = 0x02;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return dst_tone_power_cmd(state);
+}
+
+static int dst_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t minicmd)
+{
+ struct dst_state *state = fe->demodulator_priv;
+
+ if (state->dst_type != DST_TYPE_IS_SAT)
+ return -EOPNOTSUPP;
+ state->minicmd = minicmd;
+ switch (minicmd) {
+ case SEC_MINI_A:
+ state->tx_tuna[3] = 0x02;
+ break;
+ case SEC_MINI_B:
+ state->tx_tuna[3] = 0xff;
+ break;
+ }
+ return dst_tone_power_cmd(state);
+}
+
+
+static int dst_init(struct dvb_frontend *fe)
+{
+ struct dst_state *state = fe->demodulator_priv;
+
+ static u8 sat_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x00, 0x73, 0x21, 0x00, 0x00 };
+ static u8 sat_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x55, 0xbd, 0x50, 0x00, 0x00 };
+ static u8 ter_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 };
+ static u8 ter_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 };
+ static u8 cab_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 };
+ static u8 cab_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 };
+ static u8 atsc_tuner[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 };
+
+ state->inversion = INVERSION_OFF;
+ state->voltage = SEC_VOLTAGE_13;
+ state->tone = SEC_TONE_OFF;
+ state->diseq_flags = 0;
+ state->k22 = 0x02;
+ state->bandwidth = 7000000;
+ state->cur_jiff = jiffies;
+ if (state->dst_type == DST_TYPE_IS_SAT)
+ memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? sat_tuna_188 : sat_tuna_204), sizeof (sat_tuna_204));
+ else if (state->dst_type == DST_TYPE_IS_TERR)
+ memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? ter_tuna_188 : ter_tuna_204), sizeof (ter_tuna_204));
+ else if (state->dst_type == DST_TYPE_IS_CABLE)
+ memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? cab_tuna_188 : cab_tuna_204), sizeof (cab_tuna_204));
+ else if (state->dst_type == DST_TYPE_IS_ATSC)
+ memcpy(state->tx_tuna, atsc_tuner, sizeof (atsc_tuner));
+
+ return 0;
+}
+
+static int dst_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct dst_state *state = fe->demodulator_priv;
+
+ *status = 0;
+ if (state->diseq_flags & HAS_LOCK) {
+// dst_get_signal(state); // don't require(?) to ask MCU
+ if (state->decode_lock)
+ *status |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC | FE_HAS_VITERBI;
+ }
+
+ return 0;
+}
+
+static int dst_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct dst_state *state = fe->demodulator_priv;
+
+ int retval = dst_get_signal(state);
+ *strength = state->decode_strength;
+
+ return retval;
+}
+
+static int dst_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct dst_state *state = fe->demodulator_priv;
+
+ int retval = dst_get_signal(state);
+ *snr = state->decode_snr;
+
+ return retval;
+}
+
+static int dst_set_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ int retval = -EINVAL;
+ struct dst_state *state = fe->demodulator_priv;
+
+ if (p != NULL) {
+ retval = dst_set_freq(state, p->frequency);
+ if(retval != 0)
+ return retval;
+ dprintk(verbose, DST_DEBUG, 1, "Set Frequency=[%d]", p->frequency);
+
+ if (state->dst_type == DST_TYPE_IS_SAT) {
+ if (state->type_flags & DST_TYPE_HAS_OBS_REGS)
+ dst_set_inversion(state, p->inversion);
+ dst_set_fec(state, p->fec_inner);
+ dst_set_symbolrate(state, p->symbol_rate);
+ dst_set_polarization(state);
+ dprintk(verbose, DST_DEBUG, 1, "Set Symbolrate=[%d]", p->symbol_rate);
+
+ } else if (state->dst_type == DST_TYPE_IS_TERR)
+ dst_set_bandwidth(state, p->bandwidth_hz);
+ else if (state->dst_type == DST_TYPE_IS_CABLE) {
+ dst_set_fec(state, p->fec_inner);
+ dst_set_symbolrate(state, p->symbol_rate);
+ dst_set_modulation(state, p->modulation);
+ }
+ retval = dst_write_tuna(fe);
+ }
+
+ return retval;
+}
+
+static int dst_tune_frontend(struct dvb_frontend* fe,
+ bool re_tune,
+ unsigned int mode_flags,
+ unsigned int *delay,
+ fe_status_t *status)
+{
+ struct dst_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ if (re_tune) {
+ dst_set_freq(state, p->frequency);
+ dprintk(verbose, DST_DEBUG, 1, "Set Frequency=[%d]", p->frequency);
+
+ if (state->dst_type == DST_TYPE_IS_SAT) {
+ if (state->type_flags & DST_TYPE_HAS_OBS_REGS)
+ dst_set_inversion(state, p->inversion);
+ dst_set_fec(state, p->fec_inner);
+ dst_set_symbolrate(state, p->symbol_rate);
+ dst_set_polarization(state);
+ dprintk(verbose, DST_DEBUG, 1, "Set Symbolrate=[%d]", p->symbol_rate);
+
+ } else if (state->dst_type == DST_TYPE_IS_TERR)
+ dst_set_bandwidth(state, p->bandwidth_hz);
+ else if (state->dst_type == DST_TYPE_IS_CABLE) {
+ dst_set_fec(state, p->fec_inner);
+ dst_set_symbolrate(state, p->symbol_rate);
+ dst_set_modulation(state, p->modulation);
+ }
+ dst_write_tuna(fe);
+ }
+
+ if (!(mode_flags & FE_TUNE_MODE_ONESHOT))
+ dst_read_status(fe, status);
+
+ *delay = HZ/10;
+ return 0;
+}
+
+static int dst_get_tuning_algo(struct dvb_frontend *fe)
+{
+ return dst_algo ? DVBFE_ALGO_HW : DVBFE_ALGO_SW;
+}
+
+static int dst_get_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct dst_state *state = fe->demodulator_priv;
+
+ p->frequency = state->decode_freq;
+ if (state->dst_type == DST_TYPE_IS_SAT) {
+ if (state->type_flags & DST_TYPE_HAS_OBS_REGS)
+ p->inversion = state->inversion;
+ p->symbol_rate = state->symbol_rate;
+ p->fec_inner = dst_get_fec(state);
+ } else if (state->dst_type == DST_TYPE_IS_TERR) {
+ p->bandwidth_hz = state->bandwidth;
+ } else if (state->dst_type == DST_TYPE_IS_CABLE) {
+ p->symbol_rate = state->symbol_rate;
+ p->fec_inner = dst_get_fec(state);
+ p->modulation = dst_get_modulation(state);
+ }
+
+ return 0;
+}
+
+static void dst_release(struct dvb_frontend *fe)
+{
+ struct dst_state *state = fe->demodulator_priv;
+ if (state->dst_ca) {
+ dvb_unregister_device(state->dst_ca);
+#ifdef CONFIG_MEDIA_ATTACH
+ symbol_put(dst_ca_attach);
+#endif
+ }
+ kfree(state);
+}
+
+static struct dvb_frontend_ops dst_dvbt_ops;
+static struct dvb_frontend_ops dst_dvbs_ops;
+static struct dvb_frontend_ops dst_dvbc_ops;
+static struct dvb_frontend_ops dst_atsc_ops;
+
+struct dst_state *dst_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter)
+{
+ /* check if the ASIC is there */
+ if (dst_probe(state) < 0) {
+ kfree(state);
+ return NULL;
+ }
+ /* determine settings based on type */
+ /* create dvb_frontend */
+ switch (state->dst_type) {
+ case DST_TYPE_IS_TERR:
+ memcpy(&state->frontend.ops, &dst_dvbt_ops, sizeof(struct dvb_frontend_ops));
+ break;
+ case DST_TYPE_IS_CABLE:
+ memcpy(&state->frontend.ops, &dst_dvbc_ops, sizeof(struct dvb_frontend_ops));
+ break;
+ case DST_TYPE_IS_SAT:
+ memcpy(&state->frontend.ops, &dst_dvbs_ops, sizeof(struct dvb_frontend_ops));
+ break;
+ case DST_TYPE_IS_ATSC:
+ memcpy(&state->frontend.ops, &dst_atsc_ops, sizeof(struct dvb_frontend_ops));
+ break;
+ default:
+ dprintk(verbose, DST_ERROR, 1, "unknown DST type. please report to the LinuxTV.org DVB mailinglist.");
+ kfree(state);
+ return NULL;
+ }
+ state->frontend.demodulator_priv = state;
+
+ return state; /* Manu (DST is a card not a frontend) */
+}
+
+EXPORT_SYMBOL(dst_attach);
+
+static struct dvb_frontend_ops dst_dvbt_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "DST DVB-T",
+ .frequency_min = 137000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 166667,
+ .caps = FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_32 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 |
+ FE_CAN_QAM_256 |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO
+ },
+
+ .release = dst_release,
+ .init = dst_init,
+ .tune = dst_tune_frontend,
+ .set_frontend = dst_set_frontend,
+ .get_frontend = dst_get_frontend,
+ .get_frontend_algo = dst_get_tuning_algo,
+ .read_status = dst_read_status,
+ .read_signal_strength = dst_read_signal_strength,
+ .read_snr = dst_read_snr,
+};
+
+static struct dvb_frontend_ops dst_dvbs_ops = {
+ .delsys = { SYS_DVBS },
+ .info = {
+ .name = "DST DVB-S",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 1000, /* kHz for QPSK frontends */
+ .frequency_tolerance = 29500,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ /* . symbol_rate_tolerance = ???,*/
+ .caps = FE_CAN_FEC_AUTO | FE_CAN_QPSK
+ },
+
+ .release = dst_release,
+ .init = dst_init,
+ .tune = dst_tune_frontend,
+ .set_frontend = dst_set_frontend,
+ .get_frontend = dst_get_frontend,
+ .get_frontend_algo = dst_get_tuning_algo,
+ .read_status = dst_read_status,
+ .read_signal_strength = dst_read_signal_strength,
+ .read_snr = dst_read_snr,
+ .diseqc_send_burst = dst_send_burst,
+ .diseqc_send_master_cmd = dst_set_diseqc,
+ .set_voltage = dst_set_voltage,
+ .set_tone = dst_set_tone,
+};
+
+static struct dvb_frontend_ops dst_dvbc_ops = {
+ .delsys = { SYS_DVBC_ANNEX_A },
+ .info = {
+ .name = "DST DVB-C",
+ .frequency_stepsize = 62500,
+ .frequency_min = 51000000,
+ .frequency_max = 858000000,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .caps = FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_32 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 |
+ FE_CAN_QAM_256
+ },
+
+ .release = dst_release,
+ .init = dst_init,
+ .tune = dst_tune_frontend,
+ .set_frontend = dst_set_frontend,
+ .get_frontend = dst_get_frontend,
+ .get_frontend_algo = dst_get_tuning_algo,
+ .read_status = dst_read_status,
+ .read_signal_strength = dst_read_signal_strength,
+ .read_snr = dst_read_snr,
+};
+
+static struct dvb_frontend_ops dst_atsc_ops = {
+ .delsys = { SYS_ATSC },
+ .info = {
+ .name = "DST ATSC",
+ .frequency_stepsize = 62500,
+ .frequency_min = 510000000,
+ .frequency_max = 858000000,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
+ },
+
+ .release = dst_release,
+ .init = dst_init,
+ .tune = dst_tune_frontend,
+ .set_frontend = dst_set_frontend,
+ .get_frontend = dst_get_frontend,
+ .get_frontend_algo = dst_get_tuning_algo,
+ .read_status = dst_read_status,
+ .read_signal_strength = dst_read_signal_strength,
+ .read_snr = dst_read_snr,
+};
+
+MODULE_DESCRIPTION("DST DVB-S/T/C/ATSC Combo Frontend driver");
+MODULE_AUTHOR("Jamie Honan, Manu Abraham");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c
new file mode 100644
index 0000000..ee3884f
--- /dev/null
+++ b/drivers/media/pci/bt8xx/dst_ca.c
@@ -0,0 +1,726 @@
+/*
+ CA-driver for TwinHan DST Frontend/Card
+
+ Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/dvb/ca.h>
+#include "dvbdev.h"
+#include "dvb_frontend.h"
+#include "dst_ca.h"
+#include "dst_common.h"
+
+#define DST_CA_ERROR 0
+#define DST_CA_NOTICE 1
+#define DST_CA_INFO 2
+#define DST_CA_DEBUG 3
+
+#define dprintk(x, y, z, format, arg...) do { \
+ if (z) { \
+ if ((x > DST_CA_ERROR) && (x > y)) \
+ printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \
+ else if ((x > DST_CA_NOTICE) && (x > y)) \
+ printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \
+ else if ((x > DST_CA_INFO) && (x > y)) \
+ printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \
+ else if ((x > DST_CA_DEBUG) && (x > y)) \
+ printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \
+ } else { \
+ if (x > y) \
+ printk(format, ## arg); \
+ } \
+} while(0)
+
+
+static DEFINE_MUTEX(dst_ca_mutex);
+static unsigned int verbose = 5;
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)");
+
+/* Need some more work */
+static int ca_set_slot_descr(void)
+{
+ /* We could make this more graceful ? */
+ return -EOPNOTSUPP;
+}
+
+/* Need some more work */
+static int ca_set_pid(void)
+{
+ /* We could make this more graceful ? */
+ return -EOPNOTSUPP;
+}
+
+static void put_command_and_length(u8 *data, int command, int length)
+{
+ data[0] = (command >> 16) & 0xff;
+ data[1] = (command >> 8) & 0xff;
+ data[2] = command & 0xff;
+ data[3] = length;
+}
+
+static void put_checksum(u8 *check_string, int length)
+{
+ dprintk(verbose, DST_CA_DEBUG, 1, " Computing string checksum.");
+ dprintk(verbose, DST_CA_DEBUG, 1, " -> string length : 0x%02x", length);
+ check_string[length] = dst_check_sum (check_string, length);
+ dprintk(verbose, DST_CA_DEBUG, 1, " -> checksum : 0x%02x", check_string[length]);
+}
+
+static int dst_ci_command(struct dst_state* state, u8 * data, u8 *ca_string, u8 len, int read)
+{
+ u8 reply;
+
+ mutex_lock(&state->dst_mutex);
+ dst_comm_init(state);
+ msleep(65);
+
+ if (write_dst(state, data, len)) {
+ dprintk(verbose, DST_CA_INFO, 1, " Write not successful, trying to recover");
+ dst_error_recovery(state);
+ goto error;
+ }
+ if ((dst_pio_disable(state)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " DST PIO disable failed.");
+ goto error;
+ }
+ if (read_dst(state, &reply, GET_ACK) < 0) {
+ dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover");
+ dst_error_recovery(state);
+ goto error;
+ }
+ if (read) {
+ if (! dst_wait_dst_ready(state, LONG_DELAY)) {
+ dprintk(verbose, DST_CA_NOTICE, 1, " 8820 not ready");
+ goto error;
+ }
+ if (read_dst(state, ca_string, 128) < 0) { /* Try to make this dynamic */
+ dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover");
+ dst_error_recovery(state);
+ goto error;
+ }
+ }
+ mutex_unlock(&state->dst_mutex);
+ return 0;
+
+error:
+ mutex_unlock(&state->dst_mutex);
+ return -EIO;
+}
+
+
+static int dst_put_ci(struct dst_state *state, u8 *data, int len, u8 *ca_string, int read)
+{
+ u8 dst_ca_comm_err = 0;
+
+ while (dst_ca_comm_err < RETRIES) {
+ dprintk(verbose, DST_CA_NOTICE, 1, " Put Command");
+ if (dst_ci_command(state, data, ca_string, len, read)) { // If error
+ dst_error_recovery(state);
+ dst_ca_comm_err++; // work required here.
+ } else {
+ break;
+ }
+ }
+
+ if(dst_ca_comm_err == RETRIES)
+ return -1;
+
+ return 0;
+}
+
+
+
+static int ca_get_app_info(struct dst_state *state)
+{
+ int length, str_length;
+ static u8 command[8] = {0x07, 0x40, 0x01, 0x00, 0x01, 0x00, 0x00, 0xff};
+
+ put_checksum(&command[0], command[0]);
+ if ((dst_put_ci(state, command, sizeof(command), state->messages, GET_REPLY)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
+ return -1;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !");
+ dprintk(verbose, DST_CA_INFO, 1, " ================================ CI Module Application Info ======================================");
+ dprintk(verbose, DST_CA_INFO, 1, " Application Type=[%d], Application Vendor=[%d], Vendor Code=[%d]\n%s: Application info=[%s]",
+ state->messages[7], (state->messages[8] << 8) | state->messages[9],
+ (state->messages[10] << 8) | state->messages[11], __func__, (char *)(&state->messages[12]));
+ dprintk(verbose, DST_CA_INFO, 1, " ==================================================================================================");
+
+ // Transform dst message to correct application_info message
+ length = state->messages[5];
+ str_length = length - 6;
+ if (str_length < 0) {
+ str_length = 0;
+ dprintk(verbose, DST_CA_ERROR, 1, "Invalid string length returned in ca_get_app_info(). Recovering.");
+ }
+
+ // First, the command and length fields
+ put_command_and_length(&state->messages[0], CA_APP_INFO, length);
+
+ // Copy application_type, application_manufacturer and manufacturer_code
+ memcpy(&state->messages[4], &state->messages[7], 5);
+
+ // Set string length and copy string
+ state->messages[9] = str_length;
+ memcpy(&state->messages[10], &state->messages[12], str_length);
+
+ return 0;
+}
+
+static int ca_get_ca_info(struct dst_state *state)
+{
+ int srcPtr, dstPtr, i, num_ids;
+ static u8 slot_command[8] = {0x07, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff};
+ const int in_system_id_pos = 8, out_system_id_pos = 4, in_num_ids_pos = 7;
+
+ put_checksum(&slot_command[0], slot_command[0]);
+ if ((dst_put_ci(state, slot_command, sizeof (slot_command), state->messages, GET_REPLY)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
+ return -1;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !");
+
+ // Print raw data
+ dprintk(verbose, DST_CA_INFO, 0, " DST data = [");
+ for (i = 0; i < state->messages[0] + 1; i++) {
+ dprintk(verbose, DST_CA_INFO, 0, " 0x%02x", state->messages[i]);
+ }
+ dprintk(verbose, DST_CA_INFO, 0, "]\n");
+
+ // Set the command and length of the output
+ num_ids = state->messages[in_num_ids_pos];
+ if (num_ids >= 100) {
+ num_ids = 100;
+ dprintk(verbose, DST_CA_ERROR, 1, "Invalid number of ids (>100). Recovering.");
+ }
+ put_command_and_length(&state->messages[0], CA_INFO, num_ids * 2);
+
+ dprintk(verbose, DST_CA_INFO, 0, " CA_INFO = [");
+ srcPtr = in_system_id_pos;
+ dstPtr = out_system_id_pos;
+ for(i = 0; i < num_ids; i++) {
+ dprintk(verbose, DST_CA_INFO, 0, " 0x%02x%02x", state->messages[srcPtr + 0], state->messages[srcPtr + 1]);
+ // Append to output
+ state->messages[dstPtr + 0] = state->messages[srcPtr + 0];
+ state->messages[dstPtr + 1] = state->messages[srcPtr + 1];
+ srcPtr += 2;
+ dstPtr += 2;
+ }
+ dprintk(verbose, DST_CA_INFO, 0, "]\n");
+
+ return 0;
+}
+
+static int ca_get_slot_caps(struct dst_state *state, struct ca_caps *p_ca_caps, void __user *arg)
+{
+ int i;
+ u8 slot_cap[256];
+ static u8 slot_command[8] = {0x07, 0x40, 0x02, 0x00, 0x02, 0x00, 0x00, 0xff};
+
+ put_checksum(&slot_command[0], slot_command[0]);
+ if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_cap, GET_REPLY)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
+ return -1;
+ }
+ dprintk(verbose, DST_CA_NOTICE, 1, " -->dst_put_ci SUCCESS !");
+
+ /* Will implement the rest soon */
+
+ dprintk(verbose, DST_CA_INFO, 1, " Slot cap = [%d]", slot_cap[7]);
+ dprintk(verbose, DST_CA_INFO, 0, "===================================\n");
+ for (i = 0; i < slot_cap[0] + 1; i++)
+ dprintk(verbose, DST_CA_INFO, 0, " %d", slot_cap[i]);
+ dprintk(verbose, DST_CA_INFO, 0, "\n");
+
+ p_ca_caps->slot_num = 1;
+ p_ca_caps->slot_type = 1;
+ p_ca_caps->descr_num = slot_cap[7];
+ p_ca_caps->descr_type = 1;
+
+ if (copy_to_user(arg, p_ca_caps, sizeof (struct ca_caps)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/* Need some more work */
+static int ca_get_slot_descr(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg)
+{
+ return -EOPNOTSUPP;
+}
+
+
+static int ca_get_slot_info(struct dst_state *state, struct ca_slot_info *p_ca_slot_info, void __user *arg)
+{
+ int i;
+ static u8 slot_command[8] = {0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff};
+
+ u8 *slot_info = state->messages;
+
+ put_checksum(&slot_command[0], 7);
+ if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_info, GET_REPLY)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
+ return -1;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !");
+
+ /* Will implement the rest soon */
+
+ dprintk(verbose, DST_CA_INFO, 1, " Slot info = [%d]", slot_info[3]);
+ dprintk(verbose, DST_CA_INFO, 0, "===================================\n");
+ for (i = 0; i < 8; i++)
+ dprintk(verbose, DST_CA_INFO, 0, " %d", slot_info[i]);
+ dprintk(verbose, DST_CA_INFO, 0, "\n");
+
+ if (slot_info[4] & 0x80) {
+ p_ca_slot_info->flags = CA_CI_MODULE_PRESENT;
+ p_ca_slot_info->num = 1;
+ p_ca_slot_info->type = CA_CI;
+ } else if (slot_info[4] & 0x40) {
+ p_ca_slot_info->flags = CA_CI_MODULE_READY;
+ p_ca_slot_info->num = 1;
+ p_ca_slot_info->type = CA_CI;
+ } else
+ p_ca_slot_info->flags = 0;
+
+ if (copy_to_user(arg, p_ca_slot_info, sizeof (struct ca_slot_info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int ca_get_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg)
+{
+ u8 i = 0;
+ u32 command = 0;
+
+ if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg)))
+ return -EFAULT;
+
+ if (p_ca_message->msg) {
+ dprintk(verbose, DST_CA_NOTICE, 1, " Message = [%*ph]",
+ 3, p_ca_message->msg);
+
+ for (i = 0; i < 3; i++) {
+ command = command | p_ca_message->msg[i];
+ if (i < 2)
+ command = command << 8;
+ }
+ dprintk(verbose, DST_CA_NOTICE, 1, " Command=[0x%x]", command);
+
+ switch (command) {
+ case CA_APP_INFO:
+ memcpy(p_ca_message->msg, state->messages, 128);
+ if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) )
+ return -EFAULT;
+ break;
+ case CA_INFO:
+ memcpy(p_ca_message->msg, state->messages, 128);
+ if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) )
+ return -EFAULT;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int handle_dst_tag(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u32 length)
+{
+ if (state->dst_hw_cap & DST_TYPE_HAS_SESSION) {
+ hw_buffer->msg[2] = p_ca_message->msg[1]; /* MSB */
+ hw_buffer->msg[3] = p_ca_message->msg[2]; /* LSB */
+ } else {
+ if (length > 247) {
+ dprintk(verbose, DST_CA_ERROR, 1, " Message too long ! *** Bailing Out *** !");
+ return -1;
+ }
+ hw_buffer->msg[0] = (length & 0xff) + 7;
+ hw_buffer->msg[1] = 0x40;
+ hw_buffer->msg[2] = 0x03;
+ hw_buffer->msg[3] = 0x00;
+ hw_buffer->msg[4] = 0x03;
+ hw_buffer->msg[5] = length & 0xff;
+ hw_buffer->msg[6] = 0x00;
+
+ /*
+ * Need to compute length for EN50221 section 8.3.2, for the time being
+ * assuming 8.3.2 is not applicable
+ */
+ memcpy(&hw_buffer->msg[7], &p_ca_message->msg[4], length);
+ }
+
+ return 0;
+}
+
+static int write_to_8820(struct dst_state *state, struct ca_msg *hw_buffer, u8 length, u8 reply)
+{
+ if ((dst_put_ci(state, hw_buffer->msg, length, hw_buffer->msg, reply)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " DST-CI Command failed.");
+ dprintk(verbose, DST_CA_NOTICE, 1, " Resetting DST.");
+ rdc_reset_state(state);
+ return -1;
+ }
+ dprintk(verbose, DST_CA_NOTICE, 1, " DST-CI Command success.");
+
+ return 0;
+}
+
+static u32 asn_1_decode(u8 *asn_1_array)
+{
+ u8 length_field = 0, word_count = 0, count = 0;
+ u32 length = 0;
+
+ length_field = asn_1_array[0];
+ dprintk(verbose, DST_CA_DEBUG, 1, " Length field=[%02x]", length_field);
+ if (length_field < 0x80) {
+ length = length_field & 0x7f;
+ dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%02x]\n", length);
+ } else {
+ word_count = length_field & 0x7f;
+ for (count = 0; count < word_count; count++) {
+ length = length << 8;
+ length += asn_1_array[count + 1];
+ dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%04x]", length);
+ }
+ }
+ return length;
+}
+
+static int debug_string(u8 *msg, u32 length, u32 offset)
+{
+ u32 i;
+
+ dprintk(verbose, DST_CA_DEBUG, 0, " String=[ ");
+ for (i = offset; i < length; i++)
+ dprintk(verbose, DST_CA_DEBUG, 0, "%02x ", msg[i]);
+ dprintk(verbose, DST_CA_DEBUG, 0, "]\n");
+
+ return 0;
+}
+
+
+static int ca_set_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u8 reply, u8 query)
+{
+ u32 length = 0;
+ u8 tag_length = 8;
+
+ length = asn_1_decode(&p_ca_message->msg[3]);
+ dprintk(verbose, DST_CA_DEBUG, 1, " CA Message length=[%d]", length);
+ debug_string(&p_ca_message->msg[4], length, 0); /* length is excluding tag & length */
+
+ memset(hw_buffer->msg, '\0', length);
+ handle_dst_tag(state, p_ca_message, hw_buffer, length);
+ put_checksum(hw_buffer->msg, hw_buffer->msg[0]);
+
+ debug_string(hw_buffer->msg, (length + tag_length), 0); /* tags too */
+ write_to_8820(state, hw_buffer, (length + tag_length), reply);
+
+ return 0;
+}
+
+
+/* Board supports CA PMT reply ? */
+static int dst_check_ca_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer)
+{
+ int ca_pmt_reply_test = 0;
+
+ /* Do test board */
+ /* Not there yet but soon */
+
+ /* CA PMT Reply capable */
+ if (ca_pmt_reply_test) {
+ if ((ca_set_pmt(state, p_ca_message, hw_buffer, 1, GET_REPLY)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !");
+ return -1;
+ }
+
+ /* Process CA PMT Reply */
+ /* will implement soon */
+ dprintk(verbose, DST_CA_ERROR, 1, " Not there yet");
+ }
+ /* CA PMT Reply not capable */
+ if (!ca_pmt_reply_test) {
+ if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, NO_REPLY)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !");
+ return -1;
+ }
+ dprintk(verbose, DST_CA_NOTICE, 1, " ca_set_pmt.. success !");
+ /* put a dummy message */
+
+ }
+ return 0;
+}
+
+static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg)
+{
+ int i = 0;
+
+ u32 command = 0;
+ struct ca_msg *hw_buffer;
+ int result = 0;
+
+ if ((hw_buffer = kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) {
+ dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure");
+ return -ENOMEM;
+ }
+ dprintk(verbose, DST_CA_DEBUG, 1, " ");
+
+ if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg))) {
+ result = -EFAULT;
+ goto free_mem_and_exit;
+ }
+
+
+ if (p_ca_message->msg) {
+ /* EN50221 tag */
+ command = 0;
+
+ for (i = 0; i < 3; i++) {
+ command = command | p_ca_message->msg[i];
+ if (i < 2)
+ command = command << 8;
+ }
+ dprintk(verbose, DST_CA_DEBUG, 1, " Command=[0x%x]\n", command);
+
+ switch (command) {
+ case CA_PMT:
+ dprintk(verbose, DST_CA_DEBUG, 1, "Command = SEND_CA_PMT");
+ if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, 0)) < 0) { // code simplification started
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT Success !");
+ break;
+ case CA_PMT_REPLY:
+ dprintk(verbose, DST_CA_INFO, 1, "Command = CA_PMT_REPLY");
+ /* Have to handle the 2 basic types of cards here */
+ if ((dst_check_ca_pmt(state, p_ca_message, hw_buffer)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT_REPLY Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT_REPLY Success !");
+ break;
+ case CA_APP_INFO_ENQUIRY: // only for debugging
+ dprintk(verbose, DST_CA_INFO, 1, " Getting Cam Application information");
+
+ if ((ca_get_app_info(state)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_APP_INFO_ENQUIRY Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_APP_INFO_ENQUIRY Success !");
+ break;
+ case CA_INFO_ENQUIRY:
+ dprintk(verbose, DST_CA_INFO, 1, " Getting CA Information");
+
+ if ((ca_get_ca_info(state)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_INFO_ENQUIRY Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_INFO_ENQUIRY Success !");
+ break;
+ }
+ }
+free_mem_and_exit:
+ kfree (hw_buffer);
+
+ return result;
+}
+
+static long dst_ca_ioctl(struct file *file, unsigned int cmd, unsigned long ioctl_arg)
+{
+ struct dvb_device *dvbdev;
+ struct dst_state *state;
+ struct ca_slot_info *p_ca_slot_info;
+ struct ca_caps *p_ca_caps;
+ struct ca_msg *p_ca_message;
+ void __user *arg = (void __user *)ioctl_arg;
+ int result = 0;
+
+ mutex_lock(&dst_ca_mutex);
+ dvbdev = file->private_data;
+ state = (struct dst_state *)dvbdev->priv;
+ p_ca_message = kmalloc(sizeof (struct ca_msg), GFP_KERNEL);
+ p_ca_slot_info = kmalloc(sizeof (struct ca_slot_info), GFP_KERNEL);
+ p_ca_caps = kmalloc(sizeof (struct ca_caps), GFP_KERNEL);
+ if (!p_ca_message || !p_ca_slot_info || !p_ca_caps) {
+ dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure");
+ result = -ENOMEM;
+ goto free_mem_and_exit;
+ }
+
+ /* We have now only the standard ioctl's, the driver is upposed to handle internals. */
+ switch (cmd) {
+ case CA_SEND_MSG:
+ dprintk(verbose, DST_CA_INFO, 1, " Sending message");
+ if ((ca_send_message(state, p_ca_message, arg)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SEND_MSG Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ break;
+ case CA_GET_MSG:
+ dprintk(verbose, DST_CA_INFO, 1, " Getting message");
+ if ((ca_get_message(state, p_ca_message, arg)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_MSG Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_MSG Success !");
+ break;
+ case CA_RESET:
+ dprintk(verbose, DST_CA_ERROR, 1, " Resetting DST");
+ dst_error_bailout(state);
+ msleep(4000);
+ break;
+ case CA_GET_SLOT_INFO:
+ dprintk(verbose, DST_CA_INFO, 1, " Getting Slot info");
+ if ((ca_get_slot_info(state, p_ca_slot_info, arg)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_SLOT_INFO Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_SLOT_INFO Success !");
+ break;
+ case CA_GET_CAP:
+ dprintk(verbose, DST_CA_INFO, 1, " Getting Slot capabilities");
+ if ((ca_get_slot_caps(state, p_ca_caps, arg)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_CAP Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_CAP Success !");
+ break;
+ case CA_GET_DESCR_INFO:
+ dprintk(verbose, DST_CA_INFO, 1, " Getting descrambler description");
+ if ((ca_get_slot_descr(state, p_ca_message, arg)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_DESCR_INFO Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_DESCR_INFO Success !");
+ break;
+ case CA_SET_DESCR:
+ dprintk(verbose, DST_CA_INFO, 1, " Setting descrambler");
+ if ((ca_set_slot_descr()) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_DESCR Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_DESCR Success !");
+ break;
+ case CA_SET_PID:
+ dprintk(verbose, DST_CA_INFO, 1, " Setting PID");
+ if ((ca_set_pid()) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_PID Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_PID Success !");
+ default:
+ result = -EOPNOTSUPP;
+ };
+ free_mem_and_exit:
+ kfree (p_ca_message);
+ kfree (p_ca_slot_info);
+ kfree (p_ca_caps);
+
+ mutex_unlock(&dst_ca_mutex);
+ return result;
+}
+
+static int dst_ca_open(struct inode *inode, struct file *file)
+{
+ dprintk(verbose, DST_CA_DEBUG, 1, " Device opened [%p] ", file);
+ try_module_get(THIS_MODULE);
+
+ return 0;
+}
+
+static int dst_ca_release(struct inode *inode, struct file *file)
+{
+ dprintk(verbose, DST_CA_DEBUG, 1, " Device closed.");
+ module_put(THIS_MODULE);
+
+ return 0;
+}
+
+static ssize_t dst_ca_read(struct file *file, char __user *buffer, size_t length, loff_t *offset)
+{
+ ssize_t bytes_read = 0;
+
+ dprintk(verbose, DST_CA_DEBUG, 1, " Device read.");
+
+ return bytes_read;
+}
+
+static ssize_t dst_ca_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset)
+{
+ dprintk(verbose, DST_CA_DEBUG, 1, " Device write.");
+
+ return 0;
+}
+
+static const struct file_operations dst_ca_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = dst_ca_ioctl,
+ .open = dst_ca_open,
+ .release = dst_ca_release,
+ .read = dst_ca_read,
+ .write = dst_ca_write,
+ .llseek = noop_llseek,
+};
+
+static struct dvb_device dvbdev_ca = {
+ .priv = NULL,
+ .users = 1,
+ .readers = 1,
+ .writers = 1,
+ .fops = &dst_ca_fops
+};
+
+struct dvb_device *dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_adapter)
+{
+ struct dvb_device *dvbdev;
+
+ dprintk(verbose, DST_CA_ERROR, 1, "registering DST-CA device");
+ if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA) == 0) {
+ dst->dst_ca = dvbdev;
+ return dst->dst_ca;
+ }
+
+ return NULL;
+}
+
+EXPORT_SYMBOL(dst_ca_attach);
+
+MODULE_DESCRIPTION("DST DVB-S/T/C Combo CA driver");
+MODULE_AUTHOR("Manu Abraham");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/bt8xx/dst_ca.h b/drivers/media/pci/bt8xx/dst_ca.h
new file mode 100644
index 0000000..59cd0dd
--- /dev/null
+++ b/drivers/media/pci/bt8xx/dst_ca.h
@@ -0,0 +1,58 @@
+/*
+ CA-driver for TwinHan DST Frontend/Card
+
+ Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 _DST_CA_H_
+#define _DST_CA_H_
+
+#define RETRIES 5
+
+
+#define CA_APP_INFO_ENQUIRY 0x9f8020
+#define CA_APP_INFO 0x9f8021
+#define CA_ENTER_MENU 0x9f8022
+#define CA_INFO_ENQUIRY 0x9f8030
+#define CA_INFO 0x9f8031
+#define CA_PMT 0x9f8032
+#define CA_PMT_REPLY 0x9f8033
+
+#define CA_CLOSE_MMI 0x9f8800
+#define CA_DISPLAY_CONTROL 0x9f8801
+#define CA_DISPLAY_REPLY 0x9f8802
+#define CA_TEXT_LAST 0x9f8803
+#define CA_TEXT_MORE 0x9f8804
+#define CA_KEYPAD_CONTROL 0x9f8805
+#define CA_KEYPRESS 0x9f8806
+
+#define CA_ENQUIRY 0x9f8807
+#define CA_ANSWER 0x9f8808
+#define CA_MENU_LAST 0x9f8809
+#define CA_MENU_MORE 0x9f880a
+#define CA_MENU_ANSWER 0x9f880b
+#define CA_LIST_LAST 0x9f880c
+#define CA_LIST_MORE 0x9f880d
+
+
+struct dst_ca_private {
+ struct dst_state *dst;
+ struct dvb_device *dvbdev;
+};
+
+
+#endif
diff --git a/drivers/media/pci/bt8xx/dst_common.h b/drivers/media/pci/bt8xx/dst_common.h
new file mode 100644
index 0000000..d70d98f
--- /dev/null
+++ b/drivers/media/pci/bt8xx/dst_common.h
@@ -0,0 +1,182 @@
+/*
+ Frontend-driver for TwinHan DST Frontend
+
+ Copyright (C) 2003 Jamie Honan
+ Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 DST_COMMON_H
+#define DST_COMMON_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include "bt878.h"
+
+#include "dst_ca.h"
+
+
+#define NO_DELAY 0
+#define LONG_DELAY 1
+#define DEVICE_INIT 2
+
+#define DELAY 1
+
+#define DST_TYPE_IS_SAT 0
+#define DST_TYPE_IS_TERR 1
+#define DST_TYPE_IS_CABLE 2
+#define DST_TYPE_IS_ATSC 3
+
+#define DST_TYPE_HAS_TS188 1
+#define DST_TYPE_HAS_TS204 2
+#define DST_TYPE_HAS_SYMDIV 4
+#define DST_TYPE_HAS_FW_1 8
+#define DST_TYPE_HAS_FW_2 16
+#define DST_TYPE_HAS_FW_3 32
+#define DST_TYPE_HAS_FW_BUILD 64
+#define DST_TYPE_HAS_OBS_REGS 128
+#define DST_TYPE_HAS_INC_COUNT 256
+#define DST_TYPE_HAS_MULTI_FE 512
+#define DST_TYPE_HAS_NEWTUNE_2 1024
+#define DST_TYPE_HAS_DBOARD 2048
+#define DST_TYPE_HAS_VLF 4096
+
+/* Card capability list */
+
+#define DST_TYPE_HAS_MAC 1
+#define DST_TYPE_HAS_DISEQC3 2
+#define DST_TYPE_HAS_DISEQC4 4
+#define DST_TYPE_HAS_DISEQC5 8
+#define DST_TYPE_HAS_MOTO 16
+#define DST_TYPE_HAS_CA 32
+#define DST_TYPE_HAS_ANALOG 64 /* Analog inputs */
+#define DST_TYPE_HAS_SESSION 128
+
+#define TUNER_TYPE_MULTI 1
+#define TUNER_TYPE_UNKNOWN 2
+/* DVB-S */
+#define TUNER_TYPE_L64724 4
+#define TUNER_TYPE_STV0299 8
+#define TUNER_TYPE_MB86A15 16
+
+/* DVB-T */
+#define TUNER_TYPE_TDA10046 32
+
+/* ATSC */
+#define TUNER_TYPE_NXT200x 64
+
+
+#define RDC_8820_PIO_0_DISABLE 0
+#define RDC_8820_PIO_0_ENABLE 1
+#define RDC_8820_INT 2
+#define RDC_8820_RESET 4
+
+/* DST Communication */
+#define GET_REPLY 1
+#define NO_REPLY 0
+
+#define GET_ACK 1
+#define FIXED_COMM 8
+
+#define ACK 0xff
+
+struct dst_state {
+
+ struct i2c_adapter* i2c;
+
+ struct bt878* bt;
+
+ /* configuration settings */
+ const struct dst_config* config;
+
+ struct dvb_frontend frontend;
+
+ /* private ASIC data */
+ u8 tx_tuna[10];
+ u8 rx_tuna[10];
+ u8 rxbuffer[10];
+ u8 diseq_flags;
+ u8 dst_type;
+ u32 type_flags;
+ u32 frequency; /* intermediate frequency in kHz for QPSK */
+ fe_spectral_inversion_t inversion;
+ u32 symbol_rate; /* symbol rate in Symbols per second */
+ fe_code_rate_t fec;
+ fe_sec_voltage_t voltage;
+ fe_sec_tone_mode_t tone;
+ u32 decode_freq;
+ u8 decode_lock;
+ u16 decode_strength;
+ u16 decode_snr;
+ unsigned long cur_jiff;
+ u8 k22;
+ u32 bandwidth;
+ u32 dst_hw_cap;
+ u8 dst_fw_version;
+ fe_sec_mini_cmd_t minicmd;
+ fe_modulation_t modulation;
+ u8 messages[256];
+ u8 mac_address[8];
+ u8 fw_version[8];
+ u8 card_info[8];
+ u8 vendor[8];
+ u8 board_info[8];
+ u32 tuner_type;
+ char *tuner_name;
+ struct mutex dst_mutex;
+ u8 fw_name[8];
+ struct dvb_device *dst_ca;
+};
+
+struct tuner_types {
+ u32 tuner_type;
+ char *tuner_name;
+ char *board_name;
+ char *fw_name;
+};
+
+struct dst_types {
+ char *device_id;
+ int offset;
+ u8 dst_type;
+ u32 type_flags;
+ u32 dst_feature;
+ u32 tuner_type;
+};
+
+struct dst_config
+{
+ /* the ASIC i2c address */
+ u8 demod_address;
+};
+
+int rdc_reset_state(struct dst_state *state);
+
+int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode);
+int dst_pio_disable(struct dst_state *state);
+int dst_error_recovery(struct dst_state* state);
+int dst_error_bailout(struct dst_state *state);
+int dst_comm_init(struct dst_state* state);
+
+int write_dst(struct dst_state *state, u8 * data, u8 len);
+int read_dst(struct dst_state *state, u8 * ret, u8 len);
+u8 dst_check_sum(u8 * buf, u32 len);
+struct dst_state* dst_attach(struct dst_state* state, struct dvb_adapter *dvb_adapter);
+struct dvb_device *dst_ca_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter);
+
+
+#endif // DST_COMMON_H
diff --git a/drivers/media/pci/bt8xx/dst_priv.h b/drivers/media/pci/bt8xx/dst_priv.h
new file mode 100644
index 0000000..3974a4c
--- /dev/null
+++ b/drivers/media/pci/bt8xx/dst_priv.h
@@ -0,0 +1,35 @@
+/*
+ * dst-bt878.h: part of the DST driver for the TwinHan DST Frontend
+ *
+ * Copyright (C) 2003 Jamie Honan
+ */
+
+struct dst_gpio_enable {
+ u32 mask;
+ u32 enable;
+};
+
+struct dst_gpio_output {
+ u32 mask;
+ u32 highvals;
+};
+
+struct dst_gpio_read {
+ unsigned long value;
+};
+
+union dst_gpio_packet {
+ struct dst_gpio_enable enb;
+ struct dst_gpio_output outp;
+ struct dst_gpio_read rd;
+ int psize;
+};
+
+#define DST_IG_ENABLE 0
+#define DST_IG_WRITE 1
+#define DST_IG_READ 2
+#define DST_IG_TS 3
+
+struct bt878;
+
+int bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp);
diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.c b/drivers/media/pci/bt8xx/dvb-bt8xx.c
new file mode 100644
index 0000000..81fab9a
--- /dev/null
+++ b/drivers/media/pci/bt8xx/dvb-bt8xx.c
@@ -0,0 +1,975 @@
+/*
+ * Bt8xx based DVB adapter driver
+ *
+ * Copyright (C) 2002,2003 Florian Schirmer <jolt@tuxbox.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 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 pr_fmt(fmt) "dvb_bt8xx: " fmt
+
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb-bt8xx.h"
+#include "bt878.h"
+
+static int debug;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define dprintk( args... ) \
+ do { \
+ if (debug) printk(KERN_DEBUG args); \
+ } while (0)
+
+#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */
+
+static void dvb_bt8xx_task(unsigned long data)
+{
+ struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *)data;
+
+ //printk("%d ", card->bt->finished_block);
+
+ while (card->bt->last_block != card->bt->finished_block) {
+ (card->bt->TS_Size ? dvb_dmx_swfilter_204 : dvb_dmx_swfilter)
+ (&card->demux,
+ &card->bt->buf_cpu[card->bt->last_block *
+ card->bt->block_bytes],
+ card->bt->block_bytes);
+ card->bt->last_block = (card->bt->last_block + 1) %
+ card->bt->block_count;
+ }
+}
+
+static int dvb_bt8xx_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux*dvbdmx = dvbdmxfeed->demux;
+ struct dvb_bt8xx_card *card = dvbdmx->priv;
+ int rc;
+
+ dprintk("dvb_bt8xx: start_feed\n");
+
+ if (!dvbdmx->dmx.frontend)
+ return -EINVAL;
+
+ mutex_lock(&card->lock);
+ card->nfeeds++;
+ rc = card->nfeeds;
+ if (card->nfeeds == 1)
+ bt878_start(card->bt, card->gpio_mode,
+ card->op_sync_orin, card->irq_err_ignore);
+ mutex_unlock(&card->lock);
+ return rc;
+}
+
+static int dvb_bt8xx_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ struct dvb_bt8xx_card *card = dvbdmx->priv;
+
+ dprintk("dvb_bt8xx: stop_feed\n");
+
+ if (!dvbdmx->dmx.frontend)
+ return -EINVAL;
+
+ mutex_lock(&card->lock);
+ card->nfeeds--;
+ if (card->nfeeds == 0)
+ bt878_stop(card->bt);
+ mutex_unlock(&card->lock);
+
+ return 0;
+}
+
+static int is_pci_slot_eq(struct pci_dev* adev, struct pci_dev* bdev)
+{
+ if ((adev->subsystem_vendor == bdev->subsystem_vendor) &&
+ (adev->subsystem_device == bdev->subsystem_device) &&
+ (adev->bus->number == bdev->bus->number) &&
+ (PCI_SLOT(adev->devfn) == PCI_SLOT(bdev->devfn)))
+ return 1;
+ return 0;
+}
+
+static struct bt878 __devinit *dvb_bt8xx_878_match(unsigned int bttv_nr, struct pci_dev* bttv_pci_dev)
+{
+ unsigned int card_nr;
+
+ /* Hmm, n squared. Hope n is small */
+ for (card_nr = 0; card_nr < bt878_num; card_nr++)
+ if (is_pci_slot_eq(bt878[card_nr].dev, bttv_pci_dev))
+ return &bt878[card_nr];
+ return NULL;
+}
+
+static int thomson_dtt7579_demod_init(struct dvb_frontend* fe)
+{
+ static u8 mt352_clock_config [] = { 0x89, 0x38, 0x38 };
+ static u8 mt352_reset [] = { 0x50, 0x80 };
+ static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+ static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0x20 };
+ static u8 mt352_gpp_ctl_cfg [] = { 0x8C, 0x33 };
+ static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
+
+ mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
+ udelay(2000);
+ mt352_write(fe, mt352_reset, sizeof(mt352_reset));
+ mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
+
+ mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
+ mt352_write(fe, mt352_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg));
+ mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
+
+ return 0;
+}
+
+static int thomson_dtt7579_tuner_calc_regs(struct dvb_frontend *fe, u8* pllbuf, int buf_len)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 div;
+ unsigned char bs = 0;
+ unsigned char cp = 0;
+
+ if (buf_len < 5)
+ return -EINVAL;
+
+ div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
+
+ if (c->frequency < 542000000)
+ cp = 0xb4;
+ else if (c->frequency < 771000000)
+ cp = 0xbc;
+ else
+ cp = 0xf4;
+
+ if (c->frequency == 0)
+ bs = 0x03;
+ else if (c->frequency < 443250000)
+ bs = 0x02;
+ else
+ bs = 0x08;
+
+ pllbuf[0] = 0x60;
+ pllbuf[1] = div >> 8;
+ pllbuf[2] = div & 0xff;
+ pllbuf[3] = cp;
+ pllbuf[4] = bs;
+
+ return 5;
+}
+
+static struct mt352_config thomson_dtt7579_config = {
+ .demod_address = 0x0f,
+ .demod_init = thomson_dtt7579_demod_init,
+};
+
+static struct zl10353_config thomson_dtt7579_zl10353_config = {
+ .demod_address = 0x0f,
+};
+
+static int cx24108_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 freq = c->frequency;
+ int i, a, n, pump;
+ u32 band, pll;
+ u32 osci[]={950000,1019000,1075000,1178000,1296000,1432000,
+ 1576000,1718000,1856000,2036000,2150000};
+ u32 bandsel[]={0,0x00020000,0x00040000,0x00100800,0x00101000,
+ 0x00102000,0x00104000,0x00108000,0x00110000,
+ 0x00120000,0x00140000};
+
+ #define XTAL 1011100 /* Hz, really 1.0111 MHz and a /10 prescaler */
+ dprintk("cx24108 debug: entering SetTunerFreq, freq=%d\n", freq);
+
+ /* This is really the bit driving the tuner chip cx24108 */
+
+ if (freq<950000)
+ freq = 950000; /* kHz */
+ else if (freq>2150000)
+ freq = 2150000; /* satellite IF is 950..2150MHz */
+
+ /* decide which VCO to use for the input frequency */
+ for(i = 1; (i < ARRAY_SIZE(osci) - 1) && (osci[i] < freq); i++);
+ dprintk("cx24108 debug: select vco #%d (f=%d)\n", i, freq);
+ band=bandsel[i];
+ /* the gain values must be set by SetSymbolrate */
+ /* compute the pll divider needed, from Conexant data sheet,
+ resolved for (n*32+a), remember f(vco) is f(receive) *2 or *4,
+ depending on the divider bit. It is set to /4 on the 2 lowest
+ bands */
+ n=((i<=2?2:1)*freq*10L)/(XTAL/100);
+ a=n%32; n/=32; if(a==0) n--;
+ pump=(freq<(osci[i-1]+osci[i])/2);
+ pll=0xf8000000|
+ ((pump?1:2)<<(14+11))|
+ ((n&0x1ff)<<(5+11))|
+ ((a&0x1f)<<11);
+ /* everything is shifted left 11 bits to left-align the bits in the
+ 32bit word. Output to the tuner goes MSB-aligned, after all */
+ dprintk("cx24108 debug: pump=%d, n=%d, a=%d\n", pump, n, a);
+ cx24110_pll_write(fe,band);
+ /* set vga and vca to their widest-band settings, as a precaution.
+ SetSymbolrate might not be called to set this up */
+ cx24110_pll_write(fe,0x500c0000);
+ cx24110_pll_write(fe,0x83f1f800);
+ cx24110_pll_write(fe,pll);
+ //writereg(client,0x56,0x7f);
+
+ return 0;
+}
+
+static int pinnsat_tuner_init(struct dvb_frontend* fe)
+{
+ struct dvb_bt8xx_card *card = fe->dvb->priv;
+
+ bttv_gpio_enable(card->bttv_nr, 1, 1); /* output */
+ bttv_write_gpio(card->bttv_nr, 1, 1); /* relay on */
+
+ return 0;
+}
+
+static int pinnsat_tuner_sleep(struct dvb_frontend* fe)
+{
+ struct dvb_bt8xx_card *card = fe->dvb->priv;
+
+ bttv_write_gpio(card->bttv_nr, 1, 0); /* relay off */
+
+ return 0;
+}
+
+static struct cx24110_config pctvsat_config = {
+ .demod_address = 0x55,
+};
+
+static int microtune_mt7202dtf_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv;
+ u8 cfg, cpump, band_select;
+ u8 data[4];
+ u32 div;
+ struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) };
+
+ div = (36000000 + c->frequency + 83333) / 166666;
+ cfg = 0x88;
+
+ if (c->frequency < 175000000)
+ cpump = 2;
+ else if (c->frequency < 390000000)
+ cpump = 1;
+ else if (c->frequency < 470000000)
+ cpump = 2;
+ else if (c->frequency < 750000000)
+ cpump = 2;
+ else
+ cpump = 3;
+
+ if (c->frequency < 175000000)
+ band_select = 0x0e;
+ else if (c->frequency < 470000000)
+ band_select = 0x05;
+ else
+ band_select = 0x03;
+
+ data[0] = (div >> 8) & 0x7f;
+ data[1] = div & 0xff;
+ data[2] = ((div >> 10) & 0x60) | cfg;
+ data[3] = (cpump << 6) | band_select;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(card->i2c_adapter, &msg, 1);
+ return (div * 166666 - 36000000);
+}
+
+static int microtune_mt7202dtf_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
+{
+ struct dvb_bt8xx_card* bt = (struct dvb_bt8xx_card*) fe->dvb->priv;
+
+ return request_firmware(fw, name, &bt->bt->dev->dev);
+}
+
+static struct sp887x_config microtune_mt7202dtf_config = {
+ .demod_address = 0x70,
+ .request_firmware = microtune_mt7202dtf_request_firmware,
+};
+
+static int advbt771_samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
+{
+ static u8 mt352_clock_config [] = { 0x89, 0x38, 0x2d };
+ static u8 mt352_reset [] = { 0x50, 0x80 };
+ static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+ static u8 mt352_agc_cfg [] = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF,
+ 0x00, 0xFF, 0x00, 0x40, 0x40 };
+ static u8 mt352_av771_extra[] = { 0xB5, 0x7A };
+ static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
+
+ mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
+ udelay(2000);
+ mt352_write(fe, mt352_reset, sizeof(mt352_reset));
+ mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
+
+ mt352_write(fe, mt352_agc_cfg,sizeof(mt352_agc_cfg));
+ udelay(2000);
+ mt352_write(fe, mt352_av771_extra,sizeof(mt352_av771_extra));
+ mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
+
+ return 0;
+}
+
+static int advbt771_samsung_tdtc9251dh0_tuner_calc_regs(struct dvb_frontend *fe, u8 *pllbuf, int buf_len)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 div;
+ unsigned char bs = 0;
+ unsigned char cp = 0;
+
+ if (buf_len < 5) return -EINVAL;
+
+ div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
+
+ if (c->frequency < 150000000)
+ cp = 0xB4;
+ else if (c->frequency < 173000000)
+ cp = 0xBC;
+ else if (c->frequency < 250000000)
+ cp = 0xB4;
+ else if (c->frequency < 400000000)
+ cp = 0xBC;
+ else if (c->frequency < 420000000)
+ cp = 0xF4;
+ else if (c->frequency < 470000000)
+ cp = 0xFC;
+ else if (c->frequency < 600000000)
+ cp = 0xBC;
+ else if (c->frequency < 730000000)
+ cp = 0xF4;
+ else
+ cp = 0xFC;
+
+ if (c->frequency < 150000000)
+ bs = 0x01;
+ else if (c->frequency < 173000000)
+ bs = 0x01;
+ else if (c->frequency < 250000000)
+ bs = 0x02;
+ else if (c->frequency < 400000000)
+ bs = 0x02;
+ else if (c->frequency < 420000000)
+ bs = 0x02;
+ else if (c->frequency < 470000000)
+ bs = 0x02;
+ else if (c->frequency < 600000000)
+ bs = 0x08;
+ else if (c->frequency < 730000000)
+ bs = 0x08;
+ else
+ bs = 0x08;
+
+ pllbuf[0] = 0x61;
+ pllbuf[1] = div >> 8;
+ pllbuf[2] = div & 0xff;
+ pllbuf[3] = cp;
+ pllbuf[4] = bs;
+
+ return 5;
+}
+
+static struct mt352_config advbt771_samsung_tdtc9251dh0_config = {
+ .demod_address = 0x0f,
+ .demod_init = advbt771_samsung_tdtc9251dh0_demod_init,
+};
+
+static struct dst_config dst_config = {
+ .demod_address = 0x55,
+};
+
+static int or51211_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
+{
+ struct dvb_bt8xx_card* bt = (struct dvb_bt8xx_card*) fe->dvb->priv;
+
+ return request_firmware(fw, name, &bt->bt->dev->dev);
+}
+
+static void or51211_setmode(struct dvb_frontend * fe, int mode)
+{
+ struct dvb_bt8xx_card *bt = fe->dvb->priv;
+ bttv_write_gpio(bt->bttv_nr, 0x0002, mode); /* Reset */
+ msleep(20);
+}
+
+static void or51211_reset(struct dvb_frontend * fe)
+{
+ struct dvb_bt8xx_card *bt = fe->dvb->priv;
+
+ /* RESET DEVICE
+ * reset is controlled by GPIO-0
+ * when set to 0 causes reset and when to 1 for normal op
+ * must remain reset for 128 clock cycles on a 50Mhz clock
+ * also PRM1 PRM2 & PRM4 are controlled by GPIO-1,GPIO-2 & GPIO-4
+ * We assume that the reset has be held low long enough or we
+ * have been reset by a power on. When the driver is unloaded
+ * reset set to 0 so if reloaded we have been reset.
+ */
+ /* reset & PRM1,2&4 are outputs */
+ int ret = bttv_gpio_enable(bt->bttv_nr, 0x001F, 0x001F);
+ if (ret != 0)
+ printk(KERN_WARNING "or51211: Init Error - Can't Reset DVR (%i)\n", ret);
+ bttv_write_gpio(bt->bttv_nr, 0x001F, 0x0000); /* Reset */
+ msleep(20);
+ /* Now set for normal operation */
+ bttv_write_gpio(bt->bttv_nr, 0x0001F, 0x0001);
+ /* wait for operation to begin */
+ msleep(500);
+}
+
+static void or51211_sleep(struct dvb_frontend * fe)
+{
+ struct dvb_bt8xx_card *bt = fe->dvb->priv;
+ bttv_write_gpio(bt->bttv_nr, 0x0001, 0x0000);
+}
+
+static struct or51211_config or51211_config = {
+ .demod_address = 0x15,
+ .request_firmware = or51211_request_firmware,
+ .setmode = or51211_setmode,
+ .reset = or51211_reset,
+ .sleep = or51211_sleep,
+};
+
+static int vp3021_alps_tded4_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv;
+ u8 buf[4];
+ u32 div;
+ struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf) };
+
+ div = (c->frequency + 36166667) / 166667;
+
+ buf[0] = (div >> 8) & 0x7F;
+ buf[1] = div & 0xFF;
+ buf[2] = 0x85;
+ if ((c->frequency >= 47000000) && (c->frequency < 153000000))
+ buf[3] = 0x01;
+ else if ((c->frequency >= 153000000) && (c->frequency < 430000000))
+ buf[3] = 0x02;
+ else if ((c->frequency >= 430000000) && (c->frequency < 824000000))
+ buf[3] = 0x0C;
+ else if ((c->frequency >= 824000000) && (c->frequency < 863000000))
+ buf[3] = 0x8C;
+ else
+ return -EINVAL;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(card->i2c_adapter, &msg, 1);
+ return 0;
+}
+
+static struct nxt6000_config vp3021_alps_tded4_config = {
+ .demod_address = 0x0a,
+ .clock_inversion = 1,
+};
+
+static int digitv_alps_tded4_demod_init(struct dvb_frontend* fe)
+{
+ static u8 mt352_clock_config [] = { 0x89, 0x38, 0x2d };
+ static u8 mt352_reset [] = { 0x50, 0x80 };
+ static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+ static u8 mt352_agc_cfg [] = { 0x67, 0x20, 0xa0 };
+ static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
+
+ mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
+ udelay(2000);
+ mt352_write(fe, mt352_reset, sizeof(mt352_reset));
+ mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
+ mt352_write(fe, mt352_agc_cfg,sizeof(mt352_agc_cfg));
+ mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
+
+ return 0;
+}
+
+static int digitv_alps_tded4_tuner_calc_regs(struct dvb_frontend *fe, u8 *pllbuf, int buf_len)
+{
+ u32 div;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ if (buf_len < 5)
+ return -EINVAL;
+
+ div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
+
+ pllbuf[0] = 0x61;
+ pllbuf[1] = (div >> 8) & 0x7F;
+ pllbuf[2] = div & 0xFF;
+ pllbuf[3] = 0x85;
+
+ dprintk("frequency %u, div %u\n", c->frequency, div);
+
+ if (c->frequency < 470000000)
+ pllbuf[4] = 0x02;
+ else if (c->frequency > 823000000)
+ pllbuf[4] = 0x88;
+ else
+ pllbuf[4] = 0x08;
+
+ if (c->bandwidth_hz == 8000000)
+ pllbuf[4] |= 0x04;
+
+ return 5;
+}
+
+static void digitv_alps_tded4_reset(struct dvb_bt8xx_card *bt)
+{
+ /*
+ * Reset the frontend, must be called before trying
+ * to initialise the MT352 or mt352_attach
+ * will fail. Same goes for the nxt6000 frontend.
+ *
+ */
+
+ int ret = bttv_gpio_enable(bt->bttv_nr, 0x08, 0x08);
+ if (ret != 0)
+ printk(KERN_WARNING "digitv_alps_tded4: Init Error - Can't Reset DVR (%i)\n", ret);
+
+ /* Pulse the reset line */
+ bttv_write_gpio(bt->bttv_nr, 0x08, 0x08); /* High */
+ bttv_write_gpio(bt->bttv_nr, 0x08, 0x00); /* Low */
+ msleep(100);
+
+ bttv_write_gpio(bt->bttv_nr, 0x08, 0x08); /* High */
+}
+
+static struct mt352_config digitv_alps_tded4_config = {
+ .demod_address = 0x0a,
+ .demod_init = digitv_alps_tded4_demod_init,
+};
+
+static struct lgdt330x_config tdvs_tua6034_config = {
+ .demod_address = 0x0e,
+ .demod_chip = LGDT3303,
+ .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
+};
+
+static void lgdt330x_reset(struct dvb_bt8xx_card *bt)
+{
+ /* Set pin 27 of the lgdt3303 chip high to reset the frontend */
+
+ /* Pulse the reset line */
+ bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000001); /* High */
+ bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000000); /* Low */
+ msleep(100);
+
+ bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000001); /* High */
+ msleep(100);
+}
+
+static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
+{
+ struct dst_state* state = NULL;
+
+ switch(type) {
+ case BTTV_BOARD_DVICO_DVBT_LITE:
+ card->fe = dvb_attach(mt352_attach, &thomson_dtt7579_config, card->i2c_adapter);
+
+ if (card->fe == NULL)
+ card->fe = dvb_attach(zl10353_attach, &thomson_dtt7579_zl10353_config,
+ card->i2c_adapter);
+
+ if (card->fe != NULL) {
+ card->fe->ops.tuner_ops.calc_regs = thomson_dtt7579_tuner_calc_regs;
+ card->fe->ops.info.frequency_min = 174000000;
+ card->fe->ops.info.frequency_max = 862000000;
+ }
+ break;
+
+ case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE:
+ lgdt330x_reset(card);
+ card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter);
+ if (card->fe != NULL) {
+ dvb_attach(simple_tuner_attach, card->fe,
+ card->i2c_adapter, 0x61,
+ TUNER_LG_TDVS_H06XF);
+ dprintk ("dvb_bt8xx: lgdt330x detected\n");
+ }
+ break;
+
+ case BTTV_BOARD_NEBULA_DIGITV:
+ /*
+ * It is possible to determine the correct frontend using the I2C bus (see the Nebula SDK);
+ * this would be a cleaner solution than trying each frontend in turn.
+ */
+
+ /* Old Nebula (marked (c)2003 on high profile pci card) has nxt6000 demod */
+ digitv_alps_tded4_reset(card);
+ card->fe = dvb_attach(nxt6000_attach, &vp3021_alps_tded4_config, card->i2c_adapter);
+ if (card->fe != NULL) {
+ card->fe->ops.tuner_ops.set_params = vp3021_alps_tded4_tuner_set_params;
+ dprintk ("dvb_bt8xx: an nxt6000 was detected on your digitv card\n");
+ break;
+ }
+
+ /* New Nebula (marked (c)2005 on low profile pci card) has mt352 demod */
+ digitv_alps_tded4_reset(card);
+ card->fe = dvb_attach(mt352_attach, &digitv_alps_tded4_config, card->i2c_adapter);
+
+ if (card->fe != NULL) {
+ card->fe->ops.tuner_ops.calc_regs = digitv_alps_tded4_tuner_calc_regs;
+ dprintk ("dvb_bt8xx: an mt352 was detected on your digitv card\n");
+ }
+ break;
+
+ case BTTV_BOARD_AVDVBT_761:
+ card->fe = dvb_attach(sp887x_attach, &microtune_mt7202dtf_config, card->i2c_adapter);
+ if (card->fe) {
+ card->fe->ops.tuner_ops.set_params = microtune_mt7202dtf_tuner_set_params;
+ }
+ break;
+
+ case BTTV_BOARD_AVDVBT_771:
+ card->fe = dvb_attach(mt352_attach, &advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter);
+ if (card->fe != NULL) {
+ card->fe->ops.tuner_ops.calc_regs = advbt771_samsung_tdtc9251dh0_tuner_calc_regs;
+ card->fe->ops.info.frequency_min = 174000000;
+ card->fe->ops.info.frequency_max = 862000000;
+ }
+ break;
+
+ case BTTV_BOARD_TWINHAN_DST:
+ /* DST is not a frontend driver !!! */
+ state = kmalloc(sizeof (struct dst_state), GFP_KERNEL);
+ if (!state) {
+ pr_err("No memory\n");
+ break;
+ }
+ /* Setup the Card */
+ state->config = &dst_config;
+ state->i2c = card->i2c_adapter;
+ state->bt = card->bt;
+ state->dst_ca = NULL;
+ /* DST is not a frontend, attaching the ASIC */
+ if (dvb_attach(dst_attach, state, &card->dvb_adapter) == NULL) {
+ pr_err("%s: Could not find a Twinhan DST\n", __func__);
+ break;
+ }
+ /* Attach other DST peripherals if any */
+ /* Conditional Access device */
+ card->fe = &state->frontend;
+ if (state->dst_hw_cap & DST_TYPE_HAS_CA)
+ dvb_attach(dst_ca_attach, state, &card->dvb_adapter);
+ break;
+
+ case BTTV_BOARD_PINNACLESAT:
+ card->fe = dvb_attach(cx24110_attach, &pctvsat_config, card->i2c_adapter);
+ if (card->fe) {
+ card->fe->ops.tuner_ops.init = pinnsat_tuner_init;
+ card->fe->ops.tuner_ops.sleep = pinnsat_tuner_sleep;
+ card->fe->ops.tuner_ops.set_params = cx24108_tuner_set_params;
+ }
+ break;
+
+ case BTTV_BOARD_PC_HDTV:
+ card->fe = dvb_attach(or51211_attach, &or51211_config, card->i2c_adapter);
+ if (card->fe != NULL)
+ dvb_attach(simple_tuner_attach, card->fe,
+ card->i2c_adapter, 0x61,
+ TUNER_PHILIPS_FCV1236D);
+ break;
+ }
+
+ if (card->fe == NULL)
+ pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
+ card->bt->dev->vendor,
+ card->bt->dev->device,
+ card->bt->dev->subsystem_vendor,
+ card->bt->dev->subsystem_device);
+ else
+ if (dvb_register_frontend(&card->dvb_adapter, card->fe)) {
+ pr_err("Frontend registration failed!\n");
+ dvb_frontend_detach(card->fe);
+ card->fe = NULL;
+ }
+}
+
+static int __devinit dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type)
+{
+ int result;
+
+ result = dvb_register_adapter(&card->dvb_adapter, card->card_name,
+ THIS_MODULE, &card->bt->dev->dev,
+ adapter_nr);
+ if (result < 0) {
+ pr_err("dvb_register_adapter failed (errno = %d)\n", result);
+ return result;
+ }
+ card->dvb_adapter.priv = card;
+
+ card->bt->adapter = card->i2c_adapter;
+
+ memset(&card->demux, 0, sizeof(struct dvb_demux));
+
+ card->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING;
+
+ card->demux.priv = card;
+ card->demux.filternum = 256;
+ card->demux.feednum = 256;
+ card->demux.start_feed = dvb_bt8xx_start_feed;
+ card->demux.stop_feed = dvb_bt8xx_stop_feed;
+ card->demux.write_to_decoder = NULL;
+
+ result = dvb_dmx_init(&card->demux);
+ if (result < 0) {
+ pr_err("dvb_dmx_init failed (errno = %d)\n", result);
+ goto err_unregister_adaptor;
+ }
+
+ card->dmxdev.filternum = 256;
+ card->dmxdev.demux = &card->demux.dmx;
+ card->dmxdev.capabilities = 0;
+
+ result = dvb_dmxdev_init(&card->dmxdev, &card->dvb_adapter);
+ if (result < 0) {
+ pr_err("dvb_dmxdev_init failed (errno = %d)\n", result);
+ goto err_dmx_release;
+ }
+
+ card->fe_hw.source = DMX_FRONTEND_0;
+
+ result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_hw);
+ if (result < 0) {
+ pr_err("dvb_dmx_init failed (errno = %d)\n", result);
+ goto err_dmxdev_release;
+ }
+
+ card->fe_mem.source = DMX_MEMORY_FE;
+
+ result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_mem);
+ if (result < 0) {
+ pr_err("dvb_dmx_init failed (errno = %d)\n", result);
+ goto err_remove_hw_frontend;
+ }
+
+ result = card->demux.dmx.connect_frontend(&card->demux.dmx, &card->fe_hw);
+ if (result < 0) {
+ pr_err("dvb_dmx_init failed (errno = %d)\n", result);
+ goto err_remove_mem_frontend;
+ }
+
+ result = dvb_net_init(&card->dvb_adapter, &card->dvbnet, &card->demux.dmx);
+ if (result < 0) {
+ pr_err("dvb_net_init failed (errno = %d)\n", result);
+ goto err_disconnect_frontend;
+ }
+
+ tasklet_init(&card->bt->tasklet, dvb_bt8xx_task, (unsigned long) card);
+
+ frontend_init(card, type);
+
+ return 0;
+
+err_disconnect_frontend:
+ card->demux.dmx.disconnect_frontend(&card->demux.dmx);
+err_remove_mem_frontend:
+ card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem);
+err_remove_hw_frontend:
+ card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw);
+err_dmxdev_release:
+ dvb_dmxdev_release(&card->dmxdev);
+err_dmx_release:
+ dvb_dmx_release(&card->demux);
+err_unregister_adaptor:
+ dvb_unregister_adapter(&card->dvb_adapter);
+ return result;
+}
+
+static int __devinit dvb_bt8xx_probe(struct bttv_sub_device *sub)
+{
+ struct dvb_bt8xx_card *card;
+ struct pci_dev* bttv_pci_dev;
+ int ret;
+
+ if (!(card = kzalloc(sizeof(struct dvb_bt8xx_card), GFP_KERNEL)))
+ return -ENOMEM;
+
+ mutex_init(&card->lock);
+ card->bttv_nr = sub->core->nr;
+ strlcpy(card->card_name, sub->core->v4l2_dev.name, sizeof(card->card_name));
+ card->i2c_adapter = &sub->core->i2c_adap;
+
+ switch(sub->core->type) {
+ case BTTV_BOARD_PINNACLESAT:
+ card->gpio_mode = 0x0400c060;
+ /* should be: BT878_A_GAIN=0,BT878_A_PWRDN,BT878_DA_DPM,BT878_DA_SBR,
+ BT878_DA_IOM=1,BT878_DA_APP to enable serial highspeed mode. */
+ card->op_sync_orin = BT878_RISC_SYNC_MASK;
+ card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
+ break;
+
+ case BTTV_BOARD_DVICO_DVBT_LITE:
+ card->gpio_mode = 0x0400C060;
+ card->op_sync_orin = BT878_RISC_SYNC_MASK;
+ card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
+ /* 26, 15, 14, 6, 5
+ * A_PWRDN DA_DPM DA_SBR DA_IOM_DA
+ * DA_APP(parallel) */
+ break;
+
+ case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE:
+ card->gpio_mode = 0x0400c060;
+ card->op_sync_orin = BT878_RISC_SYNC_MASK;
+ card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
+ break;
+
+ case BTTV_BOARD_NEBULA_DIGITV:
+ case BTTV_BOARD_AVDVBT_761:
+ card->gpio_mode = (1 << 26) | (1 << 14) | (1 << 5);
+ card->op_sync_orin = BT878_RISC_SYNC_MASK;
+ card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
+ /* A_PWRDN DA_SBR DA_APP (high speed serial) */
+ break;
+
+ case BTTV_BOARD_AVDVBT_771: //case 0x07711461:
+ card->gpio_mode = 0x0400402B;
+ card->op_sync_orin = BT878_RISC_SYNC_MASK;
+ card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
+ /* A_PWRDN DA_SBR DA_APP[0] PKTP=10 RISC_ENABLE FIFO_ENABLE*/
+ break;
+
+ case BTTV_BOARD_TWINHAN_DST:
+ card->gpio_mode = 0x2204f2c;
+ card->op_sync_orin = BT878_RISC_SYNC_MASK;
+ card->irq_err_ignore = BT878_APABORT | BT878_ARIPERR |
+ BT878_APPERR | BT878_AFBUS;
+ /* 25,21,14,11,10,9,8,3,2 then
+ * 0x33 = 5,4,1,0
+ * A_SEL=SML, DA_MLB, DA_SBR,
+ * DA_SDR=f, fifo trigger = 32 DWORDS
+ * IOM = 0 == audio A/D
+ * DPM = 0 == digital audio mode
+ * == async data parallel port
+ * then 0x33 (13 is set by start_capture)
+ * DA_APP = async data parallel port,
+ * ACAP_EN = 1,
+ * RISC+FIFO ENABLE */
+ break;
+
+ case BTTV_BOARD_PC_HDTV:
+ card->gpio_mode = 0x0100EC7B;
+ card->op_sync_orin = BT878_RISC_SYNC_MASK;
+ card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR;
+ break;
+
+ default:
+ pr_err("Unknown bttv card type: %d\n", sub->core->type);
+ kfree(card);
+ return -ENODEV;
+ }
+
+ dprintk("dvb_bt8xx: identified card%d as %s\n", card->bttv_nr, card->card_name);
+
+ if (!(bttv_pci_dev = bttv_get_pcidev(card->bttv_nr))) {
+ pr_err("no pci device for card %d\n", card->bttv_nr);
+ kfree(card);
+ return -ENODEV;
+ }
+
+ if (!(card->bt = dvb_bt8xx_878_match(card->bttv_nr, bttv_pci_dev))) {
+ pr_err("unable to determine DMA core of card %d,\n", card->bttv_nr);
+ pr_err("if you have the ALSA bt87x audio driver installed, try removing it.\n");
+
+ kfree(card);
+ return -ENODEV;
+ }
+
+ mutex_init(&card->bt->gpio_lock);
+ card->bt->bttv_nr = sub->core->nr;
+
+ if ( (ret = dvb_bt8xx_load_card(card, sub->core->type)) ) {
+ kfree(card);
+ return ret;
+ }
+
+ dev_set_drvdata(&sub->dev, card);
+ return 0;
+}
+
+static void dvb_bt8xx_remove(struct bttv_sub_device *sub)
+{
+ struct dvb_bt8xx_card *card = dev_get_drvdata(&sub->dev);
+
+ dprintk("dvb_bt8xx: unloading card%d\n", card->bttv_nr);
+
+ bt878_stop(card->bt);
+ tasklet_kill(&card->bt->tasklet);
+ dvb_net_release(&card->dvbnet);
+ card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem);
+ card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw);
+ dvb_dmxdev_release(&card->dmxdev);
+ dvb_dmx_release(&card->demux);
+ if (card->fe) {
+ dvb_unregister_frontend(card->fe);
+ dvb_frontend_detach(card->fe);
+ }
+ dvb_unregister_adapter(&card->dvb_adapter);
+
+ kfree(card);
+}
+
+static struct bttv_sub_driver driver = {
+ .drv = {
+ .name = "dvb-bt8xx",
+ },
+ .probe = dvb_bt8xx_probe,
+ .remove = dvb_bt8xx_remove,
+ /* FIXME:
+ * .shutdown = dvb_bt8xx_shutdown,
+ * .suspend = dvb_bt8xx_suspend,
+ * .resume = dvb_bt8xx_resume,
+ */
+};
+
+static int __init dvb_bt8xx_init(void)
+{
+ return bttv_sub_register(&driver, "dvb");
+}
+
+static void __exit dvb_bt8xx_exit(void)
+{
+ bttv_sub_unregister(&driver);
+}
+
+module_init(dvb_bt8xx_init);
+module_exit(dvb_bt8xx_exit);
+
+MODULE_DESCRIPTION("Bt8xx based DVB adapter driver");
+MODULE_AUTHOR("Florian Schirmer <jolt@tuxbox.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.h b/drivers/media/pci/bt8xx/dvb-bt8xx.h
new file mode 100644
index 0000000..4499ed2
--- /dev/null
+++ b/drivers/media/pci/bt8xx/dvb-bt8xx.h
@@ -0,0 +1,63 @@
+/*
+ * Bt8xx based DVB adapter driver
+ *
+ * Copyright (C) 2002,2003 Florian Schirmer <jolt@tuxbox.org>
+ * Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de>
+ * Copyright (C) 1999-2001 Ralph Metzler & Marcus Metzler for convergence integrated media GmbH
+ * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.de>
+ *
+ * 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 DVB_BT8XX_H
+#define DVB_BT8XX_H
+
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include "dvbdev.h"
+#include "dvb_net.h"
+#include "bttv.h"
+#include "mt352.h"
+#include "sp887x.h"
+#include "dst_common.h"
+#include "nxt6000.h"
+#include "cx24110.h"
+#include "or51211.h"
+#include "lgdt330x.h"
+#include "zl10353.h"
+#include "tuner-simple.h"
+
+struct dvb_bt8xx_card {
+ struct mutex lock;
+ int nfeeds;
+ char card_name[32];
+ struct dvb_adapter dvb_adapter;
+ struct bt878 *bt;
+ unsigned int bttv_nr;
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct dmx_frontend fe_hw;
+ struct dmx_frontend fe_mem;
+ u32 gpio_mode;
+ u32 op_sync_orin;
+ u32 irq_err_ignore;
+ struct i2c_adapter *i2c_adapter;
+ struct dvb_net dvbnet;
+
+ struct dvb_frontend* fe;
+};
+
+#endif /* DVB_BT8XX_H */
OpenPOWER on IntegriCloud