795 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * drivers/media/radio/si470x/radio-si470x-common.c4 *5 * Driver for radios with Silicon Labs Si470x FM Radio Receivers6 *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.016 * - First working version17 * 2008-01-13 Tobias Lorenz <tobias.lorenz@gmx.net>18 * Version 1.0.119 * - Improved error handling, every function now returns errno20 * - Improved multi user access (start/mute/stop)21 * - Channel doesn't get lost anymore after start/mute/stop22 * - RDS support added (polling mode via interrupt EP 1)23 * - marked default module parameters with *value*24 * - switched from bit structs to bit masks25 * - header file cleaned and integrated26 * 2008-01-14 Tobias Lorenz <tobias.lorenz@gmx.net>27 * Version 1.0.228 * - hex values are now lower case29 * - commented USB ID for ADS/Tech moved on todo list30 * - blacklisted si470x in hid-quirks.c31 * - rds buffer handling functions integrated into *_work, *_read32 * - rds_command in si470x_poll exchanged against simple retval33 * - check for firmware version 1534 * - code order and prototypes still remain the same35 * - spacing and bottom of band codes remain the same36 * 2008-01-16 Tobias Lorenz <tobias.lorenz@gmx.net>37 * Version 1.0.338 * - code reordered to avoid function prototypes39 * - switch/case defaults are now more user-friendly40 * - unified comment style41 * - applied all checkpatch.pl v1.12 suggestions42 * except the warning about the too long lines with bit comments43 * - renamed FMRADIO to RADIO to cut line length (checkpatch.pl)44 * 2008-01-22 Tobias Lorenz <tobias.lorenz@gmx.net>45 * Version 1.0.446 * - avoid poss. locking when doing copy_to_user which may sleep47 * - RDS is automatically activated on read now48 * - code cleaned of unnecessary rds_commands49 * - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified50 * (thanks to Guillaume RAMOUSSE)51 * 2008-01-27 Tobias Lorenz <tobias.lorenz@gmx.net>52 * Version 1.0.553 * - number of seek_retries changed to tune_timeout54 * - fixed problem with incomplete tune operations by own buffers55 * - optimization of variables and printf types56 * - improved error logging57 * 2008-01-31 Tobias Lorenz <tobias.lorenz@gmx.net>58 * Oliver Neukum <oliver@neukum.org>59 * Version 1.0.660 * - fixed coverity checker warnings in *_usb_driver_disconnect61 * - probe()/open() race by correct ordering in probe()62 * - DMA coherency rules by separate allocation of all buffers63 * - use of endianness macros64 * - abuse of spinlock, replaced by mutex65 * - racy handling of timer in disconnect,66 * replaced by delayed_work67 * - 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.773 * - usb autosuspend support74 * - unplugging fixed75 * 2008-05-07 Tobias Lorenz <tobias.lorenz@gmx.net>76 * Version 1.0.877 * - hardware frequency seek support78 * - afc indication79 * - more safety checks, let si470x_get_freq return errno80 * - vidioc behavior corrected according to v4l2 spec81 * 2008-10-20 Alexey Klimov <klimov.linux@gmail.com>82 * - add support for KWorld USB FM Radio FM70083 * - blacklisted KWorld radio in hid-core.c and hid-ids.h84 * 2008-12-03 Mark Lord <mlord@pobox.com>85 * - add support for DealExtreme USB Radio86 * 2009-01-31 Bob Ross <pigiron@gmx.com>87 * - correction of stereo detection/setting88 * - correction of signal strength indicator scaling89 * 2009-01-31 Rick Bronson <rick@efn.org>90 * Tobias Lorenz <tobias.lorenz@gmx.net>91 * - add LED status output92 * - get HW/SW version from scratchpad93 * 2009-06-16 Edouard Lafargue <edouard@lafargue.name>94 * Version 1.0.1095 * - add support for interrupt mode for RDS endpoint,96 * instead of polling.97 * Improves RDS reception significantly98 */99 100 101/* kernel includes */102#include "radio-si470x.h"103 104/**************************************************************************105 * Module Parameters106 **************************************************************************/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 Functions174 **************************************************************************/175 176/*177 * si470x_set_band - set the band178 */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 channel192 */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 spacing238 */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 frequency258 */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 frequency276 */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 seek293 */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 } else312 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 else331 radio->registers[POWERCFG] |= POWERCFG_SKMODE;332 if (seek->seek_upward)333 radio->registers[POWERCFG] |= POWERCFG_SEEKUP;334 else335 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 radio366 */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 radio411 */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 reception436 */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 Interface454 **************************************************************************/455 456/*457 * si470x_fops_read - read RDS data458 */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 data514 */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 release547 */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 interface558 */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 Interface572 **************************************************************************/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 else589 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 attributes599 */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 else629 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 this632 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 else639 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 attributes659 */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 frequency685 */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 frequency701 */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 seek724 */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 bands741 */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 operations768 */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 interface784 */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_DESCRIPTION("Core radio driver for Si470x FM Radio Receivers");794MODULE_LICENSE("GPL");795