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