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