174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 29a0bf528SMauro Carvalho Chehab /* 39a0bf528SMauro Carvalho Chehab Driver for M88RS2000 demodulator and tuner 49a0bf528SMauro Carvalho Chehab 59a0bf528SMauro Carvalho Chehab Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com) 69a0bf528SMauro Carvalho Chehab Beta Driver 79a0bf528SMauro Carvalho Chehab 89a0bf528SMauro Carvalho Chehab Include various calculation code from DS3000 driver. 99a0bf528SMauro Carvalho Chehab Copyright (C) 2009 Konstantin Dimitrov. 109a0bf528SMauro Carvalho Chehab 119a0bf528SMauro Carvalho Chehab 129a0bf528SMauro Carvalho Chehab */ 139a0bf528SMauro Carvalho Chehab #include <linux/init.h> 149a0bf528SMauro Carvalho Chehab #include <linux/module.h> 159a0bf528SMauro Carvalho Chehab #include <linux/device.h> 169a0bf528SMauro Carvalho Chehab #include <linux/jiffies.h> 179a0bf528SMauro Carvalho Chehab #include <linux/string.h> 189a0bf528SMauro Carvalho Chehab #include <linux/slab.h> 199a0bf528SMauro Carvalho Chehab #include <linux/types.h> 209a0bf528SMauro Carvalho Chehab 219a0bf528SMauro Carvalho Chehab 22fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h> 239a0bf528SMauro Carvalho Chehab #include "m88rs2000.h" 249a0bf528SMauro Carvalho Chehab 259a0bf528SMauro Carvalho Chehab struct m88rs2000_state { 269a0bf528SMauro Carvalho Chehab struct i2c_adapter *i2c; 279a0bf528SMauro Carvalho Chehab const struct m88rs2000_config *config; 289a0bf528SMauro Carvalho Chehab struct dvb_frontend frontend; 299a0bf528SMauro Carvalho Chehab u8 no_lock_count; 309a0bf528SMauro Carvalho Chehab u32 tuner_frequency; 319a0bf528SMauro Carvalho Chehab u32 symbol_rate; 320df289a2SMauro Carvalho Chehab enum fe_code_rate fec_inner; 339a0bf528SMauro Carvalho Chehab u8 tuner_level; 349a0bf528SMauro Carvalho Chehab int errmode; 359a0bf528SMauro Carvalho Chehab }; 369a0bf528SMauro Carvalho Chehab 379a0bf528SMauro Carvalho Chehab static int m88rs2000_debug; 389a0bf528SMauro Carvalho Chehab 399a0bf528SMauro Carvalho Chehab module_param_named(debug, m88rs2000_debug, int, 0644); 409a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); 419a0bf528SMauro Carvalho Chehab 429a0bf528SMauro Carvalho Chehab #define dprintk(level, args...) do { \ 439a0bf528SMauro Carvalho Chehab if (level & m88rs2000_debug) \ 449a0bf528SMauro Carvalho Chehab printk(KERN_DEBUG "m88rs2000-fe: " args); \ 459a0bf528SMauro Carvalho Chehab } while (0) 469a0bf528SMauro Carvalho Chehab 479a0bf528SMauro Carvalho Chehab #define deb_info(args...) dprintk(0x01, args) 489a0bf528SMauro Carvalho Chehab #define info(format, arg...) \ 499a0bf528SMauro Carvalho Chehab printk(KERN_INFO "m88rs2000-fe: " format "\n" , ## arg) 509a0bf528SMauro Carvalho Chehab 51b858c331SIgor M. Liplianin static int m88rs2000_writereg(struct m88rs2000_state *state, 529a0bf528SMauro Carvalho Chehab u8 reg, u8 data) 539a0bf528SMauro Carvalho Chehab { 549a0bf528SMauro Carvalho Chehab int ret; 559a0bf528SMauro Carvalho Chehab u8 buf[] = { reg, data }; 569a0bf528SMauro Carvalho Chehab struct i2c_msg msg = { 57b858c331SIgor M. Liplianin .addr = state->config->demod_addr, 589a0bf528SMauro Carvalho Chehab .flags = 0, 599a0bf528SMauro Carvalho Chehab .buf = buf, 609a0bf528SMauro Carvalho Chehab .len = 2 619a0bf528SMauro Carvalho Chehab }; 629a0bf528SMauro Carvalho Chehab 639a0bf528SMauro Carvalho Chehab ret = i2c_transfer(state->i2c, &msg, 1); 649a0bf528SMauro Carvalho Chehab 659a0bf528SMauro Carvalho Chehab if (ret != 1) 664bd69e7bSMauro Carvalho Chehab deb_info("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", 674bd69e7bSMauro Carvalho Chehab __func__, reg, data, ret); 689a0bf528SMauro Carvalho Chehab 699a0bf528SMauro Carvalho Chehab return (ret != 1) ? -EREMOTEIO : 0; 709a0bf528SMauro Carvalho Chehab } 719a0bf528SMauro Carvalho Chehab 72b858c331SIgor M. Liplianin static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 reg) 739a0bf528SMauro Carvalho Chehab { 749a0bf528SMauro Carvalho Chehab int ret; 759a0bf528SMauro Carvalho Chehab u8 b0[] = { reg }; 769a0bf528SMauro Carvalho Chehab u8 b1[] = { 0 }; 77b858c331SIgor M. Liplianin 789a0bf528SMauro Carvalho Chehab struct i2c_msg msg[] = { 799a0bf528SMauro Carvalho Chehab { 80b858c331SIgor M. Liplianin .addr = state->config->demod_addr, 819a0bf528SMauro Carvalho Chehab .flags = 0, 829a0bf528SMauro Carvalho Chehab .buf = b0, 839a0bf528SMauro Carvalho Chehab .len = 1 849a0bf528SMauro Carvalho Chehab }, { 85b858c331SIgor M. Liplianin .addr = state->config->demod_addr, 869a0bf528SMauro Carvalho Chehab .flags = I2C_M_RD, 879a0bf528SMauro Carvalho Chehab .buf = b1, 889a0bf528SMauro Carvalho Chehab .len = 1 899a0bf528SMauro Carvalho Chehab } 909a0bf528SMauro Carvalho Chehab }; 919a0bf528SMauro Carvalho Chehab 929a0bf528SMauro Carvalho Chehab ret = i2c_transfer(state->i2c, msg, 2); 939a0bf528SMauro Carvalho Chehab 949a0bf528SMauro Carvalho Chehab if (ret != 2) 959a0bf528SMauro Carvalho Chehab deb_info("%s: readreg error (reg == 0x%02x, ret == %i)\n", 969a0bf528SMauro Carvalho Chehab __func__, reg, ret); 979a0bf528SMauro Carvalho Chehab 989a0bf528SMauro Carvalho Chehab return b1[0]; 999a0bf528SMauro Carvalho Chehab } 1009a0bf528SMauro Carvalho Chehab 10106af15d1SMalcolm Priestley static u32 m88rs2000_get_mclk(struct dvb_frontend *fe) 10206af15d1SMalcolm Priestley { 10306af15d1SMalcolm Priestley struct m88rs2000_state *state = fe->demodulator_priv; 10406af15d1SMalcolm Priestley u32 mclk; 10506af15d1SMalcolm Priestley u8 reg; 10606af15d1SMalcolm Priestley /* Must not be 0x00 or 0xff */ 10706af15d1SMalcolm Priestley reg = m88rs2000_readreg(state, 0x86); 10806af15d1SMalcolm Priestley if (!reg || reg == 0xff) 10906af15d1SMalcolm Priestley return 0; 11006af15d1SMalcolm Priestley 11106af15d1SMalcolm Priestley reg /= 2; 11206af15d1SMalcolm Priestley reg += 1; 11306af15d1SMalcolm Priestley 11406af15d1SMalcolm Priestley mclk = (u32)(reg * RS2000_FE_CRYSTAL_KHZ + 28 / 2) / 28; 11506af15d1SMalcolm Priestley 11606af15d1SMalcolm Priestley return mclk; 11706af15d1SMalcolm Priestley } 11806af15d1SMalcolm Priestley 11906af15d1SMalcolm Priestley static int m88rs2000_set_carrieroffset(struct dvb_frontend *fe, s16 offset) 12006af15d1SMalcolm Priestley { 12106af15d1SMalcolm Priestley struct m88rs2000_state *state = fe->demodulator_priv; 12206af15d1SMalcolm Priestley u32 mclk; 12306af15d1SMalcolm Priestley s32 tmp; 12406af15d1SMalcolm Priestley u8 reg; 12506af15d1SMalcolm Priestley int ret; 12606af15d1SMalcolm Priestley 12706af15d1SMalcolm Priestley mclk = m88rs2000_get_mclk(fe); 12806af15d1SMalcolm Priestley if (!mclk) 12906af15d1SMalcolm Priestley return -EINVAL; 13006af15d1SMalcolm Priestley 13106af15d1SMalcolm Priestley tmp = (offset * 4096 + (s32)mclk / 2) / (s32)mclk; 13206af15d1SMalcolm Priestley if (tmp < 0) 13306af15d1SMalcolm Priestley tmp += 4096; 13406af15d1SMalcolm Priestley 13506af15d1SMalcolm Priestley /* Carrier Offset */ 13606af15d1SMalcolm Priestley ret = m88rs2000_writereg(state, 0x9c, (u8)(tmp >> 4)); 13706af15d1SMalcolm Priestley 13806af15d1SMalcolm Priestley reg = m88rs2000_readreg(state, 0x9d); 13906af15d1SMalcolm Priestley reg &= 0xf; 14006af15d1SMalcolm Priestley reg |= (u8)(tmp & 0xf) << 4; 14106af15d1SMalcolm Priestley 14206af15d1SMalcolm Priestley ret |= m88rs2000_writereg(state, 0x9d, reg); 14306af15d1SMalcolm Priestley 14406af15d1SMalcolm Priestley return ret; 14506af15d1SMalcolm Priestley } 14606af15d1SMalcolm Priestley 1479a0bf528SMauro Carvalho Chehab static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate) 1489a0bf528SMauro Carvalho Chehab { 1499a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 1509a0bf528SMauro Carvalho Chehab int ret; 151dd4491dfSMalcolm Priestley u64 temp; 152dd4491dfSMalcolm Priestley u32 mclk; 1539a0bf528SMauro Carvalho Chehab u8 b[3]; 1549a0bf528SMauro Carvalho Chehab 1559a0bf528SMauro Carvalho Chehab if ((srate < 1000000) || (srate > 45000000)) 1569a0bf528SMauro Carvalho Chehab return -EINVAL; 1579a0bf528SMauro Carvalho Chehab 158dd4491dfSMalcolm Priestley mclk = m88rs2000_get_mclk(fe); 159dd4491dfSMalcolm Priestley if (!mclk) 160dd4491dfSMalcolm Priestley return -EINVAL; 161dd4491dfSMalcolm Priestley 1629a0bf528SMauro Carvalho Chehab temp = srate / 1000; 163dd4491dfSMalcolm Priestley temp *= 1 << 24; 164dd4491dfSMalcolm Priestley 165dd4491dfSMalcolm Priestley do_div(temp, mclk); 1669a0bf528SMauro Carvalho Chehab 1679a0bf528SMauro Carvalho Chehab b[0] = (u8) (temp >> 16) & 0xff; 1689a0bf528SMauro Carvalho Chehab b[1] = (u8) (temp >> 8) & 0xff; 1699a0bf528SMauro Carvalho Chehab b[2] = (u8) temp & 0xff; 170dd4491dfSMalcolm Priestley 171b858c331SIgor M. Liplianin ret = m88rs2000_writereg(state, 0x93, b[2]); 172b858c331SIgor M. Liplianin ret |= m88rs2000_writereg(state, 0x94, b[1]); 173b858c331SIgor M. Liplianin ret |= m88rs2000_writereg(state, 0x95, b[0]); 1749a0bf528SMauro Carvalho Chehab 175dd4491dfSMalcolm Priestley if (srate > 10000000) 176dd4491dfSMalcolm Priestley ret |= m88rs2000_writereg(state, 0xa0, 0x20); 177dd4491dfSMalcolm Priestley else 178dd4491dfSMalcolm Priestley ret |= m88rs2000_writereg(state, 0xa0, 0x60); 179dd4491dfSMalcolm Priestley 180dd4491dfSMalcolm Priestley ret |= m88rs2000_writereg(state, 0xa1, 0xe0); 181dd4491dfSMalcolm Priestley 182dd4491dfSMalcolm Priestley if (srate > 12000000) 183dd4491dfSMalcolm Priestley ret |= m88rs2000_writereg(state, 0xa3, 0x20); 184dd4491dfSMalcolm Priestley else if (srate > 2800000) 185dd4491dfSMalcolm Priestley ret |= m88rs2000_writereg(state, 0xa3, 0x98); 186dd4491dfSMalcolm Priestley else 187dd4491dfSMalcolm Priestley ret |= m88rs2000_writereg(state, 0xa3, 0x90); 188dd4491dfSMalcolm Priestley 1899a0bf528SMauro Carvalho Chehab deb_info("m88rs2000: m88rs2000_set_symbolrate\n"); 1909a0bf528SMauro Carvalho Chehab return ret; 1919a0bf528SMauro Carvalho Chehab } 1929a0bf528SMauro Carvalho Chehab 1939a0bf528SMauro Carvalho Chehab static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe, 1949a0bf528SMauro Carvalho Chehab struct dvb_diseqc_master_cmd *m) 1959a0bf528SMauro Carvalho Chehab { 1969a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 1979a0bf528SMauro Carvalho Chehab 1989a0bf528SMauro Carvalho Chehab int i; 1999a0bf528SMauro Carvalho Chehab u8 reg; 2009a0bf528SMauro Carvalho Chehab deb_info("%s\n", __func__); 201b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0x30); 202b858c331SIgor M. Liplianin reg = m88rs2000_readreg(state, 0xb2); 2039a0bf528SMauro Carvalho Chehab reg &= 0x3f; 204b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb2, reg); 2059a0bf528SMauro Carvalho Chehab for (i = 0; i < m->msg_len; i++) 206b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb3 + i, m->msg[i]); 2079a0bf528SMauro Carvalho Chehab 208b858c331SIgor M. Liplianin reg = m88rs2000_readreg(state, 0xb1); 2099a0bf528SMauro Carvalho Chehab reg &= 0x87; 2109a0bf528SMauro Carvalho Chehab reg |= ((m->msg_len - 1) << 3) | 0x07; 2119a0bf528SMauro Carvalho Chehab reg &= 0x7f; 212b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb1, reg); 2139a0bf528SMauro Carvalho Chehab 2149a0bf528SMauro Carvalho Chehab for (i = 0; i < 15; i++) { 215b858c331SIgor M. Liplianin if ((m88rs2000_readreg(state, 0xb1) & 0x40) == 0x0) 2169a0bf528SMauro Carvalho Chehab break; 2179a0bf528SMauro Carvalho Chehab msleep(20); 2189a0bf528SMauro Carvalho Chehab } 2199a0bf528SMauro Carvalho Chehab 220b858c331SIgor M. Liplianin reg = m88rs2000_readreg(state, 0xb1); 2219a0bf528SMauro Carvalho Chehab if ((reg & 0x40) > 0x0) { 2229a0bf528SMauro Carvalho Chehab reg &= 0x7f; 2239a0bf528SMauro Carvalho Chehab reg |= 0x40; 224b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb1, reg); 2259a0bf528SMauro Carvalho Chehab } 2269a0bf528SMauro Carvalho Chehab 227b858c331SIgor M. Liplianin reg = m88rs2000_readreg(state, 0xb2); 2289a0bf528SMauro Carvalho Chehab reg &= 0x3f; 2299a0bf528SMauro Carvalho Chehab reg |= 0x80; 230b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb2, reg); 231b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0xb0); 2329a0bf528SMauro Carvalho Chehab 2339a0bf528SMauro Carvalho Chehab 2349a0bf528SMauro Carvalho Chehab return 0; 2359a0bf528SMauro Carvalho Chehab } 2369a0bf528SMauro Carvalho Chehab 2379a0bf528SMauro Carvalho Chehab static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe, 2380df289a2SMauro Carvalho Chehab enum fe_sec_mini_cmd burst) 2399a0bf528SMauro Carvalho Chehab { 2409a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 2419a0bf528SMauro Carvalho Chehab u8 reg0, reg1; 2429a0bf528SMauro Carvalho Chehab deb_info("%s\n", __func__); 243b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0x30); 2449a0bf528SMauro Carvalho Chehab msleep(50); 245b858c331SIgor M. Liplianin reg0 = m88rs2000_readreg(state, 0xb1); 246b858c331SIgor M. Liplianin reg1 = m88rs2000_readreg(state, 0xb2); 2479a0bf528SMauro Carvalho Chehab /* TODO complete this section */ 248b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb2, reg1); 249b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb1, reg0); 250b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0xb0); 2519a0bf528SMauro Carvalho Chehab 2529a0bf528SMauro Carvalho Chehab return 0; 2539a0bf528SMauro Carvalho Chehab } 2549a0bf528SMauro Carvalho Chehab 2550df289a2SMauro Carvalho Chehab static int m88rs2000_set_tone(struct dvb_frontend *fe, 2560df289a2SMauro Carvalho Chehab enum fe_sec_tone_mode tone) 2579a0bf528SMauro Carvalho Chehab { 2589a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 2599a0bf528SMauro Carvalho Chehab u8 reg0, reg1; 260b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0x30); 261b858c331SIgor M. Liplianin reg0 = m88rs2000_readreg(state, 0xb1); 262b858c331SIgor M. Liplianin reg1 = m88rs2000_readreg(state, 0xb2); 2639a0bf528SMauro Carvalho Chehab 2649a0bf528SMauro Carvalho Chehab reg1 &= 0x3f; 2659a0bf528SMauro Carvalho Chehab 2669a0bf528SMauro Carvalho Chehab switch (tone) { 2679a0bf528SMauro Carvalho Chehab case SEC_TONE_ON: 2689a0bf528SMauro Carvalho Chehab reg0 |= 0x4; 2699a0bf528SMauro Carvalho Chehab reg0 &= 0xbc; 2709a0bf528SMauro Carvalho Chehab break; 2719a0bf528SMauro Carvalho Chehab case SEC_TONE_OFF: 2729a0bf528SMauro Carvalho Chehab reg1 |= 0x80; 2739a0bf528SMauro Carvalho Chehab break; 2749a0bf528SMauro Carvalho Chehab default: 2759a0bf528SMauro Carvalho Chehab break; 2769a0bf528SMauro Carvalho Chehab } 277b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb2, reg1); 278b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb1, reg0); 279b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0xb0); 2809a0bf528SMauro Carvalho Chehab return 0; 2819a0bf528SMauro Carvalho Chehab } 2829a0bf528SMauro Carvalho Chehab 2839a0bf528SMauro Carvalho Chehab struct inittab { 2849a0bf528SMauro Carvalho Chehab u8 cmd; 2859a0bf528SMauro Carvalho Chehab u8 reg; 2869a0bf528SMauro Carvalho Chehab u8 val; 2879a0bf528SMauro Carvalho Chehab }; 2889a0bf528SMauro Carvalho Chehab 2897d3c8e8fSMalcolm Priestley static struct inittab m88rs2000_setup[] = { 2909a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9a, 0x30}, 2919a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x00, 0x01}, 2929a0bf528SMauro Carvalho Chehab {WRITE_DELAY, 0x19, 0x00}, 2939a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x00, 0x00}, 2949a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9a, 0xb0}, 2959a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x81, 0xc1}, 2969a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x81, 0x81}, 2979a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x86, 0xc6}, 2989a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9a, 0x30}, 2999a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0xf0, 0x22}, 3009a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0xf1, 0xbf}, 3019a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0xb0, 0x45}, 3029a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0xb2, 0x01}, /* set voltage pin always set 1*/ 3039a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9a, 0xb0}, 3049a0bf528SMauro Carvalho Chehab {0xff, 0xaa, 0xff} 3059a0bf528SMauro Carvalho Chehab }; 3069a0bf528SMauro Carvalho Chehab 3077d3c8e8fSMalcolm Priestley static struct inittab m88rs2000_shutdown[] = { 3089a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9a, 0x30}, 3099a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0xb0, 0x00}, 3109a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0xf1, 0x89}, 3119a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x00, 0x01}, 3129a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9a, 0xb0}, 3139a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x81, 0x81}, 3149a0bf528SMauro Carvalho Chehab {0xff, 0xaa, 0xff} 3159a0bf528SMauro Carvalho Chehab }; 3169a0bf528SMauro Carvalho Chehab 3177d3c8e8fSMalcolm Priestley static struct inittab fe_reset[] = { 3189a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x00, 0x01}, 3199a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x20, 0x81}, 3209a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x21, 0x80}, 3219a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x10, 0x33}, 3229a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x11, 0x44}, 3239a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x12, 0x07}, 3249a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x18, 0x20}, 3259a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x28, 0x04}, 3269a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x29, 0x8e}, 3279a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x3b, 0xff}, 3289a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x32, 0x10}, 3299a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x33, 0x02}, 3309a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x34, 0x30}, 3319a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x35, 0xff}, 3329a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x38, 0x50}, 3339a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x39, 0x68}, 3349a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x3c, 0x7f}, 3359a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x3d, 0x0f}, 3369a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x45, 0x20}, 3379a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x46, 0x24}, 3389a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x47, 0x7c}, 3399a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x48, 0x16}, 3409a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x49, 0x04}, 3419a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x4a, 0x01}, 3429a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x4b, 0x78}, 3439a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0X4d, 0xd2}, 3449a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x4e, 0x6d}, 3459a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x50, 0x30}, 3469a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x51, 0x30}, 3479a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x54, 0x7b}, 3489a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x56, 0x09}, 3499a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x58, 0x59}, 3509a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x59, 0x37}, 3519a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x63, 0xfa}, 3529a0bf528SMauro Carvalho Chehab {0xff, 0xaa, 0xff} 3539a0bf528SMauro Carvalho Chehab }; 3549a0bf528SMauro Carvalho Chehab 3557d3c8e8fSMalcolm Priestley static struct inittab fe_trigger[] = { 3569a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x97, 0x04}, 3579a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x99, 0x77}, 3589a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9b, 0x64}, 3599a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9e, 0x00}, 3609a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9f, 0xf8}, 3619a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x98, 0xff}, 3629a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0xc0, 0x0f}, 3639a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x89, 0x01}, 3649a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x00, 0x00}, 3659a0bf528SMauro Carvalho Chehab {WRITE_DELAY, 0x0a, 0x00}, 3669a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x00, 0x01}, 3679a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x00, 0x00}, 3689a0bf528SMauro Carvalho Chehab {DEMOD_WRITE, 0x9a, 0xb0}, 3699a0bf528SMauro Carvalho Chehab {0xff, 0xaa, 0xff} 3709a0bf528SMauro Carvalho Chehab }; 3719a0bf528SMauro Carvalho Chehab 3729a0bf528SMauro Carvalho Chehab static int m88rs2000_tab_set(struct m88rs2000_state *state, 3739a0bf528SMauro Carvalho Chehab struct inittab *tab) 3749a0bf528SMauro Carvalho Chehab { 3759a0bf528SMauro Carvalho Chehab int ret = 0; 3769a0bf528SMauro Carvalho Chehab u8 i; 3779a0bf528SMauro Carvalho Chehab if (tab == NULL) 3789a0bf528SMauro Carvalho Chehab return -EINVAL; 3799a0bf528SMauro Carvalho Chehab 3809a0bf528SMauro Carvalho Chehab for (i = 0; i < 255; i++) { 3819a0bf528SMauro Carvalho Chehab switch (tab[i].cmd) { 3829a0bf528SMauro Carvalho Chehab case 0x01: 383b858c331SIgor M. Liplianin ret = m88rs2000_writereg(state, tab[i].reg, 3849a0bf528SMauro Carvalho Chehab tab[i].val); 3859a0bf528SMauro Carvalho Chehab break; 3869a0bf528SMauro Carvalho Chehab case 0x10: 3879a0bf528SMauro Carvalho Chehab if (tab[i].reg > 0) 3889a0bf528SMauro Carvalho Chehab mdelay(tab[i].reg); 3899a0bf528SMauro Carvalho Chehab break; 3909a0bf528SMauro Carvalho Chehab case 0xff: 3919a0bf528SMauro Carvalho Chehab if (tab[i].reg == 0xaa && tab[i].val == 0xff) 3929a0bf528SMauro Carvalho Chehab return 0; 3939a0bf528SMauro Carvalho Chehab case 0x00: 3949a0bf528SMauro Carvalho Chehab break; 3959a0bf528SMauro Carvalho Chehab default: 3969a0bf528SMauro Carvalho Chehab return -EINVAL; 3979a0bf528SMauro Carvalho Chehab } 3989a0bf528SMauro Carvalho Chehab if (ret < 0) 3999a0bf528SMauro Carvalho Chehab return -ENODEV; 4009a0bf528SMauro Carvalho Chehab } 4019a0bf528SMauro Carvalho Chehab return 0; 4029a0bf528SMauro Carvalho Chehab } 4039a0bf528SMauro Carvalho Chehab 4040df289a2SMauro Carvalho Chehab static int m88rs2000_set_voltage(struct dvb_frontend *fe, 4050df289a2SMauro Carvalho Chehab enum fe_sec_voltage volt) 4069a0bf528SMauro Carvalho Chehab { 4079a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 4089a0bf528SMauro Carvalho Chehab u8 data; 4099a0bf528SMauro Carvalho Chehab 410b858c331SIgor M. Liplianin data = m88rs2000_readreg(state, 0xb2); 4119a0bf528SMauro Carvalho Chehab data |= 0x03; /* bit0 V/H, bit1 off/on */ 4129a0bf528SMauro Carvalho Chehab 4139a0bf528SMauro Carvalho Chehab switch (volt) { 4149a0bf528SMauro Carvalho Chehab case SEC_VOLTAGE_18: 4159a0bf528SMauro Carvalho Chehab data &= ~0x03; 4169a0bf528SMauro Carvalho Chehab break; 4179a0bf528SMauro Carvalho Chehab case SEC_VOLTAGE_13: 4189a0bf528SMauro Carvalho Chehab data &= ~0x03; 4199a0bf528SMauro Carvalho Chehab data |= 0x01; 4209a0bf528SMauro Carvalho Chehab break; 4219a0bf528SMauro Carvalho Chehab case SEC_VOLTAGE_OFF: 4229a0bf528SMauro Carvalho Chehab break; 4239a0bf528SMauro Carvalho Chehab } 4249a0bf528SMauro Carvalho Chehab 425b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xb2, data); 4269a0bf528SMauro Carvalho Chehab 4279a0bf528SMauro Carvalho Chehab return 0; 4289a0bf528SMauro Carvalho Chehab } 4299a0bf528SMauro Carvalho Chehab 4309a0bf528SMauro Carvalho Chehab static int m88rs2000_init(struct dvb_frontend *fe) 4319a0bf528SMauro Carvalho Chehab { 4329a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 4339a0bf528SMauro Carvalho Chehab int ret; 4349a0bf528SMauro Carvalho Chehab 4359a0bf528SMauro Carvalho Chehab deb_info("m88rs2000: init chip\n"); 4369a0bf528SMauro Carvalho Chehab /* Setup frontend from shutdown/cold */ 437081416e6SIgor M. Liplianin if (state->config->inittab) 438081416e6SIgor M. Liplianin ret = m88rs2000_tab_set(state, 439081416e6SIgor M. Liplianin (struct inittab *)state->config->inittab); 440081416e6SIgor M. Liplianin else 4419a0bf528SMauro Carvalho Chehab ret = m88rs2000_tab_set(state, m88rs2000_setup); 4429a0bf528SMauro Carvalho Chehab 4439a0bf528SMauro Carvalho Chehab return ret; 4449a0bf528SMauro Carvalho Chehab } 4459a0bf528SMauro Carvalho Chehab 4469a0bf528SMauro Carvalho Chehab static int m88rs2000_sleep(struct dvb_frontend *fe) 4479a0bf528SMauro Carvalho Chehab { 4489a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 4499a0bf528SMauro Carvalho Chehab int ret; 4509a0bf528SMauro Carvalho Chehab /* Shutdown the frondend */ 4519a0bf528SMauro Carvalho Chehab ret = m88rs2000_tab_set(state, m88rs2000_shutdown); 4529a0bf528SMauro Carvalho Chehab return ret; 4539a0bf528SMauro Carvalho Chehab } 4549a0bf528SMauro Carvalho Chehab 4550df289a2SMauro Carvalho Chehab static int m88rs2000_read_status(struct dvb_frontend *fe, 4560df289a2SMauro Carvalho Chehab enum fe_status *status) 4579a0bf528SMauro Carvalho Chehab { 4589a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 459b858c331SIgor M. Liplianin u8 reg = m88rs2000_readreg(state, 0x8c); 4609a0bf528SMauro Carvalho Chehab 4619a0bf528SMauro Carvalho Chehab *status = 0; 4629a0bf528SMauro Carvalho Chehab 4637a9d6b43SMalcolm Priestley if ((reg & 0xee) == 0xee) { 4649a0bf528SMauro Carvalho Chehab *status = FE_HAS_CARRIER | FE_HAS_SIGNAL | FE_HAS_VITERBI 4659a0bf528SMauro Carvalho Chehab | FE_HAS_SYNC | FE_HAS_LOCK; 4669a0bf528SMauro Carvalho Chehab if (state->config->set_ts_params) 4679a0bf528SMauro Carvalho Chehab state->config->set_ts_params(fe, CALL_IS_READ); 4689a0bf528SMauro Carvalho Chehab } 4699a0bf528SMauro Carvalho Chehab return 0; 4709a0bf528SMauro Carvalho Chehab } 4719a0bf528SMauro Carvalho Chehab 4729a0bf528SMauro Carvalho Chehab static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber) 4739a0bf528SMauro Carvalho Chehab { 47438f7889cSIgor M. Liplianin struct m88rs2000_state *state = fe->demodulator_priv; 47538f7889cSIgor M. Liplianin u8 tmp0, tmp1; 47638f7889cSIgor M. Liplianin 477b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0x30); 478b858c331SIgor M. Liplianin tmp0 = m88rs2000_readreg(state, 0xd8); 47938f7889cSIgor M. Liplianin if ((tmp0 & 0x10) != 0) { 480b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0xb0); 48138f7889cSIgor M. Liplianin *ber = 0xffffffff; 48238f7889cSIgor M. Liplianin return 0; 48338f7889cSIgor M. Liplianin } 48438f7889cSIgor M. Liplianin 485b858c331SIgor M. Liplianin *ber = (m88rs2000_readreg(state, 0xd7) << 8) | 486b858c331SIgor M. Liplianin m88rs2000_readreg(state, 0xd6); 48738f7889cSIgor M. Liplianin 488b858c331SIgor M. Liplianin tmp1 = m88rs2000_readreg(state, 0xd9); 489b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xd9, (tmp1 & ~7) | 4); 49038f7889cSIgor M. Liplianin /* needs twice */ 491b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30); 492b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30); 493b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0xb0); 49438f7889cSIgor M. Liplianin 4959a0bf528SMauro Carvalho Chehab return 0; 4969a0bf528SMauro Carvalho Chehab } 4979a0bf528SMauro Carvalho Chehab 4989a0bf528SMauro Carvalho Chehab static int m88rs2000_read_signal_strength(struct dvb_frontend *fe, 4999a0bf528SMauro Carvalho Chehab u16 *strength) 5009a0bf528SMauro Carvalho Chehab { 501a0a030bdSMalcolm Priestley if (fe->ops.tuner_ops.get_rf_strength) 502a0a030bdSMalcolm Priestley fe->ops.tuner_ops.get_rf_strength(fe, strength); 503a0a030bdSMalcolm Priestley 5049a0bf528SMauro Carvalho Chehab return 0; 5059a0bf528SMauro Carvalho Chehab } 5069a0bf528SMauro Carvalho Chehab 5079a0bf528SMauro Carvalho Chehab static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr) 5089a0bf528SMauro Carvalho Chehab { 50938f7889cSIgor M. Liplianin struct m88rs2000_state *state = fe->demodulator_priv; 51038f7889cSIgor M. Liplianin 511b858c331SIgor M. Liplianin *snr = 512 * m88rs2000_readreg(state, 0x65); 51238f7889cSIgor M. Liplianin 5139a0bf528SMauro Carvalho Chehab return 0; 5149a0bf528SMauro Carvalho Chehab } 5159a0bf528SMauro Carvalho Chehab 5169a0bf528SMauro Carvalho Chehab static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) 5179a0bf528SMauro Carvalho Chehab { 51838f7889cSIgor M. Liplianin struct m88rs2000_state *state = fe->demodulator_priv; 51938f7889cSIgor M. Liplianin u8 tmp; 52038f7889cSIgor M. Liplianin 521b858c331SIgor M. Liplianin *ucblocks = (m88rs2000_readreg(state, 0xd5) << 8) | 522b858c331SIgor M. Liplianin m88rs2000_readreg(state, 0xd4); 523b858c331SIgor M. Liplianin tmp = m88rs2000_readreg(state, 0xd8); 524b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xd8, tmp & ~0x20); 52538f7889cSIgor M. Liplianin /* needs two times */ 526b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xd8, tmp | 0x20); 527b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0xd8, tmp | 0x20); 52838f7889cSIgor M. Liplianin 5299a0bf528SMauro Carvalho Chehab return 0; 5309a0bf528SMauro Carvalho Chehab } 5319a0bf528SMauro Carvalho Chehab 5329a0bf528SMauro Carvalho Chehab static int m88rs2000_set_fec(struct m88rs2000_state *state, 5330df289a2SMauro Carvalho Chehab enum fe_code_rate fec) 5349a0bf528SMauro Carvalho Chehab { 53549c44802SMalcolm Priestley u8 fec_set, reg; 53649c44802SMalcolm Priestley int ret; 53749c44802SMalcolm Priestley 5389a0bf528SMauro Carvalho Chehab switch (fec) { 53949c44802SMalcolm Priestley case FEC_1_2: 54049c44802SMalcolm Priestley fec_set = 0x8; 5419a0bf528SMauro Carvalho Chehab break; 5429a0bf528SMauro Carvalho Chehab case FEC_2_3: 54349c44802SMalcolm Priestley fec_set = 0x10; 5449a0bf528SMauro Carvalho Chehab break; 5459a0bf528SMauro Carvalho Chehab case FEC_3_4: 54649c44802SMalcolm Priestley fec_set = 0x20; 5479a0bf528SMauro Carvalho Chehab break; 5489a0bf528SMauro Carvalho Chehab case FEC_5_6: 54949c44802SMalcolm Priestley fec_set = 0x40; 5509a0bf528SMauro Carvalho Chehab break; 5519a0bf528SMauro Carvalho Chehab case FEC_7_8: 55249c44802SMalcolm Priestley fec_set = 0x80; 55349c44802SMalcolm Priestley break; 5549a0bf528SMauro Carvalho Chehab case FEC_AUTO: 5559a0bf528SMauro Carvalho Chehab default: 55649c44802SMalcolm Priestley fec_set = 0x0; 5579a0bf528SMauro Carvalho Chehab } 5589a0bf528SMauro Carvalho Chehab 55949c44802SMalcolm Priestley reg = m88rs2000_readreg(state, 0x70); 56049c44802SMalcolm Priestley reg &= 0x7; 56149c44802SMalcolm Priestley ret = m88rs2000_writereg(state, 0x70, reg | fec_set); 56249c44802SMalcolm Priestley 56349c44802SMalcolm Priestley ret |= m88rs2000_writereg(state, 0x76, 0x8); 56449c44802SMalcolm Priestley 56549c44802SMalcolm Priestley return ret; 56649c44802SMalcolm Priestley } 5679a0bf528SMauro Carvalho Chehab 5680df289a2SMauro Carvalho Chehab static enum fe_code_rate m88rs2000_get_fec(struct m88rs2000_state *state) 5699a0bf528SMauro Carvalho Chehab { 5709a0bf528SMauro Carvalho Chehab u8 reg; 571b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0x30); 572b858c331SIgor M. Liplianin reg = m88rs2000_readreg(state, 0x76); 573b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x9a, 0xb0); 5749a0bf528SMauro Carvalho Chehab 575a6d8e68bSMalcolm Priestley reg &= 0xf0; 576a6d8e68bSMalcolm Priestley reg >>= 5; 577a6d8e68bSMalcolm Priestley 5789a0bf528SMauro Carvalho Chehab switch (reg) { 579a6d8e68bSMalcolm Priestley case 0x4: 5809a0bf528SMauro Carvalho Chehab return FEC_1_2; 581a6d8e68bSMalcolm Priestley case 0x3: 5829a0bf528SMauro Carvalho Chehab return FEC_2_3; 583a6d8e68bSMalcolm Priestley case 0x2: 5849a0bf528SMauro Carvalho Chehab return FEC_3_4; 585a6d8e68bSMalcolm Priestley case 0x1: 5869a0bf528SMauro Carvalho Chehab return FEC_5_6; 587a6d8e68bSMalcolm Priestley case 0x0: 5889a0bf528SMauro Carvalho Chehab return FEC_7_8; 5899a0bf528SMauro Carvalho Chehab default: 5909a0bf528SMauro Carvalho Chehab break; 5919a0bf528SMauro Carvalho Chehab } 5929a0bf528SMauro Carvalho Chehab 5939a0bf528SMauro Carvalho Chehab return FEC_AUTO; 5949a0bf528SMauro Carvalho Chehab } 5959a0bf528SMauro Carvalho Chehab 5969a0bf528SMauro Carvalho Chehab static int m88rs2000_set_frontend(struct dvb_frontend *fe) 5979a0bf528SMauro Carvalho Chehab { 5989a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 5999a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *c = &fe->dtv_property_cache; 600cac1c639SColin Ian King enum fe_status status = 0; 601b858c331SIgor M. Liplianin int i, ret = 0; 602b858c331SIgor M. Liplianin u32 tuner_freq; 60306af15d1SMalcolm Priestley s16 offset = 0; 6049a0bf528SMauro Carvalho Chehab u8 reg; 6059a0bf528SMauro Carvalho Chehab 6069a0bf528SMauro Carvalho Chehab state->no_lock_count = 0; 6079a0bf528SMauro Carvalho Chehab 6089a0bf528SMauro Carvalho Chehab if (c->delivery_system != SYS_DVBS) { 6094bd69e7bSMauro Carvalho Chehab deb_info("%s: unsupported delivery system selected (%d)\n", 6109a0bf528SMauro Carvalho Chehab __func__, c->delivery_system); 6119a0bf528SMauro Carvalho Chehab return -EOPNOTSUPP; 6129a0bf528SMauro Carvalho Chehab } 6139a0bf528SMauro Carvalho Chehab 6149a0bf528SMauro Carvalho Chehab /* Set Tuner */ 615b858c331SIgor M. Liplianin if (fe->ops.tuner_ops.set_params) 616b858c331SIgor M. Liplianin ret = fe->ops.tuner_ops.set_params(fe); 617b858c331SIgor M. Liplianin 6189a0bf528SMauro Carvalho Chehab if (ret < 0) 6199a0bf528SMauro Carvalho Chehab return -ENODEV; 6209a0bf528SMauro Carvalho Chehab 62174a6799cSMauro Carvalho Chehab if (fe->ops.tuner_ops.get_frequency) { 622b858c331SIgor M. Liplianin ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_freq); 623b858c331SIgor M. Liplianin 624b858c331SIgor M. Liplianin if (ret < 0) 625b858c331SIgor M. Liplianin return -ENODEV; 626b858c331SIgor M. Liplianin 62706af15d1SMalcolm Priestley offset = (s16)((s32)tuner_freq - c->frequency); 62874a6799cSMauro Carvalho Chehab } else { 62974a6799cSMauro Carvalho Chehab offset = 0; 63074a6799cSMauro Carvalho Chehab } 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 /* Set FEC */ 65449c44802SMalcolm Priestley ret = m88rs2000_set_fec(state, c->fec_inner); 655b858c331SIgor M. Liplianin ret |= m88rs2000_writereg(state, 0x85, 0x1); 656b858c331SIgor M. Liplianin ret |= m88rs2000_writereg(state, 0x8a, 0xbf); 657b858c331SIgor M. Liplianin ret |= m88rs2000_writereg(state, 0x8d, 0x1e); 658b858c331SIgor M. Liplianin ret |= m88rs2000_writereg(state, 0x90, 0xf1); 659b858c331SIgor M. Liplianin ret |= m88rs2000_writereg(state, 0x91, 0x08); 6609a0bf528SMauro Carvalho Chehab 6619a0bf528SMauro Carvalho Chehab if (ret < 0) 6629a0bf528SMauro Carvalho Chehab return -ENODEV; 6639a0bf528SMauro Carvalho Chehab 6649a0bf528SMauro Carvalho Chehab /* Set Symbol Rate */ 6659a0bf528SMauro Carvalho Chehab ret = m88rs2000_set_symbolrate(fe, c->symbol_rate); 6669a0bf528SMauro Carvalho Chehab if (ret < 0) 6679a0bf528SMauro Carvalho Chehab return -ENODEV; 6689a0bf528SMauro Carvalho Chehab 6699a0bf528SMauro Carvalho Chehab /* Set up Demod */ 6709a0bf528SMauro Carvalho Chehab ret = m88rs2000_tab_set(state, fe_trigger); 6719a0bf528SMauro Carvalho Chehab if (ret < 0) 6729a0bf528SMauro Carvalho Chehab return -ENODEV; 6739a0bf528SMauro Carvalho Chehab 6749a0bf528SMauro Carvalho Chehab for (i = 0; i < 25; i++) { 675b858c331SIgor M. Liplianin reg = m88rs2000_readreg(state, 0x8c); 6767a9d6b43SMalcolm Priestley if ((reg & 0xee) == 0xee) { 6779a0bf528SMauro Carvalho Chehab status = FE_HAS_LOCK; 6789a0bf528SMauro Carvalho Chehab break; 6799a0bf528SMauro Carvalho Chehab } 6809a0bf528SMauro Carvalho Chehab state->no_lock_count++; 6819a0bf528SMauro Carvalho Chehab if (state->no_lock_count == 15) { 682b858c331SIgor M. Liplianin reg = m88rs2000_readreg(state, 0x70); 6839a0bf528SMauro Carvalho Chehab reg ^= 0x4; 684b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x70, reg); 6859a0bf528SMauro Carvalho Chehab state->no_lock_count = 0; 6869a0bf528SMauro Carvalho Chehab } 6879a0bf528SMauro Carvalho Chehab msleep(20); 6889a0bf528SMauro Carvalho Chehab } 6899a0bf528SMauro Carvalho Chehab 6909a0bf528SMauro Carvalho Chehab if (status & FE_HAS_LOCK) { 6919a0bf528SMauro Carvalho Chehab state->fec_inner = m88rs2000_get_fec(state); 692868c9a17SMauro Carvalho Chehab /* Unknown suspect SNR level */ 693b858c331SIgor M. Liplianin reg = m88rs2000_readreg(state, 0x65); 6949a0bf528SMauro Carvalho Chehab } 6959a0bf528SMauro Carvalho Chehab 6969a0bf528SMauro Carvalho Chehab state->tuner_frequency = c->frequency; 6979a0bf528SMauro Carvalho Chehab state->symbol_rate = c->symbol_rate; 6989a0bf528SMauro Carvalho Chehab return 0; 6999a0bf528SMauro Carvalho Chehab } 7009a0bf528SMauro Carvalho Chehab 7017e3e68bcSMauro Carvalho Chehab static int m88rs2000_get_frontend(struct dvb_frontend *fe, 7027e3e68bcSMauro Carvalho Chehab struct dtv_frontend_properties *c) 7039a0bf528SMauro Carvalho Chehab { 7049a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 7057e3e68bcSMauro Carvalho Chehab 7069a0bf528SMauro Carvalho Chehab c->fec_inner = state->fec_inner; 7079a0bf528SMauro Carvalho Chehab c->frequency = state->tuner_frequency; 7089a0bf528SMauro Carvalho Chehab c->symbol_rate = state->symbol_rate; 7099a0bf528SMauro Carvalho Chehab return 0; 7109a0bf528SMauro Carvalho Chehab } 7119a0bf528SMauro Carvalho Chehab 7128272d0a0SMalcolm Priestley static int m88rs2000_get_tune_settings(struct dvb_frontend *fe, 7138272d0a0SMalcolm Priestley struct dvb_frontend_tune_settings *tune) 7148272d0a0SMalcolm Priestley { 7158272d0a0SMalcolm Priestley struct dtv_frontend_properties *c = &fe->dtv_property_cache; 7168272d0a0SMalcolm Priestley 7178272d0a0SMalcolm Priestley if (c->symbol_rate > 3000000) 7188272d0a0SMalcolm Priestley tune->min_delay_ms = 2000; 7198272d0a0SMalcolm Priestley else 7208272d0a0SMalcolm Priestley tune->min_delay_ms = 3000; 7218272d0a0SMalcolm Priestley 7228272d0a0SMalcolm Priestley tune->step_size = c->symbol_rate / 16000; 7238272d0a0SMalcolm Priestley tune->max_drift = c->symbol_rate / 2000; 7248272d0a0SMalcolm Priestley 7258272d0a0SMalcolm Priestley return 0; 7268272d0a0SMalcolm Priestley } 7278272d0a0SMalcolm Priestley 7289a0bf528SMauro Carvalho Chehab static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) 7299a0bf528SMauro Carvalho Chehab { 7309a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 7319a0bf528SMauro Carvalho Chehab 7329a0bf528SMauro Carvalho Chehab if (enable) 733b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x81, 0x84); 7349a0bf528SMauro Carvalho Chehab else 735b858c331SIgor M. Liplianin m88rs2000_writereg(state, 0x81, 0x81); 7369a0bf528SMauro Carvalho Chehab udelay(10); 7379a0bf528SMauro Carvalho Chehab return 0; 7389a0bf528SMauro Carvalho Chehab } 7399a0bf528SMauro Carvalho Chehab 7409a0bf528SMauro Carvalho Chehab static void m88rs2000_release(struct dvb_frontend *fe) 7419a0bf528SMauro Carvalho Chehab { 7429a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = fe->demodulator_priv; 7439a0bf528SMauro Carvalho Chehab kfree(state); 7449a0bf528SMauro Carvalho Chehab } 7459a0bf528SMauro Carvalho Chehab 746bd336e63SMax Kellermann static const struct dvb_frontend_ops m88rs2000_ops = { 7479a0bf528SMauro Carvalho Chehab .delsys = { SYS_DVBS }, 7489a0bf528SMauro Carvalho Chehab .info = { 7499a0bf528SMauro Carvalho Chehab .name = "M88RS2000 DVB-S", 750f1b1eabfSMauro Carvalho Chehab .frequency_min_hz = 950 * MHz, 751f1b1eabfSMauro Carvalho Chehab .frequency_max_hz = 2150 * MHz, 752f1b1eabfSMauro Carvalho Chehab .frequency_stepsize_hz = 1 * MHz, 753f1b1eabfSMauro Carvalho Chehab .frequency_tolerance_hz = 5 * MHz, 7549a0bf528SMauro Carvalho Chehab .symbol_rate_min = 1000000, 7559a0bf528SMauro Carvalho Chehab .symbol_rate_max = 45000000, 7569a0bf528SMauro Carvalho Chehab .symbol_rate_tolerance = 500, /* ppm */ 7579a0bf528SMauro Carvalho Chehab .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 7589a0bf528SMauro Carvalho Chehab FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | 7593c8023a7SMalcolm Priestley FE_CAN_QPSK | FE_CAN_INVERSION_AUTO | 7609a0bf528SMauro Carvalho Chehab FE_CAN_FEC_AUTO 7619a0bf528SMauro Carvalho Chehab }, 7629a0bf528SMauro Carvalho Chehab 7639a0bf528SMauro Carvalho Chehab .release = m88rs2000_release, 7649a0bf528SMauro Carvalho Chehab .init = m88rs2000_init, 7659a0bf528SMauro Carvalho Chehab .sleep = m88rs2000_sleep, 7669a0bf528SMauro Carvalho Chehab .i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl, 7679a0bf528SMauro Carvalho Chehab .read_status = m88rs2000_read_status, 7689a0bf528SMauro Carvalho Chehab .read_ber = m88rs2000_read_ber, 7699a0bf528SMauro Carvalho Chehab .read_signal_strength = m88rs2000_read_signal_strength, 7709a0bf528SMauro Carvalho Chehab .read_snr = m88rs2000_read_snr, 7719a0bf528SMauro Carvalho Chehab .read_ucblocks = m88rs2000_read_ucblocks, 7729a0bf528SMauro Carvalho Chehab .diseqc_send_master_cmd = m88rs2000_send_diseqc_msg, 7739a0bf528SMauro Carvalho Chehab .diseqc_send_burst = m88rs2000_send_diseqc_burst, 7749a0bf528SMauro Carvalho Chehab .set_tone = m88rs2000_set_tone, 7759a0bf528SMauro Carvalho Chehab .set_voltage = m88rs2000_set_voltage, 7769a0bf528SMauro Carvalho Chehab 7779a0bf528SMauro Carvalho Chehab .set_frontend = m88rs2000_set_frontend, 7789a0bf528SMauro Carvalho Chehab .get_frontend = m88rs2000_get_frontend, 7798272d0a0SMalcolm Priestley .get_tune_settings = m88rs2000_get_tune_settings, 7809a0bf528SMauro Carvalho Chehab }; 7819a0bf528SMauro Carvalho Chehab 7829a0bf528SMauro Carvalho Chehab struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config, 7839a0bf528SMauro Carvalho Chehab struct i2c_adapter *i2c) 7849a0bf528SMauro Carvalho Chehab { 7859a0bf528SMauro Carvalho Chehab struct m88rs2000_state *state = NULL; 7869a0bf528SMauro Carvalho Chehab 7879a0bf528SMauro Carvalho Chehab /* allocate memory for the internal state */ 7889a0bf528SMauro Carvalho Chehab state = kzalloc(sizeof(struct m88rs2000_state), GFP_KERNEL); 7899a0bf528SMauro Carvalho Chehab if (state == NULL) 7909a0bf528SMauro Carvalho Chehab goto error; 7919a0bf528SMauro Carvalho Chehab 7929a0bf528SMauro Carvalho Chehab /* setup the state */ 7939a0bf528SMauro Carvalho Chehab state->config = config; 7949a0bf528SMauro Carvalho Chehab state->i2c = i2c; 7959a0bf528SMauro Carvalho Chehab state->tuner_frequency = 0; 7969a0bf528SMauro Carvalho Chehab state->symbol_rate = 0; 7979a0bf528SMauro Carvalho Chehab state->fec_inner = 0; 7989a0bf528SMauro Carvalho Chehab 7999a0bf528SMauro Carvalho Chehab /* create dvb_frontend */ 8009a0bf528SMauro Carvalho Chehab memcpy(&state->frontend.ops, &m88rs2000_ops, 8019a0bf528SMauro Carvalho Chehab sizeof(struct dvb_frontend_ops)); 8029a0bf528SMauro Carvalho Chehab state->frontend.demodulator_priv = state; 8039a0bf528SMauro Carvalho Chehab return &state->frontend; 8049a0bf528SMauro Carvalho Chehab 8059a0bf528SMauro Carvalho Chehab error: 8069a0bf528SMauro Carvalho Chehab kfree(state); 8079a0bf528SMauro Carvalho Chehab 8089a0bf528SMauro Carvalho Chehab return NULL; 8099a0bf528SMauro Carvalho Chehab } 8109a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(m88rs2000_attach); 8119a0bf528SMauro Carvalho Chehab 8129a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("M88RS2000 DVB-S Demodulator driver"); 8139a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com"); 8149a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 8159a0bf528SMauro Carvalho Chehab MODULE_VERSION("1.13"); 8169a0bf528SMauro Carvalho Chehab 817