xref: /openbmc/linux/drivers/media/dvb-frontends/or51211.c (revision 870f31cbf03e0c759900eea2feb276b2fe6558f4)
19a0bf528SMauro Carvalho Chehab /*
29a0bf528SMauro Carvalho Chehab  *    Support for OR51211 (pcHDTV HD-2000) - VSB
39a0bf528SMauro Carvalho Chehab  *
49a0bf528SMauro Carvalho Chehab  *    Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com>
59a0bf528SMauro Carvalho Chehab  *
69a0bf528SMauro Carvalho Chehab  *    Based on code from Jack Kelliher (kelliher@xmission.com)
79a0bf528SMauro Carvalho Chehab  *                           Copyright (C) 2002 & pcHDTV, inc.
89a0bf528SMauro Carvalho Chehab  *
99a0bf528SMauro Carvalho Chehab  *    This program is free software; you can redistribute it and/or modify
109a0bf528SMauro Carvalho Chehab  *    it under the terms of the GNU General Public License as published by
119a0bf528SMauro Carvalho Chehab  *    the Free Software Foundation; either version 2 of the License, or
129a0bf528SMauro Carvalho Chehab  *    (at your option) any later version.
139a0bf528SMauro Carvalho Chehab  *
149a0bf528SMauro Carvalho Chehab  *    This program is distributed in the hope that it will be useful,
159a0bf528SMauro Carvalho Chehab  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
169a0bf528SMauro Carvalho Chehab  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
179a0bf528SMauro Carvalho Chehab  *    GNU General Public License for more details.
189a0bf528SMauro Carvalho Chehab  *
199a0bf528SMauro Carvalho Chehab  *    You should have received a copy of the GNU General Public License
209a0bf528SMauro Carvalho Chehab  *    along with this program; if not, write to the Free Software
219a0bf528SMauro Carvalho Chehab  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
229a0bf528SMauro Carvalho Chehab  *
239a0bf528SMauro Carvalho Chehab */
249a0bf528SMauro Carvalho Chehab 
259a0bf528SMauro Carvalho Chehab /*
269a0bf528SMauro Carvalho Chehab  * This driver needs external firmware. Please use the command
279a0bf528SMauro Carvalho Chehab  * "<kerneldir>/Documentation/dvb/get_dvb_firmware or51211" to
289a0bf528SMauro Carvalho Chehab  * download/extract it, and then copy it to /usr/lib/hotplug/firmware
299a0bf528SMauro Carvalho Chehab  * or /lib/firmware (depending on configuration of firmware hotplug).
309a0bf528SMauro Carvalho Chehab  */
319a0bf528SMauro Carvalho Chehab #define OR51211_DEFAULT_FIRMWARE "dvb-fe-or51211.fw"
329a0bf528SMauro Carvalho Chehab 
339a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
349a0bf528SMauro Carvalho Chehab #include <linux/module.h>
359a0bf528SMauro Carvalho Chehab #include <linux/device.h>
369a0bf528SMauro Carvalho Chehab #include <linux/firmware.h>
379a0bf528SMauro Carvalho Chehab #include <linux/string.h>
389a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
399a0bf528SMauro Carvalho Chehab #include <asm/byteorder.h>
409a0bf528SMauro Carvalho Chehab 
419a0bf528SMauro Carvalho Chehab #include "dvb_math.h"
429a0bf528SMauro Carvalho Chehab #include "dvb_frontend.h"
439a0bf528SMauro Carvalho Chehab #include "or51211.h"
449a0bf528SMauro Carvalho Chehab 
459a0bf528SMauro Carvalho Chehab static int debug;
469a0bf528SMauro Carvalho Chehab #define dprintk(args...) \
479a0bf528SMauro Carvalho Chehab 	do { \
489a0bf528SMauro Carvalho Chehab 		if (debug) printk(KERN_DEBUG "or51211: " args); \
499a0bf528SMauro Carvalho Chehab 	} while (0)
509a0bf528SMauro Carvalho Chehab 
519a0bf528SMauro Carvalho Chehab static u8 run_buf[] = {0x7f,0x01};
529a0bf528SMauro Carvalho Chehab static u8 cmd_buf[] = {0x04,0x01,0x50,0x80,0x06}; // ATSC
539a0bf528SMauro Carvalho Chehab 
549a0bf528SMauro Carvalho Chehab struct or51211_state {
559a0bf528SMauro Carvalho Chehab 
569a0bf528SMauro Carvalho Chehab 	struct i2c_adapter* i2c;
579a0bf528SMauro Carvalho Chehab 
589a0bf528SMauro Carvalho Chehab 	/* Configuration settings */
599a0bf528SMauro Carvalho Chehab 	const struct or51211_config* config;
609a0bf528SMauro Carvalho Chehab 
619a0bf528SMauro Carvalho Chehab 	struct dvb_frontend frontend;
629a0bf528SMauro Carvalho Chehab 	struct bt878* bt;
639a0bf528SMauro Carvalho Chehab 
649a0bf528SMauro Carvalho Chehab 	/* Demodulator private data */
659a0bf528SMauro Carvalho Chehab 	u8 initialized:1;
669a0bf528SMauro Carvalho Chehab 	u32 snr; /* Result of last SNR claculation */
679a0bf528SMauro Carvalho Chehab 
689a0bf528SMauro Carvalho Chehab 	/* Tuner private data */
699a0bf528SMauro Carvalho Chehab 	u32 current_frequency;
709a0bf528SMauro Carvalho Chehab };
719a0bf528SMauro Carvalho Chehab 
729a0bf528SMauro Carvalho Chehab static int i2c_writebytes (struct or51211_state* state, u8 reg, const u8 *buf,
739a0bf528SMauro Carvalho Chehab 			   int len)
749a0bf528SMauro Carvalho Chehab {
759a0bf528SMauro Carvalho Chehab 	int err;
769a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg;
779a0bf528SMauro Carvalho Chehab 	msg.addr	= reg;
789a0bf528SMauro Carvalho Chehab 	msg.flags	= 0;
799a0bf528SMauro Carvalho Chehab 	msg.len		= len;
809a0bf528SMauro Carvalho Chehab 	msg.buf		= (u8 *)buf;
819a0bf528SMauro Carvalho Chehab 
829a0bf528SMauro Carvalho Chehab 	if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
839a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "or51211: i2c_writebytes error "
849a0bf528SMauro Carvalho Chehab 		       "(addr %02x, err == %i)\n", reg, err);
859a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
869a0bf528SMauro Carvalho Chehab 	}
879a0bf528SMauro Carvalho Chehab 
889a0bf528SMauro Carvalho Chehab 	return 0;
899a0bf528SMauro Carvalho Chehab }
909a0bf528SMauro Carvalho Chehab 
919a0bf528SMauro Carvalho Chehab static int i2c_readbytes(struct or51211_state *state, u8 reg, u8 *buf, int len)
929a0bf528SMauro Carvalho Chehab {
939a0bf528SMauro Carvalho Chehab 	int err;
949a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg;
959a0bf528SMauro Carvalho Chehab 	msg.addr	= reg;
969a0bf528SMauro Carvalho Chehab 	msg.flags	= I2C_M_RD;
979a0bf528SMauro Carvalho Chehab 	msg.len		= len;
989a0bf528SMauro Carvalho Chehab 	msg.buf		= buf;
999a0bf528SMauro Carvalho Chehab 
1009a0bf528SMauro Carvalho Chehab 	if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
1019a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "or51211: i2c_readbytes error "
1029a0bf528SMauro Carvalho Chehab 		       "(addr %02x, err == %i)\n", reg, err);
1039a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
1049a0bf528SMauro Carvalho Chehab 	}
1059a0bf528SMauro Carvalho Chehab 
1069a0bf528SMauro Carvalho Chehab 	return 0;
1079a0bf528SMauro Carvalho Chehab }
1089a0bf528SMauro Carvalho Chehab 
1099a0bf528SMauro Carvalho Chehab static int or51211_load_firmware (struct dvb_frontend* fe,
1109a0bf528SMauro Carvalho Chehab 				  const struct firmware *fw)
1119a0bf528SMauro Carvalho Chehab {
1129a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = fe->demodulator_priv;
1139a0bf528SMauro Carvalho Chehab 	u8 tudata[585];
1149a0bf528SMauro Carvalho Chehab 	int i;
1159a0bf528SMauro Carvalho Chehab 
1169a0bf528SMauro Carvalho Chehab 	dprintk("Firmware is %zd bytes\n",fw->size);
1179a0bf528SMauro Carvalho Chehab 
1189a0bf528SMauro Carvalho Chehab 	/* Get eprom data */
1199a0bf528SMauro Carvalho Chehab 	tudata[0] = 17;
1209a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,0x50,tudata,1)) {
1219a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "or51211:load_firmware error eprom addr\n");
1229a0bf528SMauro Carvalho Chehab 		return -1;
1239a0bf528SMauro Carvalho Chehab 	}
1249a0bf528SMauro Carvalho Chehab 	if (i2c_readbytes(state,0x50,&tudata[145],192)) {
1259a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "or51211: load_firmware error eprom\n");
1269a0bf528SMauro Carvalho Chehab 		return -1;
1279a0bf528SMauro Carvalho Chehab 	}
1289a0bf528SMauro Carvalho Chehab 
1299a0bf528SMauro Carvalho Chehab 	/* Create firmware buffer */
1309a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 145; i++)
1319a0bf528SMauro Carvalho Chehab 		tudata[i] = fw->data[i];
1329a0bf528SMauro Carvalho Chehab 
1339a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 248; i++)
1349a0bf528SMauro Carvalho Chehab 		tudata[i+337] = fw->data[145+i];
1359a0bf528SMauro Carvalho Chehab 
1369a0bf528SMauro Carvalho Chehab 	state->config->reset(fe);
1379a0bf528SMauro Carvalho Chehab 
1389a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,tudata,585)) {
1399a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "or51211: load_firmware error 1\n");
1409a0bf528SMauro Carvalho Chehab 		return -1;
1419a0bf528SMauro Carvalho Chehab 	}
1429a0bf528SMauro Carvalho Chehab 	msleep(1);
1439a0bf528SMauro Carvalho Chehab 
1449a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,
1459a0bf528SMauro Carvalho Chehab 			   &fw->data[393],8125)) {
1469a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "or51211: load_firmware error 2\n");
1479a0bf528SMauro Carvalho Chehab 		return -1;
1489a0bf528SMauro Carvalho Chehab 	}
1499a0bf528SMauro Carvalho Chehab 	msleep(1);
1509a0bf528SMauro Carvalho Chehab 
1519a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
1529a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "or51211: load_firmware error 3\n");
1539a0bf528SMauro Carvalho Chehab 		return -1;
1549a0bf528SMauro Carvalho Chehab 	}
1559a0bf528SMauro Carvalho Chehab 
1569a0bf528SMauro Carvalho Chehab 	/* Wait at least 5 msec */
1579a0bf528SMauro Carvalho Chehab 	msleep(10);
1589a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
1599a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "or51211: load_firmware error 4\n");
1609a0bf528SMauro Carvalho Chehab 		return -1;
1619a0bf528SMauro Carvalho Chehab 	}
1629a0bf528SMauro Carvalho Chehab 	msleep(10);
1639a0bf528SMauro Carvalho Chehab 
1649a0bf528SMauro Carvalho Chehab 	printk("or51211: Done.\n");
1659a0bf528SMauro Carvalho Chehab 	return 0;
1669a0bf528SMauro Carvalho Chehab };
1679a0bf528SMauro Carvalho Chehab 
1689a0bf528SMauro Carvalho Chehab static int or51211_setmode(struct dvb_frontend* fe, int mode)
1699a0bf528SMauro Carvalho Chehab {
1709a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = fe->demodulator_priv;
1719a0bf528SMauro Carvalho Chehab 	u8 rec_buf[14];
1729a0bf528SMauro Carvalho Chehab 
1739a0bf528SMauro Carvalho Chehab 	state->config->setmode(fe, mode);
1749a0bf528SMauro Carvalho Chehab 
1759a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
1769a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "or51211: setmode error 1\n");
1779a0bf528SMauro Carvalho Chehab 		return -1;
1789a0bf528SMauro Carvalho Chehab 	}
1799a0bf528SMauro Carvalho Chehab 
1809a0bf528SMauro Carvalho Chehab 	/* Wait at least 5 msec */
1819a0bf528SMauro Carvalho Chehab 	msleep(10);
1829a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
1839a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "or51211: setmode error 2\n");
1849a0bf528SMauro Carvalho Chehab 		return -1;
1859a0bf528SMauro Carvalho Chehab 	}
1869a0bf528SMauro Carvalho Chehab 
1879a0bf528SMauro Carvalho Chehab 	msleep(10);
1889a0bf528SMauro Carvalho Chehab 
1899a0bf528SMauro Carvalho Chehab 	/* Set operation mode in Receiver 1 register;
1909a0bf528SMauro Carvalho Chehab 	 * type 1:
1919a0bf528SMauro Carvalho Chehab 	 * data 0x50h  Automatic sets receiver channel conditions
1929a0bf528SMauro Carvalho Chehab 	 *             Automatic NTSC rejection filter
1939a0bf528SMauro Carvalho Chehab 	 *             Enable  MPEG serial data output
1949a0bf528SMauro Carvalho Chehab 	 *             MPEG2tr
1959a0bf528SMauro Carvalho Chehab 	 *             High tuner phase noise
1969a0bf528SMauro Carvalho Chehab 	 *             normal +/-150kHz Carrier acquisition range
1979a0bf528SMauro Carvalho Chehab 	 */
1989a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,cmd_buf,3)) {
1999a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "or51211: setmode error 3\n");
2009a0bf528SMauro Carvalho Chehab 		return -1;
2019a0bf528SMauro Carvalho Chehab 	}
2029a0bf528SMauro Carvalho Chehab 
2039a0bf528SMauro Carvalho Chehab 	rec_buf[0] = 0x04;
2049a0bf528SMauro Carvalho Chehab 	rec_buf[1] = 0x00;
2059a0bf528SMauro Carvalho Chehab 	rec_buf[2] = 0x03;
2069a0bf528SMauro Carvalho Chehab 	rec_buf[3] = 0x00;
2079a0bf528SMauro Carvalho Chehab 	msleep(20);
2089a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,rec_buf,3)) {
2099a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "or51211: setmode error 5\n");
2109a0bf528SMauro Carvalho Chehab 	}
2119a0bf528SMauro Carvalho Chehab 	msleep(3);
2129a0bf528SMauro Carvalho Chehab 	if (i2c_readbytes(state,state->config->demod_address,&rec_buf[10],2)) {
2139a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "or51211: setmode error 6");
2149a0bf528SMauro Carvalho Chehab 		return -1;
2159a0bf528SMauro Carvalho Chehab 	}
2169a0bf528SMauro Carvalho Chehab 	dprintk("setmode rec status %02x %02x\n",rec_buf[10],rec_buf[11]);
2179a0bf528SMauro Carvalho Chehab 
2189a0bf528SMauro Carvalho Chehab 	return 0;
2199a0bf528SMauro Carvalho Chehab }
2209a0bf528SMauro Carvalho Chehab 
2219a0bf528SMauro Carvalho Chehab static int or51211_set_parameters(struct dvb_frontend *fe)
2229a0bf528SMauro Carvalho Chehab {
2239a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
2249a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = fe->demodulator_priv;
2259a0bf528SMauro Carvalho Chehab 
2269a0bf528SMauro Carvalho Chehab 	/* Change only if we are actually changing the channel */
2279a0bf528SMauro Carvalho Chehab 	if (state->current_frequency != p->frequency) {
2289a0bf528SMauro Carvalho Chehab 		if (fe->ops.tuner_ops.set_params) {
2299a0bf528SMauro Carvalho Chehab 			fe->ops.tuner_ops.set_params(fe);
2309a0bf528SMauro Carvalho Chehab 			if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
2319a0bf528SMauro Carvalho Chehab 		}
2329a0bf528SMauro Carvalho Chehab 
2339a0bf528SMauro Carvalho Chehab 		/* Set to ATSC mode */
2349a0bf528SMauro Carvalho Chehab 		or51211_setmode(fe,0);
2359a0bf528SMauro Carvalho Chehab 
2369a0bf528SMauro Carvalho Chehab 		/* Update current frequency */
2379a0bf528SMauro Carvalho Chehab 		state->current_frequency = p->frequency;
2389a0bf528SMauro Carvalho Chehab 	}
2399a0bf528SMauro Carvalho Chehab 	return 0;
2409a0bf528SMauro Carvalho Chehab }
2419a0bf528SMauro Carvalho Chehab 
2429a0bf528SMauro Carvalho Chehab static int or51211_read_status(struct dvb_frontend* fe, fe_status_t* status)
2439a0bf528SMauro Carvalho Chehab {
2449a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = fe->demodulator_priv;
2459a0bf528SMauro Carvalho Chehab 	unsigned char rec_buf[2];
2469a0bf528SMauro Carvalho Chehab 	unsigned char snd_buf[] = {0x04,0x00,0x03,0x00};
2479a0bf528SMauro Carvalho Chehab 	*status = 0;
2489a0bf528SMauro Carvalho Chehab 
2499a0bf528SMauro Carvalho Chehab 	/* Receiver Status */
2509a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
2519a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "or51132: read_status write error\n");
2529a0bf528SMauro Carvalho Chehab 		return -1;
2539a0bf528SMauro Carvalho Chehab 	}
2549a0bf528SMauro Carvalho Chehab 	msleep(3);
2559a0bf528SMauro Carvalho Chehab 	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
2569a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "or51132: read_status read error\n");
2579a0bf528SMauro Carvalho Chehab 		return -1;
2589a0bf528SMauro Carvalho Chehab 	}
2599a0bf528SMauro Carvalho Chehab 	dprintk("read_status %x %x\n",rec_buf[0],rec_buf[1]);
2609a0bf528SMauro Carvalho Chehab 
2619a0bf528SMauro Carvalho Chehab 	if (rec_buf[0] &  0x01) { /* Receiver Lock */
2629a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SIGNAL;
2639a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_CARRIER;
2649a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_VITERBI;
2659a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SYNC;
2669a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_LOCK;
2679a0bf528SMauro Carvalho Chehab 	}
2689a0bf528SMauro Carvalho Chehab 	return 0;
2699a0bf528SMauro Carvalho Chehab }
2709a0bf528SMauro Carvalho Chehab 
2719a0bf528SMauro Carvalho Chehab /* Calculate SNR estimation (scaled by 2^24)
2729a0bf528SMauro Carvalho Chehab 
2739a0bf528SMauro Carvalho Chehab    8-VSB SNR equation from Oren datasheets
2749a0bf528SMauro Carvalho Chehab 
2759a0bf528SMauro Carvalho Chehab    For 8-VSB:
2769a0bf528SMauro Carvalho Chehab      SNR[dB] = 10 * log10(219037.9454 / MSE^2 )
2779a0bf528SMauro Carvalho Chehab 
2789a0bf528SMauro Carvalho Chehab    We re-write the snr equation as:
2799a0bf528SMauro Carvalho Chehab      SNR * 2^24 = 10*(c - 2*intlog10(MSE))
2809a0bf528SMauro Carvalho Chehab    Where for 8-VSB, c = log10(219037.9454) * 2^24 */
2819a0bf528SMauro Carvalho Chehab 
2829a0bf528SMauro Carvalho Chehab static u32 calculate_snr(u32 mse, u32 c)
2839a0bf528SMauro Carvalho Chehab {
2849a0bf528SMauro Carvalho Chehab 	if (mse == 0) /* No signal */
2859a0bf528SMauro Carvalho Chehab 		return 0;
2869a0bf528SMauro Carvalho Chehab 
2879a0bf528SMauro Carvalho Chehab 	mse = 2*intlog10(mse);
2889a0bf528SMauro Carvalho Chehab 	if (mse > c) {
2899a0bf528SMauro Carvalho Chehab 		/* Negative SNR, which is possible, but realisticly the
2909a0bf528SMauro Carvalho Chehab 		demod will lose lock before the signal gets this bad.  The
2919a0bf528SMauro Carvalho Chehab 		API only allows for unsigned values, so just return 0 */
2929a0bf528SMauro Carvalho Chehab 		return 0;
2939a0bf528SMauro Carvalho Chehab 	}
2949a0bf528SMauro Carvalho Chehab 	return 10*(c - mse);
2959a0bf528SMauro Carvalho Chehab }
2969a0bf528SMauro Carvalho Chehab 
2979a0bf528SMauro Carvalho Chehab static int or51211_read_snr(struct dvb_frontend* fe, u16* snr)
2989a0bf528SMauro Carvalho Chehab {
2999a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = fe->demodulator_priv;
3009a0bf528SMauro Carvalho Chehab 	u8 rec_buf[2];
3019a0bf528SMauro Carvalho Chehab 	u8 snd_buf[3];
3029a0bf528SMauro Carvalho Chehab 
3039a0bf528SMauro Carvalho Chehab 	/* SNR after Equalizer */
3049a0bf528SMauro Carvalho Chehab 	snd_buf[0] = 0x04;
3059a0bf528SMauro Carvalho Chehab 	snd_buf[1] = 0x00;
3069a0bf528SMauro Carvalho Chehab 	snd_buf[2] = 0x04;
3079a0bf528SMauro Carvalho Chehab 
3089a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
3099a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "%s: error writing snr reg\n",
3109a0bf528SMauro Carvalho Chehab 		       __func__);
3119a0bf528SMauro Carvalho Chehab 		return -1;
3129a0bf528SMauro Carvalho Chehab 	}
3139a0bf528SMauro Carvalho Chehab 	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
3149a0bf528SMauro Carvalho Chehab 		printk(KERN_WARNING "%s: read_status read error\n",
3159a0bf528SMauro Carvalho Chehab 		       __func__);
3169a0bf528SMauro Carvalho Chehab 		return -1;
3179a0bf528SMauro Carvalho Chehab 	}
3189a0bf528SMauro Carvalho Chehab 
3199a0bf528SMauro Carvalho Chehab 	state->snr = calculate_snr(rec_buf[0], 89599047);
3209a0bf528SMauro Carvalho Chehab 	*snr = (state->snr) >> 16;
3219a0bf528SMauro Carvalho Chehab 
3229a0bf528SMauro Carvalho Chehab 	dprintk("%s: noise = 0x%02x, snr = %d.%02d dB\n", __func__, rec_buf[0],
3239a0bf528SMauro Carvalho Chehab 		state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16);
3249a0bf528SMauro Carvalho Chehab 
3259a0bf528SMauro Carvalho Chehab 	return 0;
3269a0bf528SMauro Carvalho Chehab }
3279a0bf528SMauro Carvalho Chehab 
3289a0bf528SMauro Carvalho Chehab static int or51211_read_signal_strength(struct dvb_frontend* fe, u16* strength)
3299a0bf528SMauro Carvalho Chehab {
3309a0bf528SMauro Carvalho Chehab 	/* Calculate Strength from SNR up to 35dB */
3319a0bf528SMauro Carvalho Chehab 	/* Even though the SNR can go higher than 35dB, there is some comfort */
3329a0bf528SMauro Carvalho Chehab 	/* factor in having a range of strong signals that can show at 100%   */
3339a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = (struct or51211_state*)fe->demodulator_priv;
3349a0bf528SMauro Carvalho Chehab 	u16 snr;
3359a0bf528SMauro Carvalho Chehab 	int ret;
3369a0bf528SMauro Carvalho Chehab 
3379a0bf528SMauro Carvalho Chehab 	ret = fe->ops.read_snr(fe, &snr);
3389a0bf528SMauro Carvalho Chehab 	if (ret != 0)
3399a0bf528SMauro Carvalho Chehab 		return ret;
3409a0bf528SMauro Carvalho Chehab 	/* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
3419a0bf528SMauro Carvalho Chehab 	/* scale the range 0 - 35*2^24 into 0 - 65535 */
3429a0bf528SMauro Carvalho Chehab 	if (state->snr >= 8960 * 0x10000)
3439a0bf528SMauro Carvalho Chehab 		*strength = 0xffff;
3449a0bf528SMauro Carvalho Chehab 	else
3459a0bf528SMauro Carvalho Chehab 		*strength = state->snr / 8960;
3469a0bf528SMauro Carvalho Chehab 
3479a0bf528SMauro Carvalho Chehab 	return 0;
3489a0bf528SMauro Carvalho Chehab }
3499a0bf528SMauro Carvalho Chehab 
3509a0bf528SMauro Carvalho Chehab static int or51211_read_ber(struct dvb_frontend* fe, u32* ber)
3519a0bf528SMauro Carvalho Chehab {
3529a0bf528SMauro Carvalho Chehab 	*ber = -ENOSYS;
3539a0bf528SMauro Carvalho Chehab 	return 0;
3549a0bf528SMauro Carvalho Chehab }
3559a0bf528SMauro Carvalho Chehab 
3569a0bf528SMauro Carvalho Chehab static int or51211_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
3579a0bf528SMauro Carvalho Chehab {
3589a0bf528SMauro Carvalho Chehab 	*ucblocks = -ENOSYS;
3599a0bf528SMauro Carvalho Chehab 	return 0;
3609a0bf528SMauro Carvalho Chehab }
3619a0bf528SMauro Carvalho Chehab 
3629a0bf528SMauro Carvalho Chehab static int or51211_sleep(struct dvb_frontend* fe)
3639a0bf528SMauro Carvalho Chehab {
3649a0bf528SMauro Carvalho Chehab 	return 0;
3659a0bf528SMauro Carvalho Chehab }
3669a0bf528SMauro Carvalho Chehab 
3679a0bf528SMauro Carvalho Chehab static int or51211_init(struct dvb_frontend* fe)
3689a0bf528SMauro Carvalho Chehab {
3699a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = fe->demodulator_priv;
3709a0bf528SMauro Carvalho Chehab 	const struct or51211_config* config = state->config;
3719a0bf528SMauro Carvalho Chehab 	const struct firmware* fw;
3729a0bf528SMauro Carvalho Chehab 	unsigned char get_ver_buf[] = {0x04,0x00,0x30,0x00,0x00};
3739a0bf528SMauro Carvalho Chehab 	unsigned char rec_buf[14];
3749a0bf528SMauro Carvalho Chehab 	int ret,i;
3759a0bf528SMauro Carvalho Chehab 
3769a0bf528SMauro Carvalho Chehab 	if (!state->initialized) {
3779a0bf528SMauro Carvalho Chehab 		/* Request the firmware, this will block until it uploads */
3789a0bf528SMauro Carvalho Chehab 		printk(KERN_INFO "or51211: Waiting for firmware upload "
3799a0bf528SMauro Carvalho Chehab 		       "(%s)...\n", OR51211_DEFAULT_FIRMWARE);
3809a0bf528SMauro Carvalho Chehab 		ret = config->request_firmware(fe, &fw,
3819a0bf528SMauro Carvalho Chehab 					       OR51211_DEFAULT_FIRMWARE);
3829a0bf528SMauro Carvalho Chehab 		printk(KERN_INFO "or51211:Got Hotplug firmware\n");
3839a0bf528SMauro Carvalho Chehab 		if (ret) {
3849a0bf528SMauro Carvalho Chehab 			printk(KERN_WARNING "or51211: No firmware uploaded "
3859a0bf528SMauro Carvalho Chehab 			       "(timeout or file not found?)\n");
3869a0bf528SMauro Carvalho Chehab 			return ret;
3879a0bf528SMauro Carvalho Chehab 		}
3889a0bf528SMauro Carvalho Chehab 
3899a0bf528SMauro Carvalho Chehab 		ret = or51211_load_firmware(fe, fw);
3909a0bf528SMauro Carvalho Chehab 		release_firmware(fw);
3919a0bf528SMauro Carvalho Chehab 		if (ret) {
3929a0bf528SMauro Carvalho Chehab 			printk(KERN_WARNING "or51211: Writing firmware to "
3939a0bf528SMauro Carvalho Chehab 			       "device failed!\n");
3949a0bf528SMauro Carvalho Chehab 			return ret;
3959a0bf528SMauro Carvalho Chehab 		}
3969a0bf528SMauro Carvalho Chehab 		printk(KERN_INFO "or51211: Firmware upload complete.\n");
3979a0bf528SMauro Carvalho Chehab 
3989a0bf528SMauro Carvalho Chehab 		/* Set operation mode in Receiver 1 register;
3999a0bf528SMauro Carvalho Chehab 		 * type 1:
4009a0bf528SMauro Carvalho Chehab 		 * data 0x50h  Automatic sets receiver channel conditions
4019a0bf528SMauro Carvalho Chehab 		 *             Automatic NTSC rejection filter
4029a0bf528SMauro Carvalho Chehab 		 *             Enable  MPEG serial data output
4039a0bf528SMauro Carvalho Chehab 		 *             MPEG2tr
4049a0bf528SMauro Carvalho Chehab 		 *             High tuner phase noise
4059a0bf528SMauro Carvalho Chehab 		 *             normal +/-150kHz Carrier acquisition range
4069a0bf528SMauro Carvalho Chehab 		 */
4079a0bf528SMauro Carvalho Chehab 		if (i2c_writebytes(state,state->config->demod_address,
4089a0bf528SMauro Carvalho Chehab 				   cmd_buf,3)) {
4099a0bf528SMauro Carvalho Chehab 			printk(KERN_WARNING "or51211: Load DVR Error 5\n");
4109a0bf528SMauro Carvalho Chehab 			return -1;
4119a0bf528SMauro Carvalho Chehab 		}
4129a0bf528SMauro Carvalho Chehab 
4139a0bf528SMauro Carvalho Chehab 		/* Read back ucode version to besure we loaded correctly */
4149a0bf528SMauro Carvalho Chehab 		/* and are really up and running */
4159a0bf528SMauro Carvalho Chehab 		rec_buf[0] = 0x04;
4169a0bf528SMauro Carvalho Chehab 		rec_buf[1] = 0x00;
4179a0bf528SMauro Carvalho Chehab 		rec_buf[2] = 0x03;
4189a0bf528SMauro Carvalho Chehab 		rec_buf[3] = 0x00;
4199a0bf528SMauro Carvalho Chehab 		msleep(30);
4209a0bf528SMauro Carvalho Chehab 		if (i2c_writebytes(state,state->config->demod_address,
4219a0bf528SMauro Carvalho Chehab 				   rec_buf,3)) {
4229a0bf528SMauro Carvalho Chehab 			printk(KERN_WARNING "or51211: Load DVR Error A\n");
4239a0bf528SMauro Carvalho Chehab 			return -1;
4249a0bf528SMauro Carvalho Chehab 		}
4259a0bf528SMauro Carvalho Chehab 		msleep(3);
4269a0bf528SMauro Carvalho Chehab 		if (i2c_readbytes(state,state->config->demod_address,
4279a0bf528SMauro Carvalho Chehab 				  &rec_buf[10],2)) {
4289a0bf528SMauro Carvalho Chehab 			printk(KERN_WARNING "or51211: Load DVR Error B\n");
4299a0bf528SMauro Carvalho Chehab 			return -1;
4309a0bf528SMauro Carvalho Chehab 		}
4319a0bf528SMauro Carvalho Chehab 
4329a0bf528SMauro Carvalho Chehab 		rec_buf[0] = 0x04;
4339a0bf528SMauro Carvalho Chehab 		rec_buf[1] = 0x00;
4349a0bf528SMauro Carvalho Chehab 		rec_buf[2] = 0x01;
4359a0bf528SMauro Carvalho Chehab 		rec_buf[3] = 0x00;
4369a0bf528SMauro Carvalho Chehab 		msleep(20);
4379a0bf528SMauro Carvalho Chehab 		if (i2c_writebytes(state,state->config->demod_address,
4389a0bf528SMauro Carvalho Chehab 				   rec_buf,3)) {
4399a0bf528SMauro Carvalho Chehab 			printk(KERN_WARNING "or51211: Load DVR Error C\n");
4409a0bf528SMauro Carvalho Chehab 			return -1;
4419a0bf528SMauro Carvalho Chehab 		}
4429a0bf528SMauro Carvalho Chehab 		msleep(3);
4439a0bf528SMauro Carvalho Chehab 		if (i2c_readbytes(state,state->config->demod_address,
4449a0bf528SMauro Carvalho Chehab 				  &rec_buf[12],2)) {
4459a0bf528SMauro Carvalho Chehab 			printk(KERN_WARNING "or51211: Load DVR Error D\n");
4469a0bf528SMauro Carvalho Chehab 			return -1;
4479a0bf528SMauro Carvalho Chehab 		}
4489a0bf528SMauro Carvalho Chehab 
4499a0bf528SMauro Carvalho Chehab 		for (i = 0; i < 8; i++)
4509a0bf528SMauro Carvalho Chehab 			rec_buf[i]=0xed;
4519a0bf528SMauro Carvalho Chehab 
4529a0bf528SMauro Carvalho Chehab 		for (i = 0; i < 5; i++) {
4539a0bf528SMauro Carvalho Chehab 			msleep(30);
4549a0bf528SMauro Carvalho Chehab 			get_ver_buf[4] = i+1;
4559a0bf528SMauro Carvalho Chehab 			if (i2c_writebytes(state,state->config->demod_address,
4569a0bf528SMauro Carvalho Chehab 					   get_ver_buf,5)) {
4579a0bf528SMauro Carvalho Chehab 				printk(KERN_WARNING "or51211:Load DVR Error 6"
4589a0bf528SMauro Carvalho Chehab 				       " - %d\n",i);
4599a0bf528SMauro Carvalho Chehab 				return -1;
4609a0bf528SMauro Carvalho Chehab 			}
4619a0bf528SMauro Carvalho Chehab 			msleep(3);
4629a0bf528SMauro Carvalho Chehab 
4639a0bf528SMauro Carvalho Chehab 			if (i2c_readbytes(state,state->config->demod_address,
4649a0bf528SMauro Carvalho Chehab 					  &rec_buf[i*2],2)) {
4659a0bf528SMauro Carvalho Chehab 				printk(KERN_WARNING "or51211:Load DVR Error 7"
4669a0bf528SMauro Carvalho Chehab 				       " - %d\n",i);
4679a0bf528SMauro Carvalho Chehab 				return -1;
4689a0bf528SMauro Carvalho Chehab 			}
4699a0bf528SMauro Carvalho Chehab 			/* If we didn't receive the right index, try again */
4709a0bf528SMauro Carvalho Chehab 			if ((int)rec_buf[i*2+1]!=i+1){
4719a0bf528SMauro Carvalho Chehab 			  i--;
4729a0bf528SMauro Carvalho Chehab 			}
4739a0bf528SMauro Carvalho Chehab 		}
474*870f31cbSAndy Shevchenko 		dprintk("read_fwbits %10ph\n", rec_buf);
4759a0bf528SMauro Carvalho Chehab 
4769a0bf528SMauro Carvalho Chehab 		printk(KERN_INFO "or51211: ver TU%02x%02x%02x VSB mode %02x"
4779a0bf528SMauro Carvalho Chehab 		       " Status %02x\n",
4789a0bf528SMauro Carvalho Chehab 		       rec_buf[2], rec_buf[4],rec_buf[6],
4799a0bf528SMauro Carvalho Chehab 		       rec_buf[12],rec_buf[10]);
4809a0bf528SMauro Carvalho Chehab 
4819a0bf528SMauro Carvalho Chehab 		rec_buf[0] = 0x04;
4829a0bf528SMauro Carvalho Chehab 		rec_buf[1] = 0x00;
4839a0bf528SMauro Carvalho Chehab 		rec_buf[2] = 0x03;
4849a0bf528SMauro Carvalho Chehab 		rec_buf[3] = 0x00;
4859a0bf528SMauro Carvalho Chehab 		msleep(20);
4869a0bf528SMauro Carvalho Chehab 		if (i2c_writebytes(state,state->config->demod_address,
4879a0bf528SMauro Carvalho Chehab 				   rec_buf,3)) {
4889a0bf528SMauro Carvalho Chehab 			printk(KERN_WARNING "or51211: Load DVR Error 8\n");
4899a0bf528SMauro Carvalho Chehab 			return -1;
4909a0bf528SMauro Carvalho Chehab 		}
4919a0bf528SMauro Carvalho Chehab 		msleep(20);
4929a0bf528SMauro Carvalho Chehab 		if (i2c_readbytes(state,state->config->demod_address,
4939a0bf528SMauro Carvalho Chehab 				  &rec_buf[8],2)) {
4949a0bf528SMauro Carvalho Chehab 			printk(KERN_WARNING "or51211: Load DVR Error 9\n");
4959a0bf528SMauro Carvalho Chehab 			return -1;
4969a0bf528SMauro Carvalho Chehab 		}
4979a0bf528SMauro Carvalho Chehab 		state->initialized = 1;
4989a0bf528SMauro Carvalho Chehab 	}
4999a0bf528SMauro Carvalho Chehab 
5009a0bf528SMauro Carvalho Chehab 	return 0;
5019a0bf528SMauro Carvalho Chehab }
5029a0bf528SMauro Carvalho Chehab 
5039a0bf528SMauro Carvalho Chehab static int or51211_get_tune_settings(struct dvb_frontend* fe,
5049a0bf528SMauro Carvalho Chehab 				     struct dvb_frontend_tune_settings* fesettings)
5059a0bf528SMauro Carvalho Chehab {
5069a0bf528SMauro Carvalho Chehab 	fesettings->min_delay_ms = 500;
5079a0bf528SMauro Carvalho Chehab 	fesettings->step_size = 0;
5089a0bf528SMauro Carvalho Chehab 	fesettings->max_drift = 0;
5099a0bf528SMauro Carvalho Chehab 	return 0;
5109a0bf528SMauro Carvalho Chehab }
5119a0bf528SMauro Carvalho Chehab 
5129a0bf528SMauro Carvalho Chehab static void or51211_release(struct dvb_frontend* fe)
5139a0bf528SMauro Carvalho Chehab {
5149a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = fe->demodulator_priv;
5159a0bf528SMauro Carvalho Chehab 	state->config->sleep(fe);
5169a0bf528SMauro Carvalho Chehab 	kfree(state);
5179a0bf528SMauro Carvalho Chehab }
5189a0bf528SMauro Carvalho Chehab 
5199a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops or51211_ops;
5209a0bf528SMauro Carvalho Chehab 
5219a0bf528SMauro Carvalho Chehab struct dvb_frontend* or51211_attach(const struct or51211_config* config,
5229a0bf528SMauro Carvalho Chehab 				    struct i2c_adapter* i2c)
5239a0bf528SMauro Carvalho Chehab {
5249a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = NULL;
5259a0bf528SMauro Carvalho Chehab 
5269a0bf528SMauro Carvalho Chehab 	/* Allocate memory for the internal state */
5279a0bf528SMauro Carvalho Chehab 	state = kzalloc(sizeof(struct or51211_state), GFP_KERNEL);
5289a0bf528SMauro Carvalho Chehab 	if (state == NULL)
5299a0bf528SMauro Carvalho Chehab 		return NULL;
5309a0bf528SMauro Carvalho Chehab 
5319a0bf528SMauro Carvalho Chehab 	/* Setup the state */
5329a0bf528SMauro Carvalho Chehab 	state->config = config;
5339a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
5349a0bf528SMauro Carvalho Chehab 	state->initialized = 0;
5359a0bf528SMauro Carvalho Chehab 	state->current_frequency = 0;
5369a0bf528SMauro Carvalho Chehab 
5379a0bf528SMauro Carvalho Chehab 	/* Create dvb_frontend */
5389a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &or51211_ops, sizeof(struct dvb_frontend_ops));
5399a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
5409a0bf528SMauro Carvalho Chehab 	return &state->frontend;
5419a0bf528SMauro Carvalho Chehab }
5429a0bf528SMauro Carvalho Chehab 
5439a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops or51211_ops = {
5449a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
5459a0bf528SMauro Carvalho Chehab 	.info = {
5469a0bf528SMauro Carvalho Chehab 		.name               = "Oren OR51211 VSB Frontend",
5479a0bf528SMauro Carvalho Chehab 		.frequency_min      = 44000000,
5489a0bf528SMauro Carvalho Chehab 		.frequency_max      = 958000000,
5499a0bf528SMauro Carvalho Chehab 		.frequency_stepsize = 166666,
5509a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
5519a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
5529a0bf528SMauro Carvalho Chehab 			FE_CAN_8VSB
5539a0bf528SMauro Carvalho Chehab 	},
5549a0bf528SMauro Carvalho Chehab 
5559a0bf528SMauro Carvalho Chehab 	.release = or51211_release,
5569a0bf528SMauro Carvalho Chehab 
5579a0bf528SMauro Carvalho Chehab 	.init = or51211_init,
5589a0bf528SMauro Carvalho Chehab 	.sleep = or51211_sleep,
5599a0bf528SMauro Carvalho Chehab 
5609a0bf528SMauro Carvalho Chehab 	.set_frontend = or51211_set_parameters,
5619a0bf528SMauro Carvalho Chehab 	.get_tune_settings = or51211_get_tune_settings,
5629a0bf528SMauro Carvalho Chehab 
5639a0bf528SMauro Carvalho Chehab 	.read_status = or51211_read_status,
5649a0bf528SMauro Carvalho Chehab 	.read_ber = or51211_read_ber,
5659a0bf528SMauro Carvalho Chehab 	.read_signal_strength = or51211_read_signal_strength,
5669a0bf528SMauro Carvalho Chehab 	.read_snr = or51211_read_snr,
5679a0bf528SMauro Carvalho Chehab 	.read_ucblocks = or51211_read_ucblocks,
5689a0bf528SMauro Carvalho Chehab };
5699a0bf528SMauro Carvalho Chehab 
5709a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
5719a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
5729a0bf528SMauro Carvalho Chehab 
5739a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("Oren OR51211 VSB [pcHDTV HD-2000] Demodulator Driver");
5749a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Kirk Lapray");
5759a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
5769a0bf528SMauro Carvalho Chehab 
5779a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(or51211_attach);
5789a0bf528SMauro Carvalho Chehab 
579