root/drivers/mfd/si476x-cmd.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. si476x_core_parse_and_nag_about_error
  2. si476x_core_send_command
  3. si476x_cmd_clear_stc
  4. si476x_cmd_tune_seek_freq
  5. si476x_core_cmd_func_info
  6. si476x_core_cmd_set_property
  7. si476x_core_cmd_get_property
  8. si476x_core_cmd_dig_audio_pin_cfg
  9. si476x_core_cmd_zif_pin_cfg
  10. si476x_core_cmd_ic_link_gpo_ctl_pin_cfg
  11. si476x_core_cmd_ana_audio_pin_cfg
  12. si476x_core_cmd_intb_pin_cfg_a10
  13. si476x_core_cmd_intb_pin_cfg_a20
  14. si476x_core_cmd_am_rsq_status
  15. si476x_core_cmd_fm_acf_status
  16. si476x_core_cmd_am_acf_status
  17. si476x_core_cmd_fm_seek_start
  18. si476x_core_cmd_fm_rds_status
  19. si476x_core_cmd_fm_rds_blockcount
  20. si476x_core_cmd_fm_phase_diversity
  21. si476x_core_cmd_fm_phase_div_status
  22. si476x_core_cmd_am_seek_start
  23. si476x_core_cmd_power_up_a10
  24. si476x_core_cmd_power_up_a20
  25. si476x_core_cmd_power_down_a10
  26. si476x_core_cmd_power_down_a20
  27. si476x_core_cmd_am_tune_freq_a10
  28. si476x_core_cmd_am_tune_freq_a20
  29. si476x_core_cmd_fm_rsq_status_a10
  30. si476x_core_cmd_fm_rsq_status_a20
  31. si476x_core_cmd_fm_rsq_status_a30
  32. si476x_core_cmd_fm_tune_freq_a10
  33. si476x_core_cmd_fm_tune_freq_a20
  34. si476x_core_cmd_agc_status_a20
  35. si476x_core_cmd_agc_status_a10
  36. si476x_core_cmd_power_up
  37. si476x_core_cmd_power_down
  38. si476x_core_cmd_fm_tune_freq
  39. si476x_core_cmd_am_tune_freq
  40. si476x_core_cmd_fm_rsq_status
  41. si476x_core_cmd_agc_status
  42. si476x_core_cmd_intb_pin_cfg

   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 
 138 enum 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 
 151 enum 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 
 158 enum 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 
 171 static 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  * @response: buffer to place the expected response from the device
 250  * @respn:    actual size of @response
 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  */
 257 static 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;
 343 exit:
 344         return err;
 345 }
 346 
 347 static 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 
 372 static 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_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  */
 404 int 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 }
 424 EXPORT_SYMBOL_GPL(si476x_core_cmd_func_info);
 425 
 426 /**
 427  * si476x_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  */
 435 int 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 }
 452 EXPORT_SYMBOL_GPL(si476x_core_cmd_set_property);
 453 
 454 /**
 455  * si476x_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  */
 462 int 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 }
 481 EXPORT_SYMBOL_GPL(si476x_core_cmd_get_property);
 482 
 483 /**
 484  * si476x_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  */
 520 int 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 }
 539 EXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg);
 540 
 541 /**
 542  * si476x_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  */
 569 int 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 }
 588 EXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg);
 589 
 590 /**
 591  * si476x_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  */
 626 int 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 }
 645 EXPORT_SYMBOL_GPL(si476x_core_cmd_ic_link_gpo_ctl_pin_cfg);
 646 
 647 /**
 648  * si476x_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  */
 660 int 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 }
 673 EXPORT_SYMBOL_GPL(si476x_core_cmd_ana_audio_pin_cfg);
 674 
 675 
 676 /**
 677  * si476x_cmd_intb_pin_cfg - 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  */
 694 static 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 
 710 static 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_cmd_am_rsq_status - send 'AM_RSQ_STATUS' command to the
 730  * device
 731  * @core  - device to send the command to
 732  * @rsqack - if set command clears RSQINT, SNRINT, SNRLINT, RSSIHINT,
 733  *           RSSSILINT, BLENDINT, MULTHINT and MULTLINT
 734  * @attune - when set the values in the status report are the values
 735  *           that were calculated at tune
 736  * @cancel - abort ongoing seek/tune opertation
 737  * @stcack - clear the STCINT bin in status register
 738  * @report - all signal quality information retured by the command
 739  *           (if NULL then the output of the command is ignored)
 740  *
 741  * Function returns 0 on success and negative error code on failure
 742  */
 743 int si476x_core_cmd_am_rsq_status(struct si476x_core *core,
 744                                   struct si476x_rsq_status_args *rsqargs,
 745                                   struct si476x_rsq_status_report *report)
 746 {
 747         int err;
 748         u8       resp[CMD_AM_RSQ_STATUS_NRESP];
 749         const u8 args[CMD_AM_RSQ_STATUS_NARGS] = {
 750                 rsqargs->rsqack << 3 | rsqargs->attune << 2 |
 751                 rsqargs->cancel << 1 | rsqargs->stcack,
 752         };
 753 
 754         err = si476x_core_send_command(core, CMD_AM_RSQ_STATUS,
 755                                        args, ARRAY_SIZE(args),
 756                                        resp, ARRAY_SIZE(resp),
 757                                        SI476X_DEFAULT_TIMEOUT);
 758         /*
 759          * Besides getting received signal quality information this
 760          * command can be used to just acknowledge different interrupt
 761          * flags in those cases it is useless to copy and parse
 762          * received data so user can pass NULL, and thus avoid
 763          * unnecessary copying.
 764          */
 765         if (!report)
 766                 return err;
 767 
 768         report->snrhint         = 0x08 & resp[1];
 769         report->snrlint         = 0x04 & resp[1];
 770         report->rssihint        = 0x02 & resp[1];
 771         report->rssilint        = 0x01 & resp[1];
 772 
 773         report->bltf            = 0x80 & resp[2];
 774         report->snr_ready       = 0x20 & resp[2];
 775         report->rssiready       = 0x08 & resp[2];
 776         report->afcrl           = 0x02 & resp[2];
 777         report->valid           = 0x01 & resp[2];
 778 
 779         report->readfreq        = get_unaligned_be16(resp + 3);
 780         report->freqoff         = resp[5];
 781         report->rssi            = resp[6];
 782         report->snr             = resp[7];
 783         report->lassi           = resp[9];
 784         report->hassi           = resp[10];
 785         report->mult            = resp[11];
 786         report->dev             = resp[12];
 787 
 788         return err;
 789 }
 790 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status);
 791 
 792 int si476x_core_cmd_fm_acf_status(struct si476x_core *core,
 793                              struct si476x_acf_status_report *report)
 794 {
 795         int err;
 796         u8       resp[CMD_FM_ACF_STATUS_NRESP];
 797         const u8 args[CMD_FM_ACF_STATUS_NARGS] = {
 798                 0x0,
 799         };
 800 
 801         if (!report)
 802                 return -EINVAL;
 803 
 804         err = si476x_core_send_command(core, CMD_FM_ACF_STATUS,
 805                                        args, ARRAY_SIZE(args),
 806                                        resp, ARRAY_SIZE(resp),
 807                                        SI476X_DEFAULT_TIMEOUT);
 808         if (err < 0)
 809                 return err;
 810 
 811         report->blend_int       = resp[1] & SI476X_ACF_BLEND_INT;
 812         report->hblend_int      = resp[1] & SI476X_ACF_HIBLEND_INT;
 813         report->hicut_int       = resp[1] & SI476X_ACF_HICUT_INT;
 814         report->chbw_int        = resp[1] & SI476X_ACF_CHBW_INT;
 815         report->softmute_int    = resp[1] & SI476X_ACF_SOFTMUTE_INT;
 816         report->smute           = resp[2] & SI476X_ACF_SMUTE;
 817         report->smattn          = resp[3] & SI476X_ACF_SMATTN;
 818         report->chbw            = resp[4];
 819         report->hicut           = resp[5];
 820         report->hiblend         = resp[6];
 821         report->pilot           = resp[7] & SI476X_ACF_PILOT;
 822         report->stblend         = resp[7] & SI476X_ACF_STBLEND;
 823 
 824         return err;
 825 }
 826 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status);
 827 
 828 int si476x_core_cmd_am_acf_status(struct si476x_core *core,
 829                                   struct si476x_acf_status_report *report)
 830 {
 831         int err;
 832         u8       resp[CMD_AM_ACF_STATUS_NRESP];
 833         const u8 args[CMD_AM_ACF_STATUS_NARGS] = {
 834                 0x0,
 835         };
 836 
 837         if (!report)
 838                 return -EINVAL;
 839 
 840         err = si476x_core_send_command(core, CMD_AM_ACF_STATUS,
 841                                        args, ARRAY_SIZE(args),
 842                                        resp, ARRAY_SIZE(resp),
 843                                        SI476X_DEFAULT_TIMEOUT);
 844         if (err < 0)
 845                 return err;
 846 
 847         report->blend_int       = resp[1] & SI476X_ACF_BLEND_INT;
 848         report->hblend_int      = resp[1] & SI476X_ACF_HIBLEND_INT;
 849         report->hicut_int       = resp[1] & SI476X_ACF_HICUT_INT;
 850         report->chbw_int        = resp[1] & SI476X_ACF_CHBW_INT;
 851         report->softmute_int    = resp[1] & SI476X_ACF_SOFTMUTE_INT;
 852         report->smute           = resp[2] & SI476X_ACF_SMUTE;
 853         report->smattn          = resp[3] & SI476X_ACF_SMATTN;
 854         report->chbw            = resp[4];
 855         report->hicut           = resp[5];
 856 
 857         return err;
 858 }
 859 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status);
 860 
 861 
 862 /**
 863  * si476x_cmd_fm_seek_start - send 'FM_SEEK_START' command to the
 864  * device
 865  * @core  - device to send the command to
 866  * @seekup - if set the direction of the search is 'up'
 867  * @wrap   - if set seek wraps when hitting band limit
 868  *
 869  * This function begins search for a valid station. The station is
 870  * considered valid when 'FM_VALID_SNR_THRESHOLD' and
 871  * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
 872  * are met.
 873 } *
 874  * Function returns 0 on success and negative error code on failure
 875  */
 876 int si476x_core_cmd_fm_seek_start(struct si476x_core *core,
 877                                   bool seekup, bool wrap)
 878 {
 879         u8       resp[CMD_FM_SEEK_START_NRESP];
 880         const u8 args[CMD_FM_SEEK_START_NARGS] = {
 881                 seekup << 3 | wrap << 2,
 882         };
 883 
 884         return si476x_cmd_tune_seek_freq(core, CMD_FM_SEEK_START,
 885                                          args, sizeof(args),
 886                                          resp, sizeof(resp));
 887 }
 888 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start);
 889 
 890 /**
 891  * si476x_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the
 892  * device
 893  * @core - device to send the command to
 894  * @status_only - if set the data is not removed from RDSFIFO,
 895  *                RDSFIFOUSED is not decremented and data in all the
 896  *                rest RDS data contains the last valid info received
 897  * @mtfifo if set the command clears RDS receive FIFO
 898  * @intack if set the command clards the RDSINT bit.
 899  *
 900  * Function returns 0 on success and negative error code on failure
 901  */
 902 int si476x_core_cmd_fm_rds_status(struct si476x_core *core,
 903                                   bool status_only,
 904                                   bool mtfifo,
 905                                   bool intack,
 906                                   struct si476x_rds_status_report *report)
 907 {
 908         int err;
 909         u8       resp[CMD_FM_RDS_STATUS_NRESP];
 910         const u8 args[CMD_FM_RDS_STATUS_NARGS] = {
 911                 status_only << 2 | mtfifo << 1 | intack,
 912         };
 913 
 914         err = si476x_core_send_command(core, CMD_FM_RDS_STATUS,
 915                                        args, ARRAY_SIZE(args),
 916                                        resp, ARRAY_SIZE(resp),
 917                                        SI476X_DEFAULT_TIMEOUT);
 918         /*
 919          * Besides getting RDS status information this command can be
 920          * used to just acknowledge different interrupt flags in those
 921          * cases it is useless to copy and parse received data so user
 922          * can pass NULL, and thus avoid unnecessary copying.
 923          */
 924         if (err < 0 || report == NULL)
 925                 return err;
 926 
 927         report->rdstpptyint     = 0x10 & resp[1];
 928         report->rdspiint        = 0x08 & resp[1];
 929         report->rdssyncint      = 0x02 & resp[1];
 930         report->rdsfifoint      = 0x01 & resp[1];
 931 
 932         report->tpptyvalid      = 0x10 & resp[2];
 933         report->pivalid         = 0x08 & resp[2];
 934         report->rdssync         = 0x02 & resp[2];
 935         report->rdsfifolost     = 0x01 & resp[2];
 936 
 937         report->tp              = 0x20 & resp[3];
 938         report->pty             = 0x1f & resp[3];
 939 
 940         report->pi              = get_unaligned_be16(resp + 4);
 941         report->rdsfifoused     = resp[6];
 942 
 943         report->ble[V4L2_RDS_BLOCK_A]   = 0xc0 & resp[7];
 944         report->ble[V4L2_RDS_BLOCK_B]   = 0x30 & resp[7];
 945         report->ble[V4L2_RDS_BLOCK_C]   = 0x0c & resp[7];
 946         report->ble[V4L2_RDS_BLOCK_D]   = 0x03 & resp[7];
 947 
 948         report->rds[V4L2_RDS_BLOCK_A].block = V4L2_RDS_BLOCK_A;
 949         report->rds[V4L2_RDS_BLOCK_A].msb = resp[8];
 950         report->rds[V4L2_RDS_BLOCK_A].lsb = resp[9];
 951 
 952         report->rds[V4L2_RDS_BLOCK_B].block = V4L2_RDS_BLOCK_B;
 953         report->rds[V4L2_RDS_BLOCK_B].msb = resp[10];
 954         report->rds[V4L2_RDS_BLOCK_B].lsb = resp[11];
 955 
 956         report->rds[V4L2_RDS_BLOCK_C].block = V4L2_RDS_BLOCK_C;
 957         report->rds[V4L2_RDS_BLOCK_C].msb = resp[12];
 958         report->rds[V4L2_RDS_BLOCK_C].lsb = resp[13];
 959 
 960         report->rds[V4L2_RDS_BLOCK_D].block = V4L2_RDS_BLOCK_D;
 961         report->rds[V4L2_RDS_BLOCK_D].msb = resp[14];
 962         report->rds[V4L2_RDS_BLOCK_D].lsb = resp[15];
 963 
 964         return err;
 965 }
 966 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status);
 967 
 968 int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *core,
 969                                 bool clear,
 970                                 struct si476x_rds_blockcount_report *report)
 971 {
 972         int err;
 973         u8       resp[CMD_FM_RDS_BLOCKCOUNT_NRESP];
 974         const u8 args[CMD_FM_RDS_BLOCKCOUNT_NARGS] = {
 975                 clear,
 976         };
 977 
 978         if (!report)
 979                 return -EINVAL;
 980 
 981         err = si476x_core_send_command(core, CMD_FM_RDS_BLOCKCOUNT,
 982                                        args, ARRAY_SIZE(args),
 983                                        resp, ARRAY_SIZE(resp),
 984                                        SI476X_DEFAULT_TIMEOUT);
 985 
 986         if (!err) {
 987                 report->expected        = get_unaligned_be16(resp + 2);
 988                 report->received        = get_unaligned_be16(resp + 4);
 989                 report->uncorrectable   = get_unaligned_be16(resp + 6);
 990         }
 991 
 992         return err;
 993 }
 994 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount);
 995 
 996 int si476x_core_cmd_fm_phase_diversity(struct si476x_core *core,
 997                                        enum si476x_phase_diversity_mode mode)
 998 {
 999         u8       resp[CMD_FM_PHASE_DIVERSITY_NRESP];
1000         const u8 args[CMD_FM_PHASE_DIVERSITY_NARGS] = {
1001                 mode & 0x07,
1002         };
1003 
1004         return si476x_core_send_command(core, CMD_FM_PHASE_DIVERSITY,
1005                                         args, ARRAY_SIZE(args),
1006                                         resp, ARRAY_SIZE(resp),
1007                                         SI476X_DEFAULT_TIMEOUT);
1008 }
1009 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity);
1010 /**
1011  * si476x_core_cmd_fm_phase_div_status() - get the phase diversity
1012  * status
1013  *
1014  * @core: si476x device
1015  *
1016  * NOTE caller must hold core lock
1017  *
1018  * Function returns the value of the status bit in case of success and
1019  * negative error code in case of failre.
1020  */
1021 int si476x_core_cmd_fm_phase_div_status(struct si476x_core *core)
1022 {
1023         int err;
1024         u8 resp[CMD_FM_PHASE_DIV_STATUS_NRESP];
1025 
1026         err = si476x_core_send_command(core, CMD_FM_PHASE_DIV_STATUS,
1027                                        NULL, 0,
1028                                        resp, ARRAY_SIZE(resp),
1029                                        SI476X_DEFAULT_TIMEOUT);
1030 
1031         return (err < 0) ? err : resp[1];
1032 }
1033 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status);
1034 
1035 
1036 /**
1037  * si476x_cmd_am_seek_start - send 'FM_SEEK_START' command to the
1038  * device
1039  * @core  - device to send the command to
1040  * @seekup - if set the direction of the search is 'up'
1041  * @wrap   - if set seek wraps when hitting band limit
1042  *
1043  * This function begins search for a valid station. The station is
1044  * considered valid when 'FM_VALID_SNR_THRESHOLD' and
1045  * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
1046  * are met.
1047  *
1048  * Function returns 0 on success and negative error code on failure
1049  */
1050 int si476x_core_cmd_am_seek_start(struct si476x_core *core,
1051                                   bool seekup, bool wrap)
1052 {
1053         u8       resp[CMD_AM_SEEK_START_NRESP];
1054         const u8 args[CMD_AM_SEEK_START_NARGS] = {
1055                 seekup << 3 | wrap << 2,
1056         };
1057 
1058         return si476x_cmd_tune_seek_freq(core,  CMD_AM_SEEK_START,
1059                                          args, sizeof(args),
1060                                          resp, sizeof(resp));
1061 }
1062 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start);
1063 
1064 
1065 
1066 static int si476x_core_cmd_power_up_a10(struct si476x_core *core,
1067                                         struct si476x_power_up_args *puargs)
1068 {
1069         u8       resp[CMD_POWER_UP_A10_NRESP];
1070         const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1071         const bool ctsen  = (core->client->irq != 0);
1072         const u8 args[CMD_POWER_UP_A10_NARGS] = {
1073                 0xF7,           /* Reserved, always 0xF7 */
1074                 0x3F & puargs->xcload,  /* First two bits are reserved to be
1075                                  * zeros */
1076                 ctsen << 7 | intsel << 6 | 0x07, /* Last five bits
1077                                                    * are reserved to
1078                                                    * be written as 0x7 */
1079                 puargs->func << 4 | puargs->freq,
1080                 0x11,           /* Reserved, always 0x11 */
1081         };
1082 
1083         return si476x_core_send_command(core, CMD_POWER_UP,
1084                                         args, ARRAY_SIZE(args),
1085                                         resp, ARRAY_SIZE(resp),
1086                                         SI476X_TIMEOUT_POWER_UP);
1087 }
1088 
1089 static int si476x_core_cmd_power_up_a20(struct si476x_core *core,
1090                                  struct si476x_power_up_args *puargs)
1091 {
1092         u8       resp[CMD_POWER_UP_A20_NRESP];
1093         const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1094         const bool ctsen  = (core->client->irq != 0);
1095         const u8 args[CMD_POWER_UP_A20_NARGS] = {
1096                 puargs->ibias6x << 7 | puargs->xstart,
1097                 0x3F & puargs->xcload,  /* First two bits are reserved to be
1098                                          * zeros */
1099                 ctsen << 7 | intsel << 6 | puargs->fastboot << 5 |
1100                 puargs->xbiashc << 3 | puargs->xbias,
1101                 puargs->func << 4 | puargs->freq,
1102                 0x10 | puargs->xmode,
1103         };
1104 
1105         return si476x_core_send_command(core, CMD_POWER_UP,
1106                                         args, ARRAY_SIZE(args),
1107                                         resp, ARRAY_SIZE(resp),
1108                                         SI476X_TIMEOUT_POWER_UP);
1109 }
1110 
1111 static int si476x_core_cmd_power_down_a10(struct si476x_core *core,
1112                                           struct si476x_power_down_args *pdargs)
1113 {
1114         u8 resp[CMD_POWER_DOWN_A10_NRESP];
1115 
1116         return si476x_core_send_command(core, CMD_POWER_DOWN,
1117                                         NULL, 0,
1118                                         resp, ARRAY_SIZE(resp),
1119                                         SI476X_DEFAULT_TIMEOUT);
1120 }
1121 
1122 static int si476x_core_cmd_power_down_a20(struct si476x_core *core,
1123                                           struct si476x_power_down_args *pdargs)
1124 {
1125         u8 resp[CMD_POWER_DOWN_A20_NRESP];
1126         const u8 args[CMD_POWER_DOWN_A20_NARGS] = {
1127                 pdargs->xosc,
1128         };
1129         return si476x_core_send_command(core, CMD_POWER_DOWN,
1130                                         args, ARRAY_SIZE(args),
1131                                         resp, ARRAY_SIZE(resp),
1132                                         SI476X_DEFAULT_TIMEOUT);
1133 }
1134 
1135 static int si476x_core_cmd_am_tune_freq_a10(struct si476x_core *core,
1136                                         struct si476x_tune_freq_args *tuneargs)
1137 {
1138 
1139         const int am_freq = tuneargs->freq;
1140         u8       resp[CMD_AM_TUNE_FREQ_NRESP];
1141         const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1142                 (tuneargs->hd << 6),
1143                 msb(am_freq),
1144                 lsb(am_freq),
1145         };
1146 
1147         return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, args,
1148                                          sizeof(args),
1149                                          resp, sizeof(resp));
1150 }
1151 
1152 static int si476x_core_cmd_am_tune_freq_a20(struct si476x_core *core,
1153                                         struct si476x_tune_freq_args *tuneargs)
1154 {
1155         const int am_freq = tuneargs->freq;
1156         u8       resp[CMD_AM_TUNE_FREQ_NRESP];
1157         const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1158                 (tuneargs->zifsr << 6) | (tuneargs->injside & 0x03),
1159                 msb(am_freq),
1160                 lsb(am_freq),
1161         };
1162 
1163         return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ,
1164                                          args, sizeof(args),
1165                                          resp, sizeof(resp));
1166 }
1167 
1168 static int si476x_core_cmd_fm_rsq_status_a10(struct si476x_core *core,
1169                                         struct si476x_rsq_status_args *rsqargs,
1170                                         struct si476x_rsq_status_report *report)
1171 {
1172         int err;
1173         u8       resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1174         const u8 args[CMD_FM_RSQ_STATUS_A10_NARGS] = {
1175                 rsqargs->rsqack << 3 | rsqargs->attune << 2 |
1176                 rsqargs->cancel << 1 | rsqargs->stcack,
1177         };
1178 
1179         err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1180                                        args, ARRAY_SIZE(args),
1181                                        resp, ARRAY_SIZE(resp),
1182                                        SI476X_DEFAULT_TIMEOUT);
1183         /*
1184          * Besides getting received signal quality information this
1185          * command can be used to just acknowledge different interrupt
1186          * flags in those cases it is useless to copy and parse
1187          * received data so user can pass NULL, and thus avoid
1188          * unnecessary copying.
1189          */
1190         if (err < 0 || report == NULL)
1191                 return err;
1192 
1193         report->multhint        = 0x80 & resp[1];
1194         report->multlint        = 0x40 & resp[1];
1195         report->snrhint         = 0x08 & resp[1];
1196         report->snrlint         = 0x04 & resp[1];
1197         report->rssihint        = 0x02 & resp[1];
1198         report->rssilint        = 0x01 & resp[1];
1199 
1200         report->bltf            = 0x80 & resp[2];
1201         report->snr_ready       = 0x20 & resp[2];
1202         report->rssiready       = 0x08 & resp[2];
1203         report->afcrl           = 0x02 & resp[2];
1204         report->valid           = 0x01 & resp[2];
1205 
1206         report->readfreq        = get_unaligned_be16(resp + 3);
1207         report->freqoff         = resp[5];
1208         report->rssi            = resp[6];
1209         report->snr             = resp[7];
1210         report->lassi           = resp[9];
1211         report->hassi           = resp[10];
1212         report->mult            = resp[11];
1213         report->dev             = resp[12];
1214         report->readantcap      = get_unaligned_be16(resp + 13);
1215         report->assi            = resp[15];
1216         report->usn             = resp[16];
1217 
1218         return err;
1219 }
1220 
1221 static int si476x_core_cmd_fm_rsq_status_a20(struct si476x_core *core,
1222                                      struct si476x_rsq_status_args *rsqargs,
1223                                      struct si476x_rsq_status_report *report)
1224 {
1225         int err;
1226         u8       resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1227         const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1228                 rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1229                 rsqargs->attune  << 2 | rsqargs->cancel << 1 |
1230                 rsqargs->stcack,
1231         };
1232 
1233         err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1234                                        args, ARRAY_SIZE(args),
1235                                        resp, ARRAY_SIZE(resp),
1236                                        SI476X_DEFAULT_TIMEOUT);
1237         /*
1238          * Besides getting received signal quality information this
1239          * command can be used to just acknowledge different interrupt
1240          * flags in those cases it is useless to copy and parse
1241          * received data so user can pass NULL, and thus avoid
1242          * unnecessary copying.
1243          */
1244         if (err < 0 || report == NULL)
1245                 return err;
1246 
1247         report->multhint        = 0x80 & resp[1];
1248         report->multlint        = 0x40 & resp[1];
1249         report->snrhint         = 0x08 & resp[1];
1250         report->snrlint         = 0x04 & resp[1];
1251         report->rssihint        = 0x02 & resp[1];
1252         report->rssilint        = 0x01 & resp[1];
1253 
1254         report->bltf            = 0x80 & resp[2];
1255         report->snr_ready       = 0x20 & resp[2];
1256         report->rssiready       = 0x08 & resp[2];
1257         report->afcrl           = 0x02 & resp[2];
1258         report->valid           = 0x01 & resp[2];
1259 
1260         report->readfreq        = get_unaligned_be16(resp + 3);
1261         report->freqoff         = resp[5];
1262         report->rssi            = resp[6];
1263         report->snr             = resp[7];
1264         report->lassi           = resp[9];
1265         report->hassi           = resp[10];
1266         report->mult            = resp[11];
1267         report->dev             = resp[12];
1268         report->readantcap      = get_unaligned_be16(resp + 13);
1269         report->assi            = resp[15];
1270         report->usn             = resp[16];
1271 
1272         return err;
1273 }
1274 
1275 
1276 static int si476x_core_cmd_fm_rsq_status_a30(struct si476x_core *core,
1277                                         struct si476x_rsq_status_args *rsqargs,
1278                                         struct si476x_rsq_status_report *report)
1279 {
1280         int err;
1281         u8       resp[CMD_FM_RSQ_STATUS_A30_NRESP];
1282         const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1283                 rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1284                 rsqargs->attune << 2 | rsqargs->cancel << 1 |
1285                 rsqargs->stcack,
1286         };
1287 
1288         err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1289                                        args, ARRAY_SIZE(args),
1290                                        resp, ARRAY_SIZE(resp),
1291                                        SI476X_DEFAULT_TIMEOUT);
1292         /*
1293          * Besides getting received signal quality information this
1294          * command can be used to just acknowledge different interrupt
1295          * flags in those cases it is useless to copy and parse
1296          * received data so user can pass NULL, and thus avoid
1297          * unnecessary copying.
1298          */
1299         if (err < 0 || report == NULL)
1300                 return err;
1301 
1302         report->multhint        = 0x80 & resp[1];
1303         report->multlint        = 0x40 & resp[1];
1304         report->snrhint         = 0x08 & resp[1];
1305         report->snrlint         = 0x04 & resp[1];
1306         report->rssihint        = 0x02 & resp[1];
1307         report->rssilint        = 0x01 & resp[1];
1308 
1309         report->bltf            = 0x80 & resp[2];
1310         report->snr_ready       = 0x20 & resp[2];
1311         report->rssiready       = 0x08 & resp[2];
1312         report->injside         = 0x04 & resp[2];
1313         report->afcrl           = 0x02 & resp[2];
1314         report->valid           = 0x01 & resp[2];
1315 
1316         report->readfreq        = get_unaligned_be16(resp + 3);
1317         report->freqoff         = resp[5];
1318         report->rssi            = resp[6];
1319         report->snr             = resp[7];
1320         report->issi            = resp[8];
1321         report->lassi           = resp[9];
1322         report->hassi           = resp[10];
1323         report->mult            = resp[11];
1324         report->dev             = resp[12];
1325         report->readantcap      = get_unaligned_be16(resp + 13);
1326         report->assi            = resp[15];
1327         report->usn             = resp[16];
1328 
1329         report->pilotdev        = resp[17];
1330         report->rdsdev          = resp[18];
1331         report->assidev         = resp[19];
1332         report->strongdev       = resp[20];
1333         report->rdspi           = get_unaligned_be16(resp + 21);
1334 
1335         return err;
1336 }
1337 
1338 static int si476x_core_cmd_fm_tune_freq_a10(struct si476x_core *core,
1339                                         struct si476x_tune_freq_args *tuneargs)
1340 {
1341         u8       resp[CMD_FM_TUNE_FREQ_NRESP];
1342         const u8 args[CMD_FM_TUNE_FREQ_A10_NARGS] = {
1343                 (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1344                 | (tuneargs->smoothmetrics << 2),
1345                 msb(tuneargs->freq),
1346                 lsb(tuneargs->freq),
1347                 msb(tuneargs->antcap),
1348                 lsb(tuneargs->antcap)
1349         };
1350 
1351         return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1352                                          args, sizeof(args),
1353                                          resp, sizeof(resp));
1354 }
1355 
1356 static int si476x_core_cmd_fm_tune_freq_a20(struct si476x_core *core,
1357                                         struct si476x_tune_freq_args *tuneargs)
1358 {
1359         u8       resp[CMD_FM_TUNE_FREQ_NRESP];
1360         const u8 args[CMD_FM_TUNE_FREQ_A20_NARGS] = {
1361                 (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1362                 |  (tuneargs->smoothmetrics << 2) | (tuneargs->injside),
1363                 msb(tuneargs->freq),
1364                 lsb(tuneargs->freq),
1365         };
1366 
1367         return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1368                                          args, sizeof(args),
1369                                          resp, sizeof(resp));
1370 }
1371 
1372 static int si476x_core_cmd_agc_status_a20(struct si476x_core *core,
1373                                         struct si476x_agc_status_report *report)
1374 {
1375         int err;
1376         u8 resp[CMD_AGC_STATUS_NRESP_A20];
1377 
1378         if (!report)
1379                 return -EINVAL;
1380 
1381         err = si476x_core_send_command(core, CMD_AGC_STATUS,
1382                                        NULL, 0,
1383                                        resp, ARRAY_SIZE(resp),
1384                                        SI476X_DEFAULT_TIMEOUT);
1385         if (err < 0)
1386                 return err;
1387 
1388         report->mxhi            = resp[1] & SI476X_AGC_MXHI;
1389         report->mxlo            = resp[1] & SI476X_AGC_MXLO;
1390         report->lnahi           = resp[1] & SI476X_AGC_LNAHI;
1391         report->lnalo           = resp[1] & SI476X_AGC_LNALO;
1392         report->fmagc1          = resp[2];
1393         report->fmagc2          = resp[3];
1394         report->pgagain         = resp[4];
1395         report->fmwblang        = resp[5];
1396 
1397         return err;
1398 }
1399 
1400 static int si476x_core_cmd_agc_status_a10(struct si476x_core *core,
1401                                         struct si476x_agc_status_report *report)
1402 {
1403         int err;
1404         u8 resp[CMD_AGC_STATUS_NRESP_A10];
1405 
1406         if (!report)
1407                 return -EINVAL;
1408 
1409         err = si476x_core_send_command(core, CMD_AGC_STATUS,
1410                                        NULL, 0,
1411                                        resp, ARRAY_SIZE(resp),
1412                                        SI476X_DEFAULT_TIMEOUT);
1413         if (err < 0)
1414                 return err;
1415 
1416         report->mxhi    = resp[1] & SI476X_AGC_MXHI;
1417         report->mxlo    = resp[1] & SI476X_AGC_MXLO;
1418         report->lnahi   = resp[1] & SI476X_AGC_LNAHI;
1419         report->lnalo   = resp[1] & SI476X_AGC_LNALO;
1420 
1421         return err;
1422 }
1423 
1424 typedef int (*tune_freq_func_t) (struct si476x_core *core,
1425                                  struct si476x_tune_freq_args *tuneargs);
1426 
1427 static struct {
1428         int (*power_up)(struct si476x_core *,
1429                         struct si476x_power_up_args *);
1430         int (*power_down)(struct si476x_core *,
1431                           struct si476x_power_down_args *);
1432 
1433         tune_freq_func_t fm_tune_freq;
1434         tune_freq_func_t am_tune_freq;
1435 
1436         int (*fm_rsq_status)(struct si476x_core *,
1437                              struct si476x_rsq_status_args *,
1438                              struct si476x_rsq_status_report *);
1439 
1440         int (*agc_status)(struct si476x_core *,
1441                           struct si476x_agc_status_report *);
1442         int (*intb_pin_cfg)(struct si476x_core *core,
1443                             enum si476x_intb_config intb,
1444                             enum si476x_a1_config a1);
1445 } si476x_cmds_vtable[] = {
1446         [SI476X_REVISION_A10] = {
1447                 .power_up       = si476x_core_cmd_power_up_a10,
1448                 .power_down     = si476x_core_cmd_power_down_a10,
1449                 .fm_tune_freq   = si476x_core_cmd_fm_tune_freq_a10,
1450                 .am_tune_freq   = si476x_core_cmd_am_tune_freq_a10,
1451                 .fm_rsq_status  = si476x_core_cmd_fm_rsq_status_a10,
1452                 .agc_status     = si476x_core_cmd_agc_status_a10,
1453                 .intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a10,
1454         },
1455         [SI476X_REVISION_A20] = {
1456                 .power_up       = si476x_core_cmd_power_up_a20,
1457                 .power_down     = si476x_core_cmd_power_down_a20,
1458                 .fm_tune_freq   = si476x_core_cmd_fm_tune_freq_a20,
1459                 .am_tune_freq   = si476x_core_cmd_am_tune_freq_a20,
1460                 .fm_rsq_status  = si476x_core_cmd_fm_rsq_status_a20,
1461                 .agc_status     = si476x_core_cmd_agc_status_a20,
1462                 .intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a20,
1463         },
1464         [SI476X_REVISION_A30] = {
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_a30,
1470                 .agc_status     = si476x_core_cmd_agc_status_a20,
1471                 .intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a20,
1472         },
1473 };
1474 
1475 int si476x_core_cmd_power_up(struct si476x_core *core,
1476                              struct si476x_power_up_args *args)
1477 {
1478         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1479                core->revision == -1);
1480         return si476x_cmds_vtable[core->revision].power_up(core, args);
1481 }
1482 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up);
1483 
1484 int si476x_core_cmd_power_down(struct si476x_core *core,
1485                                struct si476x_power_down_args *args)
1486 {
1487         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1488                core->revision == -1);
1489         return si476x_cmds_vtable[core->revision].power_down(core, args);
1490 }
1491 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down);
1492 
1493 int si476x_core_cmd_fm_tune_freq(struct si476x_core *core,
1494                                  struct si476x_tune_freq_args *args)
1495 {
1496         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1497                core->revision == -1);
1498         return si476x_cmds_vtable[core->revision].fm_tune_freq(core, args);
1499 }
1500 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq);
1501 
1502 int si476x_core_cmd_am_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].am_tune_freq(core, args);
1508 }
1509 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq);
1510 
1511 int si476x_core_cmd_fm_rsq_status(struct si476x_core *core,
1512                                   struct si476x_rsq_status_args *args,
1513                                   struct si476x_rsq_status_report *report)
1514 
1515 {
1516         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1517                core->revision == -1);
1518         return si476x_cmds_vtable[core->revision].fm_rsq_status(core, args,
1519                                                                 report);
1520 }
1521 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status);
1522 
1523 int si476x_core_cmd_agc_status(struct si476x_core *core,
1524                                   struct si476x_agc_status_report *report)
1525 
1526 {
1527         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1528                core->revision == -1);
1529         return si476x_cmds_vtable[core->revision].agc_status(core, report);
1530 }
1531 EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status);
1532 
1533 int si476x_core_cmd_intb_pin_cfg(struct si476x_core *core,
1534                             enum si476x_intb_config intb,
1535                             enum si476x_a1_config a1)
1536 {
1537         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1538                core->revision == -1);
1539 
1540         return si476x_cmds_vtable[core->revision].intb_pin_cfg(core, intb, a1);
1541 }
1542 EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg);
1543 
1544 MODULE_LICENSE("GPL");
1545 MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
1546 MODULE_DESCRIPTION("API for command exchange for si476x");

/* [<][>][^][v][top][bottom][index][help] */