1*9a0bf528SMauro Carvalho Chehab /* 2*9a0bf528SMauro Carvalho Chehab * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator 3*9a0bf528SMauro Carvalho Chehab * ATBM8830, ATBM8831 4*9a0bf528SMauro Carvalho Chehab * 5*9a0bf528SMauro Carvalho Chehab * Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com> 6*9a0bf528SMauro Carvalho Chehab * 7*9a0bf528SMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 8*9a0bf528SMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 9*9a0bf528SMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 10*9a0bf528SMauro Carvalho Chehab * (at your option) any later version. 11*9a0bf528SMauro Carvalho Chehab * 12*9a0bf528SMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 13*9a0bf528SMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 14*9a0bf528SMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15*9a0bf528SMauro Carvalho Chehab * GNU General Public License for more details. 16*9a0bf528SMauro Carvalho Chehab * 17*9a0bf528SMauro Carvalho Chehab * You should have received a copy of the GNU General Public License 18*9a0bf528SMauro Carvalho Chehab * along with this program; if not, write to the Free Software 19*9a0bf528SMauro Carvalho Chehab * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20*9a0bf528SMauro Carvalho Chehab */ 21*9a0bf528SMauro Carvalho Chehab 22*9a0bf528SMauro Carvalho Chehab #include <asm/div64.h> 23*9a0bf528SMauro Carvalho Chehab #include "dvb_frontend.h" 24*9a0bf528SMauro Carvalho Chehab 25*9a0bf528SMauro Carvalho Chehab #include "atbm8830.h" 26*9a0bf528SMauro Carvalho Chehab #include "atbm8830_priv.h" 27*9a0bf528SMauro Carvalho Chehab 28*9a0bf528SMauro Carvalho Chehab #define dprintk(args...) \ 29*9a0bf528SMauro Carvalho Chehab do { \ 30*9a0bf528SMauro Carvalho Chehab if (debug) \ 31*9a0bf528SMauro Carvalho Chehab printk(KERN_DEBUG "atbm8830: " args); \ 32*9a0bf528SMauro Carvalho Chehab } while (0) 33*9a0bf528SMauro Carvalho Chehab 34*9a0bf528SMauro Carvalho Chehab static int debug; 35*9a0bf528SMauro Carvalho Chehab 36*9a0bf528SMauro Carvalho Chehab module_param(debug, int, 0644); 37*9a0bf528SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); 38*9a0bf528SMauro Carvalho Chehab 39*9a0bf528SMauro Carvalho Chehab static int atbm8830_write_reg(struct atbm_state *priv, u16 reg, u8 data) 40*9a0bf528SMauro Carvalho Chehab { 41*9a0bf528SMauro Carvalho Chehab int ret = 0; 42*9a0bf528SMauro Carvalho Chehab u8 dev_addr; 43*9a0bf528SMauro Carvalho Chehab u8 buf1[] = { reg >> 8, reg & 0xFF }; 44*9a0bf528SMauro Carvalho Chehab u8 buf2[] = { data }; 45*9a0bf528SMauro Carvalho Chehab struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; 46*9a0bf528SMauro Carvalho Chehab struct i2c_msg msg2 = { .flags = 0, .buf = buf2, .len = 1 }; 47*9a0bf528SMauro Carvalho Chehab 48*9a0bf528SMauro Carvalho Chehab dev_addr = priv->config->demod_address; 49*9a0bf528SMauro Carvalho Chehab msg1.addr = dev_addr; 50*9a0bf528SMauro Carvalho Chehab msg2.addr = dev_addr; 51*9a0bf528SMauro Carvalho Chehab 52*9a0bf528SMauro Carvalho Chehab if (debug >= 2) 53*9a0bf528SMauro Carvalho Chehab dprintk("%s: reg=0x%04X, data=0x%02X\n", __func__, reg, data); 54*9a0bf528SMauro Carvalho Chehab 55*9a0bf528SMauro Carvalho Chehab ret = i2c_transfer(priv->i2c, &msg1, 1); 56*9a0bf528SMauro Carvalho Chehab if (ret != 1) 57*9a0bf528SMauro Carvalho Chehab return -EIO; 58*9a0bf528SMauro Carvalho Chehab 59*9a0bf528SMauro Carvalho Chehab ret = i2c_transfer(priv->i2c, &msg2, 1); 60*9a0bf528SMauro Carvalho Chehab return (ret != 1) ? -EIO : 0; 61*9a0bf528SMauro Carvalho Chehab } 62*9a0bf528SMauro Carvalho Chehab 63*9a0bf528SMauro Carvalho Chehab static int atbm8830_read_reg(struct atbm_state *priv, u16 reg, u8 *p_data) 64*9a0bf528SMauro Carvalho Chehab { 65*9a0bf528SMauro Carvalho Chehab int ret; 66*9a0bf528SMauro Carvalho Chehab u8 dev_addr; 67*9a0bf528SMauro Carvalho Chehab 68*9a0bf528SMauro Carvalho Chehab u8 buf1[] = { reg >> 8, reg & 0xFF }; 69*9a0bf528SMauro Carvalho Chehab u8 buf2[] = { 0 }; 70*9a0bf528SMauro Carvalho Chehab struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; 71*9a0bf528SMauro Carvalho Chehab struct i2c_msg msg2 = { .flags = I2C_M_RD, .buf = buf2, .len = 1 }; 72*9a0bf528SMauro Carvalho Chehab 73*9a0bf528SMauro Carvalho Chehab dev_addr = priv->config->demod_address; 74*9a0bf528SMauro Carvalho Chehab msg1.addr = dev_addr; 75*9a0bf528SMauro Carvalho Chehab msg2.addr = dev_addr; 76*9a0bf528SMauro Carvalho Chehab 77*9a0bf528SMauro Carvalho Chehab ret = i2c_transfer(priv->i2c, &msg1, 1); 78*9a0bf528SMauro Carvalho Chehab if (ret != 1) { 79*9a0bf528SMauro Carvalho Chehab dprintk("%s: error reg=0x%04x, ret=%i\n", __func__, reg, ret); 80*9a0bf528SMauro Carvalho Chehab return -EIO; 81*9a0bf528SMauro Carvalho Chehab } 82*9a0bf528SMauro Carvalho Chehab 83*9a0bf528SMauro Carvalho Chehab ret = i2c_transfer(priv->i2c, &msg2, 1); 84*9a0bf528SMauro Carvalho Chehab if (ret != 1) 85*9a0bf528SMauro Carvalho Chehab return -EIO; 86*9a0bf528SMauro Carvalho Chehab 87*9a0bf528SMauro Carvalho Chehab *p_data = buf2[0]; 88*9a0bf528SMauro Carvalho Chehab if (debug >= 2) 89*9a0bf528SMauro Carvalho Chehab dprintk("%s: reg=0x%04X, data=0x%02X\n", 90*9a0bf528SMauro Carvalho Chehab __func__, reg, buf2[0]); 91*9a0bf528SMauro Carvalho Chehab 92*9a0bf528SMauro Carvalho Chehab return 0; 93*9a0bf528SMauro Carvalho Chehab } 94*9a0bf528SMauro Carvalho Chehab 95*9a0bf528SMauro Carvalho Chehab /* Lock register latch so that multi-register read is atomic */ 96*9a0bf528SMauro Carvalho Chehab static inline int atbm8830_reglatch_lock(struct atbm_state *priv, int lock) 97*9a0bf528SMauro Carvalho Chehab { 98*9a0bf528SMauro Carvalho Chehab return atbm8830_write_reg(priv, REG_READ_LATCH, lock ? 1 : 0); 99*9a0bf528SMauro Carvalho Chehab } 100*9a0bf528SMauro Carvalho Chehab 101*9a0bf528SMauro Carvalho Chehab static int set_osc_freq(struct atbm_state *priv, u32 freq /*in kHz*/) 102*9a0bf528SMauro Carvalho Chehab { 103*9a0bf528SMauro Carvalho Chehab u32 val; 104*9a0bf528SMauro Carvalho Chehab u64 t; 105*9a0bf528SMauro Carvalho Chehab 106*9a0bf528SMauro Carvalho Chehab /* 0x100000 * freq / 30.4MHz */ 107*9a0bf528SMauro Carvalho Chehab t = (u64)0x100000 * freq; 108*9a0bf528SMauro Carvalho Chehab do_div(t, 30400); 109*9a0bf528SMauro Carvalho Chehab val = t; 110*9a0bf528SMauro Carvalho Chehab 111*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_OSC_CLK, val); 112*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_OSC_CLK + 1, val >> 8); 113*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_OSC_CLK + 2, val >> 16); 114*9a0bf528SMauro Carvalho Chehab 115*9a0bf528SMauro Carvalho Chehab return 0; 116*9a0bf528SMauro Carvalho Chehab } 117*9a0bf528SMauro Carvalho Chehab 118*9a0bf528SMauro Carvalho Chehab static int set_if_freq(struct atbm_state *priv, u32 freq /*in kHz*/) 119*9a0bf528SMauro Carvalho Chehab { 120*9a0bf528SMauro Carvalho Chehab 121*9a0bf528SMauro Carvalho Chehab u32 fs = priv->config->osc_clk_freq; 122*9a0bf528SMauro Carvalho Chehab u64 t; 123*9a0bf528SMauro Carvalho Chehab u32 val; 124*9a0bf528SMauro Carvalho Chehab u8 dat; 125*9a0bf528SMauro Carvalho Chehab 126*9a0bf528SMauro Carvalho Chehab if (freq != 0) { 127*9a0bf528SMauro Carvalho Chehab /* 2 * PI * (freq - fs) / fs * (2 ^ 22) */ 128*9a0bf528SMauro Carvalho Chehab t = (u64) 2 * 31416 * (freq - fs); 129*9a0bf528SMauro Carvalho Chehab t <<= 22; 130*9a0bf528SMauro Carvalho Chehab do_div(t, fs); 131*9a0bf528SMauro Carvalho Chehab do_div(t, 1000); 132*9a0bf528SMauro Carvalho Chehab val = t; 133*9a0bf528SMauro Carvalho Chehab 134*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 1); 135*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_IF_FREQ, val); 136*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_IF_FREQ+1, val >> 8); 137*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_IF_FREQ+2, val >> 16); 138*9a0bf528SMauro Carvalho Chehab 139*9a0bf528SMauro Carvalho Chehab atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); 140*9a0bf528SMauro Carvalho Chehab dat &= 0xFC; 141*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); 142*9a0bf528SMauro Carvalho Chehab } else { 143*9a0bf528SMauro Carvalho Chehab /* Zero IF */ 144*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 0); 145*9a0bf528SMauro Carvalho Chehab 146*9a0bf528SMauro Carvalho Chehab atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); 147*9a0bf528SMauro Carvalho Chehab dat &= 0xFC; 148*9a0bf528SMauro Carvalho Chehab dat |= 0x02; 149*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); 150*9a0bf528SMauro Carvalho Chehab 151*9a0bf528SMauro Carvalho Chehab if (priv->config->zif_swap_iq) 152*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x03); 153*9a0bf528SMauro Carvalho Chehab else 154*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x01); 155*9a0bf528SMauro Carvalho Chehab } 156*9a0bf528SMauro Carvalho Chehab 157*9a0bf528SMauro Carvalho Chehab return 0; 158*9a0bf528SMauro Carvalho Chehab } 159*9a0bf528SMauro Carvalho Chehab 160*9a0bf528SMauro Carvalho Chehab static int is_locked(struct atbm_state *priv, u8 *locked) 161*9a0bf528SMauro Carvalho Chehab { 162*9a0bf528SMauro Carvalho Chehab u8 status; 163*9a0bf528SMauro Carvalho Chehab 164*9a0bf528SMauro Carvalho Chehab atbm8830_read_reg(priv, REG_LOCK_STATUS, &status); 165*9a0bf528SMauro Carvalho Chehab 166*9a0bf528SMauro Carvalho Chehab if (locked != NULL) 167*9a0bf528SMauro Carvalho Chehab *locked = (status == 1); 168*9a0bf528SMauro Carvalho Chehab return 0; 169*9a0bf528SMauro Carvalho Chehab } 170*9a0bf528SMauro Carvalho Chehab 171*9a0bf528SMauro Carvalho Chehab static int set_agc_config(struct atbm_state *priv, 172*9a0bf528SMauro Carvalho Chehab u8 min, u8 max, u8 hold_loop) 173*9a0bf528SMauro Carvalho Chehab { 174*9a0bf528SMauro Carvalho Chehab /* no effect if both min and max are zero */ 175*9a0bf528SMauro Carvalho Chehab if (!min && !max) 176*9a0bf528SMauro Carvalho Chehab return 0; 177*9a0bf528SMauro Carvalho Chehab 178*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_AGC_MIN, min); 179*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_AGC_MAX, max); 180*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_AGC_HOLD_LOOP, hold_loop); 181*9a0bf528SMauro Carvalho Chehab 182*9a0bf528SMauro Carvalho Chehab return 0; 183*9a0bf528SMauro Carvalho Chehab } 184*9a0bf528SMauro Carvalho Chehab 185*9a0bf528SMauro Carvalho Chehab static int set_static_channel_mode(struct atbm_state *priv) 186*9a0bf528SMauro Carvalho Chehab { 187*9a0bf528SMauro Carvalho Chehab int i; 188*9a0bf528SMauro Carvalho Chehab 189*9a0bf528SMauro Carvalho Chehab for (i = 0; i < 5; i++) 190*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x099B + i, 0x08); 191*9a0bf528SMauro Carvalho Chehab 192*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x095B, 0x7F); 193*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x09CB, 0x01); 194*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x09CC, 0x7F); 195*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x09CD, 0x7F); 196*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x0E01, 0x20); 197*9a0bf528SMauro Carvalho Chehab 198*9a0bf528SMauro Carvalho Chehab /* For single carrier */ 199*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x0B03, 0x0A); 200*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x0935, 0x10); 201*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x0936, 0x08); 202*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x093E, 0x08); 203*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x096E, 0x06); 204*9a0bf528SMauro Carvalho Chehab 205*9a0bf528SMauro Carvalho Chehab /* frame_count_max0 */ 206*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x0B09, 0x00); 207*9a0bf528SMauro Carvalho Chehab /* frame_count_max1 */ 208*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x0B0A, 0x08); 209*9a0bf528SMauro Carvalho Chehab 210*9a0bf528SMauro Carvalho Chehab return 0; 211*9a0bf528SMauro Carvalho Chehab } 212*9a0bf528SMauro Carvalho Chehab 213*9a0bf528SMauro Carvalho Chehab static int set_ts_config(struct atbm_state *priv) 214*9a0bf528SMauro Carvalho Chehab { 215*9a0bf528SMauro Carvalho Chehab const struct atbm8830_config *cfg = priv->config; 216*9a0bf528SMauro Carvalho Chehab 217*9a0bf528SMauro Carvalho Chehab /*Set parallel/serial ts mode*/ 218*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_TS_SERIAL, cfg->serial_ts ? 1 : 0); 219*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_TS_CLK_MODE, cfg->serial_ts ? 1 : 0); 220*9a0bf528SMauro Carvalho Chehab /*Set ts sampling edge*/ 221*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_TS_SAMPLE_EDGE, 222*9a0bf528SMauro Carvalho Chehab cfg->ts_sampling_edge ? 1 : 0); 223*9a0bf528SMauro Carvalho Chehab /*Set ts clock freerun*/ 224*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_TS_CLK_FREERUN, 225*9a0bf528SMauro Carvalho Chehab cfg->ts_clk_gated ? 0 : 1); 226*9a0bf528SMauro Carvalho Chehab 227*9a0bf528SMauro Carvalho Chehab return 0; 228*9a0bf528SMauro Carvalho Chehab } 229*9a0bf528SMauro Carvalho Chehab 230*9a0bf528SMauro Carvalho Chehab static int atbm8830_init(struct dvb_frontend *fe) 231*9a0bf528SMauro Carvalho Chehab { 232*9a0bf528SMauro Carvalho Chehab struct atbm_state *priv = fe->demodulator_priv; 233*9a0bf528SMauro Carvalho Chehab const struct atbm8830_config *cfg = priv->config; 234*9a0bf528SMauro Carvalho Chehab 235*9a0bf528SMauro Carvalho Chehab /*Set oscillator frequency*/ 236*9a0bf528SMauro Carvalho Chehab set_osc_freq(priv, cfg->osc_clk_freq); 237*9a0bf528SMauro Carvalho Chehab 238*9a0bf528SMauro Carvalho Chehab /*Set IF frequency*/ 239*9a0bf528SMauro Carvalho Chehab set_if_freq(priv, cfg->if_freq); 240*9a0bf528SMauro Carvalho Chehab 241*9a0bf528SMauro Carvalho Chehab /*Set AGC Config*/ 242*9a0bf528SMauro Carvalho Chehab set_agc_config(priv, cfg->agc_min, cfg->agc_max, 243*9a0bf528SMauro Carvalho Chehab cfg->agc_hold_loop); 244*9a0bf528SMauro Carvalho Chehab 245*9a0bf528SMauro Carvalho Chehab /*Set static channel mode*/ 246*9a0bf528SMauro Carvalho Chehab set_static_channel_mode(priv); 247*9a0bf528SMauro Carvalho Chehab 248*9a0bf528SMauro Carvalho Chehab set_ts_config(priv); 249*9a0bf528SMauro Carvalho Chehab /*Turn off DSP reset*/ 250*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x000A, 0); 251*9a0bf528SMauro Carvalho Chehab 252*9a0bf528SMauro Carvalho Chehab /*SW version test*/ 253*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, 0x020C, 11); 254*9a0bf528SMauro Carvalho Chehab 255*9a0bf528SMauro Carvalho Chehab /* Run */ 256*9a0bf528SMauro Carvalho Chehab atbm8830_write_reg(priv, REG_DEMOD_RUN, 1); 257*9a0bf528SMauro Carvalho Chehab 258*9a0bf528SMauro Carvalho Chehab return 0; 259*9a0bf528SMauro Carvalho Chehab } 260*9a0bf528SMauro Carvalho Chehab 261*9a0bf528SMauro Carvalho Chehab 262*9a0bf528SMauro Carvalho Chehab static void atbm8830_release(struct dvb_frontend *fe) 263*9a0bf528SMauro Carvalho Chehab { 264*9a0bf528SMauro Carvalho Chehab struct atbm_state *state = fe->demodulator_priv; 265*9a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 266*9a0bf528SMauro Carvalho Chehab 267*9a0bf528SMauro Carvalho Chehab kfree(state); 268*9a0bf528SMauro Carvalho Chehab } 269*9a0bf528SMauro Carvalho Chehab 270*9a0bf528SMauro Carvalho Chehab static int atbm8830_set_fe(struct dvb_frontend *fe) 271*9a0bf528SMauro Carvalho Chehab { 272*9a0bf528SMauro Carvalho Chehab struct atbm_state *priv = fe->demodulator_priv; 273*9a0bf528SMauro Carvalho Chehab int i; 274*9a0bf528SMauro Carvalho Chehab u8 locked = 0; 275*9a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 276*9a0bf528SMauro Carvalho Chehab 277*9a0bf528SMauro Carvalho Chehab /* set frequency */ 278*9a0bf528SMauro Carvalho Chehab if (fe->ops.tuner_ops.set_params) { 279*9a0bf528SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 280*9a0bf528SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 1); 281*9a0bf528SMauro Carvalho Chehab fe->ops.tuner_ops.set_params(fe); 282*9a0bf528SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 283*9a0bf528SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); 284*9a0bf528SMauro Carvalho Chehab } 285*9a0bf528SMauro Carvalho Chehab 286*9a0bf528SMauro Carvalho Chehab /* start auto lock */ 287*9a0bf528SMauro Carvalho Chehab for (i = 0; i < 10; i++) { 288*9a0bf528SMauro Carvalho Chehab mdelay(100); 289*9a0bf528SMauro Carvalho Chehab dprintk("Try %d\n", i); 290*9a0bf528SMauro Carvalho Chehab is_locked(priv, &locked); 291*9a0bf528SMauro Carvalho Chehab if (locked != 0) { 292*9a0bf528SMauro Carvalho Chehab dprintk("ATBM8830 locked!\n"); 293*9a0bf528SMauro Carvalho Chehab break; 294*9a0bf528SMauro Carvalho Chehab } 295*9a0bf528SMauro Carvalho Chehab } 296*9a0bf528SMauro Carvalho Chehab 297*9a0bf528SMauro Carvalho Chehab return 0; 298*9a0bf528SMauro Carvalho Chehab } 299*9a0bf528SMauro Carvalho Chehab 300*9a0bf528SMauro Carvalho Chehab static int atbm8830_get_fe(struct dvb_frontend *fe) 301*9a0bf528SMauro Carvalho Chehab { 302*9a0bf528SMauro Carvalho Chehab struct dtv_frontend_properties *c = &fe->dtv_property_cache; 303*9a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 304*9a0bf528SMauro Carvalho Chehab 305*9a0bf528SMauro Carvalho Chehab /* TODO: get real readings from device */ 306*9a0bf528SMauro Carvalho Chehab /* inversion status */ 307*9a0bf528SMauro Carvalho Chehab c->inversion = INVERSION_OFF; 308*9a0bf528SMauro Carvalho Chehab 309*9a0bf528SMauro Carvalho Chehab /* bandwidth */ 310*9a0bf528SMauro Carvalho Chehab c->bandwidth_hz = 8000000; 311*9a0bf528SMauro Carvalho Chehab 312*9a0bf528SMauro Carvalho Chehab c->code_rate_HP = FEC_AUTO; 313*9a0bf528SMauro Carvalho Chehab c->code_rate_LP = FEC_AUTO; 314*9a0bf528SMauro Carvalho Chehab 315*9a0bf528SMauro Carvalho Chehab c->modulation = QAM_AUTO; 316*9a0bf528SMauro Carvalho Chehab 317*9a0bf528SMauro Carvalho Chehab /* transmission mode */ 318*9a0bf528SMauro Carvalho Chehab c->transmission_mode = TRANSMISSION_MODE_AUTO; 319*9a0bf528SMauro Carvalho Chehab 320*9a0bf528SMauro Carvalho Chehab /* guard interval */ 321*9a0bf528SMauro Carvalho Chehab c->guard_interval = GUARD_INTERVAL_AUTO; 322*9a0bf528SMauro Carvalho Chehab 323*9a0bf528SMauro Carvalho Chehab /* hierarchy */ 324*9a0bf528SMauro Carvalho Chehab c->hierarchy = HIERARCHY_NONE; 325*9a0bf528SMauro Carvalho Chehab 326*9a0bf528SMauro Carvalho Chehab return 0; 327*9a0bf528SMauro Carvalho Chehab } 328*9a0bf528SMauro Carvalho Chehab 329*9a0bf528SMauro Carvalho Chehab static int atbm8830_get_tune_settings(struct dvb_frontend *fe, 330*9a0bf528SMauro Carvalho Chehab struct dvb_frontend_tune_settings *fesettings) 331*9a0bf528SMauro Carvalho Chehab { 332*9a0bf528SMauro Carvalho Chehab fesettings->min_delay_ms = 0; 333*9a0bf528SMauro Carvalho Chehab fesettings->step_size = 0; 334*9a0bf528SMauro Carvalho Chehab fesettings->max_drift = 0; 335*9a0bf528SMauro Carvalho Chehab return 0; 336*9a0bf528SMauro Carvalho Chehab } 337*9a0bf528SMauro Carvalho Chehab 338*9a0bf528SMauro Carvalho Chehab static int atbm8830_read_status(struct dvb_frontend *fe, fe_status_t *fe_status) 339*9a0bf528SMauro Carvalho Chehab { 340*9a0bf528SMauro Carvalho Chehab struct atbm_state *priv = fe->demodulator_priv; 341*9a0bf528SMauro Carvalho Chehab u8 locked = 0; 342*9a0bf528SMauro Carvalho Chehab u8 agc_locked = 0; 343*9a0bf528SMauro Carvalho Chehab 344*9a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 345*9a0bf528SMauro Carvalho Chehab *fe_status = 0; 346*9a0bf528SMauro Carvalho Chehab 347*9a0bf528SMauro Carvalho Chehab is_locked(priv, &locked); 348*9a0bf528SMauro Carvalho Chehab if (locked) { 349*9a0bf528SMauro Carvalho Chehab *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | 350*9a0bf528SMauro Carvalho Chehab FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; 351*9a0bf528SMauro Carvalho Chehab } 352*9a0bf528SMauro Carvalho Chehab dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); 353*9a0bf528SMauro Carvalho Chehab 354*9a0bf528SMauro Carvalho Chehab atbm8830_read_reg(priv, REG_AGC_LOCK, &agc_locked); 355*9a0bf528SMauro Carvalho Chehab dprintk("AGC Lock: %d\n", agc_locked); 356*9a0bf528SMauro Carvalho Chehab 357*9a0bf528SMauro Carvalho Chehab return 0; 358*9a0bf528SMauro Carvalho Chehab } 359*9a0bf528SMauro Carvalho Chehab 360*9a0bf528SMauro Carvalho Chehab static int atbm8830_read_ber(struct dvb_frontend *fe, u32 *ber) 361*9a0bf528SMauro Carvalho Chehab { 362*9a0bf528SMauro Carvalho Chehab struct atbm_state *priv = fe->demodulator_priv; 363*9a0bf528SMauro Carvalho Chehab u32 frame_err; 364*9a0bf528SMauro Carvalho Chehab u8 t; 365*9a0bf528SMauro Carvalho Chehab 366*9a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 367*9a0bf528SMauro Carvalho Chehab 368*9a0bf528SMauro Carvalho Chehab atbm8830_reglatch_lock(priv, 1); 369*9a0bf528SMauro Carvalho Chehab 370*9a0bf528SMauro Carvalho Chehab atbm8830_read_reg(priv, REG_FRAME_ERR_CNT + 1, &t); 371*9a0bf528SMauro Carvalho Chehab frame_err = t & 0x7F; 372*9a0bf528SMauro Carvalho Chehab frame_err <<= 8; 373*9a0bf528SMauro Carvalho Chehab atbm8830_read_reg(priv, REG_FRAME_ERR_CNT, &t); 374*9a0bf528SMauro Carvalho Chehab frame_err |= t; 375*9a0bf528SMauro Carvalho Chehab 376*9a0bf528SMauro Carvalho Chehab atbm8830_reglatch_lock(priv, 0); 377*9a0bf528SMauro Carvalho Chehab 378*9a0bf528SMauro Carvalho Chehab *ber = frame_err * 100 / 32767; 379*9a0bf528SMauro Carvalho Chehab 380*9a0bf528SMauro Carvalho Chehab dprintk("%s: ber=0x%x\n", __func__, *ber); 381*9a0bf528SMauro Carvalho Chehab return 0; 382*9a0bf528SMauro Carvalho Chehab } 383*9a0bf528SMauro Carvalho Chehab 384*9a0bf528SMauro Carvalho Chehab static int atbm8830_read_signal_strength(struct dvb_frontend *fe, u16 *signal) 385*9a0bf528SMauro Carvalho Chehab { 386*9a0bf528SMauro Carvalho Chehab struct atbm_state *priv = fe->demodulator_priv; 387*9a0bf528SMauro Carvalho Chehab u32 pwm; 388*9a0bf528SMauro Carvalho Chehab u8 t; 389*9a0bf528SMauro Carvalho Chehab 390*9a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 391*9a0bf528SMauro Carvalho Chehab atbm8830_reglatch_lock(priv, 1); 392*9a0bf528SMauro Carvalho Chehab 393*9a0bf528SMauro Carvalho Chehab atbm8830_read_reg(priv, REG_AGC_PWM_VAL + 1, &t); 394*9a0bf528SMauro Carvalho Chehab pwm = t & 0x03; 395*9a0bf528SMauro Carvalho Chehab pwm <<= 8; 396*9a0bf528SMauro Carvalho Chehab atbm8830_read_reg(priv, REG_AGC_PWM_VAL, &t); 397*9a0bf528SMauro Carvalho Chehab pwm |= t; 398*9a0bf528SMauro Carvalho Chehab 399*9a0bf528SMauro Carvalho Chehab atbm8830_reglatch_lock(priv, 0); 400*9a0bf528SMauro Carvalho Chehab 401*9a0bf528SMauro Carvalho Chehab dprintk("AGC PWM = 0x%02X\n", pwm); 402*9a0bf528SMauro Carvalho Chehab pwm = 0x400 - pwm; 403*9a0bf528SMauro Carvalho Chehab 404*9a0bf528SMauro Carvalho Chehab *signal = pwm * 0x10000 / 0x400; 405*9a0bf528SMauro Carvalho Chehab 406*9a0bf528SMauro Carvalho Chehab return 0; 407*9a0bf528SMauro Carvalho Chehab } 408*9a0bf528SMauro Carvalho Chehab 409*9a0bf528SMauro Carvalho Chehab static int atbm8830_read_snr(struct dvb_frontend *fe, u16 *snr) 410*9a0bf528SMauro Carvalho Chehab { 411*9a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 412*9a0bf528SMauro Carvalho Chehab *snr = 0; 413*9a0bf528SMauro Carvalho Chehab return 0; 414*9a0bf528SMauro Carvalho Chehab } 415*9a0bf528SMauro Carvalho Chehab 416*9a0bf528SMauro Carvalho Chehab static int atbm8830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) 417*9a0bf528SMauro Carvalho Chehab { 418*9a0bf528SMauro Carvalho Chehab dprintk("%s\n", __func__); 419*9a0bf528SMauro Carvalho Chehab *ucblocks = 0; 420*9a0bf528SMauro Carvalho Chehab return 0; 421*9a0bf528SMauro Carvalho Chehab } 422*9a0bf528SMauro Carvalho Chehab 423*9a0bf528SMauro Carvalho Chehab static int atbm8830_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) 424*9a0bf528SMauro Carvalho Chehab { 425*9a0bf528SMauro Carvalho Chehab struct atbm_state *priv = fe->demodulator_priv; 426*9a0bf528SMauro Carvalho Chehab 427*9a0bf528SMauro Carvalho Chehab return atbm8830_write_reg(priv, REG_I2C_GATE, enable ? 1 : 0); 428*9a0bf528SMauro Carvalho Chehab } 429*9a0bf528SMauro Carvalho Chehab 430*9a0bf528SMauro Carvalho Chehab static struct dvb_frontend_ops atbm8830_ops = { 431*9a0bf528SMauro Carvalho Chehab .delsys = { SYS_DTMB }, 432*9a0bf528SMauro Carvalho Chehab .info = { 433*9a0bf528SMauro Carvalho Chehab .name = "AltoBeam ATBM8830/8831 DMB-TH", 434*9a0bf528SMauro Carvalho Chehab .frequency_min = 474000000, 435*9a0bf528SMauro Carvalho Chehab .frequency_max = 858000000, 436*9a0bf528SMauro Carvalho Chehab .frequency_stepsize = 10000, 437*9a0bf528SMauro Carvalho Chehab .caps = 438*9a0bf528SMauro Carvalho Chehab FE_CAN_FEC_AUTO | 439*9a0bf528SMauro Carvalho Chehab FE_CAN_QAM_AUTO | 440*9a0bf528SMauro Carvalho Chehab FE_CAN_TRANSMISSION_MODE_AUTO | 441*9a0bf528SMauro Carvalho Chehab FE_CAN_GUARD_INTERVAL_AUTO 442*9a0bf528SMauro Carvalho Chehab }, 443*9a0bf528SMauro Carvalho Chehab 444*9a0bf528SMauro Carvalho Chehab .release = atbm8830_release, 445*9a0bf528SMauro Carvalho Chehab 446*9a0bf528SMauro Carvalho Chehab .init = atbm8830_init, 447*9a0bf528SMauro Carvalho Chehab .sleep = NULL, 448*9a0bf528SMauro Carvalho Chehab .write = NULL, 449*9a0bf528SMauro Carvalho Chehab .i2c_gate_ctrl = atbm8830_i2c_gate_ctrl, 450*9a0bf528SMauro Carvalho Chehab 451*9a0bf528SMauro Carvalho Chehab .set_frontend = atbm8830_set_fe, 452*9a0bf528SMauro Carvalho Chehab .get_frontend = atbm8830_get_fe, 453*9a0bf528SMauro Carvalho Chehab .get_tune_settings = atbm8830_get_tune_settings, 454*9a0bf528SMauro Carvalho Chehab 455*9a0bf528SMauro Carvalho Chehab .read_status = atbm8830_read_status, 456*9a0bf528SMauro Carvalho Chehab .read_ber = atbm8830_read_ber, 457*9a0bf528SMauro Carvalho Chehab .read_signal_strength = atbm8830_read_signal_strength, 458*9a0bf528SMauro Carvalho Chehab .read_snr = atbm8830_read_snr, 459*9a0bf528SMauro Carvalho Chehab .read_ucblocks = atbm8830_read_ucblocks, 460*9a0bf528SMauro Carvalho Chehab }; 461*9a0bf528SMauro Carvalho Chehab 462*9a0bf528SMauro Carvalho Chehab struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, 463*9a0bf528SMauro Carvalho Chehab struct i2c_adapter *i2c) 464*9a0bf528SMauro Carvalho Chehab { 465*9a0bf528SMauro Carvalho Chehab struct atbm_state *priv = NULL; 466*9a0bf528SMauro Carvalho Chehab u8 data = 0; 467*9a0bf528SMauro Carvalho Chehab 468*9a0bf528SMauro Carvalho Chehab dprintk("%s()\n", __func__); 469*9a0bf528SMauro Carvalho Chehab 470*9a0bf528SMauro Carvalho Chehab if (config == NULL || i2c == NULL) 471*9a0bf528SMauro Carvalho Chehab return NULL; 472*9a0bf528SMauro Carvalho Chehab 473*9a0bf528SMauro Carvalho Chehab priv = kzalloc(sizeof(struct atbm_state), GFP_KERNEL); 474*9a0bf528SMauro Carvalho Chehab if (priv == NULL) 475*9a0bf528SMauro Carvalho Chehab goto error_out; 476*9a0bf528SMauro Carvalho Chehab 477*9a0bf528SMauro Carvalho Chehab priv->config = config; 478*9a0bf528SMauro Carvalho Chehab priv->i2c = i2c; 479*9a0bf528SMauro Carvalho Chehab 480*9a0bf528SMauro Carvalho Chehab /* check if the demod is there */ 481*9a0bf528SMauro Carvalho Chehab if (atbm8830_read_reg(priv, REG_CHIP_ID, &data) != 0) { 482*9a0bf528SMauro Carvalho Chehab dprintk("%s atbm8830/8831 not found at i2c addr 0x%02X\n", 483*9a0bf528SMauro Carvalho Chehab __func__, priv->config->demod_address); 484*9a0bf528SMauro Carvalho Chehab goto error_out; 485*9a0bf528SMauro Carvalho Chehab } 486*9a0bf528SMauro Carvalho Chehab dprintk("atbm8830 chip id: 0x%02X\n", data); 487*9a0bf528SMauro Carvalho Chehab 488*9a0bf528SMauro Carvalho Chehab memcpy(&priv->frontend.ops, &atbm8830_ops, 489*9a0bf528SMauro Carvalho Chehab sizeof(struct dvb_frontend_ops)); 490*9a0bf528SMauro Carvalho Chehab priv->frontend.demodulator_priv = priv; 491*9a0bf528SMauro Carvalho Chehab 492*9a0bf528SMauro Carvalho Chehab atbm8830_init(&priv->frontend); 493*9a0bf528SMauro Carvalho Chehab 494*9a0bf528SMauro Carvalho Chehab atbm8830_i2c_gate_ctrl(&priv->frontend, 1); 495*9a0bf528SMauro Carvalho Chehab 496*9a0bf528SMauro Carvalho Chehab return &priv->frontend; 497*9a0bf528SMauro Carvalho Chehab 498*9a0bf528SMauro Carvalho Chehab error_out: 499*9a0bf528SMauro Carvalho Chehab dprintk("%s() error_out\n", __func__); 500*9a0bf528SMauro Carvalho Chehab kfree(priv); 501*9a0bf528SMauro Carvalho Chehab return NULL; 502*9a0bf528SMauro Carvalho Chehab 503*9a0bf528SMauro Carvalho Chehab } 504*9a0bf528SMauro Carvalho Chehab EXPORT_SYMBOL(atbm8830_attach); 505*9a0bf528SMauro Carvalho Chehab 506*9a0bf528SMauro Carvalho Chehab MODULE_DESCRIPTION("AltoBeam ATBM8830/8831 GB20600 demodulator driver"); 507*9a0bf528SMauro Carvalho Chehab MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>"); 508*9a0bf528SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 509