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_PLL_LOCKED 0x40
|
||||||
#define TUNER_STEREO_MK3 0x04
|
#define TUNER_STEREO_MK3 0x04
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(tuner_simple_list_mutex);
|
||||||
|
static LIST_HEAD(hybrid_tuner_instance_list);
|
||||||
|
|
||||||
struct tuner_simple_priv {
|
struct tuner_simple_priv {
|
||||||
u16 last_div;
|
u16 last_div;
|
||||||
|
|
||||||
struct tuner_i2c_props i2c_props;
|
struct tuner_i2c_props i2c_props;
|
||||||
|
struct list_head hybrid_tuner_instance_list;
|
||||||
|
|
||||||
unsigned int type;
|
unsigned int type;
|
||||||
struct tunertype *tun;
|
struct tunertype *tun;
|
||||||
|
|
||||||
u32 frequency;
|
u32 frequency;
|
||||||
|
u32 bandwidth;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------- */
|
||||||
|
@ -188,6 +194,9 @@ static inline char *tuner_param_name(enum param_type type)
|
||||||
case TUNER_PARAM_TYPE_NTSC:
|
case TUNER_PARAM_TYPE_NTSC:
|
||||||
name = "ntsc";
|
name = "ntsc";
|
||||||
break;
|
break;
|
||||||
|
case TUNER_PARAM_TYPE_DIGITAL:
|
||||||
|
name = "digital";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
name = "unknown";
|
name = "unknown";
|
||||||
break;
|
break;
|
||||||
|
@ -687,14 +696,118 @@ static int simple_set_params(struct dvb_frontend *fe,
|
||||||
priv->frequency = params->frequency * 62500;
|
priv->frequency = params->frequency * 62500;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
priv->bandwidth = 0;
|
||||||
|
|
||||||
return ret;
|
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)
|
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;
|
fe->tuner_priv = NULL;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -707,10 +820,20 @@ static int simple_get_frequency(struct dvb_frontend *fe, u32 *frequency)
|
||||||
return 0;
|
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 = {
|
static struct dvb_tuner_ops simple_tuner_ops = {
|
||||||
.set_analog_params = simple_set_params,
|
.set_analog_params = simple_set_params,
|
||||||
|
.set_params = simple_dvb_set_params,
|
||||||
|
.calc_regs = simple_dvb_calc_regs,
|
||||||
.release = simple_release,
|
.release = simple_release,
|
||||||
.get_frequency = simple_get_frequency,
|
.get_frequency = simple_get_frequency,
|
||||||
|
.get_bandwidth = simple_get_bandwidth,
|
||||||
.get_status = simple_get_status,
|
.get_status = simple_get_status,
|
||||||
.get_rf_strength = simple_get_rf_strength,
|
.get_rf_strength = simple_get_rf_strength,
|
||||||
};
|
};
|
||||||
|
@ -721,6 +844,7 @@ struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe,
|
||||||
unsigned int type)
|
unsigned int type)
|
||||||
{
|
{
|
||||||
struct tuner_simple_priv *priv = NULL;
|
struct tuner_simple_priv *priv = NULL;
|
||||||
|
int instance;
|
||||||
|
|
||||||
if (type >= tuner_count) {
|
if (type >= tuner_count) {
|
||||||
printk(KERN_WARNING "%s: invalid tuner type: %d (max: %d)\n",
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
priv = kzalloc(sizeof(struct tuner_simple_priv), GFP_KERNEL);
|
mutex_lock(&tuner_simple_list_mutex);
|
||||||
if (priv == NULL)
|
|
||||||
return NULL;
|
|
||||||
fe->tuner_priv = priv;
|
|
||||||
|
|
||||||
priv->i2c_props.addr = i2c_addr;
|
instance = hybrid_tuner_request_state(struct tuner_simple_priv, priv,
|
||||||
priv->i2c_props.adap = i2c_adap;
|
hybrid_tuner_instance_list,
|
||||||
priv->i2c_props.name = "tuner-simple";
|
i2c_adap, i2c_addr,
|
||||||
|
"tuner-simple");
|
||||||
|
switch (instance) {
|
||||||
|
case 0:
|
||||||
|
mutex_unlock(&tuner_simple_list_mutex);
|
||||||
|
return NULL;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
fe->tuner_priv = priv;
|
||||||
|
|
||||||
priv->type = type;
|
priv->type = type;
|
||||||
priv->tun = &tuners[type];
|
priv->tun = &tuners[type];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fe->tuner_priv = priv;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&tuner_simple_list_mutex);
|
||||||
|
|
||||||
memcpy(&fe->ops.tuner_ops, &simple_tuner_ops,
|
memcpy(&fe->ops.tuner_ops, &simple_tuner_ops,
|
||||||
sizeof(struct dvb_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,
|
strlcpy(fe->ops.tuner_ops.info.name, priv->tun->name,
|
||||||
sizeof(fe->ops.tuner_ops.info.name));
|
sizeof(fe->ops.tuner_ops.info.name));
|
||||||
|
|
|
@ -6,10 +6,11 @@
|
||||||
#define __TUNER_TYPES_H__
|
#define __TUNER_TYPES_H__
|
||||||
|
|
||||||
enum param_type {
|
enum param_type {
|
||||||
TUNER_PARAM_TYPE_RADIO, \
|
TUNER_PARAM_TYPE_RADIO,
|
||||||
TUNER_PARAM_TYPE_PAL, \
|
TUNER_PARAM_TYPE_PAL,
|
||||||
TUNER_PARAM_TYPE_SECAM, \
|
TUNER_PARAM_TYPE_SECAM,
|
||||||
TUNER_PARAM_TYPE_NTSC
|
TUNER_PARAM_TYPE_NTSC,
|
||||||
|
TUNER_PARAM_TYPE_DIGITAL,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tuner_range {
|
struct tuner_range {
|
||||||
|
@ -105,6 +106,7 @@ struct tuner_params {
|
||||||
the SECAM-L/L' standards. Range: -16:+15 */
|
the SECAM-L/L' standards. Range: -16:+15 */
|
||||||
signed int default_top_secam_high:5;
|
signed int default_top_secam_high:5;
|
||||||
|
|
||||||
|
u16 iffreq;
|
||||||
|
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
struct tuner_range *ranges;
|
struct tuner_range *ranges;
|
||||||
|
@ -114,6 +116,10 @@ struct tunertype {
|
||||||
char *name;
|
char *name;
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
struct tuner_params *params;
|
struct tuner_params *params;
|
||||||
|
|
||||||
|
u16 min;
|
||||||
|
u16 max;
|
||||||
|
u16 stepsize;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct tunertype tuners[];
|
extern struct tunertype tuners[];
|
||||||
|
|
Loading…
Reference in New Issue