1 /* 2 * drivers/mfd/si476x-cmd.c -- Subroutines implementing command 3 * protocol of si476x series of chips 4 * 5 * Copyright (C) 2012 Innovative Converged Devices(ICD) 6 * Copyright (C) 2013 Andrey Smirnov 7 * 8 * Author: Andrey Smirnov <andrew.smirnov@gmail.com> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; version 2 of the License. 13 * 14 * This program is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * General Public License for more details. 18 * 19 */ 20 21 #include <linux/module.h> 22 #include <linux/completion.h> 23 #include <linux/delay.h> 24 #include <linux/atomic.h> 25 #include <linux/i2c.h> 26 #include <linux/device.h> 27 #include <linux/gpio.h> 28 #include <linux/videodev2.h> 29 30 #include <linux/mfd/si476x-core.h> 31 32 #define msb(x) ((u8)((u16) x >> 8)) 33 #define lsb(x) ((u8)((u16) x & 0x00FF)) 34 35 36 37 #define CMD_POWER_UP 0x01 38 #define CMD_POWER_UP_A10_NRESP 1 39 #define CMD_POWER_UP_A10_NARGS 5 40 41 #define CMD_POWER_UP_A20_NRESP 1 42 #define CMD_POWER_UP_A20_NARGS 5 43 44 #define POWER_UP_DELAY_MS 110 45 46 #define CMD_POWER_DOWN 0x11 47 #define CMD_POWER_DOWN_A10_NRESP 1 48 49 #define CMD_POWER_DOWN_A20_NRESP 1 50 #define CMD_POWER_DOWN_A20_NARGS 1 51 52 #define CMD_FUNC_INFO 0x12 53 #define CMD_FUNC_INFO_NRESP 7 54 55 #define CMD_SET_PROPERTY 0x13 56 #define CMD_SET_PROPERTY_NARGS 5 57 #define CMD_SET_PROPERTY_NRESP 1 58 59 #define CMD_GET_PROPERTY 0x14 60 #define CMD_GET_PROPERTY_NARGS 3 61 #define CMD_GET_PROPERTY_NRESP 4 62 63 #define CMD_AGC_STATUS 0x17 64 #define CMD_AGC_STATUS_NRESP_A10 2 65 #define CMD_AGC_STATUS_NRESP_A20 6 66 67 #define PIN_CFG_BYTE(x) (0x7F & (x)) 68 #define CMD_DIG_AUDIO_PIN_CFG 0x18 69 #define CMD_DIG_AUDIO_PIN_CFG_NARGS 4 70 #define CMD_DIG_AUDIO_PIN_CFG_NRESP 5 71 72 #define CMD_ZIF_PIN_CFG 0x19 73 #define CMD_ZIF_PIN_CFG_NARGS 4 74 #define CMD_ZIF_PIN_CFG_NRESP 5 75 76 #define CMD_IC_LINK_GPO_CTL_PIN_CFG 0x1A 77 #define CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS 4 78 #define CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP 5 79 80 #define CMD_ANA_AUDIO_PIN_CFG 0x1B 81 #define CMD_ANA_AUDIO_PIN_CFG_NARGS 1 82 #define CMD_ANA_AUDIO_PIN_CFG_NRESP 2 83 84 #define CMD_INTB_PIN_CFG 0x1C 85 #define CMD_INTB_PIN_CFG_NARGS 2 86 #define CMD_INTB_PIN_CFG_A10_NRESP 6 87 #define CMD_INTB_PIN_CFG_A20_NRESP 3 88 89 #define CMD_FM_TUNE_FREQ 0x30 90 #define CMD_FM_TUNE_FREQ_A10_NARGS 5 91 #define CMD_FM_TUNE_FREQ_A20_NARGS 3 92 #define CMD_FM_TUNE_FREQ_NRESP 1 93 94 #define CMD_FM_RSQ_STATUS 0x32 95 96 #define CMD_FM_RSQ_STATUS_A10_NARGS 1 97 #define CMD_FM_RSQ_STATUS_A10_NRESP 17 98 #define CMD_FM_RSQ_STATUS_A30_NARGS 1 99 #define CMD_FM_RSQ_STATUS_A30_NRESP 23 100 101 102 #define CMD_FM_SEEK_START 0x31 103 #define CMD_FM_SEEK_START_NARGS 1 104 #define CMD_FM_SEEK_START_NRESP 1 105 106 #define CMD_FM_RDS_STATUS 0x36 107 #define CMD_FM_RDS_STATUS_NARGS 1 108 #define CMD_FM_RDS_STATUS_NRESP 16 109 110 #define CMD_FM_RDS_BLOCKCOUNT 0x37 111 #define CMD_FM_RDS_BLOCKCOUNT_NARGS 1 112 #define CMD_FM_RDS_BLOCKCOUNT_NRESP 8 113 114 #define CMD_FM_PHASE_DIVERSITY 0x38 115 #define CMD_FM_PHASE_DIVERSITY_NARGS 1 116 #define CMD_FM_PHASE_DIVERSITY_NRESP 1 117 118 #define CMD_FM_PHASE_DIV_STATUS 0x39 119 #define CMD_FM_PHASE_DIV_STATUS_NRESP 2 120 121 #define CMD_AM_TUNE_FREQ 0x40 122 #define CMD_AM_TUNE_FREQ_NARGS 3 123 #define CMD_AM_TUNE_FREQ_NRESP 1 124 125 #define CMD_AM_RSQ_STATUS 0x42 126 #define CMD_AM_RSQ_STATUS_NARGS 1 127 #define CMD_AM_RSQ_STATUS_NRESP 13 128 129 #define CMD_AM_SEEK_START 0x41 130 #define CMD_AM_SEEK_START_NARGS 1 131 #define CMD_AM_SEEK_START_NRESP 1 132 133 134 #define CMD_AM_ACF_STATUS 0x45 135 #define CMD_AM_ACF_STATUS_NRESP 6 136 #define CMD_AM_ACF_STATUS_NARGS 1 137 138 #define CMD_FM_ACF_STATUS 0x35 139 #define CMD_FM_ACF_STATUS_NRESP 8 140 #define CMD_FM_ACF_STATUS_NARGS 1 141 142 #define CMD_MAX_ARGS_COUNT (10) 143 144 145 enum si476x_acf_status_report_bits { 146 SI476X_ACF_BLEND_INT = (1 << 4), 147 SI476X_ACF_HIBLEND_INT = (1 << 3), 148 SI476X_ACF_HICUT_INT = (1 << 2), 149 SI476X_ACF_CHBW_INT = (1 << 1), 150 SI476X_ACF_SOFTMUTE_INT = (1 << 0), 151 152 SI476X_ACF_SMUTE = (1 << 0), 153 SI476X_ACF_SMATTN = 0b11111, 154 SI476X_ACF_PILOT = (1 << 7), 155 SI476X_ACF_STBLEND = ~SI476X_ACF_PILOT, 156 }; 157 158 enum si476x_agc_status_report_bits { 159 SI476X_AGC_MXHI = (1 << 5), 160 SI476X_AGC_MXLO = (1 << 4), 161 SI476X_AGC_LNAHI = (1 << 3), 162 SI476X_AGC_LNALO = (1 << 2), 163 }; 164 165 enum si476x_errors { 166 SI476X_ERR_BAD_COMMAND = 0x10, 167 SI476X_ERR_BAD_ARG1 = 0x11, 168 SI476X_ERR_BAD_ARG2 = 0x12, 169 SI476X_ERR_BAD_ARG3 = 0x13, 170 SI476X_ERR_BAD_ARG4 = 0x14, 171 SI476X_ERR_BUSY = 0x18, 172 SI476X_ERR_BAD_INTERNAL_MEMORY = 0x20, 173 SI476X_ERR_BAD_PATCH = 0x30, 174 SI476X_ERR_BAD_BOOT_MODE = 0x31, 175 SI476X_ERR_BAD_PROPERTY = 0x40, 176 }; 177 178 static int si476x_core_parse_and_nag_about_error(struct si476x_core *core) 179 { 180 int err; 181 char *cause; 182 u8 buffer[2]; 183 184 if (core->revision != SI476X_REVISION_A10) { 185 err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, 186 buffer, sizeof(buffer)); 187 if (err == sizeof(buffer)) { 188 switch (buffer[1]) { 189 case SI476X_ERR_BAD_COMMAND: 190 cause = "Bad command"; 191 err = -EINVAL; 192 break; 193 case SI476X_ERR_BAD_ARG1: 194 cause = "Bad argument #1"; 195 err = -EINVAL; 196 break; 197 case SI476X_ERR_BAD_ARG2: 198 cause = "Bad argument #2"; 199 err = -EINVAL; 200 break; 201 case SI476X_ERR_BAD_ARG3: 202 cause = "Bad argument #3"; 203 err = -EINVAL; 204 break; 205 case SI476X_ERR_BAD_ARG4: 206 cause = "Bad argument #4"; 207 err = -EINVAL; 208 break; 209 case SI476X_ERR_BUSY: 210 cause = "Chip is busy"; 211 err = -EBUSY; 212 break; 213 case SI476X_ERR_BAD_INTERNAL_MEMORY: 214 cause = "Bad internal memory"; 215 err = -EIO; 216 break; 217 case SI476X_ERR_BAD_PATCH: 218 cause = "Bad patch"; 219 err = -EINVAL; 220 break; 221 case SI476X_ERR_BAD_BOOT_MODE: 222 cause = "Bad boot mode"; 223 err = -EINVAL; 224 break; 225 case SI476X_ERR_BAD_PROPERTY: 226 cause = "Bad property"; 227 err = -EINVAL; 228 break; 229 default: 230 cause = "Unknown"; 231 err = -EIO; 232 } 233 234 dev_err(&core->client->dev, 235 "[Chip error status]: %s\n", cause); 236 } else { 237 dev_err(&core->client->dev, 238 "Failed to fetch error code\n"); 239 err = (err >= 0) ? -EIO : err; 240 } 241 } else { 242 err = -EIO; 243 } 244 245 return err; 246 } 247 248 /** 249 * si476x_core_send_command() - sends a command to si476x and waits its 250 * response 251 * @core: si476x_device structure for the device we are 252 * communicating with 253 * @command: command id 254 * @args: command arguments we are sending 255 * @argn: actual size of @args 256 * @response: buffer to place the expected response from the device 257 * @respn: actual size of @response 258 * @usecs: amount of time to wait before reading the response (in 259 * usecs) 260 * 261 * Function returns 0 on succsess and negative error code on 262 * failure 263 */ 264 static int si476x_core_send_command(struct si476x_core *core, 265 const u8 command, 266 const u8 args[], 267 const int argn, 268 u8 resp[], 269 const int respn, 270 const int usecs) 271 { 272 struct i2c_client *client = core->client; 273 int err; 274 u8 data[CMD_MAX_ARGS_COUNT + 1]; 275 276 if (argn > CMD_MAX_ARGS_COUNT) { 277 err = -ENOMEM; 278 goto exit; 279 } 280 281 if (!client->adapter) { 282 err = -ENODEV; 283 goto exit; 284 } 285 286 /* First send the command and its arguments */ 287 data[0] = command; 288 memcpy(&data[1], args, argn); 289 dev_dbg(&client->dev, "Command:\n %*ph\n", argn + 1, data); 290 291 err = si476x_core_i2c_xfer(core, SI476X_I2C_SEND, 292 (char *) data, argn + 1); 293 if (err != argn + 1) { 294 dev_err(&core->client->dev, 295 "Error while sending command 0x%02x\n", 296 command); 297 err = (err >= 0) ? -EIO : err; 298 goto exit; 299 } 300 /* Set CTS to zero only after the command is send to avoid 301 * possible racing conditions when working in polling mode */ 302 atomic_set(&core->cts, 0); 303 304 /* if (unlikely(command == CMD_POWER_DOWN) */ 305 if (!wait_event_timeout(core->command, 306 atomic_read(&core->cts), 307 usecs_to_jiffies(usecs) + 1)) 308 dev_warn(&core->client->dev, 309 "(%s) [CMD 0x%02x] Answer timeout.\n", 310 __func__, command); 311 312 /* 313 When working in polling mode, for some reason the tuner will 314 report CTS bit as being set in the first status byte read, 315 but all the consequtive ones will return zeros until the 316 tuner is actually completed the POWER_UP command. To 317 workaround that we wait for second CTS to be reported 318 */ 319 if (unlikely(!core->client->irq && command == CMD_POWER_UP)) { 320 if (!wait_event_timeout(core->command, 321 atomic_read(&core->cts), 322 usecs_to_jiffies(usecs) + 1)) 323 dev_warn(&core->client->dev, 324 "(%s) Power up took too much time.\n", 325 __func__); 326 } 327 328 /* Then get the response */ 329 err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, resp, respn); 330 if (err != respn) { 331 dev_err(&core->client->dev, 332 "Error while reading response for command 0x%02x\n", 333 command); 334 err = (err >= 0) ? -EIO : err; 335 goto exit; 336 } 337 dev_dbg(&client->dev, "Response:\n %*ph\n", respn, resp); 338 339 err = 0; 340 341 if (resp[0] & SI476X_ERR) { 342 dev_err(&core->client->dev, 343 "[CMD 0x%02x] Chip set error flag\n", command); 344 err = si476x_core_parse_and_nag_about_error(core); 345 goto exit; 346 } 347 348 if (!(resp[0] & SI476X_CTS)) 349 err = -EBUSY; 350 exit: 351 return err; 352 } 353 354 static int si476x_cmd_clear_stc(struct si476x_core *core) 355 { 356 int err; 357 struct si476x_rsq_status_args args = { 358 .primary = false, 359 .rsqack = false, 360 .attune = false, 361 .cancel = false, 362 .stcack = true, 363 }; 364 365 switch (core->power_up_parameters.func) { 366 case SI476X_FUNC_FM_RECEIVER: 367 err = si476x_core_cmd_fm_rsq_status(core, &args, NULL); 368 break; 369 case SI476X_FUNC_AM_RECEIVER: 370 err = si476x_core_cmd_am_rsq_status(core, &args, NULL); 371 break; 372 default: 373 err = -EINVAL; 374 } 375 376 return err; 377 } 378 379 static int si476x_cmd_tune_seek_freq(struct si476x_core *core, 380 uint8_t cmd, 381 const uint8_t args[], size_t argn, 382 uint8_t *resp, size_t respn) 383 { 384 int err; 385 386 387 atomic_set(&core->stc, 0); 388 err = si476x_core_send_command(core, cmd, args, argn, resp, respn, 389 SI476X_TIMEOUT_TUNE); 390 if (!err) { 391 wait_event_killable(core->tuning, 392 atomic_read(&core->stc)); 393 si476x_cmd_clear_stc(core); 394 } 395 396 return err; 397 } 398 399 /** 400 * si476x_cmd_func_info() - send 'FUNC_INFO' command to the device 401 * @core: device to send the command to 402 * @info: struct si476x_func_info to fill all the information 403 * returned by the command 404 * 405 * The command requests the firmware and patch version for currently 406 * loaded firmware (dependent on the function of the device FM/AM/WB) 407 * 408 * Function returns 0 on succsess and negative error code on 409 * failure 410 */ 411 int si476x_core_cmd_func_info(struct si476x_core *core, 412 struct si476x_func_info *info) 413 { 414 int err; 415 u8 resp[CMD_FUNC_INFO_NRESP]; 416 417 err = si476x_core_send_command(core, CMD_FUNC_INFO, 418 NULL, 0, 419 resp, ARRAY_SIZE(resp), 420 SI476X_DEFAULT_TIMEOUT); 421 422 info->firmware.major = resp[1]; 423 info->firmware.minor[0] = resp[2]; 424 info->firmware.minor[1] = resp[3]; 425 426 info->patch_id = ((u16) resp[4] << 8) | resp[5]; 427 info->func = resp[6]; 428 429 return err; 430 } 431 EXPORT_SYMBOL_GPL(si476x_core_cmd_func_info); 432 433 /** 434 * si476x_cmd_set_property() - send 'SET_PROPERTY' command to the device 435 * @core: device to send the command to 436 * @property: property address 437 * @value: property value 438 * 439 * Function returns 0 on succsess and negative error code on 440 * failure 441 */ 442 int si476x_core_cmd_set_property(struct si476x_core *core, 443 u16 property, u16 value) 444 { 445 u8 resp[CMD_SET_PROPERTY_NRESP]; 446 const u8 args[CMD_SET_PROPERTY_NARGS] = { 447 0x00, 448 msb(property), 449 lsb(property), 450 msb(value), 451 lsb(value), 452 }; 453 454 return si476x_core_send_command(core, CMD_SET_PROPERTY, 455 args, ARRAY_SIZE(args), 456 resp, ARRAY_SIZE(resp), 457 SI476X_DEFAULT_TIMEOUT); 458 } 459 EXPORT_SYMBOL_GPL(si476x_core_cmd_set_property); 460 461 /** 462 * si476x_cmd_get_property() - send 'GET_PROPERTY' command to the device 463 * @core: device to send the command to 464 * @property: property address 465 * 466 * Function return the value of property as u16 on success or a 467 * negative error on failure 468 */ 469 int si476x_core_cmd_get_property(struct si476x_core *core, u16 property) 470 { 471 int err; 472 u8 resp[CMD_GET_PROPERTY_NRESP]; 473 const u8 args[CMD_GET_PROPERTY_NARGS] = { 474 0x00, 475 msb(property), 476 lsb(property), 477 }; 478 479 err = si476x_core_send_command(core, CMD_GET_PROPERTY, 480 args, ARRAY_SIZE(args), 481 resp, ARRAY_SIZE(resp), 482 SI476X_DEFAULT_TIMEOUT); 483 if (err < 0) 484 return err; 485 else 486 return be16_to_cpup((__be16 *)(resp + 2)); 487 } 488 EXPORT_SYMBOL_GPL(si476x_core_cmd_get_property); 489 490 /** 491 * si476x_cmd_dig_audio_pin_cfg() - send 'DIG_AUDIO_PIN_CFG' command to 492 * the device 493 * @core: device to send the command to 494 * @dclk: DCLK pin function configuration: 495 * #SI476X_DCLK_NOOP - do not modify the behaviour 496 * #SI476X_DCLK_TRISTATE - put the pin in tristate condition, 497 * enable 1MOhm pulldown 498 * #SI476X_DCLK_DAUDIO - set the pin to be a part of digital 499 * audio interface 500 * @dfs: DFS pin function configuration: 501 * #SI476X_DFS_NOOP - do not modify the behaviour 502 * #SI476X_DFS_TRISTATE - put the pin in tristate condition, 503 * enable 1MOhm pulldown 504 * SI476X_DFS_DAUDIO - set the pin to be a part of digital 505 * audio interface 506 * @dout - DOUT pin function configuration: 507 * SI476X_DOUT_NOOP - do not modify the behaviour 508 * SI476X_DOUT_TRISTATE - put the pin in tristate condition, 509 * enable 1MOhm pulldown 510 * SI476X_DOUT_I2S_OUTPUT - set this pin to be digital out on I2S 511 * port 1 512 * SI476X_DOUT_I2S_INPUT - set this pin to be digital in on I2S 513 * port 1 514 * @xout - XOUT pin function configuration: 515 * SI476X_XOUT_NOOP - do not modify the behaviour 516 * SI476X_XOUT_TRISTATE - put the pin in tristate condition, 517 * enable 1MOhm pulldown 518 * SI476X_XOUT_I2S_INPUT - set this pin to be digital in on I2S 519 * port 1 520 * SI476X_XOUT_MODE_SELECT - set this pin to be the input that 521 * selects the mode of the I2S audio 522 * combiner (analog or HD) 523 * [SI4761/63/65/67 Only] 524 * 525 * Function returns 0 on success and negative error code on failure 526 */ 527 int si476x_core_cmd_dig_audio_pin_cfg(struct si476x_core *core, 528 enum si476x_dclk_config dclk, 529 enum si476x_dfs_config dfs, 530 enum si476x_dout_config dout, 531 enum si476x_xout_config xout) 532 { 533 u8 resp[CMD_DIG_AUDIO_PIN_CFG_NRESP]; 534 const u8 args[CMD_DIG_AUDIO_PIN_CFG_NARGS] = { 535 PIN_CFG_BYTE(dclk), 536 PIN_CFG_BYTE(dfs), 537 PIN_CFG_BYTE(dout), 538 PIN_CFG_BYTE(xout), 539 }; 540 541 return si476x_core_send_command(core, CMD_DIG_AUDIO_PIN_CFG, 542 args, ARRAY_SIZE(args), 543 resp, ARRAY_SIZE(resp), 544 SI476X_DEFAULT_TIMEOUT); 545 } 546 EXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg); 547 548 /** 549 * si476x_cmd_zif_pin_cfg - send 'ZIF_PIN_CFG_COMMAND' 550 * @core - device to send the command to 551 * @iqclk - IQCL pin function configuration: 552 * SI476X_IQCLK_NOOP - do not modify the behaviour 553 * SI476X_IQCLK_TRISTATE - put the pin in tristate condition, 554 * enable 1MOhm pulldown 555 * SI476X_IQCLK_IQ - set pin to be a part of I/Q interace 556 * in master mode 557 * @iqfs - IQFS pin function configuration: 558 * SI476X_IQFS_NOOP - do not modify the behaviour 559 * SI476X_IQFS_TRISTATE - put the pin in tristate condition, 560 * enable 1MOhm pulldown 561 * SI476X_IQFS_IQ - set pin to be a part of I/Q interace 562 * in master mode 563 * @iout - IOUT pin function configuration: 564 * SI476X_IOUT_NOOP - do not modify the behaviour 565 * SI476X_IOUT_TRISTATE - put the pin in tristate condition, 566 * enable 1MOhm pulldown 567 * SI476X_IOUT_OUTPUT - set pin to be I out 568 * @qout - QOUT pin function configuration: 569 * SI476X_QOUT_NOOP - do not modify the behaviour 570 * SI476X_QOUT_TRISTATE - put the pin in tristate condition, 571 * enable 1MOhm pulldown 572 * SI476X_QOUT_OUTPUT - set pin to be Q out 573 * 574 * Function returns 0 on success and negative error code on failure 575 */ 576 int si476x_core_cmd_zif_pin_cfg(struct si476x_core *core, 577 enum si476x_iqclk_config iqclk, 578 enum si476x_iqfs_config iqfs, 579 enum si476x_iout_config iout, 580 enum si476x_qout_config qout) 581 { 582 u8 resp[CMD_ZIF_PIN_CFG_NRESP]; 583 const u8 args[CMD_ZIF_PIN_CFG_NARGS] = { 584 PIN_CFG_BYTE(iqclk), 585 PIN_CFG_BYTE(iqfs), 586 PIN_CFG_BYTE(iout), 587 PIN_CFG_BYTE(qout), 588 }; 589 590 return si476x_core_send_command(core, CMD_ZIF_PIN_CFG, 591 args, ARRAY_SIZE(args), 592 resp, ARRAY_SIZE(resp), 593 SI476X_DEFAULT_TIMEOUT); 594 } 595 EXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg); 596 597 /** 598 * si476x_cmd_ic_link_gpo_ctl_pin_cfg - send 599 * 'IC_LINK_GPIO_CTL_PIN_CFG' comand to the device 600 * @core - device to send the command to 601 * @icin - ICIN pin function configuration: 602 * SI476X_ICIN_NOOP - do not modify the behaviour 603 * SI476X_ICIN_TRISTATE - put the pin in tristate condition, 604 * enable 1MOhm pulldown 605 * SI476X_ICIN_GPO1_HIGH - set pin to be an output, drive it high 606 * SI476X_ICIN_GPO1_LOW - set pin to be an output, drive it low 607 * SI476X_ICIN_IC_LINK - set the pin to be a part of Inter-Chip link 608 * @icip - ICIP pin function configuration: 609 * SI476X_ICIP_NOOP - do not modify the behaviour 610 * SI476X_ICIP_TRISTATE - put the pin in tristate condition, 611 * enable 1MOhm pulldown 612 * SI476X_ICIP_GPO1_HIGH - set pin to be an output, drive it high 613 * SI476X_ICIP_GPO1_LOW - set pin to be an output, drive it low 614 * SI476X_ICIP_IC_LINK - set the pin to be a part of Inter-Chip link 615 * @icon - ICON pin function configuration: 616 * SI476X_ICON_NOOP - do not modify the behaviour 617 * SI476X_ICON_TRISTATE - put the pin in tristate condition, 618 * enable 1MOhm pulldown 619 * SI476X_ICON_I2S - set the pin to be a part of audio 620 * interface in slave mode (DCLK) 621 * SI476X_ICON_IC_LINK - set the pin to be a part of Inter-Chip link 622 * @icop - ICOP pin function configuration: 623 * SI476X_ICOP_NOOP - do not modify the behaviour 624 * SI476X_ICOP_TRISTATE - put the pin in tristate condition, 625 * enable 1MOhm pulldown 626 * SI476X_ICOP_I2S - set the pin to be a part of audio 627 * interface in slave mode (DOUT) 628 * [Si4761/63/65/67 Only] 629 * SI476X_ICOP_IC_LINK - set the pin to be a part of Inter-Chip link 630 * 631 * Function returns 0 on success and negative error code on failure 632 */ 633 int si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(struct si476x_core *core, 634 enum si476x_icin_config icin, 635 enum si476x_icip_config icip, 636 enum si476x_icon_config icon, 637 enum si476x_icop_config icop) 638 { 639 u8 resp[CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP]; 640 const u8 args[CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS] = { 641 PIN_CFG_BYTE(icin), 642 PIN_CFG_BYTE(icip), 643 PIN_CFG_BYTE(icon), 644 PIN_CFG_BYTE(icop), 645 }; 646 647 return si476x_core_send_command(core, CMD_IC_LINK_GPO_CTL_PIN_CFG, 648 args, ARRAY_SIZE(args), 649 resp, ARRAY_SIZE(resp), 650 SI476X_DEFAULT_TIMEOUT); 651 } 652 EXPORT_SYMBOL_GPL(si476x_core_cmd_ic_link_gpo_ctl_pin_cfg); 653 654 /** 655 * si476x_cmd_ana_audio_pin_cfg - send 'ANA_AUDIO_PIN_CFG' to the 656 * device 657 * @core - device to send the command to 658 * @lrout - LROUT pin function configuration: 659 * SI476X_LROUT_NOOP - do not modify the behaviour 660 * SI476X_LROUT_TRISTATE - put the pin in tristate condition, 661 * enable 1MOhm pulldown 662 * SI476X_LROUT_AUDIO - set pin to be audio output 663 * SI476X_LROUT_MPX - set pin to be MPX output 664 * 665 * Function returns 0 on success and negative error code on failure 666 */ 667 int si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *core, 668 enum si476x_lrout_config lrout) 669 { 670 u8 resp[CMD_ANA_AUDIO_PIN_CFG_NRESP]; 671 const u8 args[CMD_ANA_AUDIO_PIN_CFG_NARGS] = { 672 PIN_CFG_BYTE(lrout), 673 }; 674 675 return si476x_core_send_command(core, CMD_ANA_AUDIO_PIN_CFG, 676 args, ARRAY_SIZE(args), 677 resp, ARRAY_SIZE(resp), 678 SI476X_DEFAULT_TIMEOUT); 679 } 680 EXPORT_SYMBOL_GPL(si476x_core_cmd_ana_audio_pin_cfg); 681 682 683 /** 684 * si476x_cmd_intb_pin_cfg - send 'INTB_PIN_CFG' command to the device 685 * @core - device to send the command to 686 * @intb - INTB pin function configuration: 687 * SI476X_INTB_NOOP - do not modify the behaviour 688 * SI476X_INTB_TRISTATE - put the pin in tristate condition, 689 * enable 1MOhm pulldown 690 * SI476X_INTB_DAUDIO - set pin to be a part of digital 691 * audio interface in slave mode 692 * SI476X_INTB_IRQ - set pin to be an interrupt request line 693 * @a1 - A1 pin function configuration: 694 * SI476X_A1_NOOP - do not modify the behaviour 695 * SI476X_A1_TRISTATE - put the pin in tristate condition, 696 * enable 1MOhm pulldown 697 * SI476X_A1_IRQ - set pin to be an interrupt request line 698 * 699 * Function returns 0 on success and negative error code on failure 700 */ 701 static int si476x_core_cmd_intb_pin_cfg_a10(struct si476x_core *core, 702 enum si476x_intb_config intb, 703 enum si476x_a1_config a1) 704 { 705 u8 resp[CMD_INTB_PIN_CFG_A10_NRESP]; 706 const u8 args[CMD_INTB_PIN_CFG_NARGS] = { 707 PIN_CFG_BYTE(intb), 708 PIN_CFG_BYTE(a1), 709 }; 710 711 return si476x_core_send_command(core, CMD_INTB_PIN_CFG, 712 args, ARRAY_SIZE(args), 713 resp, ARRAY_SIZE(resp), 714 SI476X_DEFAULT_TIMEOUT); 715 } 716 717 static int si476x_core_cmd_intb_pin_cfg_a20(struct si476x_core *core, 718 enum si476x_intb_config intb, 719 enum si476x_a1_config a1) 720 { 721 u8 resp[CMD_INTB_PIN_CFG_A20_NRESP]; 722 const u8 args[CMD_INTB_PIN_CFG_NARGS] = { 723 PIN_CFG_BYTE(intb), 724 PIN_CFG_BYTE(a1), 725 }; 726 727 return si476x_core_send_command(core, CMD_INTB_PIN_CFG, 728 args, ARRAY_SIZE(args), 729 resp, ARRAY_SIZE(resp), 730 SI476X_DEFAULT_TIMEOUT); 731 } 732 733 734 735 /** 736 * si476x_cmd_am_rsq_status - send 'AM_RSQ_STATUS' command to the 737 * device 738 * @core - device to send the command to 739 * @rsqack - if set command clears RSQINT, SNRINT, SNRLINT, RSSIHINT, 740 * RSSSILINT, BLENDINT, MULTHINT and MULTLINT 741 * @attune - when set the values in the status report are the values 742 * that were calculated at tune 743 * @cancel - abort ongoing seek/tune opertation 744 * @stcack - clear the STCINT bin in status register 745 * @report - all signal quality information retured by the command 746 * (if NULL then the output of the command is ignored) 747 * 748 * Function returns 0 on success and negative error code on failure 749 */ 750 int si476x_core_cmd_am_rsq_status(struct si476x_core *core, 751 struct si476x_rsq_status_args *rsqargs, 752 struct si476x_rsq_status_report *report) 753 { 754 int err; 755 u8 resp[CMD_AM_RSQ_STATUS_NRESP]; 756 const u8 args[CMD_AM_RSQ_STATUS_NARGS] = { 757 rsqargs->rsqack << 3 | rsqargs->attune << 2 | 758 rsqargs->cancel << 1 | rsqargs->stcack, 759 }; 760 761 err = si476x_core_send_command(core, CMD_AM_RSQ_STATUS, 762 args, ARRAY_SIZE(args), 763 resp, ARRAY_SIZE(resp), 764 SI476X_DEFAULT_TIMEOUT); 765 /* 766 * Besides getting received signal quality information this 767 * command can be used to just acknowledge different interrupt 768 * flags in those cases it is useless to copy and parse 769 * received data so user can pass NULL, and thus avoid 770 * unnecessary copying. 771 */ 772 if (!report) 773 return err; 774 775 report->snrhint = 0b00001000 & resp[1]; 776 report->snrlint = 0b00000100 & resp[1]; 777 report->rssihint = 0b00000010 & resp[1]; 778 report->rssilint = 0b00000001 & resp[1]; 779 780 report->bltf = 0b10000000 & resp[2]; 781 report->snr_ready = 0b00100000 & resp[2]; 782 report->rssiready = 0b00001000 & resp[2]; 783 report->afcrl = 0b00000010 & resp[2]; 784 report->valid = 0b00000001 & resp[2]; 785 786 report->readfreq = be16_to_cpup((__be16 *)(resp + 3)); 787 report->freqoff = resp[5]; 788 report->rssi = resp[6]; 789 report->snr = resp[7]; 790 report->lassi = resp[9]; 791 report->hassi = resp[10]; 792 report->mult = resp[11]; 793 report->dev = resp[12]; 794 795 return err; 796 } 797 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status); 798 799 int si476x_core_cmd_fm_acf_status(struct si476x_core *core, 800 struct si476x_acf_status_report *report) 801 { 802 int err; 803 u8 resp[CMD_FM_ACF_STATUS_NRESP]; 804 const u8 args[CMD_FM_ACF_STATUS_NARGS] = { 805 0x0, 806 }; 807 808 if (!report) 809 return -EINVAL; 810 811 err = si476x_core_send_command(core, CMD_FM_ACF_STATUS, 812 args, ARRAY_SIZE(args), 813 resp, ARRAY_SIZE(resp), 814 SI476X_DEFAULT_TIMEOUT); 815 if (err < 0) 816 return err; 817 818 report->blend_int = resp[1] & SI476X_ACF_BLEND_INT; 819 report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT; 820 report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT; 821 report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT; 822 report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT; 823 report->smute = resp[2] & SI476X_ACF_SMUTE; 824 report->smattn = resp[3] & SI476X_ACF_SMATTN; 825 report->chbw = resp[4]; 826 report->hicut = resp[5]; 827 report->hiblend = resp[6]; 828 report->pilot = resp[7] & SI476X_ACF_PILOT; 829 report->stblend = resp[7] & SI476X_ACF_STBLEND; 830 831 return err; 832 } 833 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status); 834 835 int si476x_core_cmd_am_acf_status(struct si476x_core *core, 836 struct si476x_acf_status_report *report) 837 { 838 int err; 839 u8 resp[CMD_AM_ACF_STATUS_NRESP]; 840 const u8 args[CMD_AM_ACF_STATUS_NARGS] = { 841 0x0, 842 }; 843 844 if (!report) 845 return -EINVAL; 846 847 err = si476x_core_send_command(core, CMD_AM_ACF_STATUS, 848 args, ARRAY_SIZE(args), 849 resp, ARRAY_SIZE(resp), 850 SI476X_DEFAULT_TIMEOUT); 851 if (err < 0) 852 return err; 853 854 report->blend_int = resp[1] & SI476X_ACF_BLEND_INT; 855 report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT; 856 report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT; 857 report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT; 858 report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT; 859 report->smute = resp[2] & SI476X_ACF_SMUTE; 860 report->smattn = resp[3] & SI476X_ACF_SMATTN; 861 report->chbw = resp[4]; 862 report->hicut = resp[5]; 863 864 return err; 865 } 866 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status); 867 868 869 /** 870 * si476x_cmd_fm_seek_start - send 'FM_SEEK_START' command to the 871 * device 872 * @core - device to send the command to 873 * @seekup - if set the direction of the search is 'up' 874 * @wrap - if set seek wraps when hitting band limit 875 * 876 * This function begins search for a valid station. The station is 877 * considered valid when 'FM_VALID_SNR_THRESHOLD' and 878 * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria 879 * are met. 880 } * 881 * Function returns 0 on success and negative error code on failure 882 */ 883 int si476x_core_cmd_fm_seek_start(struct si476x_core *core, 884 bool seekup, bool wrap) 885 { 886 u8 resp[CMD_FM_SEEK_START_NRESP]; 887 const u8 args[CMD_FM_SEEK_START_NARGS] = { 888 seekup << 3 | wrap << 2, 889 }; 890 891 return si476x_cmd_tune_seek_freq(core, CMD_FM_SEEK_START, 892 args, sizeof(args), 893 resp, sizeof(resp)); 894 } 895 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start); 896 897 /** 898 * si476x_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the 899 * device 900 * @core - device to send the command to 901 * @status_only - if set the data is not removed from RDSFIFO, 902 * RDSFIFOUSED is not decremented and data in all the 903 * rest RDS data contains the last valid info received 904 * @mtfifo if set the command clears RDS receive FIFO 905 * @intack if set the command clards the RDSINT bit. 906 * 907 * Function returns 0 on success and negative error code on failure 908 */ 909 int si476x_core_cmd_fm_rds_status(struct si476x_core *core, 910 bool status_only, 911 bool mtfifo, 912 bool intack, 913 struct si476x_rds_status_report *report) 914 { 915 int err; 916 u8 resp[CMD_FM_RDS_STATUS_NRESP]; 917 const u8 args[CMD_FM_RDS_STATUS_NARGS] = { 918 status_only << 2 | mtfifo << 1 | intack, 919 }; 920 921 err = si476x_core_send_command(core, CMD_FM_RDS_STATUS, 922 args, ARRAY_SIZE(args), 923 resp, ARRAY_SIZE(resp), 924 SI476X_DEFAULT_TIMEOUT); 925 /* 926 * Besides getting RDS status information this command can be 927 * used to just acknowledge different interrupt flags in those 928 * cases it is useless to copy and parse received data so user 929 * can pass NULL, and thus avoid unnecessary copying. 930 */ 931 if (err < 0 || report == NULL) 932 return err; 933 934 report->rdstpptyint = 0b00010000 & resp[1]; 935 report->rdspiint = 0b00001000 & resp[1]; 936 report->rdssyncint = 0b00000010 & resp[1]; 937 report->rdsfifoint = 0b00000001 & resp[1]; 938 939 report->tpptyvalid = 0b00010000 & resp[2]; 940 report->pivalid = 0b00001000 & resp[2]; 941 report->rdssync = 0b00000010 & resp[2]; 942 report->rdsfifolost = 0b00000001 & resp[2]; 943 944 report->tp = 0b00100000 & resp[3]; 945 report->pty = 0b00011111 & resp[3]; 946 947 report->pi = be16_to_cpup((__be16 *)(resp + 4)); 948 report->rdsfifoused = resp[6]; 949 950 report->ble[V4L2_RDS_BLOCK_A] = 0b11000000 & resp[7]; 951 report->ble[V4L2_RDS_BLOCK_B] = 0b00110000 & resp[7]; 952 report->ble[V4L2_RDS_BLOCK_C] = 0b00001100 & resp[7]; 953 report->ble[V4L2_RDS_BLOCK_D] = 0b00000011 & resp[7]; 954 955 report->rds[V4L2_RDS_BLOCK_A].block = V4L2_RDS_BLOCK_A; 956 report->rds[V4L2_RDS_BLOCK_A].msb = resp[8]; 957 report->rds[V4L2_RDS_BLOCK_A].lsb = resp[9]; 958 959 report->rds[V4L2_RDS_BLOCK_B].block = V4L2_RDS_BLOCK_B; 960 report->rds[V4L2_RDS_BLOCK_B].msb = resp[10]; 961 report->rds[V4L2_RDS_BLOCK_B].lsb = resp[11]; 962 963 report->rds[V4L2_RDS_BLOCK_C].block = V4L2_RDS_BLOCK_C; 964 report->rds[V4L2_RDS_BLOCK_C].msb = resp[12]; 965 report->rds[V4L2_RDS_BLOCK_C].lsb = resp[13]; 966 967 report->rds[V4L2_RDS_BLOCK_D].block = V4L2_RDS_BLOCK_D; 968 report->rds[V4L2_RDS_BLOCK_D].msb = resp[14]; 969 report->rds[V4L2_RDS_BLOCK_D].lsb = resp[15]; 970 971 return err; 972 } 973 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status); 974 975 int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *core, 976 bool clear, 977 struct si476x_rds_blockcount_report *report) 978 { 979 int err; 980 u8 resp[CMD_FM_RDS_BLOCKCOUNT_NRESP]; 981 const u8 args[CMD_FM_RDS_BLOCKCOUNT_NARGS] = { 982 clear, 983 }; 984 985 if (!report) 986 return -EINVAL; 987 988 err = si476x_core_send_command(core, CMD_FM_RDS_BLOCKCOUNT, 989 args, ARRAY_SIZE(args), 990 resp, ARRAY_SIZE(resp), 991 SI476X_DEFAULT_TIMEOUT); 992 993 if (!err) { 994 report->expected = be16_to_cpup((__be16 *)(resp + 2)); 995 report->received = be16_to_cpup((__be16 *)(resp + 4)); 996 report->uncorrectable = be16_to_cpup((__be16 *)(resp + 6)); 997 } 998 999 return err; 1000 } 1001 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount); 1002 1003 int si476x_core_cmd_fm_phase_diversity(struct si476x_core *core, 1004 enum si476x_phase_diversity_mode mode) 1005 { 1006 u8 resp[CMD_FM_PHASE_DIVERSITY_NRESP]; 1007 const u8 args[CMD_FM_PHASE_DIVERSITY_NARGS] = { 1008 mode & 0b111, 1009 }; 1010 1011 return si476x_core_send_command(core, CMD_FM_PHASE_DIVERSITY, 1012 args, ARRAY_SIZE(args), 1013 resp, ARRAY_SIZE(resp), 1014 SI476X_DEFAULT_TIMEOUT); 1015 } 1016 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity); 1017 /** 1018 * si476x_core_cmd_fm_phase_div_status() - get the phase diversity 1019 * status 1020 * 1021 * @core: si476x device 1022 * 1023 * NOTE caller must hold core lock 1024 * 1025 * Function returns the value of the status bit in case of success and 1026 * negative error code in case of failre. 1027 */ 1028 int si476x_core_cmd_fm_phase_div_status(struct si476x_core *core) 1029 { 1030 int err; 1031 u8 resp[CMD_FM_PHASE_DIV_STATUS_NRESP]; 1032 1033 err = si476x_core_send_command(core, CMD_FM_PHASE_DIV_STATUS, 1034 NULL, 0, 1035 resp, ARRAY_SIZE(resp), 1036 SI476X_DEFAULT_TIMEOUT); 1037 1038 return (err < 0) ? err : resp[1]; 1039 } 1040 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status); 1041 1042 1043 /** 1044 * si476x_cmd_am_seek_start - send 'FM_SEEK_START' command to the 1045 * device 1046 * @core - device to send the command to 1047 * @seekup - if set the direction of the search is 'up' 1048 * @wrap - if set seek wraps when hitting band limit 1049 * 1050 * This function begins search for a valid station. The station is 1051 * considered valid when 'FM_VALID_SNR_THRESHOLD' and 1052 * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria 1053 * are met. 1054 * 1055 * Function returns 0 on success and negative error code on failure 1056 */ 1057 int si476x_core_cmd_am_seek_start(struct si476x_core *core, 1058 bool seekup, bool wrap) 1059 { 1060 u8 resp[CMD_AM_SEEK_START_NRESP]; 1061 const u8 args[CMD_AM_SEEK_START_NARGS] = { 1062 seekup << 3 | wrap << 2, 1063 }; 1064 1065 return si476x_cmd_tune_seek_freq(core, CMD_AM_SEEK_START, 1066 args, sizeof(args), 1067 resp, sizeof(resp)); 1068 } 1069 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start); 1070 1071 1072 1073 static int si476x_core_cmd_power_up_a10(struct si476x_core *core, 1074 struct si476x_power_up_args *puargs) 1075 { 1076 u8 resp[CMD_POWER_UP_A10_NRESP]; 1077 const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ); 1078 const bool ctsen = (core->client->irq != 0); 1079 const u8 args[CMD_POWER_UP_A10_NARGS] = { 1080 0xF7, /* Reserved, always 0xF7 */ 1081 0x3F & puargs->xcload, /* First two bits are reserved to be 1082 * zeros */ 1083 ctsen << 7 | intsel << 6 | 0x07, /* Last five bits 1084 * are reserved to 1085 * be written as 0x7 */ 1086 puargs->func << 4 | puargs->freq, 1087 0x11, /* Reserved, always 0x11 */ 1088 }; 1089 1090 return si476x_core_send_command(core, CMD_POWER_UP, 1091 args, ARRAY_SIZE(args), 1092 resp, ARRAY_SIZE(resp), 1093 SI476X_TIMEOUT_POWER_UP); 1094 } 1095 1096 static int si476x_core_cmd_power_up_a20(struct si476x_core *core, 1097 struct si476x_power_up_args *puargs) 1098 { 1099 u8 resp[CMD_POWER_UP_A20_NRESP]; 1100 const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ); 1101 const bool ctsen = (core->client->irq != 0); 1102 const u8 args[CMD_POWER_UP_A20_NARGS] = { 1103 puargs->ibias6x << 7 | puargs->xstart, 1104 0x3F & puargs->xcload, /* First two bits are reserved to be 1105 * zeros */ 1106 ctsen << 7 | intsel << 6 | puargs->fastboot << 5 | 1107 puargs->xbiashc << 3 | puargs->xbias, 1108 puargs->func << 4 | puargs->freq, 1109 0x10 | puargs->xmode, 1110 }; 1111 1112 return si476x_core_send_command(core, CMD_POWER_UP, 1113 args, ARRAY_SIZE(args), 1114 resp, ARRAY_SIZE(resp), 1115 SI476X_TIMEOUT_POWER_UP); 1116 } 1117 1118 static int si476x_core_cmd_power_down_a10(struct si476x_core *core, 1119 struct si476x_power_down_args *pdargs) 1120 { 1121 u8 resp[CMD_POWER_DOWN_A10_NRESP]; 1122 1123 return si476x_core_send_command(core, CMD_POWER_DOWN, 1124 NULL, 0, 1125 resp, ARRAY_SIZE(resp), 1126 SI476X_DEFAULT_TIMEOUT); 1127 } 1128 1129 static int si476x_core_cmd_power_down_a20(struct si476x_core *core, 1130 struct si476x_power_down_args *pdargs) 1131 { 1132 u8 resp[CMD_POWER_DOWN_A20_NRESP]; 1133 const u8 args[CMD_POWER_DOWN_A20_NARGS] = { 1134 pdargs->xosc, 1135 }; 1136 return si476x_core_send_command(core, CMD_POWER_DOWN, 1137 args, ARRAY_SIZE(args), 1138 resp, ARRAY_SIZE(resp), 1139 SI476X_DEFAULT_TIMEOUT); 1140 } 1141 1142 static int si476x_core_cmd_am_tune_freq_a10(struct si476x_core *core, 1143 struct si476x_tune_freq_args *tuneargs) 1144 { 1145 1146 const int am_freq = tuneargs->freq; 1147 u8 resp[CMD_AM_TUNE_FREQ_NRESP]; 1148 const u8 args[CMD_AM_TUNE_FREQ_NARGS] = { 1149 (tuneargs->hd << 6), 1150 msb(am_freq), 1151 lsb(am_freq), 1152 }; 1153 1154 return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, args, 1155 sizeof(args), 1156 resp, sizeof(resp)); 1157 } 1158 1159 static int si476x_core_cmd_am_tune_freq_a20(struct si476x_core *core, 1160 struct si476x_tune_freq_args *tuneargs) 1161 { 1162 const int am_freq = tuneargs->freq; 1163 u8 resp[CMD_AM_TUNE_FREQ_NRESP]; 1164 const u8 args[CMD_AM_TUNE_FREQ_NARGS] = { 1165 (tuneargs->zifsr << 6) | (tuneargs->injside & 0b11), 1166 msb(am_freq), 1167 lsb(am_freq), 1168 }; 1169 1170 return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, 1171 args, sizeof(args), 1172 resp, sizeof(resp)); 1173 } 1174 1175 static int si476x_core_cmd_fm_rsq_status_a10(struct si476x_core *core, 1176 struct si476x_rsq_status_args *rsqargs, 1177 struct si476x_rsq_status_report *report) 1178 { 1179 int err; 1180 u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP]; 1181 const u8 args[CMD_FM_RSQ_STATUS_A10_NARGS] = { 1182 rsqargs->rsqack << 3 | rsqargs->attune << 2 | 1183 rsqargs->cancel << 1 | rsqargs->stcack, 1184 }; 1185 1186 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS, 1187 args, ARRAY_SIZE(args), 1188 resp, ARRAY_SIZE(resp), 1189 SI476X_DEFAULT_TIMEOUT); 1190 /* 1191 * Besides getting received signal quality information this 1192 * command can be used to just acknowledge different interrupt 1193 * flags in those cases it is useless to copy and parse 1194 * received data so user can pass NULL, and thus avoid 1195 * unnecessary copying. 1196 */ 1197 if (err < 0 || report == NULL) 1198 return err; 1199 1200 report->multhint = 0b10000000 & resp[1]; 1201 report->multlint = 0b01000000 & resp[1]; 1202 report->snrhint = 0b00001000 & resp[1]; 1203 report->snrlint = 0b00000100 & resp[1]; 1204 report->rssihint = 0b00000010 & resp[1]; 1205 report->rssilint = 0b00000001 & resp[1]; 1206 1207 report->bltf = 0b10000000 & resp[2]; 1208 report->snr_ready = 0b00100000 & resp[2]; 1209 report->rssiready = 0b00001000 & resp[2]; 1210 report->afcrl = 0b00000010 & resp[2]; 1211 report->valid = 0b00000001 & resp[2]; 1212 1213 report->readfreq = be16_to_cpup((__be16 *)(resp + 3)); 1214 report->freqoff = resp[5]; 1215 report->rssi = resp[6]; 1216 report->snr = resp[7]; 1217 report->lassi = resp[9]; 1218 report->hassi = resp[10]; 1219 report->mult = resp[11]; 1220 report->dev = resp[12]; 1221 report->readantcap = be16_to_cpup((__be16 *)(resp + 13)); 1222 report->assi = resp[15]; 1223 report->usn = resp[16]; 1224 1225 return err; 1226 } 1227 1228 static int si476x_core_cmd_fm_rsq_status_a20(struct si476x_core *core, 1229 struct si476x_rsq_status_args *rsqargs, 1230 struct si476x_rsq_status_report *report) 1231 { 1232 int err; 1233 u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP]; 1234 const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = { 1235 rsqargs->primary << 4 | rsqargs->rsqack << 3 | 1236 rsqargs->attune << 2 | rsqargs->cancel << 1 | 1237 rsqargs->stcack, 1238 }; 1239 1240 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS, 1241 args, ARRAY_SIZE(args), 1242 resp, ARRAY_SIZE(resp), 1243 SI476X_DEFAULT_TIMEOUT); 1244 /* 1245 * Besides getting received signal quality information this 1246 * command can be used to just acknowledge different interrupt 1247 * flags in those cases it is useless to copy and parse 1248 * received data so user can pass NULL, and thus avoid 1249 * unnecessary copying. 1250 */ 1251 if (err < 0 || report == NULL) 1252 return err; 1253 1254 report->multhint = 0b10000000 & resp[1]; 1255 report->multlint = 0b01000000 & resp[1]; 1256 report->snrhint = 0b00001000 & resp[1]; 1257 report->snrlint = 0b00000100 & resp[1]; 1258 report->rssihint = 0b00000010 & resp[1]; 1259 report->rssilint = 0b00000001 & resp[1]; 1260 1261 report->bltf = 0b10000000 & resp[2]; 1262 report->snr_ready = 0b00100000 & resp[2]; 1263 report->rssiready = 0b00001000 & resp[2]; 1264 report->afcrl = 0b00000010 & resp[2]; 1265 report->valid = 0b00000001 & resp[2]; 1266 1267 report->readfreq = be16_to_cpup((__be16 *)(resp + 3)); 1268 report->freqoff = resp[5]; 1269 report->rssi = resp[6]; 1270 report->snr = resp[7]; 1271 report->lassi = resp[9]; 1272 report->hassi = resp[10]; 1273 report->mult = resp[11]; 1274 report->dev = resp[12]; 1275 report->readantcap = be16_to_cpup((__be16 *)(resp + 13)); 1276 report->assi = resp[15]; 1277 report->usn = resp[16]; 1278 1279 return err; 1280 } 1281 1282 1283 static int si476x_core_cmd_fm_rsq_status_a30(struct si476x_core *core, 1284 struct si476x_rsq_status_args *rsqargs, 1285 struct si476x_rsq_status_report *report) 1286 { 1287 int err; 1288 u8 resp[CMD_FM_RSQ_STATUS_A30_NRESP]; 1289 const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = { 1290 rsqargs->primary << 4 | rsqargs->rsqack << 3 | 1291 rsqargs->attune << 2 | rsqargs->cancel << 1 | 1292 rsqargs->stcack, 1293 }; 1294 1295 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS, 1296 args, ARRAY_SIZE(args), 1297 resp, ARRAY_SIZE(resp), 1298 SI476X_DEFAULT_TIMEOUT); 1299 /* 1300 * Besides getting received signal quality information this 1301 * command can be used to just acknowledge different interrupt 1302 * flags in those cases it is useless to copy and parse 1303 * received data so user can pass NULL, and thus avoid 1304 * unnecessary copying. 1305 */ 1306 if (err < 0 || report == NULL) 1307 return err; 1308 1309 report->multhint = 0b10000000 & resp[1]; 1310 report->multlint = 0b01000000 & resp[1]; 1311 report->snrhint = 0b00001000 & resp[1]; 1312 report->snrlint = 0b00000100 & resp[1]; 1313 report->rssihint = 0b00000010 & resp[1]; 1314 report->rssilint = 0b00000001 & resp[1]; 1315 1316 report->bltf = 0b10000000 & resp[2]; 1317 report->snr_ready = 0b00100000 & resp[2]; 1318 report->rssiready = 0b00001000 & resp[2]; 1319 report->injside = 0b00000100 & resp[2]; 1320 report->afcrl = 0b00000010 & resp[2]; 1321 report->valid = 0b00000001 & resp[2]; 1322 1323 report->readfreq = be16_to_cpup((__be16 *)(resp + 3)); 1324 report->freqoff = resp[5]; 1325 report->rssi = resp[6]; 1326 report->snr = resp[7]; 1327 report->issi = resp[8]; 1328 report->lassi = resp[9]; 1329 report->hassi = resp[10]; 1330 report->mult = resp[11]; 1331 report->dev = resp[12]; 1332 report->readantcap = be16_to_cpup((__be16 *)(resp + 13)); 1333 report->assi = resp[15]; 1334 report->usn = resp[16]; 1335 1336 report->pilotdev = resp[17]; 1337 report->rdsdev = resp[18]; 1338 report->assidev = resp[19]; 1339 report->strongdev = resp[20]; 1340 report->rdspi = be16_to_cpup((__be16 *)(resp + 21)); 1341 1342 return err; 1343 } 1344 1345 static int si476x_core_cmd_fm_tune_freq_a10(struct si476x_core *core, 1346 struct si476x_tune_freq_args *tuneargs) 1347 { 1348 u8 resp[CMD_FM_TUNE_FREQ_NRESP]; 1349 const u8 args[CMD_FM_TUNE_FREQ_A10_NARGS] = { 1350 (tuneargs->hd << 6) | (tuneargs->tunemode << 4) 1351 | (tuneargs->smoothmetrics << 2), 1352 msb(tuneargs->freq), 1353 lsb(tuneargs->freq), 1354 msb(tuneargs->antcap), 1355 lsb(tuneargs->antcap) 1356 }; 1357 1358 return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ, 1359 args, sizeof(args), 1360 resp, sizeof(resp)); 1361 } 1362 1363 static int si476x_core_cmd_fm_tune_freq_a20(struct si476x_core *core, 1364 struct si476x_tune_freq_args *tuneargs) 1365 { 1366 u8 resp[CMD_FM_TUNE_FREQ_NRESP]; 1367 const u8 args[CMD_FM_TUNE_FREQ_A20_NARGS] = { 1368 (tuneargs->hd << 6) | (tuneargs->tunemode << 4) 1369 | (tuneargs->smoothmetrics << 2) | (tuneargs->injside), 1370 msb(tuneargs->freq), 1371 lsb(tuneargs->freq), 1372 }; 1373 1374 return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ, 1375 args, sizeof(args), 1376 resp, sizeof(resp)); 1377 } 1378 1379 static int si476x_core_cmd_agc_status_a20(struct si476x_core *core, 1380 struct si476x_agc_status_report *report) 1381 { 1382 int err; 1383 u8 resp[CMD_AGC_STATUS_NRESP_A20]; 1384 1385 if (!report) 1386 return -EINVAL; 1387 1388 err = si476x_core_send_command(core, CMD_AGC_STATUS, 1389 NULL, 0, 1390 resp, ARRAY_SIZE(resp), 1391 SI476X_DEFAULT_TIMEOUT); 1392 if (err < 0) 1393 return err; 1394 1395 report->mxhi = resp[1] & SI476X_AGC_MXHI; 1396 report->mxlo = resp[1] & SI476X_AGC_MXLO; 1397 report->lnahi = resp[1] & SI476X_AGC_LNAHI; 1398 report->lnalo = resp[1] & SI476X_AGC_LNALO; 1399 report->fmagc1 = resp[2]; 1400 report->fmagc2 = resp[3]; 1401 report->pgagain = resp[4]; 1402 report->fmwblang = resp[5]; 1403 1404 return err; 1405 } 1406 1407 static int si476x_core_cmd_agc_status_a10(struct si476x_core *core, 1408 struct si476x_agc_status_report *report) 1409 { 1410 int err; 1411 u8 resp[CMD_AGC_STATUS_NRESP_A10]; 1412 1413 if (!report) 1414 return -EINVAL; 1415 1416 err = si476x_core_send_command(core, CMD_AGC_STATUS, 1417 NULL, 0, 1418 resp, ARRAY_SIZE(resp), 1419 SI476X_DEFAULT_TIMEOUT); 1420 if (err < 0) 1421 return err; 1422 1423 report->mxhi = resp[1] & SI476X_AGC_MXHI; 1424 report->mxlo = resp[1] & SI476X_AGC_MXLO; 1425 report->lnahi = resp[1] & SI476X_AGC_LNAHI; 1426 report->lnalo = resp[1] & SI476X_AGC_LNALO; 1427 1428 return err; 1429 } 1430 1431 typedef int (*tune_freq_func_t) (struct si476x_core *core, 1432 struct si476x_tune_freq_args *tuneargs); 1433 1434 static struct { 1435 int (*power_up) (struct si476x_core *, 1436 struct si476x_power_up_args *); 1437 int (*power_down) (struct si476x_core *, 1438 struct si476x_power_down_args *); 1439 1440 tune_freq_func_t fm_tune_freq; 1441 tune_freq_func_t am_tune_freq; 1442 1443 int (*fm_rsq_status)(struct si476x_core *, 1444 struct si476x_rsq_status_args *, 1445 struct si476x_rsq_status_report *); 1446 1447 int (*agc_status)(struct si476x_core *, 1448 struct si476x_agc_status_report *); 1449 int (*intb_pin_cfg)(struct si476x_core *core, 1450 enum si476x_intb_config intb, 1451 enum si476x_a1_config a1); 1452 } si476x_cmds_vtable[] = { 1453 [SI476X_REVISION_A10] = { 1454 .power_up = si476x_core_cmd_power_up_a10, 1455 .power_down = si476x_core_cmd_power_down_a10, 1456 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a10, 1457 .am_tune_freq = si476x_core_cmd_am_tune_freq_a10, 1458 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a10, 1459 .agc_status = si476x_core_cmd_agc_status_a10, 1460 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a10, 1461 }, 1462 [SI476X_REVISION_A20] = { 1463 .power_up = si476x_core_cmd_power_up_a20, 1464 .power_down = si476x_core_cmd_power_down_a20, 1465 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20, 1466 .am_tune_freq = si476x_core_cmd_am_tune_freq_a20, 1467 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a20, 1468 .agc_status = si476x_core_cmd_agc_status_a20, 1469 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20, 1470 }, 1471 [SI476X_REVISION_A30] = { 1472 .power_up = si476x_core_cmd_power_up_a20, 1473 .power_down = si476x_core_cmd_power_down_a20, 1474 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20, 1475 .am_tune_freq = si476x_core_cmd_am_tune_freq_a20, 1476 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a30, 1477 .agc_status = si476x_core_cmd_agc_status_a20, 1478 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20, 1479 }, 1480 }; 1481 1482 int si476x_core_cmd_power_up(struct si476x_core *core, 1483 struct si476x_power_up_args *args) 1484 { 1485 BUG_ON(core->revision > SI476X_REVISION_A30 || 1486 core->revision == -1); 1487 return si476x_cmds_vtable[core->revision].power_up(core, args); 1488 } 1489 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up); 1490 1491 int si476x_core_cmd_power_down(struct si476x_core *core, 1492 struct si476x_power_down_args *args) 1493 { 1494 BUG_ON(core->revision > SI476X_REVISION_A30 || 1495 core->revision == -1); 1496 return si476x_cmds_vtable[core->revision].power_down(core, args); 1497 } 1498 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down); 1499 1500 int si476x_core_cmd_fm_tune_freq(struct si476x_core *core, 1501 struct si476x_tune_freq_args *args) 1502 { 1503 BUG_ON(core->revision > SI476X_REVISION_A30 || 1504 core->revision == -1); 1505 return si476x_cmds_vtable[core->revision].fm_tune_freq(core, args); 1506 } 1507 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq); 1508 1509 int si476x_core_cmd_am_tune_freq(struct si476x_core *core, 1510 struct si476x_tune_freq_args *args) 1511 { 1512 BUG_ON(core->revision > SI476X_REVISION_A30 || 1513 core->revision == -1); 1514 return si476x_cmds_vtable[core->revision].am_tune_freq(core, args); 1515 } 1516 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq); 1517 1518 int si476x_core_cmd_fm_rsq_status(struct si476x_core *core, 1519 struct si476x_rsq_status_args *args, 1520 struct si476x_rsq_status_report *report) 1521 1522 { 1523 BUG_ON(core->revision > SI476X_REVISION_A30 || 1524 core->revision == -1); 1525 return si476x_cmds_vtable[core->revision].fm_rsq_status(core, args, 1526 report); 1527 } 1528 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status); 1529 1530 int si476x_core_cmd_agc_status(struct si476x_core *core, 1531 struct si476x_agc_status_report *report) 1532 1533 { 1534 BUG_ON(core->revision > SI476X_REVISION_A30 || 1535 core->revision == -1); 1536 return si476x_cmds_vtable[core->revision].agc_status(core, report); 1537 } 1538 EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status); 1539 1540 int si476x_core_cmd_intb_pin_cfg(struct si476x_core *core, 1541 enum si476x_intb_config intb, 1542 enum si476x_a1_config a1) 1543 { 1544 BUG_ON(core->revision > SI476X_REVISION_A30 || 1545 core->revision == -1); 1546 1547 return si476x_cmds_vtable[core->revision].intb_pin_cfg(core, intb, a1); 1548 } 1549 EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg); 1550 1551 MODULE_LICENSE("GPL"); 1552 MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>"); 1553 MODULE_DESCRIPTION("API for command exchange for si476x"); 1554