Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  Driver for Conexant CX24113/CX24128 Tuner (Satellite)
0004  *
0005  *  Copyright (C) 2007-8 Patrick Boettcher <pb@linuxtv.org>
0006  *
0007  *  Developed for BBTI / Technisat
0008  */
0009 
0010 #include <linux/slab.h>
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/init.h>
0014 
0015 #include <media/dvb_frontend.h>
0016 #include "cx24113.h"
0017 
0018 static int debug;
0019 
0020 #define cx_info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0)
0021 #define cx_err(args...)  do { printk(KERN_ERR  "CX24113: " args); } while (0)
0022 
0023 #define dprintk(args...) \
0024     do { \
0025         if (debug) { \
0026             printk(KERN_DEBUG "CX24113: %s: ", __func__); \
0027             printk(args); \
0028         } \
0029     } while (0)
0030 
0031 struct cx24113_state {
0032     struct i2c_adapter *i2c;
0033     const struct cx24113_config *config;
0034 
0035 #define REV_CX24113 0x23
0036     u8 rev;
0037     u8 ver;
0038 
0039     u8 icp_mode:1;
0040 
0041 #define ICP_LEVEL1 0
0042 #define ICP_LEVEL2 1
0043 #define ICP_LEVEL3 2
0044 #define ICP_LEVEL4 3
0045     u8 icp_man:2;
0046     u8 icp_auto_low:2;
0047     u8 icp_auto_mlow:2;
0048     u8 icp_auto_mhi:2;
0049     u8 icp_auto_hi:2;
0050     u8 icp_dig;
0051 
0052 #define LNA_MIN_GAIN 0
0053 #define LNA_MID_GAIN 1
0054 #define LNA_MAX_GAIN 2
0055     u8 lna_gain:2;
0056 
0057     u8 acp_on:1;
0058 
0059     u8 vco_mode:2;
0060     u8 vco_shift:1;
0061 #define VCOBANDSEL_6 0x80
0062 #define VCOBANDSEL_5 0x01
0063 #define VCOBANDSEL_4 0x02
0064 #define VCOBANDSEL_3 0x04
0065 #define VCOBANDSEL_2 0x08
0066 #define VCOBANDSEL_1 0x10
0067     u8 vco_band;
0068 
0069 #define VCODIV4 4
0070 #define VCODIV2 2
0071     u8 vcodiv;
0072 
0073     u8 bs_delay:4;
0074     u16 bs_freqcnt:13;
0075     u16 bs_rdiv;
0076     u8 prescaler_mode:1;
0077 
0078     u8 rfvga_bias_ctrl;
0079 
0080     s16 tuner_gain_thres;
0081     u8  gain_level;
0082 
0083     u32 frequency;
0084 
0085     u8 refdiv;
0086 
0087     u8 Fwindow_enabled;
0088 };
0089 
0090 static int cx24113_writereg(struct cx24113_state *state, int reg, int data)
0091 {
0092     u8 buf[] = { reg, data };
0093     struct i2c_msg msg = { .addr = state->config->i2c_addr,
0094         .flags = 0, .buf = buf, .len = 2 };
0095     int err = i2c_transfer(state->i2c, &msg, 1);
0096     if (err != 1) {
0097         printk(KERN_DEBUG "%s: writereg error(err == %i, reg == 0x%02x, data == 0x%02x)\n",
0098                __func__, err, reg, data);
0099         return err;
0100     }
0101 
0102     return 0;
0103 }
0104 
0105 static int cx24113_readreg(struct cx24113_state *state, u8 reg)
0106 {
0107     int ret;
0108     u8 b;
0109     struct i2c_msg msg[] = {
0110         { .addr = state->config->i2c_addr,
0111             .flags = 0, .buf = &reg, .len = 1 },
0112         { .addr = state->config->i2c_addr,
0113             .flags = I2C_M_RD, .buf = &b, .len = 1 }
0114     };
0115 
0116     ret = i2c_transfer(state->i2c, msg, 2);
0117 
0118     if (ret != 2) {
0119         printk(KERN_DEBUG "%s: reg=0x%x (error=%d)\n",
0120             __func__, reg, ret);
0121         return ret;
0122     }
0123 
0124     return b;
0125 }
0126 
0127 static void cx24113_set_parameters(struct cx24113_state *state)
0128 {
0129     u8 r;
0130 
0131     r = cx24113_readreg(state, 0x10) & 0x82;
0132     r |= state->icp_mode;
0133     r |= state->icp_man << 4;
0134     r |= state->icp_dig << 2;
0135     r |= state->prescaler_mode << 5;
0136     cx24113_writereg(state, 0x10, r);
0137 
0138     r = (state->icp_auto_low  << 0) | (state->icp_auto_mlow << 2)
0139         | (state->icp_auto_mhi << 4) | (state->icp_auto_hi << 6);
0140     cx24113_writereg(state, 0x11, r);
0141 
0142     if (state->rev == REV_CX24113) {
0143         r = cx24113_readreg(state, 0x20) & 0xec;
0144         r |= state->lna_gain;
0145         r |= state->rfvga_bias_ctrl << 4;
0146         cx24113_writereg(state, 0x20, r);
0147     }
0148 
0149     r = cx24113_readreg(state, 0x12) & 0x03;
0150     r |= state->acp_on << 2;
0151     r |= state->bs_delay << 4;
0152     cx24113_writereg(state, 0x12, r);
0153 
0154     r = cx24113_readreg(state, 0x18) & 0x40;
0155     r |= state->vco_shift;
0156     if (state->vco_band == VCOBANDSEL_6)
0157         r |= (1 << 7);
0158     else
0159         r |= (state->vco_band << 1);
0160     cx24113_writereg(state, 0x18, r);
0161 
0162     r  = cx24113_readreg(state, 0x14) & 0x20;
0163     r |= (state->vco_mode << 6) | ((state->bs_freqcnt >> 8) & 0x1f);
0164     cx24113_writereg(state, 0x14, r);
0165     cx24113_writereg(state, 0x15, (state->bs_freqcnt        & 0xff));
0166 
0167     cx24113_writereg(state, 0x16, (state->bs_rdiv >> 4) & 0xff);
0168     r = (cx24113_readreg(state, 0x17) & 0x0f) |
0169         ((state->bs_rdiv & 0x0f) << 4);
0170     cx24113_writereg(state, 0x17, r);
0171 }
0172 
0173 #define VGA_0 0x00
0174 #define VGA_1 0x04
0175 #define VGA_2 0x02
0176 #define VGA_3 0x06
0177 #define VGA_4 0x01
0178 #define VGA_5 0x05
0179 #define VGA_6 0x03
0180 #define VGA_7 0x07
0181 
0182 #define RFVGA_0 0x00
0183 #define RFVGA_1 0x01
0184 #define RFVGA_2 0x02
0185 #define RFVGA_3 0x03
0186 
0187 static int cx24113_set_gain_settings(struct cx24113_state *state,
0188         s16 power_estimation)
0189 {
0190     u8 ampout = cx24113_readreg(state, 0x1d) & 0xf0,
0191        vga    = cx24113_readreg(state, 0x1f) & 0x3f,
0192        rfvga  = cx24113_readreg(state, 0x20) & 0xf3;
0193     u8 gain_level = power_estimation >= state->tuner_gain_thres;
0194 
0195     dprintk("power estimation: %d, thres: %d, gain_level: %d/%d\n",
0196             power_estimation, state->tuner_gain_thres,
0197             state->gain_level, gain_level);
0198 
0199     if (gain_level == state->gain_level)
0200         return 0; /* nothing to be done */
0201 
0202     ampout |= 0xf;
0203 
0204     if (gain_level) {
0205         rfvga |= RFVGA_0 << 2;
0206         vga   |= (VGA_7 << 3) | VGA_7;
0207     } else {
0208         rfvga |= RFVGA_2 << 2;
0209         vga  |= (VGA_6 << 3) | VGA_2;
0210     }
0211     state->gain_level = gain_level;
0212 
0213     cx24113_writereg(state, 0x1d, ampout);
0214     cx24113_writereg(state, 0x1f, vga);
0215     cx24113_writereg(state, 0x20, rfvga);
0216 
0217     return 1; /* did something */
0218 }
0219 
0220 static int cx24113_set_Fref(struct cx24113_state *state, u8 high)
0221 {
0222     u8 xtal = cx24113_readreg(state, 0x02);
0223     if (state->rev == 0x43 && state->vcodiv == VCODIV4)
0224         high = 1;
0225 
0226     xtal &= ~0x2;
0227     if (high)
0228         xtal |= high << 1;
0229     return cx24113_writereg(state, 0x02, xtal);
0230 }
0231 
0232 static int cx24113_enable(struct cx24113_state *state, u8 enable)
0233 {
0234     u8 r21 = (cx24113_readreg(state, 0x21) & 0xc0) | enable;
0235     if (state->rev == REV_CX24113)
0236         r21 |= (1 << 1);
0237     return cx24113_writereg(state, 0x21, r21);
0238 }
0239 
0240 static int cx24113_set_bandwidth(struct cx24113_state *state, u32 bandwidth_khz)
0241 {
0242     u8 r;
0243 
0244     if (bandwidth_khz <= 19000)
0245         r = 0x03 << 6;
0246     else if (bandwidth_khz <= 25000)
0247         r = 0x02 << 6;
0248     else
0249         r = 0x01 << 6;
0250 
0251     dprintk("bandwidth to be set: %d\n", bandwidth_khz);
0252     bandwidth_khz *= 10;
0253     bandwidth_khz -= 10000;
0254     bandwidth_khz /= 1000;
0255     bandwidth_khz += 5;
0256     bandwidth_khz /= 10;
0257 
0258     dprintk("bandwidth: %d %d\n", r >> 6, bandwidth_khz);
0259 
0260     r |= bandwidth_khz & 0x3f;
0261 
0262     return cx24113_writereg(state, 0x1e, r);
0263 }
0264 
0265 static int cx24113_set_clk_inversion(struct cx24113_state *state, u8 on)
0266 {
0267     u8 r = (cx24113_readreg(state, 0x10) & 0x7f) | ((on & 0x1) << 7);
0268     return cx24113_writereg(state, 0x10, r);
0269 }
0270 
0271 static int cx24113_get_status(struct dvb_frontend *fe, u32 *status)
0272 {
0273     struct cx24113_state *state = fe->tuner_priv;
0274     u8 r = (cx24113_readreg(state, 0x10) & 0x02) >> 1;
0275     if (r)
0276         *status |= TUNER_STATUS_LOCKED;
0277     dprintk("PLL locked: %d\n", r);
0278     return 0;
0279 }
0280 
0281 static u8 cx24113_set_ref_div(struct cx24113_state *state, u8 refdiv)
0282 {
0283     if (state->rev == 0x43 && state->vcodiv == VCODIV4)
0284         refdiv = 2;
0285     return state->refdiv = refdiv;
0286 }
0287 
0288 static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f)
0289 {
0290     s32 N;
0291     s64 F;
0292     u64 dividend;
0293     u8 R, r;
0294     u8 vcodiv;
0295     u8 factor;
0296     s32 freq_hz = state->frequency * 1000;
0297 
0298     if (state->config->xtal_khz < 20000)
0299         factor = 1;
0300     else
0301         factor = 2;
0302 
0303     if (state->rev == REV_CX24113) {
0304         if (state->frequency >= 1100000)
0305             vcodiv = VCODIV2;
0306         else
0307             vcodiv = VCODIV4;
0308     } else {
0309         if (state->frequency >= 1165000)
0310             vcodiv = VCODIV2;
0311         else
0312             vcodiv = VCODIV4;
0313     }
0314     state->vcodiv = vcodiv;
0315 
0316     dprintk("calculating N/F for %dHz with vcodiv %d\n", freq_hz, vcodiv);
0317     R = 0;
0318     do {
0319         R = cx24113_set_ref_div(state, R + 1);
0320 
0321         /* calculate tuner PLL settings: */
0322         N =  (freq_hz / 100 * vcodiv) * R;
0323         N /= (state->config->xtal_khz) * factor * 2;
0324         N += 5;     /* For round up. */
0325         N /= 10;
0326         N -= 32;
0327     } while (N < 6 && R < 3);
0328 
0329     if (N < 6) {
0330         cx_err("strange frequency: N < 6\n");
0331         return;
0332     }
0333     F = freq_hz;
0334     F *= (u64) (R * vcodiv * 262144);
0335     dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
0336     /* do_div needs an u64 as first argument */
0337     dividend = F;
0338     do_div(dividend, state->config->xtal_khz * 1000 * factor * 2);
0339     F = dividend;
0340     dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
0341     F -= (N + 32) * 262144;
0342 
0343     dprintk("3 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
0344 
0345     if (state->Fwindow_enabled) {
0346         if (F > (262144 / 2 - 1638))
0347             F = 262144 / 2 - 1638;
0348         if (F < (-262144 / 2 + 1638))
0349             F = -262144 / 2 + 1638;
0350         if ((F < 3277 && F > 0) || (F > -3277 && F < 0)) {
0351             F = 0;
0352             r = cx24113_readreg(state, 0x10);
0353             cx24113_writereg(state, 0x10, r | (1 << 6));
0354         }
0355     }
0356     dprintk("4 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
0357 
0358     *n = (u16) N;
0359     *f = (s32) F;
0360 }
0361 
0362 
0363 static void cx24113_set_nfr(struct cx24113_state *state, u16 n, s32 f, u8 r)
0364 {
0365     u8 reg;
0366     cx24113_writereg(state, 0x19, (n >> 1) & 0xff);
0367 
0368     reg = ((n & 0x1) << 7) | ((f >> 11) & 0x7f);
0369     cx24113_writereg(state, 0x1a, reg);
0370 
0371     cx24113_writereg(state, 0x1b, (f >> 3) & 0xff);
0372 
0373     reg = cx24113_readreg(state, 0x1c) & 0x1f;
0374     cx24113_writereg(state, 0x1c, reg | ((f & 0x7) << 5));
0375 
0376     cx24113_set_Fref(state, r - 1);
0377 }
0378 
0379 static int cx24113_set_frequency(struct cx24113_state *state, u32 frequency)
0380 {
0381     u8 r;
0382     u16 n = 6;
0383     s32 f = 0;
0384 
0385     r = cx24113_readreg(state, 0x14);
0386     cx24113_writereg(state, 0x14, r & 0x3f);
0387 
0388     r = cx24113_readreg(state, 0x10);
0389     cx24113_writereg(state, 0x10, r & 0xbf);
0390 
0391     state->frequency = frequency;
0392 
0393     dprintk("tuning to frequency: %d\n", frequency);
0394 
0395     cx24113_calc_pll_nf(state, &n, &f);
0396     cx24113_set_nfr(state, n, f, state->refdiv);
0397 
0398     r = cx24113_readreg(state, 0x18) & 0xbf;
0399     if (state->vcodiv != VCODIV2)
0400         r |= 1 << 6;
0401     cx24113_writereg(state, 0x18, r);
0402 
0403     /* The need for this sleep is not clear. But helps in some cases */
0404     msleep(5);
0405 
0406     r = cx24113_readreg(state, 0x1c) & 0xef;
0407     cx24113_writereg(state, 0x1c, r | (1 << 4));
0408     return 0;
0409 }
0410 
0411 static int cx24113_init(struct dvb_frontend *fe)
0412 {
0413     struct cx24113_state *state = fe->tuner_priv;
0414     int ret;
0415 
0416     state->tuner_gain_thres = -50;
0417     state->gain_level = 255; /* to force a gain-setting initialization */
0418     state->icp_mode = 0;
0419 
0420     if (state->config->xtal_khz < 11000) {
0421         state->icp_auto_hi  = ICP_LEVEL4;
0422         state->icp_auto_mhi  = ICP_LEVEL4;
0423         state->icp_auto_mlow = ICP_LEVEL3;
0424         state->icp_auto_low = ICP_LEVEL3;
0425     } else {
0426         state->icp_auto_hi  = ICP_LEVEL4;
0427         state->icp_auto_mhi  = ICP_LEVEL4;
0428         state->icp_auto_mlow = ICP_LEVEL3;
0429         state->icp_auto_low = ICP_LEVEL2;
0430     }
0431 
0432     state->icp_dig = ICP_LEVEL3;
0433     state->icp_man = ICP_LEVEL1;
0434     state->acp_on  = 1;
0435     state->vco_mode = 0;
0436     state->vco_shift = 0;
0437     state->vco_band = VCOBANDSEL_1;
0438     state->bs_delay = 8;
0439     state->bs_freqcnt = 0x0fff;
0440     state->bs_rdiv = 0x0fff;
0441     state->prescaler_mode = 0;
0442     state->lna_gain = LNA_MAX_GAIN;
0443     state->rfvga_bias_ctrl = 1;
0444     state->Fwindow_enabled = 1;
0445 
0446     cx24113_set_Fref(state, 0);
0447     cx24113_enable(state, 0x3d);
0448     cx24113_set_parameters(state);
0449 
0450     cx24113_set_gain_settings(state, -30);
0451 
0452     cx24113_set_bandwidth(state, 18025);
0453     cx24113_set_clk_inversion(state, 1);
0454 
0455     if (state->config->xtal_khz >= 40000)
0456         ret = cx24113_writereg(state, 0x02,
0457             (cx24113_readreg(state, 0x02) & 0xfb) | (1 << 2));
0458     else
0459         ret = cx24113_writereg(state, 0x02,
0460             (cx24113_readreg(state, 0x02) & 0xfb) | (0 << 2));
0461 
0462     return ret;
0463 }
0464 
0465 static int cx24113_set_params(struct dvb_frontend *fe)
0466 {
0467     struct dtv_frontend_properties *c = &fe->dtv_property_cache;
0468     struct cx24113_state *state = fe->tuner_priv;
0469     /* for a ROLL-OFF factor of 0.35, 0.2: 600, 0.25: 625 */
0470     u32 roll_off = 675;
0471     u32 bw;
0472 
0473     bw  = ((c->symbol_rate/100) * roll_off) / 1000;
0474     bw += (10000000/100) + 5;
0475     bw /= 10;
0476     bw += 1000;
0477     cx24113_set_bandwidth(state, bw);
0478 
0479     cx24113_set_frequency(state, c->frequency);
0480     msleep(5);
0481     return cx24113_get_status(fe, &bw);
0482 }
0483 
0484 static s8 cx24113_agc_table[2][10] = {
0485     {-54, -41, -35, -30, -25, -21, -16, -10,  -6,  -2},
0486     {-39, -35, -30, -25, -19, -15, -11,  -5,   1,   9},
0487 };
0488 
0489 void cx24113_agc_callback(struct dvb_frontend *fe)
0490 {
0491     struct cx24113_state *state = fe->tuner_priv;
0492     s16 s, i;
0493     if (!fe->ops.read_signal_strength)
0494         return;
0495 
0496     do {
0497         /* this only works with the current CX24123 implementation */
0498         fe->ops.read_signal_strength(fe, (u16 *) &s);
0499         s >>= 8;
0500         dprintk("signal strength: %d\n", s);
0501         for (i = 0; i < sizeof(cx24113_agc_table[0]); i++)
0502             if (cx24113_agc_table[state->gain_level][i] > s)
0503                 break;
0504         s = -25 - i*5;
0505     } while (cx24113_set_gain_settings(state, s));
0506 }
0507 EXPORT_SYMBOL(cx24113_agc_callback);
0508 
0509 static int cx24113_get_frequency(struct dvb_frontend *fe, u32 *frequency)
0510 {
0511     struct cx24113_state *state = fe->tuner_priv;
0512     *frequency = state->frequency;
0513     return 0;
0514 }
0515 
0516 static void cx24113_release(struct dvb_frontend *fe)
0517 {
0518     struct cx24113_state *state = fe->tuner_priv;
0519     dprintk("\n");
0520     fe->tuner_priv = NULL;
0521     kfree(state);
0522 }
0523 
0524 static const struct dvb_tuner_ops cx24113_tuner_ops = {
0525     .info = {
0526         .name              = "Conexant CX24113",
0527         .frequency_min_hz  =  950 * MHz,
0528         .frequency_max_hz  = 2150 * MHz,
0529         .frequency_step_hz =  125 * kHz,
0530     },
0531 
0532     .release       = cx24113_release,
0533 
0534     .init          = cx24113_init,
0535 
0536     .set_params    = cx24113_set_params,
0537     .get_frequency = cx24113_get_frequency,
0538     .get_status    = cx24113_get_status,
0539 };
0540 
0541 struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe,
0542         const struct cx24113_config *config, struct i2c_adapter *i2c)
0543 {
0544     /* allocate memory for the internal state */
0545     struct cx24113_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
0546     int rc;
0547 
0548     if (!state)
0549         return NULL;
0550 
0551     /* setup the state */
0552     state->config = config;
0553     state->i2c = i2c;
0554 
0555     cx_info("trying to detect myself\n");
0556 
0557     /* making a dummy read, because of some expected troubles
0558      * after power on */
0559     cx24113_readreg(state, 0x00);
0560 
0561     rc = cx24113_readreg(state, 0x00);
0562     if (rc < 0) {
0563         cx_info("CX24113 not found.\n");
0564         goto error;
0565     }
0566     state->rev = rc;
0567 
0568     switch (rc) {
0569     case 0x43:
0570         cx_info("detected CX24113 variant\n");
0571         break;
0572     case REV_CX24113:
0573         cx_info("successfully detected\n");
0574         break;
0575     default:
0576         cx_err("unsupported device id: %x\n", state->rev);
0577         goto error;
0578     }
0579     state->ver = cx24113_readreg(state, 0x01);
0580     cx_info("version: %x\n", state->ver);
0581 
0582     /* create dvb_frontend */
0583     memcpy(&fe->ops.tuner_ops, &cx24113_tuner_ops,
0584             sizeof(struct dvb_tuner_ops));
0585     fe->tuner_priv = state;
0586     return fe;
0587 
0588 error:
0589     kfree(state);
0590 
0591     return NULL;
0592 }
0593 EXPORT_SYMBOL(cx24113_attach);
0594 
0595 module_param(debug, int, 0644);
0596 MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
0597 
0598 MODULE_AUTHOR("Patrick Boettcher <pb@linuxtv.org>");
0599 MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24113/CX24128hardware");
0600 MODULE_LICENSE("GPL");
0601