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