linux/drivers/media/dvb-frontends/tda10021.c
Mauro Carvalho Chehab 3f83aa6b4b media: tda10021: avoid casts when using symbol_rate
The usage of castings and float point when checking for
the setup based at the symbol_rate cause those warnings
with smatch:

	drivers/media/dvb-frontends/tda10021.c:153 tda10021_set_symbolrate() warn: unsigned 'symbolrate' is never less than zero.
	drivers/media/dvb-frontends/tda10021.c:155 tda10021_set_symbolrate() warn: unsigned 'symbolrate' is never less than zero.
	drivers/media/dvb-frontends/tda10021.c:157 tda10021_set_symbolrate() warn: unsigned 'symbolrate' is never less than zero.
	drivers/media/dvb-frontends/tda10021.c:159 tda10021_set_symbolrate() warn: unsigned 'symbolrate' is never less than zero.

While the code should work with gcc, as it will evaluate the
values into a constant before compiling, other compilers
could do otherwise. So, get rid of float pointing math on it,
avoiding the need of doing typecasts.

While here, cleanup some coding style issues at the related
code.

Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-09-03 11:11:03 +02:00

527 lines
13 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
TDA10021 - Single Chip Cable Channel Receiver driver module
used on the Siemens DVB-C cards
Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
Copyright (C) 2004 Markus Schulz <msc@antzsystem.de>
Support for TDA10021
*/
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <media/dvb_frontend.h>
#include "tda1002x.h"
struct tda10021_state {
struct i2c_adapter* i2c;
/* configuration settings */
const struct tda1002x_config* config;
struct dvb_frontend frontend;
u8 pwm;
u8 reg0;
};
#if 0
#define dprintk(x...) printk(x)
#else
#define dprintk(x...)
#endif
static int verbose;
#define XIN 57840000UL
#define FIN (XIN >> 4)
static int tda10021_inittab_size = 0x40;
static u8 tda10021_inittab[0x40]=
{
0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a,
0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40,
0xb8, 0x3f, 0xa1, 0x00, 0xcd, 0x01, 0x00, 0xff,
0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00,
0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00,
0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58,
0x00, 0x00, 0x80, 0x00, 0x80, 0xff, 0x00, 0x00,
0x04, 0x2d, 0x2f, 0xff, 0x00, 0x00, 0x00, 0x00,
};
static int _tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data)
{
u8 buf[] = { reg, data };
struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
int ret;
ret = i2c_transfer (state->i2c, &msg, 1);
if (ret != 1)
printk("DVB: TDA10021(%d): %s, writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
state->frontend.dvb->num, __func__, reg, data, ret);
msleep(10);
return (ret != 1) ? -EREMOTEIO : 0;
}
static u8 tda10021_readreg (struct tda10021_state* state, u8 reg)
{
u8 b0 [] = { reg };
u8 b1 [] = { 0 };
struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
{ .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
int ret;
ret = i2c_transfer (state->i2c, msg, 2);
// Don't print an error message if the id is read.
if (ret != 2 && reg != 0x1a)
printk("DVB: TDA10021: %s: readreg error (ret == %i)\n",
__func__, ret);
return b1[0];
}
//get access to tuner
static int lock_tuner(struct tda10021_state* state)
{
u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] | 0x80 };
struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
if(i2c_transfer(state->i2c, &msg, 1) != 1)
{
printk("tda10021: lock tuner fails\n");
return -EREMOTEIO;
}
return 0;
}
//release access from tuner
static int unlock_tuner(struct tda10021_state* state)
{
u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] & 0x7f };
struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
if(i2c_transfer(state->i2c, &msg_post, 1) != 1)
{
printk("tda10021: unlock tuner fails\n");
return -EREMOTEIO;
}
return 0;
}
static int tda10021_setup_reg0(struct tda10021_state *state, u8 reg0,
enum fe_spectral_inversion inversion)
{
reg0 |= state->reg0 & 0x63;
if ((INVERSION_ON == inversion) ^ (state->config->invert == 0))
reg0 &= ~0x20;
else
reg0 |= 0x20;
_tda10021_writereg (state, 0x00, reg0 & 0xfe);
_tda10021_writereg (state, 0x00, reg0 | 0x01);
state->reg0 = reg0;
return 0;
}
static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate)
{
s32 BDR;
s32 BDRI;
s16 SFIL = 0;
u16 NDEC = 0;
u32 tmp, ratio;
if (symbolrate > XIN / 2)
symbolrate = XIN / 2;
else if (symbolrate < 500000)
symbolrate = 500000;
if (symbolrate < XIN / 16)
NDEC = 1;
if (symbolrate < XIN / 32)
NDEC = 2;
if (symbolrate < XIN / 64)
NDEC = 3;
if (symbolrate < XIN * 10 / 123)
SFIL = 1;
if (symbolrate < XIN * 10 / 160)
SFIL = 0;
if (symbolrate < XIN * 10 / 246)
SFIL = 1;
if (symbolrate < XIN * 10 / 320)
SFIL = 0;
if (symbolrate < XIN * 10 / 492)
SFIL = 1;
if (symbolrate < XIN * 10 / 640)
SFIL = 0;
if (symbolrate < XIN * 10 / 984)
SFIL = 1;
symbolrate <<= NDEC;
ratio = (symbolrate << 4) / FIN;
tmp = ((symbolrate << 4) % FIN) << 8;
ratio = (ratio << 8) + tmp / FIN;
tmp = (tmp % FIN) << 8;
ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, FIN);
BDR = ratio;
BDRI = (((XIN << 5) / symbolrate) + 1) / 2;
if (BDRI > 0xFF)
BDRI = 0xFF;
SFIL = (SFIL << 4) | tda10021_inittab[0x0E];
NDEC = (NDEC << 6) | tda10021_inittab[0x03];
_tda10021_writereg (state, 0x03, NDEC);
_tda10021_writereg (state, 0x0a, BDR&0xff);
_tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff);
_tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f);
_tda10021_writereg (state, 0x0d, BDRI);
_tda10021_writereg (state, 0x0e, SFIL);
return 0;
}
static int tda10021_init (struct dvb_frontend *fe)
{
struct tda10021_state* state = fe->demodulator_priv;
int i;
dprintk("DVB: TDA10021(%d): init chip\n", fe->adapter->num);
//_tda10021_writereg (fe, 0, 0);
for (i=0; i<tda10021_inittab_size; i++)
_tda10021_writereg (state, i, tda10021_inittab[i]);
_tda10021_writereg (state, 0x34, state->pwm);
//Comment by markus
//0x2A[3-0] == PDIV -> P multiplaying factor (P=PDIV+1)(default 0)
//0x2A[4] == BYPPLL -> Power down mode (default 1)
//0x2A[5] == LCK -> PLL Lock Flag
//0x2A[6] == POLAXIN -> Polarity of the input reference clock (default 0)
//Activate PLL
_tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef);
return 0;
}
struct qam_params {
u8 conf, agcref, lthr, mseth, aref;
};
static int tda10021_set_parameters(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
u32 delsys = c->delivery_system;
unsigned qam = c->modulation;
bool is_annex_c;
u32 reg0x3d;
struct tda10021_state* state = fe->demodulator_priv;
static const struct qam_params qam_params[] = {
/* Modulation Conf AGCref LTHR MSETH AREF */
[QPSK] = { 0x14, 0x78, 0x78, 0x8c, 0x96 },
[QAM_16] = { 0x00, 0x8c, 0x87, 0xa2, 0x91 },
[QAM_32] = { 0x04, 0x8c, 0x64, 0x74, 0x96 },
[QAM_64] = { 0x08, 0x6a, 0x46, 0x43, 0x6a },
[QAM_128] = { 0x0c, 0x78, 0x36, 0x34, 0x7e },
[QAM_256] = { 0x10, 0x5c, 0x26, 0x23, 0x6b },
};
switch (delsys) {
case SYS_DVBC_ANNEX_A:
is_annex_c = false;
break;
case SYS_DVBC_ANNEX_C:
is_annex_c = true;
break;
default:
return -EINVAL;
}
/*
* gcc optimizes the code below the same way as it would code:
* "if (qam > 5) return -EINVAL;"
* Yet, the code is clearer, as it shows what QAM standards are
* supported by the driver, and avoids the usage of magic numbers on
* it.
*/
switch (qam) {
case QPSK:
case QAM_16:
case QAM_32:
case QAM_64:
case QAM_128:
case QAM_256:
break;
default:
return -EINVAL;
}
if (c->inversion != INVERSION_ON && c->inversion != INVERSION_OFF)
return -EINVAL;
/*printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->symbol_rate);*/
if (fe->ops.tuner_ops.set_params) {
fe->ops.tuner_ops.set_params(fe);
if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
}
tda10021_set_symbolrate(state, c->symbol_rate);
_tda10021_writereg(state, 0x34, state->pwm);
_tda10021_writereg(state, 0x01, qam_params[qam].agcref);
_tda10021_writereg(state, 0x05, qam_params[qam].lthr);
_tda10021_writereg(state, 0x08, qam_params[qam].mseth);
_tda10021_writereg(state, 0x09, qam_params[qam].aref);
/*
* Bit 0 == 0 means roll-off = 0.15 (Annex A)
* == 1 means roll-off = 0.13 (Annex C)
*/
reg0x3d = tda10021_readreg (state, 0x3d);
if (is_annex_c)
_tda10021_writereg (state, 0x3d, 0x01 | reg0x3d);
else
_tda10021_writereg (state, 0x3d, 0xfe & reg0x3d);
tda10021_setup_reg0(state, qam_params[qam].conf, c->inversion);
return 0;
}
static int tda10021_read_status(struct dvb_frontend *fe,
enum fe_status *status)
{
struct tda10021_state* state = fe->demodulator_priv;
int sync;
*status = 0;
//0x11[0] == EQALGO -> Equalizer algorithms state
//0x11[1] == CARLOCK -> Carrier locked
//0x11[2] == FSYNC -> Frame synchronisation
//0x11[3] == FEL -> Front End locked
//0x11[6] == NODVB -> DVB Mode Information
sync = tda10021_readreg (state, 0x11);
if (sync & 2)
*status |= FE_HAS_SIGNAL|FE_HAS_CARRIER;
if (sync & 4)
*status |= FE_HAS_SYNC|FE_HAS_VITERBI;
if (sync & 8)
*status |= FE_HAS_LOCK;
return 0;
}
static int tda10021_read_ber(struct dvb_frontend* fe, u32* ber)
{
struct tda10021_state* state = fe->demodulator_priv;
u32 _ber = tda10021_readreg(state, 0x14) |
(tda10021_readreg(state, 0x15) << 8) |
((tda10021_readreg(state, 0x16) & 0x0f) << 16);
_tda10021_writereg(state, 0x10, (tda10021_readreg(state, 0x10) & ~0xc0)
| (tda10021_inittab[0x10] & 0xc0));
*ber = 10 * _ber;
return 0;
}
static int tda10021_read_signal_strength(struct dvb_frontend* fe, u16* strength)
{
struct tda10021_state* state = fe->demodulator_priv;
u8 config = tda10021_readreg(state, 0x02);
u8 gain = tda10021_readreg(state, 0x17);
if (config & 0x02)
/* the agc value is inverted */
gain = ~gain;
*strength = (gain << 8) | gain;
return 0;
}
static int tda10021_read_snr(struct dvb_frontend* fe, u16* snr)
{
struct tda10021_state* state = fe->demodulator_priv;
u8 quality = ~tda10021_readreg(state, 0x18);
*snr = (quality << 8) | quality;
return 0;
}
static int tda10021_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
{
struct tda10021_state* state = fe->demodulator_priv;
*ucblocks = tda10021_readreg (state, 0x13) & 0x7f;
if (*ucblocks == 0x7f)
*ucblocks = 0xffffffff;
/* reset uncorrected block counter */
_tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf);
_tda10021_writereg (state, 0x10, tda10021_inittab[0x10]);
return 0;
}
static int tda10021_get_frontend(struct dvb_frontend *fe,
struct dtv_frontend_properties *p)
{
struct tda10021_state* state = fe->demodulator_priv;
int sync;
s8 afc = 0;
sync = tda10021_readreg(state, 0x11);
afc = tda10021_readreg(state, 0x19);
if (verbose) {
/* AFC only valid when carrier has been recovered */
printk(sync & 2 ? "DVB: TDA10021(%d): AFC (%d) %dHz\n" :
"DVB: TDA10021(%d): [AFC (%d) %dHz]\n",
state->frontend.dvb->num, afc,
-((s32)p->symbol_rate * afc) >> 10);
}
p->inversion = ((state->reg0 & 0x20) == 0x20) ^ (state->config->invert != 0) ? INVERSION_ON : INVERSION_OFF;
p->modulation = ((state->reg0 >> 2) & 7) + QAM_16;
p->fec_inner = FEC_NONE;
p->frequency = ((p->frequency + 31250) / 62500) * 62500;
if (sync & 2)
p->frequency -= ((s32)p->symbol_rate * afc) >> 10;
return 0;
}
static int tda10021_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
{
struct tda10021_state* state = fe->demodulator_priv;
if (enable) {
lock_tuner(state);
} else {
unlock_tuner(state);
}
return 0;
}
static int tda10021_sleep(struct dvb_frontend* fe)
{
struct tda10021_state* state = fe->demodulator_priv;
_tda10021_writereg (state, 0x1b, 0x02); /* pdown ADC */
_tda10021_writereg (state, 0x00, 0x80); /* standby */
return 0;
}
static void tda10021_release(struct dvb_frontend* fe)
{
struct tda10021_state* state = fe->demodulator_priv;
kfree(state);
}
static const struct dvb_frontend_ops tda10021_ops;
struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config,
struct i2c_adapter* i2c,
u8 pwm)
{
struct tda10021_state* state = NULL;
u8 id;
/* allocate memory for the internal state */
state = kzalloc(sizeof(struct tda10021_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
state->config = config;
state->i2c = i2c;
state->pwm = pwm;
state->reg0 = tda10021_inittab[0];
/* check if the demod is there */
id = tda10021_readreg(state, 0x1a);
if ((id & 0xf0) != 0x70) goto error;
/* Don't claim TDA10023 */
if (id == 0x7d)
goto error;
printk("TDA10021: i2c-addr = 0x%02x, id = 0x%02x\n",
state->config->demod_address, id);
/* create dvb_frontend */
memcpy(&state->frontend.ops, &tda10021_ops, sizeof(struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
return &state->frontend;
error:
kfree(state);
return NULL;
}
static const struct dvb_frontend_ops tda10021_ops = {
.delsys = { SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_C },
.info = {
.name = "Philips TDA10021 DVB-C",
.frequency_min_hz = 47 * MHz,
.frequency_max_hz = 862 * MHz,
.frequency_stepsize_hz = 62500,
.symbol_rate_min = (XIN / 2) / 64, /* SACLK/64 == (XIN/2)/64 */
.symbol_rate_max = (XIN / 2) / 4, /* SACLK/4 */
#if 0
.frequency_tolerance = ???,
.symbol_rate_tolerance = ???, /* ppm */ /* == 8% (spec p. 5) */
#endif
.caps = 0x400 | //FE_CAN_QAM_4
FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
FE_CAN_QAM_128 | FE_CAN_QAM_256 |
FE_CAN_FEC_AUTO
},
.release = tda10021_release,
.init = tda10021_init,
.sleep = tda10021_sleep,
.i2c_gate_ctrl = tda10021_i2c_gate_ctrl,
.set_frontend = tda10021_set_parameters,
.get_frontend = tda10021_get_frontend,
.read_status = tda10021_read_status,
.read_ber = tda10021_read_ber,
.read_signal_strength = tda10021_read_signal_strength,
.read_snr = tda10021_read_snr,
.read_ucblocks = tda10021_read_ucblocks,
};
module_param(verbose, int, 0644);
MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");
MODULE_DESCRIPTION("Philips TDA10021 DVB-C demodulator driver");
MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Markus Schulz");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(tda10021_attach);