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 23 int 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 45 static 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 65 static 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 88 static 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 104 int 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 147 int 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 175 int 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 195 int 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 215 int 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 */ 232 static 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 */ 250 static 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 */ 288 int 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 */ 327 int 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.*/ 359 int 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 */ 379 int 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