18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci Copyright (C) 2006-2008 Steven Toth <stoth@hauppauge.com> 68c2ecf20Sopenharmony_ci Copyright (C) 2006-2007 Georg Acher 78c2ecf20Sopenharmony_ci Copyright (C) 2007-2008 Darron Broad 88c2ecf20Sopenharmony_ci March 2007 98c2ecf20Sopenharmony_ci Fixed some bugs. 108c2ecf20Sopenharmony_ci Added diseqc support. 118c2ecf20Sopenharmony_ci Added corrected signal strength support. 128c2ecf20Sopenharmony_ci August 2007 138c2ecf20Sopenharmony_ci Sync with legacy version. 148c2ecf20Sopenharmony_ci Some clean ups. 158c2ecf20Sopenharmony_ci Copyright (C) 2008 Igor Liplianin 168c2ecf20Sopenharmony_ci September, 9th 2008 178c2ecf20Sopenharmony_ci Fixed locking on high symbol rates (>30000). 188c2ecf20Sopenharmony_ci Implement MPEG initialization parameter. 198c2ecf20Sopenharmony_ci January, 17th 2009 208c2ecf20Sopenharmony_ci Fill set_voltage with actually control voltage code. 218c2ecf20Sopenharmony_ci Correct set tone to not affect voltage. 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci*/ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <linux/kernel.h> 278c2ecf20Sopenharmony_ci#include <linux/module.h> 288c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 298c2ecf20Sopenharmony_ci#include <linux/init.h> 308c2ecf20Sopenharmony_ci#include <linux/firmware.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 338c2ecf20Sopenharmony_ci#include "cx24116.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic int debug; 368c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 378c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define dprintk(args...) \ 408c2ecf20Sopenharmony_ci do { \ 418c2ecf20Sopenharmony_ci if (debug) \ 428c2ecf20Sopenharmony_ci printk(KERN_INFO "cx24116: " args); \ 438c2ecf20Sopenharmony_ci } while (0) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw" 468c2ecf20Sopenharmony_ci#define CX24116_SEARCH_RANGE_KHZ 5000 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* known registers */ 498c2ecf20Sopenharmony_ci#define CX24116_REG_COMMAND (0x00) /* command args 0x00..0x1e */ 508c2ecf20Sopenharmony_ci#define CX24116_REG_EXECUTE (0x1f) /* execute command */ 518c2ecf20Sopenharmony_ci#define CX24116_REG_MAILBOX (0x96) /* FW or multipurpose mailbox? */ 528c2ecf20Sopenharmony_ci#define CX24116_REG_RESET (0x20) /* reset status > 0 */ 538c2ecf20Sopenharmony_ci#define CX24116_REG_SIGNAL (0x9e) /* signal low */ 548c2ecf20Sopenharmony_ci#define CX24116_REG_SSTATUS (0x9d) /* signal high / status */ 558c2ecf20Sopenharmony_ci#define CX24116_REG_QUALITY8 (0xa3) 568c2ecf20Sopenharmony_ci#define CX24116_REG_QSTATUS (0xbc) 578c2ecf20Sopenharmony_ci#define CX24116_REG_QUALITY0 (0xd5) 588c2ecf20Sopenharmony_ci#define CX24116_REG_BER0 (0xc9) 598c2ecf20Sopenharmony_ci#define CX24116_REG_BER8 (0xc8) 608c2ecf20Sopenharmony_ci#define CX24116_REG_BER16 (0xc7) 618c2ecf20Sopenharmony_ci#define CX24116_REG_BER24 (0xc6) 628c2ecf20Sopenharmony_ci#define CX24116_REG_UCB0 (0xcb) 638c2ecf20Sopenharmony_ci#define CX24116_REG_UCB8 (0xca) 648c2ecf20Sopenharmony_ci#define CX24116_REG_CLKDIV (0xf3) 658c2ecf20Sopenharmony_ci#define CX24116_REG_RATEDIV (0xf9) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* configured fec (not tuned) or actual FEC (tuned) 1=1/2 2=2/3 etc */ 688c2ecf20Sopenharmony_ci#define CX24116_REG_FECSTATUS (0x9c) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* FECSTATUS bits */ 718c2ecf20Sopenharmony_ci/* mask to determine configured fec (not tuned) or actual fec (tuned) */ 728c2ecf20Sopenharmony_ci#define CX24116_FEC_FECMASK (0x1f) 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* Select DVB-S demodulator, else DVB-S2 */ 758c2ecf20Sopenharmony_ci#define CX24116_FEC_DVBS (0x20) 768c2ecf20Sopenharmony_ci#define CX24116_FEC_UNKNOWN (0x40) /* Unknown/unused */ 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* Pilot mode requested when tuning else always reset when tuned */ 798c2ecf20Sopenharmony_ci#define CX24116_FEC_PILOT (0x80) 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* arg buffer size */ 828c2ecf20Sopenharmony_ci#define CX24116_ARGLEN (0x1e) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* rolloff */ 858c2ecf20Sopenharmony_ci#define CX24116_ROLLOFF_020 (0x00) 868c2ecf20Sopenharmony_ci#define CX24116_ROLLOFF_025 (0x01) 878c2ecf20Sopenharmony_ci#define CX24116_ROLLOFF_035 (0x02) 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* pilot bit */ 908c2ecf20Sopenharmony_ci#define CX24116_PILOT_OFF (0x00) 918c2ecf20Sopenharmony_ci#define CX24116_PILOT_ON (0x40) 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* signal status */ 948c2ecf20Sopenharmony_ci#define CX24116_HAS_SIGNAL (0x01) 958c2ecf20Sopenharmony_ci#define CX24116_HAS_CARRIER (0x02) 968c2ecf20Sopenharmony_ci#define CX24116_HAS_VITERBI (0x04) 978c2ecf20Sopenharmony_ci#define CX24116_HAS_SYNCLOCK (0x08) 988c2ecf20Sopenharmony_ci#define CX24116_HAS_UNKNOWN1 (0x10) 998c2ecf20Sopenharmony_ci#define CX24116_HAS_UNKNOWN2 (0x20) 1008c2ecf20Sopenharmony_ci#define CX24116_STATUS_MASK (0x0f) 1018c2ecf20Sopenharmony_ci#define CX24116_SIGNAL_MASK (0xc0) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#define CX24116_DISEQC_TONEOFF (0) /* toneburst never sent */ 1048c2ecf20Sopenharmony_ci#define CX24116_DISEQC_TONECACHE (1) /* toneburst cached */ 1058c2ecf20Sopenharmony_ci#define CX24116_DISEQC_MESGCACHE (2) /* message cached */ 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* arg offset for DiSEqC */ 1088c2ecf20Sopenharmony_ci#define CX24116_DISEQC_BURST (1) 1098c2ecf20Sopenharmony_ci#define CX24116_DISEQC_ARG2_2 (2) /* unknown value=2 */ 1108c2ecf20Sopenharmony_ci#define CX24116_DISEQC_ARG3_0 (3) /* unknown value=0 */ 1118c2ecf20Sopenharmony_ci#define CX24116_DISEQC_ARG4_0 (4) /* unknown value=0 */ 1128c2ecf20Sopenharmony_ci#define CX24116_DISEQC_MSGLEN (5) 1138c2ecf20Sopenharmony_ci#define CX24116_DISEQC_MSGOFS (6) 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* DiSEqC burst */ 1168c2ecf20Sopenharmony_ci#define CX24116_DISEQC_MINI_A (0) 1178c2ecf20Sopenharmony_ci#define CX24116_DISEQC_MINI_B (1) 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* DiSEqC tone burst */ 1208c2ecf20Sopenharmony_cistatic int toneburst = 1; 1218c2ecf20Sopenharmony_cimodule_param(toneburst, int, 0644); 1228c2ecf20Sopenharmony_ciMODULE_PARM_DESC(toneburst, "DiSEqC toneburst 0=OFF, 1=TONE CACHE, "\ 1238c2ecf20Sopenharmony_ci "2=MESSAGE CACHE (default:1)"); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* SNR measurements */ 1268c2ecf20Sopenharmony_cistatic int esno_snr; 1278c2ecf20Sopenharmony_cimodule_param(esno_snr, int, 0644); 1288c2ecf20Sopenharmony_ciMODULE_PARM_DESC(esno_snr, "SNR return units, 0=PERCENTAGE 0-100, "\ 1298c2ecf20Sopenharmony_ci "1=ESNO(db * 10) (default:0)"); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cienum cmds { 1328c2ecf20Sopenharmony_ci CMD_SET_VCO = 0x10, 1338c2ecf20Sopenharmony_ci CMD_TUNEREQUEST = 0x11, 1348c2ecf20Sopenharmony_ci CMD_MPEGCONFIG = 0x13, 1358c2ecf20Sopenharmony_ci CMD_TUNERINIT = 0x14, 1368c2ecf20Sopenharmony_ci CMD_BANDWIDTH = 0x15, 1378c2ecf20Sopenharmony_ci CMD_GETAGC = 0x19, 1388c2ecf20Sopenharmony_ci CMD_LNBCONFIG = 0x20, 1398c2ecf20Sopenharmony_ci CMD_LNBSEND = 0x21, /* Formerly CMD_SEND_DISEQC */ 1408c2ecf20Sopenharmony_ci CMD_LNBDCLEVEL = 0x22, 1418c2ecf20Sopenharmony_ci CMD_SET_TONE = 0x23, 1428c2ecf20Sopenharmony_ci CMD_UPDFWVERS = 0x35, 1438c2ecf20Sopenharmony_ci CMD_TUNERSLEEP = 0x36, 1448c2ecf20Sopenharmony_ci CMD_AGCCONTROL = 0x3b, /* Unknown */ 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* The Demod/Tuner can't easily provide these, we cache them */ 1488c2ecf20Sopenharmony_cistruct cx24116_tuning { 1498c2ecf20Sopenharmony_ci u32 frequency; 1508c2ecf20Sopenharmony_ci u32 symbol_rate; 1518c2ecf20Sopenharmony_ci enum fe_spectral_inversion inversion; 1528c2ecf20Sopenharmony_ci enum fe_code_rate fec; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci enum fe_delivery_system delsys; 1558c2ecf20Sopenharmony_ci enum fe_modulation modulation; 1568c2ecf20Sopenharmony_ci enum fe_pilot pilot; 1578c2ecf20Sopenharmony_ci enum fe_rolloff rolloff; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* Demod values */ 1608c2ecf20Sopenharmony_ci u8 fec_val; 1618c2ecf20Sopenharmony_ci u8 fec_mask; 1628c2ecf20Sopenharmony_ci u8 inversion_val; 1638c2ecf20Sopenharmony_ci u8 pilot_val; 1648c2ecf20Sopenharmony_ci u8 rolloff_val; 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* Basic commands that are sent to the firmware */ 1688c2ecf20Sopenharmony_cistruct cx24116_cmd { 1698c2ecf20Sopenharmony_ci u8 len; 1708c2ecf20Sopenharmony_ci u8 args[CX24116_ARGLEN]; 1718c2ecf20Sopenharmony_ci}; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistruct cx24116_state { 1748c2ecf20Sopenharmony_ci struct i2c_adapter *i2c; 1758c2ecf20Sopenharmony_ci const struct cx24116_config *config; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci struct dvb_frontend frontend; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci struct cx24116_tuning dcur; 1808c2ecf20Sopenharmony_ci struct cx24116_tuning dnxt; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci u8 skip_fw_load; 1838c2ecf20Sopenharmony_ci u8 burst; 1848c2ecf20Sopenharmony_ci struct cx24116_cmd dsec_cmd; 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int cx24116_writereg(struct cx24116_state *state, int reg, int data) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci u8 buf[] = { reg, data }; 1908c2ecf20Sopenharmony_ci struct i2c_msg msg = { .addr = state->config->demod_address, 1918c2ecf20Sopenharmony_ci .flags = 0, .buf = buf, .len = 2 }; 1928c2ecf20Sopenharmony_ci int err; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (debug > 1) 1958c2ecf20Sopenharmony_ci printk("cx24116: %s: write reg 0x%02x, value 0x%02x\n", 1968c2ecf20Sopenharmony_ci __func__, reg, data); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci err = i2c_transfer(state->i2c, &msg, 1); 1998c2ecf20Sopenharmony_ci if (err != 1) { 2008c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x, value == 0x%02x)\n", 2018c2ecf20Sopenharmony_ci __func__, err, reg, data); 2028c2ecf20Sopenharmony_ci return -EREMOTEIO; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci/* Bulk byte writes to a single I2C address, for 32k firmware load */ 2098c2ecf20Sopenharmony_cistatic int cx24116_writeregN(struct cx24116_state *state, int reg, 2108c2ecf20Sopenharmony_ci const u8 *data, u16 len) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci int ret; 2138c2ecf20Sopenharmony_ci struct i2c_msg msg; 2148c2ecf20Sopenharmony_ci u8 *buf; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci buf = kmalloc(len + 1, GFP_KERNEL); 2178c2ecf20Sopenharmony_ci if (!buf) 2188c2ecf20Sopenharmony_ci return -ENOMEM; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci *(buf) = reg; 2218c2ecf20Sopenharmony_ci memcpy(buf + 1, data, len); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci msg.addr = state->config->demod_address; 2248c2ecf20Sopenharmony_ci msg.flags = 0; 2258c2ecf20Sopenharmony_ci msg.buf = buf; 2268c2ecf20Sopenharmony_ci msg.len = len + 1; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (debug > 1) 2298c2ecf20Sopenharmony_ci printk(KERN_INFO "cx24116: %s: write regN 0x%02x, len = %d\n", 2308c2ecf20Sopenharmony_ci __func__, reg, len); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci ret = i2c_transfer(state->i2c, &msg, 1); 2338c2ecf20Sopenharmony_ci if (ret != 1) { 2348c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x\n", 2358c2ecf20Sopenharmony_ci __func__, ret, reg); 2368c2ecf20Sopenharmony_ci ret = -EREMOTEIO; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci kfree(buf); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return ret; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int cx24116_readreg(struct cx24116_state *state, u8 reg) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci int ret; 2478c2ecf20Sopenharmony_ci u8 b0[] = { reg }; 2488c2ecf20Sopenharmony_ci u8 b1[] = { 0 }; 2498c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 2508c2ecf20Sopenharmony_ci { .addr = state->config->demod_address, .flags = 0, 2518c2ecf20Sopenharmony_ci .buf = b0, .len = 1 }, 2528c2ecf20Sopenharmony_ci { .addr = state->config->demod_address, .flags = I2C_M_RD, 2538c2ecf20Sopenharmony_ci .buf = b1, .len = 1 } 2548c2ecf20Sopenharmony_ci }; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci ret = i2c_transfer(state->i2c, msg, 2); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (ret != 2) { 2598c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: reg=0x%x (error=%d)\n", 2608c2ecf20Sopenharmony_ci __func__, reg, ret); 2618c2ecf20Sopenharmony_ci return ret; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (debug > 1) 2658c2ecf20Sopenharmony_ci printk(KERN_INFO "cx24116: read reg 0x%02x, value 0x%02x\n", 2668c2ecf20Sopenharmony_ci reg, b1[0]); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return b1[0]; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic int cx24116_set_inversion(struct cx24116_state *state, 2728c2ecf20Sopenharmony_ci enum fe_spectral_inversion inversion) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci dprintk("%s(%d)\n", __func__, inversion); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci switch (inversion) { 2778c2ecf20Sopenharmony_ci case INVERSION_OFF: 2788c2ecf20Sopenharmony_ci state->dnxt.inversion_val = 0x00; 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci case INVERSION_ON: 2818c2ecf20Sopenharmony_ci state->dnxt.inversion_val = 0x04; 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci case INVERSION_AUTO: 2848c2ecf20Sopenharmony_ci state->dnxt.inversion_val = 0x0C; 2858c2ecf20Sopenharmony_ci break; 2868c2ecf20Sopenharmony_ci default: 2878c2ecf20Sopenharmony_ci return -EINVAL; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci state->dnxt.inversion = inversion; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/* 2968c2ecf20Sopenharmony_ci * modfec (modulation and FEC) 2978c2ecf20Sopenharmony_ci * =========================== 2988c2ecf20Sopenharmony_ci * 2998c2ecf20Sopenharmony_ci * MOD FEC mask/val standard 3008c2ecf20Sopenharmony_ci * ---- -------- ----------- -------- 3018c2ecf20Sopenharmony_ci * QPSK FEC_1_2 0x02 0x02+X DVB-S 3028c2ecf20Sopenharmony_ci * QPSK FEC_2_3 0x04 0x02+X DVB-S 3038c2ecf20Sopenharmony_ci * QPSK FEC_3_4 0x08 0x02+X DVB-S 3048c2ecf20Sopenharmony_ci * QPSK FEC_4_5 0x10 0x02+X DVB-S (?) 3058c2ecf20Sopenharmony_ci * QPSK FEC_5_6 0x20 0x02+X DVB-S 3068c2ecf20Sopenharmony_ci * QPSK FEC_6_7 0x40 0x02+X DVB-S 3078c2ecf20Sopenharmony_ci * QPSK FEC_7_8 0x80 0x02+X DVB-S 3088c2ecf20Sopenharmony_ci * QPSK FEC_8_9 0x01 0x02+X DVB-S (?) (NOT SUPPORTED?) 3098c2ecf20Sopenharmony_ci * QPSK AUTO 0xff 0x02+X DVB-S 3108c2ecf20Sopenharmony_ci * 3118c2ecf20Sopenharmony_ci * For DVB-S high byte probably represents FEC 3128c2ecf20Sopenharmony_ci * and low byte selects the modulator. The high 3138c2ecf20Sopenharmony_ci * byte is search range mask. Bit 5 may turn 3148c2ecf20Sopenharmony_ci * on DVB-S and remaining bits represent some 3158c2ecf20Sopenharmony_ci * kind of calibration (how/what i do not know). 3168c2ecf20Sopenharmony_ci * 3178c2ecf20Sopenharmony_ci * Eg.(2/3) szap "Zone Horror" 3188c2ecf20Sopenharmony_ci * 3198c2ecf20Sopenharmony_ci * mask/val = 0x04, 0x20 3208c2ecf20Sopenharmony_ci * status 1f | signal c3c0 | snr a333 | ber 00000098 | unc 0 | FE_HAS_LOCK 3218c2ecf20Sopenharmony_ci * 3228c2ecf20Sopenharmony_ci * mask/val = 0x04, 0x30 3238c2ecf20Sopenharmony_ci * status 1f | signal c3c0 | snr a333 | ber 00000000 | unc 0 | FE_HAS_LOCK 3248c2ecf20Sopenharmony_ci * 3258c2ecf20Sopenharmony_ci * After tuning FECSTATUS contains actual FEC 3268c2ecf20Sopenharmony_ci * in use numbered 1 through to 8 for 1/2 .. 2/3 etc 3278c2ecf20Sopenharmony_ci * 3288c2ecf20Sopenharmony_ci * NBC=NOT/NON BACKWARD COMPATIBLE WITH DVB-S (DVB-S2 only) 3298c2ecf20Sopenharmony_ci * 3308c2ecf20Sopenharmony_ci * NBC-QPSK FEC_1_2 0x00, 0x04 DVB-S2 3318c2ecf20Sopenharmony_ci * NBC-QPSK FEC_3_5 0x00, 0x05 DVB-S2 3328c2ecf20Sopenharmony_ci * NBC-QPSK FEC_2_3 0x00, 0x06 DVB-S2 3338c2ecf20Sopenharmony_ci * NBC-QPSK FEC_3_4 0x00, 0x07 DVB-S2 3348c2ecf20Sopenharmony_ci * NBC-QPSK FEC_4_5 0x00, 0x08 DVB-S2 3358c2ecf20Sopenharmony_ci * NBC-QPSK FEC_5_6 0x00, 0x09 DVB-S2 3368c2ecf20Sopenharmony_ci * NBC-QPSK FEC_8_9 0x00, 0x0a DVB-S2 3378c2ecf20Sopenharmony_ci * NBC-QPSK FEC_9_10 0x00, 0x0b DVB-S2 3388c2ecf20Sopenharmony_ci * 3398c2ecf20Sopenharmony_ci * NBC-8PSK FEC_3_5 0x00, 0x0c DVB-S2 3408c2ecf20Sopenharmony_ci * NBC-8PSK FEC_2_3 0x00, 0x0d DVB-S2 3418c2ecf20Sopenharmony_ci * NBC-8PSK FEC_3_4 0x00, 0x0e DVB-S2 3428c2ecf20Sopenharmony_ci * NBC-8PSK FEC_5_6 0x00, 0x0f DVB-S2 3438c2ecf20Sopenharmony_ci * NBC-8PSK FEC_8_9 0x00, 0x10 DVB-S2 3448c2ecf20Sopenharmony_ci * NBC-8PSK FEC_9_10 0x00, 0x11 DVB-S2 3458c2ecf20Sopenharmony_ci * 3468c2ecf20Sopenharmony_ci * For DVB-S2 low bytes selects both modulator 3478c2ecf20Sopenharmony_ci * and FEC. High byte is meaningless here. To 3488c2ecf20Sopenharmony_ci * set pilot, bit 6 (0x40) is set. When inspecting 3498c2ecf20Sopenharmony_ci * FECSTATUS bit 7 (0x80) represents the pilot 3508c2ecf20Sopenharmony_ci * selection whilst not tuned. When tuned, actual FEC 3518c2ecf20Sopenharmony_ci * in use is found in FECSTATUS as per above. Pilot 3528c2ecf20Sopenharmony_ci * value is reset. 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci/* A table of modulation, fec and configuration bytes for the demod. 3568c2ecf20Sopenharmony_ci * Not all S2 mmodulation schemes are support and not all rates with 3578c2ecf20Sopenharmony_ci * a scheme are support. Especially, no auto detect when in S2 mode. 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_cistatic struct cx24116_modfec { 3608c2ecf20Sopenharmony_ci enum fe_delivery_system delivery_system; 3618c2ecf20Sopenharmony_ci enum fe_modulation modulation; 3628c2ecf20Sopenharmony_ci enum fe_code_rate fec; 3638c2ecf20Sopenharmony_ci u8 mask; /* In DVBS mode this is used to autodetect */ 3648c2ecf20Sopenharmony_ci u8 val; /* Passed to the firmware to indicate mode selection */ 3658c2ecf20Sopenharmony_ci} CX24116_MODFEC_MODES[] = { 3668c2ecf20Sopenharmony_ci /* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */ 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /*mod fec mask val */ 3698c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_NONE, 0xfe, 0x30 }, 3708c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_1_2, 0x02, 0x2e }, /* 00000010 00101110 */ 3718c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_2_3, 0x04, 0x2f }, /* 00000100 00101111 */ 3728c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_3_4, 0x08, 0x30 }, /* 00001000 00110000 */ 3738c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_4_5, 0xfe, 0x30 }, /* 000?0000 ? */ 3748c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_5_6, 0x20, 0x31 }, /* 00100000 00110001 */ 3758c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_6_7, 0xfe, 0x30 }, /* 0?000000 ? */ 3768c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_7_8, 0x80, 0x32 }, /* 10000000 00110010 */ 3778c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_8_9, 0xfe, 0x30 }, /* 0000000? ? */ 3788c2ecf20Sopenharmony_ci { SYS_DVBS, QPSK, FEC_AUTO, 0xfe, 0x30 }, 3798c2ecf20Sopenharmony_ci /* NBC-QPSK */ 3808c2ecf20Sopenharmony_ci { SYS_DVBS2, QPSK, FEC_1_2, 0x00, 0x04 }, 3818c2ecf20Sopenharmony_ci { SYS_DVBS2, QPSK, FEC_3_5, 0x00, 0x05 }, 3828c2ecf20Sopenharmony_ci { SYS_DVBS2, QPSK, FEC_2_3, 0x00, 0x06 }, 3838c2ecf20Sopenharmony_ci { SYS_DVBS2, QPSK, FEC_3_4, 0x00, 0x07 }, 3848c2ecf20Sopenharmony_ci { SYS_DVBS2, QPSK, FEC_4_5, 0x00, 0x08 }, 3858c2ecf20Sopenharmony_ci { SYS_DVBS2, QPSK, FEC_5_6, 0x00, 0x09 }, 3868c2ecf20Sopenharmony_ci { SYS_DVBS2, QPSK, FEC_8_9, 0x00, 0x0a }, 3878c2ecf20Sopenharmony_ci { SYS_DVBS2, QPSK, FEC_9_10, 0x00, 0x0b }, 3888c2ecf20Sopenharmony_ci /* 8PSK */ 3898c2ecf20Sopenharmony_ci { SYS_DVBS2, PSK_8, FEC_3_5, 0x00, 0x0c }, 3908c2ecf20Sopenharmony_ci { SYS_DVBS2, PSK_8, FEC_2_3, 0x00, 0x0d }, 3918c2ecf20Sopenharmony_ci { SYS_DVBS2, PSK_8, FEC_3_4, 0x00, 0x0e }, 3928c2ecf20Sopenharmony_ci { SYS_DVBS2, PSK_8, FEC_5_6, 0x00, 0x0f }, 3938c2ecf20Sopenharmony_ci { SYS_DVBS2, PSK_8, FEC_8_9, 0x00, 0x10 }, 3948c2ecf20Sopenharmony_ci { SYS_DVBS2, PSK_8, FEC_9_10, 0x00, 0x11 }, 3958c2ecf20Sopenharmony_ci /* 3968c2ecf20Sopenharmony_ci * `val' can be found in the FECSTATUS register when tuning. 3978c2ecf20Sopenharmony_ci * FECSTATUS will give the actual FEC in use if tuning was successful. 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_ci}; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int cx24116_lookup_fecmod(struct cx24116_state *state, 4028c2ecf20Sopenharmony_ci enum fe_delivery_system d, enum fe_modulation m, enum fe_code_rate f) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci int i, ret = -EOPNOTSUPP; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci dprintk("%s(0x%02x,0x%02x)\n", __func__, m, f); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(CX24116_MODFEC_MODES); i++) { 4098c2ecf20Sopenharmony_ci if ((d == CX24116_MODFEC_MODES[i].delivery_system) && 4108c2ecf20Sopenharmony_ci (m == CX24116_MODFEC_MODES[i].modulation) && 4118c2ecf20Sopenharmony_ci (f == CX24116_MODFEC_MODES[i].fec)) { 4128c2ecf20Sopenharmony_ci ret = i; 4138c2ecf20Sopenharmony_ci break; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return ret; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int cx24116_set_fec(struct cx24116_state *state, 4218c2ecf20Sopenharmony_ci enum fe_delivery_system delsys, 4228c2ecf20Sopenharmony_ci enum fe_modulation mod, 4238c2ecf20Sopenharmony_ci enum fe_code_rate fec) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci int ret = 0; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci dprintk("%s(0x%02x,0x%02x)\n", __func__, mod, fec); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci ret = cx24116_lookup_fecmod(state, delsys, mod, fec); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (ret < 0) 4328c2ecf20Sopenharmony_ci return ret; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci state->dnxt.fec = fec; 4358c2ecf20Sopenharmony_ci state->dnxt.fec_val = CX24116_MODFEC_MODES[ret].val; 4368c2ecf20Sopenharmony_ci state->dnxt.fec_mask = CX24116_MODFEC_MODES[ret].mask; 4378c2ecf20Sopenharmony_ci dprintk("%s() mask/val = 0x%02x/0x%02x\n", __func__, 4388c2ecf20Sopenharmony_ci state->dnxt.fec_mask, state->dnxt.fec_val); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return 0; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int cx24116_set_symbolrate(struct cx24116_state *state, u32 rate) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci dprintk("%s(%d)\n", __func__, rate); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* check if symbol rate is within limits */ 4488c2ecf20Sopenharmony_ci if ((rate > state->frontend.ops.info.symbol_rate_max) || 4498c2ecf20Sopenharmony_ci (rate < state->frontend.ops.info.symbol_rate_min)) { 4508c2ecf20Sopenharmony_ci dprintk("%s() unsupported symbol_rate = %d\n", __func__, rate); 4518c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci state->dnxt.symbol_rate = rate; 4558c2ecf20Sopenharmony_ci dprintk("%s() symbol_rate = %d\n", __func__, rate); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return 0; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic int cx24116_load_firmware(struct dvb_frontend *fe, 4618c2ecf20Sopenharmony_ci const struct firmware *fw); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic int cx24116_firmware_ondemand(struct dvb_frontend *fe) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 4668c2ecf20Sopenharmony_ci const struct firmware *fw; 4678c2ecf20Sopenharmony_ci int ret = 0; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci dprintk("%s()\n", __func__); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (cx24116_readreg(state, 0x20) > 0) { 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (state->skip_fw_load) 4748c2ecf20Sopenharmony_ci return 0; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* Load firmware */ 4778c2ecf20Sopenharmony_ci /* request the firmware, this will block until loaded */ 4788c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", 4798c2ecf20Sopenharmony_ci __func__, CX24116_DEFAULT_FIRMWARE); 4808c2ecf20Sopenharmony_ci ret = request_firmware(&fw, CX24116_DEFAULT_FIRMWARE, 4818c2ecf20Sopenharmony_ci state->i2c->dev.parent); 4828c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", 4838c2ecf20Sopenharmony_ci __func__); 4848c2ecf20Sopenharmony_ci if (ret) { 4858c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: No firmware uploaded (timeout or file not found?)\n", 4868c2ecf20Sopenharmony_ci __func__); 4878c2ecf20Sopenharmony_ci return ret; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* Make sure we don't recurse back through here 4918c2ecf20Sopenharmony_ci * during loading */ 4928c2ecf20Sopenharmony_ci state->skip_fw_load = 1; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci ret = cx24116_load_firmware(fe, fw); 4958c2ecf20Sopenharmony_ci if (ret) 4968c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Writing firmware to device failed\n", 4978c2ecf20Sopenharmony_ci __func__); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci release_firmware(fw); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Firmware upload %s\n", __func__, 5028c2ecf20Sopenharmony_ci ret == 0 ? "complete" : "failed"); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* Ensure firmware is always loaded if required */ 5058c2ecf20Sopenharmony_ci state->skip_fw_load = 0; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci return ret; 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci/* Take a basic firmware command structure, format it 5128c2ecf20Sopenharmony_ci * and forward it for processing 5138c2ecf20Sopenharmony_ci */ 5148c2ecf20Sopenharmony_cistatic int cx24116_cmd_execute(struct dvb_frontend *fe, struct cx24116_cmd *cmd) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 5178c2ecf20Sopenharmony_ci int i, ret; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci dprintk("%s()\n", __func__); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci /* Load the firmware if required */ 5228c2ecf20Sopenharmony_ci ret = cx24116_firmware_ondemand(fe); 5238c2ecf20Sopenharmony_ci if (ret != 0) { 5248c2ecf20Sopenharmony_ci printk(KERN_ERR "%s(): Unable initialise the firmware\n", 5258c2ecf20Sopenharmony_ci __func__); 5268c2ecf20Sopenharmony_ci return ret; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* Write the command */ 5308c2ecf20Sopenharmony_ci for (i = 0; i < cmd->len ; i++) { 5318c2ecf20Sopenharmony_ci dprintk("%s: 0x%02x == 0x%02x\n", __func__, i, cmd->args[i]); 5328c2ecf20Sopenharmony_ci cx24116_writereg(state, i, cmd->args[i]); 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* Start execution and wait for cmd to terminate */ 5368c2ecf20Sopenharmony_ci cx24116_writereg(state, CX24116_REG_EXECUTE, 0x01); 5378c2ecf20Sopenharmony_ci while (cx24116_readreg(state, CX24116_REG_EXECUTE)) { 5388c2ecf20Sopenharmony_ci msleep(10); 5398c2ecf20Sopenharmony_ci if (i++ > 64) { 5408c2ecf20Sopenharmony_ci /* Avoid looping forever if the firmware does 5418c2ecf20Sopenharmony_ci not respond */ 5428c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s() Firmware not responding\n", 5438c2ecf20Sopenharmony_ci __func__); 5448c2ecf20Sopenharmony_ci return -EREMOTEIO; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci return 0; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic int cx24116_load_firmware(struct dvb_frontend *fe, 5518c2ecf20Sopenharmony_ci const struct firmware *fw) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 5548c2ecf20Sopenharmony_ci struct cx24116_cmd cmd; 5558c2ecf20Sopenharmony_ci int i, ret, len, max, remaining; 5568c2ecf20Sopenharmony_ci unsigned char vers[4]; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 5598c2ecf20Sopenharmony_ci dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n", 5608c2ecf20Sopenharmony_ci fw->size, 5618c2ecf20Sopenharmony_ci fw->data[0], 5628c2ecf20Sopenharmony_ci fw->data[1], 5638c2ecf20Sopenharmony_ci fw->data[fw->size-2], 5648c2ecf20Sopenharmony_ci fw->data[fw->size-1]); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* Toggle 88x SRST pin to reset demod */ 5678c2ecf20Sopenharmony_ci if (state->config->reset_device) 5688c2ecf20Sopenharmony_ci state->config->reset_device(fe); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* Begin the firmware load process */ 5718c2ecf20Sopenharmony_ci /* Prepare the demod, load the firmware, cleanup after load */ 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* Init PLL */ 5748c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xE5, 0x00); 5758c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xF1, 0x08); 5768c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xF2, 0x13); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci /* Start PLL */ 5798c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xe0, 0x03); 5808c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xe0, 0x00); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* Unknown */ 5838c2ecf20Sopenharmony_ci cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46); 5848c2ecf20Sopenharmony_ci cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci /* Unknown */ 5878c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xF0, 0x03); 5888c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xF4, 0x81); 5898c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xF5, 0x00); 5908c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xF6, 0x00); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* Split firmware to the max I2C write len and write. 5938c2ecf20Sopenharmony_ci * Writes whole firmware as one write when i2c_wr_max is set to 0. */ 5948c2ecf20Sopenharmony_ci if (state->config->i2c_wr_max) 5958c2ecf20Sopenharmony_ci max = state->config->i2c_wr_max; 5968c2ecf20Sopenharmony_ci else 5978c2ecf20Sopenharmony_ci max = INT_MAX; /* enough for 32k firmware */ 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci for (remaining = fw->size; remaining > 0; remaining -= max - 1) { 6008c2ecf20Sopenharmony_ci len = remaining; 6018c2ecf20Sopenharmony_ci if (len > max - 1) 6028c2ecf20Sopenharmony_ci len = max - 1; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci cx24116_writeregN(state, 0xF7, &fw->data[fw->size - remaining], 6058c2ecf20Sopenharmony_ci len); 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xF4, 0x10); 6098c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xF0, 0x00); 6108c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xF8, 0x06); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci /* Firmware CMD 10: VCO config */ 6138c2ecf20Sopenharmony_ci cmd.args[0x00] = CMD_SET_VCO; 6148c2ecf20Sopenharmony_ci cmd.args[0x01] = 0x05; 6158c2ecf20Sopenharmony_ci cmd.args[0x02] = 0xdc; 6168c2ecf20Sopenharmony_ci cmd.args[0x03] = 0xda; 6178c2ecf20Sopenharmony_ci cmd.args[0x04] = 0xae; 6188c2ecf20Sopenharmony_ci cmd.args[0x05] = 0xaa; 6198c2ecf20Sopenharmony_ci cmd.args[0x06] = 0x04; 6208c2ecf20Sopenharmony_ci cmd.args[0x07] = 0x9d; 6218c2ecf20Sopenharmony_ci cmd.args[0x08] = 0xfc; 6228c2ecf20Sopenharmony_ci cmd.args[0x09] = 0x06; 6238c2ecf20Sopenharmony_ci cmd.len = 0x0a; 6248c2ecf20Sopenharmony_ci ret = cx24116_cmd_execute(fe, &cmd); 6258c2ecf20Sopenharmony_ci if (ret != 0) 6268c2ecf20Sopenharmony_ci return ret; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci cx24116_writereg(state, CX24116_REG_SSTATUS, 0x00); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci /* Firmware CMD 14: Tuner config */ 6318c2ecf20Sopenharmony_ci cmd.args[0x00] = CMD_TUNERINIT; 6328c2ecf20Sopenharmony_ci cmd.args[0x01] = 0x00; 6338c2ecf20Sopenharmony_ci cmd.args[0x02] = 0x00; 6348c2ecf20Sopenharmony_ci cmd.len = 0x03; 6358c2ecf20Sopenharmony_ci ret = cx24116_cmd_execute(fe, &cmd); 6368c2ecf20Sopenharmony_ci if (ret != 0) 6378c2ecf20Sopenharmony_ci return ret; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xe5, 0x00); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci /* Firmware CMD 13: MPEG config */ 6428c2ecf20Sopenharmony_ci cmd.args[0x00] = CMD_MPEGCONFIG; 6438c2ecf20Sopenharmony_ci cmd.args[0x01] = 0x01; 6448c2ecf20Sopenharmony_ci cmd.args[0x02] = 0x75; 6458c2ecf20Sopenharmony_ci cmd.args[0x03] = 0x00; 6468c2ecf20Sopenharmony_ci if (state->config->mpg_clk_pos_pol) 6478c2ecf20Sopenharmony_ci cmd.args[0x04] = state->config->mpg_clk_pos_pol; 6488c2ecf20Sopenharmony_ci else 6498c2ecf20Sopenharmony_ci cmd.args[0x04] = 0x02; 6508c2ecf20Sopenharmony_ci cmd.args[0x05] = 0x00; 6518c2ecf20Sopenharmony_ci cmd.len = 0x06; 6528c2ecf20Sopenharmony_ci ret = cx24116_cmd_execute(fe, &cmd); 6538c2ecf20Sopenharmony_ci if (ret != 0) 6548c2ecf20Sopenharmony_ci return ret; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* Firmware CMD 35: Get firmware version */ 6578c2ecf20Sopenharmony_ci cmd.args[0x00] = CMD_UPDFWVERS; 6588c2ecf20Sopenharmony_ci cmd.len = 0x02; 6598c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 6608c2ecf20Sopenharmony_ci cmd.args[0x01] = i; 6618c2ecf20Sopenharmony_ci ret = cx24116_cmd_execute(fe, &cmd); 6628c2ecf20Sopenharmony_ci if (ret != 0) 6638c2ecf20Sopenharmony_ci return ret; 6648c2ecf20Sopenharmony_ci vers[i] = cx24116_readreg(state, CX24116_REG_MAILBOX); 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: FW version %i.%i.%i.%i\n", __func__, 6678c2ecf20Sopenharmony_ci vers[0], vers[1], vers[2], vers[3]); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci return 0; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic int cx24116_read_status(struct dvb_frontend *fe, enum fe_status *status) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci int lock = cx24116_readreg(state, CX24116_REG_SSTATUS) & 6778c2ecf20Sopenharmony_ci CX24116_STATUS_MASK; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci dprintk("%s: status = 0x%02x\n", __func__, lock); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci *status = 0; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (lock & CX24116_HAS_SIGNAL) 6848c2ecf20Sopenharmony_ci *status |= FE_HAS_SIGNAL; 6858c2ecf20Sopenharmony_ci if (lock & CX24116_HAS_CARRIER) 6868c2ecf20Sopenharmony_ci *status |= FE_HAS_CARRIER; 6878c2ecf20Sopenharmony_ci if (lock & CX24116_HAS_VITERBI) 6888c2ecf20Sopenharmony_ci *status |= FE_HAS_VITERBI; 6898c2ecf20Sopenharmony_ci if (lock & CX24116_HAS_SYNCLOCK) 6908c2ecf20Sopenharmony_ci *status |= FE_HAS_SYNC | FE_HAS_LOCK; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci return 0; 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cistatic int cx24116_read_ber(struct dvb_frontend *fe, u32 *ber) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci dprintk("%s()\n", __func__); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci *ber = (cx24116_readreg(state, CX24116_REG_BER24) << 24) | 7028c2ecf20Sopenharmony_ci (cx24116_readreg(state, CX24116_REG_BER16) << 16) | 7038c2ecf20Sopenharmony_ci (cx24116_readreg(state, CX24116_REG_BER8) << 8) | 7048c2ecf20Sopenharmony_ci cx24116_readreg(state, CX24116_REG_BER0); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci return 0; 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci/* TODO Determine function and scale appropriately */ 7108c2ecf20Sopenharmony_cistatic int cx24116_read_signal_strength(struct dvb_frontend *fe, 7118c2ecf20Sopenharmony_ci u16 *signal_strength) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 7148c2ecf20Sopenharmony_ci struct cx24116_cmd cmd; 7158c2ecf20Sopenharmony_ci int ret; 7168c2ecf20Sopenharmony_ci u16 sig_reading; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci dprintk("%s()\n", __func__); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci /* Firmware CMD 19: Get AGC */ 7218c2ecf20Sopenharmony_ci cmd.args[0x00] = CMD_GETAGC; 7228c2ecf20Sopenharmony_ci cmd.len = 0x01; 7238c2ecf20Sopenharmony_ci ret = cx24116_cmd_execute(fe, &cmd); 7248c2ecf20Sopenharmony_ci if (ret != 0) 7258c2ecf20Sopenharmony_ci return ret; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci sig_reading = 7288c2ecf20Sopenharmony_ci (cx24116_readreg(state, 7298c2ecf20Sopenharmony_ci CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK) | 7308c2ecf20Sopenharmony_ci (cx24116_readreg(state, CX24116_REG_SIGNAL) << 6); 7318c2ecf20Sopenharmony_ci *signal_strength = 0 - sig_reading; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", 7348c2ecf20Sopenharmony_ci __func__, sig_reading, *signal_strength); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci return 0; 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci/* SNR (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */ 7408c2ecf20Sopenharmony_cistatic int cx24116_read_snr_pct(struct dvb_frontend *fe, u16 *snr) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 7438c2ecf20Sopenharmony_ci u8 snr_reading; 7448c2ecf20Sopenharmony_ci static const u32 snr_tab[] = { /* 10 x Table (rounded up) */ 7458c2ecf20Sopenharmony_ci 0x00000, 0x0199A, 0x03333, 0x04ccD, 0x06667, 7468c2ecf20Sopenharmony_ci 0x08000, 0x0999A, 0x0b333, 0x0cccD, 0x0e667, 7478c2ecf20Sopenharmony_ci 0x10000, 0x1199A, 0x13333, 0x14ccD, 0x16667, 7488c2ecf20Sopenharmony_ci 0x18000 }; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci dprintk("%s()\n", __func__); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci snr_reading = cx24116_readreg(state, CX24116_REG_QUALITY0); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci if (snr_reading >= 0xa0 /* 100% */) 7558c2ecf20Sopenharmony_ci *snr = 0xffff; 7568c2ecf20Sopenharmony_ci else 7578c2ecf20Sopenharmony_ci *snr = snr_tab[(snr_reading & 0xf0) >> 4] + 7588c2ecf20Sopenharmony_ci (snr_tab[(snr_reading & 0x0f)] >> 4); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__, 7618c2ecf20Sopenharmony_ci snr_reading, *snr); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci return 0; 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci/* The reelbox patches show the value in the registers represents 7678c2ecf20Sopenharmony_ci * ESNO, from 0->30db (values 0->300). We provide this value by 7688c2ecf20Sopenharmony_ci * default. 7698c2ecf20Sopenharmony_ci */ 7708c2ecf20Sopenharmony_cistatic int cx24116_read_snr_esno(struct dvb_frontend *fe, u16 *snr) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci dprintk("%s()\n", __func__); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci *snr = cx24116_readreg(state, CX24116_REG_QUALITY8) << 8 | 7778c2ecf20Sopenharmony_ci cx24116_readreg(state, CX24116_REG_QUALITY0); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci dprintk("%s: raw 0x%04x\n", __func__, *snr); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci return 0; 7828c2ecf20Sopenharmony_ci} 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_cistatic int cx24116_read_snr(struct dvb_frontend *fe, u16 *snr) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci if (esno_snr == 1) 7878c2ecf20Sopenharmony_ci return cx24116_read_snr_esno(fe, snr); 7888c2ecf20Sopenharmony_ci else 7898c2ecf20Sopenharmony_ci return cx24116_read_snr_pct(fe, snr); 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_cistatic int cx24116_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci dprintk("%s()\n", __func__); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci *ucblocks = (cx24116_readreg(state, CX24116_REG_UCB8) << 8) | 7998c2ecf20Sopenharmony_ci cx24116_readreg(state, CX24116_REG_UCB0); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci return 0; 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci/* Overwrite the current tuning params, we are about to tune */ 8058c2ecf20Sopenharmony_cistatic void cx24116_clone_params(struct dvb_frontend *fe) 8068c2ecf20Sopenharmony_ci{ 8078c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 8088c2ecf20Sopenharmony_ci state->dcur = state->dnxt; 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci/* Wait for LNB */ 8128c2ecf20Sopenharmony_cistatic int cx24116_wait_for_lnb(struct dvb_frontend *fe) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 8158c2ecf20Sopenharmony_ci int i; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci dprintk("%s() qstatus = 0x%02x\n", __func__, 8188c2ecf20Sopenharmony_ci cx24116_readreg(state, CX24116_REG_QSTATUS)); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci /* Wait for up to 300 ms */ 8218c2ecf20Sopenharmony_ci for (i = 0; i < 30 ; i++) { 8228c2ecf20Sopenharmony_ci if (cx24116_readreg(state, CX24116_REG_QSTATUS) & 0x20) 8238c2ecf20Sopenharmony_ci return 0; 8248c2ecf20Sopenharmony_ci msleep(10); 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci dprintk("%s(): LNB not ready\n", __func__); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci return -ETIMEDOUT; /* -EBUSY ? */ 8308c2ecf20Sopenharmony_ci} 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_cistatic int cx24116_set_voltage(struct dvb_frontend *fe, 8338c2ecf20Sopenharmony_ci enum fe_sec_voltage voltage) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci struct cx24116_cmd cmd; 8368c2ecf20Sopenharmony_ci int ret; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci dprintk("%s: %s\n", __func__, 8398c2ecf20Sopenharmony_ci voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : 8408c2ecf20Sopenharmony_ci voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci /* Wait for LNB ready */ 8438c2ecf20Sopenharmony_ci ret = cx24116_wait_for_lnb(fe); 8448c2ecf20Sopenharmony_ci if (ret != 0) 8458c2ecf20Sopenharmony_ci return ret; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci /* Wait for voltage/min repeat delay */ 8488c2ecf20Sopenharmony_ci msleep(100); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci cmd.args[0x00] = CMD_LNBDCLEVEL; 8518c2ecf20Sopenharmony_ci cmd.args[0x01] = (voltage == SEC_VOLTAGE_18 ? 0x01 : 0x00); 8528c2ecf20Sopenharmony_ci cmd.len = 0x02; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci /* Min delay time before DiSEqC send */ 8558c2ecf20Sopenharmony_ci msleep(15); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci return cx24116_cmd_execute(fe, &cmd); 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_cistatic int cx24116_set_tone(struct dvb_frontend *fe, 8618c2ecf20Sopenharmony_ci enum fe_sec_tone_mode tone) 8628c2ecf20Sopenharmony_ci{ 8638c2ecf20Sopenharmony_ci struct cx24116_cmd cmd; 8648c2ecf20Sopenharmony_ci int ret; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci dprintk("%s(%d)\n", __func__, tone); 8678c2ecf20Sopenharmony_ci if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) { 8688c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone); 8698c2ecf20Sopenharmony_ci return -EINVAL; 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci /* Wait for LNB ready */ 8738c2ecf20Sopenharmony_ci ret = cx24116_wait_for_lnb(fe); 8748c2ecf20Sopenharmony_ci if (ret != 0) 8758c2ecf20Sopenharmony_ci return ret; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci /* Min delay time after DiSEqC send */ 8788c2ecf20Sopenharmony_ci msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */ 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci /* Now we set the tone */ 8818c2ecf20Sopenharmony_ci cmd.args[0x00] = CMD_SET_TONE; 8828c2ecf20Sopenharmony_ci cmd.args[0x01] = 0x00; 8838c2ecf20Sopenharmony_ci cmd.args[0x02] = 0x00; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci switch (tone) { 8868c2ecf20Sopenharmony_ci case SEC_TONE_ON: 8878c2ecf20Sopenharmony_ci dprintk("%s: setting tone on\n", __func__); 8888c2ecf20Sopenharmony_ci cmd.args[0x03] = 0x01; 8898c2ecf20Sopenharmony_ci break; 8908c2ecf20Sopenharmony_ci case SEC_TONE_OFF: 8918c2ecf20Sopenharmony_ci dprintk("%s: setting tone off\n", __func__); 8928c2ecf20Sopenharmony_ci cmd.args[0x03] = 0x00; 8938c2ecf20Sopenharmony_ci break; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci cmd.len = 0x04; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci /* Min delay time before DiSEqC send */ 8988c2ecf20Sopenharmony_ci msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */ 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci return cx24116_cmd_execute(fe, &cmd); 9018c2ecf20Sopenharmony_ci} 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci/* Initialise DiSEqC */ 9048c2ecf20Sopenharmony_cistatic int cx24116_diseqc_init(struct dvb_frontend *fe) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 9078c2ecf20Sopenharmony_ci struct cx24116_cmd cmd; 9088c2ecf20Sopenharmony_ci int ret; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* Firmware CMD 20: LNB/DiSEqC config */ 9118c2ecf20Sopenharmony_ci cmd.args[0x00] = CMD_LNBCONFIG; 9128c2ecf20Sopenharmony_ci cmd.args[0x01] = 0x00; 9138c2ecf20Sopenharmony_ci cmd.args[0x02] = 0x10; 9148c2ecf20Sopenharmony_ci cmd.args[0x03] = 0x00; 9158c2ecf20Sopenharmony_ci cmd.args[0x04] = 0x8f; 9168c2ecf20Sopenharmony_ci cmd.args[0x05] = 0x28; 9178c2ecf20Sopenharmony_ci cmd.args[0x06] = (toneburst == CX24116_DISEQC_TONEOFF) ? 0x00 : 0x01; 9188c2ecf20Sopenharmony_ci cmd.args[0x07] = 0x01; 9198c2ecf20Sopenharmony_ci cmd.len = 0x08; 9208c2ecf20Sopenharmony_ci ret = cx24116_cmd_execute(fe, &cmd); 9218c2ecf20Sopenharmony_ci if (ret != 0) 9228c2ecf20Sopenharmony_ci return ret; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci /* Prepare a DiSEqC command */ 9258c2ecf20Sopenharmony_ci state->dsec_cmd.args[0x00] = CMD_LNBSEND; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci /* DiSEqC burst */ 9288c2ecf20Sopenharmony_ci state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci /* Unknown */ 9318c2ecf20Sopenharmony_ci state->dsec_cmd.args[CX24116_DISEQC_ARG2_2] = 0x02; 9328c2ecf20Sopenharmony_ci state->dsec_cmd.args[CX24116_DISEQC_ARG3_0] = 0x00; 9338c2ecf20Sopenharmony_ci /* Continuation flag? */ 9348c2ecf20Sopenharmony_ci state->dsec_cmd.args[CX24116_DISEQC_ARG4_0] = 0x00; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci /* DiSEqC message length */ 9378c2ecf20Sopenharmony_ci state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = 0x00; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* Command length */ 9408c2ecf20Sopenharmony_ci state->dsec_cmd.len = CX24116_DISEQC_MSGOFS; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci return 0; 9438c2ecf20Sopenharmony_ci} 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci/* Send DiSEqC message with derived burst (hack) || previous burst */ 9468c2ecf20Sopenharmony_cistatic int cx24116_send_diseqc_msg(struct dvb_frontend *fe, 9478c2ecf20Sopenharmony_ci struct dvb_diseqc_master_cmd *d) 9488c2ecf20Sopenharmony_ci{ 9498c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 9508c2ecf20Sopenharmony_ci int i, ret; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci /* Validate length */ 9538c2ecf20Sopenharmony_ci if (d->msg_len > sizeof(d->msg)) 9548c2ecf20Sopenharmony_ci return -EINVAL; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci /* Dump DiSEqC message */ 9578c2ecf20Sopenharmony_ci if (debug) { 9588c2ecf20Sopenharmony_ci printk(KERN_INFO "cx24116: %s(", __func__); 9598c2ecf20Sopenharmony_ci for (i = 0 ; i < d->msg_len ;) { 9608c2ecf20Sopenharmony_ci printk(KERN_INFO "0x%02x", d->msg[i]); 9618c2ecf20Sopenharmony_ci if (++i < d->msg_len) 9628c2ecf20Sopenharmony_ci printk(KERN_INFO ", "); 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci printk(") toneburst=%d\n", toneburst); 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci /* DiSEqC message */ 9688c2ecf20Sopenharmony_ci for (i = 0; i < d->msg_len; i++) 9698c2ecf20Sopenharmony_ci state->dsec_cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i]; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci /* DiSEqC message length */ 9728c2ecf20Sopenharmony_ci state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci /* Command length */ 9758c2ecf20Sopenharmony_ci state->dsec_cmd.len = CX24116_DISEQC_MSGOFS + 9768c2ecf20Sopenharmony_ci state->dsec_cmd.args[CX24116_DISEQC_MSGLEN]; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci /* DiSEqC toneburst */ 9798c2ecf20Sopenharmony_ci if (toneburst == CX24116_DISEQC_MESGCACHE) 9808c2ecf20Sopenharmony_ci /* Message is cached */ 9818c2ecf20Sopenharmony_ci return 0; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci else if (toneburst == CX24116_DISEQC_TONEOFF) 9848c2ecf20Sopenharmony_ci /* Message is sent without burst */ 9858c2ecf20Sopenharmony_ci state->dsec_cmd.args[CX24116_DISEQC_BURST] = 0; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci else if (toneburst == CX24116_DISEQC_TONECACHE) { 9888c2ecf20Sopenharmony_ci /* 9898c2ecf20Sopenharmony_ci * Message is sent with derived else cached burst 9908c2ecf20Sopenharmony_ci * 9918c2ecf20Sopenharmony_ci * WRITE PORT GROUP COMMAND 38 9928c2ecf20Sopenharmony_ci * 9938c2ecf20Sopenharmony_ci * 0/A/A: E0 10 38 F0..F3 9948c2ecf20Sopenharmony_ci * 1/B/B: E0 10 38 F4..F7 9958c2ecf20Sopenharmony_ci * 2/C/A: E0 10 38 F8..FB 9968c2ecf20Sopenharmony_ci * 3/D/B: E0 10 38 FC..FF 9978c2ecf20Sopenharmony_ci * 9988c2ecf20Sopenharmony_ci * databyte[3]= 8421:8421 9998c2ecf20Sopenharmony_ci * ABCD:WXYZ 10008c2ecf20Sopenharmony_ci * CLR :SET 10018c2ecf20Sopenharmony_ci * 10028c2ecf20Sopenharmony_ci * WX= PORT SELECT 0..3 (X=TONEBURST) 10038c2ecf20Sopenharmony_ci * Y = VOLTAGE (0=13V, 1=18V) 10048c2ecf20Sopenharmony_ci * Z = BAND (0=LOW, 1=HIGH(22K)) 10058c2ecf20Sopenharmony_ci */ 10068c2ecf20Sopenharmony_ci if (d->msg_len >= 4 && d->msg[2] == 0x38) 10078c2ecf20Sopenharmony_ci state->dsec_cmd.args[CX24116_DISEQC_BURST] = 10088c2ecf20Sopenharmony_ci ((d->msg[3] & 4) >> 2); 10098c2ecf20Sopenharmony_ci if (debug) 10108c2ecf20Sopenharmony_ci dprintk("%s burst=%d\n", __func__, 10118c2ecf20Sopenharmony_ci state->dsec_cmd.args[CX24116_DISEQC_BURST]); 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci /* Wait for LNB ready */ 10158c2ecf20Sopenharmony_ci ret = cx24116_wait_for_lnb(fe); 10168c2ecf20Sopenharmony_ci if (ret != 0) 10178c2ecf20Sopenharmony_ci return ret; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci /* Wait for voltage/min repeat delay */ 10208c2ecf20Sopenharmony_ci msleep(100); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci /* Command */ 10238c2ecf20Sopenharmony_ci ret = cx24116_cmd_execute(fe, &state->dsec_cmd); 10248c2ecf20Sopenharmony_ci if (ret != 0) 10258c2ecf20Sopenharmony_ci return ret; 10268c2ecf20Sopenharmony_ci /* 10278c2ecf20Sopenharmony_ci * Wait for send 10288c2ecf20Sopenharmony_ci * 10298c2ecf20Sopenharmony_ci * Eutelsat spec: 10308c2ecf20Sopenharmony_ci * >15ms delay + (XXX determine if FW does this, see set_tone) 10318c2ecf20Sopenharmony_ci * 13.5ms per byte + 10328c2ecf20Sopenharmony_ci * >15ms delay + 10338c2ecf20Sopenharmony_ci * 12.5ms burst + 10348c2ecf20Sopenharmony_ci * >15ms delay (XXX determine if FW does this, see set_tone) 10358c2ecf20Sopenharmony_ci */ 10368c2ecf20Sopenharmony_ci msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 10378c2ecf20Sopenharmony_ci ((toneburst == CX24116_DISEQC_TONEOFF) ? 30 : 60)); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci return 0; 10408c2ecf20Sopenharmony_ci} 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci/* Send DiSEqC burst */ 10438c2ecf20Sopenharmony_cistatic int cx24116_diseqc_send_burst(struct dvb_frontend *fe, 10448c2ecf20Sopenharmony_ci enum fe_sec_mini_cmd burst) 10458c2ecf20Sopenharmony_ci{ 10468c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 10478c2ecf20Sopenharmony_ci int ret; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci dprintk("%s(%d) toneburst=%d\n", __func__, burst, toneburst); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci /* DiSEqC burst */ 10528c2ecf20Sopenharmony_ci if (burst == SEC_MINI_A) 10538c2ecf20Sopenharmony_ci state->dsec_cmd.args[CX24116_DISEQC_BURST] = 10548c2ecf20Sopenharmony_ci CX24116_DISEQC_MINI_A; 10558c2ecf20Sopenharmony_ci else if (burst == SEC_MINI_B) 10568c2ecf20Sopenharmony_ci state->dsec_cmd.args[CX24116_DISEQC_BURST] = 10578c2ecf20Sopenharmony_ci CX24116_DISEQC_MINI_B; 10588c2ecf20Sopenharmony_ci else 10598c2ecf20Sopenharmony_ci return -EINVAL; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci /* DiSEqC toneburst */ 10628c2ecf20Sopenharmony_ci if (toneburst != CX24116_DISEQC_MESGCACHE) 10638c2ecf20Sopenharmony_ci /* Burst is cached */ 10648c2ecf20Sopenharmony_ci return 0; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci /* Burst is to be sent with cached message */ 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci /* Wait for LNB ready */ 10698c2ecf20Sopenharmony_ci ret = cx24116_wait_for_lnb(fe); 10708c2ecf20Sopenharmony_ci if (ret != 0) 10718c2ecf20Sopenharmony_ci return ret; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci /* Wait for voltage/min repeat delay */ 10748c2ecf20Sopenharmony_ci msleep(100); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci /* Command */ 10778c2ecf20Sopenharmony_ci ret = cx24116_cmd_execute(fe, &state->dsec_cmd); 10788c2ecf20Sopenharmony_ci if (ret != 0) 10798c2ecf20Sopenharmony_ci return ret; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci /* 10828c2ecf20Sopenharmony_ci * Wait for send 10838c2ecf20Sopenharmony_ci * 10848c2ecf20Sopenharmony_ci * Eutelsat spec: 10858c2ecf20Sopenharmony_ci * >15ms delay + (XXX determine if FW does this, see set_tone) 10868c2ecf20Sopenharmony_ci * 13.5ms per byte + 10878c2ecf20Sopenharmony_ci * >15ms delay + 10888c2ecf20Sopenharmony_ci * 12.5ms burst + 10898c2ecf20Sopenharmony_ci * >15ms delay (XXX determine if FW does this, see set_tone) 10908c2ecf20Sopenharmony_ci */ 10918c2ecf20Sopenharmony_ci msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci return 0; 10948c2ecf20Sopenharmony_ci} 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_cistatic void cx24116_release(struct dvb_frontend *fe) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 10998c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 11008c2ecf20Sopenharmony_ci kfree(state); 11018c2ecf20Sopenharmony_ci} 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops cx24116_ops; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_cistruct dvb_frontend *cx24116_attach(const struct cx24116_config *config, 11068c2ecf20Sopenharmony_ci struct i2c_adapter *i2c) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci struct cx24116_state *state; 11098c2ecf20Sopenharmony_ci int ret; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci /* allocate memory for the internal state */ 11148c2ecf20Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 11158c2ecf20Sopenharmony_ci if (state == NULL) 11168c2ecf20Sopenharmony_ci return NULL; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci state->config = config; 11198c2ecf20Sopenharmony_ci state->i2c = i2c; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci /* check if the demod is present */ 11228c2ecf20Sopenharmony_ci ret = (cx24116_readreg(state, 0xFF) << 8) | 11238c2ecf20Sopenharmony_ci cx24116_readreg(state, 0xFE); 11248c2ecf20Sopenharmony_ci if (ret != 0x0501) { 11258c2ecf20Sopenharmony_ci kfree(state); 11268c2ecf20Sopenharmony_ci printk(KERN_INFO "Invalid probe, probably not a CX24116 device\n"); 11278c2ecf20Sopenharmony_ci return NULL; 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci /* create dvb_frontend */ 11318c2ecf20Sopenharmony_ci memcpy(&state->frontend.ops, &cx24116_ops, 11328c2ecf20Sopenharmony_ci sizeof(struct dvb_frontend_ops)); 11338c2ecf20Sopenharmony_ci state->frontend.demodulator_priv = state; 11348c2ecf20Sopenharmony_ci return &state->frontend; 11358c2ecf20Sopenharmony_ci} 11368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cx24116_attach); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci/* 11398c2ecf20Sopenharmony_ci * Initialise or wake up device 11408c2ecf20Sopenharmony_ci * 11418c2ecf20Sopenharmony_ci * Power config will reset and load initial firmware if required 11428c2ecf20Sopenharmony_ci */ 11438c2ecf20Sopenharmony_cistatic int cx24116_initfe(struct dvb_frontend *fe) 11448c2ecf20Sopenharmony_ci{ 11458c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 11468c2ecf20Sopenharmony_ci struct cx24116_cmd cmd; 11478c2ecf20Sopenharmony_ci int ret; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci dprintk("%s()\n", __func__); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci /* Power on */ 11528c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xe0, 0); 11538c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xe1, 0); 11548c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xea, 0); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci /* Firmware CMD 36: Power config */ 11578c2ecf20Sopenharmony_ci cmd.args[0x00] = CMD_TUNERSLEEP; 11588c2ecf20Sopenharmony_ci cmd.args[0x01] = 0; 11598c2ecf20Sopenharmony_ci cmd.len = 0x02; 11608c2ecf20Sopenharmony_ci ret = cx24116_cmd_execute(fe, &cmd); 11618c2ecf20Sopenharmony_ci if (ret != 0) 11628c2ecf20Sopenharmony_ci return ret; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci ret = cx24116_diseqc_init(fe); 11658c2ecf20Sopenharmony_ci if (ret != 0) 11668c2ecf20Sopenharmony_ci return ret; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci /* HVR-4000 needs this */ 11698c2ecf20Sopenharmony_ci return cx24116_set_voltage(fe, SEC_VOLTAGE_13); 11708c2ecf20Sopenharmony_ci} 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci/* 11738c2ecf20Sopenharmony_ci * Put device to sleep 11748c2ecf20Sopenharmony_ci */ 11758c2ecf20Sopenharmony_cistatic int cx24116_sleep(struct dvb_frontend *fe) 11768c2ecf20Sopenharmony_ci{ 11778c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 11788c2ecf20Sopenharmony_ci struct cx24116_cmd cmd; 11798c2ecf20Sopenharmony_ci int ret; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci dprintk("%s()\n", __func__); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* Firmware CMD 36: Power config */ 11848c2ecf20Sopenharmony_ci cmd.args[0x00] = CMD_TUNERSLEEP; 11858c2ecf20Sopenharmony_ci cmd.args[0x01] = 1; 11868c2ecf20Sopenharmony_ci cmd.len = 0x02; 11878c2ecf20Sopenharmony_ci ret = cx24116_cmd_execute(fe, &cmd); 11888c2ecf20Sopenharmony_ci if (ret != 0) 11898c2ecf20Sopenharmony_ci return ret; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci /* Power off (Shutdown clocks) */ 11928c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xea, 0xff); 11938c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xe1, 1); 11948c2ecf20Sopenharmony_ci cx24116_writereg(state, 0xe0, 1); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci return 0; 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci/* dvb-core told us to tune, the tv property cache will be complete, 12008c2ecf20Sopenharmony_ci * it's safe for is to pull values and use them for tuning purposes. 12018c2ecf20Sopenharmony_ci */ 12028c2ecf20Sopenharmony_cistatic int cx24116_set_frontend(struct dvb_frontend *fe) 12038c2ecf20Sopenharmony_ci{ 12048c2ecf20Sopenharmony_ci struct cx24116_state *state = fe->demodulator_priv; 12058c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 12068c2ecf20Sopenharmony_ci struct cx24116_cmd cmd; 12078c2ecf20Sopenharmony_ci enum fe_status tunerstat; 12088c2ecf20Sopenharmony_ci int i, status, ret, retune = 1; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci dprintk("%s()\n", __func__); 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci switch (c->delivery_system) { 12138c2ecf20Sopenharmony_ci case SYS_DVBS: 12148c2ecf20Sopenharmony_ci dprintk("%s: DVB-S delivery system selected\n", __func__); 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci /* Only QPSK is supported for DVB-S */ 12178c2ecf20Sopenharmony_ci if (c->modulation != QPSK) { 12188c2ecf20Sopenharmony_ci dprintk("%s: unsupported modulation selected (%d)\n", 12198c2ecf20Sopenharmony_ci __func__, c->modulation); 12208c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12218c2ecf20Sopenharmony_ci } 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci /* Pilot doesn't exist in DVB-S, turn bit off */ 12248c2ecf20Sopenharmony_ci state->dnxt.pilot_val = CX24116_PILOT_OFF; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci /* DVB-S only supports 0.35 */ 12278c2ecf20Sopenharmony_ci if (c->rolloff != ROLLOFF_35) { 12288c2ecf20Sopenharmony_ci dprintk("%s: unsupported rolloff selected (%d)\n", 12298c2ecf20Sopenharmony_ci __func__, c->rolloff); 12308c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci state->dnxt.rolloff_val = CX24116_ROLLOFF_035; 12338c2ecf20Sopenharmony_ci break; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci case SYS_DVBS2: 12368c2ecf20Sopenharmony_ci dprintk("%s: DVB-S2 delivery system selected\n", __func__); 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci /* 12398c2ecf20Sopenharmony_ci * NBC 8PSK/QPSK with DVB-S is supported for DVB-S2, 12408c2ecf20Sopenharmony_ci * but not hardware auto detection 12418c2ecf20Sopenharmony_ci */ 12428c2ecf20Sopenharmony_ci if (c->modulation != PSK_8 && c->modulation != QPSK) { 12438c2ecf20Sopenharmony_ci dprintk("%s: unsupported modulation selected (%d)\n", 12448c2ecf20Sopenharmony_ci __func__, c->modulation); 12458c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci switch (c->pilot) { 12498c2ecf20Sopenharmony_ci case PILOT_AUTO: /* Not supported but emulated */ 12508c2ecf20Sopenharmony_ci state->dnxt.pilot_val = (c->modulation == QPSK) 12518c2ecf20Sopenharmony_ci ? CX24116_PILOT_OFF : CX24116_PILOT_ON; 12528c2ecf20Sopenharmony_ci retune++; 12538c2ecf20Sopenharmony_ci break; 12548c2ecf20Sopenharmony_ci case PILOT_OFF: 12558c2ecf20Sopenharmony_ci state->dnxt.pilot_val = CX24116_PILOT_OFF; 12568c2ecf20Sopenharmony_ci break; 12578c2ecf20Sopenharmony_ci case PILOT_ON: 12588c2ecf20Sopenharmony_ci state->dnxt.pilot_val = CX24116_PILOT_ON; 12598c2ecf20Sopenharmony_ci break; 12608c2ecf20Sopenharmony_ci default: 12618c2ecf20Sopenharmony_ci dprintk("%s: unsupported pilot mode selected (%d)\n", 12628c2ecf20Sopenharmony_ci __func__, c->pilot); 12638c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci switch (c->rolloff) { 12678c2ecf20Sopenharmony_ci case ROLLOFF_20: 12688c2ecf20Sopenharmony_ci state->dnxt.rolloff_val = CX24116_ROLLOFF_020; 12698c2ecf20Sopenharmony_ci break; 12708c2ecf20Sopenharmony_ci case ROLLOFF_25: 12718c2ecf20Sopenharmony_ci state->dnxt.rolloff_val = CX24116_ROLLOFF_025; 12728c2ecf20Sopenharmony_ci break; 12738c2ecf20Sopenharmony_ci case ROLLOFF_35: 12748c2ecf20Sopenharmony_ci state->dnxt.rolloff_val = CX24116_ROLLOFF_035; 12758c2ecf20Sopenharmony_ci break; 12768c2ecf20Sopenharmony_ci case ROLLOFF_AUTO: /* Rolloff must be explicit */ 12778c2ecf20Sopenharmony_ci default: 12788c2ecf20Sopenharmony_ci dprintk("%s: unsupported rolloff selected (%d)\n", 12798c2ecf20Sopenharmony_ci __func__, c->rolloff); 12808c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci break; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci default: 12858c2ecf20Sopenharmony_ci dprintk("%s: unsupported delivery system selected (%d)\n", 12868c2ecf20Sopenharmony_ci __func__, c->delivery_system); 12878c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12888c2ecf20Sopenharmony_ci } 12898c2ecf20Sopenharmony_ci state->dnxt.delsys = c->delivery_system; 12908c2ecf20Sopenharmony_ci state->dnxt.modulation = c->modulation; 12918c2ecf20Sopenharmony_ci state->dnxt.frequency = c->frequency; 12928c2ecf20Sopenharmony_ci state->dnxt.pilot = c->pilot; 12938c2ecf20Sopenharmony_ci state->dnxt.rolloff = c->rolloff; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci ret = cx24116_set_inversion(state, c->inversion); 12968c2ecf20Sopenharmony_ci if (ret != 0) 12978c2ecf20Sopenharmony_ci return ret; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci /* FEC_NONE/AUTO for DVB-S2 is not supported and detected here */ 13008c2ecf20Sopenharmony_ci ret = cx24116_set_fec(state, c->delivery_system, c->modulation, c->fec_inner); 13018c2ecf20Sopenharmony_ci if (ret != 0) 13028c2ecf20Sopenharmony_ci return ret; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci ret = cx24116_set_symbolrate(state, c->symbol_rate); 13058c2ecf20Sopenharmony_ci if (ret != 0) 13068c2ecf20Sopenharmony_ci return ret; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci /* discard the 'current' tuning parameters and prepare to tune */ 13098c2ecf20Sopenharmony_ci cx24116_clone_params(fe); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci dprintk("%s: delsys = %d\n", __func__, state->dcur.delsys); 13128c2ecf20Sopenharmony_ci dprintk("%s: modulation = %d\n", __func__, state->dcur.modulation); 13138c2ecf20Sopenharmony_ci dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency); 13148c2ecf20Sopenharmony_ci dprintk("%s: pilot = %d (val = 0x%02x)\n", __func__, 13158c2ecf20Sopenharmony_ci state->dcur.pilot, state->dcur.pilot_val); 13168c2ecf20Sopenharmony_ci dprintk("%s: retune = %d\n", __func__, retune); 13178c2ecf20Sopenharmony_ci dprintk("%s: rolloff = %d (val = 0x%02x)\n", __func__, 13188c2ecf20Sopenharmony_ci state->dcur.rolloff, state->dcur.rolloff_val); 13198c2ecf20Sopenharmony_ci dprintk("%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate); 13208c2ecf20Sopenharmony_ci dprintk("%s: FEC = %d (mask/val = 0x%02x/0x%02x)\n", __func__, 13218c2ecf20Sopenharmony_ci state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val); 13228c2ecf20Sopenharmony_ci dprintk("%s: Inversion = %d (val = 0x%02x)\n", __func__, 13238c2ecf20Sopenharmony_ci state->dcur.inversion, state->dcur.inversion_val); 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci /* This is also done in advise/acquire on HVR4000 but not on LITE */ 13268c2ecf20Sopenharmony_ci if (state->config->set_ts_params) 13278c2ecf20Sopenharmony_ci state->config->set_ts_params(fe, 0); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci /* Set/Reset B/W */ 13308c2ecf20Sopenharmony_ci cmd.args[0x00] = CMD_BANDWIDTH; 13318c2ecf20Sopenharmony_ci cmd.args[0x01] = 0x01; 13328c2ecf20Sopenharmony_ci cmd.len = 0x02; 13338c2ecf20Sopenharmony_ci ret = cx24116_cmd_execute(fe, &cmd); 13348c2ecf20Sopenharmony_ci if (ret != 0) 13358c2ecf20Sopenharmony_ci return ret; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci /* Prepare a tune request */ 13388c2ecf20Sopenharmony_ci cmd.args[0x00] = CMD_TUNEREQUEST; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci /* Frequency */ 13418c2ecf20Sopenharmony_ci cmd.args[0x01] = (state->dcur.frequency & 0xff0000) >> 16; 13428c2ecf20Sopenharmony_ci cmd.args[0x02] = (state->dcur.frequency & 0x00ff00) >> 8; 13438c2ecf20Sopenharmony_ci cmd.args[0x03] = (state->dcur.frequency & 0x0000ff); 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci /* Symbol Rate */ 13468c2ecf20Sopenharmony_ci cmd.args[0x04] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8; 13478c2ecf20Sopenharmony_ci cmd.args[0x05] = ((state->dcur.symbol_rate / 1000) & 0x00ff); 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci /* Automatic Inversion */ 13508c2ecf20Sopenharmony_ci cmd.args[0x06] = state->dcur.inversion_val; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci /* Modulation / FEC / Pilot */ 13538c2ecf20Sopenharmony_ci cmd.args[0x07] = state->dcur.fec_val | state->dcur.pilot_val; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci cmd.args[0x08] = CX24116_SEARCH_RANGE_KHZ >> 8; 13568c2ecf20Sopenharmony_ci cmd.args[0x09] = CX24116_SEARCH_RANGE_KHZ & 0xff; 13578c2ecf20Sopenharmony_ci cmd.args[0x0a] = 0x00; 13588c2ecf20Sopenharmony_ci cmd.args[0x0b] = 0x00; 13598c2ecf20Sopenharmony_ci cmd.args[0x0c] = state->dcur.rolloff_val; 13608c2ecf20Sopenharmony_ci cmd.args[0x0d] = state->dcur.fec_mask; 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci if (state->dcur.symbol_rate > 30000000) { 13638c2ecf20Sopenharmony_ci cmd.args[0x0e] = 0x04; 13648c2ecf20Sopenharmony_ci cmd.args[0x0f] = 0x00; 13658c2ecf20Sopenharmony_ci cmd.args[0x10] = 0x01; 13668c2ecf20Sopenharmony_ci cmd.args[0x11] = 0x77; 13678c2ecf20Sopenharmony_ci cmd.args[0x12] = 0x36; 13688c2ecf20Sopenharmony_ci cx24116_writereg(state, CX24116_REG_CLKDIV, 0x44); 13698c2ecf20Sopenharmony_ci cx24116_writereg(state, CX24116_REG_RATEDIV, 0x01); 13708c2ecf20Sopenharmony_ci } else { 13718c2ecf20Sopenharmony_ci cmd.args[0x0e] = 0x06; 13728c2ecf20Sopenharmony_ci cmd.args[0x0f] = 0x00; 13738c2ecf20Sopenharmony_ci cmd.args[0x10] = 0x00; 13748c2ecf20Sopenharmony_ci cmd.args[0x11] = 0xFA; 13758c2ecf20Sopenharmony_ci cmd.args[0x12] = 0x24; 13768c2ecf20Sopenharmony_ci cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46); 13778c2ecf20Sopenharmony_ci cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00); 13788c2ecf20Sopenharmony_ci } 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci cmd.len = 0x13; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci /* We need to support pilot and non-pilot tuning in the 13838c2ecf20Sopenharmony_ci * driver automatically. This is a workaround for because 13848c2ecf20Sopenharmony_ci * the demod does not support autodetect. 13858c2ecf20Sopenharmony_ci */ 13868c2ecf20Sopenharmony_ci do { 13878c2ecf20Sopenharmony_ci /* Reset status register */ 13888c2ecf20Sopenharmony_ci status = cx24116_readreg(state, CX24116_REG_SSTATUS) 13898c2ecf20Sopenharmony_ci & CX24116_SIGNAL_MASK; 13908c2ecf20Sopenharmony_ci cx24116_writereg(state, CX24116_REG_SSTATUS, status); 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci /* Tune */ 13938c2ecf20Sopenharmony_ci ret = cx24116_cmd_execute(fe, &cmd); 13948c2ecf20Sopenharmony_ci if (ret != 0) 13958c2ecf20Sopenharmony_ci break; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci /* 13988c2ecf20Sopenharmony_ci * Wait for up to 500 ms before retrying 13998c2ecf20Sopenharmony_ci * 14008c2ecf20Sopenharmony_ci * If we are able to tune then generally it occurs within 100ms. 14018c2ecf20Sopenharmony_ci * If it takes longer, try a different toneburst setting. 14028c2ecf20Sopenharmony_ci */ 14038c2ecf20Sopenharmony_ci for (i = 0; i < 50 ; i++) { 14048c2ecf20Sopenharmony_ci cx24116_read_status(fe, &tunerstat); 14058c2ecf20Sopenharmony_ci status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC); 14068c2ecf20Sopenharmony_ci if (status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) { 14078c2ecf20Sopenharmony_ci dprintk("%s: Tuned\n", __func__); 14088c2ecf20Sopenharmony_ci goto tuned; 14098c2ecf20Sopenharmony_ci } 14108c2ecf20Sopenharmony_ci msleep(10); 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci dprintk("%s: Not tuned\n", __func__); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci /* Toggle pilot bit when in auto-pilot */ 14168c2ecf20Sopenharmony_ci if (state->dcur.pilot == PILOT_AUTO) 14178c2ecf20Sopenharmony_ci cmd.args[0x07] ^= CX24116_PILOT_ON; 14188c2ecf20Sopenharmony_ci } while (--retune); 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_cituned: /* Set/Reset B/W */ 14218c2ecf20Sopenharmony_ci cmd.args[0x00] = CMD_BANDWIDTH; 14228c2ecf20Sopenharmony_ci cmd.args[0x01] = 0x00; 14238c2ecf20Sopenharmony_ci cmd.len = 0x02; 14248c2ecf20Sopenharmony_ci return cx24116_cmd_execute(fe, &cmd); 14258c2ecf20Sopenharmony_ci} 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_cistatic int cx24116_tune(struct dvb_frontend *fe, bool re_tune, 14288c2ecf20Sopenharmony_ci unsigned int mode_flags, unsigned int *delay, enum fe_status *status) 14298c2ecf20Sopenharmony_ci{ 14308c2ecf20Sopenharmony_ci /* 14318c2ecf20Sopenharmony_ci * It is safe to discard "params" here, as the DVB core will sync 14328c2ecf20Sopenharmony_ci * fe->dtv_property_cache with fepriv->parameters_in, where the 14338c2ecf20Sopenharmony_ci * DVBv3 params are stored. The only practical usage for it indicate 14348c2ecf20Sopenharmony_ci * that re-tuning is needed, e. g. (fepriv->state & FESTATE_RETUNE) is 14358c2ecf20Sopenharmony_ci * true. 14368c2ecf20Sopenharmony_ci */ 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci *delay = HZ / 5; 14398c2ecf20Sopenharmony_ci if (re_tune) { 14408c2ecf20Sopenharmony_ci int ret = cx24116_set_frontend(fe); 14418c2ecf20Sopenharmony_ci if (ret) 14428c2ecf20Sopenharmony_ci return ret; 14438c2ecf20Sopenharmony_ci } 14448c2ecf20Sopenharmony_ci return cx24116_read_status(fe, status); 14458c2ecf20Sopenharmony_ci} 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_cistatic enum dvbfe_algo cx24116_get_algo(struct dvb_frontend *fe) 14488c2ecf20Sopenharmony_ci{ 14498c2ecf20Sopenharmony_ci return DVBFE_ALGO_HW; 14508c2ecf20Sopenharmony_ci} 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops cx24116_ops = { 14538c2ecf20Sopenharmony_ci .delsys = { SYS_DVBS, SYS_DVBS2 }, 14548c2ecf20Sopenharmony_ci .info = { 14558c2ecf20Sopenharmony_ci .name = "Conexant CX24116/CX24118", 14568c2ecf20Sopenharmony_ci .frequency_min_hz = 950 * MHz, 14578c2ecf20Sopenharmony_ci .frequency_max_hz = 2150 * MHz, 14588c2ecf20Sopenharmony_ci .frequency_stepsize_hz = 1011 * kHz, 14598c2ecf20Sopenharmony_ci .frequency_tolerance_hz = 5 * MHz, 14608c2ecf20Sopenharmony_ci .symbol_rate_min = 1000000, 14618c2ecf20Sopenharmony_ci .symbol_rate_max = 45000000, 14628c2ecf20Sopenharmony_ci .caps = FE_CAN_INVERSION_AUTO | 14638c2ecf20Sopenharmony_ci FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 14648c2ecf20Sopenharmony_ci FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | 14658c2ecf20Sopenharmony_ci FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | 14668c2ecf20Sopenharmony_ci FE_CAN_2G_MODULATION | 14678c2ecf20Sopenharmony_ci FE_CAN_QPSK | FE_CAN_RECOVER 14688c2ecf20Sopenharmony_ci }, 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci .release = cx24116_release, 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci .init = cx24116_initfe, 14738c2ecf20Sopenharmony_ci .sleep = cx24116_sleep, 14748c2ecf20Sopenharmony_ci .read_status = cx24116_read_status, 14758c2ecf20Sopenharmony_ci .read_ber = cx24116_read_ber, 14768c2ecf20Sopenharmony_ci .read_signal_strength = cx24116_read_signal_strength, 14778c2ecf20Sopenharmony_ci .read_snr = cx24116_read_snr, 14788c2ecf20Sopenharmony_ci .read_ucblocks = cx24116_read_ucblocks, 14798c2ecf20Sopenharmony_ci .set_tone = cx24116_set_tone, 14808c2ecf20Sopenharmony_ci .set_voltage = cx24116_set_voltage, 14818c2ecf20Sopenharmony_ci .diseqc_send_master_cmd = cx24116_send_diseqc_msg, 14828c2ecf20Sopenharmony_ci .diseqc_send_burst = cx24116_diseqc_send_burst, 14838c2ecf20Sopenharmony_ci .get_frontend_algo = cx24116_get_algo, 14848c2ecf20Sopenharmony_ci .tune = cx24116_tune, 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci .set_frontend = cx24116_set_frontend, 14878c2ecf20Sopenharmony_ci}; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DVB Frontend module for Conexant cx24116/cx24118 hardware"); 14908c2ecf20Sopenharmony_ciMODULE_AUTHOR("Steven Toth"); 14918c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 14928c2ecf20Sopenharmony_ci 1493