Commit 62325497 authored by Michael Krufky's avatar Michael Krufky Committed by Mauro Carvalho Chehab
Browse files

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: default avatarMichael Krufky <mkrufky@linuxtv.org>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent ac8b63b3
Loading
Loading
Loading
Loading
+146 −10
Original line number Diff line number Diff line
@@ -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;
		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;
	}

	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));
+10 −4
Original line number Diff line number Diff line
@@ -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[];