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 * File Operations Interface
 430 **************************************************************************/
 431
 432/*
 433 * si470x_fops_read - read RDS data
 434 */
 435static ssize_t si470x_fops_read(struct file *file, char __user *buf,
 436                size_t count, loff_t *ppos)
 437{
 438        struct si470x_device *radio = video_drvdata(file);
 439        int retval = 0;
 440        unsigned int block_count = 0;
 441
 442        /* switch on rds reception */
 443        if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
 444                si470x_rds_on(radio);
 445
 446        /* block if no new data available */
 447        while (radio->wr_index == radio->rd_index) {
 448                if (file->f_flags & O_NONBLOCK) {
 449                        retval = -EWOULDBLOCK;
 450                        goto done;
 451                }
 452                if (wait_event_interruptible(radio->read_queue,
 453                        radio->wr_index != radio->rd_index) < 0) {
 454                        retval = -EINTR;
 455                        goto done;
 456                }
 457        }
 458
 459        /* calculate block count from byte count */
 460        count /= 3;
 461
 462        /* copy RDS block out of internal buffer and to user buffer */
 463        mutex_lock(&radio->lock);
 464        while (block_count < count) {
 465                if (radio->rd_index == radio->wr_index)
 466                        break;
 467
 468                /* always transfer rds complete blocks */
 469                if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3))
 470                        /* retval = -EFAULT; */
 471                        break;
 472
 473                /* increment and wrap read pointer */
 474                radio->rd_index += 3;
 475                if (radio->rd_index >= radio->buf_size)
 476                        radio->rd_index = 0;
 477
 478                /* increment counters */
 479                block_count++;
 480                buf += 3;
 481                retval += 3;
 482        }
 483        mutex_unlock(&radio->lock);
 484
 485done:
 486        return retval;
 487}
 488
 489
 490/*
 491 * si470x_fops_poll - poll RDS data
 492 */
 493static unsigned int si470x_fops_poll(struct file *file,
 494                struct poll_table_struct *pts)
 495{
 496        struct si470x_device *radio = video_drvdata(file);
 497        int retval = 0;
 498
 499        /* switch on rds reception */
 500        if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
 501                si470x_rds_on(radio);
 502
 503        poll_wait(file, &radio->read_queue, pts);
 504
 505        if (radio->rd_index != radio->wr_index)
 506                retval = POLLIN | POLLRDNORM;
 507
 508        return retval;
 509}
 510
 511
 512/*
 513 * si470x_fops - file operations interface
 514 */
 515static const struct v4l2_file_operations si470x_fops = {
 516        .owner                  = THIS_MODULE,
 517        .read                   = si470x_fops_read,
 518        .poll                   = si470x_fops_poll,
 519        .ioctl                  = video_ioctl2,
 520        .open                   = si470x_fops_open,
 521        .release                = si470x_fops_release,
 522};
 523
 524
 525
 526/**************************************************************************
 527 * Video4Linux Interface
 528 **************************************************************************/
 529
 530/*
 531 * si470x_vidioc_queryctrl - enumerate control items
 532 */
 533static int si470x_vidioc_queryctrl(struct file *file, void *priv,
 534                struct v4l2_queryctrl *qc)
 535{
 536        struct si470x_device *radio = video_drvdata(file);
 537        int retval = -EINVAL;
 538
 539        /* abort if qc->id is below V4L2_CID_BASE */
 540        if (qc->id < V4L2_CID_BASE)
 541                goto done;
 542
 543        /* search video control */
 544        switch (qc->id) {
 545        case V4L2_CID_AUDIO_VOLUME:
 546                return v4l2_ctrl_query_fill(qc, 0, 15, 1, 15);
 547        case V4L2_CID_AUDIO_MUTE:
 548                return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
 549        }
 550
 551        /* disable unsupported base controls */
 552        /* to satisfy kradio and such apps */
 553        if ((retval == -EINVAL) && (qc->id < V4L2_CID_LASTP1)) {
 554                qc->flags = V4L2_CTRL_FLAG_DISABLED;
 555                retval = 0;
 556        }
 557
 558done:
 559        if (retval < 0)
 560                dev_warn(&radio->videodev->dev,
 561                        "query controls failed with %d\n", retval);
 562        return retval;
 563}
 564
 565
 566/*
 567 * si470x_vidioc_g_ctrl - get the value of a control
 568 */
 569static int si470x_vidioc_g_ctrl(struct file *file, void *priv,
 570                struct v4l2_control *ctrl)
 571{
 572        struct si470x_device *radio = video_drvdata(file);
 573        int retval = 0;
 574
 575        /* safety checks */
 576        retval = si470x_disconnect_check(radio);
 577        if (retval)
 578                goto done;
 579
 580        switch (ctrl->id) {
 581        case V4L2_CID_AUDIO_VOLUME:
 582                ctrl->value = radio->registers[SYSCONFIG2] &
 583                                SYSCONFIG2_VOLUME;
 584                break;
 585        case V4L2_CID_AUDIO_MUTE:
 586                ctrl->value = ((radio->registers[POWERCFG] &
 587                                POWERCFG_DMUTE) == 0) ? 1 : 0;
 588                break;
 589        default:
 590                retval = -EINVAL;
 591        }
 592
 593done:
 594        if (retval < 0)
 595                dev_warn(&radio->videodev->dev,
 596                        "get control failed with %d\n", retval);
 597        return retval;
 598}
 599
 600
 601/*
 602 * si470x_vidioc_s_ctrl - set the value of a control
 603 */
 604static int si470x_vidioc_s_ctrl(struct file *file, void *priv,
 605                struct v4l2_control *ctrl)
 606{
 607        struct si470x_device *radio = video_drvdata(file);
 608        int retval = 0;
 609
 610        /* safety checks */
 611        retval = si470x_disconnect_check(radio);
 612        if (retval)
 613                goto done;
 614
 615        switch (ctrl->id) {
 616        case V4L2_CID_AUDIO_VOLUME:
 617                radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME;
 618                radio->registers[SYSCONFIG2] |= ctrl->value;
 619                retval = si470x_set_register(radio, SYSCONFIG2);
 620                break;
 621        case V4L2_CID_AUDIO_MUTE:
 622                if (ctrl->value == 1)
 623                        radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
 624                else
 625                        radio->registers[POWERCFG] |= POWERCFG_DMUTE;
 626                retval = si470x_set_register(radio, POWERCFG);
 627                break;
 628        default:
 629                retval = -EINVAL;
 630        }
 631
 632done:
 633        if (retval < 0)
 634                dev_warn(&radio->videodev->dev,
 635                        "set control failed with %d\n", retval);
 636        return retval;
 637}
 638
 639
 640/*
 641 * si470x_vidioc_g_audio - get audio attributes
 642 */
 643static int si470x_vidioc_g_audio(struct file *file, void *priv,
 644                struct v4l2_audio *audio)
 645{
 646        /* driver constants */
 647        audio->index = 0;
 648        strcpy(audio->name, "Radio");
 649        audio->capability = V4L2_AUDCAP_STEREO;
 650        audio->mode = 0;
 651
 652        return 0;
 653}
 654
 655
 656/*
 657 * si470x_vidioc_g_tuner - get tuner attributes
 658 */
 659static int si470x_vidioc_g_tuner(struct file *file, void *priv,
 660                struct v4l2_tuner *tuner)
 661{
 662        struct si470x_device *radio = video_drvdata(file);
 663        int retval = 0;
 664
 665        /* safety checks */
 666        retval = si470x_disconnect_check(radio);
 667        if (retval)
 668                goto done;
 669
 670        if (tuner->index != 0) {
 671                retval = -EINVAL;
 672                goto done;
 673        }
 674
 675        retval = si470x_get_register(radio, STATUSRSSI);
 676        if (retval < 0)
 677                goto done;
 678
 679        /* driver constants */
 680        strcpy(tuner->name, "FM");
 681        tuner->type = V4L2_TUNER_RADIO;
 682#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
 683        tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
 684                            V4L2_TUNER_CAP_RDS;
 685#else
 686        tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
 687#endif
 688
 689        /* range limits */
 690        switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
 691        /* 0: 87.5 - 108 MHz (USA, Europe, default) */
 692        default:
 693                tuner->rangelow  =  87.5 * FREQ_MUL;
 694                tuner->rangehigh = 108   * FREQ_MUL;
 695                break;
 696        /* 1: 76   - 108 MHz (Japan wide band) */
 697        case 1:
 698                tuner->rangelow  =  76   * FREQ_MUL;
 699                tuner->rangehigh = 108   * FREQ_MUL;
 700                break;
 701        /* 2: 76   -  90 MHz (Japan) */
 702        case 2:
 703                tuner->rangelow  =  76   * FREQ_MUL;
 704                tuner->rangehigh =  90   * FREQ_MUL;
 705                break;
 706        };
 707
 708        /* stereo indicator == stereo (instead of mono) */
 709        if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
 710                tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
 711        else
 712                tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
 713#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
 714        /* If there is a reliable method of detecting an RDS channel,
 715           then this code should check for that before setting this
 716           RDS subchannel. */
 717        tuner->rxsubchans |= V4L2_TUNER_SUB_RDS;
 718#endif
 719
 720        /* mono/stereo selector */
 721        if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0)
 722                tuner->audmode = V4L2_TUNER_MODE_STEREO;
 723        else
 724                tuner->audmode = V4L2_TUNER_MODE_MONO;
 725
 726        /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */
 727        /* measured in units of db\xC2\xB5V in 1 db increments (max at ~75 db\xC2\xB5V) */
 728        tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI);
 729        /* the ideal factor is 0xffff/75 = 873,8 */
 730        tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10);
 731
 732        /* automatic frequency control: -1: freq to low, 1 freq to high */
 733        /* AFCRL does only indicate that freq. differs, not if too low/high */
 734        tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0;
 735
 736done:
 737        if (retval < 0)
 738                dev_warn(&radio->videodev->dev,
 739                        "get tuner failed with %d\n", retval);
 740        return retval;
 741}
 742
 743
 744/*
 745 * si470x_vidioc_s_tuner - set tuner attributes
 746 */
 747static int si470x_vidioc_s_tuner(struct file *file, void *priv,
 748                struct v4l2_tuner *tuner)
 749{
 750        struct si470x_device *radio = video_drvdata(file);
 751        int retval = -EINVAL;
 752
 753        /* safety checks */
 754        retval = si470x_disconnect_check(radio);
 755        if (retval)
 756                goto done;
 757
 758        if (tuner->index != 0)
 759                goto done;
 760
 761        /* mono/stereo selector */
 762        switch (tuner->audmode) {
 763        case V4L2_TUNER_MODE_MONO:
 764                radio->registers[POWERCFG] |= POWERCFG_MONO;  /* force mono */
 765                break;
 766        case V4L2_TUNER_MODE_STEREO:
 767                radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */
 768                break;
 769        default:
 770                goto done;
 771        }
 772
 773        retval = si470x_set_register(radio, POWERCFG);
 774
 775done:
 776        if (retval < 0)
 777                dev_warn(&radio->videodev->dev,
 778                        "set tuner failed with %d\n", retval);
 779        return retval;
 780}
 781
 782
 783/*
 784 * si470x_vidioc_g_frequency - get tuner or modulator radio frequency
 785 */
 786static int si470x_vidioc_g_frequency(struct file *file, void *priv,
 787                struct v4l2_frequency *freq)
 788{
 789        struct si470x_device *radio = video_drvdata(file);
 790        int retval = 0;
 791
 792        /* safety checks */
 793        retval = si470x_disconnect_check(radio);
 794        if (retval)
 795                goto done;
 796
 797        if (freq->tuner != 0) {
 798                retval = -EINVAL;
 799                goto done;
 800        }
 801
 802        freq->type = V4L2_TUNER_RADIO;
 803        retval = si470x_get_freq(radio, &freq->frequency);
 804
 805done:
 806        if (retval < 0)
 807                dev_warn(&radio->videodev->dev,
 808                        "get frequency failed with %d\n", retval);
 809        return retval;
 810}
 811
 812
 813/*
 814 * si470x_vidioc_s_frequency - set tuner or modulator radio frequency
 815 */
 816static int si470x_vidioc_s_frequency(struct file *file, void *priv,
 817                struct v4l2_frequency *freq)
 818{
 819        struct si470x_device *radio = video_drvdata(file);
 820        int retval = 0;
 821
 822        /* safety checks */
 823        retval = si470x_disconnect_check(radio);
 824        if (retval)
 825                goto done;
 826
 827        if (freq->tuner != 0) {
 828                retval = -EINVAL;
 829                goto done;
 830        }
 831
 832        retval = si470x_set_freq(radio, freq->frequency);
 833
 834done:
 835        if (retval < 0)
 836                dev_warn(&radio->videodev->dev,
 837                        "set frequency failed with %d\n", retval);
 838        return retval;
 839}
 840
 841
 842/*
 843 * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek
 844 */
 845static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
 846                struct v4l2_hw_freq_seek *seek)
 847{
 848        struct si470x_device *radio = video_drvdata(file);
 849        int retval = 0;
 850
 851        /* safety checks */
 852        retval = si470x_disconnect_check(radio);
 853        if (retval)
 854                goto done;
 855
 856        if (seek->tuner != 0) {
 857                retval = -EINVAL;
 858                goto done;
 859        }
 860
 861        retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward);
 862
 863done:
 864        if (retval < 0)
 865                dev_warn(&radio->videodev->dev,
 866                        "set hardware frequency seek failed with %d\n", retval);
 867        return retval;
 868}
 869
 870
 871/*
 872 * si470x_ioctl_ops - video device ioctl operations
 873 */
 874static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
 875        .vidioc_querycap        = si470x_vidioc_querycap,
 876        .vidioc_queryctrl       = si470x_vidioc_queryctrl,
 877        .vidioc_g_ctrl          = si470x_vidioc_g_ctrl,
 878        .vidioc_s_ctrl          = si470x_vidioc_s_ctrl,
 879        .vidioc_g_audio         = si470x_vidioc_g_audio,
 880        .vidioc_g_tuner         = si470x_vidioc_g_tuner,
 881        .vidioc_s_tuner         = si470x_vidioc_s_tuner,
 882        .vidioc_g_frequency     = si470x_vidioc_g_frequency,
 883        .vidioc_s_frequency     = si470x_vidioc_s_frequency,
 884        .vidioc_s_hw_freq_seek  = si470x_vidioc_s_hw_freq_seek,
 885};
 886
 887
 888/*
 889 * si470x_viddev_template - video device interface
 890 */
 891struct video_device si470x_viddev_template = {
 892        .fops                   = &si470x_fops,
 893        .name                   = DRIVER_NAME,
 894        .release                = video_device_release,
 895        .ioctl_ops              = &si470x_ioctl_ops,
 896};
 897