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