]> git.hungrycats.org Git - linux/commitdiff
[PATCH] bring core media/video up to date with dvb changes
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Tue, 8 Apr 2003 02:40:04 +0000 (19:40 -0700)
committerLinus Torvalds <torvalds@home.transmeta.com>
Tue, 8 Apr 2003 02:40:04 +0000 (19:40 -0700)
12 files changed:
drivers/media/video/Kconfig
drivers/media/video/Makefile
drivers/media/video/dpc7146.c [new file with mode: 0644]
drivers/media/video/mxb.c [new file with mode: 0644]
drivers/media/video/mxb.h [new file with mode: 0644]
drivers/media/video/saa7111.c
drivers/media/video/tda9840.c [new file with mode: 0644]
drivers/media/video/tda9840.h [new file with mode: 0644]
drivers/media/video/tea6415c.c [new file with mode: 0644]
drivers/media/video/tea6415c.h [new file with mode: 0644]
drivers/media/video/tea6420.c [new file with mode: 0644]
drivers/media/video/tea6420.h [new file with mode: 0644]

index 8e5e36e797ad0812feb56e07df6d08ef963aeb75..be8ebca243ddd38ec93509ce4a814b8be24f18df 100644 (file)
@@ -230,5 +230,32 @@ config VIDEO_SAA7134
          whenever you want). If you want to compile it as a module, say M
          here and read <file:Documentation/modules.txt>.
 
+config VIDEO_MXB
+       tristate "Siemens-Nixdorf 'Multimedia eXtension Board'"
+       depends on VIDEO_DEV && PCI && I2C
+       ---help---
+         This is a video4linux driver for the 'Multimedia eXtension Board'
+         TV card by Siemens-Nixdorf.
+         
+         This driver is available as a module called mxb ( = code
+         which can be inserted in and removed from the running kernel
+         whenever you want). If you want to compile it as a module, say M
+         here and read <file:Documentation/modules.txt>.
+
+config VIDEO_DPC
+       tristate "Philips-Semiconductors 'dpc7146 demonstration board'"
+       depends on VIDEO_DEV && PCI && I2C
+       ---help---
+         This is a video4linux driver for the 'dpc7146 demonstration
+         board' by Philips-Semiconductors. It's the reference design
+         for SAA7146 bases boards, so if you have some unsupported
+         saa7146 based, analog video card, chances are good that it
+         will work with this skeleton driver.
+         
+         This driver is available as a module called dpc7146 ( = code
+         which can be inserted in and removed from the running kernel
+         whenever you want). If you want to compile it as a module, say M
+         here and read <file:Documentation/modules.txt>.
+
 endmenu
 
index 5bb0b014f8e4d3682de93214b5ed4123941b30a6..cbc7e09aaf8b785522911a149d5ad1e40f43fadc 100644 (file)
@@ -9,7 +9,7 @@ zoran-objs      :=      zr36120.o zr36120_i2c.o zr36120_mem.o
 obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o v4l1-compat.o
 
 obj-$(CONFIG_VIDEO_BT848) += bttv.o msp3400.o tvaudio.o \
-       tda7432.o tda9875.o tuner.o video-buf.o tda9887.o
+       tda7432.o tda9875.o
 obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o
 
 obj-$(CONFIG_VIDEO_ZR36120) += zoran.o
@@ -29,5 +29,11 @@ obj-$(CONFIG_VIDEO_CPIA) += cpia.o
 obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o
 obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o
 obj-$(CONFIG_VIDEO_MEYE) += meye.o
-obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ tuner.o tda9887.o video-buf.o
+obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
+obj-$(CONFIG_VIDEO_MXB) += saa7111.o tuner.o tda9840.o tea6415c.o tea6420.o mxb.o
+obj-$(CONFIG_VIDEO_DPC) += saa7111.o dpc7146.o
 obj-$(CONFIG_TUNER_3036) += tuner-3036.o
+
+obj-$(CONFIG_VIDEO_TUNER) += tuner.o tda9887.o
+obj-$(CONFIG_VIDEO_BUF)   += video-buf.o
+
diff --git a/drivers/media/video/dpc7146.c b/drivers/media/video/dpc7146.c
new file mode 100644 (file)
index 0000000..ee2eb28
--- /dev/null
@@ -0,0 +1,391 @@
+/*
+    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 */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+       #define KBUILD_MODNAME dpc7146
+#endif
+
+#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_PARM(debug,"i");
+MODULE_PARM_DESC(debug, "debug verbosity");
+
+/* global variables */
+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 = 0;    
+       int i = 0;
+
+       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));
+
+       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 */
+       for(i = 0; i < I2C_CLIENT_MAX; i++) {
+               if( NULL == dpc->i2c_adapter.clients[i] ) {
+                       continue;
+               }
+               if( I2C_SAA7111A == dpc->i2c_adapter.clients[i]->addr ) 
+                       dpc->saa7111a = dpc->i2c_adapter.clients[i];
+       }
+
+       /* check if all devices are present */
+       if( 0 == dpc->saa7111a ) {
+               DEB_D(("dpc_v4l2.o: dpc_attach failed for this device.\n"));    
+               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 */
+       (struct dpc*)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;
+}
+
+/* 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);
+       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_dev *dev, unsigned int cmd, void *arg) 
+{
+       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[] = {
+       { "PAL-BG",     V4L2_STD_PAL_BG,        SAA7146_PAL_VALUES },
+       { "PAL-I",      V4L2_STD_PAL_I,         SAA7146_PAL_VALUES },
+       { "NTSC",       V4L2_STD_NTSC,          SAA7146_NTSC_VALUES },
+       { "SECAM",      V4L2_STD_SECAM,         SAA7146_SECAM_VALUES },
+};
+
+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,
+       }
+};
+
+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,
+       .ext_vv_data    = &vv_data,
+
+       .probe          = dpc_probe,
+       .attach         = dpc_attach,
+       .detach         = dpc_detach,
+
+       .irq_mask       = 0,
+       .irq_func       = NULL,
+};     
+
+int __init dpc_init_module(void) 
+{
+       if( 0 != saa7146_register_extension(&extension)) {
+               DEB_S(("failed to register extension.\n"));
+               return -ENODEV;
+       }
+       
+       return 0;
+}
+
+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/drivers/media/video/mxb.c b/drivers/media/video/mxb.c
new file mode 100644 (file)
index 0000000..c5a5e69
--- /dev/null
@@ -0,0 +1,1075 @@
+/*
+    mxb.c - 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 <linux/video_decoder.h>       /* for saa7111a */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+       #define KBUILD_MODNAME mxb
+#endif
+
+#include "mxb.h"
+#include "tea6415c.h"
+#include "tea6420.h"
+#include "tda9840.h"
+#include <media/tuner.h>
+
+#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 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_PARM(freq,"i");
+MODULE_PARM_DESC(freq, "initial frequency the tuner will be tuned to while setup");
+
+static int debug = 0;
+MODULE_PARM(debug,"i");
+MODULE_PARM_DESC(debug, "debug verbosity");
+
+#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, 1, 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_AUDIOS] =
+       { 0, 1, 2, 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_freq;       /* current frequency the tuner is tuned to */
+       int     cur_mute;       /* current mute status */
+};
+
+static int mxb_vbi_bypass(struct saa7146_dev* dev)
+{
+       struct mxb* mxb = (struct mxb*)dev->ext_priv;
+       s32 byte = 0x0;
+       int result = 0;
+
+       DEB_EE(("dev:%p\n",dev));
+
+       /* switch bypass in saa7111a, this should be done in the
+          saa7111a driver of course... */
+       if ( -1 == (result = i2c_smbus_read_byte_data(mxb->saa7111a, SAA711X_OUTPUT_CONTROL_3))) {
+               DEB_D(("could not read from saa7111a.\n"));
+               return -EFAULT;
+       }
+       byte = result;
+       byte &= 0xf0;
+       byte |= 0x0a;
+
+       if ( 0 != (result = i2c_smbus_write_byte_data(mxb->saa7111a, SAA711X_OUTPUT_CONTROL_3, byte))) {
+               DEB_D(("could not write to saa7111a.\n"));
+               return -EFAULT;
+       }
+       return 0;
+}
+
+static int mxb_probe(struct saa7146_dev* dev)
+{
+       struct mxb* mxb = 0;
+       int i = 0;      
+
+       request_module("tuner");
+       request_module("tea6420");
+       request_module("tea6415c");
+       request_module("tda9840");
+       request_module("saa7111");
+
+       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));   
+
+       /* 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));
+
+       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 */
+       for(i = 0; i < I2C_CLIENT_MAX; i++) {
+               if( NULL == mxb->i2c_adapter.clients[i] ) {
+                       continue;
+               }
+               if( I2C_TEA6420_1 == mxb->i2c_adapter.clients[i]->addr )
+                       mxb->tea6420_1 = mxb->i2c_adapter.clients[i];
+               if( I2C_TEA6420_2 == mxb->i2c_adapter.clients[i]->addr ) 
+                       mxb->tea6420_2 = mxb->i2c_adapter.clients[i];
+               if( I2C_TEA6415C_2 == mxb->i2c_adapter.clients[i]->addr ) 
+                       mxb->tea6415c = mxb->i2c_adapter.clients[i];
+               if( I2C_TDA9840 == mxb->i2c_adapter.clients[i]->addr ) 
+                       mxb->tda9840 = mxb->i2c_adapter.clients[i];
+               if( I2C_SAA7111A == mxb->i2c_adapter.clients[i]->addr ) 
+                       mxb->saa7111a = mxb->i2c_adapter.clients[i];
+               if( 0x60 == mxb->i2c_adapter.clients[i]->addr ) 
+                       mxb->tuner = mxb->i2c_adapter.clients[i];
+       }
+
+       /* 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. are you sure you\n");
+               printk("mxb: insmod'ed tea6420, tea6415c, saa7111, tea6415c and tuner?\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 */
+       (struct mxb*)dev->ext_priv = mxb;
+
+       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 mxb_init_done(struct saa7146_dev* dev)
+{
+       struct mxb* mxb = (struct mxb*)dev->ext_priv;
+       
+       struct {
+               int     length;
+               char    data[9];
+       } 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} }
+       };
+       
+       unsigned char init[25] = {
+               0x00,
+               
+               0x00,     /* 00 - ID byte */
+               0x00,     /* 01 - reserved */
+
+               /*front end */
+               0xd8,     /* 02 - FUSE=x, GUDL=x, MODE=x */
+               0x23,     /* 03 - HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */
+               0x00,     /* 04 - GAI1=256 */
+               0x00,     /* 05 - GAI2=256 */
+
+               /* decoder */
+               0xf0,     /* 06 - HSB at  xx(50Hz) /  xx(60Hz) pixels after end of last line */
+               0x30,     /* 07 - HSS at  xx(50Hz) /  xx(60Hz) pixels after end of last line */
+               0xa8,     /* 08 - AUFD=x, FSEL=x, EXFIL=x, VTRC=x, HPLL=x, VNOI=x */
+               0x02,     /* 09 - BYPS=x, PREF=x, BPSS=x, VBLB=x, UPTCV=x, APER=x */
+               0x80,     /* 0a - BRIG=128 */
+               0x47,     /* 0b - CONT=1.109 */
+               0x40,     /* 0c - SATN=1.0 */
+               0x00,     /* 0d - HUE=0 */
+               0x01,     /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */
+               0x00,     /* 0f - reserved */
+               0xd0,     /* 10 - OFTS=x, HDEL=x, VRLN=x, YDEL=x */
+               0x8c,     /* 11 - GPSW=x, CM99=x, FECO=x, COMPO=x, OEYC=1, OEHV=1, VIPB=0, COLO=0 */
+               0x80,     /* 12 - xx output control 2 */
+               0x30,     /* 13 - xx output control 3 */
+               0x00,     /* 14 - reserved */
+               0x15,     /* 15 - VBI */
+               0x04,     /* 16 - VBI */
+               0x00,     /* 17 - VBI */
+       };
+
+       struct i2c_msg msg;
+
+       int i = 0, err = 0;
+       struct  tea6415c_multiplex vm;  
+
+       /* write configuration to saa7111a */
+       i = i2c_master_send(mxb->saa7111a, init, sizeof(init));
+       if (i < 0) {
+               printk("failed to initialize saa7111a. this should never happen.\n");
+       }
+
+       /* select tuner-output on saa7111a */
+       i = 0;
+       mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_INPUT, &i);
+//     i = VIDEO_MODE_PAL;
+//     mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_NORM, &i);
+
+       mxb_vbi_bypass(dev);
+
+       /* select a tuner type */
+       i = 5; 
+       mxb->tuner->driver->command(mxb->tuner,TUNER_SET_TYPE, &i);
+       
+       /* 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);
+                               
+       /* tune in some frequency on tuner */
+       mxb->tuner->driver->command(mxb->tuner, VIDIOCSFREQ, &freq);
+
+       /* the rest for mxb */
+       mxb->cur_input = 0;
+       mxb->cur_freq = freq;
+       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 = saa7740_init[0].length;
+       msg.buf = &saa7740_init[0].data[0];
+
+       if( 1 == (err = i2c_transfer(&mxb->i2c_adapter, &msg, 1))) {
+               for(i = 1;;i++) {
+                       msg.len = saa7740_init[i].length;               
+                       if( -1 == msg.len ) {
+                               break;
+                       }
+                       msg.buf = &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;
+}
+*/
+
+/* 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);
+       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;
+}
+
+/* hack: this should go into saa711x */
+static int saa7111_set_gpio(struct saa7146_dev *dev, int bl)
+{
+       struct mxb* mxb = (struct mxb*)dev->ext_priv;
+       s32 byte = 0x0;
+       int result = 0;
+       
+       DEB_EE(("dev:%p\n",dev));
+
+       /* get the old register contents */
+       if ( -1 == (byte = i2c_smbus_read_byte_data(mxb->saa7111a, SAA711X_OUTPUT_CONTROL_1))) {
+               DEB_D(("could not read from saa711x\n"));
+               return -EFAULT;
+       }
+       
+       if( 0 == bl ) {
+               byte &= 0x7f;
+       } else {
+               byte |= 0x80;
+       }
+
+       /* write register contents back */
+       if ( 0 != (result = i2c_smbus_write_byte_data(mxb->saa7111a, SAA711X_OUTPUT_CONTROL_1, byte))) {
+               DEB_D(("could not write to saa711x\n"));
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int mxb_ioctl(struct saa7146_dev *dev, unsigned int cmd, void *arg) 
+{
+       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;             
+
+               byte = mxb->tda9840->driver->command(mxb->tda9840,TDA9840_DETECT, NULL);
+               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;
+               }
+
+               memset(f,0,sizeof(*f));
+               f->type = V4L2_TUNER_ANALOG_TV;
+               f->frequency =  mxb->cur_freq;
+
+               DEB_EE(("VIDIOC_G_FREQ: freq:0x%08x.\n", mxb->cur_freq));
+               return 0;
+       }
+       case VIDIOC_S_FREQUENCY:
+       {
+               struct v4l2_frequency *f = arg;
+               int t_locked = 0;
+               int v_byte = 0;
+
+               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;
+               }
+
+               DEB_EE(("VIDIOC_S_FREQUENCY: freq:0x%08x.\n",f->frequency));
+
+               mxb->cur_freq = f->frequency;
+
+               /* tune in desired frequency */                 
+               mxb->tuner->driver->command(mxb->tuner, VIDIOCSFREQ, &mxb->cur_freq);
+
+               /* check if pll of tuner & saa7111a is locked */
+//             mxb->tuner->driver->command(mxb->tuner,TUNER_IS_LOCKED, &t_locked);
+               mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_GET_STATUS, &v_byte);
+
+               /* not locked -- anything to do here ? */
+               if( 0 == t_locked || 0 == (v_byte & DECODER_STATUS_GOOD)) {
+               }
+
+               /* 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)
+{
+       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 */
+               saa7111_set_gpio(dev,0);
+       } 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 */
+               saa7111_set_gpio(dev,1);
+       }
+       return 0;
+}
+
+static struct saa7146_standard standard[] = {
+       { "PAL-BG",     V4L2_STD_PAL_BG,        SAA7146_PAL_VALUES },
+       { "PAL-I",      V4L2_STD_PAL_I,         SAA7146_PAL_VALUES },
+       { "NTSC",       V4L2_STD_NTSC,          SAA7146_NTSC_VALUES },
+       { "SECAM",      V4L2_STD_SECAM,         SAA7146_SECAM_VALUES },
+};
+
+static
+struct saa7146_extension extension;
+
+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,
+       }
+};
+
+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,
+       .ext_vv_data    = &vv_data,
+
+       .probe          = mxb_probe,
+       .attach         = mxb_attach,
+       .detach         = mxb_detach,
+
+       .irq_mask       = 0,
+       .irq_func       = NULL,
+};     
+
+int __init mxb_init_module(void) 
+{
+       if( 0 != saa7146_register_extension(&extension)) {
+               DEB_S(("failed to register extension.\n"));
+               return -ENODEV;
+       }
+       
+       return 0;
+}
+
+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/drivers/media/video/mxb.h b/drivers/media/video/mxb.h
new file mode 100644 (file)
index 0000000..755e792
--- /dev/null
@@ -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 */
+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
index 3ac54bdcfb43aaf3f69ff7253f1f7e44d3a52eba..9427c6dfea6261c7b8a6755df9a13b4ec1d98bf4 100644 (file)
@@ -57,15 +57,13 @@ struct saa7111 {
        int sat;
 };
 
-#define   I2C_SAA7111        0x48
-
-#define   I2C_DELAY   10
-
 static unsigned short normal_i2c[] = { 34>>1, I2C_CLIENT_END };        
 static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; 
 
 I2C_CLIENT_INSMOD;
 
+static struct i2c_client client_template;
+
 /* ----------------------------------------------------------------------- */
 
 static int saa7111_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind)
@@ -73,40 +71,45 @@ static int saa7111_attach(struct i2c_adapter *adap, int addr, unsigned short fla
        int i;
        struct saa7111 *decoder;
        struct i2c_client *client;
+
+       /* who wrote this? init[] is used for i2c_master_send() which expects an array that
+          will be used for the 'buf' part of an i2c message unchanged. so, the first byte
+          needs to be the subaddress to start with, then follow the data bytes... */
        static const unsigned char init[] = {
-               0x00, 0x00,     /* 00 - ID byte */
-               0x01, 0x00,     /* 01 - reserved */
+               0x00,     /* start address */
+       
+               0x00,     /* 00 - ID byte */
+               0x00,     /* 01 - reserved */
 
                /*front end */
-               0x02, 0xd0,     /* 02 - FUSE=3, GUDL=2, MODE=0 */
-               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 */
+               0xd0,     /* 02 - FUSE=3, GUDL=2, MODE=0 */
+               0x23,     /* 03 - HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */
+               0x00,     /* 04 - GAI1=256 */
+               0x00,     /* 05 - GAI2=256 */
 
                /* decoder */
-               0x06, 0xf3,     /* 06 - HSB at  13(50Hz) /  17(60Hz) pixels after end of last line */
-               0x07, 0x13,     /* 07 - HSS at 113(50Hz) / 117(60Hz) pixels after end of last line */
-               0x08, 0xc8,     /* 08 - AUFD=1, FSEL=1, EXFIL=0, VTRC=1, HPLL=0, VNOI=0 */
-               0x09, 0x01,     /* 09 - BYPS=0, PREF=0, BPSS=0, VBLB=0, UPTCV=0, APER=1 */
-               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, 0x48,     /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */
-               0x11, 0x1c,     /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, OEYC=1, OEHV=1, VIPB=0, COLO=0 */
-               0x12, 0x00,     /* 12 - output control 2 */
-               0x13, 0x00,     /* 13 - output control 3 */
-               0x14, 0x00,     /* 14 - reserved */
-               0x15, 0x00,     /* 15 - VBI */
-               0x16, 0x00,     /* 16 - VBI */
-               0x17, 0x00,     /* 17 - VBI */
+               0xf3,     /* 06 - HSB at  13(50Hz) /  17(60Hz) pixels after end of last line */
+               0x13,     /* 07 - HSS at 113(50Hz) / 117(60Hz) pixels after end of last line */
+               0xc8,     /* 08 - AUFD=1, FSEL=1, EXFIL=0, VTRC=1, HPLL=0, VNOI=0 */
+               0x01,     /* 09 - BYPS=0, PREF=0, BPSS=0, VBLB=0, UPTCV=0, APER=1 */
+               0x80,     /* 0a - BRIG=128 */
+               0x47,     /* 0b - CONT=1.109 */
+               0x40,     /* 0c - SATN=1.0 */
+               0x00,     /* 0d - HUE=0 */
+               0x01,     /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */
+               0x00,     /* 0f - reserved */
+               0x48,     /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */
+               0x1c,     /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, OEYC=1, OEHV=1, VIPB=0, COLO=0 */
+               0x00,     /* 12 - output control 2 */
+               0x00,     /* 13 - output control 3 */
+               0x00,     /* 14 - reserved */
+               0x00,     /* 15 - VBI */
+               0x00,     /* 16 - VBI */
+               0x00,     /* 17 - VBI */
        };
        client = kmalloc(sizeof(*client), GFP_KERNEL);
        if(client == NULL) 
                return -ENOMEM;
-       memset(client, 0, sizeof(*client));
        client_template.adapter = adap;
        client_template.addr = addr;
        memcpy(client, &client_template, sizeof(*client));
@@ -136,9 +139,10 @@ static int saa7111_attach(struct i2c_adapter *adap, int addr, unsigned short fla
                printk(KERN_ERR "%s_attach: init status %d\n",
                       client->dev.name, i);
        } else {
-               printk(KERN_INFO "%s_attach: chip version %x\n",
-                      client->dev.name, i2c_smbus_read_byte_data(client, 0x00) >> 4);
+               printk(KERN_INFO "%s_attach: chip version %x @ 0x%08x\n",
+                      client->dev.name, i2c_smbus_read_byte_data(client, 0x00) >> 4,addr);
        }
+
        init_MUTEX(&decoder->lock);
        i2c_attach_client(client);
        MOD_INC_USE_COUNT;
@@ -146,6 +150,16 @@ static int saa7111_attach(struct i2c_adapter *adap, int addr, unsigned short fla
 }
 static int saa7111_probe(struct i2c_adapter *adap)
 {
+       /* probing unknown devices on any Matrox i2c-bus takes ages due to the
+          slow bit banging algorithm used. because of the fact a saa7111(a)
+          is *never* present on a Matrox gfx card, we can skip such adapters
+          here */
+       if( 0 != (adap->id & I2C_HW_B_G400)) {
+               return -ENODEV;
+       }
+       
+       printk("saa7111: probing %s i2c adapter [id=0x%x]\n",
+                       adap->dev.name,adap->id);
        return i2c_probe(adap, &addr_data, saa7111_attach);
 }
 
@@ -385,6 +399,9 @@ static int saa7111_command(struct i2c_client *client, unsigned int cmd,
 /* ----------------------------------------------------------------------- */
 
 static struct i2c_driver i2c_driver_saa7111 = {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,54)
+       .owner          = THIS_MODULE,
+#endif
        .name           = "saa7111",             /* name */
        .id             = I2C_DRIVERID_SAA7111A, /* ID */
        .flags          = I2C_DF_NOTIFY,
diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c
new file mode 100644 (file)
index 0000000..39d8820
--- /dev/null
@@ -0,0 +1,289 @@
+ /*
+    tda9840.h - 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/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+
+#include "tda9840.h"
+
+static int debug = 0;  /* insmod parameter */
+MODULE_PARM(debug,"i");
+#define dprintk        if (debug) printk
+
+#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};
+static unsigned short normal_i2c_range[] = {I2C_CLIENT_END};
+
+/* magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+/* unique ID allocation */
+static int tda9840_id = 0;
+
+static struct i2c_driver driver;
+
+static int tda9840_command(struct i2c_client *client, unsigned int cmd, void* arg)
+{
+       int result = 0;
+
+       switch (cmd) {
+               case TDA9840_SWITCH:
+               {
+                       int byte = *(int*)arg;
+
+                       dprintk("tda9840.o: 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;
+                       }
+                       
+                       if ( 0 != (result = i2c_smbus_write_byte_data(client, SWITCH, byte))) {
+                               printk("tda9840.o: TDA9840_SWITCH error.\n");
+                               return -EFAULT;
+                       }
+                       
+                       return 0;
+               }
+
+               case TDA9840_LEVEL_ADJUST:
+               {
+                       int  byte = *(int*)arg;
+
+                       dprintk("tda9840.o: 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;
+
+                       if ( 0 != (result = i2c_smbus_write_byte_data(client, LEVEL_ADJUST, byte))) {
+                               printk("tda9840.o: TDA9840_LEVEL_ADJUST error.\n");
+                               return -EFAULT;
+                       }
+                       
+                       return 0;
+               }
+
+               case TDA9840_STEREO_ADJUST:
+               {
+                       int  byte = *(int*)arg;
+
+                       dprintk("tda9840.o: 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;
+
+                       if ( 0 != (result = i2c_smbus_write_byte_data(client, STEREO_ADJUST, byte))) {
+                               printk("tda9840.o: TDA9840_STEREO_ADJUST error.\n");
+                               return -EFAULT;
+                       }
+                       
+                       return 0;
+               }
+
+               case TDA9840_DETECT:
+               {
+                       int byte = 0x0;
+
+                       if ( -1 == (byte = i2c_smbus_read_byte_data(client, STEREO_ADJUST))) {
+                               printk("tda9840.o: TDA9840_DETECT error while reading.\n");
+                               return -EFAULT;
+                       }                       
+
+                       if( 0 != (byte & 0x80)) {
+                               dprintk("tda9840.o: TDA9840_DETECT, register contents invalid.\n");
+                               return -EFAULT;
+                       }
+
+                       dprintk("tda9840.o: TDA9840_DETECT, result: 0x%02x (original byte)\n",byte);
+
+                       return ((byte & 0x60) >> 5);                            
+               }
+
+               case TDA9840_TEST:
+               {
+                       int  byte = *(int*)arg;
+
+                       dprintk("tda9840.o: TDA9840_TEST: 0x%02x\n",byte);
+
+                       /* mask out irrelevant bits */
+                       byte &= 0x3;
+
+                       if ( 0 != (result = i2c_smbus_write_byte_data(client, TEST, byte))) {
+                               printk("tda9840.o: TDA9840_TEST error.\n");
+                               return -EFAULT;
+                       }
+               
+                       return 0;
+               }
+
+               default:
+                       return -ENOIOCTLCMD;
+       }
+
+       return 0;
+}
+
+static int tda9840_detect(struct i2c_adapter *adapter, int address, unsigned short flags, 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("tda9840.o: not enough kernel memory.\n");
+               return -ENOMEM;
+       }
+       
+       /* fill client structure */
+       sprintf(client->dev.name,"tda9840 (0x%02x)", address);
+       client->id = tda9840_id++;
+       client->flags = 0;
+       client->addr = address;
+       client->adapter = adapter;
+       client->driver = &driver;
+       i2c_set_clientdata(client, NULL);
+
+       /* 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;
+       if ( 0 != (result = tda9840_command(client, TDA9840_LEVEL_ADJUST, &byte))) {
+               printk("tda9840.o: could not initialize ic #1. continuing anyway. (result:%d)\n",result);
+       }
+       
+       if ( 0 != (result = tda9840_command(client, TDA9840_STEREO_ADJUST, &byte))) {
+               printk("tda9840.o: could not initialize ic #2. continuing anyway. (result:%d)\n",result);
+       }
+
+       byte = TDA9840_SET_MONO;
+       if ( 0 != (result = tda9840_command(client, TDA9840_SWITCH, &byte))) {
+               printk("tda9840.o: could not initialize ic #3. continuing anyway. (result:%d)\n",result);
+       } 
+       
+       printk("tda9840.o: detected @ 0x%02x on adapter %s\n",2*address,&client->adapter->dev.name[0]);
+
+       return 0;
+}
+
+static int tda9840_attach(struct i2c_adapter *adapter)
+{
+       /* let's see whether this is a know adapter we can attach to */
+       if( adapter->id != I2C_ALGO_SAA7146 ) {
+               dprintk("tda9840.o: refusing to probe on unknown adapter [name='%s',id=0x%x]\n",adapter->dev.name,adapter->id);
+               return -ENODEV;
+       }
+
+       return i2c_probe(adapter,&addr_data,&tda9840_detect);
+}
+
+static int tda9840_detach(struct i2c_client *client)
+{
+       int err = 0;
+
+       if ( 0 != (err = i2c_detach_client(client))) {
+               printk("tda9840.o: Client deregistration failed, client not detached.\n");
+               return err;
+       }
+       
+       kfree(client);
+
+       return 0;
+}
+
+static struct i2c_driver driver = {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,54)
+       .owner          = THIS_MODULE,
+#endif
+       .name           = "tda9840 driver",
+       .id             = I2C_DRIVERID_TDA9840,
+       .flags          = I2C_DF_NOTIFY,
+        .attach_adapter = tda9840_attach,
+        .detach_client = tda9840_detach,
+        .command       = tda9840_command,
+};
+
+static int tda9840_init_module(void)
+{
+        i2c_add_driver(&driver);
+        return 0;
+}
+
+static void tda9840_cleanup_module(void)
+{
+        i2c_del_driver(&driver);
+}
+
+module_init(tda9840_init_module);
+module_exit(tda9840_cleanup_module);
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("tda9840 driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/media/video/tda9840.h b/drivers/media/video/tda9840.h
new file mode 100644 (file)
index 0000000..2802105
--- /dev/null
@@ -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/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c
new file mode 100644 (file)
index 0000000..71ce11a
--- /dev/null
@@ -0,0 +1,238 @@
+ /*
+    tea6415c.h - 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/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include "tea6415c.h"
+
+static int debug = 0;  /* insmod parameter */
+MODULE_PARM(debug,"i");
+#define dprintk        if (debug) printk
+
+#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};
+static unsigned short normal_i2c_range[] = {I2C_CLIENT_END};
+
+/* magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver driver;
+
+/* unique ID allocation */
+static int tea6415c_id = 0;
+
+/* this function is called by i2c_probe */
+static int tea6415c_detect(struct i2c_adapter *adapter, int address, unsigned short flags, int kind)
+{
+       struct  i2c_client *client = 0;
+       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 */
+       sprintf(client->dev.name,"tea6415c (0x%02x)", address);
+       client->id = tea6415c_id++;
+       client->flags = 0;
+       client->addr = address;
+       client->adapter = adapter;
+       client->driver = &driver;
+
+       /* tell the i2c layer a new client has arrived */
+       if (0 != (err = i2c_attach_client(client))) {
+               kfree(client);
+               return err;
+       }
+
+       printk("tea6415c.o: detected @ 0x%02x on adapter %s\n",2*address,&client->adapter->dev.name[0]);
+
+       return 0;
+}
+
+static int tea6415c_attach(struct i2c_adapter *adapter)
+{
+       /* let's see whether this is a know adapter we can attach to */
+       if( adapter->id != I2C_ALGO_SAA7146 ) {
+               dprintk("tea6415c.o: refusing to probe on unknown adapter [name='%s',id=0x%x]\n",adapter->dev.name,adapter->id);
+               return -ENODEV;
+       }
+
+       return i2c_probe(adapter,&addr_data,&tea6415c_detect);
+}
+
+static int tea6415c_detach(struct i2c_client *client)
+{
+       int err = 0;
+
+       if ( 0 != (err = i2c_detach_client(client))) {
+               printk("tea6415c.o: Client deregistration failed, client not detached.\n");
+               return err;
+       }
+       
+       kfree(client);
+
+       return 0;
+}
+
+/* makes a connection between the input-pin 'i' and the output-pin 'o'
+   for the tea6415c-client 'client' */
+static int tea6415c_switch(struct i2c_client *client, int i, int o)
+{
+       u8      byte = 0;
+       
+       dprintk("tea6415c.o: tea6415c_switch: 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;
+       };
+
+       if ( 0 != i2c_smbus_write_byte(client,byte)) {
+               dprintk("tea6415c.o: tea6415c_switch: could not write to tea6415c\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int tea6415c_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 = tea6415c_switch(client,v->in,v->out);
+                       break;
+               }
+               default: {
+                       return -ENOIOCTLCMD;
+               }
+       }
+
+       if ( 0 != result )
+               return result;
+       
+       return 0;
+}
+
+static struct i2c_driver driver = {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,54)
+       .owner          = THIS_MODULE,
+#endif
+       .name           = "tea6415c driver",
+       .id             = I2C_DRIVERID_TEA6415C,
+       .flags          = I2C_DF_NOTIFY,
+        .attach_adapter = tea6415c_attach,
+        .detach_client = tea6415c_detach,
+        .command       = tea6415c_command,
+};
+
+static int tea6415c_init_module(void)
+{
+       i2c_add_driver(&driver);
+       return 0;
+}
+
+static void tea6415c_cleanup_module(void)
+{
+        i2c_del_driver(&driver);
+}
+
+module_init(tea6415c_init_module);
+module_exit(tea6415c_cleanup_module);
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("tea6415c driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/media/video/tea6415c.h b/drivers/media/video/tea6415c.h
new file mode 100644 (file)
index 0000000..f84ed80
--- /dev/null
@@ -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/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c
new file mode 100644 (file)
index 0000000..b55e8b2
--- /dev/null
@@ -0,0 +1,217 @@
+ /*
+    tea6420.o - 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/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+
+#include "tea6420.h"
+
+static int debug = 0;  /* insmod parameter */
+MODULE_PARM(debug,"i");
+#define dprintk        if (debug) printk
+
+/* 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};
+static unsigned short normal_i2c_range[] = {I2C_CLIENT_END};
+
+/* magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver driver;
+
+/* unique ID allocation */
+static int tea6420_id = 0;
+
+/* 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     result = 0;
+       
+       dprintk("tea6420.o: tea6420_switch: 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;
+       }
+
+       /* fixme?: 1 != ... => 0 != */
+       if ( 0 != (result = i2c_smbus_write_byte(client,byte))) {
+               printk("tea6402:%d\n",result);
+               dprintk(KERN_ERR "tea6420.o: could not switch, result:%d\n",result);
+               return -EFAULT;
+       }
+       
+       return 0;
+}
+
+/* this function is called by i2c_probe */
+static int tea6420_detect(struct i2c_adapter *adapter, int address, unsigned short flags, 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;
+       }
+       
+       /* fill client structure */
+       sprintf(client->dev.name,"tea6420 (0x%02x)", address);
+       client->id = tea6420_id++;
+       client->flags = 0;
+       client->addr = address;
+       client->adapter = adapter;
+       client->driver = &driver;
+       i2c_set_clientdata(client, NULL);
+
+       /* 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( 0 != err) {
+               printk("tea6420.o: could not initialize chipset. continuing anyway.\n");
+       }
+       
+       printk("tea6420.o: detected @ 0x%02x on adapter %s\n",2*address,&client->adapter->dev.name[0]);
+
+       return 0;
+}
+
+static int tea6420_attach(struct i2c_adapter *adapter)
+{
+       /* let's see whether this is a know adapter we can attach to */
+       if( adapter->id != I2C_ALGO_SAA7146 ) {
+               dprintk("tea6420.o: refusing to probe on unknown adapter [name='%s',id=0x%x]\n",adapter->dev.name,adapter->id);
+               return -ENODEV;
+       }
+
+       return i2c_probe(adapter,&addr_data,&tea6420_detect);
+}
+
+static int tea6420_detach(struct i2c_client *client)
+{
+       int err = 0;
+
+       if ( 0 != (err = i2c_detach_client(client))) {
+               printk("tea6420.o: Client deregistration failed, client not detached.\n");
+               return err;
+       }
+       
+       kfree(client);
+
+       return 0;
+}
+
+static int tea6420_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;
+               }
+       }
+
+       if ( 0 != result )
+               return result;
+       
+       return 0;
+}
+
+static struct i2c_driver driver = {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,54)
+       .owner          = THIS_MODULE,
+#endif
+       .name           = "tea6420 driver",
+       .id             = I2C_DRIVERID_TEA6420,
+       .flags          = I2C_DF_NOTIFY,
+        .attach_adapter = tea6420_attach,
+        .detach_client = tea6420_detach,
+        .command       = tea6420_command,
+};
+
+static int tea6420_init_module(void)
+{
+       i2c_add_driver(&driver);
+       return 0;
+}
+
+static void tea6420_cleanup_module(void)
+{
+        i2c_del_driver(&driver);
+}
+
+module_init(tea6420_init_module);
+module_exit(tea6420_cleanup_module);
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("tea6420 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tea6420.h b/drivers/media/video/tea6420.h
new file mode 100644 (file)
index 0000000..ea664df
--- /dev/null
@@ -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