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