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");