This reverts commit a92591a711.
From: Michael Büsch <m@bues.ch>
To: linux-media@vger.kernel.org
Cc: mchehab@redhat.com
Subject: Re: [git:v4l-dvb/for_v3.9] [media] fc0011: Return early, if the frequency is already tuned
Date: Mon, 11 Feb 2013 21:59:19 +0100
Can you please revert this one again? It might cause issues if the dvb device
is reset/reinitialized.
Requested-by: Michael Büsch <m@bues.ch>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
		
	
			
		
			
				
	
	
		
			523 lines
		
	
	
	
		
			13 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			523 lines
		
	
	
	
		
			13 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Fitipower FC0011 tuner driver
 | 
						|
 *
 | 
						|
 * Copyright (C) 2012 Michael Buesch <m@bues.ch>
 | 
						|
 *
 | 
						|
 * Derived from FC0012 tuner driver:
 | 
						|
 * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify
 | 
						|
 * it under the terms of the GNU General Public License as published by
 | 
						|
 * the Free Software Foundation; either version 2 of the License, or
 | 
						|
 * (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 * GNU General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License
 | 
						|
 * along with this program; if not, write to the Free Software
 | 
						|
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
						|
 */
 | 
						|
 | 
						|
#include "fc0011.h"
 | 
						|
 | 
						|
 | 
						|
/* Tuner registers */
 | 
						|
enum {
 | 
						|
	FC11_REG_0,
 | 
						|
	FC11_REG_FA,		/* FA */
 | 
						|
	FC11_REG_FP,		/* FP */
 | 
						|
	FC11_REG_XINHI,		/* XIN high 8 bit */
 | 
						|
	FC11_REG_XINLO,		/* XIN low 8 bit */
 | 
						|
	FC11_REG_VCO,		/* VCO */
 | 
						|
	FC11_REG_VCOSEL,	/* VCO select */
 | 
						|
	FC11_REG_7,		/* Unknown tuner reg 7 */
 | 
						|
	FC11_REG_8,		/* Unknown tuner reg 8 */
 | 
						|
	FC11_REG_9,
 | 
						|
	FC11_REG_10,		/* Unknown tuner reg 10 */
 | 
						|
	FC11_REG_11,		/* Unknown tuner reg 11 */
 | 
						|
	FC11_REG_12,
 | 
						|
	FC11_REG_RCCAL,		/* RC calibrate */
 | 
						|
	FC11_REG_VCOCAL,	/* VCO calibrate */
 | 
						|
	FC11_REG_15,
 | 
						|
	FC11_REG_16,		/* Unknown tuner reg 16 */
 | 
						|
	FC11_REG_17,
 | 
						|
 | 
						|
	FC11_NR_REGS,		/* Number of registers */
 | 
						|
};
 | 
						|
 | 
						|
enum FC11_REG_VCOSEL_bits {
 | 
						|
	FC11_VCOSEL_2		= 0x08, /* VCO select 2 */
 | 
						|
	FC11_VCOSEL_1		= 0x10, /* VCO select 1 */
 | 
						|
	FC11_VCOSEL_CLKOUT	= 0x20, /* Fix clock out */
 | 
						|
	FC11_VCOSEL_BW7M	= 0x40, /* 7MHz bw */
 | 
						|
	FC11_VCOSEL_BW6M	= 0x80, /* 6MHz bw */
 | 
						|
};
 | 
						|
 | 
						|
enum FC11_REG_RCCAL_bits {
 | 
						|
	FC11_RCCAL_FORCE	= 0x10, /* force */
 | 
						|
};
 | 
						|
 | 
						|
enum FC11_REG_VCOCAL_bits {
 | 
						|
	FC11_VCOCAL_RUN		= 0,	/* VCO calibration run */
 | 
						|
	FC11_VCOCAL_VALUEMASK	= 0x3F,	/* VCO calibration value mask */
 | 
						|
	FC11_VCOCAL_OK		= 0x40,	/* VCO calibration Ok */
 | 
						|
	FC11_VCOCAL_RESET	= 0x80, /* VCO calibration reset */
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
struct fc0011_priv {
 | 
						|
	struct i2c_adapter *i2c;
 | 
						|
	u8 addr;
 | 
						|
 | 
						|
	u32 frequency;
 | 
						|
	u32 bandwidth;
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
static int fc0011_writereg(struct fc0011_priv *priv, u8 reg, u8 val)
 | 
						|
{
 | 
						|
	u8 buf[2] = { reg, val };
 | 
						|
	struct i2c_msg msg = { .addr = priv->addr,
 | 
						|
		.flags = 0, .buf = buf, .len = 2 };
 | 
						|
 | 
						|
	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
 | 
						|
		dev_err(&priv->i2c->dev,
 | 
						|
			"I2C write reg failed, reg: %02x, val: %02x\n",
 | 
						|
			reg, val);
 | 
						|
		return -EIO;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int fc0011_readreg(struct fc0011_priv *priv, u8 reg, u8 *val)
 | 
						|
{
 | 
						|
	u8 dummy;
 | 
						|
	struct i2c_msg msg[2] = {
 | 
						|
		{ .addr = priv->addr,
 | 
						|
		  .flags = 0, .buf = ®, .len = 1 },
 | 
						|
		{ .addr = priv->addr,
 | 
						|
		  .flags = I2C_M_RD, .buf = val ? : &dummy, .len = 1 },
 | 
						|
	};
 | 
						|
 | 
						|
	if (i2c_transfer(priv->i2c, msg, 2) != 2) {
 | 
						|
		dev_err(&priv->i2c->dev,
 | 
						|
			"I2C read failed, reg: %02x\n", reg);
 | 
						|
		return -EIO;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int fc0011_release(struct dvb_frontend *fe)
 | 
						|
{
 | 
						|
	kfree(fe->tuner_priv);
 | 
						|
	fe->tuner_priv = NULL;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int fc0011_init(struct dvb_frontend *fe)
 | 
						|
{
 | 
						|
	struct fc0011_priv *priv = fe->tuner_priv;
 | 
						|
	int err;
 | 
						|
 | 
						|
	if (WARN_ON(!fe->callback))
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
	err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
 | 
						|
			   FC0011_FE_CALLBACK_POWER, priv->addr);
 | 
						|
	if (err) {
 | 
						|
		dev_err(&priv->i2c->dev, "Power-on callback failed\n");
 | 
						|
		return err;
 | 
						|
	}
 | 
						|
	err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
 | 
						|
			   FC0011_FE_CALLBACK_RESET, priv->addr);
 | 
						|
	if (err) {
 | 
						|
		dev_err(&priv->i2c->dev, "Reset callback failed\n");
 | 
						|
		return err;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Initiate VCO calibration */
 | 
						|
static int fc0011_vcocal_trigger(struct fc0011_priv *priv)
 | 
						|
{
 | 
						|
	int err;
 | 
						|
 | 
						|
	err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RESET);
 | 
						|
	if (err)
 | 
						|
		return err;
 | 
						|
	err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN);
 | 
						|
	if (err)
 | 
						|
		return err;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Read VCO calibration value */
 | 
						|
static int fc0011_vcocal_read(struct fc0011_priv *priv, u8 *value)
 | 
						|
{
 | 
						|
	int err;
 | 
						|
 | 
						|
	err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN);
 | 
						|
	if (err)
 | 
						|
		return err;
 | 
						|
	usleep_range(10000, 20000);
 | 
						|
	err = fc0011_readreg(priv, FC11_REG_VCOCAL, value);
 | 
						|
	if (err)
 | 
						|
		return err;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int fc0011_set_params(struct dvb_frontend *fe)
 | 
						|
{
 | 
						|
	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
 | 
						|
	struct fc0011_priv *priv = fe->tuner_priv;
 | 
						|
	int err;
 | 
						|
	unsigned int i, vco_retries;
 | 
						|
	u32 freq = p->frequency / 1000;
 | 
						|
	u32 bandwidth = p->bandwidth_hz / 1000;
 | 
						|
	u32 fvco, xin, frac, xdiv, xdivr;
 | 
						|
	u8 fa, fp, vco_sel, vco_cal;
 | 
						|
	u8 regs[FC11_NR_REGS] = { };
 | 
						|
 | 
						|
	regs[FC11_REG_7] = 0x0F;
 | 
						|
	regs[FC11_REG_8] = 0x3E;
 | 
						|
	regs[FC11_REG_10] = 0xB8;
 | 
						|
	regs[FC11_REG_11] = 0x80;
 | 
						|
	regs[FC11_REG_RCCAL] = 0x04;
 | 
						|
	err = fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]);
 | 
						|
	err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]);
 | 
						|
	err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]);
 | 
						|
	err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]);
 | 
						|
	err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
 | 
						|
	if (err)
 | 
						|
		return -EIO;
 | 
						|
 | 
						|
	/* Set VCO freq and VCO div */
 | 
						|
	if (freq < 54000) {
 | 
						|
		fvco = freq * 64;
 | 
						|
		regs[FC11_REG_VCO] = 0x82;
 | 
						|
	} else if (freq < 108000) {
 | 
						|
		fvco = freq * 32;
 | 
						|
		regs[FC11_REG_VCO] = 0x42;
 | 
						|
	} else if (freq < 216000) {
 | 
						|
		fvco = freq * 16;
 | 
						|
		regs[FC11_REG_VCO] = 0x22;
 | 
						|
	} else if (freq < 432000) {
 | 
						|
		fvco = freq * 8;
 | 
						|
		regs[FC11_REG_VCO] = 0x12;
 | 
						|
	} else {
 | 
						|
		fvco = freq * 4;
 | 
						|
		regs[FC11_REG_VCO] = 0x0A;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Calc XIN. The PLL reference frequency is 18 MHz. */
 | 
						|
	xdiv = fvco / 18000;
 | 
						|
	WARN_ON(xdiv > 0xFF);
 | 
						|
	frac = fvco - xdiv * 18000;
 | 
						|
	frac = (frac << 15) / 18000;
 | 
						|
	if (frac >= 16384)
 | 
						|
		frac += 32786;
 | 
						|
	if (!frac)
 | 
						|
		xin = 0;
 | 
						|
	else
 | 
						|
		xin = clamp_t(u32, frac, 512, 65024);
 | 
						|
	regs[FC11_REG_XINHI] = xin >> 8;
 | 
						|
	regs[FC11_REG_XINLO] = xin;
 | 
						|
 | 
						|
	/* Calc FP and FA */
 | 
						|
	xdivr = xdiv;
 | 
						|
	if (fvco - xdiv * 18000 >= 9000)
 | 
						|
		xdivr += 1; /* round */
 | 
						|
	fp = xdivr / 8;
 | 
						|
	fa = xdivr - fp * 8;
 | 
						|
	if (fa < 2) {
 | 
						|
		fp -= 1;
 | 
						|
		fa += 8;
 | 
						|
	}
 | 
						|
	if (fp > 0x1F) {
 | 
						|
		fp = 0x1F;
 | 
						|
		fa = 0xF;
 | 
						|
	}
 | 
						|
	if (fa >= fp) {
 | 
						|
		dev_warn(&priv->i2c->dev,
 | 
						|
			 "fa %02X >= fp %02X, but trying to continue\n",
 | 
						|
			 (unsigned int)(u8)fa, (unsigned int)(u8)fp);
 | 
						|
	}
 | 
						|
	regs[FC11_REG_FA] = fa;
 | 
						|
	regs[FC11_REG_FP] = fp;
 | 
						|
 | 
						|
	/* Select bandwidth */
 | 
						|
	switch (bandwidth) {
 | 
						|
	case 8000:
 | 
						|
		break;
 | 
						|
	case 7000:
 | 
						|
		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW7M;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		dev_warn(&priv->i2c->dev, "Unsupported bandwidth %u kHz. "
 | 
						|
			 "Using 6000 kHz.\n",
 | 
						|
			 bandwidth);
 | 
						|
		bandwidth = 6000;
 | 
						|
		/* fallthrough */
 | 
						|
	case 6000:
 | 
						|
		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW6M;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Pre VCO select */
 | 
						|
	if (fvco < 2320000) {
 | 
						|
		vco_sel = 0;
 | 
						|
		regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 | 
						|
	} else if (fvco < 3080000) {
 | 
						|
		vco_sel = 1;
 | 
						|
		regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 | 
						|
		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
 | 
						|
	} else {
 | 
						|
		vco_sel = 2;
 | 
						|
		regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 | 
						|
		regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Fix for low freqs */
 | 
						|
	if (freq < 45000) {
 | 
						|
		regs[FC11_REG_FA] = 0x6;
 | 
						|
		regs[FC11_REG_FP] = 0x11;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Clock out fix */
 | 
						|
	regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_CLKOUT;
 | 
						|
 | 
						|
	/* Write the cached registers */
 | 
						|
	for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) {
 | 
						|
		err = fc0011_writereg(priv, i, regs[i]);
 | 
						|
		if (err)
 | 
						|
			return err;
 | 
						|
	}
 | 
						|
 | 
						|
	/* VCO calibration */
 | 
						|
	err = fc0011_vcocal_trigger(priv);
 | 
						|
	if (err)
 | 
						|
		return err;
 | 
						|
	err = fc0011_vcocal_read(priv, &vco_cal);
 | 
						|
	if (err)
 | 
						|
		return err;
 | 
						|
	vco_retries = 0;
 | 
						|
	while (!(vco_cal & FC11_VCOCAL_OK) && vco_retries < 3) {
 | 
						|
		/* Reset the tuner and try again */
 | 
						|
		err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
 | 
						|
				   FC0011_FE_CALLBACK_RESET, priv->addr);
 | 
						|
		if (err) {
 | 
						|
			dev_err(&priv->i2c->dev, "Failed to reset tuner\n");
 | 
						|
			return err;
 | 
						|
		}
 | 
						|
		/* Reinit tuner config */
 | 
						|
		err = 0;
 | 
						|
		for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++)
 | 
						|
			err |= fc0011_writereg(priv, i, regs[i]);
 | 
						|
		err |= fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]);
 | 
						|
		err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]);
 | 
						|
		err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]);
 | 
						|
		err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]);
 | 
						|
		err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
 | 
						|
		if (err)
 | 
						|
			return -EIO;
 | 
						|
		/* VCO calibration */
 | 
						|
		err = fc0011_vcocal_trigger(priv);
 | 
						|
		if (err)
 | 
						|
			return err;
 | 
						|
		err = fc0011_vcocal_read(priv, &vco_cal);
 | 
						|
		if (err)
 | 
						|
			return err;
 | 
						|
		vco_retries++;
 | 
						|
	}
 | 
						|
	if (!(vco_cal & FC11_VCOCAL_OK)) {
 | 
						|
		dev_err(&priv->i2c->dev,
 | 
						|
			"Failed to read VCO calibration value (got %02X)\n",
 | 
						|
			(unsigned int)vco_cal);
 | 
						|
		return -EIO;
 | 
						|
	}
 | 
						|
	vco_cal &= FC11_VCOCAL_VALUEMASK;
 | 
						|
 | 
						|
	switch (vco_sel) {
 | 
						|
	default:
 | 
						|
		WARN_ON(1);
 | 
						|
	case 0:
 | 
						|
		if (vco_cal < 8) {
 | 
						|
			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 | 
						|
			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
 | 
						|
			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
 | 
						|
					      regs[FC11_REG_VCOSEL]);
 | 
						|
			if (err)
 | 
						|
				return err;
 | 
						|
			err = fc0011_vcocal_trigger(priv);
 | 
						|
			if (err)
 | 
						|
				return err;
 | 
						|
		} else {
 | 
						|
			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 | 
						|
			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
 | 
						|
					      regs[FC11_REG_VCOSEL]);
 | 
						|
			if (err)
 | 
						|
				return err;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	case 1:
 | 
						|
		if (vco_cal < 5) {
 | 
						|
			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 | 
						|
			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
 | 
						|
			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
 | 
						|
					      regs[FC11_REG_VCOSEL]);
 | 
						|
			if (err)
 | 
						|
				return err;
 | 
						|
			err = fc0011_vcocal_trigger(priv);
 | 
						|
			if (err)
 | 
						|
				return err;
 | 
						|
		} else if (vco_cal <= 48) {
 | 
						|
			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 | 
						|
			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
 | 
						|
			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
 | 
						|
					      regs[FC11_REG_VCOSEL]);
 | 
						|
			if (err)
 | 
						|
				return err;
 | 
						|
		} else {
 | 
						|
			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 | 
						|
			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
 | 
						|
					      regs[FC11_REG_VCOSEL]);
 | 
						|
			if (err)
 | 
						|
				return err;
 | 
						|
			err = fc0011_vcocal_trigger(priv);
 | 
						|
			if (err)
 | 
						|
				return err;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	case 2:
 | 
						|
		if (vco_cal > 53) {
 | 
						|
			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 | 
						|
			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
 | 
						|
			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
 | 
						|
					      regs[FC11_REG_VCOSEL]);
 | 
						|
			if (err)
 | 
						|
				return err;
 | 
						|
			err = fc0011_vcocal_trigger(priv);
 | 
						|
			if (err)
 | 
						|
				return err;
 | 
						|
		} else {
 | 
						|
			regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 | 
						|
			regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
 | 
						|
			err = fc0011_writereg(priv, FC11_REG_VCOSEL,
 | 
						|
					      regs[FC11_REG_VCOSEL]);
 | 
						|
			if (err)
 | 
						|
				return err;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	err = fc0011_vcocal_read(priv, NULL);
 | 
						|
	if (err)
 | 
						|
		return err;
 | 
						|
	usleep_range(10000, 50000);
 | 
						|
 | 
						|
	err = fc0011_readreg(priv, FC11_REG_RCCAL, ®s[FC11_REG_RCCAL]);
 | 
						|
	if (err)
 | 
						|
		return err;
 | 
						|
	regs[FC11_REG_RCCAL] |= FC11_RCCAL_FORCE;
 | 
						|
	err = fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
 | 
						|
	if (err)
 | 
						|
		return err;
 | 
						|
	regs[FC11_REG_16] = 0xB;
 | 
						|
	err = fc0011_writereg(priv, FC11_REG_16, regs[FC11_REG_16]);
 | 
						|
	if (err)
 | 
						|
		return err;
 | 
						|
 | 
						|
	dev_dbg(&priv->i2c->dev, "Tuned to "
 | 
						|
		"fa=%02X fp=%02X xin=%02X%02X vco=%02X vcosel=%02X "
 | 
						|
		"vcocal=%02X(%u) bw=%u\n",
 | 
						|
		(unsigned int)regs[FC11_REG_FA],
 | 
						|
		(unsigned int)regs[FC11_REG_FP],
 | 
						|
		(unsigned int)regs[FC11_REG_XINHI],
 | 
						|
		(unsigned int)regs[FC11_REG_XINLO],
 | 
						|
		(unsigned int)regs[FC11_REG_VCO],
 | 
						|
		(unsigned int)regs[FC11_REG_VCOSEL],
 | 
						|
		(unsigned int)vco_cal, vco_retries,
 | 
						|
		(unsigned int)bandwidth);
 | 
						|
 | 
						|
	priv->frequency = p->frequency;
 | 
						|
	priv->bandwidth = p->bandwidth_hz;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int fc0011_get_frequency(struct dvb_frontend *fe, u32 *frequency)
 | 
						|
{
 | 
						|
	struct fc0011_priv *priv = fe->tuner_priv;
 | 
						|
 | 
						|
	*frequency = priv->frequency;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
 | 
						|
{
 | 
						|
	*frequency = 0;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
 | 
						|
{
 | 
						|
	struct fc0011_priv *priv = fe->tuner_priv;
 | 
						|
 | 
						|
	*bandwidth = priv->bandwidth;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static const struct dvb_tuner_ops fc0011_tuner_ops = {
 | 
						|
	.info = {
 | 
						|
		.name		= "Fitipower FC0011",
 | 
						|
 | 
						|
		.frequency_min	= 45000000,
 | 
						|
		.frequency_max	= 1000000000,
 | 
						|
	},
 | 
						|
 | 
						|
	.release		= fc0011_release,
 | 
						|
	.init			= fc0011_init,
 | 
						|
 | 
						|
	.set_params		= fc0011_set_params,
 | 
						|
 | 
						|
	.get_frequency		= fc0011_get_frequency,
 | 
						|
	.get_if_frequency	= fc0011_get_if_frequency,
 | 
						|
	.get_bandwidth		= fc0011_get_bandwidth,
 | 
						|
};
 | 
						|
 | 
						|
struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe,
 | 
						|
				   struct i2c_adapter *i2c,
 | 
						|
				   const struct fc0011_config *config)
 | 
						|
{
 | 
						|
	struct fc0011_priv *priv;
 | 
						|
 | 
						|
	priv = kzalloc(sizeof(struct fc0011_priv), GFP_KERNEL);
 | 
						|
	if (!priv)
 | 
						|
		return NULL;
 | 
						|
 | 
						|
	priv->i2c = i2c;
 | 
						|
	priv->addr = config->i2c_address;
 | 
						|
 | 
						|
	fe->tuner_priv = priv;
 | 
						|
	fe->ops.tuner_ops = fc0011_tuner_ops;
 | 
						|
 | 
						|
	dev_info(&priv->i2c->dev, "Fitipower FC0011 tuner attached\n");
 | 
						|
 | 
						|
	return fe;
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(fc0011_attach);
 | 
						|
 | 
						|
MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver");
 | 
						|
MODULE_AUTHOR("Michael Buesch <m@bues.ch>");
 | 
						|
MODULE_LICENSE("GPL");
 |