const: constify remaining dev_pm_ops
[safe/jmp/linux-2.6] / drivers / media / video / tuner-core.c
index 335a971..5b3eaa1 100644 (file)
 #include <linux/i2c.h>
 #include <linux/types.h>
 #include <linux/init.h>
-#include <linux/videodev.h>
+#include <linux/videodev2.h>
 #include <media/tuner.h>
 #include <media/tuner-types.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-i2c-drv-legacy.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-i2c-drv.h>
 #include "mt20xx.h"
 #include "tda8290.h"
 #include "tea5761.h"
 #include "tuner-simple.h"
 #include "tda9887.h"
 #include "xc5000.h"
+#include "tda18271.h"
 
 #define UNSET (-1U)
 
 #define PREFIX t->i2c->driver->driver.name
 
+/** This macro allows us to probe dynamically, avoiding static links */
+#ifdef CONFIG_MEDIA_ATTACH
+#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \
+       int __r = -EINVAL; \
+       typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
+       if (__a) { \
+               __r = (int) __a(ARGS); \
+               symbol_put(FUNCTION); \
+       } else { \
+               printk(KERN_ERR "TUNER: Unable to find " \
+                               "symbol "#FUNCTION"()\n"); \
+       } \
+       __r; \
+})
+
+static void tuner_detach(struct dvb_frontend *fe)
+{
+       if (fe->ops.tuner_ops.release) {
+               fe->ops.tuner_ops.release(fe);
+               symbol_put_addr(fe->ops.tuner_ops.release);
+       }
+       if (fe->ops.analog_ops.release) {
+               fe->ops.analog_ops.release(fe);
+               symbol_put_addr(fe->ops.analog_ops.release);
+       }
+}
+#else
+#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \
+       FUNCTION(ARGS); \
+})
+
+static void tuner_detach(struct dvb_frontend *fe)
+{
+       if (fe->ops.tuner_ops.release)
+               fe->ops.tuner_ops.release(fe);
+       if (fe->ops.analog_ops.release)
+               fe->ops.analog_ops.release(fe);
+}
+#endif
+
 struct tuner {
        /* device */
        struct dvb_frontend fe;
        struct i2c_client   *i2c;
+       struct v4l2_subdev  sd;
        struct list_head    list;
        unsigned int        using_v4l2:1;
 
@@ -51,21 +94,14 @@ struct tuner {
 
        unsigned int        type; /* chip type id */
        unsigned int        config;
-       int (*tuner_callback) (void *dev, int command, int arg);
+       const char          *name;
 };
 
-/* standard i2c insmod options */
-static unsigned short normal_i2c[] = {
-#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE))
-       0x10,
-#endif
-       0x42, 0x43, 0x4a, 0x4b,                 /* tda8290 */
-       0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
-       0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
-       I2C_CLIENT_END
-};
+static inline struct tuner *to_tuner(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct tuner, sd);
+}
 
-I2C_CLIENT_INSMOD;
 
 /* insmod options used at init time => read/only */
 static unsigned int addr;
@@ -139,22 +175,6 @@ static void fe_set_params(struct dvb_frontend *fe,
        fe_tuner_ops->set_analog_params(fe, params);
 }
 
-static void fe_release(struct dvb_frontend *fe)
-{
-       if (fe->ops.tuner_ops.release)
-               fe->ops.tuner_ops.release(fe);
-
-       /* DO NOT kfree(fe->analog_demod_priv)
-        *
-        * If we are in this function, analog_demod_priv contains a pointer
-        * to struct tuner *t.  This will be kfree'd in tuner_detach().
-        *
-        * Otherwise, fe->ops.analog_demod_ops->release will
-        * handle the cleanup for analog demodulator modules.
-        */
-       fe->analog_demod_priv = NULL;
-}
-
 static void fe_standby(struct dvb_frontend *fe)
 {
        struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
@@ -188,10 +208,9 @@ static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg)
 
 static void tuner_status(struct dvb_frontend *fe);
 
-static struct analog_demod_ops tuner_core_ops = {
+static struct analog_demod_ops tuner_analog_ops = {
        .set_params     = fe_set_params,
        .standby        = fe_standby,
-       .release        = fe_release,
        .has_signal     = fe_has_signal,
        .set_config     = fe_set_config,
        .tuner_status   = tuner_status
@@ -200,7 +219,7 @@ static struct analog_demod_ops tuner_core_ops = {
 /* 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);
+       struct tuner *t = to_tuner(i2c_get_clientdata(c));
        struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
 
        struct analog_parameters params = {
@@ -235,7 +254,7 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq)
 
 static void set_radio_freq(struct i2c_client *c, unsigned int freq)
 {
-       struct tuner *t = i2c_get_clientdata(c);
+       struct tuner *t = to_tuner(i2c_get_clientdata(c));
        struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
 
        struct analog_parameters params = {
@@ -270,7 +289,7 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq)
 
 static void set_freq(struct i2c_client *c, unsigned long freq)
 {
-       struct tuner *t = i2c_get_clientdata(c);
+       struct tuner *t = to_tuner(i2c_get_clientdata(c));
 
        switch (t->mode) {
        case V4L2_TUNER_RADIO:
@@ -291,51 +310,17 @@ static void set_freq(struct i2c_client *c, unsigned long freq)
        }
 }
 
-static void tuner_i2c_address_check(struct tuner *t)
-{
-       if ((t->type == UNSET || t->type == TUNER_ABSENT) ||
-           ((t->i2c->addr < 0x64) || (t->i2c->addr > 0x6f)))
-               return;
-
-       /* We already know that the XC5000 can only be located at
-        * i2c address 0x61, 0x62, 0x63 or 0x64 */
-       if ((t->type == TUNER_XC5000) &&
-           ((t->i2c->addr <= 0x64)) && (t->i2c->addr >= 0x61))
-               return;
-
-       tuner_warn("====================== WARNING! ======================\n");
-       tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n");
-       tuner_warn("will soon be dropped. This message indicates that your\n");
-       tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n",
-                  t->i2c->name, t->i2c->addr);
-       tuner_warn("To ensure continued support for your device, please\n");
-       tuner_warn("send a copy of this message, along with full dmesg\n");
-       tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n");
-       tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n");
-       tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n",
-                  t->i2c->adapter->name, t->i2c->addr, t->type, t->i2c->name);
-       tuner_warn("====================== WARNING! ======================\n");
-}
-
-static void attach_tda829x(struct tuner *t)
-{
-       struct tda829x_config cfg = {
-               .lna_cfg        = &t->config,
-               .tuner_callback = t->tuner_callback,
-       };
-       tda829x_attach(&t->fe, t->i2c->adapter, t->i2c->addr, &cfg);
-}
-
 static struct xc5000_config xc5000_cfg;
 
 static void set_type(struct i2c_client *c, unsigned int type,
                     unsigned int new_mode_mask, unsigned int new_config,
-                    int (*tuner_callback) (void *dev, int command,int arg))
+                    int (*tuner_callback) (void *dev, int component, int cmd, int arg))
 {
-       struct tuner *t = i2c_get_clientdata(c);
+       struct tuner *t = to_tuner(i2c_get_clientdata(c));
        struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
        struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
        unsigned char buffer[4];
+       int tune_now = 1;
 
        if (type == UNSET || type == TUNER_ABSENT) {
                tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
@@ -343,10 +328,11 @@ static void set_type(struct i2c_client *c, unsigned int type,
        }
 
        t->type = type;
-       t->config = new_config;
+       /* prevent invalid config values */
+       t->config = new_config < 256 ? new_config : 0;
        if (tuner_callback != NULL) {
                tuner_dbg("defining GPIO callback\n");
-               t->tuner_callback = tuner_callback;
+               t->fe.callback = tuner_callback;
        }
 
        if (t->mode == T_UNINITIALIZED) {
@@ -356,32 +342,35 @@ static void set_type(struct i2c_client *c, unsigned int type,
        }
 
        /* discard private data, in case set_type() was previously called */
-       if (analog_ops->release)
-               analog_ops->release(&t->fe);
+       tuner_detach(&t->fe);
+       t->fe.analog_demod_priv = NULL;
 
        switch (t->type) {
        case TUNER_MT2032:
-               microtune_attach(&t->fe, t->i2c->adapter, t->i2c->addr);
+               if (!dvb_attach(microtune_attach,
+                          &t->fe, t->i2c->adapter, t->i2c->addr))
+                       goto attach_failed;
                break;
        case TUNER_PHILIPS_TDA8290:
        {
-               attach_tda829x(t);
+               struct tda829x_config cfg = {
+                       .lna_cfg        = t->config,
+               };
+               if (!dvb_attach(tda829x_attach, &t->fe, t->i2c->adapter,
+                               t->i2c->addr, &cfg))
+                       goto attach_failed;
                break;
        }
        case TUNER_TEA5767:
-               if (tea5767_attach(&t->fe, t->i2c->adapter, t->i2c->addr) == NULL) {
-                       t->type = TUNER_ABSENT;
-                       t->mode_mask = T_UNINITIALIZED;
-                       return;
-               }
+               if (!dvb_attach(tea5767_attach, &t->fe,
+                               t->i2c->adapter, t->i2c->addr))
+                       goto attach_failed;
                t->mode_mask = T_RADIO;
                break;
        case TUNER_TEA5761:
-               if (tea5761_attach(&t->fe, t->i2c->adapter, t->i2c->addr) == NULL) {
-                       t->type = TUNER_ABSENT;
-                       t->mode_mask = T_UNINITIALIZED;
-                       return;
-               }
+               if (!dvb_attach(tea5761_attach, &t->fe,
+                               t->i2c->adapter, t->i2c->addr))
+                       goto attach_failed;
                t->mode_mask = T_RADIO;
                break;
        case TUNER_PHILIPS_FMD1216ME_MK3:
@@ -394,94 +383,106 @@ static void set_type(struct i2c_client *c, unsigned int type,
                buffer[2] = 0x86;
                buffer[3] = 0x54;
                i2c_master_send(c, buffer, 4);
-               if (simple_tuner_attach(&t->fe, t->i2c->adapter, t->i2c->addr,
-                                       t->type) == NULL) {
-                       t->type = TUNER_ABSENT;
-                       t->mode_mask = T_UNINITIALIZED;
-                       return;
-               }
+               if (!dvb_attach(simple_tuner_attach, &t->fe,
+                               t->i2c->adapter, t->i2c->addr, t->type))
+                       goto attach_failed;
                break;
        case TUNER_PHILIPS_TD1316:
                buffer[0] = 0x0b;
                buffer[1] = 0xdc;
                buffer[2] = 0x86;
                buffer[3] = 0xa4;
-               i2c_master_send(c,buffer,4);
-               if (simple_tuner_attach(&t->fe, t->i2c->adapter,
-                                       t->i2c->addr, t->type) == NULL) {
-                       t->type = TUNER_ABSENT;
-                       t->mode_mask = T_UNINITIALIZED;
-                       return;
-               }
+               i2c_master_send(c, buffer, 4);
+               if (!dvb_attach(simple_tuner_attach, &t->fe,
+                               t->i2c->adapter, t->i2c->addr, t->type))
+                       goto attach_failed;
                break;
        case TUNER_XC2028:
        {
                struct xc2028_config cfg = {
                        .i2c_adap  = t->i2c->adapter,
                        .i2c_addr  = t->i2c->addr,
-                       .video_dev = c->adapter->algo_data,
-                       .callback  = t->tuner_callback,
                };
-               if (!xc2028_attach(&t->fe, &cfg)) {
-                       t->type = TUNER_ABSENT;
-                       t->mode_mask = T_UNINITIALIZED;
-                       return;
-               }
+               if (!dvb_attach(xc2028_attach, &t->fe, &cfg))
+                       goto attach_failed;
+               tune_now = 0;
                break;
        }
        case TUNER_TDA9887:
-               tda9887_attach(&t->fe, t->i2c->adapter, t->i2c->addr);
+               if (!dvb_attach(tda9887_attach,
+                          &t->fe, t->i2c->adapter, t->i2c->addr))
+                       goto attach_failed;
                break;
        case TUNER_XC5000:
+       {
                xc5000_cfg.i2c_address    = t->i2c->addr;
-               xc5000_cfg.if_khz         = 5380;
-               xc5000_cfg.priv           = c->adapter->algo_data;
-               xc5000_cfg.tuner_callback = t->tuner_callback;
-               if (!xc5000_attach(&t->fe, t->i2c->adapter, &xc5000_cfg)) {
-                       t->type = TUNER_ABSENT;
-                       t->mode_mask = T_UNINITIALIZED;
-                       return;
-               }
-               {
-               struct dvb_tuner_ops *xc_tuner_ops;
-               xc_tuner_ops = &t->fe.ops.tuner_ops;
-               if(xc_tuner_ops->init != NULL)
-                       xc_tuner_ops->init(&t->fe);
-               }
+               /* if_khz will be set when the digital dvb_attach() occurs */
+               xc5000_cfg.if_khz         = 0;
+               if (!dvb_attach(xc5000_attach,
+                               &t->fe, t->i2c->adapter, &xc5000_cfg))
+                       goto attach_failed;
+               tune_now = 0;
+               break;
+       }
+       case TUNER_NXP_TDA18271:
+       {
+               struct tda18271_config cfg = {
+                       .config = t->config,
+               };
+
+               if (!dvb_attach(tda18271_attach, &t->fe, t->i2c->addr,
+                               t->i2c->adapter, &cfg))
+                       goto attach_failed;
+               tune_now = 0;
                break;
+       }
        default:
-               if (simple_tuner_attach(&t->fe, t->i2c->adapter,
-                                       t->i2c->addr, t->type) == NULL) {
-                       t->type = TUNER_ABSENT;
-                       t->mode_mask = T_UNINITIALIZED;
-                       return;
-               }
+               if (!dvb_attach(simple_tuner_attach, &t->fe,
+                               t->i2c->adapter, t->i2c->addr, t->type))
+                       goto attach_failed;
+
                break;
        }
 
        if ((NULL == analog_ops->set_params) &&
            (fe_tuner_ops->set_analog_params)) {
-               strlcpy(t->i2c->name, fe_tuner_ops->info.name,
-                       sizeof(t->i2c->name));
+
+               t->name = fe_tuner_ops->info.name;
 
                t->fe.analog_demod_priv = t;
-               memcpy(analog_ops, &tuner_core_ops,
+               memcpy(analog_ops, &tuner_analog_ops,
                       sizeof(struct analog_demod_ops));
+
        } else {
-               strlcpy(t->i2c->name, analog_ops->info.name,
-                       sizeof(t->i2c->name));
+               t->name = analog_ops->info.name;
        }
 
-       tuner_dbg("type set to %s\n", t->i2c->name);
+       tuner_dbg("type set to %s\n", t->name);
 
        if (t->mode_mask == T_UNINITIALIZED)
                t->mode_mask = new_mode_mask;
 
-       set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq);
+       /* Some tuners require more initialization setup before use,
+          such as firmware download or device calibration.
+          trying to set a frequency here will just fail
+          FIXME: better to move set_freq to the tuner code. This is needed
+          on analog tuners for PLL to properly work
+        */
+       if (tune_now)
+               set_freq(c, (V4L2_TUNER_RADIO == t->mode) ?
+                           t->radio_freq : t->tv_freq);
+
        tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n",
                  c->adapter->name, c->driver->driver.name, c->addr << 1, type,
                  t->mode_mask);
-       tuner_i2c_address_check(t);
+       return;
+
+attach_failed:
+       tuner_dbg("Tuner attach for type = %d failed.\n", t->type);
+       t->type = TUNER_ABSENT;
+       t->mode_mask = T_UNINITIALIZED;
+
+       return;
 }
 
 /*
@@ -494,22 +495,24 @@ static void set_type(struct i2c_client *c, unsigned int type,
 
 static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
 {
-       struct tuner *t = i2c_get_clientdata(c);
-
-       tuner_dbg("set addr for type %i\n", t->type);
+       struct tuner *t = to_tuner(i2c_get_clientdata(c));
 
        if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
                (t->mode_mask & tun_setup->mode_mask))) ||
                (tun_setup->addr == c->addr)) {
                        set_type(c, tun_setup->type, tun_setup->mode_mask,
                                 tun_setup->config, tun_setup->tuner_callback);
-       }
+       } else
+               tuner_dbg("set addr discarded for type %i, mask %x. "
+                         "Asked to change tuner at addr 0x%02x, with mask %x\n",
+                         t->type, t->mode_mask,
+                         tun_setup->addr, tun_setup->mode_mask);
 }
 
 static inline int check_mode(struct tuner *t, char *cmd)
 {
        if ((1 << t->mode & t->mode_mask) == 0) {
-               return EINVAL;
+               return -EINVAL;
        }
 
        switch (t->mode) {
@@ -646,8 +649,8 @@ static void tuner_status(struct dvb_frontend *fe)
 {
        struct tuner *t = fe->analog_demod_priv;
        unsigned long freq, freq_fraction;
-       struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
-       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+       struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops;
+       struct analog_demod_ops *analog_ops = &fe->ops.analog_ops;
        const char *p;
 
        switch (t->mode) {
@@ -703,11 +706,13 @@ static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode,
 
        t->mode = mode;
 
-       if (check_mode(t, cmd) == EINVAL) {
+       if (check_mode(t, cmd) == -EINVAL) {
+               tuner_dbg("Tuner doesn't support this mode. "
+                         "Putting tuner to sleep\n");
                t->mode = T_STANDBY;
                if (analog_ops->standby)
                        analog_ops->standby(&t->fe);
-               return EINVAL;
+               return -EINVAL;
        }
        return 0;
 }
@@ -725,296 +730,205 @@ static inline int check_v4l2(struct tuner *t)
        return 0;
 }
 
-static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
+static int tuner_s_type_addr(struct v4l2_subdev *sd, struct tuner_setup *type)
 {
-       struct tuner *t = i2c_get_clientdata(client);
-       struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
-       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+       struct tuner *t = to_tuner(sd);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-       if (tuner_debug>1)
-               v4l_i2c_print_ioctl(client,cmd);
+       tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n",
+                       type->type,
+                       type->addr,
+                       type->mode_mask,
+                       type->config);
 
-       switch (cmd) {
-       /* --- configuration --- */
-       case TUNER_SET_TYPE_ADDR:
-               tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n",
-                               ((struct tuner_setup *)arg)->type,
-                               ((struct tuner_setup *)arg)->addr,
-                               ((struct tuner_setup *)arg)->mode_mask,
-                               ((struct tuner_setup *)arg)->config);
-
-               set_addr(client, (struct tuner_setup *)arg);
-               break;
-       case AUDC_SET_RADIO:
-               if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
-                               == EINVAL)
-                       return 0;
-               if (t->radio_freq)
-                       set_freq(client, t->radio_freq);
-               break;
-       case TUNER_SET_STANDBY:
-               if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL)
-                       return 0;
-               t->mode = T_STANDBY;
-               if (analog_ops->standby)
-                       analog_ops->standby(&t->fe);
-               break;
-#ifdef CONFIG_VIDEO_V4L1
-       case VIDIOCSAUDIO:
-               if (check_mode(t, "VIDIOCSAUDIO") == EINVAL)
-                       return 0;
-               if (check_v4l2(t) == EINVAL)
-                       return 0;
-
-               /* Should be implemented, since bttv calls it */
-               tuner_dbg("VIDIOCSAUDIO not implemented.\n");
-               break;
-       case VIDIOCSCHAN:
-               {
-                       static const v4l2_std_id map[] = {
-                               [VIDEO_MODE_PAL] = V4L2_STD_PAL,
-                               [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
-                               [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
-                               [4 /* bttv */ ] = V4L2_STD_PAL_M,
-                               [5 /* bttv */ ] = V4L2_STD_PAL_N,
-                               [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
-                       };
-                       struct video_channel *vc = arg;
-
-                       if (check_v4l2(t) == EINVAL)
-                               return 0;
-
-                       if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==EINVAL)
-                               return 0;
-
-                       if (vc->norm < ARRAY_SIZE(map))
-                               t->std = map[vc->norm];
-                       tuner_fixup_std(t);
-                       if (t->tv_freq)
-                               set_tv_freq(client, t->tv_freq);
-                       return 0;
-               }
-       case VIDIOCSFREQ:
-               {
-                       unsigned long *v = arg;
+       set_addr(client, type);
+       return 0;
+}
 
-                       if (check_mode(t, "VIDIOCSFREQ") == EINVAL)
-                               return 0;
-                       if (check_v4l2(t) == EINVAL)
-                               return 0;
+static int tuner_s_radio(struct v4l2_subdev *sd)
+{
+       struct tuner *t = to_tuner(sd);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-                       set_freq(client, *v);
-                       return 0;
-               }
-       case VIDIOCGTUNER:
-               {
-                       struct video_tuner *vt = arg;
-
-                       if (check_mode(t, "VIDIOCGTUNER") == EINVAL)
-                               return 0;
-                       if (check_v4l2(t) == EINVAL)
-                               return 0;
-
-                       if (V4L2_TUNER_RADIO == t->mode) {
-                               if (fe_tuner_ops->get_status) {
-                                       u32 tuner_status;
-
-                                       fe_tuner_ops->get_status(&t->fe, &tuner_status);
-                                       if (tuner_status & TUNER_STATUS_STEREO)
-                                               vt->flags |= VIDEO_TUNER_STEREO_ON;
-                                       else
-                                               vt->flags &= ~VIDEO_TUNER_STEREO_ON;
-                               } else {
-                                       if (analog_ops->is_stereo) {
-                                               if (analog_ops->is_stereo(&t->fe))
-                                                       vt->flags |=
-                                                               VIDEO_TUNER_STEREO_ON;
-                                               else
-                                                       vt->flags &=
-                                                               ~VIDEO_TUNER_STEREO_ON;
-                                       }
-                               }
-                               if (analog_ops->has_signal)
-                                       vt->signal =
-                                               analog_ops->has_signal(&t->fe);
-
-                               vt->flags |= VIDEO_TUNER_LOW;   /* Allow freqs at 62.5 Hz */
-
-                               vt->rangelow = radio_range[0] * 16000;
-                               vt->rangehigh = radio_range[1] * 16000;
+       if (set_mode(client, t, V4L2_TUNER_RADIO, "s_radio") == -EINVAL)
+               return 0;
+       if (t->radio_freq)
+               set_freq(client, t->radio_freq);
+       return 0;
+}
 
-                       } else {
-                               vt->rangelow = tv_range[0] * 16;
-                               vt->rangehigh = tv_range[1] * 16;
-                       }
+static int tuner_s_power(struct v4l2_subdev *sd, int on)
+{
+       struct tuner *t = to_tuner(sd);
+       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
 
-                       return 0;
-               }
-       case VIDIOCGAUDIO:
-               {
-                       struct video_audio *va = arg;
-
-                       if (check_mode(t, "VIDIOCGAUDIO") == EINVAL)
-                               return 0;
-                       if (check_v4l2(t) == EINVAL)
-                               return 0;
-
-                       if (V4L2_TUNER_RADIO == t->mode) {
-                               if (fe_tuner_ops->get_status) {
-                                       u32 tuner_status;
-
-                                       fe_tuner_ops->get_status(&t->fe, &tuner_status);
-                                       va->mode = (tuner_status & TUNER_STATUS_STEREO)
-                                           ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
-                               } else if (analog_ops->is_stereo)
-                                       va->mode = analog_ops->is_stereo(&t->fe)
-                                           ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
-                       }
-                       return 0;
-               }
-#endif
-       case TUNER_SET_CONFIG:
-       {
-               struct v4l2_priv_tun_config *cfg = arg;
+       if (on)
+               return 0;
 
-               if (t->type != cfg->tuner)
-                       break;
+       tuner_dbg("Putting tuner to sleep\n");
 
-               if (analog_ops->set_config) {
-                       analog_ops->set_config(&t->fe, cfg->priv);
-                       break;
-               }
+       if (check_mode(t, "s_power") == -EINVAL)
+               return 0;
+       t->mode = T_STANDBY;
+       if (analog_ops->standby)
+               analog_ops->standby(&t->fe);
+       return 0;
+}
 
-               tuner_dbg("Tuner frontend module has no way to set config\n");
-               break;
+static int tuner_s_config(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *cfg)
+{
+       struct tuner *t = to_tuner(sd);
+       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+       if (t->type != cfg->tuner)
+               return 0;
+
+       if (analog_ops->set_config) {
+               analog_ops->set_config(&t->fe, cfg->priv);
+               return 0;
        }
-       /* --- v4l ioctls --- */
-       /* take care: bttv does userspace copying, we'll get a
-          kernel pointer here... */
-       case VIDIOC_S_STD:
-               {
-                       v4l2_std_id *id = arg;
-
-                       if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
-                                       == EINVAL)
-                               return 0;
-
-                       switch_v4l2();
-
-                       t->std = *id;
-                       tuner_fixup_std(t);
-                       if (t->tv_freq)
-                               set_freq(client, t->tv_freq);
-                       break;
-               }
-       case VIDIOC_S_FREQUENCY:
-               {
-                       struct v4l2_frequency *f = arg;
 
-                       if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
-                                       == EINVAL)
-                               return 0;
-                       switch_v4l2();
-                       set_freq(client,f->frequency);
+       tuner_dbg("Tuner frontend module has no way to set config\n");
+       return 0;
+}
 
-                       break;
-               }
-       case VIDIOC_G_FREQUENCY:
-               {
-                       struct v4l2_frequency *f = arg;
-
-                       if (check_mode(t, "VIDIOC_G_FREQUENCY") == EINVAL)
-                               return 0;
-                       switch_v4l2();
-                       f->type = t->mode;
-                       if (fe_tuner_ops->get_frequency) {
-                               u32 abs_freq;
-
-                               fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
-                               f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
-                                       (abs_freq * 2 + 125/2) / 125 :
-                                       (abs_freq + 62500/2) / 62500;
-                               break;
-                       }
-                       f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
-                               t->radio_freq : t->tv_freq;
-                       break;
-               }
-       case VIDIOC_G_TUNER:
-               {
-                       struct v4l2_tuner *tuner = arg;
-
-                       if (check_mode(t, "VIDIOC_G_TUNER") == EINVAL)
-                               return 0;
-                       switch_v4l2();
-
-                       tuner->type = t->mode;
-                       if (analog_ops->get_afc)
-                               tuner->afc = analog_ops->get_afc(&t->fe);
-                       if (t->mode == V4L2_TUNER_ANALOG_TV)
-                               tuner->capability |= V4L2_TUNER_CAP_NORM;
-                       if (t->mode != V4L2_TUNER_RADIO) {
-                               tuner->rangelow = tv_range[0] * 16;
-                               tuner->rangehigh = tv_range[1] * 16;
-                               break;
-                       }
+/* --- v4l ioctls --- */
+/* take care: bttv does userspace copying, we'll get a
+   kernel pointer here... */
+static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+       struct tuner *t = to_tuner(sd);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-                       /* radio mode */
-                       tuner->rxsubchans =
-                               V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
-                       if (fe_tuner_ops->get_status) {
-                               u32 tuner_status;
-
-                               fe_tuner_ops->get_status(&t->fe, &tuner_status);
-                               tuner->rxsubchans =
-                                       (tuner_status & TUNER_STATUS_STEREO) ?
-                                       V4L2_TUNER_SUB_STEREO :
-                                       V4L2_TUNER_SUB_MONO;
-                       } else {
-                               if (analog_ops->is_stereo) {
-                                       tuner->rxsubchans =
-                                               analog_ops->is_stereo(&t->fe) ?
-                                               V4L2_TUNER_SUB_STEREO :
-                                               V4L2_TUNER_SUB_MONO;
-                               }
-                       }
-                       if (analog_ops->has_signal)
-                               tuner->signal = analog_ops->has_signal(&t->fe);
-                       tuner->capability |=
-                           V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
-                       tuner->audmode = t->audmode;
-                       tuner->rangelow = radio_range[0] * 16000;
-                       tuner->rangehigh = radio_range[1] * 16000;
-                       break;
-               }
-       case VIDIOC_S_TUNER:
-               {
-                       struct v4l2_tuner *tuner = arg;
+       if (set_mode(client, t, V4L2_TUNER_ANALOG_TV, "s_std") == -EINVAL)
+               return 0;
+
+       switch_v4l2();
 
-                       if (check_mode(t, "VIDIOC_S_TUNER") == EINVAL)
-                               return 0;
+       t->std = std;
+       tuner_fixup_std(t);
+       if (t->tv_freq)
+               set_freq(client, t->tv_freq);
+       return 0;
+}
 
-                       switch_v4l2();
+static int tuner_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+{
+       struct tuner *t = to_tuner(sd);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-                       /* do nothing unless we're a radio tuner */
-                       if (t->mode != V4L2_TUNER_RADIO)
-                               break;
-                       t->audmode = tuner->audmode;
-                       set_radio_freq(client, t->radio_freq);
-                       break;
+       if (set_mode(client, t, f->type, "s_frequency") == -EINVAL)
+               return 0;
+       switch_v4l2();
+       set_freq(client, f->frequency);
+
+       return 0;
+}
+
+static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+{
+       struct tuner *t = to_tuner(sd);
+       struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+
+       if (check_mode(t, "g_frequency") == -EINVAL)
+               return 0;
+       switch_v4l2();
+       f->type = t->mode;
+       if (fe_tuner_ops->get_frequency) {
+               u32 abs_freq;
+
+               fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
+               f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
+                       DIV_ROUND_CLOSEST(abs_freq * 2, 125) :
+                       DIV_ROUND_CLOSEST(abs_freq, 62500);
+               return 0;
+       }
+       f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
+               t->radio_freq : t->tv_freq;
+       return 0;
+}
+
+static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+       struct tuner *t = to_tuner(sd);
+       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+       struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+
+       if (check_mode(t, "g_tuner") == -EINVAL)
+               return 0;
+       switch_v4l2();
+
+       vt->type = t->mode;
+       if (analog_ops->get_afc)
+               vt->afc = analog_ops->get_afc(&t->fe);
+       if (t->mode == V4L2_TUNER_ANALOG_TV)
+               vt->capability |= V4L2_TUNER_CAP_NORM;
+       if (t->mode != V4L2_TUNER_RADIO) {
+               vt->rangelow = tv_range[0] * 16;
+               vt->rangehigh = tv_range[1] * 16;
+               return 0;
+       }
+
+       /* radio mode */
+       vt->rxsubchans =
+               V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+       if (fe_tuner_ops->get_status) {
+               u32 tuner_status;
+
+               fe_tuner_ops->get_status(&t->fe, &tuner_status);
+               vt->rxsubchans =
+                       (tuner_status & TUNER_STATUS_STEREO) ?
+                       V4L2_TUNER_SUB_STEREO :
+                       V4L2_TUNER_SUB_MONO;
+       } else {
+               if (analog_ops->is_stereo) {
+                       vt->rxsubchans =
+                               analog_ops->is_stereo(&t->fe) ?
+                               V4L2_TUNER_SUB_STEREO :
+                               V4L2_TUNER_SUB_MONO;
                }
-       case VIDIOC_LOG_STATUS:
-               if (analog_ops->tuner_status)
-                       analog_ops->tuner_status(&t->fe);
-               break;
        }
+       if (analog_ops->has_signal)
+               vt->signal = analog_ops->has_signal(&t->fe);
+       vt->capability |=
+               V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+       vt->audmode = t->audmode;
+       vt->rangelow = radio_range[0] * 16000;
+       vt->rangehigh = radio_range[1] * 16000;
+       return 0;
+}
 
+static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+       struct tuner *t = to_tuner(sd);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+       if (check_mode(t, "s_tuner") == -EINVAL)
+               return 0;
+
+       switch_v4l2();
+
+       /* do nothing unless we're a radio tuner */
+       if (t->mode != V4L2_TUNER_RADIO)
+               return 0;
+       t->audmode = vt->audmode;
+       set_radio_freq(client, t->radio_freq);
+       return 0;
+}
+
+static int tuner_log_status(struct v4l2_subdev *sd)
+{
+       struct tuner *t = to_tuner(sd);
+       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+
+       if (analog_ops->tuner_status)
+               analog_ops->tuner_status(&t->fe);
        return 0;
 }
 
 static int tuner_suspend(struct i2c_client *c, pm_message_t state)
 {
-       struct tuner *t = i2c_get_clientdata(c);
+       struct tuner *t = to_tuner(i2c_get_clientdata(c));
 
        tuner_dbg("suspend\n");
        /* FIXME: power down ??? */
@@ -1023,7 +937,7 @@ static int tuner_suspend(struct i2c_client *c, pm_message_t state)
 
 static int tuner_resume(struct i2c_client *c)
 {
-       struct tuner *t = i2c_get_clientdata(c);
+       struct tuner *t = to_tuner(i2c_get_clientdata(c));
 
        tuner_dbg("resume\n");
        if (V4L2_TUNER_RADIO == t->mode) {
@@ -1036,6 +950,43 @@ static int tuner_resume(struct i2c_client *c)
        return 0;
 }
 
+static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+       struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+       /* TUNER_SET_CONFIG is still called by tuner-simple.c, so we have
+          to handle it here.
+          There must be a better way of doing this... */
+       switch (cmd) {
+       case TUNER_SET_CONFIG:
+               return tuner_s_config(sd, arg);
+       }
+       return -ENOIOCTLCMD;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tuner_core_ops = {
+       .log_status = tuner_log_status,
+       .s_std = tuner_s_std,
+       .s_power = tuner_s_power,
+};
+
+static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = {
+       .s_radio = tuner_s_radio,
+       .g_tuner = tuner_g_tuner,
+       .s_tuner = tuner_s_tuner,
+       .s_frequency = tuner_s_frequency,
+       .g_frequency = tuner_g_frequency,
+       .s_type_addr = tuner_s_type_addr,
+       .s_config = tuner_s_config,
+};
+
+static const struct v4l2_subdev_ops tuner_ops = {
+       .core = &tuner_core_ops,
+       .tuner = &tuner_tuner_ops,
+};
+
 /* ---------------------------------------------------------------------- */
 
 static LIST_HEAD(tuner_list);
@@ -1056,7 +1007,7 @@ static void tuner_lookup(struct i2c_adapter *adap,
                int mode_mask;
 
                if (pos->i2c->adapter != adap ||
-                   pos->i2c->driver->id != I2C_DRIVERID_TUNER)
+                   strcmp(pos->i2c->driver->driver.name, "tuner"))
                        continue;
 
                mode_mask = pos->mode_mask & ~T_STANDBY;
@@ -1074,7 +1025,8 @@ static void tuner_lookup(struct i2c_adapter *adap,
 /* During client attach, set_type is called by adapter's attach_inform callback.
    set_type must then be completed by tuner_probe.
  */
-static int tuner_probe(struct i2c_client *client)
+static int tuner_probe(struct i2c_client *client,
+                      const struct i2c_device_id *id)
 {
        struct tuner *t;
        struct tuner *radio;
@@ -1083,9 +1035,9 @@ static int tuner_probe(struct i2c_client *client)
        t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
        if (NULL == t)
                return -ENOMEM;
+       v4l2_i2c_subdev_init(&t->sd, client, &tuner_ops);
        t->i2c = client;
-       strlcpy(client->name, "(tuner unset)", sizeof(client->name));
-       i2c_set_clientdata(client, t);
+       t->name = "(tuner unset)";
        t->type = UNSET;
        t->audmode = V4L2_TUNER_MODE_STEREO;
        t->mode_mask = T_UNINITIALIZED;
@@ -1112,8 +1064,9 @@ static int tuner_probe(struct i2c_client *client)
        if (!no_autodetect) {
                switch (client->addr) {
                case 0x10:
-                       if (tea5761_autodetection(t->i2c->adapter, t->i2c->addr)
-                                       != EINVAL) {
+                       if (tuner_symbol_probe(tea5761_autodetection,
+                                              t->i2c->adapter,
+                                              t->i2c->addr) >= 0) {
                                t->type = TUNER_TEA5761;
                                t->mode_mask = T_RADIO;
                                t->mode = T_STANDBY;
@@ -1125,15 +1078,15 @@ static int tuner_probe(struct i2c_client *client)
 
                                goto register_client;
                        }
-                       break;
+                       return -ENODEV;
                case 0x42:
                case 0x43:
                case 0x4a:
                case 0x4b:
                        /* If chip is not tda8290, don't register.
                           since it can be tda9887*/
-                       if (tda829x_probe(t->i2c->adapter,
-                                         t->i2c->addr) == 0) {
+                       if (tuner_symbol_probe(tda829x_probe, t->i2c->adapter,
+                                              t->i2c->addr) >= 0) {
                                tuner_dbg("tda829x detected\n");
                        } else {
                                /* Default is being tda9887 */
@@ -1145,8 +1098,9 @@ static int tuner_probe(struct i2c_client *client)
                        }
                        break;
                case 0x60:
-                       if (tea5767_autodetection(t->i2c->adapter, t->i2c->addr)
-                                       != EINVAL) {
+                       if (tuner_symbol_probe(tea5767_autodetection,
+                                              t->i2c->adapter, t->i2c->addr)
+                                       >= 0) {
                                t->type = TUNER_TEA5767;
                                t->mode_mask = T_RADIO;
                                t->mode = T_STANDBY;
@@ -1192,52 +1146,18 @@ register_client:
        } else {
                t->mode = V4L2_TUNER_DIGITAL_TV;
        }
-       set_type(client, t->type, t->mode_mask, t->config, t->tuner_callback);
+       set_type(client, t->type, t->mode_mask, t->config, t->fe.callback);
        list_add_tail(&t->list, &tuner_list);
        return 0;
 }
 
-static int tuner_legacy_probe(struct i2c_adapter *adap)
-{
-       if (0 != addr) {
-               normal_i2c[0] = addr;
-               normal_i2c[1] = I2C_CLIENT_END;
-       }
-
-       if ((adap->class & I2C_CLASS_TV_ANALOG) == 0)
-               return 0;
-
-       /* HACK: Ignore 0x6b and 0x6f on cx88 boards.
-        * FusionHDTV5 RT Gold has an ir receiver at 0x6b
-        * and an RTC at 0x6f which can get corrupted if probed.
-        */
-       if ((adap->id == I2C_HW_B_CX2388x) ||
-           (adap->id == I2C_HW_B_CX23885)) {
-               unsigned int i = 0;
-
-               while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END)
-                       i += 2;
-               if (i + 4 < I2C_CLIENT_MAX_OPTS) {
-                       ignore[i+0] = adap->nr;
-                       ignore[i+1] = 0x6b;
-                       ignore[i+2] = adap->nr;
-                       ignore[i+3] = 0x6f;
-                       ignore[i+4] = I2C_CLIENT_END;
-               } else
-                       printk(KERN_WARNING "tuner: "
-                              "too many options specified "
-                              "in i2c probe ignore list!\n");
-       }
-       return 1;
-}
-
 static int tuner_remove(struct i2c_client *client)
 {
-       struct tuner *t = i2c_get_clientdata(client);
-       struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+       struct tuner *t = to_tuner(i2c_get_clientdata(client));
 
-       if (analog_ops->release)
-               analog_ops->release(&t->fe);
+       v4l2_device_unregister_subdev(&t->sd);
+       tuner_detach(&t->fe);
+       t->fe.analog_demod_priv = NULL;
 
        list_del(&t->list);
        kfree(t);
@@ -1246,18 +1166,25 @@ static int tuner_remove(struct i2c_client *client)
 
 /* ----------------------------------------------------------------------- */
 
+/* This driver supports many devices and the idea is to let the driver
+   detect which device is present. So rather than listing all supported
+   devices here, we pretend to support a single, fake device type. */
+static const struct i2c_device_id tuner_id[] = {
+       { "tuner", }, /* autodetect */
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, tuner_id);
+
 static struct v4l2_i2c_driver_data v4l2_i2c_data = {
        .name = "tuner",
-       .driverid = I2C_DRIVERID_TUNER,
-       .command = tuner_command,
        .probe = tuner_probe,
        .remove = tuner_remove,
+       .command = tuner_command,
        .suspend = tuner_suspend,
        .resume = tuner_resume,
-       .legacy_probe = tuner_legacy_probe,
+       .id_table = tuner_id,
 };
 
-
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
  * ---------------------------------------------------------------------------