mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-06 13:55:08 +08:00
ed5c2f5fd1
The value returned by an i2c driver's remove function is mostly ignored. (Only an error message is printed if the value is non-zero that the error is ignored.) So change the prototype of the remove function to return no value. This way driver authors are not tempted to assume that passing an error to the upper layer is a good idea. All drivers are adapted accordingly. There is no intended change of behaviour, all callbacks were prepared to return 0 before. Reviewed-by: Peter Senna Tschudin <peter.senna@gmail.com> Reviewed-by: Jeremy Kerr <jk@codeconstruct.com.au> Reviewed-by: Benjamin Mugnier <benjamin.mugnier@foss.st.com> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com> Reviewed-by: Crt Mori <cmo@melexis.com> Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Acked-by: Marek Behún <kabel@kernel.org> # for leds-turris-omnia Acked-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Reviewed-by: Petr Machata <petrm@nvidia.com> # for mlxsw Reviewed-by: Maximilian Luz <luzmaximilian@gmail.com> # for surface3_power Acked-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> # for bmc150-accel-i2c + kxcjk-1013 Reviewed-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> # for media/* + staging/media/* Acked-by: Miguel Ojeda <ojeda@kernel.org> # for auxdisplay/ht16k33 + auxdisplay/lcd2s Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com> # for versaclock5 Reviewed-by: Ajay Gupta <ajayg@nvidia.com> # for ucsi_ccg Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> # for iio Acked-by: Peter Rosin <peda@axentia.se> # for i2c-mux-*, max9860 Acked-by: Adrien Grassein <adrien.grassein@gmail.com> # for lontium-lt8912b Reviewed-by: Jean Delvare <jdelvare@suse.de> # for hwmon, i2c-core and i2c/muxes Acked-by: Corey Minyard <cminyard@mvista.com> # for IPMI Reviewed-by: Vladimir Oltean <olteanv@gmail.com> Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> Acked-by: Sebastian Reichel <sebastian.reichel@collabora.com> # for drivers/power Acked-by: Krzysztof Hałasa <khalasa@piap.pl> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Signed-off-by: Wolfram Sang <wsa@kernel.org>
1255 lines
27 KiB
C
1255 lines
27 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver
|
|
*
|
|
* Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
|
|
*/
|
|
|
|
#include "tda10071_priv.h"
|
|
|
|
static const struct dvb_frontend_ops tda10071_ops;
|
|
|
|
/*
|
|
* XXX: regmap_update_bits() does not fit our needs as it does not support
|
|
* partially volatile registers. Also it performs register read even mask is as
|
|
* wide as register value.
|
|
*/
|
|
/* write single register with mask */
|
|
static int tda10071_wr_reg_mask(struct tda10071_dev *dev,
|
|
u8 reg, u8 val, u8 mask)
|
|
{
|
|
int ret;
|
|
u8 tmp;
|
|
|
|
/* no need for read if whole reg is written */
|
|
if (mask != 0xff) {
|
|
ret = regmap_bulk_read(dev->regmap, reg, &tmp, 1);
|
|
if (ret)
|
|
return ret;
|
|
|
|
val &= mask;
|
|
tmp &= ~mask;
|
|
val |= tmp;
|
|
}
|
|
|
|
return regmap_bulk_write(dev->regmap, reg, &val, 1);
|
|
}
|
|
|
|
/* execute firmware command */
|
|
static int tda10071_cmd_execute(struct tda10071_dev *dev,
|
|
struct tda10071_cmd *cmd)
|
|
{
|
|
struct i2c_client *client = dev->client;
|
|
int ret, i;
|
|
unsigned int uitmp;
|
|
|
|
if (!dev->warm) {
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
|
|
mutex_lock(&dev->cmd_execute_mutex);
|
|
|
|
/* write cmd and args for firmware */
|
|
ret = regmap_bulk_write(dev->regmap, 0x00, cmd->args, cmd->len);
|
|
if (ret)
|
|
goto error_mutex_unlock;
|
|
|
|
/* start cmd execution */
|
|
ret = regmap_write(dev->regmap, 0x1f, 1);
|
|
if (ret)
|
|
goto error_mutex_unlock;
|
|
|
|
/* wait cmd execution terminate */
|
|
for (i = 1000, uitmp = 1; i && uitmp; i--) {
|
|
ret = regmap_read(dev->regmap, 0x1f, &uitmp);
|
|
if (ret)
|
|
goto error_mutex_unlock;
|
|
|
|
usleep_range(200, 5000);
|
|
}
|
|
|
|
mutex_unlock(&dev->cmd_execute_mutex);
|
|
dev_dbg(&client->dev, "loop=%d\n", i);
|
|
|
|
if (i == 0) {
|
|
ret = -ETIMEDOUT;
|
|
goto error;
|
|
}
|
|
|
|
return ret;
|
|
error_mutex_unlock:
|
|
mutex_unlock(&dev->cmd_execute_mutex);
|
|
error:
|
|
dev_dbg(&client->dev, "failed=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int tda10071_set_tone(struct dvb_frontend *fe,
|
|
enum fe_sec_tone_mode fe_sec_tone_mode)
|
|
{
|
|
struct tda10071_dev *dev = fe->demodulator_priv;
|
|
struct i2c_client *client = dev->client;
|
|
struct tda10071_cmd cmd;
|
|
int ret;
|
|
u8 tone;
|
|
|
|
if (!dev->warm) {
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
|
|
dev_dbg(&client->dev, "tone_mode=%d\n", fe_sec_tone_mode);
|
|
|
|
switch (fe_sec_tone_mode) {
|
|
case SEC_TONE_ON:
|
|
tone = 1;
|
|
break;
|
|
case SEC_TONE_OFF:
|
|
tone = 0;
|
|
break;
|
|
default:
|
|
dev_dbg(&client->dev, "invalid fe_sec_tone_mode\n");
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
cmd.args[0] = CMD_LNB_PCB_CONFIG;
|
|
cmd.args[1] = 0;
|
|
cmd.args[2] = 0x00;
|
|
cmd.args[3] = 0x00;
|
|
cmd.args[4] = tone;
|
|
cmd.len = 5;
|
|
ret = tda10071_cmd_execute(dev, &cmd);
|
|
if (ret)
|
|
goto error;
|
|
|
|
return ret;
|
|
error:
|
|
dev_dbg(&client->dev, "failed=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int tda10071_set_voltage(struct dvb_frontend *fe,
|
|
enum fe_sec_voltage fe_sec_voltage)
|
|
{
|
|
struct tda10071_dev *dev = fe->demodulator_priv;
|
|
struct i2c_client *client = dev->client;
|
|
struct tda10071_cmd cmd;
|
|
int ret;
|
|
u8 voltage;
|
|
|
|
if (!dev->warm) {
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
|
|
dev_dbg(&client->dev, "voltage=%d\n", fe_sec_voltage);
|
|
|
|
switch (fe_sec_voltage) {
|
|
case SEC_VOLTAGE_13:
|
|
voltage = 0;
|
|
break;
|
|
case SEC_VOLTAGE_18:
|
|
voltage = 1;
|
|
break;
|
|
case SEC_VOLTAGE_OFF:
|
|
voltage = 0;
|
|
break;
|
|
default:
|
|
dev_dbg(&client->dev, "invalid fe_sec_voltage\n");
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
cmd.args[0] = CMD_LNB_SET_DC_LEVEL;
|
|
cmd.args[1] = 0;
|
|
cmd.args[2] = voltage;
|
|
cmd.len = 3;
|
|
ret = tda10071_cmd_execute(dev, &cmd);
|
|
if (ret)
|
|
goto error;
|
|
|
|
return ret;
|
|
error:
|
|
dev_dbg(&client->dev, "failed=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe,
|
|
struct dvb_diseqc_master_cmd *diseqc_cmd)
|
|
{
|
|
struct tda10071_dev *dev = fe->demodulator_priv;
|
|
struct i2c_client *client = dev->client;
|
|
struct tda10071_cmd cmd;
|
|
int ret, i;
|
|
unsigned int uitmp;
|
|
|
|
if (!dev->warm) {
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
|
|
dev_dbg(&client->dev, "msg_len=%d\n", diseqc_cmd->msg_len);
|
|
|
|
if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 6) {
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
/* wait LNB TX */
|
|
for (i = 500, uitmp = 0; i && !uitmp; i--) {
|
|
ret = regmap_read(dev->regmap, 0x47, &uitmp);
|
|
if (ret)
|
|
goto error;
|
|
uitmp = (uitmp >> 0) & 1;
|
|
usleep_range(10000, 20000);
|
|
}
|
|
|
|
dev_dbg(&client->dev, "loop=%d\n", i);
|
|
|
|
if (i == 0) {
|
|
ret = -ETIMEDOUT;
|
|
goto error;
|
|
}
|
|
|
|
ret = regmap_update_bits(dev->regmap, 0x47, 0x01, 0x00);
|
|
if (ret)
|
|
goto error;
|
|
|
|
cmd.args[0] = CMD_LNB_SEND_DISEQC;
|
|
cmd.args[1] = 0;
|
|
cmd.args[2] = 0;
|
|
cmd.args[3] = 0;
|
|
cmd.args[4] = 2;
|
|
cmd.args[5] = 0;
|
|
cmd.args[6] = diseqc_cmd->msg_len;
|
|
memcpy(&cmd.args[7], diseqc_cmd->msg, diseqc_cmd->msg_len);
|
|
cmd.len = 7 + diseqc_cmd->msg_len;
|
|
ret = tda10071_cmd_execute(dev, &cmd);
|
|
if (ret)
|
|
goto error;
|
|
|
|
return ret;
|
|
error:
|
|
dev_dbg(&client->dev, "failed=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe,
|
|
struct dvb_diseqc_slave_reply *reply)
|
|
{
|
|
struct tda10071_dev *dev = fe->demodulator_priv;
|
|
struct i2c_client *client = dev->client;
|
|
struct tda10071_cmd cmd;
|
|
int ret, i;
|
|
unsigned int uitmp;
|
|
|
|
if (!dev->warm) {
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
|
|
dev_dbg(&client->dev, "\n");
|
|
|
|
/* wait LNB RX */
|
|
for (i = 500, uitmp = 0; i && !uitmp; i--) {
|
|
ret = regmap_read(dev->regmap, 0x47, &uitmp);
|
|
if (ret)
|
|
goto error;
|
|
uitmp = (uitmp >> 1) & 1;
|
|
usleep_range(10000, 20000);
|
|
}
|
|
|
|
dev_dbg(&client->dev, "loop=%d\n", i);
|
|
|
|
if (i == 0) {
|
|
ret = -ETIMEDOUT;
|
|
goto error;
|
|
}
|
|
|
|
/* reply len */
|
|
ret = regmap_read(dev->regmap, 0x46, &uitmp);
|
|
if (ret)
|
|
goto error;
|
|
|
|
reply->msg_len = uitmp & 0x1f; /* [4:0] */
|
|
if (reply->msg_len > sizeof(reply->msg))
|
|
reply->msg_len = sizeof(reply->msg); /* truncate API max */
|
|
|
|
/* read reply */
|
|
cmd.args[0] = CMD_LNB_UPDATE_REPLY;
|
|
cmd.args[1] = 0;
|
|
cmd.len = 2;
|
|
ret = tda10071_cmd_execute(dev, &cmd);
|
|
if (ret)
|
|
goto error;
|
|
|
|
ret = regmap_bulk_read(dev->regmap, cmd.len, reply->msg,
|
|
reply->msg_len);
|
|
if (ret)
|
|
goto error;
|
|
|
|
return ret;
|
|
error:
|
|
dev_dbg(&client->dev, "failed=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int tda10071_diseqc_send_burst(struct dvb_frontend *fe,
|
|
enum fe_sec_mini_cmd fe_sec_mini_cmd)
|
|
{
|
|
struct tda10071_dev *dev = fe->demodulator_priv;
|
|
struct i2c_client *client = dev->client;
|
|
struct tda10071_cmd cmd;
|
|
int ret, i;
|
|
unsigned int uitmp;
|
|
u8 burst;
|
|
|
|
if (!dev->warm) {
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
|
|
dev_dbg(&client->dev, "fe_sec_mini_cmd=%d\n", fe_sec_mini_cmd);
|
|
|
|
switch (fe_sec_mini_cmd) {
|
|
case SEC_MINI_A:
|
|
burst = 0;
|
|
break;
|
|
case SEC_MINI_B:
|
|
burst = 1;
|
|
break;
|
|
default:
|
|
dev_dbg(&client->dev, "invalid fe_sec_mini_cmd\n");
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
/* wait LNB TX */
|
|
for (i = 500, uitmp = 0; i && !uitmp; i--) {
|
|
ret = regmap_read(dev->regmap, 0x47, &uitmp);
|
|
if (ret)
|
|
goto error;
|
|
uitmp = (uitmp >> 0) & 1;
|
|
usleep_range(10000, 20000);
|
|
}
|
|
|
|
dev_dbg(&client->dev, "loop=%d\n", i);
|
|
|
|
if (i == 0) {
|
|
ret = -ETIMEDOUT;
|
|
goto error;
|
|
}
|
|
|
|
ret = regmap_update_bits(dev->regmap, 0x47, 0x01, 0x00);
|
|
if (ret)
|
|
goto error;
|
|
|
|
cmd.args[0] = CMD_LNB_SEND_TONEBURST;
|
|
cmd.args[1] = 0;
|
|
cmd.args[2] = burst;
|
|
cmd.len = 3;
|
|
ret = tda10071_cmd_execute(dev, &cmd);
|
|
if (ret)
|
|
goto error;
|
|
|
|
return ret;
|
|
error:
|
|
dev_dbg(&client->dev, "failed=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int tda10071_read_status(struct dvb_frontend *fe, enum fe_status *status)
|
|
{
|
|
struct tda10071_dev *dev = fe->demodulator_priv;
|
|
struct i2c_client *client = dev->client;
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
struct tda10071_cmd cmd;
|
|
int ret;
|
|
unsigned int uitmp;
|
|
u8 buf[8];
|
|
|
|
*status = 0;
|
|
|
|
if (!dev->warm) {
|
|
ret = 0;
|
|
goto error;
|
|
}
|
|
|
|
ret = regmap_read(dev->regmap, 0x39, &uitmp);
|
|
if (ret)
|
|
goto error;
|
|
|
|
/* 0x39[0] tuner PLL */
|
|
if (uitmp & 0x02) /* demod PLL */
|
|
*status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
|
|
if (uitmp & 0x04) /* viterbi or LDPC*/
|
|
*status |= FE_HAS_VITERBI;
|
|
if (uitmp & 0x08) /* RS or BCH */
|
|
*status |= FE_HAS_SYNC | FE_HAS_LOCK;
|
|
|
|
dev->fe_status = *status;
|
|
|
|
/* signal strength */
|
|
if (dev->fe_status & FE_HAS_SIGNAL) {
|
|
cmd.args[0] = CMD_GET_AGCACC;
|
|
cmd.args[1] = 0;
|
|
cmd.len = 2;
|
|
ret = tda10071_cmd_execute(dev, &cmd);
|
|
if (ret)
|
|
goto error;
|
|
|
|
/* input power estimate dBm */
|
|
ret = regmap_read(dev->regmap, 0x50, &uitmp);
|
|
if (ret)
|
|
goto error;
|
|
|
|
c->strength.stat[0].scale = FE_SCALE_DECIBEL;
|
|
c->strength.stat[0].svalue = (int) (uitmp - 256) * 1000;
|
|
} else {
|
|
c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
|
}
|
|
|
|
/* CNR */
|
|
if (dev->fe_status & FE_HAS_VITERBI) {
|
|
/* Es/No */
|
|
ret = regmap_bulk_read(dev->regmap, 0x3a, buf, 2);
|
|
if (ret)
|
|
goto error;
|
|
|
|
c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
|
|
c->cnr.stat[0].svalue = (buf[0] << 8 | buf[1] << 0) * 100;
|
|
} else {
|
|
c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
|
}
|
|
|
|
/* UCB/PER/BER */
|
|
if (dev->fe_status & FE_HAS_LOCK) {
|
|
/* TODO: report total bits/packets */
|
|
u8 delivery_system, reg, len;
|
|
|
|
switch (dev->delivery_system) {
|
|
case SYS_DVBS:
|
|
reg = 0x4c;
|
|
len = 8;
|
|
delivery_system = 1;
|
|
break;
|
|
case SYS_DVBS2:
|
|
reg = 0x4d;
|
|
len = 4;
|
|
delivery_system = 0;
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
ret = regmap_read(dev->regmap, reg, &uitmp);
|
|
if (ret)
|
|
goto error;
|
|
|
|
if (dev->meas_count == uitmp) {
|
|
dev_dbg(&client->dev, "meas not ready=%02x\n", uitmp);
|
|
ret = 0;
|
|
goto error;
|
|
} else {
|
|
dev->meas_count = uitmp;
|
|
}
|
|
|
|
cmd.args[0] = CMD_BER_UPDATE_COUNTERS;
|
|
cmd.args[1] = 0;
|
|
cmd.args[2] = delivery_system;
|
|
cmd.len = 3;
|
|
ret = tda10071_cmd_execute(dev, &cmd);
|
|
if (ret)
|
|
goto error;
|
|
|
|
ret = regmap_bulk_read(dev->regmap, cmd.len, buf, len);
|
|
if (ret)
|
|
goto error;
|
|
|
|
if (dev->delivery_system == SYS_DVBS) {
|
|
u32 bit_error = buf[0] << 24 | buf[1] << 16 |
|
|
buf[2] << 8 | buf[3] << 0;
|
|
|
|
dev->dvbv3_ber = bit_error;
|
|
dev->post_bit_error += bit_error;
|
|
c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
|
|
c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
|
|
dev->block_error += buf[4] << 8 | buf[5] << 0;
|
|
c->block_error.stat[0].scale = FE_SCALE_COUNTER;
|
|
c->block_error.stat[0].uvalue = dev->block_error;
|
|
} else {
|
|
dev->dvbv3_ber = buf[0] << 8 | buf[1] << 0;
|
|
dev->post_bit_error += buf[0] << 8 | buf[1] << 0;
|
|
c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
|
|
c->post_bit_error.stat[0].uvalue = dev->post_bit_error;
|
|
c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
|
}
|
|
} else {
|
|
c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
|
c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
|
}
|
|
|
|
return ret;
|
|
error:
|
|
dev_dbg(&client->dev, "failed=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr)
|
|
{
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
|
|
if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL)
|
|
*snr = div_s64(c->cnr.stat[0].svalue, 100);
|
|
else
|
|
*snr = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
|
|
{
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
unsigned int uitmp;
|
|
|
|
if (c->strength.stat[0].scale == FE_SCALE_DECIBEL) {
|
|
uitmp = div_s64(c->strength.stat[0].svalue, 1000) + 256;
|
|
uitmp = clamp(uitmp, 181U, 236U); /* -75dBm - -20dBm */
|
|
/* scale value to 0x0000-0xffff */
|
|
*strength = (uitmp-181) * 0xffff / (236-181);
|
|
} else {
|
|
*strength = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber)
|
|
{
|
|
struct tda10071_dev *dev = fe->demodulator_priv;
|
|
|
|
*ber = dev->dvbv3_ber;
|
|
return 0;
|
|
}
|
|
|
|
static int tda10071_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
|
|
{
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
|
|
if (c->block_error.stat[0].scale == FE_SCALE_COUNTER)
|
|
*ucblocks = c->block_error.stat[0].uvalue;
|
|
else
|
|
*ucblocks = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int tda10071_set_frontend(struct dvb_frontend *fe)
|
|
{
|
|
struct tda10071_dev *dev = fe->demodulator_priv;
|
|
struct i2c_client *client = dev->client;
|
|
struct tda10071_cmd cmd;
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
int ret, i;
|
|
u8 mode, rolloff, pilot, inversion, div;
|
|
enum fe_modulation modulation;
|
|
|
|
dev_dbg(&client->dev,
|
|
"delivery_system=%d modulation=%d frequency=%u symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n",
|
|
c->delivery_system, c->modulation, c->frequency, c->symbol_rate,
|
|
c->inversion, c->pilot, c->rolloff);
|
|
|
|
dev->delivery_system = SYS_UNDEFINED;
|
|
|
|
if (!dev->warm) {
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
|
|
switch (c->inversion) {
|
|
case INVERSION_OFF:
|
|
inversion = 1;
|
|
break;
|
|
case INVERSION_ON:
|
|
inversion = 0;
|
|
break;
|
|
case INVERSION_AUTO:
|
|
/* 2 = auto; try first on then off
|
|
* 3 = auto; try first off then on */
|
|
inversion = 3;
|
|
break;
|
|
default:
|
|
dev_dbg(&client->dev, "invalid inversion\n");
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
switch (c->delivery_system) {
|
|
case SYS_DVBS:
|
|
modulation = QPSK;
|
|
rolloff = 0;
|
|
pilot = 2;
|
|
break;
|
|
case SYS_DVBS2:
|
|
modulation = c->modulation;
|
|
|
|
switch (c->rolloff) {
|
|
case ROLLOFF_20:
|
|
rolloff = 2;
|
|
break;
|
|
case ROLLOFF_25:
|
|
rolloff = 1;
|
|
break;
|
|
case ROLLOFF_35:
|
|
rolloff = 0;
|
|
break;
|
|
case ROLLOFF_AUTO:
|
|
default:
|
|
dev_dbg(&client->dev, "invalid rolloff\n");
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
switch (c->pilot) {
|
|
case PILOT_OFF:
|
|
pilot = 0;
|
|
break;
|
|
case PILOT_ON:
|
|
pilot = 1;
|
|
break;
|
|
case PILOT_AUTO:
|
|
pilot = 2;
|
|
break;
|
|
default:
|
|
dev_dbg(&client->dev, "invalid pilot\n");
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
break;
|
|
default:
|
|
dev_dbg(&client->dev, "invalid delivery_system\n");
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
for (i = 0, mode = 0xff; i < ARRAY_SIZE(TDA10071_MODCOD); i++) {
|
|
if (c->delivery_system == TDA10071_MODCOD[i].delivery_system &&
|
|
modulation == TDA10071_MODCOD[i].modulation &&
|
|
c->fec_inner == TDA10071_MODCOD[i].fec) {
|
|
mode = TDA10071_MODCOD[i].val;
|
|
dev_dbg(&client->dev, "mode found=%02x\n", mode);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mode == 0xff) {
|
|
dev_dbg(&client->dev, "invalid parameter combination\n");
|
|
ret = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
if (c->symbol_rate <= 5000000)
|
|
div = 14;
|
|
else
|
|
div = 4;
|
|
|
|
ret = regmap_write(dev->regmap, 0x81, div);
|
|
if (ret)
|
|
goto error;
|
|
|
|
ret = regmap_write(dev->regmap, 0xe3, div);
|
|
if (ret)
|
|
goto error;
|
|
|
|
cmd.args[0] = CMD_CHANGE_CHANNEL;
|
|
cmd.args[1] = 0;
|
|
cmd.args[2] = mode;
|
|
cmd.args[3] = (c->frequency >> 16) & 0xff;
|
|
cmd.args[4] = (c->frequency >> 8) & 0xff;
|
|
cmd.args[5] = (c->frequency >> 0) & 0xff;
|
|
cmd.args[6] = ((c->symbol_rate / 1000) >> 8) & 0xff;
|
|
cmd.args[7] = ((c->symbol_rate / 1000) >> 0) & 0xff;
|
|
cmd.args[8] = ((tda10071_ops.info.frequency_tolerance_hz / 1000) >> 8) & 0xff;
|
|
cmd.args[9] = ((tda10071_ops.info.frequency_tolerance_hz / 1000) >> 0) & 0xff;
|
|
cmd.args[10] = rolloff;
|
|
cmd.args[11] = inversion;
|
|
cmd.args[12] = pilot;
|
|
cmd.args[13] = 0x00;
|
|
cmd.args[14] = 0x00;
|
|
cmd.len = 15;
|
|
ret = tda10071_cmd_execute(dev, &cmd);
|
|
if (ret)
|
|
goto error;
|
|
|
|
dev->delivery_system = c->delivery_system;
|
|
|
|
return ret;
|
|
error:
|
|
dev_dbg(&client->dev, "failed=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int tda10071_get_frontend(struct dvb_frontend *fe,
|
|
struct dtv_frontend_properties *c)
|
|
{
|
|
struct tda10071_dev *dev = fe->demodulator_priv;
|
|
struct i2c_client *client = dev->client;
|
|
int ret, i;
|
|
u8 buf[5], tmp;
|
|
|
|
if (!dev->warm || !(dev->fe_status & FE_HAS_LOCK)) {
|
|
ret = 0;
|
|
goto error;
|
|
}
|
|
|
|
ret = regmap_bulk_read(dev->regmap, 0x30, buf, 5);
|
|
if (ret)
|
|
goto error;
|
|
|
|
tmp = buf[0] & 0x3f;
|
|
for (i = 0; i < ARRAY_SIZE(TDA10071_MODCOD); i++) {
|
|
if (tmp == TDA10071_MODCOD[i].val) {
|
|
c->modulation = TDA10071_MODCOD[i].modulation;
|
|
c->fec_inner = TDA10071_MODCOD[i].fec;
|
|
c->delivery_system = TDA10071_MODCOD[i].delivery_system;
|
|
}
|
|
}
|
|
|
|
switch ((buf[1] >> 0) & 0x01) {
|
|
case 0:
|
|
c->inversion = INVERSION_ON;
|
|
break;
|
|
case 1:
|
|
c->inversion = INVERSION_OFF;
|
|
break;
|
|
}
|
|
|
|
switch ((buf[1] >> 7) & 0x01) {
|
|
case 0:
|
|
c->pilot = PILOT_OFF;
|
|
break;
|
|
case 1:
|
|
c->pilot = PILOT_ON;
|
|
break;
|
|
}
|
|
|
|
c->frequency = (buf[2] << 16) | (buf[3] << 8) | (buf[4] << 0);
|
|
|
|
ret = regmap_bulk_read(dev->regmap, 0x52, buf, 3);
|
|
if (ret)
|
|
goto error;
|
|
|
|
c->symbol_rate = ((buf[0] << 16) | (buf[1] << 8) | (buf[2] << 0)) * 1000;
|
|
|
|
return ret;
|
|
error:
|
|
dev_dbg(&client->dev, "failed=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int tda10071_init(struct dvb_frontend *fe)
|
|
{
|
|
struct tda10071_dev *dev = fe->demodulator_priv;
|
|
struct i2c_client *client = dev->client;
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
struct tda10071_cmd cmd;
|
|
int ret, i, len, remaining, fw_size;
|
|
unsigned int uitmp;
|
|
const struct firmware *fw;
|
|
u8 *fw_file = TDA10071_FIRMWARE;
|
|
u8 tmp, buf[4];
|
|
struct tda10071_reg_val_mask tab[] = {
|
|
{ 0xcd, 0x00, 0x07 },
|
|
{ 0x80, 0x00, 0x02 },
|
|
{ 0xcd, 0x00, 0xc0 },
|
|
{ 0xce, 0x00, 0x1b },
|
|
{ 0x9d, 0x00, 0x01 },
|
|
{ 0x9d, 0x00, 0x02 },
|
|
{ 0x9e, 0x00, 0x01 },
|
|
{ 0x87, 0x00, 0x80 },
|
|
{ 0xce, 0x00, 0x08 },
|
|
{ 0xce, 0x00, 0x10 },
|
|
};
|
|
struct tda10071_reg_val_mask tab2[] = {
|
|
{ 0xf1, 0x70, 0xff },
|
|
{ 0x88, dev->pll_multiplier, 0x3f },
|
|
{ 0x89, 0x00, 0x10 },
|
|
{ 0x89, 0x10, 0x10 },
|
|
{ 0xc0, 0x01, 0x01 },
|
|
{ 0xc0, 0x00, 0x01 },
|
|
{ 0xe0, 0xff, 0xff },
|
|
{ 0xe0, 0x00, 0xff },
|
|
{ 0x96, 0x1e, 0x7e },
|
|
{ 0x8b, 0x08, 0x08 },
|
|
{ 0x8b, 0x00, 0x08 },
|
|
{ 0x8f, 0x1a, 0x7e },
|
|
{ 0x8c, 0x68, 0xff },
|
|
{ 0x8d, 0x08, 0xff },
|
|
{ 0x8e, 0x4c, 0xff },
|
|
{ 0x8f, 0x01, 0x01 },
|
|
{ 0x8b, 0x04, 0x04 },
|
|
{ 0x8b, 0x00, 0x04 },
|
|
{ 0x87, 0x05, 0x07 },
|
|
{ 0x80, 0x00, 0x20 },
|
|
{ 0xc8, 0x01, 0xff },
|
|
{ 0xb4, 0x47, 0xff },
|
|
{ 0xb5, 0x9c, 0xff },
|
|
{ 0xb6, 0x7d, 0xff },
|
|
{ 0xba, 0x00, 0x03 },
|
|
{ 0xb7, 0x47, 0xff },
|
|
{ 0xb8, 0x9c, 0xff },
|
|
{ 0xb9, 0x7d, 0xff },
|
|
{ 0xba, 0x00, 0x0c },
|
|
{ 0xc8, 0x00, 0xff },
|
|
{ 0xcd, 0x00, 0x04 },
|
|
{ 0xcd, 0x00, 0x20 },
|
|
{ 0xe8, 0x02, 0xff },
|
|
{ 0xcf, 0x20, 0xff },
|
|
{ 0x9b, 0xd7, 0xff },
|
|
{ 0x9a, 0x01, 0x03 },
|
|
{ 0xa8, 0x05, 0x0f },
|
|
{ 0xa8, 0x65, 0xf0 },
|
|
{ 0xa6, 0xa0, 0xf0 },
|
|
{ 0x9d, 0x50, 0xfc },
|
|
{ 0x9e, 0x20, 0xe0 },
|
|
{ 0xa3, 0x1c, 0x7c },
|
|
{ 0xd5, 0x03, 0x03 },
|
|
};
|
|
|
|
if (dev->warm) {
|
|
/* warm state - wake up device from sleep */
|
|
|
|
for (i = 0; i < ARRAY_SIZE(tab); i++) {
|
|
ret = tda10071_wr_reg_mask(dev, tab[i].reg,
|
|
tab[i].val, tab[i].mask);
|
|
if (ret)
|
|
goto error;
|
|
}
|
|
|
|
cmd.args[0] = CMD_SET_SLEEP_MODE;
|
|
cmd.args[1] = 0;
|
|
cmd.args[2] = 0;
|
|
cmd.len = 3;
|
|
ret = tda10071_cmd_execute(dev, &cmd);
|
|
if (ret)
|
|
goto error;
|
|
} else {
|
|
/* cold state - try to download firmware */
|
|
|
|
/* request the firmware, this will block and timeout */
|
|
ret = request_firmware(&fw, fw_file, &client->dev);
|
|
if (ret) {
|
|
dev_err(&client->dev,
|
|
"did not find the firmware file '%s' (status %d). You can use <kernel_dir>/scripts/get_dvb_firmware to get the firmware\n",
|
|
fw_file, ret);
|
|
goto error;
|
|
}
|
|
|
|
/* init */
|
|
for (i = 0; i < ARRAY_SIZE(tab2); i++) {
|
|
ret = tda10071_wr_reg_mask(dev, tab2[i].reg,
|
|
tab2[i].val, tab2[i].mask);
|
|
if (ret)
|
|
goto error_release_firmware;
|
|
}
|
|
|
|
/* download firmware */
|
|
ret = regmap_write(dev->regmap, 0xe0, 0x7f);
|
|
if (ret)
|
|
goto error_release_firmware;
|
|
|
|
ret = regmap_write(dev->regmap, 0xf7, 0x81);
|
|
if (ret)
|
|
goto error_release_firmware;
|
|
|
|
ret = regmap_write(dev->regmap, 0xf8, 0x00);
|
|
if (ret)
|
|
goto error_release_firmware;
|
|
|
|
ret = regmap_write(dev->regmap, 0xf9, 0x00);
|
|
if (ret)
|
|
goto error_release_firmware;
|
|
|
|
dev_info(&client->dev,
|
|
"found a '%s' in cold state, will try to load a firmware\n",
|
|
tda10071_ops.info.name);
|
|
dev_info(&client->dev, "downloading firmware from file '%s'\n",
|
|
fw_file);
|
|
|
|
/* do not download last byte */
|
|
fw_size = fw->size - 1;
|
|
|
|
for (remaining = fw_size; remaining > 0;
|
|
remaining -= (dev->i2c_wr_max - 1)) {
|
|
len = remaining;
|
|
if (len > (dev->i2c_wr_max - 1))
|
|
len = (dev->i2c_wr_max - 1);
|
|
|
|
ret = regmap_bulk_write(dev->regmap, 0xfa,
|
|
(u8 *) &fw->data[fw_size - remaining], len);
|
|
if (ret) {
|
|
dev_err(&client->dev,
|
|
"firmware download failed=%d\n", ret);
|
|
goto error_release_firmware;
|
|
}
|
|
}
|
|
release_firmware(fw);
|
|
|
|
ret = regmap_write(dev->regmap, 0xf7, 0x0c);
|
|
if (ret)
|
|
goto error;
|
|
|
|
ret = regmap_write(dev->regmap, 0xe0, 0x00);
|
|
if (ret)
|
|
goto error;
|
|
|
|
/* wait firmware start */
|
|
msleep(250);
|
|
|
|
/* firmware status */
|
|
ret = regmap_read(dev->regmap, 0x51, &uitmp);
|
|
if (ret)
|
|
goto error;
|
|
|
|
if (uitmp) {
|
|
dev_info(&client->dev, "firmware did not run\n");
|
|
ret = -EFAULT;
|
|
goto error;
|
|
} else {
|
|
dev->warm = true;
|
|
}
|
|
|
|
cmd.args[0] = CMD_GET_FW_VERSION;
|
|
cmd.len = 1;
|
|
ret = tda10071_cmd_execute(dev, &cmd);
|
|
if (ret)
|
|
goto error;
|
|
|
|
ret = regmap_bulk_read(dev->regmap, cmd.len, buf, 4);
|
|
if (ret)
|
|
goto error;
|
|
|
|
dev_info(&client->dev, "firmware version %d.%d.%d.%d\n",
|
|
buf[0], buf[1], buf[2], buf[3]);
|
|
dev_info(&client->dev, "found a '%s' in warm state\n",
|
|
tda10071_ops.info.name);
|
|
|
|
ret = regmap_bulk_read(dev->regmap, 0x81, buf, 2);
|
|
if (ret)
|
|
goto error;
|
|
|
|
cmd.args[0] = CMD_DEMOD_INIT;
|
|
cmd.args[1] = ((dev->clk / 1000) >> 8) & 0xff;
|
|
cmd.args[2] = ((dev->clk / 1000) >> 0) & 0xff;
|
|
cmd.args[3] = buf[0];
|
|
cmd.args[4] = buf[1];
|
|
cmd.args[5] = dev->pll_multiplier;
|
|
cmd.args[6] = dev->spec_inv;
|
|
cmd.args[7] = 0x00;
|
|
cmd.len = 8;
|
|
ret = tda10071_cmd_execute(dev, &cmd);
|
|
if (ret)
|
|
goto error;
|
|
|
|
if (dev->tuner_i2c_addr)
|
|
tmp = dev->tuner_i2c_addr;
|
|
else
|
|
tmp = 0x14;
|
|
|
|
cmd.args[0] = CMD_TUNER_INIT;
|
|
cmd.args[1] = 0x00;
|
|
cmd.args[2] = 0x00;
|
|
cmd.args[3] = 0x00;
|
|
cmd.args[4] = 0x00;
|
|
cmd.args[5] = tmp;
|
|
cmd.args[6] = 0x00;
|
|
cmd.args[7] = 0x03;
|
|
cmd.args[8] = 0x02;
|
|
cmd.args[9] = 0x02;
|
|
cmd.args[10] = 0x00;
|
|
cmd.args[11] = 0x00;
|
|
cmd.args[12] = 0x00;
|
|
cmd.args[13] = 0x00;
|
|
cmd.args[14] = 0x00;
|
|
cmd.len = 15;
|
|
ret = tda10071_cmd_execute(dev, &cmd);
|
|
if (ret)
|
|
goto error;
|
|
|
|
cmd.args[0] = CMD_MPEG_CONFIG;
|
|
cmd.args[1] = 0;
|
|
cmd.args[2] = dev->ts_mode;
|
|
cmd.args[3] = 0x00;
|
|
cmd.args[4] = 0x04;
|
|
cmd.args[5] = 0x00;
|
|
cmd.len = 6;
|
|
ret = tda10071_cmd_execute(dev, &cmd);
|
|
if (ret)
|
|
goto error;
|
|
|
|
ret = regmap_update_bits(dev->regmap, 0xf0, 0x01, 0x01);
|
|
if (ret)
|
|
goto error;
|
|
|
|
cmd.args[0] = CMD_LNB_CONFIG;
|
|
cmd.args[1] = 0;
|
|
cmd.args[2] = 150;
|
|
cmd.args[3] = 3;
|
|
cmd.args[4] = 22;
|
|
cmd.args[5] = 1;
|
|
cmd.args[6] = 1;
|
|
cmd.args[7] = 30;
|
|
cmd.args[8] = 30;
|
|
cmd.args[9] = 30;
|
|
cmd.args[10] = 30;
|
|
cmd.len = 11;
|
|
ret = tda10071_cmd_execute(dev, &cmd);
|
|
if (ret)
|
|
goto error;
|
|
|
|
cmd.args[0] = CMD_BER_CONTROL;
|
|
cmd.args[1] = 0;
|
|
cmd.args[2] = 14;
|
|
cmd.args[3] = 14;
|
|
cmd.len = 4;
|
|
ret = tda10071_cmd_execute(dev, &cmd);
|
|
if (ret)
|
|
goto error;
|
|
}
|
|
|
|
/* init stats here in order signal app which stats are supported */
|
|
c->strength.len = 1;
|
|
c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
|
c->cnr.len = 1;
|
|
c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
|
c->post_bit_error.len = 1;
|
|
c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
|
c->block_error.len = 1;
|
|
c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
|
|
|
return ret;
|
|
error_release_firmware:
|
|
release_firmware(fw);
|
|
error:
|
|
dev_dbg(&client->dev, "failed=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int tda10071_sleep(struct dvb_frontend *fe)
|
|
{
|
|
struct tda10071_dev *dev = fe->demodulator_priv;
|
|
struct i2c_client *client = dev->client;
|
|
struct tda10071_cmd cmd;
|
|
int ret, i;
|
|
struct tda10071_reg_val_mask tab[] = {
|
|
{ 0xcd, 0x07, 0x07 },
|
|
{ 0x80, 0x02, 0x02 },
|
|
{ 0xcd, 0xc0, 0xc0 },
|
|
{ 0xce, 0x1b, 0x1b },
|
|
{ 0x9d, 0x01, 0x01 },
|
|
{ 0x9d, 0x02, 0x02 },
|
|
{ 0x9e, 0x01, 0x01 },
|
|
{ 0x87, 0x80, 0x80 },
|
|
{ 0xce, 0x08, 0x08 },
|
|
{ 0xce, 0x10, 0x10 },
|
|
};
|
|
|
|
if (!dev->warm) {
|
|
ret = -EFAULT;
|
|
goto error;
|
|
}
|
|
|
|
cmd.args[0] = CMD_SET_SLEEP_MODE;
|
|
cmd.args[1] = 0;
|
|
cmd.args[2] = 1;
|
|
cmd.len = 3;
|
|
ret = tda10071_cmd_execute(dev, &cmd);
|
|
if (ret)
|
|
goto error;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(tab); i++) {
|
|
ret = tda10071_wr_reg_mask(dev, tab[i].reg, tab[i].val,
|
|
tab[i].mask);
|
|
if (ret)
|
|
goto error;
|
|
}
|
|
|
|
return ret;
|
|
error:
|
|
dev_dbg(&client->dev, "failed=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int tda10071_get_tune_settings(struct dvb_frontend *fe,
|
|
struct dvb_frontend_tune_settings *s)
|
|
{
|
|
s->min_delay_ms = 8000;
|
|
s->step_size = 0;
|
|
s->max_drift = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dvb_frontend_ops tda10071_ops = {
|
|
.delsys = { SYS_DVBS, SYS_DVBS2 },
|
|
.info = {
|
|
.name = "NXP TDA10071",
|
|
.frequency_min_hz = 950 * MHz,
|
|
.frequency_max_hz = 2150 * MHz,
|
|
.frequency_tolerance_hz = 5 * MHz,
|
|
.symbol_rate_min = 1000000,
|
|
.symbol_rate_max = 45000000,
|
|
.caps = FE_CAN_INVERSION_AUTO |
|
|
FE_CAN_FEC_1_2 |
|
|
FE_CAN_FEC_2_3 |
|
|
FE_CAN_FEC_3_4 |
|
|
FE_CAN_FEC_4_5 |
|
|
FE_CAN_FEC_5_6 |
|
|
FE_CAN_FEC_6_7 |
|
|
FE_CAN_FEC_7_8 |
|
|
FE_CAN_FEC_8_9 |
|
|
FE_CAN_FEC_AUTO |
|
|
FE_CAN_QPSK |
|
|
FE_CAN_RECOVER |
|
|
FE_CAN_2G_MODULATION
|
|
},
|
|
|
|
.get_tune_settings = tda10071_get_tune_settings,
|
|
|
|
.init = tda10071_init,
|
|
.sleep = tda10071_sleep,
|
|
|
|
.set_frontend = tda10071_set_frontend,
|
|
.get_frontend = tda10071_get_frontend,
|
|
|
|
.read_status = tda10071_read_status,
|
|
.read_snr = tda10071_read_snr,
|
|
.read_signal_strength = tda10071_read_signal_strength,
|
|
.read_ber = tda10071_read_ber,
|
|
.read_ucblocks = tda10071_read_ucblocks,
|
|
|
|
.diseqc_send_master_cmd = tda10071_diseqc_send_master_cmd,
|
|
.diseqc_recv_slave_reply = tda10071_diseqc_recv_slave_reply,
|
|
.diseqc_send_burst = tda10071_diseqc_send_burst,
|
|
|
|
.set_tone = tda10071_set_tone,
|
|
.set_voltage = tda10071_set_voltage,
|
|
};
|
|
|
|
static struct dvb_frontend *tda10071_get_dvb_frontend(struct i2c_client *client)
|
|
{
|
|
struct tda10071_dev *dev = i2c_get_clientdata(client);
|
|
|
|
dev_dbg(&client->dev, "\n");
|
|
|
|
return &dev->fe;
|
|
}
|
|
|
|
static int tda10071_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
struct tda10071_dev *dev;
|
|
struct tda10071_platform_data *pdata = client->dev.platform_data;
|
|
int ret;
|
|
unsigned int uitmp;
|
|
static const struct regmap_config regmap_config = {
|
|
.reg_bits = 8,
|
|
.val_bits = 8,
|
|
};
|
|
|
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
if (!dev) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
dev->client = client;
|
|
mutex_init(&dev->cmd_execute_mutex);
|
|
dev->clk = pdata->clk;
|
|
dev->i2c_wr_max = pdata->i2c_wr_max;
|
|
dev->ts_mode = pdata->ts_mode;
|
|
dev->spec_inv = pdata->spec_inv;
|
|
dev->pll_multiplier = pdata->pll_multiplier;
|
|
dev->tuner_i2c_addr = pdata->tuner_i2c_addr;
|
|
dev->regmap = devm_regmap_init_i2c(client, ®map_config);
|
|
if (IS_ERR(dev->regmap)) {
|
|
ret = PTR_ERR(dev->regmap);
|
|
goto err_kfree;
|
|
}
|
|
|
|
/* chip ID */
|
|
ret = regmap_read(dev->regmap, 0xff, &uitmp);
|
|
if (ret)
|
|
goto err_kfree;
|
|
if (uitmp != 0x0f) {
|
|
ret = -ENODEV;
|
|
goto err_kfree;
|
|
}
|
|
|
|
/* chip type */
|
|
ret = regmap_read(dev->regmap, 0xdd, &uitmp);
|
|
if (ret)
|
|
goto err_kfree;
|
|
if (uitmp != 0x00) {
|
|
ret = -ENODEV;
|
|
goto err_kfree;
|
|
}
|
|
|
|
/* chip version */
|
|
ret = regmap_read(dev->regmap, 0xfe, &uitmp);
|
|
if (ret)
|
|
goto err_kfree;
|
|
if (uitmp != 0x01) {
|
|
ret = -ENODEV;
|
|
goto err_kfree;
|
|
}
|
|
|
|
/* create dvb_frontend */
|
|
memcpy(&dev->fe.ops, &tda10071_ops, sizeof(struct dvb_frontend_ops));
|
|
dev->fe.demodulator_priv = dev;
|
|
i2c_set_clientdata(client, dev);
|
|
|
|
/* setup callbacks */
|
|
pdata->get_dvb_frontend = tda10071_get_dvb_frontend;
|
|
|
|
dev_info(&client->dev, "NXP TDA10071 successfully identified\n");
|
|
return 0;
|
|
err_kfree:
|
|
kfree(dev);
|
|
err:
|
|
dev_dbg(&client->dev, "failed=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static void tda10071_remove(struct i2c_client *client)
|
|
{
|
|
struct tda10071_dev *dev = i2c_get_clientdata(client);
|
|
|
|
dev_dbg(&client->dev, "\n");
|
|
|
|
kfree(dev);
|
|
}
|
|
|
|
static const struct i2c_device_id tda10071_id_table[] = {
|
|
{"tda10071_cx24118", 0},
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, tda10071_id_table);
|
|
|
|
static struct i2c_driver tda10071_driver = {
|
|
.driver = {
|
|
.name = "tda10071",
|
|
.suppress_bind_attrs = true,
|
|
},
|
|
.probe = tda10071_probe,
|
|
.remove = tda10071_remove,
|
|
.id_table = tda10071_id_table,
|
|
};
|
|
|
|
module_i2c_driver(tda10071_driver);
|
|
|
|
MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
|
|
MODULE_DESCRIPTION("NXP TDA10071 DVB-S/S2 demodulator driver");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_FIRMWARE(TDA10071_FIRMWARE);
|