1b09cd163SJoonyoung Shim /* 2b09cd163SJoonyoung Shim * drivers/media/radio/si470x/radio-si470x-common.c 3b09cd163SJoonyoung Shim * 4b09cd163SJoonyoung Shim * Driver for radios with Silicon Labs Si470x FM Radio Receivers 5b09cd163SJoonyoung Shim * 6b09cd163SJoonyoung Shim * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net> 7b09cd163SJoonyoung Shim * 8b09cd163SJoonyoung Shim * This program is free software; you can redistribute it and/or modify 9b09cd163SJoonyoung Shim * it under the terms of the GNU General Public License as published by 10b09cd163SJoonyoung Shim * the Free Software Foundation; either version 2 of the License, or 11b09cd163SJoonyoung Shim * (at your option) any later version. 12b09cd163SJoonyoung Shim * 13b09cd163SJoonyoung Shim * This program is distributed in the hope that it will be useful, 14b09cd163SJoonyoung Shim * but WITHOUT ANY WARRANTY; without even the implied warranty of 15b09cd163SJoonyoung Shim * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16b09cd163SJoonyoung Shim * GNU General Public License for more details. 17b09cd163SJoonyoung Shim * 18b09cd163SJoonyoung Shim * You should have received a copy of the GNU General Public License 19b09cd163SJoonyoung Shim * along with this program; if not, write to the Free Software 20b09cd163SJoonyoung Shim * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21b09cd163SJoonyoung Shim */ 22b09cd163SJoonyoung Shim 23b09cd163SJoonyoung Shim 24b09cd163SJoonyoung Shim /* 25b09cd163SJoonyoung Shim * History: 26b09cd163SJoonyoung Shim * 2008-01-12 Tobias Lorenz <tobias.lorenz@gmx.net> 27b09cd163SJoonyoung Shim * Version 1.0.0 28b09cd163SJoonyoung Shim * - First working version 29b09cd163SJoonyoung Shim * 2008-01-13 Tobias Lorenz <tobias.lorenz@gmx.net> 30b09cd163SJoonyoung Shim * Version 1.0.1 31b09cd163SJoonyoung Shim * - Improved error handling, every function now returns errno 32b09cd163SJoonyoung Shim * - Improved multi user access (start/mute/stop) 33b09cd163SJoonyoung Shim * - Channel doesn't get lost anymore after start/mute/stop 34b09cd163SJoonyoung Shim * - RDS support added (polling mode via interrupt EP 1) 35b09cd163SJoonyoung Shim * - marked default module parameters with *value* 36b09cd163SJoonyoung Shim * - switched from bit structs to bit masks 37b09cd163SJoonyoung Shim * - header file cleaned and integrated 38b09cd163SJoonyoung Shim * 2008-01-14 Tobias Lorenz <tobias.lorenz@gmx.net> 39b09cd163SJoonyoung Shim * Version 1.0.2 40b09cd163SJoonyoung Shim * - hex values are now lower case 41b09cd163SJoonyoung Shim * - commented USB ID for ADS/Tech moved on todo list 42b09cd163SJoonyoung Shim * - blacklisted si470x in hid-quirks.c 43b09cd163SJoonyoung Shim * - rds buffer handling functions integrated into *_work, *_read 44b09cd163SJoonyoung Shim * - rds_command in si470x_poll exchanged against simple retval 45b09cd163SJoonyoung Shim * - check for firmware version 15 46b09cd163SJoonyoung Shim * - code order and prototypes still remain the same 47b09cd163SJoonyoung Shim * - spacing and bottom of band codes remain the same 48b09cd163SJoonyoung Shim * 2008-01-16 Tobias Lorenz <tobias.lorenz@gmx.net> 49b09cd163SJoonyoung Shim * Version 1.0.3 50b09cd163SJoonyoung Shim * - code reordered to avoid function prototypes 51b09cd163SJoonyoung Shim * - switch/case defaults are now more user-friendly 52b09cd163SJoonyoung Shim * - unified comment style 53b09cd163SJoonyoung Shim * - applied all checkpatch.pl v1.12 suggestions 54b09cd163SJoonyoung Shim * except the warning about the too long lines with bit comments 55b09cd163SJoonyoung Shim * - renamed FMRADIO to RADIO to cut line length (checkpatch.pl) 56b09cd163SJoonyoung Shim * 2008-01-22 Tobias Lorenz <tobias.lorenz@gmx.net> 57b09cd163SJoonyoung Shim * Version 1.0.4 58b09cd163SJoonyoung Shim * - avoid poss. locking when doing copy_to_user which may sleep 59b09cd163SJoonyoung Shim * - RDS is automatically activated on read now 60b09cd163SJoonyoung Shim * - code cleaned of unnecessary rds_commands 61b09cd163SJoonyoung Shim * - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified 62b09cd163SJoonyoung Shim * (thanks to Guillaume RAMOUSSE) 63b09cd163SJoonyoung Shim * 2008-01-27 Tobias Lorenz <tobias.lorenz@gmx.net> 64b09cd163SJoonyoung Shim * Version 1.0.5 65b09cd163SJoonyoung Shim * - number of seek_retries changed to tune_timeout 66b09cd163SJoonyoung Shim * - fixed problem with incomplete tune operations by own buffers 67b09cd163SJoonyoung Shim * - optimization of variables and printf types 68b09cd163SJoonyoung Shim * - improved error logging 69b09cd163SJoonyoung Shim * 2008-01-31 Tobias Lorenz <tobias.lorenz@gmx.net> 70b09cd163SJoonyoung Shim * Oliver Neukum <oliver@neukum.org> 71b09cd163SJoonyoung Shim * Version 1.0.6 72b09cd163SJoonyoung Shim * - fixed coverity checker warnings in *_usb_driver_disconnect 73b09cd163SJoonyoung Shim * - probe()/open() race by correct ordering in probe() 74b09cd163SJoonyoung Shim * - DMA coherency rules by separate allocation of all buffers 75b09cd163SJoonyoung Shim * - use of endianness macros 76b09cd163SJoonyoung Shim * - abuse of spinlock, replaced by mutex 77b09cd163SJoonyoung Shim * - racy handling of timer in disconnect, 78b09cd163SJoonyoung Shim * replaced by delayed_work 79b09cd163SJoonyoung Shim * - racy interruptible_sleep_on(), 80b09cd163SJoonyoung Shim * replaced with wait_event_interruptible() 81b09cd163SJoonyoung Shim * - handle signals in read() 82b09cd163SJoonyoung Shim * 2008-02-08 Tobias Lorenz <tobias.lorenz@gmx.net> 83b09cd163SJoonyoung Shim * Oliver Neukum <oliver@neukum.org> 84b09cd163SJoonyoung Shim * Version 1.0.7 85b09cd163SJoonyoung Shim * - usb autosuspend support 86b09cd163SJoonyoung Shim * - unplugging fixed 87b09cd163SJoonyoung Shim * 2008-05-07 Tobias Lorenz <tobias.lorenz@gmx.net> 88b09cd163SJoonyoung Shim * Version 1.0.8 89b09cd163SJoonyoung Shim * - hardware frequency seek support 90b09cd163SJoonyoung Shim * - afc indication 91b09cd163SJoonyoung Shim * - more safety checks, let si470x_get_freq return errno 92b09cd163SJoonyoung Shim * - vidioc behavior corrected according to v4l2 spec 93b09cd163SJoonyoung Shim * 2008-10-20 Alexey Klimov <klimov.linux@gmail.com> 94b09cd163SJoonyoung Shim * - add support for KWorld USB FM Radio FM700 95b09cd163SJoonyoung Shim * - blacklisted KWorld radio in hid-core.c and hid-ids.h 96b09cd163SJoonyoung Shim * 2008-12-03 Mark Lord <mlord@pobox.com> 97b09cd163SJoonyoung Shim * - add support for DealExtreme USB Radio 98b09cd163SJoonyoung Shim * 2009-01-31 Bob Ross <pigiron@gmx.com> 99b09cd163SJoonyoung Shim * - correction of stereo detection/setting 100b09cd163SJoonyoung Shim * - correction of signal strength indicator scaling 101b09cd163SJoonyoung Shim * 2009-01-31 Rick Bronson <rick@efn.org> 102b09cd163SJoonyoung Shim * Tobias Lorenz <tobias.lorenz@gmx.net> 103b09cd163SJoonyoung Shim * - add LED status output 104b09cd163SJoonyoung Shim * - get HW/SW version from scratchpad 105b09cd163SJoonyoung Shim * 2009-06-16 Edouard Lafargue <edouard@lafargue.name> 106b09cd163SJoonyoung Shim * Version 1.0.10 107b09cd163SJoonyoung Shim * - add support for interrupt mode for RDS endpoint, 108b09cd163SJoonyoung Shim * instead of polling. 109b09cd163SJoonyoung Shim * Improves RDS reception significantly 110b09cd163SJoonyoung Shim */ 111b09cd163SJoonyoung Shim 112b09cd163SJoonyoung Shim 113b09cd163SJoonyoung Shim /* kernel includes */ 114b09cd163SJoonyoung Shim #include "radio-si470x.h" 115b09cd163SJoonyoung Shim 116b09cd163SJoonyoung Shim 117b09cd163SJoonyoung Shim 118b09cd163SJoonyoung Shim /************************************************************************** 119b09cd163SJoonyoung Shim * Module Parameters 120b09cd163SJoonyoung Shim **************************************************************************/ 121b09cd163SJoonyoung Shim 122b09cd163SJoonyoung Shim /* Spacing (kHz) */ 123b09cd163SJoonyoung Shim /* 0: 200 kHz (USA, Australia) */ 124b09cd163SJoonyoung Shim /* 1: 100 kHz (Europe, Japan) */ 125b09cd163SJoonyoung Shim /* 2: 50 kHz */ 126b09cd163SJoonyoung Shim static unsigned short space = 2; 127b09cd163SJoonyoung Shim module_param(space, ushort, 0444); 128b09cd163SJoonyoung Shim MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*"); 129b09cd163SJoonyoung Shim 130b09cd163SJoonyoung Shim /* Bottom of Band (MHz) */ 131b09cd163SJoonyoung Shim /* 0: 87.5 - 108 MHz (USA, Europe)*/ 132b09cd163SJoonyoung Shim /* 1: 76 - 108 MHz (Japan wide band) */ 133b09cd163SJoonyoung Shim /* 2: 76 - 90 MHz (Japan) */ 134b09cd163SJoonyoung Shim static unsigned short band = 1; 135b09cd163SJoonyoung Shim module_param(band, ushort, 0444); 136b09cd163SJoonyoung Shim MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz"); 137b09cd163SJoonyoung Shim 138b09cd163SJoonyoung Shim /* De-emphasis */ 139b09cd163SJoonyoung Shim /* 0: 75 us (USA) */ 140b09cd163SJoonyoung Shim /* 1: 50 us (Europe, Australia, Japan) */ 141b09cd163SJoonyoung Shim static unsigned short de = 1; 142b09cd163SJoonyoung Shim module_param(de, ushort, 0444); 143b09cd163SJoonyoung Shim MODULE_PARM_DESC(de, "De-emphasis: 0=75us *1=50us*"); 144b09cd163SJoonyoung Shim 145b09cd163SJoonyoung Shim /* Tune timeout */ 146b09cd163SJoonyoung Shim static unsigned int tune_timeout = 3000; 147b09cd163SJoonyoung Shim module_param(tune_timeout, uint, 0644); 148b09cd163SJoonyoung Shim MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*"); 149b09cd163SJoonyoung Shim 150b09cd163SJoonyoung Shim /* Seek timeout */ 151b09cd163SJoonyoung Shim static unsigned int seek_timeout = 5000; 152b09cd163SJoonyoung Shim module_param(seek_timeout, uint, 0644); 153b09cd163SJoonyoung Shim MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*"); 154b09cd163SJoonyoung Shim 155b09cd163SJoonyoung Shim 156b09cd163SJoonyoung Shim 157b09cd163SJoonyoung Shim /************************************************************************** 158b09cd163SJoonyoung Shim * Generic Functions 159b09cd163SJoonyoung Shim **************************************************************************/ 160b09cd163SJoonyoung Shim 161b09cd163SJoonyoung Shim /* 162b09cd163SJoonyoung Shim * si470x_set_chan - set the channel 163b09cd163SJoonyoung Shim */ 164b09cd163SJoonyoung Shim static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) 165b09cd163SJoonyoung Shim { 166b09cd163SJoonyoung Shim int retval; 167b09cd163SJoonyoung Shim unsigned long timeout; 168b09cd163SJoonyoung Shim bool timed_out = 0; 169b09cd163SJoonyoung Shim 170b09cd163SJoonyoung Shim /* start tuning */ 171b09cd163SJoonyoung Shim radio->registers[CHANNEL] &= ~CHANNEL_CHAN; 172b09cd163SJoonyoung Shim radio->registers[CHANNEL] |= CHANNEL_TUNE | chan; 173b09cd163SJoonyoung Shim retval = si470x_set_register(radio, CHANNEL); 174b09cd163SJoonyoung Shim if (retval < 0) 175b09cd163SJoonyoung Shim goto done; 176b09cd163SJoonyoung Shim 1770830be3fSJoonyoung Shim /* currently I2C driver only uses interrupt way to tune */ 1780830be3fSJoonyoung Shim if (radio->stci_enabled) { 1790830be3fSJoonyoung Shim INIT_COMPLETION(radio->completion); 1800830be3fSJoonyoung Shim 1810830be3fSJoonyoung Shim /* wait till tune operation has completed */ 1820830be3fSJoonyoung Shim retval = wait_for_completion_timeout(&radio->completion, 1830830be3fSJoonyoung Shim msecs_to_jiffies(tune_timeout)); 1840830be3fSJoonyoung Shim if (!retval) 1850830be3fSJoonyoung Shim timed_out = true; 1860830be3fSJoonyoung Shim } else { 187b09cd163SJoonyoung Shim /* wait till tune operation has completed */ 188b09cd163SJoonyoung Shim timeout = jiffies + msecs_to_jiffies(tune_timeout); 189b09cd163SJoonyoung Shim do { 190b09cd163SJoonyoung Shim retval = si470x_get_register(radio, STATUSRSSI); 191b09cd163SJoonyoung Shim if (retval < 0) 192b09cd163SJoonyoung Shim goto stop; 193b09cd163SJoonyoung Shim timed_out = time_after(jiffies, timeout); 1940830be3fSJoonyoung Shim } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) 1950830be3fSJoonyoung Shim && (!timed_out)); 1960830be3fSJoonyoung Shim } 1970830be3fSJoonyoung Shim 198b09cd163SJoonyoung Shim if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) 199*4967d53dSHans Verkuil dev_warn(&radio->videodev.dev, "tune does not complete\n"); 200b09cd163SJoonyoung Shim if (timed_out) 201*4967d53dSHans Verkuil dev_warn(&radio->videodev.dev, 202a9d6fd5eSJoonyoung Shim "tune timed out after %u ms\n", tune_timeout); 203b09cd163SJoonyoung Shim 204b09cd163SJoonyoung Shim stop: 205b09cd163SJoonyoung Shim /* stop tuning */ 206b09cd163SJoonyoung Shim radio->registers[CHANNEL] &= ~CHANNEL_TUNE; 207b09cd163SJoonyoung Shim retval = si470x_set_register(radio, CHANNEL); 208b09cd163SJoonyoung Shim 209b09cd163SJoonyoung Shim done: 210b09cd163SJoonyoung Shim return retval; 211b09cd163SJoonyoung Shim } 212b09cd163SJoonyoung Shim 213b09cd163SJoonyoung Shim 214b09cd163SJoonyoung Shim /* 215b09cd163SJoonyoung Shim * si470x_get_freq - get the frequency 216b09cd163SJoonyoung Shim */ 217b09cd163SJoonyoung Shim static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq) 218b09cd163SJoonyoung Shim { 219b09cd163SJoonyoung Shim unsigned int spacing, band_bottom; 220b09cd163SJoonyoung Shim unsigned short chan; 221b09cd163SJoonyoung Shim int retval; 222b09cd163SJoonyoung Shim 223b09cd163SJoonyoung Shim /* Spacing (kHz) */ 224b09cd163SJoonyoung Shim switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) { 225b09cd163SJoonyoung Shim /* 0: 200 kHz (USA, Australia) */ 226b09cd163SJoonyoung Shim case 0: 227b09cd163SJoonyoung Shim spacing = 0.200 * FREQ_MUL; break; 228b09cd163SJoonyoung Shim /* 1: 100 kHz (Europe, Japan) */ 229b09cd163SJoonyoung Shim case 1: 230b09cd163SJoonyoung Shim spacing = 0.100 * FREQ_MUL; break; 231b09cd163SJoonyoung Shim /* 2: 50 kHz */ 232b09cd163SJoonyoung Shim default: 233b09cd163SJoonyoung Shim spacing = 0.050 * FREQ_MUL; break; 234b09cd163SJoonyoung Shim }; 235b09cd163SJoonyoung Shim 236b09cd163SJoonyoung Shim /* Bottom of Band (MHz) */ 237b09cd163SJoonyoung Shim switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { 238b09cd163SJoonyoung Shim /* 0: 87.5 - 108 MHz (USA, Europe) */ 239b09cd163SJoonyoung Shim case 0: 240b09cd163SJoonyoung Shim band_bottom = 87.5 * FREQ_MUL; break; 241b09cd163SJoonyoung Shim /* 1: 76 - 108 MHz (Japan wide band) */ 242b09cd163SJoonyoung Shim default: 243b09cd163SJoonyoung Shim band_bottom = 76 * FREQ_MUL; break; 244b09cd163SJoonyoung Shim /* 2: 76 - 90 MHz (Japan) */ 245b09cd163SJoonyoung Shim case 2: 246b09cd163SJoonyoung Shim band_bottom = 76 * FREQ_MUL; break; 247b09cd163SJoonyoung Shim }; 248b09cd163SJoonyoung Shim 249b09cd163SJoonyoung Shim /* read channel */ 250b09cd163SJoonyoung Shim retval = si470x_get_register(radio, READCHAN); 251b09cd163SJoonyoung Shim chan = radio->registers[READCHAN] & READCHAN_READCHAN; 252b09cd163SJoonyoung Shim 253b09cd163SJoonyoung Shim /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */ 254b09cd163SJoonyoung Shim *freq = chan * spacing + band_bottom; 255b09cd163SJoonyoung Shim 256b09cd163SJoonyoung Shim return retval; 257b09cd163SJoonyoung Shim } 258b09cd163SJoonyoung Shim 259b09cd163SJoonyoung Shim 260b09cd163SJoonyoung Shim /* 261b09cd163SJoonyoung Shim * si470x_set_freq - set the frequency 262b09cd163SJoonyoung Shim */ 263b09cd163SJoonyoung Shim int si470x_set_freq(struct si470x_device *radio, unsigned int freq) 264b09cd163SJoonyoung Shim { 265b09cd163SJoonyoung Shim unsigned int spacing, band_bottom; 266b09cd163SJoonyoung Shim unsigned short chan; 267b09cd163SJoonyoung Shim 268b09cd163SJoonyoung Shim /* Spacing (kHz) */ 269b09cd163SJoonyoung Shim switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) { 270b09cd163SJoonyoung Shim /* 0: 200 kHz (USA, Australia) */ 271b09cd163SJoonyoung Shim case 0: 272b09cd163SJoonyoung Shim spacing = 0.200 * FREQ_MUL; break; 273b09cd163SJoonyoung Shim /* 1: 100 kHz (Europe, Japan) */ 274b09cd163SJoonyoung Shim case 1: 275b09cd163SJoonyoung Shim spacing = 0.100 * FREQ_MUL; break; 276b09cd163SJoonyoung Shim /* 2: 50 kHz */ 277b09cd163SJoonyoung Shim default: 278b09cd163SJoonyoung Shim spacing = 0.050 * FREQ_MUL; break; 279b09cd163SJoonyoung Shim }; 280b09cd163SJoonyoung Shim 281b09cd163SJoonyoung Shim /* Bottom of Band (MHz) */ 282b09cd163SJoonyoung Shim switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { 283b09cd163SJoonyoung Shim /* 0: 87.5 - 108 MHz (USA, Europe) */ 284b09cd163SJoonyoung Shim case 0: 285b09cd163SJoonyoung Shim band_bottom = 87.5 * FREQ_MUL; break; 286b09cd163SJoonyoung Shim /* 1: 76 - 108 MHz (Japan wide band) */ 287b09cd163SJoonyoung Shim default: 288b09cd163SJoonyoung Shim band_bottom = 76 * FREQ_MUL; break; 289b09cd163SJoonyoung Shim /* 2: 76 - 90 MHz (Japan) */ 290b09cd163SJoonyoung Shim case 2: 291b09cd163SJoonyoung Shim band_bottom = 76 * FREQ_MUL; break; 292b09cd163SJoonyoung Shim }; 293b09cd163SJoonyoung Shim 294b09cd163SJoonyoung Shim /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */ 295b09cd163SJoonyoung Shim chan = (freq - band_bottom) / spacing; 296b09cd163SJoonyoung Shim 297b09cd163SJoonyoung Shim return si470x_set_chan(radio, chan); 298b09cd163SJoonyoung Shim } 299b09cd163SJoonyoung Shim 300b09cd163SJoonyoung Shim 301b09cd163SJoonyoung Shim /* 302b09cd163SJoonyoung Shim * si470x_set_seek - set seek 303b09cd163SJoonyoung Shim */ 304b09cd163SJoonyoung Shim static int si470x_set_seek(struct si470x_device *radio, 305b09cd163SJoonyoung Shim unsigned int wrap_around, unsigned int seek_upward) 306b09cd163SJoonyoung Shim { 307b09cd163SJoonyoung Shim int retval = 0; 308b09cd163SJoonyoung Shim unsigned long timeout; 309b09cd163SJoonyoung Shim bool timed_out = 0; 310b09cd163SJoonyoung Shim 311b09cd163SJoonyoung Shim /* start seeking */ 312b09cd163SJoonyoung Shim radio->registers[POWERCFG] |= POWERCFG_SEEK; 313b09cd163SJoonyoung Shim if (wrap_around == 1) 314b09cd163SJoonyoung Shim radio->registers[POWERCFG] &= ~POWERCFG_SKMODE; 315b09cd163SJoonyoung Shim else 316b09cd163SJoonyoung Shim radio->registers[POWERCFG] |= POWERCFG_SKMODE; 317b09cd163SJoonyoung Shim if (seek_upward == 1) 318b09cd163SJoonyoung Shim radio->registers[POWERCFG] |= POWERCFG_SEEKUP; 319b09cd163SJoonyoung Shim else 320b09cd163SJoonyoung Shim radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP; 321b09cd163SJoonyoung Shim retval = si470x_set_register(radio, POWERCFG); 322b09cd163SJoonyoung Shim if (retval < 0) 323b09cd163SJoonyoung Shim goto done; 324b09cd163SJoonyoung Shim 3250830be3fSJoonyoung Shim /* currently I2C driver only uses interrupt way to seek */ 3260830be3fSJoonyoung Shim if (radio->stci_enabled) { 3270830be3fSJoonyoung Shim INIT_COMPLETION(radio->completion); 3280830be3fSJoonyoung Shim 3290830be3fSJoonyoung Shim /* wait till seek operation has completed */ 3300830be3fSJoonyoung Shim retval = wait_for_completion_timeout(&radio->completion, 3310830be3fSJoonyoung Shim msecs_to_jiffies(seek_timeout)); 3320830be3fSJoonyoung Shim if (!retval) 3330830be3fSJoonyoung Shim timed_out = true; 3340830be3fSJoonyoung Shim } else { 335b09cd163SJoonyoung Shim /* wait till seek operation has completed */ 336b09cd163SJoonyoung Shim timeout = jiffies + msecs_to_jiffies(seek_timeout); 337b09cd163SJoonyoung Shim do { 338b09cd163SJoonyoung Shim retval = si470x_get_register(radio, STATUSRSSI); 339b09cd163SJoonyoung Shim if (retval < 0) 340b09cd163SJoonyoung Shim goto stop; 341b09cd163SJoonyoung Shim timed_out = time_after(jiffies, timeout); 3420830be3fSJoonyoung Shim } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) 3430830be3fSJoonyoung Shim && (!timed_out)); 3440830be3fSJoonyoung Shim } 3450830be3fSJoonyoung Shim 346b09cd163SJoonyoung Shim if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) 347*4967d53dSHans Verkuil dev_warn(&radio->videodev.dev, "seek does not complete\n"); 348b09cd163SJoonyoung Shim if (radio->registers[STATUSRSSI] & STATUSRSSI_SF) 349*4967d53dSHans Verkuil dev_warn(&radio->videodev.dev, 350a9d6fd5eSJoonyoung Shim "seek failed / band limit reached\n"); 351b09cd163SJoonyoung Shim if (timed_out) 352*4967d53dSHans Verkuil dev_warn(&radio->videodev.dev, 353a9d6fd5eSJoonyoung Shim "seek timed out after %u ms\n", seek_timeout); 354b09cd163SJoonyoung Shim 355b09cd163SJoonyoung Shim stop: 356b09cd163SJoonyoung Shim /* stop seeking */ 357b09cd163SJoonyoung Shim radio->registers[POWERCFG] &= ~POWERCFG_SEEK; 358b09cd163SJoonyoung Shim retval = si470x_set_register(radio, POWERCFG); 359b09cd163SJoonyoung Shim 360b09cd163SJoonyoung Shim done: 361b09cd163SJoonyoung Shim /* try again, if timed out */ 362b09cd163SJoonyoung Shim if ((retval == 0) && timed_out) 363b09cd163SJoonyoung Shim retval = -EAGAIN; 364b09cd163SJoonyoung Shim 365b09cd163SJoonyoung Shim return retval; 366b09cd163SJoonyoung Shim } 367b09cd163SJoonyoung Shim 368b09cd163SJoonyoung Shim 369b09cd163SJoonyoung Shim /* 370b09cd163SJoonyoung Shim * si470x_start - switch on radio 371b09cd163SJoonyoung Shim */ 372b09cd163SJoonyoung Shim int si470x_start(struct si470x_device *radio) 373b09cd163SJoonyoung Shim { 374b09cd163SJoonyoung Shim int retval; 375b09cd163SJoonyoung Shim 376b09cd163SJoonyoung Shim /* powercfg */ 377b09cd163SJoonyoung Shim radio->registers[POWERCFG] = 378b09cd163SJoonyoung Shim POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM; 379b09cd163SJoonyoung Shim retval = si470x_set_register(radio, POWERCFG); 380b09cd163SJoonyoung Shim if (retval < 0) 381b09cd163SJoonyoung Shim goto done; 382b09cd163SJoonyoung Shim 383b09cd163SJoonyoung Shim /* sysconfig 1 */ 384131ddd1aSTobias Lorenz radio->registers[SYSCONFIG1] = 385131ddd1aSTobias Lorenz (de << 11) & SYSCONFIG1_DE; /* DE*/ 386b09cd163SJoonyoung Shim retval = si470x_set_register(radio, SYSCONFIG1); 387b09cd163SJoonyoung Shim if (retval < 0) 388b09cd163SJoonyoung Shim goto done; 389b09cd163SJoonyoung Shim 390b09cd163SJoonyoung Shim /* sysconfig 2 */ 391b09cd163SJoonyoung Shim radio->registers[SYSCONFIG2] = 392b09cd163SJoonyoung Shim (0x3f << 8) | /* SEEKTH */ 393b09cd163SJoonyoung Shim ((band << 6) & SYSCONFIG2_BAND) | /* BAND */ 394b09cd163SJoonyoung Shim ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */ 395b09cd163SJoonyoung Shim 15; /* VOLUME (max) */ 396b09cd163SJoonyoung Shim retval = si470x_set_register(radio, SYSCONFIG2); 397b09cd163SJoonyoung Shim if (retval < 0) 398b09cd163SJoonyoung Shim goto done; 399b09cd163SJoonyoung Shim 400b09cd163SJoonyoung Shim /* reset last channel */ 401b09cd163SJoonyoung Shim retval = si470x_set_chan(radio, 402b09cd163SJoonyoung Shim radio->registers[CHANNEL] & CHANNEL_CHAN); 403b09cd163SJoonyoung Shim 404b09cd163SJoonyoung Shim done: 405b09cd163SJoonyoung Shim return retval; 406b09cd163SJoonyoung Shim } 407b09cd163SJoonyoung Shim 408b09cd163SJoonyoung Shim 409b09cd163SJoonyoung Shim /* 410b09cd163SJoonyoung Shim * si470x_stop - switch off radio 411b09cd163SJoonyoung Shim */ 412b09cd163SJoonyoung Shim int si470x_stop(struct si470x_device *radio) 413b09cd163SJoonyoung Shim { 414b09cd163SJoonyoung Shim int retval; 415b09cd163SJoonyoung Shim 416b09cd163SJoonyoung Shim /* sysconfig 1 */ 417b09cd163SJoonyoung Shim radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS; 418b09cd163SJoonyoung Shim retval = si470x_set_register(radio, SYSCONFIG1); 419b09cd163SJoonyoung Shim if (retval < 0) 420b09cd163SJoonyoung Shim goto done; 421b09cd163SJoonyoung Shim 422b09cd163SJoonyoung Shim /* powercfg */ 423b09cd163SJoonyoung Shim radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; 424b09cd163SJoonyoung Shim /* POWERCFG_ENABLE has to automatically go low */ 425b09cd163SJoonyoung Shim radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE; 426b09cd163SJoonyoung Shim retval = si470x_set_register(radio, POWERCFG); 427b09cd163SJoonyoung Shim 428b09cd163SJoonyoung Shim done: 429b09cd163SJoonyoung Shim return retval; 430b09cd163SJoonyoung Shim } 431b09cd163SJoonyoung Shim 432b09cd163SJoonyoung Shim 433b09cd163SJoonyoung Shim /* 434b09cd163SJoonyoung Shim * si470x_rds_on - switch on rds reception 435b09cd163SJoonyoung Shim */ 436f2f8e850SMauro Carvalho Chehab static int si470x_rds_on(struct si470x_device *radio) 437b09cd163SJoonyoung Shim { 438b09cd163SJoonyoung Shim int retval; 439b09cd163SJoonyoung Shim 440b09cd163SJoonyoung Shim /* sysconfig 1 */ 441b09cd163SJoonyoung Shim radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS; 442b09cd163SJoonyoung Shim retval = si470x_set_register(radio, SYSCONFIG1); 443b09cd163SJoonyoung Shim if (retval < 0) 444b09cd163SJoonyoung Shim radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS; 445b09cd163SJoonyoung Shim 446b09cd163SJoonyoung Shim return retval; 447b09cd163SJoonyoung Shim } 448b09cd163SJoonyoung Shim 449b09cd163SJoonyoung Shim 450b09cd163SJoonyoung Shim 451b09cd163SJoonyoung Shim /************************************************************************** 4521aa925c9SJoonyoung Shim * File Operations Interface 4531aa925c9SJoonyoung Shim **************************************************************************/ 4541aa925c9SJoonyoung Shim 4551aa925c9SJoonyoung Shim /* 4561aa925c9SJoonyoung Shim * si470x_fops_read - read RDS data 4571aa925c9SJoonyoung Shim */ 4581aa925c9SJoonyoung Shim static ssize_t si470x_fops_read(struct file *file, char __user *buf, 4591aa925c9SJoonyoung Shim size_t count, loff_t *ppos) 4601aa925c9SJoonyoung Shim { 4611aa925c9SJoonyoung Shim struct si470x_device *radio = video_drvdata(file); 4621aa925c9SJoonyoung Shim int retval = 0; 4631aa925c9SJoonyoung Shim unsigned int block_count = 0; 4641aa925c9SJoonyoung Shim 4651aa925c9SJoonyoung Shim /* switch on rds reception */ 4661aa925c9SJoonyoung Shim if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) 4671aa925c9SJoonyoung Shim si470x_rds_on(radio); 4681aa925c9SJoonyoung Shim 4691aa925c9SJoonyoung Shim /* block if no new data available */ 4701aa925c9SJoonyoung Shim while (radio->wr_index == radio->rd_index) { 4711aa925c9SJoonyoung Shim if (file->f_flags & O_NONBLOCK) { 4721aa925c9SJoonyoung Shim retval = -EWOULDBLOCK; 4731aa925c9SJoonyoung Shim goto done; 4741aa925c9SJoonyoung Shim } 4751aa925c9SJoonyoung Shim if (wait_event_interruptible(radio->read_queue, 4761aa925c9SJoonyoung Shim radio->wr_index != radio->rd_index) < 0) { 4771aa925c9SJoonyoung Shim retval = -EINTR; 4781aa925c9SJoonyoung Shim goto done; 4791aa925c9SJoonyoung Shim } 4801aa925c9SJoonyoung Shim } 4811aa925c9SJoonyoung Shim 4821aa925c9SJoonyoung Shim /* calculate block count from byte count */ 4831aa925c9SJoonyoung Shim count /= 3; 4841aa925c9SJoonyoung Shim 4851aa925c9SJoonyoung Shim /* copy RDS block out of internal buffer and to user buffer */ 4861aa925c9SJoonyoung Shim while (block_count < count) { 4871aa925c9SJoonyoung Shim if (radio->rd_index == radio->wr_index) 4881aa925c9SJoonyoung Shim break; 4891aa925c9SJoonyoung Shim 4901aa925c9SJoonyoung Shim /* always transfer rds complete blocks */ 4911aa925c9SJoonyoung Shim if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3)) 4921aa925c9SJoonyoung Shim /* retval = -EFAULT; */ 4931aa925c9SJoonyoung Shim break; 4941aa925c9SJoonyoung Shim 4951aa925c9SJoonyoung Shim /* increment and wrap read pointer */ 4961aa925c9SJoonyoung Shim radio->rd_index += 3; 4971aa925c9SJoonyoung Shim if (radio->rd_index >= radio->buf_size) 4981aa925c9SJoonyoung Shim radio->rd_index = 0; 4991aa925c9SJoonyoung Shim 5001aa925c9SJoonyoung Shim /* increment counters */ 5011aa925c9SJoonyoung Shim block_count++; 5021aa925c9SJoonyoung Shim buf += 3; 5031aa925c9SJoonyoung Shim retval += 3; 5041aa925c9SJoonyoung Shim } 5051aa925c9SJoonyoung Shim 5061aa925c9SJoonyoung Shim done: 5071aa925c9SJoonyoung Shim return retval; 5081aa925c9SJoonyoung Shim } 5091aa925c9SJoonyoung Shim 5101aa925c9SJoonyoung Shim 5111aa925c9SJoonyoung Shim /* 5121aa925c9SJoonyoung Shim * si470x_fops_poll - poll RDS data 5131aa925c9SJoonyoung Shim */ 5141aa925c9SJoonyoung Shim static unsigned int si470x_fops_poll(struct file *file, 5151aa925c9SJoonyoung Shim struct poll_table_struct *pts) 5161aa925c9SJoonyoung Shim { 5171aa925c9SJoonyoung Shim struct si470x_device *radio = video_drvdata(file); 5181aa925c9SJoonyoung Shim int retval = 0; 5191aa925c9SJoonyoung Shim 5201aa925c9SJoonyoung Shim /* switch on rds reception */ 521f2f8e850SMauro Carvalho Chehab 5221aa925c9SJoonyoung Shim if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) 5231aa925c9SJoonyoung Shim si470x_rds_on(radio); 5241aa925c9SJoonyoung Shim 5251aa925c9SJoonyoung Shim poll_wait(file, &radio->read_queue, pts); 5261aa925c9SJoonyoung Shim 5271aa925c9SJoonyoung Shim if (radio->rd_index != radio->wr_index) 5281aa925c9SJoonyoung Shim retval = POLLIN | POLLRDNORM; 5291aa925c9SJoonyoung Shim 5301aa925c9SJoonyoung Shim return retval; 5311aa925c9SJoonyoung Shim } 5321aa925c9SJoonyoung Shim 5331aa925c9SJoonyoung Shim 5341aa925c9SJoonyoung Shim /* 5351aa925c9SJoonyoung Shim * si470x_fops - file operations interface 5361aa925c9SJoonyoung Shim */ 5371aa925c9SJoonyoung Shim static const struct v4l2_file_operations si470x_fops = { 5381aa925c9SJoonyoung Shim .owner = THIS_MODULE, 5391aa925c9SJoonyoung Shim .read = si470x_fops_read, 5401aa925c9SJoonyoung Shim .poll = si470x_fops_poll, 541f2f8e850SMauro Carvalho Chehab .unlocked_ioctl = video_ioctl2, 5421aa925c9SJoonyoung Shim .open = si470x_fops_open, 5431aa925c9SJoonyoung Shim .release = si470x_fops_release, 5441aa925c9SJoonyoung Shim }; 5451aa925c9SJoonyoung Shim 5461aa925c9SJoonyoung Shim 5471aa925c9SJoonyoung Shim 5481aa925c9SJoonyoung Shim /************************************************************************** 549b09cd163SJoonyoung Shim * Video4Linux Interface 550b09cd163SJoonyoung Shim **************************************************************************/ 551b09cd163SJoonyoung Shim 552*4967d53dSHans Verkuil 553*4967d53dSHans Verkuil static int si470x_s_ctrl(struct v4l2_ctrl *ctrl) 554b09cd163SJoonyoung Shim { 555*4967d53dSHans Verkuil struct si470x_device *radio = 556*4967d53dSHans Verkuil container_of(ctrl->handler, struct si470x_device, hdl); 557b09cd163SJoonyoung Shim 558b09cd163SJoonyoung Shim switch (ctrl->id) { 559b09cd163SJoonyoung Shim case V4L2_CID_AUDIO_VOLUME: 560b09cd163SJoonyoung Shim radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME; 561*4967d53dSHans Verkuil radio->registers[SYSCONFIG2] |= ctrl->val; 562*4967d53dSHans Verkuil return si470x_set_register(radio, SYSCONFIG2); 563b09cd163SJoonyoung Shim case V4L2_CID_AUDIO_MUTE: 564*4967d53dSHans Verkuil if (ctrl->val) 565b09cd163SJoonyoung Shim radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; 566b09cd163SJoonyoung Shim else 567b09cd163SJoonyoung Shim radio->registers[POWERCFG] |= POWERCFG_DMUTE; 568*4967d53dSHans Verkuil return si470x_set_register(radio, POWERCFG); 569b09cd163SJoonyoung Shim break; 570b09cd163SJoonyoung Shim default: 571*4967d53dSHans Verkuil return -EINVAL; 572b09cd163SJoonyoung Shim } 573b09cd163SJoonyoung Shim } 574b09cd163SJoonyoung Shim 575b09cd163SJoonyoung Shim 576b09cd163SJoonyoung Shim /* 577b09cd163SJoonyoung Shim * si470x_vidioc_g_tuner - get tuner attributes 578b09cd163SJoonyoung Shim */ 579b09cd163SJoonyoung Shim static int si470x_vidioc_g_tuner(struct file *file, void *priv, 580b09cd163SJoonyoung Shim struct v4l2_tuner *tuner) 581b09cd163SJoonyoung Shim { 582b09cd163SJoonyoung Shim struct si470x_device *radio = video_drvdata(file); 583b09cd163SJoonyoung Shim int retval = 0; 584b09cd163SJoonyoung Shim 585b09cd163SJoonyoung Shim if (tuner->index != 0) { 586b09cd163SJoonyoung Shim retval = -EINVAL; 587b09cd163SJoonyoung Shim goto done; 588b09cd163SJoonyoung Shim } 589b09cd163SJoonyoung Shim 590b09cd163SJoonyoung Shim retval = si470x_get_register(radio, STATUSRSSI); 591b09cd163SJoonyoung Shim if (retval < 0) 592b09cd163SJoonyoung Shim goto done; 593b09cd163SJoonyoung Shim 594b09cd163SJoonyoung Shim /* driver constants */ 595b09cd163SJoonyoung Shim strcpy(tuner->name, "FM"); 596b09cd163SJoonyoung Shim tuner->type = V4L2_TUNER_RADIO; 597b09cd163SJoonyoung Shim tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | 598cb0ed222SMatti Aaltonen V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO; 599b09cd163SJoonyoung Shim 600b09cd163SJoonyoung Shim /* range limits */ 601b09cd163SJoonyoung Shim switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { 602b09cd163SJoonyoung Shim /* 0: 87.5 - 108 MHz (USA, Europe, default) */ 603b09cd163SJoonyoung Shim default: 604b09cd163SJoonyoung Shim tuner->rangelow = 87.5 * FREQ_MUL; 605b09cd163SJoonyoung Shim tuner->rangehigh = 108 * FREQ_MUL; 606b09cd163SJoonyoung Shim break; 607b09cd163SJoonyoung Shim /* 1: 76 - 108 MHz (Japan wide band) */ 608b09cd163SJoonyoung Shim case 1: 609b09cd163SJoonyoung Shim tuner->rangelow = 76 * FREQ_MUL; 610b09cd163SJoonyoung Shim tuner->rangehigh = 108 * FREQ_MUL; 611b09cd163SJoonyoung Shim break; 612b09cd163SJoonyoung Shim /* 2: 76 - 90 MHz (Japan) */ 613b09cd163SJoonyoung Shim case 2: 614b09cd163SJoonyoung Shim tuner->rangelow = 76 * FREQ_MUL; 615b09cd163SJoonyoung Shim tuner->rangehigh = 90 * FREQ_MUL; 616b09cd163SJoonyoung Shim break; 617b09cd163SJoonyoung Shim }; 618b09cd163SJoonyoung Shim 619b09cd163SJoonyoung Shim /* stereo indicator == stereo (instead of mono) */ 620b09cd163SJoonyoung Shim if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0) 621b09cd163SJoonyoung Shim tuner->rxsubchans = V4L2_TUNER_SUB_MONO; 622b09cd163SJoonyoung Shim else 623*4967d53dSHans Verkuil tuner->rxsubchans = V4L2_TUNER_SUB_STEREO; 624b09cd163SJoonyoung Shim /* If there is a reliable method of detecting an RDS channel, 625b09cd163SJoonyoung Shim then this code should check for that before setting this 626b09cd163SJoonyoung Shim RDS subchannel. */ 627b09cd163SJoonyoung Shim tuner->rxsubchans |= V4L2_TUNER_SUB_RDS; 628b09cd163SJoonyoung Shim 629b09cd163SJoonyoung Shim /* mono/stereo selector */ 630b09cd163SJoonyoung Shim if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0) 631b09cd163SJoonyoung Shim tuner->audmode = V4L2_TUNER_MODE_STEREO; 632b09cd163SJoonyoung Shim else 633b09cd163SJoonyoung Shim tuner->audmode = V4L2_TUNER_MODE_MONO; 634b09cd163SJoonyoung Shim 635b09cd163SJoonyoung Shim /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */ 636144dcdceSTobias Lorenz /* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */ 637b09cd163SJoonyoung Shim tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI); 638b09cd163SJoonyoung Shim /* the ideal factor is 0xffff/75 = 873,8 */ 639b09cd163SJoonyoung Shim tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10); 640b09cd163SJoonyoung Shim 641b09cd163SJoonyoung Shim /* automatic frequency control: -1: freq to low, 1 freq to high */ 642b09cd163SJoonyoung Shim /* AFCRL does only indicate that freq. differs, not if too low/high */ 643b09cd163SJoonyoung Shim tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0; 644b09cd163SJoonyoung Shim 645b09cd163SJoonyoung Shim done: 646b09cd163SJoonyoung Shim if (retval < 0) 647*4967d53dSHans Verkuil dev_warn(&radio->videodev.dev, 648a9d6fd5eSJoonyoung Shim "get tuner failed with %d\n", retval); 649b09cd163SJoonyoung Shim return retval; 650b09cd163SJoonyoung Shim } 651b09cd163SJoonyoung Shim 652b09cd163SJoonyoung Shim 653b09cd163SJoonyoung Shim /* 654b09cd163SJoonyoung Shim * si470x_vidioc_s_tuner - set tuner attributes 655b09cd163SJoonyoung Shim */ 656b09cd163SJoonyoung Shim static int si470x_vidioc_s_tuner(struct file *file, void *priv, 657b09cd163SJoonyoung Shim struct v4l2_tuner *tuner) 658b09cd163SJoonyoung Shim { 659b09cd163SJoonyoung Shim struct si470x_device *radio = video_drvdata(file); 66073c994e4STobias Lorenz int retval = 0; 661b09cd163SJoonyoung Shim 662b09cd163SJoonyoung Shim if (tuner->index != 0) 663b09cd163SJoonyoung Shim goto done; 664b09cd163SJoonyoung Shim 665b09cd163SJoonyoung Shim /* mono/stereo selector */ 666b09cd163SJoonyoung Shim switch (tuner->audmode) { 667b09cd163SJoonyoung Shim case V4L2_TUNER_MODE_MONO: 668b09cd163SJoonyoung Shim radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */ 669b09cd163SJoonyoung Shim break; 670b09cd163SJoonyoung Shim case V4L2_TUNER_MODE_STEREO: 671b09cd163SJoonyoung Shim radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */ 672b09cd163SJoonyoung Shim break; 673b09cd163SJoonyoung Shim default: 674b09cd163SJoonyoung Shim goto done; 675b09cd163SJoonyoung Shim } 676b09cd163SJoonyoung Shim 677b09cd163SJoonyoung Shim retval = si470x_set_register(radio, POWERCFG); 678b09cd163SJoonyoung Shim 679b09cd163SJoonyoung Shim done: 680b09cd163SJoonyoung Shim if (retval < 0) 681*4967d53dSHans Verkuil dev_warn(&radio->videodev.dev, 682a9d6fd5eSJoonyoung Shim "set tuner failed with %d\n", retval); 683b09cd163SJoonyoung Shim return retval; 684b09cd163SJoonyoung Shim } 685b09cd163SJoonyoung Shim 686b09cd163SJoonyoung Shim 687b09cd163SJoonyoung Shim /* 688b09cd163SJoonyoung Shim * si470x_vidioc_g_frequency - get tuner or modulator radio frequency 689b09cd163SJoonyoung Shim */ 690b09cd163SJoonyoung Shim static int si470x_vidioc_g_frequency(struct file *file, void *priv, 691b09cd163SJoonyoung Shim struct v4l2_frequency *freq) 692b09cd163SJoonyoung Shim { 693b09cd163SJoonyoung Shim struct si470x_device *radio = video_drvdata(file); 694b09cd163SJoonyoung Shim int retval = 0; 695b09cd163SJoonyoung Shim 696b09cd163SJoonyoung Shim if (freq->tuner != 0) { 697b09cd163SJoonyoung Shim retval = -EINVAL; 698b09cd163SJoonyoung Shim goto done; 699b09cd163SJoonyoung Shim } 700b09cd163SJoonyoung Shim 701b09cd163SJoonyoung Shim freq->type = V4L2_TUNER_RADIO; 702b09cd163SJoonyoung Shim retval = si470x_get_freq(radio, &freq->frequency); 703b09cd163SJoonyoung Shim 704b09cd163SJoonyoung Shim done: 705b09cd163SJoonyoung Shim if (retval < 0) 706*4967d53dSHans Verkuil dev_warn(&radio->videodev.dev, 707a9d6fd5eSJoonyoung Shim "get frequency failed with %d\n", retval); 708b09cd163SJoonyoung Shim return retval; 709b09cd163SJoonyoung Shim } 710b09cd163SJoonyoung Shim 711b09cd163SJoonyoung Shim 712b09cd163SJoonyoung Shim /* 713b09cd163SJoonyoung Shim * si470x_vidioc_s_frequency - set tuner or modulator radio frequency 714b09cd163SJoonyoung Shim */ 715b09cd163SJoonyoung Shim static int si470x_vidioc_s_frequency(struct file *file, void *priv, 716b09cd163SJoonyoung Shim struct v4l2_frequency *freq) 717b09cd163SJoonyoung Shim { 718b09cd163SJoonyoung Shim struct si470x_device *radio = video_drvdata(file); 719b09cd163SJoonyoung Shim int retval = 0; 720b09cd163SJoonyoung Shim 721b09cd163SJoonyoung Shim if (freq->tuner != 0) { 722b09cd163SJoonyoung Shim retval = -EINVAL; 723b09cd163SJoonyoung Shim goto done; 724b09cd163SJoonyoung Shim } 725b09cd163SJoonyoung Shim 726b09cd163SJoonyoung Shim retval = si470x_set_freq(radio, freq->frequency); 727b09cd163SJoonyoung Shim 728b09cd163SJoonyoung Shim done: 729b09cd163SJoonyoung Shim if (retval < 0) 730*4967d53dSHans Verkuil dev_warn(&radio->videodev.dev, 731a9d6fd5eSJoonyoung Shim "set frequency failed with %d\n", retval); 732b09cd163SJoonyoung Shim return retval; 733b09cd163SJoonyoung Shim } 734b09cd163SJoonyoung Shim 735b09cd163SJoonyoung Shim 736b09cd163SJoonyoung Shim /* 737b09cd163SJoonyoung Shim * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek 738b09cd163SJoonyoung Shim */ 739b09cd163SJoonyoung Shim static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, 740b09cd163SJoonyoung Shim struct v4l2_hw_freq_seek *seek) 741b09cd163SJoonyoung Shim { 742b09cd163SJoonyoung Shim struct si470x_device *radio = video_drvdata(file); 743b09cd163SJoonyoung Shim int retval = 0; 744b09cd163SJoonyoung Shim 745b09cd163SJoonyoung Shim if (seek->tuner != 0) { 746b09cd163SJoonyoung Shim retval = -EINVAL; 747b09cd163SJoonyoung Shim goto done; 748b09cd163SJoonyoung Shim } 749b09cd163SJoonyoung Shim 750b09cd163SJoonyoung Shim retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward); 751b09cd163SJoonyoung Shim 752b09cd163SJoonyoung Shim done: 753b09cd163SJoonyoung Shim if (retval < 0) 754*4967d53dSHans Verkuil dev_warn(&radio->videodev.dev, 755a9d6fd5eSJoonyoung Shim "set hardware frequency seek failed with %d\n", retval); 756b09cd163SJoonyoung Shim return retval; 757b09cd163SJoonyoung Shim } 758b09cd163SJoonyoung Shim 759*4967d53dSHans Verkuil const struct v4l2_ctrl_ops si470x_ctrl_ops = { 760*4967d53dSHans Verkuil .s_ctrl = si470x_s_ctrl, 761*4967d53dSHans Verkuil }; 762b09cd163SJoonyoung Shim 763b09cd163SJoonyoung Shim /* 764b09cd163SJoonyoung Shim * si470x_ioctl_ops - video device ioctl operations 765b09cd163SJoonyoung Shim */ 766b09cd163SJoonyoung Shim static const struct v4l2_ioctl_ops si470x_ioctl_ops = { 767b09cd163SJoonyoung Shim .vidioc_querycap = si470x_vidioc_querycap, 768b09cd163SJoonyoung Shim .vidioc_g_tuner = si470x_vidioc_g_tuner, 769b09cd163SJoonyoung Shim .vidioc_s_tuner = si470x_vidioc_s_tuner, 770b09cd163SJoonyoung Shim .vidioc_g_frequency = si470x_vidioc_g_frequency, 771b09cd163SJoonyoung Shim .vidioc_s_frequency = si470x_vidioc_s_frequency, 772b09cd163SJoonyoung Shim .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek, 773b09cd163SJoonyoung Shim }; 774b09cd163SJoonyoung Shim 775b09cd163SJoonyoung Shim 776b09cd163SJoonyoung Shim /* 777b09cd163SJoonyoung Shim * si470x_viddev_template - video device interface 778b09cd163SJoonyoung Shim */ 779b09cd163SJoonyoung Shim struct video_device si470x_viddev_template = { 780b09cd163SJoonyoung Shim .fops = &si470x_fops, 781b09cd163SJoonyoung Shim .name = DRIVER_NAME, 782*4967d53dSHans Verkuil .release = video_device_release_empty, 783b09cd163SJoonyoung Shim .ioctl_ops = &si470x_ioctl_ops, 784b09cd163SJoonyoung Shim }; 785