1
linux/drivers/media/radio/wl128x/fmdrv_rx.c
Thomas Gleixner 1802d0beec treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 174
Based on 1 normalized pattern(s):

  this program is free software you can redistribute it and or modify
  it under the terms of the gnu general public license version 2 as
  published by the free software foundation this program is
  distributed in the hope that it will be useful but without any
  warranty without even the implied warranty of merchantability or
  fitness for a particular purpose see the gnu general public license
  for more details

extracted by the scancode license scanner the SPDX license identifier

  GPL-2.0-only

has been chosen to replace the boilerplate/reference in 655 file(s).

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Allison Randal <allison@lohutok.net>
Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org>
Reviewed-by: Richard Fontana <rfontana@redhat.com>
Cc: linux-spdx@vger.kernel.org
Link: https://lkml.kernel.org/r/20190527070034.575739538@linutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-05-30 11:26:41 -07:00

821 lines
19 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* FM Driver for Connectivity chip of Texas Instruments.
* This sub-module of FM driver implements FM RX functionality.
*
* Copyright (C) 2011 Texas Instruments
* Author: Raja Mani <raja_mani@ti.com>
* Author: Manjunatha Halli <manjunatha_halli@ti.com>
*/
#include "fmdrv.h"
#include "fmdrv_common.h"
#include "fmdrv_rx.h"
void fm_rx_reset_rds_cache(struct fmdev *fmdev)
{
fmdev->rx.rds.flag = FM_RDS_DISABLE;
fmdev->rx.rds.last_blk_idx = 0;
fmdev->rx.rds.wr_idx = 0;
fmdev->rx.rds.rd_idx = 0;
if (fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON)
fmdev->irq_info.mask |= FM_LEV_EVENT;
}
void fm_rx_reset_station_info(struct fmdev *fmdev)
{
fmdev->rx.stat_info.picode = FM_NO_PI_CODE;
fmdev->rx.stat_info.afcache_size = 0;
fmdev->rx.stat_info.af_list_max = 0;
}
int fm_rx_set_freq(struct fmdev *fmdev, u32 freq)
{
unsigned long timeleft;
u16 payload, curr_frq, intr_flag;
u32 curr_frq_in_khz;
u32 resp_len;
int ret;
if (freq < fmdev->rx.region.bot_freq || freq > fmdev->rx.region.top_freq) {
fmerr("Invalid frequency %d\n", freq);
return -EINVAL;
}
/* Set audio enable */
payload = FM_RX_AUDIO_ENABLE_I2S_AND_ANALOG;
ret = fmc_send_cmd(fmdev, AUDIO_ENABLE_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
/* Set hilo to automatic selection */
payload = FM_RX_IFFREQ_HILO_AUTOMATIC;
ret = fmc_send_cmd(fmdev, HILO_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
/* Calculate frequency index and set*/
payload = (freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
/* Read flags - just to clear any pending interrupts if we had */
ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL);
if (ret < 0)
return ret;
/* Enable FR, BL interrupts */
intr_flag = fmdev->irq_info.mask;
fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT);
payload = fmdev->irq_info.mask;
ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
/* Start tune */
payload = FM_TUNER_PRESET_MODE;
ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
goto exit;
/* Wait for tune ended interrupt */
init_completion(&fmdev->maintask_comp);
timeleft = wait_for_completion_timeout(&fmdev->maintask_comp,
FM_DRV_TX_TIMEOUT);
if (!timeleft) {
fmerr("Timeout(%d sec),didn't get tune ended int\n",
jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000);
ret = -ETIMEDOUT;
goto exit;
}
/* Read freq back to confirm */
ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2, &curr_frq, &resp_len);
if (ret < 0)
goto exit;
curr_frq = be16_to_cpu((__force __be16)curr_frq);
curr_frq_in_khz = (fmdev->rx.region.bot_freq + ((u32)curr_frq * FM_FREQ_MUL));
if (curr_frq_in_khz != freq) {
pr_info("Frequency is set to (%d) but requested freq is (%d)\n",
curr_frq_in_khz, freq);
}
/* Update local cache */
fmdev->rx.freq = curr_frq_in_khz;
exit:
/* Re-enable default FM interrupts */
fmdev->irq_info.mask = intr_flag;
payload = fmdev->irq_info.mask;
ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
/* Reset RDS cache and current station pointers */
fm_rx_reset_rds_cache(fmdev);
fm_rx_reset_station_info(fmdev);
return ret;
}
static int fm_rx_set_channel_spacing(struct fmdev *fmdev, u32 spacing)
{
u16 payload;
int ret;
if (spacing > 0 && spacing <= 50000)
spacing = FM_CHANNEL_SPACING_50KHZ;
else if (spacing > 50000 && spacing <= 100000)
spacing = FM_CHANNEL_SPACING_100KHZ;
else
spacing = FM_CHANNEL_SPACING_200KHZ;
/* set channel spacing */
payload = spacing;
ret = fmc_send_cmd(fmdev, CHANL_BW_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
fmdev->rx.region.chanl_space = spacing * FM_FREQ_MUL;
return ret;
}
int fm_rx_seek(struct fmdev *fmdev, u32 seek_upward,
u32 wrap_around, u32 spacing)
{
u32 resp_len;
u16 curr_frq, next_frq, last_frq;
u16 payload, int_reason, intr_flag;
u16 offset, space_idx;
unsigned long timeleft;
int ret;
/* Set channel spacing */
ret = fm_rx_set_channel_spacing(fmdev, spacing);
if (ret < 0) {
fmerr("Failed to set channel spacing\n");
return ret;
}
/* Read the current frequency from chip */
ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL,
sizeof(curr_frq), &curr_frq, &resp_len);
if (ret < 0)
return ret;
curr_frq = be16_to_cpu((__force __be16)curr_frq);
last_frq = (fmdev->rx.region.top_freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
/* Check the offset in order to be aligned to the channel spacing*/
space_idx = fmdev->rx.region.chanl_space / FM_FREQ_MUL;
offset = curr_frq % space_idx;
next_frq = seek_upward ? curr_frq + space_idx /* Seek Up */ :
curr_frq - space_idx /* Seek Down */ ;
/*
* Add or subtract offset in order to stay aligned to the channel
* spacing.
*/
if ((short)next_frq < 0)
next_frq = last_frq - offset;
else if (next_frq > last_frq)
next_frq = 0 + offset;
again:
/* Set calculated next frequency to perform seek */
payload = next_frq;
ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
/* Set search direction (0:Seek Down, 1:Seek Up) */
payload = (seek_upward ? FM_SEARCH_DIRECTION_UP : FM_SEARCH_DIRECTION_DOWN);
ret = fmc_send_cmd(fmdev, SEARCH_DIR_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
/* Read flags - just to clear any pending interrupts if we had */
ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL);
if (ret < 0)
return ret;
/* Enable FR, BL interrupts */
intr_flag = fmdev->irq_info.mask;
fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT);
payload = fmdev->irq_info.mask;
ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
/* Start seek */
payload = FM_TUNER_AUTONOMOUS_SEARCH_MODE;
ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
/* Wait for tune ended/band limit reached interrupt */
init_completion(&fmdev->maintask_comp);
timeleft = wait_for_completion_timeout(&fmdev->maintask_comp,
FM_DRV_RX_SEEK_TIMEOUT);
if (!timeleft) {
fmerr("Timeout(%d sec),didn't get tune ended int\n",
jiffies_to_msecs(FM_DRV_RX_SEEK_TIMEOUT) / 1000);
return -ENODATA;
}
int_reason = fmdev->irq_info.flag & (FM_TUNE_COMPLETE | FM_BAND_LIMIT);
/* Re-enable default FM interrupts */
fmdev->irq_info.mask = intr_flag;
payload = fmdev->irq_info.mask;
ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
if (int_reason & FM_BL_EVENT) {
if (wrap_around == 0) {
fmdev->rx.freq = seek_upward ?
fmdev->rx.region.top_freq :
fmdev->rx.region.bot_freq;
} else {
fmdev->rx.freq = seek_upward ?
fmdev->rx.region.bot_freq :
fmdev->rx.region.top_freq;
/* Calculate frequency index to write */
next_frq = (fmdev->rx.freq -
fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
goto again;
}
} else {
/* Read freq to know where operation tune operation stopped */
ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2,
&curr_frq, &resp_len);
if (ret < 0)
return ret;
curr_frq = be16_to_cpu((__force __be16)curr_frq);
fmdev->rx.freq = (fmdev->rx.region.bot_freq +
((u32)curr_frq * FM_FREQ_MUL));
}
/* Reset RDS cache and current station pointers */
fm_rx_reset_rds_cache(fmdev);
fm_rx_reset_station_info(fmdev);
return ret;
}
int fm_rx_set_volume(struct fmdev *fmdev, u16 vol_to_set)
{
u16 payload;
int ret;
if (fmdev->curr_fmmode != FM_MODE_RX)
return -EPERM;
if (vol_to_set > FM_RX_VOLUME_MAX) {
fmerr("Volume is not within(%d-%d) range\n",
FM_RX_VOLUME_MIN, FM_RX_VOLUME_MAX);
return -EINVAL;
}
vol_to_set *= FM_RX_VOLUME_GAIN_STEP;
payload = vol_to_set;
ret = fmc_send_cmd(fmdev, VOLUME_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
fmdev->rx.volume = vol_to_set;
return ret;
}
/* Get volume */
int fm_rx_get_volume(struct fmdev *fmdev, u16 *curr_vol)
{
if (fmdev->curr_fmmode != FM_MODE_RX)
return -EPERM;
if (curr_vol == NULL) {
fmerr("Invalid memory\n");
return -ENOMEM;
}
*curr_vol = fmdev->rx.volume / FM_RX_VOLUME_GAIN_STEP;
return 0;
}
/* To get current band's bottom and top frequency */
int fm_rx_get_band_freq_range(struct fmdev *fmdev, u32 *bot_freq, u32 *top_freq)
{
if (bot_freq != NULL)
*bot_freq = fmdev->rx.region.bot_freq;
if (top_freq != NULL)
*top_freq = fmdev->rx.region.top_freq;
return 0;
}
/* Returns current band index (0-Europe/US; 1-Japan) */
void fm_rx_get_region(struct fmdev *fmdev, u8 *region)
{
*region = fmdev->rx.region.fm_band;
}
/* Sets band (0-Europe/US; 1-Japan) */
int fm_rx_set_region(struct fmdev *fmdev, u8 region_to_set)
{
u16 payload;
u32 new_frq = 0;
int ret;
if (region_to_set != FM_BAND_EUROPE_US &&
region_to_set != FM_BAND_JAPAN) {
fmerr("Invalid band\n");
return -EINVAL;
}
if (fmdev->rx.region.fm_band == region_to_set) {
fmerr("Requested band is already configured\n");
return 0;
}
/* Send cmd to set the band */
payload = (u16)region_to_set;
ret = fmc_send_cmd(fmdev, BAND_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
fmc_update_region_info(fmdev, region_to_set);
/* Check whether current RX frequency is within band boundary */
if (fmdev->rx.freq < fmdev->rx.region.bot_freq)
new_frq = fmdev->rx.region.bot_freq;
else if (fmdev->rx.freq > fmdev->rx.region.top_freq)
new_frq = fmdev->rx.region.top_freq;
if (new_frq) {
fmdbg("Current freq is not within band limit boundary,switching to %d KHz\n",
new_frq);
/* Current RX frequency is not in range. So, update it */
ret = fm_rx_set_freq(fmdev, new_frq);
}
return ret;
}
/* Reads current mute mode (Mute Off/On/Attenuate)*/
int fm_rx_get_mute_mode(struct fmdev *fmdev, u8 *curr_mute_mode)
{
if (fmdev->curr_fmmode != FM_MODE_RX)
return -EPERM;
if (curr_mute_mode == NULL) {
fmerr("Invalid memory\n");
return -ENOMEM;
}
*curr_mute_mode = fmdev->rx.mute_mode;
return 0;
}
static int fm_config_rx_mute_reg(struct fmdev *fmdev)
{
u16 payload, muteval;
int ret;
muteval = 0;
switch (fmdev->rx.mute_mode) {
case FM_MUTE_ON:
muteval = FM_RX_AC_MUTE_MODE;
break;
case FM_MUTE_OFF:
muteval = FM_RX_UNMUTE_MODE;
break;
case FM_MUTE_ATTENUATE:
muteval = FM_RX_SOFT_MUTE_FORCE_MODE;
break;
}
if (fmdev->rx.rf_depend_mute == FM_RX_RF_DEPENDENT_MUTE_ON)
muteval |= FM_RX_RF_DEP_MODE;
else
muteval &= ~FM_RX_RF_DEP_MODE;
payload = muteval;
ret = fmc_send_cmd(fmdev, MUTE_STATUS_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
return 0;
}
/* Configures mute mode (Mute Off/On/Attenuate) */
int fm_rx_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset)
{
u8 org_state;
int ret;
if (fmdev->rx.mute_mode == mute_mode_toset)
return 0;
org_state = fmdev->rx.mute_mode;
fmdev->rx.mute_mode = mute_mode_toset;
ret = fm_config_rx_mute_reg(fmdev);
if (ret < 0) {
fmdev->rx.mute_mode = org_state;
return ret;
}
return 0;
}
/* Gets RF dependent soft mute mode enable/disable status */
int fm_rx_get_rfdepend_softmute(struct fmdev *fmdev, u8 *curr_mute_mode)
{
if (fmdev->curr_fmmode != FM_MODE_RX)
return -EPERM;
if (curr_mute_mode == NULL) {
fmerr("Invalid memory\n");
return -ENOMEM;
}
*curr_mute_mode = fmdev->rx.rf_depend_mute;
return 0;
}
/* Sets RF dependent soft mute mode */
int fm_rx_set_rfdepend_softmute(struct fmdev *fmdev, u8 rfdepend_mute)
{
u8 org_state;
int ret;
if (fmdev->curr_fmmode != FM_MODE_RX)
return -EPERM;
if (rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_ON &&
rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_OFF) {
fmerr("Invalid RF dependent soft mute\n");
return -EINVAL;
}
if (fmdev->rx.rf_depend_mute == rfdepend_mute)
return 0;
org_state = fmdev->rx.rf_depend_mute;
fmdev->rx.rf_depend_mute = rfdepend_mute;
ret = fm_config_rx_mute_reg(fmdev);
if (ret < 0) {
fmdev->rx.rf_depend_mute = org_state;
return ret;
}
return 0;
}
/* Returns the signal strength level of current channel */
int fm_rx_get_rssi_level(struct fmdev *fmdev, u16 *rssilvl)
{
__be16 curr_rssi_lel;
u32 resp_len;
int ret;
if (rssilvl == NULL) {
fmerr("Invalid memory\n");
return -ENOMEM;
}
/* Read current RSSI level */
ret = fmc_send_cmd(fmdev, RSSI_LVL_GET, REG_RD, NULL, 2,
&curr_rssi_lel, &resp_len);
if (ret < 0)
return ret;
*rssilvl = be16_to_cpu(curr_rssi_lel);
return 0;
}
/*
* Sets the signal strength level that once reached
* will stop the auto search process
*/
int fm_rx_set_rssi_threshold(struct fmdev *fmdev, short rssi_lvl_toset)
{
u16 payload;
int ret;
if (rssi_lvl_toset < FM_RX_RSSI_THRESHOLD_MIN ||
rssi_lvl_toset > FM_RX_RSSI_THRESHOLD_MAX) {
fmerr("Invalid RSSI threshold level\n");
return -EINVAL;
}
payload = (u16)rssi_lvl_toset;
ret = fmc_send_cmd(fmdev, SEARCH_LVL_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
fmdev->rx.rssi_threshold = rssi_lvl_toset;
return 0;
}
/* Returns current RX RSSI threshold value */
int fm_rx_get_rssi_threshold(struct fmdev *fmdev, short *curr_rssi_lvl)
{
if (fmdev->curr_fmmode != FM_MODE_RX)
return -EPERM;
if (curr_rssi_lvl == NULL) {
fmerr("Invalid memory\n");
return -ENOMEM;
}
*curr_rssi_lvl = fmdev->rx.rssi_threshold;
return 0;
}
/* Sets RX stereo/mono modes */
int fm_rx_set_stereo_mono(struct fmdev *fmdev, u16 mode)
{
u16 payload;
int ret;
if (mode != FM_STEREO_MODE && mode != FM_MONO_MODE) {
fmerr("Invalid mode\n");
return -EINVAL;
}
/* Set stereo/mono mode */
payload = (u16)mode;
ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
/* Set stereo blending mode */
payload = FM_STEREO_SOFT_BLEND;
ret = fmc_send_cmd(fmdev, MOST_BLEND_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
return 0;
}
/* Gets current RX stereo/mono mode */
int fm_rx_get_stereo_mono(struct fmdev *fmdev, u16 *mode)
{
__be16 curr_mode;
u32 resp_len;
int ret;
if (mode == NULL) {
fmerr("Invalid memory\n");
return -ENOMEM;
}
ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_RD, NULL, 2,
&curr_mode, &resp_len);
if (ret < 0)
return ret;
*mode = be16_to_cpu(curr_mode);
return 0;
}
/* Choose RX de-emphasis filter mode (50us/75us) */
int fm_rx_set_deemphasis_mode(struct fmdev *fmdev, u16 mode)
{
u16 payload;
int ret;
if (fmdev->curr_fmmode != FM_MODE_RX)
return -EPERM;
if (mode != FM_RX_EMPHASIS_FILTER_50_USEC &&
mode != FM_RX_EMPHASIS_FILTER_75_USEC) {
fmerr("Invalid rx de-emphasis mode (%d)\n", mode);
return -EINVAL;
}
payload = mode;
ret = fmc_send_cmd(fmdev, DEMPH_MODE_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
fmdev->rx.deemphasis_mode = mode;
return 0;
}
/* Gets current RX de-emphasis filter mode */
int fm_rx_get_deemph_mode(struct fmdev *fmdev, u16 *curr_deemphasis_mode)
{
if (fmdev->curr_fmmode != FM_MODE_RX)
return -EPERM;
if (curr_deemphasis_mode == NULL) {
fmerr("Invalid memory\n");
return -ENOMEM;
}
*curr_deemphasis_mode = fmdev->rx.deemphasis_mode;
return 0;
}
/* Enable/Disable RX RDS */
int fm_rx_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis)
{
u16 payload;
int ret;
if (rds_en_dis != FM_RDS_ENABLE && rds_en_dis != FM_RDS_DISABLE) {
fmerr("Invalid rds option\n");
return -EINVAL;
}
if (rds_en_dis == FM_RDS_ENABLE
&& fmdev->rx.rds.flag == FM_RDS_DISABLE) {
/* Turn on RX RDS and RDS circuit */
payload = FM_RX_PWR_SET_FM_AND_RDS_BLK_ON;
ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
/* Clear and reset RDS FIFO */
payload = FM_RX_RDS_FLUSH_FIFO;
ret = fmc_send_cmd(fmdev, RDS_CNTRL_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
/* Read flags - just to clear any pending interrupts. */
ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2,
NULL, NULL);
if (ret < 0)
return ret;
/* Set RDS FIFO threshold value */
payload = FM_RX_RDS_FIFO_THRESHOLD;
ret = fmc_send_cmd(fmdev, RDS_MEM_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
/* Enable RDS interrupt */
fmdev->irq_info.mask |= FM_RDS_EVENT;
payload = fmdev->irq_info.mask;
ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0) {
fmdev->irq_info.mask &= ~FM_RDS_EVENT;
return ret;
}
/* Update our local flag */
fmdev->rx.rds.flag = FM_RDS_ENABLE;
} else if (rds_en_dis == FM_RDS_DISABLE
&& fmdev->rx.rds.flag == FM_RDS_ENABLE) {
/* Turn off RX RDS */
payload = FM_RX_PWR_SET_FM_ON_RDS_OFF;
ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
/* Reset RDS pointers */
fmdev->rx.rds.last_blk_idx = 0;
fmdev->rx.rds.wr_idx = 0;
fmdev->rx.rds.rd_idx = 0;
fm_rx_reset_station_info(fmdev);
/* Update RDS local cache */
fmdev->irq_info.mask &= ~(FM_RDS_EVENT);
fmdev->rx.rds.flag = FM_RDS_DISABLE;
}
return 0;
}
/* Returns current RX RDS enable/disable status */
int fm_rx_get_rds_mode(struct fmdev *fmdev, u8 *curr_rds_en_dis)
{
if (fmdev->curr_fmmode != FM_MODE_RX)
return -EPERM;
if (curr_rds_en_dis == NULL) {
fmerr("Invalid memory\n");
return -ENOMEM;
}
*curr_rds_en_dis = fmdev->rx.rds.flag;
return 0;
}
/* Sets RDS operation mode (RDS/RDBS) */
int fm_rx_set_rds_system(struct fmdev *fmdev, u8 rds_mode)
{
u16 payload;
int ret;
if (fmdev->curr_fmmode != FM_MODE_RX)
return -EPERM;
if (rds_mode != FM_RDS_SYSTEM_RDS && rds_mode != FM_RDS_SYSTEM_RBDS) {
fmerr("Invalid rds mode\n");
return -EINVAL;
}
/* Set RDS operation mode */
payload = (u16)rds_mode;
ret = fmc_send_cmd(fmdev, RDS_SYSTEM_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
fmdev->rx.rds_mode = rds_mode;
return 0;
}
/* Configures Alternate Frequency switch mode */
int fm_rx_set_af_switch(struct fmdev *fmdev, u8 af_mode)
{
u16 payload;
int ret;
if (fmdev->curr_fmmode != FM_MODE_RX)
return -EPERM;
if (af_mode != FM_RX_RDS_AF_SWITCH_MODE_ON &&
af_mode != FM_RX_RDS_AF_SWITCH_MODE_OFF) {
fmerr("Invalid af mode\n");
return -EINVAL;
}
/* Enable/disable low RSSI interrupt based on af_mode */
if (af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON)
fmdev->irq_info.mask |= FM_LEV_EVENT;
else
fmdev->irq_info.mask &= ~FM_LEV_EVENT;
payload = fmdev->irq_info.mask;
ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
sizeof(payload), NULL, NULL);
if (ret < 0)
return ret;
fmdev->rx.af_mode = af_mode;
return 0;
}
/* Returns Alternate Frequency switch status */
int fm_rx_get_af_switch(struct fmdev *fmdev, u8 *af_mode)
{
if (fmdev->curr_fmmode != FM_MODE_RX)
return -EPERM;
if (af_mode == NULL) {
fmerr("Invalid memory\n");
return -ENOMEM;
}
*af_mode = fmdev->rx.af_mode;
return 0;
}