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