summaryrefslogtreecommitdiffstats
path: root/drivers/media/video
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2008-04-29 14:53:40 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2008-04-29 14:53:40 -0700
commit2d5e3e8d28a7820de1eb7b18a7c15d645bb26992 (patch)
treee6af4574977947de83e670dad1f71505b249fe1c /drivers/media/video
parentb57ab7632b8fc1eef139bbbb7a89002be61f99e1 (diff)
parentaed6abd662c2903733bea7fcd3856c306e650680 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/v4l-dvb
* git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/v4l-dvb: (28 commits) V4L-DVB(7789a): cx18: fix symbol conflict with ivtv driver V4L/DVB (7789): tuner: remove static dependencies on analog tuner sub-modules V4L/DVB (7785): [2.6 patch] make mt9{m001,v022}_controls[] static V4L/DVB (7786): cx18: new driver for the Conexant CX23418 MPEG encoder chip V4L/DVB (7783): drivers/media/dvb/frontends/s5h1420.c: printk fix V4L/DVB (7782): pvrusb2: Driver is no longer experimental V4L/DVB (7781): pvrusb2-dvb: include dvb support by default and update Kconfig help text V4L/DVB (7780): pvrusb2: always enable support for OnAir Creator / HDTV USB2 V4L/DVB (7779): pvrusb2-dvb: quiet down noise in kernel log for feed debug Rename common tuner Kconfig names to use the same Fix V4L/DVB core help messages V4L/DVB (7769): Move other terrestrial tuners to common/tuners V4L/DVB (7768): reorganize some DVB-S Kconfig items V4L/DVB(7767): Move tuners to common/tuners V4L/DVB (7766): saa7134: add another PCI ID for Beholder M6 V4L/DVB (7765): Add support for Beholder BeholdTV H6 V4L/DVB (7763): ivtv: add tuner support for the AverMedia M116 V4L/DVB (7762): ivtv: fix tuner detection for PAL-N/Nc V4L/DVB (7761): ivtv: increase the DMA timeout from 100 to 300 ms V4L/DVB (7759): ivtv: increase version number to 1.2.1 ...
Diffstat (limited to 'drivers/media/video')
-rw-r--r--drivers/media/video/Kconfig50
-rw-r--r--drivers/media/video/Makefile14
-rw-r--r--drivers/media/video/au0828/Kconfig2
-rw-r--r--drivers/media/video/au0828/Makefile2
-rw-r--r--drivers/media/video/bt8xx/Kconfig2
-rw-r--r--drivers/media/video/bt8xx/Makefile1
-rw-r--r--drivers/media/video/cx18/Kconfig20
-rw-r--r--drivers/media/video/cx18/Makefile11
-rw-r--r--drivers/media/video/cx18/cx18-audio.c73
-rw-r--r--drivers/media/video/cx18/cx18-audio.h26
-rw-r--r--drivers/media/video/cx18/cx18-av-audio.c361
-rw-r--r--drivers/media/video/cx18/cx18-av-core.c879
-rw-r--r--drivers/media/video/cx18/cx18-av-core.h318
-rw-r--r--drivers/media/video/cx18/cx18-av-firmware.c120
-rw-r--r--drivers/media/video/cx18/cx18-av-vbi.c413
-rw-r--r--drivers/media/video/cx18/cx18-cards.c277
-rw-r--r--drivers/media/video/cx18/cx18-cards.h170
-rw-r--r--drivers/media/video/cx18/cx18-controls.c306
-rw-r--r--drivers/media/video/cx18/cx18-controls.h24
-rw-r--r--drivers/media/video/cx18/cx18-driver.c971
-rw-r--r--drivers/media/video/cx18/cx18-driver.h500
-rw-r--r--drivers/media/video/cx18/cx18-dvb.c288
-rw-r--r--drivers/media/video/cx18/cx18-dvb.h25
-rw-r--r--drivers/media/video/cx18/cx18-fileops.c711
-rw-r--r--drivers/media/video/cx18/cx18-fileops.h45
-rw-r--r--drivers/media/video/cx18/cx18-firmware.c373
-rw-r--r--drivers/media/video/cx18/cx18-firmware.h25
-rw-r--r--drivers/media/video/cx18/cx18-gpio.c74
-rw-r--r--drivers/media/video/cx18/cx18-gpio.h24
-rw-r--r--drivers/media/video/cx18/cx18-i2c.c431
-rw-r--r--drivers/media/video/cx18/cx18-i2c.h33
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c851
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.h30
-rw-r--r--drivers/media/video/cx18/cx18-irq.c179
-rw-r--r--drivers/media/video/cx18/cx18-irq.h37
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c372
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.h73
-rw-r--r--drivers/media/video/cx18/cx18-queue.c282
-rw-r--r--drivers/media/video/cx18/cx18-queue.h59
-rw-r--r--drivers/media/video/cx18/cx18-scb.c121
-rw-r--r--drivers/media/video/cx18/cx18-scb.h285
-rw-r--r--drivers/media/video/cx18/cx18-streams.c566
-rw-r--r--drivers/media/video/cx18/cx18-streams.h33
-rw-r--r--drivers/media/video/cx18/cx18-vbi.c208
-rw-r--r--drivers/media/video/cx18/cx18-vbi.h26
-rw-r--r--drivers/media/video/cx18/cx18-version.h34
-rw-r--r--drivers/media/video/cx18/cx18-video.c45
-rw-r--r--drivers/media/video/cx18/cx18-video.h22
-rw-r--r--drivers/media/video/cx18/cx23418.h458
-rw-r--r--drivers/media/video/cx23885/Kconfig12
-rw-r--r--drivers/media/video/cx23885/Makefile1
-rw-r--r--drivers/media/video/cx88/Kconfig4
-rw-r--r--drivers/media/video/cx88/Makefile1
-rw-r--r--drivers/media/video/cx88/cx88-cards.c50
-rw-r--r--drivers/media/video/cx88/cx88-i2c.c30
-rw-r--r--drivers/media/video/em28xx/Kconfig2
-rw-r--r--drivers/media/video/em28xx/Makefile1
-rw-r--r--drivers/media/video/ivtv/Kconfig2
-rw-r--r--drivers/media/video/ivtv/Makefile1
-rw-r--r--drivers/media/video/ivtv/ivtv-cards.c98
-rw-r--r--drivers/media/video/ivtv/ivtv-cards.h5
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c43
-rw-r--r--drivers/media/video/ivtv/ivtv-gpio.c9
-rw-r--r--drivers/media/video/ivtv/ivtv-i2c.c3
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.c44
-rw-r--r--drivers/media/video/ivtv/ivtv-irq.c4
-rw-r--r--drivers/media/video/ivtv/ivtv-version.h2
-rw-r--r--drivers/media/video/ivtv/ivtvfb.c2
-rw-r--r--drivers/media/video/mt20xx.c670
-rw-r--r--drivers/media/video/mt20xx.h37
-rw-r--r--drivers/media/video/mt9m001.c2
-rw-r--r--drivers/media/video/mt9v022.c2
-rw-r--r--drivers/media/video/pvrusb2/Kconfig55
-rw-r--r--drivers/media/video/pvrusb2/Makefile1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debug.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-devattr.c8
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-dvb.c42
-rw-r--r--drivers/media/video/saa7134/Kconfig6
-rw-r--r--drivers/media/video/saa7134/Makefile1
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c260
-rw-r--r--drivers/media/video/saa7134/saa7134-i2c.c42
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c1
-rw-r--r--drivers/media/video/saa7134/saa7134.h1
-rw-r--r--drivers/media/video/tda8290.c804
-rw-r--r--drivers/media/video/tda8290.h57
-rw-r--r--drivers/media/video/tda9887.c717
-rw-r--r--drivers/media/video/tda9887.h38
-rw-r--r--drivers/media/video/tea5761.c324
-rw-r--r--drivers/media/video/tea5761.h47
-rw-r--r--drivers/media/video/tea5767.c474
-rw-r--r--drivers/media/video/tea5767.h66
-rw-r--r--drivers/media/video/tuner-core.c124
-rw-r--r--drivers/media/video/tuner-i2c.h173
-rw-r--r--drivers/media/video/tuner-simple.c1093
-rw-r--r--drivers/media/video/tuner-simple.h39
-rw-r--r--drivers/media/video/tuner-types.c1652
-rw-r--r--drivers/media/video/tuner-xc2028-types.h141
-rw-r--r--drivers/media/video/tuner-xc2028.c1227
-rw-r--r--drivers/media/video/tuner-xc2028.h63
-rw-r--r--drivers/media/video/usbvision/Kconfig2
-rw-r--r--drivers/media/video/usbvision/Makefile1
101 files changed, 10719 insertions, 8016 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index fe9a4cc1414..fe743aa7f64 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -1,4 +1,50 @@
#
+# Generic video config states
+#
+
+config VIDEO_V4L2
+ tristate
+ depends on VIDEO_DEV && VIDEO_V4L2_COMMON
+ default VIDEO_DEV && VIDEO_V4L2_COMMON
+
+config VIDEO_V4L1
+ tristate
+ depends on VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1
+ default VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1
+
+config VIDEOBUF_GEN
+ tristate
+
+config VIDEOBUF_DMA_SG
+ depends on HAS_DMA
+ select VIDEOBUF_GEN
+ tristate
+
+config VIDEOBUF_VMALLOC
+ select VIDEOBUF_GEN
+ tristate
+
+config VIDEOBUF_DVB
+ tristate
+ select VIDEOBUF_GEN
+ select VIDEOBUF_DMA_SG
+
+config VIDEO_BTCX
+ tristate
+
+config VIDEO_IR_I2C
+ tristate
+
+config VIDEO_IR
+ tristate
+ depends on INPUT
+ select VIDEO_IR_I2C if I2C
+
+config VIDEO_TVEEPROM
+ tristate
+ depends on I2C
+
+#
# Multimedia Video device configuration
#
@@ -644,7 +690,7 @@ config VIDEO_MXB
tristate "Siemens-Nixdorf 'Multimedia eXtension Board'"
depends on PCI && VIDEO_V4L1 && I2C
select VIDEO_SAA7146_VV
- select VIDEO_TUNER
+ select MEDIA_TUNER
select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO
select VIDEO_TDA9840 if VIDEO_HELPER_CHIPS_AUTO
select VIDEO_TEA6415C if VIDEO_HELPER_CHIPS_AUTO
@@ -702,6 +748,8 @@ source "drivers/media/video/au0828/Kconfig"
source "drivers/media/video/ivtv/Kconfig"
+source "drivers/media/video/cx18/Kconfig"
+
config VIDEO_M32R_AR
tristate "AR devices"
depends on M32R && VIDEO_V4L1
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index be14227f372..a352c6e31f0 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -84,17 +84,7 @@ obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o
obj-$(CONFIG_VIDEO_DPC) += dpc7146.o
obj-$(CONFIG_TUNER_3036) += tuner-3036.o
-obj-$(CONFIG_VIDEO_TUNER) += tuner.o
-
-obj-$(CONFIG_TUNER_XC2028) += tuner-xc2028.o
-obj-$(CONFIG_TUNER_SIMPLE) += tuner-simple.o
-# tuner-types will be merged into tuner-simple, in the future
-obj-$(CONFIG_TUNER_SIMPLE) += tuner-types.o
-obj-$(CONFIG_TUNER_MT20XX) += mt20xx.o
-obj-$(CONFIG_TUNER_TDA8290) += tda8290.o
-obj-$(CONFIG_TUNER_TEA5767) += tea5767.o
-obj-$(CONFIG_TUNER_TEA5761) += tea5761.o
-obj-$(CONFIG_TUNER_TDA9887) += tda9887.o
+obj-$(CONFIG_MEDIA_TUNER) += tuner.o
obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
@@ -134,6 +124,7 @@ obj-$(CONFIG_USB_VICAM) += usbvideo/
obj-$(CONFIG_USB_QUICKCAM_MESSENGER) += usbvideo/
obj-$(CONFIG_VIDEO_IVTV) += ivtv/
+obj-$(CONFIG_VIDEO_CX18) += cx18/
obj-$(CONFIG_VIDEO_VIVI) += vivi.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885/
@@ -147,3 +138,4 @@ obj-$(CONFIG_VIDEO_AU0828) += au0828/
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
diff --git a/drivers/media/video/au0828/Kconfig b/drivers/media/video/au0828/Kconfig
index 41708267e7a..cab277fafa6 100644
--- a/drivers/media/video/au0828/Kconfig
+++ b/drivers/media/video/au0828/Kconfig
@@ -4,7 +4,7 @@ config VIDEO_AU0828
depends on VIDEO_DEV && I2C && INPUT && DVB_CORE
select I2C_ALGOBIT
select DVB_AU8522 if !DVB_FE_CUSTOMIZE
- select DVB_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
+ select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
---help---
This is a video4linux driver for Auvitek's USB device.
diff --git a/drivers/media/video/au0828/Makefile b/drivers/media/video/au0828/Makefile
index 9f4f572c89c..cd2c58281b4 100644
--- a/drivers/media/video/au0828/Makefile
+++ b/drivers/media/video/au0828/Makefile
@@ -2,7 +2,7 @@ au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o
obj-$(CONFIG_VIDEO_AU0828) += au0828.o
-EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/bt8xx/Kconfig b/drivers/media/video/bt8xx/Kconfig
index cfc822bb502..7431ef6de9f 100644
--- a/drivers/media/video/bt8xx/Kconfig
+++ b/drivers/media/video/bt8xx/Kconfig
@@ -6,7 +6,7 @@ config VIDEO_BT848
select VIDEO_BTCX
select VIDEOBUF_DMA_SG
select VIDEO_IR
- select VIDEO_TUNER
+ select MEDIA_TUNER
select VIDEO_TVEEPROM
select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO
select VIDEO_TVAUDIO if VIDEO_HELPER_CHIPS_AUTO
diff --git a/drivers/media/video/bt8xx/Makefile b/drivers/media/video/bt8xx/Makefile
index 924d216d957..e415f6fc447 100644
--- a/drivers/media/video/bt8xx/Makefile
+++ b/drivers/media/video/bt8xx/Makefile
@@ -9,4 +9,5 @@ bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \
obj-$(CONFIG_VIDEO_BT848) += bttv.o
EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig
new file mode 100644
index 00000000000..be654a27bd3
--- /dev/null
+++ b/drivers/media/video/cx18/Kconfig
@@ -0,0 +1,20 @@
+config VIDEO_CX18
+ tristate "Conexant cx23418 MPEG encoder support"
+ depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C && EXPERIMENTAL
+ select I2C_ALGOBIT
+ select FW_LOADER
+ select VIDEO_IR
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEO_CX2341X
+ select VIDEO_CS5345
+ select DVB_S5H1409
+ ---help---
+ This is a video4linux driver for Conexant cx23418 based
+ PCI combo video recorder devices.
+
+ This is used in devices such as the Hauppauge HVR-1600
+ cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx18.
diff --git a/drivers/media/video/cx18/Makefile b/drivers/media/video/cx18/Makefile
new file mode 100644
index 00000000000..b23d2e26120
--- /dev/null
+++ b/drivers/media/video/cx18/Makefile
@@ -0,0 +1,11 @@
+cx18-objs := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio.o \
+ cx18-queue.o cx18-streams.o cx18-fileops.o cx18-ioctl.o cx18-controls.o \
+ cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \
+ cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \
+ cx18-dvb.o
+
+obj-$(CONFIG_VIDEO_CX18) += cx18.o
+
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
diff --git a/drivers/media/video/cx18/cx18-audio.c b/drivers/media/video/cx18/cx18-audio.c
new file mode 100644
index 00000000000..1adc404d955
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-audio.c
@@ -0,0 +1,73 @@
+/*
+ * cx18 audio-related functions
+ *
+ * Derived from ivtv-audio.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-i2c.h"
+#include "cx18-cards.h"
+#include "cx18-audio.h"
+
+/* Selects the audio input and output according to the current
+ settings. */
+int cx18_audio_set_io(struct cx18 *cx)
+{
+ struct v4l2_routing route;
+ u32 audio_input;
+ int mux_input;
+
+ /* Determine which input to use */
+ if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
+ audio_input = cx->card->radio_input.audio_input;
+ mux_input = cx->card->radio_input.muxer_input;
+ } else {
+ audio_input =
+ cx->card->audio_inputs[cx->audio_input].audio_input;
+ mux_input =
+ cx->card->audio_inputs[cx->audio_input].muxer_input;
+ }
+
+ /* handle muxer chips */
+ route.input = mux_input;
+ route.output = 0;
+ cx18_i2c_hw(cx, cx->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+
+ route.input = audio_input;
+ return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl,
+ VIDIOC_INT_S_AUDIO_ROUTING, &route);
+}
+
+void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route)
+{
+ cx18_i2c_hw(cx, cx->card->hw_audio_ctrl,
+ VIDIOC_INT_S_AUDIO_ROUTING, route);
+}
+
+void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq)
+{
+ static u32 freqs[3] = { 44100, 48000, 32000 };
+
+ /* The audio clock of the digitizer must match the codec sample
+ rate otherwise you get some very strange effects. */
+ if (freq > 2)
+ return;
+ cx18_call_i2c_clients(cx, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]);
+}
diff --git a/drivers/media/video/cx18/cx18-audio.h b/drivers/media/video/cx18/cx18-audio.h
new file mode 100644
index 00000000000..cb569a69379
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-audio.h
@@ -0,0 +1,26 @@
+/*
+ * cx18 audio-related functions
+ *
+ * Derived from ivtv-audio.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+int cx18_audio_set_io(struct cx18 *cx);
+void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route);
+void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq);
diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c
new file mode 100644
index 00000000000..2dc3a5dd170
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-av-audio.c
@@ -0,0 +1,361 @@
+/*
+ * cx18 ADEC audio functions
+ *
+ * Derived from cx25840-audio.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "cx18-driver.h"
+
+static int set_audclk_freq(struct cx18 *cx, u32 freq)
+{
+ struct cx18_av_state *state = &cx->av_state;
+
+ if (freq != 32000 && freq != 44100 && freq != 48000)
+ return -EINVAL;
+
+ /* common for all inputs and rates */
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
+ cx18_av_write(cx, 0x127, 0x50);
+
+ if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
+ switch (freq) {
+ case 32000:
+ /* VID_PLL and AUX_PLL */
+ cx18_av_write4(cx, 0x108, 0x1006040f);
+
+ /* AUX_PLL_FRAC */
+ cx18_av_write4(cx, 0x110, 0x01bb39ee);
+
+ /* src3/4/6_ctl = 0x0801f77f */
+ cx18_av_write4(cx, 0x900, 0x0801f77f);
+ cx18_av_write4(cx, 0x904, 0x0801f77f);
+ cx18_av_write4(cx, 0x90c, 0x0801f77f);
+ break;
+
+ case 44100:
+ /* VID_PLL and AUX_PLL */
+ cx18_av_write4(cx, 0x108, 0x1009040f);
+
+ /* AUX_PLL_FRAC */
+ cx18_av_write4(cx, 0x110, 0x00ec6bd6);
+
+ /* src3/4/6_ctl = 0x08016d59 */
+ cx18_av_write4(cx, 0x900, 0x08016d59);
+ cx18_av_write4(cx, 0x904, 0x08016d59);
+ cx18_av_write4(cx, 0x90c, 0x08016d59);
+ break;
+
+ case 48000:
+ /* VID_PLL and AUX_PLL */
+ cx18_av_write4(cx, 0x108, 0x100a040f);
+
+ /* AUX_PLL_FRAC */
+ cx18_av_write4(cx, 0x110, 0x0098d6e5);
+
+ /* src3/4/6_ctl = 0x08014faa */
+ cx18_av_write4(cx, 0x900, 0x08014faa);
+ cx18_av_write4(cx, 0x904, 0x08014faa);
+ cx18_av_write4(cx, 0x90c, 0x08014faa);
+ break;
+ }
+ } else {
+ switch (freq) {
+ case 32000:
+ /* VID_PLL and AUX_PLL */
+ cx18_av_write4(cx, 0x108, 0x1e08040f);
+
+ /* AUX_PLL_FRAC */
+ cx18_av_write4(cx, 0x110, 0x012a0869);
+
+ /* src1_ctl = 0x08010000 */
+ cx18_av_write4(cx, 0x8f8, 0x08010000);
+
+ /* src3/4/6_ctl = 0x08020000 */
+ cx18_av_write4(cx, 0x900, 0x08020000);
+ cx18_av_write4(cx, 0x904, 0x08020000);
+ cx18_av_write4(cx, 0x90c, 0x08020000);
+
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
+ cx18_av_write(cx, 0x127, 0x54);
+ break;
+
+ case 44100:
+ /* VID_PLL and AUX_PLL */
+ cx18_av_write4(cx, 0x108, 0x1809040f);
+
+ /* AUX_PLL_FRAC */
+ cx18_av_write4(cx, 0x110, 0x00ec6bd6);
+
+ /* src1_ctl = 0x08010000 */
+ cx18_av_write4(cx, 0x8f8, 0x080160cd);
+
+ /* src3/4/6_ctl = 0x08020000 */
+ cx18_av_write4(cx, 0x900, 0x08017385);
+ cx18_av_write4(cx, 0x904, 0x08017385);
+ cx18_av_write4(cx, 0x90c, 0x08017385);
+ break;
+
+ case 48000:
+ /* VID_PLL and AUX_PLL */
+ cx18_av_write4(cx, 0x108, 0x180a040f);
+
+ /* AUX_PLL_FRAC */
+ cx18_av_write4(cx, 0x110, 0x0098d6e5);
+
+ /* src1_ctl = 0x08010000 */
+ cx18_av_write4(cx, 0x8f8, 0x08018000);
+
+ /* src3/4/6_ctl = 0x08020000 */
+ cx18_av_write4(cx, 0x900, 0x08015555);
+ cx18_av_write4(cx, 0x904, 0x08015555);
+ cx18_av_write4(cx, 0x90c, 0x08015555);
+ break;
+ }
+ }
+
+ state->audclk_freq = freq;
+
+ return 0;
+}
+
+void cx18_av_audio_set_path(struct cx18 *cx)
+{
+ struct cx18_av_state *state = &cx->av_state;
+
+ /* stop microcontroller */
+ cx18_av_and_or(cx, 0x803, ~0x10, 0);
+
+ /* assert soft reset */
+ cx18_av_and_or(cx, 0x810, ~0x1, 0x01);
+
+ /* Mute everything to prevent the PFFT! */
+ cx18_av_write(cx, 0x8d3, 0x1f);
+
+ if (state->aud_input == CX18_AV_AUDIO_SERIAL) {
+ /* Set Path1 to Serial Audio Input */
+ cx18_av_write4(cx, 0x8d0, 0x01011012);
+
+ /* The microcontroller should not be started for the
+ * non-tuner inputs: autodetection is specific for
+ * TV audio. */
+ } else {
+ /* Set Path1 to Analog Demod Main Channel */
+ cx18_av_write4(cx, 0x8d0, 0x1f063870);
+ }
+
+ set_audclk_freq(cx, state->audclk_freq);
+
+ /* deassert soft reset */
+ cx18_av_and_or(cx, 0x810, ~0x1, 0x00);
+
+ if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
+ /* When the microcontroller detects the
+ * audio format, it will unmute the lines */
+ cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+ }
+}
+
+static int get_volume(struct cx18 *cx)
+{
+ /* Volume runs +18dB to -96dB in 1/2dB steps
+ * change to fit the msp3400 -114dB to +12dB range */
+
+ /* check PATH1_VOLUME */
+ int vol = 228 - cx18_av_read(cx, 0x8d4);
+ vol = (vol / 2) + 23;
+ return vol << 9;
+}
+
+static void set_volume(struct cx18 *cx, int volume)
+{
+ /* First convert the volume to msp3400 values (0-127) */
+ int vol = volume >> 9;
+ /* now scale it up to cx18_av values
+ * -114dB to -96dB maps to 0
+ * this should be 19, but in my testing that was 4dB too loud */
+ if (vol <= 23)
+ vol = 0;
+ else
+ vol -= 23;
+
+ /* PATH1_VOLUME */
+ cx18_av_write(cx, 0x8d4, 228 - (vol * 2));
+}
+
+static int get_bass(struct cx18 *cx)
+{
+ /* bass is 49 steps +12dB to -12dB */
+
+ /* check PATH1_EQ_BASS_VOL */
+ int bass = cx18_av_read(cx, 0x8d9) & 0x3f;
+ bass = (((48 - bass) * 0xffff) + 47) / 48;
+ return bass;
+}
+
+static void set_bass(struct cx18 *cx, int bass)
+{
+ /* PATH1_EQ_BASS_VOL */
+ cx18_av_and_or(cx, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff));
+}
+
+static int get_treble(struct cx18 *cx)
+{
+ /* treble is 49 steps +12dB to -12dB */
+
+ /* check PATH1_EQ_TREBLE_VOL */
+ int treble = cx18_av_read(cx, 0x8db) & 0x3f;
+ treble = (((48 - treble) * 0xffff) + 47) / 48;
+ return treble;
+}
+
+static void set_treble(struct cx18 *cx, int treble)
+{
+ /* PATH1_EQ_TREBLE_VOL */
+ cx18_av_and_or(cx, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff));
+}
+
+static int get_balance(struct cx18 *cx)
+{
+ /* balance is 7 bit, 0 to -96dB */
+
+ /* check PATH1_BAL_LEVEL */
+ int balance = cx18_av_read(cx, 0x8d5) & 0x7f;
+ /* check PATH1_BAL_LEFT */
+ if ((cx18_av_read(cx, 0x8d5) & 0x80) == 0)
+ balance = 0x80 - balance;
+ else
+ balance = 0x80 + balance;
+ return balance << 8;
+}
+
+static void set_balance(struct cx18 *cx, int balance)
+{
+ int bal = balance >> 8;
+ if (bal > 0x80) {
+ /* PATH1_BAL_LEFT */
+ cx18_av_and_or(cx, 0x8d5, 0x7f, 0x80);
+ /* PATH1_BAL_LEVEL */
+ cx18_av_and_or(cx, 0x8d5, ~0x7f, bal & 0x7f);
+ } else {
+ /* PATH1_BAL_LEFT */
+ cx18_av_and_or(cx, 0x8d5, 0x7f, 0x00);
+ /* PATH1_BAL_LEVEL */
+ cx18_av_and_or(cx, 0x8d5, ~0x7f, 0x80 - bal);
+ }
+}
+
+static int get_mute(struct cx18 *cx)
+{
+ /* check SRC1_MUTE_EN */
+ return cx18_av_read(cx, 0x8d3) & 0x2 ? 1 : 0;
+}
+
+static void set_mute(struct cx18 *cx, int mute)
+{
+ struct cx18_av_state *state = &cx->av_state;
+
+ if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
+ /* Must turn off microcontroller in order to mute sound.
+ * Not sure if this is the best method, but it does work.
+ * If the microcontroller is running, then it will undo any
+ * changes to the mute register. */
+ if (mute) {
+ /* disable microcontroller */
+ cx18_av_and_or(cx, 0x803, ~0x10, 0x00);
+ cx18_av_write(cx, 0x8d3, 0x1f);
+ } else {
+ /* enable microcontroller */
+ cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+ }
+ } else {
+ /* SRC1_MUTE_EN */
+ cx18_av_and_or(cx, 0x8d3, ~0x2, mute ? 0x02 : 0x00);
+ }
+}
+
+int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ struct v4l2_control *ctrl = arg;
+ int retval;
+
+ switch (cmd) {
+ case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+ if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
+ cx18_av_and_or(cx, 0x803, ~0x10, 0);
+ cx18_av_write(cx, 0x8d3, 0x1f);
+ }
+ cx18_av_and_or(cx, 0x810, ~0x1, 1);
+ retval = set_audclk_freq(cx, *(u32 *)arg);
+ cx18_av_and_or(cx, 0x810, ~0x1, 0);
+ if (state->aud_input != CX18_AV_AUDIO_SERIAL)
+ cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+ return retval;
+
+ case VIDIOC_G_CTRL:
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value = get_volume(cx);
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ ctrl->value = get_bass(cx);
+ break;
+ case V4L2_CID_AUDIO_TREBLE:
+ ctrl->value = get_treble(cx);
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ ctrl->value = get_balance(cx);
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = get_mute(cx);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case VIDIOC_S_CTRL:
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ set_volume(cx, ctrl->value);
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ set_bass(cx, ctrl->value);
+ break;
+ case V4L2_CID_AUDIO_TREBLE:
+ set_treble(cx, ctrl->value);
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ set_balance(cx, ctrl->value);
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ set_mute(cx, ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c
new file mode 100644
index 00000000000..66864904c99
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-av-core.c
@@ -0,0 +1,879 @@
+/*
+ * cx18 ADEC audio functions
+ *
+ * Derived from cx25840-core.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "cx18-driver.h"
+
+int cx18_av_write(struct cx18 *cx, u16 addr, u8 value)
+{
+ u32 x = readl(cx->reg_mem + 0xc40000 + (addr & ~3));
+ u32 mask = 0xff;
+ int shift = (addr & 3) * 8;
+
+ x = (x & ~(mask << shift)) | ((u32)value << shift);
+ writel(x, cx->reg_mem + 0xc40000 + (addr & ~3));
+ return 0;
+}
+
+int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value)
+{
+ writel(value, cx->reg_mem + 0xc40000 + addr);
+ return 0;
+}
+
+u8 cx18_av_read(struct cx18 *cx, u16 addr)
+{
+ u32 x = readl(cx->reg_mem + 0xc40000 + (addr & ~3));
+ int shift = (addr & 3) * 8;
+
+ return (x >> shift) & 0xff;
+}
+
+u32 cx18_av_read4(struct cx18 *cx, u16 addr)
+{
+ return readl(cx->reg_mem + 0xc40000 + addr);
+}
+
+int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned and_mask,
+ u8 or_value)
+{
+ return cx18_av_write(cx, addr,
+ (cx18_av_read(cx, addr) & and_mask) |
+ or_value);
+}
+
+int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask,
+ u32 or_value)
+{
+ return cx18_av_write4(cx, addr,
+ (cx18_av_read4(cx, addr) & and_mask) |
+ or_value);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
+ enum cx18_av_audio_input aud_input);
+static void log_audio_status(struct cx18 *cx);
+static void log_video_status(struct cx18 *cx);
+
+/* ----------------------------------------------------------------------- */
+
+static void cx18_av_initialize(struct cx18 *cx)
+{
+ u32 v;
+
+ cx18_av_loadfw(cx);
+ /* Stop 8051 code execution */
+ cx18_av_write4(cx, CXADEC_DL_CTL, 0x03000000);
+
+ /* initallize the PLL by toggling sleep bit */
+ v = cx18_av_read4(cx, CXADEC_HOST_REG1);
+ /* enable sleep mode */
+ cx18_av_write4(cx, CXADEC_HOST_REG1, v | 1);
+ /* disable sleep mode */
+ cx18_av_write4(cx, CXADEC_HOST_REG1, v & 0xfffe);
+
+ /* initialize DLLs */
+ v = cx18_av_read4(cx, CXADEC_DLL1_DIAG_CTRL) & 0xE1FFFEFF;
+ /* disable FLD */
+ cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v);
+ /* enable FLD */
+ cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v | 0x10000100);
+
+ v = cx18_av_read4(cx, CXADEC_DLL2_DIAG_CTRL) & 0xE1FFFEFF;
+ /* disable FLD */
+ cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v);
+ /* enable FLD */
+ cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v | 0x06000100);
+
+ /* set analog bias currents. Set Vreg to 1.20V. */
+ cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL1, 0x000A1802);
+
+ v = cx18_av_read4(cx, CXADEC_AFE_DIAG_CTRL3) | 1;
+ /* enable TUNE_FIL_RST */
+ cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v);
+ /* disable TUNE_FIL_RST */
+ cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v & 0xFFFFFFFE);
+
+ /* enable 656 output */
+ cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x040C00);
+
+ /* video output drive strength */
+ cx18_av_and_or4(cx, CXADEC_PIN_CTRL2, ~0, 0x2);
+
+ /* reset video */
+ cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0x8000);
+ cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0);
+
+ /* set video to auto-detect */
+ /* Clear bits 11-12 to enable slow locking mode. Set autodetect mode */
+ /* set the comb notch = 1 */
+ cx18_av_and_or4(cx, CXADEC_MODE_CTRL, 0xFFF7E7F0, 0x02040800);
+
+ /* Enable wtw_en in CRUSH_CTRL (Set bit 22) */
+ /* Enable maj_sel in CRUSH_CTRL (Set bit 20) */
+ cx18_av_and_or4(cx, CXADEC_CRUSH_CTRL, ~0, 0x00500000);
+
+ /* Set VGA_TRACK_RANGE to 0x20 */
+ cx18_av_and_or4(cx, CXADEC_DFE_CTRL2, 0xFFFF00FF, 0x00002000);
+
+ /* Enable VBI capture */
+ cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253F);
+ /* cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253E); */
+
+ /* Set the video input.
+ The setting in MODE_CTRL gets lost when we do the above setup */
+ /* EncSetSignalStd(dwDevNum, pEnc->dwSigStd); */
+ /* EncSetVideoInput(dwDevNum, pEnc->VidIndSelection); */
+
+ v = cx18_av_read4(cx, CXADEC_AFE_CTRL);
+ v &= 0xFFFBFFFF; /* turn OFF bit 18 for droop_comp_ch1 */
+ v &= 0xFFFF7FFF; /* turn OFF bit 9 for clamp_sel_ch1 */
+ v &= 0xFFFFFFFE; /* turn OFF bit 0 for 12db_ch1 */
+ /* v |= 0x00000001;*/ /* turn ON bit 0 for 12db_ch1 */
+ cx18_av_write4(cx, CXADEC_AFE_CTRL, v);
+
+/* if(dwEnable && dw3DCombAvailable) { */
+/* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x7728021F); */
+/* } else { */
+/* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x6628021F); */
+/* } */
+ cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, 0x6628021F);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void input_change(struct cx18 *cx)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ v4l2_std_id std = state->std;
+
+ /* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */
+ if (std & V4L2_STD_SECAM)
+ cx18_av_write(cx, 0x402, 0);
+ else {
+ cx18_av_write(cx, 0x402, 0x04);
+ cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11);
+ }
+ cx18_av_and_or(cx, 0x401, ~0x60, 0);
+ cx18_av_and_or(cx, 0x401, ~0x60, 0x60);
+
+ if (std & V4L2_STD_525_60) {
+ if (std == V4L2_STD_NTSC_M_JP) {
+ /* Japan uses EIAJ audio standard */
+ cx18_av_write(cx, 0x808, 0xf7);
+ } else if (std == V4L2_STD_NTSC_M_KR) {
+ /* South Korea uses A2 audio standard */
+ cx18_av_write(cx, 0x808, 0xf8);
+ } else {
+ /* Others use the BTSC audio standard */
+ cx18_av_write(cx, 0x808, 0xf6);
+ }
+ cx18_av_write(cx, 0x80b, 0x00);
+ } else if (std & V4L2_STD_PAL) {
+ /* Follow tuner change procedure for PAL */
+ cx18_av_write(cx, 0x808, 0xff);
+ cx18_av_write(cx, 0x80b, 0x03);
+ } else if (std & V4L2_STD_SECAM) {
+ /* Select autodetect for SECAM */
+ cx18_av_write(cx, 0x808, 0xff);
+ cx18_av_write(cx, 0x80b, 0x03);
+ }
+
+ if (cx18_av_read(cx, 0x803) & 0x10) {
+ /* restart audio decoder microcontroller */
+ cx18_av_and_or(cx, 0x803, ~0x10, 0x00);
+ cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
+ }
+}
+
+static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
+ enum cx18_av_audio_input aud_input)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ u8 is_composite = (vid_input >= CX18_AV_COMPOSITE1 &&
+ vid_input <= CX18_AV_COMPOSITE8);
+ u8 reg;
+
+ CX18_DEBUG_INFO("decoder set video input %d, audio input %d\n",
+ vid_input, aud_input);
+
+ if (is_composite) {
+ reg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1);
+ } else {
+ int luma = vid_input & 0xf0;
+ int chroma = vid_input & 0xf00;
+
+ if ((vid_input & ~0xff0) ||
+ luma < CX18_AV_SVIDEO_LUMA1 ||
+ luma > CX18_AV_SVIDEO_LUMA4 ||
+ chroma < CX18_AV_SVIDEO_CHROMA4 ||
+ chroma > CX18_AV_SVIDEO_CHROMA8) {
+ CX18_ERR("0x%04x is not a valid video input!\n",
+ vid_input);
+ return -EINVAL;
+ }
+ reg = 0xf0 + ((luma - CX18_AV_SVIDEO_LUMA1) >> 4);
+ if (chroma >= CX18_AV_SVIDEO_CHROMA7) {
+ reg &= 0x3f;
+ reg |= (chroma - CX18_AV_SVIDEO_CHROMA7) >> 2;
+ } else {
+ reg &= 0xcf;
+ reg |= (chroma - CX18_AV_SVIDEO_CHROMA4) >> 4;
+ }
+ }
+
+ switch (aud_input) {
+ case CX18_AV_AUDIO_SERIAL:
+ /* do nothing, use serial audio input */
+ break;
+ case CX18_AV_AUDIO4: reg &= ~0x30; break;
+ case CX18_AV_AUDIO5: reg &= ~0x30; reg |= 0x10; break;
+ case CX18_AV_AUDIO6: reg &= ~0x30; reg |= 0x20; break;
+ case CX18_AV_AUDIO7: reg &= ~0xc0; break;
+ case CX18_AV_AUDIO8: reg &= ~0xc0; reg |= 0x40; break;
+
+ default:
+ CX18_ERR("0x%04x is not a valid audio input!\n", aud_input);
+ return -EINVAL;
+ }
+
+ cx18_av_write(cx, 0x103, reg);
+ /* Set INPUT_MODE to Composite (0) or S-Video (1) */
+ cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02);
+ /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
+ cx18_av_and_or(cx, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
+ /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */
+ if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30)
+ cx18_av_and_or(cx, 0x102, ~0x4, 4);
+ else
+ cx18_av_and_or(cx, 0x102, ~0x4, 0);
+ /*cx18_av_and_or4(cx, 0x104, ~0x001b4180, 0x00004180);*/
+
+ state->vid_input = vid_input;
+ state->aud_input = aud_input;
+ cx18_av_audio_set_path(cx);
+ input_change(cx);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int set_v4lstd(struct cx18 *cx)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ u8 fmt = 0; /* zero is autodetect */
+ u8 pal_m = 0;
+
+ /* First tests should be against specific std */
+ if (state->std == V4L2_STD_NTSC_M_JP) {
+ fmt = 0x2;
+ } else if (state->std == V4L2_STD_NTSC_443) {
+ fmt = 0x3;
+ } else if (state->std == V4L2_STD_PAL_M) {
+ pal_m = 1;
+ fmt = 0x5;
+ } else if (state->std == V4L2_STD_PAL_N) {
+ fmt = 0x6;
+ } else if (state->std == V4L2_STD_PAL_Nc) {
+ fmt = 0x7;
+ } else if (state->std == V4L2_STD_PAL_60) {
+ fmt = 0x8;
+ } else {
+ /* Then, test against generic ones */
+ if (state->std & V4L2_STD_NTSC)
+ fmt = 0x1;
+ else if (state->std & V4L2_STD_PAL)
+ fmt = 0x4;
+ else if (state->std & V4L2_STD_SECAM)
+ fmt = 0xc;
+ }
+
+ CX18_DEBUG_INFO("changing video std to fmt %i\n", fmt);
+
+ /* Follow step 9 of section 3.16 in the cx18_av datasheet.
+ Without this PAL may display a vertical ghosting effect.
+ This happens for example with the Yuan MPC622. */
+ if (fmt >= 4 && fmt < 8) {
+ /* Set format to NTSC-M */
+ cx18_av_and_or(cx, 0x400, ~0xf, 1);
+ /* Turn off LCOMB */
+ cx18_av_and_or(cx, 0x47b, ~6, 0);
+ }
+ cx18_av_and_or(cx, 0x400, ~0xf, fmt);
+ cx18_av_and_or(cx, 0x403, ~0x3, pal_m);
+ cx18_av_vbi_setup(cx);
+ input_change(cx);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ CX18_ERR("invalid brightness setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+
+ cx18_av_write(cx, 0x414, ctrl->value - 128);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ if (ctrl->value < 0 || ctrl->value > 127) {
+ CX18_ERR("invalid contrast setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+
+ cx18_av_write(cx, 0x415, ctrl->value << 1);
+ break;
+
+ case V4L2_CID_SATURATION:
+ if (ctrl->value < 0 || ctrl->value > 127) {
+ CX18_ERR("invalid saturation setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+
+ cx18_av_write(cx, 0x420, ctrl->value << 1);
+ cx18_av_write(cx, 0x421, ctrl->value << 1);
+ break;
+
+ case V4L2_CID_HUE:
+ if (ctrl->value < -127 || ctrl->value > 127) {
+ CX18_ERR("invalid hue setting %d\n", ctrl->value);
+ return -ERANGE;
+ }
+
+ cx18_av_write(cx, 0x422, ctrl->value);
+ break;
+
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_MUTE:
+ return cx18_av_audio(cx, VIDIOC_S_CTRL, ctrl);
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int get_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = (s8)cx18_av_read(cx, 0x414) + 128;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = cx18_av_read(cx, 0x415) >> 1;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = cx18_av_read(cx, 0x420) >> 1;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = (s8)cx18_av_read(cx, 0x422);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_MUTE:
+ return cx18_av_audio(cx, VIDIOC_G_CTRL, ctrl);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int get_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt)
+{
+ switch (fmt->type) {
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ return cx18_av_vbi(cx, VIDIOC_G_FMT, fmt);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ struct v4l2_pix_format *pix;
+ int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
+ int is_50Hz = !(state->std & V4L2_STD_525_60);
+
+ switch (fmt->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pix = &(fmt->fmt.pix);
+
+ Vsrc = (cx18_av_read(cx, 0x476) & 0x3f) << 4;
+ Vsrc |= (cx18_av_read(cx, 0x475) & 0xf0) >> 4;
+
+ Hsrc = (cx18_av_read(cx, 0x472) & 0x3f) << 4;
+ Hsrc |= (cx18_av_read(cx, 0x471) & 0xf0) >> 4;
+
+ Vlines = pix->height + (is_50Hz ? 4 : 7);
+
+ if ((pix->width * 16 < Hsrc) || (Hsrc < pix->width) ||
+ (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) {
+ CX18_ERR("%dx%d is not a valid size!\n",
+ pix->width, pix->height);
+ return -ERANGE;
+ }
+
+ HSC = (Hsrc * (1 << 20)) / pix->width - (1 << 20);
+ VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9));
+ VSC &= 0x1fff;
+
+ if (pix->width >= 385)
+ filter = 0;
+ else if (pix->width > 192)
+ filter = 1;
+ else if (pix->width > 96)
+ filter = 2;
+ else
+ filter = 3;
+
+ CX18_DEBUG_INFO("decoder set size %dx%d -> scale %ux%u\n",
+ pix->width, pix->height, HSC, VSC);
+
+ /* HSCALE=HSC */
+ cx18_av_write(cx, 0x418, HSC & 0xff);
+ cx18_av_write(cx, 0x419, (HSC >> 8) & 0xff);
+ cx18_av_write(cx, 0x41a, HSC >> 16);
+ /* VSCALE=VSC */
+ cx18_av_write(cx, 0x41c, VSC & 0xff);
+ cx18_av_write(cx, 0x41d, VSC >> 8);
+ /* VS_INTRLACE=1 VFILT=filter */
+ cx18_av_write(cx, 0x41e, 0x8 | filter);
+ break;
+
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt);
+
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt);
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ struct v4l2_tuner *vt = arg;
+ struct v4l2_routing *route = arg;
+
+ /* ignore these commands */
+ switch (cmd) {
+ case TUNER_SET_TYPE_ADDR:
+ return 0;
+ }
+
+ if (!state->is_initialized) {
+ CX18_DEBUG_INFO("cmd %08x triggered fw load\n", cmd);
+ /* initialize on first use */
+ state->is_initialized = 1;
+ cx18_av_initialize(cx);
+ }
+
+ switch (cmd) {
+ case VIDIOC_INT_DECODE_VBI_LINE:
+ return cx18_av_vbi(cx, cmd, arg);
+
+ case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+ return cx18_av_audio(cx, cmd, arg);
+
+ case VIDIOC_STREAMON:
+ CX18_DEBUG_INFO("enable output\n");
+ cx18_av_write(cx, 0x115, 0x8c);
+ cx18_av_write(cx, 0x116, 0x07);
+ break;
+
+ case VIDIOC_STREAMOFF:
+ CX18_DEBUG_INFO("disable output\n");
+ cx18_av_write(cx, 0x115, 0x00);
+ cx18_av_write(cx, 0x116, 0x00);
+ break;
+
+ case VIDIOC_LOG_STATUS:
+ log_video_status(cx);
+ log_audio_status(cx);
+ break;
+
+ case VIDIOC_G_CTRL:
+ return get_v4lctrl(cx, (struct v4l2_control *)arg);
+
+ case VIDIOC_S_CTRL:
+ return set_v4lctrl(cx, (struct v4l2_control *)arg);
+
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *qc = arg;
+
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill_std(qc);
+ default:
+ break;
+ }
+
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ return v4l2_ctrl_query_fill_std(qc);
+ default:
+ return -EINVAL;
+ }
+ return -EINVAL;
+ }
+
+ case VIDIOC_G_STD:
+ *(v4l2_std_id *)arg = state->std;
+ break;
+
+ case VIDIOC_S_STD:
+ if (state->radio == 0 && state->std == *(v4l2_std_id *)arg)
+ return 0;
+ state->radio = 0;
+ state->std = *(v4l2_std_id *)arg;
+ return set_v4lstd(cx);
+
+ case AUDC_SET_RADIO:
+ state->radio = 1;
+ break;
+
+ case VIDIOC_INT_G_VIDEO_ROUTING:
+ route->input = state->vid_input;
+ route->output = 0;
+ break;
+
+ case VIDIOC_INT_S_VIDEO_ROUTING:
+ return set_input(cx, route->input, state->aud_input);
+
+ case VIDIOC_INT_G_AUDIO_ROUTING:
+ route->input = state->aud_input;
+ route->output = 0;
+ break;
+
+ case VIDIOC_INT_S_AUDIO_ROUTING:
+ return set_input(cx, state->vid_input, route->input);
+
+ case VIDIOC_S_FREQUENCY:
+ input_change(cx);
+ break;
+
+ case VIDIOC_G_TUNER:
+ {
+ u8 vpres = cx18_av_read(cx, 0x40e) & 0x20;
+ u8 mode;
+ int val = 0;
+
+ if (state->radio)
+ break;
+
+ vt->signal = vpres ? 0xffff : 0x0;
+
+ vt->capability |=
+ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+
+ mode = cx18_av_read(cx, 0x804);
+
+ /* get rxsubchans and audmode */
+ if ((mode & 0xf) == 1)
+ val |= V4L2_TUNER_SUB_STEREO;
+ else
+ val |= V4L2_TUNER_SUB_MONO;
+
+ if (mode == 2 || mode == 4)
+ val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+
+ if (mode & 0x10)
+ val |= V4L2_TUNER_SUB_SAP;
+
+ vt->rxsubchans = val;
+ vt->audmode = state->audmode;
+ break;
+ }
+
+ case VIDIOC_S_TUNER:
+ if (state->radio)
+ break;
+
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ /* mono -> mono
+ stereo -> mono
+ bilingual -> lang1 */
+ cx18_av_and_or(cx, 0x809, ~0xf, 0x00);
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1:
+ /* mono -> mono
+ stereo -> stereo
+ bilingual -> lang1 */
+ cx18_av_and_or(cx, 0x809, ~0xf, 0x04);
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ /* mono -> mono
+ stereo -> stereo
+ bilingual -> lang1/lang2 */
+ cx18_av_and_or(cx, 0x809, ~0xf, 0x07);
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ /* mono -> mono
+ stereo -> stereo
+ bilingual -> lang2 */
+ cx18_av_and_or(cx, 0x809, ~0xf, 0x01);
+ break;
+ default:
+ return -EINVAL;
+ }
+ state->audmode = vt->audmode;
+ break;
+
+ case VIDIOC_G_FMT:
+ return get_v4lfmt(cx, (struct v4l2_format *)arg);
+
+ case VIDIOC_S_FMT:
+ return set_v4lfmt(cx, (struct v4l2_format *)arg);
+
+ case VIDIOC_INT_RESET:
+ cx18_av_initialize(cx);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------- */
+
+static void log_video_status(struct cx18 *cx)
+{
+ static const char *const fmt_strs[] = {
+ "0x0",
+ "NTSC-M", "NTSC-J", "NTSC-4.43",
+ "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60",
+ "0x9", "0xA", "0xB",
+ "SECAM",
+ "0xD", "0xE", "0xF"
+ };
+
+ struct cx18_av_state *state = &cx->av_state;
+ u8 vidfmt_sel = cx18_av_read(cx, 0x400) & 0xf;
+ u8 gen_stat1 = cx18_av_read(cx, 0x40d);
+ u8 gen_stat2 = cx18_av_read(cx, 0x40e);
+ int vid_input = state->vid_input;
+
+ CX18_INFO("Video signal: %spresent\n",
+ (gen_stat2 & 0x20) ? "" : "not ");
+ CX18_INFO("Detected format: %s\n",
+ fmt_strs[gen_stat1 & 0xf]);
+
+ CX18_INFO("Specified standard: %s\n",
+ vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection");
+
+ if (vid_input >= CX18_AV_COMPOSITE1 &&
+ vid_input <= CX18_AV_COMPOSITE8) {
+ CX18_INFO("Specified video input: Composite %d\n",
+ vid_input - CX18_AV_COMPOSITE1 + 1);
+ } else {
+ CX18_INFO("Specified video input: S-Video (Luma In%d, Chroma In%d)\n",
+ (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8);
+ }
+
+ CX18_INFO("Specified audioclock freq: %d Hz\n", state->audclk_freq);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void log_audio_status(struct cx18 *cx)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ u8 download_ctl = cx18_av_read(cx, 0x803);
+ u8 mod_det_stat0 = cx18_av_read(cx, 0x805);
+ u8 mod_det_stat1 = cx18_av_read(cx, 0x804);
+ u8 audio_config = cx18_av_read(cx, 0x808);
+ u8 pref_mode = cx18_av_read(cx, 0x809);
+ u8 afc0 = cx18_av_read(cx, 0x80b);
+ u8 mute_ctl = cx18_av_read(cx, 0x8d3);
+ int aud_input = state->aud_input;
+ char *p;
+
+ switch (mod_det_stat0) {
+ case 0x00: p = "mono"; break;
+ case 0x01: p = "stereo"; break;
+ case 0x02: p = "dual"; break;
+ case 0x04: p = "tri"; break;
+ case 0x10: p = "mono with SAP"; break;
+ case 0x11: p = "stereo with SAP"; break;
+ case 0x12: p = "dual with SAP"; break;
+ case 0x14: p = "tri with SAP"; break;
+ case 0xfe: p = "forced mode"; break;
+ default: p = "not defined";
+ }
+ CX18_INFO("Detected audio mode: %s\n", p);
+
+ switch (mod_det_stat1) {
+ case 0x00: p = "BTSC"; break;
+ case 0x01: p = "EIAJ"; break;
+ case 0x02: p = "A2-M"; break;
+ case 0x03: p = "A2-BG"; break;
+ case 0x04: p = "A2-DK1"; break;
+ case 0x05: p = "A2-DK2"; break;
+ case 0x06: p = "A2-DK3"; break;
+ case 0x07: p = "A1 (6.0 MHz FM Mono)"; break;
+ case 0x08: p = "AM-L"; break;
+ case 0x09: p = "NICAM-BG"; break;
+ case 0x0a: p = "NICAM-DK"; break;
+ case 0x0b: p = "NICAM-I"; break;
+ case 0x0c: p = "NICAM-L"; break;
+ case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break;
+ case 0xff: p = "no detected audio standard"; break;
+ default: p = "not defined";
+ }
+ CX18_INFO("Detected audio standard: %s\n", p);
+ CX18_INFO("Audio muted: %s\n",
+ (mute_ctl & 0x2) ? "yes" : "no");
+ CX18_INFO("Audio microcontroller: %s\n",
+ (download_ctl & 0x10) ? "running" : "stopped");
+
+ switch (audio_config >> 4) {
+ case 0x00: p = "BTSC"; break;
+ case 0x01: p = "EIAJ"; break;
+ case 0x02: p = "A2-M"; break;
+ case 0x03: p = "A2-BG"; break;
+ case 0x04: p = "A2-DK1"; break;
+ case 0x05: p = "A2-DK2"; break;
+ case 0x06: p = "A2-DK3"; break;
+ case 0x07: p = "A1 (6.0 MHz FM Mono)"; break;
+ case 0x08: p = "AM-L"; break;
+ case 0x09: p = "NICAM-BG"; break;
+ case 0x0a: p = "NICAM-DK"; break;
+ case 0x0b: p = "NICAM-I"; break;
+ case 0x0c: p = "NICAM-L"; break;
+ case 0x0d: p = "FM radio"; break;
+ case 0x0f: p = "automatic detection"; break;
+ default: p = "undefined";
+ }
+ CX18_INFO("Configured audio standard: %s\n", p);
+
+ if ((audio_config >> 4) < 0xF) {
+ switch (audio_config & 0xF) {
+ case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break;
+ case 0x01: p = "MONO2 (LANGUAGE B)"; break;
+ case 0x02: p = "MONO3 (STEREO forced MONO)"; break;
+ case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break;
+ case 0x04: p = "STEREO"; break;
+ case 0x05: p = "DUAL1 (AB)"; break;
+ case 0x06: p = "DUAL2 (AC) (FM)"; break;
+ case 0x07: p = "DUAL3 (BC) (FM)"; break;
+ case 0x08: p = "DUAL4 (AC) (AM)"; break;
+ case 0x09: p = "DUAL5 (BC) (AM)"; break;
+ case 0x0a: p = "SAP"; break;
+ default: p = "undefined";
+ }
+ CX18_INFO("Configured audio mode: %s\n", p);
+ } else {
+ switch (audio_config & 0xF) {
+ case 0x00: p = "BG"; break;
+ case 0x01: p = "DK1"; break;
+ case 0x02: p = "DK2"; break;
+ case 0x03: p = "DK3"; break;
+ case 0x04: p = "I"; break;
+ case 0x05: p = "L"; break;
+ case 0x06: p = "BTSC"; break;
+ case 0x07: p = "EIAJ"; break;
+ case 0x08: p = "A2-M"; break;
+ case 0x09: p = "FM Radio"; break;
+ case 0x0f: p = "automatic standard and mode detection"; break;
+ default: p = "undefined";
+ }
+ CX18_INFO("Configured audio system: %s\n", p);
+ }
+
+ if (aud_input)
+ CX18_INFO("Specified audio input: Tuner (In%d)\n",
+ aud_input);
+ else
+ CX18_INFO("Specified audio input: External\n");
+
+ switch (pref_mode & 0xf) {
+ case 0: p = "mono/language A"; break;
+ case 1: p = "language B"; break;
+ case 2: p = "language C"; break;
+ case 3: p = "analog fallback"; break;
+ case 4: p = "stereo"; break;
+ case 5: p = "language AC"; break;
+ case 6: p = "language BC"; break;
+ case 7: p = "language AB"; break;
+ default: p = "undefined";
+ }
+ CX18_INFO("Preferred audio mode: %s\n", p);
+
+ if ((audio_config & 0xf) == 0xf) {
+ switch ((afc0 >> 2) & 0x1) {
+ case 0: p = "system DK"; break;
+ case 1: p = "system L"; break;
+ }
+ CX18_INFO("Selected 65 MHz format: %s\n", p);
+
+ switch (afc0 & 0x3) {
+ case 0: p = "BTSC"; break;
+ case 1: p = "EIAJ"; break;
+ case 2: p = "A2-M"; break;
+ default: p = "undefined";
+ }
+ CX18_INFO("Selected 45 MHz format: %s\n", p);
+ }
+}
diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h
new file mode 100644
index 00000000000..786901d72e9
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-av-core.h
@@ -0,0 +1,318 @@
+/*
+ * cx18 ADEC header
+ *
+ * Derived from cx25840-core.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef _CX18_AV_CORE_H_
+#define _CX18_AV_CORE_H_
+
+struct cx18;
+
+enum cx18_av_video_input {
+ /* Composite video inputs In1-In8 */
+ CX18_AV_COMPOSITE1 = 1,
+ CX18_AV_COMPOSITE2,
+ CX18_AV_COMPOSITE3,
+ CX18_AV_COMPOSITE4,
+ CX18_AV_COMPOSITE5,
+ CX18_AV_COMPOSITE6,
+ CX18_AV_COMPOSITE7,
+ CX18_AV_COMPOSITE8,
+
+ /* S-Video inputs consist of one luma input (In1-In4) ORed with one
+ chroma input (In5-In8) */
+ CX18_AV_SVIDEO_LUMA1 = 0x10,
+ CX18_AV_SVIDEO_LUMA2 = 0x20,
+ CX18_AV_SVIDEO_LUMA3 = 0x30,
+ CX18_AV_SVIDEO_LUMA4 = 0x40,
+ CX18_AV_SVIDEO_CHROMA4 = 0x400,
+ CX18_AV_SVIDEO_CHROMA5 = 0x500,
+ CX18_AV_SVIDEO_CHROMA6 = 0x600,
+ CX18_AV_SVIDEO_CHROMA7 = 0x700,
+ CX18_AV_SVIDEO_CHROMA8 = 0x800,
+
+ /* S-Video aliases for common luma/chroma combinations */
+ CX18_AV_SVIDEO1 = 0x510,
+ CX18_AV_SVIDEO2 = 0x620,
+ CX18_AV_SVIDEO3 = 0x730,
+ CX18_AV_SVIDEO4 = 0x840,
+};
+
+enum cx18_av_audio_input {
+ /* Audio inputs: serial or In4-In8 */
+ CX18_AV_AUDIO_SERIAL,
+ CX18_AV_AUDIO4 = 4,
+ CX18_AV_AUDIO5,
+ CX18_AV_AUDIO6,
+ CX18_AV_AUDIO7,
+ CX18_AV_AUDIO8,
+};
+
+struct cx18_av_state {
+ int radio;
+ v4l2_std_id std;
+ enum cx18_av_video_input vid_input;
+ enum cx18_av_audio_input aud_input;
+ u32 audclk_freq;
+ int audmode;
+ int vbi_line_offset;
+ u32 id;
+ u32 rev;
+ int is_initialized;
+};
+
+
+/* Registers */
+#define CXADEC_CHIP_TYPE_TIGER 0x837
+#define CXADEC_CHIP_TYPE_MAKO 0x843
+
+#define CXADEC_HOST_REG1 0x000
+#define CXADEC_HOST_REG2 0x001
+
+#define CXADEC_CHIP_CTRL 0x100
+#define CXADEC_AFE_CTRL 0x104
+#define CXADEC_PLL_CTRL1 0x108
+#define CXADEC_VID_PLL_FRAC 0x10C
+#define CXADEC_AUX_PLL_FRAC 0x110
+#define CXADEC_PIN_CTRL1 0x114
+#define CXADEC_PIN_CTRL2 0x118
+#define CXADEC_PIN_CFG1 0x11C
+#define CXADEC_PIN_CFG2 0x120
+
+#define CXADEC_PIN_CFG3 0x124
+#define CXADEC_I2S_MCLK 0x127
+
+#define CXADEC_AUD_LOCK1 0x128
+#define CXADEC_AUD_LOCK2 0x12C
+#define CXADEC_POWER_CTRL 0x130
+#define CXADEC_AFE_DIAG_CTRL1 0x134
+#define CXADEC_AFE_DIAG_CTRL2 0x138
+#define CXADEC_AFE_DIAG_CTRL3 0x13C
+#define CXADEC_PLL_DIAG_CTRL 0x140
+#define CXADEC_TEST_CTRL1 0x144
+#define CXADEC_TEST_CTRL2 0x148
+#define CXADEC_BIST_STAT 0x14C
+#define CXADEC_DLL1_DIAG_CTRL 0x158
+#define CXADEC_DLL2_DIAG_CTRL 0x15C
+
+/* IR registers */
+#define CXADEC_IR_CTRL_REG 0x200
+#define CXADEC_IR_TXCLK_REG 0x204
+#define CXADEC_IR_RXCLK_REG 0x208
+#define CXADEC_IR_CDUTY_REG 0x20C
+#define CXADEC_IR_STAT_REG 0x210
+#define CXADEC_IR_IRQEN_REG 0x214
+#define CXADEC_IR_FILTER_REG 0x218
+#define CXADEC_IR_FIFO_REG 0x21C
+
+/* Video Registers */
+#define CXADEC_MODE_CTRL 0x400
+#define CXADEC_OUT_CTRL1 0x404
+#define CXADEC_OUT_CTRL2 0x408
+#define CXADEC_GEN_STAT 0x40C
+#define CXADEC_INT_STAT_MASK 0x410
+#define CXADEC_LUMA_CTRL 0x414
+
+#define CXADEC_BRIGHTNESS_CTRL_BYTE 0x414
+#define CXADEC_CONTRAST_CTRL_BYTE 0x415
+#define CXADEC_LUMA_CTRL_BYTE_3 0x416
+
+#define CXADEC_HSCALE_CTRL 0x418
+#define CXADEC_VSCALE_CTRL 0x41C
+
+#define CXADEC_CHROMA_CTRL 0x420
+
+#define CXADEC_USAT_CTRL_BYTE 0x420
+#define CXADEC_VSAT_CTRL_BYTE 0x421
+#define CXADEC_HUE_CTRL_BYTE 0x422
+
+#define CXADEC_VBI_LINE_CTRL1 0x424
+#define CXADEC_VBI_LINE_CTRL2 0x428
+#define CXADEC_VBI_LINE_CTRL3 0x42C
+#define CXADEC_VBI_LINE_CTRL4 0x430
+#define CXADEC_VBI_LINE_CTRL5 0x434
+#define CXADEC_VBI_FC_CFG 0x438
+#define CXADEC_VBI_MISC_CFG1 0x43C
+#define CXADEC_VBI_MISC_CFG2 0x440
+#define CXADEC_VBI_PAY1 0x444
+#define CXADEC_VBI_PAY2 0x448
+#define CXADEC_VBI_CUST1_CFG1 0x44C
+#define CXADEC_VBI_CUST1_CFG2 0x450
+#define CXADEC_VBI_CUST1_CFG3 0x454
+#define CXADEC_VBI_CUST2_CFG1 0x458
+#define CXADEC_VBI_CUST2_CFG2 0x45C
+#define CXADEC_VBI_CUST2_CFG3 0x460
+#define CXADEC_VBI_CUST3_CFG1 0x464
+#define CXADEC_VBI_CUST3_CFG2 0x468
+#define CXADEC_VBI_CUST3_CFG3 0x46C
+#define CXADEC_HORIZ_TIM_CTRL 0x470
+#define CXADEC_VERT_TIM_CTRL 0x474
+#define CXADEC_SRC_COMB_CFG 0x478
+#define CXADEC_CHROMA_VBIOFF_CFG 0x47C
+#define CXADEC_FIELD_COUNT 0x480
+#define CXADEC_MISC_TIM_CTRL 0x484
+#define CXADEC_DFE_CTRL1 0x488
+#define CXADEC_DFE_CTRL2 0x48C
+#define CXADEC_DFE_CTRL3 0x490
+#define CXADEC_PLL_CTRL2 0x494
+#define CXADEC_HTL_CTRL 0x498
+#define CXADEC_COMB_CTRL 0x49C
+#define CXADEC_CRUSH_CTRL 0x4A0
+#define CXADEC_SOFT_RST_CTRL 0x4A4
+#define CXADEC_MV_DT_CTRL2 0x4A8
+#define CXADEC_MV_DT_CTRL3 0x4AC
+#define CXADEC_MISC_DIAG_CTRL 0x4B8
+
+#define CXADEC_DL_CTL 0x800
+#define CXADEC_DL_CTL_ADDRESS_LOW 0x800 /* Byte 1 in DL_CTL */
+#define CXADEC_DL_CTL_ADDRESS_HIGH 0x801 /* Byte 2 in DL_CTL */
+#define CXADEC_DL_CTL_DATA 0x802 /* Byte 3 in DL_CTL */
+#define CXADEC_DL_CTL_CONTROL 0x803 /* Byte 4 in DL_CTL */
+
+#define CXADEC_STD_DET_STATUS 0x804
+
+#define CXADEC_STD_DET_CTL 0x808
+#define CXADEC_STD_DET_CTL_AUD_CTL 0x808 /* Byte 1 in STD_DET_CTL */
+#define CXADEC_STD_DET_CTL_PREF_MODE 0x809 /* Byte 2 in STD_DET_CTL */
+
+#define CXADEC_DW8051_INT 0x80C
+#define CXADEC_GENERAL_CTL 0x810
+#define CXADEC_AAGC_CTL 0x814
+#define CXADEC_IF_SRC_CTL 0x818
+#define CXADEC_ANLOG_DEMOD_CTL 0x81C
+#define CXADEC_ROT_FREQ_CTL 0x820
+#define CXADEC_FM1_CTL 0x824
+#define CXADEC_PDF_CTL 0x828
+#define CXADEC_DFT1_CTL1 0x82C
+#define CXADEC_DFT1_CTL2 0x830
+#define CXADEC_DFT_STATUS 0x834
+#define CXADEC_DFT2_CTL1 0x838
+#define CXADEC_DFT2_CTL2 0x83C
+#define CXADEC_DFT2_STATUS 0x840
+#define CXADEC_DFT3_CTL1 0x844
+#define CXADEC_DFT3_CTL2 0x848
+#define CXADEC_DFT3_STATUS 0x84C
+#define CXADEC_DFT4_CTL1 0x850
+#define CXADEC_DFT4_CTL2 0x854
+#define CXADEC_DFT4_STATUS 0x858
+#define CXADEC_AM_MTS_DET 0x85C
+#define CXADEC_ANALOG_MUX_CTL 0x860
+#define CXADEC_DIG_PLL_CTL1 0x864
+#define CXADEC_DIG_PLL_CTL2 0x868
+#define CXADEC_DIG_PLL_CTL3 0x86C
+#define CXADEC_DIG_PLL_CTL4 0x870
+#define CXADEC_DIG_PLL_CTL5 0x874
+#define CXADEC_DEEMPH_GAIN_CTL 0x878
+#define CXADEC_DEEMPH_COEF1 0x87C
+#define CXADEC_DEEMPH_COEF2 0x880
+#define CXADEC_DBX1_CTL1 0x884
+#define CXADEC_DBX1_CTL2 0x888
+#define CXADEC_DBX1_STATUS 0x88C
+#define CXADEC_DBX2_CTL1 0x890
+#define CXADEC_DBX2_CTL2 0x894
+#define CXADEC_DBX2_STATUS 0x898
+#define CXADEC_AM_FM_DIFF 0x89C
+
+/* NICAM registers go here */
+#define CXADEC_NICAM_STATUS 0x8C8
+#define CXADEC_DEMATRIX_CTL 0x8CC
+
+#define CXADEC_PATH1_CTL1 0x8D0
+#define CXADEC_PATH1_VOL_CTL 0x8D4
+#define CXADEC_PATH1_EQ_CTL 0x8D8
+#define CXADEC_PATH1_SC_CTL 0x8DC
+
+#define CXADEC_PATH2_CTL1 0x8E0
+#define CXADEC_PATH2_VOL_CTL 0x8E4
+#define CXADEC_PATH2_EQ_CTL 0x8E8
+#define CXADEC_PATH2_SC_CTL 0x8EC
+
+#define CXADEC_SRC_CTL 0x8F0
+#define CXADEC_SRC_LF_COEF 0x8F4
+#define CXADEC_SRC1_CTL 0x8F8
+#define CXADEC_SRC2_CTL 0x8FC
+#define CXADEC_SRC3_CTL 0x900
+#define CXADEC_SRC4_CTL 0x904
+#define CXADEC_SRC5_CTL 0x908
+#define CXADEC_SRC6_CTL 0x90C
+
+#define CXADEC_BASEBAND_OUT_SEL 0x910
+#define CXADEC_I2S_IN_CTL 0x914
+#define CXADEC_I2S_OUT_CTL 0x918
+#define CXADEC_AC97_CTL 0x91C
+#define CXADEC_QAM_PDF 0x920
+#define CXADEC_QAM_CONST_DEC 0x924
+#define CXADEC_QAM_ROTATOR_FREQ 0x948
+
+/* Bit defintions / settings used in Mako Audio */
+#define CXADEC_PREF_MODE_MONO_LANGA 0
+#define CXADEC_PREF_MODE_MONO_LANGB 1
+#define CXADEC_PREF_MODE_MONO_LANGC 2
+#define CXADEC_PREF_MODE_FALLBACK 3
+#define CXADEC_PREF_MODE_STEREO 4
+#define CXADEC_PREF_MODE_DUAL_LANG_AC 5
+#define CXADEC_PREF_MODE_DUAL_LANG_BC 6
+#define CXADEC_PREF_MODE_DUAL_LANG_AB 7
+
+
+#define CXADEC_DETECT_STEREO 1
+#define CXADEC_DETECT_DUAL 2
+#define CXADEC_DETECT_TRI 4
+#define CXADEC_DETECT_SAP 0x10
+#define CXADEC_DETECT_NO_SIGNAL 0xFF
+
+#define CXADEC_SELECT_AUDIO_STANDARD_BG 0xF0 /* NICAM BG and A2 BG */
+#define CXADEC_SELECT_AUDIO_STANDARD_DK1 0xF1 /* NICAM DK and A2 DK */
+#define CXADEC_SELECT_AUDIO_STANDARD_DK2 0xF2
+#define CXADEC_SELECT_AUDIO_STANDARD_DK3 0xF3
+#define CXADEC_SELECT_AUDIO_STANDARD_I 0xF4 /* NICAM I and A1 */
+#define CXADEC_SELECT_AUDIO_STANDARD_L 0xF5 /* NICAM L and System L AM */
+#define CXADEC_SELECT_AUDIO_STANDARD_BTSC 0xF6
+#define CXADEC_SELECT_AUDIO_STANDARD_EIAJ 0xF7
+#define CXADEC_SELECT_AUDIO_STANDARD_A2_M 0xF8 /* A2 M */
+#define CXADEC_SELECT_AUDIO_STANDARD_FM 0xF9 /* FM radio */
+#define CXADEC_SELECT_AUDIO_STANDARD_AUTO 0xFF /* Auto detect */
+
+/* ----------------------------------------------------------------------- */
+/* cx18_av-core.c */
+int cx18_av_write(struct cx18 *cx, u16 addr, u8 value);
+int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value);
+u8 cx18_av_read(struct cx18 *cx, u16 addr);
+u32 cx18_av_read4(struct cx18 *cx, u16 addr);
+int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value);
+int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value);
+int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg);
+
+/* ----------------------------------------------------------------------- */
+/* cx18_av-firmware.c */
+int cx18_av_loadfw(struct cx18 *cx);
+
+/* ----------------------------------------------------------------------- */
+/* cx18_av-audio.c */
+int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg);
+void cx18_av_audio_set_path(struct cx18 *cx);
+
+/* ----------------------------------------------------------------------- */
+/* cx18_av-vbi.c */
+void cx18_av_vbi_setup(struct cx18 *cx);
+int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg);
+
+#endif
diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c
new file mode 100644
index 00000000000..526e142156c
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-av-firmware.c
@@ -0,0 +1,120 @@
+/*
+ * cx18 ADEC firmware functions
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "cx18-driver.h"
+#include <linux/firmware.h>
+
+#define FWFILE "v4l-cx23418-dig.fw"
+
+int cx18_av_loadfw(struct cx18 *cx)
+{
+ const struct firmware *fw = NULL;
+ u32 size;
+ u32 v;
+ u8 *ptr;
+ int i;
+
+ if (request_firmware(&fw, FWFILE, &cx->dev->dev) != 0) {
+ CX18_ERR("unable to open firmware %s\n", FWFILE);
+ return -EINVAL;
+ }
+
+ cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000);
+ cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6); /* Byte 0 */
+
+ /* Reset the Mako core (Register is undocumented.) */
+ cx18_av_write4(cx, 0x8100, 0x00010000);
+
+ /* Put the 8051 in reset and enable firmware upload */
+ cx18_av_write4(cx, CXADEC_DL_CTL, 0x0F000000);
+
+ ptr = fw->data;
+ size = fw->size;
+
+ for (i = 0; i < size; i++) {
+ u32 dl_control = 0x0F000000 | ((u32)ptr[i] << 16);
+ u32 value = 0;
+ int retries;
+
+ for (retries = 0; retries < 5; retries++) {
+ cx18_av_write4(cx, CXADEC_DL_CTL, dl_control);
+ value = cx18_av_read4(cx, CXADEC_DL_CTL);
+ if ((value & 0x3F00) == (dl_control & 0x3F00))
+ break;
+ }
+ if (retries >= 5) {
+ CX18_ERR("unable to load firmware %s\n", FWFILE);
+ release_firmware(fw);
+ return -EIO;
+ }
+ }
+
+ cx18_av_write4(cx, CXADEC_DL_CTL, 0x13000000 | fw->size);
+
+ /* Output to the 416 */
+ cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000);
+
+ /* Audio input control 1 set to Sony mode */
+ /* Audio output input 2 is 0 for slave operation input */
+ /* 0xC4000914[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */
+ /* 0xC4000914[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge
+ after WS transition for first bit of audio word. */
+ cx18_av_write4(cx, CXADEC_I2S_IN_CTL, 0x000000A0);
+
+ /* Audio output control 1 is set to Sony mode */
+ /* Audio output control 2 is set to 1 for master mode */
+ /* 0xC4000918[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */
+ /* 0xC4000918[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge
+ after WS transition for first bit of audio word. */
+ /* 0xC4000918[8]: 0 = slave operation, 1 = master (SCK_OUT and WS_OUT
+ are generated) */
+ cx18_av_write4(cx, CXADEC_I2S_OUT_CTL, 0x000001A0);
+
+ /* set alt I2s master clock to /16 and enable alt divider i2s
+ passthrough */
+ cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5000B687);
+
+ cx18_av_write4(cx, CXADEC_STD_DET_CTL, 0x000000F6);
+ /* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */
+
+ /* Set bit 0 in register 0x9CC to signify that this is MiniMe. */
+ /* Register 0x09CC is defined by the Merlin firmware, and doesn't
+ have a name in the spec. */
+ cx18_av_write4(cx, 0x09CC, 1);
+
+#define CX18_AUDIO_ENABLE 0xc72014
+ v = read_reg(CX18_AUDIO_ENABLE);
+ /* If bit 11 is 1 */
+ if (v & 0x800)
+ write_reg(v & 0xFFFFFBFF, CX18_AUDIO_ENABLE); /* Clear bit 10 */
+
+ /* Enable WW auto audio standard detection */
+ v = cx18_av_read4(cx, CXADEC_STD_DET_CTL);
+ v |= 0xFF; /* Auto by default */
+ v |= 0x400; /* Stereo by default */
+ v |= 0x14000000;
+ cx18_av_write4(cx, CXADEC_STD_DET_CTL, v);
+
+ release_firmware(fw);
+
+ CX18_INFO("loaded %s firmware (%d bytes)\n", FWFILE, size);
+ return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c
new file mode 100644
index 00000000000..d09f1daf4eb
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-av-vbi.c
@@ -0,0 +1,413 @@
+/*
+ * cx18 ADEC VBI functions
+ *
+ * Derived from cx25840-vbi.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+#include "cx18-driver.h"
+
+static int odd_parity(u8 c)
+{
+ c ^= (c >> 4);
+ c ^= (c >> 2);
+ c ^= (c >> 1);
+
+ return c & 1;
+}
+
+static int decode_vps(u8 *dst, u8 *p)
+{
+ static const u8 biphase_tbl[] = {
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
+ 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
+ 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
+ 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
+ 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
+ 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
+ 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ };
+
+ u8 c, err = 0;
+ int i;
+
+ for (i = 0; i < 2 * 13; i += 2) {
+ err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
+ c = (biphase_tbl[p[i + 1]] & 0xf) |
+ ((biphase_tbl[p[i]] & 0xf) << 4);
+ dst[i / 2] = c;
+ }
+
+ return err & 0xf0;
+}
+
+void cx18_av_vbi_setup(struct cx18 *cx)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ v4l2_std_id std = state->std;
+ int hblank, hactive, burst, vblank, vactive, sc;
+ int vblank656, src_decimation;
+ int luma_lpf, uv_lpf, comb;
+ u32 pll_int, pll_frac, pll_post;
+
+ /* datasheet startup, step 8d */
+ if (std & ~V4L2_STD_NTSC)
+ cx18_av_write(cx, 0x49f, 0x11);
+ else
+ cx18_av_write(cx, 0x49f, 0x14);
+
+ if (std & V4L2_STD_625_50) {
+ hblank = 0x084;
+ hactive = 0x2d0;
+ burst = 0x5d;
+ vblank = 0x024;
+ vactive = 0x244;
+ vblank656 = 0x28;
+ src_decimation = 0x21f;
+
+ luma_lpf = 2;
+ if (std & V4L2_STD_SECAM) {
+ uv_lpf = 0;
+ comb = 0;
+ sc = 0x0a425f;
+ } else if (std == V4L2_STD_PAL_Nc) {
+ uv_lpf = 1;
+ comb = 0x20;
+ sc = 556453;
+ } else {
+ uv_lpf = 1;
+ comb = 0x20;
+ sc = 0x0a8263;
+ }
+ } else {
+ hactive = 720;
+ hblank = 122;
+ vactive = 487;
+ luma_lpf = 1;
+ uv_lpf = 1;
+
+ src_decimation = 0x21f;
+ if (std == V4L2_STD_PAL_60) {
+ vblank = 26;
+ vblank656 = 26;
+ burst = 0x5b;
+ luma_lpf = 2;
+ comb = 0x20;
+ sc = 0x0a8263;
+ } else if (std == V4L2_STD_PAL_M) {
+ vblank = 20;
+ vblank656 = 24;
+ burst = 0x61;
+ comb = 0x20;
+
+ sc = 555452;
+ } else {
+ vblank = 26;
+ vblank656 = 26;
+ burst = 0x5b;
+ comb = 0x66;
+ sc = 556063;
+ }
+ }
+
+ /* DEBUG: Displays configured PLL frequency */
+ pll_int = cx18_av_read(cx, 0x108);
+ pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff;
+ pll_post = cx18_av_read(cx, 0x109);
+ CX18_DEBUG_INFO("PLL regs = int: %u, frac: %u, post: %u\n",
+ pll_int, pll_frac, pll_post);
+
+ if (pll_post) {
+ int fin, fsc;
+ int pll = 28636363L * ((((u64)pll_int) << 25) + pll_frac);
+
+ pll >>= 25;
+ pll /= pll_post;
+ CX18_DEBUG_INFO("PLL = %d.%06d MHz\n",
+ pll / 1000000, pll % 1000000);
+ CX18_DEBUG_INFO("PLL/8 = %d.%06d MHz\n",
+ pll / 8000000, (pll / 8) % 1000000);
+
+ fin = ((u64)src_decimation * pll) >> 12;
+ CX18_DEBUG_INFO("ADC Sampling freq = %d.%06d MHz\n",
+ fin / 1000000, fin % 1000000);
+
+ fsc = (((u64)sc) * pll) >> 24L;
+ CX18_DEBUG_INFO("Chroma sub-carrier freq = %d.%06d MHz\n",
+ fsc / 1000000, fsc % 1000000);
+
+ CX18_DEBUG_INFO("hblank %i, hactive %i, "
+ "vblank %i , vactive %i, vblank656 %i, src_dec %i,"
+ "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x,"
+ " sc 0x%06x\n",
+ hblank, hactive, vblank, vactive, vblank656,
+ src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
+ }
+
+ /* Sets horizontal blanking delay and active lines */
+ cx18_av_write(cx, 0x470, hblank);
+ cx18_av_write(cx, 0x471, 0xff & (((hblank >> 8) & 0x3) |
+ (hactive << 4)));
+ cx18_av_write(cx, 0x472, hactive >> 4);
+
+ /* Sets burst gate delay */
+ cx18_av_write(cx, 0x473, burst);
+
+ /* Sets vertical blanking delay and active duration */
+ cx18_av_write(cx, 0x474, vblank);
+ cx18_av_write(cx, 0x475, 0xff & (((vblank >> 8) & 0x3) |
+ (vactive << 4)));
+ cx18_av_write(cx, 0x476, vactive >> 4);
+ cx18_av_write(cx, 0x477, vblank656);
+
+ /* Sets src decimation rate */
+ cx18_av_write(cx, 0x478, 0xff & src_decimation);
+ cx18_av_write(cx, 0x479, 0xff & (src_decimation >> 8));
+
+ /* Sets Luma and UV Low pass filters */
+ cx18_av_write(cx, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30));
+
+ /* Enables comb filters */
+ cx18_av_write(cx, 0x47b, comb);
+
+ /* Sets SC Step*/
+ cx18_av_write(cx, 0x47c, sc);
+ cx18_av_write(cx, 0x47d, 0xff & sc >> 8);
+ cx18_av_write(cx, 0x47e, 0xff & sc >> 16);
+
+ /* Sets VBI parameters */
+ if (std & V4L2_STD_625_50) {
+ cx18_av_write(cx, 0x47f, 0x01);
+ state->vbi_line_offset = 5;
+ } else {
+ cx18_av_write(cx, 0x47f, 0x00);
+ state->vbi_line_offset = 8;
+ }
+}
+
+int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg)
+{
+ struct cx18_av_state *state = &cx->av_state;
+ struct v4l2_format *fmt;
+ struct v4l2_sliced_vbi_format *svbi;
+
+ switch (cmd) {
+ case VIDIOC_G_FMT:
+ {
+ static u16 lcr2vbi[] = {
+ 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
+ 0, V4L2_SLICED_WSS_625, 0, /* 4 */
+ V4L2_SLICED_CAPTION_525, /* 6 */
+ 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */
+ 0, 0, 0, 0
+ };
+ int is_pal = !(state->std & V4L2_STD_525_60);
+ int i;
+
+ fmt = arg;
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+ svbi = &fmt->fmt.sliced;
+ memset(svbi, 0, sizeof(*svbi));
+ /* we're done if raw VBI is active */
+ if ((cx18_av_read(cx, 0x404) & 0x10) == 0)
+ break;
+
+ if (is_pal) {
+ for (i = 7; i <= 23; i++) {
+ u8 v = cx18_av_read(cx, 0x424 + i - 7);
+
+ svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+ svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+ svbi->service_set |= svbi->service_lines[0][i] |
+ svbi->service_lines[1][i];
+ }
+ } else {
+ for (i = 10; i <= 21; i++) {
+ u8 v = cx18_av_read(cx, 0x424 + i - 10);
+
+ svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+ svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+ svbi->service_set |= svbi->service_lines[0][i] |
+ svbi->service_lines[1][i];
+ }
+ }
+ break;
+ }
+
+ case VIDIOC_S_FMT:
+ {
+ int is_pal = !(state->std & V4L2_STD_525_60);
+ int vbi_offset = is_pal ? 1 : 0;
+ int i, x;
+ u8 lcr[24];
+
+ fmt = arg;
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+ svbi = &fmt->fmt.sliced;
+ if (svbi->service_set == 0) {
+ /* raw VBI */
+ memset(svbi, 0, sizeof(*svbi));
+
+ /* Setup VBI */
+ cx18_av_vbi_setup(cx);
+
+ /* VBI Offset */
+ cx18_av_write(cx, 0x47f, vbi_offset);
+ cx18_av_write(cx, 0x404, 0x2e);
+ break;
+ }
+
+ for (x = 0; x <= 23; x++)
+ lcr[x] = 0x00;
+
+ /* Setup VBI */
+ cx18_av_vbi_setup(cx);
+
+ /* Sliced VBI */
+ cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */
+ cx18_av_write(cx, 0x406, 0x13);
+ cx18_av_write(cx, 0x47f, vbi_offset);
+
+ if (is_pal) {
+ for (i = 0; i <= 6; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+ } else {
+ for (i = 0; i <= 9; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+
+ for (i = 22; i <= 23; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+ }
+
+ for (i = 7; i <= 23; i++) {
+ for (x = 0; x <= 1; x++) {
+ switch (svbi->service_lines[1-x][i]) {
+ case V4L2_SLICED_TELETEXT_B:
+ lcr[i] |= 1 << (4 * x);
+ break;
+ case V4L2_SLICED_WSS_625:
+ lcr[i] |= 4 << (4 * x);
+ break;
+ case V4L2_SLICED_CAPTION_525:
+ lcr[i] |= 6 << (4 * x);
+ break;
+ case V4L2_SLICED_VPS:
+ lcr[i] |= 9 << (4 * x);
+ break;
+ }
+ }
+ }
+
+ if (is_pal) {
+ for (x = 1, i = 0x424; i <= 0x434; i++, x++)
+ cx18_av_write(cx, i, lcr[6 + x]);
+ } else {
+ for (x = 1, i = 0x424; i <= 0x430; i++, x++)
+ cx18_av_write(cx, i, lcr[9 + x]);
+ for (i = 0x431; i <= 0x434; i++)
+ cx18_av_write(cx, i, 0);
+ }
+
+ cx18_av_write(cx, 0x43c, 0x16);
+ cx18_av_write(cx, 0x474, is_pal ? 0x2a : 0x22);
+ break;
+ }
+
+ case VIDIOC_INT_DECODE_VBI_LINE:
+ {
+ struct v4l2_decode_vbi_line *vbi = arg;
+ u8 *p = vbi->p;
+ int id1, id2, l, err = 0;
+
+ if (p[0] || p[1] != 0xff || p[2] != 0xff ||
+ (p[3] != 0x55 && p[3] != 0x91)) {
+ vbi->line = vbi->type = 0;
+ break;
+ }
+
+ p += 4;
+ id1 = p[-1];
+ id2 = p[0] & 0xf;
+ l = p[2] & 0x3f;
+ l += state->vbi_line_offset;
+ p += 4;
+
+ switch (id2) {
+ case 1:
+ id2 = V4L2_SLICED_TELETEXT_B;
+ break;
+ case 4:
+ id2 = V4L2_SLICED_WSS_625;
+ break;
+ case 6:
+ id2 = V4L2_SLICED_CAPTION_525;
+ err = !odd_parity(p[0]) || !odd_parity(p[1]);
+ break;
+ case 9:
+ id2 = V4L2_SLICED_VPS;
+ if (decode_vps(p, p) != 0)
+ err = 1;
+ break;
+ default:
+ id2 = 0;
+ err = 1;
+ break;
+ }
+
+ vbi->type = err ? 0 : id2;
+ vbi->line = err ? 0 : l;
+ vbi->is_second_field = err ? 0 : (id1 == 0x55);
+ vbi->p = p;
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c
new file mode 100644
index 00000000000..f5e3ba1f535
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-cards.c
@@ -0,0 +1,277 @@
+/*
+ * cx18 functions to query card hardware
+ *
+ * Derived from ivtv-cards.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-cards.h"
+#include "cx18-i2c.h"
+#include <media/cs5345.h>
+
+/********************** card configuration *******************************/
+
+/* usual i2c tuner addresses to probe */
+static struct cx18_card_tuner_i2c cx18_i2c_std = {
+ .radio = { I2C_CLIENT_END },
+ .demod = { 0x43, I2C_CLIENT_END },
+ .tv = { 0x61, 0x60, I2C_CLIENT_END },
+};
+
+/* Please add new PCI IDs to: http://pci-ids.ucw.cz/iii
+ This keeps the PCI ID database up to date. Note that the entries
+ must be added under vendor 0x4444 (Conexant) as subsystem IDs.
+ New vendor IDs should still be added to the vendor ID list. */
+
+/* Hauppauge HVR-1600 cards */
+
+/* Note: for Hauppauge cards the tveeprom information is used instead
+ of PCI IDs */
+static const struct cx18_card cx18_card_hvr1600_esmt = {
+ .type = CX18_CARD_HVR_1600_ESMT,
+ .name = "Hauppauge HVR-1600",
+ .comment = "DVB & VBI are not yet supported\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+ .hw_audio_ctrl = CX18_HW_CX23418,
+ .hw_muxer = CX18_HW_CS5345,
+ .hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER | CX18_HW_CS5345,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 },
+ { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
+ { CX18_CARD_INPUT_SVIDEO2, 2, CX23418_SVIDEO2 },
+ { CX18_CARD_INPUT_COMPOSITE2, 2, CX23418_COMPOSITE4 },
+ },
+ .audio_inputs = {
+ { CX18_CARD_INPUT_AUD_TUNER,
+ CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
+ { CX18_CARD_INPUT_LINE_IN1,
+ CX23418_AUDIO_SERIAL, CS5345_IN_2 },
+ { CX18_CARD_INPUT_LINE_IN2,
+ CX23418_AUDIO_SERIAL, CS5345_IN_2 },
+ },
+ .radio_input = { CX18_CARD_INPUT_AUD_TUNER,
+ CX23418_AUDIO_SERIAL, 0 },
+ .ddr = {
+ /* ESMT M13S128324A-5B memory */
+ .chip_config = 0x003,
+ .refresh = 0x30c,
+ .timing1 = 0x44220e82,
+ .timing2 = 0x08,
+ .tune_lane = 0,
+ .initial_emrs = 0,
+ },
+ .gpio_init.initial_value = 0x3001,
+ .gpio_init.direction = 0x3001,
+ .i2c = &cx18_i2c_std,
+};
+
+static const struct cx18_card cx18_card_hvr1600_samsung = {
+ .type = CX18_CARD_HVR_1600_SAMSUNG,
+ .name = "Hauppauge HVR-1600 (Preproduction)",
+ .comment = "DVB & VBI are not yet supported\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+ .hw_audio_ctrl = CX18_HW_CX23418,
+ .hw_muxer = CX18_HW_CS5345,
+ .hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER | CX18_HW_CS5345,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 },
+ { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
+ { CX18_CARD_INPUT_SVIDEO2, 2, CX23418_SVIDEO2 },
+ { CX18_CARD_INPUT_COMPOSITE2, 2, CX23418_COMPOSITE4 },
+ },
+ .audio_inputs = {
+ { CX18_CARD_INPUT_AUD_TUNER,
+ CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
+ { CX18_CARD_INPUT_LINE_IN1,
+ CX23418_AUDIO_SERIAL, CS5345_IN_2 },
+ { CX18_CARD_INPUT_LINE_IN2,
+ CX23418_AUDIO_SERIAL, CS5345_IN_2 },
+ },
+ .radio_input = { CX18_CARD_INPUT_AUD_TUNER,
+ CX23418_AUDIO_SERIAL, 0 },
+ .ddr = {
+ /* Samsung K4D263238G-VC33 memory */
+ .chip_config = 0x003,
+ .refresh = 0x30c,
+ .timing1 = 0x23230b73,
+ .timing2 = 0x08,
+ .tune_lane = 0,
+ .initial_emrs = 2,
+ },
+ .gpio_init.initial_value = 0x3001,
+ .gpio_init.direction = 0x3001,
+ .i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Compro VideoMate H900: not working at the moment! */
+
+static const struct cx18_card_pci_info cx18_pci_h900[] = {
+ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_COMPRO, 0xe100 },
+ { 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_h900 = {
+ .type = CX18_CARD_COMPRO_H900,
+ .name = "Compro VideoMate H900",
+ .comment = "Not yet supported!\n",
+ .v4l2_capabilities = 0,
+ .hw_audio_ctrl = CX18_HW_CX23418,
+ .hw_all = CX18_HW_TUNER,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 },
+ { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
+ },
+ .audio_inputs = {
+ { CX18_CARD_INPUT_AUD_TUNER,
+ CX23418_AUDIO8, 0 },
+ { CX18_CARD_INPUT_LINE_IN1,
+ CX23418_AUDIO_SERIAL, 0 },
+ },
+ .radio_input = { CX18_CARD_INPUT_AUD_TUNER,
+ CX23418_AUDIO_SERIAL, 0 },
+ .tuners = {
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
+ .ddr = {
+ /* EtronTech EM6A9160TS-5G memory */
+ .chip_config = 0x50003,
+ .refresh = 0x753,
+ .timing1 = 0x24330e84,
+ .timing2 = 0x1f,
+ .tune_lane = 0,
+ .initial_emrs = 0,
+ },
+ .pci_list = cx18_pci_h900,
+ .i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Yuan MPC718: not working at the moment! */
+
+static const struct cx18_card_pci_info cx18_pci_mpc718[] = {
+ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_YUAN, 0x0718 },
+ { 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_mpc718 = {
+ .type = CX18_CARD_YUAN_MPC718,
+ .name = "Yuan MPC718",
+ .comment = "Not yet supported!\n",
+ .v4l2_capabilities = 0,
+ .hw_audio_ctrl = CX18_HW_CX23418,
+ .hw_all = CX18_HW_TUNER,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 },
+ { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
+ },
+ .audio_inputs = {
+ { CX18_CARD_INPUT_AUD_TUNER,
+ CX23418_AUDIO8, 0 },
+ { CX18_CARD_INPUT_LINE_IN1,
+ CX23418_AUDIO_SERIAL, 0 },
+ },
+ .radio_input = { CX18_CARD_INPUT_AUD_TUNER,
+ CX23418_AUDIO_SERIAL, 0 },
+ .tuners = {
+ /* XC3028 tuner */
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
+ /* tuner reset */
+ .gpio_init = { .direction = 0x1000, .initial_value = 0x1000 },
+ .ddr = {
+ /* Probably Samsung K4D263238G-VC33 memory */
+ .chip_config = 0x003,
+ .refresh = 0x30c,
+ .timing1 = 0x23230b73,
+ .timing2 = 0x08,
+ .tune_lane = 0,
+ .initial_emrs = 2,
+ },
+ .pci_list = cx18_pci_mpc718,
+ .i2c = &cx18_i2c_std,
+};
+
+static const struct cx18_card *cx18_card_list[] = {
+ &cx18_card_hvr1600_esmt,
+ &cx18_card_hvr1600_samsung,
+ &cx18_card_h900,
+ &cx18_card_mpc718,
+};
+
+const struct cx18_card *cx18_get_card(u16 index)
+{
+ if (index >= ARRAY_SIZE(cx18_card_list))
+ return NULL;
+ return cx18_card_list[index];
+}
+
+int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input)
+{
+ const struct cx18_card_video_input *card_input =
+ cx->card->video_inputs + index;
+ static const char * const input_strs[] = {
+ "Tuner 1",
+ "S-Video 1",
+ "S-Video 2",
+ "Composite 1",
+ "Composite 2",
+ "Composite 3"
+ };
+
+ memset(input, 0, sizeof(*input));
+ if (index >= cx->nof_inputs)
+ return -EINVAL;
+ input->index = index;
+ strlcpy(input->name, input_strs[card_input->video_type - 1],
+ sizeof(input->name));
+ input->type = (card_input->video_type == CX18_CARD_INPUT_VID_TUNER ?
+ V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA);
+ input->audioset = (1 << cx->nof_audio_inputs) - 1;
+ input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ?
+ cx->tuner_std : V4L2_STD_ALL;
+ return 0;
+}
+
+int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *audio)
+{
+ const struct cx18_card_audio_input *aud_input =
+ cx->card->audio_inputs + index;
+ static const char * const input_strs[] = {
+ "Tuner 1",
+ "Line In 1",
+ "Line In 2"
+ };
+
+ memset(audio, 0, sizeof(*audio));
+ if (index >= cx->nof_audio_inputs)
+ return -EINVAL;
+ strlcpy(audio->name, input_strs[aud_input->audio_type - 1],
+ sizeof(audio->name));
+ audio->index = index;
+ audio->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h
new file mode 100644
index 00000000000..bca249bdd33
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-cards.h
@@ -0,0 +1,170 @@
+/*
+ * cx18 functions to query card hardware
+ *
+ * Derived from ivtv-cards.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+/* hardware flags */
+#define CX18_HW_TUNER (1 << 0)
+#define CX18_HW_TVEEPROM (1 << 1)
+#define CX18_HW_CS5345 (1 << 2)
+#define CX18_HW_GPIO (1 << 3)
+#define CX18_HW_CX23418 (1 << 4)
+#define CX18_HW_DVB (1 << 5)
+
+/* video inputs */
+#define CX18_CARD_INPUT_VID_TUNER 1
+#define CX18_CARD_INPUT_SVIDEO1 2
+#define CX18_CARD_INPUT_SVIDEO2 3
+#define CX18_CARD_INPUT_COMPOSITE1 4
+#define CX18_CARD_INPUT_COMPOSITE2 5
+#define CX18_CARD_INPUT_COMPOSITE3 6
+
+enum cx34180_video_input {
+ /* Composite video inputs In1-In8 */
+ CX23418_COMPOSITE1 = 1,
+ CX23418_COMPOSITE2,
+ CX23418_COMPOSITE3,
+ CX23418_COMPOSITE4,
+ CX23418_COMPOSITE5,
+ CX23418_COMPOSITE6,
+ CX23418_COMPOSITE7,
+ CX23418_COMPOSITE8,
+
+ /* S-Video inputs consist of one luma input (In1-In4) ORed with one
+ chroma input (In5-In8) */
+ CX23418_SVIDEO_LUMA1 = 0x10,
+ CX23418_SVIDEO_LUMA2 = 0x20,
+ CX23418_SVIDEO_LUMA3 = 0x30,
+ CX23418_SVIDEO_LUMA4 = 0x40,
+ CX23418_SVIDEO_CHROMA4 = 0x400,
+ CX23418_SVIDEO_CHROMA5 = 0x500,
+ CX23418_SVIDEO_CHROMA6 = 0x600,
+ CX23418_SVIDEO_CHROMA7 = 0x700,
+ CX23418_SVIDEO_CHROMA8 = 0x800,
+
+ /* S-Video aliases for common luma/chroma combinations */
+ CX23418_SVIDEO1 = 0x510,
+ CX23418_SVIDEO2 = 0x620,
+ CX23418_SVIDEO3 = 0x730,
+ CX23418_SVIDEO4 = 0x840,
+};
+
+/* audio inputs */
+#define CX18_CARD_INPUT_AUD_TUNER 1
+#define CX18_CARD_INPUT_LINE_IN1 2
+#define CX18_CARD_INPUT_LINE_IN2 3
+
+#define CX18_CARD_MAX_VIDEO_INPUTS 6
+#define CX18_CARD_MAX_AUDIO_INPUTS 3
+#define CX18_CARD_MAX_TUNERS 2
+
+enum cx23418_audio_input {
+ /* Audio inputs: serial or In4-In8 */
+ CX23418_AUDIO_SERIAL,
+ CX23418_AUDIO4 = 4,
+ CX23418_AUDIO5,
+ CX23418_AUDIO6,
+ CX23418_AUDIO7,
+ CX23418_AUDIO8,
+};
+
+/* V4L2 capability aliases */
+#define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE)
+/* | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */
+
+struct cx18_card_video_input {
+ u8 video_type; /* video input type */
+ u8 audio_index; /* index in cx18_card_audio_input array */
+ u16 video_input; /* hardware video input */
+};
+
+struct cx18_card_audio_input {
+ u8 audio_type; /* audio input type */
+ u32 audio_input; /* hardware audio input */
+ u16 muxer_input; /* hardware muxer input for boards with a
+ multiplexer chip */
+};
+
+struct cx18_card_pci_info {
+ u16 device;
+ u16 subsystem_vendor;
+ u16 subsystem_device;
+};
+
+/* GPIO definitions */
+
+/* The mask is the set of bits used by the operation */
+
+struct cx18_gpio_init { /* set initial GPIO DIR and OUT values */
+ u16 direction; /* DIR setting. Leave to 0 if no init is needed */
+ u16 initial_value;
+};
+
+struct cx18_card_tuner {
+ v4l2_std_id std; /* standard for which the tuner is suitable */
+ int tuner; /* tuner ID (from tuner.h) */
+};
+
+struct cx18_card_tuner_i2c {
+ unsigned short radio[2];/* radio tuner i2c address to probe */
+ unsigned short demod[2];/* demodulator i2c address to probe */
+ unsigned short tv[4]; /* tv tuner i2c addresses to probe */
+};
+
+struct cx18_ddr { /* DDR config data */
+ u32 chip_config;
+ u32 refresh;
+ u32 timing1;
+ u32 timing2;
+ u32 tune_lane;
+ u32 initial_emrs;
+};
+
+/* for card information/parameters */
+struct cx18_card {
+ int type;
+ char *name;
+ char *comment;
+ u32 v4l2_capabilities;
+ u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only
+ 1 dev allowed) */
+ u32 hw_muxer; /* hardware used to multiplex audio input */
+ u32 hw_all; /* all hardware used by the board */
+ struct cx18_card_video_input video_inputs[CX18_CARD_MAX_VIDEO_INPUTS];
+ struct cx18_card_audio_input audio_inputs[CX18_CARD_MAX_AUDIO_INPUTS];
+ struct cx18_card_audio_input radio_input;
+
+ /* GPIO card-specific settings */
+ struct cx18_gpio_init gpio_init;
+
+ struct cx18_card_tuner tuners[CX18_CARD_MAX_TUNERS];
+ struct cx18_card_tuner_i2c *i2c;
+
+ struct cx18_ddr ddr;
+
+ /* list of device and subsystem vendor/devices that
+ correspond to this card type. */
+ const struct cx18_card_pci_info *pci_list;
+};
+
+int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input);
+int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *input);
+const struct cx18_card *cx18_get_card(u16 index);
diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c
new file mode 100644
index 00000000000..2bdac5ebbb0
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-controls.c
@@ -0,0 +1,306 @@
+/*
+ * cx18 ioctl control functions
+ *
+ * Derived from ivtv-controls.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-av-core.h"
+#include "cx18-cards.h"
+#include "cx18-ioctl.h"
+#include "cx18-audio.h"
+#include "cx18-i2c.h"
+#include "cx18-mailbox.h"
+#include "cx18-controls.h"
+
+static const u32 user_ctrls[] = {
+ V4L2_CID_USER_CLASS,
+ V4L2_CID_BRIGHTNESS,
+ V4L2_CID_CONTRAST,
+ V4L2_CID_SATURATION,
+ V4L2_CID_HUE,
+ V4L2_CID_AUDIO_VOLUME,
+ V4L2_CID_AUDIO_BALANCE,
+ V4L2_CID_AUDIO_BASS,
+ V4L2_CID_AUDIO_TREBLE,
+ V4L2_CID_AUDIO_MUTE,
+ V4L2_CID_AUDIO_LOUDNESS,
+ 0
+};
+
+static const u32 *ctrl_classes[] = {
+ user_ctrls,
+ cx2341x_mpeg_ctrls,
+ NULL
+};
+
+static int cx18_queryctrl(struct cx18 *cx, struct v4l2_queryctrl *qctrl)
+{
+ const char *name;
+
+ CX18_DEBUG_IOCTL("VIDIOC_QUERYCTRL(%08x)\n", qctrl->id);
+
+ qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+ if (qctrl->id == 0)
+ return -EINVAL;
+
+ switch (qctrl->id) {
+ /* Standard V4L2 controls */
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_HUE:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_CONTRAST:
+ if (cx18_av_cmd(cx, VIDIOC_QUERYCTRL, qctrl))
+ qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+ return 0;
+
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_AUDIO_LOUDNESS:
+ if (cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl))
+ qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+ return 0;
+
+ default:
+ if (cx2341x_ctrl_query(&cx->params, qctrl))
+ qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+ return 0;
+ }
+ strncpy(qctrl->name, name, sizeof(qctrl->name) - 1);
+ qctrl->name[sizeof(qctrl->name) - 1] = 0;
+ return 0;
+}
+
+static int cx18_querymenu(struct cx18 *cx, struct v4l2_querymenu *qmenu)
+{
+ struct v4l2_queryctrl qctrl;
+
+ qctrl.id = qmenu->id;
+ cx18_queryctrl(cx, &qctrl);
+ return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id));
+}
+
+static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
+{
+ s32 v = vctrl->value;
+
+ CX18_DEBUG_IOCTL("VIDIOC_S_CTRL(%08x, %x)\n", vctrl->id, v);
+
+ switch (vctrl->id) {
+ /* Standard V4L2 controls */
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_HUE:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_CONTRAST:
+ return cx18_av_cmd(cx, VIDIOC_S_CTRL, vctrl);
+
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_AUDIO_LOUDNESS:
+ return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl);
+
+ default:
+ CX18_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
+{
+ CX18_DEBUG_IOCTL("VIDIOC_G_CTRL(%08x)\n", vctrl->id);
+
+ switch (vctrl->id) {
+ /* Standard V4L2 controls */
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_HUE:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_CONTRAST:
+ return cx18_av_cmd(cx, VIDIOC_G_CTRL, vctrl);
+
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_AUDIO_LOUDNESS:
+ return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl);
+ default:
+ CX18_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cx18_setup_vbi_fmt(struct cx18 *cx, enum v4l2_mpeg_stream_vbi_fmt fmt)
+{
+ if (!(cx->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
+ return -EINVAL;
+ if (atomic_read(&cx->capturing) > 0)
+ return -EBUSY;
+
+ /* First try to allocate sliced VBI buffers if needed. */
+ if (fmt && cx->vbi.sliced_mpeg_data[0] == NULL) {
+ int i;
+
+ for (i = 0; i < CX18_VBI_FRAMES; i++) {
+ /* Yuck, hardcoded. Needs to be a define */
+ cx->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL);
+ if (cx->vbi.sliced_mpeg_data[i] == NULL) {
+ while (--i >= 0) {
+ kfree(cx->vbi.sliced_mpeg_data[i]);
+ cx->vbi.sliced_mpeg_data[i] = NULL;
+ }
+ return -ENOMEM;
+ }
+ }
+ }
+
+ cx->vbi.insert_mpeg = fmt;
+
+ if (cx->vbi.insert_mpeg == 0)
+ return 0;
+ /* Need sliced data for mpeg insertion */
+ if (cx18_get_service_set(cx->vbi.sliced_in) == 0) {
+ if (cx->is_60hz)
+ cx->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525;
+ else
+ cx->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625;
+ cx18_expand_service_set(cx->vbi.sliced_in, cx->is_50hz);
+ }
+ return 0;
+}
+
+int cx18_control_ioctls(struct cx18 *cx, unsigned int cmd, void *arg)
+{
+ struct v4l2_control ctrl;
+
+ switch (cmd) {
+ case VIDIOC_QUERYMENU:
+ CX18_DEBUG_IOCTL("VIDIOC_QUERYMENU\n");
+ return cx18_querymenu(cx, arg);
+
+ case VIDIOC_QUERYCTRL:
+ return cx18_queryctrl(cx, arg);
+
+ case VIDIOC_S_CTRL:
+ return cx18_s_ctrl(cx, arg);
+
+ case VIDIOC_G_CTRL:
+ return cx18_g_ctrl(cx, arg);
+
+ case VIDIOC_S_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *c = arg;
+
+ if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
+ int i;
+ int err = 0;
+
+ for (i = 0; i < c->count; i++) {
+ ctrl.id = c->controls[i].id;
+ ctrl.value = c->controls[i].value;
+ err = cx18_s_ctrl(cx, &ctrl);
+ c->controls[i].value = ctrl.value;
+ if (err) {
+ c->error_idx = i;
+ break;
+ }
+ }
+ return err;
+ }
+ CX18_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n");
+ if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ struct cx2341x_mpeg_params p = cx->params;
+ int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->capturing), arg, cmd);
+
+ if (err)
+ return err;
+
+ if (p.video_encoding != cx->params.video_encoding) {
+ int is_mpeg1 = p.video_encoding ==
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
+ struct v4l2_format fmt;
+
+ /* fix videodecoder resolution */
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt.fmt.pix.width = cx->params.width / (is_mpeg1 ? 2 : 1);
+ fmt.fmt.pix.height = cx->params.height;
+ cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt);
+ }
+ err = cx2341x_update(cx, cx18_api_func, &cx->params, &p);
+ if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt)
+ err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt);
+ cx->params = p;
+ cx->dualwatch_stereo_mode = p.audio_properties & 0x0300;
+ cx18_audio_set_audio_clock_freq(cx, p.audio_properties & 0x03);
+ return err;
+ }
+ return -EINVAL;
+ }
+
+ case VIDIOC_G_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *c = arg;
+
+ if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
+ int i;
+ int err = 0;
+
+ for (i = 0; i < c->count; i++) {
+ ctrl.id = c->controls[i].id;
+ ctrl.value = c->controls[i].value;
+ err = cx18_g_ctrl(cx, &ctrl);
+ c->controls[i].value = ctrl.value;
+ if (err) {
+ c->error_idx = i;
+ break;
+ }
+ }
+ return err;
+ }
+ CX18_DEBUG_IOCTL("VIDIOC_G_EXT_CTRLS\n");
+ if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
+ return cx2341x_ext_ctrls(&cx->params, 0, arg, cmd);
+ return -EINVAL;
+ }
+
+ case VIDIOC_TRY_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *c = arg;
+
+ CX18_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n");
+ if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
+ return cx2341x_ext_ctrls(&cx->params,
+ atomic_read(&cx->capturing), arg, cmd);
+ return -EINVAL;
+ }
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-controls.h b/drivers/media/video/cx18/cx18-controls.h
new file mode 100644
index 00000000000..6e985cf422a
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-controls.h
@@ -0,0 +1,24 @@
+/*
+ * cx18 ioctl control functions
+ *
+ * Derived from ivtv-controls.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ * 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
+ */
+
+int cx18_control_ioctls(struct cx18 *cx, unsigned int cmd, void *arg);
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
new file mode 100644
index 00000000000..9f31befc313
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -0,0 +1,971 @@
+/*
+ * cx18 driver initialization and card probing
+ *
+ * Derived from ivtv-driver.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-version.h"
+#include "cx18-cards.h"
+#include "cx18-i2c.h"
+#include "cx18-irq.h"
+#include "cx18-gpio.h"
+#include "cx18-firmware.h"
+#include "cx18-streams.h"
+#include "cx18-av-core.h"
+#include "cx18-scb.h"
+#include "cx18-mailbox.h"
+#include "cx18-ioctl.h"
+#include "tuner-xc2028.h"
+
+#include <media/tveeprom.h>
+
+
+/* var to keep track of the number of array elements in use */
+int cx18_cards_active;
+
+/* If you have already X v4l cards, then set this to X. This way
+ the device numbers stay matched. Example: you have a WinTV card
+ without radio and a Compro H900 with. Normally this would give a
+ video1 device together with a radio0 device for the Compro. By
+ setting this to 1 you ensure that radio0 is now also radio1. */
+int cx18_first_minor;
+
+/* Master variable for all cx18 info */
+struct cx18 *cx18_cards[CX18_MAX_CARDS];
+
+/* Protects cx18_cards_active */
+DEFINE_SPINLOCK(cx18_cards_lock);
+
+/* add your revision and whatnot here */
+static struct pci_device_id cx18_pci_tbl[] __devinitdata = {
+ {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, cx18_pci_tbl);
+
+/* Parameter declarations */
+static int cardtype[CX18_MAX_CARDS];
+static int tuner[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
+static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
+
+static int cardtype_c = 1;
+static int tuner_c = 1;
+static int radio_c = 1;
+static char pal[] = "--";
+static char secam[] = "--";
+static char ntsc[] = "-";
+
+/* Buffers */
+static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS;
+static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS;
+static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS;
+static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS;
+static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS;
+
+static int cx18_pci_latency = 1;
+
+int cx18_debug;
+
+module_param_array(tuner, int, &tuner_c, 0644);
+module_param_array(radio, bool, &radio_c, 0644);
+module_param_array(cardtype, int, &cardtype_c, 0644);
+module_param_string(pal, pal, sizeof(pal), 0644);
+module_param_string(secam, secam, sizeof(secam), 0644);
+module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
+module_param_named(debug, cx18_debug, int, 0644);
+module_param(cx18_pci_latency, int, 0644);
+module_param(cx18_first_minor, int, 0644);
+
+module_param(enc_mpg_buffers, int, 0644);
+module_param(enc_ts_buffers, int, 0644);
+module_param(enc_yuv_buffers, int, 0644);
+module_param(enc_vbi_buffers, int, 0644);
+module_param(enc_pcm_buffers, int, 0644);
+
+MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
+ "\t\t\tsee tuner.h for values");
+MODULE_PARM_DESC(radio,
+ "Enable or disable the radio. Use only if autodetection\n"
+ "\t\t\tfails. 0 = disable, 1 = enable");
+MODULE_PARM_DESC(cardtype,
+ "Only use this option if your card is not detected properly.\n"
+ "\t\tSpecify card type:\n"
+ "\t\t\t 1 = Hauppauge HVR 1600 (ESMT memory)\n"
+ "\t\t\t 2 = Hauppauge HVR 1600 (Samsung memory)\n"
+ "\t\t\t 3 = Compro VideoMate H900\n"
+ "\t\t\t 4 = Yuan MPC718\n"
+ "\t\t\t 0 = Autodetect (default)\n"
+ "\t\t\t-1 = Ignore this card\n\t\t");
+MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60");
+MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC");
+MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K");
+MODULE_PARM_DESC(debug,
+ "Debug level (bitmask). Default: 0\n"
+ "\t\t\t 1/0x0001: warning\n"
+ "\t\t\t 2/0x0002: info\n"
+ "\t\t\t 4/0x0004: mailbox\n"
+ "\t\t\t 8/0x0008: dma\n"
+ "\t\t\t 16/0x0010: ioctl\n"
+ "\t\t\t 32/0x0020: file\n"
+ "\t\t\t 64/0x0040: i2c\n"
+ "\t\t\t128/0x0080: irq\n"
+ "\t\t\t256/0x0100: high volume\n");
+MODULE_PARM_DESC(cx18_pci_latency,
+ "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
+ "\t\t\tDefault: Yes");
+MODULE_PARM_DESC(enc_mpg_buffers,
+ "Encoder MPG Buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
+MODULE_PARM_DESC(enc_ts_buffers,
+ "Encoder TS Buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS));
+MODULE_PARM_DESC(enc_yuv_buffers,
+ "Encoder YUV Buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
+MODULE_PARM_DESC(enc_vbi_buffers,
+ "Encoder VBI Buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS));
+MODULE_PARM_DESC(enc_pcm_buffers,
+ "Encoder PCM buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));
+
+MODULE_PARM_DESC(cx18_first_minor, "Set minor assigned to first card");
+
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_DESCRIPTION("CX23418 driver");
+MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder");
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(CX18_VERSION);
+
+int cx18_waitq(wait_queue_head_t *waitq)
+{
+ DEFINE_WAIT(wait);
+
+ prepare_to_wait(waitq, &wait, TASK_INTERRUPTIBLE);
+ schedule();
+ finish_wait(waitq, &wait);
+ return signal_pending(current) ? -EINTR : 0;
+}
+
+/* Generic utility functions */
+int cx18_msleep_timeout(unsigned int msecs, int intr)
+{
+ int timeout = msecs_to_jiffies(msecs);
+ int sig;
+
+ do {
+ set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+ timeout = schedule_timeout(timeout);
+ sig = intr ? signal_pending(current) : 0;
+ } while (!sig && timeout);
+ return sig;
+}
+
+/* Release ioremapped memory */
+static void cx18_iounmap(struct cx18 *cx)
+{
+ if (cx == NULL)
+ return;
+
+ /* Release io memory */
+ if (cx->enc_mem != NULL) {
+ CX18_DEBUG_INFO("releasing enc_mem\n");
+ iounmap(cx->enc_mem);
+ cx->enc_mem = NULL;
+ }
+}
+
+/* Hauppauge card? get values from tveeprom */
+void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
+{
+ u8 eedata[256];
+
+ cx->i2c_client[0].addr = 0xA0 >> 1;
+ tveeprom_read(&cx->i2c_client[0], eedata, sizeof(eedata));
+ tveeprom_hauppauge_analog(&cx->i2c_client[0], tv, eedata);
+}
+
+static void cx18_process_eeprom(struct cx18 *cx)
+{
+ struct tveeprom tv;
+
+ cx18_read_eeprom(cx, &tv);
+
+ /* Many thanks to Steven Toth from Hauppauge for providing the
+ model numbers */
+ switch (tv.model) {
+ case 74000 ... 74099:
+ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+ break;
+ case 74700 ... 74799:
+ cx->card = cx18_get_card(CX18_CARD_HVR_1600_SAMSUNG);
+ break;
+ case 0:
+ CX18_ERR("Invalid EEPROM\n");
+ return;
+ default:
+ CX18_ERR("Unknown model %d, defaulting to HVR-1600\n", tv.model);
+ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+ break;
+ }
+
+ cx->v4l2_cap = cx->card->v4l2_capabilities;
+ cx->card_name = cx->card->name;
+ cx->card_i2c = cx->card->i2c;
+
+ CX18_INFO("Autodetected %s\n", cx->card_name);
+
+ if (tv.tuner_type == TUNER_ABSENT)
+ CX18_ERR("tveeprom cannot autodetect tuner!");
+
+ if (cx->options.tuner == -1)
+ cx->options.tuner = tv.tuner_type;
+ if (cx->options.radio == -1)
+ cx->options.radio = (tv.has_radio != 0);
+
+ if (cx->std != 0)
+ /* user specified tuner standard */
+ return;
+
+ /* autodetect tuner standard */
+ if (tv.tuner_formats & V4L2_STD_PAL) {
+ CX18_DEBUG_INFO("PAL tuner detected\n");
+ cx->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+ } else if (tv.tuner_formats & V4L2_STD_NTSC) {
+ CX18_DEBUG_INFO("NTSC tuner detected\n");
+ cx->std |= V4L2_STD_NTSC_M;
+ } else if (tv.tuner_formats & V4L2_STD_SECAM) {
+ CX18_DEBUG_INFO("SECAM tuner detected\n");
+ cx->std |= V4L2_STD_SECAM_L;
+ } else {
+ CX18_INFO("No tuner detected, default to NTSC-M\n");
+ cx->std |= V4L2_STD_NTSC_M;
+ }
+}
+
+static v4l2_std_id cx18_parse_std(struct cx18 *cx)
+{
+ switch (pal[0]) {
+ case '6':
+ return V4L2_STD_PAL_60;
+ case 'b':
+ case 'B':
+ case 'g':
+ case 'G':
+ return V4L2_STD_PAL_BG;
+ case 'h':
+ case 'H':
+ return V4L2_STD_PAL_H;
+ case 'n':
+ case 'N':
+ if (pal[1] == 'c' || pal[1] == 'C')
+ return V4L2_STD_PAL_Nc;
+ return V4L2_STD_PAL_N;
+ case 'i':
+ case 'I':
+ return V4L2_STD_PAL_I;
+ case 'd':
+ case 'D':
+ case 'k':
+ case 'K':
+ return V4L2_STD_PAL_DK;
+ case 'M':
+ case 'm':
+ return V4L2_STD_PAL_M;
+ case '-':
+ break;
+ default:
+ CX18_WARN("pal= argument not recognised\n");
+ return 0;
+ }
+
+ switch (secam[0]) {
+ case 'b':
+ case 'B':
+ case 'g':
+ case 'G':
+ case 'h':
+ case 'H':
+ return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
+ case 'd':
+ case 'D':
+ case 'k':
+ case 'K':
+ return V4L2_STD_SECAM_DK;
+ case 'l':
+ case 'L':
+ if (secam[1] == 'C' || secam[1] == 'c')
+ return V4L2_STD_SECAM_LC;
+ return V4L2_STD_SECAM_L;
+ case '-':
+ break;
+ default:
+ CX18_WARN("secam= argument not recognised\n");
+ return 0;
+ }
+
+ switch (ntsc[0]) {
+ case 'm':
+ case 'M':
+ return V4L2_STD_NTSC_M;
+ case 'j':
+ case 'J':
+ return V4L2_STD_NTSC_M_JP;
+ case 'k':
+ case 'K':
+ return V4L2_STD_NTSC_M_KR;
+ case '-':
+ break;
+ default:
+ CX18_WARN("ntsc= argument not recognised\n");
+ return 0;
+ }
+
+ /* no match found */
+ return 0;
+}
+
+static void cx18_process_options(struct cx18 *cx)
+{
+ int i, j;
+
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
+ cx->options.cardtype = cardtype[cx->num];
+ cx->options.tuner = tuner[cx->num];
+ cx->options.radio = radio[cx->num];
+
+ cx->std = cx18_parse_std(cx);
+ if (cx->options.cardtype == -1) {
+ CX18_INFO("Ignore card\n");
+ return;
+ }
+ cx->card = cx18_get_card(cx->options.cardtype - 1);
+ if (cx->card)
+ CX18_INFO("User specified %s card\n", cx->card->name);
+ else if (cx->options.cardtype != 0)
+ CX18_ERR("Unknown user specified type, trying to autodetect card\n");
+ if (cx->card == NULL) {
+ if (cx->dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) {
+ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+ CX18_INFO("Autodetected Hauppauge card\n");
+ }
+ }
+ if (cx->card == NULL) {
+ for (i = 0; (cx->card = cx18_get_card(i)); i++) {
+ if (cx->card->pci_list == NULL)
+ continue;
+ for (j = 0; cx->card->pci_list[j].device; j++) {
+ if (cx->dev->device !=
+ cx->card->pci_list[j].device)
+ continue;
+ if (cx->dev->subsystem_vendor !=
+ cx->card->pci_list[j].subsystem_vendor)
+ continue;
+ if (cx->dev->subsystem_device !=
+ cx->card->pci_list[j].subsystem_device)
+ continue;
+ CX18_INFO("Autodetected %s card\n", cx->card->name);
+ goto done;
+ }
+ }
+ }
+done:
+
+ if (cx->card == NULL) {
+ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+ CX18_ERR("Unknown card: vendor/device: %04x/%04x\n",
+ cx->dev->vendor, cx->dev->device);
+ CX18_ERR(" subsystem vendor/device: %04x/%04x\n",
+ cx->dev->subsystem_vendor, cx->dev->subsystem_device);
+ CX18_ERR("Defaulting to %s card\n", cx->card->name);
+ CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
+ CX18_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
+ CX18_ERR("Prefix your subject line with [UNKNOWN CX18 CARD].\n");
+ }
+ cx->v4l2_cap = cx->card->v4l2_capabilities;
+ cx->card_name = cx->card->name;
+ cx->card_i2c = cx->card->i2c;
+}
+
+/* Precondition: the cx18 structure has been memset to 0. Only
+ the dev and num fields have been filled in.
+ No assumptions on the card type may be made here (see cx18_init_struct2
+ for that).
+ */
+static int __devinit cx18_init_struct1(struct cx18 *cx)
+{
+ cx->base_addr = pci_resource_start(cx->dev, 0);
+
+ mutex_init(&cx->serialize_lock);
+ mutex_init(&cx->i2c_bus_lock[0]);
+ mutex_init(&cx->i2c_bus_lock[1]);
+
+ spin_lock_init(&cx->lock);
+ spin_lock_init(&cx->dma_reg_lock);
+
+ /* start counting open_id at 1 */
+ cx->open_id = 1;
+
+ /* Initial settings */
+ cx2341x_fill_defaults(&cx->params);
+ cx->temporal_strength = cx->params.video_temporal_filter;
+ cx->spatial_strength = cx->params.video_spatial_filter;
+ cx->filter_mode = cx->params.video_spatial_filter_mode |
+ (cx->params.video_temporal_filter_mode << 1) |
+ (cx->params.video_median_filter_type << 2);
+ cx->params.port = CX2341X_PORT_MEMORY;
+ cx->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
+ init_waitqueue_head(&cx->cap_w);
+ init_waitqueue_head(&cx->mb_apu_waitq);
+ init_waitqueue_head(&cx->mb_cpu_waitq);
+ init_waitqueue_head(&cx->mb_epu_waitq);
+ init_waitqueue_head(&cx->mb_hpu_waitq);
+ init_waitqueue_head(&cx->dma_waitq);
+
+ /* VBI */
+ cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+ cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
+ cx->vbi.raw_size = 1456;
+ cx->vbi.raw_decoder_line_size = 1456;
+ cx->vbi.raw_decoder_sav_odd_field = 0x20;
+ cx->vbi.raw_decoder_sav_even_field = 0x60;
+ cx->vbi.sliced_decoder_line_size = 272;
+ cx->vbi.sliced_decoder_sav_odd_field = 0xB0;
+ cx->vbi.sliced_decoder_sav_even_field = 0xF0;
+ return 0;
+}
+
+/* Second initialization part. Here the card type has been
+ autodetected. */
+static void __devinit cx18_init_struct2(struct cx18 *cx)
+{
+ int i;
+
+ for (i = 0; i < CX18_CARD_MAX_VIDEO_INPUTS; i++)
+ if (cx->card->video_inputs[i].video_type == 0)
+ break;
+ cx->nof_inputs = i;
+ for (i = 0; i < CX18_CARD_MAX_AUDIO_INPUTS; i++)
+ if (cx->card->audio_inputs[i].audio_type == 0)
+ break;
+ cx->nof_audio_inputs = i;
+
+ /* Find tuner input */
+ for (i = 0; i < cx->nof_inputs; i++) {
+ if (cx->card->video_inputs[i].video_type ==
+ CX18_CARD_INPUT_VID_TUNER)
+ break;
+ }
+ if (i == cx->nof_inputs)
+ i = 0;
+ cx->active_input = i;
+ cx->audio_input = cx->card->video_inputs[i].audio_index;
+ cx->av_state.vid_input = CX18_AV_COMPOSITE7;
+ cx->av_state.aud_input = CX18_AV_AUDIO8;
+ cx->av_state.audclk_freq = 48000;
+ cx->av_state.audmode = V4L2_TUNER_MODE_LANG1;
+ cx->av_state.vbi_line_offset = 8;
+}
+
+static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *dev,
+ const struct pci_device_id *pci_id)
+{
+ u16 cmd;
+ unsigned char pci_latency;
+
+ CX18_DEBUG_INFO("Enabling pci device\n");
+
+ if (pci_enable_device(dev)) {
+ CX18_ERR("Can't enable device %d!\n", cx->num);
+ return -EIO;
+ }
+ if (pci_set_dma_mask(dev, 0xffffffff)) {
+ CX18_ERR("No suitable DMA available on card %d.\n", cx->num);
+ return -EIO;
+ }
+ if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) {
+ CX18_ERR("Cannot request encoder memory region on card %d.\n", cx->num);
+ return -EIO;
+ }
+
+ /* Check for bus mastering */
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+
+ pci_read_config_byte(dev, PCI_CLASS_REVISION, &cx->card_rev);
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
+
+ if (pci_latency < 64 && cx18_pci_latency) {
+ CX18_INFO("Unreasonably low latency timer, "
+ "setting to 64 (was %d)\n", pci_latency);
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
+ }
+ /* This config space value relates to DMA latencies. The
+ default value 0x8080 is too low however and will lead
+ to DMA errors. 0xffff is the max value which solves
+ these problems. */
+ pci_write_config_dword(dev, 0x40, 0xffff);
+
+ CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, "
+ "irq: %d, latency: %d, memory: 0x%lx\n",
+ cx->dev->device, cx->card_rev, dev->bus->number,
+ PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
+ cx->dev->irq, pci_latency, (unsigned long)cx->base_addr);
+
+ return 0;
+}
+
+static u32 cx18_request_module(struct cx18 *cx, u32 hw,
+ const char *name, u32 id)
+{
+ if ((hw & id) == 0)
+ return hw;
+ if (request_module(name) != 0) {
+ CX18_ERR("Failed to load module %s\n", name);
+ return hw & ~id;
+ }
+ CX18_DEBUG_INFO("Loaded module %s\n", name);
+ return hw;
+}
+
+static void cx18_load_and_init_modules(struct cx18 *cx)
+{
+ u32 hw = cx->card->hw_all;
+ int i;
+
+ /* load modules */
+#ifndef CONFIG_VIDEO_TUNER
+ hw = cx18_request_module(cx, hw, "tuner", CX18_HW_TUNER);
+#endif
+#ifndef CONFIG_VIDEO_CS5345
+ hw = cx18_request_module(cx, hw, "cs5345", CX18_HW_CS5345);
+#endif
+
+ /* check which i2c devices are actually found */
+ for (i = 0; i < 32; i++) {
+ u32 device = 1 << i;
+
+ if (!(device & hw))
+ continue;
+ if (device == CX18_HW_GPIO || device == CX18_HW_TVEEPROM ||
+ device == CX18_HW_CX23418 || device == CX18_HW_DVB) {
+ /* These 'devices' do not use i2c probing */
+ cx->hw_flags |= device;
+ continue;
+ }
+ cx18_i2c_register(cx, i);
+ if (cx18_i2c_hw_addr(cx, device) > 0)
+ cx->hw_flags |= device;
+ }
+
+ hw = cx->hw_flags;
+}
+
+static int __devinit cx18_probe(struct pci_dev *dev,
+ const struct pci_device_id *pci_id)
+{
+ int retval = 0;
+ int vbi_buf_size;
+ u32 devtype;
+ struct cx18 *cx;
+
+ spin_lock(&cx18_cards_lock);
+
+ /* Make sure we've got a place for this card */
+ if (cx18_cards_active == CX18_MAX_CARDS) {
+ printk(KERN_ERR "cx18: Maximum number of cards detected (%d).\n",
+ cx18_cards_active);
+ spin_unlock(&cx18_cards_lock);
+ return -ENOMEM;
+ }
+
+ cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC);
+ if (cx == 0) {
+ spin_unlock(&cx18_cards_lock);
+ return -ENOMEM;
+ }
+ cx18_cards[cx18_cards_active] = cx;
+ cx->dev = dev;
+ cx->num = cx18_cards_active++;
+ snprintf(cx->name, sizeof(cx->name) - 1, "cx18-%d", cx->num);
+ CX18_INFO("Initializing card #%d\n", cx->num);
+
+ spin_unlock(&cx18_cards_lock);
+
+ cx18_process_options(cx);
+ if (cx->options.cardtype == -1) {
+ retval = -ENODEV;
+ goto err;
+ }
+ if (cx18_init_struct1(cx)) {
+ retval = -ENOMEM;
+ goto err;
+ }
+
+ CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr);
+
+ /* PCI Device Setup */
+ retval = cx18_setup_pci(cx, dev, pci_id);
+ if (retval != 0) {
+ if (retval == -EIO)
+ goto free_workqueue;
+ else if (retval == -ENXIO)
+ goto free_mem;
+ }
+ /* save cx in the pci struct for later use */
+ pci_set_drvdata(dev, cx);
+
+ /* map io memory */
+ CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
+ cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE);
+ cx->enc_mem = ioremap_nocache(cx->base_addr + CX18_MEM_OFFSET,
+ CX18_MEM_SIZE);
+ if (!cx->enc_mem) {
+ CX18_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n");
+ CX18_ERR("or disabling CONFIG_HIGHMEM4G into the kernel would help\n");
+ retval = -ENOMEM;
+ goto free_mem;
+ }
+ cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET;
+ devtype = read_reg(0xC72028);
+ switch (devtype & 0xff000000) {
+ case 0xff000000:
+ CX18_INFO("cx23418 revision %08x (A)\n", devtype);
+ break;
+ case 0x01000000:
+ CX18_INFO("cx23418 revision %08x (B)\n", devtype);
+ break;
+ default:
+ CX18_INFO("cx23418 revision %08x (Unknown)\n", devtype);
+ break;
+ }
+
+ cx18_init_power(cx, 1);
+ cx18_init_memory(cx);
+
+ cx->scb = (struct cx18_scb *)(cx->enc_mem + SCB_OFFSET);
+ cx18_init_scb(cx);
+
+ cx18_gpio_init(cx);
+
+ /* active i2c */
+ CX18_DEBUG_INFO("activating i2c...\n");
+ if (init_cx18_i2c(cx)) {
+ CX18_ERR("Could not initialize i2c\n");
+ goto free_map;
+ }
+
+ CX18_DEBUG_INFO("Active card count: %d.\n", cx18_cards_active);
+
+ if (cx->card->hw_all & CX18_HW_TVEEPROM) {
+ /* Based on the model number the cardtype may be changed.
+ The PCI IDs are not always reliable. */
+ cx18_process_eeprom(cx);
+ }
+ if (cx->card->comment)
+ CX18_INFO("%s", cx->card->comment);
+ if (cx->card->v4l2_capabilities == 0) {
+ retval = -ENODEV;
+ goto free_i2c;
+ }
+ cx18_init_memory(cx);
+
+ /* Register IRQ */
+ retval = request_irq(cx->dev->irq, cx18_irq_handler,
+ IRQF_SHARED | IRQF_DISABLED, cx->name, (void *)cx);
+ if (retval) {
+ CX18_ERR("Failed to register irq %d\n", retval);
+ goto free_i2c;
+ }
+
+ if (cx->std == 0)
+ cx->std = V4L2_STD_NTSC_M;
+
+ if (cx->options.tuner == -1) {
+ int i;
+
+ for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) {
+ if ((cx->std & cx->card->tuners[i].std) == 0)
+ continue;
+ cx->options.tuner = cx->card->tuners[i].tuner;
+ break;
+ }
+ }
+ /* if no tuner was found, then pick the first tuner in the card list */
+ if (cx->options.tuner == -1 && cx->card->tuners[0].std) {
+ cx->std = cx->card->tuners[0].std;
+ cx->options.tuner = cx->card->tuners[0].tuner;
+ }
+ if (cx->options.radio == -1)
+ cx->options.radio = (cx->card->radio_input.audio_type != 0);
+
+ /* The card is now fully identified, continue with card-specific
+ initialization. */
+ cx18_init_struct2(cx);
+
+ cx18_load_and_init_modules(cx);
+
+ if (cx->std & V4L2_STD_525_60) {
+ cx->is_60hz = 1;
+ cx->is_out_60hz = 1;
+ } else {
+ cx->is_50hz = 1;
+ cx->is_out_50hz = 1;
+ }
+ cx->params.video_gop_size = cx->is_60hz ? 15 : 12;
+
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = 0x08000;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = 0x08000;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = 0x01200;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = 0x20000;
+ vbi_buf_size = cx->vbi.raw_size * (cx->is_60hz ? 24 : 36) / 2;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_buf_size;
+
+ if (cx->options.radio > 0)
+ cx->v4l2_cap |= V4L2_CAP_RADIO;
+
+ retval = cx18_streams_setup(cx);
+ if (retval) {
+ CX18_ERR("Error %d setting up streams\n", retval);
+ goto free_irq;
+ }
+ retval = cx18_streams_register(cx);
+ if (retval) {
+ CX18_ERR("Error %d registering devices\n", retval);
+ goto free_streams;
+ }
+
+ if (cx->options.tuner > -1) {
+ struct tuner_setup setup;
+
+ setup.addr = ADDR_UNSET;
+ setup.type = cx->options.tuner;
+ setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
+ setup.tuner_callback = (setup.type == TUNER_XC2028) ?
+ cx18_reset_tuner_gpio : NULL;
+ cx18_call_i2c_clients(cx, TUNER_SET_TYPE_ADDR, &setup);
+ if (setup.type == TUNER_XC2028) {
+ static struct xc2028_ctrl ctrl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ };
+ struct v4l2_priv_tun_config cfg = {
+ .tuner = cx->options.tuner,
+ .priv = &ctrl,
+ };
+ cx18_call_i2c_clients(cx, TUNER_SET_CONFIG, &cfg);
+ }
+ }
+
+ /* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
+ are not. */
+ cx->tuner_std = cx->std;
+
+ cx18_init_on_first_open(cx);
+
+ CX18_INFO("Initialized card #%d: %s\n", cx->num, cx->card_name);
+
+ return 0;
+
+free_streams:
+ cx18_streams_cleanup(cx);
+free_irq:
+ free_irq(cx->dev->irq, (void *)cx);
+free_i2c:
+ exit_cx18_i2c(cx);
+free_map:
+ cx18_iounmap(cx);
+free_mem:
+ release_mem_region(cx->base_addr, CX18_MEM_SIZE);
+free_workqueue:
+err:
+ if (retval == 0)
+ retval = -ENODEV;
+ CX18_ERR("Error %d on initialization\n", retval);
+
+ kfree(cx18_cards[cx18_cards_active]);
+ cx18_cards[cx18_cards_active] = NULL;
+ return retval;
+}
+
+int cx18_init_on_first_open(struct cx18 *cx)
+{
+ int video_input;
+ int fw_retry_count = 3;
+ struct v4l2_frequency vf;
+
+ if (test_bit(CX18_F_I_FAILED, &cx->i_flags))
+ return -ENXIO;
+
+ if (test_and_set_bit(CX18_F_I_INITED, &cx->i_flags))
+ return 0;
+
+ while (--fw_retry_count > 0) {
+ /* load firmware */
+ if (cx18_firmware_init(cx) == 0)
+ break;
+ if (fw_retry_count > 1)
+ CX18_WARN("Retry loading firmware\n");
+ }
+
+ if (fw_retry_count == 0) {
+ set_bit(CX18_F_I_FAILED, &cx->i_flags);
+ return -ENXIO;
+ }
+ set_bit(CX18_F_I_LOADED_FW, &cx->i_flags);
+
+ /* Init the firmware twice to work around a silicon bug
+ * transport related. */
+
+ fw_retry_count = 3;
+ while (--fw_retry_count > 0) {
+ /* load firmware */
+ if (cx18_firmware_init(cx) == 0)
+ break;
+ if (fw_retry_count > 1)
+ CX18_WARN("Retry loading firmware\n");
+ }
+
+ if (fw_retry_count == 0) {
+ set_bit(CX18_F_I_FAILED, &cx->i_flags);
+ return -ENXIO;
+ }
+
+ vf.tuner = 0;
+ vf.type = V4L2_TUNER_ANALOG_TV;
+ vf.frequency = 6400; /* the tuner 'baseline' frequency */
+
+ /* Set initial frequency. For PAL/SECAM broadcasts no
+ 'default' channel exists AFAIK. */
+ if (cx->std == V4L2_STD_NTSC_M_JP)
+ vf.frequency = 1460; /* ch. 1 91250*16/1000 */
+ else if (cx->std & V4L2_STD_NTSC_M)
+ vf.frequency = 1076; /* ch. 4 67250*16/1000 */
+
+ video_input = cx->active_input;
+ cx->active_input++; /* Force update of input */
+ cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_INPUT, &video_input);
+
+ /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
+ in one place. */
+ cx->std++; /* Force full standard initialization */
+ cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_STD, &cx->tuner_std);
+ cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_FREQUENCY, &vf);
+ return 0;
+}
+
+static void cx18_remove(struct pci_dev *pci_dev)
+{
+ struct cx18 *cx = pci_get_drvdata(pci_dev);
+
+ CX18_DEBUG_INFO("Removing Card #%d\n", cx->num);
+
+ /* Stop all captures */
+ CX18_DEBUG_INFO("Stopping all streams\n");
+ if (atomic_read(&cx->capturing) > 0)
+ cx18_stop_all_captures(cx);
+
+ /* Interrupts */
+ sw1_irq_disable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
+ sw2_irq_disable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+
+ cx18_halt_firmware(cx);
+
+ cx18_streams_cleanup(cx);
+
+ exit_cx18_i2c(cx);
+
+ free_irq(cx->dev->irq, (void *)cx);
+
+ if (cx->dev)
+ cx18_iounmap(cx);
+
+ release_mem_region(cx->base_addr, CX18_MEM_SIZE);
+
+ pci_disable_device(cx->dev);
+
+ CX18_INFO("Removed %s, card #%d\n", cx->card_name, cx->num);
+}
+
+/* define a pci_driver for card detection */
+static struct pci_driver cx18_pci_driver = {
+ .name = "cx18",
+ .id_table = cx18_pci_tbl,
+ .probe = cx18_probe,
+ .remove = cx18_remove,
+};
+
+static int module_start(void)
+{
+ printk(KERN_INFO "cx18: Start initialization, version %s\n", CX18_VERSION);
+
+ memset(cx18_cards, 0, sizeof(cx18_cards));
+
+ /* Validate parameters */
+ if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) {
+ printk(KERN_ERR "cx18: Exiting, ivtv_first_minor must be between 0 and %d\n",
+ CX18_MAX_CARDS - 1);
+ return -1;
+ }
+
+ if (cx18_debug < 0 || cx18_debug > 511) {
+ cx18_debug = 0;
+ printk(KERN_INFO "cx18: Debug value must be >= 0 and <= 511!\n");
+ }
+
+ if (pci_register_driver(&cx18_pci_driver)) {
+ printk(KERN_ERR "cx18: Error detecting PCI card\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO "cx18: End initialization\n");
+ return 0;
+}
+
+static void module_cleanup(void)
+{
+ int i;
+
+ pci_unregister_driver(&cx18_pci_driver);
+
+ for (i = 0; i < cx18_cards_active; i++) {
+ if (cx18_cards[i] == NULL)
+ continue;
+ kfree(cx18_cards[i]);
+ }
+}
+
+module_init(module_start);
+module_exit(module_cleanup);
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
new file mode 100644
index 00000000000..2ee939193bb
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -0,0 +1,500 @@
+/*
+ * cx18 driver internal defines and structures
+ *
+ * Derived from ivtv-driver.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#ifndef CX18_DRIVER_H
+#define CX18_DRIVER_H
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/list.h>
+#include <linux/unistd.h>
+#include <linux/byteorder/swab.h>
+#include <linux/pagemap.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+
+#include <linux/dvb/video.h>
+#include <linux/dvb/audio.h>
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+#include "cx18-mailbox.h"
+#include "cx18-av-core.h"
+#include "cx23418.h"
+
+/* DVB */
+#include "demux.h"
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+#include "dvbdev.h"
+
+#ifndef CONFIG_PCI
+# error "This driver requires kernel PCI support."
+#endif
+
+#define CX18_MEM_OFFSET 0x00000000
+#define CX18_MEM_SIZE 0x04000000
+#define CX18_REG_OFFSET 0x02000000
+
+/* Maximum cx18 driver instances. */
+#define CX18_MAX_CARDS 32
+
+/* Supported cards */
+#define CX18_CARD_HVR_1600_ESMT 0 /* Hauppauge HVR 1600 (ESMT memory) */
+#define CX18_CARD_HVR_1600_SAMSUNG 1 /* Hauppauge HVR 1600 (Samsung memory) */
+#define CX18_CARD_COMPRO_H900 2 /* Compro VideoMate H900 */
+#define CX18_CARD_YUAN_MPC718 3 /* Yuan MPC718 */
+#define CX18_CARD_LAST 3
+
+#define CX18_ENC_STREAM_TYPE_MPG 0
+#define CX18_ENC_STREAM_TYPE_TS 1
+#define CX18_ENC_STREAM_TYPE_YUV 2
+#define CX18_ENC_STREAM_TYPE_VBI 3
+#define CX18_ENC_STREAM_TYPE_PCM 4
+#define CX18_ENC_STREAM_TYPE_IDX 5
+#define CX18_ENC_STREAM_TYPE_RAD 6
+#define CX18_MAX_STREAMS 7
+
+/* system vendor and device IDs */
+#define PCI_VENDOR_ID_CX 0x14f1
+#define PCI_DEVICE_ID_CX23418 0x5b7a
+
+/* subsystem vendor ID */
+#define CX18_PCI_ID_HAUPPAUGE 0x0070
+#define CX18_PCI_ID_COMPRO 0x185b
+#define CX18_PCI_ID_YUAN 0x12ab
+
+/* ======================================================================== */
+/* ========================== START USER SETTABLE DMA VARIABLES =========== */
+/* ======================================================================== */
+
+/* DMA Buffers, Default size in MB allocated */
+#define CX18_DEFAULT_ENC_TS_BUFFERS 1
+#define CX18_DEFAULT_ENC_MPG_BUFFERS 2
+#define CX18_DEFAULT_ENC_IDX_BUFFERS 1
+#define CX18_DEFAULT_ENC_YUV_BUFFERS 2
+#define CX18_DEFAULT_ENC_VBI_BUFFERS 1
+#define CX18_DEFAULT_ENC_PCM_BUFFERS 1
+
+/* i2c stuff */
+#define I2C_CLIENTS_MAX 16
+
+/* debugging */
+
+/* Flag to turn on high volume debugging */
+#define CX18_DBGFLG_WARN (1 << 0)
+#define CX18_DBGFLG_INFO (1 << 1)
+#define CX18_DBGFLG_API (1 << 2)
+#define CX18_DBGFLG_DMA (1 << 3)
+#define CX18_DBGFLG_IOCTL (1 << 4)
+#define CX18_DBGFLG_FILE (1 << 5)
+#define CX18_DBGFLG_I2C (1 << 6)
+#define CX18_DBGFLG_IRQ (1 << 7)
+/* Flag to turn on high volume debugging */
+#define CX18_DBGFLG_HIGHVOL (1 << 8)
+
+/* NOTE: extra space before comma in 'cx->num , ## args' is required for
+ gcc-2.95, otherwise it won't compile. */
+#define CX18_DEBUG(x, type, fmt, args...) \
+ do { \
+ if ((x) & cx18_debug) \
+ printk(KERN_INFO "cx18-%d " type ": " fmt, cx->num , ## args); \
+ } while (0)
+#define CX18_DEBUG_WARN(fmt, args...) CX18_DEBUG(CX18_DBGFLG_WARN, "warning", fmt , ## args)
+#define CX18_DEBUG_INFO(fmt, args...) CX18_DEBUG(CX18_DBGFLG_INFO, "info", fmt , ## args)
+#define CX18_DEBUG_API(fmt, args...) CX18_DEBUG(CX18_DBGFLG_API, "api", fmt , ## args)
+#define CX18_DEBUG_DMA(fmt, args...) CX18_DEBUG(CX18_DBGFLG_DMA, "dma", fmt , ## args)
+#define CX18_DEBUG_IOCTL(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define CX18_DEBUG_FILE(fmt, args...) CX18_DEBUG(CX18_DBGFLG_FILE, "file", fmt , ## args)
+#define CX18_DEBUG_I2C(fmt, args...) CX18_DEBUG(CX18_DBGFLG_I2C, "i2c", fmt , ## args)
+#define CX18_DEBUG_IRQ(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IRQ, "irq", fmt , ## args)
+
+#define CX18_DEBUG_HIGH_VOL(x, type, fmt, args...) \
+ do { \
+ if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \
+ printk(KERN_INFO "cx18%d " type ": " fmt, cx->num , ## args); \
+ } while (0)
+#define CX18_DEBUG_HI_WARN(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_WARN, "warning", fmt , ## args)
+#define CX18_DEBUG_HI_INFO(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_INFO, "info", fmt , ## args)
+#define CX18_DEBUG_HI_API(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_API, "api", fmt , ## args)
+#define CX18_DEBUG_HI_DMA(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_DMA, "dma", fmt , ## args)
+#define CX18_DEBUG_HI_IOCTL(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define CX18_DEBUG_HI_FILE(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_FILE, "file", fmt , ## args)
+#define CX18_DEBUG_HI_I2C(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_I2C, "i2c", fmt , ## args)
+#define CX18_DEBUG_HI_IRQ(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IRQ, "irq", fmt , ## args)
+
+/* Standard kernel messages */
+#define CX18_ERR(fmt, args...) printk(KERN_ERR "cx18-%d: " fmt, cx->num , ## args)
+#define CX18_WARN(fmt, args...) printk(KERN_WARNING "cx18-%d: " fmt, cx->num , ## args)
+#define CX18_INFO(fmt, args...) printk(KERN_INFO "cx18-%d: " fmt, cx->num , ## args)
+
+/* Values for CX18_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */
+#define MPEG_FRAME_TYPE_IFRAME 1
+#define MPEG_FRAME_TYPE_IFRAME_PFRAME 3
+#define MPEG_FRAME_TYPE_ALL 7
+
+#define CX18_MAX_PGM_INDEX (400)
+
+extern int cx18_debug;
+
+
+struct cx18_options {
+ int megabytes[CX18_MAX_STREAMS]; /* Size in megabytes of each stream */
+ int cardtype; /* force card type on load */
+ int tuner; /* set tuner on load */
+ int radio; /* enable/disable radio */
+};
+
+/* per-buffer bit flags */
+#define CX18_F_B_NEED_BUF_SWAP 0 /* this buffer should be byte swapped */
+
+/* per-stream, s_flags */
+#define CX18_F_S_CLAIMED 3 /* this stream is claimed */
+#define CX18_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */
+#define CX18_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */
+#define CX18_F_S_STREAMOFF 7 /* signal end of stream EOS */
+#define CX18_F_S_APPL_IO 8 /* this stream is used read/written by an application */
+
+/* per-cx18, i_flags */
+#define CX18_F_I_LOADED_FW 0 /* Loaded the firmware the first time */
+#define CX18_F_I_EOS 4 /* End of encoder stream reached */
+#define CX18_F_I_RADIO_USER 5 /* The radio tuner is selected */
+#define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */
+#define CX18_F_I_INITED 21 /* set after first open */
+#define CX18_F_I_FAILED 22 /* set if first open failed */
+
+/* These are the VBI types as they appear in the embedded VBI private packets. */
+#define CX18_SLICED_TYPE_TELETEXT_B (1)
+#define CX18_SLICED_TYPE_CAPTION_525 (4)
+#define CX18_SLICED_TYPE_WSS_625 (5)
+#define CX18_SLICED_TYPE_VPS (7)
+
+struct cx18_buffer {
+ struct list_head list;
+ dma_addr_t dma_handle;
+ u32 id;
+ unsigned long b_flags;
+ char *buf;
+
+ u32 bytesused;
+ u32 readpos;
+};
+
+struct cx18_queue {
+ struct list_head list;
+ u32 buffers;
+ u32 length;
+ u32 bytesused;
+};
+
+struct cx18_dvb {
+ struct dmx_frontend hw_frontend;
+ struct dmx_frontend mem_frontend;
+ struct dmxdev dmxdev;
+ struct dvb_adapter dvb_adapter;
+ struct dvb_demux demux;
+ struct dvb_frontend *fe;
+ struct dvb_net dvbnet;
+ int enabled;
+ int feeding;
+
+ struct mutex feedlock;
+
+};
+
+struct cx18; /* forward reference */
+struct cx18_scb; /* forward reference */
+
+struct cx18_stream {
+ /* These first four fields are always set, even if the stream
+ is not actually created. */
+ struct video_device *v4l2dev; /* NULL when stream not created */
+ struct cx18 *cx; /* for ease of use */
+ const char *name; /* name of the stream */
+ int type; /* stream type */
+ u32 handle; /* task handle */
+ unsigned mdl_offset;
+
+ u32 id;
+ spinlock_t qlock; /* locks access to the queues */
+ unsigned long s_flags; /* status flags, see above */
+ int dma; /* can be PCI_DMA_TODEVICE,
+ PCI_DMA_FROMDEVICE or
+ PCI_DMA_NONE */
+ u64 dma_pts;
+ wait_queue_head_t waitq;
+
+ /* Buffer Stats */
+ u32 buffers;
+ u32 buf_size;
+ u32 buffers_stolen;
+
+ /* Buffer Queues */
+ struct cx18_queue q_free; /* free buffers */
+ struct cx18_queue q_full; /* full buffers */
+ struct cx18_queue q_io; /* waiting for I/O */
+
+ /* DVB / Digital Transport */
+ struct cx18_dvb dvb;
+};
+
+struct cx18_open_id {
+ u32 open_id;
+ int type;
+ enum v4l2_priority prio;
+ struct cx18 *cx;
+};
+
+/* forward declaration of struct defined in cx18-cards.h */
+struct cx18_card;
+
+
+#define CX18_VBI_FRAMES 32
+
+/* VBI data */
+struct vbi_info {
+ u32 enc_size;
+ u32 frame;
+ u8 cc_data_odd[256];
+ u8 cc_data_even[256];
+ int cc_pos;
+ u8 cc_no_update;
+ u8 vps[5];
+ u8 vps_found;
+ int wss;
+ u8 wss_found;
+ u8 wss_no_update;
+ u32 raw_decoder_line_size;
+ u8 raw_decoder_sav_odd_field;
+ u8 raw_decoder_sav_even_field;
+ u32 sliced_decoder_line_size;
+ u8 sliced_decoder_sav_odd_field;
+ u8 sliced_decoder_sav_even_field;
+ struct v4l2_format in;
+ /* convenience pointer to sliced struct in vbi_in union */
+ struct v4l2_sliced_vbi_format *sliced_in;
+ u32 service_set_in;
+ int insert_mpeg;
+
+ /* Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines.
+ One for /dev/vbi0 and one for /dev/vbi8 */
+ struct v4l2_sliced_vbi_data sliced_data[36];
+
+ /* Buffer for VBI data inserted into MPEG stream.
+ The first byte is a dummy byte that's never used.
+ The next 16 bytes contain the MPEG header for the VBI data,
+ the remainder is the actual VBI data.
+ The max size accepted by the MPEG VBI reinsertion turns out
+ to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes,
+ where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is
+ a single line header byte and 2 * 18 is the number of VBI lines per frame.
+
+ However, it seems that the data must be 1K aligned, so we have to
+ pad the data until the 1 or 2 K boundary.
+
+ This pointer array will allocate 2049 bytes to store each VBI frame. */
+ u8 *sliced_mpeg_data[CX18_VBI_FRAMES];
+ u32 sliced_mpeg_size[CX18_VBI_FRAMES];
+ struct cx18_buffer sliced_mpeg_buf;
+ u32 inserted_frame;
+
+ u32 start[2], count;
+ u32 raw_size;
+ u32 sliced_size;
+};
+
+/* Per cx23418, per I2C bus private algo callback data */
+struct cx18_i2c_algo_callback_data {
+ struct cx18 *cx;
+ int bus_index; /* 0 or 1 for the cx23418's 1st or 2nd I2C bus */
+};
+
+/* Struct to hold info about cx18 cards */
+struct cx18 {
+ int num; /* board number, -1 during init! */
+ char name[8]; /* board name for printk and interrupts (e.g. 'cx180') */
+ struct pci_dev *dev; /* PCI device */
+ const struct cx18_card *card; /* card information */
+ const char *card_name; /* full name of the card */
+ const struct cx18_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */
+ u8 is_50hz;
+ u8 is_60hz;
+ u8 is_out_50hz;
+ u8 is_out_60hz;
+ u8 nof_inputs; /* number of video inputs */
+ u8 nof_audio_inputs; /* number of audio inputs */
+ u16 buffer_id; /* buffer ID counter */
+ u32 v4l2_cap; /* V4L2 capabilities of card */
+ u32 hw_flags; /* Hardware description of the board */
+ unsigned mdl_offset;
+ struct cx18_scb *scb; /* pointer to SCB */
+
+ struct cx18_av_state av_state;
+
+ /* codec settings */
+ struct cx2341x_mpeg_params params;
+ u32 filter_mode;
+ u32 temporal_strength;
+ u32 spatial_strength;
+
+ /* dualwatch */
+ unsigned long dualwatch_jiffies;
+ u16 dualwatch_stereo_mode;
+
+ /* Digitizer type */
+ int digitizer; /* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */
+
+ struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */
+ struct cx18_options options; /* User options */
+ int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */
+ struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */
+ unsigned long i_flags; /* global cx18 flags */
+ atomic_t capturing; /* count number of active capture streams */
+ spinlock_t lock; /* lock access to this struct */
+ int search_pack_header;
+
+ spinlock_t dma_reg_lock; /* lock access to DMA engine registers */
+
+ int open_id; /* incremented each time an open occurs, used as
+ unique ID. Starts at 1, so 0 can be used as
+ uninitialized value in the stream->id. */
+
+ u32 base_addr;
+ struct v4l2_prio_state prio;
+
+ u8 card_rev;
+ void __iomem *enc_mem, *reg_mem;
+
+ struct vbi_info vbi;
+
+ u32 pgm_info_offset;
+ u32 pgm_info_num;
+ u32 pgm_info_write_idx;
+ u32 pgm_info_read_idx;
+ struct v4l2_enc_idx_entry pgm_info[CX18_MAX_PGM_INDEX];
+
+ u64 mpg_data_received;
+ u64 vbi_data_inserted;
+
+ wait_queue_head_t mb_apu_waitq;
+ wait_queue_head_t mb_cpu_waitq;
+ wait_queue_head_t mb_epu_waitq;
+ wait_queue_head_t mb_hpu_waitq;
+ wait_queue_head_t cap_w;
+ /* when the current DMA is finished this queue is woken up */
+ wait_queue_head_t dma_waitq;
+
+ /* i2c */
+ struct i2c_adapter i2c_adap[2];
+ struct i2c_algo_bit_data i2c_algo[2];
+ struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2];
+ struct i2c_client i2c_client[2];
+ struct mutex i2c_bus_lock[2];
+ struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];
+
+ /* v4l2 and User settings */
+
+ /* codec settings */
+ u32 audio_input;
+ u32 active_input;
+ u32 active_output;
+ v4l2_std_id std;
+ v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */
+};
+
+/* Globals */
+extern struct cx18 *cx18_cards[];
+extern int cx18_cards_active;
+extern int cx18_first_minor;
+extern spinlock_t cx18_cards_lock;
+
+/*==============Prototypes==================*/
+
+/* Return non-zero if a signal is pending */
+int cx18_msleep_timeout(unsigned int msecs, int intr);
+
+/* Wait on queue, returns -EINTR if interrupted */
+int cx18_waitq(wait_queue_head_t *waitq);
+
+/* Read Hauppauge eeprom */
+struct tveeprom; /* forward reference */
+void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv);
+
+/* First-open initialization: load firmware, etc. */
+int cx18_init_on_first_open(struct cx18 *cx);
+
+/* This is a PCI post thing, where if the pci register is not read, then
+ the write doesn't always take effect right away. By reading back the
+ register any pending PCI writes will be performed (in order), and so
+ you can be sure that the writes are guaranteed to be done.
+
+ Rarely needed, only in some timing sensitive cases.
+ Apparently if this is not done some motherboards seem
+ to kill the firmware and get into the broken state until computer is
+ rebooted. */
+#define write_sync(val, reg) \
+ do { writel(val, reg); readl(reg); } while (0)
+
+#define read_reg(reg) readl(cx->reg_mem + (reg))
+#define write_reg(val, reg) writel(val, cx->reg_mem + (reg))
+#define write_reg_sync(val, reg) \
+ do { write_reg(val, reg); read_reg(reg); } while (0)
+
+#define read_enc(addr) readl(cx->enc_mem + (u32)(addr))
+#define write_enc(val, addr) writel(val, cx->enc_mem + (u32)(addr))
+#define write_enc_sync(val, addr) \
+ do { write_enc(val, addr); read_enc(addr); } while (0)
+
+#define sw1_irq_enable(val) do { \
+ write_reg(val, SW1_INT_STATUS); \
+ write_reg(read_reg(SW1_INT_ENABLE_PCI) | (val), SW1_INT_ENABLE_PCI); \
+} while (0)
+
+#define sw1_irq_disable(val) \
+ write_reg(read_reg(SW1_INT_ENABLE_PCI) & ~(val), SW1_INT_ENABLE_PCI);
+
+#define sw2_irq_enable(val) do { \
+ write_reg(val, SW2_INT_STATUS); \
+ write_reg(read_reg(SW2_INT_ENABLE_PCI) | (val), SW2_INT_ENABLE_PCI); \
+} while (0)
+
+#define sw2_irq_disable(val) \
+ write_reg(read_reg(SW2_INT_ENABLE_PCI) & ~(val), SW2_INT_ENABLE_PCI);
+
+#define setup_page(addr) do { \
+ u32 val = read_reg(0xD000F8) & ~0x1f00; \
+ write_reg(val | (((addr) >> 17) & 0x1f00), 0xD000F8); \
+} while (0)
+
+#endif /* CX18_DRIVER_H */
diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c
new file mode 100644
index 00000000000..65efe69d939
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-dvb.c
@@ -0,0 +1,288 @@
+/*
+ * cx18 functions for DVB support
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@hauppauge.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 "cx18-version.h"
+#include "cx18-dvb.h"
+#include "cx18-streams.h"
+#include "cx18-cards.h"
+#include "s5h1409.h"
+
+/* Wait until the MXL500X driver is merged */
+#ifdef HAVE_MXL500X
+#include "mxl500x.h"
+#endif
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000
+
+#ifdef HAVE_MXL500X
+static struct mxl500x_config hauppauge_hvr1600_tuner = {
+ .delsys = MXL500x_MODE_ATSC,
+ .octf = MXL500x_OCTF_CH,
+ .xtal_freq = 16000000,
+ .iflo_freq = 5380000,
+ .ref_freq = 322800000,
+ .rssi_ena = MXL_RSSI_ENABLE,
+ .addr = 0xC6 >> 1,
+};
+
+static struct s5h1409_config hauppauge_hvr1600_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_ON,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK
+
+};
+#endif
+
+static int dvb_register(struct cx18_stream *stream);
+
+/* Kernel DVB framework calls this when the feed needs to start.
+ * The CX18 framework should enable the transport DMA handling
+ * and queue processing.
+ */
+static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct cx18_stream *stream = (struct cx18_stream *) demux->priv;
+ struct cx18 *cx = stream->cx;
+ int ret = -EINVAL;
+ u32 v;
+
+ CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n",
+ feed->pid, feed->index);
+ switch (cx->card->type) {
+ case CX18_CARD_HVR_1600_ESMT:
+ case CX18_CARD_HVR_1600_SAMSUNG:
+ v = read_reg(CX18_REG_DMUX_NUM_PORT_0_CONTROL);
+ v |= 0x00400000; /* Serial Mode */
+ v |= 0x00002000; /* Data Length - Byte */
+ v |= 0x00010000; /* Error - Polarity */
+ v |= 0x00020000; /* Error - Passthru */
+ v |= 0x000c0000; /* Error - Ignore */
+ write_reg(v, CX18_REG_DMUX_NUM_PORT_0_CONTROL);
+ break;
+
+ default:
+ /* Assumption - Parallel transport - Signalling
+ * undefined or default.
+ */
+ break;
+ }
+
+ if (!demux->dmx.frontend)
+ return -EINVAL;
+
+ if (stream) {
+ mutex_lock(&stream->dvb.feedlock);
+ if (stream->dvb.feeding++ == 0) {
+ CX18_DEBUG_INFO("Starting Transport DMA\n");
+ ret = cx18_start_v4l2_encode_stream(stream);
+ } else
+ ret = 0;
+ mutex_unlock(&stream->dvb.feedlock);
+ }
+
+ return ret;
+}
+
+/* Kernel DVB framework calls this when the feed needs to stop. */
+static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct cx18_stream *stream = (struct cx18_stream *)demux->priv;
+ struct cx18 *cx = stream->cx;
+ int ret = -EINVAL;
+
+ CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n",
+ feed->pid, feed->index);
+
+ if (stream) {
+ mutex_lock(&stream->dvb.feedlock);
+ if (--stream->dvb.feeding == 0) {
+ CX18_DEBUG_INFO("Stopping Transport DMA\n");
+ ret = cx18_stop_v4l2_encode_stream(stream, 0);
+ } else
+ ret = 0;
+ mutex_unlock(&stream->dvb.feedlock);
+ }
+
+ return ret;
+}
+
+int cx18_dvb_register(struct cx18_stream *stream)
+{
+ struct cx18 *cx = stream->cx;
+ struct cx18_dvb *dvb = &stream->dvb;
+ struct dvb_adapter *dvb_adapter;
+ struct dvb_demux *dvbdemux;
+ struct dmx_demux *dmx;
+ int ret;
+
+ if (!dvb)
+ return -EINVAL;
+
+ ret = dvb_register_adapter(&dvb->dvb_adapter,
+ CX18_DRIVER_NAME,
+ THIS_MODULE, &cx->dev->dev, adapter_nr);
+ if (ret < 0)
+ goto err_out;
+
+ dvb_adapter = &dvb->dvb_adapter;
+
+ dvbdemux = &dvb->demux;
+
+ dvbdemux->priv = (void *)stream;
+
+ dvbdemux->filternum = 256;
+ dvbdemux->feednum = 256;
+ dvbdemux->start_feed = cx18_dvb_start_feed;
+ dvbdemux->stop_feed = cx18_dvb_stop_feed;
+ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
+ DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
+ ret = dvb_dmx_init(dvbdemux);
+ if (ret < 0)
+ goto err_dvb_unregister_adapter;
+
+ dmx = &dvbdemux->dmx;
+
+ dvb->hw_frontend.source = DMX_FRONTEND_0;
+ dvb->mem_frontend.source = DMX_MEMORY_FE;
+ dvb->dmxdev.filternum = 256;
+ dvb->dmxdev.demux = dmx;
+
+ ret = dvb_dmxdev_init(&dvb->dmxdev, dvb_adapter);
+ if (ret < 0)
+ goto err_dvb_dmx_release;
+
+ ret = dmx->add_frontend(dmx, &dvb->hw_frontend);
+ if (ret < 0)
+ goto err_dvb_dmxdev_release;
+
+ ret = dmx->add_frontend(dmx, &dvb->mem_frontend);
+ if (ret < 0)
+ goto err_remove_hw_frontend;
+
+ ret = dmx->connect_frontend(dmx, &dvb->hw_frontend);
+ if (ret < 0)
+ goto err_remove_mem_frontend;
+
+ ret = dvb_register(stream);
+ if (ret < 0)
+ goto err_disconnect_frontend;
+
+ dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx);
+
+ CX18_INFO("DVB Frontend registered\n");
+ mutex_init(&dvb->feedlock);
+ dvb->enabled = 1;
+ return ret;
+
+err_disconnect_frontend:
+ dmx->disconnect_frontend(dmx);
+err_remove_mem_frontend:
+ dmx->remove_frontend(dmx, &dvb->mem_frontend);
+err_remove_hw_frontend:
+ dmx->remove_frontend(dmx, &dvb->hw_frontend);
+err_dvb_dmxdev_release:
+ dvb_dmxdev_release(&dvb->dmxdev);
+err_dvb_dmx_release:
+ dvb_dmx_release(dvbdemux);
+err_dvb_unregister_adapter:
+ dvb_unregister_adapter(dvb_adapter);
+err_out:
+ return ret;
+}
+
+void cx18_dvb_unregister(struct cx18_stream *stream)
+{
+ struct cx18 *cx = stream->cx;
+ struct cx18_dvb *dvb = &stream->dvb;
+ struct dvb_adapter *dvb_adapter;
+ struct dvb_demux *dvbdemux;
+ struct dmx_demux *dmx;
+
+ CX18_INFO("unregister DVB\n");
+
+ dvb_adapter = &dvb->dvb_adapter;
+ dvbdemux = &dvb->demux;
+ dmx = &dvbdemux->dmx;
+
+ dmx->close(dmx);
+ dvb_net_release(&dvb->dvbnet);
+ dmx->remove_frontend(dmx, &dvb->mem_frontend);
+ dmx->remove_frontend(dmx, &dvb->hw_frontend);
+ dvb_dmxdev_release(&dvb->dmxdev);
+ dvb_dmx_release(dvbdemux);
+ dvb_unregister_frontend(dvb->fe);
+ dvb_frontend_detach(dvb->fe);
+ dvb_unregister_adapter(dvb_adapter);
+}
+
+/* All the DVB attach calls go here, this function get's modified
+ * for each new card. No other function in this file needs
+ * to change.
+ */
+static int dvb_register(struct cx18_stream *stream)
+{
+ struct cx18_dvb *dvb = &stream->dvb;
+ struct cx18 *cx = stream->cx;
+ int ret = 0;
+
+ switch (cx->card->type) {
+/* Wait until the MXL500X driver is merged */
+#ifdef HAVE_MXL500X
+ case CX18_CARD_HVR_1600_ESMT:
+ case CX18_CARD_HVR_1600_SAMSUNG:
+ dvb->fe = dvb_attach(s5h1409_attach,
+ &hauppauge_hvr1600_config,
+ &cx->i2c_adap[0]);
+ if (dvb->fe != NULL) {
+ dvb_attach(mxl500x_attach, dvb->fe,
+ &hauppauge_hvr1600_tuner,
+ &cx->i2c_adap[0]);
+ ret = 0;
+ }
+ break;
+#endif
+ default:
+ /* No Digital Tv Support */
+ break;
+ }
+
+ if (dvb->fe == NULL) {
+ CX18_ERR("frontend initialization failed\n");
+ return -1;
+ }
+
+ ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe);
+ if (ret < 0) {
+ if (dvb->fe->ops.release)
+ dvb->fe->ops.release(dvb->fe);
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/drivers/media/video/cx18/cx18-dvb.h b/drivers/media/video/cx18/cx18-dvb.h
new file mode 100644
index 00000000000..d6a6ccda79a
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-dvb.h
@@ -0,0 +1,25 @@
+/*
+ * cx18 functions for DVB support
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@hauppauge.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 "cx18-driver.h"
+
+int cx18_dvb_register(struct cx18_stream *stream);
+void cx18_dvb_unregister(struct cx18_stream *stream);
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
new file mode 100644
index 00000000000..69303065a29
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -0,0 +1,711 @@
+/*
+ * cx18 file operation functions
+ *
+ * Derived from ivtv-fileops.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-fileops.h"
+#include "cx18-i2c.h"
+#include "cx18-queue.h"
+#include "cx18-vbi.h"
+#include "cx18-audio.h"
+#include "cx18-mailbox.h"
+#include "cx18-scb.h"
+#include "cx18-streams.h"
+#include "cx18-controls.h"
+#include "cx18-ioctl.h"
+#include "cx18-cards.h"
+
+/* This function tries to claim the stream for a specific file descriptor.
+ If no one else is using this stream then the stream is claimed and
+ associated VBI streams are also automatically claimed.
+ Possible error returns: -EBUSY if someone else has claimed
+ the stream or 0 on success. */
+int cx18_claim_stream(struct cx18_open_id *id, int type)
+{
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[type];
+ struct cx18_stream *s_vbi;
+ int vbi_type;
+
+ if (test_and_set_bit(CX18_F_S_CLAIMED, &s->s_flags)) {
+ /* someone already claimed this stream */
+ if (s->id == id->open_id) {
+ /* yes, this file descriptor did. So that's OK. */
+ return 0;
+ }
+ if (s->id == -1 && type == CX18_ENC_STREAM_TYPE_VBI) {
+ /* VBI is handled already internally, now also assign
+ the file descriptor to this stream for external
+ reading of the stream. */
+ s->id = id->open_id;
+ CX18_DEBUG_INFO("Start Read VBI\n");
+ return 0;
+ }
+ /* someone else is using this stream already */
+ CX18_DEBUG_INFO("Stream %d is busy\n", type);
+ return -EBUSY;
+ }
+ s->id = id->open_id;
+
+ /* CX18_DEC_STREAM_TYPE_MPG needs to claim CX18_DEC_STREAM_TYPE_VBI,
+ CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI
+ (provided VBI insertion is on and sliced VBI is selected), for all
+ other streams we're done */
+ if (type == CX18_ENC_STREAM_TYPE_MPG &&
+ cx->vbi.insert_mpeg && cx->vbi.sliced_in->service_set) {
+ vbi_type = CX18_ENC_STREAM_TYPE_VBI;
+ } else {
+ return 0;
+ }
+ s_vbi = &cx->streams[vbi_type];
+
+ set_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags);
+
+ /* mark that it is used internally */
+ set_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags);
+ return 0;
+}
+
+/* This function releases a previously claimed stream. It will take into
+ account associated VBI streams. */
+void cx18_release_stream(struct cx18_stream *s)
+{
+ struct cx18 *cx = s->cx;
+ struct cx18_stream *s_vbi;
+
+ s->id = -1;
+ if (s->type == CX18_ENC_STREAM_TYPE_VBI &&
+ test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) {
+ /* this stream is still in use internally */
+ return;
+ }
+ if (!test_and_clear_bit(CX18_F_S_CLAIMED, &s->s_flags)) {
+ CX18_DEBUG_WARN("Release stream %s not in use!\n", s->name);
+ return;
+ }
+
+ cx18_flush_queues(s);
+
+ /* CX18_ENC_STREAM_TYPE_MPG needs to release CX18_ENC_STREAM_TYPE_VBI,
+ for all other streams we're done */
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG)
+ s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+ else
+ return;
+
+ /* clear internal use flag */
+ if (!test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags)) {
+ /* was already cleared */
+ return;
+ }
+ if (s_vbi->id != -1) {
+ /* VBI stream still claimed by a file descriptor */
+ return;
+ }
+ clear_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags);
+ cx18_flush_queues(s_vbi);
+}
+
+static void cx18_dualwatch(struct cx18 *cx)
+{
+ struct v4l2_tuner vt;
+ u16 new_bitmap;
+ u16 new_stereo_mode;
+ const u16 stereo_mask = 0x0300;
+ const u16 dual = 0x0200;
+
+ new_stereo_mode = cx->params.audio_properties & stereo_mask;
+ memset(&vt, 0, sizeof(vt));
+ cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, &vt);
+ if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 &&
+ (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
+ new_stereo_mode = dual;
+
+ if (new_stereo_mode == cx->dualwatch_stereo_mode)
+ return;
+
+ new_bitmap = new_stereo_mode | (cx->params.audio_properties & ~stereo_mask);
+
+ CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n",
+ cx->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);
+
+ if (cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2,
+ cx18_find_handle(cx), new_bitmap) == 0) {
+ cx->dualwatch_stereo_mode = new_stereo_mode;
+ return;
+ }
+ CX18_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
+}
+
+
+static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, int *err)
+{
+ struct cx18 *cx = s->cx;
+ struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+ struct cx18_buffer *buf;
+ DEFINE_WAIT(wait);
+
+ *err = 0;
+ while (1) {
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
+
+ if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) {
+ cx->dualwatch_jiffies = jiffies;
+ cx18_dualwatch(cx);
+ }
+ if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+ !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
+ while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) {
+ /* byteswap and process VBI data */
+/* cx18_process_vbi_data(cx, buf, s_vbi->dma_pts, s_vbi->type); */
+ cx18_enqueue(s_vbi, buf, &s_vbi->q_free);
+ }
+ }
+ buf = &cx->vbi.sliced_mpeg_buf;
+ if (buf->readpos != buf->bytesused)
+ return buf;
+ }
+
+ /* do we have leftover data? */
+ buf = cx18_dequeue(s, &s->q_io);
+ if (buf)
+ return buf;
+
+ /* do we have new data? */
+ buf = cx18_dequeue(s, &s->q_full);
+ if (buf) {
+ if (!test_and_clear_bit(CX18_F_B_NEED_BUF_SWAP,
+ &buf->b_flags))
+ return buf;
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG)
+ /* byteswap MPG data */
+ cx18_buf_swap(buf);
+ else {
+ /* byteswap and process VBI data */
+ cx18_process_vbi_data(cx, buf,
+ s->dma_pts, s->type);
+ }
+ return buf;
+ }
+
+ /* return if end of stream */
+ if (!test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+ CX18_DEBUG_INFO("EOS %s\n", s->name);
+ return NULL;
+ }
+
+ /* return if file was opened with O_NONBLOCK */
+ if (non_block) {
+ *err = -EAGAIN;
+ return NULL;
+ }
+
+ /* wait for more data to arrive */
+ prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
+ /* New buffers might have become available before we were added
+ to the waitqueue */
+ if (!s->q_full.buffers)
+ schedule();
+ finish_wait(&s->waitq, &wait);
+ if (signal_pending(current)) {
+ /* return if a signal was received */
+ CX18_DEBUG_INFO("User stopped %s\n", s->name);
+ *err = -EINTR;
+ return NULL;
+ }
+ }
+}
+
+static void cx18_setup_sliced_vbi_buf(struct cx18 *cx)
+{
+ int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
+
+ cx->vbi.sliced_mpeg_buf.buf = cx->vbi.sliced_mpeg_data[idx];
+ cx->vbi.sliced_mpeg_buf.bytesused = cx->vbi.sliced_mpeg_size[idx];
+ cx->vbi.sliced_mpeg_buf.readpos = 0;
+}
+
+static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
+ struct cx18_buffer *buf, char __user *ubuf, size_t ucount)
+{
+ struct cx18 *cx = s->cx;
+ size_t len = buf->bytesused - buf->readpos;
+
+ if (len > ucount)
+ len = ucount;
+ if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG &&
+ cx->vbi.sliced_in->service_set && buf != &cx->vbi.sliced_mpeg_buf) {
+ const char *start = buf->buf + buf->readpos;
+ const char *p = start + 1;
+ const u8 *q;
+ u8 ch = cx->search_pack_header ? 0xba : 0xe0;
+ int stuffing, i;
+
+ while (start + len > p) {
+ q = memchr(p, 0, start + len - p);
+ if (q == NULL)
+ break;
+ p = q + 1;
+ if ((char *)q + 15 >= buf->buf + buf->bytesused ||
+ q[1] != 0 || q[2] != 1 || q[3] != ch)
+ continue;
+ if (!cx->search_pack_header) {
+ if ((q[6] & 0xc0) != 0x80)
+ continue;
+ if (((q[7] & 0xc0) == 0x80 &&
+ (q[9] & 0xf0) == 0x20) ||
+ ((q[7] & 0xc0) == 0xc0 &&
+ (q[9] & 0xf0) == 0x30)) {
+ ch = 0xba;
+ cx->search_pack_header = 1;
+ p = q + 9;
+ }
+ continue;
+ }
+ stuffing = q[13] & 7;
+ /* all stuffing bytes must be 0xff */
+ for (i = 0; i < stuffing; i++)
+ if (q[14 + i] != 0xff)
+ break;
+ if (i == stuffing &&
+ (q[4] & 0xc4) == 0x44 &&
+ (q[12] & 3) == 3 &&
+ q[14 + stuffing] == 0 &&
+ q[15 + stuffing] == 0 &&
+ q[16 + stuffing] == 1) {
+ cx->search_pack_header = 0;
+ len = (char *)q - start;
+ cx18_setup_sliced_vbi_buf(cx);
+ break;
+ }
+ }
+ }
+ if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) {
+ CX18_DEBUG_WARN("copy %zd bytes to user failed for %s\n",
+ len, s->name);
+ return -EFAULT;
+ }
+ buf->readpos += len;
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG &&
+ buf != &cx->vbi.sliced_mpeg_buf)
+ cx->mpg_data_received += len;
+ return len;
+}
+
+static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
+ size_t tot_count, int non_block)
+{
+ struct cx18 *cx = s->cx;
+ size_t tot_written = 0;
+ int single_frame = 0;
+
+ if (atomic_read(&cx->capturing) == 0 && s->id == -1) {
+ /* shouldn't happen */
+ CX18_DEBUG_WARN("Stream %s not initialized before read\n",
+ s->name);
+ return -EIO;
+ }
+
+ /* Each VBI buffer is one frame, the v4l2 API says that for VBI the
+ frames should arrive one-by-one, so make sure we never output more
+ than one VBI frame at a time */
+ if (s->type == CX18_ENC_STREAM_TYPE_VBI &&
+ cx->vbi.sliced_in->service_set)
+ single_frame = 1;
+
+ for (;;) {
+ struct cx18_buffer *buf;
+ int rc;
+
+ buf = cx18_get_buffer(s, non_block, &rc);
+ /* if there is no data available... */
+ if (buf == NULL) {
+ /* if we got data, then return that regardless */
+ if (tot_written)
+ break;
+ /* EOS condition */
+ if (rc == 0) {
+ clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+ clear_bit(CX18_F_S_APPL_IO, &s->s_flags);
+ cx18_release_stream(s);
+ }
+ /* set errno */
+ return rc;
+ }
+
+ rc = cx18_copy_buf_to_user(s, buf, ubuf + tot_written,
+ tot_count - tot_written);
+
+ if (buf != &cx->vbi.sliced_mpeg_buf) {
+ if (buf->readpos == buf->bytesused) {
+ cx18_buf_sync_for_device(s, buf);
+ cx18_enqueue(s, buf, &s->q_free);
+ cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5,
+ s->handle,
+ (void *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
+ 1, buf->id, s->buf_size);
+ } else
+ cx18_enqueue(s, buf, &s->q_io);
+ } else if (buf->readpos == buf->bytesused) {
+ int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
+
+ cx->vbi.sliced_mpeg_size[idx] = 0;
+ cx->vbi.inserted_frame++;
+ cx->vbi_data_inserted += buf->bytesused;
+ }
+ if (rc < 0)
+ return rc;
+ tot_written += rc;
+
+ if (tot_written == tot_count || single_frame)
+ break;
+ }
+ return tot_written;
+}
+
+static ssize_t cx18_read_pos(struct cx18_stream *s, char __user *ubuf,
+ size_t count, loff_t *pos, int non_block)
+{
+ ssize_t rc = count ? cx18_read(s, ubuf, count, non_block) : 0;
+ struct cx18 *cx = s->cx;
+
+ CX18_DEBUG_HI_FILE("read %zd from %s, got %zd\n", count, s->name, rc);
+ if (rc > 0)
+ pos += rc;
+ return rc;
+}
+
+int cx18_start_capture(struct cx18_open_id *id)
+{
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+ struct cx18_stream *s_vbi;
+
+ if (s->type == CX18_ENC_STREAM_TYPE_RAD) {
+ /* you cannot read from these stream types. */
+ return -EPERM;
+ }
+
+ /* Try to claim this stream. */
+ if (cx18_claim_stream(id, s->type))
+ return -EBUSY;
+
+ /* If capture is already in progress, then we also have to
+ do nothing extra. */
+ if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) ||
+ test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+ set_bit(CX18_F_S_APPL_IO, &s->s_flags);
+ return 0;
+ }
+
+ /* Start VBI capture if required */
+ s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG &&
+ test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+ !test_and_set_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) {
+ /* Note: the CX18_ENC_STREAM_TYPE_VBI is claimed
+ automatically when the MPG stream is claimed.
+ We only need to start the VBI capturing. */
+ if (cx18_start_v4l2_encode_stream(s_vbi)) {
+ CX18_DEBUG_WARN("VBI capture start failed\n");
+
+ /* Failure, clean up and return an error */
+ clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
+ clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+ /* also releases the associated VBI stream */
+ cx18_release_stream(s);
+ return -EIO;
+ }
+ CX18_DEBUG_INFO("VBI insertion started\n");
+ }
+
+ /* Tell the card to start capturing */
+ if (!cx18_start_v4l2_encode_stream(s)) {
+ /* We're done */
+ set_bit(CX18_F_S_APPL_IO, &s->s_flags);
+ /* Resume a possibly paused encoder */
+ if (test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
+ cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, s->handle);
+ return 0;
+ }
+
+ /* failure, clean up */
+ CX18_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name);
+
+ /* Note: the CX18_ENC_STREAM_TYPE_VBI is released
+ automatically when the MPG stream is released.
+ We only need to stop the VBI capturing. */
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG &&
+ test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) {
+ cx18_stop_v4l2_encode_stream(s_vbi, 0);
+ clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
+ }
+ clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+ cx18_release_stream(s);
+ return -EIO;
+}
+
+ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct cx18_open_id *id = filp->private_data;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+ int rc;
+
+ CX18_DEBUG_HI_FILE("read %zd bytes from %s\n", count, s->name);
+
+ mutex_lock(&cx->serialize_lock);
+ rc = cx18_start_capture(id);
+ mutex_unlock(&cx->serialize_lock);
+ if (rc)
+ return rc;
+ return cx18_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK);
+}
+
+unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait)
+{
+ struct cx18_open_id *id = filp->private_data;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+ int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+
+ /* Start a capture if there is none */
+ if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+ int rc;
+
+ mutex_lock(&cx->serialize_lock);
+ rc = cx18_start_capture(id);
+ mutex_unlock(&cx->serialize_lock);
+ if (rc) {
+ CX18_DEBUG_INFO("Could not start capture for %s (%d)\n",
+ s->name, rc);
+ return POLLERR;
+ }
+ CX18_DEBUG_FILE("Encoder poll started capture\n");
+ }
+
+ /* add stream's waitq to the poll list */
+ CX18_DEBUG_HI_FILE("Encoder poll\n");
+ poll_wait(filp, &s->waitq, wait);
+
+ if (s->q_full.length || s->q_io.length)
+ return POLLIN | POLLRDNORM;
+ if (eof)
+ return POLLHUP;
+ return 0;
+}
+
+void cx18_stop_capture(struct cx18_open_id *id, int gop_end)
+{
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+
+ CX18_DEBUG_IOCTL("close() of %s\n", s->name);
+
+ /* 'Unclaim' this stream */
+
+ /* Stop capturing */
+ if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
+ struct cx18_stream *s_vbi =
+ &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
+
+ CX18_DEBUG_INFO("close stopping capture\n");
+ /* Special case: a running VBI capture for VBI insertion
+ in the mpeg stream. Need to stop that too. */
+ if (id->type == CX18_ENC_STREAM_TYPE_MPG &&
+ test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) &&
+ !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
+ CX18_DEBUG_INFO("close stopping embedded VBI capture\n");
+ cx18_stop_v4l2_encode_stream(s_vbi, 0);
+ }
+ if (id->type == CX18_ENC_STREAM_TYPE_VBI &&
+ test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags))
+ /* Also used internally, don't stop capturing */
+ s->id = -1;
+ else
+ cx18_stop_v4l2_encode_stream(s, gop_end);
+ }
+ if (!gop_end) {
+ clear_bit(CX18_F_S_APPL_IO, &s->s_flags);
+ clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+ cx18_release_stream(s);
+ }
+}
+
+int cx18_v4l2_close(struct inode *inode, struct file *filp)
+{
+ struct cx18_open_id *id = filp->private_data;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+
+ CX18_DEBUG_IOCTL("close() of %s\n", s->name);
+
+ v4l2_prio_close(&cx->prio, &id->prio);
+
+ /* Easy case first: this stream was never claimed by us */
+ if (s->id != id->open_id) {
+ kfree(id);
+ return 0;
+ }
+
+ /* 'Unclaim' this stream */
+
+ /* Stop radio */
+ mutex_lock(&cx->serialize_lock);
+ if (id->type == CX18_ENC_STREAM_TYPE_RAD) {
+ /* Closing radio device, return to TV mode */
+ cx18_mute(cx);
+ /* Mark that the radio is no longer in use */
+ clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
+ /* Switch tuner to TV */
+ cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
+ /* Select correct audio input (i.e. TV tuner or Line in) */
+ cx18_audio_set_io(cx);
+ if (atomic_read(&cx->capturing) > 0) {
+ /* Undo video mute */
+ cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
+ cx->params.video_mute |
+ (cx->params.video_mute_yuv << 8));
+ }
+ /* Done! Unmute and continue. */
+ cx18_unmute(cx);
+ cx18_release_stream(s);
+ } else {
+ cx18_stop_capture(id, 0);
+ }
+ kfree(id);
+ mutex_unlock(&cx->serialize_lock);
+ return 0;
+}
+
+static int cx18_serialized_open(struct cx18_stream *s, struct file *filp)
+{
+ struct cx18 *cx = s->cx;
+ struct cx18_open_id *item;
+
+ CX18_DEBUG_FILE("open %s\n", s->name);
+
+ /* Allocate memory */
+ item = kmalloc(sizeof(struct cx18_open_id), GFP_KERNEL);
+ if (NULL == item) {
+ CX18_DEBUG_WARN("nomem on v4l2 open\n");
+ return -ENOMEM;
+ }
+ item->cx = cx;
+ item->type = s->type;
+ v4l2_prio_open(&cx->prio, &item->prio);
+
+ item->open_id = cx->open_id++;
+ filp->private_data = item;
+
+ if (item->type == CX18_ENC_STREAM_TYPE_RAD) {
+ /* Try to claim this stream */
+ if (cx18_claim_stream(item, item->type)) {
+ /* No, it's already in use */
+ kfree(item);
+ return -EBUSY;
+ }
+
+ if (!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
+ if (atomic_read(&cx->capturing) > 0) {
+ /* switching to radio while capture is
+ in progress is not polite */
+ cx18_release_stream(s);
+ kfree(item);
+ return -EBUSY;
+ }
+ }
+
+ /* Mark that the radio is being used. */
+ set_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
+ /* We have the radio */
+ cx18_mute(cx);
+ /* Switch tuner to radio */
+ cx18_call_i2c_clients(cx, AUDC_SET_RADIO, NULL);
+ /* Select the correct audio input (i.e. radio tuner) */
+ cx18_audio_set_io(cx);
+ /* Done! Unmute and continue. */
+ cx18_unmute(cx);
+ }
+ return 0;
+}
+
+int cx18_v4l2_open(struct inode *inode, struct file *filp)
+{
+ int res, x, y = 0;
+ struct cx18 *cx = NULL;
+ struct cx18_stream *s = NULL;
+ int minor = iminor(inode);
+
+ /* Find which card this open was on */
+ spin_lock(&cx18_cards_lock);
+ for (x = 0; cx == NULL && x < cx18_cards_active; x++) {
+ /* find out which stream this open was on */
+ for (y = 0; y < CX18_MAX_STREAMS; y++) {
+ s = &cx18_cards[x]->streams[y];
+ if (s->v4l2dev && s->v4l2dev->minor == minor) {
+ cx = cx18_cards[x];
+ break;
+ }
+ }
+ }
+ spin_unlock(&cx18_cards_lock);
+
+ if (cx == NULL) {
+ /* Couldn't find a device registered
+ on that minor, shouldn't happen! */
+ printk(KERN_WARNING "No cx18 device found on minor %d\n",
+ minor);
+ return -ENXIO;
+ }
+
+ mutex_lock(&cx->serialize_lock);
+ if (cx18_init_on_first_open(cx)) {
+ CX18_ERR("Failed to initialize on minor %d\n", minor);
+ mutex_unlock(&cx->serialize_lock);
+ return -ENXIO;
+ }
+ res = cx18_serialized_open(s, filp);
+ mutex_unlock(&cx->serialize_lock);
+ return res;
+}
+
+void cx18_mute(struct cx18 *cx)
+{
+ if (atomic_read(&cx->capturing))
+ cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
+ cx18_find_handle(cx), 1);
+ CX18_DEBUG_INFO("Mute\n");
+}
+
+void cx18_unmute(struct cx18 *cx)
+{
+ if (atomic_read(&cx->capturing)) {
+ cx18_msleep_timeout(100, 0);
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2,
+ cx18_find_handle(cx), 12);
+ cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
+ cx18_find_handle(cx), 0);
+ }
+ CX18_DEBUG_INFO("Unmute\n");
+}
diff --git a/drivers/media/video/cx18/cx18-fileops.h b/drivers/media/video/cx18/cx18-fileops.h
new file mode 100644
index 00000000000..16cdafbd24c
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-fileops.h
@@ -0,0 +1,45 @@
+/*
+ * cx18 file operation functions
+ *
+ * Derived from ivtv-fileops.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+/* Testing/Debugging */
+int cx18_v4l2_open(struct inode *inode, struct file *filp);
+ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *pos);
+ssize_t cx18_v4l2_write(struct file *filp, const char __user *buf, size_t count,
+ loff_t *pos);
+int cx18_v4l2_close(struct inode *inode, struct file *filp);
+unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait);
+int cx18_start_capture(struct cx18_open_id *id);
+void cx18_stop_capture(struct cx18_open_id *id, int gop_end);
+void cx18_mute(struct cx18 *cx);
+void cx18_unmute(struct cx18 *cx);
+
+/* Utilities */
+
+/* Try to claim a stream for the filehandle. Return 0 on success,
+ -EBUSY if stream already claimed. Once a stream is claimed, it
+ remains claimed until the associated filehandle is closed. */
+int cx18_claim_stream(struct cx18_open_id *id, int type);
+
+/* Release a previously claimed stream. */
+void cx18_release_stream(struct cx18_stream *s);
diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c
new file mode 100644
index 00000000000..2694ce35063
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-firmware.c
@@ -0,0 +1,373 @@
+/*
+ * cx18 firmware functions
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-scb.h"
+#include "cx18-irq.h"
+#include "cx18-firmware.h"
+#include "cx18-cards.h"
+#include <linux/firmware.h>
+
+#define CX18_PROC_SOFT_RESET 0xc70010
+#define CX18_DDR_SOFT_RESET 0xc70014
+#define CX18_CLOCK_SELECT1 0xc71000
+#define CX18_CLOCK_SELECT2 0xc71004
+#define CX18_HALF_CLOCK_SELECT1 0xc71008
+#define CX18_HALF_CLOCK_SELECT2 0xc7100C
+#define CX18_CLOCK_POLARITY1 0xc71010
+#define CX18_CLOCK_POLARITY2 0xc71014
+#define CX18_ADD_DELAY_ENABLE1 0xc71018
+#define CX18_ADD_DELAY_ENABLE2 0xc7101C
+#define CX18_CLOCK_ENABLE1 0xc71020
+#define CX18_CLOCK_ENABLE2 0xc71024
+
+#define CX18_REG_BUS_TIMEOUT_EN 0xc72024
+
+#define CX18_AUDIO_ENABLE 0xc72014
+#define CX18_REG_BUS_TIMEOUT_EN 0xc72024
+
+#define CX18_FAST_CLOCK_PLL_INT 0xc78000
+#define CX18_FAST_CLOCK_PLL_FRAC 0xc78004
+#define CX18_FAST_CLOCK_PLL_POST 0xc78008
+#define CX18_FAST_CLOCK_PLL_PRESCALE 0xc7800C
+#define CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH 0xc78010
+
+#define CX18_SLOW_CLOCK_PLL_INT 0xc78014
+#define CX18_SLOW_CLOCK_PLL_FRAC 0xc78018
+#define CX18_SLOW_CLOCK_PLL_POST 0xc7801C
+#define CX18_MPEG_CLOCK_PLL_INT 0xc78040
+#define CX18_MPEG_CLOCK_PLL_FRAC 0xc78044
+#define CX18_MPEG_CLOCK_PLL_POST 0xc78048
+#define CX18_PLL_POWER_DOWN 0xc78088
+#define CX18_SW1_INT_STATUS 0xc73104
+#define CX18_SW1_INT_ENABLE_PCI 0xc7311C
+#define CX18_SW2_INT_SET 0xc73140
+#define CX18_SW2_INT_STATUS 0xc73144
+#define CX18_ADEC_CONTROL 0xc78120
+
+#define CX18_DDR_REQUEST_ENABLE 0xc80000
+#define CX18_DDR_CHIP_CONFIG 0xc80004
+#define CX18_DDR_REFRESH 0xc80008
+#define CX18_DDR_TIMING1 0xc8000C
+#define CX18_DDR_TIMING2 0xc80010
+#define CX18_DDR_POWER_REG 0xc8001C
+
+#define CX18_DDR_TUNE_LANE 0xc80048
+#define CX18_DDR_INITIAL_EMRS 0xc80054
+#define CX18_DDR_MB_PER_ROW_7 0xc8009C
+#define CX18_DDR_BASE_63_ADDR 0xc804FC
+
+#define CX18_WMB_CLIENT02 0xc90108
+#define CX18_WMB_CLIENT05 0xc90114
+#define CX18_WMB_CLIENT06 0xc90118
+#define CX18_WMB_CLIENT07 0xc9011C
+#define CX18_WMB_CLIENT08 0xc90120
+#define CX18_WMB_CLIENT09 0xc90124
+#define CX18_WMB_CLIENT10 0xc90128
+#define CX18_WMB_CLIENT11 0xc9012C
+#define CX18_WMB_CLIENT12 0xc90130
+#define CX18_WMB_CLIENT13 0xc90134
+#define CX18_WMB_CLIENT14 0xc90138
+
+#define CX18_DSP0_INTERRUPT_MASK 0xd0004C
+
+/* Encoder/decoder firmware sizes */
+#define CX18_FW_CPU_SIZE (174716)
+#define CX18_FW_APU_SIZE (141200)
+
+#define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */
+#define APU_ROM_SYNC2 0x72646548 /* "rdeH" */
+
+struct cx18_apu_rom_seghdr {
+ u32 sync1;
+ u32 sync2;
+ u32 addr;
+ u32 size;
+};
+
+static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx, long size)
+{
+ const struct firmware *fw = NULL;
+ int retries = 3;
+ int i, j;
+ u32 __iomem *dst = (u32 __iomem *)mem;
+ const u32 *src;
+
+retry:
+ if (!retries || request_firmware(&fw, fn, &cx->dev->dev)) {
+ CX18_ERR("Unable to open firmware %s (must be %ld bytes)\n",
+ fn, size);
+ CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n");
+ return -ENOMEM;
+ }
+
+ src = (const u32 *)fw->data;
+
+ if (fw->size != size) {
+ /* Due to race conditions in firmware loading (esp. with
+ udev <0.95) the wrong file was sometimes loaded. So we check
+ filesizes to see if at least the right-sized file was
+ loaded. If not, then we retry. */
+ CX18_INFO("retry: file loaded was not %s (expected size %ld, got %zd)\n",
+ fn, size, fw->size);
+ release_firmware(fw);
+ retries--;
+ goto retry;
+ }
+ for (i = 0; i < fw->size; i += 4096) {
+ setup_page(i);
+ for (j = i; j < fw->size && j < i + 4096; j += 4) {
+ /* no need for endianness conversion on the ppc */
+ __raw_writel(*src, dst);
+ if (__raw_readl(dst) != *src) {
+ CX18_ERR("Mismatch at offset %x\n", i);
+ release_firmware(fw);
+ return -EIO;
+ }
+ dst++;
+ src++;
+ }
+ }
+ if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
+ CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
+ release_firmware(fw);
+ return size;
+}
+
+static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, long size)
+{
+ const struct firmware *fw = NULL;
+ int retries = 3;
+ int i, j;
+ const u32 *src;
+ struct cx18_apu_rom_seghdr seghdr;
+ const u8 *vers;
+ u32 offset = 0;
+ u32 apu_version = 0;
+ int sz;
+
+retry:
+ if (!retries || request_firmware(&fw, fn, &cx->dev->dev)) {
+ CX18_ERR("unable to open firmware %s (must be %ld bytes)\n",
+ fn, size);
+ CX18_ERR("did you put the firmware in the hotplug firmware directory?\n");
+ return -ENOMEM;
+ }
+
+ src = (const u32 *)fw->data;
+ vers = fw->data + sizeof(seghdr);
+ sz = fw->size;
+
+ if (fw->size != size) {
+ /* Due to race conditions in firmware loading (esp. with
+ udev <0.95) the wrong file was sometimes loaded. So we check
+ filesizes to see if at least the right-sized file was
+ loaded. If not, then we retry. */
+ CX18_INFO("retry: file loaded was not %s (expected size %ld, got %zd)\n",
+ fn, size, fw->size);
+ release_firmware(fw);
+ retries--;
+ goto retry;
+ }
+ apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32];
+ while (offset + sizeof(seghdr) < size) {
+ /* TODO: byteswapping */
+ memcpy(&seghdr, src + offset / 4, sizeof(seghdr));
+ offset += sizeof(seghdr);
+ if (seghdr.sync1 != APU_ROM_SYNC1 ||
+ seghdr.sync2 != APU_ROM_SYNC2) {
+ offset += seghdr.size;
+ continue;
+ }
+ CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr,
+ seghdr.addr + seghdr.size - 1);
+ if (offset + seghdr.size > sz)
+ break;
+ for (i = 0; i < seghdr.size; i += 4096) {
+ setup_page(offset + i);
+ for (j = i; j < seghdr.size && j < i + 4096; j += 4) {
+ /* no need for endianness conversion on the ppc */
+ __raw_writel(src[(offset + j) / 4], dst + seghdr.addr + j);
+ if (__raw_readl(dst + seghdr.addr + j) != src[(offset + j) / 4]) {
+ CX18_ERR("Mismatch at offset %x\n", offset + j);
+ release_firmware(fw);
+ return -EIO;
+ }
+ }
+ }
+ offset += seghdr.size;
+ }
+ if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
+ CX18_INFO("loaded %s firmware V%08x (%zd bytes)\n",
+ fn, apu_version, fw->size);
+ release_firmware(fw);
+ /* Clear bit0 for APU to start from 0 */
+ write_reg(read_reg(0xc72030) & ~1, 0xc72030);
+ return size;
+}
+
+void cx18_halt_firmware(struct cx18 *cx)
+{
+ CX18_DEBUG_INFO("Preparing for firmware halt.\n");
+ write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */
+ write_reg(0x00020002, CX18_ADEC_CONTROL);
+}
+
+void cx18_init_power(struct cx18 *cx, int lowpwr)
+{
+ /* power-down Spare and AOM PLLs */
+ /* power-up fast, slow and mpeg PLLs */
+ write_reg(0x00000008, CX18_PLL_POWER_DOWN);
+
+ /* ADEC out of sleep */
+ write_reg(0x00020000, CX18_ADEC_CONTROL);
+
+ /* The fast clock is at 200/245 MHz */
+ write_reg(lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT);
+ write_reg(lowpwr ? 0x1EFBF37 : 0x038E3D7, CX18_FAST_CLOCK_PLL_FRAC);
+
+ write_reg(2, CX18_FAST_CLOCK_PLL_POST);
+ write_reg(1, CX18_FAST_CLOCK_PLL_PRESCALE);
+ write_reg(4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH);
+
+ /* set slow clock to 125/120 MHz */
+ write_reg(lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT);
+ write_reg(lowpwr ? 0xEBAF05 : 0x18618A8, CX18_SLOW_CLOCK_PLL_FRAC);
+ write_reg(4, CX18_SLOW_CLOCK_PLL_POST);
+
+ /* mpeg clock pll 54MHz */
+ write_reg(0xF, CX18_MPEG_CLOCK_PLL_INT);
+ write_reg(0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC);
+ write_reg(8, CX18_MPEG_CLOCK_PLL_POST);
+
+ /* Defaults */
+ /* APU = SC or SC/2 = 125/62.5 */
+ /* EPU = SC = 125 */
+ /* DDR = FC = 180 */
+ /* ENC = SC = 125 */
+ /* AI1 = SC = 125 */
+ /* VIM2 = disabled */
+ /* PCI = FC/2 = 90 */
+ /* AI2 = disabled */
+ /* DEMUX = disabled */
+ /* AO = SC/2 = 62.5 */
+ /* SER = 54MHz */
+ /* VFC = disabled */
+ /* USB = disabled */
+
+ write_reg(lowpwr ? 0xFFFF0020 : 0x00060004, CX18_CLOCK_SELECT1);
+ write_reg(lowpwr ? 0xFFFF0004 : 0x00060006, CX18_CLOCK_SELECT2);
+
+ write_reg(0xFFFF0002, CX18_HALF_CLOCK_SELECT1);
+ write_reg(0xFFFF0104, CX18_HALF_CLOCK_SELECT2);
+
+ write_reg(0xFFFF9026, CX18_CLOCK_ENABLE1);
+ write_reg(0xFFFF3105, CX18_CLOCK_ENABLE2);
+}
+
+void cx18_init_memory(struct cx18 *cx)
+{
+ cx18_msleep_timeout(10, 0);
+ write_reg(0x10000, CX18_DDR_SOFT_RESET);
+ cx18_msleep_timeout(10, 0);
+
+ write_reg(cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG);
+
+ cx18_msleep_timeout(10, 0);
+
+ write_reg(cx->card->ddr.refresh, CX18_DDR_REFRESH);
+ write_reg(cx->card->ddr.timing1, CX18_DDR_TIMING1);
+ write_reg(cx->card->ddr.timing2, CX18_DDR_TIMING2);
+
+ cx18_msleep_timeout(10, 0);
+
+ /* Initialize DQS pad time */
+ write_reg(cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE);
+ write_reg(cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS);
+
+ cx18_msleep_timeout(10, 0);
+
+ write_reg(0x20000, CX18_DDR_SOFT_RESET);
+ cx18_msleep_timeout(10, 0);
+
+ /* use power-down mode when idle */
+ write_reg(0x00000010, CX18_DDR_POWER_REG);
+
+ write_reg(0x10001, CX18_REG_BUS_TIMEOUT_EN);
+
+ write_reg(0x48, CX18_DDR_MB_PER_ROW_7);
+ write_reg(0xE0000, CX18_DDR_BASE_63_ADDR);
+
+ write_reg(0x00000101, CX18_WMB_CLIENT02); /* AO */
+ write_reg(0x00000101, CX18_WMB_CLIENT09); /* AI2 */
+ write_reg(0x00000101, CX18_WMB_CLIENT05); /* VIM1 */
+ write_reg(0x00000101, CX18_WMB_CLIENT06); /* AI1 */
+ write_reg(0x00000101, CX18_WMB_CLIENT07); /* 3D comb */
+ write_reg(0x00000101, CX18_WMB_CLIENT10); /* ME */
+ write_reg(0x00000101, CX18_WMB_CLIENT12); /* ENC */
+ write_reg(0x00000101, CX18_WMB_CLIENT13); /* PK */
+ write_reg(0x00000101, CX18_WMB_CLIENT11); /* RC */
+ write_reg(0x00000101, CX18_WMB_CLIENT14); /* AVO */
+}
+
+int cx18_firmware_init(struct cx18 *cx)
+{
+ /* Allow chip to control CLKRUN */
+ write_reg(0x5, CX18_DSP0_INTERRUPT_MASK);
+
+ write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */
+
+ cx18_msleep_timeout(1, 0);
+
+ sw1_irq_enable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
+ sw2_irq_enable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+
+ /* Only if the processor is not running */
+ if (read_reg(CX18_PROC_SOFT_RESET) & 8) {
+ int sz = load_apu_fw_direct("v4l-cx23418-apu.fw",
+ cx->enc_mem, cx, CX18_FW_APU_SIZE);
+
+ sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw",
+ cx->enc_mem, cx, CX18_FW_CPU_SIZE);
+
+ if (sz > 0) {
+ int retries = 0;
+
+ /* start the CPU */
+ write_reg(0x00080000, CX18_PROC_SOFT_RESET);
+ while (retries++ < 50) { /* Loop for max 500mS */
+ if ((read_reg(CX18_PROC_SOFT_RESET) & 1) == 0)
+ break;
+ cx18_msleep_timeout(10, 0);
+ }
+ cx18_msleep_timeout(200, 0);
+ if (retries == 51) {
+ CX18_ERR("Could not start the CPU\n");
+ return -EIO;
+ }
+ }
+ if (sz <= 0)
+ return -EIO;
+ }
+ /* initialize GPIO */
+ write_reg(0x14001400, 0xC78110);
+ return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-firmware.h b/drivers/media/video/cx18/cx18-firmware.h
new file mode 100644
index 00000000000..38d4c05e849
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-firmware.h
@@ -0,0 +1,25 @@
+/*
+ * cx18 firmware functions
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+int cx18_firmware_init(struct cx18 *cx);
+void cx18_halt_firmware(struct cx18 *cx);
+void cx18_init_memory(struct cx18 *cx);
+void cx18_init_power(struct cx18 *cx, int lowpwr);
diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c
new file mode 100644
index 00000000000..19253e6b867
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-gpio.c
@@ -0,0 +1,74 @@
+/*
+ * cx18 gpio functions
+ *
+ * Derived from ivtv-gpio.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-cards.h"
+#include "cx18-gpio.h"
+#include "tuner-xc2028.h"
+
+/********************* GPIO stuffs *********************/
+
+/* GPIO registers */
+#define CX18_REG_GPIO_IN 0xc72010
+#define CX18_REG_GPIO_OUT1 0xc78100
+#define CX18_REG_GPIO_DIR1 0xc78108
+#define CX18_REG_GPIO_OUT2 0xc78104
+#define CX18_REG_GPIO_DIR2 0xc7810c
+
+/*
+ * HVR-1600 GPIO pins, courtesy of Hauppauge:
+ *
+ * gpio0: zilog ir process reset pin
+ * gpio1: zilog programming pin (you should never use this)
+ * gpio12: cx24227 reset pin
+ * gpio13: cs5345 reset pin
+*/
+
+void cx18_gpio_init(struct cx18 *cx)
+{
+ if (cx->card->gpio_init.direction == 0)
+ return;
+
+ CX18_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
+ read_reg(CX18_REG_GPIO_DIR1), read_reg(CX18_REG_GPIO_OUT1));
+
+ /* init output data then direction */
+ write_reg(cx->card->gpio_init.direction << 16, CX18_REG_GPIO_DIR1);
+ write_reg(0, CX18_REG_GPIO_DIR2);
+ write_reg((cx->card->gpio_init.direction << 16) |
+ cx->card->gpio_init.initial_value, CX18_REG_GPIO_OUT1);
+ write_reg(0, CX18_REG_GPIO_OUT2);
+}
+
+/* Xceive tuner reset function */
+int cx18_reset_tuner_gpio(void *dev, int cmd, int value)
+{
+ struct i2c_algo_bit_data *algo = dev;
+ struct cx18 *cx = algo->data;
+/* int curdir, curout;*/
+
+ if (cmd != XC2028_TUNER_RESET)
+ return 0;
+ CX18_DEBUG_INFO("Resetting tuner\n");
+ return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h
new file mode 100644
index 00000000000..41bac8856b5
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-gpio.h
@@ -0,0 +1,24 @@
+/*
+ * cx18 gpio functions
+ *
+ * Derived from ivtv-gpio.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+void cx18_gpio_init(struct cx18 *cx);
+int cx18_reset_tuner_gpio(void *dev, int cmd, int value);
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c
new file mode 100644
index 00000000000..18c88d1e483
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-i2c.c
@@ -0,0 +1,431 @@
+/*
+ * cx18 I2C functions
+ *
+ * Derived from ivtv-i2c.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-cards.h"
+#include "cx18-gpio.h"
+#include "cx18-av-core.h"
+
+#include <media/ir-kbd-i2c.h>
+
+#define CX18_REG_I2C_1_WR 0xf15000
+#define CX18_REG_I2C_1_RD 0xf15008
+#define CX18_REG_I2C_2_WR 0xf25100
+#define CX18_REG_I2C_2_RD 0xf25108
+
+#define SETSCL_BIT 0x0001
+#define SETSDL_BIT 0x0002
+#define GETSCL_BIT 0x0004
+#define GETSDL_BIT 0x0008
+
+#ifndef I2C_ADAP_CLASS_TV_ANALOG
+#define I2C_ADAP_CLASS_TV_ANALOG I2C_CLASS_TV_ANALOG
+#endif
+
+#define CX18_CS5345_I2C_ADDR 0x4c
+
+/* This array should match the CX18_HW_ defines */
+static const u8 hw_driverids[] = {
+ I2C_DRIVERID_TUNER,
+ I2C_DRIVERID_TVEEPROM,
+ I2C_DRIVERID_CS5345,
+ 0, /* CX18_HW_GPIO dummy driver ID */
+ 0 /* CX18_HW_CX23418 dummy driver ID */
+};
+
+/* This array should match the CX18_HW_ defines */
+static const u8 hw_addrs[] = {
+ 0,
+ 0,
+ CX18_CS5345_I2C_ADDR,
+ 0, /* CX18_HW_GPIO dummy driver ID */
+ 0, /* CX18_HW_CX23418 dummy driver ID */
+};
+
+/* This array should match the CX18_HW_ defines */
+/* This might well become a card-specific array */
+static const u8 hw_bus[] = {
+ 0,
+ 0,
+ 0,
+ 0, /* CX18_HW_GPIO dummy driver ID */
+ 0, /* CX18_HW_CX23418 dummy driver ID */
+};
+
+/* This array should match the CX18_HW_ defines */
+static const char * const hw_drivernames[] = {
+ "tuner",
+ "tveeprom",
+ "cs5345",
+ "gpio",
+ "cx23418",
+};
+
+int cx18_i2c_register(struct cx18 *cx, unsigned idx)
+{
+ struct i2c_board_info info;
+ struct i2c_client *c;
+ u8 id, bus;
+ int i;
+
+ CX18_DEBUG_I2C("i2c client register\n");
+ if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0)
+ return -1;
+ id = hw_driverids[idx];
+ bus = hw_bus[idx];
+ memset(&info, 0, sizeof(info));
+ strlcpy(info.driver_name, hw_drivernames[idx],
+ sizeof(info.driver_name));
+ info.addr = hw_addrs[idx];
+ for (i = 0; i < I2C_CLIENTS_MAX; i++)
+ if (cx->i2c_clients[i] == NULL)
+ break;
+
+ if (i == I2C_CLIENTS_MAX) {
+ CX18_ERR("insufficient room for new I2C client!\n");
+ return -ENOMEM;
+ }
+
+ if (id != I2C_DRIVERID_TUNER) {
+ c = i2c_new_device(&cx->i2c_adap[bus], &info);
+ if (c->driver == NULL)
+ i2c_unregister_device(c);
+ else
+ cx->i2c_clients[i] = c;
+ return cx->i2c_clients[i] ? 0 : -ENODEV;
+ }
+
+ /* special tuner handling */
+ c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->radio);
+ if (c && c->driver == NULL)
+ i2c_unregister_device(c);
+ else if (c)
+ cx->i2c_clients[i++] = c;
+ c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->demod);
+ if (c && c->driver == NULL)
+ i2c_unregister_device(c);
+ else if (c)
+ cx->i2c_clients[i++] = c;
+ c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->tv);
+ if (c && c->driver == NULL)
+ i2c_unregister_device(c);
+ else if (c)
+ cx->i2c_clients[i++] = c;
+ return 0;
+}
+
+static int attach_inform(struct i2c_client *client)
+{
+ return 0;
+}
+
+static int detach_inform(struct i2c_client *client)
+{
+ int i;
+ struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter);
+
+ CX18_DEBUG_I2C("i2c client detach\n");
+ for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+ if (cx->i2c_clients[i] == client) {
+ cx->i2c_clients[i] = NULL;
+ break;
+ }
+ }
+ CX18_DEBUG_I2C("i2c detach [client=%s,%s]\n",
+ client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");
+
+ return 0;
+}
+
+static void cx18_setscl(void *data, int state)
+{
+ struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+ int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
+ u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR;
+ u32 r = read_reg(addr);
+
+ if (state)
+ write_reg_sync(r | SETSCL_BIT, addr);
+ else
+ write_reg_sync(r & ~SETSCL_BIT, addr);
+}
+
+static void cx18_setsda(void *data, int state)
+{
+ struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+ int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
+ u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR;
+ u32 r = read_reg(addr);
+
+ if (state)
+ write_reg_sync(r | SETSDL_BIT, addr);
+ else
+ write_reg_sync(r & ~SETSDL_BIT, addr);
+}
+
+static int cx18_getscl(void *data)
+{
+ struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+ int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
+ u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD;
+
+ return read_reg(addr) & GETSCL_BIT;
+}
+
+static int cx18_getsda(void *data)
+{
+ struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+ int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
+ u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD;
+
+ return read_reg(addr) & GETSDL_BIT;
+}
+
+/* template for i2c-bit-algo */
+static struct i2c_adapter cx18_i2c_adap_template = {
+ .name = "cx18 i2c driver",
+ .id = I2C_HW_B_CX2341X,
+ .algo = NULL, /* set by i2c-algo-bit */
+ .algo_data = NULL, /* filled from template */
+ .client_register = attach_inform,
+ .client_unregister = detach_inform,
+ .owner = THIS_MODULE,
+};
+
+#define CX18_SCL_PERIOD (10) /* usecs. 10 usec is period for a 100 KHz clock */
+#define CX18_ALGO_BIT_TIMEOUT (2) /* seconds */
+
+static struct i2c_algo_bit_data cx18_i2c_algo_template = {
+ .setsda = cx18_setsda,
+ .setscl = cx18_setscl,
+ .getsda = cx18_getsda,
+ .getscl = cx18_getscl,
+ .udelay = CX18_SCL_PERIOD/2, /* 1/2 clock period in usec*/
+ .timeout = CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */
+};
+
+static struct i2c_client cx18_i2c_client_template = {
+ .name = "cx18 internal",
+};
+
+int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg)
+{
+ struct i2c_client *client;
+ int retval;
+ int i;
+
+ CX18_DEBUG_I2C("call_i2c_client addr=%02x\n", addr);
+ for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+ client = cx->i2c_clients[i];
+ if (client == NULL || client->driver == NULL ||
+ client->driver->command == NULL)
+ continue;
+ if (addr == client->addr) {
+ retval = client->driver->command(client, cmd, arg);
+ return retval;
+ }
+ }
+ if (cmd != VIDIOC_G_CHIP_IDENT)
+ CX18_ERR("i2c addr 0x%02x not found for cmd 0x%x!\n",
+ addr, cmd);
+ return -ENODEV;
+}
+
+/* Find the i2c device based on the driver ID and return
+ its i2c address or -ENODEV if no matching device was found. */
+static int cx18_i2c_id_addr(struct cx18 *cx, u32 id)
+{
+ struct i2c_client *client;
+ int retval = -ENODEV;
+ int i;
+
+ for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+ client = cx->i2c_clients[i];
+ if (client == NULL || client->driver == NULL)
+ continue;
+ if (id == client->driver->id) {
+ retval = client->addr;
+ break;
+ }
+ }
+ return retval;
+}
+
+/* Find the i2c device name matching the DRIVERID */
+static const char *cx18_i2c_id_name(u32 id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
+ if (hw_driverids[i] == id)
+ return hw_drivernames[i];
+ return "unknown device";
+}
+
+/* Find the i2c device name matching the CX18_HW_ flag */
+static const char *cx18_i2c_hw_name(u32 hw)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
+ if (1 << i == hw)
+ return hw_drivernames[i];
+ return "unknown device";
+}
+
+/* Find the i2c device matching the CX18_HW_ flag and return
+ its i2c address or -ENODEV if no matching device was found. */
+int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
+ if (1 << i == hw)
+ return cx18_i2c_id_addr(cx, hw_driverids[i]);
+ return -ENODEV;
+}
+
+/* Calls i2c device based on CX18_HW_ flag. If hw == 0, then do nothing.
+ If hw == CX18_HW_GPIO then call the gpio handler. */
+int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg)
+{
+ int addr;
+
+ if (hw == CX18_HW_GPIO || hw == 0)
+ return 0;
+ if (hw == CX18_HW_CX23418)
+ return cx18_av_cmd(cx, cmd, arg);
+
+ addr = cx18_i2c_hw_addr(cx, hw);
+ if (addr < 0) {
+ CX18_ERR("i2c hardware 0x%08x (%s) not found for cmd 0x%x!\n",
+ hw, cx18_i2c_hw_name(hw), cmd);
+ return addr;
+ }
+ return cx18_call_i2c_client(cx, addr, cmd, arg);
+}
+
+/* Calls i2c device based on I2C driver ID. */
+int cx18_i2c_id(struct cx18 *cx, u32 id, unsigned int cmd, void *arg)
+{
+ int addr;
+
+ addr = cx18_i2c_id_addr(cx, id);
+ if (addr < 0) {
+ if (cmd != VIDIOC_G_CHIP_IDENT)
+ CX18_ERR("i2c ID 0x%08x (%s) not found for cmd 0x%x!\n",
+ id, cx18_i2c_id_name(id), cmd);
+ return addr;
+ }
+ return cx18_call_i2c_client(cx, addr, cmd, arg);
+}
+
+/* broadcast cmd for all I2C clients and for the gpio subsystem */
+void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg)
+{
+ if (cx->i2c_adap[0].algo == NULL || cx->i2c_adap[1].algo == NULL) {
+ CX18_ERR("adapter is not set\n");
+ return;
+ }
+ cx18_av_cmd(cx, cmd, arg);
+ i2c_clients_command(&cx->i2c_adap[0], cmd, arg);
+ i2c_clients_command(&cx->i2c_adap[1], cmd, arg);
+}
+
+/* init + register i2c algo-bit adapter */
+int init_cx18_i2c(struct cx18 *cx)
+{
+ int i;
+ CX18_DEBUG_I2C("i2c init\n");
+
+ for (i = 0; i < 2; i++) {
+ memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template,
+ sizeof(struct i2c_adapter));
+ memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template,
+ sizeof(struct i2c_algo_bit_data));
+ cx->i2c_algo_cb_data[i].cx = cx;
+ cx->i2c_algo_cb_data[i].bus_index = i;
+ cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i];
+ cx->i2c_adap[i].algo_data = &cx->i2c_algo[i];
+
+ sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name),
+ " #%d-%d", cx->num, i);
+ i2c_set_adapdata(&cx->i2c_adap[i], cx);
+
+ memcpy(&cx->i2c_client[i], &cx18_i2c_client_template,
+ sizeof(struct i2c_client));
+ sprintf(cx->i2c_client[i].name +
+ strlen(cx->i2c_client[i].name), "%d", i);
+ cx->i2c_client[i].adapter = &cx->i2c_adap[i];
+ cx->i2c_adap[i].dev.parent = &cx->dev->dev;
+ }
+
+ if (read_reg(CX18_REG_I2C_2_WR) != 0x0003c02f) {
+ /* Reset/Unreset I2C hardware block */
+ write_reg(0x10000000, 0xc71004); /* Clock select 220MHz */
+ write_reg_sync(0x10001000, 0xc71024); /* Clock Enable */
+ }
+ /* courtesy of Steven Toth <stoth@hauppauge.com> */
+ write_reg_sync(0x00c00000, 0xc7001c);
+ mdelay(10);
+ write_reg_sync(0x00c000c0, 0xc7001c);
+ mdelay(10);
+ write_reg_sync(0x00c00000, 0xc7001c);
+
+ write_reg_sync(0x00c00000, 0xc730c8); /* Set to edge-triggered intrs. */
+ write_reg_sync(0x00c00000, 0xc730c4); /* Clear any stale intrs */
+
+ /* Hw I2C1 Clock Freq ~100kHz */
+ write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_1_WR);
+ cx18_setscl(&cx->i2c_algo_cb_data[0], 1);
+ cx18_setsda(&cx->i2c_algo_cb_data[0], 1);
+
+ /* Hw I2C2 Clock Freq ~100kHz */
+ write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_2_WR);
+ cx18_setscl(&cx->i2c_algo_cb_data[1], 1);
+ cx18_setsda(&cx->i2c_algo_cb_data[1], 1);
+
+ return i2c_bit_add_bus(&cx->i2c_adap[0]) ||
+ i2c_bit_add_bus(&cx->i2c_adap[1]);
+}
+
+void exit_cx18_i2c(struct cx18 *cx)
+{
+ int i;
+ CX18_DEBUG_I2C("i2c exit\n");
+ write_reg(read_reg(CX18_REG_I2C_1_WR) | 4, CX18_REG_I2C_1_WR);
+ write_reg(read_reg(CX18_REG_I2C_2_WR) | 4, CX18_REG_I2C_2_WR);
+
+ for (i = 0; i < 2; i++) {
+ i2c_del_adapter(&cx->i2c_adap[i]);
+ }
+}
+
+/*
+ Hauppauge HVR1600 should have:
+ 32 cx24227
+ 98 unknown
+ a0 eeprom
+ c2 tuner
+ e? zilog ir
+ */
diff --git a/drivers/media/video/cx18/cx18-i2c.h b/drivers/media/video/cx18/cx18-i2c.h
new file mode 100644
index 00000000000..113c3f9a2cc
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-i2c.h
@@ -0,0 +1,33 @@
+/*
+ * cx18 I2C functions
+ *
+ * Derived from ivtv-i2c.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw);
+int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg);
+int cx18_i2c_id(struct cx18 *cx, u32 id, unsigned int cmd, void *arg);
+int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg);
+void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg);
+int cx18_i2c_register(struct cx18 *cx, unsigned idx);
+
+/* init + register i2c algo-bit adapter */
+int init_cx18_i2c(struct cx18 *cx);
+void exit_cx18_i2c(struct cx18 *cx);
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
new file mode 100644
index 00000000000..dbdcb86ec5a
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -0,0 +1,851 @@
+/*
+ * cx18 ioctl system call
+ *
+ * Derived from ivtv-ioctl.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-version.h"
+#include "cx18-mailbox.h"
+#include "cx18-i2c.h"
+#include "cx18-queue.h"
+#include "cx18-fileops.h"
+#include "cx18-vbi.h"
+#include "cx18-audio.h"
+#include "cx18-video.h"
+#include "cx18-streams.h"
+#include "cx18-ioctl.h"
+#include "cx18-gpio.h"
+#include "cx18-controls.h"
+#include "cx18-cards.h"
+#include "cx18-av-core.h"
+#include <media/tveeprom.h>
+#include <media/v4l2-chip-ident.h>
+#include <linux/i2c-id.h>
+
+u16 cx18_service2vbi(int type)
+{
+ switch (type) {
+ case V4L2_SLICED_TELETEXT_B:
+ return CX18_SLICED_TYPE_TELETEXT_B;
+ case V4L2_SLICED_CAPTION_525:
+ return CX18_SLICED_TYPE_CAPTION_525;
+ case V4L2_SLICED_WSS_625:
+ return CX18_SLICED_TYPE_WSS_625;
+ case V4L2_SLICED_VPS:
+ return CX18_SLICED_TYPE_VPS;
+ default:
+ return 0;
+ }
+}
+
+static int valid_service_line(int field, int line, int is_pal)
+{
+ return (is_pal && line >= 6 && (line != 23 || field == 0)) ||
+ (!is_pal && line >= 10 && line < 22);
+}
+
+static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
+{
+ u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
+ int i;
+
+ set = set & valid_set;
+ if (set == 0 || !valid_service_line(field, line, is_pal))
+ return 0;
+ if (!is_pal) {
+ if (line == 21 && (set & V4L2_SLICED_CAPTION_525))
+ return V4L2_SLICED_CAPTION_525;
+ } else {
+ if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))
+ return V4L2_SLICED_VPS;
+ if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))
+ return V4L2_SLICED_WSS_625;
+ if (line == 23)
+ return 0;
+ }
+ for (i = 0; i < 32; i++) {
+ if ((1 << i) & set)
+ return 1 << i;
+ }
+ return 0;
+}
+
+void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+ u16 set = fmt->service_set;
+ int f, l;
+
+ fmt->service_set = 0;
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++)
+ fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);
+ }
+}
+
+static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+ int f, l;
+ u16 set = 0;
+
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
+ set |= fmt->service_lines[f][l];
+ }
+ }
+ return set != 0;
+}
+
+u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt)
+{
+ int f, l;
+ u16 set = 0;
+
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++)
+ set |= fmt->service_lines[f][l];
+ }
+ return set;
+}
+
+static const struct {
+ v4l2_std_id std;
+ char *name;
+} enum_stds[] = {
+ { V4L2_STD_PAL_BG | V4L2_STD_PAL_H, "PAL-BGH" },
+ { V4L2_STD_PAL_DK, "PAL-DK" },
+ { V4L2_STD_PAL_I, "PAL-I" },
+ { V4L2_STD_PAL_M, "PAL-M" },
+ { V4L2_STD_PAL_N, "PAL-N" },
+ { V4L2_STD_PAL_Nc, "PAL-Nc" },
+ { V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, "SECAM-BGH" },
+ { V4L2_STD_SECAM_DK, "SECAM-DK" },
+ { V4L2_STD_SECAM_L, "SECAM-L" },
+ { V4L2_STD_SECAM_LC, "SECAM-L'" },
+ { V4L2_STD_NTSC_M, "NTSC-M" },
+ { V4L2_STD_NTSC_M_JP, "NTSC-J" },
+ { V4L2_STD_NTSC_M_KR, "NTSC-K" },
+};
+
+static const struct v4l2_standard cx18_std_60hz = {
+ .frameperiod = {.numerator = 1001, .denominator = 30000},
+ .framelines = 525,
+};
+
+static const struct v4l2_standard cx18_std_50hz = {
+ .frameperiod = { .numerator = 1, .denominator = 25 },
+ .framelines = 625,
+};
+
+static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
+{
+ struct v4l2_register *regs = arg;
+ unsigned long flags;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
+ return -EINVAL;
+
+ spin_lock_irqsave(&cx18_cards_lock, flags);
+ if (cmd == VIDIOC_DBG_G_REGISTER)
+ regs->val = read_enc(regs->reg);
+ else
+ write_enc(regs->val, regs->reg);
+ spin_unlock_irqrestore(&cx18_cards_lock, flags);
+ return 0;
+}
+
+static int cx18_get_fmt(struct cx18 *cx, int streamtype, struct v4l2_format *fmt)
+{
+ switch (fmt->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ fmt->fmt.pix.width = cx->params.width;
+ fmt->fmt.pix.height = cx->params.height;
+ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ if (streamtype == CX18_ENC_STREAM_TYPE_YUV) {
+ fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
+ /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+ fmt->fmt.pix.sizeimage =
+ fmt->fmt.pix.height * fmt->fmt.pix.width +
+ fmt->fmt.pix.height * (fmt->fmt.pix.width / 2);
+ } else {
+ fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ fmt->fmt.pix.sizeimage = 128 * 1024;
+ }
+ break;
+
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ fmt->fmt.vbi.sampling_rate = 27000000;
+ fmt->fmt.vbi.offset = 248;
+ fmt->fmt.vbi.samples_per_line = cx->vbi.raw_decoder_line_size - 4;
+ fmt->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ fmt->fmt.vbi.start[0] = cx->vbi.start[0];
+ fmt->fmt.vbi.start[1] = cx->vbi.start[1];
+ fmt->fmt.vbi.count[0] = fmt->fmt.vbi.count[1] = cx->vbi.count;
+ break;
+
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ {
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+ memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
+ memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
+
+ cx18_av_cmd(cx, VIDIOC_G_FMT, fmt);
+ vbifmt->service_set = cx18_get_service_set(vbifmt);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cx18_try_or_set_fmt(struct cx18 *cx, int streamtype,
+ struct v4l2_format *fmt, int set_fmt)
+{
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+ u16 set;
+
+ /* set window size */
+ if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ int w = fmt->fmt.pix.width;
+ int h = fmt->fmt.pix.height;
+
+ if (w > 720)
+ w = 720;
+ else if (w < 1)
+ w = 1;
+ if (h > (cx->is_50hz ? 576 : 480))
+ h = (cx->is_50hz ? 576 : 480);
+ else if (h < 2)
+ h = 2;
+ cx18_get_fmt(cx, streamtype, fmt);
+ fmt->fmt.pix.width = w;
+ fmt->fmt.pix.height = h;
+
+ if (!set_fmt || (cx->params.width == w && cx->params.height == h))
+ return 0;
+ if (atomic_read(&cx->capturing) > 0)
+ return -EBUSY;
+
+ cx->params.width = w;
+ cx->params.height = h;
+ if (w != 720 || h != (cx->is_50hz ? 576 : 480))
+ cx->params.video_temporal_filter = 0;
+ else
+ cx->params.video_temporal_filter = 8;
+ cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
+ return cx18_get_fmt(cx, streamtype, fmt);
+ }
+
+ /* set raw VBI format */
+ if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (set_fmt && streamtype == CX18_ENC_STREAM_TYPE_VBI &&
+ cx->vbi.sliced_in->service_set &&
+ atomic_read(&cx->capturing) > 0)
+ return -EBUSY;
+ if (set_fmt) {
+ cx->vbi.sliced_in->service_set = 0;
+ cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
+ }
+ return cx18_get_fmt(cx, streamtype, fmt);
+ }
+
+ /* any else but sliced VBI capture is an error */
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+
+ /* TODO: implement sliced VBI, for now silently return 0 */
+ return 0;
+
+ /* set sliced VBI capture format */
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+ memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
+
+ if (vbifmt->service_set)
+ cx18_expand_service_set(vbifmt, cx->is_50hz);
+ set = check_service_set(vbifmt, cx->is_50hz);
+ vbifmt->service_set = cx18_get_service_set(vbifmt);
+
+ if (!set_fmt)
+ return 0;
+ if (set == 0)
+ return -EINVAL;
+ if (atomic_read(&cx->capturing) > 0 && cx->vbi.sliced_in->service_set == 0)
+ return -EBUSY;
+ cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
+ memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
+ return 0;
+}
+
+static int cx18_debug_ioctls(struct file *filp, unsigned int cmd, void *arg)
+{
+ struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data;
+ struct cx18 *cx = id->cx;
+ struct v4l2_register *reg = arg;
+
+ switch (cmd) {
+ /* ioctls to allow direct access to the encoder registers for testing */
+ case VIDIOC_DBG_G_REGISTER:
+ if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return cx18_cxc(cx, cmd, arg);
+ if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+ return cx18_i2c_id(cx, reg->match_chip, cmd, arg);
+ return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg);
+
+ case VIDIOC_DBG_S_REGISTER:
+ if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return cx18_cxc(cx, cmd, arg);
+ if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+ return cx18_i2c_id(cx, reg->match_chip, cmd, arg);
+ return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg);
+
+ case VIDIOC_G_CHIP_IDENT: {
+ struct v4l2_chip_ident *chip = arg;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ if (reg->match_type == V4L2_CHIP_MATCH_HOST) {
+ if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) {
+ struct v4l2_chip_ident *chip = arg;
+
+ chip->ident = V4L2_IDENT_CX23418;
+ }
+ return 0;
+ }
+ if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
+ return cx18_i2c_id(cx, reg->match_chip, cmd, arg);
+ if (reg->match_type == V4L2_CHIP_MATCH_I2C_ADDR)
+ return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg);
+ return -EINVAL;
+ }
+
+ case VIDIOC_INT_S_AUDIO_ROUTING: {
+ struct v4l2_routing *route = arg;
+
+ cx18_audio_set_route(cx, route);
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int cx18_v4l2_ioctls(struct cx18 *cx, struct file *filp, unsigned cmd, void *arg)
+{
+ struct cx18_open_id *id = NULL;
+
+ if (filp)
+ id = (struct cx18_open_id *)filp->private_data;
+
+ switch (cmd) {
+ case VIDIOC_G_PRIORITY:
+ {
+ enum v4l2_priority *p = arg;
+
+ *p = v4l2_prio_max(&cx->prio);
+ break;
+ }
+
+ case VIDIOC_S_PRIORITY:
+ {
+ enum v4l2_priority *prio = arg;
+
+ return v4l2_prio_change(&cx->prio, &id->prio, *prio);
+ }
+
+ case VIDIOC_QUERYCAP:{
+ struct v4l2_capability *vcap = arg;
+
+ memset(vcap, 0, sizeof(*vcap));
+ strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
+ strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));
+ strlcpy(vcap->bus_info, pci_name(cx->dev), sizeof(vcap->bus_info));
+ vcap->version = CX18_DRIVER_VERSION; /* version */
+ vcap->capabilities = cx->v4l2_cap; /* capabilities */
+
+ /* reserved.. must set to 0! */
+ vcap->reserved[0] = vcap->reserved[1] =
+ vcap->reserved[2] = vcap->reserved[3] = 0;
+ break;
+ }
+
+ case VIDIOC_ENUMAUDIO:{
+ struct v4l2_audio *vin = arg;
+
+ return cx18_get_audio_input(cx, vin->index, vin);
+ }
+
+ case VIDIOC_G_AUDIO:{
+ struct v4l2_audio *vin = arg;
+
+ vin->index = cx->audio_input;
+ return cx18_get_audio_input(cx, vin->index, vin);
+ }
+
+ case VIDIOC_S_AUDIO:{
+ struct v4l2_audio *vout = arg;
+
+ if (vout->index >= cx->nof_audio_inputs)
+ return -EINVAL;
+ cx->audio_input = vout->index;
+ cx18_audio_set_io(cx);
+ break;
+ }
+
+ case VIDIOC_ENUMINPUT:{
+ struct v4l2_input *vin = arg;
+
+ /* set it to defaults from our table */
+ return cx18_get_input(cx, vin->index, vin);
+ }
+
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_S_FMT: {
+ struct v4l2_format *fmt = arg;
+
+ return cx18_try_or_set_fmt(cx, id->type, fmt, cmd == VIDIOC_S_FMT);
+ }
+
+ case VIDIOC_G_FMT: {
+ struct v4l2_format *fmt = arg;
+ int type = fmt->type;
+
+ memset(fmt, 0, sizeof(*fmt));
+ fmt->type = type;
+ return cx18_get_fmt(cx, id->type, fmt);
+ }
+
+ case VIDIOC_CROPCAP: {
+ struct v4l2_cropcap *cropcap = arg;
+
+ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ cropcap->bounds.top = cropcap->bounds.left = 0;
+ cropcap->bounds.width = 720;
+ cropcap->bounds.height = cx->is_50hz ? 576 : 480;
+ cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10;
+ cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11;
+ cropcap->defrect = cropcap->bounds;
+ return 0;
+ }
+
+ case VIDIOC_S_CROP: {
+ struct v4l2_crop *crop = arg;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ return cx18_av_cmd(cx, VIDIOC_S_CROP, arg);
+ }
+
+ case VIDIOC_G_CROP: {
+ struct v4l2_crop *crop = arg;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ return cx18_av_cmd(cx, VIDIOC_G_CROP, arg);
+ }
+
+ case VIDIOC_ENUM_FMT: {
+ static struct v4l2_fmtdesc formats[] = {
+ { 0, 0, 0,
+ "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12,
+ { 0, 0, 0, 0 }
+ },
+ { 1, 0, V4L2_FMT_FLAG_COMPRESSED,
+ "MPEG", V4L2_PIX_FMT_MPEG,
+ { 0, 0, 0, 0 }
+ }
+ };
+ struct v4l2_fmtdesc *fmt = arg;
+ enum v4l2_buf_type type = fmt->type;
+
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (fmt->index > 1)
+ return -EINVAL;
+ *fmt = formats[fmt->index];
+ fmt->type = type;
+ return 0;
+ }
+
+ case VIDIOC_G_INPUT:{
+ *(int *)arg = cx->active_input;
+ break;
+ }
+
+ case VIDIOC_S_INPUT:{
+ int inp = *(int *)arg;
+
+ if (inp < 0 || inp >= cx->nof_inputs)
+ return -EINVAL;
+
+ if (inp == cx->active_input) {
+ CX18_DEBUG_INFO("Input unchanged\n");
+ break;
+ }
+ CX18_DEBUG_INFO("Changing input from %d to %d\n",
+ cx->active_input, inp);
+
+ cx->active_input = inp;
+ /* Set the audio input to whatever is appropriate for the
+ input type. */
+ cx->audio_input = cx->card->video_inputs[inp].audio_index;
+
+ /* prevent others from messing with the streams until
+ we're finished changing inputs. */
+ cx18_mute(cx);
+ cx18_video_set_io(cx);
+ cx18_audio_set_io(cx);
+ cx18_unmute(cx);
+ break;
+ }
+
+ case VIDIOC_G_FREQUENCY:{
+ struct v4l2_frequency *vf = arg;
+
+ if (vf->tuner != 0)
+ return -EINVAL;
+ cx18_call_i2c_clients(cx, cmd, arg);
+ break;
+ }
+
+ case VIDIOC_S_FREQUENCY:{
+ struct v4l2_frequency vf = *(struct v4l2_frequency *)arg;
+
+ if (vf.tuner != 0)
+ return -EINVAL;
+
+ cx18_mute(cx);
+ CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf.frequency);
+ cx18_call_i2c_clients(cx, cmd, &vf);
+ cx18_unmute(cx);
+ break;
+ }
+
+ case VIDIOC_ENUMSTD:{
+ struct v4l2_standard *vs = arg;
+ int idx = vs->index;
+
+ if (idx < 0 || idx >= ARRAY_SIZE(enum_stds))
+ return -EINVAL;
+
+ *vs = (enum_stds[idx].std & V4L2_STD_525_60) ?
+ cx18_std_60hz : cx18_std_50hz;
+ vs->index = idx;
+ vs->id = enum_stds[idx].std;
+ strlcpy(vs->name, enum_stds[idx].name, sizeof(vs->name));
+ break;
+ }
+
+ case VIDIOC_G_STD:{
+ *(v4l2_std_id *) arg = cx->std;
+ break;
+ }
+
+ case VIDIOC_S_STD: {
+ v4l2_std_id std = *(v4l2_std_id *) arg;
+
+ if ((std & V4L2_STD_ALL) == 0)
+ return -EINVAL;
+
+ if (std == cx->std)
+ break;
+
+ if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ||
+ atomic_read(&cx->capturing) > 0) {
+ /* Switching standard would turn off the radio or mess
+ with already running streams, prevent that by
+ returning EBUSY. */
+ return -EBUSY;
+ }
+
+ cx->std = std;
+ cx->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0;
+ cx->params.is_50hz = cx->is_50hz = !cx->is_60hz;
+ cx->params.width = 720;
+ cx->params.height = cx->is_50hz ? 576 : 480;
+ cx->vbi.count = cx->is_50hz ? 18 : 12;
+ cx->vbi.start[0] = cx->is_50hz ? 6 : 10;
+ cx->vbi.start[1] = cx->is_50hz ? 318 : 273;
+ cx->vbi.sliced_decoder_line_size = cx->is_60hz ? 272 : 284;
+ CX18_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)cx->std);
+
+ /* Tuner */
+ cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
+ break;
+ }
+
+ case VIDIOC_S_TUNER: { /* Setting tuner can only set audio mode */
+ struct v4l2_tuner *vt = arg;
+
+ if (vt->index != 0)
+ return -EINVAL;
+
+ cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt);
+ break;
+ }
+
+ case VIDIOC_G_TUNER: {
+ struct v4l2_tuner *vt = arg;
+
+ if (vt->index != 0)
+ return -EINVAL;
+
+ memset(vt, 0, sizeof(*vt));
+ cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, vt);
+
+ if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
+ strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
+ vt->type = V4L2_TUNER_RADIO;
+ } else {
+ strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name));
+ vt->type = V4L2_TUNER_ANALOG_TV;
+ }
+ break;
+ }
+
+ case VIDIOC_G_SLICED_VBI_CAP: {
+ struct v4l2_sliced_vbi_cap *cap = arg;
+ int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
+ int f, l;
+ enum v4l2_buf_type type = cap->type;
+
+ memset(cap, 0, sizeof(*cap));
+ cap->type = type;
+ if (type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ if (valid_service_line(f, l, cx->is_50hz))
+ cap->service_lines[f][l] = set;
+ }
+ }
+ return 0;
+ }
+ return -EINVAL;
+ }
+
+ case VIDIOC_ENCODER_CMD:
+ case VIDIOC_TRY_ENCODER_CMD: {
+ struct v4l2_encoder_cmd *enc = arg;
+ int try = cmd == VIDIOC_TRY_ENCODER_CMD;
+
+ memset(&enc->raw, 0, sizeof(enc->raw));
+ switch (enc->cmd) {
+ case V4L2_ENC_CMD_START:
+ enc->flags = 0;
+ if (try)
+ return 0;
+ return cx18_start_capture(id);
+
+ case V4L2_ENC_CMD_STOP:
+ enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+ if (try)
+ return 0;
+ cx18_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
+ return 0;
+
+ case V4L2_ENC_CMD_PAUSE:
+ enc->flags = 0;
+ if (try)
+ return 0;
+ if (!atomic_read(&cx->capturing))
+ return -EPERM;
+ if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
+ return 0;
+ cx18_mute(cx);
+ cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, cx18_find_handle(cx));
+ break;
+
+ case V4L2_ENC_CMD_RESUME:
+ enc->flags = 0;
+ if (try)
+ return 0;
+ if (!atomic_read(&cx->capturing))
+ return -EPERM;
+ if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
+ return 0;
+ cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, cx18_find_handle(cx));
+ cx18_unmute(cx);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ }
+
+ case VIDIOC_LOG_STATUS:
+ {
+ struct v4l2_input vidin;
+ struct v4l2_audio audin;
+ int i;
+
+ CX18_INFO("================= START STATUS CARD #%d =================\n", cx->num);
+ if (cx->hw_flags & CX18_HW_TVEEPROM) {
+ struct tveeprom tv;
+
+ cx18_read_eeprom(cx, &tv);
+ }
+ cx18_call_i2c_clients(cx, VIDIOC_LOG_STATUS, NULL);
+ cx18_get_input(cx, cx->active_input, &vidin);
+ cx18_get_audio_input(cx, cx->audio_input, &audin);
+ CX18_INFO("Video Input: %s\n", vidin.name);
+ CX18_INFO("Audio Input: %s\n", audin.name);
+ CX18_INFO("Tuner: %s\n",
+ test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ?
+ "Radio" : "TV");
+ cx2341x_log_status(&cx->params, cx->name);
+ CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags);
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ struct cx18_stream *s = &cx->streams[i];
+
+ if (s->v4l2dev == NULL || s->buffers == 0)
+ continue;
+ CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
+ s->name, s->s_flags,
+ (s->buffers - s->q_free.buffers) * 100 / s->buffers,
+ (s->buffers * s->buf_size) / 1024, s->buffers);
+ }
+ CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
+ (long long)cx->mpg_data_received,
+ (long long)cx->vbi_data_inserted);
+ CX18_INFO("================== END STATUS CARD #%d ==================\n", cx->num);
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cx18_v4l2_do_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, void *arg)
+{
+ struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data;
+ struct cx18 *cx = id->cx;
+ int ret;
+
+ /* check priority */
+ switch (cmd) {
+ case VIDIOC_S_CTRL:
+ case VIDIOC_S_STD:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_S_FREQUENCY:
+ case VIDIOC_S_FMT:
+ case VIDIOC_S_CROP:
+ case VIDIOC_S_EXT_CTRLS:
+ ret = v4l2_prio_check(&cx->prio, &id->prio);
+ if (ret)
+ return ret;
+ }
+
+ switch (cmd) {
+ case VIDIOC_DBG_G_REGISTER:
+ case VIDIOC_DBG_S_REGISTER:
+ case VIDIOC_G_CHIP_IDENT:
+ case VIDIOC_INT_S_AUDIO_ROUTING:
+ case VIDIOC_INT_RESET:
+ if (cx18_debug & CX18_DBGFLG_IOCTL) {
+ printk(KERN_INFO "cx18%d ioctl: ", cx->num);
+ v4l_printk_ioctl(cmd);
+ }
+ return cx18_debug_ioctls(filp, cmd, arg);
+
+ case VIDIOC_G_PRIORITY:
+ case VIDIOC_S_PRIORITY:
+ case VIDIOC_QUERYCAP:
+ case VIDIOC_ENUMINPUT:
+ case VIDIOC_G_INPUT:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_G_FMT:
+ case VIDIOC_S_FMT:
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_CROPCAP:
+ case VIDIOC_G_CROP:
+ case VIDIOC_S_CROP:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY:
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_ENUMAUDIO:
+ case VIDIOC_S_AUDIO:
+ case VIDIOC_G_AUDIO:
+ case VIDIOC_G_SLICED_VBI_CAP:
+ case VIDIOC_LOG_STATUS:
+ case VIDIOC_G_ENC_INDEX:
+ case VIDIOC_ENCODER_CMD:
+ case VIDIOC_TRY_ENCODER_CMD:
+ if (cx18_debug & CX18_DBGFLG_IOCTL) {
+ printk(KERN_INFO "cx18%d ioctl: ", cx->num);
+ v4l_printk_ioctl(cmd);
+ }
+ return cx18_v4l2_ioctls(cx, filp, cmd, arg);
+
+ case VIDIOC_QUERYMENU:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_S_CTRL:
+ case VIDIOC_G_CTRL:
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_G_EXT_CTRLS:
+ case VIDIOC_TRY_EXT_CTRLS:
+ if (cx18_debug & CX18_DBGFLG_IOCTL) {
+ printk(KERN_INFO "cx18%d ioctl: ", cx->num);
+ v4l_printk_ioctl(cmd);
+ }
+ return cx18_control_ioctls(cx, cmd, arg);
+
+ case 0x00005401: /* Handle isatty() calls */
+ return -EINVAL;
+ default:
+ return v4l_compat_translate_ioctl(inode, filp, cmd, arg,
+ cx18_v4l2_do_ioctl);
+ }
+ return 0;
+}
+
+int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data;
+ struct cx18 *cx = id->cx;
+ int res;
+
+ mutex_lock(&cx->serialize_lock);
+ res = video_usercopy(inode, filp, cmd, arg, cx18_v4l2_do_ioctl);
+ mutex_unlock(&cx->serialize_lock);
+ return res;
+}
diff --git a/drivers/media/video/cx18/cx18-ioctl.h b/drivers/media/video/cx18/cx18-ioctl.h
new file mode 100644
index 00000000000..9f4c7eb2897
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-ioctl.h
@@ -0,0 +1,30 @@
+/*
+ * cx18 ioctl system call
+ *
+ * Derived from ivtv-ioctl.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+u16 cx18_service2vbi(int type);
+void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal);
+u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt);
+int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg);
+int cx18_v4l2_ioctls(struct cx18 *cx, struct file *filp, unsigned cmd,
+ void *arg);
diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c
new file mode 100644
index 00000000000..6e14f8bda55
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-irq.c
@@ -0,0 +1,179 @@
+/*
+ * cx18 interrupt handling
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-firmware.h"
+#include "cx18-fileops.h"
+#include "cx18-queue.h"
+#include "cx18-irq.h"
+#include "cx18-ioctl.h"
+#include "cx18-mailbox.h"
+#include "cx18-vbi.h"
+#include "cx18-scb.h"
+
+#define DMA_MAGIC_COOKIE 0x000001fe
+
+static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb)
+{
+ u32 handle = mb->args[0];
+ struct cx18_stream *s = NULL;
+ struct cx18_buffer *buf;
+ u32 off;
+ int i;
+ int id;
+
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ s = &cx->streams[i];
+ if ((handle == s->handle) && (s->dvb.enabled))
+ break;
+ if (s->v4l2dev && handle == s->handle)
+ break;
+ }
+ if (i == CX18_MAX_STREAMS) {
+ CX18_WARN("DMA done for unknown handle %d for stream %s\n",
+ handle, s->name);
+ mb->error = CXERR_NOT_OPEN;
+ mb->cmd = 0;
+ cx18_mb_ack(cx, mb);
+ return;
+ }
+
+ off = mb->args[1];
+ if (mb->args[2] != 1)
+ CX18_WARN("Ack struct = %d for %s\n",
+ mb->args[2], s->name);
+ id = read_enc(off);
+ buf = cx18_queue_find_buf(s, id, read_enc(off + 4));
+ CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id);
+ if (buf) {
+ cx18_buf_sync_for_cpu(s, buf);
+ if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) {
+ /* process the buffer here */
+ CX18_DEBUG_HI_DMA("TS recv and sent bytesused=%d\n",
+ buf->bytesused);
+
+ dvb_dmx_swfilter(&s->dvb.demux, buf->buf,
+ buf->bytesused);
+
+ cx18_buf_sync_for_device(s, buf);
+ cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
+ (void *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
+ 1, buf->id, s->buf_size);
+ } else
+ set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags);
+ } else {
+ CX18_WARN("Could not find buf %d for stream %s\n",
+ read_enc(off), s->name);
+ }
+ mb->error = 0;
+ mb->cmd = 0;
+ cx18_mb_ack(cx, mb);
+ wake_up(&cx->dma_waitq);
+ if (s->id != -1)
+ wake_up(&s->waitq);
+}
+
+static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb)
+{
+ char str[256] = { 0 };
+ char *p;
+
+ if (mb->args[1]) {
+ setup_page(mb->args[1]);
+ memcpy_fromio(str, cx->enc_mem + mb->args[1], 252);
+ str[252] = 0;
+ }
+ cx18_mb_ack(cx, mb);
+ CX18_DEBUG_INFO("%x %s\n", mb->args[0], str);
+ p = strchr(str, '.');
+ if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str)
+ CX18_INFO("FW version: %s\n", p - 1);
+}
+
+static void hpu_cmd(struct cx18 *cx, u32 sw1)
+{
+ struct cx18_mailbox mb;
+
+ if (sw1 & IRQ_CPU_TO_EPU) {
+ memcpy_fromio(&mb, &cx->scb->cpu2epu_mb, sizeof(mb));
+ mb.error = 0;
+
+ switch (mb.cmd) {
+ case CX18_EPU_DMA_DONE:
+ epu_dma_done(cx, &mb);
+ break;
+ case CX18_EPU_DEBUG:
+ epu_debug(cx, &mb);
+ break;
+ default:
+ CX18_WARN("Unexpected mailbox command %08x\n", mb.cmd);
+ break;
+ }
+ }
+ if (sw1 & (IRQ_APU_TO_EPU | IRQ_HPU_TO_EPU))
+ CX18_WARN("Unexpected interrupt %08x\n", sw1);
+}
+
+irqreturn_t cx18_irq_handler(int irq, void *dev_id)
+{
+ struct cx18 *cx = (struct cx18 *)dev_id;
+ u32 sw1, sw1_mask;
+ u32 sw2, sw2_mask;
+ u32 hw2, hw2_mask;
+
+ spin_lock(&cx->dma_reg_lock);
+
+ hw2_mask = read_reg(HW2_INT_MASK5_PCI);
+ hw2 = read_reg(HW2_INT_CLR_STATUS) & hw2_mask;
+ sw2_mask = read_reg(SW2_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU_ACK;
+ sw2 = read_reg(SW2_INT_STATUS) & sw2_mask;
+ sw1_mask = read_reg(SW1_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU;
+ sw1 = read_reg(SW1_INT_STATUS) & sw1_mask;
+
+ write_reg(sw2&sw2_mask, SW2_INT_STATUS);
+ write_reg(sw1&sw1_mask, SW1_INT_STATUS);
+ write_reg(hw2&hw2_mask, HW2_INT_CLR_STATUS);
+
+ if (sw1 || sw2 || hw2)
+ CX18_DEBUG_HI_IRQ("SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2);
+
+ /* To do: interrupt-based I2C handling
+ if (hw2 & 0x00c00000) {
+ }
+ */
+
+ if (sw2) {
+ if (sw2 & (cx->scb->cpu2hpu_irq_ack | cx->scb->cpu2epu_irq_ack))
+ wake_up(&cx->mb_cpu_waitq);
+ if (sw2 & (cx->scb->apu2hpu_irq_ack | cx->scb->apu2epu_irq_ack))
+ wake_up(&cx->mb_apu_waitq);
+ if (sw2 & cx->scb->epu2hpu_irq_ack)
+ wake_up(&cx->mb_epu_waitq);
+ if (sw2 & cx->scb->hpu2epu_irq_ack)
+ wake_up(&cx->mb_hpu_waitq);
+ }
+
+ if (sw1)
+ hpu_cmd(cx, sw1);
+ spin_unlock(&cx->dma_reg_lock);
+
+ return (hw2 | sw1 | sw2) ? IRQ_HANDLED : IRQ_NONE;
+}
diff --git a/drivers/media/video/cx18/cx18-irq.h b/drivers/media/video/cx18/cx18-irq.h
new file mode 100644
index 00000000000..379f704f5cb
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-irq.h
@@ -0,0 +1,37 @@
+/*
+ * cx18 interrupt handling
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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 HW2_I2C1_INT (1 << 22)
+#define HW2_I2C2_INT (1 << 23)
+#define HW2_INT_CLR_STATUS 0xc730c4
+#define HW2_INT_MASK5_PCI 0xc730e4
+#define SW1_INT_SET 0xc73100
+#define SW1_INT_STATUS 0xc73104
+#define SW1_INT_ENABLE_PCI 0xc7311c
+#define SW2_INT_SET 0xc73140
+#define SW2_INT_STATUS 0xc73144
+#define SW2_INT_ENABLE_PCI 0xc7315c
+
+irqreturn_t cx18_irq_handler(int irq, void *dev_id);
+
+void cx18_irq_work_handler(struct work_struct *work);
+void cx18_dma_stream_dec_prepare(struct cx18_stream *s, u32 offset, int lock);
+void cx18_unfinished_dma(unsigned long arg);
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
new file mode 100644
index 00000000000..0c5f328bca5
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -0,0 +1,372 @@
+/*
+ * cx18 mailbox functions
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#include <stdarg.h>
+
+#include "cx18-driver.h"
+#include "cx18-scb.h"
+#include "cx18-irq.h"
+#include "cx18-mailbox.h"
+
+#define API_FAST (1 << 2) /* Short timeout */
+#define API_SLOW (1 << 3) /* Additional 300ms timeout */
+
+#define APU 0
+#define CPU 1
+#define EPU 2
+#define HPU 3
+
+struct cx18_api_info {
+ u32 cmd;
+ u8 flags; /* Flags, see above */
+ u8 rpu; /* Processing unit */
+ const char *name; /* The name of the command */
+};
+
+#define API_ENTRY(rpu, x, f) { (x), (f), (rpu), #x }
+
+static const struct cx18_api_info api_info[] = {
+ /* MPEG encoder API */
+ API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0),
+ API_ENTRY(CPU, CX18_EPU_DEBUG, 0),
+ API_ENTRY(CPU, CX18_CREATE_TASK, 0),
+ API_ENTRY(CPU, CX18_DESTROY_TASK, 0),
+ API_ENTRY(CPU, CX18_CPU_CAPTURE_START, API_SLOW),
+ API_ENTRY(CPU, CX18_CPU_CAPTURE_STOP, API_SLOW),
+ API_ENTRY(CPU, CX18_CPU_CAPTURE_PAUSE, 0),
+ API_ENTRY(CPU, CX18_CPU_CAPTURE_RESUME, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_VIDEO_IN, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RATE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RESOLUTION, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_FILTER_PARAM, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_MEDIAN_CORING, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_INDEXTABLE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PARAMETERS, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_VIDEO_MUTE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_AUDIO_MUTE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_MISC_PARAMETERS, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_RAW_VBI_PARAM, API_SLOW),
+ API_ENTRY(CPU, CX18_CPU_SET_CAPTURE_LINE_NO, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_COPYRIGHT, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PID, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_VIDEO_PID, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_VER_CROP_LINE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_GOP_STRUCTURE, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_SCENE_CHANGE_DETECTION, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_ASPECT_RATIO, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_SKIP_INPUT_FRAME, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM, 0),
+ API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER, 0),
+ API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0),
+ API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0),
+ API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST),
+ API_ENTRY(0, 0, 0),
+};
+
+static const struct cx18_api_info *find_api_info(u32 cmd)
+{
+ int i;
+
+ for (i = 0; api_info[i].cmd; i++)
+ if (api_info[i].cmd == cmd)
+ return &api_info[i];
+ return NULL;
+}
+
+static struct cx18_mailbox *cx18_mb_is_complete(struct cx18 *cx, int rpu,
+ u32 *state, u32 *irq, u32 *req)
+{
+ struct cx18_mailbox *mb = NULL;
+ int wait_count = 0;
+ u32 ack;
+
+ switch (rpu) {
+ case APU:
+ mb = &cx->scb->epu2apu_mb;
+ *state = readl(&cx->scb->apu_state);
+ *irq = readl(&cx->scb->epu2apu_irq);
+ break;
+
+ case CPU:
+ mb = &cx->scb->epu2cpu_mb;
+ *state = readl(&cx->scb->cpu_state);
+ *irq = readl(&cx->scb->epu2cpu_irq);
+ break;
+
+ case HPU:
+ mb = &cx->scb->epu2hpu_mb;
+ *state = readl(&cx->scb->hpu_state);
+ *irq = readl(&cx->scb->epu2hpu_irq);
+ break;
+ }
+
+ if (mb == NULL)
+ return mb;
+
+ do {
+ *req = readl(&mb->request);
+ ack = readl(&mb->ack);
+ wait_count++;
+ } while (*req != ack && wait_count < 600);
+
+ if (*req == ack) {
+ (*req)++;
+ if (*req == 0 || *req == 0xffffffff)
+ *req = 1;
+ return mb;
+ }
+ return NULL;
+}
+
+long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb)
+{
+ const struct cx18_api_info *info = find_api_info(mb->cmd);
+ struct cx18_mailbox *ack_mb;
+ u32 ack_irq;
+ u8 rpu = CPU;
+
+ if (info == NULL && mb->cmd) {
+ CX18_WARN("Cannot ack unknown command %x\n", mb->cmd);
+ return -EINVAL;
+ }
+ if (info)
+ rpu = info->rpu;
+
+ switch (rpu) {
+ case HPU:
+ ack_irq = IRQ_EPU_TO_HPU_ACK;
+ ack_mb = &cx->scb->hpu2epu_mb;
+ break;
+ case APU:
+ ack_irq = IRQ_EPU_TO_APU_ACK;
+ ack_mb = &cx->scb->apu2epu_mb;
+ break;
+ case CPU:
+ ack_irq = IRQ_EPU_TO_CPU_ACK;
+ ack_mb = &cx->scb->cpu2epu_mb;
+ break;
+ default:
+ CX18_WARN("Unknown RPU for command %x\n", mb->cmd);
+ return -EINVAL;
+ }
+
+ setup_page(SCB_OFFSET);
+ write_sync(mb->request, &ack_mb->ack);
+ write_reg(ack_irq, SW2_INT_SET);
+ return 0;
+}
+
+
+static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
+{
+ const struct cx18_api_info *info = find_api_info(cmd);
+ u32 state = 0, irq = 0, req, oldreq, err;
+ struct cx18_mailbox *mb;
+ wait_queue_head_t *waitq;
+ int timeout = 100;
+ int cnt = 0;
+ int sig = 0;
+ int i;
+
+ if (info == NULL) {
+ CX18_WARN("unknown cmd %x\n", cmd);
+ return -EINVAL;
+ }
+
+ if (cmd == CX18_CPU_DE_SET_MDL)
+ CX18_DEBUG_HI_API("%s\n", info->name);
+ else
+ CX18_DEBUG_API("%s\n", info->name);
+ setup_page(SCB_OFFSET);
+ mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req);
+
+ if (mb == NULL) {
+ CX18_ERR("mb %s busy\n", info->name);
+ return -EBUSY;
+ }
+
+ oldreq = req - 1;
+ writel(cmd, &mb->cmd);
+ for (i = 0; i < args; i++)
+ writel(data[i], &mb->args[i]);
+ writel(0, &mb->error);
+ writel(req, &mb->request);
+
+ switch (info->rpu) {
+ case APU: waitq = &cx->mb_apu_waitq; break;
+ case CPU: waitq = &cx->mb_cpu_waitq; break;
+ case EPU: waitq = &cx->mb_epu_waitq; break;
+ case HPU: waitq = &cx->mb_hpu_waitq; break;
+ default: return -EINVAL;
+ }
+ if (info->flags & API_FAST)
+ timeout /= 2;
+ write_reg(irq, SW1_INT_SET);
+
+ while (!sig && readl(&mb->ack) != readl(&mb->request) && cnt < 660) {
+ if (cnt > 200 && !in_atomic())
+ sig = cx18_msleep_timeout(10, 1);
+ cnt++;
+ }
+ if (sig)
+ return -EINTR;
+ if (cnt == 660) {
+ writel(oldreq, &mb->request);
+ CX18_ERR("mb %s failed\n", info->name);
+ return -EINVAL;
+ }
+ for (i = 0; i < MAX_MB_ARGUMENTS; i++)
+ data[i] = readl(&mb->args[i]);
+ err = readl(&mb->error);
+ if (!in_atomic() && (info->flags & API_SLOW))
+ cx18_msleep_timeout(300, 0);
+ if (err)
+ CX18_DEBUG_API("mailbox error %08x for command %s\n", err,
+ info->name);
+ return err ? -EIO : 0;
+}
+
+int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[])
+{
+ int res = cx18_api_call(cx, cmd, args, data);
+
+ /* Allow a single retry, probably already too late though.
+ If there is no free mailbox then that is usually an indication
+ of a more serious problem. */
+ return (res == -EBUSY) ? cx18_api_call(cx, cmd, args, data) : res;
+}
+
+static int cx18_set_filter_param(struct cx18_stream *s)
+{
+ struct cx18 *cx = s->cx;
+ u32 mode;
+ int ret;
+
+ mode = (cx->filter_mode & 1) ? 2 : (cx->spatial_strength ? 1 : 0);
+ ret = cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
+ s->handle, 1, mode, cx->spatial_strength);
+ mode = (cx->filter_mode & 2) ? 2 : (cx->temporal_strength ? 1 : 0);
+ ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
+ s->handle, 0, mode, cx->temporal_strength);
+ ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
+ s->handle, 2, cx->filter_mode >> 2, 0);
+ return ret;
+}
+
+int cx18_api_func(void *priv, u32 cmd, int in, int out,
+ u32 data[CX2341X_MBOX_MAX_DATA])
+{
+ struct cx18 *cx = priv;
+ struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+
+ switch (cmd) {
+ case CX2341X_ENC_SET_OUTPUT_PORT:
+ return 0;
+ case CX2341X_ENC_SET_FRAME_RATE:
+ return cx18_vapi(cx, CX18_CPU_SET_VIDEO_IN, 6,
+ s->handle, 0, 0, 0, 0, data[0]);
+ case CX2341X_ENC_SET_FRAME_SIZE:
+ return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RESOLUTION, 3,
+ s->handle, data[1], data[0]);
+ case CX2341X_ENC_SET_STREAM_TYPE:
+ return cx18_vapi(cx, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 2,
+ s->handle, data[0]);
+ case CX2341X_ENC_SET_ASPECT_RATIO:
+ return cx18_vapi(cx, CX18_CPU_SET_ASPECT_RATIO, 2,
+ s->handle, data[0]);
+
+ case CX2341X_ENC_SET_GOP_PROPERTIES:
+ return cx18_vapi(cx, CX18_CPU_SET_GOP_STRUCTURE, 3,
+ s->handle, data[0], data[1]);
+ case CX2341X_ENC_SET_GOP_CLOSURE:
+ return 0;
+ case CX2341X_ENC_SET_AUDIO_PROPERTIES:
+ return cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2,
+ s->handle, data[0]);
+ case CX2341X_ENC_MUTE_AUDIO:
+ return cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
+ s->handle, data[0]);
+ case CX2341X_ENC_SET_BIT_RATE:
+ return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RATE, 5,
+ s->handle, data[0], data[1], data[2], data[3]);
+ case CX2341X_ENC_MUTE_VIDEO:
+ return cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2,
+ s->handle, data[0]);
+ case CX2341X_ENC_SET_FRAME_DROP_RATE:
+ return cx18_vapi(cx, CX18_CPU_SET_SKIP_INPUT_FRAME, 2,
+ s->handle, data[0]);
+ case CX2341X_ENC_MISC:
+ return cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 4,
+ s->handle, data[0], data[1], data[2]);
+ case CX2341X_ENC_SET_DNR_FILTER_MODE:
+ cx->filter_mode = (data[0] & 3) | (data[1] << 2);
+ return cx18_set_filter_param(s);
+ case CX2341X_ENC_SET_DNR_FILTER_PROPS:
+ cx->spatial_strength = data[0];
+ cx->temporal_strength = data[1];
+ return cx18_set_filter_param(s);
+ case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE:
+ return cx18_vapi(cx, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 3,
+ s->handle, data[0], data[1]);
+ case CX2341X_ENC_SET_CORING_LEVELS:
+ return cx18_vapi(cx, CX18_CPU_SET_MEDIAN_CORING, 5,
+ s->handle, data[0], data[1], data[2], data[3]);
+ }
+ CX18_WARN("Unknown cmd %x\n", cmd);
+ return 0;
+}
+
+int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS],
+ u32 cmd, int args, ...)
+{
+ va_list ap;
+ int i;
+
+ va_start(ap, args);
+ for (i = 0; i < args; i++)
+ data[i] = va_arg(ap, u32);
+ va_end(ap);
+ return cx18_api(cx, cmd, args, data);
+}
+
+int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...)
+{
+ u32 data[MAX_MB_ARGUMENTS];
+ va_list ap;
+ int i;
+
+ if (cx == NULL) {
+ CX18_ERR("cx == NULL (cmd=%x)\n", cmd);
+ return 0;
+ }
+ if (args > MAX_MB_ARGUMENTS) {
+ CX18_ERR("args too big (cmd=%x)\n", cmd);
+ args = MAX_MB_ARGUMENTS;
+ }
+ va_start(ap, args);
+ for (i = 0; i < args; i++)
+ data[i] = va_arg(ap, u32);
+ va_end(ap);
+ return cx18_api(cx, cmd, args, data);
+}
diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h
new file mode 100644
index 00000000000..d995641536b
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-mailbox.h
@@ -0,0 +1,73 @@
+/*
+ * cx18 mailbox functions
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#ifndef _CX18_MAILBOX_H_
+#define _CX18_MAILBOX_H_
+
+/* mailbox max args */
+#define MAX_MB_ARGUMENTS 6
+/* compatibility, should be same as the define in cx2341x.h */
+#define CX2341X_MBOX_MAX_DATA 16
+
+#define MB_RESERVED_HANDLE_0 0
+#define MB_RESERVED_HANDLE_1 0xFFFFFFFF
+
+struct cx18;
+
+/* The cx18_mailbox struct is the mailbox structure which is used for passing
+ messages between processors */
+struct cx18_mailbox {
+ /* The sender sets a handle in 'request' after he fills the command. The
+ 'request' should be different than 'ack'. The sender, also, generates
+ an interrupt on XPU2YPU_irq where XPU is the sender and YPU is the
+ receiver. */
+ u32 request;
+ /* The receiver detects a new command when 'req' is different than 'ack'.
+ He sets 'ack' to the same value as 'req' to clear the command. He, also,
+ generates an interrupt on YPU2XPU_irq where XPU is the sender and YPU
+ is the receiver. */
+ u32 ack;
+ u32 reserved[6];
+ /* 'cmd' identifies the command. The list of these commands are in
+ cx23418.h */
+ u32 cmd;
+ /* Each command can have up to 6 arguments */
+ u32 args[MAX_MB_ARGUMENTS];
+ /* The return code can be one of the codes in the file cx23418.h. If the
+ command is completed successfuly, the error will be ERR_SYS_SUCCESS.
+ If it is pending, the code is ERR_SYS_PENDING. If it failed, the error
+ code would indicate the task from which the error originated and will
+ be one of the errors in cx23418.h. In that case, the following
+ applies ((error & 0xff) != 0).
+ If the command is pending, the return will be passed in a MB from the
+ receiver to the sender. 'req' will be returned in args[0] */
+ u32 error;
+};
+
+int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]);
+int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd,
+ int args, ...);
+int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...);
+int cx18_api_func(void *priv, u32 cmd, int in, int out,
+ u32 data[CX2341X_MBOX_MAX_DATA]);
+long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb);
+
+#endif
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
new file mode 100644
index 00000000000..65af1bb507c
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-queue.c
@@ -0,0 +1,282 @@
+/*
+ * cx18 buffer queues
+ *
+ * Derived from ivtv-queue.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-streams.h"
+#include "cx18-queue.h"
+#include "cx18-scb.h"
+
+int cx18_buf_copy_from_user(struct cx18_stream *s, struct cx18_buffer *buf,
+ const char __user *src, int copybytes)
+{
+ if (s->buf_size - buf->bytesused < copybytes)
+ copybytes = s->buf_size - buf->bytesused;
+ if (copy_from_user(buf->buf + buf->bytesused, src, copybytes))
+ return -EFAULT;
+ buf->bytesused += copybytes;
+ return copybytes;
+}
+
+void cx18_buf_swap(struct cx18_buffer *buf)
+{
+ int i;
+
+ for (i = 0; i < buf->bytesused; i += 4)
+ swab32s((u32 *)(buf->buf + i));
+}
+
+void cx18_queue_init(struct cx18_queue *q)
+{
+ INIT_LIST_HEAD(&q->list);
+ q->buffers = 0;
+ q->length = 0;
+ q->bytesused = 0;
+}
+
+void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q)
+{
+ unsigned long flags = 0;
+
+ /* clear the buffer if it is going to be enqueued to the free queue */
+ if (q == &s->q_free) {
+ buf->bytesused = 0;
+ buf->readpos = 0;
+ buf->b_flags = 0;
+ }
+ spin_lock_irqsave(&s->qlock, flags);
+ list_add_tail(&buf->list, &q->list);
+ q->buffers++;
+ q->length += s->buf_size;
+ q->bytesused += buf->bytesused - buf->readpos;
+ spin_unlock_irqrestore(&s->qlock, flags);
+}
+
+struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
+{
+ struct cx18_buffer *buf = NULL;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&s->qlock, flags);
+ if (!list_empty(&q->list)) {
+ buf = list_entry(q->list.next, struct cx18_buffer, list);
+ list_del_init(q->list.next);
+ q->buffers--;
+ q->length -= s->buf_size;
+ q->bytesused -= buf->bytesused - buf->readpos;
+ }
+ spin_unlock_irqrestore(&s->qlock, flags);
+ return buf;
+}
+
+struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id,
+ u32 bytesused)
+{
+ struct cx18 *cx = s->cx;
+ struct list_head *p;
+
+ list_for_each(p, &s->q_free.list) {
+ struct cx18_buffer *buf =
+ list_entry(p, struct cx18_buffer, list);
+
+ if (buf->id != id)
+ continue;
+ buf->bytesused = bytesused;
+ /* the transport buffers are handled differently,
+ so there is no need to move them to the full queue */
+ if (s->type == CX18_ENC_STREAM_TYPE_TS)
+ return buf;
+ s->q_free.buffers--;
+ s->q_free.length -= s->buf_size;
+ s->q_full.buffers++;
+ s->q_full.length += s->buf_size;
+ s->q_full.bytesused += buf->bytesused;
+ list_move_tail(&buf->list, &s->q_full.list);
+ return buf;
+ }
+ CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name);
+ return NULL;
+}
+
+static void cx18_queue_move_buf(struct cx18_stream *s, struct cx18_queue *from,
+ struct cx18_queue *to, int clear, int full)
+{
+ struct cx18_buffer *buf =
+ list_entry(from->list.next, struct cx18_buffer, list);
+
+ list_move_tail(from->list.next, &to->list);
+ from->buffers--;
+ from->length -= s->buf_size;
+ from->bytesused -= buf->bytesused - buf->readpos;
+ /* special handling for q_free */
+ if (clear)
+ buf->bytesused = buf->readpos = buf->b_flags = 0;
+ else if (full) {
+ /* special handling for stolen buffers, assume
+ all bytes are used. */
+ buf->bytesused = s->buf_size;
+ buf->readpos = buf->b_flags = 0;
+ }
+ to->buffers++;
+ to->length += s->buf_size;
+ to->bytesused += buf->bytesused - buf->readpos;
+}
+
+/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'.
+ If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'.
+ If 'steal' != NULL, then buffers may also taken from that queue if
+ needed.
+
+ The buffer is automatically cleared if it goes to the free queue. It is
+ also cleared if buffers need to be taken from the 'steal' queue and
+ the 'from' queue is the free queue.
+
+ When 'from' is q_free, then needed_bytes is compared to the total
+ available buffer length, otherwise needed_bytes is compared to the
+ bytesused value. For the 'steal' queue the total available buffer
+ length is always used.
+
+ -ENOMEM is returned if the buffers could not be obtained, 0 if all
+ buffers where obtained from the 'from' list and if non-zero then
+ the number of stolen buffers is returned. */
+int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from,
+ struct cx18_queue *steal, struct cx18_queue *to, int needed_bytes)
+{
+ unsigned long flags;
+ int rc = 0;
+ int from_free = from == &s->q_free;
+ int to_free = to == &s->q_free;
+ int bytes_available;
+
+ spin_lock_irqsave(&s->qlock, flags);
+ if (needed_bytes == 0) {
+ from_free = 1;
+ needed_bytes = from->length;
+ }
+
+ bytes_available = from_free ? from->length : from->bytesused;
+ bytes_available += steal ? steal->length : 0;
+
+ if (bytes_available < needed_bytes) {
+ spin_unlock_irqrestore(&s->qlock, flags);
+ return -ENOMEM;
+ }
+ if (from_free) {
+ u32 old_length = to->length;
+
+ while (to->length - old_length < needed_bytes) {
+ if (list_empty(&from->list))
+ from = steal;
+ if (from == steal)
+ rc++; /* keep track of 'stolen' buffers */
+ cx18_queue_move_buf(s, from, to, 1, 0);
+ }
+ } else {
+ u32 old_bytesused = to->bytesused;
+
+ while (to->bytesused - old_bytesused < needed_bytes) {
+ if (list_empty(&from->list))
+ from = steal;
+ if (from == steal)
+ rc++; /* keep track of 'stolen' buffers */
+ cx18_queue_move_buf(s, from, to, to_free, rc);
+ }
+ }
+ spin_unlock_irqrestore(&s->qlock, flags);
+ return rc;
+}
+
+void cx18_flush_queues(struct cx18_stream *s)
+{
+ cx18_queue_move(s, &s->q_io, NULL, &s->q_free, 0);
+ cx18_queue_move(s, &s->q_full, NULL, &s->q_free, 0);
+}
+
+int cx18_stream_alloc(struct cx18_stream *s)
+{
+ struct cx18 *cx = s->cx;
+ int i;
+
+ if (s->buffers == 0)
+ return 0;
+
+ CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers (%dkB total)\n",
+ s->name, s->buffers, s->buf_size,
+ s->buffers * s->buf_size / 1024);
+
+ if (((char *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] -
+ (char *)cx->scb) > SCB_RESERVED_SIZE) {
+ unsigned bufsz = (((char *)cx->scb) + SCB_RESERVED_SIZE -
+ ((char *)cx->scb->cpu_mdl));
+
+ CX18_ERR("Too many buffers, cannot fit in SCB area\n");
+ CX18_ERR("Max buffers = %zd\n",
+ bufsz / sizeof(struct cx18_mdl));
+ return -ENOMEM;
+ }
+
+ s->mdl_offset = cx->mdl_offset;
+
+ /* allocate stream buffers. Initially all buffers are in q_free. */
+ for (i = 0; i < s->buffers; i++) {
+ struct cx18_buffer *buf =
+ kzalloc(sizeof(struct cx18_buffer), GFP_KERNEL);
+
+ if (buf == NULL)
+ break;
+ buf->buf = kmalloc(s->buf_size, GFP_KERNEL);
+ if (buf->buf == NULL) {
+ kfree(buf);
+ break;
+ }
+ buf->id = cx->buffer_id++;
+ INIT_LIST_HEAD(&buf->list);
+ buf->dma_handle = pci_map_single(s->cx->dev,
+ buf->buf, s->buf_size, s->dma);
+ cx18_buf_sync_for_cpu(s, buf);
+ cx18_enqueue(s, buf, &s->q_free);
+ }
+ if (i == s->buffers) {
+ cx->mdl_offset += s->buffers;
+ return 0;
+ }
+ CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name);
+ cx18_stream_free(s);
+ return -ENOMEM;
+}
+
+void cx18_stream_free(struct cx18_stream *s)
+{
+ struct cx18_buffer *buf;
+
+ /* move all buffers to q_free */
+ cx18_flush_queues(s);
+
+ /* empty q_free */
+ while ((buf = cx18_dequeue(s, &s->q_free))) {
+ pci_unmap_single(s->cx->dev, buf->dma_handle,
+ s->buf_size, s->dma);
+ kfree(buf->buf);
+ kfree(buf);
+ }
+}
diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h
new file mode 100644
index 00000000000..f86c8a6fa6e
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-queue.h
@@ -0,0 +1,59 @@
+/*
+ * cx18 buffer queues
+ *
+ * Derived from ivtv-queue.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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 CX18_DMA_UNMAPPED ((u32) -1)
+
+/* cx18_buffer utility functions */
+
+static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s,
+ struct cx18_buffer *buf)
+{
+ pci_dma_sync_single_for_cpu(s->cx->dev, buf->dma_handle,
+ s->buf_size, s->dma);
+}
+
+static inline void cx18_buf_sync_for_device(struct cx18_stream *s,
+ struct cx18_buffer *buf)
+{
+ pci_dma_sync_single_for_device(s->cx->dev, buf->dma_handle,
+ s->buf_size, s->dma);
+}
+
+int cx18_buf_copy_from_user(struct cx18_stream *s, struct cx18_buffer *buf,
+ const char __user *src, int copybytes);
+void cx18_buf_swap(struct cx18_buffer *buf);
+
+/* cx18_queue utility functions */
+void cx18_queue_init(struct cx18_queue *q);
+void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q);
+struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q);
+int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from,
+ struct cx18_queue *steal, struct cx18_queue *to, int needed_bytes);
+struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id,
+ u32 bytesused);
+void cx18_flush_queues(struct cx18_stream *s);
+
+/* cx18_stream utility functions */
+int cx18_stream_alloc(struct cx18_stream *s);
+void cx18_stream_free(struct cx18_stream *s);
diff --git a/drivers/media/video/cx18/cx18-scb.c b/drivers/media/video/cx18/cx18-scb.c
new file mode 100644
index 00000000000..30bc803e30d
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-scb.c
@@ -0,0 +1,121 @@
+/*
+ * cx18 System Control Block initialization
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-scb.h"
+
+void cx18_init_scb(struct cx18 *cx)
+{
+ setup_page(SCB_OFFSET);
+ memset_io(cx->scb, 0, 0x10000);
+
+ writel(IRQ_APU_TO_CPU, &cx->scb->apu2cpu_irq);
+ writel(IRQ_CPU_TO_APU_ACK, &cx->scb->cpu2apu_irq_ack);
+ writel(IRQ_HPU_TO_CPU, &cx->scb->hpu2cpu_irq);
+ writel(IRQ_CPU_TO_HPU_ACK, &cx->scb->cpu2hpu_irq_ack);
+ writel(IRQ_PPU_TO_CPU, &cx->scb->ppu2cpu_irq);
+ writel(IRQ_CPU_TO_PPU_ACK, &cx->scb->cpu2ppu_irq_ack);
+ writel(IRQ_EPU_TO_CPU, &cx->scb->epu2cpu_irq);
+ writel(IRQ_CPU_TO_EPU_ACK, &cx->scb->cpu2epu_irq_ack);
+
+ writel(IRQ_CPU_TO_APU, &cx->scb->cpu2apu_irq);
+ writel(IRQ_APU_TO_CPU_ACK, &cx->scb->apu2cpu_irq_ack);
+ writel(IRQ_HPU_TO_APU, &cx->scb->hpu2apu_irq);
+ writel(IRQ_APU_TO_HPU_ACK, &cx->scb->apu2hpu_irq_ack);
+ writel(IRQ_PPU_TO_APU, &cx->scb->ppu2apu_irq);
+ writel(IRQ_APU_TO_PPU_ACK, &cx->scb->apu2ppu_irq_ack);
+ writel(IRQ_EPU_TO_APU, &cx->scb->epu2apu_irq);
+ writel(IRQ_APU_TO_EPU_ACK, &cx->scb->apu2epu_irq_ack);
+
+ writel(IRQ_CPU_TO_HPU, &cx->scb->cpu2hpu_irq);
+ writel(IRQ_HPU_TO_CPU_ACK, &cx->scb->hpu2cpu_irq_ack);
+ writel(IRQ_APU_TO_HPU, &cx->scb->apu2hpu_irq);
+ writel(IRQ_HPU_TO_APU_ACK, &cx->scb->hpu2apu_irq_ack);
+ writel(IRQ_PPU_TO_HPU, &cx->scb->ppu2hpu_irq);
+ writel(IRQ_HPU_TO_PPU_ACK, &cx->scb->hpu2ppu_irq_ack);
+ writel(IRQ_EPU_TO_HPU, &cx->scb->epu2hpu_irq);
+ writel(IRQ_HPU_TO_EPU_ACK, &cx->scb->hpu2epu_irq_ack);
+
+ writel(IRQ_CPU_TO_PPU, &cx->scb->cpu2ppu_irq);
+ writel(IRQ_PPU_TO_CPU_ACK, &cx->scb->ppu2cpu_irq_ack);
+ writel(IRQ_APU_TO_PPU, &cx->scb->apu2ppu_irq);
+ writel(IRQ_PPU_TO_APU_ACK, &cx->scb->ppu2apu_irq_ack);
+ writel(IRQ_HPU_TO_PPU, &cx->scb->hpu2ppu_irq);
+ writel(IRQ_PPU_TO_HPU_ACK, &cx->scb->ppu2hpu_irq_ack);
+ writel(IRQ_EPU_TO_PPU, &cx->scb->epu2ppu_irq);
+ writel(IRQ_PPU_TO_EPU_ACK, &cx->scb->ppu2epu_irq_ack);
+
+ writel(IRQ_CPU_TO_EPU, &cx->scb->cpu2epu_irq);
+ writel(IRQ_EPU_TO_CPU_ACK, &cx->scb->epu2cpu_irq_ack);
+ writel(IRQ_APU_TO_EPU, &cx->scb->apu2epu_irq);
+ writel(IRQ_EPU_TO_APU_ACK, &cx->scb->epu2apu_irq_ack);
+ writel(IRQ_HPU_TO_EPU, &cx->scb->hpu2epu_irq);
+ writel(IRQ_EPU_TO_HPU_ACK, &cx->scb->epu2hpu_irq_ack);
+ writel(IRQ_PPU_TO_EPU, &cx->scb->ppu2epu_irq);
+ writel(IRQ_EPU_TO_PPU_ACK, &cx->scb->epu2ppu_irq_ack);
+
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2cpu_mb),
+ &cx->scb->apu2cpu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2cpu_mb),
+ &cx->scb->hpu2cpu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2cpu_mb),
+ &cx->scb->ppu2cpu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2cpu_mb),
+ &cx->scb->epu2cpu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2apu_mb),
+ &cx->scb->cpu2apu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2apu_mb),
+ &cx->scb->hpu2apu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2apu_mb),
+ &cx->scb->ppu2apu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2apu_mb),
+ &cx->scb->epu2apu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2hpu_mb),
+ &cx->scb->cpu2hpu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2hpu_mb),
+ &cx->scb->apu2hpu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2hpu_mb),
+ &cx->scb->ppu2hpu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2hpu_mb),
+ &cx->scb->epu2hpu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2ppu_mb),
+ &cx->scb->cpu2ppu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2ppu_mb),
+ &cx->scb->apu2ppu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2ppu_mb),
+ &cx->scb->hpu2ppu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2ppu_mb),
+ &cx->scb->epu2ppu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2epu_mb),
+ &cx->scb->cpu2epu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2epu_mb),
+ &cx->scb->apu2epu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2epu_mb),
+ &cx->scb->hpu2epu_mb_offset);
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2epu_mb),
+ &cx->scb->ppu2epu_mb_offset);
+
+ writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu_state),
+ &cx->scb->ipc_offset);
+
+ writel(1, &cx->scb->hpu_state);
+ writel(1, &cx->scb->epu_state);
+}
diff --git a/drivers/media/video/cx18/cx18-scb.h b/drivers/media/video/cx18/cx18-scb.h
new file mode 100644
index 00000000000..86b4cb15d16
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-scb.h
@@ -0,0 +1,285 @@
+/*
+ * cx18 System Control Block initialization
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#ifndef CX18_SCB_H
+#define CX18_SCB_H
+
+#include "cx18-mailbox.h"
+
+/* NOTE: All ACK interrupts are in the SW2 register. All non-ACK interrupts
+ are in the SW1 register. */
+
+#define IRQ_APU_TO_CPU 0x00000001
+#define IRQ_CPU_TO_APU_ACK 0x00000001
+#define IRQ_HPU_TO_CPU 0x00000002
+#define IRQ_CPU_TO_HPU_ACK 0x00000002
+#define IRQ_PPU_TO_CPU 0x00000004
+#define IRQ_CPU_TO_PPU_ACK 0x00000004
+#define IRQ_EPU_TO_CPU 0x00000008
+#define IRQ_CPU_TO_EPU_ACK 0x00000008
+
+#define IRQ_CPU_TO_APU 0x00000010
+#define IRQ_APU_TO_CPU_ACK 0x00000010
+#define IRQ_HPU_TO_APU 0x00000020
+#define IRQ_APU_TO_HPU_ACK 0x00000020
+#define IRQ_PPU_TO_APU 0x00000040
+#define IRQ_APU_TO_PPU_ACK 0x00000040
+#define IRQ_EPU_TO_APU 0x00000080
+#define IRQ_APU_TO_EPU_ACK 0x00000080
+
+#define IRQ_CPU_TO_HPU 0x00000100
+#define IRQ_HPU_TO_CPU_ACK 0x00000100
+#define IRQ_APU_TO_HPU 0x00000200
+#define IRQ_HPU_TO_APU_ACK 0x00000200
+#define IRQ_PPU_TO_HPU 0x00000400
+#define IRQ_HPU_TO_PPU_ACK 0x00000400
+#define IRQ_EPU_TO_HPU 0x00000800
+#define IRQ_HPU_TO_EPU_ACK 0x00000800
+
+#define IRQ_CPU_TO_PPU 0x00001000
+#define IRQ_PPU_TO_CPU_ACK 0x00001000
+#define IRQ_APU_TO_PPU 0x00002000
+#define IRQ_PPU_TO_APU_ACK 0x00002000
+#define IRQ_HPU_TO_PPU 0x00004000
+#define IRQ_PPU_TO_HPU_ACK 0x00004000
+#define IRQ_EPU_TO_PPU 0x00008000
+#define IRQ_PPU_TO_EPU_ACK 0x00008000
+
+#define IRQ_CPU_TO_EPU 0x00010000
+#define IRQ_EPU_TO_CPU_ACK 0x00010000
+#define IRQ_APU_TO_EPU 0x00020000
+#define IRQ_EPU_TO_APU_ACK 0x00020000
+#define IRQ_HPU_TO_EPU 0x00040000
+#define IRQ_EPU_TO_HPU_ACK 0x00040000
+#define IRQ_PPU_TO_EPU 0x00080000
+#define IRQ_EPU_TO_PPU_ACK 0x00080000
+
+#define SCB_OFFSET 0xDC0000
+
+/* If Firmware uses fixed memory map, it shall not allocate the area
+ between SCB_OFFSET and SCB_OFFSET+SCB_RESERVED_SIZE-1 inclusive */
+#define SCB_RESERVED_SIZE 0x10000
+
+
+/* This structure is used by EPU to provide memory descriptors in its memory */
+struct cx18_mdl {
+ u32 paddr; /* Physical address of a buffer segment */
+ u32 length; /* Length of the buffer segment */
+};
+
+/* This structure is used by CPU to provide completed buffers information */
+struct cx18_mdl_ack {
+ u32 id; /* ID of a completed MDL */
+ u32 data_used; /* Total data filled in the MDL for buffer 'id' */
+};
+
+struct cx18_scb {
+ /* These fields form the System Control Block which is used at boot time
+ for localizing the IPC data as well as the code positions for all
+ processors. The offsets are from the start of this struct. */
+
+ /* Offset where to find the Inter-Processor Communication data */
+ u32 ipc_offset;
+ u32 reserved01[7];
+ /* Offset where to find the start of the CPU code */
+ u32 cpu_code_offset;
+ u32 reserved02[3];
+ /* Offset where to find the start of the APU code */
+ u32 apu_code_offset;
+ u32 reserved03[3];
+ /* Offset where to find the start of the HPU code */
+ u32 hpu_code_offset;
+ u32 reserved04[3];
+ /* Offset where to find the start of the PPU code */
+ u32 ppu_code_offset;
+ u32 reserved05[3];
+
+ /* These fields form Inter-Processor Communication data which is used
+ by all processors to locate the information needed for communicating
+ with other processors */
+
+ /* Fields for CPU: */
+
+ /* bit 0: 1/0 processor ready/not ready. Set other bits to 0. */
+ u32 cpu_state;
+ u32 reserved1[7];
+ /* Offset to the mailbox used for sending commands from APU to CPU */
+ u32 apu2cpu_mb_offset;
+ /* Value to write to register SW1 register set (0xC7003100) after the
+ command is ready */
+ u32 apu2cpu_irq;
+ /* Value to write to register SW2 register set (0xC7003140) after the
+ command is cleared */
+ u32 apu2cpu_irq_ack;
+ u32 reserved2[13];
+
+ u32 hpu2cpu_mb_offset;
+ u32 hpu2cpu_irq;
+ u32 hpu2cpu_irq_ack;
+ u32 reserved3[13];
+
+ u32 ppu2cpu_mb_offset;
+ u32 ppu2cpu_irq;
+ u32 ppu2cpu_irq_ack;
+ u32 reserved4[13];
+
+ u32 epu2cpu_mb_offset;
+ u32 epu2cpu_irq;
+ u32 epu2cpu_irq_ack;
+ u32 reserved5[13];
+ u32 reserved6[8];
+
+ /* Fields for APU: */
+
+ u32 apu_state;
+ u32 reserved11[7];
+ u32 cpu2apu_mb_offset;
+ u32 cpu2apu_irq;
+ u32 cpu2apu_irq_ack;
+ u32 reserved12[13];
+
+ u32 hpu2apu_mb_offset;
+ u32 hpu2apu_irq;
+ u32 hpu2apu_irq_ack;
+ u32 reserved13[13];
+
+ u32 ppu2apu_mb_offset;
+ u32 ppu2apu_irq;
+ u32 ppu2apu_irq_ack;
+ u32 reserved14[13];
+
+ u32 epu2apu_mb_offset;
+ u32 epu2apu_irq;
+ u32 epu2apu_irq_ack;
+ u32 reserved15[13];
+ u32 reserved16[8];
+
+ /* Fields for HPU: */
+
+ u32 hpu_state;
+ u32 reserved21[7];
+ u32 cpu2hpu_mb_offset;
+ u32 cpu2hpu_irq;
+ u32 cpu2hpu_irq_ack;
+ u32 reserved22[13];
+
+ u32 apu2hpu_mb_offset;
+ u32 apu2hpu_irq;
+ u32 apu2hpu_irq_ack;
+ u32 reserved23[13];
+
+ u32 ppu2hpu_mb_offset;
+ u32 ppu2hpu_irq;
+ u32 ppu2hpu_irq_ack;
+ u32 reserved24[13];
+
+ u32 epu2hpu_mb_offset;
+ u32 epu2hpu_irq;
+ u32 epu2hpu_irq_ack;
+ u32 reserved25[13];
+ u32 reserved26[8];
+
+ /* Fields for PPU: */
+
+ u32 ppu_state;
+ u32 reserved31[7];
+ u32 cpu2ppu_mb_offset;
+ u32 cpu2ppu_irq;
+ u32 cpu2ppu_irq_ack;
+ u32 reserved32[13];
+
+ u32 apu2ppu_mb_offset;
+ u32 apu2ppu_irq;
+ u32 apu2ppu_irq_ack;
+ u32 reserved33[13];
+
+ u32 hpu2ppu_mb_offset;
+ u32 hpu2ppu_irq;
+ u32 hpu2ppu_irq_ack;
+ u32 reserved34[13];
+
+ u32 epu2ppu_mb_offset;
+ u32 epu2ppu_irq;
+ u32 epu2ppu_irq_ack;
+ u32 reserved35[13];
+ u32 reserved36[8];
+
+ /* Fields for EPU: */
+
+ u32 epu_state;
+ u32 reserved41[7];
+ u32 cpu2epu_mb_offset;
+ u32 cpu2epu_irq;
+ u32 cpu2epu_irq_ack;
+ u32 reserved42[13];
+
+ u32 apu2epu_mb_offset;
+ u32 apu2epu_irq;
+ u32 apu2epu_irq_ack;
+ u32 reserved43[13];
+
+ u32 hpu2epu_mb_offset;
+ u32 hpu2epu_irq;
+ u32 hpu2epu_irq_ack;
+ u32 reserved44[13];
+
+ u32 ppu2epu_mb_offset;
+ u32 ppu2epu_irq;
+ u32 ppu2epu_irq_ack;
+ u32 reserved45[13];
+ u32 reserved46[8];
+
+ u32 semaphores[8]; /* Semaphores */
+
+ u32 reserved50[32]; /* Reserved for future use */
+
+ struct cx18_mailbox apu2cpu_mb;
+ struct cx18_mailbox hpu2cpu_mb;
+ struct cx18_mailbox ppu2cpu_mb;
+ struct cx18_mailbox epu2cpu_mb;
+
+ struct cx18_mailbox cpu2apu_mb;
+ struct cx18_mailbox hpu2apu_mb;
+ struct cx18_mailbox ppu2apu_mb;
+ struct cx18_mailbox epu2apu_mb;
+
+ struct cx18_mailbox cpu2hpu_mb;
+ struct cx18_mailbox apu2hpu_mb;
+ struct cx18_mailbox ppu2hpu_mb;
+ struct cx18_mailbox epu2hpu_mb;
+
+ struct cx18_mailbox cpu2ppu_mb;
+ struct cx18_mailbox apu2ppu_mb;
+ struct cx18_mailbox hpu2ppu_mb;
+ struct cx18_mailbox epu2ppu_mb;
+
+ struct cx18_mailbox cpu2epu_mb;
+ struct cx18_mailbox apu2epu_mb;
+ struct cx18_mailbox hpu2epu_mb;
+ struct cx18_mailbox ppu2epu_mb;
+
+ struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][2];
+ struct cx18_mdl cpu_mdl[1];
+};
+
+void cx18_init_scb(struct cx18 *cx);
+
+#endif
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
new file mode 100644
index 00000000000..afb141b2027
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -0,0 +1,566 @@
+/*
+ * cx18 init/start/stop/exit stream functions
+ *
+ * Derived from ivtv-streams.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-fileops.h"
+#include "cx18-mailbox.h"
+#include "cx18-i2c.h"
+#include "cx18-queue.h"
+#include "cx18-ioctl.h"
+#include "cx18-streams.h"
+#include "cx18-cards.h"
+#include "cx18-scb.h"
+#include "cx18-av-core.h"
+#include "cx18-dvb.h"
+
+#define CX18_DSP0_INTERRUPT_MASK 0xd0004C
+
+static struct file_operations cx18_v4l2_enc_fops = {
+ .owner = THIS_MODULE,
+ .read = cx18_v4l2_read,
+ .open = cx18_v4l2_open,
+ .ioctl = cx18_v4l2_ioctl,
+ .release = cx18_v4l2_close,
+ .poll = cx18_v4l2_enc_poll,
+};
+
+/* offset from 0 to register ts v4l2 minors on */
+#define CX18_V4L2_ENC_TS_OFFSET 16
+/* offset from 0 to register pcm v4l2 minors on */
+#define CX18_V4L2_ENC_PCM_OFFSET 24
+/* offset from 0 to register yuv v4l2 minors on */
+#define CX18_V4L2_ENC_YUV_OFFSET 32
+
+static struct {
+ const char *name;
+ int vfl_type;
+ int minor_offset;
+ int dma;
+ enum v4l2_buf_type buf_type;
+ struct file_operations *fops;
+} cx18_stream_info[] = {
+ { /* CX18_ENC_STREAM_TYPE_MPG */
+ "encoder MPEG",
+ VFL_TYPE_GRABBER, 0,
+ PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ &cx18_v4l2_enc_fops
+ },
+ { /* CX18_ENC_STREAM_TYPE_TS */
+ "TS",
+ VFL_TYPE_GRABBER, -1,
+ PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ &cx18_v4l2_enc_fops
+ },
+ { /* CX18_ENC_STREAM_TYPE_YUV */
+ "encoder YUV",
+ VFL_TYPE_GRABBER, CX18_V4L2_ENC_YUV_OFFSET,
+ PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ &cx18_v4l2_enc_fops
+ },
+ { /* CX18_ENC_STREAM_TYPE_VBI */
+ "encoder VBI",
+ VFL_TYPE_VBI, 0,
+ PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VBI_CAPTURE,
+ &cx18_v4l2_enc_fops
+ },
+ { /* CX18_ENC_STREAM_TYPE_PCM */
+ "encoder PCM audio",
+ VFL_TYPE_GRABBER, CX18_V4L2_ENC_PCM_OFFSET,
+ PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_PRIVATE,
+ &cx18_v4l2_enc_fops
+ },
+ { /* CX18_ENC_STREAM_TYPE_IDX */
+ "encoder IDX",
+ VFL_TYPE_GRABBER, -1,
+ PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ &cx18_v4l2_enc_fops
+ },
+ { /* CX18_ENC_STREAM_TYPE_RAD */
+ "encoder radio",
+ VFL_TYPE_RADIO, 0,
+ PCI_DMA_NONE, V4L2_BUF_TYPE_PRIVATE,
+ &cx18_v4l2_enc_fops
+ },
+};
+
+static void cx18_stream_init(struct cx18 *cx, int type)
+{
+ struct cx18_stream *s = &cx->streams[type];
+ struct video_device *dev = s->v4l2dev;
+ u32 max_size = cx->options.megabytes[type] * 1024 * 1024;
+
+ /* we need to keep v4l2dev, so restore it afterwards */
+ memset(s, 0, sizeof(*s));
+ s->v4l2dev = dev;
+
+ /* initialize cx18_stream fields */
+ s->cx = cx;
+ s->type = type;
+ s->name = cx18_stream_info[type].name;
+ s->handle = 0xffffffff;
+
+ s->dma = cx18_stream_info[type].dma;
+ s->buf_size = cx->stream_buf_size[type];
+ if (s->buf_size)
+ s->buffers = max_size / s->buf_size;
+ if (s->buffers > 63) {
+ /* Each stream has a maximum of 63 buffers,
+ ensure we do not exceed that. */
+ s->buffers = 63;
+ s->buf_size = (max_size / s->buffers) & ~0xfff;
+ }
+ spin_lock_init(&s->qlock);
+ init_waitqueue_head(&s->waitq);
+ s->id = -1;
+ cx18_queue_init(&s->q_free);
+ cx18_queue_init(&s->q_full);
+ cx18_queue_init(&s->q_io);
+}
+
+static int cx18_prep_dev(struct cx18 *cx, int type)
+{
+ struct cx18_stream *s = &cx->streams[type];
+ u32 cap = cx->v4l2_cap;
+ int minor_offset = cx18_stream_info[type].minor_offset;
+ int minor;
+
+ /* These four fields are always initialized. If v4l2dev == NULL, then
+ this stream is not in use. In that case no other fields but these
+ four can be used. */
+ s->v4l2dev = NULL;
+ s->cx = cx;
+ s->type = type;
+ s->name = cx18_stream_info[type].name;
+
+ /* Check whether the radio is supported */
+ if (type == CX18_ENC_STREAM_TYPE_RAD && !(cap & V4L2_CAP_RADIO))
+ return 0;
+
+ /* Check whether VBI is supported */
+ if (type == CX18_ENC_STREAM_TYPE_VBI &&
+ !(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)))
+ return 0;
+
+ /* card number + user defined offset + device offset */
+ minor = cx->num + cx18_first_minor + minor_offset;
+
+ /* User explicitly selected 0 buffers for these streams, so don't
+ create them. */
+ if (cx18_stream_info[type].dma != PCI_DMA_NONE &&
+ cx->options.megabytes[type] == 0) {
+ CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name);
+ return 0;
+ }
+
+ cx18_stream_init(cx, type);
+
+ if (minor_offset == -1)
+ return 0;
+
+ /* allocate and initialize the v4l2 video device structure */
+ s->v4l2dev = video_device_alloc();
+ if (s->v4l2dev == NULL) {
+ CX18_ERR("Couldn't allocate v4l2 video_device for %s\n",
+ s->name);
+ return -ENOMEM;
+ }
+
+ s->v4l2dev->type =
+ VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_TELETEXT |
+ VID_TYPE_CLIPPING | VID_TYPE_SCALES | VID_TYPE_MPEG_ENCODER;
+ snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18%d %s",
+ cx->num, s->name);
+
+ s->v4l2dev->minor = minor;
+ s->v4l2dev->dev = &cx->dev->dev;
+ s->v4l2dev->fops = cx18_stream_info[type].fops;
+ s->v4l2dev->release = video_device_release;
+
+ return 0;
+}
+
+/* Initialize v4l2 variables and register v4l2 devices */
+int cx18_streams_setup(struct cx18 *cx)
+{
+ int type;
+
+ /* Setup V4L2 Devices */
+ for (type = 0; type < CX18_MAX_STREAMS; type++) {
+ /* Prepare device */
+ if (cx18_prep_dev(cx, type))
+ break;
+
+ /* Allocate Stream */
+ if (cx18_stream_alloc(&cx->streams[type]))
+ break;
+ }
+ if (type == CX18_MAX_STREAMS)
+ return 0;
+
+ /* One or more streams could not be initialized. Clean 'em all up. */
+ cx18_streams_cleanup(cx);
+ return -ENOMEM;
+}
+
+static int cx18_reg_dev(struct cx18 *cx, int type)
+{
+ struct cx18_stream *s = &cx->streams[type];
+ int vfl_type = cx18_stream_info[type].vfl_type;
+ int minor;
+
+ /* TODO: Shouldn't this be a VFL_TYPE_TRANSPORT or something?
+ * We need a VFL_TYPE_TS defined.
+ */
+ if (strcmp("TS", s->name) == 0) {
+ /* just return if no DVB is supported */
+ if ((cx->card->hw_all & CX18_HW_DVB) == 0)
+ return 0;
+ if (cx18_dvb_register(s) < 0) {
+ CX18_ERR("DVB failed to register\n");
+ return -EINVAL;
+ }
+ }
+
+ if (s->v4l2dev == NULL)
+ return 0;
+
+ minor = s->v4l2dev->minor;
+
+ /* Register device. First try the desired minor, then any free one. */
+ if (video_register_device(s->v4l2dev, vfl_type, minor) &&
+ video_register_device(s->v4l2dev, vfl_type, -1)) {
+ CX18_ERR("Couldn't register v4l2 device for %s minor %d\n",
+ s->name, minor);
+ video_device_release(s->v4l2dev);
+ s->v4l2dev = NULL;
+ return -ENOMEM;
+ }
+ minor = s->v4l2dev->minor;
+
+ switch (vfl_type) {
+ case VFL_TYPE_GRABBER:
+ CX18_INFO("Registered device video%d for %s (%d MB)\n",
+ minor, s->name, cx->options.megabytes[type]);
+ break;
+
+ case VFL_TYPE_RADIO:
+ CX18_INFO("Registered device radio%d for %s\n",
+ minor - MINOR_VFL_TYPE_RADIO_MIN, s->name);
+ break;
+
+ case VFL_TYPE_VBI:
+ if (cx->options.megabytes[type])
+ CX18_INFO("Registered device vbi%d for %s (%d MB)\n",
+ minor - MINOR_VFL_TYPE_VBI_MIN,
+ s->name, cx->options.megabytes[type]);
+ else
+ CX18_INFO("Registered device vbi%d for %s\n",
+ minor - MINOR_VFL_TYPE_VBI_MIN, s->name);
+ break;
+ }
+
+ return 0;
+}
+
+/* Register v4l2 devices */
+int cx18_streams_register(struct cx18 *cx)
+{
+ int type;
+ int err = 0;
+
+ /* Register V4L2 devices */
+ for (type = 0; type < CX18_MAX_STREAMS; type++)
+ err |= cx18_reg_dev(cx, type);
+
+ if (err == 0)
+ return 0;
+
+ /* One or more streams could not be initialized. Clean 'em all up. */
+ cx18_streams_cleanup(cx);
+ return -ENOMEM;
+}
+
+/* Unregister v4l2 devices */
+void cx18_streams_cleanup(struct cx18 *cx)
+{
+ struct video_device *vdev;
+ int type;
+
+ /* Teardown all streams */
+ for (type = 0; type < CX18_MAX_STREAMS; type++) {
+ if (cx->streams[type].dvb.enabled)
+ cx18_dvb_unregister(&cx->streams[type]);
+
+ vdev = cx->streams[type].v4l2dev;
+
+ cx->streams[type].v4l2dev = NULL;
+ if (vdev == NULL)
+ continue;
+
+ cx18_stream_free(&cx->streams[type]);
+
+ /* Unregister device */
+ video_unregister_device(vdev);
+ }
+}
+
+static void cx18_vbi_setup(struct cx18_stream *s)
+{
+ struct cx18 *cx = s->cx;
+ int raw = cx->vbi.sliced_in->service_set == 0;
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ int lines;
+
+ if (cx->is_60hz) {
+ cx->vbi.count = 12;
+ cx->vbi.start[0] = 10;
+ cx->vbi.start[1] = 273;
+ } else { /* PAL/SECAM */
+ cx->vbi.count = 18;
+ cx->vbi.start[0] = 6;
+ cx->vbi.start[1] = 318;
+ }
+
+ /* setup VBI registers */
+ cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
+
+ /* determine number of lines and total number of VBI bytes.
+ A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1
+ The '- 1' byte is probably an unused U or V byte. Or something...
+ A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal
+ header, 42 data bytes + checksum (to be confirmed) */
+ if (raw) {
+ lines = cx->vbi.count * 2;
+ } else {
+ lines = cx->is_60hz ? 24 : 38;
+ if (cx->is_60hz)
+ lines += 2;
+ }
+
+ cx->vbi.enc_size = lines *
+ (raw ? cx->vbi.raw_size : cx->vbi.sliced_size);
+
+ data[0] = s->handle;
+ /* Lines per field */
+ data[1] = (lines / 2) | ((lines / 2) << 16);
+ /* bytes per line */
+ data[2] = (raw ? cx->vbi.raw_size : cx->vbi.sliced_size);
+ /* Every X number of frames a VBI interrupt arrives
+ (frames as in 25 or 30 fps) */
+ data[3] = 1;
+ /* Setup VBI for the cx25840 digitizer */
+ if (raw) {
+ data[4] = 0x20602060;
+ data[5] = 0x30703070;
+ } else {
+ data[4] = 0xB0F0B0F0;
+ data[5] = 0xA0E0A0E0;
+ }
+
+ CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n",
+ data[0], data[1], data[2], data[3], data[4], data[5]);
+
+ if (s->type == CX18_ENC_STREAM_TYPE_VBI)
+ cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
+}
+
+int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
+{
+ u32 data[MAX_MB_ARGUMENTS];
+ struct cx18 *cx = s->cx;
+ struct list_head *p;
+ int ts = 0;
+ int captype = 0;
+
+ if (s->v4l2dev == NULL && s->dvb.enabled == 0)
+ return -EINVAL;
+
+ CX18_DEBUG_INFO("Start encoder stream %s\n", s->name);
+
+ switch (s->type) {
+ case CX18_ENC_STREAM_TYPE_MPG:
+ captype = CAPTURE_CHANNEL_TYPE_MPEG;
+ cx->mpg_data_received = cx->vbi_data_inserted = 0;
+ cx->dualwatch_jiffies = jiffies;
+ cx->dualwatch_stereo_mode = cx->params.audio_properties & 0x300;
+ cx->search_pack_header = 0;
+ break;
+
+ case CX18_ENC_STREAM_TYPE_TS:
+ captype = CAPTURE_CHANNEL_TYPE_TS;
+ ts = 1;
+ break;
+ case CX18_ENC_STREAM_TYPE_YUV:
+ captype = CAPTURE_CHANNEL_TYPE_YUV;
+ break;
+ case CX18_ENC_STREAM_TYPE_PCM:
+ captype = CAPTURE_CHANNEL_TYPE_PCM;
+ break;
+ case CX18_ENC_STREAM_TYPE_VBI:
+ captype = cx->vbi.sliced_in->service_set ?
+ CAPTURE_CHANNEL_TYPE_SLICED_VBI : CAPTURE_CHANNEL_TYPE_VBI;
+ cx->vbi.frame = 0;
+ cx->vbi.inserted_frame = 0;
+ memset(cx->vbi.sliced_mpeg_size,
+ 0, sizeof(cx->vbi.sliced_mpeg_size));
+ break;
+ default:
+ return -EINVAL;
+ }
+ s->buffers_stolen = 0;
+
+ /* mute/unmute video */
+ cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2,
+ s->handle, !!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags));
+
+ /* Clear Streamoff flags in case left from last capture */
+ clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+
+ cx18_vapi_result(cx, data, CX18_CREATE_TASK, 1, CPU_CMD_MASK_CAPTURE);
+ s->handle = data[0];
+ cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype);
+
+ if (atomic_read(&cx->capturing) == 0 && !ts) {
+ /* Stuff from Windows, we don't know what it is */
+ cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0);
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1);
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0);
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1);
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, s->handle, 12);
+
+ cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3,
+ s->handle, cx->digitizer, cx->digitizer);
+
+ /* Setup VBI */
+ if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE)
+ cx18_vbi_setup(s);
+
+ /* assign program index info.
+ Mask 7: select I/P/B, Num_req: 400 max */
+ cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0);
+
+ /* Setup API for Stream */
+ cx2341x_update(cx, cx18_api_func, NULL, &cx->params);
+ }
+
+ if (atomic_read(&cx->capturing) == 0) {
+ clear_bit(CX18_F_I_EOS, &cx->i_flags);
+ write_reg(7, CX18_DSP0_INTERRUPT_MASK);
+ }
+
+ cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle,
+ (void *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem,
+ (void *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem);
+
+ list_for_each(p, &s->q_free.list) {
+ struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list);
+
+ writel(buf->dma_handle, &cx->scb->cpu_mdl[buf->id].paddr);
+ writel(s->buf_size, &cx->scb->cpu_mdl[buf->id].length);
+ cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
+ (void *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, 1,
+ buf->id, s->buf_size);
+ }
+ /* begin_capture */
+ if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) {
+ CX18_DEBUG_WARN("Error starting capture!\n");
+ cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
+ return -EINVAL;
+ }
+
+ /* you're live! sit back and await interrupts :) */
+ atomic_inc(&cx->capturing);
+ return 0;
+}
+
+void cx18_stop_all_captures(struct cx18 *cx)
+{
+ int i;
+
+ for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) {
+ struct cx18_stream *s = &cx->streams[i];
+
+ if (s->v4l2dev == NULL && s->dvb.enabled == 0)
+ continue;
+ if (test_bit(CX18_F_S_STREAMING, &s->s_flags))
+ cx18_stop_v4l2_encode_stream(s, 0);
+ }
+}
+
+int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
+{
+ struct cx18 *cx = s->cx;
+ unsigned long then;
+
+ if (s->v4l2dev == NULL && s->dvb.enabled == 0)
+ return -EINVAL;
+
+ /* This function assumes that you are allowed to stop the capture
+ and that we are actually capturing */
+
+ CX18_DEBUG_INFO("Stop Capture\n");
+
+ if (atomic_read(&cx->capturing) == 0)
+ return 0;
+
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG)
+ cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end);
+ else
+ cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
+
+ then = jiffies;
+
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) {
+ CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n");
+ }
+
+ atomic_dec(&cx->capturing);
+
+ /* Clear capture and no-read bits */
+ clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+
+ cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
+ s->handle = 0xffffffff;
+
+ if (atomic_read(&cx->capturing) > 0)
+ return 0;
+
+ write_reg(5, CX18_DSP0_INTERRUPT_MASK);
+ wake_up(&s->waitq);
+
+ return 0;
+}
+
+u32 cx18_find_handle(struct cx18 *cx)
+{
+ int i;
+
+ /* find first available handle to be used for global settings */
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ struct cx18_stream *s = &cx->streams[i];
+
+ if (s->v4l2dev && s->handle)
+ return s->handle;
+ }
+ return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h
new file mode 100644
index 00000000000..8c7ba7d2fa7
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-streams.h
@@ -0,0 +1,33 @@
+/*
+ * cx18 init/start/stop/exit stream functions
+ *
+ * Derived from ivtv-streams.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+u32 cx18_find_handle(struct cx18 *cx);
+int cx18_streams_setup(struct cx18 *cx);
+int cx18_streams_register(struct cx18 *cx);
+void cx18_streams_cleanup(struct cx18 *cx);
+
+/* Capture related */
+int cx18_start_v4l2_encode_stream(struct cx18_stream *s);
+int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end);
+
+void cx18_stop_all_captures(struct cx18 *cx);
diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c
new file mode 100644
index 00000000000..22e76ee3f44
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-vbi.c
@@ -0,0 +1,208 @@
+/*
+ * cx18 Vertical Blank Interval support functions
+ *
+ * Derived from ivtv-vbi.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-vbi.h"
+#include "cx18-ioctl.h"
+#include "cx18-queue.h"
+#include "cx18-av-core.h"
+
+static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp)
+{
+ int line = 0;
+ int i;
+ u32 linemask[2] = { 0, 0 };
+ unsigned short size;
+ static const u8 mpeg_hdr_data[] = {
+ 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x0c, 0x66,
+ 0x24, 0x01, 0x01, 0xd1, 0xd3, 0xfa, 0xff, 0xff,
+ 0x00, 0x00, 0x01, 0xbd, 0x00, 0x1a, 0x84, 0x80,
+ 0x07, 0x21, 0x00, 0x5d, 0x63, 0xa7, 0xff, 0xff
+ };
+ const int sd = sizeof(mpeg_hdr_data); /* start of vbi data */
+ int idx = cx->vbi.frame % CX18_VBI_FRAMES;
+ u8 *dst = &cx->vbi.sliced_mpeg_data[idx][0];
+
+ for (i = 0; i < lines; i++) {
+ struct v4l2_sliced_vbi_data *sdata = cx->vbi.sliced_data + i;
+ int f, l;
+
+ if (sdata->id == 0)
+ continue;
+
+ l = sdata->line - 6;
+ f = sdata->field;
+ if (f)
+ l += 18;
+ if (l < 32)
+ linemask[0] |= (1 << l);
+ else
+ linemask[1] |= (1 << (l - 32));
+ dst[sd + 12 + line * 43] = cx18_service2vbi(sdata->id);
+ memcpy(dst + sd + 12 + line * 43 + 1, sdata->data, 42);
+ line++;
+ }
+ memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data));
+ if (line == 36) {
+ /* All lines are used, so there is no space for the linemask
+ (the max size of the VBI data is 36 * 43 + 4 bytes).
+ So in this case we use the magic number 'ITV0'. */
+ memcpy(dst + sd, "ITV0", 4);
+ memcpy(dst + sd + 4, dst + sd + 12, line * 43);
+ size = 4 + ((43 * line + 3) & ~3);
+ } else {
+ memcpy(dst + sd, "cx0", 4);
+ memcpy(dst + sd + 4, &linemask[0], 8);
+ size = 12 + ((43 * line + 3) & ~3);
+ }
+ dst[4+16] = (size + 10) >> 8;
+ dst[5+16] = (size + 10) & 0xff;
+ dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6);
+ dst[10+16] = (pts_stamp >> 22) & 0xff;
+ dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff);
+ dst[12+16] = (pts_stamp >> 7) & 0xff;
+ dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1);
+ cx->vbi.sliced_mpeg_size[idx] = sd + size;
+}
+
+/* Compress raw VBI format, removes leading SAV codes and surplus space
+ after the field.
+ Returns new compressed size. */
+static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size)
+{
+ u32 line_size = cx->vbi.raw_decoder_line_size;
+ u32 lines = cx->vbi.count;
+ u8 sav1 = cx->vbi.raw_decoder_sav_odd_field;
+ u8 sav2 = cx->vbi.raw_decoder_sav_even_field;
+ u8 *q = buf;
+ u8 *p;
+ int i;
+
+ for (i = 0; i < lines; i++) {
+ p = buf + i * line_size;
+
+ /* Look for SAV code */
+ if (p[0] != 0xff || p[1] || p[2] ||
+ (p[3] != sav1 && p[3] != sav2))
+ break;
+ memcpy(q, p + 4, line_size - 4);
+ q += line_size - 4;
+ }
+ return lines * (line_size - 4);
+}
+
+
+/* Compressed VBI format, all found sliced blocks put next to one another
+ Returns new compressed size */
+static u32 compress_sliced_buf(struct cx18 *cx, u32 line, u8 *buf,
+ u32 size, u8 sav)
+{
+ u32 line_size = cx->vbi.sliced_decoder_line_size;
+ struct v4l2_decode_vbi_line vbi;
+ int i;
+
+ /* find the first valid line */
+ for (i = 0; i < size; i++, buf++) {
+ if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav)
+ break;
+ }
+
+ size -= i;
+ if (size < line_size)
+ return line;
+ for (i = 0; i < size / line_size; i++) {
+ u8 *p = buf + i * line_size;
+
+ /* Look for SAV code */
+ if (p[0] != 0xff || p[1] || p[2] || p[3] != sav)
+ continue;
+ vbi.p = p + 4;
+ cx18_av_cmd(cx, VIDIOC_INT_DECODE_VBI_LINE, &vbi);
+ if (vbi.type) {
+ cx->vbi.sliced_data[line].id = vbi.type;
+ cx->vbi.sliced_data[line].field = vbi.is_second_field;
+ cx->vbi.sliced_data[line].line = vbi.line;
+ memcpy(cx->vbi.sliced_data[line].data, vbi.p, 42);
+ line++;
+ }
+ }
+ return line;
+}
+
+void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
+ u64 pts_stamp, int streamtype)
+{
+ u8 *p = (u8 *) buf->buf;
+ u32 size = buf->bytesused;
+ int lines;
+
+ if (streamtype != CX18_ENC_STREAM_TYPE_VBI)
+ return;
+
+ /* Raw VBI data */
+ if (cx->vbi.sliced_in->service_set == 0) {
+ u8 type;
+
+ cx18_buf_swap(buf);
+
+ type = p[3];
+
+ size = buf->bytesused = compress_raw_buf(cx, p, size);
+
+ /* second field of the frame? */
+ if (type == cx->vbi.raw_decoder_sav_even_field) {
+ /* Dirty hack needed for backwards
+ compatibility of old VBI software. */
+ p += size - 4;
+ memcpy(p, &cx->vbi.frame, 4);
+ cx->vbi.frame++;
+ }
+ return;
+ }
+
+ /* Sliced VBI data with data insertion */
+ cx18_buf_swap(buf);
+
+ /* first field */
+ lines = compress_sliced_buf(cx, 0, p, size / 2,
+ cx->vbi.sliced_decoder_sav_odd_field);
+ /* second field */
+ /* experimentation shows that the second half does not always
+ begin at the exact address. So start a bit earlier
+ (hence 32). */
+ lines = compress_sliced_buf(cx, lines, p + size / 2 - 32,
+ size / 2 + 32, cx->vbi.sliced_decoder_sav_even_field);
+ /* always return at least one empty line */
+ if (lines == 0) {
+ cx->vbi.sliced_data[0].id = 0;
+ cx->vbi.sliced_data[0].line = 0;
+ cx->vbi.sliced_data[0].field = 0;
+ lines = 1;
+ }
+ buf->bytesused = size = lines * sizeof(cx->vbi.sliced_data[0]);
+ memcpy(p, &cx->vbi.sliced_data[0], size);
+
+ if (cx->vbi.insert_mpeg)
+ copy_vbi_data(cx, lines, pts_stamp);
+ cx->vbi.frame++;
+}
diff --git a/drivers/media/video/cx18/cx18-vbi.h b/drivers/media/video/cx18/cx18-vbi.h
new file mode 100644
index 00000000000..c56ff7d28f2
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-vbi.h
@@ -0,0 +1,26 @@
+/*
+ * cx18 Vertical Blank Interval support functions
+ *
+ * Derived from ivtv-vbi.h
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
+ u64 pts_stamp, int streamtype);
+int cx18_used_line(struct cx18 *cx, int line, int field);
diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h
new file mode 100644
index 00000000000..d5c7a6f968d
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-version.h
@@ -0,0 +1,34 @@
+/*
+ * cx18 driver version information
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#ifndef CX18_VERSION_H
+#define CX18_VERSION_H
+
+#define CX18_DRIVER_NAME "cx18"
+#define CX18_DRIVER_VERSION_MAJOR 1
+#define CX18_DRIVER_VERSION_MINOR 0
+#define CX18_DRIVER_VERSION_PATCHLEVEL 0
+
+#define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL)
+#define CX18_DRIVER_VERSION KERNEL_VERSION(CX18_DRIVER_VERSION_MAJOR, \
+ CX18_DRIVER_VERSION_MINOR, CX18_DRIVER_VERSION_PATCHLEVEL)
+
+#endif
diff --git a/drivers/media/video/cx18/cx18-video.c b/drivers/media/video/cx18/cx18-video.c
new file mode 100644
index 00000000000..2e5c4193933
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-video.c
@@ -0,0 +1,45 @@
+/*
+ * cx18 video interface functions
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-video.h"
+#include "cx18-av-core.h"
+#include "cx18-cards.h"
+
+void cx18_video_set_io(struct cx18 *cx)
+{
+ struct v4l2_routing route;
+ int inp = cx->active_input;
+ u32 type;
+
+ route.input = cx->card->video_inputs[inp].video_input;
+ route.output = 0;
+ cx18_av_cmd(cx, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+
+ type = cx->card->video_inputs[inp].video_type;
+
+ if (type == CX18_CARD_INPUT_VID_TUNER)
+ route.input = 0; /* Tuner */
+ else if (type < CX18_CARD_INPUT_COMPOSITE1)
+ route.input = 2; /* S-Video */
+ else
+ route.input = 1; /* Composite */
+}
diff --git a/drivers/media/video/cx18/cx18-video.h b/drivers/media/video/cx18/cx18-video.h
new file mode 100644
index 00000000000..529006a06e5
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-video.h
@@ -0,0 +1,22 @@
+/*
+ * cx18 video interface functions
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+void cx18_video_set_io(struct cx18 *cx);
diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h
new file mode 100644
index 00000000000..33f78da9dba
--- /dev/null
+++ b/drivers/media/video/cx18/cx23418.h
@@ -0,0 +1,458 @@
+/*
+ * cx18 header containing common defines.
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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
+ */
+
+#ifndef CX23418_H
+#define CX23418_H
+
+#include <media/cx2341x.h>
+
+#define MGR_CMD_MASK 0x40000000
+/* The MSB of the command code indicates that this is the completion of a
+ command */
+#define MGR_CMD_MASK_ACK (MGR_CMD_MASK | 0x80000000)
+
+/* Description: This command creates a new instance of a certain task
+ IN[0] - Task ID. This is one of the XPU_CMD_MASK_YYY where XPU is
+ the processor on which the task YYY will be created
+ OUT[0] - Task handle. This handle is passed along with commands to
+ dispatch to the right instance of the task
+ ReturnCode - One of the ERR_SYS_... */
+#define CX18_CREATE_TASK (MGR_CMD_MASK | 0x0001)
+
+/* Description: This command destroys an instance of a task
+ IN[0] - Task handle. Hanlde of the task to destroy
+ ReturnCode - One of the ERR_SYS_... */
+#define CX18_DESTROY_TASK (MGR_CMD_MASK | 0x0002)
+
+/* All commands for CPU have the following mask set */
+#define CPU_CMD_MASK 0x20000000
+#define CPU_CMD_MASK_ACK (CPU_CMD_MASK | 0x80000000)
+#define CPU_CMD_MASK_CAPTURE (CPU_CMD_MASK | 0x00020000)
+#define CPU_CMD_MASK_TS (CPU_CMD_MASK | 0x00040000)
+
+#define EPU_CMD_MASK 0x02000000
+#define EPU_CMD_MASK_DEBUG (EPU_CMD_MASK | 0x000000)
+#define EPU_CMD_MASK_DE (EPU_CMD_MASK | 0x040000)
+
+/* Description: This command indicates that a Memory Descriptor List has been
+ filled with the requested channel type
+ IN[0] - Task handle. Handle of the task
+ IN[1] - Offset of the MDL_ACK from the beginning of the local DDR.
+ IN[2] - Number of CNXT_MDL_ACK structures in the array pointed to by IN[1]
+ ReturnCode - One of the ERR_DE_... */
+#define CX18_EPU_DMA_DONE (EPU_CMD_MASK_DE | 0x0001)
+
+/* Something interesting happened
+ IN[0] - A value to log
+ IN[1] - An offset of a string in the MiniMe memory;
+ 0/zero/NULL means "I have nothing to say" */
+#define CX18_EPU_DEBUG (EPU_CMD_MASK_DEBUG | 0x0003)
+
+/* Description: This command starts streaming with the set channel type
+ IN[0] - Task handle. Handle of the task to start
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_CAPTURE_START (CPU_CMD_MASK_CAPTURE | 0x0002)
+
+/* Description: This command stops streaming with the set channel type
+ IN[0] - Task handle. Handle of the task to stop
+ IN[1] - 0 = stop at end of GOP, 1 = stop at end of frame (MPEG only)
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_CAPTURE_STOP (CPU_CMD_MASK_CAPTURE | 0x0003)
+
+/* Description: This command pauses streaming with the set channel type
+ IN[0] - Task handle. Handle of the task to pause
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_CAPTURE_PAUSE (CPU_CMD_MASK_CAPTURE | 0x0007)
+
+/* Description: This command resumes streaming with the set channel type
+ IN[0] - Task handle. Handle of the task to resume
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_CAPTURE_RESUME (CPU_CMD_MASK_CAPTURE | 0x0008)
+
+#define CAPTURE_CHANNEL_TYPE_NONE 0
+#define CAPTURE_CHANNEL_TYPE_MPEG 1
+#define CAPTURE_CHANNEL_TYPE_INDEX 2
+#define CAPTURE_CHANNEL_TYPE_YUV 3
+#define CAPTURE_CHANNEL_TYPE_PCM 4
+#define CAPTURE_CHANNEL_TYPE_VBI 5
+#define CAPTURE_CHANNEL_TYPE_SLICED_VBI 6
+#define CAPTURE_CHANNEL_TYPE_TS 7
+#define CAPTURE_CHANNEL_TYPE_MAX 15
+
+/* Description: This command sets the channel type. This can only be done
+ when stopped.
+ IN[0] - Task handle. Handle of the task to start
+ IN[1] - Channel Type. See Below.
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_CHANNEL_TYPE (CPU_CMD_MASK_CAPTURE + 1)
+
+/* Description: Set stream output type
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - type
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_STREAM_OUTPUT_TYPE (CPU_CMD_MASK_CAPTURE | 0x0012)
+
+/* Description: Set video input resolution and frame rate
+ IN[0] - task handle
+ IN[1] - reserved
+ IN[2] - reserved
+ IN[3] - reserved
+ IN[4] - reserved
+ IN[5] - frame rate, 0 - 29.97f/s, 1 - 25f/s
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_IN (CPU_CMD_MASK_CAPTURE | 0x0004)
+
+/* Description: Set video frame rate
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - video bit rate mode
+ IN[2] - video average rate
+ IN[3] - video peak rate
+ IN[4] - system mux rate
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_RATE (CPU_CMD_MASK_CAPTURE | 0x0005)
+
+/* Description: Set video output resolution
+ IN[0] - task handle
+ IN[1] - horizontal size
+ IN[2] - vertical size
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_RESOLUTION (CPU_CMD_MASK_CAPTURE | 0x0006)
+
+/* Description: This command set filter parameters
+ IN[0] - Task handle. Handle of the task
+ IN[1] - type, 0 - temporal, 1 - spatial, 2 - median
+ IN[2] - mode, temporal/spatial: 0 - disable, 1 - static, 2 - dynamic
+ median: 0 = disable, 1 = horizontal, 2 = vertical,
+ 3 = horizontal/vertical, 4 = diagonal
+ IN[3] - strength, temporal 0 - 31, spatial 0 - 15
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_FILTER_PARAM (CPU_CMD_MASK_CAPTURE | 0x0009)
+
+/* Description: This command set spatial filter type
+ IN[0] - Task handle.
+ IN[1] - luma type: 0 = disable, 1 = 1D horizontal only, 2 = 1D vertical only,
+ 3 = 2D H/V separable, 4 = 2D symmetric non-separable
+ IN[2] - chroma type: 0 - diable, 1 = 1D horizontal
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_SPATIAL_FILTER_TYPE (CPU_CMD_MASK_CAPTURE | 0x000C)
+
+/* Description: This command set coring levels for median filter
+ IN[0] - Task handle.
+ IN[1] - luma_high
+ IN[2] - luma_low
+ IN[3] - chroma_high
+ IN[4] - chroma_low
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_MEDIAN_CORING (CPU_CMD_MASK_CAPTURE | 0x000E)
+
+/* Description: This command set the picture type mask for index file
+ IN[0] - 0 = disable index file output
+ 1 = output I picture
+ 2 = P picture
+ 4 = B picture
+ other = illegal */
+#define CX18_CPU_SET_INDEXTABLE (CPU_CMD_MASK_CAPTURE | 0x0010)
+
+/* Description: Set audio parameters
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - audio parameter
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_AUDIO_PARAMETERS (CPU_CMD_MASK_CAPTURE | 0x0011)
+
+/* Description: Set video mute
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - bit31-24: muteYvalue
+ bit23-16: muteUvalue
+ bit15-8: muteVvalue
+ bit0: 1:mute, 0: unmute
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_MUTE (CPU_CMD_MASK_CAPTURE | 0x0013)
+
+/* Description: Set audio mute
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - mute/unmute
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_AUDIO_MUTE (CPU_CMD_MASK_CAPTURE | 0x0014)
+
+/* Description: Set stream output type
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - subType
+ SET_INITIAL_SCR 1
+ SET_QUALITY_MODE 2
+ SET_VIM_PROTECT_MODE 3
+ SET_PTS_CORRECTION 4
+ SET_USB_FLUSH_MODE 5
+ SET_MERAQPAR_ENABLE 6
+ SET_NAV_PACK_INSERTION 7
+ SET_SCENE_CHANGE_ENABLE 8
+ IN[2] - parameter 1
+ IN[3] - parameter 2
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_MISC_PARAMETERS (CPU_CMD_MASK_CAPTURE | 0x0015)
+
+/* Description: Set raw VBI parameters
+ IN[0] - Task handle
+ IN[1] - No. of input lines per field:
+ bit[15:0]: field 1,
+ bit[31:16]: field 2
+ IN[2] - No. of input bytes per line
+ IN[3] - No. of output frames per transfer
+ IN[4] - start code
+ IN[5] - stop code
+ ReturnCode */
+#define CX18_CPU_SET_RAW_VBI_PARAM (CPU_CMD_MASK_CAPTURE | 0x0016)
+
+/* Description: Set capture line No.
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - height1
+ IN[2] - height2
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_CAPTURE_LINE_NO (CPU_CMD_MASK_CAPTURE | 0x0017)
+
+/* Description: Set copyright
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - copyright
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_COPYRIGHT (CPU_CMD_MASK_CAPTURE | 0x0018)
+
+/* Description: Set audio PID
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - PID
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_AUDIO_PID (CPU_CMD_MASK_CAPTURE | 0x0019)
+
+/* Description: Set video PID
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - PID
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VIDEO_PID (CPU_CMD_MASK_CAPTURE | 0x001A)
+
+/* Description: Set Vertical Crop Line
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - Line
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_VER_CROP_LINE (CPU_CMD_MASK_CAPTURE | 0x001B)
+
+/* Description: Set COP structure
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - M
+ IN[2] - N
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_GOP_STRUCTURE (CPU_CMD_MASK_CAPTURE | 0x001C)
+
+/* Description: Set Scene Change Detection
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - scene change
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_SCENE_CHANGE_DETECTION (CPU_CMD_MASK_CAPTURE | 0x001D)
+
+/* Description: Set Aspect Ratio
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - AspectRatio
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_ASPECT_RATIO (CPU_CMD_MASK_CAPTURE | 0x001E)
+
+/* Description: Set Skip Input Frame
+ IN[0] - task handle. Handle of the task to start
+ IN[1] - skip input frames
+ ReturnCode - One of the ERR_CAPTURE_... */
+#define CX18_CPU_SET_SKIP_INPUT_FRAME (CPU_CMD_MASK_CAPTURE | 0x001F)
+
+/* Description: Set sliced VBI parameters -
+ Note This API will only apply to MPEG and Sliced VBI Channels
+ IN[0] - Task handle
+ IN[1] - output type, 0 - CC, 1 - Moji, 2 - Teletext
+ IN[2] - start / stop line
+ bit[15:0] start line number
+ bit[31:16] stop line number
+ IN[3] - number of output frames per interrupt
+ IN[4] - VBI insertion mode
+ bit 0: output user data, 1 - enable
+ bit 1: output private stream, 1 - enable
+ bit 2: mux option, 0 - in GOP, 1 - in picture
+ bit[7:0] private stream ID
+ IN[5] - insertion period while mux option is in picture
+ ReturnCode - VBI data offset */
+#define CX18_CPU_SET_SLICED_VBI_PARAM (CPU_CMD_MASK_CAPTURE | 0x0020)
+
+/* Description: Set the user data place holder
+ IN[0] - type of data (0 for user)
+ IN[1] - Stuffing period
+ IN[2] - ID data size in word (less than 10)
+ IN[3] - Pointer to ID buffer */
+#define CX18_CPU_SET_USERDATA_PLACE_HOLDER (CPU_CMD_MASK_CAPTURE | 0x0021)
+
+
+/* Description:
+ In[0] Task Handle
+ return parameter:
+ Out[0] Reserved
+ Out[1] Video PTS bit[32:2] of last output video frame.
+ Out[2] Video PTS bit[ 1:0] of last output video frame.
+ Out[3] Hardware Video PTS counter bit[31:0],
+ these bits get incremented on every 90kHz clock tick.
+ Out[4] Hardware Video PTS counter bit32,
+ these bits get incremented on every 90kHz clock tick.
+ ReturnCode */
+#define CX18_CPU_GET_ENC_PTS (CPU_CMD_MASK_CAPTURE | 0x0022)
+
+/* Below is the list of commands related to the data exchange */
+#define CPU_CMD_MASK_DE (CPU_CMD_MASK | 0x040000)
+
+/* Description: This command provides the physical base address of the local
+ DDR as viewed by EPU
+ IN[0] - Physical offset where EPU has the local DDR mapped
+ ReturnCode - One of the ERR_DE_... */
+#define CPU_CMD_DE_SetBase (CPU_CMD_MASK_DE | 0x0001)
+
+/* Description: This command provides the offsets in the device memory where
+ the 2 cx18_mdl_ack blocks reside
+ IN[0] - Task handle. Handle of the task to start
+ IN[1] - Offset of the first cx18_mdl_ack from the beginning of the
+ local DDR.
+ IN[2] - Offset of the second cx18_mdl_ack from the beginning of the
+ local DDR.
+ ReturnCode - One of the ERR_DE_... */
+#define CX18_CPU_DE_SET_MDL_ACK (CPU_CMD_MASK_DE | 0x0002)
+
+/* Description: This command provides the offset to a Memory Descriptor List
+ IN[0] - Task handle. Handle of the task to start
+ IN[1] - Offset of the MDL from the beginning of the local DDR.
+ IN[2] - Number of cx18_mdl structures in the array pointed to by IN[1]
+ IN[3] - Buffer ID
+ IN[4] - Total buffer length
+ ReturnCode - One of the ERR_DE_... */
+#define CX18_CPU_DE_SET_MDL (CPU_CMD_MASK_DE | 0x0005)
+
+/* Description: This command requests return of all current Memory
+ Descriptor Lists to the driver
+ IN[0] - Task handle. Handle of the task to start
+ ReturnCode - One of the ERR_DE_... */
+/* #define CX18_CPU_DE_ReleaseMDL (CPU_CMD_MASK_DE | 0x0006) */
+
+/* Description: This command signals the cpu that the dat buffer has been
+ consumed and ready for re-use.
+ IN[0] - Task handle. Handle of the task
+ IN[1] - Offset of the data block from the beginning of the local DDR.
+ IN[2] - Number of bytes in the data block
+ ReturnCode - One of the ERR_DE_... */
+/* #define CX18_CPU_DE_RELEASE_BUFFER (CPU_CMD_MASK_DE | 0x0007) */
+
+/* No Error / Success */
+#define CNXT_OK 0x000000
+
+/* Received unknown command */
+#define CXERR_UNK_CMD 0x000001
+
+/* First parameter in the command is invalid */
+#define CXERR_INVALID_PARAM1 0x000002
+
+/* Second parameter in the command is invalid */
+#define CXERR_INVALID_PARAM2 0x000003
+
+/* Device interface is not open/found */
+#define CXERR_DEV_NOT_FOUND 0x000004
+
+/* Requested function is not implemented/available */
+#define CXERR_NOTSUPPORTED 0x000005
+
+/* Invalid pointer is provided */
+#define CXERR_BADPTR 0x000006
+
+/* Unable to allocate memory */
+#define CXERR_NOMEM 0x000007
+
+/* Object/Link not found */
+#define CXERR_LINK 0x000008
+
+/* Device busy, command cannot be executed */
+#define CXERR_BUSY 0x000009
+
+/* File/device/handle is not open. */
+#define CXERR_NOT_OPEN 0x00000A
+
+/* Value is out of range */
+#define CXERR_OUTOFRANGE 0x00000B
+
+/* Buffer overflow */
+#define CXERR_OVERFLOW 0x00000C
+
+/* Version mismatch */
+#define CXERR_BADVER 0x00000D
+
+/* Operation timed out */
+#define CXERR_TIMEOUT 0x00000E
+
+/* Operation aborted */
+#define CXERR_ABORT 0x00000F
+
+/* Specified I2C device not found for read/write */
+#define CXERR_I2CDEV_NOTFOUND 0x000010
+
+/* Error in I2C data xfer (but I2C device is present) */
+#define CXERR_I2CDEV_XFERERR 0x000011
+
+/* Chanel changing component not ready */
+#define CXERR_CHANNELNOTREADY 0x000012
+
+/* PPU (Presensation/Decoder) mail box is corrupted */
+#define CXERR_PPU_MB_CORRUPT 0x000013
+
+/* CPU (Capture/Encoder) mail box is corrupted */
+#define CXERR_CPU_MB_CORRUPT 0x000014
+
+/* APU (Audio) mail box is corrupted */
+#define CXERR_APU_MB_CORRUPT 0x000015
+
+/* Unable to open file for reading */
+#define CXERR_FILE_OPEN_READ 0x000016
+
+/* Unable to open file for writing */
+#define CXERR_FILE_OPEN_WRITE 0x000017
+
+/* Unable to find the I2C section specified */
+#define CXERR_I2C_BADSECTION 0x000018
+
+/* Error in I2C data xfer (but I2C device is present) */
+#define CXERR_I2CDEV_DATALOW 0x000019
+
+/* Error in I2C data xfer (but I2C device is present) */
+#define CXERR_I2CDEV_CLOCKLOW 0x00001A
+
+/* No Interrupt received from HW (for I2C access) */
+#define CXERR_NO_HW_I2C_INTR 0x00001B
+
+/* RPU is not ready to accept commands! */
+#define CXERR_RPU_NOT_READY 0x00001C
+
+/* RPU is not ready to accept commands! */
+#define CXERR_RPU_NO_ACK 0x00001D
+
+/* The are no buffers ready. Try again soon! */
+#define CXERR_NODATA_AGAIN 0x00001E
+
+/* The stream is stopping. Function not alllowed now! */
+#define CXERR_STOPPING_STATUS 0x00001F
+
+/* Trying to access hardware when the power is turned OFF */
+#define CXERR_DEVPOWER_OFF 0x000020
+
+#endif /* CX23418_H */
diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig
index ca5fbce3a90..cadf936c367 100644
--- a/drivers/media/video/cx23885/Kconfig
+++ b/drivers/media/video/cx23885/Kconfig
@@ -4,19 +4,19 @@ config VIDEO_CX23885
select I2C_ALGOBIT
select FW_LOADER
select VIDEO_BTCX
- select VIDEO_TUNER
+ select MEDIA_TUNER
select VIDEO_TVEEPROM
select VIDEO_IR
select VIDEOBUF_DVB
select VIDEO_CX25840
- select DVB_TUNER_MT2131 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MT2131 if !DVB_FE_CUSTOMISE
select DVB_S5H1409 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
select DVB_PLL if !DVB_FE_CUSTOMISE
- select TUNER_XC2028 if !DVB_FE_CUSTOMIZE
- select TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
- select DVB_TDA18271 if !DVB_FE_CUSTOMIZE
- select DVB_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
+ select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMIZE
+ select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
+ select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE
+ select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
select DVB_TDA10048 if !DVB_FE_CUSTOMIZE
---help---
This is a video4linux driver for Conexant 23885 based
diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile
index d7b0721af06..29c23b44c13 100644
--- a/drivers/media/video/cx23885/Makefile
+++ b/drivers/media/video/cx23885/Makefile
@@ -3,6 +3,7 @@ cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o cx23885-core.o cx2
obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig
index 27635cdcbaf..b0d7d6a7a4c 100644
--- a/drivers/media/video/cx88/Kconfig
+++ b/drivers/media/video/cx88/Kconfig
@@ -5,7 +5,7 @@ config VIDEO_CX88
select FW_LOADER
select VIDEO_BTCX
select VIDEOBUF_DMA_SG
- select VIDEO_TUNER
+ select MEDIA_TUNER
select VIDEO_TVEEPROM
select VIDEO_IR
select VIDEO_WM8775 if VIDEO_HELPER_CHIPS_AUTO
@@ -57,7 +57,7 @@ config VIDEO_CX88_DVB
select DVB_NXT200X if !DVB_FE_CUSTOMISE
select DVB_CX24123 if !DVB_FE_CUSTOMISE
select DVB_ISL6421 if !DVB_FE_CUSTOMISE
- select TUNER_SIMPLE if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
select DVB_S5H1411 if !DVB_FE_CUSTOMISE
---help---
This adds support for DVB/ATSC cards based on the
diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile
index 532cee35eb3..6ec30f24257 100644
--- a/drivers/media/video/cx88/Makefile
+++ b/drivers/media/video/cx88/Makefile
@@ -10,5 +10,6 @@ obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o
obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o
EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index 2b6b283cda1..aeba26dc0a3 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -57,6 +57,9 @@ MODULE_PARM_DESC(latency,"pci latency timer");
/* ------------------------------------------------------------------ */
/* board config info */
+/* If radio_type !=UNSET, radio_addr should be specified
+ */
+
static const struct cx88_board cx88_boards[] = {
[CX88_BOARD_UNKNOWN] = {
.name = "UNKNOWN/GENERIC",
@@ -2446,25 +2449,31 @@ EXPORT_SYMBOL_GPL(cx88_setup_xc3028);
static void cx88_card_setup(struct cx88_core *core)
{
static u8 eeprom[256];
+ struct tuner_setup tun_setup;
+ unsigned int mode_mask = T_RADIO |
+ T_ANALOG_TV |
+ T_DIGITAL_TV;
+
+ memset(&tun_setup, 0, sizeof(tun_setup));
if (0 == core->i2c_rc) {
core->i2c_client.addr = 0xa0 >> 1;
- tveeprom_read(&core->i2c_client,eeprom,sizeof(eeprom));
+ tveeprom_read(&core->i2c_client, eeprom, sizeof(eeprom));
}
switch (core->boardnr) {
case CX88_BOARD_HAUPPAUGE:
case CX88_BOARD_HAUPPAUGE_ROSLYN:
if (0 == core->i2c_rc)
- hauppauge_eeprom(core,eeprom+8);
+ hauppauge_eeprom(core, eeprom+8);
break;
case CX88_BOARD_GDI:
if (0 == core->i2c_rc)
- gdi_eeprom(core,eeprom);
+ gdi_eeprom(core, eeprom);
break;
case CX88_BOARD_WINFAST2000XP_EXPERT:
if (0 == core->i2c_rc)
- leadtek_eeprom(core,eeprom);
+ leadtek_eeprom(core, eeprom);
break;
case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
@@ -2474,7 +2483,7 @@ static void cx88_card_setup(struct cx88_core *core)
case CX88_BOARD_HAUPPAUGE_HVR3000:
case CX88_BOARD_HAUPPAUGE_HVR1300:
if (0 == core->i2c_rc)
- hauppauge_eeprom(core,eeprom);
+ hauppauge_eeprom(core, eeprom);
break;
case CX88_BOARD_KWORLD_DVBS_100:
cx_write(MO_GP0_IO, 0x000007f8);
@@ -2555,6 +2564,35 @@ static void cx88_card_setup(struct cx88_core *core)
cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &tea5767_cfg);
}
+ } /*end switch() */
+
+
+ /* Setup tuners */
+ if ((core->board.radio_type != UNSET)) {
+ tun_setup.mode_mask = T_RADIO;
+ tun_setup.type = core->board.radio_type;
+ tun_setup.addr = core->board.radio_addr;
+ tun_setup.tuner_callback = cx88_tuner_callback;
+ cx88_call_i2c_clients(core, TUNER_SET_TYPE_ADDR, &tun_setup);
+ mode_mask &= ~T_RADIO;
+ }
+
+ if (core->board.tuner_type != TUNER_ABSENT) {
+ tun_setup.mode_mask = mode_mask;
+ tun_setup.type = core->board.tuner_type;
+ tun_setup.addr = core->board.tuner_addr;
+ tun_setup.tuner_callback = cx88_tuner_callback;
+
+ cx88_call_i2c_clients(core, TUNER_SET_TYPE_ADDR, &tun_setup);
+ }
+
+ if (core->board.tda9887_conf) {
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &core->board.tda9887_conf;
+
+ cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &tda9887_cfg);
}
if (core->board.tuner_type == TUNER_XC2028) {
@@ -2572,6 +2610,7 @@ static void cx88_card_setup(struct cx88_core *core)
ctl.fname);
cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &xc2028_cfg);
}
+ cx88_call_i2c_clients (core, TUNER_SET_STANDBY, NULL);
}
/* ------------------------------------------------------------------ */
@@ -2710,7 +2749,6 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
if (TUNER_ABSENT != core->board.tuner_type)
request_module("tuner");
- cx88_call_i2c_clients (core, TUNER_SET_STANDBY, NULL);
cx88_card_setup(core);
cx88_ir_init(core, pci);
diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c
index c6b44732a08..00aa7a3f110 100644
--- a/drivers/media/video/cx88/cx88-i2c.c
+++ b/drivers/media/video/cx88/cx88-i2c.c
@@ -104,37 +104,7 @@ static int attach_inform(struct i2c_client *client)
dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n",
client->driver->driver.name, client->addr, client->name);
- if (!client->driver->command)
- return 0;
-
- if (core->board.radio_type != UNSET) {
- if ((core->board.radio_addr==ADDR_UNSET)||(core->board.radio_addr==client->addr)) {
- tun_setup.mode_mask = T_RADIO;
- tun_setup.type = core->board.radio_type;
- tun_setup.addr = core->board.radio_addr;
- tun_setup.tuner_callback = cx88_tuner_callback;
- client->driver->command (client, TUNER_SET_TYPE_ADDR, &tun_setup);
- }
- }
- if (core->board.tuner_type != UNSET) {
- if ((core->board.tuner_addr==ADDR_UNSET)||(core->board.tuner_addr==client->addr)) {
-
- tun_setup.mode_mask = T_ANALOG_TV;
- tun_setup.type = core->board.tuner_type;
- tun_setup.addr = core->board.tuner_addr;
- tun_setup.tuner_callback = cx88_tuner_callback;
- client->driver->command (client,TUNER_SET_TYPE_ADDR, &tun_setup);
- }
- }
-
- if (core->board.tda9887_conf) {
- struct v4l2_priv_tun_config tda9887_cfg;
- tda9887_cfg.tuner = TUNER_TDA9887;
- tda9887_cfg.priv = &core->board.tda9887_conf;
-
- client->driver->command(client, TUNER_SET_CONFIG, &tda9887_cfg);
- }
return 0;
}
diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig
index 9caffed2b6b..c7c2896bbd8 100644
--- a/drivers/media/video/em28xx/Kconfig
+++ b/drivers/media/video/em28xx/Kconfig
@@ -1,7 +1,7 @@
config VIDEO_EM28XX
tristate "Empia EM28xx USB video capture support"
depends on VIDEO_DEV && I2C && INPUT
- select VIDEO_TUNER
+ select MEDIA_TUNER
select VIDEO_TVEEPROM
select VIDEO_IR
select VIDEOBUF_VMALLOC
diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile
index 3d1c3cc337f..8137a8c94bf 100644
--- a/drivers/media/video/em28xx/Makefile
+++ b/drivers/media/video/em28xx/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o
obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o
EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig
index b6171702c4d..eec115bf951 100644
--- a/drivers/media/video/ivtv/Kconfig
+++ b/drivers/media/video/ivtv/Kconfig
@@ -4,7 +4,7 @@ config VIDEO_IVTV
select I2C_ALGOBIT
select FW_LOADER
select VIDEO_IR
- select VIDEO_TUNER
+ select MEDIA_TUNER
select VIDEO_TVEEPROM
select VIDEO_CX2341X
select VIDEO_CX25840
diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile
index a0389014fa8..26ce0d6eaee 100644
--- a/drivers/media/video/ivtv/Makefile
+++ b/drivers/media/video/ivtv/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_VIDEO_IVTV) += ivtv.o
obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o
EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c
index e908649ea37..4fb8faefe2c 100644
--- a/drivers/media/video/ivtv/ivtv-cards.c
+++ b/drivers/media/video/ivtv/ivtv-cards.c
@@ -40,6 +40,8 @@
#define MSP_MONO MSP_INPUT(MSP_IN_MONO, MSP_IN_TUNER1, \
MSP_DSP_IN_SCART, MSP_DSP_IN_SCART)
+#define V4L2_STD_NOT_MN (V4L2_STD_PAL|V4L2_STD_SECAM)
+
/* usual i2c tuner addresses to probe */
static struct ivtv_card_tuner_i2c ivtv_i2c_std = {
.radio = { I2C_CLIENT_END },
@@ -298,7 +300,7 @@ static const struct ivtv_card ivtv_card_mpg600 = {
.gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 },
.tuners = {
/* The PAL tuner is confirmed */
- { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME },
+ { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FQ1216ME },
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_mpg600,
@@ -339,7 +341,7 @@ static const struct ivtv_card ivtv_card_mpg160 = {
.lang1 = 0x0004, .lang2 = 0x0000, .both = 0x0008 },
.gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 },
.tuners = {
- { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME },
+ { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FQ1216ME },
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_mpg160,
@@ -375,7 +377,7 @@ static const struct ivtv_card ivtv_card_pg600 = {
{ IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
},
.tuners = {
- { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME },
+ { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FQ1216ME },
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_pg600,
@@ -416,7 +418,7 @@ static const struct ivtv_card ivtv_card_avc2410 = {
on the country/region setting of the user to decide which tuner
is available. */
.tuners = {
- { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
{ .std = V4L2_STD_ALL - V4L2_STD_NTSC_M_JP,
.tuner = TUNER_PHILIPS_FM1236_MK3 },
{ .std = V4L2_STD_NTSC_M_JP, .tuner = TUNER_PHILIPS_FQ1286 },
@@ -490,7 +492,7 @@ static const struct ivtv_card ivtv_card_tg5000tv = {
.gpio_video_input = { .mask = 0x0030, .tuner = 0x0000,
.composite = 0x0010, .svideo = 0x0020 },
.tuners = {
- { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
+ { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_tg5000tv,
.i2c = &ivtv_i2c_std,
@@ -521,7 +523,7 @@ static const struct ivtv_card ivtv_card_va2000 = {
{ IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER },
},
.tuners = {
- { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
+ { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_va2000,
.i2c = &ivtv_i2c_std,
@@ -565,7 +567,7 @@ static const struct ivtv_card ivtv_card_cx23416gyc = {
.gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000,
.f44100 = 0x4000, .f48000 = 0x8000 },
.tuners = {
- { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
},
.pci_list = ivtv_pci_cx23416gyc,
@@ -597,7 +599,7 @@ static const struct ivtv_card ivtv_card_cx23416gyc_nogr = {
.gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000,
.f44100 = 0x4000, .f48000 = 0x8000 },
.tuners = {
- { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
},
.i2c = &ivtv_i2c_std,
@@ -627,7 +629,7 @@ static const struct ivtv_card ivtv_card_cx23416gyc_nogrycs = {
.gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000,
.f44100 = 0x4000, .f48000 = 0x8000 },
.tuners = {
- { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
{ .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 },
},
.i2c = &ivtv_i2c_std,
@@ -667,7 +669,7 @@ static const struct ivtv_card ivtv_card_gv_mvprx = {
.gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 },
.tuners = {
/* This card has the Panasonic VP27 tuner */
- { .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 },
+ { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 },
},
.pci_list = ivtv_pci_gv_mvprx,
.i2c = &ivtv_i2c_std,
@@ -704,7 +706,7 @@ static const struct ivtv_card ivtv_card_gv_mvprx2e = {
.gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 },
.tuners = {
/* This card has the Panasonic VP27 tuner */
- { .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 },
+ { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 },
},
.pci_list = ivtv_pci_gv_mvprx2e,
.i2c = &ivtv_i2c_std,
@@ -739,7 +741,7 @@ static const struct ivtv_card ivtv_card_gotview_pci_dvd = {
.gpio_init = { .direction = 0xf000, .initial_value = 0xA000 },
.tuners = {
/* This card has a Philips FQ1216ME MK3 tuner */
- { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
},
.pci_list = ivtv_pci_gotview_pci_dvd,
.i2c = &ivtv_i2c_std,
@@ -778,7 +780,7 @@ static const struct ivtv_card ivtv_card_gotview_pci_dvd2 = {
.gpio_audio_input = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 },
.tuners = {
/* This card has a Philips FQ1216ME MK5 tuner */
- { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
+ { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 },
},
.pci_list = ivtv_pci_gotview_pci_dvd2,
.i2c = &ivtv_i2c_std,
@@ -856,7 +858,7 @@ static const struct ivtv_card ivtv_card_dctmvtvp1 = {
.gpio_video_input = { .mask = 0x0030, .tuner = 0x0000,
.composite = 0x0010, .svideo = 0x0020},
.tuners = {
- { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 },
+ { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 },
},
.pci_list = ivtv_pci_dctmvtvp1,
.i2c = &ivtv_i2c_std,
@@ -875,6 +877,7 @@ static const struct ivtv_card_pci_info ivtv_pci_pg600v2[] = {
static const struct ivtv_card ivtv_card_pg600v2 = {
.type = IVTV_CARD_PG600V2,
.name = "Yuan PG600-2, GotView PCI DVD Lite",
+ .comment = "only Composite and S-Video inputs are supported, not the tuner\n",
.v4l2_capabilities = IVTV_CAP_ENCODER,
.hw_video = IVTV_HW_CX25840,
.hw_audio = IVTV_HW_CX25840,
@@ -921,6 +924,7 @@ static const struct ivtv_card ivtv_card_club3d = {
},
.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
.gpio_init = { .direction = 0x1000, .initial_value = 0x1000 }, /* tuner reset */
+ .xceive_pin = 12,
.tuners = {
{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
},
@@ -944,15 +948,22 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = {
.hw_video = IVTV_HW_CX25840,
.hw_audio = IVTV_HW_CX25840,
.hw_audio_ctrl = IVTV_HW_CX25840,
- .hw_all = IVTV_HW_CX25840 | IVTV_HW_WM8739,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739,
.video_inputs = {
- { IVTV_CARD_INPUT_SVIDEO1, 0, CX25840_SVIDEO3 },
- { IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE1 },
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
},
.audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
{ IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 },
},
- .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 }, /* enable line-in */
+ /* enable line-in */
+ .gpio_init = { .direction = 0xe400, .initial_value = 0x4400 },
+ .xceive_pin = 10,
+ .tuners = {
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
.pci_list = ivtv_pci_avertv_mce116,
.i2c = &ivtv_i2c_std,
};
@@ -990,7 +1001,7 @@ static const struct ivtv_card ivtv_card_aver_pvr150 = {
.gpio_audio_input = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 },
.tuners = {
/* This card has a Partsnic PTI-5NF05 tuner */
- { .std = V4L2_STD_525_60, .tuner = TUNER_TCL_2002N },
+ { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_TCL_2002N },
},
.pci_list = ivtv_pci_aver_pvr150,
.i2c = &ivtv_i2c_radio,
@@ -1058,12 +1069,48 @@ static const struct ivtv_card ivtv_card_asus_falcon2 = {
},
.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, M52790_IN_TUNER },
.tuners = {
- { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+ { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 },
},
.pci_list = ivtv_pci_asus_falcon2,
.i2c = &ivtv_i2c_std,
};
+/* ------------------------------------------------------------------------- */
+
+/* AVerMedia M104 miniPCI card */
+
+static const struct ivtv_card_pci_info ivtv_pci_aver_m104[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc136 },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_aver_m104 = {
+ .type = IVTV_CARD_AVER_M104,
+ .name = "AVerMedia M104",
+ .comment = "Not yet supported!\n",
+ .v4l2_capabilities = 0, /*IVTV_CAP_ENCODER,*/
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_SVIDEO1, 0, CX25840_SVIDEO3 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 },
+ },
+ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 },
+ /* enable line-in + reset tuner */
+ .gpio_init = { .direction = 0xe400, .initial_value = 0x4000 },
+ .xceive_pin = 10,
+ .tuners = {
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
+ .pci_list = ivtv_pci_aver_m104,
+ .i2c = &ivtv_i2c_std,
+};
+
static const struct ivtv_card *ivtv_card_list[] = {
&ivtv_card_pvr250,
&ivtv_card_pvr350,
@@ -1089,6 +1136,7 @@ static const struct ivtv_card *ivtv_card_list[] = {
&ivtv_card_asus_falcon2,
&ivtv_card_aver_pvr150,
&ivtv_card_aver_ezmaker,
+ &ivtv_card_aver_m104,
/* Variations of standard cards but with the same PCI IDs.
These cards must come last in this list. */
@@ -1120,7 +1168,8 @@ int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input)
if (index >= itv->nof_inputs)
return -EINVAL;
input->index = index;
- strcpy(input->name, input_strs[card_input->video_type - 1]);
+ strlcpy(input->name, input_strs[card_input->video_type - 1],
+ sizeof(input->name));
input->type = (card_input->video_type == IVTV_CARD_INPUT_VID_TUNER ?
V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA);
input->audioset = (1 << itv->nof_audio_inputs) - 1;
@@ -1137,7 +1186,7 @@ int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output)
if (index >= itv->card->nof_outputs)
return -EINVAL;
output->index = index;
- strcpy(output->name, card_output->name);
+ strlcpy(output->name, card_output->name, sizeof(output->name));
output->type = V4L2_OUTPUT_TYPE_ANALOG;
output->audioset = 1;
output->std = V4L2_STD_ALL;
@@ -1156,7 +1205,8 @@ int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *audio)
memset(audio, 0, sizeof(*audio));
if (index >= itv->nof_audio_inputs)
return -EINVAL;
- strcpy(audio->name, input_strs[aud_input->audio_type - 1]);
+ strlcpy(audio->name, input_strs[aud_input->audio_type - 1],
+ sizeof(audio->name));
audio->index = index;
audio->capability = V4L2_AUDCAP_STEREO;
return 0;
@@ -1167,6 +1217,6 @@ int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *aud
memset(aud_output, 0, sizeof(*aud_output));
if (itv->card->video_outputs == NULL || index != 0)
return -EINVAL;
- strcpy(aud_output->name, "A/V Audio Out");
+ strlcpy(aud_output->name, "A/V Audio Out", sizeof(aud_output->name));
return 0;
}
diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h
index 9186fa2ee5f..748485dcebb 100644
--- a/drivers/media/video/ivtv/ivtv-cards.h
+++ b/drivers/media/video/ivtv/ivtv-cards.h
@@ -48,7 +48,8 @@
#define IVTV_CARD_ASUS_FALCON2 21 /* ASUS Falcon2 */
#define IVTV_CARD_AVER_PVR150PLUS 22 /* AVerMedia PVR-150 Plus */
#define IVTV_CARD_AVER_EZMAKER 23 /* AVerMedia EZMaker PCI Deluxe */
-#define IVTV_CARD_LAST 23
+#define IVTV_CARD_AVER_M104 24 /* AverMedia M104 miniPCI card */
+#define IVTV_CARD_LAST 24
/* Variants of existing cards but with the same PCI IDs. The driver
detects these based on other device information.
@@ -244,6 +245,7 @@ struct ivtv_card_tuner_i2c {
struct ivtv_card {
int type;
char *name;
+ char *comment;
u32 v4l2_capabilities;
u32 hw_video; /* hardware used to process video */
u32 hw_audio; /* hardware used to process audio */
@@ -256,6 +258,7 @@ struct ivtv_card {
int nof_outputs;
const struct ivtv_card_output *video_outputs;
u8 gr_config; /* config byte for the ghost reduction device */
+ u8 xceive_pin; /* XCeive tuner GPIO reset pin */
/* GPIO card-specific settings */
struct ivtv_gpio_init gpio_init;
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 065df53f80f..47b5649729d 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -190,6 +190,7 @@ MODULE_PARM_DESC(cardtype,
"\t\t\t22 = ASUS Falcon2\n"
"\t\t\t23 = AverMedia PVR-150 Plus\n"
"\t\t\t24 = AverMedia EZMaker PCI Deluxe\n"
+ "\t\t\t25 = AverMedia M104 (not yet working)\n"
"\t\t\t 0 = Autodetect (default)\n"
"\t\t\t-1 = Ignore this card\n\t\t");
MODULE_PARM_DESC(pal, "Set PAL standard: BGH, DK, I, M, N, Nc, 60");
@@ -871,7 +872,7 @@ static void ivtv_load_and_init_modules(struct ivtv *itv)
unsigned i;
/* load modules */
-#ifndef CONFIG_VIDEO_TUNER
+#ifndef CONFIG_MEDIA_TUNER
hw = ivtv_request_module(itv, hw, "tuner", IVTV_HW_TUNER);
#endif
#ifndef CONFIG_VIDEO_CX25840
@@ -1097,6 +1098,13 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
The PCI IDs are not always reliable. */
ivtv_process_eeprom(itv);
}
+ if (itv->card->comment)
+ IVTV_INFO("%s", itv->card->comment);
+ if (itv->card->v4l2_capabilities == 0) {
+ /* card was detected but is not supported */
+ retval = -ENODEV;
+ goto free_i2c;
+ }
if (itv->std == 0) {
itv->std = V4L2_STD_NTSC_M;
@@ -1195,13 +1203,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std);
}
- retval = ivtv_streams_setup(itv);
- if (retval) {
- IVTV_ERR("Error %d setting up streams\n", retval);
- goto free_i2c;
- }
-
- IVTV_DEBUG_IRQ("Masking interrupts\n");
/* clear interrupt mask, effectively disabling interrupts */
ivtv_set_irq_mask(itv, 0xffffffff);
@@ -1210,32 +1211,38 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
IRQF_SHARED | IRQF_DISABLED, itv->name, (void *)itv);
if (retval) {
IVTV_ERR("Failed to register irq %d\n", retval);
- goto free_streams;
+ goto free_i2c;
+ }
+
+ retval = ivtv_streams_setup(itv);
+ if (retval) {
+ IVTV_ERR("Error %d setting up streams\n", retval);
+ goto free_irq;
}
retval = ivtv_streams_register(itv);
if (retval) {
IVTV_ERR("Error %d registering devices\n", retval);
- goto free_irq;
+ goto free_streams;
}
IVTV_INFO("Initialized card #%d: %s\n", itv->num, itv->card_name);
return 0;
- free_irq:
- free_irq(itv->dev->irq, (void *)itv);
- free_streams:
+free_streams:
ivtv_streams_cleanup(itv);
- free_i2c:
+free_irq:
+ free_irq(itv->dev->irq, (void *)itv);
+free_i2c:
exit_ivtv_i2c(itv);
- free_io:
+free_io:
ivtv_iounmap(itv);
- free_mem:
+free_mem:
release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
if (itv->has_cx23415)
release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
- free_workqueue:
+free_workqueue:
destroy_workqueue(itv->irq_work_queues);
- err:
+err:
if (retval == 0)
retval = -ENODEV;
IVTV_ERR("Error %d on initialization\n", retval);
diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c
index 688cd385668..d8ac09f3cce 100644
--- a/drivers/media/video/ivtv/ivtv-gpio.c
+++ b/drivers/media/video/ivtv/ivtv-gpio.c
@@ -128,20 +128,17 @@ int ivtv_reset_tuner_gpio(void *dev, int cmd, int value)
{
struct i2c_algo_bit_data *algo = dev;
struct ivtv *itv = algo->data;
- int curdir, curout;
+ u32 curout;
if (cmd != XC2028_TUNER_RESET)
return 0;
IVTV_DEBUG_INFO("Resetting tuner\n");
curout = read_reg(IVTV_REG_GPIO_OUT);
- curdir = read_reg(IVTV_REG_GPIO_DIR);
- curdir |= (1 << 12); /* GPIO bit 12 */
-
- curout &= ~(1 << 12);
+ curout &= ~(1 << itv->card->xceive_pin);
write_reg(curout, IVTV_REG_GPIO_OUT);
schedule_timeout_interruptible(msecs_to_jiffies(1));
- curout |= (1 << 12);
+ curout |= 1 << itv->card->xceive_pin;
write_reg(curout, IVTV_REG_GPIO_OUT);
schedule_timeout_interruptible(msecs_to_jiffies(1));
return 0;
diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c
index 9824eafee02..771adf47e94 100644
--- a/drivers/media/video/ivtv/ivtv-i2c.c
+++ b/drivers/media/video/ivtv/ivtv-i2c.c
@@ -167,7 +167,8 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
return -1;
id = hw_driverids[idx];
memset(&info, 0, sizeof(info));
- strcpy(info.driver_name, hw_drivernames[idx]);
+ strlcpy(info.driver_name, hw_drivernames[idx],
+ sizeof(info.driver_name));
info.addr = hw_addrs[idx];
for (i = 0; itv->i2c_clients[i] && i < I2C_CLIENTS_MAX; i++) {}
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
index 15cac181212..d508b5d0538 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -243,20 +243,31 @@ static int ivtv_validate_speed(int cur_speed, int new_speed)
int fact = new_speed < 0 ? -1 : 1;
int s;
- if (new_speed < 0) new_speed = -new_speed;
- if (cur_speed < 0) cur_speed = -cur_speed;
+ if (cur_speed == 0)
+ cur_speed = 1000;
+ if (new_speed < 0)
+ new_speed = -new_speed;
+ if (cur_speed < 0)
+ cur_speed = -cur_speed;
if (cur_speed <= new_speed) {
- if (new_speed > 1500) return fact * 2000;
- if (new_speed > 1000) return fact * 1500;
+ if (new_speed > 1500)
+ return fact * 2000;
+ if (new_speed > 1000)
+ return fact * 1500;
}
else {
- if (new_speed >= 2000) return fact * 2000;
- if (new_speed >= 1500) return fact * 1500;
- if (new_speed >= 1000) return fact * 1000;
- }
- if (new_speed == 0) return 1000;
- if (new_speed == 1 || new_speed == 1000) return fact * new_speed;
+ if (new_speed >= 2000)
+ return fact * 2000;
+ if (new_speed >= 1500)
+ return fact * 1500;
+ if (new_speed >= 1000)
+ return fact * 1000;
+ }
+ if (new_speed == 0)
+ return 1000;
+ if (new_speed == 1 || new_speed == 1000)
+ return fact * new_speed;
s = new_speed;
new_speed = 1000 / new_speed;
@@ -741,10 +752,9 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void
struct v4l2_capability *vcap = arg;
memset(vcap, 0, sizeof(*vcap));
- strcpy(vcap->driver, IVTV_DRIVER_NAME); /* driver name */
- strncpy(vcap->card, itv->card_name,
- sizeof(vcap->card)-1); /* card type */
- strcpy(vcap->bus_info, pci_name(itv->dev)); /* bus info... */
+ strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver));
+ strlcpy(vcap->card, itv->card_name, sizeof(vcap->card));
+ strlcpy(vcap->bus_info, pci_name(itv->dev), sizeof(vcap->bus_info));
vcap->version = IVTV_DRIVER_VERSION; /* version */
vcap->capabilities = itv->v4l2_cap; /* capabilities */
@@ -1018,7 +1028,7 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void
ivtv_std_60hz : ivtv_std_50hz;
vs->index = idx;
vs->id = enum_stds[idx].std;
- strcpy(vs->name, enum_stds[idx].name);
+ strlcpy(vs->name, enum_stds[idx].name, sizeof(vs->name));
break;
}
@@ -1102,10 +1112,10 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void
ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt);
if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
- strcpy(vt->name, "ivtv Radio Tuner");
+ strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name));
vt->type = V4L2_TUNER_RADIO;
} else {
- strcpy(vt->name, "ivtv TV Tuner");
+ strlcpy(vt->name, "ivtv TV Tuner", sizeof(vt->name));
vt->type = V4L2_TUNER_ANALOG_TV;
}
break;
diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c
index a329c4689db..d8ba3a4a876 100644
--- a/drivers/media/video/ivtv/ivtv-irq.c
+++ b/drivers/media/video/ivtv/ivtv-irq.c
@@ -384,7 +384,7 @@ static void ivtv_dma_enc_start_xfer(struct ivtv_stream *s)
ivtv_stream_sync_for_device(s);
write_reg(s->sg_handle, IVTV_REG_ENCDMAADDR);
write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER);
- itv->dma_timer.expires = jiffies + msecs_to_jiffies(100);
+ itv->dma_timer.expires = jiffies + msecs_to_jiffies(300);
add_timer(&itv->dma_timer);
}
@@ -400,7 +400,7 @@ static void ivtv_dma_dec_start_xfer(struct ivtv_stream *s)
ivtv_stream_sync_for_device(s);
write_reg(s->sg_handle, IVTV_REG_DECDMAADDR);
write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
- itv->dma_timer.expires = jiffies + msecs_to_jiffies(100);
+ itv->dma_timer.expires = jiffies + msecs_to_jiffies(300);
add_timer(&itv->dma_timer);
}
diff --git a/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/video/ivtv/ivtv-version.h
index 0f1d4cc4b4d..02c5ab071d1 100644
--- a/drivers/media/video/ivtv/ivtv-version.h
+++ b/drivers/media/video/ivtv/ivtv-version.h
@@ -23,7 +23,7 @@
#define IVTV_DRIVER_NAME "ivtv"
#define IVTV_DRIVER_VERSION_MAJOR 1
#define IVTV_DRIVER_VERSION_MINOR 2
-#define IVTV_DRIVER_VERSION_PATCHLEVEL 0
+#define IVTV_DRIVER_VERSION_PATCHLEVEL 1
#define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL)
#define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL)
diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c
index 3b23fc05f7c..df789f683e6 100644
--- a/drivers/media/video/ivtv/ivtvfb.c
+++ b/drivers/media/video/ivtv/ivtvfb.c
@@ -532,7 +532,7 @@ static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix)
IVTVFB_DEBUG_INFO("ivtvfb_get_fix\n");
memset(fix, 0, sizeof(struct fb_fix_screeninfo));
- strcpy(fix->id, "cx23415 TV out");
+ strlcpy(fix->id, "cx23415 TV out", sizeof(fix->id));
fix->smem_start = oi->video_pbase;
fix->smem_len = oi->video_buffer_size;
fix->type = FB_TYPE_PACKED_PIXELS;
diff --git a/drivers/media/video/mt20xx.c b/drivers/media/video/mt20xx.c
deleted file mode 100644
index fbcb2823373..00000000000
--- a/drivers/media/video/mt20xx.c
+++ /dev/null
@@ -1,670 +0,0 @@
-/*
- * i2c tv tuner chip device driver
- * controls microtune tuners, mt2032 + mt2050 at the moment.
- *
- * This "mt20xx" module was split apart from the original "tuner" module.
- */
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/videodev.h>
-#include "tuner-i2c.h"
-#include "mt20xx.h"
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "enable verbose debug messages");
-
-/* ---------------------------------------------------------------------- */
-
-static unsigned int optimize_vco = 1;
-module_param(optimize_vco, int, 0644);
-
-static unsigned int tv_antenna = 1;
-module_param(tv_antenna, int, 0644);
-
-static unsigned int radio_antenna;
-module_param(radio_antenna, int, 0644);
-
-/* ---------------------------------------------------------------------- */
-
-#define MT2032 0x04
-#define MT2030 0x06
-#define MT2040 0x07
-#define MT2050 0x42
-
-static char *microtune_part[] = {
- [ MT2030 ] = "MT2030",
- [ MT2032 ] = "MT2032",
- [ MT2040 ] = "MT2040",
- [ MT2050 ] = "MT2050",
-};
-
-struct microtune_priv {
- struct tuner_i2c_props i2c_props;
-
- unsigned int xogc;
- //unsigned int radio_if2;
-
- u32 frequency;
-};
-
-static int microtune_release(struct dvb_frontend *fe)
-{
- kfree(fe->tuner_priv);
- fe->tuner_priv = NULL;
-
- return 0;
-}
-
-static int microtune_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct microtune_priv *priv = fe->tuner_priv;
- *frequency = priv->frequency;
- return 0;
-}
-
-// IsSpurInBand()?
-static int mt2032_spurcheck(struct dvb_frontend *fe,
- int f1, int f2, int spectrum_from,int spectrum_to)
-{
- struct microtune_priv *priv = fe->tuner_priv;
- int n1=1,n2,f;
-
- f1=f1/1000; //scale to kHz to avoid 32bit overflows
- f2=f2/1000;
- spectrum_from/=1000;
- spectrum_to/=1000;
-
- tuner_dbg("spurcheck f1=%d f2=%d from=%d to=%d\n",
- f1,f2,spectrum_from,spectrum_to);
-
- do {
- n2=-n1;
- f=n1*(f1-f2);
- do {
- n2--;
- f=f-f2;
- tuner_dbg("spurtest n1=%d n2=%d ftest=%d\n",n1,n2,f);
-
- if( (f>spectrum_from) && (f<spectrum_to))
- tuner_dbg("mt2032 spurcheck triggered: %d\n",n1);
- } while ( (f>(f2-spectrum_to)) || (n2>-5));
- n1++;
- } while (n1<5);
-
- return 1;
-}
-
-static int mt2032_compute_freq(struct dvb_frontend *fe,
- unsigned int rfin,
- unsigned int if1, unsigned int if2,
- unsigned int spectrum_from,
- unsigned int spectrum_to,
- unsigned char *buf,
- int *ret_sel,
- unsigned int xogc) //all in Hz
-{
- struct microtune_priv *priv = fe->tuner_priv;
- unsigned int fref,lo1,lo1n,lo1a,s,sel,lo1freq, desired_lo1,
- desired_lo2,lo2,lo2n,lo2a,lo2num,lo2freq;
-
- fref= 5250 *1000; //5.25MHz
- desired_lo1=rfin+if1;
-
- lo1=(2*(desired_lo1/1000)+(fref/1000)) / (2*fref/1000);
- lo1n=lo1/8;
- lo1a=lo1-(lo1n*8);
-
- s=rfin/1000/1000+1090;
-
- if(optimize_vco) {
- if(s>1890) sel=0;
- else if(s>1720) sel=1;
- else if(s>1530) sel=2;
- else if(s>1370) sel=3;
- else sel=4; // >1090
- }
- else {
- if(s>1790) sel=0; // <1958
- else if(s>1617) sel=1;
- else if(s>1449) sel=2;
- else if(s>1291) sel=3;
- else sel=4; // >1090
- }
- *ret_sel=sel;
-
- lo1freq=(lo1a+8*lo1n)*fref;
-
- tuner_dbg("mt2032: rfin=%d lo1=%d lo1n=%d lo1a=%d sel=%d, lo1freq=%d\n",
- rfin,lo1,lo1n,lo1a,sel,lo1freq);
-
- desired_lo2=lo1freq-rfin-if2;
- lo2=(desired_lo2)/fref;
- lo2n=lo2/8;
- lo2a=lo2-(lo2n*8);
- lo2num=((desired_lo2/1000)%(fref/1000))* 3780/(fref/1000); //scale to fit in 32bit arith
- lo2freq=(lo2a+8*lo2n)*fref + lo2num*(fref/1000)/3780*1000;
-
- tuner_dbg("mt2032: rfin=%d lo2=%d lo2n=%d lo2a=%d num=%d lo2freq=%d\n",
- rfin,lo2,lo2n,lo2a,lo2num,lo2freq);
-
- if(lo1a<0 || lo1a>7 || lo1n<17 ||lo1n>48 || lo2a<0 ||lo2a >7 ||lo2n<17 || lo2n>30) {
- tuner_info("mt2032: frequency parameters out of range: %d %d %d %d\n",
- lo1a, lo1n, lo2a,lo2n);
- return(-1);
- }
-
- mt2032_spurcheck(fe, lo1freq, desired_lo2, spectrum_from, spectrum_to);
- // should recalculate lo1 (one step up/down)
-
- // set up MT2032 register map for transfer over i2c
- buf[0]=lo1n-1;
- buf[1]=lo1a | (sel<<4);
- buf[2]=0x86; // LOGC
- buf[3]=0x0f; //reserved
- buf[4]=0x1f;
- buf[5]=(lo2n-1) | (lo2a<<5);
- if(rfin >400*1000*1000)
- buf[6]=0xe4;
- else
- buf[6]=0xf4; // set PKEN per rev 1.2
- buf[7]=8+xogc;
- buf[8]=0xc3; //reserved
- buf[9]=0x4e; //reserved
- buf[10]=0xec; //reserved
- buf[11]=(lo2num&0xff);
- buf[12]=(lo2num>>8) |0x80; // Lo2RST
-
- return 0;
-}
-
-static int mt2032_check_lo_lock(struct dvb_frontend *fe)
-{
- struct microtune_priv *priv = fe->tuner_priv;
- int try,lock=0;
- unsigned char buf[2];
-
- for(try=0;try<10;try++) {
- buf[0]=0x0e;
- tuner_i2c_xfer_send(&priv->i2c_props,buf,1);
- tuner_i2c_xfer_recv(&priv->i2c_props,buf,1);
- tuner_dbg("mt2032 Reg.E=0x%02x\n",buf[0]);
- lock=buf[0] &0x06;
-
- if (lock==6)
- break;
-
- tuner_dbg("mt2032: pll wait 1ms for lock (0x%2x)\n",buf[0]);
- udelay(1000);
- }
- return lock;
-}
-
-static int mt2032_optimize_vco(struct dvb_frontend *fe,int sel,int lock)
-{
- struct microtune_priv *priv = fe->tuner_priv;
- unsigned char buf[2];
- int tad1;
-
- buf[0]=0x0f;
- tuner_i2c_xfer_send(&priv->i2c_props,buf,1);
- tuner_i2c_xfer_recv(&priv->i2c_props,buf,1);
- tuner_dbg("mt2032 Reg.F=0x%02x\n",buf[0]);
- tad1=buf[0]&0x07;
-
- if(tad1 ==0) return lock;
- if(tad1 ==1) return lock;
-
- if(tad1==2) {
- if(sel==0)
- return lock;
- else sel--;
- }
- else {
- if(sel<4)
- sel++;
- else
- return lock;
- }
-
- tuner_dbg("mt2032 optimize_vco: sel=%d\n",sel);
-
- buf[0]=0x0f;
- buf[1]=sel;
- tuner_i2c_xfer_send(&priv->i2c_props,buf,2);
- lock=mt2032_check_lo_lock(fe);
- return lock;
-}
-
-
-static void mt2032_set_if_freq(struct dvb_frontend *fe, unsigned int rfin,
- unsigned int if1, unsigned int if2,
- unsigned int from, unsigned int to)
-{
- unsigned char buf[21];
- int lint_try,ret,sel,lock=0;
- struct microtune_priv *priv = fe->tuner_priv;
-
- tuner_dbg("mt2032_set_if_freq rfin=%d if1=%d if2=%d from=%d to=%d\n",
- rfin,if1,if2,from,to);
-
- buf[0]=0;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,1);
- tuner_i2c_xfer_recv(&priv->i2c_props,buf,21);
-
- buf[0]=0;
- ret=mt2032_compute_freq(fe,rfin,if1,if2,from,to,&buf[1],&sel,priv->xogc);
- if (ret<0)
- return;
-
- // send only the relevant registers per Rev. 1.2
- buf[0]=0;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,4);
- buf[5]=5;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+5,4);
- buf[11]=11;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+11,3);
- if(ret!=3)
- tuner_warn("i2c i/o error: rc == %d (should be 3)\n",ret);
-
- // wait for PLLs to lock (per manual), retry LINT if not.
- for(lint_try=0; lint_try<2; lint_try++) {
- lock=mt2032_check_lo_lock(fe);
-
- if(optimize_vco)
- lock=mt2032_optimize_vco(fe,sel,lock);
- if(lock==6) break;
-
- tuner_dbg("mt2032: re-init PLLs by LINT\n");
- buf[0]=7;
- buf[1]=0x80 +8+priv->xogc; // set LINT to re-init PLLs
- tuner_i2c_xfer_send(&priv->i2c_props,buf,2);
- mdelay(10);
- buf[1]=8+priv->xogc;
- tuner_i2c_xfer_send(&priv->i2c_props,buf,2);
- }
-
- if (lock!=6)
- tuner_warn("MT2032 Fatal Error: PLLs didn't lock.\n");
-
- buf[0]=2;
- buf[1]=0x20; // LOGC for optimal phase noise
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2);
- if (ret!=2)
- tuner_warn("i2c i/o error: rc == %d (should be 2)\n",ret);
-}
-
-
-static int mt2032_set_tv_freq(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- int if2,from,to;
-
- // signal bandwidth and picture carrier
- if (params->std & V4L2_STD_525_60) {
- // NTSC
- from = 40750*1000;
- to = 46750*1000;
- if2 = 45750*1000;
- } else {
- // PAL
- from = 32900*1000;
- to = 39900*1000;
- if2 = 38900*1000;
- }
-
- mt2032_set_if_freq(fe, params->frequency*62500,
- 1090*1000*1000, if2, from, to);
-
- return 0;
-}
-
-static int mt2032_set_radio_freq(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct microtune_priv *priv = fe->tuner_priv;
- int if2;
-
- if (params->std & V4L2_STD_525_60) {
- tuner_dbg("pinnacle ntsc\n");
- if2 = 41300 * 1000;
- } else {
- tuner_dbg("pinnacle pal\n");
- if2 = 33300 * 1000;
- }
-
- // per Manual for FM tuning: first if center freq. 1085 MHz
- mt2032_set_if_freq(fe, params->frequency * 125 / 2,
- 1085*1000*1000,if2,if2,if2);
-
- return 0;
-}
-
-static int mt2032_set_params(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct microtune_priv *priv = fe->tuner_priv;
- int ret = -EINVAL;
-
- switch (params->mode) {
- case V4L2_TUNER_RADIO:
- ret = mt2032_set_radio_freq(fe, params);
- priv->frequency = params->frequency * 125 / 2;
- break;
- case V4L2_TUNER_ANALOG_TV:
- case V4L2_TUNER_DIGITAL_TV:
- ret = mt2032_set_tv_freq(fe, params);
- priv->frequency = params->frequency * 62500;
- break;
- }
-
- return ret;
-}
-
-static struct dvb_tuner_ops mt2032_tuner_ops = {
- .set_analog_params = mt2032_set_params,
- .release = microtune_release,
- .get_frequency = microtune_get_frequency,
-};
-
-// Initialization as described in "MT203x Programming Procedures", Rev 1.2, Feb.2001
-static int mt2032_init(struct dvb_frontend *fe)
-{
- struct microtune_priv *priv = fe->tuner_priv;
- unsigned char buf[21];
- int ret,xogc,xok=0;
-
- // Initialize Registers per spec.
- buf[1]=2; // Index to register 2
- buf[2]=0xff;
- buf[3]=0x0f;
- buf[4]=0x1f;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+1,4);
-
- buf[5]=6; // Index register 6
- buf[6]=0xe4;
- buf[7]=0x8f;
- buf[8]=0xc3;
- buf[9]=0x4e;
- buf[10]=0xec;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+5,6);
-
- buf[12]=13; // Index register 13
- buf[13]=0x32;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+12,2);
-
- // Adjust XOGC (register 7), wait for XOK
- xogc=7;
- do {
- tuner_dbg("mt2032: xogc = 0x%02x\n",xogc&0x07);
- mdelay(10);
- buf[0]=0x0e;
- tuner_i2c_xfer_send(&priv->i2c_props,buf,1);
- tuner_i2c_xfer_recv(&priv->i2c_props,buf,1);
- xok=buf[0]&0x01;
- tuner_dbg("mt2032: xok = 0x%02x\n",xok);
- if (xok == 1) break;
-
- xogc--;
- tuner_dbg("mt2032: xogc = 0x%02x\n",xogc&0x07);
- if (xogc == 3) {
- xogc=4; // min. 4 per spec
- break;
- }
- buf[0]=0x07;
- buf[1]=0x88 + xogc;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2);
- if (ret!=2)
- tuner_warn("i2c i/o error: rc == %d (should be 2)\n",ret);
- } while (xok != 1 );
- priv->xogc=xogc;
-
- memcpy(&fe->ops.tuner_ops, &mt2032_tuner_ops, sizeof(struct dvb_tuner_ops));
-
- return(1);
-}
-
-static void mt2050_set_antenna(struct dvb_frontend *fe, unsigned char antenna)
-{
- struct microtune_priv *priv = fe->tuner_priv;
- unsigned char buf[2];
- int ret;
-
- buf[0] = 6;
- buf[1] = antenna ? 0x11 : 0x10;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2);
- tuner_dbg("mt2050: enabled antenna connector %d\n", antenna);
-}
-
-static void mt2050_set_if_freq(struct dvb_frontend *fe,unsigned int freq, unsigned int if2)
-{
- struct microtune_priv *priv = fe->tuner_priv;
- unsigned int if1=1218*1000*1000;
- unsigned int f_lo1,f_lo2,lo1,lo2,f_lo1_modulo,f_lo2_modulo,num1,num2,div1a,div1b,div2a,div2b;
- int ret;
- unsigned char buf[6];
-
- tuner_dbg("mt2050_set_if_freq freq=%d if1=%d if2=%d\n",
- freq,if1,if2);
-
- f_lo1=freq+if1;
- f_lo1=(f_lo1/1000000)*1000000;
-
- f_lo2=f_lo1-freq-if2;
- f_lo2=(f_lo2/50000)*50000;
-
- lo1=f_lo1/4000000;
- lo2=f_lo2/4000000;
-
- f_lo1_modulo= f_lo1-(lo1*4000000);
- f_lo2_modulo= f_lo2-(lo2*4000000);
-
- num1=4*f_lo1_modulo/4000000;
- num2=4096*(f_lo2_modulo/1000)/4000;
-
- // todo spurchecks
-
- div1a=(lo1/12)-1;
- div1b=lo1-(div1a+1)*12;
-
- div2a=(lo2/8)-1;
- div2b=lo2-(div2a+1)*8;
-
- if (debug > 1) {
- tuner_dbg("lo1 lo2 = %d %d\n", lo1, lo2);
- tuner_dbg("num1 num2 div1a div1b div2a div2b= %x %x %x %x %x %x\n",
- num1,num2,div1a,div1b,div2a,div2b);
- }
-
- buf[0]=1;
- buf[1]= 4*div1b + num1;
- if(freq<275*1000*1000) buf[1] = buf[1]|0x80;
-
- buf[2]=div1a;
- buf[3]=32*div2b + num2/256;
- buf[4]=num2-(num2/256)*256;
- buf[5]=div2a;
- if(num2!=0) buf[5]=buf[5]|0x40;
-
- if (debug > 1) {
- int i;
- tuner_dbg("bufs is: ");
- for(i=0;i<6;i++)
- printk("%x ",buf[i]);
- printk("\n");
- }
-
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,6);
- if (ret!=6)
- tuner_warn("i2c i/o error: rc == %d (should be 6)\n",ret);
-}
-
-static int mt2050_set_tv_freq(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- unsigned int if2;
-
- if (params->std & V4L2_STD_525_60) {
- // NTSC
- if2 = 45750*1000;
- } else {
- // PAL
- if2 = 38900*1000;
- }
- if (V4L2_TUNER_DIGITAL_TV == params->mode) {
- // DVB (pinnacle 300i)
- if2 = 36150*1000;
- }
- mt2050_set_if_freq(fe, params->frequency*62500, if2);
- mt2050_set_antenna(fe, tv_antenna);
-
- return 0;
-}
-
-static int mt2050_set_radio_freq(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct microtune_priv *priv = fe->tuner_priv;
- int if2;
-
- if (params->std & V4L2_STD_525_60) {
- tuner_dbg("pinnacle ntsc\n");
- if2 = 41300 * 1000;
- } else {
- tuner_dbg("pinnacle pal\n");
- if2 = 33300 * 1000;
- }
-
- mt2050_set_if_freq(fe, params->frequency * 125 / 2, if2);
- mt2050_set_antenna(fe, radio_antenna);
-
- return 0;
-}
-
-static int mt2050_set_params(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct microtune_priv *priv = fe->tuner_priv;
- int ret = -EINVAL;
-
- switch (params->mode) {
- case V4L2_TUNER_RADIO:
- ret = mt2050_set_radio_freq(fe, params);
- priv->frequency = params->frequency * 125 / 2;
- break;
- case V4L2_TUNER_ANALOG_TV:
- case V4L2_TUNER_DIGITAL_TV:
- ret = mt2050_set_tv_freq(fe, params);
- priv->frequency = params->frequency * 62500;
- break;
- }
-
- return ret;
-}
-
-static struct dvb_tuner_ops mt2050_tuner_ops = {
- .set_analog_params = mt2050_set_params,
- .release = microtune_release,
- .get_frequency = microtune_get_frequency,
-};
-
-static int mt2050_init(struct dvb_frontend *fe)
-{
- struct microtune_priv *priv = fe->tuner_priv;
- unsigned char buf[2];
- int ret;
-
- buf[0]=6;
- buf[1]=0x10;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2); // power
-
- buf[0]=0x0f;
- buf[1]=0x0f;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2); // m1lo
-
- buf[0]=0x0d;
- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,1);
- tuner_i2c_xfer_recv(&priv->i2c_props,buf,1);
-
- tuner_dbg("mt2050: sro is %x\n",buf[0]);
-
- memcpy(&fe->ops.tuner_ops, &mt2050_tuner_ops, sizeof(struct dvb_tuner_ops));
-
- return 0;
-}
-
-struct dvb_frontend *microtune_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
- u8 i2c_addr)
-{
- struct microtune_priv *priv = NULL;
- char *name;
- unsigned char buf[21];
- int company_code;
-
- priv = kzalloc(sizeof(struct microtune_priv), GFP_KERNEL);
- if (priv == NULL)
- return NULL;
- fe->tuner_priv = priv;
-
- priv->i2c_props.addr = i2c_addr;
- priv->i2c_props.adap = i2c_adap;
- priv->i2c_props.name = "mt20xx";
-
- //priv->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */
-
- memset(buf,0,sizeof(buf));
-
- name = "unknown";
-
- tuner_i2c_xfer_send(&priv->i2c_props,buf,1);
- tuner_i2c_xfer_recv(&priv->i2c_props,buf,21);
- if (debug) {
- int i;
- tuner_dbg("MT20xx hexdump:");
- for(i=0;i<21;i++) {
- printk(" %02x",buf[i]);
- if(((i+1)%8)==0) printk(" ");
- }
- printk("\n");
- }
- company_code = buf[0x11] << 8 | buf[0x12];
- tuner_info("microtune: companycode=%04x part=%02x rev=%02x\n",
- company_code,buf[0x13],buf[0x14]);
-
-
- if (buf[0x13] < ARRAY_SIZE(microtune_part) &&
- NULL != microtune_part[buf[0x13]])
- name = microtune_part[buf[0x13]];
- switch (buf[0x13]) {
- case MT2032:
- mt2032_init(fe);
- break;
- case MT2050:
- mt2050_init(fe);
- break;
- default:
- tuner_info("microtune %s found, not (yet?) supported, sorry :-/\n",
- name);
- return NULL;
- }
-
- strlcpy(fe->ops.tuner_ops.info.name, name,
- sizeof(fe->ops.tuner_ops.info.name));
- tuner_info("microtune %s found, OK\n",name);
- return fe;
-}
-
-EXPORT_SYMBOL_GPL(microtune_attach);
-
-MODULE_DESCRIPTION("Microtune tuner driver");
-MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
-MODULE_LICENSE("GPL");
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/mt20xx.h b/drivers/media/video/mt20xx.h
deleted file mode 100644
index aa848e14ce5..00000000000
--- a/drivers/media/video/mt20xx.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- 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 __MT20XX_H__
-#define __MT20XX_H__
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-
-#if defined(CONFIG_TUNER_MT20XX) || (defined(CONFIG_TUNER_MT20XX_MODULE) && defined(MODULE))
-extern struct dvb_frontend *microtune_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
- u8 i2c_addr);
-#else
-static inline struct dvb_frontend *microtune_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
- u8 i2c_addr)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif /* __MT20XX_H__ */
diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
index ba09826ddf4..179e47049a4 100644
--- a/drivers/media/video/mt9m001.c
+++ b/drivers/media/video/mt9m001.c
@@ -372,7 +372,7 @@ static int mt9m001_set_register(struct soc_camera_device *icd,
}
#endif
-const struct v4l2_queryctrl mt9m001_controls[] = {
+static const struct v4l2_queryctrl mt9m001_controls[] = {
{
.id = V4L2_CID_VFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c
index 7b223691ce9..d1391ac5509 100644
--- a/drivers/media/video/mt9v022.c
+++ b/drivers/media/video/mt9v022.c
@@ -452,7 +452,7 @@ static int mt9v022_set_register(struct soc_camera_device *icd,
}
#endif
-const struct v4l2_queryctrl mt9v022_controls[] = {
+static const struct v4l2_queryctrl mt9v022_controls[] = {
{
.id = V4L2_CID_VFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig
index 158b3d0c653..9620c67fae7 100644
--- a/drivers/media/video/pvrusb2/Kconfig
+++ b/drivers/media/video/pvrusb2/Kconfig
@@ -1,14 +1,15 @@
config VIDEO_PVRUSB2
tristate "Hauppauge WinTV-PVR USB2 support"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C
select FW_LOADER
- select VIDEO_TUNER
+ select MEDIA_TUNER
select VIDEO_TVEEPROM
select VIDEO_CX2341X
select VIDEO_SAA711X
select VIDEO_CX25840
select VIDEO_MSP3400
select VIDEO_WM8775
+ select VIDEO_CS53L32A
---help---
This is a video4linux driver for Conexant 23416 based
usb2 personal video recorder devices.
@@ -16,32 +17,6 @@ config VIDEO_PVRUSB2
To compile this driver as a module, choose M here: the
module will be called pvrusb2
-config VIDEO_PVRUSB2_ONAIR_CREATOR
- bool "pvrusb2 driver support for OnAir Creator model"
- depends on VIDEO_PVRUSB2 && EXPERIMENTAL
- select VIDEO_SAA711X
- select VIDEO_CS53L32A
- ---help---
-
- This option enables support for the OnAir Creator USB tuner
- device. This is a hybrid device, however currently only
- analog mode is supported.
-
- If you are in doubt, say Y.
-
-config VIDEO_PVRUSB2_ONAIR_USB2
- bool "pvrusb2 driver support for OnAir USB2 model"
- depends on VIDEO_PVRUSB2 && EXPERIMENTAL
- select VIDEO_SAA711X
- select VIDEO_CS53L32A
- ---help---
-
- This option enables support for the OnAir USB2 tuner device
- (also known as the Sasem tuner). This is a hybrid device,
- however currently only analog mode is supported.
-
- If you are in doubt, say Y.
-
config VIDEO_PVRUSB2_SYSFS
bool "pvrusb2 sysfs support (EXPERIMENTAL)"
default y
@@ -59,29 +34,23 @@ config VIDEO_PVRUSB2_SYSFS
Note: This feature is experimental and subject to change.
config VIDEO_PVRUSB2_DVB
- bool "pvrusb2 DVB support (EXPERIMENTAL)"
- default n
+ bool "pvrusb2 ATSC/DVB support (EXPERIMENTAL)"
+ default y
depends on VIDEO_PVRUSB2 && DVB_CORE && EXPERIMENTAL
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
select DVB_S5H1409 if !DVB_FE_CUSTOMISE
select DVB_S5H1411 if !DVB_FE_CUSTOMISE
select DVB_TDA10048 if !DVB_FE_CUSTOMIZE
- select DVB_TDA18271 if !DVB_FE_CUSTOMIZE
- select TUNER_SIMPLE if !DVB_FE_CUSTOMISE
- select TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
+ select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE
+ select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
---help---
- This option enables compilation of a DVB interface for the
- pvrusb2 driver. Currently this is very very experimental.
- It is also limiting - the DVB interface can only access the
- digital side of hybrid devices, and there are going to be
- issues if you attempt to mess with the V4L side at the same
- time. Don't turn this on unless you know what you are
- doing.
-
- If you are in doubt, say N.
+ This option enables a DVB interface for the pvrusb2 driver.
+ If your device does not support digital television, this
+ feature will have no affect on the driver's operation.
- Note: This feature is very experimental and might break
+ If you are in doubt, say Y.
config VIDEO_PVRUSB2_DEBUGIFC
bool "pvrusb2 debug interface"
diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile
index 5b3083c89aa..4fda2de69ab 100644
--- a/drivers/media/video/pvrusb2/Makefile
+++ b/drivers/media/video/pvrusb2/Makefile
@@ -16,5 +16,6 @@ pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o
EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debug.h b/drivers/media/video/pvrusb2/pvrusb2-debug.h
index 11537ddf8aa..707d2d9635d 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-debug.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-debug.h
@@ -54,6 +54,7 @@ extern int pvrusb2_debug;
#define PVR2_TRACE_DATA_FLOW (1 << 25) /* Track data flow */
#define PVR2_TRACE_DEBUGIFC (1 << 26) /* Debug interface actions */
#define PVR2_TRACE_GPIO (1 << 27) /* GPIO state bit changes */
+#define PVR2_TRACE_DVB_FEED (1 << 28) /* DVB transport feed debug */
#endif /* __PVRUSB2_HDW_INTERNAL_H */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
index 3a141d93e1a..5bf6d8fda1f 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
@@ -153,7 +153,6 @@ static const struct pvr2_device_desc pvr2_device_gotview_2d = {
-#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_CREATOR
/*------------------------------------------------------------------------*/
/* OnAir Creator */
@@ -212,11 +211,9 @@ static const struct pvr2_device_desc pvr2_device_onair_creator = {
.dvb_props = &pvr2_onair_creator_fe_props,
#endif
};
-#endif
-#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_USB2
/*------------------------------------------------------------------------*/
/* OnAir USB 2.0 */
@@ -274,7 +271,6 @@ static const struct pvr2_device_desc pvr2_device_onair_usb2 = {
.dvb_props = &pvr2_onair_usb2_fe_props,
#endif
};
-#endif
@@ -497,14 +493,10 @@ struct usb_device_id pvr2_device_table[] = {
.driver_info = (kernel_ulong_t)&pvr2_device_gotview_2},
{ USB_DEVICE(0x1164, 0x0602),
.driver_info = (kernel_ulong_t)&pvr2_device_gotview_2d},
-#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_CREATOR
{ USB_DEVICE(0x11ba, 0x1003),
.driver_info = (kernel_ulong_t)&pvr2_device_onair_creator},
-#endif
-#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_USB2
{ USB_DEVICE(0x11ba, 0x1001),
.driver_info = (kernel_ulong_t)&pvr2_device_onair_usb2},
-#endif
{ USB_DEVICE(0x2040, 0x7300),
.driver_info = (kernel_ulong_t)&pvr2_device_73xxx},
{ USB_DEVICE(0x2040, 0x7500),
diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/drivers/media/video/pvrusb2/pvrusb2-dvb.c
index 6504c97e0bb..6ec4bf81fc7 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-dvb.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-dvb.c
@@ -21,6 +21,7 @@
#include <linux/kthread.h>
#include <linux/freezer.h>
#include "dvbdev.h"
+#include "pvrusb2-debug.h"
#include "pvrusb2-hdw-internal.h"
#include "pvrusb2-hdw.h"
#include "pvrusb2-io.h"
@@ -35,7 +36,7 @@ static int pvr2_dvb_feed_func(struct pvr2_dvb_adapter *adap)
struct pvr2_buffer *bp;
struct pvr2_stream *stream;
- printk(KERN_DEBUG "dvb thread started\n");
+ pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread started");
set_freezable();
stream = adap->channel.stream->stream;
@@ -82,7 +83,7 @@ static int pvr2_dvb_feed_func(struct pvr2_dvb_adapter *adap)
/* If we get here and ret is < 0, then an error has occurred.
Probably would be a good idea to communicate that to DVB core... */
- printk(KERN_DEBUG "dvb thread stopped\n");
+ pvr2_trace(PVR2_TRACE_DVB_FEED, "dvb feed thread stopped");
return 0;
}
@@ -210,7 +211,8 @@ static int pvr2_dvb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
do {
if (onoff) {
if (!adap->feedcount) {
- printk(KERN_DEBUG "start feeding\n");
+ pvr2_trace(PVR2_TRACE_DVB_FEED,
+ "start feeding demux");
ret = pvr2_dvb_stream_start(adap);
if (ret < 0) break;
}
@@ -218,7 +220,8 @@ static int pvr2_dvb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
} else if (adap->feedcount > 0) {
(adap->feedcount)--;
if (!adap->feedcount) {
- printk(KERN_DEBUG "stop feeding\n");
+ pvr2_trace(PVR2_TRACE_DVB_FEED,
+ "stop feeding demux");
pvr2_dvb_stream_end(adap);
}
}
@@ -230,15 +233,13 @@ static int pvr2_dvb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
static int pvr2_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
{
- printk(KERN_DEBUG "start pid: 0x%04x, feedtype: %d\n",
- dvbdmxfeed->pid, dvbdmxfeed->type);
+ pvr2_trace(PVR2_TRACE_DVB_FEED, "start pid: 0x%04x", dvbdmxfeed->pid);
return pvr2_dvb_ctrl_feed(dvbdmxfeed, 1);
}
static int pvr2_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
{
- printk(KERN_DEBUG "stop pid: 0x%04x, feedtype: %d\n",
- dvbdmxfeed->pid, dvbdmxfeed->type);
+ pvr2_trace(PVR2_TRACE_DVB_FEED, "stop pid: 0x%04x", dvbdmxfeed->pid);
return pvr2_dvb_ctrl_feed(dvbdmxfeed, 0);
}
@@ -259,7 +260,8 @@ static int pvr2_dvb_adapter_init(struct pvr2_dvb_adapter *adap)
&adap->channel.hdw->usb_dev->dev,
adapter_nr);
if (ret < 0) {
- err("dvb_register_adapter failed: error %d", ret);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "dvb_register_adapter failed: error %d", ret);
goto err;
}
adap->dvb_adap.priv = adap;
@@ -276,7 +278,8 @@ static int pvr2_dvb_adapter_init(struct pvr2_dvb_adapter *adap)
ret = dvb_dmx_init(&adap->demux);
if (ret < 0) {
- err("dvb_dmx_init failed: error %d", ret);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "dvb_dmx_init failed: error %d", ret);
goto err_dmx;
}
@@ -286,7 +289,8 @@ static int pvr2_dvb_adapter_init(struct pvr2_dvb_adapter *adap)
ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap);
if (ret < 0) {
- err("dvb_dmxdev_init failed: error %d", ret);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "dvb_dmxdev_init failed: error %d", ret);
goto err_dmx_dev;
}
@@ -304,7 +308,7 @@ err:
static int pvr2_dvb_adapter_exit(struct pvr2_dvb_adapter *adap)
{
- printk(KERN_DEBUG "unregistering DVB devices\n");
+ pvr2_trace(PVR2_TRACE_INFO, "unregistering DVB devices");
dvb_net_release(&adap->dvb_net);
adap->demux.dmx.close(&adap->demux.dmx);
dvb_dmxdev_release(&adap->dmxdev);
@@ -320,7 +324,7 @@ static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap)
int ret = 0;
if (dvb_props == NULL) {
- err("fe_props not defined!");
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS, "fe_props not defined!");
return -EINVAL;
}
@@ -328,13 +332,15 @@ static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap)
&adap->channel,
(1 << PVR2_CVAL_INPUT_DTV));
if (ret) {
- err("failed to grab control of dtv input (code=%d)",
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "failed to grab control of dtv input (code=%d)",
ret);
return ret;
}
if (dvb_props->frontend_attach == NULL) {
- err("frontend_attach not defined!");
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "frontend_attach not defined!");
ret = -EINVAL;
goto done;
}
@@ -342,7 +348,8 @@ static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap)
if ((dvb_props->frontend_attach(adap) == 0) && (adap->fe)) {
if (dvb_register_frontend(&adap->dvb_adap, adap->fe)) {
- err("frontend registration failed!");
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "frontend registration failed!");
dvb_frontend_detach(adap->fe);
adap->fe = NULL;
ret = -ENODEV;
@@ -359,7 +366,8 @@ static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap)
adap->fe->ops.ts_bus_ctrl = pvr2_dvb_bus_ctrl;
} else {
- err("no frontend was attached!");
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "no frontend was attached!");
ret = -ENODEV;
return ret;
}
diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig
index e086f14d566..40e4c3bd2cb 100644
--- a/drivers/media/video/saa7134/Kconfig
+++ b/drivers/media/video/saa7134/Kconfig
@@ -3,7 +3,7 @@ config VIDEO_SAA7134
depends on VIDEO_DEV && PCI && I2C && INPUT
select VIDEOBUF_DMA_SG
select VIDEO_IR
- select VIDEO_TUNER
+ select MEDIA_TUNER
select VIDEO_TVEEPROM
select CRC32
---help---
@@ -35,9 +35,9 @@ config VIDEO_SAA7134_DVB
select DVB_NXT200X if !DVB_FE_CUSTOMISE
select DVB_TDA10086 if !DVB_FE_CUSTOMISE
select DVB_TDA826X if !DVB_FE_CUSTOMISE
- select DVB_TDA827X if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_TDA827X if !DVB_FE_CUSTOMISE
select DVB_ISL6421 if !DVB_FE_CUSTOMISE
- select TUNER_SIMPLE if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
---help---
This adds support for DVB cards based on the
Philips saa7134 chip.
diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile
index 9aff937ba7a..3dbaa19a6d0 100644
--- a/drivers/media/video/saa7134/Makefile
+++ b/drivers/media/video/saa7134/Makefile
@@ -11,5 +11,6 @@ obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o
obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o
EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index 98375955a84..b111903aa32 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -47,6 +47,9 @@ static char name_svideo[] = "S-Video";
/* ------------------------------------------------------------------ */
/* board config info */
+/* If radio_type !=UNSET, radio_addr should be specified
+ */
+
struct saa7134_board saa7134_boards[] = {
[SAA7134_BOARD_UNKNOWN] = {
.name = "UNKNOWN/GENERIC",
@@ -3087,7 +3090,7 @@ struct saa7134_board saa7134_boards[] = {
.tuner_type = TUNER_PHILIPS_TD1316, /* untested */
.radio_type = TUNER_TEA5767, /* untested */
.tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
+ .radio_addr = 0x60,
.tda9887_conf = TDA9887_PRESENT,
.mpeg = SAA7134_MPEG_DVB,
.inputs = {{
@@ -4247,6 +4250,36 @@ struct saa7134_board saa7134_boards[] = {
.amux = LINE1,
} },
},
+ [SAA7134_BOARD_BEHOLD_H6] = {
+ /* Igor Kuznetsov <igk@igk.ru> */
+ .name = "Beholder BeholdTV H6",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ /* no DVB support for now */
+ /* .mpeg = SAA7134_MPEG_DVB, */
+ },
};
const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
@@ -5197,6 +5230,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.subvendor = 0x5ace,
.subdevice = 0x6193,
.driver_data = SAA7134_BOARD_BEHOLD_M6,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6191,
+ .driver_data = SAA7134_BOARD_BEHOLD_M6,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
@@ -5246,6 +5285,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.subdevice = 0xc900,
.driver_data = SAA7134_BOARD_VIDEOMATE_T750,
}, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5ace,
+ .subdevice = 0x6290,
+ .driver_data = SAA7134_BOARD_BEHOLD_H6,
+ }, {
/* --- boards without eeprom + subsystem ID --- */
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7134,
@@ -5577,20 +5622,87 @@ int saa7134_board_init1(struct saa7134_dev *dev)
return 0;
}
+static void saa7134_tuner_setup(struct saa7134_dev *dev)
+{
+ struct tuner_setup tun_setup;
+ unsigned int mode_mask = T_RADIO |
+ T_ANALOG_TV |
+ T_DIGITAL_TV;
+
+ memset(&tun_setup, 0, sizeof(tun_setup));
+ tun_setup.tuner_callback = saa7134_tuner_callback;
+
+ if (saa7134_boards[dev->board].radio_type != UNSET) {
+ tun_setup.type = saa7134_boards[dev->board].radio_type;
+ tun_setup.addr = saa7134_boards[dev->board].radio_addr;
+
+ tun_setup.mode_mask = T_RADIO;
+
+ saa7134_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup);
+ mode_mask &= ~T_RADIO;
+ }
+
+ if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type != UNSET)) {
+ tun_setup.type = dev->tuner_type;
+ tun_setup.addr = dev->tuner_addr;
+ tun_setup.config = saa7134_boards[dev->board].tuner_config;
+ tun_setup.tuner_callback = saa7134_tuner_callback;
+
+ tun_setup.mode_mask = mode_mask;
+
+ saa7134_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup);
+ }
+
+ if (dev->tda9887_conf) {
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &dev->tda9887_conf;
+
+ saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG,
+ &tda9887_cfg);
+ }
+
+ if (dev->tuner_type == TUNER_XC2028) {
+ struct v4l2_priv_tun_config xc2028_cfg;
+ struct xc2028_ctrl ctl;
+
+ memset(&xc2028_cfg, 0, sizeof(ctl));
+ memset(&ctl, 0, sizeof(ctl));
+
+ ctl.fname = XC2028_DEFAULT_FIRMWARE;
+ ctl.max_len = 64;
+
+ switch (dev->board) {
+ case SAA7134_BOARD_AVERMEDIA_A16D:
+ ctl.demod = XC3028_FE_ZARLINK456;
+ break;
+ default:
+ ctl.demod = XC3028_FE_OREN538;
+ ctl.mts = 1;
+ }
+
+ xc2028_cfg.tuner = TUNER_XC2028;
+ xc2028_cfg.priv = &ctl;
+
+ saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &xc2028_cfg);
+ }
+}
+
/* stuff which needs working i2c */
int saa7134_board_init2(struct saa7134_dev *dev)
{
unsigned char buf;
int board;
- struct tuner_setup tun_setup;
- tun_setup.config = 0;
- tun_setup.tuner_callback = saa7134_tuner_callback;
+
+ dev->tuner_type = saa7134_boards[dev->board].tuner_type;
+ dev->tuner_addr = saa7134_boards[dev->board].tuner_addr;
switch (dev->board) {
case SAA7134_BOARD_BMK_MPEX_NOTUNER:
case SAA7134_BOARD_BMK_MPEX_TUNER:
dev->i2c_client.addr = 0x60;
- board = (i2c_master_recv(&dev->i2c_client,&buf,0) < 0)
+ board = (i2c_master_recv(&dev->i2c_client, &buf, 0) < 0)
? SAA7134_BOARD_BMK_MPEX_NOTUNER
: SAA7134_BOARD_BMK_MPEX_TUNER;
if (board == dev->board)
@@ -5600,21 +5712,9 @@ int saa7134_board_init2(struct saa7134_dev *dev)
saa7134_boards[dev->board].name);
dev->tuner_type = saa7134_boards[dev->board].tuner_type;
- if (TUNER_ABSENT != dev->tuner_type) {
- tun_setup.mode_mask = T_RADIO |
- T_ANALOG_TV |
- T_DIGITAL_TV;
- tun_setup.type = dev->tuner_type;
- tun_setup.addr = ADDR_UNSET;
- tun_setup.tuner_callback = saa7134_tuner_callback;
-
- saa7134_i2c_call_clients(dev,
- TUNER_SET_TYPE_ADDR,
- &tun_setup);
- }
break;
case SAA7134_BOARD_MD7134:
- {
+ {
u8 subaddr;
u8 data[3];
int ret, tuner_t;
@@ -5667,30 +5767,8 @@ int saa7134_board_init2(struct saa7134_dev *dev)
}
printk(KERN_INFO "%s Tuner type is %d\n", dev->name, dev->tuner_type);
- if (dev->tuner_type == TUNER_PHILIPS_FMD1216ME_MK3) {
- struct v4l2_priv_tun_config tda9887_cfg;
-
- tda9887_cfg.tuner = TUNER_TDA9887;
- tda9887_cfg.priv = &dev->tda9887_conf;
-
- dev->tda9887_conf = TDA9887_PRESENT |
- TDA9887_PORT1_ACTIVE |
- TDA9887_PORT2_ACTIVE;
-
- saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG,
- &tda9887_cfg);
- }
-
- tun_setup.mode_mask = T_RADIO |
- T_ANALOG_TV |
- T_DIGITAL_TV;
- tun_setup.type = dev->tuner_type;
- tun_setup.addr = ADDR_UNSET;
-
- saa7134_i2c_call_clients(dev,
- TUNER_SET_TYPE_ADDR, &tun_setup);
- }
break;
+ }
case SAA7134_BOARD_PHILIPS_EUROPA:
if (dev->autodetected && (dev->eedata[0x41] == 0x1c)) {
/* Reconfigure board as Snake reference design */
@@ -5702,43 +5780,43 @@ int saa7134_board_init2(struct saa7134_dev *dev)
}
case SAA7134_BOARD_VIDEOMATE_DVBT_300:
case SAA7134_BOARD_ASUS_EUROPA2_HYBRID:
+ {
+
/* The Philips EUROPA based hybrid boards have the tuner connected through
* the channel decoder. We have to make it transparent to find it
*/
- {
u8 data[] = { 0x07, 0x02};
struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
i2c_transfer(&dev->i2c_adap, &msg, 1);
- tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV;
- tun_setup.type = dev->tuner_type;
- tun_setup.addr = dev->tuner_addr;
-
- saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR,&tun_setup);
- }
break;
+ }
case SAA7134_BOARD_PHILIPS_TIGER:
case SAA7134_BOARD_PHILIPS_TIGER_S:
- {
+ {
u8 data[] = { 0x3c, 0x33, 0x60};
struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
- if(dev->autodetected && (dev->eedata[0x49] == 0x50)) {
+ if (dev->autodetected && (dev->eedata[0x49] == 0x50)) {
dev->board = SAA7134_BOARD_PHILIPS_TIGER_S;
printk(KERN_INFO "%s: Reconfigured board as %s\n",
dev->name, saa7134_boards[dev->board].name);
}
- if(dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) {
- tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV;
- tun_setup.type = TUNER_PHILIPS_TDA8290;
- tun_setup.addr = 0x4b;
- tun_setup.config = 2;
+ if (dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) {
+ dev->tuner_type = TUNER_PHILIPS_TDA8290;
+
+ saa7134_tuner_setup(dev);
- saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR,&tun_setup);
data[2] = 0x68;
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+
+ /* Tuner setup is handled before I2C transfer.
+ Due to that, there's no need to do it later
+ */
+ return 0;
}
i2c_transfer(&dev->i2c_adap, &msg, 1);
- }
break;
+ }
case SAA7134_BOARD_HAUPPAUGE_HVR1110:
hauppauge_eeprom(dev, dev->eedata+0x80);
/* break intentionally omitted */
@@ -5751,52 +5829,55 @@ int saa7134_board_init2(struct saa7134_dev *dev)
case SAA7134_BOARD_AVERMEDIA_SUPER_007:
case SAA7134_BOARD_TWINHAN_DTV_DVB_3056:
case SAA7134_BOARD_CREATIX_CTX953:
+ {
/* this is a hybrid board, initialize to analog mode
* and configure firmware eeprom address
*/
- {
u8 data[] = { 0x3c, 0x33, 0x60};
struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
i2c_transfer(&dev->i2c_adap, &msg, 1);
- }
break;
+ }
case SAA7134_BOARD_FLYDVB_TRIO:
- {
+ {
u8 data[] = { 0x3c, 0x33, 0x62};
struct i2c_msg msg = {.addr=0x09, .flags=0, .buf=data, .len = sizeof(data)};
i2c_transfer(&dev->i2c_adap, &msg, 1);
- }
break;
+ }
case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
+ {
/* initialize analog mode */
- {
u8 data[] = { 0x3c, 0x33, 0x6a};
struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
i2c_transfer(&dev->i2c_adap, &msg, 1);
- }
break;
+ }
case SAA7134_BOARD_CINERGY_HT_PCMCIA:
case SAA7134_BOARD_CINERGY_HT_PCI:
+ {
/* initialize analog mode */
- {
u8 data[] = { 0x3c, 0x33, 0x68};
struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
i2c_transfer(&dev->i2c_adap, &msg, 1);
- }
break;
+ }
case SAA7134_BOARD_KWORLD_ATSC110:
- {
- /* enable tuner */
- int i;
- static const u8 buffer [] = { 0x10,0x12,0x13,0x04,0x16,0x00,0x14,0x04,0x017,0x00 };
- dev->i2c_client.addr = 0x0a;
- for (i = 0; i < 5; i++)
- if (2 != i2c_master_send(&dev->i2c_client,&buffer[i*2],2))
- printk(KERN_WARNING "%s: Unable to enable tuner(%i).\n",
- dev->name, i);
- }
+ {
+ /* enable tuner */
+ int i;
+ static const u8 buffer [] = { 0x10, 0x12, 0x13, 0x04, 0x16,
+ 0x00, 0x14, 0x04, 0x17, 0x00 };
+ dev->i2c_client.addr = 0x0a;
+ for (i = 0; i < 5; i++)
+ if (2 != i2c_master_send(&dev->i2c_client,
+ &buffer[i*2], 2))
+ printk(KERN_WARNING
+ "%s: Unable to enable tuner(%i).\n",
+ dev->name, i);
break;
+ }
case SAA7134_BOARD_VIDEOMATE_DVBT_200:
case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
/* The T200 and the T200A share the same pci id. Consequently,
@@ -5821,7 +5902,7 @@ int saa7134_board_init2(struct saa7134_dev *dev)
}
break;
case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM:
- {
+ {
struct v4l2_priv_tun_config tea5767_cfg;
struct tea5767_ctrl ctl;
@@ -5832,34 +5913,11 @@ int saa7134_board_init2(struct saa7134_dev *dev)
tea5767_cfg.tuner = TUNER_TEA5767;
tea5767_cfg.priv = &ctl;
saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &tea5767_cfg);
- }
break;
}
+ } /* switch() */
- if (dev->tuner_type == TUNER_XC2028) {
- struct v4l2_priv_tun_config xc2028_cfg;
- struct xc2028_ctrl ctl;
-
- memset(&xc2028_cfg, 0, sizeof(ctl));
- memset(&ctl, 0, sizeof(ctl));
-
- ctl.fname = XC2028_DEFAULT_FIRMWARE;
- ctl.max_len = 64;
-
- switch (dev->board) {
- case SAA7134_BOARD_AVERMEDIA_A16D:
- ctl.demod = XC3028_FE_ZARLINK456;
- break;
- default:
- ctl.demod = XC3028_FE_OREN538;
- ctl.mts = 1;
- }
-
- xc2028_cfg.tuner = TUNER_XC2028;
- xc2028_cfg.priv = &ctl;
-
- saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &xc2028_cfg);
- }
+ saa7134_tuner_setup(dev);
return 0;
}
diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c
index 2ccfaba0c49..d8af3863f2d 100644
--- a/drivers/media/video/saa7134/saa7134-i2c.c
+++ b/drivers/media/video/saa7134/saa7134-i2c.c
@@ -324,8 +324,6 @@ static u32 functionality(struct i2c_adapter *adap)
static int attach_inform(struct i2c_client *client)
{
struct saa7134_dev *dev = client->adapter->algo_data;
- int tuner = dev->tuner_type;
- struct tuner_setup tun_setup;
d1printk( "%s i2c attach [addr=0x%x,client=%s]\n",
client->driver->driver.name, client->addr, client->name);
@@ -346,46 +344,6 @@ static int attach_inform(struct i2c_client *client)
}
}
- if (!client->driver->command)
- return 0;
-
- if (saa7134_boards[dev->board].radio_type != UNSET) {
-
- tun_setup.type = saa7134_boards[dev->board].radio_type;
- tun_setup.addr = saa7134_boards[dev->board].radio_addr;
-
- if ((tun_setup.addr == ADDR_UNSET) || (tun_setup.addr == client->addr)) {
- tun_setup.mode_mask = T_RADIO;
-
- client->driver->command(client, TUNER_SET_TYPE_ADDR, &tun_setup);
- }
- }
-
- if (tuner != UNSET) {
- tun_setup.type = tuner;
- tun_setup.addr = saa7134_boards[dev->board].tuner_addr;
- tun_setup.config = saa7134_boards[dev->board].tuner_config;
- tun_setup.tuner_callback = saa7134_tuner_callback;
-
- if ((tun_setup.addr == ADDR_UNSET)||(tun_setup.addr == client->addr)) {
-
- tun_setup.mode_mask = T_ANALOG_TV;
-
- client->driver->command(client,TUNER_SET_TYPE_ADDR, &tun_setup);
- }
-
- if (tuner == TUNER_TDA9887) {
- struct v4l2_priv_tun_config tda9887_cfg;
-
- tda9887_cfg.tuner = TUNER_TDA9887;
- tda9887_cfg.priv = &dev->tda9887_conf;
-
- client->driver->command(client, TUNER_SET_CONFIG,
- &tda9887_cfg);
- }
- }
-
-
return 0;
}
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index 767ff30832f..919632b10aa 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -531,6 +531,7 @@ void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir)
break;
case SAA7134_BOARD_BEHOLD_607_9FM:
case SAA7134_BOARD_BEHOLD_M6:
+ case SAA7134_BOARD_BEHOLD_H6:
snprintf(ir->c.name, sizeof(ir->c.name), "BeholdTV");
ir->get_key = get_key_beholdm6xx;
ir->ir_codes = ir_codes_behold;
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index 924ffd13637..34ff0d4998f 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -263,6 +263,7 @@ struct saa7134_format {
#define SAA7134_BOARD_VIDEOMATE_T750 139
#define SAA7134_BOARD_AVERMEDIA_A700_PRO 140
#define SAA7134_BOARD_AVERMEDIA_A700_HYBRID 141
+#define SAA7134_BOARD_BEHOLD_H6 142
#define SAA7134_MAXBOARDS 8
diff --git a/drivers/media/video/tda8290.c b/drivers/media/video/tda8290.c
deleted file mode 100644
index 0ebb5b525e5..00000000000
--- a/drivers/media/video/tda8290.c
+++ /dev/null
@@ -1,804 +0,0 @@
-/*
-
- i2c tv tuner chip device driver
- controls the philips tda8290+75 tuner chip combo.
-
- 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.
-
- This "tda8290" module was split apart from the original "tuner" module.
-*/
-
-#include <linux/i2c.h>
-#include <linux/delay.h>
-#include <linux/videodev.h>
-#include "tuner-i2c.h"
-#include "tda8290.h"
-#include "tda827x.h"
-#include "tda18271.h"
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "enable verbose debug messages");
-
-/* ---------------------------------------------------------------------- */
-
-struct tda8290_priv {
- struct tuner_i2c_props i2c_props;
-
- unsigned char tda8290_easy_mode;
-
- unsigned char tda827x_addr;
-
- unsigned char ver;
-#define TDA8290 1
-#define TDA8295 2
-#define TDA8275 4
-#define TDA8275A 8
-#define TDA18271 16
-
- struct tda827x_config cfg;
-};
-
-/*---------------------------------------------------------------------*/
-
-static int tda8290_i2c_bridge(struct dvb_frontend *fe, int close)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
-
- unsigned char enable[2] = { 0x21, 0xC0 };
- unsigned char disable[2] = { 0x21, 0x00 };
- unsigned char *msg;
-
- if (close) {
- msg = enable;
- tuner_i2c_xfer_send(&priv->i2c_props, msg, 2);
- /* let the bridge stabilize */
- msleep(20);
- } else {
- msg = disable;
- tuner_i2c_xfer_send(&priv->i2c_props, msg, 2);
- }
-
- return 0;
-}
-
-static int tda8295_i2c_bridge(struct dvb_frontend *fe, int close)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
-
- unsigned char enable[2] = { 0x45, 0xc1 };
- unsigned char disable[2] = { 0x46, 0x00 };
- unsigned char buf[3] = { 0x45, 0x01, 0x00 };
- unsigned char *msg;
-
- if (close) {
- msg = enable;
- tuner_i2c_xfer_send(&priv->i2c_props, msg, 2);
- /* let the bridge stabilize */
- msleep(20);
- } else {
- msg = disable;
- tuner_i2c_xfer_send(&priv->i2c_props, msg, 1);
- tuner_i2c_xfer_recv(&priv->i2c_props, &msg[1], 1);
-
- buf[2] = msg[1];
- buf[2] &= ~0x04;
- tuner_i2c_xfer_send(&priv->i2c_props, buf, 3);
- msleep(5);
-
- msg[1] |= 0x04;
- tuner_i2c_xfer_send(&priv->i2c_props, msg, 2);
- }
-
- return 0;
-}
-
-/*---------------------------------------------------------------------*/
-
-static void set_audio(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
- char* mode;
-
- if (params->std & V4L2_STD_MN) {
- priv->tda8290_easy_mode = 0x01;
- mode = "MN";
- } else if (params->std & V4L2_STD_B) {
- priv->tda8290_easy_mode = 0x02;
- mode = "B";
- } else if (params->std & V4L2_STD_GH) {
- priv->tda8290_easy_mode = 0x04;
- mode = "GH";
- } else if (params->std & V4L2_STD_PAL_I) {
- priv->tda8290_easy_mode = 0x08;
- mode = "I";
- } else if (params->std & V4L2_STD_DK) {
- priv->tda8290_easy_mode = 0x10;
- mode = "DK";
- } else if (params->std & V4L2_STD_SECAM_L) {
- priv->tda8290_easy_mode = 0x20;
- mode = "L";
- } else if (params->std & V4L2_STD_SECAM_LC) {
- priv->tda8290_easy_mode = 0x40;
- mode = "LC";
- } else {
- priv->tda8290_easy_mode = 0x10;
- mode = "xx";
- }
-
- tuner_dbg("setting tda829x to system %s\n", mode);
-}
-
-static void tda8290_set_params(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
-
- unsigned char soft_reset[] = { 0x00, 0x00 };
- unsigned char easy_mode[] = { 0x01, priv->tda8290_easy_mode };
- unsigned char expert_mode[] = { 0x01, 0x80 };
- unsigned char agc_out_on[] = { 0x02, 0x00 };
- unsigned char gainset_off[] = { 0x28, 0x14 };
- unsigned char if_agc_spd[] = { 0x0f, 0x88 };
- unsigned char adc_head_6[] = { 0x05, 0x04 };
- unsigned char adc_head_9[] = { 0x05, 0x02 };
- unsigned char adc_head_12[] = { 0x05, 0x01 };
- unsigned char pll_bw_nom[] = { 0x0d, 0x47 };
- unsigned char pll_bw_low[] = { 0x0d, 0x27 };
- unsigned char gainset_2[] = { 0x28, 0x64 };
- unsigned char agc_rst_on[] = { 0x0e, 0x0b };
- unsigned char agc_rst_off[] = { 0x0e, 0x09 };
- unsigned char if_agc_set[] = { 0x0f, 0x81 };
- unsigned char addr_adc_sat = 0x1a;
- unsigned char addr_agc_stat = 0x1d;
- unsigned char addr_pll_stat = 0x1b;
- unsigned char adc_sat, agc_stat,
- pll_stat;
- int i;
-
- set_audio(fe, params);
-
- if (priv->cfg.config)
- tuner_dbg("tda827xa config is 0x%02x\n", priv->cfg.config);
- tuner_i2c_xfer_send(&priv->i2c_props, easy_mode, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, agc_out_on, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, soft_reset, 2);
- msleep(1);
-
- expert_mode[1] = priv->tda8290_easy_mode + 0x80;
- tuner_i2c_xfer_send(&priv->i2c_props, expert_mode, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, gainset_off, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, if_agc_spd, 2);
- if (priv->tda8290_easy_mode & 0x60)
- tuner_i2c_xfer_send(&priv->i2c_props, adc_head_9, 2);
- else
- tuner_i2c_xfer_send(&priv->i2c_props, adc_head_6, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_nom, 2);
-
- tda8290_i2c_bridge(fe, 1);
-
- if (fe->ops.tuner_ops.set_analog_params)
- fe->ops.tuner_ops.set_analog_params(fe, params);
-
- for (i = 0; i < 3; i++) {
- tuner_i2c_xfer_send(&priv->i2c_props, &addr_pll_stat, 1);
- tuner_i2c_xfer_recv(&priv->i2c_props, &pll_stat, 1);
- if (pll_stat & 0x80) {
- tuner_i2c_xfer_send(&priv->i2c_props, &addr_adc_sat, 1);
- tuner_i2c_xfer_recv(&priv->i2c_props, &adc_sat, 1);
- tuner_i2c_xfer_send(&priv->i2c_props, &addr_agc_stat, 1);
- tuner_i2c_xfer_recv(&priv->i2c_props, &agc_stat, 1);
- tuner_dbg("tda8290 is locked, AGC: %d\n", agc_stat);
- break;
- } else {
- tuner_dbg("tda8290 not locked, no signal?\n");
- msleep(100);
- }
- }
- /* adjust headroom resp. gain */
- if ((agc_stat > 115) || (!(pll_stat & 0x80) && (adc_sat < 20))) {
- tuner_dbg("adjust gain, step 1. Agc: %d, ADC stat: %d, lock: %d\n",
- agc_stat, adc_sat, pll_stat & 0x80);
- tuner_i2c_xfer_send(&priv->i2c_props, gainset_2, 2);
- msleep(100);
- tuner_i2c_xfer_send(&priv->i2c_props, &addr_agc_stat, 1);
- tuner_i2c_xfer_recv(&priv->i2c_props, &agc_stat, 1);
- tuner_i2c_xfer_send(&priv->i2c_props, &addr_pll_stat, 1);
- tuner_i2c_xfer_recv(&priv->i2c_props, &pll_stat, 1);
- if ((agc_stat > 115) || !(pll_stat & 0x80)) {
- tuner_dbg("adjust gain, step 2. Agc: %d, lock: %d\n",
- agc_stat, pll_stat & 0x80);
- if (priv->cfg.agcf)
- priv->cfg.agcf(fe);
- msleep(100);
- tuner_i2c_xfer_send(&priv->i2c_props, &addr_agc_stat, 1);
- tuner_i2c_xfer_recv(&priv->i2c_props, &agc_stat, 1);
- tuner_i2c_xfer_send(&priv->i2c_props, &addr_pll_stat, 1);
- tuner_i2c_xfer_recv(&priv->i2c_props, &pll_stat, 1);
- if((agc_stat > 115) || !(pll_stat & 0x80)) {
- tuner_dbg("adjust gain, step 3. Agc: %d\n", agc_stat);
- tuner_i2c_xfer_send(&priv->i2c_props, adc_head_12, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_low, 2);
- msleep(100);
- }
- }
- }
-
- /* l/ l' deadlock? */
- if(priv->tda8290_easy_mode & 0x60) {
- tuner_i2c_xfer_send(&priv->i2c_props, &addr_adc_sat, 1);
- tuner_i2c_xfer_recv(&priv->i2c_props, &adc_sat, 1);
- tuner_i2c_xfer_send(&priv->i2c_props, &addr_pll_stat, 1);
- tuner_i2c_xfer_recv(&priv->i2c_props, &pll_stat, 1);
- if ((adc_sat > 20) || !(pll_stat & 0x80)) {
- tuner_dbg("trying to resolve SECAM L deadlock\n");
- tuner_i2c_xfer_send(&priv->i2c_props, agc_rst_on, 2);
- msleep(40);
- tuner_i2c_xfer_send(&priv->i2c_props, agc_rst_off, 2);
- }
- }
-
- tda8290_i2c_bridge(fe, 0);
- tuner_i2c_xfer_send(&priv->i2c_props, if_agc_set, 2);
-}
-
-/*---------------------------------------------------------------------*/
-
-static void tda8295_power(struct dvb_frontend *fe, int enable)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
- unsigned char buf[] = { 0x30, 0x00 }; /* clb_stdbt */
-
- tuner_i2c_xfer_send(&priv->i2c_props, &buf[0], 1);
- tuner_i2c_xfer_recv(&priv->i2c_props, &buf[1], 1);
-
- if (enable)
- buf[1] = 0x01;
- else
- buf[1] = 0x03;
-
- tuner_i2c_xfer_send(&priv->i2c_props, buf, 2);
-}
-
-static void tda8295_set_easy_mode(struct dvb_frontend *fe, int enable)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
- unsigned char buf[] = { 0x01, 0x00 };
-
- tuner_i2c_xfer_send(&priv->i2c_props, &buf[0], 1);
- tuner_i2c_xfer_recv(&priv->i2c_props, &buf[1], 1);
-
- if (enable)
- buf[1] = 0x01; /* rising edge sets regs 0x02 - 0x23 */
- else
- buf[1] = 0x00; /* reset active bit */
-
- tuner_i2c_xfer_send(&priv->i2c_props, buf, 2);
-}
-
-static void tda8295_set_video_std(struct dvb_frontend *fe)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
- unsigned char buf[] = { 0x00, priv->tda8290_easy_mode };
-
- tuner_i2c_xfer_send(&priv->i2c_props, buf, 2);
-
- tda8295_set_easy_mode(fe, 1);
- msleep(20);
- tda8295_set_easy_mode(fe, 0);
-}
-
-/*---------------------------------------------------------------------*/
-
-static void tda8295_agc1_out(struct dvb_frontend *fe, int enable)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
- unsigned char buf[] = { 0x02, 0x00 }; /* DIV_FUNC */
-
- tuner_i2c_xfer_send(&priv->i2c_props, &buf[0], 1);
- tuner_i2c_xfer_recv(&priv->i2c_props, &buf[1], 1);
-
- if (enable)
- buf[1] &= ~0x40;
- else
- buf[1] |= 0x40;
-
- tuner_i2c_xfer_send(&priv->i2c_props, buf, 2);
-}
-
-static void tda8295_agc2_out(struct dvb_frontend *fe, int enable)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
- unsigned char set_gpio_cf[] = { 0x44, 0x00 };
- unsigned char set_gpio_val[] = { 0x46, 0x00 };
-
- tuner_i2c_xfer_send(&priv->i2c_props, &set_gpio_cf[0], 1);
- tuner_i2c_xfer_recv(&priv->i2c_props, &set_gpio_cf[1], 1);
- tuner_i2c_xfer_send(&priv->i2c_props, &set_gpio_val[0], 1);
- tuner_i2c_xfer_recv(&priv->i2c_props, &set_gpio_val[1], 1);
-
- set_gpio_cf[1] &= 0xf0; /* clear GPIO_0 bits 3-0 */
-
- if (enable) {
- set_gpio_cf[1] |= 0x01; /* config GPIO_0 as Open Drain Out */
- set_gpio_val[1] &= 0xfe; /* set GPIO_0 pin low */
- }
- tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_cf, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_val, 2);
-}
-
-static int tda8295_has_signal(struct dvb_frontend *fe)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
-
- unsigned char hvpll_stat = 0x26;
- unsigned char ret;
-
- tuner_i2c_xfer_send(&priv->i2c_props, &hvpll_stat, 1);
- tuner_i2c_xfer_recv(&priv->i2c_props, &ret, 1);
- return (ret & 0x01) ? 65535 : 0;
-}
-
-/*---------------------------------------------------------------------*/
-
-static void tda8295_set_params(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
-
- unsigned char blanking_mode[] = { 0x1d, 0x00 };
-
- set_audio(fe, params);
-
- tuner_dbg("%s: freq = %d\n", __func__, params->frequency);
-
- tda8295_power(fe, 1);
- tda8295_agc1_out(fe, 1);
-
- tuner_i2c_xfer_send(&priv->i2c_props, &blanking_mode[0], 1);
- tuner_i2c_xfer_recv(&priv->i2c_props, &blanking_mode[1], 1);
-
- tda8295_set_video_std(fe);
-
- blanking_mode[1] = 0x03;
- tuner_i2c_xfer_send(&priv->i2c_props, blanking_mode, 2);
- msleep(20);
-
- tda8295_i2c_bridge(fe, 1);
-
- if (fe->ops.tuner_ops.set_analog_params)
- fe->ops.tuner_ops.set_analog_params(fe, params);
-
- if (priv->cfg.agcf)
- priv->cfg.agcf(fe);
-
- if (tda8295_has_signal(fe))
- tuner_dbg("tda8295 is locked\n");
- else
- tuner_dbg("tda8295 not locked, no signal?\n");
-
- tda8295_i2c_bridge(fe, 0);
-}
-
-/*---------------------------------------------------------------------*/
-
-static int tda8290_has_signal(struct dvb_frontend *fe)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
-
- unsigned char i2c_get_afc[1] = { 0x1B };
- unsigned char afc = 0;
-
- tuner_i2c_xfer_send(&priv->i2c_props, i2c_get_afc, ARRAY_SIZE(i2c_get_afc));
- tuner_i2c_xfer_recv(&priv->i2c_props, &afc, 1);
- return (afc & 0x80)? 65535:0;
-}
-
-/*---------------------------------------------------------------------*/
-
-static void tda8290_standby(struct dvb_frontend *fe)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
-
- unsigned char cb1[] = { 0x30, 0xD0 };
- unsigned char tda8290_standby[] = { 0x00, 0x02 };
- unsigned char tda8290_agc_tri[] = { 0x02, 0x20 };
- struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0, .buf=cb1, .len = 2};
-
- tda8290_i2c_bridge(fe, 1);
- if (priv->ver & TDA8275A)
- cb1[1] = 0x90;
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
- tda8290_i2c_bridge(fe, 0);
- tuner_i2c_xfer_send(&priv->i2c_props, tda8290_agc_tri, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, tda8290_standby, 2);
-}
-
-static void tda8295_standby(struct dvb_frontend *fe)
-{
- tda8295_agc1_out(fe, 0); /* Put AGC in tri-state */
-
- tda8295_power(fe, 0);
-}
-
-static void tda8290_init_if(struct dvb_frontend *fe)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
-
- unsigned char set_VS[] = { 0x30, 0x6F };
- unsigned char set_GP00_CF[] = { 0x20, 0x01 };
- unsigned char set_GP01_CF[] = { 0x20, 0x0B };
-
- if ((priv->cfg.config == 1) || (priv->cfg.config == 2))
- tuner_i2c_xfer_send(&priv->i2c_props, set_GP00_CF, 2);
- else
- tuner_i2c_xfer_send(&priv->i2c_props, set_GP01_CF, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, set_VS, 2);
-}
-
-static void tda8295_init_if(struct dvb_frontend *fe)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
-
- static unsigned char set_adc_ctl[] = { 0x33, 0x14 };
- static unsigned char set_adc_ctl2[] = { 0x34, 0x00 };
- static unsigned char set_pll_reg6[] = { 0x3e, 0x63 };
- static unsigned char set_pll_reg0[] = { 0x38, 0x23 };
- static unsigned char set_pll_reg7[] = { 0x3f, 0x01 };
- static unsigned char set_pll_reg10[] = { 0x42, 0x61 };
- static unsigned char set_gpio_reg0[] = { 0x44, 0x0b };
-
- tda8295_power(fe, 1);
-
- tda8295_set_easy_mode(fe, 0);
- tda8295_set_video_std(fe);
-
- tuner_i2c_xfer_send(&priv->i2c_props, set_adc_ctl, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, set_adc_ctl2, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg6, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg0, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg7, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg10, 2);
- tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_reg0, 2);
-
- tda8295_agc1_out(fe, 0);
- tda8295_agc2_out(fe, 0);
-}
-
-static void tda8290_init_tuner(struct dvb_frontend *fe)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
- unsigned char tda8275_init[] = { 0x00, 0x00, 0x00, 0x40, 0xdC, 0x04, 0xAf,
- 0x3F, 0x2A, 0x04, 0xFF, 0x00, 0x00, 0x40 };
- unsigned char tda8275a_init[] = { 0x00, 0x00, 0x00, 0x00, 0xdC, 0x05, 0x8b,
- 0x0c, 0x04, 0x20, 0xFF, 0x00, 0x00, 0x4b };
- struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0,
- .buf=tda8275_init, .len = 14};
- if (priv->ver & TDA8275A)
- msg.buf = tda8275a_init;
-
- tda8290_i2c_bridge(fe, 1);
- i2c_transfer(priv->i2c_props.adap, &msg, 1);
- tda8290_i2c_bridge(fe, 0);
-}
-
-/*---------------------------------------------------------------------*/
-
-static void tda829x_release(struct dvb_frontend *fe)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
-
- /* only try to release the tuner if we've
- * attached it from within this module */
- if (priv->ver & (TDA18271 | TDA8275 | TDA8275A))
- if (fe->ops.tuner_ops.release)
- fe->ops.tuner_ops.release(fe);
-
- kfree(fe->analog_demod_priv);
- fe->analog_demod_priv = NULL;
-}
-
-static struct tda18271_config tda829x_tda18271_config = {
- .gate = TDA18271_GATE_ANALOG,
-};
-
-static int tda829x_find_tuner(struct dvb_frontend *fe)
-{
- struct tda8290_priv *priv = fe->analog_demod_priv;
- struct analog_demod_ops *analog_ops = &fe->ops.analog_ops;
- int i, ret, tuners_found;
- u32 tuner_addrs;
- u8 data;
- struct i2c_msg msg = { .flags = I2C_M_RD, .buf = &data, .len = 1 };
-
- if (NULL == analog_ops->i2c_gate_ctrl)
- return -EINVAL;
-
- analog_ops->i2c_gate_ctrl(fe, 1);
-
- /* probe for tuner chip */
- tuners_found = 0;
- tuner_addrs = 0;
- for (i = 0x60; i <= 0x63; i++) {
- msg.addr = i;
- ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
- if (ret == 1) {
- tuners_found++;
- tuner_addrs = (tuner_addrs << 8) + i;
- }
- }
- /* if there is more than one tuner, we expect the right one is
- behind the bridge and we choose the highest address that doesn't
- give a response now
- */
-
- analog_ops->i2c_gate_ctrl(fe, 0);
-
- if (tuners_found > 1)
- for (i = 0; i < tuners_found; i++) {
- msg.addr = tuner_addrs & 0xff;
- ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
- if (ret == 1)
- tuner_addrs = tuner_addrs >> 8;
- else
- break;
- }
-
- if (tuner_addrs == 0) {
- tuner_addrs = 0x60;
- tuner_info("could not clearly identify tuner address, "
- "defaulting to %x\n", tuner_addrs);
- } else {
- tuner_addrs = tuner_addrs & 0xff;
- tuner_info("setting tuner address to %x\n", tuner_addrs);
- }
- priv->tda827x_addr = tuner_addrs;
- msg.addr = tuner_addrs;
-
- analog_ops->i2c_gate_ctrl(fe, 1);
- ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
-
- if (ret != 1) {
- tuner_warn("tuner access failed!\n");
- return -EREMOTEIO;
- }
-
- if ((data == 0x83) || (data == 0x84)) {
- priv->ver |= TDA18271;
- tda18271_attach(fe, priv->tda827x_addr,
- priv->i2c_props.adap,
- &tda829x_tda18271_config);
- } else {
- if ((data & 0x3c) == 0)
- priv->ver |= TDA8275;
- else
- priv->ver |= TDA8275A;
-
- tda827x_attach(fe, priv->tda827x_addr, priv->i2c_props.adap, &priv->cfg);
- priv->cfg.switch_addr = priv->i2c_props.addr;
- }
- if (fe->ops.tuner_ops.init)
- fe->ops.tuner_ops.init(fe);
-
- if (fe->ops.tuner_ops.sleep)
- fe->ops.tuner_ops.sleep(fe);
-
- analog_ops->i2c_gate_ctrl(fe, 0);
-
- return 0;
-}
-
-static int tda8290_probe(struct tuner_i2c_props *i2c_props)
-{
-#define TDA8290_ID 0x89
- unsigned char tda8290_id[] = { 0x1f, 0x00 };
-
- /* detect tda8290 */
- tuner_i2c_xfer_send(i2c_props, &tda8290_id[0], 1);
- tuner_i2c_xfer_recv(i2c_props, &tda8290_id[1], 1);
-
- if (tda8290_id[1] == TDA8290_ID) {
- if (debug)
- printk(KERN_DEBUG "%s: tda8290 detected @ %d-%04x\n",
- __func__, i2c_adapter_id(i2c_props->adap),
- i2c_props->addr);
- return 0;
- }
-
- return -ENODEV;
-}
-
-static int tda8295_probe(struct tuner_i2c_props *i2c_props)
-{
-#define TDA8295_ID 0x8a
- unsigned char tda8295_id[] = { 0x2f, 0x00 };
-
- /* detect tda8295 */
- tuner_i2c_xfer_send(i2c_props, &tda8295_id[0], 1);
- tuner_i2c_xfer_recv(i2c_props, &tda8295_id[1], 1);
-
- if (tda8295_id[1] == TDA8295_ID) {
- if (debug)
- printk(KERN_DEBUG "%s: tda8295 detected @ %d-%04x\n",
- __func__, i2c_adapter_id(i2c_props->adap),
- i2c_props->addr);
- return 0;
- }
-
- return -ENODEV;
-}
-
-static struct analog_demod_ops tda8290_ops = {
- .set_params = tda8290_set_params,
- .has_signal = tda8290_has_signal,
- .standby = tda8290_standby,
- .release = tda829x_release,
- .i2c_gate_ctrl = tda8290_i2c_bridge,
-};
-
-static struct analog_demod_ops tda8295_ops = {
- .set_params = tda8295_set_params,
- .has_signal = tda8295_has_signal,
- .standby = tda8295_standby,
- .release = tda829x_release,
- .i2c_gate_ctrl = tda8295_i2c_bridge,
-};
-
-struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c_adap, u8 i2c_addr,
- struct tda829x_config *cfg)
-{
- struct tda8290_priv *priv = NULL;
- char *name;
-
- priv = kzalloc(sizeof(struct tda8290_priv), GFP_KERNEL);
- if (priv == NULL)
- return NULL;
- fe->analog_demod_priv = priv;
-
- priv->i2c_props.addr = i2c_addr;
- priv->i2c_props.adap = i2c_adap;
- priv->i2c_props.name = "tda829x";
- if (cfg) {
- priv->cfg.config = cfg->lna_cfg;
- priv->cfg.tuner_callback = cfg->tuner_callback;
- }
-
- if (tda8290_probe(&priv->i2c_props) == 0) {
- priv->ver = TDA8290;
- memcpy(&fe->ops.analog_ops, &tda8290_ops,
- sizeof(struct analog_demod_ops));
- }
-
- if (tda8295_probe(&priv->i2c_props) == 0) {
- priv->ver = TDA8295;
- memcpy(&fe->ops.analog_ops, &tda8295_ops,
- sizeof(struct analog_demod_ops));
- }
-
- if ((!(cfg) || (TDA829X_PROBE_TUNER == cfg->probe_tuner)) &&
- (tda829x_find_tuner(fe) < 0))
- goto fail;
-
- switch (priv->ver) {
- case TDA8290:
- name = "tda8290";
- break;
- case TDA8295:
- name = "tda8295";
- break;
- case TDA8290 | TDA8275:
- name = "tda8290+75";
- break;
- case TDA8295 | TDA8275:
- name = "tda8295+75";
- break;
- case TDA8290 | TDA8275A:
- name = "tda8290+75a";
- break;
- case TDA8295 | TDA8275A:
- name = "tda8295+75a";
- break;
- case TDA8290 | TDA18271:
- name = "tda8290+18271";
- break;
- case TDA8295 | TDA18271:
- name = "tda8295+18271";
- break;
- default:
- goto fail;
- }
- tuner_info("type set to %s\n", name);
-
- fe->ops.analog_ops.info.name = name;
-
- if (priv->ver & TDA8290) {
- tda8290_init_tuner(fe);
- tda8290_init_if(fe);
- } else if (priv->ver & TDA8295)
- tda8295_init_if(fe);
-
- return fe;
-
-fail:
- tda829x_release(fe);
- return NULL;
-}
-EXPORT_SYMBOL_GPL(tda829x_attach);
-
-int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr)
-{
- struct tuner_i2c_props i2c_props = {
- .adap = i2c_adap,
- .addr = i2c_addr,
- };
-
- unsigned char soft_reset[] = { 0x00, 0x00 };
- unsigned char easy_mode_b[] = { 0x01, 0x02 };
- unsigned char easy_mode_g[] = { 0x01, 0x04 };
- unsigned char restore_9886[] = { 0x00, 0xd6, 0x30 };
- unsigned char addr_dto_lsb = 0x07;
- unsigned char data;
-#define PROBE_BUFFER_SIZE 8
- unsigned char buf[PROBE_BUFFER_SIZE];
- int i;
-
- /* rule out tda9887, which would return the same byte repeatedly */
- tuner_i2c_xfer_send(&i2c_props, soft_reset, 1);
- tuner_i2c_xfer_recv(&i2c_props, buf, PROBE_BUFFER_SIZE);
- for (i = 1; i < PROBE_BUFFER_SIZE; i++) {
- if (buf[i] != buf[0])
- break;
- }
-
- /* all bytes are equal, not a tda829x - probably a tda9887 */
- if (i == PROBE_BUFFER_SIZE)
- return -ENODEV;
-
- if ((tda8290_probe(&i2c_props) == 0) ||
- (tda8295_probe(&i2c_props) == 0))
- return 0;
-
- /* fall back to old probing method */
- tuner_i2c_xfer_send(&i2c_props, easy_mode_b, 2);
- tuner_i2c_xfer_send(&i2c_props, soft_reset, 2);
- tuner_i2c_xfer_send(&i2c_props, &addr_dto_lsb, 1);
- tuner_i2c_xfer_recv(&i2c_props, &data, 1);
- if (data == 0) {
- tuner_i2c_xfer_send(&i2c_props, easy_mode_g, 2);
- tuner_i2c_xfer_send(&i2c_props, soft_reset, 2);
- tuner_i2c_xfer_send(&i2c_props, &addr_dto_lsb, 1);
- tuner_i2c_xfer_recv(&i2c_props, &data, 1);
- if (data == 0x7b) {
- return 0;
- }
- }
- tuner_i2c_xfer_send(&i2c_props, restore_9886, 3);
- return -ENODEV;
-}
-EXPORT_SYMBOL_GPL(tda829x_probe);
-
-MODULE_DESCRIPTION("Philips/NXP TDA8290/TDA8295 analog IF demodulator driver");
-MODULE_AUTHOR("Gerd Knorr, Hartmut Hackmann, Michael Krufky");
-MODULE_LICENSE("GPL");
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/tda8290.h b/drivers/media/video/tda8290.h
deleted file mode 100644
index d3bbf276a46..00000000000
--- a/drivers/media/video/tda8290.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- 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 __TDA8290_H__
-#define __TDA8290_H__
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-
-struct tda829x_config {
- unsigned int lna_cfg;
- int (*tuner_callback) (void *dev, int command, int arg);
-
- unsigned int probe_tuner:1;
-#define TDA829X_PROBE_TUNER 0
-#define TDA829X_DONT_PROBE 1
-};
-
-#if defined(CONFIG_TUNER_TDA8290) || (defined(CONFIG_TUNER_TDA8290_MODULE) && defined(MODULE))
-extern int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr);
-
-extern struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c_adap,
- u8 i2c_addr,
- struct tda829x_config *cfg);
-#else
-static inline int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return -EINVAL;
-}
-
-static inline struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c_adap,
- u8 i2c_addr,
- struct tda829x_config *cfg)
-{
- printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
- __func__);
- return NULL;
-}
-#endif
-
-#endif /* __TDA8290_H__ */
diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c
deleted file mode 100644
index a0545ba957b..00000000000
--- a/drivers/media/video/tda9887.c
+++ /dev/null
@@ -1,717 +0,0 @@
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/i2c.h>
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/videodev.h>
-#include <media/v4l2-common.h>
-#include <media/tuner.h>
-#include "tuner-i2c.h"
-#include "tda9887.h"
-
-
-/* Chips:
- TDA9885 (PAL, NTSC)
- TDA9886 (PAL, SECAM, NTSC)
- TDA9887 (PAL, SECAM, NTSC, FM Radio)
-
- Used as part of several tuners
-*/
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "enable verbose debug messages");
-
-static DEFINE_MUTEX(tda9887_list_mutex);
-static LIST_HEAD(hybrid_tuner_instance_list);
-
-struct tda9887_priv {
- struct tuner_i2c_props i2c_props;
- struct list_head hybrid_tuner_instance_list;
-
- unsigned char data[4];
- unsigned int config;
- unsigned int mode;
- unsigned int audmode;
- v4l2_std_id std;
-};
-
-/* ---------------------------------------------------------------------- */
-
-#define UNSET (-1U)
-
-struct tvnorm {
- v4l2_std_id std;
- char *name;
- unsigned char b;
- unsigned char c;
- unsigned char e;
-};
-
-/* ---------------------------------------------------------------------- */
-
-//
-// TDA defines
-//
-
-//// first reg (b)
-#define cVideoTrapBypassOFF 0x00 // bit b0
-#define cVideoTrapBypassON 0x01 // bit b0
-
-#define cAutoMuteFmInactive 0x00 // bit b1
-#define cAutoMuteFmActive 0x02 // bit b1
-
-#define cIntercarrier 0x00 // bit b2
-#define cQSS 0x04 // bit b2
-
-#define cPositiveAmTV 0x00 // bit b3:4
-#define cFmRadio 0x08 // bit b3:4
-#define cNegativeFmTV 0x10 // bit b3:4
-
-
-#define cForcedMuteAudioON 0x20 // bit b5
-#define cForcedMuteAudioOFF 0x00 // bit b5
-
-#define cOutputPort1Active 0x00 // bit b6
-#define cOutputPort1Inactive 0x40 // bit b6
-
-#define cOutputPort2Active 0x00 // bit b7
-#define cOutputPort2Inactive 0x80 // bit b7
-
-
-//// second reg (c)
-#define cDeemphasisOFF 0x00 // bit c5
-#define cDeemphasisON 0x20 // bit c5
-
-#define cDeemphasis75 0x00 // bit c6
-#define cDeemphasis50 0x40 // bit c6
-
-#define cAudioGain0 0x00 // bit c7
-#define cAudioGain6 0x80 // bit c7
-
-#define cTopMask 0x1f // bit c0:4
-#define cTopDefault 0x10 // bit c0:4
-
-//// third reg (e)
-#define cAudioIF_4_5 0x00 // bit e0:1
-#define cAudioIF_5_5 0x01 // bit e0:1
-#define cAudioIF_6_0 0x02 // bit e0:1
-#define cAudioIF_6_5 0x03 // bit e0:1
-
-
-#define cVideoIFMask 0x1c // bit e2:4
-/* Video IF selection in TV Mode (bit B3=0) */
-#define cVideoIF_58_75 0x00 // bit e2:4
-#define cVideoIF_45_75 0x04 // bit e2:4
-#define cVideoIF_38_90 0x08 // bit e2:4
-#define cVideoIF_38_00 0x0C // bit e2:4
-#define cVideoIF_33_90 0x10 // bit e2:4
-#define cVideoIF_33_40 0x14 // bit e2:4
-#define cRadioIF_45_75 0x18 // bit e2:4
-#define cRadioIF_38_90 0x1C // bit e2:4
-
-/* IF1 selection in Radio Mode (bit B3=1) */
-#define cRadioIF_33_30 0x00 // bit e2,4 (also 0x10,0x14)
-#define cRadioIF_41_30 0x04 // bit e2,4
-
-/* Output of AFC pin in radio mode when bit E7=1 */
-#define cRadioAGC_SIF 0x00 // bit e3
-#define cRadioAGC_FM 0x08 // bit e3
-
-#define cTunerGainNormal 0x00 // bit e5
-#define cTunerGainLow 0x20 // bit e5
-
-#define cGating_18 0x00 // bit e6
-#define cGating_36 0x40 // bit e6
-
-#define cAgcOutON 0x80 // bit e7
-#define cAgcOutOFF 0x00 // bit e7
-
-/* ---------------------------------------------------------------------- */
-
-static struct tvnorm tvnorms[] = {
- {
- .std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N,
- .name = "PAL-BGHN",
- .b = ( cNegativeFmTV |
- cQSS ),
- .c = ( cDeemphasisON |
- cDeemphasis50 |
- cTopDefault),
- .e = ( cGating_36 |
- cAudioIF_5_5 |
- cVideoIF_38_90 ),
- },{
- .std = V4L2_STD_PAL_I,
- .name = "PAL-I",
- .b = ( cNegativeFmTV |
- cQSS ),
- .c = ( cDeemphasisON |
- cDeemphasis50 |
- cTopDefault),
- .e = ( cGating_36 |
- cAudioIF_6_0 |
- cVideoIF_38_90 ),
- },{
- .std = V4L2_STD_PAL_DK,
- .name = "PAL-DK",
- .b = ( cNegativeFmTV |
- cQSS ),
- .c = ( cDeemphasisON |
- cDeemphasis50 |
- cTopDefault),
- .e = ( cGating_36 |
- cAudioIF_6_5 |
- cVideoIF_38_90 ),
- },{
- .std = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc,
- .name = "PAL-M/Nc",
- .b = ( cNegativeFmTV |
- cQSS ),
- .c = ( cDeemphasisON |
- cDeemphasis75 |
- cTopDefault),
- .e = ( cGating_36 |
- cAudioIF_4_5 |
- cVideoIF_45_75 ),
- },{
- .std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H,
- .name = "SECAM-BGH",
- .b = ( cPositiveAmTV |
- cQSS ),
- .c = ( cTopDefault),
- .e = ( cGating_36 |
- cAudioIF_5_5 |
- cVideoIF_38_90 ),
- },{
- .std = V4L2_STD_SECAM_L,
- .name = "SECAM-L",
- .b = ( cPositiveAmTV |
- cQSS ),
- .c = ( cTopDefault),
- .e = ( cGating_36 |
- cAudioIF_6_5 |
- cVideoIF_38_90 ),
- },{
- .std = V4L2_STD_SECAM_LC,
- .name = "SECAM-L'",
- .b = ( cOutputPort2Inactive |
- cPositiveAmTV |
- cQSS ),
- .c = ( cTopDefault),
- .e = ( cGating_36 |
- cAudioIF_6_5 |
- cVideoIF_33_90 ),
- },{
- .std = V4L2_STD_SECAM_DK,
- .name = "SECAM-DK",
- .b = ( cNegativeFmTV |
- cQSS ),
- .c = ( cDeemphasisON |
- cDeemphasis50 |
- cTopDefault),
- .e = ( cGating_36 |
- cAudioIF_6_5 |
- cVideoIF_38_90 ),
- },{
- .std = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
- .name = "NTSC-M",
- .b = ( cNegativeFmTV |
- cQSS ),
- .c = ( cDeemphasisON |
- cDeemphasis75 |
- cTopDefault),
- .e = ( cGating_36 |
- cAudioIF_4_5 |
- cVideoIF_45_75 ),
- },{
- .std = V4L2_STD_NTSC_M_JP,
- .name = "NTSC-M-JP",
- .b = ( cNegativeFmTV |
- cQSS ),
- .c = ( cDeemphasisON |
- cDeemphasis50 |
- cTopDefault),
- .e = ( cGating_36 |
- cAudioIF_4_5 |
- cVideoIF_58_75 ),
- }
-};
-
-static struct tvnorm radio_stereo = {
- .name = "Radio Stereo",
- .b = ( cFmRadio |
- cQSS ),
- .c = ( cDeemphasisOFF |
- cAudioGain6 |
- cTopDefault),
- .e = ( cTunerGainLow |
- cAudioIF_5_5 |
- cRadioIF_38_90 ),
-};
-
-static struct tvnorm radio_mono = {
- .name = "Radio Mono",
- .b = ( cFmRadio |
- cQSS ),
- .c = ( cDeemphasisON |
- cDeemphasis75 |
- cTopDefault),
- .e = ( cTunerGainLow |
- cAudioIF_5_5 |
- cRadioIF_38_90 ),
-};
-
-/* ---------------------------------------------------------------------- */
-
-static void dump_read_message(struct dvb_frontend *fe, unsigned char *buf)
-{
- struct tda9887_priv *priv = fe->analog_demod_priv;
-
- static char *afc[16] = {
- "- 12.5 kHz",
- "- 37.5 kHz",
- "- 62.5 kHz",
- "- 87.5 kHz",
- "-112.5 kHz",
- "-137.5 kHz",
- "-162.5 kHz",
- "-187.5 kHz [min]",
- "+187.5 kHz [max]",
- "+162.5 kHz",
- "+137.5 kHz",
- "+112.5 kHz",
- "+ 87.5 kHz",
- "+ 62.5 kHz",
- "+ 37.5 kHz",
- "+ 12.5 kHz",
- };
- tuner_info("read: 0x%2x\n", buf[0]);
- tuner_info(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no");
- tuner_info(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]);
- tuner_info(" fmif level : %s\n", (buf[0] & 0x20) ? "high" : "low");
- tuner_info(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out");
- tuner_info(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low");
-}
-
-static void dump_write_message(struct dvb_frontend *fe, unsigned char *buf)
-{
- struct tda9887_priv *priv = fe->analog_demod_priv;
-
- static char *sound[4] = {
- "AM/TV",
- "FM/radio",
- "FM/TV",
- "FM/radio"
- };
- static char *adjust[32] = {
- "-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9",
- "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1",
- "0", "+1", "+2", "+3", "+4", "+5", "+6", "+7",
- "+8", "+9", "+10", "+11", "+12", "+13", "+14", "+15"
- };
- static char *deemph[4] = {
- "no", "no", "75", "50"
- };
- static char *carrier[4] = {
- "4.5 MHz",
- "5.5 MHz",
- "6.0 MHz",
- "6.5 MHz / AM"
- };
- static char *vif[8] = {
- "58.75 MHz",
- "45.75 MHz",
- "38.9 MHz",
- "38.0 MHz",
- "33.9 MHz",
- "33.4 MHz",
- "45.75 MHz + pin13",
- "38.9 MHz + pin13",
- };
- static char *rif[4] = {
- "44 MHz",
- "52 MHz",
- "52 MHz",
- "44 MHz",
- };
-
- tuner_info("write: byte B 0x%02x\n", buf[1]);
- tuner_info(" B0 video mode : %s\n",
- (buf[1] & 0x01) ? "video trap" : "sound trap");
- tuner_info(" B1 auto mute fm : %s\n",
- (buf[1] & 0x02) ? "yes" : "no");
- tuner_info(" B2 carrier mode : %s\n",
- (buf[1] & 0x04) ? "QSS" : "Intercarrier");
- tuner_info(" B3-4 tv sound/radio : %s\n",
- sound[(buf[1] & 0x18) >> 3]);
- tuner_info(" B5 force mute audio: %s\n",
- (buf[1] & 0x20) ? "yes" : "no");
- tuner_info(" B6 output port 1 : %s\n",
- (buf[1] & 0x40) ? "high (inactive)" : "low (active)");
- tuner_info(" B7 output port 2 : %s\n",
- (buf[1] & 0x80) ? "high (inactive)" : "low (active)");
-
- tuner_info("write: byte C 0x%02x\n", buf[2]);
- tuner_info(" C0-4 top adjustment : %s dB\n",
- adjust[buf[2] & 0x1f]);
- tuner_info(" C5-6 de-emphasis : %s\n",
- deemph[(buf[2] & 0x60) >> 5]);
- tuner_info(" C7 audio gain : %s\n",
- (buf[2] & 0x80) ? "-6" : "0");
-
- tuner_info("write: byte E 0x%02x\n", buf[3]);
- tuner_info(" E0-1 sound carrier : %s\n",
- carrier[(buf[3] & 0x03)]);
- tuner_info(" E6 l pll gating : %s\n",
- (buf[3] & 0x40) ? "36" : "13");
-
- if (buf[1] & 0x08) {
- /* radio */
- tuner_info(" E2-4 video if : %s\n",
- rif[(buf[3] & 0x0c) >> 2]);
- tuner_info(" E7 vif agc output : %s\n",
- (buf[3] & 0x80)
- ? ((buf[3] & 0x10) ? "fm-agc radio" :
- "sif-agc radio")
- : "fm radio carrier afc");
- } else {
- /* video */
- tuner_info(" E2-4 video if : %s\n",
- vif[(buf[3] & 0x1c) >> 2]);
- tuner_info(" E5 tuner gain : %s\n",
- (buf[3] & 0x80)
- ? ((buf[3] & 0x20) ? "external" : "normal")
- : ((buf[3] & 0x20) ? "minimum" : "normal"));
- tuner_info(" E7 vif agc output : %s\n",
- (buf[3] & 0x80) ? ((buf[3] & 0x20)
- ? "pin3 port, pin22 vif agc out"
- : "pin22 port, pin3 vif acg ext in")
- : "pin3+pin22 port");
- }
- tuner_info("--\n");
-}
-
-/* ---------------------------------------------------------------------- */
-
-static int tda9887_set_tvnorm(struct dvb_frontend *fe)
-{
- struct tda9887_priv *priv = fe->analog_demod_priv;
- struct tvnorm *norm = NULL;
- char *buf = priv->data;
- int i;
-
- if (priv->mode == V4L2_TUNER_RADIO) {
- if (priv->audmode == V4L2_TUNER_MODE_MONO)
- norm = &radio_mono;
- else
- norm = &radio_stereo;
- } else {
- for (i = 0; i < ARRAY_SIZE(tvnorms); i++) {
- if (tvnorms[i].std & priv->std) {
- norm = tvnorms+i;
- break;
- }
- }
- }
- if (NULL == norm) {
- tuner_dbg("Unsupported tvnorm entry - audio muted\n");
- return -1;
- }
-
- tuner_dbg("configure for: %s\n", norm->name);
- buf[1] = norm->b;
- buf[2] = norm->c;
- buf[3] = norm->e;
- return 0;
-}
-
-static unsigned int port1 = UNSET;
-static unsigned int port2 = UNSET;
-static unsigned int qss = UNSET;
-static unsigned int adjust = UNSET;
-
-module_param(port1, int, 0644);
-module_param(port2, int, 0644);
-module_param(qss, int, 0644);
-module_param(adjust, int, 0644);
-
-static int tda9887_set_insmod(struct dvb_frontend *fe)
-{
- struct tda9887_priv *priv = fe->analog_demod_priv;
- char *buf = priv->data;
-
- if (UNSET != port1) {
- if (port1)
- buf[1] |= cOutputPort1Inactive;
- else
- buf[1] &= ~cOutputPort1Inactive;
- }
- if (UNSET != port2) {
- if (port2)
- buf[1] |= cOutputPort2Inactive;
- else
- buf[1] &= ~cOutputPort2Inactive;
- }
-
- if (UNSET != qss) {
- if (qss)
- buf[1] |= cQSS;
- else
- buf[1] &= ~cQSS;
- }
-
- if (adjust >= 0x00 && adjust < 0x20) {
- buf[2] &= ~cTopMask;
- buf[2] |= adjust;
- }
- return 0;
-}
-
-static int tda9887_do_config(struct dvb_frontend *fe)
-{
- struct tda9887_priv *priv = fe->analog_demod_priv;
- char *buf = priv->data;
-
- if (priv->config & TDA9887_PORT1_ACTIVE)
- buf[1] &= ~cOutputPort1Inactive;
- if (priv->config & TDA9887_PORT1_INACTIVE)
- buf[1] |= cOutputPort1Inactive;
- if (priv->config & TDA9887_PORT2_ACTIVE)
- buf[1] &= ~cOutputPort2Inactive;
- if (priv->config & TDA9887_PORT2_INACTIVE)
- buf[1] |= cOutputPort2Inactive;
-
- if (priv->config & TDA9887_QSS)
- buf[1] |= cQSS;
- if (priv->config & TDA9887_INTERCARRIER)
- buf[1] &= ~cQSS;
-
- if (priv->config & TDA9887_AUTOMUTE)
- buf[1] |= cAutoMuteFmActive;
- if (priv->config & TDA9887_DEEMPHASIS_MASK) {
- buf[2] &= ~0x60;
- switch (priv->config & TDA9887_DEEMPHASIS_MASK) {
- case TDA9887_DEEMPHASIS_NONE:
- buf[2] |= cDeemphasisOFF;
- break;
- case TDA9887_DEEMPHASIS_50:
- buf[2] |= cDeemphasisON | cDeemphasis50;
- break;
- case TDA9887_DEEMPHASIS_75:
- buf[2] |= cDeemphasisON | cDeemphasis75;
- break;
- }
- }
- if (priv->config & TDA9887_TOP_SET) {
- buf[2] &= ~cTopMask;
- buf[2] |= (priv->config >> 8) & cTopMask;
- }
- if ((priv->config & TDA9887_INTERCARRIER_NTSC) &&
- (priv->std & V4L2_STD_NTSC))
- buf[1] &= ~cQSS;
- if (priv->config & TDA9887_GATING_18)
- buf[3] &= ~cGating_36;
-
- if (priv->mode == V4L2_TUNER_RADIO) {
- if (priv->config & TDA9887_RIF_41_3) {
- buf[3] &= ~cVideoIFMask;
- buf[3] |= cRadioIF_41_30;
- }
- if (priv->config & TDA9887_GAIN_NORMAL)
- buf[3] &= ~cTunerGainLow;
- }
-
- return 0;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static int tda9887_status(struct dvb_frontend *fe)
-{
- struct tda9887_priv *priv = fe->analog_demod_priv;
- unsigned char buf[1];
- int rc;
-
- memset(buf,0,sizeof(buf));
- if (1 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props,buf,1)))
- tuner_info("i2c i/o error: rc == %d (should be 1)\n", rc);
- dump_read_message(fe, buf);
- return 0;
-}
-
-static void tda9887_configure(struct dvb_frontend *fe)
-{
- struct tda9887_priv *priv = fe->analog_demod_priv;
- int rc;
-
- memset(priv->data,0,sizeof(priv->data));
- tda9887_set_tvnorm(fe);
-
- /* A note on the port settings:
- These settings tend to depend on the specifics of the board.
- By default they are set to inactive (bit value 1) by this driver,
- overwriting any changes made by the tvnorm. This means that it
- is the responsibility of the module using the tda9887 to set
- these values in case of changes in the tvnorm.
- In many cases port 2 should be made active (0) when selecting
- SECAM-L, and port 2 should remain inactive (1) for SECAM-L'.
-
- For the other standards the tda9887 application note says that
- the ports should be set to active (0), but, again, that may
- differ depending on the precise hardware configuration.
- */
- priv->data[1] |= cOutputPort1Inactive;
- priv->data[1] |= cOutputPort2Inactive;
-
- tda9887_do_config(fe);
- tda9887_set_insmod(fe);
-
- if (priv->mode == T_STANDBY)
- priv->data[1] |= cForcedMuteAudioON;
-
- tuner_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n",
- priv->data[1], priv->data[2], priv->data[3]);
- if (debug > 1)
- dump_write_message(fe, priv->data);
-
- if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,priv->data,4)))
- tuner_info("i2c i/o error: rc == %d (should be 4)\n", rc);
-
- if (debug > 2) {
- msleep_interruptible(1000);
- tda9887_status(fe);
- }
-}
-
-/* ---------------------------------------------------------------------- */
-
-static void tda9887_tuner_status(struct dvb_frontend *fe)
-{
- struct tda9887_priv *priv = fe->analog_demod_priv;
- tuner_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n",
- priv->data[1], priv->data[2], priv->data[3]);
-}
-
-static int tda9887_get_afc(struct dvb_frontend *fe)
-{
- struct tda9887_priv *priv = fe->analog_demod_priv;
- static int AFC_BITS_2_kHz[] = {
- -12500, -37500, -62500, -97500,
- -112500, -137500, -162500, -187500,
- 187500, 162500, 137500, 112500,
- 97500 , 62500, 37500 , 12500
- };
- int afc=0;
- __u8 reg = 0;
-
- if (1 == tuner_i2c_xfer_recv(&priv->i2c_props,&reg,1))
- afc = AFC_BITS_2_kHz[(reg>>1)&0x0f];
-
- return afc;
-}
-
-static void tda9887_standby(struct dvb_frontend *fe)
-{
- struct tda9887_priv *priv = fe->analog_demod_priv;
-
- priv->mode = T_STANDBY;
-
- tda9887_configure(fe);
-}
-
-static void tda9887_set_params(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct tda9887_priv *priv = fe->analog_demod_priv;
-
- priv->mode = params->mode;
- priv->audmode = params->audmode;
- priv->std = params->std;
- tda9887_configure(fe);
-}
-
-static int tda9887_set_config(struct dvb_frontend *fe, void *priv_cfg)
-{
- struct tda9887_priv *priv = fe->analog_demod_priv;
-
- priv->config = *(unsigned int *)priv_cfg;
- tda9887_configure(fe);
-
- return 0;
-}
-
-static void tda9887_release(struct dvb_frontend *fe)
-{
- struct tda9887_priv *priv = fe->analog_demod_priv;
-
- mutex_lock(&tda9887_list_mutex);
-
- if (priv)
- hybrid_tuner_release_state(priv);
-
- mutex_unlock(&tda9887_list_mutex);
-
- fe->analog_demod_priv = NULL;
-}
-
-static struct analog_demod_ops tda9887_ops = {
- .info = {
- .name = "tda9887",
- },
- .set_params = tda9887_set_params,
- .standby = tda9887_standby,
- .tuner_status = tda9887_tuner_status,
- .get_afc = tda9887_get_afc,
- .release = tda9887_release,
- .set_config = tda9887_set_config,
-};
-
-struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c_adap,
- u8 i2c_addr)
-{
- struct tda9887_priv *priv = NULL;
- int instance;
-
- mutex_lock(&tda9887_list_mutex);
-
- instance = hybrid_tuner_request_state(struct tda9887_priv, priv,
- hybrid_tuner_instance_list,
- i2c_adap, i2c_addr, "tda9887");
- switch (instance) {
- case 0:
- mutex_unlock(&tda9887_list_mutex);
- return NULL;
- break;
- case 1:
- fe->analog_demod_priv = priv;
- priv->mode = T_STANDBY;
- tuner_info("tda988[5/6/7] found\n");
- break;
- default:
- fe->analog_demod_priv = priv;
- break;
- }
-
- mutex_unlock(&tda9887_list_mutex);
-
- memcpy(&fe->ops.analog_ops, &tda9887_ops,
- sizeof(struct analog_demod_ops));
-
- return fe;
-}
-EXPORT_SYMBOL_GPL(tda9887_attach);
-
-MODULE_LICENSE("GPL");
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/tda9887.h b/drivers/media/video/tda9887.h
deleted file mode 100644
index be49dcbfc70..00000000000
--- a/drivers/media/video/tda9887.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- 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 __TDA9887_H__
-#define __TDA9887_H__
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-
-/* ------------------------------------------------------------------------ */
-#if defined(CONFIG_TUNER_TDA9887) || (defined(CONFIG_TUNER_TDA9887_MODULE) && defined(MODULE))
-extern struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c_adap,
- u8 i2c_addr);
-#else
-static inline struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c_adap,
- u8 i2c_addr)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif /* __TDA9887_H__ */
diff --git a/drivers/media/video/tea5761.c b/drivers/media/video/tea5761.c
deleted file mode 100644
index b93cdef9ac7..00000000000
--- a/drivers/media/video/tea5761.c
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * For Philips TEA5761 FM Chip
- * I2C address is allways 0x20 (0x10 at 7-bit mode).
- *
- * Copyright (c) 2005-2007 Mauro Carvalho Chehab (mchehab@infradead.org)
- * This code is placed under the terms of the GNUv2 General Public License
- *
- */
-
-#include <linux/i2c.h>
-#include <linux/delay.h>
-#include <linux/videodev.h>
-#include <media/tuner.h>
-#include "tuner-i2c.h"
-#include "tea5761.h"
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "enable verbose debug messages");
-
-struct tea5761_priv {
- struct tuner_i2c_props i2c_props;
-
- u32 frequency;
-};
-
-/*****************************************************************************/
-
-/***************************
- * TEA5761HN I2C registers *
- ***************************/
-
-/* INTREG - Read: bytes 0 and 1 / Write: byte 0 */
-
- /* first byte for reading */
-#define TEA5761_INTREG_IFFLAG 0x10
-#define TEA5761_INTREG_LEVFLAG 0x8
-#define TEA5761_INTREG_FRRFLAG 0x2
-#define TEA5761_INTREG_BLFLAG 0x1
-
- /* second byte for reading / byte for writing */
-#define TEA5761_INTREG_IFMSK 0x10
-#define TEA5761_INTREG_LEVMSK 0x8
-#define TEA5761_INTREG_FRMSK 0x2
-#define TEA5761_INTREG_BLMSK 0x1
-
-/* FRQSET - Read: bytes 2 and 3 / Write: byte 1 and 2 */
-
- /* First byte */
-#define TEA5761_FRQSET_SEARCH_UP 0x80 /* 1=Station search from botton to up */
-#define TEA5761_FRQSET_SEARCH_MODE 0x40 /* 1=Search mode */
-
- /* Bits 0-5 for divider MSB */
-
- /* Second byte */
- /* Bits 0-7 for divider LSB */
-
-/* TNCTRL - Read: bytes 4 and 5 / Write: Bytes 3 and 4 */
-
- /* first byte */
-
-#define TEA5761_TNCTRL_PUPD_0 0x40 /* Power UP/Power Down MSB */
-#define TEA5761_TNCTRL_BLIM 0X20 /* 1= Japan Frequencies, 0= European frequencies */
-#define TEA5761_TNCTRL_SWPM 0x10 /* 1= software port is FRRFLAG */
-#define TEA5761_TNCTRL_IFCTC 0x08 /* 1= IF count time 15.02 ms, 0= IF count time 2.02 ms */
-#define TEA5761_TNCTRL_AFM 0x04
-#define TEA5761_TNCTRL_SMUTE 0x02 /* 1= Soft mute */
-#define TEA5761_TNCTRL_SNC 0x01
-
- /* second byte */
-
-#define TEA5761_TNCTRL_MU 0x80 /* 1=Hard mute */
-#define TEA5761_TNCTRL_SSL_1 0x40
-#define TEA5761_TNCTRL_SSL_0 0x20
-#define TEA5761_TNCTRL_HLSI 0x10
-#define TEA5761_TNCTRL_MST 0x08 /* 1 = mono */
-#define TEA5761_TNCTRL_SWP 0x04
-#define TEA5761_TNCTRL_DTC 0x02 /* 1 = deemphasis 50 us, 0 = deemphasis 75 us */
-#define TEA5761_TNCTRL_AHLSI 0x01
-
-/* FRQCHECK - Read: bytes 6 and 7 */
- /* First byte */
-
- /* Bits 0-5 for divider MSB */
-
- /* Second byte */
- /* Bits 0-7 for divider LSB */
-
-/* TUNCHECK - Read: bytes 8 and 9 */
-
- /* First byte */
-#define TEA5761_TUNCHECK_IF_MASK 0x7e /* IF count */
-#define TEA5761_TUNCHECK_TUNTO 0x01
-
- /* Second byte */
-#define TEA5761_TUNCHECK_LEV_MASK 0xf0 /* Level Count */
-#define TEA5761_TUNCHECK_LD 0x08
-#define TEA5761_TUNCHECK_STEREO 0x04
-
-/* TESTREG - Read: bytes 10 and 11 / Write: bytes 5 and 6 */
-
- /* All zero = no test mode */
-
-/* MANID - Read: bytes 12 and 13 */
-
- /* First byte - should be 0x10 */
-#define TEA5767_MANID_VERSION_MASK 0xf0 /* Version = 1 */
-#define TEA5767_MANID_ID_MSB_MASK 0x0f /* Manufacurer ID - should be 0 */
-
- /* Second byte - Should be 0x2b */
-
-#define TEA5767_MANID_ID_LSB_MASK 0xfe /* Manufacturer ID - should be 0x15 */
-#define TEA5767_MANID_IDAV 0x01 /* 1 = Chip has ID, 0 = Chip has no ID */
-
-/* Chip ID - Read: bytes 14 and 15 */
-
- /* First byte - should be 0x57 */
-
- /* Second byte - should be 0x61 */
-
-/*****************************************************************************/
-
-#define FREQ_OFFSET 0 /* for TEA5767, it is 700 to give the right freq */
-static void tea5761_status_dump(unsigned char *buffer)
-{
- unsigned int div, frq;
-
- div = ((buffer[2] & 0x3f) << 8) | buffer[3];
-
- frq = 1000 * (div * 32768 / 1000 + FREQ_OFFSET + 225) / 4; /* Freq in KHz */
-
- printk(KERN_INFO "tea5761: Frequency %d.%03d KHz (divider = 0x%04x)\n",
- frq / 1000, frq % 1000, div);
-}
-
-/* Freq should be specifyed at 62.5 Hz */
-static int set_radio_freq(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct tea5761_priv *priv = fe->tuner_priv;
- unsigned int frq = params->frequency;
- unsigned char buffer[7] = {0, 0, 0, 0, 0, 0, 0 };
- unsigned div;
- int rc;
-
- tuner_dbg("radio freq counter %d\n", frq);
-
- if (params->mode == T_STANDBY) {
- tuner_dbg("TEA5761 set to standby mode\n");
- buffer[5] |= TEA5761_TNCTRL_MU;
- } else {
- buffer[4] |= TEA5761_TNCTRL_PUPD_0;
- }
-
-
- if (params->audmode == V4L2_TUNER_MODE_MONO) {
- tuner_dbg("TEA5761 set to mono\n");
- buffer[5] |= TEA5761_TNCTRL_MST;
- } else {
- tuner_dbg("TEA5761 set to stereo\n");
- }
-
- div = (1000 * (frq * 4 / 16 + 700 + 225) ) >> 15;
- buffer[1] = (div >> 8) & 0x3f;
- buffer[2] = div & 0xff;
-
- if (debug)
- tea5761_status_dump(buffer);
-
- if (7 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 7)))
- tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc);
-
- priv->frequency = frq * 125 / 2;
-
- return 0;
-}
-
-static int tea5761_read_status(struct dvb_frontend *fe, char *buffer)
-{
- struct tea5761_priv *priv = fe->tuner_priv;
- int rc;
-
- memset(buffer, 0, 16);
- if (16 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 16))) {
- tuner_warn("i2c i/o error: rc == %d (should be 16)\n", rc);
- return -EREMOTEIO;
- }
-
- return 0;
-}
-
-static inline int tea5761_signal(struct dvb_frontend *fe, const char *buffer)
-{
- struct tea5761_priv *priv = fe->tuner_priv;
-
- int signal = ((buffer[9] & TEA5761_TUNCHECK_LEV_MASK) << (13 - 4));
-
- tuner_dbg("Signal strength: %d\n", signal);
-
- return signal;
-}
-
-static inline int tea5761_stereo(struct dvb_frontend *fe, const char *buffer)
-{
- struct tea5761_priv *priv = fe->tuner_priv;
-
- int stereo = buffer[9] & TEA5761_TUNCHECK_STEREO;
-
- tuner_dbg("Radio ST GET = %02x\n", stereo);
-
- return (stereo ? V4L2_TUNER_SUB_STEREO : 0);
-}
-
-static int tea5761_get_status(struct dvb_frontend *fe, u32 *status)
-{
- unsigned char buffer[16];
-
- *status = 0;
-
- if (0 == tea5761_read_status(fe, buffer)) {
- if (tea5761_signal(fe, buffer))
- *status = TUNER_STATUS_LOCKED;
- if (tea5761_stereo(fe, buffer))
- *status |= TUNER_STATUS_STEREO;
- }
-
- return 0;
-}
-
-static int tea5761_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
-{
- unsigned char buffer[16];
-
- *strength = 0;
-
- if (0 == tea5761_read_status(fe, buffer))
- *strength = tea5761_signal(fe, buffer);
-
- return 0;
-}
-
-int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr)
-{
- unsigned char buffer[16];
- int rc;
- struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr };
-
- if (16 != (rc = tuner_i2c_xfer_recv(&i2c, buffer, 16))) {
- printk(KERN_WARNING "it is not a TEA5761. Received %i chars.\n", rc);
- return -EINVAL;
- }
-
- if ((buffer[13] != 0x2b) || (buffer[14] != 0x57) || (buffer[15] != 0x061)) {
- printk(KERN_WARNING "Manufacturer ID= 0x%02x, Chip ID = %02x%02x."
- " It is not a TEA5761\n",
- buffer[13], buffer[14], buffer[15]);
- return -EINVAL;
- }
- printk(KERN_WARNING "tea5761: TEA%02x%02x detected. "
- "Manufacturer ID= 0x%02x\n",
- buffer[14], buffer[15], buffer[13]);
-
- return 0;
-}
-
-static int tea5761_release(struct dvb_frontend *fe)
-{
- kfree(fe->tuner_priv);
- fe->tuner_priv = NULL;
-
- return 0;
-}
-
-static int tea5761_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct tea5761_priv *priv = fe->tuner_priv;
- *frequency = priv->frequency;
- return 0;
-}
-
-static struct dvb_tuner_ops tea5761_tuner_ops = {
- .info = {
- .name = "tea5761", // Philips TEA5761HN FM Radio
- },
- .set_analog_params = set_radio_freq,
- .release = tea5761_release,
- .get_frequency = tea5761_get_frequency,
- .get_status = tea5761_get_status,
- .get_rf_strength = tea5761_get_rf_strength,
-};
-
-struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
- u8 i2c_addr)
-{
- struct tea5761_priv *priv = NULL;
-
- if (tea5761_autodetection(i2c_adap, i2c_addr) == EINVAL)
- return NULL;
-
- priv = kzalloc(sizeof(struct tea5761_priv), GFP_KERNEL);
- if (priv == NULL)
- return NULL;
- fe->tuner_priv = priv;
-
- priv->i2c_props.addr = i2c_addr;
- priv->i2c_props.adap = i2c_adap;
- priv->i2c_props.name = "tea5761";
-
- memcpy(&fe->ops.tuner_ops, &tea5761_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- tuner_info("type set to %s\n", "Philips TEA5761HN FM Radio");
-
- return fe;
-}
-
-
-EXPORT_SYMBOL_GPL(tea5761_attach);
-EXPORT_SYMBOL_GPL(tea5761_autodetection);
-
-MODULE_DESCRIPTION("Philips TEA5761 FM tuner driver");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tea5761.h b/drivers/media/video/tea5761.h
deleted file mode 100644
index 8eb62722b98..00000000000
--- a/drivers/media/video/tea5761.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- 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 __TEA5761_H__
-#define __TEA5761_H__
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-
-#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE))
-extern int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr);
-
-extern struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
- u8 i2c_addr);
-#else
-static inline int tea5761_autodetection(struct i2c_adapter* i2c_adap,
- u8 i2c_addr)
-{
- printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
- __func__);
- return -EINVAL;
-}
-
-static inline struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
- u8 i2c_addr)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif /* __TEA5761_H__ */
diff --git a/drivers/media/video/tea5767.c b/drivers/media/video/tea5767.c
deleted file mode 100644
index f6e7d7ad842..00000000000
--- a/drivers/media/video/tea5767.c
+++ /dev/null
@@ -1,474 +0,0 @@
-/*
- * For Philips TEA5767 FM Chip used on some TV Cards like Prolink Pixelview
- * I2C address is allways 0xC0.
- *
- *
- * Copyright (c) 2005 Mauro Carvalho Chehab (mchehab@infradead.org)
- * This code is placed under the terms of the GNU General Public License
- *
- * tea5767 autodetection thanks to Torsten Seeboth and Atsushi Nakagawa
- * from their contributions on DScaler.
- */
-
-#include <linux/i2c.h>
-#include <linux/delay.h>
-#include <linux/videodev.h>
-#include "tuner-i2c.h"
-#include "tea5767.h"
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "enable verbose debug messages");
-
-/*****************************************************************************/
-
-struct tea5767_priv {
- struct tuner_i2c_props i2c_props;
- u32 frequency;
- struct tea5767_ctrl ctrl;
-};
-
-/*****************************************************************************/
-
-/******************************
- * Write mode register values *
- ******************************/
-
-/* First register */
-#define TEA5767_MUTE 0x80 /* Mutes output */
-#define TEA5767_SEARCH 0x40 /* Activates station search */
-/* Bits 0-5 for divider MSB */
-
-/* Second register */
-/* Bits 0-7 for divider LSB */
-
-/* Third register */
-
-/* Station search from botton to up */
-#define TEA5767_SEARCH_UP 0x80
-
-/* Searches with ADC output = 10 */
-#define TEA5767_SRCH_HIGH_LVL 0x60
-
-/* Searches with ADC output = 10 */
-#define TEA5767_SRCH_MID_LVL 0x40
-
-/* Searches with ADC output = 5 */
-#define TEA5767_SRCH_LOW_LVL 0x20
-
-/* if on, div=4*(Frf+Fif)/Fref otherwise, div=4*(Frf-Fif)/Freq) */
-#define TEA5767_HIGH_LO_INJECT 0x10
-
-/* Disable stereo */
-#define TEA5767_MONO 0x08
-
-/* Disable right channel and turns to mono */
-#define TEA5767_MUTE_RIGHT 0x04
-
-/* Disable left channel and turns to mono */
-#define TEA5767_MUTE_LEFT 0x02
-
-#define TEA5767_PORT1_HIGH 0x01
-
-/* Fourth register */
-#define TEA5767_PORT2_HIGH 0x80
-/* Chips stops working. Only I2C bus remains on */
-#define TEA5767_STDBY 0x40
-
-/* Japan freq (76-108 MHz. If disabled, 87.5-108 MHz */
-#define TEA5767_JAPAN_BAND 0x20
-
-/* Unselected means 32.768 KHz freq as reference. Otherwise Xtal at 13 MHz */
-#define TEA5767_XTAL_32768 0x10
-
-/* Cuts weak signals */
-#define TEA5767_SOFT_MUTE 0x08
-
-/* Activates high cut control */
-#define TEA5767_HIGH_CUT_CTRL 0x04
-
-/* Activates stereo noise control */
-#define TEA5767_ST_NOISE_CTL 0x02
-
-/* If activate PORT 1 indicates SEARCH or else it is used as PORT1 */
-#define TEA5767_SRCH_IND 0x01
-
-/* Fifth register */
-
-/* By activating, it will use Xtal at 13 MHz as reference for divider */
-#define TEA5767_PLLREF_ENABLE 0x80
-
-/* By activating, deemphasis=50, or else, deemphasis of 50us */
-#define TEA5767_DEEMPH_75 0X40
-
-/*****************************
- * Read mode register values *
- *****************************/
-
-/* First register */
-#define TEA5767_READY_FLAG_MASK 0x80
-#define TEA5767_BAND_LIMIT_MASK 0X40
-/* Bits 0-5 for divider MSB after search or preset */
-
-/* Second register */
-/* Bits 0-7 for divider LSB after search or preset */
-
-/* Third register */
-#define TEA5767_STEREO_MASK 0x80
-#define TEA5767_IF_CNTR_MASK 0x7f
-
-/* Fourth register */
-#define TEA5767_ADC_LEVEL_MASK 0xf0
-
-/* should be 0 */
-#define TEA5767_CHIP_ID_MASK 0x0f
-
-/* Fifth register */
-/* Reserved for future extensions */
-#define TEA5767_RESERVED_MASK 0xff
-
-/*****************************************************************************/
-
-static void tea5767_status_dump(struct tea5767_priv *priv,
- unsigned char *buffer)
-{
- unsigned int div, frq;
-
- if (TEA5767_READY_FLAG_MASK & buffer[0])
- tuner_info("Ready Flag ON\n");
- else
- tuner_info("Ready Flag OFF\n");
-
- if (TEA5767_BAND_LIMIT_MASK & buffer[0])
- tuner_info("Tuner at band limit\n");
- else
- tuner_info("Tuner not at band limit\n");
-
- div = ((buffer[0] & 0x3f) << 8) | buffer[1];
-
- switch (priv->ctrl.xtal_freq) {
- case TEA5767_HIGH_LO_13MHz:
- frq = (div * 50000 - 700000 - 225000) / 4; /* Freq in KHz */
- break;
- case TEA5767_LOW_LO_13MHz:
- frq = (div * 50000 + 700000 + 225000) / 4; /* Freq in KHz */
- break;
- case TEA5767_LOW_LO_32768:
- frq = (div * 32768 + 700000 + 225000) / 4; /* Freq in KHz */
- break;
- case TEA5767_HIGH_LO_32768:
- default:
- frq = (div * 32768 - 700000 - 225000) / 4; /* Freq in KHz */
- break;
- }
- buffer[0] = (div >> 8) & 0x3f;
- buffer[1] = div & 0xff;
-
- tuner_info("Frequency %d.%03d KHz (divider = 0x%04x)\n",
- frq / 1000, frq % 1000, div);
-
- if (TEA5767_STEREO_MASK & buffer[2])
- tuner_info("Stereo\n");
- else
- tuner_info("Mono\n");
-
- tuner_info("IF Counter = %d\n", buffer[2] & TEA5767_IF_CNTR_MASK);
-
- tuner_info("ADC Level = %d\n",
- (buffer[3] & TEA5767_ADC_LEVEL_MASK) >> 4);
-
- tuner_info("Chip ID = %d\n", (buffer[3] & TEA5767_CHIP_ID_MASK));
-
- tuner_info("Reserved = 0x%02x\n",
- (buffer[4] & TEA5767_RESERVED_MASK));
-}
-
-/* Freq should be specifyed at 62.5 Hz */
-static int set_radio_freq(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct tea5767_priv *priv = fe->tuner_priv;
- unsigned int frq = params->frequency;
- unsigned char buffer[5];
- unsigned div;
- int rc;
-
- tuner_dbg("radio freq = %d.%03d MHz\n", frq/16000,(frq/16)%1000);
-
- buffer[2] = 0;
-
- if (priv->ctrl.port1)
- buffer[2] |= TEA5767_PORT1_HIGH;
-
- if (params->audmode == V4L2_TUNER_MODE_MONO) {
- tuner_dbg("TEA5767 set to mono\n");
- buffer[2] |= TEA5767_MONO;
- } else {
- tuner_dbg("TEA5767 set to stereo\n");
- }
-
-
- buffer[3] = 0;
-
- if (priv->ctrl.port2)
- buffer[3] |= TEA5767_PORT2_HIGH;
-
- if (priv->ctrl.high_cut)
- buffer[3] |= TEA5767_HIGH_CUT_CTRL;
-
- if (priv->ctrl.st_noise)
- buffer[3] |= TEA5767_ST_NOISE_CTL;
-
- if (priv->ctrl.soft_mute)
- buffer[3] |= TEA5767_SOFT_MUTE;
-
- if (priv->ctrl.japan_band)
- buffer[3] |= TEA5767_JAPAN_BAND;
-
- buffer[4] = 0;
-
- if (priv->ctrl.deemph_75)
- buffer[4] |= TEA5767_DEEMPH_75;
-
- if (priv->ctrl.pllref)
- buffer[4] |= TEA5767_PLLREF_ENABLE;
-
-
- /* Rounds freq to next decimal value - for 62.5 KHz step */
- /* frq = 20*(frq/16)+radio_frq[frq%16]; */
-
- switch (priv->ctrl.xtal_freq) {
- case TEA5767_HIGH_LO_13MHz:
- tuner_dbg("radio HIGH LO inject xtal @ 13 MHz\n");
- buffer[2] |= TEA5767_HIGH_LO_INJECT;
- div = (frq * (4000 / 16) + 700000 + 225000 + 25000) / 50000;
- break;
- case TEA5767_LOW_LO_13MHz:
- tuner_dbg("radio LOW LO inject xtal @ 13 MHz\n");
-
- div = (frq * (4000 / 16) - 700000 - 225000 + 25000) / 50000;
- break;
- case TEA5767_LOW_LO_32768:
- tuner_dbg("radio LOW LO inject xtal @ 32,768 MHz\n");
- buffer[3] |= TEA5767_XTAL_32768;
- /* const 700=4000*175 Khz - to adjust freq to right value */
- div = ((frq * (4000 / 16) - 700000 - 225000) + 16384) >> 15;
- break;
- case TEA5767_HIGH_LO_32768:
- default:
- tuner_dbg("radio HIGH LO inject xtal @ 32,768 MHz\n");
-
- buffer[2] |= TEA5767_HIGH_LO_INJECT;
- buffer[3] |= TEA5767_XTAL_32768;
- div = ((frq * (4000 / 16) + 700000 + 225000) + 16384) >> 15;
- break;
- }
- buffer[0] = (div >> 8) & 0x3f;
- buffer[1] = div & 0xff;
-
- if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5)))
- tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc);
-
- if (debug) {
- if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5)))
- tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc);
- else
- tea5767_status_dump(priv, buffer);
- }
-
- priv->frequency = frq * 125 / 2;
-
- return 0;
-}
-
-static int tea5767_read_status(struct dvb_frontend *fe, char *buffer)
-{
- struct tea5767_priv *priv = fe->tuner_priv;
- int rc;
-
- memset(buffer, 0, 5);
- if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) {
- tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc);
- return -EREMOTEIO;
- }
-
- return 0;
-}
-
-static inline int tea5767_signal(struct dvb_frontend *fe, const char *buffer)
-{
- struct tea5767_priv *priv = fe->tuner_priv;
-
- int signal = ((buffer[3] & TEA5767_ADC_LEVEL_MASK) << 8);
-
- tuner_dbg("Signal strength: %d\n", signal);
-
- return signal;
-}
-
-static inline int tea5767_stereo(struct dvb_frontend *fe, const char *buffer)
-{
- struct tea5767_priv *priv = fe->tuner_priv;
-
- int stereo = buffer[2] & TEA5767_STEREO_MASK;
-
- tuner_dbg("Radio ST GET = %02x\n", stereo);
-
- return (stereo ? V4L2_TUNER_SUB_STEREO : 0);
-}
-
-static int tea5767_get_status(struct dvb_frontend *fe, u32 *status)
-{
- unsigned char buffer[5];
-
- *status = 0;
-
- if (0 == tea5767_read_status(fe, buffer)) {
- if (tea5767_signal(fe, buffer))
- *status = TUNER_STATUS_LOCKED;
- if (tea5767_stereo(fe, buffer))
- *status |= TUNER_STATUS_STEREO;
- }
-
- return 0;
-}
-
-static int tea5767_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
-{
- unsigned char buffer[5];
-
- *strength = 0;
-
- if (0 == tea5767_read_status(fe, buffer))
- *strength = tea5767_signal(fe, buffer);
-
- return 0;
-}
-
-static int tea5767_standby(struct dvb_frontend *fe)
-{
- unsigned char buffer[5];
- struct tea5767_priv *priv = fe->tuner_priv;
- unsigned div, rc;
-
- div = (87500 * 4 + 700 + 225 + 25) / 50; /* Set frequency to 87.5 MHz */
- buffer[0] = (div >> 8) & 0x3f;
- buffer[1] = div & 0xff;
- buffer[2] = TEA5767_PORT1_HIGH;
- buffer[3] = TEA5767_PORT2_HIGH | TEA5767_HIGH_CUT_CTRL |
- TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND | TEA5767_STDBY;
- buffer[4] = 0;
-
- if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5)))
- tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc);
-
- return 0;
-}
-
-int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr)
-{
- struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr };
- unsigned char buffer[7] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
- int rc;
-
- if ((rc = tuner_i2c_xfer_recv(&i2c, buffer, 7))< 5) {
- printk(KERN_WARNING "It is not a TEA5767. Received %i bytes.\n", rc);
- return EINVAL;
- }
-
- /* If all bytes are the same then it's a TV tuner and not a tea5767 */
- if (buffer[0] == buffer[1] && buffer[0] == buffer[2] &&
- buffer[0] == buffer[3] && buffer[0] == buffer[4]) {
- printk(KERN_WARNING "All bytes are equal. It is not a TEA5767\n");
- return EINVAL;
- }
-
- /* Status bytes:
- * Byte 4: bit 3:1 : CI (Chip Identification) == 0
- * bit 0 : internally set to 0
- * Byte 5: bit 7:0 : == 0
- */
- if (((buffer[3] & 0x0f) != 0x00) || (buffer[4] != 0x00)) {
- printk(KERN_WARNING "Chip ID is not zero. It is not a TEA5767\n");
- return EINVAL;
- }
-
-
- return 0;
-}
-
-static int tea5767_release(struct dvb_frontend *fe)
-{
- kfree(fe->tuner_priv);
- fe->tuner_priv = NULL;
-
- return 0;
-}
-
-static int tea5767_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct tea5767_priv *priv = fe->tuner_priv;
- *frequency = priv->frequency;
-
- return 0;
-}
-
-static int tea5767_set_config (struct dvb_frontend *fe, void *priv_cfg)
-{
- struct tea5767_priv *priv = fe->tuner_priv;
-
- memcpy(&priv->ctrl, priv_cfg, sizeof(priv->ctrl));
-
- return 0;
-}
-
-static struct dvb_tuner_ops tea5767_tuner_ops = {
- .info = {
- .name = "tea5767", // Philips TEA5767HN FM Radio
- },
-
- .set_analog_params = set_radio_freq,
- .set_config = tea5767_set_config,
- .sleep = tea5767_standby,
- .release = tea5767_release,
- .get_frequency = tea5767_get_frequency,
- .get_status = tea5767_get_status,
- .get_rf_strength = tea5767_get_rf_strength,
-};
-
-struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
- u8 i2c_addr)
-{
- struct tea5767_priv *priv = NULL;
-
- priv = kzalloc(sizeof(struct tea5767_priv), GFP_KERNEL);
- if (priv == NULL)
- return NULL;
- fe->tuner_priv = priv;
-
- priv->i2c_props.addr = i2c_addr;
- priv->i2c_props.adap = i2c_adap;
- priv->i2c_props.name = "tea5767";
-
- priv->ctrl.xtal_freq = TEA5767_HIGH_LO_32768;
- priv->ctrl.port1 = 1;
- priv->ctrl.port2 = 1;
- priv->ctrl.high_cut = 1;
- priv->ctrl.st_noise = 1;
- priv->ctrl.japan_band = 1;
-
- memcpy(&fe->ops.tuner_ops, &tea5767_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- tuner_info("type set to %s\n", "Philips TEA5767HN FM Radio");
-
- return fe;
-}
-
-EXPORT_SYMBOL_GPL(tea5767_attach);
-EXPORT_SYMBOL_GPL(tea5767_autodetection);
-
-MODULE_DESCRIPTION("Philips TEA5767 FM tuner driver");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tea5767.h b/drivers/media/video/tea5767.h
deleted file mode 100644
index 7b547c092e2..00000000000
--- a/drivers/media/video/tea5767.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- 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 __TEA5767_H__
-#define __TEA5767_H__
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-
-enum tea5767_xtal {
- TEA5767_LOW_LO_32768 = 0,
- TEA5767_HIGH_LO_32768 = 1,
- TEA5767_LOW_LO_13MHz = 2,
- TEA5767_HIGH_LO_13MHz = 3,
-};
-
-struct tea5767_ctrl {
- unsigned int port1:1;
- unsigned int port2:1;
- unsigned int high_cut:1;
- unsigned int st_noise:1;
- unsigned int soft_mute:1;
- unsigned int japan_band:1;
- unsigned int deemph_75:1;
- unsigned int pllref:1;
- enum tea5767_xtal xtal_freq;
-};
-
-#if defined(CONFIG_TUNER_TEA5767) || (defined(CONFIG_TUNER_TEA5767_MODULE) && defined(MODULE))
-extern int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr);
-
-extern struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
- u8 i2c_addr);
-#else
-static inline int tea5767_autodetection(struct i2c_adapter* i2c_adap,
- u8 i2c_addr)
-{
- printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
- __func__);
- return -EINVAL;
-}
-
-static inline struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe,
- struct i2c_adapter* i2c_adap,
- u8 i2c_addr)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif /* __TEA5767_H__ */
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index 2a2748238c7..cc19c4abb46 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -33,6 +33,46 @@
#define PREFIX t->i2c->driver->driver.name
+/** This macro allows us to probe dynamically, avoiding static links */
+#ifdef CONFIG_DVB_CORE_ATTACH
+#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \
+ int __r = -EINVAL; \
+ typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
+ if (__a) { \
+ __r = (int) __a(ARGS); \
+ } else { \
+ printk(KERN_ERR "TUNER: Unable to find " \
+ "symbol "#FUNCTION"()\n"); \
+ } \
+ symbol_put(FUNCTION); \
+ __r; \
+})
+
+static void tuner_detach(struct dvb_frontend *fe)
+{
+ if (fe->ops.tuner_ops.release) {
+ fe->ops.tuner_ops.release(fe);
+ symbol_put_addr(fe->ops.tuner_ops.release);
+ }
+ if (fe->ops.analog_ops.release) {
+ fe->ops.analog_ops.release(fe);
+ symbol_put_addr(fe->ops.analog_ops.release);
+ }
+}
+#else
+#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \
+ FUNCTION(ARGS); \
+})
+
+static void tuner_detach(struct dvb_frontend *fe)
+{
+ if (fe->ops.tuner_ops.release)
+ fe->ops.tuner_ops.release(fe);
+ if (fe->ops.analog_ops.release)
+ fe->ops.analog_ops.release(fe);
+}
+#endif
+
struct tuner {
/* device */
struct dvb_frontend fe;
@@ -56,7 +96,7 @@ struct tuner {
/* standard i2c insmod options */
static unsigned short normal_i2c[] = {
-#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE))
0x10,
#endif
0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
@@ -139,22 +179,6 @@ static void fe_set_params(struct dvb_frontend *fe,
fe_tuner_ops->set_analog_params(fe, params);
}
-static void fe_release(struct dvb_frontend *fe)
-{
- if (fe->ops.tuner_ops.release)
- fe->ops.tuner_ops.release(fe);
-
- /* DO NOT kfree(fe->analog_demod_priv)
- *
- * If we are in this function, analog_demod_priv contains a pointer
- * to struct tuner *t. This will be kfree'd in tuner_detach().
- *
- * Otherwise, fe->ops.analog_demod_ops->release will
- * handle the cleanup for analog demodulator modules.
- */
- fe->analog_demod_priv = NULL;
-}
-
static void fe_standby(struct dvb_frontend *fe)
{
struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
@@ -191,7 +215,6 @@ static void tuner_status(struct dvb_frontend *fe);
static struct analog_demod_ops tuner_core_ops = {
.set_params = fe_set_params,
.standby = fe_standby,
- .release = fe_release,
.has_signal = fe_has_signal,
.set_config = fe_set_config,
.tuner_status = tuner_status
@@ -323,7 +346,8 @@ static void attach_tda829x(struct tuner *t)
.lna_cfg = t->config,
.tuner_callback = t->tuner_callback,
};
- tda829x_attach(&t->fe, t->i2c->adapter, t->i2c->addr, &cfg);
+ dvb_attach(tda829x_attach,
+ &t->fe, t->i2c->adapter, t->i2c->addr, &cfg);
}
static struct xc5000_config xc5000_cfg;
@@ -356,12 +380,13 @@ static void set_type(struct i2c_client *c, unsigned int type,
}
/* discard private data, in case set_type() was previously called */
- if (analog_ops->release)
- analog_ops->release(&t->fe);
+ tuner_detach(&t->fe);
+ t->fe.analog_demod_priv = NULL;
switch (t->type) {
case TUNER_MT2032:
- microtune_attach(&t->fe, t->i2c->adapter, t->i2c->addr);
+ dvb_attach(microtune_attach,
+ &t->fe, t->i2c->adapter, t->i2c->addr);
break;
case TUNER_PHILIPS_TDA8290:
{
@@ -369,12 +394,14 @@ static void set_type(struct i2c_client *c, unsigned int type,
break;
}
case TUNER_TEA5767:
- if (!tea5767_attach(&t->fe, t->i2c->adapter, t->i2c->addr))
+ if (!dvb_attach(tea5767_attach, &t->fe,
+ t->i2c->adapter, t->i2c->addr))
goto attach_failed;
t->mode_mask = T_RADIO;
break;
case TUNER_TEA5761:
- if (!tea5761_attach(&t->fe, t->i2c->adapter, t->i2c->addr))
+ if (!dvb_attach(tea5761_attach, &t->fe,
+ t->i2c->adapter, t->i2c->addr))
goto attach_failed;
t->mode_mask = T_RADIO;
break;
@@ -388,8 +415,8 @@ static void set_type(struct i2c_client *c, unsigned int type,
buffer[2] = 0x86;
buffer[3] = 0x54;
i2c_master_send(c, buffer, 4);
- if (!simple_tuner_attach(&t->fe, t->i2c->adapter, t->i2c->addr,
- t->type))
+ if (!dvb_attach(simple_tuner_attach, &t->fe,
+ t->i2c->adapter, t->i2c->addr, t->type))
goto attach_failed;
break;
case TUNER_PHILIPS_TD1316:
@@ -397,9 +424,9 @@ static void set_type(struct i2c_client *c, unsigned int type,
buffer[1] = 0xdc;
buffer[2] = 0x86;
buffer[3] = 0xa4;
- i2c_master_send(c,buffer,4);
- if (!simple_tuner_attach(&t->fe, t->i2c->adapter,
- t->i2c->addr, t->type))
+ i2c_master_send(c, buffer, 4);
+ if (!dvb_attach(simple_tuner_attach, &t->fe,
+ t->i2c->adapter, t->i2c->addr, t->type))
goto attach_failed;
break;
case TUNER_XC2028:
@@ -409,12 +436,13 @@ static void set_type(struct i2c_client *c, unsigned int type,
.i2c_addr = t->i2c->addr,
.callback = t->tuner_callback,
};
- if (!xc2028_attach(&t->fe, &cfg))
+ if (!dvb_attach(xc2028_attach, &t->fe, &cfg))
goto attach_failed;
break;
}
case TUNER_TDA9887:
- tda9887_attach(&t->fe, t->i2c->adapter, t->i2c->addr);
+ dvb_attach(tda9887_attach,
+ &t->fe, t->i2c->adapter, t->i2c->addr);
break;
case TUNER_XC5000:
{
@@ -424,7 +452,8 @@ static void set_type(struct i2c_client *c, unsigned int type,
xc5000_cfg.if_khz = 5380;
xc5000_cfg.priv = c->adapter->algo_data;
xc5000_cfg.tuner_callback = t->tuner_callback;
- if (!xc5000_attach(&t->fe, t->i2c->adapter, &xc5000_cfg))
+ if (!dvb_attach(xc5000_attach,
+ &t->fe, t->i2c->adapter, &xc5000_cfg))
goto attach_failed;
xc_tuner_ops = &t->fe.ops.tuner_ops;
@@ -433,8 +462,8 @@ static void set_type(struct i2c_client *c, unsigned int type,
break;
}
default:
- if (!simple_tuner_attach(&t->fe, t->i2c->adapter,
- t->i2c->addr, t->type))
+ if (!dvb_attach(simple_tuner_attach, &t->fe,
+ t->i2c->adapter, t->i2c->addr, t->type))
goto attach_failed;
break;
@@ -442,12 +471,14 @@ static void set_type(struct i2c_client *c, unsigned int type,
if ((NULL == analog_ops->set_params) &&
(fe_tuner_ops->set_analog_params)) {
+
strlcpy(t->i2c->name, fe_tuner_ops->info.name,
sizeof(t->i2c->name));
t->fe.analog_demod_priv = t;
memcpy(analog_ops, &tuner_core_ops,
sizeof(struct analog_demod_ops));
+
} else {
strlcpy(t->i2c->name, analog_ops->info.name,
sizeof(t->i2c->name));
@@ -645,8 +676,8 @@ static void tuner_status(struct dvb_frontend *fe)
{
struct tuner *t = fe->analog_demod_priv;
unsigned long freq, freq_fraction;
- struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
- struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+ struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
+ struct analog_demod_ops *analog_ops = &fe->ops.analog_ops;
const char *p;
switch (t->mode) {
@@ -730,8 +761,10 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
- if (tuner_debug>1)
+ if (tuner_debug > 1) {
v4l_i2c_print_ioctl(client,cmd);
+ printk("\n");
+ }
switch (cmd) {
/* --- configuration --- */
@@ -1112,8 +1145,9 @@ static int tuner_probe(struct i2c_client *client,
if (!no_autodetect) {
switch (client->addr) {
case 0x10:
- if (tea5761_autodetection(t->i2c->adapter,
- t->i2c->addr) >= 0) {
+ if (tuner_symbol_probe(tea5761_autodetection,
+ t->i2c->adapter,
+ t->i2c->addr) >= 0) {
t->type = TUNER_TEA5761;
t->mode_mask = T_RADIO;
t->mode = T_STANDBY;
@@ -1132,8 +1166,8 @@ static int tuner_probe(struct i2c_client *client,
case 0x4b:
/* If chip is not tda8290, don't register.
since it can be tda9887*/
- if (tda829x_probe(t->i2c->adapter,
- t->i2c->addr) == 0) {
+ if (tuner_symbol_probe(tda829x_probe, t->i2c->adapter,
+ t->i2c->addr) == 0) {
tuner_dbg("tda829x detected\n");
} else {
/* Default is being tda9887 */
@@ -1145,7 +1179,8 @@ static int tuner_probe(struct i2c_client *client,
}
break;
case 0x60:
- if (tea5767_autodetection(t->i2c->adapter, t->i2c->addr)
+ if (tuner_symbol_probe(tea5767_autodetection,
+ t->i2c->adapter, t->i2c->addr)
!= EINVAL) {
t->type = TUNER_TEA5767;
t->mode_mask = T_RADIO;
@@ -1234,10 +1269,9 @@ static int tuner_legacy_probe(struct i2c_adapter *adap)
static int tuner_remove(struct i2c_client *client)
{
struct tuner *t = i2c_get_clientdata(client);
- struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
- if (analog_ops->release)
- analog_ops->release(&t->fe);
+ tuner_detach(&t->fe);
+ t->fe.analog_demod_priv = NULL;
list_del(&t->list);
kfree(t);
diff --git a/drivers/media/video/tuner-i2c.h b/drivers/media/video/tuner-i2c.h
deleted file mode 100644
index 3ad6c8e0b04..00000000000
--- a/drivers/media/video/tuner-i2c.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- tuner-i2c.h - i2c interface for different tuners
-
- Copyright (C) 2007 Michael Krufky (mkrufky@linuxtv.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 __TUNER_I2C_H__
-#define __TUNER_I2C_H__
-
-#include <linux/i2c.h>
-
-struct tuner_i2c_props {
- u8 addr;
- struct i2c_adapter *adap;
-
- /* used for tuner instance management */
- int count;
- char *name;
-};
-
-static inline int tuner_i2c_xfer_send(struct tuner_i2c_props *props, char *buf, int len)
-{
- struct i2c_msg msg = { .addr = props->addr, .flags = 0,
- .buf = buf, .len = len };
- int ret = i2c_transfer(props->adap, &msg, 1);
-
- return (ret == 1) ? len : ret;
-}
-
-static inline int tuner_i2c_xfer_recv(struct tuner_i2c_props *props, char *buf, int len)
-{
- struct i2c_msg msg = { .addr = props->addr, .flags = I2C_M_RD,
- .buf = buf, .len = len };
- int ret = i2c_transfer(props->adap, &msg, 1);
-
- return (ret == 1) ? len : ret;
-}
-
-static inline int tuner_i2c_xfer_send_recv(struct tuner_i2c_props *props,
- char *obuf, int olen,
- char *ibuf, int ilen)
-{
- struct i2c_msg msg[2] = { { .addr = props->addr, .flags = 0,
- .buf = obuf, .len = olen },
- { .addr = props->addr, .flags = I2C_M_RD,
- .buf = ibuf, .len = ilen } };
- int ret = i2c_transfer(props->adap, msg, 2);
-
- return (ret == 2) ? ilen : ret;
-}
-
-/* Callers must declare as a global for the module:
- *
- * static LIST_HEAD(hybrid_tuner_instance_list);
- *
- * hybrid_tuner_instance_list should be the third argument
- * passed into hybrid_tuner_request_state().
- *
- * state structure must contain the following:
- *
- * struct list_head hybrid_tuner_instance_list;
- * struct tuner_i2c_props i2c_props;
- *
- * hybrid_tuner_instance_list (both within state structure and globally)
- * is only required if the driver is using hybrid_tuner_request_state
- * and hybrid_tuner_release_state to manage state sharing between
- * multiple instances of hybrid tuners.
- */
-
-#define tuner_printk(kernlvl, i2cprops, fmt, arg...) do { \
- printk(kernlvl "%s %d-%04x: " fmt, i2cprops.name, \
- i2cprops.adap ? \
- i2c_adapter_id(i2cprops.adap) : -1, \
- i2cprops.addr, ##arg); \
- } while (0)
-
-/* TO DO: convert all callers of these macros to pass in
- * struct tuner_i2c_props, then remove the macro wrappers */
-
-#define __tuner_warn(i2cprops, fmt, arg...) do { \
- tuner_printk(KERN_WARNING, i2cprops, fmt, ##arg); \
- } while (0)
-
-#define __tuner_info(i2cprops, fmt, arg...) do { \
- tuner_printk(KERN_INFO, i2cprops, fmt, ##arg); \
- } while (0)
-
-#define __tuner_err(i2cprops, fmt, arg...) do { \
- tuner_printk(KERN_ERR, i2cprops, fmt, ##arg); \
- } while (0)
-
-#define __tuner_dbg(i2cprops, fmt, arg...) do { \
- if ((debug)) \
- tuner_printk(KERN_DEBUG, i2cprops, fmt, ##arg); \
- } while (0)
-
-#define tuner_warn(fmt, arg...) __tuner_warn(priv->i2c_props, fmt, ##arg)
-#define tuner_info(fmt, arg...) __tuner_info(priv->i2c_props, fmt, ##arg)
-#define tuner_err(fmt, arg...) __tuner_err(priv->i2c_props, fmt, ##arg)
-#define tuner_dbg(fmt, arg...) __tuner_dbg(priv->i2c_props, fmt, ##arg)
-
-/****************************************************************************/
-
-/* The return value of hybrid_tuner_request_state indicates the number of
- * instances using this tuner object.
- *
- * 0 - no instances, indicates an error - kzalloc must have failed
- *
- * 1 - one instance, indicates that the tuner object was created successfully
- *
- * 2 (or more) instances, indicates that an existing tuner object was found
- */
-
-#define hybrid_tuner_request_state(type, state, list, i2cadap, i2caddr, devname)\
-({ \
- int __ret = 0; \
- list_for_each_entry(state, &list, hybrid_tuner_instance_list) { \
- if (((i2cadap) && (state->i2c_props.adap)) && \
- ((i2c_adapter_id(state->i2c_props.adap) == \
- i2c_adapter_id(i2cadap)) && \
- (i2caddr == state->i2c_props.addr))) { \
- __tuner_info(state->i2c_props, \
- "attaching existing instance\n"); \
- state->i2c_props.count++; \
- __ret = state->i2c_props.count; \
- break; \
- } \
- } \
- if (0 == __ret) { \
- state = kzalloc(sizeof(type), GFP_KERNEL); \
- if (NULL == state) \
- goto __fail; \
- state->i2c_props.addr = i2caddr; \
- state->i2c_props.adap = i2cadap; \
- state->i2c_props.name = devname; \
- __tuner_info(state->i2c_props, \
- "creating new instance\n"); \
- list_add_tail(&state->hybrid_tuner_instance_list, &list);\
- state->i2c_props.count++; \
- __ret = state->i2c_props.count; \
- } \
-__fail: \
- __ret; \
-})
-
-#define hybrid_tuner_release_state(state) \
-({ \
- int __ret; \
- state->i2c_props.count--; \
- __ret = state->i2c_props.count; \
- if (!state->i2c_props.count) { \
- __tuner_info(state->i2c_props, "destroying instance\n");\
- list_del(&state->hybrid_tuner_instance_list); \
- kfree(state); \
- } \
- __ret; \
-})
-
-#endif /* __TUNER_I2C_H__ */
diff --git a/drivers/media/video/tuner-simple.c b/drivers/media/video/tuner-simple.c
deleted file mode 100644
index be8d903171b..00000000000
--- a/drivers/media/video/tuner-simple.c
+++ /dev/null
@@ -1,1093 +0,0 @@
-/*
- * i2c tv tuner chip device driver
- * controls all those simple 4-control-bytes style tuners.
- *
- * This "tuner-simple" module was split apart from the original "tuner" module.
- */
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/videodev.h>
-#include <media/tuner.h>
-#include <media/v4l2-common.h>
-#include <media/tuner-types.h>
-#include "tuner-i2c.h"
-#include "tuner-simple.h"
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "enable verbose debug messages");
-
-#define TUNER_SIMPLE_MAX 64
-static unsigned int simple_devcount;
-
-static int offset;
-module_param(offset, int, 0664);
-MODULE_PARM_DESC(offset, "Allows to specify an offset for tuner");
-
-static unsigned int atv_input[TUNER_SIMPLE_MAX] = \
- { [0 ... (TUNER_SIMPLE_MAX-1)] = 0 };
-static unsigned int dtv_input[TUNER_SIMPLE_MAX] = \
- { [0 ... (TUNER_SIMPLE_MAX-1)] = 0 };
-module_param_array(atv_input, int, NULL, 0644);
-module_param_array(dtv_input, int, NULL, 0644);
-MODULE_PARM_DESC(atv_input, "specify atv rf input, 0 for autoselect");
-MODULE_PARM_DESC(dtv_input, "specify dtv rf input, 0 for autoselect");
-
-/* ---------------------------------------------------------------------- */
-
-/* tv standard selection for Temic 4046 FM5
- this value takes the low bits of control byte 2
- from datasheet Rev.01, Feb.00
- standard BG I L L2 D
- picture IF 38.9 38.9 38.9 33.95 38.9
- sound 1 33.4 32.9 32.4 40.45 32.4
- sound 2 33.16
- NICAM 33.05 32.348 33.05 33.05
- */
-#define TEMIC_SET_PAL_I 0x05
-#define TEMIC_SET_PAL_DK 0x09
-#define TEMIC_SET_PAL_L 0x0a /* SECAM ? */
-#define TEMIC_SET_PAL_L2 0x0b /* change IF ! */
-#define TEMIC_SET_PAL_BG 0x0c
-
-/* tv tuner system standard selection for Philips FQ1216ME
- this value takes the low bits of control byte 2
- from datasheet "1999 Nov 16" (supersedes "1999 Mar 23")
- standard BG DK I L L`
- picture carrier 38.90 38.90 38.90 38.90 33.95
- colour 34.47 34.47 34.47 34.47 38.38
- sound 1 33.40 32.40 32.90 32.40 40.45
- sound 2 33.16 - - - -
- NICAM 33.05 33.05 32.35 33.05 39.80
- */
-#define PHILIPS_SET_PAL_I 0x01 /* Bit 2 always zero !*/
-#define PHILIPS_SET_PAL_BGDK 0x09
-#define PHILIPS_SET_PAL_L2 0x0a
-#define PHILIPS_SET_PAL_L 0x0b
-
-/* system switching for Philips FI1216MF MK2
- from datasheet "1996 Jul 09",
- standard BG L L'
- picture carrier 38.90 38.90 33.95
- colour 34.47 34.37 38.38
- sound 1 33.40 32.40 40.45
- sound 2 33.16 - -
- NICAM 33.05 33.05 39.80
- */
-#define PHILIPS_MF_SET_STD_BG 0x01 /* Bit 2 must be zero, Bit 3 is system output */
-#define PHILIPS_MF_SET_STD_L 0x03 /* Used on Secam France */
-#define PHILIPS_MF_SET_STD_LC 0x02 /* Used on SECAM L' */
-
-/* Control byte */
-
-#define TUNER_RATIO_MASK 0x06 /* Bit cb1:cb2 */
-#define TUNER_RATIO_SELECT_50 0x00
-#define TUNER_RATIO_SELECT_32 0x02
-#define TUNER_RATIO_SELECT_166 0x04
-#define TUNER_RATIO_SELECT_62 0x06
-
-#define TUNER_CHARGE_PUMP 0x40 /* Bit cb6 */
-
-/* Status byte */
-
-#define TUNER_POR 0x80
-#define TUNER_FL 0x40
-#define TUNER_MODE 0x38
-#define TUNER_AFC 0x07
-#define TUNER_SIGNAL 0x07
-#define TUNER_STEREO 0x10
-
-#define TUNER_PLL_LOCKED 0x40
-#define TUNER_STEREO_MK3 0x04
-
-static DEFINE_MUTEX(tuner_simple_list_mutex);
-static LIST_HEAD(hybrid_tuner_instance_list);
-
-struct tuner_simple_priv {
- unsigned int nr;
- u16 last_div;
-
- struct tuner_i2c_props i2c_props;
- struct list_head hybrid_tuner_instance_list;
-
- unsigned int type;
- struct tunertype *tun;
-
- u32 frequency;
- u32 bandwidth;
-};
-
-/* ---------------------------------------------------------------------- */
-
-static int tuner_read_status(struct dvb_frontend *fe)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
- unsigned char byte;
-
- if (1 != tuner_i2c_xfer_recv(&priv->i2c_props, &byte, 1))
- return 0;
-
- return byte;
-}
-
-static inline int tuner_signal(const int status)
-{
- return (status & TUNER_SIGNAL) << 13;
-}
-
-static inline int tuner_stereo(const int type, const int status)
-{
- switch (type) {
- case TUNER_PHILIPS_FM1216ME_MK3:
- case TUNER_PHILIPS_FM1236_MK3:
- case TUNER_PHILIPS_FM1256_IH3:
- case TUNER_LG_NTSC_TAPE:
- return ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3);
- default:
- return status & TUNER_STEREO;
- }
-}
-
-static inline int tuner_islocked(const int status)
-{
- return (status & TUNER_FL);
-}
-
-static inline int tuner_afcstatus(const int status)
-{
- return (status & TUNER_AFC) - 2;
-}
-
-
-static int simple_get_status(struct dvb_frontend *fe, u32 *status)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
- int tuner_status;
-
- if (priv->i2c_props.adap == NULL)
- return -EINVAL;
-
- tuner_status = tuner_read_status(fe);
-
- *status = 0;
-
- if (tuner_islocked(tuner_status))
- *status = TUNER_STATUS_LOCKED;
- if (tuner_stereo(priv->type, tuner_status))
- *status |= TUNER_STATUS_STEREO;
-
- tuner_dbg("AFC Status: %d\n", tuner_afcstatus(tuner_status));
-
- return 0;
-}
-
-static int simple_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
- int signal;
-
- if (priv->i2c_props.adap == NULL)
- return -EINVAL;
-
- signal = tuner_signal(tuner_read_status(fe));
-
- *strength = signal;
-
- tuner_dbg("Signal strength: %d\n", signal);
-
- return 0;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static inline char *tuner_param_name(enum param_type type)
-{
- char *name;
-
- switch (type) {
- case TUNER_PARAM_TYPE_RADIO:
- name = "radio";
- break;
- case TUNER_PARAM_TYPE_PAL:
- name = "pal";
- break;
- case TUNER_PARAM_TYPE_SECAM:
- name = "secam";
- break;
- case TUNER_PARAM_TYPE_NTSC:
- name = "ntsc";
- break;
- case TUNER_PARAM_TYPE_DIGITAL:
- name = "digital";
- break;
- default:
- name = "unknown";
- break;
- }
- return name;
-}
-
-static struct tuner_params *simple_tuner_params(struct dvb_frontend *fe,
- enum param_type desired_type)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
- struct tunertype *tun = priv->tun;
- int i;
-
- for (i = 0; i < tun->count; i++)
- if (desired_type == tun->params[i].type)
- break;
-
- /* use default tuner params if desired_type not available */
- if (i == tun->count) {
- tuner_dbg("desired params (%s) undefined for tuner %d\n",
- tuner_param_name(desired_type), priv->type);
- i = 0;
- }
-
- tuner_dbg("using tuner params #%d (%s)\n", i,
- tuner_param_name(tun->params[i].type));
-
- return &tun->params[i];
-}
-
-static int simple_config_lookup(struct dvb_frontend *fe,
- struct tuner_params *t_params,
- int *frequency, u8 *config, u8 *cb)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
- int i;
-
- for (i = 0; i < t_params->count; i++) {
- if (*frequency > t_params->ranges[i].limit)
- continue;
- break;
- }
- if (i == t_params->count) {
- tuner_dbg("frequency out of range (%d > %d)\n",
- *frequency, t_params->ranges[i - 1].limit);
- *frequency = t_params->ranges[--i].limit;
- }
- *config = t_params->ranges[i].config;
- *cb = t_params->ranges[i].cb;
-
- tuner_dbg("freq = %d.%02d (%d), range = %d, "
- "config = 0x%02x, cb = 0x%02x\n",
- *frequency / 16, *frequency % 16 * 100 / 16, *frequency,
- i, *config, *cb);
-
- return i;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static void simple_set_rf_input(struct dvb_frontend *fe,
- u8 *config, u8 *cb, unsigned int rf)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
-
- switch (priv->type) {
- case TUNER_PHILIPS_TUV1236D:
- switch (rf) {
- case 1:
- *cb |= 0x08;
- break;
- default:
- *cb &= ~0x08;
- break;
- }
- break;
- case TUNER_PHILIPS_FCV1236D:
- switch (rf) {
- case 1:
- *cb |= 0x01;
- break;
- default:
- *cb &= ~0x01;
- break;
- }
- break;
- default:
- break;
- }
-}
-
-static int simple_std_setup(struct dvb_frontend *fe,
- struct analog_parameters *params,
- u8 *config, u8 *cb)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
- u8 tuneraddr;
- int rc;
-
- /* tv norm specific stuff for multi-norm tuners */
- switch (priv->type) {
- case TUNER_PHILIPS_SECAM: /* FI1216MF */
- /* 0x01 -> ??? no change ??? */
- /* 0x02 -> PAL BDGHI / SECAM L */
- /* 0x04 -> ??? PAL others / SECAM others ??? */
- *cb &= ~0x03;
- if (params->std & V4L2_STD_SECAM_L)
- /* also valid for V4L2_STD_SECAM */
- *cb |= PHILIPS_MF_SET_STD_L;
- else if (params->std & V4L2_STD_SECAM_LC)
- *cb |= PHILIPS_MF_SET_STD_LC;
- else /* V4L2_STD_B|V4L2_STD_GH */
- *cb |= PHILIPS_MF_SET_STD_BG;
- break;
-
- case TUNER_TEMIC_4046FM5:
- *cb &= ~0x0f;
-
- if (params->std & V4L2_STD_PAL_BG) {
- *cb |= TEMIC_SET_PAL_BG;
-
- } else if (params->std & V4L2_STD_PAL_I) {
- *cb |= TEMIC_SET_PAL_I;
-
- } else if (params->std & V4L2_STD_PAL_DK) {
- *cb |= TEMIC_SET_PAL_DK;
-
- } else if (params->std & V4L2_STD_SECAM_L) {
- *cb |= TEMIC_SET_PAL_L;
-
- }
- break;
-
- case TUNER_PHILIPS_FQ1216ME:
- *cb &= ~0x0f;
-
- if (params->std & (V4L2_STD_PAL_BG|V4L2_STD_PAL_DK)) {
- *cb |= PHILIPS_SET_PAL_BGDK;
-
- } else if (params->std & V4L2_STD_PAL_I) {
- *cb |= PHILIPS_SET_PAL_I;
-
- } else if (params->std & V4L2_STD_SECAM_L) {
- *cb |= PHILIPS_SET_PAL_L;
-
- }
- break;
-
- case TUNER_PHILIPS_FCV1236D:
- /* 0x00 -> ATSC antenna input 1 */
- /* 0x01 -> ATSC antenna input 2 */
- /* 0x02 -> NTSC antenna input 1 */
- /* 0x03 -> NTSC antenna input 2 */
- *cb &= ~0x03;
- if (!(params->std & V4L2_STD_ATSC))
- *cb |= 2;
- break;
-
- case TUNER_MICROTUNE_4042FI5:
- /* Set the charge pump for fast tuning */
- *config |= TUNER_CHARGE_PUMP;
- break;
-
- case TUNER_PHILIPS_TUV1236D:
- {
- /* 0x40 -> ATSC antenna input 1 */
- /* 0x48 -> ATSC antenna input 2 */
- /* 0x00 -> NTSC antenna input 1 */
- /* 0x08 -> NTSC antenna input 2 */
- u8 buffer[4] = { 0x14, 0x00, 0x17, 0x00};
- *cb &= ~0x40;
- if (params->std & V4L2_STD_ATSC) {
- *cb |= 0x40;
- buffer[1] = 0x04;
- }
- /* set to the correct mode (analog or digital) */
- tuneraddr = priv->i2c_props.addr;
- priv->i2c_props.addr = 0x0a;
- rc = tuner_i2c_xfer_send(&priv->i2c_props, &buffer[0], 2);
- if (2 != rc)
- tuner_warn("i2c i/o error: rc == %d "
- "(should be 2)\n", rc);
- rc = tuner_i2c_xfer_send(&priv->i2c_props, &buffer[2], 2);
- if (2 != rc)
- tuner_warn("i2c i/o error: rc == %d "
- "(should be 2)\n", rc);
- priv->i2c_props.addr = tuneraddr;
- break;
- }
- }
- if (atv_input[priv->nr])
- simple_set_rf_input(fe, config, cb, atv_input[priv->nr]);
-
- return 0;
-}
-
-static int simple_post_tune(struct dvb_frontend *fe, u8 *buffer,
- u16 div, u8 config, u8 cb)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
- int rc;
-
- switch (priv->type) {
- case TUNER_LG_TDVS_H06XF:
- /* Set the Auxiliary Byte. */
- buffer[0] = buffer[2];
- buffer[0] &= ~0x20;
- buffer[0] |= 0x18;
- buffer[1] = 0x20;
- tuner_dbg("tv 0x%02x 0x%02x\n", buffer[0], buffer[1]);
-
- rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 2);
- if (2 != rc)
- tuner_warn("i2c i/o error: rc == %d "
- "(should be 2)\n", rc);
- break;
- case TUNER_MICROTUNE_4042FI5:
- {
- /* FIXME - this may also work for other tuners */
- unsigned long timeout = jiffies + msecs_to_jiffies(1);
- u8 status_byte = 0;
-
- /* Wait until the PLL locks */
- for (;;) {
- if (time_after(jiffies, timeout))
- return 0;
- rc = tuner_i2c_xfer_recv(&priv->i2c_props,
- &status_byte, 1);
- if (1 != rc) {
- tuner_warn("i2c i/o read error: rc == %d "
- "(should be 1)\n", rc);
- break;
- }
- if (status_byte & TUNER_PLL_LOCKED)
- break;
- udelay(10);
- }
-
- /* Set the charge pump for optimized phase noise figure */
- config &= ~TUNER_CHARGE_PUMP;
- buffer[0] = (div>>8) & 0x7f;
- buffer[1] = div & 0xff;
- buffer[2] = config;
- buffer[3] = cb;
- tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n",
- buffer[0], buffer[1], buffer[2], buffer[3]);
-
- rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4);
- if (4 != rc)
- tuner_warn("i2c i/o error: rc == %d "
- "(should be 4)\n", rc);
- break;
- }
- }
-
- return 0;
-}
-
-static int simple_radio_bandswitch(struct dvb_frontend *fe, u8 *buffer)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
-
- switch (priv->type) {
- case TUNER_TENA_9533_DI:
- case TUNER_YMEC_TVF_5533MF:
- tuner_dbg("This tuner doesn't have FM. "
- "Most cards have a TEA5767 for FM\n");
- return 0;
- case TUNER_PHILIPS_FM1216ME_MK3:
- case TUNER_PHILIPS_FM1236_MK3:
- case TUNER_PHILIPS_FMD1216ME_MK3:
- case TUNER_LG_NTSC_TAPE:
- case TUNER_PHILIPS_FM1256_IH3:
- buffer[3] = 0x19;
- break;
- case TUNER_TNF_5335MF:
- buffer[3] = 0x11;
- break;
- case TUNER_LG_PAL_FM:
- buffer[3] = 0xa5;
- break;
- case TUNER_THOMSON_DTT761X:
- buffer[3] = 0x39;
- break;
- case TUNER_MICROTUNE_4049FM5:
- default:
- buffer[3] = 0xa4;
- break;
- }
-
- return 0;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static int simple_set_tv_freq(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
- u8 config, cb;
- u16 div;
- struct tunertype *tun;
- u8 buffer[4];
- int rc, IFPCoff, i;
- enum param_type desired_type;
- struct tuner_params *t_params;
-
- tun = priv->tun;
-
- /* IFPCoff = Video Intermediate Frequency - Vif:
- 940 =16*58.75 NTSC/J (Japan)
- 732 =16*45.75 M/N STD
- 704 =16*44 ATSC (at DVB code)
- 632 =16*39.50 I U.K.
- 622.4=16*38.90 B/G D/K I, L STD
- 592 =16*37.00 D China
- 590 =16.36.875 B Australia
- 543.2=16*33.95 L' STD
- 171.2=16*10.70 FM Radio (at set_radio_freq)
- */
-
- if (params->std == V4L2_STD_NTSC_M_JP) {
- IFPCoff = 940;
- desired_type = TUNER_PARAM_TYPE_NTSC;
- } else if ((params->std & V4L2_STD_MN) &&
- !(params->std & ~V4L2_STD_MN)) {
- IFPCoff = 732;
- desired_type = TUNER_PARAM_TYPE_NTSC;
- } else if (params->std == V4L2_STD_SECAM_LC) {
- IFPCoff = 543;
- desired_type = TUNER_PARAM_TYPE_SECAM;
- } else {
- IFPCoff = 623;
- desired_type = TUNER_PARAM_TYPE_PAL;
- }
-
- t_params = simple_tuner_params(fe, desired_type);
-
- i = simple_config_lookup(fe, t_params, &params->frequency,
- &config, &cb);
-
- div = params->frequency + IFPCoff + offset;
-
- tuner_dbg("Freq= %d.%02d MHz, V_IF=%d.%02d MHz, "
- "Offset=%d.%02d MHz, div=%0d\n",
- params->frequency / 16, params->frequency % 16 * 100 / 16,
- IFPCoff / 16, IFPCoff % 16 * 100 / 16,
- offset / 16, offset % 16 * 100 / 16, div);
-
- /* tv norm specific stuff for multi-norm tuners */
- simple_std_setup(fe, params, &config, &cb);
-
- if (t_params->cb_first_if_lower_freq && div < priv->last_div) {
- buffer[0] = config;
- buffer[1] = cb;
- buffer[2] = (div>>8) & 0x7f;
- buffer[3] = div & 0xff;
- } else {
- buffer[0] = (div>>8) & 0x7f;
- buffer[1] = div & 0xff;
- buffer[2] = config;
- buffer[3] = cb;
- }
- priv->last_div = div;
- if (t_params->has_tda9887) {
- struct v4l2_priv_tun_config tda9887_cfg;
- int config = 0;
- int is_secam_l = (params->std & (V4L2_STD_SECAM_L |
- V4L2_STD_SECAM_LC)) &&
- !(params->std & ~(V4L2_STD_SECAM_L |
- V4L2_STD_SECAM_LC));
-
- tda9887_cfg.tuner = TUNER_TDA9887;
- tda9887_cfg.priv = &config;
-
- if (params->std == V4L2_STD_SECAM_LC) {
- if (t_params->port1_active ^ t_params->port1_invert_for_secam_lc)
- config |= TDA9887_PORT1_ACTIVE;
- if (t_params->port2_active ^ t_params->port2_invert_for_secam_lc)
- config |= TDA9887_PORT2_ACTIVE;
- } else {
- if (t_params->port1_active)
- config |= TDA9887_PORT1_ACTIVE;
- if (t_params->port2_active)
- config |= TDA9887_PORT2_ACTIVE;
- }
- if (t_params->intercarrier_mode)
- config |= TDA9887_INTERCARRIER;
- if (is_secam_l) {
- if (i == 0 && t_params->default_top_secam_low)
- config |= TDA9887_TOP(t_params->default_top_secam_low);
- else if (i == 1 && t_params->default_top_secam_mid)
- config |= TDA9887_TOP(t_params->default_top_secam_mid);
- else if (t_params->default_top_secam_high)
- config |= TDA9887_TOP(t_params->default_top_secam_high);
- } else {
- if (i == 0 && t_params->default_top_low)
- config |= TDA9887_TOP(t_params->default_top_low);
- else if (i == 1 && t_params->default_top_mid)
- config |= TDA9887_TOP(t_params->default_top_mid);
- else if (t_params->default_top_high)
- config |= TDA9887_TOP(t_params->default_top_high);
- }
- if (t_params->default_pll_gating_18)
- config |= TDA9887_GATING_18;
- i2c_clients_command(priv->i2c_props.adap, TUNER_SET_CONFIG,
- &tda9887_cfg);
- }
- tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n",
- buffer[0], buffer[1], buffer[2], buffer[3]);
-
- rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4);
- if (4 != rc)
- tuner_warn("i2c i/o error: rc == %d (should be 4)\n", rc);
-
- simple_post_tune(fe, &buffer[0], div, config, cb);
-
- return 0;
-}
-
-static int simple_set_radio_freq(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct tunertype *tun;
- struct tuner_simple_priv *priv = fe->tuner_priv;
- u8 buffer[4];
- u16 div;
- int rc, j;
- struct tuner_params *t_params;
- unsigned int freq = params->frequency;
-
- tun = priv->tun;
-
- for (j = tun->count-1; j > 0; j--)
- if (tun->params[j].type == TUNER_PARAM_TYPE_RADIO)
- break;
- /* default t_params (j=0) will be used if desired type wasn't found */
- t_params = &tun->params[j];
-
- /* Select Radio 1st IF used */
- switch (t_params->radio_if) {
- case 0: /* 10.7 MHz */
- freq += (unsigned int)(10.7*16000);
- break;
- case 1: /* 33.3 MHz */
- freq += (unsigned int)(33.3*16000);
- break;
- case 2: /* 41.3 MHz */
- freq += (unsigned int)(41.3*16000);
- break;
- default:
- tuner_warn("Unsupported radio_if value %d\n",
- t_params->radio_if);
- return 0;
- }
-
- /* Bandswitch byte */
- simple_radio_bandswitch(fe, &buffer[0]);
-
- buffer[2] = (t_params->ranges[0].config & ~TUNER_RATIO_MASK) |
- TUNER_RATIO_SELECT_50; /* 50 kHz step */
-
- /* Convert from 1/16 kHz V4L steps to 1/20 MHz (=50 kHz) PLL steps
- freq * (1 Mhz / 16000 V4L steps) * (20 PLL steps / 1 MHz) =
- freq * (1/800) */
- div = (freq + 400) / 800;
-
- if (t_params->cb_first_if_lower_freq && div < priv->last_div) {
- buffer[0] = buffer[2];
- buffer[1] = buffer[3];
- buffer[2] = (div>>8) & 0x7f;
- buffer[3] = div & 0xff;
- } else {
- buffer[0] = (div>>8) & 0x7f;
- buffer[1] = div & 0xff;
- }
-
- tuner_dbg("radio 0x%02x 0x%02x 0x%02x 0x%02x\n",
- buffer[0], buffer[1], buffer[2], buffer[3]);
- priv->last_div = div;
-
- if (t_params->has_tda9887) {
- int config = 0;
- struct v4l2_priv_tun_config tda9887_cfg;
-
- tda9887_cfg.tuner = TUNER_TDA9887;
- tda9887_cfg.priv = &config;
-
- if (t_params->port1_active &&
- !t_params->port1_fm_high_sensitivity)
- config |= TDA9887_PORT1_ACTIVE;
- if (t_params->port2_active &&
- !t_params->port2_fm_high_sensitivity)
- config |= TDA9887_PORT2_ACTIVE;
- if (t_params->intercarrier_mode)
- config |= TDA9887_INTERCARRIER;
-/* if (t_params->port1_set_for_fm_mono)
- config &= ~TDA9887_PORT1_ACTIVE;*/
- if (t_params->fm_gain_normal)
- config |= TDA9887_GAIN_NORMAL;
- if (t_params->radio_if == 2)
- config |= TDA9887_RIF_41_3;
- i2c_clients_command(priv->i2c_props.adap, TUNER_SET_CONFIG,
- &tda9887_cfg);
- }
- rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4);
- if (4 != rc)
- tuner_warn("i2c i/o error: rc == %d (should be 4)\n", rc);
-
- return 0;
-}
-
-static int simple_set_params(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
- int ret = -EINVAL;
-
- if (priv->i2c_props.adap == NULL)
- return -EINVAL;
-
- switch (params->mode) {
- case V4L2_TUNER_RADIO:
- ret = simple_set_radio_freq(fe, params);
- priv->frequency = params->frequency * 125 / 2;
- break;
- case V4L2_TUNER_ANALOG_TV:
- case V4L2_TUNER_DIGITAL_TV:
- ret = simple_set_tv_freq(fe, params);
- priv->frequency = params->frequency * 62500;
- break;
- }
- priv->bandwidth = 0;
-
- return ret;
-}
-
-static void simple_set_dvb(struct dvb_frontend *fe, u8 *buf,
- const struct dvb_frontend_parameters *params)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
-
- switch (priv->type) {
- case TUNER_PHILIPS_FMD1216ME_MK3:
- if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ &&
- params->frequency >= 158870000)
- buf[3] |= 0x08;
- break;
- case TUNER_PHILIPS_TD1316:
- /* determine band */
- buf[3] |= (params->frequency < 161000000) ? 1 :
- (params->frequency < 444000000) ? 2 : 4;
-
- /* setup PLL filter */
- if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ)
- buf[3] |= 1 << 3;
- break;
- case TUNER_PHILIPS_TUV1236D:
- case TUNER_PHILIPS_FCV1236D:
- {
- unsigned int new_rf;
-
- if (dtv_input[priv->nr])
- new_rf = dtv_input[priv->nr];
- else
- switch (params->u.vsb.modulation) {
- case QAM_64:
- case QAM_256:
- new_rf = 1;
- break;
- case VSB_8:
- default:
- new_rf = 0;
- break;
- }
- simple_set_rf_input(fe, &buf[2], &buf[3], new_rf);
- break;
- }
- default:
- break;
- }
-}
-
-static u32 simple_dvb_configure(struct dvb_frontend *fe, u8 *buf,
- const struct dvb_frontend_parameters *params)
-{
- /* This function returns the tuned frequency on success, 0 on error */
- struct tuner_simple_priv *priv = fe->tuner_priv;
- struct tunertype *tun = priv->tun;
- static struct tuner_params *t_params;
- u8 config, cb;
- u32 div;
- int ret, frequency = params->frequency / 62500;
-
- t_params = simple_tuner_params(fe, TUNER_PARAM_TYPE_DIGITAL);
- ret = simple_config_lookup(fe, t_params, &frequency, &config, &cb);
- if (ret < 0)
- return 0; /* failure */
-
- div = ((frequency + t_params->iffreq) * 62500 + offset +
- tun->stepsize/2) / tun->stepsize;
-
- buf[0] = div >> 8;
- buf[1] = div & 0xff;
- buf[2] = config;
- buf[3] = cb;
-
- simple_set_dvb(fe, buf, params);
-
- tuner_dbg("%s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n",
- tun->name, div, buf[0], buf[1], buf[2], buf[3]);
-
- /* calculate the frequency we set it to */
- return (div * tun->stepsize) - t_params->iffreq;
-}
-
-static int simple_dvb_calc_regs(struct dvb_frontend *fe,
- struct dvb_frontend_parameters *params,
- u8 *buf, int buf_len)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
- u32 frequency;
-
- if (buf_len < 5)
- return -EINVAL;
-
- frequency = simple_dvb_configure(fe, buf+1, params);
- if (frequency == 0)
- return -EINVAL;
-
- buf[0] = priv->i2c_props.addr;
-
- priv->frequency = frequency;
- priv->bandwidth = (fe->ops.info.type == FE_OFDM) ?
- params->u.ofdm.bandwidth : 0;
-
- return 5;
-}
-
-static int simple_dvb_set_params(struct dvb_frontend *fe,
- struct dvb_frontend_parameters *params)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
- u32 prev_freq, prev_bw;
- int ret;
- u8 buf[5];
-
- if (priv->i2c_props.adap == NULL)
- return -EINVAL;
-
- prev_freq = priv->frequency;
- prev_bw = priv->bandwidth;
-
- ret = simple_dvb_calc_regs(fe, params, buf, 5);
- if (ret != 5)
- goto fail;
-
- /* put analog demod in standby when tuning digital */
- if (fe->ops.analog_ops.standby)
- fe->ops.analog_ops.standby(fe);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
-
- /* buf[0] contains the i2c address, but *
- * we already have it in i2c_props.addr */
- ret = tuner_i2c_xfer_send(&priv->i2c_props, buf+1, 4);
- if (ret != 4)
- goto fail;
-
- return 0;
-fail:
- /* calc_regs sets frequency and bandwidth. if we failed, unset them */
- priv->frequency = prev_freq;
- priv->bandwidth = prev_bw;
-
- return ret;
-}
-
-static int simple_init(struct dvb_frontend *fe)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
-
- if (priv->i2c_props.adap == NULL)
- return -EINVAL;
-
- if (priv->tun->initdata) {
- int ret;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
-
- ret = tuner_i2c_xfer_send(&priv->i2c_props,
- priv->tun->initdata + 1,
- priv->tun->initdata[0]);
- if (ret != priv->tun->initdata[0])
- return ret;
- }
-
- return 0;
-}
-
-static int simple_sleep(struct dvb_frontend *fe)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
-
- if (priv->i2c_props.adap == NULL)
- return -EINVAL;
-
- if (priv->tun->sleepdata) {
- int ret;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
-
- ret = tuner_i2c_xfer_send(&priv->i2c_props,
- priv->tun->sleepdata + 1,
- priv->tun->sleepdata[0]);
- if (ret != priv->tun->sleepdata[0])
- return ret;
- }
-
- return 0;
-}
-
-static int simple_release(struct dvb_frontend *fe)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
-
- mutex_lock(&tuner_simple_list_mutex);
-
- if (priv)
- hybrid_tuner_release_state(priv);
-
- mutex_unlock(&tuner_simple_list_mutex);
-
- fe->tuner_priv = NULL;
-
- return 0;
-}
-
-static int simple_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
- *frequency = priv->frequency;
- return 0;
-}
-
-static int simple_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
-{
- struct tuner_simple_priv *priv = fe->tuner_priv;
- *bandwidth = priv->bandwidth;
- return 0;
-}
-
-static struct dvb_tuner_ops simple_tuner_ops = {
- .init = simple_init,
- .sleep = simple_sleep,
- .set_analog_params = simple_set_params,
- .set_params = simple_dvb_set_params,
- .calc_regs = simple_dvb_calc_regs,
- .release = simple_release,
- .get_frequency = simple_get_frequency,
- .get_bandwidth = simple_get_bandwidth,
- .get_status = simple_get_status,
- .get_rf_strength = simple_get_rf_strength,
-};
-
-struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c_adap,
- u8 i2c_addr,
- unsigned int type)
-{
- struct tuner_simple_priv *priv = NULL;
- int instance;
-
- if (type >= tuner_count) {
- printk(KERN_WARNING "%s: invalid tuner type: %d (max: %d)\n",
- __func__, type, tuner_count-1);
- return NULL;
- }
-
- /* If i2c_adap is set, check that the tuner is at the correct address.
- * Otherwise, if i2c_adap is NULL, the tuner will be programmed directly
- * by the digital demod via calc_regs.
- */
- if (i2c_adap != NULL) {
- u8 b[1];
- struct i2c_msg msg = {
- .addr = i2c_addr, .flags = I2C_M_RD,
- .buf = b, .len = 1,
- };
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
-
- if (1 != i2c_transfer(i2c_adap, &msg, 1))
- tuner_warn("unable to probe %s, proceeding anyway.",
- tuners[type].name);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
- }
-
- mutex_lock(&tuner_simple_list_mutex);
-
- instance = hybrid_tuner_request_state(struct tuner_simple_priv, priv,
- hybrid_tuner_instance_list,
- i2c_adap, i2c_addr,
- "tuner-simple");
- switch (instance) {
- case 0:
- mutex_unlock(&tuner_simple_list_mutex);
- return NULL;
- break;
- case 1:
- fe->tuner_priv = priv;
-
- priv->type = type;
- priv->tun = &tuners[type];
- priv->nr = simple_devcount++;
- break;
- default:
- fe->tuner_priv = priv;
- break;
- }
-
- mutex_unlock(&tuner_simple_list_mutex);
-
- memcpy(&fe->ops.tuner_ops, &simple_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- tuner_info("type set to %d (%s)\n", type, priv->tun->name);
-
- if ((debug) || ((atv_input[priv->nr] > 0) ||
- (dtv_input[priv->nr] > 0))) {
- if (0 == atv_input[priv->nr])
- tuner_info("tuner %d atv rf input will be "
- "autoselected\n", priv->nr);
- else
- tuner_info("tuner %d atv rf input will be "
- "set to input %d (insmod option)\n",
- priv->nr, atv_input[priv->nr]);
- if (0 == dtv_input[priv->nr])
- tuner_info("tuner %d dtv rf input will be "
- "autoselected\n", priv->nr);
- else
- tuner_info("tuner %d dtv rf input will be "
- "set to input %d (insmod option)\n",
- priv->nr, dtv_input[priv->nr]);
- }
-
- strlcpy(fe->ops.tuner_ops.info.name, priv->tun->name,
- sizeof(fe->ops.tuner_ops.info.name));
-
- return fe;
-}
-EXPORT_SYMBOL_GPL(simple_tuner_attach);
-
-MODULE_DESCRIPTION("Simple 4-control-bytes style tuner driver");
-MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
-MODULE_LICENSE("GPL");
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/tuner-simple.h b/drivers/media/video/tuner-simple.h
deleted file mode 100644
index e46cf0121e0..00000000000
--- a/drivers/media/video/tuner-simple.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- 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 __TUNER_SIMPLE_H__
-#define __TUNER_SIMPLE_H__
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-
-#if defined(CONFIG_TUNER_SIMPLE) || (defined(CONFIG_TUNER_SIMPLE_MODULE) && defined(MODULE))
-extern struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c_adap,
- u8 i2c_addr,
- unsigned int type);
-#else
-static inline struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c_adap,
- u8 i2c_addr,
- unsigned int type)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif /* __TUNER_SIMPLE_H__ */
diff --git a/drivers/media/video/tuner-types.c b/drivers/media/video/tuner-types.c
deleted file mode 100644
index 10dddca8b5d..00000000000
--- a/drivers/media/video/tuner-types.c
+++ /dev/null
@@ -1,1652 +0,0 @@
-/*
- *
- * i2c tv tuner chip device type database.
- *
- */
-
-#include <linux/i2c.h>
-#include <media/tuner.h>
-#include <media/tuner-types.h>
-
-/* ---------------------------------------------------------------------- */
-
-/*
- * The floats in the tuner struct are computed at compile time
- * by gcc and cast back to integers. Thus we don't violate the
- * "no float in kernel" rule.
- *
- * A tuner_range may be referenced by multiple tuner_params structs.
- * There are many duplicates in here. Reusing tuner_range structs,
- * rather than defining new ones for each tuner, will cut down on
- * memory usage, and is preferred when possible.
- *
- * Each tuner_params array may contain one or more elements, one
- * for each video standard.
- *
- * FIXME: tuner_params struct contains an element, tda988x. We must
- * set this for all tuners that contain a tda988x chip, and then we
- * can remove this setting from the various card structs.
- *
- * FIXME: Right now, all tuners are using the first tuner_params[]
- * array element for analog mode. In the future, we will be merging
- * similar tuner definitions together, such that each tuner definition
- * will have a tuner_params struct for each available video standard.
- * At that point, the tuner_params[] array element will be chosen
- * based on the video standard in use.
- */
-
-/* The following was taken from dvb-pll.c: */
-
-/* Set AGC TOP value to 103 dBuV:
- * 0x80 = Control Byte
- * 0x40 = 250 uA charge pump (irrelevant)
- * 0x18 = Aux Byte to follow
- * 0x06 = 64.5 kHz divider (irrelevant)
- * 0x01 = Disable Vt (aka sleep)
- *
- * 0x00 = AGC Time constant 2s Iagc = 300 nA (vs 0x80 = 9 nA)
- * 0x50 = AGC Take over point = 103 dBuV
- */
-static u8 tua603x_agc103[] = { 2, 0x80|0x40|0x18|0x06|0x01, 0x00|0x50 };
-
-/* 0x04 = 166.67 kHz divider
- *
- * 0x80 = AGC Time constant 50ms Iagc = 9 uA
- * 0x20 = AGC Take over point = 112 dBuV
- */
-static u8 tua603x_agc112[] = { 2, 0x80|0x40|0x18|0x04|0x01, 0x80|0x20 };
-
-/* 0-9 */
-/* ------------ TUNER_TEMIC_PAL - TEMIC PAL ------------ */
-
-static struct tuner_range tuner_temic_pal_ranges[] = {
- { 16 * 140.25 /*MHz*/, 0x8e, 0x02, },
- { 16 * 463.25 /*MHz*/, 0x8e, 0x04, },
- { 16 * 999.99 , 0x8e, 0x01, },
-};
-
-static struct tuner_params tuner_temic_pal_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_temic_pal_ranges,
- .count = ARRAY_SIZE(tuner_temic_pal_ranges),
- },
-};
-
-/* ------------ TUNER_PHILIPS_PAL_I - Philips PAL_I ------------ */
-
-static struct tuner_range tuner_philips_pal_i_ranges[] = {
- { 16 * 140.25 /*MHz*/, 0x8e, 0xa0, },
- { 16 * 463.25 /*MHz*/, 0x8e, 0x90, },
- { 16 * 999.99 , 0x8e, 0x30, },
-};
-
-static struct tuner_params tuner_philips_pal_i_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_philips_pal_i_ranges,
- .count = ARRAY_SIZE(tuner_philips_pal_i_ranges),
- },
-};
-
-/* ------------ TUNER_PHILIPS_NTSC - Philips NTSC ------------ */
-
-static struct tuner_range tuner_philips_ntsc_ranges[] = {
- { 16 * 157.25 /*MHz*/, 0x8e, 0xa0, },
- { 16 * 451.25 /*MHz*/, 0x8e, 0x90, },
- { 16 * 999.99 , 0x8e, 0x30, },
-};
-
-static struct tuner_params tuner_philips_ntsc_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_philips_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_philips_ntsc_ranges),
- .cb_first_if_lower_freq = 1,
- },
-};
-
-/* ------------ TUNER_PHILIPS_SECAM - Philips SECAM ------------ */
-
-static struct tuner_range tuner_philips_secam_ranges[] = {
- { 16 * 168.25 /*MHz*/, 0x8e, 0xa7, },
- { 16 * 447.25 /*MHz*/, 0x8e, 0x97, },
- { 16 * 999.99 , 0x8e, 0x37, },
-};
-
-static struct tuner_params tuner_philips_secam_params[] = {
- {
- .type = TUNER_PARAM_TYPE_SECAM,
- .ranges = tuner_philips_secam_ranges,
- .count = ARRAY_SIZE(tuner_philips_secam_ranges),
- .cb_first_if_lower_freq = 1,
- },
-};
-
-/* ------------ TUNER_PHILIPS_PAL - Philips PAL ------------ */
-
-static struct tuner_range tuner_philips_pal_ranges[] = {
- { 16 * 168.25 /*MHz*/, 0x8e, 0xa0, },
- { 16 * 447.25 /*MHz*/, 0x8e, 0x90, },
- { 16 * 999.99 , 0x8e, 0x30, },
-};
-
-static struct tuner_params tuner_philips_pal_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_philips_pal_ranges,
- .count = ARRAY_SIZE(tuner_philips_pal_ranges),
- .cb_first_if_lower_freq = 1,
- },
-};
-
-/* ------------ TUNER_TEMIC_NTSC - TEMIC NTSC ------------ */
-
-static struct tuner_range tuner_temic_ntsc_ranges[] = {
- { 16 * 157.25 /*MHz*/, 0x8e, 0x02, },
- { 16 * 463.25 /*MHz*/, 0x8e, 0x04, },
- { 16 * 999.99 , 0x8e, 0x01, },
-};
-
-static struct tuner_params tuner_temic_ntsc_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_temic_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_temic_ntsc_ranges),
- },
-};
-
-/* ------------ TUNER_TEMIC_PAL_I - TEMIC PAL_I ------------ */
-
-static struct tuner_range tuner_temic_pal_i_ranges[] = {
- { 16 * 170.00 /*MHz*/, 0x8e, 0x02, },
- { 16 * 450.00 /*MHz*/, 0x8e, 0x04, },
- { 16 * 999.99 , 0x8e, 0x01, },
-};
-
-static struct tuner_params tuner_temic_pal_i_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_temic_pal_i_ranges,
- .count = ARRAY_SIZE(tuner_temic_pal_i_ranges),
- },
-};
-
-/* ------------ TUNER_TEMIC_4036FY5_NTSC - TEMIC NTSC ------------ */
-
-static struct tuner_range tuner_temic_4036fy5_ntsc_ranges[] = {
- { 16 * 157.25 /*MHz*/, 0x8e, 0xa0, },
- { 16 * 463.25 /*MHz*/, 0x8e, 0x90, },
- { 16 * 999.99 , 0x8e, 0x30, },
-};
-
-static struct tuner_params tuner_temic_4036fy5_ntsc_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_temic_4036fy5_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_temic_4036fy5_ntsc_ranges),
- },
-};
-
-/* ------------ TUNER_ALPS_TSBH1_NTSC - TEMIC NTSC ------------ */
-
-static struct tuner_range tuner_alps_tsb_1_ranges[] = {
- { 16 * 137.25 /*MHz*/, 0x8e, 0x01, },
- { 16 * 385.25 /*MHz*/, 0x8e, 0x02, },
- { 16 * 999.99 , 0x8e, 0x08, },
-};
-
-static struct tuner_params tuner_alps_tsbh1_ntsc_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_alps_tsb_1_ranges,
- .count = ARRAY_SIZE(tuner_alps_tsb_1_ranges),
- },
-};
-
-/* 10-19 */
-/* ------------ TUNER_ALPS_TSBE1_PAL - TEMIC PAL ------------ */
-
-static struct tuner_params tuner_alps_tsb_1_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_alps_tsb_1_ranges,
- .count = ARRAY_SIZE(tuner_alps_tsb_1_ranges),
- },
-};
-
-/* ------------ TUNER_ALPS_TSBB5_PAL_I - Alps PAL_I ------------ */
-
-static struct tuner_range tuner_alps_tsb_5_pal_ranges[] = {
- { 16 * 133.25 /*MHz*/, 0x8e, 0x01, },
- { 16 * 351.25 /*MHz*/, 0x8e, 0x02, },
- { 16 * 999.99 , 0x8e, 0x08, },
-};
-
-static struct tuner_params tuner_alps_tsbb5_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_alps_tsb_5_pal_ranges,
- .count = ARRAY_SIZE(tuner_alps_tsb_5_pal_ranges),
- },
-};
-
-/* ------------ TUNER_ALPS_TSBE5_PAL - Alps PAL ------------ */
-
-static struct tuner_params tuner_alps_tsbe5_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_alps_tsb_5_pal_ranges,
- .count = ARRAY_SIZE(tuner_alps_tsb_5_pal_ranges),
- },
-};
-
-/* ------------ TUNER_ALPS_TSBC5_PAL - Alps PAL ------------ */
-
-static struct tuner_params tuner_alps_tsbc5_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_alps_tsb_5_pal_ranges,
- .count = ARRAY_SIZE(tuner_alps_tsb_5_pal_ranges),
- },
-};
-
-/* ------------ TUNER_TEMIC_4006FH5_PAL - TEMIC PAL ------------ */
-
-static struct tuner_range tuner_lg_pal_ranges[] = {
- { 16 * 170.00 /*MHz*/, 0x8e, 0xa0, },
- { 16 * 450.00 /*MHz*/, 0x8e, 0x90, },
- { 16 * 999.99 , 0x8e, 0x30, },
-};
-
-static struct tuner_params tuner_temic_4006fh5_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_lg_pal_ranges,
- .count = ARRAY_SIZE(tuner_lg_pal_ranges),
- },
-};
-
-/* ------------ TUNER_ALPS_TSHC6_NTSC - Alps NTSC ------------ */
-
-static struct tuner_range tuner_alps_tshc6_ntsc_ranges[] = {
- { 16 * 137.25 /*MHz*/, 0x8e, 0x14, },
- { 16 * 385.25 /*MHz*/, 0x8e, 0x12, },
- { 16 * 999.99 , 0x8e, 0x11, },
-};
-
-static struct tuner_params tuner_alps_tshc6_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_alps_tshc6_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_alps_tshc6_ntsc_ranges),
- },
-};
-
-/* ------------ TUNER_TEMIC_PAL_DK - TEMIC PAL ------------ */
-
-static struct tuner_range tuner_temic_pal_dk_ranges[] = {
- { 16 * 168.25 /*MHz*/, 0x8e, 0xa0, },
- { 16 * 456.25 /*MHz*/, 0x8e, 0x90, },
- { 16 * 999.99 , 0x8e, 0x30, },
-};
-
-static struct tuner_params tuner_temic_pal_dk_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_temic_pal_dk_ranges,
- .count = ARRAY_SIZE(tuner_temic_pal_dk_ranges),
- },
-};
-
-/* ------------ TUNER_PHILIPS_NTSC_M - Philips NTSC ------------ */
-
-static struct tuner_range tuner_philips_ntsc_m_ranges[] = {
- { 16 * 160.00 /*MHz*/, 0x8e, 0xa0, },
- { 16 * 454.00 /*MHz*/, 0x8e, 0x90, },
- { 16 * 999.99 , 0x8e, 0x30, },
-};
-
-static struct tuner_params tuner_philips_ntsc_m_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_philips_ntsc_m_ranges,
- .count = ARRAY_SIZE(tuner_philips_ntsc_m_ranges),
- },
-};
-
-/* ------------ TUNER_TEMIC_4066FY5_PAL_I - TEMIC PAL_I ------------ */
-
-static struct tuner_range tuner_temic_40x6f_5_pal_ranges[] = {
- { 16 * 169.00 /*MHz*/, 0x8e, 0xa0, },
- { 16 * 454.00 /*MHz*/, 0x8e, 0x90, },
- { 16 * 999.99 , 0x8e, 0x30, },
-};
-
-static struct tuner_params tuner_temic_4066fy5_pal_i_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_temic_40x6f_5_pal_ranges,
- .count = ARRAY_SIZE(tuner_temic_40x6f_5_pal_ranges),
- },
-};
-
-/* ------------ TUNER_TEMIC_4006FN5_MULTI_PAL - TEMIC PAL ------------ */
-
-static struct tuner_params tuner_temic_4006fn5_multi_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_temic_40x6f_5_pal_ranges,
- .count = ARRAY_SIZE(tuner_temic_40x6f_5_pal_ranges),
- },
-};
-
-/* 20-29 */
-/* ------------ TUNER_TEMIC_4009FR5_PAL - TEMIC PAL ------------ */
-
-static struct tuner_range tuner_temic_4009f_5_pal_ranges[] = {
- { 16 * 141.00 /*MHz*/, 0x8e, 0xa0, },
- { 16 * 464.00 /*MHz*/, 0x8e, 0x90, },
- { 16 * 999.99 , 0x8e, 0x30, },
-};
-
-static struct tuner_params tuner_temic_4009f_5_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_temic_4009f_5_pal_ranges,
- .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges),
- },
-};
-
-/* ------------ TUNER_TEMIC_4039FR5_NTSC - TEMIC NTSC ------------ */
-
-static struct tuner_range tuner_temic_4x3x_f_5_ntsc_ranges[] = {
- { 16 * 158.00 /*MHz*/, 0x8e, 0xa0, },
- { 16 * 453.00 /*MHz*/, 0x8e, 0x90, },
- { 16 * 999.99 , 0x8e, 0x30, },
-};
-
-static struct tuner_params tuner_temic_4039fr5_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_temic_4x3x_f_5_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_temic_4x3x_f_5_ntsc_ranges),
- },
-};
-
-/* ------------ TUNER_TEMIC_4046FM5 - TEMIC PAL ------------ */
-
-static struct tuner_params tuner_temic_4046fm5_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_temic_40x6f_5_pal_ranges,
- .count = ARRAY_SIZE(tuner_temic_40x6f_5_pal_ranges),
- },
-};
-
-/* ------------ TUNER_PHILIPS_PAL_DK - Philips PAL ------------ */
-
-static struct tuner_params tuner_philips_pal_dk_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_lg_pal_ranges,
- .count = ARRAY_SIZE(tuner_lg_pal_ranges),
- },
-};
-
-/* ------------ TUNER_PHILIPS_FQ1216ME - Philips PAL ------------ */
-
-static struct tuner_params tuner_philips_fq1216me_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_lg_pal_ranges,
- .count = ARRAY_SIZE(tuner_lg_pal_ranges),
- .has_tda9887 = 1,
- .port1_active = 1,
- .port2_active = 1,
- .port2_invert_for_secam_lc = 1,
- },
-};
-
-/* ------------ TUNER_LG_PAL_I_FM - LGINNOTEK PAL_I ------------ */
-
-static struct tuner_params tuner_lg_pal_i_fm_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_lg_pal_ranges,
- .count = ARRAY_SIZE(tuner_lg_pal_ranges),
- },
-};
-
-/* ------------ TUNER_LG_PAL_I - LGINNOTEK PAL_I ------------ */
-
-static struct tuner_params tuner_lg_pal_i_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_lg_pal_ranges,
- .count = ARRAY_SIZE(tuner_lg_pal_ranges),
- },
-};
-
-/* ------------ TUNER_LG_NTSC_FM - LGINNOTEK NTSC ------------ */
-
-static struct tuner_range tuner_lg_ntsc_fm_ranges[] = {
- { 16 * 210.00 /*MHz*/, 0x8e, 0xa0, },
- { 16 * 497.00 /*MHz*/, 0x8e, 0x90, },
- { 16 * 999.99 , 0x8e, 0x30, },
-};
-
-static struct tuner_params tuner_lg_ntsc_fm_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_lg_ntsc_fm_ranges,
- .count = ARRAY_SIZE(tuner_lg_ntsc_fm_ranges),
- },
-};
-
-/* ------------ TUNER_LG_PAL_FM - LGINNOTEK PAL ------------ */
-
-static struct tuner_params tuner_lg_pal_fm_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_lg_pal_ranges,
- .count = ARRAY_SIZE(tuner_lg_pal_ranges),
- },
-};
-
-/* ------------ TUNER_LG_PAL - LGINNOTEK PAL ------------ */
-
-static struct tuner_params tuner_lg_pal_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_lg_pal_ranges,
- .count = ARRAY_SIZE(tuner_lg_pal_ranges),
- },
-};
-
-/* 30-39 */
-/* ------------ TUNER_TEMIC_4009FN5_MULTI_PAL_FM - TEMIC PAL ------------ */
-
-static struct tuner_params tuner_temic_4009_fn5_multi_pal_fm_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_temic_4009f_5_pal_ranges,
- .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges),
- },
-};
-
-/* ------------ TUNER_SHARP_2U5JF5540_NTSC - SHARP NTSC ------------ */
-
-static struct tuner_range tuner_sharp_2u5jf5540_ntsc_ranges[] = {
- { 16 * 137.25 /*MHz*/, 0x8e, 0x01, },
- { 16 * 317.25 /*MHz*/, 0x8e, 0x02, },
- { 16 * 999.99 , 0x8e, 0x08, },
-};
-
-static struct tuner_params tuner_sharp_2u5jf5540_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_sharp_2u5jf5540_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_sharp_2u5jf5540_ntsc_ranges),
- },
-};
-
-/* ------------ TUNER_Samsung_PAL_TCPM9091PD27 - Samsung PAL ------------ */
-
-static struct tuner_range tuner_samsung_pal_tcpm9091pd27_ranges[] = {
- { 16 * 169 /*MHz*/, 0x8e, 0xa0, },
- { 16 * 464 /*MHz*/, 0x8e, 0x90, },
- { 16 * 999.99 , 0x8e, 0x30, },
-};
-
-static struct tuner_params tuner_samsung_pal_tcpm9091pd27_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_samsung_pal_tcpm9091pd27_ranges,
- .count = ARRAY_SIZE(tuner_samsung_pal_tcpm9091pd27_ranges),
- },
-};
-
-/* ------------ TUNER_TEMIC_4106FH5 - TEMIC PAL ------------ */
-
-static struct tuner_params tuner_temic_4106fh5_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_temic_4009f_5_pal_ranges,
- .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges),
- },
-};
-
-/* ------------ TUNER_TEMIC_4012FY5 - TEMIC PAL ------------ */
-
-static struct tuner_params tuner_temic_4012fy5_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_temic_pal_ranges,
- .count = ARRAY_SIZE(tuner_temic_pal_ranges),
- },
-};
-
-/* ------------ TUNER_TEMIC_4136FY5 - TEMIC NTSC ------------ */
-
-static struct tuner_params tuner_temic_4136_fy5_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_temic_4x3x_f_5_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_temic_4x3x_f_5_ntsc_ranges),
- },
-};
-
-/* ------------ TUNER_LG_PAL_NEW_TAPC - LGINNOTEK PAL ------------ */
-
-static struct tuner_range tuner_lg_new_tapc_ranges[] = {
- { 16 * 170.00 /*MHz*/, 0x8e, 0x01, },
- { 16 * 450.00 /*MHz*/, 0x8e, 0x02, },
- { 16 * 999.99 , 0x8e, 0x08, },
-};
-
-static struct tuner_params tuner_lg_pal_new_tapc_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_lg_new_tapc_ranges,
- .count = ARRAY_SIZE(tuner_lg_new_tapc_ranges),
- },
-};
-
-/* ------------ TUNER_PHILIPS_FM1216ME_MK3 - Philips PAL ------------ */
-
-static struct tuner_range tuner_fm1216me_mk3_pal_ranges[] = {
- { 16 * 158.00 /*MHz*/, 0x8e, 0x01, },
- { 16 * 442.00 /*MHz*/, 0x8e, 0x02, },
- { 16 * 999.99 , 0x8e, 0x04, },
-};
-
-static struct tuner_params tuner_fm1216me_mk3_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_fm1216me_mk3_pal_ranges,
- .count = ARRAY_SIZE(tuner_fm1216me_mk3_pal_ranges),
- .cb_first_if_lower_freq = 1,
- .has_tda9887 = 1,
- .port1_active = 1,
- .port2_active = 1,
- .port2_invert_for_secam_lc = 1,
- .port1_fm_high_sensitivity = 1,
- .default_top_mid = -2,
- .default_top_secam_mid = -2,
- .default_top_secam_high = -2,
- },
-};
-
-/* ------------ TUNER_LG_NTSC_NEW_TAPC - LGINNOTEK NTSC ------------ */
-
-static struct tuner_params tuner_lg_ntsc_new_tapc_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_lg_new_tapc_ranges,
- .count = ARRAY_SIZE(tuner_lg_new_tapc_ranges),
- },
-};
-
-/* 40-49 */
-/* ------------ TUNER_HITACHI_NTSC - HITACHI NTSC ------------ */
-
-static struct tuner_params tuner_hitachi_ntsc_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_lg_new_tapc_ranges,
- .count = ARRAY_SIZE(tuner_lg_new_tapc_ranges),
- },
-};
-
-/* ------------ TUNER_PHILIPS_PAL_MK - Philips PAL ------------ */
-
-static struct tuner_range tuner_philips_pal_mk_pal_ranges[] = {
- { 16 * 140.25 /*MHz*/, 0x8e, 0x01, },
- { 16 * 463.25 /*MHz*/, 0x8e, 0xc2, },
- { 16 * 999.99 , 0x8e, 0xcf, },
-};
-
-static struct tuner_params tuner_philips_pal_mk_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_philips_pal_mk_pal_ranges,
- .count = ARRAY_SIZE(tuner_philips_pal_mk_pal_ranges),
- },
-};
-
-/* ---- TUNER_PHILIPS_FCV1236D - Philips FCV1236D (ATSC/NTSC) ---- */
-
-static struct tuner_range tuner_philips_fcv1236d_ntsc_ranges[] = {
- { 16 * 157.25 /*MHz*/, 0x8e, 0xa2, },
- { 16 * 451.25 /*MHz*/, 0x8e, 0x92, },
- { 16 * 999.99 , 0x8e, 0x32, },
-};
-
-static struct tuner_range tuner_philips_fcv1236d_atsc_ranges[] = {
- { 16 * 159.00 /*MHz*/, 0x8e, 0xa0, },
- { 16 * 453.00 /*MHz*/, 0x8e, 0x90, },
- { 16 * 999.99 , 0x8e, 0x30, },
-};
-
-static struct tuner_params tuner_philips_fcv1236d_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_philips_fcv1236d_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_philips_fcv1236d_ntsc_ranges),
- },
- {
- .type = TUNER_PARAM_TYPE_DIGITAL,
- .ranges = tuner_philips_fcv1236d_atsc_ranges,
- .count = ARRAY_SIZE(tuner_philips_fcv1236d_atsc_ranges),
- .iffreq = 16 * 44.00,
- },
-};
-
-/* ------------ TUNER_PHILIPS_FM1236_MK3 - Philips NTSC ------------ */
-
-static struct tuner_range tuner_fm1236_mk3_ntsc_ranges[] = {
- { 16 * 160.00 /*MHz*/, 0x8e, 0x01, },
- { 16 * 442.00 /*MHz*/, 0x8e, 0x02, },
- { 16 * 999.99 , 0x8e, 0x04, },
-};
-
-static struct tuner_params tuner_fm1236_mk3_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_fm1236_mk3_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges),
- .cb_first_if_lower_freq = 1,
- .has_tda9887 = 1,
- .port1_active = 1,
- .port2_active = 1,
- .port1_fm_high_sensitivity = 1,
- },
-};
-
-/* ------------ TUNER_PHILIPS_4IN1 - Philips NTSC ------------ */
-
-static struct tuner_params tuner_philips_4in1_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_fm1236_mk3_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges),
- },
-};
-
-/* ------------ TUNER_MICROTUNE_4049FM5 - Microtune PAL ------------ */
-
-static struct tuner_params tuner_microtune_4049_fm5_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_temic_4009f_5_pal_ranges,
- .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges),
- .has_tda9887 = 1,
- .port1_invert_for_secam_lc = 1,
- .default_pll_gating_18 = 1,
- .fm_gain_normal=1,
- .radio_if = 1, /* 33.3 MHz */
- },
-};
-
-/* ------------ TUNER_PANASONIC_VP27 - Panasonic NTSC ------------ */
-
-static struct tuner_range tuner_panasonic_vp27_ntsc_ranges[] = {
- { 16 * 160.00 /*MHz*/, 0xce, 0x01, },
- { 16 * 454.00 /*MHz*/, 0xce, 0x02, },
- { 16 * 999.99 , 0xce, 0x08, },
-};
-
-static struct tuner_params tuner_panasonic_vp27_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_panasonic_vp27_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_panasonic_vp27_ntsc_ranges),
- .has_tda9887 = 1,
- .intercarrier_mode = 1,
- .default_top_low = -3,
- .default_top_mid = -3,
- .default_top_high = -3,
- },
-};
-
-/* ------------ TUNER_TNF_8831BGFF - Philips PAL ------------ */
-
-static struct tuner_range tuner_tnf_8831bgff_pal_ranges[] = {
- { 16 * 161.25 /*MHz*/, 0x8e, 0xa0, },
- { 16 * 463.25 /*MHz*/, 0x8e, 0x90, },
- { 16 * 999.99 , 0x8e, 0x30, },
-};
-
-static struct tuner_params tuner_tnf_8831bgff_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_tnf_8831bgff_pal_ranges,
- .count = ARRAY_SIZE(tuner_tnf_8831bgff_pal_ranges),
- },
-};
-
-/* ------------ TUNER_MICROTUNE_4042FI5 - Microtune NTSC ------------ */
-
-static struct tuner_range tuner_microtune_4042fi5_ntsc_ranges[] = {
- { 16 * 162.00 /*MHz*/, 0x8e, 0xa2, },
- { 16 * 457.00 /*MHz*/, 0x8e, 0x94, },
- { 16 * 999.99 , 0x8e, 0x31, },
-};
-
-static struct tuner_range tuner_microtune_4042fi5_atsc_ranges[] = {
- { 16 * 162.00 /*MHz*/, 0x8e, 0xa1, },
- { 16 * 457.00 /*MHz*/, 0x8e, 0x91, },
- { 16 * 999.99 , 0x8e, 0x31, },
-};
-
-static struct tuner_params tuner_microtune_4042fi5_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_microtune_4042fi5_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_microtune_4042fi5_ntsc_ranges),
- },
- {
- .type = TUNER_PARAM_TYPE_DIGITAL,
- .ranges = tuner_microtune_4042fi5_atsc_ranges,
- .count = ARRAY_SIZE(tuner_microtune_4042fi5_atsc_ranges),
- .iffreq = 16 * 44.00 /*MHz*/,
- },
-};
-
-/* 50-59 */
-/* ------------ TUNER_TCL_2002N - TCL NTSC ------------ */
-
-static struct tuner_range tuner_tcl_2002n_ntsc_ranges[] = {
- { 16 * 172.00 /*MHz*/, 0x8e, 0x01, },
- { 16 * 448.00 /*MHz*/, 0x8e, 0x02, },
- { 16 * 999.99 , 0x8e, 0x08, },
-};
-
-static struct tuner_params tuner_tcl_2002n_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_tcl_2002n_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_tcl_2002n_ntsc_ranges),
- .cb_first_if_lower_freq = 1,
- },
-};
-
-/* ------------ TUNER_PHILIPS_FM1256_IH3 - Philips PAL ------------ */
-
-static struct tuner_params tuner_philips_fm1256_ih3_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_fm1236_mk3_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges),
- .radio_if = 1, /* 33.3 MHz */
- },
-};
-
-/* ------------ TUNER_THOMSON_DTT7610 - THOMSON ATSC ------------ */
-
-/* single range used for both ntsc and atsc */
-static struct tuner_range tuner_thomson_dtt7610_ntsc_ranges[] = {
- { 16 * 157.25 /*MHz*/, 0x8e, 0x39, },
- { 16 * 454.00 /*MHz*/, 0x8e, 0x3a, },
- { 16 * 999.99 , 0x8e, 0x3c, },
-};
-
-static struct tuner_params tuner_thomson_dtt7610_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_thomson_dtt7610_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_thomson_dtt7610_ntsc_ranges),
- },
- {
- .type = TUNER_PARAM_TYPE_DIGITAL,
- .ranges = tuner_thomson_dtt7610_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_thomson_dtt7610_ntsc_ranges),
- .iffreq = 16 * 44.00 /*MHz*/,
- },
-};
-
-/* ------------ TUNER_PHILIPS_FQ1286 - Philips NTSC ------------ */
-
-static struct tuner_range tuner_philips_fq1286_ntsc_ranges[] = {
- { 16 * 160.00 /*MHz*/, 0x8e, 0x41, },
- { 16 * 454.00 /*MHz*/, 0x8e, 0x42, },
- { 16 * 999.99 , 0x8e, 0x04, },
-};
-
-static struct tuner_params tuner_philips_fq1286_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_philips_fq1286_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_philips_fq1286_ntsc_ranges),
- },
-};
-
-/* ------------ TUNER_TCL_2002MB - TCL PAL ------------ */
-
-static struct tuner_range tuner_tcl_2002mb_pal_ranges[] = {
- { 16 * 170.00 /*MHz*/, 0xce, 0x01, },
- { 16 * 450.00 /*MHz*/, 0xce, 0x02, },
- { 16 * 999.99 , 0xce, 0x08, },
-};
-
-static struct tuner_params tuner_tcl_2002mb_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_tcl_2002mb_pal_ranges,
- .count = ARRAY_SIZE(tuner_tcl_2002mb_pal_ranges),
- },
-};
-
-/* ------------ TUNER_PHILIPS_FQ1216AME_MK4 - Philips PAL ------------ */
-
-static struct tuner_range tuner_philips_fq12_6a___mk4_pal_ranges[] = {
- { 16 * 160.00 /*MHz*/, 0xce, 0x01, },
- { 16 * 442.00 /*MHz*/, 0xce, 0x02, },
- { 16 * 999.99 , 0xce, 0x04, },
-};
-
-static struct tuner_params tuner_philips_fq1216ame_mk4_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_philips_fq12_6a___mk4_pal_ranges,
- .count = ARRAY_SIZE(tuner_philips_fq12_6a___mk4_pal_ranges),
- .has_tda9887 = 1,
- .port1_active = 1,
- .port2_invert_for_secam_lc = 1,
- .default_top_mid = -2,
- .default_top_secam_low = -2,
- .default_top_secam_mid = -2,
- .default_top_secam_high = -2,
- },
-};
-
-/* ------------ TUNER_PHILIPS_FQ1236A_MK4 - Philips NTSC ------------ */
-
-static struct tuner_params tuner_philips_fq1236a_mk4_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_fm1236_mk3_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges),
- },
-};
-
-/* ------------ TUNER_YMEC_TVF_8531MF - Philips NTSC ------------ */
-
-static struct tuner_params tuner_ymec_tvf_8531mf_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_philips_ntsc_m_ranges,
- .count = ARRAY_SIZE(tuner_philips_ntsc_m_ranges),
- },
-};
-
-/* ------------ TUNER_YMEC_TVF_5533MF - Philips NTSC ------------ */
-
-static struct tuner_range tuner_ymec_tvf_5533mf_ntsc_ranges[] = {
- { 16 * 160.00 /*MHz*/, 0x8e, 0x01, },
- { 16 * 454.00 /*MHz*/, 0x8e, 0x02, },
- { 16 * 999.99 , 0x8e, 0x04, },
-};
-
-static struct tuner_params tuner_ymec_tvf_5533mf_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_ymec_tvf_5533mf_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_ymec_tvf_5533mf_ntsc_ranges),
- },
-};
-
-/* 60-69 */
-/* ------------ TUNER_THOMSON_DTT761X - THOMSON ATSC ------------ */
-/* DTT 7611 7611A 7612 7613 7613A 7614 7615 7615A */
-
-static struct tuner_range tuner_thomson_dtt761x_ntsc_ranges[] = {
- { 16 * 145.25 /*MHz*/, 0x8e, 0x39, },
- { 16 * 415.25 /*MHz*/, 0x8e, 0x3a, },
- { 16 * 999.99 , 0x8e, 0x3c, },
-};
-
-static struct tuner_range tuner_thomson_dtt761x_atsc_ranges[] = {
- { 16 * 147.00 /*MHz*/, 0x8e, 0x39, },
- { 16 * 417.00 /*MHz*/, 0x8e, 0x3a, },
- { 16 * 999.99 , 0x8e, 0x3c, },
-};
-
-static struct tuner_params tuner_thomson_dtt761x_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_thomson_dtt761x_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_thomson_dtt761x_ntsc_ranges),
- .has_tda9887 = 1,
- .fm_gain_normal = 1,
- .radio_if = 2, /* 41.3 MHz */
- },
- {
- .type = TUNER_PARAM_TYPE_DIGITAL,
- .ranges = tuner_thomson_dtt761x_atsc_ranges,
- .count = ARRAY_SIZE(tuner_thomson_dtt761x_atsc_ranges),
- .iffreq = 16 * 44.00, /*MHz*/
- },
-};
-
-/* ------------ TUNER_TENA_9533_DI - Philips PAL ------------ */
-
-static struct tuner_range tuner_tena_9533_di_pal_ranges[] = {
- { 16 * 160.25 /*MHz*/, 0x8e, 0x01, },
- { 16 * 464.25 /*MHz*/, 0x8e, 0x02, },
- { 16 * 999.99 , 0x8e, 0x04, },
-};
-
-static struct tuner_params tuner_tena_9533_di_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_tena_9533_di_pal_ranges,
- .count = ARRAY_SIZE(tuner_tena_9533_di_pal_ranges),
- },
-};
-
-/* ------------ TUNER_PHILIPS_FMD1216ME_MK3 - Philips PAL ------------ */
-
-static struct tuner_range tuner_philips_fmd1216me_mk3_pal_ranges[] = {
- { 16 * 160.00 /*MHz*/, 0x86, 0x51, },
- { 16 * 442.00 /*MHz*/, 0x86, 0x52, },
- { 16 * 999.99 , 0x86, 0x54, },
-};
-
-static struct tuner_range tuner_philips_fmd1216me_mk3_dvb_ranges[] = {
- { 16 * 143.87 /*MHz*/, 0xbc, 0x41 },
- { 16 * 158.87 /*MHz*/, 0xf4, 0x41 },
- { 16 * 329.87 /*MHz*/, 0xbc, 0x42 },
- { 16 * 441.87 /*MHz*/, 0xf4, 0x42 },
- { 16 * 625.87 /*MHz*/, 0xbc, 0x44 },
- { 16 * 803.87 /*MHz*/, 0xf4, 0x44 },
- { 16 * 999.99 , 0xfc, 0x44 },
-};
-
-static struct tuner_params tuner_philips_fmd1216me_mk3_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_philips_fmd1216me_mk3_pal_ranges,
- .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_pal_ranges),
- .has_tda9887 = 1,
- .port1_active = 1,
- .port2_active = 1,
- .port2_fm_high_sensitivity = 1,
- .port2_invert_for_secam_lc = 1,
- .port1_set_for_fm_mono = 1,
- },
- {
- .type = TUNER_PARAM_TYPE_DIGITAL,
- .ranges = tuner_philips_fmd1216me_mk3_dvb_ranges,
- .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_dvb_ranges),
- .iffreq = 16 * 36.125, /*MHz*/
- },
-};
-
-
-/* ------ TUNER_LG_TDVS_H06XF - LG INNOTEK / INFINEON ATSC ----- */
-
-static struct tuner_range tuner_tua6034_ntsc_ranges[] = {
- { 16 * 165.00 /*MHz*/, 0x8e, 0x01 },
- { 16 * 450.00 /*MHz*/, 0x8e, 0x02 },
- { 16 * 999.99 , 0x8e, 0x04 },
-};
-
-static struct tuner_range tuner_tua6034_atsc_ranges[] = {
- { 16 * 165.00 /*MHz*/, 0xce, 0x01 },
- { 16 * 450.00 /*MHz*/, 0xce, 0x02 },
- { 16 * 999.99 , 0xce, 0x04 },
-};
-
-static struct tuner_params tuner_lg_tdvs_h06xf_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_tua6034_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_tua6034_ntsc_ranges),
- },
- {
- .type = TUNER_PARAM_TYPE_DIGITAL,
- .ranges = tuner_tua6034_atsc_ranges,
- .count = ARRAY_SIZE(tuner_tua6034_atsc_ranges),
- .iffreq = 16 * 44.00,
- },
-};
-
-/* ------------ TUNER_YMEC_TVF66T5_B_DFF - Philips PAL ------------ */
-
-static struct tuner_range tuner_ymec_tvf66t5_b_dff_pal_ranges[] = {
- { 16 * 160.25 /*MHz*/, 0x8e, 0x01, },
- { 16 * 464.25 /*MHz*/, 0x8e, 0x02, },
- { 16 * 999.99 , 0x8e, 0x08, },
-};
-
-static struct tuner_params tuner_ymec_tvf66t5_b_dff_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_ymec_tvf66t5_b_dff_pal_ranges,
- .count = ARRAY_SIZE(tuner_ymec_tvf66t5_b_dff_pal_ranges),
- },
-};
-
-/* ------------ TUNER_LG_NTSC_TALN_MINI - LGINNOTEK NTSC ------------ */
-
-static struct tuner_range tuner_lg_taln_ntsc_ranges[] = {
- { 16 * 137.25 /*MHz*/, 0x8e, 0x01, },
- { 16 * 373.25 /*MHz*/, 0x8e, 0x02, },
- { 16 * 999.99 , 0x8e, 0x08, },
-};
-
-static struct tuner_range tuner_lg_taln_pal_secam_ranges[] = {
- { 16 * 150.00 /*MHz*/, 0x8e, 0x01, },
- { 16 * 425.00 /*MHz*/, 0x8e, 0x02, },
- { 16 * 999.99 , 0x8e, 0x08, },
-};
-
-static struct tuner_params tuner_lg_taln_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_lg_taln_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_lg_taln_ntsc_ranges),
- },{
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_lg_taln_pal_secam_ranges,
- .count = ARRAY_SIZE(tuner_lg_taln_pal_secam_ranges),
- },
-};
-
-/* ------------ TUNER_PHILIPS_TD1316 - Philips PAL ------------ */
-
-static struct tuner_range tuner_philips_td1316_pal_ranges[] = {
- { 16 * 160.00 /*MHz*/, 0xc8, 0xa1, },
- { 16 * 442.00 /*MHz*/, 0xc8, 0xa2, },
- { 16 * 999.99 , 0xc8, 0xa4, },
-};
-
-static struct tuner_range tuner_philips_td1316_dvb_ranges[] = {
- { 16 * 93.834 /*MHz*/, 0xca, 0x60, },
- { 16 * 123.834 /*MHz*/, 0xca, 0xa0, },
- { 16 * 163.834 /*MHz*/, 0xca, 0xc0, },
- { 16 * 253.834 /*MHz*/, 0xca, 0x60, },
- { 16 * 383.834 /*MHz*/, 0xca, 0xa0, },
- { 16 * 443.834 /*MHz*/, 0xca, 0xc0, },
- { 16 * 583.834 /*MHz*/, 0xca, 0x60, },
- { 16 * 793.834 /*MHz*/, 0xca, 0xa0, },
- { 16 * 999.999 , 0xca, 0xe0, },
-};
-
-static struct tuner_params tuner_philips_td1316_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_philips_td1316_pal_ranges,
- .count = ARRAY_SIZE(tuner_philips_td1316_pal_ranges),
- },
- {
- .type = TUNER_PARAM_TYPE_DIGITAL,
- .ranges = tuner_philips_td1316_dvb_ranges,
- .count = ARRAY_SIZE(tuner_philips_td1316_dvb_ranges),
- .iffreq = 16 * 36.166667 /*MHz*/,
- },
-};
-
-/* ------------ TUNER_PHILIPS_TUV1236D - Philips ATSC ------------ */
-
-static struct tuner_range tuner_tuv1236d_ntsc_ranges[] = {
- { 16 * 157.25 /*MHz*/, 0xce, 0x01, },
- { 16 * 454.00 /*MHz*/, 0xce, 0x02, },
- { 16 * 999.99 , 0xce, 0x04, },
-};
-
-static struct tuner_range tuner_tuv1236d_atsc_ranges[] = {
- { 16 * 157.25 /*MHz*/, 0xc6, 0x41, },
- { 16 * 454.00 /*MHz*/, 0xc6, 0x42, },
- { 16 * 999.99 , 0xc6, 0x44, },
-};
-
-static struct tuner_params tuner_tuv1236d_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_tuv1236d_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_tuv1236d_ntsc_ranges),
- },
- {
- .type = TUNER_PARAM_TYPE_DIGITAL,
- .ranges = tuner_tuv1236d_atsc_ranges,
- .count = ARRAY_SIZE(tuner_tuv1236d_atsc_ranges),
- .iffreq = 16 * 44.00,
- },
-};
-
-/* ------------ TUNER_TNF_xxx5 - Texas Instruments--------- */
-/* This is known to work with Tenna TVF58t5-MFF and TVF5835 MFF
- * but it is expected to work also with other Tenna/Ymec
- * models based on TI SN 761677 chip on both PAL and NTSC
- */
-
-static struct tuner_range tuner_tnf_5335_d_if_pal_ranges[] = {
- { 16 * 168.25 /*MHz*/, 0x8e, 0x01, },
- { 16 * 471.25 /*MHz*/, 0x8e, 0x02, },
- { 16 * 999.99 , 0x8e, 0x08, },
-};
-
-static struct tuner_range tuner_tnf_5335mf_ntsc_ranges[] = {
- { 16 * 169.25 /*MHz*/, 0x8e, 0x01, },
- { 16 * 469.25 /*MHz*/, 0x8e, 0x02, },
- { 16 * 999.99 , 0x8e, 0x08, },
-};
-
-static struct tuner_params tuner_tnf_5335mf_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_tnf_5335mf_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_tnf_5335mf_ntsc_ranges),
- },
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_tnf_5335_d_if_pal_ranges,
- .count = ARRAY_SIZE(tuner_tnf_5335_d_if_pal_ranges),
- },
-};
-
-/* 70-79 */
-/* ------------ TUNER_SAMSUNG_TCPN_2121P30A - Samsung NTSC ------------ */
-
-/* '+ 4' turns on the Low Noise Amplifier */
-static struct tuner_range tuner_samsung_tcpn_2121p30a_ntsc_ranges[] = {
- { 16 * 130.00 /*MHz*/, 0xce, 0x01 + 4, },
- { 16 * 364.50 /*MHz*/, 0xce, 0x02 + 4, },
- { 16 * 999.99 , 0xce, 0x08 + 4, },
-};
-
-static struct tuner_params tuner_samsung_tcpn_2121p30a_params[] = {
- {
- .type = TUNER_PARAM_TYPE_NTSC,
- .ranges = tuner_samsung_tcpn_2121p30a_ntsc_ranges,
- .count = ARRAY_SIZE(tuner_samsung_tcpn_2121p30a_ntsc_ranges),
- },
-};
-
-/* ------------ TUNER_THOMSON_FE6600 - DViCO Hybrid PAL ------------ */
-
-static struct tuner_range tuner_thomson_fe6600_pal_ranges[] = {
- { 16 * 160.00 /*MHz*/, 0xfe, 0x11, },
- { 16 * 442.00 /*MHz*/, 0xf6, 0x12, },
- { 16 * 999.99 , 0xf6, 0x18, },
-};
-
-static struct tuner_range tuner_thomson_fe6600_dvb_ranges[] = {
- { 16 * 250.00 /*MHz*/, 0xb4, 0x12, },
- { 16 * 455.00 /*MHz*/, 0xfe, 0x11, },
- { 16 * 775.50 /*MHz*/, 0xbc, 0x18, },
- { 16 * 999.99 , 0xf4, 0x18, },
-};
-
-static struct tuner_params tuner_thomson_fe6600_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_thomson_fe6600_pal_ranges,
- .count = ARRAY_SIZE(tuner_thomson_fe6600_pal_ranges),
- },
- {
- .type = TUNER_PARAM_TYPE_DIGITAL,
- .ranges = tuner_thomson_fe6600_dvb_ranges,
- .count = ARRAY_SIZE(tuner_thomson_fe6600_dvb_ranges),
- .iffreq = 16 * 36.125 /*MHz*/,
- },
-};
-
-/* ------------ TUNER_SAMSUNG_TCPG_6121P30A - Samsung PAL ------------ */
-
-/* '+ 4' turns on the Low Noise Amplifier */
-static struct tuner_range tuner_samsung_tcpg_6121p30a_pal_ranges[] = {
- { 16 * 146.25 /*MHz*/, 0xce, 0x01 + 4, },
- { 16 * 428.50 /*MHz*/, 0xce, 0x02 + 4, },
- { 16 * 999.99 , 0xce, 0x08 + 4, },
-};
-
-static struct tuner_params tuner_samsung_tcpg_6121p30a_params[] = {
- {
- .type = TUNER_PARAM_TYPE_PAL,
- .ranges = tuner_samsung_tcpg_6121p30a_pal_ranges,
- .count = ARRAY_SIZE(tuner_samsung_tcpg_6121p30a_pal_ranges),
- .has_tda9887 = 1,
- .port1_active = 1,
- .port2_active = 1,
- .port2_invert_for_secam_lc = 1,
- },
-};
-
-/* --------------------------------------------------------------------- */
-
-struct tunertype tuners[] = {
- /* 0-9 */
- [TUNER_TEMIC_PAL] = { /* TEMIC PAL */
- .name = "Temic PAL (4002 FH5)",
- .params = tuner_temic_pal_params,
- .count = ARRAY_SIZE(tuner_temic_pal_params),
- },
- [TUNER_PHILIPS_PAL_I] = { /* Philips PAL_I */
- .name = "Philips PAL_I (FI1246 and compatibles)",
- .params = tuner_philips_pal_i_params,
- .count = ARRAY_SIZE(tuner_philips_pal_i_params),
- },
- [TUNER_PHILIPS_NTSC] = { /* Philips NTSC */
- .name = "Philips NTSC (FI1236,FM1236 and compatibles)",
- .params = tuner_philips_ntsc_params,
- .count = ARRAY_SIZE(tuner_philips_ntsc_params),
- },
- [TUNER_PHILIPS_SECAM] = { /* Philips SECAM */
- .name = "Philips (SECAM+PAL_BG) (FI1216MF, FM1216MF, FR1216MF)",
- .params = tuner_philips_secam_params,
- .count = ARRAY_SIZE(tuner_philips_secam_params),
- },
- [TUNER_ABSENT] = { /* Tuner Absent */
- .name = "NoTuner",
- },
- [TUNER_PHILIPS_PAL] = { /* Philips PAL */
- .name = "Philips PAL_BG (FI1216 and compatibles)",
- .params = tuner_philips_pal_params,
- .count = ARRAY_SIZE(tuner_philips_pal_params),
- },
- [TUNER_TEMIC_NTSC] = { /* TEMIC NTSC */
- .name = "Temic NTSC (4032 FY5)",
- .params = tuner_temic_ntsc_params,
- .count = ARRAY_SIZE(tuner_temic_ntsc_params),
- },
- [TUNER_TEMIC_PAL_I] = { /* TEMIC PAL_I */
- .name = "Temic PAL_I (4062 FY5)",
- .params = tuner_temic_pal_i_params,
- .count = ARRAY_SIZE(tuner_temic_pal_i_params),
- },
- [TUNER_TEMIC_4036FY5_NTSC] = { /* TEMIC NTSC */
- .name = "Temic NTSC (4036 FY5)",
- .params = tuner_temic_4036fy5_ntsc_params,
- .count = ARRAY_SIZE(tuner_temic_4036fy5_ntsc_params),
- },
- [TUNER_ALPS_TSBH1_NTSC] = { /* TEMIC NTSC */
- .name = "Alps HSBH1",
- .params = tuner_alps_tsbh1_ntsc_params,
- .count = ARRAY_SIZE(tuner_alps_tsbh1_ntsc_params),
- },
-
- /* 10-19 */
- [TUNER_ALPS_TSBE1_PAL] = { /* TEMIC PAL */
- .name = "Alps TSBE1",
- .params = tuner_alps_tsb_1_params,
- .count = ARRAY_SIZE(tuner_alps_tsb_1_params),
- },
- [TUNER_ALPS_TSBB5_PAL_I] = { /* Alps PAL_I */
- .name = "Alps TSBB5",
- .params = tuner_alps_tsbb5_params,
- .count = ARRAY_SIZE(tuner_alps_tsbb5_params),
- },
- [TUNER_ALPS_TSBE5_PAL] = { /* Alps PAL */
- .name = "Alps TSBE5",
- .params = tuner_alps_tsbe5_params,
- .count = ARRAY_SIZE(tuner_alps_tsbe5_params),
- },
- [TUNER_ALPS_TSBC5_PAL] = { /* Alps PAL */
- .name = "Alps TSBC5",
- .params = tuner_alps_tsbc5_params,
- .count = ARRAY_SIZE(tuner_alps_tsbc5_params),
- },
- [TUNER_TEMIC_4006FH5_PAL] = { /* TEMIC PAL */
- .name = "Temic PAL_BG (4006FH5)",
- .params = tuner_temic_4006fh5_params,
- .count = ARRAY_SIZE(tuner_temic_4006fh5_params),
- },
- [TUNER_ALPS_TSHC6_NTSC] = { /* Alps NTSC */
- .name = "Alps TSCH6",
- .params = tuner_alps_tshc6_params,
- .count = ARRAY_SIZE(tuner_alps_tshc6_params),
- },
- [TUNER_TEMIC_PAL_DK] = { /* TEMIC PAL */
- .name = "Temic PAL_DK (4016 FY5)",
- .params = tuner_temic_pal_dk_params,
- .count = ARRAY_SIZE(tuner_temic_pal_dk_params),
- },
- [TUNER_PHILIPS_NTSC_M] = { /* Philips NTSC */
- .name = "Philips NTSC_M (MK2)",
- .params = tuner_philips_ntsc_m_params,
- .count = ARRAY_SIZE(tuner_philips_ntsc_m_params),
- },
- [TUNER_TEMIC_4066FY5_PAL_I] = { /* TEMIC PAL_I */
- .name = "Temic PAL_I (4066 FY5)",
- .params = tuner_temic_4066fy5_pal_i_params,
- .count = ARRAY_SIZE(tuner_temic_4066fy5_pal_i_params),
- },
- [TUNER_TEMIC_4006FN5_MULTI_PAL] = { /* TEMIC PAL */
- .name = "Temic PAL* auto (4006 FN5)",
- .params = tuner_temic_4006fn5_multi_params,
- .count = ARRAY_SIZE(tuner_temic_4006fn5_multi_params),
- },
-
- /* 20-29 */
- [TUNER_TEMIC_4009FR5_PAL] = { /* TEMIC PAL */
- .name = "Temic PAL_BG (4009 FR5) or PAL_I (4069 FR5)",
- .params = tuner_temic_4009f_5_params,
- .count = ARRAY_SIZE(tuner_temic_4009f_5_params),
- },
- [TUNER_TEMIC_4039FR5_NTSC] = { /* TEMIC NTSC */
- .name = "Temic NTSC (4039 FR5)",
- .params = tuner_temic_4039fr5_params,
- .count = ARRAY_SIZE(tuner_temic_4039fr5_params),
- },
- [TUNER_TEMIC_4046FM5] = { /* TEMIC PAL */
- .name = "Temic PAL/SECAM multi (4046 FM5)",
- .params = tuner_temic_4046fm5_params,
- .count = ARRAY_SIZE(tuner_temic_4046fm5_params),
- },
- [TUNER_PHILIPS_PAL_DK] = { /* Philips PAL */
- .name = "Philips PAL_DK (FI1256 and compatibles)",
- .params = tuner_philips_pal_dk_params,
- .count = ARRAY_SIZE(tuner_philips_pal_dk_params),
- },
- [TUNER_PHILIPS_FQ1216ME] = { /* Philips PAL */
- .name = "Philips PAL/SECAM multi (FQ1216ME)",
- .params = tuner_philips_fq1216me_params,
- .count = ARRAY_SIZE(tuner_philips_fq1216me_params),
- },
- [TUNER_LG_PAL_I_FM] = { /* LGINNOTEK PAL_I */
- .name = "LG PAL_I+FM (TAPC-I001D)",
- .params = tuner_lg_pal_i_fm_params,
- .count = ARRAY_SIZE(tuner_lg_pal_i_fm_params),
- },
- [TUNER_LG_PAL_I] = { /* LGINNOTEK PAL_I */
- .name = "LG PAL_I (TAPC-I701D)",
- .params = tuner_lg_pal_i_params,
- .count = ARRAY_SIZE(tuner_lg_pal_i_params),
- },
- [TUNER_LG_NTSC_FM] = { /* LGINNOTEK NTSC */
- .name = "LG NTSC+FM (TPI8NSR01F)",
- .params = tuner_lg_ntsc_fm_params,
- .count = ARRAY_SIZE(tuner_lg_ntsc_fm_params),
- },
- [TUNER_LG_PAL_FM] = { /* LGINNOTEK PAL */
- .name = "LG PAL_BG+FM (TPI8PSB01D)",
- .params = tuner_lg_pal_fm_params,
- .count = ARRAY_SIZE(tuner_lg_pal_fm_params),
- },
- [TUNER_LG_PAL] = { /* LGINNOTEK PAL */
- .name = "LG PAL_BG (TPI8PSB11D)",
- .params = tuner_lg_pal_params,
- .count = ARRAY_SIZE(tuner_lg_pal_params),
- },
-
- /* 30-39 */
- [TUNER_TEMIC_4009FN5_MULTI_PAL_FM] = { /* TEMIC PAL */
- .name = "Temic PAL* auto + FM (4009 FN5)",
- .params = tuner_temic_4009_fn5_multi_pal_fm_params,
- .count = ARRAY_SIZE(tuner_temic_4009_fn5_multi_pal_fm_params),
- },
- [TUNER_SHARP_2U5JF5540_NTSC] = { /* SHARP NTSC */
- .name = "SHARP NTSC_JP (2U5JF5540)",
- .params = tuner_sharp_2u5jf5540_params,
- .count = ARRAY_SIZE(tuner_sharp_2u5jf5540_params),
- },
- [TUNER_Samsung_PAL_TCPM9091PD27] = { /* Samsung PAL */
- .name = "Samsung PAL TCPM9091PD27",
- .params = tuner_samsung_pal_tcpm9091pd27_params,
- .count = ARRAY_SIZE(tuner_samsung_pal_tcpm9091pd27_params),
- },
- [TUNER_MT2032] = { /* Microtune PAL|NTSC */
- .name = "MT20xx universal",
- /* see mt20xx.c for details */ },
- [TUNER_TEMIC_4106FH5] = { /* TEMIC PAL */
- .name = "Temic PAL_BG (4106 FH5)",
- .params = tuner_temic_4106fh5_params,
- .count = ARRAY_SIZE(tuner_temic_4106fh5_params),
- },
- [TUNER_TEMIC_4012FY5] = { /* TEMIC PAL */
- .name = "Temic PAL_DK/SECAM_L (4012 FY5)",
- .params = tuner_temic_4012fy5_params,
- .count = ARRAY_SIZE(tuner_temic_4012fy5_params),
- },
- [TUNER_TEMIC_4136FY5] = { /* TEMIC NTSC */
- .name = "Temic NTSC (4136 FY5)",
- .params = tuner_temic_4136_fy5_params,
- .count = ARRAY_SIZE(tuner_temic_4136_fy5_params),
- },
- [TUNER_LG_PAL_NEW_TAPC] = { /* LGINNOTEK PAL */
- .name = "LG PAL (newer TAPC series)",
- .params = tuner_lg_pal_new_tapc_params,
- .count = ARRAY_SIZE(tuner_lg_pal_new_tapc_params),
- },
- [TUNER_PHILIPS_FM1216ME_MK3] = { /* Philips PAL */
- .name = "Philips PAL/SECAM multi (FM1216ME MK3)",
- .params = tuner_fm1216me_mk3_params,
- .count = ARRAY_SIZE(tuner_fm1216me_mk3_params),
- },
- [TUNER_LG_NTSC_NEW_TAPC] = { /* LGINNOTEK NTSC */
- .name = "LG NTSC (newer TAPC series)",
- .params = tuner_lg_ntsc_new_tapc_params,
- .count = ARRAY_SIZE(tuner_lg_ntsc_new_tapc_params),
- },
-
- /* 40-49 */
- [TUNER_HITACHI_NTSC] = { /* HITACHI NTSC */
- .name = "HITACHI V7-J180AT",
- .params = tuner_hitachi_ntsc_params,
- .count = ARRAY_SIZE(tuner_hitachi_ntsc_params),
- },
- [TUNER_PHILIPS_PAL_MK] = { /* Philips PAL */
- .name = "Philips PAL_MK (FI1216 MK)",
- .params = tuner_philips_pal_mk_params,
- .count = ARRAY_SIZE(tuner_philips_pal_mk_params),
- },
- [TUNER_PHILIPS_FCV1236D] = { /* Philips ATSC */
- .name = "Philips FCV1236D ATSC/NTSC dual in",
- .params = tuner_philips_fcv1236d_params,
- .count = ARRAY_SIZE(tuner_philips_fcv1236d_params),
- .min = 16 * 53.00,
- .max = 16 * 803.00,
- .stepsize = 62500,
- },
- [TUNER_PHILIPS_FM1236_MK3] = { /* Philips NTSC */
- .name = "Philips NTSC MK3 (FM1236MK3 or FM1236/F)",
- .params = tuner_fm1236_mk3_params,
- .count = ARRAY_SIZE(tuner_fm1236_mk3_params),
- },
- [TUNER_PHILIPS_4IN1] = { /* Philips NTSC */
- .name = "Philips 4 in 1 (ATI TV Wonder Pro/Conexant)",
- .params = tuner_philips_4in1_params,
- .count = ARRAY_SIZE(tuner_philips_4in1_params),
- },
- [TUNER_MICROTUNE_4049FM5] = { /* Microtune PAL */
- .name = "Microtune 4049 FM5",
- .params = tuner_microtune_4049_fm5_params,
- .count = ARRAY_SIZE(tuner_microtune_4049_fm5_params),
- },
- [TUNER_PANASONIC_VP27] = { /* Panasonic NTSC */
- .name = "Panasonic VP27s/ENGE4324D",
- .params = tuner_panasonic_vp27_params,
- .count = ARRAY_SIZE(tuner_panasonic_vp27_params),
- },
- [TUNER_LG_NTSC_TAPE] = { /* LGINNOTEK NTSC */
- .name = "LG NTSC (TAPE series)",
- .params = tuner_fm1236_mk3_params,
- .count = ARRAY_SIZE(tuner_fm1236_mk3_params),
- },
- [TUNER_TNF_8831BGFF] = { /* Philips PAL */
- .name = "Tenna TNF 8831 BGFF)",
- .params = tuner_tnf_8831bgff_params,
- .count = ARRAY_SIZE(tuner_tnf_8831bgff_params),
- },
- [TUNER_MICROTUNE_4042FI5] = { /* Microtune NTSC */
- .name = "Microtune 4042 FI5 ATSC/NTSC dual in",
- .params = tuner_microtune_4042fi5_params,
- .count = ARRAY_SIZE(tuner_microtune_4042fi5_params),
- .min = 16 * 57.00,
- .max = 16 * 858.00,
- .stepsize = 62500,
- },
-
- /* 50-59 */
- [TUNER_TCL_2002N] = { /* TCL NTSC */
- .name = "TCL 2002N",
- .params = tuner_tcl_2002n_params,
- .count = ARRAY_SIZE(tuner_tcl_2002n_params),
- },
- [TUNER_PHILIPS_FM1256_IH3] = { /* Philips PAL */
- .name = "Philips PAL/SECAM_D (FM 1256 I-H3)",
- .params = tuner_philips_fm1256_ih3_params,
- .count = ARRAY_SIZE(tuner_philips_fm1256_ih3_params),
- },
- [TUNER_THOMSON_DTT7610] = { /* THOMSON ATSC */
- .name = "Thomson DTT 7610 (ATSC/NTSC)",
- .params = tuner_thomson_dtt7610_params,
- .count = ARRAY_SIZE(tuner_thomson_dtt7610_params),
- .min = 16 * 44.00,
- .max = 16 * 958.00,
- .stepsize = 62500,
- },
- [TUNER_PHILIPS_FQ1286] = { /* Philips NTSC */
- .name = "Philips FQ1286",
- .params = tuner_philips_fq1286_params,
- .count = ARRAY_SIZE(tuner_philips_fq1286_params),
- },
- [TUNER_PHILIPS_TDA8290] = { /* Philips PAL|NTSC */
- .name = "Philips/NXP TDA 8290/8295 + 8275/8275A/18271",
- /* see tda8290.c for details */ },
- [TUNER_TCL_2002MB] = { /* TCL PAL */
- .name = "TCL 2002MB",
- .params = tuner_tcl_2002mb_params,
- .count = ARRAY_SIZE(tuner_tcl_2002mb_params),
- },
- [TUNER_PHILIPS_FQ1216AME_MK4] = { /* Philips PAL */
- .name = "Philips PAL/SECAM multi (FQ1216AME MK4)",
- .params = tuner_philips_fq1216ame_mk4_params,
- .count = ARRAY_SIZE(tuner_philips_fq1216ame_mk4_params),
- },
- [TUNER_PHILIPS_FQ1236A_MK4] = { /* Philips NTSC */
- .name = "Philips FQ1236A MK4",
- .params = tuner_philips_fq1236a_mk4_params,
- .count = ARRAY_SIZE(tuner_philips_fq1236a_mk4_params),
- },
- [TUNER_YMEC_TVF_8531MF] = { /* Philips NTSC */
- .name = "Ymec TVision TVF-8531MF/8831MF/8731MF",
- .params = tuner_ymec_tvf_8531mf_params,
- .count = ARRAY_SIZE(tuner_ymec_tvf_8531mf_params),
- },
- [TUNER_YMEC_TVF_5533MF] = { /* Philips NTSC */
- .name = "Ymec TVision TVF-5533MF",
- .params = tuner_ymec_tvf_5533mf_params,
- .count = ARRAY_SIZE(tuner_ymec_tvf_5533mf_params),
- },
-
- /* 60-69 */
- [TUNER_THOMSON_DTT761X] = { /* THOMSON ATSC */
- /* DTT 7611 7611A 7612 7613 7613A 7614 7615 7615A */
- .name = "Thomson DTT 761X (ATSC/NTSC)",
- .params = tuner_thomson_dtt761x_params,
- .count = ARRAY_SIZE(tuner_thomson_dtt761x_params),
- .min = 16 * 57.00,
- .max = 16 * 863.00,
- .stepsize = 62500,
- .initdata = tua603x_agc103,
- },
- [TUNER_TENA_9533_DI] = { /* Philips PAL */
- .name = "Tena TNF9533-D/IF/TNF9533-B/DF",
- .params = tuner_tena_9533_di_params,
- .count = ARRAY_SIZE(tuner_tena_9533_di_params),
- },
- [TUNER_TEA5767] = { /* Philips RADIO */
- .name = "Philips TEA5767HN FM Radio",
- /* see tea5767.c for details */
- },
- [TUNER_PHILIPS_FMD1216ME_MK3] = { /* Philips PAL */
- .name = "Philips FMD1216ME MK3 Hybrid Tuner",
- .params = tuner_philips_fmd1216me_mk3_params,
- .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_params),
- .min = 16 * 50.87,
- .max = 16 * 858.00,
- .stepsize = 166667,
- .initdata = tua603x_agc112,
- .sleepdata = (u8[]){ 4, 0x9c, 0x60, 0x85, 0x54 },
- },
- [TUNER_LG_TDVS_H06XF] = { /* LGINNOTEK ATSC */
- .name = "LG TDVS-H06xF", /* H061F, H062F & H064F */
- .params = tuner_lg_tdvs_h06xf_params,
- .count = ARRAY_SIZE(tuner_lg_tdvs_h06xf_params),
- .min = 16 * 54.00,
- .max = 16 * 863.00,
- .stepsize = 62500,
- .initdata = tua603x_agc103,
- },
- [TUNER_YMEC_TVF66T5_B_DFF] = { /* Philips PAL */
- .name = "Ymec TVF66T5-B/DFF",
- .params = tuner_ymec_tvf66t5_b_dff_params,
- .count = ARRAY_SIZE(tuner_ymec_tvf66t5_b_dff_params),
- },
- [TUNER_LG_TALN] = { /* LGINNOTEK NTSC / PAL / SECAM */
- .name = "LG TALN series",
- .params = tuner_lg_taln_params,
- .count = ARRAY_SIZE(tuner_lg_taln_params),
- },
- [TUNER_PHILIPS_TD1316] = { /* Philips PAL */
- .name = "Philips TD1316 Hybrid Tuner",
- .params = tuner_philips_td1316_params,
- .count = ARRAY_SIZE(tuner_philips_td1316_params),
- .min = 16 * 87.00,
- .max = 16 * 895.00,
- .stepsize = 166667,
- },
- [TUNER_PHILIPS_TUV1236D] = { /* Philips ATSC */
- .name = "Philips TUV1236D ATSC/NTSC dual in",
- .params = tuner_tuv1236d_params,
- .count = ARRAY_SIZE(tuner_tuv1236d_params),
- .min = 16 * 54.00,
- .max = 16 * 864.00,
- .stepsize = 62500,
- },
- [TUNER_TNF_5335MF] = { /* Tenna PAL/NTSC */
- .name = "Tena TNF 5335 and similar models",
- .params = tuner_tnf_5335mf_params,
- .count = ARRAY_SIZE(tuner_tnf_5335mf_params),
- },
-
- /* 70-79 */
- [TUNER_SAMSUNG_TCPN_2121P30A] = { /* Samsung NTSC */
- .name = "Samsung TCPN 2121P30A",
- .params = tuner_samsung_tcpn_2121p30a_params,
- .count = ARRAY_SIZE(tuner_samsung_tcpn_2121p30a_params),
- },
- [TUNER_XC2028] = { /* Xceive 2028 */
- .name = "Xceive xc2028/xc3028 tuner",
- /* see tuner-xc2028.c for details */
- },
- [TUNER_THOMSON_FE6600] = { /* Thomson PAL / DVB-T */
- .name = "Thomson FE6600",
- .params = tuner_thomson_fe6600_params,
- .count = ARRAY_SIZE(tuner_thomson_fe6600_params),
- .min = 16 * 44.25,
- .max = 16 * 858.00,
- .stepsize = 166667,
- },
- [TUNER_SAMSUNG_TCPG_6121P30A] = { /* Samsung PAL */
- .name = "Samsung TCPG 6121P30A",
- .params = tuner_samsung_tcpg_6121p30a_params,
- .count = ARRAY_SIZE(tuner_samsung_tcpg_6121p30a_params),
- },
- [TUNER_TDA9887] = { /* Philips TDA 9887 IF PLL Demodulator.
- This chip is part of some modern tuners */
- .name = "Philips TDA988[5,6,7] IF PLL Demodulator",
- /* see tda9887.c for details */
- },
- [TUNER_TEA5761] = { /* Philips RADIO */
- .name = "Philips TEA5761 FM Radio",
- /* see tea5767.c for details */
- },
- [TUNER_XC5000] = { /* Xceive 5000 */
- .name = "Xceive 5000 tuner",
- /* see xc5000.c for details */
- },
-};
-EXPORT_SYMBOL(tuners);
-
-unsigned const int tuner_count = ARRAY_SIZE(tuners);
-EXPORT_SYMBOL(tuner_count);
-
-MODULE_DESCRIPTION("Simple tuner device type database");
-MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tuner-xc2028-types.h b/drivers/media/video/tuner-xc2028-types.h
deleted file mode 100644
index 74dc46a71f6..00000000000
--- a/drivers/media/video/tuner-xc2028-types.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/* tuner-xc2028_types
- *
- * This file includes internal tipes to be used inside tuner-xc2028.
- * Shouldn't be included outside tuner-xc2028
- *
- * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org)
- * This code is placed under the terms of the GNU General Public License v2
- */
-
-/* xc3028 firmware types */
-
-/* BASE firmware should be loaded before any other firmware */
-#define BASE (1<<0)
-#define BASE_TYPES (BASE|F8MHZ|MTS|FM|INPUT1|INPUT2|INIT1)
-
-/* F8MHZ marks BASE firmwares for 8 MHz Bandwidth */
-#define F8MHZ (1<<1)
-
-/* Multichannel Television Sound (MTS)
- Those firmwares are capable of using xc2038 DSP to decode audio and
- produce a baseband audio output on some pins of the chip.
- There are MTS firmwares for the most used video standards. It should be
- required to use MTS firmwares, depending on the way audio is routed into
- the bridge chip
- */
-#define MTS (1<<2)
-
-/* FIXME: I have no idea what's the difference between
- D2620 and D2633 firmwares
- */
-#define D2620 (1<<3)
-#define D2633 (1<<4)
-
-/* DTV firmwares for 6, 7 and 8 MHz
- DTV6 - 6MHz - ATSC/DVB-C/DVB-T/ISDB-T/DOCSIS
- DTV8 - 8MHz - DVB-C/DVB-T
- */
-#define DTV6 (1 << 5)
-#define QAM (1 << 6)
-#define DTV7 (1<<7)
-#define DTV78 (1<<8)
-#define DTV8 (1<<9)
-
-#define DTV_TYPES (D2620|D2633|DTV6|QAM|DTV7|DTV78|DTV8|ATSC)
-
-/* There's a FM | BASE firmware + FM specific firmware (std=0) */
-#define FM (1<<10)
-
-#define STD_SPECIFIC_TYPES (MTS|FM|LCD|NOGD)
-
-/* Applies only for FM firmware
- Makes it use RF input 1 (pin #2) instead of input 2 (pin #4)
- */
-#define INPUT1 (1<<11)
-
-
-/* LCD firmwares exist only for MTS STD/MN (PAL or NTSC/M)
- and for non-MTS STD/MN (PAL, NTSC/M or NTSC/Kr)
- There are variants both with and without NOGD
- Those firmwares produce better result with LCD displays
- */
-#define LCD (1<<12)
-
-/* NOGD firmwares exist only for MTS STD/MN (PAL or NTSC/M)
- and for non-MTS STD/MN (PAL, NTSC/M or NTSC/Kr)
- The NOGD firmwares don't have group delay compensation filter
- */
-#define NOGD (1<<13)
-
-/* Old firmwares were broken into init0 and init1 */
-#define INIT1 (1<<14)
-
-/* SCODE firmware selects particular behaviours */
-#define MONO (1 << 15)
-#define ATSC (1 << 16)
-#define IF (1 << 17)
-#define LG60 (1 << 18)
-#define ATI638 (1 << 19)
-#define OREN538 (1 << 20)
-#define OREN36 (1 << 21)
-#define TOYOTA388 (1 << 22)
-#define TOYOTA794 (1 << 23)
-#define DIBCOM52 (1 << 24)
-#define ZARLINK456 (1 << 25)
-#define CHINA (1 << 26)
-#define F6MHZ (1 << 27)
-#define INPUT2 (1 << 28)
-#define SCODE (1 << 29)
-
-/* This flag identifies that the scode table has a new format */
-#define HAS_IF (1 << 30)
-
-/* There are different scode tables for MTS and non-MTS.
- The MTS firmwares support mono only
- */
-#define SCODE_TYPES (SCODE | MTS)
-
-
-/* Newer types not defined on videodev2.h.
- The original idea were to move all those types to videodev2.h, but
- it seemed overkill, since, with the exception of SECAM/K3, the other
- types seem to be autodetected.
- It is not clear where secam/k3 is used, nor we have a feedback of this
- working or being autodetected by the standard secam firmware.
- */
-
-#define V4L2_STD_SECAM_K3 (0x04000000)
-
-/* Audio types */
-
-#define V4L2_STD_A2_A (1LL<<32)
-#define V4L2_STD_A2_B (1LL<<33)
-#define V4L2_STD_NICAM_A (1LL<<34)
-#define V4L2_STD_NICAM_B (1LL<<35)
-#define V4L2_STD_AM (1LL<<36)
-#define V4L2_STD_BTSC (1LL<<37)
-#define V4L2_STD_EIAJ (1LL<<38)
-
-#define V4L2_STD_A2 (V4L2_STD_A2_A | V4L2_STD_A2_B)
-#define V4L2_STD_NICAM (V4L2_STD_NICAM_A | V4L2_STD_NICAM_B)
-
-/* To preserve backward compatibilty,
- (std & V4L2_STD_AUDIO) = 0 means that ALL audio stds are supported
- */
-
-#define V4L2_STD_AUDIO (V4L2_STD_A2 | \
- V4L2_STD_NICAM | \
- V4L2_STD_AM | \
- V4L2_STD_BTSC | \
- V4L2_STD_EIAJ)
-
-/* Used standards with audio restrictions */
-
-#define V4L2_STD_PAL_BG_A2_A (V4L2_STD_PAL_BG | V4L2_STD_A2_A)
-#define V4L2_STD_PAL_BG_A2_B (V4L2_STD_PAL_BG | V4L2_STD_A2_B)
-#define V4L2_STD_PAL_BG_NICAM_A (V4L2_STD_PAL_BG | V4L2_STD_NICAM_A)
-#define V4L2_STD_PAL_BG_NICAM_B (V4L2_STD_PAL_BG | V4L2_STD_NICAM_B)
-#define V4L2_STD_PAL_DK_A2 (V4L2_STD_PAL_DK | V4L2_STD_A2)
-#define V4L2_STD_PAL_DK_NICAM (V4L2_STD_PAL_DK | V4L2_STD_NICAM)
-#define V4L2_STD_SECAM_L_NICAM (V4L2_STD_SECAM_L | V4L2_STD_NICAM)
-#define V4L2_STD_SECAM_L_AM (V4L2_STD_SECAM_L | V4L2_STD_AM)
diff --git a/drivers/media/video/tuner-xc2028.c b/drivers/media/video/tuner-xc2028.c
deleted file mode 100644
index 9e9003cffc7..00000000000
--- a/drivers/media/video/tuner-xc2028.c
+++ /dev/null
@@ -1,1227 +0,0 @@
-/* tuner-xc2028
- *
- * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org)
- *
- * Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com)
- * - frontend interface
- *
- * This code is placed under the terms of the GNU General Public License v2
- */
-
-#include <linux/i2c.h>
-#include <asm/div64.h>
-#include <linux/firmware.h>
-#include <linux/videodev2.h>
-#include <linux/delay.h>
-#include <media/tuner.h>
-#include <linux/mutex.h>
-#include "tuner-i2c.h"
-#include "tuner-xc2028.h"
-#include "tuner-xc2028-types.h"
-
-#include <linux/dvb/frontend.h>
-#include "dvb_frontend.h"
-
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "enable verbose debug messages");
-
-static char audio_std[8];
-module_param_string(audio_std, audio_std, sizeof(audio_std), 0);
-MODULE_PARM_DESC(audio_std,
- "Audio standard. XC3028 audio decoder explicitly "
- "needs to know what audio\n"
- "standard is needed for some video standards with audio A2 or NICAM.\n"
- "The valid values are:\n"
- "A2\n"
- "A2/A\n"
- "A2/B\n"
- "NICAM\n"
- "NICAM/A\n"
- "NICAM/B\n");
-
-static char firmware_name[FIRMWARE_NAME_MAX];
-module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0);
-MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the "
- "default firmware name\n");
-
-static LIST_HEAD(xc2028_list);
-static DEFINE_MUTEX(xc2028_list_mutex);
-
-/* struct for storing firmware table */
-struct firmware_description {
- unsigned int type;
- v4l2_std_id id;
- __u16 int_freq;
- unsigned char *ptr;
- unsigned int size;
-};
-
-struct firmware_properties {
- unsigned int type;
- v4l2_std_id id;
- v4l2_std_id std_req;
- __u16 int_freq;
- unsigned int scode_table;
- int scode_nr;
-};
-
-struct xc2028_data {
- struct list_head xc2028_list;
- struct tuner_i2c_props i2c_props;
- int (*tuner_callback) (void *dev,
- int command, int arg);
- void *video_dev;
- int count;
- __u32 frequency;
-
- struct firmware_description *firm;
- int firm_size;
- __u16 firm_version;
-
- __u16 hwmodel;
- __u16 hwvers;
-
- struct xc2028_ctrl ctrl;
-
- struct firmware_properties cur_fw;
-
- struct mutex lock;
-};
-
-#define i2c_send(priv, buf, size) ({ \
- int _rc; \
- _rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size); \
- if (size != _rc) \
- tuner_info("i2c output error: rc = %d (should be %d)\n",\
- _rc, (int)size); \
- _rc; \
-})
-
-#define i2c_rcv(priv, buf, size) ({ \
- int _rc; \
- _rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, size); \
- if (size != _rc) \
- tuner_err("i2c input error: rc = %d (should be %d)\n", \
- _rc, (int)size); \
- _rc; \
-})
-
-#define i2c_send_recv(priv, obuf, osize, ibuf, isize) ({ \
- int _rc; \
- _rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, obuf, osize, \
- ibuf, isize); \
- if (isize != _rc) \
- tuner_err("i2c input error: rc = %d (should be %d)\n", \
- _rc, (int)isize); \
- _rc; \
-})
-
-#define send_seq(priv, data...) ({ \
- static u8 _val[] = data; \
- int _rc; \
- if (sizeof(_val) != \
- (_rc = tuner_i2c_xfer_send(&priv->i2c_props, \
- _val, sizeof(_val)))) { \
- tuner_err("Error on line %d: %d\n", __LINE__, _rc); \
- } else \
- msleep(10); \
- _rc; \
-})
-
-static int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val)
-{
- unsigned char buf[2];
- unsigned char ibuf[2];
-
- tuner_dbg("%s %04x called\n", __func__, reg);
-
- buf[0] = reg >> 8;
- buf[1] = (unsigned char) reg;
-
- if (i2c_send_recv(priv, buf, 2, ibuf, 2) != 2)
- return -EIO;
-
- *val = (ibuf[1]) | (ibuf[0] << 8);
- return 0;
-}
-
-#define dump_firm_type(t) dump_firm_type_and_int_freq(t, 0)
-static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq)
-{
- if (type & BASE)
- printk("BASE ");
- if (type & INIT1)
- printk("INIT1 ");
- if (type & F8MHZ)
- printk("F8MHZ ");
- if (type & MTS)
- printk("MTS ");
- if (type & D2620)
- printk("D2620 ");
- if (type & D2633)
- printk("D2633 ");
- if (type & DTV6)
- printk("DTV6 ");
- if (type & QAM)
- printk("QAM ");
- if (type & DTV7)
- printk("DTV7 ");
- if (type & DTV78)
- printk("DTV78 ");
- if (type & DTV8)
- printk("DTV8 ");
- if (type & FM)
- printk("FM ");
- if (type & INPUT1)
- printk("INPUT1 ");
- if (type & LCD)
- printk("LCD ");
- if (type & NOGD)
- printk("NOGD ");
- if (type & MONO)
- printk("MONO ");
- if (type & ATSC)
- printk("ATSC ");
- if (type & IF)
- printk("IF ");
- if (type & LG60)
- printk("LG60 ");
- if (type & ATI638)
- printk("ATI638 ");
- if (type & OREN538)
- printk("OREN538 ");
- if (type & OREN36)
- printk("OREN36 ");
- if (type & TOYOTA388)
- printk("TOYOTA388 ");
- if (type & TOYOTA794)
- printk("TOYOTA794 ");
- if (type & DIBCOM52)
- printk("DIBCOM52 ");
- if (type & ZARLINK456)
- printk("ZARLINK456 ");
- if (type & CHINA)
- printk("CHINA ");
- if (type & F6MHZ)
- printk("F6MHZ ");
- if (type & INPUT2)
- printk("INPUT2 ");
- if (type & SCODE)
- printk("SCODE ");
- if (type & HAS_IF)
- printk("HAS_IF_%d ", int_freq);
-}
-
-static v4l2_std_id parse_audio_std_option(void)
-{
- if (strcasecmp(audio_std, "A2") == 0)
- return V4L2_STD_A2;
- if (strcasecmp(audio_std, "A2/A") == 0)
- return V4L2_STD_A2_A;
- if (strcasecmp(audio_std, "A2/B") == 0)
- return V4L2_STD_A2_B;
- if (strcasecmp(audio_std, "NICAM") == 0)
- return V4L2_STD_NICAM;
- if (strcasecmp(audio_std, "NICAM/A") == 0)
- return V4L2_STD_NICAM_A;
- if (strcasecmp(audio_std, "NICAM/B") == 0)
- return V4L2_STD_NICAM_B;
-
- return 0;
-}
-
-static void free_firmware(struct xc2028_data *priv)
-{
- int i;
- tuner_dbg("%s called\n", __func__);
-
- if (!priv->firm)
- return;
-
- for (i = 0; i < priv->firm_size; i++)
- kfree(priv->firm[i].ptr);
-
- kfree(priv->firm);
-
- priv->firm = NULL;
- priv->firm_size = 0;
-
- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
-}
-
-static int load_all_firmwares(struct dvb_frontend *fe)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- const struct firmware *fw = NULL;
- unsigned char *p, *endp;
- int rc = 0;
- int n, n_array;
- char name[33];
- char *fname;
-
- tuner_dbg("%s called\n", __func__);
-
- if (!firmware_name[0])
- fname = priv->ctrl.fname;
- else
- fname = firmware_name;
-
- tuner_dbg("Reading firmware %s\n", fname);
- rc = request_firmware(&fw, fname, &priv->i2c_props.adap->dev);
- if (rc < 0) {
- if (rc == -ENOENT)
- tuner_err("Error: firmware %s not found.\n",
- fname);
- else
- tuner_err("Error %d while requesting firmware %s \n",
- rc, fname);
-
- return rc;
- }
- p = fw->data;
- endp = p + fw->size;
-
- if (fw->size < sizeof(name) - 1 + 2 + 2) {
- tuner_err("Error: firmware file %s has invalid size!\n",
- fname);
- goto corrupt;
- }
-
- memcpy(name, p, sizeof(name) - 1);
- name[sizeof(name) - 1] = 0;
- p += sizeof(name) - 1;
-
- priv->firm_version = le16_to_cpu(*(__u16 *) p);
- p += 2;
-
- n_array = le16_to_cpu(*(__u16 *) p);
- p += 2;
-
- tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n",
- n_array, fname, name,
- priv->firm_version >> 8, priv->firm_version & 0xff);
-
- priv->firm = kzalloc(sizeof(*priv->firm) * n_array, GFP_KERNEL);
- if (priv->firm == NULL) {
- tuner_err("Not enough memory to load firmware file.\n");
- rc = -ENOMEM;
- goto err;
- }
- priv->firm_size = n_array;
-
- n = -1;
- while (p < endp) {
- __u32 type, size;
- v4l2_std_id id;
- __u16 int_freq = 0;
-
- n++;
- if (n >= n_array) {
- tuner_err("More firmware images in file than "
- "were expected!\n");
- goto corrupt;
- }
-
- /* Checks if there's enough bytes to read */
- if (p + sizeof(type) + sizeof(id) + sizeof(size) > endp) {
- tuner_err("Firmware header is incomplete!\n");
- goto corrupt;
- }
-
- type = le32_to_cpu(*(__u32 *) p);
- p += sizeof(type);
-
- id = le64_to_cpu(*(v4l2_std_id *) p);
- p += sizeof(id);
-
- if (type & HAS_IF) {
- int_freq = le16_to_cpu(*(__u16 *) p);
- p += sizeof(int_freq);
- }
-
- size = le32_to_cpu(*(__u32 *) p);
- p += sizeof(size);
-
- if ((!size) || (size + p > endp)) {
- tuner_err("Firmware type ");
- dump_firm_type(type);
- printk("(%x), id %llx is corrupted "
- "(size=%d, expected %d)\n",
- type, (unsigned long long)id,
- (unsigned)(endp - p), size);
- goto corrupt;
- }
-
- priv->firm[n].ptr = kzalloc(size, GFP_KERNEL);
- if (priv->firm[n].ptr == NULL) {
- tuner_err("Not enough memory to load firmware file.\n");
- rc = -ENOMEM;
- goto err;
- }
- tuner_dbg("Reading firmware type ");
- if (debug) {
- dump_firm_type_and_int_freq(type, int_freq);
- printk("(%x), id %llx, size=%d.\n",
- type, (unsigned long long)id, size);
- }
-
- memcpy(priv->firm[n].ptr, p, size);
- priv->firm[n].type = type;
- priv->firm[n].id = id;
- priv->firm[n].size = size;
- priv->firm[n].int_freq = int_freq;
-
- p += size;
- }
-
- if (n + 1 != priv->firm_size) {
- tuner_err("Firmware file is incomplete!\n");
- goto corrupt;
- }
-
- goto done;
-
-corrupt:
- rc = -EINVAL;
- tuner_err("Error: firmware file is corrupted!\n");
-
-err:
- tuner_info("Releasing partially loaded firmware file.\n");
- free_firmware(priv);
-
-done:
- release_firmware(fw);
- if (rc == 0)
- tuner_dbg("Firmware files loaded.\n");
-
- return rc;
-}
-
-static int seek_firmware(struct dvb_frontend *fe, unsigned int type,
- v4l2_std_id *id)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- int i, best_i = -1, best_nr_matches = 0;
- unsigned int type_mask = 0;
-
- tuner_dbg("%s called, want type=", __func__);
- if (debug) {
- dump_firm_type(type);
- printk("(%x), id %016llx.\n", type, (unsigned long long)*id);
- }
-
- if (!priv->firm) {
- tuner_err("Error! firmware not loaded\n");
- return -EINVAL;
- }
-
- if (((type & ~SCODE) == 0) && (*id == 0))
- *id = V4L2_STD_PAL;
-
- if (type & BASE)
- type_mask = BASE_TYPES;
- else if (type & SCODE) {
- type &= SCODE_TYPES;
- type_mask = SCODE_TYPES & ~HAS_IF;
- } else if (type & DTV_TYPES)
- type_mask = DTV_TYPES;
- else if (type & STD_SPECIFIC_TYPES)
- type_mask = STD_SPECIFIC_TYPES;
-
- type &= type_mask;
-
- if (!(type & SCODE))
- type_mask = ~0;
-
- /* Seek for exact match */
- for (i = 0; i < priv->firm_size; i++) {
- if ((type == (priv->firm[i].type & type_mask)) &&
- (*id == priv->firm[i].id))
- goto found;
- }
-
- /* Seek for generic video standard match */
- for (i = 0; i < priv->firm_size; i++) {
- v4l2_std_id match_mask;
- int nr_matches;
-
- if (type != (priv->firm[i].type & type_mask))
- continue;
-
- match_mask = *id & priv->firm[i].id;
- if (!match_mask)
- continue;
-
- if ((*id & match_mask) == *id)
- goto found; /* Supports all the requested standards */
-
- nr_matches = hweight64(match_mask);
- if (nr_matches > best_nr_matches) {
- best_nr_matches = nr_matches;
- best_i = i;
- }
- }
-
- if (best_nr_matches > 0) {
- tuner_dbg("Selecting best matching firmware (%d bits) for "
- "type=", best_nr_matches);
- dump_firm_type(type);
- printk("(%x), id %016llx:\n", type, (unsigned long long)*id);
- i = best_i;
- goto found;
- }
-
- /*FIXME: Would make sense to seek for type "hint" match ? */
-
- i = -ENOENT;
- goto ret;
-
-found:
- *id = priv->firm[i].id;
-
-ret:
- tuner_dbg("%s firmware for type=", (i < 0) ? "Can't find" : "Found");
- if (debug) {
- dump_firm_type(type);
- printk("(%x), id %016llx.\n", type, (unsigned long long)*id);
- }
- return i;
-}
-
-static int load_firmware(struct dvb_frontend *fe, unsigned int type,
- v4l2_std_id *id)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- int pos, rc;
- unsigned char *p, *endp, buf[priv->ctrl.max_len];
-
- tuner_dbg("%s called\n", __func__);
-
- pos = seek_firmware(fe, type, id);
- if (pos < 0)
- return pos;
-
- tuner_info("Loading firmware for type=");
- dump_firm_type(priv->firm[pos].type);
- printk("(%x), id %016llx.\n", priv->firm[pos].type,
- (unsigned long long)*id);
-
- p = priv->firm[pos].ptr;
- endp = p + priv->firm[pos].size;
-
- while (p < endp) {
- __u16 size;
-
- /* Checks if there's enough bytes to read */
- if (p + sizeof(size) > endp) {
- tuner_err("Firmware chunk size is wrong\n");
- return -EINVAL;
- }
-
- size = le16_to_cpu(*(__u16 *) p);
- p += sizeof(size);
-
- if (size == 0xffff)
- return 0;
-
- if (!size) {
- /* Special callback command received */
- rc = priv->tuner_callback(priv->video_dev,
- XC2028_TUNER_RESET, 0);
- if (rc < 0) {
- tuner_err("Error at RESET code %d\n",
- (*p) & 0x7f);
- return -EINVAL;
- }
- continue;
- }
- if (size >= 0xff00) {
- switch (size) {
- case 0xff00:
- rc = priv->tuner_callback(priv->video_dev,
- XC2028_RESET_CLK, 0);
- if (rc < 0) {
- tuner_err("Error at RESET code %d\n",
- (*p) & 0x7f);
- return -EINVAL;
- }
- break;
- default:
- tuner_info("Invalid RESET code %d\n",
- size & 0x7f);
- return -EINVAL;
-
- }
- continue;
- }
-
- /* Checks for a sleep command */
- if (size & 0x8000) {
- msleep(size & 0x7fff);
- continue;
- }
-
- if ((size + p > endp)) {
- tuner_err("missing bytes: need %d, have %d\n",
- size, (int)(endp - p));
- return -EINVAL;
- }
-
- buf[0] = *p;
- p++;
- size--;
-
- /* Sends message chunks */
- while (size > 0) {
- int len = (size < priv->ctrl.max_len - 1) ?
- size : priv->ctrl.max_len - 1;
-
- memcpy(buf + 1, p, len);
-
- rc = i2c_send(priv, buf, len + 1);
- if (rc < 0) {
- tuner_err("%d returned from send\n", rc);
- return -EINVAL;
- }
-
- p += len;
- size -= len;
- }
- }
- return 0;
-}
-
-static int load_scode(struct dvb_frontend *fe, unsigned int type,
- v4l2_std_id *id, __u16 int_freq, int scode)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- int pos, rc;
- unsigned char *p;
-
- tuner_dbg("%s called\n", __func__);
-
- if (!int_freq) {
- pos = seek_firmware(fe, type, id);
- if (pos < 0)
- return pos;
- } else {
- for (pos = 0; pos < priv->firm_size; pos++) {
- if ((priv->firm[pos].int_freq == int_freq) &&
- (priv->firm[pos].type & HAS_IF))
- break;
- }
- if (pos == priv->firm_size)
- return -ENOENT;
- }
-
- p = priv->firm[pos].ptr;
-
- if (priv->firm[pos].type & HAS_IF) {
- if (priv->firm[pos].size != 12 * 16 || scode >= 16)
- return -EINVAL;
- p += 12 * scode;
- } else {
- /* 16 SCODE entries per file; each SCODE entry is 12 bytes and
- * has a 2-byte size header in the firmware format. */
- if (priv->firm[pos].size != 14 * 16 || scode >= 16 ||
- le16_to_cpu(*(__u16 *)(p + 14 * scode)) != 12)
- return -EINVAL;
- p += 14 * scode + 2;
- }
-
- tuner_info("Loading SCODE for type=");
- dump_firm_type_and_int_freq(priv->firm[pos].type,
- priv->firm[pos].int_freq);
- printk("(%x), id %016llx.\n", priv->firm[pos].type,
- (unsigned long long)*id);
-
- if (priv->firm_version < 0x0202)
- rc = send_seq(priv, {0x20, 0x00, 0x00, 0x00});
- else
- rc = send_seq(priv, {0xa0, 0x00, 0x00, 0x00});
- if (rc < 0)
- return -EIO;
-
- rc = i2c_send(priv, p, 12);
- if (rc < 0)
- return -EIO;
-
- rc = send_seq(priv, {0x00, 0x8c});
- if (rc < 0)
- return -EIO;
-
- return 0;
-}
-
-static int check_firmware(struct dvb_frontend *fe, unsigned int type,
- v4l2_std_id std, __u16 int_freq)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- struct firmware_properties new_fw;
- int rc = 0, is_retry = 0;
- u16 version, hwmodel;
- v4l2_std_id std0;
-
- tuner_dbg("%s called\n", __func__);
-
- if (!priv->firm) {
- if (!priv->ctrl.fname) {
- tuner_info("xc2028/3028 firmware name not set!\n");
- return -EINVAL;
- }
-
- rc = load_all_firmwares(fe);
- if (rc < 0)
- return rc;
- }
-
- if (priv->ctrl.mts && !(type & FM))
- type |= MTS;
-
-retry:
- new_fw.type = type;
- new_fw.id = std;
- new_fw.std_req = std;
- new_fw.scode_table = SCODE | priv->ctrl.scode_table;
- new_fw.scode_nr = 0;
- new_fw.int_freq = int_freq;
-
- tuner_dbg("checking firmware, user requested type=");
- if (debug) {
- dump_firm_type(new_fw.type);
- printk("(%x), id %016llx, ", new_fw.type,
- (unsigned long long)new_fw.std_req);
- if (!int_freq) {
- printk("scode_tbl ");
- dump_firm_type(priv->ctrl.scode_table);
- printk("(%x), ", priv->ctrl.scode_table);
- } else
- printk("int_freq %d, ", new_fw.int_freq);
- printk("scode_nr %d\n", new_fw.scode_nr);
- }
-
- /* No need to reload base firmware if it matches */
- if (((BASE | new_fw.type) & BASE_TYPES) ==
- (priv->cur_fw.type & BASE_TYPES)) {
- tuner_dbg("BASE firmware not changed.\n");
- goto skip_base;
- }
-
- /* Updating BASE - forget about all currently loaded firmware */
- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
-
- /* Reset is needed before loading firmware */
- rc = priv->tuner_callback(priv->video_dev,
- XC2028_TUNER_RESET, 0);
- if (rc < 0)
- goto fail;
-
- /* BASE firmwares are all std0 */
- std0 = 0;
- rc = load_firmware(fe, BASE | new_fw.type, &std0);
- if (rc < 0) {
- tuner_err("Error %d while loading base firmware\n",
- rc);
- goto fail;
- }
-
- /* Load INIT1, if needed */
- tuner_dbg("Load init1 firmware, if exists\n");
-
- rc = load_firmware(fe, BASE | INIT1 | new_fw.type, &std0);
- if (rc == -ENOENT)
- rc = load_firmware(fe, (BASE | INIT1 | new_fw.type) & ~F8MHZ,
- &std0);
- if (rc < 0 && rc != -ENOENT) {
- tuner_err("Error %d while loading init1 firmware\n",
- rc);
- goto fail;
- }
-
-skip_base:
- /*
- * No need to reload standard specific firmware if base firmware
- * was not reloaded and requested video standards have not changed.
- */
- if (priv->cur_fw.type == (BASE | new_fw.type) &&
- priv->cur_fw.std_req == std) {
- tuner_dbg("Std-specific firmware already loaded.\n");
- goto skip_std_specific;
- }
-
- /* Reloading std-specific firmware forces a SCODE update */
- priv->cur_fw.scode_table = 0;
-
- rc = load_firmware(fe, new_fw.type, &new_fw.id);
- if (rc == -ENOENT)
- rc = load_firmware(fe, new_fw.type & ~F8MHZ, &new_fw.id);
-
- if (rc < 0)
- goto fail;
-
-skip_std_specific:
- if (priv->cur_fw.scode_table == new_fw.scode_table &&
- priv->cur_fw.scode_nr == new_fw.scode_nr) {
- tuner_dbg("SCODE firmware already loaded.\n");
- goto check_device;
- }
-
- if (new_fw.type & FM)
- goto check_device;
-
- /* Load SCODE firmware, if exists */
- tuner_dbg("Trying to load scode %d\n", new_fw.scode_nr);
-
- rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id,
- new_fw.int_freq, new_fw.scode_nr);
-
-check_device:
- if (xc2028_get_reg(priv, 0x0004, &version) < 0 ||
- xc2028_get_reg(priv, 0x0008, &hwmodel) < 0) {
- tuner_err("Unable to read tuner registers.\n");
- goto fail;
- }
-
- tuner_dbg("Device is Xceive %d version %d.%d, "
- "firmware version %d.%d\n",
- hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8,
- (version & 0xf0) >> 4, version & 0xf);
-
- /* Check firmware version against what we downloaded. */
- if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) {
- tuner_err("Incorrect readback of firmware version.\n");
- goto fail;
- }
-
- /* Check that the tuner hardware model remains consistent over time. */
- if (priv->hwmodel == 0 && (hwmodel == 2028 || hwmodel == 3028)) {
- priv->hwmodel = hwmodel;
- priv->hwvers = version & 0xff00;
- } else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel ||
- priv->hwvers != (version & 0xff00)) {
- tuner_err("Read invalid device hardware information - tuner "
- "hung?\n");
- goto fail;
- }
-
- memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw));
-
- /*
- * By setting BASE in cur_fw.type only after successfully loading all
- * firmwares, we can:
- * 1. Identify that BASE firmware with type=0 has been loaded;
- * 2. Tell whether BASE firmware was just changed the next time through.
- */
- priv->cur_fw.type |= BASE;
-
- return 0;
-
-fail:
- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
- if (!is_retry) {
- msleep(50);
- is_retry = 1;
- tuner_dbg("Retrying firmware load\n");
- goto retry;
- }
-
- if (rc == -ENOENT)
- rc = -EINVAL;
- return rc;
-}
-
-static int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- u16 frq_lock, signal = 0;
- int rc;
-
- tuner_dbg("%s called\n", __func__);
-
- mutex_lock(&priv->lock);
-
- /* Sync Lock Indicator */
- rc = xc2028_get_reg(priv, 0x0002, &frq_lock);
- if (rc < 0)
- goto ret;
-
- /* Frequency is locked */
- if (frq_lock == 1)
- signal = 32768;
-
- /* Get SNR of the video signal */
- rc = xc2028_get_reg(priv, 0x0040, &signal);
- if (rc < 0)
- goto ret;
-
- /* Use both frq_lock and signal to generate the result */
- signal = signal || ((signal & 0x07) << 12);
-
-ret:
- mutex_unlock(&priv->lock);
-
- *strength = signal;
-
- tuner_dbg("signal strength is %d\n", signal);
-
- return rc;
-}
-
-#define DIV 15625
-
-static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,
- enum tuner_mode new_mode,
- unsigned int type,
- v4l2_std_id std,
- u16 int_freq)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- int rc = -EINVAL;
- unsigned char buf[4];
- u32 div, offset = 0;
-
- tuner_dbg("%s called\n", __func__);
-
- mutex_lock(&priv->lock);
-
- tuner_dbg("should set frequency %d kHz\n", freq / 1000);
-
- if (check_firmware(fe, type, std, int_freq) < 0)
- goto ret;
-
- /* On some cases xc2028 can disable video output, if
- * very weak signals are received. By sending a soft
- * reset, this is re-enabled. So, it is better to always
- * send a soft reset before changing channels, to be sure
- * that xc2028 will be in a safe state.
- * Maybe this might also be needed for DTV.
- */
- if (new_mode == T_ANALOG_TV) {
- rc = send_seq(priv, {0x00, 0x00});
- } else if (priv->cur_fw.type & ATSC) {
- offset = 1750000;
- } else {
- offset = 2750000;
- /*
- * We must adjust the offset by 500kHz in two cases in order
- * to correctly center the IF output:
- * 1) When the ZARLINK456 or DIBCOM52 tables were explicitly
- * selected and a 7MHz channel is tuned;
- * 2) When tuning a VHF channel with DTV78 firmware.
- */
- if (((priv->cur_fw.type & DTV7) &&
- (priv->cur_fw.scode_table & (ZARLINK456 | DIBCOM52))) ||
- ((priv->cur_fw.type & DTV78) && freq < 470000000))
- offset -= 500000;
- }
-
- div = (freq - offset + DIV / 2) / DIV;
-
- /* CMD= Set frequency */
- if (priv->firm_version < 0x0202)
- rc = send_seq(priv, {0x00, 0x02, 0x00, 0x00});
- else
- rc = send_seq(priv, {0x80, 0x02, 0x00, 0x00});
- if (rc < 0)
- goto ret;
-
- /* Return code shouldn't be checked.
- The reset CLK is needed only with tm6000.
- Driver should work fine even if this fails.
- */
- priv->tuner_callback(priv->video_dev, XC2028_RESET_CLK, 1);
-
- msleep(10);
-
- buf[0] = 0xff & (div >> 24);
- buf[1] = 0xff & (div >> 16);
- buf[2] = 0xff & (div >> 8);
- buf[3] = 0xff & (div);
-
- rc = i2c_send(priv, buf, sizeof(buf));
- if (rc < 0)
- goto ret;
- msleep(100);
-
- priv->frequency = freq;
-
- tuner_dbg("divisor= %02x %02x %02x %02x (freq=%d.%03d)\n",
- buf[0], buf[1], buf[2], buf[3],
- freq / 1000000, (freq % 1000000) / 1000);
-
- rc = 0;
-
-ret:
- mutex_unlock(&priv->lock);
-
- return rc;
-}
-
-static int xc2028_set_analog_freq(struct dvb_frontend *fe,
- struct analog_parameters *p)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- unsigned int type=0;
-
- tuner_dbg("%s called\n", __func__);
-
- if (p->mode == V4L2_TUNER_RADIO) {
- type |= FM;
- if (priv->ctrl.input1)
- type |= INPUT1;
- return generic_set_freq(fe, (625l * p->frequency) / 10,
- T_ANALOG_TV, type, 0, 0);
- }
-
- /* if std is not defined, choose one */
- if (!p->std)
- p->std = V4L2_STD_MN;
-
- /* PAL/M, PAL/N, PAL/Nc and NTSC variants should use 6MHz firmware */
- if (!(p->std & V4L2_STD_MN))
- type |= F8MHZ;
-
- /* Add audio hack to std mask */
- p->std |= parse_audio_std_option();
-
- return generic_set_freq(fe, 62500l * p->frequency,
- T_ANALOG_TV, type, p->std, 0);
-}
-
-static int xc2028_set_params(struct dvb_frontend *fe,
- struct dvb_frontend_parameters *p)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- unsigned int type=0;
- fe_bandwidth_t bw = BANDWIDTH_8_MHZ;
- u16 demod = 0;
-
- tuner_dbg("%s called\n", __func__);
-
- if (priv->ctrl.d2633)
- type |= D2633;
- else
- type |= D2620;
-
- switch(fe->ops.info.type) {
- case FE_OFDM:
- bw = p->u.ofdm.bandwidth;
- break;
- case FE_QAM:
- tuner_info("WARN: There are some reports that "
- "QAM 6 MHz doesn't work.\n"
- "If this works for you, please report by "
- "e-mail to: v4l-dvb-maintainer@linuxtv.org\n");
- bw = BANDWIDTH_6_MHZ;
- type |= QAM;
- break;
- case FE_ATSC:
- bw = BANDWIDTH_6_MHZ;
- /* The only ATSC firmware (at least on v2.7) is D2633,
- so overrides ctrl->d2633 */
- type |= ATSC| D2633;
- type &= ~D2620;
- break;
- /* DVB-S is not supported */
- default:
- return -EINVAL;
- }
-
- switch (bw) {
- case BANDWIDTH_8_MHZ:
- if (p->frequency < 470000000)
- priv->ctrl.vhfbw7 = 0;
- else
- priv->ctrl.uhfbw8 = 1;
- type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV8;
- type |= F8MHZ;
- break;
- case BANDWIDTH_7_MHZ:
- if (p->frequency < 470000000)
- priv->ctrl.vhfbw7 = 1;
- else
- priv->ctrl.uhfbw8 = 0;
- type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV7;
- type |= F8MHZ;
- break;
- case BANDWIDTH_6_MHZ:
- type |= DTV6;
- priv->ctrl.vhfbw7 = 0;
- priv->ctrl.uhfbw8 = 0;
- break;
- default:
- tuner_err("error: bandwidth not supported.\n");
- };
-
- /* All S-code tables need a 200kHz shift */
- if (priv->ctrl.demod)
- demod = priv->ctrl.demod + 200;
-
- return generic_set_freq(fe, p->frequency,
- T_DIGITAL_TV, type, 0, demod);
-}
-
-
-static int xc2028_dvb_release(struct dvb_frontend *fe)
-{
- struct xc2028_data *priv = fe->tuner_priv;
-
- tuner_dbg("%s called\n", __func__);
-
- mutex_lock(&xc2028_list_mutex);
-
- priv->count--;
-
- if (!priv->count) {
- list_del(&priv->xc2028_list);
-
- kfree(priv->ctrl.fname);
-
- free_firmware(priv);
- kfree(priv);
- fe->tuner_priv = NULL;
- }
-
- mutex_unlock(&xc2028_list_mutex);
-
- return 0;
-}
-
-static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct xc2028_data *priv = fe->tuner_priv;
-
- tuner_dbg("%s called\n", __func__);
-
- *frequency = priv->frequency;
-
- return 0;
-}
-
-static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- struct xc2028_ctrl *p = priv_cfg;
- int rc = 0;
-
- tuner_dbg("%s called\n", __func__);
-
- mutex_lock(&priv->lock);
-
- memcpy(&priv->ctrl, p, sizeof(priv->ctrl));
- if (priv->ctrl.max_len < 9)
- priv->ctrl.max_len = 13;
-
- if (p->fname) {
- if (priv->ctrl.fname && strcmp(p->fname, priv->ctrl.fname)) {
- kfree(priv->ctrl.fname);
- free_firmware(priv);
- }
-
- priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL);
- if (priv->ctrl.fname == NULL)
- rc = -ENOMEM;
- }
-
- mutex_unlock(&priv->lock);
-
- return rc;
-}
-
-static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
- .info = {
- .name = "Xceive XC3028",
- .frequency_min = 42000000,
- .frequency_max = 864000000,
- .frequency_step = 50000,
- },
-
- .set_config = xc2028_set_config,
- .set_analog_params = xc2028_set_analog_freq,
- .release = xc2028_dvb_release,
- .get_frequency = xc2028_get_frequency,
- .get_rf_strength = xc2028_signal,
- .set_params = xc2028_set_params,
-};
-
-struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
- struct xc2028_config *cfg)
-{
- struct xc2028_data *priv;
- void *video_dev;
-
- if (debug)
- printk(KERN_DEBUG "xc2028: Xcv2028/3028 init called!\n");
-
- if (NULL == cfg)
- return NULL;
-
- if (!fe) {
- printk(KERN_ERR "xc2028: No frontend!\n");
- return NULL;
- }
-
- video_dev = cfg->i2c_adap->algo_data;
-
- if (debug)
- printk(KERN_DEBUG "xc2028: video_dev =%p\n", video_dev);
-
- mutex_lock(&xc2028_list_mutex);
-
- list_for_each_entry(priv, &xc2028_list, xc2028_list) {
- if (&priv->i2c_props.adap->dev == &cfg->i2c_adap->dev) {
- video_dev = NULL;
- if (debug)
- printk(KERN_DEBUG "xc2028: reusing device\n");
-
- break;
- }
- }
-
- if (video_dev) {
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (priv == NULL) {
- mutex_unlock(&xc2028_list_mutex);
- return NULL;
- }
-
- priv->i2c_props.addr = cfg->i2c_addr;
- priv->i2c_props.adap = cfg->i2c_adap;
- priv->i2c_props.name = "xc2028";
-
- priv->video_dev = video_dev;
- priv->tuner_callback = cfg->callback;
- priv->ctrl.max_len = 13;
-
- mutex_init(&priv->lock);
-
- list_add_tail(&priv->xc2028_list, &xc2028_list);
- }
-
- fe->tuner_priv = priv;
- priv->count++;
-
- if (debug)
- printk(KERN_DEBUG "xc2028: usage count is %i\n", priv->count);
-
- memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops,
- sizeof(xc2028_dvb_tuner_ops));
-
- tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner");
-
- if (cfg->ctrl)
- xc2028_set_config(fe, cfg->ctrl);
-
- mutex_unlock(&xc2028_list_mutex);
-
- return fe;
-}
-
-EXPORT_SYMBOL(xc2028_attach);
-
-MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");
-MODULE_AUTHOR("Michel Ludwig <michel.ludwig@gmail.com>");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tuner-xc2028.h b/drivers/media/video/tuner-xc2028.h
deleted file mode 100644
index fc2f132a554..00000000000
--- a/drivers/media/video/tuner-xc2028.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* tuner-xc2028
- *
- * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org)
- * This code is placed under the terms of the GNU General Public License v2
- */
-
-#ifndef __TUNER_XC2028_H__
-#define __TUNER_XC2028_H__
-
-#include "dvb_frontend.h"
-
-#define XC2028_DEFAULT_FIRMWARE "xc3028-v27.fw"
-
-/* Dmoduler IF (kHz) */
-#define XC3028_FE_DEFAULT 0 /* Don't load SCODE */
-#define XC3028_FE_LG60 6000
-#define XC3028_FE_ATI638 6380
-#define XC3028_FE_OREN538 5380
-#define XC3028_FE_OREN36 3600
-#define XC3028_FE_TOYOTA388 3880
-#define XC3028_FE_TOYOTA794 7940
-#define XC3028_FE_DIBCOM52 5200
-#define XC3028_FE_ZARLINK456 4560
-#define XC3028_FE_CHINA 5200
-
-struct xc2028_ctrl {
- char *fname;
- int max_len;
- unsigned int scode_table;
- unsigned int mts :1;
- unsigned int d2633 :1;
- unsigned int input1:1;
- unsigned int vhfbw7:1;
- unsigned int uhfbw8:1;
- unsigned int demod;
-};
-
-struct xc2028_config {
- struct i2c_adapter *i2c_adap;
- u8 i2c_addr;
- void *video_dev;
- struct xc2028_ctrl *ctrl;
- int (*callback) (void *dev, int command, int arg);
-};
-
-/* xc2028 commands for callback */
-#define XC2028_TUNER_RESET 0
-#define XC2028_RESET_CLK 1
-
-#if defined(CONFIG_TUNER_XC2028) || (defined(CONFIG_TUNER_XC2028_MODULE) && defined(MODULE))
-extern struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
- struct xc2028_config *cfg);
-#else
-static inline struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
- struct xc2028_config *cfg)
-{
- printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
- __func__);
- return NULL;
-}
-#endif
-
-#endif /* __TUNER_XC2028_H__ */
diff --git a/drivers/media/video/usbvision/Kconfig b/drivers/media/video/usbvision/Kconfig
index fc24ef05b3f..74e1d3075a2 100644
--- a/drivers/media/video/usbvision/Kconfig
+++ b/drivers/media/video/usbvision/Kconfig
@@ -1,7 +1,7 @@
config VIDEO_USBVISION
tristate "USB video devices based on Nogatech NT1003/1004/1005"
depends on I2C && VIDEO_V4L2
- select VIDEO_TUNER
+ select MEDIA_TUNER
select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
---help---
There are more than 50 different USB video devices based on
diff --git a/drivers/media/video/usbvision/Makefile b/drivers/media/video/usbvision/Makefile
index 9ac92a80c64..33871875094 100644
--- a/drivers/media/video/usbvision/Makefile
+++ b/drivers/media/video/usbvision/Makefile
@@ -3,3 +3,4 @@ usbvision-objs := usbvision-core.o usbvision-video.o usbvision-i2c.o usbvision-
obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o
EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/common/tuners