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