18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Driver for ST STV0288 demodulator 48c2ecf20Sopenharmony_ci Copyright (C) 2006 Georg Acher, BayCom GmbH, acher (at) baycom (dot) de 58c2ecf20Sopenharmony_ci for Reel Multimedia 68c2ecf20Sopenharmony_ci Copyright (C) 2008 TurboSight.com, Bob Liu <bob@turbosight.com> 78c2ecf20Sopenharmony_ci Copyright (C) 2008 Igor M. Liplianin <liplianin@me.by> 88c2ecf20Sopenharmony_ci Removed stb6000 specific tuner code and revised some 98c2ecf20Sopenharmony_ci procedures. 108c2ecf20Sopenharmony_ci 2010-09-01 Josef Pavlik <josef@pavlik.it> 118c2ecf20Sopenharmony_ci Fixed diseqc_msg, diseqc_burst and set_tone problems 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci*/ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/string.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 228c2ecf20Sopenharmony_ci#include <asm/div64.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 258c2ecf20Sopenharmony_ci#include "stv0288.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct stv0288_state { 288c2ecf20Sopenharmony_ci struct i2c_adapter *i2c; 298c2ecf20Sopenharmony_ci const struct stv0288_config *config; 308c2ecf20Sopenharmony_ci struct dvb_frontend frontend; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci u8 initialised:1; 338c2ecf20Sopenharmony_ci u32 tuner_frequency; 348c2ecf20Sopenharmony_ci u32 symbol_rate; 358c2ecf20Sopenharmony_ci enum fe_code_rate fec_inner; 368c2ecf20Sopenharmony_ci int errmode; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define STATUS_BER 0 408c2ecf20Sopenharmony_ci#define STATUS_UCBLOCKS 1 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int debug; 438c2ecf20Sopenharmony_cistatic int debug_legacy_dish_switch; 448c2ecf20Sopenharmony_ci#define dprintk(args...) \ 458c2ecf20Sopenharmony_ci do { \ 468c2ecf20Sopenharmony_ci if (debug) \ 478c2ecf20Sopenharmony_ci printk(KERN_DEBUG "stv0288: " args); \ 488c2ecf20Sopenharmony_ci } while (0) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int stv0288_writeregI(struct stv0288_state *state, u8 reg, u8 data) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci int ret; 548c2ecf20Sopenharmony_ci u8 buf[] = { reg, data }; 558c2ecf20Sopenharmony_ci struct i2c_msg msg = { 568c2ecf20Sopenharmony_ci .addr = state->config->demod_address, 578c2ecf20Sopenharmony_ci .flags = 0, 588c2ecf20Sopenharmony_ci .buf = buf, 598c2ecf20Sopenharmony_ci .len = 2 608c2ecf20Sopenharmony_ci }; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci ret = i2c_transfer(state->i2c, &msg, 1); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (ret != 1) 658c2ecf20Sopenharmony_ci dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", 668c2ecf20Sopenharmony_ci __func__, reg, data, ret); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci return (ret != 1) ? -EREMOTEIO : 0; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int stv0288_write(struct dvb_frontend *fe, const u8 buf[], int len) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct stv0288_state *state = fe->demodulator_priv; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (len != 2) 768c2ecf20Sopenharmony_ci return -EINVAL; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return stv0288_writeregI(state, buf[0], buf[1]); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic u8 stv0288_readreg(struct stv0288_state *state, u8 reg) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci int ret; 848c2ecf20Sopenharmony_ci u8 b0[] = { reg }; 858c2ecf20Sopenharmony_ci u8 b1[] = { 0 }; 868c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 878c2ecf20Sopenharmony_ci { 888c2ecf20Sopenharmony_ci .addr = state->config->demod_address, 898c2ecf20Sopenharmony_ci .flags = 0, 908c2ecf20Sopenharmony_ci .buf = b0, 918c2ecf20Sopenharmony_ci .len = 1 928c2ecf20Sopenharmony_ci }, { 938c2ecf20Sopenharmony_ci .addr = state->config->demod_address, 948c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 958c2ecf20Sopenharmony_ci .buf = b1, 968c2ecf20Sopenharmony_ci .len = 1 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci }; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci ret = i2c_transfer(state->i2c, msg, 2); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (ret != 2) 1038c2ecf20Sopenharmony_ci dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", 1048c2ecf20Sopenharmony_ci __func__, reg, ret); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return b1[0]; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int stv0288_set_symbolrate(struct dvb_frontend *fe, u32 srate) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct stv0288_state *state = fe->demodulator_priv; 1128c2ecf20Sopenharmony_ci unsigned int temp; 1138c2ecf20Sopenharmony_ci unsigned char b[3]; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if ((srate < 1000000) || (srate > 45000000)) 1168c2ecf20Sopenharmony_ci return -EINVAL; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x22, 0); 1198c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x23, 0); 1208c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x2b, 0xff); 1218c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x2c, 0xf7); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci temp = (unsigned int)srate / 1000; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci temp = temp * 32768; 1268c2ecf20Sopenharmony_ci temp = temp / 25; 1278c2ecf20Sopenharmony_ci temp = temp / 125; 1288c2ecf20Sopenharmony_ci b[0] = (unsigned char)((temp >> 12) & 0xff); 1298c2ecf20Sopenharmony_ci b[1] = (unsigned char)((temp >> 4) & 0xff); 1308c2ecf20Sopenharmony_ci b[2] = (unsigned char)((temp << 4) & 0xf0); 1318c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x28, 0x80); /* SFRH */ 1328c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x29, 0); /* SFRM */ 1338c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x2a, 0); /* SFRL */ 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x28, b[0]); 1368c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x29, b[1]); 1378c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x2a, b[2]); 1388c2ecf20Sopenharmony_ci dprintk("stv0288: stv0288_set_symbolrate\n"); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int stv0288_send_diseqc_msg(struct dvb_frontend *fe, 1448c2ecf20Sopenharmony_ci struct dvb_diseqc_master_cmd *m) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct stv0288_state *state = fe->demodulator_priv; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci int i; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x09, 0); 1538c2ecf20Sopenharmony_ci msleep(30); 1548c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x05, 0x12);/* modulated mode, single shot */ 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci for (i = 0; i < m->msg_len; i++) { 1578c2ecf20Sopenharmony_ci if (stv0288_writeregI(state, 0x06, m->msg[i])) 1588c2ecf20Sopenharmony_ci return -EREMOTEIO; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci msleep(m->msg_len*12); 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int stv0288_send_diseqc_burst(struct dvb_frontend *fe, 1658c2ecf20Sopenharmony_ci enum fe_sec_mini_cmd burst) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct stv0288_state *state = fe->demodulator_priv; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (stv0288_writeregI(state, 0x05, 0x03))/* burst mode, single shot */ 1728c2ecf20Sopenharmony_ci return -EREMOTEIO; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (stv0288_writeregI(state, 0x06, burst == SEC_MINI_A ? 0x00 : 0xff)) 1758c2ecf20Sopenharmony_ci return -EREMOTEIO; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci msleep(15); 1788c2ecf20Sopenharmony_ci if (stv0288_writeregI(state, 0x05, 0x12)) 1798c2ecf20Sopenharmony_ci return -EREMOTEIO; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int stv0288_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct stv0288_state *state = fe->demodulator_priv; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci switch (tone) { 1898c2ecf20Sopenharmony_ci case SEC_TONE_ON: 1908c2ecf20Sopenharmony_ci if (stv0288_writeregI(state, 0x05, 0x10))/* cont carrier */ 1918c2ecf20Sopenharmony_ci return -EREMOTEIO; 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci case SEC_TONE_OFF: 1958c2ecf20Sopenharmony_ci if (stv0288_writeregI(state, 0x05, 0x12))/* burst mode off*/ 1968c2ecf20Sopenharmony_ci return -EREMOTEIO; 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci default: 2008c2ecf20Sopenharmony_ci return -EINVAL; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci return 0; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic u8 stv0288_inittab[] = { 2068c2ecf20Sopenharmony_ci 0x01, 0x15, 2078c2ecf20Sopenharmony_ci 0x02, 0x20, 2088c2ecf20Sopenharmony_ci 0x09, 0x0, 2098c2ecf20Sopenharmony_ci 0x0a, 0x4, 2108c2ecf20Sopenharmony_ci 0x0b, 0x0, 2118c2ecf20Sopenharmony_ci 0x0c, 0x0, 2128c2ecf20Sopenharmony_ci 0x0d, 0x0, 2138c2ecf20Sopenharmony_ci 0x0e, 0xd4, 2148c2ecf20Sopenharmony_ci 0x0f, 0x30, 2158c2ecf20Sopenharmony_ci 0x11, 0x80, 2168c2ecf20Sopenharmony_ci 0x12, 0x03, 2178c2ecf20Sopenharmony_ci 0x13, 0x48, 2188c2ecf20Sopenharmony_ci 0x14, 0x84, 2198c2ecf20Sopenharmony_ci 0x15, 0x45, 2208c2ecf20Sopenharmony_ci 0x16, 0xb7, 2218c2ecf20Sopenharmony_ci 0x17, 0x9c, 2228c2ecf20Sopenharmony_ci 0x18, 0x0, 2238c2ecf20Sopenharmony_ci 0x19, 0xa6, 2248c2ecf20Sopenharmony_ci 0x1a, 0x88, 2258c2ecf20Sopenharmony_ci 0x1b, 0x8f, 2268c2ecf20Sopenharmony_ci 0x1c, 0xf0, 2278c2ecf20Sopenharmony_ci 0x20, 0x0b, 2288c2ecf20Sopenharmony_ci 0x21, 0x54, 2298c2ecf20Sopenharmony_ci 0x22, 0x0, 2308c2ecf20Sopenharmony_ci 0x23, 0x0, 2318c2ecf20Sopenharmony_ci 0x2b, 0xff, 2328c2ecf20Sopenharmony_ci 0x2c, 0xf7, 2338c2ecf20Sopenharmony_ci 0x30, 0x0, 2348c2ecf20Sopenharmony_ci 0x31, 0x1e, 2358c2ecf20Sopenharmony_ci 0x32, 0x14, 2368c2ecf20Sopenharmony_ci 0x33, 0x0f, 2378c2ecf20Sopenharmony_ci 0x34, 0x09, 2388c2ecf20Sopenharmony_ci 0x35, 0x0c, 2398c2ecf20Sopenharmony_ci 0x36, 0x05, 2408c2ecf20Sopenharmony_ci 0x37, 0x2f, 2418c2ecf20Sopenharmony_ci 0x38, 0x16, 2428c2ecf20Sopenharmony_ci 0x39, 0xbe, 2438c2ecf20Sopenharmony_ci 0x3a, 0x0, 2448c2ecf20Sopenharmony_ci 0x3b, 0x13, 2458c2ecf20Sopenharmony_ci 0x3c, 0x11, 2468c2ecf20Sopenharmony_ci 0x3d, 0x30, 2478c2ecf20Sopenharmony_ci 0x40, 0x63, 2488c2ecf20Sopenharmony_ci 0x41, 0x04, 2498c2ecf20Sopenharmony_ci 0x42, 0x20, 2508c2ecf20Sopenharmony_ci 0x43, 0x00, 2518c2ecf20Sopenharmony_ci 0x44, 0x00, 2528c2ecf20Sopenharmony_ci 0x45, 0x00, 2538c2ecf20Sopenharmony_ci 0x46, 0x00, 2548c2ecf20Sopenharmony_ci 0x47, 0x00, 2558c2ecf20Sopenharmony_ci 0x4a, 0x00, 2568c2ecf20Sopenharmony_ci 0x50, 0x10, 2578c2ecf20Sopenharmony_ci 0x51, 0x38, 2588c2ecf20Sopenharmony_ci 0x52, 0x21, 2598c2ecf20Sopenharmony_ci 0x58, 0x54, 2608c2ecf20Sopenharmony_ci 0x59, 0x86, 2618c2ecf20Sopenharmony_ci 0x5a, 0x0, 2628c2ecf20Sopenharmony_ci 0x5b, 0x9b, 2638c2ecf20Sopenharmony_ci 0x5c, 0x08, 2648c2ecf20Sopenharmony_ci 0x5d, 0x7f, 2658c2ecf20Sopenharmony_ci 0x5e, 0x0, 2668c2ecf20Sopenharmony_ci 0x5f, 0xff, 2678c2ecf20Sopenharmony_ci 0x70, 0x0, 2688c2ecf20Sopenharmony_ci 0x71, 0x0, 2698c2ecf20Sopenharmony_ci 0x72, 0x0, 2708c2ecf20Sopenharmony_ci 0x74, 0x0, 2718c2ecf20Sopenharmony_ci 0x75, 0x0, 2728c2ecf20Sopenharmony_ci 0x76, 0x0, 2738c2ecf20Sopenharmony_ci 0x81, 0x0, 2748c2ecf20Sopenharmony_ci 0x82, 0x3f, 2758c2ecf20Sopenharmony_ci 0x83, 0x3f, 2768c2ecf20Sopenharmony_ci 0x84, 0x0, 2778c2ecf20Sopenharmony_ci 0x85, 0x0, 2788c2ecf20Sopenharmony_ci 0x88, 0x0, 2798c2ecf20Sopenharmony_ci 0x89, 0x0, 2808c2ecf20Sopenharmony_ci 0x8a, 0x0, 2818c2ecf20Sopenharmony_ci 0x8b, 0x0, 2828c2ecf20Sopenharmony_ci 0x8c, 0x0, 2838c2ecf20Sopenharmony_ci 0x90, 0x0, 2848c2ecf20Sopenharmony_ci 0x91, 0x0, 2858c2ecf20Sopenharmony_ci 0x92, 0x0, 2868c2ecf20Sopenharmony_ci 0x93, 0x0, 2878c2ecf20Sopenharmony_ci 0x94, 0x1c, 2888c2ecf20Sopenharmony_ci 0x97, 0x0, 2898c2ecf20Sopenharmony_ci 0xa0, 0x48, 2908c2ecf20Sopenharmony_ci 0xa1, 0x0, 2918c2ecf20Sopenharmony_ci 0xb0, 0xb8, 2928c2ecf20Sopenharmony_ci 0xb1, 0x3a, 2938c2ecf20Sopenharmony_ci 0xb2, 0x10, 2948c2ecf20Sopenharmony_ci 0xb3, 0x82, 2958c2ecf20Sopenharmony_ci 0xb4, 0x80, 2968c2ecf20Sopenharmony_ci 0xb5, 0x82, 2978c2ecf20Sopenharmony_ci 0xb6, 0x82, 2988c2ecf20Sopenharmony_ci 0xb7, 0x82, 2998c2ecf20Sopenharmony_ci 0xb8, 0x20, 3008c2ecf20Sopenharmony_ci 0xb9, 0x0, 3018c2ecf20Sopenharmony_ci 0xf0, 0x0, 3028c2ecf20Sopenharmony_ci 0xf1, 0x0, 3038c2ecf20Sopenharmony_ci 0xf2, 0xc0, 3048c2ecf20Sopenharmony_ci 0x51, 0x36, 3058c2ecf20Sopenharmony_ci 0x52, 0x09, 3068c2ecf20Sopenharmony_ci 0x53, 0x94, 3078c2ecf20Sopenharmony_ci 0x54, 0x62, 3088c2ecf20Sopenharmony_ci 0x55, 0x29, 3098c2ecf20Sopenharmony_ci 0x56, 0x64, 3108c2ecf20Sopenharmony_ci 0x57, 0x2b, 3118c2ecf20Sopenharmony_ci 0xff, 0xff, 3128c2ecf20Sopenharmony_ci}; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int stv0288_set_voltage(struct dvb_frontend *fe, 3158c2ecf20Sopenharmony_ci enum fe_sec_voltage volt) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci dprintk("%s: %s\n", __func__, 3188c2ecf20Sopenharmony_ci volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : 3198c2ecf20Sopenharmony_ci volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic int stv0288_init(struct dvb_frontend *fe) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct stv0288_state *state = fe->demodulator_priv; 3278c2ecf20Sopenharmony_ci int i; 3288c2ecf20Sopenharmony_ci u8 reg; 3298c2ecf20Sopenharmony_ci u8 val; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci dprintk("stv0288: init chip\n"); 3328c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x41, 0x04); 3338c2ecf20Sopenharmony_ci msleep(50); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* we have default inittab */ 3368c2ecf20Sopenharmony_ci if (state->config->inittab == NULL) { 3378c2ecf20Sopenharmony_ci for (i = 0; !(stv0288_inittab[i] == 0xff && 3388c2ecf20Sopenharmony_ci stv0288_inittab[i + 1] == 0xff); i += 2) 3398c2ecf20Sopenharmony_ci stv0288_writeregI(state, stv0288_inittab[i], 3408c2ecf20Sopenharmony_ci stv0288_inittab[i + 1]); 3418c2ecf20Sopenharmony_ci } else { 3428c2ecf20Sopenharmony_ci for (i = 0; ; i += 2) { 3438c2ecf20Sopenharmony_ci reg = state->config->inittab[i]; 3448c2ecf20Sopenharmony_ci val = state->config->inittab[i+1]; 3458c2ecf20Sopenharmony_ci if (reg == 0xff && val == 0xff) 3468c2ecf20Sopenharmony_ci break; 3478c2ecf20Sopenharmony_ci stv0288_writeregI(state, reg, val); 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci return 0; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic int stv0288_read_status(struct dvb_frontend *fe, enum fe_status *status) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct stv0288_state *state = fe->demodulator_priv; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci u8 sync = stv0288_readreg(state, 0x24); 3588c2ecf20Sopenharmony_ci if (sync == 255) 3598c2ecf20Sopenharmony_ci sync = 0; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, sync); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci *status = 0; 3648c2ecf20Sopenharmony_ci if (sync & 0x80) 3658c2ecf20Sopenharmony_ci *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; 3668c2ecf20Sopenharmony_ci if (sync & 0x10) 3678c2ecf20Sopenharmony_ci *status |= FE_HAS_VITERBI; 3688c2ecf20Sopenharmony_ci if (sync & 0x08) { 3698c2ecf20Sopenharmony_ci *status |= FE_HAS_LOCK; 3708c2ecf20Sopenharmony_ci dprintk("stv0288 has locked\n"); 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci return 0; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic int stv0288_read_ber(struct dvb_frontend *fe, u32 *ber) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct stv0288_state *state = fe->demodulator_priv; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (state->errmode != STATUS_BER) 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci *ber = (stv0288_readreg(state, 0x26) << 8) | 3838c2ecf20Sopenharmony_ci stv0288_readreg(state, 0x27); 3848c2ecf20Sopenharmony_ci dprintk("stv0288_read_ber %d\n", *ber); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return 0; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic int stv0288_read_signal_strength(struct dvb_frontend *fe, u16 *strength) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci struct stv0288_state *state = fe->demodulator_priv; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci s32 signal = 0xffff - ((stv0288_readreg(state, 0x10) << 8)); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci signal = signal * 5 / 4; 3988c2ecf20Sopenharmony_ci *strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal; 3998c2ecf20Sopenharmony_ci dprintk("stv0288_read_signal_strength %d\n", *strength); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_cistatic int stv0288_sleep(struct dvb_frontend *fe) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct stv0288_state *state = fe->demodulator_priv; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x41, 0x84); 4088c2ecf20Sopenharmony_ci state->initialised = 0; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci return 0; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_cistatic int stv0288_read_snr(struct dvb_frontend *fe, u16 *snr) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct stv0288_state *state = fe->demodulator_priv; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci s32 xsnr = 0xffff - ((stv0288_readreg(state, 0x2d) << 8) 4178c2ecf20Sopenharmony_ci | stv0288_readreg(state, 0x2e)); 4188c2ecf20Sopenharmony_ci xsnr = 3 * (xsnr - 0xa100); 4198c2ecf20Sopenharmony_ci *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr; 4208c2ecf20Sopenharmony_ci dprintk("stv0288_read_snr %d\n", *snr); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic int stv0288_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct stv0288_state *state = fe->demodulator_priv; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (state->errmode != STATUS_BER) 4308c2ecf20Sopenharmony_ci return 0; 4318c2ecf20Sopenharmony_ci *ucblocks = (stv0288_readreg(state, 0x26) << 8) | 4328c2ecf20Sopenharmony_ci stv0288_readreg(state, 0x27); 4338c2ecf20Sopenharmony_ci dprintk("stv0288_read_ber %d\n", *ucblocks); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return 0; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic int stv0288_set_frontend(struct dvb_frontend *fe) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci struct stv0288_state *state = fe->demodulator_priv; 4418c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci u8 tda[3], reg, time_out = 0; 4448c2ecf20Sopenharmony_ci s8 tm; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci dprintk("%s : FE_SET_FRONTEND\n", __func__); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (c->delivery_system != SYS_DVBS) { 4498c2ecf20Sopenharmony_ci dprintk("%s: unsupported delivery system selected (%d)\n", 4508c2ecf20Sopenharmony_ci __func__, c->delivery_system); 4518c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (state->config->set_ts_params) 4558c2ecf20Sopenharmony_ci state->config->set_ts_params(fe, 0); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* only frequency & symbol_rate are used for tuner*/ 4588c2ecf20Sopenharmony_ci if (fe->ops.tuner_ops.set_params) { 4598c2ecf20Sopenharmony_ci fe->ops.tuner_ops.set_params(fe); 4608c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 4618c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci udelay(10); 4658c2ecf20Sopenharmony_ci stv0288_set_symbolrate(fe, c->symbol_rate); 4668c2ecf20Sopenharmony_ci /* Carrier lock control register */ 4678c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x15, 0xc5); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci tda[2] = 0x0; /* CFRL */ 4708c2ecf20Sopenharmony_ci for (tm = -9; tm < 7;) { 4718c2ecf20Sopenharmony_ci /* Viterbi status */ 4728c2ecf20Sopenharmony_ci reg = stv0288_readreg(state, 0x24); 4738c2ecf20Sopenharmony_ci if (reg & 0x8) 4748c2ecf20Sopenharmony_ci break; 4758c2ecf20Sopenharmony_ci if (reg & 0x80) { 4768c2ecf20Sopenharmony_ci time_out++; 4778c2ecf20Sopenharmony_ci if (time_out > 10) 4788c2ecf20Sopenharmony_ci break; 4798c2ecf20Sopenharmony_ci tda[2] += 40; 4808c2ecf20Sopenharmony_ci if (tda[2] < 40) 4818c2ecf20Sopenharmony_ci tm++; 4828c2ecf20Sopenharmony_ci } else { 4838c2ecf20Sopenharmony_ci tm++; 4848c2ecf20Sopenharmony_ci tda[2] = 0; 4858c2ecf20Sopenharmony_ci time_out = 0; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci tda[1] = (unsigned char)tm; 4888c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x2b, tda[1]); 4898c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x2c, tda[2]); 4908c2ecf20Sopenharmony_ci msleep(30); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci state->tuner_frequency = c->frequency; 4938c2ecf20Sopenharmony_ci state->fec_inner = FEC_AUTO; 4948c2ecf20Sopenharmony_ci state->symbol_rate = c->symbol_rate; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci return 0; 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic int stv0288_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci struct stv0288_state *state = fe->demodulator_priv; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if (enable) 5048c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x01, 0xb5); 5058c2ecf20Sopenharmony_ci else 5068c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x01, 0x35); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci udelay(1); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic void stv0288_release(struct dvb_frontend *fe) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct stv0288_state *state = fe->demodulator_priv; 5168c2ecf20Sopenharmony_ci kfree(state); 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops stv0288_ops = { 5208c2ecf20Sopenharmony_ci .delsys = { SYS_DVBS }, 5218c2ecf20Sopenharmony_ci .info = { 5228c2ecf20Sopenharmony_ci .name = "ST STV0288 DVB-S", 5238c2ecf20Sopenharmony_ci .frequency_min_hz = 950 * MHz, 5248c2ecf20Sopenharmony_ci .frequency_max_hz = 2150 * MHz, 5258c2ecf20Sopenharmony_ci .frequency_stepsize_hz = 1 * MHz, 5268c2ecf20Sopenharmony_ci .symbol_rate_min = 1000000, 5278c2ecf20Sopenharmony_ci .symbol_rate_max = 45000000, 5288c2ecf20Sopenharmony_ci .symbol_rate_tolerance = 500, /* ppm */ 5298c2ecf20Sopenharmony_ci .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 5308c2ecf20Sopenharmony_ci FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | 5318c2ecf20Sopenharmony_ci FE_CAN_QPSK | 5328c2ecf20Sopenharmony_ci FE_CAN_FEC_AUTO 5338c2ecf20Sopenharmony_ci }, 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci .release = stv0288_release, 5368c2ecf20Sopenharmony_ci .init = stv0288_init, 5378c2ecf20Sopenharmony_ci .sleep = stv0288_sleep, 5388c2ecf20Sopenharmony_ci .write = stv0288_write, 5398c2ecf20Sopenharmony_ci .i2c_gate_ctrl = stv0288_i2c_gate_ctrl, 5408c2ecf20Sopenharmony_ci .read_status = stv0288_read_status, 5418c2ecf20Sopenharmony_ci .read_ber = stv0288_read_ber, 5428c2ecf20Sopenharmony_ci .read_signal_strength = stv0288_read_signal_strength, 5438c2ecf20Sopenharmony_ci .read_snr = stv0288_read_snr, 5448c2ecf20Sopenharmony_ci .read_ucblocks = stv0288_read_ucblocks, 5458c2ecf20Sopenharmony_ci .diseqc_send_master_cmd = stv0288_send_diseqc_msg, 5468c2ecf20Sopenharmony_ci .diseqc_send_burst = stv0288_send_diseqc_burst, 5478c2ecf20Sopenharmony_ci .set_tone = stv0288_set_tone, 5488c2ecf20Sopenharmony_ci .set_voltage = stv0288_set_voltage, 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci .set_frontend = stv0288_set_frontend, 5518c2ecf20Sopenharmony_ci}; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistruct dvb_frontend *stv0288_attach(const struct stv0288_config *config, 5548c2ecf20Sopenharmony_ci struct i2c_adapter *i2c) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct stv0288_state *state = NULL; 5578c2ecf20Sopenharmony_ci int id; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* allocate memory for the internal state */ 5608c2ecf20Sopenharmony_ci state = kzalloc(sizeof(struct stv0288_state), GFP_KERNEL); 5618c2ecf20Sopenharmony_ci if (state == NULL) 5628c2ecf20Sopenharmony_ci goto error; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci /* setup the state */ 5658c2ecf20Sopenharmony_ci state->config = config; 5668c2ecf20Sopenharmony_ci state->i2c = i2c; 5678c2ecf20Sopenharmony_ci state->initialised = 0; 5688c2ecf20Sopenharmony_ci state->tuner_frequency = 0; 5698c2ecf20Sopenharmony_ci state->symbol_rate = 0; 5708c2ecf20Sopenharmony_ci state->fec_inner = 0; 5718c2ecf20Sopenharmony_ci state->errmode = STATUS_BER; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci stv0288_writeregI(state, 0x41, 0x04); 5748c2ecf20Sopenharmony_ci msleep(200); 5758c2ecf20Sopenharmony_ci id = stv0288_readreg(state, 0x00); 5768c2ecf20Sopenharmony_ci dprintk("stv0288 id %x\n", id); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci /* register 0x00 contains 0x11 for STV0288 */ 5798c2ecf20Sopenharmony_ci if (id != 0x11) 5808c2ecf20Sopenharmony_ci goto error; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* create dvb_frontend */ 5838c2ecf20Sopenharmony_ci memcpy(&state->frontend.ops, &stv0288_ops, 5848c2ecf20Sopenharmony_ci sizeof(struct dvb_frontend_ops)); 5858c2ecf20Sopenharmony_ci state->frontend.demodulator_priv = state; 5868c2ecf20Sopenharmony_ci return &state->frontend; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cierror: 5898c2ecf20Sopenharmony_ci kfree(state); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci return NULL; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(stv0288_attach); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cimodule_param(debug_legacy_dish_switch, int, 0444); 5968c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug_legacy_dish_switch, 5978c2ecf20Sopenharmony_ci "Enable timing analysis for Dish Network legacy switches"); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 6008c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ST STV0288 DVB Demodulator driver"); 6038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Georg Acher, Bob Liu, Igor liplianin"); 6048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6058c2ecf20Sopenharmony_ci 606