19a0bf528SMauro Carvalho Chehab /* 29a0bf528SMauro Carvalho Chehab Driver for M88RS2000 demodulator and tuner 39a0bf528SMauro Carvalho Chehab 49a0bf528SMauro Carvalho Chehab Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com) 59a0bf528SMauro Carvalho Chehab Beta Driver 69a0bf528SMauro Carvalho Chehab 79a0bf528SMauro Carvalho Chehab Include various calculation code from DS3000 driver. 89a0bf528SMauro Carvalho Chehab Copyright (C) 2009 Konstantin Dimitrov. 99a0bf528SMauro Carvalho Chehab 109a0bf528SMauro Carvalho Chehab This program is free software; you can redistribute it and/or modify 119a0bf528SMauro Carvalho Chehab it under the terms of the GNU General Public License as published by 129a0bf528SMauro Carvalho Chehab the Free Software Foundation; either version 2 of the License, or 139a0bf528SMauro Carvalho Chehab (at your option) any later version. 149a0bf528SMauro Carvalho Chehab 159a0bf528SMauro Carvalho Chehab This program is distributed in the hope that it will be useful, 169a0bf528SMauro Carvalho Chehab but WITHOUT ANY WARRANTY; without even the implied warranty of 179a0bf528SMauro Carvalho Chehab MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 189a0bf528SMauro Carvalho Chehab GNU General Public License for more details. 199a0bf528SMauro Carvalho Chehab 209a0bf528SMauro Carvalho Chehab You should have received a copy of the GNU General Public License 219a0bf528SMauro Carvalho Chehab along with this program; if not, write to the Free Software 229a0bf528SMauro Carvalho Chehab Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 239a0bf528SMauro Carvalho Chehab 249a0bf528SMauro Carvalho Chehab */ 259a0bf528SMauro Carvalho Chehab #include <linux/init.h> 269a0bf528SMauro Carvalho Chehab #include <linux/module.h> 279a0bf528SMauro Carvalho Chehab #include <linux/device.h> 289a0bf528SMauro Carvalho Chehab #include <linux/jiffies.h> 299a0bf528SMauro Carvalho Chehab #include <linux/string.h> 309a0bf528SMauro Carvalho Chehab #include <linux/slab.h> 319a0bf528SMauro Carvalho Chehab #include <linux/types.h> 329a0bf528SMauro Carvalho Chehab 339a0bf528SMauro Carvalho Chehab 349a0bf528SMauro Carvalho Chehab #include "dvb_frontend.h" 359a0bf528SMauro Carvalho Chehab #include "m88rs2000.h" 369a0bf528SMauro Carvalho Chehab 379a0bf528SMauro Carvalho Chehab struct m88rs2000_state { 389a0bf528SMauro Carvalho Chehab struct i2c_adapter *i2c; 399a0bf528SMauro Carvalho Chehab const struct m88rs2000_config *config; 409a0bf528SMauro Carvalho Chehab struct dvb_frontend frontend; 419a0bf528SMauro Carvalho Chehab u8 no_lock_count; 429a0bf528SMauro Carvalho Chehab u32 tuner_frequency; 439a0bf528SMauro Carvalho Chehab u32 symbol_rate; 449a0bf528SMauro Carvalho Chehab fe_code_rate_t fec_inner; 459a0bf528SMauro Carvalho Chehab u8 tuner_level; 469a0bf528SMauro Carvalho Chehab int errmode; 479a0bf528SMauro Carvalho Chehab }; 489a0bf528SMauro Carvalho Chehab 499a0bf528SMauro Carvalho Chehab static int m88rs2000_debug; 509a0bf528SMauro Carvalho Chehab 519a0bf528SMauro Carvalho Chehab module_param_named(debug, m88rs2000_debug, int, 0644); 529a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); 539a0bf528SMauro Carvalho Chehab 549a0bf528SMauro Carvalho Chehab #define dprintk(level, args...) do { \ 559a0bf528SMauro Carvalho Chehab if (level & m88rs2000_debug) \ 569a0bf528SMauro Carvalho Chehab printk(KERN_DEBUG "m88rs2000-fe: " args); \ 579a0bf528SMauro Carvalho Chehab } while (0) 589a0bf528SMauro Carvalho Chehab 599a0bf528SMauro Carvalho Chehab #define deb_info(args...) dprintk(0x01, args) 609a0bf528SMauro Carvalho Chehab #define info(format, arg...) \ 619a0bf528SMauro Carvalho Chehab printk(KERN_INFO "m88rs2000-fe: " format "\n" , ## arg) 629a0bf528SMauro Carvalho Chehab 63b858c331SIgor M. Liplianin static int m88rs2000_writereg(struct m88rs2000_state *state, 649a0bf528SMauro Carvalho Chehab u8 reg, u8 data) 659a0bf528SMauro Carvalho Chehab { 669a0bf528SMauro Carvalho Chehab int ret; 679a0bf528SMauro Carvalho Chehab u8 buf[] = { reg, data }; 689a0bf528SMauro Carvalho Chehab struct i2c_msg msg = { 69b858c331SIgor M. Liplianin .addr = state->config->demod_addr, 709a0bf528SMauro Carvalho Chehab .flags = 0, 719a0bf528SMauro Carvalho Chehab .buf = buf, 729a0bf528SMauro Carvalho Chehab .len = 2 739a0bf528SMauro Carvalho Chehab }; 749a0bf528SMauro Carvalho Chehab 759a0bf528SMauro Carvalho Chehab ret = i2c_transfer(state->i2c, &msg, 1); 769a0bf528SMauro Carvalho Chehab 779a0bf528SMauro Carvalho Chehab if (ret != 1) 789a0bf528SMauro Carvalho Chehab deb_info("%s: writereg error (reg == 0x%02x, val == 0x%02x, " 799a0bf528SMauro Carvalho Chehab "ret == %i)\n", __func__, reg, data, ret); 809a0bf528SMauro Carvalho Chehab 819a0bf528SMauro Carvalho Chehab return (ret != 1) ? -EREMOTEIO : 0; 829a0bf528SMauro Carvalho Chehab } 839a0bf528SMauro Carvalho Chehab 84b858c331SIgor M. Liplianin static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 reg) 859a0bf528SMauro Carvalho Chehab { 869a0bf528SMauro Carvalho Chehab int ret; 879a0bf528SMauro Carvalho Chehab u8 b0[] = { reg }; 889a0bf528SMauro Carvalho Chehab u8 b1[] = { 0 }; 89b858c331SIgor M. Liplianin 909a0bf528SMauro Carvalho Chehab struct i2c_msg msg[] = { 919a0bf528SMauro Carvalho Chehab { 92b858c331SIgor M. Liplianin .addr = state->config->demod_addr, 939a0bf528SMauro Carvalho Chehab .flags = 0, 949a0bf528SMauro Carvalho Chehab .buf = b0, 959a0bf528SMauro Carvalho Chehab .len = 1 969a0bf528SMauro Carvalho Chehab }, { 97b858c331SIgor M. Liplianin .addr = state->config->demod_addr, 989a0bf528SMauro Carvalho Chehab .flags = I2C_M_RD, 999a0bf528SMauro Carvalho Chehab .buf = b1, 1009a0bf528SMauro Carvalho Chehab .len = 1 1019a0bf528SMauro Carvalho Chehab } 1029a0bf528SMauro Carvalho Chehab }; 1039a0bf528SMauro Carvalho Chehab 1049a0bf528SMauro Carvalho Chehab ret = i2c_transfer(state->i2c, msg, 2); 1059a0bf528SMauro Carvalho Chehab 1069a0bf528SMauro Carvalho Chehab if (ret != 2) 1079a0bf528SMauro Carvalho Chehab deb_info("%s: readreg error (reg == 0x%02x, ret == %i)\n", 1089a0bf528SMauro Carvalho Chehab __func__, reg, ret); 1099a0bf528SMauro Carvalho Chehab 1109a0bf528SMauro Carvalho Chehab return b1[0]; 1119a0bf528SMauro Carvalho Chehab } 1129a0bf528SMauro Carvalho Chehab 11306af15d1SMalcolm Priestley static u32 m88rs2000_get_mclk(struct dvb_frontend *fe) 11406af15d1SMalcolm Priestley { 11506af15d1SMalcolm Priestley struct m88rs2000_state *state = fe->demodulator_priv; 11606af15d1SMalcolm Priestley u32 mclk; 11706af15d1SMalcolm Priestley u8 reg; 11806af15d1SMalcolm Priestley /* Must not be 0x00 or 0xff */ 11906af15d1SMalcolm Priestley reg = m88rs2000_readreg(state, 0x86); 12006af15d1SMalcolm Priestley if (!reg || reg == 0xff) 12106af15d1SMalcolm Priestley return 0; 12206af15d1SMalcolm Priestley 12306af15d1SMalcolm Priestley reg /= 2; 12406af15d1SMalcolm Priestley reg += 1; 12506af15d1SMalcolm Priestley 12606af15d1SMalcolm Priestley mclk = (u32)(reg * RS2000_FE_CRYSTAL_KHZ + 28 / 2) / 28; 12706af15d1SMalcolm Priestley 12806af15d1SMalcolm Priestley return mclk; 12906af15d1SMalcolm Priestley } 13006af15d1SMalcolm Priestley 13106af15d1SMalcolm Priestley static int m88rs2000_set_carrieroffset(struct dvb_frontend *fe, s16 offset) 13206af15d1SMalcolm Priestley { 13306af15d1SMalcolm Priestley struct m88rs2000_state *state = fe->demodulator_priv; 13406af15d1SMalcolm Priestley u32 mclk; 13506af15d1SMalcolm Priestley s32 tmp; 13606af15d1SMalcolm Priestley u8 reg; 13706af15d1SMalcolm Priestley int ret; 13806af15d1SMalcolm Priestley 13906af15d1SMalcolm Priestley mclk = m88rs2000_get_mclk(fe); 14006af15d1SMalcolm Priestley if (!mclk) 14106af15d1SMalcolm Priestley return -EINVAL; 14206af15d1SMalcolm Priestley 14306af15d1SMalcolm Priestley tmp = (offset * 4096 + (s32)mclk / 2) / (s32)mclk; 14406af15d1SMalcolm Priestley if (tmp < 0) 14506af15d1SMalcolm Priestley tmp += 4096; 14606af15d1SMalcolm Priestley 14706af15d1SMalcolm Priestley /* Carrier Offset */ 14806af15d1SMalcolm Priestley ret = m88rs2000_writereg(state, 0x9c, (u8)(tmp >> 4)); 14906af15d1SMalcolm Priestley 15006af15d1SMalcolm Priestley reg = m88rs2000_readreg(state, 0x9d); 15106af15d1SMalcolm Priestley reg &= 0xf; 15206af15d1SMalcolm Priestley reg |= (u8)(tmp & 0xf) << 4; 15306af15d1SMalcolm Priestley 15406af15d1SMalcolm Priestley ret |= m88rs2000_writereg(state, 0x9d, reg); 15506af15d1SMalcolm Priestley 15606af15d1SMalcolm Priestley return ret; 15706af15d1SMalcolm Priestley } 15806af15d1SMalcolm Priestley 1599a0bf528SMauro Carvalho Chehab static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate) 1609a0bf528SMauro Carvalho Chehab { 1619a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 1629a0bf528SMauro Carvalho Chehab int ret; 163dd4491dfSMalcolm Priestley u64 temp; 164dd4491dfSMalcolm Priestley u32 mclk; 1659a0bf528SMauro Carvalho Chehab u8 b[3]; 1669a0bf528SMauro Carvalho Chehab 1679a0bf528SMauro Carvalho Chehab if ((srate < 1000000) || (srate > 45000000)) 1689a0bf528SMauro Carvalho Chehab return -EINVAL; 1699a0bf528SMauro Carvalho Chehab 170dd4491dfSMalcolm Priestley mclk = m88rs2000_get_mclk(fe); 171dd4491dfSMalcolm Priestley if (!mclk) 172dd4491dfSMalcolm Priestley return -EINVAL; 173dd4491dfSMalcolm Priestley 1749a0bf528SMauro Carvalho Chehab temp = srate / 1000; 175dd4491dfSMalcolm Priestley temp *= 1 << 24; 176dd4491dfSMalcolm Priestley 177dd4491dfSMalcolm Priestley do_div(temp, mclk); 1789a0bf528SMauro Carvalho Chehab 1799a0bf528SMauro Carvalho Chehab b[0] = (u8) (temp >> 16) & 0xff; 1809a0bf528SMauro Carvalho Chehab b[1] = (u8) (temp >> 8) & 0xff; 1819a0bf528SMauro Carvalho Chehab b[2] = (u8) temp & 0xff; 182dd4491dfSMalcolm Priestley 183b858c331SIgor M. Liplianin ret = m88rs2000_writereg(state, 0x93, b[2]); 184b858c331SIgor M. Liplianin ret |= m88rs2000_writereg(state, 0x94, b[1]); 185b858c331SIgor M. Liplianin ret |= m88rs2000_writereg(state, 0x95, b[0]); 1869a0bf528SMauro Carvalho Chehab 187dd4491dfSMalcolm Priestley if (srate > 10000000) 188dd4491dfSMalcolm Priestley ret |= m88rs2000_writereg(state, 0xa0, 0x20); 189dd4491dfSMalcolm Priestley else 190dd4491dfSMalcolm Priestley ret |= m88rs2000_writereg(state, 0xa0, 0x60); 191dd4491dfSMalcolm Priestley 192dd4491dfSMalcolm Priestley ret |= m88rs2000_writereg(state, 0xa1, 0xe0); 193dd4491dfSMalcolm Priestley 194dd4491dfSMalcolm Priestley if (srate > 12000000) 195dd4491dfSMalcolm Priestley ret |= m88rs2000_writereg(state, 0xa3, 0x20); 196dd4491dfSMalcolm Priestley else if (srate > 2800000) 197dd4491dfSMalcolm Priestley ret |= m88rs2000_writereg(state, 0xa3, 0x98); 198dd4491dfSMalcolm Priestley else 199dd4491dfSMalcolm Priestley ret |= m88rs2000_writereg(state, 0xa3, 0x90); 200dd4491dfSMalcolm Priestley 2019a0bf528SMauro Carvalho Chehab deb_info("m88rs2000: m88rs2000_set_symbolrate\n"); 2029a0bf528SMauro Carvalho Chehab return ret; 2039a0bf528SMauro Carvalho Chehab } 2049a0bf528SMauro Carvalho Chehab 2059a0bf528SMauro Carvalho Chehab static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe, 2069a0bf528SMauro Carvalho Chehab struct dvb_diseqc_master_cmd *m) 2079a0bf528SMauro Carvalho Chehab { 2089a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 2099a0bf528SMauro Carvalho Chehab 2109a0bf528SMauro Carvalho Chehab int i; 2119a0bf528SMauro Carvalho Chehab u8 reg; 2129a0bf528SMauro Carvalho Chehab deb_info("%s\n", __func__); 213b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0x30); 214b858c331SIgor M. Liplianin reg = m88rs2000_readreg(state, 0xb2); 2159a0bf528SMauro Carvalho Chehab reg &= 0x3f; 216b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb2, reg); 2179a0bf528SMauro Carvalho Chehab for (i = 0; i < m->msg_len; i++) 218b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb3 + i, m->msg[i]); 2199a0bf528SMauro Carvalho Chehab 220b858c331SIgor M. Liplianin reg = m88rs2000_readreg(state, 0xb1); 2219a0bf528SMauro Carvalho Chehab reg &= 0x87; 2229a0bf528SMauro Carvalho Chehab reg |= ((m->msg_len - 1) << 3) | 0x07; 2239a0bf528SMauro Carvalho Chehab reg &= 0x7f; 224b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb1, reg); 2259a0bf528SMauro Carvalho Chehab 2269a0bf528SMauro Carvalho Chehab for (i = 0; i < 15; i++) { 227b858c331SIgor M. Liplianin if ((m88rs2000_readreg(state, 0xb1) & 0x40) == 0x0) 2289a0bf528SMauro Carvalho Chehab break; 2299a0bf528SMauro Carvalho Chehab msleep(20); 2309a0bf528SMauro Carvalho Chehab } 2319a0bf528SMauro Carvalho Chehab 232b858c331SIgor M. Liplianin reg = m88rs2000_readreg(state, 0xb1); 2339a0bf528SMauro Carvalho Chehab if ((reg & 0x40) > 0x0) { 2349a0bf528SMauro Carvalho Chehab reg &= 0x7f; 2359a0bf528SMauro Carvalho Chehab reg |= 0x40; 236b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb1, reg); 2379a0bf528SMauro Carvalho Chehab } 2389a0bf528SMauro Carvalho Chehab 239b858c331SIgor M. Liplianin reg = m88rs2000_readreg(state, 0xb2); 2409a0bf528SMauro Carvalho Chehab reg &= 0x3f; 2419a0bf528SMauro Carvalho Chehab reg |= 0x80; 242b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb2, reg); 243b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0xb0); 2449a0bf528SMauro Carvalho Chehab 2459a0bf528SMauro Carvalho Chehab 2469a0bf528SMauro Carvalho Chehab return 0; 2479a0bf528SMauro Carvalho Chehab } 2489a0bf528SMauro Carvalho Chehab 2499a0bf528SMauro Carvalho Chehab static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe, 2509a0bf528SMauro Carvalho Chehab fe_sec_mini_cmd_t burst) 2519a0bf528SMauro Carvalho Chehab { 2529a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 2539a0bf528SMauro Carvalho Chehab u8 reg0, reg1; 2549a0bf528SMauro Carvalho Chehab deb_info("%s\n", __func__); 255b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0x30); 2569a0bf528SMauro Carvalho Chehab msleep(50); 257b858c331SIgor M. Liplianin reg0 = m88rs2000_readreg(state, 0xb1); 258b858c331SIgor M. Liplianin reg1 = m88rs2000_readreg(state, 0xb2); 2599a0bf528SMauro Carvalho Chehab /* TODO complete this section */ 260b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb2, reg1); 261b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb1, reg0); 262b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0xb0); 2639a0bf528SMauro Carvalho Chehab 2649a0bf528SMauro Carvalho Chehab return 0; 2659a0bf528SMauro Carvalho Chehab } 2669a0bf528SMauro Carvalho Chehab 2679a0bf528SMauro Carvalho Chehab static int m88rs2000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) 2689a0bf528SMauro Carvalho Chehab { 2699a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 2709a0bf528SMauro Carvalho Chehab u8 reg0, reg1; 271b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0x30); 272b858c331SIgor M. Liplianin reg0 = m88rs2000_readreg(state, 0xb1); 273b858c331SIgor M. Liplianin reg1 = m88rs2000_readreg(state, 0xb2); 2749a0bf528SMauro Carvalho Chehab 2759a0bf528SMauro Carvalho Chehab reg1 &= 0x3f; 2769a0bf528SMauro Carvalho Chehab 2779a0bf528SMauro Carvalho Chehab switch (tone) { 2789a0bf528SMauro Carvalho Chehab case SEC_TONE_ON: 2799a0bf528SMauro Carvalho Chehab reg0 |= 0x4; 2809a0bf528SMauro Carvalho Chehab reg0 &= 0xbc; 2819a0bf528SMauro Carvalho Chehab break; 2829a0bf528SMauro Carvalho Chehab case SEC_TONE_OFF: 2839a0bf528SMauro Carvalho Chehab reg1 |= 0x80; 2849a0bf528SMauro Carvalho Chehab break; 2859a0bf528SMauro Carvalho Chehab default: 2869a0bf528SMauro Carvalho Chehab break; 2879a0bf528SMauro Carvalho Chehab } 288b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb2, reg1); 289b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb1, reg0); 290b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0xb0); 2919a0bf528SMauro Carvalho Chehab return 0; 2929a0bf528SMauro Carvalho Chehab } 2939a0bf528SMauro Carvalho Chehab 2949a0bf528SMauro Carvalho Chehab struct inittab { 2959a0bf528SMauro Carvalho Chehab u8 cmd; 2969a0bf528SMauro Carvalho Chehab u8 reg; 2979a0bf528SMauro Carvalho Chehab u8 val; 2989a0bf528SMauro Carvalho Chehab }; 2999a0bf528SMauro Carvalho Chehab 3009a0bf528SMauro Carvalho Chehab struct inittab m88rs2000_setup[] = { 3019a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9a, 0x30}, 3029a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x00, 0x01}, 3039a0bf528SMauro Carvalho Chehab {WRITE_DELAY, 0x19, 0x00}, 3049a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x00, 0x00}, 3059a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9a, 0xb0}, 3069a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x81, 0xc1}, 3079a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x81, 0x81}, 3089a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x86, 0xc6}, 3099a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9a, 0x30}, 3109a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0xf0, 0x22}, 3119a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0xf1, 0xbf}, 3129a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0xb0, 0x45}, 3139a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0xb2, 0x01}, /* set voltage pin always set 1*/ 3149a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9a, 0xb0}, 3159a0bf528SMauro Carvalho Chehab {0xff, 0xaa, 0xff} 3169a0bf528SMauro Carvalho Chehab }; 3179a0bf528SMauro Carvalho Chehab 3189a0bf528SMauro Carvalho Chehab struct inittab m88rs2000_shutdown[] = { 3199a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9a, 0x30}, 3209a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0xb0, 0x00}, 3219a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0xf1, 0x89}, 3229a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x00, 0x01}, 3239a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9a, 0xb0}, 3249a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x81, 0x81}, 3259a0bf528SMauro Carvalho Chehab {0xff, 0xaa, 0xff} 3269a0bf528SMauro Carvalho Chehab }; 3279a0bf528SMauro Carvalho Chehab 3289a0bf528SMauro Carvalho Chehab struct inittab fe_reset[] = { 3299a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x00, 0x01}, 3309a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x20, 0x81}, 3319a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x21, 0x80}, 3329a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x10, 0x33}, 3339a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x11, 0x44}, 3349a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x12, 0x07}, 3359a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x18, 0x20}, 3369a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x28, 0x04}, 3379a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x29, 0x8e}, 3389a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x3b, 0xff}, 3399a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x32, 0x10}, 3409a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x33, 0x02}, 3419a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x34, 0x30}, 3429a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x35, 0xff}, 3439a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x38, 0x50}, 3449a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x39, 0x68}, 3459a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x3c, 0x7f}, 3469a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x3d, 0x0f}, 3479a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x45, 0x20}, 3489a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x46, 0x24}, 3499a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x47, 0x7c}, 3509a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x48, 0x16}, 3519a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x49, 0x04}, 3529a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x4a, 0x01}, 3539a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x4b, 0x78}, 3549a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0X4d, 0xd2}, 3559a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x4e, 0x6d}, 3569a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x50, 0x30}, 3579a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x51, 0x30}, 3589a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x54, 0x7b}, 3599a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x56, 0x09}, 3609a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x58, 0x59}, 3619a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x59, 0x37}, 3629a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x63, 0xfa}, 3639a0bf528SMauro Carvalho Chehab {0xff, 0xaa, 0xff} 3649a0bf528SMauro Carvalho Chehab }; 3659a0bf528SMauro Carvalho Chehab 3669a0bf528SMauro Carvalho Chehab struct inittab fe_trigger[] = { 3679a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x97, 0x04}, 3689a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x99, 0x77}, 3699a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9b, 0x64}, 3709a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9e, 0x00}, 3719a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9f, 0xf8}, 3729a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x98, 0xff}, 3739a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0xc0, 0x0f}, 3749a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x89, 0x01}, 3759a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x00, 0x00}, 3769a0bf528SMauro Carvalho Chehab {WRITE_DELAY, 0x0a, 0x00}, 3779a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x00, 0x01}, 3789a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x00, 0x00}, 3799a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9a, 0xb0}, 3809a0bf528SMauro Carvalho Chehab {0xff, 0xaa, 0xff} 3819a0bf528SMauro Carvalho Chehab }; 3829a0bf528SMauro Carvalho Chehab 3839a0bf528SMauro Carvalho Chehab static int m88rs2000_tab_set(struct m88rs2000_state *state, 3849a0bf528SMauro Carvalho Chehab struct inittab *tab) 3859a0bf528SMauro Carvalho Chehab { 3869a0bf528SMauro Carvalho Chehab int ret = 0; 3879a0bf528SMauro Carvalho Chehab u8 i; 3889a0bf528SMauro Carvalho Chehab if (tab == NULL) 3899a0bf528SMauro Carvalho Chehab return -EINVAL; 3909a0bf528SMauro Carvalho Chehab 3919a0bf528SMauro Carvalho Chehab for (i = 0; i < 255; i++) { 3929a0bf528SMauro Carvalho Chehab switch (tab[i].cmd) { 3939a0bf528SMauro Carvalho Chehab case 0x01: 394b858c331SIgor M. Liplianin ret = m88rs2000_writereg(state, tab[i].reg, 3959a0bf528SMauro Carvalho Chehab tab[i].val); 3969a0bf528SMauro Carvalho Chehab break; 3979a0bf528SMauro Carvalho Chehab case 0x10: 3989a0bf528SMauro Carvalho Chehab if (tab[i].reg > 0) 3999a0bf528SMauro Carvalho Chehab mdelay(tab[i].reg); 4009a0bf528SMauro Carvalho Chehab break; 4019a0bf528SMauro Carvalho Chehab case 0xff: 4029a0bf528SMauro Carvalho Chehab if (tab[i].reg == 0xaa && tab[i].val == 0xff) 4039a0bf528SMauro Carvalho Chehab return 0; 4049a0bf528SMauro Carvalho Chehab case 0x00: 4059a0bf528SMauro Carvalho Chehab break; 4069a0bf528SMauro Carvalho Chehab default: 4079a0bf528SMauro Carvalho Chehab return -EINVAL; 4089a0bf528SMauro Carvalho Chehab } 4099a0bf528SMauro Carvalho Chehab if (ret < 0) 4109a0bf528SMauro Carvalho Chehab return -ENODEV; 4119a0bf528SMauro Carvalho Chehab } 4129a0bf528SMauro Carvalho Chehab return 0; 4139a0bf528SMauro Carvalho Chehab } 4149a0bf528SMauro Carvalho Chehab 4159a0bf528SMauro Carvalho Chehab static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) 4169a0bf528SMauro Carvalho Chehab { 4179a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 4189a0bf528SMauro Carvalho Chehab u8 data; 4199a0bf528SMauro Carvalho Chehab 420b858c331SIgor M. Liplianin data = m88rs2000_readreg(state, 0xb2); 4219a0bf528SMauro Carvalho Chehab data |= 0x03; /* bit0 V/H, bit1 off/on */ 4229a0bf528SMauro Carvalho Chehab 4239a0bf528SMauro Carvalho Chehab switch (volt) { 4249a0bf528SMauro Carvalho Chehab case SEC_VOLTAGE_18: 4259a0bf528SMauro Carvalho Chehab data &= ~0x03; 4269a0bf528SMauro Carvalho Chehab break; 4279a0bf528SMauro Carvalho Chehab case SEC_VOLTAGE_13: 4289a0bf528SMauro Carvalho Chehab data &= ~0x03; 4299a0bf528SMauro Carvalho Chehab data |= 0x01; 4309a0bf528SMauro Carvalho Chehab break; 4319a0bf528SMauro Carvalho Chehab case SEC_VOLTAGE_OFF: 4329a0bf528SMauro Carvalho Chehab break; 4339a0bf528SMauro Carvalho Chehab } 4349a0bf528SMauro Carvalho Chehab 435b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb2, data); 4369a0bf528SMauro Carvalho Chehab 4379a0bf528SMauro Carvalho Chehab return 0; 4389a0bf528SMauro Carvalho Chehab } 4399a0bf528SMauro Carvalho Chehab 4409a0bf528SMauro Carvalho Chehab static int m88rs2000_init(struct dvb_frontend *fe) 4419a0bf528SMauro Carvalho Chehab { 4429a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 4439a0bf528SMauro Carvalho Chehab int ret; 4449a0bf528SMauro Carvalho Chehab 4459a0bf528SMauro Carvalho Chehab deb_info("m88rs2000: init chip\n"); 4469a0bf528SMauro Carvalho Chehab /* Setup frontend from shutdown/cold */ 447081416e6SIgor M. Liplianin if (state->config->inittab) 448081416e6SIgor M. Liplianin ret = m88rs2000_tab_set(state, 449081416e6SIgor M. Liplianin (struct inittab *)state->config->inittab); 450081416e6SIgor M. Liplianin else 4519a0bf528SMauro Carvalho Chehab ret = m88rs2000_tab_set(state, m88rs2000_setup); 4529a0bf528SMauro Carvalho Chehab 4539a0bf528SMauro Carvalho Chehab return ret; 4549a0bf528SMauro Carvalho Chehab } 4559a0bf528SMauro Carvalho Chehab 4569a0bf528SMauro Carvalho Chehab static int m88rs2000_sleep(struct dvb_frontend *fe) 4579a0bf528SMauro Carvalho Chehab { 4589a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 4599a0bf528SMauro Carvalho Chehab int ret; 4609a0bf528SMauro Carvalho Chehab /* Shutdown the frondend */ 4619a0bf528SMauro Carvalho Chehab ret = m88rs2000_tab_set(state, m88rs2000_shutdown); 4629a0bf528SMauro Carvalho Chehab return ret; 4639a0bf528SMauro Carvalho Chehab } 4649a0bf528SMauro Carvalho Chehab 4659a0bf528SMauro Carvalho Chehab static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status) 4669a0bf528SMauro Carvalho Chehab { 4679a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 468b858c331SIgor M. Liplianin u8 reg = m88rs2000_readreg(state, 0x8c); 4699a0bf528SMauro Carvalho Chehab 4709a0bf528SMauro Carvalho Chehab *status = 0; 4719a0bf528SMauro Carvalho Chehab 4727a9d6b43SMalcolm Priestley if ((reg & 0xee) == 0xee) { 4739a0bf528SMauro Carvalho Chehab *status = FE_HAS_CARRIER | FE_HAS_SIGNAL | FE_HAS_VITERBI 4749a0bf528SMauro Carvalho Chehab | FE_HAS_SYNC | FE_HAS_LOCK; 4759a0bf528SMauro Carvalho Chehab if (state->config->set_ts_params) 4769a0bf528SMauro Carvalho Chehab state->config->set_ts_params(fe, CALL_IS_READ); 4779a0bf528SMauro Carvalho Chehab } 4789a0bf528SMauro Carvalho Chehab return 0; 4799a0bf528SMauro Carvalho Chehab } 4809a0bf528SMauro Carvalho Chehab 4819a0bf528SMauro Carvalho Chehab static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber) 4829a0bf528SMauro Carvalho Chehab { 48338f7889cSIgor M. Liplianin struct m88rs2000_state *state = fe->demodulator_priv; 48438f7889cSIgor M. Liplianin u8 tmp0, tmp1; 48538f7889cSIgor M. Liplianin 486b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0x30); 487b858c331SIgor M. Liplianin tmp0 = m88rs2000_readreg(state, 0xd8); 48838f7889cSIgor M. Liplianin if ((tmp0 & 0x10) != 0) { 489b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0xb0); 49038f7889cSIgor M. Liplianin *ber = 0xffffffff; 49138f7889cSIgor M. Liplianin return 0; 49238f7889cSIgor M. Liplianin } 49338f7889cSIgor M. Liplianin 494b858c331SIgor M. Liplianin *ber = (m88rs2000_readreg(state, 0xd7) << 8) | 495b858c331SIgor M. Liplianin m88rs2000_readreg(state, 0xd6); 49638f7889cSIgor M. Liplianin 497b858c331SIgor M. Liplianin tmp1 = m88rs2000_readreg(state, 0xd9); 498b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xd9, (tmp1 & ~7) | 4); 49938f7889cSIgor M. Liplianin /* needs twice */ 500b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30); 501b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30); 502b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0xb0); 50338f7889cSIgor M. Liplianin 5049a0bf528SMauro Carvalho Chehab return 0; 5059a0bf528SMauro Carvalho Chehab } 5069a0bf528SMauro Carvalho Chehab 5079a0bf528SMauro Carvalho Chehab static int m88rs2000_read_signal_strength(struct dvb_frontend *fe, 5089a0bf528SMauro Carvalho Chehab u16 *strength) 5099a0bf528SMauro Carvalho Chehab { 510a0a030bdSMalcolm Priestley if (fe->ops.tuner_ops.get_rf_strength) 511a0a030bdSMalcolm Priestley fe->ops.tuner_ops.get_rf_strength(fe, strength); 512a0a030bdSMalcolm Priestley 5139a0bf528SMauro Carvalho Chehab return 0; 5149a0bf528SMauro Carvalho Chehab } 5159a0bf528SMauro Carvalho Chehab 5169a0bf528SMauro Carvalho Chehab static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr) 5179a0bf528SMauro Carvalho Chehab { 51838f7889cSIgor M. Liplianin struct m88rs2000_state *state = fe->demodulator_priv; 51938f7889cSIgor M. Liplianin 520b858c331SIgor M. Liplianin *snr = 512 * m88rs2000_readreg(state, 0x65); 52138f7889cSIgor M. Liplianin 5229a0bf528SMauro Carvalho Chehab return 0; 5239a0bf528SMauro Carvalho Chehab } 5249a0bf528SMauro Carvalho Chehab 5259a0bf528SMauro Carvalho Chehab static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) 5269a0bf528SMauro Carvalho Chehab { 52738f7889cSIgor M. Liplianin struct m88rs2000_state *state = fe->demodulator_priv; 52838f7889cSIgor M. Liplianin u8 tmp; 52938f7889cSIgor M. Liplianin 530b858c331SIgor M. Liplianin *ucblocks = (m88rs2000_readreg(state, 0xd5) << 8) | 531b858c331SIgor M. Liplianin m88rs2000_readreg(state, 0xd4); 532b858c331SIgor M. Liplianin tmp = m88rs2000_readreg(state, 0xd8); 533b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xd8, tmp & ~0x20); 53438f7889cSIgor M. Liplianin /* needs two times */ 535b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xd8, tmp | 0x20); 536b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xd8, tmp | 0x20); 53738f7889cSIgor M. Liplianin 5389a0bf528SMauro Carvalho Chehab return 0; 5399a0bf528SMauro Carvalho Chehab } 5409a0bf528SMauro Carvalho Chehab 5419a0bf528SMauro Carvalho Chehab static int m88rs2000_set_fec(struct m88rs2000_state *state, 5429a0bf528SMauro Carvalho Chehab fe_code_rate_t fec) 5439a0bf528SMauro Carvalho Chehab { 5449a0bf528SMauro Carvalho Chehab u16 fec_set; 5459a0bf528SMauro Carvalho Chehab switch (fec) { 5469a0bf528SMauro Carvalho Chehab /* This is not confirmed kept for reference */ 5479a0bf528SMauro Carvalho Chehab /* case FEC_1_2: 5489a0bf528SMauro Carvalho Chehab fec_set = 0x88; 5499a0bf528SMauro Carvalho Chehab break; 5509a0bf528SMauro Carvalho Chehab case FEC_2_3: 5519a0bf528SMauro Carvalho Chehab fec_set = 0x68; 5529a0bf528SMauro Carvalho Chehab break; 5539a0bf528SMauro Carvalho Chehab case FEC_3_4: 5549a0bf528SMauro Carvalho Chehab fec_set = 0x48; 5559a0bf528SMauro Carvalho Chehab break; 5569a0bf528SMauro Carvalho Chehab case FEC_5_6: 5579a0bf528SMauro Carvalho Chehab fec_set = 0x28; 5589a0bf528SMauro Carvalho Chehab break; 5599a0bf528SMauro Carvalho Chehab case FEC_7_8: 5609a0bf528SMauro Carvalho Chehab fec_set = 0x18; 5619a0bf528SMauro Carvalho Chehab break; */ 5629a0bf528SMauro Carvalho Chehab case FEC_AUTO: 5639a0bf528SMauro Carvalho Chehab default: 5649a0bf528SMauro Carvalho Chehab fec_set = 0x08; 5659a0bf528SMauro Carvalho Chehab } 566b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x76, fec_set); 5679a0bf528SMauro Carvalho Chehab 5689a0bf528SMauro Carvalho Chehab return 0; 5699a0bf528SMauro Carvalho Chehab } 5709a0bf528SMauro Carvalho Chehab 5719a0bf528SMauro Carvalho Chehab 5729a0bf528SMauro Carvalho Chehab static fe_code_rate_t m88rs2000_get_fec(struct m88rs2000_state *state) 5739a0bf528SMauro Carvalho Chehab { 5749a0bf528SMauro Carvalho Chehab u8 reg; 575b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0x30); 576b858c331SIgor M. Liplianin reg = m88rs2000_readreg(state, 0x76); 577b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0xb0); 5789a0bf528SMauro Carvalho Chehab 5799a0bf528SMauro Carvalho Chehab switch (reg) { 5809a0bf528SMauro Carvalho Chehab case 0x88: 5819a0bf528SMauro Carvalho Chehab return FEC_1_2; 5829a0bf528SMauro Carvalho Chehab case 0x68: 5839a0bf528SMauro Carvalho Chehab return FEC_2_3; 5849a0bf528SMauro Carvalho Chehab case 0x48: 5859a0bf528SMauro Carvalho Chehab return FEC_3_4; 5869a0bf528SMauro Carvalho Chehab case 0x28: 5879a0bf528SMauro Carvalho Chehab return FEC_5_6; 5889a0bf528SMauro Carvalho Chehab case 0x18: 5899a0bf528SMauro Carvalho Chehab return FEC_7_8; 5909a0bf528SMauro Carvalho Chehab case 0x08: 5919a0bf528SMauro Carvalho Chehab default: 5929a0bf528SMauro Carvalho Chehab break; 5939a0bf528SMauro Carvalho Chehab } 5949a0bf528SMauro Carvalho Chehab 5959a0bf528SMauro Carvalho Chehab return FEC_AUTO; 5969a0bf528SMauro Carvalho Chehab } 5979a0bf528SMauro Carvalho Chehab 5989a0bf528SMauro Carvalho Chehab static int m88rs2000_set_frontend(struct dvb_frontend *fe) 5999a0bf528SMauro Carvalho Chehab { 6009a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 6019a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *c = &fe->dtv_property_cache; 6029a0bf528SMauro Carvalho Chehab fe_status_t status; 603b858c331SIgor M. Liplianin int i, ret = 0; 604b858c331SIgor M. Liplianin u32 tuner_freq; 60506af15d1SMalcolm Priestley s16 offset = 0; 6069a0bf528SMauro Carvalho Chehab u8 reg; 6079a0bf528SMauro Carvalho Chehab 6089a0bf528SMauro Carvalho Chehab state->no_lock_count = 0; 6099a0bf528SMauro Carvalho Chehab 6109a0bf528SMauro Carvalho Chehab if (c->delivery_system != SYS_DVBS) { 6119a0bf528SMauro Carvalho Chehab deb_info("%s: unsupported delivery " 6129a0bf528SMauro Carvalho Chehab "system selected (%d)\n", 6139a0bf528SMauro Carvalho Chehab __func__, c->delivery_system); 6149a0bf528SMauro Carvalho Chehab return -EOPNOTSUPP; 6159a0bf528SMauro Carvalho Chehab } 6169a0bf528SMauro Carvalho Chehab 6179a0bf528SMauro Carvalho Chehab /* Set Tuner */ 618b858c331SIgor M. Liplianin if (fe->ops.tuner_ops.set_params) 619b858c331SIgor M. Liplianin ret = fe->ops.tuner_ops.set_params(fe); 620b858c331SIgor M. Liplianin 6219a0bf528SMauro Carvalho Chehab if (ret < 0) 6229a0bf528SMauro Carvalho Chehab return -ENODEV; 6239a0bf528SMauro Carvalho Chehab 624b858c331SIgor M. Liplianin if (fe->ops.tuner_ops.get_frequency) 625b858c331SIgor M. Liplianin ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_freq); 626b858c331SIgor M. Liplianin 627b858c331SIgor M. Liplianin if (ret < 0) 628b858c331SIgor M. Liplianin return -ENODEV; 629b858c331SIgor M. Liplianin 63006af15d1SMalcolm Priestley offset = (s16)((s32)tuner_freq - c->frequency); 631b858c331SIgor M. Liplianin 63206af15d1SMalcolm Priestley /* default mclk value 96.4285 * 2 * 1000 = 192857 */ 63306af15d1SMalcolm Priestley if (((c->frequency % 192857) >= (192857 - 3000)) || 63406af15d1SMalcolm Priestley (c->frequency % 192857) <= 3000) 63506af15d1SMalcolm Priestley ret = m88rs2000_writereg(state, 0x86, 0xc2); 63606af15d1SMalcolm Priestley else 63706af15d1SMalcolm Priestley ret = m88rs2000_writereg(state, 0x86, 0xc6); 638b858c331SIgor M. Liplianin 63906af15d1SMalcolm Priestley ret |= m88rs2000_set_carrieroffset(fe, offset); 64006af15d1SMalcolm Priestley if (ret < 0) 64106af15d1SMalcolm Priestley return -ENODEV; 6429a0bf528SMauro Carvalho Chehab 643dd4491dfSMalcolm Priestley /* Reset demod by symbol rate */ 644dd4491dfSMalcolm Priestley if (c->symbol_rate > 27500000) 645dd4491dfSMalcolm Priestley ret = m88rs2000_writereg(state, 0xf1, 0xa4); 646dd4491dfSMalcolm Priestley else 647dd4491dfSMalcolm Priestley ret = m88rs2000_writereg(state, 0xf1, 0xbf); 648dd4491dfSMalcolm Priestley 649dd4491dfSMalcolm Priestley ret |= m88rs2000_tab_set(state, fe_reset); 6509a0bf528SMauro Carvalho Chehab if (ret < 0) 6519a0bf528SMauro Carvalho Chehab return -ENODEV; 6529a0bf528SMauro Carvalho Chehab 6539a0bf528SMauro Carvalho Chehab /* Unknown */ 654b858c331SIgor M. Liplianin reg = m88rs2000_readreg(state, 0x70); 655b858c331SIgor M. Liplianin ret = m88rs2000_writereg(state, 0x70, reg); 6569a0bf528SMauro Carvalho Chehab 6579a0bf528SMauro Carvalho Chehab /* Set FEC */ 6589a0bf528SMauro Carvalho Chehab ret |= m88rs2000_set_fec(state, c->fec_inner); 659b858c331SIgor M. Liplianin ret |= m88rs2000_writereg(state, 0x85, 0x1); 660b858c331SIgor M. Liplianin ret |= m88rs2000_writereg(state, 0x8a, 0xbf); 661b858c331SIgor M. Liplianin ret |= m88rs2000_writereg(state, 0x8d, 0x1e); 662b858c331SIgor M. Liplianin ret |= m88rs2000_writereg(state, 0x90, 0xf1); 663b858c331SIgor M. Liplianin ret |= m88rs2000_writereg(state, 0x91, 0x08); 6649a0bf528SMauro Carvalho Chehab 6659a0bf528SMauro Carvalho Chehab if (ret < 0) 6669a0bf528SMauro Carvalho Chehab return -ENODEV; 6679a0bf528SMauro Carvalho Chehab 6689a0bf528SMauro Carvalho Chehab /* Set Symbol Rate */ 6699a0bf528SMauro Carvalho Chehab ret = m88rs2000_set_symbolrate(fe, c->symbol_rate); 6709a0bf528SMauro Carvalho Chehab if (ret < 0) 6719a0bf528SMauro Carvalho Chehab return -ENODEV; 6729a0bf528SMauro Carvalho Chehab 6739a0bf528SMauro Carvalho Chehab /* Set up Demod */ 6749a0bf528SMauro Carvalho Chehab ret = m88rs2000_tab_set(state, fe_trigger); 6759a0bf528SMauro Carvalho Chehab if (ret < 0) 6769a0bf528SMauro Carvalho Chehab return -ENODEV; 6779a0bf528SMauro Carvalho Chehab 6789a0bf528SMauro Carvalho Chehab for (i = 0; i < 25; i++) { 679b858c331SIgor M. Liplianin reg = m88rs2000_readreg(state, 0x8c); 6807a9d6b43SMalcolm Priestley if ((reg & 0xee) == 0xee) { 6819a0bf528SMauro Carvalho Chehab status = FE_HAS_LOCK; 6829a0bf528SMauro Carvalho Chehab break; 6839a0bf528SMauro Carvalho Chehab } 6849a0bf528SMauro Carvalho Chehab state->no_lock_count++; 6859a0bf528SMauro Carvalho Chehab if (state->no_lock_count == 15) { 686b858c331SIgor M. Liplianin reg = m88rs2000_readreg(state, 0x70); 6879a0bf528SMauro Carvalho Chehab reg ^= 0x4; 688b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x70, reg); 6899a0bf528SMauro Carvalho Chehab state->no_lock_count = 0; 6909a0bf528SMauro Carvalho Chehab } 6919a0bf528SMauro Carvalho Chehab msleep(20); 6929a0bf528SMauro Carvalho Chehab } 6939a0bf528SMauro Carvalho Chehab 6949a0bf528SMauro Carvalho Chehab if (status & FE_HAS_LOCK) { 6959a0bf528SMauro Carvalho Chehab state->fec_inner = m88rs2000_get_fec(state); 6969a0bf528SMauro Carvalho Chehab /* Uknown suspect SNR level */ 697b858c331SIgor M. Liplianin reg = m88rs2000_readreg(state, 0x65); 6989a0bf528SMauro Carvalho Chehab } 6999a0bf528SMauro Carvalho Chehab 7009a0bf528SMauro Carvalho Chehab state->tuner_frequency = c->frequency; 7019a0bf528SMauro Carvalho Chehab state->symbol_rate = c->symbol_rate; 7029a0bf528SMauro Carvalho Chehab return 0; 7039a0bf528SMauro Carvalho Chehab } 7049a0bf528SMauro Carvalho Chehab 7059a0bf528SMauro Carvalho Chehab static int m88rs2000_get_frontend(struct dvb_frontend *fe) 7069a0bf528SMauro Carvalho Chehab { 7079a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *c = &fe->dtv_property_cache; 7089a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 7099a0bf528SMauro Carvalho Chehab c->fec_inner = state->fec_inner; 7109a0bf528SMauro Carvalho Chehab c->frequency = state->tuner_frequency; 7119a0bf528SMauro Carvalho Chehab c->symbol_rate = state->symbol_rate; 7129a0bf528SMauro Carvalho Chehab return 0; 7139a0bf528SMauro Carvalho Chehab } 7149a0bf528SMauro Carvalho Chehab 7159a0bf528SMauro Carvalho Chehab static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) 7169a0bf528SMauro Carvalho Chehab { 7179a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 7189a0bf528SMauro Carvalho Chehab 7199a0bf528SMauro Carvalho Chehab if (enable) 720b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x81, 0x84); 7219a0bf528SMauro Carvalho Chehab else 722b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x81, 0x81); 7239a0bf528SMauro Carvalho Chehab udelay(10); 7249a0bf528SMauro Carvalho Chehab return 0; 7259a0bf528SMauro Carvalho Chehab } 7269a0bf528SMauro Carvalho Chehab 7279a0bf528SMauro Carvalho Chehab static void m88rs2000_release(struct dvb_frontend *fe) 7289a0bf528SMauro Carvalho Chehab { 7299a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 7309a0bf528SMauro Carvalho Chehab kfree(state); 7319a0bf528SMauro Carvalho Chehab } 7329a0bf528SMauro Carvalho Chehab 7339a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops m88rs2000_ops = { 7349a0bf528SMauro Carvalho Chehab .delsys = { SYS_DVBS }, 7359a0bf528SMauro Carvalho Chehab .info = { 7369a0bf528SMauro Carvalho Chehab .name = "M88RS2000 DVB-S", 7379a0bf528SMauro Carvalho Chehab .frequency_min = 950000, 7389a0bf528SMauro Carvalho Chehab .frequency_max = 2150000, 7399a0bf528SMauro Carvalho Chehab .frequency_stepsize = 1000, /* kHz for QPSK frontends */ 7409a0bf528SMauro Carvalho Chehab .frequency_tolerance = 5000, 7419a0bf528SMauro Carvalho Chehab .symbol_rate_min = 1000000, 7429a0bf528SMauro Carvalho Chehab .symbol_rate_max = 45000000, 7439a0bf528SMauro Carvalho Chehab .symbol_rate_tolerance = 500, /* ppm */ 7449a0bf528SMauro Carvalho Chehab .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 7459a0bf528SMauro Carvalho Chehab FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | 7469a0bf528SMauro Carvalho Chehab FE_CAN_QPSK | 7479a0bf528SMauro Carvalho Chehab FE_CAN_FEC_AUTO 7489a0bf528SMauro Carvalho Chehab }, 7499a0bf528SMauro Carvalho Chehab 7509a0bf528SMauro Carvalho Chehab .release = m88rs2000_release, 7519a0bf528SMauro Carvalho Chehab .init = m88rs2000_init, 7529a0bf528SMauro Carvalho Chehab .sleep = m88rs2000_sleep, 7539a0bf528SMauro Carvalho Chehab .i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl, 7549a0bf528SMauro Carvalho Chehab .read_status = m88rs2000_read_status, 7559a0bf528SMauro Carvalho Chehab .read_ber = m88rs2000_read_ber, 7569a0bf528SMauro Carvalho Chehab .read_signal_strength = m88rs2000_read_signal_strength, 7579a0bf528SMauro Carvalho Chehab .read_snr = m88rs2000_read_snr, 7589a0bf528SMauro Carvalho Chehab .read_ucblocks = m88rs2000_read_ucblocks, 7599a0bf528SMauro Carvalho Chehab .diseqc_send_master_cmd = m88rs2000_send_diseqc_msg, 7609a0bf528SMauro Carvalho Chehab .diseqc_send_burst = m88rs2000_send_diseqc_burst, 7619a0bf528SMauro Carvalho Chehab .set_tone = m88rs2000_set_tone, 7629a0bf528SMauro Carvalho Chehab .set_voltage = m88rs2000_set_voltage, 7639a0bf528SMauro Carvalho Chehab 7649a0bf528SMauro Carvalho Chehab .set_frontend = m88rs2000_set_frontend, 7659a0bf528SMauro Carvalho Chehab .get_frontend = m88rs2000_get_frontend, 7669a0bf528SMauro Carvalho Chehab }; 7679a0bf528SMauro Carvalho Chehab 7689a0bf528SMauro Carvalho Chehab struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config, 7699a0bf528SMauro Carvalho Chehab struct i2c_adapter *i2c) 7709a0bf528SMauro Carvalho Chehab { 7719a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = NULL; 7729a0bf528SMauro Carvalho Chehab 7739a0bf528SMauro Carvalho Chehab /* allocate memory for the internal state */ 7749a0bf528SMauro Carvalho Chehab state = kzalloc(sizeof(struct m88rs2000_state), GFP_KERNEL); 7759a0bf528SMauro Carvalho Chehab if (state == NULL) 7769a0bf528SMauro Carvalho Chehab goto error; 7779a0bf528SMauro Carvalho Chehab 7789a0bf528SMauro Carvalho Chehab /* setup the state */ 7799a0bf528SMauro Carvalho Chehab state->config = config; 7809a0bf528SMauro Carvalho Chehab state->i2c = i2c; 7819a0bf528SMauro Carvalho Chehab state->tuner_frequency = 0; 7829a0bf528SMauro Carvalho Chehab state->symbol_rate = 0; 7839a0bf528SMauro Carvalho Chehab state->fec_inner = 0; 7849a0bf528SMauro Carvalho Chehab 7859a0bf528SMauro Carvalho Chehab /* create dvb_frontend */ 7869a0bf528SMauro Carvalho Chehab memcpy(&state->frontend.ops, &m88rs2000_ops, 7879a0bf528SMauro Carvalho Chehab sizeof(struct dvb_frontend_ops)); 7889a0bf528SMauro Carvalho Chehab state->frontend.demodulator_priv = state; 7899a0bf528SMauro Carvalho Chehab return &state->frontend; 7909a0bf528SMauro Carvalho Chehab 7919a0bf528SMauro Carvalho Chehab error: 7929a0bf528SMauro Carvalho Chehab kfree(state); 7939a0bf528SMauro Carvalho Chehab 7949a0bf528SMauro Carvalho Chehab return NULL; 7959a0bf528SMauro Carvalho Chehab } 7969a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(m88rs2000_attach); 7979a0bf528SMauro Carvalho Chehab 7989a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("M88RS2000 DVB-S Demodulator driver"); 7999a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com"); 8009a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 8019a0bf528SMauro Carvalho Chehab MODULE_VERSION("1.13"); 8029a0bf528SMauro Carvalho Chehab 803