vivid-radio-common.c (5196B)
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}