linux/drivers/mfd/si476x-cmd.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * drivers/mfd/si476x-cmd.c -- Subroutines implementing command
   4 * protocol of si476x series of chips
   5 *
   6 * Copyright (C) 2012 Innovative Converged Devices(ICD)
   7 * Copyright (C) 2013 Andrey Smirnov
   8 *
   9 * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/completion.h>
  14#include <linux/delay.h>
  15#include <linux/atomic.h>
  16#include <linux/i2c.h>
  17#include <linux/device.h>
  18#include <linux/gpio.h>
  19#include <linux/videodev2.h>
  20
  21#include <linux/mfd/si476x-core.h>
  22
  23#include <asm/unaligned.h>
  24
  25#define msb(x)                  ((u8)((u16) x >> 8))
  26#define lsb(x)                  ((u8)((u16) x &  0x00FF))
  27
  28
  29
  30#define CMD_POWER_UP                            0x01
  31#define CMD_POWER_UP_A10_NRESP                  1
  32#define CMD_POWER_UP_A10_NARGS                  5
  33
  34#define CMD_POWER_UP_A20_NRESP                  1
  35#define CMD_POWER_UP_A20_NARGS                  5
  36
  37#define POWER_UP_DELAY_MS                       110
  38
  39#define CMD_POWER_DOWN                          0x11
  40#define CMD_POWER_DOWN_A10_NRESP                1
  41
  42#define CMD_POWER_DOWN_A20_NRESP                1
  43#define CMD_POWER_DOWN_A20_NARGS                1
  44
  45#define CMD_FUNC_INFO                           0x12
  46#define CMD_FUNC_INFO_NRESP                     7
  47
  48#define CMD_SET_PROPERTY                        0x13
  49#define CMD_SET_PROPERTY_NARGS                  5
  50#define CMD_SET_PROPERTY_NRESP                  1
  51
  52#define CMD_GET_PROPERTY                        0x14
  53#define CMD_GET_PROPERTY_NARGS                  3
  54#define CMD_GET_PROPERTY_NRESP                  4
  55
  56#define CMD_AGC_STATUS                          0x17
  57#define CMD_AGC_STATUS_NRESP_A10                2
  58#define CMD_AGC_STATUS_NRESP_A20                6
  59
  60#define PIN_CFG_BYTE(x) (0x7F & (x))
  61#define CMD_DIG_AUDIO_PIN_CFG                   0x18
  62#define CMD_DIG_AUDIO_PIN_CFG_NARGS             4
  63#define CMD_DIG_AUDIO_PIN_CFG_NRESP             5
  64
  65#define CMD_ZIF_PIN_CFG                         0x19
  66#define CMD_ZIF_PIN_CFG_NARGS                   4
  67#define CMD_ZIF_PIN_CFG_NRESP                   5
  68
  69#define CMD_IC_LINK_GPO_CTL_PIN_CFG             0x1A
  70#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS       4
  71#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP       5
  72
  73#define CMD_ANA_AUDIO_PIN_CFG                   0x1B
  74#define CMD_ANA_AUDIO_PIN_CFG_NARGS             1
  75#define CMD_ANA_AUDIO_PIN_CFG_NRESP             2
  76
  77#define CMD_INTB_PIN_CFG                        0x1C
  78#define CMD_INTB_PIN_CFG_NARGS                  2
  79#define CMD_INTB_PIN_CFG_A10_NRESP              6
  80#define CMD_INTB_PIN_CFG_A20_NRESP              3
  81
  82#define CMD_FM_TUNE_FREQ                        0x30
  83#define CMD_FM_TUNE_FREQ_A10_NARGS              5
  84#define CMD_FM_TUNE_FREQ_A20_NARGS              3
  85#define CMD_FM_TUNE_FREQ_NRESP                  1
  86
  87#define CMD_FM_RSQ_STATUS                       0x32
  88
  89#define CMD_FM_RSQ_STATUS_A10_NARGS             1
  90#define CMD_FM_RSQ_STATUS_A10_NRESP             17
  91#define CMD_FM_RSQ_STATUS_A30_NARGS             1
  92#define CMD_FM_RSQ_STATUS_A30_NRESP             23
  93
  94
  95#define CMD_FM_SEEK_START                       0x31
  96#define CMD_FM_SEEK_START_NARGS                 1
  97#define CMD_FM_SEEK_START_NRESP                 1
  98
  99#define CMD_FM_RDS_STATUS                       0x36
 100#define CMD_FM_RDS_STATUS_NARGS                 1
 101#define CMD_FM_RDS_STATUS_NRESP                 16
 102
 103#define CMD_FM_RDS_BLOCKCOUNT                   0x37
 104#define CMD_FM_RDS_BLOCKCOUNT_NARGS             1
 105#define CMD_FM_RDS_BLOCKCOUNT_NRESP             8
 106
 107#define CMD_FM_PHASE_DIVERSITY                  0x38
 108#define CMD_FM_PHASE_DIVERSITY_NARGS            1
 109#define CMD_FM_PHASE_DIVERSITY_NRESP            1
 110
 111#define CMD_FM_PHASE_DIV_STATUS                 0x39
 112#define CMD_FM_PHASE_DIV_STATUS_NRESP           2
 113
 114#define CMD_AM_TUNE_FREQ                        0x40
 115#define CMD_AM_TUNE_FREQ_NARGS                  3
 116#define CMD_AM_TUNE_FREQ_NRESP                  1
 117
 118#define CMD_AM_RSQ_STATUS                       0x42
 119#define CMD_AM_RSQ_STATUS_NARGS                 1
 120#define CMD_AM_RSQ_STATUS_NRESP                 13
 121
 122#define CMD_AM_SEEK_START                       0x41
 123#define CMD_AM_SEEK_START_NARGS                 1
 124#define CMD_AM_SEEK_START_NRESP                 1
 125
 126
 127#define CMD_AM_ACF_STATUS                       0x45
 128#define CMD_AM_ACF_STATUS_NRESP                 6
 129#define CMD_AM_ACF_STATUS_NARGS                 1
 130
 131#define CMD_FM_ACF_STATUS                       0x35
 132#define CMD_FM_ACF_STATUS_NRESP                 8
 133#define CMD_FM_ACF_STATUS_NARGS                 1
 134
 135#define CMD_MAX_ARGS_COUNT                      (10)
 136
 137
 138enum si476x_acf_status_report_bits {
 139        SI476X_ACF_BLEND_INT    = (1 << 4),
 140        SI476X_ACF_HIBLEND_INT  = (1 << 3),
 141        SI476X_ACF_HICUT_INT    = (1 << 2),
 142        SI476X_ACF_CHBW_INT     = (1 << 1),
 143        SI476X_ACF_SOFTMUTE_INT = (1 << 0),
 144
 145        SI476X_ACF_SMUTE        = (1 << 0),
 146        SI476X_ACF_SMATTN       = 0x1f,
 147        SI476X_ACF_PILOT        = (1 << 7),
 148        SI476X_ACF_STBLEND      = ~SI476X_ACF_PILOT,
 149};
 150
 151enum si476x_agc_status_report_bits {
 152        SI476X_AGC_MXHI         = (1 << 5),
 153        SI476X_AGC_MXLO         = (1 << 4),
 154        SI476X_AGC_LNAHI        = (1 << 3),
 155        SI476X_AGC_LNALO        = (1 << 2),
 156};
 157
 158enum si476x_errors {
 159        SI476X_ERR_BAD_COMMAND          = 0x10,
 160        SI476X_ERR_BAD_ARG1             = 0x11,
 161        SI476X_ERR_BAD_ARG2             = 0x12,
 162        SI476X_ERR_BAD_ARG3             = 0x13,
 163        SI476X_ERR_BAD_ARG4             = 0x14,
 164        SI476X_ERR_BUSY                 = 0x18,
 165        SI476X_ERR_BAD_INTERNAL_MEMORY  = 0x20,
 166        SI476X_ERR_BAD_PATCH            = 0x30,
 167        SI476X_ERR_BAD_BOOT_MODE        = 0x31,
 168        SI476X_ERR_BAD_PROPERTY         = 0x40,
 169};
 170
 171static int si476x_core_parse_and_nag_about_error(struct si476x_core *core)
 172{
 173        int err;
 174        char *cause;
 175        u8 buffer[2];
 176
 177        if (core->revision != SI476X_REVISION_A10) {
 178                err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV,
 179                                           buffer, sizeof(buffer));
 180                if (err == sizeof(buffer)) {
 181                        switch (buffer[1]) {
 182                        case SI476X_ERR_BAD_COMMAND:
 183                                cause = "Bad command";
 184                                err = -EINVAL;
 185                                break;
 186                        case SI476X_ERR_BAD_ARG1:
 187                                cause = "Bad argument #1";
 188                                err = -EINVAL;
 189                                break;
 190                        case SI476X_ERR_BAD_ARG2:
 191                                cause = "Bad argument #2";
 192                                err = -EINVAL;
 193                                break;
 194                        case SI476X_ERR_BAD_ARG3:
 195                                cause = "Bad argument #3";
 196                                err = -EINVAL;
 197                                break;
 198                        case SI476X_ERR_BAD_ARG4:
 199                                cause = "Bad argument #4";
 200                                err = -EINVAL;
 201                                break;
 202                        case SI476X_ERR_BUSY:
 203                                cause = "Chip is busy";
 204                                err = -EBUSY;
 205                                break;
 206                        case SI476X_ERR_BAD_INTERNAL_MEMORY:
 207                                cause = "Bad internal memory";
 208                                err = -EIO;
 209                                break;
 210                        case SI476X_ERR_BAD_PATCH:
 211                                cause = "Bad patch";
 212                                err = -EINVAL;
 213                                break;
 214                        case SI476X_ERR_BAD_BOOT_MODE:
 215                                cause = "Bad boot mode";
 216                                err = -EINVAL;
 217                                break;
 218                        case SI476X_ERR_BAD_PROPERTY:
 219                                cause = "Bad property";
 220                                err = -EINVAL;
 221                                break;
 222                        default:
 223                                cause = "Unknown";
 224                                err = -EIO;
 225                        }
 226
 227                        dev_err(&core->client->dev,
 228                                "[Chip error status]: %s\n", cause);
 229                } else {
 230                        dev_err(&core->client->dev,
 231                                "Failed to fetch error code\n");
 232                        err = (err >= 0) ? -EIO : err;
 233                }
 234        } else {
 235                err = -EIO;
 236        }
 237
 238        return err;
 239}
 240
 241/**
 242 * si476x_core_send_command() - sends a command to si476x and waits its
 243 * response
 244 * @core:    si476x_device structure for the device we are
 245 *            communicating with
 246 * @command:  command id
 247 * @args:     command arguments we are sending
 248 * @argn:     actual size of @args
 249 * @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 */
 257static int si476x_core_send_command(struct si476x_core *core,
 258                                    const u8 command,
 259                                    const u8 args[],
 260                                    const int argn,
 261                                    u8 resp[],
 262                                    const int respn,
 263                                    const int usecs)
 264{
 265        struct i2c_client *client = core->client;
 266        int err;
 267        u8  data[CMD_MAX_ARGS_COUNT + 1];
 268
 269        if (argn > CMD_MAX_ARGS_COUNT) {
 270                err = -ENOMEM;
 271                goto exit;
 272        }
 273
 274        if (!client->adapter) {
 275                err = -ENODEV;
 276                goto exit;
 277        }
 278
 279        /* First send the command and its arguments */
 280        data[0] = command;
 281        memcpy(&data[1], args, argn);
 282        dev_dbg(&client->dev, "Command:\n %*ph\n", argn + 1, data);
 283
 284        err = si476x_core_i2c_xfer(core, SI476X_I2C_SEND,
 285                                   (char *) data, argn + 1);
 286        if (err != argn + 1) {
 287                dev_err(&core->client->dev,
 288                        "Error while sending command 0x%02x\n",
 289                        command);
 290                err = (err >= 0) ? -EIO : err;
 291                goto exit;
 292        }
 293        /* Set CTS to zero only after the command is send to avoid
 294         * possible racing conditions when working in polling mode */
 295        atomic_set(&core->cts, 0);
 296
 297        /* if (unlikely(command == CMD_POWER_DOWN) */
 298        if (!wait_event_timeout(core->command,
 299                                atomic_read(&core->cts),
 300                                usecs_to_jiffies(usecs) + 1))
 301                dev_warn(&core->client->dev,
 302                         "(%s) [CMD 0x%02x] Answer timeout.\n",
 303                         __func__, command);
 304
 305        /*
 306          When working in polling mode, for some reason the tuner will
 307          report CTS bit as being set in the first status byte read,
 308          but all the consequtive ones will return zeros until the
 309          tuner is actually completed the POWER_UP command. To
 310          workaround that we wait for second CTS to be reported
 311         */
 312        if (unlikely(!core->client->irq && command == CMD_POWER_UP)) {
 313                if (!wait_event_timeout(core->command,
 314                                        atomic_read(&core->cts),
 315                                        usecs_to_jiffies(usecs) + 1))
 316                        dev_warn(&core->client->dev,
 317                                 "(%s) Power up took too much time.\n",
 318                                 __func__);
 319        }
 320
 321        /* Then get the response */
 322        err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, resp, respn);
 323        if (err != respn) {
 324                dev_err(&core->client->dev,
 325                        "Error while reading response for command 0x%02x\n",
 326                        command);
 327                err = (err >= 0) ? -EIO : err;
 328                goto exit;
 329        }
 330        dev_dbg(&client->dev, "Response:\n %*ph\n", respn, resp);
 331
 332        err = 0;
 333
 334        if (resp[0] & SI476X_ERR) {
 335                dev_err(&core->client->dev,
 336                        "[CMD 0x%02x] Chip set error flag\n", command);
 337                err = si476x_core_parse_and_nag_about_error(core);
 338                goto exit;
 339        }
 340
 341        if (!(resp[0] & SI476X_CTS))
 342                err = -EBUSY;
 343exit:
 344        return err;
 345}
 346
 347static int si476x_cmd_clear_stc(struct si476x_core *core)
 348{
 349        int err;
 350        struct si476x_rsq_status_args args = {
 351                .primary        = false,
 352                .rsqack         = false,
 353                .attune         = false,
 354                .cancel         = false,
 355                .stcack         = true,
 356        };
 357
 358        switch (core->power_up_parameters.func) {
 359        case SI476X_FUNC_FM_RECEIVER:
 360                err = si476x_core_cmd_fm_rsq_status(core, &args, NULL);
 361                break;
 362        case SI476X_FUNC_AM_RECEIVER:
 363                err = si476x_core_cmd_am_rsq_status(core, &args, NULL);
 364                break;
 365        default:
 366                err = -EINVAL;
 367        }
 368
 369        return err;
 370}
 371
 372static int si476x_cmd_tune_seek_freq(struct si476x_core *core,
 373                                     uint8_t cmd,
 374                                     const uint8_t args[], size_t argn,
 375                                     uint8_t *resp, size_t respn)
 376{
 377        int err;
 378
 379
 380        atomic_set(&core->stc, 0);
 381        err = si476x_core_send_command(core, cmd, args, argn, resp, respn,
 382                                       SI476X_TIMEOUT_TUNE);
 383        if (!err) {
 384                wait_event_killable(core->tuning,
 385                                    atomic_read(&core->stc));
 386                si476x_cmd_clear_stc(core);
 387        }
 388
 389        return err;
 390}
 391
 392/**
 393 * si476x_cmd_func_info() - send 'FUNC_INFO' command to the device
 394 * @core: device to send the command to
 395 * @info:  struct si476x_func_info to fill all the information
 396 *         returned by the command
 397 *
 398 * The command requests the firmware and patch version for currently
 399 * loaded firmware (dependent on the function of the device FM/AM/WB)
 400 *
 401 * Function returns 0 on succsess and negative error code on
 402 * failure
 403 */
 404int si476x_core_cmd_func_info(struct si476x_core *core,
 405                              struct si476x_func_info *info)
 406{
 407        int err;
 408        u8  resp[CMD_FUNC_INFO_NRESP];
 409
 410        err = si476x_core_send_command(core, CMD_FUNC_INFO,
 411                                       NULL, 0,
 412                                       resp, ARRAY_SIZE(resp),
 413                                       SI476X_DEFAULT_TIMEOUT);
 414
 415        info->firmware.major    = resp[1];
 416        info->firmware.minor[0] = resp[2];
 417        info->firmware.minor[1] = resp[3];
 418
 419        info->patch_id = ((u16) resp[4] << 8) | resp[5];
 420        info->func     = resp[6];
 421
 422        return err;
 423}
 424EXPORT_SYMBOL_GPL(si476x_core_cmd_func_info);
 425
 426/**
 427 * si476x_cmd_set_property() - send 'SET_PROPERTY' command to the device
 428 * @core:    device to send the command to
 429 * @property: property address
 430 * @value:    property value
 431 *
 432 * Function returns 0 on succsess and negative error code on
 433 * failure
 434 */
 435int si476x_core_cmd_set_property(struct si476x_core *core,
 436                                 u16 property, u16 value)
 437{
 438        u8       resp[CMD_SET_PROPERTY_NRESP];
 439        const u8 args[CMD_SET_PROPERTY_NARGS] = {
 440                0x00,
 441                msb(property),
 442                lsb(property),
 443                msb(value),
 444                lsb(value),
 445        };
 446
 447        return si476x_core_send_command(core, CMD_SET_PROPERTY,
 448                                        args, ARRAY_SIZE(args),
 449                                        resp, ARRAY_SIZE(resp),
 450                                        SI476X_DEFAULT_TIMEOUT);
 451}
 452EXPORT_SYMBOL_GPL(si476x_core_cmd_set_property);
 453
 454/**
 455 * si476x_cmd_get_property() - send 'GET_PROPERTY' command to the device
 456 * @core:    device to send the command to
 457 * @property: property address
 458 *
 459 * Function return the value of property as u16 on success or a
 460 * negative error on failure
 461 */
 462int si476x_core_cmd_get_property(struct si476x_core *core, u16 property)
 463{
 464        int err;
 465        u8       resp[CMD_GET_PROPERTY_NRESP];
 466        const u8 args[CMD_GET_PROPERTY_NARGS] = {
 467                0x00,
 468                msb(property),
 469                lsb(property),
 470        };
 471
 472        err = si476x_core_send_command(core, CMD_GET_PROPERTY,
 473                                       args, ARRAY_SIZE(args),
 474                                       resp, ARRAY_SIZE(resp),
 475                                       SI476X_DEFAULT_TIMEOUT);
 476        if (err < 0)
 477                return err;
 478        else
 479                return get_unaligned_be16(resp + 2);
 480}
 481EXPORT_SYMBOL_GPL(si476x_core_cmd_get_property);
 482
 483/**
 484 * si476x_cmd_dig_audio_pin_cfg() - send 'DIG_AUDIO_PIN_CFG' command to
 485 * the device
 486 * @core: device to send the command to
 487 * @dclk:  DCLK pin function configuration:
 488 *         #SI476X_DCLK_NOOP     - do not modify the behaviour
 489 *         #SI476X_DCLK_TRISTATE - put the pin in tristate condition,
 490 *                                 enable 1MOhm pulldown
 491 *         #SI476X_DCLK_DAUDIO   - set the pin to be a part of digital
 492 *                                 audio interface
 493 * @dfs:   DFS pin function configuration:
 494 *         #SI476X_DFS_NOOP      - do not modify the behaviour
 495 *         #SI476X_DFS_TRISTATE  - put the pin in tristate condition,
 496 *                             enable 1MOhm pulldown
 497 *      SI476X_DFS_DAUDIO    - set the pin to be a part of digital
 498 *                             audio interface
 499 * @dout - DOUT pin function configuration:
 500 *      SI476X_DOUT_NOOP       - do not modify the behaviour
 501 *      SI476X_DOUT_TRISTATE   - put the pin in tristate condition,
 502 *                               enable 1MOhm pulldown
 503 *      SI476X_DOUT_I2S_OUTPUT - set this pin to be digital out on I2S
 504 *                               port 1
 505 *      SI476X_DOUT_I2S_INPUT  - set this pin to be digital in on I2S
 506 *                               port 1
 507 * @xout - XOUT pin function configuration:
 508 *      SI476X_XOUT_NOOP        - do not modify the behaviour
 509 *      SI476X_XOUT_TRISTATE    - put the pin in tristate condition,
 510 *                                enable 1MOhm pulldown
 511 *      SI476X_XOUT_I2S_INPUT   - set this pin to be digital in on I2S
 512 *                                port 1
 513 *      SI476X_XOUT_MODE_SELECT - set this pin to be the input that
 514 *                                selects the mode of the I2S audio
 515 *                                combiner (analog or HD)
 516 *                                [SI4761/63/65/67 Only]
 517 *
 518 * Function returns 0 on success and negative error code on failure
 519 */
 520int si476x_core_cmd_dig_audio_pin_cfg(struct  si476x_core *core,
 521                                      enum si476x_dclk_config dclk,
 522                                      enum si476x_dfs_config  dfs,
 523                                      enum si476x_dout_config dout,
 524                                      enum si476x_xout_config xout)
 525{
 526        u8       resp[CMD_DIG_AUDIO_PIN_CFG_NRESP];
 527        const u8 args[CMD_DIG_AUDIO_PIN_CFG_NARGS] = {
 528                PIN_CFG_BYTE(dclk),
 529                PIN_CFG_BYTE(dfs),
 530                PIN_CFG_BYTE(dout),
 531                PIN_CFG_BYTE(xout),
 532        };
 533
 534        return si476x_core_send_command(core, CMD_DIG_AUDIO_PIN_CFG,
 535                                        args, ARRAY_SIZE(args),
 536                                        resp, ARRAY_SIZE(resp),
 537                                        SI476X_DEFAULT_TIMEOUT);
 538}
 539EXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg);
 540
 541/**
 542 * si476x_cmd_zif_pin_cfg - send 'ZIF_PIN_CFG_COMMAND'
 543 * @core - device to send the command to
 544 * @iqclk - IQCL pin function configuration:
 545 *       SI476X_IQCLK_NOOP     - do not modify the behaviour
 546 *       SI476X_IQCLK_TRISTATE - put the pin in tristate condition,
 547 *                               enable 1MOhm pulldown
 548 *       SI476X_IQCLK_IQ       - set pin to be a part of I/Q interace
 549 *                               in master mode
 550 * @iqfs - IQFS pin function configuration:
 551 *       SI476X_IQFS_NOOP     - do not modify the behaviour
 552 *       SI476X_IQFS_TRISTATE - put the pin in tristate condition,
 553 *                              enable 1MOhm pulldown
 554 *       SI476X_IQFS_IQ       - set pin to be a part of I/Q interace
 555 *                              in master mode
 556 * @iout - IOUT pin function configuration:
 557 *       SI476X_IOUT_NOOP     - do not modify the behaviour
 558 *       SI476X_IOUT_TRISTATE - put the pin in tristate condition,
 559 *                              enable 1MOhm pulldown
 560 *       SI476X_IOUT_OUTPUT   - set pin to be I out
 561 * @qout - QOUT pin function configuration:
 562 *       SI476X_QOUT_NOOP     - do not modify the behaviour
 563 *       SI476X_QOUT_TRISTATE - put the pin in tristate condition,
 564 *                              enable 1MOhm pulldown
 565 *       SI476X_QOUT_OUTPUT   - set pin to be Q out
 566 *
 567 * Function returns 0 on success and negative error code on failure
 568 */
 569int si476x_core_cmd_zif_pin_cfg(struct si476x_core *core,
 570                                enum si476x_iqclk_config iqclk,
 571                                enum si476x_iqfs_config iqfs,
 572                                enum si476x_iout_config iout,
 573                                enum si476x_qout_config qout)
 574{
 575        u8       resp[CMD_ZIF_PIN_CFG_NRESP];
 576        const u8 args[CMD_ZIF_PIN_CFG_NARGS] = {
 577                PIN_CFG_BYTE(iqclk),
 578                PIN_CFG_BYTE(iqfs),
 579                PIN_CFG_BYTE(iout),
 580                PIN_CFG_BYTE(qout),
 581        };
 582
 583        return si476x_core_send_command(core, CMD_ZIF_PIN_CFG,
 584                                        args, ARRAY_SIZE(args),
 585                                        resp, ARRAY_SIZE(resp),
 586                                        SI476X_DEFAULT_TIMEOUT);
 587}
 588EXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg);
 589
 590/**
 591 * si476x_cmd_ic_link_gpo_ctl_pin_cfg - send
 592 * 'IC_LINK_GPIO_CTL_PIN_CFG' comand to the device
 593 * @core - device to send the command to
 594 * @icin - ICIN pin function configuration:
 595 *      SI476X_ICIN_NOOP      - do not modify the behaviour
 596 *      SI476X_ICIN_TRISTATE  - put the pin in tristate condition,
 597 *                              enable 1MOhm pulldown
 598 *      SI476X_ICIN_GPO1_HIGH - set pin to be an output, drive it high
 599 *      SI476X_ICIN_GPO1_LOW  - set pin to be an output, drive it low
 600 *      SI476X_ICIN_IC_LINK   - set the pin to be a part of Inter-Chip link
 601 * @icip - ICIP pin function configuration:
 602 *      SI476X_ICIP_NOOP      - do not modify the behaviour
 603 *      SI476X_ICIP_TRISTATE  - put the pin in tristate condition,
 604 *                              enable 1MOhm pulldown
 605 *      SI476X_ICIP_GPO1_HIGH - set pin to be an output, drive it high
 606 *      SI476X_ICIP_GPO1_LOW  - set pin to be an output, drive it low
 607 *      SI476X_ICIP_IC_LINK   - set the pin to be a part of Inter-Chip link
 608 * @icon - ICON pin function configuration:
 609 *      SI476X_ICON_NOOP     - do not modify the behaviour
 610 *      SI476X_ICON_TRISTATE - put the pin in tristate condition,
 611 *                             enable 1MOhm pulldown
 612 *      SI476X_ICON_I2S      - set the pin to be a part of audio
 613 *                             interface in slave mode (DCLK)
 614 *      SI476X_ICON_IC_LINK  - set the pin to be a part of Inter-Chip link
 615 * @icop - ICOP pin function configuration:
 616 *      SI476X_ICOP_NOOP     - do not modify the behaviour
 617 *      SI476X_ICOP_TRISTATE - put the pin in tristate condition,
 618 *                             enable 1MOhm pulldown
 619 *      SI476X_ICOP_I2S      - set the pin to be a part of audio
 620 *                             interface in slave mode (DOUT)
 621 *                             [Si4761/63/65/67 Only]
 622 *      SI476X_ICOP_IC_LINK  - set the pin to be a part of Inter-Chip link
 623 *
 624 * Function returns 0 on success and negative error code on failure
 625 */
 626int si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(struct si476x_core *core,
 627                                            enum si476x_icin_config icin,
 628                                            enum si476x_icip_config icip,
 629                                            enum si476x_icon_config icon,
 630                                            enum si476x_icop_config icop)
 631{
 632        u8       resp[CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP];
 633        const u8 args[CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS] = {
 634                PIN_CFG_BYTE(icin),
 635                PIN_CFG_BYTE(icip),
 636                PIN_CFG_BYTE(icon),
 637                PIN_CFG_BYTE(icop),
 638        };
 639
 640        return si476x_core_send_command(core, CMD_IC_LINK_GPO_CTL_PIN_CFG,
 641                                        args, ARRAY_SIZE(args),
 642                                        resp, ARRAY_SIZE(resp),
 643                                        SI476X_DEFAULT_TIMEOUT);
 644}
 645EXPORT_SYMBOL_GPL(si476x_core_cmd_ic_link_gpo_ctl_pin_cfg);
 646
 647/**
 648 * si476x_cmd_ana_audio_pin_cfg - send 'ANA_AUDIO_PIN_CFG' to the
 649 * device
 650 * @core - device to send the command to
 651 * @lrout - LROUT pin function configuration:
 652 *       SI476X_LROUT_NOOP     - do not modify the behaviour
 653 *       SI476X_LROUT_TRISTATE - put the pin in tristate condition,
 654 *                               enable 1MOhm pulldown
 655 *       SI476X_LROUT_AUDIO    - set pin to be audio output
 656 *       SI476X_LROUT_MPX      - set pin to be MPX output
 657 *
 658 * Function returns 0 on success and negative error code on failure
 659 */
 660int si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *core,
 661                                      enum si476x_lrout_config lrout)
 662{
 663        u8       resp[CMD_ANA_AUDIO_PIN_CFG_NRESP];
 664        const u8 args[CMD_ANA_AUDIO_PIN_CFG_NARGS] = {
 665                PIN_CFG_BYTE(lrout),
 666        };
 667
 668        return si476x_core_send_command(core, CMD_ANA_AUDIO_PIN_CFG,
 669                                        args, ARRAY_SIZE(args),
 670                                        resp, ARRAY_SIZE(resp),
 671                                        SI476X_DEFAULT_TIMEOUT);
 672}
 673EXPORT_SYMBOL_GPL(si476x_core_cmd_ana_audio_pin_cfg);
 674
 675
 676/**
 677 * si476x_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 */
 694static int si476x_core_cmd_intb_pin_cfg_a10(struct si476x_core *core,
 695                                            enum si476x_intb_config intb,
 696                                            enum si476x_a1_config a1)
 697{
 698        u8       resp[CMD_INTB_PIN_CFG_A10_NRESP];
 699        const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
 700                PIN_CFG_BYTE(intb),
 701                PIN_CFG_BYTE(a1),
 702        };
 703
 704        return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
 705                                        args, ARRAY_SIZE(args),
 706                                        resp, ARRAY_SIZE(resp),
 707                                        SI476X_DEFAULT_TIMEOUT);
 708}
 709
 710static int si476x_core_cmd_intb_pin_cfg_a20(struct si476x_core *core,
 711                                            enum si476x_intb_config intb,
 712                                            enum si476x_a1_config a1)
 713{
 714        u8       resp[CMD_INTB_PIN_CFG_A20_NRESP];
 715        const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
 716                PIN_CFG_BYTE(intb),
 717                PIN_CFG_BYTE(a1),
 718        };
 719
 720        return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
 721                                        args, ARRAY_SIZE(args),
 722                                        resp, ARRAY_SIZE(resp),
 723                                        SI476X_DEFAULT_TIMEOUT);
 724}
 725
 726
 727
 728/**
 729 * si476x_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 */
 743int 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}
 790EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status);
 791
 792int 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}
 826EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status);
 827
 828int 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}
 859EXPORT_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 */
 876int 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}
 888EXPORT_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 */
 902int 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}
 966EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status);
 967
 968int 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}
 994EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount);
 995
 996int 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}
1009EXPORT_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 */
1021int 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}
1033EXPORT_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 */
1050int 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}
1062EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start);
1063
1064
1065
1066static 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
1089static 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
1111static 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
1122static 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
1135static 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
1152static 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
1168static 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
1221static 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
1276static 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
1338static 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
1356static 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
1372static 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
1400static 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
1424typedef int (*tune_freq_func_t) (struct si476x_core *core,
1425                                 struct si476x_tune_freq_args *tuneargs);
1426
1427static 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
1475int 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}
1482EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up);
1483
1484int 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}
1491EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down);
1492
1493int 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}
1500EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq);
1501
1502int 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}
1509EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq);
1510
1511int 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}
1521EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status);
1522
1523int 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}
1531EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status);
1532
1533int 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}
1542EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg);
1543
1544MODULE_LICENSE("GPL");
1545MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
1546MODULE_DESCRIPTION("API for command exchange for si476x");
1547