2 * Sharp VA3A5JZ921 One Seg Broadcast Module driver
3 * This device is labeled as just S. 921 at the top of the frontend can
5 * Copyright (C) 2009-2010 Mauro Carvalho Chehab
6 * Copyright (C) 2009-2010 Douglas Landgraf <dougsland@redhat.com>
8 * Developed for Leadership SBTVD 1seg device sold in Brazil
10 * Frontend module based on cx24123 driver, getting some info from
11 * the old s921 driver.
13 * FIXME: Need to port to DVB v5.2 API
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License as
17 * published by the Free Software Foundation version 2.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
25 #include <linux/kernel.h>
26 #include <asm/div64.h>
28 #include "dvb_frontend.h"
32 module_param(debug
, int, 0644);
33 MODULE_PARM_DESC(debug
, "Activates frontend debugging (default:0)");
35 #define rc(args...) do { \
36 printk(KERN_ERR "s921: " args); \
39 #define dprintk(args...) \
42 printk(KERN_DEBUG "s921: %s: ", __func__); \
48 struct i2c_adapter
*i2c
;
49 const struct s921_config
*config
;
51 struct dvb_frontend frontend
;
53 /* The Demod can't easily provide these, we cache them */
58 * Various tuner defaults need to be established for a given frequency kHz.
59 * fixme: The bounds on the bands do not match the doc in real life.
60 * fixme: Some of them have been moved, other might need adjustment.
62 static struct s921_bandselect_val
{
65 } s921_bandselect
[] = {
81 static struct regdata s921_init
[] = {
82 { 0x01, 0x80 }, /* Probably, a reset sequence */
189 static struct regdata s921_prefreq
[] = {
197 static struct regdata s921_postfreq
[] = {
206 static int s921_i2c_writereg(struct s921_state
*state
,
207 u8 i2c_addr
, int reg
, int data
)
209 u8 buf
[] = { reg
, data
};
210 struct i2c_msg msg
= {
211 .addr
= i2c_addr
, .flags
= 0, .buf
= buf
, .len
= 2
215 rc
= i2c_transfer(state
->i2c
, &msg
, 1);
217 printk("%s: writereg rcor(rc == %i, reg == 0x%02x, data == 0x%02x)\n",
218 __func__
, rc
, reg
, data
);
225 static int s921_i2c_writeregdata(struct s921_state
*state
, u8 i2c_addr
,
226 struct regdata
*rd
, int size
)
230 for (i
= 0; i
< size
; i
++) {
231 rc
= s921_i2c_writereg(state
, i2c_addr
, rd
[i
].reg
, rd
[i
].data
);
238 static int s921_i2c_readreg(struct s921_state
*state
, u8 i2c_addr
, u8 reg
)
242 struct i2c_msg msg
[] = {
243 { .addr
= i2c_addr
, .flags
= 0, .buf
= ®
, .len
= 1 },
244 { .addr
= i2c_addr
, .flags
= I2C_M_RD
, .buf
= &val
, .len
= 1 }
247 rc
= i2c_transfer(state
->i2c
, msg
, 2);
250 rc("%s: reg=0x%x (rcor=%d)\n", __func__
, reg
, rc
);
257 #define s921_readreg(state, reg) \
258 s921_i2c_readreg(state, state->config->demod_address, reg)
259 #define s921_writereg(state, reg, val) \
260 s921_i2c_writereg(state, state->config->demod_address, reg, val)
261 #define s921_writeregdata(state, regdata) \
262 s921_i2c_writeregdata(state, state->config->demod_address, \
263 regdata, ARRAY_SIZE(regdata))
265 static int s921_pll_tune(struct dvb_frontend
*fe
)
267 struct dtv_frontend_properties
*p
= &fe
->dtv_property_cache
;
268 struct s921_state
*state
= fe
->demodulator_priv
;
270 unsigned long f_offset
;
274 dprintk("frequency=%i\n", p
->frequency
);
276 for (band
= 0; band
< ARRAY_SIZE(s921_bandselect
); band
++)
277 if (p
->frequency
< s921_bandselect
[band
].freq_low
)
282 rc("%s: frequency out of range\n", __func__
);
286 f_switch
= s921_bandselect
[band
].band_reg
;
288 offset
= ((u64
)p
->frequency
) * 258;
289 do_div(offset
, 6000000);
290 f_offset
= ((unsigned long)offset
) + 2321;
292 rc
= s921_writeregdata(state
, s921_prefreq
);
296 rc
= s921_writereg(state
, 0xf2, (f_offset
>> 8) & 0xff);
300 rc
= s921_writereg(state
, 0xf3, f_offset
& 0xff);
304 rc
= s921_writereg(state
, 0xf4, f_switch
);
308 rc
= s921_writeregdata(state
, s921_postfreq
);
312 for (i
= 0 ; i
< 6; i
++) {
313 rc
= s921_readreg(state
, 0x80);
314 dprintk("status 0x80: %02x\n", rc
);
316 rc
= s921_writereg(state
, 0x01, 0x40);
320 rc
= s921_readreg(state
, 0x01);
321 dprintk("status 0x01: %02x\n", rc
);
323 rc
= s921_readreg(state
, 0x80);
324 dprintk("status 0x80: %02x\n", rc
);
326 rc
= s921_readreg(state
, 0x80);
327 dprintk("status 0x80: %02x\n", rc
);
329 rc
= s921_readreg(state
, 0x32);
330 dprintk("status 0x32: %02x\n", rc
);
332 dprintk("pll tune band=%d, pll=%d\n", f_switch
, (int)f_offset
);
337 static int s921_initfe(struct dvb_frontend
*fe
)
339 struct s921_state
*state
= fe
->demodulator_priv
;
344 rc
= s921_writeregdata(state
, s921_init
);
351 static int s921_read_status(struct dvb_frontend
*fe
, enum fe_status
*status
)
353 struct s921_state
*state
= fe
->demodulator_priv
;
358 rc
= s921_readreg(state
, 0x81);
364 rc
= s921_readreg(state
, 0x82);
370 dprintk("status = %04x\n", regstatus
);
372 /* Full Sync - We don't know what each bit means on regs 0x81/0x82 */
373 if ((regstatus
& 0xff) == 0x40) {
374 *status
= FE_HAS_SIGNAL
|
379 } else if (regstatus
& 0x40) {
380 /* This is close to Full Sync, but not enough to get useful info */
381 *status
= FE_HAS_SIGNAL
|
390 static int s921_read_signal_strength(struct dvb_frontend
*fe
, u16
*strength
)
392 enum fe_status status
;
393 struct s921_state
*state
= fe
->demodulator_priv
;
396 /* FIXME: Use the proper register for it... 0x80? */
397 rc
= s921_read_status(fe
, &status
);
401 *strength
= (status
& FE_HAS_LOCK
) ? 0xffff : 0;
403 dprintk("strength = 0x%04x\n", *strength
);
405 rc
= s921_readreg(state
, 0x01);
406 dprintk("status 0x01: %02x\n", rc
);
408 rc
= s921_readreg(state
, 0x80);
409 dprintk("status 0x80: %02x\n", rc
);
411 rc
= s921_readreg(state
, 0x32);
412 dprintk("status 0x32: %02x\n", rc
);
417 static int s921_set_frontend(struct dvb_frontend
*fe
)
419 struct dtv_frontend_properties
*p
= &fe
->dtv_property_cache
;
420 struct s921_state
*state
= fe
->demodulator_priv
;
425 /* FIXME: We don't know how to use non-auto mode */
427 rc
= s921_pll_tune(fe
);
431 state
->currentfreq
= p
->frequency
;
436 static int s921_get_frontend(struct dvb_frontend
*fe
,
437 struct dtv_frontend_properties
*p
)
439 struct s921_state
*state
= fe
->demodulator_priv
;
441 /* FIXME: Probably it is possible to get it from regs f1 and f2 */
442 p
->frequency
= state
->currentfreq
;
443 p
->delivery_system
= SYS_ISDBT
;
448 static int s921_tune(struct dvb_frontend
*fe
,
450 unsigned int mode_flags
,
452 enum fe_status
*status
)
459 rc
= s921_set_frontend(fe
);
461 if (!(mode_flags
& FE_TUNE_MODE_ONESHOT
))
462 s921_read_status(fe
, status
);
467 static int s921_get_algo(struct dvb_frontend
*fe
)
469 return DVBFE_ALGO_HW
;
472 static void s921_release(struct dvb_frontend
*fe
)
474 struct s921_state
*state
= fe
->demodulator_priv
;
480 static const struct dvb_frontend_ops s921_ops
;
482 struct dvb_frontend
*s921_attach(const struct s921_config
*config
,
483 struct i2c_adapter
*i2c
)
485 /* allocate memory for the internal state */
486 struct s921_state
*state
=
487 kzalloc(sizeof(struct s921_state
), GFP_KERNEL
);
491 rc("Unable to kzalloc\n");
495 /* setup the state */
496 state
->config
= config
;
499 /* create dvb_frontend */
500 memcpy(&state
->frontend
.ops
, &s921_ops
,
501 sizeof(struct dvb_frontend_ops
));
502 state
->frontend
.demodulator_priv
= state
;
504 return &state
->frontend
;
506 EXPORT_SYMBOL(s921_attach
);
508 static const struct dvb_frontend_ops s921_ops
= {
509 .delsys
= { SYS_ISDBT
},
510 /* Use dib8000 values per default */
512 .name
= "Sharp S921",
513 .frequency_min
= 470000000,
515 * Max should be 770MHz instead, according with Sharp docs,
516 * but Leadership doc says it works up to 806 MHz. This is
517 * required to get channel 69, used in Brazil
519 .frequency_max
= 806000000,
520 .frequency_tolerance
= 0,
521 .caps
= FE_CAN_INVERSION_AUTO
|
522 FE_CAN_FEC_1_2
| FE_CAN_FEC_2_3
| FE_CAN_FEC_3_4
|
523 FE_CAN_FEC_5_6
| FE_CAN_FEC_7_8
| FE_CAN_FEC_AUTO
|
524 FE_CAN_QPSK
| FE_CAN_QAM_16
| FE_CAN_QAM_64
|
525 FE_CAN_QAM_AUTO
| FE_CAN_TRANSMISSION_MODE_AUTO
|
526 FE_CAN_GUARD_INTERVAL_AUTO
| FE_CAN_RECOVER
|
527 FE_CAN_HIERARCHY_AUTO
,
530 .release
= s921_release
,
533 .set_frontend
= s921_set_frontend
,
534 .get_frontend
= s921_get_frontend
,
535 .read_status
= s921_read_status
,
536 .read_signal_strength
= s921_read_signal_strength
,
538 .get_frontend_algo
= s921_get_algo
,
541 MODULE_DESCRIPTION("DVB Frontend module for Sharp S921 hardware");
542 MODULE_AUTHOR("Mauro Carvalho Chehab");
543 MODULE_AUTHOR("Douglas Landgraf <dougsland@redhat.com>");
544 MODULE_LICENSE("GPL");