19a0bf528SMauro Carvalho Chehab /*
29a0bf528SMauro Carvalho Chehab 	TDA8261 8PSK/QPSK tuner driver
39a0bf528SMauro Carvalho Chehab 	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
49a0bf528SMauro Carvalho Chehab 
59a0bf528SMauro Carvalho Chehab 	This program is free software; you can redistribute it and/or modify
69a0bf528SMauro Carvalho Chehab 	it under the terms of the GNU General Public License as published by
79a0bf528SMauro Carvalho Chehab 	the Free Software Foundation; either version 2 of the License, or
89a0bf528SMauro Carvalho Chehab 	(at your option) any later version.
99a0bf528SMauro Carvalho Chehab 
109a0bf528SMauro Carvalho Chehab 	This program is distributed in the hope that it will be useful,
119a0bf528SMauro Carvalho Chehab 	but WITHOUT ANY WARRANTY; without even the implied warranty of
129a0bf528SMauro Carvalho Chehab 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
139a0bf528SMauro Carvalho Chehab 	GNU General Public License for more details.
149a0bf528SMauro Carvalho Chehab 
159a0bf528SMauro Carvalho Chehab 	You should have received a copy of the GNU General Public License
169a0bf528SMauro Carvalho Chehab 	along with this program; if not, write to the Free Software
179a0bf528SMauro Carvalho Chehab 	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
189a0bf528SMauro Carvalho Chehab */
199a0bf528SMauro Carvalho Chehab 
209a0bf528SMauro Carvalho Chehab 
219a0bf528SMauro Carvalho Chehab #include <linux/init.h>
229a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
239a0bf528SMauro Carvalho Chehab #include <linux/module.h>
249a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
259a0bf528SMauro Carvalho Chehab 
269a0bf528SMauro Carvalho Chehab #include "dvb_frontend.h"
279a0bf528SMauro Carvalho Chehab #include "tda8261.h"
289a0bf528SMauro Carvalho Chehab 
299a0bf528SMauro Carvalho Chehab struct tda8261_state {
309a0bf528SMauro Carvalho Chehab 	struct dvb_frontend		*fe;
319a0bf528SMauro Carvalho Chehab 	struct i2c_adapter		*i2c;
329a0bf528SMauro Carvalho Chehab 	const struct tda8261_config	*config;
339a0bf528SMauro Carvalho Chehab 
349a0bf528SMauro Carvalho Chehab 	/* state cache */
359a0bf528SMauro Carvalho Chehab 	u32 frequency;
369a0bf528SMauro Carvalho Chehab 	u32 bandwidth;
379a0bf528SMauro Carvalho Chehab };
389a0bf528SMauro Carvalho Chehab 
399a0bf528SMauro Carvalho Chehab static int tda8261_read(struct tda8261_state *state, u8 *buf)
409a0bf528SMauro Carvalho Chehab {
419a0bf528SMauro Carvalho Chehab 	const struct tda8261_config *config = state->config;
429a0bf528SMauro Carvalho Chehab 	int err = 0;
439a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = { .addr	= config->addr, .flags = I2C_M_RD,.buf = buf,  .len = 1 };
449a0bf528SMauro Carvalho Chehab 
459a0bf528SMauro Carvalho Chehab 	if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1)
46eb2e2652SAlan Cox 		pr_err("%s: read error, err=%d\n", __func__, err);
479a0bf528SMauro Carvalho Chehab 
489a0bf528SMauro Carvalho Chehab 	return err;
499a0bf528SMauro Carvalho Chehab }
509a0bf528SMauro Carvalho Chehab 
519a0bf528SMauro Carvalho Chehab static int tda8261_write(struct tda8261_state *state, u8 *buf)
529a0bf528SMauro Carvalho Chehab {
539a0bf528SMauro Carvalho Chehab 	const struct tda8261_config *config = state->config;
549a0bf528SMauro Carvalho Chehab 	int err = 0;
559a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = { .addr = config->addr, .flags = 0, .buf = buf, .len = 4 };
569a0bf528SMauro Carvalho Chehab 
579a0bf528SMauro Carvalho Chehab 	if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1)
58eb2e2652SAlan Cox 		pr_err("%s: write error, err=%d\n", __func__, err);
599a0bf528SMauro Carvalho Chehab 
609a0bf528SMauro Carvalho Chehab 	return err;
619a0bf528SMauro Carvalho Chehab }
629a0bf528SMauro Carvalho Chehab 
639a0bf528SMauro Carvalho Chehab static int tda8261_get_status(struct dvb_frontend *fe, u32 *status)
649a0bf528SMauro Carvalho Chehab {
659a0bf528SMauro Carvalho Chehab 	struct tda8261_state *state = fe->tuner_priv;
669a0bf528SMauro Carvalho Chehab 	u8 result = 0;
679a0bf528SMauro Carvalho Chehab 	int err = 0;
689a0bf528SMauro Carvalho Chehab 
699a0bf528SMauro Carvalho Chehab 	*status = 0;
709a0bf528SMauro Carvalho Chehab 
719a0bf528SMauro Carvalho Chehab 	if ((err = tda8261_read(state, &result)) < 0) {
72eb2e2652SAlan Cox 		pr_err("%s: I/O Error\n", __func__);
739a0bf528SMauro Carvalho Chehab 		return err;
749a0bf528SMauro Carvalho Chehab 	}
759a0bf528SMauro Carvalho Chehab 	if ((result >> 6) & 0x01) {
76eb2e2652SAlan Cox 		pr_debug("%s: Tuner Phase Locked\n", __func__);
779a0bf528SMauro Carvalho Chehab 		*status = 1;
789a0bf528SMauro Carvalho Chehab 	}
799a0bf528SMauro Carvalho Chehab 
809a0bf528SMauro Carvalho Chehab 	return err;
819a0bf528SMauro Carvalho Chehab }
829a0bf528SMauro Carvalho Chehab 
839a0bf528SMauro Carvalho Chehab static const u32 div_tab[] = { 2000, 1000,  500,  250,  125 }; /* kHz */
849a0bf528SMauro Carvalho Chehab static const u8  ref_div[] = { 0x00, 0x01, 0x02, 0x05, 0x07 };
859a0bf528SMauro Carvalho Chehab 
86e417668dSMauro Carvalho Chehab static int tda8261_get_frequency(struct dvb_frontend *fe, u32 *frequency)
879a0bf528SMauro Carvalho Chehab {
889a0bf528SMauro Carvalho Chehab 	struct tda8261_state *state = fe->tuner_priv;
899a0bf528SMauro Carvalho Chehab 
90e417668dSMauro Carvalho Chehab 	*frequency = state->frequency;
91e417668dSMauro Carvalho Chehab 
92e417668dSMauro Carvalho Chehab 	return 0;
939a0bf528SMauro Carvalho Chehab }
949a0bf528SMauro Carvalho Chehab 
95e417668dSMauro Carvalho Chehab static int tda8261_set_params(struct dvb_frontend *fe)
969a0bf528SMauro Carvalho Chehab {
97e417668dSMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
989a0bf528SMauro Carvalho Chehab 	struct tda8261_state *state = fe->tuner_priv;
999a0bf528SMauro Carvalho Chehab 	const struct tda8261_config *config = state->config;
1009a0bf528SMauro Carvalho Chehab 	u32 frequency, N, status = 0;
1019a0bf528SMauro Carvalho Chehab 	u8 buf[4];
1029a0bf528SMauro Carvalho Chehab 	int err = 0;
1039a0bf528SMauro Carvalho Chehab 
104e417668dSMauro Carvalho Chehab 	/*
1059a0bf528SMauro Carvalho Chehab 	 * N = Max VCO Frequency / Channel Spacing
1069a0bf528SMauro Carvalho Chehab 	 * Max VCO Frequency = VCO frequency + (channel spacing - 1)
1079a0bf528SMauro Carvalho Chehab 	 * (to account for half channel spacing on either side)
1089a0bf528SMauro Carvalho Chehab 	 */
109e417668dSMauro Carvalho Chehab 	frequency = c->frequency;
1109a0bf528SMauro Carvalho Chehab 	if ((frequency < 950000) || (frequency > 2150000)) {
111e417668dSMauro Carvalho Chehab 		pr_warn("%s: Frequency beyond limits, frequency=%d\n",
112e417668dSMauro Carvalho Chehab 			__func__, frequency);
1139a0bf528SMauro Carvalho Chehab 		return -EINVAL;
1149a0bf528SMauro Carvalho Chehab 	}
1159a0bf528SMauro Carvalho Chehab 	N = (frequency + (div_tab[config->step_size] - 1)) / div_tab[config->step_size];
116eb2e2652SAlan Cox 	pr_debug("%s: Step size=%d, Divider=%d, PG=0x%02x (%d)\n",
1179a0bf528SMauro Carvalho Chehab 		__func__, config->step_size, div_tab[config->step_size], N, N);
1189a0bf528SMauro Carvalho Chehab 
1199a0bf528SMauro Carvalho Chehab 	buf[0] = (N >> 8) & 0xff;
1209a0bf528SMauro Carvalho Chehab 	buf[1] = N & 0xff;
1219a0bf528SMauro Carvalho Chehab 	buf[2] = (0x01 << 7) | ((ref_div[config->step_size] & 0x07) << 1);
1229a0bf528SMauro Carvalho Chehab 
1239a0bf528SMauro Carvalho Chehab 	if (frequency < 1450000)
1249a0bf528SMauro Carvalho Chehab 		buf[3] = 0x00;
1259a0bf528SMauro Carvalho Chehab 	else if (frequency < 2000000)
1269a0bf528SMauro Carvalho Chehab 		buf[3] = 0x40;
1279a0bf528SMauro Carvalho Chehab 	else if (frequency < 2150000)
1289a0bf528SMauro Carvalho Chehab 		buf[3] = 0x80;
1299a0bf528SMauro Carvalho Chehab 
1309a0bf528SMauro Carvalho Chehab 	/* Set params */
131e417668dSMauro Carvalho Chehab 	err = tda8261_write(state, buf);
132e417668dSMauro Carvalho Chehab 	if (err < 0) {
133eb2e2652SAlan Cox 		pr_err("%s: I/O Error\n", __func__);
1349a0bf528SMauro Carvalho Chehab 		return err;
1359a0bf528SMauro Carvalho Chehab 	}
1369a0bf528SMauro Carvalho Chehab 	/* sleep for some time */
137eb2e2652SAlan Cox 	pr_debug("%s: Waiting to Phase LOCK\n", __func__);
1389a0bf528SMauro Carvalho Chehab 	msleep(20);
1399a0bf528SMauro Carvalho Chehab 	/* check status */
1409a0bf528SMauro Carvalho Chehab 	if ((err = tda8261_get_status(fe, &status)) < 0) {
141eb2e2652SAlan Cox 		pr_err("%s: I/O Error\n", __func__);
1429a0bf528SMauro Carvalho Chehab 		return err;
1439a0bf528SMauro Carvalho Chehab 	}
1449a0bf528SMauro Carvalho Chehab 	if (status == 1) {
145e417668dSMauro Carvalho Chehab 		pr_debug("%s: Tuner Phase locked: status=%d\n", __func__,
146e417668dSMauro Carvalho Chehab 			 status);
1479a0bf528SMauro Carvalho Chehab 		state->frequency = frequency; /* cache successful state */
1489a0bf528SMauro Carvalho Chehab 	} else {
149eb2e2652SAlan Cox 		pr_debug("%s: No Phase lock: status=%d\n", __func__, status);
1509a0bf528SMauro Carvalho Chehab 	}
1519a0bf528SMauro Carvalho Chehab 
1529a0bf528SMauro Carvalho Chehab 	return 0;
1539a0bf528SMauro Carvalho Chehab }
1549a0bf528SMauro Carvalho Chehab 
1559a0bf528SMauro Carvalho Chehab static int tda8261_release(struct dvb_frontend *fe)
1569a0bf528SMauro Carvalho Chehab {
1579a0bf528SMauro Carvalho Chehab 	struct tda8261_state *state = fe->tuner_priv;
1589a0bf528SMauro Carvalho Chehab 
1599a0bf528SMauro Carvalho Chehab 	fe->tuner_priv = NULL;
1609a0bf528SMauro Carvalho Chehab 	kfree(state);
1619a0bf528SMauro Carvalho Chehab 	return 0;
1629a0bf528SMauro Carvalho Chehab }
1639a0bf528SMauro Carvalho Chehab 
16414c4bf3cSJulia Lawall static const struct dvb_tuner_ops tda8261_ops = {
1659a0bf528SMauro Carvalho Chehab 
1669a0bf528SMauro Carvalho Chehab 	.info = {
1679a0bf528SMauro Carvalho Chehab 		.name		= "TDA8261",
1689a0bf528SMauro Carvalho Chehab 		.frequency_min	=  950000,
1699a0bf528SMauro Carvalho Chehab 		.frequency_max	= 2150000,
1709a0bf528SMauro Carvalho Chehab 		.frequency_step = 0
1719a0bf528SMauro Carvalho Chehab 	},
1729a0bf528SMauro Carvalho Chehab 
173e417668dSMauro Carvalho Chehab 	.set_params	= tda8261_set_params,
174e417668dSMauro Carvalho Chehab 	.get_frequency	= tda8261_get_frequency,
1759a0bf528SMauro Carvalho Chehab 	.get_status	= tda8261_get_status,
1769a0bf528SMauro Carvalho Chehab 	.release	= tda8261_release
1779a0bf528SMauro Carvalho Chehab };
1789a0bf528SMauro Carvalho Chehab 
1799a0bf528SMauro Carvalho Chehab struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe,
1809a0bf528SMauro Carvalho Chehab 				    const struct tda8261_config *config,
1819a0bf528SMauro Carvalho Chehab 				    struct i2c_adapter *i2c)
1829a0bf528SMauro Carvalho Chehab {
1839a0bf528SMauro Carvalho Chehab 	struct tda8261_state *state = NULL;
1849a0bf528SMauro Carvalho Chehab 
1859a0bf528SMauro Carvalho Chehab 	if ((state = kzalloc(sizeof (struct tda8261_state), GFP_KERNEL)) == NULL)
1869a0bf528SMauro Carvalho Chehab 		goto exit;
1879a0bf528SMauro Carvalho Chehab 
1889a0bf528SMauro Carvalho Chehab 	state->config		= config;
1899a0bf528SMauro Carvalho Chehab 	state->i2c		= i2c;
1909a0bf528SMauro Carvalho Chehab 	state->fe		= fe;
1919a0bf528SMauro Carvalho Chehab 	fe->tuner_priv		= state;
1929a0bf528SMauro Carvalho Chehab 	fe->ops.tuner_ops	= tda8261_ops;
1939a0bf528SMauro Carvalho Chehab 
1949a0bf528SMauro Carvalho Chehab 	fe->ops.tuner_ops.info.frequency_step = div_tab[config->step_size];
1959a0bf528SMauro Carvalho Chehab 
196eb2e2652SAlan Cox 	pr_info("%s: Attaching TDA8261 8PSK/QPSK tuner\n", __func__);
1979a0bf528SMauro Carvalho Chehab 
1989a0bf528SMauro Carvalho Chehab 	return fe;
1999a0bf528SMauro Carvalho Chehab 
2009a0bf528SMauro Carvalho Chehab exit:
2019a0bf528SMauro Carvalho Chehab 	kfree(state);
2029a0bf528SMauro Carvalho Chehab 	return NULL;
2039a0bf528SMauro Carvalho Chehab }
2049a0bf528SMauro Carvalho Chehab 
2059a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(tda8261_attach);
2069a0bf528SMauro Carvalho Chehab 
2079a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Manu Abraham");
2089a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("TDA8261 8PSK/QPSK Tuner");
2099a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
210