summaryrefslogtreecommitdiff
path: root/linux/drivers/media/video
diff options
context:
space:
mode:
authorMichael Hunold <hunold@linuxtv.org>2006-03-08 22:28:04 +0100
committerMichael Hunold <hunold@linuxtv.org>2006-03-08 22:28:04 +0100
commitfb1c40c7fb9da899745ca1eab318fe9009f886d1 (patch)
tree6a1e9252ae81b6dd8a5d831d95d6108124eb92c9 /linux/drivers/media/video
parentf780713336a2db00609e7bb63648fbd96c5a856d (diff)
downloadmediapointer-dvb-s2-fb1c40c7fb9da899745ca1eab318fe9009f886d1.tar.gz
mediapointer-dvb-s2-fb1c40c7fb9da899745ca1eab318fe9009f886d1.tar.bz2
Add mxb, dpc7146, hexium_gemini and hexium_orion from 2.6.15
From: Michael Hunold <hunold@linuxtv.org> Signed-off-by: Michael Hunold <hunold@linuxtv.org>
Diffstat (limited to 'linux/drivers/media/video')
-rw-r--r--linux/drivers/media/video/dpc7146.c401
-rw-r--r--linux/drivers/media/video/hexium_gemini.c556
-rw-r--r--linux/drivers/media/video/hexium_orion.c522
-rw-r--r--linux/drivers/media/video/mxb.c1028
-rw-r--r--linux/drivers/media/video/mxb.h42
-rw-r--r--linux/drivers/media/video/tda9840.c253
-rw-r--r--linux/drivers/media/video/tda9840.h35
-rw-r--r--linux/drivers/media/video/tea6415c.c222
-rw-r--r--linux/drivers/media/video/tea6415c.h39
-rw-r--r--linux/drivers/media/video/tea6420.c199
-rw-r--r--linux/drivers/media/video/tea6420.h17
11 files changed, 3314 insertions, 0 deletions
diff --git a/linux/drivers/media/video/dpc7146.c b/linux/drivers/media/video/dpc7146.c
new file mode 100644
index 000000000..a14b51f0e
--- /dev/null
+++ b/linux/drivers/media/video/dpc7146.c
@@ -0,0 +1,401 @@
+/*
+ dpc7146.c - v4l2 driver for the dpc7146 demonstration board
+
+ Copyright (C) 2000-2003 Michael Hunold <michael@mihu.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define DEBUG_VARIABLE debug
+
+#include <media/saa7146_vv.h>
+#include <linux/video_decoder.h> /* for saa7111a */
+
+#define I2C_SAA7111A 0x24
+
+/* All unused bytes are reserverd. */
+#define SAA711X_CHIP_VERSION 0x00
+#define SAA711X_ANALOG_INPUT_CONTROL_1 0x02
+#define SAA711X_ANALOG_INPUT_CONTROL_2 0x03
+#define SAA711X_ANALOG_INPUT_CONTROL_3 0x04
+#define SAA711X_ANALOG_INPUT_CONTROL_4 0x05
+#define SAA711X_HORIZONTAL_SYNC_START 0x06
+#define SAA711X_HORIZONTAL_SYNC_STOP 0x07
+#define SAA711X_SYNC_CONTROL 0x08
+#define SAA711X_LUMINANCE_CONTROL 0x09
+#define SAA711X_LUMINANCE_BRIGHTNESS 0x0A
+#define SAA711X_LUMINANCE_CONTRAST 0x0B
+#define SAA711X_CHROMA_SATURATION 0x0C
+#define SAA711X_CHROMA_HUE_CONTROL 0x0D
+#define SAA711X_CHROMA_CONTROL 0x0E
+#define SAA711X_FORMAT_DELAY_CONTROL 0x10
+#define SAA711X_OUTPUT_CONTROL_1 0x11
+#define SAA711X_OUTPUT_CONTROL_2 0x12
+#define SAA711X_OUTPUT_CONTROL_3 0x13
+#define SAA711X_V_GATE_1_START 0x15
+#define SAA711X_V_GATE_1_STOP 0x16
+#define SAA711X_V_GATE_1_MSB 0x17
+#define SAA711X_TEXT_SLICER_STATUS 0x1A
+#define SAA711X_DECODED_BYTES_OF_TS_1 0x1B
+#define SAA711X_DECODED_BYTES_OF_TS_2 0x1C
+#define SAA711X_STATUS_BYTE 0x1F
+
+#define DPC_BOARD_CAN_DO_VBI(dev) (dev->revision != 0)
+
+static int debug = 0;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "debug verbosity");
+
+static int dpc_num = 0;
+
+#define DPC_INPUTS 2
+static struct v4l2_input dpc_inputs[DPC_INPUTS] = {
+ { 0, "Port A", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 1, "Port B", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+};
+
+#define DPC_AUDIOS 0
+
+static struct saa7146_extension_ioctls ioctls[] = {
+ { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE },
+ { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE },
+ { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE },
+ { VIDIOC_S_STD, SAA7146_AFTER },
+ { 0, 0 }
+};
+
+struct dpc
+{
+ struct video_device *video_dev;
+ struct video_device *vbi_dev;
+
+ struct i2c_adapter i2c_adapter;
+ struct i2c_client *saa7111a;
+
+ int cur_input; /* current input */
+};
+
+/* fixme: add vbi stuff here */
+static int dpc_probe(struct saa7146_dev* dev)
+{
+ struct dpc* dpc = NULL;
+ struct i2c_client *client;
+ struct list_head *item;
+
+ dpc = (struct dpc*)kmalloc(sizeof(struct dpc), GFP_KERNEL);
+ if( NULL == dpc ) {
+ printk("dpc_v4l2.o: dpc_probe: not enough kernel memory.\n");
+ return -ENOMEM;
+ }
+ memset(dpc, 0x0, sizeof(struct dpc));
+
+ /* FIXME: enable i2c-port pins, video-port-pins
+ video port pins should be enabled here ?! */
+ saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
+
+ dpc->i2c_adapter = (struct i2c_adapter) {
+ .class = I2C_CLASS_TV_ANALOG,
+ .name = "dpc7146",
+ };
+ saa7146_i2c_adapter_prepare(dev, &dpc->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+ if(i2c_add_adapter(&dpc->i2c_adapter) < 0) {
+ DEB_S(("cannot register i2c-device. skipping.\n"));
+ kfree(dpc);
+ return -EFAULT;
+ }
+
+ /* loop through all i2c-devices on the bus and look who is there */
+ list_for_each(item,&dpc->i2c_adapter.clients) {
+ client = list_entry(item, struct i2c_client, list);
+ if( I2C_SAA7111A == client->addr )
+ dpc->saa7111a = client;
+ }
+
+ /* check if all devices are present */
+ if( 0 == dpc->saa7111a ) {
+ DEB_D(("dpc_v4l2.o: dpc_attach failed for this device.\n"));
+ i2c_del_adapter(&dpc->i2c_adapter);
+ kfree(dpc);
+ return -ENODEV;
+ }
+
+ /* all devices are present, probe was successful */
+ DEB_D(("dpc_v4l2.o: dpc_probe succeeded for this device.\n"));
+
+ /* we store the pointer in our private data field */
+ dev->ext_priv = dpc;
+
+ return 0;
+}
+
+/* bring hardware to a sane state. this has to be done, just in case someone
+ wants to capture from this device before it has been properly initialized.
+ the capture engine would badly fail, because no valid signal arrives on the
+ saa7146, thus leading to timeouts and stuff. */
+static int dpc_init_done(struct saa7146_dev* dev)
+{
+ struct dpc* dpc = (struct dpc*)dev->ext_priv;
+
+ DEB_D(("dpc_v4l2.o: dpc_init_done called.\n"));
+
+ /* initialize the helper ics to useful values */
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x00, 0x11);
+
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x02, 0xc0);
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x03, 0x30);
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x04, 0x00);
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x05, 0x00);
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x06, 0xde);
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x07, 0xad);
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x08, 0xa8);
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x09, 0x00);
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x0a, 0x80);
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x0b, 0x47);
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x0c, 0x40);
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x0d, 0x00);
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x0e, 0x03);
+
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x10, 0xd0);
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x11, 0x1c);
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x12, 0xc1);
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x13, 0x30);
+
+ i2c_smbus_write_byte_data(dpc->saa7111a, 0x1f, 0x81);
+
+ return 0;
+}
+
+static struct saa7146_ext_vv vv_data;
+
+/* this function only gets called when the probing was successful */
+static int dpc_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
+{
+ struct dpc* dpc = (struct dpc*)dev->ext_priv;
+
+ DEB_D(("dpc_v4l2.o: dpc_attach called.\n"));
+
+ /* checking for i2c-devices can be omitted here, because we
+ already did this in "dpc_vl42_probe" */
+
+ saa7146_vv_init(dev,&vv_data);
+ if( 0 != saa7146_register_device(&dpc->video_dev, dev, "dpc", VFL_TYPE_GRABBER)) {
+ ERR(("cannot register capture v4l2 device. skipping.\n"));
+ return -1;
+ }
+
+ /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/
+ if( 0 != DPC_BOARD_CAN_DO_VBI(dev)) {
+ if( 0 != saa7146_register_device(&dpc->vbi_dev, dev, "dpc", VFL_TYPE_VBI)) {
+ ERR(("cannot register vbi v4l2 device. skipping.\n"));
+ }
+ }
+
+ i2c_use_client(dpc->saa7111a);
+
+ printk("dpc: found 'dpc7146 demonstration board'-%d.\n",dpc_num);
+ dpc_num++;
+
+ /* the rest */
+ dpc->cur_input = 0;
+ dpc_init_done(dev);
+
+ return 0;
+}
+
+static int dpc_detach(struct saa7146_dev* dev)
+{
+ struct dpc* dpc = (struct dpc*)dev->ext_priv;
+
+ DEB_EE(("dev:%p\n",dev));
+
+ i2c_release_client(dpc->saa7111a);
+
+ saa7146_unregister_device(&dpc->video_dev,dev);
+ if( 0 != DPC_BOARD_CAN_DO_VBI(dev)) {
+ saa7146_unregister_device(&dpc->vbi_dev,dev);
+ }
+ saa7146_vv_release(dev);
+
+ dpc_num--;
+
+ i2c_del_adapter(&dpc->i2c_adapter);
+ kfree(dpc);
+ return 0;
+}
+
+#ifdef axa
+int dpc_vbi_bypass(struct saa7146_dev* dev)
+{
+ struct dpc* dpc = (struct dpc*)dev->ext_priv;
+
+ int i = 1;
+
+ /* switch bypass in saa7111a */
+ if ( 0 != dpc->saa7111a->driver->command(dpc->saa7111a,SAA711X_VBI_BYPASS, &i)) {
+ printk("dpc_v4l2.o: VBI_BYPASS: could not address saa7111a.\n");
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+static int dpc_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct dpc* dpc = (struct dpc*)dev->ext_priv;
+/*
+ struct saa7146_vv *vv = dev->vv_data;
+*/
+ switch(cmd)
+ {
+ case VIDIOC_ENUMINPUT:
+ {
+ struct v4l2_input *i = arg;
+ DEB_EE(("VIDIOC_ENUMINPUT %d.\n",i->index));
+
+ if( i->index < 0 || i->index >= DPC_INPUTS) {
+ return -EINVAL;
+ }
+
+ memcpy(i, &dpc_inputs[i->index], sizeof(struct v4l2_input));
+
+ DEB_D(("dpc_v4l2.o: v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n",i->index));
+ return 0;
+ }
+ case VIDIOC_G_INPUT:
+ {
+ int *input = (int *)arg;
+ *input = dpc->cur_input;
+
+ DEB_D(("dpc_v4l2.o: VIDIOC_G_INPUT: %d\n",*input));
+ return 0;
+ }
+ case VIDIOC_S_INPUT:
+ {
+ int input = *(int *)arg;
+
+ if (input < 0 || input >= DPC_INPUTS) {
+ return -EINVAL;
+ }
+
+ dpc->cur_input = input;
+
+ /* fixme: switch input here, switch audio, too! */
+// saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, input_port_selection[input].hps_sync);
+ printk("dpc_v4l2.o: VIDIOC_S_INPUT: fixme switch input.\n");
+
+ return 0;
+ }
+ default:
+/*
+ DEB_D(("dpc_v4l2.o: v4l2_ioctl does not handle this ioctl.\n"));
+*/
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std)
+{
+ return 0;
+}
+
+static struct saa7146_standard standard[] = {
+ {
+ .name = "PAL", .id = V4L2_STD_PAL,
+ .v_offset = 0x17, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 680,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "NTSC", .id = V4L2_STD_NTSC,
+ .v_offset = 0x16, .v_field = 240,
+ .h_offset = 0x06, .h_pixels = 708,
+ .v_max_out = 480, .h_max_out = 640,
+ }, {
+ .name = "SECAM", .id = V4L2_STD_SECAM,
+ .v_offset = 0x14, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 720,
+ .v_max_out = 576, .h_max_out = 768,
+ }
+};
+
+static struct saa7146_extension extension;
+
+static struct saa7146_pci_extension_data dpc = {
+ .ext_priv = "Multimedia eXtension Board",
+ .ext = &extension,
+};
+
+static struct pci_device_id pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x0000,
+ .subdevice = 0x0000,
+ .driver_data = (unsigned long)&dpc,
+ }, {
+ .vendor = 0,
+ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_ext_vv vv_data = {
+ .inputs = DPC_INPUTS,
+ .capabilities = V4L2_CAP_VBI_CAPTURE,
+ .stds = &standard[0],
+ .num_stds = sizeof(standard)/sizeof(struct saa7146_standard),
+ .std_callback = &std_callback,
+ .ioctls = &ioctls[0],
+ .ioctl = dpc_ioctl,
+};
+
+static struct saa7146_extension extension = {
+ .name = "dpc7146 demonstration board",
+ .flags = SAA7146_USE_I2C_IRQ,
+
+ .pci_tbl = &pci_tbl[0],
+ .module = THIS_MODULE,
+
+ .probe = dpc_probe,
+ .attach = dpc_attach,
+ .detach = dpc_detach,
+
+ .irq_mask = 0,
+ .irq_func = NULL,
+};
+
+static int __init dpc_init_module(void)
+{
+ if( 0 != saa7146_register_extension(&extension)) {
+ DEB_S(("failed to register extension.\n"));
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit dpc_cleanup_module(void)
+{
+ saa7146_unregister_extension(&extension);
+}
+
+module_init(dpc_init_module);
+module_exit(dpc_cleanup_module);
+
+MODULE_DESCRIPTION("video4linux-2 driver for the 'dpc7146 demonstration board'");
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/hexium_gemini.c b/linux/drivers/media/video/hexium_gemini.c
new file mode 100644
index 000000000..34598e608
--- /dev/null
+++ b/linux/drivers/media/video/hexium_gemini.c
@@ -0,0 +1,556 @@
+/*
+ hexium_gemini.c - v4l2 driver for Hexium Gemini frame grabber cards
+
+ Visit http://www.mihu.de/linux/saa7146/ and follow the link
+ to "hexium" for further details about this card.
+
+ Copyright (C) 2003 Michael Hunold <michael@mihu.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define DEBUG_VARIABLE debug
+
+#include <media/saa7146_vv.h>
+
+static int debug = 0;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "debug verbosity");
+
+/* global variables */
+static int hexium_num = 0;
+
+#define HEXIUM_GEMINI 4
+#define HEXIUM_GEMINI_DUAL 5
+
+#define HEXIUM_INPUTS 9
+static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
+ { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+};
+
+#define HEXIUM_AUDIOS 0
+
+struct hexium_data
+{
+ s8 adr;
+ u8 byte;
+};
+
+static struct saa7146_extension_ioctls ioctls[] = {
+ { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE },
+ { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE },
+ { VIDIOC_QUERYCTRL, SAA7146_BEFORE },
+ { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE },
+ { VIDIOC_S_STD, SAA7146_AFTER },
+ { VIDIOC_G_CTRL, SAA7146_BEFORE },
+ { VIDIOC_S_CTRL, SAA7146_BEFORE },
+ { 0, 0 }
+};
+
+#define HEXIUM_CONTROLS 1
+static struct v4l2_queryctrl hexium_controls[] = {
+ { V4L2_CID_PRIVATE_BASE, V4L2_CTRL_TYPE_BOOLEAN, "B/W", 0, 1, 1, 0, 0 },
+};
+
+#define HEXIUM_GEMINI_V_1_0 1
+#define HEXIUM_GEMINI_DUAL_V_1_0 2
+
+struct hexium
+{
+ int type;
+
+ struct video_device *video_dev;
+ struct i2c_adapter i2c_adapter;
+
+ int cur_input; /* current input */
+ v4l2_std_id cur_std; /* current standard */
+ int cur_bw; /* current black/white status */
+};
+
+/* Samsung KS0127B decoder default registers */
+static u8 hexium_ks0127b[0x100]={
+/*00*/ 0x00,0x52,0x30,0x40,0x01,0x0C,0x2A,0x10,
+/*08*/ 0x00,0x00,0x00,0x60,0x00,0x00,0x0F,0x06,
+/*10*/ 0x00,0x00,0xE4,0xC0,0x00,0x00,0x00,0x00,
+/*18*/ 0x14,0x9B,0xFE,0xFF,0xFC,0xFF,0x03,0x22,
+/*20*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*28*/ 0x00,0x00,0x00,0x00,0x00,0x2C,0x9B,0x00,
+/*30*/ 0x00,0x00,0x10,0x80,0x80,0x10,0x80,0x80,
+/*38*/ 0x01,0x04,0x00,0x00,0x00,0x29,0xC0,0x00,
+/*40*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*48*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*50*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*58*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*60*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*68*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*70*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*78*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*80*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*88*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*90*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*98*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*A0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*A8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*B0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*B8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*C0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*C8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*D0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*D8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*E0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*E8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*F0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*F8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+};
+
+static struct hexium_data hexium_pal[] = {
+ { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
+};
+
+static struct hexium_data hexium_pal_bw[] = {
+ { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
+};
+
+static struct hexium_data hexium_ntsc[] = {
+ { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
+};
+
+static struct hexium_data hexium_ntsc_bw[] = {
+ { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
+};
+
+static struct hexium_data hexium_secam[] = {
+ { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
+};
+
+static struct hexium_data hexium_input_select[] = {
+ { 0x02, 0x60 },
+ { 0x02, 0x64 },
+ { 0x02, 0x61 },
+ { 0x02, 0x65 },
+ { 0x02, 0x62 },
+ { 0x02, 0x66 },
+ { 0x02, 0x68 },
+ { 0x02, 0x69 },
+ { 0x02, 0x6A },
+};
+
+/* fixme: h_offset = 0 for Hexium Gemini *Dual*, which
+ are currently *not* supported*/
+static struct saa7146_standard hexium_standards[] = {
+ {
+ .name = "PAL", .id = V4L2_STD_PAL,
+ .v_offset = 28, .v_field = 288,
+ .h_offset = 1, .h_pixels = 680,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "NTSC", .id = V4L2_STD_NTSC,
+ .v_offset = 28, .v_field = 240,
+ .h_offset = 1, .h_pixels = 640,
+ .v_max_out = 480, .h_max_out = 640,
+ }, {
+ .name = "SECAM", .id = V4L2_STD_SECAM,
+ .v_offset = 28, .v_field = 288,
+ .h_offset = 1, .h_pixels = 720,
+ .v_max_out = 576, .h_max_out = 768,
+ }
+};
+
+/* bring hardware to a sane state. this has to be done, just in case someone
+ wants to capture from this device before it has been properly initialized.
+ the capture engine would badly fail, because no valid signal arrives on the
+ saa7146, thus leading to timeouts and stuff. */
+static int hexium_init_done(struct saa7146_dev *dev)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+ union i2c_smbus_data data;
+ int i = 0;
+
+ DEB_D(("hexium_init_done called.\n"));
+
+ /* initialize the helper ics to useful values */
+ for (i = 0; i < sizeof(hexium_ks0127b); i++) {
+ data.byte = hexium_ks0127b[i];
+ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
+ printk("hexium_gemini: hexium_init_done() failed for address 0x%02x\n", i);
+ }
+ }
+
+ return 0;
+}
+
+static int hexium_set_input(struct hexium *hexium, int input)
+{
+ union i2c_smbus_data data;
+
+ DEB_D((".\n"));
+
+ data.byte = hexium_input_select[input].byte;
+ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, &data)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec)
+{
+ union i2c_smbus_data data;
+ int i = 0;
+
+ DEB_D((".\n"));
+
+ while (vdec[i].adr != -1) {
+ data.byte = vdec[i].byte;
+ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, vdec[i].adr, I2C_SMBUS_BYTE_DATA, &data)) {
+ printk("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n", i);
+ return -1;
+ }
+ i++;
+ }
+ return 0;
+}
+
+static struct saa7146_ext_vv vv_data;
+
+/* this function only gets called when the probing was successful */
+static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ DEB_EE((".\n"));
+
+ hexium = (struct hexium *) kmalloc(sizeof(struct hexium), GFP_KERNEL);
+ if (NULL == hexium) {
+ printk("hexium_gemini: not enough kernel memory in hexium_attach().\n");
+ return -ENOMEM;
+ }
+ memset(hexium, 0x0, sizeof(struct hexium));
+ dev->ext_priv = hexium;
+
+ /* enable i2c-port pins */
+ saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
+
+ hexium->i2c_adapter = (struct i2c_adapter) {
+ .class = I2C_CLASS_TV_ANALOG,
+ .name = "hexium gemini",
+ };
+ saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+ if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
+ DEB_S(("cannot register i2c-device. skipping.\n"));
+ kfree(hexium);
+ return -EFAULT;
+ }
+
+ /* set HWControl GPIO number 2 */
+ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
+
+ saa7146_write(dev, DD1_INIT, 0x07000700);
+ saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+ /* the rest */
+ hexium->cur_input = 0;
+ hexium_init_done(dev);
+
+ hexium_set_standard(hexium, hexium_pal);
+ hexium->cur_std = V4L2_STD_PAL;
+
+ hexium_set_input(hexium, 0);
+ hexium->cur_input = 0;
+
+ saa7146_vv_init(dev, &vv_data);
+ if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER)) {
+ printk("hexium_gemini: cannot register capture v4l2 device. skipping.\n");
+ return -1;
+ }
+
+ printk("hexium_gemini: found 'hexium gemini' frame grabber-%d.\n", hexium_num);
+ hexium_num++;
+
+ return 0;
+}
+
+static int hexium_detach(struct saa7146_dev *dev)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ DEB_EE(("dev:%p\n", dev));
+
+ saa7146_unregister_device(&hexium->video_dev, dev);
+ saa7146_vv_release(dev);
+
+ hexium_num--;
+
+ i2c_del_adapter(&hexium->i2c_adapter);
+ kfree(hexium);
+ return 0;
+}
+
+static int hexium_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+/*
+ struct saa7146_vv *vv = dev->vv_data;
+*/
+ switch (cmd) {
+ case VIDIOC_ENUMINPUT:
+ {
+ struct v4l2_input *i = arg;
+ DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
+
+ if (i->index < 0 || i->index >= HEXIUM_INPUTS) {
+ return -EINVAL;
+ }
+
+ memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
+
+ DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
+ return 0;
+ }
+ case VIDIOC_G_INPUT:
+ {
+ int *input = (int *) arg;
+ *input = hexium->cur_input;
+
+ DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
+ return 0;
+ }
+ case VIDIOC_S_INPUT:
+ {
+ int input = *(int *) arg;
+
+ DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
+
+ if (input < 0 || input >= HEXIUM_INPUTS) {
+ return -EINVAL;
+ }
+
+ hexium->cur_input = input;
+ hexium_set_input(hexium, input);
+
+ return 0;
+ }
+ /* the saa7146 provides some controls (brightness, contrast, saturation)
+ which gets registered *after* this function. because of this we have
+ to return with a value != 0 even if the function succeded.. */
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *qc = arg;
+ int i;
+
+ for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
+ if (hexium_controls[i].id == qc->id) {
+ *qc = hexium_controls[i];
+ DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id));
+ return 0;
+ }
+ }
+ return -EAGAIN;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *vc = arg;
+ int i;
+
+ for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
+ if (hexium_controls[i].id == vc->id) {
+ break;
+ }
+ }
+
+ if (i < 0) {
+ return -EAGAIN;
+ }
+
+ switch (vc->id) {
+ case V4L2_CID_PRIVATE_BASE:{
+ vc->value = hexium->cur_bw;
+ DEB_D(("VIDIOC_G_CTRL BW:%d.\n", vc->value));
+ return 0;
+ }
+ }
+ return -EINVAL;
+ }
+
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *vc = arg;
+ int i = 0;
+
+ for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
+ if (hexium_controls[i].id == vc->id) {
+ break;
+ }
+ }
+
+ if (i < 0) {
+ return -EAGAIN;
+ }
+
+ switch (vc->id) {
+ case V4L2_CID_PRIVATE_BASE:{
+ hexium->cur_bw = vc->value;
+ break;
+ }
+ }
+
+ DEB_D(("VIDIOC_S_CTRL BW:%d.\n", hexium->cur_bw));
+
+ if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
+ hexium_set_standard(hexium, hexium_pal);
+ return 0;
+ }
+ if (0 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
+ hexium_set_standard(hexium, hexium_ntsc);
+ return 0;
+ }
+ if (0 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) {
+ hexium_set_standard(hexium, hexium_secam);
+ return 0;
+ }
+ if (1 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
+ hexium_set_standard(hexium, hexium_pal_bw);
+ return 0;
+ }
+ if (1 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
+ hexium_set_standard(hexium, hexium_ntsc_bw);
+ return 0;
+ }
+ if (1 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) {
+ /* fixme: is there no bw secam mode? */
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+ }
+ default:
+/*
+ DEB_D(("hexium_ioctl() does not handle this ioctl.\n"));
+*/
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ if (V4L2_STD_PAL == std->id) {
+ hexium_set_standard(hexium, hexium_pal);
+ hexium->cur_std = V4L2_STD_PAL;
+ return 0;
+ } else if (V4L2_STD_NTSC == std->id) {
+ hexium_set_standard(hexium, hexium_ntsc);
+ hexium->cur_std = V4L2_STD_NTSC;
+ return 0;
+ } else if (V4L2_STD_SECAM == std->id) {
+ hexium_set_standard(hexium, hexium_secam);
+ hexium->cur_std = V4L2_STD_SECAM;
+ return 0;
+ }
+
+ return -1;
+}
+
+static struct saa7146_extension hexium_extension;
+
+static struct saa7146_pci_extension_data hexium_gemini_4bnc = {
+ .ext_priv = "Hexium Gemini (4 BNC)",
+ .ext = &hexium_extension,
+};
+
+static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = {
+ .ext_priv = "Hexium Gemini Dual (4 BNC)",
+ .ext = &hexium_extension,
+};
+
+static struct pci_device_id pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x17c8,
+ .subdevice = 0x2401,
+ .driver_data = (unsigned long) &hexium_gemini_4bnc,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x17c8,
+ .subdevice = 0x2402,
+ .driver_data = (unsigned long) &hexium_gemini_dual_4bnc,
+ },
+ {
+ .vendor = 0,
+ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_ext_vv vv_data = {
+ .inputs = HEXIUM_INPUTS,
+ .capabilities = 0,
+ .stds = &hexium_standards[0],
+ .num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard),
+ .std_callback = &std_callback,
+ .ioctls = &ioctls[0],
+ .ioctl = hexium_ioctl,
+};
+
+static struct saa7146_extension hexium_extension = {
+ .name = "hexium gemini",
+ .flags = SAA7146_USE_I2C_IRQ,
+
+ .pci_tbl = &pci_tbl[0],
+ .module = THIS_MODULE,
+
+ .attach = hexium_attach,
+ .detach = hexium_detach,
+
+ .irq_mask = 0,
+ .irq_func = NULL,
+};
+
+static int __init hexium_init_module(void)
+{
+ if (0 != saa7146_register_extension(&hexium_extension)) {
+ DEB_S(("failed to register extension.\n"));
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit hexium_cleanup_module(void)
+{
+ saa7146_unregister_extension(&hexium_extension);
+}
+
+module_init(hexium_init_module);
+module_exit(hexium_cleanup_module);
+
+MODULE_DESCRIPTION("video4linux-2 driver for Hexium Gemini frame grabber cards");
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/hexium_orion.c b/linux/drivers/media/video/hexium_orion.c
new file mode 100644
index 000000000..8c9bdcf72
--- /dev/null
+++ b/linux/drivers/media/video/hexium_orion.c
@@ -0,0 +1,522 @@
+/*
+ hexium_orion.c - v4l2 driver for the Hexium Orion frame grabber cards
+
+ Visit http://www.mihu.de/linux/saa7146/ and follow the link
+ to "hexium" for further details about this card.
+
+ Copyright (C) 2003 Michael Hunold <michael@mihu.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define DEBUG_VARIABLE debug
+
+#include <media/saa7146_vv.h>
+
+static int debug = 0;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "debug verbosity");
+
+/* global variables */
+static int hexium_num = 0;
+
+#define HEXIUM_HV_PCI6_ORION 1
+#define HEXIUM_ORION_1SVHS_3BNC 2
+#define HEXIUM_ORION_4BNC 3
+
+#define HEXIUM_INPUTS 9
+static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
+ { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+};
+
+#define HEXIUM_AUDIOS 0
+
+struct hexium_data
+{
+ s8 adr;
+ u8 byte;
+};
+
+static struct saa7146_extension_ioctls ioctls[] = {
+ { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE },
+ { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE },
+ { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE },
+ { VIDIOC_S_STD, SAA7146_AFTER },
+ { 0, 0 }
+};
+
+struct hexium
+{
+ int type;
+ struct video_device *video_dev;
+ struct i2c_adapter i2c_adapter;
+
+ int cur_input; /* current input */
+};
+
+/* Philips SAA7110 decoder default registers */
+static u8 hexium_saa7110[53]={
+/*00*/ 0x4C,0x3C,0x0D,0xEF,0xBD,0xF0,0x00,0x00,
+/*08*/ 0xF8,0xF8,0x60,0x60,0x40,0x86,0x18,0x90,
+/*10*/ 0x00,0x2C,0x40,0x46,0x42,0x1A,0xFF,0xDA,
+/*18*/ 0xF0,0x8B,0x00,0x00,0x00,0x00,0x00,0x00,
+/*20*/ 0xD9,0x17,0x40,0x41,0x80,0x41,0x80,0x4F,
+/*28*/ 0xFE,0x01,0x0F,0x0F,0x03,0x01,0x81,0x03,
+/*30*/ 0x44,0x75,0x01,0x8C,0x03
+};
+
+static struct {
+ struct hexium_data data[8];
+} hexium_input_select[] = {
+{
+ { /* cvbs 1 */
+ { 0x06, 0x00 },
+ { 0x20, 0xD9 },
+ { 0x21, 0x17 }, // 0x16,
+ { 0x22, 0x40 },
+ { 0x2C, 0x03 },
+ { 0x30, 0x44 },
+ { 0x31, 0x75 }, // ??
+ { 0x21, 0x16 }, // 0x03,
+ }
+}, {
+ { /* cvbs 2 */
+ { 0x06, 0x00 },
+ { 0x20, 0x78 },
+ { 0x21, 0x07 }, // 0x03,
+ { 0x22, 0xD2 },
+ { 0x2C, 0x83 },
+ { 0x30, 0x60 },
+ { 0x31, 0xB5 }, // ?
+ { 0x21, 0x03 },
+ }
+}, {
+ { /* cvbs 3 */
+ { 0x06, 0x00 },
+ { 0x20, 0xBA },
+ { 0x21, 0x07 }, // 0x05,
+ { 0x22, 0x91 },
+ { 0x2C, 0x03 },
+ { 0x30, 0x60 },
+ { 0x31, 0xB5 }, // ??
+ { 0x21, 0x05 }, // 0x03,
+ }
+}, {
+ { /* cvbs 4 */
+ { 0x06, 0x00 },
+ { 0x20, 0xD8 },
+ { 0x21, 0x17 }, // 0x16,
+ { 0x22, 0x40 },
+ { 0x2C, 0x03 },
+ { 0x30, 0x44 },
+ { 0x31, 0x75 }, // ??
+ { 0x21, 0x16 }, // 0x03,
+ }
+}, {
+ { /* cvbs 5 */
+ { 0x06, 0x00 },
+ { 0x20, 0xB8 },
+ { 0x21, 0x07 }, // 0x05,
+ { 0x22, 0x91 },
+ { 0x2C, 0x03 },
+ { 0x30, 0x60 },
+ { 0x31, 0xB5 }, // ??
+ { 0x21, 0x05 }, // 0x03,
+ }
+}, {
+ { /* cvbs 6 */
+ { 0x06, 0x00 },
+ { 0x20, 0x7C },
+ { 0x21, 0x07 }, // 0x03
+ { 0x22, 0xD2 },
+ { 0x2C, 0x83 },
+ { 0x30, 0x60 },
+ { 0x31, 0xB5 }, // ??
+ { 0x21, 0x03 },
+ }
+}, {
+ { /* y/c 1 */
+ { 0x06, 0x80 },
+ { 0x20, 0x59 },
+ { 0x21, 0x17 },
+ { 0x22, 0x42 },
+ { 0x2C, 0xA3 },
+ { 0x30, 0x44 },
+ { 0x31, 0x75 },
+ { 0x21, 0x12 },
+ }
+}, {
+ { /* y/c 2 */
+ { 0x06, 0x80 },
+ { 0x20, 0x9A },
+ { 0x21, 0x17 },
+ { 0x22, 0xB1 },
+ { 0x2C, 0x13 },
+ { 0x30, 0x60 },
+ { 0x31, 0xB5 },
+ { 0x21, 0x14 },
+ }
+}, {
+ { /* y/c 3 */
+ { 0x06, 0x80 },
+ { 0x20, 0x3C },
+ { 0x21, 0x27 },
+ { 0x22, 0xC1 },
+ { 0x2C, 0x23 },
+ { 0x30, 0x44 },
+ { 0x31, 0x75 },
+ { 0x21, 0x21 },
+ }
+}
+};
+
+static struct saa7146_standard hexium_standards[] = {
+ {
+ .name = "PAL", .id = V4L2_STD_PAL,
+ .v_offset = 16, .v_field = 288,
+ .h_offset = 1, .h_pixels = 680,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "NTSC", .id = V4L2_STD_NTSC,
+ .v_offset = 16, .v_field = 240,
+ .h_offset = 1, .h_pixels = 640,
+ .v_max_out = 480, .h_max_out = 640,
+ }, {
+ .name = "SECAM", .id = V4L2_STD_SECAM,
+ .v_offset = 16, .v_field = 288,
+ .h_offset = 1, .h_pixels = 720,
+ .v_max_out = 576, .h_max_out = 768,
+ }
+};
+
+/* this is only called for old HV-PCI6/Orion cards
+ without eeprom */
+static int hexium_probe(struct saa7146_dev *dev)
+{
+ struct hexium *hexium = NULL;
+ union i2c_smbus_data data;
+ int err = 0;
+
+ DEB_EE((".\n"));
+
+ /* there are no hexium orion cards with revision 0 saa7146s */
+ if (0 == dev->revision) {
+ return -EFAULT;
+ }
+
+ hexium = (struct hexium *) kmalloc(sizeof(struct hexium), GFP_KERNEL);
+ if (NULL == hexium) {
+ printk("hexium_orion: hexium_probe: not enough kernel memory.\n");
+ return -ENOMEM;
+ }
+ memset(hexium, 0x0, sizeof(struct hexium));
+
+ /* enable i2c-port pins */
+ saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
+
+ saa7146_write(dev, DD1_INIT, 0x01000100);
+ saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+ hexium->i2c_adapter = (struct i2c_adapter) {
+ .class = I2C_CLASS_TV_ANALOG,
+ .name = "hexium orion",
+ };
+ saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+ if (i2c_add_adapter(&hexium->i2c_adapter) < 0) {
+ DEB_S(("cannot register i2c-device. skipping.\n"));
+ kfree(hexium);
+ return -EFAULT;
+ }
+
+ /* set SAA7110 control GPIO 0 */
+ saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTHI);
+ /* set HWControl GPIO number 2 */
+ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
+
+ mdelay(10);
+
+ /* detect newer Hexium Orion cards by subsystem ids */
+ if (0x17c8 == dev->pci->subsystem_vendor && 0x0101 == dev->pci->subsystem_device) {
+ printk("hexium_orion: device is a Hexium Orion w/ 1 SVHS + 3 BNC inputs.\n");
+ /* we store the pointer in our private data field */
+ dev->ext_priv = hexium;
+ hexium->type = HEXIUM_ORION_1SVHS_3BNC;
+ return 0;
+ }
+
+ if (0x17c8 == dev->pci->subsystem_vendor && 0x2101 == dev->pci->subsystem_device) {
+ printk("hexium_orion: device is a Hexium Orion w/ 4 BNC inputs.\n");
+ /* we store the pointer in our private data field */
+ dev->ext_priv = hexium;
+ hexium->type = HEXIUM_ORION_4BNC;
+ return 0;
+ }
+
+ /* check if this is an old hexium Orion card by looking at
+ a saa7110 at address 0x4e */
+ if (0 == (err = i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE_DATA, &data))) {
+ printk("hexium_orion: device is a Hexium HV-PCI6/Orion (old).\n");
+ /* we store the pointer in our private data field */
+ dev->ext_priv = hexium;
+ hexium->type = HEXIUM_HV_PCI6_ORION;
+ return 0;
+ }
+
+ i2c_del_adapter(&hexium->i2c_adapter);
+ kfree(hexium);
+ return -EFAULT;
+}
+
+/* bring hardware to a sane state. this has to be done, just in case someone
+ wants to capture from this device before it has been properly initialized.
+ the capture engine would badly fail, because no valid signal arrives on the
+ saa7146, thus leading to timeouts and stuff. */
+static int hexium_init_done(struct saa7146_dev *dev)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+ union i2c_smbus_data data;
+ int i = 0;
+
+ DEB_D(("hexium_init_done called.\n"));
+
+ /* initialize the helper ics to useful values */
+ for (i = 0; i < sizeof(hexium_saa7110); i++) {
+ data.byte = hexium_saa7110[i];
+ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) {
+ printk("hexium_orion: failed for address 0x%02x\n", i);
+ }
+ }
+
+ return 0;
+}
+
+static int hexium_set_input(struct hexium *hexium, int input)
+{
+ union i2c_smbus_data data;
+ int i = 0;
+
+ DEB_D((".\n"));
+
+ for (i = 0; i < 8; i++) {
+ int adr = hexium_input_select[input].data[i].adr;
+ data.byte = hexium_input_select[input].data[i].byte;
+ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, adr, I2C_SMBUS_BYTE_DATA, &data)) {
+ return -1;
+ }
+ printk("%d: 0x%02x => 0x%02x\n",input, adr,data.byte);
+ }
+
+ return 0;
+}
+
+static struct saa7146_ext_vv vv_data;
+
+/* this function only gets called when the probing was successful */
+static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ DEB_EE((".\n"));
+
+ saa7146_vv_init(dev, &vv_data);
+ if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_GRABBER)) {
+ printk("hexium_orion: cannot register capture v4l2 device. skipping.\n");
+ return -1;
+ }
+
+ printk("hexium_orion: found 'hexium orion' frame grabber-%d.\n", hexium_num);
+ hexium_num++;
+
+ /* the rest */
+ hexium->cur_input = 0;
+ hexium_init_done(dev);
+
+ return 0;
+}
+
+static int hexium_detach(struct saa7146_dev *dev)
+{
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+ DEB_EE(("dev:%p\n", dev));
+
+ saa7146_unregister_device(&hexium->video_dev, dev);
+ saa7146_vv_release(dev);
+
+ hexium_num--;
+
+ i2c_del_adapter(&hexium->i2c_adapter);
+ kfree(hexium);
+ return 0;
+}
+
+static int hexium_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+/*
+ struct saa7146_vv *vv = dev->vv_data;
+*/
+ switch (cmd) {
+ case VIDIOC_ENUMINPUT:
+ {
+ struct v4l2_input *i = arg;
+ DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
+
+ if (i->index < 0 || i->index >= HEXIUM_INPUTS) {
+ return -EINVAL;
+ }
+
+ memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
+
+ DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
+ return 0;
+ }
+ case VIDIOC_G_INPUT:
+ {
+ int *input = (int *) arg;
+ *input = hexium->cur_input;
+
+ DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
+ return 0;
+ }
+ case VIDIOC_S_INPUT:
+ {
+ int input = *(int *) arg;
+
+ if (input < 0 || input >= HEXIUM_INPUTS) {
+ return -EINVAL;
+ }
+
+ hexium->cur_input = input;
+ hexium_set_input(hexium, input);
+
+ return 0;
+ }
+ default:
+/*
+ DEB_D(("hexium_ioctl() does not handle this ioctl.\n"));
+*/
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
+{
+ return 0;
+}
+
+static struct saa7146_extension extension;
+
+static struct saa7146_pci_extension_data hexium_hv_pci6 = {
+ .ext_priv = "Hexium HV-PCI6 / Orion",
+ .ext = &extension,
+};
+
+static struct saa7146_pci_extension_data hexium_orion_1svhs_3bnc = {
+ .ext_priv = "Hexium HV-PCI6 / Orion (1 SVHS/3 BNC)",
+ .ext = &extension,
+};
+
+static struct saa7146_pci_extension_data hexium_orion_4bnc = {
+ .ext_priv = "Hexium HV-PCI6 / Orion (4 BNC)",
+ .ext = &extension,
+};
+
+static struct pci_device_id pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x0000,
+ .subdevice = 0x0000,
+ .driver_data = (unsigned long) &hexium_hv_pci6,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x17c8,
+ .subdevice = 0x0101,
+ .driver_data = (unsigned long) &hexium_orion_1svhs_3bnc,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x17c8,
+ .subdevice = 0x2101,
+ .driver_data = (unsigned long) &hexium_orion_4bnc,
+ },
+ {
+ .vendor = 0,
+ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_ext_vv vv_data = {
+ .inputs = HEXIUM_INPUTS,
+ .capabilities = 0,
+ .stds = &hexium_standards[0],
+ .num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard),
+ .std_callback = &std_callback,
+ .ioctls = &ioctls[0],
+ .ioctl = hexium_ioctl,
+};
+
+static struct saa7146_extension extension = {
+ .name = "hexium HV-PCI6/Orion",
+ .flags = 0, // SAA7146_USE_I2C_IRQ,
+
+ .pci_tbl = &pci_tbl[0],
+ .module = THIS_MODULE,
+
+ .probe = hexium_probe,
+ .attach = hexium_attach,
+ .detach = hexium_detach,
+
+ .irq_mask = 0,
+ .irq_func = NULL,
+};
+
+static int __init hexium_init_module(void)
+{
+ if (0 != saa7146_register_extension(&extension)) {
+ DEB_S(("failed to register extension.\n"));
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit hexium_cleanup_module(void)
+{
+ saa7146_unregister_extension(&extension);
+}
+
+module_init(hexium_init_module);
+module_exit(hexium_cleanup_module);
+
+MODULE_DESCRIPTION("video4linux-2 driver for Hexium Orion frame grabber cards");
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/mxb.c b/linux/drivers/media/video/mxb.c
new file mode 100644
index 000000000..155967988
--- /dev/null
+++ b/linux/drivers/media/video/mxb.c
@@ -0,0 +1,1028 @@
+/*
+ mxb - v4l2 driver for the Multimedia eXtension Board
+
+ Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
+
+ Visit http://www.mihu.de/linux/saa7146/mxb/
+ for further details about this card.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define DEBUG_VARIABLE debug
+
+#include <media/saa7146_vv.h>
+#include <media/tuner.h>
+#include <media/v4l2-common.h>
+#include <linux/video_decoder.h>
+
+#include "mxb.h"
+#include "tea6415c.h"
+#include "tea6420.h"
+#include "tda9840.h"
+
+#define I2C_SAA7111 0x24
+
+#define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0)
+
+/* global variable */
+static int mxb_num = 0;
+
+/* initial frequence the tuner will be tuned to.
+ in verden (lower saxony, germany) 4148 is a
+ channel called "phoenix" */
+static int freq = 4148;
+module_param(freq, int, 0644);
+MODULE_PARM_DESC(freq, "initial frequency the tuner will be tuned to while setup");
+
+static int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off).");
+
+#define MXB_INPUTS 4
+enum { TUNER, AUX1, AUX3, AUX3_YC };
+
+static struct v4l2_input mxb_inputs[MXB_INPUTS] = {
+ { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 1, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 4, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 4, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+};
+
+/* this array holds the information, which port of the saa7146 each
+ input actually uses. the mxb uses port 0 for every input */
+static struct {
+ int hps_source;
+ int hps_sync;
+} input_port_selection[MXB_INPUTS] = {
+ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+};
+
+/* this array holds the information of the audio source (mxb_audios),
+ which has to be switched corresponding to the video source (mxb_channels) */
+static int video_audio_connect[MXB_INPUTS] =
+ { 0, 1, 3, 3 };
+
+/* these are the necessary input-output-pins for bringing one audio source
+(see above) to the CD-output */
+static struct tea6420_multiplex TEA6420_cd[MXB_AUDIOS+1][2] =
+ {
+ {{1,1,0},{1,1,0}}, /* Tuner */
+ {{5,1,0},{6,1,0}}, /* AUX 1 */
+ {{4,1,0},{6,1,0}}, /* AUX 2 */
+ {{3,1,0},{6,1,0}}, /* AUX 3 */
+ {{1,1,0},{3,1,0}}, /* Radio */
+ {{1,1,0},{2,1,0}}, /* CD-Rom */
+ {{6,1,0},{6,1,0}} /* Mute */
+ };
+
+/* these are the necessary input-output-pins for bringing one audio source
+(see above) to the line-output */
+static struct tea6420_multiplex TEA6420_line[MXB_AUDIOS+1][2] =
+ {
+ {{2,3,0},{1,2,0}},
+ {{5,3,0},{6,2,0}},
+ {{4,3,0},{6,2,0}},
+ {{3,3,0},{6,2,0}},
+ {{2,3,0},{3,2,0}},
+ {{2,3,0},{2,2,0}},
+ {{6,3,0},{6,2,0}} /* Mute */
+ };
+
+#define MAXCONTROLS 1
+static struct v4l2_queryctrl mxb_controls[] = {
+ { V4L2_CID_AUDIO_MUTE, V4L2_CTRL_TYPE_BOOLEAN, "Mute", 0, 1, 1, 0, 0 },
+};
+
+static struct saa7146_extension_ioctls ioctls[] = {
+ { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE },
+ { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE },
+ { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE },
+ { VIDIOC_QUERYCTRL, SAA7146_BEFORE },
+ { VIDIOC_G_CTRL, SAA7146_BEFORE },
+ { VIDIOC_S_CTRL, SAA7146_BEFORE },
+ { VIDIOC_G_TUNER, SAA7146_EXCLUSIVE },
+ { VIDIOC_S_TUNER, SAA7146_EXCLUSIVE },
+ { VIDIOC_G_FREQUENCY, SAA7146_EXCLUSIVE },
+ { VIDIOC_S_FREQUENCY, SAA7146_EXCLUSIVE },
+ { VIDIOC_G_AUDIO, SAA7146_EXCLUSIVE },
+ { VIDIOC_S_AUDIO, SAA7146_EXCLUSIVE },
+ { MXB_S_AUDIO_CD, SAA7146_EXCLUSIVE }, /* custom control */
+ { MXB_S_AUDIO_LINE, SAA7146_EXCLUSIVE }, /* custom control */
+ { 0, 0 }
+};
+
+struct mxb
+{
+ struct video_device *video_dev;
+ struct video_device *vbi_dev;
+
+ struct i2c_adapter i2c_adapter;
+
+ struct i2c_client* saa7111a;
+ struct i2c_client* tda9840;
+ struct i2c_client* tea6415c;
+ struct i2c_client* tuner;
+ struct i2c_client* tea6420_1;
+ struct i2c_client* tea6420_2;
+
+ int cur_mode; /* current audio mode (mono, stereo, ...) */
+ int cur_input; /* current input */
+ int cur_mute; /* current mute status */
+ struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */
+};
+
+static struct saa7146_extension extension;
+
+static int mxb_probe(struct saa7146_dev* dev)
+{
+ struct mxb* mxb = NULL;
+ struct i2c_client *client;
+ struct list_head *item;
+ int result;
+
+ if ((result = request_module("saa7111")) < 0) {
+ printk("mxb: saa7111 i2c module not available.\n");
+ return -ENODEV;
+ }
+ if ((result = request_module("tuner")) < 0) {
+ printk("mxb: tuner i2c module not available.\n");
+ return -ENODEV;
+ }
+ if ((result = request_module("tea6420")) < 0) {
+ printk("mxb: tea6420 i2c module not available.\n");
+ return -ENODEV;
+ }
+ if ((result = request_module("tea6415c")) < 0) {
+ printk("mxb: tea6415c i2c module not available.\n");
+ return -ENODEV;
+ }
+ if ((result = request_module("tda9840")) < 0) {
+ printk("mxb: tda9840 i2c module not available.\n");
+ return -ENODEV;
+ }
+
+ mxb = (struct mxb*)kmalloc(sizeof(struct mxb), GFP_KERNEL);
+ if( NULL == mxb ) {
+ DEB_D(("not enough kernel memory.\n"));
+ return -ENOMEM;
+ }
+ memset(mxb, 0x0, sizeof(struct mxb));
+
+ mxb->i2c_adapter = (struct i2c_adapter) {
+ .class = I2C_CLASS_TV_ANALOG,
+ .name = "mxb",
+ };
+
+ saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+ if(i2c_add_adapter(&mxb->i2c_adapter) < 0) {
+ DEB_S(("cannot register i2c-device. skipping.\n"));
+ kfree(mxb);
+ return -EFAULT;
+ }
+
+ /* loop through all i2c-devices on the bus and look who is there */
+ list_for_each(item,&mxb->i2c_adapter.clients) {
+ client = list_entry(item, struct i2c_client, list);
+ if( I2C_TEA6420_1 == client->addr )
+ mxb->tea6420_1 = client;
+ if( I2C_TEA6420_2 == client->addr )
+ mxb->tea6420_2 = client;
+ if( I2C_TEA6415C_2 == client->addr )
+ mxb->tea6415c = client;
+ if( I2C_TDA9840 == client->addr )
+ mxb->tda9840 = client;
+ if( I2C_SAA7111 == client->addr )
+ mxb->saa7111a = client;
+ if( 0x60 == client->addr )
+ mxb->tuner = client;
+ }
+
+ /* check if all devices are present */
+ if( 0 == mxb->tea6420_1 || 0 == mxb->tea6420_2 || 0 == mxb->tea6415c
+ || 0 == mxb->tda9840 || 0 == mxb->saa7111a || 0 == mxb->tuner ) {
+
+ printk("mxb: did not find all i2c devices. aborting\n");
+ i2c_del_adapter(&mxb->i2c_adapter);
+ kfree(mxb);
+ return -ENODEV;
+ }
+
+ /* all devices are present, probe was successful */
+
+ /* we store the pointer in our private data field */
+ dev->ext_priv = mxb;
+
+ return 0;
+}
+
+/* some init data for the saa7740, the so-called 'sound arena module'.
+ there are no specs available, so we simply use some init values */
+static struct {
+ int length;
+ char data[9];
+} mxb_saa7740_init[] = {
+ { 3, { 0x80, 0x00, 0x00 } },{ 3, { 0x80, 0x89, 0x00 } },
+ { 3, { 0x80, 0xb0, 0x0a } },{ 3, { 0x00, 0x00, 0x00 } },
+ { 3, { 0x49, 0x00, 0x00 } },{ 3, { 0x4a, 0x00, 0x00 } },
+ { 3, { 0x4b, 0x00, 0x00 } },{ 3, { 0x4c, 0x00, 0x00 } },
+ { 3, { 0x4d, 0x00, 0x00 } },{ 3, { 0x4e, 0x00, 0x00 } },
+ { 3, { 0x4f, 0x00, 0x00 } },{ 3, { 0x50, 0x00, 0x00 } },
+ { 3, { 0x51, 0x00, 0x00 } },{ 3, { 0x52, 0x00, 0x00 } },
+ { 3, { 0x53, 0x00, 0x00 } },{ 3, { 0x54, 0x00, 0x00 } },
+ { 3, { 0x55, 0x00, 0x00 } },{ 3, { 0x56, 0x00, 0x00 } },
+ { 3, { 0x57, 0x00, 0x00 } },{ 3, { 0x58, 0x00, 0x00 } },
+ { 3, { 0x59, 0x00, 0x00 } },{ 3, { 0x5a, 0x00, 0x00 } },
+ { 3, { 0x5b, 0x00, 0x00 } },{ 3, { 0x5c, 0x00, 0x00 } },
+ { 3, { 0x5d, 0x00, 0x00 } },{ 3, { 0x5e, 0x00, 0x00 } },
+ { 3, { 0x5f, 0x00, 0x00 } },{ 3, { 0x60, 0x00, 0x00 } },
+ { 3, { 0x61, 0x00, 0x00 } },{ 3, { 0x62, 0x00, 0x00 } },
+ { 3, { 0x63, 0x00, 0x00 } },{ 3, { 0x64, 0x00, 0x00 } },
+ { 3, { 0x65, 0x00, 0x00 } },{ 3, { 0x66, 0x00, 0x00 } },
+ { 3, { 0x67, 0x00, 0x00 } },{ 3, { 0x68, 0x00, 0x00 } },
+ { 3, { 0x69, 0x00, 0x00 } },{ 3, { 0x6a, 0x00, 0x00 } },
+ { 3, { 0x6b, 0x00, 0x00 } },{ 3, { 0x6c, 0x00, 0x00 } },
+ { 3, { 0x6d, 0x00, 0x00 } },{ 3, { 0x6e, 0x00, 0x00 } },
+ { 3, { 0x6f, 0x00, 0x00 } },{ 3, { 0x70, 0x00, 0x00 } },
+ { 3, { 0x71, 0x00, 0x00 } },{ 3, { 0x72, 0x00, 0x00 } },
+ { 3, { 0x73, 0x00, 0x00 } },{ 3, { 0x74, 0x00, 0x00 } },
+ { 3, { 0x75, 0x00, 0x00 } },{ 3, { 0x76, 0x00, 0x00 } },
+ { 3, { 0x77, 0x00, 0x00 } },{ 3, { 0x41, 0x00, 0x42 } },
+ { 3, { 0x42, 0x10, 0x42 } },{ 3, { 0x43, 0x20, 0x42 } },
+ { 3, { 0x44, 0x30, 0x42 } },{ 3, { 0x45, 0x00, 0x01 } },
+ { 3, { 0x46, 0x00, 0x01 } },{ 3, { 0x47, 0x00, 0x01 } },
+ { 3, { 0x48, 0x00, 0x01 } },
+ { 9, { 0x01, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } },
+ { 9, { 0x21, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } },
+ { 9, { 0x09, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } },
+ { 9, { 0x29, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } },
+ { 9, { 0x11, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } },
+ { 9, { 0x31, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } },
+ { 9, { 0x19, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } },
+ { 9, { 0x39, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } },
+ { 9, { 0x05, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } },
+ { 9, { 0x25, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } },
+ { 9, { 0x0d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } },
+ { 9, { 0x2d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } },
+ { 9, { 0x15, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } },
+ { 9, { 0x35, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } },
+ { 9, { 0x1d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } },
+ { 9, { 0x3d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } },
+ { 3, { 0x80, 0xb3, 0x0a } },
+ {-1, { 0} }
+};
+
+static const unsigned char mxb_saa7111_init[] = {
+ 0x00, 0x00, /* 00 - ID byte */
+ 0x01, 0x00, /* 01 - reserved */
+
+ /*front end */
+ 0x02, 0xd8, /* 02 - FUSE=x, GUDL=x, MODE=x */
+ 0x03, 0x23, /* 03 - HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */
+ 0x04, 0x00, /* 04 - GAI1=256 */
+ 0x05, 0x00, /* 05 - GAI2=256 */
+
+ /* decoder */
+ 0x06, 0xf0, /* 06 - HSB at xx(50Hz) / xx(60Hz) pixels after end of last line */
+ 0x07, 0x30, /* 07 - HSS at xx(50Hz) / xx(60Hz) pixels after end of last line */
+ 0x08, 0xa8, /* 08 - AUFD=x, FSEL=x, EXFIL=x, VTRC=x, HPLL=x, VNOI=x */
+ 0x09, 0x02, /* 09 - BYPS=x, PREF=x, BPSS=x, VBLB=x, UPTCV=x, APER=x */
+ 0x0a, 0x80, /* 0a - BRIG=128 */
+ 0x0b, 0x47, /* 0b - CONT=1.109 */
+ 0x0c, 0x40, /* 0c - SATN=1.0 */
+ 0x0d, 0x00, /* 0d - HUE=0 */
+ 0x0e, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */
+ 0x0f, 0x00, /* 0f - reserved */
+ 0x10, 0xd0, /* 10 - OFTS=x, HDEL=x, VRLN=x, YDEL=x */
+ 0x11, 0x8c, /* 11 - GPSW=x, CM99=x, FECO=x, COMPO=x, OEYC=1, OEHV=1, VIPB=0, COLO=0 */
+ 0x12, 0x80, /* 12 - xx output control 2 */
+ 0x13, 0x30, /* 13 - xx output control 3 */
+ 0x14, 0x00, /* 14 - reserved */
+ 0x15, 0x15, /* 15 - VBI */
+ 0x16, 0x04, /* 16 - VBI */
+ 0x17, 0x00, /* 17 - VBI */
+};
+
+/* bring hardware to a sane state. this has to be done, just in case someone
+ wants to capture from this device before it has been properly initialized.
+ the capture engine would badly fail, because no valid signal arrives on the
+ saa7146, thus leading to timeouts and stuff. */
+static int mxb_init_done(struct saa7146_dev* dev)
+{
+ struct mxb* mxb = (struct mxb*)dev->ext_priv;
+ struct video_decoder_init init;
+ struct i2c_msg msg;
+ struct tuner_setup tun_setup;
+
+ int i = 0, err = 0;
+ struct tea6415c_multiplex vm;
+
+ /* select video mode in saa7111a */
+ i = VIDEO_MODE_PAL;
+ /* fixme: currently pointless: gets overwritten by configuration below */
+ mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_NORM, &i);
+
+ /* write configuration to saa7111a */
+ init.data = mxb_saa7111_init;
+ init.len = sizeof(mxb_saa7111_init);
+ mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_INIT, &init);
+
+ /* select tuner-output on saa7111a */
+ i = 0;
+ mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_INPUT, &i);
+
+ /* enable vbi bypass */
+ i = 1;
+ mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_VBI_BYPASS, &i);
+
+ /* select a tuner type */
+ tun_setup.mode_mask = T_ANALOG_TV;
+ tun_setup.addr = ADDR_UNSET;
+ tun_setup.type = TUNER_PHILIPS_PAL;
+ mxb->tuner->driver->command(mxb->tuner,TUNER_SET_TYPE_ADDR, &tun_setup);
+ /* tune in some frequency on tuner */
+ mxb->cur_freq.tuner = 0;
+ mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV;
+ mxb->cur_freq.frequency = freq;
+ mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_FREQUENCY,
+ &mxb->cur_freq);
+
+ /* mute audio on tea6420s */
+ mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[6][0]);
+ mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[6][1]);
+ mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_cd[6][0]);
+ mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_cd[6][1]);
+
+ /* switch to tuner-channel on tea6415c*/
+ vm.out = 17;
+ vm.in = 3;
+ mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm);
+
+ /* select tuner-output on multicable on tea6415c*/
+ vm.in = 3;
+ vm.out = 13;
+ mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm);
+
+ /* the rest for mxb */
+ mxb->cur_input = 0;
+ mxb->cur_mute = 1;
+
+ mxb->cur_mode = V4L2_TUNER_MODE_STEREO;
+ mxb->tda9840->driver->command(mxb->tda9840, TDA9840_SWITCH, &mxb->cur_mode);
+
+ /* check if the saa7740 (aka 'sound arena module') is present
+ on the mxb. if so, we must initialize it. due to lack of
+ informations about the saa7740, the values were reverse
+ engineered. */
+ msg.addr = 0x1b;
+ msg.flags = 0;
+ msg.len = mxb_saa7740_init[0].length;
+ msg.buf = &mxb_saa7740_init[0].data[0];
+
+ if( 1 == (err = i2c_transfer(&mxb->i2c_adapter, &msg, 1))) {
+ /* the sound arena module is a pos, that's probably the reason
+ philips refuses to hand out a datasheet for the saa7740...
+ it seems to screw up the i2c bus, so we disable fast irq
+ based i2c transactions here and rely on the slow and safe
+ polling method ... */
+ extension.flags &= ~SAA7146_USE_I2C_IRQ;
+ for(i = 1;;i++) {
+ if( -1 == mxb_saa7740_init[i].length ) {
+ break;
+ }
+
+ msg.len = mxb_saa7740_init[i].length;
+ msg.buf = &mxb_saa7740_init[i].data[0];
+ if( 1 != (err = i2c_transfer(&mxb->i2c_adapter, &msg, 1))) {
+ DEB_D(("failed to initialize 'sound arena module'.\n"));
+ goto err;
+ }
+ }
+ INFO(("'sound arena module' detected.\n"));
+ }
+err:
+ /* the rest for saa7146: you should definitely set some basic values
+ for the input-port handling of the saa7146. */
+
+ /* ext->saa has been filled by the core driver */
+
+ /* some stuff is done via variables */
+ saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source, input_port_selection[mxb->cur_input].hps_sync);
+
+ /* some stuff is done via direct write to the registers */
+
+ /* this is ugly, but because of the fact that this is completely
+ hardware dependend, it should be done directly... */
+ saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+ saa7146_write(dev, DD1_INIT, 0x02000200);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+ return 0;
+}
+
+/* interrupt-handler. this gets called when irq_mask is != 0.
+ it must clear the interrupt-bits in irq_mask it has handled */
+/*
+void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask)
+{
+ struct mxb* mxb = (struct mxb*)dev->ext_priv;
+}
+*/
+
+static struct saa7146_ext_vv vv_data;
+
+/* this function only gets called when the probing was successful */
+static int mxb_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
+{
+ struct mxb* mxb = (struct mxb*)dev->ext_priv;
+
+ DEB_EE(("dev:%p\n",dev));
+
+ /* checking for i2c-devices can be omitted here, because we
+ already did this in "mxb_vl42_probe" */
+
+ saa7146_vv_init(dev,&vv_data);
+ if( 0 != saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) {
+ ERR(("cannot register capture v4l2 device. skipping.\n"));
+ return -1;
+ }
+
+ /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/
+ if( 0 != MXB_BOARD_CAN_DO_VBI(dev)) {
+ if( 0 != saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) {
+ ERR(("cannot register vbi v4l2 device. skipping.\n"));
+ }
+ }
+
+ i2c_use_client(mxb->tea6420_1);
+ i2c_use_client(mxb->tea6420_2);
+ i2c_use_client(mxb->tea6415c);
+ i2c_use_client(mxb->tda9840);
+ i2c_use_client(mxb->saa7111a);
+ i2c_use_client(mxb->tuner);
+
+ printk("mxb: found 'Multimedia eXtension Board'-%d.\n",mxb_num);
+
+ mxb_num++;
+ mxb_init_done(dev);
+ return 0;
+}
+
+static int mxb_detach(struct saa7146_dev* dev)
+{
+ struct mxb* mxb = (struct mxb*)dev->ext_priv;
+
+ DEB_EE(("dev:%p\n",dev));
+
+ i2c_release_client(mxb->tea6420_1);
+ i2c_release_client(mxb->tea6420_2);
+ i2c_release_client(mxb->tea6415c);
+ i2c_release_client(mxb->tda9840);
+ i2c_release_client(mxb->saa7111a);
+ i2c_release_client(mxb->tuner);
+
+ saa7146_unregister_device(&mxb->video_dev,dev);
+ if( 0 != MXB_BOARD_CAN_DO_VBI(dev)) {
+ saa7146_unregister_device(&mxb->vbi_dev,dev);
+ }
+ saa7146_vv_release(dev);
+
+ mxb_num--;
+
+ i2c_del_adapter(&mxb->i2c_adapter);
+ kfree(mxb);
+
+ return 0;
+}
+
+static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct mxb* mxb = (struct mxb*)dev->ext_priv;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ switch(cmd) {
+ case VIDIOC_ENUMINPUT:
+ {
+ struct v4l2_input *i = arg;
+
+ DEB_EE(("VIDIOC_ENUMINPUT %d.\n",i->index));
+ if( i->index < 0 || i->index >= MXB_INPUTS) {
+ return -EINVAL;
+ }
+ memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input));
+
+ return 0;
+ }
+ /* the saa7146 provides some controls (brightness, contrast, saturation)
+ which gets registered *after* this function. because of this we have
+ to return with a value != 0 even if the function succeded.. */
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *qc = arg;
+ int i;
+
+ for (i = MAXCONTROLS - 1; i >= 0; i--) {
+ if (mxb_controls[i].id == qc->id) {
+ *qc = mxb_controls[i];
+ DEB_D(("VIDIOC_QUERYCTRL %d.\n",qc->id));
+ return 0;
+ }
+ }
+ return -EAGAIN;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *vc = arg;
+ int i;
+
+ for (i = MAXCONTROLS - 1; i >= 0; i--) {
+ if (mxb_controls[i].id == vc->id) {
+ break;
+ }
+ }
+
+ if( i < 0 ) {
+ return -EAGAIN;
+ }
+
+ switch (vc->id ) {
+ case V4L2_CID_AUDIO_MUTE: {
+ vc->value = mxb->cur_mute;
+ DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n",vc->value));
+ return 0;
+ }
+ }
+
+ DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n",vc->value));
+ return 0;
+ }
+
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *vc = arg;
+ int i = 0;
+
+ for (i = MAXCONTROLS - 1; i >= 0; i--) {
+ if (mxb_controls[i].id == vc->id) {
+ break;
+ }
+ }
+
+ if( i < 0 ) {
+ return -EAGAIN;
+ }
+
+ switch (vc->id ) {
+ case V4L2_CID_AUDIO_MUTE: {
+ mxb->cur_mute = vc->value;
+ if( 0 == vc->value ) {
+ /* switch the audio-source */
+ mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[mxb->cur_input]][0]);
+ mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[mxb->cur_input]][1]);
+ } else {
+ mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[6][0]);
+ mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[6][1]);
+ }
+ DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n",vc->value));
+ break;
+ }
+ }
+ return 0;
+ }
+ case VIDIOC_G_INPUT:
+ {
+ int *input = (int *)arg;
+ *input = mxb->cur_input;
+
+ DEB_EE(("VIDIOC_G_INPUT %d.\n",*input));
+ return 0;
+ }
+ case VIDIOC_S_INPUT:
+ {
+ int input = *(int *)arg;
+ struct tea6415c_multiplex vm;
+ int i = 0;
+
+ DEB_EE(("VIDIOC_S_INPUT %d.\n",input));
+
+ if (input < 0 || input >= MXB_INPUTS) {
+ return -EINVAL;
+ }
+
+ /* fixme: locke das setzen des inputs mit hilfe des mutexes
+ down(&dev->lock);
+ video_mux(dev,*i);
+ up(&dev->lock);
+ */
+
+ /* fixme: check if streaming capture
+ if ( 0 != dev->streaming ) {
+ DEB_D(("VIDIOC_S_INPUT illegal while streaming.\n"));
+ return -EPERM;
+ }
+ */
+
+ mxb->cur_input = input;
+
+ saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, input_port_selection[input].hps_sync);
+
+ /* prepare switching of tea6415c and saa7111a;
+ have a look at the 'background'-file for further informations */
+ switch( input ) {
+
+ case TUNER:
+ {
+ i = 0;
+ vm.in = 3;
+ vm.out = 17;
+
+ if ( 0 != mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm)) {
+ printk("VIDIOC_S_INPUT: could not address tea6415c #1\n");
+ return -EFAULT;
+ }
+ /* connect tuner-output always to multicable */
+ vm.in = 3;
+ vm.out = 13;
+ break;
+ }
+ case AUX3_YC:
+ {
+ /* nothing to be done here. aux3_yc is
+ directly connected to the saa711a */
+ i = 5;
+ break;
+ }
+ case AUX3:
+ {
+ /* nothing to be done here. aux3 is
+ directly connected to the saa711a */
+ i = 1;
+ break;
+ }
+ case AUX1:
+ {
+ i = 0;
+ vm.in = 1;
+ vm.out = 17;
+ break;
+ }
+ }
+
+ /* switch video in tea6415c only if necessary */
+ switch( input ) {
+ case TUNER:
+ case AUX1:
+ {
+ if ( 0 != mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm)) {
+ printk("VIDIOC_S_INPUT: could not address tea6415c #3\n");
+ return -EFAULT;
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ /* switch video in saa7111a */
+ if ( 0 != mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_INPUT, &i)) {
+ printk("VIDIOC_S_INPUT: could not address saa7111a #1.\n");
+ }
+
+ /* switch the audio-source only if necessary */
+ if( 0 == mxb->cur_mute ) {
+ mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[input]][0]);
+ mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[input]][1]);
+ }
+
+ return 0;
+ }
+ case VIDIOC_G_TUNER:
+ {
+ struct v4l2_tuner *t = arg;
+ int byte = 0;
+
+ if( 0 != t->index ) {
+ DEB_D(("VIDIOC_G_TUNER: channel %d does not have a tuner attached.\n", t->index));
+ return -EINVAL;
+ }
+
+ DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index));
+
+ memset(t,0,sizeof(*t));
+ strcpy(t->name, "Television");
+
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+ t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */
+ t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */
+ /* FIXME: add the real signal strength here */
+ t->signal = 0xffff;
+ t->afc = 0;
+
+ mxb->tda9840->driver->command(mxb->tda9840,TDA9840_DETECT, &byte);
+ t->audmode = mxb->cur_mode;
+
+ if( byte < 0 ) {
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ } else {
+ switch(byte) {
+ case TDA9840_MONO_DETECT: {
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_MONO.\n"));
+ break;
+ }
+ case TDA9840_DUAL_DETECT: {
+ t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_LANG1.\n"));
+ break;
+ }
+ case TDA9840_STEREO_DETECT: {
+ t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
+ DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_STEREO.\n"));
+ break;
+ }
+ default: { /* TDA9840_INCORRECT_DETECT */
+ t->rxsubchans = V4L2_TUNER_MODE_MONO;
+ DEB_D(("VIDIOC_G_TUNER: TDA9840_INCORRECT_DETECT => V4L2_TUNER_MODE_MONO\n"));
+ break;
+ }
+ }
+ }
+
+ return 0;
+ }
+ case VIDIOC_S_TUNER:
+ {
+ struct v4l2_tuner *t = arg;
+ int result = 0;
+ int byte = 0;
+
+ if( 0 != t->index ) {
+ DEB_D(("VIDIOC_S_TUNER: channel %d does not have a tuner attached.\n",t->index));
+ return -EINVAL;
+ }
+
+ switch(t->audmode) {
+ case V4L2_TUNER_MODE_STEREO: {
+ mxb->cur_mode = V4L2_TUNER_MODE_STEREO;
+ byte = TDA9840_SET_STEREO;
+ DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n"));
+ break;
+ }
+ case V4L2_TUNER_MODE_LANG1: {
+ mxb->cur_mode = V4L2_TUNER_MODE_LANG1;
+ byte = TDA9840_SET_LANG1;
+ DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n"));
+ break;
+ }
+ case V4L2_TUNER_MODE_LANG2: {
+ mxb->cur_mode = V4L2_TUNER_MODE_LANG2;
+ byte = TDA9840_SET_LANG2;
+ DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n"));
+ break;
+ }
+ default: { /* case V4L2_TUNER_MODE_MONO: {*/
+ mxb->cur_mode = V4L2_TUNER_MODE_MONO;
+ byte = TDA9840_SET_MONO;
+ DEB_D(("VIDIOC_S_TUNER: TDA9840_SET_MONO\n"));
+ break;
+ }
+ }
+
+ if( 0 != (result = mxb->tda9840->driver->command(mxb->tda9840, TDA9840_SWITCH, &byte))) {
+ printk("VIDIOC_S_TUNER error. result:%d, byte:%d\n",result,byte);
+ }
+
+ return 0;
+ }
+ case VIDIOC_G_FREQUENCY:
+ {
+ struct v4l2_frequency *f = arg;
+
+ if(0 != mxb->cur_input) {
+ DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n",mxb->cur_input));
+ return -EINVAL;
+ }
+
+ *f = mxb->cur_freq;
+
+ DEB_EE(("VIDIOC_G_FREQ: freq:0x%08x.\n", mxb->cur_freq.frequency));
+ return 0;
+ }
+ case VIDIOC_S_FREQUENCY:
+ {
+ struct v4l2_frequency *f = arg;
+
+ if (0 != f->tuner)
+ return -EINVAL;
+
+ if (V4L2_TUNER_ANALOG_TV != f->type)
+ return -EINVAL;
+
+ if(0 != mxb->cur_input) {
+ DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n",mxb->cur_input));
+ return -EINVAL;
+ }
+
+ mxb->cur_freq = *f;
+ DEB_EE(("VIDIOC_S_FREQUENCY: freq:0x%08x.\n", mxb->cur_freq.frequency));
+
+ /* tune in desired frequency */
+ mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_FREQUENCY, &mxb->cur_freq);
+
+ /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */
+ spin_lock(&dev->slock);
+ vv->vbi_fieldcount = 0;
+ spin_unlock(&dev->slock);
+
+ return 0;
+ }
+ case MXB_S_AUDIO_CD:
+ {
+ int i = *(int*)arg;
+
+ if( i < 0 || i >= MXB_AUDIOS ) {
+ DEB_D(("illegal argument to MXB_S_AUDIO_CD: i:%d.\n",i));
+ return -EINVAL;
+ }
+
+ DEB_EE(("MXB_S_AUDIO_CD: i:%d.\n",i));
+
+ mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_cd[i][0]);
+ mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_cd[i][1]);
+
+ return 0;
+ }
+ case MXB_S_AUDIO_LINE:
+ {
+ int i = *(int*)arg;
+
+ if( i < 0 || i >= MXB_AUDIOS ) {
+ DEB_D(("illegal argument to MXB_S_AUDIO_LINE: i:%d.\n",i));
+ return -EINVAL;
+ }
+
+ DEB_EE(("MXB_S_AUDIO_LINE: i:%d.\n",i));
+ mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[i][0]);
+ mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[i][1]);
+
+ return 0;
+ }
+ case VIDIOC_G_AUDIO:
+ {
+ struct v4l2_audio *a = arg;
+
+ if( a->index < 0 || a->index > MXB_INPUTS ) {
+ DEB_D(("VIDIOC_G_AUDIO %d out of range.\n",a->index));
+ return -EINVAL;
+ }
+
+ DEB_EE(("VIDIOC_G_AUDIO %d.\n",a->index));
+ memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio));
+
+ return 0;
+ }
+ case VIDIOC_S_AUDIO:
+ {
+ struct v4l2_audio *a = arg;
+ DEB_D(("VIDIOC_S_AUDIO %d.\n",a->index));
+ return 0;
+ }
+ default:
+/*
+ DEB2(printk("does not handle this ioctl.\n"));
+*/
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std)
+{
+ struct mxb* mxb = (struct mxb*)dev->ext_priv;
+ int zero = 0;
+ int one = 1;
+
+ if(V4L2_STD_PAL_I == std->id ) {
+ DEB_D(("VIDIOC_S_STD: setting mxb for PAL_I.\n"));
+ /* set the 7146 gpio register -- I don't know what this does exactly */
+ saa7146_write(dev, GPIO_CTRL, 0x00404050);
+ /* unset the 7111 gpio register -- I don't know what this does exactly */
+ mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_GPIO, &zero);
+ } else {
+ DEB_D(("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM.\n"));
+ /* set the 7146 gpio register -- I don't know what this does exactly */
+ saa7146_write(dev, GPIO_CTRL, 0x00404050);
+ /* set the 7111 gpio register -- I don't know what this does exactly */
+ mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_GPIO, &one);
+ }
+ return 0;
+}
+
+static struct saa7146_standard standard[] = {
+ {
+ .name = "PAL-BG", .id = V4L2_STD_PAL_BG,
+ .v_offset = 0x17, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 680,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "PAL-I", .id = V4L2_STD_PAL_I,
+ .v_offset = 0x17, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 680,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "NTSC", .id = V4L2_STD_NTSC,
+ .v_offset = 0x16, .v_field = 240,
+ .h_offset = 0x06, .h_pixels = 708,
+ .v_max_out = 480, .h_max_out = 640,
+ }, {
+ .name = "SECAM", .id = V4L2_STD_SECAM,
+ .v_offset = 0x14, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 720,
+ .v_max_out = 576, .h_max_out = 768,
+ }
+};
+
+static struct saa7146_pci_extension_data mxb = {
+ .ext_priv = "Multimedia eXtension Board",
+ .ext = &extension,
+};
+
+static struct pci_device_id pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x0000,
+ .subdevice = 0x0000,
+ .driver_data = (unsigned long)&mxb,
+ }, {
+ .vendor = 0,
+ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_ext_vv vv_data = {
+ .inputs = MXB_INPUTS,
+ .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE,
+ .stds = &standard[0],
+ .num_stds = sizeof(standard)/sizeof(struct saa7146_standard),
+ .std_callback = &std_callback,
+ .ioctls = &ioctls[0],
+ .ioctl = mxb_ioctl,
+};
+
+static struct saa7146_extension extension = {
+ .name = MXB_IDENTIFIER,
+ .flags = SAA7146_USE_I2C_IRQ,
+
+ .pci_tbl = &pci_tbl[0],
+ .module = THIS_MODULE,
+
+ .probe = mxb_probe,
+ .attach = mxb_attach,
+ .detach = mxb_detach,
+
+ .irq_mask = 0,
+ .irq_func = NULL,
+};
+
+static int __init mxb_init_module(void)
+{
+ if( 0 != saa7146_register_extension(&extension)) {
+ DEB_S(("failed to register extension.\n"));
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit mxb_cleanup_module(void)
+{
+ saa7146_unregister_extension(&extension);
+}
+
+module_init(mxb_init_module);
+module_exit(mxb_cleanup_module);
+
+MODULE_DESCRIPTION("video4linux-2 driver for the Siemens-Nixdorf 'Multimedia eXtension board'");
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/mxb.h b/linux/drivers/media/video/mxb.h
new file mode 100644
index 000000000..400a57ba6
--- /dev/null
+++ b/linux/drivers/media/video/mxb.h
@@ -0,0 +1,42 @@
+#ifndef __MXB__
+#define __MXB__
+
+#define BASE_VIDIOC_MXB 10
+
+#define MXB_S_AUDIO_CD _IOW ('V', BASE_VIDIOC_PRIVATE+BASE_VIDIOC_MXB+0, int)
+#define MXB_S_AUDIO_LINE _IOW ('V', BASE_VIDIOC_PRIVATE+BASE_VIDIOC_MXB+1, int)
+
+#define MXB_IDENTIFIER "Multimedia eXtension Board"
+
+#define MXB_AUDIOS 6
+
+/* these are the available audio sources, which can switched
+ to the line- and cd-output individually */
+static struct v4l2_audio mxb_audios[MXB_AUDIOS] = {
+ {
+ .index = 0,
+ .name = "Tuner",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 1,
+ .name = "AUX1",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 2,
+ .name = "AUX2",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 3,
+ .name = "AUX3",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 4,
+ .name = "Radio (X9)",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 5,
+ .name = "CD-ROM (X10)",
+ .capability = V4L2_AUDCAP_STEREO,
+ }
+};
+#endif
diff --git a/linux/drivers/media/video/tda9840.c b/linux/drivers/media/video/tda9840.c
new file mode 100644
index 000000000..f39e4f54c
--- /dev/null
+++ b/linux/drivers/media/video/tda9840.c
@@ -0,0 +1,253 @@
+ /*
+ tda9840 - i2c-driver for the tda9840 by SGS Thomson
+
+ Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
+
+ The tda9840 is a stereo/dual sound processor with digital
+ identification. It can be found at address 0x84 on the i2c-bus.
+
+ For detailed informations download the specifications directly
+ from SGS Thomson at http://www.st.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/ioctl.h>
+#include <linux/i2c.h>
+
+#include "tda9840.h"
+
+static int debug = 0; /* insmod parameter */
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off).");
+#define dprintk(args...) \
+ do { if (debug) { printk("%s: %s()[%d]: ",__stringify(KBUILD_MODNAME), __FUNCTION__, __LINE__); printk(args); } } while (0)
+
+#define SWITCH 0x00
+#define LEVEL_ADJUST 0x02
+#define STEREO_ADJUST 0x03
+#define TEST 0x04
+
+/* addresses to scan, found only at 0x42 (7-Bit) */
+static unsigned short normal_i2c[] = { I2C_TDA9840, I2C_CLIENT_END };
+
+/* magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver driver;
+static struct i2c_client client_template;
+
+static int command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+ int result;
+ int byte = *(int *)arg;
+
+ switch (cmd) {
+ case TDA9840_SWITCH:
+
+ dprintk("TDA9840_SWITCH: 0x%02x\n", byte);
+
+ if (byte != TDA9840_SET_MONO
+ && byte != TDA9840_SET_MUTE
+ && byte != TDA9840_SET_STEREO
+ && byte != TDA9840_SET_LANG1
+ && byte != TDA9840_SET_LANG2
+ && byte != TDA9840_SET_BOTH
+ && byte != TDA9840_SET_BOTH_R
+ && byte != TDA9840_SET_EXTERNAL) {
+ return -EINVAL;
+ }
+
+ result = i2c_smbus_write_byte_data(client, SWITCH, byte);
+ if (result)
+ dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result);
+ break;
+
+ case TDA9840_LEVEL_ADJUST:
+
+ dprintk("TDA9840_LEVEL_ADJUST: %d\n", byte);
+
+ /* check for correct range */
+ if (byte > 25 || byte < -20)
+ return -EINVAL;
+
+ /* calculate actual value to set, see specs, page 18 */
+ byte /= 5;
+ if (0 < byte)
+ byte += 0x8;
+ else
+ byte = -byte;
+
+ result = i2c_smbus_write_byte_data(client, LEVEL_ADJUST, byte);
+ if (result)
+ dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result);
+ break;
+
+ case TDA9840_STEREO_ADJUST:
+
+ dprintk("TDA9840_STEREO_ADJUST: %d\n", byte);
+
+ /* check for correct range */
+ if (byte > 25 || byte < -24)
+ return -EINVAL;
+
+ /* calculate actual value to set */
+ byte /= 5;
+ if (0 < byte)
+ byte += 0x20;
+ else
+ byte = -byte;
+
+ result = i2c_smbus_write_byte_data(client, STEREO_ADJUST, byte);
+ if (result)
+ dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result);
+ break;
+
+ case TDA9840_DETECT: {
+ int *ret = (int *)arg;
+
+ byte = i2c_smbus_read_byte_data(client, STEREO_ADJUST);
+ if (byte == -1) {
+ dprintk("i2c_smbus_read_byte_data() failed\n");
+ return -EIO;
+ }
+
+ if (0 != (byte & 0x80)) {
+ dprintk("TDA9840_DETECT: register contents invalid\n");
+ return -EINVAL;
+ }
+
+ dprintk("TDA9840_DETECT: byte: 0x%02x\n", byte);
+ *ret = ((byte & 0x60) >> 5);
+ result = 0;
+ break;
+ }
+ case TDA9840_TEST:
+ dprintk("TDA9840_TEST: 0x%02x\n", byte);
+
+ /* mask out irrelevant bits */
+ byte &= 0x3;
+
+ result = i2c_smbus_write_byte_data(client, TEST, byte);
+ if (result)
+ dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result);
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ if (result)
+ return -EIO;
+
+ return 0;
+}
+
+static int detect(struct i2c_adapter *adapter, int address, int kind)
+{
+ struct i2c_client *client;
+ int result = 0;
+
+ int byte = 0x0;
+
+ /* let's see whether this adapter can support what we need */
+ if (0 == i2c_check_functionality(adapter,
+ I2C_FUNC_SMBUS_READ_BYTE_DATA |
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) {
+ return 0;
+ }
+
+ /* allocate memory for client structure */
+ client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (0 == client) {
+ printk("not enough kernel memory\n");
+ return -ENOMEM;
+ }
+
+ /* fill client structure */
+ memcpy(client, &client_template, sizeof(struct i2c_client));
+ client->addr = address;
+ client->adapter = adapter;
+
+ /* tell the i2c layer a new client has arrived */
+ if (0 != (result = i2c_attach_client(client))) {
+ kfree(client);
+ return result;
+ }
+
+ /* set initial values for level & stereo - adjustment, mode */
+ byte = 0;
+ result = command(client, TDA9840_LEVEL_ADJUST, &byte);
+ result += command(client, TDA9840_STEREO_ADJUST, &byte);
+ byte = TDA9840_SET_MONO;
+ result = command(client, TDA9840_SWITCH, &byte);
+ if (result) {
+ dprintk("could not initialize tda9840\n");
+ return -ENODEV;
+ }
+
+ printk("tda9840: detected @ 0x%02x on adapter %s\n", address, &client->adapter->name[0]);
+ return 0;
+}
+
+static int attach(struct i2c_adapter *adapter)
+{
+ /* let's see whether this is a know adapter we can attach to */
+ if (adapter->id != I2C_HW_SAA7146) {
+ dprintk("refusing to probe on unknown adapter [name='%s',id=0x%x]\n", adapter->name, adapter->id);
+ return -ENODEV;
+ }
+
+ return i2c_probe(adapter, &addr_data, &detect);
+}
+
+static int detach(struct i2c_client *client)
+{
+ int ret = i2c_detach_client(client);
+ kfree(client);
+ return ret;
+}
+
+static struct i2c_driver driver = {
+ .owner = THIS_MODULE,
+ .name = "tda9840",
+ .id = I2C_DRIVERID_TDA9840,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = attach,
+ .detach_client = detach,
+ .command = command,
+};
+
+static struct i2c_client client_template = {
+ .name = "tda9840",
+ .driver = &driver,
+};
+
+static int __init this_module_init(void)
+{
+ return i2c_add_driver(&driver);
+}
+
+static void __exit this_module_exit(void)
+{
+ i2c_del_driver(&driver);
+}
+
+module_init(this_module_init);
+module_exit(this_module_exit);
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("tda9840 driver");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/tda9840.h b/linux/drivers/media/video/tda9840.h
new file mode 100644
index 000000000..28021053b
--- /dev/null
+++ b/linux/drivers/media/video/tda9840.h
@@ -0,0 +1,35 @@
+#ifndef __INCLUDED_TDA9840__
+#define __INCLUDED_TDA9840__
+
+#define I2C_TDA9840 0x42
+
+#define TDA9840_DETECT _IOR('v',1,int)
+/* return values for TDA9840_DETCT */
+#define TDA9840_MONO_DETECT 0x0
+#define TDA9840_DUAL_DETECT 0x1
+#define TDA9840_STEREO_DETECT 0x2
+#define TDA9840_INCORRECT_DETECT 0x3
+
+#define TDA9840_SWITCH _IOW('v',2,int)
+/* modes than can be set with TDA9840_SWITCH */
+#define TDA9840_SET_MUTE 0x00
+#define TDA9840_SET_MONO 0x10
+#define TDA9840_SET_STEREO 0x2a
+#define TDA9840_SET_LANG1 0x12
+#define TDA9840_SET_LANG2 0x1e
+#define TDA9840_SET_BOTH 0x1a
+#define TDA9840_SET_BOTH_R 0x16
+#define TDA9840_SET_EXTERNAL 0x7a
+
+/* values may range between +2.5 and -2.0;
+ the value has to be multiplied with 10 */
+#define TDA9840_LEVEL_ADJUST _IOW('v',3,int)
+
+/* values may range between +2.5 and -2.4;
+ the value has to be multiplied with 10 */
+#define TDA9840_STEREO_ADJUST _IOW('v',4,int)
+
+/* currently not implemented */
+#define TDA9840_TEST _IOW('v',5,int)
+
+#endif
diff --git a/linux/drivers/media/video/tea6415c.c b/linux/drivers/media/video/tea6415c.c
new file mode 100644
index 000000000..ad09e4c1c
--- /dev/null
+++ b/linux/drivers/media/video/tea6415c.c
@@ -0,0 +1,222 @@
+ /*
+ tea6415c - i2c-driver for the tea6415c by SGS Thomson
+
+ Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
+
+ The tea6415c is a bus controlled video-matrix-switch
+ with 8 inputs and 6 outputs.
+ It is cascadable, i.e. it can be found at the addresses
+ 0x86 and 0x06 on the i2c-bus.
+
+ For detailed informations download the specifications directly
+ from SGS Thomson at http://www.st.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License vs 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 Mvss Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/ioctl.h>
+#include <linux/i2c.h>
+
+#include "tea6415c.h"
+
+static int debug = 0; /* insmod parameter */
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off).");
+#define dprintk(args...) \
+ do { if (debug) { printk("%s: %s()[%d]: ",__stringify(KBUILD_MODNAME), __FUNCTION__, __LINE__); printk(args); } } while (0)
+
+#define TEA6415C_NUM_INPUTS 8
+#define TEA6415C_NUM_OUTPUTS 6
+
+/* addresses to scan, found only at 0x03 and/or 0x43 (7-bit) */
+static unsigned short normal_i2c[] = { I2C_TEA6415C_1, I2C_TEA6415C_2, I2C_CLIENT_END };
+
+/* magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver driver;
+static struct i2c_client client_template;
+
+/* this function is called by i2c_probe */
+static int detect(struct i2c_adapter *adapter, int address, int kind)
+{
+ struct i2c_client *client = NULL;
+ int err = 0;
+
+ /* let's see whether this adapter can support what we need */
+ if (0 == i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) {
+ return 0;
+ }
+
+ /* allocate memory for client structure */
+ client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (0 == client) {
+ return -ENOMEM;
+ }
+
+ /* fill client structure */
+ memcpy(client, &client_template, sizeof(struct i2c_client));
+ client->addr = address;
+ client->adapter = adapter;
+
+ /* tell the i2c layer a new client has arrived */
+ if (0 != (err = i2c_attach_client(client))) {
+ kfree(client);
+ return err;
+ }
+
+ printk("tea6415c: detected @ 0x%02x on adapter %s\n", address, &client->adapter->name[0]);
+
+ return 0;
+}
+
+static int attach(struct i2c_adapter *adapter)
+{
+ /* let's see whether this is a know adapter we can attach to */
+ if (adapter->id != I2C_HW_SAA7146) {
+ dprintk("refusing to probe on unknown adapter [name='%s',id=0x%x]\n", adapter->name, adapter->id);
+ return -ENODEV;
+ }
+
+ return i2c_probe(adapter, &addr_data, &detect);
+}
+
+static int detach(struct i2c_client *client)
+{
+ int ret = i2c_detach_client(client);
+ kfree(client);
+ return ret;
+}
+
+/* makes a connection between the input-pin 'i' and the output-pin 'o'
+ for the tea6415c-client 'client' */
+static int switch_matrix(struct i2c_client *client, int i, int o)
+{
+ u8 byte = 0;
+ int ret;
+
+ dprintk("adr:0x%02x, i:%d, o:%d\n", client->addr, i, o);
+
+ /* check if the pins are valid */
+ if (0 == ((1 == i || 3 == i || 5 == i || 6 == i || 8 == i || 10 == i || 20 == i || 11 == i)
+ && (18 == o || 17 == o || 16 == o || 15 == o || 14 == o || 13 == o)))
+ return -1;
+
+ /* to understand this, have a look at the tea6415c-specs (p.5) */
+ switch (o) {
+ case 18:
+ byte = 0x00;
+ break;
+ case 14:
+ byte = 0x20;
+ break;
+ case 16:
+ byte = 0x10;
+ break;
+ case 17:
+ byte = 0x08;
+ break;
+ case 15:
+ byte = 0x18;
+ break;
+ case 13:
+ byte = 0x28;
+ break;
+ };
+
+ switch (i) {
+ case 5:
+ byte |= 0x00;
+ break;
+ case 8:
+ byte |= 0x04;
+ break;
+ case 3:
+ byte |= 0x02;
+ break;
+ case 20:
+ byte |= 0x06;
+ break;
+ case 6:
+ byte |= 0x01;
+ break;
+ case 10:
+ byte |= 0x05;
+ break;
+ case 1:
+ byte |= 0x03;
+ break;
+ case 11:
+ byte |= 0x07;
+ break;
+ };
+
+ ret = i2c_smbus_write_byte(client, byte);
+ if (ret) {
+ dprintk("i2c_smbus_write_byte() failed, ret:%d\n", ret);
+ return -EIO;
+ }
+
+ return ret;
+}
+
+static int command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+ struct tea6415c_multiplex *v = (struct tea6415c_multiplex *)arg;
+ int result = 0;
+
+ switch (cmd) {
+ case TEA6415C_SWITCH:
+ result = switch_matrix(client, v->in, v->out);
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return result;
+}
+
+static struct i2c_driver driver = {
+ .owner = THIS_MODULE,
+ .name = "tea6415c",
+ .id = I2C_DRIVERID_TEA6415C,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = attach,
+ .detach_client = detach,
+ .command = command,
+};
+
+static struct i2c_client client_template = {
+ .name = "tea6415c",
+ .driver = &driver,
+};
+
+static int __init this_module_init(void)
+{
+ return i2c_add_driver(&driver);
+}
+
+static void __exit this_module_exit(void)
+{
+ i2c_del_driver(&driver);
+}
+
+module_init(this_module_init);
+module_exit(this_module_exit);
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("tea6415c driver");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/tea6415c.h b/linux/drivers/media/video/tea6415c.h
new file mode 100644
index 000000000..f84ed8005
--- /dev/null
+++ b/linux/drivers/media/video/tea6415c.h
@@ -0,0 +1,39 @@
+#ifndef __INCLUDED_TEA6415C__
+#define __INCLUDED_TEA6415C__
+
+/* possible i2c-addresses */
+#define I2C_TEA6415C_1 0x03
+#define I2C_TEA6415C_2 0x43
+
+/* the tea6415c's design is quite brain-dead. although there are
+ 8 inputs and 6 outputs, these aren't enumerated in any way. because
+ I don't want to say "connect input pin 20 to output pin 17", I define
+ a "virtual" pin-order. */
+
+/* input pins */
+#define TEA6415C_OUTPUT1 18
+#define TEA6415C_OUTPUT2 14
+#define TEA6415C_OUTPUT3 16
+#define TEA6415C_OUTPUT4 17
+#define TEA6415C_OUTPUT5 13
+#define TEA6415C_OUTPUT6 15
+
+/* output pins */
+#define TEA6415C_INPUT1 5
+#define TEA6415C_INPUT2 8
+#define TEA6415C_INPUT3 3
+#define TEA6415C_INPUT4 20
+#define TEA6415C_INPUT5 6
+#define TEA6415C_INPUT6 10
+#define TEA6415C_INPUT7 1
+#define TEA6415C_INPUT8 11
+
+struct tea6415c_multiplex
+{
+ int in; /* input-pin */
+ int out; /* output-pin */
+};
+
+#define TEA6415C_SWITCH _IOW('v',1,struct tea6415c_multiplex)
+
+#endif
diff --git a/linux/drivers/media/video/tea6420.c b/linux/drivers/media/video/tea6420.c
new file mode 100644
index 000000000..dd8279ad5
--- /dev/null
+++ b/linux/drivers/media/video/tea6420.c
@@ -0,0 +1,199 @@
+ /*
+ tea6420 - i2c-driver for the tea6420 by SGS Thomson
+
+ Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
+
+ The tea6420 is a bus controlled audio-matrix with 5 stereo inputs,
+ 4 stereo outputs and gain control for each output.
+ It is cascadable, i.e. it can be found at the adresses 0x98
+ and 0x9a on the i2c-bus.
+
+ For detailed informations download the specifications directly
+ from SGS Thomson at http://www.st.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/ioctl.h>
+#include <linux/i2c.h>
+
+#include "tea6420.h"
+
+static int debug = 0; /* insmod parameter */
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off).");
+#define dprintk(args...) \
+ do { if (debug) { printk("%s: %s()[%d]: ",__stringify(KBUILD_MODNAME), __FUNCTION__, __LINE__); printk(args); } } while (0)
+
+/* addresses to scan, found only at 0x4c and/or 0x4d (7-Bit) */
+static unsigned short normal_i2c[] = { I2C_TEA6420_1, I2C_TEA6420_2, I2C_CLIENT_END };
+
+/* magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver driver;
+static struct i2c_client client_template;
+
+/* make a connection between the input 'i' and the output 'o'
+ with gain 'g' for the tea6420-client 'client' (note: i = 6 means 'mute') */
+static int tea6420_switch(struct i2c_client *client, int i, int o, int g)
+{
+ u8 byte = 0;
+ int ret;
+
+ dprintk("adr:0x%02x, i:%d, o:%d, g:%d\n", client->addr, i, o, g);
+
+ /* check if the paramters are valid */
+ if (i < 1 || i > 6 || o < 1 || o > 4 || g < 0 || g > 6 || g % 2 != 0)
+ return -1;
+
+ byte = ((o - 1) << 5);
+ byte |= (i - 1);
+
+ /* to understand this, have a look at the tea6420-specs (p.5) */
+ switch (g) {
+ case 0:
+ byte |= (3 << 3);
+ break;
+ case 2:
+ byte |= (2 << 3);
+ break;
+ case 4:
+ byte |= (1 << 3);
+ break;
+ case 6:
+ break;
+ }
+
+ ret = i2c_smbus_write_byte(client, byte);
+ if (ret) {
+ dprintk("i2c_smbus_write_byte() failed, ret:%d\n", ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/* this function is called by i2c_probe */
+static int tea6420_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+ struct i2c_client *client;
+ int err = 0, i = 0;
+
+ /* let's see whether this adapter can support what we need */
+ if (0 == i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) {
+ return 0;
+ }
+
+ /* allocate memory for client structure */
+ client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (0 == client) {
+ return -ENOMEM;
+ }
+ memset(client, 0x0, sizeof(struct i2c_client));
+
+ /* fill client structure */
+ memcpy(client, &client_template, sizeof(struct i2c_client));
+ client->addr = address;
+ client->adapter = adapter;
+
+ /* tell the i2c layer a new client has arrived */
+ if (0 != (err = i2c_attach_client(client))) {
+ kfree(client);
+ return err;
+ }
+
+ /* set initial values: set "mute"-input to all outputs at gain 0 */
+ err = 0;
+ for (i = 1; i < 5; i++) {
+ err += tea6420_switch(client, 6, i, 0);
+ }
+ if (err) {
+ dprintk("could not initialize tea6420\n");
+ kfree(client);
+ return -ENODEV;
+ }
+
+ printk("tea6420: detected @ 0x%02x on adapter %s\n", address, &client->adapter->name[0]);
+
+ return 0;
+}
+
+static int attach(struct i2c_adapter *adapter)
+{
+ /* let's see whether this is a know adapter we can attach to */
+ if (adapter->id != I2C_HW_SAA7146) {
+ dprintk("refusing to probe on unknown adapter [name='%s',id=0x%x]\n", adapter->name, adapter->id);
+ return -ENODEV;
+ }
+
+ return i2c_probe(adapter, &addr_data, &tea6420_detect);
+}
+
+static int detach(struct i2c_client *client)
+{
+ int ret = i2c_detach_client(client);
+ kfree(client);
+ return ret;
+}
+
+static int command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+ struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg;
+ int result = 0;
+
+ switch (cmd) {
+ case TEA6420_SWITCH:
+ result = tea6420_switch(client, a->in, a->out, a->gain);
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return result;
+}
+
+static struct i2c_driver driver = {
+ .owner = THIS_MODULE,
+ .name = "tea6420",
+ .id = I2C_DRIVERID_TEA6420,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = attach,
+ .detach_client = detach,
+ .command = command,
+};
+
+static struct i2c_client client_template = {
+ .name = "tea6420",
+ .driver = &driver,
+};
+
+static int __init this_module_init(void)
+{
+ return i2c_add_driver(&driver);
+}
+
+static void __exit this_module_exit(void)
+{
+ i2c_del_driver(&driver);
+}
+
+module_init(this_module_init);
+module_exit(this_module_exit);
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("tea6420 driver");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/tea6420.h b/linux/drivers/media/video/tea6420.h
new file mode 100644
index 000000000..ea664df15
--- /dev/null
+++ b/linux/drivers/media/video/tea6420.h
@@ -0,0 +1,17 @@
+#ifndef __INCLUDED_TEA6420__
+#define __INCLUDED_TEA6420__
+
+/* possible addresses */
+#define I2C_TEA6420_1 0x4c
+#define I2C_TEA6420_2 0x4d
+
+struct tea6420_multiplex
+{
+ int in; /* input of audio switch */
+ int out; /* output of audio switch */
+ int gain; /* gain of connection */
+};
+
+#define TEA6420_SWITCH _IOW('v',1,struct tea6420_multiplex)
+
+#endif