19a0bf528SMauro Carvalho Chehab /* 29a0bf528SMauro Carvalho Chehab cx24110 - Single Chip Satellite Channel Receiver driver module 39a0bf528SMauro Carvalho Chehab 49a0bf528SMauro Carvalho Chehab Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de> based on 59a0bf528SMauro Carvalho Chehab work 69a0bf528SMauro Carvalho Chehab Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> 79a0bf528SMauro Carvalho Chehab 89a0bf528SMauro Carvalho Chehab This program is free software; you can redistribute it and/or modify 99a0bf528SMauro Carvalho Chehab it under the terms of the GNU General Public License as published by 109a0bf528SMauro Carvalho Chehab the Free Software Foundation; either version 2 of the License, or 119a0bf528SMauro Carvalho Chehab (at your option) any later version. 129a0bf528SMauro Carvalho Chehab 139a0bf528SMauro Carvalho Chehab This program is distributed in the hope that it will be useful, 149a0bf528SMauro Carvalho Chehab but WITHOUT ANY WARRANTY; without even the implied warranty of 159a0bf528SMauro Carvalho Chehab MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 169a0bf528SMauro Carvalho Chehab 179a0bf528SMauro Carvalho Chehab GNU General Public License for more details. 189a0bf528SMauro Carvalho Chehab 199a0bf528SMauro Carvalho Chehab You should have received a copy of the GNU General Public License 209a0bf528SMauro Carvalho Chehab along with this program; if not, write to the Free Software 219a0bf528SMauro Carvalho Chehab Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 229a0bf528SMauro Carvalho Chehab 239a0bf528SMauro Carvalho Chehab */ 249a0bf528SMauro Carvalho Chehab 259a0bf528SMauro Carvalho Chehab #include <linux/slab.h> 269a0bf528SMauro Carvalho Chehab #include <linux/kernel.h> 279a0bf528SMauro Carvalho Chehab #include <linux/module.h> 289a0bf528SMauro Carvalho Chehab #include <linux/init.h> 299a0bf528SMauro Carvalho Chehab 309a0bf528SMauro Carvalho Chehab #include "dvb_frontend.h" 319a0bf528SMauro Carvalho Chehab #include "cx24110.h" 329a0bf528SMauro Carvalho Chehab 339a0bf528SMauro Carvalho Chehab 349a0bf528SMauro Carvalho Chehab struct cx24110_state { 359a0bf528SMauro Carvalho Chehab 369a0bf528SMauro Carvalho Chehab struct i2c_adapter* i2c; 379a0bf528SMauro Carvalho Chehab 389a0bf528SMauro Carvalho Chehab const struct cx24110_config* config; 399a0bf528SMauro Carvalho Chehab 409a0bf528SMauro Carvalho Chehab struct dvb_frontend frontend; 419a0bf528SMauro Carvalho Chehab 429a0bf528SMauro Carvalho Chehab u32 lastber; 439a0bf528SMauro Carvalho Chehab u32 lastbler; 449a0bf528SMauro Carvalho Chehab u32 lastesn0; 459a0bf528SMauro Carvalho Chehab }; 469a0bf528SMauro Carvalho Chehab 479a0bf528SMauro Carvalho Chehab static int debug; 489a0bf528SMauro Carvalho Chehab #define dprintk(args...) \ 499a0bf528SMauro Carvalho Chehab do { \ 509a0bf528SMauro Carvalho Chehab if (debug) printk(KERN_DEBUG "cx24110: " args); \ 519a0bf528SMauro Carvalho Chehab } while (0) 529a0bf528SMauro Carvalho Chehab 539a0bf528SMauro Carvalho Chehab static struct {u8 reg; u8 data;} cx24110_regdata[]= 549a0bf528SMauro Carvalho Chehab /* Comments beginning with @ denote this value should 559a0bf528SMauro Carvalho Chehab be the default */ 569a0bf528SMauro Carvalho Chehab {{0x09,0x01}, /* SoftResetAll */ 579a0bf528SMauro Carvalho Chehab {0x09,0x00}, /* release reset */ 589a0bf528SMauro Carvalho Chehab {0x01,0xe8}, /* MSB of code rate 27.5MS/s */ 599a0bf528SMauro Carvalho Chehab {0x02,0x17}, /* middle byte " */ 609a0bf528SMauro Carvalho Chehab {0x03,0x29}, /* LSB " */ 619a0bf528SMauro Carvalho Chehab {0x05,0x03}, /* @ DVB mode, standard code rate 3/4 */ 629a0bf528SMauro Carvalho Chehab {0x06,0xa5}, /* @ PLL 60MHz */ 639a0bf528SMauro Carvalho Chehab {0x07,0x01}, /* @ Fclk, i.e. sampling clock, 60MHz */ 649a0bf528SMauro Carvalho Chehab {0x0a,0x00}, /* @ partial chip disables, do not set */ 659a0bf528SMauro Carvalho Chehab {0x0b,0x01}, /* set output clock in gapped mode, start signal low 669a0bf528SMauro Carvalho Chehab active for first byte */ 679a0bf528SMauro Carvalho Chehab {0x0c,0x11}, /* no parity bytes, large hold time, serial data out */ 689a0bf528SMauro Carvalho Chehab {0x0d,0x6f}, /* @ RS Sync/Unsync thresholds */ 699a0bf528SMauro Carvalho Chehab {0x10,0x40}, /* chip doc is misleading here: write bit 6 as 1 709a0bf528SMauro Carvalho Chehab to avoid starting the BER counter. Reset the 719a0bf528SMauro Carvalho Chehab CRC test bit. Finite counting selected */ 729a0bf528SMauro Carvalho Chehab {0x15,0xff}, /* @ size of the limited time window for RS BER 739a0bf528SMauro Carvalho Chehab estimation. It is <value>*256 RS blocks, this 749a0bf528SMauro Carvalho Chehab gives approx. 2.6 sec at 27.5MS/s, rate 3/4 */ 759a0bf528SMauro Carvalho Chehab {0x16,0x00}, /* @ enable all RS output ports */ 769a0bf528SMauro Carvalho Chehab {0x17,0x04}, /* @ time window allowed for the RS to sync */ 779a0bf528SMauro Carvalho Chehab {0x18,0xae}, /* @ allow all standard DVB code rates to be scanned 789a0bf528SMauro Carvalho Chehab for automatically */ 799a0bf528SMauro Carvalho Chehab /* leave the current code rate and normalization 809a0bf528SMauro Carvalho Chehab registers as they are after reset... */ 819a0bf528SMauro Carvalho Chehab {0x21,0x10}, /* @ during AutoAcq, search each viterbi setting 829a0bf528SMauro Carvalho Chehab only once */ 839a0bf528SMauro Carvalho Chehab {0x23,0x18}, /* @ size of the limited time window for Viterbi BER 849a0bf528SMauro Carvalho Chehab estimation. It is <value>*65536 channel bits, i.e. 859a0bf528SMauro Carvalho Chehab approx. 38ms at 27.5MS/s, rate 3/4 */ 869a0bf528SMauro Carvalho Chehab {0x24,0x24}, /* do not trigger Viterbi CRC test. Finite count window */ 879a0bf528SMauro Carvalho Chehab /* leave front-end AGC parameters at default values */ 889a0bf528SMauro Carvalho Chehab /* leave decimation AGC parameters at default values */ 899a0bf528SMauro Carvalho Chehab {0x35,0x40}, /* disable all interrupts. They are not connected anyway */ 909a0bf528SMauro Carvalho Chehab {0x36,0xff}, /* clear all interrupt pending flags */ 919a0bf528SMauro Carvalho Chehab {0x37,0x00}, /* @ fully enable AutoAcqq state machine */ 929a0bf528SMauro Carvalho Chehab {0x38,0x07}, /* @ enable fade recovery, but not autostart AutoAcq */ 939a0bf528SMauro Carvalho Chehab /* leave the equalizer parameters on their default values */ 949a0bf528SMauro Carvalho Chehab /* leave the final AGC parameters on their default values */ 959a0bf528SMauro Carvalho Chehab {0x41,0x00}, /* @ MSB of front-end derotator frequency */ 969a0bf528SMauro Carvalho Chehab {0x42,0x00}, /* @ middle bytes " */ 979a0bf528SMauro Carvalho Chehab {0x43,0x00}, /* @ LSB " */ 989a0bf528SMauro Carvalho Chehab /* leave the carrier tracking loop parameters on default */ 999a0bf528SMauro Carvalho Chehab /* leave the bit timing loop parameters at default */ 1009a0bf528SMauro Carvalho Chehab {0x56,0x4d}, /* set the filtune voltage to 2.7V, as recommended by */ 1019a0bf528SMauro Carvalho Chehab /* the cx24108 data sheet for symbol rates above 15MS/s */ 1029a0bf528SMauro Carvalho Chehab {0x57,0x00}, /* @ Filter sigma delta enabled, positive */ 1039a0bf528SMauro Carvalho Chehab {0x61,0x95}, /* GPIO pins 1-4 have special function */ 1049a0bf528SMauro Carvalho Chehab {0x62,0x05}, /* GPIO pin 5 has special function, pin 6 is GPIO */ 1059a0bf528SMauro Carvalho Chehab {0x63,0x00}, /* All GPIO pins use CMOS output characteristics */ 1069a0bf528SMauro Carvalho Chehab {0x64,0x20}, /* GPIO 6 is input, all others are outputs */ 1079a0bf528SMauro Carvalho Chehab {0x6d,0x30}, /* tuner auto mode clock freq 62kHz */ 1089a0bf528SMauro Carvalho Chehab {0x70,0x15}, /* use auto mode, tuner word is 21 bits long */ 1099a0bf528SMauro Carvalho Chehab {0x73,0x00}, /* @ disable several demod bypasses */ 1109a0bf528SMauro Carvalho Chehab {0x74,0x00}, /* @ " */ 1119a0bf528SMauro Carvalho Chehab {0x75,0x00} /* @ " */ 1129a0bf528SMauro Carvalho Chehab /* the remaining registers are for SEC */ 1139a0bf528SMauro Carvalho Chehab }; 1149a0bf528SMauro Carvalho Chehab 1159a0bf528SMauro Carvalho Chehab 1169a0bf528SMauro Carvalho Chehab static int cx24110_writereg (struct cx24110_state* state, int reg, int data) 1179a0bf528SMauro Carvalho Chehab { 1189a0bf528SMauro Carvalho Chehab u8 buf [] = { reg, data }; 1199a0bf528SMauro Carvalho Chehab struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; 1209a0bf528SMauro Carvalho Chehab int err; 1219a0bf528SMauro Carvalho Chehab 1229a0bf528SMauro Carvalho Chehab if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { 1239a0bf528SMauro Carvalho Chehab dprintk ("%s: writereg error (err == %i, reg == 0x%02x," 1249a0bf528SMauro Carvalho Chehab " data == 0x%02x)\n", __func__, err, reg, data); 1259a0bf528SMauro Carvalho Chehab return -EREMOTEIO; 1269a0bf528SMauro Carvalho Chehab } 1279a0bf528SMauro Carvalho Chehab 1289a0bf528SMauro Carvalho Chehab return 0; 1299a0bf528SMauro Carvalho Chehab } 1309a0bf528SMauro Carvalho Chehab 1319a0bf528SMauro Carvalho Chehab static int cx24110_readreg (struct cx24110_state* state, u8 reg) 1329a0bf528SMauro Carvalho Chehab { 1339a0bf528SMauro Carvalho Chehab int ret; 1349a0bf528SMauro Carvalho Chehab u8 b0 [] = { reg }; 1359a0bf528SMauro Carvalho Chehab u8 b1 [] = { 0 }; 1369a0bf528SMauro Carvalho Chehab struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, 1379a0bf528SMauro Carvalho Chehab { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; 1389a0bf528SMauro Carvalho Chehab 1399a0bf528SMauro Carvalho Chehab ret = i2c_transfer(state->i2c, msg, 2); 1409a0bf528SMauro Carvalho Chehab 1419a0bf528SMauro Carvalho Chehab if (ret != 2) return ret; 1429a0bf528SMauro Carvalho Chehab 1439a0bf528SMauro Carvalho Chehab return b1[0]; 1449a0bf528SMauro Carvalho Chehab } 1459a0bf528SMauro Carvalho Chehab 1469a0bf528SMauro Carvalho Chehab static int cx24110_set_inversion (struct cx24110_state* state, fe_spectral_inversion_t inversion) 1479a0bf528SMauro Carvalho Chehab { 1489a0bf528SMauro Carvalho Chehab /* fixme (low): error handling */ 1499a0bf528SMauro Carvalho Chehab 1509a0bf528SMauro Carvalho Chehab switch (inversion) { 1519a0bf528SMauro Carvalho Chehab case INVERSION_OFF: 1529a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1); 1539a0bf528SMauro Carvalho Chehab /* AcqSpectrInvDis on. No idea why someone should want this */ 1549a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)&0xf7); 1559a0bf528SMauro Carvalho Chehab /* Initial value 0 at start of acq */ 1569a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)&0xef); 1579a0bf528SMauro Carvalho Chehab /* current value 0 */ 1589a0bf528SMauro Carvalho Chehab /* The cx24110 manual tells us this reg is read-only. 1599a0bf528SMauro Carvalho Chehab But what the heck... set it ayways */ 1609a0bf528SMauro Carvalho Chehab break; 1619a0bf528SMauro Carvalho Chehab case INVERSION_ON: 1629a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1); 1639a0bf528SMauro Carvalho Chehab /* AcqSpectrInvDis on. No idea why someone should want this */ 1649a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)|0x08); 1659a0bf528SMauro Carvalho Chehab /* Initial value 1 at start of acq */ 1669a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)|0x10); 1679a0bf528SMauro Carvalho Chehab /* current value 1 */ 1689a0bf528SMauro Carvalho Chehab break; 1699a0bf528SMauro Carvalho Chehab case INVERSION_AUTO: 1709a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xfe); 1719a0bf528SMauro Carvalho Chehab /* AcqSpectrInvDis off. Leave initial & current states as is */ 1729a0bf528SMauro Carvalho Chehab break; 1739a0bf528SMauro Carvalho Chehab default: 1749a0bf528SMauro Carvalho Chehab return -EINVAL; 1759a0bf528SMauro Carvalho Chehab } 1769a0bf528SMauro Carvalho Chehab 1779a0bf528SMauro Carvalho Chehab return 0; 1789a0bf528SMauro Carvalho Chehab } 1799a0bf528SMauro Carvalho Chehab 1809a0bf528SMauro Carvalho Chehab static int cx24110_set_fec (struct cx24110_state* state, fe_code_rate_t fec) 1819a0bf528SMauro Carvalho Chehab { 1829a0bf528SMauro Carvalho Chehab /* fixme (low): error handling */ 1839a0bf528SMauro Carvalho Chehab 1849a0bf528SMauro Carvalho Chehab static const int rate[]={-1,1,2,3,5,7,-1}; 1859a0bf528SMauro Carvalho Chehab static const int g1[]={-1,0x01,0x02,0x05,0x15,0x45,-1}; 1869a0bf528SMauro Carvalho Chehab static const int g2[]={-1,0x01,0x03,0x06,0x1a,0x7a,-1}; 1879a0bf528SMauro Carvalho Chehab 1889a0bf528SMauro Carvalho Chehab /* Well, the AutoAcq engine of the cx24106 and 24110 automatically 1899a0bf528SMauro Carvalho Chehab searches all enabled viterbi rates, and can handle non-standard 1909a0bf528SMauro Carvalho Chehab rates as well. */ 1919a0bf528SMauro Carvalho Chehab 1929a0bf528SMauro Carvalho Chehab if (fec>FEC_AUTO) 1939a0bf528SMauro Carvalho Chehab fec=FEC_AUTO; 1949a0bf528SMauro Carvalho Chehab 1959a0bf528SMauro Carvalho Chehab if (fec==FEC_AUTO) { /* (re-)establish AutoAcq behaviour */ 1969a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xdf); 1979a0bf528SMauro Carvalho Chehab /* clear AcqVitDis bit */ 1989a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x18,0xae); 1999a0bf528SMauro Carvalho Chehab /* allow all DVB standard code rates */ 2009a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|0x3); 2019a0bf528SMauro Carvalho Chehab /* set nominal Viterbi rate 3/4 */ 2029a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|0x3); 2039a0bf528SMauro Carvalho Chehab /* set current Viterbi rate 3/4 */ 2049a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x1a,0x05); cx24110_writereg(state,0x1b,0x06); 2059a0bf528SMauro Carvalho Chehab /* set the puncture registers for code rate 3/4 */ 2069a0bf528SMauro Carvalho Chehab return 0; 2079a0bf528SMauro Carvalho Chehab } else { 2089a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x20); 2099a0bf528SMauro Carvalho Chehab /* set AcqVitDis bit */ 2109a0bf528SMauro Carvalho Chehab if(rate[fec]>0) { 2119a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|rate[fec]); 2129a0bf528SMauro Carvalho Chehab /* set nominal Viterbi rate */ 2139a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|rate[fec]); 2149a0bf528SMauro Carvalho Chehab /* set current Viterbi rate */ 2159a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x1a,g1[fec]); 2169a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x1b,g2[fec]); 2179a0bf528SMauro Carvalho Chehab /* not sure if this is the right way: I always used AutoAcq mode */ 2189a0bf528SMauro Carvalho Chehab } else 2199a0bf528SMauro Carvalho Chehab return -EOPNOTSUPP; 2209a0bf528SMauro Carvalho Chehab /* fixme (low): which is the correct return code? */ 221c2c1b415SPeter Senna Tschudin } 2229a0bf528SMauro Carvalho Chehab return 0; 2239a0bf528SMauro Carvalho Chehab } 2249a0bf528SMauro Carvalho Chehab 2259a0bf528SMauro Carvalho Chehab static fe_code_rate_t cx24110_get_fec (struct cx24110_state* state) 2269a0bf528SMauro Carvalho Chehab { 2279a0bf528SMauro Carvalho Chehab int i; 2289a0bf528SMauro Carvalho Chehab 2299a0bf528SMauro Carvalho Chehab i=cx24110_readreg(state,0x22)&0x0f; 2309a0bf528SMauro Carvalho Chehab if(!(i&0x08)) { 2319a0bf528SMauro Carvalho Chehab return FEC_1_2 + i - 1; 2329a0bf528SMauro Carvalho Chehab } else { 2339a0bf528SMauro Carvalho Chehab /* fixme (low): a special code rate has been selected. In theory, we need to 2349a0bf528SMauro Carvalho Chehab return a denominator value, a numerator value, and a pair of puncture 2359a0bf528SMauro Carvalho Chehab maps to correctly describe this mode. But this should never happen in 2369a0bf528SMauro Carvalho Chehab practice, because it cannot be set by cx24110_get_fec. */ 2379a0bf528SMauro Carvalho Chehab return FEC_NONE; 2389a0bf528SMauro Carvalho Chehab } 2399a0bf528SMauro Carvalho Chehab } 2409a0bf528SMauro Carvalho Chehab 2419a0bf528SMauro Carvalho Chehab static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate) 2429a0bf528SMauro Carvalho Chehab { 2439a0bf528SMauro Carvalho Chehab /* fixme (low): add error handling */ 2449a0bf528SMauro Carvalho Chehab u32 ratio; 2459a0bf528SMauro Carvalho Chehab u32 tmp, fclk, BDRI; 2469a0bf528SMauro Carvalho Chehab 2479a0bf528SMauro Carvalho Chehab static const u32 bands[]={5000000UL,15000000UL,90999000UL/2}; 2489a0bf528SMauro Carvalho Chehab int i; 2499a0bf528SMauro Carvalho Chehab 2509a0bf528SMauro Carvalho Chehab dprintk("cx24110 debug: entering %s(%d)\n",__func__,srate); 2519a0bf528SMauro Carvalho Chehab if (srate>90999000UL/2) 2529a0bf528SMauro Carvalho Chehab srate=90999000UL/2; 2539a0bf528SMauro Carvalho Chehab if (srate<500000) 2549a0bf528SMauro Carvalho Chehab srate=500000; 2559a0bf528SMauro Carvalho Chehab 2569a0bf528SMauro Carvalho Chehab for(i = 0; (i < ARRAY_SIZE(bands)) && (srate>bands[i]); i++) 2579a0bf528SMauro Carvalho Chehab ; 2589a0bf528SMauro Carvalho Chehab /* first, check which sample rate is appropriate: 45, 60 80 or 90 MHz, 2599a0bf528SMauro Carvalho Chehab and set the PLL accordingly (R07[1:0] Fclk, R06[7:4] PLLmult, 2609a0bf528SMauro Carvalho Chehab R06[3:0] PLLphaseDetGain */ 2619a0bf528SMauro Carvalho Chehab tmp=cx24110_readreg(state,0x07)&0xfc; 2629a0bf528SMauro Carvalho Chehab if(srate<90999000UL/4) { /* sample rate 45MHz*/ 2639a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x07,tmp); 2649a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x06,0x78); 2659a0bf528SMauro Carvalho Chehab fclk=90999000UL/2; 2669a0bf528SMauro Carvalho Chehab } else if(srate<60666000UL/2) { /* sample rate 60MHz */ 2679a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x07,tmp|0x1); 2689a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x06,0xa5); 2699a0bf528SMauro Carvalho Chehab fclk=60666000UL; 2709a0bf528SMauro Carvalho Chehab } else if(srate<80888000UL/2) { /* sample rate 80MHz */ 2719a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x07,tmp|0x2); 2729a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x06,0x87); 2739a0bf528SMauro Carvalho Chehab fclk=80888000UL; 2749a0bf528SMauro Carvalho Chehab } else { /* sample rate 90MHz */ 2759a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x07,tmp|0x3); 2769a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x06,0x78); 2779a0bf528SMauro Carvalho Chehab fclk=90999000UL; 278c2c1b415SPeter Senna Tschudin } 2799a0bf528SMauro Carvalho Chehab dprintk("cx24110 debug: fclk %d Hz\n",fclk); 2809a0bf528SMauro Carvalho Chehab /* we need to divide two integers with approx. 27 bits in 32 bit 2819a0bf528SMauro Carvalho Chehab arithmetic giving a 25 bit result */ 2829a0bf528SMauro Carvalho Chehab /* the maximum dividend is 90999000/2, 0x02b6446c, this number is 2839a0bf528SMauro Carvalho Chehab also the most complex divisor. Hence, the dividend has, 2849a0bf528SMauro Carvalho Chehab assuming 32bit unsigned arithmetic, 6 clear bits on top, the 2859a0bf528SMauro Carvalho Chehab divisor 2 unused bits at the bottom. Also, the quotient is 2869a0bf528SMauro Carvalho Chehab always less than 1/2. Borrowed from VES1893.c, of course */ 2879a0bf528SMauro Carvalho Chehab 2889a0bf528SMauro Carvalho Chehab tmp=srate<<6; 2899a0bf528SMauro Carvalho Chehab BDRI=fclk>>2; 2909a0bf528SMauro Carvalho Chehab ratio=(tmp/BDRI); 2919a0bf528SMauro Carvalho Chehab 2929a0bf528SMauro Carvalho Chehab tmp=(tmp%BDRI)<<8; 2939a0bf528SMauro Carvalho Chehab ratio=(ratio<<8)+(tmp/BDRI); 2949a0bf528SMauro Carvalho Chehab 2959a0bf528SMauro Carvalho Chehab tmp=(tmp%BDRI)<<8; 2969a0bf528SMauro Carvalho Chehab ratio=(ratio<<8)+(tmp/BDRI); 2979a0bf528SMauro Carvalho Chehab 2989a0bf528SMauro Carvalho Chehab tmp=(tmp%BDRI)<<1; 2999a0bf528SMauro Carvalho Chehab ratio=(ratio<<1)+(tmp/BDRI); 3009a0bf528SMauro Carvalho Chehab 3019a0bf528SMauro Carvalho Chehab dprintk("srate= %d (range %d, up to %d)\n", srate,i,bands[i]); 3029a0bf528SMauro Carvalho Chehab dprintk("fclk = %d\n", fclk); 3039a0bf528SMauro Carvalho Chehab dprintk("ratio= %08x\n", ratio); 3049a0bf528SMauro Carvalho Chehab 3059a0bf528SMauro Carvalho Chehab cx24110_writereg(state, 0x1, (ratio>>16)&0xff); 3069a0bf528SMauro Carvalho Chehab cx24110_writereg(state, 0x2, (ratio>>8)&0xff); 3079a0bf528SMauro Carvalho Chehab cx24110_writereg(state, 0x3, (ratio)&0xff); 3089a0bf528SMauro Carvalho Chehab 3099a0bf528SMauro Carvalho Chehab return 0; 3109a0bf528SMauro Carvalho Chehab 3119a0bf528SMauro Carvalho Chehab } 3129a0bf528SMauro Carvalho Chehab 3139a0bf528SMauro Carvalho Chehab static int _cx24110_pll_write (struct dvb_frontend* fe, const u8 buf[], int len) 3149a0bf528SMauro Carvalho Chehab { 3159a0bf528SMauro Carvalho Chehab struct cx24110_state *state = fe->demodulator_priv; 3169a0bf528SMauro Carvalho Chehab 3179a0bf528SMauro Carvalho Chehab if (len != 3) 3189a0bf528SMauro Carvalho Chehab return -EINVAL; 3199a0bf528SMauro Carvalho Chehab 3209a0bf528SMauro Carvalho Chehab /* tuner data is 21 bits long, must be left-aligned in data */ 3219a0bf528SMauro Carvalho Chehab /* tuner cx24108 is written through a dedicated 3wire interface on the demod chip */ 3229a0bf528SMauro Carvalho Chehab /* FIXME (low): add error handling, avoid infinite loops if HW fails... */ 3239a0bf528SMauro Carvalho Chehab 3249a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x6d,0x30); /* auto mode at 62kHz */ 3259a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x70,0x15); /* auto mode 21 bits */ 3269a0bf528SMauro Carvalho Chehab 3279a0bf528SMauro Carvalho Chehab /* if the auto tuner writer is still busy, clear it out */ 3289a0bf528SMauro Carvalho Chehab while (cx24110_readreg(state,0x6d)&0x80) 3299a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x72,0); 3309a0bf528SMauro Carvalho Chehab 3319a0bf528SMauro Carvalho Chehab /* write the topmost 8 bits */ 3329a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x72,buf[0]); 3339a0bf528SMauro Carvalho Chehab 3349a0bf528SMauro Carvalho Chehab /* wait for the send to be completed */ 3359a0bf528SMauro Carvalho Chehab while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) 3369a0bf528SMauro Carvalho Chehab ; 3379a0bf528SMauro Carvalho Chehab 3389a0bf528SMauro Carvalho Chehab /* send another 8 bytes */ 3399a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x72,buf[1]); 3409a0bf528SMauro Carvalho Chehab while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) 3419a0bf528SMauro Carvalho Chehab ; 3429a0bf528SMauro Carvalho Chehab 3439a0bf528SMauro Carvalho Chehab /* and the topmost 5 bits of this byte */ 3449a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x72,buf[2]); 3459a0bf528SMauro Carvalho Chehab while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) 3469a0bf528SMauro Carvalho Chehab ; 3479a0bf528SMauro Carvalho Chehab 3489a0bf528SMauro Carvalho Chehab /* now strobe the enable line once */ 3499a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x6d,0x32); 3509a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x6d,0x30); 3519a0bf528SMauro Carvalho Chehab 3529a0bf528SMauro Carvalho Chehab return 0; 3539a0bf528SMauro Carvalho Chehab } 3549a0bf528SMauro Carvalho Chehab 3559a0bf528SMauro Carvalho Chehab static int cx24110_initfe(struct dvb_frontend* fe) 3569a0bf528SMauro Carvalho Chehab { 3579a0bf528SMauro Carvalho Chehab struct cx24110_state *state = fe->demodulator_priv; 3589a0bf528SMauro Carvalho Chehab /* fixme (low): error handling */ 3599a0bf528SMauro Carvalho Chehab int i; 3609a0bf528SMauro Carvalho Chehab 3619a0bf528SMauro Carvalho Chehab dprintk("%s: init chip\n", __func__); 3629a0bf528SMauro Carvalho Chehab 3639a0bf528SMauro Carvalho Chehab for(i = 0; i < ARRAY_SIZE(cx24110_regdata); i++) { 3649a0bf528SMauro Carvalho Chehab cx24110_writereg(state, cx24110_regdata[i].reg, cx24110_regdata[i].data); 365c2c1b415SPeter Senna Tschudin } 3669a0bf528SMauro Carvalho Chehab 3679a0bf528SMauro Carvalho Chehab return 0; 3689a0bf528SMauro Carvalho Chehab } 3699a0bf528SMauro Carvalho Chehab 3709a0bf528SMauro Carvalho Chehab static int cx24110_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) 3719a0bf528SMauro Carvalho Chehab { 3729a0bf528SMauro Carvalho Chehab struct cx24110_state *state = fe->demodulator_priv; 3739a0bf528SMauro Carvalho Chehab 3749a0bf528SMauro Carvalho Chehab switch (voltage) { 3759a0bf528SMauro Carvalho Chehab case SEC_VOLTAGE_13: 3769a0bf528SMauro Carvalho Chehab return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0xc0); 3779a0bf528SMauro Carvalho Chehab case SEC_VOLTAGE_18: 3789a0bf528SMauro Carvalho Chehab return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0x40); 3799a0bf528SMauro Carvalho Chehab default: 3809a0bf528SMauro Carvalho Chehab return -EINVAL; 3819a0bf528SMauro Carvalho Chehab }; 3829a0bf528SMauro Carvalho Chehab } 3839a0bf528SMauro Carvalho Chehab 3849a0bf528SMauro Carvalho Chehab static int cx24110_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) 3859a0bf528SMauro Carvalho Chehab { 3869a0bf528SMauro Carvalho Chehab int rv, bit; 3879a0bf528SMauro Carvalho Chehab struct cx24110_state *state = fe->demodulator_priv; 3889a0bf528SMauro Carvalho Chehab unsigned long timeout; 3899a0bf528SMauro Carvalho Chehab 3909a0bf528SMauro Carvalho Chehab if (burst == SEC_MINI_A) 3919a0bf528SMauro Carvalho Chehab bit = 0x00; 3929a0bf528SMauro Carvalho Chehab else if (burst == SEC_MINI_B) 3939a0bf528SMauro Carvalho Chehab bit = 0x08; 3949a0bf528SMauro Carvalho Chehab else 3959a0bf528SMauro Carvalho Chehab return -EINVAL; 3969a0bf528SMauro Carvalho Chehab 3979a0bf528SMauro Carvalho Chehab rv = cx24110_readreg(state, 0x77); 3989a0bf528SMauro Carvalho Chehab if (!(rv & 0x04)) 3999a0bf528SMauro Carvalho Chehab cx24110_writereg(state, 0x77, rv | 0x04); 4009a0bf528SMauro Carvalho Chehab 4019a0bf528SMauro Carvalho Chehab rv = cx24110_readreg(state, 0x76); 4029a0bf528SMauro Carvalho Chehab cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40 | bit)); 4039a0bf528SMauro Carvalho Chehab timeout = jiffies + msecs_to_jiffies(100); 4049a0bf528SMauro Carvalho Chehab while (!time_after(jiffies, timeout) && !(cx24110_readreg(state, 0x76) & 0x40)) 4059a0bf528SMauro Carvalho Chehab ; /* wait for LNB ready */ 4069a0bf528SMauro Carvalho Chehab 4079a0bf528SMauro Carvalho Chehab return 0; 4089a0bf528SMauro Carvalho Chehab } 4099a0bf528SMauro Carvalho Chehab 4109a0bf528SMauro Carvalho Chehab static int cx24110_send_diseqc_msg(struct dvb_frontend* fe, 4119a0bf528SMauro Carvalho Chehab struct dvb_diseqc_master_cmd *cmd) 4129a0bf528SMauro Carvalho Chehab { 4139a0bf528SMauro Carvalho Chehab int i, rv; 4149a0bf528SMauro Carvalho Chehab struct cx24110_state *state = fe->demodulator_priv; 4159a0bf528SMauro Carvalho Chehab unsigned long timeout; 4169a0bf528SMauro Carvalho Chehab 4179a0bf528SMauro Carvalho Chehab if (cmd->msg_len < 3 || cmd->msg_len > 6) 4189a0bf528SMauro Carvalho Chehab return -EINVAL; /* not implemented */ 4199a0bf528SMauro Carvalho Chehab 4209a0bf528SMauro Carvalho Chehab for (i = 0; i < cmd->msg_len; i++) 4219a0bf528SMauro Carvalho Chehab cx24110_writereg(state, 0x79 + i, cmd->msg[i]); 4229a0bf528SMauro Carvalho Chehab 4239a0bf528SMauro Carvalho Chehab rv = cx24110_readreg(state, 0x77); 4249a0bf528SMauro Carvalho Chehab if (rv & 0x04) { 4259a0bf528SMauro Carvalho Chehab cx24110_writereg(state, 0x77, rv & ~0x04); 4269a0bf528SMauro Carvalho Chehab msleep(30); /* reportedly fixes switching problems */ 4279a0bf528SMauro Carvalho Chehab } 4289a0bf528SMauro Carvalho Chehab 4299a0bf528SMauro Carvalho Chehab rv = cx24110_readreg(state, 0x76); 4309a0bf528SMauro Carvalho Chehab 4319a0bf528SMauro Carvalho Chehab cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40) | ((cmd->msg_len-3) & 3)); 4329a0bf528SMauro Carvalho Chehab timeout = jiffies + msecs_to_jiffies(100); 4339a0bf528SMauro Carvalho Chehab while (!time_after(jiffies, timeout) && !(cx24110_readreg(state, 0x76) & 0x40)) 4349a0bf528SMauro Carvalho Chehab ; /* wait for LNB ready */ 4359a0bf528SMauro Carvalho Chehab 4369a0bf528SMauro Carvalho Chehab return 0; 4379a0bf528SMauro Carvalho Chehab } 4389a0bf528SMauro Carvalho Chehab 4399a0bf528SMauro Carvalho Chehab static int cx24110_read_status(struct dvb_frontend* fe, fe_status_t* status) 4409a0bf528SMauro Carvalho Chehab { 4419a0bf528SMauro Carvalho Chehab struct cx24110_state *state = fe->demodulator_priv; 4429a0bf528SMauro Carvalho Chehab 4439a0bf528SMauro Carvalho Chehab int sync = cx24110_readreg (state, 0x55); 4449a0bf528SMauro Carvalho Chehab 4459a0bf528SMauro Carvalho Chehab *status = 0; 4469a0bf528SMauro Carvalho Chehab 4479a0bf528SMauro Carvalho Chehab if (sync & 0x10) 4489a0bf528SMauro Carvalho Chehab *status |= FE_HAS_SIGNAL; 4499a0bf528SMauro Carvalho Chehab 4509a0bf528SMauro Carvalho Chehab if (sync & 0x08) 4519a0bf528SMauro Carvalho Chehab *status |= FE_HAS_CARRIER; 4529a0bf528SMauro Carvalho Chehab 4539a0bf528SMauro Carvalho Chehab sync = cx24110_readreg (state, 0x08); 4549a0bf528SMauro Carvalho Chehab 4559a0bf528SMauro Carvalho Chehab if (sync & 0x40) 4569a0bf528SMauro Carvalho Chehab *status |= FE_HAS_VITERBI; 4579a0bf528SMauro Carvalho Chehab 4589a0bf528SMauro Carvalho Chehab if (sync & 0x20) 4599a0bf528SMauro Carvalho Chehab *status |= FE_HAS_SYNC; 4609a0bf528SMauro Carvalho Chehab 4619a0bf528SMauro Carvalho Chehab if ((sync & 0x60) == 0x60) 4629a0bf528SMauro Carvalho Chehab *status |= FE_HAS_LOCK; 4639a0bf528SMauro Carvalho Chehab 4649a0bf528SMauro Carvalho Chehab return 0; 4659a0bf528SMauro Carvalho Chehab } 4669a0bf528SMauro Carvalho Chehab 4679a0bf528SMauro Carvalho Chehab static int cx24110_read_ber(struct dvb_frontend* fe, u32* ber) 4689a0bf528SMauro Carvalho Chehab { 4699a0bf528SMauro Carvalho Chehab struct cx24110_state *state = fe->demodulator_priv; 4709a0bf528SMauro Carvalho Chehab 4719a0bf528SMauro Carvalho Chehab /* fixme (maybe): value range is 16 bit. Scale? */ 4729a0bf528SMauro Carvalho Chehab if(cx24110_readreg(state,0x24)&0x10) { 4739a0bf528SMauro Carvalho Chehab /* the Viterbi error counter has finished one counting window */ 4749a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x24,0x04); /* select the ber reg */ 4759a0bf528SMauro Carvalho Chehab state->lastber=cx24110_readreg(state,0x25)| 4769a0bf528SMauro Carvalho Chehab (cx24110_readreg(state,0x26)<<8); 4779a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x24,0x04); /* start new count window */ 4789a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x24,0x14); 4799a0bf528SMauro Carvalho Chehab } 4809a0bf528SMauro Carvalho Chehab *ber = state->lastber; 4819a0bf528SMauro Carvalho Chehab 4829a0bf528SMauro Carvalho Chehab return 0; 4839a0bf528SMauro Carvalho Chehab } 4849a0bf528SMauro Carvalho Chehab 4859a0bf528SMauro Carvalho Chehab static int cx24110_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) 4869a0bf528SMauro Carvalho Chehab { 4879a0bf528SMauro Carvalho Chehab struct cx24110_state *state = fe->demodulator_priv; 4889a0bf528SMauro Carvalho Chehab 4899a0bf528SMauro Carvalho Chehab /* no provision in hardware. Read the frontend AGC accumulator. No idea how to scale this, but I know it is 2s complement */ 4909a0bf528SMauro Carvalho Chehab u8 signal = cx24110_readreg (state, 0x27)+128; 4919a0bf528SMauro Carvalho Chehab *signal_strength = (signal << 8) | signal; 4929a0bf528SMauro Carvalho Chehab 4939a0bf528SMauro Carvalho Chehab return 0; 4949a0bf528SMauro Carvalho Chehab } 4959a0bf528SMauro Carvalho Chehab 4969a0bf528SMauro Carvalho Chehab static int cx24110_read_snr(struct dvb_frontend* fe, u16* snr) 4979a0bf528SMauro Carvalho Chehab { 4989a0bf528SMauro Carvalho Chehab struct cx24110_state *state = fe->demodulator_priv; 4999a0bf528SMauro Carvalho Chehab 5009a0bf528SMauro Carvalho Chehab /* no provision in hardware. Can be computed from the Es/N0 estimator, but I don't know how. */ 5019a0bf528SMauro Carvalho Chehab if(cx24110_readreg(state,0x6a)&0x80) { 5029a0bf528SMauro Carvalho Chehab /* the Es/N0 error counter has finished one counting window */ 5039a0bf528SMauro Carvalho Chehab state->lastesn0=cx24110_readreg(state,0x69)| 5049a0bf528SMauro Carvalho Chehab (cx24110_readreg(state,0x68)<<8); 5059a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x6a,0x84); /* start new count window */ 5069a0bf528SMauro Carvalho Chehab } 5079a0bf528SMauro Carvalho Chehab *snr = state->lastesn0; 5089a0bf528SMauro Carvalho Chehab 5099a0bf528SMauro Carvalho Chehab return 0; 5109a0bf528SMauro Carvalho Chehab } 5119a0bf528SMauro Carvalho Chehab 5129a0bf528SMauro Carvalho Chehab static int cx24110_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) 5139a0bf528SMauro Carvalho Chehab { 5149a0bf528SMauro Carvalho Chehab struct cx24110_state *state = fe->demodulator_priv; 5159a0bf528SMauro Carvalho Chehab 5169a0bf528SMauro Carvalho Chehab if(cx24110_readreg(state,0x10)&0x40) { 5179a0bf528SMauro Carvalho Chehab /* the RS error counter has finished one counting window */ 5189a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x10,0x60); /* select the byer reg */ 5199a0bf528SMauro Carvalho Chehab (void)(cx24110_readreg(state, 0x12) | 5209a0bf528SMauro Carvalho Chehab (cx24110_readreg(state, 0x13) << 8) | 5219a0bf528SMauro Carvalho Chehab (cx24110_readreg(state, 0x14) << 16)); 5229a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x10,0x70); /* select the bler reg */ 5239a0bf528SMauro Carvalho Chehab state->lastbler=cx24110_readreg(state,0x12)| 5249a0bf528SMauro Carvalho Chehab (cx24110_readreg(state,0x13)<<8)| 5259a0bf528SMauro Carvalho Chehab (cx24110_readreg(state,0x14)<<16); 5269a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x10,0x20); /* start new count window */ 5279a0bf528SMauro Carvalho Chehab } 5289a0bf528SMauro Carvalho Chehab *ucblocks = state->lastbler; 5299a0bf528SMauro Carvalho Chehab 5309a0bf528SMauro Carvalho Chehab return 0; 5319a0bf528SMauro Carvalho Chehab } 5329a0bf528SMauro Carvalho Chehab 5339a0bf528SMauro Carvalho Chehab static int cx24110_set_frontend(struct dvb_frontend *fe) 5349a0bf528SMauro Carvalho Chehab { 5359a0bf528SMauro Carvalho Chehab struct cx24110_state *state = fe->demodulator_priv; 5369a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *p = &fe->dtv_property_cache; 5379a0bf528SMauro Carvalho Chehab 5389a0bf528SMauro Carvalho Chehab if (fe->ops.tuner_ops.set_params) { 5399a0bf528SMauro Carvalho Chehab fe->ops.tuner_ops.set_params(fe); 5409a0bf528SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); 5419a0bf528SMauro Carvalho Chehab } 5429a0bf528SMauro Carvalho Chehab 5439a0bf528SMauro Carvalho Chehab cx24110_set_inversion(state, p->inversion); 5449a0bf528SMauro Carvalho Chehab cx24110_set_fec(state, p->fec_inner); 5459a0bf528SMauro Carvalho Chehab cx24110_set_symbolrate(state, p->symbol_rate); 5469a0bf528SMauro Carvalho Chehab cx24110_writereg(state,0x04,0x05); /* start acquisition */ 5479a0bf528SMauro Carvalho Chehab 5489a0bf528SMauro Carvalho Chehab return 0; 5499a0bf528SMauro Carvalho Chehab } 5509a0bf528SMauro Carvalho Chehab 5519a0bf528SMauro Carvalho Chehab static int cx24110_get_frontend(struct dvb_frontend *fe) 5529a0bf528SMauro Carvalho Chehab { 5539a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *p = &fe->dtv_property_cache; 5549a0bf528SMauro Carvalho Chehab struct cx24110_state *state = fe->demodulator_priv; 5559a0bf528SMauro Carvalho Chehab s32 afc; unsigned sclk; 5569a0bf528SMauro Carvalho Chehab 5579a0bf528SMauro Carvalho Chehab /* cannot read back tuner settings (freq). Need to have some private storage */ 5589a0bf528SMauro Carvalho Chehab 5599a0bf528SMauro Carvalho Chehab sclk = cx24110_readreg (state, 0x07) & 0x03; 5609a0bf528SMauro Carvalho Chehab /* ok, real AFC (FEDR) freq. is afc/2^24*fsamp, fsamp=45/60/80/90MHz. 5619a0bf528SMauro Carvalho Chehab * Need 64 bit arithmetic. Is thiss possible in the kernel? */ 5629a0bf528SMauro Carvalho Chehab if (sclk==0) sclk=90999000L/2L; 5639a0bf528SMauro Carvalho Chehab else if (sclk==1) sclk=60666000L; 5649a0bf528SMauro Carvalho Chehab else if (sclk==2) sclk=80888000L; 5659a0bf528SMauro Carvalho Chehab else sclk=90999000L; 5669a0bf528SMauro Carvalho Chehab sclk>>=8; 5679a0bf528SMauro Carvalho Chehab afc = sclk*(cx24110_readreg (state, 0x44)&0x1f)+ 5689a0bf528SMauro Carvalho Chehab ((sclk*cx24110_readreg (state, 0x45))>>8)+ 5699a0bf528SMauro Carvalho Chehab ((sclk*cx24110_readreg (state, 0x46))>>16); 5709a0bf528SMauro Carvalho Chehab 5719a0bf528SMauro Carvalho Chehab p->frequency += afc; 5729a0bf528SMauro Carvalho Chehab p->inversion = (cx24110_readreg (state, 0x22) & 0x10) ? 5739a0bf528SMauro Carvalho Chehab INVERSION_ON : INVERSION_OFF; 5749a0bf528SMauro Carvalho Chehab p->fec_inner = cx24110_get_fec(state); 5759a0bf528SMauro Carvalho Chehab 5769a0bf528SMauro Carvalho Chehab return 0; 5779a0bf528SMauro Carvalho Chehab } 5789a0bf528SMauro Carvalho Chehab 5799a0bf528SMauro Carvalho Chehab static int cx24110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) 5809a0bf528SMauro Carvalho Chehab { 5819a0bf528SMauro Carvalho Chehab struct cx24110_state *state = fe->demodulator_priv; 5829a0bf528SMauro Carvalho Chehab 5839a0bf528SMauro Carvalho Chehab return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&~0x10)|(((tone==SEC_TONE_ON))?0x10:0)); 5849a0bf528SMauro Carvalho Chehab } 5859a0bf528SMauro Carvalho Chehab 5869a0bf528SMauro Carvalho Chehab static void cx24110_release(struct dvb_frontend* fe) 5879a0bf528SMauro Carvalho Chehab { 5889a0bf528SMauro Carvalho Chehab struct cx24110_state* state = fe->demodulator_priv; 5899a0bf528SMauro Carvalho Chehab kfree(state); 5909a0bf528SMauro Carvalho Chehab } 5919a0bf528SMauro Carvalho Chehab 5929a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops cx24110_ops; 5939a0bf528SMauro Carvalho Chehab 5949a0bf528SMauro Carvalho Chehab struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, 5959a0bf528SMauro Carvalho Chehab struct i2c_adapter* i2c) 5969a0bf528SMauro Carvalho Chehab { 5979a0bf528SMauro Carvalho Chehab struct cx24110_state* state = NULL; 5989a0bf528SMauro Carvalho Chehab int ret; 5999a0bf528SMauro Carvalho Chehab 6009a0bf528SMauro Carvalho Chehab /* allocate memory for the internal state */ 6019a0bf528SMauro Carvalho Chehab state = kzalloc(sizeof(struct cx24110_state), GFP_KERNEL); 6029a0bf528SMauro Carvalho Chehab if (state == NULL) goto error; 6039a0bf528SMauro Carvalho Chehab 6049a0bf528SMauro Carvalho Chehab /* setup the state */ 6059a0bf528SMauro Carvalho Chehab state->config = config; 6069a0bf528SMauro Carvalho Chehab state->i2c = i2c; 6079a0bf528SMauro Carvalho Chehab state->lastber = 0; 6089a0bf528SMauro Carvalho Chehab state->lastbler = 0; 6099a0bf528SMauro Carvalho Chehab state->lastesn0 = 0; 6109a0bf528SMauro Carvalho Chehab 6119a0bf528SMauro Carvalho Chehab /* check if the demod is there */ 6129a0bf528SMauro Carvalho Chehab ret = cx24110_readreg(state, 0x00); 6139a0bf528SMauro Carvalho Chehab if ((ret != 0x5a) && (ret != 0x69)) goto error; 6149a0bf528SMauro Carvalho Chehab 6159a0bf528SMauro Carvalho Chehab /* create dvb_frontend */ 6169a0bf528SMauro Carvalho Chehab memcpy(&state->frontend.ops, &cx24110_ops, sizeof(struct dvb_frontend_ops)); 6179a0bf528SMauro Carvalho Chehab state->frontend.demodulator_priv = state; 6189a0bf528SMauro Carvalho Chehab return &state->frontend; 6199a0bf528SMauro Carvalho Chehab 6209a0bf528SMauro Carvalho Chehab error: 6219a0bf528SMauro Carvalho Chehab kfree(state); 6229a0bf528SMauro Carvalho Chehab return NULL; 6239a0bf528SMauro Carvalho Chehab } 6249a0bf528SMauro Carvalho Chehab 6259a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops cx24110_ops = { 6269a0bf528SMauro Carvalho Chehab .delsys = { SYS_DVBS }, 6279a0bf528SMauro Carvalho Chehab .info = { 6289a0bf528SMauro Carvalho Chehab .name = "Conexant CX24110 DVB-S", 6299a0bf528SMauro Carvalho Chehab .frequency_min = 950000, 6309a0bf528SMauro Carvalho Chehab .frequency_max = 2150000, 6319a0bf528SMauro Carvalho Chehab .frequency_stepsize = 1011, /* kHz for QPSK frontends */ 6329a0bf528SMauro Carvalho Chehab .frequency_tolerance = 29500, 6339a0bf528SMauro Carvalho Chehab .symbol_rate_min = 1000000, 6349a0bf528SMauro Carvalho Chehab .symbol_rate_max = 45000000, 6359a0bf528SMauro Carvalho Chehab .caps = FE_CAN_INVERSION_AUTO | 6369a0bf528SMauro Carvalho Chehab FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 6379a0bf528SMauro Carvalho Chehab FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | 6389a0bf528SMauro Carvalho Chehab FE_CAN_QPSK | FE_CAN_RECOVER 6399a0bf528SMauro Carvalho Chehab }, 6409a0bf528SMauro Carvalho Chehab 6419a0bf528SMauro Carvalho Chehab .release = cx24110_release, 6429a0bf528SMauro Carvalho Chehab 6439a0bf528SMauro Carvalho Chehab .init = cx24110_initfe, 6449a0bf528SMauro Carvalho Chehab .write = _cx24110_pll_write, 6459a0bf528SMauro Carvalho Chehab .set_frontend = cx24110_set_frontend, 6469a0bf528SMauro Carvalho Chehab .get_frontend = cx24110_get_frontend, 6479a0bf528SMauro Carvalho Chehab .read_status = cx24110_read_status, 6489a0bf528SMauro Carvalho Chehab .read_ber = cx24110_read_ber, 6499a0bf528SMauro Carvalho Chehab .read_signal_strength = cx24110_read_signal_strength, 6509a0bf528SMauro Carvalho Chehab .read_snr = cx24110_read_snr, 6519a0bf528SMauro Carvalho Chehab .read_ucblocks = cx24110_read_ucblocks, 6529a0bf528SMauro Carvalho Chehab 6539a0bf528SMauro Carvalho Chehab .diseqc_send_master_cmd = cx24110_send_diseqc_msg, 6549a0bf528SMauro Carvalho Chehab .set_tone = cx24110_set_tone, 6559a0bf528SMauro Carvalho Chehab .set_voltage = cx24110_set_voltage, 6569a0bf528SMauro Carvalho Chehab .diseqc_send_burst = cx24110_diseqc_send_burst, 6579a0bf528SMauro Carvalho Chehab }; 6589a0bf528SMauro Carvalho Chehab 6599a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644); 6609a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); 6619a0bf528SMauro Carvalho Chehab 6629a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("Conexant CX24110 DVB-S Demodulator driver"); 6639a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Peter Hettkamp"); 6649a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 6659a0bf528SMauro Carvalho Chehab 6669a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(cx24110_attach); 667