19a0bf528SMauro Carvalho Chehab /* 29a0bf528SMauro Carvalho Chehab * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator 39a0bf528SMauro Carvalho Chehab * ATBM8830, ATBM8831 49a0bf528SMauro Carvalho Chehab * 59a0bf528SMauro Carvalho Chehab * Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com> 69a0bf528SMauro Carvalho Chehab * 79a0bf528SMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 89a0bf528SMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 99a0bf528SMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 109a0bf528SMauro Carvalho Chehab * (at your option) any later version. 119a0bf528SMauro Carvalho Chehab * 129a0bf528SMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 139a0bf528SMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 149a0bf528SMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 159a0bf528SMauro Carvalho Chehab * GNU General Public License for more details. 169a0bf528SMauro Carvalho Chehab * 179a0bf528SMauro Carvalho Chehab * You should have received a copy of the GNU General Public License 189a0bf528SMauro Carvalho Chehab * along with this program; if not, write to the Free Software 199a0bf528SMauro Carvalho Chehab * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 209a0bf528SMauro Carvalho Chehab */ 219a0bf528SMauro Carvalho Chehab 229a0bf528SMauro Carvalho Chehab #include <asm/div64.h> 239a0bf528SMauro Carvalho Chehab #include "dvb_frontend.h" 249a0bf528SMauro Carvalho Chehab 259a0bf528SMauro Carvalho Chehab #include "atbm8830.h" 269a0bf528SMauro Carvalho Chehab #include "atbm8830_priv.h" 279a0bf528SMauro Carvalho Chehab 289a0bf528SMauro Carvalho Chehab #define dprintk(args...) \ 299a0bf528SMauro Carvalho Chehab do { \ 309a0bf528SMauro Carvalho Chehab if (debug) \ 319a0bf528SMauro Carvalho Chehab printk(KERN_DEBUG "atbm8830: " args); \ 329a0bf528SMauro Carvalho Chehab } while (0) 339a0bf528SMauro Carvalho Chehab 349a0bf528SMauro Carvalho Chehab static int debug; 359a0bf528SMauro Carvalho Chehab 369a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644); 379a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); 389a0bf528SMauro Carvalho Chehab 399a0bf528SMauro Carvalho Chehab static int atbm8830_write_reg(struct atbm_state *priv, u16 reg, u8 data) 409a0bf528SMauro Carvalho Chehab { 419a0bf528SMauro Carvalho Chehab int ret = 0; 429a0bf528SMauro Carvalho Chehab u8 dev_addr; 439a0bf528SMauro Carvalho Chehab u8 buf1[] = { reg >> 8, reg & 0xFF }; 449a0bf528SMauro Carvalho Chehab u8 buf2[] = { data }; 459a0bf528SMauro Carvalho Chehab struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; 469a0bf528SMauro Carvalho Chehab struct i2c_msg msg2 = { .flags = 0, .buf = buf2, .len = 1 }; 479a0bf528SMauro Carvalho Chehab 489a0bf528SMauro Carvalho Chehab dev_addr = priv->config->demod_address; 499a0bf528SMauro Carvalho Chehab msg1.addr = dev_addr; 509a0bf528SMauro Carvalho Chehab msg2.addr = dev_addr; 519a0bf528SMauro Carvalho Chehab 529a0bf528SMauro Carvalho Chehab if (debug >= 2) 539a0bf528SMauro Carvalho Chehab dprintk("%s: reg=0x%04X, data=0x%02X\n", __func__, reg, data); 549a0bf528SMauro Carvalho Chehab 559a0bf528SMauro Carvalho Chehab ret = i2c_transfer(priv->i2c, &msg1, 1); 569a0bf528SMauro Carvalho Chehab if (ret != 1) 579a0bf528SMauro Carvalho Chehab return -EIO; 589a0bf528SMauro Carvalho Chehab 599a0bf528SMauro Carvalho Chehab ret = i2c_transfer(priv->i2c, &msg2, 1); 609a0bf528SMauro Carvalho Chehab return (ret != 1) ? -EIO : 0; 619a0bf528SMauro Carvalho Chehab } 629a0bf528SMauro Carvalho Chehab 639a0bf528SMauro Carvalho Chehab static int atbm8830_read_reg(struct atbm_state *priv, u16 reg, u8 *p_data) 649a0bf528SMauro Carvalho Chehab { 659a0bf528SMauro Carvalho Chehab int ret; 669a0bf528SMauro Carvalho Chehab u8 dev_addr; 679a0bf528SMauro Carvalho Chehab 689a0bf528SMauro Carvalho Chehab u8 buf1[] = { reg >> 8, reg & 0xFF }; 699a0bf528SMauro Carvalho Chehab u8 buf2[] = { 0 }; 709a0bf528SMauro Carvalho Chehab struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; 719a0bf528SMauro Carvalho Chehab struct i2c_msg msg2 = { .flags = I2C_M_RD, .buf = buf2, .len = 1 }; 729a0bf528SMauro Carvalho Chehab 739a0bf528SMauro Carvalho Chehab dev_addr = priv->config->demod_address; 749a0bf528SMauro Carvalho Chehab msg1.addr = dev_addr; 759a0bf528SMauro Carvalho Chehab msg2.addr = dev_addr; 769a0bf528SMauro Carvalho Chehab 779a0bf528SMauro Carvalho Chehab ret = i2c_transfer(priv->i2c, &msg1, 1); 789a0bf528SMauro Carvalho Chehab if (ret != 1) { 799a0bf528SMauro Carvalho Chehab dprintk("%s: error reg=0x%04x, ret=%i\n", __func__, reg, ret); 809a0bf528SMauro Carvalho Chehab return -EIO; 819a0bf528SMauro Carvalho Chehab } 829a0bf528SMauro Carvalho Chehab 839a0bf528SMauro Carvalho Chehab ret = i2c_transfer(priv->i2c, &msg2, 1); 849a0bf528SMauro Carvalho Chehab if (ret != 1) 859a0bf528SMauro Carvalho Chehab return -EIO; 869a0bf528SMauro Carvalho Chehab 879a0bf528SMauro Carvalho Chehab *p_data = buf2[0]; 889a0bf528SMauro Carvalho Chehab if (debug >= 2) 899a0bf528SMauro Carvalho Chehab dprintk("%s: reg=0x%04X, data=0x%02X\n", 909a0bf528SMauro Carvalho Chehab __func__, reg, buf2[0]); 919a0bf528SMauro Carvalho Chehab 929a0bf528SMauro Carvalho Chehab return 0; 939a0bf528SMauro Carvalho Chehab } 949a0bf528SMauro Carvalho Chehab 959a0bf528SMauro Carvalho Chehab /* Lock register latch so that multi-register read is atomic */ 969a0bf528SMauro Carvalho Chehab static inline int atbm8830_reglatch_lock(struct atbm_state *priv, int lock) 979a0bf528SMauro Carvalho Chehab { 989a0bf528SMauro Carvalho Chehab return atbm8830_write_reg(priv, REG_READ_LATCH, lock ? 1 : 0); 999a0bf528SMauro Carvalho Chehab } 1009a0bf528SMauro Carvalho Chehab 1019a0bf528SMauro Carvalho Chehab static int set_osc_freq(struct atbm_state *priv, u32 freq /*in kHz*/) 1029a0bf528SMauro Carvalho Chehab { 1039a0bf528SMauro Carvalho Chehab u32 val; 1049a0bf528SMauro Carvalho Chehab u64 t; 1059a0bf528SMauro Carvalho Chehab 1069a0bf528SMauro Carvalho Chehab /* 0x100000 * freq / 30.4MHz */ 1079a0bf528SMauro Carvalho Chehab t = (u64)0x100000 * freq; 1089a0bf528SMauro Carvalho Chehab do_div(t, 30400); 1099a0bf528SMauro Carvalho Chehab val = t; 1109a0bf528SMauro Carvalho Chehab 1119a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_OSC_CLK, val); 1129a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_OSC_CLK + 1, val >> 8); 1139a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_OSC_CLK + 2, val >> 16); 1149a0bf528SMauro Carvalho Chehab 1159a0bf528SMauro Carvalho Chehab return 0; 1169a0bf528SMauro Carvalho Chehab } 1179a0bf528SMauro Carvalho Chehab 1189a0bf528SMauro Carvalho Chehab static int set_if_freq(struct atbm_state *priv, u32 freq /*in kHz*/) 1199a0bf528SMauro Carvalho Chehab { 1209a0bf528SMauro Carvalho Chehab 1219a0bf528SMauro Carvalho Chehab u32 fs = priv->config->osc_clk_freq; 1229a0bf528SMauro Carvalho Chehab u64 t; 1239a0bf528SMauro Carvalho Chehab u32 val; 1249a0bf528SMauro Carvalho Chehab u8 dat; 1259a0bf528SMauro Carvalho Chehab 1269a0bf528SMauro Carvalho Chehab if (freq != 0) { 1279a0bf528SMauro Carvalho Chehab /* 2 * PI * (freq - fs) / fs * (2 ^ 22) */ 1289a0bf528SMauro Carvalho Chehab t = (u64) 2 * 31416 * (freq - fs); 1299a0bf528SMauro Carvalho Chehab t <<= 22; 1309a0bf528SMauro Carvalho Chehab do_div(t, fs); 1319a0bf528SMauro Carvalho Chehab do_div(t, 1000); 1329a0bf528SMauro Carvalho Chehab val = t; 1339a0bf528SMauro Carvalho Chehab 1349a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 1); 1359a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_IF_FREQ, val); 1369a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_IF_FREQ+1, val >> 8); 1379a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_IF_FREQ+2, val >> 16); 1389a0bf528SMauro Carvalho Chehab 1399a0bf528SMauro Carvalho Chehab atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); 1409a0bf528SMauro Carvalho Chehab dat &= 0xFC; 1419a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); 1429a0bf528SMauro Carvalho Chehab } else { 1439a0bf528SMauro Carvalho Chehab /* Zero IF */ 1449a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 0); 1459a0bf528SMauro Carvalho Chehab 1469a0bf528SMauro Carvalho Chehab atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); 1479a0bf528SMauro Carvalho Chehab dat &= 0xFC; 1489a0bf528SMauro Carvalho Chehab dat |= 0x02; 1499a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); 1509a0bf528SMauro Carvalho Chehab 1519a0bf528SMauro Carvalho Chehab if (priv->config->zif_swap_iq) 1529a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x03); 1539a0bf528SMauro Carvalho Chehab else 1549a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x01); 1559a0bf528SMauro Carvalho Chehab } 1569a0bf528SMauro Carvalho Chehab 1579a0bf528SMauro Carvalho Chehab return 0; 1589a0bf528SMauro Carvalho Chehab } 1599a0bf528SMauro Carvalho Chehab 1609a0bf528SMauro Carvalho Chehab static int is_locked(struct atbm_state *priv, u8 *locked) 1619a0bf528SMauro Carvalho Chehab { 1629a0bf528SMauro Carvalho Chehab u8 status; 1639a0bf528SMauro Carvalho Chehab 1649a0bf528SMauro Carvalho Chehab atbm8830_read_reg(priv, REG_LOCK_STATUS, &status); 1659a0bf528SMauro Carvalho Chehab 1669a0bf528SMauro Carvalho Chehab if (locked != NULL) 1679a0bf528SMauro Carvalho Chehab *locked = (status == 1); 1689a0bf528SMauro Carvalho Chehab return 0; 1699a0bf528SMauro Carvalho Chehab } 1709a0bf528SMauro Carvalho Chehab 1719a0bf528SMauro Carvalho Chehab static int set_agc_config(struct atbm_state *priv, 1729a0bf528SMauro Carvalho Chehab u8 min, u8 max, u8 hold_loop) 1739a0bf528SMauro Carvalho Chehab { 1749a0bf528SMauro Carvalho Chehab /* no effect if both min and max are zero */ 1759a0bf528SMauro Carvalho Chehab if (!min && !max) 1769a0bf528SMauro Carvalho Chehab return 0; 1779a0bf528SMauro Carvalho Chehab 1789a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_AGC_MIN, min); 1799a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_AGC_MAX, max); 1809a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_AGC_HOLD_LOOP, hold_loop); 1819a0bf528SMauro Carvalho Chehab 1829a0bf528SMauro Carvalho Chehab return 0; 1839a0bf528SMauro Carvalho Chehab } 1849a0bf528SMauro Carvalho Chehab 1859a0bf528SMauro Carvalho Chehab static int set_static_channel_mode(struct atbm_state *priv) 1869a0bf528SMauro Carvalho Chehab { 1879a0bf528SMauro Carvalho Chehab int i; 1889a0bf528SMauro Carvalho Chehab 1899a0bf528SMauro Carvalho Chehab for (i = 0; i < 5; i++) 1909a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x099B + i, 0x08); 1919a0bf528SMauro Carvalho Chehab 1929a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x095B, 0x7F); 1939a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x09CB, 0x01); 1949a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x09CC, 0x7F); 1959a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x09CD, 0x7F); 1969a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x0E01, 0x20); 1979a0bf528SMauro Carvalho Chehab 1989a0bf528SMauro Carvalho Chehab /* For single carrier */ 1999a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x0B03, 0x0A); 2009a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x0935, 0x10); 2019a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x0936, 0x08); 2029a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x093E, 0x08); 2039a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x096E, 0x06); 2049a0bf528SMauro Carvalho Chehab 2059a0bf528SMauro Carvalho Chehab /* frame_count_max0 */ 2069a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x0B09, 0x00); 2079a0bf528SMauro Carvalho Chehab /* frame_count_max1 */ 2089a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x0B0A, 0x08); 2099a0bf528SMauro Carvalho Chehab 2109a0bf528SMauro Carvalho Chehab return 0; 2119a0bf528SMauro Carvalho Chehab } 2129a0bf528SMauro Carvalho Chehab 2139a0bf528SMauro Carvalho Chehab static int set_ts_config(struct atbm_state *priv) 2149a0bf528SMauro Carvalho Chehab { 2159a0bf528SMauro Carvalho Chehab const struct atbm8830_config *cfg = priv->config; 2169a0bf528SMauro Carvalho Chehab 2179a0bf528SMauro Carvalho Chehab /*Set parallel/serial ts mode*/ 2189a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_TS_SERIAL, cfg->serial_ts ? 1 : 0); 2199a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_TS_CLK_MODE, cfg->serial_ts ? 1 : 0); 2209a0bf528SMauro Carvalho Chehab /*Set ts sampling edge*/ 2219a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_TS_SAMPLE_EDGE, 2229a0bf528SMauro Carvalho Chehab cfg->ts_sampling_edge ? 1 : 0); 2239a0bf528SMauro Carvalho Chehab /*Set ts clock freerun*/ 2249a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_TS_CLK_FREERUN, 2259a0bf528SMauro Carvalho Chehab cfg->ts_clk_gated ? 0 : 1); 2269a0bf528SMauro Carvalho Chehab 2279a0bf528SMauro Carvalho Chehab return 0; 2289a0bf528SMauro Carvalho Chehab } 2299a0bf528SMauro Carvalho Chehab 2309a0bf528SMauro Carvalho Chehab static int atbm8830_init(struct dvb_frontend *fe) 2319a0bf528SMauro Carvalho Chehab { 2329a0bf528SMauro Carvalho Chehab struct atbm_state *priv = fe->demodulator_priv; 2339a0bf528SMauro Carvalho Chehab const struct atbm8830_config *cfg = priv->config; 2349a0bf528SMauro Carvalho Chehab 2359a0bf528SMauro Carvalho Chehab /*Set oscillator frequency*/ 2369a0bf528SMauro Carvalho Chehab set_osc_freq(priv, cfg->osc_clk_freq); 2379a0bf528SMauro Carvalho Chehab 2389a0bf528SMauro Carvalho Chehab /*Set IF frequency*/ 2399a0bf528SMauro Carvalho Chehab set_if_freq(priv, cfg->if_freq); 2409a0bf528SMauro Carvalho Chehab 2419a0bf528SMauro Carvalho Chehab /*Set AGC Config*/ 2429a0bf528SMauro Carvalho Chehab set_agc_config(priv, cfg->agc_min, cfg->agc_max, 2439a0bf528SMauro Carvalho Chehab cfg->agc_hold_loop); 2449a0bf528SMauro Carvalho Chehab 2459a0bf528SMauro Carvalho Chehab /*Set static channel mode*/ 2469a0bf528SMauro Carvalho Chehab set_static_channel_mode(priv); 2479a0bf528SMauro Carvalho Chehab 2489a0bf528SMauro Carvalho Chehab set_ts_config(priv); 2499a0bf528SMauro Carvalho Chehab /*Turn off DSP reset*/ 2509a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x000A, 0); 2519a0bf528SMauro Carvalho Chehab 2529a0bf528SMauro Carvalho Chehab /*SW version test*/ 2539a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x020C, 11); 2549a0bf528SMauro Carvalho Chehab 2559a0bf528SMauro Carvalho Chehab /* Run */ 2569a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_DEMOD_RUN, 1); 2579a0bf528SMauro Carvalho Chehab 2589a0bf528SMauro Carvalho Chehab return 0; 2599a0bf528SMauro Carvalho Chehab } 2609a0bf528SMauro Carvalho Chehab 2619a0bf528SMauro Carvalho Chehab 2629a0bf528SMauro Carvalho Chehab static void atbm8830_release(struct dvb_frontend *fe) 2639a0bf528SMauro Carvalho Chehab { 2649a0bf528SMauro Carvalho Chehab struct atbm_state *state = fe->demodulator_priv; 2659a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 2669a0bf528SMauro Carvalho Chehab 2679a0bf528SMauro Carvalho Chehab kfree(state); 2689a0bf528SMauro Carvalho Chehab } 2699a0bf528SMauro Carvalho Chehab 2709a0bf528SMauro Carvalho Chehab static int atbm8830_set_fe(struct dvb_frontend *fe) 2719a0bf528SMauro Carvalho Chehab { 2729a0bf528SMauro Carvalho Chehab struct atbm_state *priv = fe->demodulator_priv; 2739a0bf528SMauro Carvalho Chehab int i; 2749a0bf528SMauro Carvalho Chehab u8 locked = 0; 2759a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 2769a0bf528SMauro Carvalho Chehab 2779a0bf528SMauro Carvalho Chehab /* set frequency */ 2789a0bf528SMauro Carvalho Chehab if (fe->ops.tuner_ops.set_params) { 2799a0bf528SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 2809a0bf528SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 1); 2819a0bf528SMauro Carvalho Chehab fe->ops.tuner_ops.set_params(fe); 2829a0bf528SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 2839a0bf528SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); 2849a0bf528SMauro Carvalho Chehab } 2859a0bf528SMauro Carvalho Chehab 2869a0bf528SMauro Carvalho Chehab /* start auto lock */ 2879a0bf528SMauro Carvalho Chehab for (i = 0; i < 10; i++) { 2889a0bf528SMauro Carvalho Chehab mdelay(100); 2899a0bf528SMauro Carvalho Chehab dprintk("Try %d\n", i); 2909a0bf528SMauro Carvalho Chehab is_locked(priv, &locked); 2919a0bf528SMauro Carvalho Chehab if (locked != 0) { 2929a0bf528SMauro Carvalho Chehab dprintk("ATBM8830 locked!\n"); 2939a0bf528SMauro Carvalho Chehab break; 2949a0bf528SMauro Carvalho Chehab } 2959a0bf528SMauro Carvalho Chehab } 2969a0bf528SMauro Carvalho Chehab 2979a0bf528SMauro Carvalho Chehab return 0; 2989a0bf528SMauro Carvalho Chehab } 2999a0bf528SMauro Carvalho Chehab 300*7e3e68bcSMauro Carvalho Chehab static int atbm8830_get_fe(struct dvb_frontend *fe, 301*7e3e68bcSMauro Carvalho Chehab struct dtv_frontend_properties *c) 3029a0bf528SMauro Carvalho Chehab { 3039a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 3049a0bf528SMauro Carvalho Chehab 3059a0bf528SMauro Carvalho Chehab /* TODO: get real readings from device */ 3069a0bf528SMauro Carvalho Chehab /* inversion status */ 3079a0bf528SMauro Carvalho Chehab c->inversion = INVERSION_OFF; 3089a0bf528SMauro Carvalho Chehab 3099a0bf528SMauro Carvalho Chehab /* bandwidth */ 3109a0bf528SMauro Carvalho Chehab c->bandwidth_hz = 8000000; 3119a0bf528SMauro Carvalho Chehab 3129a0bf528SMauro Carvalho Chehab c->code_rate_HP = FEC_AUTO; 3139a0bf528SMauro Carvalho Chehab c->code_rate_LP = FEC_AUTO; 3149a0bf528SMauro Carvalho Chehab 3159a0bf528SMauro Carvalho Chehab c->modulation = QAM_AUTO; 3169a0bf528SMauro Carvalho Chehab 3179a0bf528SMauro Carvalho Chehab /* transmission mode */ 3189a0bf528SMauro Carvalho Chehab c->transmission_mode = TRANSMISSION_MODE_AUTO; 3199a0bf528SMauro Carvalho Chehab 3209a0bf528SMauro Carvalho Chehab /* guard interval */ 3219a0bf528SMauro Carvalho Chehab c->guard_interval = GUARD_INTERVAL_AUTO; 3229a0bf528SMauro Carvalho Chehab 3239a0bf528SMauro Carvalho Chehab /* hierarchy */ 3249a0bf528SMauro Carvalho Chehab c->hierarchy = HIERARCHY_NONE; 3259a0bf528SMauro Carvalho Chehab 3269a0bf528SMauro Carvalho Chehab return 0; 3279a0bf528SMauro Carvalho Chehab } 3289a0bf528SMauro Carvalho Chehab 3299a0bf528SMauro Carvalho Chehab static int atbm8830_get_tune_settings(struct dvb_frontend *fe, 3309a0bf528SMauro Carvalho Chehab struct dvb_frontend_tune_settings *fesettings) 3319a0bf528SMauro Carvalho Chehab { 3329a0bf528SMauro Carvalho Chehab fesettings->min_delay_ms = 0; 3339a0bf528SMauro Carvalho Chehab fesettings->step_size = 0; 3349a0bf528SMauro Carvalho Chehab fesettings->max_drift = 0; 3359a0bf528SMauro Carvalho Chehab return 0; 3369a0bf528SMauro Carvalho Chehab } 3379a0bf528SMauro Carvalho Chehab 3380df289a2SMauro Carvalho Chehab static int atbm8830_read_status(struct dvb_frontend *fe, 3390df289a2SMauro Carvalho Chehab enum fe_status *fe_status) 3409a0bf528SMauro Carvalho Chehab { 3419a0bf528SMauro Carvalho Chehab struct atbm_state *priv = fe->demodulator_priv; 3429a0bf528SMauro Carvalho Chehab u8 locked = 0; 3439a0bf528SMauro Carvalho Chehab u8 agc_locked = 0; 3449a0bf528SMauro Carvalho Chehab 3459a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 3469a0bf528SMauro Carvalho Chehab *fe_status = 0; 3479a0bf528SMauro Carvalho Chehab 3489a0bf528SMauro Carvalho Chehab is_locked(priv, &locked); 3499a0bf528SMauro Carvalho Chehab if (locked) { 3509a0bf528SMauro Carvalho Chehab *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | 3519a0bf528SMauro Carvalho Chehab FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; 3529a0bf528SMauro Carvalho Chehab } 3539a0bf528SMauro Carvalho Chehab dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); 3549a0bf528SMauro Carvalho Chehab 3559a0bf528SMauro Carvalho Chehab atbm8830_read_reg(priv, REG_AGC_LOCK, &agc_locked); 3569a0bf528SMauro Carvalho Chehab dprintk("AGC Lock: %d\n", agc_locked); 3579a0bf528SMauro Carvalho Chehab 3589a0bf528SMauro Carvalho Chehab return 0; 3599a0bf528SMauro Carvalho Chehab } 3609a0bf528SMauro Carvalho Chehab 3619a0bf528SMauro Carvalho Chehab static int atbm8830_read_ber(struct dvb_frontend *fe, u32 *ber) 3629a0bf528SMauro Carvalho Chehab { 3639a0bf528SMauro Carvalho Chehab struct atbm_state *priv = fe->demodulator_priv; 3649a0bf528SMauro Carvalho Chehab u32 frame_err; 3659a0bf528SMauro Carvalho Chehab u8 t; 3669a0bf528SMauro Carvalho Chehab 3679a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 3689a0bf528SMauro Carvalho Chehab 3699a0bf528SMauro Carvalho Chehab atbm8830_reglatch_lock(priv, 1); 3709a0bf528SMauro Carvalho Chehab 3719a0bf528SMauro Carvalho Chehab atbm8830_read_reg(priv, REG_FRAME_ERR_CNT + 1, &t); 3729a0bf528SMauro Carvalho Chehab frame_err = t & 0x7F; 3739a0bf528SMauro Carvalho Chehab frame_err <<= 8; 3749a0bf528SMauro Carvalho Chehab atbm8830_read_reg(priv, REG_FRAME_ERR_CNT, &t); 3759a0bf528SMauro Carvalho Chehab frame_err |= t; 3769a0bf528SMauro Carvalho Chehab 3779a0bf528SMauro Carvalho Chehab atbm8830_reglatch_lock(priv, 0); 3789a0bf528SMauro Carvalho Chehab 3799a0bf528SMauro Carvalho Chehab *ber = frame_err * 100 / 32767; 3809a0bf528SMauro Carvalho Chehab 3819a0bf528SMauro Carvalho Chehab dprintk("%s: ber=0x%x\n", __func__, *ber); 3829a0bf528SMauro Carvalho Chehab return 0; 3839a0bf528SMauro Carvalho Chehab } 3849a0bf528SMauro Carvalho Chehab 3859a0bf528SMauro Carvalho Chehab static int atbm8830_read_signal_strength(struct dvb_frontend *fe, u16 *signal) 3869a0bf528SMauro Carvalho Chehab { 3879a0bf528SMauro Carvalho Chehab struct atbm_state *priv = fe->demodulator_priv; 3889a0bf528SMauro Carvalho Chehab u32 pwm; 3899a0bf528SMauro Carvalho Chehab u8 t; 3909a0bf528SMauro Carvalho Chehab 3919a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 3929a0bf528SMauro Carvalho Chehab atbm8830_reglatch_lock(priv, 1); 3939a0bf528SMauro Carvalho Chehab 3949a0bf528SMauro Carvalho Chehab atbm8830_read_reg(priv, REG_AGC_PWM_VAL + 1, &t); 3959a0bf528SMauro Carvalho Chehab pwm = t & 0x03; 3969a0bf528SMauro Carvalho Chehab pwm <<= 8; 3979a0bf528SMauro Carvalho Chehab atbm8830_read_reg(priv, REG_AGC_PWM_VAL, &t); 3989a0bf528SMauro Carvalho Chehab pwm |= t; 3999a0bf528SMauro Carvalho Chehab 4009a0bf528SMauro Carvalho Chehab atbm8830_reglatch_lock(priv, 0); 4019a0bf528SMauro Carvalho Chehab 4029a0bf528SMauro Carvalho Chehab dprintk("AGC PWM = 0x%02X\n", pwm); 4039a0bf528SMauro Carvalho Chehab pwm = 0x400 - pwm; 4049a0bf528SMauro Carvalho Chehab 4059a0bf528SMauro Carvalho Chehab *signal = pwm * 0x10000 / 0x400; 4069a0bf528SMauro Carvalho Chehab 4079a0bf528SMauro Carvalho Chehab return 0; 4089a0bf528SMauro Carvalho Chehab } 4099a0bf528SMauro Carvalho Chehab 4109a0bf528SMauro Carvalho Chehab static int atbm8830_read_snr(struct dvb_frontend *fe, u16 *snr) 4119a0bf528SMauro Carvalho Chehab { 4129a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 4139a0bf528SMauro Carvalho Chehab *snr = 0; 4149a0bf528SMauro Carvalho Chehab return 0; 4159a0bf528SMauro Carvalho Chehab } 4169a0bf528SMauro Carvalho Chehab 4179a0bf528SMauro Carvalho Chehab static int atbm8830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) 4189a0bf528SMauro Carvalho Chehab { 4199a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 4209a0bf528SMauro Carvalho Chehab *ucblocks = 0; 4219a0bf528SMauro Carvalho Chehab return 0; 4229a0bf528SMauro Carvalho Chehab } 4239a0bf528SMauro Carvalho Chehab 4249a0bf528SMauro Carvalho Chehab static int atbm8830_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) 4259a0bf528SMauro Carvalho Chehab { 4269a0bf528SMauro Carvalho Chehab struct atbm_state *priv = fe->demodulator_priv; 4279a0bf528SMauro Carvalho Chehab 4289a0bf528SMauro Carvalho Chehab return atbm8830_write_reg(priv, REG_I2C_GATE, enable ? 1 : 0); 4299a0bf528SMauro Carvalho Chehab } 4309a0bf528SMauro Carvalho Chehab 4319a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops atbm8830_ops = { 4329a0bf528SMauro Carvalho Chehab .delsys = { SYS_DTMB }, 4339a0bf528SMauro Carvalho Chehab .info = { 4349a0bf528SMauro Carvalho Chehab .name = "AltoBeam ATBM8830/8831 DMB-TH", 4359a0bf528SMauro Carvalho Chehab .frequency_min = 474000000, 4369a0bf528SMauro Carvalho Chehab .frequency_max = 858000000, 4379a0bf528SMauro Carvalho Chehab .frequency_stepsize = 10000, 4389a0bf528SMauro Carvalho Chehab .caps = 4399a0bf528SMauro Carvalho Chehab FE_CAN_FEC_AUTO | 4409a0bf528SMauro Carvalho Chehab FE_CAN_QAM_AUTO | 4419a0bf528SMauro Carvalho Chehab FE_CAN_TRANSMISSION_MODE_AUTO | 4429a0bf528SMauro Carvalho Chehab FE_CAN_GUARD_INTERVAL_AUTO 4439a0bf528SMauro Carvalho Chehab }, 4449a0bf528SMauro Carvalho Chehab 4459a0bf528SMauro Carvalho Chehab .release = atbm8830_release, 4469a0bf528SMauro Carvalho Chehab 4479a0bf528SMauro Carvalho Chehab .init = atbm8830_init, 4489a0bf528SMauro Carvalho Chehab .sleep = NULL, 4499a0bf528SMauro Carvalho Chehab .write = NULL, 4509a0bf528SMauro Carvalho Chehab .i2c_gate_ctrl = atbm8830_i2c_gate_ctrl, 4519a0bf528SMauro Carvalho Chehab 4529a0bf528SMauro Carvalho Chehab .set_frontend = atbm8830_set_fe, 4539a0bf528SMauro Carvalho Chehab .get_frontend = atbm8830_get_fe, 4549a0bf528SMauro Carvalho Chehab .get_tune_settings = atbm8830_get_tune_settings, 4559a0bf528SMauro Carvalho Chehab 4569a0bf528SMauro Carvalho Chehab .read_status = atbm8830_read_status, 4579a0bf528SMauro Carvalho Chehab .read_ber = atbm8830_read_ber, 4589a0bf528SMauro Carvalho Chehab .read_signal_strength = atbm8830_read_signal_strength, 4599a0bf528SMauro Carvalho Chehab .read_snr = atbm8830_read_snr, 4609a0bf528SMauro Carvalho Chehab .read_ucblocks = atbm8830_read_ucblocks, 4619a0bf528SMauro Carvalho Chehab }; 4629a0bf528SMauro Carvalho Chehab 4639a0bf528SMauro Carvalho Chehab struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, 4649a0bf528SMauro Carvalho Chehab struct i2c_adapter *i2c) 4659a0bf528SMauro Carvalho Chehab { 4669a0bf528SMauro Carvalho Chehab struct atbm_state *priv = NULL; 4679a0bf528SMauro Carvalho Chehab u8 data = 0; 4689a0bf528SMauro Carvalho Chehab 4699a0bf528SMauro Carvalho Chehab dprintk("%s()\n", __func__); 4709a0bf528SMauro Carvalho Chehab 4719a0bf528SMauro Carvalho Chehab if (config == NULL || i2c == NULL) 4729a0bf528SMauro Carvalho Chehab return NULL; 4739a0bf528SMauro Carvalho Chehab 4749a0bf528SMauro Carvalho Chehab priv = kzalloc(sizeof(struct atbm_state), GFP_KERNEL); 4759a0bf528SMauro Carvalho Chehab if (priv == NULL) 4769a0bf528SMauro Carvalho Chehab goto error_out; 4779a0bf528SMauro Carvalho Chehab 4789a0bf528SMauro Carvalho Chehab priv->config = config; 4799a0bf528SMauro Carvalho Chehab priv->i2c = i2c; 4809a0bf528SMauro Carvalho Chehab 4819a0bf528SMauro Carvalho Chehab /* check if the demod is there */ 4829a0bf528SMauro Carvalho Chehab if (atbm8830_read_reg(priv, REG_CHIP_ID, &data) != 0) { 4839a0bf528SMauro Carvalho Chehab dprintk("%s atbm8830/8831 not found at i2c addr 0x%02X\n", 4849a0bf528SMauro Carvalho Chehab __func__, priv->config->demod_address); 4859a0bf528SMauro Carvalho Chehab goto error_out; 4869a0bf528SMauro Carvalho Chehab } 4879a0bf528SMauro Carvalho Chehab dprintk("atbm8830 chip id: 0x%02X\n", data); 4889a0bf528SMauro Carvalho Chehab 4899a0bf528SMauro Carvalho Chehab memcpy(&priv->frontend.ops, &atbm8830_ops, 4909a0bf528SMauro Carvalho Chehab sizeof(struct dvb_frontend_ops)); 4919a0bf528SMauro Carvalho Chehab priv->frontend.demodulator_priv = priv; 4929a0bf528SMauro Carvalho Chehab 4939a0bf528SMauro Carvalho Chehab atbm8830_init(&priv->frontend); 4949a0bf528SMauro Carvalho Chehab 4959a0bf528SMauro Carvalho Chehab atbm8830_i2c_gate_ctrl(&priv->frontend, 1); 4969a0bf528SMauro Carvalho Chehab 4979a0bf528SMauro Carvalho Chehab return &priv->frontend; 4989a0bf528SMauro Carvalho Chehab 4999a0bf528SMauro Carvalho Chehab error_out: 5009a0bf528SMauro Carvalho Chehab dprintk("%s() error_out\n", __func__); 5019a0bf528SMauro Carvalho Chehab kfree(priv); 5029a0bf528SMauro Carvalho Chehab return NULL; 5039a0bf528SMauro Carvalho Chehab 5049a0bf528SMauro Carvalho Chehab } 5059a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(atbm8830_attach); 5069a0bf528SMauro Carvalho Chehab 5079a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("AltoBeam ATBM8830/8831 GB20600 demodulator driver"); 5089a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>"); 5099a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 510