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