xref: /openbmc/linux/drivers/media/radio/si470x/radio-si470x-common.c (revision e33bbe69149b802c0c77bfb822685772f85388ca)
1 /*
2  *  drivers/media/radio/si470x/radio-si470x-common.c
3  *
4  *  Driver for radios with Silicon Labs Si470x FM Radio Receivers
5  *
6  *  Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
7  *  Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  */
19 
20 
21 /*
22  * History:
23  * 2008-01-12	Tobias Lorenz <tobias.lorenz@gmx.net>
24  *		Version 1.0.0
25  *		- First working version
26  * 2008-01-13	Tobias Lorenz <tobias.lorenz@gmx.net>
27  *		Version 1.0.1
28  *		- Improved error handling, every function now returns errno
29  *		- Improved multi user access (start/mute/stop)
30  *		- Channel doesn't get lost anymore after start/mute/stop
31  *		- RDS support added (polling mode via interrupt EP 1)
32  *		- marked default module parameters with *value*
33  *		- switched from bit structs to bit masks
34  *		- header file cleaned and integrated
35  * 2008-01-14	Tobias Lorenz <tobias.lorenz@gmx.net>
36  *		Version 1.0.2
37  *		- hex values are now lower case
38  *		- commented USB ID for ADS/Tech moved on todo list
39  *		- blacklisted si470x in hid-quirks.c
40  *		- rds buffer handling functions integrated into *_work, *_read
41  *		- rds_command in si470x_poll exchanged against simple retval
42  *		- check for firmware version 15
43  *		- code order and prototypes still remain the same
44  *		- spacing and bottom of band codes remain the same
45  * 2008-01-16	Tobias Lorenz <tobias.lorenz@gmx.net>
46  *		Version 1.0.3
47  *		- code reordered to avoid function prototypes
48  *		- switch/case defaults are now more user-friendly
49  *		- unified comment style
50  *		- applied all checkpatch.pl v1.12 suggestions
51  *		  except the warning about the too long lines with bit comments
52  *		- renamed FMRADIO to RADIO to cut line length (checkpatch.pl)
53  * 2008-01-22	Tobias Lorenz <tobias.lorenz@gmx.net>
54  *		Version 1.0.4
55  *		- avoid poss. locking when doing copy_to_user which may sleep
56  *		- RDS is automatically activated on read now
57  *		- code cleaned of unnecessary rds_commands
58  *		- USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified
59  *		  (thanks to Guillaume RAMOUSSE)
60  * 2008-01-27	Tobias Lorenz <tobias.lorenz@gmx.net>
61  *		Version 1.0.5
62  *		- number of seek_retries changed to tune_timeout
63  *		- fixed problem with incomplete tune operations by own buffers
64  *		- optimization of variables and printf types
65  *		- improved error logging
66  * 2008-01-31	Tobias Lorenz <tobias.lorenz@gmx.net>
67  *		Oliver Neukum <oliver@neukum.org>
68  *		Version 1.0.6
69  *		- fixed coverity checker warnings in *_usb_driver_disconnect
70  *		- probe()/open() race by correct ordering in probe()
71  *		- DMA coherency rules by separate allocation of all buffers
72  *		- use of endianness macros
73  *		- abuse of spinlock, replaced by mutex
74  *		- racy handling of timer in disconnect,
75  *		  replaced by delayed_work
76  *		- racy interruptible_sleep_on(),
77  *		  replaced with wait_event_interruptible()
78  *		- handle signals in read()
79  * 2008-02-08	Tobias Lorenz <tobias.lorenz@gmx.net>
80  *		Oliver Neukum <oliver@neukum.org>
81  *		Version 1.0.7
82  *		- usb autosuspend support
83  *		- unplugging fixed
84  * 2008-05-07	Tobias Lorenz <tobias.lorenz@gmx.net>
85  *		Version 1.0.8
86  *		- hardware frequency seek support
87  *		- afc indication
88  *		- more safety checks, let si470x_get_freq return errno
89  *		- vidioc behavior corrected according to v4l2 spec
90  * 2008-10-20	Alexey Klimov <klimov.linux@gmail.com>
91  *		- add support for KWorld USB FM Radio FM700
92  *		- blacklisted KWorld radio in hid-core.c and hid-ids.h
93  * 2008-12-03	Mark Lord <mlord@pobox.com>
94  *		- add support for DealExtreme USB Radio
95  * 2009-01-31	Bob Ross <pigiron@gmx.com>
96  *		- correction of stereo detection/setting
97  *		- correction of signal strength indicator scaling
98  * 2009-01-31	Rick Bronson <rick@efn.org>
99  *		Tobias Lorenz <tobias.lorenz@gmx.net>
100  *		- add LED status output
101  *		- get HW/SW version from scratchpad
102  * 2009-06-16   Edouard Lafargue <edouard@lafargue.name>
103  *		Version 1.0.10
104  *		- add support for interrupt mode for RDS endpoint,
105  *                instead of polling.
106  *                Improves RDS reception significantly
107  */
108 
109 
110 /* kernel includes */
111 #include "radio-si470x.h"
112 
113 
114 
115 /**************************************************************************
116  * Module Parameters
117  **************************************************************************/
118 
119 /* Spacing (kHz) */
120 /* 0: 200 kHz (USA, Australia) */
121 /* 1: 100 kHz (Europe, Japan) */
122 /* 2:  50 kHz */
123 static unsigned short space = 2;
124 module_param(space, ushort, 0444);
125 MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
126 
127 /* De-emphasis */
128 /* 0: 75 us (USA) */
129 /* 1: 50 us (Europe, Australia, Japan) */
130 static unsigned short de = 1;
131 module_param(de, ushort, 0444);
132 MODULE_PARM_DESC(de, "De-emphasis: 0=75us *1=50us*");
133 
134 /* Tune timeout */
135 static unsigned int tune_timeout = 3000;
136 module_param(tune_timeout, uint, 0644);
137 MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*");
138 
139 /* Seek timeout */
140 static unsigned int seek_timeout = 5000;
141 module_param(seek_timeout, uint, 0644);
142 MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
143 
144 static const struct v4l2_frequency_band bands[] = {
145 	{
146 		.type = V4L2_TUNER_RADIO,
147 		.index = 0,
148 		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
149 			    V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
150 			    V4L2_TUNER_CAP_FREQ_BANDS |
151 			    V4L2_TUNER_CAP_HWSEEK_BOUNDED |
152 			    V4L2_TUNER_CAP_HWSEEK_WRAP,
153 		.rangelow   =  87500 * 16,
154 		.rangehigh  = 108000 * 16,
155 		.modulation = V4L2_BAND_MODULATION_FM,
156 	},
157 	{
158 		.type = V4L2_TUNER_RADIO,
159 		.index = 1,
160 		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
161 			    V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
162 			    V4L2_TUNER_CAP_FREQ_BANDS |
163 			    V4L2_TUNER_CAP_HWSEEK_BOUNDED |
164 			    V4L2_TUNER_CAP_HWSEEK_WRAP,
165 		.rangelow   =  76000 * 16,
166 		.rangehigh  = 108000 * 16,
167 		.modulation = V4L2_BAND_MODULATION_FM,
168 	},
169 	{
170 		.type = V4L2_TUNER_RADIO,
171 		.index = 2,
172 		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
173 			    V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
174 			    V4L2_TUNER_CAP_FREQ_BANDS |
175 			    V4L2_TUNER_CAP_HWSEEK_BOUNDED |
176 			    V4L2_TUNER_CAP_HWSEEK_WRAP,
177 		.rangelow   =  76000 * 16,
178 		.rangehigh  =  90000 * 16,
179 		.modulation = V4L2_BAND_MODULATION_FM,
180 	},
181 };
182 
183 /**************************************************************************
184  * Generic Functions
185  **************************************************************************/
186 
187 /*
188  * si470x_set_band - set the band
189  */
190 static int si470x_set_band(struct si470x_device *radio, int band)
191 {
192 	if (radio->band == band)
193 		return 0;
194 
195 	radio->band = band;
196 	radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_BAND;
197 	radio->registers[SYSCONFIG2] |= radio->band << 6;
198 	return si470x_set_register(radio, SYSCONFIG2);
199 }
200 
201 /*
202  * si470x_set_chan - set the channel
203  */
204 static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
205 {
206 	int retval;
207 	unsigned long time_left;
208 	bool timed_out = false;
209 
210 	retval = si470x_get_register(radio, POWERCFG);
211 	if (retval)
212 		return retval;
213 
214 	if ((radio->registers[POWERCFG] & (POWERCFG_ENABLE|POWERCFG_DMUTE))
215 		!= (POWERCFG_ENABLE|POWERCFG_DMUTE)) {
216 		return 0;
217 	}
218 
219 	/* start tuning */
220 	radio->registers[CHANNEL] &= ~CHANNEL_CHAN;
221 	radio->registers[CHANNEL] |= CHANNEL_TUNE | chan;
222 	retval = si470x_set_register(radio, CHANNEL);
223 	if (retval < 0)
224 		goto done;
225 
226 	/* wait till tune operation has completed */
227 	reinit_completion(&radio->completion);
228 	time_left = wait_for_completion_timeout(&radio->completion,
229 						msecs_to_jiffies(tune_timeout));
230 	if (time_left == 0)
231 		timed_out = true;
232 
233 	if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
234 		dev_warn(&radio->videodev.dev, "tune does not complete\n");
235 	if (timed_out)
236 		dev_warn(&radio->videodev.dev,
237 			"tune timed out after %u ms\n", tune_timeout);
238 
239 	/* stop tuning */
240 	radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
241 	retval = si470x_set_register(radio, CHANNEL);
242 
243 done:
244 	return retval;
245 }
246 
247 /*
248  * si470x_get_step - get channel spacing
249  */
250 static unsigned int si470x_get_step(struct si470x_device *radio)
251 {
252 	/* Spacing (kHz) */
253 	switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
254 	/* 0: 200 kHz (USA, Australia) */
255 	case 0:
256 		return 200 * 16;
257 	/* 1: 100 kHz (Europe, Japan) */
258 	case 1:
259 		return 100 * 16;
260 	/* 2:  50 kHz */
261 	default:
262 		return 50 * 16;
263 	}
264 }
265 
266 
267 /*
268  * si470x_get_freq - get the frequency
269  */
270 static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
271 {
272 	int chan, retval;
273 
274 	/* read channel */
275 	retval = si470x_get_register(radio, READCHAN);
276 	chan = radio->registers[READCHAN] & READCHAN_READCHAN;
277 
278 	/* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
279 	*freq = chan * si470x_get_step(radio) + bands[radio->band].rangelow;
280 
281 	return retval;
282 }
283 
284 
285 /*
286  * si470x_set_freq - set the frequency
287  */
288 int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
289 {
290 	unsigned short chan;
291 
292 	freq = clamp(freq, bands[radio->band].rangelow,
293 			   bands[radio->band].rangehigh);
294 	/* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
295 	chan = (freq - bands[radio->band].rangelow) / si470x_get_step(radio);
296 
297 	return si470x_set_chan(radio, chan);
298 }
299 
300 
301 /*
302  * si470x_set_seek - set seek
303  */
304 static int si470x_set_seek(struct si470x_device *radio,
305 			   const struct v4l2_hw_freq_seek *seek)
306 {
307 	int band, retval;
308 	unsigned int freq;
309 	bool timed_out = false;
310 	unsigned long time_left;
311 
312 	/* set band */
313 	if (seek->rangelow || seek->rangehigh) {
314 		for (band = 0; band < ARRAY_SIZE(bands); band++) {
315 			if (bands[band].rangelow  == seek->rangelow &&
316 			    bands[band].rangehigh == seek->rangehigh)
317 				break;
318 		}
319 		if (band == ARRAY_SIZE(bands))
320 			return -EINVAL; /* No matching band found */
321 	} else
322 		band = 1; /* If nothing is specified seek 76 - 108 Mhz */
323 
324 	if (radio->band != band) {
325 		retval = si470x_get_freq(radio, &freq);
326 		if (retval)
327 			return retval;
328 		retval = si470x_set_band(radio, band);
329 		if (retval)
330 			return retval;
331 		retval = si470x_set_freq(radio, freq);
332 		if (retval)
333 			return retval;
334 	}
335 
336 	/* start seeking */
337 	radio->registers[POWERCFG] |= POWERCFG_SEEK;
338 	if (seek->wrap_around)
339 		radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;
340 	else
341 		radio->registers[POWERCFG] |= POWERCFG_SKMODE;
342 	if (seek->seek_upward)
343 		radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
344 	else
345 		radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
346 	retval = si470x_set_register(radio, POWERCFG);
347 	if (retval < 0)
348 		return retval;
349 
350 	/* wait till tune operation has completed */
351 	reinit_completion(&radio->completion);
352 	time_left = wait_for_completion_timeout(&radio->completion,
353 						msecs_to_jiffies(seek_timeout));
354 	if (time_left == 0)
355 		timed_out = true;
356 
357 	if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
358 		dev_warn(&radio->videodev.dev, "seek does not complete\n");
359 	if (radio->registers[STATUSRSSI] & STATUSRSSI_SF)
360 		dev_warn(&radio->videodev.dev,
361 			"seek failed / band limit reached\n");
362 
363 	/* stop seeking */
364 	radio->registers[POWERCFG] &= ~POWERCFG_SEEK;
365 	retval = si470x_set_register(radio, POWERCFG);
366 
367 	/* try again, if timed out */
368 	if (retval == 0 && timed_out)
369 		return -ENODATA;
370 	return retval;
371 }
372 
373 
374 /*
375  * si470x_start - switch on radio
376  */
377 int si470x_start(struct si470x_device *radio)
378 {
379 	int retval;
380 
381 	/* powercfg */
382 	radio->registers[POWERCFG] =
383 		POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM;
384 	retval = si470x_set_register(radio, POWERCFG);
385 	if (retval < 0)
386 		goto done;
387 
388 	/* sysconfig 1 */
389 	radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDSIEN | SYSCONFIG1_STCIEN |
390 					SYSCONFIG1_RDS;
391 	radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_GPIO2;
392 	radio->registers[SYSCONFIG1] |= SYSCONFIG1_GPIO2_INT;
393 	if (de)
394 		radio->registers[SYSCONFIG1] |= SYSCONFIG1_DE;
395 	retval = si470x_set_register(radio, SYSCONFIG1);
396 	if (retval < 0)
397 		goto done;
398 
399 	/* sysconfig 2 */
400 	radio->registers[SYSCONFIG2] =
401 		(0x1f  << 8) |				/* SEEKTH */
402 		((radio->band << 6) & SYSCONFIG2_BAND) |/* BAND */
403 		((space << 4) & SYSCONFIG2_SPACE) |	/* SPACE */
404 		15;					/* VOLUME (max) */
405 	retval = si470x_set_register(radio, SYSCONFIG2);
406 	if (retval < 0)
407 		goto done;
408 
409 	/* reset last channel */
410 	retval = si470x_set_chan(radio,
411 		radio->registers[CHANNEL] & CHANNEL_CHAN);
412 
413 done:
414 	return retval;
415 }
416 
417 
418 /*
419  * si470x_stop - switch off radio
420  */
421 int si470x_stop(struct si470x_device *radio)
422 {
423 	int retval;
424 
425 	/* sysconfig 1 */
426 	radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
427 	retval = si470x_set_register(radio, SYSCONFIG1);
428 	if (retval < 0)
429 		goto done;
430 
431 	/* powercfg */
432 	radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
433 	/* POWERCFG_ENABLE has to automatically go low */
434 	radio->registers[POWERCFG] |= POWERCFG_ENABLE |	POWERCFG_DISABLE;
435 	retval = si470x_set_register(radio, POWERCFG);
436 
437 done:
438 	return retval;
439 }
440 
441 
442 /*
443  * si470x_rds_on - switch on rds reception
444  */
445 static int si470x_rds_on(struct si470x_device *radio)
446 {
447 	int retval;
448 
449 	/* sysconfig 1 */
450 	radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS;
451 	retval = si470x_set_register(radio, SYSCONFIG1);
452 	if (retval < 0)
453 		radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
454 
455 	return retval;
456 }
457 
458 
459 
460 /**************************************************************************
461  * File Operations Interface
462  **************************************************************************/
463 
464 /*
465  * si470x_fops_read - read RDS data
466  */
467 static ssize_t si470x_fops_read(struct file *file, char __user *buf,
468 		size_t count, loff_t *ppos)
469 {
470 	struct si470x_device *radio = video_drvdata(file);
471 	int retval = 0;
472 	unsigned int block_count = 0;
473 
474 	/* switch on rds reception */
475 	if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
476 		si470x_rds_on(radio);
477 
478 	/* block if no new data available */
479 	while (radio->wr_index == radio->rd_index) {
480 		if (file->f_flags & O_NONBLOCK) {
481 			retval = -EWOULDBLOCK;
482 			goto done;
483 		}
484 		if (wait_event_interruptible(radio->read_queue,
485 			radio->wr_index != radio->rd_index) < 0) {
486 			retval = -EINTR;
487 			goto done;
488 		}
489 	}
490 
491 	/* calculate block count from byte count */
492 	count /= 3;
493 
494 	/* copy RDS block out of internal buffer and to user buffer */
495 	while (block_count < count) {
496 		if (radio->rd_index == radio->wr_index)
497 			break;
498 
499 		/* always transfer rds complete blocks */
500 		if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3))
501 			/* retval = -EFAULT; */
502 			break;
503 
504 		/* increment and wrap read pointer */
505 		radio->rd_index += 3;
506 		if (radio->rd_index >= radio->buf_size)
507 			radio->rd_index = 0;
508 
509 		/* increment counters */
510 		block_count++;
511 		buf += 3;
512 		retval += 3;
513 	}
514 
515 done:
516 	return retval;
517 }
518 
519 
520 /*
521  * si470x_fops_poll - poll RDS data
522  */
523 static __poll_t si470x_fops_poll(struct file *file,
524 		struct poll_table_struct *pts)
525 {
526 	struct si470x_device *radio = video_drvdata(file);
527 	__poll_t req_events = poll_requested_events(pts);
528 	__poll_t retval = v4l2_ctrl_poll(file, pts);
529 
530 	if (req_events & (EPOLLIN | EPOLLRDNORM)) {
531 		/* switch on rds reception */
532 		if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
533 			si470x_rds_on(radio);
534 
535 		poll_wait(file, &radio->read_queue, pts);
536 
537 		if (radio->rd_index != radio->wr_index)
538 			retval |= EPOLLIN | EPOLLRDNORM;
539 	}
540 
541 	return retval;
542 }
543 
544 
545 /*
546  * si470x_fops - file operations interface
547  */
548 static const struct v4l2_file_operations si470x_fops = {
549 	.owner			= THIS_MODULE,
550 	.read			= si470x_fops_read,
551 	.poll			= si470x_fops_poll,
552 	.unlocked_ioctl		= video_ioctl2,
553 	.open			= si470x_fops_open,
554 	.release		= si470x_fops_release,
555 };
556 
557 
558 
559 /**************************************************************************
560  * Video4Linux Interface
561  **************************************************************************/
562 
563 
564 static int si470x_s_ctrl(struct v4l2_ctrl *ctrl)
565 {
566 	struct si470x_device *radio =
567 		container_of(ctrl->handler, struct si470x_device, hdl);
568 
569 	switch (ctrl->id) {
570 	case V4L2_CID_AUDIO_VOLUME:
571 		radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME;
572 		radio->registers[SYSCONFIG2] |= ctrl->val;
573 		return si470x_set_register(radio, SYSCONFIG2);
574 	case V4L2_CID_AUDIO_MUTE:
575 		if (ctrl->val)
576 			radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
577 		else
578 			radio->registers[POWERCFG] |= POWERCFG_DMUTE;
579 		return si470x_set_register(radio, POWERCFG);
580 	default:
581 		return -EINVAL;
582 	}
583 }
584 
585 
586 /*
587  * si470x_vidioc_g_tuner - get tuner attributes
588  */
589 static int si470x_vidioc_g_tuner(struct file *file, void *priv,
590 		struct v4l2_tuner *tuner)
591 {
592 	struct si470x_device *radio = video_drvdata(file);
593 	int retval = 0;
594 
595 	if (tuner->index != 0)
596 		return -EINVAL;
597 
598 	if (!radio->status_rssi_auto_update) {
599 		retval = si470x_get_register(radio, STATUSRSSI);
600 		if (retval < 0)
601 			return retval;
602 	}
603 
604 	/* driver constants */
605 	strcpy(tuner->name, "FM");
606 	tuner->type = V4L2_TUNER_RADIO;
607 	tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
608 			    V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
609 			    V4L2_TUNER_CAP_HWSEEK_BOUNDED |
610 			    V4L2_TUNER_CAP_HWSEEK_WRAP;
611 	tuner->rangelow  =  76 * FREQ_MUL;
612 	tuner->rangehigh = 108 * FREQ_MUL;
613 
614 	/* stereo indicator == stereo (instead of mono) */
615 	if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
616 		tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
617 	else
618 		tuner->rxsubchans = V4L2_TUNER_SUB_STEREO;
619 	/* If there is a reliable method of detecting an RDS channel,
620 	   then this code should check for that before setting this
621 	   RDS subchannel. */
622 	tuner->rxsubchans |= V4L2_TUNER_SUB_RDS;
623 
624 	/* mono/stereo selector */
625 	if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0)
626 		tuner->audmode = V4L2_TUNER_MODE_STEREO;
627 	else
628 		tuner->audmode = V4L2_TUNER_MODE_MONO;
629 
630 	/* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */
631 	/* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */
632 	tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI);
633 	/* the ideal factor is 0xffff/75 = 873,8 */
634 	tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10);
635 	if (tuner->signal > 0xffff)
636 		tuner->signal = 0xffff;
637 
638 	/* automatic frequency control: -1: freq to low, 1 freq to high */
639 	/* AFCRL does only indicate that freq. differs, not if too low/high */
640 	tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0;
641 
642 	return retval;
643 }
644 
645 
646 /*
647  * si470x_vidioc_s_tuner - set tuner attributes
648  */
649 static int si470x_vidioc_s_tuner(struct file *file, void *priv,
650 		const struct v4l2_tuner *tuner)
651 {
652 	struct si470x_device *radio = video_drvdata(file);
653 
654 	if (tuner->index != 0)
655 		return -EINVAL;
656 
657 	/* mono/stereo selector */
658 	switch (tuner->audmode) {
659 	case V4L2_TUNER_MODE_MONO:
660 		radio->registers[POWERCFG] |= POWERCFG_MONO;  /* force mono */
661 		break;
662 	case V4L2_TUNER_MODE_STEREO:
663 	default:
664 		radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */
665 		break;
666 	}
667 
668 	return si470x_set_register(radio, POWERCFG);
669 }
670 
671 
672 /*
673  * si470x_vidioc_g_frequency - get tuner or modulator radio frequency
674  */
675 static int si470x_vidioc_g_frequency(struct file *file, void *priv,
676 		struct v4l2_frequency *freq)
677 {
678 	struct si470x_device *radio = video_drvdata(file);
679 
680 	if (freq->tuner != 0)
681 		return -EINVAL;
682 
683 	freq->type = V4L2_TUNER_RADIO;
684 	return si470x_get_freq(radio, &freq->frequency);
685 }
686 
687 
688 /*
689  * si470x_vidioc_s_frequency - set tuner or modulator radio frequency
690  */
691 static int si470x_vidioc_s_frequency(struct file *file, void *priv,
692 		const struct v4l2_frequency *freq)
693 {
694 	struct si470x_device *radio = video_drvdata(file);
695 	int retval;
696 
697 	if (freq->tuner != 0)
698 		return -EINVAL;
699 
700 	if (freq->frequency < bands[radio->band].rangelow ||
701 	    freq->frequency > bands[radio->band].rangehigh) {
702 		/* Switch to band 1 which covers everything we support */
703 		retval = si470x_set_band(radio, 1);
704 		if (retval)
705 			return retval;
706 	}
707 	return si470x_set_freq(radio, freq->frequency);
708 }
709 
710 
711 /*
712  * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek
713  */
714 static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
715 		const struct v4l2_hw_freq_seek *seek)
716 {
717 	struct si470x_device *radio = video_drvdata(file);
718 
719 	if (seek->tuner != 0)
720 		return -EINVAL;
721 
722 	if (file->f_flags & O_NONBLOCK)
723 		return -EWOULDBLOCK;
724 
725 	return si470x_set_seek(radio, seek);
726 }
727 
728 /*
729  * si470x_vidioc_enum_freq_bands - enumerate supported bands
730  */
731 static int si470x_vidioc_enum_freq_bands(struct file *file, void *priv,
732 					 struct v4l2_frequency_band *band)
733 {
734 	if (band->tuner != 0)
735 		return -EINVAL;
736 	if (band->index >= ARRAY_SIZE(bands))
737 		return -EINVAL;
738 	*band = bands[band->index];
739 	return 0;
740 }
741 
742 const struct v4l2_ctrl_ops si470x_ctrl_ops = {
743 	.s_ctrl = si470x_s_ctrl,
744 };
745 
746 /*
747  * si470x_ioctl_ops - video device ioctl operations
748  */
749 static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
750 	.vidioc_querycap	= si470x_vidioc_querycap,
751 	.vidioc_g_tuner		= si470x_vidioc_g_tuner,
752 	.vidioc_s_tuner		= si470x_vidioc_s_tuner,
753 	.vidioc_g_frequency	= si470x_vidioc_g_frequency,
754 	.vidioc_s_frequency	= si470x_vidioc_s_frequency,
755 	.vidioc_s_hw_freq_seek	= si470x_vidioc_s_hw_freq_seek,
756 	.vidioc_enum_freq_bands = si470x_vidioc_enum_freq_bands,
757 	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
758 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
759 };
760 
761 
762 /*
763  * si470x_viddev_template - video device interface
764  */
765 const struct video_device si470x_viddev_template = {
766 	.fops			= &si470x_fops,
767 	.name			= DRIVER_NAME,
768 	.release		= video_device_release_empty,
769 	.ioctl_ops		= &si470x_ioctl_ops,
770 };
771