1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * vivid-radio-rx.c - radio receiver 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 | #include <linux/v4l2-dv-timings.h> |
13 | #include <linux/sched/signal.h> |
14 | |
15 | #include <media/v4l2-common.h> |
16 | #include <media/v4l2-event.h> |
17 | #include <media/v4l2-dv-timings.h> |
18 | |
19 | #include "vivid-core.h" |
20 | #include "vivid-ctrls.h" |
21 | #include "vivid-radio-common.h" |
22 | #include "vivid-rds-gen.h" |
23 | #include "vivid-radio-rx.h" |
24 | |
25 | ssize_t vivid_radio_rx_read(struct file *file, char __user *buf, |
26 | size_t size, loff_t *offset) |
27 | { |
28 | struct vivid_dev *dev = video_drvdata(file); |
29 | struct v4l2_rds_data *data = dev->rds_gen.data; |
30 | bool use_alternates; |
31 | ktime_t timestamp; |
32 | unsigned blk; |
33 | int perc; |
34 | int i; |
35 | |
36 | if (dev->radio_rx_rds_controls) |
37 | return -EINVAL; |
38 | if (size < sizeof(*data)) |
39 | return 0; |
40 | size = sizeof(*data) * (size / sizeof(*data)); |
41 | |
42 | if (mutex_lock_interruptible(&dev->mutex)) |
43 | return -ERESTARTSYS; |
44 | if (dev->radio_rx_rds_owner && |
45 | file->private_data != dev->radio_rx_rds_owner) { |
46 | mutex_unlock(lock: &dev->mutex); |
47 | return -EBUSY; |
48 | } |
49 | if (dev->radio_rx_rds_owner == NULL) { |
50 | vivid_radio_rds_init(dev); |
51 | dev->radio_rx_rds_owner = file->private_data; |
52 | } |
53 | |
54 | retry: |
55 | timestamp = ktime_sub(ktime_get(), dev->radio_rds_init_time); |
56 | blk = ktime_divns(kt: timestamp, VIVID_RDS_NSEC_PER_BLK); |
57 | use_alternates = (blk % VIVID_RDS_GEN_BLOCKS) & 1; |
58 | |
59 | if (dev->radio_rx_rds_last_block == 0 || |
60 | dev->radio_rx_rds_use_alternates != use_alternates) { |
61 | dev->radio_rx_rds_use_alternates = use_alternates; |
62 | /* Re-init the RDS generator */ |
63 | vivid_radio_rds_init(dev); |
64 | } |
65 | if (blk >= dev->radio_rx_rds_last_block + VIVID_RDS_GEN_BLOCKS) |
66 | dev->radio_rx_rds_last_block = blk - VIVID_RDS_GEN_BLOCKS + 1; |
67 | |
68 | /* |
69 | * No data is available if there hasn't been time to get new data, |
70 | * or if the RDS receiver has been disabled, or if we use the data |
71 | * from the RDS transmitter and that RDS transmitter has been disabled, |
72 | * or if the signal quality is too weak. |
73 | */ |
74 | if (blk == dev->radio_rx_rds_last_block || !dev->radio_rx_rds_enabled || |
75 | (dev->radio_rds_loop && !(dev->radio_tx_subchans & V4L2_TUNER_SUB_RDS)) || |
76 | abs(dev->radio_rx_sig_qual) > 200) { |
77 | mutex_unlock(lock: &dev->mutex); |
78 | if (file->f_flags & O_NONBLOCK) |
79 | return -EWOULDBLOCK; |
80 | if (msleep_interruptible(msecs: 20) && signal_pending(current)) |
81 | return -EINTR; |
82 | if (mutex_lock_interruptible(&dev->mutex)) |
83 | return -ERESTARTSYS; |
84 | goto retry; |
85 | } |
86 | |
87 | /* abs(dev->radio_rx_sig_qual) <= 200, map that to a 0-50% range */ |
88 | perc = abs(dev->radio_rx_sig_qual) / 4; |
89 | |
90 | for (i = 0; i < size && blk > dev->radio_rx_rds_last_block; |
91 | dev->radio_rx_rds_last_block++) { |
92 | unsigned data_blk = dev->radio_rx_rds_last_block % VIVID_RDS_GEN_BLOCKS; |
93 | struct v4l2_rds_data rds = data[data_blk]; |
94 | |
95 | if (data_blk == 0 && dev->radio_rds_loop) |
96 | vivid_radio_rds_init(dev); |
97 | if (perc && get_random_u32_below(ceil: 100) < perc) { |
98 | switch (get_random_u32_below(ceil: 4)) { |
99 | case 0: |
100 | rds.block |= V4L2_RDS_BLOCK_CORRECTED; |
101 | break; |
102 | case 1: |
103 | rds.block |= V4L2_RDS_BLOCK_INVALID; |
104 | break; |
105 | case 2: |
106 | rds.block |= V4L2_RDS_BLOCK_ERROR; |
107 | rds.lsb = get_random_u8(); |
108 | rds.msb = get_random_u8(); |
109 | break; |
110 | case 3: /* Skip block altogether */ |
111 | if (i) |
112 | continue; |
113 | /* |
114 | * Must make sure at least one block is |
115 | * returned, otherwise the application |
116 | * might think that end-of-file occurred. |
117 | */ |
118 | break; |
119 | } |
120 | } |
121 | if (copy_to_user(to: buf + i, from: &rds, n: sizeof(rds))) { |
122 | i = -EFAULT; |
123 | break; |
124 | } |
125 | i += sizeof(rds); |
126 | } |
127 | mutex_unlock(lock: &dev->mutex); |
128 | return i; |
129 | } |
130 | |
131 | __poll_t vivid_radio_rx_poll(struct file *file, struct poll_table_struct *wait) |
132 | { |
133 | return EPOLLIN | EPOLLRDNORM | v4l2_ctrl_poll(file, wait); |
134 | } |
135 | |
136 | int vivid_radio_rx_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band) |
137 | { |
138 | if (band->tuner != 0) |
139 | return -EINVAL; |
140 | |
141 | if (band->index >= TOT_BANDS) |
142 | return -EINVAL; |
143 | |
144 | *band = vivid_radio_bands[band->index]; |
145 | return 0; |
146 | } |
147 | |
148 | int vivid_radio_rx_s_hw_freq_seek(struct file *file, void *fh, const struct v4l2_hw_freq_seek *a) |
149 | { |
150 | struct vivid_dev *dev = video_drvdata(file); |
151 | unsigned low, high; |
152 | unsigned freq; |
153 | unsigned spacing; |
154 | unsigned band; |
155 | |
156 | if (a->tuner) |
157 | return -EINVAL; |
158 | if (a->wrap_around && dev->radio_rx_hw_seek_mode == VIVID_HW_SEEK_BOUNDED) |
159 | return -EINVAL; |
160 | |
161 | if (!a->wrap_around && dev->radio_rx_hw_seek_mode == VIVID_HW_SEEK_WRAP) |
162 | return -EINVAL; |
163 | if (!a->rangelow ^ !a->rangehigh) |
164 | return -EINVAL; |
165 | |
166 | if (file->f_flags & O_NONBLOCK) |
167 | return -EWOULDBLOCK; |
168 | |
169 | if (a->rangelow) { |
170 | for (band = 0; band < TOT_BANDS; band++) |
171 | if (a->rangelow >= vivid_radio_bands[band].rangelow && |
172 | a->rangehigh <= vivid_radio_bands[band].rangehigh) |
173 | break; |
174 | if (band == TOT_BANDS) |
175 | return -EINVAL; |
176 | if (!dev->radio_rx_hw_seek_prog_lim && |
177 | (a->rangelow != vivid_radio_bands[band].rangelow || |
178 | a->rangehigh != vivid_radio_bands[band].rangehigh)) |
179 | return -EINVAL; |
180 | low = a->rangelow; |
181 | high = a->rangehigh; |
182 | } else { |
183 | for (band = 0; band < TOT_BANDS; band++) |
184 | if (dev->radio_rx_freq >= vivid_radio_bands[band].rangelow && |
185 | dev->radio_rx_freq <= vivid_radio_bands[band].rangehigh) |
186 | break; |
187 | if (band == TOT_BANDS) |
188 | return -EINVAL; |
189 | low = vivid_radio_bands[band].rangelow; |
190 | high = vivid_radio_bands[band].rangehigh; |
191 | } |
192 | spacing = band == BAND_AM ? 1600 : 16000; |
193 | freq = clamp(dev->radio_rx_freq, low, high); |
194 | |
195 | if (a->seek_upward) { |
196 | freq = spacing * (freq / spacing) + spacing; |
197 | if (freq > high) { |
198 | if (!a->wrap_around) |
199 | return -ENODATA; |
200 | freq = spacing * (low / spacing) + spacing; |
201 | if (freq >= dev->radio_rx_freq) |
202 | return -ENODATA; |
203 | } |
204 | } else { |
205 | freq = spacing * ((freq + spacing - 1) / spacing) - spacing; |
206 | if (freq < low) { |
207 | if (!a->wrap_around) |
208 | return -ENODATA; |
209 | freq = spacing * ((high + spacing - 1) / spacing) - spacing; |
210 | if (freq <= dev->radio_rx_freq) |
211 | return -ENODATA; |
212 | } |
213 | } |
214 | return 0; |
215 | } |
216 | |
217 | int vivid_radio_rx_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) |
218 | { |
219 | struct vivid_dev *dev = video_drvdata(file); |
220 | int delta = 800; |
221 | int sig_qual; |
222 | |
223 | if (vt->index > 0) |
224 | return -EINVAL; |
225 | |
226 | strscpy(vt->name, "AM/FM/SW Receiver" , sizeof(vt->name)); |
227 | vt->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | |
228 | V4L2_TUNER_CAP_FREQ_BANDS | V4L2_TUNER_CAP_RDS | |
229 | (dev->radio_rx_rds_controls ? |
230 | V4L2_TUNER_CAP_RDS_CONTROLS : |
231 | V4L2_TUNER_CAP_RDS_BLOCK_IO) | |
232 | (dev->radio_rx_hw_seek_prog_lim ? |
233 | V4L2_TUNER_CAP_HWSEEK_PROG_LIM : 0); |
234 | switch (dev->radio_rx_hw_seek_mode) { |
235 | case VIVID_HW_SEEK_BOUNDED: |
236 | vt->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED; |
237 | break; |
238 | case VIVID_HW_SEEK_WRAP: |
239 | vt->capability |= V4L2_TUNER_CAP_HWSEEK_WRAP; |
240 | break; |
241 | case VIVID_HW_SEEK_BOTH: |
242 | vt->capability |= V4L2_TUNER_CAP_HWSEEK_WRAP | |
243 | V4L2_TUNER_CAP_HWSEEK_BOUNDED; |
244 | break; |
245 | } |
246 | vt->rangelow = AM_FREQ_RANGE_LOW; |
247 | vt->rangehigh = FM_FREQ_RANGE_HIGH; |
248 | sig_qual = dev->radio_rx_sig_qual; |
249 | vt->signal = abs(sig_qual) > delta ? 0 : |
250 | 0xffff - ((unsigned)abs(sig_qual) * 0xffff) / delta; |
251 | vt->afc = sig_qual > delta ? 0 : sig_qual; |
252 | if (abs(sig_qual) > delta) |
253 | vt->rxsubchans = 0; |
254 | else if (dev->radio_rx_freq < FM_FREQ_RANGE_LOW || vt->signal < 0x8000) |
255 | vt->rxsubchans = V4L2_TUNER_SUB_MONO; |
256 | else if (dev->radio_rds_loop && !(dev->radio_tx_subchans & V4L2_TUNER_SUB_STEREO)) |
257 | vt->rxsubchans = V4L2_TUNER_SUB_MONO; |
258 | else |
259 | vt->rxsubchans = V4L2_TUNER_SUB_STEREO; |
260 | if (dev->radio_rx_rds_enabled && |
261 | (!dev->radio_rds_loop || (dev->radio_tx_subchans & V4L2_TUNER_SUB_RDS)) && |
262 | dev->radio_rx_freq >= FM_FREQ_RANGE_LOW && vt->signal >= 0xc000) |
263 | vt->rxsubchans |= V4L2_TUNER_SUB_RDS; |
264 | if (dev->radio_rx_rds_controls) |
265 | vivid_radio_rds_init(dev); |
266 | vt->audmode = dev->radio_rx_audmode; |
267 | return 0; |
268 | } |
269 | |
270 | int vivid_radio_rx_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt) |
271 | { |
272 | struct vivid_dev *dev = video_drvdata(file); |
273 | |
274 | if (vt->index) |
275 | return -EINVAL; |
276 | dev->radio_rx_audmode = vt->audmode >= V4L2_TUNER_MODE_STEREO; |
277 | return 0; |
278 | } |
279 | |