diff options
Diffstat (limited to 'drivers/media/usb/dvb-usb/cxusb.c')
| -rw-r--r-- | drivers/media/usb/dvb-usb/cxusb.c | 1043 |
1 files changed, 620 insertions, 423 deletions
diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index a51a45c60233..d0501c1e81d6 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* DVB USB compliant linux driver for Conexant USB reference design. * * The Conexant reference design I saw on their website was only for analogue @@ -11,22 +12,21 @@ * 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) * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation, version 2. - * - * see Documentation/media/dvb-drivers/dvb-usb.rst for more information + * see Documentation/driver-api/media/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" @@ -35,7 +35,7 @@ #include "mt352.h" #include "mt352_priv.h" #include "zl10353.h" -#include "tuner-xc2028.h" +#include "xc2028.h" #include "tuner-simple.h" #include "mxl5005s.h" #include "max2165.h" @@ -47,17 +47,41 @@ #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) +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, + NR__cxusb_table_index +}; + +static const struct usb_device_id cxusb_table[]; -static int cxusb_ctrl_msg(struct dvb_usb_device *d, - u8 cmd, const u8 *wbuf, int wlen, u8 *rbuf, int rlen) +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; @@ -89,21 +113,22 @@ 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; o[1] = onoff; - cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1); - if (i != 0x01) - deb_info("gpio_write failed.\n"); + if (!cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1) && i != 0x01) + dev_info(&d->udev->dev, "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; @@ -113,7 +138,7 @@ static int cxusb_bluebird_gpio_rw(struct dvb_usb_device *d, u8 changemask, rc = cxusb_ctrl_msg(d, CMD_BLUEBIRD_GPIO_RW, o, 2, &gpio_state, 1); if (rc < 0 || (gpio_state & changemask) != (newval & changemask)) - deb_info("bluebird_gpio_write failed.\n"); + dev_info(&d->udev->dev, "bluebird_gpio_write failed.\n"); return rc < 0 ? rc : gpio_state; } @@ -131,7 +156,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; @@ -141,12 +166,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; - } + + dev_info(&d->udev->dev, "gpio_write failed.\n"); + return -EIO; } /* I2C */ @@ -161,7 +186,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: @@ -187,13 +211,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]; @@ -210,19 +234,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"); + dev_info(&d->udev->dev, "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 { @@ -240,10 +264,10 @@ 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"); + dev_info(&d->udev->dev, "i2c write may have failed\n"); } } @@ -259,37 +283,75 @@ unlock: 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 = { +static const struct i2c_algorithm cxusb_i2c_algo = { .master_xfer = cxusb_i2c_xfer, .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; + + dev_info(&d->udev->dev, "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) { + dev_info(&d->udev->dev, "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[] = { @@ -307,7 +369,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) @@ -320,6 +382,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 @@ -341,6 +404,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; @@ -353,11 +417,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; } @@ -372,26 +451,6 @@ static int cxusb_aver_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) return 0; } -static int cxusb_read_status(struct dvb_frontend *fe, - 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; - int ret; - - ret = state->fe_read_status(fe, status); - - /* it need resync slave fifo when signal change from unlock to lock.*/ - if ((*status & FE_HAS_LOCK) && (!state->last_lock)) { - mutex_lock(&state->stream_mutex); - cxusb_streaming_ctrl(adap, 1); - mutex_unlock(&state->stream_mutex); - } - - state->last_lock = (*status & FE_HAS_LOCK) ? 1 : 0; - return ret; -} - static void cxusb_d680_dmb_drain_message(struct dvb_usb_device *d) { int ep = d->props.generic_bulk_ctrl_endpoint; @@ -406,8 +465,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; @@ -429,8 +488,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; @@ -438,17 +497,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; } } @@ -457,7 +517,8 @@ static int cxusb_rc_query(struct dvb_usb_device *d) { u8 ircode[4]; - cxusb_ctrl_msg(d, CMD_GET_IR_CODE, NULL, 0, ircode, 4); + if (cxusb_ctrl_msg(d, CMD_GET_IR_CODE, NULL, 0, ircode, 4) < 0) + return 0; if (ircode[2] || ircode[3]) rc_keydown(d->rc_dev, RC_PROTO_NEC, @@ -468,8 +529,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; @@ -493,13 +558,13 @@ static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d) return 0; } -static int cxusb_dee1601_demod_init(struct dvb_frontend* fe) +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)); @@ -514,13 +579,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)); @@ -630,9 +696,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; } @@ -645,7 +723,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; } @@ -671,16 +750,16 @@ static int dvico_bluebird_xc2028_callback(void *ptr, int component, switch (command) { case XC2028_TUNER_RESET: - deb_info("%s: XC2028_TUNER_RESET %d\n", __func__, arg); + dev_info(&d->udev->dev, "XC2028_TUNER_RESET %d\n", arg); cxusb_bluebird_gpio_pulse(d, 0x01, 1); break; case XC2028_RESET_CLK: - deb_info("%s: XC2028_RESET_CLK %d\n", __func__, arg); + dev_info(&d->udev->dev, "XC2028_RESET_CLK %d\n", arg); break; case XC2028_I2C_FLUSH: break; default: - deb_info("%s: unknown command %d, arg %d\n", __func__, + dev_info(&d->udev->dev, "unknown command %d, arg %d\n", command, arg); return -EINVAL; } @@ -705,7 +784,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); @@ -723,33 +802,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; + + /* + * 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; - cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, &b, 1); + 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) @@ -763,7 +929,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; @@ -775,7 +941,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; @@ -791,7 +957,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; @@ -806,13 +972,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; @@ -822,8 +988,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"); @@ -839,7 +1009,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 */ @@ -937,7 +1107,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; }; @@ -956,14 +1126,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; @@ -996,11 +1167,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)); @@ -1016,13 +1192,13 @@ static int cxusb_dualdig4_rev2_tuner_attach(struct dvb_usb_adapter *adap) /* * No need to call dvb7000p_attach here, as it was called * already, as frontend_attach method is called first, and - * tuner_attach is only called on sucess. + * tuner_attach is only called on success. */ tun_i2c = st->dib7000p_ops.get_i2c_master(adap->fe_adap[0].fe, 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; @@ -1045,13 +1221,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; @@ -1082,11 +1258,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++) { @@ -1108,8 +1287,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; @@ -1139,12 +1319,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) { @@ -1159,91 +1341,15 @@ 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; } -static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap) -{ - struct dvb_usb_device *d = adap->dev; - struct cxusb_state *st = d->priv; - struct i2c_adapter *adapter; - struct i2c_client *client_demod; - struct i2c_client *client_tuner; - struct i2c_board_info info; - struct si2168_config si2168_config; - struct si2157_config si2157_config; - - /* Select required USB configuration */ - if (usb_set_interface(d->udev, 0, 0) < 0) - err("set interface failed"); - - /* Unblock all USB pipes */ - usb_clear_halt(d->udev, - 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_clear_halt(d->udev, - usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint)); - - /* attach frontend */ - si2168_config.i2c_adapter = &adapter; - si2168_config.fe = &adap->fe_adap[0].fe; - si2168_config.ts_mode = SI2168_TS_PARALLEL; - si2168_config.ts_clock_inv = 1; - memset(&info, 0, sizeof(struct i2c_board_info)); - strscpy(info.type, "si2168", I2C_NAME_SIZE); - info.addr = 0x64; - 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) - return -ENODEV; - - if (!try_module_get(client_demod->dev.driver->owner)) { - i2c_unregister_device(client_demod); - return -ENODEV; - } - - st->i2c_client_demod = client_demod; - - /* attach tuner */ - memset(&si2157_config, 0, sizeof(si2157_config)); - si2157_config.fe = adap->fe_adap[0].fe; - si2157_config.if_port = 1; - memset(&info, 0, sizeof(struct i2c_board_info)); - strscpy(info.type, "si2157", I2C_NAME_SIZE); - info.addr = 0x60; - 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) { - module_put(client_demod->dev.driver->owner); - i2c_unregister_device(client_demod); - return -ENODEV; - } - if (!try_module_get(client_tuner->dev.driver->owner)) { - i2c_unregister_device(client_tuner); - module_put(client_demod->dev.driver->owner); - i2c_unregister_device(client_demod); - return -ENODEV; - } - - st->i2c_client_tuner = client_tuner; - - /* hook fe: need to resync the slave fifo when signal locks. */ - mutex_init(&st->stream_mutex); - st->last_lock = 0; - st->fe_read_status = adap->fe_adap[0].fe->ops.read_status; - adap->fe_adap[0].fe->ops.read_status = cxusb_read_status; - - return 0; -} - /* * DViCO has shipped two devices with the same USB ID, but only one of them * needs a firmware download. Check the device class details to see if they @@ -1251,8 +1357,8 @@ static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap) * not, and forget a match if it turns out we selected the wrong device. */ static int bluebird_fx2_identify_state(struct usb_device *udev, - struct dvb_usb_device_properties *props, - struct dvb_usb_device_description **desc, + const struct dvb_usb_device_properties *props, + const struct dvb_usb_device_description **desc, int *cold) { int wascold = *cold; @@ -1312,6 +1418,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) { + dev_info(&dvbdev->udev->dev, "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 { + dev_info(&dvbdev->udev->dev, "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++; + dev_info(&dvbdev->udev->dev, "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--; + + dev_info(&dvbdev->udev->dev, "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; @@ -1325,43 +1529,140 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope static struct dvb_usb_device_properties cxusb_aver_a868r_properties; 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) || + 0) return 0; return -EINVAL; + +ret_uninit: + dvb_usb_device_exit(intf); + + return ret; } static void cxusb_disconnect(struct usb_interface *intf) @@ -1370,6 +1671,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) { @@ -1387,105 +1691,40 @@ 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 const struct usb_device_id cxusb_table[] = { + DVB_USB_DEV(MEDION, MEDION_MD95700), + DVB_USB_DEV(DVICO, DVICO_BLUEBIRD_LG064F_COLD), + DVB_USB_DEV(DVICO, DVICO_BLUEBIRD_LG064F_WARM), + DVB_USB_DEV(DVICO, DVICO_BLUEBIRD_DUAL_1_COLD), + DVB_USB_DEV(DVICO, DVICO_BLUEBIRD_DUAL_1_WARM), + DVB_USB_DEV(DVICO, DVICO_BLUEBIRD_LGZ201_COLD), + DVB_USB_DEV(DVICO, DVICO_BLUEBIRD_LGZ201_WARM), + DVB_USB_DEV(DVICO, DVICO_BLUEBIRD_TH7579_COLD), + DVB_USB_DEV(DVICO, DVICO_BLUEBIRD_TH7579_WARM), + DVB_USB_DEV(DVICO, DIGITALNOW_BLUEBIRD_DUAL_1_COLD), + DVB_USB_DEV(DVICO, DIGITALNOW_BLUEBIRD_DUAL_1_WARM), + DVB_USB_DEV(DVICO, DVICO_BLUEBIRD_DUAL_2_COLD), + DVB_USB_DEV(DVICO, DVICO_BLUEBIRD_DUAL_2_WARM), + DVB_USB_DEV(DVICO, DVICO_BLUEBIRD_DUAL_4), + DVB_USB_DEV(DVICO, DVICO_BLUEBIRD_DVB_T_NANO_2), + DVB_USB_DEV(DVICO, DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM), + DVB_USB_DEV(AVERMEDIA, AVERMEDIA_VOLAR_A868R), + DVB_USB_DEV(DVICO, DVICO_BLUEBIRD_DUAL_4_REV_2), + DVB_USB_DEV(CONEXANT, CONEXANT_D680_DMB), + DVB_USB_DEV(CONEXANT, MYGICA_D689), + { } }; -static struct usb_device_id cxusb_table[NR__cxusb_table_index + 1] = { - [MEDION_MD95700] = { - USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700) - }, - [DVICO_BLUEBIRD_LG064F_COLD] = { - USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_COLD) - }, - [DVICO_BLUEBIRD_LG064F_WARM] = { - USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_WARM) - }, - [DVICO_BLUEBIRD_DUAL_1_COLD] = { - USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD) - }, - [DVICO_BLUEBIRD_DUAL_1_WARM] = { - USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM) - }, - [DVICO_BLUEBIRD_LGZ201_COLD] = { - USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_COLD) - }, - [DVICO_BLUEBIRD_LGZ201_WARM] = { - USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_WARM) - }, - [DVICO_BLUEBIRD_TH7579_COLD] = { - USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_COLD) - }, - [DVICO_BLUEBIRD_TH7579_WARM] = { - 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) - }, - [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) - }, - [DVICO_BLUEBIRD_DUAL_2_WARM] = { - USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM) - }, - [DVICO_BLUEBIRD_DUAL_4] = { - USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4) - }, - [DVICO_BLUEBIRD_DVB_T_NANO_2] = { - 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) - }, - [AVERMEDIA_VOLAR_A868R] = { - USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_A868R) - }, - [DVICO_BLUEBIRD_DUAL_4_REV_2] = { - USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2) - }, - [CONEXANT_D680_DMB] = { - USB_DEVICE(USB_VID_CONEXANT, USB_PID_CONEXANT_D680_DMB) - }, - [MYGICA_D689] = { - USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_D689) - }, - [MYGICA_T230] = { - USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230) - }, - {} /* 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 = { @@ -1506,7 +1745,7 @@ static struct dvb_usb_device_properties cxusb_medion_properties = { } } }, - }}, + } }, }, }, .power_ctrl = cxusb_power_ctrl, @@ -1517,7 +1756,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 }, }, @@ -1530,8 +1770,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), @@ -1555,7 +1797,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = { } } }, - }}, + } }, }, }, @@ -1588,8 +1830,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), @@ -1612,7 +1856,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = { } } }, - }}, + } }, }, }, @@ -1637,7 +1881,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", @@ -1653,12 +1897,14 @@ 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), - .num_adapters = 2, + .num_adapters = 1, .adapter = { { .num_frontends = 1, @@ -1678,7 +1924,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = { } } }, - }}, + } }, }, }, .power_ctrl = cxusb_bluebird_power_ctrl, @@ -1709,8 +1955,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), @@ -1734,7 +1983,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = { } } }, - }}, + } }, }, }, .power_ctrl = cxusb_bluebird_power_ctrl, @@ -1786,7 +2035,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties = { } } }, - }}, + } }, }, }, @@ -1840,7 +2089,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = { } } }, - }}, + } }, }, }, @@ -1867,7 +2116,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, @@ -1896,7 +2146,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope } } }, - }}, + } }, }, }, @@ -1915,10 +2165,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 }, }, } }; @@ -1949,7 +2200,7 @@ static struct dvb_usb_device_properties cxusb_aver_a868r_properties = { } } }, - }}, + } }, }, }, .power_ctrl = cxusb_aver_power_ctrl, @@ -1995,7 +2246,7 @@ struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties = { } } }, - }}, + } }, }, }, @@ -2049,7 +2300,7 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = { } } }, - }}, + } }, }, }, @@ -2104,7 +2355,7 @@ static struct dvb_usb_device_properties cxusb_mygica_d689_properties = { } } }, - }}, + } }, }, }, @@ -2132,60 +2383,6 @@ static struct dvb_usb_device_properties cxusb_mygica_d689_properties = { } }; -static struct dvb_usb_device_properties cxusb_mygica_t230_properties = { - .caps = DVB_USB_IS_AN_I2C_ADAPTER, - - .usb_ctrl = CYPRESS_FX2, - - .size_of_priv = sizeof(struct cxusb_state), - - .num_adapters = 1, - .adapter = { - { - .num_frontends = 1, - .fe = {{ - .streaming_ctrl = cxusb_streaming_ctrl, - .frontend_attach = cxusb_mygica_t230_frontend_attach, - - /* parameter for the MPEG2-data transfer */ - .stream = { - .type = USB_BULK, - .count = 5, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 8192, - } - } - }, - } }, - }, - }, - - .power_ctrl = cxusb_d680_dmb_power_ctrl, - - .i2c_algo = &cxusb_i2c_algo, - - .generic_bulk_ctrl_endpoint = 0x01, - - .rc.core = { - .rc_interval = 100, - .rc_codes = RC_MAP_D680_DMB, - .module_name = KBUILD_MODNAME, - .rc_query = cxusb_d680_dmb_rc_query, - .allowed_protos = RC_PROTO_BIT_UNKNOWN, - }, - - .num_device_descs = 1, - .devices = { - { - "Mygica T230 DVB-T/T2/C", - { NULL }, - { &cxusb_table[MYGICA_T230], NULL }, - }, - } -}; - static struct usb_driver cxusb_driver = { .name = "dvb_usb_cxusb", .probe = cxusb_probe, @@ -2198,6 +2395,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"); |
