xref: /openbmc/linux/drivers/media/dvb-frontends/or51211.c (revision 868c9a17e22c89d241f12501e21ae4f104ce036c)
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 */
209a0bf528SMauro Carvalho Chehab 
21bb9e31f3SAndy Shevchenko #define pr_fmt(fmt)	KBUILD_MODNAME ": %s: " fmt, __func__
22bb9e31f3SAndy Shevchenko 
239a0bf528SMauro Carvalho Chehab /*
249a0bf528SMauro Carvalho Chehab  * This driver needs external firmware. Please use the command
25fe63a1a6SMauro Carvalho Chehab  * "<kerneldir>/scripts/get_dvb_firmware or51211" to
269a0bf528SMauro Carvalho Chehab  * download/extract it, and then copy it to /usr/lib/hotplug/firmware
279a0bf528SMauro Carvalho Chehab  * or /lib/firmware (depending on configuration of firmware hotplug).
289a0bf528SMauro Carvalho Chehab  */
299a0bf528SMauro Carvalho Chehab #define OR51211_DEFAULT_FIRMWARE "dvb-fe-or51211.fw"
309a0bf528SMauro Carvalho Chehab 
319a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
329a0bf528SMauro Carvalho Chehab #include <linux/module.h>
339a0bf528SMauro Carvalho Chehab #include <linux/device.h>
349a0bf528SMauro Carvalho Chehab #include <linux/firmware.h>
359a0bf528SMauro Carvalho Chehab #include <linux/string.h>
369a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
379a0bf528SMauro Carvalho Chehab #include <asm/byteorder.h>
389a0bf528SMauro Carvalho Chehab 
39fada1935SMauro Carvalho Chehab #include <media/dvb_math.h>
40fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
419a0bf528SMauro Carvalho Chehab #include "or51211.h"
429a0bf528SMauro Carvalho Chehab 
439a0bf528SMauro Carvalho Chehab static int debug;
449a0bf528SMauro Carvalho Chehab #define dprintk(args...) \
45bb9e31f3SAndy Shevchenko 	do { if (debug) pr_debug(args); } while (0)
469a0bf528SMauro Carvalho Chehab 
479a0bf528SMauro Carvalho Chehab static u8 run_buf[] = {0x7f,0x01};
489a0bf528SMauro Carvalho Chehab static u8 cmd_buf[] = {0x04,0x01,0x50,0x80,0x06}; // ATSC
499a0bf528SMauro Carvalho Chehab 
509a0bf528SMauro Carvalho Chehab struct or51211_state {
519a0bf528SMauro Carvalho Chehab 
529a0bf528SMauro Carvalho Chehab 	struct i2c_adapter* i2c;
539a0bf528SMauro Carvalho Chehab 
549a0bf528SMauro Carvalho Chehab 	/* Configuration settings */
559a0bf528SMauro Carvalho Chehab 	const struct or51211_config* config;
569a0bf528SMauro Carvalho Chehab 
579a0bf528SMauro Carvalho Chehab 	struct dvb_frontend frontend;
589a0bf528SMauro Carvalho Chehab 	struct bt878* bt;
599a0bf528SMauro Carvalho Chehab 
609a0bf528SMauro Carvalho Chehab 	/* Demodulator private data */
619a0bf528SMauro Carvalho Chehab 	u8 initialized:1;
62*868c9a17SMauro Carvalho Chehab 	u32 snr; /* Result of last SNR calculation */
639a0bf528SMauro Carvalho Chehab 
649a0bf528SMauro Carvalho Chehab 	/* Tuner private data */
659a0bf528SMauro Carvalho Chehab 	u32 current_frequency;
669a0bf528SMauro Carvalho Chehab };
679a0bf528SMauro Carvalho Chehab 
689a0bf528SMauro Carvalho Chehab static int i2c_writebytes (struct or51211_state* state, u8 reg, const u8 *buf,
699a0bf528SMauro Carvalho Chehab 			   int len)
709a0bf528SMauro Carvalho Chehab {
719a0bf528SMauro Carvalho Chehab 	int err;
729a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg;
739a0bf528SMauro Carvalho Chehab 	msg.addr	= reg;
749a0bf528SMauro Carvalho Chehab 	msg.flags	= 0;
759a0bf528SMauro Carvalho Chehab 	msg.len		= len;
769a0bf528SMauro Carvalho Chehab 	msg.buf		= (u8 *)buf;
779a0bf528SMauro Carvalho Chehab 
789a0bf528SMauro Carvalho Chehab 	if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
79bb9e31f3SAndy Shevchenko 		pr_warn("error (addr %02x, err == %i)\n", reg, err);
809a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
819a0bf528SMauro Carvalho Chehab 	}
829a0bf528SMauro Carvalho Chehab 
839a0bf528SMauro Carvalho Chehab 	return 0;
849a0bf528SMauro Carvalho Chehab }
859a0bf528SMauro Carvalho Chehab 
869a0bf528SMauro Carvalho Chehab static int i2c_readbytes(struct or51211_state *state, u8 reg, u8 *buf, int len)
879a0bf528SMauro Carvalho Chehab {
889a0bf528SMauro Carvalho Chehab 	int err;
899a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg;
909a0bf528SMauro Carvalho Chehab 	msg.addr	= reg;
919a0bf528SMauro Carvalho Chehab 	msg.flags	= I2C_M_RD;
929a0bf528SMauro Carvalho Chehab 	msg.len		= len;
939a0bf528SMauro Carvalho Chehab 	msg.buf		= buf;
949a0bf528SMauro Carvalho Chehab 
959a0bf528SMauro Carvalho Chehab 	if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
96bb9e31f3SAndy Shevchenko 		pr_warn("error (addr %02x, err == %i)\n", reg, err);
979a0bf528SMauro Carvalho Chehab 		return -EREMOTEIO;
989a0bf528SMauro Carvalho Chehab 	}
999a0bf528SMauro Carvalho Chehab 
1009a0bf528SMauro Carvalho Chehab 	return 0;
1019a0bf528SMauro Carvalho Chehab }
1029a0bf528SMauro Carvalho Chehab 
1039a0bf528SMauro Carvalho Chehab static int or51211_load_firmware (struct dvb_frontend* fe,
1049a0bf528SMauro Carvalho Chehab 				  const struct firmware *fw)
1059a0bf528SMauro Carvalho Chehab {
1069a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = fe->demodulator_priv;
1079a0bf528SMauro Carvalho Chehab 	u8 tudata[585];
1089a0bf528SMauro Carvalho Chehab 	int i;
1099a0bf528SMauro Carvalho Chehab 
11035f30f36SMauro Carvalho Chehab 	dprintk("Firmware is %zu bytes\n", fw->size);
1119a0bf528SMauro Carvalho Chehab 
1129a0bf528SMauro Carvalho Chehab 	/* Get eprom data */
1139a0bf528SMauro Carvalho Chehab 	tudata[0] = 17;
1149a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,0x50,tudata,1)) {
115bb9e31f3SAndy Shevchenko 		pr_warn("error eprom addr\n");
1169a0bf528SMauro Carvalho Chehab 		return -1;
1179a0bf528SMauro Carvalho Chehab 	}
1189a0bf528SMauro Carvalho Chehab 	if (i2c_readbytes(state,0x50,&tudata[145],192)) {
119bb9e31f3SAndy Shevchenko 		pr_warn("error eprom\n");
1209a0bf528SMauro Carvalho Chehab 		return -1;
1219a0bf528SMauro Carvalho Chehab 	}
1229a0bf528SMauro Carvalho Chehab 
1239a0bf528SMauro Carvalho Chehab 	/* Create firmware buffer */
1249a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 145; i++)
1259a0bf528SMauro Carvalho Chehab 		tudata[i] = fw->data[i];
1269a0bf528SMauro Carvalho Chehab 
1279a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 248; i++)
1289a0bf528SMauro Carvalho Chehab 		tudata[i+337] = fw->data[145+i];
1299a0bf528SMauro Carvalho Chehab 
1309a0bf528SMauro Carvalho Chehab 	state->config->reset(fe);
1319a0bf528SMauro Carvalho Chehab 
1329a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,tudata,585)) {
133bb9e31f3SAndy Shevchenko 		pr_warn("error 1\n");
1349a0bf528SMauro Carvalho Chehab 		return -1;
1359a0bf528SMauro Carvalho Chehab 	}
1369a0bf528SMauro Carvalho Chehab 	msleep(1);
1379a0bf528SMauro Carvalho Chehab 
1389a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,
1399a0bf528SMauro Carvalho Chehab 			   &fw->data[393],8125)) {
140bb9e31f3SAndy Shevchenko 		pr_warn("error 2\n");
1419a0bf528SMauro Carvalho Chehab 		return -1;
1429a0bf528SMauro Carvalho Chehab 	}
1439a0bf528SMauro Carvalho Chehab 	msleep(1);
1449a0bf528SMauro Carvalho Chehab 
1459a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
146bb9e31f3SAndy Shevchenko 		pr_warn("error 3\n");
1479a0bf528SMauro Carvalho Chehab 		return -1;
1489a0bf528SMauro Carvalho Chehab 	}
1499a0bf528SMauro Carvalho Chehab 
1509a0bf528SMauro Carvalho Chehab 	/* Wait at least 5 msec */
1519a0bf528SMauro Carvalho Chehab 	msleep(10);
1529a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
153bb9e31f3SAndy Shevchenko 		pr_warn("error 4\n");
1549a0bf528SMauro Carvalho Chehab 		return -1;
1559a0bf528SMauro Carvalho Chehab 	}
1569a0bf528SMauro Carvalho Chehab 	msleep(10);
1579a0bf528SMauro Carvalho Chehab 
158bb9e31f3SAndy Shevchenko 	pr_info("Done.\n");
1599a0bf528SMauro Carvalho Chehab 	return 0;
1609a0bf528SMauro Carvalho Chehab };
1619a0bf528SMauro Carvalho Chehab 
1629a0bf528SMauro Carvalho Chehab static int or51211_setmode(struct dvb_frontend* fe, int mode)
1639a0bf528SMauro Carvalho Chehab {
1649a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = fe->demodulator_priv;
1659a0bf528SMauro Carvalho Chehab 	u8 rec_buf[14];
1669a0bf528SMauro Carvalho Chehab 
1679a0bf528SMauro Carvalho Chehab 	state->config->setmode(fe, mode);
1689a0bf528SMauro Carvalho Chehab 
1699a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
170bb9e31f3SAndy Shevchenko 		pr_warn("error 1\n");
1719a0bf528SMauro Carvalho Chehab 		return -1;
1729a0bf528SMauro Carvalho Chehab 	}
1739a0bf528SMauro Carvalho Chehab 
1749a0bf528SMauro Carvalho Chehab 	/* Wait at least 5 msec */
1759a0bf528SMauro Carvalho Chehab 	msleep(10);
1769a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
177bb9e31f3SAndy Shevchenko 		pr_warn("error 2\n");
1789a0bf528SMauro Carvalho Chehab 		return -1;
1799a0bf528SMauro Carvalho Chehab 	}
1809a0bf528SMauro Carvalho Chehab 
1819a0bf528SMauro Carvalho Chehab 	msleep(10);
1829a0bf528SMauro Carvalho Chehab 
1839a0bf528SMauro Carvalho Chehab 	/* Set operation mode in Receiver 1 register;
1849a0bf528SMauro Carvalho Chehab 	 * type 1:
1859a0bf528SMauro Carvalho Chehab 	 * data 0x50h  Automatic sets receiver channel conditions
1869a0bf528SMauro Carvalho Chehab 	 *             Automatic NTSC rejection filter
1879a0bf528SMauro Carvalho Chehab 	 *             Enable  MPEG serial data output
1889a0bf528SMauro Carvalho Chehab 	 *             MPEG2tr
1899a0bf528SMauro Carvalho Chehab 	 *             High tuner phase noise
1909a0bf528SMauro Carvalho Chehab 	 *             normal +/-150kHz Carrier acquisition range
1919a0bf528SMauro Carvalho Chehab 	 */
1929a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,cmd_buf,3)) {
193bb9e31f3SAndy Shevchenko 		pr_warn("error 3\n");
1949a0bf528SMauro Carvalho Chehab 		return -1;
1959a0bf528SMauro Carvalho Chehab 	}
1969a0bf528SMauro Carvalho Chehab 
1979a0bf528SMauro Carvalho Chehab 	rec_buf[0] = 0x04;
1989a0bf528SMauro Carvalho Chehab 	rec_buf[1] = 0x00;
1999a0bf528SMauro Carvalho Chehab 	rec_buf[2] = 0x03;
2009a0bf528SMauro Carvalho Chehab 	rec_buf[3] = 0x00;
2019a0bf528SMauro Carvalho Chehab 	msleep(20);
2029a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,rec_buf,3)) {
203bb9e31f3SAndy Shevchenko 		pr_warn("error 5\n");
2049a0bf528SMauro Carvalho Chehab 	}
2059a0bf528SMauro Carvalho Chehab 	msleep(3);
2069a0bf528SMauro Carvalho Chehab 	if (i2c_readbytes(state,state->config->demod_address,&rec_buf[10],2)) {
207bb9e31f3SAndy Shevchenko 		pr_warn("error 6\n");
2089a0bf528SMauro Carvalho Chehab 		return -1;
2099a0bf528SMauro Carvalho Chehab 	}
210bb9e31f3SAndy Shevchenko 	dprintk("rec status %02x %02x\n", rec_buf[10], rec_buf[11]);
2119a0bf528SMauro Carvalho Chehab 
2129a0bf528SMauro Carvalho Chehab 	return 0;
2139a0bf528SMauro Carvalho Chehab }
2149a0bf528SMauro Carvalho Chehab 
2159a0bf528SMauro Carvalho Chehab static int or51211_set_parameters(struct dvb_frontend *fe)
2169a0bf528SMauro Carvalho Chehab {
2179a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
2189a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = fe->demodulator_priv;
2199a0bf528SMauro Carvalho Chehab 
2209a0bf528SMauro Carvalho Chehab 	/* Change only if we are actually changing the channel */
2219a0bf528SMauro Carvalho Chehab 	if (state->current_frequency != p->frequency) {
2229a0bf528SMauro Carvalho Chehab 		if (fe->ops.tuner_ops.set_params) {
2239a0bf528SMauro Carvalho Chehab 			fe->ops.tuner_ops.set_params(fe);
2249a0bf528SMauro Carvalho Chehab 			if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
2259a0bf528SMauro Carvalho Chehab 		}
2269a0bf528SMauro Carvalho Chehab 
2279a0bf528SMauro Carvalho Chehab 		/* Set to ATSC mode */
2289a0bf528SMauro Carvalho Chehab 		or51211_setmode(fe,0);
2299a0bf528SMauro Carvalho Chehab 
2309a0bf528SMauro Carvalho Chehab 		/* Update current frequency */
2319a0bf528SMauro Carvalho Chehab 		state->current_frequency = p->frequency;
2329a0bf528SMauro Carvalho Chehab 	}
2339a0bf528SMauro Carvalho Chehab 	return 0;
2349a0bf528SMauro Carvalho Chehab }
2359a0bf528SMauro Carvalho Chehab 
2360df289a2SMauro Carvalho Chehab static int or51211_read_status(struct dvb_frontend *fe, enum fe_status *status)
2379a0bf528SMauro Carvalho Chehab {
2389a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = fe->demodulator_priv;
2399a0bf528SMauro Carvalho Chehab 	unsigned char rec_buf[2];
2409a0bf528SMauro Carvalho Chehab 	unsigned char snd_buf[] = {0x04,0x00,0x03,0x00};
2419a0bf528SMauro Carvalho Chehab 	*status = 0;
2429a0bf528SMauro Carvalho Chehab 
2439a0bf528SMauro Carvalho Chehab 	/* Receiver Status */
2449a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
245bb9e31f3SAndy Shevchenko 		pr_warn("write error\n");
2469a0bf528SMauro Carvalho Chehab 		return -1;
2479a0bf528SMauro Carvalho Chehab 	}
2489a0bf528SMauro Carvalho Chehab 	msleep(3);
2499a0bf528SMauro Carvalho Chehab 	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
250bb9e31f3SAndy Shevchenko 		pr_warn("read error\n");
2519a0bf528SMauro Carvalho Chehab 		return -1;
2529a0bf528SMauro Carvalho Chehab 	}
253bb9e31f3SAndy Shevchenko 	dprintk("%x %x\n", rec_buf[0], rec_buf[1]);
2549a0bf528SMauro Carvalho Chehab 
2559a0bf528SMauro Carvalho Chehab 	if (rec_buf[0] &  0x01) { /* Receiver Lock */
2569a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SIGNAL;
2579a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_CARRIER;
2589a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_VITERBI;
2599a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_SYNC;
2609a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_LOCK;
2619a0bf528SMauro Carvalho Chehab 	}
2629a0bf528SMauro Carvalho Chehab 	return 0;
2639a0bf528SMauro Carvalho Chehab }
2649a0bf528SMauro Carvalho Chehab 
2659a0bf528SMauro Carvalho Chehab /* Calculate SNR estimation (scaled by 2^24)
2669a0bf528SMauro Carvalho Chehab 
2679a0bf528SMauro Carvalho Chehab    8-VSB SNR equation from Oren datasheets
2689a0bf528SMauro Carvalho Chehab 
2699a0bf528SMauro Carvalho Chehab    For 8-VSB:
2709a0bf528SMauro Carvalho Chehab      SNR[dB] = 10 * log10(219037.9454 / MSE^2 )
2719a0bf528SMauro Carvalho Chehab 
2729a0bf528SMauro Carvalho Chehab    We re-write the snr equation as:
2739a0bf528SMauro Carvalho Chehab      SNR * 2^24 = 10*(c - 2*intlog10(MSE))
2749a0bf528SMauro Carvalho Chehab    Where for 8-VSB, c = log10(219037.9454) * 2^24 */
2759a0bf528SMauro Carvalho Chehab 
2769a0bf528SMauro Carvalho Chehab static u32 calculate_snr(u32 mse, u32 c)
2779a0bf528SMauro Carvalho Chehab {
2789a0bf528SMauro Carvalho Chehab 	if (mse == 0) /* No signal */
2799a0bf528SMauro Carvalho Chehab 		return 0;
2809a0bf528SMauro Carvalho Chehab 
2819a0bf528SMauro Carvalho Chehab 	mse = 2*intlog10(mse);
2829a0bf528SMauro Carvalho Chehab 	if (mse > c) {
2839a0bf528SMauro Carvalho Chehab 		/* Negative SNR, which is possible, but realisticly the
2849a0bf528SMauro Carvalho Chehab 		demod will lose lock before the signal gets this bad.  The
2859a0bf528SMauro Carvalho Chehab 		API only allows for unsigned values, so just return 0 */
2869a0bf528SMauro Carvalho Chehab 		return 0;
2879a0bf528SMauro Carvalho Chehab 	}
2889a0bf528SMauro Carvalho Chehab 	return 10*(c - mse);
2899a0bf528SMauro Carvalho Chehab }
2909a0bf528SMauro Carvalho Chehab 
2919a0bf528SMauro Carvalho Chehab static int or51211_read_snr(struct dvb_frontend* fe, u16* snr)
2929a0bf528SMauro Carvalho Chehab {
2939a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = fe->demodulator_priv;
2949a0bf528SMauro Carvalho Chehab 	u8 rec_buf[2];
2959a0bf528SMauro Carvalho Chehab 	u8 snd_buf[3];
2969a0bf528SMauro Carvalho Chehab 
2979a0bf528SMauro Carvalho Chehab 	/* SNR after Equalizer */
2989a0bf528SMauro Carvalho Chehab 	snd_buf[0] = 0x04;
2999a0bf528SMauro Carvalho Chehab 	snd_buf[1] = 0x00;
3009a0bf528SMauro Carvalho Chehab 	snd_buf[2] = 0x04;
3019a0bf528SMauro Carvalho Chehab 
3029a0bf528SMauro Carvalho Chehab 	if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
303bb9e31f3SAndy Shevchenko 		pr_warn("error writing snr reg\n");
3049a0bf528SMauro Carvalho Chehab 		return -1;
3059a0bf528SMauro Carvalho Chehab 	}
3069a0bf528SMauro Carvalho Chehab 	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
307bb9e31f3SAndy Shevchenko 		pr_warn("read_status read error\n");
3089a0bf528SMauro Carvalho Chehab 		return -1;
3099a0bf528SMauro Carvalho Chehab 	}
3109a0bf528SMauro Carvalho Chehab 
3119a0bf528SMauro Carvalho Chehab 	state->snr = calculate_snr(rec_buf[0], 89599047);
3129a0bf528SMauro Carvalho Chehab 	*snr = (state->snr) >> 16;
3139a0bf528SMauro Carvalho Chehab 
314bb9e31f3SAndy Shevchenko 	dprintk("noise = 0x%02x, snr = %d.%02d dB\n", rec_buf[0],
3159a0bf528SMauro Carvalho Chehab 		state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16);
3169a0bf528SMauro Carvalho Chehab 
3179a0bf528SMauro Carvalho Chehab 	return 0;
3189a0bf528SMauro Carvalho Chehab }
3199a0bf528SMauro Carvalho Chehab 
3209a0bf528SMauro Carvalho Chehab static int or51211_read_signal_strength(struct dvb_frontend* fe, u16* strength)
3219a0bf528SMauro Carvalho Chehab {
3229a0bf528SMauro Carvalho Chehab 	/* Calculate Strength from SNR up to 35dB */
3239a0bf528SMauro Carvalho Chehab 	/* Even though the SNR can go higher than 35dB, there is some comfort */
3249a0bf528SMauro Carvalho Chehab 	/* factor in having a range of strong signals that can show at 100%   */
3259a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = (struct or51211_state*)fe->demodulator_priv;
3269a0bf528SMauro Carvalho Chehab 	u16 snr;
3279a0bf528SMauro Carvalho Chehab 	int ret;
3289a0bf528SMauro Carvalho Chehab 
3299a0bf528SMauro Carvalho Chehab 	ret = fe->ops.read_snr(fe, &snr);
3309a0bf528SMauro Carvalho Chehab 	if (ret != 0)
3319a0bf528SMauro Carvalho Chehab 		return ret;
3329a0bf528SMauro Carvalho Chehab 	/* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
3339a0bf528SMauro Carvalho Chehab 	/* scale the range 0 - 35*2^24 into 0 - 65535 */
3349a0bf528SMauro Carvalho Chehab 	if (state->snr >= 8960 * 0x10000)
3359a0bf528SMauro Carvalho Chehab 		*strength = 0xffff;
3369a0bf528SMauro Carvalho Chehab 	else
3379a0bf528SMauro Carvalho Chehab 		*strength = state->snr / 8960;
3389a0bf528SMauro Carvalho Chehab 
3399a0bf528SMauro Carvalho Chehab 	return 0;
3409a0bf528SMauro Carvalho Chehab }
3419a0bf528SMauro Carvalho Chehab 
3429a0bf528SMauro Carvalho Chehab static int or51211_read_ber(struct dvb_frontend* fe, u32* ber)
3439a0bf528SMauro Carvalho Chehab {
3449a0bf528SMauro Carvalho Chehab 	*ber = -ENOSYS;
3459a0bf528SMauro Carvalho Chehab 	return 0;
3469a0bf528SMauro Carvalho Chehab }
3479a0bf528SMauro Carvalho Chehab 
3489a0bf528SMauro Carvalho Chehab static int or51211_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
3499a0bf528SMauro Carvalho Chehab {
3509a0bf528SMauro Carvalho Chehab 	*ucblocks = -ENOSYS;
3519a0bf528SMauro Carvalho Chehab 	return 0;
3529a0bf528SMauro Carvalho Chehab }
3539a0bf528SMauro Carvalho Chehab 
3549a0bf528SMauro Carvalho Chehab static int or51211_sleep(struct dvb_frontend* fe)
3559a0bf528SMauro Carvalho Chehab {
3569a0bf528SMauro Carvalho Chehab 	return 0;
3579a0bf528SMauro Carvalho Chehab }
3589a0bf528SMauro Carvalho Chehab 
3599a0bf528SMauro Carvalho Chehab static int or51211_init(struct dvb_frontend* fe)
3609a0bf528SMauro Carvalho Chehab {
3619a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = fe->demodulator_priv;
3629a0bf528SMauro Carvalho Chehab 	const struct or51211_config* config = state->config;
3639a0bf528SMauro Carvalho Chehab 	const struct firmware* fw;
3649a0bf528SMauro Carvalho Chehab 	unsigned char get_ver_buf[] = {0x04,0x00,0x30,0x00,0x00};
3659a0bf528SMauro Carvalho Chehab 	unsigned char rec_buf[14];
3669a0bf528SMauro Carvalho Chehab 	int ret,i;
3679a0bf528SMauro Carvalho Chehab 
3689a0bf528SMauro Carvalho Chehab 	if (!state->initialized) {
3699a0bf528SMauro Carvalho Chehab 		/* Request the firmware, this will block until it uploads */
370bb9e31f3SAndy Shevchenko 		pr_info("Waiting for firmware upload (%s)...\n",
371bb9e31f3SAndy Shevchenko 			OR51211_DEFAULT_FIRMWARE);
3729a0bf528SMauro Carvalho Chehab 		ret = config->request_firmware(fe, &fw,
3739a0bf528SMauro Carvalho Chehab 					       OR51211_DEFAULT_FIRMWARE);
374bb9e31f3SAndy Shevchenko 		pr_info("Got Hotplug firmware\n");
3759a0bf528SMauro Carvalho Chehab 		if (ret) {
3764bd69e7bSMauro Carvalho Chehab 			pr_warn("No firmware uploaded (timeout or file not found?)\n");
3779a0bf528SMauro Carvalho Chehab 			return ret;
3789a0bf528SMauro Carvalho Chehab 		}
3799a0bf528SMauro Carvalho Chehab 
3809a0bf528SMauro Carvalho Chehab 		ret = or51211_load_firmware(fe, fw);
3819a0bf528SMauro Carvalho Chehab 		release_firmware(fw);
3829a0bf528SMauro Carvalho Chehab 		if (ret) {
383bb9e31f3SAndy Shevchenko 			pr_warn("Writing firmware to device failed!\n");
3849a0bf528SMauro Carvalho Chehab 			return ret;
3859a0bf528SMauro Carvalho Chehab 		}
386bb9e31f3SAndy Shevchenko 		pr_info("Firmware upload complete.\n");
3879a0bf528SMauro Carvalho Chehab 
3889a0bf528SMauro Carvalho Chehab 		/* Set operation mode in Receiver 1 register;
3899a0bf528SMauro Carvalho Chehab 		 * type 1:
3909a0bf528SMauro Carvalho Chehab 		 * data 0x50h  Automatic sets receiver channel conditions
3919a0bf528SMauro Carvalho Chehab 		 *             Automatic NTSC rejection filter
3929a0bf528SMauro Carvalho Chehab 		 *             Enable  MPEG serial data output
3939a0bf528SMauro Carvalho Chehab 		 *             MPEG2tr
3949a0bf528SMauro Carvalho Chehab 		 *             High tuner phase noise
3959a0bf528SMauro Carvalho Chehab 		 *             normal +/-150kHz Carrier acquisition range
3969a0bf528SMauro Carvalho Chehab 		 */
3979a0bf528SMauro Carvalho Chehab 		if (i2c_writebytes(state,state->config->demod_address,
3989a0bf528SMauro Carvalho Chehab 				   cmd_buf,3)) {
399bb9e31f3SAndy Shevchenko 			pr_warn("Load DVR Error 5\n");
4009a0bf528SMauro Carvalho Chehab 			return -1;
4019a0bf528SMauro Carvalho Chehab 		}
4029a0bf528SMauro Carvalho Chehab 
4039a0bf528SMauro Carvalho Chehab 		/* Read back ucode version to besure we loaded correctly */
4049a0bf528SMauro Carvalho Chehab 		/* and are really up and running */
4059a0bf528SMauro Carvalho Chehab 		rec_buf[0] = 0x04;
4069a0bf528SMauro Carvalho Chehab 		rec_buf[1] = 0x00;
4079a0bf528SMauro Carvalho Chehab 		rec_buf[2] = 0x03;
4089a0bf528SMauro Carvalho Chehab 		rec_buf[3] = 0x00;
4099a0bf528SMauro Carvalho Chehab 		msleep(30);
4109a0bf528SMauro Carvalho Chehab 		if (i2c_writebytes(state,state->config->demod_address,
4119a0bf528SMauro Carvalho Chehab 				   rec_buf,3)) {
412bb9e31f3SAndy Shevchenko 			pr_warn("Load DVR Error A\n");
4139a0bf528SMauro Carvalho Chehab 			return -1;
4149a0bf528SMauro Carvalho Chehab 		}
4159a0bf528SMauro Carvalho Chehab 		msleep(3);
4169a0bf528SMauro Carvalho Chehab 		if (i2c_readbytes(state,state->config->demod_address,
4179a0bf528SMauro Carvalho Chehab 				  &rec_buf[10],2)) {
418bb9e31f3SAndy Shevchenko 			pr_warn("Load DVR Error B\n");
4199a0bf528SMauro Carvalho Chehab 			return -1;
4209a0bf528SMauro Carvalho Chehab 		}
4219a0bf528SMauro Carvalho Chehab 
4229a0bf528SMauro Carvalho Chehab 		rec_buf[0] = 0x04;
4239a0bf528SMauro Carvalho Chehab 		rec_buf[1] = 0x00;
4249a0bf528SMauro Carvalho Chehab 		rec_buf[2] = 0x01;
4259a0bf528SMauro Carvalho Chehab 		rec_buf[3] = 0x00;
4269a0bf528SMauro Carvalho Chehab 		msleep(20);
4279a0bf528SMauro Carvalho Chehab 		if (i2c_writebytes(state,state->config->demod_address,
4289a0bf528SMauro Carvalho Chehab 				   rec_buf,3)) {
429bb9e31f3SAndy Shevchenko 			pr_warn("Load DVR Error C\n");
4309a0bf528SMauro Carvalho Chehab 			return -1;
4319a0bf528SMauro Carvalho Chehab 		}
4329a0bf528SMauro Carvalho Chehab 		msleep(3);
4339a0bf528SMauro Carvalho Chehab 		if (i2c_readbytes(state,state->config->demod_address,
4349a0bf528SMauro Carvalho Chehab 				  &rec_buf[12],2)) {
435bb9e31f3SAndy Shevchenko 			pr_warn("Load DVR Error D\n");
4369a0bf528SMauro Carvalho Chehab 			return -1;
4379a0bf528SMauro Carvalho Chehab 		}
4389a0bf528SMauro Carvalho Chehab 
4399a0bf528SMauro Carvalho Chehab 		for (i = 0; i < 8; i++)
4409a0bf528SMauro Carvalho Chehab 			rec_buf[i]=0xed;
4419a0bf528SMauro Carvalho Chehab 
4429a0bf528SMauro Carvalho Chehab 		for (i = 0; i < 5; i++) {
4439a0bf528SMauro Carvalho Chehab 			msleep(30);
4449a0bf528SMauro Carvalho Chehab 			get_ver_buf[4] = i+1;
4459a0bf528SMauro Carvalho Chehab 			if (i2c_writebytes(state,state->config->demod_address,
4469a0bf528SMauro Carvalho Chehab 					   get_ver_buf,5)) {
447bb9e31f3SAndy Shevchenko 				pr_warn("Load DVR Error 6 - %d\n", i);
4489a0bf528SMauro Carvalho Chehab 				return -1;
4499a0bf528SMauro Carvalho Chehab 			}
4509a0bf528SMauro Carvalho Chehab 			msleep(3);
4519a0bf528SMauro Carvalho Chehab 
4529a0bf528SMauro Carvalho Chehab 			if (i2c_readbytes(state,state->config->demod_address,
4539a0bf528SMauro Carvalho Chehab 					  &rec_buf[i*2],2)) {
454bb9e31f3SAndy Shevchenko 				pr_warn("Load DVR Error 7 - %d\n", i);
4559a0bf528SMauro Carvalho Chehab 				return -1;
4569a0bf528SMauro Carvalho Chehab 			}
4579a0bf528SMauro Carvalho Chehab 			/* If we didn't receive the right index, try again */
4589a0bf528SMauro Carvalho Chehab 			if ((int)rec_buf[i*2+1]!=i+1){
4599a0bf528SMauro Carvalho Chehab 			  i--;
4609a0bf528SMauro Carvalho Chehab 			}
4619a0bf528SMauro Carvalho Chehab 		}
462870f31cbSAndy Shevchenko 		dprintk("read_fwbits %10ph\n", rec_buf);
4639a0bf528SMauro Carvalho Chehab 
464bb9e31f3SAndy Shevchenko 		pr_info("ver TU%02x%02x%02x VSB mode %02x Status %02x\n",
465bb9e31f3SAndy Shevchenko 			rec_buf[2], rec_buf[4], rec_buf[6], rec_buf[12],
466bb9e31f3SAndy Shevchenko 			rec_buf[10]);
4679a0bf528SMauro Carvalho Chehab 
4689a0bf528SMauro Carvalho Chehab 		rec_buf[0] = 0x04;
4699a0bf528SMauro Carvalho Chehab 		rec_buf[1] = 0x00;
4709a0bf528SMauro Carvalho Chehab 		rec_buf[2] = 0x03;
4719a0bf528SMauro Carvalho Chehab 		rec_buf[3] = 0x00;
4729a0bf528SMauro Carvalho Chehab 		msleep(20);
4739a0bf528SMauro Carvalho Chehab 		if (i2c_writebytes(state,state->config->demod_address,
4749a0bf528SMauro Carvalho Chehab 				   rec_buf,3)) {
475bb9e31f3SAndy Shevchenko 			pr_warn("Load DVR Error 8\n");
4769a0bf528SMauro Carvalho Chehab 			return -1;
4779a0bf528SMauro Carvalho Chehab 		}
4789a0bf528SMauro Carvalho Chehab 		msleep(20);
4799a0bf528SMauro Carvalho Chehab 		if (i2c_readbytes(state,state->config->demod_address,
4809a0bf528SMauro Carvalho Chehab 				  &rec_buf[8],2)) {
481bb9e31f3SAndy Shevchenko 			pr_warn("Load DVR Error 9\n");
4829a0bf528SMauro Carvalho Chehab 			return -1;
4839a0bf528SMauro Carvalho Chehab 		}
4849a0bf528SMauro Carvalho Chehab 		state->initialized = 1;
4859a0bf528SMauro Carvalho Chehab 	}
4869a0bf528SMauro Carvalho Chehab 
4879a0bf528SMauro Carvalho Chehab 	return 0;
4889a0bf528SMauro Carvalho Chehab }
4899a0bf528SMauro Carvalho Chehab 
4909a0bf528SMauro Carvalho Chehab static int or51211_get_tune_settings(struct dvb_frontend* fe,
4919a0bf528SMauro Carvalho Chehab 				     struct dvb_frontend_tune_settings* fesettings)
4929a0bf528SMauro Carvalho Chehab {
4939a0bf528SMauro Carvalho Chehab 	fesettings->min_delay_ms = 500;
4949a0bf528SMauro Carvalho Chehab 	fesettings->step_size = 0;
4959a0bf528SMauro Carvalho Chehab 	fesettings->max_drift = 0;
4969a0bf528SMauro Carvalho Chehab 	return 0;
4979a0bf528SMauro Carvalho Chehab }
4989a0bf528SMauro Carvalho Chehab 
4999a0bf528SMauro Carvalho Chehab static void or51211_release(struct dvb_frontend* fe)
5009a0bf528SMauro Carvalho Chehab {
5019a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = fe->demodulator_priv;
5029a0bf528SMauro Carvalho Chehab 	state->config->sleep(fe);
5039a0bf528SMauro Carvalho Chehab 	kfree(state);
5049a0bf528SMauro Carvalho Chehab }
5059a0bf528SMauro Carvalho Chehab 
506bd336e63SMax Kellermann static const struct dvb_frontend_ops or51211_ops;
5079a0bf528SMauro Carvalho Chehab 
5089a0bf528SMauro Carvalho Chehab struct dvb_frontend* or51211_attach(const struct or51211_config* config,
5099a0bf528SMauro Carvalho Chehab 				    struct i2c_adapter* i2c)
5109a0bf528SMauro Carvalho Chehab {
5119a0bf528SMauro Carvalho Chehab 	struct or51211_state* state = NULL;
5129a0bf528SMauro Carvalho Chehab 
5139a0bf528SMauro Carvalho Chehab 	/* Allocate memory for the internal state */
5149a0bf528SMauro Carvalho Chehab 	state = kzalloc(sizeof(struct or51211_state), GFP_KERNEL);
5159a0bf528SMauro Carvalho Chehab 	if (state == NULL)
5169a0bf528SMauro Carvalho Chehab 		return NULL;
5179a0bf528SMauro Carvalho Chehab 
5189a0bf528SMauro Carvalho Chehab 	/* Setup the state */
5199a0bf528SMauro Carvalho Chehab 	state->config = config;
5209a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
5219a0bf528SMauro Carvalho Chehab 	state->initialized = 0;
5229a0bf528SMauro Carvalho Chehab 	state->current_frequency = 0;
5239a0bf528SMauro Carvalho Chehab 
5249a0bf528SMauro Carvalho Chehab 	/* Create dvb_frontend */
5259a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &or51211_ops, sizeof(struct dvb_frontend_ops));
5269a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
5279a0bf528SMauro Carvalho Chehab 	return &state->frontend;
5289a0bf528SMauro Carvalho Chehab }
5299a0bf528SMauro Carvalho Chehab 
530bd336e63SMax Kellermann static const struct dvb_frontend_ops or51211_ops = {
5319a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
5329a0bf528SMauro Carvalho Chehab 	.info = {
5339a0bf528SMauro Carvalho Chehab 		.name                  = "Oren OR51211 VSB Frontend",
534f1b1eabfSMauro Carvalho Chehab 		.frequency_min_hz      =  44 * MHz,
535f1b1eabfSMauro Carvalho Chehab 		.frequency_max_hz      = 958 * MHz,
536f1b1eabfSMauro Carvalho Chehab 		.frequency_stepsize_hz = 166666,
5379a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
5389a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
5399a0bf528SMauro Carvalho Chehab 			FE_CAN_8VSB
5409a0bf528SMauro Carvalho Chehab 	},
5419a0bf528SMauro Carvalho Chehab 
5429a0bf528SMauro Carvalho Chehab 	.release = or51211_release,
5439a0bf528SMauro Carvalho Chehab 
5449a0bf528SMauro Carvalho Chehab 	.init = or51211_init,
5459a0bf528SMauro Carvalho Chehab 	.sleep = or51211_sleep,
5469a0bf528SMauro Carvalho Chehab 
5479a0bf528SMauro Carvalho Chehab 	.set_frontend = or51211_set_parameters,
5489a0bf528SMauro Carvalho Chehab 	.get_tune_settings = or51211_get_tune_settings,
5499a0bf528SMauro Carvalho Chehab 
5509a0bf528SMauro Carvalho Chehab 	.read_status = or51211_read_status,
5519a0bf528SMauro Carvalho Chehab 	.read_ber = or51211_read_ber,
5529a0bf528SMauro Carvalho Chehab 	.read_signal_strength = or51211_read_signal_strength,
5539a0bf528SMauro Carvalho Chehab 	.read_snr = or51211_read_snr,
5549a0bf528SMauro Carvalho Chehab 	.read_ucblocks = or51211_read_ucblocks,
5559a0bf528SMauro Carvalho Chehab };
5569a0bf528SMauro Carvalho Chehab 
5579a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
5589a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
5599a0bf528SMauro Carvalho Chehab 
5609a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("Oren OR51211 VSB [pcHDTV HD-2000] Demodulator Driver");
5619a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Kirk Lapray");
5629a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
5639a0bf528SMauro Carvalho Chehab 
5649a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(or51211_attach);
5659a0bf528SMauro Carvalho Chehab 
566