[PATCH 3/9] anysee: convert tda18212 tuner to I2C client

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Used tda18212 tuner is implemented as I2C driver. Implement I2C
client to anysee and use it for tda18212.

Signed-off-by: Antti Palosaari <crope@xxxxxx>
---
 drivers/media/usb/dvb-usb-v2/anysee.c | 183 +++++++++++++++++++++++++++-------
 drivers/media/usb/dvb-usb-v2/anysee.h |   3 +
 2 files changed, 150 insertions(+), 36 deletions(-)

diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c
index e4a2382..c5ea0ee 100644
--- a/drivers/media/usb/dvb-usb-v2/anysee.c
+++ b/drivers/media/usb/dvb-usb-v2/anysee.c
@@ -332,7 +332,6 @@ static struct tda10023_config anysee_tda10023_tda18212_config = {
 };
 
 static struct tda18212_config anysee_tda18212_config = {
-	.i2c_address = (0xc0 >> 1),
 	.if_dvbt_6 = 4150,
 	.if_dvbt_7 = 4150,
 	.if_dvbt_8 = 4150,
@@ -340,7 +339,6 @@ static struct tda18212_config anysee_tda18212_config = {
 };
 
 static struct tda18212_config anysee_tda18212_config2 = {
-	.i2c_address = 0x60 /* (0xc0 >> 1) */,
 	.if_dvbt_6 = 3550,
 	.if_dvbt_7 = 3700,
 	.if_dvbt_8 = 4150,
@@ -632,6 +630,90 @@ error:
 	return ret;
 }
 
+static int anysee_load_subdev(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_unload_subdev(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);
+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);
@@ -640,12 +722,12 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *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,
@@ -723,9 +805,11 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
 		/* 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;
 
@@ -939,46 +1023,63 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
 		 * 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_load_subdev(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_load_subdev(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 */
@@ -997,13 +1098,18 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
 		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_load_subdev(d, "tda18212", 0x60, &tda18212_config);
+		if (ret)
+			goto err;
 
-		break;
+		return 0;
+	}
 	default:
 		fe = NULL;
 	}
@@ -1012,7 +1118,7 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
 		ret = 0;
 	else
 		ret = -ENODEV;
-
+err:
 	return ret;
 }
 
@@ -1270,6 +1376,11 @@ static int anysee_init(struct dvb_usb_device *d)
 
 static void anysee_exit(struct dvb_usb_device *d)
 {
+	struct anysee_state *state = d_to_priv(d);
+
+	if (state->i2c_client[0])
+		anysee_unload_subdev(d);
+
 	return anysee_ci_release(d);
 }
 
diff --git a/drivers/media/usb/dvb-usb-v2/anysee.h b/drivers/media/usb/dvb-usb-v2/anysee.h
index 8f426d9..3ca2bca 100644
--- a/drivers/media/usb/dvb-usb-v2/anysee.h
+++ b/drivers/media/usb/dvb-usb-v2/anysee.h
@@ -55,8 +55,11 @@ struct anysee_state {
 	u8 buf[64];
 	u8 seq;
 	u8 hw; /* PCB ID */
+	#define ANYSEE_I2C_CLIENT_MAX 1
+	struct i2c_client *i2c_client[ANYSEE_I2C_CLIENT_MAX];
 	u8 fe_id:1; /* frondend ID */
 	u8 has_ci:1;
+	u8 has_tda18212:1;
 	u8 ci_attached:1;
 	struct dvb_ca_en50221 ci;
 	unsigned long ci_cam_ready; /* jiffies */
-- 
http://palosaari.fi/

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux