linux/drivers/media/dvb/frontends/cx22702.c
<<
>>
Prefs
   1/*
   2    Conexant 22702 DVB OFDM demodulator driver
   3
   4    based on:
   5        Alps TDMB7 DVB OFDM demodulator driver
   6
   7    Copyright (C) 2001-2002 Convergence Integrated Media GmbH
   8          Holger Waechtler <holger@convergence.de>
   9
  10    Copyright (C) 2004 Steven Toth <stoth@hauppauge.com>
  11
  12    This program is free software; you can redistribute it and/or modify
  13    it under the terms of the GNU General Public License as published by
  14    the Free Software Foundation; either version 2 of the License, or
  15    (at your option) any later version.
  16
  17    This program is distributed in the hope that it will be useful,
  18    but WITHOUT ANY WARRANTY; without even the implied warranty of
  19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20    GNU General Public License for more details.
  21
  22    You should have received a copy of the GNU General Public License
  23    along with this program; if not, write to the Free Software
  24    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  25
  26*/
  27
  28#include <linux/kernel.h>
  29#include <linux/init.h>
  30#include <linux/module.h>
  31#include <linux/string.h>
  32#include <linux/slab.h>
  33#include <linux/delay.h>
  34#include "dvb_frontend.h"
  35#include "cx22702.h"
  36
  37
  38struct cx22702_state {
  39
  40        struct i2c_adapter* i2c;
  41
  42        /* configuration settings */
  43        const struct cx22702_config* config;
  44
  45        struct dvb_frontend frontend;
  46
  47        /* previous uncorrected block counter */
  48        u8 prevUCBlocks;
  49};
  50
  51static int debug = 0;
  52#define dprintk if (debug) printk
  53
  54/* Register values to initialise the demod */
  55static u8 init_tab [] = {
  56        0x00, 0x00, /* Stop aquisition */
  57        0x0B, 0x06,
  58        0x09, 0x01,
  59        0x0D, 0x41,
  60        0x16, 0x32,
  61        0x20, 0x0A,
  62        0x21, 0x17,
  63        0x24, 0x3e,
  64        0x26, 0xff,
  65        0x27, 0x10,
  66        0x28, 0x00,
  67        0x29, 0x00,
  68        0x2a, 0x10,
  69        0x2b, 0x00,
  70        0x2c, 0x10,
  71        0x2d, 0x00,
  72        0x48, 0xd4,
  73        0x49, 0x56,
  74        0x6b, 0x1e,
  75        0xc8, 0x02,
  76        0xf9, 0x00,
  77        0xfa, 0x00,
  78        0xfb, 0x00,
  79        0xfc, 0x00,
  80        0xfd, 0x00,
  81};
  82
  83static int cx22702_writereg (struct cx22702_state* state, u8 reg, u8 data)
  84{
  85        int ret;
  86        u8 buf [] = { reg, data };
  87        struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
  88
  89        ret = i2c_transfer(state->i2c, &msg, 1);
  90
  91        if (ret != 1)
  92                printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
  93                        __FUNCTION__, reg, data, ret);
  94
  95        return (ret != 1) ? -1 : 0;
  96}
  97
  98static u8 cx22702_readreg (struct cx22702_state* state, u8 reg)
  99{
 100        int ret;
 101        u8 b0 [] = { reg };
 102        u8 b1 [] = { 0 };
 103
 104        struct i2c_msg msg [] = {
 105                { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
 106                { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
 107
 108        ret = i2c_transfer(state->i2c, msg, 2);
 109
 110        if (ret != 2)
 111                printk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
 112
 113        return b1[0];
 114}
 115
 116static int cx22702_set_inversion (struct cx22702_state *state, int inversion)
 117{
 118        u8 val;
 119
 120        switch (inversion) {
 121
 122                case INVERSION_AUTO:
 123                        return -EOPNOTSUPP;
 124
 125                case INVERSION_ON:
 126                        val = cx22702_readreg (state, 0x0C);
 127                        return cx22702_writereg (state, 0x0C, val | 0x01);
 128
 129                case INVERSION_OFF:
 130                        val = cx22702_readreg (state, 0x0C);
 131                        return cx22702_writereg (state, 0x0C, val & 0xfe);
 132
 133                default:
 134                        return -EINVAL;
 135
 136        }
 137
 138}
 139
 140/* Retrieve the demod settings */
 141static int cx22702_get_tps (struct cx22702_state *state, struct dvb_ofdm_parameters *p)
 142{
 143        u8 val;
 144
 145        /* Make sure the TPS regs are valid */
 146        if (!(cx22702_readreg(state, 0x0A) & 0x20))
 147                return -EAGAIN;
 148
 149        val = cx22702_readreg (state, 0x01);
 150        switch( (val&0x18)>>3) {
 151                case 0: p->constellation =   QPSK; break;
 152                case 1: p->constellation = QAM_16; break;
 153                case 2: p->constellation = QAM_64; break;
 154        }
 155        switch( val&0x07 ) {
 156                case 0: p->hierarchy_information = HIERARCHY_NONE; break;
 157                case 1: p->hierarchy_information =    HIERARCHY_1; break;
 158                case 2: p->hierarchy_information =    HIERARCHY_2; break;
 159                case 3: p->hierarchy_information =    HIERARCHY_4; break;
 160        }
 161
 162
 163        val = cx22702_readreg (state, 0x02);
 164        switch( (val&0x38)>>3 ) {
 165                case 0: p->code_rate_HP = FEC_1_2; break;
 166                case 1: p->code_rate_HP = FEC_2_3; break;
 167                case 2: p->code_rate_HP = FEC_3_4; break;
 168                case 3: p->code_rate_HP = FEC_5_6; break;
 169                case 4: p->code_rate_HP = FEC_7_8; break;
 170        }
 171        switch( val&0x07 ) {
 172                case 0: p->code_rate_LP = FEC_1_2; break;
 173                case 1: p->code_rate_LP = FEC_2_3; break;
 174                case 2: p->code_rate_LP = FEC_3_4; break;
 175                case 3: p->code_rate_LP = FEC_5_6; break;
 176                case 4: p->code_rate_LP = FEC_7_8; break;
 177        }
 178
 179
 180        val = cx22702_readreg (state, 0x03);
 181        switch( (val&0x0c)>>2 ) {
 182                case 0: p->guard_interval = GUARD_INTERVAL_1_32; break;
 183                case 1: p->guard_interval = GUARD_INTERVAL_1_16; break;
 184                case 2: p->guard_interval =  GUARD_INTERVAL_1_8; break;
 185                case 3: p->guard_interval =  GUARD_INTERVAL_1_4; break;
 186        }
 187        switch( val&0x03 ) {
 188                case 0: p->transmission_mode = TRANSMISSION_MODE_2K; break;
 189                case 1: p->transmission_mode = TRANSMISSION_MODE_8K; break;
 190        }
 191
 192        return 0;
 193}
 194
 195static int cx22702_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
 196{
 197        struct cx22702_state* state = fe->demodulator_priv;
 198        dprintk ("%s(%d)\n", __FUNCTION__, enable);
 199        if (enable)
 200                return cx22702_writereg (state, 0x0D, cx22702_readreg(state, 0x0D) & 0xfe);
 201        else
 202                return cx22702_writereg (state, 0x0D, cx22702_readreg(state, 0x0D) | 1);
 203}
 204
 205/* Talk to the demod, set the FEC, GUARD, QAM settings etc */
 206static int cx22702_set_tps (struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
 207{
 208        u8 val;
 209        struct cx22702_state* state = fe->demodulator_priv;
 210
 211        if (fe->ops.tuner_ops.set_params) {
 212                fe->ops.tuner_ops.set_params(fe, p);
 213                if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
 214        }
 215
 216        /* set inversion */
 217        cx22702_set_inversion (state, p->inversion);
 218
 219        /* set bandwidth */
 220        switch(p->u.ofdm.bandwidth) {
 221        case BANDWIDTH_6_MHZ:
 222                cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xcf) | 0x20 );
 223                break;
 224        case BANDWIDTH_7_MHZ:
 225                cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xcf) | 0x10 );
 226                break;
 227        case BANDWIDTH_8_MHZ:
 228                cx22702_writereg(state, 0x0C, cx22702_readreg(state, 0x0C) &0xcf );
 229                break;
 230        default:
 231                dprintk ("%s: invalid bandwidth\n",__FUNCTION__);
 232                return -EINVAL;
 233        }
 234
 235
 236        p->u.ofdm.code_rate_LP = FEC_AUTO; //temp hack as manual not working
 237
 238        /* use auto configuration? */
 239        if((p->u.ofdm.hierarchy_information==HIERARCHY_AUTO) ||
 240           (p->u.ofdm.constellation==QAM_AUTO) ||
 241           (p->u.ofdm.code_rate_HP==FEC_AUTO) ||
 242           (p->u.ofdm.code_rate_LP==FEC_AUTO) ||
 243           (p->u.ofdm.guard_interval==GUARD_INTERVAL_AUTO) ||
 244           (p->u.ofdm.transmission_mode==TRANSMISSION_MODE_AUTO) ) {
 245
 246                /* TPS Source - use hardware driven values */
 247                cx22702_writereg(state, 0x06, 0x10);
 248                cx22702_writereg(state, 0x07, 0x9);
 249                cx22702_writereg(state, 0x08, 0xC1);
 250                cx22702_writereg(state, 0x0B, cx22702_readreg(state, 0x0B) & 0xfc );
 251                cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40 );
 252                cx22702_writereg(state, 0x00, 0x01); /* Begin aquisition */
 253                dprintk("%s: Autodetecting\n",__FUNCTION__);
 254                return 0;
 255        }
 256
 257        /* manually programmed values */
 258        val=0;
 259        switch(p->u.ofdm.constellation) {
 260                case   QPSK: val = (val&0xe7); break;
 261                case QAM_16: val = (val&0xe7)|0x08; break;
 262                case QAM_64: val = (val&0xe7)|0x10; break;
 263                default:
 264                        dprintk ("%s: invalid constellation\n",__FUNCTION__);
 265                        return -EINVAL;
 266        }
 267        switch(p->u.ofdm.hierarchy_information) {
 268                case HIERARCHY_NONE: val = (val&0xf8); break;
 269                case    HIERARCHY_1: val = (val&0xf8)|1; break;
 270                case    HIERARCHY_2: val = (val&0xf8)|2; break;
 271                case    HIERARCHY_4: val = (val&0xf8)|3; break;
 272                default:
 273                        dprintk ("%s: invalid hierarchy\n",__FUNCTION__);
 274                        return -EINVAL;
 275        }
 276        cx22702_writereg (state, 0x06, val);
 277
 278        val=0;
 279        switch(p->u.ofdm.code_rate_HP) {
 280                case FEC_NONE:
 281                case FEC_1_2: val = (val&0xc7); break;
 282                case FEC_2_3: val = (val&0xc7)|0x08; break;
 283                case FEC_3_4: val = (val&0xc7)|0x10; break;
 284                case FEC_5_6: val = (val&0xc7)|0x18; break;
 285                case FEC_7_8: val = (val&0xc7)|0x20; break;
 286                default:
 287                        dprintk ("%s: invalid code_rate_HP\n",__FUNCTION__);
 288                        return -EINVAL;
 289        }
 290        switch(p->u.ofdm.code_rate_LP) {
 291                case FEC_NONE:
 292                case FEC_1_2: val = (val&0xf8); break;
 293                case FEC_2_3: val = (val&0xf8)|1; break;
 294                case FEC_3_4: val = (val&0xf8)|2; break;
 295                case FEC_5_6: val = (val&0xf8)|3; break;
 296                case FEC_7_8: val = (val&0xf8)|4; break;
 297                default:
 298                        dprintk ("%s: invalid code_rate_LP\n",__FUNCTION__);
 299                        return -EINVAL;
 300        }
 301        cx22702_writereg (state, 0x07, val);
 302
 303        val=0;
 304        switch(p->u.ofdm.guard_interval) {
 305                case GUARD_INTERVAL_1_32: val = (val&0xf3); break;
 306                case GUARD_INTERVAL_1_16: val = (val&0xf3)|0x04; break;
 307                case  GUARD_INTERVAL_1_8: val = (val&0xf3)|0x08; break;
 308                case  GUARD_INTERVAL_1_4: val = (val&0xf3)|0x0c; break;
 309                default:
 310                        dprintk ("%s: invalid guard_interval\n",__FUNCTION__);
 311                        return -EINVAL;
 312        }
 313        switch(p->u.ofdm.transmission_mode) {
 314                case TRANSMISSION_MODE_2K: val = (val&0xfc); break;
 315                case TRANSMISSION_MODE_8K: val = (val&0xfc)|1; break;
 316                default:
 317                        dprintk ("%s: invalid transmission_mode\n",__FUNCTION__);
 318                        return -EINVAL;
 319        }
 320        cx22702_writereg(state, 0x08, val);
 321        cx22702_writereg(state, 0x0B, (cx22702_readreg(state, 0x0B) & 0xfc) | 0x02 );
 322        cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40 );
 323
 324        /* Begin channel aquisition */
 325        cx22702_writereg(state, 0x00, 0x01);
 326
 327        return 0;
 328}
 329
 330/* Reset the demod hardware and reset all of the configuration registers
 331   to a default state. */
 332static int cx22702_init (struct dvb_frontend* fe)
 333{
 334        int i;
 335        struct cx22702_state* state = fe->demodulator_priv;
 336
 337        cx22702_writereg (state, 0x00, 0x02);
 338
 339        msleep(10);
 340
 341        for (i=0; i<sizeof(init_tab); i+=2)
 342                cx22702_writereg (state, init_tab[i], init_tab[i+1]);
 343
 344        cx22702_writereg (state, 0xf8, (state->config->output_mode << 1) & 0x02);
 345
 346        cx22702_i2c_gate_ctrl(fe, 0);
 347
 348        return 0;
 349}
 350
 351static int cx22702_read_status(struct dvb_frontend* fe, fe_status_t* status)
 352{
 353        struct cx22702_state* state = fe->demodulator_priv;
 354        u8 reg0A;
 355        u8 reg23;
 356
 357        *status = 0;
 358
 359        reg0A = cx22702_readreg (state, 0x0A);
 360        reg23 = cx22702_readreg (state, 0x23);
 361
 362        dprintk ("%s: status demod=0x%02x agc=0x%02x\n"
 363                ,__FUNCTION__,reg0A,reg23);
 364
 365        if(reg0A & 0x10) {
 366                *status |= FE_HAS_LOCK;
 367                *status |= FE_HAS_VITERBI;
 368                *status |= FE_HAS_SYNC;
 369        }
 370
 371        if(reg0A & 0x20)
 372                *status |= FE_HAS_CARRIER;
 373
 374        if(reg23 < 0xf0)
 375                *status |= FE_HAS_SIGNAL;
 376
 377        return 0;
 378}
 379
 380static int cx22702_read_ber(struct dvb_frontend* fe, u32* ber)
 381{
 382        struct cx22702_state* state = fe->demodulator_priv;
 383
 384        if(cx22702_readreg (state, 0xE4) & 0x02) {
 385                /* Realtime statistics */
 386                *ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7
 387                        | (cx22702_readreg (state, 0xDF)&0x7F);
 388        } else {
 389                /* Averagtine statistics */
 390                *ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7
 391                        | cx22702_readreg (state, 0xDF);
 392        }
 393
 394        return 0;
 395}
 396
 397static int cx22702_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
 398{
 399        struct cx22702_state* state = fe->demodulator_priv;
 400
 401        u16 rs_ber = 0;
 402        rs_ber = cx22702_readreg (state, 0x23);
 403        *signal_strength = (rs_ber << 8) | rs_ber;
 404
 405        return 0;
 406}
 407
 408static int cx22702_read_snr(struct dvb_frontend* fe, u16* snr)
 409{
 410        struct cx22702_state* state = fe->demodulator_priv;
 411
 412        u16 rs_ber=0;
 413        if(cx22702_readreg (state, 0xE4) & 0x02) {
 414                /* Realtime statistics */
 415                rs_ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7
 416                        | (cx22702_readreg (state, 0xDF)& 0x7F);
 417        } else {
 418                /* Averagine statistics */
 419                rs_ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 8
 420                        | cx22702_readreg (state, 0xDF);
 421        }
 422        *snr = ~rs_ber;
 423
 424        return 0;
 425}
 426
 427static int cx22702_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
 428{
 429        struct cx22702_state* state = fe->demodulator_priv;
 430
 431        u8 _ucblocks;
 432
 433        /* RS Uncorrectable Packet Count then reset */
 434        _ucblocks = cx22702_readreg (state, 0xE3);
 435        if (state->prevUCBlocks < _ucblocks)
 436                *ucblocks = (_ucblocks - state->prevUCBlocks);
 437        else
 438                *ucblocks = state->prevUCBlocks - _ucblocks;
 439        state->prevUCBlocks = _ucblocks;
 440
 441        return 0;
 442}
 443
 444static int cx22702_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
 445{
 446        struct cx22702_state* state = fe->demodulator_priv;
 447
 448        u8 reg0C = cx22702_readreg (state, 0x0C);
 449
 450        p->inversion = reg0C & 0x1 ? INVERSION_ON : INVERSION_OFF;
 451        return cx22702_get_tps (state, &p->u.ofdm);
 452}
 453
 454static int cx22702_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
 455{
 456        tune->min_delay_ms = 1000;
 457        return 0;
 458}
 459
 460static void cx22702_release(struct dvb_frontend* fe)
 461{
 462        struct cx22702_state* state = fe->demodulator_priv;
 463        kfree(state);
 464}
 465
 466static struct dvb_frontend_ops cx22702_ops;
 467
 468struct dvb_frontend* cx22702_attach(const struct cx22702_config* config,
 469                                    struct i2c_adapter* i2c)
 470{
 471        struct cx22702_state* state = NULL;
 472
 473        /* allocate memory for the internal state */
 474        state = kmalloc(sizeof(struct cx22702_state), GFP_KERNEL);
 475        if (state == NULL)
 476                goto error;
 477
 478        /* setup the state */
 479        state->config = config;
 480        state->i2c = i2c;
 481        state->prevUCBlocks = 0;
 482
 483        /* check if the demod is there */
 484        if (cx22702_readreg(state, 0x1f) != 0x3)
 485                goto error;
 486
 487        /* create dvb_frontend */
 488        memcpy(&state->frontend.ops, &cx22702_ops, sizeof(struct dvb_frontend_ops));
 489        state->frontend.demodulator_priv = state;
 490        return &state->frontend;
 491
 492error:
 493        kfree(state);
 494        return NULL;
 495}
 496
 497static struct dvb_frontend_ops cx22702_ops = {
 498
 499        .info = {
 500                .name                   = "Conexant CX22702 DVB-T",
 501                .type                   = FE_OFDM,
 502                .frequency_min          = 177000000,
 503                .frequency_max          = 858000000,
 504                .frequency_stepsize     = 166666,
 505                .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
 506                FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
 507                FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
 508                FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
 509                FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER
 510        },
 511
 512        .release = cx22702_release,
 513
 514        .init = cx22702_init,
 515        .i2c_gate_ctrl = cx22702_i2c_gate_ctrl,
 516
 517        .set_frontend = cx22702_set_tps,
 518        .get_frontend = cx22702_get_frontend,
 519        .get_tune_settings = cx22702_get_tune_settings,
 520
 521        .read_status = cx22702_read_status,
 522        .read_ber = cx22702_read_ber,
 523        .read_signal_strength = cx22702_read_signal_strength,
 524        .read_snr = cx22702_read_snr,
 525        .read_ucblocks = cx22702_read_ucblocks,
 526};
 527
 528module_param(debug, int, 0644);
 529MODULE_PARM_DESC(debug, "Enable verbose debug messages");
 530
 531MODULE_DESCRIPTION("Conexant CX22702 DVB-T Demodulator driver");
 532MODULE_AUTHOR("Steven Toth");
 533MODULE_LICENSE("GPL");
 534
 535EXPORT_SYMBOL(cx22702_attach);
 536
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.