Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/media/dvb/frontends/cx24113.c
15112 views
1
/*
2
* Driver for Conexant CX24113/CX24128 Tuner (Satellite)
3
*
4
* Copyright (C) 2007-8 Patrick Boettcher <[email protected]>
5
*
6
* Developed for BBTI / Technisat
7
*
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 2 of the License, or
11
* (at your option) any later version.
12
*
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
*
17
* GNU General Public License for more details.
18
*
19
* You should have received a copy of the GNU General Public License
20
* along with this program; if not, write to the Free Software
21
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
*/
23
24
#include <linux/slab.h>
25
#include <linux/kernel.h>
26
#include <linux/module.h>
27
#include <linux/init.h>
28
29
#include "dvb_frontend.h"
30
#include "cx24113.h"
31
32
static int debug;
33
34
#define info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0)
35
#define err(args...) do { printk(KERN_ERR "CX24113: " args); } while (0)
36
37
#define dprintk(args...) \
38
do { \
39
if (debug) { \
40
printk(KERN_DEBUG "CX24113: %s: ", __func__); \
41
printk(args); \
42
} \
43
} while (0)
44
45
struct cx24113_state {
46
struct i2c_adapter *i2c;
47
const struct cx24113_config *config;
48
49
#define REV_CX24113 0x23
50
u8 rev;
51
u8 ver;
52
53
u8 icp_mode:1;
54
55
#define ICP_LEVEL1 0
56
#define ICP_LEVEL2 1
57
#define ICP_LEVEL3 2
58
#define ICP_LEVEL4 3
59
u8 icp_man:2;
60
u8 icp_auto_low:2;
61
u8 icp_auto_mlow:2;
62
u8 icp_auto_mhi:2;
63
u8 icp_auto_hi:2;
64
u8 icp_dig;
65
66
#define LNA_MIN_GAIN 0
67
#define LNA_MID_GAIN 1
68
#define LNA_MAX_GAIN 2
69
u8 lna_gain:2;
70
71
u8 acp_on:1;
72
73
u8 vco_mode:2;
74
u8 vco_shift:1;
75
#define VCOBANDSEL_6 0x80
76
#define VCOBANDSEL_5 0x01
77
#define VCOBANDSEL_4 0x02
78
#define VCOBANDSEL_3 0x04
79
#define VCOBANDSEL_2 0x08
80
#define VCOBANDSEL_1 0x10
81
u8 vco_band;
82
83
#define VCODIV4 4
84
#define VCODIV2 2
85
u8 vcodiv;
86
87
u8 bs_delay:4;
88
u16 bs_freqcnt:13;
89
u16 bs_rdiv;
90
u8 prescaler_mode:1;
91
92
u8 rfvga_bias_ctrl;
93
94
s16 tuner_gain_thres;
95
u8 gain_level;
96
97
u32 frequency;
98
99
u8 refdiv;
100
101
u8 Fwindow_enabled;
102
};
103
104
static int cx24113_writereg(struct cx24113_state *state, int reg, int data)
105
{
106
u8 buf[] = { reg, data };
107
struct i2c_msg msg = { .addr = state->config->i2c_addr,
108
.flags = 0, .buf = buf, .len = 2 };
109
int err = i2c_transfer(state->i2c, &msg, 1);
110
if (err != 1) {
111
printk(KERN_DEBUG "%s: writereg error(err == %i, reg == 0x%02x,"
112
" data == 0x%02x)\n", __func__, err, reg, data);
113
return err;
114
}
115
116
return 0;
117
}
118
119
static int cx24113_readreg(struct cx24113_state *state, u8 reg)
120
{
121
int ret;
122
u8 b;
123
struct i2c_msg msg[] = {
124
{ .addr = state->config->i2c_addr,
125
.flags = 0, .buf = &reg, .len = 1 },
126
{ .addr = state->config->i2c_addr,
127
.flags = I2C_M_RD, .buf = &b, .len = 1 }
128
};
129
130
ret = i2c_transfer(state->i2c, msg, 2);
131
132
if (ret != 2) {
133
printk(KERN_DEBUG "%s: reg=0x%x (error=%d)\n",
134
__func__, reg, ret);
135
return ret;
136
}
137
138
return b;
139
}
140
141
static void cx24113_set_parameters(struct cx24113_state *state)
142
{
143
u8 r;
144
145
r = cx24113_readreg(state, 0x10) & 0x82;
146
r |= state->icp_mode;
147
r |= state->icp_man << 4;
148
r |= state->icp_dig << 2;
149
r |= state->prescaler_mode << 5;
150
cx24113_writereg(state, 0x10, r);
151
152
r = (state->icp_auto_low << 0) | (state->icp_auto_mlow << 2)
153
| (state->icp_auto_mhi << 4) | (state->icp_auto_hi << 6);
154
cx24113_writereg(state, 0x11, r);
155
156
if (state->rev == REV_CX24113) {
157
r = cx24113_readreg(state, 0x20) & 0xec;
158
r |= state->lna_gain;
159
r |= state->rfvga_bias_ctrl << 4;
160
cx24113_writereg(state, 0x20, r);
161
}
162
163
r = cx24113_readreg(state, 0x12) & 0x03;
164
r |= state->acp_on << 2;
165
r |= state->bs_delay << 4;
166
cx24113_writereg(state, 0x12, r);
167
168
r = cx24113_readreg(state, 0x18) & 0x40;
169
r |= state->vco_shift;
170
if (state->vco_band == VCOBANDSEL_6)
171
r |= (1 << 7);
172
else
173
r |= (state->vco_band << 1);
174
cx24113_writereg(state, 0x18, r);
175
176
r = cx24113_readreg(state, 0x14) & 0x20;
177
r |= (state->vco_mode << 6) | ((state->bs_freqcnt >> 8) & 0x1f);
178
cx24113_writereg(state, 0x14, r);
179
cx24113_writereg(state, 0x15, (state->bs_freqcnt & 0xff));
180
181
cx24113_writereg(state, 0x16, (state->bs_rdiv >> 4) & 0xff);
182
r = (cx24113_readreg(state, 0x17) & 0x0f) |
183
((state->bs_rdiv & 0x0f) << 4);
184
cx24113_writereg(state, 0x17, r);
185
}
186
187
#define VGA_0 0x00
188
#define VGA_1 0x04
189
#define VGA_2 0x02
190
#define VGA_3 0x06
191
#define VGA_4 0x01
192
#define VGA_5 0x05
193
#define VGA_6 0x03
194
#define VGA_7 0x07
195
196
#define RFVGA_0 0x00
197
#define RFVGA_1 0x01
198
#define RFVGA_2 0x02
199
#define RFVGA_3 0x03
200
201
static int cx24113_set_gain_settings(struct cx24113_state *state,
202
s16 power_estimation)
203
{
204
u8 ampout = cx24113_readreg(state, 0x1d) & 0xf0,
205
vga = cx24113_readreg(state, 0x1f) & 0x3f,
206
rfvga = cx24113_readreg(state, 0x20) & 0xf3;
207
u8 gain_level = power_estimation >= state->tuner_gain_thres;
208
209
dprintk("power estimation: %d, thres: %d, gain_level: %d/%d\n",
210
power_estimation, state->tuner_gain_thres,
211
state->gain_level, gain_level);
212
213
if (gain_level == state->gain_level)
214
return 0; /* nothing to be done */
215
216
ampout |= 0xf;
217
218
if (gain_level) {
219
rfvga |= RFVGA_0 << 2;
220
vga |= (VGA_7 << 3) | VGA_7;
221
} else {
222
rfvga |= RFVGA_2 << 2;
223
vga |= (VGA_6 << 3) | VGA_2;
224
}
225
state->gain_level = gain_level;
226
227
cx24113_writereg(state, 0x1d, ampout);
228
cx24113_writereg(state, 0x1f, vga);
229
cx24113_writereg(state, 0x20, rfvga);
230
231
return 1; /* did something */
232
}
233
234
static int cx24113_set_Fref(struct cx24113_state *state, u8 high)
235
{
236
u8 xtal = cx24113_readreg(state, 0x02);
237
if (state->rev == 0x43 && state->vcodiv == VCODIV4)
238
high = 1;
239
240
xtal &= ~0x2;
241
if (high)
242
xtal |= high << 1;
243
return cx24113_writereg(state, 0x02, xtal);
244
}
245
246
static int cx24113_enable(struct cx24113_state *state, u8 enable)
247
{
248
u8 r21 = (cx24113_readreg(state, 0x21) & 0xc0) | enable;
249
if (state->rev == REV_CX24113)
250
r21 |= (1 << 1);
251
return cx24113_writereg(state, 0x21, r21);
252
}
253
254
static int cx24113_set_bandwidth(struct cx24113_state *state, u32 bandwidth_khz)
255
{
256
u8 r;
257
258
if (bandwidth_khz <= 19000)
259
r = 0x03 << 6;
260
else if (bandwidth_khz <= 25000)
261
r = 0x02 << 6;
262
else
263
r = 0x01 << 6;
264
265
dprintk("bandwidth to be set: %d\n", bandwidth_khz);
266
bandwidth_khz *= 10;
267
bandwidth_khz -= 10000;
268
bandwidth_khz /= 1000;
269
bandwidth_khz += 5;
270
bandwidth_khz /= 10;
271
272
dprintk("bandwidth: %d %d\n", r >> 6, bandwidth_khz);
273
274
r |= bandwidth_khz & 0x3f;
275
276
return cx24113_writereg(state, 0x1e, r);
277
}
278
279
static int cx24113_set_clk_inversion(struct cx24113_state *state, u8 on)
280
{
281
u8 r = (cx24113_readreg(state, 0x10) & 0x7f) | ((on & 0x1) << 7);
282
return cx24113_writereg(state, 0x10, r);
283
}
284
285
static int cx24113_get_status(struct dvb_frontend *fe, u32 *status)
286
{
287
struct cx24113_state *state = fe->tuner_priv;
288
u8 r = (cx24113_readreg(state, 0x10) & 0x02) >> 1;
289
if (r)
290
*status |= TUNER_STATUS_LOCKED;
291
dprintk("PLL locked: %d\n", r);
292
return 0;
293
}
294
295
static u8 cx24113_set_ref_div(struct cx24113_state *state, u8 refdiv)
296
{
297
if (state->rev == 0x43 && state->vcodiv == VCODIV4)
298
refdiv = 2;
299
return state->refdiv = refdiv;
300
}
301
302
static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f)
303
{
304
s32 N;
305
s64 F;
306
u64 dividend;
307
u8 R, r;
308
u8 vcodiv;
309
u8 factor;
310
s32 freq_hz = state->frequency * 1000;
311
312
if (state->config->xtal_khz < 20000)
313
factor = 1;
314
else
315
factor = 2;
316
317
if (state->rev == REV_CX24113) {
318
if (state->frequency >= 1100000)
319
vcodiv = VCODIV2;
320
else
321
vcodiv = VCODIV4;
322
} else {
323
if (state->frequency >= 1165000)
324
vcodiv = VCODIV2;
325
else
326
vcodiv = VCODIV4;
327
}
328
state->vcodiv = vcodiv;
329
330
dprintk("calculating N/F for %dHz with vcodiv %d\n", freq_hz, vcodiv);
331
R = 0;
332
do {
333
R = cx24113_set_ref_div(state, R + 1);
334
335
/* calculate tuner PLL settings: */
336
N = (freq_hz / 100 * vcodiv) * R;
337
N /= (state->config->xtal_khz) * factor * 2;
338
N += 5; /* For round up. */
339
N /= 10;
340
N -= 32;
341
} while (N < 6 && R < 3);
342
343
if (N < 6) {
344
err("strange frequency: N < 6\n");
345
return;
346
}
347
F = freq_hz;
348
F *= (u64) (R * vcodiv * 262144);
349
dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
350
/* do_div needs an u64 as first argument */
351
dividend = F;
352
do_div(dividend, state->config->xtal_khz * 1000 * factor * 2);
353
F = dividend;
354
dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
355
F -= (N + 32) * 262144;
356
357
dprintk("3 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
358
359
if (state->Fwindow_enabled) {
360
if (F > (262144 / 2 - 1638))
361
F = 262144 / 2 - 1638;
362
if (F < (-262144 / 2 + 1638))
363
F = -262144 / 2 + 1638;
364
if ((F < 3277 && F > 0) || (F > -3277 && F < 0)) {
365
F = 0;
366
r = cx24113_readreg(state, 0x10);
367
cx24113_writereg(state, 0x10, r | (1 << 6));
368
}
369
}
370
dprintk("4 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
371
372
*n = (u16) N;
373
*f = (s32) F;
374
}
375
376
377
static void cx24113_set_nfr(struct cx24113_state *state, u16 n, s32 f, u8 r)
378
{
379
u8 reg;
380
cx24113_writereg(state, 0x19, (n >> 1) & 0xff);
381
382
reg = ((n & 0x1) << 7) | ((f >> 11) & 0x7f);
383
cx24113_writereg(state, 0x1a, reg);
384
385
cx24113_writereg(state, 0x1b, (f >> 3) & 0xff);
386
387
reg = cx24113_readreg(state, 0x1c) & 0x1f;
388
cx24113_writereg(state, 0x1c, reg | ((f & 0x7) << 5));
389
390
cx24113_set_Fref(state, r - 1);
391
}
392
393
static int cx24113_set_frequency(struct cx24113_state *state, u32 frequency)
394
{
395
u8 r = 1; /* or 2 */
396
u16 n = 6;
397
s32 f = 0;
398
399
r = cx24113_readreg(state, 0x14);
400
cx24113_writereg(state, 0x14, r & 0x3f);
401
402
r = cx24113_readreg(state, 0x10);
403
cx24113_writereg(state, 0x10, r & 0xbf);
404
405
state->frequency = frequency;
406
407
dprintk("tuning to frequency: %d\n", frequency);
408
409
cx24113_calc_pll_nf(state, &n, &f);
410
cx24113_set_nfr(state, n, f, state->refdiv);
411
412
r = cx24113_readreg(state, 0x18) & 0xbf;
413
if (state->vcodiv != VCODIV2)
414
r |= 1 << 6;
415
cx24113_writereg(state, 0x18, r);
416
417
/* The need for this sleep is not clear. But helps in some cases */
418
msleep(5);
419
420
r = cx24113_readreg(state, 0x1c) & 0xef;
421
cx24113_writereg(state, 0x1c, r | (1 << 4));
422
return 0;
423
}
424
425
static int cx24113_init(struct dvb_frontend *fe)
426
{
427
struct cx24113_state *state = fe->tuner_priv;
428
int ret;
429
430
state->tuner_gain_thres = -50;
431
state->gain_level = 255; /* to force a gain-setting initialization */
432
state->icp_mode = 0;
433
434
if (state->config->xtal_khz < 11000) {
435
state->icp_auto_hi = ICP_LEVEL4;
436
state->icp_auto_mhi = ICP_LEVEL4;
437
state->icp_auto_mlow = ICP_LEVEL3;
438
state->icp_auto_low = ICP_LEVEL3;
439
} else {
440
state->icp_auto_hi = ICP_LEVEL4;
441
state->icp_auto_mhi = ICP_LEVEL4;
442
state->icp_auto_mlow = ICP_LEVEL3;
443
state->icp_auto_low = ICP_LEVEL2;
444
}
445
446
state->icp_dig = ICP_LEVEL3;
447
state->icp_man = ICP_LEVEL1;
448
state->acp_on = 1;
449
state->vco_mode = 0;
450
state->vco_shift = 0;
451
state->vco_band = VCOBANDSEL_1;
452
state->bs_delay = 8;
453
state->bs_freqcnt = 0x0fff;
454
state->bs_rdiv = 0x0fff;
455
state->prescaler_mode = 0;
456
state->lna_gain = LNA_MAX_GAIN;
457
state->rfvga_bias_ctrl = 1;
458
state->Fwindow_enabled = 1;
459
460
cx24113_set_Fref(state, 0);
461
cx24113_enable(state, 0x3d);
462
cx24113_set_parameters(state);
463
464
cx24113_set_gain_settings(state, -30);
465
466
cx24113_set_bandwidth(state, 18025);
467
cx24113_set_clk_inversion(state, 1);
468
469
if (state->config->xtal_khz >= 40000)
470
ret = cx24113_writereg(state, 0x02,
471
(cx24113_readreg(state, 0x02) & 0xfb) | (1 << 2));
472
else
473
ret = cx24113_writereg(state, 0x02,
474
(cx24113_readreg(state, 0x02) & 0xfb) | (0 << 2));
475
476
return ret;
477
}
478
479
static int cx24113_set_params(struct dvb_frontend *fe,
480
struct dvb_frontend_parameters *p)
481
{
482
struct cx24113_state *state = fe->tuner_priv;
483
/* for a ROLL-OFF factor of 0.35, 0.2: 600, 0.25: 625 */
484
u32 roll_off = 675;
485
u32 bw;
486
487
bw = ((p->u.qpsk.symbol_rate/100) * roll_off) / 1000;
488
bw += (10000000/100) + 5;
489
bw /= 10;
490
bw += 1000;
491
cx24113_set_bandwidth(state, bw);
492
493
cx24113_set_frequency(state, p->frequency);
494
msleep(5);
495
return cx24113_get_status(fe, &bw);
496
}
497
498
static s8 cx24113_agc_table[2][10] = {
499
{-54, -41, -35, -30, -25, -21, -16, -10, -6, -2},
500
{-39, -35, -30, -25, -19, -15, -11, -5, 1, 9},
501
};
502
503
void cx24113_agc_callback(struct dvb_frontend *fe)
504
{
505
struct cx24113_state *state = fe->tuner_priv;
506
s16 s, i;
507
if (!fe->ops.read_signal_strength)
508
return;
509
510
do {
511
/* this only works with the current CX24123 implementation */
512
fe->ops.read_signal_strength(fe, (u16 *) &s);
513
s >>= 8;
514
dprintk("signal strength: %d\n", s);
515
for (i = 0; i < sizeof(cx24113_agc_table[0]); i++)
516
if (cx24113_agc_table[state->gain_level][i] > s)
517
break;
518
s = -25 - i*5;
519
} while (cx24113_set_gain_settings(state, s));
520
}
521
EXPORT_SYMBOL(cx24113_agc_callback);
522
523
static int cx24113_get_frequency(struct dvb_frontend *fe, u32 *frequency)
524
{
525
struct cx24113_state *state = fe->tuner_priv;
526
*frequency = state->frequency;
527
return 0;
528
}
529
530
static int cx24113_release(struct dvb_frontend *fe)
531
{
532
struct cx24113_state *state = fe->tuner_priv;
533
dprintk("\n");
534
fe->tuner_priv = NULL;
535
kfree(state);
536
return 0;
537
}
538
539
static const struct dvb_tuner_ops cx24113_tuner_ops = {
540
.info = {
541
.name = "Conexant CX24113",
542
.frequency_min = 950000,
543
.frequency_max = 2150000,
544
.frequency_step = 125,
545
},
546
547
.release = cx24113_release,
548
549
.init = cx24113_init,
550
.sleep = NULL,
551
552
.set_params = cx24113_set_params,
553
.get_frequency = cx24113_get_frequency,
554
.get_bandwidth = NULL,
555
.get_status = cx24113_get_status,
556
};
557
558
struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe,
559
const struct cx24113_config *config, struct i2c_adapter *i2c)
560
{
561
/* allocate memory for the internal state */
562
struct cx24113_state *state =
563
kzalloc(sizeof(struct cx24113_state), GFP_KERNEL);
564
int rc;
565
if (state == NULL) {
566
err("Unable to kzalloc\n");
567
goto error;
568
}
569
570
/* setup the state */
571
state->config = config;
572
state->i2c = i2c;
573
574
info("trying to detect myself\n");
575
576
/* making a dummy read, because of some expected troubles
577
* after power on */
578
cx24113_readreg(state, 0x00);
579
580
rc = cx24113_readreg(state, 0x00);
581
if (rc < 0) {
582
info("CX24113 not found.\n");
583
goto error;
584
}
585
state->rev = rc;
586
587
switch (rc) {
588
case 0x43:
589
info("detected CX24113 variant\n");
590
break;
591
case REV_CX24113:
592
info("successfully detected\n");
593
break;
594
default:
595
err("unsupported device id: %x\n", state->rev);
596
goto error;
597
}
598
state->ver = cx24113_readreg(state, 0x01);
599
info("version: %x\n", state->ver);
600
601
/* create dvb_frontend */
602
memcpy(&fe->ops.tuner_ops, &cx24113_tuner_ops,
603
sizeof(struct dvb_tuner_ops));
604
fe->tuner_priv = state;
605
return fe;
606
607
error:
608
kfree(state);
609
610
return NULL;
611
}
612
EXPORT_SYMBOL(cx24113_attach);
613
614
module_param(debug, int, 0644);
615
MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
616
617
MODULE_AUTHOR("Patrick Boettcher <[email protected]>");
618
MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24113/CX24128hardware");
619
MODULE_LICENSE("GPL");
620
621
622