xref: /OK3568_Linux_fs/kernel/drivers/media/test-drivers/vivid/vivid-radio-common.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * vivid-radio-common.c - common radio rx/tx support functions.
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <linux/errno.h>
9*4882a593Smuzhiyun #include <linux/kernel.h>
10*4882a593Smuzhiyun #include <linux/delay.h>
11*4882a593Smuzhiyun #include <linux/videodev2.h>
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #include "vivid-core.h"
14*4882a593Smuzhiyun #include "vivid-ctrls.h"
15*4882a593Smuzhiyun #include "vivid-radio-common.h"
16*4882a593Smuzhiyun #include "vivid-rds-gen.h"
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun /*
19*4882a593Smuzhiyun  * These functions are shared between the vivid receiver and transmitter
20*4882a593Smuzhiyun  * since both use the same frequency bands.
21*4882a593Smuzhiyun  */
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun const struct v4l2_frequency_band vivid_radio_bands[TOT_BANDS] = {
24*4882a593Smuzhiyun 	/* Band FM */
25*4882a593Smuzhiyun 	{
26*4882a593Smuzhiyun 		.type = V4L2_TUNER_RADIO,
27*4882a593Smuzhiyun 		.index = 0,
28*4882a593Smuzhiyun 		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
29*4882a593Smuzhiyun 			      V4L2_TUNER_CAP_FREQ_BANDS,
30*4882a593Smuzhiyun 		.rangelow   = FM_FREQ_RANGE_LOW,
31*4882a593Smuzhiyun 		.rangehigh  = FM_FREQ_RANGE_HIGH,
32*4882a593Smuzhiyun 		.modulation = V4L2_BAND_MODULATION_FM,
33*4882a593Smuzhiyun 	},
34*4882a593Smuzhiyun 	/* Band AM */
35*4882a593Smuzhiyun 	{
36*4882a593Smuzhiyun 		.type = V4L2_TUNER_RADIO,
37*4882a593Smuzhiyun 		.index = 1,
38*4882a593Smuzhiyun 		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
39*4882a593Smuzhiyun 		.rangelow   = AM_FREQ_RANGE_LOW,
40*4882a593Smuzhiyun 		.rangehigh  = AM_FREQ_RANGE_HIGH,
41*4882a593Smuzhiyun 		.modulation = V4L2_BAND_MODULATION_AM,
42*4882a593Smuzhiyun 	},
43*4882a593Smuzhiyun 	/* Band SW */
44*4882a593Smuzhiyun 	{
45*4882a593Smuzhiyun 		.type = V4L2_TUNER_RADIO,
46*4882a593Smuzhiyun 		.index = 2,
47*4882a593Smuzhiyun 		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
48*4882a593Smuzhiyun 		.rangelow   = SW_FREQ_RANGE_LOW,
49*4882a593Smuzhiyun 		.rangehigh  = SW_FREQ_RANGE_HIGH,
50*4882a593Smuzhiyun 		.modulation = V4L2_BAND_MODULATION_AM,
51*4882a593Smuzhiyun 	},
52*4882a593Smuzhiyun };
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun /*
55*4882a593Smuzhiyun  * Initialize the RDS generator. If we can loop, then the RDS generator
56*4882a593Smuzhiyun  * is set up with the values from the RDS TX controls, otherwise it
57*4882a593Smuzhiyun  * will fill in standard values using one of two alternates.
58*4882a593Smuzhiyun  */
vivid_radio_rds_init(struct vivid_dev * dev)59*4882a593Smuzhiyun void vivid_radio_rds_init(struct vivid_dev *dev)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun 	struct vivid_rds_gen *rds = &dev->rds_gen;
62*4882a593Smuzhiyun 	bool alt = dev->radio_rx_rds_use_alternates;
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	/* Do nothing, blocks will be filled by the transmitter */
65*4882a593Smuzhiyun 	if (dev->radio_rds_loop && !dev->radio_tx_rds_controls)
66*4882a593Smuzhiyun 		return;
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	if (dev->radio_rds_loop) {
69*4882a593Smuzhiyun 		v4l2_ctrl_lock(dev->radio_tx_rds_pi);
70*4882a593Smuzhiyun 		rds->picode = dev->radio_tx_rds_pi->cur.val;
71*4882a593Smuzhiyun 		rds->pty = dev->radio_tx_rds_pty->cur.val;
72*4882a593Smuzhiyun 		rds->mono_stereo = dev->radio_tx_rds_mono_stereo->cur.val;
73*4882a593Smuzhiyun 		rds->art_head = dev->radio_tx_rds_art_head->cur.val;
74*4882a593Smuzhiyun 		rds->compressed = dev->radio_tx_rds_compressed->cur.val;
75*4882a593Smuzhiyun 		rds->dyn_pty = dev->radio_tx_rds_dyn_pty->cur.val;
76*4882a593Smuzhiyun 		rds->ta = dev->radio_tx_rds_ta->cur.val;
77*4882a593Smuzhiyun 		rds->tp = dev->radio_tx_rds_tp->cur.val;
78*4882a593Smuzhiyun 		rds->ms = dev->radio_tx_rds_ms->cur.val;
79*4882a593Smuzhiyun 		strscpy(rds->psname,
80*4882a593Smuzhiyun 			dev->radio_tx_rds_psname->p_cur.p_char,
81*4882a593Smuzhiyun 			sizeof(rds->psname));
82*4882a593Smuzhiyun 		strscpy(rds->radiotext,
83*4882a593Smuzhiyun 			dev->radio_tx_rds_radiotext->p_cur.p_char + alt * 64,
84*4882a593Smuzhiyun 			sizeof(rds->radiotext));
85*4882a593Smuzhiyun 		v4l2_ctrl_unlock(dev->radio_tx_rds_pi);
86*4882a593Smuzhiyun 	} else {
87*4882a593Smuzhiyun 		vivid_rds_gen_fill(rds, dev->radio_rx_freq, alt);
88*4882a593Smuzhiyun 	}
89*4882a593Smuzhiyun 	if (dev->radio_rx_rds_controls) {
90*4882a593Smuzhiyun 		v4l2_ctrl_s_ctrl(dev->radio_rx_rds_pty, rds->pty);
91*4882a593Smuzhiyun 		v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ta, rds->ta);
92*4882a593Smuzhiyun 		v4l2_ctrl_s_ctrl(dev->radio_rx_rds_tp, rds->tp);
93*4882a593Smuzhiyun 		v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ms, rds->ms);
94*4882a593Smuzhiyun 		v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_psname, rds->psname);
95*4882a593Smuzhiyun 		v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_radiotext, rds->radiotext);
96*4882a593Smuzhiyun 		if (!dev->radio_rds_loop)
97*4882a593Smuzhiyun 			dev->radio_rx_rds_use_alternates = !dev->radio_rx_rds_use_alternates;
98*4882a593Smuzhiyun 	}
99*4882a593Smuzhiyun 	vivid_rds_generate(rds);
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun /*
103*4882a593Smuzhiyun  * Calculate the emulated signal quality taking into account the frequency
104*4882a593Smuzhiyun  * the transmitter is using.
105*4882a593Smuzhiyun  */
vivid_radio_calc_sig_qual(struct vivid_dev * dev)106*4882a593Smuzhiyun static void vivid_radio_calc_sig_qual(struct vivid_dev *dev)
107*4882a593Smuzhiyun {
108*4882a593Smuzhiyun 	int mod = 16000;
109*4882a593Smuzhiyun 	int delta = 800;
110*4882a593Smuzhiyun 	int sig_qual, sig_qual_tx = mod;
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	/*
113*4882a593Smuzhiyun 	 * For SW and FM there is a channel every 1000 kHz, for AM there is one
114*4882a593Smuzhiyun 	 * every 100 kHz.
115*4882a593Smuzhiyun 	 */
116*4882a593Smuzhiyun 	if (dev->radio_rx_freq <= AM_FREQ_RANGE_HIGH) {
117*4882a593Smuzhiyun 		mod /= 10;
118*4882a593Smuzhiyun 		delta /= 10;
119*4882a593Smuzhiyun 	}
120*4882a593Smuzhiyun 	sig_qual = (dev->radio_rx_freq + delta) % mod - delta;
121*4882a593Smuzhiyun 	if (dev->has_radio_tx)
122*4882a593Smuzhiyun 		sig_qual_tx = dev->radio_rx_freq - dev->radio_tx_freq;
123*4882a593Smuzhiyun 	if (abs(sig_qual_tx) <= abs(sig_qual)) {
124*4882a593Smuzhiyun 		sig_qual = sig_qual_tx;
125*4882a593Smuzhiyun 		/*
126*4882a593Smuzhiyun 		 * Zero the internal rds buffer if we are going to loop
127*4882a593Smuzhiyun 		 * rds blocks.
128*4882a593Smuzhiyun 		 */
129*4882a593Smuzhiyun 		if (!dev->radio_rds_loop && !dev->radio_tx_rds_controls)
130*4882a593Smuzhiyun 			memset(dev->rds_gen.data, 0,
131*4882a593Smuzhiyun 			       sizeof(dev->rds_gen.data));
132*4882a593Smuzhiyun 		dev->radio_rds_loop = dev->radio_rx_freq >= FM_FREQ_RANGE_LOW;
133*4882a593Smuzhiyun 	} else {
134*4882a593Smuzhiyun 		dev->radio_rds_loop = false;
135*4882a593Smuzhiyun 	}
136*4882a593Smuzhiyun 	if (dev->radio_rx_freq <= AM_FREQ_RANGE_HIGH)
137*4882a593Smuzhiyun 		sig_qual *= 10;
138*4882a593Smuzhiyun 	dev->radio_rx_sig_qual = sig_qual;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun 
vivid_radio_g_frequency(struct file * file,const unsigned * pfreq,struct v4l2_frequency * vf)141*4882a593Smuzhiyun int vivid_radio_g_frequency(struct file *file, const unsigned *pfreq, struct v4l2_frequency *vf)
142*4882a593Smuzhiyun {
143*4882a593Smuzhiyun 	if (vf->tuner != 0)
144*4882a593Smuzhiyun 		return -EINVAL;
145*4882a593Smuzhiyun 	vf->frequency = *pfreq;
146*4882a593Smuzhiyun 	return 0;
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun 
vivid_radio_s_frequency(struct file * file,unsigned * pfreq,const struct v4l2_frequency * vf)149*4882a593Smuzhiyun int vivid_radio_s_frequency(struct file *file, unsigned *pfreq, const struct v4l2_frequency *vf)
150*4882a593Smuzhiyun {
151*4882a593Smuzhiyun 	struct vivid_dev *dev = video_drvdata(file);
152*4882a593Smuzhiyun 	unsigned freq;
153*4882a593Smuzhiyun 	unsigned band;
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	if (vf->tuner != 0)
156*4882a593Smuzhiyun 		return -EINVAL;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	if (vf->frequency >= (FM_FREQ_RANGE_LOW + SW_FREQ_RANGE_HIGH) / 2)
159*4882a593Smuzhiyun 		band = BAND_FM;
160*4882a593Smuzhiyun 	else if (vf->frequency <= (AM_FREQ_RANGE_HIGH + SW_FREQ_RANGE_LOW) / 2)
161*4882a593Smuzhiyun 		band = BAND_AM;
162*4882a593Smuzhiyun 	else
163*4882a593Smuzhiyun 		band = BAND_SW;
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	freq = clamp_t(u32, vf->frequency, vivid_radio_bands[band].rangelow,
166*4882a593Smuzhiyun 					   vivid_radio_bands[band].rangehigh);
167*4882a593Smuzhiyun 	*pfreq = freq;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	/*
170*4882a593Smuzhiyun 	 * For both receiver and transmitter recalculate the signal quality
171*4882a593Smuzhiyun 	 * (since that depends on both frequencies) and re-init the rds
172*4882a593Smuzhiyun 	 * generator.
173*4882a593Smuzhiyun 	 */
174*4882a593Smuzhiyun 	vivid_radio_calc_sig_qual(dev);
175*4882a593Smuzhiyun 	vivid_radio_rds_init(dev);
176*4882a593Smuzhiyun 	return 0;
177*4882a593Smuzhiyun }
178