V4L/DVB (7347): tuner-simple: add basic support for digital tuning of hybrid devices
Add entry points used for digital tuning via the dvb_frontend. Share state data between multiple instances of the driver for hybrid tuners. Signed-off-by: Michael Krufky <mkrufky@linuxtv.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
This commit is contained in:
parent
ac8b63b30a
commit
62325497db
|
@ -88,14 +88,20 @@ MODULE_PARM_DESC(offset, "Allows to specify an offset for tuner");
|
|||
#define TUNER_PLL_LOCKED 0x40
|
||||
#define TUNER_STEREO_MK3 0x04
|
||||
|
||||
static DEFINE_MUTEX(tuner_simple_list_mutex);
|
||||
static LIST_HEAD(hybrid_tuner_instance_list);
|
||||
|
||||
struct tuner_simple_priv {
|
||||
u16 last_div;
|
||||
|
||||
struct tuner_i2c_props i2c_props;
|
||||
struct list_head hybrid_tuner_instance_list;
|
||||
|
||||
unsigned int type;
|
||||
struct tunertype *tun;
|
||||
|
||||
u32 frequency;
|
||||
u32 bandwidth;
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
@ -188,6 +194,9 @@ static inline char *tuner_param_name(enum param_type type)
|
|||
case TUNER_PARAM_TYPE_NTSC:
|
||||
name = "ntsc";
|
||||
break;
|
||||
case TUNER_PARAM_TYPE_DIGITAL:
|
||||
name = "digital";
|
||||
break;
|
||||
default:
|
||||
name = "unknown";
|
||||
break;
|
||||
|
@ -687,14 +696,118 @@ static int simple_set_params(struct dvb_frontend *fe,
|
|||
priv->frequency = params->frequency * 62500;
|
||||
break;
|
||||
}
|
||||
priv->bandwidth = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int simple_dvb_configure(struct dvb_frontend *fe, u8 *buf,
|
||||
const struct dvb_frontend_parameters *params)
|
||||
{
|
||||
struct tuner_simple_priv *priv = fe->tuner_priv;
|
||||
struct tunertype *tun = priv->tun;
|
||||
static struct tuner_params *t_params;
|
||||
u8 config, cb;
|
||||
u32 div;
|
||||
int ret, frequency = params->frequency / 62500;
|
||||
|
||||
t_params = simple_tuner_params(fe, TUNER_PARAM_TYPE_DIGITAL);
|
||||
ret = simple_config_lookup(fe, t_params, &frequency, &config, &cb);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
div = ((frequency + t_params->iffreq) * 62500 + offset +
|
||||
tun->stepsize/2) / tun->stepsize;
|
||||
|
||||
buf[0] = div >> 8;
|
||||
buf[1] = div & 0xff;
|
||||
buf[2] = config;
|
||||
buf[3] = cb;
|
||||
|
||||
tuner_dbg("%s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n",
|
||||
tun->name, div, buf[0], buf[1], buf[2], buf[3]);
|
||||
|
||||
/* calculate the frequency we set it to */
|
||||
return (div * tun->stepsize) - t_params->iffreq;
|
||||
}
|
||||
|
||||
static int simple_dvb_calc_regs(struct dvb_frontend *fe,
|
||||
struct dvb_frontend_parameters *params,
|
||||
u8 *buf, int buf_len)
|
||||
{
|
||||
struct tuner_simple_priv *priv = fe->tuner_priv;
|
||||
int ret;
|
||||
u32 frequency;
|
||||
|
||||
if (buf_len < 5)
|
||||
return -EINVAL;
|
||||
|
||||
ret = simple_dvb_configure(fe, buf+1, params);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else
|
||||
frequency = ret;
|
||||
|
||||
buf[0] = priv->i2c_props.addr;
|
||||
|
||||
priv->frequency = frequency;
|
||||
priv->bandwidth = (fe->ops.info.type == FE_OFDM) ?
|
||||
params->u.ofdm.bandwidth : 0;
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
static int simple_dvb_set_params(struct dvb_frontend *fe,
|
||||
struct dvb_frontend_parameters *params)
|
||||
{
|
||||
struct tuner_simple_priv *priv = fe->tuner_priv;
|
||||
u32 prev_freq, prev_bw;
|
||||
int ret;
|
||||
u8 buf[5];
|
||||
|
||||
if (priv->i2c_props.adap == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
prev_freq = priv->frequency;
|
||||
prev_bw = priv->bandwidth;
|
||||
|
||||
ret = simple_dvb_calc_regs(fe, params, buf, 5);
|
||||
if (ret != 5)
|
||||
goto fail;
|
||||
|
||||
/* put analog demod in standby when tuning digital */
|
||||
if (fe->ops.analog_ops.standby)
|
||||
fe->ops.analog_ops.standby(fe);
|
||||
|
||||
if (fe->ops.i2c_gate_ctrl)
|
||||
fe->ops.i2c_gate_ctrl(fe, 1);
|
||||
|
||||
/* buf[0] contains the i2c address, but *
|
||||
* we already have it in i2c_props.addr */
|
||||
ret = tuner_i2c_xfer_send(&priv->i2c_props, buf+1, 4);
|
||||
if (ret != 4)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
/* calc_regs sets frequency and bandwidth. if we failed, unset them */
|
||||
priv->frequency = prev_freq;
|
||||
priv->bandwidth = prev_bw;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int simple_release(struct dvb_frontend *fe)
|
||||
{
|
||||
kfree(fe->tuner_priv);
|
||||
struct tuner_simple_priv *priv = fe->tuner_priv;
|
||||
|
||||
mutex_lock(&tuner_simple_list_mutex);
|
||||
|
||||
if (priv)
|
||||
hybrid_tuner_release_state(priv);
|
||||
|
||||
mutex_unlock(&tuner_simple_list_mutex);
|
||||
|
||||
fe->tuner_priv = NULL;
|
||||
|
||||
return 0;
|
||||
|
@ -707,10 +820,20 @@ static int simple_get_frequency(struct dvb_frontend *fe, u32 *frequency)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int simple_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
|
||||
{
|
||||
struct tuner_simple_priv *priv = fe->tuner_priv;
|
||||
*bandwidth = priv->bandwidth;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dvb_tuner_ops simple_tuner_ops = {
|
||||
.set_analog_params = simple_set_params,
|
||||
.set_params = simple_dvb_set_params,
|
||||
.calc_regs = simple_dvb_calc_regs,
|
||||
.release = simple_release,
|
||||
.get_frequency = simple_get_frequency,
|
||||
.get_bandwidth = simple_get_bandwidth,
|
||||
.get_status = simple_get_status,
|
||||
.get_rf_strength = simple_get_rf_strength,
|
||||
};
|
||||
|
@ -721,6 +844,7 @@ struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe,
|
|||
unsigned int type)
|
||||
{
|
||||
struct tuner_simple_priv *priv = NULL;
|
||||
int instance;
|
||||
|
||||
if (type >= tuner_count) {
|
||||
printk(KERN_WARNING "%s: invalid tuner type: %d (max: %d)\n",
|
||||
|
@ -728,22 +852,34 @@ struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
priv = kzalloc(sizeof(struct tuner_simple_priv), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
mutex_lock(&tuner_simple_list_mutex);
|
||||
|
||||
instance = hybrid_tuner_request_state(struct tuner_simple_priv, priv,
|
||||
hybrid_tuner_instance_list,
|
||||
i2c_adap, i2c_addr,
|
||||
"tuner-simple");
|
||||
switch (instance) {
|
||||
case 0:
|
||||
mutex_unlock(&tuner_simple_list_mutex);
|
||||
return NULL;
|
||||
fe->tuner_priv = priv;
|
||||
break;
|
||||
case 1:
|
||||
fe->tuner_priv = priv;
|
||||
|
||||
priv->i2c_props.addr = i2c_addr;
|
||||
priv->i2c_props.adap = i2c_adap;
|
||||
priv->i2c_props.name = "tuner-simple";
|
||||
priv->type = type;
|
||||
priv->tun = &tuners[type];
|
||||
break;
|
||||
default:
|
||||
fe->tuner_priv = priv;
|
||||
break;
|
||||
}
|
||||
|
||||
priv->type = type;
|
||||
priv->tun = &tuners[type];
|
||||
mutex_unlock(&tuner_simple_list_mutex);
|
||||
|
||||
memcpy(&fe->ops.tuner_ops, &simple_tuner_ops,
|
||||
sizeof(struct dvb_tuner_ops));
|
||||
|
||||
tuner_info("type set to %d (%s)\n", priv->type, priv->tun->name);
|
||||
tuner_info("type set to %d (%s)\n", type, priv->tun->name);
|
||||
|
||||
strlcpy(fe->ops.tuner_ops.info.name, priv->tun->name,
|
||||
sizeof(fe->ops.tuner_ops.info.name));
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
#define __TUNER_TYPES_H__
|
||||
|
||||
enum param_type {
|
||||
TUNER_PARAM_TYPE_RADIO, \
|
||||
TUNER_PARAM_TYPE_PAL, \
|
||||
TUNER_PARAM_TYPE_SECAM, \
|
||||
TUNER_PARAM_TYPE_NTSC
|
||||
TUNER_PARAM_TYPE_RADIO,
|
||||
TUNER_PARAM_TYPE_PAL,
|
||||
TUNER_PARAM_TYPE_SECAM,
|
||||
TUNER_PARAM_TYPE_NTSC,
|
||||
TUNER_PARAM_TYPE_DIGITAL,
|
||||
};
|
||||
|
||||
struct tuner_range {
|
||||
|
@ -105,6 +106,7 @@ struct tuner_params {
|
|||
the SECAM-L/L' standards. Range: -16:+15 */
|
||||
signed int default_top_secam_high:5;
|
||||
|
||||
u16 iffreq;
|
||||
|
||||
unsigned int count;
|
||||
struct tuner_range *ranges;
|
||||
|
@ -114,6 +116,10 @@ struct tunertype {
|
|||
char *name;
|
||||
unsigned int count;
|
||||
struct tuner_params *params;
|
||||
|
||||
u16 min;
|
||||
u16 max;
|
||||
u16 stepsize;
|
||||
};
|
||||
|
||||
extern struct tunertype tuners[];
|
||||
|
|
Loading…
Reference in New Issue