cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

si476x-cmd.c (46809B)


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