1 // SPDX-License-Identifier: GPL-2.0-only
3 * Sharp VA3A5JZ921 One Seg Broadcast Module driver
4 * This device is labeled as just S. 921 at the top of the frontend can
6 * Copyright (C) 2009-2010 Mauro Carvalho Chehab
7 * Copyright (C) 2009-2010 Douglas Landgraf <dougsland@redhat.com>
9 * Developed for Leadership SBTVD 1seg device sold in Brazil
11 * Frontend module based on cx24123 driver, getting some info from
12 * the old s921 driver.
14 * FIXME: Need to port to DVB v5.2 API
17 #include <linux/kernel.h>
18 #include <asm/div64.h>
20 #include <media/dvb_frontend.h>
24 module_param(debug
, int, 0644);
25 MODULE_PARM_DESC(debug
, "Activates frontend debugging (default:0)");
27 #define rc(args...) do { \
28 printk(KERN_ERR "s921: " args); \
31 #define dprintk(args...) \
34 printk(KERN_DEBUG "s921: %s: ", __func__); \
40 struct i2c_adapter
*i2c
;
41 const struct s921_config
*config
;
43 struct dvb_frontend frontend
;
45 /* The Demod can't easily provide these, we cache them */
50 * Various tuner defaults need to be established for a given frequency kHz.
51 * fixme: The bounds on the bands do not match the doc in real life.
52 * fixme: Some of them have been moved, other might need adjustment.
54 static struct s921_bandselect_val
{
57 } s921_bandselect
[] = {
73 static struct regdata s921_init
[] = {
74 { 0x01, 0x80 }, /* Probably, a reset sequence */
181 static struct regdata s921_prefreq
[] = {
189 static struct regdata s921_postfreq
[] = {
198 static int s921_i2c_writereg(struct s921_state
*state
,
199 u8 i2c_addr
, int reg
, int data
)
201 u8 buf
[] = { reg
, data
};
202 struct i2c_msg msg
= {
203 .addr
= i2c_addr
, .flags
= 0, .buf
= buf
, .len
= 2
207 rc
= i2c_transfer(state
->i2c
, &msg
, 1);
209 printk("%s: writereg rcor(rc == %i, reg == 0x%02x, data == 0x%02x)\n",
210 __func__
, rc
, reg
, data
);
217 static int s921_i2c_writeregdata(struct s921_state
*state
, u8 i2c_addr
,
218 struct regdata
*rd
, int size
)
222 for (i
= 0; i
< size
; i
++) {
223 rc
= s921_i2c_writereg(state
, i2c_addr
, rd
[i
].reg
, rd
[i
].data
);
230 static int s921_i2c_readreg(struct s921_state
*state
, u8 i2c_addr
, u8 reg
)
234 struct i2c_msg msg
[] = {
235 { .addr
= i2c_addr
, .flags
= 0, .buf
= ®
, .len
= 1 },
236 { .addr
= i2c_addr
, .flags
= I2C_M_RD
, .buf
= &val
, .len
= 1 }
239 rc
= i2c_transfer(state
->i2c
, msg
, 2);
242 rc("%s: reg=0x%x (rcor=%d)\n", __func__
, reg
, rc
);
249 #define s921_readreg(state, reg) \
250 s921_i2c_readreg(state, state->config->demod_address, reg)
251 #define s921_writereg(state, reg, val) \
252 s921_i2c_writereg(state, state->config->demod_address, reg, val)
253 #define s921_writeregdata(state, regdata) \
254 s921_i2c_writeregdata(state, state->config->demod_address, \
255 regdata, ARRAY_SIZE(regdata))
257 static int s921_pll_tune(struct dvb_frontend
*fe
)
259 struct dtv_frontend_properties
*p
= &fe
->dtv_property_cache
;
260 struct s921_state
*state
= fe
->demodulator_priv
;
262 unsigned long f_offset
;
266 dprintk("frequency=%i\n", p
->frequency
);
268 for (band
= 0; band
< ARRAY_SIZE(s921_bandselect
); band
++)
269 if (p
->frequency
< s921_bandselect
[band
].freq_low
)
274 rc("%s: frequency out of range\n", __func__
);
278 f_switch
= s921_bandselect
[band
].band_reg
;
280 offset
= ((u64
)p
->frequency
) * 258;
281 do_div(offset
, 6000000);
282 f_offset
= ((unsigned long)offset
) + 2321;
284 rc
= s921_writeregdata(state
, s921_prefreq
);
288 rc
= s921_writereg(state
, 0xf2, (f_offset
>> 8) & 0xff);
292 rc
= s921_writereg(state
, 0xf3, f_offset
& 0xff);
296 rc
= s921_writereg(state
, 0xf4, f_switch
);
300 rc
= s921_writeregdata(state
, s921_postfreq
);
304 for (i
= 0 ; i
< 6; i
++) {
305 rc
= s921_readreg(state
, 0x80);
306 dprintk("status 0x80: %02x\n", rc
);
308 rc
= s921_writereg(state
, 0x01, 0x40);
312 rc
= s921_readreg(state
, 0x01);
313 dprintk("status 0x01: %02x\n", rc
);
315 rc
= s921_readreg(state
, 0x80);
316 dprintk("status 0x80: %02x\n", rc
);
318 rc
= s921_readreg(state
, 0x80);
319 dprintk("status 0x80: %02x\n", rc
);
321 rc
= s921_readreg(state
, 0x32);
322 dprintk("status 0x32: %02x\n", rc
);
324 dprintk("pll tune band=%d, pll=%d\n", f_switch
, (int)f_offset
);
329 static int s921_initfe(struct dvb_frontend
*fe
)
331 struct s921_state
*state
= fe
->demodulator_priv
;
336 rc
= s921_writeregdata(state
, s921_init
);
343 static int s921_read_status(struct dvb_frontend
*fe
, enum fe_status
*status
)
345 struct s921_state
*state
= fe
->demodulator_priv
;
350 rc
= s921_readreg(state
, 0x81);
356 rc
= s921_readreg(state
, 0x82);
362 dprintk("status = %04x\n", regstatus
);
364 /* Full Sync - We don't know what each bit means on regs 0x81/0x82 */
365 if ((regstatus
& 0xff) == 0x40) {
366 *status
= FE_HAS_SIGNAL
|
371 } else if (regstatus
& 0x40) {
372 /* This is close to Full Sync, but not enough to get useful info */
373 *status
= FE_HAS_SIGNAL
|
382 static int s921_read_signal_strength(struct dvb_frontend
*fe
, u16
*strength
)
384 enum fe_status status
;
385 struct s921_state
*state
= fe
->demodulator_priv
;
388 /* FIXME: Use the proper register for it... 0x80? */
389 rc
= s921_read_status(fe
, &status
);
393 *strength
= (status
& FE_HAS_LOCK
) ? 0xffff : 0;
395 dprintk("strength = 0x%04x\n", *strength
);
397 rc
= s921_readreg(state
, 0x01);
398 dprintk("status 0x01: %02x\n", rc
);
400 rc
= s921_readreg(state
, 0x80);
401 dprintk("status 0x80: %02x\n", rc
);
403 rc
= s921_readreg(state
, 0x32);
404 dprintk("status 0x32: %02x\n", rc
);
409 static int s921_set_frontend(struct dvb_frontend
*fe
)
411 struct dtv_frontend_properties
*p
= &fe
->dtv_property_cache
;
412 struct s921_state
*state
= fe
->demodulator_priv
;
417 /* FIXME: We don't know how to use non-auto mode */
419 rc
= s921_pll_tune(fe
);
423 state
->currentfreq
= p
->frequency
;
428 static int s921_get_frontend(struct dvb_frontend
*fe
,
429 struct dtv_frontend_properties
*p
)
431 struct s921_state
*state
= fe
->demodulator_priv
;
433 /* FIXME: Probably it is possible to get it from regs f1 and f2 */
434 p
->frequency
= state
->currentfreq
;
435 p
->delivery_system
= SYS_ISDBT
;
440 static int s921_tune(struct dvb_frontend
*fe
,
442 unsigned int mode_flags
,
444 enum fe_status
*status
)
451 rc
= s921_set_frontend(fe
);
453 if (!(mode_flags
& FE_TUNE_MODE_ONESHOT
))
454 s921_read_status(fe
, status
);
459 static enum dvbfe_algo
s921_get_algo(struct dvb_frontend
*fe
)
461 return DVBFE_ALGO_HW
;
464 static void s921_release(struct dvb_frontend
*fe
)
466 struct s921_state
*state
= fe
->demodulator_priv
;
472 static const struct dvb_frontend_ops s921_ops
;
474 struct dvb_frontend
*s921_attach(const struct s921_config
*config
,
475 struct i2c_adapter
*i2c
)
477 /* allocate memory for the internal state */
478 struct s921_state
*state
=
479 kzalloc(sizeof(struct s921_state
), GFP_KERNEL
);
483 rc("Unable to kzalloc\n");
487 /* setup the state */
488 state
->config
= config
;
491 /* create dvb_frontend */
492 memcpy(&state
->frontend
.ops
, &s921_ops
,
493 sizeof(struct dvb_frontend_ops
));
494 state
->frontend
.demodulator_priv
= state
;
496 return &state
->frontend
;
498 EXPORT_SYMBOL(s921_attach
);
500 static const struct dvb_frontend_ops s921_ops
= {
501 .delsys
= { SYS_ISDBT
},
502 /* Use dib8000 values per default */
504 .name
= "Sharp S921",
505 .frequency_min_hz
= 470 * MHz
,
507 * Max should be 770MHz instead, according with Sharp docs,
508 * but Leadership doc says it works up to 806 MHz. This is
509 * required to get channel 69, used in Brazil
511 .frequency_max_hz
= 806 * MHz
,
512 .caps
= FE_CAN_INVERSION_AUTO
|
513 FE_CAN_FEC_1_2
| FE_CAN_FEC_2_3
| FE_CAN_FEC_3_4
|
514 FE_CAN_FEC_5_6
| FE_CAN_FEC_7_8
| FE_CAN_FEC_AUTO
|
515 FE_CAN_QPSK
| FE_CAN_QAM_16
| FE_CAN_QAM_64
|
516 FE_CAN_QAM_AUTO
| FE_CAN_TRANSMISSION_MODE_AUTO
|
517 FE_CAN_GUARD_INTERVAL_AUTO
| FE_CAN_RECOVER
|
518 FE_CAN_HIERARCHY_AUTO
,
521 .release
= s921_release
,
524 .set_frontend
= s921_set_frontend
,
525 .get_frontend
= s921_get_frontend
,
526 .read_status
= s921_read_status
,
527 .read_signal_strength
= s921_read_signal_strength
,
529 .get_frontend_algo
= s921_get_algo
,
532 MODULE_DESCRIPTION("DVB Frontend module for Sharp S921 hardware");
533 MODULE_AUTHOR("Mauro Carvalho Chehab");
534 MODULE_AUTHOR("Douglas Landgraf <dougsland@redhat.com>");
535 MODULE_LICENSE("GPL");