xref: /linux/drivers/media/dvb-frontends/s5h1409.c (revision 58e16d792a6a8c6b750f637a4649967fcac853dc)
1*74ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
289885558SSteven Toth /*
389885558SSteven Toth     Samsung S5H1409 VSB/QAM demodulator driver
489885558SSteven Toth 
56d897616SSteven Toth     Copyright (C) 2006 Steven Toth <stoth@linuxtv.org>
689885558SSteven Toth 
789885558SSteven Toth 
889885558SSteven Toth */
989885558SSteven Toth 
1089885558SSteven Toth #include <linux/kernel.h>
1189885558SSteven Toth #include <linux/init.h>
1289885558SSteven Toth #include <linux/module.h>
1389885558SSteven Toth #include <linux/string.h>
1489885558SSteven Toth #include <linux/slab.h>
1589885558SSteven Toth #include <linux/delay.h>
16fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h>
1789885558SSteven Toth #include "s5h1409.h"
1889885558SSteven Toth 
1989885558SSteven Toth struct s5h1409_state {
2089885558SSteven Toth 
2189885558SSteven Toth 	struct i2c_adapter *i2c;
2289885558SSteven Toth 
2389885558SSteven Toth 	/* configuration settings */
2489885558SSteven Toth 	const struct s5h1409_config *config;
2589885558SSteven Toth 
2689885558SSteven Toth 	struct dvb_frontend frontend;
2789885558SSteven Toth 
2889885558SSteven Toth 	/* previous uncorrected block counter */
290df289a2SMauro Carvalho Chehab 	enum fe_modulation current_modulation;
3089885558SSteven Toth 
3189885558SSteven Toth 	u32 current_frequency;
322b03238aSMichael Krufky 	int if_freq;
33dd7d5013SSteven Toth 
34dd7d5013SSteven Toth 	u32 is_qam_locked;
35f0cd44b4SDevin Heitmueller 
36f0cd44b4SDevin Heitmueller 	/* QAM tuning state goes through the following state transitions */
37f0cd44b4SDevin Heitmueller #define QAM_STATE_UNTUNED 0
38f0cd44b4SDevin Heitmueller #define QAM_STATE_TUNING_STARTED 1
39f0cd44b4SDevin Heitmueller #define QAM_STATE_INTERLEAVE_SET 2
40f0cd44b4SDevin Heitmueller #define QAM_STATE_QAM_OPTIMIZED_L1 3
41f0cd44b4SDevin Heitmueller #define QAM_STATE_QAM_OPTIMIZED_L2 4
42f0cd44b4SDevin Heitmueller #define QAM_STATE_QAM_OPTIMIZED_L3 5
43f0cd44b4SDevin Heitmueller 	u8  qam_state;
4489885558SSteven Toth };
4589885558SSteven Toth 
46ff699e6bSDouglas Schilling Landgraf static int debug;
47b7709c0dSSteven Toth module_param(debug, int, 0644);
48b7709c0dSSteven Toth MODULE_PARM_DESC(debug, "Enable verbose debug messages");
49b7709c0dSSteven Toth 
5089885558SSteven Toth #define dprintk	if (debug) printk
5189885558SSteven Toth 
5289885558SSteven Toth /* Register values to initialise the demod, this will set VSB by default */
5389885558SSteven Toth static struct init_tab {
5489885558SSteven Toth 	u8	reg;
5589885558SSteven Toth 	u16	data;
5689885558SSteven Toth } init_tab[] = {
5789885558SSteven Toth 	{ 0x00, 0x0071, },
5889885558SSteven Toth 	{ 0x01, 0x3213, },
5989885558SSteven Toth 	{ 0x09, 0x0025, },
6089885558SSteven Toth 	{ 0x1c, 0x001d, },
6189885558SSteven Toth 	{ 0x1f, 0x002d, },
6289885558SSteven Toth 	{ 0x20, 0x001d, },
6389885558SSteven Toth 	{ 0x22, 0x0022, },
6489885558SSteven Toth 	{ 0x23, 0x0020, },
6589885558SSteven Toth 	{ 0x29, 0x110f, },
6689885558SSteven Toth 	{ 0x2a, 0x10b4, },
6789885558SSteven Toth 	{ 0x2b, 0x10ae, },
6889885558SSteven Toth 	{ 0x2c, 0x0031, },
6989885558SSteven Toth 	{ 0x31, 0x010d, },
7089885558SSteven Toth 	{ 0x32, 0x0100, },
7189885558SSteven Toth 	{ 0x44, 0x0510, },
7289885558SSteven Toth 	{ 0x54, 0x0104, },
7389885558SSteven Toth 	{ 0x58, 0x2222, },
7489885558SSteven Toth 	{ 0x59, 0x1162, },
7589885558SSteven Toth 	{ 0x5a, 0x3211, },
7689885558SSteven Toth 	{ 0x5d, 0x0370, },
7789885558SSteven Toth 	{ 0x5e, 0x0296, },
7889885558SSteven Toth 	{ 0x61, 0x0010, },
7989885558SSteven Toth 	{ 0x63, 0x4a00, },
8089885558SSteven Toth 	{ 0x65, 0x0800, },
8189885558SSteven Toth 	{ 0x71, 0x0003, },
8289885558SSteven Toth 	{ 0x72, 0x0470, },
8389885558SSteven Toth 	{ 0x81, 0x0002, },
8489885558SSteven Toth 	{ 0x82, 0x0600, },
8589885558SSteven Toth 	{ 0x86, 0x0002, },
8689885558SSteven Toth 	{ 0x8a, 0x2c38, },
8789885558SSteven Toth 	{ 0x8b, 0x2a37, },
8889885558SSteven Toth 	{ 0x92, 0x302f, },
8989885558SSteven Toth 	{ 0x93, 0x3332, },
9089885558SSteven Toth 	{ 0x96, 0x000c, },
9189885558SSteven Toth 	{ 0x99, 0x0101, },
9289885558SSteven Toth 	{ 0x9c, 0x2e37, },
9389885558SSteven Toth 	{ 0x9d, 0x2c37, },
9489885558SSteven Toth 	{ 0x9e, 0x2c37, },
9589885558SSteven Toth 	{ 0xab, 0x0100, },
9689885558SSteven Toth 	{ 0xac, 0x1003, },
9789885558SSteven Toth 	{ 0xad, 0x103f, },
9889885558SSteven Toth 	{ 0xe2, 0x0100, },
99dfc1c08aSSteven Toth 	{ 0xe3, 0x1000, },
10089885558SSteven Toth 	{ 0x28, 0x1010, },
10189885558SSteven Toth 	{ 0xb1, 0x000e, },
10289885558SSteven Toth };
10389885558SSteven Toth 
10489885558SSteven Toth /* VSB SNR lookup table */
10589885558SSteven Toth static struct vsb_snr_tab {
10689885558SSteven Toth 	u16	val;
10789885558SSteven Toth 	u16	data;
10889885558SSteven Toth } vsb_snr_tab[] = {
1092300317fSSteven Toth 	{  924, 300, },
11089885558SSteven Toth 	{  923, 300, },
11189885558SSteven Toth 	{  918, 295, },
11289885558SSteven Toth 	{  915, 290, },
11389885558SSteven Toth 	{  911, 285, },
11489885558SSteven Toth 	{  906, 280, },
11589885558SSteven Toth 	{  901, 275, },
11689885558SSteven Toth 	{  896, 270, },
11789885558SSteven Toth 	{  891, 265, },
11889885558SSteven Toth 	{  885, 260, },
11989885558SSteven Toth 	{  879, 255, },
12089885558SSteven Toth 	{  873, 250, },
12189885558SSteven Toth 	{  864, 245, },
12289885558SSteven Toth 	{  858, 240, },
12389885558SSteven Toth 	{  850, 235, },
12489885558SSteven Toth 	{  841, 230, },
12589885558SSteven Toth 	{  832, 225, },
12689885558SSteven Toth 	{  823, 220, },
12789885558SSteven Toth 	{  812, 215, },
12889885558SSteven Toth 	{  802, 210, },
12989885558SSteven Toth 	{  788, 205, },
13089885558SSteven Toth 	{  778, 200, },
13189885558SSteven Toth 	{  767, 195, },
13289885558SSteven Toth 	{  753, 190, },
13389885558SSteven Toth 	{  740, 185, },
13489885558SSteven Toth 	{  725, 180, },
13589885558SSteven Toth 	{  707, 175, },
13689885558SSteven Toth 	{  689, 170, },
13789885558SSteven Toth 	{  671, 165, },
13889885558SSteven Toth 	{  656, 160, },
13989885558SSteven Toth 	{  637, 155, },
14089885558SSteven Toth 	{  616, 150, },
14189885558SSteven Toth 	{  542, 145, },
14289885558SSteven Toth 	{  519, 140, },
14389885558SSteven Toth 	{  507, 135, },
14489885558SSteven Toth 	{  497, 130, },
14589885558SSteven Toth 	{  492, 125, },
14689885558SSteven Toth 	{  474, 120, },
14789885558SSteven Toth 	{  300, 111, },
14889885558SSteven Toth 	{    0,   0, },
14989885558SSteven Toth };
15089885558SSteven Toth 
15189885558SSteven Toth /* QAM64 SNR lookup table */
15289885558SSteven Toth static struct qam64_snr_tab {
15389885558SSteven Toth 	u16	val;
15489885558SSteven Toth 	u16	data;
15589885558SSteven Toth } qam64_snr_tab[] = {
1562300317fSSteven Toth 	{    1,   0, },
15789885558SSteven Toth 	{   12, 300, },
15889885558SSteven Toth 	{   15, 290, },
15989885558SSteven Toth 	{   18, 280, },
16089885558SSteven Toth 	{   22, 270, },
16189885558SSteven Toth 	{   23, 268, },
16289885558SSteven Toth 	{   24, 266, },
16389885558SSteven Toth 	{   25, 264, },
16489885558SSteven Toth 	{   27, 262, },
16589885558SSteven Toth 	{   28, 260, },
16689885558SSteven Toth 	{   29, 258, },
16789885558SSteven Toth 	{   30, 256, },
16889885558SSteven Toth 	{   32, 254, },
16989885558SSteven Toth 	{   33, 252, },
17089885558SSteven Toth 	{   34, 250, },
17189885558SSteven Toth 	{   35, 249, },
17289885558SSteven Toth 	{   36, 248, },
17389885558SSteven Toth 	{   37, 247, },
17489885558SSteven Toth 	{   38, 246, },
17589885558SSteven Toth 	{   39, 245, },
17689885558SSteven Toth 	{   40, 244, },
17789885558SSteven Toth 	{   41, 243, },
17889885558SSteven Toth 	{   42, 241, },
17989885558SSteven Toth 	{   43, 240, },
18089885558SSteven Toth 	{   44, 239, },
18189885558SSteven Toth 	{   45, 238, },
18289885558SSteven Toth 	{   46, 237, },
18389885558SSteven Toth 	{   47, 236, },
18489885558SSteven Toth 	{   48, 235, },
18589885558SSteven Toth 	{   49, 234, },
18689885558SSteven Toth 	{   50, 233, },
18789885558SSteven Toth 	{   51, 232, },
18889885558SSteven Toth 	{   52, 231, },
18989885558SSteven Toth 	{   53, 230, },
19089885558SSteven Toth 	{   55, 229, },
19189885558SSteven Toth 	{   56, 228, },
19289885558SSteven Toth 	{   57, 227, },
19389885558SSteven Toth 	{   58, 226, },
19489885558SSteven Toth 	{   59, 225, },
19589885558SSteven Toth 	{   60, 224, },
19689885558SSteven Toth 	{   62, 223, },
19789885558SSteven Toth 	{   63, 222, },
19889885558SSteven Toth 	{   65, 221, },
19989885558SSteven Toth 	{   66, 220, },
20089885558SSteven Toth 	{   68, 219, },
20189885558SSteven Toth 	{   69, 218, },
20289885558SSteven Toth 	{   70, 217, },
20389885558SSteven Toth 	{   72, 216, },
20489885558SSteven Toth 	{   73, 215, },
20589885558SSteven Toth 	{   75, 214, },
20689885558SSteven Toth 	{   76, 213, },
20789885558SSteven Toth 	{   78, 212, },
20889885558SSteven Toth 	{   80, 211, },
20989885558SSteven Toth 	{   81, 210, },
21089885558SSteven Toth 	{   83, 209, },
21189885558SSteven Toth 	{   84, 208, },
21289885558SSteven Toth 	{   85, 207, },
21389885558SSteven Toth 	{   87, 206, },
21489885558SSteven Toth 	{   89, 205, },
21589885558SSteven Toth 	{   91, 204, },
21689885558SSteven Toth 	{   93, 203, },
21789885558SSteven Toth 	{   95, 202, },
21889885558SSteven Toth 	{   96, 201, },
21989885558SSteven Toth 	{  104, 200, },
2202300317fSSteven Toth 	{  255,   0, },
22189885558SSteven Toth };
22289885558SSteven Toth 
22389885558SSteven Toth /* QAM256 SNR lookup table */
22489885558SSteven Toth static struct qam256_snr_tab {
22589885558SSteven Toth 	u16	val;
22689885558SSteven Toth 	u16	data;
22789885558SSteven Toth } qam256_snr_tab[] = {
2282300317fSSteven Toth 	{    1,   0, },
22989885558SSteven Toth 	{   12, 400, },
23089885558SSteven Toth 	{   13, 390, },
23189885558SSteven Toth 	{   15, 380, },
23289885558SSteven Toth 	{   17, 360, },
23389885558SSteven Toth 	{   19, 350, },
23489885558SSteven Toth 	{   22, 348, },
23589885558SSteven Toth 	{   23, 346, },
23689885558SSteven Toth 	{   24, 344, },
23789885558SSteven Toth 	{   25, 342, },
23889885558SSteven Toth 	{   26, 340, },
23989885558SSteven Toth 	{   27, 336, },
24089885558SSteven Toth 	{   28, 334, },
24189885558SSteven Toth 	{   29, 332, },
24289885558SSteven Toth 	{   30, 330, },
24389885558SSteven Toth 	{   31, 328, },
24489885558SSteven Toth 	{   32, 326, },
24589885558SSteven Toth 	{   33, 325, },
24689885558SSteven Toth 	{   34, 322, },
24789885558SSteven Toth 	{   35, 320, },
24889885558SSteven Toth 	{   37, 318, },
24989885558SSteven Toth 	{   39, 316, },
25089885558SSteven Toth 	{   40, 314, },
25189885558SSteven Toth 	{   41, 312, },
25289885558SSteven Toth 	{   42, 310, },
25389885558SSteven Toth 	{   43, 308, },
25489885558SSteven Toth 	{   46, 306, },
25589885558SSteven Toth 	{   47, 304, },
25689885558SSteven Toth 	{   49, 302, },
25789885558SSteven Toth 	{   51, 300, },
25889885558SSteven Toth 	{   53, 298, },
25989885558SSteven Toth 	{   54, 297, },
26089885558SSteven Toth 	{   55, 296, },
26189885558SSteven Toth 	{   56, 295, },
26289885558SSteven Toth 	{   57, 294, },
26389885558SSteven Toth 	{   59, 293, },
26489885558SSteven Toth 	{   60, 292, },
26589885558SSteven Toth 	{   61, 291, },
26689885558SSteven Toth 	{   63, 290, },
26789885558SSteven Toth 	{   64, 289, },
26889885558SSteven Toth 	{   65, 288, },
26989885558SSteven Toth 	{   66, 287, },
27089885558SSteven Toth 	{   68, 286, },
27189885558SSteven Toth 	{   69, 285, },
27289885558SSteven Toth 	{   71, 284, },
27389885558SSteven Toth 	{   72, 283, },
27489885558SSteven Toth 	{   74, 282, },
27589885558SSteven Toth 	{   75, 281, },
27689885558SSteven Toth 	{   76, 280, },
27789885558SSteven Toth 	{   77, 279, },
27889885558SSteven Toth 	{   78, 278, },
27989885558SSteven Toth 	{   81, 277, },
28089885558SSteven Toth 	{   83, 276, },
28189885558SSteven Toth 	{   84, 275, },
28289885558SSteven Toth 	{   86, 274, },
28389885558SSteven Toth 	{   87, 273, },
28489885558SSteven Toth 	{   89, 272, },
28589885558SSteven Toth 	{   90, 271, },
28689885558SSteven Toth 	{   92, 270, },
28789885558SSteven Toth 	{   93, 269, },
28889885558SSteven Toth 	{   95, 268, },
28989885558SSteven Toth 	{   96, 267, },
29089885558SSteven Toth 	{   98, 266, },
29189885558SSteven Toth 	{  100, 265, },
29289885558SSteven Toth 	{  102, 264, },
29389885558SSteven Toth 	{  104, 263, },
29489885558SSteven Toth 	{  105, 262, },
29589885558SSteven Toth 	{  106, 261, },
29689885558SSteven Toth 	{  110, 260, },
2972300317fSSteven Toth 	{  255,   0, },
29889885558SSteven Toth };
29989885558SSteven Toth 
30089885558SSteven Toth /* 8 bit registers, 16 bit values */
30189885558SSteven Toth static int s5h1409_writereg(struct s5h1409_state *state, u8 reg, u16 data)
30289885558SSteven Toth {
30389885558SSteven Toth 	int ret;
30489885558SSteven Toth 	u8 buf[] = { reg, data >> 8,  data & 0xff };
30589885558SSteven Toth 
3063873dd04SMichael Krufky 	struct i2c_msg msg = { .addr = state->config->demod_address,
3073873dd04SMichael Krufky 			       .flags = 0, .buf = buf, .len = 3 };
30889885558SSteven Toth 
30989885558SSteven Toth 	ret = i2c_transfer(state->i2c, &msg, 1);
31089885558SSteven Toth 
31189885558SSteven Toth 	if (ret != 1)
3124bd69e7bSMauro Carvalho Chehab 		printk(KERN_ERR "%s: error (reg == 0x%02x, val == 0x%04x, ret == %i)\n",
3134bd69e7bSMauro Carvalho Chehab 		       __func__, reg, data, ret);
31489885558SSteven Toth 
31589885558SSteven Toth 	return (ret != 1) ? -1 : 0;
31689885558SSteven Toth }
31789885558SSteven Toth 
31889885558SSteven Toth static u16 s5h1409_readreg(struct s5h1409_state *state, u8 reg)
31989885558SSteven Toth {
32089885558SSteven Toth 	int ret;
32189885558SSteven Toth 	u8 b0[] = { reg };
32289885558SSteven Toth 	u8 b1[] = { 0, 0 };
32389885558SSteven Toth 
32489885558SSteven Toth 	struct i2c_msg msg[] = {
3253873dd04SMichael Krufky 		{ .addr = state->config->demod_address, .flags = 0,
3263873dd04SMichael Krufky 		  .buf = b0, .len = 1 },
3273873dd04SMichael Krufky 		{ .addr = state->config->demod_address, .flags = I2C_M_RD,
3283873dd04SMichael Krufky 		  .buf = b1, .len = 2 } };
32989885558SSteven Toth 
33089885558SSteven Toth 	ret = i2c_transfer(state->i2c, msg, 2);
33189885558SSteven Toth 
33289885558SSteven Toth 	if (ret != 2)
333271ddbf7SHarvey Harrison 		printk("%s: readreg error (ret == %i)\n", __func__, ret);
33489885558SSteven Toth 	return (b1[0] << 8) | b1[1];
33589885558SSteven Toth }
33689885558SSteven Toth 
33789885558SSteven Toth static int s5h1409_softreset(struct dvb_frontend *fe)
33889885558SSteven Toth {
33989885558SSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
34089885558SSteven Toth 
341271ddbf7SHarvey Harrison 	dprintk("%s()\n", __func__);
34289885558SSteven Toth 
34389885558SSteven Toth 	s5h1409_writereg(state, 0xf5, 0);
34489885558SSteven Toth 	s5h1409_writereg(state, 0xf5, 1);
345dd7d5013SSteven Toth 	state->is_qam_locked = 0;
346f0cd44b4SDevin Heitmueller 	state->qam_state = QAM_STATE_UNTUNED;
34789885558SSteven Toth 	return 0;
34889885558SSteven Toth }
34989885558SSteven Toth 
3502b03238aSMichael Krufky #define S5H1409_VSB_IF_FREQ 5380
351b7709c0dSSteven Toth #define S5H1409_QAM_IF_FREQ (state->config->qam_if)
3522b03238aSMichael Krufky 
35389885558SSteven Toth static int s5h1409_set_if_freq(struct dvb_frontend *fe, int KHz)
35489885558SSteven Toth {
35589885558SSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
35689885558SSteven Toth 
357271ddbf7SHarvey Harrison 	dprintk("%s(%d KHz)\n", __func__, KHz);
35889885558SSteven Toth 
3596b7daa88SMichael Krufky 	switch (KHz) {
3606b7daa88SMichael Krufky 	case 4000:
361dd7d5013SSteven Toth 		s5h1409_writereg(state, 0x87, 0x014b);
362dd7d5013SSteven Toth 		s5h1409_writereg(state, 0x88, 0x0cb5);
363dd7d5013SSteven Toth 		s5h1409_writereg(state, 0x89, 0x03e2);
3646b7daa88SMichael Krufky 		break;
3656b7daa88SMichael Krufky 	case 5380:
3666b7daa88SMichael Krufky 	case 44000:
3676b7daa88SMichael Krufky 	default:
3686b7daa88SMichael Krufky 		s5h1409_writereg(state, 0x87, 0x01be);
3696b7daa88SMichael Krufky 		s5h1409_writereg(state, 0x88, 0x0436);
3706b7daa88SMichael Krufky 		s5h1409_writereg(state, 0x89, 0x054d);
3716b7daa88SMichael Krufky 		break;
37289885558SSteven Toth 	}
3732b03238aSMichael Krufky 	state->if_freq = KHz;
3742b03238aSMichael Krufky 
3756b7daa88SMichael Krufky 	return 0;
37689885558SSteven Toth }
37789885558SSteven Toth 
37889885558SSteven Toth static int s5h1409_set_spectralinversion(struct dvb_frontend *fe, int inverted)
37989885558SSteven Toth {
38089885558SSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
38189885558SSteven Toth 
382271ddbf7SHarvey Harrison 	dprintk("%s(%d)\n", __func__, inverted);
38389885558SSteven Toth 
38489885558SSteven Toth 	if (inverted == 1)
38589885558SSteven Toth 		return s5h1409_writereg(state, 0x1b, 0x1101); /* Inverted */
38689885558SSteven Toth 	else
38789885558SSteven Toth 		return s5h1409_writereg(state, 0x1b, 0x0110); /* Normal */
38889885558SSteven Toth }
38989885558SSteven Toth 
3903873dd04SMichael Krufky static int s5h1409_enable_modulation(struct dvb_frontend *fe,
3910df289a2SMauro Carvalho Chehab 				     enum fe_modulation m)
39289885558SSteven Toth {
39389885558SSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
39489885558SSteven Toth 
395271ddbf7SHarvey Harrison 	dprintk("%s(0x%08x)\n", __func__, m);
39689885558SSteven Toth 
39789885558SSteven Toth 	switch (m) {
39889885558SSteven Toth 	case VSB_8:
399271ddbf7SHarvey Harrison 		dprintk("%s() VSB_8\n", __func__);
4002b03238aSMichael Krufky 		if (state->if_freq != S5H1409_VSB_IF_FREQ)
4012b03238aSMichael Krufky 			s5h1409_set_if_freq(fe, S5H1409_VSB_IF_FREQ);
40289885558SSteven Toth 		s5h1409_writereg(state, 0xf4, 0);
40389885558SSteven Toth 		break;
40489885558SSteven Toth 	case QAM_64:
40589885558SSteven Toth 	case QAM_256:
4064fc85c74SSteven Toth 	case QAM_AUTO:
407271ddbf7SHarvey Harrison 		dprintk("%s() QAM_AUTO (64/256)\n", __func__);
4082b03238aSMichael Krufky 		if (state->if_freq != S5H1409_QAM_IF_FREQ)
4092b03238aSMichael Krufky 			s5h1409_set_if_freq(fe, S5H1409_QAM_IF_FREQ);
41089885558SSteven Toth 		s5h1409_writereg(state, 0xf4, 1);
411dd7d5013SSteven Toth 		s5h1409_writereg(state, 0x85, 0x110);
41289885558SSteven Toth 		break;
41389885558SSteven Toth 	default:
414271ddbf7SHarvey Harrison 		dprintk("%s() Invalid modulation\n", __func__);
41589885558SSteven Toth 		return -EINVAL;
41689885558SSteven Toth 	}
41789885558SSteven Toth 
41889885558SSteven Toth 	state->current_modulation = m;
41989885558SSteven Toth 	s5h1409_softreset(fe);
42089885558SSteven Toth 
42189885558SSteven Toth 	return 0;
42289885558SSteven Toth }
42389885558SSteven Toth 
42489885558SSteven Toth static int s5h1409_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
42589885558SSteven Toth {
42689885558SSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
42789885558SSteven Toth 
428271ddbf7SHarvey Harrison 	dprintk("%s(%d)\n", __func__, enable);
42989885558SSteven Toth 
43089885558SSteven Toth 	if (enable)
43189885558SSteven Toth 		return s5h1409_writereg(state, 0xf3, 1);
43289885558SSteven Toth 	else
43389885558SSteven Toth 		return s5h1409_writereg(state, 0xf3, 0);
43489885558SSteven Toth }
43589885558SSteven Toth 
43689885558SSteven Toth static int s5h1409_set_gpio(struct dvb_frontend *fe, int enable)
43789885558SSteven Toth {
43889885558SSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
43989885558SSteven Toth 
440271ddbf7SHarvey Harrison 	dprintk("%s(%d)\n", __func__, enable);
44189885558SSteven Toth 
44289885558SSteven Toth 	if (enable)
443dfc1c08aSSteven Toth 		return s5h1409_writereg(state, 0xe3,
444dfc1c08aSSteven Toth 			s5h1409_readreg(state, 0xe3) | 0x1100);
44589885558SSteven Toth 	else
446dfc1c08aSSteven Toth 		return s5h1409_writereg(state, 0xe3,
4478e08af3cSMichael Krufky 			s5h1409_readreg(state, 0xe3) & 0xfeff);
44889885558SSteven Toth }
44989885558SSteven Toth 
45089885558SSteven Toth static int s5h1409_sleep(struct dvb_frontend *fe, int enable)
45189885558SSteven Toth {
45289885558SSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
45389885558SSteven Toth 
454271ddbf7SHarvey Harrison 	dprintk("%s(%d)\n", __func__, enable);
45589885558SSteven Toth 
45689885558SSteven Toth 	return s5h1409_writereg(state, 0xf2, enable);
45789885558SSteven Toth }
45889885558SSteven Toth 
45989885558SSteven Toth static int s5h1409_register_reset(struct dvb_frontend *fe)
46089885558SSteven Toth {
46189885558SSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
46289885558SSteven Toth 
463271ddbf7SHarvey Harrison 	dprintk("%s()\n", __func__);
46489885558SSteven Toth 
46589885558SSteven Toth 	return s5h1409_writereg(state, 0xfa, 0);
46689885558SSteven Toth }
46789885558SSteven Toth 
468dd7d5013SSteven Toth static void s5h1409_set_qam_amhum_mode(struct dvb_frontend *fe)
469dd7d5013SSteven Toth {
470dd7d5013SSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
471dd7d5013SSteven Toth 	u16 reg;
472dd7d5013SSteven Toth 
473f0cd44b4SDevin Heitmueller 	if (state->qam_state < QAM_STATE_INTERLEAVE_SET) {
474f0cd44b4SDevin Heitmueller 		/* We should not perform amhum optimization until
475f0cd44b4SDevin Heitmueller 		   the interleave mode has been configured */
476f0cd44b4SDevin Heitmueller 		return;
477f0cd44b4SDevin Heitmueller 	}
478f0cd44b4SDevin Heitmueller 
479f0cd44b4SDevin Heitmueller 	if (state->qam_state == QAM_STATE_QAM_OPTIMIZED_L3) {
480f0cd44b4SDevin Heitmueller 		/* We've already reached the maximum optimization level, so
481868c9a17SMauro Carvalho Chehab 		   don't bother banging on the status registers */
482f0cd44b4SDevin Heitmueller 		return;
483f0cd44b4SDevin Heitmueller 	}
484f0cd44b4SDevin Heitmueller 
485f0cd44b4SDevin Heitmueller 	/* QAM EQ lock check */
486f0cd44b4SDevin Heitmueller 	reg = s5h1409_readreg(state, 0xf0);
487f0cd44b4SDevin Heitmueller 
488f0cd44b4SDevin Heitmueller 	if ((reg >> 13) & 0x1) {
489f0cd44b4SDevin Heitmueller 		reg &= 0xff;
490f0cd44b4SDevin Heitmueller 
491f0cd44b4SDevin Heitmueller 		s5h1409_writereg(state, 0x96, 0x000c);
492f0cd44b4SDevin Heitmueller 		if (reg < 0x68) {
493f0cd44b4SDevin Heitmueller 			if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L3) {
494f0cd44b4SDevin Heitmueller 				dprintk("%s() setting QAM state to OPT_L3\n",
495f0cd44b4SDevin Heitmueller 					__func__);
496f0cd44b4SDevin Heitmueller 				s5h1409_writereg(state, 0x93, 0x3130);
497f0cd44b4SDevin Heitmueller 				s5h1409_writereg(state, 0x9e, 0x2836);
498f0cd44b4SDevin Heitmueller 				state->qam_state = QAM_STATE_QAM_OPTIMIZED_L3;
499f0cd44b4SDevin Heitmueller 			}
500f0cd44b4SDevin Heitmueller 		} else {
501f0cd44b4SDevin Heitmueller 			if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L2) {
502f0cd44b4SDevin Heitmueller 				dprintk("%s() setting QAM state to OPT_L2\n",
503f0cd44b4SDevin Heitmueller 					__func__);
504f0cd44b4SDevin Heitmueller 				s5h1409_writereg(state, 0x93, 0x3332);
505f0cd44b4SDevin Heitmueller 				s5h1409_writereg(state, 0x9e, 0x2c37);
506f0cd44b4SDevin Heitmueller 				state->qam_state = QAM_STATE_QAM_OPTIMIZED_L2;
507f0cd44b4SDevin Heitmueller 			}
508f0cd44b4SDevin Heitmueller 		}
509f0cd44b4SDevin Heitmueller 
510f0cd44b4SDevin Heitmueller 	} else {
511f0cd44b4SDevin Heitmueller 		if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L1) {
512f0cd44b4SDevin Heitmueller 			dprintk("%s() setting QAM state to OPT_L1\n", __func__);
513f0cd44b4SDevin Heitmueller 			s5h1409_writereg(state, 0x96, 0x0008);
514f0cd44b4SDevin Heitmueller 			s5h1409_writereg(state, 0x93, 0x3332);
515f0cd44b4SDevin Heitmueller 			s5h1409_writereg(state, 0x9e, 0x2c37);
516f0cd44b4SDevin Heitmueller 			state->qam_state = QAM_STATE_QAM_OPTIMIZED_L1;
517f0cd44b4SDevin Heitmueller 		}
518f0cd44b4SDevin Heitmueller 	}
519f0cd44b4SDevin Heitmueller }
520f0cd44b4SDevin Heitmueller 
521f0cd44b4SDevin Heitmueller static void s5h1409_set_qam_amhum_mode_legacy(struct dvb_frontend *fe)
522f0cd44b4SDevin Heitmueller {
523f0cd44b4SDevin Heitmueller 	struct s5h1409_state *state = fe->demodulator_priv;
524f0cd44b4SDevin Heitmueller 	u16 reg;
525f0cd44b4SDevin Heitmueller 
526dd7d5013SSteven Toth 	if (state->is_qam_locked)
527dd7d5013SSteven Toth 		return;
528dd7d5013SSteven Toth 
529dd7d5013SSteven Toth 	/* QAM EQ lock check */
530dd7d5013SSteven Toth 	reg = s5h1409_readreg(state, 0xf0);
531dd7d5013SSteven Toth 
532dd7d5013SSteven Toth 	if ((reg >> 13) & 0x1) {
533dd7d5013SSteven Toth 
534dd7d5013SSteven Toth 		state->is_qam_locked = 1;
535dd7d5013SSteven Toth 		reg &= 0xff;
536dd7d5013SSteven Toth 
537dd7d5013SSteven Toth 		s5h1409_writereg(state, 0x96, 0x00c);
538dd7d5013SSteven Toth 		if ((reg < 0x38) || (reg > 0x68)) {
539dd7d5013SSteven Toth 			s5h1409_writereg(state, 0x93, 0x3332);
540dd7d5013SSteven Toth 			s5h1409_writereg(state, 0x9e, 0x2c37);
541dd7d5013SSteven Toth 		} else {
542dd7d5013SSteven Toth 			s5h1409_writereg(state, 0x93, 0x3130);
543dd7d5013SSteven Toth 			s5h1409_writereg(state, 0x9e, 0x2836);
544dd7d5013SSteven Toth 		}
545dd7d5013SSteven Toth 
546dd7d5013SSteven Toth 	} else {
547dd7d5013SSteven Toth 		s5h1409_writereg(state, 0x96, 0x0008);
548dd7d5013SSteven Toth 		s5h1409_writereg(state, 0x93, 0x3332);
549dd7d5013SSteven Toth 		s5h1409_writereg(state, 0x9e, 0x2c37);
550dd7d5013SSteven Toth 	}
551dd7d5013SSteven Toth }
552dd7d5013SSteven Toth 
553dd7d5013SSteven Toth static void s5h1409_set_qam_interleave_mode(struct dvb_frontend *fe)
554dd7d5013SSteven Toth {
555dd7d5013SSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
556dd7d5013SSteven Toth 	u16 reg, reg1, reg2;
557dd7d5013SSteven Toth 
558f0cd44b4SDevin Heitmueller 	if (state->qam_state >= QAM_STATE_INTERLEAVE_SET) {
559f0cd44b4SDevin Heitmueller 		/* We've done the optimization already */
560f0cd44b4SDevin Heitmueller 		return;
561f0cd44b4SDevin Heitmueller 	}
562f0cd44b4SDevin Heitmueller 
563f0cd44b4SDevin Heitmueller 	reg = s5h1409_readreg(state, 0xf1);
564f0cd44b4SDevin Heitmueller 
565f0cd44b4SDevin Heitmueller 	/* Master lock */
566f0cd44b4SDevin Heitmueller 	if ((reg >> 15) & 0x1) {
567f0cd44b4SDevin Heitmueller 		if (state->qam_state == QAM_STATE_UNTUNED ||
568f0cd44b4SDevin Heitmueller 		    state->qam_state == QAM_STATE_TUNING_STARTED) {
569f0cd44b4SDevin Heitmueller 			dprintk("%s() setting QAM state to INTERLEAVE_SET\n",
570f0cd44b4SDevin Heitmueller 				__func__);
571f0cd44b4SDevin Heitmueller 			reg1 = s5h1409_readreg(state, 0xb2);
572f0cd44b4SDevin Heitmueller 			reg2 = s5h1409_readreg(state, 0xad);
573f0cd44b4SDevin Heitmueller 
574f0cd44b4SDevin Heitmueller 			s5h1409_writereg(state, 0x96, 0x0020);
575f0cd44b4SDevin Heitmueller 			s5h1409_writereg(state, 0xad,
576f0cd44b4SDevin Heitmueller 				(((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff)));
577f0cd44b4SDevin Heitmueller 			state->qam_state = QAM_STATE_INTERLEAVE_SET;
578f0cd44b4SDevin Heitmueller 		}
579f0cd44b4SDevin Heitmueller 	} else {
580f0cd44b4SDevin Heitmueller 		if (state->qam_state == QAM_STATE_UNTUNED) {
581f0cd44b4SDevin Heitmueller 			dprintk("%s() setting QAM state to TUNING_STARTED\n",
582f0cd44b4SDevin Heitmueller 				__func__);
583f0cd44b4SDevin Heitmueller 			s5h1409_writereg(state, 0x96, 0x08);
584f0cd44b4SDevin Heitmueller 			s5h1409_writereg(state, 0xab,
585f0cd44b4SDevin Heitmueller 				s5h1409_readreg(state, 0xab) | 0x1001);
586f0cd44b4SDevin Heitmueller 			state->qam_state = QAM_STATE_TUNING_STARTED;
587f0cd44b4SDevin Heitmueller 		}
588f0cd44b4SDevin Heitmueller 	}
589f0cd44b4SDevin Heitmueller }
590f0cd44b4SDevin Heitmueller 
591f0cd44b4SDevin Heitmueller static void s5h1409_set_qam_interleave_mode_legacy(struct dvb_frontend *fe)
592f0cd44b4SDevin Heitmueller {
593f0cd44b4SDevin Heitmueller 	struct s5h1409_state *state = fe->demodulator_priv;
594f0cd44b4SDevin Heitmueller 	u16 reg, reg1, reg2;
595f0cd44b4SDevin Heitmueller 
596dd7d5013SSteven Toth 	reg = s5h1409_readreg(state, 0xf1);
597dd7d5013SSteven Toth 
598dd7d5013SSteven Toth 	/* Master lock */
599dd7d5013SSteven Toth 	if ((reg >> 15) & 0x1) {
600dd7d5013SSteven Toth 		if (state->qam_state != 2) {
601dd7d5013SSteven Toth 			state->qam_state = 2;
602dd7d5013SSteven Toth 			reg1 = s5h1409_readreg(state, 0xb2);
603dd7d5013SSteven Toth 			reg2 = s5h1409_readreg(state, 0xad);
604dd7d5013SSteven Toth 
605dd7d5013SSteven Toth 			s5h1409_writereg(state, 0x96, 0x20);
606dd7d5013SSteven Toth 			s5h1409_writereg(state, 0xad,
607dd7d5013SSteven Toth 				(((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff)));
608dfc1c08aSSteven Toth 			s5h1409_writereg(state, 0xab,
609dfc1c08aSSteven Toth 				s5h1409_readreg(state, 0xab) & 0xeffe);
610dd7d5013SSteven Toth 		}
611dd7d5013SSteven Toth 	} else {
612dd7d5013SSteven Toth 		if (state->qam_state != 1) {
613dd7d5013SSteven Toth 			state->qam_state = 1;
614dd7d5013SSteven Toth 			s5h1409_writereg(state, 0x96, 0x08);
615dfc1c08aSSteven Toth 			s5h1409_writereg(state, 0xab,
616dfc1c08aSSteven Toth 				s5h1409_readreg(state, 0xab) | 0x1001);
617dd7d5013SSteven Toth 		}
618dd7d5013SSteven Toth 	}
619dd7d5013SSteven Toth }
620dd7d5013SSteven Toth 
62189885558SSteven Toth /* Talk to the demod, set the FEC, GUARD, QAM settings etc */
622fd129844SMauro Carvalho Chehab static int s5h1409_set_frontend(struct dvb_frontend *fe)
62389885558SSteven Toth {
624fd129844SMauro Carvalho Chehab 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
62589885558SSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
62689885558SSteven Toth 
627271ddbf7SHarvey Harrison 	dprintk("%s(frequency=%d)\n", __func__, p->frequency);
62889885558SSteven Toth 
62989885558SSteven Toth 	s5h1409_softreset(fe);
63089885558SSteven Toth 
63189885558SSteven Toth 	state->current_frequency = p->frequency;
63289885558SSteven Toth 
633fd129844SMauro Carvalho Chehab 	s5h1409_enable_modulation(fe, p->modulation);
63489885558SSteven Toth 
63589885558SSteven Toth 	if (fe->ops.tuner_ops.set_params) {
636b7709c0dSSteven Toth 		if (fe->ops.i2c_gate_ctrl)
637b7709c0dSSteven Toth 			fe->ops.i2c_gate_ctrl(fe, 1);
63814d24d14SMauro Carvalho Chehab 		fe->ops.tuner_ops.set_params(fe);
639b7709c0dSSteven Toth 		if (fe->ops.i2c_gate_ctrl)
640b7709c0dSSteven Toth 			fe->ops.i2c_gate_ctrl(fe, 0);
64189885558SSteven Toth 	}
64289885558SSteven Toth 
64367e70bafSDevin Heitmueller 	/* Issue a reset to the demod so it knows to resync against the
64467e70bafSDevin Heitmueller 	   newly tuned frequency */
64567e70bafSDevin Heitmueller 	s5h1409_softreset(fe);
64667e70bafSDevin Heitmueller 
647f0cd44b4SDevin Heitmueller 	/* Optimize the demod for QAM */
648f0cd44b4SDevin Heitmueller 	if (state->current_modulation != VSB_8) {
649f0cd44b4SDevin Heitmueller 		/* This almost certainly applies to all boards, but for now
650f0cd44b4SDevin Heitmueller 		   only do it for the HVR-1600.  Once the other boards are
651f0cd44b4SDevin Heitmueller 		   tested, the "legacy" versions can just go away */
652f0cd44b4SDevin Heitmueller 		if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) {
653f0cd44b4SDevin Heitmueller 			s5h1409_set_qam_interleave_mode(fe);
654adcd8de6SDevin Heitmueller 			s5h1409_set_qam_amhum_mode(fe);
655f0cd44b4SDevin Heitmueller 		} else {
656f0cd44b4SDevin Heitmueller 			s5h1409_set_qam_amhum_mode_legacy(fe);
657f0cd44b4SDevin Heitmueller 			s5h1409_set_qam_interleave_mode_legacy(fe);
658f0cd44b4SDevin Heitmueller 		}
659f0cd44b4SDevin Heitmueller 	}
660f0cd44b4SDevin Heitmueller 
66189885558SSteven Toth 	return 0;
66289885558SSteven Toth }
66389885558SSteven Toth 
664dfc1c08aSSteven Toth static int s5h1409_set_mpeg_timing(struct dvb_frontend *fe, int mode)
665dfc1c08aSSteven Toth {
666dfc1c08aSSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
667dfc1c08aSSteven Toth 	u16 val;
668dfc1c08aSSteven Toth 
669271ddbf7SHarvey Harrison 	dprintk("%s(%d)\n", __func__, mode);
670dfc1c08aSSteven Toth 
671dfc1c08aSSteven Toth 	val = s5h1409_readreg(state, 0xac) & 0xcfff;
672dfc1c08aSSteven Toth 	switch (mode) {
673ad05ff09SMauro Carvalho Chehab 	case S5H1409_MPEGTIMING_CONTINUOUS_INVERTING_CLOCK:
674dfc1c08aSSteven Toth 		val |= 0x0000;
675dfc1c08aSSteven Toth 		break;
676ad05ff09SMauro Carvalho Chehab 	case S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK:
677271ddbf7SHarvey Harrison 		dprintk("%s(%d) Mode1 or Defaulting\n", __func__, mode);
678dfc1c08aSSteven Toth 		val |= 0x1000;
679dfc1c08aSSteven Toth 		break;
680ad05ff09SMauro Carvalho Chehab 	case S5H1409_MPEGTIMING_NONCONTINUOUS_INVERTING_CLOCK:
681dfc1c08aSSteven Toth 		val |= 0x2000;
682dfc1c08aSSteven Toth 		break;
683ad05ff09SMauro Carvalho Chehab 	case S5H1409_MPEGTIMING_NONCONTINUOUS_NONINVERTING_CLOCK:
684dfc1c08aSSteven Toth 		val |= 0x3000;
685dfc1c08aSSteven Toth 		break;
686dfc1c08aSSteven Toth 	default:
687dfc1c08aSSteven Toth 		return -EINVAL;
688dfc1c08aSSteven Toth 	}
689dfc1c08aSSteven Toth 
690dfc1c08aSSteven Toth 	/* Configure MPEG Signal Timing charactistics */
691dfc1c08aSSteven Toth 	return s5h1409_writereg(state, 0xac, val);
692dfc1c08aSSteven Toth }
693dfc1c08aSSteven Toth 
69489885558SSteven Toth /* Reset the demod hardware and reset all of the configuration registers
69589885558SSteven Toth    to a default state. */
69689885558SSteven Toth static int s5h1409_init(struct dvb_frontend *fe)
69789885558SSteven Toth {
69889885558SSteven Toth 	int i;
69989885558SSteven Toth 
70089885558SSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
701271ddbf7SHarvey Harrison 	dprintk("%s()\n", __func__);
70289885558SSteven Toth 
70389885558SSteven Toth 	s5h1409_sleep(fe, 0);
70489885558SSteven Toth 	s5h1409_register_reset(fe);
70589885558SSteven Toth 
706a45c9275SMichael Krufky 	for (i = 0; i < ARRAY_SIZE(init_tab); i++)
70789885558SSteven Toth 		s5h1409_writereg(state, init_tab[i].reg, init_tab[i].data);
70889885558SSteven Toth 
70989885558SSteven Toth 	/* The datasheet says that after initialisation, VSB is default */
71089885558SSteven Toth 	state->current_modulation = VSB_8;
71189885558SSteven Toth 
712af5c8e15SDevin Heitmueller 	/* Optimize for the HVR-1600 if appropriate.  Note that some of these
713af5c8e15SDevin Heitmueller 	   may get folded into the generic case after testing with other
714af5c8e15SDevin Heitmueller 	   devices */
715af5c8e15SDevin Heitmueller 	if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) {
716af5c8e15SDevin Heitmueller 		/* VSB AGC REF */
717af5c8e15SDevin Heitmueller 		s5h1409_writereg(state, 0x09, 0x0050);
718af5c8e15SDevin Heitmueller 
719af5c8e15SDevin Heitmueller 		/* Unknown but Windows driver does it... */
720af5c8e15SDevin Heitmueller 		s5h1409_writereg(state, 0x21, 0x0001);
721af5c8e15SDevin Heitmueller 		s5h1409_writereg(state, 0x50, 0x030e);
722af5c8e15SDevin Heitmueller 
723af5c8e15SDevin Heitmueller 		/* QAM AGC REF */
724af5c8e15SDevin Heitmueller 		s5h1409_writereg(state, 0x82, 0x0800);
725af5c8e15SDevin Heitmueller 	}
726af5c8e15SDevin Heitmueller 
72789885558SSteven Toth 	if (state->config->output_mode == S5H1409_SERIAL_OUTPUT)
728dfc1c08aSSteven Toth 		s5h1409_writereg(state, 0xab,
729dfc1c08aSSteven Toth 			s5h1409_readreg(state, 0xab) | 0x100); /* Serial */
73089885558SSteven Toth 	else
731dfc1c08aSSteven Toth 		s5h1409_writereg(state, 0xab,
732dfc1c08aSSteven Toth 			s5h1409_readreg(state, 0xab) & 0xfeff); /* Parallel */
73389885558SSteven Toth 
73489885558SSteven Toth 	s5h1409_set_spectralinversion(fe, state->config->inversion);
7352b03238aSMichael Krufky 	s5h1409_set_if_freq(fe, state->if_freq);
73689885558SSteven Toth 	s5h1409_set_gpio(fe, state->config->gpio);
737dfc1c08aSSteven Toth 	s5h1409_set_mpeg_timing(fe, state->config->mpeg_timing);
73889885558SSteven Toth 	s5h1409_softreset(fe);
73989885558SSteven Toth 
740dd7d5013SSteven Toth 	/* Note: Leaving the I2C gate closed. */
741dd7d5013SSteven Toth 	s5h1409_i2c_gate_ctrl(fe, 0);
74289885558SSteven Toth 
74389885558SSteven Toth 	return 0;
74489885558SSteven Toth }
74589885558SSteven Toth 
7460df289a2SMauro Carvalho Chehab static int s5h1409_read_status(struct dvb_frontend *fe, enum fe_status *status)
74789885558SSteven Toth {
74889885558SSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
74989885558SSteven Toth 	u16 reg;
75089885558SSteven Toth 	u32 tuner_status = 0;
75189885558SSteven Toth 
75289885558SSteven Toth 	*status = 0;
75389885558SSteven Toth 
754f0cd44b4SDevin Heitmueller 	/* Optimize the demod for QAM */
755f0cd44b4SDevin Heitmueller 	if (state->current_modulation != VSB_8) {
756f0cd44b4SDevin Heitmueller 		/* This almost certainly applies to all boards, but for now
757f0cd44b4SDevin Heitmueller 		   only do it for the HVR-1600.  Once the other boards are
758f0cd44b4SDevin Heitmueller 		   tested, the "legacy" versions can just go away */
759f0cd44b4SDevin Heitmueller 		if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) {
760f0cd44b4SDevin Heitmueller 			s5h1409_set_qam_interleave_mode(fe);
761adcd8de6SDevin Heitmueller 			s5h1409_set_qam_amhum_mode(fe);
762f0cd44b4SDevin Heitmueller 		}
763f0cd44b4SDevin Heitmueller 	}
764f0cd44b4SDevin Heitmueller 
76589885558SSteven Toth 	/* Get the demodulator status */
76689885558SSteven Toth 	reg = s5h1409_readreg(state, 0xf1);
76789885558SSteven Toth 	if (reg & 0x1000)
76889885558SSteven Toth 		*status |= FE_HAS_VITERBI;
76989885558SSteven Toth 	if (reg & 0x8000)
77089885558SSteven Toth 		*status |= FE_HAS_LOCK | FE_HAS_SYNC;
77189885558SSteven Toth 
77289885558SSteven Toth 	switch (state->config->status_mode) {
77389885558SSteven Toth 	case S5H1409_DEMODLOCKING:
77489885558SSteven Toth 		if (*status & FE_HAS_VITERBI)
77589885558SSteven Toth 			*status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
77689885558SSteven Toth 		break;
77789885558SSteven Toth 	case S5H1409_TUNERLOCKING:
77889885558SSteven Toth 		/* Get the tuner status */
77989885558SSteven Toth 		if (fe->ops.tuner_ops.get_status) {
7803873dd04SMichael Krufky 			if (fe->ops.i2c_gate_ctrl)
7813873dd04SMichael Krufky 				fe->ops.i2c_gate_ctrl(fe, 1);
78289885558SSteven Toth 
78389885558SSteven Toth 			fe->ops.tuner_ops.get_status(fe, &tuner_status);
78489885558SSteven Toth 
7853873dd04SMichael Krufky 			if (fe->ops.i2c_gate_ctrl)
7863873dd04SMichael Krufky 				fe->ops.i2c_gate_ctrl(fe, 0);
78789885558SSteven Toth 		}
78889885558SSteven Toth 		if (tuner_status)
78989885558SSteven Toth 			*status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
79089885558SSteven Toth 		break;
79189885558SSteven Toth 	}
79289885558SSteven Toth 
793271ddbf7SHarvey Harrison 	dprintk("%s() status 0x%08x\n", __func__, *status);
79489885558SSteven Toth 
79589885558SSteven Toth 	return 0;
79689885558SSteven Toth }
79789885558SSteven Toth 
79889885558SSteven Toth static int s5h1409_qam256_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v)
79989885558SSteven Toth {
80089885558SSteven Toth 	int i, ret = -EINVAL;
801271ddbf7SHarvey Harrison 	dprintk("%s()\n", __func__);
80289885558SSteven Toth 
803a45c9275SMichael Krufky 	for (i = 0; i < ARRAY_SIZE(qam256_snr_tab); i++) {
80489885558SSteven Toth 		if (v < qam256_snr_tab[i].val) {
80589885558SSteven Toth 			*snr = qam256_snr_tab[i].data;
80689885558SSteven Toth 			ret = 0;
80789885558SSteven Toth 			break;
80889885558SSteven Toth 		}
80989885558SSteven Toth 	}
81089885558SSteven Toth 	return ret;
81189885558SSteven Toth }
81289885558SSteven Toth 
81389885558SSteven Toth static int s5h1409_qam64_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v)
81489885558SSteven Toth {
81589885558SSteven Toth 	int i, ret = -EINVAL;
816271ddbf7SHarvey Harrison 	dprintk("%s()\n", __func__);
81789885558SSteven Toth 
818a45c9275SMichael Krufky 	for (i = 0; i < ARRAY_SIZE(qam64_snr_tab); i++) {
81989885558SSteven Toth 		if (v < qam64_snr_tab[i].val) {
82089885558SSteven Toth 			*snr = qam64_snr_tab[i].data;
82189885558SSteven Toth 			ret = 0;
82289885558SSteven Toth 			break;
82389885558SSteven Toth 		}
82489885558SSteven Toth 	}
82589885558SSteven Toth 	return ret;
82689885558SSteven Toth }
82789885558SSteven Toth 
82889885558SSteven Toth static int s5h1409_vsb_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v)
82989885558SSteven Toth {
83089885558SSteven Toth 	int i, ret = -EINVAL;
831271ddbf7SHarvey Harrison 	dprintk("%s()\n", __func__);
83289885558SSteven Toth 
833a45c9275SMichael Krufky 	for (i = 0; i < ARRAY_SIZE(vsb_snr_tab); i++) {
83489885558SSteven Toth 		if (v > vsb_snr_tab[i].val) {
83589885558SSteven Toth 			*snr = vsb_snr_tab[i].data;
83689885558SSteven Toth 			ret = 0;
83789885558SSteven Toth 			break;
83889885558SSteven Toth 		}
83989885558SSteven Toth 	}
840271ddbf7SHarvey Harrison 	dprintk("%s() snr=%d\n", __func__, *snr);
84189885558SSteven Toth 	return ret;
84289885558SSteven Toth }
84389885558SSteven Toth 
84489885558SSteven Toth static int s5h1409_read_snr(struct dvb_frontend *fe, u16 *snr)
84589885558SSteven Toth {
84689885558SSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
84789885558SSteven Toth 	u16 reg;
848271ddbf7SHarvey Harrison 	dprintk("%s()\n", __func__);
84989885558SSteven Toth 
85089885558SSteven Toth 	switch (state->current_modulation) {
85189885558SSteven Toth 	case QAM_64:
8522300317fSSteven Toth 		reg = s5h1409_readreg(state, 0xf0) & 0xff;
85389885558SSteven Toth 		return s5h1409_qam64_lookup_snr(fe, snr, reg);
85489885558SSteven Toth 	case QAM_256:
8552300317fSSteven Toth 		reg = s5h1409_readreg(state, 0xf0) & 0xff;
85689885558SSteven Toth 		return s5h1409_qam256_lookup_snr(fe, snr, reg);
85789885558SSteven Toth 	case VSB_8:
8582300317fSSteven Toth 		reg = s5h1409_readreg(state, 0xf1) & 0x3ff;
85989885558SSteven Toth 		return s5h1409_vsb_lookup_snr(fe, snr, reg);
86089885558SSteven Toth 	default:
86189885558SSteven Toth 		break;
86289885558SSteven Toth 	}
86389885558SSteven Toth 
86489885558SSteven Toth 	return -EINVAL;
86589885558SSteven Toth }
86689885558SSteven Toth 
8673873dd04SMichael Krufky static int s5h1409_read_signal_strength(struct dvb_frontend *fe,
8683873dd04SMichael Krufky 					u16 *signal_strength)
86989885558SSteven Toth {
87019661c08SMichael Krufky 	/* borrowed from lgdt330x.c
87119661c08SMichael Krufky 	 *
87219661c08SMichael Krufky 	 * Calculate strength from SNR up to 35dB
87319661c08SMichael Krufky 	 * Even though the SNR can go higher than 35dB,
87419661c08SMichael Krufky 	 * there is some comfort factor in having a range of
87519661c08SMichael Krufky 	 * strong signals that can show at 100%
87619661c08SMichael Krufky 	 */
87719661c08SMichael Krufky 	u16 snr;
87819661c08SMichael Krufky 	u32 tmp;
87919661c08SMichael Krufky 	int ret = s5h1409_read_snr(fe, &snr);
88019661c08SMichael Krufky 
88119661c08SMichael Krufky 	*signal_strength = 0;
88219661c08SMichael Krufky 
88319661c08SMichael Krufky 	if (0 == ret) {
88419661c08SMichael Krufky 		/* The following calculation method was chosen
88519661c08SMichael Krufky 		 * purely for the sake of code re-use from the
88619661c08SMichael Krufky 		 * other demod drivers that use this method */
88719661c08SMichael Krufky 
88819661c08SMichael Krufky 		/* Convert from SNR in dB * 10 to 8.24 fixed-point */
88919661c08SMichael Krufky 		tmp = (snr * ((1 << 24) / 10));
89019661c08SMichael Krufky 
89119661c08SMichael Krufky 		/* Convert from 8.24 fixed-point to
89219661c08SMichael Krufky 		 * scale the range 0 - 35*2^24 into 0 - 65535*/
89319661c08SMichael Krufky 		if (tmp >= 8960 * 0x10000)
89419661c08SMichael Krufky 			*signal_strength = 0xffff;
89519661c08SMichael Krufky 		else
89619661c08SMichael Krufky 			*signal_strength = tmp / 8960;
89719661c08SMichael Krufky 	}
89819661c08SMichael Krufky 
89919661c08SMichael Krufky 	return ret;
90089885558SSteven Toth }
90189885558SSteven Toth 
90289885558SSteven Toth static int s5h1409_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
90389885558SSteven Toth {
90489885558SSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
90589885558SSteven Toth 
90689885558SSteven Toth 	*ucblocks = s5h1409_readreg(state, 0xb5);
90789885558SSteven Toth 
90889885558SSteven Toth 	return 0;
90989885558SSteven Toth }
91089885558SSteven Toth 
91189885558SSteven Toth static int s5h1409_read_ber(struct dvb_frontend *fe, u32 *ber)
91289885558SSteven Toth {
91389885558SSteven Toth 	return s5h1409_read_ucblocks(fe, ber);
91489885558SSteven Toth }
91589885558SSteven Toth 
9167e3e68bcSMauro Carvalho Chehab static int s5h1409_get_frontend(struct dvb_frontend *fe,
9177e3e68bcSMauro Carvalho Chehab 				struct dtv_frontend_properties *p)
91889885558SSteven Toth {
91989885558SSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
92089885558SSteven Toth 
92189885558SSteven Toth 	p->frequency = state->current_frequency;
922fd129844SMauro Carvalho Chehab 	p->modulation = state->current_modulation;
92389885558SSteven Toth 
92489885558SSteven Toth 	return 0;
92589885558SSteven Toth }
92689885558SSteven Toth 
9273873dd04SMichael Krufky static int s5h1409_get_tune_settings(struct dvb_frontend *fe,
9283873dd04SMichael Krufky 				     struct dvb_frontend_tune_settings *tune)
92989885558SSteven Toth {
93089885558SSteven Toth 	tune->min_delay_ms = 1000;
93189885558SSteven Toth 	return 0;
93289885558SSteven Toth }
93389885558SSteven Toth 
93489885558SSteven Toth static void s5h1409_release(struct dvb_frontend *fe)
93589885558SSteven Toth {
93689885558SSteven Toth 	struct s5h1409_state *state = fe->demodulator_priv;
93789885558SSteven Toth 	kfree(state);
93889885558SSteven Toth }
93989885558SSteven Toth 
940bd336e63SMax Kellermann static const struct dvb_frontend_ops s5h1409_ops;
94189885558SSteven Toth 
94289885558SSteven Toth struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config,
94389885558SSteven Toth 				    struct i2c_adapter *i2c)
94489885558SSteven Toth {
94589885558SSteven Toth 	struct s5h1409_state *state = NULL;
946a57ed8a1SSteven Toth 	u16 reg;
94789885558SSteven Toth 
94889885558SSteven Toth 	/* allocate memory for the internal state */
949084e24acSMatthias Schwarzott 	state = kzalloc(sizeof(struct s5h1409_state), GFP_KERNEL);
95089885558SSteven Toth 	if (state == NULL)
95189885558SSteven Toth 		goto error;
95289885558SSteven Toth 
95389885558SSteven Toth 	/* setup the state */
95489885558SSteven Toth 	state->config = config;
95589885558SSteven Toth 	state->i2c = i2c;
95689885558SSteven Toth 	state->current_modulation = 0;
9572b03238aSMichael Krufky 	state->if_freq = S5H1409_VSB_IF_FREQ;
95889885558SSteven Toth 
95989885558SSteven Toth 	/* check if the demod exists */
960a57ed8a1SSteven Toth 	reg = s5h1409_readreg(state, 0x04);
961a57ed8a1SSteven Toth 	if ((reg != 0x0066) && (reg != 0x007f))
96289885558SSteven Toth 		goto error;
96389885558SSteven Toth 
96489885558SSteven Toth 	/* create dvb_frontend */
9653873dd04SMichael Krufky 	memcpy(&state->frontend.ops, &s5h1409_ops,
9663873dd04SMichael Krufky 	       sizeof(struct dvb_frontend_ops));
96789885558SSteven Toth 	state->frontend.demodulator_priv = state;
96889885558SSteven Toth 
969a57ed8a1SSteven Toth 	if (s5h1409_init(&state->frontend) != 0) {
970a57ed8a1SSteven Toth 		printk(KERN_ERR "%s: Failed to initialize correctly\n",
971271ddbf7SHarvey Harrison 			__func__);
972a57ed8a1SSteven Toth 		goto error;
973a57ed8a1SSteven Toth 	}
974a57ed8a1SSteven Toth 
97589885558SSteven Toth 	/* Note: Leaving the I2C gate open here. */
976a57ed8a1SSteven Toth 	s5h1409_i2c_gate_ctrl(&state->frontend, 1);
97789885558SSteven Toth 
97889885558SSteven Toth 	return &state->frontend;
97989885558SSteven Toth 
98089885558SSteven Toth error:
98189885558SSteven Toth 	kfree(state);
98289885558SSteven Toth 	return NULL;
98389885558SSteven Toth }
984b7709c0dSSteven Toth EXPORT_SYMBOL(s5h1409_attach);
98589885558SSteven Toth 
986bd336e63SMax Kellermann static const struct dvb_frontend_ops s5h1409_ops = {
987fd129844SMauro Carvalho Chehab 	.delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
98889885558SSteven Toth 	.info = {
98989885558SSteven Toth 		.name			= "Samsung S5H1409 QAM/8VSB Frontend",
990f1b1eabfSMauro Carvalho Chehab 		.frequency_min_hz	=  54 * MHz,
991f1b1eabfSMauro Carvalho Chehab 		.frequency_max_hz	= 858 * MHz,
992f1b1eabfSMauro Carvalho Chehab 		.frequency_stepsize_hz	= 62500,
99389885558SSteven Toth 		.caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
99489885558SSteven Toth 	},
99589885558SSteven Toth 
99689885558SSteven Toth 	.init                 = s5h1409_init,
99789885558SSteven Toth 	.i2c_gate_ctrl        = s5h1409_i2c_gate_ctrl,
998fd129844SMauro Carvalho Chehab 	.set_frontend         = s5h1409_set_frontend,
999fd129844SMauro Carvalho Chehab 	.get_frontend         = s5h1409_get_frontend,
100089885558SSteven Toth 	.get_tune_settings    = s5h1409_get_tune_settings,
100189885558SSteven Toth 	.read_status          = s5h1409_read_status,
100289885558SSteven Toth 	.read_ber             = s5h1409_read_ber,
100389885558SSteven Toth 	.read_signal_strength = s5h1409_read_signal_strength,
100489885558SSteven Toth 	.read_snr             = s5h1409_read_snr,
100589885558SSteven Toth 	.read_ucblocks        = s5h1409_read_ucblocks,
100689885558SSteven Toth 	.release              = s5h1409_release,
100789885558SSteven Toth };
100889885558SSteven Toth 
100989885558SSteven Toth MODULE_DESCRIPTION("Samsung S5H1409 QAM-B/ATSC Demodulator driver");
101089885558SSteven Toth MODULE_AUTHOR("Steven Toth");
101189885558SSteven Toth MODULE_LICENSE("GPL");
1012