]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/media/usb/dvb-usb/cxusb.c
Merge tag 'v5.2-rc4' into media/master
[linux.git] / drivers / media / usb / dvb-usb / cxusb.c
index 8039ba4ebf6805bee70fe7cbce0c3749b2892245..bac0778f7defc1c653762def3f777d7294518bcc 100644 (file)
  * design, so it can be reused for the "analogue-only" device (if it will
  * appear at all).
  *
- * TODO: Use the cx25840-driver for the analogue part
  *
  * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@posteo.de)
  * Copyright (C) 2006 Michael Krufky (mkrufky@linuxtv.org)
  * Copyright (C) 2006, 2007 Chris Pascoe (c.pascoe@itee.uq.edu.au)
+ * Copyright (C) 2011, 2017 Maciej S. Szmigiero (mail@maciej.szmigiero.name)
  *
  * see Documentation/media/dvb-drivers/dvb-usb.rst for more information
  */
 #include <media/tuner.h>
-#include <linux/vmalloc.h>
-#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/device.h>
 #include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/vmalloc.h>
 
 #include "cxusb.h"
 
 #include "si2157.h"
 
 /* debug */
-static int dvb_usb_cxusb_debug;
+int dvb_usb_cxusb_debug;
 module_param_named(debug, dvb_usb_cxusb_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
+MODULE_PARM_DESC(debug, "set debugging level (see cxusb.h)."
+                DVB_USB_DEBUG_STATUS);
 
 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
-#define deb_info(args...)   dprintk(dvb_usb_cxusb_debug, 0x03, args)
-#define deb_i2c(args...)    dprintk(dvb_usb_cxusb_debug, 0x02, args)
+#define deb_info(args...)   dprintk(dvb_usb_cxusb_debug, CXUSB_DBG_MISC, args)
+#define deb_i2c(args...)    dprintk(dvb_usb_cxusb_debug, CXUSB_DBG_I2C, args)
 
-static int cxusb_ctrl_msg(struct dvb_usb_device *d,
-                         u8 cmd, const u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+enum cxusb_table_index {
+       MEDION_MD95700,
+       DVICO_BLUEBIRD_LG064F_COLD,
+       DVICO_BLUEBIRD_LG064F_WARM,
+       DVICO_BLUEBIRD_DUAL_1_COLD,
+       DVICO_BLUEBIRD_DUAL_1_WARM,
+       DVICO_BLUEBIRD_LGZ201_COLD,
+       DVICO_BLUEBIRD_LGZ201_WARM,
+       DVICO_BLUEBIRD_TH7579_COLD,
+       DVICO_BLUEBIRD_TH7579_WARM,
+       DIGITALNOW_BLUEBIRD_DUAL_1_COLD,
+       DIGITALNOW_BLUEBIRD_DUAL_1_WARM,
+       DVICO_BLUEBIRD_DUAL_2_COLD,
+       DVICO_BLUEBIRD_DUAL_2_WARM,
+       DVICO_BLUEBIRD_DUAL_4,
+       DVICO_BLUEBIRD_DVB_T_NANO_2,
+       DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM,
+       AVERMEDIA_VOLAR_A868R,
+       DVICO_BLUEBIRD_DUAL_4_REV_2,
+       CONEXANT_D680_DMB,
+       MYGICA_D689,
+       MYGICA_T230,
+       NR__cxusb_table_index
+};
+
+static struct usb_device_id cxusb_table[];
+
+int cxusb_ctrl_msg(struct dvb_usb_device *d,
+                  u8 cmd, const u8 *wbuf, int wlen, u8 *rbuf, int rlen)
 {
        struct cxusb_state *st = d->priv;
        int ret;
@@ -86,7 +117,8 @@ static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff)
        struct cxusb_state *st = d->priv;
        u8 o[2], i;
 
-       if (st->gpio_write_state[GPIO_TUNER] == onoff)
+       if (st->gpio_write_state[GPIO_TUNER] == onoff &&
+           !st->gpio_write_refresh[GPIO_TUNER])
                return;
 
        o[0] = GPIO_TUNER;
@@ -97,10 +129,11 @@ static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff)
                deb_info("gpio_write failed.\n");
 
        st->gpio_write_state[GPIO_TUNER] = onoff;
+       st->gpio_write_refresh[GPIO_TUNER] = false;
 }
 
 static int cxusb_bluebird_gpio_rw(struct dvb_usb_device *d, u8 changemask,
-                                u8 newval)
+                                 u8 newval)
 {
        u8 o[2], gpio_state;
        int rc;
@@ -128,7 +161,7 @@ static void cxusb_nano2_led(struct dvb_usb_device *d, int onoff)
 }
 
 static int cxusb_d680_dmb_gpio_tuner(struct dvb_usb_device *d,
-               u8 addr, int onoff)
+                                    u8 addr, int onoff)
 {
        u8  o[2] = {addr, onoff};
        u8  i;
@@ -138,12 +171,12 @@ static int cxusb_d680_dmb_gpio_tuner(struct dvb_usb_device *d,
 
        if (rc < 0)
                return rc;
+
        if (i == 0x01)
                return 0;
-       else {
-               deb_info("gpio_write failed.\n");
-               return -EIO;
-       }
+
+       deb_info("gpio_write failed.\n");
+       return -EIO;
 }
 
 /* I2C */
@@ -158,7 +191,6 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
                return -EAGAIN;
 
        for (i = 0; i < num; i++) {
-
                if (le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_MEDION)
                        switch (msg[i].addr) {
                        case 0x63:
@@ -184,13 +216,13 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
                        obuf[2] = msg[i].addr;
                        if (cxusb_ctrl_msg(d, CMD_I2C_READ,
                                           obuf, 3,
-                                          ibuf, 1+msg[i].len) < 0) {
+                                          ibuf, 1 + msg[i].len) < 0) {
                                warn("i2c read failed");
                                break;
                        }
                        memcpy(msg[i].buf, &ibuf[1], msg[i].len);
-               } else if (i+1 < num && (msg[i+1].flags & I2C_M_RD) &&
-                          msg[i].addr == msg[i+1].addr) {
+               } else if (i + 1 < num && (msg[i + 1].flags & I2C_M_RD) &&
+                          msg[i].addr == msg[i + 1].addr) {
                        /* write to then read from same address */
                        u8 obuf[MAX_XFER_SIZE], ibuf[MAX_XFER_SIZE];
 
@@ -207,19 +239,19 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
                                goto unlock;
                        }
                        obuf[0] = msg[i].len;
-                       obuf[1] = msg[i+1].len;
+                       obuf[1] = msg[i + 1].len;
                        obuf[2] = msg[i].addr;
                        memcpy(&obuf[3], msg[i].buf, msg[i].len);
 
                        if (cxusb_ctrl_msg(d, CMD_I2C_READ,
-                                          obuf, 3+msg[i].len,
-                                          ibuf, 1+msg[i+1].len) < 0)
+                                          obuf, 3 + msg[i].len,
+                                          ibuf, 1 + msg[i + 1].len) < 0)
                                break;
 
                        if (ibuf[0] != 0x08)
                                deb_i2c("i2c read may have failed\n");
 
-                       memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len);
+                       memcpy(msg[i + 1].buf, &ibuf[1], msg[i + 1].len);
 
                        i++;
                } else {
@@ -237,7 +269,7 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
                        memcpy(&obuf[2], msg[i].buf, msg[i].len);
 
                        if (cxusb_ctrl_msg(d, CMD_I2C_WRITE, obuf,
-                                          2+msg[i].len, &ibuf,1) < 0)
+                                          2 + msg[i].len, &ibuf, 1) < 0)
                                break;
                        if (ibuf != 0x08)
                                deb_i2c("i2c write may have failed\n");
@@ -256,7 +288,7 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
 
 static u32 cxusb_i2c_func(struct i2c_adapter *adapter)
 {
-       return I2C_FUNC_I2C;
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
 }
 
 static struct i2c_algorithm cxusb_i2c_algo = {
@@ -264,29 +296,67 @@ static struct i2c_algorithm cxusb_i2c_algo = {
        .functionality = cxusb_i2c_func,
 };
 
-static int cxusb_power_ctrl(struct dvb_usb_device *d, int onoff)
+static int _cxusb_power_ctrl(struct dvb_usb_device *d, int onoff)
 {
        u8 b = 0;
+
+       deb_info("setting power %s\n", onoff ? "ON" : "OFF");
+
        if (onoff)
                return cxusb_ctrl_msg(d, CMD_POWER_ON, &b, 1, NULL, 0);
        else
                return cxusb_ctrl_msg(d, CMD_POWER_OFF, &b, 1, NULL, 0);
 }
 
+static int cxusb_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+       bool is_medion = d->props.devices[0].warm_ids[0] == &cxusb_table[MEDION_MD95700];
+       int ret;
+
+       if (is_medion && !onoff) {
+               struct cxusb_medion_dev *cxdev = d->priv;
+
+               mutex_lock(&cxdev->open_lock);
+
+               if (cxdev->open_type == CXUSB_OPEN_ANALOG) {
+                       deb_info("preventing DVB core from setting power OFF while we are in analog mode\n");
+                       ret = -EBUSY;
+                       goto ret_unlock;
+               }
+       }
+
+       ret = _cxusb_power_ctrl(d, onoff);
+
+ret_unlock:
+       if (is_medion && !onoff) {
+               struct cxusb_medion_dev *cxdev = d->priv;
+
+               mutex_unlock(&cxdev->open_lock);
+       }
+
+       return ret;
+}
+
 static int cxusb_aver_power_ctrl(struct dvb_usb_device *d, int onoff)
 {
        int ret;
+
        if (!onoff)
                return cxusb_ctrl_msg(d, CMD_POWER_OFF, NULL, 0, NULL, 0);
        if (d->state == DVB_USB_STATE_INIT &&
            usb_set_interface(d->udev, 0, 0) < 0)
                err("set interface failed");
-       do {} while (!(ret = cxusb_ctrl_msg(d, CMD_POWER_ON, NULL, 0, NULL, 0)) &&
-                  !(ret = cxusb_ctrl_msg(d, 0x15, NULL, 0, NULL, 0)) &&
-                  !(ret = cxusb_ctrl_msg(d, 0x17, NULL, 0, NULL, 0)) && 0);
+       do {
+               /* Nothing */
+       } while (!(ret = cxusb_ctrl_msg(d, CMD_POWER_ON, NULL, 0, NULL, 0)) &&
+                !(ret = cxusb_ctrl_msg(d, 0x15, NULL, 0, NULL, 0)) &&
+                !(ret = cxusb_ctrl_msg(d, 0x17, NULL, 0, NULL, 0)) && 0);
+
        if (!ret) {
-               /* FIXME: We don't know why, but we need to configure the
-                * lgdt3303 with the register settings below on resume */
+               /*
+                * FIXME: We don't know why, but we need to configure the
+                * lgdt3303 with the register settings below on resume
+                */
                int i;
                u8 buf;
                static const u8 bufs[] = {
@@ -304,7 +374,7 @@ static int cxusb_aver_power_ctrl(struct dvb_usb_device *d, int onoff)
                msleep(20);
                for (i = 0; i < ARRAY_SIZE(bufs); i += 4 / sizeof(u8)) {
                        ret = cxusb_ctrl_msg(d, CMD_I2C_WRITE,
-                                            bufs+i, 4, &buf, 1);
+                                            bufs + i, 4, &buf, 1);
                        if (ret)
                                break;
                        if (buf != 0x8)
@@ -317,6 +387,7 @@ static int cxusb_aver_power_ctrl(struct dvb_usb_device *d, int onoff)
 static int cxusb_bluebird_power_ctrl(struct dvb_usb_device *d, int onoff)
 {
        u8 b = 0;
+
        if (onoff)
                return cxusb_ctrl_msg(d, CMD_POWER_ON, &b, 1, NULL, 0);
        else
@@ -338,6 +409,7 @@ static int cxusb_d680_dmb_power_ctrl(struct dvb_usb_device *d, int onoff)
 {
        int ret;
        u8  b;
+
        ret = cxusb_power_ctrl(d, onoff);
        if (!onoff)
                return ret;
@@ -350,11 +422,26 @@ static int cxusb_d680_dmb_power_ctrl(struct dvb_usb_device *d, int onoff)
 
 static int cxusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
 {
+       struct dvb_usb_device *dvbdev = adap->dev;
+       bool is_medion = dvbdev->props.devices[0].warm_ids[0] ==
+               &cxusb_table[MEDION_MD95700];
        u8 buf[2] = { 0x03, 0x00 };
+
+       if (is_medion && onoff) {
+               int ret;
+
+               ret = cxusb_medion_get(dvbdev, CXUSB_OPEN_DIGITAL);
+               if (ret != 0)
+                       return ret;
+       }
+
        if (onoff)
-               cxusb_ctrl_msg(adap->dev, CMD_STREAMING_ON, buf, 2, NULL, 0);
+               cxusb_ctrl_msg(dvbdev, CMD_STREAMING_ON, buf, 2, NULL, 0);
        else
-               cxusb_ctrl_msg(adap->dev, CMD_STREAMING_OFF, NULL, 0, NULL, 0);
+               cxusb_ctrl_msg(dvbdev, CMD_STREAMING_OFF, NULL, 0, NULL, 0);
+
+       if (is_medion && !onoff)
+               cxusb_medion_put(dvbdev);
 
        return 0;
 }
@@ -370,7 +457,7 @@ static int cxusb_aver_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
 }
 
 static int cxusb_read_status(struct dvb_frontend *fe,
-                                 enum fe_status *status)
+                            enum fe_status *status)
 {
        struct dvb_usb_adapter *adap = (struct dvb_usb_adapter *)fe->dvb->priv;
        struct cxusb_state *state = (struct cxusb_state *)adap->dev->priv;
@@ -403,8 +490,8 @@ static void cxusb_d680_dmb_drain_message(struct dvb_usb_device *d)
                return;
        while (1) {
                if (usb_bulk_msg(d->udev,
-                       usb_rcvbulkpipe(d->udev, ep),
-                       junk, junk_len, &rd_count, timeout) < 0)
+                                usb_rcvbulkpipe(d->udev, ep),
+                                junk, junk_len, &rd_count, timeout) < 0)
                        break;
                if (!rd_count)
                        break;
@@ -426,8 +513,8 @@ static void cxusb_d680_dmb_drain_video(struct dvb_usb_device *d)
                return;
        while (1) {
                if (usb_bulk_msg(d->udev,
-                       usb_rcvbulkpipe(d->udev, p->endpoint),
-                       junk, junk_len, &rd_count, timeout) < 0)
+                                usb_rcvbulkpipe(d->udev, p->endpoint),
+                                junk, junk_len, &rd_count, timeout) < 0)
                        break;
                if (!rd_count)
                        break;
@@ -435,17 +522,18 @@ static void cxusb_d680_dmb_drain_video(struct dvb_usb_device *d)
        kfree(junk);
 }
 
-static int cxusb_d680_dmb_streaming_ctrl(
-               struct dvb_usb_adapter *adap, int onoff)
+static int cxusb_d680_dmb_streaming_ctrl(struct dvb_usb_adapter *adap,
+                                        int onoff)
 {
        if (onoff) {
                u8 buf[2] = { 0x03, 0x00 };
+
                cxusb_d680_dmb_drain_video(adap->dev);
                return cxusb_ctrl_msg(adap->dev, CMD_STREAMING_ON,
-                       buf, sizeof(buf), NULL, 0);
+                                     buf, sizeof(buf), NULL, 0);
        } else {
                int ret = cxusb_ctrl_msg(adap->dev,
-                       CMD_STREAMING_OFF, NULL, 0, NULL, 0);
+                                        CMD_STREAMING_OFF, NULL, 0, NULL, 0);
                return ret;
        }
 }
@@ -465,8 +553,12 @@ static int cxusb_rc_query(struct dvb_usb_device *d)
 static int cxusb_bluebird2_rc_query(struct dvb_usb_device *d)
 {
        u8 ircode[4];
-       struct i2c_msg msg = { .addr = 0x6b, .flags = I2C_M_RD,
-                              .buf = ircode, .len = 4 };
+       struct i2c_msg msg = {
+               .addr = 0x6b,
+               .flags = I2C_M_RD,
+               .buf = ircode,
+               .len = 4
+       };
 
        if (cxusb_i2c_xfer(&d->i2c_adap, &msg, 1) != 1)
                return 0;
@@ -490,13 +582,13 @@ static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d)
        return 0;
 }
 
-static int cxusb_dee1601_demod_init(struct dvb_frontendfe)
+static int cxusb_dee1601_demod_init(struct dvb_frontend *fe)
 {
-       static u8 clock_config []  = { CLOCK_CTL,  0x38, 0x28 };
-       static u8 reset []         = { RESET,      0x80 };
-       static u8 adc_ctl_1_cfg [] = { ADC_CTL_1,  0x40 };
-       static u8 agc_cfg []       = { AGC_TARGET, 0x28, 0x20 };
-       static u8 gpp_ctl_cfg []   = { GPP_CTL,    0x33 };
+       static u8 clock_config[]   = { CLOCK_CTL,  0x38, 0x28 };
+       static u8 reset[]          = { RESET,      0x80 };
+       static u8 adc_ctl_1_cfg[]  = { ADC_CTL_1,  0x40 };
+       static u8 agc_cfg[]        = { AGC_TARGET, 0x28, 0x20 };
+       static u8 gpp_ctl_cfg[]    = { GPP_CTL,    0x33 };
        static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
 
        mt352_write(fe, clock_config,   sizeof(clock_config));
@@ -511,13 +603,14 @@ static int cxusb_dee1601_demod_init(struct dvb_frontend* fe)
        return 0;
 }
 
-static int cxusb_mt352_demod_init(struct dvb_frontend* fe)
-{      /* used in both lgz201 and th7579 */
-       static u8 clock_config []  = { CLOCK_CTL,  0x38, 0x29 };
-       static u8 reset []         = { RESET,      0x80 };
-       static u8 adc_ctl_1_cfg [] = { ADC_CTL_1,  0x40 };
-       static u8 agc_cfg []       = { AGC_TARGET, 0x24, 0x20 };
-       static u8 gpp_ctl_cfg []   = { GPP_CTL,    0x33 };
+static int cxusb_mt352_demod_init(struct dvb_frontend *fe)
+{
+       /* used in both lgz201 and th7579 */
+       static u8 clock_config[]   = { CLOCK_CTL,  0x38, 0x29 };
+       static u8 reset[]          = { RESET,      0x80 };
+       static u8 adc_ctl_1_cfg[]  = { ADC_CTL_1,  0x40 };
+       static u8 agc_cfg[]        = { AGC_TARGET, 0x24, 0x20 };
+       static u8 gpp_ctl_cfg[]    = { GPP_CTL,    0x33 };
        static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
 
        mt352_write(fe, clock_config,   sizeof(clock_config));
@@ -627,9 +720,21 @@ static struct max2165_config mygica_d689_max2165_cfg = {
 /* Callbacks for DVB USB */
 static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
 {
+       struct dvb_usb_device *dvbdev = adap->dev;
+       bool is_medion = dvbdev->props.devices[0].warm_ids[0] ==
+               &cxusb_table[MEDION_MD95700];
+
        dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe,
-                  &adap->dev->i2c_adap, 0x61,
+                  &dvbdev->i2c_adap, 0x61,
                   TUNER_PHILIPS_FMD1216ME_MK3);
+
+       if (is_medion && adap->fe_adap[0].fe)
+               /*
+                * make sure that DVB core won't put to sleep (reset, really)
+                * tuner when we might be open in analog mode
+                */
+               adap->fe_adap[0].fe->ops.tuner_ops.sleep = NULL;
+
        return 0;
 }
 
@@ -642,7 +747,8 @@ static int cxusb_dee1601_tuner_attach(struct dvb_usb_adapter *adap)
 
 static int cxusb_lgz201_tuner_attach(struct dvb_usb_adapter *adap)
 {
-       dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, NULL, DVB_PLL_LG_Z201);
+       dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61,
+                  NULL, DVB_PLL_LG_Z201);
        return 0;
 }
 
@@ -702,7 +808,7 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap)
        adap->fe_adap[0].fe->callback = dvico_bluebird_xc2028_callback;
 
        fe = dvb_attach(xc2028_attach, adap->fe_adap[0].fe, &cfg);
-       if (fe == NULL || fe->ops.tuner_ops.set_config == NULL)
+       if (!fe || !fe->ops.tuner_ops.set_config)
                return -EIO;
 
        fe->ops.tuner_ops.set_config(fe, &ctl);
@@ -720,33 +826,120 @@ static int cxusb_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
 static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap)
 {
        struct dvb_frontend *fe;
+
        fe = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe,
                        &adap->dev->i2c_adap, &d680_dmb_tuner);
-       return (fe == NULL) ? -EIO : 0;
+       return (!fe) ? -EIO : 0;
 }
 
 static int cxusb_mygica_d689_tuner_attach(struct dvb_usb_adapter *adap)
 {
        struct dvb_frontend *fe;
+
        fe = dvb_attach(max2165_attach, adap->fe_adap[0].fe,
                        &adap->dev->i2c_adap, &mygica_d689_max2165_cfg);
-       return (fe == NULL) ? -EIO : 0;
+       return (!fe) ? -EIO : 0;
 }
 
-static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap)
+static int cxusb_medion_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)
 {
+       struct dvb_usb_adapter *adap = fe->dvb->priv;
+       struct dvb_usb_device *dvbdev = adap->dev;
+
+       if (acquire)
+               return cxusb_medion_get(dvbdev, CXUSB_OPEN_DIGITAL);
+
+       cxusb_medion_put(dvbdev);
+
+       return 0;
+}
+
+static int cxusb_medion_set_mode(struct dvb_usb_device *dvbdev, bool digital)
+{
+       struct cxusb_state *st = dvbdev->priv;
+       int ret;
        u8 b;
-       if (usb_set_interface(adap->dev->udev, 0, 6) < 0)
-               err("set interface failed");
+       unsigned int i;
 
-       cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, &b, 1);
+       /*
+        * switching mode while doing an I2C transaction often causes
+        * the device to crash
+        */
+       mutex_lock(&dvbdev->i2c_mutex);
+
+       if (digital) {
+               ret = usb_set_interface(dvbdev->udev, 0, 6);
+               if (ret != 0) {
+                       dev_err(&dvbdev->udev->dev,
+                               "digital interface selection failed (%d)\n",
+                               ret);
+                       goto ret_unlock;
+               }
+       } else {
+               ret = usb_set_interface(dvbdev->udev, 0, 1);
+               if (ret != 0) {
+                       dev_err(&dvbdev->udev->dev,
+                               "analog interface selection failed (%d)\n",
+                               ret);
+                       goto ret_unlock;
+               }
+       }
+
+       /* pipes need to be cleared after setting interface */
+       ret = usb_clear_halt(dvbdev->udev, usb_rcvbulkpipe(dvbdev->udev, 1));
+       if (ret != 0)
+               dev_warn(&dvbdev->udev->dev,
+                        "clear halt on IN pipe failed (%d)\n",
+                        ret);
+
+       ret = usb_clear_halt(dvbdev->udev, usb_sndbulkpipe(dvbdev->udev, 1));
+       if (ret != 0)
+               dev_warn(&dvbdev->udev->dev,
+                        "clear halt on OUT pipe failed (%d)\n",
+                        ret);
+
+       ret = cxusb_ctrl_msg(dvbdev, digital ? CMD_DIGITAL : CMD_ANALOG,
+                            NULL, 0, &b, 1);
+       if (ret != 0) {
+               dev_err(&dvbdev->udev->dev, "mode switch failed (%d)\n",
+                       ret);
+               goto ret_unlock;
+       }
+
+       /* mode switch seems to reset GPIO states */
+       for (i = 0; i < ARRAY_SIZE(st->gpio_write_refresh); i++)
+               st->gpio_write_refresh[i] = true;
+
+ret_unlock:
+       mutex_unlock(&dvbdev->i2c_mutex);
+
+       return ret;
+}
+
+static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap)
+{
+       struct dvb_usb_device *dvbdev = adap->dev;
+       bool is_medion = dvbdev->props.devices[0].warm_ids[0] ==
+               &cxusb_table[MEDION_MD95700];
+
+       if (is_medion) {
+               int ret;
+
+               ret = cxusb_medion_set_mode(dvbdev, true);
+               if (ret)
+                       return ret;
+       }
 
        adap->fe_adap[0].fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config,
-                                        &adap->dev->i2c_adap);
-       if ((adap->fe_adap[0].fe) != NULL)
-               return 0;
+                                        &dvbdev->i2c_adap);
+       if (!adap->fe_adap[0].fe)
+               return -EIO;
 
-       return -EIO;
+       if (is_medion)
+               adap->fe_adap[0].fe->ops.ts_bus_ctrl =
+                       cxusb_medion_fe_ts_bus_ctrl;
+
+       return 0;
 }
 
 static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap)
@@ -760,7 +953,7 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap)
                                         &cxusb_lgdt3303_config,
                                         0x0e,
                                         &adap->dev->i2c_adap);
-       if ((adap->fe_adap[0].fe) != NULL)
+       if (adap->fe_adap[0].fe)
                return 0;
 
        return -EIO;
@@ -772,7 +965,7 @@ static int cxusb_aver_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap)
                                         &cxusb_aver_lgdt3303_config,
                                         0x0e,
                                         &adap->dev->i2c_adap);
-       if (adap->fe_adap[0].fe != NULL)
+       if (adap->fe_adap[0].fe)
                return 0;
 
        return -EIO;
@@ -788,7 +981,7 @@ static int cxusb_mt352_frontend_attach(struct dvb_usb_adapter *adap)
 
        adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_mt352_config,
                                         &adap->dev->i2c_adap);
-       if ((adap->fe_adap[0].fe) != NULL)
+       if (adap->fe_adap[0].fe)
                return 0;
 
        return -EIO;
@@ -803,13 +996,13 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_adapter *adap)
 
        adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_dee1601_config,
                                         &adap->dev->i2c_adap);
-       if ((adap->fe_adap[0].fe) != NULL)
+       if (adap->fe_adap[0].fe)
                return 0;
 
        adap->fe_adap[0].fe = dvb_attach(zl10353_attach,
                                         &cxusb_zl10353_dee1601_config,
                                         &adap->dev->i2c_adap);
-       if ((adap->fe_adap[0].fe) != NULL)
+       if (adap->fe_adap[0].fe)
                return 0;
 
        return -EIO;
@@ -819,8 +1012,12 @@ static int cxusb_dualdig4_frontend_attach(struct dvb_usb_adapter *adap)
 {
        u8 ircode[4];
        int i;
-       struct i2c_msg msg = { .addr = 0x6b, .flags = I2C_M_RD,
-                              .buf = ircode, .len = 4 };
+       struct i2c_msg msg = {
+               .addr = 0x6b,
+               .flags = I2C_M_RD,
+               .buf = ircode,
+               .len = 4
+       };
 
        if (usb_set_interface(adap->dev->udev, 0, 1) < 0)
                err("set interface failed");
@@ -836,7 +1033,7 @@ static int cxusb_dualdig4_frontend_attach(struct dvb_usb_adapter *adap)
                dvb_attach(zl10353_attach,
                           &cxusb_zl10353_xc3028_config_no_i2c_gate,
                           &adap->dev->i2c_adap);
-       if ((adap->fe_adap[0].fe) == NULL)
+       if (!adap->fe_adap[0].fe)
                return -EIO;
 
        /* try to determine if there is no IR decoder on the I2C bus */
@@ -934,7 +1131,7 @@ static struct dib7000p_config cxusb_dualdig4_rev2_config = {
 };
 
 struct dib0700_adapter_state {
-       int (*set_param_save)(struct dvb_frontend *);
+       int (*set_param_save)(struct dvb_frontend *fe);
        struct dib7000p_ops dib7000p_ops;
 };
 
@@ -953,14 +1150,15 @@ static int cxusb_dualdig4_rev2_frontend_attach(struct dvb_usb_adapter *adap)
                return -ENODEV;
 
        if (state->dib7000p_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 18,
-                                      &cxusb_dualdig4_rev2_config) < 0) {
-               printk(KERN_WARNING "Unable to enumerate dib7000p\n");
+                                               &cxusb_dualdig4_rev2_config) < 0) {
+               pr_warn("Unable to enumerate dib7000p\n");
                return -ENODEV;
        }
 
-       adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 0x80,
-                                             &cxusb_dualdig4_rev2_config);
-       if (adap->fe_adap[0].fe == NULL)
+       adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap,
+                                                      0x80,
+                                                      &cxusb_dualdig4_rev2_config);
+       if (!adap->fe_adap[0].fe)
                return -EIO;
 
        return 0;
@@ -993,11 +1191,16 @@ static int dib7070_set_param_override(struct dvb_frontend *fe)
        struct dib0700_adapter_state *state = adap->priv;
 
        u16 offset;
-       u8 band = BAND_OF_FREQUENCY(p->frequency/1000);
+       u8 band = BAND_OF_FREQUENCY(p->frequency / 1000);
+
        switch (band) {
-       case BAND_VHF: offset = 950; break;
+       case BAND_VHF:
+               offset = 950;
+               break;
        default:
-       case BAND_UHF: offset = 550; break;
+       case BAND_UHF:
+               offset = 550;
+               break;
        }
 
        state->dib7000p_ops.set_wbd_ref(fe, offset + dib0070_wbd_offset(fe));
@@ -1019,7 +1222,7 @@ static int cxusb_dualdig4_rev2_tuner_attach(struct dvb_usb_adapter *adap)
                                        DIBX000_I2C_INTERFACE_TUNER, 1);
 
        if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c,
-           &dib7070p_dib0070_config) == NULL)
+                      &dib7070p_dib0070_config) == NULL)
                return -ENODEV;
 
        st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params;
@@ -1042,13 +1245,13 @@ static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap)
        adap->fe_adap[0].fe = dvb_attach(zl10353_attach,
                                         &cxusb_zl10353_xc3028_config,
                                         &adap->dev->i2c_adap);
-       if ((adap->fe_adap[0].fe) != NULL)
+       if (adap->fe_adap[0].fe)
                return 0;
 
        adap->fe_adap[0].fe = dvb_attach(mt352_attach,
                                         &cxusb_mt352_xc3028_config,
                                         &adap->dev->i2c_adap);
-       if ((adap->fe_adap[0].fe) != NULL)
+       if (adap->fe_adap[0].fe)
                return 0;
 
        return -EIO;
@@ -1079,11 +1282,14 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap)
 
        /* Unblock all USB pipes */
        usb_clear_halt(d->udev,
-               usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
+                      usb_sndbulkpipe(d->udev,
+                                      d->props.generic_bulk_ctrl_endpoint));
        usb_clear_halt(d->udev,
-               usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
+                      usb_rcvbulkpipe(d->udev,
+                                      d->props.generic_bulk_ctrl_endpoint));
        usb_clear_halt(d->udev,
-               usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint));
+                      usb_rcvbulkpipe(d->udev,
+                                      d->props.adapter[0].fe[0].stream.endpoint));
 
        /* Drain USB pipes to avoid hang after reboot */
        for (n = 0;  n < 5;  n++) {
@@ -1105,8 +1311,9 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap)
        msleep(100);
 
        /* Attach frontend */
-       adap->fe_adap[0].fe = dvb_attach(lgs8gxx_attach, &d680_lgs8gl5_cfg, &d->i2c_adap);
-       if (adap->fe_adap[0].fe == NULL)
+       adap->fe_adap[0].fe = dvb_attach(lgs8gxx_attach,
+                                        &d680_lgs8gl5_cfg, &d->i2c_adap);
+       if (!adap->fe_adap[0].fe)
                return -EIO;
 
        return 0;
@@ -1136,12 +1343,14 @@ static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap)
 
        /* Unblock all USB pipes */
        usb_clear_halt(d->udev,
-               usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
+                      usb_sndbulkpipe(d->udev,
+                                      d->props.generic_bulk_ctrl_endpoint));
        usb_clear_halt(d->udev,
-               usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
+                      usb_rcvbulkpipe(d->udev,
+                                      d->props.generic_bulk_ctrl_endpoint));
        usb_clear_halt(d->udev,
-               usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint));
-
+                      usb_rcvbulkpipe(d->udev,
+                                      d->props.adapter[0].fe[0].stream.endpoint));
 
        /* Reset the tuner */
        if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 0) < 0) {
@@ -1156,9 +1365,10 @@ static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap)
        msleep(100);
 
        /* Attach frontend */
-       adap->fe_adap[0].fe = dvb_attach(atbm8830_attach, &mygica_d689_atbm8830_cfg,
-               &d->i2c_adap);
-       if (adap->fe_adap[0].fe == NULL)
+       adap->fe_adap[0].fe = dvb_attach(atbm8830_attach,
+                                        &mygica_d689_atbm8830_cfg,
+                                        &d->i2c_adap);
+       if (!adap->fe_adap[0].fe)
                return -EIO;
 
        return 0;
@@ -1181,11 +1391,14 @@ static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap)
 
        /* Unblock all USB pipes */
        usb_clear_halt(d->udev,
-               usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
+                      usb_sndbulkpipe(d->udev,
+                                      d->props.generic_bulk_ctrl_endpoint));
        usb_clear_halt(d->udev,
-               usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
+                      usb_rcvbulkpipe(d->udev,
+                                      d->props.generic_bulk_ctrl_endpoint));
        usb_clear_halt(d->udev,
-               usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint));
+                      usb_rcvbulkpipe(d->udev,
+                                      d->props.adapter[0].fe[0].stream.endpoint));
 
        /* attach frontend */
        si2168_config.i2c_adapter = &adapter;
@@ -1198,7 +1411,7 @@ static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap)
        info.platform_data = &si2168_config;
        request_module(info.type);
        client_demod = i2c_new_device(&d->i2c_adap, &info);
-       if (client_demod == NULL || client_demod->dev.driver == NULL)
+       if (!client_demod || !client_demod->dev.driver)
                return -ENODEV;
 
        if (!try_module_get(client_demod->dev.driver->owner)) {
@@ -1218,7 +1431,7 @@ static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap)
        info.platform_data = &si2157_config;
        request_module(info.type);
        client_tuner = i2c_new_device(adapter, &info);
-       if (client_tuner == NULL || client_tuner->dev.driver == NULL) {
+       if (!client_tuner || !client_tuner->dev.driver) {
                module_put(client_demod->dev.driver->owner);
                i2c_unregister_device(client_demod);
                return -ENODEV;
@@ -1309,6 +1522,104 @@ static int bluebird_patch_dvico_firmware_download(struct usb_device *udev,
        return -EINVAL;
 }
 
+int cxusb_medion_get(struct dvb_usb_device *dvbdev,
+                    enum cxusb_open_type open_type)
+{
+       struct cxusb_medion_dev *cxdev = dvbdev->priv;
+       int ret = 0;
+
+       mutex_lock(&cxdev->open_lock);
+
+       if (WARN_ON((cxdev->open_type == CXUSB_OPEN_INIT ||
+                    cxdev->open_type == CXUSB_OPEN_NONE) &&
+                   cxdev->open_ctr != 0)) {
+               ret = -EINVAL;
+               goto ret_unlock;
+       }
+
+       if (cxdev->open_type == CXUSB_OPEN_INIT) {
+               ret = -EAGAIN;
+               goto ret_unlock;
+       }
+
+       if (cxdev->open_ctr == 0) {
+               if (cxdev->open_type != open_type) {
+                       deb_info("will acquire and switch to %s\n",
+                                open_type == CXUSB_OPEN_ANALOG ?
+                                "analog" : "digital");
+
+                       if (open_type == CXUSB_OPEN_ANALOG) {
+                               ret = _cxusb_power_ctrl(dvbdev, 1);
+                               if (ret != 0)
+                                       dev_warn(&dvbdev->udev->dev,
+                                                "powerup for analog switch failed (%d)\n",
+                                                ret);
+
+                               ret = cxusb_medion_set_mode(dvbdev, false);
+                               if (ret != 0)
+                                       goto ret_unlock;
+
+                               ret = cxusb_medion_analog_init(dvbdev);
+                               if (ret != 0)
+                                       goto ret_unlock;
+                       } else { /* digital */
+                               ret = _cxusb_power_ctrl(dvbdev, 1);
+                               if (ret != 0)
+                                       dev_warn(&dvbdev->udev->dev,
+                                                "powerup for digital switch failed (%d)\n",
+                                                ret);
+
+                               ret = cxusb_medion_set_mode(dvbdev, true);
+                               if (ret != 0)
+                                       goto ret_unlock;
+                       }
+
+                       cxdev->open_type = open_type;
+               } else {
+                       deb_info("reacquired idle %s\n",
+                                open_type == CXUSB_OPEN_ANALOG ?
+                                "analog" : "digital");
+               }
+
+               cxdev->open_ctr = 1;
+       } else if (cxdev->open_type == open_type) {
+               cxdev->open_ctr++;
+               deb_info("acquired %s\n", open_type == CXUSB_OPEN_ANALOG ?
+                        "analog" : "digital");
+       } else {
+               ret = -EBUSY;
+       }
+
+ret_unlock:
+       mutex_unlock(&cxdev->open_lock);
+
+       return ret;
+}
+
+void cxusb_medion_put(struct dvb_usb_device *dvbdev)
+{
+       struct cxusb_medion_dev *cxdev = dvbdev->priv;
+
+       mutex_lock(&cxdev->open_lock);
+
+       if (cxdev->open_type == CXUSB_OPEN_INIT) {
+               WARN_ON(cxdev->open_ctr != 0);
+               cxdev->open_type = CXUSB_OPEN_NONE;
+               goto unlock;
+       }
+
+       if (!WARN_ON(cxdev->open_ctr < 1)) {
+               cxdev->open_ctr--;
+
+               deb_info("release %s\n",
+                        cxdev->open_type == CXUSB_OPEN_ANALOG ?
+                        "analog" : "digital");
+       }
+
+unlock:
+       mutex_unlock(&cxdev->open_lock);
+}
+
 /* DVB USB Driver stuff */
 static struct dvb_usb_device_properties cxusb_medion_properties;
 static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties;
@@ -1324,41 +1635,141 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties;
 static struct dvb_usb_device_properties cxusb_mygica_d689_properties;
 static struct dvb_usb_device_properties cxusb_mygica_t230_properties;
 
+static int cxusb_medion_priv_init(struct dvb_usb_device *dvbdev)
+{
+       struct cxusb_medion_dev *cxdev = dvbdev->priv;
+
+       cxdev->dvbdev = dvbdev;
+       cxdev->open_type = CXUSB_OPEN_INIT;
+       mutex_init(&cxdev->open_lock);
+
+       return 0;
+}
+
+static void cxusb_medion_priv_destroy(struct dvb_usb_device *dvbdev)
+{
+       struct cxusb_medion_dev *cxdev = dvbdev->priv;
+
+       mutex_destroy(&cxdev->open_lock);
+}
+
+static bool cxusb_medion_check_altsetting(struct usb_host_interface *as)
+{
+       unsigned int ctr;
+
+       for (ctr = 0; ctr < as->desc.bNumEndpoints; ctr++) {
+               if ((as->endpoint[ctr].desc.bEndpointAddress &
+                    USB_ENDPOINT_NUMBER_MASK) != 2)
+                       continue;
+
+               if (as->endpoint[ctr].desc.bEndpointAddress & USB_DIR_IN &&
+                   ((as->endpoint[ctr].desc.bmAttributes &
+                     USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC))
+                       return true;
+
+               break;
+       }
+
+       return false;
+}
+
+static bool cxusb_medion_check_intf(struct usb_interface *intf)
+{
+       unsigned int ctr;
+
+       if (intf->num_altsetting < 2) {
+               dev_err(intf->usb_dev, "no alternate interface");
+
+               return false;
+       }
+
+       for (ctr = 0; ctr < intf->num_altsetting; ctr++) {
+               if (intf->altsetting[ctr].desc.bAlternateSetting != 1)
+                       continue;
+
+               if (cxusb_medion_check_altsetting(&intf->altsetting[ctr]))
+                       return true;
+
+               break;
+       }
+
+       dev_err(intf->usb_dev, "no iso interface");
+
+       return false;
+}
+
 static int cxusb_probe(struct usb_interface *intf,
                       const struct usb_device_id *id)
 {
-       if (0 == dvb_usb_device_init(intf, &cxusb_medion_properties,
-                                    THIS_MODULE, NULL, adapter_nr) ||
-           0 == dvb_usb_device_init(intf, &cxusb_bluebird_lgh064f_properties,
-                                    THIS_MODULE, NULL, adapter_nr) ||
-           0 == dvb_usb_device_init(intf, &cxusb_bluebird_dee1601_properties,
-                                    THIS_MODULE, NULL, adapter_nr) ||
-           0 == dvb_usb_device_init(intf, &cxusb_bluebird_lgz201_properties,
-                                    THIS_MODULE, NULL, adapter_nr) ||
-           0 == dvb_usb_device_init(intf, &cxusb_bluebird_dtt7579_properties,
-                                    THIS_MODULE, NULL, adapter_nr) ||
-           0 == dvb_usb_device_init(intf, &cxusb_bluebird_dualdig4_properties,
-                                    THIS_MODULE, NULL, adapter_nr) ||
-           0 == dvb_usb_device_init(intf, &cxusb_bluebird_nano2_properties,
-                                    THIS_MODULE, NULL, adapter_nr) ||
-           0 == dvb_usb_device_init(intf,
-                               &cxusb_bluebird_nano2_needsfirmware_properties,
-                                    THIS_MODULE, NULL, adapter_nr) ||
-           0 == dvb_usb_device_init(intf, &cxusb_aver_a868r_properties,
-                                    THIS_MODULE, NULL, adapter_nr) ||
-           0 == dvb_usb_device_init(intf,
-                                    &cxusb_bluebird_dualdig4_rev2_properties,
-                                    THIS_MODULE, NULL, adapter_nr) ||
-           0 == dvb_usb_device_init(intf, &cxusb_d680_dmb_properties,
-                                    THIS_MODULE, NULL, adapter_nr) ||
-           0 == dvb_usb_device_init(intf, &cxusb_mygica_d689_properties,
-                                    THIS_MODULE, NULL, adapter_nr) ||
-           0 == dvb_usb_device_init(intf, &cxusb_mygica_t230_properties,
-                                    THIS_MODULE, NULL, adapter_nr) ||
-           0)
+       struct dvb_usb_device *dvbdev;
+       int ret;
+
+       /* Medion 95700 */
+       if (!dvb_usb_device_init(intf, &cxusb_medion_properties,
+                                THIS_MODULE, &dvbdev, adapter_nr)) {
+               if (!cxusb_medion_check_intf(intf)) {
+                       ret = -ENODEV;
+                       goto ret_uninit;
+               }
+
+               _cxusb_power_ctrl(dvbdev, 1);
+               ret = cxusb_medion_set_mode(dvbdev, false);
+               if (ret)
+                       goto ret_uninit;
+
+               ret = cxusb_medion_register_analog(dvbdev);
+
+               cxusb_medion_set_mode(dvbdev, true);
+               _cxusb_power_ctrl(dvbdev, 0);
+
+               if (ret != 0)
+                       goto ret_uninit;
+
+               /* release device from INIT mode to normal operation */
+               cxusb_medion_put(dvbdev);
+
+               return 0;
+       } else if (!dvb_usb_device_init(intf,
+                                       &cxusb_bluebird_lgh064f_properties,
+                                       THIS_MODULE, NULL, adapter_nr) ||
+                  !dvb_usb_device_init(intf,
+                                       &cxusb_bluebird_dee1601_properties,
+                                       THIS_MODULE, NULL, adapter_nr) ||
+                  !dvb_usb_device_init(intf,
+                                       &cxusb_bluebird_lgz201_properties,
+                                       THIS_MODULE, NULL, adapter_nr) ||
+                  !dvb_usb_device_init(intf,
+                                       &cxusb_bluebird_dtt7579_properties,
+                                       THIS_MODULE, NULL, adapter_nr) ||
+                  !dvb_usb_device_init(intf,
+                                       &cxusb_bluebird_dualdig4_properties,
+                                       THIS_MODULE, NULL, adapter_nr) ||
+                  !dvb_usb_device_init(intf,
+                                       &cxusb_bluebird_nano2_properties,
+                                       THIS_MODULE, NULL, adapter_nr) ||
+                  !dvb_usb_device_init(intf,
+                                       &cxusb_bluebird_nano2_needsfirmware_properties,
+                                       THIS_MODULE, NULL, adapter_nr) ||
+                  !dvb_usb_device_init(intf, &cxusb_aver_a868r_properties,
+                                       THIS_MODULE, NULL, adapter_nr) ||
+                  !dvb_usb_device_init(intf,
+                                       &cxusb_bluebird_dualdig4_rev2_properties,
+                                       THIS_MODULE, NULL, adapter_nr) ||
+                  !dvb_usb_device_init(intf, &cxusb_d680_dmb_properties,
+                                       THIS_MODULE, NULL, adapter_nr) ||
+                  !dvb_usb_device_init(intf, &cxusb_mygica_d689_properties,
+                                       THIS_MODULE, NULL, adapter_nr) ||
+                  !dvb_usb_device_init(intf, &cxusb_mygica_t230_properties,
+                                       THIS_MODULE, NULL, adapter_nr) ||
+                  0)
                return 0;
 
        return -EINVAL;
+
+ret_uninit:
+       dvb_usb_device_exit(intf);
+
+       return ret;
 }
 
 static void cxusb_disconnect(struct usb_interface *intf)
@@ -1367,6 +1778,9 @@ static void cxusb_disconnect(struct usb_interface *intf)
        struct cxusb_state *st = d->priv;
        struct i2c_client *client;
 
+       if (d->props.devices[0].warm_ids[0] == &cxusb_table[MEDION_MD95700])
+               cxusb_medion_unregister_analog(d);
+
        /* remove I2C client for tuner */
        client = st->i2c_client_tuner;
        if (client) {
@@ -1384,31 +1798,6 @@ static void cxusb_disconnect(struct usb_interface *intf)
        dvb_usb_device_exit(intf);
 }
 
-enum cxusb_table_index {
-       MEDION_MD95700,
-       DVICO_BLUEBIRD_LG064F_COLD,
-       DVICO_BLUEBIRD_LG064F_WARM,
-       DVICO_BLUEBIRD_DUAL_1_COLD,
-       DVICO_BLUEBIRD_DUAL_1_WARM,
-       DVICO_BLUEBIRD_LGZ201_COLD,
-       DVICO_BLUEBIRD_LGZ201_WARM,
-       DVICO_BLUEBIRD_TH7579_COLD,
-       DVICO_BLUEBIRD_TH7579_WARM,
-       DIGITALNOW_BLUEBIRD_DUAL_1_COLD,
-       DIGITALNOW_BLUEBIRD_DUAL_1_WARM,
-       DVICO_BLUEBIRD_DUAL_2_COLD,
-       DVICO_BLUEBIRD_DUAL_2_WARM,
-       DVICO_BLUEBIRD_DUAL_4,
-       DVICO_BLUEBIRD_DVB_T_NANO_2,
-       DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM,
-       AVERMEDIA_VOLAR_A868R,
-       DVICO_BLUEBIRD_DUAL_4_REV_2,
-       CONEXANT_D680_DMB,
-       MYGICA_D689,
-       MYGICA_T230,
-       NR__cxusb_table_index
-};
-
 static struct usb_device_id cxusb_table[NR__cxusb_table_index + 1] = {
        [MEDION_MD95700] = {
                USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700)
@@ -1438,10 +1827,12 @@ static struct usb_device_id cxusb_table[NR__cxusb_table_index + 1] = {
                USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_WARM)
        },
        [DIGITALNOW_BLUEBIRD_DUAL_1_COLD] = {
-               USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD)
+               USB_DEVICE(USB_VID_DVICO,
+                          USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD)
        },
        [DIGITALNOW_BLUEBIRD_DUAL_1_WARM] = {
-               USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM)
+               USB_DEVICE(USB_VID_DVICO,
+                          USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM)
        },
        [DVICO_BLUEBIRD_DUAL_2_COLD] = {
                USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD)
@@ -1456,7 +1847,8 @@ static struct usb_device_id cxusb_table[NR__cxusb_table_index + 1] = {
                USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2)
        },
        [DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM] = {
-               USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM)
+               USB_DEVICE(USB_VID_DVICO,
+                          USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM)
        },
        [AVERMEDIA_VOLAR_A868R] = {
                USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_A868R)
@@ -1475,14 +1867,16 @@ static struct usb_device_id cxusb_table[NR__cxusb_table_index + 1] = {
        },
        {}              /* Terminating entry */
 };
-MODULE_DEVICE_TABLE (usb, cxusb_table);
+MODULE_DEVICE_TABLE(usb, cxusb_table);
 
 static struct dvb_usb_device_properties cxusb_medion_properties = {
        .caps = DVB_USB_IS_AN_I2C_ADAPTER,
 
        .usb_ctrl = CYPRESS_FX2,
 
-       .size_of_priv     = sizeof(struct cxusb_state),
+       .size_of_priv     = sizeof(struct cxusb_medion_dev),
+       .priv_init        = cxusb_medion_priv_init,
+       .priv_destroy     = cxusb_medion_priv_destroy,
 
        .num_adapters = 1,
        .adapter = {
@@ -1503,7 +1897,7 @@ static struct dvb_usb_device_properties cxusb_medion_properties = {
                                        }
                                }
                        },
-               }},
+               } },
                },
        },
        .power_ctrl       = cxusb_power_ctrl,
@@ -1514,7 +1908,8 @@ static struct dvb_usb_device_properties cxusb_medion_properties = {
 
        .num_device_descs = 1,
        .devices = {
-               {   "Medion MD95700 (MDUSBTV-HYBRID)",
+               {
+                       "Medion MD95700 (MDUSBTV-HYBRID)",
                        { NULL },
                        { &cxusb_table[MEDION_MD95700], NULL },
                },
@@ -1527,8 +1922,10 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = {
        .usb_ctrl          = DEVICE_SPECIFIC,
        .firmware          = "dvb-usb-bluebird-01.fw",
        .download_firmware = bluebird_patch_dvico_firmware_download,
-       /* use usb alt setting 0 for EP4 transfer (dvb-t),
-          use usb alt setting 7 for EP2 transfer (atsc) */
+       /*
+        * use usb alt setting 0 for EP4 transfer (dvb-t),
+        * use usb alt setting 7 for EP2 transfer (atsc)
+        */
 
        .size_of_priv     = sizeof(struct cxusb_state),
 
@@ -1552,7 +1949,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = {
                                        }
                                }
                        },
-               }},
+               } },
                },
        },
 
@@ -1585,8 +1982,10 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = {
        .usb_ctrl          = DEVICE_SPECIFIC,
        .firmware          = "dvb-usb-bluebird-01.fw",
        .download_firmware = bluebird_patch_dvico_firmware_download,
-       /* use usb alt setting 0 for EP4 transfer (dvb-t),
-          use usb alt setting 7 for EP2 transfer (atsc) */
+       /*
+        * use usb alt setting 0 for EP4 transfer (dvb-t),
+        * use usb alt setting 7 for EP2 transfer (atsc)
+        */
 
        .size_of_priv     = sizeof(struct cxusb_state),
 
@@ -1609,7 +2008,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = {
                                        }
                                }
                        },
-               }},
+               } },
                },
        },
 
@@ -1634,7 +2033,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = {
                        { &cxusb_table[DVICO_BLUEBIRD_DUAL_1_WARM], NULL },
                },
                {   "DigitalNow DVB-T Dual USB",
-                       { &cxusb_table[DIGITALNOW_BLUEBIRD_DUAL_1_COLD],  NULL },
+                       { &cxusb_table[DIGITALNOW_BLUEBIRD_DUAL_1_COLD], NULL },
                        { &cxusb_table[DIGITALNOW_BLUEBIRD_DUAL_1_WARM], NULL },
                },
                {   "DViCO FusionHDTV DVB-T Dual Digital 2",
@@ -1650,8 +2049,10 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = {
        .usb_ctrl          = DEVICE_SPECIFIC,
        .firmware          = "dvb-usb-bluebird-01.fw",
        .download_firmware = bluebird_patch_dvico_firmware_download,
-       /* use usb alt setting 0 for EP4 transfer (dvb-t),
-          use usb alt setting 7 for EP2 transfer (atsc) */
+       /*
+        * use usb alt setting 0 for EP4 transfer (dvb-t),
+        * use usb alt setting 7 for EP2 transfer (atsc)
+        */
 
        .size_of_priv     = sizeof(struct cxusb_state),
 
@@ -1675,7 +2076,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = {
                                        }
                                }
                        },
-               }},
+               } },
                },
        },
        .power_ctrl       = cxusb_bluebird_power_ctrl,
@@ -1706,8 +2107,11 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = {
        .usb_ctrl          = DEVICE_SPECIFIC,
        .firmware          = "dvb-usb-bluebird-01.fw",
        .download_firmware = bluebird_patch_dvico_firmware_download,
-       /* use usb alt setting 0 for EP4 transfer (dvb-t),
-          use usb alt setting 7 for EP2 transfer (atsc) */
+
+       /*
+        * use usb alt setting 0 for EP4 transfer (dvb-t),
+        * use usb alt setting 7 for EP2 transfer (atsc)
+        */
 
        .size_of_priv     = sizeof(struct cxusb_state),
 
@@ -1731,7 +2135,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = {
                                        }
                                }
                        },
-               }},
+               } },
                },
        },
        .power_ctrl       = cxusb_bluebird_power_ctrl,
@@ -1783,7 +2187,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties = {
                                        }
                                }
                        },
-               }},
+               } },
                },
        },
 
@@ -1837,7 +2241,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = {
                                        }
                                }
                        },
-               }},
+               } },
                },
        },
 
@@ -1864,7 +2268,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = {
        }
 };
 
-static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties = {
+static struct dvb_usb_device_properties
+cxusb_bluebird_nano2_needsfirmware_properties = {
        .caps = DVB_USB_IS_AN_I2C_ADAPTER,
 
        .usb_ctrl          = DEVICE_SPECIFIC,
@@ -1893,7 +2298,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope
                                        }
                                }
                        },
-               }},
+               } },
                },
        },
 
@@ -1912,10 +2317,11 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope
        },
 
        .num_device_descs = 1,
-       .devices = {
-               {   "DViCO FusionHDTV DVB-T NANO2 w/o firmware",
+       .devices = { {
+                       "DViCO FusionHDTV DVB-T NANO2 w/o firmware",
                        { &cxusb_table[DVICO_BLUEBIRD_DVB_T_NANO_2], NULL },
-                       { &cxusb_table[DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM], NULL },
+                       { &cxusb_table[DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM],
+                         NULL },
                },
        }
 };
@@ -1946,7 +2352,7 @@ static struct dvb_usb_device_properties cxusb_aver_a868r_properties = {
                                        }
                                }
                        },
-               }},
+               } },
                },
        },
        .power_ctrl       = cxusb_aver_power_ctrl,
@@ -1992,7 +2398,7 @@ struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties = {
                                        }
                                }
                        },
-               }},
+               } },
                },
        },
 
@@ -2046,7 +2452,7 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = {
                                        }
                                }
                        },
-               }},
+               } },
                },
        },
 
@@ -2101,7 +2507,7 @@ static struct dvb_usb_device_properties cxusb_mygica_d689_properties = {
                                        }
                                }
                        },
-               }},
+               } },
                },
        },
 
@@ -2195,6 +2601,6 @@ module_usb_driver(cxusb_driver);
 MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
 MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
 MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
+MODULE_AUTHOR("Maciej S. Szmigiero <mail@maciej.szmigiero.name>");
 MODULE_DESCRIPTION("Driver for Conexant USB2.0 hybrid reference design");
-MODULE_VERSION("1.0-alpha");
 MODULE_LICENSE("GPL");