2018-02-07 09:12:45 -05:00
// SPDX-License-Identifier: GPL-2.0-only
2014-08-25 08:02:56 -03:00
/*
* vivid - radio - common . c - common radio rx / tx support functions .
*
* Copyright 2014 Cisco Systems , Inc . and / or its affiliates . All rights reserved .
*/
# 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 ;
}