diff options
Diffstat (limited to 'drivers/media/platform/vivid/vivid-radio-common.c')
| -rw-r--r-- | drivers/media/platform/vivid/vivid-radio-common.c | 189 | 
1 files changed, 189 insertions, 0 deletions
| diff --git a/drivers/media/platform/vivid/vivid-radio-common.c b/drivers/media/platform/vivid/vivid-radio-common.c new file mode 100644 index 000000000000..78c1e920670a --- /dev/null +++ b/drivers/media/platform/vivid/vivid-radio-common.c @@ -0,0 +1,189 @@ +/* + * vivid-radio-common.c - common radio rx/tx support functions. + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/videodev2.h> + +#include "vivid-core.h" +#include "vivid-ctrls.h" +#include "vivid-radio-common.h" +#include "vivid-rds-gen.h" + +/* + * These functions are shared between the vivid receiver and transmitter + * since both use the same frequency bands. + */ + +const struct v4l2_frequency_band vivid_radio_bands[TOT_BANDS] = { +	/* Band FM */ +	{ +		.type = V4L2_TUNER_RADIO, +		.index = 0, +		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | +			      V4L2_TUNER_CAP_FREQ_BANDS, +		.rangelow   = FM_FREQ_RANGE_LOW, +		.rangehigh  = FM_FREQ_RANGE_HIGH, +		.modulation = V4L2_BAND_MODULATION_FM, +	}, +	/* Band AM */ +	{ +		.type = V4L2_TUNER_RADIO, +		.index = 1, +		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS, +		.rangelow   = AM_FREQ_RANGE_LOW, +		.rangehigh  = AM_FREQ_RANGE_HIGH, +		.modulation = V4L2_BAND_MODULATION_AM, +	}, +	/* Band SW */ +	{ +		.type = V4L2_TUNER_RADIO, +		.index = 2, +		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS, +		.rangelow   = SW_FREQ_RANGE_LOW, +		.rangehigh  = SW_FREQ_RANGE_HIGH, +		.modulation = V4L2_BAND_MODULATION_AM, +	}, +}; + +/* + * Initialize the RDS generator. If we can loop, then the RDS generator + * is set up with the values from the RDS TX controls, otherwise it + * will fill in standard values using one of two alternates. + */ +void vivid_radio_rds_init(struct vivid_dev *dev) +{ +	struct vivid_rds_gen *rds = &dev->rds_gen; +	bool alt = dev->radio_rx_rds_use_alternates; + +	/* Do nothing, blocks will be filled by the transmitter */ +	if (dev->radio_rds_loop && !dev->radio_tx_rds_controls) +		return; + +	if (dev->radio_rds_loop) { +		v4l2_ctrl_lock(dev->radio_tx_rds_pi); +		rds->picode = dev->radio_tx_rds_pi->cur.val; +		rds->pty = dev->radio_tx_rds_pty->cur.val; +		rds->mono_stereo = dev->radio_tx_rds_mono_stereo->cur.val; +		rds->art_head = dev->radio_tx_rds_art_head->cur.val; +		rds->compressed = dev->radio_tx_rds_compressed->cur.val; +		rds->dyn_pty = dev->radio_tx_rds_dyn_pty->cur.val; +		rds->ta = dev->radio_tx_rds_ta->cur.val; +		rds->tp = dev->radio_tx_rds_tp->cur.val; +		rds->ms = dev->radio_tx_rds_ms->cur.val; +		strlcpy(rds->psname, +			dev->radio_tx_rds_psname->p_cur.p_char, +			sizeof(rds->psname)); +		strlcpy(rds->radiotext, +			dev->radio_tx_rds_radiotext->p_cur.p_char + alt * 64, +			sizeof(rds->radiotext)); +		v4l2_ctrl_unlock(dev->radio_tx_rds_pi); +	} else { +		vivid_rds_gen_fill(rds, dev->radio_rx_freq, alt); +	} +	if (dev->radio_rx_rds_controls) { +		v4l2_ctrl_s_ctrl(dev->radio_rx_rds_pty, rds->pty); +		v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ta, rds->ta); +		v4l2_ctrl_s_ctrl(dev->radio_rx_rds_tp, rds->tp); +		v4l2_ctrl_s_ctrl(dev->radio_rx_rds_ms, rds->ms); +		v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_psname, rds->psname); +		v4l2_ctrl_s_ctrl_string(dev->radio_rx_rds_radiotext, rds->radiotext); +		if (!dev->radio_rds_loop) +			dev->radio_rx_rds_use_alternates = !dev->radio_rx_rds_use_alternates; +	} +	vivid_rds_generate(rds); +} + +/* + * Calculate the emulated signal quality taking into account the frequency + * the transmitter is using. + */ +static void vivid_radio_calc_sig_qual(struct vivid_dev *dev) +{ +	int mod = 16000; +	int delta = 800; +	int sig_qual, sig_qual_tx = mod; + +	/* +	 * For SW and FM there is a channel every 1000 kHz, for AM there is one +	 * every 100 kHz. +	 */ +	if (dev->radio_rx_freq <= AM_FREQ_RANGE_HIGH) { +		mod /= 10; +		delta /= 10; +	} +	sig_qual = (dev->radio_rx_freq + delta) % mod - delta; +	if (dev->has_radio_tx) +		sig_qual_tx = dev->radio_rx_freq - dev->radio_tx_freq; +	if (abs(sig_qual_tx) <= abs(sig_qual)) { +		sig_qual = sig_qual_tx; +		/* +		 * Zero the internal rds buffer if we are going to loop +		 * rds blocks. +		 */ +		if (!dev->radio_rds_loop && !dev->radio_tx_rds_controls) +			memset(dev->rds_gen.data, 0, +			       sizeof(dev->rds_gen.data)); +		dev->radio_rds_loop = dev->radio_rx_freq >= FM_FREQ_RANGE_LOW; +	} else { +		dev->radio_rds_loop = false; +	} +	if (dev->radio_rx_freq <= AM_FREQ_RANGE_HIGH) +		sig_qual *= 10; +	dev->radio_rx_sig_qual = sig_qual; +} + +int vivid_radio_g_frequency(struct file *file, const unsigned *pfreq, struct v4l2_frequency *vf) +{ +	if (vf->tuner != 0) +		return -EINVAL; +	vf->frequency = *pfreq; +	return 0; +} + +int vivid_radio_s_frequency(struct file *file, unsigned *pfreq, const struct v4l2_frequency *vf) +{ +	struct vivid_dev *dev = video_drvdata(file); +	unsigned freq; +	unsigned band; + +	if (vf->tuner != 0) +		return -EINVAL; + +	if (vf->frequency >= (FM_FREQ_RANGE_LOW + SW_FREQ_RANGE_HIGH) / 2) +		band = BAND_FM; +	else if (vf->frequency <= (AM_FREQ_RANGE_HIGH + SW_FREQ_RANGE_LOW) / 2) +		band = BAND_AM; +	else +		band = BAND_SW; + +	freq = clamp_t(u32, vf->frequency, vivid_radio_bands[band].rangelow, +					   vivid_radio_bands[band].rangehigh); +	*pfreq = freq; + +	/* +	 * For both receiver and transmitter recalculate the signal quality +	 * (since that depends on both frequencies) and re-init the rds +	 * generator. +	 */ +	vivid_radio_calc_sig_qual(dev); +	vivid_radio_rds_init(dev); +	return 0; +} | 
