[PATCH] tuner-core.c improvments and Ymec Tvision TVF8533MF support
authorMauro Carvalho Chehab <mchehab@brturbo.com.br>
Fri, 24 Jun 2005 05:02:43 +0000 (22:02 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Fri, 24 Jun 2005 07:05:31 +0000 (00:05 -0700)
tuner-core.c, tuner.h:

- tuner-core changed to support multiple I2C devices used on some
  adapters;

- Kconfig now has an option (CONFIG_TUNER_MULTI_I2C) to enable this new
  behavor;

- By default, even enabling CONFIG_TUNER_MULTI_I2C, tuner-core emulates
  the old behavor, using first I2C device for both FM and TV;

- There is a new i2c command (TUNER_SET_ADDR) to allow tuner clients to
  select I2C address for FM or TV tuner;

- Tuner I2C dettach now generates a warning on syslog if failed.

tuner-simple.c:

- TVision TVF-8531MF and TVF-5533 MF tuner included.  It uses, by
  default, I2C on 0xC2 address for TV and on 0xC0 for Radio.  Both TV and
  FM Radio mode are working.

Signed-off-by: Mauro Carvalho Chehab <mchehab@brturbo.com.br>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
drivers/media/video/Kconfig
drivers/media/video/tuner-core.c
drivers/media/video/tuner-simple.c
include/media/tuner.h

index 6c05fddb69ab484841abefdf007e52ebc4d17bf5..8c349706f8501a8e4eaf624d4cea09b110c60e14 100644 (file)
@@ -7,6 +7,19 @@ menu "Video For Linux"
 
 comment "Video Adapters"
 
+config CONFIG_TUNER_MULTI_I2C
+       bool "Enable support for multiple I2C devices on Video Adapters (EXPERIMENTAL)"
+       depends on VIDEO_DEV && EXPERIMENTAL
+       ---help---
+         Some video adapters have more than one tuner inside. This patch
+         enables support for using more than one tuner. This is required
+         for some cards to allow tunning  both video and radio.
+         It also improves I2C autodetection for these cards.
+
+         Only few tuners currently is supporting this. More to come.
+
+         It is safe to say 'Y' here even if your card has only one I2C tuner.
+
 config VIDEO_BT848
        tristate "BT848 Video For Linux"
        depends on VIDEO_DEV && PCI && I2C
index 81882ddab859dd52c3db6ecfbfeac5c228b6a97f..71423ae3b4dd0289e839b484ba8600f6259b6a42 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: tuner-core.c,v 1.5 2005/02/15 15:59:35 kraxel Exp $
+ * $Id: tuner-core.c,v 1.7 2005/05/30 02:02:47 mchehab Exp $
  *
  * i2c tv tuner chip device driver
  * core core, i.e. kernel interfaces, registering and so on
 #include <media/tuner.h>
 #include <media/audiochip.h>
 
+/*
+ * comment line bellow to return to old behavor, where only one I2C device is supported
+ */
+/* #define CONFIG_TUNER_MULTI_I2C */
+
 #define UNSET (-1U)
 
 /* standard i2c insmod options */
@@ -53,6 +58,9 @@ MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
 MODULE_LICENSE("GPL");
 
 static int this_adap;
+#ifdef CONFIG_TUNER_MULTI_I2C
+static unsigned short tv_tuner, radio_tuner;
+#endif
 
 static struct i2c_driver driver;
 static struct i2c_client client_template;
@@ -125,6 +133,28 @@ static void set_freq(struct i2c_client *c, unsigned long freq)
        t->freq = freq;
 }
 
+#ifdef CONFIG_TUNER_MULTI_I2C
+static void set_addr(struct i2c_client *c, struct tuner_addr *tun_addr)
+{
+       struct tuner *t = i2c_get_clientdata(c);
+
+       switch (tun_addr->type) {
+       case V4L2_TUNER_RADIO:
+               radio_tuner=tun_addr->addr;
+               tuner_dbg("radio tuner set to I2C address 0x%02x\n",radio_tuner<<1);
+
+               break;
+       default:
+               tv_tuner=tun_addr->addr;
+               tuner_dbg("TV tuner set to I2C address 0x%02x\n",tv_tuner<<1);
+               break;
+       }
+}
+#else
+#define set_addr(c,tun_addr) \
+               tuner_warn("It is recommended to enable CONFIG_TUNER_MULTI_I2C for this card.\n");
+#endif
+
 static void set_type(struct i2c_client *c, unsigned int type)
 {
        struct tuner *t = i2c_get_clientdata(c);
@@ -197,8 +227,16 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
 {
        struct tuner *t;
 
+#ifndef CONFIG_TUNER_MULTI_I2C
        if (this_adap > 0)
                return -1;
+#else
+       /* by default, first I2C card is both tv and radio tuner */
+       if (this_adap == 0) {
+               tv_tuner = addr;
+               radio_tuner = addr;
+       }
+#endif
        this_adap++;
 
         client_template.adapter = adap;
@@ -228,6 +266,11 @@ static int tuner_probe(struct i2c_adapter *adap)
        }
        this_adap = 0;
 
+#ifdef CONFIG_TUNER_MULTI_I2C
+       tv_tuner = 0;
+       radio_tuner = 0;
+#endif
+
        if (adap->class & I2C_CLASS_TV_ANALOG)
                return i2c_probe(adap, &addr_data, tuner_attach);
        return 0;
@@ -236,8 +279,14 @@ static int tuner_probe(struct i2c_adapter *adap)
 static int tuner_detach(struct i2c_client *client)
 {
        struct tuner *t = i2c_get_clientdata(client);
+       int err;
+
+       err=i2c_detach_client(&t->i2c);
+       if (err) {
+               tuner_warn ("Client deregistration failed, client not detached.\n");
+               return err;
+       }
 
-       i2c_detach_client(&t->i2c);
        kfree(t);
        return 0;
 }
@@ -249,6 +298,17 @@ static int tuner_detach(struct i2c_client *client)
                          tuner_info("ignore v4l1 call\n"); \
                          return 0; }
 
+#ifdef CONFIG_TUNER_MULTI_I2C
+#define CHECK_ADDR(tp,cmd)     if (client->addr!=tp) { \
+                         tuner_info ("Cmd %s to addr 0x%02x rejected.\n",cmd,client->addr<<1); \
+                         return 0; }
+#define CHECK_MODE(cmd)        if (t->mode == V4L2_TUNER_RADIO) { \
+                         CHECK_ADDR(radio_tuner,cmd) } else { CHECK_ADDR(tv_tuner,cmd); }
+#else
+#define CHECK_ADDR(tp,cmd)
+#define CHECK_MODE(cmd)
+#endif
+
 static int
 tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
 {
@@ -256,18 +316,23 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
         unsigned int *iarg = (int*)arg;
 
         switch (cmd) {
-
        /* --- configuration --- */
        case TUNER_SET_TYPE:
                set_type(client,*iarg);
                break;
+       case TUNER_SET_ADDR:
+               set_addr(client,(struct tuner_addr *)arg);
+               break;
        case AUDC_SET_RADIO:
+               CHECK_ADDR(radio_tuner,"AUDC_SET_RADIO");
+
                if (V4L2_TUNER_RADIO != t->mode) {
                        set_tv_freq(client,400 * 16);
                        t->mode = V4L2_TUNER_RADIO;
                }
                break;
        case AUDC_CONFIG_PINNACLE:
+               CHECK_ADDR(tv_tuner,"AUDC_CONFIG_PINNACLE");
                switch (*iarg) {
                case 2:
                        tuner_dbg("pinnacle pal\n");
@@ -295,6 +360,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
                };
                struct video_channel *vc = arg;
 
+               CHECK_ADDR(tv_tuner,"VIDIOCSCHAN");
                CHECK_V4L2;
                t->mode = V4L2_TUNER_ANALOG_TV;
                if (vc->norm < ARRAY_SIZE(map))
@@ -308,6 +374,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
        {
                unsigned long *v = arg;
 
+               CHECK_MODE("VIDIOCSFREQ");
                CHECK_V4L2;
                set_freq(client,*v);
                return 0;
@@ -316,6 +383,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
        {
                struct video_tuner *vt = arg;
 
+               CHECK_ADDR(radio_tuner,"VIDIOCGTUNER:");
                CHECK_V4L2;
                if (V4L2_TUNER_RADIO == t->mode  &&  t->has_signal)
                        vt->signal = t->has_signal(client);
@@ -325,6 +393,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
        {
                struct video_audio *va = arg;
 
+               CHECK_ADDR(radio_tuner,"VIDIOCGAUDIO");
                CHECK_V4L2;
                if (V4L2_TUNER_RADIO == t->mode  &&  t->is_stereo)
                        va->mode = t->is_stereo(client)
@@ -337,6 +406,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
        {
                v4l2_std_id *id = arg;
 
+               CHECK_ADDR(tv_tuner,"VIDIOC_S_STD");
                SWITCH_V4L2;
                t->mode = V4L2_TUNER_ANALOG_TV;
                t->std = *id;
@@ -349,6 +419,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
        {
                struct v4l2_frequency *f = arg;
 
+               CHECK_MODE("VIDIOC_S_FREQUENCY");
                SWITCH_V4L2;
                if (V4L2_TUNER_RADIO == f->type &&
                    V4L2_TUNER_RADIO != t->mode)
@@ -361,6 +432,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
        {
                struct v4l2_frequency *f = arg;
 
+               CHECK_MODE("VIDIOC_G_FREQUENCY");
                SWITCH_V4L2;
                f->type = t->mode;
                f->frequency = t->freq;
@@ -370,6 +442,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
        {
                struct v4l2_tuner *tuner = arg;
 
+               CHECK_MODE("VIDIOC_G_TUNER");
                SWITCH_V4L2;
                if (V4L2_TUNER_RADIO == t->mode  &&  t->has_signal)
                        tuner->signal = t->has_signal(client);
index 48c6ceff1dc265c39799ec15120c42e59c024735..f7305c8d53de2534dd4042c4ed938df929275622 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: tuner-simple.c,v 1.10 2005/03/08 08:38:00 kraxel Exp $
+ * $Id: tuner-simple.c,v 1.14 2005/05/30 02:02:47 mchehab Exp $
  *
  * i2c tv tuner chip device driver
  * controls all those simple 4-control-bytes style tuners.
@@ -212,6 +212,11 @@ static struct tunertype tuners[] = {
         { "Philips FQ1236A MK4", Philips, NTSC,
           16*160.00,16*442.00,0x01,0x02,0x04,0x8e,732 },
 
+       /* Should work for TVF8531MF, TVF8831MF, TVF8731MF */
+       { "Ymec TVision TVF-8531MF", Philips, NTSC,
+         16*160.00,16*454.00,0xa0,0x90,0x30,0x8e,732},
+       { "Ymec TVision TVF-5533MF", Philips, NTSC,
+         16*160.00,16*454.00,0x01,0x02,0x04,0x8e,732},
 };
 unsigned const int tuner_count = ARRAY_SIZE(tuners);
 
@@ -424,6 +429,13 @@ static void default_set_radio_freq(struct i2c_client *c, unsigned int freq)
        buffer[2] = tun->config;
 
        switch (t->type) {
+       case TUNER_YMEC_TVF_5533MF:
+
+               /*These values are empirically determinated */
+               div = (freq*122)/16 - 20;
+               buffer[2] = 0x88; /* could be also 0x80 */
+               buffer[3] = 0x19; /* could be also 0x10, 0x18, 0x99 */
+               break;
        case TUNER_PHILIPS_FM1216ME_MK3:
        case TUNER_PHILIPS_FM1236_MK3:
                buffer[3] = 0x19;
@@ -458,6 +470,20 @@ int default_tuner_init(struct i2c_client *c)
                   t->type, tuners[t->type].name);
        strlcpy(c->name, tuners[t->type].name, sizeof(c->name));
 
+       switch (t->type) {
+       case TUNER_YMEC_TVF_5533MF:
+               {
+                       struct tuner_addr tun_addr = { V4L2_TUNER_ANALOG_TV, 0xc2>>1 };
+
+                       if (c->driver->command) {
+                               c->driver->command(c, TUNER_SET_ADDR, &tun_addr);
+                       } else {
+                               tuner_warn("Couldn't set TV tuner I2C address to 0x%02x\n",tun_addr.addr<<1);
+                       }
+                       break;
+               }
+       }
+
        t->tv_freq    = default_set_tv_freq;
        t->radio_freq = default_set_radio_freq;
        t->has_signal = tuner_signal;
index 156a9c51ffec92b9070ff24ff7e84c227f4e519d..6d29d8db0c838d50e6324148aaddfc26b1d410fb 100644 (file)
@@ -98,6 +98,9 @@
 #define TUNER_PHILIPS_FQ1216AME_MK4 56 /* Hauppauge PVR-150 PAL */
 #define TUNER_PHILIPS_FQ1236A_MK4 57   /* Hauppauge PVR-500MCE NTSC */
 
+#define TUNER_YMEC_TVF_8531MF 58
+#define TUNER_YMEC_TVF_5533MF 59       /* Pixelview Pro Ultra NTSC */
+
 #define NOTUNER 0
 #define PAL     1      /* PAL_BG */
 #define PAL_I   2
 
 #define TUNER_SET_TYPE               _IOW('t',1,int)    /* set tuner type */
 #define TUNER_SET_TVFREQ             _IOW('t',2,int)    /* set tv freq */
+#define TUNER_SET_ADDR               _IOW('T',3,int)   /* Chooses tuner I2C address */
 
 #define  TDA9887_SET_CONFIG          _IOW('t',5,int)
+
 /* tv card specific */
 # define TDA9887_PRESENT             (1<<0)
 # define TDA9887_PORT1_INACTIVE      (1<<1)
 #define I2C_ADDR_TDA8290        0x4b
 #define I2C_ADDR_TDA8275        0x61
 
+struct tuner_addr {
+       enum v4l2_tuner_type type;
+       unsigned short addr;
+};
+
 struct tuner {
        /* device */
        struct i2c_client i2c;