2 * Abilis Systems Single DVB-T Receiver
3 * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
4 * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
17 #include <media/dvb_frontend.h>
22 struct dvb_frontend frontend
;
23 struct as10x_demod_stats demod_stats
;
25 const struct as102_fe_ops
*ops
;
30 uint16_t signal_strength
;
35 static uint8_t as102_fe_get_code_rate(enum fe_code_rate arg
)
56 c
= CODE_RATE_UNKNOWN
;
63 static int as102_fe_set_frontend(struct dvb_frontend
*fe
)
65 struct as102_state
*state
= fe
->demodulator_priv
;
66 struct dtv_frontend_properties
*c
= &fe
->dtv_property_cache
;
67 struct as10x_tune_args tune_args
= { 0 };
70 tune_args
.freq
= c
->frequency
/ 1000;
72 /* fix interleaving_mode */
73 tune_args
.interleaving_mode
= INTLV_NATIVE
;
75 switch (c
->bandwidth_hz
) {
77 tune_args
.bandwidth
= BW_8_MHZ
;
80 tune_args
.bandwidth
= BW_7_MHZ
;
83 tune_args
.bandwidth
= BW_6_MHZ
;
86 tune_args
.bandwidth
= BW_8_MHZ
;
89 switch (c
->guard_interval
) {
90 case GUARD_INTERVAL_1_32
:
91 tune_args
.guard_interval
= GUARD_INT_1_32
;
93 case GUARD_INTERVAL_1_16
:
94 tune_args
.guard_interval
= GUARD_INT_1_16
;
96 case GUARD_INTERVAL_1_8
:
97 tune_args
.guard_interval
= GUARD_INT_1_8
;
99 case GUARD_INTERVAL_1_4
:
100 tune_args
.guard_interval
= GUARD_INT_1_4
;
102 case GUARD_INTERVAL_AUTO
:
104 tune_args
.guard_interval
= GUARD_UNKNOWN
;
108 switch (c
->modulation
) {
110 tune_args
.modulation
= CONST_QPSK
;
113 tune_args
.modulation
= CONST_QAM16
;
116 tune_args
.modulation
= CONST_QAM64
;
119 tune_args
.modulation
= CONST_UNKNOWN
;
123 switch (c
->transmission_mode
) {
124 case TRANSMISSION_MODE_2K
:
125 tune_args
.transmission_mode
= TRANS_MODE_2K
;
127 case TRANSMISSION_MODE_8K
:
128 tune_args
.transmission_mode
= TRANS_MODE_8K
;
131 tune_args
.transmission_mode
= TRANS_MODE_UNKNOWN
;
134 switch (c
->hierarchy
) {
136 tune_args
.hierarchy
= HIER_NONE
;
139 tune_args
.hierarchy
= HIER_ALPHA_1
;
142 tune_args
.hierarchy
= HIER_ALPHA_2
;
145 tune_args
.hierarchy
= HIER_ALPHA_4
;
148 tune_args
.hierarchy
= HIER_UNKNOWN
;
152 pr_debug("as102: tuner parameters: freq: %d bw: 0x%02x gi: 0x%02x\n",
155 tune_args
.guard_interval
);
158 * Detect a hierarchy selection
159 * if HP/LP are both set to FEC_NONE, HP will be selected.
161 if ((tune_args
.hierarchy
!= HIER_NONE
) &&
162 ((c
->code_rate_LP
== FEC_NONE
) ||
163 (c
->code_rate_HP
== FEC_NONE
))) {
165 if (c
->code_rate_LP
== FEC_NONE
) {
166 tune_args
.hier_select
= HIER_HIGH_PRIORITY
;
167 tune_args
.code_rate
=
168 as102_fe_get_code_rate(c
->code_rate_HP
);
171 if (c
->code_rate_HP
== FEC_NONE
) {
172 tune_args
.hier_select
= HIER_LOW_PRIORITY
;
173 tune_args
.code_rate
=
174 as102_fe_get_code_rate(c
->code_rate_LP
);
177 pr_debug("as102: \thierarchy: 0x%02x selected: %s code_rate_%s: 0x%02x\n",
179 tune_args
.hier_select
== HIER_HIGH_PRIORITY
?
181 tune_args
.hier_select
== HIER_HIGH_PRIORITY
?
183 tune_args
.code_rate
);
185 tune_args
.code_rate
=
186 as102_fe_get_code_rate(c
->code_rate_HP
);
189 /* Set frontend arguments */
190 return state
->ops
->set_tune(state
->priv
, &tune_args
);
193 static int as102_fe_get_frontend(struct dvb_frontend
*fe
,
194 struct dtv_frontend_properties
*c
)
196 struct as102_state
*state
= fe
->demodulator_priv
;
198 struct as10x_tps tps
= { 0 };
200 /* send abilis command: GET_TPS */
201 ret
= state
->ops
->get_tps(state
->priv
, &tps
);
205 /* extract constellation */
206 switch (tps
.modulation
) {
208 c
->modulation
= QPSK
;
211 c
->modulation
= QAM_16
;
214 c
->modulation
= QAM_64
;
218 /* extract hierarchy */
219 switch (tps
.hierarchy
) {
221 c
->hierarchy
= HIERARCHY_NONE
;
224 c
->hierarchy
= HIERARCHY_1
;
227 c
->hierarchy
= HIERARCHY_2
;
230 c
->hierarchy
= HIERARCHY_4
;
234 /* extract code rate HP */
235 switch (tps
.code_rate_HP
) {
237 c
->code_rate_HP
= FEC_1_2
;
240 c
->code_rate_HP
= FEC_2_3
;
243 c
->code_rate_HP
= FEC_3_4
;
246 c
->code_rate_HP
= FEC_5_6
;
249 c
->code_rate_HP
= FEC_7_8
;
253 /* extract code rate LP */
254 switch (tps
.code_rate_LP
) {
256 c
->code_rate_LP
= FEC_1_2
;
259 c
->code_rate_LP
= FEC_2_3
;
262 c
->code_rate_LP
= FEC_3_4
;
265 c
->code_rate_LP
= FEC_5_6
;
268 c
->code_rate_LP
= FEC_7_8
;
272 /* extract guard interval */
273 switch (tps
.guard_interval
) {
275 c
->guard_interval
= GUARD_INTERVAL_1_32
;
278 c
->guard_interval
= GUARD_INTERVAL_1_16
;
281 c
->guard_interval
= GUARD_INTERVAL_1_8
;
284 c
->guard_interval
= GUARD_INTERVAL_1_4
;
288 /* extract transmission mode */
289 switch (tps
.transmission_mode
) {
291 c
->transmission_mode
= TRANSMISSION_MODE_2K
;
294 c
->transmission_mode
= TRANSMISSION_MODE_8K
;
301 static int as102_fe_get_tune_settings(struct dvb_frontend
*fe
,
302 struct dvb_frontend_tune_settings
*settings
) {
304 settings
->min_delay_ms
= 1000;
309 static int as102_fe_read_status(struct dvb_frontend
*fe
, enum fe_status
*status
)
312 struct as102_state
*state
= fe
->demodulator_priv
;
313 struct as10x_tune_status tstate
= { 0 };
315 /* send abilis command: GET_TUNE_STATUS */
316 ret
= state
->ops
->get_status(state
->priv
, &tstate
);
320 state
->signal_strength
= tstate
.signal_strength
;
321 state
->ber
= tstate
.BER
;
323 switch (tstate
.tune_state
) {
324 case TUNE_STATUS_SIGNAL_DVB_OK
:
325 *status
= FE_HAS_SIGNAL
| FE_HAS_CARRIER
;
327 case TUNE_STATUS_STREAM_DETECTED
:
328 *status
= FE_HAS_SIGNAL
| FE_HAS_CARRIER
| FE_HAS_SYNC
|
331 case TUNE_STATUS_STREAM_TUNED
:
332 *status
= FE_HAS_SIGNAL
| FE_HAS_CARRIER
| FE_HAS_SYNC
|
333 FE_HAS_LOCK
| FE_HAS_VITERBI
;
336 *status
= TUNE_STATUS_NOT_TUNED
;
339 pr_debug("as102: tuner status: 0x%02x, strength %d, per: %d, ber: %d\n",
340 tstate
.tune_state
, tstate
.signal_strength
,
341 tstate
.PER
, tstate
.BER
);
343 if (!(*status
& FE_HAS_LOCK
)) {
344 memset(&state
->demod_stats
, 0, sizeof(state
->demod_stats
));
348 ret
= state
->ops
->get_stats(state
->priv
, &state
->demod_stats
);
350 memset(&state
->demod_stats
, 0, sizeof(state
->demod_stats
));
358 * - the SNR will be returned in linear terms, i.e. not in dB
359 * - the accuracy equals ±2dB for a SNR range from 4dB to 30dB
360 * - the accuracy is >2dB for SNR values outside this range
362 static int as102_fe_read_snr(struct dvb_frontend
*fe
, u16
*snr
)
364 struct as102_state
*state
= fe
->demodulator_priv
;
366 *snr
= state
->demod_stats
.mer
;
371 static int as102_fe_read_ber(struct dvb_frontend
*fe
, u32
*ber
)
373 struct as102_state
*state
= fe
->demodulator_priv
;
380 static int as102_fe_read_signal_strength(struct dvb_frontend
*fe
,
383 struct as102_state
*state
= fe
->demodulator_priv
;
385 *strength
= (((0xffff * 400) * state
->signal_strength
+ 41000) * 2);
390 static int as102_fe_read_ucblocks(struct dvb_frontend
*fe
, u32
*ucblocks
)
392 struct as102_state
*state
= fe
->demodulator_priv
;
394 if (state
->demod_stats
.has_started
)
395 *ucblocks
= state
->demod_stats
.bad_frame_count
;
402 static int as102_fe_ts_bus_ctrl(struct dvb_frontend
*fe
, int acquire
)
404 struct as102_state
*state
= fe
->demodulator_priv
;
406 return state
->ops
->stream_ctrl(state
->priv
, acquire
,
410 static void as102_fe_release(struct dvb_frontend
*fe
)
412 struct as102_state
*state
= fe
->demodulator_priv
;
418 static const struct dvb_frontend_ops as102_fe_ops
= {
419 .delsys
= { SYS_DVBT
},
421 .name
= "Abilis AS102 DVB-T",
422 .frequency_min
= 174000000,
423 .frequency_max
= 862000000,
424 .frequency_stepsize
= 166667,
425 .caps
= FE_CAN_INVERSION_AUTO
426 | FE_CAN_FEC_1_2
| FE_CAN_FEC_2_3
| FE_CAN_FEC_3_4
427 | FE_CAN_FEC_5_6
| FE_CAN_FEC_7_8
| FE_CAN_FEC_AUTO
428 | FE_CAN_QAM_16
| FE_CAN_QAM_64
| FE_CAN_QPSK
430 | FE_CAN_TRANSMISSION_MODE_AUTO
431 | FE_CAN_GUARD_INTERVAL_AUTO
432 | FE_CAN_HIERARCHY_AUTO
437 .set_frontend
= as102_fe_set_frontend
,
438 .get_frontend
= as102_fe_get_frontend
,
439 .get_tune_settings
= as102_fe_get_tune_settings
,
441 .read_status
= as102_fe_read_status
,
442 .read_snr
= as102_fe_read_snr
,
443 .read_ber
= as102_fe_read_ber
,
444 .read_signal_strength
= as102_fe_read_signal_strength
,
445 .read_ucblocks
= as102_fe_read_ucblocks
,
446 .ts_bus_ctrl
= as102_fe_ts_bus_ctrl
,
447 .release
= as102_fe_release
,
450 struct dvb_frontend
*as102_attach(const char *name
,
451 const struct as102_fe_ops
*ops
,
455 struct as102_state
*state
;
456 struct dvb_frontend
*fe
;
458 state
= kzalloc(sizeof(*state
), GFP_KERNEL
);
462 fe
= &state
->frontend
;
463 fe
->demodulator_priv
= state
;
466 state
->elna_cfg
= elna_cfg
;
468 /* init frontend callback ops */
469 memcpy(&fe
->ops
, &as102_fe_ops
, sizeof(struct dvb_frontend_ops
));
470 strncpy(fe
->ops
.info
.name
, name
, sizeof(fe
->ops
.info
.name
));
475 EXPORT_SYMBOL_GPL(as102_attach
);
477 MODULE_DESCRIPTION("as102-fe");
478 MODULE_LICENSE("GPL");
479 MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>");