linux/drivers/media/radio/wl128x/fmdrv_rx.c
<<
>>
Prefs
   1/*
   2 *  FM Driver for Connectivity chip of Texas Instruments.
   3 *  This sub-module of FM driver implements FM RX functionality.
   4 *
   5 *  Copyright (C) 2011 Texas Instruments
   6 *  Author: Raja Mani <raja_mani@ti.com>
   7 *  Author: Manjunatha Halli <manjunatha_halli@ti.com>
   8 *
   9 *  This program is free software; you can redistribute it and/or modify
  10 *  it under the terms of the GNU General Public License version 2 as
  11 *  published by the Free Software Foundation.
  12 *
  13 *  This program is distributed in the hope that it will be useful,
  14 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 *  GNU General Public License for more details.
  17 *
  18 */
  19
  20#include "fmdrv.h"
  21#include "fmdrv_common.h"
  22#include "fmdrv_rx.h"
  23
  24void fm_rx_reset_rds_cache(struct fmdev *fmdev)
  25{
  26        fmdev->rx.rds.flag = FM_RDS_DISABLE;
  27        fmdev->rx.rds.last_blk_idx = 0;
  28        fmdev->rx.rds.wr_idx = 0;
  29        fmdev->rx.rds.rd_idx = 0;
  30
  31        if (fmdev->rx.af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON)
  32                fmdev->irq_info.mask |= FM_LEV_EVENT;
  33}
  34
  35void fm_rx_reset_station_info(struct fmdev *fmdev)
  36{
  37        fmdev->rx.stat_info.picode = FM_NO_PI_CODE;
  38        fmdev->rx.stat_info.afcache_size = 0;
  39        fmdev->rx.stat_info.af_list_max = 0;
  40}
  41
  42int fm_rx_set_freq(struct fmdev *fmdev, u32 freq)
  43{
  44        unsigned long timeleft;
  45        u16 payload, curr_frq, intr_flag;
  46        u32 curr_frq_in_khz;
  47        u32 resp_len;
  48        int ret;
  49
  50        if (freq < fmdev->rx.region.bot_freq || freq > fmdev->rx.region.top_freq) {
  51                fmerr("Invalid frequency %d\n", freq);
  52                return -EINVAL;
  53        }
  54
  55        /* Set audio enable */
  56        payload = FM_RX_AUDIO_ENABLE_I2S_AND_ANALOG;
  57
  58        ret = fmc_send_cmd(fmdev, AUDIO_ENABLE_SET, REG_WR, &payload,
  59                        sizeof(payload), NULL, NULL);
  60        if (ret < 0)
  61                return ret;
  62
  63        /* Set hilo to automatic selection */
  64        payload = FM_RX_IFFREQ_HILO_AUTOMATIC;
  65        ret = fmc_send_cmd(fmdev, HILO_SET, REG_WR, &payload,
  66                        sizeof(payload), NULL, NULL);
  67        if (ret < 0)
  68                return ret;
  69
  70        /* Calculate frequency index and set*/
  71        payload = (freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
  72
  73        ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload,
  74                        sizeof(payload), NULL, NULL);
  75        if (ret < 0)
  76                return ret;
  77
  78        /* Read flags - just to clear any pending interrupts if we had */
  79        ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL);
  80        if (ret < 0)
  81                return ret;
  82
  83        /* Enable FR, BL interrupts */
  84        intr_flag = fmdev->irq_info.mask;
  85        fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT);
  86        payload = fmdev->irq_info.mask;
  87        ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
  88                        sizeof(payload), NULL, NULL);
  89        if (ret < 0)
  90                return ret;
  91
  92        /* Start tune */
  93        payload = FM_TUNER_PRESET_MODE;
  94        ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload,
  95                        sizeof(payload), NULL, NULL);
  96        if (ret < 0)
  97                goto exit;
  98
  99        /* Wait for tune ended interrupt */
 100        init_completion(&fmdev->maintask_comp);
 101        timeleft = wait_for_completion_timeout(&fmdev->maintask_comp,
 102                        FM_DRV_TX_TIMEOUT);
 103        if (!timeleft) {
 104                fmerr("Timeout(%d sec),didn't get tune ended int\n",
 105                           jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000);
 106                ret = -ETIMEDOUT;
 107                goto exit;
 108        }
 109
 110        /* Read freq back to confirm */
 111        ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2, &curr_frq, &resp_len);
 112        if (ret < 0)
 113                goto exit;
 114
 115        curr_frq = be16_to_cpu((__force __be16)curr_frq);
 116        curr_frq_in_khz = (fmdev->rx.region.bot_freq + ((u32)curr_frq * FM_FREQ_MUL));
 117
 118        if (curr_frq_in_khz != freq) {
 119                pr_info("Frequency is set to (%d) but requested freq is (%d)\n",
 120                        curr_frq_in_khz, freq);
 121        }
 122
 123        /* Update local cache  */
 124        fmdev->rx.freq = curr_frq_in_khz;
 125exit:
 126        /* Re-enable default FM interrupts */
 127        fmdev->irq_info.mask = intr_flag;
 128        payload = fmdev->irq_info.mask;
 129        ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
 130                        sizeof(payload), NULL, NULL);
 131        if (ret < 0)
 132                return ret;
 133
 134        /* Reset RDS cache and current station pointers */
 135        fm_rx_reset_rds_cache(fmdev);
 136        fm_rx_reset_station_info(fmdev);
 137
 138        return ret;
 139}
 140
 141static int fm_rx_set_channel_spacing(struct fmdev *fmdev, u32 spacing)
 142{
 143        u16 payload;
 144        int ret;
 145
 146        if (spacing > 0 && spacing <= 50000)
 147                spacing = FM_CHANNEL_SPACING_50KHZ;
 148        else if (spacing > 50000 && spacing <= 100000)
 149                spacing = FM_CHANNEL_SPACING_100KHZ;
 150        else
 151                spacing = FM_CHANNEL_SPACING_200KHZ;
 152
 153        /* set channel spacing */
 154        payload = spacing;
 155        ret = fmc_send_cmd(fmdev, CHANL_BW_SET, REG_WR, &payload,
 156                        sizeof(payload), NULL, NULL);
 157        if (ret < 0)
 158                return ret;
 159
 160        fmdev->rx.region.chanl_space = spacing * FM_FREQ_MUL;
 161
 162        return ret;
 163}
 164
 165int fm_rx_seek(struct fmdev *fmdev, u32 seek_upward,
 166                u32 wrap_around, u32 spacing)
 167{
 168        u32 resp_len;
 169        u16 curr_frq, next_frq, last_frq;
 170        u16 payload, int_reason, intr_flag;
 171        u16 offset, space_idx;
 172        unsigned long timeleft;
 173        int ret;
 174
 175        /* Set channel spacing */
 176        ret = fm_rx_set_channel_spacing(fmdev, spacing);
 177        if (ret < 0) {
 178                fmerr("Failed to set channel spacing\n");
 179                return ret;
 180        }
 181
 182        /* Read the current frequency from chip */
 183        ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL,
 184                        sizeof(curr_frq), &curr_frq, &resp_len);
 185        if (ret < 0)
 186                return ret;
 187
 188        curr_frq = be16_to_cpu((__force __be16)curr_frq);
 189        last_frq = (fmdev->rx.region.top_freq - fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
 190
 191        /* Check the offset in order to be aligned to the channel spacing*/
 192        space_idx = fmdev->rx.region.chanl_space / FM_FREQ_MUL;
 193        offset = curr_frq % space_idx;
 194
 195        next_frq = seek_upward ? curr_frq + space_idx /* Seek Up */ :
 196                                curr_frq - space_idx /* Seek Down */ ;
 197
 198        /*
 199         * Add or subtract offset in order to stay aligned to the channel
 200         * spacing.
 201         */
 202        if ((short)next_frq < 0)
 203                next_frq = last_frq - offset;
 204        else if (next_frq > last_frq)
 205                next_frq = 0 + offset;
 206
 207again:
 208        /* Set calculated next frequency to perform seek */
 209        payload = next_frq;
 210        ret = fmc_send_cmd(fmdev, FREQ_SET, REG_WR, &payload,
 211                        sizeof(payload), NULL, NULL);
 212        if (ret < 0)
 213                return ret;
 214
 215        /* Set search direction (0:Seek Down, 1:Seek Up) */
 216        payload = (seek_upward ? FM_SEARCH_DIRECTION_UP : FM_SEARCH_DIRECTION_DOWN);
 217        ret = fmc_send_cmd(fmdev, SEARCH_DIR_SET, REG_WR, &payload,
 218                        sizeof(payload), NULL, NULL);
 219        if (ret < 0)
 220                return ret;
 221
 222        /* Read flags - just to clear any pending interrupts if we had */
 223        ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2, NULL, NULL);
 224        if (ret < 0)
 225                return ret;
 226
 227        /* Enable FR, BL interrupts */
 228        intr_flag = fmdev->irq_info.mask;
 229        fmdev->irq_info.mask = (FM_FR_EVENT | FM_BL_EVENT);
 230        payload = fmdev->irq_info.mask;
 231        ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
 232                        sizeof(payload), NULL, NULL);
 233        if (ret < 0)
 234                return ret;
 235
 236        /* Start seek */
 237        payload = FM_TUNER_AUTONOMOUS_SEARCH_MODE;
 238        ret = fmc_send_cmd(fmdev, TUNER_MODE_SET, REG_WR, &payload,
 239                        sizeof(payload), NULL, NULL);
 240        if (ret < 0)
 241                return ret;
 242
 243        /* Wait for tune ended/band limit reached interrupt */
 244        init_completion(&fmdev->maintask_comp);
 245        timeleft = wait_for_completion_timeout(&fmdev->maintask_comp,
 246                        FM_DRV_RX_SEEK_TIMEOUT);
 247        if (!timeleft) {
 248                fmerr("Timeout(%d sec),didn't get tune ended int\n",
 249                           jiffies_to_msecs(FM_DRV_RX_SEEK_TIMEOUT) / 1000);
 250                return -ENODATA;
 251        }
 252
 253        int_reason = fmdev->irq_info.flag & (FM_TUNE_COMPLETE | FM_BAND_LIMIT);
 254
 255        /* Re-enable default FM interrupts */
 256        fmdev->irq_info.mask = intr_flag;
 257        payload = fmdev->irq_info.mask;
 258        ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
 259                        sizeof(payload), NULL, NULL);
 260        if (ret < 0)
 261                return ret;
 262
 263        if (int_reason & FM_BL_EVENT) {
 264                if (wrap_around == 0) {
 265                        fmdev->rx.freq = seek_upward ?
 266                                fmdev->rx.region.top_freq :
 267                                fmdev->rx.region.bot_freq;
 268                } else {
 269                        fmdev->rx.freq = seek_upward ?
 270                                fmdev->rx.region.bot_freq :
 271                                fmdev->rx.region.top_freq;
 272                        /* Calculate frequency index to write */
 273                        next_frq = (fmdev->rx.freq -
 274                                        fmdev->rx.region.bot_freq) / FM_FREQ_MUL;
 275                        goto again;
 276                }
 277        } else {
 278                /* Read freq to know where operation tune operation stopped */
 279                ret = fmc_send_cmd(fmdev, FREQ_SET, REG_RD, NULL, 2,
 280                                &curr_frq, &resp_len);
 281                if (ret < 0)
 282                        return ret;
 283
 284                curr_frq = be16_to_cpu((__force __be16)curr_frq);
 285                fmdev->rx.freq = (fmdev->rx.region.bot_freq +
 286                                ((u32)curr_frq * FM_FREQ_MUL));
 287
 288        }
 289        /* Reset RDS cache and current station pointers */
 290        fm_rx_reset_rds_cache(fmdev);
 291        fm_rx_reset_station_info(fmdev);
 292
 293        return ret;
 294}
 295
 296int fm_rx_set_volume(struct fmdev *fmdev, u16 vol_to_set)
 297{
 298        u16 payload;
 299        int ret;
 300
 301        if (fmdev->curr_fmmode != FM_MODE_RX)
 302                return -EPERM;
 303
 304        if (vol_to_set > FM_RX_VOLUME_MAX) {
 305                fmerr("Volume is not within(%d-%d) range\n",
 306                           FM_RX_VOLUME_MIN, FM_RX_VOLUME_MAX);
 307                return -EINVAL;
 308        }
 309        vol_to_set *= FM_RX_VOLUME_GAIN_STEP;
 310
 311        payload = vol_to_set;
 312        ret = fmc_send_cmd(fmdev, VOLUME_SET, REG_WR, &payload,
 313                        sizeof(payload), NULL, NULL);
 314        if (ret < 0)
 315                return ret;
 316
 317        fmdev->rx.volume = vol_to_set;
 318        return ret;
 319}
 320
 321/* Get volume */
 322int fm_rx_get_volume(struct fmdev *fmdev, u16 *curr_vol)
 323{
 324        if (fmdev->curr_fmmode != FM_MODE_RX)
 325                return -EPERM;
 326
 327        if (curr_vol == NULL) {
 328                fmerr("Invalid memory\n");
 329                return -ENOMEM;
 330        }
 331
 332        *curr_vol = fmdev->rx.volume / FM_RX_VOLUME_GAIN_STEP;
 333
 334        return 0;
 335}
 336
 337/* To get current band's bottom and top frequency */
 338int fm_rx_get_band_freq_range(struct fmdev *fmdev, u32 *bot_freq, u32 *top_freq)
 339{
 340        if (bot_freq != NULL)
 341                *bot_freq = fmdev->rx.region.bot_freq;
 342
 343        if (top_freq != NULL)
 344                *top_freq = fmdev->rx.region.top_freq;
 345
 346        return 0;
 347}
 348
 349/* Returns current band index (0-Europe/US; 1-Japan) */
 350void fm_rx_get_region(struct fmdev *fmdev, u8 *region)
 351{
 352        *region = fmdev->rx.region.fm_band;
 353}
 354
 355/* Sets band (0-Europe/US; 1-Japan) */
 356int fm_rx_set_region(struct fmdev *fmdev, u8 region_to_set)
 357{
 358        u16 payload;
 359        u32 new_frq = 0;
 360        int ret;
 361
 362        if (region_to_set != FM_BAND_EUROPE_US &&
 363            region_to_set != FM_BAND_JAPAN) {
 364                fmerr("Invalid band\n");
 365                return -EINVAL;
 366        }
 367
 368        if (fmdev->rx.region.fm_band == region_to_set) {
 369                fmerr("Requested band is already configured\n");
 370                return 0;
 371        }
 372
 373        /* Send cmd to set the band  */
 374        payload = (u16)region_to_set;
 375        ret = fmc_send_cmd(fmdev, BAND_SET, REG_WR, &payload,
 376                        sizeof(payload), NULL, NULL);
 377        if (ret < 0)
 378                return ret;
 379
 380        fmc_update_region_info(fmdev, region_to_set);
 381
 382        /* Check whether current RX frequency is within band boundary */
 383        if (fmdev->rx.freq < fmdev->rx.region.bot_freq)
 384                new_frq = fmdev->rx.region.bot_freq;
 385        else if (fmdev->rx.freq > fmdev->rx.region.top_freq)
 386                new_frq = fmdev->rx.region.top_freq;
 387
 388        if (new_frq) {
 389                fmdbg("Current freq is not within band limit boundary,switching to %d KHz\n",
 390                      new_frq);
 391                 /* Current RX frequency is not in range. So, update it */
 392                ret = fm_rx_set_freq(fmdev, new_frq);
 393        }
 394
 395        return ret;
 396}
 397
 398/* Reads current mute mode (Mute Off/On/Attenuate)*/
 399int fm_rx_get_mute_mode(struct fmdev *fmdev, u8 *curr_mute_mode)
 400{
 401        if (fmdev->curr_fmmode != FM_MODE_RX)
 402                return -EPERM;
 403
 404        if (curr_mute_mode == NULL) {
 405                fmerr("Invalid memory\n");
 406                return -ENOMEM;
 407        }
 408
 409        *curr_mute_mode = fmdev->rx.mute_mode;
 410
 411        return 0;
 412}
 413
 414static int fm_config_rx_mute_reg(struct fmdev *fmdev)
 415{
 416        u16 payload, muteval;
 417        int ret;
 418
 419        muteval = 0;
 420        switch (fmdev->rx.mute_mode) {
 421        case FM_MUTE_ON:
 422                muteval = FM_RX_AC_MUTE_MODE;
 423                break;
 424
 425        case FM_MUTE_OFF:
 426                muteval = FM_RX_UNMUTE_MODE;
 427                break;
 428
 429        case FM_MUTE_ATTENUATE:
 430                muteval = FM_RX_SOFT_MUTE_FORCE_MODE;
 431                break;
 432        }
 433        if (fmdev->rx.rf_depend_mute == FM_RX_RF_DEPENDENT_MUTE_ON)
 434                muteval |= FM_RX_RF_DEP_MODE;
 435        else
 436                muteval &= ~FM_RX_RF_DEP_MODE;
 437
 438        payload = muteval;
 439        ret = fmc_send_cmd(fmdev, MUTE_STATUS_SET, REG_WR, &payload,
 440                        sizeof(payload), NULL, NULL);
 441        if (ret < 0)
 442                return ret;
 443
 444        return 0;
 445}
 446
 447/* Configures mute mode (Mute Off/On/Attenuate) */
 448int fm_rx_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset)
 449{
 450        u8 org_state;
 451        int ret;
 452
 453        if (fmdev->rx.mute_mode == mute_mode_toset)
 454                return 0;
 455
 456        org_state = fmdev->rx.mute_mode;
 457        fmdev->rx.mute_mode = mute_mode_toset;
 458
 459        ret = fm_config_rx_mute_reg(fmdev);
 460        if (ret < 0) {
 461                fmdev->rx.mute_mode = org_state;
 462                return ret;
 463        }
 464
 465        return 0;
 466}
 467
 468/* Gets RF dependent soft mute mode enable/disable status */
 469int fm_rx_get_rfdepend_softmute(struct fmdev *fmdev, u8 *curr_mute_mode)
 470{
 471        if (fmdev->curr_fmmode != FM_MODE_RX)
 472                return -EPERM;
 473
 474        if (curr_mute_mode == NULL) {
 475                fmerr("Invalid memory\n");
 476                return -ENOMEM;
 477        }
 478
 479        *curr_mute_mode = fmdev->rx.rf_depend_mute;
 480
 481        return 0;
 482}
 483
 484/* Sets RF dependent soft mute mode */
 485int fm_rx_set_rfdepend_softmute(struct fmdev *fmdev, u8 rfdepend_mute)
 486{
 487        u8 org_state;
 488        int ret;
 489
 490        if (fmdev->curr_fmmode != FM_MODE_RX)
 491                return -EPERM;
 492
 493        if (rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_ON &&
 494            rfdepend_mute != FM_RX_RF_DEPENDENT_MUTE_OFF) {
 495                fmerr("Invalid RF dependent soft mute\n");
 496                return -EINVAL;
 497        }
 498        if (fmdev->rx.rf_depend_mute == rfdepend_mute)
 499                return 0;
 500
 501        org_state = fmdev->rx.rf_depend_mute;
 502        fmdev->rx.rf_depend_mute = rfdepend_mute;
 503
 504        ret = fm_config_rx_mute_reg(fmdev);
 505        if (ret < 0) {
 506                fmdev->rx.rf_depend_mute = org_state;
 507                return ret;
 508        }
 509
 510        return 0;
 511}
 512
 513/* Returns the signal strength level of current channel */
 514int fm_rx_get_rssi_level(struct fmdev *fmdev, u16 *rssilvl)
 515{
 516        __be16 curr_rssi_lel;
 517        u32 resp_len;
 518        int ret;
 519
 520        if (rssilvl == NULL) {
 521                fmerr("Invalid memory\n");
 522                return -ENOMEM;
 523        }
 524        /* Read current RSSI level */
 525        ret = fmc_send_cmd(fmdev, RSSI_LVL_GET, REG_RD, NULL, 2,
 526                        &curr_rssi_lel, &resp_len);
 527        if (ret < 0)
 528                return ret;
 529
 530        *rssilvl = be16_to_cpu(curr_rssi_lel);
 531
 532        return 0;
 533}
 534
 535/*
 536 * Sets the signal strength level that once reached
 537 * will stop the auto search process
 538 */
 539int fm_rx_set_rssi_threshold(struct fmdev *fmdev, short rssi_lvl_toset)
 540{
 541        u16 payload;
 542        int ret;
 543
 544        if (rssi_lvl_toset < FM_RX_RSSI_THRESHOLD_MIN ||
 545                        rssi_lvl_toset > FM_RX_RSSI_THRESHOLD_MAX) {
 546                fmerr("Invalid RSSI threshold level\n");
 547                return -EINVAL;
 548        }
 549        payload = (u16)rssi_lvl_toset;
 550        ret = fmc_send_cmd(fmdev, SEARCH_LVL_SET, REG_WR, &payload,
 551                        sizeof(payload), NULL, NULL);
 552        if (ret < 0)
 553                return ret;
 554
 555        fmdev->rx.rssi_threshold = rssi_lvl_toset;
 556
 557        return 0;
 558}
 559
 560/* Returns current RX RSSI threshold value */
 561int fm_rx_get_rssi_threshold(struct fmdev *fmdev, short *curr_rssi_lvl)
 562{
 563        if (fmdev->curr_fmmode != FM_MODE_RX)
 564                return -EPERM;
 565
 566        if (curr_rssi_lvl == NULL) {
 567                fmerr("Invalid memory\n");
 568                return -ENOMEM;
 569        }
 570
 571        *curr_rssi_lvl = fmdev->rx.rssi_threshold;
 572
 573        return 0;
 574}
 575
 576/* Sets RX stereo/mono modes */
 577int fm_rx_set_stereo_mono(struct fmdev *fmdev, u16 mode)
 578{
 579        u16 payload;
 580        int ret;
 581
 582        if (mode != FM_STEREO_MODE && mode != FM_MONO_MODE) {
 583                fmerr("Invalid mode\n");
 584                return -EINVAL;
 585        }
 586
 587        /* Set stereo/mono mode */
 588        payload = (u16)mode;
 589        ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_WR, &payload,
 590                        sizeof(payload), NULL, NULL);
 591        if (ret < 0)
 592                return ret;
 593
 594        /* Set stereo blending mode */
 595        payload = FM_STEREO_SOFT_BLEND;
 596        ret = fmc_send_cmd(fmdev, MOST_BLEND_SET, REG_WR, &payload,
 597                        sizeof(payload), NULL, NULL);
 598        if (ret < 0)
 599                return ret;
 600
 601        return 0;
 602}
 603
 604/* Gets current RX stereo/mono mode */
 605int fm_rx_get_stereo_mono(struct fmdev *fmdev, u16 *mode)
 606{
 607        __be16 curr_mode;
 608        u32 resp_len;
 609        int ret;
 610
 611        if (mode == NULL) {
 612                fmerr("Invalid memory\n");
 613                return -ENOMEM;
 614        }
 615
 616        ret = fmc_send_cmd(fmdev, MOST_MODE_SET, REG_RD, NULL, 2,
 617                        &curr_mode, &resp_len);
 618        if (ret < 0)
 619                return ret;
 620
 621        *mode = be16_to_cpu(curr_mode);
 622
 623        return 0;
 624}
 625
 626/* Choose RX de-emphasis filter mode (50us/75us) */
 627int fm_rx_set_deemphasis_mode(struct fmdev *fmdev, u16 mode)
 628{
 629        u16 payload;
 630        int ret;
 631
 632        if (fmdev->curr_fmmode != FM_MODE_RX)
 633                return -EPERM;
 634
 635        if (mode != FM_RX_EMPHASIS_FILTER_50_USEC &&
 636                        mode != FM_RX_EMPHASIS_FILTER_75_USEC) {
 637                fmerr("Invalid rx de-emphasis mode (%d)\n", mode);
 638                return -EINVAL;
 639        }
 640
 641        payload = mode;
 642        ret = fmc_send_cmd(fmdev, DEMPH_MODE_SET, REG_WR, &payload,
 643                        sizeof(payload), NULL, NULL);
 644        if (ret < 0)
 645                return ret;
 646
 647        fmdev->rx.deemphasis_mode = mode;
 648
 649        return 0;
 650}
 651
 652/* Gets current RX de-emphasis filter mode */
 653int fm_rx_get_deemph_mode(struct fmdev *fmdev, u16 *curr_deemphasis_mode)
 654{
 655        if (fmdev->curr_fmmode != FM_MODE_RX)
 656                return -EPERM;
 657
 658        if (curr_deemphasis_mode == NULL) {
 659                fmerr("Invalid memory\n");
 660                return -ENOMEM;
 661        }
 662
 663        *curr_deemphasis_mode = fmdev->rx.deemphasis_mode;
 664
 665        return 0;
 666}
 667
 668/* Enable/Disable RX RDS */
 669int fm_rx_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis)
 670{
 671        u16 payload;
 672        int ret;
 673
 674        if (rds_en_dis != FM_RDS_ENABLE && rds_en_dis != FM_RDS_DISABLE) {
 675                fmerr("Invalid rds option\n");
 676                return -EINVAL;
 677        }
 678
 679        if (rds_en_dis == FM_RDS_ENABLE
 680            && fmdev->rx.rds.flag == FM_RDS_DISABLE) {
 681                /* Turn on RX RDS and RDS circuit */
 682                payload = FM_RX_PWR_SET_FM_AND_RDS_BLK_ON;
 683                ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload,
 684                                sizeof(payload), NULL, NULL);
 685                if (ret < 0)
 686                        return ret;
 687
 688                /* Clear and reset RDS FIFO */
 689                payload = FM_RX_RDS_FLUSH_FIFO;
 690                ret = fmc_send_cmd(fmdev, RDS_CNTRL_SET, REG_WR, &payload,
 691                sizeof(payload), NULL, NULL);
 692                if (ret < 0)
 693                        return ret;
 694
 695                /* Read flags - just to clear any pending interrupts. */
 696                ret = fmc_send_cmd(fmdev, FLAG_GET, REG_RD, NULL, 2,
 697                                NULL, NULL);
 698                if (ret < 0)
 699                        return ret;
 700
 701                /* Set RDS FIFO threshold value */
 702                payload = FM_RX_RDS_FIFO_THRESHOLD;
 703                ret = fmc_send_cmd(fmdev, RDS_MEM_SET, REG_WR, &payload,
 704                sizeof(payload), NULL, NULL);
 705                if (ret < 0)
 706                        return ret;
 707
 708                /* Enable RDS interrupt */
 709                fmdev->irq_info.mask |= FM_RDS_EVENT;
 710                payload = fmdev->irq_info.mask;
 711                ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
 712                                sizeof(payload), NULL, NULL);
 713                if (ret < 0) {
 714                        fmdev->irq_info.mask &= ~FM_RDS_EVENT;
 715                        return ret;
 716                }
 717
 718                /* Update our local flag */
 719                fmdev->rx.rds.flag = FM_RDS_ENABLE;
 720        } else if (rds_en_dis == FM_RDS_DISABLE
 721                   && fmdev->rx.rds.flag == FM_RDS_ENABLE) {
 722                /* Turn off RX RDS */
 723                payload = FM_RX_PWR_SET_FM_ON_RDS_OFF;
 724                ret = fmc_send_cmd(fmdev, POWER_SET, REG_WR, &payload,
 725                                sizeof(payload), NULL, NULL);
 726                if (ret < 0)
 727                        return ret;
 728
 729                /* Reset RDS pointers */
 730                fmdev->rx.rds.last_blk_idx = 0;
 731                fmdev->rx.rds.wr_idx = 0;
 732                fmdev->rx.rds.rd_idx = 0;
 733                fm_rx_reset_station_info(fmdev);
 734
 735                /* Update RDS local cache */
 736                fmdev->irq_info.mask &= ~(FM_RDS_EVENT);
 737                fmdev->rx.rds.flag = FM_RDS_DISABLE;
 738        }
 739
 740        return 0;
 741}
 742
 743/* Returns current RX RDS enable/disable status */
 744int fm_rx_get_rds_mode(struct fmdev *fmdev, u8 *curr_rds_en_dis)
 745{
 746        if (fmdev->curr_fmmode != FM_MODE_RX)
 747                return -EPERM;
 748
 749        if (curr_rds_en_dis == NULL) {
 750                fmerr("Invalid memory\n");
 751                return -ENOMEM;
 752        }
 753
 754        *curr_rds_en_dis = fmdev->rx.rds.flag;
 755
 756        return 0;
 757}
 758
 759/* Sets RDS operation mode (RDS/RDBS) */
 760int fm_rx_set_rds_system(struct fmdev *fmdev, u8 rds_mode)
 761{
 762        u16 payload;
 763        int ret;
 764
 765        if (fmdev->curr_fmmode != FM_MODE_RX)
 766                return -EPERM;
 767
 768        if (rds_mode != FM_RDS_SYSTEM_RDS && rds_mode != FM_RDS_SYSTEM_RBDS) {
 769                fmerr("Invalid rds mode\n");
 770                return -EINVAL;
 771        }
 772        /* Set RDS operation mode */
 773        payload = (u16)rds_mode;
 774        ret = fmc_send_cmd(fmdev, RDS_SYSTEM_SET, REG_WR, &payload,
 775                        sizeof(payload), NULL, NULL);
 776        if (ret < 0)
 777                return ret;
 778
 779        fmdev->rx.rds_mode = rds_mode;
 780
 781        return 0;
 782}
 783
 784/* Configures Alternate Frequency switch mode */
 785int fm_rx_set_af_switch(struct fmdev *fmdev, u8 af_mode)
 786{
 787        u16 payload;
 788        int ret;
 789
 790        if (fmdev->curr_fmmode != FM_MODE_RX)
 791                return -EPERM;
 792
 793        if (af_mode != FM_RX_RDS_AF_SWITCH_MODE_ON &&
 794            af_mode != FM_RX_RDS_AF_SWITCH_MODE_OFF) {
 795                fmerr("Invalid af mode\n");
 796                return -EINVAL;
 797        }
 798        /* Enable/disable low RSSI interrupt based on af_mode */
 799        if (af_mode == FM_RX_RDS_AF_SWITCH_MODE_ON)
 800                fmdev->irq_info.mask |= FM_LEV_EVENT;
 801        else
 802                fmdev->irq_info.mask &= ~FM_LEV_EVENT;
 803
 804        payload = fmdev->irq_info.mask;
 805        ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
 806                        sizeof(payload), NULL, NULL);
 807        if (ret < 0)
 808                return ret;
 809
 810        fmdev->rx.af_mode = af_mode;
 811
 812        return 0;
 813}
 814
 815/* Returns Alternate Frequency switch status */
 816int fm_rx_get_af_switch(struct fmdev *fmdev, u8 *af_mode)
 817{
 818        if (fmdev->curr_fmmode != FM_MODE_RX)
 819                return -EPERM;
 820
 821        if (af_mode == NULL) {
 822                fmerr("Invalid memory\n");
 823                return -ENOMEM;
 824        }
 825
 826        *af_mode = fmdev->rx.af_mode;
 827
 828        return 0;
 829}
 830