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 | |
23 | const 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 | */ |
59 | void 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(ctrl: 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(ctrl: dev->radio_tx_rds_pi); |
86 | } else { |
87 | vivid_rds_gen_fill(rds, freq: dev->radio_rx_freq, use_alternate: alt); |
88 | } |
89 | if (dev->radio_rx_rds_controls) { |
90 | v4l2_ctrl_s_ctrl(ctrl: dev->radio_rx_rds_pty, val: rds->pty); |
91 | v4l2_ctrl_s_ctrl(ctrl: dev->radio_rx_rds_ta, val: rds->ta); |
92 | v4l2_ctrl_s_ctrl(ctrl: dev->radio_rx_rds_tp, val: rds->tp); |
93 | v4l2_ctrl_s_ctrl(ctrl: dev->radio_rx_rds_ms, val: rds->ms); |
94 | v4l2_ctrl_s_ctrl_string(ctrl: dev->radio_rx_rds_psname, s: rds->psname); |
95 | v4l2_ctrl_s_ctrl_string(ctrl: dev->radio_rx_rds_radiotext, s: 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 | */ |
106 | static 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 | |
141 | int 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 | |
149 | int 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 | } |
178 | |