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