1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Abilis Systems Single DVB-T Receiver
4 * Copyright (C) 2008 Pierrick Hascoet <pierrick.hascoet@abilis.com>
5 * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
8 #include <media/dvb_frontend.h>
13 struct dvb_frontend frontend
;
14 struct as10x_demod_stats demod_stats
;
16 const struct as102_fe_ops
*ops
;
21 uint16_t signal_strength
;
26 static uint8_t as102_fe_get_code_rate(enum fe_code_rate arg
)
47 c
= CODE_RATE_UNKNOWN
;
54 static int as102_fe_set_frontend(struct dvb_frontend
*fe
)
56 struct as102_state
*state
= fe
->demodulator_priv
;
57 struct dtv_frontend_properties
*c
= &fe
->dtv_property_cache
;
58 struct as10x_tune_args tune_args
= { 0 };
61 tune_args
.freq
= c
->frequency
/ 1000;
63 /* fix interleaving_mode */
64 tune_args
.interleaving_mode
= INTLV_NATIVE
;
66 switch (c
->bandwidth_hz
) {
68 tune_args
.bandwidth
= BW_8_MHZ
;
71 tune_args
.bandwidth
= BW_7_MHZ
;
74 tune_args
.bandwidth
= BW_6_MHZ
;
77 tune_args
.bandwidth
= BW_8_MHZ
;
80 switch (c
->guard_interval
) {
81 case GUARD_INTERVAL_1_32
:
82 tune_args
.guard_interval
= GUARD_INT_1_32
;
84 case GUARD_INTERVAL_1_16
:
85 tune_args
.guard_interval
= GUARD_INT_1_16
;
87 case GUARD_INTERVAL_1_8
:
88 tune_args
.guard_interval
= GUARD_INT_1_8
;
90 case GUARD_INTERVAL_1_4
:
91 tune_args
.guard_interval
= GUARD_INT_1_4
;
93 case GUARD_INTERVAL_AUTO
:
95 tune_args
.guard_interval
= GUARD_UNKNOWN
;
99 switch (c
->modulation
) {
101 tune_args
.modulation
= CONST_QPSK
;
104 tune_args
.modulation
= CONST_QAM16
;
107 tune_args
.modulation
= CONST_QAM64
;
110 tune_args
.modulation
= CONST_UNKNOWN
;
114 switch (c
->transmission_mode
) {
115 case TRANSMISSION_MODE_2K
:
116 tune_args
.transmission_mode
= TRANS_MODE_2K
;
118 case TRANSMISSION_MODE_8K
:
119 tune_args
.transmission_mode
= TRANS_MODE_8K
;
122 tune_args
.transmission_mode
= TRANS_MODE_UNKNOWN
;
125 switch (c
->hierarchy
) {
127 tune_args
.hierarchy
= HIER_NONE
;
130 tune_args
.hierarchy
= HIER_ALPHA_1
;
133 tune_args
.hierarchy
= HIER_ALPHA_2
;
136 tune_args
.hierarchy
= HIER_ALPHA_4
;
139 tune_args
.hierarchy
= HIER_UNKNOWN
;
143 pr_debug("as102: tuner parameters: freq: %d bw: 0x%02x gi: 0x%02x\n",
146 tune_args
.guard_interval
);
149 * Detect a hierarchy selection
150 * if HP/LP are both set to FEC_NONE, HP will be selected.
152 if ((tune_args
.hierarchy
!= HIER_NONE
) &&
153 ((c
->code_rate_LP
== FEC_NONE
) ||
154 (c
->code_rate_HP
== FEC_NONE
))) {
156 if (c
->code_rate_LP
== FEC_NONE
) {
157 tune_args
.hier_select
= HIER_HIGH_PRIORITY
;
158 tune_args
.code_rate
=
159 as102_fe_get_code_rate(c
->code_rate_HP
);
162 if (c
->code_rate_HP
== FEC_NONE
) {
163 tune_args
.hier_select
= HIER_LOW_PRIORITY
;
164 tune_args
.code_rate
=
165 as102_fe_get_code_rate(c
->code_rate_LP
);
168 pr_debug("as102: \thierarchy: 0x%02x selected: %s code_rate_%s: 0x%02x\n",
170 tune_args
.hier_select
== HIER_HIGH_PRIORITY
?
172 tune_args
.hier_select
== HIER_HIGH_PRIORITY
?
174 tune_args
.code_rate
);
176 tune_args
.code_rate
=
177 as102_fe_get_code_rate(c
->code_rate_HP
);
180 /* Set frontend arguments */
181 return state
->ops
->set_tune(state
->priv
, &tune_args
);
184 static int as102_fe_get_frontend(struct dvb_frontend
*fe
,
185 struct dtv_frontend_properties
*c
)
187 struct as102_state
*state
= fe
->demodulator_priv
;
189 struct as10x_tps tps
= { 0 };
191 /* send abilis command: GET_TPS */
192 ret
= state
->ops
->get_tps(state
->priv
, &tps
);
196 /* extract constellation */
197 switch (tps
.modulation
) {
199 c
->modulation
= QPSK
;
202 c
->modulation
= QAM_16
;
205 c
->modulation
= QAM_64
;
209 /* extract hierarchy */
210 switch (tps
.hierarchy
) {
212 c
->hierarchy
= HIERARCHY_NONE
;
215 c
->hierarchy
= HIERARCHY_1
;
218 c
->hierarchy
= HIERARCHY_2
;
221 c
->hierarchy
= HIERARCHY_4
;
225 /* extract code rate HP */
226 switch (tps
.code_rate_HP
) {
228 c
->code_rate_HP
= FEC_1_2
;
231 c
->code_rate_HP
= FEC_2_3
;
234 c
->code_rate_HP
= FEC_3_4
;
237 c
->code_rate_HP
= FEC_5_6
;
240 c
->code_rate_HP
= FEC_7_8
;
244 /* extract code rate LP */
245 switch (tps
.code_rate_LP
) {
247 c
->code_rate_LP
= FEC_1_2
;
250 c
->code_rate_LP
= FEC_2_3
;
253 c
->code_rate_LP
= FEC_3_4
;
256 c
->code_rate_LP
= FEC_5_6
;
259 c
->code_rate_LP
= FEC_7_8
;
263 /* extract guard interval */
264 switch (tps
.guard_interval
) {
266 c
->guard_interval
= GUARD_INTERVAL_1_32
;
269 c
->guard_interval
= GUARD_INTERVAL_1_16
;
272 c
->guard_interval
= GUARD_INTERVAL_1_8
;
275 c
->guard_interval
= GUARD_INTERVAL_1_4
;
279 /* extract transmission mode */
280 switch (tps
.transmission_mode
) {
282 c
->transmission_mode
= TRANSMISSION_MODE_2K
;
285 c
->transmission_mode
= TRANSMISSION_MODE_8K
;
292 static int as102_fe_get_tune_settings(struct dvb_frontend
*fe
,
293 struct dvb_frontend_tune_settings
*settings
)
296 settings
->min_delay_ms
= 1000;
301 static int as102_fe_read_status(struct dvb_frontend
*fe
, enum fe_status
*status
)
304 struct as102_state
*state
= fe
->demodulator_priv
;
305 struct as10x_tune_status tstate
= { 0 };
307 /* send abilis command: GET_TUNE_STATUS */
308 ret
= state
->ops
->get_status(state
->priv
, &tstate
);
312 state
->signal_strength
= tstate
.signal_strength
;
313 state
->ber
= tstate
.BER
;
315 switch (tstate
.tune_state
) {
316 case TUNE_STATUS_SIGNAL_DVB_OK
:
317 *status
= FE_HAS_SIGNAL
| FE_HAS_CARRIER
;
319 case TUNE_STATUS_STREAM_DETECTED
:
320 *status
= FE_HAS_SIGNAL
| FE_HAS_CARRIER
| FE_HAS_SYNC
|
323 case TUNE_STATUS_STREAM_TUNED
:
324 *status
= FE_HAS_SIGNAL
| FE_HAS_CARRIER
| FE_HAS_SYNC
|
325 FE_HAS_LOCK
| FE_HAS_VITERBI
;
328 *status
= TUNE_STATUS_NOT_TUNED
;
331 pr_debug("as102: tuner status: 0x%02x, strength %d, per: %d, ber: %d\n",
332 tstate
.tune_state
, tstate
.signal_strength
,
333 tstate
.PER
, tstate
.BER
);
335 if (!(*status
& FE_HAS_LOCK
)) {
336 memset(&state
->demod_stats
, 0, sizeof(state
->demod_stats
));
340 ret
= state
->ops
->get_stats(state
->priv
, &state
->demod_stats
);
342 memset(&state
->demod_stats
, 0, sizeof(state
->demod_stats
));
350 * - the SNR will be returned in linear terms, i.e. not in dB
351 * - the accuracy equals ±2dB for a SNR range from 4dB to 30dB
352 * - the accuracy is >2dB for SNR values outside this range
354 static int as102_fe_read_snr(struct dvb_frontend
*fe
, u16
*snr
)
356 struct as102_state
*state
= fe
->demodulator_priv
;
358 *snr
= state
->demod_stats
.mer
;
363 static int as102_fe_read_ber(struct dvb_frontend
*fe
, u32
*ber
)
365 struct as102_state
*state
= fe
->demodulator_priv
;
372 static int as102_fe_read_signal_strength(struct dvb_frontend
*fe
,
375 struct as102_state
*state
= fe
->demodulator_priv
;
377 *strength
= (((0xffff * 400) * state
->signal_strength
+ 41000) * 2);
382 static int as102_fe_read_ucblocks(struct dvb_frontend
*fe
, u32
*ucblocks
)
384 struct as102_state
*state
= fe
->demodulator_priv
;
386 if (state
->demod_stats
.has_started
)
387 *ucblocks
= state
->demod_stats
.bad_frame_count
;
394 static int as102_fe_ts_bus_ctrl(struct dvb_frontend
*fe
, int acquire
)
396 struct as102_state
*state
= fe
->demodulator_priv
;
398 return state
->ops
->stream_ctrl(state
->priv
, acquire
,
402 static void as102_fe_release(struct dvb_frontend
*fe
)
404 struct as102_state
*state
= fe
->demodulator_priv
;
410 static const struct dvb_frontend_ops as102_fe_ops
= {
411 .delsys
= { SYS_DVBT
},
413 .name
= "Abilis AS102 DVB-T",
414 .frequency_min_hz
= 174 * MHz
,
415 .frequency_max_hz
= 862 * MHz
,
416 .frequency_stepsize_hz
= 166667,
417 .caps
= FE_CAN_INVERSION_AUTO
418 | FE_CAN_FEC_1_2
| FE_CAN_FEC_2_3
| FE_CAN_FEC_3_4
419 | FE_CAN_FEC_5_6
| FE_CAN_FEC_7_8
| FE_CAN_FEC_AUTO
420 | FE_CAN_QAM_16
| FE_CAN_QAM_64
| FE_CAN_QPSK
422 | FE_CAN_TRANSMISSION_MODE_AUTO
423 | FE_CAN_GUARD_INTERVAL_AUTO
424 | FE_CAN_HIERARCHY_AUTO
429 .set_frontend
= as102_fe_set_frontend
,
430 .get_frontend
= as102_fe_get_frontend
,
431 .get_tune_settings
= as102_fe_get_tune_settings
,
433 .read_status
= as102_fe_read_status
,
434 .read_snr
= as102_fe_read_snr
,
435 .read_ber
= as102_fe_read_ber
,
436 .read_signal_strength
= as102_fe_read_signal_strength
,
437 .read_ucblocks
= as102_fe_read_ucblocks
,
438 .ts_bus_ctrl
= as102_fe_ts_bus_ctrl
,
439 .release
= as102_fe_release
,
442 struct dvb_frontend
*as102_attach(const char *name
,
443 const struct as102_fe_ops
*ops
,
447 struct as102_state
*state
;
448 struct dvb_frontend
*fe
;
450 state
= kzalloc(sizeof(*state
), GFP_KERNEL
);
454 fe
= &state
->frontend
;
455 fe
->demodulator_priv
= state
;
458 state
->elna_cfg
= elna_cfg
;
460 /* init frontend callback ops */
461 memcpy(&fe
->ops
, &as102_fe_ops
, sizeof(struct dvb_frontend_ops
));
462 strscpy(fe
->ops
.info
.name
, name
, sizeof(fe
->ops
.info
.name
));
467 EXPORT_SYMBOL_GPL(as102_attach
);
469 MODULE_DESCRIPTION("as102-fe");
470 MODULE_LICENSE("GPL");
471 MODULE_AUTHOR("Pierrick Hascoet <pierrick.hascoet@abilis.com>");