xref: /linux/drivers/media/dvb-frontends/zl10039.c (revision 194ced7a5a99b303daf0bca6d0a1697731265602)
12ccf5a99SIgor M. Liplianin /*
22ccf5a99SIgor M. Liplianin  *  Driver for Zarlink ZL10039 DVB-S tuner
32ccf5a99SIgor M. Liplianin  *
42ccf5a99SIgor M. Liplianin  *  Copyright 2007 Jan D. Louw <jd.louw@mweb.co.za>
52ccf5a99SIgor M. Liplianin  *
62ccf5a99SIgor M. Liplianin  *  This program is free software; you can redistribute it and/or modify
72ccf5a99SIgor M. Liplianin  *  it under the terms of the GNU General Public License as published by
82ccf5a99SIgor M. Liplianin  *  the Free Software Foundation; either version 2 of the License, or
92ccf5a99SIgor M. Liplianin  *  (at your option) any later version.
102ccf5a99SIgor M. Liplianin  *
112ccf5a99SIgor M. Liplianin  *  This program is distributed in the hope that it will be useful,
122ccf5a99SIgor M. Liplianin  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
132ccf5a99SIgor M. Liplianin  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
142ccf5a99SIgor M. Liplianin  *
152ccf5a99SIgor M. Liplianin  *  GNU General Public License for more details.
162ccf5a99SIgor M. Liplianin  *
172ccf5a99SIgor M. Liplianin  *  You should have received a copy of the GNU General Public License
182ccf5a99SIgor M. Liplianin  *  along with this program; if not, write to the Free Software
192ccf5a99SIgor M. Liplianin  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
202ccf5a99SIgor M. Liplianin  */
212ccf5a99SIgor M. Liplianin 
222ccf5a99SIgor M. Liplianin #include <linux/module.h>
232ccf5a99SIgor M. Liplianin #include <linux/init.h>
242ccf5a99SIgor M. Liplianin #include <linux/string.h>
252ccf5a99SIgor M. Liplianin #include <linux/slab.h>
262ccf5a99SIgor M. Liplianin #include <linux/dvb/frontend.h>
272ccf5a99SIgor M. Liplianin 
282ccf5a99SIgor M. Liplianin #include "dvb_frontend.h"
292ccf5a99SIgor M. Liplianin #include "zl10039.h"
302ccf5a99SIgor M. Liplianin 
312ccf5a99SIgor M. Liplianin static int debug;
322ccf5a99SIgor M. Liplianin 
338393796dSMauro Carvalho Chehab /* Max transfer size done by I2C transfer functions */
348393796dSMauro Carvalho Chehab #define MAX_XFER_SIZE  64
358393796dSMauro Carvalho Chehab 
362ccf5a99SIgor M. Liplianin #define dprintk(args...) \
372ccf5a99SIgor M. Liplianin 	do { \
382ccf5a99SIgor M. Liplianin 		if (debug) \
392ccf5a99SIgor M. Liplianin 			printk(KERN_DEBUG args); \
402ccf5a99SIgor M. Liplianin 	} while (0)
412ccf5a99SIgor M. Liplianin 
422ccf5a99SIgor M. Liplianin enum zl10039_model_id {
432ccf5a99SIgor M. Liplianin 	ID_ZL10039 = 1
442ccf5a99SIgor M. Liplianin };
452ccf5a99SIgor M. Liplianin 
462ccf5a99SIgor M. Liplianin struct zl10039_state {
472ccf5a99SIgor M. Liplianin 	struct i2c_adapter *i2c;
482ccf5a99SIgor M. Liplianin 	u8 i2c_addr;
492ccf5a99SIgor M. Liplianin 	u8 id;
502ccf5a99SIgor M. Liplianin };
512ccf5a99SIgor M. Liplianin 
522ccf5a99SIgor M. Liplianin enum zl10039_reg_addr {
532ccf5a99SIgor M. Liplianin 	PLL0 = 0,
542ccf5a99SIgor M. Liplianin 	PLL1,
552ccf5a99SIgor M. Liplianin 	PLL2,
562ccf5a99SIgor M. Liplianin 	PLL3,
572ccf5a99SIgor M. Liplianin 	RFFE,
582ccf5a99SIgor M. Liplianin 	BASE0,
592ccf5a99SIgor M. Liplianin 	BASE1,
602ccf5a99SIgor M. Liplianin 	BASE2,
612ccf5a99SIgor M. Liplianin 	LO0,
622ccf5a99SIgor M. Liplianin 	LO1,
632ccf5a99SIgor M. Liplianin 	LO2,
642ccf5a99SIgor M. Liplianin 	LO3,
652ccf5a99SIgor M. Liplianin 	LO4,
662ccf5a99SIgor M. Liplianin 	LO5,
672ccf5a99SIgor M. Liplianin 	LO6,
682ccf5a99SIgor M. Liplianin 	GENERAL
692ccf5a99SIgor M. Liplianin };
702ccf5a99SIgor M. Liplianin 
712ccf5a99SIgor M. Liplianin static int zl10039_read(const struct zl10039_state *state,
722ccf5a99SIgor M. Liplianin 			const enum zl10039_reg_addr reg, u8 *buf,
732ccf5a99SIgor M. Liplianin 			const size_t count)
742ccf5a99SIgor M. Liplianin {
752ccf5a99SIgor M. Liplianin 	u8 regbuf[] = { reg };
762ccf5a99SIgor M. Liplianin 	struct i2c_msg msg[] = {
772ccf5a99SIgor M. Liplianin 		{/* Write register address */
782ccf5a99SIgor M. Liplianin 			.addr = state->i2c_addr,
792ccf5a99SIgor M. Liplianin 			.flags = 0,
802ccf5a99SIgor M. Liplianin 			.buf = regbuf,
812ccf5a99SIgor M. Liplianin 			.len = 1,
822ccf5a99SIgor M. Liplianin 		}, {/* Read count bytes */
832ccf5a99SIgor M. Liplianin 			.addr = state->i2c_addr,
842ccf5a99SIgor M. Liplianin 			.flags = I2C_M_RD,
852ccf5a99SIgor M. Liplianin 			.buf = buf,
862ccf5a99SIgor M. Liplianin 			.len = count,
872ccf5a99SIgor M. Liplianin 		},
882ccf5a99SIgor M. Liplianin 	};
892ccf5a99SIgor M. Liplianin 
902ccf5a99SIgor M. Liplianin 	dprintk("%s\n", __func__);
912ccf5a99SIgor M. Liplianin 
922ccf5a99SIgor M. Liplianin 	if (i2c_transfer(state->i2c, msg, 2) != 2) {
932ccf5a99SIgor M. Liplianin 		dprintk("%s: i2c read error\n", __func__);
942ccf5a99SIgor M. Liplianin 		return -EREMOTEIO;
952ccf5a99SIgor M. Liplianin 	}
962ccf5a99SIgor M. Liplianin 
972ccf5a99SIgor M. Liplianin 	return 0; /* Success */
982ccf5a99SIgor M. Liplianin }
992ccf5a99SIgor M. Liplianin 
1002ccf5a99SIgor M. Liplianin static int zl10039_write(struct zl10039_state *state,
1012ccf5a99SIgor M. Liplianin 			const enum zl10039_reg_addr reg, const u8 *src,
1022ccf5a99SIgor M. Liplianin 			const size_t count)
1032ccf5a99SIgor M. Liplianin {
1048393796dSMauro Carvalho Chehab 	u8 buf[MAX_XFER_SIZE];
1052ccf5a99SIgor M. Liplianin 	struct i2c_msg msg = {
1062ccf5a99SIgor M. Liplianin 		.addr = state->i2c_addr,
1072ccf5a99SIgor M. Liplianin 		.flags = 0,
1082ccf5a99SIgor M. Liplianin 		.buf = buf,
1092ccf5a99SIgor M. Liplianin 		.len = count + 1,
1102ccf5a99SIgor M. Liplianin 	};
1112ccf5a99SIgor M. Liplianin 
1128393796dSMauro Carvalho Chehab 	if (1 + count > sizeof(buf)) {
1138393796dSMauro Carvalho Chehab 		printk(KERN_WARNING
11435f30f36SMauro Carvalho Chehab 		       "%s: i2c wr reg=%04x: len=%zu is too big!\n",
1158393796dSMauro Carvalho Chehab 		       KBUILD_MODNAME, reg, count);
1168393796dSMauro Carvalho Chehab 		return -EINVAL;
1178393796dSMauro Carvalho Chehab 	}
1188393796dSMauro Carvalho Chehab 
1192ccf5a99SIgor M. Liplianin 	dprintk("%s\n", __func__);
1202ccf5a99SIgor M. Liplianin 	/* Write register address and data in one go */
1212ccf5a99SIgor M. Liplianin 	buf[0] = reg;
1222ccf5a99SIgor M. Liplianin 	memcpy(&buf[1], src, count);
1232ccf5a99SIgor M. Liplianin 	if (i2c_transfer(state->i2c, &msg, 1) != 1) {
1242ccf5a99SIgor M. Liplianin 		dprintk("%s: i2c write error\n", __func__);
1252ccf5a99SIgor M. Liplianin 		return -EREMOTEIO;
1262ccf5a99SIgor M. Liplianin 	}
1272ccf5a99SIgor M. Liplianin 
1282ccf5a99SIgor M. Liplianin 	return 0; /* Success */
1292ccf5a99SIgor M. Liplianin }
1302ccf5a99SIgor M. Liplianin 
1312ccf5a99SIgor M. Liplianin static inline int zl10039_readreg(struct zl10039_state *state,
1322ccf5a99SIgor M. Liplianin 				const enum zl10039_reg_addr reg, u8 *val)
1332ccf5a99SIgor M. Liplianin {
1342ccf5a99SIgor M. Liplianin 	return zl10039_read(state, reg, val, 1);
1352ccf5a99SIgor M. Liplianin }
1362ccf5a99SIgor M. Liplianin 
1372ccf5a99SIgor M. Liplianin static inline int zl10039_writereg(struct zl10039_state *state,
1382ccf5a99SIgor M. Liplianin 				const enum zl10039_reg_addr reg,
1392ccf5a99SIgor M. Liplianin 				const u8 val)
1402ccf5a99SIgor M. Liplianin {
1412ccf5a99SIgor M. Liplianin 	return zl10039_write(state, reg, &val, 1);
1422ccf5a99SIgor M. Liplianin }
1432ccf5a99SIgor M. Liplianin 
1442ccf5a99SIgor M. Liplianin static int zl10039_init(struct dvb_frontend *fe)
1452ccf5a99SIgor M. Liplianin {
1462ccf5a99SIgor M. Liplianin 	struct zl10039_state *state = fe->tuner_priv;
1472ccf5a99SIgor M. Liplianin 	int ret;
1482ccf5a99SIgor M. Liplianin 
1492ccf5a99SIgor M. Liplianin 	dprintk("%s\n", __func__);
1502ccf5a99SIgor M. Liplianin 	if (fe->ops.i2c_gate_ctrl)
1512ccf5a99SIgor M. Liplianin 		fe->ops.i2c_gate_ctrl(fe, 1);
1522ccf5a99SIgor M. Liplianin 	/* Reset logic */
1532ccf5a99SIgor M. Liplianin 	ret = zl10039_writereg(state, GENERAL, 0x40);
1542ccf5a99SIgor M. Liplianin 	if (ret < 0) {
1554bd69e7bSMauro Carvalho Chehab 		dprintk("Note: i2c write error normal when resetting the tuner\n");
1562ccf5a99SIgor M. Liplianin 	}
1572ccf5a99SIgor M. Liplianin 	/* Wake up */
1582ccf5a99SIgor M. Liplianin 	ret = zl10039_writereg(state, GENERAL, 0x01);
1592ccf5a99SIgor M. Liplianin 	if (ret < 0) {
1602ccf5a99SIgor M. Liplianin 		dprintk("Tuner power up failed\n");
1612ccf5a99SIgor M. Liplianin 		return ret;
1622ccf5a99SIgor M. Liplianin 	}
1632ccf5a99SIgor M. Liplianin 	if (fe->ops.i2c_gate_ctrl)
1642ccf5a99SIgor M. Liplianin 		fe->ops.i2c_gate_ctrl(fe, 0);
1652ccf5a99SIgor M. Liplianin 
1662ccf5a99SIgor M. Liplianin 	return 0;
1672ccf5a99SIgor M. Liplianin }
1682ccf5a99SIgor M. Liplianin 
1692ccf5a99SIgor M. Liplianin static int zl10039_sleep(struct dvb_frontend *fe)
1702ccf5a99SIgor M. Liplianin {
1712ccf5a99SIgor M. Liplianin 	struct zl10039_state *state = fe->tuner_priv;
1722ccf5a99SIgor M. Liplianin 	int ret;
1732ccf5a99SIgor M. Liplianin 
1742ccf5a99SIgor M. Liplianin 	dprintk("%s\n", __func__);
1752ccf5a99SIgor M. Liplianin 	if (fe->ops.i2c_gate_ctrl)
1762ccf5a99SIgor M. Liplianin 		fe->ops.i2c_gate_ctrl(fe, 1);
1772ccf5a99SIgor M. Liplianin 	ret = zl10039_writereg(state, GENERAL, 0x80);
1782ccf5a99SIgor M. Liplianin 	if (ret < 0) {
1792ccf5a99SIgor M. Liplianin 		dprintk("Tuner sleep failed\n");
1802ccf5a99SIgor M. Liplianin 		return ret;
1812ccf5a99SIgor M. Liplianin 	}
1822ccf5a99SIgor M. Liplianin 	if (fe->ops.i2c_gate_ctrl)
1832ccf5a99SIgor M. Liplianin 		fe->ops.i2c_gate_ctrl(fe, 0);
1842ccf5a99SIgor M. Liplianin 
1852ccf5a99SIgor M. Liplianin 	return 0;
1862ccf5a99SIgor M. Liplianin }
1872ccf5a99SIgor M. Liplianin 
18814d24d14SMauro Carvalho Chehab static int zl10039_set_params(struct dvb_frontend *fe)
1892ccf5a99SIgor M. Liplianin {
1908579881bSMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
1912ccf5a99SIgor M. Liplianin 	struct zl10039_state *state = fe->tuner_priv;
1922ccf5a99SIgor M. Liplianin 	u8 buf[6];
1932ccf5a99SIgor M. Liplianin 	u8 bf;
1942ccf5a99SIgor M. Liplianin 	u32 fbw;
1952ccf5a99SIgor M. Liplianin 	u32 div;
1962ccf5a99SIgor M. Liplianin 	int ret;
1972ccf5a99SIgor M. Liplianin 
1982ccf5a99SIgor M. Liplianin 	dprintk("%s\n", __func__);
1992ccf5a99SIgor M. Liplianin 	dprintk("Set frequency = %d, symbol rate = %d\n",
2008579881bSMauro Carvalho Chehab 			c->frequency, c->symbol_rate);
2012ccf5a99SIgor M. Liplianin 
2022ccf5a99SIgor M. Liplianin 	/* Assumed 10.111 MHz crystal oscillator */
2032ccf5a99SIgor M. Liplianin 	/* Cancelled num/den 80 to prevent overflow */
2048579881bSMauro Carvalho Chehab 	div = (c->frequency * 1000) / 126387;
2058579881bSMauro Carvalho Chehab 	fbw = (c->symbol_rate * 27) / 32000;
2062ccf5a99SIgor M. Liplianin 	/* Cancelled num/den 10 to prevent overflow */
2072ccf5a99SIgor M. Liplianin 	bf = ((fbw * 5088) / 1011100) - 1;
2082ccf5a99SIgor M. Liplianin 
2092ccf5a99SIgor M. Liplianin 	/*PLL divider*/
2102ccf5a99SIgor M. Liplianin 	buf[0] = (div >> 8) & 0x7f;
2112ccf5a99SIgor M. Liplianin 	buf[1] = (div >> 0) & 0xff;
2122ccf5a99SIgor M. Liplianin 	/*Reference divider*/
2132ccf5a99SIgor M. Liplianin 	/* Select reference ratio of 80 */
2142ccf5a99SIgor M. Liplianin 	buf[2] = 0x1D;
2152ccf5a99SIgor M. Liplianin 	/*PLL test modes*/
2162ccf5a99SIgor M. Liplianin 	buf[3] = 0x40;
2172ccf5a99SIgor M. Liplianin 	/*RF Control register*/
2182ccf5a99SIgor M. Liplianin 	buf[4] = 0x6E; /* Bypass enable */
2192ccf5a99SIgor M. Liplianin 	/*Baseband filter cutoff */
2202ccf5a99SIgor M. Liplianin 	buf[5] = bf;
2212ccf5a99SIgor M. Liplianin 
2222ccf5a99SIgor M. Liplianin 	/* Open i2c gate */
2232ccf5a99SIgor M. Liplianin 	if (fe->ops.i2c_gate_ctrl)
2242ccf5a99SIgor M. Liplianin 		fe->ops.i2c_gate_ctrl(fe, 1);
2252ccf5a99SIgor M. Liplianin 	/* BR = 10, Enable filter adjustment */
2262ccf5a99SIgor M. Liplianin 	ret = zl10039_writereg(state, BASE1, 0x0A);
2272ccf5a99SIgor M. Liplianin 	if (ret < 0)
2282ccf5a99SIgor M. Liplianin 		goto error;
2292ccf5a99SIgor M. Liplianin 	/* Write new config values */
2302ccf5a99SIgor M. Liplianin 	ret = zl10039_write(state, PLL0, buf, sizeof(buf));
2312ccf5a99SIgor M. Liplianin 	if (ret < 0)
2322ccf5a99SIgor M. Liplianin 		goto error;
2332ccf5a99SIgor M. Liplianin 	/* BR = 10, Disable filter adjustment */
2342ccf5a99SIgor M. Liplianin 	ret = zl10039_writereg(state, BASE1, 0x6A);
2352ccf5a99SIgor M. Liplianin 	if (ret < 0)
2362ccf5a99SIgor M. Liplianin 		goto error;
2372ccf5a99SIgor M. Liplianin 
2382ccf5a99SIgor M. Liplianin 	/* Close i2c gate */
2392ccf5a99SIgor M. Liplianin 	if (fe->ops.i2c_gate_ctrl)
2402ccf5a99SIgor M. Liplianin 		fe->ops.i2c_gate_ctrl(fe, 0);
2412ccf5a99SIgor M. Liplianin 	return 0;
2422ccf5a99SIgor M. Liplianin error:
2432ccf5a99SIgor M. Liplianin 	dprintk("Error setting tuner\n");
2442ccf5a99SIgor M. Liplianin 	return ret;
2452ccf5a99SIgor M. Liplianin }
2462ccf5a99SIgor M. Liplianin 
247*194ced7aSMax Kellermann static void zl10039_release(struct dvb_frontend *fe)
2482ccf5a99SIgor M. Liplianin {
2492ccf5a99SIgor M. Liplianin 	struct zl10039_state *state = fe->tuner_priv;
2502ccf5a99SIgor M. Liplianin 
2512ccf5a99SIgor M. Liplianin 	dprintk("%s\n", __func__);
2522ccf5a99SIgor M. Liplianin 	kfree(state);
2532ccf5a99SIgor M. Liplianin 	fe->tuner_priv = NULL;
2542ccf5a99SIgor M. Liplianin }
2552ccf5a99SIgor M. Liplianin 
25614c4bf3cSJulia Lawall static const struct dvb_tuner_ops zl10039_ops = {
2572ccf5a99SIgor M. Liplianin 	.release = zl10039_release,
2582ccf5a99SIgor M. Liplianin 	.init = zl10039_init,
2592ccf5a99SIgor M. Liplianin 	.sleep = zl10039_sleep,
2602ccf5a99SIgor M. Liplianin 	.set_params = zl10039_set_params,
2612ccf5a99SIgor M. Liplianin };
2622ccf5a99SIgor M. Liplianin 
2632ccf5a99SIgor M. Liplianin struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe,
2642ccf5a99SIgor M. Liplianin 		u8 i2c_addr, struct i2c_adapter *i2c)
2652ccf5a99SIgor M. Liplianin {
2662ccf5a99SIgor M. Liplianin 	struct zl10039_state *state = NULL;
2672ccf5a99SIgor M. Liplianin 
2682ccf5a99SIgor M. Liplianin 	dprintk("%s\n", __func__);
2692ccf5a99SIgor M. Liplianin 	state = kmalloc(sizeof(struct zl10039_state), GFP_KERNEL);
2702ccf5a99SIgor M. Liplianin 	if (state == NULL)
2712ccf5a99SIgor M. Liplianin 		goto error;
2722ccf5a99SIgor M. Liplianin 
2732ccf5a99SIgor M. Liplianin 	state->i2c = i2c;
2742ccf5a99SIgor M. Liplianin 	state->i2c_addr = i2c_addr;
2752ccf5a99SIgor M. Liplianin 
2762ccf5a99SIgor M. Liplianin 	/* Open i2c gate */
2772ccf5a99SIgor M. Liplianin 	if (fe->ops.i2c_gate_ctrl)
2782ccf5a99SIgor M. Liplianin 		fe->ops.i2c_gate_ctrl(fe, 1);
2792ccf5a99SIgor M. Liplianin 	/* check if this is a valid tuner */
2802ccf5a99SIgor M. Liplianin 	if (zl10039_readreg(state, GENERAL, &state->id) < 0) {
2812ccf5a99SIgor M. Liplianin 		/* Close i2c gate */
2822ccf5a99SIgor M. Liplianin 		if (fe->ops.i2c_gate_ctrl)
2832ccf5a99SIgor M. Liplianin 			fe->ops.i2c_gate_ctrl(fe, 0);
2842ccf5a99SIgor M. Liplianin 		goto error;
2852ccf5a99SIgor M. Liplianin 	}
2862ccf5a99SIgor M. Liplianin 	/* Close i2c gate */
2872ccf5a99SIgor M. Liplianin 	if (fe->ops.i2c_gate_ctrl)
2882ccf5a99SIgor M. Liplianin 		fe->ops.i2c_gate_ctrl(fe, 0);
2892ccf5a99SIgor M. Liplianin 
2902ccf5a99SIgor M. Liplianin 	state->id = state->id & 0x0f;
2912ccf5a99SIgor M. Liplianin 	switch (state->id) {
2922ccf5a99SIgor M. Liplianin 	case ID_ZL10039:
2932ccf5a99SIgor M. Liplianin 		strcpy(fe->ops.tuner_ops.info.name,
2942ccf5a99SIgor M. Liplianin 			"Zarlink ZL10039 DVB-S tuner");
2952ccf5a99SIgor M. Liplianin 		break;
2962ccf5a99SIgor M. Liplianin 	default:
2972ccf5a99SIgor M. Liplianin 		dprintk("Chip ID=%x does not match a known type\n", state->id);
2982ccf5a99SIgor M. Liplianin 		goto error;
2992ccf5a99SIgor M. Liplianin 	}
3002ccf5a99SIgor M. Liplianin 
3012ccf5a99SIgor M. Liplianin 	memcpy(&fe->ops.tuner_ops, &zl10039_ops, sizeof(struct dvb_tuner_ops));
3022ccf5a99SIgor M. Liplianin 	fe->tuner_priv = state;
3032ccf5a99SIgor M. Liplianin 	dprintk("Tuner attached @ i2c address 0x%02x\n", i2c_addr);
3042ccf5a99SIgor M. Liplianin 	return fe;
3052ccf5a99SIgor M. Liplianin error:
3062ccf5a99SIgor M. Liplianin 	kfree(state);
3072ccf5a99SIgor M. Liplianin 	return NULL;
3082ccf5a99SIgor M. Liplianin }
3092ccf5a99SIgor M. Liplianin EXPORT_SYMBOL(zl10039_attach);
3102ccf5a99SIgor M. Liplianin 
3112ccf5a99SIgor M. Liplianin module_param(debug, int, 0644);
3122ccf5a99SIgor M. Liplianin MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
3132ccf5a99SIgor M. Liplianin MODULE_DESCRIPTION("Zarlink ZL10039 DVB-S tuner driver");
3142ccf5a99SIgor M. Liplianin MODULE_AUTHOR("Jan D. Louw <jd.louw@mweb.co.za>");
3152ccf5a99SIgor M. Liplianin MODULE_LICENSE("GPL");
316