summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--linux/drivers/media/video/mxb.c1009
-rw-r--r--linux/drivers/media/video/mxb.h22
-rw-r--r--linux/drivers/media/video/saa7111.c433
-rw-r--r--linux/drivers/media/video/tda9840.c274
-rw-r--r--linux/drivers/media/video/tda9840.h69
-rw-r--r--linux/drivers/media/video/tea6415c.c223
-rw-r--r--linux/drivers/media/video/tea6415c.h68
-rw-r--r--linux/drivers/media/video/tea6420.c202
-rw-r--r--linux/drivers/media/video/tea6420.h46
9 files changed, 2346 insertions, 0 deletions
diff --git a/linux/drivers/media/video/mxb.c b/linux/drivers/media/video/mxb.c
new file mode 100644
index 000000000..094cbbf9a
--- /dev/null
+++ b/linux/drivers/media/video/mxb.c
@@ -0,0 +1,1009 @@
+/*
+ mxb.c - v4l2 driver for the Multimedia eXtension Board
+
+ Copyright (C) 1998-2002 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.
+*/
+
+#include <saa7146.h>
+
+#include "mxb.h"
+#include "tea6415c.h"
+#include "tea6420.h"
+#include "tda9840.h"
+#include "tuner.h"
+#include <linux/video_decoder.h> /* for saa7111a */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ #define KBUILD_MODNAME "mxb"
+#endif
+
+#define I2C_SAA7111A 0x24
+
+/* 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;
+
+/* debug verbosity */
+/* fixme */
+static int debug = 247;
+
+#ifdef MODULE
+MODULE_PARM(freq,"i");
+MODULE_PARM_DESC(freq, "initial frequency the tuner will be tuned to while setup");
+MODULE_PARM(debug,"i");
+MODULE_PARM_DESC(debug, "debug verbosity");
+#endif
+
+
+#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 audio_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 audio_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 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 */
+};
+
+/* this function gets called very early in the registration process of
+ the extension. it has been reported that some devices need to enable
+ the i2c-bus explicitly for example -- this can be done here. please
+ note, that you cannot be sure that the device is really the hardware
+ you expect, so you should do as little as possible in here, in order
+ to avoid confusing the hardware.
+*/
+static int mxb_preinit(struct saa7146_dev* dev)
+{
+ return 0;
+}
+
+static int mxb_probe(struct saa7146_dev* dev, unsigned int subvendor, unsigned int subdevice)
+{
+ struct mxb* mxb = 0;
+
+ int i = 0;
+
+ 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));
+
+ /* loop through all i2c-devices on the bus and look who is there */
+ for(i = 0; i < dev->i2c_adapter->client_count; i++) {
+ if( I2C_TEA6420_1 == dev->i2c_adapter->clients[i]->addr )
+ mxb->tea6420_1 = dev->i2c_adapter->clients[i];
+ if( I2C_TEA6420_2 == dev->i2c_adapter->clients[i]->addr )
+ mxb->tea6420_2 = dev->i2c_adapter->clients[i];
+ if( I2C_TEA6415C_2 == dev->i2c_adapter->clients[i]->addr )
+ mxb->tea6415c = dev->i2c_adapter->clients[i];
+ if( I2C_TDA9840 == dev->i2c_adapter->clients[i]->addr )
+ mxb->tda9840 = dev->i2c_adapter->clients[i];
+ if( I2C_SAA7111A == dev->i2c_adapter->clients[i]->addr )
+ mxb->saa7111a = dev->i2c_adapter->clients[i];
+ if( 0x60 == dev->i2c_adapter->clients[i]->addr )
+ mxb->tuner = dev->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 ) {
+ DEB_D(("this saa7146 is not on an mxb.\n"));
+ 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 video_decoder_init saa7111a_init;
+ struct i2c_msg msg;
+
+ int i = 0, err = 0;
+ struct tea6415c_video_multiplex vm;
+
+ memcpy(&saa7111a_init.data, &init, sizeof(init));
+ saa7111a_init.count = sizeof(init);
+
+ /* write configuration to saa7111a */
+ mxb->saa7111a->driver->command(mxb->saa7111a, DECODER_INIT, &saa7111a_init);
+ /* 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);
+
+ /* 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(dev->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(dev->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 bh-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 mxb* mxb = (struct mxb*)dev->ext_priv;
+
+ /* checking for i2c-devices can be omitted here, because we
+ already did this in "mxb_vl42_probe" */
+
+ mxb_num++;
+
+ i2c_inc_use_client(mxb->tea6420_1);
+ i2c_inc_use_client(mxb->tea6420_2);
+ i2c_inc_use_client(mxb->tea6415c);
+ i2c_inc_use_client(mxb->tda9840);
+ i2c_inc_use_client(mxb->saa7111a);
+ i2c_inc_use_client(mxb->tuner);
+
+ return mxb_init_done(dev);
+}
+
+static int mxb_detach(struct saa7146_dev* dev)
+{
+ struct mxb* mxb = (struct mxb*)dev->ext_priv;
+
+ i2c_dec_use_client(mxb->tea6420_1);
+ i2c_dec_use_client(mxb->tea6420_2);
+ i2c_dec_use_client(mxb->tea6415c);
+ i2c_dec_use_client(mxb->tda9840);
+ i2c_dec_use_client(mxb->saa7111a);
+ i2c_dec_use_client(mxb->tuner);
+
+ mxb_num--;
+ kfree(mxb);
+
+ return 0;
+}
+
+static int mxb_vbi_bypass(struct saa7146_dev* dev)
+{
+ struct mxb* mxb = (struct mxb*)dev->ext_priv;
+ int i = 1;
+
+ /* switch bypass in saa7111a */
+ /* fixme saa
+ if ( 0 != mxb->saa7111a->driver->command(mxb->saa7111a,SAA711X_VBI_BYPASS, &i)) {
+ DEB_D(("could not address saa7111a.\n"));
+ return -1;
+ }
+ */
+
+ 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;
+
+ /* get the old register contents */
+ if ( -1 == (byte = i2c_smbus_read_byte_data(mxb->saa7111a, 0x11))) {
+ 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, 0x11, 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;
+
+ 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_video_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;
+ /* fixme: _CAP_NORM needed? */
+ 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);
+ dev->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 },
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+static void mxb_inc_use(struct saa7146_dev *dev)
+{
+ MOD_INC_USE_COUNT;
+}
+
+static void mxb_dec_use(struct saa7146_dev *dev)
+{
+ MOD_DEC_USE_COUNT;
+}
+#endif
+
+static struct saa7146_sub_info sub_data[] = {
+ { 0x0000, 0x0000 },
+ { 0xffff, 0xffff },
+};
+
+static struct saa7146_extension extension = {
+ MXB_IDENTIFIER,
+ MXB_INPUTS,
+ MXB_AUDIOS,
+ V4L2_CAP_TUNER,
+
+ sub_data,
+
+ THIS_MODULE,
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ mxb_inc_use,
+ mxb_dec_use,
+#endif
+
+ &standard[0],
+ sizeof(standard)/sizeof(struct saa7146_standard),
+ &std_callback,
+
+ 1,
+ mxb_vbi_bypass,
+
+ &ioctls[0],
+
+ mxb_preinit,
+ mxb_probe,
+ mxb_attach,
+ mxb_detach,
+
+ mxb_ioctl,
+
+ 0,
+ NULL,
+};
+
+static int mxb_init(void)
+{
+ if( 0 != saa7146_register_extension(&extension)) {
+ DEB_S(("failed to register extension.\n"));
+ return -ENODEV;
+ }
+
+ /* mxb_num gets increased if the probe function is called successfully */
+
+ if( 0 == mxb_num ) {
+ INFO(("no mxb(s) found.\n"));
+ return -ENODEV;
+ }
+
+ INFO(("%d mxb(s) found.\n", mxb_num));
+ return 0;
+}
+
+#ifdef MODULE
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("video4linux driver for the Siemens-Nixdorf 'Multimedia eXtension board'");
+MODULE_LICENSE("GPL");
+
+EXPORT_NO_SYMBOLS;
+
+static int mxb_init_module(void)
+{
+ return mxb_init();
+}
+
+static void mxb_cleanup_module(void)
+{
+ saa7146_unregister_extension(&extension);
+}
+
+module_init(mxb_init_module);
+module_exit(mxb_cleanup_module);
+#endif
diff --git a/linux/drivers/media/video/mxb.h b/linux/drivers/media/video/mxb.h
new file mode 100644
index 000000000..3f73ea7ca
--- /dev/null
+++ b/linux/drivers/media/video/mxb.h
@@ -0,0 +1,22 @@
+#ifndef __MXB__
+#define __MXB__
+
+#define BASE_VIDIOC_MXB 10
+
+#define MXB_S_AUDIO_CD _IOW ('V', BASE_VIDIOC_PRIVATE+BASE_VIDIOC_MXB+00, int)
+#define MXB_S_AUDIO_LINE _IOW ('V', BASE_VIDIOC_PRIVATE+BASE_VIDIOC_MXB+01, 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] =
+ { { 0, "Tuner", 0},
+ { 1, "AUX1", 0},
+ { 2, "AUX2", 0},
+ { 3, "AUX3", 0},
+ { 4, "Radio (X9)", 0},
+ { 5, "CD-ROM (X10)", 0} };
+#endif
diff --git a/linux/drivers/media/video/saa7111.c b/linux/drivers/media/video/saa7111.c
new file mode 100644
index 000000000..ad2efed8b
--- /dev/null
+++ b/linux/drivers/media/video/saa7111.c
@@ -0,0 +1,433 @@
+/*
+ saa7111 - Philips SAA7111A video decoder driver version 0.0.3
+
+ Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+
+ Slight changes for video timing and attachment output by
+ Wolfgang Scherr <scherr@net4you.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+
+#include <linux/videodev.h>
+#include <linux/version.h>
+#include <linux/i2c.h>
+
+#include <linux/video_decoder.h>
+
+#define DEBUG(x) /* Debug driver */
+
+/* ----------------------------------------------------------------------- */
+
+struct saa7111 {
+ struct i2c_client *client;
+ int addr;
+ struct semaphore lock;
+ unsigned char reg[32];
+
+ int norm;
+ int input;
+ int enable;
+ int bright;
+ int contrast;
+ int hue;
+ int sat;
+};
+
+/* hmm, the saa7111(a) specs don't say anything about address 34>>1 (= 17 = 0x11),
+ only for 0x24 and 0x25 ... */
+static unsigned short normal_i2c[] = { 34>>1, 0x24, 0x25, 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_client client_template;
+/* ----------------------------------------------------------------------- */
+
+static int saa7111_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind)
+{
+ 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, /* start address */
+
+ 0x00, /* 00 - ID byte */
+ 0x00, /* 01 - reserved */
+
+ /*front end */
+ 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 */
+ 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;
+ client_template.adapter = adap;
+ client_template.addr = addr;
+ memcpy(client, &client_template, sizeof(*client));
+
+ decoder = kmalloc(sizeof(*decoder), GFP_KERNEL);
+ if (decoder == NULL)
+ {
+ kfree(client);
+ return -ENOMEM;
+ }
+
+ memset(decoder, 0, sizeof(*decoder));
+ strcpy(client->name, "saa7111");
+ decoder->client = client;
+ client->data = decoder;
+ decoder->addr = addr;
+ decoder->norm = VIDEO_MODE_NTSC;
+ decoder->input = 0;
+ decoder->enable = 1;
+ decoder->bright = 32768;
+ decoder->contrast = 32768;
+ decoder->hue = 32768;
+ decoder->sat = 32768;
+
+ i = i2c_master_send(client, init, sizeof(init));
+ if (i < 0) {
+ printk(KERN_ERR "%s_attach: init status %d\n",
+ client->name, i);
+ } else {
+ printk(KERN_INFO "%s_attach: chip version %x @ 0x%08x\n",
+ client->name, i2c_smbus_read_byte_data(client, 0x00) >> 4,addr);
+ }
+
+ init_MUTEX(&decoder->lock);
+ i2c_attach_client(client);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+static int saa7111_probe(struct i2c_adapter *adap)
+{
+ printk("saa7111: probing %s i2c adapter [id=0x%x]\n",
+ adap->name,adap->id);
+ return i2c_probe(adap, &addr_data, saa7111_attach);
+}
+
+static int saa7111_detach(struct i2c_client *client)
+{
+ struct saa7111 *decoder = client->data;
+ i2c_detach_client(client);
+ kfree(decoder);
+ kfree(client);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int saa7111_command(struct i2c_client *client, unsigned int cmd,
+ void *arg)
+{
+ struct saa7111 *decoder = client->data;
+ switch (cmd) {
+
+#if defined(DECODER_DUMP)
+ case DECODER_DUMP:
+ {
+ int i;
+
+ for (i = 0; i < 32; i += 16) {
+ int j;
+
+ printk("KERN_DEBUG %s: %03x", client->name,
+ i);
+ for (j = 0; j < 16; ++j) {
+ printk(" %02x",
+ i2c_smbus_read_byte_data(client,
+ i + j));
+ }
+ printk("\n");
+ }
+ }
+ break;
+#endif /* defined(DECODER_DUMP) */
+ case DECODER_GET_CAPABILITIES:
+ {
+ struct video_decoder_capability *cap = arg;
+
+ cap->flags
+ = VIDEO_DECODER_PAL
+ | VIDEO_DECODER_NTSC
+ | VIDEO_DECODER_AUTO | VIDEO_DECODER_CCIR;
+ cap->inputs = 8;
+ cap->outputs = 1;
+ }
+ break;
+ case DECODER_INIT:
+ {
+ int i = 0;
+ struct video_decoder_init *init = arg;
+ printk("DECODER_INIT: %d\n",init->count);
+ i = i2c_master_send(client, init->data, init->count);
+ if (i < 0) {
+ printk(KERN_ERR "DECODER_INIT failed.");
+ return -EFAULT;
+ } else {
+ printk(KERN_ERR "DECODER_INIT ok.");
+ }
+ }
+ break;
+ case DECODER_GET_STATUS:
+ {
+ int *iarg = arg;
+ int status;
+ int res;
+
+ status = i2c_smbus_read_byte_data(client, 0x1f);
+ res = 0;
+ if ((status & (1 << 6)) == 0) {
+ res |= DECODER_STATUS_GOOD;
+ }
+ switch (decoder->norm) {
+ case VIDEO_MODE_NTSC:
+ res |= DECODER_STATUS_NTSC;
+ break;
+ case VIDEO_MODE_PAL:
+ res |= DECODER_STATUS_PAL;
+ break;
+ default:
+ case VIDEO_MODE_AUTO:
+ if ((status & (1 << 5)) != 0) {
+ res |= DECODER_STATUS_NTSC;
+ } else {
+ res |= DECODER_STATUS_PAL;
+ }
+ break;
+ }
+ if ((status & (1 << 0)) != 0) {
+ res |= DECODER_STATUS_COLOR;
+ }
+ *iarg = res;
+ }
+ break;
+
+ case DECODER_SET_NORM:
+ {
+ int *iarg = arg;
+
+ switch (*iarg) {
+
+ case VIDEO_MODE_NTSC:
+ i2c_smbus_write_byte_data(client, 0x08,
+ (decoder->
+ reg[0x08] & 0x3f) | 0x40);
+ break;
+
+ case VIDEO_MODE_PAL:
+ i2c_smbus_write_byte_data(client, 0x08,
+ (decoder->
+ reg[0x08] & 0x3f) | 0x00);
+ break;
+
+ case VIDEO_MODE_AUTO:
+ i2c_smbus_write_byte_data(client, 0x08,
+ (decoder->
+ reg[0x08] & 0x3f) | 0x80);
+ break;
+
+ default:
+ return -EINVAL;
+
+ }
+ decoder->norm = *iarg;
+ }
+ break;
+
+ case DECODER_SET_INPUT:
+ {
+ int *iarg = arg;
+
+ if (*iarg < 0 || *iarg > 7) {
+ return -EINVAL;
+ }
+
+ if (decoder->input != *iarg) {
+ decoder->input = *iarg;
+ /* select mode */
+ i2c_smbus_write_byte_data(client, 0x02,
+ (decoder->
+ reg[0x02] & 0xf8) |
+ decoder->input);
+ /* bypass chrominance trap for modes 4..7 */
+ i2c_smbus_write_byte_data(client, 0x09,
+ (decoder->
+ reg[0x09] & 0x7f) |
+ ((decoder->input >
+ 3) ? 0x80 : 0));
+ }
+ }
+ break;
+
+ case DECODER_SET_OUTPUT:
+ {
+ int *iarg = arg;
+
+ /* not much choice of outputs */
+ if (*iarg != 0) {
+ return -EINVAL;
+ }
+ }
+ break;
+
+ case DECODER_ENABLE_OUTPUT:
+ {
+ int *iarg = arg;
+ int enable = (*iarg != 0);
+
+ if (decoder->enable != enable) {
+ decoder->enable = enable;
+
+// RJ: If output should be disabled (for playing videos), we also need a open PLL.
+// The input is set to 0 (where no input source is connected), although this
+// is not necessary.
+//
+// If output should be enabled, we have to reverse the above.
+
+ if (decoder->enable) {
+ i2c_smbus_write_byte_data(client, 0x02,
+ (decoder->
+ reg[0x02] & 0xf8) |
+ decoder->input);
+ i2c_smbus_write_byte_data(client, 0x08,
+ (decoder->
+ reg[0x08] & 0xfb));
+ i2c_smbus_write_byte_data(client, 0x11,
+ (decoder->
+ reg[0x11] & 0xf3) |
+ 0x0c);
+ } else {
+ i2c_smbus_write_byte_data(client, 0x02,
+ (decoder->
+ reg[0x02] & 0xf8));
+ i2c_smbus_write_byte_data(client, 0x08,
+ (decoder->
+ reg[0x08] & 0xfb) |
+ 0x04);
+ i2c_smbus_write_byte_data(client, 0x11,
+ (decoder->
+ reg[0x11] & 0xf3));
+ }
+ }
+ }
+ break;
+
+ case DECODER_SET_PICTURE:
+ {
+ struct video_picture *pic = arg;
+
+ if (decoder->bright != pic->brightness) {
+ /* We want 0 to 255 we get 0-65535 */
+ decoder->bright = pic->brightness;
+ i2c_smbus_write_byte_data(client, 0x0a,
+ decoder->bright >> 8);
+ }
+ if (decoder->contrast != pic->contrast) {
+ /* We want 0 to 127 we get 0-65535 */
+ decoder->contrast = pic->contrast;
+ i2c_smbus_write_byte_data(client, 0x0b,
+ decoder->contrast >> 9);
+ }
+ if (decoder->sat != pic->colour) {
+ /* We want 0 to 127 we get 0-65535 */
+ decoder->sat = pic->colour;
+ i2c_smbus_write_byte_data(client, 0x0c,
+ decoder->sat >> 9);
+ }
+ if (decoder->hue != pic->hue) {
+ /* We want -128 to 127 we get 0-65535 */
+ decoder->hue = pic->hue;
+ i2c_smbus_write_byte_data(client, 0x0d,
+ (decoder->hue - 32768) >> 8);
+ }
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_driver i2c_driver_saa7111 = {
+ .name = "saa7111", /* name */
+ .id = I2C_DRIVERID_SAA7111A, /* ID */
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = saa7111_probe,
+ .detach_client = saa7111_detach,
+ .command = saa7111_command
+};
+
+static struct i2c_client client_template = {
+ .name = "saa7111_client",
+ .id = -1,
+ .driver = &i2c_driver_saa7111
+};
+
+static int saa7111_init(void)
+{
+ return i2c_add_driver(&i2c_driver_saa7111);
+}
+
+static void saa7111_exit(void)
+{
+ i2c_del_driver(&i2c_driver_saa7111);
+}
+
+module_init(saa7111_init);
+module_exit(saa7111_exit);
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/tda9840.c b/linux/drivers/media/video/tda9840.c
new file mode 100644
index 000000000..116b59f74
--- /dev/null
+++ b/linux/drivers/media/video/tda9840.c
@@ -0,0 +1,274 @@
+#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"
+
+#ifdef MODULE
+MODULE_PARM(debug,"i");
+#endif
+
+static int debug = 0; /* insmod parameter */
+#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->name,"tda9840 (0x%02x)", address);
+ client->id = tda9840_id++;
+ client->flags = 0;
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &driver;
+ client->data = 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->name[0]);
+
+ return 0;
+}
+
+static int tda9840_attach(struct i2c_adapter *adapter)
+{
+ 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;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+static void tda9840_inc_use(struct i2c_client *client)
+{
+ MOD_INC_USE_COUNT;
+}
+
+static void tda9840_dec_use(struct i2c_client *client)
+{
+ MOD_DEC_USE_COUNT;
+}
+#endif
+
+static struct i2c_driver driver = {
+ .name = "tda9840 driver",
+ .id = I2C_DRIVERID_TDA9840,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = tda9840_attach,
+ .detach_client = tda9840_detach,
+ .command = tda9840_command,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ .inc_use = tda9840_inc_use,
+ .dec_use = tda9840_dec_use,
+#endif
+};
+
+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);
+
+#ifdef MODULE
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("tda9840 driver");
+MODULE_LICENSE("GPL");
+#endif
+
diff --git a/linux/drivers/media/video/tda9840.h b/linux/drivers/media/video/tda9840.h
new file mode 100644
index 000000000..54e4a77e4
--- /dev/null
+++ b/linux/drivers/media/video/tda9840.h
@@ -0,0 +1,69 @@
+ /*
+ tda9840.h - definitions for the i2c-driver
+ for the tda9840 by SGS Thomson
+
+ Copyright (C) 1998,1999,2000 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.
+ */
+
+#ifndef __INCLUDED_TDA9840__
+#define __INCLUDED_TDA9840__
+
+#define I2C_TDA9840 0x42
+
+/* I'm using the currently unused letter 'd' for
+ identifying. perhaps this will be changed in the
+ future.
+
+ (see /usr/src/linux/Documentation/ioctl-number.txt
+ for further details.) */
+
+#define TDA9840_DETECT _IOR('d',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('d',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('d',3,int)
+
+/* values may range between +2.5 and -2.4;
+ the value has to be multiplied with 10 */
+#define TDA9840_STEREO_ADJUST _IOW('d',4,int)
+
+/* currently not implemented */
+#define TDA9840_TEST _IOW('d',5,int)
+
+#endif
diff --git a/linux/drivers/media/video/tea6415c.c b/linux/drivers/media/video/tea6415c.c
new file mode 100644
index 000000000..5050fe286
--- /dev/null
+++ b/linux/drivers/media/video/tea6415c.c
@@ -0,0 +1,223 @@
+#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"
+
+#ifdef MODULE
+MODULE_PARM(debug,"i");
+#endif
+
+static int debug = 0; /* insmod parameter */
+#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->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->name[0]);
+
+ return 0;
+}
+
+static int tea6415c_attach(struct i2c_adapter *adapter)
+{
+ 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_video_multiplex *v = (struct tea6415c_video_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;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+static void tea6415c_inc_use(struct i2c_client *client)
+{
+ MOD_INC_USE_COUNT;
+}
+
+static void tea6415c_dec_use(struct i2c_client *client)
+{
+ MOD_DEC_USE_COUNT;
+}
+#endif
+
+static struct i2c_driver driver = {
+ "tea6415c driver",
+ I2C_DRIVERID_TEA6415C,
+ I2C_DF_NOTIFY,
+ tea6415c_attach,
+ tea6415c_detach,
+ tea6415c_command,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ tea6415c_inc_use,
+ tea6415c_dec_use,
+#endif
+};
+
+EXPORT_NO_SYMBOLS;
+
+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);
+
+#ifdef MODULE
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("tea6415c driver");
+MODULE_LICENSE("GPL");
+#endif
+
diff --git a/linux/drivers/media/video/tea6415c.h b/linux/drivers/media/video/tea6415c.h
new file mode 100644
index 000000000..775fb6fe8
--- /dev/null
+++ b/linux/drivers/media/video/tea6415c.h
@@ -0,0 +1,68 @@
+ /*
+ tea6415c.h - definitions for the i2c-driver
+ for the tea6415c by SGS Thomson
+
+ Copyright (C) 1998,1999,2000 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.
+ */
+
+#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_video_multiplex
+{
+ int in; /* input-pin */
+ int out; /* output-pin */
+};
+
+#define TEA6415C_SWITCH _IOW('t',1,struct tea6415c_video_multiplex)
+
+#endif
diff --git a/linux/drivers/media/video/tea6420.c b/linux/drivers/media/video/tea6420.c
new file mode 100644
index 000000000..9955df2cf
--- /dev/null
+++ b/linux/drivers/media/video/tea6420.c
@@ -0,0 +1,202 @@
+#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"
+
+#ifdef MODULE
+MODULE_PARM(debug,"i");
+#endif
+
+static int debug = 0; /* insmod parameter */
+#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->name,"tea6420 (0x%02x)", address);
+ client->id = tea6420_id++;
+ client->flags = 0;
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &driver;
+ client->data = 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->name[0]);
+
+ return 0;
+}
+
+static int tea6420_attach(struct i2c_adapter *adapter)
+{
+ 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 audio_multiplex *a = (struct audio_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;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+static void tea6420_inc_use(struct i2c_client *client)
+{
+ MOD_INC_USE_COUNT;
+}
+
+static void tea6420_dec_use(struct i2c_client *client)
+{
+ MOD_DEC_USE_COUNT;
+}
+#endif
+
+static struct i2c_driver driver = {
+ .name = "tea6420 driver",
+ .id = I2C_DRIVERID_TEA6420,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = tea6420_attach,
+ .detach_client = tea6420_detach,
+ .command = tea6420_command,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ .inc_use = tea6420_inc_use,
+ .dec_use = tea6420_dec_use,
+#endif
+};
+
+EXPORT_NO_SYMBOLS;
+
+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);
+
+#ifdef MODULE
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("tea6420 driver");
+MODULE_LICENSE("GPL");
+#endif
diff --git a/linux/drivers/media/video/tea6420.h b/linux/drivers/media/video/tea6420.h
new file mode 100644
index 000000000..5cda0c408
--- /dev/null
+++ b/linux/drivers/media/video/tea6420.h
@@ -0,0 +1,46 @@
+ /*
+ tea6420.h - definitions for the i2c-driver
+ for the tea6420 by SGS Thomson
+
+ Copyright (C) 1998,1999,2000 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.
+ */
+
+#ifndef __INCLUDED_TEA6420__
+#define __INCLUDED_TEA6420__
+
+/* possible addresses */
+#define I2C_TEA6420_1 0x4c
+#define I2C_TEA6420_2 0x4d
+
+struct audio_multiplex
+{
+ int in; /* input of audio switch */
+ int out; /* output of audio switch */
+ int gain; /* gain of connection */
+};
+
+#define TEA6420_SWITCH _IOW('t',1,struct audio_multiplex)
+
+#endif