1af901ca181d92aac3a7dc265144a9081a86d8f39André Goddard Rosa/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    cx24110 - Single Chip Satellite Channel Receiver driver module
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4a8d995c99ef56a3dbcdbe291bb71658bf00e9ad6Johannes Stezenbach    Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de> based on
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    work
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    This program is free software; you can redistribute it and/or modify
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    it under the terms of the GNU General Public License as published by
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    the Free Software Foundation; either version 2 of the License, or
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    (at your option) any later version.
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    This program is distributed in the hope that it will be useful,
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    but WITHOUT ANY WARRANTY; without even the implied warranty of
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    GNU General Public License for more details.
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    You should have received a copy of the GNU General Public License
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    along with this program; if not, write to the Free Software
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "dvb_frontend.h"
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "cx24110.h"
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct cx24110_state {
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct i2c_adapter* i2c;
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	const struct cx24110_config* config;
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dvb_frontend frontend;
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 lastber;
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 lastbler;
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 lastesn0;
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int debug;
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define dprintk(args...) \
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do { \
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (debug) printk(KERN_DEBUG "cx24110: " args); \
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} while (0)
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct {u8 reg; u8 data;} cx24110_regdata[]=
549101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		      /* Comments beginning with @ denote this value should
5550c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			 be the default */
569101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	{{0x09,0x01}, /* SoftResetAll */
579101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x09,0x00}, /* release reset */
589101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x01,0xe8}, /* MSB of code rate 27.5MS/s */
599101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x02,0x17}, /* middle byte " */
609101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x03,0x29}, /* LSB         " */
619101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x05,0x03}, /* @ DVB mode, standard code rate 3/4 */
629101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x06,0xa5}, /* @ PLL 60MHz */
639101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x07,0x01}, /* @ Fclk, i.e. sampling clock, 60MHz */
649101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x0a,0x00}, /* @ partial chip disables, do not set */
659101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x0b,0x01}, /* set output clock in gapped mode, start signal low
6650c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			 active for first byte */
679101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x0c,0x11}, /* no parity bytes, large hold time, serial data out */
689101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x0d,0x6f}, /* @ RS Sync/Unsync thresholds */
699101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x10,0x40}, /* chip doc is misleading here: write bit 6 as 1
7050c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			 to avoid starting the BER counter. Reset the
7150c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			 CRC test bit. Finite counting selected */
729101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x15,0xff}, /* @ size of the limited time window for RS BER
7350c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			 estimation. It is <value>*256 RS blocks, this
7450c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			 gives approx. 2.6 sec at 27.5MS/s, rate 3/4 */
759101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x16,0x00}, /* @ enable all RS output ports */
769101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x17,0x04}, /* @ time window allowed for the RS to sync */
779101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x18,0xae}, /* @ allow all standard DVB code rates to be scanned
7850c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			 for automatically */
799101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		      /* leave the current code rate and normalization
8050c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			 registers as they are after reset... */
819101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x21,0x10}, /* @ during AutoAcq, search each viterbi setting
8250c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			 only once */
839101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x23,0x18}, /* @ size of the limited time window for Viterbi BER
8450c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			 estimation. It is <value>*65536 channel bits, i.e.
8550c25fff5385c6baf3114f7c369b0f75a29ac1e8Michael Krufky			 approx. 38ms at 27.5MS/s, rate 3/4 */
869101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x24,0x24}, /* do not trigger Viterbi CRC test. Finite count window */
879101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		      /* leave front-end AGC parameters at default values */
889101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		      /* leave decimation AGC parameters at default values */
899101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x35,0x40}, /* disable all interrupts. They are not connected anyway */
909101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x36,0xff}, /* clear all interrupt pending flags */
919101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x37,0x00}, /* @ fully enable AutoAcqq state machine */
929101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x38,0x07}, /* @ enable fade recovery, but not autostart AutoAcq */
939101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		      /* leave the equalizer parameters on their default values */
949101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		      /* leave the final AGC parameters on their default values */
959101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x41,0x00}, /* @ MSB of front-end derotator frequency */
969101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x42,0x00}, /* @ middle bytes " */
979101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x43,0x00}, /* @ LSB          " */
989101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		      /* leave the carrier tracking loop parameters on default */
99af901ca181d92aac3a7dc265144a9081a86d8f39André Goddard Rosa		      /* leave the bit timing loop parameters at default */
1009101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x56,0x4d}, /* set the filtune voltage to 2.7V, as recommended by */
1019101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		      /* the cx24108 data sheet for symbol rates above 15MS/s */
1029101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x57,0x00}, /* @ Filter sigma delta enabled, positive */
1039101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x61,0x95}, /* GPIO pins 1-4 have special function */
1049101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x62,0x05}, /* GPIO pin 5 has special function, pin 6 is GPIO */
1059101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x63,0x00}, /* All GPIO pins use CMOS output characteristics */
1069101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x64,0x20}, /* GPIO 6 is input, all others are outputs */
1079101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x6d,0x30}, /* tuner auto mode clock freq 62kHz */
1089101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x70,0x15}, /* use auto mode, tuner word is 21 bits long */
1099101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x73,0x00}, /* @ disable several demod bypasses */
1109101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x74,0x00}, /* @  " */
1119101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	 {0x75,0x00}  /* @  " */
1129101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		      /* the remaining registers are for SEC */
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	};
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cx24110_writereg (struct cx24110_state* state, int reg, int data)
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1189101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	u8 buf [] = { reg, data };
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1229101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dprintk ("%s: writereg error (err == %i, reg == 0x%02x,"
124271ddbf702c3a4e6b18f6464180eda0f62efd9a5Harvey Harrison			 " data == 0x%02x)\n", __func__, err, reg, data);
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EREMOTEIO;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1289101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	return 0;
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cx24110_readreg (struct cx24110_state* state, u8 reg)
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 b0 [] = { reg };
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 b1 [] = { 0 };
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = i2c_transfer(state->i2c, msg, 2);
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret != 2) return ret;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return b1[0];
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cx24110_set_inversion (struct cx24110_state* state, fe_spectral_inversion_t inversion)
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* fixme (low): error handling */
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (inversion) {
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case INVERSION_OFF:
1529101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1);
1539101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		/* AcqSpectrInvDis on. No idea why someone should want this */
1549101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)&0xf7);
1559101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		/* Initial value 0 at start of acq */
1569101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)&0xef);
1579101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		/* current value 0 */
1589101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		/* The cx24110 manual tells us this reg is read-only.
1599101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		   But what the heck... set it ayways */
1609101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		break;
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case INVERSION_ON:
1629101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1);
1639101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		/* AcqSpectrInvDis on. No idea why someone should want this */
1649101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)|0x08);
1659101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		/* Initial value 1 at start of acq */
1669101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)|0x10);
1679101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		/* current value 1 */
1689101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		break;
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case INVERSION_AUTO:
1709101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xfe);
1719101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		/* AcqSpectrInvDis off. Leave initial & current states as is */
1729101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		break;
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cx24110_set_fec (struct cx24110_state* state, fe_code_rate_t fec)
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* fixme (low): error handling */
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1849101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	static const int rate[]={-1,1,2,3,5,7,-1};
1859101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	static const int g1[]={-1,0x01,0x02,0x05,0x15,0x45,-1};
1869101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	static const int g2[]={-1,0x01,0x03,0x06,0x1a,0x7a,-1};
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1889101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	/* Well, the AutoAcq engine of the cx24106 and 24110 automatically
1899101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	   searches all enabled viterbi rates, and can handle non-standard
1909101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	   rates as well. */
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1929101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	if (fec>FEC_AUTO)
1939101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		fec=FEC_AUTO;
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1959101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	if (fec==FEC_AUTO) { /* (re-)establish AutoAcq behaviour */
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xdf);
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* clear AcqVitDis bit */
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x18,0xae);
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* allow all DVB standard code rates */
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|0x3);
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* set nominal Viterbi rate 3/4 */
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|0x3);
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* set current Viterbi rate 3/4 */
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x1a,0x05); cx24110_writereg(state,0x1b,0x06);
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* set the puncture registers for code rate 3/4 */
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
2079101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	} else {
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x20);
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* set AcqVitDis bit */
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if(rate[fec]>0) {
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|rate[fec]);
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* set nominal Viterbi rate */
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|rate[fec]);
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* set current Viterbi rate */
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			cx24110_writereg(state,0x1a,g1[fec]);
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			cx24110_writereg(state,0x1b,g2[fec]);
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* not sure if this is the right way: I always used AutoAcq mode */
2189101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	   } else
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   return -EOPNOTSUPP;
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* fixme (low): which is the correct return code? */
2219101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	};
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic fe_code_rate_t cx24110_get_fec (struct cx24110_state* state)
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i=cx24110_readreg(state,0x22)&0x0f;
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if(!(i&0x08)) {
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return FEC_1_2 + i - 1;
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* fixme (low): a special code rate has been selected. In theory, we need to
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   return a denominator value, a numerator value, and a pair of puncture
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   maps to correctly describe this mode. But this should never happen in
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   practice, because it cannot be set by cx24110_get_fec. */
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   return FEC_NONE;
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate)
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* fixme (low): add error handling */
2449101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	u32 ratio;
2459101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	u32 tmp, fclk, BDRI;
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2479101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	static const u32 bands[]={5000000UL,15000000UL,90999000UL/2};
2489101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	int i;
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
250271ddbf702c3a4e6b18f6464180eda0f62efd9a5Harvey Harrison	dprintk("cx24110 debug: entering %s(%d)\n",__func__,srate);
2519101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	if (srate>90999000UL/2)
2529101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		srate=90999000UL/2;
2539101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	if (srate<500000)
2549101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab		srate=500000;
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2560496daa7d88d117fab4dd190c7f6e7c4a5aa15cdAhmed S. Darwish	for(i = 0; (i < ARRAY_SIZE(bands)) && (srate>bands[i]); i++)
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		;
2589101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	/* first, check which sample rate is appropriate: 45, 60 80 or 90 MHz,
2599101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	   and set the PLL accordingly (R07[1:0] Fclk, R06[7:4] PLLmult,
2609101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	   R06[3:0] PLLphaseDetGain */
2619101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	tmp=cx24110_readreg(state,0x07)&0xfc;
2629101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	if(srate<90999000UL/4) { /* sample rate 45MHz*/
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x07,tmp);
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x06,0x78);
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fclk=90999000UL/2;
2669101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	} else if(srate<60666000UL/2) { /* sample rate 60MHz */
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x07,tmp|0x1);
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x06,0xa5);
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fclk=60666000UL;
2709101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	} else if(srate<80888000UL/2) { /* sample rate 80MHz */
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x07,tmp|0x2);
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x06,0x87);
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fclk=80888000UL;
2749101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	} else { /* sample rate 90MHz */
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x07,tmp|0x3);
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x06,0x78);
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fclk=90999000UL;
2789101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	};
2799101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	dprintk("cx24110 debug: fclk %d Hz\n",fclk);
2809101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	/* we need to divide two integers with approx. 27 bits in 32 bit
2819101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	   arithmetic giving a 25 bit result */
2829101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	/* the maximum dividend is 90999000/2, 0x02b6446c, this number is
2839101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	   also the most complex divisor. Hence, the dividend has,
2849101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	   assuming 32bit unsigned arithmetic, 6 clear bits on top, the
2859101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	   divisor 2 unused bits at the bottom. Also, the quotient is
2869101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	   always less than 1/2. Borrowed from VES1893.c, of course */
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2889101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	tmp=srate<<6;
2899101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	BDRI=fclk>>2;
2909101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	ratio=(tmp/BDRI);
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2929101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	tmp=(tmp%BDRI)<<8;
2939101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	ratio=(ratio<<8)+(tmp/BDRI);
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2959101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	tmp=(tmp%BDRI)<<8;
2969101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	ratio=(ratio<<8)+(tmp/BDRI);
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2989101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	tmp=(tmp%BDRI)<<1;
2999101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	ratio=(ratio<<1)+(tmp/BDRI);
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3019101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	dprintk("srate= %d (range %d, up to %d)\n", srate,i,bands[i]);
3029101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	dprintk("fclk = %d\n", fclk);
3039101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	dprintk("ratio= %08x\n", ratio);
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3059101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	cx24110_writereg(state, 0x1, (ratio>>16)&0xff);
3069101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	cx24110_writereg(state, 0x2, (ratio>>8)&0xff);
3079101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	cx24110_writereg(state, 0x3, (ratio)&0xff);
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3099101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	return 0;
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3132e4e98e788d8fbe30892bee3375067a4937155dalawrence ruststatic int _cx24110_pll_write (struct dvb_frontend* fe, const u8 buf[], int len)
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
315b8742700f13163ffa00cddce2a3c940b9ab2ab5aJohannes Stezenbach	struct cx24110_state *state = fe->demodulator_priv;
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
317c10d14d62d7b7596fd5c7bb8aad3f2b56f8640e6Andrew de Quincey	if (len != 3)
318c10d14d62d7b7596fd5c7bb8aad3f2b56f8640e6Andrew de Quincey		return -EINVAL;
319c10d14d62d7b7596fd5c7bb8aad3f2b56f8640e6Andrew de Quincey
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* tuner data is 21 bits long, must be left-aligned in data */
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* tuner cx24108 is written through a dedicated 3wire interface on the demod chip */
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* FIXME (low): add error handling, avoid infinite loops if HW fails... */
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3249101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	cx24110_writereg(state,0x6d,0x30); /* auto mode at 62kHz */
3259101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	cx24110_writereg(state,0x70,0x15); /* auto mode 21 bits */
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3279101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	/* if the auto tuner writer is still busy, clear it out */
3289101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	while (cx24110_readreg(state,0x6d)&0x80)
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x72,0);
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3319101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	/* write the topmost 8 bits */
332c10d14d62d7b7596fd5c7bb8aad3f2b56f8640e6Andrew de Quincey	cx24110_writereg(state,0x72,buf[0]);
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3349101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	/* wait for the send to be completed */
3359101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		;
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3389101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	/* send another 8 bytes */
339c10d14d62d7b7596fd5c7bb8aad3f2b56f8640e6Andrew de Quincey	cx24110_writereg(state,0x72,buf[1]);
3409101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		;
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3439101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	/* and the topmost 5 bits of this byte */
344c10d14d62d7b7596fd5c7bb8aad3f2b56f8640e6Andrew de Quincey	cx24110_writereg(state,0x72,buf[2]);
3459101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		;
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3489101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	/* now strobe the enable line once */
3499101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	cx24110_writereg(state,0x6d,0x32);
3509101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	cx24110_writereg(state,0x6d,0x30);
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3529101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	return 0;
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cx24110_initfe(struct dvb_frontend* fe)
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
357b8742700f13163ffa00cddce2a3c940b9ab2ab5aJohannes Stezenbach	struct cx24110_state *state = fe->demodulator_priv;
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* fixme (low): error handling */
3599101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	int i;
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
361271ddbf702c3a4e6b18f6464180eda0f62efd9a5Harvey Harrison	dprintk("%s: init chip\n", __func__);
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3630496daa7d88d117fab4dd190c7f6e7c4a5aa15cdAhmed S. Darwish	for(i = 0; i < ARRAY_SIZE(cx24110_regdata); i++) {
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state, cx24110_regdata[i].reg, cx24110_regdata[i].data);
3659101e6222cb115240e24160bb90cce425bb74de5Mauro Carvalho Chehab	};
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
367e7ac46469c247a931f760354deaed9cf10b75fdeManu Abraham	return 0;
368e7ac46469c247a931f760354deaed9cf10b75fdeManu Abraham}
369e7ac46469c247a931f760354deaed9cf10b75fdeManu Abraham
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cx24110_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
372b8742700f13163ffa00cddce2a3c940b9ab2ab5aJohannes Stezenbach	struct cx24110_state *state = fe->demodulator_priv;
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (voltage) {
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SEC_VOLTAGE_13:
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0xc0);
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case SEC_VOLTAGE_18:
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0x40);
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	};
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
384b8742700f13163ffa00cddce2a3c940b9ab2ab5aJohannes Stezenbachstatic int cx24110_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
386c589ebfce79834a9617c44d7ec0f608fa70eb42dJohannes Stezenbach	int rv, bit;
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct cx24110_state *state = fe->demodulator_priv;
388c589ebfce79834a9617c44d7ec0f608fa70eb42dJohannes Stezenbach	unsigned long timeout;
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (burst == SEC_MINI_A)
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		bit = 0x00;
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (burst == SEC_MINI_B)
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		bit = 0x08;
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rv = cx24110_readreg(state, 0x77);
398296c786a0d2122b1e47c80ca717d8a8ac36402c1Adam Szalkowski	if (!(rv & 0x04))
399296c786a0d2122b1e47c80ca717d8a8ac36402c1Adam Szalkowski		cx24110_writereg(state, 0x77, rv | 0x04);
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rv = cx24110_readreg(state, 0x76);
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40 | bit));
403c589ebfce79834a9617c44d7ec0f608fa70eb42dJohannes Stezenbach	timeout = jiffies + msecs_to_jiffies(100);
404c589ebfce79834a9617c44d7ec0f608fa70eb42dJohannes Stezenbach	while (!time_after(jiffies, timeout) && !(cx24110_readreg(state, 0x76) & 0x40))
405c589ebfce79834a9617c44d7ec0f608fa70eb42dJohannes Stezenbach		; /* wait for LNB ready */
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cx24110_send_diseqc_msg(struct dvb_frontend* fe,
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   struct dvb_diseqc_master_cmd *cmd)
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, rv;
414b8742700f13163ffa00cddce2a3c940b9ab2ab5aJohannes Stezenbach	struct cx24110_state *state = fe->demodulator_priv;
415c589ebfce79834a9617c44d7ec0f608fa70eb42dJohannes Stezenbach	unsigned long timeout;
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4171e7eb89ba936fc1db54e247a336f3f55bdbc644dManu Abraham	if (cmd->msg_len < 3 || cmd->msg_len > 6)
4181e7eb89ba936fc1db54e247a336f3f55bdbc644dManu Abraham		return -EINVAL;  /* not implemented */
4191e7eb89ba936fc1db54e247a336f3f55bdbc644dManu Abraham
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < cmd->msg_len; i++)
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state, 0x79 + i, cmd->msg[i]);
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rv = cx24110_readreg(state, 0x77);
424296c786a0d2122b1e47c80ca717d8a8ac36402c1Adam Szalkowski	if (rv & 0x04) {
425296c786a0d2122b1e47c80ca717d8a8ac36402c1Adam Szalkowski		cx24110_writereg(state, 0x77, rv & ~0x04);
426296c786a0d2122b1e47c80ca717d8a8ac36402c1Adam Szalkowski		msleep(30); /* reportedly fixes switching problems */
427296c786a0d2122b1e47c80ca717d8a8ac36402c1Adam Szalkowski	}
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rv = cx24110_readreg(state, 0x76);
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40) | ((cmd->msg_len-3) & 3));
432c589ebfce79834a9617c44d7ec0f608fa70eb42dJohannes Stezenbach	timeout = jiffies + msecs_to_jiffies(100);
433c589ebfce79834a9617c44d7ec0f608fa70eb42dJohannes Stezenbach	while (!time_after(jiffies, timeout) && !(cx24110_readreg(state, 0x76) & 0x40))
434c589ebfce79834a9617c44d7ec0f608fa70eb42dJohannes Stezenbach		; /* wait for LNB ready */
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cx24110_read_status(struct dvb_frontend* fe, fe_status_t* status)
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
441b8742700f13163ffa00cddce2a3c940b9ab2ab5aJohannes Stezenbach	struct cx24110_state *state = fe->demodulator_priv;
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int sync = cx24110_readreg (state, 0x55);
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*status = 0;
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sync & 0x10)
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*status |= FE_HAS_SIGNAL;
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sync & 0x08)
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*status |= FE_HAS_CARRIER;
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sync = cx24110_readreg (state, 0x08);
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sync & 0x40)
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*status |= FE_HAS_VITERBI;
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sync & 0x20)
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*status |= FE_HAS_SYNC;
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((sync & 0x60) == 0x60)
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*status |= FE_HAS_LOCK;
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cx24110_read_ber(struct dvb_frontend* fe, u32* ber)
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
469b8742700f13163ffa00cddce2a3c940b9ab2ab5aJohannes Stezenbach	struct cx24110_state *state = fe->demodulator_priv;
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* fixme (maybe): value range is 16 bit. Scale? */
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if(cx24110_readreg(state,0x24)&0x10) {
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* the Viterbi error counter has finished one counting window */
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x24,0x04); /* select the ber reg */
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		state->lastber=cx24110_readreg(state,0x25)|
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			(cx24110_readreg(state,0x26)<<8);
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x24,0x04); /* start new count window */
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x24,0x14);
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*ber = state->lastber;
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cx24110_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
487b8742700f13163ffa00cddce2a3c940b9ab2ab5aJohannes Stezenbach	struct cx24110_state *state = fe->demodulator_priv;
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* no provision in hardware. Read the frontend AGC accumulator. No idea how to scale this, but I know it is 2s complement */
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 signal = cx24110_readreg (state, 0x27)+128;
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*signal_strength = (signal << 8) | signal;
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cx24110_read_snr(struct dvb_frontend* fe, u16* snr)
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
498b8742700f13163ffa00cddce2a3c940b9ab2ab5aJohannes Stezenbach	struct cx24110_state *state = fe->demodulator_priv;
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* no provision in hardware. Can be computed from the Es/N0 estimator, but I don't know how. */
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if(cx24110_readreg(state,0x6a)&0x80) {
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* the Es/N0 error counter has finished one counting window */
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		state->lastesn0=cx24110_readreg(state,0x69)|
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			(cx24110_readreg(state,0x68)<<8);
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x6a,0x84); /* start new count window */
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*snr = state->lastesn0;
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cx24110_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
514b8742700f13163ffa00cddce2a3c940b9ab2ab5aJohannes Stezenbach	struct cx24110_state *state = fe->demodulator_priv;
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 lastbyer;
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if(cx24110_readreg(state,0x10)&0x40) {
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* the RS error counter has finished one counting window */
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x10,0x60); /* select the byer reg */
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		lastbyer=cx24110_readreg(state,0x12)|
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			(cx24110_readreg(state,0x13)<<8)|
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			(cx24110_readreg(state,0x14)<<16);
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x10,0x70); /* select the bler reg */
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		state->lastbler=cx24110_readreg(state,0x12)|
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			(cx24110_readreg(state,0x13)<<8)|
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			(cx24110_readreg(state,0x14)<<16);
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cx24110_writereg(state,0x10,0x20); /* start new count window */
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*ucblocks = state->lastbler;
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5344be325c9668f6af89baa52e092e054632658e850Mauro Carvalho Chehabstatic int cx24110_set_frontend(struct dvb_frontend *fe)
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
536b8742700f13163ffa00cddce2a3c940b9ab2ab5aJohannes Stezenbach	struct cx24110_state *state = fe->demodulator_priv;
5374be325c9668f6af89baa52e092e054632658e850Mauro Carvalho Chehab	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
53881d8a8da8233eb63f783eb3241805ab629b15907Andrew de Quincey
539dea74869f3c62b0b7addd67017b22b394e942aacPatrick Boettcher	if (fe->ops.tuner_ops.set_params) {
54014d24d148c7521b2b88b396652e36f55d061e195Mauro Carvalho Chehab		fe->ops.tuner_ops.set_params(fe);
541dea74869f3c62b0b7addd67017b22b394e942aacPatrick Boettcher		if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
54281d8a8da8233eb63f783eb3241805ab629b15907Andrew de Quincey	}
54381d8a8da8233eb63f783eb3241805ab629b15907Andrew de Quincey
5444be325c9668f6af89baa52e092e054632658e850Mauro Carvalho Chehab	cx24110_set_inversion(state, p->inversion);
5454be325c9668f6af89baa52e092e054632658e850Mauro Carvalho Chehab	cx24110_set_fec(state, p->fec_inner);
5464be325c9668f6af89baa52e092e054632658e850Mauro Carvalho Chehab	cx24110_set_symbolrate(state, p->symbol_rate);
54725985edcedea6396277003854657b5f3cb31a628Lucas De Marchi	cx24110_writereg(state,0x04,0x05); /* start acquisition */
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5527c61d80a9bcfc3fdec8ffd75756cad6a64678229Mauro Carvalho Chehabstatic int cx24110_get_frontend(struct dvb_frontend *fe)
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5547c61d80a9bcfc3fdec8ffd75756cad6a64678229Mauro Carvalho Chehab	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
555b8742700f13163ffa00cddce2a3c940b9ab2ab5aJohannes Stezenbach	struct cx24110_state *state = fe->demodulator_priv;
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	s32 afc; unsigned sclk;
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* cannot read back tuner settings (freq). Need to have some private storage */
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sclk = cx24110_readreg (state, 0x07) & 0x03;
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* ok, real AFC (FEDR) freq. is afc/2^24*fsamp, fsamp=45/60/80/90MHz.
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Need 64 bit arithmetic. Is thiss possible in the kernel? */
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sclk==0) sclk=90999000L/2L;
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (sclk==1) sclk=60666000L;
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else if (sclk==2) sclk=80888000L;
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else sclk=90999000L;
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sclk>>=8;
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	afc = sclk*(cx24110_readreg (state, 0x44)&0x1f)+
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	      ((sclk*cx24110_readreg (state, 0x45))>>8)+
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	      ((sclk*cx24110_readreg (state, 0x46))>>16);
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	p->frequency += afc;
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	p->inversion = (cx24110_readreg (state, 0x22) & 0x10) ?
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				INVERSION_ON : INVERSION_OFF;
5754be325c9668f6af89baa52e092e054632658e850Mauro Carvalho Chehab	p->fec_inner = cx24110_get_fec(state);
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cx24110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
582b8742700f13163ffa00cddce2a3c940b9ab2ab5aJohannes Stezenbach	struct cx24110_state *state = fe->demodulator_priv;
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&~0x10)|(((tone==SEC_TONE_ON))?0x10:0));
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void cx24110_release(struct dvb_frontend* fe)
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
589b8742700f13163ffa00cddce2a3c940b9ab2ab5aJohannes Stezenbach	struct cx24110_state* state = fe->demodulator_priv;
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(state);
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct dvb_frontend_ops cx24110_ops;
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct dvb_frontend* cx24110_attach(const struct cx24110_config* config,
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				    struct i2c_adapter* i2c)
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct cx24110_state* state = NULL;
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* allocate memory for the internal state */
602084e24acc906c162c92de7df807190856ae60928Matthias Schwarzott	state = kzalloc(sizeof(struct cx24110_state), GFP_KERNEL);
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (state == NULL) goto error;
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* setup the state */
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	state->config = config;
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	state->i2c = i2c;
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	state->lastber = 0;
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	state->lastbler = 0;
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	state->lastesn0 = 0;
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* check if the demod is there */
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = cx24110_readreg(state, 0x00);
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((ret != 0x5a) && (ret != 0x69)) goto error;
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* create dvb_frontend */
617dea74869f3c62b0b7addd67017b22b394e942aacPatrick Boettcher	memcpy(&state->frontend.ops, &cx24110_ops, sizeof(struct dvb_frontend_ops));
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	state->frontend.demodulator_priv = state;
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return &state->frontend;
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserror:
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(state);
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return NULL;
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct dvb_frontend_ops cx24110_ops = {
6274be325c9668f6af89baa52e092e054632658e850Mauro Carvalho Chehab	.delsys = { SYS_DVBS },
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.info = {
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.name = "Conexant CX24110 DVB-S",
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.frequency_min = 950000,
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.frequency_max = 2150000,
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.frequency_stepsize = 1011,  /* kHz for QPSK frontends */
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.frequency_tolerance = 29500,
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.symbol_rate_min = 1000000,
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.symbol_rate_max = 45000000,
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.caps = FE_CAN_INVERSION_AUTO |
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			FE_CAN_QPSK | FE_CAN_RECOVER
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.release = cx24110_release,
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.init = cx24110_initfe,
645c10d14d62d7b7596fd5c7bb8aad3f2b56f8640e6Andrew de Quincey	.write = _cx24110_pll_write,
6464be325c9668f6af89baa52e092e054632658e850Mauro Carvalho Chehab	.set_frontend = cx24110_set_frontend,
6474be325c9668f6af89baa52e092e054632658e850Mauro Carvalho Chehab	.get_frontend = cx24110_get_frontend,
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read_status = cx24110_read_status,
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read_ber = cx24110_read_ber,
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read_signal_strength = cx24110_read_signal_strength,
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read_snr = cx24110_read_snr,
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read_ucblocks = cx24110_read_ucblocks,
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.diseqc_send_master_cmd = cx24110_send_diseqc_msg,
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_tone = cx24110_set_tone,
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_voltage = cx24110_set_voltage,
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.diseqc_send_burst = cx24110_diseqc_send_burst,
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(debug, int, 0644);
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("Conexant CX24110 DVB-S Demodulator driver");
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Peter Hettkamp");
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(cx24110_attach);
668