Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  drivers/media/radio/si470x/radio-si470x-common.c
0004  *
0005  *  Driver for radios with Silicon Labs Si470x FM Radio Receivers
0006  *
0007  *  Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
0008  *  Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com>
0009  */
0010 
0011 
0012 /*
0013  * History:
0014  * 2008-01-12   Tobias Lorenz <tobias.lorenz@gmx.net>
0015  *      Version 1.0.0
0016  *      - First working version
0017  * 2008-01-13   Tobias Lorenz <tobias.lorenz@gmx.net>
0018  *      Version 1.0.1
0019  *      - Improved error handling, every function now returns errno
0020  *      - Improved multi user access (start/mute/stop)
0021  *      - Channel doesn't get lost anymore after start/mute/stop
0022  *      - RDS support added (polling mode via interrupt EP 1)
0023  *      - marked default module parameters with *value*
0024  *      - switched from bit structs to bit masks
0025  *      - header file cleaned and integrated
0026  * 2008-01-14   Tobias Lorenz <tobias.lorenz@gmx.net>
0027  *      Version 1.0.2
0028  *      - hex values are now lower case
0029  *      - commented USB ID for ADS/Tech moved on todo list
0030  *      - blacklisted si470x in hid-quirks.c
0031  *      - rds buffer handling functions integrated into *_work, *_read
0032  *      - rds_command in si470x_poll exchanged against simple retval
0033  *      - check for firmware version 15
0034  *      - code order and prototypes still remain the same
0035  *      - spacing and bottom of band codes remain the same
0036  * 2008-01-16   Tobias Lorenz <tobias.lorenz@gmx.net>
0037  *      Version 1.0.3
0038  *      - code reordered to avoid function prototypes
0039  *      - switch/case defaults are now more user-friendly
0040  *      - unified comment style
0041  *      - applied all checkpatch.pl v1.12 suggestions
0042  *        except the warning about the too long lines with bit comments
0043  *      - renamed FMRADIO to RADIO to cut line length (checkpatch.pl)
0044  * 2008-01-22   Tobias Lorenz <tobias.lorenz@gmx.net>
0045  *      Version 1.0.4
0046  *      - avoid poss. locking when doing copy_to_user which may sleep
0047  *      - RDS is automatically activated on read now
0048  *      - code cleaned of unnecessary rds_commands
0049  *      - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified
0050  *        (thanks to Guillaume RAMOUSSE)
0051  * 2008-01-27   Tobias Lorenz <tobias.lorenz@gmx.net>
0052  *      Version 1.0.5
0053  *      - number of seek_retries changed to tune_timeout
0054  *      - fixed problem with incomplete tune operations by own buffers
0055  *      - optimization of variables and printf types
0056  *      - improved error logging
0057  * 2008-01-31   Tobias Lorenz <tobias.lorenz@gmx.net>
0058  *      Oliver Neukum <oliver@neukum.org>
0059  *      Version 1.0.6
0060  *      - fixed coverity checker warnings in *_usb_driver_disconnect
0061  *      - probe()/open() race by correct ordering in probe()
0062  *      - DMA coherency rules by separate allocation of all buffers
0063  *      - use of endianness macros
0064  *      - abuse of spinlock, replaced by mutex
0065  *      - racy handling of timer in disconnect,
0066  *        replaced by delayed_work
0067  *      - racy interruptible_sleep_on(),
0068  *        replaced with wait_event_interruptible()
0069  *      - handle signals in read()
0070  * 2008-02-08   Tobias Lorenz <tobias.lorenz@gmx.net>
0071  *      Oliver Neukum <oliver@neukum.org>
0072  *      Version 1.0.7
0073  *      - usb autosuspend support
0074  *      - unplugging fixed
0075  * 2008-05-07   Tobias Lorenz <tobias.lorenz@gmx.net>
0076  *      Version 1.0.8
0077  *      - hardware frequency seek support
0078  *      - afc indication
0079  *      - more safety checks, let si470x_get_freq return errno
0080  *      - vidioc behavior corrected according to v4l2 spec
0081  * 2008-10-20   Alexey Klimov <klimov.linux@gmail.com>
0082  *      - add support for KWorld USB FM Radio FM700
0083  *      - blacklisted KWorld radio in hid-core.c and hid-ids.h
0084  * 2008-12-03   Mark Lord <mlord@pobox.com>
0085  *      - add support for DealExtreme USB Radio
0086  * 2009-01-31   Bob Ross <pigiron@gmx.com>
0087  *      - correction of stereo detection/setting
0088  *      - correction of signal strength indicator scaling
0089  * 2009-01-31   Rick Bronson <rick@efn.org>
0090  *      Tobias Lorenz <tobias.lorenz@gmx.net>
0091  *      - add LED status output
0092  *      - get HW/SW version from scratchpad
0093  * 2009-06-16   Edouard Lafargue <edouard@lafargue.name>
0094  *      Version 1.0.10
0095  *      - add support for interrupt mode for RDS endpoint,
0096  *                instead of polling.
0097  *                Improves RDS reception significantly
0098  */
0099 
0100 
0101 /* kernel includes */
0102 #include "radio-si470x.h"
0103 
0104 /**************************************************************************
0105  * Module Parameters
0106  **************************************************************************/
0107 
0108 /* Spacing (kHz) */
0109 /* 0: 200 kHz (USA, Australia) */
0110 /* 1: 100 kHz (Europe, Japan) */
0111 /* 2:  50 kHz */
0112 static unsigned short space = 2;
0113 module_param(space, ushort, 0444);
0114 MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
0115 
0116 /* De-emphasis */
0117 /* 0: 75 us (USA) */
0118 /* 1: 50 us (Europe, Australia, Japan) */
0119 static unsigned short de = 1;
0120 module_param(de, ushort, 0444);
0121 MODULE_PARM_DESC(de, "De-emphasis: 0=75us *1=50us*");
0122 
0123 /* Tune timeout */
0124 static unsigned int tune_timeout = 3000;
0125 module_param(tune_timeout, uint, 0644);
0126 MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*");
0127 
0128 /* Seek timeout */
0129 static unsigned int seek_timeout = 5000;
0130 module_param(seek_timeout, uint, 0644);
0131 MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
0132 
0133 static const struct v4l2_frequency_band bands[] = {
0134     {
0135         .type = V4L2_TUNER_RADIO,
0136         .index = 0,
0137         .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
0138                 V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
0139                 V4L2_TUNER_CAP_FREQ_BANDS |
0140                 V4L2_TUNER_CAP_HWSEEK_BOUNDED |
0141                 V4L2_TUNER_CAP_HWSEEK_WRAP,
0142         .rangelow   =  87500 * 16,
0143         .rangehigh  = 108000 * 16,
0144         .modulation = V4L2_BAND_MODULATION_FM,
0145     },
0146     {
0147         .type = V4L2_TUNER_RADIO,
0148         .index = 1,
0149         .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
0150                 V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
0151                 V4L2_TUNER_CAP_FREQ_BANDS |
0152                 V4L2_TUNER_CAP_HWSEEK_BOUNDED |
0153                 V4L2_TUNER_CAP_HWSEEK_WRAP,
0154         .rangelow   =  76000 * 16,
0155         .rangehigh  = 108000 * 16,
0156         .modulation = V4L2_BAND_MODULATION_FM,
0157     },
0158     {
0159         .type = V4L2_TUNER_RADIO,
0160         .index = 2,
0161         .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
0162                 V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
0163                 V4L2_TUNER_CAP_FREQ_BANDS |
0164                 V4L2_TUNER_CAP_HWSEEK_BOUNDED |
0165                 V4L2_TUNER_CAP_HWSEEK_WRAP,
0166         .rangelow   =  76000 * 16,
0167         .rangehigh  =  90000 * 16,
0168         .modulation = V4L2_BAND_MODULATION_FM,
0169     },
0170 };
0171 
0172 /**************************************************************************
0173  * Generic Functions
0174  **************************************************************************/
0175 
0176 /*
0177  * si470x_set_band - set the band
0178  */
0179 static int si470x_set_band(struct si470x_device *radio, int band)
0180 {
0181     if (radio->band == band)
0182         return 0;
0183 
0184     radio->band = band;
0185     radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_BAND;
0186     radio->registers[SYSCONFIG2] |= radio->band << 6;
0187     return radio->set_register(radio, SYSCONFIG2);
0188 }
0189 
0190 /*
0191  * si470x_set_chan - set the channel
0192  */
0193 static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
0194 {
0195     int retval;
0196     unsigned long time_left;
0197     bool timed_out = false;
0198 
0199     retval = radio->get_register(radio, POWERCFG);
0200     if (retval)
0201         return retval;
0202 
0203     if ((radio->registers[POWERCFG] & (POWERCFG_ENABLE|POWERCFG_DMUTE))
0204         != (POWERCFG_ENABLE|POWERCFG_DMUTE)) {
0205         return 0;
0206     }
0207 
0208     /* start tuning */
0209     radio->registers[CHANNEL] &= ~CHANNEL_CHAN;
0210     radio->registers[CHANNEL] |= CHANNEL_TUNE | chan;
0211     retval = radio->set_register(radio, CHANNEL);
0212     if (retval < 0)
0213         goto done;
0214 
0215     /* wait till tune operation has completed */
0216     reinit_completion(&radio->completion);
0217     time_left = wait_for_completion_timeout(&radio->completion,
0218                         msecs_to_jiffies(tune_timeout));
0219     if (time_left == 0)
0220         timed_out = true;
0221 
0222     if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
0223         dev_warn(&radio->videodev.dev, "tune does not complete\n");
0224     if (timed_out)
0225         dev_warn(&radio->videodev.dev,
0226             "tune timed out after %u ms\n", tune_timeout);
0227 
0228     /* stop tuning */
0229     radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
0230     retval = radio->set_register(radio, CHANNEL);
0231 
0232 done:
0233     return retval;
0234 }
0235 
0236 /*
0237  * si470x_get_step - get channel spacing
0238  */
0239 static unsigned int si470x_get_step(struct si470x_device *radio)
0240 {
0241     /* Spacing (kHz) */
0242     switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
0243     /* 0: 200 kHz (USA, Australia) */
0244     case 0:
0245         return 200 * 16;
0246     /* 1: 100 kHz (Europe, Japan) */
0247     case 1:
0248         return 100 * 16;
0249     /* 2:  50 kHz */
0250     default:
0251         return 50 * 16;
0252     }
0253 }
0254 
0255 
0256 /*
0257  * si470x_get_freq - get the frequency
0258  */
0259 static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
0260 {
0261     int chan, retval;
0262 
0263     /* read channel */
0264     retval = radio->get_register(radio, READCHAN);
0265     chan = radio->registers[READCHAN] & READCHAN_READCHAN;
0266 
0267     /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
0268     *freq = chan * si470x_get_step(radio) + bands[radio->band].rangelow;
0269 
0270     return retval;
0271 }
0272 
0273 
0274 /*
0275  * si470x_set_freq - set the frequency
0276  */
0277 int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
0278 {
0279     unsigned short chan;
0280 
0281     freq = clamp(freq, bands[radio->band].rangelow,
0282                bands[radio->band].rangehigh);
0283     /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
0284     chan = (freq - bands[radio->band].rangelow) / si470x_get_step(radio);
0285 
0286     return si470x_set_chan(radio, chan);
0287 }
0288 EXPORT_SYMBOL_GPL(si470x_set_freq);
0289 
0290 
0291 /*
0292  * si470x_set_seek - set seek
0293  */
0294 static int si470x_set_seek(struct si470x_device *radio,
0295                const struct v4l2_hw_freq_seek *seek)
0296 {
0297     int band, retval;
0298     unsigned int freq;
0299     bool timed_out = false;
0300     unsigned long time_left;
0301 
0302     /* set band */
0303     if (seek->rangelow || seek->rangehigh) {
0304         for (band = 0; band < ARRAY_SIZE(bands); band++) {
0305             if (bands[band].rangelow  == seek->rangelow &&
0306                 bands[band].rangehigh == seek->rangehigh)
0307                 break;
0308         }
0309         if (band == ARRAY_SIZE(bands))
0310             return -EINVAL; /* No matching band found */
0311     } else
0312         band = 1; /* If nothing is specified seek 76 - 108 Mhz */
0313 
0314     if (radio->band != band) {
0315         retval = si470x_get_freq(radio, &freq);
0316         if (retval)
0317             return retval;
0318         retval = si470x_set_band(radio, band);
0319         if (retval)
0320             return retval;
0321         retval = si470x_set_freq(radio, freq);
0322         if (retval)
0323             return retval;
0324     }
0325 
0326     /* start seeking */
0327     radio->registers[POWERCFG] |= POWERCFG_SEEK;
0328     if (seek->wrap_around)
0329         radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;
0330     else
0331         radio->registers[POWERCFG] |= POWERCFG_SKMODE;
0332     if (seek->seek_upward)
0333         radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
0334     else
0335         radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
0336     retval = radio->set_register(radio, POWERCFG);
0337     if (retval < 0)
0338         return retval;
0339 
0340     /* wait till tune operation has completed */
0341     reinit_completion(&radio->completion);
0342     time_left = wait_for_completion_timeout(&radio->completion,
0343                         msecs_to_jiffies(seek_timeout));
0344     if (time_left == 0)
0345         timed_out = true;
0346 
0347     if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
0348         dev_warn(&radio->videodev.dev, "seek does not complete\n");
0349     if (radio->registers[STATUSRSSI] & STATUSRSSI_SF)
0350         dev_warn(&radio->videodev.dev,
0351             "seek failed / band limit reached\n");
0352 
0353     /* stop seeking */
0354     radio->registers[POWERCFG] &= ~POWERCFG_SEEK;
0355     retval = radio->set_register(radio, POWERCFG);
0356 
0357     /* try again, if timed out */
0358     if (retval == 0 && timed_out)
0359         return -ENODATA;
0360     return retval;
0361 }
0362 
0363 
0364 /*
0365  * si470x_start - switch on radio
0366  */
0367 int si470x_start(struct si470x_device *radio)
0368 {
0369     int retval;
0370 
0371     /* powercfg */
0372     radio->registers[POWERCFG] =
0373         POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM;
0374     retval = radio->set_register(radio, POWERCFG);
0375     if (retval < 0)
0376         goto done;
0377 
0378     /* sysconfig 1 */
0379     radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDSIEN | SYSCONFIG1_STCIEN |
0380                     SYSCONFIG1_RDS;
0381     radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_GPIO2;
0382     radio->registers[SYSCONFIG1] |= SYSCONFIG1_GPIO2_INT;
0383     if (de)
0384         radio->registers[SYSCONFIG1] |= SYSCONFIG1_DE;
0385     retval = radio->set_register(radio, SYSCONFIG1);
0386     if (retval < 0)
0387         goto done;
0388 
0389     /* sysconfig 2 */
0390     radio->registers[SYSCONFIG2] =
0391         (0x1f  << 8) |              /* SEEKTH */
0392         ((radio->band << 6) & SYSCONFIG2_BAND) |/* BAND */
0393         ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */
0394         15;                 /* VOLUME (max) */
0395     retval = radio->set_register(radio, SYSCONFIG2);
0396     if (retval < 0)
0397         goto done;
0398 
0399     /* reset last channel */
0400     retval = si470x_set_chan(radio,
0401         radio->registers[CHANNEL] & CHANNEL_CHAN);
0402 
0403 done:
0404     return retval;
0405 }
0406 EXPORT_SYMBOL_GPL(si470x_start);
0407 
0408 
0409 /*
0410  * si470x_stop - switch off radio
0411  */
0412 int si470x_stop(struct si470x_device *radio)
0413 {
0414     int retval;
0415 
0416     /* sysconfig 1 */
0417     radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
0418     retval = radio->set_register(radio, SYSCONFIG1);
0419     if (retval < 0)
0420         goto done;
0421 
0422     /* powercfg */
0423     radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
0424     /* POWERCFG_ENABLE has to automatically go low */
0425     radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE;
0426     retval = radio->set_register(radio, POWERCFG);
0427 
0428 done:
0429     return retval;
0430 }
0431 EXPORT_SYMBOL_GPL(si470x_stop);
0432 
0433 
0434 /*
0435  * si470x_rds_on - switch on rds reception
0436  */
0437 static int si470x_rds_on(struct si470x_device *radio)
0438 {
0439     int retval;
0440 
0441     /* sysconfig 1 */
0442     radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS;
0443     retval = radio->set_register(radio, SYSCONFIG1);
0444     if (retval < 0)
0445         radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
0446 
0447     return retval;
0448 }
0449 
0450 
0451 
0452 /**************************************************************************
0453  * File Operations Interface
0454  **************************************************************************/
0455 
0456 /*
0457  * si470x_fops_read - read RDS data
0458  */
0459 static ssize_t si470x_fops_read(struct file *file, char __user *buf,
0460         size_t count, loff_t *ppos)
0461 {
0462     struct si470x_device *radio = video_drvdata(file);
0463     int retval = 0;
0464     unsigned int block_count = 0;
0465 
0466     /* switch on rds reception */
0467     if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
0468         si470x_rds_on(radio);
0469 
0470     /* block if no new data available */
0471     while (radio->wr_index == radio->rd_index) {
0472         if (file->f_flags & O_NONBLOCK) {
0473             retval = -EWOULDBLOCK;
0474             goto done;
0475         }
0476         if (wait_event_interruptible(radio->read_queue,
0477             radio->wr_index != radio->rd_index) < 0) {
0478             retval = -EINTR;
0479             goto done;
0480         }
0481     }
0482 
0483     /* calculate block count from byte count */
0484     count /= 3;
0485 
0486     /* copy RDS block out of internal buffer and to user buffer */
0487     while (block_count < count) {
0488         if (radio->rd_index == radio->wr_index)
0489             break;
0490 
0491         /* always transfer rds complete blocks */
0492         if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3))
0493             /* retval = -EFAULT; */
0494             break;
0495 
0496         /* increment and wrap read pointer */
0497         radio->rd_index += 3;
0498         if (radio->rd_index >= radio->buf_size)
0499             radio->rd_index = 0;
0500 
0501         /* increment counters */
0502         block_count++;
0503         buf += 3;
0504         retval += 3;
0505     }
0506 
0507 done:
0508     return retval;
0509 }
0510 
0511 
0512 /*
0513  * si470x_fops_poll - poll RDS data
0514  */
0515 static __poll_t si470x_fops_poll(struct file *file,
0516         struct poll_table_struct *pts)
0517 {
0518     struct si470x_device *radio = video_drvdata(file);
0519     __poll_t req_events = poll_requested_events(pts);
0520     __poll_t retval = v4l2_ctrl_poll(file, pts);
0521 
0522     if (req_events & (EPOLLIN | EPOLLRDNORM)) {
0523         /* switch on rds reception */
0524         if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
0525             si470x_rds_on(radio);
0526 
0527         poll_wait(file, &radio->read_queue, pts);
0528 
0529         if (radio->rd_index != radio->wr_index)
0530             retval |= EPOLLIN | EPOLLRDNORM;
0531     }
0532 
0533     return retval;
0534 }
0535 
0536 
0537 static int si470x_fops_open(struct file *file)
0538 {
0539     struct si470x_device *radio = video_drvdata(file);
0540 
0541     return radio->fops_open(file);
0542 }
0543 
0544 
0545 /*
0546  * si470x_fops_release - file release
0547  */
0548 static int si470x_fops_release(struct file *file)
0549 {
0550     struct si470x_device *radio = video_drvdata(file);
0551 
0552     return radio->fops_release(file);
0553 }
0554 
0555 
0556 /*
0557  * si470x_fops - file operations interface
0558  */
0559 static const struct v4l2_file_operations si470x_fops = {
0560     .owner          = THIS_MODULE,
0561     .read           = si470x_fops_read,
0562     .poll           = si470x_fops_poll,
0563     .unlocked_ioctl     = video_ioctl2,
0564     .open           = si470x_fops_open,
0565     .release        = si470x_fops_release,
0566 };
0567 
0568 
0569 
0570 /**************************************************************************
0571  * Video4Linux Interface
0572  **************************************************************************/
0573 
0574 
0575 static int si470x_s_ctrl(struct v4l2_ctrl *ctrl)
0576 {
0577     struct si470x_device *radio =
0578         container_of(ctrl->handler, struct si470x_device, hdl);
0579 
0580     switch (ctrl->id) {
0581     case V4L2_CID_AUDIO_VOLUME:
0582         radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME;
0583         radio->registers[SYSCONFIG2] |= ctrl->val;
0584         return radio->set_register(radio, SYSCONFIG2);
0585     case V4L2_CID_AUDIO_MUTE:
0586         if (ctrl->val)
0587             radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
0588         else
0589             radio->registers[POWERCFG] |= POWERCFG_DMUTE;
0590         return radio->set_register(radio, POWERCFG);
0591     default:
0592         return -EINVAL;
0593     }
0594 }
0595 
0596 
0597 /*
0598  * si470x_vidioc_g_tuner - get tuner attributes
0599  */
0600 static int si470x_vidioc_g_tuner(struct file *file, void *priv,
0601         struct v4l2_tuner *tuner)
0602 {
0603     struct si470x_device *radio = video_drvdata(file);
0604     int retval = 0;
0605 
0606     if (tuner->index != 0)
0607         return -EINVAL;
0608 
0609     if (!radio->status_rssi_auto_update) {
0610         retval = radio->get_register(radio, STATUSRSSI);
0611         if (retval < 0)
0612             return retval;
0613     }
0614 
0615     /* driver constants */
0616     strscpy(tuner->name, "FM", sizeof(tuner->name));
0617     tuner->type = V4L2_TUNER_RADIO;
0618     tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
0619                 V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
0620                 V4L2_TUNER_CAP_HWSEEK_BOUNDED |
0621                 V4L2_TUNER_CAP_HWSEEK_WRAP;
0622     tuner->rangelow  =  76 * FREQ_MUL;
0623     tuner->rangehigh = 108 * FREQ_MUL;
0624 
0625     /* stereo indicator == stereo (instead of mono) */
0626     if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
0627         tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
0628     else
0629         tuner->rxsubchans = V4L2_TUNER_SUB_STEREO;
0630     /* If there is a reliable method of detecting an RDS channel,
0631        then this code should check for that before setting this
0632        RDS subchannel. */
0633     tuner->rxsubchans |= V4L2_TUNER_SUB_RDS;
0634 
0635     /* mono/stereo selector */
0636     if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0)
0637         tuner->audmode = V4L2_TUNER_MODE_STEREO;
0638     else
0639         tuner->audmode = V4L2_TUNER_MODE_MONO;
0640 
0641     /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */
0642     /* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */
0643     tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI);
0644     /* the ideal factor is 0xffff/75 = 873,8 */
0645     tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10);
0646     if (tuner->signal > 0xffff)
0647         tuner->signal = 0xffff;
0648 
0649     /* automatic frequency control: -1: freq to low, 1 freq to high */
0650     /* AFCRL does only indicate that freq. differs, not if too low/high */
0651     tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0;
0652 
0653     return retval;
0654 }
0655 
0656 
0657 /*
0658  * si470x_vidioc_s_tuner - set tuner attributes
0659  */
0660 static int si470x_vidioc_s_tuner(struct file *file, void *priv,
0661         const struct v4l2_tuner *tuner)
0662 {
0663     struct si470x_device *radio = video_drvdata(file);
0664 
0665     if (tuner->index != 0)
0666         return -EINVAL;
0667 
0668     /* mono/stereo selector */
0669     switch (tuner->audmode) {
0670     case V4L2_TUNER_MODE_MONO:
0671         radio->registers[POWERCFG] |= POWERCFG_MONO;  /* force mono */
0672         break;
0673     case V4L2_TUNER_MODE_STEREO:
0674     default:
0675         radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */
0676         break;
0677     }
0678 
0679     return radio->set_register(radio, POWERCFG);
0680 }
0681 
0682 
0683 /*
0684  * si470x_vidioc_g_frequency - get tuner or modulator radio frequency
0685  */
0686 static int si470x_vidioc_g_frequency(struct file *file, void *priv,
0687         struct v4l2_frequency *freq)
0688 {
0689     struct si470x_device *radio = video_drvdata(file);
0690 
0691     if (freq->tuner != 0)
0692         return -EINVAL;
0693 
0694     freq->type = V4L2_TUNER_RADIO;
0695     return si470x_get_freq(radio, &freq->frequency);
0696 }
0697 
0698 
0699 /*
0700  * si470x_vidioc_s_frequency - set tuner or modulator radio frequency
0701  */
0702 static int si470x_vidioc_s_frequency(struct file *file, void *priv,
0703         const struct v4l2_frequency *freq)
0704 {
0705     struct si470x_device *radio = video_drvdata(file);
0706     int retval;
0707 
0708     if (freq->tuner != 0)
0709         return -EINVAL;
0710 
0711     if (freq->frequency < bands[radio->band].rangelow ||
0712         freq->frequency > bands[radio->band].rangehigh) {
0713         /* Switch to band 1 which covers everything we support */
0714         retval = si470x_set_band(radio, 1);
0715         if (retval)
0716             return retval;
0717     }
0718     return si470x_set_freq(radio, freq->frequency);
0719 }
0720 
0721 
0722 /*
0723  * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek
0724  */
0725 static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
0726         const struct v4l2_hw_freq_seek *seek)
0727 {
0728     struct si470x_device *radio = video_drvdata(file);
0729 
0730     if (seek->tuner != 0)
0731         return -EINVAL;
0732 
0733     if (file->f_flags & O_NONBLOCK)
0734         return -EWOULDBLOCK;
0735 
0736     return si470x_set_seek(radio, seek);
0737 }
0738 
0739 /*
0740  * si470x_vidioc_enum_freq_bands - enumerate supported bands
0741  */
0742 static int si470x_vidioc_enum_freq_bands(struct file *file, void *priv,
0743                      struct v4l2_frequency_band *band)
0744 {
0745     if (band->tuner != 0)
0746         return -EINVAL;
0747     if (band->index >= ARRAY_SIZE(bands))
0748         return -EINVAL;
0749     *band = bands[band->index];
0750     return 0;
0751 }
0752 
0753 const struct v4l2_ctrl_ops si470x_ctrl_ops = {
0754     .s_ctrl = si470x_s_ctrl,
0755 };
0756 EXPORT_SYMBOL_GPL(si470x_ctrl_ops);
0757 
0758 static int si470x_vidioc_querycap(struct file *file, void *priv,
0759         struct v4l2_capability *capability)
0760 {
0761     struct si470x_device *radio = video_drvdata(file);
0762 
0763     return radio->vidioc_querycap(file, priv, capability);
0764 };
0765 
0766 /*
0767  * si470x_ioctl_ops - video device ioctl operations
0768  */
0769 static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
0770     .vidioc_querycap    = si470x_vidioc_querycap,
0771     .vidioc_g_tuner     = si470x_vidioc_g_tuner,
0772     .vidioc_s_tuner     = si470x_vidioc_s_tuner,
0773     .vidioc_g_frequency = si470x_vidioc_g_frequency,
0774     .vidioc_s_frequency = si470x_vidioc_s_frequency,
0775     .vidioc_s_hw_freq_seek  = si470x_vidioc_s_hw_freq_seek,
0776     .vidioc_enum_freq_bands = si470x_vidioc_enum_freq_bands,
0777     .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
0778     .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
0779 };
0780 
0781 
0782 /*
0783  * si470x_viddev_template - video device interface
0784  */
0785 const struct video_device si470x_viddev_template = {
0786     .fops           = &si470x_fops,
0787     .name           = DRIVER_NAME,
0788     .release        = video_device_release_empty,
0789     .ioctl_ops      = &si470x_ioctl_ops,
0790 };
0791 EXPORT_SYMBOL_GPL(si470x_viddev_template);
0792 
0793 MODULE_LICENSE("GPL");