linux/drivers/media/platform/vivid/vivid-radio-common.c
<<
>>
Prefs
   1/*
   2 * vivid-radio-common.c - common radio rx/tx 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
  25#include "vivid-core.h"
  26#include "vivid-ctrls.h"
  27#include "vivid-radio-common.h"
  28#include "vivid-rds-gen.h"
  29
  30/*
  31 * These functions are shared between the vivid receiver and transmitter
  32 * since both use the same frequency bands.
  33 */
  34
  35const struct v4l2_frequency_band vivid_radio_bands[TOT_BANDS] = {
  36        /* Band FM */
  37        {
  38                .type = V4L2_TUNER_RADIO,
  39                .index = 0,
  40                .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
  41                              V4L2_TUNER_CAP_FREQ_BANDS,
  42                .rangelow   = FM_FREQ_RANGE_LOW,
  43                .rangehigh  = FM_FREQ_RANGE_HIGH,
  44                .modulation = V4L2_BAND_MODULATION_FM,
  45        },
  46        /* Band AM */
  47        {
  48                .type = V4L2_TUNER_RADIO,
  49                .index = 1,
  50                .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
  51                .rangelow   = AM_FREQ_RANGE_LOW,
  52                .rangehigh  = AM_FREQ_RANGE_HIGH,
  53                .modulation = V4L2_BAND_MODULATION_AM,
  54        },
  55        /* Band SW */
  56        {
  57                .type = V4L2_TUNER_RADIO,
  58                .index = 2,
  59                .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
  60                .rangelow   = SW_FREQ_RANGE_LOW,
  61                .rangehigh  = SW_FREQ_RANGE_HIGH,
  62                .modulation = V4L2_BAND_MODULATION_AM,
  63        },
  64};
  65
  66/*
  67 * Initialize the RDS generator. If we can loop, then the RDS generator
  68 * is set up with the values from the RDS TX controls, otherwise it
  69 * will fill in standard values using one of two alternates.
  70 */
  71void vivid_radio_rds_init(struct vivid_dev *dev)
  72{
  73        struct vivid_rds_gen *rds = &dev->rds_gen;
  74        bool alt = dev->radio_rx_rds_use_alternates;
  75
  76        /* Do nothing, blocks will be filled by the transmitter */
  77        if (dev->radio_rds_loop && !dev->radio_tx_rds_controls)
  78                return;
  79
  80        if (dev->radio_rds_loop) {
  81                v4l2_ctrl_lock(dev->radio_tx_rds_pi);
  82                rds->picode = dev->radio_tx_rds_pi->cur.val;
  83                rds->pty = dev->radio_tx_rds_pty->cur.val;
  84                rds->mono_stereo = dev->radio_tx_rds_mono_stereo->cur.val;
  85                rds->art_head = dev->radio_tx_rds_art_head->cur.val;
  86                rds->compressed = dev->radio_tx_rds_compressed->cur.val;
  87                rds->dyn_pty = dev->radio_tx_rds_dyn_pty->cur.val;
  88                rds->ta = dev->radio_tx_rds_ta->cur.val;
  89                rds->tp = dev->radio_tx_rds_tp->cur.val;
  90                rds->ms = dev->radio_tx_rds_ms->cur.val;
  91                strlcpy(rds->psname,
  92                        dev->radio_tx_rds_psname->p_cur.p_char,
  93                        sizeof(rds->psname));
  94                strlcpy(rds->radiotext,
  95                        dev->radio_tx_rds_radiotext->p_cur.p_char + alt * 64,
  96                        sizeof(rds->radiotext));
  97                v4l2_ctrl_unlock(dev->radio_tx_rds_pi);
  98        } else {
  99                vivid_rds_gen_fill(rds, dev->radio_rx_freq, alt);
 100        }
 101        if (dev->radio_rx_rds_controls) {
 102                v4l2_ctrl_s_ctrl(dev->radio_rx_rds_pty, rds->pty);
 103                v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ta, rds->ta);
 104                v4l2_ctrl_s_ctrl(dev->radio_rx_rds_tp, rds->tp);
 105                v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ms, rds->ms);
 106                v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_psname, rds->psname);
 107                v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_radiotext, rds->radiotext);
 108                if (!dev->radio_rds_loop)
 109                        dev->radio_rx_rds_use_alternates = !dev->radio_rx_rds_use_alternates;
 110        }
 111        vivid_rds_generate(rds);
 112}
 113
 114/*
 115 * Calculate the emulated signal quality taking into account the frequency
 116 * the transmitter is using.
 117 */
 118static void vivid_radio_calc_sig_qual(struct vivid_dev *dev)
 119{
 120        int mod = 16000;
 121        int delta = 800;
 122        int sig_qual, sig_qual_tx = mod;
 123
 124        /*
 125         * For SW and FM there is a channel every 1000 kHz, for AM there is one
 126         * every 100 kHz.
 127         */
 128        if (dev->radio_rx_freq <= AM_FREQ_RANGE_HIGH) {
 129                mod /= 10;
 130                delta /= 10;
 131        }
 132        sig_qual = (dev->radio_rx_freq + delta) % mod - delta;
 133        if (dev->has_radio_tx)
 134                sig_qual_tx = dev->radio_rx_freq - dev->radio_tx_freq;
 135        if (abs(sig_qual_tx) <= abs(sig_qual)) {
 136                sig_qual = sig_qual_tx;
 137                /*
 138                 * Zero the internal rds buffer if we are going to loop
 139                 * rds blocks.
 140                 */
 141                if (!dev->radio_rds_loop && !dev->radio_tx_rds_controls)
 142                        memset(dev->rds_gen.data, 0,
 143                               sizeof(dev->rds_gen.data));
 144                dev->radio_rds_loop = dev->radio_rx_freq >= FM_FREQ_RANGE_LOW;
 145        } else {
 146                dev->radio_rds_loop = false;
 147        }
 148        if (dev->radio_rx_freq <= AM_FREQ_RANGE_HIGH)
 149                sig_qual *= 10;
 150        dev->radio_rx_sig_qual = sig_qual;
 151}
 152
 153int vivid_radio_g_frequency(struct file *file, const unsigned *pfreq, struct v4l2_frequency *vf)
 154{
 155        if (vf->tuner != 0)
 156                return -EINVAL;
 157        vf->frequency = *pfreq;
 158        return 0;
 159}
 160
 161int vivid_radio_s_frequency(struct file *file, unsigned *pfreq, const struct v4l2_frequency *vf)
 162{
 163        struct vivid_dev *dev = video_drvdata(file);
 164        unsigned freq;
 165        unsigned band;
 166
 167        if (vf->tuner != 0)
 168                return -EINVAL;
 169
 170        if (vf->frequency >= (FM_FREQ_RANGE_LOW + SW_FREQ_RANGE_HIGH) / 2)
 171                band = BAND_FM;
 172        else if (vf->frequency <= (AM_FREQ_RANGE_HIGH + SW_FREQ_RANGE_LOW) / 2)
 173                band = BAND_AM;
 174        else
 175                band = BAND_SW;
 176
 177        freq = clamp_t(u32, vf->frequency, vivid_radio_bands[band].rangelow,
 178                                           vivid_radio_bands[band].rangehigh);
 179        *pfreq = freq;
 180
 181        /*
 182         * For both receiver and transmitter recalculate the signal quality
 183         * (since that depends on both frequencies) and re-init the rds
 184         * generator.
 185         */
 186        vivid_radio_calc_sig_qual(dev);
 187        vivid_radio_rds_init(dev);
 188        return 0;
 189}
 190