linux/drivers/media/radio/wl128x/fmdrv_tx.c
<<
>>
Prefs
   1/*
   2 *  FM Driver for Connectivity chip of Texas Instruments.
   3 *  This sub-module of FM driver implements FM TX functionality.
   4 *
   5 *  Copyright (C) 2011 Texas Instruments
   6 *
   7 *  This program is free software; you can redistribute it and/or modify
   8 *  it under the terms of the GNU General Public License version 2 as
   9 *  published by the Free Software Foundation.
  10 *
  11 *  This program is distributed in the hope that it will be useful,
  12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *  GNU General Public License for more details.
  15 *
  16 */
  17
  18#include <linux/delay.h>
  19#include "fmdrv.h"
  20#include "fmdrv_common.h"
  21#include "fmdrv_tx.h"
  22
  23int fm_tx_set_stereo_mono(struct fmdev *fmdev, u16 mode)
  24{
  25        u16 payload;
  26        int ret;
  27
  28        if (fmdev->tx_data.aud_mode == mode)
  29                return 0;
  30
  31        fmdbg("stereo mode: %d\n", mode);
  32
  33        /* Set Stereo/Mono mode */
  34        payload = (1 - mode);
  35        ret = fmc_send_cmd(fmdev, MONO_SET, REG_WR, &payload,
  36                        sizeof(payload), NULL, NULL);
  37        if (ret < 0)
  38                return ret;
  39
  40        fmdev->tx_data.aud_mode = mode;
  41
  42        return ret;
  43}
  44
  45static int set_rds_text(struct fmdev *fmdev, u8 *rds_text)
  46{
  47        u16 payload;
  48        int ret;
  49
  50        ret = fmc_send_cmd(fmdev, RDS_DATA_SET, REG_WR, rds_text,
  51                        strlen(rds_text), NULL, NULL);
  52        if (ret < 0)
  53                return ret;
  54
  55        /* Scroll mode */
  56        payload = (u16)0x1;
  57        ret = fmc_send_cmd(fmdev, DISPLAY_MODE, REG_WR, &payload,
  58                        sizeof(payload), NULL, NULL);
  59        if (ret < 0)
  60                return ret;
  61
  62        return 0;
  63}
  64
  65static int set_rds_data_mode(struct fmdev *fmdev, u8 mode)
  66{
  67        u16 payload;
  68        int ret;
  69
  70        /* Setting unique PI TODO: how unique? */
  71        payload = (u16)0xcafe;
  72        ret = fmc_send_cmd(fmdev, PI_SET, REG_WR, &payload,
  73                        sizeof(payload), NULL, NULL);
  74        if (ret < 0)
  75                return ret;
  76
  77        /* Set decoder id */
  78        payload = (u16)0xa;
  79        ret = fmc_send_cmd(fmdev, DI_SET, REG_WR, &payload,
  80                        sizeof(payload), NULL, NULL);
  81        if (ret < 0)
  82                return ret;
  83
  84        /* TODO: RDS_MODE_GET? */
  85        return 0;
  86}
  87
  88static int set_rds_len(struct fmdev *fmdev, u8 type, u16 len)
  89{
  90        u16 payload;
  91        int ret;
  92
  93        len |= type << 8;
  94        payload = len;
  95        ret = fmc_send_cmd(fmdev, RDS_CONFIG_DATA_SET, REG_WR, &payload,
  96                        sizeof(payload), NULL, NULL);
  97        if (ret < 0)
  98                return ret;
  99
 100        /* TODO: LENGTH_GET? */
 101        return 0;
 102}
 103
 104int fm_tx_set_rds_mode(struct fmdev *fmdev, u8 rds_en_dis)
 105{
 106        u16 payload;
 107        int ret;
 108        u8 rds_text[] = "Zoom2\n";
 109
 110        fmdbg("rds_en_dis:%d(E:%d, D:%d)\n", rds_en_dis,
 111                   FM_RDS_ENABLE, FM_RDS_DISABLE);
 112
 113        if (rds_en_dis == FM_RDS_ENABLE) {
 114                /* Set RDS length */
 115                set_rds_len(fmdev, 0, strlen(rds_text));
 116
 117                /* Set RDS text */
 118                set_rds_text(fmdev, rds_text);
 119
 120                /* Set RDS mode */
 121                set_rds_data_mode(fmdev, 0x0);
 122        }
 123
 124        /* Send command to enable RDS */
 125        if (rds_en_dis == FM_RDS_ENABLE)
 126                payload = 0x01;
 127        else
 128                payload = 0x00;
 129
 130        ret = fmc_send_cmd(fmdev, RDS_DATA_ENB, REG_WR, &payload,
 131                        sizeof(payload), NULL, NULL);
 132        if (ret < 0)
 133                return ret;
 134
 135        if (rds_en_dis == FM_RDS_ENABLE) {
 136                /* Set RDS length */
 137                set_rds_len(fmdev, 0, strlen(rds_text));
 138
 139                /* Set RDS text */
 140                set_rds_text(fmdev, rds_text);
 141        }
 142        fmdev->tx_data.rds.flag = rds_en_dis;
 143
 144        return 0;
 145}
 146
 147int fm_tx_set_radio_text(struct fmdev *fmdev, u8 *rds_text, u8 rds_type)
 148{
 149        u16 payload;
 150        int ret;
 151
 152        if (fmdev->curr_fmmode != FM_MODE_TX)
 153                return -EPERM;
 154
 155        fm_tx_set_rds_mode(fmdev, 0);
 156
 157        /* Set RDS length */
 158        set_rds_len(fmdev, rds_type, strlen(rds_text));
 159
 160        /* Set RDS text */
 161        set_rds_text(fmdev, rds_text);
 162
 163        /* Set RDS mode */
 164        set_rds_data_mode(fmdev, 0x0);
 165
 166        payload = 1;
 167        ret = fmc_send_cmd(fmdev, RDS_DATA_ENB, REG_WR, &payload,
 168                        sizeof(payload), NULL, NULL);
 169        if (ret < 0)
 170                return ret;
 171
 172        return 0;
 173}
 174
 175int fm_tx_set_af(struct fmdev *fmdev, u32 af)
 176{
 177        u16 payload;
 178        int ret;
 179
 180        if (fmdev->curr_fmmode != FM_MODE_TX)
 181                return -EPERM;
 182
 183        fmdbg("AF: %d\n", af);
 184
 185        af = (af - 87500) / 100;
 186        payload = (u16)af;
 187        ret = fmc_send_cmd(fmdev, TA_SET, REG_WR, &payload,
 188                        sizeof(payload), NULL, NULL);
 189        if (ret < 0)
 190                return ret;
 191
 192        return 0;
 193}
 194
 195int fm_tx_set_region(struct fmdev *fmdev, u8 region)
 196{
 197        u16 payload;
 198        int ret;
 199
 200        if (region != FM_BAND_EUROPE_US && region != FM_BAND_JAPAN) {
 201                fmerr("Invalid band\n");
 202                return -EINVAL;
 203        }
 204
 205        /* Send command to set the band */
 206        payload = (u16)region;
 207        ret = fmc_send_cmd(fmdev, TX_BAND_SET, REG_WR, &payload,
 208                        sizeof(payload), NULL, NULL);
 209        if (ret < 0)
 210                return ret;
 211
 212        return 0;
 213}
 214
 215int fm_tx_set_mute_mode(struct fmdev *fmdev, u8 mute_mode_toset)
 216{
 217        u16 payload;
 218        int ret;
 219
 220        fmdbg("tx: mute mode %d\n", mute_mode_toset);
 221
 222        payload = mute_mode_toset;
 223        ret = fmc_send_cmd(fmdev, MUTE, REG_WR, &payload,
 224                        sizeof(payload), NULL, NULL);
 225        if (ret < 0)
 226                return ret;
 227
 228        return 0;
 229}
 230
 231/* Set TX Audio I/O */
 232static int set_audio_io(struct fmdev *fmdev)
 233{
 234        struct fmtx_data *tx = &fmdev->tx_data;
 235        u16 payload;
 236        int ret;
 237
 238        /* Set Audio I/O Enable */
 239        payload = tx->audio_io;
 240        ret = fmc_send_cmd(fmdev, AUDIO_IO_SET, REG_WR, &payload,
 241                        sizeof(payload), NULL, NULL);
 242        if (ret < 0)
 243                return ret;
 244
 245        /* TODO: is audio set? */
 246        return 0;
 247}
 248
 249/* Start TX Transmission */
 250static int enable_xmit(struct fmdev *fmdev, u8 new_xmit_state)
 251{
 252        struct fmtx_data *tx = &fmdev->tx_data;
 253        unsigned long timeleft;
 254        u16 payload;
 255        int ret;
 256
 257        /* Enable POWER_ENB interrupts */
 258        payload = FM_POW_ENB_EVENT;
 259        ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
 260                        sizeof(payload), NULL, NULL);
 261        if (ret < 0)
 262                return ret;
 263
 264        /* Set Power Enable */
 265        payload = new_xmit_state;
 266        ret = fmc_send_cmd(fmdev, POWER_ENB_SET, REG_WR, &payload,
 267                        sizeof(payload), NULL, NULL);
 268        if (ret < 0)
 269                return ret;
 270
 271        /* Wait for Power Enabled */
 272        init_completion(&fmdev->maintask_comp);
 273        timeleft = wait_for_completion_timeout(&fmdev->maintask_comp,
 274                        FM_DRV_TX_TIMEOUT);
 275        if (!timeleft) {
 276                fmerr("Timeout(%d sec),didn't get tune ended interrupt\n",
 277                           jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000);
 278                return -ETIMEDOUT;
 279        }
 280
 281        set_bit(FM_CORE_TX_XMITING, &fmdev->flag);
 282        tx->xmit_state = new_xmit_state;
 283
 284        return 0;
 285}
 286
 287/* Set TX power level */
 288int fm_tx_set_pwr_lvl(struct fmdev *fmdev, u8 new_pwr_lvl)
 289{
 290        u16 payload;
 291        struct fmtx_data *tx = &fmdev->tx_data;
 292        int ret;
 293
 294        if (fmdev->curr_fmmode != FM_MODE_TX)
 295                return -EPERM;
 296        fmdbg("tx: pwr_level_to_set %ld\n", (long int)new_pwr_lvl);
 297
 298        /* If the core isn't ready update global variable */
 299        if (!test_bit(FM_CORE_READY, &fmdev->flag)) {
 300                tx->pwr_lvl = new_pwr_lvl;
 301                return 0;
 302        }
 303
 304        /* Set power level: Application will specify power level value in
 305         * units of dB/uV, whereas range and step are specific to FM chip.
 306         * For TI's WL chips, convert application specified power level value
 307         * to chip specific value by subtracting 122 from it. Refer to TI FM
 308         * data sheet for details.
 309         * */
 310
 311        payload = (FM_PWR_LVL_HIGH - new_pwr_lvl);
 312        ret = fmc_send_cmd(fmdev, POWER_LEV_SET, REG_WR, &payload,
 313                        sizeof(payload), NULL, NULL);
 314        if (ret < 0)
 315                return ret;
 316
 317        /* TODO: is the power level set? */
 318        tx->pwr_lvl = new_pwr_lvl;
 319
 320        return 0;
 321}
 322
 323/*
 324 * Sets FM TX pre-emphasis filter value (OFF, 50us, or 75us)
 325 * Convert V4L2 specified filter values to chip specific filter values.
 326 */
 327int fm_tx_set_preemph_filter(struct fmdev *fmdev, u32 preemphasis)
 328{
 329        struct fmtx_data *tx = &fmdev->tx_data;
 330        u16 payload;
 331        int ret;
 332
 333        if (fmdev->curr_fmmode != FM_MODE_TX)
 334                return -EPERM;
 335
 336        switch (preemphasis) {
 337        case V4L2_PREEMPHASIS_DISABLED:
 338                payload = FM_TX_PREEMPH_OFF;
 339                break;
 340        case V4L2_PREEMPHASIS_50_uS:
 341                payload = FM_TX_PREEMPH_50US;
 342                break;
 343        case V4L2_PREEMPHASIS_75_uS:
 344                payload = FM_TX_PREEMPH_75US;
 345                break;
 346        }
 347
 348        ret = fmc_send_cmd(fmdev, PREMPH_SET, REG_WR, &payload,
 349                        sizeof(payload), NULL, NULL);
 350        if (ret < 0)
 351                return ret;
 352
 353        tx->preemph = payload;
 354
 355        return ret;
 356}
 357
 358/* Get the TX tuning capacitor value.*/
 359int fm_tx_get_tune_cap_val(struct fmdev *fmdev)
 360{
 361        u16 curr_val;
 362        u32 resp_len;
 363        int ret;
 364
 365        if (fmdev->curr_fmmode != FM_MODE_TX)
 366                return -EPERM;
 367
 368        ret = fmc_send_cmd(fmdev, READ_FMANT_TUNE_VALUE, REG_RD,
 369                        NULL, sizeof(curr_val), &curr_val, &resp_len);
 370        if (ret < 0)
 371                return ret;
 372
 373        curr_val = be16_to_cpu((__force __be16)curr_val);
 374
 375        return curr_val;
 376}
 377
 378/* Set TX Frequency */
 379int fm_tx_set_freq(struct fmdev *fmdev, u32 freq_to_set)
 380{
 381        struct fmtx_data *tx = &fmdev->tx_data;
 382        u16 payload, chanl_index;
 383        int ret;
 384
 385        if (test_bit(FM_CORE_TX_XMITING, &fmdev->flag)) {
 386                enable_xmit(fmdev, 0);
 387                clear_bit(FM_CORE_TX_XMITING, &fmdev->flag);
 388        }
 389
 390        /* Enable FR, BL interrupts */
 391        payload = (FM_FR_EVENT | FM_BL_EVENT);
 392        ret = fmc_send_cmd(fmdev, INT_MASK_SET, REG_WR, &payload,
 393                        sizeof(payload), NULL, NULL);
 394        if (ret < 0)
 395                return ret;
 396
 397        tx->tx_frq = (unsigned long)freq_to_set;
 398        fmdbg("tx: freq_to_set %ld\n", (long int)tx->tx_frq);
 399
 400        chanl_index = freq_to_set / 10;
 401
 402        /* Set current tuner channel */
 403        payload = chanl_index;
 404        ret = fmc_send_cmd(fmdev, CHANL_SET, REG_WR, &payload,
 405                        sizeof(payload), NULL, NULL);
 406        if (ret < 0)
 407                return ret;
 408
 409        fm_tx_set_pwr_lvl(fmdev, tx->pwr_lvl);
 410        fm_tx_set_preemph_filter(fmdev, tx->preemph);
 411
 412        tx->audio_io = 0x01;    /* I2S */
 413        set_audio_io(fmdev);
 414
 415        enable_xmit(fmdev, 0x01);       /* Enable transmission */
 416
 417        tx->aud_mode = FM_STEREO_MODE;
 418        tx->rds.flag = FM_RDS_DISABLE;
 419
 420        return 0;
 421}
 422
 423