2
* Fujitu mb86a20s ISDB-T/ISDB-Tsb Module driver
4
* Copyright (C) 2010 Mauro Carvalho Chehab <mchehab@redhat.com>
5
* Copyright (C) 2009-2010 Douglas Landgraf <dougsland@redhat.com>
7
* FIXME: Need to port to DVB v5.2 API
9
* This program is free software; you can redistribute it and/or
10
* modify it under the terms of the GNU General Public License as
11
* published by the Free Software Foundation version 2.
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 GNU
16
* General Public License for more details.
19
#include <linux/kernel.h>
20
#include <asm/div64.h>
22
#include "dvb_frontend.h"
26
module_param(debug, int, 0644);
27
MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
29
#define rc(args...) do { \
30
printk(KERN_ERR "mb86a20s: " args); \
33
#define dprintk(args...) \
36
printk(KERN_DEBUG "mb86a20s: %s: ", __func__); \
41
struct mb86a20s_state {
42
struct i2c_adapter *i2c;
43
const struct mb86a20s_config *config;
45
struct dvb_frontend frontend;
56
* Initialization sequence: Use whatevere default values that PV SBTVD
57
* does on its initialisation, obtained via USB snoop
59
static struct regdata mb86a20s_init[] = {
176
{ 0x51, 0x01 }, /* Serial */
305
static struct regdata mb86a20s_reset_reception[] = {
312
static int mb86a20s_i2c_writereg(struct mb86a20s_state *state,
313
u8 i2c_addr, int reg, int data)
315
u8 buf[] = { reg, data };
316
struct i2c_msg msg = {
317
.addr = i2c_addr, .flags = 0, .buf = buf, .len = 2
321
rc = i2c_transfer(state->i2c, &msg, 1);
323
printk("%s: writereg error (rc == %i, reg == 0x%02x,"
324
" data == 0x%02x)\n", __func__, rc, reg, data);
331
static int mb86a20s_i2c_writeregdata(struct mb86a20s_state *state,
332
u8 i2c_addr, struct regdata *rd, int size)
336
for (i = 0; i < size; i++) {
337
rc = mb86a20s_i2c_writereg(state, i2c_addr, rd[i].reg,
345
static int mb86a20s_i2c_readreg(struct mb86a20s_state *state,
350
struct i2c_msg msg[] = {
351
{ .addr = i2c_addr, .flags = 0, .buf = ®, .len = 1 },
352
{ .addr = i2c_addr, .flags = I2C_M_RD, .buf = &val, .len = 1 }
355
rc = i2c_transfer(state->i2c, msg, 2);
358
rc("%s: reg=0x%x (error=%d)\n", __func__, reg, rc);
365
#define mb86a20s_readreg(state, reg) \
366
mb86a20s_i2c_readreg(state, state->config->demod_address, reg)
367
#define mb86a20s_writereg(state, reg, val) \
368
mb86a20s_i2c_writereg(state, state->config->demod_address, reg, val)
369
#define mb86a20s_writeregdata(state, regdata) \
370
mb86a20s_i2c_writeregdata(state, state->config->demod_address, \
371
regdata, ARRAY_SIZE(regdata))
373
static int mb86a20s_initfe(struct dvb_frontend *fe)
375
struct mb86a20s_state *state = fe->demodulator_priv;
381
if (fe->ops.i2c_gate_ctrl)
382
fe->ops.i2c_gate_ctrl(fe, 0);
384
/* Initialize the frontend */
385
rc = mb86a20s_writeregdata(state, mb86a20s_init);
389
if (!state->config->is_serial) {
392
rc = mb86a20s_writereg(state, 0x50, 0xd5);
395
rc = mb86a20s_writereg(state, 0x51, regD5);
400
if (fe->ops.i2c_gate_ctrl)
401
fe->ops.i2c_gate_ctrl(fe, 1);
405
state->need_init = true;
406
printk(KERN_INFO "mb86a20s: Init failed. Will try again later\n");
408
state->need_init = false;
409
dprintk("Initialization succeeded.\n");
414
static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
416
struct mb86a20s_state *state = fe->demodulator_priv;
417
unsigned rf_max, rf_min, rf;
422
if (fe->ops.i2c_gate_ctrl)
423
fe->ops.i2c_gate_ctrl(fe, 0);
425
/* Does a binary search to get RF strength */
429
rf = (rf_max + rf_min) / 2;
430
mb86a20s_writereg(state, 0x04, 0x1f);
431
mb86a20s_writereg(state, 0x05, rf >> 8);
432
mb86a20s_writereg(state, 0x04, 0x20);
433
mb86a20s_writereg(state, 0x04, rf);
435
val = mb86a20s_readreg(state, 0x02);
437
rf_min = (rf_max + rf_min) / 2;
439
rf_max = (rf_max + rf_min) / 2;
440
if (rf_max - rf_min < 4) {
441
*strength = (((rf_max + rf_min) / 2) * 65535) / 4095;
446
dprintk("signal strength = %d\n", *strength);
448
if (fe->ops.i2c_gate_ctrl)
449
fe->ops.i2c_gate_ctrl(fe, 1);
454
static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status)
456
struct mb86a20s_state *state = fe->demodulator_priv;
462
if (fe->ops.i2c_gate_ctrl)
463
fe->ops.i2c_gate_ctrl(fe, 0);
464
val = mb86a20s_readreg(state, 0x0a) & 0xf;
465
if (fe->ops.i2c_gate_ctrl)
466
fe->ops.i2c_gate_ctrl(fe, 1);
469
*status |= FE_HAS_SIGNAL;
472
*status |= FE_HAS_CARRIER;
475
*status |= FE_HAS_VITERBI;
478
*status |= FE_HAS_SYNC;
480
if (val >= 8) /* Maybe 9? */
481
*status |= FE_HAS_LOCK;
483
dprintk("val = %d, status = 0x%02x\n", val, *status);
488
static int mb86a20s_set_frontend(struct dvb_frontend *fe,
489
struct dvb_frontend_parameters *p)
491
struct mb86a20s_state *state = fe->demodulator_priv;
496
if (fe->ops.i2c_gate_ctrl)
497
fe->ops.i2c_gate_ctrl(fe, 1);
498
dprintk("Calling tuner set parameters\n");
499
fe->ops.tuner_ops.set_params(fe, p);
502
* Make it more reliable: if, for some reason, the initial
503
* device initialization doesn't happen, initialize it when
504
* a SBTVD parameters are adjusted.
506
* Unfortunately, due to a hard to track bug at tda829x/tda18271,
507
* the agc callback logic is not called during DVB attach time,
508
* causing mb86a20s to not be initialized with Kworld SBTVD.
509
* So, this hack is needed, in order to make Kworld SBTVD to work.
511
if (state->need_init)
514
if (fe->ops.i2c_gate_ctrl)
515
fe->ops.i2c_gate_ctrl(fe, 0);
516
rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception);
517
if (fe->ops.i2c_gate_ctrl)
518
fe->ops.i2c_gate_ctrl(fe, 1);
523
static int mb86a20s_get_frontend(struct dvb_frontend *fe,
524
struct dvb_frontend_parameters *p)
527
/* FIXME: For now, it does nothing */
529
fe->dtv_property_cache.bandwidth_hz = 6000000;
530
fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_AUTO;
531
fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_AUTO;
532
fe->dtv_property_cache.isdbt_partial_reception = 0;
537
static int mb86a20s_tune(struct dvb_frontend *fe,
538
struct dvb_frontend_parameters *params,
539
unsigned int mode_flags,
548
rc = mb86a20s_set_frontend(fe, params);
550
if (!(mode_flags & FE_TUNE_MODE_ONESHOT))
551
mb86a20s_read_status(fe, status);
556
static void mb86a20s_release(struct dvb_frontend *fe)
558
struct mb86a20s_state *state = fe->demodulator_priv;
565
static struct dvb_frontend_ops mb86a20s_ops;
567
struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config,
568
struct i2c_adapter *i2c)
572
/* allocate memory for the internal state */
573
struct mb86a20s_state *state =
574
kzalloc(sizeof(struct mb86a20s_state), GFP_KERNEL);
578
rc("Unable to kzalloc\n");
582
/* setup the state */
583
state->config = config;
586
/* create dvb_frontend */
587
memcpy(&state->frontend.ops, &mb86a20s_ops,
588
sizeof(struct dvb_frontend_ops));
589
state->frontend.demodulator_priv = state;
591
/* Check if it is a mb86a20s frontend */
592
rev = mb86a20s_readreg(state, 0);
595
printk(KERN_INFO "Detected a Fujitsu mb86a20s frontend\n");
597
printk(KERN_ERR "Frontend revision %d is unknown - aborting.\n",
602
return &state->frontend;
608
EXPORT_SYMBOL(mb86a20s_attach);
610
static struct dvb_frontend_ops mb86a20s_ops = {
611
/* Use dib8000 values per default */
613
.name = "Fujitsu mb86A20s",
615
.caps = FE_CAN_INVERSION_AUTO | FE_CAN_RECOVER |
616
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
617
FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
618
FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
619
FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_QAM_AUTO |
620
FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
621
/* Actually, those values depend on the used tuner */
622
.frequency_min = 45000000,
623
.frequency_max = 864000000,
624
.frequency_stepsize = 62500,
627
.release = mb86a20s_release,
629
.init = mb86a20s_initfe,
630
.set_frontend = mb86a20s_set_frontend,
631
.get_frontend = mb86a20s_get_frontend,
632
.read_status = mb86a20s_read_status,
633
.read_signal_strength = mb86a20s_read_signal_strength,
634
.tune = mb86a20s_tune,
637
MODULE_DESCRIPTION("DVB Frontend module for Fujitsu mb86A20s hardware");
638
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
639
MODULE_LICENSE("GPL");