All of lore.kernel.org
 help / color / mirror / Atom feed
* [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver
@ 2014-02-27  0:30 Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 01/16] e4000: convert DVB tuner to I2C driver model Antti Palosaari
                   ` (15 more replies)
  0 siblings, 16 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:30 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Antti Palosaari

That set adds support for Realtek RTL2832 SDR. There is many new
interesting things, like DVB tuner which is converted to Regmap API.

Antti

Antti Palosaari (16):
  e4000: convert DVB tuner to I2C driver model
  e4000: implement controls via v4l2 control framework
  e4000: fix PLL calc to allow higher frequencies
  e4000: implement PLL lock v4l control
  e4000: get rid of DVB i2c_gate_ctrl()
  e4000: convert to Regmap API
  e4000: rename some variables
  rtl2832_sdr: Realtek RTL2832 SDR driver module
  rtl28xxu: constify demod config structs
  rtl28xxu: attach SDR extension module
  rtl28xxu: fix switch-case style issue
  rtl28xxu: use muxed RTL2832 I2C adapters for E4000 and RTL2832_SDR
  rtl2832_sdr: expose e4000 controls to user
  r820t: add manual gain controls
  rtl2832_sdr: expose R820T controls to user
  MAINTAINERS: add rtl2832_sdr driver

 MAINTAINERS                                      |   10 +
 drivers/media/tuners/Kconfig                     |    1 +
 drivers/media/tuners/e4000.c                     |  598 +++++----
 drivers/media/tuners/e4000.h                     |   21 +-
 drivers/media/tuners/e4000_priv.h                |   86 +-
 drivers/media/tuners/r820t.c                     |  137 +-
 drivers/media/tuners/r820t.h                     |   10 +
 drivers/media/usb/dvb-usb-v2/Makefile            |    1 +
 drivers/media/usb/dvb-usb-v2/rtl28xxu.c          |   90 +-
 drivers/media/usb/dvb-usb-v2/rtl28xxu.h          |    2 +
 drivers/staging/media/Kconfig                    |    2 +
 drivers/staging/media/Makefile                   |    2 +
 drivers/staging/media/rtl2832u_sdr/Kconfig       |    7 +
 drivers/staging/media/rtl2832u_sdr/Makefile      |    6 +
 drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c | 1476 ++++++++++++++++++++++
 drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h |   51 +
 16 files changed, 2234 insertions(+), 266 deletions(-)
 create mode 100644 drivers/staging/media/rtl2832u_sdr/Kconfig
 create mode 100644 drivers/staging/media/rtl2832u_sdr/Makefile
 create mode 100644 drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
 create mode 100644 drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h

-- 
1.8.5.3


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [REVIEW PATCH 01/16] e4000: convert DVB tuner to I2C driver model
  2014-02-27  0:30 [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver Antti Palosaari
@ 2014-02-27  0:30 ` Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 02/16] e4000: implement controls via v4l2 control framework Antti Palosaari
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:30 UTC (permalink / raw)
  To: linux-media
  Cc: Hans Verkuil, Antti Palosaari, Jean Delvare, Mauro Carvalho Chehab

Driver conversion from proprietary DVB tuner model to more
general I2C driver model.

Cc: Jean Delvare <khali@linux-fr.org>
Cc: Mauro Carvalho Chehab <m.chehab@samsung.com>
Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/tuners/e4000.c            | 115 ++++++++++++++++++++------------
 drivers/media/tuners/e4000.h            |  21 ++----
 drivers/media/tuners/e4000_priv.h       |   5 +-
 drivers/media/usb/dvb-usb-v2/rtl28xxu.c |  41 +++++++++---
 drivers/media/usb/dvb-usb-v2/rtl28xxu.h |   1 +
 5 files changed, 111 insertions(+), 72 deletions(-)

diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c
index 40c1da7..0153169 100644
--- a/drivers/media/tuners/e4000.c
+++ b/drivers/media/tuners/e4000.c
@@ -31,7 +31,7 @@ static int e4000_wr_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len)
 	u8 buf[MAX_XFER_SIZE];
 	struct i2c_msg msg[1] = {
 		{
-			.addr = priv->cfg->i2c_addr,
+			.addr = priv->client->addr,
 			.flags = 0,
 			.len = 1 + len,
 			.buf = buf,
@@ -39,7 +39,7 @@ static int e4000_wr_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len)
 	};
 
 	if (1 + len > sizeof(buf)) {
-		dev_warn(&priv->i2c->dev,
+		dev_warn(&priv->client->dev,
 			 "%s: i2c wr reg=%04x: len=%d is too big!\n",
 			 KBUILD_MODNAME, reg, len);
 		return -EINVAL;
@@ -48,11 +48,11 @@ static int e4000_wr_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len)
 	buf[0] = reg;
 	memcpy(&buf[1], val, len);
 
-	ret = i2c_transfer(priv->i2c, msg, 1);
+	ret = i2c_transfer(priv->client->adapter, msg, 1);
 	if (ret == 1) {
 		ret = 0;
 	} else {
-		dev_warn(&priv->i2c->dev,
+		dev_warn(&priv->client->dev,
 				"%s: i2c wr failed=%d reg=%02x len=%d\n",
 				KBUILD_MODNAME, ret, reg, len);
 		ret = -EREMOTEIO;
@@ -67,12 +67,12 @@ static int e4000_rd_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len)
 	u8 buf[MAX_XFER_SIZE];
 	struct i2c_msg msg[2] = {
 		{
-			.addr = priv->cfg->i2c_addr,
+			.addr = priv->client->addr,
 			.flags = 0,
 			.len = 1,
 			.buf = &reg,
 		}, {
-			.addr = priv->cfg->i2c_addr,
+			.addr = priv->client->addr,
 			.flags = I2C_M_RD,
 			.len = len,
 			.buf = buf,
@@ -80,18 +80,18 @@ static int e4000_rd_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len)
 	};
 
 	if (len > sizeof(buf)) {
-		dev_warn(&priv->i2c->dev,
+		dev_warn(&priv->client->dev,
 			 "%s: i2c rd reg=%04x: len=%d is too big!\n",
 			 KBUILD_MODNAME, reg, len);
 		return -EINVAL;
 	}
 
-	ret = i2c_transfer(priv->i2c, msg, 2);
+	ret = i2c_transfer(priv->client->adapter, msg, 2);
 	if (ret == 2) {
 		memcpy(val, buf, len);
 		ret = 0;
 	} else {
-		dev_warn(&priv->i2c->dev,
+		dev_warn(&priv->client->dev,
 				"%s: i2c rd failed=%d reg=%02x len=%d\n",
 				KBUILD_MODNAME, ret, reg, len);
 		ret = -EREMOTEIO;
@@ -117,7 +117,7 @@ static int e4000_init(struct dvb_frontend *fe)
 	struct e4000_priv *priv = fe->tuner_priv;
 	int ret;
 
-	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+	dev_dbg(&priv->client->dev, "%s:\n", __func__);
 
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
@@ -186,7 +186,7 @@ err:
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 0);
 
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 	return ret;
 }
 
@@ -195,7 +195,7 @@ static int e4000_sleep(struct dvb_frontend *fe)
 	struct e4000_priv *priv = fe->tuner_priv;
 	int ret;
 
-	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+	dev_dbg(&priv->client->dev, "%s:\n", __func__);
 
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
@@ -212,7 +212,7 @@ err:
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 0);
 
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 	return ret;
 }
 
@@ -224,7 +224,7 @@ static int e4000_set_params(struct dvb_frontend *fe)
 	unsigned int f_vco;
 	u8 buf[5], i_data[4], q_data[4];
 
-	dev_dbg(&priv->i2c->dev,
+	dev_dbg(&priv->client->dev,
 			"%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n",
 			__func__, c->delivery_system, c->frequency,
 			c->bandwidth_hz);
@@ -253,14 +253,15 @@ static int e4000_set_params(struct dvb_frontend *fe)
 	 * or more.
 	 */
 	f_vco = c->frequency * e4000_pll_lut[i].mul;
-	sigma_delta = div_u64(0x10000ULL * (f_vco % priv->cfg->clock), priv->cfg->clock);
-	buf[0] = f_vco / priv->cfg->clock;
+	sigma_delta = div_u64(0x10000ULL * (f_vco % priv->clock), priv->clock);
+	buf[0] = f_vco / priv->clock;
 	buf[1] = (sigma_delta >> 0) & 0xff;
 	buf[2] = (sigma_delta >> 8) & 0xff;
 	buf[3] = 0x00;
 	buf[4] = e4000_pll_lut[i].div;
 
-	dev_dbg(&priv->i2c->dev, "%s: f_vco=%u pll div=%d sigma_delta=%04x\n",
+	dev_dbg(&priv->client->dev,
+			"%s: f_vco=%u pll div=%d sigma_delta=%04x\n",
 			__func__, f_vco, buf[0], sigma_delta);
 
 	ret = e4000_wr_regs(priv, 0x09, buf, 5);
@@ -369,7 +370,7 @@ err:
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 0);
 
-	dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 	return ret;
 }
 
@@ -377,24 +378,13 @@ static int e4000_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
 {
 	struct e4000_priv *priv = fe->tuner_priv;
 
-	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+	dev_dbg(&priv->client->dev, "%s:\n", __func__);
 
 	*frequency = 0; /* Zero-IF */
 
 	return 0;
 }
 
-static int e4000_release(struct dvb_frontend *fe)
-{
-	struct e4000_priv *priv = fe->tuner_priv;
-
-	dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
-
-	kfree(fe->tuner_priv);
-
-	return 0;
-}
-
 static const struct dvb_tuner_ops e4000_tuner_ops = {
 	.info = {
 		.name           = "Elonics E4000",
@@ -402,8 +392,6 @@ static const struct dvb_tuner_ops e4000_tuner_ops = {
 		.frequency_max  = 862000000,
 	},
 
-	.release = e4000_release,
-
 	.init = e4000_init,
 	.sleep = e4000_sleep,
 	.set_params = e4000_set_params,
@@ -411,9 +399,11 @@ static const struct dvb_tuner_ops e4000_tuner_ops = {
 	.get_if_frequency = e4000_get_if_frequency,
 };
 
-struct dvb_frontend *e4000_attach(struct dvb_frontend *fe,
-		struct i2c_adapter *i2c, const struct e4000_config *cfg)
+static int e4000_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
 {
+	struct e4000_config *cfg = client->dev.platform_data;
+	struct dvb_frontend *fe = cfg->fe;
 	struct e4000_priv *priv;
 	int ret;
 	u8 chip_id;
@@ -424,29 +414,33 @@ struct dvb_frontend *e4000_attach(struct dvb_frontend *fe,
 	priv = kzalloc(sizeof(struct e4000_priv), GFP_KERNEL);
 	if (!priv) {
 		ret = -ENOMEM;
-		dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
+		dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
 		goto err;
 	}
 
-	priv->cfg = cfg;
-	priv->i2c = i2c;
+	priv->clock = cfg->clock;
+	priv->client = client;
+	priv->fe = cfg->fe;
 
 	/* check if the tuner is there */
 	ret = e4000_rd_reg(priv, 0x02, &chip_id);
 	if (ret < 0)
 		goto err;
 
-	dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);
+	dev_dbg(&priv->client->dev,
+			"%s: chip_id=%02x\n", __func__, chip_id);
 
-	if (chip_id != 0x40)
+	if (chip_id != 0x40) {
+		ret = -ENODEV;
 		goto err;
+	}
 
 	/* put sleep as chip seems to be in normal mode by default */
 	ret = e4000_wr_reg(priv, 0x00, 0x00);
 	if (ret < 0)
 		goto err;
 
-	dev_info(&priv->i2c->dev,
+	dev_info(&priv->client->dev,
 			"%s: Elonics E4000 successfully identified\n",
 			KBUILD_MODNAME);
 
@@ -457,16 +451,49 @@ struct dvb_frontend *e4000_attach(struct dvb_frontend *fe,
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 0);
 
-	return fe;
+	i2c_set_clientdata(client, priv);
+
+	return 0;
 err:
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 0);
 
-	dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
+	dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret);
 	kfree(priv);
-	return NULL;
+	return ret;
 }
-EXPORT_SYMBOL(e4000_attach);
+
+static int e4000_remove(struct i2c_client *client)
+{
+	struct e4000_priv *priv = i2c_get_clientdata(client);
+	struct dvb_frontend *fe = priv->fe;
+
+	dev_dbg(&client->dev, "%s:\n", __func__);
+
+	memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
+	fe->tuner_priv = NULL;
+	kfree(priv);
+
+	return 0;
+}
+
+static const struct i2c_device_id e4000_id[] = {
+	{"e4000", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, e4000_id);
+
+static struct i2c_driver e4000_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "e4000",
+	},
+	.probe		= e4000_probe,
+	.remove		= e4000_remove,
+	.id_table	= e4000_id,
+};
+
+module_i2c_driver(e4000_driver);
 
 MODULE_DESCRIPTION("Elonics E4000 silicon tuner driver");
 MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/tuners/e4000.h b/drivers/media/tuners/e4000.h
index 25ee7c0..e74b8b2 100644
--- a/drivers/media/tuners/e4000.h
+++ b/drivers/media/tuners/e4000.h
@@ -24,12 +24,15 @@
 #include <linux/kconfig.h>
 #include "dvb_frontend.h"
 
+/*
+ * I2C address
+ * 0x64, 0x65, 0x66, 0x67
+ */
 struct e4000_config {
 	/*
-	 * I2C address
-	 * 0x64, 0x65, 0x66, 0x67
+	 * frontend
 	 */
-	u8 i2c_addr;
+	struct dvb_frontend *fe;
 
 	/*
 	 * clock
@@ -37,16 +40,4 @@ struct e4000_config {
 	u32 clock;
 };
 
-#if IS_ENABLED(CONFIG_MEDIA_TUNER_E4000)
-extern struct dvb_frontend *e4000_attach(struct dvb_frontend *fe,
-		struct i2c_adapter *i2c, const struct e4000_config *cfg);
-#else
-static inline struct dvb_frontend *e4000_attach(struct dvb_frontend *fe,
-		struct i2c_adapter *i2c, const struct e4000_config *cfg)
-{
-	dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__);
-	return NULL;
-}
-#endif
-
 #endif
diff --git a/drivers/media/tuners/e4000_priv.h b/drivers/media/tuners/e4000_priv.h
index a385505..8f45a30 100644
--- a/drivers/media/tuners/e4000_priv.h
+++ b/drivers/media/tuners/e4000_priv.h
@@ -24,8 +24,9 @@
 #include "e4000.h"
 
 struct e4000_priv {
-	const struct e4000_config *cfg;
-	struct i2c_adapter *i2c;
+	struct i2c_client *client;
+	u32 clock;
+	struct dvb_frontend *fe;
 };
 
 struct e4000_pll {
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
index 52eb5db..38f4bc8 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
@@ -852,11 +852,6 @@ err:
 	return ret;
 }
 
-static const struct e4000_config rtl2832u_e4000_config = {
-	.i2c_addr = 0x64,
-	.clock = 28800000,
-};
-
 static const struct fc2580_config rtl2832u_fc2580_config = {
 	.i2c_addr = 0x56,
 	.clock = 16384000,
@@ -890,10 +885,13 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
 	int ret;
 	struct dvb_usb_device *d = adap_to_d(adap);
 	struct rtl28xxu_priv *priv = d_to_priv(d);
-	struct dvb_frontend *fe;
+	struct dvb_frontend *fe = NULL;
+	struct i2c_board_info info;
 
 	dev_dbg(&d->udev->dev, "%s:\n", __func__);
 
+	memset(&info, 0, sizeof(struct i2c_board_info));
+
 	switch (priv->tuner) {
 	case TUNER_RTL2832_FC0012:
 		fe = dvb_attach(fc0012_attach, adap->fe[0],
@@ -913,9 +911,19 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
 		adap->fe[0]->ops.read_signal_strength =
 				adap->fe[0]->ops.tuner_ops.get_rf_strength;
 		return 0;
-	case TUNER_RTL2832_E4000:
-		fe = dvb_attach(e4000_attach, adap->fe[0], &d->i2c_adap,
-				&rtl2832u_e4000_config);
+	case TUNER_RTL2832_E4000: {
+			struct e4000_config e4000_config = {
+				.fe = adap->fe[0],
+				.clock = 28800000,
+			};
+
+			strlcpy(info.type, "e4000", I2C_NAME_SIZE);
+			info.addr = 0x64;
+			info.platform_data = &e4000_config;
+
+			request_module("e4000");
+			priv->client = i2c_new_device(&d->i2c_adap, &info);
+		}
 		break;
 	case TUNER_RTL2832_FC2580:
 		fe = dvb_attach(fc2580_attach, adap->fe[0], &d->i2c_adap,
@@ -964,12 +972,11 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
 				adap->fe[0]->ops.tuner_ops.get_rf_strength;
 		break;
 	default:
-		fe = NULL;
 		dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME,
 				priv->tuner);
 	}
 
-	if (fe == NULL) {
+	if (fe == NULL && priv->client == NULL) {
 		ret = -ENODEV;
 		goto err;
 	}
@@ -1014,6 +1021,17 @@ err:
 	return ret;
 }
 
+static void rtl28xxu_exit(struct dvb_usb_device *d)
+{
+	struct rtl28xxu_priv *priv = d->priv;
+
+	dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+	i2c_release_client(priv->client);
+
+	return;
+}
+
 static int rtl2831u_power_ctrl(struct dvb_usb_device *d, int onoff)
 {
 	int ret;
@@ -1376,6 +1394,7 @@ static const struct dvb_usb_device_properties rtl2832u_props = {
 	.frontend_attach = rtl2832u_frontend_attach,
 	.tuner_attach = rtl2832u_tuner_attach,
 	.init = rtl28xxu_init,
+	.exit = rtl28xxu_exit,
 	.get_rc_config = rtl2832u_get_rc_config,
 
 	.num_adapters = 1,
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
index 2142bcb..367aca1 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
@@ -56,6 +56,7 @@ struct rtl28xxu_priv {
 	char *tuner_name;
 	u8 page; /* integrated demod active register page */
 	bool rc_active;
+	struct i2c_client *client;
 };
 
 enum rtl28xxu_chip_id {
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [REVIEW PATCH 02/16] e4000: implement controls via v4l2 control framework
  2014-02-27  0:30 [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 01/16] e4000: convert DVB tuner to I2C driver model Antti Palosaari
@ 2014-02-27  0:30 ` Antti Palosaari
  2014-03-13 13:57   ` Mauro Carvalho Chehab
  2014-02-27  0:30 ` [REVIEW PATCH 03/16] e4000: fix PLL calc to allow higher frequencies Antti Palosaari
                   ` (13 subsequent siblings)
  15 siblings, 1 reply; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:30 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Antti Palosaari, Mauro Carvalho Chehab

Implement gain and bandwidth controls using v4l2 control framework.
Pointer to control handler is provided by exported symbol.

Cc: Mauro Carvalho Chehab <m.chehab@samsung.com>
Cc: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/tuners/e4000.c      | 210 +++++++++++++++++++++++++++++++++++++-
 drivers/media/tuners/e4000.h      |  14 +++
 drivers/media/tuners/e4000_priv.h |  75 ++++++++++++++
 3 files changed, 298 insertions(+), 1 deletion(-)

diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c
index 0153169..c97daa0 100644
--- a/drivers/media/tuners/e4000.c
+++ b/drivers/media/tuners/e4000.c
@@ -385,6 +385,178 @@ static int e4000_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
 	return 0;
 }
 
+static int e4000_set_lna_gain(struct dvb_frontend *fe)
+{
+	struct e4000_priv *priv = fe->tuner_priv;
+	int ret;
+	u8 u8tmp;
+	dev_dbg(&priv->client->dev, "%s: lna auto=%d->%d val=%d->%d\n",
+			__func__, priv->lna_gain_auto->cur.val,
+			priv->lna_gain_auto->val, priv->lna_gain->cur.val,
+			priv->lna_gain->val);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	if (priv->lna_gain_auto->val && priv->if_gain_auto->cur.val)
+		u8tmp = 0x17;
+	else if (priv->lna_gain_auto->val)
+		u8tmp = 0x19;
+	else if (priv->if_gain_auto->cur.val)
+		u8tmp = 0x16;
+	else
+		u8tmp = 0x10;
+
+	ret = e4000_wr_reg(priv, 0x1a, u8tmp);
+	if (ret)
+		goto err;
+
+	if (priv->lna_gain_auto->val == false) {
+		ret = e4000_wr_reg(priv, 0x14, priv->lna_gain->val);
+		if (ret)
+			goto err;
+	}
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	return 0;
+err:
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
+static int e4000_set_mixer_gain(struct dvb_frontend *fe)
+{
+	struct e4000_priv *priv = fe->tuner_priv;
+	int ret;
+	u8 u8tmp;
+	dev_dbg(&priv->client->dev, "%s: mixer auto=%d->%d val=%d->%d\n",
+			__func__, priv->mixer_gain_auto->cur.val,
+			priv->mixer_gain_auto->val, priv->mixer_gain->cur.val,
+			priv->mixer_gain->val);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	if (priv->mixer_gain_auto->val)
+		u8tmp = 0x15;
+	else
+		u8tmp = 0x14;
+
+	ret = e4000_wr_reg(priv, 0x20, u8tmp);
+	if (ret)
+		goto err;
+
+	if (priv->mixer_gain_auto->val == false) {
+		ret = e4000_wr_reg(priv, 0x15, priv->mixer_gain->val);
+		if (ret)
+			goto err;
+	}
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	return 0;
+err:
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
+static int e4000_set_if_gain(struct dvb_frontend *fe)
+{
+	struct e4000_priv *priv = fe->tuner_priv;
+	int ret;
+	u8 buf[2];
+	u8 u8tmp;
+	dev_dbg(&priv->client->dev, "%s: if auto=%d->%d val=%d->%d\n",
+			__func__, priv->if_gain_auto->cur.val,
+			priv->if_gain_auto->val, priv->if_gain->cur.val,
+			priv->if_gain->val);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	if (priv->if_gain_auto->val && priv->lna_gain_auto->cur.val)
+		u8tmp = 0x17;
+	else if (priv->lna_gain_auto->cur.val)
+		u8tmp = 0x19;
+	else if (priv->if_gain_auto->val)
+		u8tmp = 0x16;
+	else
+		u8tmp = 0x10;
+
+	ret = e4000_wr_reg(priv, 0x1a, u8tmp);
+	if (ret)
+		goto err;
+
+	if (priv->if_gain_auto->val == false) {
+		buf[0] = e4000_if_gain_lut[priv->if_gain->val].reg16_val;
+		buf[1] = e4000_if_gain_lut[priv->if_gain->val].reg17_val;
+		ret = e4000_wr_regs(priv, 0x16, buf, 2);
+		if (ret)
+			goto err;
+	}
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	return 0;
+err:
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
+	return ret;
+}
+
+static int e4000_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct e4000_priv *priv =
+			container_of(ctrl->handler, struct e4000_priv, hdl);
+	struct dvb_frontend *fe = priv->fe;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	int ret;
+	dev_dbg(&priv->client->dev,
+			"%s: id=%d name=%s val=%d min=%d max=%d step=%d\n",
+			__func__, ctrl->id, ctrl->name, ctrl->val,
+			ctrl->minimum, ctrl->maximum, ctrl->step);
+
+	switch (ctrl->id) {
+	case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:
+	case V4L2_CID_RF_TUNER_BANDWIDTH:
+		c->bandwidth_hz = priv->bandwidth->val;
+		ret = e4000_set_params(priv->fe);
+		break;
+	case  V4L2_CID_RF_TUNER_LNA_GAIN_AUTO:
+	case  V4L2_CID_RF_TUNER_LNA_GAIN:
+		ret = e4000_set_lna_gain(priv->fe);
+		break;
+	case  V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO:
+	case  V4L2_CID_RF_TUNER_MIXER_GAIN:
+		ret = e4000_set_mixer_gain(priv->fe);
+		break;
+	case  V4L2_CID_RF_TUNER_IF_GAIN_AUTO:
+	case  V4L2_CID_RF_TUNER_IF_GAIN:
+		ret = e4000_set_if_gain(priv->fe);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops e4000_ctrl_ops = {
+	.s_ctrl = e4000_s_ctrl,
+};
+
 static const struct dvb_tuner_ops e4000_tuner_ops = {
 	.info = {
 		.name           = "Elonics E4000",
@@ -399,6 +571,13 @@ static const struct dvb_tuner_ops e4000_tuner_ops = {
 	.get_if_frequency = e4000_get_if_frequency,
 };
 
+struct v4l2_ctrl_handler *e4000_get_ctrl_handler(struct dvb_frontend *fe)
+{
+	struct e4000_priv *priv = fe->tuner_priv;
+	return &priv->hdl;
+}
+EXPORT_SYMBOL(e4000_get_ctrl_handler);
+
 static int e4000_probe(struct i2c_client *client,
 		const struct i2c_device_id *id)
 {
@@ -440,6 +619,35 @@ static int e4000_probe(struct i2c_client *client,
 	if (ret < 0)
 		goto err;
 
+	/* Register controls */
+	v4l2_ctrl_handler_init(&priv->hdl, 8);
+	priv->bandwidth_auto = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+			V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1);
+	priv->bandwidth = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+			V4L2_CID_RF_TUNER_BANDWIDTH, 4300000, 11000000, 100000, 4300000);
+	v4l2_ctrl_auto_cluster(2, &priv->bandwidth_auto, 0, false);
+	priv->lna_gain_auto = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+			V4L2_CID_RF_TUNER_LNA_GAIN_AUTO, 0, 1, 1, 1);
+	priv->lna_gain = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+			V4L2_CID_RF_TUNER_LNA_GAIN, 0, 15, 1, 10);
+	v4l2_ctrl_auto_cluster(2, &priv->lna_gain_auto, 0, false);
+	priv->mixer_gain_auto = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+			V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO, 0, 1, 1, 1);
+	priv->mixer_gain = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+			V4L2_CID_RF_TUNER_MIXER_GAIN, 0, 1, 1, 1);
+	v4l2_ctrl_auto_cluster(2, &priv->mixer_gain_auto, 0, false);
+	priv->if_gain_auto = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+			V4L2_CID_RF_TUNER_IF_GAIN_AUTO, 0, 1, 1, 1);
+	priv->if_gain = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+			V4L2_CID_RF_TUNER_IF_GAIN, 0, 54, 1, 0);
+	v4l2_ctrl_auto_cluster(2, &priv->if_gain_auto, 0, false);
+	if (priv->hdl.error) {
+		ret = priv->hdl.error;
+		dev_err(&priv->client->dev, "Could not initialize controls\n");
+		v4l2_ctrl_handler_free(&priv->hdl);
+		goto err;
+	}
+
 	dev_info(&priv->client->dev,
 			"%s: Elonics E4000 successfully identified\n",
 			KBUILD_MODNAME);
@@ -469,7 +677,7 @@ static int e4000_remove(struct i2c_client *client)
 	struct dvb_frontend *fe = priv->fe;
 
 	dev_dbg(&client->dev, "%s:\n", __func__);
-
+	v4l2_ctrl_handler_free(&priv->hdl);
 	memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
 	fe->tuner_priv = NULL;
 	kfree(priv);
diff --git a/drivers/media/tuners/e4000.h b/drivers/media/tuners/e4000.h
index e74b8b2..989f2ea 100644
--- a/drivers/media/tuners/e4000.h
+++ b/drivers/media/tuners/e4000.h
@@ -40,4 +40,18 @@ struct e4000_config {
 	u32 clock;
 };
 
+#if IS_ENABLED(CONFIG_MEDIA_TUNER_E4000)
+extern struct v4l2_ctrl_handler *e4000_get_ctrl_handler(
+		struct dvb_frontend *fe
+);
+#else
+static inline struct v4l2_ctrl_handler *e4000_get_ctrl_handler(
+		struct dvb_frontend *fe
+)
+{
+	pr_warn("%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif
+
 #endif
diff --git a/drivers/media/tuners/e4000_priv.h b/drivers/media/tuners/e4000_priv.h
index 8f45a30..8cc27b3 100644
--- a/drivers/media/tuners/e4000_priv.h
+++ b/drivers/media/tuners/e4000_priv.h
@@ -22,11 +22,23 @@
 #define E4000_PRIV_H
 
 #include "e4000.h"
+#include <media/v4l2-ctrls.h>
 
 struct e4000_priv {
 	struct i2c_client *client;
 	u32 clock;
 	struct dvb_frontend *fe;
+
+	/* Controls */
+	struct v4l2_ctrl_handler hdl;
+	struct v4l2_ctrl *bandwidth_auto;
+	struct v4l2_ctrl *bandwidth;
+	struct v4l2_ctrl *lna_gain_auto;
+	struct v4l2_ctrl *lna_gain;
+	struct v4l2_ctrl *mixer_gain_auto;
+	struct v4l2_ctrl *mixer_gain;
+	struct v4l2_ctrl *if_gain_auto;
+	struct v4l2_ctrl *if_gain;
 };
 
 struct e4000_pll {
@@ -145,4 +157,67 @@ static const struct e4000_if_filter e4000_if_filter_lut[] = {
 	{ 0xffffffff, 0x00, 0x20 },
 };
 
+struct e4000_if_gain {
+	u8 reg16_val;
+	u8 reg17_val;
+};
+
+static const struct e4000_if_gain e4000_if_gain_lut[] = {
+	{0x00, 0x00},
+	{0x20, 0x00},
+	{0x40, 0x00},
+	{0x02, 0x00},
+	{0x22, 0x00},
+	{0x42, 0x00},
+	{0x04, 0x00},
+	{0x24, 0x00},
+	{0x44, 0x00},
+	{0x01, 0x00},
+	{0x21, 0x00},
+	{0x41, 0x00},
+	{0x03, 0x00},
+	{0x23, 0x00},
+	{0x43, 0x00},
+	{0x05, 0x00},
+	{0x25, 0x00},
+	{0x45, 0x00},
+	{0x07, 0x00},
+	{0x27, 0x00},
+	{0x47, 0x00},
+	{0x0f, 0x00},
+	{0x2f, 0x00},
+	{0x4f, 0x00},
+	{0x17, 0x00},
+	{0x37, 0x00},
+	{0x57, 0x00},
+	{0x1f, 0x00},
+	{0x3f, 0x00},
+	{0x5f, 0x00},
+	{0x1f, 0x01},
+	{0x3f, 0x01},
+	{0x5f, 0x01},
+	{0x1f, 0x02},
+	{0x3f, 0x02},
+	{0x5f, 0x02},
+	{0x1f, 0x03},
+	{0x3f, 0x03},
+	{0x5f, 0x03},
+	{0x1f, 0x04},
+	{0x3f, 0x04},
+	{0x5f, 0x04},
+	{0x1f, 0x0c},
+	{0x3f, 0x0c},
+	{0x5f, 0x0c},
+	{0x1f, 0x14},
+	{0x3f, 0x14},
+	{0x5f, 0x14},
+	{0x1f, 0x1c},
+	{0x3f, 0x1c},
+	{0x5f, 0x1c},
+	{0x1f, 0x24},
+	{0x3f, 0x24},
+	{0x5f, 0x24},
+	{0x7f, 0x24},
+};
+
 #endif
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [REVIEW PATCH 03/16] e4000: fix PLL calc to allow higher frequencies
  2014-02-27  0:30 [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 01/16] e4000: convert DVB tuner to I2C driver model Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 02/16] e4000: implement controls via v4l2 control framework Antti Palosaari
@ 2014-02-27  0:30 ` Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 04/16] e4000: implement PLL lock v4l control Antti Palosaari
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:30 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Antti Palosaari

There was 32-bit overflow on VCO frequency calculation which blocks
tuning to 1073 - 1104 MHz. Use 64 bit number in order to avoid VCO
frequency overflow.

After that fix device in question tunes to following range:
60 - 1104 MHz
1250 - 2207 MHz

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/tuners/e4000.c | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c
index c97daa0..e439ef6 100644
--- a/drivers/media/tuners/e4000.c
+++ b/drivers/media/tuners/e4000.c
@@ -221,11 +221,11 @@ static int e4000_set_params(struct dvb_frontend *fe)
 	struct e4000_priv *priv = fe->tuner_priv;
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 	int ret, i, sigma_delta;
-	unsigned int f_vco;
+	u64 f_vco;
 	u8 buf[5], i_data[4], q_data[4];
 
 	dev_dbg(&priv->client->dev,
-			"%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n",
+			"%s: delivery_system=%d frequency=%u bandwidth_hz=%u\n",
 			__func__, c->delivery_system, c->frequency,
 			c->bandwidth_hz);
 
@@ -248,20 +248,16 @@ static int e4000_set_params(struct dvb_frontend *fe)
 		goto err;
 	}
 
-	/*
-	 * Note: Currently f_vco overflows when c->frequency is 1 073 741 824 Hz
-	 * or more.
-	 */
-	f_vco = c->frequency * e4000_pll_lut[i].mul;
+	f_vco = 1ull * c->frequency * e4000_pll_lut[i].mul;
 	sigma_delta = div_u64(0x10000ULL * (f_vco % priv->clock), priv->clock);
-	buf[0] = f_vco / priv->clock;
+	buf[0] = div_u64(f_vco, priv->clock);
 	buf[1] = (sigma_delta >> 0) & 0xff;
 	buf[2] = (sigma_delta >> 8) & 0xff;
 	buf[3] = 0x00;
 	buf[4] = e4000_pll_lut[i].div;
 
 	dev_dbg(&priv->client->dev,
-			"%s: f_vco=%u pll div=%d sigma_delta=%04x\n",
+			"%s: f_vco=%llu pll div=%d sigma_delta=%04x\n",
 			__func__, f_vco, buf[0], sigma_delta);
 
 	ret = e4000_wr_regs(priv, 0x09, buf, 5);
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [REVIEW PATCH 04/16] e4000: implement PLL lock v4l control
  2014-02-27  0:30 [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver Antti Palosaari
                   ` (2 preceding siblings ...)
  2014-02-27  0:30 ` [REVIEW PATCH 03/16] e4000: fix PLL calc to allow higher frequencies Antti Palosaari
@ 2014-02-27  0:30 ` Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 05/16] e4000: get rid of DVB i2c_gate_ctrl() Antti Palosaari
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:30 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Antti Palosaari, Mauro Carvalho Chehab

Implement PLL lock control to get PLL lock flag status from tuner
synthesizer.

Cc: Mauro Carvalho Chehab <m.chehab@samsung.com>
Cc: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/tuners/e4000.c      | 53 ++++++++++++++++++++++++++++++++++++++-
 drivers/media/tuners/e4000_priv.h |  2 ++
 2 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c
index e439ef6..82c6b33 100644
--- a/drivers/media/tuners/e4000.c
+++ b/drivers/media/tuners/e4000.c
@@ -181,6 +181,8 @@ static int e4000_init(struct dvb_frontend *fe)
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 0);
 
+	priv->active = true;
+
 	return 0;
 err:
 	if (fe->ops.i2c_gate_ctrl)
@@ -197,6 +199,8 @@ static int e4000_sleep(struct dvb_frontend *fe)
 
 	dev_dbg(&priv->client->dev, "%s:\n", __func__);
 
+	priv->active = false;
+
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
 
@@ -512,6 +516,50 @@ err:
 	return ret;
 }
 
+static int e4000_pll_lock(struct dvb_frontend *fe)
+{
+	struct e4000_priv *priv = fe->tuner_priv;
+	int ret;
+	u8 u8tmp;
+
+	if (priv->active == false)
+		return 0;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	ret = e4000_rd_reg(priv, 0x07, &u8tmp);
+	if (ret)
+		goto err;
+
+	priv->pll_lock->val = (u8tmp & 0x01);
+err:
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	if (ret)
+		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
+
+	return ret;
+}
+
+static int e4000_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct e4000_priv *priv =
+			container_of(ctrl->handler, struct e4000_priv, hdl);
+	int ret;
+
+	switch (ctrl->id) {
+	case  V4L2_CID_RF_TUNER_PLL_LOCK:
+		ret = e4000_pll_lock(priv->fe);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
 static int e4000_s_ctrl(struct v4l2_ctrl *ctrl)
 {
 	struct e4000_priv *priv =
@@ -550,6 +598,7 @@ static int e4000_s_ctrl(struct v4l2_ctrl *ctrl)
 }
 
 static const struct v4l2_ctrl_ops e4000_ctrl_ops = {
+	.g_volatile_ctrl = e4000_g_volatile_ctrl,
 	.s_ctrl = e4000_s_ctrl,
 };
 
@@ -616,7 +665,7 @@ static int e4000_probe(struct i2c_client *client,
 		goto err;
 
 	/* Register controls */
-	v4l2_ctrl_handler_init(&priv->hdl, 8);
+	v4l2_ctrl_handler_init(&priv->hdl, 9);
 	priv->bandwidth_auto = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
 			V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1);
 	priv->bandwidth = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
@@ -637,6 +686,8 @@ static int e4000_probe(struct i2c_client *client,
 	priv->if_gain = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
 			V4L2_CID_RF_TUNER_IF_GAIN, 0, 54, 1, 0);
 	v4l2_ctrl_auto_cluster(2, &priv->if_gain_auto, 0, false);
+	priv->pll_lock = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+			V4L2_CID_RF_TUNER_PLL_LOCK,  0, 1, 1, 0);
 	if (priv->hdl.error) {
 		ret = priv->hdl.error;
 		dev_err(&priv->client->dev, "Could not initialize controls\n");
diff --git a/drivers/media/tuners/e4000_priv.h b/drivers/media/tuners/e4000_priv.h
index 8cc27b3..d41dbcc 100644
--- a/drivers/media/tuners/e4000_priv.h
+++ b/drivers/media/tuners/e4000_priv.h
@@ -28,6 +28,7 @@ struct e4000_priv {
 	struct i2c_client *client;
 	u32 clock;
 	struct dvb_frontend *fe;
+	bool active;
 
 	/* Controls */
 	struct v4l2_ctrl_handler hdl;
@@ -39,6 +40,7 @@ struct e4000_priv {
 	struct v4l2_ctrl *mixer_gain;
 	struct v4l2_ctrl *if_gain_auto;
 	struct v4l2_ctrl *if_gain;
+	struct v4l2_ctrl *pll_lock;
 };
 
 struct e4000_pll {
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [REVIEW PATCH 05/16] e4000: get rid of DVB i2c_gate_ctrl()
  2014-02-27  0:30 [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver Antti Palosaari
                   ` (3 preceding siblings ...)
  2014-02-27  0:30 ` [REVIEW PATCH 04/16] e4000: implement PLL lock v4l control Antti Palosaari
@ 2014-02-27  0:30 ` Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 06/16] e4000: convert to Regmap API Antti Palosaari
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:30 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Antti Palosaari

Gate control is now implemented by rtl2832 I2C adapter so we do not
need proprietary DVB i2c_gate_ctrl() anymore.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/tuners/e4000.c | 106 +++++++++----------------------------------
 1 file changed, 21 insertions(+), 85 deletions(-)

diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c
index 82c6b33..fd38f51 100644
--- a/drivers/media/tuners/e4000.c
+++ b/drivers/media/tuners/e4000.c
@@ -119,9 +119,6 @@ static int e4000_init(struct dvb_frontend *fe)
 
 	dev_dbg(&priv->client->dev, "%s:\n", __func__);
 
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-
 	/* dummy I2C to ensure I2C wakes up */
 	ret = e4000_wr_reg(priv, 0x02, 0x40);
 
@@ -178,17 +175,11 @@ static int e4000_init(struct dvb_frontend *fe)
 	if (ret < 0)
 		goto err;
 
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
-
 	priv->active = true;
-
-	return 0;
 err:
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
+	if (ret)
+		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 
-	dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 	return ret;
 }
 
@@ -201,22 +192,13 @@ static int e4000_sleep(struct dvb_frontend *fe)
 
 	priv->active = false;
 
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-
 	ret = e4000_wr_reg(priv, 0x00, 0x00);
 	if (ret < 0)
 		goto err;
-
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
-
-	return 0;
 err:
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
+	if (ret)
+		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 
-	dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 	return ret;
 }
 
@@ -233,9 +215,6 @@ static int e4000_set_params(struct dvb_frontend *fe)
 			__func__, c->delivery_system, c->frequency,
 			c->bandwidth_hz);
 
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-
 	/* gain control manual */
 	ret = e4000_wr_reg(priv, 0x1a, 0x00);
 	if (ret < 0)
@@ -361,16 +340,10 @@ static int e4000_set_params(struct dvb_frontend *fe)
 	ret = e4000_wr_reg(priv, 0x1a, 0x17);
 	if (ret < 0)
 		goto err;
-
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
-
-	return 0;
 err:
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
+	if (ret)
+		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 
-	dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 	return ret;
 }
 
@@ -390,14 +363,12 @@ static int e4000_set_lna_gain(struct dvb_frontend *fe)
 	struct e4000_priv *priv = fe->tuner_priv;
 	int ret;
 	u8 u8tmp;
+
 	dev_dbg(&priv->client->dev, "%s: lna auto=%d->%d val=%d->%d\n",
 			__func__, priv->lna_gain_auto->cur.val,
 			priv->lna_gain_auto->val, priv->lna_gain->cur.val,
 			priv->lna_gain->val);
 
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-
 	if (priv->lna_gain_auto->val && priv->if_gain_auto->cur.val)
 		u8tmp = 0x17;
 	else if (priv->lna_gain_auto->val)
@@ -416,16 +387,10 @@ static int e4000_set_lna_gain(struct dvb_frontend *fe)
 		if (ret)
 			goto err;
 	}
-
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
-
-	return 0;
 err:
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
+	if (ret)
+		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 
-	dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 	return ret;
 }
 
@@ -434,14 +399,12 @@ static int e4000_set_mixer_gain(struct dvb_frontend *fe)
 	struct e4000_priv *priv = fe->tuner_priv;
 	int ret;
 	u8 u8tmp;
+
 	dev_dbg(&priv->client->dev, "%s: mixer auto=%d->%d val=%d->%d\n",
 			__func__, priv->mixer_gain_auto->cur.val,
 			priv->mixer_gain_auto->val, priv->mixer_gain->cur.val,
 			priv->mixer_gain->val);
 
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-
 	if (priv->mixer_gain_auto->val)
 		u8tmp = 0x15;
 	else
@@ -456,16 +419,10 @@ static int e4000_set_mixer_gain(struct dvb_frontend *fe)
 		if (ret)
 			goto err;
 	}
-
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
-
-	return 0;
 err:
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
+	if (ret)
+		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 
-	dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 	return ret;
 }
 
@@ -475,14 +432,12 @@ static int e4000_set_if_gain(struct dvb_frontend *fe)
 	int ret;
 	u8 buf[2];
 	u8 u8tmp;
+
 	dev_dbg(&priv->client->dev, "%s: if auto=%d->%d val=%d->%d\n",
 			__func__, priv->if_gain_auto->cur.val,
 			priv->if_gain_auto->val, priv->if_gain->cur.val,
 			priv->if_gain->val);
 
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-
 	if (priv->if_gain_auto->val && priv->lna_gain_auto->cur.val)
 		u8tmp = 0x17;
 	else if (priv->lna_gain_auto->cur.val)
@@ -503,16 +458,10 @@ static int e4000_set_if_gain(struct dvb_frontend *fe)
 		if (ret)
 			goto err;
 	}
-
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
-
-	return 0;
 err:
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
+	if (ret)
+		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 
-	dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 	return ret;
 }
 
@@ -525,18 +474,12 @@ static int e4000_pll_lock(struct dvb_frontend *fe)
 	if (priv->active == false)
 		return 0;
 
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-
 	ret = e4000_rd_reg(priv, 0x07, &u8tmp);
 	if (ret)
 		goto err;
 
 	priv->pll_lock->val = (u8tmp & 0x01);
 err:
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
-
 	if (ret)
 		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
 
@@ -567,6 +510,7 @@ static int e4000_s_ctrl(struct v4l2_ctrl *ctrl)
 	struct dvb_frontend *fe = priv->fe;
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 	int ret;
+
 	dev_dbg(&priv->client->dev,
 			"%s: id=%d name=%s val=%d min=%d max=%d step=%d\n",
 			__func__, ctrl->id, ctrl->name, ctrl->val,
@@ -632,9 +576,6 @@ static int e4000_probe(struct i2c_client *client,
 	int ret;
 	u8 chip_id;
 
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-
 	priv = kzalloc(sizeof(struct e4000_priv), GFP_KERNEL);
 	if (!priv) {
 		ret = -ENOMEM;
@@ -702,19 +643,13 @@ static int e4000_probe(struct i2c_client *client,
 	fe->tuner_priv = priv;
 	memcpy(&fe->ops.tuner_ops, &e4000_tuner_ops,
 			sizeof(struct dvb_tuner_ops));
-
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
-
 	i2c_set_clientdata(client, priv);
-
-	return 0;
 err:
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
+	if (ret) {
+		dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret);
+		kfree(priv);
+	}
 
-	dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret);
-	kfree(priv);
 	return ret;
 }
 
@@ -724,6 +659,7 @@ static int e4000_remove(struct i2c_client *client)
 	struct dvb_frontend *fe = priv->fe;
 
 	dev_dbg(&client->dev, "%s:\n", __func__);
+
 	v4l2_ctrl_handler_free(&priv->hdl);
 	memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
 	fe->tuner_priv = NULL;
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [REVIEW PATCH 06/16] e4000: convert to Regmap API
  2014-02-27  0:30 [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver Antti Palosaari
                   ` (4 preceding siblings ...)
  2014-02-27  0:30 ` [REVIEW PATCH 05/16] e4000: get rid of DVB i2c_gate_ctrl() Antti Palosaari
@ 2014-02-27  0:30 ` Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 07/16] e4000: rename some variables Antti Palosaari
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:30 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Antti Palosaari, Mauro Carvalho Chehab

That comes possible after driver was converted to kernel I2C model
(I2C binding & proper I2C client with no gate control hack). All
nasty low level I2C routines are now covered by regmap.

Cc: Mauro Carvalho Chehab <m.chehab@samsung.com>
Cc: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/tuners/Kconfig      |   1 +
 drivers/media/tuners/e4000.c      | 212 ++++++++++++--------------------------
 drivers/media/tuners/e4000_priv.h |   2 +
 3 files changed, 68 insertions(+), 147 deletions(-)

diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig
index ba2e365..a128488 100644
--- a/drivers/media/tuners/Kconfig
+++ b/drivers/media/tuners/Kconfig
@@ -204,6 +204,7 @@ config MEDIA_TUNER_TDA18212
 config MEDIA_TUNER_E4000
 	tristate "Elonics E4000 silicon tuner"
 	depends on MEDIA_SUPPORT && I2C
+	select REGMAP_I2C
 	default m if !MEDIA_SUBDRV_AUTOSELECT
 	help
 	  Elonics E4000 silicon tuner driver.
diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c
index fd38f51..3ac0254 100644
--- a/drivers/media/tuners/e4000.c
+++ b/drivers/media/tuners/e4000.c
@@ -21,97 +21,6 @@
 #include "e4000_priv.h"
 #include <linux/math64.h>
 
-/* Max transfer size done by I2C transfer functions */
-#define MAX_XFER_SIZE  64
-
-/* write multiple registers */
-static int e4000_wr_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len)
-{
-	int ret;
-	u8 buf[MAX_XFER_SIZE];
-	struct i2c_msg msg[1] = {
-		{
-			.addr = priv->client->addr,
-			.flags = 0,
-			.len = 1 + len,
-			.buf = buf,
-		}
-	};
-
-	if (1 + len > sizeof(buf)) {
-		dev_warn(&priv->client->dev,
-			 "%s: i2c wr reg=%04x: len=%d is too big!\n",
-			 KBUILD_MODNAME, reg, len);
-		return -EINVAL;
-	}
-
-	buf[0] = reg;
-	memcpy(&buf[1], val, len);
-
-	ret = i2c_transfer(priv->client->adapter, msg, 1);
-	if (ret == 1) {
-		ret = 0;
-	} else {
-		dev_warn(&priv->client->dev,
-				"%s: i2c wr failed=%d reg=%02x len=%d\n",
-				KBUILD_MODNAME, ret, reg, len);
-		ret = -EREMOTEIO;
-	}
-	return ret;
-}
-
-/* read multiple registers */
-static int e4000_rd_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len)
-{
-	int ret;
-	u8 buf[MAX_XFER_SIZE];
-	struct i2c_msg msg[2] = {
-		{
-			.addr = priv->client->addr,
-			.flags = 0,
-			.len = 1,
-			.buf = &reg,
-		}, {
-			.addr = priv->client->addr,
-			.flags = I2C_M_RD,
-			.len = len,
-			.buf = buf,
-		}
-	};
-
-	if (len > sizeof(buf)) {
-		dev_warn(&priv->client->dev,
-			 "%s: i2c rd reg=%04x: len=%d is too big!\n",
-			 KBUILD_MODNAME, reg, len);
-		return -EINVAL;
-	}
-
-	ret = i2c_transfer(priv->client->adapter, msg, 2);
-	if (ret == 2) {
-		memcpy(val, buf, len);
-		ret = 0;
-	} else {
-		dev_warn(&priv->client->dev,
-				"%s: i2c rd failed=%d reg=%02x len=%d\n",
-				KBUILD_MODNAME, ret, reg, len);
-		ret = -EREMOTEIO;
-	}
-
-	return ret;
-}
-
-/* write single register */
-static int e4000_wr_reg(struct e4000_priv *priv, u8 reg, u8 val)
-{
-	return e4000_wr_regs(priv, reg, &val, 1);
-}
-
-/* read single register */
-static int e4000_rd_reg(struct e4000_priv *priv, u8 reg, u8 *val)
-{
-	return e4000_rd_regs(priv, reg, val, 1);
-}
-
 static int e4000_init(struct dvb_frontend *fe)
 {
 	struct e4000_priv *priv = fe->tuner_priv;
@@ -120,58 +29,58 @@ static int e4000_init(struct dvb_frontend *fe)
 	dev_dbg(&priv->client->dev, "%s:\n", __func__);
 
 	/* dummy I2C to ensure I2C wakes up */
-	ret = e4000_wr_reg(priv, 0x02, 0x40);
+	ret = regmap_write(priv->regmap, 0x02, 0x40);
 
 	/* reset */
-	ret = e4000_wr_reg(priv, 0x00, 0x01);
+	ret = regmap_write(priv->regmap, 0x00, 0x01);
 	if (ret < 0)
 		goto err;
 
 	/* disable output clock */
-	ret = e4000_wr_reg(priv, 0x06, 0x00);
+	ret = regmap_write(priv->regmap, 0x06, 0x00);
 	if (ret < 0)
 		goto err;
 
-	ret = e4000_wr_reg(priv, 0x7a, 0x96);
+	ret = regmap_write(priv->regmap, 0x7a, 0x96);
 	if (ret < 0)
 		goto err;
 
 	/* configure gains */
-	ret = e4000_wr_regs(priv, 0x7e, "\x01\xfe", 2);
+	ret = regmap_bulk_write(priv->regmap, 0x7e, "\x01\xfe", 2);
 	if (ret < 0)
 		goto err;
 
-	ret = e4000_wr_reg(priv, 0x82, 0x00);
+	ret = regmap_write(priv->regmap, 0x82, 0x00);
 	if (ret < 0)
 		goto err;
 
-	ret = e4000_wr_reg(priv, 0x24, 0x05);
+	ret = regmap_write(priv->regmap, 0x24, 0x05);
 	if (ret < 0)
 		goto err;
 
-	ret = e4000_wr_regs(priv, 0x87, "\x20\x01", 2);
+	ret = regmap_bulk_write(priv->regmap, 0x87, "\x20\x01", 2);
 	if (ret < 0)
 		goto err;
 
-	ret = e4000_wr_regs(priv, 0x9f, "\x7f\x07", 2);
+	ret = regmap_bulk_write(priv->regmap, 0x9f, "\x7f\x07", 2);
 	if (ret < 0)
 		goto err;
 
 	/* DC offset control */
-	ret = e4000_wr_reg(priv, 0x2d, 0x1f);
+	ret = regmap_write(priv->regmap, 0x2d, 0x1f);
 	if (ret < 0)
 		goto err;
 
-	ret = e4000_wr_regs(priv, 0x70, "\x01\x01", 2);
+	ret = regmap_bulk_write(priv->regmap, 0x70, "\x01\x01", 2);
 	if (ret < 0)
 		goto err;
 
 	/* gain control */
-	ret = e4000_wr_reg(priv, 0x1a, 0x17);
+	ret = regmap_write(priv->regmap, 0x1a, 0x17);
 	if (ret < 0)
 		goto err;
 
-	ret = e4000_wr_reg(priv, 0x1f, 0x1a);
+	ret = regmap_write(priv->regmap, 0x1f, 0x1a);
 	if (ret < 0)
 		goto err;
 
@@ -192,7 +101,7 @@ static int e4000_sleep(struct dvb_frontend *fe)
 
 	priv->active = false;
 
-	ret = e4000_wr_reg(priv, 0x00, 0x00);
+	ret = regmap_write(priv->regmap, 0x00, 0x00);
 	if (ret < 0)
 		goto err;
 err:
@@ -216,7 +125,7 @@ static int e4000_set_params(struct dvb_frontend *fe)
 			c->bandwidth_hz);
 
 	/* gain control manual */
-	ret = e4000_wr_reg(priv, 0x1a, 0x00);
+	ret = regmap_write(priv->regmap, 0x1a, 0x00);
 	if (ret < 0)
 		goto err;
 
@@ -243,7 +152,7 @@ static int e4000_set_params(struct dvb_frontend *fe)
 			"%s: f_vco=%llu pll div=%d sigma_delta=%04x\n",
 			__func__, f_vco, buf[0], sigma_delta);
 
-	ret = e4000_wr_regs(priv, 0x09, buf, 5);
+	ret = regmap_bulk_write(priv->regmap, 0x09, buf, 5);
 	if (ret < 0)
 		goto err;
 
@@ -258,7 +167,7 @@ static int e4000_set_params(struct dvb_frontend *fe)
 		goto err;
 	}
 
-	ret = e4000_wr_reg(priv, 0x10, e400_lna_filter_lut[i].val);
+	ret = regmap_write(priv->regmap, 0x10, e400_lna_filter_lut[i].val);
 	if (ret < 0)
 		goto err;
 
@@ -276,7 +185,7 @@ static int e4000_set_params(struct dvb_frontend *fe)
 	buf[0] = e4000_if_filter_lut[i].reg11_val;
 	buf[1] = e4000_if_filter_lut[i].reg12_val;
 
-	ret = e4000_wr_regs(priv, 0x11, buf, 2);
+	ret = regmap_bulk_write(priv->regmap, 0x11, buf, 2);
 	if (ret < 0)
 		goto err;
 
@@ -291,33 +200,33 @@ static int e4000_set_params(struct dvb_frontend *fe)
 		goto err;
 	}
 
-	ret = e4000_wr_reg(priv, 0x07, e4000_band_lut[i].reg07_val);
+	ret = regmap_write(priv->regmap, 0x07, e4000_band_lut[i].reg07_val);
 	if (ret < 0)
 		goto err;
 
-	ret = e4000_wr_reg(priv, 0x78, e4000_band_lut[i].reg78_val);
+	ret = regmap_write(priv->regmap, 0x78, e4000_band_lut[i].reg78_val);
 	if (ret < 0)
 		goto err;
 
 	/* DC offset */
 	for (i = 0; i < 4; i++) {
 		if (i == 0)
-			ret = e4000_wr_regs(priv, 0x15, "\x00\x7e\x24", 3);
+			ret = regmap_bulk_write(priv->regmap, 0x15, "\x00\x7e\x24", 3);
 		else if (i == 1)
-			ret = e4000_wr_regs(priv, 0x15, "\x00\x7f", 2);
+			ret = regmap_bulk_write(priv->regmap, 0x15, "\x00\x7f", 2);
 		else if (i == 2)
-			ret = e4000_wr_regs(priv, 0x15, "\x01", 1);
+			ret = regmap_bulk_write(priv->regmap, 0x15, "\x01", 1);
 		else
-			ret = e4000_wr_regs(priv, 0x16, "\x7e", 1);
+			ret = regmap_bulk_write(priv->regmap, 0x16, "\x7e", 1);
 
 		if (ret < 0)
 			goto err;
 
-		ret = e4000_wr_reg(priv, 0x29, 0x01);
+		ret = regmap_write(priv->regmap, 0x29, 0x01);
 		if (ret < 0)
 			goto err;
 
-		ret = e4000_rd_regs(priv, 0x2a, buf, 3);
+		ret = regmap_bulk_read(priv->regmap, 0x2a, buf, 3);
 		if (ret < 0)
 			goto err;
 
@@ -328,16 +237,16 @@ static int e4000_set_params(struct dvb_frontend *fe)
 	swap(q_data[2], q_data[3]);
 	swap(i_data[2], i_data[3]);
 
-	ret = e4000_wr_regs(priv, 0x50, q_data, 4);
+	ret = regmap_bulk_write(priv->regmap, 0x50, q_data, 4);
 	if (ret < 0)
 		goto err;
 
-	ret = e4000_wr_regs(priv, 0x60, i_data, 4);
+	ret = regmap_bulk_write(priv->regmap, 0x60, i_data, 4);
 	if (ret < 0)
 		goto err;
 
 	/* gain control auto */
-	ret = e4000_wr_reg(priv, 0x1a, 0x17);
+	ret = regmap_write(priv->regmap, 0x1a, 0x17);
 	if (ret < 0)
 		goto err;
 err:
@@ -378,12 +287,12 @@ static int e4000_set_lna_gain(struct dvb_frontend *fe)
 	else
 		u8tmp = 0x10;
 
-	ret = e4000_wr_reg(priv, 0x1a, u8tmp);
+	ret = regmap_write(priv->regmap, 0x1a, u8tmp);
 	if (ret)
 		goto err;
 
 	if (priv->lna_gain_auto->val == false) {
-		ret = e4000_wr_reg(priv, 0x14, priv->lna_gain->val);
+		ret = regmap_write(priv->regmap, 0x14, priv->lna_gain->val);
 		if (ret)
 			goto err;
 	}
@@ -410,12 +319,12 @@ static int e4000_set_mixer_gain(struct dvb_frontend *fe)
 	else
 		u8tmp = 0x14;
 
-	ret = e4000_wr_reg(priv, 0x20, u8tmp);
+	ret = regmap_write(priv->regmap, 0x20, u8tmp);
 	if (ret)
 		goto err;
 
 	if (priv->mixer_gain_auto->val == false) {
-		ret = e4000_wr_reg(priv, 0x15, priv->mixer_gain->val);
+		ret = regmap_write(priv->regmap, 0x15, priv->mixer_gain->val);
 		if (ret)
 			goto err;
 	}
@@ -447,14 +356,14 @@ static int e4000_set_if_gain(struct dvb_frontend *fe)
 	else
 		u8tmp = 0x10;
 
-	ret = e4000_wr_reg(priv, 0x1a, u8tmp);
+	ret = regmap_write(priv->regmap, 0x1a, u8tmp);
 	if (ret)
 		goto err;
 
 	if (priv->if_gain_auto->val == false) {
 		buf[0] = e4000_if_gain_lut[priv->if_gain->val].reg16_val;
 		buf[1] = e4000_if_gain_lut[priv->if_gain->val].reg17_val;
-		ret = e4000_wr_regs(priv, 0x16, buf, 2);
+		ret = regmap_bulk_write(priv->regmap, 0x16, buf, 2);
 		if (ret)
 			goto err;
 	}
@@ -469,16 +378,13 @@ static int e4000_pll_lock(struct dvb_frontend *fe)
 {
 	struct e4000_priv *priv = fe->tuner_priv;
 	int ret;
-	u8 u8tmp;
+	unsigned int utmp;
 
-	if (priv->active == false)
-		return 0;
-
-	ret = e4000_rd_reg(priv, 0x07, &u8tmp);
-	if (ret)
+	ret = regmap_read(priv->regmap, 0x07, &utmp);
+	if (ret < 0)
 		goto err;
 
-	priv->pll_lock->val = (u8tmp & 0x01);
+	priv->pll_lock->val = (utmp & 0x01);
 err:
 	if (ret)
 		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
@@ -488,15 +394,19 @@ err:
 
 static int e4000_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct e4000_priv *priv =
-			container_of(ctrl->handler, struct e4000_priv, hdl);
+	struct e4000_priv *priv = container_of(ctrl->handler, struct e4000_priv, hdl);
 	int ret;
 
+	if (priv->active == false)
+		return 0;
+
 	switch (ctrl->id) {
 	case  V4L2_CID_RF_TUNER_PLL_LOCK:
 		ret = e4000_pll_lock(priv->fe);
 		break;
 	default:
+		dev_dbg(&priv->client->dev, "%s: unknown ctrl: id=%d name=%s\n",
+				__func__, ctrl->id, ctrl->name);
 		ret = -EINVAL;
 	}
 
@@ -505,16 +415,13 @@ static int e4000_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
 
 static int e4000_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct e4000_priv *priv =
-			container_of(ctrl->handler, struct e4000_priv, hdl);
+	struct e4000_priv *priv = container_of(ctrl->handler, struct e4000_priv, hdl);
 	struct dvb_frontend *fe = priv->fe;
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 	int ret;
 
-	dev_dbg(&priv->client->dev,
-			"%s: id=%d name=%s val=%d min=%d max=%d step=%d\n",
-			__func__, ctrl->id, ctrl->name, ctrl->val,
-			ctrl->minimum, ctrl->maximum, ctrl->step);
+	if (priv->active == false)
+		return 0;
 
 	switch (ctrl->id) {
 	case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:
@@ -535,6 +442,8 @@ static int e4000_s_ctrl(struct v4l2_ctrl *ctrl)
 		ret = e4000_set_if_gain(priv->fe);
 		break;
 	default:
+		dev_dbg(&priv->client->dev, "%s: unknown ctrl: id=%d name=%s\n",
+				__func__, ctrl->id, ctrl->name);
 		ret = -EINVAL;
 	}
 
@@ -574,7 +483,12 @@ static int e4000_probe(struct i2c_client *client,
 	struct dvb_frontend *fe = cfg->fe;
 	struct e4000_priv *priv;
 	int ret;
-	u8 chip_id;
+	unsigned int utmp;
+	static const struct regmap_config regmap_config = {
+		.reg_bits = 8,
+		.val_bits = 8,
+		.max_register = 0xff,
+	};
 
 	priv = kzalloc(sizeof(struct e4000_priv), GFP_KERNEL);
 	if (!priv) {
@@ -586,22 +500,26 @@ static int e4000_probe(struct i2c_client *client,
 	priv->clock = cfg->clock;
 	priv->client = client;
 	priv->fe = cfg->fe;
+	priv->regmap = devm_regmap_init_i2c(client, &regmap_config);
+	if (IS_ERR(priv->regmap)) {
+		ret = PTR_ERR(priv->regmap);
+		goto err;
+	}
 
 	/* check if the tuner is there */
-	ret = e4000_rd_reg(priv, 0x02, &chip_id);
+	ret = regmap_read(priv->regmap, 0x02, &utmp);
 	if (ret < 0)
 		goto err;
 
-	dev_dbg(&priv->client->dev,
-			"%s: chip_id=%02x\n", __func__, chip_id);
+	dev_dbg(&priv->client->dev, "%s: chip id=%02x\n", __func__, utmp);
 
-	if (chip_id != 0x40) {
+	if (utmp != 0x40) {
 		ret = -ENODEV;
 		goto err;
 	}
 
 	/* put sleep as chip seems to be in normal mode by default */
-	ret = e4000_wr_reg(priv, 0x00, 0x00);
+	ret = regmap_write(priv->regmap, 0x00, 0x00);
 	if (ret < 0)
 		goto err;
 
diff --git a/drivers/media/tuners/e4000_priv.h b/drivers/media/tuners/e4000_priv.h
index d41dbcc..40ba176 100644
--- a/drivers/media/tuners/e4000_priv.h
+++ b/drivers/media/tuners/e4000_priv.h
@@ -23,9 +23,11 @@
 
 #include "e4000.h"
 #include <media/v4l2-ctrls.h>
+#include <linux/regmap.h>
 
 struct e4000_priv {
 	struct i2c_client *client;
+	struct regmap *regmap;
 	u32 clock;
 	struct dvb_frontend *fe;
 	bool active;
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [REVIEW PATCH 07/16] e4000: rename some variables
  2014-02-27  0:30 [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver Antti Palosaari
                   ` (5 preceding siblings ...)
  2014-02-27  0:30 ` [REVIEW PATCH 06/16] e4000: convert to Regmap API Antti Palosaari
@ 2014-02-27  0:30 ` Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 08/16] rtl2832_sdr: Realtek RTL2832 SDR driver module Antti Palosaari
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:30 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Antti Palosaari

Rename some variables.
Change error status checks from (ret < 0) to (ret).
No actual functionality changes.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/tuners/e4000.c      | 332 +++++++++++++++++++-------------------
 drivers/media/tuners/e4000_priv.h |   2 +-
 2 files changed, 167 insertions(+), 167 deletions(-)

diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c
index 3ac0254..ceed604 100644
--- a/drivers/media/tuners/e4000.c
+++ b/drivers/media/tuners/e4000.c
@@ -23,110 +23,110 @@
 
 static int e4000_init(struct dvb_frontend *fe)
 {
-	struct e4000_priv *priv = fe->tuner_priv;
+	struct e4000 *s = fe->tuner_priv;
 	int ret;
 
-	dev_dbg(&priv->client->dev, "%s:\n", __func__);
+	dev_dbg(&s->client->dev, "%s:\n", __func__);
 
 	/* dummy I2C to ensure I2C wakes up */
-	ret = regmap_write(priv->regmap, 0x02, 0x40);
+	ret = regmap_write(s->regmap, 0x02, 0x40);
 
 	/* reset */
-	ret = regmap_write(priv->regmap, 0x00, 0x01);
-	if (ret < 0)
+	ret = regmap_write(s->regmap, 0x00, 0x01);
+	if (ret)
 		goto err;
 
 	/* disable output clock */
-	ret = regmap_write(priv->regmap, 0x06, 0x00);
-	if (ret < 0)
+	ret = regmap_write(s->regmap, 0x06, 0x00);
+	if (ret)
 		goto err;
 
-	ret = regmap_write(priv->regmap, 0x7a, 0x96);
-	if (ret < 0)
+	ret = regmap_write(s->regmap, 0x7a, 0x96);
+	if (ret)
 		goto err;
 
 	/* configure gains */
-	ret = regmap_bulk_write(priv->regmap, 0x7e, "\x01\xfe", 2);
-	if (ret < 0)
+	ret = regmap_bulk_write(s->regmap, 0x7e, "\x01\xfe", 2);
+	if (ret)
 		goto err;
 
-	ret = regmap_write(priv->regmap, 0x82, 0x00);
-	if (ret < 0)
+	ret = regmap_write(s->regmap, 0x82, 0x00);
+	if (ret)
 		goto err;
 
-	ret = regmap_write(priv->regmap, 0x24, 0x05);
-	if (ret < 0)
+	ret = regmap_write(s->regmap, 0x24, 0x05);
+	if (ret)
 		goto err;
 
-	ret = regmap_bulk_write(priv->regmap, 0x87, "\x20\x01", 2);
-	if (ret < 0)
+	ret = regmap_bulk_write(s->regmap, 0x87, "\x20\x01", 2);
+	if (ret)
 		goto err;
 
-	ret = regmap_bulk_write(priv->regmap, 0x9f, "\x7f\x07", 2);
-	if (ret < 0)
+	ret = regmap_bulk_write(s->regmap, 0x9f, "\x7f\x07", 2);
+	if (ret)
 		goto err;
 
 	/* DC offset control */
-	ret = regmap_write(priv->regmap, 0x2d, 0x1f);
-	if (ret < 0)
+	ret = regmap_write(s->regmap, 0x2d, 0x1f);
+	if (ret)
 		goto err;
 
-	ret = regmap_bulk_write(priv->regmap, 0x70, "\x01\x01", 2);
-	if (ret < 0)
+	ret = regmap_bulk_write(s->regmap, 0x70, "\x01\x01", 2);
+	if (ret)
 		goto err;
 
 	/* gain control */
-	ret = regmap_write(priv->regmap, 0x1a, 0x17);
-	if (ret < 0)
+	ret = regmap_write(s->regmap, 0x1a, 0x17);
+	if (ret)
 		goto err;
 
-	ret = regmap_write(priv->regmap, 0x1f, 0x1a);
-	if (ret < 0)
+	ret = regmap_write(s->regmap, 0x1f, 0x1a);
+	if (ret)
 		goto err;
 
-	priv->active = true;
+	s->active = true;
 err:
 	if (ret)
-		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
+		dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
 
 	return ret;
 }
 
 static int e4000_sleep(struct dvb_frontend *fe)
 {
-	struct e4000_priv *priv = fe->tuner_priv;
+	struct e4000 *s = fe->tuner_priv;
 	int ret;
 
-	dev_dbg(&priv->client->dev, "%s:\n", __func__);
+	dev_dbg(&s->client->dev, "%s:\n", __func__);
 
-	priv->active = false;
+	s->active = false;
 
-	ret = regmap_write(priv->regmap, 0x00, 0x00);
-	if (ret < 0)
+	ret = regmap_write(s->regmap, 0x00, 0x00);
+	if (ret)
 		goto err;
 err:
 	if (ret)
-		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
+		dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
 
 	return ret;
 }
 
 static int e4000_set_params(struct dvb_frontend *fe)
 {
-	struct e4000_priv *priv = fe->tuner_priv;
+	struct e4000 *s = fe->tuner_priv;
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 	int ret, i, sigma_delta;
 	u64 f_vco;
 	u8 buf[5], i_data[4], q_data[4];
 
-	dev_dbg(&priv->client->dev,
+	dev_dbg(&s->client->dev,
 			"%s: delivery_system=%d frequency=%u bandwidth_hz=%u\n",
 			__func__, c->delivery_system, c->frequency,
 			c->bandwidth_hz);
 
 	/* gain control manual */
-	ret = regmap_write(priv->regmap, 0x1a, 0x00);
-	if (ret < 0)
+	ret = regmap_write(s->regmap, 0x1a, 0x00);
+	if (ret)
 		goto err;
 
 	/* PLL */
@@ -141,19 +141,19 @@ static int e4000_set_params(struct dvb_frontend *fe)
 	}
 
 	f_vco = 1ull * c->frequency * e4000_pll_lut[i].mul;
-	sigma_delta = div_u64(0x10000ULL * (f_vco % priv->clock), priv->clock);
-	buf[0] = div_u64(f_vco, priv->clock);
+	sigma_delta = div_u64(0x10000ULL * (f_vco % s->clock), s->clock);
+	buf[0] = div_u64(f_vco, s->clock);
 	buf[1] = (sigma_delta >> 0) & 0xff;
 	buf[2] = (sigma_delta >> 8) & 0xff;
 	buf[3] = 0x00;
 	buf[4] = e4000_pll_lut[i].div;
 
-	dev_dbg(&priv->client->dev,
+	dev_dbg(&s->client->dev,
 			"%s: f_vco=%llu pll div=%d sigma_delta=%04x\n",
 			__func__, f_vco, buf[0], sigma_delta);
 
-	ret = regmap_bulk_write(priv->regmap, 0x09, buf, 5);
-	if (ret < 0)
+	ret = regmap_bulk_write(s->regmap, 0x09, buf, 5);
+	if (ret)
 		goto err;
 
 	/* LNA filter (RF filter) */
@@ -167,8 +167,8 @@ static int e4000_set_params(struct dvb_frontend *fe)
 		goto err;
 	}
 
-	ret = regmap_write(priv->regmap, 0x10, e400_lna_filter_lut[i].val);
-	if (ret < 0)
+	ret = regmap_write(s->regmap, 0x10, e400_lna_filter_lut[i].val);
+	if (ret)
 		goto err;
 
 	/* IF filters */
@@ -185,8 +185,8 @@ static int e4000_set_params(struct dvb_frontend *fe)
 	buf[0] = e4000_if_filter_lut[i].reg11_val;
 	buf[1] = e4000_if_filter_lut[i].reg12_val;
 
-	ret = regmap_bulk_write(priv->regmap, 0x11, buf, 2);
-	if (ret < 0)
+	ret = regmap_bulk_write(s->regmap, 0x11, buf, 2);
+	if (ret)
 		goto err;
 
 	/* frequency band */
@@ -200,34 +200,34 @@ static int e4000_set_params(struct dvb_frontend *fe)
 		goto err;
 	}
 
-	ret = regmap_write(priv->regmap, 0x07, e4000_band_lut[i].reg07_val);
-	if (ret < 0)
+	ret = regmap_write(s->regmap, 0x07, e4000_band_lut[i].reg07_val);
+	if (ret)
 		goto err;
 
-	ret = regmap_write(priv->regmap, 0x78, e4000_band_lut[i].reg78_val);
-	if (ret < 0)
+	ret = regmap_write(s->regmap, 0x78, e4000_band_lut[i].reg78_val);
+	if (ret)
 		goto err;
 
 	/* DC offset */
 	for (i = 0; i < 4; i++) {
 		if (i == 0)
-			ret = regmap_bulk_write(priv->regmap, 0x15, "\x00\x7e\x24", 3);
+			ret = regmap_bulk_write(s->regmap, 0x15, "\x00\x7e\x24", 3);
 		else if (i == 1)
-			ret = regmap_bulk_write(priv->regmap, 0x15, "\x00\x7f", 2);
+			ret = regmap_bulk_write(s->regmap, 0x15, "\x00\x7f", 2);
 		else if (i == 2)
-			ret = regmap_bulk_write(priv->regmap, 0x15, "\x01", 1);
+			ret = regmap_bulk_write(s->regmap, 0x15, "\x01", 1);
 		else
-			ret = regmap_bulk_write(priv->regmap, 0x16, "\x7e", 1);
+			ret = regmap_bulk_write(s->regmap, 0x16, "\x7e", 1);
 
-		if (ret < 0)
+		if (ret)
 			goto err;
 
-		ret = regmap_write(priv->regmap, 0x29, 0x01);
-		if (ret < 0)
+		ret = regmap_write(s->regmap, 0x29, 0x01);
+		if (ret)
 			goto err;
 
-		ret = regmap_bulk_read(priv->regmap, 0x2a, buf, 3);
-		if (ret < 0)
+		ret = regmap_bulk_read(s->regmap, 0x2a, buf, 3);
+		if (ret)
 			goto err;
 
 		i_data[i] = (((buf[2] >> 0) & 0x3) << 6) | (buf[0] & 0x3f);
@@ -237,30 +237,30 @@ static int e4000_set_params(struct dvb_frontend *fe)
 	swap(q_data[2], q_data[3]);
 	swap(i_data[2], i_data[3]);
 
-	ret = regmap_bulk_write(priv->regmap, 0x50, q_data, 4);
-	if (ret < 0)
+	ret = regmap_bulk_write(s->regmap, 0x50, q_data, 4);
+	if (ret)
 		goto err;
 
-	ret = regmap_bulk_write(priv->regmap, 0x60, i_data, 4);
-	if (ret < 0)
+	ret = regmap_bulk_write(s->regmap, 0x60, i_data, 4);
+	if (ret)
 		goto err;
 
 	/* gain control auto */
-	ret = regmap_write(priv->regmap, 0x1a, 0x17);
-	if (ret < 0)
+	ret = regmap_write(s->regmap, 0x1a, 0x17);
+	if (ret)
 		goto err;
 err:
 	if (ret)
-		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
+		dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
 
 	return ret;
 }
 
 static int e4000_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
 {
-	struct e4000_priv *priv = fe->tuner_priv;
+	struct e4000 *s = fe->tuner_priv;
 
-	dev_dbg(&priv->client->dev, "%s:\n", __func__);
+	dev_dbg(&s->client->dev, "%s:\n", __func__);
 
 	*frequency = 0; /* Zero-IF */
 
@@ -269,143 +269,143 @@ static int e4000_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
 
 static int e4000_set_lna_gain(struct dvb_frontend *fe)
 {
-	struct e4000_priv *priv = fe->tuner_priv;
+	struct e4000 *s = fe->tuner_priv;
 	int ret;
 	u8 u8tmp;
 
-	dev_dbg(&priv->client->dev, "%s: lna auto=%d->%d val=%d->%d\n",
-			__func__, priv->lna_gain_auto->cur.val,
-			priv->lna_gain_auto->val, priv->lna_gain->cur.val,
-			priv->lna_gain->val);
+	dev_dbg(&s->client->dev, "%s: lna auto=%d->%d val=%d->%d\n",
+			__func__, s->lna_gain_auto->cur.val,
+			s->lna_gain_auto->val, s->lna_gain->cur.val,
+			s->lna_gain->val);
 
-	if (priv->lna_gain_auto->val && priv->if_gain_auto->cur.val)
+	if (s->lna_gain_auto->val && s->if_gain_auto->cur.val)
 		u8tmp = 0x17;
-	else if (priv->lna_gain_auto->val)
+	else if (s->lna_gain_auto->val)
 		u8tmp = 0x19;
-	else if (priv->if_gain_auto->cur.val)
+	else if (s->if_gain_auto->cur.val)
 		u8tmp = 0x16;
 	else
 		u8tmp = 0x10;
 
-	ret = regmap_write(priv->regmap, 0x1a, u8tmp);
+	ret = regmap_write(s->regmap, 0x1a, u8tmp);
 	if (ret)
 		goto err;
 
-	if (priv->lna_gain_auto->val == false) {
-		ret = regmap_write(priv->regmap, 0x14, priv->lna_gain->val);
+	if (s->lna_gain_auto->val == false) {
+		ret = regmap_write(s->regmap, 0x14, s->lna_gain->val);
 		if (ret)
 			goto err;
 	}
 err:
 	if (ret)
-		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
+		dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
 
 	return ret;
 }
 
 static int e4000_set_mixer_gain(struct dvb_frontend *fe)
 {
-	struct e4000_priv *priv = fe->tuner_priv;
+	struct e4000 *s = fe->tuner_priv;
 	int ret;
 	u8 u8tmp;
 
-	dev_dbg(&priv->client->dev, "%s: mixer auto=%d->%d val=%d->%d\n",
-			__func__, priv->mixer_gain_auto->cur.val,
-			priv->mixer_gain_auto->val, priv->mixer_gain->cur.val,
-			priv->mixer_gain->val);
+	dev_dbg(&s->client->dev, "%s: mixer auto=%d->%d val=%d->%d\n",
+			__func__, s->mixer_gain_auto->cur.val,
+			s->mixer_gain_auto->val, s->mixer_gain->cur.val,
+			s->mixer_gain->val);
 
-	if (priv->mixer_gain_auto->val)
+	if (s->mixer_gain_auto->val)
 		u8tmp = 0x15;
 	else
 		u8tmp = 0x14;
 
-	ret = regmap_write(priv->regmap, 0x20, u8tmp);
+	ret = regmap_write(s->regmap, 0x20, u8tmp);
 	if (ret)
 		goto err;
 
-	if (priv->mixer_gain_auto->val == false) {
-		ret = regmap_write(priv->regmap, 0x15, priv->mixer_gain->val);
+	if (s->mixer_gain_auto->val == false) {
+		ret = regmap_write(s->regmap, 0x15, s->mixer_gain->val);
 		if (ret)
 			goto err;
 	}
 err:
 	if (ret)
-		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
+		dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
 
 	return ret;
 }
 
 static int e4000_set_if_gain(struct dvb_frontend *fe)
 {
-	struct e4000_priv *priv = fe->tuner_priv;
+	struct e4000 *s = fe->tuner_priv;
 	int ret;
 	u8 buf[2];
 	u8 u8tmp;
 
-	dev_dbg(&priv->client->dev, "%s: if auto=%d->%d val=%d->%d\n",
-			__func__, priv->if_gain_auto->cur.val,
-			priv->if_gain_auto->val, priv->if_gain->cur.val,
-			priv->if_gain->val);
+	dev_dbg(&s->client->dev, "%s: if auto=%d->%d val=%d->%d\n",
+			__func__, s->if_gain_auto->cur.val,
+			s->if_gain_auto->val, s->if_gain->cur.val,
+			s->if_gain->val);
 
-	if (priv->if_gain_auto->val && priv->lna_gain_auto->cur.val)
+	if (s->if_gain_auto->val && s->lna_gain_auto->cur.val)
 		u8tmp = 0x17;
-	else if (priv->lna_gain_auto->cur.val)
+	else if (s->lna_gain_auto->cur.val)
 		u8tmp = 0x19;
-	else if (priv->if_gain_auto->val)
+	else if (s->if_gain_auto->val)
 		u8tmp = 0x16;
 	else
 		u8tmp = 0x10;
 
-	ret = regmap_write(priv->regmap, 0x1a, u8tmp);
+	ret = regmap_write(s->regmap, 0x1a, u8tmp);
 	if (ret)
 		goto err;
 
-	if (priv->if_gain_auto->val == false) {
-		buf[0] = e4000_if_gain_lut[priv->if_gain->val].reg16_val;
-		buf[1] = e4000_if_gain_lut[priv->if_gain->val].reg17_val;
-		ret = regmap_bulk_write(priv->regmap, 0x16, buf, 2);
+	if (s->if_gain_auto->val == false) {
+		buf[0] = e4000_if_gain_lut[s->if_gain->val].reg16_val;
+		buf[1] = e4000_if_gain_lut[s->if_gain->val].reg17_val;
+		ret = regmap_bulk_write(s->regmap, 0x16, buf, 2);
 		if (ret)
 			goto err;
 	}
 err:
 	if (ret)
-		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
+		dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
 
 	return ret;
 }
 
 static int e4000_pll_lock(struct dvb_frontend *fe)
 {
-	struct e4000_priv *priv = fe->tuner_priv;
+	struct e4000 *s = fe->tuner_priv;
 	int ret;
 	unsigned int utmp;
 
-	ret = regmap_read(priv->regmap, 0x07, &utmp);
-	if (ret < 0)
+	ret = regmap_read(s->regmap, 0x07, &utmp);
+	if (ret)
 		goto err;
 
-	priv->pll_lock->val = (utmp & 0x01);
+	s->pll_lock->val = (utmp & 0x01);
 err:
 	if (ret)
-		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret);
+		dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret);
 
 	return ret;
 }
 
 static int e4000_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct e4000_priv *priv = container_of(ctrl->handler, struct e4000_priv, hdl);
+	struct e4000 *s = container_of(ctrl->handler, struct e4000, hdl);
 	int ret;
 
-	if (priv->active == false)
+	if (s->active == false)
 		return 0;
 
 	switch (ctrl->id) {
 	case  V4L2_CID_RF_TUNER_PLL_LOCK:
-		ret = e4000_pll_lock(priv->fe);
+		ret = e4000_pll_lock(s->fe);
 		break;
 	default:
-		dev_dbg(&priv->client->dev, "%s: unknown ctrl: id=%d name=%s\n",
+		dev_dbg(&s->client->dev, "%s: unknown ctrl: id=%d name=%s\n",
 				__func__, ctrl->id, ctrl->name);
 		ret = -EINVAL;
 	}
@@ -415,34 +415,34 @@ static int e4000_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
 
 static int e4000_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct e4000_priv *priv = container_of(ctrl->handler, struct e4000_priv, hdl);
-	struct dvb_frontend *fe = priv->fe;
+	struct e4000 *s = container_of(ctrl->handler, struct e4000, hdl);
+	struct dvb_frontend *fe = s->fe;
 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 	int ret;
 
-	if (priv->active == false)
+	if (s->active == false)
 		return 0;
 
 	switch (ctrl->id) {
 	case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:
 	case V4L2_CID_RF_TUNER_BANDWIDTH:
-		c->bandwidth_hz = priv->bandwidth->val;
-		ret = e4000_set_params(priv->fe);
+		c->bandwidth_hz = s->bandwidth->val;
+		ret = e4000_set_params(s->fe);
 		break;
 	case  V4L2_CID_RF_TUNER_LNA_GAIN_AUTO:
 	case  V4L2_CID_RF_TUNER_LNA_GAIN:
-		ret = e4000_set_lna_gain(priv->fe);
+		ret = e4000_set_lna_gain(s->fe);
 		break;
 	case  V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO:
 	case  V4L2_CID_RF_TUNER_MIXER_GAIN:
-		ret = e4000_set_mixer_gain(priv->fe);
+		ret = e4000_set_mixer_gain(s->fe);
 		break;
 	case  V4L2_CID_RF_TUNER_IF_GAIN_AUTO:
 	case  V4L2_CID_RF_TUNER_IF_GAIN:
-		ret = e4000_set_if_gain(priv->fe);
+		ret = e4000_set_if_gain(s->fe);
 		break;
 	default:
-		dev_dbg(&priv->client->dev, "%s: unknown ctrl: id=%d name=%s\n",
+		dev_dbg(&s->client->dev, "%s: unknown ctrl: id=%d name=%s\n",
 				__func__, ctrl->id, ctrl->name);
 		ret = -EINVAL;
 	}
@@ -471,8 +471,8 @@ static const struct dvb_tuner_ops e4000_tuner_ops = {
 
 struct v4l2_ctrl_handler *e4000_get_ctrl_handler(struct dvb_frontend *fe)
 {
-	struct e4000_priv *priv = fe->tuner_priv;
-	return &priv->hdl;
+	struct e4000 *s = fe->tuner_priv;
+	return &s->hdl;
 }
 EXPORT_SYMBOL(e4000_get_ctrl_handler);
 
@@ -481,7 +481,7 @@ static int e4000_probe(struct i2c_client *client,
 {
 	struct e4000_config *cfg = client->dev.platform_data;
 	struct dvb_frontend *fe = cfg->fe;
-	struct e4000_priv *priv;
+	struct e4000 *s;
 	int ret;
 	unsigned int utmp;
 	static const struct regmap_config regmap_config = {
@@ -490,28 +490,28 @@ static int e4000_probe(struct i2c_client *client,
 		.max_register = 0xff,
 	};
 
-	priv = kzalloc(sizeof(struct e4000_priv), GFP_KERNEL);
-	if (!priv) {
+	s = kzalloc(sizeof(struct e4000), GFP_KERNEL);
+	if (!s) {
 		ret = -ENOMEM;
 		dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
 		goto err;
 	}
 
-	priv->clock = cfg->clock;
-	priv->client = client;
-	priv->fe = cfg->fe;
-	priv->regmap = devm_regmap_init_i2c(client, &regmap_config);
-	if (IS_ERR(priv->regmap)) {
-		ret = PTR_ERR(priv->regmap);
+	s->clock = cfg->clock;
+	s->client = client;
+	s->fe = cfg->fe;
+	s->regmap = devm_regmap_init_i2c(client, &regmap_config);
+	if (IS_ERR(s->regmap)) {
+		ret = PTR_ERR(s->regmap);
 		goto err;
 	}
 
 	/* check if the tuner is there */
-	ret = regmap_read(priv->regmap, 0x02, &utmp);
-	if (ret < 0)
+	ret = regmap_read(s->regmap, 0x02, &utmp);
+	if (ret)
 		goto err;
 
-	dev_dbg(&priv->client->dev, "%s: chip id=%02x\n", __func__, utmp);
+	dev_dbg(&s->client->dev, "%s: chip id=%02x\n", __func__, utmp);
 
 	if (utmp != 0x40) {
 		ret = -ENODEV;
@@ -519,53 +519,53 @@ static int e4000_probe(struct i2c_client *client,
 	}
 
 	/* put sleep as chip seems to be in normal mode by default */
-	ret = regmap_write(priv->regmap, 0x00, 0x00);
-	if (ret < 0)
+	ret = regmap_write(s->regmap, 0x00, 0x00);
+	if (ret)
 		goto err;
 
 	/* Register controls */
-	v4l2_ctrl_handler_init(&priv->hdl, 9);
-	priv->bandwidth_auto = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+	v4l2_ctrl_handler_init(&s->hdl, 9);
+	s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
 			V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1);
-	priv->bandwidth = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+	s->bandwidth = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
 			V4L2_CID_RF_TUNER_BANDWIDTH, 4300000, 11000000, 100000, 4300000);
-	v4l2_ctrl_auto_cluster(2, &priv->bandwidth_auto, 0, false);
-	priv->lna_gain_auto = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+	v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
+	s->lna_gain_auto = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
 			V4L2_CID_RF_TUNER_LNA_GAIN_AUTO, 0, 1, 1, 1);
-	priv->lna_gain = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+	s->lna_gain = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
 			V4L2_CID_RF_TUNER_LNA_GAIN, 0, 15, 1, 10);
-	v4l2_ctrl_auto_cluster(2, &priv->lna_gain_auto, 0, false);
-	priv->mixer_gain_auto = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+	v4l2_ctrl_auto_cluster(2, &s->lna_gain_auto, 0, false);
+	s->mixer_gain_auto = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
 			V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO, 0, 1, 1, 1);
-	priv->mixer_gain = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+	s->mixer_gain = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
 			V4L2_CID_RF_TUNER_MIXER_GAIN, 0, 1, 1, 1);
-	v4l2_ctrl_auto_cluster(2, &priv->mixer_gain_auto, 0, false);
-	priv->if_gain_auto = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+	v4l2_ctrl_auto_cluster(2, &s->mixer_gain_auto, 0, false);
+	s->if_gain_auto = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
 			V4L2_CID_RF_TUNER_IF_GAIN_AUTO, 0, 1, 1, 1);
-	priv->if_gain = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+	s->if_gain = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
 			V4L2_CID_RF_TUNER_IF_GAIN, 0, 54, 1, 0);
-	v4l2_ctrl_auto_cluster(2, &priv->if_gain_auto, 0, false);
-	priv->pll_lock = v4l2_ctrl_new_std(&priv->hdl, &e4000_ctrl_ops,
+	v4l2_ctrl_auto_cluster(2, &s->if_gain_auto, 0, false);
+	s->pll_lock = v4l2_ctrl_new_std(&s->hdl, &e4000_ctrl_ops,
 			V4L2_CID_RF_TUNER_PLL_LOCK,  0, 1, 1, 0);
-	if (priv->hdl.error) {
-		ret = priv->hdl.error;
-		dev_err(&priv->client->dev, "Could not initialize controls\n");
-		v4l2_ctrl_handler_free(&priv->hdl);
+	if (s->hdl.error) {
+		ret = s->hdl.error;
+		dev_err(&s->client->dev, "Could not initialize controls\n");
+		v4l2_ctrl_handler_free(&s->hdl);
 		goto err;
 	}
 
-	dev_info(&priv->client->dev,
+	dev_info(&s->client->dev,
 			"%s: Elonics E4000 successfully identified\n",
 			KBUILD_MODNAME);
 
-	fe->tuner_priv = priv;
+	fe->tuner_priv = s;
 	memcpy(&fe->ops.tuner_ops, &e4000_tuner_ops,
 			sizeof(struct dvb_tuner_ops));
-	i2c_set_clientdata(client, priv);
+	i2c_set_clientdata(client, s);
 err:
 	if (ret) {
 		dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret);
-		kfree(priv);
+		kfree(s);
 	}
 
 	return ret;
@@ -573,15 +573,15 @@ err:
 
 static int e4000_remove(struct i2c_client *client)
 {
-	struct e4000_priv *priv = i2c_get_clientdata(client);
-	struct dvb_frontend *fe = priv->fe;
+	struct e4000 *s = i2c_get_clientdata(client);
+	struct dvb_frontend *fe = s->fe;
 
 	dev_dbg(&client->dev, "%s:\n", __func__);
 
-	v4l2_ctrl_handler_free(&priv->hdl);
+	v4l2_ctrl_handler_free(&s->hdl);
 	memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
 	fe->tuner_priv = NULL;
-	kfree(priv);
+	kfree(s);
 
 	return 0;
 }
diff --git a/drivers/media/tuners/e4000_priv.h b/drivers/media/tuners/e4000_priv.h
index 40ba176..ee36de1 100644
--- a/drivers/media/tuners/e4000_priv.h
+++ b/drivers/media/tuners/e4000_priv.h
@@ -25,7 +25,7 @@
 #include <media/v4l2-ctrls.h>
 #include <linux/regmap.h>
 
-struct e4000_priv {
+struct e4000 {
 	struct i2c_client *client;
 	struct regmap *regmap;
 	u32 clock;
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [REVIEW PATCH 08/16] rtl2832_sdr: Realtek RTL2832 SDR driver module
  2014-02-27  0:30 [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver Antti Palosaari
                   ` (6 preceding siblings ...)
  2014-02-27  0:30 ` [REVIEW PATCH 07/16] e4000: rename some variables Antti Palosaari
@ 2014-02-27  0:30 ` Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 09/16] rtl28xxu: constify demod config structs Antti Palosaari
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:30 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Antti Palosaari

Implement SDR driver for Realtek RTL2832U chip as a DVB extension
module. SDR module is attached by DVB USB RTL28XXU driver as a DVB
SEC (satellite equipment controller) module. Abusing unused SEC here
has no harm as that is DVB-T only frontend.

SDR functionality is provided by RTL2832 DVB-T demodulator. I suspect
it is originally planned for DAB and FM, but it could be abused general
SDR, due to modern silicon tuners that has wide frequency range and a
lot of configurable parameters (filters, gains, ...).

http://thread.gmane.org/gmane.linux.drivers.video-input-infrastructure/44461

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/staging/media/Kconfig                    |    2 +
 drivers/staging/media/Makefile                   |    2 +
 drivers/staging/media/rtl2832u_sdr/Kconfig       |    7 +
 drivers/staging/media/rtl2832u_sdr/Makefile      |    6 +
 drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c | 1470 ++++++++++++++++++++++
 drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h |   51 +
 6 files changed, 1538 insertions(+)
 create mode 100644 drivers/staging/media/rtl2832u_sdr/Kconfig
 create mode 100644 drivers/staging/media/rtl2832u_sdr/Makefile
 create mode 100644 drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
 create mode 100644 drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h

diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index 22b0c9d..a9f2e63 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -41,6 +41,8 @@ source "drivers/staging/media/solo6x10/Kconfig"
 
 source "drivers/staging/media/omap4iss/Kconfig"
 
+source "drivers/staging/media/rtl2832u_sdr/Kconfig"
+
 # Keep LIRC at the end, as it has sub-menus
 source "drivers/staging/media/lirc/Kconfig"
 
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index bedc62a..8e2c5d2 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -11,3 +11,5 @@ obj-$(CONFIG_VIDEO_OMAP4)	+= omap4iss/
 obj-$(CONFIG_USB_SN9C102)       += sn9c102/
 obj-$(CONFIG_VIDEO_OMAP2)       += omap24xx/
 obj-$(CONFIG_VIDEO_TCM825X)     += omap24xx/
+obj-$(CONFIG_DVB_RTL2832_SDR)	+= rtl2832u_sdr/
+
diff --git a/drivers/staging/media/rtl2832u_sdr/Kconfig b/drivers/staging/media/rtl2832u_sdr/Kconfig
new file mode 100644
index 0000000..3ede5fe
--- /dev/null
+++ b/drivers/staging/media/rtl2832u_sdr/Kconfig
@@ -0,0 +1,7 @@
+config DVB_RTL2832_SDR
+	tristate "Realtek RTL2832 SDR"
+	depends on USB && DVB_CORE && I2C && VIDEO_V4L2 && DVB_USB_RTL28XXU
+	select DVB_RTL2832
+	select VIDEOBUF2_VMALLOC
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+
diff --git a/drivers/staging/media/rtl2832u_sdr/Makefile b/drivers/staging/media/rtl2832u_sdr/Makefile
new file mode 100644
index 0000000..7e00a0d
--- /dev/null
+++ b/drivers/staging/media/rtl2832u_sdr/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832_sdr.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+ccflags-y += -Idrivers/media/tuners
+ccflags-y += -Idrivers/media/usb/dvb-usb-v2
diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
new file mode 100644
index 0000000..dea7743
--- /dev/null
+++ b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
@@ -0,0 +1,1470 @@
+/*
+ * Realtek RTL2832U SDR driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ *    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; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License along
+ *    with this program; if not, write to the Free Software Foundation, Inc.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * GNU Radio plugin "gr-kernel" for device usage will be on:
+ * http://git.linuxtv.org/anttip/gr-kernel.git
+ *
+ */
+
+#include "dvb_frontend.h"
+#include "rtl2832_sdr.h"
+#include "dvb_usb.h"
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include <linux/jiffies.h>
+#include <linux/math64.h>
+
+#define MAX_BULK_BUFS            (10)
+#define BULK_BUFFER_SIZE         (128 * 512)
+
+static const struct v4l2_frequency_band bands_adc[] = {
+	{
+		.tuner = 0,
+		.type = V4L2_TUNER_ADC,
+		.index = 0,
+		.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+		.rangelow   =  300000,
+		.rangehigh  =  300000,
+	},
+	{
+		.tuner = 0,
+		.type = V4L2_TUNER_ADC,
+		.index = 1,
+		.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+		.rangelow   =  900001,
+		.rangehigh  = 2800000,
+	},
+	{
+		.tuner = 0,
+		.type = V4L2_TUNER_ADC,
+		.index = 2,
+		.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+		.rangelow   = 3200000,
+		.rangehigh  = 3200000,
+	},
+};
+
+static const struct v4l2_frequency_band bands_fm[] = {
+	{
+		.tuner = 1,
+		.type = V4L2_TUNER_RF,
+		.index = 0,
+		.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+		.rangelow   =    50000000,
+		.rangehigh  =  2000000000,
+	},
+};
+
+/* stream formats */
+struct rtl2832_sdr_format {
+	char	*name;
+	u32	pixelformat;
+};
+
+static struct rtl2832_sdr_format formats[] = {
+	{
+		.name		= "IQ U8",
+		.pixelformat	=  V4L2_SDR_FMT_CU8,
+	}, {
+		.name		= "IQ U16LE (emulated)",
+		.pixelformat	= V4L2_SDR_FMT_CU16LE,
+	},
+};
+
+static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats);
+
+/* intermediate buffers with raw data from the USB device */
+struct rtl2832_sdr_frame_buf {
+	struct vb2_buffer vb;   /* common v4l buffer stuff -- must be first */
+	struct list_head list;
+};
+
+struct rtl2832_sdr_state {
+#define POWER_ON           (1 << 1)
+#define URB_BUF            (1 << 2)
+	unsigned long flags;
+
+	const struct rtl2832_config *cfg;
+	struct dvb_frontend *fe;
+	struct dvb_usb_device *d;
+	struct i2c_adapter *i2c;
+	u8 bank;
+
+	struct video_device vdev;
+	struct v4l2_device v4l2_dev;
+
+	/* videobuf2 queue and queued buffers list */
+	struct vb2_queue vb_queue;
+	struct list_head queued_bufs;
+	spinlock_t queued_bufs_lock; /* Protects queued_bufs */
+
+	/* Note if taking both locks v4l2_lock must always be locked first! */
+	struct mutex v4l2_lock;      /* Protects everything else */
+	struct mutex vb_queue_lock;  /* Protects vb_queue and capt_file */
+
+	/* Pointer to our usb_device, will be NULL after unplug */
+	struct usb_device *udev; /* Both mutexes most be hold when setting! */
+
+	unsigned int vb_full; /* vb is full and packets dropped */
+
+	struct urb     *urb_list[MAX_BULK_BUFS];
+	int            buf_num;
+	unsigned long  buf_size;
+	u8             *buf_list[MAX_BULK_BUFS];
+	dma_addr_t     dma_addr[MAX_BULK_BUFS];
+	int urbs_initialized;
+	int urbs_submitted;
+
+	unsigned int f_adc, f_tuner;
+	u32 pixelformat;
+
+	/* Controls */
+	struct v4l2_ctrl_handler hdl;
+	struct v4l2_ctrl *bandwidth_auto;
+	struct v4l2_ctrl *bandwidth;
+
+	/* for sample rate calc */
+	unsigned int sample;
+	unsigned int sample_measured;
+	unsigned long jiffies_next;
+};
+
+/* write multiple hardware registers */
+static int rtl2832_sdr_wr(struct rtl2832_sdr_state *s, u8 reg, const u8 *val,
+		int len)
+{
+	int ret;
+	u8 buf[1 + len];
+	struct i2c_msg msg[1] = {
+		{
+			.addr = s->cfg->i2c_addr,
+			.flags = 0,
+			.len = 1 + len,
+			.buf = buf,
+		}
+	};
+
+	buf[0] = reg;
+	memcpy(&buf[1], val, len);
+
+	ret = i2c_transfer(s->i2c, msg, 1);
+	if (ret == 1) {
+		ret = 0;
+	} else {
+		dev_err(&s->i2c->dev,
+			"%s: I2C wr failed=%d reg=%02x len=%d\n",
+			KBUILD_MODNAME, ret, reg, len);
+		ret = -EREMOTEIO;
+	}
+	return ret;
+}
+
+/* read multiple hardware registers */
+static int rtl2832_sdr_rd(struct rtl2832_sdr_state *s, u8 reg, u8 *val, int len)
+{
+	int ret;
+	struct i2c_msg msg[2] = {
+		{
+			.addr = s->cfg->i2c_addr,
+			.flags = 0,
+			.len = 1,
+			.buf = &reg,
+		}, {
+			.addr = s->cfg->i2c_addr,
+			.flags = I2C_M_RD,
+			.len = len,
+			.buf = val,
+		}
+	};
+
+	ret = i2c_transfer(s->i2c, msg, 2);
+	if (ret == 2) {
+		ret = 0;
+	} else {
+		dev_err(&s->i2c->dev,
+				"%s: I2C rd failed=%d reg=%02x len=%d\n",
+				KBUILD_MODNAME, ret, reg, len);
+		ret = -EREMOTEIO;
+	}
+	return ret;
+}
+
+/* write multiple registers */
+static int rtl2832_sdr_wr_regs(struct rtl2832_sdr_state *s, u16 reg,
+		const u8 *val, int len)
+{
+	int ret;
+	u8 reg2 = (reg >> 0) & 0xff;
+	u8 bank = (reg >> 8) & 0xff;
+
+	/* switch bank if needed */
+	if (bank != s->bank) {
+		ret = rtl2832_sdr_wr(s, 0x00, &bank, 1);
+		if (ret)
+			return ret;
+
+		s->bank = bank;
+	}
+
+	return rtl2832_sdr_wr(s, reg2, val, len);
+}
+
+/* read multiple registers */
+static int rtl2832_sdr_rd_regs(struct rtl2832_sdr_state *s, u16 reg, u8 *val,
+		int len)
+{
+	int ret;
+	u8 reg2 = (reg >> 0) & 0xff;
+	u8 bank = (reg >> 8) & 0xff;
+
+	/* switch bank if needed */
+	if (bank != s->bank) {
+		ret = rtl2832_sdr_wr(s, 0x00, &bank, 1);
+		if (ret)
+			return ret;
+
+		s->bank = bank;
+	}
+
+	return rtl2832_sdr_rd(s, reg2, val, len);
+}
+
+/* write single register */
+static int rtl2832_sdr_wr_reg(struct rtl2832_sdr_state *s, u16 reg, u8 val)
+{
+	return rtl2832_sdr_wr_regs(s, reg, &val, 1);
+}
+
+#if 0
+/* read single register */
+static int rtl2832_sdr_rd_reg(struct rtl2832_sdr_state *s, u16 reg, u8 *val)
+{
+	return rtl2832_sdr_rd_regs(s, reg, val, 1);
+}
+#endif
+
+/* write single register with mask */
+static int rtl2832_sdr_wr_reg_mask(struct rtl2832_sdr_state *s, u16 reg,
+		u8 val, u8 mask)
+{
+	int ret;
+	u8 tmp;
+
+	/* no need for read if whole reg is written */
+	if (mask != 0xff) {
+		ret = rtl2832_sdr_rd_regs(s, reg, &tmp, 1);
+		if (ret)
+			return ret;
+
+		val &= mask;
+		tmp &= ~mask;
+		val |= tmp;
+	}
+
+	return rtl2832_sdr_wr_regs(s, reg, &val, 1);
+}
+
+#if 0
+/* read single register with mask */
+static int rtl2832_sdr_rd_reg_mask(struct rtl2832_sdr_state *s, u16 reg,
+		u8 *val, u8 mask)
+{
+	int ret, i;
+	u8 tmp;
+
+	ret = rtl2832_sdr_rd_regs(s, reg, &tmp, 1);
+	if (ret)
+		return ret;
+
+	tmp &= mask;
+
+	/* find position of the first bit */
+	for (i = 0; i < 8; i++) {
+		if ((mask >> i) & 0x01)
+			break;
+	}
+	*val = tmp >> i;
+
+	return 0;
+}
+#endif
+
+/* Private functions */
+static struct rtl2832_sdr_frame_buf *rtl2832_sdr_get_next_fill_buf(
+		struct rtl2832_sdr_state *s)
+{
+	unsigned long flags = 0;
+	struct rtl2832_sdr_frame_buf *buf = NULL;
+
+	spin_lock_irqsave(&s->queued_bufs_lock, flags);
+	if (list_empty(&s->queued_bufs))
+		goto leave;
+
+	buf = list_entry(s->queued_bufs.next,
+			struct rtl2832_sdr_frame_buf, list);
+	list_del(&buf->list);
+leave:
+	spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
+	return buf;
+}
+
+static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_state *s,
+		void *dst, const u8 *src, unsigned int src_len)
+{
+	unsigned int dst_len;
+
+	if (s->pixelformat ==  V4L2_SDR_FMT_CU8) {
+		/* native stream, no need to convert */
+		memcpy(dst, src, src_len);
+		dst_len = src_len;
+	} else if (s->pixelformat == V4L2_SDR_FMT_CU16LE) {
+		/* convert u8 to u16 */
+		unsigned int i;
+		u16 *u16dst = dst;
+		for (i = 0; i < src_len; i++)
+			*u16dst++ = (src[i] << 8) | (src[i] >> 0);
+		dst_len = 2 * src_len;
+	} else {
+		dst_len = 0;
+	}
+
+	/* calculate samping rate and output it in 10 seconds intervals */
+	if (unlikely(time_is_before_jiffies(s->jiffies_next))) {
+#define MSECS 10000UL
+		unsigned int samples = s->sample - s->sample_measured;
+		s->jiffies_next = jiffies + msecs_to_jiffies(MSECS);
+		s->sample_measured = s->sample;
+		dev_dbg(&s->udev->dev,
+				"slen=%d samples=%u msecs=%lu sampling rate=%lu\n",
+				src_len, samples, MSECS,
+				samples * 1000UL / MSECS);
+	}
+
+	/* total number of I+Q pairs */
+	s->sample += src_len / 2;
+
+	return dst_len;
+}
+
+/*
+ * This gets called for the bulk stream pipe. This is done in interrupt
+ * time, so it has to be fast, not crash, and not stall. Neat.
+ */
+static void rtl2832_sdr_urb_complete(struct urb *urb)
+{
+	struct rtl2832_sdr_state *s = urb->context;
+	struct rtl2832_sdr_frame_buf *fbuf;
+
+	dev_dbg_ratelimited(&s->udev->dev,
+			"%s: status=%d length=%d/%d errors=%d\n",
+			__func__, urb->status, urb->actual_length,
+			urb->transfer_buffer_length, urb->error_count);
+
+	switch (urb->status) {
+	case 0:             /* success */
+	case -ETIMEDOUT:    /* NAK */
+		break;
+	case -ECONNRESET:   /* kill */
+	case -ENOENT:
+	case -ESHUTDOWN:
+		return;
+	default:            /* error */
+		dev_err_ratelimited(&s->udev->dev, "urb failed=%d\n",
+				urb->status);
+		break;
+	}
+
+	if (likely(urb->actual_length > 0)) {
+		void *ptr;
+		unsigned int len;
+		/* get free framebuffer */
+		fbuf = rtl2832_sdr_get_next_fill_buf(s);
+		if (unlikely(fbuf == NULL)) {
+			s->vb_full++;
+			dev_notice_ratelimited(&s->udev->dev,
+					"videobuf is full, %d packets dropped\n",
+					s->vb_full);
+			goto skip;
+		}
+
+		/* fill framebuffer */
+		ptr = vb2_plane_vaddr(&fbuf->vb, 0);
+		len = rtl2832_sdr_convert_stream(s, ptr, urb->transfer_buffer,
+				urb->actual_length);
+		vb2_set_plane_payload(&fbuf->vb, 0, len);
+		vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE);
+	}
+skip:
+	usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static int rtl2832_sdr_kill_urbs(struct rtl2832_sdr_state *s)
+{
+	int i;
+
+	for (i = s->urbs_submitted - 1; i >= 0; i--) {
+		dev_dbg(&s->udev->dev, "%s: kill urb=%d\n", __func__, i);
+		/* stop the URB */
+		usb_kill_urb(s->urb_list[i]);
+	}
+	s->urbs_submitted = 0;
+
+	return 0;
+}
+
+static int rtl2832_sdr_submit_urbs(struct rtl2832_sdr_state *s)
+{
+	int i, ret;
+
+	for (i = 0; i < s->urbs_initialized; i++) {
+		dev_dbg(&s->udev->dev, "%s: submit urb=%d\n", __func__, i);
+		ret = usb_submit_urb(s->urb_list[i], GFP_ATOMIC);
+		if (ret) {
+			dev_err(&s->udev->dev,
+					"Could not submit urb no. %d - get them all back\n",
+					i);
+			rtl2832_sdr_kill_urbs(s);
+			return ret;
+		}
+		s->urbs_submitted++;
+	}
+
+	return 0;
+}
+
+static int rtl2832_sdr_free_stream_bufs(struct rtl2832_sdr_state *s)
+{
+	if (s->flags & USB_STATE_URB_BUF) {
+		while (s->buf_num) {
+			s->buf_num--;
+			dev_dbg(&s->udev->dev, "%s: free buf=%d\n",
+					__func__, s->buf_num);
+			usb_free_coherent(s->udev, s->buf_size,
+					  s->buf_list[s->buf_num],
+					  s->dma_addr[s->buf_num]);
+		}
+	}
+	s->flags &= ~USB_STATE_URB_BUF;
+
+	return 0;
+}
+
+static int rtl2832_sdr_alloc_stream_bufs(struct rtl2832_sdr_state *s)
+{
+	s->buf_num = 0;
+	s->buf_size = BULK_BUFFER_SIZE;
+
+	dev_dbg(&s->udev->dev,
+			"%s: all in all I will use %u bytes for streaming\n",
+			__func__,  MAX_BULK_BUFS * BULK_BUFFER_SIZE);
+
+	for (s->buf_num = 0; s->buf_num < MAX_BULK_BUFS; s->buf_num++) {
+		s->buf_list[s->buf_num] = usb_alloc_coherent(s->udev,
+				BULK_BUFFER_SIZE, GFP_ATOMIC,
+				&s->dma_addr[s->buf_num]);
+		if (!s->buf_list[s->buf_num]) {
+			dev_dbg(&s->udev->dev, "%s: alloc buf=%d failed\n",
+					__func__, s->buf_num);
+			rtl2832_sdr_free_stream_bufs(s);
+			return -ENOMEM;
+		}
+
+		dev_dbg(&s->udev->dev, "%s: alloc buf=%d %p (dma %llu)\n",
+				__func__, s->buf_num,
+				s->buf_list[s->buf_num],
+				(long long)s->dma_addr[s->buf_num]);
+		s->flags |= USB_STATE_URB_BUF;
+	}
+
+	return 0;
+}
+
+static int rtl2832_sdr_free_urbs(struct rtl2832_sdr_state *s)
+{
+	int i;
+
+	rtl2832_sdr_kill_urbs(s);
+
+	for (i = s->urbs_initialized - 1; i >= 0; i--) {
+		if (s->urb_list[i]) {
+			dev_dbg(&s->udev->dev, "%s: free urb=%d\n",
+					__func__, i);
+			/* free the URBs */
+			usb_free_urb(s->urb_list[i]);
+		}
+	}
+	s->urbs_initialized = 0;
+
+	return 0;
+}
+
+static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_state *s)
+{
+	int i, j;
+
+	/* allocate the URBs */
+	for (i = 0; i < MAX_BULK_BUFS; i++) {
+		dev_dbg(&s->udev->dev, "%s: alloc urb=%d\n", __func__, i);
+		s->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
+		if (!s->urb_list[i]) {
+			dev_dbg(&s->udev->dev, "%s: failed\n", __func__);
+			for (j = 0; j < i; j++)
+				usb_free_urb(s->urb_list[j]);
+			return -ENOMEM;
+		}
+		usb_fill_bulk_urb(s->urb_list[i],
+				s->udev,
+				usb_rcvbulkpipe(s->udev, 0x81),
+				s->buf_list[i],
+				BULK_BUFFER_SIZE,
+				rtl2832_sdr_urb_complete, s);
+
+		s->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+		s->urb_list[i]->transfer_dma = s->dma_addr[i];
+		s->urbs_initialized++;
+	}
+
+	return 0;
+}
+
+/* Must be called with vb_queue_lock hold */
+static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_state *s)
+{
+	unsigned long flags = 0;
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	spin_lock_irqsave(&s->queued_bufs_lock, flags);
+	while (!list_empty(&s->queued_bufs)) {
+		struct rtl2832_sdr_frame_buf *buf;
+		buf = list_entry(s->queued_bufs.next,
+				struct rtl2832_sdr_frame_buf, list);
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+	}
+	spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
+}
+
+/* The user yanked out the cable... */
+static void rtl2832_sdr_release_sec(struct dvb_frontend *fe)
+{
+	struct rtl2832_sdr_state *s = fe->sec_priv;
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	mutex_lock(&s->vb_queue_lock);
+	mutex_lock(&s->v4l2_lock);
+	/* No need to keep the urbs around after disconnection */
+	s->udev = NULL;
+
+	v4l2_device_disconnect(&s->v4l2_dev);
+	video_unregister_device(&s->vdev);
+	mutex_unlock(&s->v4l2_lock);
+	mutex_unlock(&s->vb_queue_lock);
+
+	v4l2_device_put(&s->v4l2_dev);
+
+	fe->sec_priv = NULL;
+}
+
+static int rtl2832_sdr_querycap(struct file *file, void *fh,
+		struct v4l2_capability *cap)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+	strlcpy(cap->card, s->vdev.name, sizeof(cap->card));
+	usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info));
+	cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING |
+			V4L2_CAP_READWRITE | V4L2_CAP_TUNER;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+	return 0;
+}
+
+/* Videobuf2 operations */
+static int rtl2832_sdr_queue_setup(struct vb2_queue *vq,
+		const struct v4l2_format *fmt, unsigned int *nbuffers,
+		unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[])
+{
+	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
+	dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers);
+
+	/* Absolute min and max number of buffers available for mmap() */
+	*nbuffers = clamp_t(unsigned int, *nbuffers, 8, 32);
+	*nplanes = 1;
+	/* 2 = max 16-bit sample returned */
+	sizes[0] = PAGE_ALIGN(BULK_BUFFER_SIZE * 2);
+	dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n",
+			__func__, *nbuffers, sizes[0]);
+	return 0;
+}
+
+static int rtl2832_sdr_buf_prepare(struct vb2_buffer *vb)
+{
+	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue);
+
+	/* Don't allow queing new buffers after device disconnection */
+	if (!s->udev)
+		return -ENODEV;
+
+	return 0;
+}
+
+static void rtl2832_sdr_buf_queue(struct vb2_buffer *vb)
+{
+	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue);
+	struct rtl2832_sdr_frame_buf *buf =
+			container_of(vb, struct rtl2832_sdr_frame_buf, vb);
+	unsigned long flags = 0;
+
+	/* Check the device has not disconnected between prep and queuing */
+	if (!s->udev) {
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+		return;
+	}
+
+	spin_lock_irqsave(&s->queued_bufs_lock, flags);
+	list_add_tail(&buf->list, &s->queued_bufs);
+	spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
+}
+
+static int rtl2832_sdr_set_adc(struct rtl2832_sdr_state *s)
+{
+	struct dvb_frontend *fe = s->fe;
+	int ret;
+	unsigned int f_sr, f_if;
+	u8 buf[4], u8tmp1, u8tmp2;
+	u64 u64tmp;
+	u32 u32tmp;
+	dev_dbg(&s->udev->dev, "%s: f_adc=%u\n", __func__, s->f_adc);
+
+	if (!test_bit(POWER_ON, &s->flags))
+		return 0;
+
+	if (s->f_adc == 0)
+		return 0;
+
+	f_sr = s->f_adc;
+
+	ret = rtl2832_sdr_wr_regs(s, 0x13e, "\x00\x00", 2);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_regs(s, 0x115, "\x00\x00\x00\x00", 4);
+	if (ret)
+		goto err;
+
+	/* get IF from tuner */
+	if (fe->ops.tuner_ops.get_if_frequency)
+		ret = fe->ops.tuner_ops.get_if_frequency(fe, &f_if);
+	else
+		ret = -EINVAL;
+
+	if (ret)
+		goto err;
+
+	/* program IF */
+	u64tmp = f_if % s->cfg->xtal;
+	u64tmp *= 0x400000;
+	u64tmp = div_u64(u64tmp, s->cfg->xtal);
+	u64tmp = -u64tmp;
+	u32tmp = u64tmp & 0x3fffff;
+
+	dev_dbg(&s->udev->dev, "%s: f_if=%u if_ctl=%08x\n",
+			__func__, f_if, u32tmp);
+
+	buf[0] = (u32tmp >> 16) & 0xff;
+	buf[1] = (u32tmp >>  8) & 0xff;
+	buf[2] = (u32tmp >>  0) & 0xff;
+
+	ret = rtl2832_sdr_wr_regs(s, 0x119, buf, 3);
+	if (ret)
+		goto err;
+
+	/* BB / IF mode */
+	/* POR: 0x1b1=0x1f, 0x008=0x0d, 0x006=0x80 */
+	if (f_if) {
+		u8tmp1 = 0x1a; /* disable Zero-IF */
+		u8tmp2 = 0x8d; /* enable ADC I */
+	} else {
+		u8tmp1 = 0x1b; /* enable Zero-IF, DC, IQ */
+		u8tmp2 = 0xcd; /* enable ADC I, ADC Q */
+	}
+
+	ret = rtl2832_sdr_wr_reg(s, 0x1b1, u8tmp1);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_reg(s, 0x008, u8tmp2);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_reg(s, 0x006, 0x80);
+	if (ret)
+		goto err;
+
+	/* program sampling rate (resampling down) */
+	u32tmp = div_u64(s->cfg->xtal * 0x400000ULL, f_sr * 4U);
+	u32tmp <<= 2;
+	buf[0] = (u32tmp >> 24) & 0xff;
+	buf[1] = (u32tmp >> 16) & 0xff;
+	buf[2] = (u32tmp >>  8) & 0xff;
+	buf[3] = (u32tmp >>  0) & 0xff;
+	ret = rtl2832_sdr_wr_regs(s, 0x19f, buf, 4);
+	if (ret)
+		goto err;
+
+	/* low-pass filter */
+	ret = rtl2832_sdr_wr_regs(s, 0x11c,
+			"\xca\xdc\xd7\xd8\xe0\xf2\x0e\x35\x06\x50\x9c\x0d\x71\x11\x14\x71\x74\x19\x41\xa5",
+			20);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_regs(s, 0x017, "\x11\x10", 2);
+	if (ret)
+		goto err;
+
+	/* mode */
+	ret = rtl2832_sdr_wr_regs(s, 0x019, "\x05", 1);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_regs(s, 0x01a, "\x1b\x16\x0d\x06\x01\xff", 6);
+	if (ret)
+		goto err;
+
+	/* FSM */
+	ret = rtl2832_sdr_wr_regs(s, 0x192, "\x00\xf0\x0f", 3);
+	if (ret)
+		goto err;
+
+	/* PID filter */
+	ret = rtl2832_sdr_wr_regs(s, 0x061, "\x60", 1);
+	if (ret)
+		goto err;
+
+	/* used RF tuner based settings */
+	switch (s->cfg->tuner) {
+	case RTL2832_TUNER_E4000:
+		ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x103, "\x5a", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x30", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x104, "\xd0", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x18", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x011, "\xd4", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1e5, "\xf0", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1d9, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1db, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1dd, "\x14", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1de, "\xec", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1d8, "\x0c", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1e6, "\x02", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1d7, "\x09", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x83", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x010, "\x49", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x87", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x85", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x013, "\x02", 1);
+		break;
+	case RTL2832_TUNER_FC0012:
+	case RTL2832_TUNER_FC0013:
+		ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x103, "\x5a", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x2c", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x104, "\xcc", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x16", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x011, "\xe9\xbf", 2);
+		ret = rtl2832_sdr_wr_regs(s, 0x1e5, "\xf0", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1d9, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1db, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1dd, "\x11", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1de, "\xef", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1d8, "\x0c", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1e6, "\x02", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1d7, "\x09", 1);
+		break;
+	case RTL2832_TUNER_R820T:
+		ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x115, "\x01", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x103, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x24", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x104, "\xcc", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x14", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x011, "\xf4", 1);
+		break;
+	default:
+		dev_notice(&s->udev->dev, "Unsupported tuner\n");
+	}
+
+	/* software reset */
+	ret = rtl2832_sdr_wr_reg_mask(s, 0x101, 0x04, 0x04);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_reg_mask(s, 0x101, 0x00, 0x04);
+	if (ret)
+		goto err;
+err:
+	return ret;
+};
+
+static void rtl2832_sdr_unset_adc(struct rtl2832_sdr_state *s)
+{
+	int ret;
+
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	/* PID filter */
+	ret = rtl2832_sdr_wr_regs(s, 0x061, "\xe0", 1);
+	if (ret)
+		goto err;
+
+	/* mode */
+	ret = rtl2832_sdr_wr_regs(s, 0x019, "\x20", 1);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_regs(s, 0x017, "\x11\x10", 2);
+	if (ret)
+		goto err;
+
+	/* FSM */
+	ret = rtl2832_sdr_wr_regs(s, 0x192, "\x00\x0f\xff", 3);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_regs(s, 0x13e, "\x40\x00", 2);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_regs(s, 0x115, "\x06\x3f\xce\xcc", 4);
+	if (ret)
+		goto err;
+err:
+	return;
+};
+
+static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_state *s)
+{
+	struct dvb_frontend *fe = s->fe;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct v4l2_ctrl *bandwidth_auto;
+	struct v4l2_ctrl *bandwidth;
+
+	/*
+	 * tuner RF (Hz)
+	 */
+	if (s->f_tuner == 0)
+		return 0;
+
+	/*
+	 * bandwidth (Hz)
+	 */
+	bandwidth_auto = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO);
+	bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH);
+	if (v4l2_ctrl_g_ctrl(bandwidth_auto)) {
+		c->bandwidth_hz = s->f_adc;
+		v4l2_ctrl_s_ctrl(bandwidth, s->f_adc);
+	} else {
+		c->bandwidth_hz = v4l2_ctrl_g_ctrl(bandwidth);
+	}
+
+	c->frequency = s->f_tuner;
+	c->delivery_system = SYS_DVBT;
+
+	dev_dbg(&s->udev->dev, "%s: frequency=%u bandwidth=%d\n",
+			__func__, c->frequency, c->bandwidth_hz);
+
+	if (!test_bit(POWER_ON, &s->flags))
+		return 0;
+
+	if (fe->ops.tuner_ops.set_params)
+		fe->ops.tuner_ops.set_params(fe);
+
+	return 0;
+};
+
+static int rtl2832_sdr_set_tuner(struct rtl2832_sdr_state *s)
+{
+	struct dvb_frontend *fe = s->fe;
+
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	if (fe->ops.tuner_ops.init)
+		fe->ops.tuner_ops.init(fe);
+
+	return 0;
+};
+
+static void rtl2832_sdr_unset_tuner(struct rtl2832_sdr_state *s)
+{
+	struct dvb_frontend *fe = s->fe;
+
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	if (fe->ops.tuner_ops.sleep)
+		fe->ops.tuner_ops.sleep(fe);
+
+	return;
+};
+
+static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
+	int ret;
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	if (!s->udev)
+		return -ENODEV;
+
+	if (mutex_lock_interruptible(&s->v4l2_lock))
+		return -ERESTARTSYS;
+
+	if (s->d->props->power_ctrl)
+		s->d->props->power_ctrl(s->d, 1);
+
+	set_bit(POWER_ON, &s->flags);
+
+	ret = rtl2832_sdr_set_tuner(s);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_set_tuner_freq(s);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_set_adc(s);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_alloc_stream_bufs(s);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_alloc_urbs(s);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_submit_urbs(s);
+	if (ret)
+		goto err;
+
+err:
+	mutex_unlock(&s->v4l2_lock);
+
+	return ret;
+}
+
+static int rtl2832_sdr_stop_streaming(struct vb2_queue *vq)
+{
+	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	if (mutex_lock_interruptible(&s->v4l2_lock))
+		return -ERESTARTSYS;
+
+	rtl2832_sdr_kill_urbs(s);
+	rtl2832_sdr_free_urbs(s);
+	rtl2832_sdr_free_stream_bufs(s);
+	rtl2832_sdr_cleanup_queued_bufs(s);
+	rtl2832_sdr_unset_adc(s);
+	rtl2832_sdr_unset_tuner(s);
+
+	clear_bit(POWER_ON, &s->flags);
+
+	if (s->d->props->power_ctrl)
+		s->d->props->power_ctrl(s->d, 0);
+
+	mutex_unlock(&s->v4l2_lock);
+
+	return 0;
+}
+
+static struct vb2_ops rtl2832_sdr_vb2_ops = {
+	.queue_setup            = rtl2832_sdr_queue_setup,
+	.buf_prepare            = rtl2832_sdr_buf_prepare,
+	.buf_queue              = rtl2832_sdr_buf_queue,
+	.start_streaming        = rtl2832_sdr_start_streaming,
+	.stop_streaming         = rtl2832_sdr_stop_streaming,
+	.wait_prepare           = vb2_ops_wait_prepare,
+	.wait_finish            = vb2_ops_wait_finish,
+};
+
+static int rtl2832_sdr_g_tuner(struct file *file, void *priv,
+		struct v4l2_tuner *v)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	dev_dbg(&s->udev->dev, "%s: index=%d type=%d\n",
+			__func__, v->index, v->type);
+
+	if (v->index == 0) {
+		strlcpy(v->name, "ADC: Realtek RTL2832", sizeof(v->name));
+		v->type = V4L2_TUNER_ADC;
+		v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+		v->rangelow =   300000;
+		v->rangehigh = 3200000;
+	} else if (v->index == 1) {
+		strlcpy(v->name, "RF: <unknown>", sizeof(v->name));
+		v->type = V4L2_TUNER_RF;
+		v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+		v->rangelow =    50000000;
+		v->rangehigh = 2000000000;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int rtl2832_sdr_s_tuner(struct file *file, void *priv,
+		const struct v4l2_tuner *v)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	return 0;
+}
+
+static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv,
+		struct v4l2_frequency_band *band)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n",
+			__func__, band->tuner, band->type, band->index);
+
+	if (band->tuner == 0) {
+		if (band->index >= ARRAY_SIZE(bands_adc))
+			return -EINVAL;
+
+		*band = bands_adc[band->index];
+	} else if (band->tuner == 1) {
+		if (band->index >= ARRAY_SIZE(bands_fm))
+			return -EINVAL;
+
+		*band = bands_fm[band->index];
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int rtl2832_sdr_g_frequency(struct file *file, void *priv,
+		struct v4l2_frequency *f)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	int ret  = 0;
+	dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n",
+			__func__, f->tuner, f->type);
+
+	if (f->tuner == 0)
+		f->frequency = s->f_adc;
+	else if (f->tuner == 1)
+		f->frequency = s->f_tuner;
+	else
+		return -EINVAL;
+
+	return ret;
+}
+
+static int rtl2832_sdr_s_frequency(struct file *file, void *priv,
+		const struct v4l2_frequency *f)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	int ret, band;
+
+	dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n",
+			__func__, f->tuner, f->type, f->frequency);
+
+	/* ADC band midpoints */
+	#define BAND_ADC_0 ((bands_adc[0].rangehigh + bands_adc[1].rangelow) / 2)
+	#define BAND_ADC_1 ((bands_adc[1].rangehigh + bands_adc[2].rangelow) / 2)
+
+	if (f->tuner == 0 && f->type == V4L2_TUNER_ADC) {
+		if (f->frequency < BAND_ADC_0)
+			band = 0;
+		else if (f->frequency < BAND_ADC_1)
+			band = 1;
+		else
+			band = 2;
+
+		s->f_adc = clamp_t(unsigned int, f->frequency,
+				bands_adc[band].rangelow,
+				bands_adc[band].rangehigh);
+
+		dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n",
+				__func__, s->f_adc);
+		ret = rtl2832_sdr_set_adc(s);
+	} else if (f->tuner == 1) {
+		s->f_tuner = f->frequency;
+		dev_dbg(&s->udev->dev, "%s: RF frequency=%u Hz\n",
+				__func__, f->frequency);
+
+		ret = rtl2832_sdr_set_tuner_freq(s);
+	} else {
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int rtl2832_sdr_enum_fmt_sdr_cap(struct file *file, void *priv,
+		struct v4l2_fmtdesc *f)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	if (f->index >= NUM_FORMATS)
+		return -EINVAL;
+
+	strlcpy(f->description, formats[f->index].name, sizeof(f->description));
+	f->pixelformat = formats[f->index].pixelformat;
+
+	return 0;
+}
+
+static int rtl2832_sdr_g_fmt_sdr_cap(struct file *file, void *priv,
+		struct v4l2_format *f)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	f->fmt.sdr.pixelformat = s->pixelformat;
+
+	return 0;
+}
+
+static int rtl2832_sdr_s_fmt_sdr_cap(struct file *file, void *priv,
+		struct v4l2_format *f)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	struct vb2_queue *q = &s->vb_queue;
+	int i;
+	dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
+			(char *)&f->fmt.sdr.pixelformat);
+
+	if (vb2_is_busy(q))
+		return -EBUSY;
+
+	for (i = 0; i < NUM_FORMATS; i++) {
+		if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
+			s->pixelformat = f->fmt.sdr.pixelformat;
+			return 0;
+		}
+	}
+
+	f->fmt.sdr.pixelformat = formats[0].pixelformat;
+	s->pixelformat = formats[0].pixelformat;
+
+	return 0;
+}
+
+static int rtl2832_sdr_try_fmt_sdr_cap(struct file *file, void *priv,
+		struct v4l2_format *f)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	int i;
+	dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
+			(char *)&f->fmt.sdr.pixelformat);
+
+	for (i = 0; i < NUM_FORMATS; i++) {
+		if (formats[i].pixelformat == f->fmt.sdr.pixelformat)
+			return 0;
+	}
+
+	f->fmt.sdr.pixelformat = formats[0].pixelformat;
+
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops rtl2832_sdr_ioctl_ops = {
+	.vidioc_querycap          = rtl2832_sdr_querycap,
+
+	.vidioc_enum_fmt_sdr_cap  = rtl2832_sdr_enum_fmt_sdr_cap,
+	.vidioc_g_fmt_sdr_cap     = rtl2832_sdr_g_fmt_sdr_cap,
+	.vidioc_s_fmt_sdr_cap     = rtl2832_sdr_s_fmt_sdr_cap,
+	.vidioc_try_fmt_sdr_cap   = rtl2832_sdr_try_fmt_sdr_cap,
+
+	.vidioc_reqbufs           = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs       = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf       = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf          = vb2_ioctl_querybuf,
+	.vidioc_qbuf              = vb2_ioctl_qbuf,
+	.vidioc_dqbuf             = vb2_ioctl_dqbuf,
+
+	.vidioc_streamon          = vb2_ioctl_streamon,
+	.vidioc_streamoff         = vb2_ioctl_streamoff,
+
+	.vidioc_g_tuner           = rtl2832_sdr_g_tuner,
+	.vidioc_s_tuner           = rtl2832_sdr_s_tuner,
+
+	.vidioc_enum_freq_bands   = rtl2832_sdr_enum_freq_bands,
+	.vidioc_g_frequency       = rtl2832_sdr_g_frequency,
+	.vidioc_s_frequency       = rtl2832_sdr_s_frequency,
+
+	.vidioc_subscribe_event   = v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+	.vidioc_log_status        = v4l2_ctrl_log_status,
+};
+
+static const struct v4l2_file_operations rtl2832_sdr_fops = {
+	.owner                    = THIS_MODULE,
+	.open                     = v4l2_fh_open,
+	.release                  = vb2_fop_release,
+	.read                     = vb2_fop_read,
+	.poll                     = vb2_fop_poll,
+	.mmap                     = vb2_fop_mmap,
+	.unlocked_ioctl           = video_ioctl2,
+};
+
+static struct video_device rtl2832_sdr_template = {
+	.name                     = "Realtek RTL2832 SDR",
+	.release                  = video_device_release_empty,
+	.fops                     = &rtl2832_sdr_fops,
+	.ioctl_ops                = &rtl2832_sdr_ioctl_ops,
+};
+
+static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct rtl2832_sdr_state *s =
+			container_of(ctrl->handler, struct rtl2832_sdr_state,
+					hdl);
+	struct dvb_frontend *fe = s->fe;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	int ret;
+	dev_dbg(&s->udev->dev,
+			"%s: id=%d name=%s val=%d min=%d max=%d step=%d\n",
+			__func__, ctrl->id, ctrl->name, ctrl->val,
+			ctrl->minimum, ctrl->maximum, ctrl->step);
+
+	switch (ctrl->id) {
+	case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO:
+	case V4L2_CID_RF_TUNER_BANDWIDTH:
+		if (s->bandwidth_auto->val)
+			s->bandwidth->val = s->f_adc;
+
+		c->bandwidth_hz = s->bandwidth->val;
+
+		if (!test_bit(POWER_ON, &s->flags))
+			return 0;
+
+		if (fe->ops.tuner_ops.set_params)
+			ret = fe->ops.tuner_ops.set_params(fe);
+		else
+			ret = 0;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops rtl2832_sdr_ctrl_ops = {
+	.s_ctrl = rtl2832_sdr_s_ctrl,
+};
+
+static void rtl2832_sdr_video_release(struct v4l2_device *v)
+{
+	struct rtl2832_sdr_state *s =
+			container_of(v, struct rtl2832_sdr_state, v4l2_dev);
+
+	v4l2_ctrl_handler_free(&s->hdl);
+	v4l2_device_unregister(&s->v4l2_dev);
+	kfree(s);
+}
+
+struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
+		struct i2c_adapter *i2c, const struct rtl2832_config *cfg)
+{
+	int ret;
+	struct rtl2832_sdr_state *s;
+	const struct v4l2_ctrl_ops *ops = &rtl2832_sdr_ctrl_ops;
+	struct dvb_usb_device *d = i2c_get_adapdata(i2c);
+
+	s = kzalloc(sizeof(struct rtl2832_sdr_state), GFP_KERNEL);
+	if (s == NULL) {
+		dev_err(&d->udev->dev,
+				"Could not allocate memory for rtl2832_sdr_state\n");
+		return NULL;
+	}
+
+	/* setup the state */
+	s->fe = fe;
+	s->d = d;
+	s->udev = d->udev;
+	s->i2c = i2c;
+	s->cfg = cfg;
+	s->f_adc = bands_adc[0].rangelow;
+	s->pixelformat =  V4L2_SDR_FMT_CU8;
+
+	mutex_init(&s->v4l2_lock);
+	mutex_init(&s->vb_queue_lock);
+	spin_lock_init(&s->queued_bufs_lock);
+	INIT_LIST_HEAD(&s->queued_bufs);
+
+	/* Init videobuf2 queue structure */
+	s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
+	s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+	s->vb_queue.drv_priv = s;
+	s->vb_queue.buf_struct_size = sizeof(struct rtl2832_sdr_frame_buf);
+	s->vb_queue.ops = &rtl2832_sdr_vb2_ops;
+	s->vb_queue.mem_ops = &vb2_vmalloc_memops;
+	s->vb_queue.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	ret = vb2_queue_init(&s->vb_queue);
+	if (ret) {
+		dev_err(&s->udev->dev, "Could not initialize vb2 queue\n");
+		goto err_free_mem;
+	}
+
+	/* Register controls */
+	switch (s->cfg->tuner) {
+	case RTL2832_TUNER_E4000:
+		v4l2_ctrl_handler_init(&s->hdl, 2);
+		s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1);
+		s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH, 4300000, 11000000, 100000, 4300000);
+		v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
+		break;
+	case RTL2832_TUNER_R820T:
+		v4l2_ctrl_handler_init(&s->hdl, 2);
+		s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1);
+		s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH, 0, 8000000, 100000, 0);
+		v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
+		break;
+	case RTL2832_TUNER_FC0012:
+	case RTL2832_TUNER_FC0013:
+		v4l2_ctrl_handler_init(&s->hdl, 2);
+		s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1);
+		s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH, 6000000, 8000000, 1000000, 6000000);
+		v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
+		break;
+	default:
+		v4l2_ctrl_handler_init(&s->hdl, 0);
+		dev_notice(&s->udev->dev, "%s: Unsupported tuner\n",
+				KBUILD_MODNAME);
+		goto err_free_controls;
+	}
+
+	if (s->hdl.error) {
+		ret = s->hdl.error;
+		dev_err(&s->udev->dev, "Could not initialize controls\n");
+		goto err_free_controls;
+	}
+
+	/* Init video_device structure */
+	s->vdev = rtl2832_sdr_template;
+	s->vdev.queue = &s->vb_queue;
+	s->vdev.queue->lock = &s->vb_queue_lock;
+	set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev.flags);
+	video_set_drvdata(&s->vdev, s);
+
+	/* Register the v4l2_device structure */
+	s->v4l2_dev.release = rtl2832_sdr_video_release;
+	ret = v4l2_device_register(&s->udev->dev, &s->v4l2_dev);
+	if (ret) {
+		dev_err(&s->udev->dev,
+				"Failed to register v4l2-device (%d)\n", ret);
+		goto err_free_controls;
+	}
+
+	s->v4l2_dev.ctrl_handler = &s->hdl;
+	s->vdev.v4l2_dev = &s->v4l2_dev;
+	s->vdev.lock = &s->v4l2_lock;
+	s->vdev.vfl_dir = VFL_DIR_RX;
+
+	ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1);
+	if (ret) {
+		dev_err(&s->udev->dev,
+				"Failed to register as video device (%d)\n",
+				ret);
+		goto err_unregister_v4l2_dev;
+	}
+	dev_info(&s->udev->dev, "Registered as %s\n",
+			video_device_node_name(&s->vdev));
+
+	fe->sec_priv = s;
+	fe->ops.release_sec = rtl2832_sdr_release_sec;
+
+	dev_info(&s->i2c->dev, "%s: Realtek RTL2832 SDR attached\n",
+			KBUILD_MODNAME);
+	return fe;
+
+err_unregister_v4l2_dev:
+	v4l2_device_unregister(&s->v4l2_dev);
+err_free_controls:
+	v4l2_ctrl_handler_free(&s->hdl);
+err_free_mem:
+	kfree(s);
+	return NULL;
+}
+EXPORT_SYMBOL(rtl2832_sdr_attach);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Realtek RTL2832 SDR driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h
new file mode 100644
index 0000000..0803e45
--- /dev/null
+++ b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h
@@ -0,0 +1,51 @@
+/*
+ * Realtek RTL2832U SDR driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ *    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; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License along
+ *    with this program; if not, write to the Free Software Foundation, Inc.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * GNU Radio plugin "gr-kernel" for device usage will be on:
+ * http://git.linuxtv.org/anttip/gr-kernel.git
+ *
+ * TODO:
+ * Help is very highly welcome for these + all the others you could imagine:
+ * - move controls to V4L2 API
+ * - use libv4l2 for stream format conversions
+ * - gr-kernel: switch to v4l2_mmap (current read eats a lot of cpu)
+ * - SDRSharp support
+ */
+
+#ifndef RTL2832_SDR_H
+#define RTL2832_SDR_H
+
+#include <linux/kconfig.h>
+
+/* for config struct */
+#include "rtl2832.h"
+
+#if IS_ENABLED(CONFIG_DVB_RTL2832_SDR)
+extern struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
+	struct i2c_adapter *i2c, const struct rtl2832_config *cfg);
+#else
+static inline struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
+	struct i2c_adapter *i2c, const struct rtl2832_config *cfg)
+{
+	dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif
+
+#endif /* RTL2832_SDR_H */
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [REVIEW PATCH 09/16] rtl28xxu: constify demod config structs
  2014-02-27  0:30 [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver Antti Palosaari
                   ` (7 preceding siblings ...)
  2014-02-27  0:30 ` [REVIEW PATCH 08/16] rtl2832_sdr: Realtek RTL2832 SDR driver module Antti Palosaari
@ 2014-02-27  0:30 ` Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 10/16] rtl28xxu: attach SDR extension module Antti Palosaari
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:30 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Antti Palosaari

Optimize a little bit from data to text.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
index 38f4bc8..00d9440 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
@@ -516,7 +516,7 @@ err:
 	return ret;
 }
 
-static struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = {
+static const struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = {
 	.i2c_addr = 0x10, /* 0x20 */
 	.xtal = 28800000,
 	.ts_mode = 0,
@@ -527,7 +527,7 @@ static struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = {
 
 };
 
-static struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = {
+static const struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = {
 	.i2c_addr = 0x10, /* 0x20 */
 	.xtal = 28800000,
 	.ts_mode = 0,
@@ -537,7 +537,7 @@ static struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = {
 	.agc_targ_val = 0x2d,
 };
 
-static struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = {
+static const struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = {
 	.i2c_addr = 0x10, /* 0x20 */
 	.xtal = 28800000,
 	.ts_mode = 0,
@@ -551,7 +551,7 @@ static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap)
 {
 	struct dvb_usb_device *d = adap_to_d(adap);
 	struct rtl28xxu_priv *priv = d_to_priv(d);
-	struct rtl2830_config *rtl2830_config;
+	const struct rtl2830_config *rtl2830_config;
 	int ret;
 
 	dev_dbg(&d->udev->dev, "%s:\n", __func__);
@@ -586,31 +586,31 @@ err:
 	return ret;
 }
 
-static struct rtl2832_config rtl28xxu_rtl2832_fc0012_config = {
+static const struct rtl2832_config rtl28xxu_rtl2832_fc0012_config = {
 	.i2c_addr = 0x10, /* 0x20 */
 	.xtal = 28800000,
 	.tuner = TUNER_RTL2832_FC0012
 };
 
-static struct rtl2832_config rtl28xxu_rtl2832_fc0013_config = {
+static const struct rtl2832_config rtl28xxu_rtl2832_fc0013_config = {
 	.i2c_addr = 0x10, /* 0x20 */
 	.xtal = 28800000,
 	.tuner = TUNER_RTL2832_FC0013
 };
 
-static struct rtl2832_config rtl28xxu_rtl2832_tua9001_config = {
+static const struct rtl2832_config rtl28xxu_rtl2832_tua9001_config = {
 	.i2c_addr = 0x10, /* 0x20 */
 	.xtal = 28800000,
 	.tuner = TUNER_RTL2832_TUA9001,
 };
 
-static struct rtl2832_config rtl28xxu_rtl2832_e4000_config = {
+static const struct rtl2832_config rtl28xxu_rtl2832_e4000_config = {
 	.i2c_addr = 0x10, /* 0x20 */
 	.xtal = 28800000,
 	.tuner = TUNER_RTL2832_E4000,
 };
 
-static struct rtl2832_config rtl28xxu_rtl2832_r820t_config = {
+static const struct rtl2832_config rtl28xxu_rtl2832_r820t_config = {
 	.i2c_addr = 0x10,
 	.xtal = 28800000,
 	.tuner = TUNER_RTL2832_R820T,
@@ -734,7 +734,7 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
 	int ret;
 	struct dvb_usb_device *d = adap_to_d(adap);
 	struct rtl28xxu_priv *priv = d_to_priv(d);
-	struct rtl2832_config *rtl2832_config;
+	const struct rtl2832_config *rtl2832_config;
 
 	dev_dbg(&d->udev->dev, "%s:\n", __func__);
 
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [REVIEW PATCH 10/16] rtl28xxu: attach SDR extension module
  2014-02-27  0:30 [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver Antti Palosaari
                   ` (8 preceding siblings ...)
  2014-02-27  0:30 ` [REVIEW PATCH 09/16] rtl28xxu: constify demod config structs Antti Palosaari
@ 2014-02-27  0:30 ` Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 11/16] rtl28xxu: fix switch-case style issue Antti Palosaari
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:30 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Antti Palosaari

With that extension module it supports SDR.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/usb/dvb-usb-v2/Makefile   |  1 +
 drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 18 ++++++++++++++++++
 2 files changed, 19 insertions(+)

diff --git a/drivers/media/usb/dvb-usb-v2/Makefile b/drivers/media/usb/dvb-usb-v2/Makefile
index 2c06714..bfe67f9 100644
--- a/drivers/media/usb/dvb-usb-v2/Makefile
+++ b/drivers/media/usb/dvb-usb-v2/Makefile
@@ -44,3 +44,4 @@ ccflags-y += -I$(srctree)/drivers/media/dvb-core
 ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
 ccflags-y += -I$(srctree)/drivers/media/tuners
 ccflags-y += -I$(srctree)/drivers/media/common
+ccflags-y += -I$(srctree)/drivers/staging/media/rtl2832u_sdr
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
index 00d9440..73348bf 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
@@ -24,6 +24,7 @@
 
 #include "rtl2830.h"
 #include "rtl2832.h"
+#include "rtl2832_sdr.h"
 
 #include "qt1010.h"
 #include "mt2060.h"
@@ -901,6 +902,10 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
 		 * that to the tuner driver */
 		adap->fe[0]->ops.read_signal_strength =
 				adap->fe[0]->ops.tuner_ops.get_rf_strength;
+
+		/* attach SDR */
+		dvb_attach(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
+				&rtl28xxu_rtl2832_fc0012_config);
 		return 0;
 		break;
 	case TUNER_RTL2832_FC0013:
@@ -910,6 +915,10 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
 		/* fc0013 also supports signal strength reading */
 		adap->fe[0]->ops.read_signal_strength =
 				adap->fe[0]->ops.tuner_ops.get_rf_strength;
+
+		/* attach SDR */
+		dvb_attach(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
+				&rtl28xxu_rtl2832_fc0013_config);
 		return 0;
 	case TUNER_RTL2832_E4000: {
 			struct e4000_config e4000_config = {
@@ -923,6 +932,11 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
 
 			request_module("e4000");
 			priv->client = i2c_new_device(&d->i2c_adap, &info);
+
+			/* attach SDR */
+			dvb_attach(rtl2832_sdr_attach, adap->fe[0],
+					&d->i2c_adap,
+					&rtl28xxu_rtl2832_e4000_config);
 		}
 		break;
 	case TUNER_RTL2832_FC2580:
@@ -949,6 +963,10 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
 		/* Use tuner to get the signal strength */
 		adap->fe[0]->ops.read_signal_strength =
 				adap->fe[0]->ops.tuner_ops.get_rf_strength;
+
+		/* attach SDR */
+		dvb_attach(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
+				&rtl28xxu_rtl2832_r820t_config);
 		break;
 	case TUNER_RTL2832_R828D:
 		/* power off mn88472 demod on GPIO0 */
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [REVIEW PATCH 11/16] rtl28xxu: fix switch-case style issue
  2014-02-27  0:30 [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver Antti Palosaari
                   ` (9 preceding siblings ...)
  2014-02-27  0:30 ` [REVIEW PATCH 10/16] rtl28xxu: attach SDR extension module Antti Palosaari
@ 2014-02-27  0:30 ` Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 12/16] rtl28xxu: use muxed RTL2832 I2C adapters for E4000 and RTL2832_SDR Antti Palosaari
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:30 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Antti Palosaari

Use break, not return, for every case.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
index 73348bf..afafe92 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
@@ -906,7 +906,6 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
 		/* attach SDR */
 		dvb_attach(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
 				&rtl28xxu_rtl2832_fc0012_config);
-		return 0;
 		break;
 	case TUNER_RTL2832_FC0013:
 		fe = dvb_attach(fc0013_attach, adap->fe[0],
@@ -919,7 +918,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
 		/* attach SDR */
 		dvb_attach(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
 				&rtl28xxu_rtl2832_fc0013_config);
-		return 0;
+		break;
 	case TUNER_RTL2832_E4000: {
 			struct e4000_config e4000_config = {
 				.fe = adap->fe[0],
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [REVIEW PATCH 12/16] rtl28xxu: use muxed RTL2832 I2C adapters for E4000 and RTL2832_SDR
  2014-02-27  0:30 [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver Antti Palosaari
                   ` (10 preceding siblings ...)
  2014-02-27  0:30 ` [REVIEW PATCH 11/16] rtl28xxu: fix switch-case style issue Antti Palosaari
@ 2014-02-27  0:30 ` Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 13/16] rtl2832_sdr: expose e4000 controls to user Antti Palosaari
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:30 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Antti Palosaari

RTL2832 driver provides muxed I2C adapters for tuner bus I2C gate
control. Pass those adapters to rtl2832_sdr and e4000 modules in order
to get rid of proprietary DVB .i2c_gate_ctrl() callback use.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 12 ++++++++++--
 drivers/media/usb/dvb-usb-v2/rtl28xxu.h |  1 +
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
index afafe92..e04a3e9 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
@@ -774,6 +774,9 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
 		goto err;
 	}
 
+	/* RTL2832 I2C repeater */
+	priv->demod_i2c_adapter = rtl2832_get_i2c_adapter(adap->fe[0]);
+
 	/* set fe callback */
 	adap->fe[0]->callback = rtl2832u_frontend_callback;
 
@@ -920,6 +923,8 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
 				&rtl28xxu_rtl2832_fc0013_config);
 		break;
 	case TUNER_RTL2832_E4000: {
+			struct i2c_adapter *i2c_adap_internal =
+					rtl2832_get_private_i2c_adapter(adap->fe[0]);
 			struct e4000_config e4000_config = {
 				.fe = adap->fe[0],
 				.clock = 28800000,
@@ -930,11 +935,14 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
 			info.platform_data = &e4000_config;
 
 			request_module("e4000");
-			priv->client = i2c_new_device(&d->i2c_adap, &info);
+			priv->client = i2c_new_device(priv->demod_i2c_adapter,
+					&info);
+
+			i2c_set_adapdata(i2c_adap_internal, d);
 
 			/* attach SDR */
 			dvb_attach(rtl2832_sdr_attach, adap->fe[0],
-					&d->i2c_adap,
+					i2c_adap_internal,
 					&rtl28xxu_rtl2832_e4000_config);
 		}
 		break;
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
index 367aca1..a26cab1 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
@@ -55,6 +55,7 @@ struct rtl28xxu_priv {
 	u8 tuner;
 	char *tuner_name;
 	u8 page; /* integrated demod active register page */
+	struct i2c_adapter *demod_i2c_adapter;
 	bool rc_active;
 	struct i2c_client *client;
 };
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [REVIEW PATCH 13/16] rtl2832_sdr: expose e4000 controls to user
  2014-02-27  0:30 [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver Antti Palosaari
                   ` (11 preceding siblings ...)
  2014-02-27  0:30 ` [REVIEW PATCH 12/16] rtl28xxu: use muxed RTL2832 I2C adapters for E4000 and RTL2832_SDR Antti Palosaari
@ 2014-02-27  0:30 ` Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 14/16] r820t: add manual gain controls Antti Palosaari
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:30 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Antti Palosaari

E4000 tuner driver provides now some controls. Expose those to
userland.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
index dea7743..38000d2 100644
--- a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
+++ b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
@@ -25,6 +25,7 @@
 #include "dvb_frontend.h"
 #include "rtl2832_sdr.h"
 #include "dvb_usb.h"
+#include "e4000.h"
 
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
@@ -1347,6 +1348,7 @@ struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
 	struct rtl2832_sdr_state *s;
 	const struct v4l2_ctrl_ops *ops = &rtl2832_sdr_ctrl_ops;
 	struct dvb_usb_device *d = i2c_get_adapdata(i2c);
+	struct v4l2_ctrl_handler *hdl;
 
 	s = kzalloc(sizeof(struct rtl2832_sdr_state), GFP_KERNEL);
 	if (s == NULL) {
@@ -1386,10 +1388,10 @@ struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
 	/* Register controls */
 	switch (s->cfg->tuner) {
 	case RTL2832_TUNER_E4000:
-		v4l2_ctrl_handler_init(&s->hdl, 2);
-		s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1);
-		s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH, 4300000, 11000000, 100000, 4300000);
-		v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
+		hdl = e4000_get_ctrl_handler(fe);
+		v4l2_ctrl_handler_init(&s->hdl, 9);
+		if (hdl)
+			v4l2_ctrl_add_handler(&s->hdl, hdl, NULL);
 		break;
 	case RTL2832_TUNER_R820T:
 		v4l2_ctrl_handler_init(&s->hdl, 2);
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [REVIEW PATCH 14/16] r820t: add manual gain controls
  2014-02-27  0:30 [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver Antti Palosaari
                   ` (12 preceding siblings ...)
  2014-02-27  0:30 ` [REVIEW PATCH 13/16] rtl2832_sdr: expose e4000 controls to user Antti Palosaari
@ 2014-02-27  0:30 ` Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 15/16] rtl2832_sdr: expose R820T controls to user Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 16/16] MAINTAINERS: add rtl2832_sdr driver Antti Palosaari
  15 siblings, 0 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:30 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Antti Palosaari, Mauro Carvalho Chehab

Add gain control for LNA, Mixer and IF. Expose controls via
V4L control framework.

Cc: Mauro Carvalho Chehab <m.chehab@samsung.com>
Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/media/tuners/r820t.c | 137 ++++++++++++++++++++++++++++++++++++++++++-
 drivers/media/tuners/r820t.h |  10 ++++
 2 files changed, 146 insertions(+), 1 deletion(-)

diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c
index 319adc4..452a486 100644
--- a/drivers/media/tuners/r820t.c
+++ b/drivers/media/tuners/r820t.c
@@ -36,6 +36,7 @@
 #include <linux/mutex.h>
 #include <linux/slab.h>
 #include <linux/bitrev.h>
+#include <media/v4l2-ctrls.h>
 
 #include "tuner-i2c.h"
 #include "r820t.h"
@@ -80,6 +81,7 @@ struct r820t_sect_type {
 
 struct r820t_priv {
 	struct list_head		hybrid_tuner_instance_list;
+	struct dvb_frontend		*fe;
 	const struct r820t_config	*cfg;
 	struct tuner_i2c_props		i2c_props;
 	struct mutex			lock;
@@ -100,6 +102,15 @@ struct r820t_priv {
 	enum v4l2_tuner_type		type;
 	v4l2_std_id			std;
 	u32				bw;	/* in MHz */
+
+	/* Controls */
+	struct v4l2_ctrl_handler hdl;
+	struct v4l2_ctrl *lna_gain_auto;
+	struct v4l2_ctrl *lna_gain;
+	struct v4l2_ctrl *mixer_gain_auto;
+	struct v4l2_ctrl *mixer_gain;
+	struct v4l2_ctrl *if_gain_auto;
+	struct v4l2_ctrl *if_gain;
 };
 
 struct r820t_freq_range {
@@ -2255,8 +2266,10 @@ static int r820t_release(struct dvb_frontend *fe)
 
 	mutex_lock(&r820t_list_mutex);
 
-	if (priv)
+	if (priv) {
+		v4l2_ctrl_handler_free(&priv->hdl);
 		hybrid_tuner_release_state(priv);
+	}
 
 	mutex_unlock(&r820t_list_mutex);
 
@@ -2265,6 +2278,96 @@ static int r820t_release(struct dvb_frontend *fe)
 	return 0;
 }
 
+static int r820t_set_lna_gain(struct r820t_priv *priv)
+{
+	struct dvb_frontend *fe = priv->fe;
+	int rc;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	if (priv->lna_gain_auto->val)
+		rc = r820t_write_reg_mask(priv, 0x05, 0x00, 0x10);
+	else
+		rc = r820t_write_reg_mask(priv, 0x05,
+				0x10 | priv->lna_gain->val, 0x1f);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	return rc;
+}
+
+static int r820t_set_mixer_gain(struct r820t_priv *priv)
+{
+	struct dvb_frontend *fe = priv->fe;
+	int rc;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	if (priv->mixer_gain_auto->val)
+		rc = r820t_write_reg_mask(priv, 0x07, 0x10, 0x10);
+	else
+		rc = r820t_write_reg_mask(priv, 0x07,
+				0x00 | priv->mixer_gain->val, 0x1f);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	return rc;
+}
+
+static int r820t_set_if_gain(struct r820t_priv *priv)
+{
+	struct dvb_frontend *fe = priv->fe;
+	int rc;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	if (priv->if_gain_auto->val)
+		rc = r820t_write_reg_mask(priv, 0x0c, 0x10, 0x10);
+	else
+		rc = r820t_write_reg_mask(priv, 0x0c,
+				0x00 | priv->if_gain->val, 0x1f);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	return rc;
+}
+
+static int r820t_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct r820t_priv *priv =
+			container_of(ctrl->handler, struct r820t_priv, hdl);
+	int rc;
+
+	switch (ctrl->id) {
+	case  V4L2_CID_RF_TUNER_LNA_GAIN_AUTO:
+	case  V4L2_CID_RF_TUNER_LNA_GAIN:
+		rc = r820t_set_lna_gain(priv);
+		break;
+	case  V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO:
+	case  V4L2_CID_RF_TUNER_MIXER_GAIN:
+		rc = r820t_set_mixer_gain(priv);
+		break;
+	case  V4L2_CID_RF_TUNER_IF_GAIN_AUTO:
+	case  V4L2_CID_RF_TUNER_IF_GAIN:
+		rc = r820t_set_if_gain(priv);
+		break;
+	default:
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+static const struct v4l2_ctrl_ops r820t_ctrl_ops = {
+	.s_ctrl = r820t_s_ctrl,
+};
+
 static const struct dvb_tuner_ops r820t_tuner_ops = {
 	.info = {
 		.name           = "Rafael Micro R820T",
@@ -2280,6 +2383,13 @@ static const struct dvb_tuner_ops r820t_tuner_ops = {
 	.get_rf_strength = r820t_signal,
 };
 
+struct v4l2_ctrl_handler *r820t_get_ctrl_handler(struct dvb_frontend *fe)
+{
+	struct r820t_priv *priv = fe->tuner_priv;
+	return &priv->hdl;
+}
+EXPORT_SYMBOL(r820t_get_ctrl_handler);
+
 struct dvb_frontend *r820t_attach(struct dvb_frontend *fe,
 				  struct i2c_adapter *i2c,
 				  const struct r820t_config *cfg)
@@ -2314,6 +2424,8 @@ struct dvb_frontend *r820t_attach(struct dvb_frontend *fe,
 		break;
 	}
 
+	priv->fe = fe;
+
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
 
@@ -2326,6 +2438,29 @@ struct dvb_frontend *r820t_attach(struct dvb_frontend *fe,
 	if (rc < 0)
 		goto err;
 
+	/* Register controls */
+	v4l2_ctrl_handler_init(&priv->hdl, 6);
+	priv->lna_gain_auto = v4l2_ctrl_new_std(&priv->hdl, &r820t_ctrl_ops,
+			V4L2_CID_RF_TUNER_LNA_GAIN_AUTO, 0, 1, 1, 1);
+	priv->lna_gain = v4l2_ctrl_new_std(&priv->hdl, &r820t_ctrl_ops,
+			V4L2_CID_RF_TUNER_LNA_GAIN, 0, 15, 1, 6);
+	v4l2_ctrl_auto_cluster(2, &priv->lna_gain_auto, 0, false);
+	priv->mixer_gain_auto = v4l2_ctrl_new_std(&priv->hdl, &r820t_ctrl_ops,
+			V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO, 0, 1, 1, 1);
+	priv->mixer_gain = v4l2_ctrl_new_std(&priv->hdl, &r820t_ctrl_ops,
+			V4L2_CID_RF_TUNER_MIXER_GAIN, 0, 15, 1, 5);
+	v4l2_ctrl_auto_cluster(2, &priv->mixer_gain_auto, 0, false);
+	priv->if_gain_auto = v4l2_ctrl_new_std(&priv->hdl, &r820t_ctrl_ops,
+			V4L2_CID_RF_TUNER_IF_GAIN_AUTO, 0, 1, 1, 1);
+	priv->if_gain = v4l2_ctrl_new_std(&priv->hdl, &r820t_ctrl_ops,
+			V4L2_CID_RF_TUNER_IF_GAIN, 0, 15, 1, 4);
+	v4l2_ctrl_auto_cluster(2, &priv->if_gain_auto, 0, false);
+	if (priv->hdl.error) {
+		rc = priv->hdl.error;
+		tuner_info("Could not initialize controls\n");
+		goto err;
+	}
+
 	tuner_info("Rafael Micro r820t successfully identified\n");
 
 	if (fe->ops.i2c_gate_ctrl)
diff --git a/drivers/media/tuners/r820t.h b/drivers/media/tuners/r820t.h
index 48af354..b61d6ea 100644
--- a/drivers/media/tuners/r820t.h
+++ b/drivers/media/tuners/r820t.h
@@ -46,6 +46,9 @@ struct r820t_config {
 struct dvb_frontend *r820t_attach(struct dvb_frontend *fe,
 				  struct i2c_adapter *i2c,
 				  const struct r820t_config *cfg);
+
+extern struct v4l2_ctrl_handler *r820t_get_ctrl_handler(
+				struct dvb_frontend *fe);
 #else
 static inline struct dvb_frontend *r820t_attach(struct dvb_frontend *fe,
 						struct i2c_adapter *i2c,
@@ -54,6 +57,13 @@ static inline struct dvb_frontend *r820t_attach(struct dvb_frontend *fe,
 	pr_warn("%s: driver disabled by Kconfig\n", __func__);
 	return NULL;
 }
+
+static inline struct v4l2_ctrl_handler *r820t_get_ctrl_handler(
+				struct dvb_frontend *fe)
+{
+	pr_warn("%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
 #endif
 
 #endif
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [REVIEW PATCH 15/16] rtl2832_sdr: expose R820T controls to user
  2014-02-27  0:30 [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver Antti Palosaari
                   ` (13 preceding siblings ...)
  2014-02-27  0:30 ` [REVIEW PATCH 14/16] r820t: add manual gain controls Antti Palosaari
@ 2014-02-27  0:30 ` Antti Palosaari
  2014-02-27  0:30 ` [REVIEW PATCH 16/16] MAINTAINERS: add rtl2832_sdr driver Antti Palosaari
  15 siblings, 0 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:30 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Antti Palosaari

R820T tuner driver provides now some controls. Expose those to
userland.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
index 38000d2..f39e4e8 100644
--- a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
+++ b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
@@ -26,6 +26,7 @@
 #include "rtl2832_sdr.h"
 #include "dvb_usb.h"
 #include "e4000.h"
+#include "r820t.h"
 
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
@@ -1394,10 +1395,13 @@ struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
 			v4l2_ctrl_add_handler(&s->hdl, hdl, NULL);
 		break;
 	case RTL2832_TUNER_R820T:
-		v4l2_ctrl_handler_init(&s->hdl, 2);
+		v4l2_ctrl_handler_init(&s->hdl, 8);
 		s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1);
 		s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_RF_TUNER_BANDWIDTH, 0, 8000000, 100000, 0);
 		v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
+		hdl = r820t_get_ctrl_handler(fe);
+		if (hdl)
+			v4l2_ctrl_add_handler(&s->hdl, hdl, NULL);
 		break;
 	case RTL2832_TUNER_FC0012:
 	case RTL2832_TUNER_FC0013:
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [REVIEW PATCH 16/16] MAINTAINERS: add rtl2832_sdr driver
  2014-02-27  0:30 [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver Antti Palosaari
                   ` (14 preceding siblings ...)
  2014-02-27  0:30 ` [REVIEW PATCH 15/16] rtl2832_sdr: expose R820T controls to user Antti Palosaari
@ 2014-02-27  0:30 ` Antti Palosaari
  15 siblings, 0 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:30 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Antti Palosaari

Realtek RTL2832 SDR driver. Currently in staging as SDR API is not
ready.

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 MAINTAINERS | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index f03772a..0ed943a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7338,6 +7338,16 @@ T:	git git://linuxtv.org/anttip/media_tree.git
 S:	Maintained
 F:	drivers/media/dvb-frontends/rtl2832*
 
+RTL2832_SDR MEDIA DRIVER
+M:	Antti Palosaari <crope@iki.fi>
+L:	linux-media@vger.kernel.org
+W:	http://linuxtv.org/
+W:	http://palosaari.fi/linux/
+Q:	http://patchwork.linuxtv.org/project/linux-media/list/
+T:	git git://linuxtv.org/anttip/media_tree.git
+S:	Maintained
+F:	drivers/staging/media/rtl2832u_sdr/rtl2832_sdr*
+
 RTL8180 WIRELESS DRIVER
 M:	"John W. Linville" <linville@tuxdriver.com>
 L:	linux-wireless@vger.kernel.org
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [REVIEW PATCH 02/16] e4000: implement controls via v4l2 control framework
  2014-02-27  0:30 ` [REVIEW PATCH 02/16] e4000: implement controls via v4l2 control framework Antti Palosaari
@ 2014-03-13 13:57   ` Mauro Carvalho Chehab
  2014-03-13 15:17     ` Antti Palosaari
  0 siblings, 1 reply; 24+ messages in thread
From: Mauro Carvalho Chehab @ 2014-03-13 13:57 UTC (permalink / raw)
  To: Antti Palosaari; +Cc: linux-media, Hans Verkuil

Em Thu, 27 Feb 2014 02:30:11 +0200
Antti Palosaari <crope@iki.fi> escreveu:

> Implement gain and bandwidth controls using v4l2 control framework.
> Pointer to control handler is provided by exported symbol.
> 
> Cc: Mauro Carvalho Chehab <m.chehab@samsung.com>
> Cc: Hans Verkuil <hverkuil@xs4all.nl>
> Signed-off-by: Antti Palosaari <crope@iki.fi>
> ---
>  drivers/media/tuners/e4000.c      | 210 +++++++++++++++++++++++++++++++++++++-
>  drivers/media/tuners/e4000.h      |  14 +++
>  drivers/media/tuners/e4000_priv.h |  75 ++++++++++++++
>  3 files changed, 298 insertions(+), 1 deletion(-)

...
> diff --git a/drivers/media/tuners/e4000.h b/drivers/media/tuners/e4000.h
> index e74b8b2..989f2ea 100644
> --- a/drivers/media/tuners/e4000.h
> +++ b/drivers/media/tuners/e4000.h
> @@ -40,4 +40,18 @@ struct e4000_config {
>  	u32 clock;
>  };
>  
> +#if IS_ENABLED(CONFIG_MEDIA_TUNER_E4000)
> +extern struct v4l2_ctrl_handler *e4000_get_ctrl_handler(
> +		struct dvb_frontend *fe
> +);
> +#else
> +static inline struct v4l2_ctrl_handler *e4000_get_ctrl_handler(
> +		struct dvb_frontend *fe
> +)
> +{
> +	pr_warn("%s: driver disabled by Kconfig\n", __func__);
> +	return NULL;
> +}
> +#endif
> +
>  #endif

There are two things to be noticed here:

1) Please don't add any EXPORT_SYMBOL() on a pure I2C module. You
should, instead, use the subdev calls, in order to callback a
function provided by the module;

2) As you're now using request_module(), you don't need to use
#if IS_ENABLED() anymore. It is up to the module to register
itself as a V4L2 subdevice. The caller module should use the
subdevice interface to run the callbacks.

If you don't to that, you'll have several issues with the
building system.

Regards,
Mauro

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [REVIEW PATCH 02/16] e4000: implement controls via v4l2 control framework
  2014-03-13 13:57   ` Mauro Carvalho Chehab
@ 2014-03-13 15:17     ` Antti Palosaari
  2014-03-13 16:05       ` Antti Palosaari
  2014-03-13 16:40       ` Mauro Carvalho Chehab
  0 siblings, 2 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-03-13 15:17 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil

On 13.03.2014 15:57, Mauro Carvalho Chehab wrote:
> Em Thu, 27 Feb 2014 02:30:11 +0200
> Antti Palosaari <crope@iki.fi> escreveu:
>
>> Implement gain and bandwidth controls using v4l2 control framework.
>> Pointer to control handler is provided by exported symbol.
>>
>> Cc: Mauro Carvalho Chehab <m.chehab@samsung.com>
>> Cc: Hans Verkuil <hverkuil@xs4all.nl>
>> Signed-off-by: Antti Palosaari <crope@iki.fi>
>> ---
>>   drivers/media/tuners/e4000.c      | 210 +++++++++++++++++++++++++++++++++++++-
>>   drivers/media/tuners/e4000.h      |  14 +++
>>   drivers/media/tuners/e4000_priv.h |  75 ++++++++++++++
>>   3 files changed, 298 insertions(+), 1 deletion(-)
>
> ...
>> diff --git a/drivers/media/tuners/e4000.h b/drivers/media/tuners/e4000.h
>> index e74b8b2..989f2ea 100644
>> --- a/drivers/media/tuners/e4000.h
>> +++ b/drivers/media/tuners/e4000.h
>> @@ -40,4 +40,18 @@ struct e4000_config {
>>   	u32 clock;
>>   };
>>
>> +#if IS_ENABLED(CONFIG_MEDIA_TUNER_E4000)
>> +extern struct v4l2_ctrl_handler *e4000_get_ctrl_handler(
>> +		struct dvb_frontend *fe
>> +);
>> +#else
>> +static inline struct v4l2_ctrl_handler *e4000_get_ctrl_handler(
>> +		struct dvb_frontend *fe
>> +)
>> +{
>> +	pr_warn("%s: driver disabled by Kconfig\n", __func__);
>> +	return NULL;
>> +}
>> +#endif
>> +
>>   #endif
>
> There are two things to be noticed here:
>
> 1) Please don't add any EXPORT_SYMBOL() on a pure I2C module. You
> should, instead, use the subdev calls, in order to callback a
> function provided by the module;

That means, I have to implement it as a V4L subdev driver then...

Is there any problem to leave as it is? It just only provides control 
handler using that export. If you look those existing dvb frontend or 
tuner drivers there is many kind of resources exported just similarly 
(example DibCom PID filters, af9033 pid filters), so I cannot see why 
that should be different.

> 2) As you're now using request_module(), you don't need to use
> #if IS_ENABLED() anymore. It is up to the module to register
> itself as a V4L2 subdevice. The caller module should use the
> subdevice interface to run the callbacks.
>
> If you don't to that, you'll have several issues with the
> building system.

So basically you are saying I should implement that driver as a V4L 
subdev too?

regards
Antti

-- 
http://palosaari.fi/

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [REVIEW PATCH 02/16] e4000: implement controls via v4l2 control framework
  2014-03-13 15:17     ` Antti Palosaari
@ 2014-03-13 16:05       ` Antti Palosaari
  2014-03-13 16:40       ` Mauro Carvalho Chehab
  1 sibling, 0 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-03-13 16:05 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, Hans Verkuil

On 13.03.2014 17:17, Antti Palosaari wrote:
> On 13.03.2014 15:57, Mauro Carvalho Chehab wrote:
>> Em Thu, 27 Feb 2014 02:30:11 +0200
>> Antti Palosaari <crope@iki.fi> escreveu:
>>
>>> Implement gain and bandwidth controls using v4l2 control framework.
>>> Pointer to control handler is provided by exported symbol.

>> There are two things to be noticed here:
>>
>> 1) Please don't add any EXPORT_SYMBOL() on a pure I2C module. You
>> should, instead, use the subdev calls, in order to callback a
>> function provided by the module;
>
> That means, I have to implement it as a V4L subdev driver then...
>
> Is there any problem to leave as it is? It just only provides control
> handler using that export. If you look those existing dvb frontend or
> tuner drivers there is many kind of resources exported just similarly
> (example DibCom PID filters, af9033 pid filters), so I cannot see why
> that should be different.
>
>> 2) As you're now using request_module(), you don't need to use
>> #if IS_ENABLED() anymore. It is up to the module to register
>> itself as a V4L2 subdevice. The caller module should use the
>> subdevice interface to run the callbacks.
>>
>> If you don't to that, you'll have several issues with the
>> building system.
>
> So basically you are saying I should implement that driver as a V4L
> subdev too?


That is not so simple!
If you look how that device is build on driver level

rtl28xxu USB-interface driver is *master*
rtl28xxu loads rtl2832 demod driver
rtl28xxu loads e4000 tuner driver
rtl28xxu loads rtl2832_sdr driver

Whole palette is build upon DVB API, but rtl2832_sdr driver offers SDR 
API for userspace. So it is not that simple "go to and implement V4L2 
subdevice to e4000 DVB driver". Only thing needed is to find out some 
way to pass control handler from e4000 to rtl2832_sdr. There is no 
likely any existing solution where DVB side uses V4L2 subdevice and 
implementing such at this tight schedule does not sound reasonable. Why 
you didn't mention you want subdevice earlier? At the very first I 
implemented that using DVB tuner .set_config() callback, but you asked 
to switch v4l2 control framework. And now I should switch to subdev.

There does not seems to be any suitable callback for getting that kind 
of pointer. There is only one existing quite similar solution what I 
know, it is stv6110x / stv090x and I don't like it :)

Any idea?

regards
Antti

-- 
http://palosaari.fi/

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [REVIEW PATCH 02/16] e4000: implement controls via v4l2 control framework
  2014-03-13 15:17     ` Antti Palosaari
  2014-03-13 16:05       ` Antti Palosaari
@ 2014-03-13 16:40       ` Mauro Carvalho Chehab
  1 sibling, 0 replies; 24+ messages in thread
From: Mauro Carvalho Chehab @ 2014-03-13 16:40 UTC (permalink / raw)
  To: Antti Palosaari; +Cc: linux-media, Hans Verkuil

Em Thu, 13 Mar 2014 17:17:50 +0200
Antti Palosaari <crope@iki.fi> escreveu:

> On 13.03.2014 15:57, Mauro Carvalho Chehab wrote:
> > Em Thu, 27 Feb 2014 02:30:11 +0200
> > Antti Palosaari <crope@iki.fi> escreveu:
> >
> >> Implement gain and bandwidth controls using v4l2 control framework.
> >> Pointer to control handler is provided by exported symbol.
> >>
> >> Cc: Mauro Carvalho Chehab <m.chehab@samsung.com>
> >> Cc: Hans Verkuil <hverkuil@xs4all.nl>
> >> Signed-off-by: Antti Palosaari <crope@iki.fi>
> >> ---
> >>   drivers/media/tuners/e4000.c      | 210 +++++++++++++++++++++++++++++++++++++-
> >>   drivers/media/tuners/e4000.h      |  14 +++
> >>   drivers/media/tuners/e4000_priv.h |  75 ++++++++++++++
> >>   3 files changed, 298 insertions(+), 1 deletion(-)
> >
> > ...
> >> diff --git a/drivers/media/tuners/e4000.h b/drivers/media/tuners/e4000.h
> >> index e74b8b2..989f2ea 100644
> >> --- a/drivers/media/tuners/e4000.h
> >> +++ b/drivers/media/tuners/e4000.h
> >> @@ -40,4 +40,18 @@ struct e4000_config {
> >>   	u32 clock;
> >>   };
> >>
> >> +#if IS_ENABLED(CONFIG_MEDIA_TUNER_E4000)
> >> +extern struct v4l2_ctrl_handler *e4000_get_ctrl_handler(
> >> +		struct dvb_frontend *fe
> >> +);
> >> +#else
> >> +static inline struct v4l2_ctrl_handler *e4000_get_ctrl_handler(
> >> +		struct dvb_frontend *fe
> >> +)
> >> +{
> >> +	pr_warn("%s: driver disabled by Kconfig\n", __func__);
> >> +	return NULL;
> >> +}
> >> +#endif
> >> +
> >>   #endif
> >
> > There are two things to be noticed here:
> >
> > 1) Please don't add any EXPORT_SYMBOL() on a pure I2C module. You
> > should, instead, use the subdev calls, in order to callback a
> > function provided by the module;
> 
> That means, I have to implement it as a V4L subdev driver then...

Yes, or to create some other type of binding. IMO, we should move
the subdev interface one level up, as we'll need to use it also
for some other DVB drivers, with a different set of callbacks.

> Is there any problem to leave as it is? It just only provides control 
> handler using that export. If you look those existing dvb frontend or 
> tuner drivers there is many kind of resources exported just similarly 
> (example DibCom PID filters, af9033 pid filters), so I cannot see why 
> that should be different.

Well, try googling for:
	undefined reference to `dib3000mc_get_tuner_i2c_master'

And you'll see that having more than one export on a DVB frontend
driver doesn't work.

If you want to see what happens, try to compile rtl28xxu builtin
and e4000 as a module.

What happens is that dvb_attach() will request the tuner module, 
if the driver provides just the foo_attach. 

If you see what dvb_attach() does, it is actually a kind of
"request_module" that uses a symbol name instead of the module
name, plus a dynamically solved call to the attach function. 
It also increments the loaded module kref (but without assigning
the module caller ownership).

So, it is actually a dirty hack.

In your case, rtl28xxu builtin will have a hard reference for
e4000_get_ctrl_handler, with will cause a compilation breakage
if e4000 is compiled as module.

What the previous I2C model did, and v4l2 subdev does, is to provide
a way for the driver to register a set of callbacks. This way,
all that the caller driver needs to do is to check if the callback
is set. If so, calls it.

> 
> > 2) As you're now using request_module(), you don't need to use
> > #if IS_ENABLED() anymore. It is up to the module to register
> > itself as a V4L2 subdevice. The caller module should use the
> > subdevice interface to run the callbacks.
> >
> > If you don't to that, you'll have several issues with the
> > building system.
> 
> So basically you are saying I should implement that driver as a V4L 
> subdev too?

Basically, I'm saying that your patch will break compilation, and,
at a first glance, the subdev approach is the better way to solve it.
> 
> regards
> Antti
> 


-- 

Regards,
Mauro

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [REVIEW PATCH 08/16] rtl2832_sdr: Realtek RTL2832 SDR driver module
  2014-02-14 14:57   ` Hans Verkuil
@ 2014-02-27  0:42     ` Antti Palosaari
  0 siblings, 0 replies; 24+ messages in thread
From: Antti Palosaari @ 2014-02-27  0:42 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media

Moikka!

On 14.02.2014 16:57, Hans Verkuil wrote:
> On 02/11/2014 03:04 AM, Antti Palosaari wrote:

>> +static int rtl2832_sdr_stop_streaming(struct vb2_queue *vq)
>> +{
>> +	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
>> +	dev_dbg(&s->udev->dev, "%s:\n", __func__);
>> +
>> +	if (mutex_lock_interruptible(&s->v4l2_lock))
>> +		return -ERESTARTSYS;
>
> Just use mutex_lock here. The return code of stop_streaming is never checked
> so stop_streaming must always succeed. Personally I am in favor of changing
> the return code of stop_streaming to void since it simply doesn't make sense
> to return an error here.

I decided not to change that at this time. My brains are so sleepy just 
now that I cannot think about it on the level I would like. IIRC did 
similar change to DVB USB year or two back as it happens some signal was 
coming from userspace (crtl+C) and it breaks out too early without 
putting hw to sleep. So I think that there should not be interruptible 
mutex in any patch where is I/O done. But that is similarly to other 
drivers too, feel free to fix all those if you wish :)

regards
Antti

-- 
http://palosaari.fi/

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [REVIEW PATCH 08/16] rtl2832_sdr: Realtek RTL2832 SDR driver module
  2014-02-11  2:04 ` [REVIEW PATCH 08/16] rtl2832_sdr: Realtek RTL2832 SDR driver module Antti Palosaari
@ 2014-02-14 14:57   ` Hans Verkuil
  2014-02-27  0:42     ` Antti Palosaari
  0 siblings, 1 reply; 24+ messages in thread
From: Hans Verkuil @ 2014-02-14 14:57 UTC (permalink / raw)
  To: Antti Palosaari; +Cc: linux-media

On 02/11/2014 03:04 AM, Antti Palosaari wrote:
> Implement SDR driver for Realtek RTL2832U chip as a DVB extension
> module. SDR module is attached by DVB USB RTL28XXU driver as a DVB
> SEC (satellite equipment controller) module. Abusing unused SEC here
> has no harm as that is DVB-T only frontend.
> 
> SDR functionality is provided by RTL2832 DVB-T demodulator. I suspect
> it is originally planned for DAB and FM, but it could be abused general
> SDR, due to modern silicon tuners that has wide frequency range and a
> lot of configurable parameters (filters, gains, ...).
> 
> http://thread.gmane.org/gmane.linux.drivers.video-input-infrastructure/44461
> 
> Signed-off-by: Antti Palosaari <crope@iki.fi>
> ---
>  drivers/staging/media/Kconfig                    |    2 +
>  drivers/staging/media/Makefile                   |    2 +
>  drivers/staging/media/rtl2832u_sdr/Kconfig       |    7 +
>  drivers/staging/media/rtl2832u_sdr/Makefile      |    6 +
>  drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c | 1470 ++++++++++++++++++++++
>  drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h |   51 +
>  6 files changed, 1538 insertions(+)
>  create mode 100644 drivers/staging/media/rtl2832u_sdr/Kconfig
>  create mode 100644 drivers/staging/media/rtl2832u_sdr/Makefile
>  create mode 100644 drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
>  create mode 100644 drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h
> 
> diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
> index 22b0c9d..a9f2e63 100644
> --- a/drivers/staging/media/Kconfig
> +++ b/drivers/staging/media/Kconfig
> @@ -41,6 +41,8 @@ source "drivers/staging/media/solo6x10/Kconfig"
>  
>  source "drivers/staging/media/omap4iss/Kconfig"
>  
> +source "drivers/staging/media/rtl2832u_sdr/Kconfig"
> +
>  # Keep LIRC at the end, as it has sub-menus
>  source "drivers/staging/media/lirc/Kconfig"
>  
> diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
> index bedc62a..8e2c5d2 100644
> --- a/drivers/staging/media/Makefile
> +++ b/drivers/staging/media/Makefile
> @@ -11,3 +11,5 @@ obj-$(CONFIG_VIDEO_OMAP4)	+= omap4iss/
>  obj-$(CONFIG_USB_SN9C102)       += sn9c102/
>  obj-$(CONFIG_VIDEO_OMAP2)       += omap24xx/
>  obj-$(CONFIG_VIDEO_TCM825X)     += omap24xx/
> +obj-$(CONFIG_DVB_RTL2832_SDR)	+= rtl2832u_sdr/
> +
> diff --git a/drivers/staging/media/rtl2832u_sdr/Kconfig b/drivers/staging/media/rtl2832u_sdr/Kconfig
> new file mode 100644
> index 0000000..3ede5fe
> --- /dev/null
> +++ b/drivers/staging/media/rtl2832u_sdr/Kconfig
> @@ -0,0 +1,7 @@
> +config DVB_RTL2832_SDR
> +	tristate "Realtek RTL2832 SDR"
> +	depends on USB && DVB_CORE && I2C && VIDEO_V4L2 && DVB_USB_RTL28XXU
> +	select DVB_RTL2832
> +	select VIDEOBUF2_VMALLOC
> +	default m if !MEDIA_SUBDRV_AUTOSELECT
> +
> diff --git a/drivers/staging/media/rtl2832u_sdr/Makefile b/drivers/staging/media/rtl2832u_sdr/Makefile
> new file mode 100644
> index 0000000..7e00a0d
> --- /dev/null
> +++ b/drivers/staging/media/rtl2832u_sdr/Makefile
> @@ -0,0 +1,6 @@
> +obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832_sdr.o
> +
> +ccflags-y += -Idrivers/media/dvb-core
> +ccflags-y += -Idrivers/media/dvb-frontends
> +ccflags-y += -Idrivers/media/tuners
> +ccflags-y += -Idrivers/media/usb/dvb-usb-v2
> diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
> new file mode 100644
> index 0000000..9265424
> --- /dev/null
> +++ b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
> @@ -0,0 +1,1470 @@
> +/*
> + * Realtek RTL2832U SDR driver
> + *
> + * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
> + *
> + *    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; either version 2 of the License, or
> + *    (at your option) any later version.
> + *
> + *    This program is distributed in the hope that it will be useful,
> + *    but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *    GNU General Public License for more details.
> + *
> + *    You should have received a copy of the GNU General Public License along
> + *    with this program; if not, write to the Free Software Foundation, Inc.,
> + *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * GNU Radio plugin "gr-kernel" for device usage will be on:
> + * http://git.linuxtv.org/anttip/gr-kernel.git
> + *
> + */
> +
> +#include "dvb_frontend.h"
> +#include "rtl2832_sdr.h"
> +#include "dvb_usb.h"
> +
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-event.h>
> +#include <media/videobuf2-vmalloc.h>
> +
> +#include <linux/jiffies.h>
> +#include <linux/math64.h>
> +
> +#define MAX_BULK_BUFS            (10)
> +#define BULK_BUFFER_SIZE         (128 * 512)
> +
> +static const struct v4l2_frequency_band bands_adc[] = {
> +	{
> +		.tuner = 0,
> +		.type = V4L2_TUNER_ADC,
> +		.index = 0,
> +		.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
> +		.rangelow   =  300000,
> +		.rangehigh  =  300000,
> +	},
> +	{
> +		.tuner = 0,
> +		.type = V4L2_TUNER_ADC,
> +		.index = 1,
> +		.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
> +		.rangelow   =  900001,
> +		.rangehigh  = 2800000,
> +	},
> +	{
> +		.tuner = 0,
> +		.type = V4L2_TUNER_ADC,
> +		.index = 2,
> +		.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
> +		.rangelow   = 3200000,
> +		.rangehigh  = 3200000,
> +	},
> +};
> +
> +static const struct v4l2_frequency_band bands_fm[] = {
> +	{
> +		.tuner = 1,
> +		.type = V4L2_TUNER_RF,
> +		.index = 0,
> +		.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
> +		.rangelow   =    50000000,
> +		.rangehigh  =  2000000000,
> +	},
> +};
> +
> +/* stream formats */
> +struct rtl2832_sdr_format {
> +	char	*name;
> +	u32	pixelformat;
> +};
> +
> +static struct rtl2832_sdr_format formats[] = {
> +	{
> +		.name		= "IQ U8",
> +		.pixelformat	=  V4L2_SDR_FMT_CU8,
> +	}, {
> +		.name		= "IQ U16LE (emulated)",
> +		.pixelformat	= V4L2_SDR_FMT_CU16LE,
> +	},
> +};
> +
> +static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats);
> +
> +/* intermediate buffers with raw data from the USB device */
> +struct rtl2832_sdr_frame_buf {
> +	struct vb2_buffer vb;   /* common v4l buffer stuff -- must be first */
> +	struct list_head list;
> +};
> +
> +struct rtl2832_sdr_state {
> +#define POWER_ON           (1 << 1)
> +#define URB_BUF            (1 << 2)
> +	unsigned long flags;
> +
> +	const struct rtl2832_config *cfg;
> +	struct dvb_frontend *fe;
> +	struct dvb_usb_device *d;
> +	struct i2c_adapter *i2c;
> +	u8 bank;
> +
> +	struct video_device vdev;
> +	struct v4l2_device v4l2_dev;
> +
> +	/* videobuf2 queue and queued buffers list */
> +	struct vb2_queue vb_queue;
> +	struct list_head queued_bufs;
> +	spinlock_t queued_bufs_lock; /* Protects queued_bufs */
> +
> +	/* Note if taking both locks v4l2_lock must always be locked first! */
> +	struct mutex v4l2_lock;      /* Protects everything else */
> +	struct mutex vb_queue_lock;  /* Protects vb_queue and capt_file */
> +
> +	/* Pointer to our usb_device, will be NULL after unplug */
> +	struct usb_device *udev; /* Both mutexes most be hold when setting! */
> +
> +	unsigned int vb_full; /* vb is full and packets dropped */
> +
> +	struct urb     *urb_list[MAX_BULK_BUFS];
> +	int            buf_num;
> +	unsigned long  buf_size;
> +	u8             *buf_list[MAX_BULK_BUFS];
> +	dma_addr_t     dma_addr[MAX_BULK_BUFS];
> +	int urbs_initialized;
> +	int urbs_submitted;
> +
> +	unsigned int f_adc, f_tuner;
> +	u32 pixelformat;
> +
> +	/* Controls */
> +	struct v4l2_ctrl_handler hdl;
> +	struct v4l2_ctrl *bandwidth_auto;
> +	struct v4l2_ctrl *bandwidth;
> +
> +	/* for sample rate calc */
> +	unsigned int sample;
> +	unsigned int sample_measured;
> +	unsigned long jiffies_next;
> +};
> +
> +/* write multiple hardware registers */
> +static int rtl2832_sdr_wr(struct rtl2832_sdr_state *s, u8 reg, const u8 *val,
> +		int len)
> +{
> +	int ret;
> +	u8 buf[1 + len];
> +	struct i2c_msg msg[1] = {
> +		{
> +			.addr = s->cfg->i2c_addr,
> +			.flags = 0,
> +			.len = 1 + len,
> +			.buf = buf,
> +		}
> +	};
> +
> +	buf[0] = reg;
> +	memcpy(&buf[1], val, len);
> +
> +	ret = i2c_transfer(s->i2c, msg, 1);
> +	if (ret == 1) {
> +		ret = 0;
> +	} else {
> +		dev_err(&s->i2c->dev,
> +			"%s: I2C wr failed=%d reg=%02x len=%d\n",
> +			KBUILD_MODNAME, ret, reg, len);
> +		ret = -EREMOTEIO;
> +	}
> +	return ret;
> +}
> +
> +/* read multiple hardware registers */
> +static int rtl2832_sdr_rd(struct rtl2832_sdr_state *s, u8 reg, u8 *val, int len)
> +{
> +	int ret;
> +	struct i2c_msg msg[2] = {
> +		{
> +			.addr = s->cfg->i2c_addr,
> +			.flags = 0,
> +			.len = 1,
> +			.buf = &reg,
> +		}, {
> +			.addr = s->cfg->i2c_addr,
> +			.flags = I2C_M_RD,
> +			.len = len,
> +			.buf = val,
> +		}
> +	};
> +
> +	ret = i2c_transfer(s->i2c, msg, 2);
> +	if (ret == 2) {
> +		ret = 0;
> +	} else {
> +		dev_err(&s->i2c->dev,
> +				"%s: I2C rd failed=%d reg=%02x len=%d\n",
> +				KBUILD_MODNAME, ret, reg, len);
> +		ret = -EREMOTEIO;
> +	}
> +	return ret;
> +}
> +
> +/* write multiple registers */
> +static int rtl2832_sdr_wr_regs(struct rtl2832_sdr_state *s, u16 reg,
> +		const u8 *val, int len)
> +{
> +	int ret;
> +	u8 reg2 = (reg >> 0) & 0xff;
> +	u8 bank = (reg >> 8) & 0xff;
> +
> +	/* switch bank if needed */
> +	if (bank != s->bank) {
> +		ret = rtl2832_sdr_wr(s, 0x00, &bank, 1);
> +		if (ret)
> +			return ret;
> +
> +		s->bank = bank;
> +	}
> +
> +	return rtl2832_sdr_wr(s, reg2, val, len);
> +}
> +
> +/* read multiple registers */
> +static int rtl2832_sdr_rd_regs(struct rtl2832_sdr_state *s, u16 reg, u8 *val,
> +		int len)
> +{
> +	int ret;
> +	u8 reg2 = (reg >> 0) & 0xff;
> +	u8 bank = (reg >> 8) & 0xff;
> +
> +	/* switch bank if needed */
> +	if (bank != s->bank) {
> +		ret = rtl2832_sdr_wr(s, 0x00, &bank, 1);
> +		if (ret)
> +			return ret;
> +
> +		s->bank = bank;
> +	}
> +
> +	return rtl2832_sdr_rd(s, reg2, val, len);
> +}
> +
> +/* write single register */
> +static int rtl2832_sdr_wr_reg(struct rtl2832_sdr_state *s, u16 reg, u8 val)
> +{
> +	return rtl2832_sdr_wr_regs(s, reg, &val, 1);
> +}
> +
> +#if 0
> +/* read single register */
> +static int rtl2832_sdr_rd_reg(struct rtl2832_sdr_state *s, u16 reg, u8 *val)
> +{
> +	return rtl2832_sdr_rd_regs(s, reg, val, 1);
> +}
> +#endif
> +
> +/* write single register with mask */
> +static int rtl2832_sdr_wr_reg_mask(struct rtl2832_sdr_state *s, u16 reg,
> +		u8 val, u8 mask)
> +{
> +	int ret;
> +	u8 tmp;
> +
> +	/* no need for read if whole reg is written */
> +	if (mask != 0xff) {
> +		ret = rtl2832_sdr_rd_regs(s, reg, &tmp, 1);
> +		if (ret)
> +			return ret;
> +
> +		val &= mask;
> +		tmp &= ~mask;
> +		val |= tmp;
> +	}
> +
> +	return rtl2832_sdr_wr_regs(s, reg, &val, 1);
> +}
> +
> +#if 0
> +/* read single register with mask */
> +static int rtl2832_sdr_rd_reg_mask(struct rtl2832_sdr_state *s, u16 reg,
> +		u8 *val, u8 mask)
> +{
> +	int ret, i;
> +	u8 tmp;
> +
> +	ret = rtl2832_sdr_rd_regs(s, reg, &tmp, 1);
> +	if (ret)
> +		return ret;
> +
> +	tmp &= mask;
> +
> +	/* find position of the first bit */
> +	for (i = 0; i < 8; i++) {
> +		if ((mask >> i) & 0x01)
> +			break;
> +	}
> +	*val = tmp >> i;
> +
> +	return 0;
> +}
> +#endif
> +
> +/* Private functions */
> +static struct rtl2832_sdr_frame_buf *rtl2832_sdr_get_next_fill_buf(
> +		struct rtl2832_sdr_state *s)
> +{
> +	unsigned long flags = 0;
> +	struct rtl2832_sdr_frame_buf *buf = NULL;
> +
> +	spin_lock_irqsave(&s->queued_bufs_lock, flags);
> +	if (list_empty(&s->queued_bufs))
> +		goto leave;
> +
> +	buf = list_entry(s->queued_bufs.next,
> +			struct rtl2832_sdr_frame_buf, list);
> +	list_del(&buf->list);
> +leave:
> +	spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
> +	return buf;
> +}
> +
> +static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_state *s,
> +		void *dst, const u8 *src, unsigned int src_len)
> +{
> +	unsigned int dst_len;
> +
> +	if (s->pixelformat ==  V4L2_SDR_FMT_CU8) {
> +		/* native stream, no need to convert */
> +		memcpy(dst, src, src_len);
> +		dst_len = src_len;
> +	} else if (s->pixelformat == V4L2_SDR_FMT_CU16LE) {
> +		/* convert u8 to u16 */
> +		unsigned int i;
> +		u16 *u16dst = dst;
> +		for (i = 0; i < src_len; i++)
> +			*u16dst++ = (src[i] << 8) | (src[i] >> 0);
> +		dst_len = 2 * src_len;
> +	} else {
> +		dst_len = 0;
> +	}
> +
> +	/* calculate samping rate and output it in 10 seconds intervals */
> +	if (unlikely(time_is_before_jiffies(s->jiffies_next))) {
> +#define MSECS 10000UL
> +		unsigned int samples = s->sample - s->sample_measured;
> +		s->jiffies_next = jiffies + msecs_to_jiffies(MSECS);
> +		s->sample_measured = s->sample;
> +		dev_dbg(&s->udev->dev,
> +				"slen=%d samples=%u msecs=%lu sampling rate=%lu\n",
> +				src_len, samples, MSECS,
> +				samples * 1000UL / MSECS);
> +	}
> +
> +	/* total number of I+Q pairs */
> +	s->sample += src_len / 2;
> +
> +	return dst_len;
> +}
> +
> +/*
> + * This gets called for the bulk stream pipe. This is done in interrupt
> + * time, so it has to be fast, not crash, and not stall. Neat.
> + */
> +static void rtl2832_sdr_urb_complete(struct urb *urb)
> +{
> +	struct rtl2832_sdr_state *s = urb->context;
> +	struct rtl2832_sdr_frame_buf *fbuf;
> +
> +	dev_dbg_ratelimited(&s->udev->dev,
> +			"%s: status=%d length=%d/%d errors=%d\n",
> +			__func__, urb->status, urb->actual_length,
> +			urb->transfer_buffer_length, urb->error_count);
> +
> +	switch (urb->status) {
> +	case 0:             /* success */
> +	case -ETIMEDOUT:    /* NAK */
> +		break;
> +	case -ECONNRESET:   /* kill */
> +	case -ENOENT:
> +	case -ESHUTDOWN:
> +		return;
> +	default:            /* error */
> +		dev_err_ratelimited(&s->udev->dev, "urb failed=%d\n",
> +				urb->status);
> +		break;
> +	}
> +
> +	if (likely(urb->actual_length > 0)) {
> +		void *ptr;
> +		unsigned int len;
> +		/* get free framebuffer */
> +		fbuf = rtl2832_sdr_get_next_fill_buf(s);
> +		if (unlikely(fbuf == NULL)) {
> +			s->vb_full++;
> +			dev_notice_ratelimited(&s->udev->dev,
> +					"videobuf is full, %d packets dropped\n",
> +					s->vb_full);
> +			goto skip;
> +		}
> +
> +		/* fill framebuffer */
> +		ptr = vb2_plane_vaddr(&fbuf->vb, 0);
> +		len = rtl2832_sdr_convert_stream(s, ptr, urb->transfer_buffer,
> +				urb->actual_length);
> +		vb2_set_plane_payload(&fbuf->vb, 0, len);
> +		vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE);
> +	}
> +skip:
> +	usb_submit_urb(urb, GFP_ATOMIC);
> +}
> +
> +static int rtl2832_sdr_kill_urbs(struct rtl2832_sdr_state *s)
> +{
> +	int i;
> +
> +	for (i = s->urbs_submitted - 1; i >= 0; i--) {
> +		dev_dbg(&s->udev->dev, "%s: kill urb=%d\n", __func__, i);
> +		/* stop the URB */
> +		usb_kill_urb(s->urb_list[i]);
> +	}
> +	s->urbs_submitted = 0;
> +
> +	return 0;
> +}
> +
> +static int rtl2832_sdr_submit_urbs(struct rtl2832_sdr_state *s)
> +{
> +	int i, ret;
> +
> +	for (i = 0; i < s->urbs_initialized; i++) {
> +		dev_dbg(&s->udev->dev, "%s: submit urb=%d\n", __func__, i);
> +		ret = usb_submit_urb(s->urb_list[i], GFP_ATOMIC);
> +		if (ret) {
> +			dev_err(&s->udev->dev,
> +					"Could not submit urb no. %d - get them all back\n",
> +					i);
> +			rtl2832_sdr_kill_urbs(s);
> +			return ret;
> +		}
> +		s->urbs_submitted++;
> +	}
> +
> +	return 0;
> +}
> +
> +static int rtl2832_sdr_free_stream_bufs(struct rtl2832_sdr_state *s)
> +{
> +	if (s->flags & USB_STATE_URB_BUF) {
> +		while (s->buf_num) {
> +			s->buf_num--;
> +			dev_dbg(&s->udev->dev, "%s: free buf=%d\n",
> +					__func__, s->buf_num);
> +			usb_free_coherent(s->udev, s->buf_size,
> +					  s->buf_list[s->buf_num],
> +					  s->dma_addr[s->buf_num]);
> +		}
> +	}
> +	s->flags &= ~USB_STATE_URB_BUF;
> +
> +	return 0;
> +}
> +
> +static int rtl2832_sdr_alloc_stream_bufs(struct rtl2832_sdr_state *s)
> +{
> +	s->buf_num = 0;
> +	s->buf_size = BULK_BUFFER_SIZE;
> +
> +	dev_dbg(&s->udev->dev,
> +			"%s: all in all I will use %u bytes for streaming\n",
> +			__func__,  MAX_BULK_BUFS * BULK_BUFFER_SIZE);
> +
> +	for (s->buf_num = 0; s->buf_num < MAX_BULK_BUFS; s->buf_num++) {
> +		s->buf_list[s->buf_num] = usb_alloc_coherent(s->udev,
> +				BULK_BUFFER_SIZE, GFP_ATOMIC,
> +				&s->dma_addr[s->buf_num]);
> +		if (!s->buf_list[s->buf_num]) {
> +			dev_dbg(&s->udev->dev, "%s: alloc buf=%d failed\n",
> +					__func__, s->buf_num);
> +			rtl2832_sdr_free_stream_bufs(s);
> +			return -ENOMEM;
> +		}
> +
> +		dev_dbg(&s->udev->dev, "%s: alloc buf=%d %p (dma %llu)\n",
> +				__func__, s->buf_num,
> +				s->buf_list[s->buf_num],
> +				(long long)s->dma_addr[s->buf_num]);
> +		s->flags |= USB_STATE_URB_BUF;
> +	}
> +
> +	return 0;
> +}
> +
> +static int rtl2832_sdr_free_urbs(struct rtl2832_sdr_state *s)
> +{
> +	int i;
> +
> +	rtl2832_sdr_kill_urbs(s);
> +
> +	for (i = s->urbs_initialized - 1; i >= 0; i--) {
> +		if (s->urb_list[i]) {
> +			dev_dbg(&s->udev->dev, "%s: free urb=%d\n",
> +					__func__, i);
> +			/* free the URBs */
> +			usb_free_urb(s->urb_list[i]);
> +		}
> +	}
> +	s->urbs_initialized = 0;
> +
> +	return 0;
> +}
> +
> +static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_state *s)
> +{
> +	int i, j;
> +
> +	/* allocate the URBs */
> +	for (i = 0; i < MAX_BULK_BUFS; i++) {
> +		dev_dbg(&s->udev->dev, "%s: alloc urb=%d\n", __func__, i);
> +		s->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
> +		if (!s->urb_list[i]) {
> +			dev_dbg(&s->udev->dev, "%s: failed\n", __func__);
> +			for (j = 0; j < i; j++)
> +				usb_free_urb(s->urb_list[j]);
> +			return -ENOMEM;
> +		}
> +		usb_fill_bulk_urb(s->urb_list[i],
> +				s->udev,
> +				usb_rcvbulkpipe(s->udev, 0x81),
> +				s->buf_list[i],
> +				BULK_BUFFER_SIZE,
> +				rtl2832_sdr_urb_complete, s);
> +
> +		s->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
> +		s->urb_list[i]->transfer_dma = s->dma_addr[i];
> +		s->urbs_initialized++;
> +	}
> +
> +	return 0;
> +}
> +
> +/* Must be called with vb_queue_lock hold */
> +static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_state *s)
> +{
> +	unsigned long flags = 0;
> +	dev_dbg(&s->udev->dev, "%s:\n", __func__);
> +
> +	spin_lock_irqsave(&s->queued_bufs_lock, flags);
> +	while (!list_empty(&s->queued_bufs)) {
> +		struct rtl2832_sdr_frame_buf *buf;
> +		buf = list_entry(s->queued_bufs.next,
> +				struct rtl2832_sdr_frame_buf, list);
> +		list_del(&buf->list);
> +		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> +	}
> +	spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
> +}
> +
> +/* The user yanked out the cable... */
> +static void rtl2832_sdr_release_sec(struct dvb_frontend *fe)
> +{
> +	struct rtl2832_sdr_state *s = fe->sec_priv;
> +	dev_dbg(&s->udev->dev, "%s:\n", __func__);
> +
> +	mutex_lock(&s->vb_queue_lock);
> +	mutex_lock(&s->v4l2_lock);
> +	/* No need to keep the urbs around after disconnection */
> +	s->udev = NULL;
> +
> +	v4l2_device_disconnect(&s->v4l2_dev);
> +	video_unregister_device(&s->vdev);
> +	mutex_unlock(&s->v4l2_lock);
> +	mutex_unlock(&s->vb_queue_lock);
> +
> +	v4l2_device_put(&s->v4l2_dev);
> +
> +	fe->sec_priv = NULL;
> +}
> +
> +static int rtl2832_sdr_querycap(struct file *file, void *fh,
> +		struct v4l2_capability *cap)
> +{
> +	struct rtl2832_sdr_state *s = video_drvdata(file);
> +	dev_dbg(&s->udev->dev, "%s:\n", __func__);
> +
> +	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
> +	strlcpy(cap->card, s->vdev.name, sizeof(cap->card));
> +	usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info));
> +	cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING |
> +			V4L2_CAP_READWRITE | V4L2_CAP_TUNER;
> +	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
> +	return 0;
> +}
> +
> +/* Videobuf2 operations */
> +static int rtl2832_sdr_queue_setup(struct vb2_queue *vq,
> +		const struct v4l2_format *fmt, unsigned int *nbuffers,
> +		unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[])
> +{
> +	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
> +	dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers);
> +
> +	/* Absolute min and max number of buffers available for mmap() */
> +	*nbuffers = 32;

Huh? *nbuffers is the number of buffers that the application wants to allocate.
The queue_setup function only has to check that this isn't too low and set it
to whatever minimum is required, otherwise it should leave it alone.

This code suggests that the minimum is 32 buffers, which seems extreme.

> +	*nplanes = 1;
> +	/* 2 = max 16-bit sample returned */
> +	sizes[0] = PAGE_ALIGN(BULK_BUFFER_SIZE * 2);
> +	dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n",
> +			__func__, *nbuffers, sizes[0]);
> +	return 0;
> +}
> +
> +static int rtl2832_sdr_buf_prepare(struct vb2_buffer *vb)
> +{
> +	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue);
> +
> +	/* Don't allow queing new buffers after device disconnection */
> +	if (!s->udev)
> +		return -ENODEV;
> +
> +	return 0;
> +}
> +
> +static void rtl2832_sdr_buf_queue(struct vb2_buffer *vb)
> +{
> +	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue);
> +	struct rtl2832_sdr_frame_buf *buf =
> +			container_of(vb, struct rtl2832_sdr_frame_buf, vb);
> +	unsigned long flags = 0;
> +
> +	/* Check the device has not disconnected between prep and queuing */
> +	if (!s->udev) {
> +		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
> +		return;
> +	}
> +
> +	spin_lock_irqsave(&s->queued_bufs_lock, flags);
> +	list_add_tail(&buf->list, &s->queued_bufs);
> +	spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
> +}
> +
> +static int rtl2832_sdr_set_adc(struct rtl2832_sdr_state *s)
> +{
> +	struct dvb_frontend *fe = s->fe;
> +	int ret;
> +	unsigned int f_sr, f_if;
> +	u8 buf[4], u8tmp1, u8tmp2;
> +	u64 u64tmp;
> +	u32 u32tmp;
> +	dev_dbg(&s->udev->dev, "%s: f_adc=%u\n", __func__, s->f_adc);
> +
> +	if (!test_bit(POWER_ON, &s->flags))
> +		return 0;
> +
> +	if (s->f_adc == 0)
> +		return 0;
> +
> +	f_sr = s->f_adc;
> +
> +	ret = rtl2832_sdr_wr_regs(s, 0x13e, "\x00\x00", 2);
> +	if (ret)
> +		goto err;
> +
> +	ret = rtl2832_sdr_wr_regs(s, 0x115, "\x00\x00\x00\x00", 4);
> +	if (ret)
> +		goto err;
> +
> +	/* get IF from tuner */
> +	if (fe->ops.tuner_ops.get_if_frequency)
> +		ret = fe->ops.tuner_ops.get_if_frequency(fe, &f_if);
> +	else
> +		ret = -EINVAL;
> +
> +	if (ret)
> +		goto err;
> +
> +	/* program IF */
> +	u64tmp = f_if % s->cfg->xtal;
> +	u64tmp *= 0x400000;
> +	u64tmp = div_u64(u64tmp, s->cfg->xtal);
> +	u64tmp = -u64tmp;
> +	u32tmp = u64tmp & 0x3fffff;
> +
> +	dev_dbg(&s->udev->dev, "%s: f_if=%u if_ctl=%08x\n",
> +			__func__, f_if, u32tmp);
> +
> +	buf[0] = (u32tmp >> 16) & 0xff;
> +	buf[1] = (u32tmp >>  8) & 0xff;
> +	buf[2] = (u32tmp >>  0) & 0xff;
> +
> +	ret = rtl2832_sdr_wr_regs(s, 0x119, buf, 3);
> +	if (ret)
> +		goto err;
> +
> +	/* BB / IF mode */
> +	/* POR: 0x1b1=0x1f, 0x008=0x0d, 0x006=0x80 */
> +	if (f_if) {
> +		u8tmp1 = 0x1a; /* disable Zero-IF */
> +		u8tmp2 = 0x8d; /* enable ADC I */
> +	} else {
> +		u8tmp1 = 0x1b; /* enable Zero-IF, DC, IQ */
> +		u8tmp2 = 0xcd; /* enable ADC I, ADC Q */
> +	}
> +
> +	ret = rtl2832_sdr_wr_reg(s, 0x1b1, u8tmp1);
> +	if (ret)
> +		goto err;
> +
> +	ret = rtl2832_sdr_wr_reg(s, 0x008, u8tmp2);
> +	if (ret)
> +		goto err;
> +
> +	ret = rtl2832_sdr_wr_reg(s, 0x006, 0x80);
> +	if (ret)
> +		goto err;
> +
> +	/* program sampling rate (resampling down) */
> +	u32tmp = div_u64(s->cfg->xtal * 0x400000ULL, f_sr * 4U);
> +	u32tmp <<= 2;
> +	buf[0] = (u32tmp >> 24) & 0xff;
> +	buf[1] = (u32tmp >> 16) & 0xff;
> +	buf[2] = (u32tmp >>  8) & 0xff;
> +	buf[3] = (u32tmp >>  0) & 0xff;
> +	ret = rtl2832_sdr_wr_regs(s, 0x19f, buf, 4);
> +	if (ret)
> +		goto err;
> +
> +	/* low-pass filter */
> +	ret = rtl2832_sdr_wr_regs(s, 0x11c,
> +			"\xca\xdc\xd7\xd8\xe0\xf2\x0e\x35\x06\x50\x9c\x0d\x71\x11\x14\x71\x74\x19\x41\xa5",
> +			20);
> +	if (ret)
> +		goto err;
> +
> +	ret = rtl2832_sdr_wr_regs(s, 0x017, "\x11\x10", 2);
> +	if (ret)
> +		goto err;
> +
> +	/* mode */
> +	ret = rtl2832_sdr_wr_regs(s, 0x019, "\x05", 1);
> +	if (ret)
> +		goto err;
> +
> +	ret = rtl2832_sdr_wr_regs(s, 0x01a, "\x1b\x16\x0d\x06\x01\xff", 6);
> +	if (ret)
> +		goto err;
> +
> +	/* FSM */
> +	ret = rtl2832_sdr_wr_regs(s, 0x192, "\x00\xf0\x0f", 3);
> +	if (ret)
> +		goto err;
> +
> +	/* PID filter */
> +	ret = rtl2832_sdr_wr_regs(s, 0x061, "\x60", 1);
> +	if (ret)
> +		goto err;
> +
> +	/* used RF tuner based settings */
> +	switch (s->cfg->tuner) {
> +	case RTL2832_TUNER_E4000:
> +		ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x103, "\x5a", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x30", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x104, "\xd0", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x18", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x011, "\xd4", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1e5, "\xf0", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1d9, "\x00", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1db, "\x00", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1dd, "\x14", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1de, "\xec", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1d8, "\x0c", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1e6, "\x02", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1d7, "\x09", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x83", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x010, "\x49", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x87", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x85", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x013, "\x02", 1);
> +		break;
> +	case RTL2832_TUNER_FC0012:
> +	case RTL2832_TUNER_FC0013:
> +		ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x103, "\x5a", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x2c", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x104, "\xcc", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x16", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x011, "\xe9\xbf", 2);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1e5, "\xf0", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1d9, "\x00", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1db, "\x00", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1dd, "\x11", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1de, "\xef", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1d8, "\x0c", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1e6, "\x02", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1d7, "\x09", 1);
> +		break;
> +	case RTL2832_TUNER_R820T:
> +		ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x115, "\x01", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x103, "\x80", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x24", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x104, "\xcc", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x14", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
> +		ret = rtl2832_sdr_wr_regs(s, 0x011, "\xf4", 1);
> +		break;
> +	default:
> +		dev_notice(&s->udev->dev, "Unsupported tuner\n");
> +	}
> +
> +	/* software reset */
> +	ret = rtl2832_sdr_wr_reg_mask(s, 0x101, 0x04, 0x04);
> +	if (ret)
> +		goto err;
> +
> +	ret = rtl2832_sdr_wr_reg_mask(s, 0x101, 0x00, 0x04);
> +	if (ret)
> +		goto err;
> +err:
> +	return ret;
> +};
> +
> +static void rtl2832_sdr_unset_adc(struct rtl2832_sdr_state *s)
> +{
> +	int ret;
> +
> +	dev_dbg(&s->udev->dev, "%s:\n", __func__);
> +
> +	/* PID filter */
> +	ret = rtl2832_sdr_wr_regs(s, 0x061, "\xe0", 1);
> +	if (ret)
> +		goto err;
> +
> +	/* mode */
> +	ret = rtl2832_sdr_wr_regs(s, 0x019, "\x20", 1);
> +	if (ret)
> +		goto err;
> +
> +	ret = rtl2832_sdr_wr_regs(s, 0x017, "\x11\x10", 2);
> +	if (ret)
> +		goto err;
> +
> +	/* FSM */
> +	ret = rtl2832_sdr_wr_regs(s, 0x192, "\x00\x0f\xff", 3);
> +	if (ret)
> +		goto err;
> +
> +	ret = rtl2832_sdr_wr_regs(s, 0x13e, "\x40\x00", 2);
> +	if (ret)
> +		goto err;
> +
> +	ret = rtl2832_sdr_wr_regs(s, 0x115, "\x06\x3f\xce\xcc", 4);
> +	if (ret)
> +		goto err;
> +err:
> +	return;
> +};
> +
> +static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_state *s)
> +{
> +	struct dvb_frontend *fe = s->fe;
> +	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
> +	struct v4l2_ctrl *bandwidth_auto;
> +	struct v4l2_ctrl *bandwidth;
> +
> +	/*
> +	 * tuner RF (Hz)
> +	 */
> +	if (s->f_tuner == 0)
> +		return 0;
> +
> +	/*
> +	 * bandwidth (Hz)
> +	 */
> +	bandwidth_auto = v4l2_ctrl_find(&s->hdl, V4L2_CID_BANDWIDTH_AUTO);
> +	bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_BANDWIDTH);
> +	if (v4l2_ctrl_g_ctrl(bandwidth_auto)) {
> +		c->bandwidth_hz = s->f_adc;
> +		v4l2_ctrl_s_ctrl(bandwidth, s->f_adc);
> +	} else {
> +		c->bandwidth_hz = v4l2_ctrl_g_ctrl(bandwidth);
> +	}
> +
> +	c->frequency = s->f_tuner;
> +	c->delivery_system = SYS_DVBT;
> +
> +	dev_dbg(&s->udev->dev, "%s: frequency=%u bandwidth=%d\n",
> +			__func__, c->frequency, c->bandwidth_hz);
> +
> +	if (!test_bit(POWER_ON, &s->flags))
> +		return 0;
> +
> +	if (fe->ops.tuner_ops.set_params)
> +		fe->ops.tuner_ops.set_params(fe);
> +
> +	return 0;
> +};
> +
> +static int rtl2832_sdr_set_tuner(struct rtl2832_sdr_state *s)
> +{
> +	struct dvb_frontend *fe = s->fe;
> +
> +	dev_dbg(&s->udev->dev, "%s:\n", __func__);
> +
> +	if (fe->ops.tuner_ops.init)
> +		fe->ops.tuner_ops.init(fe);
> +
> +	return 0;
> +};
> +
> +static void rtl2832_sdr_unset_tuner(struct rtl2832_sdr_state *s)
> +{
> +	struct dvb_frontend *fe = s->fe;
> +
> +	dev_dbg(&s->udev->dev, "%s:\n", __func__);
> +
> +	if (fe->ops.tuner_ops.sleep)
> +		fe->ops.tuner_ops.sleep(fe);
> +
> +	return;
> +};
> +
> +static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> +	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
> +	int ret;
> +	dev_dbg(&s->udev->dev, "%s:\n", __func__);
> +
> +	if (!s->udev)
> +		return -ENODEV;
> +
> +	if (mutex_lock_interruptible(&s->v4l2_lock))
> +		return -ERESTARTSYS;
> +
> +	if (s->d->props->power_ctrl)
> +		s->d->props->power_ctrl(s->d, 1);
> +
> +	set_bit(POWER_ON, &s->flags);
> +
> +	ret = rtl2832_sdr_set_tuner(s);
> +	if (ret)
> +		goto err;
> +
> +	ret = rtl2832_sdr_set_tuner_freq(s);
> +	if (ret)
> +		goto err;
> +
> +	ret = rtl2832_sdr_set_adc(s);
> +	if (ret)
> +		goto err;
> +
> +	ret = rtl2832_sdr_alloc_stream_bufs(s);
> +	if (ret)
> +		goto err;
> +
> +	ret = rtl2832_sdr_alloc_urbs(s);
> +	if (ret)
> +		goto err;
> +
> +	ret = rtl2832_sdr_submit_urbs(s);
> +	if (ret)
> +		goto err;
> +
> +err:
> +	mutex_unlock(&s->v4l2_lock);
> +
> +	return ret;
> +}
> +
> +static int rtl2832_sdr_stop_streaming(struct vb2_queue *vq)
> +{
> +	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
> +	dev_dbg(&s->udev->dev, "%s:\n", __func__);
> +
> +	if (mutex_lock_interruptible(&s->v4l2_lock))
> +		return -ERESTARTSYS;

Just use mutex_lock here. The return code of stop_streaming is never checked
so stop_streaming must always succeed. Personally I am in favor of changing
the return code of stop_streaming to void since it simply doesn't make sense
to return an error here.

> +
> +	rtl2832_sdr_kill_urbs(s);
> +	rtl2832_sdr_free_urbs(s);
> +	rtl2832_sdr_free_stream_bufs(s);
> +	rtl2832_sdr_cleanup_queued_bufs(s);
> +	rtl2832_sdr_unset_adc(s);
> +	rtl2832_sdr_unset_tuner(s);
> +
> +	clear_bit(POWER_ON, &s->flags);
> +
> +	if (s->d->props->power_ctrl)
> +		s->d->props->power_ctrl(s->d, 0);
> +
> +	mutex_unlock(&s->v4l2_lock);
> +
> +	return 0;
> +}
> +
> +static struct vb2_ops rtl2832_sdr_vb2_ops = {
> +	.queue_setup            = rtl2832_sdr_queue_setup,
> +	.buf_prepare            = rtl2832_sdr_buf_prepare,
> +	.buf_queue              = rtl2832_sdr_buf_queue,
> +	.start_streaming        = rtl2832_sdr_start_streaming,
> +	.stop_streaming         = rtl2832_sdr_stop_streaming,
> +	.wait_prepare           = vb2_ops_wait_prepare,
> +	.wait_finish            = vb2_ops_wait_finish,
> +};
> +
> +static int rtl2832_sdr_g_tuner(struct file *file, void *priv,
> +		struct v4l2_tuner *v)
> +{
> +	struct rtl2832_sdr_state *s = video_drvdata(file);
> +	dev_dbg(&s->udev->dev, "%s: index=%d type=%d\n",
> +			__func__, v->index, v->type);
> +
> +	if (v->index == 0) {
> +		strlcpy(v->name, "ADC: Realtek RTL2832", sizeof(v->name));
> +		v->type = V4L2_TUNER_ADC;
> +		v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
> +		v->rangelow =   300000;
> +		v->rangehigh = 3200000;
> +	} else if (v->index == 1) {
> +		strlcpy(v->name, "RF: <unknown>", sizeof(v->name));
> +		v->type = V4L2_TUNER_RF;
> +		v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
> +		v->rangelow =    50000000;
> +		v->rangehigh = 2000000000;
> +	} else {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int rtl2832_sdr_s_tuner(struct file *file, void *priv,
> +		const struct v4l2_tuner *v)
> +{
> +	struct rtl2832_sdr_state *s = video_drvdata(file);
> +	dev_dbg(&s->udev->dev, "%s:\n", __func__);
> +
> +	return 0;
> +}
> +
> +static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv,
> +		struct v4l2_frequency_band *band)
> +{
> +	struct rtl2832_sdr_state *s = video_drvdata(file);
> +	dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n",
> +			__func__, band->tuner, band->type, band->index);
> +
> +	if (band->tuner == 0) {
> +		if (band->index >= ARRAY_SIZE(bands_adc))
> +			return -EINVAL;
> +
> +		*band = bands_adc[band->index];
> +	} else if (band->tuner == 1) {
> +		if (band->index >= ARRAY_SIZE(bands_fm))
> +			return -EINVAL;
> +
> +		*band = bands_fm[band->index];
> +	} else {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int rtl2832_sdr_g_frequency(struct file *file, void *priv,
> +		struct v4l2_frequency *f)
> +{
> +	struct rtl2832_sdr_state *s = video_drvdata(file);
> +	int ret  = 0;
> +	dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n",
> +			__func__, f->tuner, f->type);
> +
> +	if (f->tuner == 0)
> +		f->frequency = s->f_adc;
> +	else if (f->tuner == 1)
> +		f->frequency = s->f_tuner;
> +	else
> +		return -EINVAL;
> +
> +	return ret;
> +}
> +
> +static int rtl2832_sdr_s_frequency(struct file *file, void *priv,
> +		const struct v4l2_frequency *f)
> +{
> +	struct rtl2832_sdr_state *s = video_drvdata(file);
> +	int ret, band;
> +
> +	dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n",
> +			__func__, f->tuner, f->type, f->frequency);
> +
> +	/* ADC band midpoints */
> +	#define BAND_ADC_0 ((bands_adc[0].rangehigh + bands_adc[1].rangelow) / 2)
> +	#define BAND_ADC_1 ((bands_adc[1].rangehigh + bands_adc[2].rangelow) / 2)
> +
> +	if (f->tuner == 0 && f->type == V4L2_TUNER_ADC) {
> +		if (f->frequency < BAND_ADC_0)
> +			band = 0;
> +		else if (f->frequency < BAND_ADC_1)
> +			band = 1;
> +		else
> +			band = 2;
> +
> +		s->f_adc = clamp_t(unsigned int, f->frequency,
> +				bands_adc[band].rangelow,
> +				bands_adc[band].rangehigh);
> +
> +		dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n",
> +				__func__, s->f_adc);
> +		ret = rtl2832_sdr_set_adc(s);
> +	} else if (f->tuner == 1) {
> +		s->f_tuner = f->frequency;
> +		dev_dbg(&s->udev->dev, "%s: RF frequency=%u Hz\n",
> +				__func__, f->frequency);
> +
> +		ret = rtl2832_sdr_set_tuner_freq(s);
> +	} else {
> +		ret = -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +static int rtl2832_sdr_enum_fmt_sdr_cap(struct file *file, void *priv,
> +		struct v4l2_fmtdesc *f)
> +{
> +	struct rtl2832_sdr_state *s = video_drvdata(file);
> +	dev_dbg(&s->udev->dev, "%s:\n", __func__);
> +
> +	if (f->index >= NUM_FORMATS)
> +		return -EINVAL;
> +
> +	strlcpy(f->description, formats[f->index].name, sizeof(f->description));
> +	f->pixelformat = formats[f->index].pixelformat;
> +
> +	return 0;
> +}
> +
> +static int rtl2832_sdr_g_fmt_sdr_cap(struct file *file, void *priv,
> +		struct v4l2_format *f)
> +{
> +	struct rtl2832_sdr_state *s = video_drvdata(file);
> +	dev_dbg(&s->udev->dev, "%s:\n", __func__);
> +
> +	f->fmt.sdr.pixelformat = s->pixelformat;
> +
> +	return 0;
> +}
> +
> +static int rtl2832_sdr_s_fmt_sdr_cap(struct file *file, void *priv,
> +		struct v4l2_format *f)
> +{
> +	struct rtl2832_sdr_state *s = video_drvdata(file);
> +	struct vb2_queue *q = &s->vb_queue;
> +	int i;
> +	dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
> +			(char *)&f->fmt.sdr.pixelformat);
> +
> +	if (vb2_is_busy(q))
> +		return -EBUSY;
> +
> +	for (i = 0; i < NUM_FORMATS; i++) {
> +		if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
> +			s->pixelformat = f->fmt.sdr.pixelformat;
> +			return 0;
> +		}
> +	}
> +
> +	f->fmt.sdr.pixelformat = formats[0].pixelformat;
> +	s->pixelformat = formats[0].pixelformat;
> +
> +	return 0;
> +}
> +
> +static int rtl2832_sdr_try_fmt_sdr_cap(struct file *file, void *priv,
> +		struct v4l2_format *f)
> +{
> +	struct rtl2832_sdr_state *s = video_drvdata(file);
> +	int i;
> +	dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
> +			(char *)&f->fmt.sdr.pixelformat);
> +
> +	for (i = 0; i < NUM_FORMATS; i++) {
> +		if (formats[i].pixelformat == f->fmt.sdr.pixelformat)
> +			return 0;
> +	}
> +
> +	f->fmt.sdr.pixelformat = formats[0].pixelformat;
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_ioctl_ops rtl2832_sdr_ioctl_ops = {
> +	.vidioc_querycap          = rtl2832_sdr_querycap,
> +
> +	.vidioc_enum_fmt_sdr_cap  = rtl2832_sdr_enum_fmt_sdr_cap,
> +	.vidioc_g_fmt_sdr_cap     = rtl2832_sdr_g_fmt_sdr_cap,
> +	.vidioc_s_fmt_sdr_cap     = rtl2832_sdr_s_fmt_sdr_cap,
> +	.vidioc_try_fmt_sdr_cap   = rtl2832_sdr_try_fmt_sdr_cap,
> +
> +	.vidioc_reqbufs           = vb2_ioctl_reqbufs,
> +	.vidioc_create_bufs       = vb2_ioctl_create_bufs,
> +	.vidioc_prepare_buf       = vb2_ioctl_prepare_buf,
> +	.vidioc_querybuf          = vb2_ioctl_querybuf,
> +	.vidioc_qbuf              = vb2_ioctl_qbuf,
> +	.vidioc_dqbuf             = vb2_ioctl_dqbuf,
> +
> +	.vidioc_streamon          = vb2_ioctl_streamon,
> +	.vidioc_streamoff         = vb2_ioctl_streamoff,
> +
> +	.vidioc_g_tuner           = rtl2832_sdr_g_tuner,
> +	.vidioc_s_tuner           = rtl2832_sdr_s_tuner,
> +
> +	.vidioc_enum_freq_bands   = rtl2832_sdr_enum_freq_bands,
> +	.vidioc_g_frequency       = rtl2832_sdr_g_frequency,
> +	.vidioc_s_frequency       = rtl2832_sdr_s_frequency,
> +
> +	.vidioc_subscribe_event   = v4l2_ctrl_subscribe_event,
> +	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +	.vidioc_log_status        = v4l2_ctrl_log_status,
> +};
> +
> +static const struct v4l2_file_operations rtl2832_sdr_fops = {
> +	.owner                    = THIS_MODULE,
> +	.open                     = v4l2_fh_open,
> +	.release                  = vb2_fop_release,
> +	.read                     = vb2_fop_read,
> +	.poll                     = vb2_fop_poll,
> +	.mmap                     = vb2_fop_mmap,
> +	.unlocked_ioctl           = video_ioctl2,
> +};
> +
> +static struct video_device rtl2832_sdr_template = {
> +	.name                     = "Realtek RTL2832 SDR",
> +	.release                  = video_device_release_empty,
> +	.fops                     = &rtl2832_sdr_fops,
> +	.ioctl_ops                = &rtl2832_sdr_ioctl_ops,
> +};
> +
> +static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct rtl2832_sdr_state *s =
> +			container_of(ctrl->handler, struct rtl2832_sdr_state,
> +					hdl);
> +	struct dvb_frontend *fe = s->fe;
> +	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
> +	int ret;
> +	dev_dbg(&s->udev->dev,
> +			"%s: id=%d name=%s val=%d min=%d max=%d step=%d\n",
> +			__func__, ctrl->id, ctrl->name, ctrl->val,
> +			ctrl->minimum, ctrl->maximum, ctrl->step);
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_BANDWIDTH_AUTO:
> +	case V4L2_CID_BANDWIDTH:
> +		if (s->bandwidth_auto->val)
> +			s->bandwidth->val = s->f_adc;
> +
> +		c->bandwidth_hz = s->bandwidth->val;
> +
> +		if (!test_bit(POWER_ON, &s->flags))
> +			return 0;
> +
> +		if (fe->ops.tuner_ops.set_params)
> +			ret = fe->ops.tuner_ops.set_params(fe);
> +		else
> +			ret = 0;
> +		break;
> +	default:
> +		ret = -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +static const struct v4l2_ctrl_ops rtl2832_sdr_ctrl_ops = {
> +	.s_ctrl = rtl2832_sdr_s_ctrl,
> +};
> +
> +static void rtl2832_sdr_video_release(struct v4l2_device *v)
> +{
> +	struct rtl2832_sdr_state *s =
> +			container_of(v, struct rtl2832_sdr_state, v4l2_dev);
> +
> +	v4l2_ctrl_handler_free(&s->hdl);
> +	v4l2_device_unregister(&s->v4l2_dev);
> +	kfree(s);
> +}
> +
> +struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
> +		struct i2c_adapter *i2c, const struct rtl2832_config *cfg)
> +{
> +	int ret;
> +	struct rtl2832_sdr_state *s;
> +	const struct v4l2_ctrl_ops *ops = &rtl2832_sdr_ctrl_ops;
> +	struct dvb_usb_device *d = i2c_get_adapdata(i2c);
> +
> +	s = kzalloc(sizeof(struct rtl2832_sdr_state), GFP_KERNEL);
> +	if (s == NULL) {
> +		dev_err(&d->udev->dev,
> +				"Could not allocate memory for rtl2832_sdr_state\n");
> +		return NULL;
> +	}
> +
> +	/* setup the state */
> +	s->fe = fe;
> +	s->d = d;
> +	s->udev = d->udev;
> +	s->i2c = i2c;
> +	s->cfg = cfg;
> +	s->f_adc = bands_adc[0].rangelow;
> +	s->pixelformat =  V4L2_SDR_FMT_CU8;
> +
> +	mutex_init(&s->v4l2_lock);
> +	mutex_init(&s->vb_queue_lock);
> +	spin_lock_init(&s->queued_bufs_lock);
> +	INIT_LIST_HEAD(&s->queued_bufs);
> +
> +	/* Init videobuf2 queue structure */
> +	s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
> +	s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
> +	s->vb_queue.drv_priv = s;
> +	s->vb_queue.buf_struct_size = sizeof(struct rtl2832_sdr_frame_buf);
> +	s->vb_queue.ops = &rtl2832_sdr_vb2_ops;
> +	s->vb_queue.mem_ops = &vb2_vmalloc_memops;
> +	s->vb_queue.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
> +	ret = vb2_queue_init(&s->vb_queue);
> +	if (ret) {
> +		dev_err(&s->udev->dev, "Could not initialize vb2 queue\n");
> +		goto err_free_mem;
> +	}
> +
> +	/* Register controls */
> +	switch (s->cfg->tuner) {
> +	case RTL2832_TUNER_E4000:
> +		v4l2_ctrl_handler_init(&s->hdl, 2);
> +		s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_BANDWIDTH_AUTO, 0, 1, 1, 1);
> +		s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_BANDWIDTH, 4300000, 11000000, 100000, 4300000);
> +		v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
> +		break;
> +	case RTL2832_TUNER_R820T:
> +		v4l2_ctrl_handler_init(&s->hdl, 2);
> +		s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_BANDWIDTH_AUTO, 0, 1, 1, 1);
> +		s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_BANDWIDTH, 0, 8000000, 100000, 0);
> +		v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
> +		break;
> +	case RTL2832_TUNER_FC0012:
> +	case RTL2832_TUNER_FC0013:
> +		v4l2_ctrl_handler_init(&s->hdl, 2);
> +		s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_BANDWIDTH_AUTO, 0, 1, 1, 1);
> +		s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_BANDWIDTH, 6000000, 8000000, 1000000, 6000000);
> +		v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
> +		break;
> +	default:
> +		v4l2_ctrl_handler_init(&s->hdl, 0);
> +		dev_notice(&s->udev->dev, "%s: Unsupported tuner\n",
> +				KBUILD_MODNAME);
> +		goto err_free_controls;
> +	}
> +
> +	if (s->hdl.error) {
> +		ret = s->hdl.error;
> +		dev_err(&s->udev->dev, "Could not initialize controls\n");
> +		goto err_free_controls;
> +	}
> +
> +	/* Init video_device structure */
> +	s->vdev = rtl2832_sdr_template;
> +	s->vdev.queue = &s->vb_queue;
> +	s->vdev.queue->lock = &s->vb_queue_lock;
> +	set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev.flags);
> +	video_set_drvdata(&s->vdev, s);
> +
> +	/* Register the v4l2_device structure */
> +	s->v4l2_dev.release = rtl2832_sdr_video_release;
> +	ret = v4l2_device_register(&s->udev->dev, &s->v4l2_dev);
> +	if (ret) {
> +		dev_err(&s->udev->dev,
> +				"Failed to register v4l2-device (%d)\n", ret);
> +		goto err_free_controls;
> +	}
> +
> +	s->v4l2_dev.ctrl_handler = &s->hdl;
> +	s->vdev.v4l2_dev = &s->v4l2_dev;
> +	s->vdev.lock = &s->v4l2_lock;
> +	s->vdev.vfl_dir = VFL_DIR_RX;
> +
> +	ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1);
> +	if (ret) {
> +		dev_err(&s->udev->dev,
> +				"Failed to register as video device (%d)\n",
> +				ret);
> +		goto err_unregister_v4l2_dev;
> +	}
> +	dev_info(&s->udev->dev, "Registered as %s\n",
> +			video_device_node_name(&s->vdev));
> +
> +	fe->sec_priv = s;
> +	fe->ops.release_sec = rtl2832_sdr_release_sec;
> +
> +	dev_info(&s->i2c->dev, "%s: Realtek RTL2832 SDR attached\n",
> +			KBUILD_MODNAME);
> +	return fe;
> +
> +err_unregister_v4l2_dev:
> +	v4l2_device_unregister(&s->v4l2_dev);
> +err_free_controls:
> +	v4l2_ctrl_handler_free(&s->hdl);
> +err_free_mem:
> +	kfree(s);
> +	return NULL;
> +}
> +EXPORT_SYMBOL(rtl2832_sdr_attach);
> +
> +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
> +MODULE_DESCRIPTION("Realtek RTL2832 SDR driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h
> new file mode 100644
> index 0000000..0803e45
> --- /dev/null
> +++ b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h
> @@ -0,0 +1,51 @@
> +/*
> + * Realtek RTL2832U SDR driver
> + *
> + * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
> + *
> + *    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; either version 2 of the License, or
> + *    (at your option) any later version.
> + *
> + *    This program is distributed in the hope that it will be useful,
> + *    but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *    GNU General Public License for more details.
> + *
> + *    You should have received a copy of the GNU General Public License along
> + *    with this program; if not, write to the Free Software Foundation, Inc.,
> + *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * GNU Radio plugin "gr-kernel" for device usage will be on:
> + * http://git.linuxtv.org/anttip/gr-kernel.git
> + *
> + * TODO:
> + * Help is very highly welcome for these + all the others you could imagine:
> + * - move controls to V4L2 API
> + * - use libv4l2 for stream format conversions
> + * - gr-kernel: switch to v4l2_mmap (current read eats a lot of cpu)
> + * - SDRSharp support
> + */
> +
> +#ifndef RTL2832_SDR_H
> +#define RTL2832_SDR_H
> +
> +#include <linux/kconfig.h>
> +
> +/* for config struct */
> +#include "rtl2832.h"
> +
> +#if IS_ENABLED(CONFIG_DVB_RTL2832_SDR)
> +extern struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
> +	struct i2c_adapter *i2c, const struct rtl2832_config *cfg);
> +#else
> +static inline struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
> +	struct i2c_adapter *i2c, const struct rtl2832_config *cfg)
> +{
> +	dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__);
> +	return NULL;
> +}
> +#endif
> +
> +#endif /* RTL2832_SDR_H */
> 

Regards,

	Hans

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [REVIEW PATCH 08/16] rtl2832_sdr: Realtek RTL2832 SDR driver module
  2014-02-11  2:04 [REVIEW PATCH 00/16] SDR API - drivers Antti Palosaari
@ 2014-02-11  2:04 ` Antti Palosaari
  2014-02-14 14:57   ` Hans Verkuil
  0 siblings, 1 reply; 24+ messages in thread
From: Antti Palosaari @ 2014-02-11  2:04 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil, Antti Palosaari

Implement SDR driver for Realtek RTL2832U chip as a DVB extension
module. SDR module is attached by DVB USB RTL28XXU driver as a DVB
SEC (satellite equipment controller) module. Abusing unused SEC here
has no harm as that is DVB-T only frontend.

SDR functionality is provided by RTL2832 DVB-T demodulator. I suspect
it is originally planned for DAB and FM, but it could be abused general
SDR, due to modern silicon tuners that has wide frequency range and a
lot of configurable parameters (filters, gains, ...).

http://thread.gmane.org/gmane.linux.drivers.video-input-infrastructure/44461

Signed-off-by: Antti Palosaari <crope@iki.fi>
---
 drivers/staging/media/Kconfig                    |    2 +
 drivers/staging/media/Makefile                   |    2 +
 drivers/staging/media/rtl2832u_sdr/Kconfig       |    7 +
 drivers/staging/media/rtl2832u_sdr/Makefile      |    6 +
 drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c | 1470 ++++++++++++++++++++++
 drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h |   51 +
 6 files changed, 1538 insertions(+)
 create mode 100644 drivers/staging/media/rtl2832u_sdr/Kconfig
 create mode 100644 drivers/staging/media/rtl2832u_sdr/Makefile
 create mode 100644 drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
 create mode 100644 drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h

diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index 22b0c9d..a9f2e63 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -41,6 +41,8 @@ source "drivers/staging/media/solo6x10/Kconfig"
 
 source "drivers/staging/media/omap4iss/Kconfig"
 
+source "drivers/staging/media/rtl2832u_sdr/Kconfig"
+
 # Keep LIRC at the end, as it has sub-menus
 source "drivers/staging/media/lirc/Kconfig"
 
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index bedc62a..8e2c5d2 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -11,3 +11,5 @@ obj-$(CONFIG_VIDEO_OMAP4)	+= omap4iss/
 obj-$(CONFIG_USB_SN9C102)       += sn9c102/
 obj-$(CONFIG_VIDEO_OMAP2)       += omap24xx/
 obj-$(CONFIG_VIDEO_TCM825X)     += omap24xx/
+obj-$(CONFIG_DVB_RTL2832_SDR)	+= rtl2832u_sdr/
+
diff --git a/drivers/staging/media/rtl2832u_sdr/Kconfig b/drivers/staging/media/rtl2832u_sdr/Kconfig
new file mode 100644
index 0000000..3ede5fe
--- /dev/null
+++ b/drivers/staging/media/rtl2832u_sdr/Kconfig
@@ -0,0 +1,7 @@
+config DVB_RTL2832_SDR
+	tristate "Realtek RTL2832 SDR"
+	depends on USB && DVB_CORE && I2C && VIDEO_V4L2 && DVB_USB_RTL28XXU
+	select DVB_RTL2832
+	select VIDEOBUF2_VMALLOC
+	default m if !MEDIA_SUBDRV_AUTOSELECT
+
diff --git a/drivers/staging/media/rtl2832u_sdr/Makefile b/drivers/staging/media/rtl2832u_sdr/Makefile
new file mode 100644
index 0000000..7e00a0d
--- /dev/null
+++ b/drivers/staging/media/rtl2832u_sdr/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832_sdr.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+ccflags-y += -Idrivers/media/tuners
+ccflags-y += -Idrivers/media/usb/dvb-usb-v2
diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
new file mode 100644
index 0000000..9265424
--- /dev/null
+++ b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.c
@@ -0,0 +1,1470 @@
+/*
+ * Realtek RTL2832U SDR driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ *    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; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License along
+ *    with this program; if not, write to the Free Software Foundation, Inc.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * GNU Radio plugin "gr-kernel" for device usage will be on:
+ * http://git.linuxtv.org/anttip/gr-kernel.git
+ *
+ */
+
+#include "dvb_frontend.h"
+#include "rtl2832_sdr.h"
+#include "dvb_usb.h"
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include <linux/jiffies.h>
+#include <linux/math64.h>
+
+#define MAX_BULK_BUFS            (10)
+#define BULK_BUFFER_SIZE         (128 * 512)
+
+static const struct v4l2_frequency_band bands_adc[] = {
+	{
+		.tuner = 0,
+		.type = V4L2_TUNER_ADC,
+		.index = 0,
+		.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+		.rangelow   =  300000,
+		.rangehigh  =  300000,
+	},
+	{
+		.tuner = 0,
+		.type = V4L2_TUNER_ADC,
+		.index = 1,
+		.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+		.rangelow   =  900001,
+		.rangehigh  = 2800000,
+	},
+	{
+		.tuner = 0,
+		.type = V4L2_TUNER_ADC,
+		.index = 2,
+		.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+		.rangelow   = 3200000,
+		.rangehigh  = 3200000,
+	},
+};
+
+static const struct v4l2_frequency_band bands_fm[] = {
+	{
+		.tuner = 1,
+		.type = V4L2_TUNER_RF,
+		.index = 0,
+		.capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS,
+		.rangelow   =    50000000,
+		.rangehigh  =  2000000000,
+	},
+};
+
+/* stream formats */
+struct rtl2832_sdr_format {
+	char	*name;
+	u32	pixelformat;
+};
+
+static struct rtl2832_sdr_format formats[] = {
+	{
+		.name		= "IQ U8",
+		.pixelformat	=  V4L2_SDR_FMT_CU8,
+	}, {
+		.name		= "IQ U16LE (emulated)",
+		.pixelformat	= V4L2_SDR_FMT_CU16LE,
+	},
+};
+
+static const unsigned int NUM_FORMATS = ARRAY_SIZE(formats);
+
+/* intermediate buffers with raw data from the USB device */
+struct rtl2832_sdr_frame_buf {
+	struct vb2_buffer vb;   /* common v4l buffer stuff -- must be first */
+	struct list_head list;
+};
+
+struct rtl2832_sdr_state {
+#define POWER_ON           (1 << 1)
+#define URB_BUF            (1 << 2)
+	unsigned long flags;
+
+	const struct rtl2832_config *cfg;
+	struct dvb_frontend *fe;
+	struct dvb_usb_device *d;
+	struct i2c_adapter *i2c;
+	u8 bank;
+
+	struct video_device vdev;
+	struct v4l2_device v4l2_dev;
+
+	/* videobuf2 queue and queued buffers list */
+	struct vb2_queue vb_queue;
+	struct list_head queued_bufs;
+	spinlock_t queued_bufs_lock; /* Protects queued_bufs */
+
+	/* Note if taking both locks v4l2_lock must always be locked first! */
+	struct mutex v4l2_lock;      /* Protects everything else */
+	struct mutex vb_queue_lock;  /* Protects vb_queue and capt_file */
+
+	/* Pointer to our usb_device, will be NULL after unplug */
+	struct usb_device *udev; /* Both mutexes most be hold when setting! */
+
+	unsigned int vb_full; /* vb is full and packets dropped */
+
+	struct urb     *urb_list[MAX_BULK_BUFS];
+	int            buf_num;
+	unsigned long  buf_size;
+	u8             *buf_list[MAX_BULK_BUFS];
+	dma_addr_t     dma_addr[MAX_BULK_BUFS];
+	int urbs_initialized;
+	int urbs_submitted;
+
+	unsigned int f_adc, f_tuner;
+	u32 pixelformat;
+
+	/* Controls */
+	struct v4l2_ctrl_handler hdl;
+	struct v4l2_ctrl *bandwidth_auto;
+	struct v4l2_ctrl *bandwidth;
+
+	/* for sample rate calc */
+	unsigned int sample;
+	unsigned int sample_measured;
+	unsigned long jiffies_next;
+};
+
+/* write multiple hardware registers */
+static int rtl2832_sdr_wr(struct rtl2832_sdr_state *s, u8 reg, const u8 *val,
+		int len)
+{
+	int ret;
+	u8 buf[1 + len];
+	struct i2c_msg msg[1] = {
+		{
+			.addr = s->cfg->i2c_addr,
+			.flags = 0,
+			.len = 1 + len,
+			.buf = buf,
+		}
+	};
+
+	buf[0] = reg;
+	memcpy(&buf[1], val, len);
+
+	ret = i2c_transfer(s->i2c, msg, 1);
+	if (ret == 1) {
+		ret = 0;
+	} else {
+		dev_err(&s->i2c->dev,
+			"%s: I2C wr failed=%d reg=%02x len=%d\n",
+			KBUILD_MODNAME, ret, reg, len);
+		ret = -EREMOTEIO;
+	}
+	return ret;
+}
+
+/* read multiple hardware registers */
+static int rtl2832_sdr_rd(struct rtl2832_sdr_state *s, u8 reg, u8 *val, int len)
+{
+	int ret;
+	struct i2c_msg msg[2] = {
+		{
+			.addr = s->cfg->i2c_addr,
+			.flags = 0,
+			.len = 1,
+			.buf = &reg,
+		}, {
+			.addr = s->cfg->i2c_addr,
+			.flags = I2C_M_RD,
+			.len = len,
+			.buf = val,
+		}
+	};
+
+	ret = i2c_transfer(s->i2c, msg, 2);
+	if (ret == 2) {
+		ret = 0;
+	} else {
+		dev_err(&s->i2c->dev,
+				"%s: I2C rd failed=%d reg=%02x len=%d\n",
+				KBUILD_MODNAME, ret, reg, len);
+		ret = -EREMOTEIO;
+	}
+	return ret;
+}
+
+/* write multiple registers */
+static int rtl2832_sdr_wr_regs(struct rtl2832_sdr_state *s, u16 reg,
+		const u8 *val, int len)
+{
+	int ret;
+	u8 reg2 = (reg >> 0) & 0xff;
+	u8 bank = (reg >> 8) & 0xff;
+
+	/* switch bank if needed */
+	if (bank != s->bank) {
+		ret = rtl2832_sdr_wr(s, 0x00, &bank, 1);
+		if (ret)
+			return ret;
+
+		s->bank = bank;
+	}
+
+	return rtl2832_sdr_wr(s, reg2, val, len);
+}
+
+/* read multiple registers */
+static int rtl2832_sdr_rd_regs(struct rtl2832_sdr_state *s, u16 reg, u8 *val,
+		int len)
+{
+	int ret;
+	u8 reg2 = (reg >> 0) & 0xff;
+	u8 bank = (reg >> 8) & 0xff;
+
+	/* switch bank if needed */
+	if (bank != s->bank) {
+		ret = rtl2832_sdr_wr(s, 0x00, &bank, 1);
+		if (ret)
+			return ret;
+
+		s->bank = bank;
+	}
+
+	return rtl2832_sdr_rd(s, reg2, val, len);
+}
+
+/* write single register */
+static int rtl2832_sdr_wr_reg(struct rtl2832_sdr_state *s, u16 reg, u8 val)
+{
+	return rtl2832_sdr_wr_regs(s, reg, &val, 1);
+}
+
+#if 0
+/* read single register */
+static int rtl2832_sdr_rd_reg(struct rtl2832_sdr_state *s, u16 reg, u8 *val)
+{
+	return rtl2832_sdr_rd_regs(s, reg, val, 1);
+}
+#endif
+
+/* write single register with mask */
+static int rtl2832_sdr_wr_reg_mask(struct rtl2832_sdr_state *s, u16 reg,
+		u8 val, u8 mask)
+{
+	int ret;
+	u8 tmp;
+
+	/* no need for read if whole reg is written */
+	if (mask != 0xff) {
+		ret = rtl2832_sdr_rd_regs(s, reg, &tmp, 1);
+		if (ret)
+			return ret;
+
+		val &= mask;
+		tmp &= ~mask;
+		val |= tmp;
+	}
+
+	return rtl2832_sdr_wr_regs(s, reg, &val, 1);
+}
+
+#if 0
+/* read single register with mask */
+static int rtl2832_sdr_rd_reg_mask(struct rtl2832_sdr_state *s, u16 reg,
+		u8 *val, u8 mask)
+{
+	int ret, i;
+	u8 tmp;
+
+	ret = rtl2832_sdr_rd_regs(s, reg, &tmp, 1);
+	if (ret)
+		return ret;
+
+	tmp &= mask;
+
+	/* find position of the first bit */
+	for (i = 0; i < 8; i++) {
+		if ((mask >> i) & 0x01)
+			break;
+	}
+	*val = tmp >> i;
+
+	return 0;
+}
+#endif
+
+/* Private functions */
+static struct rtl2832_sdr_frame_buf *rtl2832_sdr_get_next_fill_buf(
+		struct rtl2832_sdr_state *s)
+{
+	unsigned long flags = 0;
+	struct rtl2832_sdr_frame_buf *buf = NULL;
+
+	spin_lock_irqsave(&s->queued_bufs_lock, flags);
+	if (list_empty(&s->queued_bufs))
+		goto leave;
+
+	buf = list_entry(s->queued_bufs.next,
+			struct rtl2832_sdr_frame_buf, list);
+	list_del(&buf->list);
+leave:
+	spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
+	return buf;
+}
+
+static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_state *s,
+		void *dst, const u8 *src, unsigned int src_len)
+{
+	unsigned int dst_len;
+
+	if (s->pixelformat ==  V4L2_SDR_FMT_CU8) {
+		/* native stream, no need to convert */
+		memcpy(dst, src, src_len);
+		dst_len = src_len;
+	} else if (s->pixelformat == V4L2_SDR_FMT_CU16LE) {
+		/* convert u8 to u16 */
+		unsigned int i;
+		u16 *u16dst = dst;
+		for (i = 0; i < src_len; i++)
+			*u16dst++ = (src[i] << 8) | (src[i] >> 0);
+		dst_len = 2 * src_len;
+	} else {
+		dst_len = 0;
+	}
+
+	/* calculate samping rate and output it in 10 seconds intervals */
+	if (unlikely(time_is_before_jiffies(s->jiffies_next))) {
+#define MSECS 10000UL
+		unsigned int samples = s->sample - s->sample_measured;
+		s->jiffies_next = jiffies + msecs_to_jiffies(MSECS);
+		s->sample_measured = s->sample;
+		dev_dbg(&s->udev->dev,
+				"slen=%d samples=%u msecs=%lu sampling rate=%lu\n",
+				src_len, samples, MSECS,
+				samples * 1000UL / MSECS);
+	}
+
+	/* total number of I+Q pairs */
+	s->sample += src_len / 2;
+
+	return dst_len;
+}
+
+/*
+ * This gets called for the bulk stream pipe. This is done in interrupt
+ * time, so it has to be fast, not crash, and not stall. Neat.
+ */
+static void rtl2832_sdr_urb_complete(struct urb *urb)
+{
+	struct rtl2832_sdr_state *s = urb->context;
+	struct rtl2832_sdr_frame_buf *fbuf;
+
+	dev_dbg_ratelimited(&s->udev->dev,
+			"%s: status=%d length=%d/%d errors=%d\n",
+			__func__, urb->status, urb->actual_length,
+			urb->transfer_buffer_length, urb->error_count);
+
+	switch (urb->status) {
+	case 0:             /* success */
+	case -ETIMEDOUT:    /* NAK */
+		break;
+	case -ECONNRESET:   /* kill */
+	case -ENOENT:
+	case -ESHUTDOWN:
+		return;
+	default:            /* error */
+		dev_err_ratelimited(&s->udev->dev, "urb failed=%d\n",
+				urb->status);
+		break;
+	}
+
+	if (likely(urb->actual_length > 0)) {
+		void *ptr;
+		unsigned int len;
+		/* get free framebuffer */
+		fbuf = rtl2832_sdr_get_next_fill_buf(s);
+		if (unlikely(fbuf == NULL)) {
+			s->vb_full++;
+			dev_notice_ratelimited(&s->udev->dev,
+					"videobuf is full, %d packets dropped\n",
+					s->vb_full);
+			goto skip;
+		}
+
+		/* fill framebuffer */
+		ptr = vb2_plane_vaddr(&fbuf->vb, 0);
+		len = rtl2832_sdr_convert_stream(s, ptr, urb->transfer_buffer,
+				urb->actual_length);
+		vb2_set_plane_payload(&fbuf->vb, 0, len);
+		vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE);
+	}
+skip:
+	usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static int rtl2832_sdr_kill_urbs(struct rtl2832_sdr_state *s)
+{
+	int i;
+
+	for (i = s->urbs_submitted - 1; i >= 0; i--) {
+		dev_dbg(&s->udev->dev, "%s: kill urb=%d\n", __func__, i);
+		/* stop the URB */
+		usb_kill_urb(s->urb_list[i]);
+	}
+	s->urbs_submitted = 0;
+
+	return 0;
+}
+
+static int rtl2832_sdr_submit_urbs(struct rtl2832_sdr_state *s)
+{
+	int i, ret;
+
+	for (i = 0; i < s->urbs_initialized; i++) {
+		dev_dbg(&s->udev->dev, "%s: submit urb=%d\n", __func__, i);
+		ret = usb_submit_urb(s->urb_list[i], GFP_ATOMIC);
+		if (ret) {
+			dev_err(&s->udev->dev,
+					"Could not submit urb no. %d - get them all back\n",
+					i);
+			rtl2832_sdr_kill_urbs(s);
+			return ret;
+		}
+		s->urbs_submitted++;
+	}
+
+	return 0;
+}
+
+static int rtl2832_sdr_free_stream_bufs(struct rtl2832_sdr_state *s)
+{
+	if (s->flags & USB_STATE_URB_BUF) {
+		while (s->buf_num) {
+			s->buf_num--;
+			dev_dbg(&s->udev->dev, "%s: free buf=%d\n",
+					__func__, s->buf_num);
+			usb_free_coherent(s->udev, s->buf_size,
+					  s->buf_list[s->buf_num],
+					  s->dma_addr[s->buf_num]);
+		}
+	}
+	s->flags &= ~USB_STATE_URB_BUF;
+
+	return 0;
+}
+
+static int rtl2832_sdr_alloc_stream_bufs(struct rtl2832_sdr_state *s)
+{
+	s->buf_num = 0;
+	s->buf_size = BULK_BUFFER_SIZE;
+
+	dev_dbg(&s->udev->dev,
+			"%s: all in all I will use %u bytes for streaming\n",
+			__func__,  MAX_BULK_BUFS * BULK_BUFFER_SIZE);
+
+	for (s->buf_num = 0; s->buf_num < MAX_BULK_BUFS; s->buf_num++) {
+		s->buf_list[s->buf_num] = usb_alloc_coherent(s->udev,
+				BULK_BUFFER_SIZE, GFP_ATOMIC,
+				&s->dma_addr[s->buf_num]);
+		if (!s->buf_list[s->buf_num]) {
+			dev_dbg(&s->udev->dev, "%s: alloc buf=%d failed\n",
+					__func__, s->buf_num);
+			rtl2832_sdr_free_stream_bufs(s);
+			return -ENOMEM;
+		}
+
+		dev_dbg(&s->udev->dev, "%s: alloc buf=%d %p (dma %llu)\n",
+				__func__, s->buf_num,
+				s->buf_list[s->buf_num],
+				(long long)s->dma_addr[s->buf_num]);
+		s->flags |= USB_STATE_URB_BUF;
+	}
+
+	return 0;
+}
+
+static int rtl2832_sdr_free_urbs(struct rtl2832_sdr_state *s)
+{
+	int i;
+
+	rtl2832_sdr_kill_urbs(s);
+
+	for (i = s->urbs_initialized - 1; i >= 0; i--) {
+		if (s->urb_list[i]) {
+			dev_dbg(&s->udev->dev, "%s: free urb=%d\n",
+					__func__, i);
+			/* free the URBs */
+			usb_free_urb(s->urb_list[i]);
+		}
+	}
+	s->urbs_initialized = 0;
+
+	return 0;
+}
+
+static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_state *s)
+{
+	int i, j;
+
+	/* allocate the URBs */
+	for (i = 0; i < MAX_BULK_BUFS; i++) {
+		dev_dbg(&s->udev->dev, "%s: alloc urb=%d\n", __func__, i);
+		s->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
+		if (!s->urb_list[i]) {
+			dev_dbg(&s->udev->dev, "%s: failed\n", __func__);
+			for (j = 0; j < i; j++)
+				usb_free_urb(s->urb_list[j]);
+			return -ENOMEM;
+		}
+		usb_fill_bulk_urb(s->urb_list[i],
+				s->udev,
+				usb_rcvbulkpipe(s->udev, 0x81),
+				s->buf_list[i],
+				BULK_BUFFER_SIZE,
+				rtl2832_sdr_urb_complete, s);
+
+		s->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+		s->urb_list[i]->transfer_dma = s->dma_addr[i];
+		s->urbs_initialized++;
+	}
+
+	return 0;
+}
+
+/* Must be called with vb_queue_lock hold */
+static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_state *s)
+{
+	unsigned long flags = 0;
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	spin_lock_irqsave(&s->queued_bufs_lock, flags);
+	while (!list_empty(&s->queued_bufs)) {
+		struct rtl2832_sdr_frame_buf *buf;
+		buf = list_entry(s->queued_bufs.next,
+				struct rtl2832_sdr_frame_buf, list);
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+	}
+	spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
+}
+
+/* The user yanked out the cable... */
+static void rtl2832_sdr_release_sec(struct dvb_frontend *fe)
+{
+	struct rtl2832_sdr_state *s = fe->sec_priv;
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	mutex_lock(&s->vb_queue_lock);
+	mutex_lock(&s->v4l2_lock);
+	/* No need to keep the urbs around after disconnection */
+	s->udev = NULL;
+
+	v4l2_device_disconnect(&s->v4l2_dev);
+	video_unregister_device(&s->vdev);
+	mutex_unlock(&s->v4l2_lock);
+	mutex_unlock(&s->vb_queue_lock);
+
+	v4l2_device_put(&s->v4l2_dev);
+
+	fe->sec_priv = NULL;
+}
+
+static int rtl2832_sdr_querycap(struct file *file, void *fh,
+		struct v4l2_capability *cap)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+	strlcpy(cap->card, s->vdev.name, sizeof(cap->card));
+	usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info));
+	cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING |
+			V4L2_CAP_READWRITE | V4L2_CAP_TUNER;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+	return 0;
+}
+
+/* Videobuf2 operations */
+static int rtl2832_sdr_queue_setup(struct vb2_queue *vq,
+		const struct v4l2_format *fmt, unsigned int *nbuffers,
+		unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[])
+{
+	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
+	dev_dbg(&s->udev->dev, "%s: *nbuffers=%d\n", __func__, *nbuffers);
+
+	/* Absolute min and max number of buffers available for mmap() */
+	*nbuffers = 32;
+	*nplanes = 1;
+	/* 2 = max 16-bit sample returned */
+	sizes[0] = PAGE_ALIGN(BULK_BUFFER_SIZE * 2);
+	dev_dbg(&s->udev->dev, "%s: nbuffers=%d sizes[0]=%d\n",
+			__func__, *nbuffers, sizes[0]);
+	return 0;
+}
+
+static int rtl2832_sdr_buf_prepare(struct vb2_buffer *vb)
+{
+	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue);
+
+	/* Don't allow queing new buffers after device disconnection */
+	if (!s->udev)
+		return -ENODEV;
+
+	return 0;
+}
+
+static void rtl2832_sdr_buf_queue(struct vb2_buffer *vb)
+{
+	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue);
+	struct rtl2832_sdr_frame_buf *buf =
+			container_of(vb, struct rtl2832_sdr_frame_buf, vb);
+	unsigned long flags = 0;
+
+	/* Check the device has not disconnected between prep and queuing */
+	if (!s->udev) {
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+		return;
+	}
+
+	spin_lock_irqsave(&s->queued_bufs_lock, flags);
+	list_add_tail(&buf->list, &s->queued_bufs);
+	spin_unlock_irqrestore(&s->queued_bufs_lock, flags);
+}
+
+static int rtl2832_sdr_set_adc(struct rtl2832_sdr_state *s)
+{
+	struct dvb_frontend *fe = s->fe;
+	int ret;
+	unsigned int f_sr, f_if;
+	u8 buf[4], u8tmp1, u8tmp2;
+	u64 u64tmp;
+	u32 u32tmp;
+	dev_dbg(&s->udev->dev, "%s: f_adc=%u\n", __func__, s->f_adc);
+
+	if (!test_bit(POWER_ON, &s->flags))
+		return 0;
+
+	if (s->f_adc == 0)
+		return 0;
+
+	f_sr = s->f_adc;
+
+	ret = rtl2832_sdr_wr_regs(s, 0x13e, "\x00\x00", 2);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_regs(s, 0x115, "\x00\x00\x00\x00", 4);
+	if (ret)
+		goto err;
+
+	/* get IF from tuner */
+	if (fe->ops.tuner_ops.get_if_frequency)
+		ret = fe->ops.tuner_ops.get_if_frequency(fe, &f_if);
+	else
+		ret = -EINVAL;
+
+	if (ret)
+		goto err;
+
+	/* program IF */
+	u64tmp = f_if % s->cfg->xtal;
+	u64tmp *= 0x400000;
+	u64tmp = div_u64(u64tmp, s->cfg->xtal);
+	u64tmp = -u64tmp;
+	u32tmp = u64tmp & 0x3fffff;
+
+	dev_dbg(&s->udev->dev, "%s: f_if=%u if_ctl=%08x\n",
+			__func__, f_if, u32tmp);
+
+	buf[0] = (u32tmp >> 16) & 0xff;
+	buf[1] = (u32tmp >>  8) & 0xff;
+	buf[2] = (u32tmp >>  0) & 0xff;
+
+	ret = rtl2832_sdr_wr_regs(s, 0x119, buf, 3);
+	if (ret)
+		goto err;
+
+	/* BB / IF mode */
+	/* POR: 0x1b1=0x1f, 0x008=0x0d, 0x006=0x80 */
+	if (f_if) {
+		u8tmp1 = 0x1a; /* disable Zero-IF */
+		u8tmp2 = 0x8d; /* enable ADC I */
+	} else {
+		u8tmp1 = 0x1b; /* enable Zero-IF, DC, IQ */
+		u8tmp2 = 0xcd; /* enable ADC I, ADC Q */
+	}
+
+	ret = rtl2832_sdr_wr_reg(s, 0x1b1, u8tmp1);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_reg(s, 0x008, u8tmp2);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_reg(s, 0x006, 0x80);
+	if (ret)
+		goto err;
+
+	/* program sampling rate (resampling down) */
+	u32tmp = div_u64(s->cfg->xtal * 0x400000ULL, f_sr * 4U);
+	u32tmp <<= 2;
+	buf[0] = (u32tmp >> 24) & 0xff;
+	buf[1] = (u32tmp >> 16) & 0xff;
+	buf[2] = (u32tmp >>  8) & 0xff;
+	buf[3] = (u32tmp >>  0) & 0xff;
+	ret = rtl2832_sdr_wr_regs(s, 0x19f, buf, 4);
+	if (ret)
+		goto err;
+
+	/* low-pass filter */
+	ret = rtl2832_sdr_wr_regs(s, 0x11c,
+			"\xca\xdc\xd7\xd8\xe0\xf2\x0e\x35\x06\x50\x9c\x0d\x71\x11\x14\x71\x74\x19\x41\xa5",
+			20);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_regs(s, 0x017, "\x11\x10", 2);
+	if (ret)
+		goto err;
+
+	/* mode */
+	ret = rtl2832_sdr_wr_regs(s, 0x019, "\x05", 1);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_regs(s, 0x01a, "\x1b\x16\x0d\x06\x01\xff", 6);
+	if (ret)
+		goto err;
+
+	/* FSM */
+	ret = rtl2832_sdr_wr_regs(s, 0x192, "\x00\xf0\x0f", 3);
+	if (ret)
+		goto err;
+
+	/* PID filter */
+	ret = rtl2832_sdr_wr_regs(s, 0x061, "\x60", 1);
+	if (ret)
+		goto err;
+
+	/* used RF tuner based settings */
+	switch (s->cfg->tuner) {
+	case RTL2832_TUNER_E4000:
+		ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x103, "\x5a", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x30", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x104, "\xd0", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x18", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x011, "\xd4", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1e5, "\xf0", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1d9, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1db, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1dd, "\x14", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1de, "\xec", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1d8, "\x0c", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1e6, "\x02", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1d7, "\x09", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x83", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x010, "\x49", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x87", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x85", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x013, "\x02", 1);
+		break;
+	case RTL2832_TUNER_FC0012:
+	case RTL2832_TUNER_FC0013:
+		ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x103, "\x5a", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x2c", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x104, "\xcc", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x16", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x011, "\xe9\xbf", 2);
+		ret = rtl2832_sdr_wr_regs(s, 0x1e5, "\xf0", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1d9, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1db, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1dd, "\x11", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1de, "\xef", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1d8, "\x0c", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1e6, "\x02", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1d7, "\x09", 1);
+		break;
+	case RTL2832_TUNER_R820T:
+		ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x115, "\x01", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x103, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x24", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x104, "\xcc", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x14", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1);
+		ret = rtl2832_sdr_wr_regs(s, 0x011, "\xf4", 1);
+		break;
+	default:
+		dev_notice(&s->udev->dev, "Unsupported tuner\n");
+	}
+
+	/* software reset */
+	ret = rtl2832_sdr_wr_reg_mask(s, 0x101, 0x04, 0x04);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_reg_mask(s, 0x101, 0x00, 0x04);
+	if (ret)
+		goto err;
+err:
+	return ret;
+};
+
+static void rtl2832_sdr_unset_adc(struct rtl2832_sdr_state *s)
+{
+	int ret;
+
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	/* PID filter */
+	ret = rtl2832_sdr_wr_regs(s, 0x061, "\xe0", 1);
+	if (ret)
+		goto err;
+
+	/* mode */
+	ret = rtl2832_sdr_wr_regs(s, 0x019, "\x20", 1);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_regs(s, 0x017, "\x11\x10", 2);
+	if (ret)
+		goto err;
+
+	/* FSM */
+	ret = rtl2832_sdr_wr_regs(s, 0x192, "\x00\x0f\xff", 3);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_regs(s, 0x13e, "\x40\x00", 2);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_wr_regs(s, 0x115, "\x06\x3f\xce\xcc", 4);
+	if (ret)
+		goto err;
+err:
+	return;
+};
+
+static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_state *s)
+{
+	struct dvb_frontend *fe = s->fe;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct v4l2_ctrl *bandwidth_auto;
+	struct v4l2_ctrl *bandwidth;
+
+	/*
+	 * tuner RF (Hz)
+	 */
+	if (s->f_tuner == 0)
+		return 0;
+
+	/*
+	 * bandwidth (Hz)
+	 */
+	bandwidth_auto = v4l2_ctrl_find(&s->hdl, V4L2_CID_BANDWIDTH_AUTO);
+	bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_BANDWIDTH);
+	if (v4l2_ctrl_g_ctrl(bandwidth_auto)) {
+		c->bandwidth_hz = s->f_adc;
+		v4l2_ctrl_s_ctrl(bandwidth, s->f_adc);
+	} else {
+		c->bandwidth_hz = v4l2_ctrl_g_ctrl(bandwidth);
+	}
+
+	c->frequency = s->f_tuner;
+	c->delivery_system = SYS_DVBT;
+
+	dev_dbg(&s->udev->dev, "%s: frequency=%u bandwidth=%d\n",
+			__func__, c->frequency, c->bandwidth_hz);
+
+	if (!test_bit(POWER_ON, &s->flags))
+		return 0;
+
+	if (fe->ops.tuner_ops.set_params)
+		fe->ops.tuner_ops.set_params(fe);
+
+	return 0;
+};
+
+static int rtl2832_sdr_set_tuner(struct rtl2832_sdr_state *s)
+{
+	struct dvb_frontend *fe = s->fe;
+
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	if (fe->ops.tuner_ops.init)
+		fe->ops.tuner_ops.init(fe);
+
+	return 0;
+};
+
+static void rtl2832_sdr_unset_tuner(struct rtl2832_sdr_state *s)
+{
+	struct dvb_frontend *fe = s->fe;
+
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	if (fe->ops.tuner_ops.sleep)
+		fe->ops.tuner_ops.sleep(fe);
+
+	return;
+};
+
+static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
+	int ret;
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	if (!s->udev)
+		return -ENODEV;
+
+	if (mutex_lock_interruptible(&s->v4l2_lock))
+		return -ERESTARTSYS;
+
+	if (s->d->props->power_ctrl)
+		s->d->props->power_ctrl(s->d, 1);
+
+	set_bit(POWER_ON, &s->flags);
+
+	ret = rtl2832_sdr_set_tuner(s);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_set_tuner_freq(s);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_set_adc(s);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_alloc_stream_bufs(s);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_alloc_urbs(s);
+	if (ret)
+		goto err;
+
+	ret = rtl2832_sdr_submit_urbs(s);
+	if (ret)
+		goto err;
+
+err:
+	mutex_unlock(&s->v4l2_lock);
+
+	return ret;
+}
+
+static int rtl2832_sdr_stop_streaming(struct vb2_queue *vq)
+{
+	struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq);
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	if (mutex_lock_interruptible(&s->v4l2_lock))
+		return -ERESTARTSYS;
+
+	rtl2832_sdr_kill_urbs(s);
+	rtl2832_sdr_free_urbs(s);
+	rtl2832_sdr_free_stream_bufs(s);
+	rtl2832_sdr_cleanup_queued_bufs(s);
+	rtl2832_sdr_unset_adc(s);
+	rtl2832_sdr_unset_tuner(s);
+
+	clear_bit(POWER_ON, &s->flags);
+
+	if (s->d->props->power_ctrl)
+		s->d->props->power_ctrl(s->d, 0);
+
+	mutex_unlock(&s->v4l2_lock);
+
+	return 0;
+}
+
+static struct vb2_ops rtl2832_sdr_vb2_ops = {
+	.queue_setup            = rtl2832_sdr_queue_setup,
+	.buf_prepare            = rtl2832_sdr_buf_prepare,
+	.buf_queue              = rtl2832_sdr_buf_queue,
+	.start_streaming        = rtl2832_sdr_start_streaming,
+	.stop_streaming         = rtl2832_sdr_stop_streaming,
+	.wait_prepare           = vb2_ops_wait_prepare,
+	.wait_finish            = vb2_ops_wait_finish,
+};
+
+static int rtl2832_sdr_g_tuner(struct file *file, void *priv,
+		struct v4l2_tuner *v)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	dev_dbg(&s->udev->dev, "%s: index=%d type=%d\n",
+			__func__, v->index, v->type);
+
+	if (v->index == 0) {
+		strlcpy(v->name, "ADC: Realtek RTL2832", sizeof(v->name));
+		v->type = V4L2_TUNER_ADC;
+		v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+		v->rangelow =   300000;
+		v->rangehigh = 3200000;
+	} else if (v->index == 1) {
+		strlcpy(v->name, "RF: <unknown>", sizeof(v->name));
+		v->type = V4L2_TUNER_RF;
+		v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS;
+		v->rangelow =    50000000;
+		v->rangehigh = 2000000000;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int rtl2832_sdr_s_tuner(struct file *file, void *priv,
+		const struct v4l2_tuner *v)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	return 0;
+}
+
+static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv,
+		struct v4l2_frequency_band *band)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d index=%d\n",
+			__func__, band->tuner, band->type, band->index);
+
+	if (band->tuner == 0) {
+		if (band->index >= ARRAY_SIZE(bands_adc))
+			return -EINVAL;
+
+		*band = bands_adc[band->index];
+	} else if (band->tuner == 1) {
+		if (band->index >= ARRAY_SIZE(bands_fm))
+			return -EINVAL;
+
+		*band = bands_fm[band->index];
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int rtl2832_sdr_g_frequency(struct file *file, void *priv,
+		struct v4l2_frequency *f)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	int ret  = 0;
+	dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d\n",
+			__func__, f->tuner, f->type);
+
+	if (f->tuner == 0)
+		f->frequency = s->f_adc;
+	else if (f->tuner == 1)
+		f->frequency = s->f_tuner;
+	else
+		return -EINVAL;
+
+	return ret;
+}
+
+static int rtl2832_sdr_s_frequency(struct file *file, void *priv,
+		const struct v4l2_frequency *f)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	int ret, band;
+
+	dev_dbg(&s->udev->dev, "%s: tuner=%d type=%d frequency=%u\n",
+			__func__, f->tuner, f->type, f->frequency);
+
+	/* ADC band midpoints */
+	#define BAND_ADC_0 ((bands_adc[0].rangehigh + bands_adc[1].rangelow) / 2)
+	#define BAND_ADC_1 ((bands_adc[1].rangehigh + bands_adc[2].rangelow) / 2)
+
+	if (f->tuner == 0 && f->type == V4L2_TUNER_ADC) {
+		if (f->frequency < BAND_ADC_0)
+			band = 0;
+		else if (f->frequency < BAND_ADC_1)
+			band = 1;
+		else
+			band = 2;
+
+		s->f_adc = clamp_t(unsigned int, f->frequency,
+				bands_adc[band].rangelow,
+				bands_adc[band].rangehigh);
+
+		dev_dbg(&s->udev->dev, "%s: ADC frequency=%u Hz\n",
+				__func__, s->f_adc);
+		ret = rtl2832_sdr_set_adc(s);
+	} else if (f->tuner == 1) {
+		s->f_tuner = f->frequency;
+		dev_dbg(&s->udev->dev, "%s: RF frequency=%u Hz\n",
+				__func__, f->frequency);
+
+		ret = rtl2832_sdr_set_tuner_freq(s);
+	} else {
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int rtl2832_sdr_enum_fmt_sdr_cap(struct file *file, void *priv,
+		struct v4l2_fmtdesc *f)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	if (f->index >= NUM_FORMATS)
+		return -EINVAL;
+
+	strlcpy(f->description, formats[f->index].name, sizeof(f->description));
+	f->pixelformat = formats[f->index].pixelformat;
+
+	return 0;
+}
+
+static int rtl2832_sdr_g_fmt_sdr_cap(struct file *file, void *priv,
+		struct v4l2_format *f)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	dev_dbg(&s->udev->dev, "%s:\n", __func__);
+
+	f->fmt.sdr.pixelformat = s->pixelformat;
+
+	return 0;
+}
+
+static int rtl2832_sdr_s_fmt_sdr_cap(struct file *file, void *priv,
+		struct v4l2_format *f)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	struct vb2_queue *q = &s->vb_queue;
+	int i;
+	dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
+			(char *)&f->fmt.sdr.pixelformat);
+
+	if (vb2_is_busy(q))
+		return -EBUSY;
+
+	for (i = 0; i < NUM_FORMATS; i++) {
+		if (formats[i].pixelformat == f->fmt.sdr.pixelformat) {
+			s->pixelformat = f->fmt.sdr.pixelformat;
+			return 0;
+		}
+	}
+
+	f->fmt.sdr.pixelformat = formats[0].pixelformat;
+	s->pixelformat = formats[0].pixelformat;
+
+	return 0;
+}
+
+static int rtl2832_sdr_try_fmt_sdr_cap(struct file *file, void *priv,
+		struct v4l2_format *f)
+{
+	struct rtl2832_sdr_state *s = video_drvdata(file);
+	int i;
+	dev_dbg(&s->udev->dev, "%s: pixelformat fourcc %4.4s\n", __func__,
+			(char *)&f->fmt.sdr.pixelformat);
+
+	for (i = 0; i < NUM_FORMATS; i++) {
+		if (formats[i].pixelformat == f->fmt.sdr.pixelformat)
+			return 0;
+	}
+
+	f->fmt.sdr.pixelformat = formats[0].pixelformat;
+
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops rtl2832_sdr_ioctl_ops = {
+	.vidioc_querycap          = rtl2832_sdr_querycap,
+
+	.vidioc_enum_fmt_sdr_cap  = rtl2832_sdr_enum_fmt_sdr_cap,
+	.vidioc_g_fmt_sdr_cap     = rtl2832_sdr_g_fmt_sdr_cap,
+	.vidioc_s_fmt_sdr_cap     = rtl2832_sdr_s_fmt_sdr_cap,
+	.vidioc_try_fmt_sdr_cap   = rtl2832_sdr_try_fmt_sdr_cap,
+
+	.vidioc_reqbufs           = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs       = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf       = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf          = vb2_ioctl_querybuf,
+	.vidioc_qbuf              = vb2_ioctl_qbuf,
+	.vidioc_dqbuf             = vb2_ioctl_dqbuf,
+
+	.vidioc_streamon          = vb2_ioctl_streamon,
+	.vidioc_streamoff         = vb2_ioctl_streamoff,
+
+	.vidioc_g_tuner           = rtl2832_sdr_g_tuner,
+	.vidioc_s_tuner           = rtl2832_sdr_s_tuner,
+
+	.vidioc_enum_freq_bands   = rtl2832_sdr_enum_freq_bands,
+	.vidioc_g_frequency       = rtl2832_sdr_g_frequency,
+	.vidioc_s_frequency       = rtl2832_sdr_s_frequency,
+
+	.vidioc_subscribe_event   = v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+	.vidioc_log_status        = v4l2_ctrl_log_status,
+};
+
+static const struct v4l2_file_operations rtl2832_sdr_fops = {
+	.owner                    = THIS_MODULE,
+	.open                     = v4l2_fh_open,
+	.release                  = vb2_fop_release,
+	.read                     = vb2_fop_read,
+	.poll                     = vb2_fop_poll,
+	.mmap                     = vb2_fop_mmap,
+	.unlocked_ioctl           = video_ioctl2,
+};
+
+static struct video_device rtl2832_sdr_template = {
+	.name                     = "Realtek RTL2832 SDR",
+	.release                  = video_device_release_empty,
+	.fops                     = &rtl2832_sdr_fops,
+	.ioctl_ops                = &rtl2832_sdr_ioctl_ops,
+};
+
+static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct rtl2832_sdr_state *s =
+			container_of(ctrl->handler, struct rtl2832_sdr_state,
+					hdl);
+	struct dvb_frontend *fe = s->fe;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	int ret;
+	dev_dbg(&s->udev->dev,
+			"%s: id=%d name=%s val=%d min=%d max=%d step=%d\n",
+			__func__, ctrl->id, ctrl->name, ctrl->val,
+			ctrl->minimum, ctrl->maximum, ctrl->step);
+
+	switch (ctrl->id) {
+	case V4L2_CID_BANDWIDTH_AUTO:
+	case V4L2_CID_BANDWIDTH:
+		if (s->bandwidth_auto->val)
+			s->bandwidth->val = s->f_adc;
+
+		c->bandwidth_hz = s->bandwidth->val;
+
+		if (!test_bit(POWER_ON, &s->flags))
+			return 0;
+
+		if (fe->ops.tuner_ops.set_params)
+			ret = fe->ops.tuner_ops.set_params(fe);
+		else
+			ret = 0;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops rtl2832_sdr_ctrl_ops = {
+	.s_ctrl = rtl2832_sdr_s_ctrl,
+};
+
+static void rtl2832_sdr_video_release(struct v4l2_device *v)
+{
+	struct rtl2832_sdr_state *s =
+			container_of(v, struct rtl2832_sdr_state, v4l2_dev);
+
+	v4l2_ctrl_handler_free(&s->hdl);
+	v4l2_device_unregister(&s->v4l2_dev);
+	kfree(s);
+}
+
+struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
+		struct i2c_adapter *i2c, const struct rtl2832_config *cfg)
+{
+	int ret;
+	struct rtl2832_sdr_state *s;
+	const struct v4l2_ctrl_ops *ops = &rtl2832_sdr_ctrl_ops;
+	struct dvb_usb_device *d = i2c_get_adapdata(i2c);
+
+	s = kzalloc(sizeof(struct rtl2832_sdr_state), GFP_KERNEL);
+	if (s == NULL) {
+		dev_err(&d->udev->dev,
+				"Could not allocate memory for rtl2832_sdr_state\n");
+		return NULL;
+	}
+
+	/* setup the state */
+	s->fe = fe;
+	s->d = d;
+	s->udev = d->udev;
+	s->i2c = i2c;
+	s->cfg = cfg;
+	s->f_adc = bands_adc[0].rangelow;
+	s->pixelformat =  V4L2_SDR_FMT_CU8;
+
+	mutex_init(&s->v4l2_lock);
+	mutex_init(&s->vb_queue_lock);
+	spin_lock_init(&s->queued_bufs_lock);
+	INIT_LIST_HEAD(&s->queued_bufs);
+
+	/* Init videobuf2 queue structure */
+	s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE;
+	s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+	s->vb_queue.drv_priv = s;
+	s->vb_queue.buf_struct_size = sizeof(struct rtl2832_sdr_frame_buf);
+	s->vb_queue.ops = &rtl2832_sdr_vb2_ops;
+	s->vb_queue.mem_ops = &vb2_vmalloc_memops;
+	s->vb_queue.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	ret = vb2_queue_init(&s->vb_queue);
+	if (ret) {
+		dev_err(&s->udev->dev, "Could not initialize vb2 queue\n");
+		goto err_free_mem;
+	}
+
+	/* Register controls */
+	switch (s->cfg->tuner) {
+	case RTL2832_TUNER_E4000:
+		v4l2_ctrl_handler_init(&s->hdl, 2);
+		s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_BANDWIDTH_AUTO, 0, 1, 1, 1);
+		s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_BANDWIDTH, 4300000, 11000000, 100000, 4300000);
+		v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
+		break;
+	case RTL2832_TUNER_R820T:
+		v4l2_ctrl_handler_init(&s->hdl, 2);
+		s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_BANDWIDTH_AUTO, 0, 1, 1, 1);
+		s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_BANDWIDTH, 0, 8000000, 100000, 0);
+		v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
+		break;
+	case RTL2832_TUNER_FC0012:
+	case RTL2832_TUNER_FC0013:
+		v4l2_ctrl_handler_init(&s->hdl, 2);
+		s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_BANDWIDTH_AUTO, 0, 1, 1, 1);
+		s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, V4L2_CID_BANDWIDTH, 6000000, 8000000, 1000000, 6000000);
+		v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false);
+		break;
+	default:
+		v4l2_ctrl_handler_init(&s->hdl, 0);
+		dev_notice(&s->udev->dev, "%s: Unsupported tuner\n",
+				KBUILD_MODNAME);
+		goto err_free_controls;
+	}
+
+	if (s->hdl.error) {
+		ret = s->hdl.error;
+		dev_err(&s->udev->dev, "Could not initialize controls\n");
+		goto err_free_controls;
+	}
+
+	/* Init video_device structure */
+	s->vdev = rtl2832_sdr_template;
+	s->vdev.queue = &s->vb_queue;
+	s->vdev.queue->lock = &s->vb_queue_lock;
+	set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev.flags);
+	video_set_drvdata(&s->vdev, s);
+
+	/* Register the v4l2_device structure */
+	s->v4l2_dev.release = rtl2832_sdr_video_release;
+	ret = v4l2_device_register(&s->udev->dev, &s->v4l2_dev);
+	if (ret) {
+		dev_err(&s->udev->dev,
+				"Failed to register v4l2-device (%d)\n", ret);
+		goto err_free_controls;
+	}
+
+	s->v4l2_dev.ctrl_handler = &s->hdl;
+	s->vdev.v4l2_dev = &s->v4l2_dev;
+	s->vdev.lock = &s->v4l2_lock;
+	s->vdev.vfl_dir = VFL_DIR_RX;
+
+	ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1);
+	if (ret) {
+		dev_err(&s->udev->dev,
+				"Failed to register as video device (%d)\n",
+				ret);
+		goto err_unregister_v4l2_dev;
+	}
+	dev_info(&s->udev->dev, "Registered as %s\n",
+			video_device_node_name(&s->vdev));
+
+	fe->sec_priv = s;
+	fe->ops.release_sec = rtl2832_sdr_release_sec;
+
+	dev_info(&s->i2c->dev, "%s: Realtek RTL2832 SDR attached\n",
+			KBUILD_MODNAME);
+	return fe;
+
+err_unregister_v4l2_dev:
+	v4l2_device_unregister(&s->v4l2_dev);
+err_free_controls:
+	v4l2_ctrl_handler_free(&s->hdl);
+err_free_mem:
+	kfree(s);
+	return NULL;
+}
+EXPORT_SYMBOL(rtl2832_sdr_attach);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Realtek RTL2832 SDR driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h
new file mode 100644
index 0000000..0803e45
--- /dev/null
+++ b/drivers/staging/media/rtl2832u_sdr/rtl2832_sdr.h
@@ -0,0 +1,51 @@
+/*
+ * Realtek RTL2832U SDR driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ *    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; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License along
+ *    with this program; if not, write to the Free Software Foundation, Inc.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * GNU Radio plugin "gr-kernel" for device usage will be on:
+ * http://git.linuxtv.org/anttip/gr-kernel.git
+ *
+ * TODO:
+ * Help is very highly welcome for these + all the others you could imagine:
+ * - move controls to V4L2 API
+ * - use libv4l2 for stream format conversions
+ * - gr-kernel: switch to v4l2_mmap (current read eats a lot of cpu)
+ * - SDRSharp support
+ */
+
+#ifndef RTL2832_SDR_H
+#define RTL2832_SDR_H
+
+#include <linux/kconfig.h>
+
+/* for config struct */
+#include "rtl2832.h"
+
+#if IS_ENABLED(CONFIG_DVB_RTL2832_SDR)
+extern struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
+	struct i2c_adapter *i2c, const struct rtl2832_config *cfg);
+#else
+static inline struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
+	struct i2c_adapter *i2c, const struct rtl2832_config *cfg)
+{
+	dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif
+
+#endif /* RTL2832_SDR_H */
-- 
1.8.5.3


^ permalink raw reply related	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2014-03-13 16:41 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-02-27  0:30 [REVIEW PATCH 00/16] SDR API - Realtek RTL2832 SDR driver Antti Palosaari
2014-02-27  0:30 ` [REVIEW PATCH 01/16] e4000: convert DVB tuner to I2C driver model Antti Palosaari
2014-02-27  0:30 ` [REVIEW PATCH 02/16] e4000: implement controls via v4l2 control framework Antti Palosaari
2014-03-13 13:57   ` Mauro Carvalho Chehab
2014-03-13 15:17     ` Antti Palosaari
2014-03-13 16:05       ` Antti Palosaari
2014-03-13 16:40       ` Mauro Carvalho Chehab
2014-02-27  0:30 ` [REVIEW PATCH 03/16] e4000: fix PLL calc to allow higher frequencies Antti Palosaari
2014-02-27  0:30 ` [REVIEW PATCH 04/16] e4000: implement PLL lock v4l control Antti Palosaari
2014-02-27  0:30 ` [REVIEW PATCH 05/16] e4000: get rid of DVB i2c_gate_ctrl() Antti Palosaari
2014-02-27  0:30 ` [REVIEW PATCH 06/16] e4000: convert to Regmap API Antti Palosaari
2014-02-27  0:30 ` [REVIEW PATCH 07/16] e4000: rename some variables Antti Palosaari
2014-02-27  0:30 ` [REVIEW PATCH 08/16] rtl2832_sdr: Realtek RTL2832 SDR driver module Antti Palosaari
2014-02-27  0:30 ` [REVIEW PATCH 09/16] rtl28xxu: constify demod config structs Antti Palosaari
2014-02-27  0:30 ` [REVIEW PATCH 10/16] rtl28xxu: attach SDR extension module Antti Palosaari
2014-02-27  0:30 ` [REVIEW PATCH 11/16] rtl28xxu: fix switch-case style issue Antti Palosaari
2014-02-27  0:30 ` [REVIEW PATCH 12/16] rtl28xxu: use muxed RTL2832 I2C adapters for E4000 and RTL2832_SDR Antti Palosaari
2014-02-27  0:30 ` [REVIEW PATCH 13/16] rtl2832_sdr: expose e4000 controls to user Antti Palosaari
2014-02-27  0:30 ` [REVIEW PATCH 14/16] r820t: add manual gain controls Antti Palosaari
2014-02-27  0:30 ` [REVIEW PATCH 15/16] rtl2832_sdr: expose R820T controls to user Antti Palosaari
2014-02-27  0:30 ` [REVIEW PATCH 16/16] MAINTAINERS: add rtl2832_sdr driver Antti Palosaari
  -- strict thread matches above, loose matches on Subject: below --
2014-02-11  2:04 [REVIEW PATCH 00/16] SDR API - drivers Antti Palosaari
2014-02-11  2:04 ` [REVIEW PATCH 08/16] rtl2832_sdr: Realtek RTL2832 SDR driver module Antti Palosaari
2014-02-14 14:57   ` Hans Verkuil
2014-02-27  0:42     ` Antti Palosaari

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.