19a0bf528SMauro Carvalho Chehab /*
29a0bf528SMauro Carvalho Chehab     Samsung S5H1409 VSB/QAM demodulator driver
39a0bf528SMauro Carvalho Chehab 
49a0bf528SMauro Carvalho Chehab     Copyright (C) 2006 Steven Toth <stoth@linuxtv.org>
59a0bf528SMauro Carvalho Chehab 
69a0bf528SMauro Carvalho Chehab     This program is free software; you can redistribute it and/or modify
79a0bf528SMauro Carvalho Chehab     it under the terms of the GNU General Public License as published by
89a0bf528SMauro Carvalho Chehab     the Free Software Foundation; either version 2 of the License, or
99a0bf528SMauro Carvalho Chehab     (at your option) any later version.
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
149a0bf528SMauro Carvalho Chehab     GNU General Public License for more details.
159a0bf528SMauro Carvalho Chehab 
169a0bf528SMauro Carvalho Chehab     You should have received a copy of the GNU General Public License
179a0bf528SMauro Carvalho Chehab     along with this program; if not, write to the Free Software
189a0bf528SMauro Carvalho Chehab     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
199a0bf528SMauro Carvalho Chehab 
209a0bf528SMauro Carvalho Chehab */
219a0bf528SMauro Carvalho Chehab 
229a0bf528SMauro Carvalho Chehab #include <linux/kernel.h>
239a0bf528SMauro Carvalho Chehab #include <linux/init.h>
249a0bf528SMauro Carvalho Chehab #include <linux/module.h>
259a0bf528SMauro Carvalho Chehab #include <linux/string.h>
269a0bf528SMauro Carvalho Chehab #include <linux/slab.h>
279a0bf528SMauro Carvalho Chehab #include <linux/delay.h>
28fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
299a0bf528SMauro Carvalho Chehab #include "s5h1409.h"
309a0bf528SMauro Carvalho Chehab 
319a0bf528SMauro Carvalho Chehab struct s5h1409_state {
329a0bf528SMauro Carvalho Chehab 
339a0bf528SMauro Carvalho Chehab 	struct i2c_adapter *i2c;
349a0bf528SMauro Carvalho Chehab 
359a0bf528SMauro Carvalho Chehab 	/* configuration settings */
369a0bf528SMauro Carvalho Chehab 	const struct s5h1409_config *config;
379a0bf528SMauro Carvalho Chehab 
389a0bf528SMauro Carvalho Chehab 	struct dvb_frontend frontend;
399a0bf528SMauro Carvalho Chehab 
409a0bf528SMauro Carvalho Chehab 	/* previous uncorrected block counter */
410df289a2SMauro Carvalho Chehab 	enum fe_modulation current_modulation;
429a0bf528SMauro Carvalho Chehab 
439a0bf528SMauro Carvalho Chehab 	u32 current_frequency;
449a0bf528SMauro Carvalho Chehab 	int if_freq;
459a0bf528SMauro Carvalho Chehab 
469a0bf528SMauro Carvalho Chehab 	u32 is_qam_locked;
479a0bf528SMauro Carvalho Chehab 
489a0bf528SMauro Carvalho Chehab 	/* QAM tuning state goes through the following state transitions */
499a0bf528SMauro Carvalho Chehab #define QAM_STATE_UNTUNED 0
509a0bf528SMauro Carvalho Chehab #define QAM_STATE_TUNING_STARTED 1
519a0bf528SMauro Carvalho Chehab #define QAM_STATE_INTERLEAVE_SET 2
529a0bf528SMauro Carvalho Chehab #define QAM_STATE_QAM_OPTIMIZED_L1 3
539a0bf528SMauro Carvalho Chehab #define QAM_STATE_QAM_OPTIMIZED_L2 4
549a0bf528SMauro Carvalho Chehab #define QAM_STATE_QAM_OPTIMIZED_L3 5
559a0bf528SMauro Carvalho Chehab 	u8  qam_state;
569a0bf528SMauro Carvalho Chehab };
579a0bf528SMauro Carvalho Chehab 
589a0bf528SMauro Carvalho Chehab static int debug;
599a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644);
609a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Enable verbose debug messages");
619a0bf528SMauro Carvalho Chehab 
629a0bf528SMauro Carvalho Chehab #define dprintk	if (debug) printk
639a0bf528SMauro Carvalho Chehab 
649a0bf528SMauro Carvalho Chehab /* Register values to initialise the demod, this will set VSB by default */
659a0bf528SMauro Carvalho Chehab static struct init_tab {
669a0bf528SMauro Carvalho Chehab 	u8	reg;
679a0bf528SMauro Carvalho Chehab 	u16	data;
689a0bf528SMauro Carvalho Chehab } init_tab[] = {
699a0bf528SMauro Carvalho Chehab 	{ 0x00, 0x0071, },
709a0bf528SMauro Carvalho Chehab 	{ 0x01, 0x3213, },
719a0bf528SMauro Carvalho Chehab 	{ 0x09, 0x0025, },
729a0bf528SMauro Carvalho Chehab 	{ 0x1c, 0x001d, },
739a0bf528SMauro Carvalho Chehab 	{ 0x1f, 0x002d, },
749a0bf528SMauro Carvalho Chehab 	{ 0x20, 0x001d, },
759a0bf528SMauro Carvalho Chehab 	{ 0x22, 0x0022, },
769a0bf528SMauro Carvalho Chehab 	{ 0x23, 0x0020, },
779a0bf528SMauro Carvalho Chehab 	{ 0x29, 0x110f, },
789a0bf528SMauro Carvalho Chehab 	{ 0x2a, 0x10b4, },
799a0bf528SMauro Carvalho Chehab 	{ 0x2b, 0x10ae, },
809a0bf528SMauro Carvalho Chehab 	{ 0x2c, 0x0031, },
819a0bf528SMauro Carvalho Chehab 	{ 0x31, 0x010d, },
829a0bf528SMauro Carvalho Chehab 	{ 0x32, 0x0100, },
839a0bf528SMauro Carvalho Chehab 	{ 0x44, 0x0510, },
849a0bf528SMauro Carvalho Chehab 	{ 0x54, 0x0104, },
859a0bf528SMauro Carvalho Chehab 	{ 0x58, 0x2222, },
869a0bf528SMauro Carvalho Chehab 	{ 0x59, 0x1162, },
879a0bf528SMauro Carvalho Chehab 	{ 0x5a, 0x3211, },
889a0bf528SMauro Carvalho Chehab 	{ 0x5d, 0x0370, },
899a0bf528SMauro Carvalho Chehab 	{ 0x5e, 0x0296, },
909a0bf528SMauro Carvalho Chehab 	{ 0x61, 0x0010, },
919a0bf528SMauro Carvalho Chehab 	{ 0x63, 0x4a00, },
929a0bf528SMauro Carvalho Chehab 	{ 0x65, 0x0800, },
939a0bf528SMauro Carvalho Chehab 	{ 0x71, 0x0003, },
949a0bf528SMauro Carvalho Chehab 	{ 0x72, 0x0470, },
959a0bf528SMauro Carvalho Chehab 	{ 0x81, 0x0002, },
969a0bf528SMauro Carvalho Chehab 	{ 0x82, 0x0600, },
979a0bf528SMauro Carvalho Chehab 	{ 0x86, 0x0002, },
989a0bf528SMauro Carvalho Chehab 	{ 0x8a, 0x2c38, },
999a0bf528SMauro Carvalho Chehab 	{ 0x8b, 0x2a37, },
1009a0bf528SMauro Carvalho Chehab 	{ 0x92, 0x302f, },
1019a0bf528SMauro Carvalho Chehab 	{ 0x93, 0x3332, },
1029a0bf528SMauro Carvalho Chehab 	{ 0x96, 0x000c, },
1039a0bf528SMauro Carvalho Chehab 	{ 0x99, 0x0101, },
1049a0bf528SMauro Carvalho Chehab 	{ 0x9c, 0x2e37, },
1059a0bf528SMauro Carvalho Chehab 	{ 0x9d, 0x2c37, },
1069a0bf528SMauro Carvalho Chehab 	{ 0x9e, 0x2c37, },
1079a0bf528SMauro Carvalho Chehab 	{ 0xab, 0x0100, },
1089a0bf528SMauro Carvalho Chehab 	{ 0xac, 0x1003, },
1099a0bf528SMauro Carvalho Chehab 	{ 0xad, 0x103f, },
1109a0bf528SMauro Carvalho Chehab 	{ 0xe2, 0x0100, },
1119a0bf528SMauro Carvalho Chehab 	{ 0xe3, 0x1000, },
1129a0bf528SMauro Carvalho Chehab 	{ 0x28, 0x1010, },
1139a0bf528SMauro Carvalho Chehab 	{ 0xb1, 0x000e, },
1149a0bf528SMauro Carvalho Chehab };
1159a0bf528SMauro Carvalho Chehab 
1169a0bf528SMauro Carvalho Chehab /* VSB SNR lookup table */
1179a0bf528SMauro Carvalho Chehab static struct vsb_snr_tab {
1189a0bf528SMauro Carvalho Chehab 	u16	val;
1199a0bf528SMauro Carvalho Chehab 	u16	data;
1209a0bf528SMauro Carvalho Chehab } vsb_snr_tab[] = {
1219a0bf528SMauro Carvalho Chehab 	{  924, 300, },
1229a0bf528SMauro Carvalho Chehab 	{  923, 300, },
1239a0bf528SMauro Carvalho Chehab 	{  918, 295, },
1249a0bf528SMauro Carvalho Chehab 	{  915, 290, },
1259a0bf528SMauro Carvalho Chehab 	{  911, 285, },
1269a0bf528SMauro Carvalho Chehab 	{  906, 280, },
1279a0bf528SMauro Carvalho Chehab 	{  901, 275, },
1289a0bf528SMauro Carvalho Chehab 	{  896, 270, },
1299a0bf528SMauro Carvalho Chehab 	{  891, 265, },
1309a0bf528SMauro Carvalho Chehab 	{  885, 260, },
1319a0bf528SMauro Carvalho Chehab 	{  879, 255, },
1329a0bf528SMauro Carvalho Chehab 	{  873, 250, },
1339a0bf528SMauro Carvalho Chehab 	{  864, 245, },
1349a0bf528SMauro Carvalho Chehab 	{  858, 240, },
1359a0bf528SMauro Carvalho Chehab 	{  850, 235, },
1369a0bf528SMauro Carvalho Chehab 	{  841, 230, },
1379a0bf528SMauro Carvalho Chehab 	{  832, 225, },
1389a0bf528SMauro Carvalho Chehab 	{  823, 220, },
1399a0bf528SMauro Carvalho Chehab 	{  812, 215, },
1409a0bf528SMauro Carvalho Chehab 	{  802, 210, },
1419a0bf528SMauro Carvalho Chehab 	{  788, 205, },
1429a0bf528SMauro Carvalho Chehab 	{  778, 200, },
1439a0bf528SMauro Carvalho Chehab 	{  767, 195, },
1449a0bf528SMauro Carvalho Chehab 	{  753, 190, },
1459a0bf528SMauro Carvalho Chehab 	{  740, 185, },
1469a0bf528SMauro Carvalho Chehab 	{  725, 180, },
1479a0bf528SMauro Carvalho Chehab 	{  707, 175, },
1489a0bf528SMauro Carvalho Chehab 	{  689, 170, },
1499a0bf528SMauro Carvalho Chehab 	{  671, 165, },
1509a0bf528SMauro Carvalho Chehab 	{  656, 160, },
1519a0bf528SMauro Carvalho Chehab 	{  637, 155, },
1529a0bf528SMauro Carvalho Chehab 	{  616, 150, },
1539a0bf528SMauro Carvalho Chehab 	{  542, 145, },
1549a0bf528SMauro Carvalho Chehab 	{  519, 140, },
1559a0bf528SMauro Carvalho Chehab 	{  507, 135, },
1569a0bf528SMauro Carvalho Chehab 	{  497, 130, },
1579a0bf528SMauro Carvalho Chehab 	{  492, 125, },
1589a0bf528SMauro Carvalho Chehab 	{  474, 120, },
1599a0bf528SMauro Carvalho Chehab 	{  300, 111, },
1609a0bf528SMauro Carvalho Chehab 	{    0,   0, },
1619a0bf528SMauro Carvalho Chehab };
1629a0bf528SMauro Carvalho Chehab 
1639a0bf528SMauro Carvalho Chehab /* QAM64 SNR lookup table */
1649a0bf528SMauro Carvalho Chehab static struct qam64_snr_tab {
1659a0bf528SMauro Carvalho Chehab 	u16	val;
1669a0bf528SMauro Carvalho Chehab 	u16	data;
1679a0bf528SMauro Carvalho Chehab } qam64_snr_tab[] = {
1689a0bf528SMauro Carvalho Chehab 	{    1,   0, },
1699a0bf528SMauro Carvalho Chehab 	{   12, 300, },
1709a0bf528SMauro Carvalho Chehab 	{   15, 290, },
1719a0bf528SMauro Carvalho Chehab 	{   18, 280, },
1729a0bf528SMauro Carvalho Chehab 	{   22, 270, },
1739a0bf528SMauro Carvalho Chehab 	{   23, 268, },
1749a0bf528SMauro Carvalho Chehab 	{   24, 266, },
1759a0bf528SMauro Carvalho Chehab 	{   25, 264, },
1769a0bf528SMauro Carvalho Chehab 	{   27, 262, },
1779a0bf528SMauro Carvalho Chehab 	{   28, 260, },
1789a0bf528SMauro Carvalho Chehab 	{   29, 258, },
1799a0bf528SMauro Carvalho Chehab 	{   30, 256, },
1809a0bf528SMauro Carvalho Chehab 	{   32, 254, },
1819a0bf528SMauro Carvalho Chehab 	{   33, 252, },
1829a0bf528SMauro Carvalho Chehab 	{   34, 250, },
1839a0bf528SMauro Carvalho Chehab 	{   35, 249, },
1849a0bf528SMauro Carvalho Chehab 	{   36, 248, },
1859a0bf528SMauro Carvalho Chehab 	{   37, 247, },
1869a0bf528SMauro Carvalho Chehab 	{   38, 246, },
1879a0bf528SMauro Carvalho Chehab 	{   39, 245, },
1889a0bf528SMauro Carvalho Chehab 	{   40, 244, },
1899a0bf528SMauro Carvalho Chehab 	{   41, 243, },
1909a0bf528SMauro Carvalho Chehab 	{   42, 241, },
1919a0bf528SMauro Carvalho Chehab 	{   43, 240, },
1929a0bf528SMauro Carvalho Chehab 	{   44, 239, },
1939a0bf528SMauro Carvalho Chehab 	{   45, 238, },
1949a0bf528SMauro Carvalho Chehab 	{   46, 237, },
1959a0bf528SMauro Carvalho Chehab 	{   47, 236, },
1969a0bf528SMauro Carvalho Chehab 	{   48, 235, },
1979a0bf528SMauro Carvalho Chehab 	{   49, 234, },
1989a0bf528SMauro Carvalho Chehab 	{   50, 233, },
1999a0bf528SMauro Carvalho Chehab 	{   51, 232, },
2009a0bf528SMauro Carvalho Chehab 	{   52, 231, },
2019a0bf528SMauro Carvalho Chehab 	{   53, 230, },
2029a0bf528SMauro Carvalho Chehab 	{   55, 229, },
2039a0bf528SMauro Carvalho Chehab 	{   56, 228, },
2049a0bf528SMauro Carvalho Chehab 	{   57, 227, },
2059a0bf528SMauro Carvalho Chehab 	{   58, 226, },
2069a0bf528SMauro Carvalho Chehab 	{   59, 225, },
2079a0bf528SMauro Carvalho Chehab 	{   60, 224, },
2089a0bf528SMauro Carvalho Chehab 	{   62, 223, },
2099a0bf528SMauro Carvalho Chehab 	{   63, 222, },
2109a0bf528SMauro Carvalho Chehab 	{   65, 221, },
2119a0bf528SMauro Carvalho Chehab 	{   66, 220, },
2129a0bf528SMauro Carvalho Chehab 	{   68, 219, },
2139a0bf528SMauro Carvalho Chehab 	{   69, 218, },
2149a0bf528SMauro Carvalho Chehab 	{   70, 217, },
2159a0bf528SMauro Carvalho Chehab 	{   72, 216, },
2169a0bf528SMauro Carvalho Chehab 	{   73, 215, },
2179a0bf528SMauro Carvalho Chehab 	{   75, 214, },
2189a0bf528SMauro Carvalho Chehab 	{   76, 213, },
2199a0bf528SMauro Carvalho Chehab 	{   78, 212, },
2209a0bf528SMauro Carvalho Chehab 	{   80, 211, },
2219a0bf528SMauro Carvalho Chehab 	{   81, 210, },
2229a0bf528SMauro Carvalho Chehab 	{   83, 209, },
2239a0bf528SMauro Carvalho Chehab 	{   84, 208, },
2249a0bf528SMauro Carvalho Chehab 	{   85, 207, },
2259a0bf528SMauro Carvalho Chehab 	{   87, 206, },
2269a0bf528SMauro Carvalho Chehab 	{   89, 205, },
2279a0bf528SMauro Carvalho Chehab 	{   91, 204, },
2289a0bf528SMauro Carvalho Chehab 	{   93, 203, },
2299a0bf528SMauro Carvalho Chehab 	{   95, 202, },
2309a0bf528SMauro Carvalho Chehab 	{   96, 201, },
2319a0bf528SMauro Carvalho Chehab 	{  104, 200, },
2329a0bf528SMauro Carvalho Chehab 	{  255,   0, },
2339a0bf528SMauro Carvalho Chehab };
2349a0bf528SMauro Carvalho Chehab 
2359a0bf528SMauro Carvalho Chehab /* QAM256 SNR lookup table */
2369a0bf528SMauro Carvalho Chehab static struct qam256_snr_tab {
2379a0bf528SMauro Carvalho Chehab 	u16	val;
2389a0bf528SMauro Carvalho Chehab 	u16	data;
2399a0bf528SMauro Carvalho Chehab } qam256_snr_tab[] = {
2409a0bf528SMauro Carvalho Chehab 	{    1,   0, },
2419a0bf528SMauro Carvalho Chehab 	{   12, 400, },
2429a0bf528SMauro Carvalho Chehab 	{   13, 390, },
2439a0bf528SMauro Carvalho Chehab 	{   15, 380, },
2449a0bf528SMauro Carvalho Chehab 	{   17, 360, },
2459a0bf528SMauro Carvalho Chehab 	{   19, 350, },
2469a0bf528SMauro Carvalho Chehab 	{   22, 348, },
2479a0bf528SMauro Carvalho Chehab 	{   23, 346, },
2489a0bf528SMauro Carvalho Chehab 	{   24, 344, },
2499a0bf528SMauro Carvalho Chehab 	{   25, 342, },
2509a0bf528SMauro Carvalho Chehab 	{   26, 340, },
2519a0bf528SMauro Carvalho Chehab 	{   27, 336, },
2529a0bf528SMauro Carvalho Chehab 	{   28, 334, },
2539a0bf528SMauro Carvalho Chehab 	{   29, 332, },
2549a0bf528SMauro Carvalho Chehab 	{   30, 330, },
2559a0bf528SMauro Carvalho Chehab 	{   31, 328, },
2569a0bf528SMauro Carvalho Chehab 	{   32, 326, },
2579a0bf528SMauro Carvalho Chehab 	{   33, 325, },
2589a0bf528SMauro Carvalho Chehab 	{   34, 322, },
2599a0bf528SMauro Carvalho Chehab 	{   35, 320, },
2609a0bf528SMauro Carvalho Chehab 	{   37, 318, },
2619a0bf528SMauro Carvalho Chehab 	{   39, 316, },
2629a0bf528SMauro Carvalho Chehab 	{   40, 314, },
2639a0bf528SMauro Carvalho Chehab 	{   41, 312, },
2649a0bf528SMauro Carvalho Chehab 	{   42, 310, },
2659a0bf528SMauro Carvalho Chehab 	{   43, 308, },
2669a0bf528SMauro Carvalho Chehab 	{   46, 306, },
2679a0bf528SMauro Carvalho Chehab 	{   47, 304, },
2689a0bf528SMauro Carvalho Chehab 	{   49, 302, },
2699a0bf528SMauro Carvalho Chehab 	{   51, 300, },
2709a0bf528SMauro Carvalho Chehab 	{   53, 298, },
2719a0bf528SMauro Carvalho Chehab 	{   54, 297, },
2729a0bf528SMauro Carvalho Chehab 	{   55, 296, },
2739a0bf528SMauro Carvalho Chehab 	{   56, 295, },
2749a0bf528SMauro Carvalho Chehab 	{   57, 294, },
2759a0bf528SMauro Carvalho Chehab 	{   59, 293, },
2769a0bf528SMauro Carvalho Chehab 	{   60, 292, },
2779a0bf528SMauro Carvalho Chehab 	{   61, 291, },
2789a0bf528SMauro Carvalho Chehab 	{   63, 290, },
2799a0bf528SMauro Carvalho Chehab 	{   64, 289, },
2809a0bf528SMauro Carvalho Chehab 	{   65, 288, },
2819a0bf528SMauro Carvalho Chehab 	{   66, 287, },
2829a0bf528SMauro Carvalho Chehab 	{   68, 286, },
2839a0bf528SMauro Carvalho Chehab 	{   69, 285, },
2849a0bf528SMauro Carvalho Chehab 	{   71, 284, },
2859a0bf528SMauro Carvalho Chehab 	{   72, 283, },
2869a0bf528SMauro Carvalho Chehab 	{   74, 282, },
2879a0bf528SMauro Carvalho Chehab 	{   75, 281, },
2889a0bf528SMauro Carvalho Chehab 	{   76, 280, },
2899a0bf528SMauro Carvalho Chehab 	{   77, 279, },
2909a0bf528SMauro Carvalho Chehab 	{   78, 278, },
2919a0bf528SMauro Carvalho Chehab 	{   81, 277, },
2929a0bf528SMauro Carvalho Chehab 	{   83, 276, },
2939a0bf528SMauro Carvalho Chehab 	{   84, 275, },
2949a0bf528SMauro Carvalho Chehab 	{   86, 274, },
2959a0bf528SMauro Carvalho Chehab 	{   87, 273, },
2969a0bf528SMauro Carvalho Chehab 	{   89, 272, },
2979a0bf528SMauro Carvalho Chehab 	{   90, 271, },
2989a0bf528SMauro Carvalho Chehab 	{   92, 270, },
2999a0bf528SMauro Carvalho Chehab 	{   93, 269, },
3009a0bf528SMauro Carvalho Chehab 	{   95, 268, },
3019a0bf528SMauro Carvalho Chehab 	{   96, 267, },
3029a0bf528SMauro Carvalho Chehab 	{   98, 266, },
3039a0bf528SMauro Carvalho Chehab 	{  100, 265, },
3049a0bf528SMauro Carvalho Chehab 	{  102, 264, },
3059a0bf528SMauro Carvalho Chehab 	{  104, 263, },
3069a0bf528SMauro Carvalho Chehab 	{  105, 262, },
3079a0bf528SMauro Carvalho Chehab 	{  106, 261, },
3089a0bf528SMauro Carvalho Chehab 	{  110, 260, },
3099a0bf528SMauro Carvalho Chehab 	{  255,   0, },
3109a0bf528SMauro Carvalho Chehab };
3119a0bf528SMauro Carvalho Chehab 
3129a0bf528SMauro Carvalho Chehab /* 8 bit registers, 16 bit values */
3139a0bf528SMauro Carvalho Chehab static int s5h1409_writereg(struct s5h1409_state *state, u8 reg, u16 data)
3149a0bf528SMauro Carvalho Chehab {
3159a0bf528SMauro Carvalho Chehab 	int ret;
3169a0bf528SMauro Carvalho Chehab 	u8 buf[] = { reg, data >> 8,  data & 0xff };
3179a0bf528SMauro Carvalho Chehab 
3189a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg = { .addr = state->config->demod_address,
3199a0bf528SMauro Carvalho Chehab 			       .flags = 0, .buf = buf, .len = 3 };
3209a0bf528SMauro Carvalho Chehab 
3219a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, &msg, 1);
3229a0bf528SMauro Carvalho Chehab 
3239a0bf528SMauro Carvalho Chehab 	if (ret != 1)
3244bd69e7bSMauro Carvalho Chehab 		printk(KERN_ERR "%s: error (reg == 0x%02x, val == 0x%04x, ret == %i)\n",
3254bd69e7bSMauro Carvalho Chehab 		       __func__, reg, data, ret);
3269a0bf528SMauro Carvalho Chehab 
3279a0bf528SMauro Carvalho Chehab 	return (ret != 1) ? -1 : 0;
3289a0bf528SMauro Carvalho Chehab }
3299a0bf528SMauro Carvalho Chehab 
3309a0bf528SMauro Carvalho Chehab static u16 s5h1409_readreg(struct s5h1409_state *state, u8 reg)
3319a0bf528SMauro Carvalho Chehab {
3329a0bf528SMauro Carvalho Chehab 	int ret;
3339a0bf528SMauro Carvalho Chehab 	u8 b0[] = { reg };
3349a0bf528SMauro Carvalho Chehab 	u8 b1[] = { 0, 0 };
3359a0bf528SMauro Carvalho Chehab 
3369a0bf528SMauro Carvalho Chehab 	struct i2c_msg msg[] = {
3379a0bf528SMauro Carvalho Chehab 		{ .addr = state->config->demod_address, .flags = 0,
3389a0bf528SMauro Carvalho Chehab 		  .buf = b0, .len = 1 },
3399a0bf528SMauro Carvalho Chehab 		{ .addr = state->config->demod_address, .flags = I2C_M_RD,
3409a0bf528SMauro Carvalho Chehab 		  .buf = b1, .len = 2 } };
3419a0bf528SMauro Carvalho Chehab 
3429a0bf528SMauro Carvalho Chehab 	ret = i2c_transfer(state->i2c, msg, 2);
3439a0bf528SMauro Carvalho Chehab 
3449a0bf528SMauro Carvalho Chehab 	if (ret != 2)
3459a0bf528SMauro Carvalho Chehab 		printk("%s: readreg error (ret == %i)\n", __func__, ret);
3469a0bf528SMauro Carvalho Chehab 	return (b1[0] << 8) | b1[1];
3479a0bf528SMauro Carvalho Chehab }
3489a0bf528SMauro Carvalho Chehab 
3499a0bf528SMauro Carvalho Chehab static int s5h1409_softreset(struct dvb_frontend *fe)
3509a0bf528SMauro Carvalho Chehab {
3519a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
3529a0bf528SMauro Carvalho Chehab 
3539a0bf528SMauro Carvalho Chehab 	dprintk("%s()\n", __func__);
3549a0bf528SMauro Carvalho Chehab 
3559a0bf528SMauro Carvalho Chehab 	s5h1409_writereg(state, 0xf5, 0);
3569a0bf528SMauro Carvalho Chehab 	s5h1409_writereg(state, 0xf5, 1);
3579a0bf528SMauro Carvalho Chehab 	state->is_qam_locked = 0;
3589a0bf528SMauro Carvalho Chehab 	state->qam_state = QAM_STATE_UNTUNED;
3599a0bf528SMauro Carvalho Chehab 	return 0;
3609a0bf528SMauro Carvalho Chehab }
3619a0bf528SMauro Carvalho Chehab 
3629a0bf528SMauro Carvalho Chehab #define S5H1409_VSB_IF_FREQ 5380
3639a0bf528SMauro Carvalho Chehab #define S5H1409_QAM_IF_FREQ (state->config->qam_if)
3649a0bf528SMauro Carvalho Chehab 
3659a0bf528SMauro Carvalho Chehab static int s5h1409_set_if_freq(struct dvb_frontend *fe, int KHz)
3669a0bf528SMauro Carvalho Chehab {
3679a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
3689a0bf528SMauro Carvalho Chehab 
3699a0bf528SMauro Carvalho Chehab 	dprintk("%s(%d KHz)\n", __func__, KHz);
3709a0bf528SMauro Carvalho Chehab 
3719a0bf528SMauro Carvalho Chehab 	switch (KHz) {
3729a0bf528SMauro Carvalho Chehab 	case 4000:
3739a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0x87, 0x014b);
3749a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0x88, 0x0cb5);
3759a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0x89, 0x03e2);
3769a0bf528SMauro Carvalho Chehab 		break;
3779a0bf528SMauro Carvalho Chehab 	case 5380:
3789a0bf528SMauro Carvalho Chehab 	case 44000:
3799a0bf528SMauro Carvalho Chehab 	default:
3809a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0x87, 0x01be);
3819a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0x88, 0x0436);
3829a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0x89, 0x054d);
3839a0bf528SMauro Carvalho Chehab 		break;
3849a0bf528SMauro Carvalho Chehab 	}
3859a0bf528SMauro Carvalho Chehab 	state->if_freq = KHz;
3869a0bf528SMauro Carvalho Chehab 
3879a0bf528SMauro Carvalho Chehab 	return 0;
3889a0bf528SMauro Carvalho Chehab }
3899a0bf528SMauro Carvalho Chehab 
3909a0bf528SMauro Carvalho Chehab static int s5h1409_set_spectralinversion(struct dvb_frontend *fe, int inverted)
3919a0bf528SMauro Carvalho Chehab {
3929a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
3939a0bf528SMauro Carvalho Chehab 
3949a0bf528SMauro Carvalho Chehab 	dprintk("%s(%d)\n", __func__, inverted);
3959a0bf528SMauro Carvalho Chehab 
3969a0bf528SMauro Carvalho Chehab 	if (inverted == 1)
3979a0bf528SMauro Carvalho Chehab 		return s5h1409_writereg(state, 0x1b, 0x1101); /* Inverted */
3989a0bf528SMauro Carvalho Chehab 	else
3999a0bf528SMauro Carvalho Chehab 		return s5h1409_writereg(state, 0x1b, 0x0110); /* Normal */
4009a0bf528SMauro Carvalho Chehab }
4019a0bf528SMauro Carvalho Chehab 
4029a0bf528SMauro Carvalho Chehab static int s5h1409_enable_modulation(struct dvb_frontend *fe,
4030df289a2SMauro Carvalho Chehab 				     enum fe_modulation m)
4049a0bf528SMauro Carvalho Chehab {
4059a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
4069a0bf528SMauro Carvalho Chehab 
4079a0bf528SMauro Carvalho Chehab 	dprintk("%s(0x%08x)\n", __func__, m);
4089a0bf528SMauro Carvalho Chehab 
4099a0bf528SMauro Carvalho Chehab 	switch (m) {
4109a0bf528SMauro Carvalho Chehab 	case VSB_8:
4119a0bf528SMauro Carvalho Chehab 		dprintk("%s() VSB_8\n", __func__);
4129a0bf528SMauro Carvalho Chehab 		if (state->if_freq != S5H1409_VSB_IF_FREQ)
4139a0bf528SMauro Carvalho Chehab 			s5h1409_set_if_freq(fe, S5H1409_VSB_IF_FREQ);
4149a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0xf4, 0);
4159a0bf528SMauro Carvalho Chehab 		break;
4169a0bf528SMauro Carvalho Chehab 	case QAM_64:
4179a0bf528SMauro Carvalho Chehab 	case QAM_256:
4189a0bf528SMauro Carvalho Chehab 	case QAM_AUTO:
4199a0bf528SMauro Carvalho Chehab 		dprintk("%s() QAM_AUTO (64/256)\n", __func__);
4209a0bf528SMauro Carvalho Chehab 		if (state->if_freq != S5H1409_QAM_IF_FREQ)
4219a0bf528SMauro Carvalho Chehab 			s5h1409_set_if_freq(fe, S5H1409_QAM_IF_FREQ);
4229a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0xf4, 1);
4239a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0x85, 0x110);
4249a0bf528SMauro Carvalho Chehab 		break;
4259a0bf528SMauro Carvalho Chehab 	default:
4269a0bf528SMauro Carvalho Chehab 		dprintk("%s() Invalid modulation\n", __func__);
4279a0bf528SMauro Carvalho Chehab 		return -EINVAL;
4289a0bf528SMauro Carvalho Chehab 	}
4299a0bf528SMauro Carvalho Chehab 
4309a0bf528SMauro Carvalho Chehab 	state->current_modulation = m;
4319a0bf528SMauro Carvalho Chehab 	s5h1409_softreset(fe);
4329a0bf528SMauro Carvalho Chehab 
4339a0bf528SMauro Carvalho Chehab 	return 0;
4349a0bf528SMauro Carvalho Chehab }
4359a0bf528SMauro Carvalho Chehab 
4369a0bf528SMauro Carvalho Chehab static int s5h1409_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
4379a0bf528SMauro Carvalho Chehab {
4389a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
4399a0bf528SMauro Carvalho Chehab 
4409a0bf528SMauro Carvalho Chehab 	dprintk("%s(%d)\n", __func__, enable);
4419a0bf528SMauro Carvalho Chehab 
4429a0bf528SMauro Carvalho Chehab 	if (enable)
4439a0bf528SMauro Carvalho Chehab 		return s5h1409_writereg(state, 0xf3, 1);
4449a0bf528SMauro Carvalho Chehab 	else
4459a0bf528SMauro Carvalho Chehab 		return s5h1409_writereg(state, 0xf3, 0);
4469a0bf528SMauro Carvalho Chehab }
4479a0bf528SMauro Carvalho Chehab 
4489a0bf528SMauro Carvalho Chehab static int s5h1409_set_gpio(struct dvb_frontend *fe, int enable)
4499a0bf528SMauro Carvalho Chehab {
4509a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
4519a0bf528SMauro Carvalho Chehab 
4529a0bf528SMauro Carvalho Chehab 	dprintk("%s(%d)\n", __func__, enable);
4539a0bf528SMauro Carvalho Chehab 
4549a0bf528SMauro Carvalho Chehab 	if (enable)
4559a0bf528SMauro Carvalho Chehab 		return s5h1409_writereg(state, 0xe3,
4569a0bf528SMauro Carvalho Chehab 			s5h1409_readreg(state, 0xe3) | 0x1100);
4579a0bf528SMauro Carvalho Chehab 	else
4589a0bf528SMauro Carvalho Chehab 		return s5h1409_writereg(state, 0xe3,
4599a0bf528SMauro Carvalho Chehab 			s5h1409_readreg(state, 0xe3) & 0xfeff);
4609a0bf528SMauro Carvalho Chehab }
4619a0bf528SMauro Carvalho Chehab 
4629a0bf528SMauro Carvalho Chehab static int s5h1409_sleep(struct dvb_frontend *fe, int enable)
4639a0bf528SMauro Carvalho Chehab {
4649a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
4659a0bf528SMauro Carvalho Chehab 
4669a0bf528SMauro Carvalho Chehab 	dprintk("%s(%d)\n", __func__, enable);
4679a0bf528SMauro Carvalho Chehab 
4689a0bf528SMauro Carvalho Chehab 	return s5h1409_writereg(state, 0xf2, enable);
4699a0bf528SMauro Carvalho Chehab }
4709a0bf528SMauro Carvalho Chehab 
4719a0bf528SMauro Carvalho Chehab static int s5h1409_register_reset(struct dvb_frontend *fe)
4729a0bf528SMauro Carvalho Chehab {
4739a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
4749a0bf528SMauro Carvalho Chehab 
4759a0bf528SMauro Carvalho Chehab 	dprintk("%s()\n", __func__);
4769a0bf528SMauro Carvalho Chehab 
4779a0bf528SMauro Carvalho Chehab 	return s5h1409_writereg(state, 0xfa, 0);
4789a0bf528SMauro Carvalho Chehab }
4799a0bf528SMauro Carvalho Chehab 
4809a0bf528SMauro Carvalho Chehab static void s5h1409_set_qam_amhum_mode(struct dvb_frontend *fe)
4819a0bf528SMauro Carvalho Chehab {
4829a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
4839a0bf528SMauro Carvalho Chehab 	u16 reg;
4849a0bf528SMauro Carvalho Chehab 
4859a0bf528SMauro Carvalho Chehab 	if (state->qam_state < QAM_STATE_INTERLEAVE_SET) {
4869a0bf528SMauro Carvalho Chehab 		/* We should not perform amhum optimization until
4879a0bf528SMauro Carvalho Chehab 		   the interleave mode has been configured */
4889a0bf528SMauro Carvalho Chehab 		return;
4899a0bf528SMauro Carvalho Chehab 	}
4909a0bf528SMauro Carvalho Chehab 
4919a0bf528SMauro Carvalho Chehab 	if (state->qam_state == QAM_STATE_QAM_OPTIMIZED_L3) {
4929a0bf528SMauro Carvalho Chehab 		/* We've already reached the maximum optimization level, so
4939a0bf528SMauro Carvalho Chehab 		   dont bother banging on the status registers */
4949a0bf528SMauro Carvalho Chehab 		return;
4959a0bf528SMauro Carvalho Chehab 	}
4969a0bf528SMauro Carvalho Chehab 
4979a0bf528SMauro Carvalho Chehab 	/* QAM EQ lock check */
4989a0bf528SMauro Carvalho Chehab 	reg = s5h1409_readreg(state, 0xf0);
4999a0bf528SMauro Carvalho Chehab 
5009a0bf528SMauro Carvalho Chehab 	if ((reg >> 13) & 0x1) {
5019a0bf528SMauro Carvalho Chehab 		reg &= 0xff;
5029a0bf528SMauro Carvalho Chehab 
5039a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0x96, 0x000c);
5049a0bf528SMauro Carvalho Chehab 		if (reg < 0x68) {
5059a0bf528SMauro Carvalho Chehab 			if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L3) {
5069a0bf528SMauro Carvalho Chehab 				dprintk("%s() setting QAM state to OPT_L3\n",
5079a0bf528SMauro Carvalho Chehab 					__func__);
5089a0bf528SMauro Carvalho Chehab 				s5h1409_writereg(state, 0x93, 0x3130);
5099a0bf528SMauro Carvalho Chehab 				s5h1409_writereg(state, 0x9e, 0x2836);
5109a0bf528SMauro Carvalho Chehab 				state->qam_state = QAM_STATE_QAM_OPTIMIZED_L3;
5119a0bf528SMauro Carvalho Chehab 			}
5129a0bf528SMauro Carvalho Chehab 		} else {
5139a0bf528SMauro Carvalho Chehab 			if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L2) {
5149a0bf528SMauro Carvalho Chehab 				dprintk("%s() setting QAM state to OPT_L2\n",
5159a0bf528SMauro Carvalho Chehab 					__func__);
5169a0bf528SMauro Carvalho Chehab 				s5h1409_writereg(state, 0x93, 0x3332);
5179a0bf528SMauro Carvalho Chehab 				s5h1409_writereg(state, 0x9e, 0x2c37);
5189a0bf528SMauro Carvalho Chehab 				state->qam_state = QAM_STATE_QAM_OPTIMIZED_L2;
5199a0bf528SMauro Carvalho Chehab 			}
5209a0bf528SMauro Carvalho Chehab 		}
5219a0bf528SMauro Carvalho Chehab 
5229a0bf528SMauro Carvalho Chehab 	} else {
5239a0bf528SMauro Carvalho Chehab 		if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L1) {
5249a0bf528SMauro Carvalho Chehab 			dprintk("%s() setting QAM state to OPT_L1\n", __func__);
5259a0bf528SMauro Carvalho Chehab 			s5h1409_writereg(state, 0x96, 0x0008);
5269a0bf528SMauro Carvalho Chehab 			s5h1409_writereg(state, 0x93, 0x3332);
5279a0bf528SMauro Carvalho Chehab 			s5h1409_writereg(state, 0x9e, 0x2c37);
5289a0bf528SMauro Carvalho Chehab 			state->qam_state = QAM_STATE_QAM_OPTIMIZED_L1;
5299a0bf528SMauro Carvalho Chehab 		}
5309a0bf528SMauro Carvalho Chehab 	}
5319a0bf528SMauro Carvalho Chehab }
5329a0bf528SMauro Carvalho Chehab 
5339a0bf528SMauro Carvalho Chehab static void s5h1409_set_qam_amhum_mode_legacy(struct dvb_frontend *fe)
5349a0bf528SMauro Carvalho Chehab {
5359a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
5369a0bf528SMauro Carvalho Chehab 	u16 reg;
5379a0bf528SMauro Carvalho Chehab 
5389a0bf528SMauro Carvalho Chehab 	if (state->is_qam_locked)
5399a0bf528SMauro Carvalho Chehab 		return;
5409a0bf528SMauro Carvalho Chehab 
5419a0bf528SMauro Carvalho Chehab 	/* QAM EQ lock check */
5429a0bf528SMauro Carvalho Chehab 	reg = s5h1409_readreg(state, 0xf0);
5439a0bf528SMauro Carvalho Chehab 
5449a0bf528SMauro Carvalho Chehab 	if ((reg >> 13) & 0x1) {
5459a0bf528SMauro Carvalho Chehab 
5469a0bf528SMauro Carvalho Chehab 		state->is_qam_locked = 1;
5479a0bf528SMauro Carvalho Chehab 		reg &= 0xff;
5489a0bf528SMauro Carvalho Chehab 
5499a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0x96, 0x00c);
5509a0bf528SMauro Carvalho Chehab 		if ((reg < 0x38) || (reg > 0x68)) {
5519a0bf528SMauro Carvalho Chehab 			s5h1409_writereg(state, 0x93, 0x3332);
5529a0bf528SMauro Carvalho Chehab 			s5h1409_writereg(state, 0x9e, 0x2c37);
5539a0bf528SMauro Carvalho Chehab 		} else {
5549a0bf528SMauro Carvalho Chehab 			s5h1409_writereg(state, 0x93, 0x3130);
5559a0bf528SMauro Carvalho Chehab 			s5h1409_writereg(state, 0x9e, 0x2836);
5569a0bf528SMauro Carvalho Chehab 		}
5579a0bf528SMauro Carvalho Chehab 
5589a0bf528SMauro Carvalho Chehab 	} else {
5599a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0x96, 0x0008);
5609a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0x93, 0x3332);
5619a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0x9e, 0x2c37);
5629a0bf528SMauro Carvalho Chehab 	}
5639a0bf528SMauro Carvalho Chehab }
5649a0bf528SMauro Carvalho Chehab 
5659a0bf528SMauro Carvalho Chehab static void s5h1409_set_qam_interleave_mode(struct dvb_frontend *fe)
5669a0bf528SMauro Carvalho Chehab {
5679a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
5689a0bf528SMauro Carvalho Chehab 	u16 reg, reg1, reg2;
5699a0bf528SMauro Carvalho Chehab 
5709a0bf528SMauro Carvalho Chehab 	if (state->qam_state >= QAM_STATE_INTERLEAVE_SET) {
5719a0bf528SMauro Carvalho Chehab 		/* We've done the optimization already */
5729a0bf528SMauro Carvalho Chehab 		return;
5739a0bf528SMauro Carvalho Chehab 	}
5749a0bf528SMauro Carvalho Chehab 
5759a0bf528SMauro Carvalho Chehab 	reg = s5h1409_readreg(state, 0xf1);
5769a0bf528SMauro Carvalho Chehab 
5779a0bf528SMauro Carvalho Chehab 	/* Master lock */
5789a0bf528SMauro Carvalho Chehab 	if ((reg >> 15) & 0x1) {
5799a0bf528SMauro Carvalho Chehab 		if (state->qam_state == QAM_STATE_UNTUNED ||
5809a0bf528SMauro Carvalho Chehab 		    state->qam_state == QAM_STATE_TUNING_STARTED) {
5819a0bf528SMauro Carvalho Chehab 			dprintk("%s() setting QAM state to INTERLEAVE_SET\n",
5829a0bf528SMauro Carvalho Chehab 				__func__);
5839a0bf528SMauro Carvalho Chehab 			reg1 = s5h1409_readreg(state, 0xb2);
5849a0bf528SMauro Carvalho Chehab 			reg2 = s5h1409_readreg(state, 0xad);
5859a0bf528SMauro Carvalho Chehab 
5869a0bf528SMauro Carvalho Chehab 			s5h1409_writereg(state, 0x96, 0x0020);
5879a0bf528SMauro Carvalho Chehab 			s5h1409_writereg(state, 0xad,
5889a0bf528SMauro Carvalho Chehab 				(((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff)));
5899a0bf528SMauro Carvalho Chehab 			state->qam_state = QAM_STATE_INTERLEAVE_SET;
5909a0bf528SMauro Carvalho Chehab 		}
5919a0bf528SMauro Carvalho Chehab 	} else {
5929a0bf528SMauro Carvalho Chehab 		if (state->qam_state == QAM_STATE_UNTUNED) {
5939a0bf528SMauro Carvalho Chehab 			dprintk("%s() setting QAM state to TUNING_STARTED\n",
5949a0bf528SMauro Carvalho Chehab 				__func__);
5959a0bf528SMauro Carvalho Chehab 			s5h1409_writereg(state, 0x96, 0x08);
5969a0bf528SMauro Carvalho Chehab 			s5h1409_writereg(state, 0xab,
5979a0bf528SMauro Carvalho Chehab 				s5h1409_readreg(state, 0xab) | 0x1001);
5989a0bf528SMauro Carvalho Chehab 			state->qam_state = QAM_STATE_TUNING_STARTED;
5999a0bf528SMauro Carvalho Chehab 		}
6009a0bf528SMauro Carvalho Chehab 	}
6019a0bf528SMauro Carvalho Chehab }
6029a0bf528SMauro Carvalho Chehab 
6039a0bf528SMauro Carvalho Chehab static void s5h1409_set_qam_interleave_mode_legacy(struct dvb_frontend *fe)
6049a0bf528SMauro Carvalho Chehab {
6059a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
6069a0bf528SMauro Carvalho Chehab 	u16 reg, reg1, reg2;
6079a0bf528SMauro Carvalho Chehab 
6089a0bf528SMauro Carvalho Chehab 	reg = s5h1409_readreg(state, 0xf1);
6099a0bf528SMauro Carvalho Chehab 
6109a0bf528SMauro Carvalho Chehab 	/* Master lock */
6119a0bf528SMauro Carvalho Chehab 	if ((reg >> 15) & 0x1) {
6129a0bf528SMauro Carvalho Chehab 		if (state->qam_state != 2) {
6139a0bf528SMauro Carvalho Chehab 			state->qam_state = 2;
6149a0bf528SMauro Carvalho Chehab 			reg1 = s5h1409_readreg(state, 0xb2);
6159a0bf528SMauro Carvalho Chehab 			reg2 = s5h1409_readreg(state, 0xad);
6169a0bf528SMauro Carvalho Chehab 
6179a0bf528SMauro Carvalho Chehab 			s5h1409_writereg(state, 0x96, 0x20);
6189a0bf528SMauro Carvalho Chehab 			s5h1409_writereg(state, 0xad,
6199a0bf528SMauro Carvalho Chehab 				(((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff)));
6209a0bf528SMauro Carvalho Chehab 			s5h1409_writereg(state, 0xab,
6219a0bf528SMauro Carvalho Chehab 				s5h1409_readreg(state, 0xab) & 0xeffe);
6229a0bf528SMauro Carvalho Chehab 		}
6239a0bf528SMauro Carvalho Chehab 	} else {
6249a0bf528SMauro Carvalho Chehab 		if (state->qam_state != 1) {
6259a0bf528SMauro Carvalho Chehab 			state->qam_state = 1;
6269a0bf528SMauro Carvalho Chehab 			s5h1409_writereg(state, 0x96, 0x08);
6279a0bf528SMauro Carvalho Chehab 			s5h1409_writereg(state, 0xab,
6289a0bf528SMauro Carvalho Chehab 				s5h1409_readreg(state, 0xab) | 0x1001);
6299a0bf528SMauro Carvalho Chehab 		}
6309a0bf528SMauro Carvalho Chehab 	}
6319a0bf528SMauro Carvalho Chehab }
6329a0bf528SMauro Carvalho Chehab 
6339a0bf528SMauro Carvalho Chehab /* Talk to the demod, set the FEC, GUARD, QAM settings etc */
6349a0bf528SMauro Carvalho Chehab static int s5h1409_set_frontend(struct dvb_frontend *fe)
6359a0bf528SMauro Carvalho Chehab {
6369a0bf528SMauro Carvalho Chehab 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
6379a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
6389a0bf528SMauro Carvalho Chehab 
6399a0bf528SMauro Carvalho Chehab 	dprintk("%s(frequency=%d)\n", __func__, p->frequency);
6409a0bf528SMauro Carvalho Chehab 
6419a0bf528SMauro Carvalho Chehab 	s5h1409_softreset(fe);
6429a0bf528SMauro Carvalho Chehab 
6439a0bf528SMauro Carvalho Chehab 	state->current_frequency = p->frequency;
6449a0bf528SMauro Carvalho Chehab 
6459a0bf528SMauro Carvalho Chehab 	s5h1409_enable_modulation(fe, p->modulation);
6469a0bf528SMauro Carvalho Chehab 
6479a0bf528SMauro Carvalho Chehab 	if (fe->ops.tuner_ops.set_params) {
6489a0bf528SMauro Carvalho Chehab 		if (fe->ops.i2c_gate_ctrl)
6499a0bf528SMauro Carvalho Chehab 			fe->ops.i2c_gate_ctrl(fe, 1);
6509a0bf528SMauro Carvalho Chehab 		fe->ops.tuner_ops.set_params(fe);
6519a0bf528SMauro Carvalho Chehab 		if (fe->ops.i2c_gate_ctrl)
6529a0bf528SMauro Carvalho Chehab 			fe->ops.i2c_gate_ctrl(fe, 0);
6539a0bf528SMauro Carvalho Chehab 	}
6549a0bf528SMauro Carvalho Chehab 
6559a0bf528SMauro Carvalho Chehab 	/* Issue a reset to the demod so it knows to resync against the
6569a0bf528SMauro Carvalho Chehab 	   newly tuned frequency */
6579a0bf528SMauro Carvalho Chehab 	s5h1409_softreset(fe);
6589a0bf528SMauro Carvalho Chehab 
6599a0bf528SMauro Carvalho Chehab 	/* Optimize the demod for QAM */
6609a0bf528SMauro Carvalho Chehab 	if (state->current_modulation != VSB_8) {
6619a0bf528SMauro Carvalho Chehab 		/* This almost certainly applies to all boards, but for now
6629a0bf528SMauro Carvalho Chehab 		   only do it for the HVR-1600.  Once the other boards are
6639a0bf528SMauro Carvalho Chehab 		   tested, the "legacy" versions can just go away */
6649a0bf528SMauro Carvalho Chehab 		if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) {
6659a0bf528SMauro Carvalho Chehab 			s5h1409_set_qam_interleave_mode(fe);
6669a0bf528SMauro Carvalho Chehab 			s5h1409_set_qam_amhum_mode(fe);
6679a0bf528SMauro Carvalho Chehab 		} else {
6689a0bf528SMauro Carvalho Chehab 			s5h1409_set_qam_amhum_mode_legacy(fe);
6699a0bf528SMauro Carvalho Chehab 			s5h1409_set_qam_interleave_mode_legacy(fe);
6709a0bf528SMauro Carvalho Chehab 		}
6719a0bf528SMauro Carvalho Chehab 	}
6729a0bf528SMauro Carvalho Chehab 
6739a0bf528SMauro Carvalho Chehab 	return 0;
6749a0bf528SMauro Carvalho Chehab }
6759a0bf528SMauro Carvalho Chehab 
6769a0bf528SMauro Carvalho Chehab static int s5h1409_set_mpeg_timing(struct dvb_frontend *fe, int mode)
6779a0bf528SMauro Carvalho Chehab {
6789a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
6799a0bf528SMauro Carvalho Chehab 	u16 val;
6809a0bf528SMauro Carvalho Chehab 
6819a0bf528SMauro Carvalho Chehab 	dprintk("%s(%d)\n", __func__, mode);
6829a0bf528SMauro Carvalho Chehab 
6839a0bf528SMauro Carvalho Chehab 	val = s5h1409_readreg(state, 0xac) & 0xcfff;
6849a0bf528SMauro Carvalho Chehab 	switch (mode) {
6859a0bf528SMauro Carvalho Chehab 	case S5H1409_MPEGTIMING_CONTINOUS_INVERTING_CLOCK:
6869a0bf528SMauro Carvalho Chehab 		val |= 0x0000;
6879a0bf528SMauro Carvalho Chehab 		break;
6889a0bf528SMauro Carvalho Chehab 	case S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK:
6899a0bf528SMauro Carvalho Chehab 		dprintk("%s(%d) Mode1 or Defaulting\n", __func__, mode);
6909a0bf528SMauro Carvalho Chehab 		val |= 0x1000;
6919a0bf528SMauro Carvalho Chehab 		break;
6929a0bf528SMauro Carvalho Chehab 	case S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK:
6939a0bf528SMauro Carvalho Chehab 		val |= 0x2000;
6949a0bf528SMauro Carvalho Chehab 		break;
6959a0bf528SMauro Carvalho Chehab 	case S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK:
6969a0bf528SMauro Carvalho Chehab 		val |= 0x3000;
6979a0bf528SMauro Carvalho Chehab 		break;
6989a0bf528SMauro Carvalho Chehab 	default:
6999a0bf528SMauro Carvalho Chehab 		return -EINVAL;
7009a0bf528SMauro Carvalho Chehab 	}
7019a0bf528SMauro Carvalho Chehab 
7029a0bf528SMauro Carvalho Chehab 	/* Configure MPEG Signal Timing charactistics */
7039a0bf528SMauro Carvalho Chehab 	return s5h1409_writereg(state, 0xac, val);
7049a0bf528SMauro Carvalho Chehab }
7059a0bf528SMauro Carvalho Chehab 
7069a0bf528SMauro Carvalho Chehab /* Reset the demod hardware and reset all of the configuration registers
7079a0bf528SMauro Carvalho Chehab    to a default state. */
7089a0bf528SMauro Carvalho Chehab static int s5h1409_init(struct dvb_frontend *fe)
7099a0bf528SMauro Carvalho Chehab {
7109a0bf528SMauro Carvalho Chehab 	int i;
7119a0bf528SMauro Carvalho Chehab 
7129a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
7139a0bf528SMauro Carvalho Chehab 	dprintk("%s()\n", __func__);
7149a0bf528SMauro Carvalho Chehab 
7159a0bf528SMauro Carvalho Chehab 	s5h1409_sleep(fe, 0);
7169a0bf528SMauro Carvalho Chehab 	s5h1409_register_reset(fe);
7179a0bf528SMauro Carvalho Chehab 
7189a0bf528SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(init_tab); i++)
7199a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, init_tab[i].reg, init_tab[i].data);
7209a0bf528SMauro Carvalho Chehab 
7219a0bf528SMauro Carvalho Chehab 	/* The datasheet says that after initialisation, VSB is default */
7229a0bf528SMauro Carvalho Chehab 	state->current_modulation = VSB_8;
7239a0bf528SMauro Carvalho Chehab 
7249a0bf528SMauro Carvalho Chehab 	/* Optimize for the HVR-1600 if appropriate.  Note that some of these
7259a0bf528SMauro Carvalho Chehab 	   may get folded into the generic case after testing with other
7269a0bf528SMauro Carvalho Chehab 	   devices */
7279a0bf528SMauro Carvalho Chehab 	if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) {
7289a0bf528SMauro Carvalho Chehab 		/* VSB AGC REF */
7299a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0x09, 0x0050);
7309a0bf528SMauro Carvalho Chehab 
7319a0bf528SMauro Carvalho Chehab 		/* Unknown but Windows driver does it... */
7329a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0x21, 0x0001);
7339a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0x50, 0x030e);
7349a0bf528SMauro Carvalho Chehab 
7359a0bf528SMauro Carvalho Chehab 		/* QAM AGC REF */
7369a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0x82, 0x0800);
7379a0bf528SMauro Carvalho Chehab 	}
7389a0bf528SMauro Carvalho Chehab 
7399a0bf528SMauro Carvalho Chehab 	if (state->config->output_mode == S5H1409_SERIAL_OUTPUT)
7409a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0xab,
7419a0bf528SMauro Carvalho Chehab 			s5h1409_readreg(state, 0xab) | 0x100); /* Serial */
7429a0bf528SMauro Carvalho Chehab 	else
7439a0bf528SMauro Carvalho Chehab 		s5h1409_writereg(state, 0xab,
7449a0bf528SMauro Carvalho Chehab 			s5h1409_readreg(state, 0xab) & 0xfeff); /* Parallel */
7459a0bf528SMauro Carvalho Chehab 
7469a0bf528SMauro Carvalho Chehab 	s5h1409_set_spectralinversion(fe, state->config->inversion);
7479a0bf528SMauro Carvalho Chehab 	s5h1409_set_if_freq(fe, state->if_freq);
7489a0bf528SMauro Carvalho Chehab 	s5h1409_set_gpio(fe, state->config->gpio);
7499a0bf528SMauro Carvalho Chehab 	s5h1409_set_mpeg_timing(fe, state->config->mpeg_timing);
7509a0bf528SMauro Carvalho Chehab 	s5h1409_softreset(fe);
7519a0bf528SMauro Carvalho Chehab 
7529a0bf528SMauro Carvalho Chehab 	/* Note: Leaving the I2C gate closed. */
7539a0bf528SMauro Carvalho Chehab 	s5h1409_i2c_gate_ctrl(fe, 0);
7549a0bf528SMauro Carvalho Chehab 
7559a0bf528SMauro Carvalho Chehab 	return 0;
7569a0bf528SMauro Carvalho Chehab }
7579a0bf528SMauro Carvalho Chehab 
7580df289a2SMauro Carvalho Chehab static int s5h1409_read_status(struct dvb_frontend *fe, enum fe_status *status)
7599a0bf528SMauro Carvalho Chehab {
7609a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
7619a0bf528SMauro Carvalho Chehab 	u16 reg;
7629a0bf528SMauro Carvalho Chehab 	u32 tuner_status = 0;
7639a0bf528SMauro Carvalho Chehab 
7649a0bf528SMauro Carvalho Chehab 	*status = 0;
7659a0bf528SMauro Carvalho Chehab 
7669a0bf528SMauro Carvalho Chehab 	/* Optimize the demod for QAM */
7679a0bf528SMauro Carvalho Chehab 	if (state->current_modulation != VSB_8) {
7689a0bf528SMauro Carvalho Chehab 		/* This almost certainly applies to all boards, but for now
7699a0bf528SMauro Carvalho Chehab 		   only do it for the HVR-1600.  Once the other boards are
7709a0bf528SMauro Carvalho Chehab 		   tested, the "legacy" versions can just go away */
7719a0bf528SMauro Carvalho Chehab 		if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) {
7729a0bf528SMauro Carvalho Chehab 			s5h1409_set_qam_interleave_mode(fe);
7739a0bf528SMauro Carvalho Chehab 			s5h1409_set_qam_amhum_mode(fe);
7749a0bf528SMauro Carvalho Chehab 		}
7759a0bf528SMauro Carvalho Chehab 	}
7769a0bf528SMauro Carvalho Chehab 
7779a0bf528SMauro Carvalho Chehab 	/* Get the demodulator status */
7789a0bf528SMauro Carvalho Chehab 	reg = s5h1409_readreg(state, 0xf1);
7799a0bf528SMauro Carvalho Chehab 	if (reg & 0x1000)
7809a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_VITERBI;
7819a0bf528SMauro Carvalho Chehab 	if (reg & 0x8000)
7829a0bf528SMauro Carvalho Chehab 		*status |= FE_HAS_LOCK | FE_HAS_SYNC;
7839a0bf528SMauro Carvalho Chehab 
7849a0bf528SMauro Carvalho Chehab 	switch (state->config->status_mode) {
7859a0bf528SMauro Carvalho Chehab 	case S5H1409_DEMODLOCKING:
7869a0bf528SMauro Carvalho Chehab 		if (*status & FE_HAS_VITERBI)
7879a0bf528SMauro Carvalho Chehab 			*status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
7889a0bf528SMauro Carvalho Chehab 		break;
7899a0bf528SMauro Carvalho Chehab 	case S5H1409_TUNERLOCKING:
7909a0bf528SMauro Carvalho Chehab 		/* Get the tuner status */
7919a0bf528SMauro Carvalho Chehab 		if (fe->ops.tuner_ops.get_status) {
7929a0bf528SMauro Carvalho Chehab 			if (fe->ops.i2c_gate_ctrl)
7939a0bf528SMauro Carvalho Chehab 				fe->ops.i2c_gate_ctrl(fe, 1);
7949a0bf528SMauro Carvalho Chehab 
7959a0bf528SMauro Carvalho Chehab 			fe->ops.tuner_ops.get_status(fe, &tuner_status);
7969a0bf528SMauro Carvalho Chehab 
7979a0bf528SMauro Carvalho Chehab 			if (fe->ops.i2c_gate_ctrl)
7989a0bf528SMauro Carvalho Chehab 				fe->ops.i2c_gate_ctrl(fe, 0);
7999a0bf528SMauro Carvalho Chehab 		}
8009a0bf528SMauro Carvalho Chehab 		if (tuner_status)
8019a0bf528SMauro Carvalho Chehab 			*status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
8029a0bf528SMauro Carvalho Chehab 		break;
8039a0bf528SMauro Carvalho Chehab 	}
8049a0bf528SMauro Carvalho Chehab 
8059a0bf528SMauro Carvalho Chehab 	dprintk("%s() status 0x%08x\n", __func__, *status);
8069a0bf528SMauro Carvalho Chehab 
8079a0bf528SMauro Carvalho Chehab 	return 0;
8089a0bf528SMauro Carvalho Chehab }
8099a0bf528SMauro Carvalho Chehab 
8109a0bf528SMauro Carvalho Chehab static int s5h1409_qam256_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v)
8119a0bf528SMauro Carvalho Chehab {
8129a0bf528SMauro Carvalho Chehab 	int i, ret = -EINVAL;
8139a0bf528SMauro Carvalho Chehab 	dprintk("%s()\n", __func__);
8149a0bf528SMauro Carvalho Chehab 
8159a0bf528SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(qam256_snr_tab); i++) {
8169a0bf528SMauro Carvalho Chehab 		if (v < qam256_snr_tab[i].val) {
8179a0bf528SMauro Carvalho Chehab 			*snr = qam256_snr_tab[i].data;
8189a0bf528SMauro Carvalho Chehab 			ret = 0;
8199a0bf528SMauro Carvalho Chehab 			break;
8209a0bf528SMauro Carvalho Chehab 		}
8219a0bf528SMauro Carvalho Chehab 	}
8229a0bf528SMauro Carvalho Chehab 	return ret;
8239a0bf528SMauro Carvalho Chehab }
8249a0bf528SMauro Carvalho Chehab 
8259a0bf528SMauro Carvalho Chehab static int s5h1409_qam64_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v)
8269a0bf528SMauro Carvalho Chehab {
8279a0bf528SMauro Carvalho Chehab 	int i, ret = -EINVAL;
8289a0bf528SMauro Carvalho Chehab 	dprintk("%s()\n", __func__);
8299a0bf528SMauro Carvalho Chehab 
8309a0bf528SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(qam64_snr_tab); i++) {
8319a0bf528SMauro Carvalho Chehab 		if (v < qam64_snr_tab[i].val) {
8329a0bf528SMauro Carvalho Chehab 			*snr = qam64_snr_tab[i].data;
8339a0bf528SMauro Carvalho Chehab 			ret = 0;
8349a0bf528SMauro Carvalho Chehab 			break;
8359a0bf528SMauro Carvalho Chehab 		}
8369a0bf528SMauro Carvalho Chehab 	}
8379a0bf528SMauro Carvalho Chehab 	return ret;
8389a0bf528SMauro Carvalho Chehab }
8399a0bf528SMauro Carvalho Chehab 
8409a0bf528SMauro Carvalho Chehab static int s5h1409_vsb_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v)
8419a0bf528SMauro Carvalho Chehab {
8429a0bf528SMauro Carvalho Chehab 	int i, ret = -EINVAL;
8439a0bf528SMauro Carvalho Chehab 	dprintk("%s()\n", __func__);
8449a0bf528SMauro Carvalho Chehab 
8459a0bf528SMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(vsb_snr_tab); i++) {
8469a0bf528SMauro Carvalho Chehab 		if (v > vsb_snr_tab[i].val) {
8479a0bf528SMauro Carvalho Chehab 			*snr = vsb_snr_tab[i].data;
8489a0bf528SMauro Carvalho Chehab 			ret = 0;
8499a0bf528SMauro Carvalho Chehab 			break;
8509a0bf528SMauro Carvalho Chehab 		}
8519a0bf528SMauro Carvalho Chehab 	}
8529a0bf528SMauro Carvalho Chehab 	dprintk("%s() snr=%d\n", __func__, *snr);
8539a0bf528SMauro Carvalho Chehab 	return ret;
8549a0bf528SMauro Carvalho Chehab }
8559a0bf528SMauro Carvalho Chehab 
8569a0bf528SMauro Carvalho Chehab static int s5h1409_read_snr(struct dvb_frontend *fe, u16 *snr)
8579a0bf528SMauro Carvalho Chehab {
8589a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
8599a0bf528SMauro Carvalho Chehab 	u16 reg;
8609a0bf528SMauro Carvalho Chehab 	dprintk("%s()\n", __func__);
8619a0bf528SMauro Carvalho Chehab 
8629a0bf528SMauro Carvalho Chehab 	switch (state->current_modulation) {
8639a0bf528SMauro Carvalho Chehab 	case QAM_64:
8649a0bf528SMauro Carvalho Chehab 		reg = s5h1409_readreg(state, 0xf0) & 0xff;
8659a0bf528SMauro Carvalho Chehab 		return s5h1409_qam64_lookup_snr(fe, snr, reg);
8669a0bf528SMauro Carvalho Chehab 	case QAM_256:
8679a0bf528SMauro Carvalho Chehab 		reg = s5h1409_readreg(state, 0xf0) & 0xff;
8689a0bf528SMauro Carvalho Chehab 		return s5h1409_qam256_lookup_snr(fe, snr, reg);
8699a0bf528SMauro Carvalho Chehab 	case VSB_8:
8709a0bf528SMauro Carvalho Chehab 		reg = s5h1409_readreg(state, 0xf1) & 0x3ff;
8719a0bf528SMauro Carvalho Chehab 		return s5h1409_vsb_lookup_snr(fe, snr, reg);
8729a0bf528SMauro Carvalho Chehab 	default:
8739a0bf528SMauro Carvalho Chehab 		break;
8749a0bf528SMauro Carvalho Chehab 	}
8759a0bf528SMauro Carvalho Chehab 
8769a0bf528SMauro Carvalho Chehab 	return -EINVAL;
8779a0bf528SMauro Carvalho Chehab }
8789a0bf528SMauro Carvalho Chehab 
8799a0bf528SMauro Carvalho Chehab static int s5h1409_read_signal_strength(struct dvb_frontend *fe,
8809a0bf528SMauro Carvalho Chehab 					u16 *signal_strength)
8819a0bf528SMauro Carvalho Chehab {
8829a0bf528SMauro Carvalho Chehab 	/* borrowed from lgdt330x.c
8839a0bf528SMauro Carvalho Chehab 	 *
8849a0bf528SMauro Carvalho Chehab 	 * Calculate strength from SNR up to 35dB
8859a0bf528SMauro Carvalho Chehab 	 * Even though the SNR can go higher than 35dB,
8869a0bf528SMauro Carvalho Chehab 	 * there is some comfort factor in having a range of
8879a0bf528SMauro Carvalho Chehab 	 * strong signals that can show at 100%
8889a0bf528SMauro Carvalho Chehab 	 */
8899a0bf528SMauro Carvalho Chehab 	u16 snr;
8909a0bf528SMauro Carvalho Chehab 	u32 tmp;
8919a0bf528SMauro Carvalho Chehab 	int ret = s5h1409_read_snr(fe, &snr);
8929a0bf528SMauro Carvalho Chehab 
8939a0bf528SMauro Carvalho Chehab 	*signal_strength = 0;
8949a0bf528SMauro Carvalho Chehab 
8959a0bf528SMauro Carvalho Chehab 	if (0 == ret) {
8969a0bf528SMauro Carvalho Chehab 		/* The following calculation method was chosen
8979a0bf528SMauro Carvalho Chehab 		 * purely for the sake of code re-use from the
8989a0bf528SMauro Carvalho Chehab 		 * other demod drivers that use this method */
8999a0bf528SMauro Carvalho Chehab 
9009a0bf528SMauro Carvalho Chehab 		/* Convert from SNR in dB * 10 to 8.24 fixed-point */
9019a0bf528SMauro Carvalho Chehab 		tmp = (snr * ((1 << 24) / 10));
9029a0bf528SMauro Carvalho Chehab 
9039a0bf528SMauro Carvalho Chehab 		/* Convert from 8.24 fixed-point to
9049a0bf528SMauro Carvalho Chehab 		 * scale the range 0 - 35*2^24 into 0 - 65535*/
9059a0bf528SMauro Carvalho Chehab 		if (tmp >= 8960 * 0x10000)
9069a0bf528SMauro Carvalho Chehab 			*signal_strength = 0xffff;
9079a0bf528SMauro Carvalho Chehab 		else
9089a0bf528SMauro Carvalho Chehab 			*signal_strength = tmp / 8960;
9099a0bf528SMauro Carvalho Chehab 	}
9109a0bf528SMauro Carvalho Chehab 
9119a0bf528SMauro Carvalho Chehab 	return ret;
9129a0bf528SMauro Carvalho Chehab }
9139a0bf528SMauro Carvalho Chehab 
9149a0bf528SMauro Carvalho Chehab static int s5h1409_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
9159a0bf528SMauro Carvalho Chehab {
9169a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
9179a0bf528SMauro Carvalho Chehab 
9189a0bf528SMauro Carvalho Chehab 	*ucblocks = s5h1409_readreg(state, 0xb5);
9199a0bf528SMauro Carvalho Chehab 
9209a0bf528SMauro Carvalho Chehab 	return 0;
9219a0bf528SMauro Carvalho Chehab }
9229a0bf528SMauro Carvalho Chehab 
9239a0bf528SMauro Carvalho Chehab static int s5h1409_read_ber(struct dvb_frontend *fe, u32 *ber)
9249a0bf528SMauro Carvalho Chehab {
9259a0bf528SMauro Carvalho Chehab 	return s5h1409_read_ucblocks(fe, ber);
9269a0bf528SMauro Carvalho Chehab }
9279a0bf528SMauro Carvalho Chehab 
9287e3e68bcSMauro Carvalho Chehab static int s5h1409_get_frontend(struct dvb_frontend *fe,
9297e3e68bcSMauro Carvalho Chehab 				struct dtv_frontend_properties *p)
9309a0bf528SMauro Carvalho Chehab {
9319a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
9329a0bf528SMauro Carvalho Chehab 
9339a0bf528SMauro Carvalho Chehab 	p->frequency = state->current_frequency;
9349a0bf528SMauro Carvalho Chehab 	p->modulation = state->current_modulation;
9359a0bf528SMauro Carvalho Chehab 
9369a0bf528SMauro Carvalho Chehab 	return 0;
9379a0bf528SMauro Carvalho Chehab }
9389a0bf528SMauro Carvalho Chehab 
9399a0bf528SMauro Carvalho Chehab static int s5h1409_get_tune_settings(struct dvb_frontend *fe,
9409a0bf528SMauro Carvalho Chehab 				     struct dvb_frontend_tune_settings *tune)
9419a0bf528SMauro Carvalho Chehab {
9429a0bf528SMauro Carvalho Chehab 	tune->min_delay_ms = 1000;
9439a0bf528SMauro Carvalho Chehab 	return 0;
9449a0bf528SMauro Carvalho Chehab }
9459a0bf528SMauro Carvalho Chehab 
9469a0bf528SMauro Carvalho Chehab static void s5h1409_release(struct dvb_frontend *fe)
9479a0bf528SMauro Carvalho Chehab {
9489a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = fe->demodulator_priv;
9499a0bf528SMauro Carvalho Chehab 	kfree(state);
9509a0bf528SMauro Carvalho Chehab }
9519a0bf528SMauro Carvalho Chehab 
952bd336e63SMax Kellermann static const struct dvb_frontend_ops s5h1409_ops;
9539a0bf528SMauro Carvalho Chehab 
9549a0bf528SMauro Carvalho Chehab struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config,
9559a0bf528SMauro Carvalho Chehab 				    struct i2c_adapter *i2c)
9569a0bf528SMauro Carvalho Chehab {
9579a0bf528SMauro Carvalho Chehab 	struct s5h1409_state *state = NULL;
9589a0bf528SMauro Carvalho Chehab 	u16 reg;
9599a0bf528SMauro Carvalho Chehab 
9609a0bf528SMauro Carvalho Chehab 	/* allocate memory for the internal state */
9619a0bf528SMauro Carvalho Chehab 	state = kzalloc(sizeof(struct s5h1409_state), GFP_KERNEL);
9629a0bf528SMauro Carvalho Chehab 	if (state == NULL)
9639a0bf528SMauro Carvalho Chehab 		goto error;
9649a0bf528SMauro Carvalho Chehab 
9659a0bf528SMauro Carvalho Chehab 	/* setup the state */
9669a0bf528SMauro Carvalho Chehab 	state->config = config;
9679a0bf528SMauro Carvalho Chehab 	state->i2c = i2c;
9689a0bf528SMauro Carvalho Chehab 	state->current_modulation = 0;
9699a0bf528SMauro Carvalho Chehab 	state->if_freq = S5H1409_VSB_IF_FREQ;
9709a0bf528SMauro Carvalho Chehab 
9719a0bf528SMauro Carvalho Chehab 	/* check if the demod exists */
9729a0bf528SMauro Carvalho Chehab 	reg = s5h1409_readreg(state, 0x04);
9739a0bf528SMauro Carvalho Chehab 	if ((reg != 0x0066) && (reg != 0x007f))
9749a0bf528SMauro Carvalho Chehab 		goto error;
9759a0bf528SMauro Carvalho Chehab 
9769a0bf528SMauro Carvalho Chehab 	/* create dvb_frontend */
9779a0bf528SMauro Carvalho Chehab 	memcpy(&state->frontend.ops, &s5h1409_ops,
9789a0bf528SMauro Carvalho Chehab 	       sizeof(struct dvb_frontend_ops));
9799a0bf528SMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
9809a0bf528SMauro Carvalho Chehab 
9819a0bf528SMauro Carvalho Chehab 	if (s5h1409_init(&state->frontend) != 0) {
9829a0bf528SMauro Carvalho Chehab 		printk(KERN_ERR "%s: Failed to initialize correctly\n",
9839a0bf528SMauro Carvalho Chehab 			__func__);
9849a0bf528SMauro Carvalho Chehab 		goto error;
9859a0bf528SMauro Carvalho Chehab 	}
9869a0bf528SMauro Carvalho Chehab 
9879a0bf528SMauro Carvalho Chehab 	/* Note: Leaving the I2C gate open here. */
9889a0bf528SMauro Carvalho Chehab 	s5h1409_i2c_gate_ctrl(&state->frontend, 1);
9899a0bf528SMauro Carvalho Chehab 
9909a0bf528SMauro Carvalho Chehab 	return &state->frontend;
9919a0bf528SMauro Carvalho Chehab 
9929a0bf528SMauro Carvalho Chehab error:
9939a0bf528SMauro Carvalho Chehab 	kfree(state);
9949a0bf528SMauro Carvalho Chehab 	return NULL;
9959a0bf528SMauro Carvalho Chehab }
9969a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(s5h1409_attach);
9979a0bf528SMauro Carvalho Chehab 
998bd336e63SMax Kellermann static const struct dvb_frontend_ops s5h1409_ops = {
9999a0bf528SMauro Carvalho Chehab 	.delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
10009a0bf528SMauro Carvalho Chehab 	.info = {
10019a0bf528SMauro Carvalho Chehab 		.name			= "Samsung S5H1409 QAM/8VSB Frontend",
10029a0bf528SMauro Carvalho Chehab 		.frequency_min		= 54000000,
10039a0bf528SMauro Carvalho Chehab 		.frequency_max		= 858000000,
10049a0bf528SMauro Carvalho Chehab 		.frequency_stepsize	= 62500,
10059a0bf528SMauro Carvalho Chehab 		.caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
10069a0bf528SMauro Carvalho Chehab 	},
10079a0bf528SMauro Carvalho Chehab 
10089a0bf528SMauro Carvalho Chehab 	.init                 = s5h1409_init,
10099a0bf528SMauro Carvalho Chehab 	.i2c_gate_ctrl        = s5h1409_i2c_gate_ctrl,
10109a0bf528SMauro Carvalho Chehab 	.set_frontend         = s5h1409_set_frontend,
10119a0bf528SMauro Carvalho Chehab 	.get_frontend         = s5h1409_get_frontend,
10129a0bf528SMauro Carvalho Chehab 	.get_tune_settings    = s5h1409_get_tune_settings,
10139a0bf528SMauro Carvalho Chehab 	.read_status          = s5h1409_read_status,
10149a0bf528SMauro Carvalho Chehab 	.read_ber             = s5h1409_read_ber,
10159a0bf528SMauro Carvalho Chehab 	.read_signal_strength = s5h1409_read_signal_strength,
10169a0bf528SMauro Carvalho Chehab 	.read_snr             = s5h1409_read_snr,
10179a0bf528SMauro Carvalho Chehab 	.read_ucblocks        = s5h1409_read_ucblocks,
10189a0bf528SMauro Carvalho Chehab 	.release              = s5h1409_release,
10199a0bf528SMauro Carvalho Chehab };
10209a0bf528SMauro Carvalho Chehab 
10219a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("Samsung S5H1409 QAM-B/ATSC Demodulator driver");
10229a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Steven Toth");
10239a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL");
1024