• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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>
5  *
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)
9  * any later version.
10  *
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.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20 #include "as102_drv.h"
21 #include "as10x_types.h"
22 #include "as10x_cmd.h"
23 
24 static void as10x_fe_copy_tps_parameters(struct dtv_frontend_properties *dst,
25 					 struct as10x_tps *src);
26 
27 static void as102_fe_copy_tune_parameters(struct as10x_tune_args *dst,
28 					  struct dtv_frontend_properties *src);
29 
as102_fe_set_frontend(struct dvb_frontend * fe)30 static int as102_fe_set_frontend(struct dvb_frontend *fe)
31 {
32 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
33 	int ret = 0;
34 	struct as102_dev_t *dev;
35 	struct as10x_tune_args tune_args = { 0 };
36 
37 	ENTER();
38 
39 	dev = (struct as102_dev_t *) fe->tuner_priv;
40 	if (dev == NULL)
41 		return -ENODEV;
42 
43 	if (mutex_lock_interruptible(&dev->bus_adap.lock))
44 		return -EBUSY;
45 
46 	as102_fe_copy_tune_parameters(&tune_args, p);
47 
48 	/* send abilis command: SET_TUNE */
49 	ret =  as10x_cmd_set_tune(&dev->bus_adap, &tune_args);
50 	if (ret != 0)
51 		dprintk(debug, "as10x_cmd_set_tune failed. (err = %d)\n", ret);
52 
53 	mutex_unlock(&dev->bus_adap.lock);
54 
55 	LEAVE();
56 	return (ret < 0) ? -EINVAL : 0;
57 }
58 
as102_fe_get_frontend(struct dvb_frontend * fe)59 static int as102_fe_get_frontend(struct dvb_frontend *fe)
60 {
61 	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
62 	int ret = 0;
63 	struct as102_dev_t *dev;
64 	struct as10x_tps tps = { 0 };
65 
66 	ENTER();
67 
68 	dev = (struct as102_dev_t *) fe->tuner_priv;
69 	if (dev == NULL)
70 		return -EINVAL;
71 
72 	if (mutex_lock_interruptible(&dev->bus_adap.lock))
73 		return -EBUSY;
74 
75 	/* send abilis command: GET_TPS */
76 	ret = as10x_cmd_get_tps(&dev->bus_adap, &tps);
77 
78 	if (ret == 0)
79 		as10x_fe_copy_tps_parameters(p, &tps);
80 
81 	mutex_unlock(&dev->bus_adap.lock);
82 
83 	LEAVE();
84 	return (ret < 0) ? -EINVAL : 0;
85 }
86 
as102_fe_get_tune_settings(struct dvb_frontend * fe,struct dvb_frontend_tune_settings * settings)87 static int as102_fe_get_tune_settings(struct dvb_frontend *fe,
88 			struct dvb_frontend_tune_settings *settings) {
89 	ENTER();
90 
91 #if 0
92 	dprintk(debug, "step_size    = %d\n", settings->step_size);
93 	dprintk(debug, "max_drift    = %d\n", settings->max_drift);
94 	dprintk(debug, "min_delay_ms = %d -> %d\n", settings->min_delay_ms,
95 		1000);
96 #endif
97 
98 	settings->min_delay_ms = 1000;
99 
100 	LEAVE();
101 	return 0;
102 }
103 
104 
as102_fe_read_status(struct dvb_frontend * fe,fe_status_t * status)105 static int as102_fe_read_status(struct dvb_frontend *fe, fe_status_t *status)
106 {
107 	int ret = 0;
108 	struct as102_dev_t *dev;
109 	struct as10x_tune_status tstate = { 0 };
110 
111 	ENTER();
112 
113 	dev = (struct as102_dev_t *) fe->tuner_priv;
114 	if (dev == NULL)
115 		return -ENODEV;
116 
117 	if (mutex_lock_interruptible(&dev->bus_adap.lock))
118 		return -EBUSY;
119 
120 	/* send abilis command: GET_TUNE_STATUS */
121 	ret = as10x_cmd_get_tune_status(&dev->bus_adap, &tstate);
122 	if (ret < 0) {
123 		dprintk(debug, "as10x_cmd_get_tune_status failed (err = %d)\n",
124 			ret);
125 		goto out;
126 	}
127 
128 	dev->signal_strength  = tstate.signal_strength;
129 	dev->ber  = tstate.BER;
130 
131 	switch (tstate.tune_state) {
132 	case TUNE_STATUS_SIGNAL_DVB_OK:
133 		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
134 		break;
135 	case TUNE_STATUS_STREAM_DETECTED:
136 		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC;
137 		break;
138 	case TUNE_STATUS_STREAM_TUNED:
139 		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC |
140 			FE_HAS_LOCK;
141 		break;
142 	default:
143 		*status = TUNE_STATUS_NOT_TUNED;
144 	}
145 
146 	dprintk(debug, "tuner status: 0x%02x, strength %d, per: %d, ber: %d\n",
147 			tstate.tune_state, tstate.signal_strength,
148 			tstate.PER, tstate.BER);
149 
150 	if (*status & FE_HAS_LOCK) {
151 		if (as10x_cmd_get_demod_stats(&dev->bus_adap,
152 			(struct as10x_demod_stats *) &dev->demod_stats) < 0) {
153 			memset(&dev->demod_stats, 0, sizeof(dev->demod_stats));
154 			dprintk(debug, "as10x_cmd_get_demod_stats failed "
155 				"(probably not tuned)\n");
156 		} else {
157 			dprintk(debug,
158 				"demod status: fc: 0x%08x, bad fc: 0x%08x, "
159 				"bytes corrected: 0x%08x , MER: 0x%04x\n",
160 				dev->demod_stats.frame_count,
161 				dev->demod_stats.bad_frame_count,
162 				dev->demod_stats.bytes_fixed_by_rs,
163 				dev->demod_stats.mer);
164 		}
165 	} else {
166 		memset(&dev->demod_stats, 0, sizeof(dev->demod_stats));
167 	}
168 
169 out:
170 	mutex_unlock(&dev->bus_adap.lock);
171 	LEAVE();
172 	return ret;
173 }
174 
175 /*
176  * Note:
177  * - in AS102 SNR=MER
178  *   - the SNR will be returned in linear terms, i.e. not in dB
179  *   - the accuracy equals ±2dB for a SNR range from 4dB to 30dB
180  *   - the accuracy is >2dB for SNR values outside this range
181  */
as102_fe_read_snr(struct dvb_frontend * fe,u16 * snr)182 static int as102_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
183 {
184 	struct as102_dev_t *dev;
185 
186 	ENTER();
187 
188 	dev = (struct as102_dev_t *) fe->tuner_priv;
189 	if (dev == NULL)
190 		return -ENODEV;
191 
192 	*snr = dev->demod_stats.mer;
193 
194 	LEAVE();
195 	return 0;
196 }
197 
as102_fe_read_ber(struct dvb_frontend * fe,u32 * ber)198 static int as102_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
199 {
200 	struct as102_dev_t *dev;
201 
202 	ENTER();
203 
204 	dev = (struct as102_dev_t *) fe->tuner_priv;
205 	if (dev == NULL)
206 		return -ENODEV;
207 
208 	*ber = dev->ber;
209 
210 	LEAVE();
211 	return 0;
212 }
213 
as102_fe_read_signal_strength(struct dvb_frontend * fe,u16 * strength)214 static int as102_fe_read_signal_strength(struct dvb_frontend *fe,
215 					 u16 *strength)
216 {
217 	struct as102_dev_t *dev;
218 
219 	ENTER();
220 
221 	dev = (struct as102_dev_t *) fe->tuner_priv;
222 	if (dev == NULL)
223 		return -ENODEV;
224 
225 	*strength = (((0xffff * 400) * dev->signal_strength + 41000) * 2);
226 
227 	LEAVE();
228 	return 0;
229 }
230 
as102_fe_read_ucblocks(struct dvb_frontend * fe,u32 * ucblocks)231 static int as102_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
232 {
233 	struct as102_dev_t *dev;
234 
235 	ENTER();
236 
237 	dev = (struct as102_dev_t *) fe->tuner_priv;
238 	if (dev == NULL)
239 		return -ENODEV;
240 
241 	if (dev->demod_stats.has_started)
242 		*ucblocks = dev->demod_stats.bad_frame_count;
243 	else
244 		*ucblocks = 0;
245 
246 	LEAVE();
247 	return 0;
248 }
249 
as102_fe_ts_bus_ctrl(struct dvb_frontend * fe,int acquire)250 static int as102_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)
251 {
252 	struct as102_dev_t *dev;
253 	int ret;
254 
255 	ENTER();
256 
257 	dev = (struct as102_dev_t *) fe->tuner_priv;
258 	if (dev == NULL)
259 		return -ENODEV;
260 
261 	if (mutex_lock_interruptible(&dev->bus_adap.lock))
262 		return -EBUSY;
263 
264 	if (acquire) {
265 		if (elna_enable)
266 			as10x_cmd_set_context(&dev->bus_adap, CONTEXT_LNA, dev->elna_cfg);
267 
268 		ret = as10x_cmd_turn_on(&dev->bus_adap);
269 	} else {
270 		ret = as10x_cmd_turn_off(&dev->bus_adap);
271 	}
272 
273 	mutex_unlock(&dev->bus_adap.lock);
274 
275 	LEAVE();
276 	return ret;
277 }
278 
279 static struct dvb_frontend_ops as102_fe_ops = {
280 	.delsys = { SYS_DVBT },
281 	.info = {
282 		.name			= "Unknown AS102 device",
283 		.frequency_min		= 174000000,
284 		.frequency_max		= 862000000,
285 		.frequency_stepsize	= 166667,
286 		.caps = FE_CAN_INVERSION_AUTO
287 			| FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4
288 			| FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO
289 			| FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QPSK
290 			| FE_CAN_QAM_AUTO
291 			| FE_CAN_TRANSMISSION_MODE_AUTO
292 			| FE_CAN_GUARD_INTERVAL_AUTO
293 			| FE_CAN_HIERARCHY_AUTO
294 			| FE_CAN_RECOVER
295 			| FE_CAN_MUTE_TS
296 	},
297 
298 	.set_frontend		= as102_fe_set_frontend,
299 	.get_frontend		= as102_fe_get_frontend,
300 	.get_tune_settings	= as102_fe_get_tune_settings,
301 
302 	.read_status		= as102_fe_read_status,
303 	.read_snr		= as102_fe_read_snr,
304 	.read_ber		= as102_fe_read_ber,
305 	.read_signal_strength	= as102_fe_read_signal_strength,
306 	.read_ucblocks		= as102_fe_read_ucblocks,
307 	.ts_bus_ctrl		= as102_fe_ts_bus_ctrl,
308 };
309 
as102_dvb_unregister_fe(struct dvb_frontend * fe)310 int as102_dvb_unregister_fe(struct dvb_frontend *fe)
311 {
312 	/* unregister frontend */
313 	dvb_unregister_frontend(fe);
314 
315 	/* detach frontend */
316 	dvb_frontend_detach(fe);
317 
318 	return 0;
319 }
320 
as102_dvb_register_fe(struct as102_dev_t * as102_dev,struct dvb_frontend * dvb_fe)321 int as102_dvb_register_fe(struct as102_dev_t *as102_dev,
322 			  struct dvb_frontend *dvb_fe)
323 {
324 	int errno;
325 	struct dvb_adapter *dvb_adap;
326 
327 	if (as102_dev == NULL)
328 		return -EINVAL;
329 
330 	/* extract dvb_adapter */
331 	dvb_adap = &as102_dev->dvb_adap;
332 
333 	/* init frontend callback ops */
334 	memcpy(&dvb_fe->ops, &as102_fe_ops, sizeof(struct dvb_frontend_ops));
335 	strncpy(dvb_fe->ops.info.name, as102_dev->name,
336 		sizeof(dvb_fe->ops.info.name));
337 
338 	/* register dvb frontend */
339 	errno = dvb_register_frontend(dvb_adap, dvb_fe);
340 	if (errno == 0)
341 		dvb_fe->tuner_priv = as102_dev;
342 
343 	return errno;
344 }
345 
as10x_fe_copy_tps_parameters(struct dtv_frontend_properties * fe_tps,struct as10x_tps * as10x_tps)346 static void as10x_fe_copy_tps_parameters(struct dtv_frontend_properties *fe_tps,
347 					 struct as10x_tps *as10x_tps)
348 {
349 
350 	/* extract constellation */
351 	switch (as10x_tps->modulation) {
352 	case CONST_QPSK:
353 		fe_tps->modulation = QPSK;
354 		break;
355 	case CONST_QAM16:
356 		fe_tps->modulation = QAM_16;
357 		break;
358 	case CONST_QAM64:
359 		fe_tps->modulation = QAM_64;
360 		break;
361 	}
362 
363 	/* extract hierarchy */
364 	switch (as10x_tps->hierarchy) {
365 	case HIER_NONE:
366 		fe_tps->hierarchy = HIERARCHY_NONE;
367 		break;
368 	case HIER_ALPHA_1:
369 		fe_tps->hierarchy = HIERARCHY_1;
370 		break;
371 	case HIER_ALPHA_2:
372 		fe_tps->hierarchy = HIERARCHY_2;
373 		break;
374 	case HIER_ALPHA_4:
375 		fe_tps->hierarchy = HIERARCHY_4;
376 		break;
377 	}
378 
379 	/* extract code rate HP */
380 	switch (as10x_tps->code_rate_HP) {
381 	case CODE_RATE_1_2:
382 		fe_tps->code_rate_HP = FEC_1_2;
383 		break;
384 	case CODE_RATE_2_3:
385 		fe_tps->code_rate_HP = FEC_2_3;
386 		break;
387 	case CODE_RATE_3_4:
388 		fe_tps->code_rate_HP = FEC_3_4;
389 		break;
390 	case CODE_RATE_5_6:
391 		fe_tps->code_rate_HP = FEC_5_6;
392 		break;
393 	case CODE_RATE_7_8:
394 		fe_tps->code_rate_HP = FEC_7_8;
395 		break;
396 	}
397 
398 	/* extract code rate LP */
399 	switch (as10x_tps->code_rate_LP) {
400 	case CODE_RATE_1_2:
401 		fe_tps->code_rate_LP = FEC_1_2;
402 		break;
403 	case CODE_RATE_2_3:
404 		fe_tps->code_rate_LP = FEC_2_3;
405 		break;
406 	case CODE_RATE_3_4:
407 		fe_tps->code_rate_LP = FEC_3_4;
408 		break;
409 	case CODE_RATE_5_6:
410 		fe_tps->code_rate_LP = FEC_5_6;
411 		break;
412 	case CODE_RATE_7_8:
413 		fe_tps->code_rate_LP = FEC_7_8;
414 		break;
415 	}
416 
417 	/* extract guard interval */
418 	switch (as10x_tps->guard_interval) {
419 	case GUARD_INT_1_32:
420 		fe_tps->guard_interval = GUARD_INTERVAL_1_32;
421 		break;
422 	case GUARD_INT_1_16:
423 		fe_tps->guard_interval = GUARD_INTERVAL_1_16;
424 		break;
425 	case GUARD_INT_1_8:
426 		fe_tps->guard_interval = GUARD_INTERVAL_1_8;
427 		break;
428 	case GUARD_INT_1_4:
429 		fe_tps->guard_interval = GUARD_INTERVAL_1_4;
430 		break;
431 	}
432 
433 	/* extract transmission mode */
434 	switch (as10x_tps->transmission_mode) {
435 	case TRANS_MODE_2K:
436 		fe_tps->transmission_mode = TRANSMISSION_MODE_2K;
437 		break;
438 	case TRANS_MODE_8K:
439 		fe_tps->transmission_mode = TRANSMISSION_MODE_8K;
440 		break;
441 	}
442 }
443 
as102_fe_get_code_rate(fe_code_rate_t arg)444 static uint8_t as102_fe_get_code_rate(fe_code_rate_t arg)
445 {
446 	uint8_t c;
447 
448 	switch (arg) {
449 	case FEC_1_2:
450 		c = CODE_RATE_1_2;
451 		break;
452 	case FEC_2_3:
453 		c = CODE_RATE_2_3;
454 		break;
455 	case FEC_3_4:
456 		c = CODE_RATE_3_4;
457 		break;
458 	case FEC_5_6:
459 		c = CODE_RATE_5_6;
460 		break;
461 	case FEC_7_8:
462 		c = CODE_RATE_7_8;
463 		break;
464 	default:
465 		c = CODE_RATE_UNKNOWN;
466 		break;
467 	}
468 
469 	return c;
470 }
471 
as102_fe_copy_tune_parameters(struct as10x_tune_args * tune_args,struct dtv_frontend_properties * params)472 static void as102_fe_copy_tune_parameters(struct as10x_tune_args *tune_args,
473 			  struct dtv_frontend_properties *params)
474 {
475 
476 	/* set frequency */
477 	tune_args->freq = params->frequency / 1000;
478 
479 	/* fix interleaving_mode */
480 	tune_args->interleaving_mode = INTLV_NATIVE;
481 
482 	switch (params->bandwidth_hz) {
483 	case 8000000:
484 		tune_args->bandwidth = BW_8_MHZ;
485 		break;
486 	case 7000000:
487 		tune_args->bandwidth = BW_7_MHZ;
488 		break;
489 	case 6000000:
490 		tune_args->bandwidth = BW_6_MHZ;
491 		break;
492 	default:
493 		tune_args->bandwidth = BW_8_MHZ;
494 	}
495 
496 	switch (params->guard_interval) {
497 	case GUARD_INTERVAL_1_32:
498 		tune_args->guard_interval = GUARD_INT_1_32;
499 		break;
500 	case GUARD_INTERVAL_1_16:
501 		tune_args->guard_interval = GUARD_INT_1_16;
502 		break;
503 	case GUARD_INTERVAL_1_8:
504 		tune_args->guard_interval = GUARD_INT_1_8;
505 		break;
506 	case GUARD_INTERVAL_1_4:
507 		tune_args->guard_interval = GUARD_INT_1_4;
508 		break;
509 	case GUARD_INTERVAL_AUTO:
510 	default:
511 		tune_args->guard_interval = GUARD_UNKNOWN;
512 		break;
513 	}
514 
515 	switch (params->modulation) {
516 	case QPSK:
517 		tune_args->modulation = CONST_QPSK;
518 		break;
519 	case QAM_16:
520 		tune_args->modulation = CONST_QAM16;
521 		break;
522 	case QAM_64:
523 		tune_args->modulation = CONST_QAM64;
524 		break;
525 	default:
526 		tune_args->modulation = CONST_UNKNOWN;
527 		break;
528 	}
529 
530 	switch (params->transmission_mode) {
531 	case TRANSMISSION_MODE_2K:
532 		tune_args->transmission_mode = TRANS_MODE_2K;
533 		break;
534 	case TRANSMISSION_MODE_8K:
535 		tune_args->transmission_mode = TRANS_MODE_8K;
536 		break;
537 	default:
538 		tune_args->transmission_mode = TRANS_MODE_UNKNOWN;
539 	}
540 
541 	switch (params->hierarchy) {
542 	case HIERARCHY_NONE:
543 		tune_args->hierarchy = HIER_NONE;
544 		break;
545 	case HIERARCHY_1:
546 		tune_args->hierarchy = HIER_ALPHA_1;
547 		break;
548 	case HIERARCHY_2:
549 		tune_args->hierarchy = HIER_ALPHA_2;
550 		break;
551 	case HIERARCHY_4:
552 		tune_args->hierarchy = HIER_ALPHA_4;
553 		break;
554 	case HIERARCHY_AUTO:
555 		tune_args->hierarchy = HIER_UNKNOWN;
556 		break;
557 	}
558 
559 	dprintk(debug, "tuner parameters: freq: %d  bw: 0x%02x  gi: 0x%02x\n",
560 			params->frequency,
561 			tune_args->bandwidth,
562 			tune_args->guard_interval);
563 
564 	/*
565 	 * Detect a hierarchy selection
566 	 * if HP/LP are both set to FEC_NONE, HP will be selected.
567 	 */
568 	if ((tune_args->hierarchy != HIER_NONE) &&
569 		       ((params->code_rate_LP == FEC_NONE) ||
570 			(params->code_rate_HP == FEC_NONE))) {
571 
572 		if (params->code_rate_LP == FEC_NONE) {
573 			tune_args->hier_select = HIER_HIGH_PRIORITY;
574 			tune_args->code_rate =
575 			   as102_fe_get_code_rate(params->code_rate_HP);
576 		}
577 
578 		if (params->code_rate_HP == FEC_NONE) {
579 			tune_args->hier_select = HIER_LOW_PRIORITY;
580 			tune_args->code_rate =
581 			   as102_fe_get_code_rate(params->code_rate_LP);
582 		}
583 
584 		dprintk(debug, "\thierarchy: 0x%02x  "
585 				"selected: %s  code_rate_%s: 0x%02x\n",
586 			tune_args->hierarchy,
587 			tune_args->hier_select == HIER_HIGH_PRIORITY ?
588 			"HP" : "LP",
589 			tune_args->hier_select == HIER_HIGH_PRIORITY ?
590 			"HP" : "LP",
591 			tune_args->code_rate);
592 	} else {
593 		tune_args->code_rate =
594 			as102_fe_get_code_rate(params->code_rate_HP);
595 	}
596 }
597