123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- /*
- * 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;
- }
|