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