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