File Index
Files and directories
Sign in
FILE

vivid-radio-common.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/test-drivers/vivid/vivid-radio-common.c C
  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
 23const 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 */
 59void 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(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(dev->radio_tx_rds_pi);
 86	} else {
 87		vivid_rds_gen_fill(rds, dev->radio_rx_freq, alt);
 88	}
 89	if (dev->radio_rx_rds_controls) {
 90		v4l2_ctrl_s_ctrl(dev->radio_rx_rds_pty, rds->pty);
 91		v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ta, rds->ta);
 92		v4l2_ctrl_s_ctrl(dev->radio_rx_rds_tp, rds->tp);
 93		v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ms, rds->ms);
 94		v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_psname, rds->psname);
 95		v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_radiotext, 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 */
106static 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
141int 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
149int 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}
~/ Read-only file preview.