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:
Michael Krufky 2008-04-22 14:45:52 -03:00 committed by Mauro Carvalho Chehab
parent ac8b63b30a
commit 62325497db
2 changed files with 156 additions and 14 deletions

View File

@ -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));

View File

@ -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[];