summaryrefslogtreecommitdiffstats
path: root/drivers/media/video/saa7134
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/media/video/saa7134
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/media/video/saa7134')
-rw-r--r--drivers/media/video/saa7134/Makefile11
-rw-r--r--drivers/media/video/saa7134/saa6752hs.c543
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c2018
-rw-r--r--drivers/media/video/saa7134/saa7134-core.c1237
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c266
-rw-r--r--drivers/media/video/saa7134/saa7134-empress.c436
-rw-r--r--drivers/media/video/saa7134/saa7134-i2c.c453
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c491
-rw-r--r--drivers/media/video/saa7134/saa7134-oss.c857
-rw-r--r--drivers/media/video/saa7134/saa7134-reg.h366
-rw-r--r--drivers/media/video/saa7134/saa7134-ts.c243
-rw-r--r--drivers/media/video/saa7134/saa7134-tvaudio.c1031
-rw-r--r--drivers/media/video/saa7134/saa7134-vbi.c270
-rw-r--r--drivers/media/video/saa7134/saa7134-video.c2406
-rw-r--r--drivers/media/video/saa7134/saa7134.h618
15 files changed, 11246 insertions, 0 deletions
diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile
new file mode 100644
index 00000000000..e577a06b136
--- /dev/null
+++ b/drivers/media/video/saa7134/Makefile
@@ -0,0 +1,11 @@
+
+saa7134-objs := saa7134-cards.o saa7134-core.o saa7134-i2c.o \
+ saa7134-oss.o saa7134-ts.o saa7134-tvaudio.o \
+ saa7134-vbi.o saa7134-video.o saa7134-input.o
+
+obj-$(CONFIG_VIDEO_SAA7134) += saa7134.o saa7134-empress.o saa6752hs.o
+obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o
+
+EXTRA_CFLAGS += -I$(src)/..
+EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core
+EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/frontends
diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/video/saa7134/saa6752hs.c
new file mode 100644
index 00000000000..cee13584c9c
--- /dev/null
+++ b/drivers/media/video/saa7134/saa6752hs.c
@@ -0,0 +1,543 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/videodev.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+
+#include <media/id.h>
+
+#define MPEG_VIDEO_TARGET_BITRATE_MAX 27000
+#define MPEG_VIDEO_MAX_BITRATE_MAX 27000
+#define MPEG_TOTAL_TARGET_BITRATE_MAX 27000
+#define MPEG_PID_MAX ((1 << 14) - 1)
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = {0x20, I2C_CLIENT_END};
+static unsigned short normal_i2c_range[] = {I2C_CLIENT_END};
+I2C_CLIENT_INSMOD;
+
+MODULE_DESCRIPTION("device driver for saa6752hs MPEG2 encoder");
+MODULE_AUTHOR("Andrew de Quincey");
+MODULE_LICENSE("GPL");
+
+static struct i2c_driver driver;
+static struct i2c_client client_template;
+
+struct saa6752hs_state {
+ struct i2c_client client;
+ struct v4l2_mpeg_compression params;
+};
+
+enum saa6752hs_command {
+ SAA6752HS_COMMAND_RESET = 0,
+ SAA6752HS_COMMAND_STOP = 1,
+ SAA6752HS_COMMAND_START = 2,
+ SAA6752HS_COMMAND_PAUSE = 3,
+ SAA6752HS_COMMAND_RECONFIGURE = 4,
+ SAA6752HS_COMMAND_SLEEP = 5,
+ SAA6752HS_COMMAND_RECONFIGURE_FORCE = 6,
+
+ SAA6752HS_COMMAND_MAX
+};
+
+/* ---------------------------------------------------------------------- */
+
+static u8 PAT[] = {
+ 0xc2, // i2c register
+ 0x00, // table number for encoder
+
+ 0x47, // sync
+ 0x40, 0x00, // transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid(0)
+ 0x10, // transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0)
+
+ 0x00, // PSI pointer to start of table
+
+ 0x00, // tid(0)
+ 0xb0, 0x0d, // section_syntax_indicator(1), section_length(13)
+
+ 0x00, 0x01, // transport_stream_id(1)
+
+ 0xc1, // version_number(0), current_next_indicator(1)
+
+ 0x00, 0x00, // section_number(0), last_section_number(0)
+
+ 0x00, 0x01, // program_number(1)
+
+ 0xe0, 0x00, // PMT PID
+
+ 0x00, 0x00, 0x00, 0x00 // CRC32
+};
+
+static u8 PMT[] = {
+ 0xc2, // i2c register
+ 0x01, // table number for encoder
+
+ 0x47, // sync
+ 0x40, 0x00, // transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid
+ 0x10, // transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0)
+
+ 0x00, // PSI pointer to start of table
+
+ 0x02, // tid(2)
+ 0xb0, 0x17, // section_syntax_indicator(1), section_length(23)
+
+ 0x00, 0x01, // program_number(1)
+
+ 0xc1, // version_number(0), current_next_indicator(1)
+
+ 0x00, 0x00, // section_number(0), last_section_number(0)
+
+ 0xe0, 0x00, // PCR_PID
+
+ 0xf0, 0x00, // program_info_length(0)
+
+ 0x02, 0xe0, 0x00, 0xf0, 0x00, // video stream type(2), pid
+ 0x04, 0xe0, 0x00, 0xf0, 0x00, // audio stream type(4), pid
+
+ 0x00, 0x00, 0x00, 0x00 // CRC32
+};
+
+static struct v4l2_mpeg_compression param_defaults =
+{
+ .st_type = V4L2_MPEG_TS_2,
+ .st_bitrate = {
+ .mode = V4L2_BITRATE_CBR,
+ .target = 7000,
+ },
+
+ .ts_pid_pmt = 16,
+ .ts_pid_video = 260,
+ .ts_pid_audio = 256,
+ .ts_pid_pcr = 259,
+
+ .vi_type = V4L2_MPEG_VI_2,
+ .vi_aspect_ratio = V4L2_MPEG_ASPECT_4_3,
+ .vi_bitrate = {
+ .mode = V4L2_BITRATE_VBR,
+ .target = 4000,
+ .max = 6000,
+ },
+
+ .au_type = V4L2_MPEG_AU_2_II,
+ .au_bitrate = {
+ .mode = V4L2_BITRATE_CBR,
+ .target = 256,
+ },
+
+#if 0
+ /* FIXME: size? via S_FMT? */
+ .video_format = MPEG_VIDEO_FORMAT_D1,
+#endif
+};
+
+/* ---------------------------------------------------------------------- */
+
+static int saa6752hs_chip_command(struct i2c_client* client,
+ enum saa6752hs_command command)
+{
+ unsigned char buf[3];
+ unsigned long timeout;
+ int status = 0;
+
+ // execute the command
+ switch(command) {
+ case SAA6752HS_COMMAND_RESET:
+ buf[0] = 0x00;
+ break;
+
+ case SAA6752HS_COMMAND_STOP:
+ buf[0] = 0x03;
+ break;
+
+ case SAA6752HS_COMMAND_START:
+ buf[0] = 0x02;
+ break;
+
+ case SAA6752HS_COMMAND_PAUSE:
+ buf[0] = 0x04;
+ break;
+
+ case SAA6752HS_COMMAND_RECONFIGURE:
+ buf[0] = 0x05;
+ break;
+
+ case SAA6752HS_COMMAND_SLEEP:
+ buf[0] = 0x06;
+ break;
+
+ case SAA6752HS_COMMAND_RECONFIGURE_FORCE:
+ buf[0] = 0x07;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ // set it and wait for it to be so
+ i2c_master_send(client, buf, 1);
+ timeout = jiffies + HZ * 3;
+ for (;;) {
+ // get the current status
+ buf[0] = 0x10;
+ i2c_master_send(client, buf, 1);
+ i2c_master_recv(client, buf, 1);
+
+ if (!(buf[0] & 0x20))
+ break;
+ if (time_after(jiffies,timeout)) {
+ status = -ETIMEDOUT;
+ break;
+ }
+
+ // wait a bit
+ msleep(10);
+ }
+
+ // delay a bit to let encoder settle
+ msleep(50);
+
+ // done
+ return status;
+}
+
+
+static int saa6752hs_set_bitrate(struct i2c_client* client,
+ struct v4l2_mpeg_compression* params)
+{
+ u8 buf[3];
+
+ // set the bitrate mode
+ buf[0] = 0x71;
+ buf[1] = (params->vi_bitrate.mode == V4L2_BITRATE_VBR) ? 0 : 1;
+ i2c_master_send(client, buf, 2);
+
+ // set the video bitrate
+ if (params->vi_bitrate.mode == V4L2_BITRATE_VBR) {
+ // set the target bitrate
+ buf[0] = 0x80;
+ buf[1] = params->vi_bitrate.target >> 8;
+ buf[2] = params->vi_bitrate.target & 0xff;
+ i2c_master_send(client, buf, 3);
+
+ // set the max bitrate
+ buf[0] = 0x81;
+ buf[1] = params->vi_bitrate.max >> 8;
+ buf[2] = params->vi_bitrate.max & 0xff;
+ i2c_master_send(client, buf, 3);
+ } else {
+ // set the target bitrate (no max bitrate for CBR)
+ buf[0] = 0x81;
+ buf[1] = params->vi_bitrate.target >> 8;
+ buf[2] = params->vi_bitrate.target & 0xff;
+ i2c_master_send(client, buf, 3);
+ }
+
+ // set the audio bitrate
+ buf[0] = 0x94;
+ buf[1] = (256 == params->au_bitrate.target) ? 0 : 1;
+ i2c_master_send(client, buf, 2);
+
+ // set the total bitrate
+ buf[0] = 0xb1;
+ buf[1] = params->st_bitrate.target >> 8;
+ buf[2] = params->st_bitrate.target & 0xff;
+ i2c_master_send(client, buf, 3);
+
+ // return success
+ return 0;
+}
+
+
+static void saa6752hs_set_params(struct i2c_client* client,
+ struct v4l2_mpeg_compression* params)
+{
+ struct saa6752hs_state *h = i2c_get_clientdata(client);
+
+ /* check PIDs */
+ if (params->ts_pid_pmt <= MPEG_PID_MAX)
+ h->params.ts_pid_pmt = params->ts_pid_pmt;
+ if (params->ts_pid_pcr <= MPEG_PID_MAX)
+ h->params.ts_pid_pcr = params->ts_pid_pcr;
+ if (params->ts_pid_video <= MPEG_PID_MAX)
+ h->params.ts_pid_video = params->ts_pid_video;
+ if (params->ts_pid_audio <= MPEG_PID_MAX)
+ h->params.ts_pid_audio = params->ts_pid_audio;
+
+ /* check bitrate parameters */
+ if ((params->vi_bitrate.mode == V4L2_BITRATE_CBR) ||
+ (params->vi_bitrate.mode == V4L2_BITRATE_VBR))
+ h->params.vi_bitrate.mode = params->vi_bitrate.mode;
+ if (params->vi_bitrate.mode != V4L2_BITRATE_NONE)
+ h->params.st_bitrate.target = params->st_bitrate.target;
+ if (params->vi_bitrate.mode != V4L2_BITRATE_NONE)
+ h->params.vi_bitrate.target = params->vi_bitrate.target;
+ if (params->vi_bitrate.mode == V4L2_BITRATE_VBR)
+ h->params.vi_bitrate.max = params->vi_bitrate.max;
+ if (params->au_bitrate.mode != V4L2_BITRATE_NONE)
+ h->params.au_bitrate.target = params->au_bitrate.target;
+
+ /* aspect ratio */
+ if (params->vi_aspect_ratio == V4L2_MPEG_ASPECT_4_3 ||
+ params->vi_aspect_ratio == V4L2_MPEG_ASPECT_16_9)
+ h->params.vi_aspect_ratio = params->vi_aspect_ratio;
+
+ /* range checks */
+ if (h->params.st_bitrate.target > MPEG_TOTAL_TARGET_BITRATE_MAX)
+ h->params.st_bitrate.target = MPEG_TOTAL_TARGET_BITRATE_MAX;
+ if (h->params.vi_bitrate.target > MPEG_VIDEO_TARGET_BITRATE_MAX)
+ h->params.vi_bitrate.target = MPEG_VIDEO_TARGET_BITRATE_MAX;
+ if (h->params.vi_bitrate.max > MPEG_VIDEO_MAX_BITRATE_MAX)
+ h->params.vi_bitrate.max = MPEG_VIDEO_MAX_BITRATE_MAX;
+ if (h->params.au_bitrate.target <= 256)
+ h->params.au_bitrate.target = 256;
+ else
+ h->params.au_bitrate.target = 384;
+}
+
+static int saa6752hs_init(struct i2c_client* client)
+{
+ unsigned char buf[9], buf2[4];
+ struct saa6752hs_state *h;
+ u32 crc;
+ unsigned char localPAT[256];
+ unsigned char localPMT[256];
+
+ h = i2c_get_clientdata(client);
+
+ // Set video format - must be done first as it resets other settings
+ buf[0] = 0x41;
+ buf[1] = 0 /* MPEG_VIDEO_FORMAT_D1 */;
+ i2c_master_send(client, buf, 2);
+
+ // set bitrate
+ saa6752hs_set_bitrate(client, &h->params);
+
+ // Set GOP structure {3, 13}
+ buf[0] = 0x72;
+ buf[1] = 0x03;
+ buf[2] = 0x0D;
+ i2c_master_send(client,buf,3);
+
+ // Set minimum Q-scale {4}
+ buf[0] = 0x82;
+ buf[1] = 0x04;
+ i2c_master_send(client,buf,2);
+
+ // Set maximum Q-scale {12}
+ buf[0] = 0x83;
+ buf[1] = 0x0C;
+ i2c_master_send(client,buf,2);
+
+ // Set Output Protocol
+ buf[0] = 0xD0;
+ buf[1] = 0x81;
+ i2c_master_send(client,buf,2);
+
+ // Set video output stream format {TS}
+ buf[0] = 0xB0;
+ buf[1] = 0x05;
+ i2c_master_send(client,buf,2);
+
+ /* compute PAT */
+ memcpy(localPAT, PAT, sizeof(PAT));
+ localPAT[17] = 0xe0 | ((h->params.ts_pid_pmt >> 8) & 0x0f);
+ localPAT[18] = h->params.ts_pid_pmt & 0xff;
+ crc = crc32_be(~0, &localPAT[7], sizeof(PAT) - 7 - 4);
+ localPAT[sizeof(PAT) - 4] = (crc >> 24) & 0xFF;
+ localPAT[sizeof(PAT) - 3] = (crc >> 16) & 0xFF;
+ localPAT[sizeof(PAT) - 2] = (crc >> 8) & 0xFF;
+ localPAT[sizeof(PAT) - 1] = crc & 0xFF;
+
+ /* compute PMT */
+ memcpy(localPMT, PMT, sizeof(PMT));
+ localPMT[3] = 0x40 | ((h->params.ts_pid_pmt >> 8) & 0x0f);
+ localPMT[4] = h->params.ts_pid_pmt & 0xff;
+ localPMT[15] = 0xE0 | ((h->params.ts_pid_pcr >> 8) & 0x0F);
+ localPMT[16] = h->params.ts_pid_pcr & 0xFF;
+ localPMT[20] = 0xE0 | ((h->params.ts_pid_video >> 8) & 0x0F);
+ localPMT[21] = h->params.ts_pid_video & 0xFF;
+ localPMT[25] = 0xE0 | ((h->params.ts_pid_audio >> 8) & 0x0F);
+ localPMT[26] = h->params.ts_pid_audio & 0xFF;
+ crc = crc32_be(~0, &localPMT[7], sizeof(PMT) - 7 - 4);
+ localPMT[sizeof(PMT) - 4] = (crc >> 24) & 0xFF;
+ localPMT[sizeof(PMT) - 3] = (crc >> 16) & 0xFF;
+ localPMT[sizeof(PMT) - 2] = (crc >> 8) & 0xFF;
+ localPMT[sizeof(PMT) - 1] = crc & 0xFF;
+
+ // Set Audio PID
+ buf[0] = 0xC1;
+ buf[1] = (h->params.ts_pid_audio >> 8) & 0xFF;
+ buf[2] = h->params.ts_pid_audio & 0xFF;
+ i2c_master_send(client,buf,3);
+
+ // Set Video PID
+ buf[0] = 0xC0;
+ buf[1] = (h->params.ts_pid_video >> 8) & 0xFF;
+ buf[2] = h->params.ts_pid_video & 0xFF;
+ i2c_master_send(client,buf,3);
+
+ // Set PCR PID
+ buf[0] = 0xC4;
+ buf[1] = (h->params.ts_pid_pcr >> 8) & 0xFF;
+ buf[2] = h->params.ts_pid_pcr & 0xFF;
+ i2c_master_send(client,buf,3);
+
+ // Send SI tables
+ i2c_master_send(client,localPAT,sizeof(PAT));
+ i2c_master_send(client,localPMT,sizeof(PMT));
+
+ // mute then unmute audio. This removes buzzing artefacts
+ buf[0] = 0xa4;
+ buf[1] = 1;
+ i2c_master_send(client, buf, 2);
+ buf[1] = 0;
+ i2c_master_send(client, buf, 2);
+
+ // start it going
+ saa6752hs_chip_command(client, SAA6752HS_COMMAND_START);
+
+ // readout current state
+ buf[0] = 0xE1;
+ buf[1] = 0xA7;
+ buf[2] = 0xFE;
+ buf[3] = 0x82;
+ buf[4] = 0xB0;
+ i2c_master_send(client, buf, 5);
+ i2c_master_recv(client, buf2, 4);
+
+ // change aspect ratio
+ buf[0] = 0xE0;
+ buf[1] = 0xA7;
+ buf[2] = 0xFE;
+ buf[3] = 0x82;
+ buf[4] = 0xB0;
+ buf[5] = buf2[0];
+ switch(h->params.vi_aspect_ratio) {
+ case V4L2_MPEG_ASPECT_16_9:
+ buf[6] = buf2[1] | 0x40;
+ break;
+ case V4L2_MPEG_ASPECT_4_3:
+ default:
+ buf[6] = buf2[1] & 0xBF;
+ break;
+ break;
+ }
+ buf[7] = buf2[2];
+ buf[8] = buf2[3];
+ i2c_master_send(client, buf, 9);
+
+ // return success
+ return 0;
+}
+
+static int saa6752hs_attach(struct i2c_adapter *adap, int addr, int kind)
+{
+ struct saa6752hs_state *h;
+
+ printk("saa6752hs: chip found @ 0x%x\n", addr<<1);
+
+ if (NULL == (h = kmalloc(sizeof(*h), GFP_KERNEL)))
+ return -ENOMEM;
+ memset(h,0,sizeof(*h));
+ h->client = client_template;
+ h->params = param_defaults;
+ h->client.adapter = adap;
+ h->client.addr = addr;
+
+ i2c_set_clientdata(&h->client, h);
+ i2c_attach_client(&h->client);
+ return 0;
+}
+
+static int saa6752hs_probe(struct i2c_adapter *adap)
+{
+ if (adap->class & I2C_CLASS_TV_ANALOG)
+ return i2c_probe(adap, &addr_data, saa6752hs_attach);
+ return 0;
+}
+
+static int saa6752hs_detach(struct i2c_client *client)
+{
+ struct saa6752hs_state *h;
+
+ h = i2c_get_clientdata(client);
+ i2c_detach_client(client);
+ kfree(h);
+ return 0;
+}
+
+static int
+saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+ struct saa6752hs_state *h = i2c_get_clientdata(client);
+ struct v4l2_mpeg_compression *params = arg;
+ int err = 0;
+
+ switch (cmd) {
+ case VIDIOC_S_MPEGCOMP:
+ if (NULL == params) {
+ /* apply settings and start encoder */
+ saa6752hs_init(client);
+ break;
+ }
+ saa6752hs_set_params(client, params);
+ /* fall through */
+ case VIDIOC_G_MPEGCOMP:
+ *params = h->params;
+ break;
+ default:
+ /* nothing */
+ break;
+ }
+
+ return err;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_driver driver = {
+ .owner = THIS_MODULE,
+ .name = "i2c saa6752hs MPEG encoder",
+ .id = I2C_DRIVERID_SAA6752HS,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = saa6752hs_probe,
+ .detach_client = saa6752hs_detach,
+ .command = saa6752hs_command,
+};
+
+static struct i2c_client client_template =
+{
+ I2C_DEVNAME("saa6752hs"),
+ .flags = I2C_CLIENT_ALLOW_USE,
+ .driver = &driver,
+};
+
+static int __init saa6752hs_init_module(void)
+{
+ return i2c_add_driver(&driver);
+}
+
+static void __exit saa6752hs_cleanup_module(void)
+{
+ i2c_del_driver(&driver);
+}
+
+module_init(saa6752hs_init_module);
+module_exit(saa6752hs_cleanup_module);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
new file mode 100644
index 00000000000..180d3175ea5
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -0,0 +1,2018 @@
+
+/*
+ * $Id: saa7134-cards.c,v 1.54 2005/03/07 12:01:51 kraxel Exp $
+ *
+ * device driver for philips saa7134 based TV cards
+ * card-specific stuff.
+ *
+ * (c) 2001-04 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+/* commly used strings */
+static char name_mute[] = "mute";
+static char name_radio[] = "Radio";
+static char name_tv[] = "Television";
+static char name_tv_mono[] = "TV (mono only)";
+static char name_comp1[] = "Composite1";
+static char name_comp2[] = "Composite2";
+static char name_comp3[] = "Composite3";
+static char name_comp4[] = "Composite4";
+static char name_svideo[] = "S-Video";
+
+/* ------------------------------------------------------------------ */
+/* board config info */
+
+struct saa7134_board saa7134_boards[] = {
+ [SAA7134_BOARD_UNKNOWN] = {
+ .name = "UNKNOWN/GENERIC",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_ABSENT,
+ .inputs = {{
+ .name = "default",
+ .vmux = 0,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_PROTEUS_PRO] = {
+ /* /me */
+ .name = "Proteus Pro [philips reference design]",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_FLYVIDEO3000] = {
+ /* "Marco d'Itri" <md@Linux.IT> */
+ .name = "LifeView FlyVIDEO3000",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .gpiomask = 0xe000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .gpio = 0x8000,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .gpio = 0x0000,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x2000,
+ },
+ },
+ [SAA7134_BOARD_FLYVIDEO2000] = {
+ /* "TC Wan" <tcwan@cs.usm.my> */
+ .name = "LifeView FlyVIDEO2000",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .gpiomask = 0xe000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .gpio = 0x0000,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x2000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE2,
+ .gpio = 0x8000,
+ },
+ },
+ [SAA7134_BOARD_FLYTVPLATINUM_MINI] = {
+ /* "Arnaud Quette" <aquette@free.fr> */
+ .name = "LifeView FlyTV Platinum Mini",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_FLYTVPLATINUM_FM] = {
+ /* LifeView FlyTV Platinum FM (LR214WF) */
+ /* "Peter Missel <peter.missel@onlinehome.de> */
+ .name = "LifeView FlyTV Platinum FM",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+// .gpiomask = 0xe000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+// .gpio = 0x0000,
+ .tv = 1,
+ },{
+/* .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .gpio = 0x0000,
+ .tv = 1,
+ },{
+*/ .name = name_comp1, /* Composite signal on S-Video input */
+ .vmux = 0,
+ .amux = LINE2,
+// .gpio = 0x4000,
+ },{
+ .name = name_comp2, /* Composite input */
+ .vmux = 3,
+ .amux = LINE2,
+// .gpio = 0x4000,
+ },{
+ .name = name_svideo, /* S-Video signal on S-Video input */
+ .vmux = 8,
+ .amux = LINE2,
+// .gpio = 0x4000,
+ }},
+/* .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x2000,
+ },
+*/ },
+ [SAA7134_BOARD_EMPRESS] = {
+ /* "Gert Vervoort" <gert.vervoort@philips.com> */
+ .name = "EMPRESS",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ .mpeg = SAA7134_MPEG_EMPRESS,
+ .video_out = CCIR656,
+ },
+ [SAA7134_BOARD_MONSTERTV] = {
+ /* "K.Ohta" <alpha292@bremen.or.jp> */
+ .name = "SKNet Monster TV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_MD9717] = {
+ .name = "Tevion MD 9717",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ /* workaround for problems with normal TV sound */
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 2,
+ .amux = LINE1,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_TVSTATION_RDS] = {
+ /* Typhoon TV Tuner RDS: Art.Nr. 50694 */
+ .name = "KNC One TV-Station RDS / Typhoon TV Tuner RDS",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+
+ .name = "CVid over SVid",
+ .vmux = 0,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_TVSTATION_DVR] = {
+ .name = "KNC One TV-Station DVR",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x820000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ .gpio = 0x20000,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x20000,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x20000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x20000,
+ },
+ .mpeg = SAA7134_MPEG_EMPRESS,
+ .video_out = CCIR656,
+ },
+ [SAA7134_BOARD_CINERGY400] = {
+ .name = "Terratec Cinergy 400 TV",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp2, // CVideo over SVideo Connector
+ .vmux = 0,
+ .amux = LINE1,
+ }}
+ },
+ [SAA7134_BOARD_MD5044] = {
+ .name = "Medion 5044",
+ .audio_clock = 0x00187de7, // was: 0x00200000,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ /* workaround for problems with normal TV sound */
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_KWORLD] = {
+ .name = "Kworld/KuroutoShikou SAA7130-TVPCI",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_CINERGY600] = {
+ .name = "Terratec Cinergy 600 TV",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp2, // CVideo over SVideo Connector
+ .vmux = 0,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_MD7134] = {
+ .name = "Medion 7134",
+ //.audio_clock = 0x00200000,
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_TYPHOON_90031] = {
+ /* aka Typhoon "TV+Radio", Art.Nr 90031 */
+ /* Tom Zoerner <tomzo at users sourceforge net> */
+ .name = "Typhoon TV+Radio 90031",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_ELSA] = {
+ .name = "ELSA EX-VISION 300TV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_HITACHI_NTSC,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 4,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_ELSA_500TV] = {
+ .name = "ELSA EX-VISION 500TV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_HITACHI_NTSC,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 7,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 8,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 8,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_ASUSTeK_TVFM7134] = {
+ .name = "ASUS TV-FM 7134",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ },
+ },
+ [SAA7135_BOARD_ASUSTeK_TVFM7135] = {
+ .name = "ASUS TV-FM 7135",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .gpiomask = 0x200000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .gpio = 0x0000,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE2,
+ .gpio = 0x0000,
+ },{
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE2,
+ .gpio = 0x0000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x200000,
+ },
+ },
+ [SAA7134_BOARD_VA1000POWER] = {
+ .name = "AOPEN VA1000 POWER",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_10MOONSTVMASTER] = {
+ /* "lilicheng" <llc@linuxfans.org> */
+ .name = "10MOONS PCI TV CAPTURE CARD",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .gpiomask = 0xe000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .gpio = 0x0000,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ .gpio = 0x4000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x2000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE2,
+ .gpio = 0x8000,
+ },
+ },
+ [SAA7134_BOARD_BMK_MPEX_NOTUNER] = {
+ /* "Andrew de Quincey" <adq@lidskialf.net> */
+ .name = "BMK MPEX No Tuner",
+ .audio_clock = 0x200000,
+ .tuner_type = TUNER_ABSENT,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE1,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_comp3,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_comp4,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ .mpeg = SAA7134_MPEG_EMPRESS,
+ .video_out = CCIR656,
+ },
+ [SAA7134_BOARD_VIDEOMATE_TV] = {
+ .name = "Compro VideoMate TV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS] = {
+ .name = "Compro VideoMate TV Gold+",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .gpiomask = 0x800c0000,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x06c00012,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x0ac20012,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .gpio = 0x08c20012,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_CRONOS_PLUS] = {
+ /* gpio pins:
+ 0 .. 3 BASE_ID
+ 4 .. 7 PROTECT_ID
+ 8 .. 11 USER_OUT
+ 12 .. 13 USER_IN
+ 14 .. 15 VIDIN_SEL */
+ .name = "Matrox CronosPlus",
+ .tuner_type = TUNER_ABSENT,
+ .gpiomask = 0xcf00,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 0,
+ .gpio = 2 << 14,
+ },{
+ .name = name_comp2,
+ .vmux = 0,
+ .gpio = 1 << 14,
+ },{
+ .name = name_comp3,
+ .vmux = 0,
+ .gpio = 0 << 14,
+ },{
+ .name = name_comp4,
+ .vmux = 0,
+ .gpio = 3 << 14,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .gpio = 2 << 14,
+ }},
+ },
+ [SAA7134_BOARD_MD2819] = {
+ .name = "AverMedia M156 / Medion 2819",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_BMK_MPEX_TUNER] = {
+ /* "Greg Wickham <greg.wickham@grangenet.net> */
+ .name = "BMK MPEX Tuner",
+ .audio_clock = 0x200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }},
+ .mpeg = SAA7134_MPEG_EMPRESS,
+ .video_out = CCIR656,
+ },
+ [SAA7134_BOARD_ASUSTEK_TVFM7133] = {
+ .name = "ASUS TV-FM 7133",
+ .audio_clock = 0x00187de7,
+ // probably wrong, the 7133 one is the NTSC version ...
+ // .tuner_type = TUNER_PHILIPS_FM1236_MK3
+ .tuner_type = TUNER_LG_NTSC_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_PINNACLE_PCTV_STEREO] = {
+ .name = "Pinnacle PCTV Stereo (saa7134)",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_MT2032,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 1,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_MANLI_MTV002] = {
+ /* Ognjen Nastic <ognjen@logosoft.ba> */
+ .name = "Manli MuchTV M-TV002",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_MANLI_MTV001] = {
+ /* Ognjen Nastic <ognjen@logosoft.ba> UNTESTED */
+ .name = "Manli MuchTV M-TV001",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_TG3000TV] = {
+ /* TransGear 3000TV */
+ .name = "Nagase Sangyo TransGear 3000TV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_ECS_TVP3XP] = {
+ .name = "Elitegroup ECS TVP3XP FM1216 Tuner Card(PAL-BG,FM) ",
+ .audio_clock = 0x187de7, // xtal 32.1 MHz
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = "CVid over SVid",
+ .vmux = 0,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_ECS_TVP3XP_4CB5] = {
+ .name = "Elitegroup ECS TVP3XP FM1236 Tuner Card (NTSC,FM)",
+ .audio_clock = 0x187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = "CVid over SVid",
+ .vmux = 0,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_AVACSSMARTTV] = {
+ /* Roman Pszonczenko <romka@kolos.math.uni.lodz.pl> */
+ .name = "AVACS SmartTV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x200000,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER] = {
+ /* Michael Smith <msmith@cbnco.com> */
+ .name = "AVerMedia DVD EZMaker",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_ABSENT,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 3,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ }},
+ },
+ [SAA7134_BOARD_NOVAC_PRIMETV7133] = {
+ /* toshii@netbsd.org */
+ .name = "Noval Prime TV 7133",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_ALPS_TSBH1_NTSC,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 3,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ }},
+ },
+ [SAA7134_BOARD_AVERMEDIA_STUDIO_305] = {
+ .name = "AverMedia AverTV Studio 305",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1256_IH3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x3,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ },
+ [SAA7133_BOARD_UPMOST_PURPLE_TV] = {
+ .name = "UPMOST PURPLE TV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1236_MK3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 7,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_svideo,
+ .vmux = 7,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_ITEMS_MTV005] = {
+ /* Norman Jonas <normanjonas@arcor.de> */
+ .name = "Items MuchTV Plus / IT-005",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .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,
+ },
+ },
+ [SAA7134_BOARD_CINERGY200] = {
+ .name = "Terratec Cinergy 200 TV",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp2, // CVideo over SVideo Connector
+ .vmux = 0,
+ .amux = LINE1,
+ }},
+ .mute = {
+ .name = name_mute,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_VIDEOMATE_TV_PVR] = {
+ /* Alain St-Denis <alain@topaze.homeip.net> */
+ .name = "Compro VideoMate TV PVR/FM",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .gpiomask = 0x808c0080,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ .gpio = 0x00080,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x00080,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2_LEFT,
+ .tv = 1,
+ .gpio = 0x00080,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x80000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE2,
+ .gpio = 0x40000,
+ },
+ },
+ [SAA7134_BOARD_SABRENT_SBTTVFM] = {
+ /* Michael Rodriguez-Torrent <mrtorrent@asu.edu> */
+ .name = "Sabrent SBT-TVFM (saa7130)",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE2,
+ },{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_ZOLID_XPERT_TV7134] = {
+ /* Helge Jensen <helge.jensen@slog.dk> */
+ .name = ":Zolid Xpert TV7134",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ },
+ [SAA7134_BOARD_EMPIRE_PCI_TV_RADIO_LE] = {
+ /* "Matteo Az" <matte.az@nospam.libero.it> ;-) */
+ .name = "Empire PCI TV-Radio LE",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .gpiomask = 0x4000,
+ .inputs = {{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .gpio = 0x8000,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x8000,
+ },{
+ .name = name_svideo,
+ .vmux = 6,
+ .amux = LINE1,
+ .gpio = 0x8000,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ .gpio = 0x8000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ .gpio =0x8000,
+ }
+ },
+ [SAA7134_BOARD_AVERMEDIA_307] = {
+ /*
+ Nickolay V. Shmyrev <nshmyrev@yandex.ru>
+ Lots of thanks to Andrey Zolotarev <zolotarev_andrey@mail.ru>
+ */
+ .name = "Avermedia AVerTV Studio 307",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1256_IH3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x03,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x00,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ .gpio = 0x00,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ .gpio = 0x00,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ .gpio = 0x00,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ .gpio = 0x01,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_CARDBUS] = {
+ /* Jon Westgate <oryn@oryn.fsck.tv> */
+ .name = "AVerMedia Cardbus TV/Radio",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_CINERGY400_CARDBUS] = {
+ .name = "Terratec Cinergy 400 mobile",
+ .audio_clock = 0x187de7,
+ .tuner_type = TUNER_ALPS_TSBE5_PAL,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_CINERGY600_MK3] = {
+ .name = "Terratec Cinergy 600 TV MK3",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 4,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ },{
+ .name = name_comp2, // CVideo over SVideo Connector
+ .vmux = 0,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_VIDEOMATE_GOLD_PLUS] = {
+ /* Dylan Walkden <dylan_walkden@hotmail.com> */
+ .name = "Compro VideoMate Gold+ Pal",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .gpiomask = 0x1ce780,
+ .inputs = {{
+ .name = name_svideo,
+ .vmux = 0, // CVideo over SVideo Connector - ok?
+ .amux = LINE1,
+ .gpio = 0x008080,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ .gpio = 0x008080,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ .gpio = 0x008080,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x80000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = LINE2,
+ .gpio = 0x0c8000,
+ },
+ },
+ [SAA7134_BOARD_PINNACLE_300I_DVBT_PAL] = {
+ .name = "Pinnacle PCTV 300i DVB-T + PAL",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_MT2032,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 1,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_PROVIDEO_PV952] = {
+ /* andreas.kretschmer@web.de */
+ .name = "ProVideo PV952",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ },{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_tv_mono,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ },
+ [SAA7134_BOARD_AVERMEDIA_305] = {
+ /* much like the "studio" version but without radio
+ * and another tuner (sirspiritus@yandex.ru) */
+ .name = "AverMedia AverTV/305",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FQ1216ME,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x3,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ },
+ [SAA7134_BOARD_FLYDVBTDUO] = {
+ /* LifeView FlyDVB-T DUO */
+ /* "Nico Sabbi <nsabbi@tiscali.it> */
+ .name = "LifeView FlyDVB-T DUO",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+// .gpiomask = 0xe000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+// .gpio = 0x0000,
+ .tv = 1,
+ },{
+ .name = name_comp1, /* Composite signal on S-Video input */
+ .vmux = 0,
+ .amux = LINE2,
+// .gpio = 0x4000,
+ },{
+ .name = name_comp2, /* Composite input */
+ .vmux = 3,
+ .amux = LINE2,
+// .gpio = 0x4000,
+ },{
+ .name = name_svideo, /* S-Video signal on S-Video input */
+ .vmux = 8,
+ .amux = LINE2,
+// .gpio = 0x4000,
+ }},
+ },
+};
+const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
+
+/* ------------------------------------------------------------------ */
+/* PCI ids + subsystem IDs */
+
+struct pci_device_id saa7134_pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0x2001,
+ .driver_data = SAA7134_BOARD_PROTEUS_PRO,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0x2001,
+ .driver_data = SAA7134_BOARD_PROTEUS_PRO,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0x6752,
+ .driver_data = SAA7134_BOARD_EMPRESS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1131,
+ .subdevice = 0x4e85,
+ .driver_data = SAA7134_BOARD_MONSTERTV,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x153B,
+ .subdevice = 0x1142,
+ .driver_data = SAA7134_BOARD_CINERGY400,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x153B,
+ .subdevice = 0x1143,
+ .driver_data = SAA7134_BOARD_CINERGY600,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x153B,
+ .subdevice = 0x1158,
+ .driver_data = SAA7134_BOARD_CINERGY600_MK3,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x153b,
+ .subdevice = 0x1162,
+ .driver_data = SAA7134_BOARD_CINERGY400_CARDBUS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x5168,
+ .subdevice = 0x0138,
+ .driver_data = SAA7134_BOARD_FLYVIDEO3000,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x4e42, //"Typhoon PCI Capture TV Card" Art.No. 50673
+ .subdevice = 0x0138,
+ .driver_data = SAA7134_BOARD_FLYVIDEO3000,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x5168,
+ .subdevice = 0x0138,
+ .driver_data = SAA7134_BOARD_FLYVIDEO2000,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7135,
+ .subvendor = 0x5168,
+ .subdevice = 0x0212, /* minipci, LR212 */
+ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5168,
+ .subdevice = 0x0214, /* Standard PCI, LR214WF */
+ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x16be,
+ .subdevice = 0x0003,
+ .driver_data = SAA7134_BOARD_MD7134,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1048,
+ .subdevice = 0x226b,
+ .driver_data = SAA7134_BOARD_ELSA,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1048,
+ .subdevice = 0x226b,
+ .driver_data = SAA7134_BOARD_ELSA_500TV,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = PCI_VENDOR_ID_ASUSTEK,
+ .subdevice = 0x4842,
+ .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = PCI_VENDOR_ID_ASUSTEK,
+ .subdevice = 0x4845,
+ .driver_data = SAA7135_BOARD_ASUSTeK_TVFM7135,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = PCI_VENDOR_ID_ASUSTEK,
+ .subdevice = 0x4830,
+ .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = PCI_VENDOR_ID_ASUSTEK,
+ .subdevice = 0x4843,
+ .driver_data = SAA7134_BOARD_ASUSTEK_TVFM7133,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = PCI_VENDOR_ID_ASUSTEK,
+ .subdevice = 0x4840,
+ .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0xfe01,
+ .driver_data = SAA7134_BOARD_TVSTATION_RDS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1894,
+ .subdevice = 0xfe01,
+ .driver_data = SAA7134_BOARD_TVSTATION_RDS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1894,
+ .subdevice = 0xa006,
+ .driver_data = SAA7134_BOARD_TVSTATION_DVR,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1131,
+ .subdevice = 0x7133,
+ .driver_data = SAA7134_BOARD_VA1000POWER,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0x2001,
+ .driver_data = SAA7134_BOARD_10MOONSTVMASTER,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x185b,
+ .subdevice = 0xc100,
+ .driver_data = SAA7134_BOARD_VIDEOMATE_TV,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x185b,
+ .subdevice = 0xc100,
+ .driver_data = SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = PCI_VENDOR_ID_MATROX,
+ .subdevice = 0x48d0,
+ .driver_data = SAA7134_BOARD_CRONOS_PLUS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xa70b,
+ .driver_data = SAA7134_BOARD_MD2819,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0x2115,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_305,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0x2108,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_305,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0x10ff,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER,
+ },{
+ /* AVerMedia CardBus */
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xd6ee,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS,
+ },{
+ /* TransGear 3000TV */
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0x050c,
+ .driver_data = SAA7134_BOARD_TG3000TV,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x11bd,
+ .subdevice = 0x002b,
+ .driver_data = SAA7134_BOARD_PINNACLE_PCTV_STEREO,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x11bd,
+ .subdevice = 0x002d,
+ .driver_data = SAA7134_BOARD_PINNACLE_300I_DVBT_PAL,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1019,
+ .subdevice = 0x4cb4,
+ .driver_data = SAA7134_BOARD_ECS_TVP3XP,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1019,
+ .subdevice = 0x4cb5,
+ .driver_data = SAA7134_BOARD_ECS_TVP3XP_4CB5,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x12ab,
+ .subdevice = 0x0800,
+ .driver_data = SAA7133_BOARD_UPMOST_PURPLE_TV,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x153B,
+ .subdevice = 0x1152,
+ .driver_data = SAA7134_BOARD_CINERGY200,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x185b,
+ .subdevice = 0xc100,
+ .driver_data = SAA7134_BOARD_VIDEOMATE_TV_PVR,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1131,
+ .subdevice = 0,
+ .driver_data = SAA7134_BOARD_SABRENT_SBTTVFM,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0x9715,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_307,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x185b,
+ .subdevice = 0xc200,
+ .driver_data = SAA7134_BOARD_VIDEOMATE_GOLD_PLUS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x1540,
+ .subdevice = 0x9524,
+ .driver_data = SAA7134_BOARD_PROVIDEO_PV952,
+
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x5168,
+ .subdevice = 0x0306,
+ .driver_data = SAA7134_BOARD_FLYDVBTDUO,
+
+ },{
+ /* --- boards without eeprom + subsystem ID --- */
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0,
+ .driver_data = SAA7134_BOARD_NOAUTO,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = PCI_VENDOR_ID_PHILIPS,
+ .subdevice = 0,
+ .driver_data = SAA7134_BOARD_NOAUTO,
+ },{
+
+ /* --- default catch --- */
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = SAA7134_BOARD_UNKNOWN,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = SAA7134_BOARD_UNKNOWN,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = SAA7134_BOARD_UNKNOWN,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7135,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = SAA7134_BOARD_UNKNOWN,
+ },{
+ /* --- end of list --- */
+ }
+};
+MODULE_DEVICE_TABLE(pci, saa7134_pci_tbl);
+
+/* ----------------------------------------------------------- */
+/* flyvideo tweaks */
+
+#if 0
+static struct {
+ char *model;
+ int tuner_type;
+} fly_list[0x20] = {
+ /* default catch ... */
+ [ 0 ... 0x1f ] = {
+ .model = "UNKNOWN",
+ .tuner_type = TUNER_ABSENT,
+ },
+ /* ... the ones known so far */
+ [ 0x05 ] = {
+ .model = "PAL-BG",
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ },
+ [ 0x10 ] = {
+ .model = "PAL-BG / PAL-DK",
+ .tuner_type = TUNER_PHILIPS_PAL,
+ },
+ [ 0x15 ] = {
+ .model = "NTSC",
+ .tuner_type = TUNER_ABSENT /* FIXME */,
+ },
+};
+#endif
+
+static void board_flyvideo(struct saa7134_dev *dev)
+{
+#if 0
+ /* non-working attempt to detect the correct tuner type ... */
+ u32 value;
+ int index;
+
+ value = dev->gpio_value;
+ index = (value & 0x1f00) >> 8;
+ printk(KERN_INFO "%s: flyvideo: gpio is 0x%x [model=%s,tuner=%d]\n",
+ dev->name, value, fly_list[index].model,
+ fly_list[index].tuner_type);
+ dev->tuner_type = fly_list[index].tuner_type;
+#endif
+ printk("%s: there are different flyvideo cards with different tuners\n"
+ "%s: out there, you might have to use the tuner=<nr> insmod\n"
+ "%s: option to override the default value.\n",
+ dev->name, dev->name, dev->name);
+}
+
+/* ----------------------------------------------------------- */
+
+int saa7134_board_init1(struct saa7134_dev *dev)
+{
+ // Always print gpio, often manufacturers encode tuner type and other info.
+ saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0);
+ dev->gpio_value = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+ printk(KERN_INFO "%s: board init: gpio is %x\n", dev->name, dev->gpio_value);
+
+ switch (dev->board) {
+ case SAA7134_BOARD_FLYVIDEO2000:
+ case SAA7134_BOARD_FLYVIDEO3000:
+ dev->has_remote = 1;
+ board_flyvideo(dev);
+ break;
+ case SAA7134_BOARD_CINERGY400:
+ case SAA7134_BOARD_CINERGY600:
+ case SAA7134_BOARD_CINERGY600_MK3:
+ case SAA7134_BOARD_ECS_TVP3XP:
+ case SAA7134_BOARD_ECS_TVP3XP_4CB5:
+ case SAA7134_BOARD_MD2819:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_305:
+ case SAA7134_BOARD_AVERMEDIA_305:
+ case SAA7134_BOARD_AVERMEDIA_307:
+// case SAA7134_BOARD_SABRENT_SBTTVFM: /* not finished yet */
+ case SAA7134_BOARD_VIDEOMATE_TV_PVR:
+ dev->has_remote = 1;
+ break;
+ case SAA7134_BOARD_AVACSSMARTTV:
+ dev->has_remote = 1;
+ break;
+ case SAA7134_BOARD_MD5044:
+ printk("%s: seems there are two different versions of the MD5044\n"
+ "%s: (with the same ID) out there. If sound doesn't work for\n"
+ "%s: you try the audio_clock_override=0x200000 insmod option.\n",
+ dev->name,dev->name,dev->name);
+ break;
+ case SAA7134_BOARD_CINERGY400_CARDBUS:
+ /* power-up tuner chip */
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00040000, 0x00040000);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00040000, 0x00000000);
+ msleep(1);
+ break;
+ }
+ if (dev->has_remote)
+ dev->irq2_mask |= (SAA7134_IRQ2_INTE_GPIO18 |
+ SAA7134_IRQ2_INTE_GPIO18A |
+ SAA7134_IRQ2_INTE_GPIO16 );
+ return 0;
+}
+
+/* stuff which needs working i2c */
+int saa7134_board_init2(struct saa7134_dev *dev)
+{
+ unsigned char buf;
+ int board;
+
+ 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)
+ ? SAA7134_BOARD_BMK_MPEX_NOTUNER
+ : SAA7134_BOARD_BMK_MPEX_TUNER;
+ if (board == dev->board)
+ break;
+ dev->board = board;
+ printk("%s: board type fixup: %s\n", dev->name,
+ saa7134_boards[dev->board].name);
+ dev->tuner_type = saa7134_boards[dev->board].tuner_type;
+ if (TUNER_ABSENT != dev->tuner_type)
+ saa7134_i2c_call_clients(dev,TUNER_SET_TYPE,&dev->tuner_type);
+ break;
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
new file mode 100644
index 00000000000..d506cafba8f
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -0,0 +1,1237 @@
+/*
+ * $Id: saa7134-core.c,v 1.28 2005/02/22 09:56:29 kraxel Exp $
+ *
+ * device driver for philips saa7134 based TV cards
+ * driver core
+ *
+ * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/kmod.h>
+#include <linux/sound.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+MODULE_DESCRIPTION("v4l2 driver module for saa7130/34 based TV cards");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int irq_debug = 0;
+module_param(irq_debug, int, 0644);
+MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]");
+
+static unsigned int core_debug = 0;
+module_param(core_debug, int, 0644);
+MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
+
+static unsigned int gpio_tracking = 0;
+module_param(gpio_tracking, int, 0644);
+MODULE_PARM_DESC(gpio_tracking,"enable debug messages [gpio]");
+
+static unsigned int oss = 0;
+module_param(oss, int, 0444);
+MODULE_PARM_DESC(oss,"register oss devices (default: no)");
+
+static unsigned int latency = UNSET;
+module_param(latency, int, 0444);
+MODULE_PARM_DESC(latency,"pci latency timer");
+
+static unsigned int video_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+static unsigned int dsp_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+static unsigned int mixer_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+static unsigned int tuner[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+static unsigned int card[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(vbi_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+module_param_array(dsp_nr, int, NULL, 0444);
+module_param_array(mixer_nr, int, NULL, 0444);
+module_param_array(tuner, int, NULL, 0444);
+module_param_array(card, int, NULL, 0444);
+
+MODULE_PARM_DESC(video_nr, "video device number");
+MODULE_PARM_DESC(vbi_nr, "vbi device number");
+MODULE_PARM_DESC(radio_nr, "radio device number");
+MODULE_PARM_DESC(dsp_nr, "oss dsp device number");
+MODULE_PARM_DESC(mixer_nr, "oss mixer device number");
+MODULE_PARM_DESC(tuner, "tuner type");
+MODULE_PARM_DESC(card, "card type");
+
+static DECLARE_MUTEX(devlist_lock);
+LIST_HEAD(saa7134_devlist);
+static LIST_HEAD(mops_list);
+static unsigned int saa7134_devcount;
+
+#define dprintk(fmt, arg...) if (core_debug) \
+ printk(KERN_DEBUG "%s/core: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+/* debug help functions */
+
+static const char *v4l1_ioctls[] = {
+ "0", "GCAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT",
+ "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ",
+ "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT",
+ "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO",
+ "SMICROCODE", "GVBIFMT", "SVBIFMT" };
+#define V4L1_IOCTLS ARRAY_SIZE(v4l1_ioctls)
+
+static const char *v4l2_ioctls[] = {
+ "QUERYCAP", "1", "ENUM_PIXFMT", "ENUM_FBUFFMT", "G_FMT", "S_FMT",
+ "G_COMP", "S_COMP", "REQBUFS", "QUERYBUF", "G_FBUF", "S_FBUF",
+ "G_WIN", "S_WIN", "PREVIEW", "QBUF", "16", "DQBUF", "STREAMON",
+ "STREAMOFF", "G_PERF", "G_PARM", "S_PARM", "G_STD", "S_STD",
+ "ENUMSTD", "ENUMINPUT", "G_CTRL", "S_CTRL", "G_TUNER", "S_TUNER",
+ "G_FREQ", "S_FREQ", "G_AUDIO", "S_AUDIO", "35", "QUERYCTRL",
+ "QUERYMENU", "G_INPUT", "S_INPUT", "ENUMCVT", "41", "42", "43",
+ "44", "45", "G_OUTPUT", "S_OUTPUT", "ENUMOUTPUT", "G_AUDOUT",
+ "S_AUDOUT", "ENUMFX", "G_EFFECT", "S_EFFECT", "G_MODULATOR",
+ "S_MODULATOR"
+};
+#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
+
+static const char *osspcm_ioctls[] = {
+ "RESET", "SYNC", "SPEED", "STEREO", "GETBLKSIZE", "SETFMT",
+ "CHANNELS", "?", "POST", "SUBDIVIDE", "SETFRAGMENT", "GETFMTS",
+ "GETOSPACE", "GETISPACE", "NONBLOCK", "GETCAPS", "GET/SETTRIGGER",
+ "GETIPTR", "GETOPTR", "MAPINBUF", "MAPOUTBUF", "SETSYNCRO",
+ "SETDUPLEX", "GETODELAY"
+};
+#define OSSPCM_IOCTLS ARRAY_SIZE(v4l2_ioctls)
+
+void saa7134_print_ioctl(char *name, unsigned int cmd)
+{
+ char *dir;
+
+ switch (_IOC_DIR(cmd)) {
+ case _IOC_NONE: dir = "--"; break;
+ case _IOC_READ: dir = "r-"; break;
+ case _IOC_WRITE: dir = "-w"; break;
+ case _IOC_READ | _IOC_WRITE: dir = "rw"; break;
+ default: dir = "??"; break;
+ }
+ switch (_IOC_TYPE(cmd)) {
+ case 'v':
+ printk(KERN_DEBUG "%s: ioctl 0x%08x (v4l1, %s, VIDIOC%s)\n",
+ name, cmd, dir, (_IOC_NR(cmd) < V4L1_IOCTLS) ?
+ v4l1_ioctls[_IOC_NR(cmd)] : "???");
+ break;
+ case 'V':
+ printk(KERN_DEBUG "%s: ioctl 0x%08x (v4l2, %s, VIDIOC_%s)\n",
+ name, cmd, dir, (_IOC_NR(cmd) < V4L2_IOCTLS) ?
+ v4l2_ioctls[_IOC_NR(cmd)] : "???");
+ break;
+ case 'P':
+ printk(KERN_DEBUG "%s: ioctl 0x%08x (oss dsp, %s, SNDCTL_DSP_%s)\n",
+ name, cmd, dir, (_IOC_NR(cmd) < OSSPCM_IOCTLS) ?
+ osspcm_ioctls[_IOC_NR(cmd)] : "???");
+ break;
+ case 'M':
+ printk(KERN_DEBUG "%s: ioctl 0x%08x (oss mixer, %s, #%d)\n",
+ name, cmd, dir, _IOC_NR(cmd));
+ break;
+ default:
+ printk(KERN_DEBUG "%s: ioctl 0x%08x (???, %s, #%d)\n",
+ name, cmd, dir, _IOC_NR(cmd));
+ }
+}
+
+void saa7134_track_gpio(struct saa7134_dev *dev, char *msg)
+{
+ unsigned long mode,status;
+
+ if (!gpio_tracking)
+ return;
+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
+ saa_andorb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN,0);
+ saa_andorb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN,SAA7134_GPIO_GPRESCAN);
+ mode = saa_readl(SAA7134_GPIO_GPMODE0 >> 2) & 0xfffffff;
+ status = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & 0xfffffff;
+ printk(KERN_DEBUG
+ "%s: gpio: mode=0x%07lx in=0x%07lx out=0x%07lx [%s]\n",
+ dev->name, mode, (~mode) & status, mode & status, msg);
+}
+
+/* ------------------------------------------------------------------ */
+
+#if 0
+static char *dec1_bits[8] = {
+ "DCSTD0", "DCSCT1", "WIPA", "GLIMB",
+ "GLIMT", "SLTCA", "HLCK"
+};
+static char *dec2_bits[8] = {
+ "RDCAP", "COPRO", "COLSTR", "TYPE3",
+ NULL, "FIDT", "HLVLN", "INTL"
+};
+static char *scale1_bits[8] = {
+ "VID_A", "VBI_A", NULL, NULL, "VID_B", "VBI_B"
+};
+static char *scale2_bits[8] = {
+ "TRERR", "CFERR", "LDERR", "WASRST",
+ "FIDSCI", "FIDSCO", "D6^D5", "TASK"
+};
+
+static void dump_statusreg(struct saa7134_dev *dev, int reg,
+ char *regname, char **bits)
+{
+ int value,i;
+
+ value = saa_readb(reg);
+ printk(KERN_DEBUG "%s: %s:", dev->name, regname);
+ for (i = 7; i >= 0; i--) {
+ if (NULL == bits[i])
+ continue;
+ printk(" %s=%d", bits[i], (value & (1 << i)) ? 1 : 0);
+ }
+ printk("\n");
+}
+
+static void dump_statusregs(struct saa7134_dev *dev)
+{
+ dump_statusreg(dev,SAA7134_STATUS_VIDEO1,"dec1",dec1_bits);
+ dump_statusreg(dev,SAA7134_STATUS_VIDEO2,"dec2",dec2_bits);
+ dump_statusreg(dev,SAA7134_SCALER_STATUS0,"scale0",scale1_bits);
+ dump_statusreg(dev,SAA7134_SCALER_STATUS1,"scale1",scale2_bits);
+}
+#endif
+
+/* ----------------------------------------------------------- */
+/* delayed request_module */
+
+#ifdef CONFIG_MODULES
+
+static int need_empress;
+static int need_dvb;
+
+static int pending_call(struct notifier_block *self, unsigned long state,
+ void *module)
+{
+ if (module != THIS_MODULE || state != MODULE_STATE_LIVE)
+ return NOTIFY_DONE;
+
+ if (need_empress)
+ request_module("saa7134-empress");
+ if (need_dvb)
+ request_module("saa7134-dvb");
+ return NOTIFY_DONE;
+}
+
+static int pending_registered;
+static struct notifier_block pending_notifier = {
+ .notifier_call = pending_call,
+};
+
+static void request_module_depend(char *name, int *flag)
+{
+ switch (THIS_MODULE->state) {
+ case MODULE_STATE_COMING:
+ if (!pending_registered) {
+ register_module_notifier(&pending_notifier);
+ pending_registered = 1;
+ }
+ *flag = 1;
+ break;
+ case MODULE_STATE_LIVE:
+ request_module(name);
+ break;
+ default:
+ /* nothing */;
+ break;
+ }
+}
+
+#else
+
+#define request_module_depend(name,flag)
+
+#endif /* CONFIG_MODULES */
+
+/* ------------------------------------------------------------------ */
+
+/* nr of (saa7134-)pages for the given buffer size */
+static int saa7134_buffer_pages(int size)
+{
+ size = PAGE_ALIGN(size);
+ size += PAGE_SIZE; /* for non-page-aligned buffers */
+ size /= 4096;
+ return size;
+}
+
+/* calc max # of buffers from size (must not exceed the 4MB virtual
+ * address space per DMA channel) */
+int saa7134_buffer_count(unsigned int size, unsigned int count)
+{
+ unsigned int maxcount;
+
+ maxcount = 1024 / saa7134_buffer_pages(size);
+ if (count > maxcount)
+ count = maxcount;
+ return count;
+}
+
+int saa7134_buffer_startpage(struct saa7134_buf *buf)
+{
+ return saa7134_buffer_pages(buf->vb.bsize) * buf->vb.i;
+}
+
+unsigned long saa7134_buffer_base(struct saa7134_buf *buf)
+{
+ unsigned long base;
+
+ base = saa7134_buffer_startpage(buf) * 4096;
+ base += buf->vb.dma.sglist[0].offset;
+ return base;
+}
+
+/* ------------------------------------------------------------------ */
+
+int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt)
+{
+ u32 *cpu;
+ dma_addr_t dma_addr;
+
+ cpu = pci_alloc_consistent(pci, SAA7134_PGTABLE_SIZE, &dma_addr);
+ if (NULL == cpu)
+ return -ENOMEM;
+ pt->size = SAA7134_PGTABLE_SIZE;
+ pt->cpu = cpu;
+ pt->dma = dma_addr;
+ return 0;
+}
+
+int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt,
+ struct scatterlist *list, unsigned int length,
+ unsigned int startpage)
+{
+ u32 *ptr;
+ unsigned int i,p;
+
+ BUG_ON(NULL == pt || NULL == pt->cpu);
+
+ ptr = pt->cpu + startpage;
+ for (i = 0; i < length; i++, list++)
+ for (p = 0; p * 4096 < list->length; p++, ptr++)
+ *ptr = sg_dma_address(list) - list->offset;
+ return 0;
+}
+
+void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt)
+{
+ if (NULL == pt->cpu)
+ return;
+ pci_free_consistent(pci, pt->size, pt->cpu, pt->dma);
+ pt->cpu = NULL;
+}
+
+/* ------------------------------------------------------------------ */
+
+void saa7134_dma_free(struct saa7134_dev *dev,struct saa7134_buf *buf)
+{
+ if (in_interrupt())
+ BUG();
+
+ videobuf_waiton(&buf->vb,0,0);
+ videobuf_dma_pci_unmap(dev->pci, &buf->vb.dma);
+ videobuf_dma_free(&buf->vb.dma);
+ buf->vb.state = STATE_NEEDS_INIT;
+}
+
+/* ------------------------------------------------------------------ */
+
+int saa7134_buffer_queue(struct saa7134_dev *dev,
+ struct saa7134_dmaqueue *q,
+ struct saa7134_buf *buf)
+{
+ struct saa7134_buf *next = NULL;
+
+ assert_spin_locked(&dev->slock);
+ dprintk("buffer_queue %p\n",buf);
+ if (NULL == q->curr) {
+ if (!q->need_two) {
+ q->curr = buf;
+ buf->activate(dev,buf,NULL);
+ } else if (list_empty(&q->queue)) {
+ list_add_tail(&buf->vb.queue,&q->queue);
+ buf->vb.state = STATE_QUEUED;
+ } else {
+ next = list_entry(q->queue.next,struct saa7134_buf,
+ vb.queue);
+ q->curr = buf;
+ buf->activate(dev,buf,next);
+ }
+ } else {
+ list_add_tail(&buf->vb.queue,&q->queue);
+ buf->vb.state = STATE_QUEUED;
+ }
+ return 0;
+}
+
+void saa7134_buffer_finish(struct saa7134_dev *dev,
+ struct saa7134_dmaqueue *q,
+ unsigned int state)
+{
+ assert_spin_locked(&dev->slock);
+ dprintk("buffer_finish %p\n",q->curr);
+
+ /* finish current buffer */
+ q->curr->vb.state = state;
+ do_gettimeofday(&q->curr->vb.ts);
+ wake_up(&q->curr->vb.done);
+ q->curr = NULL;
+}
+
+void saa7134_buffer_next(struct saa7134_dev *dev,
+ struct saa7134_dmaqueue *q)
+{
+ struct saa7134_buf *buf,*next = NULL;
+
+ assert_spin_locked(&dev->slock);
+ BUG_ON(NULL != q->curr);
+
+ if (!list_empty(&q->queue)) {
+ /* activate next one from queue */
+ buf = list_entry(q->queue.next,struct saa7134_buf,vb.queue);
+ dprintk("buffer_next %p [prev=%p/next=%p]\n",
+ buf,q->queue.prev,q->queue.next);
+ list_del(&buf->vb.queue);
+ if (!list_empty(&q->queue))
+ next = list_entry(q->queue.next,struct saa7134_buf,
+ vb.queue);
+ q->curr = buf;
+ buf->activate(dev,buf,next);
+ dprintk("buffer_next #2 prev=%p/next=%p\n",
+ q->queue.prev,q->queue.next);
+ } else {
+ /* nothing to do -- just stop DMA */
+ dprintk("buffer_next %p\n",NULL);
+ saa7134_set_dmabits(dev);
+ del_timer(&q->timeout);
+ }
+}
+
+void saa7134_buffer_timeout(unsigned long data)
+{
+ struct saa7134_dmaqueue *q = (struct saa7134_dmaqueue*)data;
+ struct saa7134_dev *dev = q->dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->slock,flags);
+
+ /* try to reset the hardware (SWRST) */
+ saa_writeb(SAA7134_REGION_ENABLE, 0x00);
+ saa_writeb(SAA7134_REGION_ENABLE, 0x80);
+ saa_writeb(SAA7134_REGION_ENABLE, 0x00);
+
+ /* flag current buffer as failed,
+ try to start over with the next one. */
+ if (q->curr) {
+ dprintk("timeout on %p\n",q->curr);
+ saa7134_buffer_finish(dev,q,STATE_ERROR);
+ }
+ saa7134_buffer_next(dev,q);
+ spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+/* ------------------------------------------------------------------ */
+
+int saa7134_set_dmabits(struct saa7134_dev *dev)
+{
+ u32 split, task=0, ctrl=0, irq=0;
+ enum v4l2_field cap = V4L2_FIELD_ANY;
+ enum v4l2_field ov = V4L2_FIELD_ANY;
+
+ assert_spin_locked(&dev->slock);
+
+ /* video capture -- dma 0 + video task A */
+ if (dev->video_q.curr) {
+ task |= 0x01;
+ ctrl |= SAA7134_MAIN_CTRL_TE0;
+ irq |= SAA7134_IRQ1_INTE_RA0_1 |
+ SAA7134_IRQ1_INTE_RA0_0;
+ cap = dev->video_q.curr->vb.field;
+ }
+
+ /* video capture -- dma 1+2 (planar modes) */
+ if (dev->video_q.curr &&
+ dev->video_q.curr->fmt->planar) {
+ ctrl |= SAA7134_MAIN_CTRL_TE4 |
+ SAA7134_MAIN_CTRL_TE5;
+ }
+
+ /* screen overlay -- dma 0 + video task B */
+ if (dev->ovenable) {
+ task |= 0x10;
+ ctrl |= SAA7134_MAIN_CTRL_TE1;
+ ov = dev->ovfield;
+ }
+
+ /* vbi capture -- dma 0 + vbi task A+B */
+ if (dev->vbi_q.curr) {
+ task |= 0x22;
+ ctrl |= SAA7134_MAIN_CTRL_TE2 |
+ SAA7134_MAIN_CTRL_TE3;
+ irq |= SAA7134_IRQ1_INTE_RA0_7 |
+ SAA7134_IRQ1_INTE_RA0_6 |
+ SAA7134_IRQ1_INTE_RA0_5 |
+ SAA7134_IRQ1_INTE_RA0_4;
+ }
+
+ /* audio capture -- dma 3 */
+ if (dev->oss.dma_running) {
+ ctrl |= SAA7134_MAIN_CTRL_TE6;
+ irq |= SAA7134_IRQ1_INTE_RA3_1 |
+ SAA7134_IRQ1_INTE_RA3_0;
+ }
+
+ /* TS capture -- dma 5 */
+ if (dev->ts_q.curr) {
+ ctrl |= SAA7134_MAIN_CTRL_TE5;
+ irq |= SAA7134_IRQ1_INTE_RA2_3 |
+ SAA7134_IRQ1_INTE_RA2_2 |
+ SAA7134_IRQ1_INTE_RA2_1 |
+ SAA7134_IRQ1_INTE_RA2_0;
+ }
+
+ /* set task conditions + field handling */
+ if (V4L2_FIELD_HAS_BOTH(cap) || V4L2_FIELD_HAS_BOTH(ov) || cap == ov) {
+ /* default config -- use full frames */
+ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0d);
+ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0d);
+ saa_writeb(SAA7134_FIELD_HANDLING(TASK_A), 0x02);
+ saa_writeb(SAA7134_FIELD_HANDLING(TASK_B), 0x02);
+ split = 0;
+ } else {
+ /* split fields between tasks */
+ if (V4L2_FIELD_TOP == cap) {
+ /* odd A, even B, repeat */
+ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0d);
+ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0e);
+ } else {
+ /* odd B, even A, repeat */
+ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0e);
+ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0d);
+ }
+ saa_writeb(SAA7134_FIELD_HANDLING(TASK_A), 0x01);
+ saa_writeb(SAA7134_FIELD_HANDLING(TASK_B), 0x01);
+ split = 1;
+ }
+
+ /* irqs */
+ saa_writeb(SAA7134_REGION_ENABLE, task);
+ saa_writel(SAA7134_IRQ1, irq);
+ saa_andorl(SAA7134_MAIN_CTRL,
+ SAA7134_MAIN_CTRL_TE0 |
+ SAA7134_MAIN_CTRL_TE1 |
+ SAA7134_MAIN_CTRL_TE2 |
+ SAA7134_MAIN_CTRL_TE3 |
+ SAA7134_MAIN_CTRL_TE4 |
+ SAA7134_MAIN_CTRL_TE5 |
+ SAA7134_MAIN_CTRL_TE6,
+ ctrl);
+ dprintk("dmabits: task=0x%02x ctrl=0x%02x irq=0x%x split=%s\n",
+ task, ctrl, irq, split ? "no" : "yes");
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* IRQ handler + helpers */
+
+static char *irqbits[] = {
+ "DONE_RA0", "DONE_RA1", "DONE_RA2", "DONE_RA3",
+ "AR", "PE", "PWR_ON", "RDCAP", "INTL", "FIDT", "MMC",
+ "TRIG_ERR", "CONF_ERR", "LOAD_ERR",
+ "GPIO16?", "GPIO18", "GPIO22", "GPIO23"
+};
+#define IRQBITS ARRAY_SIZE(irqbits)
+
+static void print_irqstatus(struct saa7134_dev *dev, int loop,
+ unsigned long report, unsigned long status)
+{
+ unsigned int i;
+
+ printk(KERN_DEBUG "%s/irq[%d,%ld]: r=0x%lx s=0x%02lx",
+ dev->name,loop,jiffies,report,status);
+ for (i = 0; i < IRQBITS; i++) {
+ if (!(report & (1 << i)))
+ continue;
+ printk(" %s",irqbits[i]);
+ }
+ if (report & SAA7134_IRQ_REPORT_DONE_RA0) {
+ printk(" | RA0=%s,%s,%s,%ld",
+ (status & 0x40) ? "vbi" : "video",
+ (status & 0x20) ? "b" : "a",
+ (status & 0x10) ? "odd" : "even",
+ (status & 0x0f));
+ }
+ printk("\n");
+}
+
+static irqreturn_t saa7134_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct saa7134_dev *dev = (struct saa7134_dev*) dev_id;
+ unsigned long report,status;
+ int loop, handled = 0;
+
+ for (loop = 0; loop < 10; loop++) {
+ report = saa_readl(SAA7134_IRQ_REPORT);
+ status = saa_readl(SAA7134_IRQ_STATUS);
+ if (0 == report) {
+ if (irq_debug > 1)
+ printk(KERN_DEBUG "%s/irq: no (more) work\n",
+ dev->name);
+ goto out;
+ }
+ handled = 1;
+ saa_writel(SAA7134_IRQ_REPORT,report);
+ if (irq_debug)
+ print_irqstatus(dev,loop,report,status);
+
+#if 0
+ if (report & SAA7134_IRQ_REPORT_CONF_ERR)
+ dump_statusregs(dev);
+#endif
+
+ if (report & SAA7134_IRQ_REPORT_RDCAP /* _INTL */)
+ saa7134_irq_video_intl(dev);
+
+ if ((report & SAA7134_IRQ_REPORT_DONE_RA0) &&
+ (status & 0x60) == 0)
+ saa7134_irq_video_done(dev,status);
+
+ if ((report & SAA7134_IRQ_REPORT_DONE_RA0) &&
+ (status & 0x40) == 0x40)
+ saa7134_irq_vbi_done(dev,status);
+
+ if ((report & SAA7134_IRQ_REPORT_DONE_RA2) &&
+ card_has_mpeg(dev))
+ saa7134_irq_ts_done(dev,status);
+
+ if ((report & SAA7134_IRQ_REPORT_DONE_RA3))
+ saa7134_irq_oss_done(dev,status);
+
+ if ((report & (SAA7134_IRQ_REPORT_GPIO16 |
+ SAA7134_IRQ_REPORT_GPIO18)) &&
+ dev->remote)
+ saa7134_input_irq(dev);
+ }
+
+ if (10 == loop) {
+ print_irqstatus(dev,loop,report,status);
+ if (report & SAA7134_IRQ_REPORT_PE) {
+ /* disable all parity error */
+ printk(KERN_WARNING "%s/irq: looping -- "
+ "clearing PE (parity error!) enable bit\n",dev->name);
+ saa_clearl(SAA7134_IRQ2,SAA7134_IRQ2_INTE_PE);
+ } else if (report & (SAA7134_IRQ_REPORT_GPIO16 |
+ SAA7134_IRQ_REPORT_GPIO18)) {
+ /* disable gpio IRQs */
+ printk(KERN_WARNING "%s/irq: looping -- "
+ "clearing GPIO enable bits\n",dev->name);
+ saa_clearl(SAA7134_IRQ2, (SAA7134_IRQ2_INTE_GPIO16 |
+ SAA7134_IRQ2_INTE_GPIO18));
+ } else {
+ /* disable all irqs */
+ printk(KERN_WARNING "%s/irq: looping -- "
+ "clearing all enable bits\n",dev->name);
+ saa_writel(SAA7134_IRQ1,0);
+ saa_writel(SAA7134_IRQ2,0);
+ }
+ }
+
+ out:
+ return IRQ_RETVAL(handled);
+}
+
+/* ------------------------------------------------------------------ */
+
+/* early init (no i2c, no irq) */
+static int saa7134_hwinit1(struct saa7134_dev *dev)
+{
+ dprintk("hwinit1\n");
+
+ saa_writel(SAA7134_IRQ1, 0);
+ saa_writel(SAA7134_IRQ2, 0);
+ init_MUTEX(&dev->lock);
+ spin_lock_init(&dev->slock);
+
+ saa7134_track_gpio(dev,"pre-init");
+ saa7134_video_init1(dev);
+ saa7134_vbi_init1(dev);
+ if (card_has_mpeg(dev))
+ saa7134_ts_init1(dev);
+ saa7134_input_init1(dev);
+
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ saa7134_oss_init1(dev);
+ break;
+ }
+
+ /* RAM FIFO config */
+ saa_writel(SAA7134_FIFO_SIZE, 0x08070503);
+ saa_writel(SAA7134_THRESHOULD,0x02020202);
+
+ /* enable audio + video processing */
+ saa_writel(SAA7134_MAIN_CTRL,
+ SAA7134_MAIN_CTRL_VPLLE |
+ SAA7134_MAIN_CTRL_APLLE |
+ SAA7134_MAIN_CTRL_EXOSC |
+ SAA7134_MAIN_CTRL_EVFE1 |
+ SAA7134_MAIN_CTRL_EVFE2 |
+ SAA7134_MAIN_CTRL_ESFE |
+ SAA7134_MAIN_CTRL_EBADC |
+ SAA7134_MAIN_CTRL_EBDAC);
+
+ /* enable peripheral devices */
+ saa_writeb(SAA7134_SPECIAL_MODE, 0x01);
+
+ /* set vertical line numbering start (vbi needs this) */
+ saa_writeb(SAA7134_SOURCE_TIMING2, 0x20);
+
+ return 0;
+}
+
+/* late init (with i2c + irq) */
+static int saa7134_hwinit2(struct saa7134_dev *dev)
+{
+ dprintk("hwinit2\n");
+
+ saa7134_video_init2(dev);
+ saa7134_tvaudio_init2(dev);
+
+ /* enable IRQ's */
+ saa_writel(SAA7134_IRQ1, 0);
+ saa_writel(SAA7134_IRQ2, dev->irq2_mask);
+
+ return 0;
+}
+
+/* shutdown */
+static int saa7134_hwfini(struct saa7134_dev *dev)
+{
+ dprintk("hwfini\n");
+
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ saa7134_oss_fini(dev);
+ break;
+ }
+ if (card_has_mpeg(dev))
+ saa7134_ts_fini(dev);
+ saa7134_input_fini(dev);
+ saa7134_vbi_fini(dev);
+ saa7134_video_fini(dev);
+ saa7134_tvaudio_fini(dev);
+ return 0;
+}
+
+static void __devinit must_configure_manually(void)
+{
+ unsigned int i,p;
+
+ printk(KERN_WARNING
+ "saa7134: <rant>\n"
+ "saa7134: Congratulations! Your TV card vendor saved a few\n"
+ "saa7134: cents for a eeprom, thus your pci board has no\n"
+ "saa7134: subsystem ID and I can't identify it automatically\n"
+ "saa7134: </rant>\n"
+ "saa7134: I feel better now. Ok, here are the good news:\n"
+ "saa7134: You can use the card=<nr> insmod option to specify\n"
+ "saa7134: which board do you have. The list:\n");
+ for (i = 0; i < saa7134_bcount; i++) {
+ printk(KERN_WARNING "saa7134: card=%d -> %-40.40s",
+ i,saa7134_boards[i].name);
+ for (p = 0; saa7134_pci_tbl[p].driver_data; p++) {
+ if (saa7134_pci_tbl[p].driver_data != i)
+ continue;
+ printk(" %04x:%04x",
+ saa7134_pci_tbl[p].subvendor,
+ saa7134_pci_tbl[p].subdevice);
+ }
+ printk("\n");
+ }
+}
+
+static struct video_device *vdev_init(struct saa7134_dev *dev,
+ struct video_device *template,
+ char *type)
+{
+ struct video_device *vfd;
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ vfd->minor = -1;
+ vfd->dev = &dev->pci->dev;
+ vfd->release = video_device_release;
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
+ dev->name, type, saa7134_boards[dev->board].name);
+ return vfd;
+}
+
+static void saa7134_unregister_video(struct saa7134_dev *dev)
+{
+ if (dev->video_dev) {
+ if (-1 != dev->video_dev->minor)
+ video_unregister_device(dev->video_dev);
+ else
+ video_device_release(dev->video_dev);
+ dev->video_dev = NULL;
+ }
+ if (dev->vbi_dev) {
+ if (-1 != dev->vbi_dev->minor)
+ video_unregister_device(dev->vbi_dev);
+ else
+ video_device_release(dev->vbi_dev);
+ dev->vbi_dev = NULL;
+ }
+ if (dev->radio_dev) {
+ if (-1 != dev->radio_dev->minor)
+ video_unregister_device(dev->radio_dev);
+ else
+ video_device_release(dev->radio_dev);
+ dev->radio_dev = NULL;
+ }
+}
+
+static void mpeg_ops_attach(struct saa7134_mpeg_ops *ops,
+ struct saa7134_dev *dev)
+{
+ int err;
+
+ if (NULL != dev->mops)
+ return;
+ if (saa7134_boards[dev->board].mpeg != ops->type)
+ return;
+ err = ops->init(dev);
+ if (0 != err)
+ return;
+ dev->mops = ops;
+}
+
+static void mpeg_ops_detach(struct saa7134_mpeg_ops *ops,
+ struct saa7134_dev *dev)
+{
+ if (NULL == dev->mops)
+ return;
+ if (dev->mops != ops)
+ return;
+ dev->mops->fini(dev);
+ dev->mops = NULL;
+}
+
+static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ struct saa7134_dev *dev;
+ struct list_head *item;
+ struct saa7134_mpeg_ops *mops;
+ int err;
+
+ dev = kmalloc(sizeof(*dev),GFP_KERNEL);
+ if (NULL == dev)
+ return -ENOMEM;
+ memset(dev,0,sizeof(*dev));
+
+ /* pci init */
+ dev->pci = pci_dev;
+ if (pci_enable_device(pci_dev)) {
+ err = -EIO;
+ goto fail1;
+ }
+
+ dev->nr = saa7134_devcount;
+ sprintf(dev->name,"saa%x[%d]",pci_dev->device,dev->nr);
+
+ /* pci quirks */
+ if (pci_pci_problems) {
+ if (pci_pci_problems & PCIPCI_TRITON)
+ printk(KERN_INFO "%s: quirk: PCIPCI_TRITON\n", dev->name);
+ if (pci_pci_problems & PCIPCI_NATOMA)
+ printk(KERN_INFO "%s: quirk: PCIPCI_NATOMA\n", dev->name);
+ if (pci_pci_problems & PCIPCI_VIAETBF)
+ printk(KERN_INFO "%s: quirk: PCIPCI_VIAETBF\n", dev->name);
+ if (pci_pci_problems & PCIPCI_VSFX)
+ printk(KERN_INFO "%s: quirk: PCIPCI_VSFX\n",dev->name);
+#ifdef PCIPCI_ALIMAGIK
+ if (pci_pci_problems & PCIPCI_ALIMAGIK) {
+ printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n",
+ dev->name);
+ latency = 0x0A;
+ }
+#endif
+ }
+ if (UNSET != latency) {
+ printk(KERN_INFO "%s: setting pci latency timer to %d\n",
+ dev->name,latency);
+ pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency);
+ }
+
+ /* print pci info */
+ pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev);
+ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat);
+ printk(KERN_INFO "%s: found at %s, rev: %d, irq: %d, "
+ "latency: %d, mmio: 0x%lx\n", dev->name,
+ pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
+ dev->pci_lat,pci_resource_start(pci_dev,0));
+ pci_set_master(pci_dev);
+ if (!pci_dma_supported(pci_dev,0xffffffff)) {
+ printk("%s: Oops: no 32bit PCI DMA ???\n",dev->name);
+ err = -EIO;
+ goto fail1;
+ }
+
+ /* board config */
+ dev->board = pci_id->driver_data;
+ if (card[dev->nr] >= 0 &&
+ card[dev->nr] < saa7134_bcount)
+ dev->board = card[dev->nr];
+ if (SAA7134_BOARD_NOAUTO == dev->board) {
+ must_configure_manually();
+ dev->board = SAA7134_BOARD_UNKNOWN;
+ }
+ dev->tuner_type = saa7134_boards[dev->board].tuner_type;
+ dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf;
+ if (UNSET != tuner[dev->nr])
+ dev->tuner_type = tuner[dev->nr];
+ printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
+ dev->name,pci_dev->subsystem_vendor,
+ pci_dev->subsystem_device,saa7134_boards[dev->board].name,
+ dev->board, card[dev->nr] == dev->board ?
+ "insmod option" : "autodetected");
+
+ /* get mmio */
+ if (!request_mem_region(pci_resource_start(pci_dev,0),
+ pci_resource_len(pci_dev,0),
+ dev->name)) {
+ err = -EBUSY;
+ printk(KERN_ERR "%s: can't get MMIO memory @ 0x%lx\n",
+ dev->name,pci_resource_start(pci_dev,0));
+ goto fail1;
+ }
+ dev->lmmio = ioremap(pci_resource_start(pci_dev,0), 0x1000);
+ dev->bmmio = (__u8 __iomem *)dev->lmmio;
+ if (NULL == dev->lmmio) {
+ err = -EIO;
+ printk(KERN_ERR "%s: can't ioremap() MMIO memory\n",
+ dev->name);
+ goto fail2;
+ }
+
+ /* initialize hardware #1 */
+ dev->irq2_mask =
+ SAA7134_IRQ2_INTE_DEC3 |
+ SAA7134_IRQ2_INTE_DEC2 |
+ SAA7134_IRQ2_INTE_DEC1 |
+ SAA7134_IRQ2_INTE_DEC0 |
+ SAA7134_IRQ2_INTE_PE |
+ SAA7134_IRQ2_INTE_AR;
+ saa7134_board_init1(dev);
+ saa7134_hwinit1(dev);
+
+ /* get irq */
+ err = request_irq(pci_dev->irq, saa7134_irq,
+ SA_SHIRQ | SA_INTERRUPT, dev->name, dev);
+ if (err < 0) {
+ printk(KERN_ERR "%s: can't get IRQ %d\n",
+ dev->name,pci_dev->irq);
+ goto fail3;
+ }
+
+ /* wait a bit, register i2c bus */
+ msleep(100);
+ saa7134_i2c_register(dev);
+
+ /* initialize hardware #2 */
+ saa7134_board_init2(dev);
+ saa7134_hwinit2(dev);
+
+ /* load i2c helpers */
+ if (TUNER_ABSENT != dev->tuner_type)
+ request_module("tuner");
+ if (dev->tda9887_conf)
+ request_module("tda9887");
+ if (card_is_empress(dev)) {
+ request_module("saa6752hs");
+ request_module_depend("saa7134-empress",&need_empress);
+ }
+ if (card_is_dvb(dev))
+ request_module_depend("saa7134-dvb",&need_dvb);
+
+ v4l2_prio_init(&dev->prio);
+
+ /* register v4l devices */
+ dev->video_dev = vdev_init(dev,&saa7134_video_template,"video");
+ err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
+ video_nr[dev->nr]);
+ if (err < 0) {
+ printk(KERN_INFO "%s: can't register video device\n",
+ dev->name);
+ goto fail4;
+ }
+ printk(KERN_INFO "%s: registered device video%d [v4l2]\n",
+ dev->name,dev->video_dev->minor & 0x1f);
+
+ dev->vbi_dev = vdev_init(dev,&saa7134_vbi_template,"vbi");
+ err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
+ vbi_nr[dev->nr]);
+ if (err < 0)
+ goto fail4;
+ printk(KERN_INFO "%s: registered device vbi%d\n",
+ dev->name,dev->vbi_dev->minor & 0x1f);
+
+ if (card_has_radio(dev)) {
+ dev->radio_dev = vdev_init(dev,&saa7134_radio_template,"radio");
+ err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
+ radio_nr[dev->nr]);
+ if (err < 0)
+ goto fail4;
+ printk(KERN_INFO "%s: registered device radio%d\n",
+ dev->name,dev->radio_dev->minor & 0x1f);
+ }
+
+ /* register oss devices */
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ if (oss) {
+ err = dev->oss.minor_dsp =
+ register_sound_dsp(&saa7134_dsp_fops,
+ dsp_nr[dev->nr]);
+ if (err < 0) {
+ goto fail4;
+ }
+ printk(KERN_INFO "%s: registered device dsp%d\n",
+ dev->name,dev->oss.minor_dsp >> 4);
+
+ err = dev->oss.minor_mixer =
+ register_sound_mixer(&saa7134_mixer_fops,
+ mixer_nr[dev->nr]);
+ if (err < 0)
+ goto fail5;
+ printk(KERN_INFO "%s: registered device mixer%d\n",
+ dev->name,dev->oss.minor_mixer >> 4);
+ }
+ break;
+ }
+
+ /* everything worked */
+ pci_set_drvdata(pci_dev,dev);
+ saa7134_devcount++;
+
+ down(&devlist_lock);
+ list_for_each(item,&mops_list) {
+ mops = list_entry(item, struct saa7134_mpeg_ops, next);
+ mpeg_ops_attach(mops, dev);
+ }
+ list_add_tail(&dev->devlist,&saa7134_devlist);
+ up(&devlist_lock);
+
+ /* check for signal */
+ saa7134_irq_video_intl(dev);
+ return 0;
+
+ fail5:
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ if (oss)
+ unregister_sound_dsp(dev->oss.minor_dsp);
+ break;
+ }
+ fail4:
+ saa7134_unregister_video(dev);
+ saa7134_i2c_unregister(dev);
+ free_irq(pci_dev->irq, dev);
+ fail3:
+ saa7134_hwfini(dev);
+ iounmap(dev->lmmio);
+ fail2:
+ release_mem_region(pci_resource_start(pci_dev,0),
+ pci_resource_len(pci_dev,0));
+ fail1:
+ kfree(dev);
+ return err;
+}
+
+static void __devexit saa7134_finidev(struct pci_dev *pci_dev)
+{
+ struct saa7134_dev *dev = pci_get_drvdata(pci_dev);
+ struct list_head *item;
+ struct saa7134_mpeg_ops *mops;
+
+ /* debugging ... */
+ if (irq_debug) {
+ u32 report = saa_readl(SAA7134_IRQ_REPORT);
+ u32 status = saa_readl(SAA7134_IRQ_STATUS);
+ print_irqstatus(dev,42,report,status);
+ }
+
+ /* disable peripheral devices */
+ saa_writeb(SAA7134_SPECIAL_MODE,0);
+
+ /* shutdown hardware */
+ saa_writel(SAA7134_IRQ1,0);
+ saa_writel(SAA7134_IRQ2,0);
+ saa_writel(SAA7134_MAIN_CTRL,0);
+
+ /* shutdown subsystems */
+ saa7134_hwfini(dev);
+
+ /* unregister */
+ down(&devlist_lock);
+ list_del(&dev->devlist);
+ list_for_each(item,&mops_list) {
+ mops = list_entry(item, struct saa7134_mpeg_ops, next);
+ mpeg_ops_detach(mops, dev);
+ }
+ up(&devlist_lock);
+ saa7134_devcount--;
+
+ saa7134_i2c_unregister(dev);
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ if (oss) {
+ unregister_sound_mixer(dev->oss.minor_mixer);
+ unregister_sound_dsp(dev->oss.minor_dsp);
+ }
+ break;
+ }
+ saa7134_unregister_video(dev);
+
+ /* release ressources */
+ free_irq(pci_dev->irq, dev);
+ iounmap(dev->lmmio);
+ release_mem_region(pci_resource_start(pci_dev,0),
+ pci_resource_len(pci_dev,0));
+
+#if 0 /* causes some trouble when reinserting the driver ... */
+ pci_disable_device(pci_dev);
+#endif
+ pci_set_drvdata(pci_dev, NULL);
+
+ /* free memory */
+ kfree(dev);
+}
+
+/* ----------------------------------------------------------- */
+
+int saa7134_ts_register(struct saa7134_mpeg_ops *ops)
+{
+ struct list_head *item;
+ struct saa7134_dev *dev;
+
+ down(&devlist_lock);
+ list_for_each(item,&saa7134_devlist) {
+ dev = list_entry(item, struct saa7134_dev, devlist);
+ mpeg_ops_attach(ops, dev);
+ }
+ list_add_tail(&ops->next,&mops_list);
+ up(&devlist_lock);
+ return 0;
+}
+
+void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops)
+{
+ struct list_head *item;
+ struct saa7134_dev *dev;
+
+ down(&devlist_lock);
+ list_del(&ops->next);
+ list_for_each(item,&saa7134_devlist) {
+ dev = list_entry(item, struct saa7134_dev, devlist);
+ mpeg_ops_detach(ops, dev);
+ }
+ up(&devlist_lock);
+}
+
+EXPORT_SYMBOL(saa7134_ts_register);
+EXPORT_SYMBOL(saa7134_ts_unregister);
+
+/* ----------------------------------------------------------- */
+
+static struct pci_driver saa7134_pci_driver = {
+ .name = "saa7134",
+ .id_table = saa7134_pci_tbl,
+ .probe = saa7134_initdev,
+ .remove = __devexit_p(saa7134_finidev),
+};
+
+static int saa7134_init(void)
+{
+ INIT_LIST_HEAD(&saa7134_devlist);
+ printk(KERN_INFO "saa7130/34: v4l2 driver version %d.%d.%d loaded\n",
+ (SAA7134_VERSION_CODE >> 16) & 0xff,
+ (SAA7134_VERSION_CODE >> 8) & 0xff,
+ SAA7134_VERSION_CODE & 0xff);
+#ifdef SNAPSHOT
+ printk(KERN_INFO "saa7130/34: snapshot date %04d-%02d-%02d\n",
+ SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
+#endif
+ return pci_module_init(&saa7134_pci_driver);
+}
+
+static void saa7134_fini(void)
+{
+#ifdef CONFIG_MODULES
+ if (pending_registered)
+ unregister_module_notifier(&pending_notifier);
+#endif
+ pci_unregister_driver(&saa7134_pci_driver);
+}
+
+module_init(saa7134_init);
+module_exit(saa7134_fini);
+
+/* ----------------------------------------------------------- */
+
+EXPORT_SYMBOL(saa7134_print_ioctl);
+EXPORT_SYMBOL(saa7134_i2c_call_clients);
+EXPORT_SYMBOL(saa7134_devlist);
+EXPORT_SYMBOL(saa7134_boards);
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
new file mode 100644
index 00000000000..dd4a6c8ee65
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -0,0 +1,266 @@
+/*
+ * $Id: saa7134-dvb.c,v 1.12 2005/02/18 12:28:29 kraxel Exp $
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/suspend.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+#include "dvb-pll.h"
+#include "mt352.h"
+#include "mt352_priv.h" /* FIXME */
+#include "tda1004x.h"
+
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static unsigned int antenna_pwr = 0;
+module_param(antenna_pwr, int, 0444);
+MODULE_PARM_DESC(antenna_pwr,"enable antenna power (Pinnacle 300i)");
+
+/* ------------------------------------------------------------------ */
+
+static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on)
+{
+ u32 ok;
+
+ if (!on) {
+ saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 26));
+ saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26));
+ return 0;
+ }
+
+ saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 26));
+ saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26));
+ udelay(10);
+
+ saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 28));
+ saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28));
+ udelay(10);
+ saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28));
+ udelay(10);
+ ok = saa_readl(SAA7134_GPIO_GPSTATUS0) & (1 << 27);
+ printk("%s: %s %s\n", dev->name, __FUNCTION__,
+ ok ? "on" : "off");
+
+ if (!ok)
+ saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26));
+ return ok;
+}
+
+static int mt352_pinnacle_init(struct dvb_frontend* fe)
+{
+ static u8 clock_config [] = { CLOCK_CTL, 0x3d, 0x28 };
+ static u8 reset [] = { RESET, 0x80 };
+ static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
+ static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0xa0 };
+ static u8 capt_range_cfg[] = { CAPT_RANGE, 0x31 };
+ static u8 fsm_ctl_cfg[] = { 0x7b, 0x04 };
+ static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x0f };
+ static u8 scan_ctl_cfg [] = { SCAN_CTL, 0x0d };
+ static u8 irq_cfg [] = { INTERRUPT_EN_0, 0x00, 0x00, 0x00, 0x00 };
+ struct saa7134_dev *dev= fe->dvb->priv;
+
+ printk("%s: %s called\n",dev->name,__FUNCTION__);
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(200);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+ mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg));
+
+ mt352_write(fe, fsm_ctl_cfg, sizeof(fsm_ctl_cfg));
+ mt352_write(fe, scan_ctl_cfg, sizeof(scan_ctl_cfg));
+ mt352_write(fe, irq_cfg, sizeof(irq_cfg));
+ return 0;
+}
+
+static int mt352_pinnacle_pll_set(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters* params,
+ u8* pllbuf)
+{
+ static int on = TDA9887_PRESENT | TDA9887_PORT2_INACTIVE;
+ static int off = TDA9887_PRESENT | TDA9887_PORT2_ACTIVE;
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct v4l2_frequency f;
+
+ /* set frequency (mt2050) */
+ f.tuner = 0;
+ f.type = V4L2_TUNER_DIGITAL_TV;
+ f.frequency = params->frequency / 1000 * 16 / 1000;
+ saa7134_i2c_call_clients(dev,TDA9887_SET_CONFIG,&on);
+ saa7134_i2c_call_clients(dev,VIDIOC_S_FREQUENCY,&f);
+ saa7134_i2c_call_clients(dev,TDA9887_SET_CONFIG,&off);
+
+ pinnacle_antenna_pwr(dev, antenna_pwr);
+
+ /* mt352 setup */
+ mt352_pinnacle_init(fe);
+ pllbuf[0] = 0xc2;
+ pllbuf[1] = 0x00;
+ pllbuf[2] = 0x00;
+ pllbuf[3] = 0x80;
+ pllbuf[4] = 0x00;
+ return 0;
+}
+
+static struct mt352_config pinnacle_300i = {
+ .demod_address = 0x3c >> 1,
+ .adc_clock = 20333,
+ .if2 = 36150,
+ .no_tuner = 1,
+ .demod_init = mt352_pinnacle_init,
+ .pll_set = mt352_pinnacle_pll_set,
+};
+
+/* ------------------------------------------------------------------ */
+
+static int medion_cardbus_init(struct dvb_frontend* fe)
+{
+ /* anything to do here ??? */
+ return 0;
+}
+
+static int medion_cardbus_pll_set(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters* params)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct v4l2_frequency f;
+
+ /*
+ * this instructs tuner.o to set the frequency, the call will
+ * end up in tuner_command(), VIDIOC_S_FREQUENCY switch.
+ * tda9887.o will see that as well.
+ */
+ f.tuner = 0;
+ f.type = V4L2_TUNER_DIGITAL_TV;
+ f.frequency = params->frequency / 1000 * 16 / 1000;
+ saa7134_i2c_call_clients(dev,VIDIOC_S_FREQUENCY,&f);
+ return 0;
+}
+
+static int fe_request_firmware(struct dvb_frontend* fe,
+ const struct firmware **fw, char* name)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ return request_firmware(fw, name, &dev->pci->dev);
+}
+
+struct tda1004x_config medion_cardbus = {
+ .demod_address = 0x08, /* not sure this is correct */
+ .invert = 0,
+ .invert_oclk = 0,
+ .pll_init = medion_cardbus_init,
+ .pll_set = medion_cardbus_pll_set,
+ .request_firmware = fe_request_firmware,
+};
+
+/* ------------------------------------------------------------------ */
+
+static int dvb_init(struct saa7134_dev *dev)
+{
+ /* init struct videobuf_dvb */
+ dev->ts.nr_bufs = 32;
+ dev->ts.nr_packets = 32*4;
+ dev->dvb.name = dev->name;
+ videobuf_queue_init(&dev->dvb.dvbq, &saa7134_ts_qops,
+ dev->pci, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_ALTERNATE,
+ sizeof(struct saa7134_buf),
+ dev);
+
+ switch (dev->board) {
+ case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL:
+ printk("%s: pinnacle 300i dvb setup\n",dev->name);
+ dev->dvb.frontend = mt352_attach(&pinnacle_300i,
+ &dev->i2c_adap);
+ break;
+ case SAA7134_BOARD_MD7134:
+ dev->dvb.frontend = tda10046_attach(&medion_cardbus,
+ &dev->i2c_adap);
+ if (NULL == dev->dvb.frontend)
+ printk("%s: Hmm, looks like this is the old MD7134 "
+ "version without DVB-T support\n",dev->name);
+ break;
+ default:
+ printk("%s: Huh? unknown DVB card?\n",dev->name);
+ break;
+ }
+
+ if (NULL == dev->dvb.frontend) {
+ printk("%s: frontend initialization failed\n",dev->name);
+ return -1;
+ }
+
+ /* register everything else */
+ return videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev);
+}
+
+static int dvb_fini(struct saa7134_dev *dev)
+{
+ static int on = TDA9887_PRESENT | TDA9887_PORT2_INACTIVE;
+
+ printk("%s: %s\n",dev->name,__FUNCTION__);
+
+ switch (dev->board) {
+ case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL:
+ /* otherwise we don't detect the tuner on next insmod */
+ saa7134_i2c_call_clients(dev,TDA9887_SET_CONFIG,&on);
+ break;
+ };
+ videobuf_dvb_unregister(&dev->dvb);
+ return 0;
+}
+
+static struct saa7134_mpeg_ops dvb_ops = {
+ .type = SAA7134_MPEG_DVB,
+ .init = dvb_init,
+ .fini = dvb_fini,
+};
+
+static int __init dvb_register(void)
+{
+ return saa7134_ts_register(&dvb_ops);
+}
+
+static void __exit dvb_unregister(void)
+{
+ saa7134_ts_unregister(&dvb_ops);
+}
+
+module_init(dvb_register);
+module_exit(dvb_unregister);
+
+/* ------------------------------------------------------------------ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c
new file mode 100644
index 00000000000..2021e099e35
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-empress.c
@@ -0,0 +1,436 @@
+/*
+ * $Id: saa7134-empress.c,v 1.10 2005/02/03 10:24:33 kraxel Exp $
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+#include <media/saa6752hs.h>
+
+/* ------------------------------------------------------------------ */
+
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static unsigned int empress_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+module_param_array(empress_nr, int, NULL, 0444);
+MODULE_PARM_DESC(empress_nr,"ts device number");
+
+static unsigned int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages");
+
+#define dprintk(fmt, arg...) if (debug) \
+ printk(KERN_DEBUG "%s/empress: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static void ts_reset_encoder(struct saa7134_dev* dev)
+{
+ if (!dev->empress_started)
+ return;
+
+ saa_writeb(SAA7134_SPECIAL_MODE, 0x00);
+ msleep(10);
+ saa_writeb(SAA7134_SPECIAL_MODE, 0x01);
+ msleep(100);
+ dev->empress_started = 0;
+}
+
+static int ts_init_encoder(struct saa7134_dev* dev)
+{
+ ts_reset_encoder(dev);
+ saa7134_i2c_call_clients(dev, VIDIOC_S_MPEGCOMP, NULL);
+ dev->empress_started = 1;
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int ts_open(struct inode *inode, struct file *file)
+{
+ int minor = iminor(inode);
+ struct saa7134_dev *h,*dev = NULL;
+ struct list_head *list;
+ int err;
+
+ list_for_each(list,&saa7134_devlist) {
+ h = list_entry(list, struct saa7134_dev, devlist);
+ if (h->empress_dev && h->empress_dev->minor == minor)
+ dev = h;
+ }
+ if (NULL == dev)
+ return -ENODEV;
+
+ dprintk("open minor=%d\n",minor);
+ err = -EBUSY;
+ if (down_trylock(&dev->empress_tsq.lock))
+ goto done;
+ if (dev->empress_users)
+ goto done_up;
+
+ dev->empress_users++;
+ file->private_data = dev;
+ err = 0;
+
+done_up:
+ up(&dev->empress_tsq.lock);
+done:
+ return err;
+}
+
+static int ts_release(struct inode *inode, struct file *file)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ if (dev->empress_tsq.streaming)
+ videobuf_streamoff(&dev->empress_tsq);
+ down(&dev->empress_tsq.lock);
+ if (dev->empress_tsq.reading)
+ videobuf_read_stop(&dev->empress_tsq);
+ videobuf_mmap_free(&dev->empress_tsq);
+ dev->empress_users--;
+
+ /* stop the encoder */
+ ts_reset_encoder(dev);
+
+ up(&dev->empress_tsq.lock);
+ return 0;
+}
+
+static ssize_t
+ts_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ if (!dev->empress_started)
+ ts_init_encoder(dev);
+
+ return videobuf_read_stream(&dev->empress_tsq,
+ data, count, ppos, 0,
+ file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int
+ts_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ return videobuf_poll_stream(file, &dev->empress_tsq, wait);
+}
+
+
+static int
+ts_mmap(struct file *file, struct vm_area_struct * vma)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ return videobuf_mmap_mapper(&dev->empress_tsq, vma);
+}
+
+/*
+ * This function is _not_ called directly, but from
+ * video_generic_ioctl (and maybe others). userspace
+ * copying is done already, arg is a kernel pointer.
+ */
+static int ts_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ if (debug > 1)
+ saa7134_print_ioctl(dev->name,cmd);
+ switch (cmd) {
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = arg;
+
+ memset(cap,0,sizeof(*cap));
+ strcpy(cap->driver, "saa7134");
+ strlcpy(cap->card, saa7134_boards[dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
+ cap->version = SAA7134_VERSION_CODE;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ return 0;
+ }
+
+ /* --- input switching --------------------------------------- */
+ case VIDIOC_ENUMINPUT:
+ {
+ struct v4l2_input *i = arg;
+
+ if (i->index != 0)
+ return -EINVAL;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(i->name,"CCIR656");
+ return 0;
+ }
+ case VIDIOC_G_INPUT:
+ {
+ int *i = arg;
+ *i = 0;
+ return 0;
+ }
+ case VIDIOC_S_INPUT:
+ {
+ int *i = arg;
+
+ if (*i != 0)
+ return -EINVAL;
+ return 0;
+ }
+ /* --- capture ioctls ---------------------------------------- */
+
+ case VIDIOC_ENUM_FMT:
+ {
+ struct v4l2_fmtdesc *f = arg;
+ int index;
+
+ index = f->index;
+ if (index != 0)
+ return -EINVAL;
+
+ memset(f,0,sizeof(*f));
+ f->index = index;
+ strlcpy(f->description, "MPEG TS", sizeof(f->description));
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->pixelformat = V4L2_PIX_FMT_MPEG;
+ return 0;
+ }
+
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format *f = arg;
+
+ memset(f,0,sizeof(*f));
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ /* FIXME: translate subsampling type EMPRESS into
+ * width/height: */
+ f->fmt.pix.width = 720; /* D1 */
+ f->fmt.pix.height = 576;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets;
+ return 0;
+ }
+
+ case VIDIOC_S_FMT:
+ {
+ struct v4l2_format *f = arg;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ /*
+ FIXME: translate and round width/height into EMPRESS
+ subsample type:
+
+ type | PAL | NTSC
+ ---------------------------
+ SIF | 352x288 | 352x240
+ 1/2 D1 | 352x576 | 352x480
+ 2/3 D1 | 480x576 | 480x480
+ D1 | 720x576 | 720x480
+ */
+
+ f->fmt.pix.width = 720; /* D1 */
+ f->fmt.pix.height = 576;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.sizeimage = TS_PACKET_SIZE* dev->ts.nr_packets;
+ return 0;
+ }
+
+ case VIDIOC_REQBUFS:
+ return videobuf_reqbufs(&dev->empress_tsq,arg);
+
+ case VIDIOC_QUERYBUF:
+ return videobuf_querybuf(&dev->empress_tsq,arg);
+
+ case VIDIOC_QBUF:
+ return videobuf_qbuf(&dev->empress_tsq,arg);
+
+ case VIDIOC_DQBUF:
+ return videobuf_dqbuf(&dev->empress_tsq,arg,
+ file->f_flags & O_NONBLOCK);
+
+ case VIDIOC_STREAMON:
+ return videobuf_streamon(&dev->empress_tsq);
+
+ case VIDIOC_STREAMOFF:
+ return videobuf_streamoff(&dev->empress_tsq);
+
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_G_CTRL:
+ case VIDIOC_S_CTRL:
+ return saa7134_common_ioctl(dev, cmd, arg);
+
+ case VIDIOC_S_MPEGCOMP:
+ saa7134_i2c_call_clients(dev, VIDIOC_S_MPEGCOMP, arg);
+ ts_init_encoder(dev);
+ return 0;
+ case VIDIOC_G_MPEGCOMP:
+ saa7134_i2c_call_clients(dev, VIDIOC_G_MPEGCOMP, arg);
+ return 0;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int ts_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, ts_do_ioctl);
+}
+
+static struct file_operations ts_fops =
+{
+ .owner = THIS_MODULE,
+ .open = ts_open,
+ .release = ts_release,
+ .read = ts_read,
+ .poll = ts_poll,
+ .mmap = ts_mmap,
+ .ioctl = ts_ioctl,
+ .llseek = no_llseek,
+};
+
+/* ----------------------------------------------------------- */
+
+static struct video_device saa7134_empress_template =
+{
+ .name = "saa7134-empress",
+ .type = 0 /* FIXME */,
+ .type2 = 0 /* FIXME */,
+ .hardware = 0,
+ .fops = &ts_fops,
+ .minor = -1,
+};
+
+static void empress_signal_update(void* data)
+{
+ struct saa7134_dev* dev = (struct saa7134_dev*) data;
+
+ if (dev->nosignal) {
+ dprintk("no video signal\n");
+ ts_reset_encoder(dev);
+ } else {
+ dprintk("video signal acquired\n");
+ if (dev->empress_users)
+ ts_init_encoder(dev);
+ }
+}
+
+static void empress_signal_change(struct saa7134_dev *dev)
+{
+ schedule_work(&dev->empress_workqueue);
+}
+
+
+static int empress_init(struct saa7134_dev *dev)
+{
+ int err;
+
+ dprintk("%s: %s\n",dev->name,__FUNCTION__);
+ dev->empress_dev = video_device_alloc();
+ if (NULL == dev->empress_dev)
+ return -ENOMEM;
+ *(dev->empress_dev) = saa7134_empress_template;
+ dev->empress_dev->dev = &dev->pci->dev;
+ dev->empress_dev->release = video_device_release;
+ snprintf(dev->empress_dev->name, sizeof(dev->empress_dev->name),
+ "%s empress (%s)", dev->name,
+ saa7134_boards[dev->board].name);
+
+ INIT_WORK(&dev->empress_workqueue, empress_signal_update, (void*) dev);
+
+ err = video_register_device(dev->empress_dev,VFL_TYPE_GRABBER,
+ empress_nr[dev->nr]);
+ if (err < 0) {
+ printk(KERN_INFO "%s: can't register video device\n",
+ dev->name);
+ video_device_release(dev->empress_dev);
+ dev->empress_dev = NULL;
+ return err;
+ }
+ printk(KERN_INFO "%s: registered device video%d [mpeg]\n",
+ dev->name,dev->empress_dev->minor & 0x1f);
+
+ videobuf_queue_init(&dev->empress_tsq, &saa7134_ts_qops,
+ dev->pci, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_ALTERNATE,
+ sizeof(struct saa7134_buf),
+ dev);
+
+ empress_signal_update(dev);
+ return 0;
+}
+
+static int empress_fini(struct saa7134_dev *dev)
+{
+ dprintk("%s: %s\n",dev->name,__FUNCTION__);
+
+ if (NULL == dev->empress_dev)
+ return 0;
+ flush_scheduled_work();
+ video_unregister_device(dev->empress_dev);
+ dev->empress_dev = NULL;
+ return 0;
+}
+
+static struct saa7134_mpeg_ops empress_ops = {
+ .type = SAA7134_MPEG_EMPRESS,
+ .init = empress_init,
+ .fini = empress_fini,
+ .signal_change = empress_signal_change,
+};
+
+static int __init empress_register(void)
+{
+ return saa7134_ts_register(&empress_ops);
+}
+
+static void __exit empress_unregister(void)
+{
+ saa7134_ts_unregister(&empress_ops);
+}
+
+module_init(empress_register);
+module_exit(empress_unregister);
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c
new file mode 100644
index 00000000000..702bb63d981
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-i2c.c
@@ -0,0 +1,453 @@
+/*
+ * $Id: saa7134-i2c.c,v 1.10 2005/01/24 17:37:23 kraxel Exp $
+ *
+ * device driver for philips saa7134 based TV cards
+ * i2c interface support
+ *
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+/* ----------------------------------------------------------- */
+
+static unsigned int i2c_debug = 0;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug,"enable debug messages [i2c]");
+
+static unsigned int i2c_scan = 0;
+module_param(i2c_scan, int, 0444);
+MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
+
+#define d1printk if (1 == i2c_debug) printk
+#define d2printk if (2 == i2c_debug) printk
+
+#define I2C_WAIT_DELAY 32
+#define I2C_WAIT_RETRY 16
+
+/* ----------------------------------------------------------- */
+
+static char *str_i2c_status[] = {
+ "IDLE", "DONE_STOP", "BUSY", "TO_SCL", "TO_ARB", "DONE_WRITE",
+ "DONE_READ", "DONE_WRITE_TO", "DONE_READ_TO", "NO_DEVICE",
+ "NO_ACKN", "BUS_ERR", "ARB_LOST", "SEQ_ERR", "ST_ERR", "SW_ERR"
+};
+
+enum i2c_status {
+ IDLE = 0, // no I2C command pending
+ DONE_STOP = 1, // I2C command done and STOP executed
+ BUSY = 2, // executing I2C command
+ TO_SCL = 3, // executing I2C command, time out on clock stretching
+ TO_ARB = 4, // time out on arbitration trial, still trying
+ DONE_WRITE = 5, // I2C command done and awaiting next write command
+ DONE_READ = 6, // I2C command done and awaiting next read command
+ DONE_WRITE_TO = 7, // see 5, and time out on status echo
+ DONE_READ_TO = 8, // see 6, and time out on status echo
+ NO_DEVICE = 9, // no acknowledge on device slave address
+ NO_ACKN = 10, // no acknowledge after data byte transfer
+ BUS_ERR = 11, // bus error
+ ARB_LOST = 12, // arbitration lost during transfer
+ SEQ_ERR = 13, // erroneous programming sequence
+ ST_ERR = 14, // wrong status echoing
+ SW_ERR = 15 // software error
+};
+
+static char *str_i2c_attr[] = {
+ "NOP", "STOP", "CONTINUE", "START"
+};
+
+enum i2c_attr {
+ NOP = 0, // no operation on I2C bus
+ STOP = 1, // stop condition, no associated byte transfer
+ CONTINUE = 2, // continue with byte transfer
+ START = 3 // start condition with byte transfer
+};
+
+static inline enum i2c_status i2c_get_status(struct saa7134_dev *dev)
+{
+ enum i2c_status status;
+
+ status = saa_readb(SAA7134_I2C_ATTR_STATUS) & 0x0f;
+ d2printk(KERN_DEBUG "%s: i2c stat <= %s\n",dev->name,
+ str_i2c_status[status]);
+ return status;
+}
+
+static inline void i2c_set_status(struct saa7134_dev *dev,
+ enum i2c_status status)
+{
+ d2printk(KERN_DEBUG "%s: i2c stat => %s\n",dev->name,
+ str_i2c_status[status]);
+ saa_andorb(SAA7134_I2C_ATTR_STATUS,0x0f,status);
+}
+
+static inline void i2c_set_attr(struct saa7134_dev *dev, enum i2c_attr attr)
+{
+ d2printk(KERN_DEBUG "%s: i2c attr => %s\n",dev->name,
+ str_i2c_attr[attr]);
+ saa_andorb(SAA7134_I2C_ATTR_STATUS,0xc0,attr << 6);
+}
+
+static inline int i2c_is_error(enum i2c_status status)
+{
+ switch (status) {
+ case NO_DEVICE:
+ case NO_ACKN:
+ case BUS_ERR:
+ case ARB_LOST:
+ case SEQ_ERR:
+ case ST_ERR:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static inline int i2c_is_idle(enum i2c_status status)
+{
+ switch (status) {
+ case IDLE:
+ case DONE_STOP:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static inline int i2c_is_busy(enum i2c_status status)
+{
+ switch (status) {
+ case BUSY:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static int i2c_is_busy_wait(struct saa7134_dev *dev)
+{
+ enum i2c_status status;
+ int count;
+
+ for (count = 0; count < I2C_WAIT_RETRY; count++) {
+ status = i2c_get_status(dev);
+ if (!i2c_is_busy(status))
+ break;
+ saa_wait(I2C_WAIT_DELAY);
+ }
+ if (I2C_WAIT_RETRY == count)
+ return FALSE;
+ return TRUE;
+}
+
+static int i2c_reset(struct saa7134_dev *dev)
+{
+ enum i2c_status status;
+ int count;
+
+ d2printk(KERN_DEBUG "%s: i2c reset\n",dev->name);
+ status = i2c_get_status(dev);
+ if (!i2c_is_error(status))
+ return TRUE;
+ i2c_set_status(dev,status);
+
+ for (count = 0; count < I2C_WAIT_RETRY; count++) {
+ status = i2c_get_status(dev);
+ if (!i2c_is_error(status))
+ break;
+ udelay(I2C_WAIT_DELAY);
+ }
+ if (I2C_WAIT_RETRY == count)
+ return FALSE;
+
+ if (!i2c_is_idle(status))
+ return FALSE;
+
+ i2c_set_attr(dev,NOP);
+ return TRUE;
+}
+
+static inline int i2c_send_byte(struct saa7134_dev *dev,
+ enum i2c_attr attr,
+ unsigned char data)
+{
+ enum i2c_status status;
+ __u32 dword;
+
+#if 0
+ i2c_set_attr(dev,attr);
+ saa_writeb(SAA7134_I2C_DATA, data);
+#else
+ /* have to write both attr + data in one 32bit word */
+ dword = saa_readl(SAA7134_I2C_ATTR_STATUS >> 2);
+ dword &= 0x0f;
+ dword |= (attr << 6);
+ dword |= ((__u32)data << 8);
+ dword |= 0x00 << 16; /* 100 kHz */
+// dword |= 0x40 << 16; /* 400 kHz */
+ dword |= 0xf0 << 24;
+ saa_writel(SAA7134_I2C_ATTR_STATUS >> 2, dword);
+#endif
+ d2printk(KERN_DEBUG "%s: i2c data => 0x%x\n",dev->name,data);
+
+ if (!i2c_is_busy_wait(dev))
+ return -EIO;
+ status = i2c_get_status(dev);
+ if (i2c_is_error(status))
+ return -EIO;
+ return 0;
+}
+
+static inline int i2c_recv_byte(struct saa7134_dev *dev)
+{
+ enum i2c_status status;
+ unsigned char data;
+
+ i2c_set_attr(dev,CONTINUE);
+ if (!i2c_is_busy_wait(dev))
+ return -EIO;
+ status = i2c_get_status(dev);
+ if (i2c_is_error(status))
+ return -EIO;
+ data = saa_readb(SAA7134_I2C_DATA);
+ d2printk(KERN_DEBUG "%s: i2c data <= 0x%x\n",dev->name,data);
+ return data;
+}
+
+static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct saa7134_dev *dev = i2c_adap->algo_data;
+ enum i2c_status status;
+ unsigned char data;
+ int addr,rc,i,byte;
+
+ status = i2c_get_status(dev);
+ if (!i2c_is_idle(status))
+ if (!i2c_reset(dev))
+ return -EIO;
+
+ d2printk("start xfer\n");
+ d1printk(KERN_DEBUG "%s: i2c xfer:",dev->name);
+ for (i = 0; i < num; i++) {
+ if (!(msgs[i].flags & I2C_M_NOSTART) || 0 == i) {
+ /* send address */
+ d2printk("send address\n");
+ addr = msgs[i].addr << 1;
+ if (msgs[i].flags & I2C_M_RD)
+ addr |= 1;
+ if (i > 0 && msgs[i].flags & I2C_M_RD) {
+ /* workaround for a saa7134 i2c bug
+ * needed to talk to the mt352 demux
+ * thanks to pinnacle for the hint */
+ int quirk = 0xfd;
+ d1printk(" [%02x quirk]",quirk);
+ i2c_send_byte(dev,START,quirk);
+ i2c_recv_byte(dev);
+ }
+ d1printk(" < %02x", addr);
+ rc = i2c_send_byte(dev,START,addr);
+ if (rc < 0)
+ goto err;
+ }
+ if (msgs[i].flags & I2C_M_RD) {
+ /* read bytes */
+ d2printk("read bytes\n");
+ for (byte = 0; byte < msgs[i].len; byte++) {
+ d1printk(" =");
+ rc = i2c_recv_byte(dev);
+ if (rc < 0)
+ goto err;
+ d1printk("%02x", rc);
+ msgs[i].buf[byte] = rc;
+ }
+ } else {
+ /* write bytes */
+ d2printk("write bytes\n");
+ for (byte = 0; byte < msgs[i].len; byte++) {
+ data = msgs[i].buf[byte];
+ d1printk(" %02x", data);
+ rc = i2c_send_byte(dev,CONTINUE,data);
+ if (rc < 0)
+ goto err;
+ }
+ }
+ }
+ d2printk("xfer done\n");
+ d1printk(" >");
+ i2c_set_attr(dev,STOP);
+ rc = -EIO;
+ if (!i2c_is_busy_wait(dev))
+ goto err;
+ status = i2c_get_status(dev);
+ if (i2c_is_error(status))
+ goto err;
+
+ d1printk("\n");
+ return num;
+ err:
+ if (1 == i2c_debug) {
+ status = i2c_get_status(dev);
+ printk(" ERROR: %s\n",str_i2c_status[status]);
+ }
+ return rc;
+}
+
+/* ----------------------------------------------------------- */
+
+static int algo_control(struct i2c_adapter *adapter,
+ unsigned int cmd, unsigned long arg)
+{
+ return 0;
+}
+
+static u32 functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL;
+}
+
+static int attach_inform(struct i2c_client *client)
+{
+ struct saa7134_dev *dev = client->adapter->algo_data;
+ int tuner = dev->tuner_type;
+ int conf = dev->tda9887_conf;
+
+ saa7134_i2c_call_clients(dev,TUNER_SET_TYPE,&tuner);
+ saa7134_i2c_call_clients(dev,TDA9887_SET_CONFIG,&conf);
+ return 0;
+}
+
+static struct i2c_algorithm saa7134_algo = {
+ .name = "saa7134",
+ .id = I2C_ALGO_SAA7134,
+ .master_xfer = saa7134_i2c_xfer,
+ .algo_control = algo_control,
+ .functionality = functionality,
+};
+
+static struct i2c_adapter saa7134_adap_template = {
+ .owner = THIS_MODULE,
+#ifdef I2C_CLASS_TV_ANALOG
+ .class = I2C_CLASS_TV_ANALOG,
+#endif
+ I2C_DEVNAME("saa7134"),
+ .id = I2C_ALGO_SAA7134,
+ .algo = &saa7134_algo,
+ .client_register = attach_inform,
+};
+
+static struct i2c_client saa7134_client_template = {
+ I2C_DEVNAME("saa7134 internal"),
+};
+
+/* ----------------------------------------------------------- */
+
+static int
+saa7134_i2c_eeprom(struct saa7134_dev *dev, unsigned char *eedata, int len)
+{
+ unsigned char buf;
+ int i,err;
+
+ dev->i2c_client.addr = 0xa0 >> 1;
+ buf = 0;
+ if (1 != (err = i2c_master_send(&dev->i2c_client,&buf,1))) {
+ printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n",
+ dev->name,err);
+ return -1;
+ }
+ if (len != (err = i2c_master_recv(&dev->i2c_client,eedata,len))) {
+ printk(KERN_WARNING "%s: i2c eeprom read error (err=%d)\n",
+ dev->name,err);
+ return -1;
+ }
+ for (i = 0; i < len; i++) {
+ if (0 == (i % 16))
+ printk(KERN_INFO "%s: i2c eeprom %02x:",dev->name,i);
+ printk(" %02x",eedata[i]);
+ if (15 == (i % 16))
+ printk("\n");
+ }
+ return 0;
+}
+
+static char *i2c_devs[128] = {
+ [ 0x20 ] = "mpeg encoder (saa6752hs)",
+ [ 0xa0 >> 1 ] = "eeprom",
+ [ 0xc0 >> 1 ] = "tuner (analog)",
+ [ 0x86 >> 1 ] = "tda9887",
+};
+
+static void do_i2c_scan(char *name, struct i2c_client *c)
+{
+ unsigned char buf;
+ int i,rc;
+
+ for (i = 0; i < 128; i++) {
+ c->addr = i;
+ rc = i2c_master_recv(c,&buf,0);
+ if (rc < 0)
+ continue;
+ printk("%s: i2c scan: found device @ 0x%x [%s]\n",
+ name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+ }
+}
+
+void saa7134_i2c_call_clients(struct saa7134_dev *dev,
+ unsigned int cmd, void *arg)
+{
+ BUG_ON(NULL == dev->i2c_adap.algo_data);
+ i2c_clients_command(&dev->i2c_adap, cmd, arg);
+}
+
+int saa7134_i2c_register(struct saa7134_dev *dev)
+{
+ dev->i2c_adap = saa7134_adap_template;
+ dev->i2c_adap.dev.parent = &dev->pci->dev;
+ strcpy(dev->i2c_adap.name,dev->name);
+ dev->i2c_adap.algo_data = dev;
+ i2c_add_adapter(&dev->i2c_adap);
+
+ dev->i2c_client = saa7134_client_template;
+ dev->i2c_client.adapter = &dev->i2c_adap;
+
+ saa7134_i2c_eeprom(dev,dev->eedata,sizeof(dev->eedata));
+ if (i2c_scan)
+ do_i2c_scan(dev->name,&dev->i2c_client);
+ return 0;
+}
+
+int saa7134_i2c_unregister(struct saa7134_dev *dev)
+{
+ i2c_del_adapter(&dev->i2c_adap);
+ return 0;
+}
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
new file mode 100644
index 00000000000..727d437e07d
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -0,0 +1,491 @@
+/*
+ * $Id: saa7134-input.c,v 1.16 2004/12/10 12:33:39 kraxel Exp $
+ *
+ * handle saa7134 IR remotes via linux kernel input layer.
+ *
+ * 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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+static unsigned int disable_ir = 0;
+module_param(disable_ir, int, 0444);
+MODULE_PARM_DESC(disable_ir,"disable infrared remote support");
+
+static unsigned int ir_debug = 0;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug,"enable debug messages [IR]");
+
+#define dprintk(fmt, arg...) if (ir_debug) \
+ printk(KERN_DEBUG "%s/ir: " fmt, dev->name , ## arg)
+
+/* ---------------------------------------------------------------------- */
+
+static IR_KEYTAB_TYPE flyvideo_codes[IR_KEYTAB_SIZE] = {
+ [ 15 ] = KEY_KP0,
+ [ 3 ] = KEY_KP1,
+ [ 4 ] = KEY_KP2,
+ [ 5 ] = KEY_KP3,
+ [ 7 ] = KEY_KP4,
+ [ 8 ] = KEY_KP5,
+ [ 9 ] = KEY_KP6,
+ [ 11 ] = KEY_KP7,
+ [ 12 ] = KEY_KP8,
+ [ 13 ] = KEY_KP9,
+
+ [ 14 ] = KEY_TUNER, // Air/Cable
+ [ 17 ] = KEY_VIDEO, // Video
+ [ 21 ] = KEY_AUDIO, // Audio
+ [ 0 ] = KEY_POWER, // Pover
+ [ 2 ] = KEY_ZOOM, // Fullscreen
+ [ 27 ] = KEY_MUTE, // Mute
+ [ 20 ] = KEY_VOLUMEUP,
+ [ 23 ] = KEY_VOLUMEDOWN,
+ [ 18 ] = KEY_CHANNELUP, // Channel +
+ [ 19 ] = KEY_CHANNELDOWN, // Channel -
+ [ 6 ] = KEY_AGAIN, // Recal
+ [ 16 ] = KEY_KPENTER, // Enter
+
+#if 1 /* FIXME */
+ [ 26 ] = KEY_F22, // Stereo
+ [ 24 ] = KEY_EDIT, // AV Source
+#endif
+};
+
+static IR_KEYTAB_TYPE cinergy_codes[IR_KEYTAB_SIZE] = {
+ [ 0 ] = KEY_KP0,
+ [ 1 ] = KEY_KP1,
+ [ 2 ] = KEY_KP2,
+ [ 3 ] = KEY_KP3,
+ [ 4 ] = KEY_KP4,
+ [ 5 ] = KEY_KP5,
+ [ 6 ] = KEY_KP6,
+ [ 7 ] = KEY_KP7,
+ [ 8 ] = KEY_KP8,
+ [ 9 ] = KEY_KP9,
+
+ [ 0x0a ] = KEY_POWER,
+ [ 0x0b ] = KEY_PROG1, // app
+ [ 0x0c ] = KEY_ZOOM, // zoom/fullscreen
+ [ 0x0d ] = KEY_CHANNELUP, // channel
+ [ 0x0e ] = KEY_CHANNELDOWN, // channel-
+ [ 0x0f ] = KEY_VOLUMEUP,
+ [ 0x10 ] = KEY_VOLUMEDOWN,
+ [ 0x11 ] = KEY_TUNER, // AV
+ [ 0x12 ] = KEY_NUMLOCK, // -/--
+ [ 0x13 ] = KEY_AUDIO, // audio
+ [ 0x14 ] = KEY_MUTE,
+ [ 0x15 ] = KEY_UP,
+ [ 0x16 ] = KEY_DOWN,
+ [ 0x17 ] = KEY_LEFT,
+ [ 0x18 ] = KEY_RIGHT,
+ [ 0x19 ] = BTN_LEFT,
+ [ 0x1a ] = BTN_RIGHT,
+ [ 0x1b ] = KEY_WWW, // text
+ [ 0x1c ] = KEY_REWIND,
+ [ 0x1d ] = KEY_FORWARD,
+ [ 0x1e ] = KEY_RECORD,
+ [ 0x1f ] = KEY_PLAY,
+ [ 0x20 ] = KEY_PREVIOUSSONG,
+ [ 0x21 ] = KEY_NEXTSONG,
+ [ 0x22 ] = KEY_PAUSE,
+ [ 0x23 ] = KEY_STOP,
+};
+
+/* Alfons Geser <a.geser@cox.net>
+ * updates from Job D. R. Borges <jobdrb@ig.com.br> */
+static IR_KEYTAB_TYPE eztv_codes[IR_KEYTAB_SIZE] = {
+ [ 18 ] = KEY_POWER,
+ [ 1 ] = KEY_TV, // DVR
+ [ 21 ] = KEY_DVD, // DVD
+ [ 23 ] = KEY_AUDIO, // music
+ // DVR mode / DVD mode / music mode
+
+ [ 27 ] = KEY_MUTE, // mute
+ [ 2 ] = KEY_LANGUAGE, // MTS/SAP / audio / autoseek
+ [ 30 ] = KEY_SUBTITLE, // closed captioning / subtitle / seek
+ [ 22 ] = KEY_ZOOM, // full screen
+ [ 28 ] = KEY_VIDEO, // video source / eject / delall
+ [ 29 ] = KEY_RESTART, // playback / angle / del
+ [ 47 ] = KEY_SEARCH, // scan / menu / playlist
+ [ 48 ] = KEY_CHANNEL, // CH surfing / bookmark / memo
+
+ [ 49 ] = KEY_HELP, // help
+ [ 50 ] = KEY_MODE, // num/memo
+ [ 51 ] = KEY_ESC, // cancel
+
+ [ 12 ] = KEY_UP, // up
+ [ 16 ] = KEY_DOWN, // down
+ [ 8 ] = KEY_LEFT, // left
+ [ 4 ] = KEY_RIGHT, // right
+ [ 3 ] = KEY_SELECT, // select
+
+ [ 31 ] = KEY_REWIND, // rewind
+ [ 32 ] = KEY_PLAYPAUSE, // play/pause
+ [ 41 ] = KEY_FORWARD, // forward
+ [ 20 ] = KEY_AGAIN, // repeat
+ [ 43 ] = KEY_RECORD, // recording
+ [ 44 ] = KEY_STOP, // stop
+ [ 45 ] = KEY_PLAY, // play
+ [ 46 ] = KEY_SHUFFLE, // snapshot / shuffle
+
+ [ 0 ] = KEY_KP0,
+ [ 5 ] = KEY_KP1,
+ [ 6 ] = KEY_KP2,
+ [ 7 ] = KEY_KP3,
+ [ 9 ] = KEY_KP4,
+ [ 10 ] = KEY_KP5,
+ [ 11 ] = KEY_KP6,
+ [ 13 ] = KEY_KP7,
+ [ 14 ] = KEY_KP8,
+ [ 15 ] = KEY_KP9,
+
+ [ 42 ] = KEY_VOLUMEUP,
+ [ 17 ] = KEY_VOLUMEDOWN,
+ [ 24 ] = KEY_CHANNELUP, // CH.tracking up
+ [ 25 ] = KEY_CHANNELDOWN, // CH.tracking down
+
+ [ 19 ] = KEY_KPENTER, // enter
+ [ 33 ] = KEY_KPDOT, // . (decimal dot)
+};
+
+static IR_KEYTAB_TYPE avacssmart_codes[IR_KEYTAB_SIZE] = {
+ [ 30 ] = KEY_POWER, // power
+ [ 28 ] = KEY_SEARCH, // scan
+ [ 7 ] = KEY_SELECT, // source
+
+ [ 22 ] = KEY_VOLUMEUP,
+ [ 20 ] = KEY_VOLUMEDOWN,
+ [ 31 ] = KEY_CHANNELUP,
+ [ 23 ] = KEY_CHANNELDOWN,
+ [ 24 ] = KEY_MUTE,
+
+ [ 2 ] = KEY_KP0,
+ [ 1 ] = KEY_KP1,
+ [ 11 ] = KEY_KP2,
+ [ 27 ] = KEY_KP3,
+ [ 5 ] = KEY_KP4,
+ [ 9 ] = KEY_KP5,
+ [ 21 ] = KEY_KP6,
+ [ 6 ] = KEY_KP7,
+ [ 10 ] = KEY_KP8,
+ [ 18 ] = KEY_KP9,
+ [ 16 ] = KEY_KPDOT,
+
+ [ 3 ] = KEY_TUNER, // tv/fm
+ [ 4 ] = KEY_REWIND, // fm tuning left or function left
+ [ 12 ] = KEY_FORWARD, // fm tuning right or function right
+
+ [ 0 ] = KEY_RECORD,
+ [ 8 ] = KEY_STOP,
+ [ 17 ] = KEY_PLAY,
+
+ [ 25 ] = KEY_ZOOM,
+ [ 14 ] = KEY_MENU, // function
+ [ 19 ] = KEY_AGAIN, // recall
+ [ 29 ] = KEY_RESTART, // reset
+
+// FIXME
+ [ 13 ] = KEY_F21, // mts
+ [ 15 ] = KEY_F22, // min
+ [ 26 ] = KEY_F23, // freeze
+};
+
+/* Alex Hermann <gaaf@gmx.net> */
+static IR_KEYTAB_TYPE md2819_codes[IR_KEYTAB_SIZE] = {
+ [ 40 ] = KEY_KP1,
+ [ 24 ] = KEY_KP2,
+ [ 56 ] = KEY_KP3,
+ [ 36 ] = KEY_KP4,
+ [ 20 ] = KEY_KP5,
+ [ 52 ] = KEY_KP6,
+ [ 44 ] = KEY_KP7,
+ [ 28 ] = KEY_KP8,
+ [ 60 ] = KEY_KP9,
+ [ 34 ] = KEY_KP0,
+
+ [ 32 ] = KEY_TV, // TV/FM
+ [ 16 ] = KEY_CD, // CD
+ [ 48 ] = KEY_TEXT, // TELETEXT
+ [ 0 ] = KEY_POWER, // POWER
+
+ [ 8 ] = KEY_VIDEO, // VIDEO
+ [ 4 ] = KEY_AUDIO, // AUDIO
+ [ 12 ] = KEY_ZOOM, // FULL SCREEN
+
+ [ 18 ] = KEY_SUBTITLE, // DISPLAY - ???
+ [ 50 ] = KEY_REWIND, // LOOP - ???
+ [ 2 ] = KEY_PRINT, // PREVIEW - ???
+
+ [ 42 ] = KEY_SEARCH, // AUTOSCAN
+ [ 26 ] = KEY_SLEEP, // FREEZE - ???
+ [ 58 ] = KEY_SHUFFLE, // SNAPSHOT - ???
+ [ 10 ] = KEY_MUTE, // MUTE
+
+ [ 38 ] = KEY_RECORD, // RECORD
+ [ 22 ] = KEY_PAUSE, // PAUSE
+ [ 54 ] = KEY_STOP, // STOP
+ [ 6 ] = KEY_PLAY, // PLAY
+
+ [ 46 ] = KEY_RED, // <RED>
+ [ 33 ] = KEY_GREEN, // <GREEN>
+ [ 14 ] = KEY_YELLOW, // <YELLOW>
+ [ 1 ] = KEY_BLUE, // <BLUE>
+
+ [ 30 ] = KEY_VOLUMEDOWN, // VOLUME-
+ [ 62 ] = KEY_VOLUMEUP, // VOLUME+
+ [ 17 ] = KEY_CHANNELDOWN, // CHANNEL/PAGE-
+ [ 49 ] = KEY_CHANNELUP // CHANNEL/PAGE+
+};
+
+static IR_KEYTAB_TYPE videomate_tv_pvr_codes[IR_KEYTAB_SIZE] = {
+ [ 20 ] = KEY_MUTE,
+ [ 36 ] = KEY_ZOOM,
+
+ [ 1 ] = KEY_DVD,
+ [ 35 ] = KEY_RADIO,
+ [ 0 ] = KEY_TV,
+
+ [ 10 ] = KEY_REWIND,
+ [ 8 ] = KEY_PLAYPAUSE,
+ [ 15 ] = KEY_FORWARD,
+
+ [ 2 ] = KEY_PREVIOUS,
+ [ 7 ] = KEY_STOP,
+ [ 6 ] = KEY_NEXT,
+
+ [ 12 ] = KEY_UP,
+ [ 14 ] = KEY_DOWN,
+ [ 11 ] = KEY_LEFT,
+ [ 13 ] = KEY_RIGHT,
+ [ 17 ] = KEY_OK,
+
+ [ 3 ] = KEY_MENU,
+ [ 9 ] = KEY_SETUP,
+ [ 5 ] = KEY_VIDEO,
+ [ 34 ] = KEY_CHANNEL,
+
+ [ 18 ] = KEY_VOLUMEUP,
+ [ 21 ] = KEY_VOLUMEDOWN,
+ [ 16 ] = KEY_CHANNELUP,
+ [ 19 ] = KEY_CHANNELDOWN,
+
+ [ 4 ] = KEY_RECORD,
+
+ [ 22 ] = KEY_KP1,
+ [ 23 ] = KEY_KP2,
+ [ 24 ] = KEY_KP3,
+ [ 25 ] = KEY_KP4,
+ [ 26 ] = KEY_KP5,
+ [ 27 ] = KEY_KP6,
+ [ 28 ] = KEY_KP7,
+ [ 29 ] = KEY_KP8,
+ [ 30 ] = KEY_KP9,
+ [ 31 ] = KEY_KP0,
+
+ [ 32 ] = KEY_LANGUAGE,
+ [ 33 ] = KEY_SLEEP,
+};
+/* ---------------------------------------------------------------------- */
+
+static int build_key(struct saa7134_dev *dev)
+{
+ struct saa7134_ir *ir = dev->remote;
+ u32 gpio, data;
+
+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
+ saa_clearb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN);
+
+ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+ if (ir->polling) {
+ if (ir->last_gpio == gpio)
+ return 0;
+ ir->last_gpio = gpio;
+ }
+
+ data = ir_extract_bits(gpio, ir->mask_keycode);
+ dprintk("build_key gpio=0x%x mask=0x%x data=%d\n",
+ gpio, ir->mask_keycode, data);
+
+ if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) ||
+ (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) {
+ ir_input_keydown(&ir->dev,&ir->ir,data,data);
+ } else {
+ ir_input_nokey(&ir->dev,&ir->ir);
+ }
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void saa7134_input_irq(struct saa7134_dev *dev)
+{
+ struct saa7134_ir *ir = dev->remote;
+
+ if (!ir->polling)
+ build_key(dev);
+}
+
+static void saa7134_input_timer(unsigned long data)
+{
+ struct saa7134_dev *dev = (struct saa7134_dev*)data;
+ struct saa7134_ir *ir = dev->remote;
+ unsigned long timeout;
+
+ build_key(dev);
+ timeout = jiffies + (ir->polling * HZ / 1000);
+ mod_timer(&ir->timer, timeout);
+}
+
+int saa7134_input_init1(struct saa7134_dev *dev)
+{
+ struct saa7134_ir *ir;
+ IR_KEYTAB_TYPE *ir_codes = NULL;
+ u32 mask_keycode = 0;
+ u32 mask_keydown = 0;
+ u32 mask_keyup = 0;
+ int polling = 0;
+ int ir_type = IR_TYPE_OTHER;
+
+ if (!dev->has_remote)
+ return -ENODEV;
+ if (disable_ir)
+ return -ENODEV;
+
+ /* detect & configure */
+ switch (dev->board) {
+ case SAA7134_BOARD_FLYVIDEO2000:
+ case SAA7134_BOARD_FLYVIDEO3000:
+ ir_codes = flyvideo_codes;
+ mask_keycode = 0xEC00000;
+ mask_keydown = 0x0040000;
+ break;
+ case SAA7134_BOARD_CINERGY400:
+ case SAA7134_BOARD_CINERGY600:
+ case SAA7134_BOARD_CINERGY600_MK3:
+ ir_codes = cinergy_codes;
+ mask_keycode = 0x00003f;
+ mask_keyup = 0x040000;
+ break;
+ case SAA7134_BOARD_ECS_TVP3XP:
+ case SAA7134_BOARD_ECS_TVP3XP_4CB5:
+ ir_codes = eztv_codes;
+ mask_keycode = 0x00017c;
+ mask_keyup = 0x000002;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_AVACSSMARTTV:
+ ir_codes = avacssmart_codes;
+ mask_keycode = 0x00001F;
+ mask_keyup = 0x000020;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_MD2819:
+ case SAA7134_BOARD_AVERMEDIA_305:
+ case SAA7134_BOARD_AVERMEDIA_307:
+ ir_codes = md2819_codes;
+ mask_keycode = 0x0007C8;
+ mask_keydown = 0x000010;
+ polling = 50; // ms
+ /* Set GPIO pin2 to high to enable the IR controller */
+ saa_setb(SAA7134_GPIO_GPMODE0, 0x4);
+ saa_setb(SAA7134_GPIO_GPSTATUS0, 0x4);
+ break;
+ case SAA7134_BOARD_VIDEOMATE_TV_PVR:
+ ir_codes = videomate_tv_pvr_codes;
+ mask_keycode = 0x00003F;
+ mask_keyup = 0x400000;
+ polling = 50; // ms
+ break;
+ }
+ if (NULL == ir_codes) {
+ printk("%s: Oops: IR config error [card=%d]\n",
+ dev->name, dev->board);
+ return -ENODEV;
+ }
+
+ ir = kmalloc(sizeof(*ir),GFP_KERNEL);
+ if (NULL == ir)
+ return -ENOMEM;
+ memset(ir,0,sizeof(*ir));
+
+ /* init hardware-specific stuff */
+ ir->mask_keycode = mask_keycode;
+ ir->mask_keydown = mask_keydown;
+ ir->mask_keyup = mask_keyup;
+ ir->polling = polling;
+
+ /* init input device */
+ snprintf(ir->name, sizeof(ir->name), "saa7134 IR (%s)",
+ saa7134_boards[dev->board].name);
+ snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
+ pci_name(dev->pci));
+
+ ir_input_init(&ir->dev, &ir->ir, ir_type, ir_codes);
+ ir->dev.name = ir->name;
+ ir->dev.phys = ir->phys;
+ ir->dev.id.bustype = BUS_PCI;
+ ir->dev.id.version = 1;
+ if (dev->pci->subsystem_vendor) {
+ ir->dev.id.vendor = dev->pci->subsystem_vendor;
+ ir->dev.id.product = dev->pci->subsystem_device;
+ } else {
+ ir->dev.id.vendor = dev->pci->vendor;
+ ir->dev.id.product = dev->pci->device;
+ }
+
+ /* all done */
+ dev->remote = ir;
+ if (ir->polling) {
+ init_timer(&ir->timer);
+ ir->timer.function = saa7134_input_timer;
+ ir->timer.data = (unsigned long)dev;
+ ir->timer.expires = jiffies + HZ;
+ add_timer(&ir->timer);
+ }
+
+ input_register_device(&dev->remote->dev);
+ printk("%s: registered input device for IR\n",dev->name);
+ return 0;
+}
+
+void saa7134_input_fini(struct saa7134_dev *dev)
+{
+ if (NULL == dev->remote)
+ return;
+
+ input_unregister_device(&dev->remote->dev);
+ if (dev->remote->polling)
+ del_timer_sync(&dev->remote->timer);
+ kfree(dev->remote);
+ dev->remote = NULL;
+}
+
+/* ----------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-oss.c b/drivers/media/video/saa7134/saa7134-oss.c
new file mode 100644
index 00000000000..6b6a643bf1c
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-oss.c
@@ -0,0 +1,857 @@
+/*
+ * $Id: saa7134-oss.c,v 1.13 2004/12/10 12:33:39 kraxel Exp $
+ *
+ * device driver for philips saa7134 based TV cards
+ * oss dsp interface
+ *
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int oss_debug = 0;
+module_param(oss_debug, int, 0644);
+MODULE_PARM_DESC(oss_debug,"enable debug messages [oss]");
+
+static unsigned int oss_rate = 0;
+module_param(oss_rate, int, 0444);
+MODULE_PARM_DESC(oss_rate,"sample rate (valid are: 32000,48000)");
+
+#define dprintk(fmt, arg...) if (oss_debug) \
+ printk(KERN_DEBUG "%s/oss: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static int dsp_buffer_conf(struct saa7134_dev *dev, int blksize, int blocks)
+{
+ blksize &= ~0xff;
+ if (blksize < 0x100)
+ blksize = 0x100;
+ if (blksize > 0x10000)
+ blksize = 0x10000;
+
+ if (blocks < 2)
+ blocks = 2;
+ while ((blksize * blocks) & ~PAGE_MASK)
+ blocks++;
+ if ((blksize * blocks) > 1024*1024)
+ blocks = 1024*1024 / blksize;
+
+ dev->oss.blocks = blocks;
+ dev->oss.blksize = blksize;
+ dev->oss.bufsize = blksize * blocks;
+
+ dprintk("buffer config: %d blocks / %d bytes, %d kB total\n",
+ blocks,blksize,blksize * blocks / 1024);
+ return 0;
+}
+
+static int dsp_buffer_init(struct saa7134_dev *dev)
+{
+ int err;
+
+ if (!dev->oss.bufsize)
+ BUG();
+ videobuf_dma_init(&dev->oss.dma);
+ err = videobuf_dma_init_kernel(&dev->oss.dma, PCI_DMA_FROMDEVICE,
+ dev->oss.bufsize >> PAGE_SHIFT);
+ if (0 != err)
+ return err;
+ return 0;
+}
+
+static int dsp_buffer_free(struct saa7134_dev *dev)
+{
+ if (!dev->oss.blksize)
+ BUG();
+ videobuf_dma_free(&dev->oss.dma);
+ dev->oss.blocks = 0;
+ dev->oss.blksize = 0;
+ dev->oss.bufsize = 0;
+ return 0;
+}
+
+static void dsp_dma_start(struct saa7134_dev *dev)
+{
+ dev->oss.dma_blk = 0;
+ dev->oss.dma_running = 1;
+ saa7134_set_dmabits(dev);
+}
+
+static void dsp_dma_stop(struct saa7134_dev *dev)
+{
+ dev->oss.dma_blk = -1;
+ dev->oss.dma_running = 0;
+ saa7134_set_dmabits(dev);
+}
+
+static int dsp_rec_start(struct saa7134_dev *dev)
+{
+ int err, bswap, sign;
+ u32 fmt, control;
+ unsigned long flags;
+
+ /* prepare buffer */
+ if (0 != (err = videobuf_dma_pci_map(dev->pci,&dev->oss.dma)))
+ return err;
+ if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->oss.pt)))
+ goto fail1;
+ if (0 != (err = saa7134_pgtable_build(dev->pci,&dev->oss.pt,
+ dev->oss.dma.sglist,
+ dev->oss.dma.sglen,
+ 0)))
+ goto fail2;
+
+ /* sample format */
+ switch (dev->oss.afmt) {
+ case AFMT_U8:
+ case AFMT_S8: fmt = 0x00; break;
+ case AFMT_U16_LE:
+ case AFMT_U16_BE:
+ case AFMT_S16_LE:
+ case AFMT_S16_BE: fmt = 0x01; break;
+ default:
+ err = -EINVAL;
+ goto fail2;
+ }
+
+ switch (dev->oss.afmt) {
+ case AFMT_S8:
+ case AFMT_S16_LE:
+ case AFMT_S16_BE: sign = 1; break;
+ default: sign = 0; break;
+ }
+
+ switch (dev->oss.afmt) {
+ case AFMT_U16_BE:
+ case AFMT_S16_BE: bswap = 1; break;
+ default: bswap = 0; break;
+ }
+
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ if (1 == dev->oss.channels)
+ fmt |= (1 << 3);
+ if (2 == dev->oss.channels)
+ fmt |= (3 << 3);
+ if (sign)
+ fmt |= 0x04;
+ fmt |= (TV == dev->oss.input) ? 0xc0 : 0x80;
+
+ saa_writeb(SAA7134_NUM_SAMPLES0, (dev->oss.blksize & 0x0000ff));
+ saa_writeb(SAA7134_NUM_SAMPLES1, (dev->oss.blksize & 0x00ff00) >> 8);
+ saa_writeb(SAA7134_NUM_SAMPLES2, (dev->oss.blksize & 0xff0000) >> 16);
+ saa_writeb(SAA7134_AUDIO_FORMAT_CTRL, fmt);
+ break;
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ if (1 == dev->oss.channels)
+ fmt |= (1 << 4);
+ if (2 == dev->oss.channels)
+ fmt |= (2 << 4);
+ if (!sign)
+ fmt |= 0x04;
+ saa_writel(0x588 >> 2, dev->oss.blksize -4);
+ saa_writel(0x58c >> 2, 0x543210 | (fmt << 24));
+ break;
+ }
+ dprintk("rec_start: afmt=%d ch=%d => fmt=0x%x swap=%c\n",
+ dev->oss.afmt, dev->oss.channels, fmt,
+ bswap ? 'b' : '-');
+
+ /* dma: setup channel 6 (= AUDIO) */
+ control = SAA7134_RS_CONTROL_BURST_16 |
+ SAA7134_RS_CONTROL_ME |
+ (dev->oss.pt.dma >> 12);
+ if (bswap)
+ control |= SAA7134_RS_CONTROL_BSWAP;
+ saa_writel(SAA7134_RS_BA1(6),0);
+ saa_writel(SAA7134_RS_BA2(6),dev->oss.blksize);
+ saa_writel(SAA7134_RS_PITCH(6),0);
+ saa_writel(SAA7134_RS_CONTROL(6),control);
+
+ /* start dma */
+ dev->oss.recording_on = 1;
+ spin_lock_irqsave(&dev->slock,flags);
+ dsp_dma_start(dev);
+ spin_unlock_irqrestore(&dev->slock,flags);
+ return 0;
+
+ fail2:
+ saa7134_pgtable_free(dev->pci,&dev->oss.pt);
+ fail1:
+ videobuf_dma_pci_unmap(dev->pci,&dev->oss.dma);
+ return err;
+}
+
+static int dsp_rec_stop(struct saa7134_dev *dev)
+{
+ unsigned long flags;
+
+ dprintk("rec_stop dma_blk=%d\n",dev->oss.dma_blk);
+
+ /* stop dma */
+ dev->oss.recording_on = 0;
+ spin_lock_irqsave(&dev->slock,flags);
+ dsp_dma_stop(dev);
+ spin_unlock_irqrestore(&dev->slock,flags);
+
+ /* unlock buffer */
+ saa7134_pgtable_free(dev->pci,&dev->oss.pt);
+ videobuf_dma_pci_unmap(dev->pci,&dev->oss.dma);
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int dsp_open(struct inode *inode, struct file *file)
+{
+ int minor = iminor(inode);
+ struct saa7134_dev *h,*dev = NULL;
+ struct list_head *list;
+ int err;
+
+ list_for_each(list,&saa7134_devlist) {
+ h = list_entry(list, struct saa7134_dev, devlist);
+ if (h->oss.minor_dsp == minor)
+ dev = h;
+ }
+ if (NULL == dev)
+ return -ENODEV;
+
+ down(&dev->oss.lock);
+ err = -EBUSY;
+ if (dev->oss.users_dsp)
+ goto fail1;
+ dev->oss.users_dsp++;
+ file->private_data = dev;
+
+ dev->oss.afmt = AFMT_U8;
+ dev->oss.channels = 1;
+ dev->oss.read_count = 0;
+ dev->oss.read_offset = 0;
+ dsp_buffer_conf(dev,PAGE_SIZE,64);
+ err = dsp_buffer_init(dev);
+ if (0 != err)
+ goto fail2;
+
+ up(&dev->oss.lock);
+ return 0;
+
+ fail2:
+ dev->oss.users_dsp--;
+ fail1:
+ up(&dev->oss.lock);
+ return err;
+}
+
+static int dsp_release(struct inode *inode, struct file *file)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ down(&dev->oss.lock);
+ if (dev->oss.recording_on)
+ dsp_rec_stop(dev);
+ dsp_buffer_free(dev);
+ dev->oss.users_dsp--;
+ file->private_data = NULL;
+ up(&dev->oss.lock);
+ return 0;
+}
+
+static ssize_t dsp_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct saa7134_dev *dev = file->private_data;
+ DECLARE_WAITQUEUE(wait, current);
+ unsigned int bytes;
+ unsigned long flags;
+ int err,ret = 0;
+
+ add_wait_queue(&dev->oss.wq, &wait);
+ down(&dev->oss.lock);
+ while (count > 0) {
+ /* wait for data if needed */
+ if (0 == dev->oss.read_count) {
+ if (!dev->oss.recording_on) {
+ err = dsp_rec_start(dev);
+ if (err < 0) {
+ if (0 == ret)
+ ret = err;
+ break;
+ }
+ }
+ if (dev->oss.recording_on &&
+ !dev->oss.dma_running) {
+ /* recover from overruns */
+ spin_lock_irqsave(&dev->slock,flags);
+ dsp_dma_start(dev);
+ spin_unlock_irqrestore(&dev->slock,flags);
+ }
+ if (file->f_flags & O_NONBLOCK) {
+ if (0 == ret)
+ ret = -EAGAIN;
+ break;
+ }
+ up(&dev->oss.lock);
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (0 == dev->oss.read_count)
+ schedule();
+ set_current_state(TASK_RUNNING);
+ down(&dev->oss.lock);
+ if (signal_pending(current)) {
+ if (0 == ret)
+ ret = -EINTR;
+ break;
+ }
+ }
+
+ /* copy data to userspace */
+ bytes = count;
+ if (bytes > dev->oss.read_count)
+ bytes = dev->oss.read_count;
+ if (bytes > dev->oss.bufsize - dev->oss.read_offset)
+ bytes = dev->oss.bufsize - dev->oss.read_offset;
+ if (copy_to_user(buffer + ret,
+ dev->oss.dma.vmalloc + dev->oss.read_offset,
+ bytes)) {
+ if (0 == ret)
+ ret = -EFAULT;
+ break;
+ }
+
+ ret += bytes;
+ count -= bytes;
+ dev->oss.read_count -= bytes;
+ dev->oss.read_offset += bytes;
+ if (dev->oss.read_offset == dev->oss.bufsize)
+ dev->oss.read_offset = 0;
+ }
+ up(&dev->oss.lock);
+ remove_wait_queue(&dev->oss.wq, &wait);
+ return ret;
+}
+
+static ssize_t dsp_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ return -EINVAL;
+}
+
+static int dsp_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct saa7134_dev *dev = file->private_data;
+ void __user *argp = (void __user *) arg;
+ int __user *p = argp;
+ int val = 0;
+
+ if (oss_debug > 1)
+ saa7134_print_ioctl(dev->name,cmd);
+ switch (cmd) {
+ case OSS_GETVERSION:
+ return put_user(SOUND_VERSION, p);
+ case SNDCTL_DSP_GETCAPS:
+ return 0;
+
+ case SNDCTL_DSP_SPEED:
+ if (get_user(val, p))
+ return -EFAULT;
+ /* fall through */
+ case SOUND_PCM_READ_RATE:
+ return put_user(dev->oss.rate, p);
+
+ case SNDCTL_DSP_STEREO:
+ if (get_user(val, p))
+ return -EFAULT;
+ down(&dev->oss.lock);
+ dev->oss.channels = val ? 2 : 1;
+ if (dev->oss.recording_on) {
+ dsp_rec_stop(dev);
+ dsp_rec_start(dev);
+ }
+ up(&dev->oss.lock);
+ return put_user(dev->oss.channels-1, p);
+
+ case SNDCTL_DSP_CHANNELS:
+ if (get_user(val, p))
+ return -EFAULT;
+ if (val != 1 && val != 2)
+ return -EINVAL;
+ down(&dev->oss.lock);
+ dev->oss.channels = val;
+ if (dev->oss.recording_on) {
+ dsp_rec_stop(dev);
+ dsp_rec_start(dev);
+ }
+ up(&dev->oss.lock);
+ /* fall through */
+ case SOUND_PCM_READ_CHANNELS:
+ return put_user(dev->oss.channels, p);
+
+ case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+ return put_user(AFMT_U8 | AFMT_S8 |
+ AFMT_U16_LE | AFMT_U16_BE |
+ AFMT_S16_LE | AFMT_S16_BE, p);
+
+ case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */
+ if (get_user(val, p))
+ return -EFAULT;
+ switch (val) {
+ case AFMT_QUERY:
+ /* nothing to do */
+ break;
+ case AFMT_U8:
+ case AFMT_S8:
+ case AFMT_U16_LE:
+ case AFMT_U16_BE:
+ case AFMT_S16_LE:
+ case AFMT_S16_BE:
+ down(&dev->oss.lock);
+ dev->oss.afmt = val;
+ if (dev->oss.recording_on) {
+ dsp_rec_stop(dev);
+ dsp_rec_start(dev);
+ }
+ up(&dev->oss.lock);
+ return put_user(dev->oss.afmt, p);
+ default:
+ return -EINVAL;
+ }
+
+ case SOUND_PCM_READ_BITS:
+ switch (dev->oss.afmt) {
+ case AFMT_U8:
+ case AFMT_S8:
+ return put_user(8, p);
+ case AFMT_U16_LE:
+ case AFMT_U16_BE:
+ case AFMT_S16_LE:
+ case AFMT_S16_BE:
+ return put_user(16, p);
+ default:
+ return -EINVAL;
+ }
+
+ case SNDCTL_DSP_NONBLOCK:
+ file->f_flags |= O_NONBLOCK;
+ return 0;
+
+ case SNDCTL_DSP_RESET:
+ down(&dev->oss.lock);
+ if (dev->oss.recording_on)
+ dsp_rec_stop(dev);
+ up(&dev->oss.lock);
+ return 0;
+ case SNDCTL_DSP_GETBLKSIZE:
+ return put_user(dev->oss.blksize, p);
+
+ case SNDCTL_DSP_SETFRAGMENT:
+ if (get_user(val, p))
+ return -EFAULT;
+ if (dev->oss.recording_on)
+ return -EBUSY;
+ dsp_buffer_free(dev);
+ /* used to be arg >> 16 instead of val >> 16; fixed */
+ dsp_buffer_conf(dev,1 << (val & 0xffff), (val >> 16) & 0xffff);
+ dsp_buffer_init(dev);
+ return 0;
+
+ case SNDCTL_DSP_SYNC:
+ /* NOP */
+ return 0;
+
+ case SNDCTL_DSP_GETISPACE:
+ {
+ audio_buf_info info;
+ info.fragsize = dev->oss.blksize;
+ info.fragstotal = dev->oss.blocks;
+ info.bytes = dev->oss.read_count;
+ info.fragments = info.bytes / info.fragsize;
+ if (copy_to_user(argp, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static unsigned int dsp_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct saa7134_dev *dev = file->private_data;
+ unsigned int mask = 0;
+
+ poll_wait(file, &dev->oss.wq, wait);
+
+ if (0 == dev->oss.read_count) {
+ down(&dev->oss.lock);
+ if (!dev->oss.recording_on)
+ dsp_rec_start(dev);
+ up(&dev->oss.lock);
+ } else
+ mask |= (POLLIN | POLLRDNORM);
+ return mask;
+}
+
+struct file_operations saa7134_dsp_fops = {
+ .owner = THIS_MODULE,
+ .open = dsp_open,
+ .release = dsp_release,
+ .read = dsp_read,
+ .write = dsp_write,
+ .ioctl = dsp_ioctl,
+ .poll = dsp_poll,
+ .llseek = no_llseek,
+};
+
+/* ------------------------------------------------------------------ */
+
+static int
+mixer_recsrc_7134(struct saa7134_dev *dev)
+{
+ int analog_io,rate;
+
+ switch (dev->oss.input) {
+ case TV:
+ saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, 0xc0);
+ saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, 0x00);
+ break;
+ case LINE1:
+ case LINE2:
+ case LINE2_LEFT:
+ analog_io = (LINE1 == dev->oss.input) ? 0x00 : 0x08;
+ rate = (32000 == dev->oss.rate) ? 0x01 : 0x03;
+ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x08, analog_io);
+ saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, 0x80);
+ saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, rate);
+ break;
+ }
+ return 0;
+}
+
+static int
+mixer_recsrc_7133(struct saa7134_dev *dev)
+{
+ u32 value = 0xbbbbbb;
+
+ switch (dev->oss.input) {
+ case TV:
+ value = 0xbbbb10; /* MAIN */
+ break;
+ case LINE1:
+ value = 0xbbbb32; /* AUX1 */
+ break;
+ case LINE2:
+ case LINE2_LEFT:
+ value = 0xbbbb54; /* AUX2 */
+ break;
+ }
+ saa_dsp_writel(dev, 0x46c >> 2, value);
+ return 0;
+}
+
+static int
+mixer_recsrc(struct saa7134_dev *dev, enum saa7134_audio_in src)
+{
+ static const char *iname[] = { "Oops", "TV", "LINE1", "LINE2" };
+
+ dev->oss.count++;
+ dev->oss.input = src;
+ dprintk("mixer input = %s\n",iname[dev->oss.input]);
+
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ mixer_recsrc_7134(dev);
+ break;
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ mixer_recsrc_7133(dev);
+ break;
+ }
+ return 0;
+}
+
+static int
+mixer_level(struct saa7134_dev *dev, enum saa7134_audio_in src, int level)
+{
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ switch (src) {
+ case TV:
+ /* nothing */
+ break;
+ case LINE1:
+ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x10,
+ (100 == level) ? 0x00 : 0x10);
+ break;
+ case LINE2:
+ case LINE2_LEFT:
+ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x20,
+ (100 == level) ? 0x00 : 0x20);
+ break;
+ }
+ break;
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ /* nothing */
+ break;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int mixer_open(struct inode *inode, struct file *file)
+{
+ int minor = iminor(inode);
+ struct saa7134_dev *h,*dev = NULL;
+ struct list_head *list;
+
+ list_for_each(list,&saa7134_devlist) {
+ h = list_entry(list, struct saa7134_dev, devlist);
+ if (h->oss.minor_mixer == minor)
+ dev = h;
+ }
+ if (NULL == dev)
+ return -ENODEV;
+
+ file->private_data = dev;
+ return 0;
+}
+
+static int mixer_release(struct inode *inode, struct file *file)
+{
+ file->private_data = NULL;
+ return 0;
+}
+
+static int mixer_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct saa7134_dev *dev = file->private_data;
+ enum saa7134_audio_in input;
+ int val,ret;
+ void __user *argp = (void __user *) arg;
+ int __user *p = argp;
+
+ if (oss_debug > 1)
+ saa7134_print_ioctl(dev->name,cmd);
+ switch (cmd) {
+ case OSS_GETVERSION:
+ return put_user(SOUND_VERSION, p);
+ case SOUND_MIXER_INFO:
+ {
+ mixer_info info;
+ memset(&info,0,sizeof(info));
+ strlcpy(info.id, "TV audio", sizeof(info.id));
+ strlcpy(info.name, dev->name, sizeof(info.name));
+ info.modify_counter = dev->oss.count;
+ if (copy_to_user(argp, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+ }
+ case SOUND_OLD_MIXER_INFO:
+ {
+ _old_mixer_info info;
+ memset(&info,0,sizeof(info));
+ strlcpy(info.id, "TV audio", sizeof(info.id));
+ strlcpy(info.name, dev->name, sizeof(info.name));
+ if (copy_to_user(argp, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+ }
+ case MIXER_READ(SOUND_MIXER_CAPS):
+ return put_user(SOUND_CAP_EXCL_INPUT, p);
+ case MIXER_READ(SOUND_MIXER_STEREODEVS):
+ return put_user(0, p);
+ case MIXER_READ(SOUND_MIXER_RECMASK):
+ case MIXER_READ(SOUND_MIXER_DEVMASK):
+ val = SOUND_MASK_LINE1 | SOUND_MASK_LINE2;
+ if (32000 == dev->oss.rate)
+ val |= SOUND_MASK_VIDEO;
+ return put_user(val, p);
+
+ case MIXER_WRITE(SOUND_MIXER_RECSRC):
+ if (get_user(val, p))
+ return -EFAULT;
+ input = dev->oss.input;
+ if (32000 == dev->oss.rate &&
+ val & SOUND_MASK_VIDEO && dev->oss.input != TV)
+ input = TV;
+ if (val & SOUND_MASK_LINE1 && dev->oss.input != LINE1)
+ input = LINE1;
+ if (val & SOUND_MASK_LINE2 && dev->oss.input != LINE2)
+ input = LINE2;
+ if (input != dev->oss.input)
+ mixer_recsrc(dev,input);
+ /* fall throuth */
+ case MIXER_READ(SOUND_MIXER_RECSRC):
+ switch (dev->oss.input) {
+ case TV: ret = SOUND_MASK_VIDEO; break;
+ case LINE1: ret = SOUND_MASK_LINE1; break;
+ case LINE2: ret = SOUND_MASK_LINE2; break;
+ default: ret = 0;
+ }
+ return put_user(ret, p);
+
+ case MIXER_WRITE(SOUND_MIXER_VIDEO):
+ case MIXER_READ(SOUND_MIXER_VIDEO):
+ if (32000 != dev->oss.rate)
+ return -EINVAL;
+ return put_user(100 | 100 << 8, p);
+
+ case MIXER_WRITE(SOUND_MIXER_LINE1):
+ if (get_user(val, p))
+ return -EFAULT;
+ val &= 0xff;
+ val = (val <= 50) ? 50 : 100;
+ dev->oss.line1 = val;
+ mixer_level(dev,LINE1,dev->oss.line1);
+ /* fall throuth */
+ case MIXER_READ(SOUND_MIXER_LINE1):
+ return put_user(dev->oss.line1 | dev->oss.line1 << 8, p);
+
+ case MIXER_WRITE(SOUND_MIXER_LINE2):
+ if (get_user(val, p))
+ return -EFAULT;
+ val &= 0xff;
+ val = (val <= 50) ? 50 : 100;
+ dev->oss.line2 = val;
+ mixer_level(dev,LINE2,dev->oss.line2);
+ /* fall throuth */
+ case MIXER_READ(SOUND_MIXER_LINE2):
+ return put_user(dev->oss.line2 | dev->oss.line2 << 8, p);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+struct file_operations saa7134_mixer_fops = {
+ .owner = THIS_MODULE,
+ .open = mixer_open,
+ .release = mixer_release,
+ .ioctl = mixer_ioctl,
+ .llseek = no_llseek,
+};
+
+/* ------------------------------------------------------------------ */
+
+int saa7134_oss_init1(struct saa7134_dev *dev)
+{
+ /* general */
+ init_MUTEX(&dev->oss.lock);
+ init_waitqueue_head(&dev->oss.wq);
+
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ saa_writel(0x588 >> 2, 0x00000fff);
+ saa_writel(0x58c >> 2, 0x00543210);
+ saa_dsp_writel(dev, 0x46c >> 2, 0xbbbbbb);
+ break;
+ }
+
+ /* dsp */
+ dev->oss.rate = 32000;
+ if (oss_rate)
+ dev->oss.rate = oss_rate;
+ dev->oss.rate = (dev->oss.rate > 40000) ? 48000 : 32000;
+
+ /* mixer */
+ dev->oss.line1 = 50;
+ dev->oss.line2 = 50;
+ mixer_level(dev,LINE1,dev->oss.line1);
+ mixer_level(dev,LINE2,dev->oss.line2);
+ mixer_recsrc(dev, (dev->oss.rate == 32000) ? TV : LINE2);
+
+ return 0;
+}
+
+int saa7134_oss_fini(struct saa7134_dev *dev)
+{
+ /* nothing */
+ return 0;
+}
+
+void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status)
+{
+ int next_blk, reg = 0;
+
+ spin_lock(&dev->slock);
+ if (UNSET == dev->oss.dma_blk) {
+ dprintk("irq: recording stopped\n");
+ goto done;
+ }
+ if (0 != (status & 0x0f000000))
+ dprintk("irq: lost %ld\n", (status >> 24) & 0x0f);
+ if (0 == (status & 0x10000000)) {
+ /* odd */
+ if (0 == (dev->oss.dma_blk & 0x01))
+ reg = SAA7134_RS_BA1(6);
+ } else {
+ /* even */
+ if (0 == (dev->oss.dma_blk & 0x00))
+ reg = SAA7134_RS_BA2(6);
+ }
+ if (0 == reg) {
+ dprintk("irq: field oops [%s]\n",
+ (status & 0x10000000) ? "even" : "odd");
+ goto done;
+ }
+ if (dev->oss.read_count >= dev->oss.blksize * (dev->oss.blocks-2)) {
+ dprintk("irq: overrun [full=%d/%d]\n",dev->oss.read_count,
+ dev->oss.bufsize);
+ dsp_dma_stop(dev);
+ goto done;
+ }
+
+ /* next block addr */
+ next_blk = (dev->oss.dma_blk + 2) % dev->oss.blocks;
+ saa_writel(reg,next_blk * dev->oss.blksize);
+ if (oss_debug > 2)
+ dprintk("irq: ok, %s, next_blk=%d, addr=%x\n",
+ (status & 0x10000000) ? "even" : "odd ", next_blk,
+ next_blk * dev->oss.blksize);
+
+ /* update status & wake waiting readers */
+ dev->oss.dma_blk = (dev->oss.dma_blk + 1) % dev->oss.blocks;
+ dev->oss.read_count += dev->oss.blksize;
+ wake_up(&dev->oss.wq);
+
+ done:
+ spin_unlock(&dev->slock);
+}
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-reg.h b/drivers/media/video/saa7134/saa7134-reg.h
new file mode 100644
index 00000000000..87734f22af7
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-reg.h
@@ -0,0 +1,366 @@
+/*
+ * $Id: saa7134-reg.h,v 1.2 2004/09/15 16:15:24 kraxel Exp $
+ *
+ * philips saa7134 registers
+ */
+
+/* ------------------------------------------------------------------ */
+/*
+ * PCI ID's
+ */
+#ifndef PCI_DEVICE_ID_PHILIPS_SAA7130
+# define PCI_DEVICE_ID_PHILIPS_SAA7130 0x7130
+#endif
+#ifndef PCI_DEVICE_ID_PHILIPS_SAA7133
+# define PCI_DEVICE_ID_PHILIPS_SAA7133 0x7133
+#endif
+#ifndef PCI_DEVICE_ID_PHILIPS_SAA7134
+# define PCI_DEVICE_ID_PHILIPS_SAA7134 0x7134
+#endif
+#ifndef PCI_DEVICE_ID_PHILIPS_SAA7135
+# define PCI_DEVICE_ID_PHILIPS_SAA7135 0x7135
+#endif
+
+/* ------------------------------------------------------------------ */
+/*
+ * registers -- 32 bit
+ */
+
+/* DMA channels, n = 0 ... 6 */
+#define SAA7134_RS_BA1(n) ((0x200 >> 2) + 4*n)
+#define SAA7134_RS_BA2(n) ((0x204 >> 2) + 4*n)
+#define SAA7134_RS_PITCH(n) ((0x208 >> 2) + 4*n)
+#define SAA7134_RS_CONTROL(n) ((0x20c >> 2) + 4*n)
+#define SAA7134_RS_CONTROL_WSWAP (0x01 << 25)
+#define SAA7134_RS_CONTROL_BSWAP (0x01 << 24)
+#define SAA7134_RS_CONTROL_BURST_2 (0x01 << 21)
+#define SAA7134_RS_CONTROL_BURST_4 (0x02 << 21)
+#define SAA7134_RS_CONTROL_BURST_8 (0x03 << 21)
+#define SAA7134_RS_CONTROL_BURST_16 (0x04 << 21)
+#define SAA7134_RS_CONTROL_BURST_32 (0x05 << 21)
+#define SAA7134_RS_CONTROL_BURST_64 (0x06 << 21)
+#define SAA7134_RS_CONTROL_BURST_MAX (0x07 << 21)
+#define SAA7134_RS_CONTROL_ME (0x01 << 20)
+#define SAA7134_FIFO_SIZE (0x2a0 >> 2)
+#define SAA7134_THRESHOULD (0x2a4 >> 2)
+
+/* main control */
+#define SAA7134_MAIN_CTRL (0x2a8 >> 2)
+#define SAA7134_MAIN_CTRL_VPLLE (1 << 15)
+#define SAA7134_MAIN_CTRL_APLLE (1 << 14)
+#define SAA7134_MAIN_CTRL_EXOSC (1 << 13)
+#define SAA7134_MAIN_CTRL_EVFE1 (1 << 12)
+#define SAA7134_MAIN_CTRL_EVFE2 (1 << 11)
+#define SAA7134_MAIN_CTRL_ESFE (1 << 10)
+#define SAA7134_MAIN_CTRL_EBADC (1 << 9)
+#define SAA7134_MAIN_CTRL_EBDAC (1 << 8)
+#define SAA7134_MAIN_CTRL_TE6 (1 << 6)
+#define SAA7134_MAIN_CTRL_TE5 (1 << 5)
+#define SAA7134_MAIN_CTRL_TE4 (1 << 4)
+#define SAA7134_MAIN_CTRL_TE3 (1 << 3)
+#define SAA7134_MAIN_CTRL_TE2 (1 << 2)
+#define SAA7134_MAIN_CTRL_TE1 (1 << 1)
+#define SAA7134_MAIN_CTRL_TE0 (1 << 0)
+
+/* DMA status */
+#define SAA7134_DMA_STATUS (0x2ac >> 2)
+
+/* audio / video status */
+#define SAA7134_AV_STATUS (0x2c0 >> 2)
+#define SAA7134_AV_STATUS_STEREO (1 << 17)
+#define SAA7134_AV_STATUS_DUAL (1 << 16)
+#define SAA7134_AV_STATUS_PILOT (1 << 15)
+#define SAA7134_AV_STATUS_SMB (1 << 14)
+#define SAA7134_AV_STATUS_DMB (1 << 13)
+#define SAA7134_AV_STATUS_VDSP (1 << 12)
+#define SAA7134_AV_STATUS_IIC_STATUS (3 << 10)
+#define SAA7134_AV_STATUS_MVM (7 << 7)
+#define SAA7134_AV_STATUS_FIDT (1 << 6)
+#define SAA7134_AV_STATUS_INTL (1 << 5)
+#define SAA7134_AV_STATUS_RDCAP (1 << 4)
+#define SAA7134_AV_STATUS_PWR_ON (1 << 3)
+#define SAA7134_AV_STATUS_LOAD_ERR (1 << 2)
+#define SAA7134_AV_STATUS_TRIG_ERR (1 << 1)
+#define SAA7134_AV_STATUS_CONF_ERR (1 << 0)
+
+/* interrupt */
+#define SAA7134_IRQ1 (0x2c4 >> 2)
+#define SAA7134_IRQ1_INTE_RA3_1 (1 << 25)
+#define SAA7134_IRQ1_INTE_RA3_0 (1 << 24)
+#define SAA7134_IRQ1_INTE_RA2_3 (1 << 19)
+#define SAA7134_IRQ1_INTE_RA2_2 (1 << 18)
+#define SAA7134_IRQ1_INTE_RA2_1 (1 << 17)
+#define SAA7134_IRQ1_INTE_RA2_0 (1 << 16)
+#define SAA7134_IRQ1_INTE_RA1_3 (1 << 11)
+#define SAA7134_IRQ1_INTE_RA1_2 (1 << 10)
+#define SAA7134_IRQ1_INTE_RA1_1 (1 << 9)
+#define SAA7134_IRQ1_INTE_RA1_0 (1 << 8)
+#define SAA7134_IRQ1_INTE_RA0_7 (1 << 7)
+#define SAA7134_IRQ1_INTE_RA0_6 (1 << 6)
+#define SAA7134_IRQ1_INTE_RA0_5 (1 << 5)
+#define SAA7134_IRQ1_INTE_RA0_4 (1 << 4)
+#define SAA7134_IRQ1_INTE_RA0_3 (1 << 3)
+#define SAA7134_IRQ1_INTE_RA0_2 (1 << 2)
+#define SAA7134_IRQ1_INTE_RA0_1 (1 << 1)
+#define SAA7134_IRQ1_INTE_RA0_0 (1 << 0)
+
+#define SAA7134_IRQ2 (0x2c8 >> 2)
+#define SAA7134_IRQ2_INTE_GPIO23A (1 << 17)
+#define SAA7134_IRQ2_INTE_GPIO23 (1 << 16)
+#define SAA7134_IRQ2_INTE_GPIO22A (1 << 15)
+#define SAA7134_IRQ2_INTE_GPIO22 (1 << 14)
+#define SAA7134_IRQ2_INTE_GPIO18A (1 << 13)
+#define SAA7134_IRQ2_INTE_GPIO18 (1 << 12)
+#define SAA7134_IRQ2_INTE_GPIO16 (1 << 11) /* not certain */
+#define SAA7134_IRQ2_INTE_SC2 (1 << 10)
+#define SAA7134_IRQ2_INTE_SC1 (1 << 9)
+#define SAA7134_IRQ2_INTE_SC0 (1 << 8)
+#define SAA7134_IRQ2_INTE_DEC5 (1 << 7)
+#define SAA7134_IRQ2_INTE_DEC4 (1 << 6)
+#define SAA7134_IRQ2_INTE_DEC3 (1 << 5)
+#define SAA7134_IRQ2_INTE_DEC2 (1 << 4)
+#define SAA7134_IRQ2_INTE_DEC1 (1 << 3)
+#define SAA7134_IRQ2_INTE_DEC0 (1 << 2)
+#define SAA7134_IRQ2_INTE_PE (1 << 1)
+#define SAA7134_IRQ2_INTE_AR (1 << 0)
+
+#define SAA7134_IRQ_REPORT (0x2cc >> 2)
+#define SAA7134_IRQ_REPORT_GPIO23 (1 << 17)
+#define SAA7134_IRQ_REPORT_GPIO22 (1 << 16)
+#define SAA7134_IRQ_REPORT_GPIO18 (1 << 15)
+#define SAA7134_IRQ_REPORT_GPIO16 (1 << 14) /* not certain */
+#define SAA7134_IRQ_REPORT_LOAD_ERR (1 << 13)
+#define SAA7134_IRQ_REPORT_CONF_ERR (1 << 12)
+#define SAA7134_IRQ_REPORT_TRIG_ERR (1 << 11)
+#define SAA7134_IRQ_REPORT_MMC (1 << 10)
+#define SAA7134_IRQ_REPORT_FIDT (1 << 9)
+#define SAA7134_IRQ_REPORT_INTL (1 << 8)
+#define SAA7134_IRQ_REPORT_RDCAP (1 << 7)
+#define SAA7134_IRQ_REPORT_PWR_ON (1 << 6)
+#define SAA7134_IRQ_REPORT_PE (1 << 5)
+#define SAA7134_IRQ_REPORT_AR (1 << 4)
+#define SAA7134_IRQ_REPORT_DONE_RA3 (1 << 3)
+#define SAA7134_IRQ_REPORT_DONE_RA2 (1 << 2)
+#define SAA7134_IRQ_REPORT_DONE_RA1 (1 << 1)
+#define SAA7134_IRQ_REPORT_DONE_RA0 (1 << 0)
+#define SAA7134_IRQ_STATUS (0x2d0 >> 2)
+
+
+/* ------------------------------------------------------------------ */
+/*
+ * registers -- 8 bit
+ */
+
+/* video decoder */
+#define SAA7134_INCR_DELAY 0x101
+#define SAA7134_ANALOG_IN_CTRL1 0x102
+#define SAA7134_ANALOG_IN_CTRL2 0x103
+#define SAA7134_ANALOG_IN_CTRL3 0x104
+#define SAA7134_ANALOG_IN_CTRL4 0x105
+#define SAA7134_HSYNC_START 0x106
+#define SAA7134_HSYNC_STOP 0x107
+#define SAA7134_SYNC_CTRL 0x108
+#define SAA7134_LUMA_CTRL 0x109
+#define SAA7134_DEC_LUMA_BRIGHT 0x10a
+#define SAA7134_DEC_LUMA_CONTRAST 0x10b
+#define SAA7134_DEC_CHROMA_SATURATION 0x10c
+#define SAA7134_DEC_CHROMA_HUE 0x10d
+#define SAA7134_CHROMA_CTRL1 0x10e
+#define SAA7134_CHROMA_GAIN 0x10f
+#define SAA7134_CHROMA_CTRL2 0x110
+#define SAA7134_MODE_DELAY_CTRL 0x111
+
+#define SAA7134_ANALOG_ADC 0x114
+#define SAA7134_VGATE_START 0x115
+#define SAA7134_VGATE_STOP 0x116
+#define SAA7134_MISC_VGATE_MSB 0x117
+#define SAA7134_RAW_DATA_GAIN 0x118
+#define SAA7134_RAW_DATA_OFFSET 0x119
+#define SAA7134_STATUS_VIDEO1 0x11e
+#define SAA7134_STATUS_VIDEO2 0x11f
+
+/* video scaler */
+#define SAA7134_SOURCE_TIMING1 0x000
+#define SAA7134_SOURCE_TIMING2 0x001
+#define SAA7134_REGION_ENABLE 0x004
+#define SAA7134_SCALER_STATUS0 0x006
+#define SAA7134_SCALER_STATUS1 0x007
+#define SAA7134_START_GREEN 0x00c
+#define SAA7134_START_BLUE 0x00d
+#define SAA7134_START_RED 0x00e
+#define SAA7134_GREEN_PATH(x) (0x010 +x)
+#define SAA7134_BLUE_PATH(x) (0x020 +x)
+#define SAA7134_RED_PATH(x) (0x030 +x)
+
+#define TASK_A 0x040
+#define TASK_B 0x080
+#define SAA7134_TASK_CONDITIONS(t) (0x000 +t)
+#define SAA7134_FIELD_HANDLING(t) (0x001 +t)
+#define SAA7134_DATA_PATH(t) (0x002 +t)
+#define SAA7134_VBI_H_START1(t) (0x004 +t)
+#define SAA7134_VBI_H_START2(t) (0x005 +t)
+#define SAA7134_VBI_H_STOP1(t) (0x006 +t)
+#define SAA7134_VBI_H_STOP2(t) (0x007 +t)
+#define SAA7134_VBI_V_START1(t) (0x008 +t)
+#define SAA7134_VBI_V_START2(t) (0x009 +t)
+#define SAA7134_VBI_V_STOP1(t) (0x00a +t)
+#define SAA7134_VBI_V_STOP2(t) (0x00b +t)
+#define SAA7134_VBI_H_LEN1(t) (0x00c +t)
+#define SAA7134_VBI_H_LEN2(t) (0x00d +t)
+#define SAA7134_VBI_V_LEN1(t) (0x00e +t)
+#define SAA7134_VBI_V_LEN2(t) (0x00f +t)
+
+#define SAA7134_VIDEO_H_START1(t) (0x014 +t)
+#define SAA7134_VIDEO_H_START2(t) (0x015 +t)
+#define SAA7134_VIDEO_H_STOP1(t) (0x016 +t)
+#define SAA7134_VIDEO_H_STOP2(t) (0x017 +t)
+#define SAA7134_VIDEO_V_START1(t) (0x018 +t)
+#define SAA7134_VIDEO_V_START2(t) (0x019 +t)
+#define SAA7134_VIDEO_V_STOP1(t) (0x01a +t)
+#define SAA7134_VIDEO_V_STOP2(t) (0x01b +t)
+#define SAA7134_VIDEO_PIXELS1(t) (0x01c +t)
+#define SAA7134_VIDEO_PIXELS2(t) (0x01d +t)
+#define SAA7134_VIDEO_LINES1(t) (0x01e +t)
+#define SAA7134_VIDEO_LINES2(t) (0x01f +t)
+
+#define SAA7134_H_PRESCALE(t) (0x020 +t)
+#define SAA7134_ACC_LENGTH(t) (0x021 +t)
+#define SAA7134_LEVEL_CTRL(t) (0x022 +t)
+#define SAA7134_FIR_PREFILTER_CTRL(t) (0x023 +t)
+#define SAA7134_LUMA_BRIGHT(t) (0x024 +t)
+#define SAA7134_LUMA_CONTRAST(t) (0x025 +t)
+#define SAA7134_CHROMA_SATURATION(t) (0x026 +t)
+#define SAA7134_VBI_H_SCALE_INC1(t) (0x028 +t)
+#define SAA7134_VBI_H_SCALE_INC2(t) (0x029 +t)
+#define SAA7134_VBI_PHASE_OFFSET_LUMA(t) (0x02a +t)
+#define SAA7134_VBI_PHASE_OFFSET_CHROMA(t) (0x02b +t)
+#define SAA7134_H_SCALE_INC1(t) (0x02c +t)
+#define SAA7134_H_SCALE_INC2(t) (0x02d +t)
+#define SAA7134_H_PHASE_OFF_LUMA(t) (0x02e +t)
+#define SAA7134_H_PHASE_OFF_CHROMA(t) (0x02f +t)
+#define SAA7134_V_SCALE_RATIO1(t) (0x030 +t)
+#define SAA7134_V_SCALE_RATIO2(t) (0x031 +t)
+#define SAA7134_V_FILTER(t) (0x032 +t)
+#define SAA7134_V_PHASE_OFFSET0(t) (0x034 +t)
+#define SAA7134_V_PHASE_OFFSET1(t) (0x035 +t)
+#define SAA7134_V_PHASE_OFFSET2(t) (0x036 +t)
+#define SAA7134_V_PHASE_OFFSET3(t) (0x037 +t)
+
+/* clipping & dma */
+#define SAA7134_OFMT_VIDEO_A 0x300
+#define SAA7134_OFMT_DATA_A 0x301
+#define SAA7134_OFMT_VIDEO_B 0x302
+#define SAA7134_OFMT_DATA_B 0x303
+#define SAA7134_ALPHA_NOCLIP 0x304
+#define SAA7134_ALPHA_CLIP 0x305
+#define SAA7134_UV_PIXEL 0x308
+#define SAA7134_CLIP_RED 0x309
+#define SAA7134_CLIP_GREEN 0x30a
+#define SAA7134_CLIP_BLUE 0x30b
+
+/* i2c bus */
+#define SAA7134_I2C_ATTR_STATUS 0x180
+#define SAA7134_I2C_DATA 0x181
+#define SAA7134_I2C_CLOCK_SELECT 0x182
+#define SAA7134_I2C_TIMER 0x183
+
+/* audio */
+#define SAA7134_NICAM_ADD_DATA1 0x140
+#define SAA7134_NICAM_ADD_DATA2 0x141
+#define SAA7134_NICAM_STATUS 0x142
+#define SAA7134_AUDIO_STATUS 0x143
+#define SAA7134_NICAM_ERROR_COUNT 0x144
+#define SAA7134_IDENT_SIF 0x145
+#define SAA7134_LEVEL_READOUT1 0x146
+#define SAA7134_LEVEL_READOUT2 0x147
+#define SAA7134_NICAM_ERROR_LOW 0x148
+#define SAA7134_NICAM_ERROR_HIGH 0x149
+#define SAA7134_DCXO_IDENT_CTRL 0x14a
+#define SAA7134_DEMODULATOR 0x14b
+#define SAA7134_AGC_GAIN_SELECT 0x14c
+#define SAA7134_CARRIER1_FREQ0 0x150
+#define SAA7134_CARRIER1_FREQ1 0x151
+#define SAA7134_CARRIER1_FREQ2 0x152
+#define SAA7134_CARRIER2_FREQ0 0x154
+#define SAA7134_CARRIER2_FREQ1 0x155
+#define SAA7134_CARRIER2_FREQ2 0x156
+#define SAA7134_NUM_SAMPLES0 0x158
+#define SAA7134_NUM_SAMPLES1 0x159
+#define SAA7134_NUM_SAMPLES2 0x15a
+#define SAA7134_AUDIO_FORMAT_CTRL 0x15b
+#define SAA7134_MONITOR_SELECT 0x160
+#define SAA7134_FM_DEEMPHASIS 0x161
+#define SAA7134_FM_DEMATRIX 0x162
+#define SAA7134_CHANNEL1_LEVEL 0x163
+#define SAA7134_CHANNEL2_LEVEL 0x164
+#define SAA7134_NICAM_CONFIG 0x165
+#define SAA7134_NICAM_LEVEL_ADJUST 0x166
+#define SAA7134_STEREO_DAC_OUTPUT_SELECT 0x167
+#define SAA7134_I2S_OUTPUT_FORMAT 0x168
+#define SAA7134_I2S_OUTPUT_SELECT 0x169
+#define SAA7134_I2S_OUTPUT_LEVEL 0x16a
+#define SAA7134_DSP_OUTPUT_SELECT 0x16b
+#define SAA7134_AUDIO_MUTE_CTRL 0x16c
+#define SAA7134_SIF_SAMPLE_FREQ 0x16d
+#define SAA7134_ANALOG_IO_SELECT 0x16e
+#define SAA7134_AUDIO_CLOCK0 0x170
+#define SAA7134_AUDIO_CLOCK1 0x171
+#define SAA7134_AUDIO_CLOCK2 0x172
+#define SAA7134_AUDIO_PLL_CTRL 0x173
+#define SAA7134_AUDIO_CLOCKS_PER_FIELD0 0x174
+#define SAA7134_AUDIO_CLOCKS_PER_FIELD1 0x175
+#define SAA7134_AUDIO_CLOCKS_PER_FIELD2 0x176
+
+/* video port output */
+#define SAA7134_VIDEO_PORT_CTRL0 0x190
+#define SAA7134_VIDEO_PORT_CTRL1 0x191
+#define SAA7134_VIDEO_PORT_CTRL2 0x192
+#define SAA7134_VIDEO_PORT_CTRL3 0x193
+#define SAA7134_VIDEO_PORT_CTRL4 0x194
+#define SAA7134_VIDEO_PORT_CTRL5 0x195
+#define SAA7134_VIDEO_PORT_CTRL6 0x196
+#define SAA7134_VIDEO_PORT_CTRL7 0x197
+#define SAA7134_VIDEO_PORT_CTRL8 0x198
+
+/* transport stream interface */
+#define SAA7134_TS_PARALLEL 0x1a0
+#define SAA7134_TS_PARALLEL_SERIAL 0x1a1
+#define SAA7134_TS_SERIAL0 0x1a2
+#define SAA7134_TS_SERIAL1 0x1a3
+#define SAA7134_TS_DMA0 0x1a4
+#define SAA7134_TS_DMA1 0x1a5
+#define SAA7134_TS_DMA2 0x1a6
+
+/* GPIO Controls */
+#define SAA7134_GPIO_GPRESCAN 0x80
+#define SAA7134_GPIO_27_25 0x0E
+
+#define SAA7134_GPIO_GPMODE0 0x1B0
+#define SAA7134_GPIO_GPMODE1 0x1B1
+#define SAA7134_GPIO_GPMODE2 0x1B2
+#define SAA7134_GPIO_GPMODE3 0x1B3
+#define SAA7134_GPIO_GPSTATUS0 0x1B4
+#define SAA7134_GPIO_GPSTATUS1 0x1B5
+#define SAA7134_GPIO_GPSTATUS2 0x1B6
+#define SAA7134_GPIO_GPSTATUS3 0x1B7
+
+/* I2S output */
+#define SAA7134_I2S_AUDIO_OUTPUT 0x1c0
+
+/* test modes */
+#define SAA7134_SPECIAL_MODE 0x1d0
+
+/* audio -- saa7133 + saa7135 only */
+#define SAA7135_DSP_RWSTATE 0x580
+#define SAA7135_DSP_RWSTATE_ERR (1 << 3)
+#define SAA7135_DSP_RWSTATE_IDA (1 << 2)
+#define SAA7135_DSP_RWSTATE_RDB (1 << 1)
+#define SAA7135_DSP_RWSTATE_WRR (1 << 0)
+
+/* ------------------------------------------------------------------ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/drivers/media/video/saa7134/saa7134-ts.c b/drivers/media/video/saa7134/saa7134-ts.c
new file mode 100644
index 00000000000..345eb2a8c28
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-ts.c
@@ -0,0 +1,243 @@
+/*
+ * $Id: saa7134-ts.c,v 1.14 2005/02/03 10:24:33 kraxel Exp $
+ *
+ * device driver for philips saa7134 based TV cards
+ * video4linux video interface
+ *
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int ts_debug = 0;
+module_param(ts_debug, int, 0644);
+MODULE_PARM_DESC(ts_debug,"enable debug messages [ts]");
+
+#define dprintk(fmt, arg...) if (ts_debug) \
+ printk(KERN_DEBUG "%s/ts: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static int buffer_activate(struct saa7134_dev *dev,
+ struct saa7134_buf *buf,
+ struct saa7134_buf *next)
+{
+ u32 control;
+
+ dprintk("buffer_activate [%p]",buf);
+ buf->vb.state = STATE_ACTIVE;
+ buf->top_seen = 0;
+
+ /* dma: setup channel 5 (= TS) */
+ control = SAA7134_RS_CONTROL_BURST_16 |
+ SAA7134_RS_CONTROL_ME |
+ (buf->pt->dma >> 12);
+
+ if (NULL == next)
+ next = buf;
+ if (V4L2_FIELD_TOP == buf->vb.field) {
+ dprintk("- [top] buf=%p next=%p\n",buf,next);
+ saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(buf));
+ saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(next));
+ } else {
+ dprintk("- [bottom] buf=%p next=%p\n",buf,next);
+ saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(next));
+ saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(buf));
+ }
+ saa_writel(SAA7134_RS_PITCH(5),TS_PACKET_SIZE);
+ saa_writel(SAA7134_RS_CONTROL(5),control);
+
+ /* start DMA */
+ saa7134_set_dmabits(dev);
+
+ mod_timer(&dev->ts_q.timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct saa7134_dev *dev = q->priv_data;
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+ unsigned int lines, llength, size;
+ int err;
+
+ dprintk("buffer_prepare [%p,%s]\n",buf,v4l2_field_names[field]);
+
+ llength = TS_PACKET_SIZE;
+ lines = dev->ts.nr_packets;
+
+ size = lines * llength;
+ if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ return -EINVAL;
+
+ if (buf->vb.size != size) {
+ saa7134_dma_free(dev,buf);
+ }
+
+ if (STATE_NEEDS_INIT == buf->vb.state) {
+ buf->vb.width = llength;
+ buf->vb.height = lines;
+ buf->vb.size = size;
+ buf->pt = &dev->ts.pt_ts;
+
+ err = videobuf_iolock(dev->pci,&buf->vb,NULL);
+ if (err)
+ goto oops;
+ err = saa7134_pgtable_build(dev->pci,buf->pt,
+ buf->vb.dma.sglist,
+ buf->vb.dma.sglen,
+ saa7134_buffer_startpage(buf));
+ if (err)
+ goto oops;
+ }
+ buf->vb.state = STATE_PREPARED;
+ buf->activate = buffer_activate;
+ buf->vb.field = field;
+ return 0;
+
+ oops:
+ saa7134_dma_free(dev,buf);
+ return err;
+}
+
+static int
+buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct saa7134_dev *dev = q->priv_data;
+
+ *size = TS_PACKET_SIZE * dev->ts.nr_packets;
+ if (0 == *count)
+ *count = dev->ts.nr_bufs;
+ *count = saa7134_buffer_count(*size,*count);
+ return 0;
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct saa7134_dev *dev = q->priv_data;
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+ saa7134_buffer_queue(dev,&dev->ts_q,buf);
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct saa7134_dev *dev = q->priv_data;
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+ saa7134_dma_free(dev,buf);
+}
+
+struct videobuf_queue_ops saa7134_ts_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+EXPORT_SYMBOL_GPL(saa7134_ts_qops);
+
+/* ----------------------------------------------------------- */
+/* exported stuff */
+
+static unsigned int tsbufs = 4;
+module_param(tsbufs, int, 0444);
+MODULE_PARM_DESC(tsbufs,"number of ts buffers, range 2-32");
+
+static unsigned int ts_nr_packets = 30;
+module_param(ts_nr_packets, int, 0444);
+MODULE_PARM_DESC(ts_nr_packets,"size of a ts buffers (in ts packets)");
+
+int saa7134_ts_init1(struct saa7134_dev *dev)
+{
+ /* sanitycheck insmod options */
+ if (tsbufs < 2)
+ tsbufs = 2;
+ if (tsbufs > VIDEO_MAX_FRAME)
+ tsbufs = VIDEO_MAX_FRAME;
+ if (ts_nr_packets < 4)
+ ts_nr_packets = 4;
+ if (ts_nr_packets > 312)
+ ts_nr_packets = 312;
+ dev->ts.nr_bufs = tsbufs;
+ dev->ts.nr_packets = ts_nr_packets;
+
+ INIT_LIST_HEAD(&dev->ts_q.queue);
+ init_timer(&dev->ts_q.timeout);
+ dev->ts_q.timeout.function = saa7134_buffer_timeout;
+ dev->ts_q.timeout.data = (unsigned long)(&dev->ts_q);
+ dev->ts_q.dev = dev;
+ dev->ts_q.need_two = 1;
+ saa7134_pgtable_alloc(dev->pci,&dev->ts.pt_ts);
+
+ /* init TS hw */
+ saa_writeb(SAA7134_TS_SERIAL1, 0x00); /* deactivate TS softreset */
+ saa_writeb(SAA7134_TS_PARALLEL, 0xec); /* TSSOP high active, TSVAL high active, TSLOCK ignored */
+ saa_writeb(SAA7134_TS_PARALLEL_SERIAL, (TS_PACKET_SIZE-1));
+ saa_writeb(SAA7134_TS_DMA0, ((dev->ts.nr_packets-1)&0xff));
+ saa_writeb(SAA7134_TS_DMA1, (((dev->ts.nr_packets-1)>>8)&0xff));
+ saa_writeb(SAA7134_TS_DMA2, ((((dev->ts.nr_packets-1)>>16)&0x3f) | 0x00)); /* TSNOPIT=0, TSCOLAP=0 */
+
+ return 0;
+}
+
+int saa7134_ts_fini(struct saa7134_dev *dev)
+{
+ saa7134_pgtable_free(dev->pci,&dev->ts.pt_ts);
+ return 0;
+}
+
+
+void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status)
+{
+ enum v4l2_field field;
+
+ spin_lock(&dev->slock);
+ if (dev->ts_q.curr) {
+ field = dev->ts_q.curr->vb.field;
+ if (field == V4L2_FIELD_TOP) {
+ if ((status & 0x100000) != 0x000000)
+ goto done;
+ } else {
+ if ((status & 0x100000) != 0x100000)
+ goto done;
+ }
+ saa7134_buffer_finish(dev,&dev->ts_q,STATE_DONE);
+ }
+ saa7134_buffer_next(dev,&dev->ts_q);
+
+ done:
+ spin_unlock(&dev->slock);
+}
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/video/saa7134/saa7134-tvaudio.c
new file mode 100644
index 00000000000..ecac13c006d
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-tvaudio.c
@@ -0,0 +1,1031 @@
+/*
+ * $Id: saa7134-tvaudio.c,v 1.22 2005/01/07 13:11:19 kraxel Exp $
+ *
+ * device driver for philips saa7134 based TV cards
+ * tv audio decoder (fm stereo, nicam, ...)
+ *
+ * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/smp_lock.h>
+#include <asm/div64.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int audio_debug = 0;
+module_param(audio_debug, int, 0644);
+MODULE_PARM_DESC(audio_debug,"enable debug messages [tv audio]");
+
+static unsigned int audio_ddep = 0;
+module_param(audio_ddep, int, 0644);
+MODULE_PARM_DESC(audio_ddep,"audio ddep overwrite");
+
+static int audio_clock_override = UNSET;
+module_param(audio_clock_override, int, 0644);
+
+static int audio_clock_tweak = 0;
+module_param(audio_clock_tweak, int, 0644);
+MODULE_PARM_DESC(audio_clock_tweak, "Audio clock tick fine tuning for cards with audio crystal that's slightly off (range [-1024 .. 1024])");
+
+#define dprintk(fmt, arg...) if (audio_debug) \
+ printk(KERN_DEBUG "%s/audio: " fmt, dev->name , ## arg)
+#define d2printk(fmt, arg...) if (audio_debug > 1) \
+ printk(KERN_DEBUG "%s/audio: " fmt, dev->name, ## arg)
+
+#define print_regb(reg) printk("%s: reg 0x%03x [%-16s]: 0x%02x\n", \
+ dev->name,(SAA7134_##reg),(#reg),saa_readb((SAA7134_##reg)))
+
+/* msecs */
+#define SCAN_INITIAL_DELAY 1000
+#define SCAN_SAMPLE_DELAY 200
+#define SCAN_SUBCARRIER_DELAY 2000
+
+/* ------------------------------------------------------------------ */
+/* saa7134 code */
+
+static struct mainscan {
+ char *name;
+ v4l2_std_id std;
+ int carr;
+} mainscan[] = {
+ {
+ .name = "M",
+ .std = V4L2_STD_NTSC | V4L2_STD_PAL_M,
+ .carr = 4500,
+ },{
+ .name = "BG",
+ .std = V4L2_STD_PAL_BG,
+ .carr = 5500,
+ },{
+ .name = "I",
+ .std = V4L2_STD_PAL_I,
+ .carr = 6000,
+ },{
+ .name = "DKL",
+ .std = V4L2_STD_PAL_DK | V4L2_STD_SECAM,
+ .carr = 6500,
+ }
+};
+
+static struct saa7134_tvaudio tvaudio[] = {
+ {
+ .name = "PAL-B/G FM-stereo",
+ .std = V4L2_STD_PAL,
+ .mode = TVAUDIO_FM_BG_STEREO,
+ .carr1 = 5500,
+ .carr2 = 5742,
+ },{
+ .name = "PAL-D/K1 FM-stereo",
+ .std = V4L2_STD_PAL,
+ .carr1 = 6500,
+ .carr2 = 6258,
+ .mode = TVAUDIO_FM_BG_STEREO,
+ },{
+ .name = "PAL-D/K2 FM-stereo",
+ .std = V4L2_STD_PAL,
+ .carr1 = 6500,
+ .carr2 = 6742,
+ .mode = TVAUDIO_FM_BG_STEREO,
+ },{
+ .name = "PAL-D/K3 FM-stereo",
+ .std = V4L2_STD_PAL,
+ .carr1 = 6500,
+ .carr2 = 5742,
+ .mode = TVAUDIO_FM_BG_STEREO,
+ },{
+ .name = "PAL-B/G NICAM",
+ .std = V4L2_STD_PAL,
+ .carr1 = 5500,
+ .carr2 = 5850,
+ .mode = TVAUDIO_NICAM_FM,
+ },{
+ .name = "PAL-I NICAM",
+ .std = V4L2_STD_PAL,
+ .carr1 = 6000,
+ .carr2 = 6552,
+ .mode = TVAUDIO_NICAM_FM,
+ },{
+ .name = "PAL-D/K NICAM",
+ .std = V4L2_STD_PAL,
+ .carr1 = 6500,
+ .carr2 = 5850,
+ .mode = TVAUDIO_NICAM_FM,
+ },{
+ .name = "SECAM-L NICAM",
+ .std = V4L2_STD_SECAM,
+ .carr1 = 6500,
+ .carr2 = 5850,
+ .mode = TVAUDIO_NICAM_AM,
+ },{
+ .name = "SECAM-D/K",
+ .std = V4L2_STD_SECAM,
+ .carr1 = 6500,
+ .carr2 = -1,
+ .mode = TVAUDIO_FM_MONO,
+ },{
+ .name = "NTSC-M",
+ .std = V4L2_STD_NTSC,
+ .carr1 = 4500,
+ .carr2 = -1,
+ .mode = TVAUDIO_FM_MONO,
+ },{
+ .name = "NTSC-A2 FM-stereo",
+ .std = V4L2_STD_NTSC,
+ .carr1 = 4500,
+ .carr2 = 4724,
+ .mode = TVAUDIO_FM_K_STEREO,
+ }
+};
+#define TVAUDIO (sizeof(tvaudio)/sizeof(struct saa7134_tvaudio))
+
+/* ------------------------------------------------------------------ */
+
+static void tvaudio_init(struct saa7134_dev *dev)
+{
+ int clock = saa7134_boards[dev->board].audio_clock;
+
+ if (UNSET != audio_clock_override)
+ clock = audio_clock_override;
+
+ /* init all audio registers */
+ saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x00);
+ if (need_resched())
+ schedule();
+ else
+ udelay(10);
+
+ saa_writeb(SAA7134_AUDIO_CLOCK0, clock & 0xff);
+ saa_writeb(SAA7134_AUDIO_CLOCK1, (clock >> 8) & 0xff);
+ saa_writeb(SAA7134_AUDIO_CLOCK2, (clock >> 16) & 0xff);
+ saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x01);
+
+ saa_writeb(SAA7134_NICAM_ERROR_LOW, 0x14);
+ saa_writeb(SAA7134_NICAM_ERROR_HIGH, 0x50);
+ saa_writeb(SAA7134_MONITOR_SELECT, 0xa0);
+ saa_writeb(SAA7134_FM_DEMATRIX, 0x80);
+}
+
+static u32 tvaudio_carr2reg(u32 carrier)
+{
+ u64 a = carrier;
+
+ a <<= 24;
+ do_div(a,12288);
+ return a;
+}
+
+static void tvaudio_setcarrier(struct saa7134_dev *dev,
+ int primary, int secondary)
+{
+ if (-1 == secondary)
+ secondary = primary;
+ saa_writel(SAA7134_CARRIER1_FREQ0 >> 2, tvaudio_carr2reg(primary));
+ saa_writel(SAA7134_CARRIER2_FREQ0 >> 2, tvaudio_carr2reg(secondary));
+}
+
+static void mute_input_7134(struct saa7134_dev *dev)
+{
+ unsigned int mute;
+ struct saa7134_input *in;
+ int ausel=0, ics=0, ocs=0;
+ int mask;
+
+ /* look what is to do ... */
+ in = dev->input;
+ mute = (dev->ctl_mute ||
+ (dev->automute && (&card(dev).radio) != in));
+ if (PCI_DEVICE_ID_PHILIPS_SAA7130 == dev->pci->device &&
+ card(dev).mute.name) {
+ /* 7130 - we'll mute using some unconnected audio input */
+ if (mute)
+ in = &card(dev).mute;
+ }
+ if (dev->hw_mute == mute &&
+ dev->hw_input == in) {
+ dprintk("mute/input: nothing to do [mute=%d,input=%s]\n",
+ mute,in->name);
+ return;
+ }
+
+ dprintk("ctl_mute=%d automute=%d input=%s => mute=%d input=%s\n",
+ dev->ctl_mute,dev->automute,dev->input->name,mute,in->name);
+ dev->hw_mute = mute;
+ dev->hw_input = in;
+
+ if (PCI_DEVICE_ID_PHILIPS_SAA7134 == dev->pci->device)
+ /* 7134 mute */
+ saa_writeb(SAA7134_AUDIO_MUTE_CTRL, mute ? 0xbf : 0xbb);
+
+ /* switch internal audio mux */
+ switch (in->amux) {
+ case TV: ausel=0xc0; ics=0x00; ocs=0x02; break;
+ case LINE1: ausel=0x80; ics=0x00; ocs=0x00; break;
+ case LINE2: ausel=0x80; ics=0x08; ocs=0x01; break;
+ case LINE2_LEFT: ausel=0x80; ics=0x08; ocs=0x05; break;
+ }
+ saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, ausel);
+ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x08, ics);
+ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, ocs);
+
+ /* switch gpio-connected external audio mux */
+ if (0 == card(dev).gpiomask)
+ return;
+ mask = card(dev).gpiomask;
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, mask, mask);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, mask, in->gpio);
+ saa7134_track_gpio(dev,in->name);
+}
+
+static void tvaudio_setmode(struct saa7134_dev *dev,
+ struct saa7134_tvaudio *audio,
+ char *note)
+{
+ int acpf, tweak = 0;
+
+ if (dev->tvnorm->id == V4L2_STD_NTSC) {
+ acpf = 0x19066;
+ } else {
+ acpf = 0x1e000;
+ }
+ if (audio_clock_tweak > -1024 && audio_clock_tweak < 1024)
+ tweak = audio_clock_tweak;
+
+ if (note)
+ dprintk("tvaudio_setmode: %s %s [%d.%03d/%d.%03d MHz] acpf=%d%+d\n",
+ note,audio->name,
+ audio->carr1 / 1000, audio->carr1 % 1000,
+ audio->carr2 / 1000, audio->carr2 % 1000,
+ acpf, tweak);
+
+ acpf += tweak;
+ saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD0, (acpf & 0x0000ff) >> 0);
+ saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD1, (acpf & 0x00ff00) >> 8);
+ saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD2, (acpf & 0x030000) >> 16);
+ tvaudio_setcarrier(dev,audio->carr1,audio->carr2);
+
+ switch (audio->mode) {
+ case TVAUDIO_FM_MONO:
+ case TVAUDIO_FM_BG_STEREO:
+ saa_writeb(SAA7134_DEMODULATOR, 0x00);
+ saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x00);
+ saa_writeb(SAA7134_FM_DEEMPHASIS, 0x22);
+ saa_writeb(SAA7134_FM_DEMATRIX, 0x80);
+ saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa0);
+ break;
+ case TVAUDIO_FM_K_STEREO:
+ saa_writeb(SAA7134_DEMODULATOR, 0x00);
+ saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x01);
+ saa_writeb(SAA7134_FM_DEEMPHASIS, 0x22);
+ saa_writeb(SAA7134_FM_DEMATRIX, 0x80);
+ saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa0);
+ break;
+ case TVAUDIO_NICAM_FM:
+ saa_writeb(SAA7134_DEMODULATOR, 0x10);
+ saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x00);
+ saa_writeb(SAA7134_FM_DEEMPHASIS, 0x44);
+ saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa1);
+ saa_writeb(SAA7134_NICAM_CONFIG, 0x00);
+ break;
+ case TVAUDIO_NICAM_AM:
+ saa_writeb(SAA7134_DEMODULATOR, 0x12);
+ saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x00);
+ saa_writeb(SAA7134_FM_DEEMPHASIS, 0x44);
+ saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa1);
+ saa_writeb(SAA7134_NICAM_CONFIG, 0x00);
+ break;
+ case TVAUDIO_FM_SAT_STEREO:
+ /* not implemented (yet) */
+ break;
+ }
+}
+
+static int tvaudio_sleep(struct saa7134_dev *dev, int timeout)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue(&dev->thread.wq, &wait);
+ if (dev->thread.scan1 == dev->thread.scan2 && !dev->thread.shutdown) {
+ if (timeout < 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ } else {
+#if 0
+ /* hmm, that one doesn't return on wakeup ... */
+ msleep_interruptible(timeout);
+#else
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(msecs_to_jiffies(timeout));
+#endif
+ }
+ }
+ remove_wait_queue(&dev->thread.wq, &wait);
+ return dev->thread.scan1 != dev->thread.scan2;
+}
+
+static int tvaudio_checkcarrier(struct saa7134_dev *dev, struct mainscan *scan)
+{
+ __s32 left,right,value;
+
+ if (audio_debug > 1) {
+ int i;
+ dprintk("debug %d:",scan->carr);
+ for (i = -150; i <= 150; i += 30) {
+ tvaudio_setcarrier(dev,scan->carr+i,scan->carr+i);
+ saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+ if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY))
+ return -1;
+ value = saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+ if (0 == i)
+ printk(" # %6d # ",value >> 16);
+ else
+ printk(" %6d",value >> 16);
+ }
+ printk("\n");
+ }
+
+ if (dev->tvnorm->id & scan->std) {
+ tvaudio_setcarrier(dev,scan->carr-90,scan->carr-90);
+ saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+ if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY))
+ return -1;
+ left = saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+
+ tvaudio_setcarrier(dev,scan->carr+90,scan->carr+90);
+ saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+ if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY))
+ return -1;
+ right = saa_readl(SAA7134_LEVEL_READOUT1 >> 2);
+
+ left >>= 16;
+ right >>= 16;
+ value = left > right ? left - right : right - left;
+ dprintk("scanning %d.%03d MHz [%4s] => dc is %5d [%d/%d]\n",
+ scan->carr / 1000, scan->carr % 1000,
+ scan->name, value, left, right);
+ } else {
+ value = 0;
+ dprintk("skipping %d.%03d MHz [%4s]\n",
+ scan->carr / 1000, scan->carr % 1000, scan->name);
+ }
+ return value;
+}
+
+#if 0
+static void sifdebug_dump_regs(struct saa7134_dev *dev)
+{
+ print_regb(AUDIO_STATUS);
+ print_regb(IDENT_SIF);
+ print_regb(LEVEL_READOUT1);
+ print_regb(LEVEL_READOUT2);
+ print_regb(DCXO_IDENT_CTRL);
+ print_regb(DEMODULATOR);
+ print_regb(AGC_GAIN_SELECT);
+ print_regb(MONITOR_SELECT);
+ print_regb(FM_DEEMPHASIS);
+ print_regb(FM_DEMATRIX);
+ print_regb(SIF_SAMPLE_FREQ);
+ print_regb(ANALOG_IO_SELECT);
+}
+#endif
+
+static int tvaudio_getstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *audio)
+{
+ __u32 idp,nicam;
+ int retval = -1;
+
+ switch (audio->mode) {
+ case TVAUDIO_FM_MONO:
+ return V4L2_TUNER_SUB_MONO;
+ case TVAUDIO_FM_K_STEREO:
+ case TVAUDIO_FM_BG_STEREO:
+ idp = (saa_readb(SAA7134_IDENT_SIF) & 0xe0) >> 5;
+ dprintk("getstereo: fm/stereo: idp=0x%x\n",idp);
+ if (0x03 == (idp & 0x03))
+ retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ else if (0x05 == (idp & 0x05))
+ retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ else if (0x01 == (idp & 0x01))
+ retval = V4L2_TUNER_SUB_MONO;
+ break;
+ case TVAUDIO_FM_SAT_STEREO:
+ /* not implemented (yet) */
+ break;
+ case TVAUDIO_NICAM_FM:
+ case TVAUDIO_NICAM_AM:
+ nicam = saa_readb(SAA7134_NICAM_STATUS);
+ dprintk("getstereo: nicam=0x%x\n",nicam);
+ switch (nicam & 0x0b) {
+ case 0x09:
+ retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ break;
+ case 0x0a:
+ retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ break;
+ case 0x08:
+ default:
+ retval = V4L2_TUNER_SUB_MONO;
+ break;
+ }
+ break;
+ }
+ if (retval != -1)
+ dprintk("found audio subchannels:%s%s%s%s\n",
+ (retval & V4L2_TUNER_SUB_MONO) ? " mono" : "",
+ (retval & V4L2_TUNER_SUB_STEREO) ? " stereo" : "",
+ (retval & V4L2_TUNER_SUB_LANG1) ? " lang1" : "",
+ (retval & V4L2_TUNER_SUB_LANG2) ? " lang2" : "");
+ return retval;
+}
+
+static int tvaudio_setstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *audio,
+ u32 mode)
+{
+ static char *name[] = {
+ [ V4L2_TUNER_MODE_MONO ] = "mono",
+ [ V4L2_TUNER_MODE_STEREO ] = "stereo",
+ [ V4L2_TUNER_MODE_LANG1 ] = "lang1",
+ [ V4L2_TUNER_MODE_LANG2 ] = "lang2",
+ };
+ static u32 fm[] = {
+ [ V4L2_TUNER_MODE_MONO ] = 0x00, /* ch1 */
+ [ V4L2_TUNER_MODE_STEREO ] = 0x80, /* auto */
+ [ V4L2_TUNER_MODE_LANG1 ] = 0x00, /* ch1 */
+ [ V4L2_TUNER_MODE_LANG2 ] = 0x01, /* ch2 */
+ };
+ u32 reg;
+
+ switch (audio->mode) {
+ case TVAUDIO_FM_MONO:
+ /* nothing to do ... */
+ break;
+ case TVAUDIO_FM_K_STEREO:
+ case TVAUDIO_FM_BG_STEREO:
+ dprintk("setstereo [fm] => %s\n",
+ name[ mode % ARRAY_SIZE(name) ]);
+ reg = fm[ mode % ARRAY_SIZE(fm) ];
+ saa_writeb(SAA7134_FM_DEMATRIX, reg);
+ break;
+ case TVAUDIO_FM_SAT_STEREO:
+ case TVAUDIO_NICAM_AM:
+ case TVAUDIO_NICAM_FM:
+ /* FIXME */
+ break;
+ }
+ return 0;
+}
+
+static int tvaudio_thread(void *data)
+{
+ struct saa7134_dev *dev = data;
+ int carr_vals[ARRAY_SIZE(mainscan)];
+ unsigned int i, audio, nscan;
+ int max1,max2,carrier,rx,mode,lastmode,default_carrier;
+
+ daemonize("%s", dev->name);
+ allow_signal(SIGTERM);
+ for (;;) {
+ tvaudio_sleep(dev,-1);
+ if (dev->thread.shutdown || signal_pending(current))
+ goto done;
+
+ restart:
+ dev->thread.scan1 = dev->thread.scan2;
+ dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1);
+ dev->tvaudio = NULL;
+ tvaudio_init(dev);
+ if (dev->ctl_automute)
+ dev->automute = 1;
+ mute_input_7134(dev);
+
+ /* give the tuner some time */
+ if (tvaudio_sleep(dev,SCAN_INITIAL_DELAY))
+ goto restart;
+
+ max1 = 0;
+ max2 = 0;
+ nscan = 0;
+ carrier = 0;
+ default_carrier = 0;
+ for (i = 0; i < ARRAY_SIZE(mainscan); i++) {
+ if (!(dev->tvnorm->id & mainscan[i].std))
+ continue;
+ if (!default_carrier)
+ default_carrier = mainscan[i].carr;
+ nscan++;
+ }
+
+ if (1 == nscan) {
+ /* only one candidate -- skip scan ;) */
+ max1 = 12345;
+ carrier = default_carrier;
+ } else {
+ /* scan for the main carrier */
+ saa_writeb(SAA7134_MONITOR_SELECT,0x00);
+ tvaudio_setmode(dev,&tvaudio[0],NULL);
+ for (i = 0; i < ARRAY_SIZE(mainscan); i++) {
+ carr_vals[i] = tvaudio_checkcarrier(dev, mainscan+i);
+ if (dev->thread.scan1 != dev->thread.scan2)
+ goto restart;
+ }
+ for (max1 = 0, max2 = 0, i = 0; i < ARRAY_SIZE(mainscan); i++) {
+ if (max1 < carr_vals[i]) {
+ max2 = max1;
+ max1 = carr_vals[i];
+ carrier = mainscan[i].carr;
+ } else if (max2 < carr_vals[i]) {
+ max2 = carr_vals[i];
+ }
+ }
+ }
+
+ if (0 != carrier && max1 > 2000 && max1 > max2*3) {
+ /* found good carrier */
+ dprintk("found %s main sound carrier @ %d.%03d MHz [%d/%d]\n",
+ dev->tvnorm->name, carrier/1000, carrier%1000,
+ max1, max2);
+ dev->last_carrier = carrier;
+
+ } else if (0 != dev->last_carrier) {
+ /* no carrier -- try last detected one as fallback */
+ carrier = dev->last_carrier;
+ printk(KERN_WARNING "%s/audio: audio carrier scan failed, "
+ "using %d.%03d MHz [last detected]\n",
+ dev->name, carrier/1000, carrier%1000);
+
+ } else {
+ /* no carrier + no fallback -- use default */
+ carrier = default_carrier;
+ printk(KERN_WARNING "%s/audio: audio carrier scan failed, "
+ "using %d.%03d MHz [default]\n",
+ dev->name, carrier/1000, carrier%1000);
+ }
+ tvaudio_setcarrier(dev,carrier,carrier);
+ dev->automute = 0;
+ saa_andorb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0x30, 0x00);
+ saa7134_tvaudio_setmute(dev);
+
+ /* find the exact tv audio norm */
+ for (audio = UNSET, i = 0; i < TVAUDIO; i++) {
+ if (dev->tvnorm->id != UNSET &&
+ !(dev->tvnorm->id & tvaudio[i].std))
+ continue;
+ if (tvaudio[i].carr1 != carrier)
+ continue;
+
+ if (UNSET == audio)
+ audio = i;
+ tvaudio_setmode(dev,&tvaudio[i],"trying");
+ if (tvaudio_sleep(dev,SCAN_SUBCARRIER_DELAY))
+ goto restart;
+ if (-1 != tvaudio_getstereo(dev,&tvaudio[i])) {
+ audio = i;
+ break;
+ }
+ }
+ saa_andorb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0x30, 0x30);
+ if (UNSET == audio)
+ continue;
+ tvaudio_setmode(dev,&tvaudio[audio],"using");
+ tvaudio_setstereo(dev,&tvaudio[audio],V4L2_TUNER_MODE_MONO);
+ dev->tvaudio = &tvaudio[audio];
+
+ lastmode = 42;
+ for (;;) {
+ if (tvaudio_sleep(dev,5000))
+ goto restart;
+ if (dev->thread.shutdown || signal_pending(current))
+ break;
+ if (UNSET == dev->thread.mode) {
+ rx = tvaudio_getstereo(dev,&tvaudio[i]);
+ mode = saa7134_tvaudio_rx2mode(rx);
+ } else {
+ mode = dev->thread.mode;
+ }
+ if (lastmode != mode) {
+ tvaudio_setstereo(dev,&tvaudio[audio],mode);
+ lastmode = mode;
+ }
+ }
+ }
+
+ done:
+ complete_and_exit(&dev->thread.exit, 0);
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* saa7133 / saa7135 code */
+
+static char *stdres[0x20] = {
+ [0x00] = "no standard detected",
+ [0x01] = "B/G (in progress)",
+ [0x02] = "D/K (in progress)",
+ [0x03] = "M (in progress)",
+
+ [0x04] = "B/G A2",
+ [0x05] = "B/G NICAM",
+ [0x06] = "D/K A2 (1)",
+ [0x07] = "D/K A2 (2)",
+ [0x08] = "D/K A2 (3)",
+ [0x09] = "D/K NICAM",
+ [0x0a] = "L NICAM",
+ [0x0b] = "I NICAM",
+
+ [0x0c] = "M Korea",
+ [0x0d] = "M BTSC ",
+ [0x0e] = "M EIAJ",
+
+ [0x0f] = "FM radio / IF 10.7 / 50 deemp",
+ [0x10] = "FM radio / IF 10.7 / 75 deemp",
+ [0x11] = "FM radio / IF sel / 50 deemp",
+ [0x12] = "FM radio / IF sel / 75 deemp",
+
+ [0x13 ... 0x1e ] = "unknown",
+ [0x1f] = "??? [in progress]",
+};
+
+#define DSP_RETRY 32
+#define DSP_DELAY 16
+
+static inline int saa_dsp_wait_bit(struct saa7134_dev *dev, int bit)
+{
+ int state, count = DSP_RETRY;
+
+ state = saa_readb(SAA7135_DSP_RWSTATE);
+ if (unlikely(state & SAA7135_DSP_RWSTATE_ERR)) {
+ printk("%s: dsp access error\n",dev->name);
+ /* FIXME: send ack ... */
+ return -EIO;
+ }
+ while (0 == (state & bit)) {
+ if (unlikely(0 == count)) {
+ printk("%s: dsp access wait timeout [bit=%s]\n",
+ dev->name,
+ (bit & SAA7135_DSP_RWSTATE_WRR) ? "WRR" :
+ (bit & SAA7135_DSP_RWSTATE_RDB) ? "RDB" :
+ (bit & SAA7135_DSP_RWSTATE_IDA) ? "IDA" :
+ "???");
+ return -EIO;
+ }
+ saa_wait(DSP_DELAY);
+ state = saa_readb(SAA7135_DSP_RWSTATE);
+ count--;
+ }
+ return 0;
+}
+
+#if 0
+static int saa_dsp_readl(struct saa7134_dev *dev, int reg, u32 *value)
+{
+ int err;
+
+ d2printk("dsp read reg 0x%x\n", reg<<2);
+ saa_readl(reg);
+ err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_RDB);
+ if (err < 0)
+ return err;
+ *value = saa_readl(reg);
+ d2printk("dsp read => 0x%06x\n", *value & 0xffffff);
+ err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_IDA);
+ if (err < 0)
+ return err;
+ return 0;
+}
+#endif
+
+int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value)
+{
+ int err;
+
+ d2printk("dsp write reg 0x%x = 0x%06x\n",reg<<2,value);
+ err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_WRR);
+ if (err < 0)
+ return err;
+ saa_writel(reg,value);
+ err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_WRR);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int getstereo_7133(struct saa7134_dev *dev)
+{
+ int retval = V4L2_TUNER_SUB_MONO;
+ u32 value;
+
+ value = saa_readl(0x528 >> 2);
+ if (value & 0x20)
+ retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ if (value & 0x40)
+ retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ return retval;
+}
+
+static int mute_input_7133(struct saa7134_dev *dev)
+{
+ u32 reg = 0;
+ int mask;
+
+ switch (dev->input->amux) {
+ case TV:
+ reg = 0x02;
+ break;
+ case LINE1:
+ reg = 0x00;
+ break;
+ case LINE2:
+ case LINE2_LEFT:
+ reg = 0x01;
+ break;
+ }
+ if (dev->ctl_mute)
+ reg = 0x07;
+ saa_writel(0x594 >> 2, reg);
+
+ /* switch gpio-connected external audio mux */
+ if (0 != card(dev).gpiomask) {
+ mask = card(dev).gpiomask;
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, mask, mask);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, mask, dev->input->gpio);
+ saa7134_track_gpio(dev,dev->input->name);
+ }
+ return 0;
+}
+
+static int tvaudio_thread_ddep(void *data)
+{
+ struct saa7134_dev *dev = data;
+ u32 value, norms, clock;
+
+ daemonize("%s", dev->name);
+ allow_signal(SIGTERM);
+
+ clock = saa7134_boards[dev->board].audio_clock;
+ if (UNSET != audio_clock_override)
+ clock = audio_clock_override;
+ saa_writel(0x598 >> 2, clock);
+
+ /* unmute */
+ saa_dsp_writel(dev, 0x474 >> 2, 0x00);
+ saa_dsp_writel(dev, 0x450 >> 2, 0x00);
+
+ for (;;) {
+ tvaudio_sleep(dev,-1);
+ if (dev->thread.shutdown || signal_pending(current))
+ goto done;
+
+ restart:
+ dev->thread.scan1 = dev->thread.scan2;
+ dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1);
+
+ if (audio_ddep >= 0x04 && audio_ddep <= 0x0e) {
+ /* insmod option override */
+ norms = (audio_ddep << 2) | 0x01;
+ dprintk("ddep override: %s\n",stdres[audio_ddep]);
+ } else if (&card(dev).radio == dev->input) {
+ dprintk("FM Radio\n");
+ norms = (0x0f << 2) | 0x01;
+ } else {
+ /* (let chip) scan for sound carrier */
+ norms = 0;
+ if (dev->tvnorm->id & V4L2_STD_PAL) {
+ dprintk("PAL scan\n");
+ norms |= 0x2c; /* B/G + D/K + I */
+ }
+ if (dev->tvnorm->id & V4L2_STD_NTSC) {
+ dprintk("NTSC scan\n");
+ norms |= 0x40; /* M */
+ }
+ if (dev->tvnorm->id & V4L2_STD_SECAM) {
+ dprintk("SECAM scan\n");
+ norms |= 0x18; /* L + D/K */
+ }
+ if (0 == norms)
+ norms = 0x7c; /* all */
+ dprintk("scanning:%s%s%s%s%s\n",
+ (norms & 0x04) ? " B/G" : "",
+ (norms & 0x08) ? " D/K" : "",
+ (norms & 0x10) ? " L/L'" : "",
+ (norms & 0x20) ? " I" : "",
+ (norms & 0x40) ? " M" : "");
+ }
+
+ /* kick automatic standard detection */
+ saa_dsp_writel(dev, 0x454 >> 2, 0);
+ saa_dsp_writel(dev, 0x454 >> 2, norms | 0x80);
+
+ /* setup crossbars */
+ saa_dsp_writel(dev, 0x464 >> 2, 0x000000);
+ saa_dsp_writel(dev, 0x470 >> 2, 0x101010);
+
+ if (tvaudio_sleep(dev,3000))
+ goto restart;
+ value = saa_readl(0x528 >> 2) & 0xffffff;
+
+ dprintk("tvaudio thread status: 0x%x [%s%s%s]\n",
+ value, stdres[value & 0x1f],
+ (value & 0x000020) ? ",stereo" : "",
+ (value & 0x000040) ? ",dual" : "");
+ dprintk("detailed status: "
+ "%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n",
+ (value & 0x000080) ? " A2/EIAJ pilot tone " : "",
+ (value & 0x000100) ? " A2/EIAJ dual " : "",
+ (value & 0x000200) ? " A2/EIAJ stereo " : "",
+ (value & 0x000400) ? " A2/EIAJ noise mute " : "",
+
+ (value & 0x000800) ? " BTSC/FM radio pilot " : "",
+ (value & 0x001000) ? " SAP carrier " : "",
+ (value & 0x002000) ? " BTSC stereo noise mute " : "",
+ (value & 0x004000) ? " SAP noise mute " : "",
+ (value & 0x008000) ? " VDSP " : "",
+
+ (value & 0x010000) ? " NICST " : "",
+ (value & 0x020000) ? " NICDU " : "",
+ (value & 0x040000) ? " NICAM muted " : "",
+ (value & 0x080000) ? " NICAM reserve sound " : "",
+
+ (value & 0x100000) ? " init done " : "");
+ }
+
+ done:
+ complete_and_exit(&dev->thread.exit, 0);
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* common stuff + external entry points */
+
+static void saa7134_enable_i2s(struct saa7134_dev *dev)
+{
+ int i2s_format;
+
+ if (!card_is_empress(dev))
+ return;
+ i2s_format = (dev->input->amux == TV) ? 0x00 : 0x01;
+
+ /* enable I2S audio output for the mpeg encoder */
+ saa_writeb(SAA7134_I2S_OUTPUT_SELECT, 0x80);
+ saa_writeb(SAA7134_I2S_OUTPUT_FORMAT, i2s_format);
+ saa_writeb(SAA7134_I2S_OUTPUT_LEVEL, 0x0F);
+ saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x01);
+}
+
+int saa7134_tvaudio_rx2mode(u32 rx)
+{
+ u32 mode;
+
+ mode = V4L2_TUNER_MODE_MONO;
+ if (rx & V4L2_TUNER_SUB_STEREO)
+ mode = V4L2_TUNER_MODE_STEREO;
+ else if (rx & V4L2_TUNER_SUB_LANG1)
+ mode = V4L2_TUNER_MODE_LANG1;
+ else if (rx & V4L2_TUNER_SUB_LANG2)
+ mode = V4L2_TUNER_MODE_LANG2;
+ return mode;
+}
+
+void saa7134_tvaudio_setmute(struct saa7134_dev *dev)
+{
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7130:
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ mute_input_7134(dev);
+ break;
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ mute_input_7133(dev);
+ break;
+ }
+}
+
+void saa7134_tvaudio_setinput(struct saa7134_dev *dev,
+ struct saa7134_input *in)
+{
+ dev->input = in;
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7130:
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ mute_input_7134(dev);
+ break;
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ mute_input_7133(dev);
+ break;
+ }
+ saa7134_enable_i2s(dev);
+}
+
+void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level)
+{
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ saa_writeb(SAA7134_CHANNEL1_LEVEL, level & 0x1f);
+ saa_writeb(SAA7134_CHANNEL2_LEVEL, level & 0x1f);
+ saa_writeb(SAA7134_NICAM_LEVEL_ADJUST, level & 0x1f);
+ break;
+ }
+}
+
+int saa7134_tvaudio_getstereo(struct saa7134_dev *dev)
+{
+ int retval = V4L2_TUNER_SUB_MONO;
+
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ if (dev->tvaudio)
+ retval = tvaudio_getstereo(dev,dev->tvaudio);
+ break;
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ retval = getstereo_7133(dev);
+ break;
+ }
+ return retval;
+}
+
+int saa7134_tvaudio_init2(struct saa7134_dev *dev)
+{
+ DECLARE_MUTEX_LOCKED(sem);
+ int (*my_thread)(void *data) = NULL;
+
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ my_thread = tvaudio_thread;
+ break;
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ my_thread = tvaudio_thread_ddep;
+ break;
+ }
+
+ dev->thread.pid = -1;
+ if (my_thread) {
+ /* start tvaudio thread */
+ init_waitqueue_head(&dev->thread.wq);
+ init_completion(&dev->thread.exit);
+ dev->thread.pid = kernel_thread(my_thread,dev,0);
+ if (dev->thread.pid < 0)
+ printk(KERN_WARNING "%s: kernel_thread() failed\n",
+ dev->name);
+ saa7134_tvaudio_do_scan(dev);
+ }
+
+ saa7134_enable_i2s(dev);
+ return 0;
+}
+
+int saa7134_tvaudio_fini(struct saa7134_dev *dev)
+{
+ /* shutdown tvaudio thread */
+ if (dev->thread.pid >= 0) {
+ dev->thread.shutdown = 1;
+ wake_up_interruptible(&dev->thread.wq);
+ wait_for_completion(&dev->thread.exit);
+ }
+ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, 0x00); /* LINE1 */
+ return 0;
+}
+
+int saa7134_tvaudio_do_scan(struct saa7134_dev *dev)
+{
+ if (dev->thread.pid >= 0) {
+ dev->thread.mode = UNSET;
+ dev->thread.scan2++;
+ wake_up_interruptible(&dev->thread.wq);
+ } else {
+ dev->automute = 0;
+ saa7134_tvaudio_setmute(dev);
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-vbi.c b/drivers/media/video/saa7134/saa7134-vbi.c
new file mode 100644
index 00000000000..86954cc7c37
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-vbi.c
@@ -0,0 +1,270 @@
+/*
+ * $Id: saa7134-vbi.c,v 1.6 2004/12/10 12:33:39 kraxel Exp $
+ *
+ * device driver for philips saa7134 based TV cards
+ * video4linux video interface
+ *
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int vbi_debug = 0;
+module_param(vbi_debug, int, 0644);
+MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]");
+
+static unsigned int vbibufs = 4;
+module_param(vbibufs, int, 0444);
+MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32");
+
+#define dprintk(fmt, arg...) if (vbi_debug) \
+ printk(KERN_DEBUG "%s/vbi: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+#define VBI_LINE_COUNT 16
+#define VBI_LINE_LENGTH 2048
+#define VBI_SCALE 0x200
+
+static void task_init(struct saa7134_dev *dev, struct saa7134_buf *buf,
+ int task)
+{
+ struct saa7134_tvnorm *norm = dev->tvnorm;
+
+ /* setup video scaler */
+ saa_writeb(SAA7134_VBI_H_START1(task), norm->h_start & 0xff);
+ saa_writeb(SAA7134_VBI_H_START2(task), norm->h_start >> 8);
+ saa_writeb(SAA7134_VBI_H_STOP1(task), norm->h_stop & 0xff);
+ saa_writeb(SAA7134_VBI_H_STOP2(task), norm->h_stop >> 8);
+ saa_writeb(SAA7134_VBI_V_START1(task), norm->vbi_v_start & 0xff);
+ saa_writeb(SAA7134_VBI_V_START2(task), norm->vbi_v_start >> 8);
+ saa_writeb(SAA7134_VBI_V_STOP1(task), norm->vbi_v_stop & 0xff);
+ saa_writeb(SAA7134_VBI_V_STOP2(task), norm->vbi_v_stop >> 8);
+
+ saa_writeb(SAA7134_VBI_H_SCALE_INC1(task), VBI_SCALE & 0xff);
+ saa_writeb(SAA7134_VBI_H_SCALE_INC2(task), VBI_SCALE >> 8);
+ saa_writeb(SAA7134_VBI_PHASE_OFFSET_LUMA(task), 0x00);
+ saa_writeb(SAA7134_VBI_PHASE_OFFSET_CHROMA(task), 0x00);
+
+ saa_writeb(SAA7134_VBI_H_LEN1(task), buf->vb.width & 0xff);
+ saa_writeb(SAA7134_VBI_H_LEN2(task), buf->vb.width >> 8);
+ saa_writeb(SAA7134_VBI_V_LEN1(task), buf->vb.height & 0xff);
+ saa_writeb(SAA7134_VBI_V_LEN2(task), buf->vb.height >> 8);
+
+ saa_andorb(SAA7134_DATA_PATH(task), 0xc0, 0x00);
+}
+
+/* ------------------------------------------------------------------ */
+
+static int buffer_activate(struct saa7134_dev *dev,
+ struct saa7134_buf *buf,
+ struct saa7134_buf *next)
+{
+ unsigned long control,base;
+
+ dprintk("buffer_activate [%p]\n",buf);
+ buf->vb.state = STATE_ACTIVE;
+ buf->top_seen = 0;
+
+ task_init(dev,buf,TASK_A);
+ task_init(dev,buf,TASK_B);
+ saa_writeb(SAA7134_OFMT_DATA_A, 0x06);
+ saa_writeb(SAA7134_OFMT_DATA_B, 0x06);
+
+ /* DMA: setup channel 2+3 (= VBI Task A+B) */
+ base = saa7134_buffer_base(buf);
+ control = SAA7134_RS_CONTROL_BURST_16 |
+ SAA7134_RS_CONTROL_ME |
+ (buf->pt->dma >> 12);
+ saa_writel(SAA7134_RS_BA1(2),base);
+ saa_writel(SAA7134_RS_BA2(2),base + buf->vb.size/2);
+ saa_writel(SAA7134_RS_PITCH(2),buf->vb.width);
+ saa_writel(SAA7134_RS_CONTROL(2),control);
+ saa_writel(SAA7134_RS_BA1(3),base);
+ saa_writel(SAA7134_RS_BA2(3),base + buf->vb.size/2);
+ saa_writel(SAA7134_RS_PITCH(3),buf->vb.width);
+ saa_writel(SAA7134_RS_CONTROL(3),control);
+
+ /* start DMA */
+ saa7134_set_dmabits(dev);
+ mod_timer(&dev->vbi_q.timeout, jiffies+BUFFER_TIMEOUT);
+
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct saa7134_fh *fh = q->priv_data;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+ struct saa7134_tvnorm *norm = dev->tvnorm;
+ unsigned int lines, llength, size;
+ int err;
+
+ lines = norm->vbi_v_stop - norm->vbi_v_start +1;
+ if (lines > VBI_LINE_COUNT)
+ lines = VBI_LINE_COUNT;
+#if 1
+ llength = VBI_LINE_LENGTH;
+#else
+ llength = (norm->h_stop - norm->h_start +1) * 2;
+ if (llength > VBI_LINE_LENGTH)
+ llength = VBI_LINE_LENGTH;
+#endif
+ size = lines * llength * 2;
+ if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ return -EINVAL;
+
+ if (buf->vb.size != size)
+ saa7134_dma_free(dev,buf);
+
+ if (STATE_NEEDS_INIT == buf->vb.state) {
+ buf->vb.width = llength;
+ buf->vb.height = lines;
+ buf->vb.size = size;
+ buf->pt = &fh->pt_vbi;
+
+ err = videobuf_iolock(dev->pci,&buf->vb,NULL);
+ if (err)
+ goto oops;
+ err = saa7134_pgtable_build(dev->pci,buf->pt,
+ buf->vb.dma.sglist,
+ buf->vb.dma.sglen,
+ saa7134_buffer_startpage(buf));
+ if (err)
+ goto oops;
+ }
+ buf->vb.state = STATE_PREPARED;
+ buf->activate = buffer_activate;
+ buf->vb.field = field;
+ return 0;
+
+ oops:
+ saa7134_dma_free(dev,buf);
+ return err;
+}
+
+static int
+buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct saa7134_fh *fh = q->priv_data;
+ struct saa7134_dev *dev = fh->dev;
+ int llength,lines;
+
+ lines = dev->tvnorm->vbi_v_stop - dev->tvnorm->vbi_v_start +1;
+#if 1
+ llength = VBI_LINE_LENGTH;
+#else
+ llength = (norm->h_stop - norm->h_start +1) * 2;
+ if (llength > VBI_LINE_LENGTH)
+ llength = VBI_LINE_LENGTH;
+#endif
+ *size = lines * llength * 2;
+ if (0 == *count)
+ *count = vbibufs;
+ *count = saa7134_buffer_count(*size,*count);
+ return 0;
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct saa7134_fh *fh = q->priv_data;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+ saa7134_buffer_queue(dev,&dev->vbi_q,buf);
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct saa7134_fh *fh = q->priv_data;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+ saa7134_dma_free(dev,buf);
+}
+
+struct videobuf_queue_ops saa7134_vbi_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+int saa7134_vbi_init1(struct saa7134_dev *dev)
+{
+ INIT_LIST_HEAD(&dev->vbi_q.queue);
+ init_timer(&dev->vbi_q.timeout);
+ dev->vbi_q.timeout.function = saa7134_buffer_timeout;
+ dev->vbi_q.timeout.data = (unsigned long)(&dev->vbi_q);
+ dev->vbi_q.dev = dev;
+
+ if (vbibufs < 2)
+ vbibufs = 2;
+ if (vbibufs > VIDEO_MAX_FRAME)
+ vbibufs = VIDEO_MAX_FRAME;
+ return 0;
+}
+
+int saa7134_vbi_fini(struct saa7134_dev *dev)
+{
+ /* nothing */
+ return 0;
+}
+
+void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status)
+{
+ spin_lock(&dev->slock);
+ if (dev->vbi_q.curr) {
+ dev->vbi_fieldcount++;
+ /* make sure we have seen both fields */
+ if ((status & 0x10) == 0x00) {
+ dev->vbi_q.curr->top_seen = 1;
+ goto done;
+ }
+ if (!dev->vbi_q.curr->top_seen)
+ goto done;
+
+ dev->vbi_q.curr->vb.field_count = dev->vbi_fieldcount;
+ saa7134_buffer_finish(dev,&dev->vbi_q,STATE_DONE);
+ }
+ saa7134_buffer_next(dev,&dev->vbi_q);
+
+ done:
+ spin_unlock(&dev->slock);
+}
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c
new file mode 100644
index 00000000000..5d66060026f
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134-video.c
@@ -0,0 +1,2406 @@
+/*
+ * $Id: saa7134-video.c,v 1.28 2005/02/15 15:59:35 kraxel Exp $
+ *
+ * device driver for philips saa7134 based TV cards
+ * video4linux video interface
+ *
+ * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+#define V4L2_I2C_CLIENTS 1
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int video_debug = 0;
+static unsigned int gbuffers = 8;
+static unsigned int noninterlaced = 0;
+static unsigned int gbufsize = 720*576*4;
+static unsigned int gbufsize_max = 720*576*4;
+module_param(video_debug, int, 0644);
+MODULE_PARM_DESC(video_debug,"enable debug messages [video]");
+module_param(gbuffers, int, 0444);
+MODULE_PARM_DESC(gbuffers,"number of capture buffers, range 2-32");
+module_param(noninterlaced, int, 0644);
+MODULE_PARM_DESC(noninterlaced,"video input is noninterlaced");
+
+#define dprintk(fmt, arg...) if (video_debug) \
+ printk(KERN_DEBUG "%s/video: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+/* data structs for video */
+
+static int video_out[][9] = {
+ [CCIR656] = { 0x00, 0xb1, 0x00, 0xa1, 0x00, 0x04, 0x06, 0x00, 0x00 },
+};
+
+static struct saa7134_format formats[] = {
+ {
+ .name = "8 bpp gray",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .depth = 8,
+ .pm = 0x06,
+ },{
+ .name = "15 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .depth = 16,
+ .pm = 0x13 | 0x80,
+ },{
+ .name = "15 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .depth = 16,
+ .pm = 0x13 | 0x80,
+ .bswap = 1,
+ },{
+ .name = "16 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .depth = 16,
+ .pm = 0x10 | 0x80,
+ },{
+ .name = "16 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .depth = 16,
+ .pm = 0x10 | 0x80,
+ .bswap = 1,
+ },{
+ .name = "24 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .depth = 24,
+ .pm = 0x11,
+ },{
+ .name = "24 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .depth = 24,
+ .pm = 0x11,
+ .bswap = 1,
+ },{
+ .name = "32 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .depth = 32,
+ .pm = 0x12,
+ },{
+ .name = "32 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .depth = 32,
+ .pm = 0x12,
+ .bswap = 1,
+ .wswap = 1,
+ },{
+ .name = "4:2:2 packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .pm = 0x00,
+ .bswap = 1,
+ .yuv = 1,
+ },{
+ .name = "4:2:2 packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .pm = 0x00,
+ .yuv = 1,
+ },{
+ .name = "4:2:2 planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .depth = 16,
+ .pm = 0x09,
+ .yuv = 1,
+ .planar = 1,
+ .hshift = 1,
+ .vshift = 0,
+ },{
+ .name = "4:2:0 planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .depth = 12,
+ .pm = 0x0a,
+ .yuv = 1,
+ .planar = 1,
+ .hshift = 1,
+ .vshift = 1,
+ },{
+ .name = "4:2:0 planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .depth = 12,
+ .pm = 0x0a,
+ .yuv = 1,
+ .planar = 1,
+ .uvswap = 1,
+ .hshift = 1,
+ .vshift = 1,
+ }
+};
+#define FORMATS ARRAY_SIZE(formats)
+
+#define NORM_625_50 \
+ .h_start = 0, \
+ .h_stop = 719, \
+ .video_v_start = 24, \
+ .video_v_stop = 311, \
+ .vbi_v_start = 7, \
+ .vbi_v_stop = 22, \
+ .src_timing = 4
+
+#define NORM_525_60 \
+ .h_start = 0, \
+ .h_stop = 703, \
+ .video_v_start = 22, \
+ .video_v_stop = 22+239, \
+ .vbi_v_start = 10, /* FIXME */ \
+ .vbi_v_stop = 21, /* FIXME */ \
+ .src_timing = 1
+
+static struct saa7134_tvnorm tvnorms[] = {
+ {
+ .name = "PAL", /* autodetect */
+ .id = V4L2_STD_PAL,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x81,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "PAL-BG",
+ .id = V4L2_STD_PAL_BG,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x81,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "PAL-I",
+ .id = V4L2_STD_PAL_I,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x81,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "PAL-DK",
+ .id = V4L2_STD_PAL_DK,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x81,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "NTSC",
+ .id = V4L2_STD_NTSC,
+ NORM_525_60,
+
+ .sync_control = 0x59,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x89,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x0e,
+ .vgate_misc = 0x18,
+
+ },{
+ .name = "SECAM",
+ .id = V4L2_STD_SECAM,
+ NORM_625_50,
+
+ .sync_control = 0x18, /* old: 0x58, */
+ .luma_control = 0x1b,
+ .chroma_ctrl1 = 0xd1,
+ .chroma_gain = 0x80,
+ .chroma_ctrl2 = 0x00,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "PAL-M",
+ .id = V4L2_STD_PAL_M,
+ NORM_525_60,
+
+ .sync_control = 0x59,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0xb9,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x0e,
+ .vgate_misc = 0x18,
+
+ },{
+ .name = "PAL-Nc",
+ .id = V4L2_STD_PAL_Nc,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0xa1,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "PAL-60",
+ .id = V4L2_STD_PAL_60,
+
+ .h_start = 0,
+ .h_stop = 719,
+ .video_v_start = 22,
+ .video_v_stop = 22+239,
+ .vbi_v_start = 10, /* FIXME */
+ .vbi_v_stop = 21, /* FIXME */
+ .src_timing = 1,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x81,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+ }
+};
+#define TVNORMS ARRAY_SIZE(tvnorms)
+
+#define V4L2_CID_PRIVATE_INVERT (V4L2_CID_PRIVATE_BASE + 0)
+#define V4L2_CID_PRIVATE_Y_ODD (V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_PRIVATE_Y_EVEN (V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 3)
+#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 4)
+
+static const struct v4l2_queryctrl no_ctrl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+static const struct v4l2_queryctrl video_ctrls[] = {
+ /* --- video --- */
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 128,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 68,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 64,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .minimum = -128,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_VFLIP,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+ /* --- audio --- */
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = -15,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ /* --- private --- */
+ {
+ .id = V4L2_CID_PRIVATE_INVERT,
+ .name = "Invert",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_Y_ODD,
+ .name = "y offset odd field",
+ .minimum = 0,
+ .maximum = 128,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_PRIVATE_Y_EVEN,
+ .name = "y offset even field",
+ .minimum = 0,
+ .maximum = 128,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_PRIVATE_AUTOMUTE,
+ .name = "automute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ }
+};
+static const unsigned int CTRLS = ARRAY_SIZE(video_ctrls);
+
+static const struct v4l2_queryctrl* ctrl_by_id(unsigned int id)
+{
+ unsigned int i;
+
+ for (i = 0; i < CTRLS; i++)
+ if (video_ctrls[i].id == id)
+ return video_ctrls+i;
+ return NULL;
+}
+
+static struct saa7134_format* format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < FORMATS; i++)
+ if (formats[i].fourcc == fourcc)
+ return formats+i;
+ return NULL;
+}
+
+/* ----------------------------------------------------------------------- */
+/* resource management */
+
+static int res_get(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bit)
+{
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ down(&dev->lock);
+ if (dev->resources & bit) {
+ /* no, someone else uses it */
+ up(&dev->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->resources |= bit;
+ dprintk("res: get %d\n",bit);
+ up(&dev->lock);
+ return 1;
+}
+
+static
+int res_check(struct saa7134_fh *fh, unsigned int bit)
+{
+ return (fh->resources & bit);
+}
+
+static
+int res_locked(struct saa7134_dev *dev, unsigned int bit)
+{
+ return (dev->resources & bit);
+}
+
+static
+void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bits)
+{
+ if ((fh->resources & bits) != bits)
+ BUG();
+
+ down(&dev->lock);
+ fh->resources &= ~bits;
+ dev->resources &= ~bits;
+ dprintk("res: put %d\n",bits);
+ up(&dev->lock);
+}
+
+/* ------------------------------------------------------------------ */
+
+static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
+{
+ int luma_control,sync_control,mux;
+
+ dprintk("set tv norm = %s\n",norm->name);
+ dev->tvnorm = norm;
+
+ mux = card_in(dev,dev->ctl_input).vmux;
+ luma_control = norm->luma_control;
+ sync_control = norm->sync_control;
+
+ if (mux > 5)
+ luma_control |= 0x80; /* svideo */
+ if (noninterlaced || dev->nosignal)
+ sync_control |= 0x20;
+
+ /* setup cropping */
+ dev->crop_bounds.left = norm->h_start;
+ dev->crop_defrect.left = norm->h_start;
+ dev->crop_bounds.width = norm->h_stop - norm->h_start +1;
+ dev->crop_defrect.width = norm->h_stop - norm->h_start +1;
+
+ dev->crop_bounds.top = (norm->vbi_v_stop+1)*2;
+ dev->crop_defrect.top = norm->video_v_start*2;
+ dev->crop_bounds.height = ((norm->id & V4L2_STD_525_60) ? 524 : 624)
+ - dev->crop_bounds.top;
+ dev->crop_defrect.height = (norm->video_v_stop - norm->video_v_start +1)*2;
+
+ dev->crop_current = dev->crop_defrect;
+
+ /* setup video decoder */
+ saa_writeb(SAA7134_INCR_DELAY, 0x08);
+ saa_writeb(SAA7134_ANALOG_IN_CTRL1, 0xc0 | mux);
+ saa_writeb(SAA7134_ANALOG_IN_CTRL2, 0x00);
+
+ saa_writeb(SAA7134_ANALOG_IN_CTRL3, 0x90);
+ saa_writeb(SAA7134_ANALOG_IN_CTRL4, 0x90);
+ saa_writeb(SAA7134_HSYNC_START, 0xeb);
+ saa_writeb(SAA7134_HSYNC_STOP, 0xe0);
+ saa_writeb(SAA7134_SOURCE_TIMING1, norm->src_timing);
+
+ saa_writeb(SAA7134_SYNC_CTRL, sync_control);
+ saa_writeb(SAA7134_LUMA_CTRL, luma_control);
+ saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright);
+ saa_writeb(SAA7134_DEC_LUMA_CONTRAST, dev->ctl_contrast);
+
+ saa_writeb(SAA7134_DEC_CHROMA_SATURATION, dev->ctl_saturation);
+ saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue);
+ saa_writeb(SAA7134_CHROMA_CTRL1, norm->chroma_ctrl1);
+ saa_writeb(SAA7134_CHROMA_GAIN, norm->chroma_gain);
+
+ saa_writeb(SAA7134_CHROMA_CTRL2, norm->chroma_ctrl2);
+ saa_writeb(SAA7134_MODE_DELAY_CTRL, 0x00);
+
+ saa_writeb(SAA7134_ANALOG_ADC, 0x01);
+ saa_writeb(SAA7134_VGATE_START, 0x11);
+ saa_writeb(SAA7134_VGATE_STOP, 0xfe);
+ saa_writeb(SAA7134_MISC_VGATE_MSB, norm->vgate_misc);
+ saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40);
+ saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80);
+
+#ifdef V4L2_I2C_CLIENTS
+ saa7134_i2c_call_clients(dev,VIDIOC_S_STD,&norm->id);
+#else
+ {
+ /* pass down info to the i2c chips (v4l1) */
+ struct video_channel c;
+ memset(&c,0,sizeof(c));
+ c.channel = dev->ctl_input;
+ c.norm = VIDEO_MODE_PAL;
+ if (norm->id & V4L2_STD_NTSC)
+ c.norm = VIDEO_MODE_NTSC;
+ if (norm->id & V4L2_STD_SECAM)
+ c.norm = VIDEO_MODE_SECAM;
+ saa7134_i2c_call_clients(dev,VIDIOCSCHAN,&c);
+ }
+#endif
+}
+
+static void video_mux(struct saa7134_dev *dev, int input)
+{
+ dprintk("video input = %d [%s]\n",input,card_in(dev,input).name);
+ dev->ctl_input = input;
+ set_tvnorm(dev,dev->tvnorm);
+ saa7134_tvaudio_setinput(dev,&card_in(dev,input));
+}
+
+static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale)
+{
+ static const struct {
+ int xpsc;
+ int xacl;
+ int xc2_1;
+ int xdcg;
+ int vpfy;
+ } vals[] = {
+ /* XPSC XACL XC2_1 XDCG VPFY */
+ { 1, 0, 0, 0, 0 },
+ { 2, 2, 1, 2, 2 },
+ { 3, 4, 1, 3, 2 },
+ { 4, 8, 1, 4, 2 },
+ { 5, 8, 1, 4, 2 },
+ { 6, 8, 1, 4, 3 },
+ { 7, 8, 1, 4, 3 },
+ { 8, 15, 0, 4, 3 },
+ { 9, 15, 0, 4, 3 },
+ { 10, 16, 1, 5, 3 },
+ };
+ static const int count = ARRAY_SIZE(vals);
+ int i;
+
+ for (i = 0; i < count; i++)
+ if (vals[i].xpsc == prescale)
+ break;
+ if (i == count)
+ return;
+
+ saa_writeb(SAA7134_H_PRESCALE(task), vals[i].xpsc);
+ saa_writeb(SAA7134_ACC_LENGTH(task), vals[i].xacl);
+ saa_writeb(SAA7134_LEVEL_CTRL(task),
+ (vals[i].xc2_1 << 3) | (vals[i].xdcg));
+ saa_andorb(SAA7134_FIR_PREFILTER_CTRL(task), 0x0f,
+ (vals[i].vpfy << 2) | vals[i].vpfy);
+}
+
+static void set_v_scale(struct saa7134_dev *dev, int task, int yscale)
+{
+ int val,mirror;
+
+ saa_writeb(SAA7134_V_SCALE_RATIO1(task), yscale & 0xff);
+ saa_writeb(SAA7134_V_SCALE_RATIO2(task), yscale >> 8);
+
+ mirror = (dev->ctl_mirror) ? 0x02 : 0x00;
+ if (yscale < 2048) {
+ /* LPI */
+ dprintk("yscale LPI yscale=%d\n",yscale);
+ saa_writeb(SAA7134_V_FILTER(task), 0x00 | mirror);
+ saa_writeb(SAA7134_LUMA_CONTRAST(task), 0x40);
+ saa_writeb(SAA7134_CHROMA_SATURATION(task), 0x40);
+ } else {
+ /* ACM */
+ val = 0x40 * 1024 / yscale;
+ dprintk("yscale ACM yscale=%d val=0x%x\n",yscale,val);
+ saa_writeb(SAA7134_V_FILTER(task), 0x01 | mirror);
+ saa_writeb(SAA7134_LUMA_CONTRAST(task), val);
+ saa_writeb(SAA7134_CHROMA_SATURATION(task), val);
+ }
+ saa_writeb(SAA7134_LUMA_BRIGHT(task), 0x80);
+}
+
+static void set_size(struct saa7134_dev *dev, int task,
+ int width, int height, int interlace)
+{
+ int prescale,xscale,yscale,y_even,y_odd;
+ int h_start, h_stop, v_start, v_stop;
+ int div = interlace ? 2 : 1;
+
+ /* setup video scaler */
+ h_start = dev->crop_current.left;
+ v_start = dev->crop_current.top/2;
+ h_stop = (dev->crop_current.left + dev->crop_current.width -1);
+ v_stop = (dev->crop_current.top + dev->crop_current.height -1)/2;
+
+ saa_writeb(SAA7134_VIDEO_H_START1(task), h_start & 0xff);
+ saa_writeb(SAA7134_VIDEO_H_START2(task), h_start >> 8);
+ saa_writeb(SAA7134_VIDEO_H_STOP1(task), h_stop & 0xff);
+ saa_writeb(SAA7134_VIDEO_H_STOP2(task), h_stop >> 8);
+ saa_writeb(SAA7134_VIDEO_V_START1(task), v_start & 0xff);
+ saa_writeb(SAA7134_VIDEO_V_START2(task), v_start >> 8);
+ saa_writeb(SAA7134_VIDEO_V_STOP1(task), v_stop & 0xff);
+ saa_writeb(SAA7134_VIDEO_V_STOP2(task), v_stop >> 8);
+
+ prescale = dev->crop_current.width / width;
+ if (0 == prescale)
+ prescale = 1;
+ xscale = 1024 * dev->crop_current.width / prescale / width;
+ yscale = 512 * div * dev->crop_current.height / height;
+ dprintk("prescale=%d xscale=%d yscale=%d\n",prescale,xscale,yscale);
+ set_h_prescale(dev,task,prescale);
+ saa_writeb(SAA7134_H_SCALE_INC1(task), xscale & 0xff);
+ saa_writeb(SAA7134_H_SCALE_INC2(task), xscale >> 8);
+ set_v_scale(dev,task,yscale);
+
+ saa_writeb(SAA7134_VIDEO_PIXELS1(task), width & 0xff);
+ saa_writeb(SAA7134_VIDEO_PIXELS2(task), width >> 8);
+ saa_writeb(SAA7134_VIDEO_LINES1(task), height/div & 0xff);
+ saa_writeb(SAA7134_VIDEO_LINES2(task), height/div >> 8);
+
+ /* deinterlace y offsets */
+ y_odd = dev->ctl_y_odd;
+ y_even = dev->ctl_y_even;
+ saa_writeb(SAA7134_V_PHASE_OFFSET0(task), y_odd);
+ saa_writeb(SAA7134_V_PHASE_OFFSET1(task), y_even);
+ saa_writeb(SAA7134_V_PHASE_OFFSET2(task), y_odd);
+ saa_writeb(SAA7134_V_PHASE_OFFSET3(task), y_even);
+}
+
+/* ------------------------------------------------------------------ */
+
+struct cliplist {
+ __u16 position;
+ __u8 enable;
+ __u8 disable;
+};
+
+static void sort_cliplist(struct cliplist *cl, int entries)
+{
+ struct cliplist swap;
+ int i,j,n;
+
+ for (i = entries-2; i >= 0; i--) {
+ for (n = 0, j = 0; j <= i; j++) {
+ if (cl[j].position > cl[j+1].position) {
+ swap = cl[j];
+ cl[j] = cl[j+1];
+ cl[j+1] = swap;
+ n++;
+ }
+ }
+ if (0 == n)
+ break;
+ }
+}
+
+static void set_cliplist(struct saa7134_dev *dev, int reg,
+ struct cliplist *cl, int entries, char *name)
+{
+ __u8 winbits = 0;
+ int i;
+
+ for (i = 0; i < entries; i++) {
+ winbits |= cl[i].enable;
+ winbits &= ~cl[i].disable;
+ if (i < 15 && cl[i].position == cl[i+1].position)
+ continue;
+ saa_writeb(reg + 0, winbits);
+ saa_writeb(reg + 2, cl[i].position & 0xff);
+ saa_writeb(reg + 3, cl[i].position >> 8);
+ dprintk("clip: %s winbits=%02x pos=%d\n",
+ name,winbits,cl[i].position);
+ reg += 8;
+ }
+ for (; reg < 0x400; reg += 8) {
+ saa_writeb(reg+ 0, 0);
+ saa_writeb(reg + 1, 0);
+ saa_writeb(reg + 2, 0);
+ saa_writeb(reg + 3, 0);
+ }
+}
+
+static int clip_range(int val)
+{
+ if (val < 0)
+ val = 0;
+ return val;
+}
+
+static int setup_clipping(struct saa7134_dev *dev, struct v4l2_clip *clips,
+ int nclips, int interlace)
+{
+ struct cliplist col[16], row[16];
+ int cols, rows, i;
+ int div = interlace ? 2 : 1;
+
+ memset(col,0,sizeof(col)); cols = 0;
+ memset(row,0,sizeof(row)); rows = 0;
+ for (i = 0; i < nclips && i < 8; i++) {
+ col[cols].position = clip_range(clips[i].c.left);
+ col[cols].enable = (1 << i);
+ cols++;
+ col[cols].position = clip_range(clips[i].c.left+clips[i].c.width);
+ col[cols].disable = (1 << i);
+ cols++;
+ row[rows].position = clip_range(clips[i].c.top / div);
+ row[rows].enable = (1 << i);
+ rows++;
+ row[rows].position = clip_range((clips[i].c.top + clips[i].c.height)
+ / div);
+ row[rows].disable = (1 << i);
+ rows++;
+ }
+ sort_cliplist(col,cols);
+ sort_cliplist(row,rows);
+ set_cliplist(dev,0x380,col,cols,"cols");
+ set_cliplist(dev,0x384,row,rows,"rows");
+ return 0;
+}
+
+static int verify_preview(struct saa7134_dev *dev, struct v4l2_window *win)
+{
+ enum v4l2_field field;
+ int maxw, maxh;
+
+ if (NULL == dev->ovbuf.base)
+ return -EINVAL;
+ if (NULL == dev->ovfmt)
+ return -EINVAL;
+ if (win->w.width < 48 || win->w.height < 32)
+ return -EINVAL;
+ if (win->clipcount > 2048)
+ return -EINVAL;
+
+ field = win->field;
+ maxw = dev->crop_current.width;
+ maxh = dev->crop_current.height;
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (win->w.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_TOP;
+ }
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ win->field = field;
+ if (win->w.width > maxw)
+ win->w.width = maxw;
+ if (win->w.height > maxh)
+ win->w.height = maxh;
+ return 0;
+}
+
+static int start_preview(struct saa7134_dev *dev, struct saa7134_fh *fh)
+{
+ unsigned long base,control,bpl;
+ int err;
+
+ err = verify_preview(dev,&fh->win);
+ if (0 != err)
+ return err;
+
+ dev->ovfield = fh->win.field;
+ dprintk("start_preview %dx%d+%d+%d %s field=%s\n",
+ fh->win.w.width,fh->win.w.height,
+ fh->win.w.left,fh->win.w.top,
+ dev->ovfmt->name,v4l2_field_names[dev->ovfield]);
+
+ /* setup window + clipping */
+ set_size(dev,TASK_B,fh->win.w.width,fh->win.w.height,
+ V4L2_FIELD_HAS_BOTH(dev->ovfield));
+ setup_clipping(dev,fh->clips,fh->nclips,
+ V4L2_FIELD_HAS_BOTH(dev->ovfield));
+ if (dev->ovfmt->yuv)
+ saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x03);
+ else
+ saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x01);
+ saa_writeb(SAA7134_OFMT_VIDEO_B, dev->ovfmt->pm | 0x20);
+
+ /* dma: setup channel 1 (= Video Task B) */
+ base = (unsigned long)dev->ovbuf.base;
+ base += dev->ovbuf.fmt.bytesperline * fh->win.w.top;
+ base += dev->ovfmt->depth/8 * fh->win.w.left;
+ bpl = dev->ovbuf.fmt.bytesperline;
+ control = SAA7134_RS_CONTROL_BURST_16;
+ if (dev->ovfmt->bswap)
+ control |= SAA7134_RS_CONTROL_BSWAP;
+ if (dev->ovfmt->wswap)
+ control |= SAA7134_RS_CONTROL_WSWAP;
+ if (V4L2_FIELD_HAS_BOTH(dev->ovfield)) {
+ saa_writel(SAA7134_RS_BA1(1),base);
+ saa_writel(SAA7134_RS_BA2(1),base+bpl);
+ saa_writel(SAA7134_RS_PITCH(1),bpl*2);
+ saa_writel(SAA7134_RS_CONTROL(1),control);
+ } else {
+ saa_writel(SAA7134_RS_BA1(1),base);
+ saa_writel(SAA7134_RS_BA2(1),base);
+ saa_writel(SAA7134_RS_PITCH(1),bpl);
+ saa_writel(SAA7134_RS_CONTROL(1),control);
+ }
+
+ /* start dma */
+ dev->ovenable = 1;
+ saa7134_set_dmabits(dev);
+
+ return 0;
+}
+
+static int stop_preview(struct saa7134_dev *dev, struct saa7134_fh *fh)
+{
+ dev->ovenable = 0;
+ saa7134_set_dmabits(dev);
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int buffer_activate(struct saa7134_dev *dev,
+ struct saa7134_buf *buf,
+ struct saa7134_buf *next)
+{
+ unsigned long base,control,bpl;
+ unsigned long bpl_uv,lines_uv,base2,base3,tmp; /* planar */
+
+ dprintk("buffer_activate buf=%p\n",buf);
+ buf->vb.state = STATE_ACTIVE;
+ buf->top_seen = 0;
+
+ set_size(dev,TASK_A,buf->vb.width,buf->vb.height,
+ V4L2_FIELD_HAS_BOTH(buf->vb.field));
+ if (buf->fmt->yuv)
+ saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x03);
+ else
+ saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x01);
+ saa_writeb(SAA7134_OFMT_VIDEO_A, buf->fmt->pm);
+
+ /* DMA: setup channel 0 (= Video Task A0) */
+ base = saa7134_buffer_base(buf);
+ if (buf->fmt->planar)
+ bpl = buf->vb.width;
+ else
+ bpl = (buf->vb.width * buf->fmt->depth) / 8;
+ control = SAA7134_RS_CONTROL_BURST_16 |
+ SAA7134_RS_CONTROL_ME |
+ (buf->pt->dma >> 12);
+ if (buf->fmt->bswap)
+ control |= SAA7134_RS_CONTROL_BSWAP;
+ if (buf->fmt->wswap)
+ control |= SAA7134_RS_CONTROL_WSWAP;
+ if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) {
+ /* interlaced */
+ saa_writel(SAA7134_RS_BA1(0),base);
+ saa_writel(SAA7134_RS_BA2(0),base+bpl);
+ saa_writel(SAA7134_RS_PITCH(0),bpl*2);
+ } else {
+ /* non-interlaced */
+ saa_writel(SAA7134_RS_BA1(0),base);
+ saa_writel(SAA7134_RS_BA2(0),base);
+ saa_writel(SAA7134_RS_PITCH(0),bpl);
+ }
+ saa_writel(SAA7134_RS_CONTROL(0),control);
+
+ if (buf->fmt->planar) {
+ /* DMA: setup channel 4+5 (= planar task A) */
+ bpl_uv = bpl >> buf->fmt->hshift;
+ lines_uv = buf->vb.height >> buf->fmt->vshift;
+ base2 = base + bpl * buf->vb.height;
+ base3 = base2 + bpl_uv * lines_uv;
+ if (buf->fmt->uvswap)
+ tmp = base2, base2 = base3, base3 = tmp;
+ dprintk("uv: bpl=%ld lines=%ld base2/3=%ld/%ld\n",
+ bpl_uv,lines_uv,base2,base3);
+ if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) {
+ /* interlaced */
+ saa_writel(SAA7134_RS_BA1(4),base2);
+ saa_writel(SAA7134_RS_BA2(4),base2+bpl_uv);
+ saa_writel(SAA7134_RS_PITCH(4),bpl_uv*2);
+ saa_writel(SAA7134_RS_BA1(5),base3);
+ saa_writel(SAA7134_RS_BA2(5),base3+bpl_uv);
+ saa_writel(SAA7134_RS_PITCH(5),bpl_uv*2);
+ } else {
+ /* non-interlaced */
+ saa_writel(SAA7134_RS_BA1(4),base2);
+ saa_writel(SAA7134_RS_BA2(4),base2);
+ saa_writel(SAA7134_RS_PITCH(4),bpl_uv);
+ saa_writel(SAA7134_RS_BA1(5),base3);
+ saa_writel(SAA7134_RS_BA2(5),base3);
+ saa_writel(SAA7134_RS_PITCH(5),bpl_uv);
+ }
+ saa_writel(SAA7134_RS_CONTROL(4),control);
+ saa_writel(SAA7134_RS_CONTROL(5),control);
+ }
+
+ /* start DMA */
+ saa7134_set_dmabits(dev);
+ mod_timer(&dev->video_q.timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct saa7134_fh *fh = q->priv_data;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+ unsigned int size;
+ int err;
+
+ /* sanity checks */
+ if (NULL == fh->fmt)
+ return -EINVAL;
+ if (fh->width < 48 ||
+ fh->height < 32 ||
+ fh->width/4 > dev->crop_current.width ||
+ fh->height/4 > dev->crop_current.height ||
+ fh->width > dev->crop_bounds.width ||
+ fh->height > dev->crop_bounds.height)
+ return -EINVAL;
+ size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ return -EINVAL;
+
+ dprintk("buffer_prepare [%d,size=%dx%d,bytes=%d,fields=%s,%s]\n",
+ vb->i,fh->width,fh->height,size,v4l2_field_names[field],
+ fh->fmt->name);
+ if (buf->vb.width != fh->width ||
+ buf->vb.height != fh->height ||
+ buf->vb.size != size ||
+ buf->vb.field != field ||
+ buf->fmt != fh->fmt) {
+ saa7134_dma_free(dev,buf);
+ }
+
+ if (STATE_NEEDS_INIT == buf->vb.state) {
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.size = size;
+ buf->vb.field = field;
+ buf->fmt = fh->fmt;
+ buf->pt = &fh->pt_cap;
+
+ err = videobuf_iolock(dev->pci,&buf->vb,&dev->ovbuf);
+ if (err)
+ goto oops;
+ err = saa7134_pgtable_build(dev->pci,buf->pt,
+ buf->vb.dma.sglist,
+ buf->vb.dma.sglen,
+ saa7134_buffer_startpage(buf));
+ if (err)
+ goto oops;
+ }
+ buf->vb.state = STATE_PREPARED;
+ buf->activate = buffer_activate;
+ return 0;
+
+ oops:
+ saa7134_dma_free(dev,buf);
+ return err;
+}
+
+static int
+buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct saa7134_fh *fh = q->priv_data;
+
+ *size = fh->fmt->depth * fh->width * fh->height >> 3;
+ if (0 == *count)
+ *count = gbuffers;
+ *count = saa7134_buffer_count(*size,*count);
+ return 0;
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct saa7134_fh *fh = q->priv_data;
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+ saa7134_buffer_queue(fh->dev,&fh->dev->video_q,buf);
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct saa7134_fh *fh = q->priv_data;
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+ saa7134_dma_free(fh->dev,buf);
+}
+
+static struct videobuf_queue_ops video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static int get_control(struct saa7134_dev *dev, struct v4l2_control *c)
+{
+ const struct v4l2_queryctrl* ctrl;
+
+ ctrl = ctrl_by_id(c->id);
+ if (NULL == ctrl)
+ return -EINVAL;
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->value = dev->ctl_bright;
+ break;
+ case V4L2_CID_HUE:
+ c->value = dev->ctl_hue;
+ break;
+ case V4L2_CID_CONTRAST:
+ c->value = dev->ctl_contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ c->value = dev->ctl_saturation;
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ c->value = dev->ctl_mute;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ c->value = dev->ctl_volume;
+ break;
+ case V4L2_CID_PRIVATE_INVERT:
+ c->value = dev->ctl_invert;
+ break;
+ case V4L2_CID_VFLIP:
+ c->value = dev->ctl_mirror;
+ break;
+ case V4L2_CID_PRIVATE_Y_EVEN:
+ c->value = dev->ctl_y_even;
+ break;
+ case V4L2_CID_PRIVATE_Y_ODD:
+ c->value = dev->ctl_y_odd;
+ break;
+ case V4L2_CID_PRIVATE_AUTOMUTE:
+ c->value = dev->ctl_automute;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int set_control(struct saa7134_dev *dev, struct saa7134_fh *fh,
+ struct v4l2_control *c)
+{
+ const struct v4l2_queryctrl* ctrl;
+ unsigned long flags;
+ int restart_overlay = 0;
+
+ ctrl = ctrl_by_id(c->id);
+ if (NULL == ctrl)
+ return -EINVAL;
+ dprintk("set_control name=%s val=%d\n",ctrl->name,c->value);
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ case V4L2_CTRL_TYPE_MENU:
+ case V4L2_CTRL_TYPE_INTEGER:
+ if (c->value < ctrl->minimum)
+ c->value = ctrl->minimum;
+ if (c->value > ctrl->maximum)
+ c->value = ctrl->maximum;
+ break;
+ default:
+ /* nothing */;
+ };
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ dev->ctl_bright = c->value;
+ saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright);
+ break;
+ case V4L2_CID_HUE:
+ dev->ctl_hue = c->value;
+ saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue);
+ break;
+ case V4L2_CID_CONTRAST:
+ dev->ctl_contrast = c->value;
+ saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
+ dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
+ break;
+ case V4L2_CID_SATURATION:
+ dev->ctl_saturation = c->value;
+ saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
+ dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ dev->ctl_mute = c->value;
+ saa7134_tvaudio_setmute(dev);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ dev->ctl_volume = c->value;
+ saa7134_tvaudio_setvolume(dev,dev->ctl_volume);
+ break;
+ case V4L2_CID_PRIVATE_INVERT:
+ dev->ctl_invert = c->value;
+ saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
+ dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
+ saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
+ dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
+ break;
+ case V4L2_CID_VFLIP:
+ dev->ctl_mirror = c->value;
+ restart_overlay = 1;
+ break;
+ case V4L2_CID_PRIVATE_Y_EVEN:
+ dev->ctl_y_even = c->value;
+ restart_overlay = 1;
+ break;
+ case V4L2_CID_PRIVATE_Y_ODD:
+ dev->ctl_y_odd = c->value;
+ restart_overlay = 1;
+ break;
+ case V4L2_CID_PRIVATE_AUTOMUTE:
+ dev->ctl_automute = c->value;
+ if (dev->tda9887_conf) {
+ if (dev->ctl_automute)
+ dev->tda9887_conf |= TDA9887_AUTOMUTE;
+ else
+ dev->tda9887_conf &= ~TDA9887_AUTOMUTE;
+ saa7134_i2c_call_clients(dev, TDA9887_SET_CONFIG,
+ &dev->tda9887_conf);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (restart_overlay && fh && res_check(fh, RESOURCE_OVERLAY)) {
+ spin_lock_irqsave(&dev->slock,flags);
+ stop_preview(dev,fh);
+ start_preview(dev,fh);
+ spin_unlock_irqrestore(&dev->slock,flags);
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static struct videobuf_queue* saa7134_queue(struct saa7134_fh *fh)
+{
+ struct videobuf_queue* q = NULL;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ q = &fh->cap;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ q = &fh->vbi;
+ break;
+ default:
+ BUG();
+ }
+ return q;
+}
+
+static int saa7134_resource(struct saa7134_fh *fh)
+{
+ int res = 0;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ res = RESOURCE_VIDEO;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ res = RESOURCE_VBI;
+ break;
+ default:
+ BUG();
+ }
+ return res;
+}
+
+static int video_open(struct inode *inode, struct file *file)
+{
+ int minor = iminor(inode);
+ struct saa7134_dev *h,*dev = NULL;
+ struct saa7134_fh *fh;
+ struct list_head *list;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ int radio = 0;
+
+ list_for_each(list,&saa7134_devlist) {
+ h = list_entry(list, struct saa7134_dev, devlist);
+ if (h->video_dev && (h->video_dev->minor == minor))
+ dev = h;
+ if (h->radio_dev && (h->radio_dev->minor == minor)) {
+ radio = 1;
+ dev = h;
+ }
+ if (h->vbi_dev && (h->vbi_dev->minor == minor)) {
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ dev = h;
+ }
+ }
+ if (NULL == dev)
+ return -ENODEV;
+
+ dprintk("open minor=%d radio=%d type=%s\n",minor,radio,
+ v4l2_type_names[type]);
+
+ /* allocate + initialize per filehandle data */
+ fh = kmalloc(sizeof(*fh),GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+ memset(fh,0,sizeof(*fh));
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->radio = radio;
+ fh->type = type;
+ fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+ fh->width = 720;
+ fh->height = 576;
+ v4l2_prio_open(&dev->prio,&fh->prio);
+
+ videobuf_queue_init(&fh->cap, &video_qops,
+ dev->pci, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct saa7134_buf),
+ fh);
+ videobuf_queue_init(&fh->vbi, &saa7134_vbi_qops,
+ dev->pci, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct saa7134_buf),
+ fh);
+ saa7134_pgtable_alloc(dev->pci,&fh->pt_cap);
+ saa7134_pgtable_alloc(dev->pci,&fh->pt_vbi);
+
+ if (fh->radio) {
+ /* switch to radio mode */
+ saa7134_tvaudio_setinput(dev,&card(dev).radio);
+ saa7134_i2c_call_clients(dev,AUDC_SET_RADIO,NULL);
+ } else {
+ /* switch to video/vbi mode */
+ video_mux(dev,dev->ctl_input);
+ }
+ return 0;
+}
+
+static ssize_t
+video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct saa7134_fh *fh = file->private_data;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (res_locked(fh->dev,RESOURCE_VIDEO))
+ return -EBUSY;
+ return videobuf_read_one(saa7134_queue(fh),
+ data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (!res_get(fh->dev,fh,RESOURCE_VBI))
+ return -EBUSY;
+ return videobuf_read_stream(saa7134_queue(fh),
+ data, count, ppos, 1,
+ file->f_flags & O_NONBLOCK);
+ break;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static unsigned int
+video_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct videobuf_buffer *buf = NULL;
+
+ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)
+ return videobuf_poll_stream(file, &fh->vbi, wait);
+
+ if (res_check(fh,RESOURCE_VIDEO)) {
+ if (!list_empty(&fh->cap.stream))
+ buf = list_entry(fh->cap.stream.next, struct videobuf_buffer, stream);
+ } else {
+ down(&fh->cap.lock);
+ if (UNSET == fh->cap.read_off) {
+ /* need to capture a new frame */
+ if (res_locked(fh->dev,RESOURCE_VIDEO)) {
+ up(&fh->cap.lock);
+ return POLLERR;
+ }
+ if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,fh->cap.field)) {
+ up(&fh->cap.lock);
+ return POLLERR;
+ }
+ fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf);
+ fh->cap.read_off = 0;
+ }
+ up(&fh->cap.lock);
+ buf = fh->cap.read_buf;
+ }
+
+ if (!buf)
+ return POLLERR;
+
+ poll_wait(file, &buf->done, wait);
+ if (buf->state == STATE_DONE ||
+ buf->state == STATE_ERROR)
+ return POLLIN|POLLRDNORM;
+ return 0;
+}
+
+static int video_release(struct inode *inode, struct file *file)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
+ unsigned long flags;
+
+ /* turn off overlay */
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ spin_lock_irqsave(&dev->slock,flags);
+ stop_preview(dev,fh);
+ spin_unlock_irqrestore(&dev->slock,flags);
+ res_free(dev,fh,RESOURCE_OVERLAY);
+ }
+
+ /* stop video capture */
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ videobuf_streamoff(&fh->cap);
+ res_free(dev,fh,RESOURCE_VIDEO);
+ }
+ if (fh->cap.read_buf) {
+ buffer_release(&fh->cap,fh->cap.read_buf);
+ kfree(fh->cap.read_buf);
+ }
+
+ /* stop vbi capture */
+ if (res_check(fh, RESOURCE_VBI)) {
+ if (fh->vbi.streaming)
+ videobuf_streamoff(&fh->vbi);
+ if (fh->vbi.reading)
+ videobuf_read_stop(&fh->vbi);
+ res_free(dev,fh,RESOURCE_VBI);
+ }
+
+ /* free stuff */
+ videobuf_mmap_free(&fh->cap);
+ videobuf_mmap_free(&fh->vbi);
+ saa7134_pgtable_free(dev->pci,&fh->pt_cap);
+ saa7134_pgtable_free(dev->pci,&fh->pt_vbi);
+
+ v4l2_prio_close(&dev->prio,&fh->prio);
+ file->private_data = NULL;
+ kfree(fh);
+ return 0;
+}
+
+static int
+video_mmap(struct file *file, struct vm_area_struct * vma)
+{
+ struct saa7134_fh *fh = file->private_data;
+
+ return videobuf_mmap_mapper(saa7134_queue(fh), vma);
+}
+
+/* ------------------------------------------------------------------ */
+
+static void saa7134_vbi_fmt(struct saa7134_dev *dev, struct v4l2_format *f)
+{
+ struct saa7134_tvnorm *norm = dev->tvnorm;
+
+ f->fmt.vbi.sampling_rate = 6750000 * 4;
+ f->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */;
+ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ f->fmt.vbi.offset = 64 * 4;
+ f->fmt.vbi.start[0] = norm->vbi_v_start;
+ f->fmt.vbi.count[0] = norm->vbi_v_stop - norm->vbi_v_start +1;
+ f->fmt.vbi.start[1] = norm->video_v_stop + norm->vbi_v_start +1;
+ f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
+ f->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */
+
+#if 0
+ if (V4L2_STD_PAL == norm->id) {
+ /* FIXME */
+ f->fmt.vbi.start[0] += 3;
+ f->fmt.vbi.start[1] += 3*2;
+ }
+#endif
+}
+
+static int saa7134_g_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh,
+ struct v4l2_format *f)
+{
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ memset(&f->fmt.pix,0,sizeof(f->fmt.pix));
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->cap.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+ return 0;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ f->fmt.win = fh->win;
+ return 0;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ saa7134_vbi_fmt(dev,f);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int saa7134_try_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh,
+ struct v4l2_format *f)
+{
+ int err;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ {
+ struct saa7134_format *fmt;
+ enum v4l2_field field;
+ unsigned int maxw, maxh;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ maxw = min(dev->crop_current.width*4, dev->crop_bounds.width);
+ maxh = min(dev->crop_current.height*4, dev->crop_bounds.height);
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ f->fmt.pix.field = field;
+ if (f->fmt.pix.width < 48)
+ f->fmt.pix.width = 48;
+ if (f->fmt.pix.height < 32)
+ f->fmt.pix.height = 32;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+ f->fmt.pix.width &= ~0x03;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+ }
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ err = verify_preview(dev,&f->fmt.win);
+ if (0 != err)
+ return err;
+ return 0;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ saa7134_vbi_fmt(dev,f);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int saa7134_s_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh,
+ struct v4l2_format *f)
+{
+ unsigned long flags;
+ int err;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ err = saa7134_try_fmt(dev,fh,f);
+ if (0 != err)
+ return err;
+
+ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ fh->cap.field = f->fmt.pix.field;
+ return 0;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ err = verify_preview(dev,&f->fmt.win);
+ if (0 != err)
+ return err;
+
+ down(&dev->lock);
+ fh->win = f->fmt.win;
+ fh->nclips = f->fmt.win.clipcount;
+ if (fh->nclips > 8)
+ fh->nclips = 8;
+ if (copy_from_user(fh->clips,f->fmt.win.clips,
+ sizeof(struct v4l2_clip)*fh->nclips)) {
+ up(&dev->lock);
+ return -EFAULT;
+ }
+
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ spin_lock_irqsave(&dev->slock,flags);
+ stop_preview(dev,fh);
+ start_preview(dev,fh);
+ spin_unlock_irqrestore(&dev->slock,flags);
+ }
+ up(&dev->lock);
+ return 0;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ saa7134_vbi_fmt(dev,f);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+int saa7134_common_ioctl(struct saa7134_dev *dev,
+ unsigned int cmd, void *arg)
+{
+ int err;
+
+ switch (cmd) {
+ case VIDIOC_QUERYCTRL:
+ {
+ const struct v4l2_queryctrl *ctrl;
+ struct v4l2_queryctrl *c = arg;
+
+ if ((c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1) &&
+ (c->id < V4L2_CID_PRIVATE_BASE ||
+ c->id >= V4L2_CID_PRIVATE_LASTP1))
+ return -EINVAL;
+ ctrl = ctrl_by_id(c->id);
+ *c = (NULL != ctrl) ? *ctrl : no_ctrl;
+ return 0;
+ }
+ case VIDIOC_G_CTRL:
+ return get_control(dev,arg);
+ case VIDIOC_S_CTRL:
+ {
+ down(&dev->lock);
+ err = set_control(dev,NULL,arg);
+ up(&dev->lock);
+ return err;
+ }
+ /* --- input switching --------------------------------------- */
+ case VIDIOC_ENUMINPUT:
+ {
+ struct v4l2_input *i = arg;
+ unsigned int n;
+
+ n = i->index;
+ if (n >= SAA7134_INPUT_MAX)
+ return -EINVAL;
+ if (NULL == card_in(dev,i->index).name)
+ return -EINVAL;
+ memset(i,0,sizeof(*i));
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(i->name,card_in(dev,n).name);
+ if (card_in(dev,n).tv)
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->audioset = 1;
+ if (n == dev->ctl_input) {
+ int v1 = saa_readb(SAA7134_STATUS_VIDEO1);
+ int v2 = saa_readb(SAA7134_STATUS_VIDEO2);
+
+ if (0 != (v1 & 0x40))
+ i->status |= V4L2_IN_ST_NO_H_LOCK;
+ if (0 != (v2 & 0x40))
+ i->status |= V4L2_IN_ST_NO_SYNC;
+ if (0 != (v2 & 0x0e))
+ i->status |= V4L2_IN_ST_MACROVISION;
+ }
+ for (n = 0; n < TVNORMS; n++)
+ i->std |= tvnorms[n].id;
+ return 0;
+ }
+ case VIDIOC_G_INPUT:
+ {
+ int *i = arg;
+ *i = dev->ctl_input;
+ return 0;
+ }
+ case VIDIOC_S_INPUT:
+ {
+ int *i = arg;
+
+ if (*i < 0 || *i >= SAA7134_INPUT_MAX)
+ return -EINVAL;
+ if (NULL == card_in(dev,*i).name)
+ return -EINVAL;
+ down(&dev->lock);
+ video_mux(dev,*i);
+ up(&dev->lock);
+ return 0;
+ }
+
+ }
+ return 0;
+}
+EXPORT_SYMBOL(saa7134_common_ioctl);
+
+/*
+ * This function is _not_ called directly, but from
+ * video_generic_ioctl (and maybe others). userspace
+ * copying is done already, arg is a kernel pointer.
+ */
+static int video_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
+ unsigned long flags;
+ int err;
+
+ if (video_debug > 1)
+ saa7134_print_ioctl(dev->name,cmd);
+
+ switch (cmd) {
+ case VIDIOC_S_CTRL:
+ case VIDIOC_S_STD:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_S_FREQUENCY:
+ err = v4l2_prio_check(&dev->prio,&fh->prio);
+ if (0 != err)
+ return err;
+ }
+
+ switch (cmd) {
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = arg;
+
+ memset(cap,0,sizeof(*cap));
+ strcpy(cap->driver, "saa7134");
+ strlcpy(cap->card, saa7134_boards[dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
+ cap->version = SAA7134_VERSION_CODE;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VIDEO_OVERLAY |
+ V4L2_CAP_VBI_CAPTURE |
+ V4L2_CAP_TUNER |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ return 0;
+ }
+
+ /* --- tv standards ------------------------------------------ */
+ case VIDIOC_ENUMSTD:
+ {
+ struct v4l2_standard *e = arg;
+ unsigned int i;
+
+ i = e->index;
+ if (i >= TVNORMS)
+ return -EINVAL;
+ err = v4l2_video_std_construct(e, tvnorms[e->index].id,
+ tvnorms[e->index].name);
+ e->index = i;
+ if (err < 0)
+ return err;
+ return 0;
+ }
+ case VIDIOC_G_STD:
+ {
+ v4l2_std_id *id = arg;
+
+ *id = dev->tvnorm->id;
+ return 0;
+ }
+ case VIDIOC_S_STD:
+ {
+ v4l2_std_id *id = arg;
+ unsigned int i;
+
+ for (i = 0; i < TVNORMS; i++)
+ if (*id == tvnorms[i].id)
+ break;
+ if (i == TVNORMS)
+ for (i = 0; i < TVNORMS; i++)
+ if (*id & tvnorms[i].id)
+ break;
+ if (i == TVNORMS)
+ return -EINVAL;
+
+ down(&dev->lock);
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ spin_lock_irqsave(&dev->slock,flags);
+ stop_preview(dev,fh);
+ set_tvnorm(dev,&tvnorms[i]);
+ start_preview(dev,fh);
+ spin_unlock_irqrestore(&dev->slock,flags);
+ } else
+ set_tvnorm(dev,&tvnorms[i]);
+ saa7134_tvaudio_do_scan(dev);
+ up(&dev->lock);
+ return 0;
+ }
+
+ case VIDIOC_CROPCAP:
+ {
+ struct v4l2_cropcap *cap = arg;
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+ cap->bounds = dev->crop_bounds;
+ cap->defrect = dev->crop_defrect;
+ cap->pixelaspect.numerator = 1;
+ cap->pixelaspect.denominator = 1;
+ if (dev->tvnorm->id & V4L2_STD_525_60) {
+ cap->pixelaspect.numerator = 11;
+ cap->pixelaspect.denominator = 10;
+ }
+ if (dev->tvnorm->id & V4L2_STD_625_50) {
+ cap->pixelaspect.numerator = 54;
+ cap->pixelaspect.denominator = 59;
+ }
+ return 0;
+ }
+
+ case VIDIOC_G_CROP:
+ {
+ struct v4l2_crop * crop = arg;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+ crop->c = dev->crop_current;
+ return 0;
+ }
+ case VIDIOC_S_CROP:
+ {
+ struct v4l2_crop *crop = arg;
+ struct v4l2_rect *b = &dev->crop_bounds;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+ if (crop->c.height < 0)
+ return -EINVAL;
+ if (crop->c.width < 0)
+ return -EINVAL;
+
+ if (res_locked(fh->dev,RESOURCE_OVERLAY))
+ return -EBUSY;
+ if (res_locked(fh->dev,RESOURCE_VIDEO))
+ return -EBUSY;
+
+ if (crop->c.top < b->top)
+ crop->c.top = b->top;
+ if (crop->c.top > b->top + b->height)
+ crop->c.top = b->top + b->height;
+ if (crop->c.height > b->top - crop->c.top + b->height)
+ crop->c.height = b->top - crop->c.top + b->height;
+
+ if (crop->c.left < b->left)
+ crop->c.top = b->left;
+ if (crop->c.left > b->left + b->width)
+ crop->c.top = b->left + b->width;
+ if (crop->c.width > b->left - crop->c.left + b->width)
+ crop->c.width = b->left - crop->c.left + b->width;
+
+ dev->crop_current = crop->c;
+ return 0;
+ }
+
+ /* --- tuner ioctls ------------------------------------------ */
+ case VIDIOC_G_TUNER:
+ {
+ struct v4l2_tuner *t = arg;
+ int n;
+
+ if (0 != t->index)
+ return -EINVAL;
+ memset(t,0,sizeof(*t));
+ for (n = 0; n < SAA7134_INPUT_MAX; n++)
+ if (card_in(dev,n).tv)
+ break;
+ if (NULL != card_in(dev,n).name) {
+ strcpy(t->name, "Television");
+ t->capability = V4L2_TUNER_CAP_NORM |
+ V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2;
+ t->rangehigh = 0xffffffffUL;
+ t->rxsubchans = saa7134_tvaudio_getstereo(dev);
+ t->audmode = saa7134_tvaudio_rx2mode(t->rxsubchans);
+ }
+ if (0 != (saa_readb(SAA7134_STATUS_VIDEO1) & 0x03))
+ t->signal = 0xffff;
+ return 0;
+ }
+ case VIDIOC_S_TUNER:
+ {
+ struct v4l2_tuner *t = arg;
+ int rx,mode;
+
+ mode = dev->thread.mode;
+ if (UNSET == mode) {
+ rx = saa7134_tvaudio_getstereo(dev);
+ mode = saa7134_tvaudio_rx2mode(t->rxsubchans);
+ }
+ if (mode != t->audmode) {
+ dev->thread.mode = t->audmode;
+ }
+ return 0;
+ }
+ case VIDIOC_G_FREQUENCY:
+ {
+ struct v4l2_frequency *f = arg;
+
+ memset(f,0,sizeof(*f));
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->ctl_freq;
+ return 0;
+ }
+ case VIDIOC_S_FREQUENCY:
+ {
+ struct v4l2_frequency *f = arg;
+
+ if (0 != f->tuner)
+ return -EINVAL;
+ if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type)
+ return -EINVAL;
+ if (1 == fh->radio && V4L2_TUNER_RADIO != f->type)
+ return -EINVAL;
+ down(&dev->lock);
+ dev->ctl_freq = f->frequency;
+#ifdef V4L2_I2C_CLIENTS
+ saa7134_i2c_call_clients(dev,VIDIOC_S_FREQUENCY,f);
+#else
+ saa7134_i2c_call_clients(dev,VIDIOCSFREQ,&dev->ctl_freq);
+#endif
+ saa7134_tvaudio_do_scan(dev);
+ up(&dev->lock);
+ return 0;
+ }
+
+ /* --- control ioctls ---------------------------------------- */
+ case VIDIOC_ENUMINPUT:
+ case VIDIOC_G_INPUT:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_G_CTRL:
+ case VIDIOC_S_CTRL:
+ return saa7134_common_ioctl(dev, cmd, arg);
+
+ case VIDIOC_G_AUDIO:
+ {
+ struct v4l2_audio *a = arg;
+
+ memset(a,0,sizeof(*a));
+ strcpy(a->name,"audio");
+ return 0;
+ }
+ case VIDIOC_S_AUDIO:
+ return 0;
+ case VIDIOC_G_PARM:
+ {
+ struct v4l2_captureparm *parm = arg;
+ memset(parm,0,sizeof(*parm));
+ return 0;
+ }
+
+ case VIDIOC_G_PRIORITY:
+ {
+ enum v4l2_priority *p = arg;
+
+ *p = v4l2_prio_max(&dev->prio);
+ return 0;
+ }
+ case VIDIOC_S_PRIORITY:
+ {
+ enum v4l2_priority *prio = arg;
+
+ return v4l2_prio_change(&dev->prio, &fh->prio, *prio);
+ }
+
+ /* --- preview ioctls ---------------------------------------- */
+ case VIDIOC_ENUM_FMT:
+ {
+ struct v4l2_fmtdesc *f = arg;
+ enum v4l2_buf_type type;
+ unsigned int index;
+
+ index = f->index;
+ type = f->type;
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ if (index >= FORMATS)
+ return -EINVAL;
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OVERLAY &&
+ formats[index].planar)
+ return -EINVAL;
+ memset(f,0,sizeof(*f));
+ f->index = index;
+ f->type = type;
+ strlcpy(f->description,formats[index].name,sizeof(f->description));
+ f->pixelformat = formats[index].fourcc;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (0 != index)
+ return -EINVAL;
+ memset(f,0,sizeof(*f));
+ f->index = index;
+ f->type = type;
+ f->pixelformat = V4L2_PIX_FMT_GREY;
+ strcpy(f->description,"vbi data");
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+ }
+ case VIDIOC_G_FBUF:
+ {
+ struct v4l2_framebuffer *fb = arg;
+
+ *fb = dev->ovbuf;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ return 0;
+ }
+ case VIDIOC_S_FBUF:
+ {
+ struct v4l2_framebuffer *fb = arg;
+ struct saa7134_format *fmt;
+
+ if(!capable(CAP_SYS_ADMIN) &&
+ !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* check args */
+ fmt = format_by_fourcc(fb->fmt.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ /* ok, accept it */
+ dev->ovbuf = *fb;
+ dev->ovfmt = fmt;
+ if (0 == dev->ovbuf.fmt.bytesperline)
+ dev->ovbuf.fmt.bytesperline =
+ dev->ovbuf.fmt.width*fmt->depth/8;
+ return 0;
+ }
+ case VIDIOC_OVERLAY:
+ {
+ int *on = arg;
+
+ if (*on) {
+ if (!res_get(dev,fh,RESOURCE_OVERLAY))
+ return -EBUSY;
+ spin_lock_irqsave(&dev->slock,flags);
+ start_preview(dev,fh);
+ spin_unlock_irqrestore(&dev->slock,flags);
+ }
+ if (!*on) {
+ if (!res_check(fh, RESOURCE_OVERLAY))
+ return -EINVAL;
+ spin_lock_irqsave(&dev->slock,flags);
+ stop_preview(dev,fh);
+ spin_unlock_irqrestore(&dev->slock,flags);
+ res_free(dev,fh,RESOURCE_OVERLAY);
+ }
+ return 0;
+ }
+
+ /* --- capture ioctls ---------------------------------------- */
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format *f = arg;
+ return saa7134_g_fmt(dev,fh,f);
+ }
+ case VIDIOC_S_FMT:
+ {
+ struct v4l2_format *f = arg;
+ return saa7134_s_fmt(dev,fh,f);
+ }
+ case VIDIOC_TRY_FMT:
+ {
+ struct v4l2_format *f = arg;
+ return saa7134_try_fmt(dev,fh,f);
+ }
+
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf *mbuf = arg;
+ struct videobuf_queue *q;
+ struct v4l2_requestbuffers req;
+ unsigned int i;
+
+ q = saa7134_queue(fh);
+ memset(&req,0,sizeof(req));
+ req.type = q->type;
+ req.count = gbuffers;
+ req.memory = V4L2_MEMORY_MMAP;
+ err = videobuf_reqbufs(q,&req);
+ if (err < 0)
+ return err;
+ memset(mbuf,0,sizeof(*mbuf));
+ mbuf->frames = req.count;
+ mbuf->size = 0;
+ for (i = 0; i < mbuf->frames; i++) {
+ mbuf->offsets[i] = q->bufs[i]->boff;
+ mbuf->size += q->bufs[i]->bsize;
+ }
+ return 0;
+ }
+ case VIDIOC_REQBUFS:
+ return videobuf_reqbufs(saa7134_queue(fh),arg);
+
+ case VIDIOC_QUERYBUF:
+ return videobuf_querybuf(saa7134_queue(fh),arg);
+
+ case VIDIOC_QBUF:
+ return videobuf_qbuf(saa7134_queue(fh),arg);
+
+ case VIDIOC_DQBUF:
+ return videobuf_dqbuf(saa7134_queue(fh),arg,
+ file->f_flags & O_NONBLOCK);
+
+ case VIDIOC_STREAMON:
+ {
+ int res = saa7134_resource(fh);
+
+ if (!res_get(dev,fh,res))
+ return -EBUSY;
+ return videobuf_streamon(saa7134_queue(fh));
+ }
+ case VIDIOC_STREAMOFF:
+ {
+ int res = saa7134_resource(fh);
+
+ err = videobuf_streamoff(saa7134_queue(fh));
+ if (err < 0)
+ return err;
+ res_free(dev,fh,res);
+ return 0;
+ }
+
+ default:
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ video_do_ioctl);
+ }
+ return 0;
+}
+
+static int video_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, video_do_ioctl);
+}
+
+static int radio_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
+
+ if (video_debug > 1)
+ saa7134_print_ioctl(dev->name,cmd);
+ switch (cmd) {
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = arg;
+
+ memset(cap,0,sizeof(*cap));
+ strcpy(cap->driver, "saa7134");
+ strlcpy(cap->card, saa7134_boards[dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
+ cap->version = SAA7134_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_TUNER;
+ return 0;
+ }
+ case VIDIOC_G_TUNER:
+ {
+ struct v4l2_tuner *t = arg;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ memset(t,0,sizeof(*t));
+ strcpy(t->name, "Radio");
+ t->rangelow = (int)(65*16);
+ t->rangehigh = (int)(108*16);
+
+#ifdef V4L2_I2C_CLIENTS
+ saa7134_i2c_call_clients(dev,VIDIOC_G_TUNER,t);
+#else
+ {
+ struct video_tuner vt;
+ memset(&vt,0,sizeof(vt));
+ saa7134_i2c_call_clients(dev,VIDIOCGTUNER,&vt);
+ t->signal = vt.signal;
+ }
+#endif
+ return 0;
+ }
+ case VIDIOC_ENUMINPUT:
+ {
+ struct v4l2_input *i = arg;
+
+ if (i->index != 0)
+ return -EINVAL;
+ strcpy(i->name,"Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ return 0;
+ }
+ case VIDIOC_G_INPUT:
+ {
+ int *i = arg;
+ *i = 0;
+ return 0;
+ }
+ case VIDIOC_G_AUDIO:
+ {
+ struct v4l2_audio *a = arg;
+
+ memset(a,0,sizeof(*a));
+ strcpy(a->name,"Radio");
+ return 0;
+ }
+ case VIDIOC_G_STD:
+ {
+ v4l2_std_id *id = arg;
+ *id = 0;
+ return 0;
+ }
+ case VIDIOC_S_AUDIO:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_STD:
+ return 0;
+
+ case VIDIOC_QUERYCTRL:
+ {
+ const struct v4l2_queryctrl *ctrl;
+ struct v4l2_queryctrl *c = arg;
+
+ if (c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ if (c->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl = ctrl_by_id(c->id);
+ *c = *ctrl;
+ } else
+ *c = no_ctrl;
+ return 0;
+ }
+
+ case VIDIOC_G_CTRL:
+ case VIDIOC_S_CTRL:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY:
+ return video_do_ioctl(inode,file,cmd,arg);
+
+ default:
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ radio_do_ioctl);
+ }
+ return 0;
+}
+
+static int radio_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, radio_do_ioctl);
+}
+
+static struct file_operations video_fops =
+{
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .read = video_read,
+ .poll = video_poll,
+ .mmap = video_mmap,
+ .ioctl = video_ioctl,
+ .llseek = no_llseek,
+};
+
+static struct file_operations radio_fops =
+{
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .ioctl = radio_ioctl,
+ .llseek = no_llseek,
+};
+
+/* ----------------------------------------------------------- */
+/* exported stuff */
+
+struct video_device saa7134_video_template =
+{
+ .name = "saa7134-video",
+ .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_OVERLAY|
+ VID_TYPE_CLIPPING|VID_TYPE_SCALES,
+ .hardware = 0,
+ .fops = &video_fops,
+ .minor = -1,
+};
+
+struct video_device saa7134_vbi_template =
+{
+ .name = "saa7134-vbi",
+ .type = VID_TYPE_TUNER|VID_TYPE_TELETEXT,
+ .hardware = 0,
+ .fops = &video_fops,
+ .minor = -1,
+};
+
+struct video_device saa7134_radio_template =
+{
+ .name = "saa7134-radio",
+ .type = VID_TYPE_TUNER,
+ .hardware = 0,
+ .fops = &radio_fops,
+ .minor = -1,
+};
+
+int saa7134_video_init1(struct saa7134_dev *dev)
+{
+ /* sanitycheck insmod options */
+ if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
+ gbuffers = 2;
+ if (gbufsize < 0 || gbufsize > gbufsize_max)
+ gbufsize = gbufsize_max;
+ gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
+
+ /* put some sensible defaults into the data structures ... */
+ dev->ctl_bright = ctrl_by_id(V4L2_CID_BRIGHTNESS)->default_value;
+ dev->ctl_contrast = ctrl_by_id(V4L2_CID_CONTRAST)->default_value;
+ dev->ctl_hue = ctrl_by_id(V4L2_CID_HUE)->default_value;
+ dev->ctl_saturation = ctrl_by_id(V4L2_CID_SATURATION)->default_value;
+ dev->ctl_volume = ctrl_by_id(V4L2_CID_AUDIO_VOLUME)->default_value;
+ dev->ctl_mute = 1; // ctrl_by_id(V4L2_CID_AUDIO_MUTE)->default_value;
+ dev->ctl_invert = ctrl_by_id(V4L2_CID_PRIVATE_INVERT)->default_value;
+ dev->ctl_automute = ctrl_by_id(V4L2_CID_PRIVATE_AUTOMUTE)->default_value;
+
+ if (dev->tda9887_conf && dev->ctl_automute)
+ dev->tda9887_conf |= TDA9887_AUTOMUTE;
+ dev->automute = 0;
+
+ INIT_LIST_HEAD(&dev->video_q.queue);
+ init_timer(&dev->video_q.timeout);
+ dev->video_q.timeout.function = saa7134_buffer_timeout;
+ dev->video_q.timeout.data = (unsigned long)(&dev->video_q);
+ dev->video_q.dev = dev;
+
+ if (saa7134_boards[dev->board].video_out) {
+ /* enable video output */
+ int vo = saa7134_boards[dev->board].video_out;
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL0, video_out[vo][0]);
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL1, video_out[vo][1]);
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL2, video_out[vo][2]);
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL3, video_out[vo][3]);
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL4, video_out[vo][4]);
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL5, video_out[vo][5]);
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL6, video_out[vo][6]);
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL7, video_out[vo][7]);
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL8, video_out[vo][8]);
+ }
+
+ return 0;
+}
+
+int saa7134_video_init2(struct saa7134_dev *dev)
+{
+ /* init video hw */
+ set_tvnorm(dev,&tvnorms[0]);
+ video_mux(dev,0);
+ saa7134_tvaudio_setmute(dev);
+ saa7134_tvaudio_setvolume(dev,dev->ctl_volume);
+ return 0;
+}
+
+int saa7134_video_fini(struct saa7134_dev *dev)
+{
+ /* nothing */
+ return 0;
+}
+
+void saa7134_irq_video_intl(struct saa7134_dev *dev)
+{
+ static const char *st[] = {
+ "(no signal)", "NTSC", "PAL", "SECAM" };
+ u32 st1,st2;
+
+ st1 = saa_readb(SAA7134_STATUS_VIDEO1);
+ st2 = saa_readb(SAA7134_STATUS_VIDEO2);
+ dprintk("DCSDT: pll: %s, sync: %s, norm: %s\n",
+ (st1 & 0x40) ? "not locked" : "locked",
+ (st2 & 0x40) ? "no" : "yes",
+ st[st1 & 0x03]);
+ dev->nosignal = (st1 & 0x40) || (st2 & 0x40);
+
+ if (dev->nosignal) {
+ /* no video signal -> mute audio */
+ if (dev->ctl_automute)
+ dev->automute = 1;
+ saa7134_tvaudio_setmute(dev);
+ saa_setb(SAA7134_SYNC_CTRL, 0x20);
+ } else {
+ /* wake up tvaudio audio carrier scan thread */
+ saa7134_tvaudio_do_scan(dev);
+ if (!noninterlaced)
+ saa_clearb(SAA7134_SYNC_CTRL, 0x20);
+ }
+ if (dev->mops && dev->mops->signal_change)
+ dev->mops->signal_change(dev);
+}
+
+void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status)
+{
+ enum v4l2_field field;
+
+ spin_lock(&dev->slock);
+ if (dev->video_q.curr) {
+ dev->video_fieldcount++;
+ field = dev->video_q.curr->vb.field;
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ /* make sure we have seen both fields */
+ if ((status & 0x10) == 0x00) {
+ dev->video_q.curr->top_seen = 1;
+ goto done;
+ }
+ if (!dev->video_q.curr->top_seen)
+ goto done;
+ } else if (field == V4L2_FIELD_TOP) {
+ if ((status & 0x10) != 0x10)
+ goto done;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ if ((status & 0x10) != 0x00)
+ goto done;
+ }
+ dev->video_q.curr->vb.field_count = dev->video_fieldcount;
+ saa7134_buffer_finish(dev,&dev->video_q,STATE_DONE);
+ }
+ saa7134_buffer_next(dev,&dev->video_q);
+
+ done:
+ spin_unlock(&dev->slock);
+}
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
new file mode 100644
index 00000000000..ac90a985323
--- /dev/null
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -0,0 +1,618 @@
+/*
+ * $Id: saa7134.h,v 1.38 2005/03/07 12:01:51 kraxel Exp $
+ *
+ * v4l2 device driver for philips saa7134 based TV cards
+ *
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/version.h>
+#define SAA7134_VERSION_CODE KERNEL_VERSION(0,2,12)
+
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/videodev.h>
+#include <linux/kdev_t.h>
+#include <linux/input.h>
+
+#include <asm/io.h>
+
+#include <media/tuner.h>
+#include <media/audiochip.h>
+#include <media/id.h>
+#include <media/ir-common.h>
+#include <media/video-buf.h>
+#include <media/video-buf-dvb.h>
+
+#ifndef TRUE
+# define TRUE (1==1)
+#endif
+#ifndef FALSE
+# define FALSE (1==0)
+#endif
+#define UNSET (-1U)
+
+/* 2.4 / 2.5 driver compatibility stuff */
+
+/* ----------------------------------------------------------- */
+/* enums */
+
+enum saa7134_tvaudio_mode {
+ TVAUDIO_FM_MONO = 1,
+ TVAUDIO_FM_BG_STEREO = 2,
+ TVAUDIO_FM_SAT_STEREO = 3,
+ TVAUDIO_FM_K_STEREO = 4,
+ TVAUDIO_NICAM_AM = 5,
+ TVAUDIO_NICAM_FM = 6,
+};
+
+enum saa7134_audio_in {
+ TV = 1,
+ LINE1 = 2,
+ LINE2 = 3,
+ LINE2_LEFT,
+};
+
+enum saa7134_video_out {
+ CCIR656 = 1,
+};
+
+/* ----------------------------------------------------------- */
+/* static data */
+
+struct saa7134_tvnorm {
+ char *name;
+ v4l2_std_id id;
+
+ /* video decoder */
+ unsigned int sync_control;
+ unsigned int luma_control;
+ unsigned int chroma_ctrl1;
+ unsigned int chroma_gain;
+ unsigned int chroma_ctrl2;
+ unsigned int vgate_misc;
+
+ /* video scaler */
+ unsigned int h_start;
+ unsigned int h_stop;
+ unsigned int video_v_start;
+ unsigned int video_v_stop;
+ unsigned int vbi_v_start;
+ unsigned int vbi_v_stop;
+ unsigned int src_timing;
+};
+
+struct saa7134_tvaudio {
+ char *name;
+ v4l2_std_id std;
+ enum saa7134_tvaudio_mode mode;
+ int carr1;
+ int carr2;
+};
+
+struct saa7134_format {
+ char *name;
+ unsigned int fourcc;
+ unsigned int depth;
+ unsigned int pm;
+ unsigned int vshift; /* vertical downsampling (for planar yuv) */
+ unsigned int hshift; /* horizontal downsampling (for planar yuv) */
+ unsigned int bswap:1;
+ unsigned int wswap:1;
+ unsigned int yuv:1;
+ unsigned int planar:1;
+ unsigned int uvswap:1;
+};
+
+/* ----------------------------------------------------------- */
+/* card configuration */
+
+#define SAA7134_BOARD_NOAUTO UNSET
+#define SAA7134_BOARD_UNKNOWN 0
+#define SAA7134_BOARD_PROTEUS_PRO 1
+#define SAA7134_BOARD_FLYVIDEO3000 2
+#define SAA7134_BOARD_FLYVIDEO2000 3
+#define SAA7134_BOARD_EMPRESS 4
+#define SAA7134_BOARD_MONSTERTV 5
+#define SAA7134_BOARD_MD9717 6
+#define SAA7134_BOARD_TVSTATION_RDS 7
+#define SAA7134_BOARD_CINERGY400 8
+#define SAA7134_BOARD_MD5044 9
+#define SAA7134_BOARD_KWORLD 10
+#define SAA7134_BOARD_CINERGY600 11
+#define SAA7134_BOARD_MD7134 12
+#define SAA7134_BOARD_TYPHOON_90031 13
+#define SAA7134_BOARD_ELSA 14
+#define SAA7134_BOARD_ELSA_500TV 15
+#define SAA7134_BOARD_ASUSTeK_TVFM7134 16
+#define SAA7134_BOARD_VA1000POWER 17
+#define SAA7134_BOARD_BMK_MPEX_NOTUNER 18
+#define SAA7134_BOARD_VIDEOMATE_TV 19
+#define SAA7134_BOARD_CRONOS_PLUS 20
+#define SAA7134_BOARD_10MOONSTVMASTER 21
+#define SAA7134_BOARD_MD2819 22
+#define SAA7134_BOARD_BMK_MPEX_TUNER 23
+#define SAA7134_BOARD_TVSTATION_DVR 24
+#define SAA7134_BOARD_ASUSTEK_TVFM7133 25
+#define SAA7134_BOARD_PINNACLE_PCTV_STEREO 26
+#define SAA7134_BOARD_MANLI_MTV002 27
+#define SAA7134_BOARD_MANLI_MTV001 28
+#define SAA7134_BOARD_TG3000TV 29
+#define SAA7134_BOARD_ECS_TVP3XP 30
+#define SAA7134_BOARD_ECS_TVP3XP_4CB5 31
+#define SAA7134_BOARD_AVACSSMARTTV 32
+#define SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER 33
+#define SAA7134_BOARD_NOVAC_PRIMETV7133 34
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_305 35
+#define SAA7133_BOARD_UPMOST_PURPLE_TV 36
+#define SAA7134_BOARD_ITEMS_MTV005 37
+#define SAA7134_BOARD_CINERGY200 38
+#define SAA7134_BOARD_FLYTVPLATINUM_MINI 39
+#define SAA7134_BOARD_VIDEOMATE_TV_PVR 40
+#define SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS 41
+#define SAA7134_BOARD_SABRENT_SBTTVFM 42
+#define SAA7134_BOARD_ZOLID_XPERT_TV7134 43
+#define SAA7134_BOARD_EMPIRE_PCI_TV_RADIO_LE 44
+#define SAA7134_BOARD_AVERMEDIA_307 45
+#define SAA7134_BOARD_AVERMEDIA_CARDBUS 46
+#define SAA7134_BOARD_CINERGY400_CARDBUS 47
+#define SAA7134_BOARD_CINERGY600_MK3 48
+#define SAA7134_BOARD_VIDEOMATE_GOLD_PLUS 49
+#define SAA7134_BOARD_PINNACLE_300I_DVBT_PAL 50
+#define SAA7134_BOARD_PROVIDEO_PV952 51
+#define SAA7134_BOARD_AVERMEDIA_305 52
+#define SAA7135_BOARD_ASUSTeK_TVFM7135 53
+#define SAA7134_BOARD_FLYTVPLATINUM_FM 54
+#define SAA7134_BOARD_FLYDVBTDUO 55
+
+#define SAA7134_MAXBOARDS 8
+#define SAA7134_INPUT_MAX 8
+
+struct saa7134_input {
+ char *name;
+ unsigned int vmux;
+ enum saa7134_audio_in amux;
+ unsigned int gpio;
+ unsigned int tv:1;
+};
+
+enum saa7134_mpeg_type {
+ SAA7134_MPEG_UNUSED,
+ SAA7134_MPEG_EMPRESS,
+ SAA7134_MPEG_DVB,
+};
+
+struct saa7134_board {
+ char *name;
+ unsigned int audio_clock;
+
+ /* input switching */
+ unsigned int gpiomask;
+ struct saa7134_input inputs[SAA7134_INPUT_MAX];
+ struct saa7134_input radio;
+ struct saa7134_input mute;
+
+ /* i2c chip info */
+ unsigned int tuner_type;
+ unsigned int tda9887_conf;
+
+ /* peripheral I/O */
+ enum saa7134_video_out video_out;
+ enum saa7134_mpeg_type mpeg;
+};
+
+#define card_has_radio(dev) (NULL != saa7134_boards[dev->board].radio.name)
+#define card_is_empress(dev) (SAA7134_MPEG_EMPRESS == saa7134_boards[dev->board].mpeg)
+#define card_is_dvb(dev) (SAA7134_MPEG_DVB == saa7134_boards[dev->board].mpeg)
+#define card_has_mpeg(dev) (SAA7134_MPEG_UNUSED != saa7134_boards[dev->board].mpeg)
+#define card(dev) (saa7134_boards[dev->board])
+#define card_in(dev,n) (saa7134_boards[dev->board].inputs[n])
+
+/* ----------------------------------------------------------- */
+/* device / file handle status */
+
+#define RESOURCE_OVERLAY 1
+#define RESOURCE_VIDEO 2
+#define RESOURCE_VBI 4
+
+#define INTERLACE_AUTO 0
+#define INTERLACE_ON 1
+#define INTERLACE_OFF 2
+
+#define BUFFER_TIMEOUT (HZ/2) /* 0.5 seconds */
+
+struct saa7134_dev;
+struct saa7134_dma;
+
+/* saa7134 page table */
+struct saa7134_pgtable {
+ unsigned int size;
+ u32 *cpu;
+ dma_addr_t dma;
+};
+
+/* tvaudio thread status */
+struct saa7134_thread {
+ pid_t pid;
+ struct completion exit;
+ wait_queue_head_t wq;
+ unsigned int shutdown;
+ unsigned int scan1;
+ unsigned int scan2;
+ unsigned int mode;
+};
+
+/* buffer for one video/vbi/ts frame */
+struct saa7134_buf {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ /* saa7134 specific */
+ struct saa7134_format *fmt;
+ unsigned int top_seen;
+ int (*activate)(struct saa7134_dev *dev,
+ struct saa7134_buf *buf,
+ struct saa7134_buf *next);
+
+ /* page tables */
+ struct saa7134_pgtable *pt;
+};
+
+struct saa7134_dmaqueue {
+ struct saa7134_dev *dev;
+ struct saa7134_buf *curr;
+ struct list_head queue;
+ struct timer_list timeout;
+ unsigned int need_two;
+};
+
+/* video filehandle status */
+struct saa7134_fh {
+ struct saa7134_dev *dev;
+ unsigned int radio;
+ enum v4l2_buf_type type;
+ unsigned int resources;
+#ifdef VIDIOC_G_PRIORITY
+ enum v4l2_priority prio;
+#endif
+
+ /* video overlay */
+ struct v4l2_window win;
+ struct v4l2_clip clips[8];
+ unsigned int nclips;
+
+ /* video capture */
+ struct saa7134_format *fmt;
+ unsigned int width,height;
+ struct videobuf_queue cap;
+ struct saa7134_pgtable pt_cap;
+
+ /* vbi capture */
+ struct videobuf_queue vbi;
+ struct saa7134_pgtable pt_vbi;
+};
+
+/* oss dsp status */
+struct saa7134_oss {
+ struct semaphore lock;
+ int minor_mixer;
+ int minor_dsp;
+ unsigned int users_dsp;
+
+ /* mixer */
+ enum saa7134_audio_in input;
+ unsigned int count;
+ unsigned int line1;
+ unsigned int line2;
+
+ /* dsp */
+ unsigned int afmt;
+ unsigned int rate;
+ unsigned int channels;
+ unsigned int recording_on;
+ unsigned int dma_running;
+ unsigned int blocks;
+ unsigned int blksize;
+ unsigned int bufsize;
+ struct saa7134_pgtable pt;
+ struct videobuf_dmabuf dma;
+ wait_queue_head_t wq;
+ unsigned int dma_blk;
+ unsigned int read_offset;
+ unsigned int read_count;
+};
+
+/* IR input */
+struct saa7134_ir {
+ struct input_dev dev;
+ struct ir_input_state ir;
+ char name[32];
+ char phys[32];
+ u32 mask_keycode;
+ u32 mask_keydown;
+ u32 mask_keyup;
+ int polling;
+ u32 last_gpio;
+ struct timer_list timer;
+};
+
+/* ts/mpeg status */
+struct saa7134_ts {
+ /* TS capture */
+ struct saa7134_pgtable pt_ts;
+ int nr_packets;
+ int nr_bufs;
+};
+
+/* ts/mpeg ops */
+struct saa7134_mpeg_ops {
+ enum saa7134_mpeg_type type;
+ struct list_head next;
+ int (*init)(struct saa7134_dev *dev);
+ int (*fini)(struct saa7134_dev *dev);
+ void (*signal_change)(struct saa7134_dev *dev);
+};
+
+/* global device status */
+struct saa7134_dev {
+ struct list_head devlist;
+ struct semaphore lock;
+ spinlock_t slock;
+#ifdef VIDIOC_G_PRIORITY
+ struct v4l2_prio_state prio;
+#endif
+
+ /* various device info */
+ unsigned int resources;
+ struct video_device *video_dev;
+ struct video_device *radio_dev;
+ struct video_device *vbi_dev;
+ struct saa7134_oss oss;
+
+ /* infrared remote */
+ int has_remote;
+ struct saa7134_ir *remote;
+
+ /* pci i/o */
+ char name[32];
+ int nr;
+ struct pci_dev *pci;
+ unsigned char pci_rev,pci_lat;
+ __u32 __iomem *lmmio;
+ __u8 __iomem *bmmio;
+
+ /* config info */
+ unsigned int board;
+ unsigned int tuner_type;
+ unsigned int tda9887_conf;
+ unsigned int gpio_value;
+ unsigned int irq2_mask;
+
+ /* i2c i/o */
+ struct i2c_adapter i2c_adap;
+ struct i2c_client i2c_client;
+ unsigned char eedata[64];
+
+ /* video overlay */
+ struct v4l2_framebuffer ovbuf;
+ struct saa7134_format *ovfmt;
+ unsigned int ovenable;
+ enum v4l2_field ovfield;
+
+ /* video+ts+vbi capture */
+ struct saa7134_dmaqueue video_q;
+ struct saa7134_dmaqueue vbi_q;
+ unsigned int video_fieldcount;
+ unsigned int vbi_fieldcount;
+
+ /* various v4l controls */
+ struct saa7134_tvnorm *tvnorm; /* video */
+ struct saa7134_tvaudio *tvaudio;
+ unsigned int ctl_input;
+ int ctl_bright;
+ int ctl_contrast;
+ int ctl_hue;
+ int ctl_saturation;
+ int ctl_freq;
+ int ctl_mute; /* audio */
+ int ctl_volume;
+ int ctl_invert; /* private */
+ int ctl_mirror;
+ int ctl_y_odd;
+ int ctl_y_even;
+ int ctl_automute;
+
+ /* crop */
+ struct v4l2_rect crop_bounds;
+ struct v4l2_rect crop_defrect;
+ struct v4l2_rect crop_current;
+
+ /* other global state info */
+ unsigned int automute;
+ struct saa7134_thread thread;
+ struct saa7134_input *input;
+ struct saa7134_input *hw_input;
+ unsigned int hw_mute;
+ int last_carrier;
+ int nosignal;
+
+ /* SAA7134_MPEG_* */
+ struct saa7134_ts ts;
+ struct saa7134_dmaqueue ts_q;
+ struct saa7134_mpeg_ops *mops;
+
+ /* SAA7134_MPEG_EMPRESS only */
+ struct video_device *empress_dev;
+ struct videobuf_queue empress_tsq;
+ unsigned int empress_users;
+ struct work_struct empress_workqueue;
+ int empress_started;
+
+ /* SAA7134_MPEG_DVB only */
+ struct videobuf_dvb dvb;
+};
+
+/* ----------------------------------------------------------- */
+
+#define saa_readl(reg) readl(dev->lmmio + (reg))
+#define saa_writel(reg,value) writel((value), dev->lmmio + (reg));
+#define saa_andorl(reg,mask,value) \
+ writel((readl(dev->lmmio+(reg)) & ~(mask)) |\
+ ((value) & (mask)), dev->lmmio+(reg))
+#define saa_setl(reg,bit) saa_andorl((reg),(bit),(bit))
+#define saa_clearl(reg,bit) saa_andorl((reg),(bit),0)
+
+#define saa_readb(reg) readb(dev->bmmio + (reg))
+#define saa_writeb(reg,value) writeb((value), dev->bmmio + (reg));
+#define saa_andorb(reg,mask,value) \
+ writeb((readb(dev->bmmio+(reg)) & ~(mask)) |\
+ ((value) & (mask)), dev->bmmio+(reg))
+#define saa_setb(reg,bit) saa_andorb((reg),(bit),(bit))
+#define saa_clearb(reg,bit) saa_andorb((reg),(bit),0)
+
+#define saa_wait(us) { udelay(us); }
+
+/* ----------------------------------------------------------- */
+/* saa7134-core.c */
+
+extern struct list_head saa7134_devlist;
+
+void saa7134_print_ioctl(char *name, unsigned int cmd);
+void saa7134_track_gpio(struct saa7134_dev *dev, char *msg);
+
+#define SAA7134_PGTABLE_SIZE 4096
+
+int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt);
+int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt,
+ struct scatterlist *list, unsigned int length,
+ unsigned int startpage);
+void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt);
+
+int saa7134_buffer_count(unsigned int size, unsigned int count);
+int saa7134_buffer_startpage(struct saa7134_buf *buf);
+unsigned long saa7134_buffer_base(struct saa7134_buf *buf);
+
+int saa7134_buffer_queue(struct saa7134_dev *dev, struct saa7134_dmaqueue *q,
+ struct saa7134_buf *buf);
+void saa7134_buffer_finish(struct saa7134_dev *dev, struct saa7134_dmaqueue *q,
+ unsigned int state);
+void saa7134_buffer_next(struct saa7134_dev *dev, struct saa7134_dmaqueue *q);
+void saa7134_buffer_timeout(unsigned long data);
+void saa7134_dma_free(struct saa7134_dev *dev,struct saa7134_buf *buf);
+
+int saa7134_set_dmabits(struct saa7134_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* saa7134-cards.c */
+
+extern struct saa7134_board saa7134_boards[];
+extern const unsigned int saa7134_bcount;
+extern struct pci_device_id __devinitdata saa7134_pci_tbl[];
+
+extern int saa7134_board_init1(struct saa7134_dev *dev);
+extern int saa7134_board_init2(struct saa7134_dev *dev);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-i2c.c */
+
+int saa7134_i2c_register(struct saa7134_dev *dev);
+int saa7134_i2c_unregister(struct saa7134_dev *dev);
+void saa7134_i2c_call_clients(struct saa7134_dev *dev,
+ unsigned int cmd, void *arg);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-video.c */
+
+extern struct video_device saa7134_video_template;
+extern struct video_device saa7134_radio_template;
+
+int saa7134_common_ioctl(struct saa7134_dev *dev,
+ unsigned int cmd, void *arg);
+
+int saa7134_video_init1(struct saa7134_dev *dev);
+int saa7134_video_init2(struct saa7134_dev *dev);
+int saa7134_video_fini(struct saa7134_dev *dev);
+void saa7134_irq_video_intl(struct saa7134_dev *dev);
+void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-ts.c */
+
+#define TS_PACKET_SIZE 188 /* TS packets 188 bytes */
+
+extern struct videobuf_queue_ops saa7134_ts_qops;
+
+int saa7134_ts_init1(struct saa7134_dev *dev);
+int saa7134_ts_fini(struct saa7134_dev *dev);
+void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status);
+
+int saa7134_ts_register(struct saa7134_mpeg_ops *ops);
+void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops);
+
+/* ----------------------------------------------------------- */
+/* saa7134-vbi.c */
+
+extern struct videobuf_queue_ops saa7134_vbi_qops;
+extern struct video_device saa7134_vbi_template;
+
+int saa7134_vbi_init1(struct saa7134_dev *dev);
+int saa7134_vbi_fini(struct saa7134_dev *dev);
+void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-tvaudio.c */
+
+int saa7134_tvaudio_rx2mode(u32 rx);
+
+void saa7134_tvaudio_setmute(struct saa7134_dev *dev);
+void saa7134_tvaudio_setinput(struct saa7134_dev *dev,
+ struct saa7134_input *in);
+void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level);
+int saa7134_tvaudio_getstereo(struct saa7134_dev *dev);
+
+int saa7134_tvaudio_init2(struct saa7134_dev *dev);
+int saa7134_tvaudio_fini(struct saa7134_dev *dev);
+int saa7134_tvaudio_do_scan(struct saa7134_dev *dev);
+
+int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value);
+
+/* ----------------------------------------------------------- */
+/* saa7134-oss.c */
+
+extern struct file_operations saa7134_dsp_fops;
+extern struct file_operations saa7134_mixer_fops;
+
+int saa7134_oss_init1(struct saa7134_dev *dev);
+int saa7134_oss_fini(struct saa7134_dev *dev);
+void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status);
+
+/* ----------------------------------------------------------- */
+/* saa7134-input.c */
+
+int saa7134_input_init1(struct saa7134_dev *dev);
+void saa7134_input_fini(struct saa7134_dev *dev);
+void saa7134_input_irq(struct saa7134_dev *dev);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */