linux/drivers/media/platform/vivid/vivid-radio-rx.c
<<
>>
Prefs
   1/*
   2 * vivid-radio-rx.c - radio receiver support functions.
   3 *
   4 * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
   5 *
   6 * This program is free software; you may redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; version 2 of the License.
   9 *
  10 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  11 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  12 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  13 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  14 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  15 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  16 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  17 * SOFTWARE.
  18 */
  19
  20#include <linux/errno.h>
  21#include <linux/kernel.h>
  22#include <linux/delay.h>
  23#include <linux/videodev2.h>
  24#include <linux/v4l2-dv-timings.h>
  25#include <linux/sched/signal.h>
  26
  27#include <media/v4l2-common.h>
  28#include <media/v4l2-event.h>
  29#include <media/v4l2-dv-timings.h>
  30
  31#include "vivid-core.h"
  32#include "vivid-ctrls.h"
  33#include "vivid-radio-common.h"
  34#include "vivid-rds-gen.h"
  35#include "vivid-radio-rx.h"
  36
  37ssize_t vivid_radio_rx_read(struct file *file, char __user *buf,
  38                         size_t size, loff_t *offset)
  39{
  40        struct vivid_dev *dev = video_drvdata(file);
  41        struct timespec ts;
  42        struct v4l2_rds_data *data = dev->rds_gen.data;
  43        bool use_alternates;
  44        unsigned blk;
  45        int perc;
  46        int i;
  47
  48        if (dev->radio_rx_rds_controls)
  49                return -EINVAL;
  50        if (size < sizeof(*data))
  51                return 0;
  52        size = sizeof(*data) * (size / sizeof(*data));
  53
  54        if (mutex_lock_interruptible(&dev->mutex))
  55                return -ERESTARTSYS;
  56        if (dev->radio_rx_rds_owner &&
  57            file->private_data != dev->radio_rx_rds_owner) {
  58                mutex_unlock(&dev->mutex);
  59                return -EBUSY;
  60        }
  61        if (dev->radio_rx_rds_owner == NULL) {
  62                vivid_radio_rds_init(dev);
  63                dev->radio_rx_rds_owner = file->private_data;
  64        }
  65
  66retry:
  67        ktime_get_ts(&ts);
  68        use_alternates = ts.tv_sec % 10 >= 5;
  69        if (dev->radio_rx_rds_last_block == 0 ||
  70            dev->radio_rx_rds_use_alternates != use_alternates) {
  71                dev->radio_rx_rds_use_alternates = use_alternates;
  72                /* Re-init the RDS generator */
  73                vivid_radio_rds_init(dev);
  74        }
  75        ts = timespec_sub(ts, dev->radio_rds_init_ts);
  76        blk = ts.tv_sec * 100 + ts.tv_nsec / 10000000;
  77        blk = (blk * VIVID_RDS_GEN_BLOCKS) / 500;
  78        if (blk >= dev->radio_rx_rds_last_block + VIVID_RDS_GEN_BLOCKS)
  79                dev->radio_rx_rds_last_block = blk - VIVID_RDS_GEN_BLOCKS + 1;
  80
  81        /*
  82         * No data is available if there hasn't been time to get new data,
  83         * or if the RDS receiver has been disabled, or if we use the data
  84         * from the RDS transmitter and that RDS transmitter has been disabled,
  85         * or if the signal quality is too weak.
  86         */
  87        if (blk == dev->radio_rx_rds_last_block || !dev->radio_rx_rds_enabled ||
  88            (dev->radio_rds_loop && !(dev->radio_tx_subchans & V4L2_TUNER_SUB_RDS)) ||
  89            abs(dev->radio_rx_sig_qual) > 200) {
  90                mutex_unlock(&dev->mutex);
  91                if (file->f_flags & O_NONBLOCK)
  92                        return -EWOULDBLOCK;
  93                if (msleep_interruptible(20) && signal_pending(current))
  94                        return -EINTR;
  95                if (mutex_lock_interruptible(&dev->mutex))
  96                        return -ERESTARTSYS;
  97                goto retry;
  98        }
  99
 100        /* abs(dev->radio_rx_sig_qual) <= 200, map that to a 0-50% range */
 101        perc = abs(dev->radio_rx_sig_qual) / 4;
 102
 103        for (i = 0; i < size && blk > dev->radio_rx_rds_last_block;
 104                        dev->radio_rx_rds_last_block++) {
 105                unsigned data_blk = dev->radio_rx_rds_last_block % VIVID_RDS_GEN_BLOCKS;
 106                struct v4l2_rds_data rds = data[data_blk];
 107
 108                if (data_blk == 0 && dev->radio_rds_loop)
 109                        vivid_radio_rds_init(dev);
 110                if (perc && prandom_u32_max(100) < perc) {
 111                        switch (prandom_u32_max(4)) {
 112                        case 0:
 113                                rds.block |= V4L2_RDS_BLOCK_CORRECTED;
 114                                break;
 115                        case 1:
 116                                rds.block |= V4L2_RDS_BLOCK_INVALID;
 117                                break;
 118                        case 2:
 119                                rds.block |= V4L2_RDS_BLOCK_ERROR;
 120                                rds.lsb = prandom_u32_max(256);
 121                                rds.msb = prandom_u32_max(256);
 122                                break;
 123                        case 3: /* Skip block altogether */
 124                                if (i)
 125                                        continue;
 126                                /*
 127                                 * Must make sure at least one block is
 128                                 * returned, otherwise the application
 129                                 * might think that end-of-file occurred.
 130                                 */
 131                                break;
 132                        }
 133                }
 134                if (copy_to_user(buf + i, &rds, sizeof(rds))) {
 135                        i = -EFAULT;
 136                        break;
 137                }
 138                i += sizeof(rds);
 139        }
 140        mutex_unlock(&dev->mutex);
 141        return i;
 142}
 143
 144unsigned int vivid_radio_rx_poll(struct file *file, struct poll_table_struct *wait)
 145{
 146        return POLLIN | POLLRDNORM | v4l2_ctrl_poll(file, wait);
 147}
 148
 149int vivid_radio_rx_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band)
 150{
 151        if (band->tuner != 0)
 152                return -EINVAL;
 153
 154        if (band->index >= TOT_BANDS)
 155                return -EINVAL;
 156
 157        *band = vivid_radio_bands[band->index];
 158        return 0;
 159}
 160
 161int vivid_radio_rx_s_hw_freq_seek(struct file *file, void *fh, const struct v4l2_hw_freq_seek *a)
 162{
 163        struct vivid_dev *dev = video_drvdata(file);
 164        unsigned low, high;
 165        unsigned freq;
 166        unsigned spacing;
 167        unsigned band;
 168
 169        if (a->tuner)
 170                return -EINVAL;
 171        if (a->wrap_around && dev->radio_rx_hw_seek_mode == VIVID_HW_SEEK_BOUNDED)
 172                return -EINVAL;
 173
 174        if (!a->wrap_around && dev->radio_rx_hw_seek_mode == VIVID_HW_SEEK_WRAP)
 175                return -EINVAL;
 176        if (!a->rangelow ^ !a->rangehigh)
 177                return -EINVAL;
 178
 179        if (file->f_flags & O_NONBLOCK)
 180                return -EWOULDBLOCK;
 181
 182        if (a->rangelow) {
 183                for (band = 0; band < TOT_BANDS; band++)
 184                        if (a->rangelow >= vivid_radio_bands[band].rangelow &&
 185                            a->rangehigh <= vivid_radio_bands[band].rangehigh)
 186                                break;
 187                if (band == TOT_BANDS)
 188                        return -EINVAL;
 189                if (!dev->radio_rx_hw_seek_prog_lim &&
 190                    (a->rangelow != vivid_radio_bands[band].rangelow ||
 191                     a->rangehigh != vivid_radio_bands[band].rangehigh))
 192                        return -EINVAL;
 193                low = a->rangelow;
 194                high = a->rangehigh;
 195        } else {
 196                for (band = 0; band < TOT_BANDS; band++)
 197                        if (dev->radio_rx_freq >= vivid_radio_bands[band].rangelow &&
 198                            dev->radio_rx_freq <= vivid_radio_bands[band].rangehigh)
 199                                break;
 200                if (band == TOT_BANDS)
 201                        return -EINVAL;
 202                low = vivid_radio_bands[band].rangelow;
 203                high = vivid_radio_bands[band].rangehigh;
 204        }
 205        spacing = band == BAND_AM ? 1600 : 16000;
 206        freq = clamp(dev->radio_rx_freq, low, high);
 207
 208        if (a->seek_upward) {
 209                freq = spacing * (freq / spacing) + spacing;
 210                if (freq > high) {
 211                        if (!a->wrap_around)
 212                                return -ENODATA;
 213                        freq = spacing * (low / spacing) + spacing;
 214                        if (freq >= dev->radio_rx_freq)
 215                                return -ENODATA;
 216                }
 217        } else {
 218                freq = spacing * ((freq + spacing - 1) / spacing) - spacing;
 219                if (freq < low) {
 220                        if (!a->wrap_around)
 221                                return -ENODATA;
 222                        freq = spacing * ((high + spacing - 1) / spacing) - spacing;
 223                        if (freq <= dev->radio_rx_freq)
 224                                return -ENODATA;
 225                }
 226        }
 227        return 0;
 228}
 229
 230int vivid_radio_rx_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
 231{
 232        struct vivid_dev *dev = video_drvdata(file);
 233        int delta = 800;
 234        int sig_qual;
 235
 236        if (vt->index > 0)
 237                return -EINVAL;
 238
 239        strlcpy(vt->name, "AM/FM/SW Receiver", sizeof(vt->name));
 240        vt->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
 241                         V4L2_TUNER_CAP_FREQ_BANDS | V4L2_TUNER_CAP_RDS |
 242                         (dev->radio_rx_rds_controls ?
 243                                V4L2_TUNER_CAP_RDS_CONTROLS :
 244                                V4L2_TUNER_CAP_RDS_BLOCK_IO) |
 245                         (dev->radio_rx_hw_seek_prog_lim ?
 246                                V4L2_TUNER_CAP_HWSEEK_PROG_LIM : 0);
 247        switch (dev->radio_rx_hw_seek_mode) {
 248        case VIVID_HW_SEEK_BOUNDED:
 249                vt->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED;
 250                break;
 251        case VIVID_HW_SEEK_WRAP:
 252                vt->capability |= V4L2_TUNER_CAP_HWSEEK_WRAP;
 253                break;
 254        case VIVID_HW_SEEK_BOTH:
 255                vt->capability |= V4L2_TUNER_CAP_HWSEEK_WRAP |
 256                                  V4L2_TUNER_CAP_HWSEEK_BOUNDED;
 257                break;
 258        }
 259        vt->rangelow = AM_FREQ_RANGE_LOW;
 260        vt->rangehigh = FM_FREQ_RANGE_HIGH;
 261        sig_qual = dev->radio_rx_sig_qual;
 262        vt->signal = abs(sig_qual) > delta ? 0 :
 263                     0xffff - (abs(sig_qual) * 0xffff) / delta;
 264        vt->afc = sig_qual > delta ? 0 : sig_qual;
 265        if (abs(sig_qual) > delta)
 266                vt->rxsubchans = 0;
 267        else if (dev->radio_rx_freq < FM_FREQ_RANGE_LOW || vt->signal < 0x8000)
 268                vt->rxsubchans = V4L2_TUNER_SUB_MONO;
 269        else if (dev->radio_rds_loop && !(dev->radio_tx_subchans & V4L2_TUNER_SUB_STEREO))
 270                vt->rxsubchans = V4L2_TUNER_SUB_MONO;
 271        else
 272                vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
 273        if (dev->radio_rx_rds_enabled &&
 274            (!dev->radio_rds_loop || (dev->radio_tx_subchans & V4L2_TUNER_SUB_RDS)) &&
 275            dev->radio_rx_freq >= FM_FREQ_RANGE_LOW && vt->signal >= 0xc000)
 276                vt->rxsubchans |= V4L2_TUNER_SUB_RDS;
 277        if (dev->radio_rx_rds_controls)
 278                vivid_radio_rds_init(dev);
 279        vt->audmode = dev->radio_rx_audmode;
 280        return 0;
 281}
 282
 283int vivid_radio_rx_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt)
 284{
 285        struct vivid_dev *dev = video_drvdata(file);
 286
 287        if (vt->index)
 288                return -EINVAL;
 289        dev->radio_rx_audmode = vt->audmode >= V4L2_TUNER_MODE_STEREO;
 290        return 0;
 291}
 292