19a0bf528SMauro Carvalho Chehab /* 29a0bf528SMauro Carvalho Chehab TDA10021 - Single Chip Cable Channel Receiver driver module 39a0bf528SMauro Carvalho Chehab used on the Siemens DVB-C cards 49a0bf528SMauro Carvalho Chehab 59a0bf528SMauro Carvalho Chehab Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> 69a0bf528SMauro Carvalho Chehab Copyright (C) 2004 Markus Schulz <msc@antzsystem.de> 79a0bf528SMauro Carvalho Chehab Support for TDA10021 89a0bf528SMauro Carvalho Chehab 99a0bf528SMauro Carvalho Chehab This program is free software; you can redistribute it and/or modify 109a0bf528SMauro Carvalho Chehab it under the terms of the GNU General Public License as published by 119a0bf528SMauro Carvalho Chehab the Free Software Foundation; either version 2 of the License, or 129a0bf528SMauro Carvalho Chehab (at your option) any later version. 139a0bf528SMauro Carvalho Chehab 149a0bf528SMauro Carvalho Chehab This program is distributed in the hope that it will be useful, 159a0bf528SMauro Carvalho Chehab but WITHOUT ANY WARRANTY; without even the implied warranty of 169a0bf528SMauro Carvalho Chehab MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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 #include <linux/delay.h> 259a0bf528SMauro Carvalho Chehab #include <linux/errno.h> 269a0bf528SMauro Carvalho Chehab #include <linux/init.h> 279a0bf528SMauro Carvalho Chehab #include <linux/kernel.h> 289a0bf528SMauro Carvalho Chehab #include <linux/module.h> 299a0bf528SMauro Carvalho Chehab #include <linux/string.h> 309a0bf528SMauro Carvalho Chehab #include <linux/slab.h> 319a0bf528SMauro Carvalho Chehab 329a0bf528SMauro Carvalho Chehab #include "dvb_frontend.h" 339a0bf528SMauro Carvalho Chehab #include "tda1002x.h" 349a0bf528SMauro Carvalho Chehab 359a0bf528SMauro Carvalho Chehab 369a0bf528SMauro Carvalho Chehab struct tda10021_state { 379a0bf528SMauro Carvalho Chehab struct i2c_adapter* i2c; 389a0bf528SMauro Carvalho Chehab /* configuration settings */ 399a0bf528SMauro Carvalho Chehab const struct tda1002x_config* config; 409a0bf528SMauro Carvalho Chehab struct dvb_frontend frontend; 419a0bf528SMauro Carvalho Chehab 429a0bf528SMauro Carvalho Chehab u8 pwm; 439a0bf528SMauro Carvalho Chehab u8 reg0; 449a0bf528SMauro Carvalho Chehab }; 459a0bf528SMauro Carvalho Chehab 469a0bf528SMauro Carvalho Chehab 479a0bf528SMauro Carvalho Chehab #if 0 489a0bf528SMauro Carvalho Chehab #define dprintk(x...) printk(x) 499a0bf528SMauro Carvalho Chehab #else 509a0bf528SMauro Carvalho Chehab #define dprintk(x...) 519a0bf528SMauro Carvalho Chehab #endif 529a0bf528SMauro Carvalho Chehab 539a0bf528SMauro Carvalho Chehab static int verbose; 549a0bf528SMauro Carvalho Chehab 559a0bf528SMauro Carvalho Chehab #define XIN 57840000UL 569a0bf528SMauro Carvalho Chehab 579a0bf528SMauro Carvalho Chehab #define FIN (XIN >> 4) 589a0bf528SMauro Carvalho Chehab 599a0bf528SMauro Carvalho Chehab static int tda10021_inittab_size = 0x40; 609a0bf528SMauro Carvalho Chehab static u8 tda10021_inittab[0x40]= 619a0bf528SMauro Carvalho Chehab { 629a0bf528SMauro Carvalho Chehab 0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a, 639a0bf528SMauro Carvalho Chehab 0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40, 649a0bf528SMauro Carvalho Chehab 0xb8, 0x3f, 0xa1, 0x00, 0xcd, 0x01, 0x00, 0xff, 659a0bf528SMauro Carvalho Chehab 0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00, 669a0bf528SMauro Carvalho Chehab 0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 679a0bf528SMauro Carvalho Chehab 0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58, 689a0bf528SMauro Carvalho Chehab 0x00, 0x00, 0x80, 0x00, 0x80, 0xff, 0x00, 0x00, 699a0bf528SMauro Carvalho Chehab 0x04, 0x2d, 0x2f, 0xff, 0x00, 0x00, 0x00, 0x00, 709a0bf528SMauro Carvalho Chehab }; 719a0bf528SMauro Carvalho Chehab 729a0bf528SMauro Carvalho Chehab static int _tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data) 739a0bf528SMauro Carvalho Chehab { 749a0bf528SMauro Carvalho Chehab u8 buf[] = { reg, data }; 759a0bf528SMauro Carvalho Chehab struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; 769a0bf528SMauro Carvalho Chehab int ret; 779a0bf528SMauro Carvalho Chehab 789a0bf528SMauro Carvalho Chehab ret = i2c_transfer (state->i2c, &msg, 1); 799a0bf528SMauro Carvalho Chehab if (ret != 1) 809a0bf528SMauro Carvalho Chehab printk("DVB: TDA10021(%d): %s, writereg error " 819a0bf528SMauro Carvalho Chehab "(reg == 0x%02x, val == 0x%02x, ret == %i)\n", 829a0bf528SMauro Carvalho Chehab state->frontend.dvb->num, __func__, reg, data, ret); 839a0bf528SMauro Carvalho Chehab 849a0bf528SMauro Carvalho Chehab msleep(10); 859a0bf528SMauro Carvalho Chehab return (ret != 1) ? -EREMOTEIO : 0; 869a0bf528SMauro Carvalho Chehab } 879a0bf528SMauro Carvalho Chehab 889a0bf528SMauro Carvalho Chehab static u8 tda10021_readreg (struct tda10021_state* state, u8 reg) 899a0bf528SMauro Carvalho Chehab { 909a0bf528SMauro Carvalho Chehab u8 b0 [] = { reg }; 919a0bf528SMauro Carvalho Chehab u8 b1 [] = { 0 }; 929a0bf528SMauro Carvalho Chehab struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, 939a0bf528SMauro Carvalho Chehab { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; 949a0bf528SMauro Carvalho Chehab int ret; 959a0bf528SMauro Carvalho Chehab 969a0bf528SMauro Carvalho Chehab ret = i2c_transfer (state->i2c, msg, 2); 979a0bf528SMauro Carvalho Chehab // Don't print an error message if the id is read. 989a0bf528SMauro Carvalho Chehab if (ret != 2 && reg != 0x1a) 999a0bf528SMauro Carvalho Chehab printk("DVB: TDA10021: %s: readreg error (ret == %i)\n", 1009a0bf528SMauro Carvalho Chehab __func__, ret); 1019a0bf528SMauro Carvalho Chehab return b1[0]; 1029a0bf528SMauro Carvalho Chehab } 1039a0bf528SMauro Carvalho Chehab 1049a0bf528SMauro Carvalho Chehab //get access to tuner 1059a0bf528SMauro Carvalho Chehab static int lock_tuner(struct tda10021_state* state) 1069a0bf528SMauro Carvalho Chehab { 1079a0bf528SMauro Carvalho Chehab u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] | 0x80 }; 1089a0bf528SMauro Carvalho Chehab struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; 1099a0bf528SMauro Carvalho Chehab 1109a0bf528SMauro Carvalho Chehab if(i2c_transfer(state->i2c, &msg, 1) != 1) 1119a0bf528SMauro Carvalho Chehab { 1129a0bf528SMauro Carvalho Chehab printk("tda10021: lock tuner fails\n"); 1139a0bf528SMauro Carvalho Chehab return -EREMOTEIO; 1149a0bf528SMauro Carvalho Chehab } 1159a0bf528SMauro Carvalho Chehab return 0; 1169a0bf528SMauro Carvalho Chehab } 1179a0bf528SMauro Carvalho Chehab 1189a0bf528SMauro Carvalho Chehab //release access from tuner 1199a0bf528SMauro Carvalho Chehab static int unlock_tuner(struct tda10021_state* state) 1209a0bf528SMauro Carvalho Chehab { 1219a0bf528SMauro Carvalho Chehab u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] & 0x7f }; 1229a0bf528SMauro Carvalho Chehab struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; 1239a0bf528SMauro Carvalho Chehab 1249a0bf528SMauro Carvalho Chehab if(i2c_transfer(state->i2c, &msg_post, 1) != 1) 1259a0bf528SMauro Carvalho Chehab { 1269a0bf528SMauro Carvalho Chehab printk("tda10021: unlock tuner fails\n"); 1279a0bf528SMauro Carvalho Chehab return -EREMOTEIO; 1289a0bf528SMauro Carvalho Chehab } 1299a0bf528SMauro Carvalho Chehab return 0; 1309a0bf528SMauro Carvalho Chehab } 1319a0bf528SMauro Carvalho Chehab 1329a0bf528SMauro Carvalho Chehab static int tda10021_setup_reg0 (struct tda10021_state* state, u8 reg0, 1339a0bf528SMauro Carvalho Chehab fe_spectral_inversion_t inversion) 1349a0bf528SMauro Carvalho Chehab { 1359a0bf528SMauro Carvalho Chehab reg0 |= state->reg0 & 0x63; 1369a0bf528SMauro Carvalho Chehab 1379a0bf528SMauro Carvalho Chehab if ((INVERSION_ON == inversion) ^ (state->config->invert == 0)) 1389a0bf528SMauro Carvalho Chehab reg0 &= ~0x20; 1399a0bf528SMauro Carvalho Chehab else 1409a0bf528SMauro Carvalho Chehab reg0 |= 0x20; 1419a0bf528SMauro Carvalho Chehab 1429a0bf528SMauro Carvalho Chehab _tda10021_writereg (state, 0x00, reg0 & 0xfe); 1439a0bf528SMauro Carvalho Chehab _tda10021_writereg (state, 0x00, reg0 | 0x01); 1449a0bf528SMauro Carvalho Chehab 1459a0bf528SMauro Carvalho Chehab state->reg0 = reg0; 1469a0bf528SMauro Carvalho Chehab return 0; 1479a0bf528SMauro Carvalho Chehab } 1489a0bf528SMauro Carvalho Chehab 1499a0bf528SMauro Carvalho Chehab static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate) 1509a0bf528SMauro Carvalho Chehab { 1519a0bf528SMauro Carvalho Chehab s32 BDR; 1529a0bf528SMauro Carvalho Chehab s32 BDRI; 1539a0bf528SMauro Carvalho Chehab s16 SFIL=0; 1549a0bf528SMauro Carvalho Chehab u16 NDEC = 0; 1559a0bf528SMauro Carvalho Chehab u32 tmp, ratio; 1569a0bf528SMauro Carvalho Chehab 1579a0bf528SMauro Carvalho Chehab if (symbolrate > XIN/2) 1589a0bf528SMauro Carvalho Chehab symbolrate = XIN/2; 1599a0bf528SMauro Carvalho Chehab if (symbolrate < 500000) 1609a0bf528SMauro Carvalho Chehab symbolrate = 500000; 1619a0bf528SMauro Carvalho Chehab 1629a0bf528SMauro Carvalho Chehab if (symbolrate < XIN/16) NDEC = 1; 1639a0bf528SMauro Carvalho Chehab if (symbolrate < XIN/32) NDEC = 2; 1649a0bf528SMauro Carvalho Chehab if (symbolrate < XIN/64) NDEC = 3; 1659a0bf528SMauro Carvalho Chehab 1669a0bf528SMauro Carvalho Chehab if (symbolrate < (u32)(XIN/12.3)) SFIL = 1; 1679a0bf528SMauro Carvalho Chehab if (symbolrate < (u32)(XIN/16)) SFIL = 0; 1689a0bf528SMauro Carvalho Chehab if (symbolrate < (u32)(XIN/24.6)) SFIL = 1; 1699a0bf528SMauro Carvalho Chehab if (symbolrate < (u32)(XIN/32)) SFIL = 0; 1709a0bf528SMauro Carvalho Chehab if (symbolrate < (u32)(XIN/49.2)) SFIL = 1; 1719a0bf528SMauro Carvalho Chehab if (symbolrate < (u32)(XIN/64)) SFIL = 0; 1729a0bf528SMauro Carvalho Chehab if (symbolrate < (u32)(XIN/98.4)) SFIL = 1; 1739a0bf528SMauro Carvalho Chehab 1749a0bf528SMauro Carvalho Chehab symbolrate <<= NDEC; 1759a0bf528SMauro Carvalho Chehab ratio = (symbolrate << 4) / FIN; 1769a0bf528SMauro Carvalho Chehab tmp = ((symbolrate << 4) % FIN) << 8; 1779a0bf528SMauro Carvalho Chehab ratio = (ratio << 8) + tmp / FIN; 1789a0bf528SMauro Carvalho Chehab tmp = (tmp % FIN) << 8; 1799a0bf528SMauro Carvalho Chehab ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, FIN); 1809a0bf528SMauro Carvalho Chehab 1819a0bf528SMauro Carvalho Chehab BDR = ratio; 1829a0bf528SMauro Carvalho Chehab BDRI = (((XIN << 5) / symbolrate) + 1) / 2; 1839a0bf528SMauro Carvalho Chehab 1849a0bf528SMauro Carvalho Chehab if (BDRI > 0xFF) 1859a0bf528SMauro Carvalho Chehab BDRI = 0xFF; 1869a0bf528SMauro Carvalho Chehab 1879a0bf528SMauro Carvalho Chehab SFIL = (SFIL << 4) | tda10021_inittab[0x0E]; 1889a0bf528SMauro Carvalho Chehab 1899a0bf528SMauro Carvalho Chehab NDEC = (NDEC << 6) | tda10021_inittab[0x03]; 1909a0bf528SMauro Carvalho Chehab 1919a0bf528SMauro Carvalho Chehab _tda10021_writereg (state, 0x03, NDEC); 1929a0bf528SMauro Carvalho Chehab _tda10021_writereg (state, 0x0a, BDR&0xff); 1939a0bf528SMauro Carvalho Chehab _tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff); 1949a0bf528SMauro Carvalho Chehab _tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f); 1959a0bf528SMauro Carvalho Chehab 1969a0bf528SMauro Carvalho Chehab _tda10021_writereg (state, 0x0d, BDRI); 1979a0bf528SMauro Carvalho Chehab _tda10021_writereg (state, 0x0e, SFIL); 1989a0bf528SMauro Carvalho Chehab 1999a0bf528SMauro Carvalho Chehab return 0; 2009a0bf528SMauro Carvalho Chehab } 2019a0bf528SMauro Carvalho Chehab 2029a0bf528SMauro Carvalho Chehab static int tda10021_init (struct dvb_frontend *fe) 2039a0bf528SMauro Carvalho Chehab { 2049a0bf528SMauro Carvalho Chehab struct tda10021_state* state = fe->demodulator_priv; 2059a0bf528SMauro Carvalho Chehab int i; 2069a0bf528SMauro Carvalho Chehab 2079a0bf528SMauro Carvalho Chehab dprintk("DVB: TDA10021(%d): init chip\n", fe->adapter->num); 2089a0bf528SMauro Carvalho Chehab 2099a0bf528SMauro Carvalho Chehab //_tda10021_writereg (fe, 0, 0); 2109a0bf528SMauro Carvalho Chehab 2119a0bf528SMauro Carvalho Chehab for (i=0; i<tda10021_inittab_size; i++) 2129a0bf528SMauro Carvalho Chehab _tda10021_writereg (state, i, tda10021_inittab[i]); 2139a0bf528SMauro Carvalho Chehab 2149a0bf528SMauro Carvalho Chehab _tda10021_writereg (state, 0x34, state->pwm); 2159a0bf528SMauro Carvalho Chehab 2169a0bf528SMauro Carvalho Chehab //Comment by markus 2179a0bf528SMauro Carvalho Chehab //0x2A[3-0] == PDIV -> P multiplaying factor (P=PDIV+1)(default 0) 2189a0bf528SMauro Carvalho Chehab //0x2A[4] == BYPPLL -> Power down mode (default 1) 2199a0bf528SMauro Carvalho Chehab //0x2A[5] == LCK -> PLL Lock Flag 2209a0bf528SMauro Carvalho Chehab //0x2A[6] == POLAXIN -> Polarity of the input reference clock (default 0) 2219a0bf528SMauro Carvalho Chehab 2229a0bf528SMauro Carvalho Chehab //Activate PLL 2239a0bf528SMauro Carvalho Chehab _tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef); 2249a0bf528SMauro Carvalho Chehab return 0; 2259a0bf528SMauro Carvalho Chehab } 2269a0bf528SMauro Carvalho Chehab 2279a0bf528SMauro Carvalho Chehab struct qam_params { 2289a0bf528SMauro Carvalho Chehab u8 conf, agcref, lthr, mseth, aref; 2299a0bf528SMauro Carvalho Chehab }; 2309a0bf528SMauro Carvalho Chehab 2319a0bf528SMauro Carvalho Chehab static int tda10021_set_parameters(struct dvb_frontend *fe) 2329a0bf528SMauro Carvalho Chehab { 2339a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *c = &fe->dtv_property_cache; 2349a0bf528SMauro Carvalho Chehab u32 delsys = c->delivery_system; 2359a0bf528SMauro Carvalho Chehab unsigned qam = c->modulation; 2369a0bf528SMauro Carvalho Chehab bool is_annex_c; 2379a0bf528SMauro Carvalho Chehab u32 reg0x3d; 2389a0bf528SMauro Carvalho Chehab struct tda10021_state* state = fe->demodulator_priv; 2399a0bf528SMauro Carvalho Chehab static const struct qam_params qam_params[] = { 2409a0bf528SMauro Carvalho Chehab /* Modulation Conf AGCref LTHR MSETH AREF */ 2419a0bf528SMauro Carvalho Chehab [QPSK] = { 0x14, 0x78, 0x78, 0x8c, 0x96 }, 2429a0bf528SMauro Carvalho Chehab [QAM_16] = { 0x00, 0x8c, 0x87, 0xa2, 0x91 }, 2439a0bf528SMauro Carvalho Chehab [QAM_32] = { 0x04, 0x8c, 0x64, 0x74, 0x96 }, 2449a0bf528SMauro Carvalho Chehab [QAM_64] = { 0x08, 0x6a, 0x46, 0x43, 0x6a }, 2459a0bf528SMauro Carvalho Chehab [QAM_128] = { 0x0c, 0x78, 0x36, 0x34, 0x7e }, 2469a0bf528SMauro Carvalho Chehab [QAM_256] = { 0x10, 0x5c, 0x26, 0x23, 0x6b }, 2479a0bf528SMauro Carvalho Chehab }; 2489a0bf528SMauro Carvalho Chehab 2499a0bf528SMauro Carvalho Chehab switch (delsys) { 2509a0bf528SMauro Carvalho Chehab case SYS_DVBC_ANNEX_A: 2519a0bf528SMauro Carvalho Chehab is_annex_c = false; 2529a0bf528SMauro Carvalho Chehab break; 2539a0bf528SMauro Carvalho Chehab case SYS_DVBC_ANNEX_C: 2549a0bf528SMauro Carvalho Chehab is_annex_c = true; 2559a0bf528SMauro Carvalho Chehab break; 2569a0bf528SMauro Carvalho Chehab default: 2579a0bf528SMauro Carvalho Chehab return -EINVAL; 2589a0bf528SMauro Carvalho Chehab } 2599a0bf528SMauro Carvalho Chehab 2609a0bf528SMauro Carvalho Chehab /* 2619a0bf528SMauro Carvalho Chehab * gcc optimizes the code bellow the same way as it would code: 2629a0bf528SMauro Carvalho Chehab * "if (qam > 5) return -EINVAL;" 2639a0bf528SMauro Carvalho Chehab * Yet, the code is clearer, as it shows what QAM standards are 2649a0bf528SMauro Carvalho Chehab * supported by the driver, and avoids the usage of magic numbers on 2659a0bf528SMauro Carvalho Chehab * it. 2669a0bf528SMauro Carvalho Chehab */ 2679a0bf528SMauro Carvalho Chehab switch (qam) { 2689a0bf528SMauro Carvalho Chehab case QPSK: 2699a0bf528SMauro Carvalho Chehab case QAM_16: 2709a0bf528SMauro Carvalho Chehab case QAM_32: 2719a0bf528SMauro Carvalho Chehab case QAM_64: 2729a0bf528SMauro Carvalho Chehab case QAM_128: 2739a0bf528SMauro Carvalho Chehab case QAM_256: 2749a0bf528SMauro Carvalho Chehab break; 2759a0bf528SMauro Carvalho Chehab default: 2769a0bf528SMauro Carvalho Chehab return -EINVAL; 2779a0bf528SMauro Carvalho Chehab } 2789a0bf528SMauro Carvalho Chehab 2799a0bf528SMauro Carvalho Chehab if (c->inversion != INVERSION_ON && c->inversion != INVERSION_OFF) 2809a0bf528SMauro Carvalho Chehab return -EINVAL; 2819a0bf528SMauro Carvalho Chehab 2829a0bf528SMauro Carvalho Chehab /*printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->symbol_rate);*/ 2839a0bf528SMauro Carvalho Chehab 2849a0bf528SMauro Carvalho Chehab if (fe->ops.tuner_ops.set_params) { 2859a0bf528SMauro Carvalho Chehab fe->ops.tuner_ops.set_params(fe); 2869a0bf528SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); 2879a0bf528SMauro Carvalho Chehab } 2889a0bf528SMauro Carvalho Chehab 2899a0bf528SMauro Carvalho Chehab tda10021_set_symbolrate(state, c->symbol_rate); 2909a0bf528SMauro Carvalho Chehab _tda10021_writereg(state, 0x34, state->pwm); 2919a0bf528SMauro Carvalho Chehab 2929a0bf528SMauro Carvalho Chehab _tda10021_writereg(state, 0x01, qam_params[qam].agcref); 2939a0bf528SMauro Carvalho Chehab _tda10021_writereg(state, 0x05, qam_params[qam].lthr); 2949a0bf528SMauro Carvalho Chehab _tda10021_writereg(state, 0x08, qam_params[qam].mseth); 2959a0bf528SMauro Carvalho Chehab _tda10021_writereg(state, 0x09, qam_params[qam].aref); 2969a0bf528SMauro Carvalho Chehab 2979a0bf528SMauro Carvalho Chehab /* 2989a0bf528SMauro Carvalho Chehab * Bit 0 == 0 means roll-off = 0.15 (Annex A) 2999a0bf528SMauro Carvalho Chehab * == 1 means roll-off = 0.13 (Annex C) 3009a0bf528SMauro Carvalho Chehab */ 3019a0bf528SMauro Carvalho Chehab reg0x3d = tda10021_readreg (state, 0x3d); 3029a0bf528SMauro Carvalho Chehab if (is_annex_c) 3039a0bf528SMauro Carvalho Chehab _tda10021_writereg (state, 0x3d, 0x01 | reg0x3d); 3049a0bf528SMauro Carvalho Chehab else 3059a0bf528SMauro Carvalho Chehab _tda10021_writereg (state, 0x3d, 0xfe & reg0x3d); 3069a0bf528SMauro Carvalho Chehab tda10021_setup_reg0(state, qam_params[qam].conf, c->inversion); 3079a0bf528SMauro Carvalho Chehab 3089a0bf528SMauro Carvalho Chehab return 0; 3099a0bf528SMauro Carvalho Chehab } 3109a0bf528SMauro Carvalho Chehab 3119a0bf528SMauro Carvalho Chehab static int tda10021_read_status(struct dvb_frontend* fe, fe_status_t* status) 3129a0bf528SMauro Carvalho Chehab { 3139a0bf528SMauro Carvalho Chehab struct tda10021_state* state = fe->demodulator_priv; 3149a0bf528SMauro Carvalho Chehab int sync; 3159a0bf528SMauro Carvalho Chehab 3169a0bf528SMauro Carvalho Chehab *status = 0; 3179a0bf528SMauro Carvalho Chehab //0x11[0] == EQALGO -> Equalizer algorithms state 3189a0bf528SMauro Carvalho Chehab //0x11[1] == CARLOCK -> Carrier locked 3199a0bf528SMauro Carvalho Chehab //0x11[2] == FSYNC -> Frame synchronisation 3209a0bf528SMauro Carvalho Chehab //0x11[3] == FEL -> Front End locked 3219a0bf528SMauro Carvalho Chehab //0x11[6] == NODVB -> DVB Mode Information 3229a0bf528SMauro Carvalho Chehab sync = tda10021_readreg (state, 0x11); 3239a0bf528SMauro Carvalho Chehab 3249a0bf528SMauro Carvalho Chehab if (sync & 2) 3259a0bf528SMauro Carvalho Chehab *status |= FE_HAS_SIGNAL|FE_HAS_CARRIER; 3269a0bf528SMauro Carvalho Chehab 3279a0bf528SMauro Carvalho Chehab if (sync & 4) 3289a0bf528SMauro Carvalho Chehab *status |= FE_HAS_SYNC|FE_HAS_VITERBI; 3299a0bf528SMauro Carvalho Chehab 3309a0bf528SMauro Carvalho Chehab if (sync & 8) 3319a0bf528SMauro Carvalho Chehab *status |= FE_HAS_LOCK; 3329a0bf528SMauro Carvalho Chehab 3339a0bf528SMauro Carvalho Chehab return 0; 3349a0bf528SMauro Carvalho Chehab } 3359a0bf528SMauro Carvalho Chehab 3369a0bf528SMauro Carvalho Chehab static int tda10021_read_ber(struct dvb_frontend* fe, u32* ber) 3379a0bf528SMauro Carvalho Chehab { 3389a0bf528SMauro Carvalho Chehab struct tda10021_state* state = fe->demodulator_priv; 3399a0bf528SMauro Carvalho Chehab 3409a0bf528SMauro Carvalho Chehab u32 _ber = tda10021_readreg(state, 0x14) | 3419a0bf528SMauro Carvalho Chehab (tda10021_readreg(state, 0x15) << 8) | 3429a0bf528SMauro Carvalho Chehab ((tda10021_readreg(state, 0x16) & 0x0f) << 16); 3439a0bf528SMauro Carvalho Chehab _tda10021_writereg(state, 0x10, (tda10021_readreg(state, 0x10) & ~0xc0) 3449a0bf528SMauro Carvalho Chehab | (tda10021_inittab[0x10] & 0xc0)); 3459a0bf528SMauro Carvalho Chehab *ber = 10 * _ber; 3469a0bf528SMauro Carvalho Chehab 3479a0bf528SMauro Carvalho Chehab return 0; 3489a0bf528SMauro Carvalho Chehab } 3499a0bf528SMauro Carvalho Chehab 3509a0bf528SMauro Carvalho Chehab static int tda10021_read_signal_strength(struct dvb_frontend* fe, u16* strength) 3519a0bf528SMauro Carvalho Chehab { 3529a0bf528SMauro Carvalho Chehab struct tda10021_state* state = fe->demodulator_priv; 3539a0bf528SMauro Carvalho Chehab 3549a0bf528SMauro Carvalho Chehab u8 config = tda10021_readreg(state, 0x02); 3559a0bf528SMauro Carvalho Chehab u8 gain = tda10021_readreg(state, 0x17); 3569a0bf528SMauro Carvalho Chehab if (config & 0x02) 3579a0bf528SMauro Carvalho Chehab /* the agc value is inverted */ 3589a0bf528SMauro Carvalho Chehab gain = ~gain; 3599a0bf528SMauro Carvalho Chehab *strength = (gain << 8) | gain; 3609a0bf528SMauro Carvalho Chehab 3619a0bf528SMauro Carvalho Chehab return 0; 3629a0bf528SMauro Carvalho Chehab } 3639a0bf528SMauro Carvalho Chehab 3649a0bf528SMauro Carvalho Chehab static int tda10021_read_snr(struct dvb_frontend* fe, u16* snr) 3659a0bf528SMauro Carvalho Chehab { 3669a0bf528SMauro Carvalho Chehab struct tda10021_state* state = fe->demodulator_priv; 3679a0bf528SMauro Carvalho Chehab 3689a0bf528SMauro Carvalho Chehab u8 quality = ~tda10021_readreg(state, 0x18); 3699a0bf528SMauro Carvalho Chehab *snr = (quality << 8) | quality; 3709a0bf528SMauro Carvalho Chehab 3719a0bf528SMauro Carvalho Chehab return 0; 3729a0bf528SMauro Carvalho Chehab } 3739a0bf528SMauro Carvalho Chehab 3749a0bf528SMauro Carvalho Chehab static int tda10021_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) 3759a0bf528SMauro Carvalho Chehab { 3769a0bf528SMauro Carvalho Chehab struct tda10021_state* state = fe->demodulator_priv; 3779a0bf528SMauro Carvalho Chehab 3789a0bf528SMauro Carvalho Chehab *ucblocks = tda10021_readreg (state, 0x13) & 0x7f; 3799a0bf528SMauro Carvalho Chehab if (*ucblocks == 0x7f) 3809a0bf528SMauro Carvalho Chehab *ucblocks = 0xffffffff; 3819a0bf528SMauro Carvalho Chehab 3829a0bf528SMauro Carvalho Chehab /* reset uncorrected block counter */ 3839a0bf528SMauro Carvalho Chehab _tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf); 3849a0bf528SMauro Carvalho Chehab _tda10021_writereg (state, 0x10, tda10021_inittab[0x10]); 3859a0bf528SMauro Carvalho Chehab 3869a0bf528SMauro Carvalho Chehab return 0; 3879a0bf528SMauro Carvalho Chehab } 3889a0bf528SMauro Carvalho Chehab 3899a0bf528SMauro Carvalho Chehab static int tda10021_get_frontend(struct dvb_frontend *fe) 3909a0bf528SMauro Carvalho Chehab { 3919a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *p = &fe->dtv_property_cache; 3929a0bf528SMauro Carvalho Chehab struct tda10021_state* state = fe->demodulator_priv; 3939a0bf528SMauro Carvalho Chehab int sync; 3949a0bf528SMauro Carvalho Chehab s8 afc = 0; 3959a0bf528SMauro Carvalho Chehab 3969a0bf528SMauro Carvalho Chehab sync = tda10021_readreg(state, 0x11); 3979a0bf528SMauro Carvalho Chehab afc = tda10021_readreg(state, 0x19); 3989a0bf528SMauro Carvalho Chehab if (verbose) { 3999a0bf528SMauro Carvalho Chehab /* AFC only valid when carrier has been recovered */ 4009a0bf528SMauro Carvalho Chehab printk(sync & 2 ? "DVB: TDA10021(%d): AFC (%d) %dHz\n" : 4019a0bf528SMauro Carvalho Chehab "DVB: TDA10021(%d): [AFC (%d) %dHz]\n", 4029a0bf528SMauro Carvalho Chehab state->frontend.dvb->num, afc, 4039a0bf528SMauro Carvalho Chehab -((s32)p->symbol_rate * afc) >> 10); 4049a0bf528SMauro Carvalho Chehab } 4059a0bf528SMauro Carvalho Chehab 4069a0bf528SMauro Carvalho Chehab p->inversion = ((state->reg0 & 0x20) == 0x20) ^ (state->config->invert != 0) ? INVERSION_ON : INVERSION_OFF; 4079a0bf528SMauro Carvalho Chehab p->modulation = ((state->reg0 >> 2) & 7) + QAM_16; 4089a0bf528SMauro Carvalho Chehab 4099a0bf528SMauro Carvalho Chehab p->fec_inner = FEC_NONE; 4109a0bf528SMauro Carvalho Chehab p->frequency = ((p->frequency + 31250) / 62500) * 62500; 4119a0bf528SMauro Carvalho Chehab 4129a0bf528SMauro Carvalho Chehab if (sync & 2) 4139a0bf528SMauro Carvalho Chehab p->frequency -= ((s32)p->symbol_rate * afc) >> 10; 4149a0bf528SMauro Carvalho Chehab 4159a0bf528SMauro Carvalho Chehab return 0; 4169a0bf528SMauro Carvalho Chehab } 4179a0bf528SMauro Carvalho Chehab 4189a0bf528SMauro Carvalho Chehab static int tda10021_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) 4199a0bf528SMauro Carvalho Chehab { 4209a0bf528SMauro Carvalho Chehab struct tda10021_state* state = fe->demodulator_priv; 4219a0bf528SMauro Carvalho Chehab 4229a0bf528SMauro Carvalho Chehab if (enable) { 4239a0bf528SMauro Carvalho Chehab lock_tuner(state); 4249a0bf528SMauro Carvalho Chehab } else { 4259a0bf528SMauro Carvalho Chehab unlock_tuner(state); 4269a0bf528SMauro Carvalho Chehab } 4279a0bf528SMauro Carvalho Chehab return 0; 4289a0bf528SMauro Carvalho Chehab } 4299a0bf528SMauro Carvalho Chehab 4309a0bf528SMauro Carvalho Chehab static int tda10021_sleep(struct dvb_frontend* fe) 4319a0bf528SMauro Carvalho Chehab { 4329a0bf528SMauro Carvalho Chehab struct tda10021_state* state = fe->demodulator_priv; 4339a0bf528SMauro Carvalho Chehab 4349a0bf528SMauro Carvalho Chehab _tda10021_writereg (state, 0x1b, 0x02); /* pdown ADC */ 4359a0bf528SMauro Carvalho Chehab _tda10021_writereg (state, 0x00, 0x80); /* standby */ 4369a0bf528SMauro Carvalho Chehab 4379a0bf528SMauro Carvalho Chehab return 0; 4389a0bf528SMauro Carvalho Chehab } 4399a0bf528SMauro Carvalho Chehab 4409a0bf528SMauro Carvalho Chehab static void tda10021_release(struct dvb_frontend* fe) 4419a0bf528SMauro Carvalho Chehab { 4429a0bf528SMauro Carvalho Chehab struct tda10021_state* state = fe->demodulator_priv; 4439a0bf528SMauro Carvalho Chehab kfree(state); 4449a0bf528SMauro Carvalho Chehab } 4459a0bf528SMauro Carvalho Chehab 4469a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops tda10021_ops; 4479a0bf528SMauro Carvalho Chehab 4489a0bf528SMauro Carvalho Chehab struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, 4499a0bf528SMauro Carvalho Chehab struct i2c_adapter* i2c, 4509a0bf528SMauro Carvalho Chehab u8 pwm) 4519a0bf528SMauro Carvalho Chehab { 4529a0bf528SMauro Carvalho Chehab struct tda10021_state* state = NULL; 4539a0bf528SMauro Carvalho Chehab u8 id; 4549a0bf528SMauro Carvalho Chehab 4559a0bf528SMauro Carvalho Chehab /* allocate memory for the internal state */ 4569a0bf528SMauro Carvalho Chehab state = kzalloc(sizeof(struct tda10021_state), GFP_KERNEL); 4579a0bf528SMauro Carvalho Chehab if (state == NULL) goto error; 4589a0bf528SMauro Carvalho Chehab 4599a0bf528SMauro Carvalho Chehab /* setup the state */ 4609a0bf528SMauro Carvalho Chehab state->config = config; 4619a0bf528SMauro Carvalho Chehab state->i2c = i2c; 4629a0bf528SMauro Carvalho Chehab state->pwm = pwm; 4639a0bf528SMauro Carvalho Chehab state->reg0 = tda10021_inittab[0]; 4649a0bf528SMauro Carvalho Chehab 4659a0bf528SMauro Carvalho Chehab /* check if the demod is there */ 4669a0bf528SMauro Carvalho Chehab id = tda10021_readreg(state, 0x1a); 4679a0bf528SMauro Carvalho Chehab if ((id & 0xf0) != 0x70) goto error; 4689a0bf528SMauro Carvalho Chehab 4699a0bf528SMauro Carvalho Chehab /* Don't claim TDA10023 */ 4709a0bf528SMauro Carvalho Chehab if (id == 0x7d) 4719a0bf528SMauro Carvalho Chehab goto error; 4729a0bf528SMauro Carvalho Chehab 4739a0bf528SMauro Carvalho Chehab printk("TDA10021: i2c-addr = 0x%02x, id = 0x%02x\n", 4749a0bf528SMauro Carvalho Chehab state->config->demod_address, id); 4759a0bf528SMauro Carvalho Chehab 4769a0bf528SMauro Carvalho Chehab /* create dvb_frontend */ 4779a0bf528SMauro Carvalho Chehab memcpy(&state->frontend.ops, &tda10021_ops, sizeof(struct dvb_frontend_ops)); 4789a0bf528SMauro Carvalho Chehab state->frontend.demodulator_priv = state; 4799a0bf528SMauro Carvalho Chehab return &state->frontend; 4809a0bf528SMauro Carvalho Chehab 4819a0bf528SMauro Carvalho Chehab error: 4829a0bf528SMauro Carvalho Chehab kfree(state); 4839a0bf528SMauro Carvalho Chehab return NULL; 4849a0bf528SMauro Carvalho Chehab } 4859a0bf528SMauro Carvalho Chehab 4869a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops tda10021_ops = { 4879a0bf528SMauro Carvalho Chehab .delsys = { SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_C }, 4889a0bf528SMauro Carvalho Chehab .info = { 4899a0bf528SMauro Carvalho Chehab .name = "Philips TDA10021 DVB-C", 4909a0bf528SMauro Carvalho Chehab .frequency_stepsize = 62500, 4919a0bf528SMauro Carvalho Chehab .frequency_min = 47000000, 4929a0bf528SMauro Carvalho Chehab .frequency_max = 862000000, 4939a0bf528SMauro Carvalho Chehab .symbol_rate_min = (XIN/2)/64, /* SACLK/64 == (XIN/2)/64 */ 4949a0bf528SMauro Carvalho Chehab .symbol_rate_max = (XIN/2)/4, /* SACLK/4 */ 4959a0bf528SMauro Carvalho Chehab #if 0 4969a0bf528SMauro Carvalho Chehab .frequency_tolerance = ???, 4979a0bf528SMauro Carvalho Chehab .symbol_rate_tolerance = ???, /* ppm */ /* == 8% (spec p. 5) */ 4989a0bf528SMauro Carvalho Chehab #endif 4999a0bf528SMauro Carvalho Chehab .caps = 0x400 | //FE_CAN_QAM_4 5009a0bf528SMauro Carvalho Chehab FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | 5019a0bf528SMauro Carvalho Chehab FE_CAN_QAM_128 | FE_CAN_QAM_256 | 5029a0bf528SMauro Carvalho Chehab FE_CAN_FEC_AUTO 5039a0bf528SMauro Carvalho Chehab }, 5049a0bf528SMauro Carvalho Chehab 5059a0bf528SMauro Carvalho Chehab .release = tda10021_release, 5069a0bf528SMauro Carvalho Chehab 5079a0bf528SMauro Carvalho Chehab .init = tda10021_init, 5089a0bf528SMauro Carvalho Chehab .sleep = tda10021_sleep, 5099a0bf528SMauro Carvalho Chehab .i2c_gate_ctrl = tda10021_i2c_gate_ctrl, 5109a0bf528SMauro Carvalho Chehab 5119a0bf528SMauro Carvalho Chehab .set_frontend = tda10021_set_parameters, 5129a0bf528SMauro Carvalho Chehab .get_frontend = tda10021_get_frontend, 5139a0bf528SMauro Carvalho Chehab 5149a0bf528SMauro Carvalho Chehab .read_status = tda10021_read_status, 5159a0bf528SMauro Carvalho Chehab .read_ber = tda10021_read_ber, 5169a0bf528SMauro Carvalho Chehab .read_signal_strength = tda10021_read_signal_strength, 5179a0bf528SMauro Carvalho Chehab .read_snr = tda10021_read_snr, 5189a0bf528SMauro Carvalho Chehab .read_ucblocks = tda10021_read_ucblocks, 5199a0bf528SMauro Carvalho Chehab }; 5209a0bf528SMauro Carvalho Chehab 5219a0bf528SMauro Carvalho Chehab module_param(verbose, int, 0644); 5229a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting"); 5239a0bf528SMauro Carvalho Chehab 5249a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("Philips TDA10021 DVB-C demodulator driver"); 5259a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Markus Schulz"); 5269a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 5279a0bf528SMauro Carvalho Chehab 5289a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(tda10021_attach); 529