[PATCH] fix silly config option.
[safe/jmp/linux-2.6] / drivers / media / video / tuner-core.c
index 881a053..eaabfc8 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.15 2005/06/12 01:36:14 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 */
 static unsigned short normal_i2c[] = {
        0x4b, /* tda8290 */
-       I2C_CLIENT_END
-};
-static unsigned short normal_i2c_range[] = {
-       0x60, 0x6f,
+       0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+       0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
        I2C_CLIENT_END
 };
 I2C_CLIENT_INSMOD;
@@ -55,13 +58,16 @@ MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
 MODULE_LICENSE("GPL");
 
 static int this_adap;
+#ifdef CONFIG_TUNER_MULTI_I2C
+static unsigned short first_tuner, tv_tuner, radio_tuner;
+#endif
 
 static struct i2c_driver driver;
 static struct i2c_client client_template;
 
 /* ---------------------------------------------------------------------- */
 
-// Set tuner frequency,  freq in Units of 62.5kHz = 1/16MHz
+/* Set tuner frequency,  freq in Units of 62.5kHz = 1/16MHz */
 static void set_tv_freq(struct i2c_client *c, unsigned int freq)
 {
        struct tuner *t = i2c_get_clientdata(c);
@@ -75,14 +81,26 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq)
                return;
        }
        if (freq < tv_range[0]*16 || freq > tv_range[1]*16) {
-               /* FIXME: better do that chip-specific, but
-                  right now we don't have that in the config
-                  struct and this way is still better than no
-                  check at all */
-               tuner_info("TV freq (%d.%02d) out of range (%d-%d)\n",
-                          freq/16,freq%16*100/16,tv_range[0],tv_range[1]);
-               return;
+
+               if (freq >= tv_range[0]*16364 && freq <= tv_range[1]*16384) {
+                       /* V4L2_TUNER_CAP_LOW frequency */
+
+                       tuner_dbg("V4L2_TUNER_CAP_LOW freq selected for TV. Tuners yet doesn't support converting it to valid freq.\n");
+
+                       t->tv_freq(c,freq>>10);
+
+                       return;
+                } else {
+                       /* FIXME: better do that chip-specific, but
+                          right now we don't have that in the config
+                          struct and this way is still better than no
+                          check at all */
+                       tuner_info("TV freq (%d.%02d) out of range (%d-%d)\n",
+                                  freq/16,freq%16*100/16,tv_range[0],tv_range[1]);
+                       return;
+               }
        }
+       tuner_dbg("62.5 Khz freq step selected for TV.\n");
        t->tv_freq(c,freq);
 }
 
@@ -99,11 +117,29 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq)
                return;
        }
        if (freq < radio_range[0]*16 || freq > radio_range[1]*16) {
-               tuner_info("radio freq (%d.%02d) out of range (%d-%d)\n",
+               if (freq >= tv_range[0]*16364 && freq <= tv_range[1]*16384) {
+                       /* V4L2_TUNER_CAP_LOW frequency */
+                       if (t->type == TUNER_TEA5767) {
+                               tuner_info("radio freq step 62.5Hz (%d.%06d)\n",(freq>>14),freq%(1<<14)*10000);
+                               t->radio_freq(c,freq>>10);
+                               return;
+                       }
+
+                       tuner_dbg("V4L2_TUNER_CAP_LOW freq selected for Radio. Tuners yet doesn't support converting it to valid freq.\n");
+
+                       tuner_info("radio freq (%d.%06d)\n",(freq>>14),freq%(1<<14)*10000);
+
+                       t->radio_freq(c,freq>>10);
+                       return;
+
+                } else {
+                       tuner_info("radio freq (%d.%02d) out of range (%d-%d)\n",
                           freq/16,freq%16*100/16,
-                          radio_range[0],radio_range[1]);
-               return;
+                                  radio_range[0],radio_range[1]);
+                       return;
+               }
        }
+       tuner_dbg("62.5 Khz freq step selected for Radio.\n");
        t->radio_freq(c,freq);
 }
 
@@ -131,8 +167,9 @@ static void set_type(struct i2c_client *c, unsigned int type)
 {
        struct tuner *t = i2c_get_clientdata(c);
 
+       tuner_dbg ("I2C addr 0x%02x with type %d\n",c->addr<<1,type);
        /* sanity check */
-       if (type == UNSET  ||  type == TUNER_ABSENT)
+       if (type == UNSET || type == TUNER_ABSENT)
                return;
        if (type >= tuner_count)
                return;
@@ -147,6 +184,7 @@ static void set_type(struct i2c_client *c, unsigned int type)
                return;
 
        t->initialized = 1;
+
        t->type = type;
        switch (t->type) {
        case TUNER_MT2032:
@@ -161,6 +199,53 @@ static void set_type(struct i2c_client *c, unsigned int type)
        }
 }
 
+#ifdef CONFIG_TUNER_MULTI_I2C
+#define CHECK_ADDR(tp,cmd,tun) if (client->addr!=tp) { \
+                         return 0; } else \
+                         tuner_info ("Cmd %s accepted to "tun"\n",cmd);
+#define CHECK_MODE(cmd)        if (t->mode == V4L2_TUNER_RADIO) { \
+                       CHECK_ADDR(radio_tuner,cmd,"radio") } else \
+                       { CHECK_ADDR(tv_tuner,cmd,"TV"); }
+#else
+#define CHECK_ADDR(tp,cmd,tun) tuner_info ("Cmd %s accepted to "tun"\n",cmd);
+#define CHECK_MODE(cmd) tuner_info ("Cmd %s accepted\n",cmd);
+#endif
+
+#ifdef CONFIG_TUNER_MULTI_I2C
+
+static void set_addr(struct i2c_client *c, struct tuner_addr *tun_addr)
+{
+       /* ADDR_UNSET defaults to first available tuner */
+       if ( tun_addr->addr == ADDR_UNSET ) {
+               if (first_tuner != c->addr)
+                       return;
+               switch (tun_addr->v4l2_tuner) {
+               case V4L2_TUNER_RADIO:
+                       radio_tuner=c->addr;
+                       break;
+               default:
+                       tv_tuner=c->addr;
+                       break;
+               }
+       } else {
+               /* Sets tuner to its configured value */
+               switch (tun_addr->v4l2_tuner) {
+               case V4L2_TUNER_RADIO:
+                       radio_tuner=tun_addr->addr;
+                       if ( tun_addr->addr == c->addr ) set_type(c,tun_addr->type);
+                       return;
+               default:
+                       tv_tuner=tun_addr->addr;
+                       if ( tun_addr->addr == c->addr ) set_type(c,tun_addr->type);
+                       return;
+               }
+       }
+       set_type(c,tun_addr->type);
+}
+#else
+#define set_addr(c,tun_addr) set_type(c,(tun_addr)->type)
+#endif
+
 static char pal[] = "-";
 module_param_string(pal, pal, sizeof(pal), 0644);
 
@@ -199,8 +284,17 @@ 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) {
+               first_tuner = addr;
+               tv_tuner = addr;
+               radio_tuner = addr;
+       }
+#endif
        this_adap++;
 
         client_template.adapter = adap;
@@ -213,11 +307,12 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
         memcpy(&t->i2c,&client_template,sizeof(struct i2c_client));
        i2c_set_clientdata(&t->i2c, t);
        t->type       = UNSET;
-       t->radio_if2  = 10700*1000; // 10.7MHz - FM radio
+       t->radio_if2  = 10700*1000; /* 10.7MHz - FM radio */
 
         i2c_attach_client(&t->i2c);
        tuner_info("chip found @ 0x%x (%s)\n",
                   addr << 1, adap->name);
+
        set_type(&t->i2c, t->type);
        return 0;
 }
@@ -225,12 +320,17 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
 static int tuner_probe(struct i2c_adapter *adap)
 {
        if (0 != addr) {
-               normal_i2c[0]       = addr;
-               normal_i2c_range[0] = addr;
-               normal_i2c_range[1] = addr;
+               normal_i2c[0] = addr;
+               normal_i2c[1] = I2C_CLIENT_END;
        }
        this_adap = 0;
 
+#ifdef CONFIG_TUNER_MULTI_I2C
+       first_tuner = 0;
+       tv_tuner = 0;
+       radio_tuner = 0;
+#endif
+
        if (adap->class & I2C_CLASS_TV_ANALOG)
                return i2c_probe(adap, &addr_data, tuner_attach);
        return 0;
@@ -239,8 +339,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;
 }
@@ -259,18 +365,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_TYPE_ADDR:
+               set_addr(client,(struct tuner_addr *)arg);
+               break;
        case AUDC_SET_RADIO:
+               t->mode = V4L2_TUNER_RADIO;
+               CHECK_ADDR(tv_tuner,"AUDC_SET_RADIO","TV");
+
                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","TV");
                switch (*iarg) {
                case 2:
                        tuner_dbg("pinnacle pal\n");
@@ -300,6 +411,8 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
 
                CHECK_V4L2;
                t->mode = V4L2_TUNER_ANALOG_TV;
+               CHECK_ADDR(tv_tuner,"VIDIOCSCHAN","TV");
+
                if (vc->norm < ARRAY_SIZE(map))
                        t->std = map[vc->norm];
                tuner_fixup_std(t);
@@ -311,6 +424,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;
@@ -319,15 +433,27 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
        {
                struct video_tuner *vt = arg;
 
+               CHECK_ADDR(radio_tuner,"VIDIOCGTUNER","radio");
                CHECK_V4L2;
-               if (V4L2_TUNER_RADIO == t->mode  &&  t->has_signal)
-                       vt->signal = t->has_signal(client);
+               if (V4L2_TUNER_RADIO == t->mode) {
+                       if (t->has_signal)
+                               vt->signal = t->has_signal(client);
+                       if (t->is_stereo) {
+                               if (t->is_stereo(client))
+                                       vt-> flags |= VIDEO_TUNER_STEREO_ON;
+                               else
+                                       vt-> flags &= 0xffff ^ VIDEO_TUNER_STEREO_ON;
+                       }
+                       vt->flags |= V4L2_TUNER_CAP_LOW; /* Allow freqs at 62.5 Hz */
+               }
+
                return 0;
        }
        case VIDIOCGAUDIO:
        {
                struct video_audio *va = arg;
 
+               CHECK_ADDR(radio_tuner,"VIDIOCGAUDIO","radio");
                CHECK_V4L2;
                if (V4L2_TUNER_RADIO == t->mode  &&  t->is_stereo)
                        va->mode = t->is_stereo(client)
@@ -342,6 +468,8 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
 
                SWITCH_V4L2;
                t->mode = V4L2_TUNER_ANALOG_TV;
+               CHECK_ADDR(tv_tuner,"VIDIOC_S_STD","TV");
+
                t->std = *id;
                tuner_fixup_std(t);
                if (t->freq)
@@ -352,25 +480,52 @@ 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)
                        set_tv_freq(client,400*16);
                t->mode  = f->type;
-               t->freq  = f->frequency;
-               set_freq(client,t->freq);
+               set_freq(client,f->frequency);
+               break;
+       }
+       case VIDIOC_G_FREQUENCY:
+       {
+               struct v4l2_frequency *f = arg;
+
+               CHECK_MODE("VIDIOC_G_FREQUENCY");
+               SWITCH_V4L2;
+               f->type = t->mode;
+               f->frequency = t->freq;
                break;
        }
        case VIDIOC_G_TUNER:
        {
                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);
+               if (V4L2_TUNER_RADIO == t->mode) {
+                       if (t->has_signal)
+                               tuner -> signal = t->has_signal(client);
+                       if (t->is_stereo) {
+                               if (t->is_stereo(client)) {
+                                       tuner -> capability |= V4L2_TUNER_CAP_STEREO;
+                                       tuner -> rxsubchans |= V4L2_TUNER_SUB_STEREO;
+                               } else {
+                                       tuner -> rxsubchans &= 0xffff ^ V4L2_TUNER_SUB_STEREO;
+                               }
+                       }
+               }
+               /* Wow to deal with V4L2_TUNER_CAP_LOW ? For now, it accepts from low at 62.5KHz step  to high at 62.5 Hz */
+               tuner->rangelow = tv_range[0] * 16;
+//             tuner->rangehigh = tv_range[1] * 16;
+//             tuner->rangelow = tv_range[0] * 16384;
+               tuner->rangehigh = tv_range[1] * 16384;
                break;
        }
        default:
+               tuner_dbg ("Unimplemented IOCTL 0x%08x called to tuner.\n", cmd);
                /* nothing */
                break;
        }
@@ -378,7 +533,7 @@ tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
        return 0;
 }
 
-static int tuner_suspend(struct device * dev, pm_message_t state, u32 level)
+static int tuner_suspend(struct device * dev, u32 state, u32 level)
 {
        struct i2c_client *c = container_of(dev, struct i2c_client, dev);
        struct tuner *t = i2c_get_clientdata(c);