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