diff options
Diffstat (limited to 'drivers/media/tuners')
24 files changed, 1894 insertions, 430 deletions
diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index 15665debc57..22b6b8bb1d9 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. @@ -215,6 +216,13 @@ config MEDIA_TUNER_FC2580  	help  	  FCI FC2580 silicon tuner driver. +config MEDIA_TUNER_M88TS2022 +	tristate "Montage M88TS2022 silicon tuner" +	depends on MEDIA_SUPPORT && I2C +	default m if !MEDIA_SUBDRV_AUTOSELECT +	help +	  Montage M88TS2022 silicon tuner driver. +  config MEDIA_TUNER_TUA9001  	tristate "Infineon TUA 9001 silicon tuner"  	depends on MEDIA_SUPPORT && I2C @@ -222,6 +230,13 @@ config MEDIA_TUNER_TUA9001  	help  	  Infineon TUA 9001 silicon tuner driver. +config MEDIA_TUNER_SI2157 +	tristate "Silicon Labs Si2157 silicon tuner" +	depends on MEDIA_SUPPORT && I2C +	default m if !MEDIA_SUBDRV_AUTOSELECT +	help +	  Silicon Labs Si2157 silicon tuner driver. +  config MEDIA_TUNER_IT913X  	tristate "ITE Tech IT913x silicon tuner"  	depends on MEDIA_SUPPORT && I2C diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile index 308f108eadb..a6ff0c628df 100644 --- a/drivers/media/tuners/Makefile +++ b/drivers/media/tuners/Makefile @@ -31,6 +31,8 @@ obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o  obj-$(CONFIG_MEDIA_TUNER_E4000) += e4000.o  obj-$(CONFIG_MEDIA_TUNER_FC2580) += fc2580.o  obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o +obj-$(CONFIG_MEDIA_TUNER_SI2157) += si2157.o +obj-$(CONFIG_MEDIA_TUNER_M88TS2022) += m88ts2022.o  obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o  obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o  obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index ad9309da4a9..90d93348f20 100644 --- a/drivers/media/tuners/e4000.c +++ b/drivers/media/tuners/e4000.c @@ -19,204 +19,115 @@   */  #include "e4000_priv.h" - -/* write multiple registers */ -static int e4000_wr_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len) -{ -	int ret; -	u8 buf[1 + len]; -	struct i2c_msg msg[1] = { -		{ -			.addr = priv->cfg->i2c_addr, -			.flags = 0, -			.len = sizeof(buf), -			.buf = buf, -		} -	}; - -	buf[0] = reg; -	memcpy(&buf[1], val, len); - -	ret = i2c_transfer(priv->i2c, msg, 1); -	if (ret == 1) { -		ret = 0; -	} else { -		dev_warn(&priv->i2c->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[len]; -	struct i2c_msg msg[2] = { -		{ -			.addr = priv->cfg->i2c_addr, -			.flags = 0, -			.len = 1, -			.buf = ®, -		}, { -			.addr = priv->cfg->i2c_addr, -			.flags = I2C_M_RD, -			.len = sizeof(buf), -			.buf = buf, -		} -	}; - -	ret = i2c_transfer(priv->i2c, msg, 2); -	if (ret == 2) { -		memcpy(val, buf, len); -		ret = 0; -	} else { -		dev_warn(&priv->i2c->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); -} +#include <linux/math64.h>  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->i2c->dev, "%s:\n", __func__); - -	if (fe->ops.i2c_gate_ctrl) -		fe->ops.i2c_gate_ctrl(fe, 1); +	dev_dbg(&s->client->dev, "%s:\n", __func__);  	/* dummy I2C to ensure I2C wakes up */ -	ret = e4000_wr_reg(priv, 0x02, 0x40); +	ret = regmap_write(s->regmap, 0x02, 0x40);  	/* reset */ -	ret = e4000_wr_reg(priv, 0x00, 0x01); -	if (ret < 0) +	ret = regmap_write(s->regmap, 0x00, 0x01); +	if (ret)  		goto err;  	/* disable output clock */ -	ret = e4000_wr_reg(priv, 0x06, 0x00); -	if (ret < 0) +	ret = regmap_write(s->regmap, 0x06, 0x00); +	if (ret)  		goto err; -	ret = e4000_wr_reg(priv, 0x7a, 0x96); -	if (ret < 0) +	ret = regmap_write(s->regmap, 0x7a, 0x96); +	if (ret)  		goto err;  	/* configure gains */ -	ret = e4000_wr_regs(priv, 0x7e, "\x01\xfe", 2); -	if (ret < 0) +	ret = regmap_bulk_write(s->regmap, 0x7e, "\x01\xfe", 2); +	if (ret)  		goto err; -	ret = e4000_wr_reg(priv, 0x82, 0x00); -	if (ret < 0) +	ret = regmap_write(s->regmap, 0x82, 0x00); +	if (ret)  		goto err; -	ret = e4000_wr_reg(priv, 0x24, 0x05); -	if (ret < 0) +	ret = regmap_write(s->regmap, 0x24, 0x05); +	if (ret)  		goto err; -	ret = e4000_wr_regs(priv, 0x87, "\x20\x01", 2); -	if (ret < 0) +	ret = regmap_bulk_write(s->regmap, 0x87, "\x20\x01", 2); +	if (ret)  		goto err; -	ret = e4000_wr_regs(priv, 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 = e4000_wr_reg(priv, 0x2d, 0x1f); -	if (ret < 0) +	ret = regmap_write(s->regmap, 0x2d, 0x1f); +	if (ret)  		goto err; -	ret = e4000_wr_regs(priv, 0x70, "\x01\x01", 2); -	if (ret < 0) +	ret = regmap_bulk_write(s->regmap, 0x70, "\x01\x01", 2); +	if (ret)  		goto err;  	/* gain control */ -	ret = e4000_wr_reg(priv, 0x1a, 0x17); -	if (ret < 0) +	ret = regmap_write(s->regmap, 0x1a, 0x17); +	if (ret)  		goto err; -	ret = e4000_wr_reg(priv, 0x1f, 0x1a); -	if (ret < 0) +	ret = regmap_write(s->regmap, 0x1f, 0x1a); +	if (ret)  		goto err; -	if (fe->ops.i2c_gate_ctrl) -		fe->ops.i2c_gate_ctrl(fe, 0); - -	return 0; +	s->active = true;  err: -	if (fe->ops.i2c_gate_ctrl) -		fe->ops.i2c_gate_ctrl(fe, 0); +	if (ret) +		dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); -	dev_dbg(&priv->i2c->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->i2c->dev, "%s:\n", __func__); +	dev_dbg(&s->client->dev, "%s:\n", __func__); -	if (fe->ops.i2c_gate_ctrl) -		fe->ops.i2c_gate_ctrl(fe, 1); +	s->active = false; -	ret = e4000_wr_reg(priv, 0x00, 0x00); -	if (ret < 0) +	ret = regmap_write(s->regmap, 0x00, 0x00); +	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(&s->client->dev, "%s: failed=%d\n", __func__, ret); -	dev_dbg(&priv->i2c->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; -	unsigned int f_vco; +	unsigned int pll_n, pll_f; +	u64 f_vco;  	u8 buf[5], i_data[4], q_data[4]; -	dev_dbg(&priv->i2c->dev, -			"%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n", +	dev_dbg(&s->client->dev, +			"%s: delivery_system=%d frequency=%u bandwidth_hz=%u\n",  			__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) +	ret = regmap_write(s->regmap, 0x1a, 0x00); +	if (ret)  		goto err;  	/* PLL */ @@ -225,26 +136,26 @@ static int e4000_set_params(struct dvb_frontend *fe)  			break;  	} -	if (i == ARRAY_SIZE(e4000_pll_lut)) +	if (i == ARRAY_SIZE(e4000_pll_lut)) { +		ret = -EINVAL;  		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; -	sigma_delta = 0x10000UL * (f_vco % priv->cfg->clock) / priv->cfg->clock; -	buf[0] = f_vco / priv->cfg->clock; +	f_vco = 1ull * c->frequency * e4000_pll_lut[i].mul; +	pll_n = div_u64_rem(f_vco, s->clock, &pll_f); +	sigma_delta = div_u64(0x10000ULL * pll_f, s->clock); +	buf[0] = pll_n;  	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(&s->client->dev, +			"%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); -	if (ret < 0) +	ret = regmap_bulk_write(s->regmap, 0x09, buf, 5); +	if (ret)  		goto err;  	/* LNA filter (RF filter) */ @@ -253,11 +164,13 @@ static int e4000_set_params(struct dvb_frontend *fe)  			break;  	} -	if (i == ARRAY_SIZE(e400_lna_filter_lut)) +	if (i == ARRAY_SIZE(e400_lna_filter_lut)) { +		ret = -EINVAL;  		goto err; +	} -	ret = e4000_wr_reg(priv, 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 */ @@ -266,14 +179,16 @@ static int e4000_set_params(struct dvb_frontend *fe)  			break;  	} -	if (i == ARRAY_SIZE(e4000_if_filter_lut)) +	if (i == ARRAY_SIZE(e4000_if_filter_lut)) { +		ret = -EINVAL;  		goto err; +	}  	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); -	if (ret < 0) +	ret = regmap_bulk_write(s->regmap, 0x11, buf, 2); +	if (ret)  		goto err;  	/* frequency band */ @@ -282,37 +197,39 @@ static int e4000_set_params(struct dvb_frontend *fe)  			break;  	} -	if (i == ARRAY_SIZE(e4000_band_lut)) +	if (i == ARRAY_SIZE(e4000_band_lut)) { +		ret = -EINVAL;  		goto err; +	} -	ret = e4000_wr_reg(priv, 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 = e4000_wr_reg(priv, 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 = e4000_wr_regs(priv, 0x15, "\x00\x7e\x24", 3); +			ret = regmap_bulk_write(s->regmap, 0x15, "\x00\x7e\x24", 3);  		else if (i == 1) -			ret = e4000_wr_regs(priv, 0x15, "\x00\x7f", 2); +			ret = regmap_bulk_write(s->regmap, 0x15, "\x00\x7f", 2);  		else if (i == 2) -			ret = e4000_wr_regs(priv, 0x15, "\x01", 1); +			ret = regmap_bulk_write(s->regmap, 0x15, "\x01", 1);  		else -			ret = e4000_wr_regs(priv, 0x16, "\x7e", 1); +			ret = regmap_bulk_write(s->regmap, 0x16, "\x7e", 1); -		if (ret < 0) +		if (ret)  			goto err; -		ret = e4000_wr_reg(priv, 0x29, 0x01); -		if (ret < 0) +		ret = regmap_write(s->regmap, 0x29, 0x01); +		if (ret)  			goto err; -		ret = e4000_rd_regs(priv, 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); @@ -322,53 +239,226 @@ 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); -	if (ret < 0) +	ret = regmap_bulk_write(s->regmap, 0x50, q_data, 4); +	if (ret)  		goto err; -	ret = e4000_wr_regs(priv, 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 = e4000_wr_reg(priv, 0x1a, 0x17); -	if (ret < 0) +	ret = regmap_write(s->regmap, 0x1a, 0x17); +	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(&s->client->dev, "%s: failed=%d\n", __func__, ret); -	dev_dbg(&priv->i2c->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->i2c->dev, "%s:\n", __func__); +	dev_dbg(&s->client->dev, "%s:\n", __func__);  	*frequency = 0; /* Zero-IF */  	return 0;  } -static int e4000_release(struct dvb_frontend *fe) +#if IS_ENABLED(CONFIG_VIDEO_V4L2) +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(&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 (s->lna_gain_auto->val && s->if_gain_auto->cur.val) +		u8tmp = 0x17; +	else if (s->lna_gain_auto->val) +		u8tmp = 0x19; +	else if (s->if_gain_auto->cur.val) +		u8tmp = 0x16; +	else +		u8tmp = 0x10; + +	ret = regmap_write(s->regmap, 0x1a, u8tmp); +	if (ret) +		goto err; + +	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(&s->client->dev, "%s: failed=%d\n", __func__, ret); -	dev_dbg(&priv->i2c->dev, "%s:\n", __func__); +	return ret; +} -	kfree(fe->tuner_priv); +static int e4000_set_mixer_gain(struct dvb_frontend *fe) +{ +	struct e4000 *s = fe->tuner_priv; +	int ret; +	u8 u8tmp; -	return 0; +	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 (s->mixer_gain_auto->val) +		u8tmp = 0x15; +	else +		u8tmp = 0x14; + +	ret = regmap_write(s->regmap, 0x20, u8tmp); +	if (ret) +		goto err; + +	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(&s->client->dev, "%s: failed=%d\n", __func__, ret); + +	return ret;  } +static int e4000_set_if_gain(struct dvb_frontend *fe) +{ +	struct e4000 *s = fe->tuner_priv; +	int ret; +	u8 buf[2]; +	u8 u8tmp; + +	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 (s->if_gain_auto->val && s->lna_gain_auto->cur.val) +		u8tmp = 0x17; +	else if (s->lna_gain_auto->cur.val) +		u8tmp = 0x19; +	else if (s->if_gain_auto->val) +		u8tmp = 0x16; +	else +		u8tmp = 0x10; + +	ret = regmap_write(s->regmap, 0x1a, u8tmp); +	if (ret) +		goto err; + +	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(&s->client->dev, "%s: failed=%d\n", __func__, ret); + +	return ret; +} + +static int e4000_pll_lock(struct dvb_frontend *fe) +{ +	struct e4000 *s = fe->tuner_priv; +	int ret; +	unsigned int utmp; + +	ret = regmap_read(s->regmap, 0x07, &utmp); +	if (ret) +		goto err; + +	s->pll_lock->val = (utmp & 0x01); +err: +	if (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 *s = container_of(ctrl->handler, struct e4000, hdl); +	int ret; + +	if (s->active == false) +		return 0; + +	switch (ctrl->id) { +	case  V4L2_CID_RF_TUNER_PLL_LOCK: +		ret = e4000_pll_lock(s->fe); +		break; +	default: +		dev_dbg(&s->client->dev, "%s: unknown ctrl: id=%d name=%s\n", +				__func__, ctrl->id, ctrl->name); +		ret = -EINVAL; +	} + +	return ret; +} + +static int e4000_s_ctrl(struct v4l2_ctrl *ctrl) +{ +	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 (s->active == false) +		return 0; + +	switch (ctrl->id) { +	case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: +	case V4L2_CID_RF_TUNER_BANDWIDTH: +		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(s->fe); +		break; +	case  V4L2_CID_RF_TUNER_MIXER_GAIN_AUTO: +	case  V4L2_CID_RF_TUNER_MIXER_GAIN: +		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(s->fe); +		break; +	default: +		dev_dbg(&s->client->dev, "%s: unknown ctrl: id=%d name=%s\n", +				__func__, ctrl->id, ctrl->name); +		ret = -EINVAL; +	} + +	return ret; +} + +static const struct v4l2_ctrl_ops e4000_ctrl_ops = { +	.g_volatile_ctrl = e4000_g_volatile_ctrl, +	.s_ctrl = e4000_s_ctrl, +}; +#endif +  static const struct dvb_tuner_ops e4000_tuner_ops = {  	.info = {  		.name           = "Elonics E4000", @@ -376,8 +466,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, @@ -385,62 +473,148 @@ 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) +/* + * Use V4L2 subdev to carry V4L2 control handler, even we don't implement + * subdev itself, just to avoid reinventing the wheel. + */ +static int e4000_probe(struct i2c_client *client, +		const struct i2c_device_id *id)  { -	struct e4000_priv *priv; +	struct e4000_config *cfg = client->dev.platform_data; +	struct dvb_frontend *fe = cfg->fe; +	struct e4000 *s;  	int ret; -	u8 chip_id; - -	if (fe->ops.i2c_gate_ctrl) -		fe->ops.i2c_gate_ctrl(fe, 1); +	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) { +	s = kzalloc(sizeof(struct e4000), GFP_KERNEL); +	if (!s) {  		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; +	s->clock = cfg->clock; +	s->client = client; +	s->fe = cfg->fe; +	s->regmap = devm_regmap_init_i2c(client, ®map_config); +	if (IS_ERR(s->regmap)) { +		ret = PTR_ERR(s->regmap); +		goto err; +	}  	/* check if the tuner is there */ -	ret = e4000_rd_reg(priv, 0x02, &chip_id); -	if (ret < 0) +	ret = regmap_read(s->regmap, 0x02, &utmp); +	if (ret)  		goto err; -	dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id); +	dev_dbg(&s->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); -	if (ret < 0) +	ret = regmap_write(s->regmap, 0x00, 0x00); +	if (ret) +		goto err; + +#if IS_ENABLED(CONFIG_VIDEO_V4L2) +	/* Register controls */ +	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); +	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, &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); +	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, &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); +	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, &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); +	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, &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 (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->i2c->dev, +	s->sd.ctrl_handler = &s->hdl; +#endif + +	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)); -	if (fe->ops.i2c_gate_ctrl) -		fe->ops.i2c_gate_ctrl(fe, 0); +	v4l2_set_subdevdata(&s->sd, client); +	i2c_set_clientdata(client, &s->sd); -	return fe; +	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(s); +	} -	dev_dbg(&i2c->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 v4l2_subdev *sd = i2c_get_clientdata(client); +	struct e4000 *s = container_of(sd, struct e4000, sd); +	struct dvb_frontend *fe = s->fe; + +	dev_dbg(&client->dev, "%s:\n", __func__); + +#if IS_ENABLED(CONFIG_VIDEO_V4L2) +	v4l2_ctrl_handler_free(&s->hdl); +#endif +	memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); +	fe->tuner_priv = NULL; +	kfree(s); + +	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 25ee7c07abf..e74b8b2f2fc 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 a3855053e78..cb0070483e6 100644 --- a/drivers/media/tuners/e4000_priv.h +++ b/drivers/media/tuners/e4000_priv.h @@ -22,10 +22,29 @@  #define E4000_PRIV_H  #include "e4000.h" +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> +#include <linux/regmap.h> -struct e4000_priv { -	const struct e4000_config *cfg; -	struct i2c_adapter *i2c; +struct e4000 { +	struct i2c_client *client; +	struct regmap *regmap; +	u32 clock; +	struct dvb_frontend *fe; +	struct v4l2_subdev sd; +	bool active; + +	/* 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 v4l2_ctrl *pll_lock;  };  struct e4000_pll { @@ -144,4 +163,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 diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index f4d0e797a6c..d74e9205681 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -139,7 +139,7 @@ static int fc0012_set_params(struct dvb_frontend *fe)  	unsigned char reg[7], am, pm, multi, tmp;  	unsigned long f_vco;  	unsigned short xtal_freq_khz_2, xin, xdiv; -	int vco_select = false; +	bool vco_select = false;  	if (fe->callback) {  		ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, diff --git a/drivers/media/tuners/fc0013.c b/drivers/media/tuners/fc0013.c index bd8f0f1e8f3..b4162315773 100644 --- a/drivers/media/tuners/fc0013.c +++ b/drivers/media/tuners/fc0013.c @@ -233,7 +233,7 @@ static int fc0013_set_params(struct dvb_frontend *fe)  	unsigned char reg[7], am, pm, multi, tmp;  	unsigned long f_vco;  	unsigned short xtal_freq_khz_2, xin, xdiv; -	int vco_select = false; +	bool vco_select = false;  	if (fe->callback) {  		ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c index 81f38aae9c6..f0c9c42867d 100644 --- a/drivers/media/tuners/fc2580.c +++ b/drivers/media/tuners/fc2580.c @@ -20,6 +20,9 @@  #include "fc2580_priv.h" +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE  64 +  /*   * TODO:   * I2C write and read works only for one single register. Multiple registers @@ -41,16 +44,23 @@  static int fc2580_wr_regs(struct fc2580_priv *priv, u8 reg, u8 *val, int len)  {  	int ret; -	u8 buf[1 + len]; +	u8 buf[MAX_XFER_SIZE];  	struct i2c_msg msg[1] = {  		{  			.addr = priv->cfg->i2c_addr,  			.flags = 0, -			.len = sizeof(buf), +			.len = 1 + len,  			.buf = buf,  		}  	}; +	if (1 + len > sizeof(buf)) { +		dev_warn(&priv->i2c->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); @@ -69,7 +79,7 @@ static int fc2580_wr_regs(struct fc2580_priv *priv, u8 reg, u8 *val, int len)  static int fc2580_rd_regs(struct fc2580_priv *priv, u8 reg, u8 *val, int len)  {  	int ret; -	u8 buf[len]; +	u8 buf[MAX_XFER_SIZE];  	struct i2c_msg msg[2] = {  		{  			.addr = priv->cfg->i2c_addr, @@ -79,11 +89,18 @@ static int fc2580_rd_regs(struct fc2580_priv *priv, u8 reg, u8 *val, int len)  		}, {  			.addr = priv->cfg->i2c_addr,  			.flags = I2C_M_RD, -			.len = sizeof(buf), +			.len = len,  			.buf = buf,  		}  	}; +	if (len > sizeof(buf)) { +		dev_warn(&priv->i2c->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);  	if (ret == 2) {  		memcpy(val, buf, len); @@ -178,7 +195,7 @@ static int fc2580_set_params(struct dvb_frontend *fe)  	f_ref = 2UL * priv->cfg->clock / r_val;  	n_val = div_u64_rem(f_vco, f_ref, &k_val); -	k_val_reg = 1UL * k_val * (1 << 20) / f_ref; +	k_val_reg = div_u64(1ULL * k_val * (1 << 20), f_ref);  	ret = fc2580_wr_reg(priv, 0x18, r18_val | ((k_val_reg >> 16) & 0xff));  	if (ret < 0) @@ -331,8 +348,8 @@ static int fc2580_set_params(struct dvb_frontend *fe)  	if (ret < 0)  		goto err; -	ret = fc2580_wr_reg(priv, 0x37, 1UL * priv->cfg->clock * \ -			fc2580_if_filter_lut[i].mul / 1000000000); +	ret = fc2580_wr_reg(priv, 0x37, div_u64(1ULL * priv->cfg->clock * +			fc2580_if_filter_lut[i].mul, 1000000000));  	if (ret < 0)  		goto err; diff --git a/drivers/media/tuners/fc2580_priv.h b/drivers/media/tuners/fc2580_priv.h index be38a9e637e..646c9945213 100644 --- a/drivers/media/tuners/fc2580_priv.h +++ b/drivers/media/tuners/fc2580_priv.h @@ -22,6 +22,7 @@  #define FC2580_PRIV_H  #include "fc2580.h" +#include <linux/math64.h>  struct fc2580_reg_val {  	u8 reg; diff --git a/drivers/media/tuners/m88ts2022.c b/drivers/media/tuners/m88ts2022.c new file mode 100644 index 00000000000..40c42dec721 --- /dev/null +++ b/drivers/media/tuners/m88ts2022.c @@ -0,0 +1,674 @@ +/* + * Montage M88TS2022 silicon tuner 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. + * + * Some calculations are taken from existing TS2020 driver. + */ + +#include "m88ts2022_priv.h" + +/* write multiple registers */ +static int m88ts2022_wr_regs(struct m88ts2022_priv *priv, +		u8 reg, const u8 *val, int len) +{ +#define MAX_WR_LEN 3 +#define MAX_WR_XFER_LEN (MAX_WR_LEN + 1) +	int ret; +	u8 buf[MAX_WR_XFER_LEN]; +	struct i2c_msg msg[1] = { +		{ +			.addr = priv->client->addr, +			.flags = 0, +			.len = 1 + len, +			.buf = buf, +		} +	}; + +	if (WARN_ON(len > MAX_WR_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 m88ts2022_rd_regs(struct m88ts2022_priv *priv, u8 reg, +		u8 *val, int len) +{ +#define MAX_RD_LEN 1 +#define MAX_RD_XFER_LEN (MAX_RD_LEN) +	int ret; +	u8 buf[MAX_RD_XFER_LEN]; +	struct i2c_msg msg[2] = { +		{ +			.addr = priv->client->addr, +			.flags = 0, +			.len = 1, +			.buf = ®, +		}, { +			.addr = priv->client->addr, +			.flags = I2C_M_RD, +			.len = len, +			.buf = buf, +		} +	}; + +	if (WARN_ON(len > MAX_RD_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 m88ts2022_wr_reg(struct m88ts2022_priv *priv, u8 reg, u8 val) +{ +	return m88ts2022_wr_regs(priv, reg, &val, 1); +} + +/* read single register */ +static int m88ts2022_rd_reg(struct m88ts2022_priv *priv, u8 reg, u8 *val) +{ +	return m88ts2022_rd_regs(priv, reg, val, 1); +} + +/* write single register with mask */ +static int m88ts2022_wr_reg_mask(struct m88ts2022_priv *priv, +		u8 reg, u8 val, u8 mask) +{ +	int ret; +	u8 u8tmp; + +	/* no need for read if whole reg is written */ +	if (mask != 0xff) { +		ret = m88ts2022_rd_regs(priv, reg, &u8tmp, 1); +		if (ret) +			return ret; + +		val &= mask; +		u8tmp &= ~mask; +		val |= u8tmp; +	} + +	return m88ts2022_wr_regs(priv, reg, &val, 1); +} + +static int m88ts2022_cmd(struct dvb_frontend *fe, +		int op, int sleep, u8 reg, u8 mask, u8 val, u8 *reg_val) +{ +	struct m88ts2022_priv *priv = fe->tuner_priv; +	int ret, i; +	u8 u8tmp; +	struct m88ts2022_reg_val reg_vals[] = { +		{0x51, 0x1f - op}, +		{0x51, 0x1f}, +		{0x50, 0x00 + op}, +		{0x50, 0x00}, +	}; + +	for (i = 0; i < 2; i++) { +		dev_dbg(&priv->client->dev, +				"%s: i=%d op=%02x reg=%02x mask=%02x val=%02x\n", +				__func__, i, op, reg, mask, val); + +		for (i = 0; i < ARRAY_SIZE(reg_vals); i++) { +			ret = m88ts2022_wr_reg(priv, reg_vals[i].reg, +					reg_vals[i].val); +			if (ret) +				goto err; +		} + +		usleep_range(sleep * 1000, sleep * 10000); + +		ret = m88ts2022_rd_reg(priv, reg, &u8tmp); +		if (ret) +			goto err; + +		if ((u8tmp & mask) != val) +			break; +	} + +	if (reg_val) +		*reg_val = u8tmp; +err: +	return ret; +} + +static int m88ts2022_set_params(struct dvb_frontend *fe) +{ +	struct m88ts2022_priv *priv = fe->tuner_priv; +	struct dtv_frontend_properties *c = &fe->dtv_property_cache; +	int ret; +	unsigned int frequency_khz, frequency_offset_khz, f_3db_hz; +	unsigned int f_ref_khz, f_vco_khz, div_ref, div_out, pll_n, gdiv28; +	u8 buf[3], u8tmp, cap_code, lpf_gm, lpf_mxdiv, div_max, div_min; +	u16 u16tmp; +	dev_dbg(&priv->client->dev, +			"%s: frequency=%d symbol_rate=%d rolloff=%d\n", +			__func__, c->frequency, c->symbol_rate, c->rolloff); +	/* +	 * Integer-N PLL synthesizer +	 * kHz is used for all calculations to keep calculations within 32-bit +	 */ +	f_ref_khz = DIV_ROUND_CLOSEST(priv->cfg.clock, 1000); +	div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000); + +	if (c->symbol_rate < 5000000) +		frequency_offset_khz = 3000; /* 3 MHz */ +	else +		frequency_offset_khz = 0; + +	frequency_khz = c->frequency + frequency_offset_khz; + +	if (frequency_khz < 1103000) { +		div_out = 4; +		u8tmp = 0x1b; +	} else { +		div_out = 2; +		u8tmp = 0x0b; +	} + +	buf[0] = u8tmp; +	buf[1] = 0x40; +	ret = m88ts2022_wr_regs(priv, 0x10, buf, 2); +	if (ret) +		goto err; + +	f_vco_khz = frequency_khz * div_out; +	pll_n = f_vco_khz * div_ref / f_ref_khz; +	pll_n += pll_n % 2; +	priv->frequency_khz = pll_n * f_ref_khz / div_ref / div_out; + +	if (pll_n < 4095) +		u16tmp = pll_n - 1024; +	else if (pll_n < 6143) +		u16tmp = pll_n + 1024; +	else +		u16tmp = pll_n + 3072; + +	buf[0] = (u16tmp >> 8) & 0x3f; +	buf[1] = (u16tmp >> 0) & 0xff; +	buf[2] = div_ref - 8; +	ret = m88ts2022_wr_regs(priv, 0x01, buf, 3); +	if (ret) +		goto err; + +	dev_dbg(&priv->client->dev, +			"%s: frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n", +			__func__, priv->frequency_khz, +			priv->frequency_khz - c->frequency, f_vco_khz, pll_n, +			div_ref, div_out); + +	ret = m88ts2022_cmd(fe, 0x10, 5, 0x15, 0x40, 0x00, NULL); +	if (ret) +		goto err; + +	ret = m88ts2022_rd_reg(priv, 0x14, &u8tmp); +	if (ret) +		goto err; + +	u8tmp &= 0x7f; +	if (u8tmp < 64) { +		ret = m88ts2022_wr_reg_mask(priv, 0x10, 0x80, 0x80); +		if (ret) +			goto err; + +		ret = m88ts2022_wr_reg(priv, 0x11, 0x6f); +		if (ret) +			goto err; + +		ret = m88ts2022_cmd(fe, 0x10, 5, 0x15, 0x40, 0x00, NULL); +		if (ret) +			goto err; +	} + +	ret = m88ts2022_rd_reg(priv, 0x14, &u8tmp); +	if (ret) +		goto err; + +	u8tmp &= 0x1f; +	if (u8tmp > 19) { +		ret = m88ts2022_wr_reg_mask(priv, 0x10, 0x00, 0x02); +		if (ret) +			goto err; +	} + +	ret = m88ts2022_cmd(fe, 0x08, 5, 0x3c, 0xff, 0x00, NULL); +	if (ret) +		goto err; + +	ret = m88ts2022_wr_reg(priv, 0x25, 0x00); +	if (ret) +		goto err; + +	ret = m88ts2022_wr_reg(priv, 0x27, 0x70); +	if (ret) +		goto err; + +	ret = m88ts2022_wr_reg(priv, 0x41, 0x09); +	if (ret) +		goto err; + +	ret = m88ts2022_wr_reg(priv, 0x08, 0x0b); +	if (ret) +		goto err; + +	/* filters */ +	gdiv28 = DIV_ROUND_CLOSEST(f_ref_khz * 1694U, 1000000U); + +	ret = m88ts2022_wr_reg(priv, 0x04, gdiv28); +	if (ret) +		goto err; + +	ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); +	if (ret) +		goto err; + +	cap_code = u8tmp & 0x3f; + +	ret = m88ts2022_wr_reg(priv, 0x41, 0x0d); +	if (ret) +		goto err; + +	ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); +	if (ret) +		goto err; + +	u8tmp &= 0x3f; +	cap_code = (cap_code + u8tmp) / 2; +	gdiv28 = gdiv28 * 207 / (cap_code * 2 + 151); +	div_max = gdiv28 * 135 / 100; +	div_min = gdiv28 * 78 / 100; +	div_max = clamp_val(div_max, 0U, 63U); + +	f_3db_hz = c->symbol_rate * 135UL / 200UL; +	f_3db_hz +=  2000000U + (frequency_offset_khz * 1000U); +	f_3db_hz = clamp(f_3db_hz, 7000000U, 40000000U); + +#define LPF_COEFF 3200U +	lpf_gm = DIV_ROUND_CLOSEST(f_3db_hz * gdiv28, LPF_COEFF * f_ref_khz); +	lpf_gm = clamp_val(lpf_gm, 1U, 23U); + +	lpf_mxdiv = DIV_ROUND_CLOSEST(lpf_gm * LPF_COEFF * f_ref_khz, f_3db_hz); +	if (lpf_mxdiv < div_min) +		lpf_mxdiv = DIV_ROUND_CLOSEST(++lpf_gm * LPF_COEFF * f_ref_khz, f_3db_hz); +	lpf_mxdiv = clamp_val(lpf_mxdiv, 0U, div_max); + +	ret = m88ts2022_wr_reg(priv, 0x04, lpf_mxdiv); +	if (ret) +		goto err; + +	ret = m88ts2022_wr_reg(priv, 0x06, lpf_gm); +	if (ret) +		goto err; + +	ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); +	if (ret) +		goto err; + +	cap_code = u8tmp & 0x3f; + +	ret = m88ts2022_wr_reg(priv, 0x41, 0x09); +	if (ret) +		goto err; + +	ret = m88ts2022_cmd(fe, 0x04, 2, 0x26, 0xff, 0x00, &u8tmp); +	if (ret) +		goto err; + +	u8tmp &= 0x3f; +	cap_code = (cap_code + u8tmp) / 2; + +	u8tmp = cap_code | 0x80; +	ret = m88ts2022_wr_reg(priv, 0x25, u8tmp); +	if (ret) +		goto err; + +	ret = m88ts2022_wr_reg(priv, 0x27, 0x30); +	if (ret) +		goto err; + +	ret = m88ts2022_wr_reg(priv, 0x08, 0x09); +	if (ret) +		goto err; + +	ret = m88ts2022_cmd(fe, 0x01, 20, 0x21, 0xff, 0x00, NULL); +	if (ret) +		goto err; +err: +	if (ret) +		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); + +	return ret; +} + +static int m88ts2022_init(struct dvb_frontend *fe) +{ +	struct m88ts2022_priv *priv = fe->tuner_priv; +	int ret, i; +	u8 u8tmp; +	static const struct m88ts2022_reg_val reg_vals[] = { +		{0x7d, 0x9d}, +		{0x7c, 0x9a}, +		{0x7a, 0x76}, +		{0x3b, 0x01}, +		{0x63, 0x88}, +		{0x61, 0x85}, +		{0x22, 0x30}, +		{0x30, 0x40}, +		{0x20, 0x23}, +		{0x24, 0x02}, +		{0x12, 0xa0}, +	}; +	dev_dbg(&priv->client->dev, "%s:\n", __func__); + +	ret = m88ts2022_wr_reg(priv, 0x00, 0x01); +	if (ret) +		goto err; + +	ret = m88ts2022_wr_reg(priv, 0x00, 0x03); +	if (ret) +		goto err; + +	switch (priv->cfg.clock_out) { +	case M88TS2022_CLOCK_OUT_DISABLED: +		u8tmp = 0x60; +		break; +	case M88TS2022_CLOCK_OUT_ENABLED: +		u8tmp = 0x70; +		ret = m88ts2022_wr_reg(priv, 0x05, priv->cfg.clock_out_div); +		if (ret) +			goto err; +		break; +	case M88TS2022_CLOCK_OUT_ENABLED_XTALOUT: +		u8tmp = 0x6c; +		break; +	default: +		goto err; +	} + +	ret = m88ts2022_wr_reg(priv, 0x42, u8tmp); +	if (ret) +		goto err; + +	if (priv->cfg.loop_through) +		u8tmp = 0xec; +	else +		u8tmp = 0x6c; + +	ret = m88ts2022_wr_reg(priv, 0x62, u8tmp); +	if (ret) +		goto err; + +	for (i = 0; i < ARRAY_SIZE(reg_vals); i++) { +		ret = m88ts2022_wr_reg(priv, reg_vals[i].reg, reg_vals[i].val); +		if (ret) +			goto err; +	} +err: +	if (ret) +		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); +	return ret; +} + +static int m88ts2022_sleep(struct dvb_frontend *fe) +{ +	struct m88ts2022_priv *priv = fe->tuner_priv; +	int ret; +	dev_dbg(&priv->client->dev, "%s:\n", __func__); + +	ret = m88ts2022_wr_reg(priv, 0x00, 0x00); +	if (ret) +		goto err; +err: +	if (ret) +		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); +	return ret; +} + +static int m88ts2022_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ +	struct m88ts2022_priv *priv = fe->tuner_priv; +	dev_dbg(&priv->client->dev, "%s:\n", __func__); + +	*frequency = priv->frequency_khz; +	return 0; +} + +static int m88ts2022_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ +	struct m88ts2022_priv *priv = fe->tuner_priv; +	dev_dbg(&priv->client->dev, "%s:\n", __func__); + +	*frequency = 0; /* Zero-IF */ +	return 0; +} + +static int m88ts2022_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +{ +	struct m88ts2022_priv *priv = fe->tuner_priv; +	int ret; +	u8 u8tmp; +	u16 gain, u16tmp; +	unsigned int gain1, gain2, gain3; + +	ret = m88ts2022_rd_reg(priv, 0x3d, &u8tmp); +	if (ret) +		goto err; + +	gain1 = (u8tmp >> 0) & 0x1f; +	gain1 = clamp(gain1, 0U, 15U); + +	ret = m88ts2022_rd_reg(priv, 0x21, &u8tmp); +	if (ret) +		goto err; + +	gain2 = (u8tmp >> 0) & 0x1f; +	gain2 = clamp(gain2, 2U, 16U); + +	ret = m88ts2022_rd_reg(priv, 0x66, &u8tmp); +	if (ret) +		goto err; + +	gain3 = (u8tmp >> 3) & 0x07; +	gain3 = clamp(gain3, 0U, 6U); + +	gain = gain1 * 265 + gain2 * 338 + gain3 * 285; + +	/* scale value to 0x0000-0xffff */ +	u16tmp = (0xffff - gain); +	u16tmp = clamp_val(u16tmp, 59000U, 61500U); + +	*strength = (u16tmp - 59000) * 0xffff / (61500 - 59000); +err: +	if (ret) +		dev_dbg(&priv->client->dev, "%s: failed=%d\n", __func__, ret); +	return ret; +} + +static const struct dvb_tuner_ops m88ts2022_tuner_ops = { +	.info = { +		.name          = "Montage M88TS2022", +		.frequency_min = 950000, +		.frequency_max = 2150000, +	}, + +	.init = m88ts2022_init, +	.sleep = m88ts2022_sleep, +	.set_params = m88ts2022_set_params, + +	.get_frequency = m88ts2022_get_frequency, +	.get_if_frequency = m88ts2022_get_if_frequency, +	.get_rf_strength = m88ts2022_get_rf_strength, +}; + +static int m88ts2022_probe(struct i2c_client *client, +		const struct i2c_device_id *id) +{ +	struct m88ts2022_config *cfg = client->dev.platform_data; +	struct dvb_frontend *fe = cfg->fe; +	struct m88ts2022_priv *priv; +	int ret; +	u8 chip_id, u8tmp; + +	priv = kzalloc(sizeof(*priv), GFP_KERNEL); +	if (!priv) { +		ret = -ENOMEM; +		dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); +		goto err; +	} + +	memcpy(&priv->cfg, cfg, sizeof(struct m88ts2022_config)); +	priv->client = client; + +	/* check if the tuner is there */ +	ret = m88ts2022_rd_reg(priv, 0x00, &u8tmp); +	if (ret) +		goto err; + +	if ((u8tmp & 0x03) == 0x00) { +		ret = m88ts2022_wr_reg(priv, 0x00, 0x01); +		if (ret < 0) +			goto err; + +		usleep_range(2000, 50000); +	} + +	ret = m88ts2022_wr_reg(priv, 0x00, 0x03); +	if (ret) +		goto err; + +	usleep_range(2000, 50000); + +	ret = m88ts2022_rd_reg(priv, 0x00, &chip_id); +	if (ret) +		goto err; + +	dev_dbg(&priv->client->dev, "%s: chip_id=%02x\n", __func__, chip_id); + +	switch (chip_id) { +	case 0xc3: +	case 0x83: +		break; +	default: +		goto err; +	} + +	switch (priv->cfg.clock_out) { +	case M88TS2022_CLOCK_OUT_DISABLED: +		u8tmp = 0x60; +		break; +	case M88TS2022_CLOCK_OUT_ENABLED: +		u8tmp = 0x70; +		ret = m88ts2022_wr_reg(priv, 0x05, priv->cfg.clock_out_div); +		if (ret) +			goto err; +		break; +	case M88TS2022_CLOCK_OUT_ENABLED_XTALOUT: +		u8tmp = 0x6c; +		break; +	default: +		goto err; +	} + +	ret = m88ts2022_wr_reg(priv, 0x42, u8tmp); +	if (ret) +		goto err; + +	if (priv->cfg.loop_through) +		u8tmp = 0xec; +	else +		u8tmp = 0x6c; + +	ret = m88ts2022_wr_reg(priv, 0x62, u8tmp); +	if (ret) +		goto err; + +	/* sleep */ +	ret = m88ts2022_wr_reg(priv, 0x00, 0x00); +	if (ret) +		goto err; + +	dev_info(&priv->client->dev, +			"%s: Montage M88TS2022 successfully identified\n", +			KBUILD_MODNAME); + +	fe->tuner_priv = priv; +	memcpy(&fe->ops.tuner_ops, &m88ts2022_tuner_ops, +			sizeof(struct dvb_tuner_ops)); + +	i2c_set_clientdata(client, priv); +	return 0; +err: +	dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret); +	kfree(priv); +	return ret; +} + +static int m88ts2022_remove(struct i2c_client *client) +{ +	struct m88ts2022_priv *priv = i2c_get_clientdata(client); +	struct dvb_frontend *fe = priv->cfg.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 m88ts2022_id[] = { +	{"m88ts2022", 0}, +	{} +}; +MODULE_DEVICE_TABLE(i2c, m88ts2022_id); + +static struct i2c_driver m88ts2022_driver = { +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "m88ts2022", +	}, +	.probe		= m88ts2022_probe, +	.remove		= m88ts2022_remove, +	.id_table	= m88ts2022_id, +}; + +module_i2c_driver(m88ts2022_driver); + +MODULE_DESCRIPTION("Montage M88TS2022 silicon tuner driver"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/m88ts2022.h b/drivers/media/tuners/m88ts2022.h new file mode 100644 index 00000000000..659fa1b1633 --- /dev/null +++ b/drivers/media/tuners/m88ts2022.h @@ -0,0 +1,54 @@ +/* + * Montage M88TS2022 silicon tuner 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. + */ + +#ifndef M88TS2022_H +#define M88TS2022_H + +#include "dvb_frontend.h" + +struct m88ts2022_config { +	/* +	 * clock +	 * 16000000 - 32000000 +	 */ +	u32 clock; + +	/* +	 * RF loop-through +	 */ +	u8 loop_through:1; + +	/* +	 * clock output +	 */ +#define M88TS2022_CLOCK_OUT_DISABLED        0 +#define M88TS2022_CLOCK_OUT_ENABLED         1 +#define M88TS2022_CLOCK_OUT_ENABLED_XTALOUT 2 +	u8 clock_out:2; + +	/* +	 * clock output divider +	 * 1 - 31 +	 */ +	u8 clock_out_div:5; + +	/* +	 * pointer to DVB frontend +	 */ +	struct dvb_frontend *fe; +}; + +#endif diff --git a/drivers/media/tuners/m88ts2022_priv.h b/drivers/media/tuners/m88ts2022_priv.h new file mode 100644 index 00000000000..0363dd866a2 --- /dev/null +++ b/drivers/media/tuners/m88ts2022_priv.h @@ -0,0 +1,34 @@ +/* + * Montage M88TS2022 silicon tuner 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. + */ + +#ifndef M88TS2022_PRIV_H +#define M88TS2022_PRIV_H + +#include "m88ts2022.h" + +struct m88ts2022_priv { +	struct m88ts2022_config cfg; +	struct i2c_client *client; +	struct dvb_frontend *fe; +	u32 frequency_khz; +}; + +struct m88ts2022_reg_val { +	u8 reg; +	u8 val; +}; + +#endif diff --git a/drivers/media/tuners/mt2063.c b/drivers/media/tuners/mt2063.c index 2e1a02e360f..f640dcf4a81 100644 --- a/drivers/media/tuners/mt2063.c +++ b/drivers/media/tuners/mt2063.c @@ -1,7 +1,7 @@  /*   * Driver for mt2063 Micronas tuner   * - * Copyright (c) 2011 Mauro Carvalho Chehab <mchehab@redhat.com> + * Copyright (c) 2011 Mauro Carvalho Chehab   *   * This driver came from a driver originally written by:   *		Henry Wang <Henry.wang@AzureWave.com> @@ -1195,7 +1195,7 @@ static u32 mt2063_set_dnc_output_enable(struct mt2063_state *state,   *   DNC Output is selected, the other is always off)   *   * @state:	ptr to mt2063_state structure - * @Mode:	desired reciever delivery system + * @Mode:	desired receiver delivery system   *   * Note: Register cache must be valid for it to work   */ @@ -2119,7 +2119,7 @@ static int mt2063_set_analog_params(struct dvb_frontend *fe,  /*   * As defined on EN 300 429, the DVB-C roll-off factor is 0.15. - * So, the amount of the needed bandwith is given by: + * So, the amount of the needed bandwidth is given by:   *	Bw = Symbol_rate * (1 + 0.15)   * As such, the maximum symbol rate supported by 6 MHz is given by:   *	max_symbol_rate = 6 MHz / 1.15 = 5217391 Bauds @@ -2298,6 +2298,6 @@ static int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe)  }  #endif -MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); +MODULE_AUTHOR("Mauro Carvalho Chehab");  MODULE_DESCRIPTION("MT2063 Silicon tuner");  MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 1c23666468c..96ccfebce7c 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -1,7 +1,7 @@  /*   * Rafael Micro R820T driver   * - * Copyright (C) 2013 Mauro Carvalho Chehab <mchehab@redhat.com> + * Copyright (C) 2013 Mauro Carvalho Chehab   *   * This driver was written from scratch, based on an existing driver   * that it is part of rtl-sdr git tree, released under GPLv2: @@ -612,10 +612,19 @@ static int r820t_set_pll(struct r820t_priv *priv, enum v4l2_tuner_type type,  	vco_fine_tune = (data[4] & 0x30) >> 4; -	if (vco_fine_tune > VCO_POWER_REF) -		div_num = div_num - 1; -	else if (vco_fine_tune < VCO_POWER_REF) -		div_num = div_num + 1; +	tuner_dbg("mix_div=%d div_num=%d vco_fine_tune=%d\n", +			mix_div, div_num, vco_fine_tune); + +	/* +	 * XXX: R828D/16MHz seems to have always vco_fine_tune=1. +	 * Due to that, this calculation goes wrong. +	 */ +	if (priv->cfg->rafael_chip != CHIP_R828D) { +		if (vco_fine_tune > VCO_POWER_REF) +			div_num = div_num - 1; +		else if (vco_fine_tune < VCO_POWER_REF) +			div_num = div_num + 1; +	}  	rc = r820t_write_reg_mask(priv, 0x10, div_num << 5, 0xe0);  	if (rc < 0) @@ -637,11 +646,6 @@ static int r820t_set_pll(struct r820t_priv *priv, enum v4l2_tuner_type type,  		vco_fra = pll_ref * 129 / 128;  	} -	if (nint > 63) { -		tuner_info("No valid PLL values for %u kHz!\n", freq); -		return -EINVAL; -	} -  	ni = (nint - 13) / 4;  	si = nint - 4 * ni - 13; @@ -1464,7 +1468,8 @@ static int r820t_imr_prepare(struct r820t_priv *priv)  static int r820t_multi_read(struct r820t_priv *priv)  {  	int rc, i; -	u8 data[2], min = 0, max = 255, sum = 0; +	u16 sum = 0; +	u8 data[2], min = 255, max = 0;  	usleep_range(5000, 6000); @@ -2347,5 +2352,5 @@ err_no_gate:  EXPORT_SYMBOL_GPL(r820t_attach);  MODULE_DESCRIPTION("Rafael Micro r820t silicon tuner driver"); -MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); +MODULE_AUTHOR("Mauro Carvalho Chehab");  MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c new file mode 100644 index 00000000000..fa4cc7b880a --- /dev/null +++ b/drivers/media/tuners/si2157.c @@ -0,0 +1,260 @@ +/* + * Silicon Labs Si2157 silicon tuner driver + * + * Copyright (C) 2014 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. + */ + +#include "si2157_priv.h" + +/* execute firmware command */ +static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd) +{ +	int ret; +	u8 buf[1]; +	unsigned long timeout; + +	mutex_lock(&s->i2c_mutex); + +	if (cmd->len) { +		/* write cmd and args for firmware */ +		ret = i2c_master_send(s->client, cmd->args, cmd->len); +		if (ret < 0) { +			goto err_mutex_unlock; +		} else if (ret != cmd->len) { +			ret = -EREMOTEIO; +			goto err_mutex_unlock; +		} +	} + +	/* wait cmd execution terminate */ +	#define TIMEOUT 80 +	timeout = jiffies + msecs_to_jiffies(TIMEOUT); +	while (!time_after(jiffies, timeout)) { +		ret = i2c_master_recv(s->client, buf, 1); +		if (ret < 0) { +			goto err_mutex_unlock; +		} else if (ret != 1) { +			ret = -EREMOTEIO; +			goto err_mutex_unlock; +		} + +		/* firmware ready? */ +		if ((buf[0] >> 7) & 0x01) +			break; +	} + +	dev_dbg(&s->client->dev, "%s: cmd execution took %d ms\n", __func__, +			jiffies_to_msecs(jiffies) - +			(jiffies_to_msecs(timeout) - TIMEOUT)); + +	if (!((buf[0] >> 7) & 0x01)) { +		ret = -ETIMEDOUT; +		goto err_mutex_unlock; +	} else { +		ret = 0; +	} + +err_mutex_unlock: +	mutex_unlock(&s->i2c_mutex); +	if (ret) +		goto err; + +	return 0; +err: +	dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); +	return ret; +} + +static int si2157_init(struct dvb_frontend *fe) +{ +	struct si2157 *s = fe->tuner_priv; + +	dev_dbg(&s->client->dev, "%s:\n", __func__); + +	s->active = true; + +	return 0; +} + +static int si2157_sleep(struct dvb_frontend *fe) +{ +	struct si2157 *s = fe->tuner_priv; + +	dev_dbg(&s->client->dev, "%s:\n", __func__); + +	s->active = false; + +	return 0; +} + +static int si2157_set_params(struct dvb_frontend *fe) +{ +	struct si2157 *s = fe->tuner_priv; +	struct dtv_frontend_properties *c = &fe->dtv_property_cache; +	int ret; +	struct si2157_cmd cmd; + +	dev_dbg(&s->client->dev, +			"%s: delivery_system=%d frequency=%u bandwidth_hz=%u\n", +			__func__, c->delivery_system, c->frequency, +			c->bandwidth_hz); + +	if (!s->active) { +		ret = -EAGAIN; +		goto err; +	} + +	/* configure? */ +	cmd.args[0] = 0xc0; +	cmd.args[1] = 0x00; +	cmd.args[2] = 0x0c; +	cmd.args[3] = 0x00; +	cmd.args[4] = 0x00; +	cmd.args[5] = 0x01; +	cmd.args[6] = 0x01; +	cmd.args[7] = 0x01; +	cmd.args[8] = 0x01; +	cmd.args[9] = 0x01; +	cmd.args[10] = 0x01; +	cmd.args[11] = 0x02; +	cmd.args[12] = 0x00; +	cmd.args[13] = 0x00; +	cmd.args[14] = 0x01; +	cmd.len = 15; +	ret = si2157_cmd_execute(s, &cmd); +	if (ret) +		goto err; + +	cmd.args[0] = 0x02; +	cmd.len = 1; +	ret = si2157_cmd_execute(s, &cmd); +	if (ret) +		goto err; + +	cmd.args[0] = 0x01; +	cmd.args[1] = 0x01; +	cmd.len = 2; +	ret = si2157_cmd_execute(s, &cmd); +	if (ret) +		goto err; + +	/* set frequency */ +	cmd.args[0] = 0x41; +	cmd.args[1] = 0x00; +	cmd.args[2] = 0x00; +	cmd.args[3] = 0x00; +	cmd.args[4] = (c->frequency >>  0) & 0xff; +	cmd.args[5] = (c->frequency >>  8) & 0xff; +	cmd.args[6] = (c->frequency >> 16) & 0xff; +	cmd.args[7] = (c->frequency >> 24) & 0xff; +	cmd.len = 8; +	ret = si2157_cmd_execute(s, &cmd); +	if (ret) +		goto err; + +	return 0; +err: +	dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); +	return ret; +} + +static const struct dvb_tuner_ops si2157_tuner_ops = { +	.info = { +		.name           = "Silicon Labs Si2157", +		.frequency_min  = 110000000, +		.frequency_max  = 862000000, +	}, + +	.init = si2157_init, +	.sleep = si2157_sleep, +	.set_params = si2157_set_params, +}; + +static int si2157_probe(struct i2c_client *client, +		const struct i2c_device_id *id) +{ +	struct si2157_config *cfg = client->dev.platform_data; +	struct dvb_frontend *fe = cfg->fe; +	struct si2157 *s; +	struct si2157_cmd cmd; +	int ret; + +	s = kzalloc(sizeof(struct si2157), GFP_KERNEL); +	if (!s) { +		ret = -ENOMEM; +		dev_err(&client->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); +		goto err; +	} + +	s->client = client; +	s->fe = cfg->fe; +	mutex_init(&s->i2c_mutex); + +	/* check if the tuner is there */ +	cmd.len = 0; +	ret = si2157_cmd_execute(s, &cmd); +	if (ret) +		goto err; + +	fe->tuner_priv = s; +	memcpy(&fe->ops.tuner_ops, &si2157_tuner_ops, +			sizeof(struct dvb_tuner_ops)); + +	i2c_set_clientdata(client, s); + +	dev_info(&s->client->dev, +			"%s: Silicon Labs Si2157 successfully attached\n", +			KBUILD_MODNAME); +	return 0; +err: +	dev_dbg(&client->dev, "%s: failed=%d\n", __func__, ret); +	kfree(s); + +	return ret; +} + +static int si2157_remove(struct i2c_client *client) +{ +	struct si2157 *s = i2c_get_clientdata(client); +	struct dvb_frontend *fe = s->fe; + +	dev_dbg(&client->dev, "%s:\n", __func__); + +	memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); +	fe->tuner_priv = NULL; +	kfree(s); + +	return 0; +} + +static const struct i2c_device_id si2157_id[] = { +	{"si2157", 0}, +	{} +}; +MODULE_DEVICE_TABLE(i2c, si2157_id); + +static struct i2c_driver si2157_driver = { +	.driver = { +		.owner	= THIS_MODULE, +		.name	= "si2157", +	}, +	.probe		= si2157_probe, +	.remove		= si2157_remove, +	.id_table	= si2157_id, +}; + +module_i2c_driver(si2157_driver); + +MODULE_DESCRIPTION("Silicon Labs Si2157 silicon tuner driver"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/si2157.h b/drivers/media/tuners/si2157.h new file mode 100644 index 00000000000..f469a092b66 --- /dev/null +++ b/drivers/media/tuners/si2157.h @@ -0,0 +1,34 @@ +/* + * Silicon Labs Si2157 silicon tuner driver + * + * Copyright (C) 2014 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. + */ + +#ifndef SI2157_H +#define SI2157_H + +#include <linux/kconfig.h> +#include "dvb_frontend.h" + +/* + * I2C address + * 0x60 + */ +struct si2157_config { +	/* +	 * frontend +	 */ +	struct dvb_frontend *fe; +}; + +#endif diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h new file mode 100644 index 00000000000..6cc6c6fdab7 --- /dev/null +++ b/drivers/media/tuners/si2157_priv.h @@ -0,0 +1,37 @@ +/* + * Silicon Labs Si2157 silicon tuner driver + * + * Copyright (C) 2014 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. + */ + +#ifndef SI2157_PRIV_H +#define SI2157_PRIV_H + +#include "si2157.h" + +/* state struct */ +struct si2157 { +	struct mutex i2c_mutex; +	struct i2c_client *client; +	struct dvb_frontend *fe; +	bool active; +}; + +/* firmare command struct */ +#define SI2157_ARGLEN      30 +struct si2157_cmd { +	u8 args[SI2157_ARGLEN]; +	unsigned len; +}; + +#endif diff --git a/drivers/media/tuners/tda18212.c b/drivers/media/tuners/tda18212.c index e4a84ee231c..05a4ac9edb6 100644 --- a/drivers/media/tuners/tda18212.c +++ b/drivers/media/tuners/tda18212.c @@ -20,6 +20,9 @@  #include "tda18212.h" +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE  64 +  struct tda18212_priv {  	struct tda18212_config *cfg;  	struct i2c_adapter *i2c; @@ -32,16 +35,23 @@ static int tda18212_wr_regs(struct tda18212_priv *priv, u8 reg, u8 *val,  	int len)  {  	int ret; -	u8 buf[len+1]; +	u8 buf[MAX_XFER_SIZE];  	struct i2c_msg msg[1] = {  		{  			.addr = priv->cfg->i2c_address,  			.flags = 0, -			.len = sizeof(buf), +			.len = 1 + len,  			.buf = buf,  		}  	}; +	if (1 + len > sizeof(buf)) { +		dev_warn(&priv->i2c->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); @@ -61,7 +71,7 @@ static int tda18212_rd_regs(struct tda18212_priv *priv, u8 reg, u8 *val,  	int len)  {  	int ret; -	u8 buf[len]; +	u8 buf[MAX_XFER_SIZE];  	struct i2c_msg msg[2] = {  		{  			.addr = priv->cfg->i2c_address, @@ -71,11 +81,18 @@ static int tda18212_rd_regs(struct tda18212_priv *priv, u8 reg, u8 *val,  		}, {  			.addr = priv->cfg->i2c_address,  			.flags = I2C_M_RD, -			.len = sizeof(buf), +			.len = len,  			.buf = buf,  		}  	}; +	if (len > sizeof(buf)) { +		dev_warn(&priv->i2c->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);  	if (ret == 2) {  		memcpy(val, buf, len); @@ -133,6 +150,8 @@ static int tda18212_set_params(struct dvb_frontend *fe)  	#define DVBT2_8  5  	#define DVBC_6   6  	#define DVBC_8   7 +	#define ATSC_VSB 8 +	#define ATSC_QAM 9  	static const u8 bw_params[][3] = {  		     /* reg:   0f    13    23 */  		[DVBT_6]  = { 0xb3, 0x20, 0x03 }, @@ -143,6 +162,8 @@ static int tda18212_set_params(struct dvb_frontend *fe)  		[DVBT2_8] = { 0xbc, 0x22, 0x01 },  		[DVBC_6]  = { 0x92, 0x50, 0x03 },  		[DVBC_8]  = { 0x92, 0x53, 0x03 }, +		[ATSC_VSB] = { 0x7d, 0x20, 0x63 }, +		[ATSC_QAM] = { 0x7d, 0x20, 0x63 },  	};  	dev_dbg(&priv->i2c->dev, @@ -154,6 +175,14 @@ static int tda18212_set_params(struct dvb_frontend *fe)  		fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */  	switch (c->delivery_system) { +	case SYS_ATSC: +		if_khz = priv->cfg->if_atsc_vsb; +		i = ATSC_VSB; +		break; +	case SYS_DVBC_ANNEX_B: +		if_khz = priv->cfg->if_atsc_qam; +		i = ATSC_QAM; +		break;  	case SYS_DVBT:  		switch (c->bandwidth_hz) {  		case 6000000: diff --git a/drivers/media/tuners/tda18212.h b/drivers/media/tuners/tda18212.h index 7e0d503baf0..c36b49e4b27 100644 --- a/drivers/media/tuners/tda18212.h +++ b/drivers/media/tuners/tda18212.h @@ -35,6 +35,8 @@ struct tda18212_config {  	u16 if_dvbt2_7;  	u16 if_dvbt2_8;  	u16 if_dvbc; +	u16 if_atsc_vsb; +	u16 if_atsc_qam;  };  #if IS_ENABLED(CONFIG_MEDIA_TUNER_TDA18212) diff --git a/drivers/media/tuners/tda18218.c b/drivers/media/tuners/tda18218.c index 2d31aeb6b08..9300e9361e3 100644 --- a/drivers/media/tuners/tda18218.c +++ b/drivers/media/tuners/tda18218.c @@ -20,11 +20,14 @@  #include "tda18218_priv.h" +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE  64 +  /* write multiple registers */  static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)  {  	int ret = 0, len2, remaining; -	u8 buf[1 + len]; +	u8 buf[MAX_XFER_SIZE];  	struct i2c_msg msg[1] = {  		{  			.addr = priv->cfg->i2c_address, @@ -33,6 +36,13 @@ static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)  		}  	}; +	if (1 + len > sizeof(buf)) { +		dev_warn(&priv->i2c->dev, +			 "%s: i2c wr reg=%04x: len=%d is too big!\n", +			 KBUILD_MODNAME, reg, len); +		return -EINVAL; +	} +  	for (remaining = len; remaining > 0;  			remaining -= (priv->cfg->i2c_wr_max - 1)) {  		len2 = remaining; @@ -63,7 +73,7 @@ static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)  static int tda18218_rd_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)  {  	int ret; -	u8 buf[reg+len]; /* we must start read always from reg 0x00 */ +	u8 buf[MAX_XFER_SIZE]; /* we must start read always from reg 0x00 */  	struct i2c_msg msg[2] = {  		{  			.addr = priv->cfg->i2c_address, @@ -73,11 +83,18 @@ static int tda18218_rd_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)  		}, {  			.addr = priv->cfg->i2c_address,  			.flags = I2C_M_RD, -			.len = sizeof(buf), +			.len = reg + len,  			.buf = buf,  		}  	}; +	if (reg + len > sizeof(buf)) { +		dev_warn(&priv->i2c->dev, +			 "%s: i2c wr reg=%04x: len=%d is too big!\n", +			 KBUILD_MODNAME, reg, len); +		return -EINVAL; +	} +  	ret = i2c_transfer(priv->i2c, msg, 2);  	if (ret == 2) {  		memcpy(val, &buf[reg], len); diff --git a/drivers/media/tuners/tda9887.c b/drivers/media/tuners/tda9887.c index 300005c535b..9823248d743 100644 --- a/drivers/media/tuners/tda9887.c +++ b/drivers/media/tuners/tda9887.c @@ -536,8 +536,8 @@ static int tda9887_status(struct dvb_frontend *fe)  	unsigned char buf[1];  	int rc; -	memset(buf,0,sizeof(buf)); -	if (1 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props,buf,1))) +	rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, 1); +	if (rc != 1)  		tuner_info("i2c i/o error: rc == %d (should be 1)\n", rc);  	dump_read_message(fe, buf);  	return 0; diff --git a/drivers/media/tuners/tuner-xc2028-types.h b/drivers/media/tuners/tuner-xc2028-types.h index 74dc46a71f6..7e4798783db 100644 --- a/drivers/media/tuners/tuner-xc2028-types.h +++ b/drivers/media/tuners/tuner-xc2028-types.h @@ -119,7 +119,7 @@  #define V4L2_STD_A2		(V4L2_STD_A2_A    | V4L2_STD_A2_B)  #define V4L2_STD_NICAM		(V4L2_STD_NICAM_A | V4L2_STD_NICAM_B) -/* To preserve backward compatibilty, +/* To preserve backward compatibility,     (std & V4L2_STD_AUDIO) = 0 means that ALL audio stds are supported   */ diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 878d2c4d9e8..6ef93ee1fdc 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -24,6 +24,9 @@  #include <linux/dvb/frontend.h>  #include "dvb_frontend.h" +/* Max transfer size done by I2C transfer functions */ +#define MAX_XFER_SIZE  80 +  /* Registers (Write-only) */  #define XREG_INIT         0x00  #define XREG_RF_FREQ      0x02 @@ -131,15 +134,6 @@ struct xc2028_data {  	_rc;								\  }) -#define i2c_rcv(priv, buf, size) ({					\ -	int _rc;							\ -	_rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, size);		\ -	if (size != _rc)						\ -		tuner_err("i2c input error: rc = %d (should be %d)\n",	\ -			   _rc, (int)size); 				\ -	_rc;								\ -}) -  #define i2c_send_recv(priv, obuf, osize, ibuf, isize) ({		\  	int _rc;							\  	_rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, obuf, osize,	\ @@ -273,6 +267,7 @@ static int check_device_status(struct xc2028_data *priv)  	case XC2028_WAITING_FIRMWARE:  		return -EAGAIN;  	case XC2028_ACTIVE: +		return 1;  	case XC2028_SLEEP:  		return 0;  	case XC2028_NODEV: @@ -547,7 +542,10 @@ static int load_firmware(struct dvb_frontend *fe, unsigned int type,  {  	struct xc2028_data *priv = fe->tuner_priv;  	int                pos, rc; -	unsigned char      *p, *endp, buf[priv->ctrl.max_len]; +	unsigned char      *p, *endp, buf[MAX_XFER_SIZE]; + +	if (priv->ctrl.max_len > sizeof(buf)) +		priv->ctrl.max_len = sizeof(buf);  	tuner_dbg("%s called\n", __func__); @@ -572,7 +570,7 @@ static int load_firmware(struct dvb_frontend *fe, unsigned int type,  			return -EINVAL;  		} -		size = le16_to_cpu(*(__u16 *) p); +		size = le16_to_cpu(*(__le16 *) p);  		p += sizeof(size);  		if (size == 0xffff) @@ -683,7 +681,7 @@ static int load_scode(struct dvb_frontend *fe, unsigned int type,  		/* 16 SCODE entries per file; each SCODE entry is 12 bytes and  		 * has a 2-byte size header in the firmware format. */  		if (priv->firm[pos].size != 14 * 16 || scode >= 16 || -		    le16_to_cpu(*(__u16 *)(p + 14 * scode)) != 12) +		    le16_to_cpu(*(__le16 *)(p + 14 * scode)) != 12)  			return -EINVAL;  		p += 14 * scode + 2;  	} @@ -712,6 +710,8 @@ static int load_scode(struct dvb_frontend *fe, unsigned int type,  	return 0;  } +static int xc2028_sleep(struct dvb_frontend *fe); +  static int check_firmware(struct dvb_frontend *fe, unsigned int type,  			  v4l2_std_id std, __u16 int_freq)  { @@ -884,7 +884,7 @@ read_not_reliable:  	return 0;  fail: -	priv->state = XC2028_SLEEP; +	priv->state = XC2028_NO_FIRMWARE;  	memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));  	if (retry_count < 8) { @@ -894,6 +894,9 @@ fail:  		goto retry;  	} +	/* Firmware didn't load. Put the device to sleep */ +	xc2028_sleep(fe); +  	if (rc == -ENOENT)  		rc = -EINVAL;  	return rc; @@ -911,6 +914,12 @@ static int xc2028_signal(struct dvb_frontend *fe, u16 *strength)  	if (rc < 0)  		return rc; +	/* If the device is sleeping, no channel is tuned */ +	if (!rc) { +		*strength = 0; +		return 0; +	} +  	mutex_lock(&priv->lock);  	/* Sync Lock Indicator */ @@ -958,6 +967,12 @@ static int xc2028_get_afc(struct dvb_frontend *fe, s32 *afc)  	if (rc < 0)  		return rc; +	/* If the device is sleeping, no channel is tuned */ +	if (!rc) { +		*afc = 0; +		return 0; +	} +  	mutex_lock(&priv->lock);  	/* Sync Lock Indicator */ @@ -1092,6 +1107,10 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,  				offset += 200000;  		}  #endif +		break; +	default: +		tuner_err("Unsupported tuner type %d.\n", new_type); +		break;  	}  	div = (freq - offset + DIV / 2) / DIV; @@ -1275,6 +1294,10 @@ static int xc2028_sleep(struct dvb_frontend *fe)  	if (rc < 0)  		return rc; +	/* Device is already in sleep mode */ +	if (!rc) +		return 0; +  	/* Avoid firmware reload on slow devices or if PM disabled */  	if (no_poweroff || priv->ctrl.disable_power_mgmt)  		return 0; @@ -1292,7 +1315,8 @@ static int xc2028_sleep(struct dvb_frontend *fe)  	else  		rc = send_seq(priv, {0x80, XREG_POWER_DOWN, 0x00, 0x00}); -	priv->state = XC2028_SLEEP; +	if (rc >= 0) +		priv->state = XC2028_SLEEP;  	mutex_unlock(&priv->lock); @@ -1360,7 +1384,7 @@ static void load_firmware_cb(const struct firmware *fw,  	if (rc < 0)  		return; -	priv->state = XC2028_SLEEP; +	priv->state = XC2028_ACTIVE;  }  static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg) diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index 5cd09a681b6..2b3d514be67 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -25,6 +25,7 @@  #include <linux/moduleparam.h>  #include <linux/videodev2.h>  #include <linux/delay.h> +#include <linux/workqueue.h>  #include <linux/dvb/frontend.h>  #include <linux/i2c.h> @@ -65,26 +66,25 @@ struct xc5000_priv {  	u16 pll_register_no;  	u8 init_status_supported;  	u8 fw_checksum_supported; + +	struct dvb_frontend *fe; +	struct delayed_work timer_sleep;  };  /* Misc Defines */  #define MAX_TV_STANDARD			24  #define XC_MAX_I2C_WRITE_LENGTH		64 +/* Time to suspend after the .sleep callback is called */ +#define XC5000_SLEEP_TIME		5000 /* ms */ +  /* Signal Types */  #define XC_RF_MODE_AIR			0  #define XC_RF_MODE_CABLE		1 -/* Result codes */ -#define XC_RESULT_SUCCESS		0 -#define XC_RESULT_RESET_FAILURE		1 -#define XC_RESULT_I2C_WRITE_FAILURE	2 -#define XC_RESULT_I2C_READ_FAILURE	3 -#define XC_RESULT_OUT_OF_RANGE		5 -  /* Product id */  #define XC_PRODUCT_ID_FW_NOT_LOADED	0x2000 -#define XC_PRODUCT_ID_FW_LOADED 	0x1388 +#define XC_PRODUCT_ID_FW_LOADED	0x1388  /* Registers */  #define XREG_INIT         0x00 @@ -152,16 +152,16 @@ struct xc5000_priv {  */  struct XC_TV_STANDARD { -	char *Name; -	u16 AudioMode; -	u16 VideoMode; +	char *name; +	u16 audio_mode; +	u16 video_mode;  };  /* Tuner standards */  #define MN_NTSC_PAL_BTSC	0  #define MN_NTSC_PAL_A2		1  #define MN_NTSC_PAL_EIAJ	2 -#define MN_NTSC_PAL_Mono	3 +#define MN_NTSC_PAL_MONO	3  #define BG_PAL_A2		4  #define BG_PAL_NICAM		5  #define BG_PAL_MONO		6 @@ -171,19 +171,19 @@ struct XC_TV_STANDARD {  #define DK_PAL_NICAM		10  #define DK_PAL_MONO		11  #define DK_SECAM_A2DK1		12 -#define DK_SECAM_A2LDK3 	13 -#define DK_SECAM_A2MONO 	14 +#define DK_SECAM_A2LDK3		13 +#define DK_SECAM_A2MONO		14  #define L_SECAM_NICAM		15  #define LC_SECAM_NICAM		16  #define DTV6			17  #define DTV8			18  #define DTV7_8			19  #define DTV7			20 -#define FM_Radio_INPUT2 	21 -#define FM_Radio_INPUT1 	22 -#define FM_Radio_INPUT1_MONO	23 +#define FM_RADIO_INPUT2		21 +#define FM_RADIO_INPUT1		22 +#define FM_RADIO_INPUT1_MONO	23 -static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = { +static struct XC_TV_STANDARD xc5000_standard[MAX_TV_STANDARD] = {  	{"M/N-NTSC/PAL-BTSC", 0x0400, 0x8020},  	{"M/N-NTSC/PAL-A2",   0x0600, 0x8020},  	{"M/N-NTSC/PAL-EIAJ", 0x0440, 0x8020}, @@ -249,7 +249,7 @@ static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id)  static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force);  static int xc5000_is_firmware_loaded(struct dvb_frontend *fe);  static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val); -static int xc5000_TunerReset(struct dvb_frontend *fe); +static int xc5000_tuner_reset(struct dvb_frontend *fe);  static int xc_send_i2c_data(struct xc5000_priv *priv, u8 *buf, int len)  { @@ -258,9 +258,9 @@ static int xc_send_i2c_data(struct xc5000_priv *priv, u8 *buf, int len)  	if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) {  		printk(KERN_ERR "xc5000: I2C write failed (len=%i)\n", len); -		return XC_RESULT_I2C_WRITE_FAILURE; +		return -EREMOTEIO;  	} -	return XC_RESULT_SUCCESS; +	return 0;  }  #if 0 @@ -297,15 +297,10 @@ static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val)  	}  	*val = (bval[0] << 8) | bval[1]; -	return XC_RESULT_SUCCESS; -} - -static void xc_wait(int wait_ms) -{ -	msleep(wait_ms); +	return 0;  } -static int xc5000_TunerReset(struct dvb_frontend *fe) +static int xc5000_tuner_reset(struct dvb_frontend *fe)  {  	struct xc5000_priv *priv = fe->tuner_priv;  	int ret; @@ -320,43 +315,43 @@ static int xc5000_TunerReset(struct dvb_frontend *fe)  					   XC5000_TUNER_RESET, 0);  		if (ret) {  			printk(KERN_ERR "xc5000: reset failed\n"); -			return XC_RESULT_RESET_FAILURE; +			return ret;  		}  	} else {  		printk(KERN_ERR "xc5000: no tuner reset callback function, fatal\n"); -		return XC_RESULT_RESET_FAILURE; +		return -EINVAL;  	} -	return XC_RESULT_SUCCESS; +	return 0;  } -static int xc_write_reg(struct xc5000_priv *priv, u16 regAddr, u16 i2cData) +static int xc_write_reg(struct xc5000_priv *priv, u16 reg_addr, u16 i2c_data)  {  	u8 buf[4]; -	int WatchDogTimer = 100; +	int watch_dog_timer = 100;  	int result; -	buf[0] = (regAddr >> 8) & 0xFF; -	buf[1] = regAddr & 0xFF; -	buf[2] = (i2cData >> 8) & 0xFF; -	buf[3] = i2cData & 0xFF; +	buf[0] = (reg_addr >> 8) & 0xFF; +	buf[1] = reg_addr & 0xFF; +	buf[2] = (i2c_data >> 8) & 0xFF; +	buf[3] = i2c_data & 0xFF;  	result = xc_send_i2c_data(priv, buf, 4); -	if (result == XC_RESULT_SUCCESS) { +	if (result == 0) {  		/* wait for busy flag to clear */ -		while ((WatchDogTimer > 0) && (result == XC_RESULT_SUCCESS)) { +		while ((watch_dog_timer > 0) && (result == 0)) {  			result = xc5000_readreg(priv, XREG_BUSY, (u16 *)buf); -			if (result == XC_RESULT_SUCCESS) { +			if (result == 0) {  				if ((buf[0] == 0) && (buf[1] == 0)) {  					/* busy flag cleared */  					break;  				} else { -					xc_wait(5); /* wait 5 ms */ -					WatchDogTimer--; +					msleep(5); /* wait 5 ms */ +					watch_dog_timer--;  				}  			}  		}  	} -	if (WatchDogTimer <= 0) -		result = XC_RESULT_I2C_WRITE_FAILURE; +	if (watch_dog_timer <= 0) +		result = -EREMOTEIO;  	return result;  } @@ -375,13 +370,13 @@ static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence)  		len = i2c_sequence[index] * 256 + i2c_sequence[index+1];  		if (len == 0x0000) {  			/* RESET command */ -			result = xc5000_TunerReset(fe); +			result = xc5000_tuner_reset(fe);  			index += 2; -			if (result != XC_RESULT_SUCCESS) +			if (result != 0)  				return result;  		} else if (len & 0x8000) {  			/* WAIT command */ -			xc_wait(len & 0x7FFF); +			msleep(len & 0x7FFF);  			index += 2;  		} else {  			/* Send i2c data whilst ensuring individual transactions @@ -404,7 +399,7 @@ static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence)  				result = xc_send_i2c_data(priv, buf,  					nbytes_to_send); -				if (result != XC_RESULT_SUCCESS) +				if (result != 0)  					return result;  				pos += nbytes_to_send - 2; @@ -412,7 +407,7 @@ static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence)  			index += len;  		}  	} -	return XC_RESULT_SUCCESS; +	return 0;  }  static int xc_initialize(struct xc5000_priv *priv) @@ -421,29 +416,29 @@ static int xc_initialize(struct xc5000_priv *priv)  	return xc_write_reg(priv, XREG_INIT, 0);  } -static int xc_SetTVStandard(struct xc5000_priv *priv, -	u16 VideoMode, u16 AudioMode, u8 RadioMode) +static int xc_set_tv_standard(struct xc5000_priv *priv, +	u16 video_mode, u16 audio_mode, u8 radio_mode)  {  	int ret; -	dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, VideoMode, AudioMode); -	if (RadioMode) { +	dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, video_mode, audio_mode); +	if (radio_mode) {  		dprintk(1, "%s() Standard = %s\n",  			__func__, -			XC5000_Standard[RadioMode].Name); +			xc5000_standard[radio_mode].name);  	} else {  		dprintk(1, "%s() Standard = %s\n",  			__func__, -			XC5000_Standard[priv->video_standard].Name); +			xc5000_standard[priv->video_standard].name);  	} -	ret = xc_write_reg(priv, XREG_VIDEO_MODE, VideoMode); -	if (ret == XC_RESULT_SUCCESS) -		ret = xc_write_reg(priv, XREG_AUDIO_MODE, AudioMode); +	ret = xc_write_reg(priv, XREG_VIDEO_MODE, video_mode); +	if (ret == 0) +		ret = xc_write_reg(priv, XREG_AUDIO_MODE, audio_mode);  	return ret;  } -static int xc_SetSignalSource(struct xc5000_priv *priv, u16 rf_mode) +static int xc_set_signal_source(struct xc5000_priv *priv, u16 rf_mode)  {  	dprintk(1, "%s(%d) Source = %s\n", __func__, rf_mode,  		rf_mode == XC_RF_MODE_AIR ? "ANTENNA" : "CABLE"); @@ -459,7 +454,7 @@ static int xc_SetSignalSource(struct xc5000_priv *priv, u16 rf_mode)  static const struct dvb_tuner_ops xc5000_tuner_ops; -static int xc_set_RF_frequency(struct xc5000_priv *priv, u32 freq_hz) +static int xc_set_rf_frequency(struct xc5000_priv *priv, u32 freq_hz)  {  	u16 freq_code; @@ -467,7 +462,7 @@ static int xc_set_RF_frequency(struct xc5000_priv *priv, u32 freq_hz)  	if ((freq_hz > xc5000_tuner_ops.info.frequency_max) ||  		(freq_hz < xc5000_tuner_ops.info.frequency_min)) -		return XC_RESULT_OUT_OF_RANGE; +		return -EINVAL;  	freq_code = (u16)(freq_hz / 15625); @@ -488,7 +483,7 @@ static int xc_set_IF_frequency(struct xc5000_priv *priv, u32 freq_khz)  } -static int xc_get_ADC_Envelope(struct xc5000_priv *priv, u16 *adc_envelope) +static int xc_get_adc_envelope(struct xc5000_priv *priv, u16 *adc_envelope)  {  	return xc5000_readreg(priv, XREG_ADC_ENV, adc_envelope);  } @@ -496,14 +491,14 @@ static int xc_get_ADC_Envelope(struct xc5000_priv *priv, u16 *adc_envelope)  static int xc_get_frequency_error(struct xc5000_priv *priv, u32 *freq_error_hz)  {  	int result; -	u16 regData; +	u16 reg_data;  	u32 tmp; -	result = xc5000_readreg(priv, XREG_FREQ_ERROR, ®Data); -	if (result != XC_RESULT_SUCCESS) +	result = xc5000_readreg(priv, XREG_FREQ_ERROR, ®_data); +	if (result != 0)  		return result; -	tmp = (u32)regData; +	tmp = (u32)reg_data;  	(*freq_error_hz) = (tmp * 15625) / 1000;  	return result;  } @@ -521,7 +516,7 @@ static int xc_get_version(struct xc5000_priv *priv,  	int result;  	result = xc5000_readreg(priv, XREG_VERSION, &data); -	if (result != XC_RESULT_SUCCESS) +	if (result != 0)  		return result;  	(*hw_majorversion) = (data >> 12) & 0x0F; @@ -539,14 +534,14 @@ static int xc_get_buildversion(struct xc5000_priv *priv, u16 *buildrev)  static int xc_get_hsync_freq(struct xc5000_priv *priv, u32 *hsync_freq_hz)  { -	u16 regData; +	u16 reg_data;  	int result; -	result = xc5000_readreg(priv, XREG_HSYNC_FREQ, ®Data); -	if (result != XC_RESULT_SUCCESS) +	result = xc5000_readreg(priv, XREG_HSYNC_FREQ, ®_data); +	if (result != 0)  		return result; -	(*hsync_freq_hz) = ((regData & 0x0fff) * 763)/100; +	(*hsync_freq_hz) = ((reg_data & 0x0fff) * 763)/100;  	return result;  } @@ -570,19 +565,19 @@ static int xc_get_totalgain(struct xc5000_priv *priv, u16 *totalgain)  	return xc5000_readreg(priv, XREG_TOTALGAIN, totalgain);  } -static u16 WaitForLock(struct xc5000_priv *priv) +static u16 wait_for_lock(struct xc5000_priv *priv)  { -	u16 lockState = 0; -	int watchDogCount = 40; - -	while ((lockState == 0) && (watchDogCount > 0)) { -		xc_get_lock_status(priv, &lockState); -		if (lockState != 1) { -			xc_wait(5); -			watchDogCount--; +	u16 lock_state = 0; +	int watch_dog_count = 40; + +	while ((lock_state == 0) && (watch_dog_count > 0)) { +		xc_get_lock_status(priv, &lock_state); +		if (lock_state != 1) { +			msleep(5); +			watch_dog_count--;  		}  	} -	return lockState; +	return lock_state;  }  #define XC_TUNE_ANALOG  0 @@ -593,11 +588,11 @@ static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz, int mode)  	dprintk(1, "%s(%u)\n", __func__, freq_hz); -	if (xc_set_RF_frequency(priv, freq_hz) != XC_RESULT_SUCCESS) +	if (xc_set_rf_frequency(priv, freq_hz) != 0)  		return 0;  	if (mode == XC_TUNE_ANALOG) { -		if (WaitForLock(priv) == 1) +		if (wait_for_lock(priv) == 1)  			found = 1;  	} @@ -607,7 +602,7 @@ static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz, int mode)  static int xc_set_xtal(struct dvb_frontend *fe)  {  	struct xc5000_priv *priv = fe->tuner_priv; -	int ret = XC_RESULT_SUCCESS; +	int ret = 0;  	switch (priv->chip_id) {  	default: @@ -649,23 +644,22 @@ static int xc5000_fwupload(struct dvb_frontend *fe)  		priv->i2c_props.adap->dev.parent);  	if (ret) {  		printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n"); -		ret = XC_RESULT_RESET_FAILURE;  		goto out;  	} else {  		printk(KERN_DEBUG "xc5000: firmware read %Zu bytes.\n",  		       fw->size); -		ret = XC_RESULT_SUCCESS; +		ret = 0;  	}  	if (fw->size != desired_fw->size) {  		printk(KERN_ERR "xc5000: firmware incorrect size\n"); -		ret = XC_RESULT_RESET_FAILURE; +		ret = -EINVAL;  	} else {  		printk(KERN_INFO "xc5000: firmware uploading...\n");  		ret = xc_load_i2c_sequence(fe,  fw->data); -		if (XC_RESULT_SUCCESS == ret) +		if (0 == ret)  			ret = xc_set_xtal(fe); -		if (XC_RESULT_SUCCESS == ret) +		if (0 == ret)  			printk(KERN_INFO "xc5000: firmware upload complete...\n");  		else  			printk(KERN_ERR "xc5000: firmware upload failed...\n"); @@ -695,9 +689,9 @@ static void xc_debug_dump(struct xc5000_priv *priv)  	 * Frame Lines needs two frame times after initial lock  	 * before it is valid.  	 */ -	xc_wait(100); +	msleep(100); -	xc_get_ADC_Envelope(priv,  &adc_envelope); +	xc_get_adc_envelope(priv,  &adc_envelope);  	dprintk(1, "*** ADC envelope (0-1023) = %d\n", adc_envelope);  	xc_get_frequency_error(priv, &freq_error_hz); @@ -744,7 +738,7 @@ static int xc5000_set_params(struct dvb_frontend *fe)  	u32 freq = fe->dtv_property_cache.frequency;  	u32 delsys  = fe->dtv_property_cache.delivery_system; -	if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) { +	if (xc_load_fw_and_init_tuner(fe, 0) != 0) {  		dprintk(1, "Unable to load firmware and init tuner\n");  		return -EINVAL;  	} @@ -820,24 +814,24 @@ static int xc5000_set_params(struct dvb_frontend *fe)  	dprintk(1, "%s() frequency=%d (compensated to %d)\n",  		__func__, freq, priv->freq_hz); -	ret = xc_SetSignalSource(priv, priv->rf_mode); -	if (ret != XC_RESULT_SUCCESS) { +	ret = xc_set_signal_source(priv, priv->rf_mode); +	if (ret != 0) {  		printk(KERN_ERR -			"xc5000: xc_SetSignalSource(%d) failed\n", +			"xc5000: xc_set_signal_source(%d) failed\n",  			priv->rf_mode);  		return -EREMOTEIO;  	} -	ret = xc_SetTVStandard(priv, -		XC5000_Standard[priv->video_standard].VideoMode, -		XC5000_Standard[priv->video_standard].AudioMode, 0); -	if (ret != XC_RESULT_SUCCESS) { -		printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n"); +	ret = xc_set_tv_standard(priv, +		xc5000_standard[priv->video_standard].video_mode, +		xc5000_standard[priv->video_standard].audio_mode, 0); +	if (ret != 0) { +		printk(KERN_ERR "xc5000: xc_set_tv_standard failed\n");  		return -EREMOTEIO;  	}  	ret = xc_set_IF_frequency(priv, priv->if_khz); -	if (ret != XC_RESULT_SUCCESS) { +	if (ret != 0) {  		printk(KERN_ERR "xc5000: xc_Set_IF_frequency(%d) failed\n",  		       priv->if_khz);  		return -EIO; @@ -862,15 +856,15 @@ static int xc5000_is_firmware_loaded(struct dvb_frontend *fe)  	u16 id;  	ret = xc5000_readreg(priv, XREG_PRODUCT_ID, &id); -	if (ret == XC_RESULT_SUCCESS) { +	if (ret == 0) {  		if (id == XC_PRODUCT_ID_FW_NOT_LOADED) -			ret = XC_RESULT_RESET_FAILURE; +			ret = -ENOENT;  		else -			ret = XC_RESULT_SUCCESS; +			ret = 0;  	}  	dprintk(1, "%s() returns %s id = 0x%x\n", __func__, -		ret == XC_RESULT_SUCCESS ? "True" : "False", id); +		ret == 0 ? "True" : "False", id);  	return ret;  } @@ -936,19 +930,19 @@ static int xc5000_set_tv_freq(struct dvb_frontend *fe,  	}  tune_channel: -	ret = xc_SetSignalSource(priv, priv->rf_mode); -	if (ret != XC_RESULT_SUCCESS) { +	ret = xc_set_signal_source(priv, priv->rf_mode); +	if (ret != 0) {  		printk(KERN_ERR -			"xc5000: xc_SetSignalSource(%d) failed\n", +			"xc5000: xc_set_signal_source(%d) failed\n",  			priv->rf_mode);  		return -EREMOTEIO;  	} -	ret = xc_SetTVStandard(priv, -		XC5000_Standard[priv->video_standard].VideoMode, -		XC5000_Standard[priv->video_standard].AudioMode, 0); -	if (ret != XC_RESULT_SUCCESS) { -		printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n"); +	ret = xc_set_tv_standard(priv, +		xc5000_standard[priv->video_standard].video_mode, +		xc5000_standard[priv->video_standard].audio_mode, 0); +	if (ret != 0) { +		printk(KERN_ERR "xc5000: xc_set_tv_standard failed\n");  		return -EREMOTEIO;  	} @@ -966,7 +960,7 @@ tune_channel:  			/* PLL is unlocked, force reload of the firmware */  			dprintk(1, "xc5000: PLL not locked (0x%x).  Reloading...\n",  				pll_lock_status); -			if (xc_load_fw_and_init_tuner(fe, 1) != XC_RESULT_SUCCESS) { +			if (xc_load_fw_and_init_tuner(fe, 1) != 0) {  				printk(KERN_ERR "xc5000: Unable to reload fw\n");  				return -EREMOTEIO;  			} @@ -993,11 +987,11 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe,  	}  	if (priv->radio_input == XC5000_RADIO_FM1) -		radio_input = FM_Radio_INPUT1; +		radio_input = FM_RADIO_INPUT1;  	else if  (priv->radio_input == XC5000_RADIO_FM2) -		radio_input = FM_Radio_INPUT2; +		radio_input = FM_RADIO_INPUT2;  	else if  (priv->radio_input == XC5000_RADIO_FM1_MONO) -		radio_input = FM_Radio_INPUT1_MONO; +		radio_input = FM_RADIO_INPUT1_MONO;  	else {  		dprintk(1, "%s() unknown radio input %d\n", __func__,  			priv->radio_input); @@ -1008,18 +1002,18 @@ static int xc5000_set_radio_freq(struct dvb_frontend *fe,  	priv->rf_mode = XC_RF_MODE_AIR; -	ret = xc_SetTVStandard(priv, XC5000_Standard[radio_input].VideoMode, -			       XC5000_Standard[radio_input].AudioMode, radio_input); +	ret = xc_set_tv_standard(priv, xc5000_standard[radio_input].video_mode, +			       xc5000_standard[radio_input].audio_mode, radio_input); -	if (ret != XC_RESULT_SUCCESS) { -		printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n"); +	if (ret != 0) { +		printk(KERN_ERR "xc5000: xc_set_tv_standard failed\n");  		return -EREMOTEIO;  	} -	ret = xc_SetSignalSource(priv, priv->rf_mode); -	if (ret != XC_RESULT_SUCCESS) { +	ret = xc_set_signal_source(priv, priv->rf_mode); +	if (ret != 0) {  		printk(KERN_ERR -			"xc5000: xc_SetSignalSource(%d) failed\n", +			"xc5000: xc_set_signal_source(%d) failed\n",  			priv->rf_mode);  		return -EREMOTEIO;  	} @@ -1044,7 +1038,7 @@ static int xc5000_set_analog_params(struct dvb_frontend *fe,  	if (priv->i2c_props.adap == NULL)  		return -EINVAL; -	if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) { +	if (xc_load_fw_and_init_tuner(fe, 0) != 0) {  		dprintk(1, "Unable to load firmware and init tuner\n");  		return -EINVAL;  	} @@ -1105,23 +1099,25 @@ static int xc5000_get_status(struct dvb_frontend *fe, u32 *status)  static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force)  {  	struct xc5000_priv *priv = fe->tuner_priv; -	int ret = XC_RESULT_SUCCESS; +	int ret = 0;  	u16 pll_lock_status;  	u16 fw_ck; -	if (force || xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { +	cancel_delayed_work(&priv->timer_sleep); + +	if (force || xc5000_is_firmware_loaded(fe) != 0) {  fw_retry:  		ret = xc5000_fwupload(fe); -		if (ret != XC_RESULT_SUCCESS) +		if (ret != 0)  			return ret;  		msleep(20);  		if (priv->fw_checksum_supported) {  			if (xc5000_readreg(priv, XREG_FW_CHECKSUM, &fw_ck) -			    != XC_RESULT_SUCCESS) { +			    != 0) {  				dprintk(1, "%s() FW checksum reading failed.\n",  					__func__);  				goto fw_retry; @@ -1137,7 +1133,7 @@ fw_retry:  		/* Start the tuner self-calibration process */  		ret |= xc_initialize(priv); -		if (ret != XC_RESULT_SUCCESS) +		if (ret != 0)  			goto fw_retry;  		/* Wait for calibration to complete. @@ -1145,10 +1141,10 @@ fw_retry:  		 * I2C transactions until calibration is complete.  This way we  		 * don't have to rely on clock stretching working.  		 */ -		xc_wait(100); +		msleep(100);  		if (priv->init_status_supported) { -			if (xc5000_readreg(priv, XREG_INIT_STATUS, &fw_ck) != XC_RESULT_SUCCESS) { +			if (xc5000_readreg(priv, XREG_INIT_STATUS, &fw_ck) != 0) {  				dprintk(1, "%s() FW failed reading init status.\n",  					__func__);  				goto fw_retry; @@ -1177,27 +1173,39 @@ fw_retry:  	return ret;  } -static int xc5000_sleep(struct dvb_frontend *fe) +static void xc5000_do_timer_sleep(struct work_struct *timer_sleep)  { +	struct xc5000_priv *priv =container_of(timer_sleep, struct xc5000_priv, +					       timer_sleep.work); +	struct dvb_frontend *fe = priv->fe;  	int ret;  	dprintk(1, "%s()\n", __func__); -	/* Avoid firmware reload on slow devices */ -	if (no_poweroff) -		return 0; -  	/* According to Xceive technical support, the "powerdown" register  	   was removed in newer versions of the firmware.  The "supported"  	   way to sleep the tuner is to pull the reset pin low for 10ms */ -	ret = xc5000_TunerReset(fe); -	if (ret != XC_RESULT_SUCCESS) { +	ret = xc5000_tuner_reset(fe); +	if (ret != 0)  		printk(KERN_ERR  			"xc5000: %s() unable to shutdown tuner\n",  			__func__); -		return -EREMOTEIO; -	} else -		return XC_RESULT_SUCCESS; +} + +static int xc5000_sleep(struct dvb_frontend *fe) +{ +	struct xc5000_priv *priv = fe->tuner_priv; + +	dprintk(1, "%s()\n", __func__); + +	/* Avoid firmware reload on slow devices */ +	if (no_poweroff) +		return 0; + +	schedule_delayed_work(&priv->timer_sleep, +			      msecs_to_jiffies(XC5000_SLEEP_TIME)); + +	return 0;  }  static int xc5000_init(struct dvb_frontend *fe) @@ -1205,7 +1213,7 @@ static int xc5000_init(struct dvb_frontend *fe)  	struct xc5000_priv *priv = fe->tuner_priv;  	dprintk(1, "%s()\n", __func__); -	if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) { +	if (xc_load_fw_and_init_tuner(fe, 0) != 0) {  		printk(KERN_ERR "xc5000: Unable to initialise tuner\n");  		return -EREMOTEIO;  	} @@ -1224,8 +1232,10 @@ static int xc5000_release(struct dvb_frontend *fe)  	mutex_lock(&xc5000_list_mutex); -	if (priv) +	if (priv) { +		cancel_delayed_work(&priv->timer_sleep);  		hybrid_tuner_release_state(priv); +	}  	mutex_unlock(&xc5000_list_mutex); @@ -1297,6 +1307,8 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,  		/* new tuner instance */  		priv->bandwidth = 6000000;  		fe->tuner_priv = priv; +		priv->fe = fe; +		INIT_DELAYED_WORK(&priv->timer_sleep, xc5000_do_timer_sleep);  		break;  	default:  		/* existing tuner instance */ @@ -1327,7 +1339,7 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,  	/* Check if firmware has been loaded. It is possible that another  	   instance of the driver has loaded the firmware.  	 */ -	if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != XC_RESULT_SUCCESS) +	if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != 0)  		goto fail;  	switch (id) {  | 
