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
26static void as10x_fe_copy_tps_parameters(struct dtv_frontend_properties *dst,
27					 struct as10x_tps *src);
28
29static void as102_fe_copy_tune_parameters(struct as10x_tune_args *dst,
30					  struct dtv_frontend_properties *src);
31
32static 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
61static 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
89static 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
107static 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
171out:
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 */
184static 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
200static 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
216static 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
233static 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
252static 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
281static 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
312int 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
323int 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
348static 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
446static 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
474static 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