};
static struct tda18212_config anysee_tda18212_config = {
- .i2c_address = (0xc0 >> 1),
.if_dvbt_6 = 4150,
.if_dvbt_7 = 4150,
.if_dvbt_8 = 4150,
};
static struct tda18212_config anysee_tda18212_config2 = {
- .i2c_address = 0x60 /* (0xc0 >> 1) */,
.if_dvbt_6 = 3550,
.if_dvbt_7 = 3700,
.if_dvbt_8 = 4150,
return ret;
}
+static int anysee_add_i2c_dev(struct dvb_usb_device *d, char *type, u8 addr,
+ void *platform_data)
+{
+ int ret, num;
+ struct anysee_state *state = d_to_priv(d);
+ struct i2c_client *client;
+ struct i2c_adapter *adapter = &d->i2c_adap;
+ struct i2c_board_info board_info = {
+ .addr = addr,
+ .platform_data = platform_data,
+ };
+
+ strlcpy(board_info.type, type, I2C_NAME_SIZE);
+
+ /* find first free client */
+ for (num = 0; num < ANYSEE_I2C_CLIENT_MAX; num++) {
+ if (state->i2c_client[num] == NULL)
+ break;
+ }
+
+ dev_dbg(&d->udev->dev, "%s: num=%d\n", __func__, num);
+
+ if (num == ANYSEE_I2C_CLIENT_MAX) {
+ dev_err(&d->udev->dev, "%s: I2C client out of index\n",
+ KBUILD_MODNAME);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ request_module(board_info.type);
+
+ /* register I2C device */
+ client = i2c_new_device(adapter, &board_info);
+ if (client == NULL || client->dev.driver == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /* increase I2C driver usage count */
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ state->i2c_client[num] = client;
+ return 0;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static void anysee_del_i2c_dev(struct dvb_usb_device *d)
+{
+ int num;
+ struct anysee_state *state = d_to_priv(d);
+ struct i2c_client *client;
+
+ /* find last used client */
+ num = ANYSEE_I2C_CLIENT_MAX;
+ while (num--) {
+ if (state->i2c_client[num] != NULL)
+ break;
+ }
+
+ dev_dbg(&d->udev->dev, "%s: num=%d\n", __func__, num);
+
+ if (num == -1) {
+ dev_err(&d->udev->dev, "%s: I2C client out of index\n",
+ KBUILD_MODNAME);
+ goto err;
+ }
+
+ client = state->i2c_client[num];
+
+ /* decrease I2C driver usage count */
+ module_put(client->dev.driver->owner);
+
+ /* unregister I2C device */
+ i2c_unregister_device(client);
+
+ state->i2c_client[num] = NULL;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed\n", __func__);
+}
+
static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
{
struct anysee_state *state = adap_to_priv(adap);
u8 tmp;
struct i2c_msg msg[2] = {
{
- .addr = anysee_tda18212_config.i2c_address,
+ .addr = 0x60,
.flags = 0,
.len = 1,
.buf = "\x00",
}, {
- .addr = anysee_tda18212_config.i2c_address,
+ .addr = 0x60,
.flags = I2C_M_RD,
.len = 1,
.buf = &tmp,
/* probe TDA18212 */
tmp = 0;
ret = i2c_transfer(&d->i2c_adap, msg, 2);
- if (ret == 2 && tmp == 0xc7)
+ if (ret == 2 && tmp == 0xc7) {
dev_dbg(&d->udev->dev, "%s: TDA18212 found\n",
__func__);
+ state->has_tda18212 = true;
+ }
else
tmp = 0;
* fails attach old simple PLL. */
/* attach tuner */
- fe = dvb_attach(tda18212_attach, adap->fe[0], &d->i2c_adap,
- &anysee_tda18212_config);
+ if (state->has_tda18212) {
+ struct tda18212_config tda18212_config =
+ anysee_tda18212_config;
- if (fe && adap->fe[1]) {
- /* attach tuner for 2nd FE */
- fe = dvb_attach(tda18212_attach, adap->fe[1],
- &d->i2c_adap, &anysee_tda18212_config);
- break;
- } else if (fe) {
- break;
- }
-
- /* attach tuner */
- fe = dvb_attach(dvb_pll_attach, adap->fe[0], (0xc0 >> 1),
- &d->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A);
+ tda18212_config.fe = adap->fe[0];
+ ret = anysee_add_i2c_dev(d, "tda18212", 0x60,
+ &tda18212_config);
+ if (ret)
+ goto err;
+
+ /* copy tuner ops for 2nd FE as tuner is shared */
+ if (adap->fe[1]) {
+ adap->fe[1]->tuner_priv =
+ adap->fe[0]->tuner_priv;
+ memcpy(&adap->fe[1]->ops.tuner_ops,
+ &adap->fe[0]->ops.tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+ }
- if (fe && adap->fe[1]) {
- /* attach tuner for 2nd FE */
- fe = dvb_attach(dvb_pll_attach, adap->fe[1],
+ return 0;
+ } else {
+ /* attach tuner */
+ fe = dvb_attach(dvb_pll_attach, adap->fe[0],
(0xc0 >> 1), &d->i2c_adap,
DVB_PLL_SAMSUNG_DTOS403IH102A);
+
+ if (fe && adap->fe[1]) {
+ /* attach tuner for 2nd FE */
+ fe = dvb_attach(dvb_pll_attach, adap->fe[1],
+ (0xc0 >> 1), &d->i2c_adap,
+ DVB_PLL_SAMSUNG_DTOS403IH102A);
+ }
}
break;
case ANYSEE_HW_508TC: /* 18 */
case ANYSEE_HW_508PTC: /* 21 */
+ {
/* E7 TC */
/* E7 PTC */
+ struct tda18212_config tda18212_config = anysee_tda18212_config;
- /* attach tuner */
- fe = dvb_attach(tda18212_attach, adap->fe[0], &d->i2c_adap,
- &anysee_tda18212_config);
-
- if (fe) {
- /* attach tuner for 2nd FE */
- fe = dvb_attach(tda18212_attach, adap->fe[1],
- &d->i2c_adap, &anysee_tda18212_config);
+ tda18212_config.fe = adap->fe[0];
+ ret = anysee_add_i2c_dev(d, "tda18212", 0x60, &tda18212_config);
+ if (ret)
+ goto err;
+
+ /* copy tuner ops for 2nd FE as tuner is shared */
+ if (adap->fe[1]) {
+ adap->fe[1]->tuner_priv = adap->fe[0]->tuner_priv;
+ memcpy(&adap->fe[1]->ops.tuner_ops,
+ &adap->fe[0]->ops.tuner_ops,
+ sizeof(struct dvb_tuner_ops));
}
- break;
+ return 0;
+ }
case ANYSEE_HW_508S2: /* 19 */
case ANYSEE_HW_508PS2: /* 22 */
/* E7 S2 */
break;
case ANYSEE_HW_508T2C: /* 20 */
+ {
/* E7 T2C */
+ struct tda18212_config tda18212_config =
+ anysee_tda18212_config2;
- /* attach tuner */
- fe = dvb_attach(tda18212_attach, adap->fe[0], &d->i2c_adap,
- &anysee_tda18212_config2);
+ tda18212_config.fe = adap->fe[0];
+ ret = anysee_add_i2c_dev(d, "tda18212", 0x60, &tda18212_config);
+ if (ret)
+ goto err;
- break;
+ return 0;
+ }
default:
fe = NULL;
}
ret = 0;
else
ret = -ENODEV;
-
+err:
return ret;
}
static void anysee_exit(struct dvb_usb_device *d)
{
+ struct anysee_state *state = d_to_priv(d);
+
+ if (state->i2c_client[0])
+ anysee_del_i2c_dev(d);
+
return anysee_ci_release(d);
}