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