cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

radio-si470x-common.c (21827B)


      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 */
    112static unsigned short space = 2;
    113module_param(space, ushort, 0444);
    114MODULE_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) */
    119static unsigned short de = 1;
    120module_param(de, ushort, 0444);
    121MODULE_PARM_DESC(de, "De-emphasis: 0=75us *1=50us*");
    122
    123/* Tune timeout */
    124static unsigned int tune_timeout = 3000;
    125module_param(tune_timeout, uint, 0644);
    126MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*");
    127
    128/* Seek timeout */
    129static unsigned int seek_timeout = 5000;
    130module_param(seek_timeout, uint, 0644);
    131MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
    132
    133static 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 */
    179static 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 */
    193static 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
    232done:
    233	return retval;
    234}
    235
    236/*
    237 * si470x_get_step - get channel spacing
    238 */
    239static 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 */
    259static 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 */
    277int 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}
    288EXPORT_SYMBOL_GPL(si470x_set_freq);
    289
    290
    291/*
    292 * si470x_set_seek - set seek
    293 */
    294static 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 */
    367int 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
    403done:
    404	return retval;
    405}
    406EXPORT_SYMBOL_GPL(si470x_start);
    407
    408
    409/*
    410 * si470x_stop - switch off radio
    411 */
    412int 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
    428done:
    429	return retval;
    430}
    431EXPORT_SYMBOL_GPL(si470x_stop);
    432
    433
    434/*
    435 * si470x_rds_on - switch on rds reception
    436 */
    437static 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 */
    459static 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
    507done:
    508	return retval;
    509}
    510
    511
    512/*
    513 * si470x_fops_poll - poll RDS data
    514 */
    515static __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
    537static 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 */
    548static 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 */
    559static 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
    575static 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 */
    600static 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 */
    660static 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 */
    686static 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 */
    702static 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 */
    725static 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 */
    742static 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
    753const struct v4l2_ctrl_ops si470x_ctrl_ops = {
    754	.s_ctrl = si470x_s_ctrl,
    755};
    756EXPORT_SYMBOL_GPL(si470x_ctrl_ops);
    757
    758static 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 */
    769static 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 */
    785const 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};
    791EXPORT_SYMBOL_GPL(si470x_viddev_template);
    792
    793MODULE_LICENSE("GPL");