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