162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * vivid-radio-common.c - common radio rx/tx support functions.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/errno.h>
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <linux/videodev2.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "vivid-core.h"
1462306a36Sopenharmony_ci#include "vivid-ctrls.h"
1562306a36Sopenharmony_ci#include "vivid-radio-common.h"
1662306a36Sopenharmony_ci#include "vivid-rds-gen.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/*
1962306a36Sopenharmony_ci * These functions are shared between the vivid receiver and transmitter
2062306a36Sopenharmony_ci * since both use the same frequency bands.
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ciconst struct v4l2_frequency_band vivid_radio_bands[TOT_BANDS] = {
2462306a36Sopenharmony_ci	/* Band FM */
2562306a36Sopenharmony_ci	{
2662306a36Sopenharmony_ci		.type = V4L2_TUNER_RADIO,
2762306a36Sopenharmony_ci		.index = 0,
2862306a36Sopenharmony_ci		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
2962306a36Sopenharmony_ci			      V4L2_TUNER_CAP_FREQ_BANDS,
3062306a36Sopenharmony_ci		.rangelow   = FM_FREQ_RANGE_LOW,
3162306a36Sopenharmony_ci		.rangehigh  = FM_FREQ_RANGE_HIGH,
3262306a36Sopenharmony_ci		.modulation = V4L2_BAND_MODULATION_FM,
3362306a36Sopenharmony_ci	},
3462306a36Sopenharmony_ci	/* Band AM */
3562306a36Sopenharmony_ci	{
3662306a36Sopenharmony_ci		.type = V4L2_TUNER_RADIO,
3762306a36Sopenharmony_ci		.index = 1,
3862306a36Sopenharmony_ci		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
3962306a36Sopenharmony_ci		.rangelow   = AM_FREQ_RANGE_LOW,
4062306a36Sopenharmony_ci		.rangehigh  = AM_FREQ_RANGE_HIGH,
4162306a36Sopenharmony_ci		.modulation = V4L2_BAND_MODULATION_AM,
4262306a36Sopenharmony_ci	},
4362306a36Sopenharmony_ci	/* Band SW */
4462306a36Sopenharmony_ci	{
4562306a36Sopenharmony_ci		.type = V4L2_TUNER_RADIO,
4662306a36Sopenharmony_ci		.index = 2,
4762306a36Sopenharmony_ci		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
4862306a36Sopenharmony_ci		.rangelow   = SW_FREQ_RANGE_LOW,
4962306a36Sopenharmony_ci		.rangehigh  = SW_FREQ_RANGE_HIGH,
5062306a36Sopenharmony_ci		.modulation = V4L2_BAND_MODULATION_AM,
5162306a36Sopenharmony_ci	},
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/*
5562306a36Sopenharmony_ci * Initialize the RDS generator. If we can loop, then the RDS generator
5662306a36Sopenharmony_ci * is set up with the values from the RDS TX controls, otherwise it
5762306a36Sopenharmony_ci * will fill in standard values using one of two alternates.
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_civoid vivid_radio_rds_init(struct vivid_dev *dev)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct vivid_rds_gen *rds = &dev->rds_gen;
6262306a36Sopenharmony_ci	bool alt = dev->radio_rx_rds_use_alternates;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* Do nothing, blocks will be filled by the transmitter */
6562306a36Sopenharmony_ci	if (dev->radio_rds_loop && !dev->radio_tx_rds_controls)
6662306a36Sopenharmony_ci		return;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (dev->radio_rds_loop) {
6962306a36Sopenharmony_ci		v4l2_ctrl_lock(dev->radio_tx_rds_pi);
7062306a36Sopenharmony_ci		rds->picode = dev->radio_tx_rds_pi->cur.val;
7162306a36Sopenharmony_ci		rds->pty = dev->radio_tx_rds_pty->cur.val;
7262306a36Sopenharmony_ci		rds->mono_stereo = dev->radio_tx_rds_mono_stereo->cur.val;
7362306a36Sopenharmony_ci		rds->art_head = dev->radio_tx_rds_art_head->cur.val;
7462306a36Sopenharmony_ci		rds->compressed = dev->radio_tx_rds_compressed->cur.val;
7562306a36Sopenharmony_ci		rds->dyn_pty = dev->radio_tx_rds_dyn_pty->cur.val;
7662306a36Sopenharmony_ci		rds->ta = dev->radio_tx_rds_ta->cur.val;
7762306a36Sopenharmony_ci		rds->tp = dev->radio_tx_rds_tp->cur.val;
7862306a36Sopenharmony_ci		rds->ms = dev->radio_tx_rds_ms->cur.val;
7962306a36Sopenharmony_ci		strscpy(rds->psname,
8062306a36Sopenharmony_ci			dev->radio_tx_rds_psname->p_cur.p_char,
8162306a36Sopenharmony_ci			sizeof(rds->psname));
8262306a36Sopenharmony_ci		strscpy(rds->radiotext,
8362306a36Sopenharmony_ci			dev->radio_tx_rds_radiotext->p_cur.p_char + alt * 64,
8462306a36Sopenharmony_ci			sizeof(rds->radiotext));
8562306a36Sopenharmony_ci		v4l2_ctrl_unlock(dev->radio_tx_rds_pi);
8662306a36Sopenharmony_ci	} else {
8762306a36Sopenharmony_ci		vivid_rds_gen_fill(rds, dev->radio_rx_freq, alt);
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci	if (dev->radio_rx_rds_controls) {
9062306a36Sopenharmony_ci		v4l2_ctrl_s_ctrl(dev->radio_rx_rds_pty, rds->pty);
9162306a36Sopenharmony_ci		v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ta, rds->ta);
9262306a36Sopenharmony_ci		v4l2_ctrl_s_ctrl(dev->radio_rx_rds_tp, rds->tp);
9362306a36Sopenharmony_ci		v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ms, rds->ms);
9462306a36Sopenharmony_ci		v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_psname, rds->psname);
9562306a36Sopenharmony_ci		v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_radiotext, rds->radiotext);
9662306a36Sopenharmony_ci		if (!dev->radio_rds_loop)
9762306a36Sopenharmony_ci			dev->radio_rx_rds_use_alternates = !dev->radio_rx_rds_use_alternates;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci	vivid_rds_generate(rds);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/*
10362306a36Sopenharmony_ci * Calculate the emulated signal quality taking into account the frequency
10462306a36Sopenharmony_ci * the transmitter is using.
10562306a36Sopenharmony_ci */
10662306a36Sopenharmony_cistatic void vivid_radio_calc_sig_qual(struct vivid_dev *dev)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	int mod = 16000;
10962306a36Sopenharmony_ci	int delta = 800;
11062306a36Sopenharmony_ci	int sig_qual, sig_qual_tx = mod;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/*
11362306a36Sopenharmony_ci	 * For SW and FM there is a channel every 1000 kHz, for AM there is one
11462306a36Sopenharmony_ci	 * every 100 kHz.
11562306a36Sopenharmony_ci	 */
11662306a36Sopenharmony_ci	if (dev->radio_rx_freq <= AM_FREQ_RANGE_HIGH) {
11762306a36Sopenharmony_ci		mod /= 10;
11862306a36Sopenharmony_ci		delta /= 10;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci	sig_qual = (dev->radio_rx_freq + delta) % mod - delta;
12162306a36Sopenharmony_ci	if (dev->has_radio_tx)
12262306a36Sopenharmony_ci		sig_qual_tx = dev->radio_rx_freq - dev->radio_tx_freq;
12362306a36Sopenharmony_ci	if (abs(sig_qual_tx) <= abs(sig_qual)) {
12462306a36Sopenharmony_ci		sig_qual = sig_qual_tx;
12562306a36Sopenharmony_ci		/*
12662306a36Sopenharmony_ci		 * Zero the internal rds buffer if we are going to loop
12762306a36Sopenharmony_ci		 * rds blocks.
12862306a36Sopenharmony_ci		 */
12962306a36Sopenharmony_ci		if (!dev->radio_rds_loop && !dev->radio_tx_rds_controls)
13062306a36Sopenharmony_ci			memset(dev->rds_gen.data, 0,
13162306a36Sopenharmony_ci			       sizeof(dev->rds_gen.data));
13262306a36Sopenharmony_ci		dev->radio_rds_loop = dev->radio_rx_freq >= FM_FREQ_RANGE_LOW;
13362306a36Sopenharmony_ci	} else {
13462306a36Sopenharmony_ci		dev->radio_rds_loop = false;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci	if (dev->radio_rx_freq <= AM_FREQ_RANGE_HIGH)
13762306a36Sopenharmony_ci		sig_qual *= 10;
13862306a36Sopenharmony_ci	dev->radio_rx_sig_qual = sig_qual;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ciint vivid_radio_g_frequency(struct file *file, const unsigned *pfreq, struct v4l2_frequency *vf)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	if (vf->tuner != 0)
14462306a36Sopenharmony_ci		return -EINVAL;
14562306a36Sopenharmony_ci	vf->frequency = *pfreq;
14662306a36Sopenharmony_ci	return 0;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ciint vivid_radio_s_frequency(struct file *file, unsigned *pfreq, const struct v4l2_frequency *vf)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct vivid_dev *dev = video_drvdata(file);
15262306a36Sopenharmony_ci	unsigned freq;
15362306a36Sopenharmony_ci	unsigned band;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if (vf->tuner != 0)
15662306a36Sopenharmony_ci		return -EINVAL;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (vf->frequency >= (FM_FREQ_RANGE_LOW + SW_FREQ_RANGE_HIGH) / 2)
15962306a36Sopenharmony_ci		band = BAND_FM;
16062306a36Sopenharmony_ci	else if (vf->frequency <= (AM_FREQ_RANGE_HIGH + SW_FREQ_RANGE_LOW) / 2)
16162306a36Sopenharmony_ci		band = BAND_AM;
16262306a36Sopenharmony_ci	else
16362306a36Sopenharmony_ci		band = BAND_SW;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	freq = clamp_t(u32, vf->frequency, vivid_radio_bands[band].rangelow,
16662306a36Sopenharmony_ci					   vivid_radio_bands[band].rangehigh);
16762306a36Sopenharmony_ci	*pfreq = freq;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/*
17062306a36Sopenharmony_ci	 * For both receiver and transmitter recalculate the signal quality
17162306a36Sopenharmony_ci	 * (since that depends on both frequencies) and re-init the rds
17262306a36Sopenharmony_ci	 * generator.
17362306a36Sopenharmony_ci	 */
17462306a36Sopenharmony_ci	vivid_radio_calc_sig_qual(dev);
17562306a36Sopenharmony_ci	vivid_radio_rds_init(dev);
17662306a36Sopenharmony_ci	return 0;
17762306a36Sopenharmony_ci}
178