Path: blob/master/drivers/media/radio/wl128x/fmdrv_rx.c
15112 views
/*1* FM Driver for Connectivity chip of Texas Instruments.2* This sub-module of FM driver implements FM RX functionality.3*4* Copyright (C) 2011 Texas Instruments5* Author: Raja Mani <[email protected]>6* Author: Manjunatha Halli <[email protected]>7*8* This program is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License version 2 as10* published by the Free Software Foundation.11*12* This program is distributed in the hope that it will be useful,13* but WITHOUT ANY WARRANTY; without even the implied warranty of14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the15* GNU General Public License for more details.16*17* You should have received a copy of the GNU General Public License18* along with this program; if not, write to the Free Software19* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA20*21*/2223#include "fmdrv.h"24#include "fmdrv_common.h"25#include "fmdrv_rx.h"2627void fm_rx_reset_rds_cache(struct fmdev *fmdev)28{29fmdev->rx.rds.flag = FM_RDS_DISABLE;30fmdev->rx.rds.last_blk_idx = 0;31fmdev->rx.rds.wr_idx = 0;32fmdev->rx.rds.rd_idx = 0;3334if (fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON)35fmdev->irq_info.mask |= FM_LEV_EVENT;36}3738void fm_rx_reset_station_info(struct fmdev *fmdev)39{40fmdev->rx.stat_info.picode = FM_NO_PI_CODE;41fmdev->rx.stat_info.afcache_size = 0;42fmdev->rx.stat_info.af_list_max = 0;43}4445u32 fm_rx_set_freq(struct fmdev *fmdev, u32 freq)46{47unsigned long timeleft;48u16 payload, curr_frq, intr_flag;49u32 curr_frq_in_khz;50u32 ret, resp_len;5152if (freq < fmdev->rx.region.bot_freq || freq > fmdev->rx.region.top_freq) {53fmerr("Invalid frequency %d\n", freq);54return -EINVAL;55}5657/* Set audio enable */58payload = FM_RX_AUDIO_ENABLE_I2S_AND_ANALOG;5960ret = fmc_send_cmd(fmdev, AUDIO_ENABLE_SET, REG_WR, &payload,61sizeof(payload), NULL, NULL);62if (ret < 0)63return ret;6465/* Set hilo to automatic selection */66payload = FM_RX_IFFREQ_HILO_AUTOMATIC;67ret = fmc_send_cmd(fmdev, HILO_SET, REG_WR, &payload,68sizeof(payload), NULL, NULL);69if (ret < 0)70return ret;7172/* Calculate frequency index and set*/73payload = (freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL;7475ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload,76sizeof(payload), NULL, NULL);77if (ret < 0)78return ret;7980/* Read flags - just to clear any pending interrupts if we had */81ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL);82if (ret < 0)83return ret;8485/* Enable FR, BL interrupts */86intr_flag = fmdev->irq_info.mask;87fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT);88payload = fmdev->irq_info.mask;89ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,90sizeof(payload), NULL, NULL);91if (ret < 0)92return ret;9394/* Start tune */95payload = FM_TUNER_PRESET_MODE;96ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload,97sizeof(payload), NULL, NULL);98if (ret < 0)99goto exit;100101/* Wait for tune ended interrupt */102init_completion(&fmdev->maintask_comp);103timeleft = wait_for_completion_timeout(&fmdev->maintask_comp,104FM_DRV_TX_TIMEOUT);105if (!timeleft) {106fmerr("Timeout(%d sec),didn't get tune ended int\n",107jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000);108ret = -ETIMEDOUT;109goto exit;110}111112/* Read freq back to confirm */113ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2, &curr_frq, &resp_len);114if (ret < 0)115goto exit;116117curr_frq = be16_to_cpu(curr_frq);118curr_frq_in_khz = (fmdev->rx.region.bot_freq + ((u32)curr_frq * FM_FREQ_MUL));119120if (curr_frq_in_khz != freq) {121pr_info("Frequency is set to (%d) but "122"requested freq is (%d)\n", curr_frq_in_khz, freq);123}124125/* Update local cache */126fmdev->rx.freq = curr_frq_in_khz;127exit:128/* Re-enable default FM interrupts */129fmdev->irq_info.mask = intr_flag;130payload = fmdev->irq_info.mask;131ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,132sizeof(payload), NULL, NULL);133if (ret < 0)134return ret;135136/* Reset RDS cache and current station pointers */137fm_rx_reset_rds_cache(fmdev);138fm_rx_reset_station_info(fmdev);139140return ret;141}142143static u32 fm_rx_set_channel_spacing(struct fmdev *fmdev, u32 spacing)144{145u16 payload;146u32 ret;147148if (spacing > 0 && spacing <= 50000)149spacing = FM_CHANNEL_SPACING_50KHZ;150else if (spacing > 50000 && spacing <= 100000)151spacing = FM_CHANNEL_SPACING_100KHZ;152else153spacing = FM_CHANNEL_SPACING_200KHZ;154155/* set channel spacing */156payload = spacing;157ret = fmc_send_cmd(fmdev, CHANL_BW_SET, REG_WR, &payload,158sizeof(payload), NULL, NULL);159if (ret < 0)160return ret;161162fmdev->rx.region.chanl_space = spacing * FM_FREQ_MUL;163164return ret;165}166167u32 fm_rx_seek(struct fmdev *fmdev, u32 seek_upward,168u32 wrap_around, u32 spacing)169{170u32 resp_len;171u16 curr_frq, next_frq, last_frq;172u16 payload, int_reason, intr_flag;173u16 offset, space_idx;174unsigned long timeleft;175u32 ret;176177/* Set channel spacing */178ret = fm_rx_set_channel_spacing(fmdev, spacing);179if (ret < 0) {180fmerr("Failed to set channel spacing\n");181return ret;182}183184/* Read the current frequency from chip */185ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL,186sizeof(curr_frq), &curr_frq, &resp_len);187if (ret < 0)188return ret;189190curr_frq = be16_to_cpu(curr_frq);191last_frq = (fmdev->rx.region.top_freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL;192193/* Check the offset in order to be aligned to the channel spacing*/194space_idx = fmdev->rx.region.chanl_space / FM_FREQ_MUL;195offset = curr_frq % space_idx;196197next_frq = seek_upward ? curr_frq + space_idx /* Seek Up */ :198curr_frq - space_idx /* Seek Down */ ;199200/*201* Add or subtract offset in order to stay aligned to the channel202* spacing.203*/204if ((short)next_frq < 0)205next_frq = last_frq - offset;206else if (next_frq > last_frq)207next_frq = 0 + offset;208209again:210/* Set calculated next frequency to perform seek */211payload = next_frq;212ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload,213sizeof(payload), NULL, NULL);214if (ret < 0)215return ret;216217/* Set search direction (0:Seek Down, 1:Seek Up) */218payload = (seek_upward ? FM_SEARCH_DIRECTION_UP : FM_SEARCH_DIRECTION_DOWN);219ret = fmc_send_cmd(fmdev, SEARCH_DIR_SET, REG_WR, &payload,220sizeof(payload), NULL, NULL);221if (ret < 0)222return ret;223224/* Read flags - just to clear any pending interrupts if we had */225ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL);226if (ret < 0)227return ret;228229/* Enable FR, BL interrupts */230intr_flag = fmdev->irq_info.mask;231fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT);232payload = fmdev->irq_info.mask;233ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,234sizeof(payload), NULL, NULL);235if (ret < 0)236return ret;237238/* Start seek */239payload = FM_TUNER_AUTONOMOUS_SEARCH_MODE;240ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload,241sizeof(payload), NULL, NULL);242if (ret < 0)243return ret;244245/* Wait for tune ended/band limit reached interrupt */246init_completion(&fmdev->maintask_comp);247timeleft = wait_for_completion_timeout(&fmdev->maintask_comp,248FM_DRV_RX_SEEK_TIMEOUT);249if (!timeleft) {250fmerr("Timeout(%d sec),didn't get tune ended int\n",251jiffies_to_msecs(FM_DRV_RX_SEEK_TIMEOUT) / 1000);252return -ETIMEDOUT;253}254255int_reason = fmdev->irq_info.flag & (FM_TUNE_COMPLETE | FM_BAND_LIMIT);256257/* Re-enable default FM interrupts */258fmdev->irq_info.mask = intr_flag;259payload = fmdev->irq_info.mask;260ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,261sizeof(payload), NULL, NULL);262if (ret < 0)263return ret;264265if (int_reason & FM_BL_EVENT) {266if (wrap_around == 0) {267fmdev->rx.freq = seek_upward ?268fmdev->rx.region.top_freq :269fmdev->rx.region.bot_freq;270} else {271fmdev->rx.freq = seek_upward ?272fmdev->rx.region.bot_freq :273fmdev->rx.region.top_freq;274/* Calculate frequency index to write */275next_frq = (fmdev->rx.freq -276fmdev->rx.region.bot_freq) / FM_FREQ_MUL;277goto again;278}279} else {280/* Read freq to know where operation tune operation stopped */281ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2,282&curr_frq, &resp_len);283if (ret < 0)284return ret;285286curr_frq = be16_to_cpu(curr_frq);287fmdev->rx.freq = (fmdev->rx.region.bot_freq +288((u32)curr_frq * FM_FREQ_MUL));289290}291/* Reset RDS cache and current station pointers */292fm_rx_reset_rds_cache(fmdev);293fm_rx_reset_station_info(fmdev);294295return ret;296}297298u32 fm_rx_set_volume(struct fmdev *fmdev, u16 vol_to_set)299{300u16 payload;301u32 ret;302303if (fmdev->curr_fmmode != FM_MODE_RX)304return -EPERM;305306if (vol_to_set < FM_RX_VOLUME_MIN || vol_to_set > FM_RX_VOLUME_MAX) {307fmerr("Volume is not within(%d-%d) range\n",308FM_RX_VOLUME_MIN, FM_RX_VOLUME_MAX);309return -EINVAL;310}311vol_to_set *= FM_RX_VOLUME_GAIN_STEP;312313payload = vol_to_set;314ret = fmc_send_cmd(fmdev, VOLUME_SET, REG_WR, &payload,315sizeof(payload), NULL, NULL);316if (ret < 0)317return ret;318319fmdev->rx.volume = vol_to_set;320return ret;321}322323/* Get volume */324u32 fm_rx_get_volume(struct fmdev *fmdev, u16 *curr_vol)325{326if (fmdev->curr_fmmode != FM_MODE_RX)327return -EPERM;328329if (curr_vol == NULL) {330fmerr("Invalid memory\n");331return -ENOMEM;332}333334*curr_vol = fmdev->rx.volume / FM_RX_VOLUME_GAIN_STEP;335336return 0;337}338339/* To get current band's bottom and top frequency */340u32 fm_rx_get_band_freq_range(struct fmdev *fmdev, u32 *bot_freq, u32 *top_freq)341{342if (bot_freq != NULL)343*bot_freq = fmdev->rx.region.bot_freq;344345if (top_freq != NULL)346*top_freq = fmdev->rx.region.top_freq;347348return 0;349}350351/* Returns current band index (0-Europe/US; 1-Japan) */352void fm_rx_get_region(struct fmdev *fmdev, u8 *region)353{354*region = fmdev->rx.region.fm_band;355}356357/* Sets band (0-Europe/US; 1-Japan) */358u32 fm_rx_set_region(struct fmdev *fmdev, u8 region_to_set)359{360u16 payload;361u32 new_frq = 0;362u32 ret;363364if (region_to_set != FM_BAND_EUROPE_US &&365region_to_set != FM_BAND_JAPAN) {366fmerr("Invalid band\n");367return -EINVAL;368}369370if (fmdev->rx.region.fm_band == region_to_set) {371fmerr("Requested band is already configured\n");372return 0;373}374375/* Send cmd to set the band */376payload = (u16)region_to_set;377ret = fmc_send_cmd(fmdev, BAND_SET, REG_WR, &payload,378sizeof(payload), NULL, NULL);379if (ret < 0)380return ret;381382fmc_update_region_info(fmdev, region_to_set);383384/* Check whether current RX frequency is within band boundary */385if (fmdev->rx.freq < fmdev->rx.region.bot_freq)386new_frq = fmdev->rx.region.bot_freq;387else if (fmdev->rx.freq > fmdev->rx.region.top_freq)388new_frq = fmdev->rx.region.top_freq;389390if (new_frq) {391fmdbg("Current freq is not within band limit boundary,"392"switching to %d KHz\n", new_frq);393/* Current RX frequency is not in range. So, update it */394ret = fm_rx_set_freq(fmdev, new_frq);395}396397return ret;398}399400/* Reads current mute mode (Mute Off/On/Attenuate)*/401u32 fm_rx_get_mute_mode(struct fmdev *fmdev, u8 *curr_mute_mode)402{403if (fmdev->curr_fmmode != FM_MODE_RX)404return -EPERM;405406if (curr_mute_mode == NULL) {407fmerr("Invalid memory\n");408return -ENOMEM;409}410411*curr_mute_mode = fmdev->rx.mute_mode;412413return 0;414}415416static u32 fm_config_rx_mute_reg(struct fmdev *fmdev)417{418u16 payload, muteval;419u32 ret;420421muteval = 0;422switch (fmdev->rx.mute_mode) {423case FM_MUTE_ON:424muteval = FM_RX_AC_MUTE_MODE;425break;426427case FM_MUTE_OFF:428muteval = FM_RX_UNMUTE_MODE;429break;430431case FM_MUTE_ATTENUATE:432muteval = FM_RX_SOFT_MUTE_FORCE_MODE;433break;434}435if (fmdev->rx.rf_depend_mute == FM_RX_RF_DEPENDENT_MUTE_ON)436muteval |= FM_RX_RF_DEP_MODE;437else438muteval &= ~FM_RX_RF_DEP_MODE;439440payload = muteval;441ret = fmc_send_cmd(fmdev, MUTE_STATUS_SET, REG_WR, &payload,442sizeof(payload), NULL, NULL);443if (ret < 0)444return ret;445446return 0;447}448449/* Configures mute mode (Mute Off/On/Attenuate) */450u32 fm_rx_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset)451{452u8 org_state;453u32 ret;454455if (fmdev->rx.mute_mode == mute_mode_toset)456return 0;457458org_state = fmdev->rx.mute_mode;459fmdev->rx.mute_mode = mute_mode_toset;460461ret = fm_config_rx_mute_reg(fmdev);462if (ret < 0) {463fmdev->rx.mute_mode = org_state;464return ret;465}466467return 0;468}469470/* Gets RF dependent soft mute mode enable/disable status */471u32 fm_rx_get_rfdepend_softmute(struct fmdev *fmdev, u8 *curr_mute_mode)472{473if (fmdev->curr_fmmode != FM_MODE_RX)474return -EPERM;475476if (curr_mute_mode == NULL) {477fmerr("Invalid memory\n");478return -ENOMEM;479}480481*curr_mute_mode = fmdev->rx.rf_depend_mute;482483return 0;484}485486/* Sets RF dependent soft mute mode */487u32 fm_rx_set_rfdepend_softmute(struct fmdev *fmdev, u8 rfdepend_mute)488{489u8 org_state;490u32 ret;491492if (fmdev->curr_fmmode != FM_MODE_RX)493return -EPERM;494495if (rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_ON &&496rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_OFF) {497fmerr("Invalid RF dependent soft mute\n");498return -EINVAL;499}500if (fmdev->rx.rf_depend_mute == rfdepend_mute)501return 0;502503org_state = fmdev->rx.rf_depend_mute;504fmdev->rx.rf_depend_mute = rfdepend_mute;505506ret = fm_config_rx_mute_reg(fmdev);507if (ret < 0) {508fmdev->rx.rf_depend_mute = org_state;509return ret;510}511512return 0;513}514515/* Returns the signal strength level of current channel */516u32 fm_rx_get_rssi_level(struct fmdev *fmdev, u16 *rssilvl)517{518u16 curr_rssi_lel;519u32 resp_len;520u32 ret;521522if (rssilvl == NULL) {523fmerr("Invalid memory\n");524return -ENOMEM;525}526/* Read current RSSI level */527ret = fmc_send_cmd(fmdev, RSSI_LVL_GET, REG_RD, NULL, 2,528&curr_rssi_lel, &resp_len);529if (ret < 0)530return ret;531532*rssilvl = be16_to_cpu(curr_rssi_lel);533534return 0;535}536537/*538* Sets the signal strength level that once reached539* will stop the auto search process540*/541u32 fm_rx_set_rssi_threshold(struct fmdev *fmdev, short rssi_lvl_toset)542{543u16 payload;544u32 ret;545546if (rssi_lvl_toset < FM_RX_RSSI_THRESHOLD_MIN ||547rssi_lvl_toset > FM_RX_RSSI_THRESHOLD_MAX) {548fmerr("Invalid RSSI threshold level\n");549return -EINVAL;550}551payload = (u16)rssi_lvl_toset;552ret = fmc_send_cmd(fmdev, SEARCH_LVL_SET, REG_WR, &payload,553sizeof(payload), NULL, NULL);554if (ret < 0)555return ret;556557fmdev->rx.rssi_threshold = rssi_lvl_toset;558559return 0;560}561562/* Returns current RX RSSI threshold value */563u32 fm_rx_get_rssi_threshold(struct fmdev *fmdev, short *curr_rssi_lvl)564{565if (fmdev->curr_fmmode != FM_MODE_RX)566return -EPERM;567568if (curr_rssi_lvl == NULL) {569fmerr("Invalid memory\n");570return -ENOMEM;571}572573*curr_rssi_lvl = fmdev->rx.rssi_threshold;574575return 0;576}577578/* Sets RX stereo/mono modes */579u32 fm_rx_set_stereo_mono(struct fmdev *fmdev, u16 mode)580{581u16 payload;582u32 ret;583584if (mode != FM_STEREO_MODE && mode != FM_MONO_MODE) {585fmerr("Invalid mode\n");586return -EINVAL;587}588589/* Set stereo/mono mode */590payload = (u16)mode;591ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_WR, &payload,592sizeof(payload), NULL, NULL);593if (ret < 0)594return ret;595596/* Set stereo blending mode */597payload = FM_STEREO_SOFT_BLEND;598ret = fmc_send_cmd(fmdev, MOST_BLEND_SET, REG_WR, &payload,599sizeof(payload), NULL, NULL);600if (ret < 0)601return ret;602603return 0;604}605606/* Gets current RX stereo/mono mode */607u32 fm_rx_get_stereo_mono(struct fmdev *fmdev, u16 *mode)608{609u16 curr_mode;610u32 ret, resp_len;611612if (mode == NULL) {613fmerr("Invalid memory\n");614return -ENOMEM;615}616617ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_RD, NULL, 2,618&curr_mode, &resp_len);619if (ret < 0)620return ret;621622*mode = be16_to_cpu(curr_mode);623624return 0;625}626627/* Choose RX de-emphasis filter mode (50us/75us) */628u32 fm_rx_set_deemphasis_mode(struct fmdev *fmdev, u16 mode)629{630u16 payload;631u32 ret;632633if (fmdev->curr_fmmode != FM_MODE_RX)634return -EPERM;635636if (mode != FM_RX_EMPHASIS_FILTER_50_USEC &&637mode != FM_RX_EMPHASIS_FILTER_75_USEC) {638fmerr("Invalid rx de-emphasis mode (%d)\n", mode);639return -EINVAL;640}641642payload = mode;643ret = fmc_send_cmd(fmdev, DEMPH_MODE_SET, REG_WR, &payload,644sizeof(payload), NULL, NULL);645if (ret < 0)646return ret;647648fmdev->rx.deemphasis_mode = mode;649650return 0;651}652653/* Gets current RX de-emphasis filter mode */654u32 fm_rx_get_deemph_mode(struct fmdev *fmdev, u16 *curr_deemphasis_mode)655{656if (fmdev->curr_fmmode != FM_MODE_RX)657return -EPERM;658659if (curr_deemphasis_mode == NULL) {660fmerr("Invalid memory\n");661return -ENOMEM;662}663664*curr_deemphasis_mode = fmdev->rx.deemphasis_mode;665666return 0;667}668669/* Enable/Disable RX RDS */670u32 fm_rx_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis)671{672u16 payload;673u32 ret;674675if (rds_en_dis != FM_RDS_ENABLE && rds_en_dis != FM_RDS_DISABLE) {676fmerr("Invalid rds option\n");677return -EINVAL;678}679680if (rds_en_dis == FM_RDS_ENABLE681&& fmdev->rx.rds.flag == FM_RDS_DISABLE) {682/* Turn on RX RDS and RDS circuit */683payload = FM_RX_PWR_SET_FM_AND_RDS_BLK_ON;684ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload,685sizeof(payload), NULL, NULL);686if (ret < 0)687return ret;688689/* Clear and reset RDS FIFO */690payload = FM_RX_RDS_FLUSH_FIFO;691ret = fmc_send_cmd(fmdev, RDS_CNTRL_SET, REG_WR, &payload,692sizeof(payload), NULL, NULL);693if (ret < 0)694return ret;695696/* Read flags - just to clear any pending interrupts. */697ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2,698NULL, NULL);699if (ret < 0)700return ret;701702/* Set RDS FIFO threshold value */703payload = FM_RX_RDS_FIFO_THRESHOLD;704ret = fmc_send_cmd(fmdev, RDS_MEM_SET, REG_WR, &payload,705sizeof(payload), NULL, NULL);706if (ret < 0)707return ret;708709/* Enable RDS interrupt */710fmdev->irq_info.mask |= FM_RDS_EVENT;711payload = fmdev->irq_info.mask;712ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,713sizeof(payload), NULL, NULL);714if (ret < 0) {715fmdev->irq_info.mask &= ~FM_RDS_EVENT;716return ret;717}718719/* Update our local flag */720fmdev->rx.rds.flag = FM_RDS_ENABLE;721} else if (rds_en_dis == FM_RDS_DISABLE722&& fmdev->rx.rds.flag == FM_RDS_ENABLE) {723/* Turn off RX RDS */724payload = FM_RX_PWR_SET_FM_ON_RDS_OFF;725ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload,726sizeof(payload), NULL, NULL);727if (ret < 0)728return ret;729730/* Reset RDS pointers */731fmdev->rx.rds.last_blk_idx = 0;732fmdev->rx.rds.wr_idx = 0;733fmdev->rx.rds.rd_idx = 0;734fm_rx_reset_station_info(fmdev);735736/* Update RDS local cache */737fmdev->irq_info.mask &= ~(FM_RDS_EVENT);738fmdev->rx.rds.flag = FM_RDS_DISABLE;739}740741return 0;742}743744/* Returns current RX RDS enable/disable status */745u32 fm_rx_get_rds_mode(struct fmdev *fmdev, u8 *curr_rds_en_dis)746{747if (fmdev->curr_fmmode != FM_MODE_RX)748return -EPERM;749750if (curr_rds_en_dis == NULL) {751fmerr("Invalid memory\n");752return -ENOMEM;753}754755*curr_rds_en_dis = fmdev->rx.rds.flag;756757return 0;758}759760/* Sets RDS operation mode (RDS/RDBS) */761u32 fm_rx_set_rds_system(struct fmdev *fmdev, u8 rds_mode)762{763u16 payload;764u32 ret;765766if (fmdev->curr_fmmode != FM_MODE_RX)767return -EPERM;768769if (rds_mode != FM_RDS_SYSTEM_RDS && rds_mode != FM_RDS_SYSTEM_RBDS) {770fmerr("Invalid rds mode\n");771return -EINVAL;772}773/* Set RDS operation mode */774payload = (u16)rds_mode;775ret = fmc_send_cmd(fmdev, RDS_SYSTEM_SET, REG_WR, &payload,776sizeof(payload), NULL, NULL);777if (ret < 0)778return ret;779780fmdev->rx.rds_mode = rds_mode;781782return 0;783}784785/* Returns current RDS operation mode */786u32 fm_rx_get_rds_system(struct fmdev *fmdev, u8 *rds_mode)787{788if (fmdev->curr_fmmode != FM_MODE_RX)789return -EPERM;790791if (rds_mode == NULL) {792fmerr("Invalid memory\n");793return -ENOMEM;794}795796*rds_mode = fmdev->rx.rds_mode;797798return 0;799}800801/* Configures Alternate Frequency switch mode */802u32 fm_rx_set_af_switch(struct fmdev *fmdev, u8 af_mode)803{804u16 payload;805u32 ret;806807if (fmdev->curr_fmmode != FM_MODE_RX)808return -EPERM;809810if (af_mode != FM_RX_RDS_AF_SWITCH_MODE_ON &&811af_mode != FM_RX_RDS_AF_SWITCH_MODE_OFF) {812fmerr("Invalid af mode\n");813return -EINVAL;814}815/* Enable/disable low RSSI interrupt based on af_mode */816if (af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON)817fmdev->irq_info.mask |= FM_LEV_EVENT;818else819fmdev->irq_info.mask &= ~FM_LEV_EVENT;820821payload = fmdev->irq_info.mask;822ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,823sizeof(payload), NULL, NULL);824if (ret < 0)825return ret;826827fmdev->rx.af_mode = af_mode;828829return 0;830}831832/* Returns Alternate Frequency switch status */833u32 fm_rx_get_af_switch(struct fmdev *fmdev, u8 *af_mode)834{835if (fmdev->curr_fmmode != FM_MODE_RX)836return -EPERM;837838if (af_mode == NULL) {839fmerr("Invalid memory\n");840return -ENOMEM;841}842843*af_mode = fmdev->rx.af_mode;844845return 0;846}847848849