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