18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Conexant cx24123/cx24109 - DVB QPSK Satellite demod/tuner driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2005 Steven Toth <stoth@linuxtv.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Support for KWorld DVB-S 100 by Vadim Catana <skystar@moldova.cc> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Support for CX24123/CX24113-NIM by Patrick Boettcher <pb@linuxtv.org> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <asm/div64.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 198c2ecf20Sopenharmony_ci#include "cx24123.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define XTAL 10111000 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic int force_band; 248c2ecf20Sopenharmony_cimodule_param(force_band, int, 0644); 258c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_band, "Force a specific band select "\ 268c2ecf20Sopenharmony_ci "(1-9, default:off)."); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int debug; 298c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 308c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define info(args...) do { printk(KERN_INFO "CX24123: " args); } while (0) 338c2ecf20Sopenharmony_ci#define err(args...) do { printk(KERN_ERR "CX24123: " args); } while (0) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define dprintk(args...) \ 368c2ecf20Sopenharmony_ci do { \ 378c2ecf20Sopenharmony_ci if (debug) { \ 388c2ecf20Sopenharmony_ci printk(KERN_DEBUG "CX24123: %s: ", __func__); \ 398c2ecf20Sopenharmony_ci printk(args); \ 408c2ecf20Sopenharmony_ci } \ 418c2ecf20Sopenharmony_ci } while (0) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct cx24123_state { 448c2ecf20Sopenharmony_ci struct i2c_adapter *i2c; 458c2ecf20Sopenharmony_ci const struct cx24123_config *config; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci struct dvb_frontend frontend; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci /* Some PLL specifics for tuning */ 508c2ecf20Sopenharmony_ci u32 VCAarg; 518c2ecf20Sopenharmony_ci u32 VGAarg; 528c2ecf20Sopenharmony_ci u32 bandselectarg; 538c2ecf20Sopenharmony_ci u32 pllarg; 548c2ecf20Sopenharmony_ci u32 FILTune; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci struct i2c_adapter tuner_i2c_adapter; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci u8 demod_rev; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* The Demod/Tuner can't easily provide these, we cache them */ 618c2ecf20Sopenharmony_ci u32 currentfreq; 628c2ecf20Sopenharmony_ci u32 currentsymbolrate; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* Various tuner defaults need to be established for a given symbol rate Sps */ 668c2ecf20Sopenharmony_cistatic struct cx24123_AGC_val { 678c2ecf20Sopenharmony_ci u32 symbolrate_low; 688c2ecf20Sopenharmony_ci u32 symbolrate_high; 698c2ecf20Sopenharmony_ci u32 VCAprogdata; 708c2ecf20Sopenharmony_ci u32 VGAprogdata; 718c2ecf20Sopenharmony_ci u32 FILTune; 728c2ecf20Sopenharmony_ci} cx24123_AGC_vals[] = 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci { 758c2ecf20Sopenharmony_ci .symbolrate_low = 1000000, 768c2ecf20Sopenharmony_ci .symbolrate_high = 4999999, 778c2ecf20Sopenharmony_ci /* the specs recommend other values for VGA offsets, 788c2ecf20Sopenharmony_ci but tests show they are wrong */ 798c2ecf20Sopenharmony_ci .VGAprogdata = (1 << 19) | (0x180 << 9) | 0x1e0, 808c2ecf20Sopenharmony_ci .VCAprogdata = (2 << 19) | (0x07 << 9) | 0x07, 818c2ecf20Sopenharmony_ci .FILTune = 0x27f /* 0.41 V */ 828c2ecf20Sopenharmony_ci }, 838c2ecf20Sopenharmony_ci { 848c2ecf20Sopenharmony_ci .symbolrate_low = 5000000, 858c2ecf20Sopenharmony_ci .symbolrate_high = 14999999, 868c2ecf20Sopenharmony_ci .VGAprogdata = (1 << 19) | (0x180 << 9) | 0x1e0, 878c2ecf20Sopenharmony_ci .VCAprogdata = (2 << 19) | (0x07 << 9) | 0x1f, 888c2ecf20Sopenharmony_ci .FILTune = 0x317 /* 0.90 V */ 898c2ecf20Sopenharmony_ci }, 908c2ecf20Sopenharmony_ci { 918c2ecf20Sopenharmony_ci .symbolrate_low = 15000000, 928c2ecf20Sopenharmony_ci .symbolrate_high = 45000000, 938c2ecf20Sopenharmony_ci .VGAprogdata = (1 << 19) | (0x100 << 9) | 0x180, 948c2ecf20Sopenharmony_ci .VCAprogdata = (2 << 19) | (0x07 << 9) | 0x3f, 958c2ecf20Sopenharmony_ci .FILTune = 0x145 /* 2.70 V */ 968c2ecf20Sopenharmony_ci }, 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* 1008c2ecf20Sopenharmony_ci * Various tuner defaults need to be established for a given frequency kHz. 1018c2ecf20Sopenharmony_ci * fixme: The bounds on the bands do not match the doc in real life. 1028c2ecf20Sopenharmony_ci * fixme: Some of them have been moved, other might need adjustment. 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_cistatic struct cx24123_bandselect_val { 1058c2ecf20Sopenharmony_ci u32 freq_low; 1068c2ecf20Sopenharmony_ci u32 freq_high; 1078c2ecf20Sopenharmony_ci u32 VCOdivider; 1088c2ecf20Sopenharmony_ci u32 progdata; 1098c2ecf20Sopenharmony_ci} cx24123_bandselect_vals[] = 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci /* band 1 */ 1128c2ecf20Sopenharmony_ci { 1138c2ecf20Sopenharmony_ci .freq_low = 950000, 1148c2ecf20Sopenharmony_ci .freq_high = 1074999, 1158c2ecf20Sopenharmony_ci .VCOdivider = 4, 1168c2ecf20Sopenharmony_ci .progdata = (0 << 19) | (0 << 9) | 0x40, 1178c2ecf20Sopenharmony_ci }, 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* band 2 */ 1208c2ecf20Sopenharmony_ci { 1218c2ecf20Sopenharmony_ci .freq_low = 1075000, 1228c2ecf20Sopenharmony_ci .freq_high = 1177999, 1238c2ecf20Sopenharmony_ci .VCOdivider = 4, 1248c2ecf20Sopenharmony_ci .progdata = (0 << 19) | (0 << 9) | 0x80, 1258c2ecf20Sopenharmony_ci }, 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* band 3 */ 1288c2ecf20Sopenharmony_ci { 1298c2ecf20Sopenharmony_ci .freq_low = 1178000, 1308c2ecf20Sopenharmony_ci .freq_high = 1295999, 1318c2ecf20Sopenharmony_ci .VCOdivider = 2, 1328c2ecf20Sopenharmony_ci .progdata = (0 << 19) | (1 << 9) | 0x01, 1338c2ecf20Sopenharmony_ci }, 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* band 4 */ 1368c2ecf20Sopenharmony_ci { 1378c2ecf20Sopenharmony_ci .freq_low = 1296000, 1388c2ecf20Sopenharmony_ci .freq_high = 1431999, 1398c2ecf20Sopenharmony_ci .VCOdivider = 2, 1408c2ecf20Sopenharmony_ci .progdata = (0 << 19) | (1 << 9) | 0x02, 1418c2ecf20Sopenharmony_ci }, 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* band 5 */ 1448c2ecf20Sopenharmony_ci { 1458c2ecf20Sopenharmony_ci .freq_low = 1432000, 1468c2ecf20Sopenharmony_ci .freq_high = 1575999, 1478c2ecf20Sopenharmony_ci .VCOdivider = 2, 1488c2ecf20Sopenharmony_ci .progdata = (0 << 19) | (1 << 9) | 0x04, 1498c2ecf20Sopenharmony_ci }, 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* band 6 */ 1528c2ecf20Sopenharmony_ci { 1538c2ecf20Sopenharmony_ci .freq_low = 1576000, 1548c2ecf20Sopenharmony_ci .freq_high = 1717999, 1558c2ecf20Sopenharmony_ci .VCOdivider = 2, 1568c2ecf20Sopenharmony_ci .progdata = (0 << 19) | (1 << 9) | 0x08, 1578c2ecf20Sopenharmony_ci }, 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* band 7 */ 1608c2ecf20Sopenharmony_ci { 1618c2ecf20Sopenharmony_ci .freq_low = 1718000, 1628c2ecf20Sopenharmony_ci .freq_high = 1855999, 1638c2ecf20Sopenharmony_ci .VCOdivider = 2, 1648c2ecf20Sopenharmony_ci .progdata = (0 << 19) | (1 << 9) | 0x10, 1658c2ecf20Sopenharmony_ci }, 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* band 8 */ 1688c2ecf20Sopenharmony_ci { 1698c2ecf20Sopenharmony_ci .freq_low = 1856000, 1708c2ecf20Sopenharmony_ci .freq_high = 2035999, 1718c2ecf20Sopenharmony_ci .VCOdivider = 2, 1728c2ecf20Sopenharmony_ci .progdata = (0 << 19) | (1 << 9) | 0x20, 1738c2ecf20Sopenharmony_ci }, 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* band 9 */ 1768c2ecf20Sopenharmony_ci { 1778c2ecf20Sopenharmony_ci .freq_low = 2036000, 1788c2ecf20Sopenharmony_ci .freq_high = 2150000, 1798c2ecf20Sopenharmony_ci .VCOdivider = 2, 1808c2ecf20Sopenharmony_ci .progdata = (0 << 19) | (1 << 9) | 0x40, 1818c2ecf20Sopenharmony_ci }, 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic struct { 1858c2ecf20Sopenharmony_ci u8 reg; 1868c2ecf20Sopenharmony_ci u8 data; 1878c2ecf20Sopenharmony_ci} cx24123_regdata[] = 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci {0x00, 0x03}, /* Reset system */ 1908c2ecf20Sopenharmony_ci {0x00, 0x00}, /* Clear reset */ 1918c2ecf20Sopenharmony_ci {0x03, 0x07}, /* QPSK, DVB, Auto Acquisition (default) */ 1928c2ecf20Sopenharmony_ci {0x04, 0x10}, /* MPEG */ 1938c2ecf20Sopenharmony_ci {0x05, 0x04}, /* MPEG */ 1948c2ecf20Sopenharmony_ci {0x06, 0x31}, /* MPEG (default) */ 1958c2ecf20Sopenharmony_ci {0x0b, 0x00}, /* Freq search start point (default) */ 1968c2ecf20Sopenharmony_ci {0x0c, 0x00}, /* Demodulator sample gain (default) */ 1978c2ecf20Sopenharmony_ci {0x0d, 0x7f}, /* Force driver to shift until the maximum (+-10 MHz) */ 1988c2ecf20Sopenharmony_ci {0x0e, 0x03}, /* Default non-inverted, FEC 3/4 (default) */ 1998c2ecf20Sopenharmony_ci {0x0f, 0xfe}, /* FEC search mask (all supported codes) */ 2008c2ecf20Sopenharmony_ci {0x10, 0x01}, /* Default search inversion, no repeat (default) */ 2018c2ecf20Sopenharmony_ci {0x16, 0x00}, /* Enable reading of frequency */ 2028c2ecf20Sopenharmony_ci {0x17, 0x01}, /* Enable EsNO Ready Counter */ 2038c2ecf20Sopenharmony_ci {0x1c, 0x80}, /* Enable error counter */ 2048c2ecf20Sopenharmony_ci {0x20, 0x00}, /* Tuner burst clock rate = 500KHz */ 2058c2ecf20Sopenharmony_ci {0x21, 0x15}, /* Tuner burst mode, word length = 0x15 */ 2068c2ecf20Sopenharmony_ci {0x28, 0x00}, /* Enable FILTERV with positive pol., DiSEqC 2.x off */ 2078c2ecf20Sopenharmony_ci {0x29, 0x00}, /* DiSEqC LNB_DC off */ 2088c2ecf20Sopenharmony_ci {0x2a, 0xb0}, /* DiSEqC Parameters (default) */ 2098c2ecf20Sopenharmony_ci {0x2b, 0x73}, /* DiSEqC Tone Frequency (default) */ 2108c2ecf20Sopenharmony_ci {0x2c, 0x00}, /* DiSEqC Message (0x2c - 0x31) */ 2118c2ecf20Sopenharmony_ci {0x2d, 0x00}, 2128c2ecf20Sopenharmony_ci {0x2e, 0x00}, 2138c2ecf20Sopenharmony_ci {0x2f, 0x00}, 2148c2ecf20Sopenharmony_ci {0x30, 0x00}, 2158c2ecf20Sopenharmony_ci {0x31, 0x00}, 2168c2ecf20Sopenharmony_ci {0x32, 0x8c}, /* DiSEqC Parameters (default) */ 2178c2ecf20Sopenharmony_ci {0x33, 0x00}, /* Interrupts off (0x33 - 0x34) */ 2188c2ecf20Sopenharmony_ci {0x34, 0x00}, 2198c2ecf20Sopenharmony_ci {0x35, 0x03}, /* DiSEqC Tone Amplitude (default) */ 2208c2ecf20Sopenharmony_ci {0x36, 0x02}, /* DiSEqC Parameters (default) */ 2218c2ecf20Sopenharmony_ci {0x37, 0x3a}, /* DiSEqC Parameters (default) */ 2228c2ecf20Sopenharmony_ci {0x3a, 0x00}, /* Enable AGC accumulator (for signal strength) */ 2238c2ecf20Sopenharmony_ci {0x44, 0x00}, /* Constellation (default) */ 2248c2ecf20Sopenharmony_ci {0x45, 0x00}, /* Symbol count (default) */ 2258c2ecf20Sopenharmony_ci {0x46, 0x0d}, /* Symbol rate estimator on (default) */ 2268c2ecf20Sopenharmony_ci {0x56, 0xc1}, /* Error Counter = Viterbi BER */ 2278c2ecf20Sopenharmony_ci {0x57, 0xff}, /* Error Counter Window (default) */ 2288c2ecf20Sopenharmony_ci {0x5c, 0x20}, /* Acquisition AFC Expiration window (default is 0x10) */ 2298c2ecf20Sopenharmony_ci {0x67, 0x83}, /* Non-DCII symbol clock */ 2308c2ecf20Sopenharmony_ci}; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic int cx24123_i2c_writereg(struct cx24123_state *state, 2338c2ecf20Sopenharmony_ci u8 i2c_addr, int reg, int data) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci u8 buf[] = { reg, data }; 2368c2ecf20Sopenharmony_ci struct i2c_msg msg = { 2378c2ecf20Sopenharmony_ci .addr = i2c_addr, .flags = 0, .buf = buf, .len = 2 2388c2ecf20Sopenharmony_ci }; 2398c2ecf20Sopenharmony_ci int err; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* printk(KERN_DEBUG "wr(%02x): %02x %02x\n", i2c_addr, reg, data); */ 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci err = i2c_transfer(state->i2c, &msg, 1); 2448c2ecf20Sopenharmony_ci if (err != 1) { 2458c2ecf20Sopenharmony_ci printk("%s: writereg error(err == %i, reg == 0x%02x, data == 0x%02x)\n", 2468c2ecf20Sopenharmony_ci __func__, err, reg, data); 2478c2ecf20Sopenharmony_ci return err; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int cx24123_i2c_readreg(struct cx24123_state *state, u8 i2c_addr, u8 reg) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci int ret; 2568c2ecf20Sopenharmony_ci u8 b = 0; 2578c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 2588c2ecf20Sopenharmony_ci { .addr = i2c_addr, .flags = 0, .buf = ®, .len = 1 }, 2598c2ecf20Sopenharmony_ci { .addr = i2c_addr, .flags = I2C_M_RD, .buf = &b, .len = 1 } 2608c2ecf20Sopenharmony_ci }; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci ret = i2c_transfer(state->i2c, msg, 2); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (ret != 2) { 2658c2ecf20Sopenharmony_ci err("%s: reg=0x%x (error=%d)\n", __func__, reg, ret); 2668c2ecf20Sopenharmony_ci return ret; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* printk(KERN_DEBUG "rd(%02x): %02x %02x\n", i2c_addr, reg, b); */ 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci return b; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci#define cx24123_readreg(state, reg) \ 2758c2ecf20Sopenharmony_ci cx24123_i2c_readreg(state, state->config->demod_address, reg) 2768c2ecf20Sopenharmony_ci#define cx24123_writereg(state, reg, val) \ 2778c2ecf20Sopenharmony_ci cx24123_i2c_writereg(state, state->config->demod_address, reg, val) 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int cx24123_set_inversion(struct cx24123_state *state, 2808c2ecf20Sopenharmony_ci enum fe_spectral_inversion inversion) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci u8 nom_reg = cx24123_readreg(state, 0x0e); 2838c2ecf20Sopenharmony_ci u8 auto_reg = cx24123_readreg(state, 0x10); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci switch (inversion) { 2868c2ecf20Sopenharmony_ci case INVERSION_OFF: 2878c2ecf20Sopenharmony_ci dprintk("inversion off\n"); 2888c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0e, nom_reg & ~0x80); 2898c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x10, auto_reg | 0x80); 2908c2ecf20Sopenharmony_ci break; 2918c2ecf20Sopenharmony_ci case INVERSION_ON: 2928c2ecf20Sopenharmony_ci dprintk("inversion on\n"); 2938c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0e, nom_reg | 0x80); 2948c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x10, auto_reg | 0x80); 2958c2ecf20Sopenharmony_ci break; 2968c2ecf20Sopenharmony_ci case INVERSION_AUTO: 2978c2ecf20Sopenharmony_ci dprintk("inversion auto\n"); 2988c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x10, auto_reg & ~0x80); 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci default: 3018c2ecf20Sopenharmony_ci return -EINVAL; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int cx24123_get_inversion(struct cx24123_state *state, 3088c2ecf20Sopenharmony_ci enum fe_spectral_inversion *inversion) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci u8 val; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci val = cx24123_readreg(state, 0x1b) >> 7; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (val == 0) { 3158c2ecf20Sopenharmony_ci dprintk("read inversion off\n"); 3168c2ecf20Sopenharmony_ci *inversion = INVERSION_OFF; 3178c2ecf20Sopenharmony_ci } else { 3188c2ecf20Sopenharmony_ci dprintk("read inversion on\n"); 3198c2ecf20Sopenharmony_ci *inversion = INVERSION_ON; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int cx24123_set_fec(struct cx24123_state *state, enum fe_code_rate fec) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci u8 nom_reg = cx24123_readreg(state, 0x0e) & ~0x07; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (((int)fec < FEC_NONE) || (fec > FEC_AUTO)) 3308c2ecf20Sopenharmony_ci fec = FEC_AUTO; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* Set the soft decision threshold */ 3338c2ecf20Sopenharmony_ci if (fec == FEC_1_2) 3348c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x43, 3358c2ecf20Sopenharmony_ci cx24123_readreg(state, 0x43) | 0x01); 3368c2ecf20Sopenharmony_ci else 3378c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x43, 3388c2ecf20Sopenharmony_ci cx24123_readreg(state, 0x43) & ~0x01); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci switch (fec) { 3418c2ecf20Sopenharmony_ci case FEC_1_2: 3428c2ecf20Sopenharmony_ci dprintk("set FEC to 1/2\n"); 3438c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0e, nom_reg | 0x01); 3448c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0f, 0x02); 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci case FEC_2_3: 3478c2ecf20Sopenharmony_ci dprintk("set FEC to 2/3\n"); 3488c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0e, nom_reg | 0x02); 3498c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0f, 0x04); 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci case FEC_3_4: 3528c2ecf20Sopenharmony_ci dprintk("set FEC to 3/4\n"); 3538c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0e, nom_reg | 0x03); 3548c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0f, 0x08); 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci case FEC_4_5: 3578c2ecf20Sopenharmony_ci dprintk("set FEC to 4/5\n"); 3588c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0e, nom_reg | 0x04); 3598c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0f, 0x10); 3608c2ecf20Sopenharmony_ci break; 3618c2ecf20Sopenharmony_ci case FEC_5_6: 3628c2ecf20Sopenharmony_ci dprintk("set FEC to 5/6\n"); 3638c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0e, nom_reg | 0x05); 3648c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0f, 0x20); 3658c2ecf20Sopenharmony_ci break; 3668c2ecf20Sopenharmony_ci case FEC_6_7: 3678c2ecf20Sopenharmony_ci dprintk("set FEC to 6/7\n"); 3688c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0e, nom_reg | 0x06); 3698c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0f, 0x40); 3708c2ecf20Sopenharmony_ci break; 3718c2ecf20Sopenharmony_ci case FEC_7_8: 3728c2ecf20Sopenharmony_ci dprintk("set FEC to 7/8\n"); 3738c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0e, nom_reg | 0x07); 3748c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0f, 0x80); 3758c2ecf20Sopenharmony_ci break; 3768c2ecf20Sopenharmony_ci case FEC_AUTO: 3778c2ecf20Sopenharmony_ci dprintk("set FEC to auto\n"); 3788c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0f, 0xfe); 3798c2ecf20Sopenharmony_ci break; 3808c2ecf20Sopenharmony_ci default: 3818c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci return 0; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic int cx24123_get_fec(struct cx24123_state *state, enum fe_code_rate *fec) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci int ret; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci ret = cx24123_readreg(state, 0x1b); 3928c2ecf20Sopenharmony_ci if (ret < 0) 3938c2ecf20Sopenharmony_ci return ret; 3948c2ecf20Sopenharmony_ci ret = ret & 0x07; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci switch (ret) { 3978c2ecf20Sopenharmony_ci case 1: 3988c2ecf20Sopenharmony_ci *fec = FEC_1_2; 3998c2ecf20Sopenharmony_ci break; 4008c2ecf20Sopenharmony_ci case 2: 4018c2ecf20Sopenharmony_ci *fec = FEC_2_3; 4028c2ecf20Sopenharmony_ci break; 4038c2ecf20Sopenharmony_ci case 3: 4048c2ecf20Sopenharmony_ci *fec = FEC_3_4; 4058c2ecf20Sopenharmony_ci break; 4068c2ecf20Sopenharmony_ci case 4: 4078c2ecf20Sopenharmony_ci *fec = FEC_4_5; 4088c2ecf20Sopenharmony_ci break; 4098c2ecf20Sopenharmony_ci case 5: 4108c2ecf20Sopenharmony_ci *fec = FEC_5_6; 4118c2ecf20Sopenharmony_ci break; 4128c2ecf20Sopenharmony_ci case 6: 4138c2ecf20Sopenharmony_ci *fec = FEC_6_7; 4148c2ecf20Sopenharmony_ci break; 4158c2ecf20Sopenharmony_ci case 7: 4168c2ecf20Sopenharmony_ci *fec = FEC_7_8; 4178c2ecf20Sopenharmony_ci break; 4188c2ecf20Sopenharmony_ci default: 4198c2ecf20Sopenharmony_ci /* this can happen when there's no lock */ 4208c2ecf20Sopenharmony_ci *fec = FEC_NONE; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci return 0; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci/* Approximation of closest integer of log2(a/b). It actually gives the 4278c2ecf20Sopenharmony_ci lowest integer i such that 2^i >= round(a/b) */ 4288c2ecf20Sopenharmony_cistatic u32 cx24123_int_log2(u32 a, u32 b) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci u32 exp, nearest = 0; 4318c2ecf20Sopenharmony_ci u32 div = a / b; 4328c2ecf20Sopenharmony_ci if (a % b >= b / 2) 4338c2ecf20Sopenharmony_ci ++div; 4348c2ecf20Sopenharmony_ci if (div < (1UL << 31)) { 4358c2ecf20Sopenharmony_ci for (exp = 1; div > exp; nearest++) 4368c2ecf20Sopenharmony_ci exp += exp; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci return nearest; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic int cx24123_set_symbolrate(struct cx24123_state *state, u32 srate) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci u64 tmp; 4448c2ecf20Sopenharmony_ci u32 sample_rate, ratio, sample_gain; 4458c2ecf20Sopenharmony_ci u8 pll_mult; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* check if symbol rate is within limits */ 4488c2ecf20Sopenharmony_ci if ((srate > state->frontend.ops.info.symbol_rate_max) || 4498c2ecf20Sopenharmony_ci (srate < state->frontend.ops.info.symbol_rate_min)) 4508c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* choose the sampling rate high enough for the required operation, 4538c2ecf20Sopenharmony_ci while optimizing the power consumed by the demodulator */ 4548c2ecf20Sopenharmony_ci if (srate < (XTAL*2)/2) 4558c2ecf20Sopenharmony_ci pll_mult = 2; 4568c2ecf20Sopenharmony_ci else if (srate < (XTAL*3)/2) 4578c2ecf20Sopenharmony_ci pll_mult = 3; 4588c2ecf20Sopenharmony_ci else if (srate < (XTAL*4)/2) 4598c2ecf20Sopenharmony_ci pll_mult = 4; 4608c2ecf20Sopenharmony_ci else if (srate < (XTAL*5)/2) 4618c2ecf20Sopenharmony_ci pll_mult = 5; 4628c2ecf20Sopenharmony_ci else if (srate < (XTAL*6)/2) 4638c2ecf20Sopenharmony_ci pll_mult = 6; 4648c2ecf20Sopenharmony_ci else if (srate < (XTAL*7)/2) 4658c2ecf20Sopenharmony_ci pll_mult = 7; 4668c2ecf20Sopenharmony_ci else if (srate < (XTAL*8)/2) 4678c2ecf20Sopenharmony_ci pll_mult = 8; 4688c2ecf20Sopenharmony_ci else 4698c2ecf20Sopenharmony_ci pll_mult = 9; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci sample_rate = pll_mult * XTAL; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* SYSSymbolRate[21:0] = (srate << 23) / sample_rate */ 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci tmp = ((u64)srate) << 23; 4778c2ecf20Sopenharmony_ci do_div(tmp, sample_rate); 4788c2ecf20Sopenharmony_ci ratio = (u32) tmp; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x01, pll_mult * 6); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x08, (ratio >> 16) & 0x3f); 4838c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x09, (ratio >> 8) & 0xff); 4848c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0a, ratio & 0xff); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci /* also set the demodulator sample gain */ 4878c2ecf20Sopenharmony_ci sample_gain = cx24123_int_log2(sample_rate, srate); 4888c2ecf20Sopenharmony_ci tmp = cx24123_readreg(state, 0x0c) & ~0xe0; 4898c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x0c, tmp | sample_gain << 5); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci dprintk("srate=%d, ratio=0x%08x, sample_rate=%i sample_gain=%d\n", 4928c2ecf20Sopenharmony_ci srate, ratio, sample_rate, sample_gain); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci return 0; 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci/* 4988c2ecf20Sopenharmony_ci * Based on the required frequency and symbolrate, the tuner AGC has 4998c2ecf20Sopenharmony_ci * to be configured and the correct band selected. 5008c2ecf20Sopenharmony_ci * Calculate those values. 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_cistatic int cx24123_pll_calculate(struct dvb_frontend *fe) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p = &fe->dtv_property_cache; 5058c2ecf20Sopenharmony_ci struct cx24123_state *state = fe->demodulator_priv; 5068c2ecf20Sopenharmony_ci u32 ndiv = 0, adiv = 0, vco_div = 0; 5078c2ecf20Sopenharmony_ci int i = 0; 5088c2ecf20Sopenharmony_ci int pump = 2; 5098c2ecf20Sopenharmony_ci int band = 0; 5108c2ecf20Sopenharmony_ci int num_bands = ARRAY_SIZE(cx24123_bandselect_vals); 5118c2ecf20Sopenharmony_ci struct cx24123_bandselect_val *bsv = NULL; 5128c2ecf20Sopenharmony_ci struct cx24123_AGC_val *agcv = NULL; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* Defaults for low freq, low rate */ 5158c2ecf20Sopenharmony_ci state->VCAarg = cx24123_AGC_vals[0].VCAprogdata; 5168c2ecf20Sopenharmony_ci state->VGAarg = cx24123_AGC_vals[0].VGAprogdata; 5178c2ecf20Sopenharmony_ci state->bandselectarg = cx24123_bandselect_vals[0].progdata; 5188c2ecf20Sopenharmony_ci vco_div = cx24123_bandselect_vals[0].VCOdivider; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci /* For the given symbol rate, determine the VCA, VGA and 5218c2ecf20Sopenharmony_ci * FILTUNE programming bits */ 5228c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cx24123_AGC_vals); i++) { 5238c2ecf20Sopenharmony_ci agcv = &cx24123_AGC_vals[i]; 5248c2ecf20Sopenharmony_ci if ((agcv->symbolrate_low <= p->symbol_rate) && 5258c2ecf20Sopenharmony_ci (agcv->symbolrate_high >= p->symbol_rate)) { 5268c2ecf20Sopenharmony_ci state->VCAarg = agcv->VCAprogdata; 5278c2ecf20Sopenharmony_ci state->VGAarg = agcv->VGAprogdata; 5288c2ecf20Sopenharmony_ci state->FILTune = agcv->FILTune; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* determine the band to use */ 5338c2ecf20Sopenharmony_ci if (force_band < 1 || force_band > num_bands) { 5348c2ecf20Sopenharmony_ci for (i = 0; i < num_bands; i++) { 5358c2ecf20Sopenharmony_ci bsv = &cx24123_bandselect_vals[i]; 5368c2ecf20Sopenharmony_ci if ((bsv->freq_low <= p->frequency) && 5378c2ecf20Sopenharmony_ci (bsv->freq_high >= p->frequency)) 5388c2ecf20Sopenharmony_ci band = i; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci } else 5418c2ecf20Sopenharmony_ci band = force_band - 1; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci state->bandselectarg = cx24123_bandselect_vals[band].progdata; 5448c2ecf20Sopenharmony_ci vco_div = cx24123_bandselect_vals[band].VCOdivider; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* determine the charge pump current */ 5478c2ecf20Sopenharmony_ci if (p->frequency < (cx24123_bandselect_vals[band].freq_low + 5488c2ecf20Sopenharmony_ci cx24123_bandselect_vals[band].freq_high) / 2) 5498c2ecf20Sopenharmony_ci pump = 0x01; 5508c2ecf20Sopenharmony_ci else 5518c2ecf20Sopenharmony_ci pump = 0x02; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* Determine the N/A dividers for the requested lband freq (in kHz). */ 5548c2ecf20Sopenharmony_ci /* Note: the reference divider R=10, frequency is in KHz, 5558c2ecf20Sopenharmony_ci * XTAL is in Hz */ 5568c2ecf20Sopenharmony_ci ndiv = (((p->frequency * vco_div * 10) / 5578c2ecf20Sopenharmony_ci (2 * XTAL / 1000)) / 32) & 0x1ff; 5588c2ecf20Sopenharmony_ci adiv = (((p->frequency * vco_div * 10) / 5598c2ecf20Sopenharmony_ci (2 * XTAL / 1000)) % 32) & 0x1f; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (adiv == 0 && ndiv > 0) 5628c2ecf20Sopenharmony_ci ndiv--; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci /* control bits 11, refdiv 11, charge pump polarity 1, 5658c2ecf20Sopenharmony_ci * charge pump current, ndiv, adiv */ 5668c2ecf20Sopenharmony_ci state->pllarg = (3 << 19) | (3 << 17) | (1 << 16) | 5678c2ecf20Sopenharmony_ci (pump << 14) | (ndiv << 5) | adiv; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci return 0; 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci/* 5738c2ecf20Sopenharmony_ci * Tuner data is 21 bits long, must be left-aligned in data. 5748c2ecf20Sopenharmony_ci * Tuner cx24109 is written through a dedicated 3wire interface 5758c2ecf20Sopenharmony_ci * on the demod chip. 5768c2ecf20Sopenharmony_ci */ 5778c2ecf20Sopenharmony_cistatic int cx24123_pll_writereg(struct dvb_frontend *fe, u32 data) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci struct cx24123_state *state = fe->demodulator_priv; 5808c2ecf20Sopenharmony_ci unsigned long timeout; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci dprintk("pll writereg called, data=0x%08x\n", data); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* align the 21 bytes into to bit23 boundary */ 5858c2ecf20Sopenharmony_ci data = data << 3; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* Reset the demod pll word length to 0x15 bits */ 5888c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x21, 0x15); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* write the msb 8 bits, wait for the send to be completed */ 5918c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(40); 5928c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x22, (data >> 16) & 0xff); 5938c2ecf20Sopenharmony_ci while ((cx24123_readreg(state, 0x20) & 0x40) == 0) { 5948c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 5958c2ecf20Sopenharmony_ci err("%s: demodulator is not responding, "\ 5968c2ecf20Sopenharmony_ci "possibly hung, aborting.\n", __func__); 5978c2ecf20Sopenharmony_ci return -EREMOTEIO; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci msleep(10); 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* send another 8 bytes, wait for the send to be completed */ 6038c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(40); 6048c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x22, (data >> 8) & 0xff); 6058c2ecf20Sopenharmony_ci while ((cx24123_readreg(state, 0x20) & 0x40) == 0) { 6068c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 6078c2ecf20Sopenharmony_ci err("%s: demodulator is not responding, "\ 6088c2ecf20Sopenharmony_ci "possibly hung, aborting.\n", __func__); 6098c2ecf20Sopenharmony_ci return -EREMOTEIO; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci msleep(10); 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci /* send the lower 5 bits of this byte, padded with 3 LBB, 6158c2ecf20Sopenharmony_ci * wait for the send to be completed */ 6168c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(40); 6178c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x22, (data) & 0xff); 6188c2ecf20Sopenharmony_ci while ((cx24123_readreg(state, 0x20) & 0x80)) { 6198c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 6208c2ecf20Sopenharmony_ci err("%s: demodulator is not responding," \ 6218c2ecf20Sopenharmony_ci "possibly hung, aborting.\n", __func__); 6228c2ecf20Sopenharmony_ci return -EREMOTEIO; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci msleep(10); 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* Trigger the demod to configure the tuner */ 6288c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x20, cx24123_readreg(state, 0x20) | 2); 6298c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x20, cx24123_readreg(state, 0x20) & 0xfd); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci return 0; 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_cistatic int cx24123_pll_tune(struct dvb_frontend *fe) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p = &fe->dtv_property_cache; 6378c2ecf20Sopenharmony_ci struct cx24123_state *state = fe->demodulator_priv; 6388c2ecf20Sopenharmony_ci u8 val; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci dprintk("frequency=%i\n", p->frequency); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (cx24123_pll_calculate(fe) != 0) { 6438c2ecf20Sopenharmony_ci err("%s: cx24123_pll_calculate failed\n", __func__); 6448c2ecf20Sopenharmony_ci return -EINVAL; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* Write the new VCO/VGA */ 6488c2ecf20Sopenharmony_ci cx24123_pll_writereg(fe, state->VCAarg); 6498c2ecf20Sopenharmony_ci cx24123_pll_writereg(fe, state->VGAarg); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci /* Write the new bandselect and pll args */ 6528c2ecf20Sopenharmony_ci cx24123_pll_writereg(fe, state->bandselectarg); 6538c2ecf20Sopenharmony_ci cx24123_pll_writereg(fe, state->pllarg); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* set the FILTUNE voltage */ 6568c2ecf20Sopenharmony_ci val = cx24123_readreg(state, 0x28) & ~0x3; 6578c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x27, state->FILTune >> 2); 6588c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x28, val | (state->FILTune & 0x3)); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci dprintk("pll tune VCA=%d, band=%d, pll=%d\n", state->VCAarg, 6618c2ecf20Sopenharmony_ci state->bandselectarg, state->pllarg); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci return 0; 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci/* 6688c2ecf20Sopenharmony_ci * 0x23: 6698c2ecf20Sopenharmony_ci * [7:7] = BTI enabled 6708c2ecf20Sopenharmony_ci * [6:6] = I2C repeater enabled 6718c2ecf20Sopenharmony_ci * [5:5] = I2C repeater start 6728c2ecf20Sopenharmony_ci * [0:0] = BTI start 6738c2ecf20Sopenharmony_ci */ 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci/* mode == 1 -> i2c-repeater, 0 -> bti */ 6768c2ecf20Sopenharmony_cistatic int cx24123_repeater_mode(struct cx24123_state *state, u8 mode, u8 start) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci u8 r = cx24123_readreg(state, 0x23) & 0x1e; 6798c2ecf20Sopenharmony_ci if (mode) 6808c2ecf20Sopenharmony_ci r |= (1 << 6) | (start << 5); 6818c2ecf20Sopenharmony_ci else 6828c2ecf20Sopenharmony_ci r |= (1 << 7) | (start); 6838c2ecf20Sopenharmony_ci return cx24123_writereg(state, 0x23, r); 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic int cx24123_initfe(struct dvb_frontend *fe) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci struct cx24123_state *state = fe->demodulator_priv; 6898c2ecf20Sopenharmony_ci int i; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci dprintk("init frontend\n"); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci /* Configure the demod to a good set of defaults */ 6948c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cx24123_regdata); i++) 6958c2ecf20Sopenharmony_ci cx24123_writereg(state, cx24123_regdata[i].reg, 6968c2ecf20Sopenharmony_ci cx24123_regdata[i].data); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci /* Set the LNB polarity */ 6998c2ecf20Sopenharmony_ci if (state->config->lnb_polarity) 7008c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x32, 7018c2ecf20Sopenharmony_ci cx24123_readreg(state, 0x32) | 0x02); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (state->config->dont_use_pll) 7048c2ecf20Sopenharmony_ci cx24123_repeater_mode(state, 1, 0); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci return 0; 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cistatic int cx24123_set_voltage(struct dvb_frontend *fe, 7108c2ecf20Sopenharmony_ci enum fe_sec_voltage voltage) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci struct cx24123_state *state = fe->demodulator_priv; 7138c2ecf20Sopenharmony_ci u8 val; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci val = cx24123_readreg(state, 0x29) & ~0x40; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci switch (voltage) { 7188c2ecf20Sopenharmony_ci case SEC_VOLTAGE_13: 7198c2ecf20Sopenharmony_ci dprintk("setting voltage 13V\n"); 7208c2ecf20Sopenharmony_ci return cx24123_writereg(state, 0x29, val & 0x7f); 7218c2ecf20Sopenharmony_ci case SEC_VOLTAGE_18: 7228c2ecf20Sopenharmony_ci dprintk("setting voltage 18V\n"); 7238c2ecf20Sopenharmony_ci return cx24123_writereg(state, 0x29, val | 0x80); 7248c2ecf20Sopenharmony_ci case SEC_VOLTAGE_OFF: 7258c2ecf20Sopenharmony_ci /* already handled in cx88-dvb */ 7268c2ecf20Sopenharmony_ci return 0; 7278c2ecf20Sopenharmony_ci default: 7288c2ecf20Sopenharmony_ci return -EINVAL; 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci return 0; 7328c2ecf20Sopenharmony_ci} 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci/* wait for diseqc queue to become ready (or timeout) */ 7358c2ecf20Sopenharmony_cistatic void cx24123_wait_for_diseqc(struct cx24123_state *state) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(200); 7388c2ecf20Sopenharmony_ci while (!(cx24123_readreg(state, 0x29) & 0x40)) { 7398c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 7408c2ecf20Sopenharmony_ci err("%s: diseqc queue not ready, " \ 7418c2ecf20Sopenharmony_ci "command may be lost.\n", __func__); 7428c2ecf20Sopenharmony_ci break; 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci msleep(10); 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci} 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_cistatic int cx24123_send_diseqc_msg(struct dvb_frontend *fe, 7498c2ecf20Sopenharmony_ci struct dvb_diseqc_master_cmd *cmd) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci struct cx24123_state *state = fe->demodulator_priv; 7528c2ecf20Sopenharmony_ci int i, val, tone; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci dprintk("\n"); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* stop continuous tone if enabled */ 7578c2ecf20Sopenharmony_ci tone = cx24123_readreg(state, 0x29); 7588c2ecf20Sopenharmony_ci if (tone & 0x10) 7598c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x29, tone & ~0x50); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* wait for diseqc queue ready */ 7628c2ecf20Sopenharmony_ci cx24123_wait_for_diseqc(state); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* select tone mode */ 7658c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) & 0xfb); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci for (i = 0; i < cmd->msg_len; i++) 7688c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x2C + i, cmd->msg[i]); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci val = cx24123_readreg(state, 0x29); 7718c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40) | 7728c2ecf20Sopenharmony_ci ((cmd->msg_len-3) & 3)); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci /* wait for diseqc message to finish sending */ 7758c2ecf20Sopenharmony_ci cx24123_wait_for_diseqc(state); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci /* restart continuous tone if enabled */ 7788c2ecf20Sopenharmony_ci if (tone & 0x10) 7798c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x29, tone & ~0x40); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci return 0; 7828c2ecf20Sopenharmony_ci} 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_cistatic int cx24123_diseqc_send_burst(struct dvb_frontend *fe, 7858c2ecf20Sopenharmony_ci enum fe_sec_mini_cmd burst) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci struct cx24123_state *state = fe->demodulator_priv; 7888c2ecf20Sopenharmony_ci int val, tone; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci dprintk("\n"); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci /* stop continuous tone if enabled */ 7938c2ecf20Sopenharmony_ci tone = cx24123_readreg(state, 0x29); 7948c2ecf20Sopenharmony_ci if (tone & 0x10) 7958c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x29, tone & ~0x50); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci /* wait for diseqc queue ready */ 7988c2ecf20Sopenharmony_ci cx24123_wait_for_diseqc(state); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci /* select tone mode */ 8018c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) | 0x4); 8028c2ecf20Sopenharmony_ci msleep(30); 8038c2ecf20Sopenharmony_ci val = cx24123_readreg(state, 0x29); 8048c2ecf20Sopenharmony_ci if (burst == SEC_MINI_A) 8058c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40 | 0x00)); 8068c2ecf20Sopenharmony_ci else if (burst == SEC_MINI_B) 8078c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40 | 0x08)); 8088c2ecf20Sopenharmony_ci else 8098c2ecf20Sopenharmony_ci return -EINVAL; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci cx24123_wait_for_diseqc(state); 8128c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) & 0xfb); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* restart continuous tone if enabled */ 8158c2ecf20Sopenharmony_ci if (tone & 0x10) 8168c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x29, tone & ~0x40); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci return 0; 8198c2ecf20Sopenharmony_ci} 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cistatic int cx24123_read_status(struct dvb_frontend *fe, enum fe_status *status) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci struct cx24123_state *state = fe->demodulator_priv; 8248c2ecf20Sopenharmony_ci int sync = cx24123_readreg(state, 0x14); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci *status = 0; 8278c2ecf20Sopenharmony_ci if (state->config->dont_use_pll) { 8288c2ecf20Sopenharmony_ci u32 tun_status = 0; 8298c2ecf20Sopenharmony_ci if (fe->ops.tuner_ops.get_status) 8308c2ecf20Sopenharmony_ci fe->ops.tuner_ops.get_status(fe, &tun_status); 8318c2ecf20Sopenharmony_ci if (tun_status & TUNER_STATUS_LOCKED) 8328c2ecf20Sopenharmony_ci *status |= FE_HAS_SIGNAL; 8338c2ecf20Sopenharmony_ci } else { 8348c2ecf20Sopenharmony_ci int lock = cx24123_readreg(state, 0x20); 8358c2ecf20Sopenharmony_ci if (lock & 0x01) 8368c2ecf20Sopenharmony_ci *status |= FE_HAS_SIGNAL; 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (sync & 0x02) 8408c2ecf20Sopenharmony_ci *status |= FE_HAS_CARRIER; /* Phase locked */ 8418c2ecf20Sopenharmony_ci if (sync & 0x04) 8428c2ecf20Sopenharmony_ci *status |= FE_HAS_VITERBI; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci /* Reed-Solomon Status */ 8458c2ecf20Sopenharmony_ci if (sync & 0x08) 8468c2ecf20Sopenharmony_ci *status |= FE_HAS_SYNC; 8478c2ecf20Sopenharmony_ci if (sync & 0x80) 8488c2ecf20Sopenharmony_ci *status |= FE_HAS_LOCK; /*Full Sync */ 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci return 0; 8518c2ecf20Sopenharmony_ci} 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci/* 8548c2ecf20Sopenharmony_ci * Configured to return the measurement of errors in blocks, 8558c2ecf20Sopenharmony_ci * because no UCBLOCKS value is available, so this value doubles up 8568c2ecf20Sopenharmony_ci * to satisfy both measurements. 8578c2ecf20Sopenharmony_ci */ 8588c2ecf20Sopenharmony_cistatic int cx24123_read_ber(struct dvb_frontend *fe, u32 *ber) 8598c2ecf20Sopenharmony_ci{ 8608c2ecf20Sopenharmony_ci struct cx24123_state *state = fe->demodulator_priv; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci /* The true bit error rate is this value divided by 8638c2ecf20Sopenharmony_ci the window size (set as 256 * 255) */ 8648c2ecf20Sopenharmony_ci *ber = ((cx24123_readreg(state, 0x1c) & 0x3f) << 16) | 8658c2ecf20Sopenharmony_ci (cx24123_readreg(state, 0x1d) << 8 | 8668c2ecf20Sopenharmony_ci cx24123_readreg(state, 0x1e)); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci dprintk("BER = %d\n", *ber); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci return 0; 8718c2ecf20Sopenharmony_ci} 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_cistatic int cx24123_read_signal_strength(struct dvb_frontend *fe, 8748c2ecf20Sopenharmony_ci u16 *signal_strength) 8758c2ecf20Sopenharmony_ci{ 8768c2ecf20Sopenharmony_ci struct cx24123_state *state = fe->demodulator_priv; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci /* larger = better */ 8798c2ecf20Sopenharmony_ci *signal_strength = cx24123_readreg(state, 0x3b) << 8; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci dprintk("Signal strength = %d\n", *signal_strength); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci return 0; 8848c2ecf20Sopenharmony_ci} 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_cistatic int cx24123_read_snr(struct dvb_frontend *fe, u16 *snr) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci struct cx24123_state *state = fe->demodulator_priv; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci /* Inverted raw Es/N0 count, totally bogus but better than the 8918c2ecf20Sopenharmony_ci BER threshold. */ 8928c2ecf20Sopenharmony_ci *snr = 65535 - (((u16)cx24123_readreg(state, 0x18) << 8) | 8938c2ecf20Sopenharmony_ci (u16)cx24123_readreg(state, 0x19)); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci dprintk("read S/N index = %d\n", *snr); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci return 0; 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cistatic int cx24123_set_frontend(struct dvb_frontend *fe) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci struct cx24123_state *state = fe->demodulator_priv; 9038c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p = &fe->dtv_property_cache; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci dprintk("\n"); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci if (state->config->set_ts_params) 9088c2ecf20Sopenharmony_ci state->config->set_ts_params(fe, 0); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci state->currentfreq = p->frequency; 9118c2ecf20Sopenharmony_ci state->currentsymbolrate = p->symbol_rate; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci cx24123_set_inversion(state, p->inversion); 9148c2ecf20Sopenharmony_ci cx24123_set_fec(state, p->fec_inner); 9158c2ecf20Sopenharmony_ci cx24123_set_symbolrate(state, p->symbol_rate); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci if (!state->config->dont_use_pll) 9188c2ecf20Sopenharmony_ci cx24123_pll_tune(fe); 9198c2ecf20Sopenharmony_ci else if (fe->ops.tuner_ops.set_params) 9208c2ecf20Sopenharmony_ci fe->ops.tuner_ops.set_params(fe); 9218c2ecf20Sopenharmony_ci else 9228c2ecf20Sopenharmony_ci err("it seems I don't have a tuner..."); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci /* Enable automatic acquisition and reset cycle */ 9258c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x03, (cx24123_readreg(state, 0x03) | 0x07)); 9268c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x00, 0x10); 9278c2ecf20Sopenharmony_ci cx24123_writereg(state, 0x00, 0); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (state->config->agc_callback) 9308c2ecf20Sopenharmony_ci state->config->agc_callback(fe); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci return 0; 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cistatic int cx24123_get_frontend(struct dvb_frontend *fe, 9368c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p) 9378c2ecf20Sopenharmony_ci{ 9388c2ecf20Sopenharmony_ci struct cx24123_state *state = fe->demodulator_priv; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci dprintk("\n"); 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci if (cx24123_get_inversion(state, &p->inversion) != 0) { 9438c2ecf20Sopenharmony_ci err("%s: Failed to get inversion status\n", __func__); 9448c2ecf20Sopenharmony_ci return -EREMOTEIO; 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci if (cx24123_get_fec(state, &p->fec_inner) != 0) { 9478c2ecf20Sopenharmony_ci err("%s: Failed to get fec status\n", __func__); 9488c2ecf20Sopenharmony_ci return -EREMOTEIO; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci p->frequency = state->currentfreq; 9518c2ecf20Sopenharmony_ci p->symbol_rate = state->currentsymbolrate; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci return 0; 9548c2ecf20Sopenharmony_ci} 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_cistatic int cx24123_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci struct cx24123_state *state = fe->demodulator_priv; 9598c2ecf20Sopenharmony_ci u8 val; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci /* wait for diseqc queue ready */ 9628c2ecf20Sopenharmony_ci cx24123_wait_for_diseqc(state); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci val = cx24123_readreg(state, 0x29) & ~0x40; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci switch (tone) { 9678c2ecf20Sopenharmony_ci case SEC_TONE_ON: 9688c2ecf20Sopenharmony_ci dprintk("setting tone on\n"); 9698c2ecf20Sopenharmony_ci return cx24123_writereg(state, 0x29, val | 0x10); 9708c2ecf20Sopenharmony_ci case SEC_TONE_OFF: 9718c2ecf20Sopenharmony_ci dprintk("setting tone off\n"); 9728c2ecf20Sopenharmony_ci return cx24123_writereg(state, 0x29, val & 0xef); 9738c2ecf20Sopenharmony_ci default: 9748c2ecf20Sopenharmony_ci err("CASE reached default with tone=%d\n", tone); 9758c2ecf20Sopenharmony_ci return -EINVAL; 9768c2ecf20Sopenharmony_ci } 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci return 0; 9798c2ecf20Sopenharmony_ci} 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_cistatic int cx24123_tune(struct dvb_frontend *fe, 9828c2ecf20Sopenharmony_ci bool re_tune, 9838c2ecf20Sopenharmony_ci unsigned int mode_flags, 9848c2ecf20Sopenharmony_ci unsigned int *delay, 9858c2ecf20Sopenharmony_ci enum fe_status *status) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci int retval = 0; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci if (re_tune) 9908c2ecf20Sopenharmony_ci retval = cx24123_set_frontend(fe); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) 9938c2ecf20Sopenharmony_ci cx24123_read_status(fe, status); 9948c2ecf20Sopenharmony_ci *delay = HZ/10; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci return retval; 9978c2ecf20Sopenharmony_ci} 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_cistatic enum dvbfe_algo cx24123_get_algo(struct dvb_frontend *fe) 10008c2ecf20Sopenharmony_ci{ 10018c2ecf20Sopenharmony_ci return DVBFE_ALGO_HW; 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cistatic void cx24123_release(struct dvb_frontend *fe) 10058c2ecf20Sopenharmony_ci{ 10068c2ecf20Sopenharmony_ci struct cx24123_state *state = fe->demodulator_priv; 10078c2ecf20Sopenharmony_ci dprintk("\n"); 10088c2ecf20Sopenharmony_ci i2c_del_adapter(&state->tuner_i2c_adapter); 10098c2ecf20Sopenharmony_ci kfree(state); 10108c2ecf20Sopenharmony_ci} 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_cistatic int cx24123_tuner_i2c_tuner_xfer(struct i2c_adapter *i2c_adap, 10138c2ecf20Sopenharmony_ci struct i2c_msg msg[], int num) 10148c2ecf20Sopenharmony_ci{ 10158c2ecf20Sopenharmony_ci struct cx24123_state *state = i2c_get_adapdata(i2c_adap); 10168c2ecf20Sopenharmony_ci /* this repeater closes after the first stop */ 10178c2ecf20Sopenharmony_ci cx24123_repeater_mode(state, 1, 1); 10188c2ecf20Sopenharmony_ci return i2c_transfer(state->i2c, msg, num); 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_cistatic u32 cx24123_tuner_i2c_func(struct i2c_adapter *adapter) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci return I2C_FUNC_I2C; 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_cistatic const struct i2c_algorithm cx24123_tuner_i2c_algo = { 10278c2ecf20Sopenharmony_ci .master_xfer = cx24123_tuner_i2c_tuner_xfer, 10288c2ecf20Sopenharmony_ci .functionality = cx24123_tuner_i2c_func, 10298c2ecf20Sopenharmony_ci}; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_cistruct i2c_adapter * 10328c2ecf20Sopenharmony_ci cx24123_get_tuner_i2c_adapter(struct dvb_frontend *fe) 10338c2ecf20Sopenharmony_ci{ 10348c2ecf20Sopenharmony_ci struct cx24123_state *state = fe->demodulator_priv; 10358c2ecf20Sopenharmony_ci return &state->tuner_i2c_adapter; 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cx24123_get_tuner_i2c_adapter); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops cx24123_ops; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_cistruct dvb_frontend *cx24123_attach(const struct cx24123_config *config, 10428c2ecf20Sopenharmony_ci struct i2c_adapter *i2c) 10438c2ecf20Sopenharmony_ci{ 10448c2ecf20Sopenharmony_ci /* allocate memory for the internal state */ 10458c2ecf20Sopenharmony_ci struct cx24123_state *state = 10468c2ecf20Sopenharmony_ci kzalloc(sizeof(struct cx24123_state), GFP_KERNEL); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci dprintk("\n"); 10498c2ecf20Sopenharmony_ci if (state == NULL) { 10508c2ecf20Sopenharmony_ci err("Unable to kzalloc\n"); 10518c2ecf20Sopenharmony_ci goto error; 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci /* setup the state */ 10558c2ecf20Sopenharmony_ci state->config = config; 10568c2ecf20Sopenharmony_ci state->i2c = i2c; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci /* check if the demod is there */ 10598c2ecf20Sopenharmony_ci state->demod_rev = cx24123_readreg(state, 0x00); 10608c2ecf20Sopenharmony_ci switch (state->demod_rev) { 10618c2ecf20Sopenharmony_ci case 0xe1: 10628c2ecf20Sopenharmony_ci info("detected CX24123C\n"); 10638c2ecf20Sopenharmony_ci break; 10648c2ecf20Sopenharmony_ci case 0xd1: 10658c2ecf20Sopenharmony_ci info("detected CX24123\n"); 10668c2ecf20Sopenharmony_ci break; 10678c2ecf20Sopenharmony_ci default: 10688c2ecf20Sopenharmony_ci err("wrong demod revision: %x\n", state->demod_rev); 10698c2ecf20Sopenharmony_ci goto error; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci /* create dvb_frontend */ 10738c2ecf20Sopenharmony_ci memcpy(&state->frontend.ops, &cx24123_ops, 10748c2ecf20Sopenharmony_ci sizeof(struct dvb_frontend_ops)); 10758c2ecf20Sopenharmony_ci state->frontend.demodulator_priv = state; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci /* create tuner i2c adapter */ 10788c2ecf20Sopenharmony_ci if (config->dont_use_pll) 10798c2ecf20Sopenharmony_ci cx24123_repeater_mode(state, 1, 0); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci strscpy(state->tuner_i2c_adapter.name, "CX24123 tuner I2C bus", 10828c2ecf20Sopenharmony_ci sizeof(state->tuner_i2c_adapter.name)); 10838c2ecf20Sopenharmony_ci state->tuner_i2c_adapter.algo = &cx24123_tuner_i2c_algo; 10848c2ecf20Sopenharmony_ci state->tuner_i2c_adapter.algo_data = NULL; 10858c2ecf20Sopenharmony_ci state->tuner_i2c_adapter.dev.parent = i2c->dev.parent; 10868c2ecf20Sopenharmony_ci i2c_set_adapdata(&state->tuner_i2c_adapter, state); 10878c2ecf20Sopenharmony_ci if (i2c_add_adapter(&state->tuner_i2c_adapter) < 0) { 10888c2ecf20Sopenharmony_ci err("tuner i2c bus could not be initialized\n"); 10898c2ecf20Sopenharmony_ci goto error; 10908c2ecf20Sopenharmony_ci } 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci return &state->frontend; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_cierror: 10958c2ecf20Sopenharmony_ci kfree(state); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci return NULL; 10988c2ecf20Sopenharmony_ci} 10998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cx24123_attach); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops cx24123_ops = { 11028c2ecf20Sopenharmony_ci .delsys = { SYS_DVBS }, 11038c2ecf20Sopenharmony_ci .info = { 11048c2ecf20Sopenharmony_ci .name = "Conexant CX24123/CX24109", 11058c2ecf20Sopenharmony_ci .frequency_min_hz = 950 * MHz, 11068c2ecf20Sopenharmony_ci .frequency_max_hz = 2150 * MHz, 11078c2ecf20Sopenharmony_ci .frequency_stepsize_hz = 1011 * kHz, 11088c2ecf20Sopenharmony_ci .frequency_tolerance_hz = 5 * MHz, 11098c2ecf20Sopenharmony_ci .symbol_rate_min = 1000000, 11108c2ecf20Sopenharmony_ci .symbol_rate_max = 45000000, 11118c2ecf20Sopenharmony_ci .caps = FE_CAN_INVERSION_AUTO | 11128c2ecf20Sopenharmony_ci FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 11138c2ecf20Sopenharmony_ci FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | 11148c2ecf20Sopenharmony_ci FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | 11158c2ecf20Sopenharmony_ci FE_CAN_QPSK | FE_CAN_RECOVER 11168c2ecf20Sopenharmony_ci }, 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci .release = cx24123_release, 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci .init = cx24123_initfe, 11218c2ecf20Sopenharmony_ci .set_frontend = cx24123_set_frontend, 11228c2ecf20Sopenharmony_ci .get_frontend = cx24123_get_frontend, 11238c2ecf20Sopenharmony_ci .read_status = cx24123_read_status, 11248c2ecf20Sopenharmony_ci .read_ber = cx24123_read_ber, 11258c2ecf20Sopenharmony_ci .read_signal_strength = cx24123_read_signal_strength, 11268c2ecf20Sopenharmony_ci .read_snr = cx24123_read_snr, 11278c2ecf20Sopenharmony_ci .diseqc_send_master_cmd = cx24123_send_diseqc_msg, 11288c2ecf20Sopenharmony_ci .diseqc_send_burst = cx24123_diseqc_send_burst, 11298c2ecf20Sopenharmony_ci .set_tone = cx24123_set_tone, 11308c2ecf20Sopenharmony_ci .set_voltage = cx24123_set_voltage, 11318c2ecf20Sopenharmony_ci .tune = cx24123_tune, 11328c2ecf20Sopenharmony_ci .get_frontend_algo = cx24123_get_algo, 11338c2ecf20Sopenharmony_ci}; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DVB Frontend module for Conexant " \ 11368c2ecf20Sopenharmony_ci "CX24123/CX24109/CX24113 hardware"); 11378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Steven Toth"); 11388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 11398c2ecf20Sopenharmony_ci 1140