root/drivers/media/radio/si470x/radio-si470x-common.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. si470x_set_band
  2. si470x_set_chan
  3. si470x_get_step
  4. si470x_get_freq
  5. si470x_set_freq
  6. si470x_set_seek
  7. si470x_start
  8. si470x_stop
  9. si470x_rds_on
  10. si470x_fops_read
  11. si470x_fops_poll
  12. si470x_fops_open
  13. si470x_fops_release
  14. si470x_s_ctrl
  15. si470x_vidioc_g_tuner
  16. si470x_vidioc_s_tuner
  17. si470x_vidioc_g_frequency
  18. si470x_vidioc_s_frequency
  19. si470x_vidioc_s_hw_freq_seek
  20. si470x_vidioc_enum_freq_bands
  21. si470x_vidioc_querycap

   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_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */