1*74ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 20d46748cSSteven Toth /* 30d46748cSSteven Toth Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver 40d46748cSSteven Toth 50d46748cSSteven Toth Copyright (C) 2006-2008 Steven Toth <stoth@hauppauge.com> 6490c8684SDarron Broad Copyright (C) 2006-2007 Georg Acher 7490c8684SDarron Broad Copyright (C) 2007-2008 Darron Broad 8490c8684SDarron Broad March 2007 9490c8684SDarron Broad Fixed some bugs. 10490c8684SDarron Broad Added diseqc support. 11490c8684SDarron Broad Added corrected signal strength support. 12490c8684SDarron Broad August 2007 13490c8684SDarron Broad Sync with legacy version. 14490c8684SDarron Broad Some clean ups. 15490c8684SDarron Broad Copyright (C) 2008 Igor Liplianin 16490c8684SDarron Broad September, 9th 2008 17490c8684SDarron Broad Fixed locking on high symbol rates (>30000). 18c063a489SIgor M. Liplianin Implement MPEG initialization parameter. 19c7bdcd0fSIgor M. Liplianin January, 17th 2009 20c7bdcd0fSIgor M. Liplianin Fill set_voltage with actually control voltage code. 21c7bdcd0fSIgor M. Liplianin Correct set tone to not affect voltage. 220d46748cSSteven Toth 230d46748cSSteven Toth */ 240d46748cSSteven Toth 250d46748cSSteven Toth #include <linux/slab.h> 260d46748cSSteven Toth #include <linux/kernel.h> 270d46748cSSteven Toth #include <linux/module.h> 280d46748cSSteven Toth #include <linux/moduleparam.h> 290d46748cSSteven Toth #include <linux/init.h> 300d46748cSSteven Toth #include <linux/firmware.h> 310d46748cSSteven Toth 32fada1935SMauro Carvalho Chehab #include <media/dvb_frontend.h> 330d46748cSSteven Toth #include "cx24116.h" 340d46748cSSteven Toth 35f11ec7d4SSteven Toth static int debug; 36f11ec7d4SSteven Toth module_param(debug, int, 0644); 37f11ec7d4SSteven Toth MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); 38f11ec7d4SSteven Toth 39490c8684SDarron Broad #define dprintk(args...) \ 40490c8684SDarron Broad do { \ 41f11ec7d4SSteven Toth if (debug) \ 4298c94823SSteven Toth printk(KERN_INFO "cx24116: " args); \ 43490c8684SDarron Broad } while (0) 44490c8684SDarron Broad 450d46748cSSteven Toth #define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw" 460d46748cSSteven Toth #define CX24116_SEARCH_RANGE_KHZ 5000 470d46748cSSteven Toth 48490c8684SDarron Broad /* known registers */ 49490c8684SDarron Broad #define CX24116_REG_COMMAND (0x00) /* command args 0x00..0x1e */ 50490c8684SDarron Broad #define CX24116_REG_EXECUTE (0x1f) /* execute command */ 51490c8684SDarron Broad #define CX24116_REG_MAILBOX (0x96) /* FW or multipurpose mailbox? */ 52490c8684SDarron Broad #define CX24116_REG_RESET (0x20) /* reset status > 0 */ 53490c8684SDarron Broad #define CX24116_REG_SIGNAL (0x9e) /* signal low */ 54490c8684SDarron Broad #define CX24116_REG_SSTATUS (0x9d) /* signal high / status */ 558953db79SSteven Toth #define CX24116_REG_QUALITY8 (0xa3) 56490c8684SDarron Broad #define CX24116_REG_QSTATUS (0xbc) 578953db79SSteven Toth #define CX24116_REG_QUALITY0 (0xd5) 58490c8684SDarron Broad #define CX24116_REG_BER0 (0xc9) 59490c8684SDarron Broad #define CX24116_REG_BER8 (0xc8) 60490c8684SDarron Broad #define CX24116_REG_BER16 (0xc7) 61490c8684SDarron Broad #define CX24116_REG_BER24 (0xc6) 62490c8684SDarron Broad #define CX24116_REG_UCB0 (0xcb) 63490c8684SDarron Broad #define CX24116_REG_UCB8 (0xca) 64490c8684SDarron Broad #define CX24116_REG_CLKDIV (0xf3) 65490c8684SDarron Broad #define CX24116_REG_RATEDIV (0xf9) 6698c94823SSteven Toth 6798c94823SSteven Toth /* configured fec (not tuned) or actual FEC (tuned) 1=1/2 2=2/3 etc */ 6898c94823SSteven Toth #define CX24116_REG_FECSTATUS (0x9c) 69681faa0bSDarron Broad 70681faa0bSDarron Broad /* FECSTATUS bits */ 7198c94823SSteven Toth /* mask to determine configured fec (not tuned) or actual fec (tuned) */ 7298c94823SSteven Toth #define CX24116_FEC_FECMASK (0x1f) 7398c94823SSteven Toth 7498c94823SSteven Toth /* Select DVB-S demodulator, else DVB-S2 */ 7598c94823SSteven Toth #define CX24116_FEC_DVBS (0x20) 76681faa0bSDarron Broad #define CX24116_FEC_UNKNOWN (0x40) /* Unknown/unused */ 7798c94823SSteven Toth 7898c94823SSteven Toth /* Pilot mode requested when tuning else always reset when tuned */ 7998c94823SSteven Toth #define CX24116_FEC_PILOT (0x80) 800d46748cSSteven Toth 810d46748cSSteven Toth /* arg buffer size */ 820d46748cSSteven Toth #define CX24116_ARGLEN (0x1e) 830d46748cSSteven Toth 84490c8684SDarron Broad /* rolloff */ 85490c8684SDarron Broad #define CX24116_ROLLOFF_020 (0x00) 86490c8684SDarron Broad #define CX24116_ROLLOFF_025 (0x01) 87490c8684SDarron Broad #define CX24116_ROLLOFF_035 (0x02) 88490c8684SDarron Broad 89490c8684SDarron Broad /* pilot bit */ 9001a8f038SDarron Broad #define CX24116_PILOT_OFF (0x00) 9101a8f038SDarron Broad #define CX24116_PILOT_ON (0x40) 92490c8684SDarron Broad 93490c8684SDarron Broad /* signal status */ 94490c8684SDarron Broad #define CX24116_HAS_SIGNAL (0x01) 95490c8684SDarron Broad #define CX24116_HAS_CARRIER (0x02) 96490c8684SDarron Broad #define CX24116_HAS_VITERBI (0x04) 97490c8684SDarron Broad #define CX24116_HAS_SYNCLOCK (0x08) 98490c8684SDarron Broad #define CX24116_HAS_UNKNOWN1 (0x10) 99490c8684SDarron Broad #define CX24116_HAS_UNKNOWN2 (0x20) 1006639f1e0SDarron Broad #define CX24116_STATUS_MASK (0x0f) 101490c8684SDarron Broad #define CX24116_SIGNAL_MASK (0xc0) 102490c8684SDarron Broad 103490c8684SDarron Broad #define CX24116_DISEQC_TONEOFF (0) /* toneburst never sent */ 104490c8684SDarron Broad #define CX24116_DISEQC_TONECACHE (1) /* toneburst cached */ 105490c8684SDarron Broad #define CX24116_DISEQC_MESGCACHE (2) /* message cached */ 106490c8684SDarron Broad 1070d46748cSSteven Toth /* arg offset for DiSEqC */ 1080d46748cSSteven Toth #define CX24116_DISEQC_BURST (1) 1090d46748cSSteven Toth #define CX24116_DISEQC_ARG2_2 (2) /* unknown value=2 */ 1100d46748cSSteven Toth #define CX24116_DISEQC_ARG3_0 (3) /* unknown value=0 */ 1110d46748cSSteven Toth #define CX24116_DISEQC_ARG4_0 (4) /* unknown value=0 */ 1120d46748cSSteven Toth #define CX24116_DISEQC_MSGLEN (5) 1130d46748cSSteven Toth #define CX24116_DISEQC_MSGOFS (6) 1140d46748cSSteven Toth 1150d46748cSSteven Toth /* DiSEqC burst */ 1160d46748cSSteven Toth #define CX24116_DISEQC_MINI_A (0) 1170d46748cSSteven Toth #define CX24116_DISEQC_MINI_B (1) 1180d46748cSSteven Toth 119490c8684SDarron Broad /* DiSEqC tone burst */ 120490c8684SDarron Broad static int toneburst = 1; 121f11ec7d4SSteven Toth module_param(toneburst, int, 0644); 12298c94823SSteven Toth MODULE_PARM_DESC(toneburst, "DiSEqC toneburst 0=OFF, 1=TONE CACHE, "\ 12398c94823SSteven Toth "2=MESSAGE CACHE (default:1)"); 124490c8684SDarron Broad 1258953db79SSteven Toth /* SNR measurements */ 126f11ec7d4SSteven Toth static int esno_snr; 127f11ec7d4SSteven Toth module_param(esno_snr, int, 0644); 12853f1fe94SHans Petter Selasky MODULE_PARM_DESC(esno_snr, "SNR return units, 0=PERCENTAGE 0-100, "\ 12998c94823SSteven Toth "1=ESNO(db * 10) (default:0)"); 1308953db79SSteven Toth 131f11ec7d4SSteven Toth enum cmds { 132490c8684SDarron Broad CMD_SET_VCO = 0x10, 1330d46748cSSteven Toth CMD_TUNEREQUEST = 0x11, 134490c8684SDarron Broad CMD_MPEGCONFIG = 0x13, 135490c8684SDarron Broad CMD_TUNERINIT = 0x14, 136490c8684SDarron Broad CMD_BANDWIDTH = 0x15, 137490c8684SDarron Broad CMD_GETAGC = 0x19, 138490c8684SDarron Broad CMD_LNBCONFIG = 0x20, 139490c8684SDarron Broad CMD_LNBSEND = 0x21, /* Formerly CMD_SEND_DISEQC */ 140c7bdcd0fSIgor M. Liplianin CMD_LNBDCLEVEL = 0x22, 1410d46748cSSteven Toth CMD_SET_TONE = 0x23, 142490c8684SDarron Broad CMD_UPDFWVERS = 0x35, 143490c8684SDarron Broad CMD_TUNERSLEEP = 0x36, 144490c8684SDarron Broad CMD_AGCCONTROL = 0x3b, /* Unknown */ 1450d46748cSSteven Toth }; 1460d46748cSSteven Toth 1470d46748cSSteven Toth /* The Demod/Tuner can't easily provide these, we cache them */ 148f11ec7d4SSteven Toth struct cx24116_tuning { 1490d46748cSSteven Toth u32 frequency; 1500d46748cSSteven Toth u32 symbol_rate; 1510df289a2SMauro Carvalho Chehab enum fe_spectral_inversion inversion; 1520df289a2SMauro Carvalho Chehab enum fe_code_rate fec; 1530d46748cSSteven Toth 1540df289a2SMauro Carvalho Chehab enum fe_delivery_system delsys; 1550df289a2SMauro Carvalho Chehab enum fe_modulation modulation; 1560df289a2SMauro Carvalho Chehab enum fe_pilot pilot; 1570df289a2SMauro Carvalho Chehab enum fe_rolloff rolloff; 1580d46748cSSteven Toth 1590d46748cSSteven Toth /* Demod values */ 1600d46748cSSteven Toth u8 fec_val; 1610d46748cSSteven Toth u8 fec_mask; 1620d46748cSSteven Toth u8 inversion_val; 16301a8f038SDarron Broad u8 pilot_val; 164490c8684SDarron Broad u8 rolloff_val; 1650d46748cSSteven Toth }; 1660d46748cSSteven Toth 1670d46748cSSteven Toth /* Basic commands that are sent to the firmware */ 168f11ec7d4SSteven Toth struct cx24116_cmd { 1690d46748cSSteven Toth u8 len; 1700d46748cSSteven Toth u8 args[CX24116_ARGLEN]; 1710d46748cSSteven Toth }; 1720d46748cSSteven Toth 173f11ec7d4SSteven Toth struct cx24116_state { 1740d46748cSSteven Toth struct i2c_adapter *i2c; 1750d46748cSSteven Toth const struct cx24116_config *config; 1760d46748cSSteven Toth 1770d46748cSSteven Toth struct dvb_frontend frontend; 1780d46748cSSteven Toth 1790d46748cSSteven Toth struct cx24116_tuning dcur; 1800d46748cSSteven Toth struct cx24116_tuning dnxt; 1810d46748cSSteven Toth 1820d46748cSSteven Toth u8 skip_fw_load; 1830d46748cSSteven Toth u8 burst; 184490c8684SDarron Broad struct cx24116_cmd dsec_cmd; 1850d46748cSSteven Toth }; 1860d46748cSSteven Toth 1870d46748cSSteven Toth static int cx24116_writereg(struct cx24116_state *state, int reg, int data) 1880d46748cSSteven Toth { 1890d46748cSSteven Toth u8 buf[] = { reg, data }; 1900d46748cSSteven Toth struct i2c_msg msg = { .addr = state->config->demod_address, 1910d46748cSSteven Toth .flags = 0, .buf = buf, .len = 2 }; 1920d46748cSSteven Toth int err; 1930d46748cSSteven Toth 1940d46748cSSteven Toth if (debug > 1) 1950d46748cSSteven Toth printk("cx24116: %s: write reg 0x%02x, value 0x%02x\n", 1960d46748cSSteven Toth __func__, reg, data); 1970d46748cSSteven Toth 198f11ec7d4SSteven Toth err = i2c_transfer(state->i2c, &msg, 1); 199f11ec7d4SSteven Toth if (err != 1) { 2004bd69e7bSMauro Carvalho Chehab printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x, value == 0x%02x)\n", 2014bd69e7bSMauro Carvalho Chehab __func__, err, reg, data); 2020d46748cSSteven Toth return -EREMOTEIO; 2030d46748cSSteven Toth } 2040d46748cSSteven Toth 2050d46748cSSteven Toth return 0; 2060d46748cSSteven Toth } 2070d46748cSSteven Toth 2080d46748cSSteven Toth /* Bulk byte writes to a single I2C address, for 32k firmware load */ 209f11ec7d4SSteven Toth static int cx24116_writeregN(struct cx24116_state *state, int reg, 21064decbfeSGeert Uytterhoeven const u8 *data, u16 len) 2110d46748cSSteven Toth { 212d303b7c5SMarkus Elfring int ret; 2130d46748cSSteven Toth struct i2c_msg msg; 2140d46748cSSteven Toth u8 *buf; 2150d46748cSSteven Toth 2160d46748cSSteven Toth buf = kmalloc(len + 1, GFP_KERNEL); 2179722e569SMarkus Elfring if (!buf) 2189722e569SMarkus Elfring return -ENOMEM; 2190d46748cSSteven Toth 2200d46748cSSteven Toth *(buf) = reg; 2210d46748cSSteven Toth memcpy(buf + 1, data, len); 2220d46748cSSteven Toth 2230d46748cSSteven Toth msg.addr = state->config->demod_address; 2240d46748cSSteven Toth msg.flags = 0; 2250d46748cSSteven Toth msg.buf = buf; 2260d46748cSSteven Toth msg.len = len + 1; 2270d46748cSSteven Toth 2280d46748cSSteven Toth if (debug > 1) 22998c94823SSteven Toth printk(KERN_INFO "cx24116: %s: write regN 0x%02x, len = %d\n", 2300d46748cSSteven Toth __func__, reg, len); 2310d46748cSSteven Toth 232f11ec7d4SSteven Toth ret = i2c_transfer(state->i2c, &msg, 1); 233f11ec7d4SSteven Toth if (ret != 1) { 23498c94823SSteven Toth printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x\n", 2350d46748cSSteven Toth __func__, ret, reg); 2360d46748cSSteven Toth ret = -EREMOTEIO; 2370d46748cSSteven Toth } 2380d46748cSSteven Toth 2390d46748cSSteven Toth kfree(buf); 2400d46748cSSteven Toth 2410d46748cSSteven Toth return ret; 2420d46748cSSteven Toth } 2430d46748cSSteven Toth 2440d46748cSSteven Toth static int cx24116_readreg(struct cx24116_state *state, u8 reg) 2450d46748cSSteven Toth { 2460d46748cSSteven Toth int ret; 2470d46748cSSteven Toth u8 b0[] = { reg }; 2480d46748cSSteven Toth u8 b1[] = { 0 }; 2490d46748cSSteven Toth struct i2c_msg msg[] = { 250f11ec7d4SSteven Toth { .addr = state->config->demod_address, .flags = 0, 251f11ec7d4SSteven Toth .buf = b0, .len = 1 }, 252f11ec7d4SSteven Toth { .addr = state->config->demod_address, .flags = I2C_M_RD, 253f11ec7d4SSteven Toth .buf = b1, .len = 1 } 2540d46748cSSteven Toth }; 2550d46748cSSteven Toth 2560d46748cSSteven Toth ret = i2c_transfer(state->i2c, msg, 2); 2570d46748cSSteven Toth 2580d46748cSSteven Toth if (ret != 2) { 25998c94823SSteven Toth printk(KERN_ERR "%s: reg=0x%x (error=%d)\n", 26098c94823SSteven Toth __func__, reg, ret); 2610d46748cSSteven Toth return ret; 2620d46748cSSteven Toth } 2630d46748cSSteven Toth 2640d46748cSSteven Toth if (debug > 1) 26598c94823SSteven Toth printk(KERN_INFO "cx24116: read reg 0x%02x, value 0x%02x\n", 266f11ec7d4SSteven Toth reg, b1[0]); 2670d46748cSSteven Toth 2680d46748cSSteven Toth return b1[0]; 2690d46748cSSteven Toth } 2700d46748cSSteven Toth 27198c94823SSteven Toth static int cx24116_set_inversion(struct cx24116_state *state, 2720df289a2SMauro Carvalho Chehab enum fe_spectral_inversion inversion) 2730d46748cSSteven Toth { 2740d46748cSSteven Toth dprintk("%s(%d)\n", __func__, inversion); 2750d46748cSSteven Toth 2760d46748cSSteven Toth switch (inversion) { 2770d46748cSSteven Toth case INVERSION_OFF: 2780d46748cSSteven Toth state->dnxt.inversion_val = 0x00; 2790d46748cSSteven Toth break; 2800d46748cSSteven Toth case INVERSION_ON: 2810d46748cSSteven Toth state->dnxt.inversion_val = 0x04; 2820d46748cSSteven Toth break; 2830d46748cSSteven Toth case INVERSION_AUTO: 2840d46748cSSteven Toth state->dnxt.inversion_val = 0x0C; 2850d46748cSSteven Toth break; 2860d46748cSSteven Toth default: 2870d46748cSSteven Toth return -EINVAL; 2880d46748cSSteven Toth } 2890d46748cSSteven Toth 2900d46748cSSteven Toth state->dnxt.inversion = inversion; 2910d46748cSSteven Toth 2920d46748cSSteven Toth return 0; 2930d46748cSSteven Toth } 2940d46748cSSteven Toth 295490c8684SDarron Broad /* 296490c8684SDarron Broad * modfec (modulation and FEC) 297490c8684SDarron Broad * =========================== 298490c8684SDarron Broad * 299490c8684SDarron Broad * MOD FEC mask/val standard 300490c8684SDarron Broad * ---- -------- ----------- -------- 301490c8684SDarron Broad * QPSK FEC_1_2 0x02 0x02+X DVB-S 302490c8684SDarron Broad * QPSK FEC_2_3 0x04 0x02+X DVB-S 303490c8684SDarron Broad * QPSK FEC_3_4 0x08 0x02+X DVB-S 304490c8684SDarron Broad * QPSK FEC_4_5 0x10 0x02+X DVB-S (?) 305490c8684SDarron Broad * QPSK FEC_5_6 0x20 0x02+X DVB-S 306490c8684SDarron Broad * QPSK FEC_6_7 0x40 0x02+X DVB-S 307490c8684SDarron Broad * QPSK FEC_7_8 0x80 0x02+X DVB-S 308490c8684SDarron Broad * QPSK FEC_8_9 0x01 0x02+X DVB-S (?) (NOT SUPPORTED?) 309490c8684SDarron Broad * QPSK AUTO 0xff 0x02+X DVB-S 310490c8684SDarron Broad * 311490c8684SDarron Broad * For DVB-S high byte probably represents FEC 312490c8684SDarron Broad * and low byte selects the modulator. The high 313490c8684SDarron Broad * byte is search range mask. Bit 5 may turn 314490c8684SDarron Broad * on DVB-S and remaining bits represent some 315490c8684SDarron Broad * kind of calibration (how/what i do not know). 316490c8684SDarron Broad * 317490c8684SDarron Broad * Eg.(2/3) szap "Zone Horror" 318490c8684SDarron Broad * 319490c8684SDarron Broad * mask/val = 0x04, 0x20 32098c94823SSteven Toth * status 1f | signal c3c0 | snr a333 | ber 00000098 | unc 0 | FE_HAS_LOCK 321490c8684SDarron Broad * 322490c8684SDarron Broad * mask/val = 0x04, 0x30 32398c94823SSteven Toth * status 1f | signal c3c0 | snr a333 | ber 00000000 | unc 0 | FE_HAS_LOCK 324490c8684SDarron Broad * 325490c8684SDarron Broad * After tuning FECSTATUS contains actual FEC 326490c8684SDarron Broad * in use numbered 1 through to 8 for 1/2 .. 2/3 etc 327490c8684SDarron Broad * 328490c8684SDarron Broad * NBC=NOT/NON BACKWARD COMPATIBLE WITH DVB-S (DVB-S2 only) 329490c8684SDarron Broad * 330490c8684SDarron Broad * NBC-QPSK FEC_1_2 0x00, 0x04 DVB-S2 331490c8684SDarron Broad * NBC-QPSK FEC_3_5 0x00, 0x05 DVB-S2 332490c8684SDarron Broad * NBC-QPSK FEC_2_3 0x00, 0x06 DVB-S2 333490c8684SDarron Broad * NBC-QPSK FEC_3_4 0x00, 0x07 DVB-S2 334490c8684SDarron Broad * NBC-QPSK FEC_4_5 0x00, 0x08 DVB-S2 335490c8684SDarron Broad * NBC-QPSK FEC_5_6 0x00, 0x09 DVB-S2 336490c8684SDarron Broad * NBC-QPSK FEC_8_9 0x00, 0x0a DVB-S2 337490c8684SDarron Broad * NBC-QPSK FEC_9_10 0x00, 0x0b DVB-S2 338490c8684SDarron Broad * 339490c8684SDarron Broad * NBC-8PSK FEC_3_5 0x00, 0x0c DVB-S2 340490c8684SDarron Broad * NBC-8PSK FEC_2_3 0x00, 0x0d DVB-S2 341490c8684SDarron Broad * NBC-8PSK FEC_3_4 0x00, 0x0e DVB-S2 342490c8684SDarron Broad * NBC-8PSK FEC_5_6 0x00, 0x0f DVB-S2 343490c8684SDarron Broad * NBC-8PSK FEC_8_9 0x00, 0x10 DVB-S2 344490c8684SDarron Broad * NBC-8PSK FEC_9_10 0x00, 0x11 DVB-S2 345490c8684SDarron Broad * 346490c8684SDarron Broad * For DVB-S2 low bytes selects both modulator 347490c8684SDarron Broad * and FEC. High byte is meaningless here. To 348490c8684SDarron Broad * set pilot, bit 6 (0x40) is set. When inspecting 349490c8684SDarron Broad * FECSTATUS bit 7 (0x80) represents the pilot 350490c8684SDarron Broad * selection whilst not tuned. When tuned, actual FEC 351490c8684SDarron Broad * in use is found in FECSTATUS as per above. Pilot 352490c8684SDarron Broad * value is reset. 353490c8684SDarron Broad */ 354490c8684SDarron Broad 3550d46748cSSteven Toth /* A table of modulation, fec and configuration bytes for the demod. 3560d46748cSSteven Toth * Not all S2 mmodulation schemes are support and not all rates with 3570d46748cSSteven Toth * a scheme are support. Especially, no auto detect when in S2 mode. 3580d46748cSSteven Toth */ 359ffbc5f88SMauro Carvalho Chehab static struct cx24116_modfec { 3600df289a2SMauro Carvalho Chehab enum fe_delivery_system delivery_system; 3610df289a2SMauro Carvalho Chehab enum fe_modulation modulation; 3620df289a2SMauro Carvalho Chehab enum fe_code_rate fec; 3630d46748cSSteven Toth u8 mask; /* In DVBS mode this is used to autodetect */ 3640d46748cSSteven Toth u8 val; /* Passed to the firmware to indicate mode selection */ 3650d46748cSSteven Toth } CX24116_MODFEC_MODES[] = { 3660d46748cSSteven Toth /* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */ 367490c8684SDarron Broad 368490c8684SDarron Broad /*mod fec mask val */ 3690a6393aeSSteven Toth { SYS_DVBS, QPSK, FEC_NONE, 0xfe, 0x30 }, 3700a6393aeSSteven Toth { SYS_DVBS, QPSK, FEC_1_2, 0x02, 0x2e }, /* 00000010 00101110 */ 3710a6393aeSSteven Toth { SYS_DVBS, QPSK, FEC_2_3, 0x04, 0x2f }, /* 00000100 00101111 */ 3720a6393aeSSteven Toth { SYS_DVBS, QPSK, FEC_3_4, 0x08, 0x30 }, /* 00001000 00110000 */ 3730a6393aeSSteven Toth { SYS_DVBS, QPSK, FEC_4_5, 0xfe, 0x30 }, /* 000?0000 ? */ 3740a6393aeSSteven Toth { SYS_DVBS, QPSK, FEC_5_6, 0x20, 0x31 }, /* 00100000 00110001 */ 3750a6393aeSSteven Toth { SYS_DVBS, QPSK, FEC_6_7, 0xfe, 0x30 }, /* 0?000000 ? */ 3760a6393aeSSteven Toth { SYS_DVBS, QPSK, FEC_7_8, 0x80, 0x32 }, /* 10000000 00110010 */ 3770a6393aeSSteven Toth { SYS_DVBS, QPSK, FEC_8_9, 0xfe, 0x30 }, /* 0000000? ? */ 3780a6393aeSSteven Toth { SYS_DVBS, QPSK, FEC_AUTO, 0xfe, 0x30 }, 3790d46748cSSteven Toth /* NBC-QPSK */ 3800a6393aeSSteven Toth { SYS_DVBS2, QPSK, FEC_1_2, 0x00, 0x04 }, 3810a6393aeSSteven Toth { SYS_DVBS2, QPSK, FEC_3_5, 0x00, 0x05 }, 3820a6393aeSSteven Toth { SYS_DVBS2, QPSK, FEC_2_3, 0x00, 0x06 }, 3830a6393aeSSteven Toth { SYS_DVBS2, QPSK, FEC_3_4, 0x00, 0x07 }, 3840a6393aeSSteven Toth { SYS_DVBS2, QPSK, FEC_4_5, 0x00, 0x08 }, 3850a6393aeSSteven Toth { SYS_DVBS2, QPSK, FEC_5_6, 0x00, 0x09 }, 3860a6393aeSSteven Toth { SYS_DVBS2, QPSK, FEC_8_9, 0x00, 0x0a }, 3870a6393aeSSteven Toth { SYS_DVBS2, QPSK, FEC_9_10, 0x00, 0x0b }, 3880d46748cSSteven Toth /* 8PSK */ 3890a6393aeSSteven Toth { SYS_DVBS2, PSK_8, FEC_3_5, 0x00, 0x0c }, 3900a6393aeSSteven Toth { SYS_DVBS2, PSK_8, FEC_2_3, 0x00, 0x0d }, 3910a6393aeSSteven Toth { SYS_DVBS2, PSK_8, FEC_3_4, 0x00, 0x0e }, 3920a6393aeSSteven Toth { SYS_DVBS2, PSK_8, FEC_5_6, 0x00, 0x0f }, 3930a6393aeSSteven Toth { SYS_DVBS2, PSK_8, FEC_8_9, 0x00, 0x10 }, 3940a6393aeSSteven Toth { SYS_DVBS2, PSK_8, FEC_9_10, 0x00, 0x11 }, 395490c8684SDarron Broad /* 396490c8684SDarron Broad * `val' can be found in the FECSTATUS register when tuning. 397490c8684SDarron Broad * FECSTATUS will give the actual FEC in use if tuning was successful. 398490c8684SDarron Broad */ 3990d46748cSSteven Toth }; 4000d46748cSSteven Toth 4010d46748cSSteven Toth static int cx24116_lookup_fecmod(struct cx24116_state *state, 4020df289a2SMauro Carvalho Chehab enum fe_delivery_system d, enum fe_modulation m, enum fe_code_rate f) 4030d46748cSSteven Toth { 4040d46748cSSteven Toth int i, ret = -EOPNOTSUPP; 4050d46748cSSteven Toth 406490c8684SDarron Broad dprintk("%s(0x%02x,0x%02x)\n", __func__, m, f); 407490c8684SDarron Broad 408f11ec7d4SSteven Toth for (i = 0; i < ARRAY_SIZE(CX24116_MODFEC_MODES); i++) { 4093569476dSDarron Broad if ((d == CX24116_MODFEC_MODES[i].delivery_system) && 4103569476dSDarron Broad (m == CX24116_MODFEC_MODES[i].modulation) && 411f11ec7d4SSteven Toth (f == CX24116_MODFEC_MODES[i].fec)) { 4120d46748cSSteven Toth ret = i; 4130d46748cSSteven Toth break; 4140d46748cSSteven Toth } 4150d46748cSSteven Toth } 4160d46748cSSteven Toth 4170d46748cSSteven Toth return ret; 4180d46748cSSteven Toth } 4190d46748cSSteven Toth 42098c94823SSteven Toth static int cx24116_set_fec(struct cx24116_state *state, 4210df289a2SMauro Carvalho Chehab enum fe_delivery_system delsys, 4220df289a2SMauro Carvalho Chehab enum fe_modulation mod, 4230df289a2SMauro Carvalho Chehab enum fe_code_rate fec) 4240d46748cSSteven Toth { 4250d46748cSSteven Toth int ret = 0; 426490c8684SDarron Broad 427490c8684SDarron Broad dprintk("%s(0x%02x,0x%02x)\n", __func__, mod, fec); 4280d46748cSSteven Toth 4293569476dSDarron Broad ret = cx24116_lookup_fecmod(state, delsys, mod, fec); 4300d46748cSSteven Toth 4310d46748cSSteven Toth if (ret < 0) 4320d46748cSSteven Toth return ret; 4330d46748cSSteven Toth 434490c8684SDarron Broad state->dnxt.fec = fec; 4350d46748cSSteven Toth state->dnxt.fec_val = CX24116_MODFEC_MODES[ret].val; 4360d46748cSSteven Toth state->dnxt.fec_mask = CX24116_MODFEC_MODES[ret].mask; 437490c8684SDarron Broad dprintk("%s() mask/val = 0x%02x/0x%02x\n", __func__, 438490c8684SDarron Broad state->dnxt.fec_mask, state->dnxt.fec_val); 4390d46748cSSteven Toth 4400d46748cSSteven Toth return 0; 4410d46748cSSteven Toth } 4420d46748cSSteven Toth 4430d46748cSSteven Toth static int cx24116_set_symbolrate(struct cx24116_state *state, u32 rate) 4440d46748cSSteven Toth { 445490c8684SDarron Broad dprintk("%s(%d)\n", __func__, rate); 4460d46748cSSteven Toth 4470d46748cSSteven Toth /* check if symbol rate is within limits */ 448490c8684SDarron Broad if ((rate > state->frontend.ops.info.symbol_rate_max) || 449490c8684SDarron Broad (rate < state->frontend.ops.info.symbol_rate_min)) { 450490c8684SDarron Broad dprintk("%s() unsupported symbol_rate = %d\n", __func__, rate); 451490c8684SDarron Broad return -EOPNOTSUPP; 452490c8684SDarron Broad } 4530d46748cSSteven Toth 454490c8684SDarron Broad state->dnxt.symbol_rate = rate; 455490c8684SDarron Broad dprintk("%s() symbol_rate = %d\n", __func__, rate); 456490c8684SDarron Broad 457490c8684SDarron Broad return 0; 4580d46748cSSteven Toth } 4590d46748cSSteven Toth 460f11ec7d4SSteven Toth static int cx24116_load_firmware(struct dvb_frontend *fe, 461f11ec7d4SSteven Toth const struct firmware *fw); 4620d46748cSSteven Toth 4630d46748cSSteven Toth static int cx24116_firmware_ondemand(struct dvb_frontend *fe) 4640d46748cSSteven Toth { 4650d46748cSSteven Toth struct cx24116_state *state = fe->demodulator_priv; 4660d46748cSSteven Toth const struct firmware *fw; 4670d46748cSSteven Toth int ret = 0; 4680d46748cSSteven Toth 4690d46748cSSteven Toth dprintk("%s()\n", __func__); 4700d46748cSSteven Toth 471f11ec7d4SSteven Toth if (cx24116_readreg(state, 0x20) > 0) { 4720d46748cSSteven Toth 4730d46748cSSteven Toth if (state->skip_fw_load) 4740d46748cSSteven Toth return 0; 4750d46748cSSteven Toth 4760d46748cSSteven Toth /* Load firmware */ 47798c94823SSteven Toth /* request the firmware, this will block until loaded */ 47898c94823SSteven Toth printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", 47998c94823SSteven Toth __func__, CX24116_DEFAULT_FIRMWARE); 48098c94823SSteven Toth ret = request_firmware(&fw, CX24116_DEFAULT_FIRMWARE, 481e9785250SJean Delvare state->i2c->dev.parent); 48298c94823SSteven Toth printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", 48398c94823SSteven Toth __func__); 4840d46748cSSteven Toth if (ret) { 4854bd69e7bSMauro Carvalho Chehab printk(KERN_ERR "%s: No firmware uploaded (timeout or file not found?)\n", 4864bd69e7bSMauro Carvalho Chehab __func__); 4870d46748cSSteven Toth return ret; 4880d46748cSSteven Toth } 4890d46748cSSteven Toth 49098c94823SSteven Toth /* Make sure we don't recurse back through here 49198c94823SSteven Toth * during loading */ 4920d46748cSSteven Toth state->skip_fw_load = 1; 4930d46748cSSteven Toth 4940d46748cSSteven Toth ret = cx24116_load_firmware(fe, fw); 4950d46748cSSteven Toth if (ret) 49698c94823SSteven Toth printk(KERN_ERR "%s: Writing firmware to device failed\n", 49798c94823SSteven Toth __func__); 4980d46748cSSteven Toth 4990d46748cSSteven Toth release_firmware(fw); 5000d46748cSSteven Toth 50198c94823SSteven Toth printk(KERN_INFO "%s: Firmware upload %s\n", __func__, 50298c94823SSteven Toth ret == 0 ? "complete" : "failed"); 5030d46748cSSteven Toth 5040d46748cSSteven Toth /* Ensure firmware is always loaded if required */ 5050d46748cSSteven Toth state->skip_fw_load = 0; 5060d46748cSSteven Toth } 5070d46748cSSteven Toth 5080d46748cSSteven Toth return ret; 5090d46748cSSteven Toth } 5100d46748cSSteven Toth 51198c94823SSteven Toth /* Take a basic firmware command structure, format it 51298c94823SSteven Toth * and forward it for processing 51398c94823SSteven Toth */ 5140d46748cSSteven Toth static int cx24116_cmd_execute(struct dvb_frontend *fe, struct cx24116_cmd *cmd) 5150d46748cSSteven Toth { 5160d46748cSSteven Toth struct cx24116_state *state = fe->demodulator_priv; 5170d46748cSSteven Toth int i, ret; 5180d46748cSSteven Toth 5190d46748cSSteven Toth dprintk("%s()\n", __func__); 5200d46748cSSteven Toth 5210d46748cSSteven Toth /* Load the firmware if required */ 522f11ec7d4SSteven Toth ret = cx24116_firmware_ondemand(fe); 523f11ec7d4SSteven Toth if (ret != 0) { 52498c94823SSteven Toth printk(KERN_ERR "%s(): Unable initialise the firmware\n", 52598c94823SSteven Toth __func__); 5260d46748cSSteven Toth return ret; 5270d46748cSSteven Toth } 5280d46748cSSteven Toth 5290d46748cSSteven Toth /* Write the command */ 530f11ec7d4SSteven Toth for (i = 0; i < cmd->len ; i++) { 5310d46748cSSteven Toth dprintk("%s: 0x%02x == 0x%02x\n", __func__, i, cmd->args[i]); 5320d46748cSSteven Toth cx24116_writereg(state, i, cmd->args[i]); 5330d46748cSSteven Toth } 5340d46748cSSteven Toth 5350d46748cSSteven Toth /* Start execution and wait for cmd to terminate */ 536490c8684SDarron Broad cx24116_writereg(state, CX24116_REG_EXECUTE, 0x01); 537f11ec7d4SSteven Toth while (cx24116_readreg(state, CX24116_REG_EXECUTE)) { 5380d46748cSSteven Toth msleep(10); 539f11ec7d4SSteven Toth if (i++ > 64) { 54098c94823SSteven Toth /* Avoid looping forever if the firmware does 54198c94823SSteven Toth not respond */ 54298c94823SSteven Toth printk(KERN_WARNING "%s() Firmware not responding\n", 54398c94823SSteven Toth __func__); 5440d46748cSSteven Toth return -EREMOTEIO; 5450d46748cSSteven Toth } 5460d46748cSSteven Toth } 5470d46748cSSteven Toth return 0; 5480d46748cSSteven Toth } 5490d46748cSSteven Toth 550f11ec7d4SSteven Toth static int cx24116_load_firmware(struct dvb_frontend *fe, 551f11ec7d4SSteven Toth const struct firmware *fw) 5520d46748cSSteven Toth { 5530d46748cSSteven Toth struct cx24116_state *state = fe->demodulator_priv; 5540d46748cSSteven Toth struct cx24116_cmd cmd; 555e0bae9b3SAntti Palosaari int i, ret, len, max, remaining; 556490c8684SDarron Broad unsigned char vers[4]; 5570d46748cSSteven Toth 5580d46748cSSteven Toth dprintk("%s\n", __func__); 559f11ec7d4SSteven Toth dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n", 560f11ec7d4SSteven Toth fw->size, 561f11ec7d4SSteven Toth fw->data[0], 562f11ec7d4SSteven Toth fw->data[1], 563f11ec7d4SSteven Toth fw->data[fw->size-2], 564f11ec7d4SSteven Toth fw->data[fw->size-1]); 5650d46748cSSteven Toth 5660d46748cSSteven Toth /* Toggle 88x SRST pin to reset demod */ 5670d46748cSSteven Toth if (state->config->reset_device) 5680d46748cSSteven Toth state->config->reset_device(fe); 5690d46748cSSteven Toth 5700d46748cSSteven Toth /* Begin the firmware load process */ 5710d46748cSSteven Toth /* Prepare the demod, load the firmware, cleanup after load */ 5720d46748cSSteven Toth 573490c8684SDarron Broad /* Init PLL */ 574490c8684SDarron Broad cx24116_writereg(state, 0xE5, 0x00); 575490c8684SDarron Broad cx24116_writereg(state, 0xF1, 0x08); 576490c8684SDarron Broad cx24116_writereg(state, 0xF2, 0x13); 577490c8684SDarron Broad 578490c8684SDarron Broad /* Start PLL */ 579490c8684SDarron Broad cx24116_writereg(state, 0xe0, 0x03); 580490c8684SDarron Broad cx24116_writereg(state, 0xe0, 0x00); 581490c8684SDarron Broad 582490c8684SDarron Broad /* Unknown */ 583490c8684SDarron Broad cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46); 584490c8684SDarron Broad cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00); 585490c8684SDarron Broad 586490c8684SDarron Broad /* Unknown */ 5870d46748cSSteven Toth cx24116_writereg(state, 0xF0, 0x03); 5880d46748cSSteven Toth cx24116_writereg(state, 0xF4, 0x81); 5890d46748cSSteven Toth cx24116_writereg(state, 0xF5, 0x00); 5900d46748cSSteven Toth cx24116_writereg(state, 0xF6, 0x00); 5910d46748cSSteven Toth 592107d7b18SAntti Palosaari /* Split firmware to the max I2C write len and write. 593e0bae9b3SAntti Palosaari * Writes whole firmware as one write when i2c_wr_max is set to 0. */ 594e0bae9b3SAntti Palosaari if (state->config->i2c_wr_max) 595e0bae9b3SAntti Palosaari max = state->config->i2c_wr_max; 596e0bae9b3SAntti Palosaari else 597e0bae9b3SAntti Palosaari max = INT_MAX; /* enough for 32k firmware */ 598107d7b18SAntti Palosaari 599e0bae9b3SAntti Palosaari for (remaining = fw->size; remaining > 0; remaining -= max - 1) { 600107d7b18SAntti Palosaari len = remaining; 601e0bae9b3SAntti Palosaari if (len > max - 1) 602e0bae9b3SAntti Palosaari len = max - 1; 603107d7b18SAntti Palosaari 604107d7b18SAntti Palosaari cx24116_writeregN(state, 0xF7, &fw->data[fw->size - remaining], 605107d7b18SAntti Palosaari len); 606107d7b18SAntti Palosaari } 6070d46748cSSteven Toth 6080d46748cSSteven Toth cx24116_writereg(state, 0xF4, 0x10); 6090d46748cSSteven Toth cx24116_writereg(state, 0xF0, 0x00); 6100d46748cSSteven Toth cx24116_writereg(state, 0xF8, 0x06); 6110d46748cSSteven Toth 612490c8684SDarron Broad /* Firmware CMD 10: VCO config */ 613490c8684SDarron Broad cmd.args[0x00] = CMD_SET_VCO; 6140d46748cSSteven Toth cmd.args[0x01] = 0x05; 6150d46748cSSteven Toth cmd.args[0x02] = 0xdc; 6160d46748cSSteven Toth cmd.args[0x03] = 0xda; 6170d46748cSSteven Toth cmd.args[0x04] = 0xae; 6180d46748cSSteven Toth cmd.args[0x05] = 0xaa; 6190d46748cSSteven Toth cmd.args[0x06] = 0x04; 6200d46748cSSteven Toth cmd.args[0x07] = 0x9d; 6210d46748cSSteven Toth cmd.args[0x08] = 0xfc; 6220d46748cSSteven Toth cmd.args[0x09] = 0x06; 6230d46748cSSteven Toth cmd.len = 0x0a; 6240d46748cSSteven Toth ret = cx24116_cmd_execute(fe, &cmd); 6250d46748cSSteven Toth if (ret != 0) 6260d46748cSSteven Toth return ret; 6270d46748cSSteven Toth 628490c8684SDarron Broad cx24116_writereg(state, CX24116_REG_SSTATUS, 0x00); 6290d46748cSSteven Toth 630490c8684SDarron Broad /* Firmware CMD 14: Tuner config */ 631490c8684SDarron Broad cmd.args[0x00] = CMD_TUNERINIT; 6320d46748cSSteven Toth cmd.args[0x01] = 0x00; 6330d46748cSSteven Toth cmd.args[0x02] = 0x00; 6340d46748cSSteven Toth cmd.len = 0x03; 6350d46748cSSteven Toth ret = cx24116_cmd_execute(fe, &cmd); 6360d46748cSSteven Toth if (ret != 0) 6370d46748cSSteven Toth return ret; 6380d46748cSSteven Toth 6390d46748cSSteven Toth cx24116_writereg(state, 0xe5, 0x00); 6400d46748cSSteven Toth 641490c8684SDarron Broad /* Firmware CMD 13: MPEG config */ 642490c8684SDarron Broad cmd.args[0x00] = CMD_MPEGCONFIG; 6430d46748cSSteven Toth cmd.args[0x01] = 0x01; 6440d46748cSSteven Toth cmd.args[0x02] = 0x75; 6450d46748cSSteven Toth cmd.args[0x03] = 0x00; 646cc8c4f3aSIgor M. Liplianin if (state->config->mpg_clk_pos_pol) 647cc8c4f3aSIgor M. Liplianin cmd.args[0x04] = state->config->mpg_clk_pos_pol; 648cc8c4f3aSIgor M. Liplianin else 6490d46748cSSteven Toth cmd.args[0x04] = 0x02; 6500d46748cSSteven Toth cmd.args[0x05] = 0x00; 6510d46748cSSteven Toth cmd.len = 0x06; 6520d46748cSSteven Toth ret = cx24116_cmd_execute(fe, &cmd); 6530d46748cSSteven Toth if (ret != 0) 6540d46748cSSteven Toth return ret; 6550d46748cSSteven Toth 656490c8684SDarron Broad /* Firmware CMD 35: Get firmware version */ 657490c8684SDarron Broad cmd.args[0x00] = CMD_UPDFWVERS; 658490c8684SDarron Broad cmd.len = 0x02; 659490c8684SDarron Broad for (i = 0; i < 4; i++) { 660490c8684SDarron Broad cmd.args[0x01] = i; 661490c8684SDarron Broad ret = cx24116_cmd_execute(fe, &cmd); 662490c8684SDarron Broad if (ret != 0) 663490c8684SDarron Broad return ret; 664490c8684SDarron Broad vers[i] = cx24116_readreg(state, CX24116_REG_MAILBOX); 665490c8684SDarron Broad } 66698c94823SSteven Toth printk(KERN_INFO "%s: FW version %i.%i.%i.%i\n", __func__, 667490c8684SDarron Broad vers[0], vers[1], vers[2], vers[3]); 668490c8684SDarron Broad 6690d46748cSSteven Toth return 0; 6700d46748cSSteven Toth } 6710d46748cSSteven Toth 6720df289a2SMauro Carvalho Chehab static int cx24116_read_status(struct dvb_frontend *fe, enum fe_status *status) 6730d46748cSSteven Toth { 6740d46748cSSteven Toth struct cx24116_state *state = fe->demodulator_priv; 6750d46748cSSteven Toth 6766639f1e0SDarron Broad int lock = cx24116_readreg(state, CX24116_REG_SSTATUS) & 6776639f1e0SDarron Broad CX24116_STATUS_MASK; 6780d46748cSSteven Toth 6790d46748cSSteven Toth dprintk("%s: status = 0x%02x\n", __func__, lock); 6800d46748cSSteven Toth 6810d46748cSSteven Toth *status = 0; 6820d46748cSSteven Toth 683490c8684SDarron Broad if (lock & CX24116_HAS_SIGNAL) 6840d46748cSSteven Toth *status |= FE_HAS_SIGNAL; 685490c8684SDarron Broad if (lock & CX24116_HAS_CARRIER) 6860d46748cSSteven Toth *status |= FE_HAS_CARRIER; 687490c8684SDarron Broad if (lock & CX24116_HAS_VITERBI) 6880d46748cSSteven Toth *status |= FE_HAS_VITERBI; 689490c8684SDarron Broad if (lock & CX24116_HAS_SYNCLOCK) 6900d46748cSSteven Toth *status |= FE_HAS_SYNC | FE_HAS_LOCK; 6910d46748cSSteven Toth 6920d46748cSSteven Toth return 0; 6930d46748cSSteven Toth } 6940d46748cSSteven Toth 6950d46748cSSteven Toth static int cx24116_read_ber(struct dvb_frontend *fe, u32 *ber) 6960d46748cSSteven Toth { 697490c8684SDarron Broad struct cx24116_state *state = fe->demodulator_priv; 698490c8684SDarron Broad 6990d46748cSSteven Toth dprintk("%s()\n", __func__); 700490c8684SDarron Broad 701490c8684SDarron Broad *ber = (cx24116_readreg(state, CX24116_REG_BER24) << 24) | 702490c8684SDarron Broad (cx24116_readreg(state, CX24116_REG_BER16) << 16) | 703490c8684SDarron Broad (cx24116_readreg(state, CX24116_REG_BER8) << 8) | 704490c8684SDarron Broad cx24116_readreg(state, CX24116_REG_BER0); 7050d46748cSSteven Toth 7060d46748cSSteven Toth return 0; 7070d46748cSSteven Toth } 7080d46748cSSteven Toth 709490c8684SDarron Broad /* TODO Determine function and scale appropriately */ 710f11ec7d4SSteven Toth static int cx24116_read_signal_strength(struct dvb_frontend *fe, 711f11ec7d4SSteven Toth u16 *signal_strength) 7120d46748cSSteven Toth { 7130d46748cSSteven Toth struct cx24116_state *state = fe->demodulator_priv; 714490c8684SDarron Broad struct cx24116_cmd cmd; 715490c8684SDarron Broad int ret; 716490c8684SDarron Broad u16 sig_reading; 717490c8684SDarron Broad 718490c8684SDarron Broad dprintk("%s()\n", __func__); 719490c8684SDarron Broad 720490c8684SDarron Broad /* Firmware CMD 19: Get AGC */ 721490c8684SDarron Broad cmd.args[0x00] = CMD_GETAGC; 722490c8684SDarron Broad cmd.len = 0x01; 723490c8684SDarron Broad ret = cx24116_cmd_execute(fe, &cmd); 724490c8684SDarron Broad if (ret != 0) 725490c8684SDarron Broad return ret; 726490c8684SDarron Broad 727f11ec7d4SSteven Toth sig_reading = 728f11ec7d4SSteven Toth (cx24116_readreg(state, 729f11ec7d4SSteven Toth CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK) | 730490c8684SDarron Broad (cx24116_readreg(state, CX24116_REG_SIGNAL) << 6); 731490c8684SDarron Broad *signal_strength = 0 - sig_reading; 732490c8684SDarron Broad 733f11ec7d4SSteven Toth dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", 734f11ec7d4SSteven Toth __func__, sig_reading, *signal_strength); 735490c8684SDarron Broad 736490c8684SDarron Broad return 0; 737490c8684SDarron Broad } 738490c8684SDarron Broad 739490c8684SDarron Broad /* SNR (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */ 7408953db79SSteven Toth static int cx24116_read_snr_pct(struct dvb_frontend *fe, u16 *snr) 741490c8684SDarron Broad { 742490c8684SDarron Broad struct cx24116_state *state = fe->demodulator_priv; 743490c8684SDarron Broad u8 snr_reading; 744490c8684SDarron Broad static const u32 snr_tab[] = { /* 10 x Table (rounded up) */ 745490c8684SDarron Broad 0x00000, 0x0199A, 0x03333, 0x04ccD, 0x06667, 746490c8684SDarron Broad 0x08000, 0x0999A, 0x0b333, 0x0cccD, 0x0e667, 747f11ec7d4SSteven Toth 0x10000, 0x1199A, 0x13333, 0x14ccD, 0x16667, 748f11ec7d4SSteven Toth 0x18000 }; 7490d46748cSSteven Toth 7500d46748cSSteven Toth dprintk("%s()\n", __func__); 7510d46748cSSteven Toth 7528953db79SSteven Toth snr_reading = cx24116_readreg(state, CX24116_REG_QUALITY0); 7530d46748cSSteven Toth 754490c8684SDarron Broad if (snr_reading >= 0xa0 /* 100% */) 755490c8684SDarron Broad *snr = 0xffff; 7560d46748cSSteven Toth else 757490c8684SDarron Broad *snr = snr_tab[(snr_reading & 0xf0) >> 4] + 758490c8684SDarron Broad (snr_tab[(snr_reading & 0x0f)] >> 4); 7590d46748cSSteven Toth 760490c8684SDarron Broad dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__, 761490c8684SDarron Broad snr_reading, *snr); 7620d46748cSSteven Toth 7630d46748cSSteven Toth return 0; 7640d46748cSSteven Toth } 7650d46748cSSteven Toth 7668953db79SSteven Toth /* The reelbox patches show the value in the registers represents 7678953db79SSteven Toth * ESNO, from 0->30db (values 0->300). We provide this value by 7688953db79SSteven Toth * default. 7698953db79SSteven Toth */ 7708953db79SSteven Toth static int cx24116_read_snr_esno(struct dvb_frontend *fe, u16 *snr) 7718953db79SSteven Toth { 7728953db79SSteven Toth struct cx24116_state *state = fe->demodulator_priv; 7738953db79SSteven Toth 7748953db79SSteven Toth dprintk("%s()\n", __func__); 7758953db79SSteven Toth 7768953db79SSteven Toth *snr = cx24116_readreg(state, CX24116_REG_QUALITY8) << 8 | 7778953db79SSteven Toth cx24116_readreg(state, CX24116_REG_QUALITY0); 7788953db79SSteven Toth 7798953db79SSteven Toth dprintk("%s: raw 0x%04x\n", __func__, *snr); 7808953db79SSteven Toth 7818953db79SSteven Toth return 0; 7828953db79SSteven Toth } 7838953db79SSteven Toth 7848953db79SSteven Toth static int cx24116_read_snr(struct dvb_frontend *fe, u16 *snr) 7858953db79SSteven Toth { 7868953db79SSteven Toth if (esno_snr == 1) 7878953db79SSteven Toth return cx24116_read_snr_esno(fe, snr); 7888953db79SSteven Toth else 7898953db79SSteven Toth return cx24116_read_snr_pct(fe, snr); 7908953db79SSteven Toth } 7918953db79SSteven Toth 7920d46748cSSteven Toth static int cx24116_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) 7930d46748cSSteven Toth { 794490c8684SDarron Broad struct cx24116_state *state = fe->demodulator_priv; 795490c8684SDarron Broad 7960d46748cSSteven Toth dprintk("%s()\n", __func__); 797490c8684SDarron Broad 798490c8684SDarron Broad *ucblocks = (cx24116_readreg(state, CX24116_REG_UCB8) << 8) | 799490c8684SDarron Broad cx24116_readreg(state, CX24116_REG_UCB0); 8000d46748cSSteven Toth 8010d46748cSSteven Toth return 0; 8020d46748cSSteven Toth } 8030d46748cSSteven Toth 8040d46748cSSteven Toth /* Overwrite the current tuning params, we are about to tune */ 8050d46748cSSteven Toth static void cx24116_clone_params(struct dvb_frontend *fe) 8060d46748cSSteven Toth { 8070d46748cSSteven Toth struct cx24116_state *state = fe->demodulator_priv; 808ee45ddc1SEzequiel Garcia state->dcur = state->dnxt; 8090d46748cSSteven Toth } 8100d46748cSSteven Toth 811490c8684SDarron Broad /* Wait for LNB */ 812490c8684SDarron Broad static int cx24116_wait_for_lnb(struct dvb_frontend *fe) 813490c8684SDarron Broad { 814490c8684SDarron Broad struct cx24116_state *state = fe->demodulator_priv; 815490c8684SDarron Broad int i; 816490c8684SDarron Broad 817490c8684SDarron Broad dprintk("%s() qstatus = 0x%02x\n", __func__, 818490c8684SDarron Broad cx24116_readreg(state, CX24116_REG_QSTATUS)); 819490c8684SDarron Broad 820490c8684SDarron Broad /* Wait for up to 300 ms */ 821490c8684SDarron Broad for (i = 0; i < 30 ; i++) { 822490c8684SDarron Broad if (cx24116_readreg(state, CX24116_REG_QSTATUS) & 0x20) 823490c8684SDarron Broad return 0; 824490c8684SDarron Broad msleep(10); 825490c8684SDarron Broad } 826490c8684SDarron Broad 827490c8684SDarron Broad dprintk("%s(): LNB not ready\n", __func__); 828490c8684SDarron Broad 829490c8684SDarron Broad return -ETIMEDOUT; /* -EBUSY ? */ 830490c8684SDarron Broad } 831490c8684SDarron Broad 832c7bdcd0fSIgor M. Liplianin static int cx24116_set_voltage(struct dvb_frontend *fe, 8330df289a2SMauro Carvalho Chehab enum fe_sec_voltage voltage) 834c7bdcd0fSIgor M. Liplianin { 835c7bdcd0fSIgor M. Liplianin struct cx24116_cmd cmd; 836c7bdcd0fSIgor M. Liplianin int ret; 837c7bdcd0fSIgor M. Liplianin 838c7bdcd0fSIgor M. Liplianin dprintk("%s: %s\n", __func__, 839c7bdcd0fSIgor M. Liplianin voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : 840c7bdcd0fSIgor M. Liplianin voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); 841c7bdcd0fSIgor M. Liplianin 842c7bdcd0fSIgor M. Liplianin /* Wait for LNB ready */ 843c7bdcd0fSIgor M. Liplianin ret = cx24116_wait_for_lnb(fe); 844c7bdcd0fSIgor M. Liplianin if (ret != 0) 845c7bdcd0fSIgor M. Liplianin return ret; 846c7bdcd0fSIgor M. Liplianin 847c7bdcd0fSIgor M. Liplianin /* Wait for voltage/min repeat delay */ 848c7bdcd0fSIgor M. Liplianin msleep(100); 849c7bdcd0fSIgor M. Liplianin 850c7bdcd0fSIgor M. Liplianin cmd.args[0x00] = CMD_LNBDCLEVEL; 851c7bdcd0fSIgor M. Liplianin cmd.args[0x01] = (voltage == SEC_VOLTAGE_18 ? 0x01 : 0x00); 852c7bdcd0fSIgor M. Liplianin cmd.len = 0x02; 853c7bdcd0fSIgor M. Liplianin 854c7bdcd0fSIgor M. Liplianin /* Min delay time before DiSEqC send */ 855c7bdcd0fSIgor M. Liplianin msleep(15); 856c7bdcd0fSIgor M. Liplianin 857c7bdcd0fSIgor M. Liplianin return cx24116_cmd_execute(fe, &cmd); 858c7bdcd0fSIgor M. Liplianin } 859c7bdcd0fSIgor M. Liplianin 860f11ec7d4SSteven Toth static int cx24116_set_tone(struct dvb_frontend *fe, 8610df289a2SMauro Carvalho Chehab enum fe_sec_tone_mode tone) 8620d46748cSSteven Toth { 8630d46748cSSteven Toth struct cx24116_cmd cmd; 8640d46748cSSteven Toth int ret; 8650d46748cSSteven Toth 8660d46748cSSteven Toth dprintk("%s(%d)\n", __func__, tone); 8670d46748cSSteven Toth if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) { 86898c94823SSteven Toth printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone); 8690d46748cSSteven Toth return -EINVAL; 8700d46748cSSteven Toth } 8710d46748cSSteven Toth 872490c8684SDarron Broad /* Wait for LNB ready */ 873490c8684SDarron Broad ret = cx24116_wait_for_lnb(fe); 874490c8684SDarron Broad if (ret != 0) 875490c8684SDarron Broad return ret; 876490c8684SDarron Broad 877490c8684SDarron Broad /* Min delay time after DiSEqC send */ 878490c8684SDarron Broad msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */ 879490c8684SDarron Broad 8800d46748cSSteven Toth /* Now we set the tone */ 8810d46748cSSteven Toth cmd.args[0x00] = CMD_SET_TONE; 8820d46748cSSteven Toth cmd.args[0x01] = 0x00; 8830d46748cSSteven Toth cmd.args[0x02] = 0x00; 8840d46748cSSteven Toth 8850d46748cSSteven Toth switch (tone) { 8860d46748cSSteven Toth case SEC_TONE_ON: 8870d46748cSSteven Toth dprintk("%s: setting tone on\n", __func__); 8880d46748cSSteven Toth cmd.args[0x03] = 0x01; 8890d46748cSSteven Toth break; 8900d46748cSSteven Toth case SEC_TONE_OFF: 8910d46748cSSteven Toth dprintk("%s: setting tone off\n", __func__); 8920d46748cSSteven Toth cmd.args[0x03] = 0x00; 8930d46748cSSteven Toth break; 8940d46748cSSteven Toth } 8950d46748cSSteven Toth cmd.len = 0x04; 8960d46748cSSteven Toth 897490c8684SDarron Broad /* Min delay time before DiSEqC send */ 898490c8684SDarron Broad msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */ 899490c8684SDarron Broad 9000d46748cSSteven Toth return cx24116_cmd_execute(fe, &cmd); 9010d46748cSSteven Toth } 9020d46748cSSteven Toth 9030d46748cSSteven Toth /* Initialise DiSEqC */ 9040d46748cSSteven Toth static int cx24116_diseqc_init(struct dvb_frontend *fe) 9050d46748cSSteven Toth { 9060d46748cSSteven Toth struct cx24116_state *state = fe->demodulator_priv; 907490c8684SDarron Broad struct cx24116_cmd cmd; 908490c8684SDarron Broad int ret; 9090d46748cSSteven Toth 910490c8684SDarron Broad /* Firmware CMD 20: LNB/DiSEqC config */ 911490c8684SDarron Broad cmd.args[0x00] = CMD_LNBCONFIG; 912490c8684SDarron Broad cmd.args[0x01] = 0x00; 913490c8684SDarron Broad cmd.args[0x02] = 0x10; 914490c8684SDarron Broad cmd.args[0x03] = 0x00; 915490c8684SDarron Broad cmd.args[0x04] = 0x8f; 916490c8684SDarron Broad cmd.args[0x05] = 0x28; 917490c8684SDarron Broad cmd.args[0x06] = (toneburst == CX24116_DISEQC_TONEOFF) ? 0x00 : 0x01; 918490c8684SDarron Broad cmd.args[0x07] = 0x01; 919490c8684SDarron Broad cmd.len = 0x08; 920490c8684SDarron Broad ret = cx24116_cmd_execute(fe, &cmd); 921490c8684SDarron Broad if (ret != 0) 922490c8684SDarron Broad return ret; 923490c8684SDarron Broad 924490c8684SDarron Broad /* Prepare a DiSEqC command */ 925490c8684SDarron Broad state->dsec_cmd.args[0x00] = CMD_LNBSEND; 926490c8684SDarron Broad 927490c8684SDarron Broad /* DiSEqC burst */ 928490c8684SDarron Broad state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A; 929490c8684SDarron Broad 930490c8684SDarron Broad /* Unknown */ 931490c8684SDarron Broad state->dsec_cmd.args[CX24116_DISEQC_ARG2_2] = 0x02; 932490c8684SDarron Broad state->dsec_cmd.args[CX24116_DISEQC_ARG3_0] = 0x00; 93398c94823SSteven Toth /* Continuation flag? */ 93498c94823SSteven Toth state->dsec_cmd.args[CX24116_DISEQC_ARG4_0] = 0x00; 935490c8684SDarron Broad 936490c8684SDarron Broad /* DiSEqC message length */ 937490c8684SDarron Broad state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = 0x00; 938490c8684SDarron Broad 939490c8684SDarron Broad /* Command length */ 940490c8684SDarron Broad state->dsec_cmd.len = CX24116_DISEQC_MSGOFS; 9410d46748cSSteven Toth 9420d46748cSSteven Toth return 0; 9430d46748cSSteven Toth } 9440d46748cSSteven Toth 9450d46748cSSteven Toth /* Send DiSEqC message with derived burst (hack) || previous burst */ 946f11ec7d4SSteven Toth static int cx24116_send_diseqc_msg(struct dvb_frontend *fe, 947f11ec7d4SSteven Toth struct dvb_diseqc_master_cmd *d) 9480d46748cSSteven Toth { 9490d46748cSSteven Toth struct cx24116_state *state = fe->demodulator_priv; 9500d46748cSSteven Toth int i, ret; 9510d46748cSSteven Toth 9521fa2337aSMauro Carvalho Chehab /* Validate length */ 9531fa2337aSMauro Carvalho Chehab if (d->msg_len > sizeof(d->msg)) 9541fa2337aSMauro Carvalho Chehab return -EINVAL; 9551fa2337aSMauro Carvalho Chehab 9560d46748cSSteven Toth /* Dump DiSEqC message */ 9570d46748cSSteven Toth if (debug) { 95898c94823SSteven Toth printk(KERN_INFO "cx24116: %s(", __func__); 9590d46748cSSteven Toth for (i = 0 ; i < d->msg_len ;) { 96098c94823SSteven Toth printk(KERN_INFO "0x%02x", d->msg[i]); 9610d46748cSSteven Toth if (++i < d->msg_len) 96298c94823SSteven Toth printk(KERN_INFO ", "); 9630d46748cSSteven Toth } 964490c8684SDarron Broad printk(") toneburst=%d\n", toneburst); 9650d46748cSSteven Toth } 9660d46748cSSteven Toth 9670d46748cSSteven Toth /* DiSEqC message */ 9680d46748cSSteven Toth for (i = 0; i < d->msg_len; i++) 969490c8684SDarron Broad state->dsec_cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i]; 9700d46748cSSteven Toth 971490c8684SDarron Broad /* DiSEqC message length */ 972490c8684SDarron Broad state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len; 973490c8684SDarron Broad 974490c8684SDarron Broad /* Command length */ 975f11ec7d4SSteven Toth state->dsec_cmd.len = CX24116_DISEQC_MSGOFS + 976f11ec7d4SSteven Toth state->dsec_cmd.args[CX24116_DISEQC_MSGLEN]; 977490c8684SDarron Broad 978490c8684SDarron Broad /* DiSEqC toneburst */ 979490c8684SDarron Broad if (toneburst == CX24116_DISEQC_MESGCACHE) 980490c8684SDarron Broad /* Message is cached */ 981490c8684SDarron Broad return 0; 982490c8684SDarron Broad 983490c8684SDarron Broad else if (toneburst == CX24116_DISEQC_TONEOFF) 984490c8684SDarron Broad /* Message is sent without burst */ 985490c8684SDarron Broad state->dsec_cmd.args[CX24116_DISEQC_BURST] = 0; 986490c8684SDarron Broad 987490c8684SDarron Broad else if (toneburst == CX24116_DISEQC_TONECACHE) { 988490c8684SDarron Broad /* 989490c8684SDarron Broad * Message is sent with derived else cached burst 990490c8684SDarron Broad * 991490c8684SDarron Broad * WRITE PORT GROUP COMMAND 38 992490c8684SDarron Broad * 993490c8684SDarron Broad * 0/A/A: E0 10 38 F0..F3 994490c8684SDarron Broad * 1/B/B: E0 10 38 F4..F7 995490c8684SDarron Broad * 2/C/A: E0 10 38 F8..FB 996490c8684SDarron Broad * 3/D/B: E0 10 38 FC..FF 997490c8684SDarron Broad * 9987396d3eaSDarron Broad * databyte[3]= 8421:8421 999490c8684SDarron Broad * ABCD:WXYZ 1000490c8684SDarron Broad * CLR :SET 1001490c8684SDarron Broad * 1002490c8684SDarron Broad * WX= PORT SELECT 0..3 (X=TONEBURST) 1003490c8684SDarron Broad * Y = VOLTAGE (0=13V, 1=18V) 1004490c8684SDarron Broad * Z = BAND (0=LOW, 1=HIGH(22K)) 1005490c8684SDarron Broad */ 10060d46748cSSteven Toth if (d->msg_len >= 4 && d->msg[2] == 0x38) 1007f11ec7d4SSteven Toth state->dsec_cmd.args[CX24116_DISEQC_BURST] = 1008f11ec7d4SSteven Toth ((d->msg[3] & 4) >> 2); 1009490c8684SDarron Broad if (debug) 1010f11ec7d4SSteven Toth dprintk("%s burst=%d\n", __func__, 1011f11ec7d4SSteven Toth state->dsec_cmd.args[CX24116_DISEQC_BURST]); 1012490c8684SDarron Broad } 10130d46748cSSteven Toth 1014490c8684SDarron Broad /* Wait for LNB ready */ 1015490c8684SDarron Broad ret = cx24116_wait_for_lnb(fe); 1016490c8684SDarron Broad if (ret != 0) 1017490c8684SDarron Broad return ret; 10180d46748cSSteven Toth 1019490c8684SDarron Broad /* Wait for voltage/min repeat delay */ 1020490c8684SDarron Broad msleep(100); 10210d46748cSSteven Toth 1022490c8684SDarron Broad /* Command */ 1023490c8684SDarron Broad ret = cx24116_cmd_execute(fe, &state->dsec_cmd); 1024490c8684SDarron Broad if (ret != 0) 1025490c8684SDarron Broad return ret; 1026490c8684SDarron Broad /* 1027490c8684SDarron Broad * Wait for send 10280d46748cSSteven Toth * 10290d46748cSSteven Toth * Eutelsat spec: 1030490c8684SDarron Broad * >15ms delay + (XXX determine if FW does this, see set_tone) 10310d46748cSSteven Toth * 13.5ms per byte + 10320d46748cSSteven Toth * >15ms delay + 10330d46748cSSteven Toth * 12.5ms burst + 1034490c8684SDarron Broad * >15ms delay (XXX determine if FW does this, see set_tone) 10350d46748cSSteven Toth */ 1036f11ec7d4SSteven Toth msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 1037f11ec7d4SSteven Toth ((toneburst == CX24116_DISEQC_TONEOFF) ? 30 : 60)); 10380d46748cSSteven Toth 1039490c8684SDarron Broad return 0; 10400d46748cSSteven Toth } 10410d46748cSSteven Toth 10420d46748cSSteven Toth /* Send DiSEqC burst */ 1043f11ec7d4SSteven Toth static int cx24116_diseqc_send_burst(struct dvb_frontend *fe, 10440df289a2SMauro Carvalho Chehab enum fe_sec_mini_cmd burst) 10450d46748cSSteven Toth { 10460d46748cSSteven Toth struct cx24116_state *state = fe->demodulator_priv; 10470d46748cSSteven Toth int ret; 10480d46748cSSteven Toth 1049490c8684SDarron Broad dprintk("%s(%d) toneburst=%d\n", __func__, burst, toneburst); 10500d46748cSSteven Toth 1051490c8684SDarron Broad /* DiSEqC burst */ 10520d46748cSSteven Toth if (burst == SEC_MINI_A) 1053f11ec7d4SSteven Toth state->dsec_cmd.args[CX24116_DISEQC_BURST] = 1054f11ec7d4SSteven Toth CX24116_DISEQC_MINI_A; 10550d46748cSSteven Toth else if (burst == SEC_MINI_B) 1056f11ec7d4SSteven Toth state->dsec_cmd.args[CX24116_DISEQC_BURST] = 1057f11ec7d4SSteven Toth CX24116_DISEQC_MINI_B; 10580d46748cSSteven Toth else 10590d46748cSSteven Toth return -EINVAL; 10600d46748cSSteven Toth 1061490c8684SDarron Broad /* DiSEqC toneburst */ 1062490c8684SDarron Broad if (toneburst != CX24116_DISEQC_MESGCACHE) 1063490c8684SDarron Broad /* Burst is cached */ 1064490c8684SDarron Broad return 0; 10650d46748cSSteven Toth 1066490c8684SDarron Broad /* Burst is to be sent with cached message */ 10670d46748cSSteven Toth 1068490c8684SDarron Broad /* Wait for LNB ready */ 1069490c8684SDarron Broad ret = cx24116_wait_for_lnb(fe); 1070490c8684SDarron Broad if (ret != 0) 10710d46748cSSteven Toth return ret; 1072490c8684SDarron Broad 1073490c8684SDarron Broad /* Wait for voltage/min repeat delay */ 1074490c8684SDarron Broad msleep(100); 1075490c8684SDarron Broad 1076490c8684SDarron Broad /* Command */ 1077490c8684SDarron Broad ret = cx24116_cmd_execute(fe, &state->dsec_cmd); 1078490c8684SDarron Broad if (ret != 0) 1079490c8684SDarron Broad return ret; 1080490c8684SDarron Broad 1081490c8684SDarron Broad /* 1082490c8684SDarron Broad * Wait for send 1083490c8684SDarron Broad * 1084490c8684SDarron Broad * Eutelsat spec: 1085490c8684SDarron Broad * >15ms delay + (XXX determine if FW does this, see set_tone) 1086490c8684SDarron Broad * 13.5ms per byte + 1087490c8684SDarron Broad * >15ms delay + 1088490c8684SDarron Broad * 12.5ms burst + 1089490c8684SDarron Broad * >15ms delay (XXX determine if FW does this, see set_tone) 1090490c8684SDarron Broad */ 1091490c8684SDarron Broad msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60); 1092490c8684SDarron Broad 1093490c8684SDarron Broad return 0; 10940d46748cSSteven Toth } 10950d46748cSSteven Toth 10960d46748cSSteven Toth static void cx24116_release(struct dvb_frontend *fe) 10970d46748cSSteven Toth { 10980d46748cSSteven Toth struct cx24116_state *state = fe->demodulator_priv; 10990d46748cSSteven Toth dprintk("%s\n", __func__); 11000d46748cSSteven Toth kfree(state); 11010d46748cSSteven Toth } 11020d46748cSSteven Toth 1103bd336e63SMax Kellermann static const struct dvb_frontend_ops cx24116_ops; 11040d46748cSSteven Toth 11050d46748cSSteven Toth struct dvb_frontend *cx24116_attach(const struct cx24116_config *config, 11060d46748cSSteven Toth struct i2c_adapter *i2c) 11070d46748cSSteven Toth { 1108d303b7c5SMarkus Elfring struct cx24116_state *state; 11090d46748cSSteven Toth int ret; 11100d46748cSSteven Toth 11110d46748cSSteven Toth dprintk("%s\n", __func__); 11120d46748cSSteven Toth 11130d46748cSSteven Toth /* allocate memory for the internal state */ 11142d3da59fSMarkus Elfring state = kzalloc(sizeof(*state), GFP_KERNEL); 111598c94823SSteven Toth if (state == NULL) 11169722e569SMarkus Elfring return NULL; 11170d46748cSSteven Toth 11180d46748cSSteven Toth state->config = config; 11190d46748cSSteven Toth state->i2c = i2c; 11200d46748cSSteven Toth 11210d46748cSSteven Toth /* check if the demod is present */ 112298c94823SSteven Toth ret = (cx24116_readreg(state, 0xFF) << 8) | 112398c94823SSteven Toth cx24116_readreg(state, 0xFE); 11240d46748cSSteven Toth if (ret != 0x0501) { 11259722e569SMarkus Elfring kfree(state); 112698c94823SSteven Toth printk(KERN_INFO "Invalid probe, probably not a CX24116 device\n"); 11279722e569SMarkus Elfring return NULL; 11280d46748cSSteven Toth } 11290d46748cSSteven Toth 11300d46748cSSteven Toth /* create dvb_frontend */ 113198c94823SSteven Toth memcpy(&state->frontend.ops, &cx24116_ops, 113298c94823SSteven Toth sizeof(struct dvb_frontend_ops)); 11330d46748cSSteven Toth state->frontend.demodulator_priv = state; 11340d46748cSSteven Toth return &state->frontend; 11350d46748cSSteven Toth } 1136f11ec7d4SSteven Toth EXPORT_SYMBOL(cx24116_attach); 1137f11ec7d4SSteven Toth 1138490c8684SDarron Broad /* 1139490c8684SDarron Broad * Initialise or wake up device 1140490c8684SDarron Broad * 1141490c8684SDarron Broad * Power config will reset and load initial firmware if required 1142490c8684SDarron Broad */ 11430d46748cSSteven Toth static int cx24116_initfe(struct dvb_frontend *fe) 11440d46748cSSteven Toth { 1145490c8684SDarron Broad struct cx24116_state *state = fe->demodulator_priv; 1146490c8684SDarron Broad struct cx24116_cmd cmd; 1147490c8684SDarron Broad int ret; 1148490c8684SDarron Broad 11490d46748cSSteven Toth dprintk("%s()\n", __func__); 11500d46748cSSteven Toth 1151490c8684SDarron Broad /* Power on */ 1152490c8684SDarron Broad cx24116_writereg(state, 0xe0, 0); 1153490c8684SDarron Broad cx24116_writereg(state, 0xe1, 0); 1154490c8684SDarron Broad cx24116_writereg(state, 0xea, 0); 1155490c8684SDarron Broad 1156490c8684SDarron Broad /* Firmware CMD 36: Power config */ 1157490c8684SDarron Broad cmd.args[0x00] = CMD_TUNERSLEEP; 1158490c8684SDarron Broad cmd.args[0x01] = 0; 1159490c8684SDarron Broad cmd.len = 0x02; 1160490c8684SDarron Broad ret = cx24116_cmd_execute(fe, &cmd); 1161490c8684SDarron Broad if (ret != 0) 1162490c8684SDarron Broad return ret; 1163490c8684SDarron Broad 11648afe6ad6SIgor M. Liplianin ret = cx24116_diseqc_init(fe); 11658afe6ad6SIgor M. Liplianin if (ret != 0) 11668afe6ad6SIgor M. Liplianin return ret; 11678afe6ad6SIgor M. Liplianin 11688afe6ad6SIgor M. Liplianin /* HVR-4000 needs this */ 11698afe6ad6SIgor M. Liplianin return cx24116_set_voltage(fe, SEC_VOLTAGE_13); 11700d46748cSSteven Toth } 11710d46748cSSteven Toth 1172490c8684SDarron Broad /* 1173490c8684SDarron Broad * Put device to sleep 1174490c8684SDarron Broad */ 1175490c8684SDarron Broad static int cx24116_sleep(struct dvb_frontend *fe) 1176490c8684SDarron Broad { 1177490c8684SDarron Broad struct cx24116_state *state = fe->demodulator_priv; 1178490c8684SDarron Broad struct cx24116_cmd cmd; 1179490c8684SDarron Broad int ret; 1180490c8684SDarron Broad 1181490c8684SDarron Broad dprintk("%s()\n", __func__); 1182490c8684SDarron Broad 1183490c8684SDarron Broad /* Firmware CMD 36: Power config */ 1184490c8684SDarron Broad cmd.args[0x00] = CMD_TUNERSLEEP; 1185490c8684SDarron Broad cmd.args[0x01] = 1; 1186490c8684SDarron Broad cmd.len = 0x02; 1187490c8684SDarron Broad ret = cx24116_cmd_execute(fe, &cmd); 1188490c8684SDarron Broad if (ret != 0) 1189490c8684SDarron Broad return ret; 1190490c8684SDarron Broad 1191490c8684SDarron Broad /* Power off (Shutdown clocks) */ 1192490c8684SDarron Broad cx24116_writereg(state, 0xea, 0xff); 1193490c8684SDarron Broad cx24116_writereg(state, 0xe1, 1); 1194490c8684SDarron Broad cx24116_writereg(state, 0xe0, 1); 1195490c8684SDarron Broad 1196490c8684SDarron Broad return 0; 1197490c8684SDarron Broad } 1198490c8684SDarron Broad 11990d46748cSSteven Toth /* dvb-core told us to tune, the tv property cache will be complete, 12000d46748cSSteven Toth * it's safe for is to pull values and use them for tuning purposes. 12010d46748cSSteven Toth */ 12021ac6a854SMauro Carvalho Chehab static int cx24116_set_frontend(struct dvb_frontend *fe) 12030d46748cSSteven Toth { 12040d46748cSSteven Toth struct cx24116_state *state = fe->demodulator_priv; 120556f0680aSSteven Toth struct dtv_frontend_properties *c = &fe->dtv_property_cache; 12060d46748cSSteven Toth struct cx24116_cmd cmd; 12070df289a2SMauro Carvalho Chehab enum fe_status tunerstat; 12082fd93396SDarron Broad int i, status, ret, retune = 1; 12090d46748cSSteven Toth 12100d46748cSSteven Toth dprintk("%s()\n", __func__); 12110d46748cSSteven Toth 1212490c8684SDarron Broad switch (c->delivery_system) { 1213490c8684SDarron Broad case SYS_DVBS: 1214490c8684SDarron Broad dprintk("%s: DVB-S delivery system selected\n", __func__); 121501a8f038SDarron Broad 121601a8f038SDarron Broad /* Only QPSK is supported for DVB-S */ 121701a8f038SDarron Broad if (c->modulation != QPSK) { 121801a8f038SDarron Broad dprintk("%s: unsupported modulation selected (%d)\n", 121901a8f038SDarron Broad __func__, c->modulation); 122001a8f038SDarron Broad return -EOPNOTSUPP; 122101a8f038SDarron Broad } 122201a8f038SDarron Broad 122301a8f038SDarron Broad /* Pilot doesn't exist in DVB-S, turn bit off */ 122401a8f038SDarron Broad state->dnxt.pilot_val = CX24116_PILOT_OFF; 122501a8f038SDarron Broad 122601a8f038SDarron Broad /* DVB-S only supports 0.35 */ 122701a8f038SDarron Broad if (c->rolloff != ROLLOFF_35) { 122801a8f038SDarron Broad dprintk("%s: unsupported rolloff selected (%d)\n", 122901a8f038SDarron Broad __func__, c->rolloff); 123001a8f038SDarron Broad return -EOPNOTSUPP; 123101a8f038SDarron Broad } 12327396d3eaSDarron Broad state->dnxt.rolloff_val = CX24116_ROLLOFF_035; 1233490c8684SDarron Broad break; 123401a8f038SDarron Broad 1235490c8684SDarron Broad case SYS_DVBS2: 1236490c8684SDarron Broad dprintk("%s: DVB-S2 delivery system selected\n", __func__); 123701a8f038SDarron Broad 123801a8f038SDarron Broad /* 123901a8f038SDarron Broad * NBC 8PSK/QPSK with DVB-S is supported for DVB-S2, 124001a8f038SDarron Broad * but not hardware auto detection 124101a8f038SDarron Broad */ 12420a6393aeSSteven Toth if (c->modulation != PSK_8 && c->modulation != QPSK) { 124301a8f038SDarron Broad dprintk("%s: unsupported modulation selected (%d)\n", 124401a8f038SDarron Broad __func__, c->modulation); 124501a8f038SDarron Broad return -EOPNOTSUPP; 124601a8f038SDarron Broad } 124701a8f038SDarron Broad 124801a8f038SDarron Broad switch (c->pilot) { 124901a8f038SDarron Broad case PILOT_AUTO: /* Not supported but emulated */ 125074563214SChristophe Thommeret state->dnxt.pilot_val = (c->modulation == QPSK) 125174563214SChristophe Thommeret ? CX24116_PILOT_OFF : CX24116_PILOT_ON; 12522fd93396SDarron Broad retune++; 125374563214SChristophe Thommeret break; 125401a8f038SDarron Broad case PILOT_OFF: 125501a8f038SDarron Broad state->dnxt.pilot_val = CX24116_PILOT_OFF; 125601a8f038SDarron Broad break; 125701a8f038SDarron Broad case PILOT_ON: 125801a8f038SDarron Broad state->dnxt.pilot_val = CX24116_PILOT_ON; 125901a8f038SDarron Broad break; 126001a8f038SDarron Broad default: 126101a8f038SDarron Broad dprintk("%s: unsupported pilot mode selected (%d)\n", 126201a8f038SDarron Broad __func__, c->pilot); 126301a8f038SDarron Broad return -EOPNOTSUPP; 126401a8f038SDarron Broad } 126501a8f038SDarron Broad 1266490c8684SDarron Broad switch (c->rolloff) { 1267490c8684SDarron Broad case ROLLOFF_20: 1268490c8684SDarron Broad state->dnxt.rolloff_val = CX24116_ROLLOFF_020; 1269490c8684SDarron Broad break; 1270490c8684SDarron Broad case ROLLOFF_25: 1271490c8684SDarron Broad state->dnxt.rolloff_val = CX24116_ROLLOFF_025; 1272490c8684SDarron Broad break; 1273490c8684SDarron Broad case ROLLOFF_35: 1274490c8684SDarron Broad state->dnxt.rolloff_val = CX24116_ROLLOFF_035; 1275490c8684SDarron Broad break; 127601a8f038SDarron Broad case ROLLOFF_AUTO: /* Rolloff must be explicit */ 127701a8f038SDarron Broad default: 127801a8f038SDarron Broad dprintk("%s: unsupported rolloff selected (%d)\n", 127901a8f038SDarron Broad __func__, c->rolloff); 1280490c8684SDarron Broad return -EOPNOTSUPP; 1281490c8684SDarron Broad } 1282490c8684SDarron Broad break; 128301a8f038SDarron Broad 1284490c8684SDarron Broad default: 1285490c8684SDarron Broad dprintk("%s: unsupported delivery system selected (%d)\n", 1286490c8684SDarron Broad __func__, c->delivery_system); 1287490c8684SDarron Broad return -EOPNOTSUPP; 1288490c8684SDarron Broad } 12893569476dSDarron Broad state->dnxt.delsys = c->delivery_system; 129001a8f038SDarron Broad state->dnxt.modulation = c->modulation; 129101a8f038SDarron Broad state->dnxt.frequency = c->frequency; 129201a8f038SDarron Broad state->dnxt.pilot = c->pilot; 129301a8f038SDarron Broad state->dnxt.rolloff = c->rolloff; 1294490c8684SDarron Broad 1295f11ec7d4SSteven Toth ret = cx24116_set_inversion(state, c->inversion); 1296f11ec7d4SSteven Toth if (ret != 0) 12970d46748cSSteven Toth return ret; 12980d46748cSSteven Toth 129901a8f038SDarron Broad /* FEC_NONE/AUTO for DVB-S2 is not supported and detected here */ 13003569476dSDarron Broad ret = cx24116_set_fec(state, c->delivery_system, c->modulation, c->fec_inner); 1301f11ec7d4SSteven Toth if (ret != 0) 13020d46748cSSteven Toth return ret; 13030d46748cSSteven Toth 1304f11ec7d4SSteven Toth ret = cx24116_set_symbolrate(state, c->symbol_rate); 1305f11ec7d4SSteven Toth if (ret != 0) 13060d46748cSSteven Toth return ret; 13070d46748cSSteven Toth 13080d46748cSSteven Toth /* discard the 'current' tuning parameters and prepare to tune */ 13090d46748cSSteven Toth cx24116_clone_params(fe); 13100d46748cSSteven Toth 13113569476dSDarron Broad dprintk("%s: delsys = %d\n", __func__, state->dcur.delsys); 131201a8f038SDarron Broad dprintk("%s: modulation = %d\n", __func__, state->dcur.modulation); 13130d46748cSSteven Toth dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency); 131401a8f038SDarron Broad dprintk("%s: pilot = %d (val = 0x%02x)\n", __func__, 131501a8f038SDarron Broad state->dcur.pilot, state->dcur.pilot_val); 131601a8f038SDarron Broad dprintk("%s: retune = %d\n", __func__, retune); 131701a8f038SDarron Broad dprintk("%s: rolloff = %d (val = 0x%02x)\n", __func__, 131801a8f038SDarron Broad state->dcur.rolloff, state->dcur.rolloff_val); 13190d46748cSSteven Toth dprintk("%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate); 13200d46748cSSteven Toth dprintk("%s: FEC = %d (mask/val = 0x%02x/0x%02x)\n", __func__, 13210d46748cSSteven Toth state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val); 13220d46748cSSteven Toth dprintk("%s: Inversion = %d (val = 0x%02x)\n", __func__, 13230d46748cSSteven Toth state->dcur.inversion, state->dcur.inversion_val); 13240d46748cSSteven Toth 1325490c8684SDarron Broad /* This is also done in advise/acquire on HVR4000 but not on LITE */ 13260d46748cSSteven Toth if (state->config->set_ts_params) 13270d46748cSSteven Toth state->config->set_ts_params(fe, 0); 13280d46748cSSteven Toth 1329490c8684SDarron Broad /* Set/Reset B/W */ 1330490c8684SDarron Broad cmd.args[0x00] = CMD_BANDWIDTH; 1331490c8684SDarron Broad cmd.args[0x01] = 0x01; 1332490c8684SDarron Broad cmd.len = 0x02; 1333490c8684SDarron Broad ret = cx24116_cmd_execute(fe, &cmd); 1334490c8684SDarron Broad if (ret != 0) 1335490c8684SDarron Broad return ret; 13363f8e51adSIgor M. Liplianin 13370d46748cSSteven Toth /* Prepare a tune request */ 13380d46748cSSteven Toth cmd.args[0x00] = CMD_TUNEREQUEST; 13390d46748cSSteven Toth 13400d46748cSSteven Toth /* Frequency */ 13410d46748cSSteven Toth cmd.args[0x01] = (state->dcur.frequency & 0xff0000) >> 16; 13420d46748cSSteven Toth cmd.args[0x02] = (state->dcur.frequency & 0x00ff00) >> 8; 13430d46748cSSteven Toth cmd.args[0x03] = (state->dcur.frequency & 0x0000ff); 13440d46748cSSteven Toth 13450d46748cSSteven Toth /* Symbol Rate */ 13460d46748cSSteven Toth cmd.args[0x04] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8; 13470d46748cSSteven Toth cmd.args[0x05] = ((state->dcur.symbol_rate / 1000) & 0x00ff); 13480d46748cSSteven Toth 13490d46748cSSteven Toth /* Automatic Inversion */ 13500d46748cSSteven Toth cmd.args[0x06] = state->dcur.inversion_val; 13510d46748cSSteven Toth 135201a8f038SDarron Broad /* Modulation / FEC / Pilot */ 135301a8f038SDarron Broad cmd.args[0x07] = state->dcur.fec_val | state->dcur.pilot_val; 13540d46748cSSteven Toth 13550d46748cSSteven Toth cmd.args[0x08] = CX24116_SEARCH_RANGE_KHZ >> 8; 13560d46748cSSteven Toth cmd.args[0x09] = CX24116_SEARCH_RANGE_KHZ & 0xff; 13570d46748cSSteven Toth cmd.args[0x0a] = 0x00; 13580d46748cSSteven Toth cmd.args[0x0b] = 0x00; 1359490c8684SDarron Broad cmd.args[0x0c] = state->dcur.rolloff_val; 13600d46748cSSteven Toth cmd.args[0x0d] = state->dcur.fec_mask; 13613f8e51adSIgor M. Liplianin 1362490c8684SDarron Broad if (state->dcur.symbol_rate > 30000000) { 13633f8e51adSIgor M. Liplianin cmd.args[0x0e] = 0x04; 13643f8e51adSIgor M. Liplianin cmd.args[0x0f] = 0x00; 13653f8e51adSIgor M. Liplianin cmd.args[0x10] = 0x01; 13663f8e51adSIgor M. Liplianin cmd.args[0x11] = 0x77; 13673f8e51adSIgor M. Liplianin cmd.args[0x12] = 0x36; 1368490c8684SDarron Broad cx24116_writereg(state, CX24116_REG_CLKDIV, 0x44); 1369490c8684SDarron Broad cx24116_writereg(state, CX24116_REG_RATEDIV, 0x01); 13703f8e51adSIgor M. Liplianin } else { 13710d46748cSSteven Toth cmd.args[0x0e] = 0x06; 13720d46748cSSteven Toth cmd.args[0x0f] = 0x00; 13730d46748cSSteven Toth cmd.args[0x10] = 0x00; 13740d46748cSSteven Toth cmd.args[0x11] = 0xFA; 13750d46748cSSteven Toth cmd.args[0x12] = 0x24; 1376490c8684SDarron Broad cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46); 1377490c8684SDarron Broad cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00); 13783f8e51adSIgor M. Liplianin } 13793f8e51adSIgor M. Liplianin 13800d46748cSSteven Toth cmd.len = 0x13; 13810d46748cSSteven Toth 13820d46748cSSteven Toth /* We need to support pilot and non-pilot tuning in the 13830d46748cSSteven Toth * driver automatically. This is a workaround for because 13840d46748cSSteven Toth * the demod does not support autodetect. 13850d46748cSSteven Toth */ 13860d46748cSSteven Toth do { 1387490c8684SDarron Broad /* Reset status register */ 1388f11ec7d4SSteven Toth status = cx24116_readreg(state, CX24116_REG_SSTATUS) 1389f11ec7d4SSteven Toth & CX24116_SIGNAL_MASK; 1390490c8684SDarron Broad cx24116_writereg(state, CX24116_REG_SSTATUS, status); 13910d46748cSSteven Toth 13920d46748cSSteven Toth /* Tune */ 13930d46748cSSteven Toth ret = cx24116_cmd_execute(fe, &cmd); 13940d46748cSSteven Toth if (ret != 0) 13950d46748cSSteven Toth break; 13960d46748cSSteven Toth 1397490c8684SDarron Broad /* 1398490c8684SDarron Broad * Wait for up to 500 ms before retrying 1399490c8684SDarron Broad * 1400490c8684SDarron Broad * If we are able to tune then generally it occurs within 100ms. 1401490c8684SDarron Broad * If it takes longer, try a different toneburst setting. 1402490c8684SDarron Broad */ 1403490c8684SDarron Broad for (i = 0; i < 50 ; i++) { 14040d46748cSSteven Toth cx24116_read_status(fe, &tunerstat); 1405490c8684SDarron Broad status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC); 1406490c8684SDarron Broad if (status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) { 1407490c8684SDarron Broad dprintk("%s: Tuned\n", __func__); 1408490c8684SDarron Broad goto tuned; 14090d46748cSSteven Toth } 1410490c8684SDarron Broad msleep(10); 1411490c8684SDarron Broad } 1412490c8684SDarron Broad 1413490c8684SDarron Broad dprintk("%s: Not tuned\n", __func__); 1414490c8684SDarron Broad 1415490c8684SDarron Broad /* Toggle pilot bit when in auto-pilot */ 1416490c8684SDarron Broad if (state->dcur.pilot == PILOT_AUTO) 141701a8f038SDarron Broad cmd.args[0x07] ^= CX24116_PILOT_ON; 1418f11ec7d4SSteven Toth } while (--retune); 14190d46748cSSteven Toth 1420490c8684SDarron Broad tuned: /* Set/Reset B/W */ 1421490c8684SDarron Broad cmd.args[0x00] = CMD_BANDWIDTH; 1422490c8684SDarron Broad cmd.args[0x01] = 0x00; 1423490c8684SDarron Broad cmd.len = 0x02; 14243735edf9SGreg Dietsche return cx24116_cmd_execute(fe, &cmd); 14250d46748cSSteven Toth } 14260d46748cSSteven Toth 14277e072221SMauro Carvalho Chehab static int cx24116_tune(struct dvb_frontend *fe, bool re_tune, 14280df289a2SMauro Carvalho Chehab unsigned int mode_flags, unsigned int *delay, enum fe_status *status) 14296639f1e0SDarron Broad { 14301ac6a854SMauro Carvalho Chehab /* 14311ac6a854SMauro Carvalho Chehab * It is safe to discard "params" here, as the DVB core will sync 14321ac6a854SMauro Carvalho Chehab * fe->dtv_property_cache with fepriv->parameters_in, where the 14331ac6a854SMauro Carvalho Chehab * DVBv3 params are stored. The only practical usage for it indicate 14341ac6a854SMauro Carvalho Chehab * that re-tuning is needed, e. g. (fepriv->state & FESTATE_RETUNE) is 14351ac6a854SMauro Carvalho Chehab * true. 14361ac6a854SMauro Carvalho Chehab */ 14371ac6a854SMauro Carvalho Chehab 14386639f1e0SDarron Broad *delay = HZ / 5; 14397e072221SMauro Carvalho Chehab if (re_tune) { 14401ac6a854SMauro Carvalho Chehab int ret = cx24116_set_frontend(fe); 14416639f1e0SDarron Broad if (ret) 14426639f1e0SDarron Broad return ret; 14436639f1e0SDarron Broad } 14446639f1e0SDarron Broad return cx24116_read_status(fe, status); 14456639f1e0SDarron Broad } 14466639f1e0SDarron Broad 14478d718e53SLuc Van Oostenryck static enum dvbfe_algo cx24116_get_algo(struct dvb_frontend *fe) 14486639f1e0SDarron Broad { 14496639f1e0SDarron Broad return DVBFE_ALGO_HW; 14506639f1e0SDarron Broad } 14516639f1e0SDarron Broad 1452bd336e63SMax Kellermann static const struct dvb_frontend_ops cx24116_ops = { 14531ac6a854SMauro Carvalho Chehab .delsys = { SYS_DVBS, SYS_DVBS2 }, 14540d46748cSSteven Toth .info = { 14550d46748cSSteven Toth .name = "Conexant CX24116/CX24118", 1456f1b1eabfSMauro Carvalho Chehab .frequency_min_hz = 950 * MHz, 1457f1b1eabfSMauro Carvalho Chehab .frequency_max_hz = 2150 * MHz, 1458f1b1eabfSMauro Carvalho Chehab .frequency_stepsize_hz = 1011 * kHz, 1459f1b1eabfSMauro Carvalho Chehab .frequency_tolerance_hz = 5 * MHz, 14600d46748cSSteven Toth .symbol_rate_min = 1000000, 14610d46748cSSteven Toth .symbol_rate_max = 45000000, 14620d46748cSSteven Toth .caps = FE_CAN_INVERSION_AUTO | 14630d46748cSSteven Toth FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 14640d46748cSSteven Toth FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | 14650d46748cSSteven Toth FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | 1466faed4aa5SKlaus Schmidinger FE_CAN_2G_MODULATION | 14670d46748cSSteven Toth FE_CAN_QPSK | FE_CAN_RECOVER 14680d46748cSSteven Toth }, 14690d46748cSSteven Toth 14700d46748cSSteven Toth .release = cx24116_release, 14710d46748cSSteven Toth 14720d46748cSSteven Toth .init = cx24116_initfe, 1473490c8684SDarron Broad .sleep = cx24116_sleep, 14740d46748cSSteven Toth .read_status = cx24116_read_status, 14750d46748cSSteven Toth .read_ber = cx24116_read_ber, 14760d46748cSSteven Toth .read_signal_strength = cx24116_read_signal_strength, 14770d46748cSSteven Toth .read_snr = cx24116_read_snr, 14780d46748cSSteven Toth .read_ucblocks = cx24116_read_ucblocks, 14790d46748cSSteven Toth .set_tone = cx24116_set_tone, 14800d46748cSSteven Toth .set_voltage = cx24116_set_voltage, 14810d46748cSSteven Toth .diseqc_send_master_cmd = cx24116_send_diseqc_msg, 14820d46748cSSteven Toth .diseqc_send_burst = cx24116_diseqc_send_burst, 14836639f1e0SDarron Broad .get_frontend_algo = cx24116_get_algo, 14846639f1e0SDarron Broad .tune = cx24116_tune, 14850d46748cSSteven Toth 14861ac6a854SMauro Carvalho Chehab .set_frontend = cx24116_set_frontend, 14870d46748cSSteven Toth }; 14880d46748cSSteven Toth 14890d46748cSSteven Toth MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24116/cx24118 hardware"); 14900d46748cSSteven Toth MODULE_AUTHOR("Steven Toth"); 14910d46748cSSteven Toth MODULE_LICENSE("GPL"); 14920d46748cSSteven Toth 1493