18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Driver for STV0297 demodulator 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci Copyright (C) 2004 Andrew de Quincey <adq_dvb@lidskialf.net> 68c2ecf20Sopenharmony_ci Copyright (C) 2003-2004 Dennis Noermann <dennis.noermann@noernet.de> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci*/ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/string.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 198c2ecf20Sopenharmony_ci#include "stv0297.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct stv0297_state { 228c2ecf20Sopenharmony_ci struct i2c_adapter *i2c; 238c2ecf20Sopenharmony_ci const struct stv0297_config *config; 248c2ecf20Sopenharmony_ci struct dvb_frontend frontend; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci unsigned long last_ber; 278c2ecf20Sopenharmony_ci unsigned long base_freq; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#if 1 318c2ecf20Sopenharmony_ci#define dprintk(x...) printk(x) 328c2ecf20Sopenharmony_ci#else 338c2ecf20Sopenharmony_ci#define dprintk(x...) 348c2ecf20Sopenharmony_ci#endif 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define STV0297_CLOCK_KHZ 28900 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int stv0297_writereg(struct stv0297_state *state, u8 reg, u8 data) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci int ret; 428c2ecf20Sopenharmony_ci u8 buf[] = { reg, data }; 438c2ecf20Sopenharmony_ci struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 2 }; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci ret = i2c_transfer(state->i2c, &msg, 1); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (ret != 1) 488c2ecf20Sopenharmony_ci dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", 498c2ecf20Sopenharmony_ci __func__, reg, data, ret); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return (ret != 1) ? -1 : 0; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int stv0297_readreg(struct stv0297_state *state, u8 reg) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci int ret; 578c2ecf20Sopenharmony_ci u8 b0[] = { reg }; 588c2ecf20Sopenharmony_ci u8 b1[] = { 0 }; 598c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 1}, 608c2ecf20Sopenharmony_ci {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1} 618c2ecf20Sopenharmony_ci }; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci // this device needs a STOP between the register and data 648c2ecf20Sopenharmony_ci if (state->config->stop_during_read) { 658c2ecf20Sopenharmony_ci if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) { 668c2ecf20Sopenharmony_ci dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg, ret); 678c2ecf20Sopenharmony_ci return -1; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) { 708c2ecf20Sopenharmony_ci dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg, ret); 718c2ecf20Sopenharmony_ci return -1; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci } else { 748c2ecf20Sopenharmony_ci if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) { 758c2ecf20Sopenharmony_ci dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg, ret); 768c2ecf20Sopenharmony_ci return -1; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return b1[0]; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int stv0297_writereg_mask(struct stv0297_state *state, u8 reg, u8 mask, u8 data) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci int val; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci val = stv0297_readreg(state, reg); 888c2ecf20Sopenharmony_ci val &= ~mask; 898c2ecf20Sopenharmony_ci val |= (data & mask); 908c2ecf20Sopenharmony_ci stv0297_writereg(state, reg, val); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int stv0297_readregs(struct stv0297_state *state, u8 reg1, u8 * b, u8 len) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci int ret; 988c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { {.addr = state->config->demod_address,.flags = 0,.buf = 998c2ecf20Sopenharmony_ci ®1,.len = 1}, 1008c2ecf20Sopenharmony_ci {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b,.len = len} 1018c2ecf20Sopenharmony_ci }; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci // this device needs a STOP between the register and data 1048c2ecf20Sopenharmony_ci if (state->config->stop_during_read) { 1058c2ecf20Sopenharmony_ci if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) { 1068c2ecf20Sopenharmony_ci dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg1, ret); 1078c2ecf20Sopenharmony_ci return -1; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) { 1108c2ecf20Sopenharmony_ci dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg1, ret); 1118c2ecf20Sopenharmony_ci return -1; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci } else { 1148c2ecf20Sopenharmony_ci if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) { 1158c2ecf20Sopenharmony_ci dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg1, ret); 1168c2ecf20Sopenharmony_ci return -1; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic u32 stv0297_get_symbolrate(struct stv0297_state *state) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci u64 tmp; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci tmp = (u64)(stv0297_readreg(state, 0x55) 1288c2ecf20Sopenharmony_ci | (stv0297_readreg(state, 0x56) << 8) 1298c2ecf20Sopenharmony_ci | (stv0297_readreg(state, 0x57) << 16) 1308c2ecf20Sopenharmony_ci | (stv0297_readreg(state, 0x58) << 24)); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci tmp *= STV0297_CLOCK_KHZ; 1338c2ecf20Sopenharmony_ci tmp >>= 32; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return (u32) tmp; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void stv0297_set_symbolrate(struct stv0297_state *state, u32 srate) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci long tmp; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci tmp = 131072L * srate; /* 131072 = 2^17 */ 1438c2ecf20Sopenharmony_ci tmp = tmp / (STV0297_CLOCK_KHZ / 4); /* 1/4 = 2^-2 */ 1448c2ecf20Sopenharmony_ci tmp = tmp * 8192L; /* 8192 = 2^13 */ 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x55, (unsigned char) (tmp & 0xFF)); 1478c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x56, (unsigned char) (tmp >> 8)); 1488c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x57, (unsigned char) (tmp >> 16)); 1498c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x58, (unsigned char) (tmp >> 24)); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic void stv0297_set_sweeprate(struct stv0297_state *state, short fshift, long symrate) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci long tmp; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci tmp = (long) fshift *262144L; /* 262144 = 2*18 */ 1578c2ecf20Sopenharmony_ci tmp /= symrate; 1588c2ecf20Sopenharmony_ci tmp *= 1024; /* 1024 = 2*10 */ 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci // adjust 1618c2ecf20Sopenharmony_ci if (tmp >= 0) { 1628c2ecf20Sopenharmony_ci tmp += 500000; 1638c2ecf20Sopenharmony_ci } else { 1648c2ecf20Sopenharmony_ci tmp -= 500000; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci tmp /= 1000000; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x60, tmp & 0xFF); 1698c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x69, 0xF0, (tmp >> 4) & 0xf0); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic void stv0297_set_carrieroffset(struct stv0297_state *state, long offset) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci long tmp; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* symrate is hardcoded to 10000 */ 1778c2ecf20Sopenharmony_ci tmp = offset * 26844L; /* (2**28)/10000 */ 1788c2ecf20Sopenharmony_ci if (tmp < 0) 1798c2ecf20Sopenharmony_ci tmp += 0x10000000; 1808c2ecf20Sopenharmony_ci tmp &= 0x0FFFFFFF; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x66, (unsigned char) (tmp & 0xFF)); 1838c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x67, (unsigned char) (tmp >> 8)); 1848c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x68, (unsigned char) (tmp >> 16)); 1858c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x69, 0x0F, (tmp >> 24) & 0x0f); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* 1898c2ecf20Sopenharmony_cistatic long stv0297_get_carrieroffset(struct stv0297_state *state) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci s64 tmp; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x6B, 0x00); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci tmp = stv0297_readreg(state, 0x66); 1968c2ecf20Sopenharmony_ci tmp |= (stv0297_readreg(state, 0x67) << 8); 1978c2ecf20Sopenharmony_ci tmp |= (stv0297_readreg(state, 0x68) << 16); 1988c2ecf20Sopenharmony_ci tmp |= (stv0297_readreg(state, 0x69) & 0x0F) << 24; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci tmp *= stv0297_get_symbolrate(state); 2018c2ecf20Sopenharmony_ci tmp >>= 28; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return (s32) tmp; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci*/ 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void stv0297_set_initialdemodfreq(struct stv0297_state *state, long freq) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci s32 tmp; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (freq > 10000) 2128c2ecf20Sopenharmony_ci freq -= STV0297_CLOCK_KHZ; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci tmp = (STV0297_CLOCK_KHZ * 1000) / (1 << 16); 2158c2ecf20Sopenharmony_ci tmp = (freq * 1000) / tmp; 2168c2ecf20Sopenharmony_ci if (tmp > 0xffff) 2178c2ecf20Sopenharmony_ci tmp = 0xffff; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x25, 0x80, 0x80); 2208c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x21, tmp >> 8); 2218c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x20, tmp); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int stv0297_set_qam(struct stv0297_state *state, 2258c2ecf20Sopenharmony_ci enum fe_modulation modulation) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci int val = 0; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci switch (modulation) { 2308c2ecf20Sopenharmony_ci case QAM_16: 2318c2ecf20Sopenharmony_ci val = 0; 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci case QAM_32: 2358c2ecf20Sopenharmony_ci val = 1; 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci case QAM_64: 2398c2ecf20Sopenharmony_ci val = 4; 2408c2ecf20Sopenharmony_ci break; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci case QAM_128: 2438c2ecf20Sopenharmony_ci val = 2; 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci case QAM_256: 2478c2ecf20Sopenharmony_ci val = 3; 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci default: 2518c2ecf20Sopenharmony_ci return -EINVAL; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x00, 0x70, val << 4); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return 0; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int stv0297_set_inversion(struct stv0297_state *state, 2608c2ecf20Sopenharmony_ci enum fe_spectral_inversion inversion) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci int val = 0; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci switch (inversion) { 2658c2ecf20Sopenharmony_ci case INVERSION_OFF: 2668c2ecf20Sopenharmony_ci val = 0; 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci case INVERSION_ON: 2708c2ecf20Sopenharmony_ci val = 1; 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci default: 2748c2ecf20Sopenharmony_ci return -EINVAL; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x83, 0x08, val << 3); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci return 0; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic int stv0297_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct stv0297_state *state = fe->demodulator_priv; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (enable) { 2878c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x87, 0x78); 2888c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x86, 0xc8); 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int stv0297_init(struct dvb_frontend *fe) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct stv0297_state *state = fe->demodulator_priv; 2978c2ecf20Sopenharmony_ci int i; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* load init table */ 3008c2ecf20Sopenharmony_ci for (i=0; !(state->config->inittab[i] == 0xff && state->config->inittab[i+1] == 0xff); i+=2) 3018c2ecf20Sopenharmony_ci stv0297_writereg(state, state->config->inittab[i], state->config->inittab[i+1]); 3028c2ecf20Sopenharmony_ci msleep(200); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci state->last_ber = 0; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int stv0297_sleep(struct dvb_frontend *fe) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct stv0297_state *state = fe->demodulator_priv; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x80, 1, 1); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int stv0297_read_status(struct dvb_frontend *fe, 3198c2ecf20Sopenharmony_ci enum fe_status *status) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct stv0297_state *state = fe->demodulator_priv; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci u8 sync = stv0297_readreg(state, 0xDF); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci *status = 0; 3268c2ecf20Sopenharmony_ci if (sync & 0x80) 3278c2ecf20Sopenharmony_ci *status |= 3288c2ecf20Sopenharmony_ci FE_HAS_SYNC | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_LOCK; 3298c2ecf20Sopenharmony_ci return 0; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int stv0297_read_ber(struct dvb_frontend *fe, u32 * ber) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct stv0297_state *state = fe->demodulator_priv; 3358c2ecf20Sopenharmony_ci u8 BER[3]; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci stv0297_readregs(state, 0xA0, BER, 3); 3388c2ecf20Sopenharmony_ci if (!(BER[0] & 0x80)) { 3398c2ecf20Sopenharmony_ci state->last_ber = BER[2] << 8 | BER[1]; 3408c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0xA0, 0x80, 0x80); 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci *ber = state->last_ber; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int stv0297_read_signal_strength(struct dvb_frontend *fe, u16 * strength) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct stv0297_state *state = fe->demodulator_priv; 3528c2ecf20Sopenharmony_ci u8 STRENGTH[3]; 3538c2ecf20Sopenharmony_ci u16 tmp; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci stv0297_readregs(state, 0x41, STRENGTH, 3); 3568c2ecf20Sopenharmony_ci tmp = (STRENGTH[1] & 0x03) << 8 | STRENGTH[0]; 3578c2ecf20Sopenharmony_ci if (STRENGTH[2] & 0x20) { 3588c2ecf20Sopenharmony_ci if (tmp < 0x200) 3598c2ecf20Sopenharmony_ci tmp = 0; 3608c2ecf20Sopenharmony_ci else 3618c2ecf20Sopenharmony_ci tmp = tmp - 0x200; 3628c2ecf20Sopenharmony_ci } else { 3638c2ecf20Sopenharmony_ci if (tmp > 0x1ff) 3648c2ecf20Sopenharmony_ci tmp = 0; 3658c2ecf20Sopenharmony_ci else 3668c2ecf20Sopenharmony_ci tmp = 0x1ff - tmp; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci *strength = (tmp << 7) | (tmp >> 2); 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic int stv0297_read_snr(struct dvb_frontend *fe, u16 * snr) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct stv0297_state *state = fe->demodulator_priv; 3758c2ecf20Sopenharmony_ci u8 SNR[2]; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci stv0297_readregs(state, 0x07, SNR, 2); 3788c2ecf20Sopenharmony_ci *snr = SNR[1] << 8 | SNR[0]; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return 0; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int stv0297_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct stv0297_state *state = fe->demodulator_priv; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0xDF, 0x03, 0x03); /* freeze the counters */ 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci *ucblocks = (stv0297_readreg(state, 0xD5) << 8) 3908c2ecf20Sopenharmony_ci | stv0297_readreg(state, 0xD4); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0xDF, 0x03, 0x02); /* clear the counters */ 3938c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0xDF, 0x03, 0x01); /* re-enable the counters */ 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return 0; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic int stv0297_set_frontend(struct dvb_frontend *fe) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p = &fe->dtv_property_cache; 4018c2ecf20Sopenharmony_ci struct stv0297_state *state = fe->demodulator_priv; 4028c2ecf20Sopenharmony_ci int u_threshold; 4038c2ecf20Sopenharmony_ci int initial_u; 4048c2ecf20Sopenharmony_ci int blind_u; 4058c2ecf20Sopenharmony_ci int delay; 4068c2ecf20Sopenharmony_ci int sweeprate; 4078c2ecf20Sopenharmony_ci int carrieroffset; 4088c2ecf20Sopenharmony_ci unsigned long timeout; 4098c2ecf20Sopenharmony_ci enum fe_spectral_inversion inversion; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci switch (p->modulation) { 4128c2ecf20Sopenharmony_ci case QAM_16: 4138c2ecf20Sopenharmony_ci case QAM_32: 4148c2ecf20Sopenharmony_ci case QAM_64: 4158c2ecf20Sopenharmony_ci delay = 100; 4168c2ecf20Sopenharmony_ci sweeprate = 1000; 4178c2ecf20Sopenharmony_ci break; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci case QAM_128: 4208c2ecf20Sopenharmony_ci case QAM_256: 4218c2ecf20Sopenharmony_ci delay = 200; 4228c2ecf20Sopenharmony_ci sweeprate = 500; 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci default: 4268c2ecf20Sopenharmony_ci return -EINVAL; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci // determine inversion dependent parameters 4308c2ecf20Sopenharmony_ci inversion = p->inversion; 4318c2ecf20Sopenharmony_ci if (state->config->invert) 4328c2ecf20Sopenharmony_ci inversion = (inversion == INVERSION_ON) ? INVERSION_OFF : INVERSION_ON; 4338c2ecf20Sopenharmony_ci carrieroffset = -330; 4348c2ecf20Sopenharmony_ci switch (inversion) { 4358c2ecf20Sopenharmony_ci case INVERSION_OFF: 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci case INVERSION_ON: 4398c2ecf20Sopenharmony_ci sweeprate = -sweeprate; 4408c2ecf20Sopenharmony_ci carrieroffset = -carrieroffset; 4418c2ecf20Sopenharmony_ci break; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci default: 4448c2ecf20Sopenharmony_ci return -EINVAL; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci stv0297_init(fe); 4488c2ecf20Sopenharmony_ci if (fe->ops.tuner_ops.set_params) { 4498c2ecf20Sopenharmony_ci fe->ops.tuner_ops.set_params(fe); 4508c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* clear software interrupts */ 4548c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x82, 0x0); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* set initial demodulation frequency */ 4578c2ecf20Sopenharmony_ci stv0297_set_initialdemodfreq(state, 7250); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* setup AGC */ 4608c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x43, 0x10, 0x00); 4618c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x41, 0x00); 4628c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x42, 0x03, 0x01); 4638c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x36, 0x60, 0x00); 4648c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x36, 0x18, 0x00); 4658c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x71, 0x80, 0x80); 4668c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x72, 0x00); 4678c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x73, 0x00); 4688c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x74, 0x0F, 0x00); 4698c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x43, 0x08, 0x00); 4708c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x71, 0x80, 0x00); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* setup STL */ 4738c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x5a, 0x20, 0x20); 4748c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x5b, 0x02, 0x02); 4758c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x5b, 0x02, 0x00); 4768c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x5b, 0x01, 0x00); 4778c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x5a, 0x40, 0x40); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* disable frequency sweep */ 4808c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x6a, 0x01, 0x00); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* reset deinterleaver */ 4838c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x81, 0x01, 0x01); 4848c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x81, 0x01, 0x00); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci /* ??? */ 4878c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x83, 0x20, 0x20); 4888c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x83, 0x20, 0x00); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* reset equaliser */ 4918c2ecf20Sopenharmony_ci u_threshold = stv0297_readreg(state, 0x00) & 0xf; 4928c2ecf20Sopenharmony_ci initial_u = stv0297_readreg(state, 0x01) >> 4; 4938c2ecf20Sopenharmony_ci blind_u = stv0297_readreg(state, 0x01) & 0xf; 4948c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x84, 0x01, 0x01); 4958c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x84, 0x01, 0x00); 4968c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x00, 0x0f, u_threshold); 4978c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x01, 0xf0, initial_u << 4); 4988c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x01, 0x0f, blind_u); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* data comes from internal A/D */ 5018c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x87, 0x80, 0x00); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci /* clear phase registers */ 5048c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x63, 0x00); 5058c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x64, 0x00); 5068c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x65, 0x00); 5078c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x66, 0x00); 5088c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x67, 0x00); 5098c2ecf20Sopenharmony_ci stv0297_writereg(state, 0x68, 0x00); 5108c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x69, 0x0f, 0x00); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* set parameters */ 5138c2ecf20Sopenharmony_ci stv0297_set_qam(state, p->modulation); 5148c2ecf20Sopenharmony_ci stv0297_set_symbolrate(state, p->symbol_rate / 1000); 5158c2ecf20Sopenharmony_ci stv0297_set_sweeprate(state, sweeprate, p->symbol_rate / 1000); 5168c2ecf20Sopenharmony_ci stv0297_set_carrieroffset(state, carrieroffset); 5178c2ecf20Sopenharmony_ci stv0297_set_inversion(state, inversion); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* kick off lock */ 5208c2ecf20Sopenharmony_ci /* Disable corner detection for higher QAMs */ 5218c2ecf20Sopenharmony_ci if (p->modulation == QAM_128 || 5228c2ecf20Sopenharmony_ci p->modulation == QAM_256) 5238c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x88, 0x08, 0x00); 5248c2ecf20Sopenharmony_ci else 5258c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x88, 0x08, 0x08); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x5a, 0x20, 0x00); 5288c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x6a, 0x01, 0x01); 5298c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x43, 0x40, 0x40); 5308c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x5b, 0x30, 0x00); 5318c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x03, 0x0c, 0x0c); 5328c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x03, 0x03, 0x03); 5338c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x43, 0x10, 0x10); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* wait for WGAGC lock */ 5368c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(2000); 5378c2ecf20Sopenharmony_ci while (time_before(jiffies, timeout)) { 5388c2ecf20Sopenharmony_ci msleep(10); 5398c2ecf20Sopenharmony_ci if (stv0297_readreg(state, 0x43) & 0x08) 5408c2ecf20Sopenharmony_ci break; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 5438c2ecf20Sopenharmony_ci goto timeout; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci msleep(20); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* wait for equaliser partial convergence */ 5488c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(500); 5498c2ecf20Sopenharmony_ci while (time_before(jiffies, timeout)) { 5508c2ecf20Sopenharmony_ci msleep(10); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (stv0297_readreg(state, 0x82) & 0x04) { 5538c2ecf20Sopenharmony_ci break; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 5578c2ecf20Sopenharmony_ci goto timeout; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci /* wait for equaliser full convergence */ 5618c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(delay); 5628c2ecf20Sopenharmony_ci while (time_before(jiffies, timeout)) { 5638c2ecf20Sopenharmony_ci msleep(10); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (stv0297_readreg(state, 0x82) & 0x08) { 5668c2ecf20Sopenharmony_ci break; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 5708c2ecf20Sopenharmony_ci goto timeout; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* disable sweep */ 5748c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x6a, 1, 0); 5758c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x88, 8, 0); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci /* wait for main lock */ 5788c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(20); 5798c2ecf20Sopenharmony_ci while (time_before(jiffies, timeout)) { 5808c2ecf20Sopenharmony_ci msleep(10); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (stv0297_readreg(state, 0xDF) & 0x80) { 5838c2ecf20Sopenharmony_ci break; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 5878c2ecf20Sopenharmony_ci goto timeout; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci msleep(100); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci /* is it still locked after that delay? */ 5928c2ecf20Sopenharmony_ci if (!(stv0297_readreg(state, 0xDF) & 0x80)) { 5938c2ecf20Sopenharmony_ci goto timeout; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* success!! */ 5978c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x5a, 0x40, 0x00); 5988c2ecf20Sopenharmony_ci state->base_freq = p->frequency; 5998c2ecf20Sopenharmony_ci return 0; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_citimeout: 6028c2ecf20Sopenharmony_ci stv0297_writereg_mask(state, 0x6a, 0x01, 0x00); 6038c2ecf20Sopenharmony_ci return 0; 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistatic int stv0297_get_frontend(struct dvb_frontend *fe, 6078c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci struct stv0297_state *state = fe->demodulator_priv; 6108c2ecf20Sopenharmony_ci int reg_00, reg_83; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci reg_00 = stv0297_readreg(state, 0x00); 6138c2ecf20Sopenharmony_ci reg_83 = stv0297_readreg(state, 0x83); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci p->frequency = state->base_freq; 6168c2ecf20Sopenharmony_ci p->inversion = (reg_83 & 0x08) ? INVERSION_ON : INVERSION_OFF; 6178c2ecf20Sopenharmony_ci if (state->config->invert) 6188c2ecf20Sopenharmony_ci p->inversion = (p->inversion == INVERSION_ON) ? INVERSION_OFF : INVERSION_ON; 6198c2ecf20Sopenharmony_ci p->symbol_rate = stv0297_get_symbolrate(state) * 1000; 6208c2ecf20Sopenharmony_ci p->fec_inner = FEC_NONE; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci switch ((reg_00 >> 4) & 0x7) { 6238c2ecf20Sopenharmony_ci case 0: 6248c2ecf20Sopenharmony_ci p->modulation = QAM_16; 6258c2ecf20Sopenharmony_ci break; 6268c2ecf20Sopenharmony_ci case 1: 6278c2ecf20Sopenharmony_ci p->modulation = QAM_32; 6288c2ecf20Sopenharmony_ci break; 6298c2ecf20Sopenharmony_ci case 2: 6308c2ecf20Sopenharmony_ci p->modulation = QAM_128; 6318c2ecf20Sopenharmony_ci break; 6328c2ecf20Sopenharmony_ci case 3: 6338c2ecf20Sopenharmony_ci p->modulation = QAM_256; 6348c2ecf20Sopenharmony_ci break; 6358c2ecf20Sopenharmony_ci case 4: 6368c2ecf20Sopenharmony_ci p->modulation = QAM_64; 6378c2ecf20Sopenharmony_ci break; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci return 0; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic void stv0297_release(struct dvb_frontend *fe) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci struct stv0297_state *state = fe->demodulator_priv; 6468c2ecf20Sopenharmony_ci kfree(state); 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops stv0297_ops; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistruct dvb_frontend *stv0297_attach(const struct stv0297_config *config, 6528c2ecf20Sopenharmony_ci struct i2c_adapter *i2c) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci struct stv0297_state *state = NULL; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* allocate memory for the internal state */ 6578c2ecf20Sopenharmony_ci state = kzalloc(sizeof(struct stv0297_state), GFP_KERNEL); 6588c2ecf20Sopenharmony_ci if (state == NULL) 6598c2ecf20Sopenharmony_ci goto error; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci /* setup the state */ 6628c2ecf20Sopenharmony_ci state->config = config; 6638c2ecf20Sopenharmony_ci state->i2c = i2c; 6648c2ecf20Sopenharmony_ci state->last_ber = 0; 6658c2ecf20Sopenharmony_ci state->base_freq = 0; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci /* check if the demod is there */ 6688c2ecf20Sopenharmony_ci if ((stv0297_readreg(state, 0x80) & 0x70) != 0x20) 6698c2ecf20Sopenharmony_ci goto error; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci /* create dvb_frontend */ 6728c2ecf20Sopenharmony_ci memcpy(&state->frontend.ops, &stv0297_ops, sizeof(struct dvb_frontend_ops)); 6738c2ecf20Sopenharmony_ci state->frontend.demodulator_priv = state; 6748c2ecf20Sopenharmony_ci return &state->frontend; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cierror: 6778c2ecf20Sopenharmony_ci kfree(state); 6788c2ecf20Sopenharmony_ci return NULL; 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops stv0297_ops = { 6828c2ecf20Sopenharmony_ci .delsys = { SYS_DVBC_ANNEX_A }, 6838c2ecf20Sopenharmony_ci .info = { 6848c2ecf20Sopenharmony_ci .name = "ST STV0297 DVB-C", 6858c2ecf20Sopenharmony_ci .frequency_min_hz = 47 * MHz, 6868c2ecf20Sopenharmony_ci .frequency_max_hz = 862 * MHz, 6878c2ecf20Sopenharmony_ci .frequency_stepsize_hz = 62500, 6888c2ecf20Sopenharmony_ci .symbol_rate_min = 870000, 6898c2ecf20Sopenharmony_ci .symbol_rate_max = 11700000, 6908c2ecf20Sopenharmony_ci .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | 6918c2ecf20Sopenharmony_ci FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO}, 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci .release = stv0297_release, 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci .init = stv0297_init, 6968c2ecf20Sopenharmony_ci .sleep = stv0297_sleep, 6978c2ecf20Sopenharmony_ci .i2c_gate_ctrl = stv0297_i2c_gate_ctrl, 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci .set_frontend = stv0297_set_frontend, 7008c2ecf20Sopenharmony_ci .get_frontend = stv0297_get_frontend, 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci .read_status = stv0297_read_status, 7038c2ecf20Sopenharmony_ci .read_ber = stv0297_read_ber, 7048c2ecf20Sopenharmony_ci .read_signal_strength = stv0297_read_signal_strength, 7058c2ecf20Sopenharmony_ci .read_snr = stv0297_read_snr, 7068c2ecf20Sopenharmony_ci .read_ucblocks = stv0297_read_ucblocks, 7078c2ecf20Sopenharmony_ci}; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ST STV0297 DVB-C Demodulator driver"); 7108c2ecf20Sopenharmony_ciMODULE_AUTHOR("Dennis Noermann and Andrew de Quincey"); 7118c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(stv0297_attach); 714