Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * drivers/mfd/si476x-cmd.c -- Subroutines implementing command
0004  * protocol of si476x series of chips
0005  *
0006  * Copyright (C) 2012 Innovative Converged Devices(ICD)
0007  * Copyright (C) 2013 Andrey Smirnov
0008  *
0009  * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
0010  */
0011 
0012 #include <linux/module.h>
0013 #include <linux/completion.h>
0014 #include <linux/delay.h>
0015 #include <linux/atomic.h>
0016 #include <linux/i2c.h>
0017 #include <linux/device.h>
0018 #include <linux/gpio.h>
0019 #include <linux/videodev2.h>
0020 
0021 #include <linux/mfd/si476x-core.h>
0022 
0023 #include <asm/unaligned.h>
0024 
0025 #define msb(x)                  ((u8)((u16) x >> 8))
0026 #define lsb(x)                  ((u8)((u16) x &  0x00FF))
0027 
0028 
0029 
0030 #define CMD_POWER_UP                0x01
0031 #define CMD_POWER_UP_A10_NRESP          1
0032 #define CMD_POWER_UP_A10_NARGS          5
0033 
0034 #define CMD_POWER_UP_A20_NRESP          1
0035 #define CMD_POWER_UP_A20_NARGS          5
0036 
0037 #define POWER_UP_DELAY_MS           110
0038 
0039 #define CMD_POWER_DOWN              0x11
0040 #define CMD_POWER_DOWN_A10_NRESP        1
0041 
0042 #define CMD_POWER_DOWN_A20_NRESP        1
0043 #define CMD_POWER_DOWN_A20_NARGS        1
0044 
0045 #define CMD_FUNC_INFO               0x12
0046 #define CMD_FUNC_INFO_NRESP         7
0047 
0048 #define CMD_SET_PROPERTY            0x13
0049 #define CMD_SET_PROPERTY_NARGS          5
0050 #define CMD_SET_PROPERTY_NRESP          1
0051 
0052 #define CMD_GET_PROPERTY            0x14
0053 #define CMD_GET_PROPERTY_NARGS          3
0054 #define CMD_GET_PROPERTY_NRESP          4
0055 
0056 #define CMD_AGC_STATUS              0x17
0057 #define CMD_AGC_STATUS_NRESP_A10        2
0058 #define CMD_AGC_STATUS_NRESP_A20                6
0059 
0060 #define PIN_CFG_BYTE(x) (0x7F & (x))
0061 #define CMD_DIG_AUDIO_PIN_CFG           0x18
0062 #define CMD_DIG_AUDIO_PIN_CFG_NARGS     4
0063 #define CMD_DIG_AUDIO_PIN_CFG_NRESP     5
0064 
0065 #define CMD_ZIF_PIN_CFG             0x19
0066 #define CMD_ZIF_PIN_CFG_NARGS           4
0067 #define CMD_ZIF_PIN_CFG_NRESP           5
0068 
0069 #define CMD_IC_LINK_GPO_CTL_PIN_CFG     0x1A
0070 #define CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS   4
0071 #define CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP   5
0072 
0073 #define CMD_ANA_AUDIO_PIN_CFG           0x1B
0074 #define CMD_ANA_AUDIO_PIN_CFG_NARGS     1
0075 #define CMD_ANA_AUDIO_PIN_CFG_NRESP     2
0076 
0077 #define CMD_INTB_PIN_CFG            0x1C
0078 #define CMD_INTB_PIN_CFG_NARGS          2
0079 #define CMD_INTB_PIN_CFG_A10_NRESP      6
0080 #define CMD_INTB_PIN_CFG_A20_NRESP      3
0081 
0082 #define CMD_FM_TUNE_FREQ            0x30
0083 #define CMD_FM_TUNE_FREQ_A10_NARGS      5
0084 #define CMD_FM_TUNE_FREQ_A20_NARGS      3
0085 #define CMD_FM_TUNE_FREQ_NRESP          1
0086 
0087 #define CMD_FM_RSQ_STATUS           0x32
0088 
0089 #define CMD_FM_RSQ_STATUS_A10_NARGS     1
0090 #define CMD_FM_RSQ_STATUS_A10_NRESP     17
0091 #define CMD_FM_RSQ_STATUS_A30_NARGS     1
0092 #define CMD_FM_RSQ_STATUS_A30_NRESP     23
0093 
0094 
0095 #define CMD_FM_SEEK_START           0x31
0096 #define CMD_FM_SEEK_START_NARGS         1
0097 #define CMD_FM_SEEK_START_NRESP         1
0098 
0099 #define CMD_FM_RDS_STATUS           0x36
0100 #define CMD_FM_RDS_STATUS_NARGS         1
0101 #define CMD_FM_RDS_STATUS_NRESP         16
0102 
0103 #define CMD_FM_RDS_BLOCKCOUNT           0x37
0104 #define CMD_FM_RDS_BLOCKCOUNT_NARGS     1
0105 #define CMD_FM_RDS_BLOCKCOUNT_NRESP     8
0106 
0107 #define CMD_FM_PHASE_DIVERSITY          0x38
0108 #define CMD_FM_PHASE_DIVERSITY_NARGS        1
0109 #define CMD_FM_PHASE_DIVERSITY_NRESP        1
0110 
0111 #define CMD_FM_PHASE_DIV_STATUS         0x39
0112 #define CMD_FM_PHASE_DIV_STATUS_NRESP       2
0113 
0114 #define CMD_AM_TUNE_FREQ            0x40
0115 #define CMD_AM_TUNE_FREQ_NARGS          3
0116 #define CMD_AM_TUNE_FREQ_NRESP          1
0117 
0118 #define CMD_AM_RSQ_STATUS           0x42
0119 #define CMD_AM_RSQ_STATUS_NARGS         1
0120 #define CMD_AM_RSQ_STATUS_NRESP         13
0121 
0122 #define CMD_AM_SEEK_START           0x41
0123 #define CMD_AM_SEEK_START_NARGS         1
0124 #define CMD_AM_SEEK_START_NRESP         1
0125 
0126 
0127 #define CMD_AM_ACF_STATUS           0x45
0128 #define CMD_AM_ACF_STATUS_NRESP         6
0129 #define CMD_AM_ACF_STATUS_NARGS         1
0130 
0131 #define CMD_FM_ACF_STATUS           0x35
0132 #define CMD_FM_ACF_STATUS_NRESP         8
0133 #define CMD_FM_ACF_STATUS_NARGS         1
0134 
0135 #define CMD_MAX_ARGS_COUNT          (10)
0136 
0137 
0138 enum si476x_acf_status_report_bits {
0139     SI476X_ACF_BLEND_INT    = (1 << 4),
0140     SI476X_ACF_HIBLEND_INT  = (1 << 3),
0141     SI476X_ACF_HICUT_INT    = (1 << 2),
0142     SI476X_ACF_CHBW_INT = (1 << 1),
0143     SI476X_ACF_SOFTMUTE_INT = (1 << 0),
0144 
0145     SI476X_ACF_SMUTE    = (1 << 0),
0146     SI476X_ACF_SMATTN   = 0x1f,
0147     SI476X_ACF_PILOT    = (1 << 7),
0148     SI476X_ACF_STBLEND  = ~SI476X_ACF_PILOT,
0149 };
0150 
0151 enum si476x_agc_status_report_bits {
0152     SI476X_AGC_MXHI     = (1 << 5),
0153     SI476X_AGC_MXLO     = (1 << 4),
0154     SI476X_AGC_LNAHI    = (1 << 3),
0155     SI476X_AGC_LNALO    = (1 << 2),
0156 };
0157 
0158 enum si476x_errors {
0159     SI476X_ERR_BAD_COMMAND      = 0x10,
0160     SI476X_ERR_BAD_ARG1     = 0x11,
0161     SI476X_ERR_BAD_ARG2     = 0x12,
0162     SI476X_ERR_BAD_ARG3     = 0x13,
0163     SI476X_ERR_BAD_ARG4     = 0x14,
0164     SI476X_ERR_BUSY         = 0x18,
0165     SI476X_ERR_BAD_INTERNAL_MEMORY  = 0x20,
0166     SI476X_ERR_BAD_PATCH        = 0x30,
0167     SI476X_ERR_BAD_BOOT_MODE    = 0x31,
0168     SI476X_ERR_BAD_PROPERTY     = 0x40,
0169 };
0170 
0171 static int si476x_core_parse_and_nag_about_error(struct si476x_core *core)
0172 {
0173     int err;
0174     char *cause;
0175     u8 buffer[2];
0176 
0177     if (core->revision != SI476X_REVISION_A10) {
0178         err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV,
0179                        buffer, sizeof(buffer));
0180         if (err == sizeof(buffer)) {
0181             switch (buffer[1]) {
0182             case SI476X_ERR_BAD_COMMAND:
0183                 cause = "Bad command";
0184                 err = -EINVAL;
0185                 break;
0186             case SI476X_ERR_BAD_ARG1:
0187                 cause = "Bad argument #1";
0188                 err = -EINVAL;
0189                 break;
0190             case SI476X_ERR_BAD_ARG2:
0191                 cause = "Bad argument #2";
0192                 err = -EINVAL;
0193                 break;
0194             case SI476X_ERR_BAD_ARG3:
0195                 cause = "Bad argument #3";
0196                 err = -EINVAL;
0197                 break;
0198             case SI476X_ERR_BAD_ARG4:
0199                 cause = "Bad argument #4";
0200                 err = -EINVAL;
0201                 break;
0202             case SI476X_ERR_BUSY:
0203                 cause = "Chip is busy";
0204                 err = -EBUSY;
0205                 break;
0206             case SI476X_ERR_BAD_INTERNAL_MEMORY:
0207                 cause = "Bad internal memory";
0208                 err = -EIO;
0209                 break;
0210             case SI476X_ERR_BAD_PATCH:
0211                 cause = "Bad patch";
0212                 err = -EINVAL;
0213                 break;
0214             case SI476X_ERR_BAD_BOOT_MODE:
0215                 cause = "Bad boot mode";
0216                 err = -EINVAL;
0217                 break;
0218             case SI476X_ERR_BAD_PROPERTY:
0219                 cause = "Bad property";
0220                 err = -EINVAL;
0221                 break;
0222             default:
0223                 cause = "Unknown";
0224                 err = -EIO;
0225             }
0226 
0227             dev_err(&core->client->dev,
0228                 "[Chip error status]: %s\n", cause);
0229         } else {
0230             dev_err(&core->client->dev,
0231                 "Failed to fetch error code\n");
0232             err = (err >= 0) ? -EIO : err;
0233         }
0234     } else {
0235         err = -EIO;
0236     }
0237 
0238     return err;
0239 }
0240 
0241 /**
0242  * si476x_core_send_command() - sends a command to si476x and waits its
0243  * response
0244  * @core:     si476x_device structure for the device we are
0245  *            communicating with
0246  * @command:  command id
0247  * @args:     command arguments we are sending
0248  * @argn:     actual size of @args
0249  * @resp:     buffer to place the expected response from the device
0250  * @respn:    actual size of @resp
0251  * @usecs:    amount of time to wait before reading the response (in
0252  *            usecs)
0253  *
0254  * Function returns 0 on succsess and negative error code on
0255  * failure
0256  */
0257 static int si476x_core_send_command(struct si476x_core *core,
0258                     const u8 command,
0259                     const u8 args[],
0260                     const int argn,
0261                     u8 resp[],
0262                     const int respn,
0263                     const int usecs)
0264 {
0265     struct i2c_client *client = core->client;
0266     int err;
0267     u8  data[CMD_MAX_ARGS_COUNT + 1];
0268 
0269     if (argn > CMD_MAX_ARGS_COUNT) {
0270         err = -ENOMEM;
0271         goto exit;
0272     }
0273 
0274     if (!client->adapter) {
0275         err = -ENODEV;
0276         goto exit;
0277     }
0278 
0279     /* First send the command and its arguments */
0280     data[0] = command;
0281     memcpy(&data[1], args, argn);
0282     dev_dbg(&client->dev, "Command:\n %*ph\n", argn + 1, data);
0283 
0284     err = si476x_core_i2c_xfer(core, SI476X_I2C_SEND,
0285                    (char *) data, argn + 1);
0286     if (err != argn + 1) {
0287         dev_err(&core->client->dev,
0288             "Error while sending command 0x%02x\n",
0289             command);
0290         err = (err >= 0) ? -EIO : err;
0291         goto exit;
0292     }
0293     /* Set CTS to zero only after the command is send to avoid
0294      * possible racing conditions when working in polling mode */
0295     atomic_set(&core->cts, 0);
0296 
0297     /* if (unlikely(command == CMD_POWER_DOWN) */
0298     if (!wait_event_timeout(core->command,
0299                 atomic_read(&core->cts),
0300                 usecs_to_jiffies(usecs) + 1))
0301         dev_warn(&core->client->dev,
0302              "(%s) [CMD 0x%02x] Answer timeout.\n",
0303              __func__, command);
0304 
0305     /*
0306       When working in polling mode, for some reason the tuner will
0307       report CTS bit as being set in the first status byte read,
0308       but all the consequtive ones will return zeros until the
0309       tuner is actually completed the POWER_UP command. To
0310       workaround that we wait for second CTS to be reported
0311      */
0312     if (unlikely(!core->client->irq && command == CMD_POWER_UP)) {
0313         if (!wait_event_timeout(core->command,
0314                     atomic_read(&core->cts),
0315                     usecs_to_jiffies(usecs) + 1))
0316             dev_warn(&core->client->dev,
0317                  "(%s) Power up took too much time.\n",
0318                  __func__);
0319     }
0320 
0321     /* Then get the response */
0322     err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, resp, respn);
0323     if (err != respn) {
0324         dev_err(&core->client->dev,
0325             "Error while reading response for command 0x%02x\n",
0326             command);
0327         err = (err >= 0) ? -EIO : err;
0328         goto exit;
0329     }
0330     dev_dbg(&client->dev, "Response:\n %*ph\n", respn, resp);
0331 
0332     err = 0;
0333 
0334     if (resp[0] & SI476X_ERR) {
0335         dev_err(&core->client->dev,
0336             "[CMD 0x%02x] Chip set error flag\n", command);
0337         err = si476x_core_parse_and_nag_about_error(core);
0338         goto exit;
0339     }
0340 
0341     if (!(resp[0] & SI476X_CTS))
0342         err = -EBUSY;
0343 exit:
0344     return err;
0345 }
0346 
0347 static int si476x_cmd_clear_stc(struct si476x_core *core)
0348 {
0349     int err;
0350     struct si476x_rsq_status_args args = {
0351         .primary    = false,
0352         .rsqack     = false,
0353         .attune     = false,
0354         .cancel     = false,
0355         .stcack     = true,
0356     };
0357 
0358     switch (core->power_up_parameters.func) {
0359     case SI476X_FUNC_FM_RECEIVER:
0360         err = si476x_core_cmd_fm_rsq_status(core, &args, NULL);
0361         break;
0362     case SI476X_FUNC_AM_RECEIVER:
0363         err = si476x_core_cmd_am_rsq_status(core, &args, NULL);
0364         break;
0365     default:
0366         err = -EINVAL;
0367     }
0368 
0369     return err;
0370 }
0371 
0372 static int si476x_cmd_tune_seek_freq(struct si476x_core *core,
0373                      uint8_t cmd,
0374                      const uint8_t args[], size_t argn,
0375                      uint8_t *resp, size_t respn)
0376 {
0377     int err;
0378 
0379 
0380     atomic_set(&core->stc, 0);
0381     err = si476x_core_send_command(core, cmd, args, argn, resp, respn,
0382                        SI476X_TIMEOUT_TUNE);
0383     if (!err) {
0384         wait_event_killable(core->tuning,
0385                     atomic_read(&core->stc));
0386         si476x_cmd_clear_stc(core);
0387     }
0388 
0389     return err;
0390 }
0391 
0392 /**
0393  * si476x_core_cmd_func_info() - send 'FUNC_INFO' command to the device
0394  * @core: device to send the command to
0395  * @info:  struct si476x_func_info to fill all the information
0396  *         returned by the command
0397  *
0398  * The command requests the firmware and patch version for currently
0399  * loaded firmware (dependent on the function of the device FM/AM/WB)
0400  *
0401  * Function returns 0 on succsess and negative error code on
0402  * failure
0403  */
0404 int si476x_core_cmd_func_info(struct si476x_core *core,
0405                   struct si476x_func_info *info)
0406 {
0407     int err;
0408     u8  resp[CMD_FUNC_INFO_NRESP];
0409 
0410     err = si476x_core_send_command(core, CMD_FUNC_INFO,
0411                        NULL, 0,
0412                        resp, ARRAY_SIZE(resp),
0413                        SI476X_DEFAULT_TIMEOUT);
0414 
0415     info->firmware.major    = resp[1];
0416     info->firmware.minor[0] = resp[2];
0417     info->firmware.minor[1] = resp[3];
0418 
0419     info->patch_id = ((u16) resp[4] << 8) | resp[5];
0420     info->func     = resp[6];
0421 
0422     return err;
0423 }
0424 EXPORT_SYMBOL_GPL(si476x_core_cmd_func_info);
0425 
0426 /**
0427  * si476x_core_cmd_set_property() - send 'SET_PROPERTY' command to the device
0428  * @core:    device to send the command to
0429  * @property: property address
0430  * @value:    property value
0431  *
0432  * Function returns 0 on succsess and negative error code on
0433  * failure
0434  */
0435 int si476x_core_cmd_set_property(struct si476x_core *core,
0436                  u16 property, u16 value)
0437 {
0438     u8       resp[CMD_SET_PROPERTY_NRESP];
0439     const u8 args[CMD_SET_PROPERTY_NARGS] = {
0440         0x00,
0441         msb(property),
0442         lsb(property),
0443         msb(value),
0444         lsb(value),
0445     };
0446 
0447     return si476x_core_send_command(core, CMD_SET_PROPERTY,
0448                     args, ARRAY_SIZE(args),
0449                     resp, ARRAY_SIZE(resp),
0450                     SI476X_DEFAULT_TIMEOUT);
0451 }
0452 EXPORT_SYMBOL_GPL(si476x_core_cmd_set_property);
0453 
0454 /**
0455  * si476x_core_cmd_get_property() - send 'GET_PROPERTY' command to the device
0456  * @core:    device to send the command to
0457  * @property: property address
0458  *
0459  * Function return the value of property as u16 on success or a
0460  * negative error on failure
0461  */
0462 int si476x_core_cmd_get_property(struct si476x_core *core, u16 property)
0463 {
0464     int err;
0465     u8       resp[CMD_GET_PROPERTY_NRESP];
0466     const u8 args[CMD_GET_PROPERTY_NARGS] = {
0467         0x00,
0468         msb(property),
0469         lsb(property),
0470     };
0471 
0472     err = si476x_core_send_command(core, CMD_GET_PROPERTY,
0473                        args, ARRAY_SIZE(args),
0474                        resp, ARRAY_SIZE(resp),
0475                        SI476X_DEFAULT_TIMEOUT);
0476     if (err < 0)
0477         return err;
0478     else
0479         return get_unaligned_be16(resp + 2);
0480 }
0481 EXPORT_SYMBOL_GPL(si476x_core_cmd_get_property);
0482 
0483 /**
0484  * si476x_core_cmd_dig_audio_pin_cfg() - send 'DIG_AUDIO_PIN_CFG' command to
0485  * the device
0486  * @core: device to send the command to
0487  * @dclk:  DCLK pin function configuration:
0488  *     #SI476X_DCLK_NOOP     - do not modify the behaviour
0489  *         #SI476X_DCLK_TRISTATE - put the pin in tristate condition,
0490  *                                 enable 1MOhm pulldown
0491  *         #SI476X_DCLK_DAUDIO   - set the pin to be a part of digital
0492  *                                 audio interface
0493  * @dfs:   DFS pin function configuration:
0494  *         #SI476X_DFS_NOOP      - do not modify the behaviour
0495  *         #SI476X_DFS_TRISTATE  - put the pin in tristate condition,
0496  *                             enable 1MOhm pulldown
0497  *      SI476X_DFS_DAUDIO    - set the pin to be a part of digital
0498  *                             audio interface
0499  * @dout: - DOUT pin function configuration:
0500  *      SI476X_DOUT_NOOP       - do not modify the behaviour
0501  *      SI476X_DOUT_TRISTATE   - put the pin in tristate condition,
0502  *                               enable 1MOhm pulldown
0503  *      SI476X_DOUT_I2S_OUTPUT - set this pin to be digital out on I2S
0504  *                               port 1
0505  *      SI476X_DOUT_I2S_INPUT  - set this pin to be digital in on I2S
0506  *                               port 1
0507  * @xout: - XOUT pin function configuration:
0508  *  SI476X_XOUT_NOOP        - do not modify the behaviour
0509  *      SI476X_XOUT_TRISTATE    - put the pin in tristate condition,
0510  *                                enable 1MOhm pulldown
0511  *      SI476X_XOUT_I2S_INPUT   - set this pin to be digital in on I2S
0512  *                                port 1
0513  *      SI476X_XOUT_MODE_SELECT - set this pin to be the input that
0514  *                                selects the mode of the I2S audio
0515  *                                combiner (analog or HD)
0516  *                                [SI4761/63/65/67 Only]
0517  *
0518  * Function returns 0 on success and negative error code on failure
0519  */
0520 int si476x_core_cmd_dig_audio_pin_cfg(struct  si476x_core *core,
0521                       enum si476x_dclk_config dclk,
0522                       enum si476x_dfs_config  dfs,
0523                       enum si476x_dout_config dout,
0524                       enum si476x_xout_config xout)
0525 {
0526     u8       resp[CMD_DIG_AUDIO_PIN_CFG_NRESP];
0527     const u8 args[CMD_DIG_AUDIO_PIN_CFG_NARGS] = {
0528         PIN_CFG_BYTE(dclk),
0529         PIN_CFG_BYTE(dfs),
0530         PIN_CFG_BYTE(dout),
0531         PIN_CFG_BYTE(xout),
0532     };
0533 
0534     return si476x_core_send_command(core, CMD_DIG_AUDIO_PIN_CFG,
0535                     args, ARRAY_SIZE(args),
0536                     resp, ARRAY_SIZE(resp),
0537                     SI476X_DEFAULT_TIMEOUT);
0538 }
0539 EXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg);
0540 
0541 /**
0542  * si476x_core_cmd_zif_pin_cfg - send 'ZIF_PIN_CFG_COMMAND'
0543  * @core: - device to send the command to
0544  * @iqclk: - IQCL pin function configuration:
0545  *       SI476X_IQCLK_NOOP     - do not modify the behaviour
0546  *       SI476X_IQCLK_TRISTATE - put the pin in tristate condition,
0547  *                               enable 1MOhm pulldown
0548  *       SI476X_IQCLK_IQ       - set pin to be a part of I/Q interace
0549  *                               in master mode
0550  * @iqfs: - IQFS pin function configuration:
0551  *       SI476X_IQFS_NOOP     - do not modify the behaviour
0552  *       SI476X_IQFS_TRISTATE - put the pin in tristate condition,
0553  *                              enable 1MOhm pulldown
0554  *       SI476X_IQFS_IQ       - set pin to be a part of I/Q interace
0555  *                              in master mode
0556  * @iout: - IOUT pin function configuration:
0557  *       SI476X_IOUT_NOOP     - do not modify the behaviour
0558  *       SI476X_IOUT_TRISTATE - put the pin in tristate condition,
0559  *                              enable 1MOhm pulldown
0560  *       SI476X_IOUT_OUTPUT   - set pin to be I out
0561  * @qout: - QOUT pin function configuration:
0562  *       SI476X_QOUT_NOOP     - do not modify the behaviour
0563  *       SI476X_QOUT_TRISTATE - put the pin in tristate condition,
0564  *                              enable 1MOhm pulldown
0565  *       SI476X_QOUT_OUTPUT   - set pin to be Q out
0566  *
0567  * Function returns 0 on success and negative error code on failure
0568  */
0569 int si476x_core_cmd_zif_pin_cfg(struct si476x_core *core,
0570                 enum si476x_iqclk_config iqclk,
0571                 enum si476x_iqfs_config iqfs,
0572                 enum si476x_iout_config iout,
0573                 enum si476x_qout_config qout)
0574 {
0575     u8       resp[CMD_ZIF_PIN_CFG_NRESP];
0576     const u8 args[CMD_ZIF_PIN_CFG_NARGS] = {
0577         PIN_CFG_BYTE(iqclk),
0578         PIN_CFG_BYTE(iqfs),
0579         PIN_CFG_BYTE(iout),
0580         PIN_CFG_BYTE(qout),
0581     };
0582 
0583     return si476x_core_send_command(core, CMD_ZIF_PIN_CFG,
0584                     args, ARRAY_SIZE(args),
0585                     resp, ARRAY_SIZE(resp),
0586                     SI476X_DEFAULT_TIMEOUT);
0587 }
0588 EXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg);
0589 
0590 /**
0591  * si476x_core_cmd_ic_link_gpo_ctl_pin_cfg - send
0592  * 'IC_LINK_GPIO_CTL_PIN_CFG' comand to the device
0593  * @core: - device to send the command to
0594  * @icin: - ICIN pin function configuration:
0595  *      SI476X_ICIN_NOOP      - do not modify the behaviour
0596  *      SI476X_ICIN_TRISTATE  - put the pin in tristate condition,
0597  *                              enable 1MOhm pulldown
0598  *      SI476X_ICIN_GPO1_HIGH - set pin to be an output, drive it high
0599  *      SI476X_ICIN_GPO1_LOW  - set pin to be an output, drive it low
0600  *      SI476X_ICIN_IC_LINK   - set the pin to be a part of Inter-Chip link
0601  * @icip: - ICIP pin function configuration:
0602  *      SI476X_ICIP_NOOP      - do not modify the behaviour
0603  *      SI476X_ICIP_TRISTATE  - put the pin in tristate condition,
0604  *                              enable 1MOhm pulldown
0605  *      SI476X_ICIP_GPO1_HIGH - set pin to be an output, drive it high
0606  *      SI476X_ICIP_GPO1_LOW  - set pin to be an output, drive it low
0607  *      SI476X_ICIP_IC_LINK   - set the pin to be a part of Inter-Chip link
0608  * @icon: - ICON pin function configuration:
0609  *      SI476X_ICON_NOOP     - do not modify the behaviour
0610  *      SI476X_ICON_TRISTATE - put the pin in tristate condition,
0611  *                             enable 1MOhm pulldown
0612  *      SI476X_ICON_I2S      - set the pin to be a part of audio
0613  *                             interface in slave mode (DCLK)
0614  *      SI476X_ICON_IC_LINK  - set the pin to be a part of Inter-Chip link
0615  * @icop: - ICOP pin function configuration:
0616  *      SI476X_ICOP_NOOP     - do not modify the behaviour
0617  *      SI476X_ICOP_TRISTATE - put the pin in tristate condition,
0618  *                             enable 1MOhm pulldown
0619  *      SI476X_ICOP_I2S      - set the pin to be a part of audio
0620  *                             interface in slave mode (DOUT)
0621  *                             [Si4761/63/65/67 Only]
0622  *      SI476X_ICOP_IC_LINK  - set the pin to be a part of Inter-Chip link
0623  *
0624  * Function returns 0 on success and negative error code on failure
0625  */
0626 int si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(struct si476x_core *core,
0627                         enum si476x_icin_config icin,
0628                         enum si476x_icip_config icip,
0629                         enum si476x_icon_config icon,
0630                         enum si476x_icop_config icop)
0631 {
0632     u8       resp[CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP];
0633     const u8 args[CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS] = {
0634         PIN_CFG_BYTE(icin),
0635         PIN_CFG_BYTE(icip),
0636         PIN_CFG_BYTE(icon),
0637         PIN_CFG_BYTE(icop),
0638     };
0639 
0640     return si476x_core_send_command(core, CMD_IC_LINK_GPO_CTL_PIN_CFG,
0641                     args, ARRAY_SIZE(args),
0642                     resp, ARRAY_SIZE(resp),
0643                     SI476X_DEFAULT_TIMEOUT);
0644 }
0645 EXPORT_SYMBOL_GPL(si476x_core_cmd_ic_link_gpo_ctl_pin_cfg);
0646 
0647 /**
0648  * si476x_core_cmd_ana_audio_pin_cfg - send 'ANA_AUDIO_PIN_CFG' to the
0649  * device
0650  * @core: - device to send the command to
0651  * @lrout: - LROUT pin function configuration:
0652  *       SI476X_LROUT_NOOP     - do not modify the behaviour
0653  *       SI476X_LROUT_TRISTATE - put the pin in tristate condition,
0654  *                               enable 1MOhm pulldown
0655  *       SI476X_LROUT_AUDIO    - set pin to be audio output
0656  *       SI476X_LROUT_MPX      - set pin to be MPX output
0657  *
0658  * Function returns 0 on success and negative error code on failure
0659  */
0660 int si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *core,
0661                       enum si476x_lrout_config lrout)
0662 {
0663     u8       resp[CMD_ANA_AUDIO_PIN_CFG_NRESP];
0664     const u8 args[CMD_ANA_AUDIO_PIN_CFG_NARGS] = {
0665         PIN_CFG_BYTE(lrout),
0666     };
0667 
0668     return si476x_core_send_command(core, CMD_ANA_AUDIO_PIN_CFG,
0669                     args, ARRAY_SIZE(args),
0670                     resp, ARRAY_SIZE(resp),
0671                     SI476X_DEFAULT_TIMEOUT);
0672 }
0673 EXPORT_SYMBOL_GPL(si476x_core_cmd_ana_audio_pin_cfg);
0674 
0675 
0676 /**
0677  * si476x_core_cmd_intb_pin_cfg_a10 - send 'INTB_PIN_CFG' command to the device
0678  * @core: - device to send the command to
0679  * @intb: - INTB pin function configuration:
0680  *      SI476X_INTB_NOOP     - do not modify the behaviour
0681  *      SI476X_INTB_TRISTATE - put the pin in tristate condition,
0682  *                             enable 1MOhm pulldown
0683  *      SI476X_INTB_DAUDIO   - set pin to be a part of digital
0684  *                             audio interface in slave mode
0685  *      SI476X_INTB_IRQ      - set pin to be an interrupt request line
0686  * @a1: - A1 pin function configuration:
0687  *      SI476X_A1_NOOP     - do not modify the behaviour
0688  *      SI476X_A1_TRISTATE - put the pin in tristate condition,
0689  *                           enable 1MOhm pulldown
0690  *      SI476X_A1_IRQ      - set pin to be an interrupt request line
0691  *
0692  * Function returns 0 on success and negative error code on failure
0693  */
0694 static int si476x_core_cmd_intb_pin_cfg_a10(struct si476x_core *core,
0695                         enum si476x_intb_config intb,
0696                         enum si476x_a1_config a1)
0697 {
0698     u8       resp[CMD_INTB_PIN_CFG_A10_NRESP];
0699     const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
0700         PIN_CFG_BYTE(intb),
0701         PIN_CFG_BYTE(a1),
0702     };
0703 
0704     return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
0705                     args, ARRAY_SIZE(args),
0706                     resp, ARRAY_SIZE(resp),
0707                     SI476X_DEFAULT_TIMEOUT);
0708 }
0709 
0710 static int si476x_core_cmd_intb_pin_cfg_a20(struct si476x_core *core,
0711                         enum si476x_intb_config intb,
0712                         enum si476x_a1_config a1)
0713 {
0714     u8       resp[CMD_INTB_PIN_CFG_A20_NRESP];
0715     const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
0716         PIN_CFG_BYTE(intb),
0717         PIN_CFG_BYTE(a1),
0718     };
0719 
0720     return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
0721                     args, ARRAY_SIZE(args),
0722                     resp, ARRAY_SIZE(resp),
0723                     SI476X_DEFAULT_TIMEOUT);
0724 }
0725 
0726 
0727 
0728 /**
0729  * si476x_core_cmd_am_rsq_status - send 'AM_RSQ_STATUS' command to the
0730  * device
0731  * @core:  - device to send the command to
0732  * @rsqargs: - pointer to a structure containing a group of sub-args
0733  *             relevant to sending the RSQ status command
0734  * @report: - all signal quality information returned by the command
0735  *           (if NULL then the output of the command is ignored)
0736  *
0737  * Function returns 0 on success and negative error code on failure
0738  */
0739 int si476x_core_cmd_am_rsq_status(struct si476x_core *core,
0740                   struct si476x_rsq_status_args *rsqargs,
0741                   struct si476x_rsq_status_report *report)
0742 {
0743     int err;
0744     u8       resp[CMD_AM_RSQ_STATUS_NRESP];
0745     const u8 args[CMD_AM_RSQ_STATUS_NARGS] = {
0746         rsqargs->rsqack << 3 | rsqargs->attune << 2 |
0747         rsqargs->cancel << 1 | rsqargs->stcack,
0748     };
0749 
0750     err = si476x_core_send_command(core, CMD_AM_RSQ_STATUS,
0751                        args, ARRAY_SIZE(args),
0752                        resp, ARRAY_SIZE(resp),
0753                        SI476X_DEFAULT_TIMEOUT);
0754     /*
0755      * Besides getting received signal quality information this
0756      * command can be used to just acknowledge different interrupt
0757      * flags in those cases it is useless to copy and parse
0758      * received data so user can pass NULL, and thus avoid
0759      * unnecessary copying.
0760      */
0761     if (!report)
0762         return err;
0763 
0764     report->snrhint     = 0x08 & resp[1];
0765     report->snrlint     = 0x04 & resp[1];
0766     report->rssihint    = 0x02 & resp[1];
0767     report->rssilint    = 0x01 & resp[1];
0768 
0769     report->bltf        = 0x80 & resp[2];
0770     report->snr_ready   = 0x20 & resp[2];
0771     report->rssiready   = 0x08 & resp[2];
0772     report->afcrl       = 0x02 & resp[2];
0773     report->valid       = 0x01 & resp[2];
0774 
0775     report->readfreq    = get_unaligned_be16(resp + 3);
0776     report->freqoff     = resp[5];
0777     report->rssi        = resp[6];
0778     report->snr     = resp[7];
0779     report->lassi       = resp[9];
0780     report->hassi       = resp[10];
0781     report->mult        = resp[11];
0782     report->dev     = resp[12];
0783 
0784     return err;
0785 }
0786 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status);
0787 
0788 int si476x_core_cmd_fm_acf_status(struct si476x_core *core,
0789                  struct si476x_acf_status_report *report)
0790 {
0791     int err;
0792     u8       resp[CMD_FM_ACF_STATUS_NRESP];
0793     const u8 args[CMD_FM_ACF_STATUS_NARGS] = {
0794         0x0,
0795     };
0796 
0797     if (!report)
0798         return -EINVAL;
0799 
0800     err = si476x_core_send_command(core, CMD_FM_ACF_STATUS,
0801                        args, ARRAY_SIZE(args),
0802                        resp, ARRAY_SIZE(resp),
0803                        SI476X_DEFAULT_TIMEOUT);
0804     if (err < 0)
0805         return err;
0806 
0807     report->blend_int   = resp[1] & SI476X_ACF_BLEND_INT;
0808     report->hblend_int  = resp[1] & SI476X_ACF_HIBLEND_INT;
0809     report->hicut_int   = resp[1] & SI476X_ACF_HICUT_INT;
0810     report->chbw_int    = resp[1] & SI476X_ACF_CHBW_INT;
0811     report->softmute_int    = resp[1] & SI476X_ACF_SOFTMUTE_INT;
0812     report->smute       = resp[2] & SI476X_ACF_SMUTE;
0813     report->smattn      = resp[3] & SI476X_ACF_SMATTN;
0814     report->chbw        = resp[4];
0815     report->hicut       = resp[5];
0816     report->hiblend     = resp[6];
0817     report->pilot       = resp[7] & SI476X_ACF_PILOT;
0818     report->stblend     = resp[7] & SI476X_ACF_STBLEND;
0819 
0820     return err;
0821 }
0822 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status);
0823 
0824 int si476x_core_cmd_am_acf_status(struct si476x_core *core,
0825                   struct si476x_acf_status_report *report)
0826 {
0827     int err;
0828     u8       resp[CMD_AM_ACF_STATUS_NRESP];
0829     const u8 args[CMD_AM_ACF_STATUS_NARGS] = {
0830         0x0,
0831     };
0832 
0833     if (!report)
0834         return -EINVAL;
0835 
0836     err = si476x_core_send_command(core, CMD_AM_ACF_STATUS,
0837                        args, ARRAY_SIZE(args),
0838                        resp, ARRAY_SIZE(resp),
0839                        SI476X_DEFAULT_TIMEOUT);
0840     if (err < 0)
0841         return err;
0842 
0843     report->blend_int   = resp[1] & SI476X_ACF_BLEND_INT;
0844     report->hblend_int  = resp[1] & SI476X_ACF_HIBLEND_INT;
0845     report->hicut_int   = resp[1] & SI476X_ACF_HICUT_INT;
0846     report->chbw_int    = resp[1] & SI476X_ACF_CHBW_INT;
0847     report->softmute_int    = resp[1] & SI476X_ACF_SOFTMUTE_INT;
0848     report->smute       = resp[2] & SI476X_ACF_SMUTE;
0849     report->smattn      = resp[3] & SI476X_ACF_SMATTN;
0850     report->chbw        = resp[4];
0851     report->hicut       = resp[5];
0852 
0853     return err;
0854 }
0855 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status);
0856 
0857 
0858 /**
0859  * si476x_core_cmd_fm_seek_start - send 'FM_SEEK_START' command to the
0860  * device
0861  * @core:  - device to send the command to
0862  * @seekup: - if set the direction of the search is 'up'
0863  * @wrap:   - if set seek wraps when hitting band limit
0864  *
0865  * This function begins search for a valid station. The station is
0866  * considered valid when 'FM_VALID_SNR_THRESHOLD' and
0867  * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
0868  * are met.
0869 } *
0870  * Function returns 0 on success and negative error code on failure
0871  */
0872 int si476x_core_cmd_fm_seek_start(struct si476x_core *core,
0873                   bool seekup, bool wrap)
0874 {
0875     u8       resp[CMD_FM_SEEK_START_NRESP];
0876     const u8 args[CMD_FM_SEEK_START_NARGS] = {
0877         seekup << 3 | wrap << 2,
0878     };
0879 
0880     return si476x_cmd_tune_seek_freq(core, CMD_FM_SEEK_START,
0881                      args, sizeof(args),
0882                      resp, sizeof(resp));
0883 }
0884 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start);
0885 
0886 /**
0887  * si476x_core_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the
0888  * device
0889  * @core: - device to send the command to
0890  * @status_only: - if set the data is not removed from RDSFIFO,
0891  *                RDSFIFOUSED is not decremented and data in all the
0892  *                rest RDS data contains the last valid info received
0893  * @mtfifo: if set the command clears RDS receive FIFO
0894  * @intack: if set the command clards the RDSINT bit.
0895  * @report: - all signal quality information returned by the command
0896  *           (if NULL then the output of the command is ignored)
0897  *
0898  * Function returns 0 on success and negative error code on failure
0899  */
0900 int si476x_core_cmd_fm_rds_status(struct si476x_core *core,
0901                   bool status_only,
0902                   bool mtfifo,
0903                   bool intack,
0904                   struct si476x_rds_status_report *report)
0905 {
0906     int err;
0907     u8       resp[CMD_FM_RDS_STATUS_NRESP];
0908     const u8 args[CMD_FM_RDS_STATUS_NARGS] = {
0909         status_only << 2 | mtfifo << 1 | intack,
0910     };
0911 
0912     err = si476x_core_send_command(core, CMD_FM_RDS_STATUS,
0913                        args, ARRAY_SIZE(args),
0914                        resp, ARRAY_SIZE(resp),
0915                        SI476X_DEFAULT_TIMEOUT);
0916     /*
0917      * Besides getting RDS status information this command can be
0918      * used to just acknowledge different interrupt flags in those
0919      * cases it is useless to copy and parse received data so user
0920      * can pass NULL, and thus avoid unnecessary copying.
0921      */
0922     if (err < 0 || report == NULL)
0923         return err;
0924 
0925     report->rdstpptyint = 0x10 & resp[1];
0926     report->rdspiint    = 0x08 & resp[1];
0927     report->rdssyncint  = 0x02 & resp[1];
0928     report->rdsfifoint  = 0x01 & resp[1];
0929 
0930     report->tpptyvalid  = 0x10 & resp[2];
0931     report->pivalid     = 0x08 & resp[2];
0932     report->rdssync     = 0x02 & resp[2];
0933     report->rdsfifolost = 0x01 & resp[2];
0934 
0935     report->tp      = 0x20 & resp[3];
0936     report->pty     = 0x1f & resp[3];
0937 
0938     report->pi      = get_unaligned_be16(resp + 4);
0939     report->rdsfifoused = resp[6];
0940 
0941     report->ble[V4L2_RDS_BLOCK_A]   = 0xc0 & resp[7];
0942     report->ble[V4L2_RDS_BLOCK_B]   = 0x30 & resp[7];
0943     report->ble[V4L2_RDS_BLOCK_C]   = 0x0c & resp[7];
0944     report->ble[V4L2_RDS_BLOCK_D]   = 0x03 & resp[7];
0945 
0946     report->rds[V4L2_RDS_BLOCK_A].block = V4L2_RDS_BLOCK_A;
0947     report->rds[V4L2_RDS_BLOCK_A].msb = resp[8];
0948     report->rds[V4L2_RDS_BLOCK_A].lsb = resp[9];
0949 
0950     report->rds[V4L2_RDS_BLOCK_B].block = V4L2_RDS_BLOCK_B;
0951     report->rds[V4L2_RDS_BLOCK_B].msb = resp[10];
0952     report->rds[V4L2_RDS_BLOCK_B].lsb = resp[11];
0953 
0954     report->rds[V4L2_RDS_BLOCK_C].block = V4L2_RDS_BLOCK_C;
0955     report->rds[V4L2_RDS_BLOCK_C].msb = resp[12];
0956     report->rds[V4L2_RDS_BLOCK_C].lsb = resp[13];
0957 
0958     report->rds[V4L2_RDS_BLOCK_D].block = V4L2_RDS_BLOCK_D;
0959     report->rds[V4L2_RDS_BLOCK_D].msb = resp[14];
0960     report->rds[V4L2_RDS_BLOCK_D].lsb = resp[15];
0961 
0962     return err;
0963 }
0964 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status);
0965 
0966 int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *core,
0967                 bool clear,
0968                 struct si476x_rds_blockcount_report *report)
0969 {
0970     int err;
0971     u8       resp[CMD_FM_RDS_BLOCKCOUNT_NRESP];
0972     const u8 args[CMD_FM_RDS_BLOCKCOUNT_NARGS] = {
0973         clear,
0974     };
0975 
0976     if (!report)
0977         return -EINVAL;
0978 
0979     err = si476x_core_send_command(core, CMD_FM_RDS_BLOCKCOUNT,
0980                        args, ARRAY_SIZE(args),
0981                        resp, ARRAY_SIZE(resp),
0982                        SI476X_DEFAULT_TIMEOUT);
0983 
0984     if (!err) {
0985         report->expected    = get_unaligned_be16(resp + 2);
0986         report->received    = get_unaligned_be16(resp + 4);
0987         report->uncorrectable   = get_unaligned_be16(resp + 6);
0988     }
0989 
0990     return err;
0991 }
0992 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount);
0993 
0994 int si476x_core_cmd_fm_phase_diversity(struct si476x_core *core,
0995                        enum si476x_phase_diversity_mode mode)
0996 {
0997     u8       resp[CMD_FM_PHASE_DIVERSITY_NRESP];
0998     const u8 args[CMD_FM_PHASE_DIVERSITY_NARGS] = {
0999         mode & 0x07,
1000     };
1001 
1002     return si476x_core_send_command(core, CMD_FM_PHASE_DIVERSITY,
1003                     args, ARRAY_SIZE(args),
1004                     resp, ARRAY_SIZE(resp),
1005                     SI476X_DEFAULT_TIMEOUT);
1006 }
1007 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity);
1008 /**
1009  * si476x_core_cmd_fm_phase_div_status() - get the phase diversity
1010  * status
1011  *
1012  * @core: si476x device
1013  *
1014  * NOTE caller must hold core lock
1015  *
1016  * Function returns the value of the status bit in case of success and
1017  * negative error code in case of failre.
1018  */
1019 int si476x_core_cmd_fm_phase_div_status(struct si476x_core *core)
1020 {
1021     int err;
1022     u8 resp[CMD_FM_PHASE_DIV_STATUS_NRESP];
1023 
1024     err = si476x_core_send_command(core, CMD_FM_PHASE_DIV_STATUS,
1025                        NULL, 0,
1026                        resp, ARRAY_SIZE(resp),
1027                        SI476X_DEFAULT_TIMEOUT);
1028 
1029     return (err < 0) ? err : resp[1];
1030 }
1031 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status);
1032 
1033 
1034 /**
1035  * si476x_core_cmd_am_seek_start - send 'FM_SEEK_START' command to the
1036  * device
1037  * @core:  - device to send the command to
1038  * @seekup: - if set the direction of the search is 'up'
1039  * @wrap:   - if set seek wraps when hitting band limit
1040  *
1041  * This function begins search for a valid station. The station is
1042  * considered valid when 'FM_VALID_SNR_THRESHOLD' and
1043  * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
1044  * are met.
1045  *
1046  * Function returns 0 on success and negative error code on failure
1047  */
1048 int si476x_core_cmd_am_seek_start(struct si476x_core *core,
1049                   bool seekup, bool wrap)
1050 {
1051     u8       resp[CMD_AM_SEEK_START_NRESP];
1052     const u8 args[CMD_AM_SEEK_START_NARGS] = {
1053         seekup << 3 | wrap << 2,
1054     };
1055 
1056     return si476x_cmd_tune_seek_freq(core,  CMD_AM_SEEK_START,
1057                      args, sizeof(args),
1058                      resp, sizeof(resp));
1059 }
1060 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start);
1061 
1062 
1063 
1064 static int si476x_core_cmd_power_up_a10(struct si476x_core *core,
1065                     struct si476x_power_up_args *puargs)
1066 {
1067     u8       resp[CMD_POWER_UP_A10_NRESP];
1068     const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1069     const bool ctsen  = (core->client->irq != 0);
1070     const u8 args[CMD_POWER_UP_A10_NARGS] = {
1071         0xF7,       /* Reserved, always 0xF7 */
1072         0x3F & puargs->xcload,  /* First two bits are reserved to be
1073                  * zeros */
1074         ctsen << 7 | intsel << 6 | 0x07, /* Last five bits
1075                            * are reserved to
1076                            * be written as 0x7 */
1077         puargs->func << 4 | puargs->freq,
1078         0x11,       /* Reserved, always 0x11 */
1079     };
1080 
1081     return si476x_core_send_command(core, CMD_POWER_UP,
1082                     args, ARRAY_SIZE(args),
1083                     resp, ARRAY_SIZE(resp),
1084                     SI476X_TIMEOUT_POWER_UP);
1085 }
1086 
1087 static int si476x_core_cmd_power_up_a20(struct si476x_core *core,
1088                  struct si476x_power_up_args *puargs)
1089 {
1090     u8       resp[CMD_POWER_UP_A20_NRESP];
1091     const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1092     const bool ctsen  = (core->client->irq != 0);
1093     const u8 args[CMD_POWER_UP_A20_NARGS] = {
1094         puargs->ibias6x << 7 | puargs->xstart,
1095         0x3F & puargs->xcload,  /* First two bits are reserved to be
1096                      * zeros */
1097         ctsen << 7 | intsel << 6 | puargs->fastboot << 5 |
1098         puargs->xbiashc << 3 | puargs->xbias,
1099         puargs->func << 4 | puargs->freq,
1100         0x10 | puargs->xmode,
1101     };
1102 
1103     return si476x_core_send_command(core, CMD_POWER_UP,
1104                     args, ARRAY_SIZE(args),
1105                     resp, ARRAY_SIZE(resp),
1106                     SI476X_TIMEOUT_POWER_UP);
1107 }
1108 
1109 static int si476x_core_cmd_power_down_a10(struct si476x_core *core,
1110                       struct si476x_power_down_args *pdargs)
1111 {
1112     u8 resp[CMD_POWER_DOWN_A10_NRESP];
1113 
1114     return si476x_core_send_command(core, CMD_POWER_DOWN,
1115                     NULL, 0,
1116                     resp, ARRAY_SIZE(resp),
1117                     SI476X_DEFAULT_TIMEOUT);
1118 }
1119 
1120 static int si476x_core_cmd_power_down_a20(struct si476x_core *core,
1121                       struct si476x_power_down_args *pdargs)
1122 {
1123     u8 resp[CMD_POWER_DOWN_A20_NRESP];
1124     const u8 args[CMD_POWER_DOWN_A20_NARGS] = {
1125         pdargs->xosc,
1126     };
1127     return si476x_core_send_command(core, CMD_POWER_DOWN,
1128                     args, ARRAY_SIZE(args),
1129                     resp, ARRAY_SIZE(resp),
1130                     SI476X_DEFAULT_TIMEOUT);
1131 }
1132 
1133 static int si476x_core_cmd_am_tune_freq_a10(struct si476x_core *core,
1134                     struct si476x_tune_freq_args *tuneargs)
1135 {
1136 
1137     const int am_freq = tuneargs->freq;
1138     u8       resp[CMD_AM_TUNE_FREQ_NRESP];
1139     const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1140         (tuneargs->hd << 6),
1141         msb(am_freq),
1142         lsb(am_freq),
1143     };
1144 
1145     return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, args,
1146                      sizeof(args),
1147                      resp, sizeof(resp));
1148 }
1149 
1150 static int si476x_core_cmd_am_tune_freq_a20(struct si476x_core *core,
1151                     struct si476x_tune_freq_args *tuneargs)
1152 {
1153     const int am_freq = tuneargs->freq;
1154     u8       resp[CMD_AM_TUNE_FREQ_NRESP];
1155     const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1156         (tuneargs->zifsr << 6) | (tuneargs->injside & 0x03),
1157         msb(am_freq),
1158         lsb(am_freq),
1159     };
1160 
1161     return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ,
1162                      args, sizeof(args),
1163                      resp, sizeof(resp));
1164 }
1165 
1166 static int si476x_core_cmd_fm_rsq_status_a10(struct si476x_core *core,
1167                     struct si476x_rsq_status_args *rsqargs,
1168                     struct si476x_rsq_status_report *report)
1169 {
1170     int err;
1171     u8       resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1172     const u8 args[CMD_FM_RSQ_STATUS_A10_NARGS] = {
1173         rsqargs->rsqack << 3 | rsqargs->attune << 2 |
1174         rsqargs->cancel << 1 | rsqargs->stcack,
1175     };
1176 
1177     err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1178                        args, ARRAY_SIZE(args),
1179                        resp, ARRAY_SIZE(resp),
1180                        SI476X_DEFAULT_TIMEOUT);
1181     /*
1182      * Besides getting received signal quality information this
1183      * command can be used to just acknowledge different interrupt
1184      * flags in those cases it is useless to copy and parse
1185      * received data so user can pass NULL, and thus avoid
1186      * unnecessary copying.
1187      */
1188     if (err < 0 || report == NULL)
1189         return err;
1190 
1191     report->multhint    = 0x80 & resp[1];
1192     report->multlint    = 0x40 & resp[1];
1193     report->snrhint     = 0x08 & resp[1];
1194     report->snrlint     = 0x04 & resp[1];
1195     report->rssihint    = 0x02 & resp[1];
1196     report->rssilint    = 0x01 & resp[1];
1197 
1198     report->bltf        = 0x80 & resp[2];
1199     report->snr_ready   = 0x20 & resp[2];
1200     report->rssiready   = 0x08 & resp[2];
1201     report->afcrl       = 0x02 & resp[2];
1202     report->valid       = 0x01 & resp[2];
1203 
1204     report->readfreq    = get_unaligned_be16(resp + 3);
1205     report->freqoff     = resp[5];
1206     report->rssi        = resp[6];
1207     report->snr     = resp[7];
1208     report->lassi       = resp[9];
1209     report->hassi       = resp[10];
1210     report->mult        = resp[11];
1211     report->dev     = resp[12];
1212     report->readantcap  = get_unaligned_be16(resp + 13);
1213     report->assi        = resp[15];
1214     report->usn     = resp[16];
1215 
1216     return err;
1217 }
1218 
1219 static int si476x_core_cmd_fm_rsq_status_a20(struct si476x_core *core,
1220                      struct si476x_rsq_status_args *rsqargs,
1221                      struct si476x_rsq_status_report *report)
1222 {
1223     int err;
1224     u8       resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1225     const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1226         rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1227         rsqargs->attune  << 2 | rsqargs->cancel << 1 |
1228         rsqargs->stcack,
1229     };
1230 
1231     err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1232                        args, ARRAY_SIZE(args),
1233                        resp, ARRAY_SIZE(resp),
1234                        SI476X_DEFAULT_TIMEOUT);
1235     /*
1236      * Besides getting received signal quality information this
1237      * command can be used to just acknowledge different interrupt
1238      * flags in those cases it is useless to copy and parse
1239      * received data so user can pass NULL, and thus avoid
1240      * unnecessary copying.
1241      */
1242     if (err < 0 || report == NULL)
1243         return err;
1244 
1245     report->multhint    = 0x80 & resp[1];
1246     report->multlint    = 0x40 & resp[1];
1247     report->snrhint     = 0x08 & resp[1];
1248     report->snrlint     = 0x04 & resp[1];
1249     report->rssihint    = 0x02 & resp[1];
1250     report->rssilint    = 0x01 & resp[1];
1251 
1252     report->bltf        = 0x80 & resp[2];
1253     report->snr_ready   = 0x20 & resp[2];
1254     report->rssiready   = 0x08 & resp[2];
1255     report->afcrl       = 0x02 & resp[2];
1256     report->valid       = 0x01 & resp[2];
1257 
1258     report->readfreq    = get_unaligned_be16(resp + 3);
1259     report->freqoff     = resp[5];
1260     report->rssi        = resp[6];
1261     report->snr     = resp[7];
1262     report->lassi       = resp[9];
1263     report->hassi       = resp[10];
1264     report->mult        = resp[11];
1265     report->dev     = resp[12];
1266     report->readantcap  = get_unaligned_be16(resp + 13);
1267     report->assi        = resp[15];
1268     report->usn     = resp[16];
1269 
1270     return err;
1271 }
1272 
1273 
1274 static int si476x_core_cmd_fm_rsq_status_a30(struct si476x_core *core,
1275                     struct si476x_rsq_status_args *rsqargs,
1276                     struct si476x_rsq_status_report *report)
1277 {
1278     int err;
1279     u8       resp[CMD_FM_RSQ_STATUS_A30_NRESP];
1280     const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1281         rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1282         rsqargs->attune << 2 | rsqargs->cancel << 1 |
1283         rsqargs->stcack,
1284     };
1285 
1286     err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1287                        args, ARRAY_SIZE(args),
1288                        resp, ARRAY_SIZE(resp),
1289                        SI476X_DEFAULT_TIMEOUT);
1290     /*
1291      * Besides getting received signal quality information this
1292      * command can be used to just acknowledge different interrupt
1293      * flags in those cases it is useless to copy and parse
1294      * received data so user can pass NULL, and thus avoid
1295      * unnecessary copying.
1296      */
1297     if (err < 0 || report == NULL)
1298         return err;
1299 
1300     report->multhint    = 0x80 & resp[1];
1301     report->multlint    = 0x40 & resp[1];
1302     report->snrhint     = 0x08 & resp[1];
1303     report->snrlint     = 0x04 & resp[1];
1304     report->rssihint    = 0x02 & resp[1];
1305     report->rssilint    = 0x01 & resp[1];
1306 
1307     report->bltf        = 0x80 & resp[2];
1308     report->snr_ready   = 0x20 & resp[2];
1309     report->rssiready   = 0x08 & resp[2];
1310     report->injside         = 0x04 & resp[2];
1311     report->afcrl       = 0x02 & resp[2];
1312     report->valid       = 0x01 & resp[2];
1313 
1314     report->readfreq    = get_unaligned_be16(resp + 3);
1315     report->freqoff     = resp[5];
1316     report->rssi        = resp[6];
1317     report->snr     = resp[7];
1318     report->issi        = resp[8];
1319     report->lassi       = resp[9];
1320     report->hassi       = resp[10];
1321     report->mult        = resp[11];
1322     report->dev     = resp[12];
1323     report->readantcap  = get_unaligned_be16(resp + 13);
1324     report->assi        = resp[15];
1325     report->usn     = resp[16];
1326 
1327     report->pilotdev    = resp[17];
1328     report->rdsdev      = resp[18];
1329     report->assidev     = resp[19];
1330     report->strongdev   = resp[20];
1331     report->rdspi       = get_unaligned_be16(resp + 21);
1332 
1333     return err;
1334 }
1335 
1336 static int si476x_core_cmd_fm_tune_freq_a10(struct si476x_core *core,
1337                     struct si476x_tune_freq_args *tuneargs)
1338 {
1339     u8       resp[CMD_FM_TUNE_FREQ_NRESP];
1340     const u8 args[CMD_FM_TUNE_FREQ_A10_NARGS] = {
1341         (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1342         | (tuneargs->smoothmetrics << 2),
1343         msb(tuneargs->freq),
1344         lsb(tuneargs->freq),
1345         msb(tuneargs->antcap),
1346         lsb(tuneargs->antcap)
1347     };
1348 
1349     return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1350                      args, sizeof(args),
1351                      resp, sizeof(resp));
1352 }
1353 
1354 static int si476x_core_cmd_fm_tune_freq_a20(struct si476x_core *core,
1355                     struct si476x_tune_freq_args *tuneargs)
1356 {
1357     u8       resp[CMD_FM_TUNE_FREQ_NRESP];
1358     const u8 args[CMD_FM_TUNE_FREQ_A20_NARGS] = {
1359         (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1360         |  (tuneargs->smoothmetrics << 2) | (tuneargs->injside),
1361         msb(tuneargs->freq),
1362         lsb(tuneargs->freq),
1363     };
1364 
1365     return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1366                      args, sizeof(args),
1367                      resp, sizeof(resp));
1368 }
1369 
1370 static int si476x_core_cmd_agc_status_a20(struct si476x_core *core,
1371                     struct si476x_agc_status_report *report)
1372 {
1373     int err;
1374     u8 resp[CMD_AGC_STATUS_NRESP_A20];
1375 
1376     if (!report)
1377         return -EINVAL;
1378 
1379     err = si476x_core_send_command(core, CMD_AGC_STATUS,
1380                        NULL, 0,
1381                        resp, ARRAY_SIZE(resp),
1382                        SI476X_DEFAULT_TIMEOUT);
1383     if (err < 0)
1384         return err;
1385 
1386     report->mxhi        = resp[1] & SI476X_AGC_MXHI;
1387     report->mxlo        = resp[1] & SI476X_AGC_MXLO;
1388     report->lnahi       = resp[1] & SI476X_AGC_LNAHI;
1389     report->lnalo       = resp[1] & SI476X_AGC_LNALO;
1390     report->fmagc1      = resp[2];
1391     report->fmagc2      = resp[3];
1392     report->pgagain     = resp[4];
1393     report->fmwblang    = resp[5];
1394 
1395     return err;
1396 }
1397 
1398 static int si476x_core_cmd_agc_status_a10(struct si476x_core *core,
1399                     struct si476x_agc_status_report *report)
1400 {
1401     int err;
1402     u8 resp[CMD_AGC_STATUS_NRESP_A10];
1403 
1404     if (!report)
1405         return -EINVAL;
1406 
1407     err = si476x_core_send_command(core, CMD_AGC_STATUS,
1408                        NULL, 0,
1409                        resp, ARRAY_SIZE(resp),
1410                        SI476X_DEFAULT_TIMEOUT);
1411     if (err < 0)
1412         return err;
1413 
1414     report->mxhi    = resp[1] & SI476X_AGC_MXHI;
1415     report->mxlo    = resp[1] & SI476X_AGC_MXLO;
1416     report->lnahi   = resp[1] & SI476X_AGC_LNAHI;
1417     report->lnalo   = resp[1] & SI476X_AGC_LNALO;
1418 
1419     return err;
1420 }
1421 
1422 typedef int (*tune_freq_func_t) (struct si476x_core *core,
1423                  struct si476x_tune_freq_args *tuneargs);
1424 
1425 static struct {
1426     int (*power_up)(struct si476x_core *,
1427             struct si476x_power_up_args *);
1428     int (*power_down)(struct si476x_core *,
1429               struct si476x_power_down_args *);
1430 
1431     tune_freq_func_t fm_tune_freq;
1432     tune_freq_func_t am_tune_freq;
1433 
1434     int (*fm_rsq_status)(struct si476x_core *,
1435                  struct si476x_rsq_status_args *,
1436                  struct si476x_rsq_status_report *);
1437 
1438     int (*agc_status)(struct si476x_core *,
1439               struct si476x_agc_status_report *);
1440     int (*intb_pin_cfg)(struct si476x_core *core,
1441                 enum si476x_intb_config intb,
1442                 enum si476x_a1_config a1);
1443 } si476x_cmds_vtable[] = {
1444     [SI476X_REVISION_A10] = {
1445         .power_up   = si476x_core_cmd_power_up_a10,
1446         .power_down = si476x_core_cmd_power_down_a10,
1447         .fm_tune_freq   = si476x_core_cmd_fm_tune_freq_a10,
1448         .am_tune_freq   = si476x_core_cmd_am_tune_freq_a10,
1449         .fm_rsq_status  = si476x_core_cmd_fm_rsq_status_a10,
1450         .agc_status = si476x_core_cmd_agc_status_a10,
1451         .intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a10,
1452     },
1453     [SI476X_REVISION_A20] = {
1454         .power_up   = si476x_core_cmd_power_up_a20,
1455         .power_down = si476x_core_cmd_power_down_a20,
1456         .fm_tune_freq   = si476x_core_cmd_fm_tune_freq_a20,
1457         .am_tune_freq   = si476x_core_cmd_am_tune_freq_a20,
1458         .fm_rsq_status  = si476x_core_cmd_fm_rsq_status_a20,
1459         .agc_status = si476x_core_cmd_agc_status_a20,
1460         .intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a20,
1461     },
1462     [SI476X_REVISION_A30] = {
1463         .power_up   = si476x_core_cmd_power_up_a20,
1464         .power_down = si476x_core_cmd_power_down_a20,
1465         .fm_tune_freq   = si476x_core_cmd_fm_tune_freq_a20,
1466         .am_tune_freq   = si476x_core_cmd_am_tune_freq_a20,
1467         .fm_rsq_status  = si476x_core_cmd_fm_rsq_status_a30,
1468         .agc_status = si476x_core_cmd_agc_status_a20,
1469         .intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a20,
1470     },
1471 };
1472 
1473 int si476x_core_cmd_power_up(struct si476x_core *core,
1474                  struct si476x_power_up_args *args)
1475 {
1476     BUG_ON(core->revision > SI476X_REVISION_A30 ||
1477            core->revision == -1);
1478     return si476x_cmds_vtable[core->revision].power_up(core, args);
1479 }
1480 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up);
1481 
1482 int si476x_core_cmd_power_down(struct si476x_core *core,
1483                    struct si476x_power_down_args *args)
1484 {
1485     BUG_ON(core->revision > SI476X_REVISION_A30 ||
1486            core->revision == -1);
1487     return si476x_cmds_vtable[core->revision].power_down(core, args);
1488 }
1489 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down);
1490 
1491 int si476x_core_cmd_fm_tune_freq(struct si476x_core *core,
1492                  struct si476x_tune_freq_args *args)
1493 {
1494     BUG_ON(core->revision > SI476X_REVISION_A30 ||
1495            core->revision == -1);
1496     return si476x_cmds_vtable[core->revision].fm_tune_freq(core, args);
1497 }
1498 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq);
1499 
1500 int si476x_core_cmd_am_tune_freq(struct si476x_core *core,
1501                  struct si476x_tune_freq_args *args)
1502 {
1503     BUG_ON(core->revision > SI476X_REVISION_A30 ||
1504            core->revision == -1);
1505     return si476x_cmds_vtable[core->revision].am_tune_freq(core, args);
1506 }
1507 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq);
1508 
1509 int si476x_core_cmd_fm_rsq_status(struct si476x_core *core,
1510                   struct si476x_rsq_status_args *args,
1511                   struct si476x_rsq_status_report *report)
1512 
1513 {
1514     BUG_ON(core->revision > SI476X_REVISION_A30 ||
1515            core->revision == -1);
1516     return si476x_cmds_vtable[core->revision].fm_rsq_status(core, args,
1517                                 report);
1518 }
1519 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status);
1520 
1521 int si476x_core_cmd_agc_status(struct si476x_core *core,
1522                   struct si476x_agc_status_report *report)
1523 
1524 {
1525     BUG_ON(core->revision > SI476X_REVISION_A30 ||
1526            core->revision == -1);
1527     return si476x_cmds_vtable[core->revision].agc_status(core, report);
1528 }
1529 EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status);
1530 
1531 int si476x_core_cmd_intb_pin_cfg(struct si476x_core *core,
1532                 enum si476x_intb_config intb,
1533                 enum si476x_a1_config a1)
1534 {
1535     BUG_ON(core->revision > SI476X_REVISION_A30 ||
1536            core->revision == -1);
1537 
1538     return si476x_cmds_vtable[core->revision].intb_pin_cfg(core, intb, a1);
1539 }
1540 EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg);
1541 
1542 MODULE_LICENSE("GPL");
1543 MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
1544 MODULE_DESCRIPTION("API for command exchange for si476x");