19a0bf528SMauro Carvalho Chehab /*
29a0bf528SMauro Carvalho Chehab  *   Fujitu mb86a20s ISDB-T/ISDB-Tsb Module driver
39a0bf528SMauro Carvalho Chehab  *
4a77cfcacSMauro Carvalho Chehab  *   Copyright (C) 2010-2013 Mauro Carvalho Chehab <mchehab@redhat.com>
59a0bf528SMauro Carvalho Chehab  *   Copyright (C) 2009-2010 Douglas Landgraf <dougsland@redhat.com>
69a0bf528SMauro Carvalho Chehab  *
79a0bf528SMauro Carvalho Chehab  *   This program is free software; you can redistribute it and/or
89a0bf528SMauro Carvalho Chehab  *   modify it under the terms of the GNU General Public License as
99a0bf528SMauro Carvalho Chehab  *   published by the Free Software Foundation version 2.
109a0bf528SMauro Carvalho Chehab  *
119a0bf528SMauro Carvalho Chehab  *   This program is distributed in the hope that it will be useful,
129a0bf528SMauro Carvalho Chehab  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
139a0bf528SMauro Carvalho Chehab  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
149a0bf528SMauro Carvalho Chehab  *   General Public License for more details.
159a0bf528SMauro Carvalho Chehab  */
169a0bf528SMauro Carvalho Chehab 
179a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
189a0bf528SMauro Carvalho Chehab #include <asm/div64.h>
199a0bf528SMauro Carvalho Chehab 
209a0bf528SMauro Carvalho Chehab #include "dvb_frontend.h"
219a0bf528SMauro Carvalho Chehab #include "mb86a20s.h"
229a0bf528SMauro Carvalho Chehab 
239a0bf528SMauro Carvalho Chehab static int debug = 1;
249a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
259a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
269a0bf528SMauro Carvalho Chehab 
279a0bf528SMauro Carvalho Chehab struct mb86a20s_state {
289a0bf528SMauro Carvalho Chehab 	struct i2c_adapter *i2c;
299a0bf528SMauro Carvalho Chehab 	const struct mb86a20s_config *config;
3009b6d21eSMauro Carvalho Chehab 	u32 last_frequency;
319a0bf528SMauro Carvalho Chehab 
329a0bf528SMauro Carvalho Chehab 	struct dvb_frontend frontend;
339a0bf528SMauro Carvalho Chehab 
34d01a8ee3SMauro Carvalho Chehab 	u32 estimated_rate[3];
35d01a8ee3SMauro Carvalho Chehab 
369a0bf528SMauro Carvalho Chehab 	bool need_init;
379a0bf528SMauro Carvalho Chehab };
389a0bf528SMauro Carvalho Chehab 
399a0bf528SMauro Carvalho Chehab struct regdata {
409a0bf528SMauro Carvalho Chehab 	u8 reg;
419a0bf528SMauro Carvalho Chehab 	u8 data;
429a0bf528SMauro Carvalho Chehab };
439a0bf528SMauro Carvalho Chehab 
44d01a8ee3SMauro Carvalho Chehab #define BER_SAMPLING_RATE	1	/* Seconds */
45d01a8ee3SMauro Carvalho Chehab 
469a0bf528SMauro Carvalho Chehab /*
479a0bf528SMauro Carvalho Chehab  * Initialization sequence: Use whatevere default values that PV SBTVD
489a0bf528SMauro Carvalho Chehab  * does on its initialisation, obtained via USB snoop
499a0bf528SMauro Carvalho Chehab  */
509a0bf528SMauro Carvalho Chehab static struct regdata mb86a20s_init[] = {
519a0bf528SMauro Carvalho Chehab 	{ 0x70, 0x0f },
529a0bf528SMauro Carvalho Chehab 	{ 0x70, 0xff },
539a0bf528SMauro Carvalho Chehab 	{ 0x08, 0x01 },
549a0bf528SMauro Carvalho Chehab 	{ 0x09, 0x3e },
559a0bf528SMauro Carvalho Chehab 	{ 0x50, 0xd1 }, { 0x51, 0x22 },
569a0bf528SMauro Carvalho Chehab 	{ 0x39, 0x01 },
579a0bf528SMauro Carvalho Chehab 	{ 0x71, 0x00 },
589a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x2a }, { 0x29, 0x00 }, { 0x2a, 0xff }, { 0x2b, 0x80 },
599a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x20 }, { 0x29, 0x33 }, { 0x2a, 0xdf }, { 0x2b, 0xa9 },
609a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x22 }, { 0x29, 0x00 }, { 0x2a, 0x1f }, { 0x2b, 0xf0 },
619a0bf528SMauro Carvalho Chehab 	{ 0x3b, 0x21 },
629a0bf528SMauro Carvalho Chehab 	{ 0x3c, 0x3a },
639a0bf528SMauro Carvalho Chehab 	{ 0x01, 0x0d },
649a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x08 }, { 0x05, 0x05 },
659a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x0e }, { 0x05, 0x00 },
669a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x0f }, { 0x05, 0x14 },
679a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x0b }, { 0x05, 0x8c },
689a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x00 }, { 0x05, 0x00 },
699a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x01 }, { 0x05, 0x07 },
709a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x02 }, { 0x05, 0x0f },
719a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x03 }, { 0x05, 0xa0 },
729a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x09 }, { 0x05, 0x00 },
739a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x0a }, { 0x05, 0xff },
749a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x27 }, { 0x05, 0x64 },
759a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x28 }, { 0x05, 0x00 },
769a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x1e }, { 0x05, 0xff },
779a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x29 }, { 0x05, 0x0a },
789a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x32 }, { 0x05, 0x0a },
799a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x14 }, { 0x05, 0x02 },
809a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x04 }, { 0x05, 0x00 },
819a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x05 }, { 0x05, 0x22 },
829a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x06 }, { 0x05, 0x0e },
839a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x07 }, { 0x05, 0xd8 },
849a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x12 }, { 0x05, 0x00 },
859a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x13 }, { 0x05, 0xff },
869a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x15 }, { 0x05, 0x4e },
879a0bf528SMauro Carvalho Chehab 	{ 0x04, 0x16 }, { 0x05, 0x20 },
8809b6d21eSMauro Carvalho Chehab 
8909b6d21eSMauro Carvalho Chehab 	/*
9009b6d21eSMauro Carvalho Chehab 	 * On this demod, when the bit count reaches the count below,
9109b6d21eSMauro Carvalho Chehab 	 * it collects the bit error count. The bit counters are initialized
9209b6d21eSMauro Carvalho Chehab 	 * to 65535 here. This warrants that all of them will be quickly
9309b6d21eSMauro Carvalho Chehab 	 * calculated when device gets locked. As TMCC is parsed, the values
94d01a8ee3SMauro Carvalho Chehab 	 * will be adjusted later in the driver's code.
9509b6d21eSMauro Carvalho Chehab 	 */
9609b6d21eSMauro Carvalho Chehab 	{ 0x52, 0x01 },				/* Turn on BER before Viterbi */
9709b6d21eSMauro Carvalho Chehab 	{ 0x50, 0xa7 }, { 0x51, 0x00 },
989a0bf528SMauro Carvalho Chehab 	{ 0x50, 0xa8 }, { 0x51, 0xff },
999a0bf528SMauro Carvalho Chehab 	{ 0x50, 0xa9 }, { 0x51, 0xff },
10009b6d21eSMauro Carvalho Chehab 	{ 0x50, 0xaa }, { 0x51, 0x00 },
1019a0bf528SMauro Carvalho Chehab 	{ 0x50, 0xab }, { 0x51, 0xff },
1029a0bf528SMauro Carvalho Chehab 	{ 0x50, 0xac }, { 0x51, 0xff },
10309b6d21eSMauro Carvalho Chehab 	{ 0x50, 0xad }, { 0x51, 0x00 },
1049a0bf528SMauro Carvalho Chehab 	{ 0x50, 0xae }, { 0x51, 0xff },
1059a0bf528SMauro Carvalho Chehab 	{ 0x50, 0xaf }, { 0x51, 0xff },
10609b6d21eSMauro Carvalho Chehab 
107d9b6f08aSMauro Carvalho Chehab 	/*
108d9b6f08aSMauro Carvalho Chehab 	 * On this demod, post BER counts blocks. When the count reaches the
109d9b6f08aSMauro Carvalho Chehab 	 * value below, it collects the block error count. The block counters
110d9b6f08aSMauro Carvalho Chehab 	 * are initialized to 127 here. This warrants that all of them will be
111d9b6f08aSMauro Carvalho Chehab 	 * quickly calculated when device gets locked. As TMCC is parsed, the
112d9b6f08aSMauro Carvalho Chehab 	 * values will be adjusted later in the driver's code.
113d9b6f08aSMauro Carvalho Chehab 	 */
114d9b6f08aSMauro Carvalho Chehab 	{ 0x5e, 0x07 },				/* Turn on BER after Viterbi */
115d9b6f08aSMauro Carvalho Chehab 	{ 0x50, 0xdc }, { 0x51, 0x00 },
116d9b6f08aSMauro Carvalho Chehab 	{ 0x50, 0xdd }, { 0x51, 0x7f },
117d9b6f08aSMauro Carvalho Chehab 	{ 0x50, 0xde }, { 0x51, 0x00 },
118d9b6f08aSMauro Carvalho Chehab 	{ 0x50, 0xdf }, { 0x51, 0x7f },
119d9b6f08aSMauro Carvalho Chehab 	{ 0x50, 0xe0 }, { 0x51, 0x00 },
120d9b6f08aSMauro Carvalho Chehab 	{ 0x50, 0xe1 }, { 0x51, 0x7f },
121593ae89aSMauro Carvalho Chehab 
122593ae89aSMauro Carvalho Chehab 	/*
123593ae89aSMauro Carvalho Chehab 	 * On this demod, when the block count reaches the count below,
124593ae89aSMauro Carvalho Chehab 	 * it collects the block error count. The block counters are initialized
125593ae89aSMauro Carvalho Chehab 	 * to 127 here. This warrants that all of them will be quickly
126593ae89aSMauro Carvalho Chehab 	 * calculated when device gets locked. As TMCC is parsed, the values
127593ae89aSMauro Carvalho Chehab 	 * will be adjusted later in the driver's code.
128593ae89aSMauro Carvalho Chehab 	 */
129593ae89aSMauro Carvalho Chehab 	{ 0x50, 0xb0 }, { 0x51, 0x07 },		/* Enable PER */
130593ae89aSMauro Carvalho Chehab 	{ 0x50, 0xb2 }, { 0x51, 0x00 },
131593ae89aSMauro Carvalho Chehab 	{ 0x50, 0xb3 }, { 0x51, 0x7f },
132593ae89aSMauro Carvalho Chehab 	{ 0x50, 0xb4 }, { 0x51, 0x00 },
133593ae89aSMauro Carvalho Chehab 	{ 0x50, 0xb5 }, { 0x51, 0x7f },
134593ae89aSMauro Carvalho Chehab 	{ 0x50, 0xb6 }, { 0x51, 0x00 },
135593ae89aSMauro Carvalho Chehab 	{ 0x50, 0xb7 }, { 0x51, 0x7f },
13625188bd0SMauro Carvalho Chehab 
13725188bd0SMauro Carvalho Chehab 	{ 0x50, 0x50 }, { 0x51, 0x02 },		/* MER manual mode */
13809b6d21eSMauro Carvalho Chehab 	{ 0x50, 0x51 }, { 0x51, 0x04 },		/* MER symbol 4 */
13909b6d21eSMauro Carvalho Chehab 	{ 0x45, 0x04 },				/* CN symbol 4 */
14025188bd0SMauro Carvalho Chehab 	{ 0x48, 0x04 },				/* CN manual mode */
14125188bd0SMauro Carvalho Chehab 
1429a0bf528SMauro Carvalho Chehab 	{ 0x50, 0xd5 }, { 0x51, 0x01 },		/* Serial */
1439a0bf528SMauro Carvalho Chehab 	{ 0x50, 0xd6 }, { 0x51, 0x1f },
1449a0bf528SMauro Carvalho Chehab 	{ 0x50, 0xd2 }, { 0x51, 0x03 },
1459a0bf528SMauro Carvalho Chehab 	{ 0x50, 0xd7 }, { 0x51, 0x3f },
1469a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x74 }, { 0x29, 0x00 }, { 0x28, 0x74 }, { 0x29, 0x40 },
1479a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x46 }, { 0x29, 0x2c }, { 0x28, 0x46 }, { 0x29, 0x0c },
148ce77d120SMauro Carvalho Chehab 
149ce77d120SMauro Carvalho Chehab 	{ 0x04, 0x40 }, { 0x05, 0x00 },
1509a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x00 }, { 0x29, 0x10 },
1519a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x05 }, { 0x29, 0x02 },
1529a0bf528SMauro Carvalho Chehab 	{ 0x1c, 0x01 },
1539a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x06 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x03 },
1549a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x07 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0d },
1559a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x08 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x02 },
1569a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x09 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x01 },
1579a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x0a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x21 },
1589a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x0b }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x29 },
1599a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x0c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x16 },
1609a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x0d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x31 },
1619a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x0e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0e },
1629a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x0f }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x4e },
1639a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x10 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x46 },
1649a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x11 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0f },
1659a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x12 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x56 },
1669a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x13 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x35 },
1679a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x14 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbe },
1689a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x15 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0x84 },
1699a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x16 }, { 0x29, 0x00 }, { 0x2a, 0x03 }, { 0x2b, 0xee },
1709a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x17 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x98 },
1719a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x18 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x9f },
1729a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x19 }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xb2 },
1739a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x1a }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0xc2 },
1749a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x1b }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0x4a },
1759a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x1c }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbc },
1769a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x1d }, { 0x29, 0x00 }, { 0x2a, 0x04 }, { 0x2b, 0xba },
1779a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x1e }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0x14 },
1789a0bf528SMauro Carvalho Chehab 	{ 0x50, 0x1e }, { 0x51, 0x5d },
1799a0bf528SMauro Carvalho Chehab 	{ 0x50, 0x22 }, { 0x51, 0x00 },
1809a0bf528SMauro Carvalho Chehab 	{ 0x50, 0x23 }, { 0x51, 0xc8 },
1819a0bf528SMauro Carvalho Chehab 	{ 0x50, 0x24 }, { 0x51, 0x00 },
1829a0bf528SMauro Carvalho Chehab 	{ 0x50, 0x25 }, { 0x51, 0xf0 },
1839a0bf528SMauro Carvalho Chehab 	{ 0x50, 0x26 }, { 0x51, 0x00 },
1849a0bf528SMauro Carvalho Chehab 	{ 0x50, 0x27 }, { 0x51, 0xc3 },
1859a0bf528SMauro Carvalho Chehab 	{ 0x50, 0x39 }, { 0x51, 0x02 },
1869a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x6a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x00 },
1879a0bf528SMauro Carvalho Chehab 	{ 0xd0, 0x00 },
1889a0bf528SMauro Carvalho Chehab };
1899a0bf528SMauro Carvalho Chehab 
1909a0bf528SMauro Carvalho Chehab static struct regdata mb86a20s_reset_reception[] = {
1919a0bf528SMauro Carvalho Chehab 	{ 0x70, 0xf0 },
1929a0bf528SMauro Carvalho Chehab 	{ 0x70, 0xff },
1939a0bf528SMauro Carvalho Chehab 	{ 0x08, 0x01 },
1949a0bf528SMauro Carvalho Chehab 	{ 0x08, 0x00 },
1959a0bf528SMauro Carvalho Chehab };
1969a0bf528SMauro Carvalho Chehab 
197d9b6f08aSMauro Carvalho Chehab static struct regdata mb86a20s_per_ber_reset[] = {
198d9b6f08aSMauro Carvalho Chehab 	{ 0x53, 0x00 },	/* pre BER Counter reset */
19909b6d21eSMauro Carvalho Chehab 	{ 0x53, 0x07 },
20009b6d21eSMauro Carvalho Chehab 
201d9b6f08aSMauro Carvalho Chehab 	{ 0x5f, 0x00 },	/* post BER Counter reset */
202d9b6f08aSMauro Carvalho Chehab 	{ 0x5f, 0x07 },
203d9b6f08aSMauro Carvalho Chehab 
20409b6d21eSMauro Carvalho Chehab 	{ 0x50, 0xb1 },	/* PER Counter reset */
20509b6d21eSMauro Carvalho Chehab 	{ 0x51, 0x07 },
20609b6d21eSMauro Carvalho Chehab 	{ 0x51, 0x00 },
20709b6d21eSMauro Carvalho Chehab };
20809b6d21eSMauro Carvalho Chehab 
209dd4493efSMauro Carvalho Chehab /*
210dd4493efSMauro Carvalho Chehab  * I2C read/write functions and macros
211dd4493efSMauro Carvalho Chehab  */
212dd4493efSMauro Carvalho Chehab 
2139a0bf528SMauro Carvalho Chehab static int mb86a20s_i2c_writereg(struct mb86a20s_state *state,
21409b6d21eSMauro Carvalho Chehab 			     u8 i2c_addr, u8 reg, u8 data)
2159a0bf528SMauro Carvalho Chehab {
2169a0bf528SMauro Carvalho Chehab 	u8 buf[] = { reg, data };
2179a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = {
2189a0bf528SMauro Carvalho Chehab 		.addr = i2c_addr, .flags = 0, .buf = buf, .len = 2
2199a0bf528SMauro Carvalho Chehab 	};
2209a0bf528SMauro Carvalho Chehab 	int rc;
2219a0bf528SMauro Carvalho Chehab 
2229a0bf528SMauro Carvalho Chehab 	rc = i2c_transfer(state->i2c, &msg, 1);
2239a0bf528SMauro Carvalho Chehab 	if (rc != 1) {
224f66d81b5SMauro Carvalho Chehab 		dev_err(&state->i2c->dev,
225f66d81b5SMauro Carvalho Chehab 			"%s: writereg error (rc == %i, reg == 0x%02x, data == 0x%02x)\n",
226f66d81b5SMauro Carvalho Chehab 			__func__, rc, reg, data);
2279a0bf528SMauro Carvalho Chehab 		return rc;
2289a0bf528SMauro Carvalho Chehab 	}
2299a0bf528SMauro Carvalho Chehab 
2309a0bf528SMauro Carvalho Chehab 	return 0;
2319a0bf528SMauro Carvalho Chehab }
2329a0bf528SMauro Carvalho Chehab 
2339a0bf528SMauro Carvalho Chehab static int mb86a20s_i2c_writeregdata(struct mb86a20s_state *state,
2349a0bf528SMauro Carvalho Chehab 				     u8 i2c_addr, struct regdata *rd, int size)
2359a0bf528SMauro Carvalho Chehab {
2369a0bf528SMauro Carvalho Chehab 	int i, rc;
2379a0bf528SMauro Carvalho Chehab 
2389a0bf528SMauro Carvalho Chehab 	for (i = 0; i < size; i++) {
2399a0bf528SMauro Carvalho Chehab 		rc = mb86a20s_i2c_writereg(state, i2c_addr, rd[i].reg,
2409a0bf528SMauro Carvalho Chehab 					   rd[i].data);
2419a0bf528SMauro Carvalho Chehab 		if (rc < 0)
2429a0bf528SMauro Carvalho Chehab 			return rc;
2439a0bf528SMauro Carvalho Chehab 	}
2449a0bf528SMauro Carvalho Chehab 	return 0;
2459a0bf528SMauro Carvalho Chehab }
2469a0bf528SMauro Carvalho Chehab 
2479a0bf528SMauro Carvalho Chehab static int mb86a20s_i2c_readreg(struct mb86a20s_state *state,
2489a0bf528SMauro Carvalho Chehab 				u8 i2c_addr, u8 reg)
2499a0bf528SMauro Carvalho Chehab {
2509a0bf528SMauro Carvalho Chehab 	u8 val;
2519a0bf528SMauro Carvalho Chehab 	int rc;
2529a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[] = {
2539a0bf528SMauro Carvalho Chehab 		{ .addr = i2c_addr, .flags = 0, .buf = &reg, .len = 1 },
2549a0bf528SMauro Carvalho Chehab 		{ .addr = i2c_addr, .flags = I2C_M_RD, .buf = &val, .len = 1 }
2559a0bf528SMauro Carvalho Chehab 	};
2569a0bf528SMauro Carvalho Chehab 
2579a0bf528SMauro Carvalho Chehab 	rc = i2c_transfer(state->i2c, msg, 2);
2589a0bf528SMauro Carvalho Chehab 
2599a0bf528SMauro Carvalho Chehab 	if (rc != 2) {
260f66d81b5SMauro Carvalho Chehab 		dev_err(&state->i2c->dev, "%s: reg=0x%x (error=%d)\n",
261f66d81b5SMauro Carvalho Chehab 			__func__, reg, rc);
262f66d81b5SMauro Carvalho Chehab 		return (rc < 0) ? rc : -EIO;
2639a0bf528SMauro Carvalho Chehab 	}
2649a0bf528SMauro Carvalho Chehab 
2659a0bf528SMauro Carvalho Chehab 	return val;
2669a0bf528SMauro Carvalho Chehab }
2679a0bf528SMauro Carvalho Chehab 
2689a0bf528SMauro Carvalho Chehab #define mb86a20s_readreg(state, reg) \
2699a0bf528SMauro Carvalho Chehab 	mb86a20s_i2c_readreg(state, state->config->demod_address, reg)
2709a0bf528SMauro Carvalho Chehab #define mb86a20s_writereg(state, reg, val) \
2719a0bf528SMauro Carvalho Chehab 	mb86a20s_i2c_writereg(state, state->config->demod_address, reg, val)
2729a0bf528SMauro Carvalho Chehab #define mb86a20s_writeregdata(state, regdata) \
2739a0bf528SMauro Carvalho Chehab 	mb86a20s_i2c_writeregdata(state, state->config->demod_address, \
2749a0bf528SMauro Carvalho Chehab 	regdata, ARRAY_SIZE(regdata))
2759a0bf528SMauro Carvalho Chehab 
27609b6d21eSMauro Carvalho Chehab /*
27709b6d21eSMauro Carvalho Chehab  * Ancillary internal routines (likely compiled inlined)
27809b6d21eSMauro Carvalho Chehab  *
27909b6d21eSMauro Carvalho Chehab  * The functions below assume that gateway lock has already obtained
28009b6d21eSMauro Carvalho Chehab  */
28109b6d21eSMauro Carvalho Chehab 
282dd4493efSMauro Carvalho Chehab static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status)
2839a0bf528SMauro Carvalho Chehab {
2849a0bf528SMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
285dd4493efSMauro Carvalho Chehab 	int val;
2869a0bf528SMauro Carvalho Chehab 
287dd4493efSMauro Carvalho Chehab 	*status = 0;
2889a0bf528SMauro Carvalho Chehab 
289dd4493efSMauro Carvalho Chehab 	val = mb86a20s_readreg(state, 0x0a) & 0xf;
290dd4493efSMauro Carvalho Chehab 	if (val < 0)
291dd4493efSMauro Carvalho Chehab 		return val;
2929a0bf528SMauro Carvalho Chehab 
293dd4493efSMauro Carvalho Chehab 	if (val >= 2)
294dd4493efSMauro Carvalho Chehab 		*status |= FE_HAS_SIGNAL;
2959a0bf528SMauro Carvalho Chehab 
296dd4493efSMauro Carvalho Chehab 	if (val >= 4)
297dd4493efSMauro Carvalho Chehab 		*status |= FE_HAS_CARRIER;
2989a0bf528SMauro Carvalho Chehab 
299dd4493efSMauro Carvalho Chehab 	if (val >= 5)
300dd4493efSMauro Carvalho Chehab 		*status |= FE_HAS_VITERBI;
3019a0bf528SMauro Carvalho Chehab 
302dd4493efSMauro Carvalho Chehab 	if (val >= 7)
303dd4493efSMauro Carvalho Chehab 		*status |= FE_HAS_SYNC;
3049a0bf528SMauro Carvalho Chehab 
305dd4493efSMauro Carvalho Chehab 	if (val >= 8)				/* Maybe 9? */
306dd4493efSMauro Carvalho Chehab 		*status |= FE_HAS_LOCK;
307dd4493efSMauro Carvalho Chehab 
308f66d81b5SMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s: Status = 0x%02x (state = %d)\n",
309f66d81b5SMauro Carvalho Chehab 		 __func__, *status, val);
310dd4493efSMauro Carvalho Chehab 
311dd4493efSMauro Carvalho Chehab 	return 0;
3129a0bf528SMauro Carvalho Chehab }
3139a0bf528SMauro Carvalho Chehab 
31409b6d21eSMauro Carvalho Chehab static int mb86a20s_read_signal_strength(struct dvb_frontend *fe)
3159a0bf528SMauro Carvalho Chehab {
3169a0bf528SMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
31709b6d21eSMauro Carvalho Chehab 	int rc;
3189a0bf528SMauro Carvalho Chehab 	unsigned rf_max, rf_min, rf;
3199a0bf528SMauro Carvalho Chehab 
3209a0bf528SMauro Carvalho Chehab 	/* Does a binary search to get RF strength */
3219a0bf528SMauro Carvalho Chehab 	rf_max = 0xfff;
3229a0bf528SMauro Carvalho Chehab 	rf_min = 0;
3239a0bf528SMauro Carvalho Chehab 	do {
3249a0bf528SMauro Carvalho Chehab 		rf = (rf_max + rf_min) / 2;
32509b6d21eSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x04, 0x1f);
32609b6d21eSMauro Carvalho Chehab 		if (rc < 0)
32709b6d21eSMauro Carvalho Chehab 			return rc;
32809b6d21eSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x05, rf >> 8);
32909b6d21eSMauro Carvalho Chehab 		if (rc < 0)
33009b6d21eSMauro Carvalho Chehab 			return rc;
33109b6d21eSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x04, 0x20);
33209b6d21eSMauro Carvalho Chehab 		if (rc < 0)
33309b6d21eSMauro Carvalho Chehab 			return rc;
33409b6d21eSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x04, rf);
33509b6d21eSMauro Carvalho Chehab 		if (rc < 0)
33609b6d21eSMauro Carvalho Chehab 			return rc;
3379a0bf528SMauro Carvalho Chehab 
33809b6d21eSMauro Carvalho Chehab 		rc = mb86a20s_readreg(state, 0x02);
33909b6d21eSMauro Carvalho Chehab 		if (rc < 0)
34009b6d21eSMauro Carvalho Chehab 			return rc;
34109b6d21eSMauro Carvalho Chehab 		if (rc & 0x08)
3429a0bf528SMauro Carvalho Chehab 			rf_min = (rf_max + rf_min) / 2;
3439a0bf528SMauro Carvalho Chehab 		else
3449a0bf528SMauro Carvalho Chehab 			rf_max = (rf_max + rf_min) / 2;
3459a0bf528SMauro Carvalho Chehab 		if (rf_max - rf_min < 4) {
34609b6d21eSMauro Carvalho Chehab 			rf = (rf_max + rf_min) / 2;
34709b6d21eSMauro Carvalho Chehab 
34809b6d21eSMauro Carvalho Chehab 			/* Rescale it from 2^12 (4096) to 2^16 */
34909b6d21eSMauro Carvalho Chehab 			rf <<= (16 - 12);
350f66d81b5SMauro Carvalho Chehab 			dev_dbg(&state->i2c->dev,
351f66d81b5SMauro Carvalho Chehab 				"%s: signal strength = %d (%d < RF=%d < %d)\n",
352f66d81b5SMauro Carvalho Chehab 				__func__, rf, rf_min, rf >> 4, rf_max);
35309b6d21eSMauro Carvalho Chehab 			return rf;
3549a0bf528SMauro Carvalho Chehab 		}
3559a0bf528SMauro Carvalho Chehab 	} while (1);
3569a0bf528SMauro Carvalho Chehab 
3579a0bf528SMauro Carvalho Chehab 	return 0;
3589a0bf528SMauro Carvalho Chehab }
3599a0bf528SMauro Carvalho Chehab 
3609a0bf528SMauro Carvalho Chehab static int mb86a20s_get_modulation(struct mb86a20s_state *state,
3619a0bf528SMauro Carvalho Chehab 				   unsigned layer)
3629a0bf528SMauro Carvalho Chehab {
3639a0bf528SMauro Carvalho Chehab 	int rc;
3649a0bf528SMauro Carvalho Chehab 	static unsigned char reg[] = {
3659a0bf528SMauro Carvalho Chehab 		[0] = 0x86,	/* Layer A */
3669a0bf528SMauro Carvalho Chehab 		[1] = 0x8a,	/* Layer B */
3679a0bf528SMauro Carvalho Chehab 		[2] = 0x8e,	/* Layer C */
3689a0bf528SMauro Carvalho Chehab 	};
3699a0bf528SMauro Carvalho Chehab 
3709a0bf528SMauro Carvalho Chehab 	if (layer >= ARRAY_SIZE(reg))
3719a0bf528SMauro Carvalho Chehab 		return -EINVAL;
3729a0bf528SMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x6d, reg[layer]);
3739a0bf528SMauro Carvalho Chehab 	if (rc < 0)
3749a0bf528SMauro Carvalho Chehab 		return rc;
3759a0bf528SMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x6e);
3769a0bf528SMauro Carvalho Chehab 	if (rc < 0)
3779a0bf528SMauro Carvalho Chehab 		return rc;
37804585921SMauro Carvalho Chehab 	switch ((rc >> 4) & 0x07) {
3799a0bf528SMauro Carvalho Chehab 	case 0:
3809a0bf528SMauro Carvalho Chehab 		return DQPSK;
3819a0bf528SMauro Carvalho Chehab 	case 1:
3829a0bf528SMauro Carvalho Chehab 		return QPSK;
3839a0bf528SMauro Carvalho Chehab 	case 2:
3849a0bf528SMauro Carvalho Chehab 		return QAM_16;
3859a0bf528SMauro Carvalho Chehab 	case 3:
3869a0bf528SMauro Carvalho Chehab 		return QAM_64;
3879a0bf528SMauro Carvalho Chehab 	default:
3889a0bf528SMauro Carvalho Chehab 		return QAM_AUTO;
3899a0bf528SMauro Carvalho Chehab 	}
3909a0bf528SMauro Carvalho Chehab }
3919a0bf528SMauro Carvalho Chehab 
3929a0bf528SMauro Carvalho Chehab static int mb86a20s_get_fec(struct mb86a20s_state *state,
3939a0bf528SMauro Carvalho Chehab 			    unsigned layer)
3949a0bf528SMauro Carvalho Chehab {
3959a0bf528SMauro Carvalho Chehab 	int rc;
3969a0bf528SMauro Carvalho Chehab 
3979a0bf528SMauro Carvalho Chehab 	static unsigned char reg[] = {
3989a0bf528SMauro Carvalho Chehab 		[0] = 0x87,	/* Layer A */
3999a0bf528SMauro Carvalho Chehab 		[1] = 0x8b,	/* Layer B */
4009a0bf528SMauro Carvalho Chehab 		[2] = 0x8f,	/* Layer C */
4019a0bf528SMauro Carvalho Chehab 	};
4029a0bf528SMauro Carvalho Chehab 
4039a0bf528SMauro Carvalho Chehab 	if (layer >= ARRAY_SIZE(reg))
4049a0bf528SMauro Carvalho Chehab 		return -EINVAL;
4059a0bf528SMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x6d, reg[layer]);
4069a0bf528SMauro Carvalho Chehab 	if (rc < 0)
4079a0bf528SMauro Carvalho Chehab 		return rc;
4089a0bf528SMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x6e);
4099a0bf528SMauro Carvalho Chehab 	if (rc < 0)
4109a0bf528SMauro Carvalho Chehab 		return rc;
41104585921SMauro Carvalho Chehab 	switch ((rc >> 4) & 0x07) {
4129a0bf528SMauro Carvalho Chehab 	case 0:
4139a0bf528SMauro Carvalho Chehab 		return FEC_1_2;
4149a0bf528SMauro Carvalho Chehab 	case 1:
4159a0bf528SMauro Carvalho Chehab 		return FEC_2_3;
4169a0bf528SMauro Carvalho Chehab 	case 2:
4179a0bf528SMauro Carvalho Chehab 		return FEC_3_4;
4189a0bf528SMauro Carvalho Chehab 	case 3:
4199a0bf528SMauro Carvalho Chehab 		return FEC_5_6;
4209a0bf528SMauro Carvalho Chehab 	case 4:
4219a0bf528SMauro Carvalho Chehab 		return FEC_7_8;
4229a0bf528SMauro Carvalho Chehab 	default:
4239a0bf528SMauro Carvalho Chehab 		return FEC_AUTO;
4249a0bf528SMauro Carvalho Chehab 	}
4259a0bf528SMauro Carvalho Chehab }
4269a0bf528SMauro Carvalho Chehab 
4279a0bf528SMauro Carvalho Chehab static int mb86a20s_get_interleaving(struct mb86a20s_state *state,
4289a0bf528SMauro Carvalho Chehab 				     unsigned layer)
4299a0bf528SMauro Carvalho Chehab {
4309a0bf528SMauro Carvalho Chehab 	int rc;
4319a0bf528SMauro Carvalho Chehab 
4329a0bf528SMauro Carvalho Chehab 	static unsigned char reg[] = {
4339a0bf528SMauro Carvalho Chehab 		[0] = 0x88,	/* Layer A */
4349a0bf528SMauro Carvalho Chehab 		[1] = 0x8c,	/* Layer B */
4359a0bf528SMauro Carvalho Chehab 		[2] = 0x90,	/* Layer C */
4369a0bf528SMauro Carvalho Chehab 	};
4379a0bf528SMauro Carvalho Chehab 
4389a0bf528SMauro Carvalho Chehab 	if (layer >= ARRAY_SIZE(reg))
4399a0bf528SMauro Carvalho Chehab 		return -EINVAL;
4409a0bf528SMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x6d, reg[layer]);
4419a0bf528SMauro Carvalho Chehab 	if (rc < 0)
4429a0bf528SMauro Carvalho Chehab 		return rc;
4439a0bf528SMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x6e);
4449a0bf528SMauro Carvalho Chehab 	if (rc < 0)
4459a0bf528SMauro Carvalho Chehab 		return rc;
44604585921SMauro Carvalho Chehab 
44704585921SMauro Carvalho Chehab 	switch ((rc >> 4) & 0x07) {
44804585921SMauro Carvalho Chehab 	case 1:
44904585921SMauro Carvalho Chehab 		return GUARD_INTERVAL_1_4;
45004585921SMauro Carvalho Chehab 	case 2:
45104585921SMauro Carvalho Chehab 		return GUARD_INTERVAL_1_8;
45204585921SMauro Carvalho Chehab 	case 3:
45304585921SMauro Carvalho Chehab 		return GUARD_INTERVAL_1_16;
45404585921SMauro Carvalho Chehab 	case 4:
45504585921SMauro Carvalho Chehab 		return GUARD_INTERVAL_1_32;
45604585921SMauro Carvalho Chehab 
45704585921SMauro Carvalho Chehab 	default:
45804585921SMauro Carvalho Chehab 	case 0:
45904585921SMauro Carvalho Chehab 		return GUARD_INTERVAL_AUTO;
46004585921SMauro Carvalho Chehab 	}
4619a0bf528SMauro Carvalho Chehab }
4629a0bf528SMauro Carvalho Chehab 
4639a0bf528SMauro Carvalho Chehab static int mb86a20s_get_segment_count(struct mb86a20s_state *state,
4649a0bf528SMauro Carvalho Chehab 				      unsigned layer)
4659a0bf528SMauro Carvalho Chehab {
4669a0bf528SMauro Carvalho Chehab 	int rc, count;
4679a0bf528SMauro Carvalho Chehab 	static unsigned char reg[] = {
4689a0bf528SMauro Carvalho Chehab 		[0] = 0x89,	/* Layer A */
4699a0bf528SMauro Carvalho Chehab 		[1] = 0x8d,	/* Layer B */
4709a0bf528SMauro Carvalho Chehab 		[2] = 0x91,	/* Layer C */
4719a0bf528SMauro Carvalho Chehab 	};
4729a0bf528SMauro Carvalho Chehab 
473f66d81b5SMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
474f66d81b5SMauro Carvalho Chehab 
4759a0bf528SMauro Carvalho Chehab 	if (layer >= ARRAY_SIZE(reg))
4769a0bf528SMauro Carvalho Chehab 		return -EINVAL;
477f66d81b5SMauro Carvalho Chehab 
4789a0bf528SMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x6d, reg[layer]);
4799a0bf528SMauro Carvalho Chehab 	if (rc < 0)
4809a0bf528SMauro Carvalho Chehab 		return rc;
4819a0bf528SMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x6e);
4829a0bf528SMauro Carvalho Chehab 	if (rc < 0)
4839a0bf528SMauro Carvalho Chehab 		return rc;
4849a0bf528SMauro Carvalho Chehab 	count = (rc >> 4) & 0x0f;
4859a0bf528SMauro Carvalho Chehab 
486f66d81b5SMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s: segments: %d.\n", __func__, count);
487f66d81b5SMauro Carvalho Chehab 
4889a0bf528SMauro Carvalho Chehab 	return count;
4899a0bf528SMauro Carvalho Chehab }
4909a0bf528SMauro Carvalho Chehab 
491a77cfcacSMauro Carvalho Chehab static void mb86a20s_reset_frontend_cache(struct dvb_frontend *fe)
492a77cfcacSMauro Carvalho Chehab {
493f66d81b5SMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
494a77cfcacSMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
495a77cfcacSMauro Carvalho Chehab 
496f66d81b5SMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
497f66d81b5SMauro Carvalho Chehab 
498a77cfcacSMauro Carvalho Chehab 	/* Fixed parameters */
499a77cfcacSMauro Carvalho Chehab 	c->delivery_system = SYS_ISDBT;
500a77cfcacSMauro Carvalho Chehab 	c->bandwidth_hz = 6000000;
501a77cfcacSMauro Carvalho Chehab 
502a77cfcacSMauro Carvalho Chehab 	/* Initialize values that will be later autodetected */
503a77cfcacSMauro Carvalho Chehab 	c->isdbt_layer_enabled = 0;
504a77cfcacSMauro Carvalho Chehab 	c->transmission_mode = TRANSMISSION_MODE_AUTO;
505a77cfcacSMauro Carvalho Chehab 	c->guard_interval = GUARD_INTERVAL_AUTO;
506a77cfcacSMauro Carvalho Chehab 	c->isdbt_sb_mode = 0;
507a77cfcacSMauro Carvalho Chehab 	c->isdbt_sb_segment_count = 0;
508a77cfcacSMauro Carvalho Chehab }
509a77cfcacSMauro Carvalho Chehab 
510d01a8ee3SMauro Carvalho Chehab /*
511d01a8ee3SMauro Carvalho Chehab  * Estimates the bit rate using the per-segment bit rate given by
512d01a8ee3SMauro Carvalho Chehab  * ABNT/NBR 15601 spec (table 4).
513d01a8ee3SMauro Carvalho Chehab  */
514d01a8ee3SMauro Carvalho Chehab static u32 isdbt_rate[3][5][4] = {
515d01a8ee3SMauro Carvalho Chehab 	{	/* DQPSK/QPSK */
516d01a8ee3SMauro Carvalho Chehab 		{  280850,  312060,  330420,  340430 },	/* 1/2 */
517d01a8ee3SMauro Carvalho Chehab 		{  374470,  416080,  440560,  453910 },	/* 2/3 */
518d01a8ee3SMauro Carvalho Chehab 		{  421280,  468090,  495630,  510650 },	/* 3/4 */
519d01a8ee3SMauro Carvalho Chehab 		{  468090,  520100,  550700,  567390 },	/* 5/6 */
520d01a8ee3SMauro Carvalho Chehab 		{  491500,  546110,  578230,  595760 },	/* 7/8 */
521d01a8ee3SMauro Carvalho Chehab 	}, {	/* QAM16 */
522d01a8ee3SMauro Carvalho Chehab 		{  561710,  624130,  660840,  680870 },	/* 1/2 */
523d01a8ee3SMauro Carvalho Chehab 		{  748950,  832170,  881120,  907820 },	/* 2/3 */
524d01a8ee3SMauro Carvalho Chehab 		{  842570,  936190,  991260, 1021300 },	/* 3/4 */
525d01a8ee3SMauro Carvalho Chehab 		{  936190, 1040210, 1101400, 1134780 },	/* 5/6 */
526d01a8ee3SMauro Carvalho Chehab 		{  983000, 1092220, 1156470, 1191520 },	/* 7/8 */
527d01a8ee3SMauro Carvalho Chehab 	}, {	/* QAM64 */
528d01a8ee3SMauro Carvalho Chehab 		{  842570,  936190,  991260, 1021300 },	/* 1/2 */
529d01a8ee3SMauro Carvalho Chehab 		{ 1123430, 1248260, 1321680, 1361740 },	/* 2/3 */
530d01a8ee3SMauro Carvalho Chehab 		{ 1263860, 1404290, 1486900, 1531950 },	/* 3/4 */
531d01a8ee3SMauro Carvalho Chehab 		{ 1404290, 1560320, 1652110, 1702170 },	/* 5/6 */
532d01a8ee3SMauro Carvalho Chehab 		{ 1474500, 1638340, 1734710, 1787280 },	/* 7/8 */
533d01a8ee3SMauro Carvalho Chehab 	}
534d01a8ee3SMauro Carvalho Chehab };
535d01a8ee3SMauro Carvalho Chehab 
536d01a8ee3SMauro Carvalho Chehab static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer,
537d01a8ee3SMauro Carvalho Chehab 				   u32 modulation, u32 fec, u32 interleaving,
538d01a8ee3SMauro Carvalho Chehab 				   u32 segment)
539d01a8ee3SMauro Carvalho Chehab {
540d01a8ee3SMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
541d01a8ee3SMauro Carvalho Chehab 	u32 rate;
542d01a8ee3SMauro Carvalho Chehab 	int m, f, i;
543d01a8ee3SMauro Carvalho Chehab 
544d01a8ee3SMauro Carvalho Chehab 	/*
545d01a8ee3SMauro Carvalho Chehab 	 * If modulation/fec/interleaving is not detected, the default is
546d01a8ee3SMauro Carvalho Chehab 	 * to consider the lowest bit rate, to avoid taking too long time
547d01a8ee3SMauro Carvalho Chehab 	 * to get BER.
548d01a8ee3SMauro Carvalho Chehab 	 */
549d01a8ee3SMauro Carvalho Chehab 	switch (modulation) {
550d01a8ee3SMauro Carvalho Chehab 	case DQPSK:
551d01a8ee3SMauro Carvalho Chehab 	case QPSK:
552d01a8ee3SMauro Carvalho Chehab 	default:
553d01a8ee3SMauro Carvalho Chehab 		m = 0;
554d01a8ee3SMauro Carvalho Chehab 		break;
555d01a8ee3SMauro Carvalho Chehab 	case QAM_16:
556d01a8ee3SMauro Carvalho Chehab 		m = 1;
557d01a8ee3SMauro Carvalho Chehab 		break;
558d01a8ee3SMauro Carvalho Chehab 	case QAM_64:
559d01a8ee3SMauro Carvalho Chehab 		m = 2;
560d01a8ee3SMauro Carvalho Chehab 		break;
561d01a8ee3SMauro Carvalho Chehab 	}
562d01a8ee3SMauro Carvalho Chehab 
563d01a8ee3SMauro Carvalho Chehab 	switch (fec) {
564d01a8ee3SMauro Carvalho Chehab 	default:
565d01a8ee3SMauro Carvalho Chehab 	case FEC_1_2:
566d01a8ee3SMauro Carvalho Chehab 	case FEC_AUTO:
567d01a8ee3SMauro Carvalho Chehab 		f = 0;
568d01a8ee3SMauro Carvalho Chehab 		break;
569d01a8ee3SMauro Carvalho Chehab 	case FEC_2_3:
570d01a8ee3SMauro Carvalho Chehab 		f = 1;
571d01a8ee3SMauro Carvalho Chehab 		break;
572d01a8ee3SMauro Carvalho Chehab 	case FEC_3_4:
573d01a8ee3SMauro Carvalho Chehab 		f = 2;
574d01a8ee3SMauro Carvalho Chehab 		break;
575d01a8ee3SMauro Carvalho Chehab 	case FEC_5_6:
576d01a8ee3SMauro Carvalho Chehab 		f = 3;
577d01a8ee3SMauro Carvalho Chehab 		break;
578d01a8ee3SMauro Carvalho Chehab 	case FEC_7_8:
579d01a8ee3SMauro Carvalho Chehab 		f = 4;
580d01a8ee3SMauro Carvalho Chehab 		break;
581d01a8ee3SMauro Carvalho Chehab 	}
582d01a8ee3SMauro Carvalho Chehab 
583d01a8ee3SMauro Carvalho Chehab 	switch (interleaving) {
584d01a8ee3SMauro Carvalho Chehab 	default:
585d01a8ee3SMauro Carvalho Chehab 	case GUARD_INTERVAL_1_4:
586d01a8ee3SMauro Carvalho Chehab 		i = 0;
587d01a8ee3SMauro Carvalho Chehab 		break;
588d01a8ee3SMauro Carvalho Chehab 	case GUARD_INTERVAL_1_8:
589d01a8ee3SMauro Carvalho Chehab 		i = 1;
590d01a8ee3SMauro Carvalho Chehab 		break;
591d01a8ee3SMauro Carvalho Chehab 	case GUARD_INTERVAL_1_16:
592d01a8ee3SMauro Carvalho Chehab 		i = 2;
593d01a8ee3SMauro Carvalho Chehab 		break;
594d01a8ee3SMauro Carvalho Chehab 	case GUARD_INTERVAL_1_32:
595d01a8ee3SMauro Carvalho Chehab 		i = 3;
596d01a8ee3SMauro Carvalho Chehab 		break;
597d01a8ee3SMauro Carvalho Chehab 	}
598d01a8ee3SMauro Carvalho Chehab 
599d01a8ee3SMauro Carvalho Chehab 	/* Samples BER at BER_SAMPLING_RATE seconds */
600d01a8ee3SMauro Carvalho Chehab 	rate = isdbt_rate[m][f][i] * segment * BER_SAMPLING_RATE;
601d01a8ee3SMauro Carvalho Chehab 
602d01a8ee3SMauro Carvalho Chehab 	/* Avoids sampling too quickly or to overflow the register */
603d01a8ee3SMauro Carvalho Chehab 	if (rate < 256)
604d01a8ee3SMauro Carvalho Chehab 		rate = 256;
605d01a8ee3SMauro Carvalho Chehab 	else if (rate > (1 << 24) - 1)
606d01a8ee3SMauro Carvalho Chehab 		rate = (1 << 24) - 1;
607d01a8ee3SMauro Carvalho Chehab 
608d01a8ee3SMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev,
609d01a8ee3SMauro Carvalho Chehab 		"%s: layer %c bitrate: %d kbps; counter = %d (0x%06x)\n",
610d01a8ee3SMauro Carvalho Chehab 	       __func__, 'A' + layer, segment * isdbt_rate[m][f][i]/1000,
611d01a8ee3SMauro Carvalho Chehab 		rate, rate);
612d01a8ee3SMauro Carvalho Chehab 
613d01a8ee3SMauro Carvalho Chehab 	state->estimated_rate[i] = rate;
614d01a8ee3SMauro Carvalho Chehab }
615d01a8ee3SMauro Carvalho Chehab 
616d01a8ee3SMauro Carvalho Chehab 
6179a0bf528SMauro Carvalho Chehab static int mb86a20s_get_frontend(struct dvb_frontend *fe)
6189a0bf528SMauro Carvalho Chehab {
6199a0bf528SMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
620a77cfcacSMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
6219a0bf528SMauro Carvalho Chehab 	int i, rc;
6229a0bf528SMauro Carvalho Chehab 
623f66d81b5SMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
624f66d81b5SMauro Carvalho Chehab 
625a77cfcacSMauro Carvalho Chehab 	/* Reset frontend cache to default values */
626a77cfcacSMauro Carvalho Chehab 	mb86a20s_reset_frontend_cache(fe);
6279a0bf528SMauro Carvalho Chehab 
6289a0bf528SMauro Carvalho Chehab 	/* Check for partial reception */
6299a0bf528SMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x6d, 0x85);
630a77cfcacSMauro Carvalho Chehab 	if (rc < 0)
631a77cfcacSMauro Carvalho Chehab 		return rc;
6329a0bf528SMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x6e);
633a77cfcacSMauro Carvalho Chehab 	if (rc < 0)
634a77cfcacSMauro Carvalho Chehab 		return rc;
635a77cfcacSMauro Carvalho Chehab 	c->isdbt_partial_reception = (rc & 0x10) ? 1 : 0;
6369a0bf528SMauro Carvalho Chehab 
6379a0bf528SMauro Carvalho Chehab 	/* Get per-layer data */
638a77cfcacSMauro Carvalho Chehab 
6399a0bf528SMauro Carvalho Chehab 	for (i = 0; i < 3; i++) {
640f66d81b5SMauro Carvalho Chehab 		dev_dbg(&state->i2c->dev, "%s: getting data for layer %c.\n",
641f66d81b5SMauro Carvalho Chehab 			__func__, 'A' + i);
642f66d81b5SMauro Carvalho Chehab 
6439a0bf528SMauro Carvalho Chehab 		rc = mb86a20s_get_segment_count(state, i);
644a77cfcacSMauro Carvalho Chehab 		if (rc < 0)
645f66d81b5SMauro Carvalho Chehab 			goto noperlayer_error;
646d01a8ee3SMauro Carvalho Chehab 		if (rc >= 0 && rc < 14) {
647a77cfcacSMauro Carvalho Chehab 			c->layer[i].segment_count = rc;
648d01a8ee3SMauro Carvalho Chehab 		} else {
649a77cfcacSMauro Carvalho Chehab 			c->layer[i].segment_count = 0;
650d01a8ee3SMauro Carvalho Chehab 			state->estimated_rate[i] = 0;
6519a0bf528SMauro Carvalho Chehab 			continue;
652a77cfcacSMauro Carvalho Chehab 		}
653a77cfcacSMauro Carvalho Chehab 		c->isdbt_layer_enabled |= 1 << i;
6549a0bf528SMauro Carvalho Chehab 		rc = mb86a20s_get_modulation(state, i);
655a77cfcacSMauro Carvalho Chehab 		if (rc < 0)
656f66d81b5SMauro Carvalho Chehab 			goto noperlayer_error;
657f66d81b5SMauro Carvalho Chehab 		dev_dbg(&state->i2c->dev, "%s: modulation %d.\n",
658f66d81b5SMauro Carvalho Chehab 			__func__, rc);
659a77cfcacSMauro Carvalho Chehab 		c->layer[i].modulation = rc;
6609a0bf528SMauro Carvalho Chehab 		rc = mb86a20s_get_fec(state, i);
661a77cfcacSMauro Carvalho Chehab 		if (rc < 0)
662f66d81b5SMauro Carvalho Chehab 			goto noperlayer_error;
663f66d81b5SMauro Carvalho Chehab 		dev_dbg(&state->i2c->dev, "%s: FEC %d.\n",
664f66d81b5SMauro Carvalho Chehab 			__func__, rc);
665a77cfcacSMauro Carvalho Chehab 		c->layer[i].fec = rc;
6669a0bf528SMauro Carvalho Chehab 		rc = mb86a20s_get_interleaving(state, i);
667a77cfcacSMauro Carvalho Chehab 		if (rc < 0)
668f66d81b5SMauro Carvalho Chehab 			goto noperlayer_error;
669f66d81b5SMauro Carvalho Chehab 		dev_dbg(&state->i2c->dev, "%s: interleaving %d.\n",
670f66d81b5SMauro Carvalho Chehab 			__func__, rc);
671a77cfcacSMauro Carvalho Chehab 		c->layer[i].interleaving = rc;
672d01a8ee3SMauro Carvalho Chehab 		mb86a20s_layer_bitrate(fe, i, c->layer[i].modulation,
673d01a8ee3SMauro Carvalho Chehab 				       c->layer[i].fec,
674d01a8ee3SMauro Carvalho Chehab 				       c->layer[i].interleaving,
675d01a8ee3SMauro Carvalho Chehab 				       c->layer[i].segment_count);
6769a0bf528SMauro Carvalho Chehab 	}
6779a0bf528SMauro Carvalho Chehab 
6789a0bf528SMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x6d, 0x84);
679a77cfcacSMauro Carvalho Chehab 	if (rc < 0)
680a77cfcacSMauro Carvalho Chehab 		return rc;
681a77cfcacSMauro Carvalho Chehab 	if ((rc & 0x60) == 0x20) {
682a77cfcacSMauro Carvalho Chehab 		c->isdbt_sb_mode = 1;
6839a0bf528SMauro Carvalho Chehab 		/* At least, one segment should exist */
684a77cfcacSMauro Carvalho Chehab 		if (!c->isdbt_sb_segment_count)
685a77cfcacSMauro Carvalho Chehab 			c->isdbt_sb_segment_count = 1;
686a77cfcacSMauro Carvalho Chehab 	}
6879a0bf528SMauro Carvalho Chehab 
6889a0bf528SMauro Carvalho Chehab 	/* Get transmission mode and guard interval */
6899a0bf528SMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x07);
690a77cfcacSMauro Carvalho Chehab 	if (rc < 0)
691a77cfcacSMauro Carvalho Chehab 		return rc;
6929a0bf528SMauro Carvalho Chehab 	if ((rc & 0x60) == 0x20) {
6939a0bf528SMauro Carvalho Chehab 		switch (rc & 0x0c >> 2) {
6949a0bf528SMauro Carvalho Chehab 		case 0:
695a77cfcacSMauro Carvalho Chehab 			c->transmission_mode = TRANSMISSION_MODE_2K;
6969a0bf528SMauro Carvalho Chehab 			break;
6979a0bf528SMauro Carvalho Chehab 		case 1:
698a77cfcacSMauro Carvalho Chehab 			c->transmission_mode = TRANSMISSION_MODE_4K;
6999a0bf528SMauro Carvalho Chehab 			break;
7009a0bf528SMauro Carvalho Chehab 		case 2:
701a77cfcacSMauro Carvalho Chehab 			c->transmission_mode = TRANSMISSION_MODE_8K;
7029a0bf528SMauro Carvalho Chehab 			break;
7039a0bf528SMauro Carvalho Chehab 		}
7049a0bf528SMauro Carvalho Chehab 	}
7059a0bf528SMauro Carvalho Chehab 	if (!(rc & 0x10)) {
7069a0bf528SMauro Carvalho Chehab 		switch (rc & 0x3) {
7079a0bf528SMauro Carvalho Chehab 		case 0:
708a77cfcacSMauro Carvalho Chehab 			c->guard_interval = GUARD_INTERVAL_1_4;
7099a0bf528SMauro Carvalho Chehab 			break;
7109a0bf528SMauro Carvalho Chehab 		case 1:
711a77cfcacSMauro Carvalho Chehab 			c->guard_interval = GUARD_INTERVAL_1_8;
7129a0bf528SMauro Carvalho Chehab 			break;
7139a0bf528SMauro Carvalho Chehab 		case 2:
714a77cfcacSMauro Carvalho Chehab 			c->guard_interval = GUARD_INTERVAL_1_16;
7159a0bf528SMauro Carvalho Chehab 			break;
7169a0bf528SMauro Carvalho Chehab 		}
7179a0bf528SMauro Carvalho Chehab 	}
71809b6d21eSMauro Carvalho Chehab 	return 0;
7199a0bf528SMauro Carvalho Chehab 
720f66d81b5SMauro Carvalho Chehab noperlayer_error:
72109b6d21eSMauro Carvalho Chehab 
72209b6d21eSMauro Carvalho Chehab 	/* per-layer info is incomplete; discard all per-layer */
72309b6d21eSMauro Carvalho Chehab 	c->isdbt_layer_enabled = 0;
7249a0bf528SMauro Carvalho Chehab 
725a77cfcacSMauro Carvalho Chehab 	return rc;
7269a0bf528SMauro Carvalho Chehab }
7279a0bf528SMauro Carvalho Chehab 
72809b6d21eSMauro Carvalho Chehab static int mb86a20s_reset_counters(struct dvb_frontend *fe)
72909b6d21eSMauro Carvalho Chehab {
73009b6d21eSMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
73109b6d21eSMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
73209b6d21eSMauro Carvalho Chehab 	int rc, val;
73309b6d21eSMauro Carvalho Chehab 
73409b6d21eSMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
73509b6d21eSMauro Carvalho Chehab 
73609b6d21eSMauro Carvalho Chehab 	/* Reset the counters, if the channel changed */
73709b6d21eSMauro Carvalho Chehab 	if (state->last_frequency != c->frequency) {
73809b6d21eSMauro Carvalho Chehab 		memset(&c->strength, 0, sizeof(c->strength));
73909b6d21eSMauro Carvalho Chehab 		memset(&c->cnr, 0, sizeof(c->cnr));
74009b6d21eSMauro Carvalho Chehab 		memset(&c->pre_bit_error, 0, sizeof(c->pre_bit_error));
74109b6d21eSMauro Carvalho Chehab 		memset(&c->pre_bit_count, 0, sizeof(c->pre_bit_count));
742d9b6f08aSMauro Carvalho Chehab 		memset(&c->post_bit_error, 0, sizeof(c->post_bit_error));
743d9b6f08aSMauro Carvalho Chehab 		memset(&c->post_bit_count, 0, sizeof(c->post_bit_count));
74409b6d21eSMauro Carvalho Chehab 		memset(&c->block_error, 0, sizeof(c->block_error));
74509b6d21eSMauro Carvalho Chehab 		memset(&c->block_count, 0, sizeof(c->block_count));
74609b6d21eSMauro Carvalho Chehab 
74709b6d21eSMauro Carvalho Chehab 		state->last_frequency = c->frequency;
74809b6d21eSMauro Carvalho Chehab 	}
74909b6d21eSMauro Carvalho Chehab 
75009b6d21eSMauro Carvalho Chehab 	/* Clear status for most stats */
75109b6d21eSMauro Carvalho Chehab 
752d9b6f08aSMauro Carvalho Chehab 	/* BER/PER counter reset */
753d9b6f08aSMauro Carvalho Chehab 	rc = mb86a20s_writeregdata(state, mb86a20s_per_ber_reset);
75409b6d21eSMauro Carvalho Chehab 	if (rc < 0)
75509b6d21eSMauro Carvalho Chehab 		goto err;
75609b6d21eSMauro Carvalho Chehab 
75709b6d21eSMauro Carvalho Chehab 	/* CNR counter reset */
75809b6d21eSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x45);
75909b6d21eSMauro Carvalho Chehab 	if (rc < 0)
76009b6d21eSMauro Carvalho Chehab 		goto err;
76109b6d21eSMauro Carvalho Chehab 	val = rc;
76209b6d21eSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x45, val | 0x10);
76309b6d21eSMauro Carvalho Chehab 	if (rc < 0)
76409b6d21eSMauro Carvalho Chehab 		goto err;
76509b6d21eSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x45, val & 0x6f);
76609b6d21eSMauro Carvalho Chehab 	if (rc < 0)
76709b6d21eSMauro Carvalho Chehab 		goto err;
76809b6d21eSMauro Carvalho Chehab 
76909b6d21eSMauro Carvalho Chehab 	/* MER counter reset */
77009b6d21eSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x50, 0x50);
77109b6d21eSMauro Carvalho Chehab 	if (rc < 0)
77209b6d21eSMauro Carvalho Chehab 		goto err;
77309b6d21eSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x51);
77409b6d21eSMauro Carvalho Chehab 	if (rc < 0)
77509b6d21eSMauro Carvalho Chehab 		goto err;
77609b6d21eSMauro Carvalho Chehab 	val = rc;
77709b6d21eSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x51, val | 0x01);
77809b6d21eSMauro Carvalho Chehab 	if (rc < 0)
77909b6d21eSMauro Carvalho Chehab 		goto err;
78009b6d21eSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x51, val & 0x06);
78109b6d21eSMauro Carvalho Chehab 	if (rc < 0)
78209b6d21eSMauro Carvalho Chehab 		goto err;
78309b6d21eSMauro Carvalho Chehab 
784149d518aSMauro Carvalho Chehab 	goto ok;
78509b6d21eSMauro Carvalho Chehab err:
786149d518aSMauro Carvalho Chehab 	dev_err(&state->i2c->dev,
787149d518aSMauro Carvalho Chehab 		"%s: Can't reset FE statistics (error %d).\n",
788149d518aSMauro Carvalho Chehab 		__func__, rc);
789149d518aSMauro Carvalho Chehab ok:
79009b6d21eSMauro Carvalho Chehab 	return rc;
79109b6d21eSMauro Carvalho Chehab }
79209b6d21eSMauro Carvalho Chehab 
793ad0abbf1SMauro Carvalho Chehab static int mb86a20s_get_pre_ber(struct dvb_frontend *fe,
794149d518aSMauro Carvalho Chehab 				unsigned layer,
795149d518aSMauro Carvalho Chehab 				u32 *error, u32 *count)
796149d518aSMauro Carvalho Chehab {
797149d518aSMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
798ad0abbf1SMauro Carvalho Chehab 	int rc, val;
799149d518aSMauro Carvalho Chehab 
800149d518aSMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
801149d518aSMauro Carvalho Chehab 
802149d518aSMauro Carvalho Chehab 	if (layer >= 3)
803149d518aSMauro Carvalho Chehab 		return -EINVAL;
804149d518aSMauro Carvalho Chehab 
805149d518aSMauro Carvalho Chehab 	/* Check if the BER measures are already available */
806149d518aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x54);
807149d518aSMauro Carvalho Chehab 	if (rc < 0)
808149d518aSMauro Carvalho Chehab 		return rc;
809149d518aSMauro Carvalho Chehab 
810149d518aSMauro Carvalho Chehab 	/* Check if data is available for that layer */
811149d518aSMauro Carvalho Chehab 	if (!(rc & (1 << layer))) {
812149d518aSMauro Carvalho Chehab 		dev_dbg(&state->i2c->dev,
813ad0abbf1SMauro Carvalho Chehab 			"%s: preBER for layer %c is not available yet.\n",
814149d518aSMauro Carvalho Chehab 			__func__, 'A' + layer);
815149d518aSMauro Carvalho Chehab 		return -EBUSY;
816149d518aSMauro Carvalho Chehab 	}
817149d518aSMauro Carvalho Chehab 
818149d518aSMauro Carvalho Chehab 	/* Read Bit Error Count */
819149d518aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x55 + layer * 3);
820149d518aSMauro Carvalho Chehab 	if (rc < 0)
821149d518aSMauro Carvalho Chehab 		return rc;
822149d518aSMauro Carvalho Chehab 	*error = rc << 16;
823149d518aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x56 + layer * 3);
824149d518aSMauro Carvalho Chehab 	if (rc < 0)
825149d518aSMauro Carvalho Chehab 		return rc;
826149d518aSMauro Carvalho Chehab 	*error |= rc << 8;
827149d518aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x57 + layer * 3);
828149d518aSMauro Carvalho Chehab 	if (rc < 0)
829149d518aSMauro Carvalho Chehab 		return rc;
830149d518aSMauro Carvalho Chehab 	*error |= rc;
831149d518aSMauro Carvalho Chehab 
832149d518aSMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev,
833149d518aSMauro Carvalho Chehab 		"%s: bit error before Viterbi for layer %c: %d.\n",
834149d518aSMauro Carvalho Chehab 		__func__, 'A' + layer, *error);
835149d518aSMauro Carvalho Chehab 
836149d518aSMauro Carvalho Chehab 	/* Read Bit Count */
837149d518aSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x50, 0xa7 + layer * 3);
838149d518aSMauro Carvalho Chehab 	if (rc < 0)
839149d518aSMauro Carvalho Chehab 		return rc;
840149d518aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x51);
841149d518aSMauro Carvalho Chehab 	if (rc < 0)
842149d518aSMauro Carvalho Chehab 		return rc;
843149d518aSMauro Carvalho Chehab 	*count = rc << 16;
844149d518aSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x50, 0xa8 + layer * 3);
845149d518aSMauro Carvalho Chehab 	if (rc < 0)
846149d518aSMauro Carvalho Chehab 		return rc;
847149d518aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x51);
848149d518aSMauro Carvalho Chehab 	if (rc < 0)
849149d518aSMauro Carvalho Chehab 		return rc;
850149d518aSMauro Carvalho Chehab 	*count |= rc << 8;
851149d518aSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x50, 0xa9 + layer * 3);
852149d518aSMauro Carvalho Chehab 	if (rc < 0)
853149d518aSMauro Carvalho Chehab 		return rc;
854149d518aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x51);
855149d518aSMauro Carvalho Chehab 	if (rc < 0)
856149d518aSMauro Carvalho Chehab 		return rc;
857149d518aSMauro Carvalho Chehab 	*count |= rc;
858149d518aSMauro Carvalho Chehab 
859149d518aSMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev,
860149d518aSMauro Carvalho Chehab 		"%s: bit count before Viterbi for layer %c: %d.\n",
861149d518aSMauro Carvalho Chehab 		__func__, 'A' + layer, *count);
862149d518aSMauro Carvalho Chehab 
863149d518aSMauro Carvalho Chehab 
864d01a8ee3SMauro Carvalho Chehab 	/*
865d01a8ee3SMauro Carvalho Chehab 	 * As we get TMCC data from the frontend, we can better estimate the
866d01a8ee3SMauro Carvalho Chehab 	 * BER bit counters, in order to do the BER measure during a longer
867d01a8ee3SMauro Carvalho Chehab 	 * time. Use those data, if available, to update the bit count
868d01a8ee3SMauro Carvalho Chehab 	 * measure.
869d01a8ee3SMauro Carvalho Chehab 	 */
870d01a8ee3SMauro Carvalho Chehab 
871d01a8ee3SMauro Carvalho Chehab 	if (state->estimated_rate[layer]
872d01a8ee3SMauro Carvalho Chehab 	    && state->estimated_rate[layer] != *count) {
873d01a8ee3SMauro Carvalho Chehab 		dev_dbg(&state->i2c->dev,
874ad0abbf1SMauro Carvalho Chehab 			"%s: updating layer %c preBER counter to %d.\n",
875d01a8ee3SMauro Carvalho Chehab 			__func__, 'A' + layer, state->estimated_rate[layer]);
876ad0abbf1SMauro Carvalho Chehab 
877ad0abbf1SMauro Carvalho Chehab 		/* Turn off BER before Viterbi */
878ad0abbf1SMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x52, 0x00);
879ad0abbf1SMauro Carvalho Chehab 
880ad0abbf1SMauro Carvalho Chehab 		/* Update counter for this layer */
881d01a8ee3SMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x50, 0xa7 + layer * 3);
882d01a8ee3SMauro Carvalho Chehab 		if (rc < 0)
883d01a8ee3SMauro Carvalho Chehab 			return rc;
884d01a8ee3SMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x51,
885d01a8ee3SMauro Carvalho Chehab 				       state->estimated_rate[layer] >> 16);
886d01a8ee3SMauro Carvalho Chehab 		if (rc < 0)
887d01a8ee3SMauro Carvalho Chehab 			return rc;
888d01a8ee3SMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x50, 0xa8 + layer * 3);
889d01a8ee3SMauro Carvalho Chehab 		if (rc < 0)
890d01a8ee3SMauro Carvalho Chehab 			return rc;
891d01a8ee3SMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x51,
892d01a8ee3SMauro Carvalho Chehab 				       state->estimated_rate[layer] >> 8);
893d01a8ee3SMauro Carvalho Chehab 		if (rc < 0)
894d01a8ee3SMauro Carvalho Chehab 			return rc;
895d01a8ee3SMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x50, 0xa9 + layer * 3);
896d01a8ee3SMauro Carvalho Chehab 		if (rc < 0)
897d01a8ee3SMauro Carvalho Chehab 			return rc;
898d01a8ee3SMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x51,
899d01a8ee3SMauro Carvalho Chehab 				       state->estimated_rate[layer]);
900d01a8ee3SMauro Carvalho Chehab 		if (rc < 0)
901d01a8ee3SMauro Carvalho Chehab 			return rc;
902ad0abbf1SMauro Carvalho Chehab 
903ad0abbf1SMauro Carvalho Chehab 		/* Turn on BER before Viterbi */
904ad0abbf1SMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x52, 0x01);
905ad0abbf1SMauro Carvalho Chehab 
906ad0abbf1SMauro Carvalho Chehab 		/* Reset all preBER counters */
907ad0abbf1SMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x53, 0x00);
908ad0abbf1SMauro Carvalho Chehab 		if (rc < 0)
909ad0abbf1SMauro Carvalho Chehab 			return rc;
910ad0abbf1SMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x53, 0x07);
911ad0abbf1SMauro Carvalho Chehab 	} else {
912ad0abbf1SMauro Carvalho Chehab 		/* Reset counter to collect new data */
913ad0abbf1SMauro Carvalho Chehab 		rc = mb86a20s_readreg(state, 0x53);
914ad0abbf1SMauro Carvalho Chehab 		if (rc < 0)
915ad0abbf1SMauro Carvalho Chehab 			return rc;
916ad0abbf1SMauro Carvalho Chehab 		val = rc;
917ad0abbf1SMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x53, val & ~(1 << layer));
918ad0abbf1SMauro Carvalho Chehab 		if (rc < 0)
919ad0abbf1SMauro Carvalho Chehab 			return rc;
920ad0abbf1SMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x53, val | (1 << layer));
921d01a8ee3SMauro Carvalho Chehab 	}
922d01a8ee3SMauro Carvalho Chehab 
923d9b6f08aSMauro Carvalho Chehab 	return rc;
924d9b6f08aSMauro Carvalho Chehab }
925d01a8ee3SMauro Carvalho Chehab 
926d9b6f08aSMauro Carvalho Chehab static int mb86a20s_get_post_ber(struct dvb_frontend *fe,
927d9b6f08aSMauro Carvalho Chehab 				 unsigned layer,
928d9b6f08aSMauro Carvalho Chehab 				  u32 *error, u32 *count)
929d9b6f08aSMauro Carvalho Chehab {
930d9b6f08aSMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
931d9b6f08aSMauro Carvalho Chehab 	u32 counter, collect_rate;
932d9b6f08aSMauro Carvalho Chehab 	int rc, val;
933d9b6f08aSMauro Carvalho Chehab 
934d9b6f08aSMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
935d9b6f08aSMauro Carvalho Chehab 
936d9b6f08aSMauro Carvalho Chehab 	if (layer >= 3)
937d9b6f08aSMauro Carvalho Chehab 		return -EINVAL;
938d9b6f08aSMauro Carvalho Chehab 
939d9b6f08aSMauro Carvalho Chehab 	/* Check if the BER measures are already available */
940d9b6f08aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x60);
941d9b6f08aSMauro Carvalho Chehab 	if (rc < 0)
942d9b6f08aSMauro Carvalho Chehab 		return rc;
943d9b6f08aSMauro Carvalho Chehab 
944d9b6f08aSMauro Carvalho Chehab 	/* Check if data is available for that layer */
945d9b6f08aSMauro Carvalho Chehab 	if (!(rc & (1 << layer))) {
946d9b6f08aSMauro Carvalho Chehab 		dev_dbg(&state->i2c->dev,
947d9b6f08aSMauro Carvalho Chehab 			"%s: post BER for layer %c is not available yet.\n",
948d9b6f08aSMauro Carvalho Chehab 			__func__, 'A' + layer);
949d9b6f08aSMauro Carvalho Chehab 		return -EBUSY;
950d9b6f08aSMauro Carvalho Chehab 	}
951d9b6f08aSMauro Carvalho Chehab 
952d9b6f08aSMauro Carvalho Chehab 	/* Read Bit Error Count */
953d9b6f08aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x64 + layer * 3);
954d9b6f08aSMauro Carvalho Chehab 	if (rc < 0)
955d9b6f08aSMauro Carvalho Chehab 		return rc;
956d9b6f08aSMauro Carvalho Chehab 	*error = rc << 16;
957d9b6f08aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x65 + layer * 3);
958d9b6f08aSMauro Carvalho Chehab 	if (rc < 0)
959d9b6f08aSMauro Carvalho Chehab 		return rc;
960d9b6f08aSMauro Carvalho Chehab 	*error |= rc << 8;
961d9b6f08aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x66 + layer * 3);
962d9b6f08aSMauro Carvalho Chehab 	if (rc < 0)
963d9b6f08aSMauro Carvalho Chehab 		return rc;
964d9b6f08aSMauro Carvalho Chehab 	*error |= rc;
965d9b6f08aSMauro Carvalho Chehab 
966d9b6f08aSMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev,
967d9b6f08aSMauro Carvalho Chehab 		"%s: post bit error for layer %c: %d.\n",
968d9b6f08aSMauro Carvalho Chehab 		__func__, 'A' + layer, *error);
969d9b6f08aSMauro Carvalho Chehab 
970d9b6f08aSMauro Carvalho Chehab 	/* Read Bit Count */
971d9b6f08aSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x50, 0xdc + layer * 2);
972d9b6f08aSMauro Carvalho Chehab 	if (rc < 0)
973d9b6f08aSMauro Carvalho Chehab 		return rc;
974d9b6f08aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x51);
975d9b6f08aSMauro Carvalho Chehab 	if (rc < 0)
976d9b6f08aSMauro Carvalho Chehab 		return rc;
977d9b6f08aSMauro Carvalho Chehab 	counter = rc << 8;
978d9b6f08aSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x50, 0xdd + layer * 2);
979d9b6f08aSMauro Carvalho Chehab 	if (rc < 0)
980d9b6f08aSMauro Carvalho Chehab 		return rc;
981d9b6f08aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x51);
982d9b6f08aSMauro Carvalho Chehab 	if (rc < 0)
983d9b6f08aSMauro Carvalho Chehab 		return rc;
984d9b6f08aSMauro Carvalho Chehab 	counter |= rc;
985d9b6f08aSMauro Carvalho Chehab 	*count = counter * 204 * 8;
986d9b6f08aSMauro Carvalho Chehab 
987d9b6f08aSMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev,
988d9b6f08aSMauro Carvalho Chehab 		"%s: post bit count for layer %c: %d.\n",
989d9b6f08aSMauro Carvalho Chehab 		__func__, 'A' + layer, *count);
990d9b6f08aSMauro Carvalho Chehab 
991d9b6f08aSMauro Carvalho Chehab 	/*
992d9b6f08aSMauro Carvalho Chehab 	 * As we get TMCC data from the frontend, we can better estimate the
993d9b6f08aSMauro Carvalho Chehab 	 * BER bit counters, in order to do the BER measure during a longer
994d9b6f08aSMauro Carvalho Chehab 	 * time. Use those data, if available, to update the bit count
995d9b6f08aSMauro Carvalho Chehab 	 * measure.
996d9b6f08aSMauro Carvalho Chehab 	 */
997d9b6f08aSMauro Carvalho Chehab 
998d9b6f08aSMauro Carvalho Chehab 	if (!state->estimated_rate[layer])
999d9b6f08aSMauro Carvalho Chehab 		goto reset_measurement;
1000d9b6f08aSMauro Carvalho Chehab 
1001d9b6f08aSMauro Carvalho Chehab 	collect_rate = state->estimated_rate[layer] / 204 / 8;
1002d9b6f08aSMauro Carvalho Chehab 	if (collect_rate < 32)
1003d9b6f08aSMauro Carvalho Chehab 		collect_rate = 32;
1004d9b6f08aSMauro Carvalho Chehab 	if (collect_rate > 65535)
1005d9b6f08aSMauro Carvalho Chehab 		collect_rate = 65535;
1006d9b6f08aSMauro Carvalho Chehab 	if (collect_rate != counter) {
1007d9b6f08aSMauro Carvalho Chehab 		dev_dbg(&state->i2c->dev,
1008d9b6f08aSMauro Carvalho Chehab 			"%s: updating postBER counter on layer %c to %d.\n",
1009d9b6f08aSMauro Carvalho Chehab 			__func__, 'A' + layer, collect_rate);
1010d9b6f08aSMauro Carvalho Chehab 
1011d9b6f08aSMauro Carvalho Chehab 		/* Turn off BER after Viterbi */
1012d9b6f08aSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x5e, 0x00);
1013d9b6f08aSMauro Carvalho Chehab 
1014d9b6f08aSMauro Carvalho Chehab 		/* Update counter for this layer */
1015d9b6f08aSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x50, 0xdc + layer * 2);
1016d9b6f08aSMauro Carvalho Chehab 		if (rc < 0)
1017d9b6f08aSMauro Carvalho Chehab 			return rc;
1018d9b6f08aSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x51, collect_rate >> 8);
1019d9b6f08aSMauro Carvalho Chehab 		if (rc < 0)
1020d9b6f08aSMauro Carvalho Chehab 			return rc;
1021d9b6f08aSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x50, 0xdd + layer * 2);
1022d9b6f08aSMauro Carvalho Chehab 		if (rc < 0)
1023d9b6f08aSMauro Carvalho Chehab 			return rc;
1024d9b6f08aSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x51, collect_rate & 0xff);
1025d9b6f08aSMauro Carvalho Chehab 		if (rc < 0)
1026d9b6f08aSMauro Carvalho Chehab 			return rc;
1027d9b6f08aSMauro Carvalho Chehab 
1028d9b6f08aSMauro Carvalho Chehab 		/* Turn on BER after Viterbi */
1029d9b6f08aSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x5e, 0x07);
1030d9b6f08aSMauro Carvalho Chehab 
1031d9b6f08aSMauro Carvalho Chehab 		/* Reset all preBER counters */
1032d9b6f08aSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x5f, 0x00);
1033d9b6f08aSMauro Carvalho Chehab 		if (rc < 0)
1034d9b6f08aSMauro Carvalho Chehab 			return rc;
1035d9b6f08aSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x5f, 0x07);
1036d9b6f08aSMauro Carvalho Chehab 
1037d9b6f08aSMauro Carvalho Chehab 		return rc;
1038d9b6f08aSMauro Carvalho Chehab 	}
1039d9b6f08aSMauro Carvalho Chehab 
1040d9b6f08aSMauro Carvalho Chehab reset_measurement:
1041149d518aSMauro Carvalho Chehab 	/* Reset counter to collect new data */
1042ad0abbf1SMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x5f);
1043149d518aSMauro Carvalho Chehab 	if (rc < 0)
1044149d518aSMauro Carvalho Chehab 		return rc;
1045ad0abbf1SMauro Carvalho Chehab 	val = rc;
1046ad0abbf1SMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x5f, val & ~(1 << layer));
1047ad0abbf1SMauro Carvalho Chehab 	if (rc < 0)
1048ad0abbf1SMauro Carvalho Chehab 		return rc;
1049d9b6f08aSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x5f, val | (1 << layer));
1050149d518aSMauro Carvalho Chehab 
1051ad0abbf1SMauro Carvalho Chehab 	return rc;
1052149d518aSMauro Carvalho Chehab }
1053149d518aSMauro Carvalho Chehab 
1054593ae89aSMauro Carvalho Chehab static int mb86a20s_get_blk_error(struct dvb_frontend *fe,
1055593ae89aSMauro Carvalho Chehab 			    unsigned layer,
1056593ae89aSMauro Carvalho Chehab 			    u32 *error, u32 *count)
1057593ae89aSMauro Carvalho Chehab {
1058593ae89aSMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
1059313cf4efSMauro Carvalho Chehab 	int rc, val;
1060593ae89aSMauro Carvalho Chehab 	u32 collect_rate;
1061593ae89aSMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
1062593ae89aSMauro Carvalho Chehab 
1063593ae89aSMauro Carvalho Chehab 	if (layer >= 3)
1064593ae89aSMauro Carvalho Chehab 		return -EINVAL;
1065593ae89aSMauro Carvalho Chehab 
1066593ae89aSMauro Carvalho Chehab 	/* Check if the PER measures are already available */
1067593ae89aSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x50, 0xb8);
1068593ae89aSMauro Carvalho Chehab 	if (rc < 0)
1069593ae89aSMauro Carvalho Chehab 		return rc;
1070593ae89aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x51);
1071593ae89aSMauro Carvalho Chehab 	if (rc < 0)
1072593ae89aSMauro Carvalho Chehab 		return rc;
1073593ae89aSMauro Carvalho Chehab 
1074593ae89aSMauro Carvalho Chehab 	/* Check if data is available for that layer */
1075593ae89aSMauro Carvalho Chehab 
1076593ae89aSMauro Carvalho Chehab 	if (!(rc & (1 << layer))) {
1077593ae89aSMauro Carvalho Chehab 		dev_dbg(&state->i2c->dev,
1078593ae89aSMauro Carvalho Chehab 			"%s: block counts for layer %c aren't available yet.\n",
1079593ae89aSMauro Carvalho Chehab 			__func__, 'A' + layer);
1080593ae89aSMauro Carvalho Chehab 		return -EBUSY;
1081593ae89aSMauro Carvalho Chehab 	}
1082593ae89aSMauro Carvalho Chehab 
1083593ae89aSMauro Carvalho Chehab 	/* Read Packet error Count */
1084593ae89aSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x50, 0xb9 + layer * 2);
1085593ae89aSMauro Carvalho Chehab 	if (rc < 0)
1086593ae89aSMauro Carvalho Chehab 		return rc;
1087593ae89aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x51);
1088593ae89aSMauro Carvalho Chehab 	if (rc < 0)
1089593ae89aSMauro Carvalho Chehab 		return rc;
1090593ae89aSMauro Carvalho Chehab 	*error = rc << 8;
1091593ae89aSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x50, 0xba + layer * 2);
1092593ae89aSMauro Carvalho Chehab 	if (rc < 0)
1093593ae89aSMauro Carvalho Chehab 		return rc;
1094593ae89aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x51);
1095593ae89aSMauro Carvalho Chehab 	if (rc < 0)
1096593ae89aSMauro Carvalho Chehab 		return rc;
1097593ae89aSMauro Carvalho Chehab 	*error |= rc;
1098d56e326fSMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s: block error for layer %c: %d.\n",
1099593ae89aSMauro Carvalho Chehab 		__func__, 'A' + layer, *error);
1100593ae89aSMauro Carvalho Chehab 
1101593ae89aSMauro Carvalho Chehab 	/* Read Bit Count */
1102593ae89aSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x50, 0xb2 + layer * 2);
1103593ae89aSMauro Carvalho Chehab 	if (rc < 0)
1104593ae89aSMauro Carvalho Chehab 		return rc;
1105593ae89aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x51);
1106593ae89aSMauro Carvalho Chehab 	if (rc < 0)
1107593ae89aSMauro Carvalho Chehab 		return rc;
1108593ae89aSMauro Carvalho Chehab 	*count = rc << 8;
1109593ae89aSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x50, 0xb3 + layer * 2);
1110593ae89aSMauro Carvalho Chehab 	if (rc < 0)
1111593ae89aSMauro Carvalho Chehab 		return rc;
1112593ae89aSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x51);
1113593ae89aSMauro Carvalho Chehab 	if (rc < 0)
1114593ae89aSMauro Carvalho Chehab 		return rc;
1115593ae89aSMauro Carvalho Chehab 	*count |= rc;
1116593ae89aSMauro Carvalho Chehab 
1117593ae89aSMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev,
1118593ae89aSMauro Carvalho Chehab 		"%s: block count for layer %c: %d.\n",
1119593ae89aSMauro Carvalho Chehab 		__func__, 'A' + layer, *count);
1120593ae89aSMauro Carvalho Chehab 
1121593ae89aSMauro Carvalho Chehab 	/*
1122593ae89aSMauro Carvalho Chehab 	 * As we get TMCC data from the frontend, we can better estimate the
1123593ae89aSMauro Carvalho Chehab 	 * BER bit counters, in order to do the BER measure during a longer
1124593ae89aSMauro Carvalho Chehab 	 * time. Use those data, if available, to update the bit count
1125593ae89aSMauro Carvalho Chehab 	 * measure.
1126593ae89aSMauro Carvalho Chehab 	 */
1127593ae89aSMauro Carvalho Chehab 
1128593ae89aSMauro Carvalho Chehab 	if (!state->estimated_rate[layer])
1129593ae89aSMauro Carvalho Chehab 		goto reset_measurement;
1130593ae89aSMauro Carvalho Chehab 
1131593ae89aSMauro Carvalho Chehab 	collect_rate = state->estimated_rate[layer] / 204 / 8;
1132593ae89aSMauro Carvalho Chehab 	if (collect_rate < 32)
1133593ae89aSMauro Carvalho Chehab 		collect_rate = 32;
1134593ae89aSMauro Carvalho Chehab 	if (collect_rate > 65535)
1135593ae89aSMauro Carvalho Chehab 		collect_rate = 65535;
1136593ae89aSMauro Carvalho Chehab 
1137593ae89aSMauro Carvalho Chehab 	if (collect_rate != *count) {
1138593ae89aSMauro Carvalho Chehab 		dev_dbg(&state->i2c->dev,
1139593ae89aSMauro Carvalho Chehab 			"%s: updating PER counter on layer %c to %d.\n",
1140593ae89aSMauro Carvalho Chehab 			__func__, 'A' + layer, collect_rate);
1141313cf4efSMauro Carvalho Chehab 
1142313cf4efSMauro Carvalho Chehab 		/* Stop PER measurement */
1143313cf4efSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x50, 0xb0);
1144313cf4efSMauro Carvalho Chehab 		if (rc < 0)
1145313cf4efSMauro Carvalho Chehab 			return rc;
1146313cf4efSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x51, 0x00);
1147313cf4efSMauro Carvalho Chehab 		if (rc < 0)
1148313cf4efSMauro Carvalho Chehab 			return rc;
1149313cf4efSMauro Carvalho Chehab 
1150313cf4efSMauro Carvalho Chehab 		/* Update this layer's counter */
1151593ae89aSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x50, 0xb2 + layer * 2);
1152593ae89aSMauro Carvalho Chehab 		if (rc < 0)
1153593ae89aSMauro Carvalho Chehab 			return rc;
1154593ae89aSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x51, collect_rate >> 8);
1155593ae89aSMauro Carvalho Chehab 		if (rc < 0)
1156593ae89aSMauro Carvalho Chehab 			return rc;
1157593ae89aSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x50, 0xb3 + layer * 2);
1158593ae89aSMauro Carvalho Chehab 		if (rc < 0)
1159593ae89aSMauro Carvalho Chehab 			return rc;
1160593ae89aSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x51, collect_rate & 0xff);
1161593ae89aSMauro Carvalho Chehab 		if (rc < 0)
1162593ae89aSMauro Carvalho Chehab 			return rc;
1163313cf4efSMauro Carvalho Chehab 
1164313cf4efSMauro Carvalho Chehab 		/* start PER measurement */
1165313cf4efSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x50, 0xb0);
1166313cf4efSMauro Carvalho Chehab 		if (rc < 0)
1167313cf4efSMauro Carvalho Chehab 			return rc;
1168313cf4efSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x51, 0x07);
1169313cf4efSMauro Carvalho Chehab 		if (rc < 0)
1170313cf4efSMauro Carvalho Chehab 			return rc;
1171313cf4efSMauro Carvalho Chehab 
1172313cf4efSMauro Carvalho Chehab 		/* Reset all counters to collect new data */
1173313cf4efSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x50, 0xb1);
1174313cf4efSMauro Carvalho Chehab 		if (rc < 0)
1175313cf4efSMauro Carvalho Chehab 			return rc;
1176313cf4efSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x51, 0x07);
1177313cf4efSMauro Carvalho Chehab 		if (rc < 0)
1178313cf4efSMauro Carvalho Chehab 			return rc;
1179313cf4efSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x51, 0x00);
1180313cf4efSMauro Carvalho Chehab 
1181313cf4efSMauro Carvalho Chehab 		return rc;
1182593ae89aSMauro Carvalho Chehab 	}
1183593ae89aSMauro Carvalho Chehab 
1184593ae89aSMauro Carvalho Chehab reset_measurement:
1185593ae89aSMauro Carvalho Chehab 	/* Reset counter to collect new data */
1186593ae89aSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x50, 0xb1);
1187593ae89aSMauro Carvalho Chehab 	if (rc < 0)
1188593ae89aSMauro Carvalho Chehab 		return rc;
1189313cf4efSMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x51);
1190593ae89aSMauro Carvalho Chehab 	if (rc < 0)
1191593ae89aSMauro Carvalho Chehab 		return rc;
1192313cf4efSMauro Carvalho Chehab 	val = rc;
1193313cf4efSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x51, val | (1 << layer));
1194593ae89aSMauro Carvalho Chehab 	if (rc < 0)
1195593ae89aSMauro Carvalho Chehab 		return rc;
1196313cf4efSMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x51, val & ~(1 << layer));
1197593ae89aSMauro Carvalho Chehab 
1198313cf4efSMauro Carvalho Chehab 	return rc;
1199593ae89aSMauro Carvalho Chehab }
1200593ae89aSMauro Carvalho Chehab 
120125188bd0SMauro Carvalho Chehab struct linear_segments {
120225188bd0SMauro Carvalho Chehab 	unsigned x, y;
120325188bd0SMauro Carvalho Chehab };
120425188bd0SMauro Carvalho Chehab 
120525188bd0SMauro Carvalho Chehab /*
120625188bd0SMauro Carvalho Chehab  * All tables below return a dB/1000 measurement
120725188bd0SMauro Carvalho Chehab  */
120825188bd0SMauro Carvalho Chehab 
120925188bd0SMauro Carvalho Chehab static struct linear_segments cnr_to_db_table[] = {
121025188bd0SMauro Carvalho Chehab 	{ 19648,     0},
121125188bd0SMauro Carvalho Chehab 	{ 18187,  1000},
121225188bd0SMauro Carvalho Chehab 	{ 16534,  2000},
121325188bd0SMauro Carvalho Chehab 	{ 14823,  3000},
121425188bd0SMauro Carvalho Chehab 	{ 13161,  4000},
121525188bd0SMauro Carvalho Chehab 	{ 11622,  5000},
121625188bd0SMauro Carvalho Chehab 	{ 10279,  6000},
121725188bd0SMauro Carvalho Chehab 	{  9089,  7000},
121825188bd0SMauro Carvalho Chehab 	{  8042,  8000},
121925188bd0SMauro Carvalho Chehab 	{  7137,  9000},
122025188bd0SMauro Carvalho Chehab 	{  6342, 10000},
122125188bd0SMauro Carvalho Chehab 	{  5641, 11000},
122225188bd0SMauro Carvalho Chehab 	{  5030, 12000},
122325188bd0SMauro Carvalho Chehab 	{  4474, 13000},
122425188bd0SMauro Carvalho Chehab 	{  3988, 14000},
122525188bd0SMauro Carvalho Chehab 	{  3556, 15000},
122625188bd0SMauro Carvalho Chehab 	{  3180, 16000},
122725188bd0SMauro Carvalho Chehab 	{  2841, 17000},
122825188bd0SMauro Carvalho Chehab 	{  2541, 18000},
122925188bd0SMauro Carvalho Chehab 	{  2276, 19000},
123025188bd0SMauro Carvalho Chehab 	{  2038, 20000},
123125188bd0SMauro Carvalho Chehab 	{  1800, 21000},
123225188bd0SMauro Carvalho Chehab 	{  1625, 22000},
123325188bd0SMauro Carvalho Chehab 	{  1462, 23000},
123425188bd0SMauro Carvalho Chehab 	{  1324, 24000},
123525188bd0SMauro Carvalho Chehab 	{  1175, 25000},
123625188bd0SMauro Carvalho Chehab 	{  1063, 26000},
123725188bd0SMauro Carvalho Chehab 	{   980, 27000},
123825188bd0SMauro Carvalho Chehab 	{   907, 28000},
123925188bd0SMauro Carvalho Chehab 	{   840, 29000},
124025188bd0SMauro Carvalho Chehab 	{   788, 30000},
124125188bd0SMauro Carvalho Chehab };
124225188bd0SMauro Carvalho Chehab 
124325188bd0SMauro Carvalho Chehab static struct linear_segments cnr_64qam_table[] = {
124425188bd0SMauro Carvalho Chehab 	{ 3922688,     0},
124525188bd0SMauro Carvalho Chehab 	{ 3920384,  1000},
124625188bd0SMauro Carvalho Chehab 	{ 3902720,  2000},
124725188bd0SMauro Carvalho Chehab 	{ 3894784,  3000},
124825188bd0SMauro Carvalho Chehab 	{ 3882496,  4000},
124925188bd0SMauro Carvalho Chehab 	{ 3872768,  5000},
125025188bd0SMauro Carvalho Chehab 	{ 3858944,  6000},
125125188bd0SMauro Carvalho Chehab 	{ 3851520,  7000},
125225188bd0SMauro Carvalho Chehab 	{ 3838976,  8000},
125325188bd0SMauro Carvalho Chehab 	{ 3829248,  9000},
125425188bd0SMauro Carvalho Chehab 	{ 3818240, 10000},
125525188bd0SMauro Carvalho Chehab 	{ 3806976, 11000},
125625188bd0SMauro Carvalho Chehab 	{ 3791872, 12000},
125725188bd0SMauro Carvalho Chehab 	{ 3767040, 13000},
125825188bd0SMauro Carvalho Chehab 	{ 3720960, 14000},
125925188bd0SMauro Carvalho Chehab 	{ 3637504, 15000},
126025188bd0SMauro Carvalho Chehab 	{ 3498496, 16000},
126125188bd0SMauro Carvalho Chehab 	{ 3296000, 17000},
126225188bd0SMauro Carvalho Chehab 	{ 3031040, 18000},
126325188bd0SMauro Carvalho Chehab 	{ 2715392, 19000},
126425188bd0SMauro Carvalho Chehab 	{ 2362624, 20000},
126525188bd0SMauro Carvalho Chehab 	{ 1963264, 21000},
126625188bd0SMauro Carvalho Chehab 	{ 1649664, 22000},
126725188bd0SMauro Carvalho Chehab 	{ 1366784, 23000},
126825188bd0SMauro Carvalho Chehab 	{ 1120768, 24000},
126925188bd0SMauro Carvalho Chehab 	{  890880, 25000},
127025188bd0SMauro Carvalho Chehab 	{  723456, 26000},
127125188bd0SMauro Carvalho Chehab 	{  612096, 27000},
127225188bd0SMauro Carvalho Chehab 	{  518912, 28000},
127325188bd0SMauro Carvalho Chehab 	{  448256, 29000},
127425188bd0SMauro Carvalho Chehab 	{  388864, 30000},
127525188bd0SMauro Carvalho Chehab };
127625188bd0SMauro Carvalho Chehab 
127725188bd0SMauro Carvalho Chehab static struct linear_segments cnr_16qam_table[] = {
127825188bd0SMauro Carvalho Chehab 	{ 5314816,     0},
127925188bd0SMauro Carvalho Chehab 	{ 5219072,  1000},
128025188bd0SMauro Carvalho Chehab 	{ 5118720,  2000},
128125188bd0SMauro Carvalho Chehab 	{ 4998912,  3000},
128225188bd0SMauro Carvalho Chehab 	{ 4875520,  4000},
128325188bd0SMauro Carvalho Chehab 	{ 4736000,  5000},
128425188bd0SMauro Carvalho Chehab 	{ 4604160,  6000},
128525188bd0SMauro Carvalho Chehab 	{ 4458752,  7000},
128625188bd0SMauro Carvalho Chehab 	{ 4300288,  8000},
128725188bd0SMauro Carvalho Chehab 	{ 4092928,  9000},
128825188bd0SMauro Carvalho Chehab 	{ 3836160, 10000},
128925188bd0SMauro Carvalho Chehab 	{ 3521024, 11000},
129025188bd0SMauro Carvalho Chehab 	{ 3155968, 12000},
129125188bd0SMauro Carvalho Chehab 	{ 2756864, 13000},
129225188bd0SMauro Carvalho Chehab 	{ 2347008, 14000},
129325188bd0SMauro Carvalho Chehab 	{ 1955072, 15000},
129425188bd0SMauro Carvalho Chehab 	{ 1593600, 16000},
129525188bd0SMauro Carvalho Chehab 	{ 1297920, 17000},
129625188bd0SMauro Carvalho Chehab 	{ 1043968, 18000},
129725188bd0SMauro Carvalho Chehab 	{  839680, 19000},
129825188bd0SMauro Carvalho Chehab 	{  672256, 20000},
129925188bd0SMauro Carvalho Chehab 	{  523008, 21000},
130025188bd0SMauro Carvalho Chehab 	{  424704, 22000},
130125188bd0SMauro Carvalho Chehab 	{  345088, 23000},
130225188bd0SMauro Carvalho Chehab 	{  280064, 24000},
130325188bd0SMauro Carvalho Chehab 	{  221440, 25000},
130425188bd0SMauro Carvalho Chehab 	{  179712, 26000},
130525188bd0SMauro Carvalho Chehab 	{  151040, 27000},
130625188bd0SMauro Carvalho Chehab 	{  128512, 28000},
130725188bd0SMauro Carvalho Chehab 	{  110080, 29000},
130825188bd0SMauro Carvalho Chehab 	{   95744, 30000},
130925188bd0SMauro Carvalho Chehab };
131025188bd0SMauro Carvalho Chehab 
131125188bd0SMauro Carvalho Chehab struct linear_segments cnr_qpsk_table[] = {
131225188bd0SMauro Carvalho Chehab 	{ 2834176,     0},
131325188bd0SMauro Carvalho Chehab 	{ 2683648,  1000},
131425188bd0SMauro Carvalho Chehab 	{ 2536960,  2000},
131525188bd0SMauro Carvalho Chehab 	{ 2391808,  3000},
131625188bd0SMauro Carvalho Chehab 	{ 2133248,  4000},
131725188bd0SMauro Carvalho Chehab 	{ 1906176,  5000},
131825188bd0SMauro Carvalho Chehab 	{ 1666560,  6000},
131925188bd0SMauro Carvalho Chehab 	{ 1422080,  7000},
132025188bd0SMauro Carvalho Chehab 	{ 1189632,  8000},
132125188bd0SMauro Carvalho Chehab 	{  976384,  9000},
132225188bd0SMauro Carvalho Chehab 	{  790272, 10000},
132325188bd0SMauro Carvalho Chehab 	{  633344, 11000},
132425188bd0SMauro Carvalho Chehab 	{  505600, 12000},
132525188bd0SMauro Carvalho Chehab 	{  402944, 13000},
132625188bd0SMauro Carvalho Chehab 	{  320768, 14000},
132725188bd0SMauro Carvalho Chehab 	{  255488, 15000},
132825188bd0SMauro Carvalho Chehab 	{  204032, 16000},
132925188bd0SMauro Carvalho Chehab 	{  163072, 17000},
133025188bd0SMauro Carvalho Chehab 	{  130304, 18000},
133125188bd0SMauro Carvalho Chehab 	{  105216, 19000},
133225188bd0SMauro Carvalho Chehab 	{   83456, 20000},
133325188bd0SMauro Carvalho Chehab 	{   65024, 21000},
133425188bd0SMauro Carvalho Chehab 	{   52480, 22000},
133525188bd0SMauro Carvalho Chehab 	{   42752, 23000},
133625188bd0SMauro Carvalho Chehab 	{   34560, 24000},
133725188bd0SMauro Carvalho Chehab 	{   27136, 25000},
133825188bd0SMauro Carvalho Chehab 	{   22016, 26000},
133925188bd0SMauro Carvalho Chehab 	{   18432, 27000},
134025188bd0SMauro Carvalho Chehab 	{   15616, 28000},
134125188bd0SMauro Carvalho Chehab 	{   13312, 29000},
134225188bd0SMauro Carvalho Chehab 	{   11520, 30000},
134325188bd0SMauro Carvalho Chehab };
134425188bd0SMauro Carvalho Chehab 
134525188bd0SMauro Carvalho Chehab static u32 interpolate_value(u32 value, struct linear_segments *segments,
134625188bd0SMauro Carvalho Chehab 			     unsigned len)
134725188bd0SMauro Carvalho Chehab {
134825188bd0SMauro Carvalho Chehab 	u64 tmp64;
134925188bd0SMauro Carvalho Chehab 	u32 dx, dy;
135025188bd0SMauro Carvalho Chehab 	int i, ret;
135125188bd0SMauro Carvalho Chehab 
135225188bd0SMauro Carvalho Chehab 	if (value >= segments[0].x)
135325188bd0SMauro Carvalho Chehab 		return segments[0].y;
135425188bd0SMauro Carvalho Chehab 	if (value < segments[len-1].x)
135525188bd0SMauro Carvalho Chehab 		return segments[len-1].y;
135625188bd0SMauro Carvalho Chehab 
135725188bd0SMauro Carvalho Chehab 	for (i = 1; i < len - 1; i++) {
135825188bd0SMauro Carvalho Chehab 		/* If value is identical, no need to interpolate */
135925188bd0SMauro Carvalho Chehab 		if (value == segments[i].x)
136025188bd0SMauro Carvalho Chehab 			return segments[i].y;
136125188bd0SMauro Carvalho Chehab 		if (value > segments[i].x)
136225188bd0SMauro Carvalho Chehab 			break;
136325188bd0SMauro Carvalho Chehab 	}
136425188bd0SMauro Carvalho Chehab 
136525188bd0SMauro Carvalho Chehab 	/* Linear interpolation between the two (x,y) points */
136625188bd0SMauro Carvalho Chehab 	dy = segments[i].y - segments[i - 1].y;
136725188bd0SMauro Carvalho Chehab 	dx = segments[i - 1].x - segments[i].x;
136825188bd0SMauro Carvalho Chehab 	tmp64 = value - segments[i].x;
136925188bd0SMauro Carvalho Chehab 	tmp64 *= dy;
137025188bd0SMauro Carvalho Chehab 	do_div(tmp64, dx);
137125188bd0SMauro Carvalho Chehab 	ret = segments[i].y - tmp64;
137225188bd0SMauro Carvalho Chehab 
137325188bd0SMauro Carvalho Chehab 	return ret;
137425188bd0SMauro Carvalho Chehab }
137525188bd0SMauro Carvalho Chehab 
137625188bd0SMauro Carvalho Chehab static int mb86a20s_get_main_CNR(struct dvb_frontend *fe)
137725188bd0SMauro Carvalho Chehab {
137825188bd0SMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
137925188bd0SMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
138025188bd0SMauro Carvalho Chehab 	u32 cnr_linear, cnr;
138125188bd0SMauro Carvalho Chehab 	int rc, val;
138225188bd0SMauro Carvalho Chehab 
138325188bd0SMauro Carvalho Chehab 	/* Check if CNR is available */
138425188bd0SMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x45);
138525188bd0SMauro Carvalho Chehab 	if (rc < 0)
138625188bd0SMauro Carvalho Chehab 		return rc;
138725188bd0SMauro Carvalho Chehab 
138825188bd0SMauro Carvalho Chehab 	if (!(rc & 0x40)) {
1389d56e326fSMauro Carvalho Chehab 		dev_dbg(&state->i2c->dev, "%s: CNR is not available yet.\n",
139025188bd0SMauro Carvalho Chehab 			 __func__);
139125188bd0SMauro Carvalho Chehab 		return -EBUSY;
139225188bd0SMauro Carvalho Chehab 	}
139325188bd0SMauro Carvalho Chehab 	val = rc;
139425188bd0SMauro Carvalho Chehab 
139525188bd0SMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x46);
139625188bd0SMauro Carvalho Chehab 	if (rc < 0)
139725188bd0SMauro Carvalho Chehab 		return rc;
139825188bd0SMauro Carvalho Chehab 	cnr_linear = rc << 8;
139925188bd0SMauro Carvalho Chehab 
140025188bd0SMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x46);
140125188bd0SMauro Carvalho Chehab 	if (rc < 0)
140225188bd0SMauro Carvalho Chehab 		return rc;
140325188bd0SMauro Carvalho Chehab 	cnr_linear |= rc;
140425188bd0SMauro Carvalho Chehab 
140525188bd0SMauro Carvalho Chehab 	cnr = interpolate_value(cnr_linear,
140625188bd0SMauro Carvalho Chehab 				cnr_to_db_table, ARRAY_SIZE(cnr_to_db_table));
140725188bd0SMauro Carvalho Chehab 
140825188bd0SMauro Carvalho Chehab 	c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
140925188bd0SMauro Carvalho Chehab 	c->cnr.stat[0].svalue = cnr;
141025188bd0SMauro Carvalho Chehab 
141125188bd0SMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s: CNR is %d.%03d dB (%d)\n",
141225188bd0SMauro Carvalho Chehab 		__func__, cnr / 1000, cnr % 1000, cnr_linear);
141325188bd0SMauro Carvalho Chehab 
141425188bd0SMauro Carvalho Chehab 	/* CNR counter reset */
141525188bd0SMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x45, val | 0x10);
141625188bd0SMauro Carvalho Chehab 	if (rc < 0)
141725188bd0SMauro Carvalho Chehab 		return rc;
141825188bd0SMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x45, val & 0x6f);
141925188bd0SMauro Carvalho Chehab 
142025188bd0SMauro Carvalho Chehab 	return rc;
142125188bd0SMauro Carvalho Chehab }
142225188bd0SMauro Carvalho Chehab 
1423593ae89aSMauro Carvalho Chehab static int mb86a20s_get_blk_error_layer_CNR(struct dvb_frontend *fe)
142425188bd0SMauro Carvalho Chehab {
142525188bd0SMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
142625188bd0SMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
142725188bd0SMauro Carvalho Chehab 	u32 mer, cnr;
142825188bd0SMauro Carvalho Chehab 	int rc, val, i;
142925188bd0SMauro Carvalho Chehab 	struct linear_segments *segs;
143025188bd0SMauro Carvalho Chehab 	unsigned segs_len;
143125188bd0SMauro Carvalho Chehab 
143225188bd0SMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
143325188bd0SMauro Carvalho Chehab 
143425188bd0SMauro Carvalho Chehab 	/* Check if the measures are already available */
143525188bd0SMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x50, 0x5b);
143625188bd0SMauro Carvalho Chehab 	if (rc < 0)
143725188bd0SMauro Carvalho Chehab 		return rc;
143825188bd0SMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x51);
143925188bd0SMauro Carvalho Chehab 	if (rc < 0)
144025188bd0SMauro Carvalho Chehab 		return rc;
144125188bd0SMauro Carvalho Chehab 
144225188bd0SMauro Carvalho Chehab 	/* Check if data is available */
144325188bd0SMauro Carvalho Chehab 	if (!(rc & 0x01)) {
1444d56e326fSMauro Carvalho Chehab 		dev_dbg(&state->i2c->dev,
144525188bd0SMauro Carvalho Chehab 			"%s: MER measures aren't available yet.\n", __func__);
144625188bd0SMauro Carvalho Chehab 		return -EBUSY;
144725188bd0SMauro Carvalho Chehab 	}
144825188bd0SMauro Carvalho Chehab 
144925188bd0SMauro Carvalho Chehab 	/* Read all layers */
145025188bd0SMauro Carvalho Chehab 	for (i = 0; i < 3; i++) {
145125188bd0SMauro Carvalho Chehab 		if (!(c->isdbt_layer_enabled & (1 << i))) {
145225188bd0SMauro Carvalho Chehab 			c->cnr.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE;
145325188bd0SMauro Carvalho Chehab 			continue;
145425188bd0SMauro Carvalho Chehab 		}
145525188bd0SMauro Carvalho Chehab 
145625188bd0SMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x50, 0x52 + i * 3);
145725188bd0SMauro Carvalho Chehab 		if (rc < 0)
145825188bd0SMauro Carvalho Chehab 			return rc;
145925188bd0SMauro Carvalho Chehab 		rc = mb86a20s_readreg(state, 0x51);
146025188bd0SMauro Carvalho Chehab 		if (rc < 0)
146125188bd0SMauro Carvalho Chehab 			return rc;
146225188bd0SMauro Carvalho Chehab 		mer = rc << 16;
146325188bd0SMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x50, 0x53 + i * 3);
146425188bd0SMauro Carvalho Chehab 		if (rc < 0)
146525188bd0SMauro Carvalho Chehab 			return rc;
146625188bd0SMauro Carvalho Chehab 		rc = mb86a20s_readreg(state, 0x51);
146725188bd0SMauro Carvalho Chehab 		if (rc < 0)
146825188bd0SMauro Carvalho Chehab 			return rc;
146925188bd0SMauro Carvalho Chehab 		mer |= rc << 8;
147025188bd0SMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x50, 0x54 + i * 3);
147125188bd0SMauro Carvalho Chehab 		if (rc < 0)
147225188bd0SMauro Carvalho Chehab 			return rc;
147325188bd0SMauro Carvalho Chehab 		rc = mb86a20s_readreg(state, 0x51);
147425188bd0SMauro Carvalho Chehab 		if (rc < 0)
147525188bd0SMauro Carvalho Chehab 			return rc;
147625188bd0SMauro Carvalho Chehab 		mer |= rc;
147725188bd0SMauro Carvalho Chehab 
147825188bd0SMauro Carvalho Chehab 		switch (c->layer[i].modulation) {
147925188bd0SMauro Carvalho Chehab 		case DQPSK:
148025188bd0SMauro Carvalho Chehab 		case QPSK:
148125188bd0SMauro Carvalho Chehab 			segs = cnr_qpsk_table;
148225188bd0SMauro Carvalho Chehab 			segs_len = ARRAY_SIZE(cnr_qpsk_table);
148325188bd0SMauro Carvalho Chehab 			break;
148425188bd0SMauro Carvalho Chehab 		case QAM_16:
148525188bd0SMauro Carvalho Chehab 			segs = cnr_16qam_table;
148625188bd0SMauro Carvalho Chehab 			segs_len = ARRAY_SIZE(cnr_16qam_table);
148725188bd0SMauro Carvalho Chehab 			break;
148825188bd0SMauro Carvalho Chehab 		default:
148925188bd0SMauro Carvalho Chehab 		case QAM_64:
149025188bd0SMauro Carvalho Chehab 			segs = cnr_64qam_table;
149125188bd0SMauro Carvalho Chehab 			segs_len = ARRAY_SIZE(cnr_64qam_table);
149225188bd0SMauro Carvalho Chehab 			break;
149325188bd0SMauro Carvalho Chehab 		}
149425188bd0SMauro Carvalho Chehab 		cnr = interpolate_value(mer, segs, segs_len);
149525188bd0SMauro Carvalho Chehab 
149625188bd0SMauro Carvalho Chehab 		c->cnr.stat[1 + i].scale = FE_SCALE_DECIBEL;
149725188bd0SMauro Carvalho Chehab 		c->cnr.stat[1 + i].svalue = cnr;
149825188bd0SMauro Carvalho Chehab 
149925188bd0SMauro Carvalho Chehab 		dev_dbg(&state->i2c->dev,
150025188bd0SMauro Carvalho Chehab 			"%s: CNR for layer %c is %d.%03d dB (MER = %d).\n",
150125188bd0SMauro Carvalho Chehab 			__func__, 'A' + i, cnr / 1000, cnr % 1000, mer);
150225188bd0SMauro Carvalho Chehab 
150325188bd0SMauro Carvalho Chehab 	}
150425188bd0SMauro Carvalho Chehab 
150525188bd0SMauro Carvalho Chehab 	/* Start a new MER measurement */
150625188bd0SMauro Carvalho Chehab 	/* MER counter reset */
150725188bd0SMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x50, 0x50);
150825188bd0SMauro Carvalho Chehab 	if (rc < 0)
150925188bd0SMauro Carvalho Chehab 		return rc;
151025188bd0SMauro Carvalho Chehab 	rc = mb86a20s_readreg(state, 0x51);
151125188bd0SMauro Carvalho Chehab 	if (rc < 0)
151225188bd0SMauro Carvalho Chehab 		return rc;
151325188bd0SMauro Carvalho Chehab 	val = rc;
151425188bd0SMauro Carvalho Chehab 
151525188bd0SMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x51, val | 0x01);
151625188bd0SMauro Carvalho Chehab 	if (rc < 0)
151725188bd0SMauro Carvalho Chehab 		return rc;
151825188bd0SMauro Carvalho Chehab 	rc = mb86a20s_writereg(state, 0x51, val & 0x06);
151925188bd0SMauro Carvalho Chehab 	if (rc < 0)
152025188bd0SMauro Carvalho Chehab 		return rc;
152125188bd0SMauro Carvalho Chehab 
152225188bd0SMauro Carvalho Chehab 	return 0;
152325188bd0SMauro Carvalho Chehab }
152425188bd0SMauro Carvalho Chehab 
152509b6d21eSMauro Carvalho Chehab static void mb86a20s_stats_not_ready(struct dvb_frontend *fe)
152609b6d21eSMauro Carvalho Chehab {
152709b6d21eSMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
152809b6d21eSMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
152909b6d21eSMauro Carvalho Chehab 	int i;
153009b6d21eSMauro Carvalho Chehab 
153109b6d21eSMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
153209b6d21eSMauro Carvalho Chehab 
153309b6d21eSMauro Carvalho Chehab 	/* Fill the length of each status counter */
153409b6d21eSMauro Carvalho Chehab 
153509b6d21eSMauro Carvalho Chehab 	/* Only global stats */
153609b6d21eSMauro Carvalho Chehab 	c->strength.len = 1;
153709b6d21eSMauro Carvalho Chehab 
153809b6d21eSMauro Carvalho Chehab 	/* Per-layer stats - 3 layers + global */
153909b6d21eSMauro Carvalho Chehab 	c->cnr.len = 4;
154009b6d21eSMauro Carvalho Chehab 	c->pre_bit_error.len = 4;
154109b6d21eSMauro Carvalho Chehab 	c->pre_bit_count.len = 4;
1542d9b6f08aSMauro Carvalho Chehab 	c->post_bit_error.len = 4;
1543d9b6f08aSMauro Carvalho Chehab 	c->post_bit_count.len = 4;
154409b6d21eSMauro Carvalho Chehab 	c->block_error.len = 4;
154509b6d21eSMauro Carvalho Chehab 	c->block_count.len = 4;
154609b6d21eSMauro Carvalho Chehab 
154709b6d21eSMauro Carvalho Chehab 	/* Signal is always available */
154809b6d21eSMauro Carvalho Chehab 	c->strength.stat[0].scale = FE_SCALE_RELATIVE;
154909b6d21eSMauro Carvalho Chehab 	c->strength.stat[0].uvalue = 0;
155009b6d21eSMauro Carvalho Chehab 
155109b6d21eSMauro Carvalho Chehab 	/* Put all of them at FE_SCALE_NOT_AVAILABLE */
155209b6d21eSMauro Carvalho Chehab 	for (i = 0; i < 4; i++) {
155309b6d21eSMauro Carvalho Chehab 		c->cnr.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
155409b6d21eSMauro Carvalho Chehab 		c->pre_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
155509b6d21eSMauro Carvalho Chehab 		c->pre_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
1556d9b6f08aSMauro Carvalho Chehab 		c->post_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
1557d9b6f08aSMauro Carvalho Chehab 		c->post_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
155809b6d21eSMauro Carvalho Chehab 		c->block_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
155909b6d21eSMauro Carvalho Chehab 		c->block_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
156009b6d21eSMauro Carvalho Chehab 	}
156109b6d21eSMauro Carvalho Chehab }
156209b6d21eSMauro Carvalho Chehab 
1563149d518aSMauro Carvalho Chehab static int mb86a20s_get_stats(struct dvb_frontend *fe)
1564149d518aSMauro Carvalho Chehab {
1565149d518aSMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
1566149d518aSMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
1567149d518aSMauro Carvalho Chehab 	int rc = 0, i;
1568149d518aSMauro Carvalho Chehab 	u32 bit_error = 0, bit_count = 0;
1569149d518aSMauro Carvalho Chehab 	u32 t_pre_bit_error = 0, t_pre_bit_count = 0;
1570d9b6f08aSMauro Carvalho Chehab 	u32 t_post_bit_error = 0, t_post_bit_count = 0;
1571593ae89aSMauro Carvalho Chehab 	u32 block_error = 0, block_count = 0;
1572593ae89aSMauro Carvalho Chehab 	u32 t_block_error = 0, t_block_count = 0;
1573d9b6f08aSMauro Carvalho Chehab 	int active_layers = 0, pre_ber_layers = 0, post_ber_layers = 0;
1574d9b6f08aSMauro Carvalho Chehab 	int per_layers = 0;
1575149d518aSMauro Carvalho Chehab 
157625188bd0SMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
157725188bd0SMauro Carvalho Chehab 
157825188bd0SMauro Carvalho Chehab 	mb86a20s_get_main_CNR(fe);
157925188bd0SMauro Carvalho Chehab 
1580149d518aSMauro Carvalho Chehab 	/* Get per-layer stats */
1581593ae89aSMauro Carvalho Chehab 	mb86a20s_get_blk_error_layer_CNR(fe);
158225188bd0SMauro Carvalho Chehab 
1583149d518aSMauro Carvalho Chehab 	for (i = 0; i < 3; i++) {
1584149d518aSMauro Carvalho Chehab 		if (c->isdbt_layer_enabled & (1 << i)) {
1585149d518aSMauro Carvalho Chehab 			/* Layer is active and has rc segments */
1586149d518aSMauro Carvalho Chehab 			active_layers++;
1587149d518aSMauro Carvalho Chehab 
1588149d518aSMauro Carvalho Chehab 			/* Handle BER before vterbi */
1589ad0abbf1SMauro Carvalho Chehab 			rc = mb86a20s_get_pre_ber(fe, i,
1590ad0abbf1SMauro Carvalho Chehab 						  &bit_error, &bit_count);
1591149d518aSMauro Carvalho Chehab 			if (rc >= 0) {
1592149d518aSMauro Carvalho Chehab 				c->pre_bit_error.stat[1 + i].scale = FE_SCALE_COUNTER;
1593149d518aSMauro Carvalho Chehab 				c->pre_bit_error.stat[1 + i].uvalue += bit_error;
1594149d518aSMauro Carvalho Chehab 				c->pre_bit_count.stat[1 + i].scale = FE_SCALE_COUNTER;
1595149d518aSMauro Carvalho Chehab 				c->pre_bit_count.stat[1 + i].uvalue += bit_count;
1596149d518aSMauro Carvalho Chehab 			} else if (rc != -EBUSY) {
1597149d518aSMauro Carvalho Chehab 				/*
1598149d518aSMauro Carvalho Chehab 					* If an I/O error happened,
1599149d518aSMauro Carvalho Chehab 					* measures are now unavailable
1600149d518aSMauro Carvalho Chehab 					*/
1601149d518aSMauro Carvalho Chehab 				c->pre_bit_error.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE;
1602149d518aSMauro Carvalho Chehab 				c->pre_bit_count.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE;
1603149d518aSMauro Carvalho Chehab 				dev_err(&state->i2c->dev,
1604149d518aSMauro Carvalho Chehab 					"%s: Can't get BER for layer %c (error %d).\n",
1605149d518aSMauro Carvalho Chehab 					__func__, 'A' + i, rc);
1606149d518aSMauro Carvalho Chehab 			}
1607149d518aSMauro Carvalho Chehab 			if (c->block_error.stat[1 + i].scale != FE_SCALE_NOT_AVAILABLE)
1608d9b6f08aSMauro Carvalho Chehab 				pre_ber_layers++;
1609d9b6f08aSMauro Carvalho Chehab 
1610d9b6f08aSMauro Carvalho Chehab 			/* Handle BER post vterbi */
1611d9b6f08aSMauro Carvalho Chehab 			rc = mb86a20s_get_post_ber(fe, i,
1612d9b6f08aSMauro Carvalho Chehab 						   &bit_error, &bit_count);
1613d9b6f08aSMauro Carvalho Chehab 			if (rc >= 0) {
1614d9b6f08aSMauro Carvalho Chehab 				c->post_bit_error.stat[1 + i].scale = FE_SCALE_COUNTER;
1615d9b6f08aSMauro Carvalho Chehab 				c->post_bit_error.stat[1 + i].uvalue += bit_error;
1616d9b6f08aSMauro Carvalho Chehab 				c->post_bit_count.stat[1 + i].scale = FE_SCALE_COUNTER;
1617d9b6f08aSMauro Carvalho Chehab 				c->post_bit_count.stat[1 + i].uvalue += bit_count;
1618d9b6f08aSMauro Carvalho Chehab 			} else if (rc != -EBUSY) {
1619d9b6f08aSMauro Carvalho Chehab 				/*
1620d9b6f08aSMauro Carvalho Chehab 					* If an I/O error happened,
1621d9b6f08aSMauro Carvalho Chehab 					* measures are now unavailable
1622d9b6f08aSMauro Carvalho Chehab 					*/
1623d9b6f08aSMauro Carvalho Chehab 				c->post_bit_error.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE;
1624d9b6f08aSMauro Carvalho Chehab 				c->post_bit_count.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE;
1625d9b6f08aSMauro Carvalho Chehab 				dev_err(&state->i2c->dev,
1626d9b6f08aSMauro Carvalho Chehab 					"%s: Can't get BER for layer %c (error %d).\n",
1627d9b6f08aSMauro Carvalho Chehab 					__func__, 'A' + i, rc);
1628d9b6f08aSMauro Carvalho Chehab 			}
1629d9b6f08aSMauro Carvalho Chehab 			if (c->block_error.stat[1 + i].scale != FE_SCALE_NOT_AVAILABLE)
1630d9b6f08aSMauro Carvalho Chehab 				post_ber_layers++;
1631149d518aSMauro Carvalho Chehab 
1632593ae89aSMauro Carvalho Chehab 			/* Handle Block errors for PER/UCB reports */
1633593ae89aSMauro Carvalho Chehab 			rc = mb86a20s_get_blk_error(fe, i,
1634593ae89aSMauro Carvalho Chehab 						&block_error,
1635593ae89aSMauro Carvalho Chehab 						&block_count);
1636593ae89aSMauro Carvalho Chehab 			if (rc >= 0) {
1637593ae89aSMauro Carvalho Chehab 				c->block_error.stat[1 + i].scale = FE_SCALE_COUNTER;
1638593ae89aSMauro Carvalho Chehab 				c->block_error.stat[1 + i].uvalue += block_error;
1639593ae89aSMauro Carvalho Chehab 				c->block_count.stat[1 + i].scale = FE_SCALE_COUNTER;
1640593ae89aSMauro Carvalho Chehab 				c->block_count.stat[1 + i].uvalue += block_count;
1641593ae89aSMauro Carvalho Chehab 			} else if (rc != -EBUSY) {
1642593ae89aSMauro Carvalho Chehab 				/*
1643593ae89aSMauro Carvalho Chehab 					* If an I/O error happened,
1644593ae89aSMauro Carvalho Chehab 					* measures are now unavailable
1645593ae89aSMauro Carvalho Chehab 					*/
1646593ae89aSMauro Carvalho Chehab 				c->block_error.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE;
1647593ae89aSMauro Carvalho Chehab 				c->block_count.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE;
1648593ae89aSMauro Carvalho Chehab 				dev_err(&state->i2c->dev,
1649593ae89aSMauro Carvalho Chehab 					"%s: Can't get PER for layer %c (error %d).\n",
1650593ae89aSMauro Carvalho Chehab 					__func__, 'A' + i, rc);
1651593ae89aSMauro Carvalho Chehab 
1652593ae89aSMauro Carvalho Chehab 			}
1653593ae89aSMauro Carvalho Chehab 			if (c->block_error.stat[1 + i].scale != FE_SCALE_NOT_AVAILABLE)
1654593ae89aSMauro Carvalho Chehab 				per_layers++;
1655593ae89aSMauro Carvalho Chehab 
1656d9b6f08aSMauro Carvalho Chehab 			/* Update total preBER */
1657149d518aSMauro Carvalho Chehab 			t_pre_bit_error += c->pre_bit_error.stat[1 + i].uvalue;
1658149d518aSMauro Carvalho Chehab 			t_pre_bit_count += c->pre_bit_count.stat[1 + i].uvalue;
1659593ae89aSMauro Carvalho Chehab 
1660d9b6f08aSMauro Carvalho Chehab 			/* Update total postBER */
1661d9b6f08aSMauro Carvalho Chehab 			t_post_bit_error += c->post_bit_error.stat[1 + i].uvalue;
1662d9b6f08aSMauro Carvalho Chehab 			t_post_bit_count += c->post_bit_count.stat[1 + i].uvalue;
1663d9b6f08aSMauro Carvalho Chehab 
1664593ae89aSMauro Carvalho Chehab 			/* Update total PER */
1665593ae89aSMauro Carvalho Chehab 			t_block_error += c->block_error.stat[1 + i].uvalue;
1666593ae89aSMauro Carvalho Chehab 			t_block_count += c->block_count.stat[1 + i].uvalue;
1667149d518aSMauro Carvalho Chehab 		}
1668149d518aSMauro Carvalho Chehab 	}
1669149d518aSMauro Carvalho Chehab 
1670149d518aSMauro Carvalho Chehab 	/*
1671149d518aSMauro Carvalho Chehab 	 * Start showing global count if at least one error count is
1672149d518aSMauro Carvalho Chehab 	 * available.
1673149d518aSMauro Carvalho Chehab 	 */
1674d9b6f08aSMauro Carvalho Chehab 	if (pre_ber_layers) {
1675149d518aSMauro Carvalho Chehab 		/*
1676149d518aSMauro Carvalho Chehab 		 * At least one per-layer BER measure was read. We can now
1677149d518aSMauro Carvalho Chehab 		 * calculate the total BER
1678149d518aSMauro Carvalho Chehab 		 *
1679149d518aSMauro Carvalho Chehab 		 * Total Bit Error/Count is calculated as the sum of the
1680149d518aSMauro Carvalho Chehab 		 * bit errors on all active layers.
1681149d518aSMauro Carvalho Chehab 		 */
1682149d518aSMauro Carvalho Chehab 		c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
1683149d518aSMauro Carvalho Chehab 		c->pre_bit_error.stat[0].uvalue = t_pre_bit_error;
1684149d518aSMauro Carvalho Chehab 		c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
1685149d518aSMauro Carvalho Chehab 		c->pre_bit_count.stat[0].uvalue = t_pre_bit_count;
1686f67102c4SMauro Carvalho Chehab 	} else {
1687f67102c4SMauro Carvalho Chehab 		c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
1688f67102c4SMauro Carvalho Chehab 		c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
1689149d518aSMauro Carvalho Chehab 	}
1690149d518aSMauro Carvalho Chehab 
1691d9b6f08aSMauro Carvalho Chehab 	/*
1692d9b6f08aSMauro Carvalho Chehab 	 * Start showing global count if at least one error count is
1693d9b6f08aSMauro Carvalho Chehab 	 * available.
1694d9b6f08aSMauro Carvalho Chehab 	 */
1695d9b6f08aSMauro Carvalho Chehab 	if (post_ber_layers) {
1696d9b6f08aSMauro Carvalho Chehab 		/*
1697d9b6f08aSMauro Carvalho Chehab 		 * At least one per-layer BER measure was read. We can now
1698d9b6f08aSMauro Carvalho Chehab 		 * calculate the total BER
1699d9b6f08aSMauro Carvalho Chehab 		 *
1700d9b6f08aSMauro Carvalho Chehab 		 * Total Bit Error/Count is calculated as the sum of the
1701d9b6f08aSMauro Carvalho Chehab 		 * bit errors on all active layers.
1702d9b6f08aSMauro Carvalho Chehab 		 */
1703d9b6f08aSMauro Carvalho Chehab 		c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
1704d9b6f08aSMauro Carvalho Chehab 		c->post_bit_error.stat[0].uvalue = t_post_bit_error;
1705d9b6f08aSMauro Carvalho Chehab 		c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
1706d9b6f08aSMauro Carvalho Chehab 		c->post_bit_count.stat[0].uvalue = t_post_bit_count;
1707f67102c4SMauro Carvalho Chehab 	} else {
1708f67102c4SMauro Carvalho Chehab 		c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
1709f67102c4SMauro Carvalho Chehab 		c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
1710d9b6f08aSMauro Carvalho Chehab 	}
1711d9b6f08aSMauro Carvalho Chehab 
1712593ae89aSMauro Carvalho Chehab 	if (per_layers) {
1713593ae89aSMauro Carvalho Chehab 		/*
1714593ae89aSMauro Carvalho Chehab 		 * At least one per-layer UCB measure was read. We can now
1715593ae89aSMauro Carvalho Chehab 		 * calculate the total UCB
1716593ae89aSMauro Carvalho Chehab 		 *
1717593ae89aSMauro Carvalho Chehab 		 * Total block Error/Count is calculated as the sum of the
1718593ae89aSMauro Carvalho Chehab 		 * block errors on all active layers.
1719593ae89aSMauro Carvalho Chehab 		 */
1720593ae89aSMauro Carvalho Chehab 		c->block_error.stat[0].scale = FE_SCALE_COUNTER;
1721593ae89aSMauro Carvalho Chehab 		c->block_error.stat[0].uvalue = t_block_error;
1722593ae89aSMauro Carvalho Chehab 		c->block_count.stat[0].scale = FE_SCALE_COUNTER;
1723593ae89aSMauro Carvalho Chehab 		c->block_count.stat[0].uvalue = t_block_count;
1724f67102c4SMauro Carvalho Chehab 	} else {
1725f67102c4SMauro Carvalho Chehab 		c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
1726f67102c4SMauro Carvalho Chehab 		c->block_count.stat[0].scale = FE_SCALE_COUNTER;
1727593ae89aSMauro Carvalho Chehab 	}
1728593ae89aSMauro Carvalho Chehab 
1729149d518aSMauro Carvalho Chehab 	return rc;
1730149d518aSMauro Carvalho Chehab }
173109b6d21eSMauro Carvalho Chehab 
173209b6d21eSMauro Carvalho Chehab /*
173309b6d21eSMauro Carvalho Chehab  * The functions below are called via DVB callbacks, so they need to
173409b6d21eSMauro Carvalho Chehab  * properly use the I2C gate control
173509b6d21eSMauro Carvalho Chehab  */
173609b6d21eSMauro Carvalho Chehab 
1737dd4493efSMauro Carvalho Chehab static int mb86a20s_initfe(struct dvb_frontend *fe)
1738dd4493efSMauro Carvalho Chehab {
1739dd4493efSMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
1740dd4493efSMauro Carvalho Chehab 	int rc;
1741dd4493efSMauro Carvalho Chehab 	u8  regD5 = 1;
1742dd4493efSMauro Carvalho Chehab 
1743f66d81b5SMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
1744dd4493efSMauro Carvalho Chehab 
1745dd4493efSMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
1746dd4493efSMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 0);
1747dd4493efSMauro Carvalho Chehab 
1748dd4493efSMauro Carvalho Chehab 	/* Initialize the frontend */
1749dd4493efSMauro Carvalho Chehab 	rc = mb86a20s_writeregdata(state, mb86a20s_init);
1750dd4493efSMauro Carvalho Chehab 	if (rc < 0)
1751dd4493efSMauro Carvalho Chehab 		goto err;
1752dd4493efSMauro Carvalho Chehab 
1753dd4493efSMauro Carvalho Chehab 	if (!state->config->is_serial) {
1754dd4493efSMauro Carvalho Chehab 		regD5 &= ~1;
1755dd4493efSMauro Carvalho Chehab 
1756dd4493efSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x50, 0xd5);
1757dd4493efSMauro Carvalho Chehab 		if (rc < 0)
1758dd4493efSMauro Carvalho Chehab 			goto err;
1759dd4493efSMauro Carvalho Chehab 		rc = mb86a20s_writereg(state, 0x51, regD5);
1760dd4493efSMauro Carvalho Chehab 		if (rc < 0)
1761dd4493efSMauro Carvalho Chehab 			goto err;
1762dd4493efSMauro Carvalho Chehab 	}
1763dd4493efSMauro Carvalho Chehab 
1764dd4493efSMauro Carvalho Chehab err:
1765dd4493efSMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
1766dd4493efSMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 1);
1767dd4493efSMauro Carvalho Chehab 
1768dd4493efSMauro Carvalho Chehab 	if (rc < 0) {
1769dd4493efSMauro Carvalho Chehab 		state->need_init = true;
1770f66d81b5SMauro Carvalho Chehab 		dev_info(&state->i2c->dev,
1771f66d81b5SMauro Carvalho Chehab 			 "mb86a20s: Init failed. Will try again later\n");
1772dd4493efSMauro Carvalho Chehab 	} else {
1773dd4493efSMauro Carvalho Chehab 		state->need_init = false;
1774f66d81b5SMauro Carvalho Chehab 		dev_dbg(&state->i2c->dev, "Initialization succeeded.\n");
1775dd4493efSMauro Carvalho Chehab 	}
1776dd4493efSMauro Carvalho Chehab 	return rc;
1777dd4493efSMauro Carvalho Chehab }
1778dd4493efSMauro Carvalho Chehab 
1779dd4493efSMauro Carvalho Chehab static int mb86a20s_set_frontend(struct dvb_frontend *fe)
1780dd4493efSMauro Carvalho Chehab {
1781dd4493efSMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
1782dd4493efSMauro Carvalho Chehab 	int rc;
1783dd4493efSMauro Carvalho Chehab #if 0
1784dd4493efSMauro Carvalho Chehab 	/*
1785dd4493efSMauro Carvalho Chehab 	 * FIXME: Properly implement the set frontend properties
1786dd4493efSMauro Carvalho Chehab 	 */
1787dd4493efSMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
1788dd4493efSMauro Carvalho Chehab #endif
1789f66d81b5SMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
1790dd4493efSMauro Carvalho Chehab 
1791dd4493efSMauro Carvalho Chehab 	/*
1792dd4493efSMauro Carvalho Chehab 	 * Gate should already be opened, but it doesn't hurt to
1793dd4493efSMauro Carvalho Chehab 	 * double-check
1794dd4493efSMauro Carvalho Chehab 	 */
1795dd4493efSMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
1796dd4493efSMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 1);
1797dd4493efSMauro Carvalho Chehab 	fe->ops.tuner_ops.set_params(fe);
1798dd4493efSMauro Carvalho Chehab 
1799dd4493efSMauro Carvalho Chehab 	/*
1800dd4493efSMauro Carvalho Chehab 	 * Make it more reliable: if, for some reason, the initial
1801dd4493efSMauro Carvalho Chehab 	 * device initialization doesn't happen, initialize it when
1802dd4493efSMauro Carvalho Chehab 	 * a SBTVD parameters are adjusted.
1803dd4493efSMauro Carvalho Chehab 	 *
1804dd4493efSMauro Carvalho Chehab 	 * Unfortunately, due to a hard to track bug at tda829x/tda18271,
1805dd4493efSMauro Carvalho Chehab 	 * the agc callback logic is not called during DVB attach time,
1806dd4493efSMauro Carvalho Chehab 	 * causing mb86a20s to not be initialized with Kworld SBTVD.
1807dd4493efSMauro Carvalho Chehab 	 * So, this hack is needed, in order to make Kworld SBTVD to work.
1808dd4493efSMauro Carvalho Chehab 	 */
1809dd4493efSMauro Carvalho Chehab 	if (state->need_init)
1810dd4493efSMauro Carvalho Chehab 		mb86a20s_initfe(fe);
1811dd4493efSMauro Carvalho Chehab 
1812dd4493efSMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
1813dd4493efSMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 0);
1814d01a8ee3SMauro Carvalho Chehab 
1815dd4493efSMauro Carvalho Chehab 	rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception);
181609b6d21eSMauro Carvalho Chehab 	mb86a20s_reset_counters(fe);
1817d01a8ee3SMauro Carvalho Chehab 
1818dd4493efSMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
1819dd4493efSMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 1);
1820dd4493efSMauro Carvalho Chehab 
1821dd4493efSMauro Carvalho Chehab 	return rc;
1822dd4493efSMauro Carvalho Chehab }
1823dd4493efSMauro Carvalho Chehab 
182409b6d21eSMauro Carvalho Chehab static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe,
1825d36e418aSMauro Carvalho Chehab 					  fe_status_t *status)
1826d36e418aSMauro Carvalho Chehab {
182709b6d21eSMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
182809b6d21eSMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
182909b6d21eSMauro Carvalho Chehab 	int rc;
1830d36e418aSMauro Carvalho Chehab 
183109b6d21eSMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
1832d36e418aSMauro Carvalho Chehab 
1833d36e418aSMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
1834d36e418aSMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 0);
1835d36e418aSMauro Carvalho Chehab 
183609b6d21eSMauro Carvalho Chehab 	/* Get lock */
183709b6d21eSMauro Carvalho Chehab 	rc = mb86a20s_read_status(fe, status);
183809b6d21eSMauro Carvalho Chehab 	if (!(*status & FE_HAS_LOCK)) {
183909b6d21eSMauro Carvalho Chehab 		mb86a20s_stats_not_ready(fe);
184009b6d21eSMauro Carvalho Chehab 		mb86a20s_reset_frontend_cache(fe);
184109b6d21eSMauro Carvalho Chehab 	}
1842149d518aSMauro Carvalho Chehab 	if (rc < 0) {
1843149d518aSMauro Carvalho Chehab 		dev_err(&state->i2c->dev,
1844149d518aSMauro Carvalho Chehab 			"%s: Can't read frontend lock status\n", __func__);
184509b6d21eSMauro Carvalho Chehab 		goto error;
1846149d518aSMauro Carvalho Chehab 	}
184709b6d21eSMauro Carvalho Chehab 
184809b6d21eSMauro Carvalho Chehab 	/* Get signal strength */
184909b6d21eSMauro Carvalho Chehab 	rc = mb86a20s_read_signal_strength(fe);
185009b6d21eSMauro Carvalho Chehab 	if (rc < 0) {
1851149d518aSMauro Carvalho Chehab 		dev_err(&state->i2c->dev,
1852149d518aSMauro Carvalho Chehab 			"%s: Can't reset VBER registers.\n", __func__);
185309b6d21eSMauro Carvalho Chehab 		mb86a20s_stats_not_ready(fe);
185409b6d21eSMauro Carvalho Chehab 		mb86a20s_reset_frontend_cache(fe);
1855149d518aSMauro Carvalho Chehab 
1856149d518aSMauro Carvalho Chehab 		rc = 0;		/* Status is OK */
185709b6d21eSMauro Carvalho Chehab 		goto error;
185809b6d21eSMauro Carvalho Chehab 	}
185909b6d21eSMauro Carvalho Chehab 	/* Fill signal strength */
186009b6d21eSMauro Carvalho Chehab 	c->strength.stat[0].uvalue = rc;
186109b6d21eSMauro Carvalho Chehab 
186209b6d21eSMauro Carvalho Chehab 	if (*status & FE_HAS_LOCK) {
186309b6d21eSMauro Carvalho Chehab 		/* Get TMCC info*/
186409b6d21eSMauro Carvalho Chehab 		rc = mb86a20s_get_frontend(fe);
1865149d518aSMauro Carvalho Chehab 		if (rc < 0) {
1866149d518aSMauro Carvalho Chehab 			dev_err(&state->i2c->dev,
1867149d518aSMauro Carvalho Chehab 				"%s: Can't get FE TMCC data.\n", __func__);
1868149d518aSMauro Carvalho Chehab 			rc = 0;		/* Status is OK */
186909b6d21eSMauro Carvalho Chehab 			goto error;
187009b6d21eSMauro Carvalho Chehab 		}
187109b6d21eSMauro Carvalho Chehab 
1872149d518aSMauro Carvalho Chehab 		/* Get statistics */
1873149d518aSMauro Carvalho Chehab 		rc = mb86a20s_get_stats(fe);
1874149d518aSMauro Carvalho Chehab 		if (rc < 0 && rc != -EBUSY) {
1875149d518aSMauro Carvalho Chehab 			dev_err(&state->i2c->dev,
1876149d518aSMauro Carvalho Chehab 				"%s: Can't get FE statistics.\n", __func__);
1877149d518aSMauro Carvalho Chehab 			rc = 0;
1878149d518aSMauro Carvalho Chehab 			goto error;
1879149d518aSMauro Carvalho Chehab 		}
1880149d518aSMauro Carvalho Chehab 		rc = 0;	/* Don't return EBUSY to userspace */
1881149d518aSMauro Carvalho Chehab 	}
1882149d518aSMauro Carvalho Chehab 	goto ok;
1883149d518aSMauro Carvalho Chehab 
1884149d518aSMauro Carvalho Chehab error:
188509b6d21eSMauro Carvalho Chehab 	mb86a20s_stats_not_ready(fe);
1886d36e418aSMauro Carvalho Chehab 
1887149d518aSMauro Carvalho Chehab ok:
1888d36e418aSMauro Carvalho Chehab 	if (fe->ops.i2c_gate_ctrl)
1889d36e418aSMauro Carvalho Chehab 		fe->ops.i2c_gate_ctrl(fe, 1);
1890149d518aSMauro Carvalho Chehab 
189109b6d21eSMauro Carvalho Chehab 	return rc;
1892d36e418aSMauro Carvalho Chehab }
1893d36e418aSMauro Carvalho Chehab 
189409b6d21eSMauro Carvalho Chehab static int mb86a20s_read_signal_strength_from_cache(struct dvb_frontend *fe,
189509b6d21eSMauro Carvalho Chehab 						    u16 *strength)
189609b6d21eSMauro Carvalho Chehab {
189709b6d21eSMauro Carvalho Chehab 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
189809b6d21eSMauro Carvalho Chehab 
189909b6d21eSMauro Carvalho Chehab 
190009b6d21eSMauro Carvalho Chehab 	*strength = c->strength.stat[0].uvalue;
190109b6d21eSMauro Carvalho Chehab 
190209b6d21eSMauro Carvalho Chehab 	return 0;
190309b6d21eSMauro Carvalho Chehab }
190409b6d21eSMauro Carvalho Chehab 
190509b6d21eSMauro Carvalho Chehab static int mb86a20s_get_frontend_dummy(struct dvb_frontend *fe)
190609b6d21eSMauro Carvalho Chehab {
190709b6d21eSMauro Carvalho Chehab 	/*
190809b6d21eSMauro Carvalho Chehab 	 * get_frontend is now handled together with other stats
190909b6d21eSMauro Carvalho Chehab 	 * retrival, when read_status() is called, as some statistics
191009b6d21eSMauro Carvalho Chehab 	 * will depend on the layers detection.
191109b6d21eSMauro Carvalho Chehab 	 */
191209b6d21eSMauro Carvalho Chehab 	return 0;
191309b6d21eSMauro Carvalho Chehab };
191409b6d21eSMauro Carvalho Chehab 
19159a0bf528SMauro Carvalho Chehab static int mb86a20s_tune(struct dvb_frontend *fe,
19169a0bf528SMauro Carvalho Chehab 			bool re_tune,
19179a0bf528SMauro Carvalho Chehab 			unsigned int mode_flags,
19189a0bf528SMauro Carvalho Chehab 			unsigned int *delay,
19199a0bf528SMauro Carvalho Chehab 			fe_status_t *status)
19209a0bf528SMauro Carvalho Chehab {
1921f66d81b5SMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
19229a0bf528SMauro Carvalho Chehab 	int rc = 0;
19239a0bf528SMauro Carvalho Chehab 
1924f66d81b5SMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
19259a0bf528SMauro Carvalho Chehab 
19269a0bf528SMauro Carvalho Chehab 	if (re_tune)
19279a0bf528SMauro Carvalho Chehab 		rc = mb86a20s_set_frontend(fe);
19289a0bf528SMauro Carvalho Chehab 
19299a0bf528SMauro Carvalho Chehab 	if (!(mode_flags & FE_TUNE_MODE_ONESHOT))
193009b6d21eSMauro Carvalho Chehab 		mb86a20s_read_status_and_stats(fe, status);
19319a0bf528SMauro Carvalho Chehab 
19329a0bf528SMauro Carvalho Chehab 	return rc;
19339a0bf528SMauro Carvalho Chehab }
19349a0bf528SMauro Carvalho Chehab 
19359a0bf528SMauro Carvalho Chehab static void mb86a20s_release(struct dvb_frontend *fe)
19369a0bf528SMauro Carvalho Chehab {
19379a0bf528SMauro Carvalho Chehab 	struct mb86a20s_state *state = fe->demodulator_priv;
19389a0bf528SMauro Carvalho Chehab 
1939f66d81b5SMauro Carvalho Chehab 	dev_dbg(&state->i2c->dev, "%s called.\n", __func__);
19409a0bf528SMauro Carvalho Chehab 
19419a0bf528SMauro Carvalho Chehab 	kfree(state);
19429a0bf528SMauro Carvalho Chehab }
19439a0bf528SMauro Carvalho Chehab 
19449a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops mb86a20s_ops;
19459a0bf528SMauro Carvalho Chehab 
19469a0bf528SMauro Carvalho Chehab struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config,
19479a0bf528SMauro Carvalho Chehab 				    struct i2c_adapter *i2c)
19489a0bf528SMauro Carvalho Chehab {
1949f66d81b5SMauro Carvalho Chehab 	struct mb86a20s_state *state;
19509a0bf528SMauro Carvalho Chehab 	u8	rev;
19519a0bf528SMauro Carvalho Chehab 
1952f167e302SMauro Carvalho Chehab 	dev_dbg(&i2c->dev, "%s called.\n", __func__);
1953f167e302SMauro Carvalho Chehab 
19549a0bf528SMauro Carvalho Chehab 	/* allocate memory for the internal state */
1955f66d81b5SMauro Carvalho Chehab 	state = kzalloc(sizeof(struct mb86a20s_state), GFP_KERNEL);
19569a0bf528SMauro Carvalho Chehab 	if (state == NULL) {
1957f167e302SMauro Carvalho Chehab 		dev_err(&i2c->dev,
1958f66d81b5SMauro Carvalho Chehab 			"%s: unable to allocate memory for state\n", __func__);
19599a0bf528SMauro Carvalho Chehab 		goto error;
19609a0bf528SMauro Carvalho Chehab 	}
19619a0bf528SMauro Carvalho Chehab 
19629a0bf528SMauro Carvalho Chehab 	/* setup the state */
19639a0bf528SMauro Carvalho Chehab 	state->config = config;
19649a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
19659a0bf528SMauro Carvalho Chehab 
19669a0bf528SMauro Carvalho Chehab 	/* create dvb_frontend */
19679a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &mb86a20s_ops,
19689a0bf528SMauro Carvalho Chehab 		sizeof(struct dvb_frontend_ops));
19699a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
19709a0bf528SMauro Carvalho Chehab 
19719a0bf528SMauro Carvalho Chehab 	/* Check if it is a mb86a20s frontend */
19729a0bf528SMauro Carvalho Chehab 	rev = mb86a20s_readreg(state, 0);
19739a0bf528SMauro Carvalho Chehab 
19749a0bf528SMauro Carvalho Chehab 	if (rev == 0x13) {
1975f167e302SMauro Carvalho Chehab 		dev_info(&i2c->dev,
1976f66d81b5SMauro Carvalho Chehab 			 "Detected a Fujitsu mb86a20s frontend\n");
19779a0bf528SMauro Carvalho Chehab 	} else {
1978f167e302SMauro Carvalho Chehab 		dev_dbg(&i2c->dev,
1979f66d81b5SMauro Carvalho Chehab 			"Frontend revision %d is unknown - aborting.\n",
19809a0bf528SMauro Carvalho Chehab 		       rev);
19819a0bf528SMauro Carvalho Chehab 		goto error;
19829a0bf528SMauro Carvalho Chehab 	}
19839a0bf528SMauro Carvalho Chehab 
19849a0bf528SMauro Carvalho Chehab 	return &state->frontend;
19859a0bf528SMauro Carvalho Chehab 
19869a0bf528SMauro Carvalho Chehab error:
19879a0bf528SMauro Carvalho Chehab 	kfree(state);
19889a0bf528SMauro Carvalho Chehab 	return NULL;
19899a0bf528SMauro Carvalho Chehab }
19909a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(mb86a20s_attach);
19919a0bf528SMauro Carvalho Chehab 
19929a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops mb86a20s_ops = {
19939a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_ISDBT },
19949a0bf528SMauro Carvalho Chehab 	/* Use dib8000 values per default */
19959a0bf528SMauro Carvalho Chehab 	.info = {
19969a0bf528SMauro Carvalho Chehab 		.name = "Fujitsu mb86A20s",
19979a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_INVERSION_AUTO | FE_CAN_RECOVER |
19989a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_1_2  | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
19999a0bf528SMauro Carvalho Chehab 			FE_CAN_FEC_5_6  | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
20009a0bf528SMauro Carvalho Chehab 			FE_CAN_QPSK     | FE_CAN_QAM_16  | FE_CAN_QAM_64 |
20019a0bf528SMauro Carvalho Chehab 			FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_QAM_AUTO |
20029a0bf528SMauro Carvalho Chehab 			FE_CAN_GUARD_INTERVAL_AUTO    | FE_CAN_HIERARCHY_AUTO,
20039a0bf528SMauro Carvalho Chehab 		/* Actually, those values depend on the used tuner */
20049a0bf528SMauro Carvalho Chehab 		.frequency_min = 45000000,
20059a0bf528SMauro Carvalho Chehab 		.frequency_max = 864000000,
20069a0bf528SMauro Carvalho Chehab 		.frequency_stepsize = 62500,
20079a0bf528SMauro Carvalho Chehab 	},
20089a0bf528SMauro Carvalho Chehab 
20099a0bf528SMauro Carvalho Chehab 	.release = mb86a20s_release,
20109a0bf528SMauro Carvalho Chehab 
20119a0bf528SMauro Carvalho Chehab 	.init = mb86a20s_initfe,
20129a0bf528SMauro Carvalho Chehab 	.set_frontend = mb86a20s_set_frontend,
201309b6d21eSMauro Carvalho Chehab 	.get_frontend = mb86a20s_get_frontend_dummy,
201409b6d21eSMauro Carvalho Chehab 	.read_status = mb86a20s_read_status_and_stats,
201509b6d21eSMauro Carvalho Chehab 	.read_signal_strength = mb86a20s_read_signal_strength_from_cache,
20169a0bf528SMauro Carvalho Chehab 	.tune = mb86a20s_tune,
20179a0bf528SMauro Carvalho Chehab };
20189a0bf528SMauro Carvalho Chehab 
20199a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("DVB Frontend module for Fujitsu mb86A20s hardware");
20209a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
20219a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
2022