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