Hans Verkuil | 55d58e9 | 2014-08-25 08:02:56 -0300 | [diff] [blame] | 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 | |
| 35 | const 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 | */ |
| 71 | void 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 | */ |
| 118 | static 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 | |
| 153 | int 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 | |
| 161 | int 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 | } |