19a0bf528SMauro Carvalho Chehab /* DVB compliant Linux driver for the DVB-S si2109/2110 demodulator 29a0bf528SMauro Carvalho Chehab * 39a0bf528SMauro Carvalho Chehab * Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) 49a0bf528SMauro Carvalho Chehab * 59a0bf528SMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 69a0bf528SMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 79a0bf528SMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 89a0bf528SMauro Carvalho Chehab * (at your option) any later version. 99a0bf528SMauro Carvalho Chehab * 109a0bf528SMauro Carvalho Chehab */ 119a0bf528SMauro Carvalho Chehab #include <linux/init.h> 129a0bf528SMauro Carvalho Chehab #include <linux/kernel.h> 139a0bf528SMauro Carvalho Chehab #include <linux/module.h> 149a0bf528SMauro Carvalho Chehab #include <linux/string.h> 159a0bf528SMauro Carvalho Chehab #include <linux/slab.h> 169a0bf528SMauro Carvalho Chehab #include <linux/jiffies.h> 179a0bf528SMauro Carvalho Chehab #include <asm/div64.h> 189a0bf528SMauro Carvalho Chehab 199a0bf528SMauro Carvalho Chehab #include "dvb_frontend.h" 209a0bf528SMauro Carvalho Chehab #include "si21xx.h" 219a0bf528SMauro Carvalho Chehab 229a0bf528SMauro Carvalho Chehab #define REVISION_REG 0x00 239a0bf528SMauro Carvalho Chehab #define SYSTEM_MODE_REG 0x01 249a0bf528SMauro Carvalho Chehab #define TS_CTRL_REG_1 0x02 259a0bf528SMauro Carvalho Chehab #define TS_CTRL_REG_2 0x03 269a0bf528SMauro Carvalho Chehab #define PIN_CTRL_REG_1 0x04 279a0bf528SMauro Carvalho Chehab #define PIN_CTRL_REG_2 0x05 289a0bf528SMauro Carvalho Chehab #define LOCK_STATUS_REG_1 0x0f 299a0bf528SMauro Carvalho Chehab #define LOCK_STATUS_REG_2 0x10 309a0bf528SMauro Carvalho Chehab #define ACQ_STATUS_REG 0x11 319a0bf528SMauro Carvalho Chehab #define ACQ_CTRL_REG_1 0x13 329a0bf528SMauro Carvalho Chehab #define ACQ_CTRL_REG_2 0x14 339a0bf528SMauro Carvalho Chehab #define PLL_DIVISOR_REG 0x15 349a0bf528SMauro Carvalho Chehab #define COARSE_TUNE_REG 0x16 359a0bf528SMauro Carvalho Chehab #define FINE_TUNE_REG_L 0x17 369a0bf528SMauro Carvalho Chehab #define FINE_TUNE_REG_H 0x18 379a0bf528SMauro Carvalho Chehab 389a0bf528SMauro Carvalho Chehab #define ANALOG_AGC_POWER_LEVEL_REG 0x28 399a0bf528SMauro Carvalho Chehab #define CFO_ESTIMATOR_CTRL_REG_1 0x29 409a0bf528SMauro Carvalho Chehab #define CFO_ESTIMATOR_CTRL_REG_2 0x2a 419a0bf528SMauro Carvalho Chehab #define CFO_ESTIMATOR_CTRL_REG_3 0x2b 429a0bf528SMauro Carvalho Chehab 439a0bf528SMauro Carvalho Chehab #define SYM_RATE_ESTIMATE_REG_L 0x31 449a0bf528SMauro Carvalho Chehab #define SYM_RATE_ESTIMATE_REG_M 0x32 459a0bf528SMauro Carvalho Chehab #define SYM_RATE_ESTIMATE_REG_H 0x33 469a0bf528SMauro Carvalho Chehab 479a0bf528SMauro Carvalho Chehab #define CFO_ESTIMATOR_OFFSET_REG_L 0x36 489a0bf528SMauro Carvalho Chehab #define CFO_ESTIMATOR_OFFSET_REG_H 0x37 499a0bf528SMauro Carvalho Chehab #define CFO_ERROR_REG_L 0x38 509a0bf528SMauro Carvalho Chehab #define CFO_ERROR_REG_H 0x39 519a0bf528SMauro Carvalho Chehab #define SYM_RATE_ESTIMATOR_CTRL_REG 0x3a 529a0bf528SMauro Carvalho Chehab 539a0bf528SMauro Carvalho Chehab #define SYM_RATE_REG_L 0x3f 549a0bf528SMauro Carvalho Chehab #define SYM_RATE_REG_M 0x40 559a0bf528SMauro Carvalho Chehab #define SYM_RATE_REG_H 0x41 569a0bf528SMauro Carvalho Chehab #define SYM_RATE_ESTIMATOR_MAXIMUM_REG 0x42 579a0bf528SMauro Carvalho Chehab #define SYM_RATE_ESTIMATOR_MINIMUM_REG 0x43 589a0bf528SMauro Carvalho Chehab 599a0bf528SMauro Carvalho Chehab #define C_N_ESTIMATOR_CTRL_REG 0x7c 609a0bf528SMauro Carvalho Chehab #define C_N_ESTIMATOR_THRSHLD_REG 0x7d 619a0bf528SMauro Carvalho Chehab #define C_N_ESTIMATOR_LEVEL_REG_L 0x7e 629a0bf528SMauro Carvalho Chehab #define C_N_ESTIMATOR_LEVEL_REG_H 0x7f 639a0bf528SMauro Carvalho Chehab 649a0bf528SMauro Carvalho Chehab #define BLIND_SCAN_CTRL_REG 0x80 659a0bf528SMauro Carvalho Chehab 669a0bf528SMauro Carvalho Chehab #define LSA_CTRL_REG_1 0x8D 679a0bf528SMauro Carvalho Chehab #define SPCTRM_TILT_CORR_THRSHLD_REG 0x8f 689a0bf528SMauro Carvalho Chehab #define ONE_DB_BNDWDTH_THRSHLD_REG 0x90 699a0bf528SMauro Carvalho Chehab #define TWO_DB_BNDWDTH_THRSHLD_REG 0x91 709a0bf528SMauro Carvalho Chehab #define THREE_DB_BNDWDTH_THRSHLD_REG 0x92 719a0bf528SMauro Carvalho Chehab #define INBAND_POWER_THRSHLD_REG 0x93 729a0bf528SMauro Carvalho Chehab #define REF_NOISE_LVL_MRGN_THRSHLD_REG 0x94 739a0bf528SMauro Carvalho Chehab 749a0bf528SMauro Carvalho Chehab #define VIT_SRCH_CTRL_REG_1 0xa0 759a0bf528SMauro Carvalho Chehab #define VIT_SRCH_CTRL_REG_2 0xa1 769a0bf528SMauro Carvalho Chehab #define VIT_SRCH_CTRL_REG_3 0xa2 779a0bf528SMauro Carvalho Chehab #define VIT_SRCH_STATUS_REG 0xa3 789a0bf528SMauro Carvalho Chehab #define VITERBI_BER_COUNT_REG_L 0xab 799a0bf528SMauro Carvalho Chehab #define REED_SOLOMON_CTRL_REG 0xb0 809a0bf528SMauro Carvalho Chehab #define REED_SOLOMON_ERROR_COUNT_REG_L 0xb1 819a0bf528SMauro Carvalho Chehab #define PRBS_CTRL_REG 0xb5 829a0bf528SMauro Carvalho Chehab 839a0bf528SMauro Carvalho Chehab #define LNB_CTRL_REG_1 0xc0 849a0bf528SMauro Carvalho Chehab #define LNB_CTRL_REG_2 0xc1 859a0bf528SMauro Carvalho Chehab #define LNB_CTRL_REG_3 0xc2 869a0bf528SMauro Carvalho Chehab #define LNB_CTRL_REG_4 0xc3 879a0bf528SMauro Carvalho Chehab #define LNB_CTRL_STATUS_REG 0xc4 889a0bf528SMauro Carvalho Chehab #define LNB_FIFO_REGS_0 0xc5 899a0bf528SMauro Carvalho Chehab #define LNB_FIFO_REGS_1 0xc6 909a0bf528SMauro Carvalho Chehab #define LNB_FIFO_REGS_2 0xc7 919a0bf528SMauro Carvalho Chehab #define LNB_FIFO_REGS_3 0xc8 929a0bf528SMauro Carvalho Chehab #define LNB_FIFO_REGS_4 0xc9 939a0bf528SMauro Carvalho Chehab #define LNB_FIFO_REGS_5 0xca 949a0bf528SMauro Carvalho Chehab #define LNB_SUPPLY_CTRL_REG_1 0xcb 959a0bf528SMauro Carvalho Chehab #define LNB_SUPPLY_CTRL_REG_2 0xcc 969a0bf528SMauro Carvalho Chehab #define LNB_SUPPLY_CTRL_REG_3 0xcd 979a0bf528SMauro Carvalho Chehab #define LNB_SUPPLY_CTRL_REG_4 0xce 989a0bf528SMauro Carvalho Chehab #define LNB_SUPPLY_STATUS_REG 0xcf 999a0bf528SMauro Carvalho Chehab 1009a0bf528SMauro Carvalho Chehab #define FAIL -1 1019a0bf528SMauro Carvalho Chehab #define PASS 0 1029a0bf528SMauro Carvalho Chehab 1039a0bf528SMauro Carvalho Chehab #define ALLOWABLE_FS_COUNT 10 1049a0bf528SMauro Carvalho Chehab #define STATUS_BER 0 1059a0bf528SMauro Carvalho Chehab #define STATUS_UCBLOCKS 1 1069a0bf528SMauro Carvalho Chehab 1079a0bf528SMauro Carvalho Chehab static int debug; 1089a0bf528SMauro Carvalho Chehab #define dprintk(args...) \ 1099a0bf528SMauro Carvalho Chehab do { \ 1109a0bf528SMauro Carvalho Chehab if (debug) \ 1119a0bf528SMauro Carvalho Chehab printk(KERN_DEBUG "si21xx: " args); \ 1129a0bf528SMauro Carvalho Chehab } while (0) 1139a0bf528SMauro Carvalho Chehab 1149a0bf528SMauro Carvalho Chehab enum { 1159a0bf528SMauro Carvalho Chehab ACTIVE_HIGH, 1169a0bf528SMauro Carvalho Chehab ACTIVE_LOW 1179a0bf528SMauro Carvalho Chehab }; 1189a0bf528SMauro Carvalho Chehab enum { 1199a0bf528SMauro Carvalho Chehab BYTE_WIDE, 1209a0bf528SMauro Carvalho Chehab BIT_WIDE 1219a0bf528SMauro Carvalho Chehab }; 1229a0bf528SMauro Carvalho Chehab enum { 1239a0bf528SMauro Carvalho Chehab CLK_GAPPED_MODE, 1249a0bf528SMauro Carvalho Chehab CLK_CONTINUOUS_MODE 1259a0bf528SMauro Carvalho Chehab }; 1269a0bf528SMauro Carvalho Chehab enum { 1279a0bf528SMauro Carvalho Chehab RISING_EDGE, 1289a0bf528SMauro Carvalho Chehab FALLING_EDGE 1299a0bf528SMauro Carvalho Chehab }; 1309a0bf528SMauro Carvalho Chehab enum { 1319a0bf528SMauro Carvalho Chehab MSB_FIRST, 1329a0bf528SMauro Carvalho Chehab LSB_FIRST 1339a0bf528SMauro Carvalho Chehab }; 1349a0bf528SMauro Carvalho Chehab enum { 1359a0bf528SMauro Carvalho Chehab SERIAL, 1369a0bf528SMauro Carvalho Chehab PARALLEL 1379a0bf528SMauro Carvalho Chehab }; 1389a0bf528SMauro Carvalho Chehab 1399a0bf528SMauro Carvalho Chehab struct si21xx_state { 1409a0bf528SMauro Carvalho Chehab struct i2c_adapter *i2c; 1419a0bf528SMauro Carvalho Chehab const struct si21xx_config *config; 1429a0bf528SMauro Carvalho Chehab struct dvb_frontend frontend; 1439a0bf528SMauro Carvalho Chehab u8 initialised:1; 1449a0bf528SMauro Carvalho Chehab int errmode; 1459a0bf528SMauro Carvalho Chehab int fs; /*Sampling rate of the ADC in MHz*/ 1469a0bf528SMauro Carvalho Chehab }; 1479a0bf528SMauro Carvalho Chehab 1489a0bf528SMauro Carvalho Chehab /* register default initialization */ 1499a0bf528SMauro Carvalho Chehab static u8 serit_sp1511lhb_inittab[] = { 1509a0bf528SMauro Carvalho Chehab 0x01, 0x28, /* set i2c_inc_disable */ 1519a0bf528SMauro Carvalho Chehab 0x20, 0x03, 1529a0bf528SMauro Carvalho Chehab 0x27, 0x20, 1539a0bf528SMauro Carvalho Chehab 0xe0, 0x45, 1549a0bf528SMauro Carvalho Chehab 0xe1, 0x08, 1559a0bf528SMauro Carvalho Chehab 0xfe, 0x01, 1569a0bf528SMauro Carvalho Chehab 0x01, 0x28, 1579a0bf528SMauro Carvalho Chehab 0x89, 0x09, 1589a0bf528SMauro Carvalho Chehab 0x04, 0x80, 1599a0bf528SMauro Carvalho Chehab 0x05, 0x01, 1609a0bf528SMauro Carvalho Chehab 0x06, 0x00, 1619a0bf528SMauro Carvalho Chehab 0x20, 0x03, 1629a0bf528SMauro Carvalho Chehab 0x24, 0x88, 1639a0bf528SMauro Carvalho Chehab 0x29, 0x09, 1649a0bf528SMauro Carvalho Chehab 0x2a, 0x0f, 1659a0bf528SMauro Carvalho Chehab 0x2c, 0x10, 1669a0bf528SMauro Carvalho Chehab 0x2d, 0x19, 1679a0bf528SMauro Carvalho Chehab 0x2e, 0x08, 1689a0bf528SMauro Carvalho Chehab 0x2f, 0x10, 1699a0bf528SMauro Carvalho Chehab 0x30, 0x19, 1709a0bf528SMauro Carvalho Chehab 0x34, 0x20, 1719a0bf528SMauro Carvalho Chehab 0x35, 0x03, 1729a0bf528SMauro Carvalho Chehab 0x45, 0x02, 1739a0bf528SMauro Carvalho Chehab 0x46, 0x45, 1749a0bf528SMauro Carvalho Chehab 0x47, 0xd0, 1759a0bf528SMauro Carvalho Chehab 0x48, 0x00, 1769a0bf528SMauro Carvalho Chehab 0x49, 0x40, 1779a0bf528SMauro Carvalho Chehab 0x4a, 0x03, 1789a0bf528SMauro Carvalho Chehab 0x4c, 0xfd, 1799a0bf528SMauro Carvalho Chehab 0x4f, 0x2e, 1809a0bf528SMauro Carvalho Chehab 0x50, 0x2e, 1819a0bf528SMauro Carvalho Chehab 0x51, 0x10, 1829a0bf528SMauro Carvalho Chehab 0x52, 0x10, 1839a0bf528SMauro Carvalho Chehab 0x56, 0x92, 1849a0bf528SMauro Carvalho Chehab 0x59, 0x00, 1859a0bf528SMauro Carvalho Chehab 0x5a, 0x2d, 1869a0bf528SMauro Carvalho Chehab 0x5b, 0x33, 1879a0bf528SMauro Carvalho Chehab 0x5c, 0x1f, 1889a0bf528SMauro Carvalho Chehab 0x5f, 0x76, 1899a0bf528SMauro Carvalho Chehab 0x62, 0xc0, 1909a0bf528SMauro Carvalho Chehab 0x63, 0xc0, 1919a0bf528SMauro Carvalho Chehab 0x64, 0xf3, 1929a0bf528SMauro Carvalho Chehab 0x65, 0xf3, 1939a0bf528SMauro Carvalho Chehab 0x79, 0x40, 1949a0bf528SMauro Carvalho Chehab 0x6a, 0x40, 1959a0bf528SMauro Carvalho Chehab 0x6b, 0x0a, 1969a0bf528SMauro Carvalho Chehab 0x6c, 0x80, 1979a0bf528SMauro Carvalho Chehab 0x6d, 0x27, 1989a0bf528SMauro Carvalho Chehab 0x71, 0x06, 1999a0bf528SMauro Carvalho Chehab 0x75, 0x60, 2009a0bf528SMauro Carvalho Chehab 0x78, 0x00, 2019a0bf528SMauro Carvalho Chehab 0x79, 0xb5, 2029a0bf528SMauro Carvalho Chehab 0x7c, 0x05, 2039a0bf528SMauro Carvalho Chehab 0x7d, 0x1a, 2049a0bf528SMauro Carvalho Chehab 0x87, 0x55, 2059a0bf528SMauro Carvalho Chehab 0x88, 0x72, 2069a0bf528SMauro Carvalho Chehab 0x8f, 0x08, 2079a0bf528SMauro Carvalho Chehab 0x90, 0xe0, 2089a0bf528SMauro Carvalho Chehab 0x94, 0x40, 2099a0bf528SMauro Carvalho Chehab 0xa0, 0x3f, 2109a0bf528SMauro Carvalho Chehab 0xa1, 0xc0, 2119a0bf528SMauro Carvalho Chehab 0xa4, 0xcc, 2129a0bf528SMauro Carvalho Chehab 0xa5, 0x66, 2139a0bf528SMauro Carvalho Chehab 0xa6, 0x66, 2149a0bf528SMauro Carvalho Chehab 0xa7, 0x7b, 2159a0bf528SMauro Carvalho Chehab 0xa8, 0x7b, 2169a0bf528SMauro Carvalho Chehab 0xa9, 0x7b, 2179a0bf528SMauro Carvalho Chehab 0xaa, 0x9a, 2189a0bf528SMauro Carvalho Chehab 0xed, 0x04, 2199a0bf528SMauro Carvalho Chehab 0xad, 0x00, 2209a0bf528SMauro Carvalho Chehab 0xae, 0x03, 2219a0bf528SMauro Carvalho Chehab 0xcc, 0xab, 2229a0bf528SMauro Carvalho Chehab 0x01, 0x08, 2239a0bf528SMauro Carvalho Chehab 0xff, 0xff 2249a0bf528SMauro Carvalho Chehab }; 2259a0bf528SMauro Carvalho Chehab 2269a0bf528SMauro Carvalho Chehab /* low level read/writes */ 2279a0bf528SMauro Carvalho Chehab static int si21_writeregs(struct si21xx_state *state, u8 reg1, 2289a0bf528SMauro Carvalho Chehab u8 *data, int len) 2299a0bf528SMauro Carvalho Chehab { 2309a0bf528SMauro Carvalho Chehab int ret; 2319a0bf528SMauro Carvalho Chehab u8 buf[60];/* = { reg1, data };*/ 2329a0bf528SMauro Carvalho Chehab struct i2c_msg msg = { 2339a0bf528SMauro Carvalho Chehab .addr = state->config->demod_address, 2349a0bf528SMauro Carvalho Chehab .flags = 0, 2359a0bf528SMauro Carvalho Chehab .buf = buf, 2369a0bf528SMauro Carvalho Chehab .len = len + 1 2379a0bf528SMauro Carvalho Chehab }; 2389a0bf528SMauro Carvalho Chehab 239cf3b576dSDan Carpenter if (len > sizeof(buf) - 1) 240cf3b576dSDan Carpenter return -EINVAL; 241cf3b576dSDan Carpenter 2429a0bf528SMauro Carvalho Chehab msg.buf[0] = reg1; 2439a0bf528SMauro Carvalho Chehab memcpy(msg.buf + 1, data, len); 2449a0bf528SMauro Carvalho Chehab 2459a0bf528SMauro Carvalho Chehab ret = i2c_transfer(state->i2c, &msg, 1); 2469a0bf528SMauro Carvalho Chehab 2479a0bf528SMauro Carvalho Chehab if (ret != 1) 2489a0bf528SMauro Carvalho Chehab dprintk("%s: writereg error (reg1 == 0x%02x, data == 0x%02x, " 2499a0bf528SMauro Carvalho Chehab "ret == %i)\n", __func__, reg1, data[0], ret); 2509a0bf528SMauro Carvalho Chehab 2519a0bf528SMauro Carvalho Chehab return (ret != 1) ? -EREMOTEIO : 0; 2529a0bf528SMauro Carvalho Chehab } 2539a0bf528SMauro Carvalho Chehab 2549a0bf528SMauro Carvalho Chehab static int si21_writereg(struct si21xx_state *state, u8 reg, u8 data) 2559a0bf528SMauro Carvalho Chehab { 2569a0bf528SMauro Carvalho Chehab int ret; 2579a0bf528SMauro Carvalho Chehab u8 buf[] = { reg, data }; 2589a0bf528SMauro Carvalho Chehab struct i2c_msg msg = { 2599a0bf528SMauro Carvalho Chehab .addr = state->config->demod_address, 2609a0bf528SMauro Carvalho Chehab .flags = 0, 2619a0bf528SMauro Carvalho Chehab .buf = buf, 2629a0bf528SMauro Carvalho Chehab .len = 2 2639a0bf528SMauro Carvalho Chehab }; 2649a0bf528SMauro Carvalho Chehab 2659a0bf528SMauro Carvalho Chehab ret = i2c_transfer(state->i2c, &msg, 1); 2669a0bf528SMauro Carvalho Chehab 2679a0bf528SMauro Carvalho Chehab if (ret != 1) 2689a0bf528SMauro Carvalho Chehab dprintk("%s: writereg error (reg == 0x%02x, data == 0x%02x, " 2699a0bf528SMauro Carvalho Chehab "ret == %i)\n", __func__, reg, data, ret); 2709a0bf528SMauro Carvalho Chehab 2719a0bf528SMauro Carvalho Chehab return (ret != 1) ? -EREMOTEIO : 0; 2729a0bf528SMauro Carvalho Chehab } 2739a0bf528SMauro Carvalho Chehab 2749a0bf528SMauro Carvalho Chehab static int si21_write(struct dvb_frontend *fe, const u8 buf[], int len) 2759a0bf528SMauro Carvalho Chehab { 2769a0bf528SMauro Carvalho Chehab struct si21xx_state *state = fe->demodulator_priv; 2779a0bf528SMauro Carvalho Chehab 2789a0bf528SMauro Carvalho Chehab if (len != 2) 2799a0bf528SMauro Carvalho Chehab return -EINVAL; 2809a0bf528SMauro Carvalho Chehab 2819a0bf528SMauro Carvalho Chehab return si21_writereg(state, buf[0], buf[1]); 2829a0bf528SMauro Carvalho Chehab } 2839a0bf528SMauro Carvalho Chehab 2849a0bf528SMauro Carvalho Chehab static u8 si21_readreg(struct si21xx_state *state, u8 reg) 2859a0bf528SMauro Carvalho Chehab { 2869a0bf528SMauro Carvalho Chehab int ret; 2879a0bf528SMauro Carvalho Chehab u8 b0[] = { reg }; 2889a0bf528SMauro Carvalho Chehab u8 b1[] = { 0 }; 2899a0bf528SMauro Carvalho Chehab struct i2c_msg msg[] = { 2909a0bf528SMauro Carvalho Chehab { 2919a0bf528SMauro Carvalho Chehab .addr = state->config->demod_address, 2929a0bf528SMauro Carvalho Chehab .flags = 0, 2939a0bf528SMauro Carvalho Chehab .buf = b0, 2949a0bf528SMauro Carvalho Chehab .len = 1 2959a0bf528SMauro Carvalho Chehab }, { 2969a0bf528SMauro Carvalho Chehab .addr = state->config->demod_address, 2979a0bf528SMauro Carvalho Chehab .flags = I2C_M_RD, 2989a0bf528SMauro Carvalho Chehab .buf = b1, 2999a0bf528SMauro Carvalho Chehab .len = 1 3009a0bf528SMauro Carvalho Chehab } 3019a0bf528SMauro Carvalho Chehab }; 3029a0bf528SMauro Carvalho Chehab 3039a0bf528SMauro Carvalho Chehab ret = i2c_transfer(state->i2c, msg, 2); 3049a0bf528SMauro Carvalho Chehab 3059a0bf528SMauro Carvalho Chehab if (ret != 2) 3069a0bf528SMauro Carvalho Chehab dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", 3079a0bf528SMauro Carvalho Chehab __func__, reg, ret); 3089a0bf528SMauro Carvalho Chehab 3099a0bf528SMauro Carvalho Chehab return b1[0]; 3109a0bf528SMauro Carvalho Chehab } 3119a0bf528SMauro Carvalho Chehab 3129a0bf528SMauro Carvalho Chehab static int si21_readregs(struct si21xx_state *state, u8 reg1, u8 *b, u8 len) 3139a0bf528SMauro Carvalho Chehab { 3149a0bf528SMauro Carvalho Chehab int ret; 3159a0bf528SMauro Carvalho Chehab struct i2c_msg msg[] = { 3169a0bf528SMauro Carvalho Chehab { 3179a0bf528SMauro Carvalho Chehab .addr = state->config->demod_address, 3189a0bf528SMauro Carvalho Chehab .flags = 0, 3199a0bf528SMauro Carvalho Chehab .buf = ®1, 3209a0bf528SMauro Carvalho Chehab .len = 1 3219a0bf528SMauro Carvalho Chehab }, { 3229a0bf528SMauro Carvalho Chehab .addr = state->config->demod_address, 3239a0bf528SMauro Carvalho Chehab .flags = I2C_M_RD, 3249a0bf528SMauro Carvalho Chehab .buf = b, 3259a0bf528SMauro Carvalho Chehab .len = len 3269a0bf528SMauro Carvalho Chehab } 3279a0bf528SMauro Carvalho Chehab }; 3289a0bf528SMauro Carvalho Chehab 3299a0bf528SMauro Carvalho Chehab ret = i2c_transfer(state->i2c, msg, 2); 3309a0bf528SMauro Carvalho Chehab 3319a0bf528SMauro Carvalho Chehab if (ret != 2) 3329a0bf528SMauro Carvalho Chehab dprintk("%s: readreg error (ret == %i)\n", __func__, ret); 3339a0bf528SMauro Carvalho Chehab 3349a0bf528SMauro Carvalho Chehab return ret == 2 ? 0 : -1; 3359a0bf528SMauro Carvalho Chehab } 3369a0bf528SMauro Carvalho Chehab 3379a0bf528SMauro Carvalho Chehab static int si21xx_wait_diseqc_idle(struct si21xx_state *state, int timeout) 3389a0bf528SMauro Carvalho Chehab { 3399a0bf528SMauro Carvalho Chehab unsigned long start = jiffies; 3409a0bf528SMauro Carvalho Chehab 3419a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 3429a0bf528SMauro Carvalho Chehab 3439a0bf528SMauro Carvalho Chehab while ((si21_readreg(state, LNB_CTRL_REG_1) & 0x8) == 8) { 3449a0bf528SMauro Carvalho Chehab if (jiffies - start > timeout) { 3459a0bf528SMauro Carvalho Chehab dprintk("%s: timeout!!\n", __func__); 3469a0bf528SMauro Carvalho Chehab return -ETIMEDOUT; 3479a0bf528SMauro Carvalho Chehab } 3489a0bf528SMauro Carvalho Chehab msleep(10); 349c2c1b415SPeter Senna Tschudin } 3509a0bf528SMauro Carvalho Chehab 3519a0bf528SMauro Carvalho Chehab return 0; 3529a0bf528SMauro Carvalho Chehab } 3539a0bf528SMauro Carvalho Chehab 3549a0bf528SMauro Carvalho Chehab static int si21xx_set_symbolrate(struct dvb_frontend *fe, u32 srate) 3559a0bf528SMauro Carvalho Chehab { 3569a0bf528SMauro Carvalho Chehab struct si21xx_state *state = fe->demodulator_priv; 3579a0bf528SMauro Carvalho Chehab u32 sym_rate, data_rate; 3589a0bf528SMauro Carvalho Chehab int i; 3599a0bf528SMauro Carvalho Chehab u8 sym_rate_bytes[3]; 3609a0bf528SMauro Carvalho Chehab 3619a0bf528SMauro Carvalho Chehab dprintk("%s : srate = %i\n", __func__ , srate); 3629a0bf528SMauro Carvalho Chehab 3639a0bf528SMauro Carvalho Chehab if ((srate < 1000000) || (srate > 45000000)) 3649a0bf528SMauro Carvalho Chehab return -EINVAL; 3659a0bf528SMauro Carvalho Chehab 3669a0bf528SMauro Carvalho Chehab data_rate = srate; 3679a0bf528SMauro Carvalho Chehab sym_rate = 0; 3689a0bf528SMauro Carvalho Chehab 3699a0bf528SMauro Carvalho Chehab for (i = 0; i < 4; ++i) { 3709a0bf528SMauro Carvalho Chehab sym_rate /= 100; 3719a0bf528SMauro Carvalho Chehab sym_rate = sym_rate + ((data_rate % 100) * 0x800000) / 3729a0bf528SMauro Carvalho Chehab state->fs; 3739a0bf528SMauro Carvalho Chehab data_rate /= 100; 3749a0bf528SMauro Carvalho Chehab } 3759a0bf528SMauro Carvalho Chehab for (i = 0; i < 3; ++i) 3769a0bf528SMauro Carvalho Chehab sym_rate_bytes[i] = (u8)((sym_rate >> (i * 8)) & 0xff); 3779a0bf528SMauro Carvalho Chehab 3789a0bf528SMauro Carvalho Chehab si21_writeregs(state, SYM_RATE_REG_L, sym_rate_bytes, 0x03); 3799a0bf528SMauro Carvalho Chehab 3809a0bf528SMauro Carvalho Chehab return 0; 3819a0bf528SMauro Carvalho Chehab } 3829a0bf528SMauro Carvalho Chehab 3839a0bf528SMauro Carvalho Chehab static int si21xx_send_diseqc_msg(struct dvb_frontend *fe, 3849a0bf528SMauro Carvalho Chehab struct dvb_diseqc_master_cmd *m) 3859a0bf528SMauro Carvalho Chehab { 3869a0bf528SMauro Carvalho Chehab struct si21xx_state *state = fe->demodulator_priv; 3879a0bf528SMauro Carvalho Chehab u8 lnb_status; 3889a0bf528SMauro Carvalho Chehab u8 LNB_CTRL_1; 3899a0bf528SMauro Carvalho Chehab int status; 3909a0bf528SMauro Carvalho Chehab 3919a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 3929a0bf528SMauro Carvalho Chehab 3939a0bf528SMauro Carvalho Chehab status = PASS; 3949a0bf528SMauro Carvalho Chehab LNB_CTRL_1 = 0; 3959a0bf528SMauro Carvalho Chehab 3969a0bf528SMauro Carvalho Chehab status |= si21_readregs(state, LNB_CTRL_STATUS_REG, &lnb_status, 0x01); 3979a0bf528SMauro Carvalho Chehab status |= si21_readregs(state, LNB_CTRL_REG_1, &lnb_status, 0x01); 3989a0bf528SMauro Carvalho Chehab 3999a0bf528SMauro Carvalho Chehab /*fill the FIFO*/ 4009a0bf528SMauro Carvalho Chehab status |= si21_writeregs(state, LNB_FIFO_REGS_0, m->msg, m->msg_len); 4019a0bf528SMauro Carvalho Chehab 4029a0bf528SMauro Carvalho Chehab LNB_CTRL_1 = (lnb_status & 0x70); 4039a0bf528SMauro Carvalho Chehab LNB_CTRL_1 |= m->msg_len; 4049a0bf528SMauro Carvalho Chehab 4059a0bf528SMauro Carvalho Chehab LNB_CTRL_1 |= 0x80; /* begin LNB signaling */ 4069a0bf528SMauro Carvalho Chehab 4079a0bf528SMauro Carvalho Chehab status |= si21_writeregs(state, LNB_CTRL_REG_1, &LNB_CTRL_1, 0x01); 4089a0bf528SMauro Carvalho Chehab 4099a0bf528SMauro Carvalho Chehab return status; 4109a0bf528SMauro Carvalho Chehab } 4119a0bf528SMauro Carvalho Chehab 4129a0bf528SMauro Carvalho Chehab static int si21xx_send_diseqc_burst(struct dvb_frontend *fe, 413*0df289a2SMauro Carvalho Chehab enum fe_sec_mini_cmd burst) 4149a0bf528SMauro Carvalho Chehab { 4159a0bf528SMauro Carvalho Chehab struct si21xx_state *state = fe->demodulator_priv; 4169a0bf528SMauro Carvalho Chehab u8 val; 4179a0bf528SMauro Carvalho Chehab 4189a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 4199a0bf528SMauro Carvalho Chehab 4209a0bf528SMauro Carvalho Chehab if (si21xx_wait_diseqc_idle(state, 100) < 0) 4219a0bf528SMauro Carvalho Chehab return -ETIMEDOUT; 4229a0bf528SMauro Carvalho Chehab 4239a0bf528SMauro Carvalho Chehab val = (0x80 | si21_readreg(state, 0xc1)); 4249a0bf528SMauro Carvalho Chehab if (si21_writereg(state, LNB_CTRL_REG_1, 4259a0bf528SMauro Carvalho Chehab burst == SEC_MINI_A ? (val & ~0x10) : (val | 0x10))) 4269a0bf528SMauro Carvalho Chehab return -EREMOTEIO; 4279a0bf528SMauro Carvalho Chehab 4289a0bf528SMauro Carvalho Chehab if (si21xx_wait_diseqc_idle(state, 100) < 0) 4299a0bf528SMauro Carvalho Chehab return -ETIMEDOUT; 4309a0bf528SMauro Carvalho Chehab 4319a0bf528SMauro Carvalho Chehab if (si21_writereg(state, LNB_CTRL_REG_1, val)) 4329a0bf528SMauro Carvalho Chehab return -EREMOTEIO; 4339a0bf528SMauro Carvalho Chehab 4349a0bf528SMauro Carvalho Chehab return 0; 4359a0bf528SMauro Carvalho Chehab } 4369a0bf528SMauro Carvalho Chehab /* 30.06.2008 */ 437*0df289a2SMauro Carvalho Chehab static int si21xx_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) 4389a0bf528SMauro Carvalho Chehab { 4399a0bf528SMauro Carvalho Chehab struct si21xx_state *state = fe->demodulator_priv; 4409a0bf528SMauro Carvalho Chehab u8 val; 4419a0bf528SMauro Carvalho Chehab 4429a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 4439a0bf528SMauro Carvalho Chehab val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1)); 4449a0bf528SMauro Carvalho Chehab 4459a0bf528SMauro Carvalho Chehab switch (tone) { 4469a0bf528SMauro Carvalho Chehab case SEC_TONE_ON: 4479a0bf528SMauro Carvalho Chehab return si21_writereg(state, LNB_CTRL_REG_1, val | 0x20); 4489a0bf528SMauro Carvalho Chehab 4499a0bf528SMauro Carvalho Chehab case SEC_TONE_OFF: 4509a0bf528SMauro Carvalho Chehab return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x20)); 4519a0bf528SMauro Carvalho Chehab 4529a0bf528SMauro Carvalho Chehab default: 4539a0bf528SMauro Carvalho Chehab return -EINVAL; 4549a0bf528SMauro Carvalho Chehab } 4559a0bf528SMauro Carvalho Chehab } 4569a0bf528SMauro Carvalho Chehab 457*0df289a2SMauro Carvalho Chehab static int si21xx_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage volt) 4589a0bf528SMauro Carvalho Chehab { 4599a0bf528SMauro Carvalho Chehab struct si21xx_state *state = fe->demodulator_priv; 4609a0bf528SMauro Carvalho Chehab 4619a0bf528SMauro Carvalho Chehab u8 val; 4629a0bf528SMauro Carvalho Chehab dprintk("%s: %s\n", __func__, 4639a0bf528SMauro Carvalho Chehab volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : 4649a0bf528SMauro Carvalho Chehab volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); 4659a0bf528SMauro Carvalho Chehab 4669a0bf528SMauro Carvalho Chehab 4679a0bf528SMauro Carvalho Chehab val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1)); 4689a0bf528SMauro Carvalho Chehab 4699a0bf528SMauro Carvalho Chehab switch (volt) { 4709a0bf528SMauro Carvalho Chehab case SEC_VOLTAGE_18: 4719a0bf528SMauro Carvalho Chehab return si21_writereg(state, LNB_CTRL_REG_1, val | 0x40); 4729a0bf528SMauro Carvalho Chehab break; 4739a0bf528SMauro Carvalho Chehab case SEC_VOLTAGE_13: 4749a0bf528SMauro Carvalho Chehab return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x40)); 4759a0bf528SMauro Carvalho Chehab break; 4769a0bf528SMauro Carvalho Chehab default: 4779a0bf528SMauro Carvalho Chehab return -EINVAL; 478c2c1b415SPeter Senna Tschudin } 4799a0bf528SMauro Carvalho Chehab } 4809a0bf528SMauro Carvalho Chehab 4819a0bf528SMauro Carvalho Chehab static int si21xx_init(struct dvb_frontend *fe) 4829a0bf528SMauro Carvalho Chehab { 4839a0bf528SMauro Carvalho Chehab struct si21xx_state *state = fe->demodulator_priv; 4849a0bf528SMauro Carvalho Chehab int i; 4859a0bf528SMauro Carvalho Chehab int status = 0; 4869a0bf528SMauro Carvalho Chehab u8 reg1; 4879a0bf528SMauro Carvalho Chehab u8 val; 4889a0bf528SMauro Carvalho Chehab u8 reg2[2]; 4899a0bf528SMauro Carvalho Chehab 4909a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 4919a0bf528SMauro Carvalho Chehab 4929a0bf528SMauro Carvalho Chehab for (i = 0; ; i += 2) { 4939a0bf528SMauro Carvalho Chehab reg1 = serit_sp1511lhb_inittab[i]; 4949a0bf528SMauro Carvalho Chehab val = serit_sp1511lhb_inittab[i+1]; 4959a0bf528SMauro Carvalho Chehab if (reg1 == 0xff && val == 0xff) 4969a0bf528SMauro Carvalho Chehab break; 4979a0bf528SMauro Carvalho Chehab si21_writeregs(state, reg1, &val, 1); 4989a0bf528SMauro Carvalho Chehab } 4999a0bf528SMauro Carvalho Chehab 5009a0bf528SMauro Carvalho Chehab /*DVB QPSK SYSTEM MODE REG*/ 5019a0bf528SMauro Carvalho Chehab reg1 = 0x08; 5029a0bf528SMauro Carvalho Chehab si21_writeregs(state, SYSTEM_MODE_REG, ®1, 0x01); 5039a0bf528SMauro Carvalho Chehab 5049a0bf528SMauro Carvalho Chehab /*transport stream config*/ 5059a0bf528SMauro Carvalho Chehab /* 5069a0bf528SMauro Carvalho Chehab mode = PARALLEL; 5079a0bf528SMauro Carvalho Chehab sdata_form = LSB_FIRST; 5089a0bf528SMauro Carvalho Chehab clk_edge = FALLING_EDGE; 5099a0bf528SMauro Carvalho Chehab clk_mode = CLK_GAPPED_MODE; 5109a0bf528SMauro Carvalho Chehab strt_len = BYTE_WIDE; 5119a0bf528SMauro Carvalho Chehab sync_pol = ACTIVE_HIGH; 5129a0bf528SMauro Carvalho Chehab val_pol = ACTIVE_HIGH; 5139a0bf528SMauro Carvalho Chehab err_pol = ACTIVE_HIGH; 5149a0bf528SMauro Carvalho Chehab sclk_rate = 0x00; 5159a0bf528SMauro Carvalho Chehab parity = 0x00 ; 5169a0bf528SMauro Carvalho Chehab data_delay = 0x00; 5179a0bf528SMauro Carvalho Chehab clk_delay = 0x00; 5189a0bf528SMauro Carvalho Chehab pclk_smooth = 0x00; 5199a0bf528SMauro Carvalho Chehab */ 5209a0bf528SMauro Carvalho Chehab reg2[0] = 5219a0bf528SMauro Carvalho Chehab PARALLEL + (LSB_FIRST << 1) 5229a0bf528SMauro Carvalho Chehab + (FALLING_EDGE << 2) + (CLK_GAPPED_MODE << 3) 5239a0bf528SMauro Carvalho Chehab + (BYTE_WIDE << 4) + (ACTIVE_HIGH << 5) 5249a0bf528SMauro Carvalho Chehab + (ACTIVE_HIGH << 6) + (ACTIVE_HIGH << 7); 5259a0bf528SMauro Carvalho Chehab 5269a0bf528SMauro Carvalho Chehab reg2[1] = 0; 5279a0bf528SMauro Carvalho Chehab /* sclk_rate + (parity << 2) 5289a0bf528SMauro Carvalho Chehab + (data_delay << 3) + (clk_delay << 4) 5299a0bf528SMauro Carvalho Chehab + (pclk_smooth << 5); 5309a0bf528SMauro Carvalho Chehab */ 5319a0bf528SMauro Carvalho Chehab status |= si21_writeregs(state, TS_CTRL_REG_1, reg2, 0x02); 5329a0bf528SMauro Carvalho Chehab if (status != 0) 5339a0bf528SMauro Carvalho Chehab dprintk(" %s : TS Set Error\n", __func__); 5349a0bf528SMauro Carvalho Chehab 5359a0bf528SMauro Carvalho Chehab return 0; 5369a0bf528SMauro Carvalho Chehab 5379a0bf528SMauro Carvalho Chehab } 5389a0bf528SMauro Carvalho Chehab 539*0df289a2SMauro Carvalho Chehab static int si21_read_status(struct dvb_frontend *fe, enum fe_status *status) 5409a0bf528SMauro Carvalho Chehab { 5419a0bf528SMauro Carvalho Chehab struct si21xx_state *state = fe->demodulator_priv; 5429a0bf528SMauro Carvalho Chehab u8 regs_read[2]; 5439a0bf528SMauro Carvalho Chehab u8 reg_read; 5449a0bf528SMauro Carvalho Chehab u8 i; 5459a0bf528SMauro Carvalho Chehab u8 lock; 5469a0bf528SMauro Carvalho Chehab u8 signal = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG); 5479a0bf528SMauro Carvalho Chehab 5489a0bf528SMauro Carvalho Chehab si21_readregs(state, LOCK_STATUS_REG_1, regs_read, 0x02); 5499a0bf528SMauro Carvalho Chehab reg_read = 0; 5509a0bf528SMauro Carvalho Chehab 5519a0bf528SMauro Carvalho Chehab for (i = 0; i < 7; ++i) 5529a0bf528SMauro Carvalho Chehab reg_read |= ((regs_read[0] >> i) & 0x01) << (6 - i); 5539a0bf528SMauro Carvalho Chehab 5549a0bf528SMauro Carvalho Chehab lock = ((reg_read & 0x7f) | (regs_read[1] & 0x80)); 5559a0bf528SMauro Carvalho Chehab 5569a0bf528SMauro Carvalho Chehab dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, lock); 5579a0bf528SMauro Carvalho Chehab *status = 0; 5589a0bf528SMauro Carvalho Chehab 5599a0bf528SMauro Carvalho Chehab if (signal > 10) 5609a0bf528SMauro Carvalho Chehab *status |= FE_HAS_SIGNAL; 5619a0bf528SMauro Carvalho Chehab 5629a0bf528SMauro Carvalho Chehab if (lock & 0x2) 5639a0bf528SMauro Carvalho Chehab *status |= FE_HAS_CARRIER; 5649a0bf528SMauro Carvalho Chehab 5659a0bf528SMauro Carvalho Chehab if (lock & 0x20) 5669a0bf528SMauro Carvalho Chehab *status |= FE_HAS_VITERBI; 5679a0bf528SMauro Carvalho Chehab 5689a0bf528SMauro Carvalho Chehab if (lock & 0x40) 5699a0bf528SMauro Carvalho Chehab *status |= FE_HAS_SYNC; 5709a0bf528SMauro Carvalho Chehab 5719a0bf528SMauro Carvalho Chehab if ((lock & 0x7b) == 0x7b) 5729a0bf528SMauro Carvalho Chehab *status |= FE_HAS_LOCK; 5739a0bf528SMauro Carvalho Chehab 5749a0bf528SMauro Carvalho Chehab return 0; 5759a0bf528SMauro Carvalho Chehab } 5769a0bf528SMauro Carvalho Chehab 5779a0bf528SMauro Carvalho Chehab static int si21_read_signal_strength(struct dvb_frontend *fe, u16 *strength) 5789a0bf528SMauro Carvalho Chehab { 5799a0bf528SMauro Carvalho Chehab struct si21xx_state *state = fe->demodulator_priv; 5809a0bf528SMauro Carvalho Chehab 5819a0bf528SMauro Carvalho Chehab /*status = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG, 5829a0bf528SMauro Carvalho Chehab (u8*)agclevel, 0x01);*/ 5839a0bf528SMauro Carvalho Chehab 5849a0bf528SMauro Carvalho Chehab u16 signal = (3 * si21_readreg(state, 0x27) * 5859a0bf528SMauro Carvalho Chehab si21_readreg(state, 0x28)); 5869a0bf528SMauro Carvalho Chehab 5879a0bf528SMauro Carvalho Chehab dprintk("%s : AGCPWR: 0x%02x%02x, signal=0x%04x\n", __func__, 5889a0bf528SMauro Carvalho Chehab si21_readreg(state, 0x27), 5899a0bf528SMauro Carvalho Chehab si21_readreg(state, 0x28), (int) signal); 5909a0bf528SMauro Carvalho Chehab 5919a0bf528SMauro Carvalho Chehab signal <<= 4; 5929a0bf528SMauro Carvalho Chehab *strength = signal; 5939a0bf528SMauro Carvalho Chehab 5949a0bf528SMauro Carvalho Chehab return 0; 5959a0bf528SMauro Carvalho Chehab } 5969a0bf528SMauro Carvalho Chehab 5979a0bf528SMauro Carvalho Chehab static int si21_read_ber(struct dvb_frontend *fe, u32 *ber) 5989a0bf528SMauro Carvalho Chehab { 5999a0bf528SMauro Carvalho Chehab struct si21xx_state *state = fe->demodulator_priv; 6009a0bf528SMauro Carvalho Chehab 6019a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 6029a0bf528SMauro Carvalho Chehab 6039a0bf528SMauro Carvalho Chehab if (state->errmode != STATUS_BER) 6049a0bf528SMauro Carvalho Chehab return 0; 6059a0bf528SMauro Carvalho Chehab 6069a0bf528SMauro Carvalho Chehab *ber = (si21_readreg(state, 0x1d) << 8) | 6079a0bf528SMauro Carvalho Chehab si21_readreg(state, 0x1e); 6089a0bf528SMauro Carvalho Chehab 6099a0bf528SMauro Carvalho Chehab return 0; 6109a0bf528SMauro Carvalho Chehab } 6119a0bf528SMauro Carvalho Chehab 6129a0bf528SMauro Carvalho Chehab static int si21_read_snr(struct dvb_frontend *fe, u16 *snr) 6139a0bf528SMauro Carvalho Chehab { 6149a0bf528SMauro Carvalho Chehab struct si21xx_state *state = fe->demodulator_priv; 6159a0bf528SMauro Carvalho Chehab 6169a0bf528SMauro Carvalho Chehab s32 xsnr = 0xffff - ((si21_readreg(state, 0x24) << 8) | 6179a0bf528SMauro Carvalho Chehab si21_readreg(state, 0x25)); 6189a0bf528SMauro Carvalho Chehab xsnr = 3 * (xsnr - 0xa100); 6199a0bf528SMauro Carvalho Chehab *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr; 6209a0bf528SMauro Carvalho Chehab 6219a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 6229a0bf528SMauro Carvalho Chehab 6239a0bf528SMauro Carvalho Chehab return 0; 6249a0bf528SMauro Carvalho Chehab } 6259a0bf528SMauro Carvalho Chehab 6269a0bf528SMauro Carvalho Chehab static int si21_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) 6279a0bf528SMauro Carvalho Chehab { 6289a0bf528SMauro Carvalho Chehab struct si21xx_state *state = fe->demodulator_priv; 6299a0bf528SMauro Carvalho Chehab 6309a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 6319a0bf528SMauro Carvalho Chehab 6329a0bf528SMauro Carvalho Chehab if (state->errmode != STATUS_UCBLOCKS) 6339a0bf528SMauro Carvalho Chehab *ucblocks = 0; 6349a0bf528SMauro Carvalho Chehab else 6359a0bf528SMauro Carvalho Chehab *ucblocks = (si21_readreg(state, 0x1d) << 8) | 6369a0bf528SMauro Carvalho Chehab si21_readreg(state, 0x1e); 6379a0bf528SMauro Carvalho Chehab 6389a0bf528SMauro Carvalho Chehab return 0; 6399a0bf528SMauro Carvalho Chehab } 6409a0bf528SMauro Carvalho Chehab 6419a0bf528SMauro Carvalho Chehab /* initiates a channel acquisition sequence 6429a0bf528SMauro Carvalho Chehab using the specified symbol rate and code rate */ 6439a0bf528SMauro Carvalho Chehab static int si21xx_setacquire(struct dvb_frontend *fe, int symbrate, 644*0df289a2SMauro Carvalho Chehab enum fe_code_rate crate) 6459a0bf528SMauro Carvalho Chehab { 6469a0bf528SMauro Carvalho Chehab 6479a0bf528SMauro Carvalho Chehab struct si21xx_state *state = fe->demodulator_priv; 6489a0bf528SMauro Carvalho Chehab u8 coderates[] = { 6499a0bf528SMauro Carvalho Chehab 0x0, 0x01, 0x02, 0x04, 0x00, 6509a0bf528SMauro Carvalho Chehab 0x8, 0x10, 0x20, 0x00, 0x3f 6519a0bf528SMauro Carvalho Chehab }; 6529a0bf528SMauro Carvalho Chehab 6539a0bf528SMauro Carvalho Chehab u8 coderate_ptr; 6549a0bf528SMauro Carvalho Chehab int status; 6559a0bf528SMauro Carvalho Chehab u8 start_acq = 0x80; 6569a0bf528SMauro Carvalho Chehab u8 reg, regs[3]; 6579a0bf528SMauro Carvalho Chehab 6589a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 6599a0bf528SMauro Carvalho Chehab 6609a0bf528SMauro Carvalho Chehab status = PASS; 6619a0bf528SMauro Carvalho Chehab coderate_ptr = coderates[crate]; 6629a0bf528SMauro Carvalho Chehab 6639a0bf528SMauro Carvalho Chehab si21xx_set_symbolrate(fe, symbrate); 6649a0bf528SMauro Carvalho Chehab 6659a0bf528SMauro Carvalho Chehab /* write code rates to use in the Viterbi search */ 6669a0bf528SMauro Carvalho Chehab status |= si21_writeregs(state, 6679a0bf528SMauro Carvalho Chehab VIT_SRCH_CTRL_REG_1, 6689a0bf528SMauro Carvalho Chehab &coderate_ptr, 0x01); 6699a0bf528SMauro Carvalho Chehab 6709a0bf528SMauro Carvalho Chehab /* clear acq_start bit */ 6719a0bf528SMauro Carvalho Chehab status |= si21_readregs(state, ACQ_CTRL_REG_2, ®, 0x01); 6729a0bf528SMauro Carvalho Chehab reg &= ~start_acq; 6739a0bf528SMauro Carvalho Chehab status |= si21_writeregs(state, ACQ_CTRL_REG_2, ®, 0x01); 6749a0bf528SMauro Carvalho Chehab 6759a0bf528SMauro Carvalho Chehab /* use new Carrier Frequency Offset Estimator (QuickLock) */ 6769a0bf528SMauro Carvalho Chehab regs[0] = 0xCB; 6779a0bf528SMauro Carvalho Chehab regs[1] = 0x40; 6789a0bf528SMauro Carvalho Chehab regs[2] = 0xCB; 6799a0bf528SMauro Carvalho Chehab 6809a0bf528SMauro Carvalho Chehab status |= si21_writeregs(state, 6819a0bf528SMauro Carvalho Chehab TWO_DB_BNDWDTH_THRSHLD_REG, 6829a0bf528SMauro Carvalho Chehab ®s[0], 0x03); 6839a0bf528SMauro Carvalho Chehab reg = 0x56; 6849a0bf528SMauro Carvalho Chehab status |= si21_writeregs(state, 6859a0bf528SMauro Carvalho Chehab LSA_CTRL_REG_1, ®, 1); 6869a0bf528SMauro Carvalho Chehab reg = 0x05; 6879a0bf528SMauro Carvalho Chehab status |= si21_writeregs(state, 6889a0bf528SMauro Carvalho Chehab BLIND_SCAN_CTRL_REG, ®, 1); 6899a0bf528SMauro Carvalho Chehab /* start automatic acq */ 6909a0bf528SMauro Carvalho Chehab status |= si21_writeregs(state, 6919a0bf528SMauro Carvalho Chehab ACQ_CTRL_REG_2, &start_acq, 0x01); 6929a0bf528SMauro Carvalho Chehab 6939a0bf528SMauro Carvalho Chehab return status; 6949a0bf528SMauro Carvalho Chehab } 6959a0bf528SMauro Carvalho Chehab 6969a0bf528SMauro Carvalho Chehab static int si21xx_set_frontend(struct dvb_frontend *fe) 6979a0bf528SMauro Carvalho Chehab { 6989a0bf528SMauro Carvalho Chehab struct si21xx_state *state = fe->demodulator_priv; 6999a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *c = &fe->dtv_property_cache; 7009a0bf528SMauro Carvalho Chehab 7019a0bf528SMauro Carvalho Chehab /* freq Channel carrier frequency in KHz (i.e. 1550000 KHz) 7029a0bf528SMauro Carvalho Chehab datarate Channel symbol rate in Sps (i.e. 22500000 Sps)*/ 7039a0bf528SMauro Carvalho Chehab 7049a0bf528SMauro Carvalho Chehab /* in MHz */ 7059a0bf528SMauro Carvalho Chehab unsigned char coarse_tune_freq; 7069a0bf528SMauro Carvalho Chehab int fine_tune_freq; 7079a0bf528SMauro Carvalho Chehab unsigned char sample_rate = 0; 7089a0bf528SMauro Carvalho Chehab /* boolean */ 7099a0bf528SMauro Carvalho Chehab bool inband_interferer_ind; 7109a0bf528SMauro Carvalho Chehab 7119a0bf528SMauro Carvalho Chehab /* INTERMEDIATE VALUES */ 7129a0bf528SMauro Carvalho Chehab int icoarse_tune_freq; /* MHz */ 7139a0bf528SMauro Carvalho Chehab int ifine_tune_freq; /* MHz */ 7149a0bf528SMauro Carvalho Chehab unsigned int band_high; 7159a0bf528SMauro Carvalho Chehab unsigned int band_low; 7169a0bf528SMauro Carvalho Chehab unsigned int x1; 7179a0bf528SMauro Carvalho Chehab unsigned int x2; 7189a0bf528SMauro Carvalho Chehab int i; 7199a0bf528SMauro Carvalho Chehab bool inband_interferer_div2[ALLOWABLE_FS_COUNT]; 7209a0bf528SMauro Carvalho Chehab bool inband_interferer_div4[ALLOWABLE_FS_COUNT]; 7219a0bf528SMauro Carvalho Chehab int status; 7229a0bf528SMauro Carvalho Chehab 7239a0bf528SMauro Carvalho Chehab /* allowable sample rates for ADC in MHz */ 7249a0bf528SMauro Carvalho Chehab int afs[ALLOWABLE_FS_COUNT] = { 200, 192, 193, 194, 195, 7259a0bf528SMauro Carvalho Chehab 196, 204, 205, 206, 207 7269a0bf528SMauro Carvalho Chehab }; 7279a0bf528SMauro Carvalho Chehab /* in MHz */ 7289a0bf528SMauro Carvalho Chehab int if_limit_high; 7299a0bf528SMauro Carvalho Chehab int if_limit_low; 7309a0bf528SMauro Carvalho Chehab int lnb_lo; 7319a0bf528SMauro Carvalho Chehab int lnb_uncertanity; 7329a0bf528SMauro Carvalho Chehab 7339a0bf528SMauro Carvalho Chehab int rf_freq; 7349a0bf528SMauro Carvalho Chehab int data_rate; 7359a0bf528SMauro Carvalho Chehab unsigned char regs[4]; 7369a0bf528SMauro Carvalho Chehab 7379a0bf528SMauro Carvalho Chehab dprintk("%s : FE_SET_FRONTEND\n", __func__); 7389a0bf528SMauro Carvalho Chehab 7399a0bf528SMauro Carvalho Chehab if (c->delivery_system != SYS_DVBS) { 7409a0bf528SMauro Carvalho Chehab dprintk("%s: unsupported delivery system selected (%d)\n", 7419a0bf528SMauro Carvalho Chehab __func__, c->delivery_system); 7429a0bf528SMauro Carvalho Chehab return -EOPNOTSUPP; 7439a0bf528SMauro Carvalho Chehab } 7449a0bf528SMauro Carvalho Chehab 7459a0bf528SMauro Carvalho Chehab for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) 7469a0bf528SMauro Carvalho Chehab inband_interferer_div2[i] = inband_interferer_div4[i] = false; 7479a0bf528SMauro Carvalho Chehab 7489a0bf528SMauro Carvalho Chehab if_limit_high = -700000; 7499a0bf528SMauro Carvalho Chehab if_limit_low = -100000; 7509a0bf528SMauro Carvalho Chehab /* in MHz */ 7519a0bf528SMauro Carvalho Chehab lnb_lo = 0; 7529a0bf528SMauro Carvalho Chehab lnb_uncertanity = 0; 7539a0bf528SMauro Carvalho Chehab 7549a0bf528SMauro Carvalho Chehab rf_freq = 10 * c->frequency ; 7559a0bf528SMauro Carvalho Chehab data_rate = c->symbol_rate / 100; 7569a0bf528SMauro Carvalho Chehab 7579a0bf528SMauro Carvalho Chehab status = PASS; 7589a0bf528SMauro Carvalho Chehab 7599a0bf528SMauro Carvalho Chehab band_low = (rf_freq - lnb_lo) - ((lnb_uncertanity * 200) 7609a0bf528SMauro Carvalho Chehab + (data_rate * 135)) / 200; 7619a0bf528SMauro Carvalho Chehab 7629a0bf528SMauro Carvalho Chehab band_high = (rf_freq - lnb_lo) + ((lnb_uncertanity * 200) 7639a0bf528SMauro Carvalho Chehab + (data_rate * 135)) / 200; 7649a0bf528SMauro Carvalho Chehab 7659a0bf528SMauro Carvalho Chehab 7669a0bf528SMauro Carvalho Chehab icoarse_tune_freq = 100000 * 7679a0bf528SMauro Carvalho Chehab (((rf_freq - lnb_lo) - 7689a0bf528SMauro Carvalho Chehab (if_limit_low + if_limit_high) / 2) 7699a0bf528SMauro Carvalho Chehab / 100000); 7709a0bf528SMauro Carvalho Chehab 7719a0bf528SMauro Carvalho Chehab ifine_tune_freq = (rf_freq - lnb_lo) - icoarse_tune_freq ; 7729a0bf528SMauro Carvalho Chehab 7739a0bf528SMauro Carvalho Chehab for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { 7749a0bf528SMauro Carvalho Chehab x1 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) * 7759a0bf528SMauro Carvalho Chehab (afs[i] * 2500) + afs[i] * 2500; 7769a0bf528SMauro Carvalho Chehab 7779a0bf528SMauro Carvalho Chehab x2 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) * 7789a0bf528SMauro Carvalho Chehab (afs[i] * 2500); 7799a0bf528SMauro Carvalho Chehab 7809a0bf528SMauro Carvalho Chehab if (((band_low < x1) && (x1 < band_high)) || 7819a0bf528SMauro Carvalho Chehab ((band_low < x2) && (x2 < band_high))) 7829a0bf528SMauro Carvalho Chehab inband_interferer_div4[i] = true; 7839a0bf528SMauro Carvalho Chehab 7849a0bf528SMauro Carvalho Chehab } 7859a0bf528SMauro Carvalho Chehab 7869a0bf528SMauro Carvalho Chehab for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { 7879a0bf528SMauro Carvalho Chehab x1 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) * 7889a0bf528SMauro Carvalho Chehab (afs[i] * 5000) + afs[i] * 5000; 7899a0bf528SMauro Carvalho Chehab 7909a0bf528SMauro Carvalho Chehab x2 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) * 7919a0bf528SMauro Carvalho Chehab (afs[i] * 5000); 7929a0bf528SMauro Carvalho Chehab 7939a0bf528SMauro Carvalho Chehab if (((band_low < x1) && (x1 < band_high)) || 7949a0bf528SMauro Carvalho Chehab ((band_low < x2) && (x2 < band_high))) 7959a0bf528SMauro Carvalho Chehab inband_interferer_div2[i] = true; 7969a0bf528SMauro Carvalho Chehab } 7979a0bf528SMauro Carvalho Chehab 7989a0bf528SMauro Carvalho Chehab inband_interferer_ind = true; 7999a0bf528SMauro Carvalho Chehab for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { 8009a0bf528SMauro Carvalho Chehab if (inband_interferer_div2[i] || inband_interferer_div4[i]) { 8019a0bf528SMauro Carvalho Chehab inband_interferer_ind = false; 8029a0bf528SMauro Carvalho Chehab break; 8039a0bf528SMauro Carvalho Chehab } 8049a0bf528SMauro Carvalho Chehab } 8059a0bf528SMauro Carvalho Chehab 8069a0bf528SMauro Carvalho Chehab if (inband_interferer_ind) { 8079a0bf528SMauro Carvalho Chehab for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { 8089a0bf528SMauro Carvalho Chehab if (!inband_interferer_div2[i]) { 8099a0bf528SMauro Carvalho Chehab sample_rate = (u8) afs[i]; 8109a0bf528SMauro Carvalho Chehab break; 8119a0bf528SMauro Carvalho Chehab } 8129a0bf528SMauro Carvalho Chehab } 8139a0bf528SMauro Carvalho Chehab } else { 8149a0bf528SMauro Carvalho Chehab for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { 8159a0bf528SMauro Carvalho Chehab if ((inband_interferer_div2[i] || 8169a0bf528SMauro Carvalho Chehab !inband_interferer_div4[i])) { 8179a0bf528SMauro Carvalho Chehab sample_rate = (u8) afs[i]; 8189a0bf528SMauro Carvalho Chehab break; 8199a0bf528SMauro Carvalho Chehab } 8209a0bf528SMauro Carvalho Chehab } 8219a0bf528SMauro Carvalho Chehab 8229a0bf528SMauro Carvalho Chehab } 8239a0bf528SMauro Carvalho Chehab 8249a0bf528SMauro Carvalho Chehab if (sample_rate > 207 || sample_rate < 192) 8259a0bf528SMauro Carvalho Chehab sample_rate = 200; 8269a0bf528SMauro Carvalho Chehab 8279a0bf528SMauro Carvalho Chehab fine_tune_freq = ((0x4000 * (ifine_tune_freq / 10)) / 8289a0bf528SMauro Carvalho Chehab ((sample_rate) * 1000)); 8299a0bf528SMauro Carvalho Chehab 8309a0bf528SMauro Carvalho Chehab coarse_tune_freq = (u8)(icoarse_tune_freq / 100000); 8319a0bf528SMauro Carvalho Chehab 8329a0bf528SMauro Carvalho Chehab regs[0] = sample_rate; 8339a0bf528SMauro Carvalho Chehab regs[1] = coarse_tune_freq; 8349a0bf528SMauro Carvalho Chehab regs[2] = fine_tune_freq & 0xFF; 8359a0bf528SMauro Carvalho Chehab regs[3] = fine_tune_freq >> 8 & 0xFF; 8369a0bf528SMauro Carvalho Chehab 8379a0bf528SMauro Carvalho Chehab status |= si21_writeregs(state, PLL_DIVISOR_REG, ®s[0], 0x04); 8389a0bf528SMauro Carvalho Chehab 8399a0bf528SMauro Carvalho Chehab state->fs = sample_rate;/*ADC MHz*/ 8409a0bf528SMauro Carvalho Chehab si21xx_setacquire(fe, c->symbol_rate, c->fec_inner); 8419a0bf528SMauro Carvalho Chehab 8429a0bf528SMauro Carvalho Chehab return 0; 8439a0bf528SMauro Carvalho Chehab } 8449a0bf528SMauro Carvalho Chehab 8459a0bf528SMauro Carvalho Chehab static int si21xx_sleep(struct dvb_frontend *fe) 8469a0bf528SMauro Carvalho Chehab { 8479a0bf528SMauro Carvalho Chehab struct si21xx_state *state = fe->demodulator_priv; 8489a0bf528SMauro Carvalho Chehab u8 regdata; 8499a0bf528SMauro Carvalho Chehab 8509a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 8519a0bf528SMauro Carvalho Chehab 8529a0bf528SMauro Carvalho Chehab si21_readregs(state, SYSTEM_MODE_REG, ®data, 0x01); 8539a0bf528SMauro Carvalho Chehab regdata |= 1 << 6; 8549a0bf528SMauro Carvalho Chehab si21_writeregs(state, SYSTEM_MODE_REG, ®data, 0x01); 8559a0bf528SMauro Carvalho Chehab state->initialised = 0; 8569a0bf528SMauro Carvalho Chehab 8579a0bf528SMauro Carvalho Chehab return 0; 8589a0bf528SMauro Carvalho Chehab } 8599a0bf528SMauro Carvalho Chehab 8609a0bf528SMauro Carvalho Chehab static void si21xx_release(struct dvb_frontend *fe) 8619a0bf528SMauro Carvalho Chehab { 8629a0bf528SMauro Carvalho Chehab struct si21xx_state *state = fe->demodulator_priv; 8639a0bf528SMauro Carvalho Chehab 8649a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 8659a0bf528SMauro Carvalho Chehab 8669a0bf528SMauro Carvalho Chehab kfree(state); 8679a0bf528SMauro Carvalho Chehab } 8689a0bf528SMauro Carvalho Chehab 8699a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops si21xx_ops = { 8709a0bf528SMauro Carvalho Chehab .delsys = { SYS_DVBS }, 8719a0bf528SMauro Carvalho Chehab .info = { 8729a0bf528SMauro Carvalho Chehab .name = "SL SI21XX DVB-S", 8739a0bf528SMauro Carvalho Chehab .frequency_min = 950000, 8749a0bf528SMauro Carvalho Chehab .frequency_max = 2150000, 8759a0bf528SMauro Carvalho Chehab .frequency_stepsize = 125, /* kHz for QPSK frontends */ 8769a0bf528SMauro Carvalho Chehab .frequency_tolerance = 0, 8779a0bf528SMauro Carvalho Chehab .symbol_rate_min = 1000000, 8789a0bf528SMauro Carvalho Chehab .symbol_rate_max = 45000000, 8799a0bf528SMauro Carvalho Chehab .symbol_rate_tolerance = 500, /* ppm */ 8809a0bf528SMauro Carvalho Chehab .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 8819a0bf528SMauro Carvalho Chehab FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | 8829a0bf528SMauro Carvalho Chehab FE_CAN_QPSK | 8839a0bf528SMauro Carvalho Chehab FE_CAN_FEC_AUTO 8849a0bf528SMauro Carvalho Chehab }, 8859a0bf528SMauro Carvalho Chehab 8869a0bf528SMauro Carvalho Chehab .release = si21xx_release, 8879a0bf528SMauro Carvalho Chehab .init = si21xx_init, 8889a0bf528SMauro Carvalho Chehab .sleep = si21xx_sleep, 8899a0bf528SMauro Carvalho Chehab .write = si21_write, 8909a0bf528SMauro Carvalho Chehab .read_status = si21_read_status, 8919a0bf528SMauro Carvalho Chehab .read_ber = si21_read_ber, 8929a0bf528SMauro Carvalho Chehab .read_signal_strength = si21_read_signal_strength, 8939a0bf528SMauro Carvalho Chehab .read_snr = si21_read_snr, 8949a0bf528SMauro Carvalho Chehab .read_ucblocks = si21_read_ucblocks, 8959a0bf528SMauro Carvalho Chehab .diseqc_send_master_cmd = si21xx_send_diseqc_msg, 8969a0bf528SMauro Carvalho Chehab .diseqc_send_burst = si21xx_send_diseqc_burst, 8979a0bf528SMauro Carvalho Chehab .set_tone = si21xx_set_tone, 8989a0bf528SMauro Carvalho Chehab .set_voltage = si21xx_set_voltage, 8999a0bf528SMauro Carvalho Chehab 9009a0bf528SMauro Carvalho Chehab .set_frontend = si21xx_set_frontend, 9019a0bf528SMauro Carvalho Chehab }; 9029a0bf528SMauro Carvalho Chehab 9039a0bf528SMauro Carvalho Chehab struct dvb_frontend *si21xx_attach(const struct si21xx_config *config, 9049a0bf528SMauro Carvalho Chehab struct i2c_adapter *i2c) 9059a0bf528SMauro Carvalho Chehab { 9069a0bf528SMauro Carvalho Chehab struct si21xx_state *state = NULL; 9079a0bf528SMauro Carvalho Chehab int id; 9089a0bf528SMauro Carvalho Chehab 9099a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 9109a0bf528SMauro Carvalho Chehab 9119a0bf528SMauro Carvalho Chehab /* allocate memory for the internal state */ 9129a0bf528SMauro Carvalho Chehab state = kzalloc(sizeof(struct si21xx_state), GFP_KERNEL); 9139a0bf528SMauro Carvalho Chehab if (state == NULL) 9149a0bf528SMauro Carvalho Chehab goto error; 9159a0bf528SMauro Carvalho Chehab 9169a0bf528SMauro Carvalho Chehab /* setup the state */ 9179a0bf528SMauro Carvalho Chehab state->config = config; 9189a0bf528SMauro Carvalho Chehab state->i2c = i2c; 9199a0bf528SMauro Carvalho Chehab state->initialised = 0; 9209a0bf528SMauro Carvalho Chehab state->errmode = STATUS_BER; 9219a0bf528SMauro Carvalho Chehab 9229a0bf528SMauro Carvalho Chehab /* check if the demod is there */ 9239a0bf528SMauro Carvalho Chehab id = si21_readreg(state, SYSTEM_MODE_REG); 9249a0bf528SMauro Carvalho Chehab si21_writereg(state, SYSTEM_MODE_REG, id | 0x40); /* standby off */ 9259a0bf528SMauro Carvalho Chehab msleep(200); 9269a0bf528SMauro Carvalho Chehab id = si21_readreg(state, 0x00); 9279a0bf528SMauro Carvalho Chehab 9289a0bf528SMauro Carvalho Chehab /* register 0x00 contains: 9299a0bf528SMauro Carvalho Chehab 0x34 for SI2107 9309a0bf528SMauro Carvalho Chehab 0x24 for SI2108 9319a0bf528SMauro Carvalho Chehab 0x14 for SI2109 9329a0bf528SMauro Carvalho Chehab 0x04 for SI2110 9339a0bf528SMauro Carvalho Chehab */ 9349a0bf528SMauro Carvalho Chehab if (id != 0x04 && id != 0x14) 9359a0bf528SMauro Carvalho Chehab goto error; 9369a0bf528SMauro Carvalho Chehab 9379a0bf528SMauro Carvalho Chehab /* create dvb_frontend */ 9389a0bf528SMauro Carvalho Chehab memcpy(&state->frontend.ops, &si21xx_ops, 9399a0bf528SMauro Carvalho Chehab sizeof(struct dvb_frontend_ops)); 9409a0bf528SMauro Carvalho Chehab state->frontend.demodulator_priv = state; 9419a0bf528SMauro Carvalho Chehab return &state->frontend; 9429a0bf528SMauro Carvalho Chehab 9439a0bf528SMauro Carvalho Chehab error: 9449a0bf528SMauro Carvalho Chehab kfree(state); 9459a0bf528SMauro Carvalho Chehab return NULL; 9469a0bf528SMauro Carvalho Chehab } 9479a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(si21xx_attach); 9489a0bf528SMauro Carvalho Chehab 9499a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644); 9509a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); 9519a0bf528SMauro Carvalho Chehab 9529a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("SL SI21XX DVB Demodulator driver"); 9539a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Igor M. Liplianin"); 9549a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 955