162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * drivers/mfd/si476x-cmd.c -- Subroutines implementing command
462306a36Sopenharmony_ci * protocol of si476x series of chips
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2012 Innovative Converged Devices(ICD)
762306a36Sopenharmony_ci * Copyright (C) 2013 Andrey Smirnov
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/completion.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/atomic.h>
1662306a36Sopenharmony_ci#include <linux/i2c.h>
1762306a36Sopenharmony_ci#include <linux/device.h>
1862306a36Sopenharmony_ci#include <linux/gpio.h>
1962306a36Sopenharmony_ci#include <linux/videodev2.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/mfd/si476x-core.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <asm/unaligned.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define msb(x)                  ((u8)((u16) x >> 8))
2662306a36Sopenharmony_ci#define lsb(x)                  ((u8)((u16) x &  0x00FF))
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define CMD_POWER_UP				0x01
3162306a36Sopenharmony_ci#define CMD_POWER_UP_A10_NRESP			1
3262306a36Sopenharmony_ci#define CMD_POWER_UP_A10_NARGS			5
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define CMD_POWER_UP_A20_NRESP			1
3562306a36Sopenharmony_ci#define CMD_POWER_UP_A20_NARGS			5
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define POWER_UP_DELAY_MS			110
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define CMD_POWER_DOWN				0x11
4062306a36Sopenharmony_ci#define CMD_POWER_DOWN_A10_NRESP		1
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define CMD_POWER_DOWN_A20_NRESP		1
4362306a36Sopenharmony_ci#define CMD_POWER_DOWN_A20_NARGS		1
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define CMD_FUNC_INFO				0x12
4662306a36Sopenharmony_ci#define CMD_FUNC_INFO_NRESP			7
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define CMD_SET_PROPERTY			0x13
4962306a36Sopenharmony_ci#define CMD_SET_PROPERTY_NARGS			5
5062306a36Sopenharmony_ci#define CMD_SET_PROPERTY_NRESP			1
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define CMD_GET_PROPERTY			0x14
5362306a36Sopenharmony_ci#define CMD_GET_PROPERTY_NARGS			3
5462306a36Sopenharmony_ci#define CMD_GET_PROPERTY_NRESP			4
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define CMD_AGC_STATUS				0x17
5762306a36Sopenharmony_ci#define CMD_AGC_STATUS_NRESP_A10		2
5862306a36Sopenharmony_ci#define CMD_AGC_STATUS_NRESP_A20                6
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#define PIN_CFG_BYTE(x) (0x7F & (x))
6162306a36Sopenharmony_ci#define CMD_DIG_AUDIO_PIN_CFG			0x18
6262306a36Sopenharmony_ci#define CMD_DIG_AUDIO_PIN_CFG_NARGS		4
6362306a36Sopenharmony_ci#define CMD_DIG_AUDIO_PIN_CFG_NRESP		5
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define CMD_ZIF_PIN_CFG				0x19
6662306a36Sopenharmony_ci#define CMD_ZIF_PIN_CFG_NARGS			4
6762306a36Sopenharmony_ci#define CMD_ZIF_PIN_CFG_NRESP			5
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#define CMD_IC_LINK_GPO_CTL_PIN_CFG		0x1A
7062306a36Sopenharmony_ci#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS	4
7162306a36Sopenharmony_ci#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP	5
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define CMD_ANA_AUDIO_PIN_CFG			0x1B
7462306a36Sopenharmony_ci#define CMD_ANA_AUDIO_PIN_CFG_NARGS		1
7562306a36Sopenharmony_ci#define CMD_ANA_AUDIO_PIN_CFG_NRESP		2
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define CMD_INTB_PIN_CFG			0x1C
7862306a36Sopenharmony_ci#define CMD_INTB_PIN_CFG_NARGS			2
7962306a36Sopenharmony_ci#define CMD_INTB_PIN_CFG_A10_NRESP		6
8062306a36Sopenharmony_ci#define CMD_INTB_PIN_CFG_A20_NRESP		3
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#define CMD_FM_TUNE_FREQ			0x30
8362306a36Sopenharmony_ci#define CMD_FM_TUNE_FREQ_A10_NARGS		5
8462306a36Sopenharmony_ci#define CMD_FM_TUNE_FREQ_A20_NARGS		3
8562306a36Sopenharmony_ci#define CMD_FM_TUNE_FREQ_NRESP			1
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define CMD_FM_RSQ_STATUS			0x32
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define CMD_FM_RSQ_STATUS_A10_NARGS		1
9062306a36Sopenharmony_ci#define CMD_FM_RSQ_STATUS_A10_NRESP		17
9162306a36Sopenharmony_ci#define CMD_FM_RSQ_STATUS_A30_NARGS		1
9262306a36Sopenharmony_ci#define CMD_FM_RSQ_STATUS_A30_NRESP		23
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci#define CMD_FM_SEEK_START			0x31
9662306a36Sopenharmony_ci#define CMD_FM_SEEK_START_NARGS			1
9762306a36Sopenharmony_ci#define CMD_FM_SEEK_START_NRESP			1
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci#define CMD_FM_RDS_STATUS			0x36
10062306a36Sopenharmony_ci#define CMD_FM_RDS_STATUS_NARGS			1
10162306a36Sopenharmony_ci#define CMD_FM_RDS_STATUS_NRESP			16
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#define CMD_FM_RDS_BLOCKCOUNT			0x37
10462306a36Sopenharmony_ci#define CMD_FM_RDS_BLOCKCOUNT_NARGS		1
10562306a36Sopenharmony_ci#define CMD_FM_RDS_BLOCKCOUNT_NRESP		8
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci#define CMD_FM_PHASE_DIVERSITY			0x38
10862306a36Sopenharmony_ci#define CMD_FM_PHASE_DIVERSITY_NARGS		1
10962306a36Sopenharmony_ci#define CMD_FM_PHASE_DIVERSITY_NRESP		1
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci#define CMD_FM_PHASE_DIV_STATUS			0x39
11262306a36Sopenharmony_ci#define CMD_FM_PHASE_DIV_STATUS_NRESP		2
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci#define CMD_AM_TUNE_FREQ			0x40
11562306a36Sopenharmony_ci#define CMD_AM_TUNE_FREQ_NARGS			3
11662306a36Sopenharmony_ci#define CMD_AM_TUNE_FREQ_NRESP			1
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci#define CMD_AM_RSQ_STATUS			0x42
11962306a36Sopenharmony_ci#define CMD_AM_RSQ_STATUS_NARGS			1
12062306a36Sopenharmony_ci#define CMD_AM_RSQ_STATUS_NRESP			13
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci#define CMD_AM_SEEK_START			0x41
12362306a36Sopenharmony_ci#define CMD_AM_SEEK_START_NARGS			1
12462306a36Sopenharmony_ci#define CMD_AM_SEEK_START_NRESP			1
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci#define CMD_AM_ACF_STATUS			0x45
12862306a36Sopenharmony_ci#define CMD_AM_ACF_STATUS_NRESP			6
12962306a36Sopenharmony_ci#define CMD_AM_ACF_STATUS_NARGS			1
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci#define CMD_FM_ACF_STATUS			0x35
13262306a36Sopenharmony_ci#define CMD_FM_ACF_STATUS_NRESP			8
13362306a36Sopenharmony_ci#define CMD_FM_ACF_STATUS_NARGS			1
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci#define CMD_MAX_ARGS_COUNT			(10)
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cienum si476x_acf_status_report_bits {
13962306a36Sopenharmony_ci	SI476X_ACF_BLEND_INT	= (1 << 4),
14062306a36Sopenharmony_ci	SI476X_ACF_HIBLEND_INT	= (1 << 3),
14162306a36Sopenharmony_ci	SI476X_ACF_HICUT_INT	= (1 << 2),
14262306a36Sopenharmony_ci	SI476X_ACF_CHBW_INT	= (1 << 1),
14362306a36Sopenharmony_ci	SI476X_ACF_SOFTMUTE_INT	= (1 << 0),
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	SI476X_ACF_SMUTE	= (1 << 0),
14662306a36Sopenharmony_ci	SI476X_ACF_SMATTN	= 0x1f,
14762306a36Sopenharmony_ci	SI476X_ACF_PILOT	= (1 << 7),
14862306a36Sopenharmony_ci	SI476X_ACF_STBLEND	= ~SI476X_ACF_PILOT,
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cienum si476x_agc_status_report_bits {
15262306a36Sopenharmony_ci	SI476X_AGC_MXHI		= (1 << 5),
15362306a36Sopenharmony_ci	SI476X_AGC_MXLO		= (1 << 4),
15462306a36Sopenharmony_ci	SI476X_AGC_LNAHI	= (1 << 3),
15562306a36Sopenharmony_ci	SI476X_AGC_LNALO	= (1 << 2),
15662306a36Sopenharmony_ci};
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cienum si476x_errors {
15962306a36Sopenharmony_ci	SI476X_ERR_BAD_COMMAND		= 0x10,
16062306a36Sopenharmony_ci	SI476X_ERR_BAD_ARG1		= 0x11,
16162306a36Sopenharmony_ci	SI476X_ERR_BAD_ARG2		= 0x12,
16262306a36Sopenharmony_ci	SI476X_ERR_BAD_ARG3		= 0x13,
16362306a36Sopenharmony_ci	SI476X_ERR_BAD_ARG4		= 0x14,
16462306a36Sopenharmony_ci	SI476X_ERR_BUSY			= 0x18,
16562306a36Sopenharmony_ci	SI476X_ERR_BAD_INTERNAL_MEMORY  = 0x20,
16662306a36Sopenharmony_ci	SI476X_ERR_BAD_PATCH		= 0x30,
16762306a36Sopenharmony_ci	SI476X_ERR_BAD_BOOT_MODE	= 0x31,
16862306a36Sopenharmony_ci	SI476X_ERR_BAD_PROPERTY		= 0x40,
16962306a36Sopenharmony_ci};
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic int si476x_core_parse_and_nag_about_error(struct si476x_core *core)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	int err;
17462306a36Sopenharmony_ci	char *cause;
17562306a36Sopenharmony_ci	u8 buffer[2];
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if (core->revision != SI476X_REVISION_A10) {
17862306a36Sopenharmony_ci		err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV,
17962306a36Sopenharmony_ci					   buffer, sizeof(buffer));
18062306a36Sopenharmony_ci		if (err == sizeof(buffer)) {
18162306a36Sopenharmony_ci			switch (buffer[1]) {
18262306a36Sopenharmony_ci			case SI476X_ERR_BAD_COMMAND:
18362306a36Sopenharmony_ci				cause = "Bad command";
18462306a36Sopenharmony_ci				err = -EINVAL;
18562306a36Sopenharmony_ci				break;
18662306a36Sopenharmony_ci			case SI476X_ERR_BAD_ARG1:
18762306a36Sopenharmony_ci				cause = "Bad argument #1";
18862306a36Sopenharmony_ci				err = -EINVAL;
18962306a36Sopenharmony_ci				break;
19062306a36Sopenharmony_ci			case SI476X_ERR_BAD_ARG2:
19162306a36Sopenharmony_ci				cause = "Bad argument #2";
19262306a36Sopenharmony_ci				err = -EINVAL;
19362306a36Sopenharmony_ci				break;
19462306a36Sopenharmony_ci			case SI476X_ERR_BAD_ARG3:
19562306a36Sopenharmony_ci				cause = "Bad argument #3";
19662306a36Sopenharmony_ci				err = -EINVAL;
19762306a36Sopenharmony_ci				break;
19862306a36Sopenharmony_ci			case SI476X_ERR_BAD_ARG4:
19962306a36Sopenharmony_ci				cause = "Bad argument #4";
20062306a36Sopenharmony_ci				err = -EINVAL;
20162306a36Sopenharmony_ci				break;
20262306a36Sopenharmony_ci			case SI476X_ERR_BUSY:
20362306a36Sopenharmony_ci				cause = "Chip is busy";
20462306a36Sopenharmony_ci				err = -EBUSY;
20562306a36Sopenharmony_ci				break;
20662306a36Sopenharmony_ci			case SI476X_ERR_BAD_INTERNAL_MEMORY:
20762306a36Sopenharmony_ci				cause = "Bad internal memory";
20862306a36Sopenharmony_ci				err = -EIO;
20962306a36Sopenharmony_ci				break;
21062306a36Sopenharmony_ci			case SI476X_ERR_BAD_PATCH:
21162306a36Sopenharmony_ci				cause = "Bad patch";
21262306a36Sopenharmony_ci				err = -EINVAL;
21362306a36Sopenharmony_ci				break;
21462306a36Sopenharmony_ci			case SI476X_ERR_BAD_BOOT_MODE:
21562306a36Sopenharmony_ci				cause = "Bad boot mode";
21662306a36Sopenharmony_ci				err = -EINVAL;
21762306a36Sopenharmony_ci				break;
21862306a36Sopenharmony_ci			case SI476X_ERR_BAD_PROPERTY:
21962306a36Sopenharmony_ci				cause = "Bad property";
22062306a36Sopenharmony_ci				err = -EINVAL;
22162306a36Sopenharmony_ci				break;
22262306a36Sopenharmony_ci			default:
22362306a36Sopenharmony_ci				cause = "Unknown";
22462306a36Sopenharmony_ci				err = -EIO;
22562306a36Sopenharmony_ci			}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci			dev_err(&core->client->dev,
22862306a36Sopenharmony_ci				"[Chip error status]: %s\n", cause);
22962306a36Sopenharmony_ci		} else {
23062306a36Sopenharmony_ci			dev_err(&core->client->dev,
23162306a36Sopenharmony_ci				"Failed to fetch error code\n");
23262306a36Sopenharmony_ci			err = (err >= 0) ? -EIO : err;
23362306a36Sopenharmony_ci		}
23462306a36Sopenharmony_ci	} else {
23562306a36Sopenharmony_ci		err = -EIO;
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return err;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/**
24262306a36Sopenharmony_ci * si476x_core_send_command() - sends a command to si476x and waits its
24362306a36Sopenharmony_ci * response
24462306a36Sopenharmony_ci * @core:     si476x_device structure for the device we are
24562306a36Sopenharmony_ci *            communicating with
24662306a36Sopenharmony_ci * @command:  command id
24762306a36Sopenharmony_ci * @args:     command arguments we are sending
24862306a36Sopenharmony_ci * @argn:     actual size of @args
24962306a36Sopenharmony_ci * @resp:     buffer to place the expected response from the device
25062306a36Sopenharmony_ci * @respn:    actual size of @resp
25162306a36Sopenharmony_ci * @usecs:    amount of time to wait before reading the response (in
25262306a36Sopenharmony_ci *            usecs)
25362306a36Sopenharmony_ci *
25462306a36Sopenharmony_ci * Function returns 0 on success and negative error code on
25562306a36Sopenharmony_ci * failure
25662306a36Sopenharmony_ci */
25762306a36Sopenharmony_cistatic int si476x_core_send_command(struct si476x_core *core,
25862306a36Sopenharmony_ci				    const u8 command,
25962306a36Sopenharmony_ci				    const u8 args[],
26062306a36Sopenharmony_ci				    const int argn,
26162306a36Sopenharmony_ci				    u8 resp[],
26262306a36Sopenharmony_ci				    const int respn,
26362306a36Sopenharmony_ci				    const int usecs)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct i2c_client *client = core->client;
26662306a36Sopenharmony_ci	int err;
26762306a36Sopenharmony_ci	u8  data[CMD_MAX_ARGS_COUNT + 1];
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (argn > CMD_MAX_ARGS_COUNT) {
27062306a36Sopenharmony_ci		err = -ENOMEM;
27162306a36Sopenharmony_ci		goto exit;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (!client->adapter) {
27562306a36Sopenharmony_ci		err = -ENODEV;
27662306a36Sopenharmony_ci		goto exit;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/* First send the command and its arguments */
28062306a36Sopenharmony_ci	data[0] = command;
28162306a36Sopenharmony_ci	memcpy(&data[1], args, argn);
28262306a36Sopenharmony_ci	dev_dbg(&client->dev, "Command:\n %*ph\n", argn + 1, data);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	err = si476x_core_i2c_xfer(core, SI476X_I2C_SEND,
28562306a36Sopenharmony_ci				   (char *) data, argn + 1);
28662306a36Sopenharmony_ci	if (err != argn + 1) {
28762306a36Sopenharmony_ci		dev_err(&core->client->dev,
28862306a36Sopenharmony_ci			"Error while sending command 0x%02x\n",
28962306a36Sopenharmony_ci			command);
29062306a36Sopenharmony_ci		err = (err >= 0) ? -EIO : err;
29162306a36Sopenharmony_ci		goto exit;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci	/* Set CTS to zero only after the command is send to avoid
29462306a36Sopenharmony_ci	 * possible racing conditions when working in polling mode */
29562306a36Sopenharmony_ci	atomic_set(&core->cts, 0);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	/* if (unlikely(command == CMD_POWER_DOWN) */
29862306a36Sopenharmony_ci	if (!wait_event_timeout(core->command,
29962306a36Sopenharmony_ci				atomic_read(&core->cts),
30062306a36Sopenharmony_ci				usecs_to_jiffies(usecs) + 1))
30162306a36Sopenharmony_ci		dev_warn(&core->client->dev,
30262306a36Sopenharmony_ci			 "(%s) [CMD 0x%02x] Answer timeout.\n",
30362306a36Sopenharmony_ci			 __func__, command);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/*
30662306a36Sopenharmony_ci	  When working in polling mode, for some reason the tuner will
30762306a36Sopenharmony_ci	  report CTS bit as being set in the first status byte read,
30862306a36Sopenharmony_ci	  but all the consequtive ones will return zeros until the
30962306a36Sopenharmony_ci	  tuner is actually completed the POWER_UP command. To
31062306a36Sopenharmony_ci	  workaround that we wait for second CTS to be reported
31162306a36Sopenharmony_ci	 */
31262306a36Sopenharmony_ci	if (unlikely(!core->client->irq && command == CMD_POWER_UP)) {
31362306a36Sopenharmony_ci		if (!wait_event_timeout(core->command,
31462306a36Sopenharmony_ci					atomic_read(&core->cts),
31562306a36Sopenharmony_ci					usecs_to_jiffies(usecs) + 1))
31662306a36Sopenharmony_ci			dev_warn(&core->client->dev,
31762306a36Sopenharmony_ci				 "(%s) Power up took too much time.\n",
31862306a36Sopenharmony_ci				 __func__);
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	/* Then get the response */
32262306a36Sopenharmony_ci	err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, resp, respn);
32362306a36Sopenharmony_ci	if (err != respn) {
32462306a36Sopenharmony_ci		dev_err(&core->client->dev,
32562306a36Sopenharmony_ci			"Error while reading response for command 0x%02x\n",
32662306a36Sopenharmony_ci			command);
32762306a36Sopenharmony_ci		err = (err >= 0) ? -EIO : err;
32862306a36Sopenharmony_ci		goto exit;
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci	dev_dbg(&client->dev, "Response:\n %*ph\n", respn, resp);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	err = 0;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (resp[0] & SI476X_ERR) {
33562306a36Sopenharmony_ci		dev_err(&core->client->dev,
33662306a36Sopenharmony_ci			"[CMD 0x%02x] Chip set error flag\n", command);
33762306a36Sopenharmony_ci		err = si476x_core_parse_and_nag_about_error(core);
33862306a36Sopenharmony_ci		goto exit;
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	if (!(resp[0] & SI476X_CTS))
34262306a36Sopenharmony_ci		err = -EBUSY;
34362306a36Sopenharmony_ciexit:
34462306a36Sopenharmony_ci	return err;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic int si476x_cmd_clear_stc(struct si476x_core *core)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	int err;
35062306a36Sopenharmony_ci	struct si476x_rsq_status_args args = {
35162306a36Sopenharmony_ci		.primary	= false,
35262306a36Sopenharmony_ci		.rsqack		= false,
35362306a36Sopenharmony_ci		.attune		= false,
35462306a36Sopenharmony_ci		.cancel		= false,
35562306a36Sopenharmony_ci		.stcack		= true,
35662306a36Sopenharmony_ci	};
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	switch (core->power_up_parameters.func) {
35962306a36Sopenharmony_ci	case SI476X_FUNC_FM_RECEIVER:
36062306a36Sopenharmony_ci		err = si476x_core_cmd_fm_rsq_status(core, &args, NULL);
36162306a36Sopenharmony_ci		break;
36262306a36Sopenharmony_ci	case SI476X_FUNC_AM_RECEIVER:
36362306a36Sopenharmony_ci		err = si476x_core_cmd_am_rsq_status(core, &args, NULL);
36462306a36Sopenharmony_ci		break;
36562306a36Sopenharmony_ci	default:
36662306a36Sopenharmony_ci		err = -EINVAL;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	return err;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic int si476x_cmd_tune_seek_freq(struct si476x_core *core,
37362306a36Sopenharmony_ci				     uint8_t cmd,
37462306a36Sopenharmony_ci				     const uint8_t args[], size_t argn,
37562306a36Sopenharmony_ci				     uint8_t *resp, size_t respn)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	int err;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	atomic_set(&core->stc, 0);
38162306a36Sopenharmony_ci	err = si476x_core_send_command(core, cmd, args, argn, resp, respn,
38262306a36Sopenharmony_ci				       SI476X_TIMEOUT_TUNE);
38362306a36Sopenharmony_ci	if (!err) {
38462306a36Sopenharmony_ci		wait_event_killable(core->tuning,
38562306a36Sopenharmony_ci				    atomic_read(&core->stc));
38662306a36Sopenharmony_ci		si476x_cmd_clear_stc(core);
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	return err;
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci/**
39362306a36Sopenharmony_ci * si476x_core_cmd_func_info() - send 'FUNC_INFO' command to the device
39462306a36Sopenharmony_ci * @core: device to send the command to
39562306a36Sopenharmony_ci * @info:  struct si476x_func_info to fill all the information
39662306a36Sopenharmony_ci *         returned by the command
39762306a36Sopenharmony_ci *
39862306a36Sopenharmony_ci * The command requests the firmware and patch version for currently
39962306a36Sopenharmony_ci * loaded firmware (dependent on the function of the device FM/AM/WB)
40062306a36Sopenharmony_ci *
40162306a36Sopenharmony_ci * Function returns 0 on success and negative error code on
40262306a36Sopenharmony_ci * failure
40362306a36Sopenharmony_ci */
40462306a36Sopenharmony_ciint si476x_core_cmd_func_info(struct si476x_core *core,
40562306a36Sopenharmony_ci			      struct si476x_func_info *info)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	int err;
40862306a36Sopenharmony_ci	u8  resp[CMD_FUNC_INFO_NRESP];
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	err = si476x_core_send_command(core, CMD_FUNC_INFO,
41162306a36Sopenharmony_ci				       NULL, 0,
41262306a36Sopenharmony_ci				       resp, ARRAY_SIZE(resp),
41362306a36Sopenharmony_ci				       SI476X_DEFAULT_TIMEOUT);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	info->firmware.major    = resp[1];
41662306a36Sopenharmony_ci	info->firmware.minor[0] = resp[2];
41762306a36Sopenharmony_ci	info->firmware.minor[1] = resp[3];
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	info->patch_id = ((u16) resp[4] << 8) | resp[5];
42062306a36Sopenharmony_ci	info->func     = resp[6];
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	return err;
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_func_info);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci/**
42762306a36Sopenharmony_ci * si476x_core_cmd_set_property() - send 'SET_PROPERTY' command to the device
42862306a36Sopenharmony_ci * @core:    device to send the command to
42962306a36Sopenharmony_ci * @property: property address
43062306a36Sopenharmony_ci * @value:    property value
43162306a36Sopenharmony_ci *
43262306a36Sopenharmony_ci * Function returns 0 on success and negative error code on
43362306a36Sopenharmony_ci * failure
43462306a36Sopenharmony_ci */
43562306a36Sopenharmony_ciint si476x_core_cmd_set_property(struct si476x_core *core,
43662306a36Sopenharmony_ci				 u16 property, u16 value)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	u8       resp[CMD_SET_PROPERTY_NRESP];
43962306a36Sopenharmony_ci	const u8 args[CMD_SET_PROPERTY_NARGS] = {
44062306a36Sopenharmony_ci		0x00,
44162306a36Sopenharmony_ci		msb(property),
44262306a36Sopenharmony_ci		lsb(property),
44362306a36Sopenharmony_ci		msb(value),
44462306a36Sopenharmony_ci		lsb(value),
44562306a36Sopenharmony_ci	};
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	return si476x_core_send_command(core, CMD_SET_PROPERTY,
44862306a36Sopenharmony_ci					args, ARRAY_SIZE(args),
44962306a36Sopenharmony_ci					resp, ARRAY_SIZE(resp),
45062306a36Sopenharmony_ci					SI476X_DEFAULT_TIMEOUT);
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_set_property);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci/**
45562306a36Sopenharmony_ci * si476x_core_cmd_get_property() - send 'GET_PROPERTY' command to the device
45662306a36Sopenharmony_ci * @core:    device to send the command to
45762306a36Sopenharmony_ci * @property: property address
45862306a36Sopenharmony_ci *
45962306a36Sopenharmony_ci * Function return the value of property as u16 on success or a
46062306a36Sopenharmony_ci * negative error on failure
46162306a36Sopenharmony_ci */
46262306a36Sopenharmony_ciint si476x_core_cmd_get_property(struct si476x_core *core, u16 property)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	int err;
46562306a36Sopenharmony_ci	u8       resp[CMD_GET_PROPERTY_NRESP];
46662306a36Sopenharmony_ci	const u8 args[CMD_GET_PROPERTY_NARGS] = {
46762306a36Sopenharmony_ci		0x00,
46862306a36Sopenharmony_ci		msb(property),
46962306a36Sopenharmony_ci		lsb(property),
47062306a36Sopenharmony_ci	};
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	err = si476x_core_send_command(core, CMD_GET_PROPERTY,
47362306a36Sopenharmony_ci				       args, ARRAY_SIZE(args),
47462306a36Sopenharmony_ci				       resp, ARRAY_SIZE(resp),
47562306a36Sopenharmony_ci				       SI476X_DEFAULT_TIMEOUT);
47662306a36Sopenharmony_ci	if (err < 0)
47762306a36Sopenharmony_ci		return err;
47862306a36Sopenharmony_ci	else
47962306a36Sopenharmony_ci		return get_unaligned_be16(resp + 2);
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_get_property);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci/**
48462306a36Sopenharmony_ci * si476x_core_cmd_dig_audio_pin_cfg() - send 'DIG_AUDIO_PIN_CFG' command to
48562306a36Sopenharmony_ci * the device
48662306a36Sopenharmony_ci * @core: device to send the command to
48762306a36Sopenharmony_ci * @dclk:  DCLK pin function configuration:
48862306a36Sopenharmony_ci *	   #SI476X_DCLK_NOOP     - do not modify the behaviour
48962306a36Sopenharmony_ci *         #SI476X_DCLK_TRISTATE - put the pin in tristate condition,
49062306a36Sopenharmony_ci *                                 enable 1MOhm pulldown
49162306a36Sopenharmony_ci *         #SI476X_DCLK_DAUDIO   - set the pin to be a part of digital
49262306a36Sopenharmony_ci *                                 audio interface
49362306a36Sopenharmony_ci * @dfs:   DFS pin function configuration:
49462306a36Sopenharmony_ci *         #SI476X_DFS_NOOP      - do not modify the behaviour
49562306a36Sopenharmony_ci *         #SI476X_DFS_TRISTATE  - put the pin in tristate condition,
49662306a36Sopenharmony_ci *                             enable 1MOhm pulldown
49762306a36Sopenharmony_ci *      SI476X_DFS_DAUDIO    - set the pin to be a part of digital
49862306a36Sopenharmony_ci *                             audio interface
49962306a36Sopenharmony_ci * @dout: - DOUT pin function configuration:
50062306a36Sopenharmony_ci *      SI476X_DOUT_NOOP       - do not modify the behaviour
50162306a36Sopenharmony_ci *      SI476X_DOUT_TRISTATE   - put the pin in tristate condition,
50262306a36Sopenharmony_ci *                               enable 1MOhm pulldown
50362306a36Sopenharmony_ci *      SI476X_DOUT_I2S_OUTPUT - set this pin to be digital out on I2S
50462306a36Sopenharmony_ci *                               port 1
50562306a36Sopenharmony_ci *      SI476X_DOUT_I2S_INPUT  - set this pin to be digital in on I2S
50662306a36Sopenharmony_ci *                               port 1
50762306a36Sopenharmony_ci * @xout: - XOUT pin function configuration:
50862306a36Sopenharmony_ci *	SI476X_XOUT_NOOP        - do not modify the behaviour
50962306a36Sopenharmony_ci *      SI476X_XOUT_TRISTATE    - put the pin in tristate condition,
51062306a36Sopenharmony_ci *                                enable 1MOhm pulldown
51162306a36Sopenharmony_ci *      SI476X_XOUT_I2S_INPUT   - set this pin to be digital in on I2S
51262306a36Sopenharmony_ci *                                port 1
51362306a36Sopenharmony_ci *      SI476X_XOUT_MODE_SELECT - set this pin to be the input that
51462306a36Sopenharmony_ci *                                selects the mode of the I2S audio
51562306a36Sopenharmony_ci *                                combiner (analog or HD)
51662306a36Sopenharmony_ci *                                [SI4761/63/65/67 Only]
51762306a36Sopenharmony_ci *
51862306a36Sopenharmony_ci * Function returns 0 on success and negative error code on failure
51962306a36Sopenharmony_ci */
52062306a36Sopenharmony_ciint si476x_core_cmd_dig_audio_pin_cfg(struct  si476x_core *core,
52162306a36Sopenharmony_ci				      enum si476x_dclk_config dclk,
52262306a36Sopenharmony_ci				      enum si476x_dfs_config  dfs,
52362306a36Sopenharmony_ci				      enum si476x_dout_config dout,
52462306a36Sopenharmony_ci				      enum si476x_xout_config xout)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	u8       resp[CMD_DIG_AUDIO_PIN_CFG_NRESP];
52762306a36Sopenharmony_ci	const u8 args[CMD_DIG_AUDIO_PIN_CFG_NARGS] = {
52862306a36Sopenharmony_ci		PIN_CFG_BYTE(dclk),
52962306a36Sopenharmony_ci		PIN_CFG_BYTE(dfs),
53062306a36Sopenharmony_ci		PIN_CFG_BYTE(dout),
53162306a36Sopenharmony_ci		PIN_CFG_BYTE(xout),
53262306a36Sopenharmony_ci	};
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	return si476x_core_send_command(core, CMD_DIG_AUDIO_PIN_CFG,
53562306a36Sopenharmony_ci					args, ARRAY_SIZE(args),
53662306a36Sopenharmony_ci					resp, ARRAY_SIZE(resp),
53762306a36Sopenharmony_ci					SI476X_DEFAULT_TIMEOUT);
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci/**
54262306a36Sopenharmony_ci * si476x_core_cmd_zif_pin_cfg - send 'ZIF_PIN_CFG_COMMAND'
54362306a36Sopenharmony_ci * @core: - device to send the command to
54462306a36Sopenharmony_ci * @iqclk: - IQCL pin function configuration:
54562306a36Sopenharmony_ci *       SI476X_IQCLK_NOOP     - do not modify the behaviour
54662306a36Sopenharmony_ci *       SI476X_IQCLK_TRISTATE - put the pin in tristate condition,
54762306a36Sopenharmony_ci *                               enable 1MOhm pulldown
54862306a36Sopenharmony_ci *       SI476X_IQCLK_IQ       - set pin to be a part of I/Q interface
54962306a36Sopenharmony_ci *                               in master mode
55062306a36Sopenharmony_ci * @iqfs: - IQFS pin function configuration:
55162306a36Sopenharmony_ci *       SI476X_IQFS_NOOP     - do not modify the behaviour
55262306a36Sopenharmony_ci *       SI476X_IQFS_TRISTATE - put the pin in tristate condition,
55362306a36Sopenharmony_ci *                              enable 1MOhm pulldown
55462306a36Sopenharmony_ci *       SI476X_IQFS_IQ       - set pin to be a part of I/Q interface
55562306a36Sopenharmony_ci *                              in master mode
55662306a36Sopenharmony_ci * @iout: - IOUT pin function configuration:
55762306a36Sopenharmony_ci *       SI476X_IOUT_NOOP     - do not modify the behaviour
55862306a36Sopenharmony_ci *       SI476X_IOUT_TRISTATE - put the pin in tristate condition,
55962306a36Sopenharmony_ci *                              enable 1MOhm pulldown
56062306a36Sopenharmony_ci *       SI476X_IOUT_OUTPUT   - set pin to be I out
56162306a36Sopenharmony_ci * @qout: - QOUT pin function configuration:
56262306a36Sopenharmony_ci *       SI476X_QOUT_NOOP     - do not modify the behaviour
56362306a36Sopenharmony_ci *       SI476X_QOUT_TRISTATE - put the pin in tristate condition,
56462306a36Sopenharmony_ci *                              enable 1MOhm pulldown
56562306a36Sopenharmony_ci *       SI476X_QOUT_OUTPUT   - set pin to be Q out
56662306a36Sopenharmony_ci *
56762306a36Sopenharmony_ci * Function returns 0 on success and negative error code on failure
56862306a36Sopenharmony_ci */
56962306a36Sopenharmony_ciint si476x_core_cmd_zif_pin_cfg(struct si476x_core *core,
57062306a36Sopenharmony_ci				enum si476x_iqclk_config iqclk,
57162306a36Sopenharmony_ci				enum si476x_iqfs_config iqfs,
57262306a36Sopenharmony_ci				enum si476x_iout_config iout,
57362306a36Sopenharmony_ci				enum si476x_qout_config qout)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	u8       resp[CMD_ZIF_PIN_CFG_NRESP];
57662306a36Sopenharmony_ci	const u8 args[CMD_ZIF_PIN_CFG_NARGS] = {
57762306a36Sopenharmony_ci		PIN_CFG_BYTE(iqclk),
57862306a36Sopenharmony_ci		PIN_CFG_BYTE(iqfs),
57962306a36Sopenharmony_ci		PIN_CFG_BYTE(iout),
58062306a36Sopenharmony_ci		PIN_CFG_BYTE(qout),
58162306a36Sopenharmony_ci	};
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	return si476x_core_send_command(core, CMD_ZIF_PIN_CFG,
58462306a36Sopenharmony_ci					args, ARRAY_SIZE(args),
58562306a36Sopenharmony_ci					resp, ARRAY_SIZE(resp),
58662306a36Sopenharmony_ci					SI476X_DEFAULT_TIMEOUT);
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci/**
59162306a36Sopenharmony_ci * si476x_core_cmd_ic_link_gpo_ctl_pin_cfg - send
59262306a36Sopenharmony_ci * 'IC_LINK_GPIO_CTL_PIN_CFG' command to the device
59362306a36Sopenharmony_ci * @core: - device to send the command to
59462306a36Sopenharmony_ci * @icin: - ICIN pin function configuration:
59562306a36Sopenharmony_ci *      SI476X_ICIN_NOOP      - do not modify the behaviour
59662306a36Sopenharmony_ci *      SI476X_ICIN_TRISTATE  - put the pin in tristate condition,
59762306a36Sopenharmony_ci *                              enable 1MOhm pulldown
59862306a36Sopenharmony_ci *      SI476X_ICIN_GPO1_HIGH - set pin to be an output, drive it high
59962306a36Sopenharmony_ci *      SI476X_ICIN_GPO1_LOW  - set pin to be an output, drive it low
60062306a36Sopenharmony_ci *      SI476X_ICIN_IC_LINK   - set the pin to be a part of Inter-Chip link
60162306a36Sopenharmony_ci * @icip: - ICIP pin function configuration:
60262306a36Sopenharmony_ci *      SI476X_ICIP_NOOP      - do not modify the behaviour
60362306a36Sopenharmony_ci *      SI476X_ICIP_TRISTATE  - put the pin in tristate condition,
60462306a36Sopenharmony_ci *                              enable 1MOhm pulldown
60562306a36Sopenharmony_ci *      SI476X_ICIP_GPO1_HIGH - set pin to be an output, drive it high
60662306a36Sopenharmony_ci *      SI476X_ICIP_GPO1_LOW  - set pin to be an output, drive it low
60762306a36Sopenharmony_ci *      SI476X_ICIP_IC_LINK   - set the pin to be a part of Inter-Chip link
60862306a36Sopenharmony_ci * @icon: - ICON pin function configuration:
60962306a36Sopenharmony_ci *      SI476X_ICON_NOOP     - do not modify the behaviour
61062306a36Sopenharmony_ci *      SI476X_ICON_TRISTATE - put the pin in tristate condition,
61162306a36Sopenharmony_ci *                             enable 1MOhm pulldown
61262306a36Sopenharmony_ci *      SI476X_ICON_I2S      - set the pin to be a part of audio
61362306a36Sopenharmony_ci *                             interface in slave mode (DCLK)
61462306a36Sopenharmony_ci *      SI476X_ICON_IC_LINK  - set the pin to be a part of Inter-Chip link
61562306a36Sopenharmony_ci * @icop: - ICOP pin function configuration:
61662306a36Sopenharmony_ci *      SI476X_ICOP_NOOP     - do not modify the behaviour
61762306a36Sopenharmony_ci *      SI476X_ICOP_TRISTATE - put the pin in tristate condition,
61862306a36Sopenharmony_ci *                             enable 1MOhm pulldown
61962306a36Sopenharmony_ci *      SI476X_ICOP_I2S      - set the pin to be a part of audio
62062306a36Sopenharmony_ci *                             interface in slave mode (DOUT)
62162306a36Sopenharmony_ci *                             [Si4761/63/65/67 Only]
62262306a36Sopenharmony_ci *      SI476X_ICOP_IC_LINK  - set the pin to be a part of Inter-Chip link
62362306a36Sopenharmony_ci *
62462306a36Sopenharmony_ci * Function returns 0 on success and negative error code on failure
62562306a36Sopenharmony_ci */
62662306a36Sopenharmony_ciint si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(struct si476x_core *core,
62762306a36Sopenharmony_ci					    enum si476x_icin_config icin,
62862306a36Sopenharmony_ci					    enum si476x_icip_config icip,
62962306a36Sopenharmony_ci					    enum si476x_icon_config icon,
63062306a36Sopenharmony_ci					    enum si476x_icop_config icop)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	u8       resp[CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP];
63362306a36Sopenharmony_ci	const u8 args[CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS] = {
63462306a36Sopenharmony_ci		PIN_CFG_BYTE(icin),
63562306a36Sopenharmony_ci		PIN_CFG_BYTE(icip),
63662306a36Sopenharmony_ci		PIN_CFG_BYTE(icon),
63762306a36Sopenharmony_ci		PIN_CFG_BYTE(icop),
63862306a36Sopenharmony_ci	};
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	return si476x_core_send_command(core, CMD_IC_LINK_GPO_CTL_PIN_CFG,
64162306a36Sopenharmony_ci					args, ARRAY_SIZE(args),
64262306a36Sopenharmony_ci					resp, ARRAY_SIZE(resp),
64362306a36Sopenharmony_ci					SI476X_DEFAULT_TIMEOUT);
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_ic_link_gpo_ctl_pin_cfg);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci/**
64862306a36Sopenharmony_ci * si476x_core_cmd_ana_audio_pin_cfg - send 'ANA_AUDIO_PIN_CFG' to the
64962306a36Sopenharmony_ci * device
65062306a36Sopenharmony_ci * @core: - device to send the command to
65162306a36Sopenharmony_ci * @lrout: - LROUT pin function configuration:
65262306a36Sopenharmony_ci *       SI476X_LROUT_NOOP     - do not modify the behaviour
65362306a36Sopenharmony_ci *       SI476X_LROUT_TRISTATE - put the pin in tristate condition,
65462306a36Sopenharmony_ci *                               enable 1MOhm pulldown
65562306a36Sopenharmony_ci *       SI476X_LROUT_AUDIO    - set pin to be audio output
65662306a36Sopenharmony_ci *       SI476X_LROUT_MPX      - set pin to be MPX output
65762306a36Sopenharmony_ci *
65862306a36Sopenharmony_ci * Function returns 0 on success and negative error code on failure
65962306a36Sopenharmony_ci */
66062306a36Sopenharmony_ciint si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *core,
66162306a36Sopenharmony_ci				      enum si476x_lrout_config lrout)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	u8       resp[CMD_ANA_AUDIO_PIN_CFG_NRESP];
66462306a36Sopenharmony_ci	const u8 args[CMD_ANA_AUDIO_PIN_CFG_NARGS] = {
66562306a36Sopenharmony_ci		PIN_CFG_BYTE(lrout),
66662306a36Sopenharmony_ci	};
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	return si476x_core_send_command(core, CMD_ANA_AUDIO_PIN_CFG,
66962306a36Sopenharmony_ci					args, ARRAY_SIZE(args),
67062306a36Sopenharmony_ci					resp, ARRAY_SIZE(resp),
67162306a36Sopenharmony_ci					SI476X_DEFAULT_TIMEOUT);
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_ana_audio_pin_cfg);
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci/**
67762306a36Sopenharmony_ci * si476x_core_cmd_intb_pin_cfg_a10 - send 'INTB_PIN_CFG' command to the device
67862306a36Sopenharmony_ci * @core: - device to send the command to
67962306a36Sopenharmony_ci * @intb: - INTB pin function configuration:
68062306a36Sopenharmony_ci *      SI476X_INTB_NOOP     - do not modify the behaviour
68162306a36Sopenharmony_ci *      SI476X_INTB_TRISTATE - put the pin in tristate condition,
68262306a36Sopenharmony_ci *                             enable 1MOhm pulldown
68362306a36Sopenharmony_ci *      SI476X_INTB_DAUDIO   - set pin to be a part of digital
68462306a36Sopenharmony_ci *                             audio interface in slave mode
68562306a36Sopenharmony_ci *      SI476X_INTB_IRQ      - set pin to be an interrupt request line
68662306a36Sopenharmony_ci * @a1: - A1 pin function configuration:
68762306a36Sopenharmony_ci *      SI476X_A1_NOOP     - do not modify the behaviour
68862306a36Sopenharmony_ci *      SI476X_A1_TRISTATE - put the pin in tristate condition,
68962306a36Sopenharmony_ci *                           enable 1MOhm pulldown
69062306a36Sopenharmony_ci *      SI476X_A1_IRQ      - set pin to be an interrupt request line
69162306a36Sopenharmony_ci *
69262306a36Sopenharmony_ci * Function returns 0 on success and negative error code on failure
69362306a36Sopenharmony_ci */
69462306a36Sopenharmony_cistatic int si476x_core_cmd_intb_pin_cfg_a10(struct si476x_core *core,
69562306a36Sopenharmony_ci					    enum si476x_intb_config intb,
69662306a36Sopenharmony_ci					    enum si476x_a1_config a1)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	u8       resp[CMD_INTB_PIN_CFG_A10_NRESP];
69962306a36Sopenharmony_ci	const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
70062306a36Sopenharmony_ci		PIN_CFG_BYTE(intb),
70162306a36Sopenharmony_ci		PIN_CFG_BYTE(a1),
70262306a36Sopenharmony_ci	};
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
70562306a36Sopenharmony_ci					args, ARRAY_SIZE(args),
70662306a36Sopenharmony_ci					resp, ARRAY_SIZE(resp),
70762306a36Sopenharmony_ci					SI476X_DEFAULT_TIMEOUT);
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic int si476x_core_cmd_intb_pin_cfg_a20(struct si476x_core *core,
71162306a36Sopenharmony_ci					    enum si476x_intb_config intb,
71262306a36Sopenharmony_ci					    enum si476x_a1_config a1)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	u8       resp[CMD_INTB_PIN_CFG_A20_NRESP];
71562306a36Sopenharmony_ci	const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
71662306a36Sopenharmony_ci		PIN_CFG_BYTE(intb),
71762306a36Sopenharmony_ci		PIN_CFG_BYTE(a1),
71862306a36Sopenharmony_ci	};
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
72162306a36Sopenharmony_ci					args, ARRAY_SIZE(args),
72262306a36Sopenharmony_ci					resp, ARRAY_SIZE(resp),
72362306a36Sopenharmony_ci					SI476X_DEFAULT_TIMEOUT);
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci/**
72962306a36Sopenharmony_ci * si476x_core_cmd_am_rsq_status - send 'AM_RSQ_STATUS' command to the
73062306a36Sopenharmony_ci * device
73162306a36Sopenharmony_ci * @core:  - device to send the command to
73262306a36Sopenharmony_ci * @rsqargs: - pointer to a structure containing a group of sub-args
73362306a36Sopenharmony_ci *             relevant to sending the RSQ status command
73462306a36Sopenharmony_ci * @report: - all signal quality information returned by the command
73562306a36Sopenharmony_ci *           (if NULL then the output of the command is ignored)
73662306a36Sopenharmony_ci *
73762306a36Sopenharmony_ci * Function returns 0 on success and negative error code on failure
73862306a36Sopenharmony_ci */
73962306a36Sopenharmony_ciint si476x_core_cmd_am_rsq_status(struct si476x_core *core,
74062306a36Sopenharmony_ci				  struct si476x_rsq_status_args *rsqargs,
74162306a36Sopenharmony_ci				  struct si476x_rsq_status_report *report)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	int err;
74462306a36Sopenharmony_ci	u8       resp[CMD_AM_RSQ_STATUS_NRESP];
74562306a36Sopenharmony_ci	const u8 args[CMD_AM_RSQ_STATUS_NARGS] = {
74662306a36Sopenharmony_ci		rsqargs->rsqack << 3 | rsqargs->attune << 2 |
74762306a36Sopenharmony_ci		rsqargs->cancel << 1 | rsqargs->stcack,
74862306a36Sopenharmony_ci	};
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	err = si476x_core_send_command(core, CMD_AM_RSQ_STATUS,
75162306a36Sopenharmony_ci				       args, ARRAY_SIZE(args),
75262306a36Sopenharmony_ci				       resp, ARRAY_SIZE(resp),
75362306a36Sopenharmony_ci				       SI476X_DEFAULT_TIMEOUT);
75462306a36Sopenharmony_ci	/*
75562306a36Sopenharmony_ci	 * Besides getting received signal quality information this
75662306a36Sopenharmony_ci	 * command can be used to just acknowledge different interrupt
75762306a36Sopenharmony_ci	 * flags in those cases it is useless to copy and parse
75862306a36Sopenharmony_ci	 * received data so user can pass NULL, and thus avoid
75962306a36Sopenharmony_ci	 * unnecessary copying.
76062306a36Sopenharmony_ci	 */
76162306a36Sopenharmony_ci	if (!report)
76262306a36Sopenharmony_ci		return err;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	report->snrhint		= 0x08 & resp[1];
76562306a36Sopenharmony_ci	report->snrlint		= 0x04 & resp[1];
76662306a36Sopenharmony_ci	report->rssihint	= 0x02 & resp[1];
76762306a36Sopenharmony_ci	report->rssilint	= 0x01 & resp[1];
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	report->bltf		= 0x80 & resp[2];
77062306a36Sopenharmony_ci	report->snr_ready	= 0x20 & resp[2];
77162306a36Sopenharmony_ci	report->rssiready	= 0x08 & resp[2];
77262306a36Sopenharmony_ci	report->afcrl		= 0x02 & resp[2];
77362306a36Sopenharmony_ci	report->valid		= 0x01 & resp[2];
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	report->readfreq	= get_unaligned_be16(resp + 3);
77662306a36Sopenharmony_ci	report->freqoff		= resp[5];
77762306a36Sopenharmony_ci	report->rssi		= resp[6];
77862306a36Sopenharmony_ci	report->snr		= resp[7];
77962306a36Sopenharmony_ci	report->lassi		= resp[9];
78062306a36Sopenharmony_ci	report->hassi		= resp[10];
78162306a36Sopenharmony_ci	report->mult		= resp[11];
78262306a36Sopenharmony_ci	report->dev		= resp[12];
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	return err;
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ciint si476x_core_cmd_fm_acf_status(struct si476x_core *core,
78962306a36Sopenharmony_ci			     struct si476x_acf_status_report *report)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	int err;
79262306a36Sopenharmony_ci	u8       resp[CMD_FM_ACF_STATUS_NRESP];
79362306a36Sopenharmony_ci	const u8 args[CMD_FM_ACF_STATUS_NARGS] = {
79462306a36Sopenharmony_ci		0x0,
79562306a36Sopenharmony_ci	};
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	if (!report)
79862306a36Sopenharmony_ci		return -EINVAL;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	err = si476x_core_send_command(core, CMD_FM_ACF_STATUS,
80162306a36Sopenharmony_ci				       args, ARRAY_SIZE(args),
80262306a36Sopenharmony_ci				       resp, ARRAY_SIZE(resp),
80362306a36Sopenharmony_ci				       SI476X_DEFAULT_TIMEOUT);
80462306a36Sopenharmony_ci	if (err < 0)
80562306a36Sopenharmony_ci		return err;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	report->blend_int	= resp[1] & SI476X_ACF_BLEND_INT;
80862306a36Sopenharmony_ci	report->hblend_int	= resp[1] & SI476X_ACF_HIBLEND_INT;
80962306a36Sopenharmony_ci	report->hicut_int	= resp[1] & SI476X_ACF_HICUT_INT;
81062306a36Sopenharmony_ci	report->chbw_int	= resp[1] & SI476X_ACF_CHBW_INT;
81162306a36Sopenharmony_ci	report->softmute_int	= resp[1] & SI476X_ACF_SOFTMUTE_INT;
81262306a36Sopenharmony_ci	report->smute		= resp[2] & SI476X_ACF_SMUTE;
81362306a36Sopenharmony_ci	report->smattn		= resp[3] & SI476X_ACF_SMATTN;
81462306a36Sopenharmony_ci	report->chbw		= resp[4];
81562306a36Sopenharmony_ci	report->hicut		= resp[5];
81662306a36Sopenharmony_ci	report->hiblend		= resp[6];
81762306a36Sopenharmony_ci	report->pilot		= resp[7] & SI476X_ACF_PILOT;
81862306a36Sopenharmony_ci	report->stblend		= resp[7] & SI476X_ACF_STBLEND;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	return err;
82162306a36Sopenharmony_ci}
82262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ciint si476x_core_cmd_am_acf_status(struct si476x_core *core,
82562306a36Sopenharmony_ci				  struct si476x_acf_status_report *report)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	int err;
82862306a36Sopenharmony_ci	u8       resp[CMD_AM_ACF_STATUS_NRESP];
82962306a36Sopenharmony_ci	const u8 args[CMD_AM_ACF_STATUS_NARGS] = {
83062306a36Sopenharmony_ci		0x0,
83162306a36Sopenharmony_ci	};
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	if (!report)
83462306a36Sopenharmony_ci		return -EINVAL;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	err = si476x_core_send_command(core, CMD_AM_ACF_STATUS,
83762306a36Sopenharmony_ci				       args, ARRAY_SIZE(args),
83862306a36Sopenharmony_ci				       resp, ARRAY_SIZE(resp),
83962306a36Sopenharmony_ci				       SI476X_DEFAULT_TIMEOUT);
84062306a36Sopenharmony_ci	if (err < 0)
84162306a36Sopenharmony_ci		return err;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	report->blend_int	= resp[1] & SI476X_ACF_BLEND_INT;
84462306a36Sopenharmony_ci	report->hblend_int	= resp[1] & SI476X_ACF_HIBLEND_INT;
84562306a36Sopenharmony_ci	report->hicut_int	= resp[1] & SI476X_ACF_HICUT_INT;
84662306a36Sopenharmony_ci	report->chbw_int	= resp[1] & SI476X_ACF_CHBW_INT;
84762306a36Sopenharmony_ci	report->softmute_int	= resp[1] & SI476X_ACF_SOFTMUTE_INT;
84862306a36Sopenharmony_ci	report->smute		= resp[2] & SI476X_ACF_SMUTE;
84962306a36Sopenharmony_ci	report->smattn		= resp[3] & SI476X_ACF_SMATTN;
85062306a36Sopenharmony_ci	report->chbw		= resp[4];
85162306a36Sopenharmony_ci	report->hicut		= resp[5];
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	return err;
85462306a36Sopenharmony_ci}
85562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci/**
85962306a36Sopenharmony_ci * si476x_core_cmd_fm_seek_start - send 'FM_SEEK_START' command to the
86062306a36Sopenharmony_ci * device
86162306a36Sopenharmony_ci * @core:  - device to send the command to
86262306a36Sopenharmony_ci * @seekup: - if set the direction of the search is 'up'
86362306a36Sopenharmony_ci * @wrap:   - if set seek wraps when hitting band limit
86462306a36Sopenharmony_ci *
86562306a36Sopenharmony_ci * This function begins search for a valid station. The station is
86662306a36Sopenharmony_ci * considered valid when 'FM_VALID_SNR_THRESHOLD' and
86762306a36Sopenharmony_ci * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
86862306a36Sopenharmony_ci * are met.
86962306a36Sopenharmony_ci} *
87062306a36Sopenharmony_ci * Function returns 0 on success and negative error code on failure
87162306a36Sopenharmony_ci */
87262306a36Sopenharmony_ciint si476x_core_cmd_fm_seek_start(struct si476x_core *core,
87362306a36Sopenharmony_ci				  bool seekup, bool wrap)
87462306a36Sopenharmony_ci{
87562306a36Sopenharmony_ci	u8       resp[CMD_FM_SEEK_START_NRESP];
87662306a36Sopenharmony_ci	const u8 args[CMD_FM_SEEK_START_NARGS] = {
87762306a36Sopenharmony_ci		seekup << 3 | wrap << 2,
87862306a36Sopenharmony_ci	};
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	return si476x_cmd_tune_seek_freq(core, CMD_FM_SEEK_START,
88162306a36Sopenharmony_ci					 args, sizeof(args),
88262306a36Sopenharmony_ci					 resp, sizeof(resp));
88362306a36Sopenharmony_ci}
88462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci/**
88762306a36Sopenharmony_ci * si476x_core_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the
88862306a36Sopenharmony_ci * device
88962306a36Sopenharmony_ci * @core: - device to send the command to
89062306a36Sopenharmony_ci * @status_only: - if set the data is not removed from RDSFIFO,
89162306a36Sopenharmony_ci *                RDSFIFOUSED is not decremented and data in all the
89262306a36Sopenharmony_ci *                rest RDS data contains the last valid info received
89362306a36Sopenharmony_ci * @mtfifo: if set the command clears RDS receive FIFO
89462306a36Sopenharmony_ci * @intack: if set the command clards the RDSINT bit.
89562306a36Sopenharmony_ci * @report: - all signal quality information returned by the command
89662306a36Sopenharmony_ci *           (if NULL then the output of the command is ignored)
89762306a36Sopenharmony_ci *
89862306a36Sopenharmony_ci * Function returns 0 on success and negative error code on failure
89962306a36Sopenharmony_ci */
90062306a36Sopenharmony_ciint si476x_core_cmd_fm_rds_status(struct si476x_core *core,
90162306a36Sopenharmony_ci				  bool status_only,
90262306a36Sopenharmony_ci				  bool mtfifo,
90362306a36Sopenharmony_ci				  bool intack,
90462306a36Sopenharmony_ci				  struct si476x_rds_status_report *report)
90562306a36Sopenharmony_ci{
90662306a36Sopenharmony_ci	int err;
90762306a36Sopenharmony_ci	u8       resp[CMD_FM_RDS_STATUS_NRESP];
90862306a36Sopenharmony_ci	const u8 args[CMD_FM_RDS_STATUS_NARGS] = {
90962306a36Sopenharmony_ci		status_only << 2 | mtfifo << 1 | intack,
91062306a36Sopenharmony_ci	};
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	err = si476x_core_send_command(core, CMD_FM_RDS_STATUS,
91362306a36Sopenharmony_ci				       args, ARRAY_SIZE(args),
91462306a36Sopenharmony_ci				       resp, ARRAY_SIZE(resp),
91562306a36Sopenharmony_ci				       SI476X_DEFAULT_TIMEOUT);
91662306a36Sopenharmony_ci	/*
91762306a36Sopenharmony_ci	 * Besides getting RDS status information this command can be
91862306a36Sopenharmony_ci	 * used to just acknowledge different interrupt flags in those
91962306a36Sopenharmony_ci	 * cases it is useless to copy and parse received data so user
92062306a36Sopenharmony_ci	 * can pass NULL, and thus avoid unnecessary copying.
92162306a36Sopenharmony_ci	 */
92262306a36Sopenharmony_ci	if (err < 0 || report == NULL)
92362306a36Sopenharmony_ci		return err;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	report->rdstpptyint	= 0x10 & resp[1];
92662306a36Sopenharmony_ci	report->rdspiint	= 0x08 & resp[1];
92762306a36Sopenharmony_ci	report->rdssyncint	= 0x02 & resp[1];
92862306a36Sopenharmony_ci	report->rdsfifoint	= 0x01 & resp[1];
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	report->tpptyvalid	= 0x10 & resp[2];
93162306a36Sopenharmony_ci	report->pivalid		= 0x08 & resp[2];
93262306a36Sopenharmony_ci	report->rdssync		= 0x02 & resp[2];
93362306a36Sopenharmony_ci	report->rdsfifolost	= 0x01 & resp[2];
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	report->tp		= 0x20 & resp[3];
93662306a36Sopenharmony_ci	report->pty		= 0x1f & resp[3];
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	report->pi		= get_unaligned_be16(resp + 4);
93962306a36Sopenharmony_ci	report->rdsfifoused	= resp[6];
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	report->ble[V4L2_RDS_BLOCK_A]	= 0xc0 & resp[7];
94262306a36Sopenharmony_ci	report->ble[V4L2_RDS_BLOCK_B]	= 0x30 & resp[7];
94362306a36Sopenharmony_ci	report->ble[V4L2_RDS_BLOCK_C]	= 0x0c & resp[7];
94462306a36Sopenharmony_ci	report->ble[V4L2_RDS_BLOCK_D]	= 0x03 & resp[7];
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	report->rds[V4L2_RDS_BLOCK_A].block = V4L2_RDS_BLOCK_A;
94762306a36Sopenharmony_ci	report->rds[V4L2_RDS_BLOCK_A].msb = resp[8];
94862306a36Sopenharmony_ci	report->rds[V4L2_RDS_BLOCK_A].lsb = resp[9];
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	report->rds[V4L2_RDS_BLOCK_B].block = V4L2_RDS_BLOCK_B;
95162306a36Sopenharmony_ci	report->rds[V4L2_RDS_BLOCK_B].msb = resp[10];
95262306a36Sopenharmony_ci	report->rds[V4L2_RDS_BLOCK_B].lsb = resp[11];
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	report->rds[V4L2_RDS_BLOCK_C].block = V4L2_RDS_BLOCK_C;
95562306a36Sopenharmony_ci	report->rds[V4L2_RDS_BLOCK_C].msb = resp[12];
95662306a36Sopenharmony_ci	report->rds[V4L2_RDS_BLOCK_C].lsb = resp[13];
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	report->rds[V4L2_RDS_BLOCK_D].block = V4L2_RDS_BLOCK_D;
95962306a36Sopenharmony_ci	report->rds[V4L2_RDS_BLOCK_D].msb = resp[14];
96062306a36Sopenharmony_ci	report->rds[V4L2_RDS_BLOCK_D].lsb = resp[15];
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	return err;
96362306a36Sopenharmony_ci}
96462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ciint si476x_core_cmd_fm_rds_blockcount(struct si476x_core *core,
96762306a36Sopenharmony_ci				bool clear,
96862306a36Sopenharmony_ci				struct si476x_rds_blockcount_report *report)
96962306a36Sopenharmony_ci{
97062306a36Sopenharmony_ci	int err;
97162306a36Sopenharmony_ci	u8       resp[CMD_FM_RDS_BLOCKCOUNT_NRESP];
97262306a36Sopenharmony_ci	const u8 args[CMD_FM_RDS_BLOCKCOUNT_NARGS] = {
97362306a36Sopenharmony_ci		clear,
97462306a36Sopenharmony_ci	};
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	if (!report)
97762306a36Sopenharmony_ci		return -EINVAL;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	err = si476x_core_send_command(core, CMD_FM_RDS_BLOCKCOUNT,
98062306a36Sopenharmony_ci				       args, ARRAY_SIZE(args),
98162306a36Sopenharmony_ci				       resp, ARRAY_SIZE(resp),
98262306a36Sopenharmony_ci				       SI476X_DEFAULT_TIMEOUT);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	if (!err) {
98562306a36Sopenharmony_ci		report->expected	= get_unaligned_be16(resp + 2);
98662306a36Sopenharmony_ci		report->received	= get_unaligned_be16(resp + 4);
98762306a36Sopenharmony_ci		report->uncorrectable	= get_unaligned_be16(resp + 6);
98862306a36Sopenharmony_ci	}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	return err;
99162306a36Sopenharmony_ci}
99262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ciint si476x_core_cmd_fm_phase_diversity(struct si476x_core *core,
99562306a36Sopenharmony_ci				       enum si476x_phase_diversity_mode mode)
99662306a36Sopenharmony_ci{
99762306a36Sopenharmony_ci	u8       resp[CMD_FM_PHASE_DIVERSITY_NRESP];
99862306a36Sopenharmony_ci	const u8 args[CMD_FM_PHASE_DIVERSITY_NARGS] = {
99962306a36Sopenharmony_ci		mode & 0x07,
100062306a36Sopenharmony_ci	};
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	return si476x_core_send_command(core, CMD_FM_PHASE_DIVERSITY,
100362306a36Sopenharmony_ci					args, ARRAY_SIZE(args),
100462306a36Sopenharmony_ci					resp, ARRAY_SIZE(resp),
100562306a36Sopenharmony_ci					SI476X_DEFAULT_TIMEOUT);
100662306a36Sopenharmony_ci}
100762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity);
100862306a36Sopenharmony_ci/**
100962306a36Sopenharmony_ci * si476x_core_cmd_fm_phase_div_status() - get the phase diversity
101062306a36Sopenharmony_ci * status
101162306a36Sopenharmony_ci *
101262306a36Sopenharmony_ci * @core: si476x device
101362306a36Sopenharmony_ci *
101462306a36Sopenharmony_ci * NOTE caller must hold core lock
101562306a36Sopenharmony_ci *
101662306a36Sopenharmony_ci * Function returns the value of the status bit in case of success and
101762306a36Sopenharmony_ci * negative error code in case of failure.
101862306a36Sopenharmony_ci */
101962306a36Sopenharmony_ciint si476x_core_cmd_fm_phase_div_status(struct si476x_core *core)
102062306a36Sopenharmony_ci{
102162306a36Sopenharmony_ci	int err;
102262306a36Sopenharmony_ci	u8 resp[CMD_FM_PHASE_DIV_STATUS_NRESP];
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	err = si476x_core_send_command(core, CMD_FM_PHASE_DIV_STATUS,
102562306a36Sopenharmony_ci				       NULL, 0,
102662306a36Sopenharmony_ci				       resp, ARRAY_SIZE(resp),
102762306a36Sopenharmony_ci				       SI476X_DEFAULT_TIMEOUT);
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	return (err < 0) ? err : resp[1];
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci/**
103562306a36Sopenharmony_ci * si476x_core_cmd_am_seek_start - send 'FM_SEEK_START' command to the
103662306a36Sopenharmony_ci * device
103762306a36Sopenharmony_ci * @core:  - device to send the command to
103862306a36Sopenharmony_ci * @seekup: - if set the direction of the search is 'up'
103962306a36Sopenharmony_ci * @wrap:   - if set seek wraps when hitting band limit
104062306a36Sopenharmony_ci *
104162306a36Sopenharmony_ci * This function begins search for a valid station. The station is
104262306a36Sopenharmony_ci * considered valid when 'FM_VALID_SNR_THRESHOLD' and
104362306a36Sopenharmony_ci * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
104462306a36Sopenharmony_ci * are met.
104562306a36Sopenharmony_ci *
104662306a36Sopenharmony_ci * Function returns 0 on success and negative error code on failure
104762306a36Sopenharmony_ci */
104862306a36Sopenharmony_ciint si476x_core_cmd_am_seek_start(struct si476x_core *core,
104962306a36Sopenharmony_ci				  bool seekup, bool wrap)
105062306a36Sopenharmony_ci{
105162306a36Sopenharmony_ci	u8       resp[CMD_AM_SEEK_START_NRESP];
105262306a36Sopenharmony_ci	const u8 args[CMD_AM_SEEK_START_NARGS] = {
105362306a36Sopenharmony_ci		seekup << 3 | wrap << 2,
105462306a36Sopenharmony_ci	};
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	return si476x_cmd_tune_seek_freq(core,  CMD_AM_SEEK_START,
105762306a36Sopenharmony_ci					 args, sizeof(args),
105862306a36Sopenharmony_ci					 resp, sizeof(resp));
105962306a36Sopenharmony_ci}
106062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start);
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_cistatic int si476x_core_cmd_power_up_a10(struct si476x_core *core,
106562306a36Sopenharmony_ci					struct si476x_power_up_args *puargs)
106662306a36Sopenharmony_ci{
106762306a36Sopenharmony_ci	u8       resp[CMD_POWER_UP_A10_NRESP];
106862306a36Sopenharmony_ci	const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
106962306a36Sopenharmony_ci	const bool ctsen  = (core->client->irq != 0);
107062306a36Sopenharmony_ci	const u8 args[CMD_POWER_UP_A10_NARGS] = {
107162306a36Sopenharmony_ci		0xF7,		/* Reserved, always 0xF7 */
107262306a36Sopenharmony_ci		0x3F & puargs->xcload,	/* First two bits are reserved to be
107362306a36Sopenharmony_ci				 * zeros */
107462306a36Sopenharmony_ci		ctsen << 7 | intsel << 6 | 0x07, /* Last five bits
107562306a36Sopenharmony_ci						   * are reserved to
107662306a36Sopenharmony_ci						   * be written as 0x7 */
107762306a36Sopenharmony_ci		puargs->func << 4 | puargs->freq,
107862306a36Sopenharmony_ci		0x11,		/* Reserved, always 0x11 */
107962306a36Sopenharmony_ci	};
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	return si476x_core_send_command(core, CMD_POWER_UP,
108262306a36Sopenharmony_ci					args, ARRAY_SIZE(args),
108362306a36Sopenharmony_ci					resp, ARRAY_SIZE(resp),
108462306a36Sopenharmony_ci					SI476X_TIMEOUT_POWER_UP);
108562306a36Sopenharmony_ci}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_cistatic int si476x_core_cmd_power_up_a20(struct si476x_core *core,
108862306a36Sopenharmony_ci				 struct si476x_power_up_args *puargs)
108962306a36Sopenharmony_ci{
109062306a36Sopenharmony_ci	u8       resp[CMD_POWER_UP_A20_NRESP];
109162306a36Sopenharmony_ci	const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
109262306a36Sopenharmony_ci	const bool ctsen  = (core->client->irq != 0);
109362306a36Sopenharmony_ci	const u8 args[CMD_POWER_UP_A20_NARGS] = {
109462306a36Sopenharmony_ci		puargs->ibias6x << 7 | puargs->xstart,
109562306a36Sopenharmony_ci		0x3F & puargs->xcload,	/* First two bits are reserved to be
109662306a36Sopenharmony_ci					 * zeros */
109762306a36Sopenharmony_ci		ctsen << 7 | intsel << 6 | puargs->fastboot << 5 |
109862306a36Sopenharmony_ci		puargs->xbiashc << 3 | puargs->xbias,
109962306a36Sopenharmony_ci		puargs->func << 4 | puargs->freq,
110062306a36Sopenharmony_ci		0x10 | puargs->xmode,
110162306a36Sopenharmony_ci	};
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	return si476x_core_send_command(core, CMD_POWER_UP,
110462306a36Sopenharmony_ci					args, ARRAY_SIZE(args),
110562306a36Sopenharmony_ci					resp, ARRAY_SIZE(resp),
110662306a36Sopenharmony_ci					SI476X_TIMEOUT_POWER_UP);
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_cistatic int si476x_core_cmd_power_down_a10(struct si476x_core *core,
111062306a36Sopenharmony_ci					  struct si476x_power_down_args *pdargs)
111162306a36Sopenharmony_ci{
111262306a36Sopenharmony_ci	u8 resp[CMD_POWER_DOWN_A10_NRESP];
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	return si476x_core_send_command(core, CMD_POWER_DOWN,
111562306a36Sopenharmony_ci					NULL, 0,
111662306a36Sopenharmony_ci					resp, ARRAY_SIZE(resp),
111762306a36Sopenharmony_ci					SI476X_DEFAULT_TIMEOUT);
111862306a36Sopenharmony_ci}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_cistatic int si476x_core_cmd_power_down_a20(struct si476x_core *core,
112162306a36Sopenharmony_ci					  struct si476x_power_down_args *pdargs)
112262306a36Sopenharmony_ci{
112362306a36Sopenharmony_ci	u8 resp[CMD_POWER_DOWN_A20_NRESP];
112462306a36Sopenharmony_ci	const u8 args[CMD_POWER_DOWN_A20_NARGS] = {
112562306a36Sopenharmony_ci		pdargs->xosc,
112662306a36Sopenharmony_ci	};
112762306a36Sopenharmony_ci	return si476x_core_send_command(core, CMD_POWER_DOWN,
112862306a36Sopenharmony_ci					args, ARRAY_SIZE(args),
112962306a36Sopenharmony_ci					resp, ARRAY_SIZE(resp),
113062306a36Sopenharmony_ci					SI476X_DEFAULT_TIMEOUT);
113162306a36Sopenharmony_ci}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_cistatic int si476x_core_cmd_am_tune_freq_a10(struct si476x_core *core,
113462306a36Sopenharmony_ci					struct si476x_tune_freq_args *tuneargs)
113562306a36Sopenharmony_ci{
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	const int am_freq = tuneargs->freq;
113862306a36Sopenharmony_ci	u8       resp[CMD_AM_TUNE_FREQ_NRESP];
113962306a36Sopenharmony_ci	const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
114062306a36Sopenharmony_ci		(tuneargs->hd << 6),
114162306a36Sopenharmony_ci		msb(am_freq),
114262306a36Sopenharmony_ci		lsb(am_freq),
114362306a36Sopenharmony_ci	};
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, args,
114662306a36Sopenharmony_ci					 sizeof(args),
114762306a36Sopenharmony_ci					 resp, sizeof(resp));
114862306a36Sopenharmony_ci}
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_cistatic int si476x_core_cmd_am_tune_freq_a20(struct si476x_core *core,
115162306a36Sopenharmony_ci					struct si476x_tune_freq_args *tuneargs)
115262306a36Sopenharmony_ci{
115362306a36Sopenharmony_ci	const int am_freq = tuneargs->freq;
115462306a36Sopenharmony_ci	u8       resp[CMD_AM_TUNE_FREQ_NRESP];
115562306a36Sopenharmony_ci	const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
115662306a36Sopenharmony_ci		(tuneargs->zifsr << 6) | (tuneargs->injside & 0x03),
115762306a36Sopenharmony_ci		msb(am_freq),
115862306a36Sopenharmony_ci		lsb(am_freq),
115962306a36Sopenharmony_ci	};
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ,
116262306a36Sopenharmony_ci					 args, sizeof(args),
116362306a36Sopenharmony_ci					 resp, sizeof(resp));
116462306a36Sopenharmony_ci}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_cistatic int si476x_core_cmd_fm_rsq_status_a10(struct si476x_core *core,
116762306a36Sopenharmony_ci					struct si476x_rsq_status_args *rsqargs,
116862306a36Sopenharmony_ci					struct si476x_rsq_status_report *report)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	int err;
117162306a36Sopenharmony_ci	u8       resp[CMD_FM_RSQ_STATUS_A10_NRESP];
117262306a36Sopenharmony_ci	const u8 args[CMD_FM_RSQ_STATUS_A10_NARGS] = {
117362306a36Sopenharmony_ci		rsqargs->rsqack << 3 | rsqargs->attune << 2 |
117462306a36Sopenharmony_ci		rsqargs->cancel << 1 | rsqargs->stcack,
117562306a36Sopenharmony_ci	};
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
117862306a36Sopenharmony_ci				       args, ARRAY_SIZE(args),
117962306a36Sopenharmony_ci				       resp, ARRAY_SIZE(resp),
118062306a36Sopenharmony_ci				       SI476X_DEFAULT_TIMEOUT);
118162306a36Sopenharmony_ci	/*
118262306a36Sopenharmony_ci	 * Besides getting received signal quality information this
118362306a36Sopenharmony_ci	 * command can be used to just acknowledge different interrupt
118462306a36Sopenharmony_ci	 * flags in those cases it is useless to copy and parse
118562306a36Sopenharmony_ci	 * received data so user can pass NULL, and thus avoid
118662306a36Sopenharmony_ci	 * unnecessary copying.
118762306a36Sopenharmony_ci	 */
118862306a36Sopenharmony_ci	if (err < 0 || report == NULL)
118962306a36Sopenharmony_ci		return err;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	report->multhint	= 0x80 & resp[1];
119262306a36Sopenharmony_ci	report->multlint	= 0x40 & resp[1];
119362306a36Sopenharmony_ci	report->snrhint		= 0x08 & resp[1];
119462306a36Sopenharmony_ci	report->snrlint		= 0x04 & resp[1];
119562306a36Sopenharmony_ci	report->rssihint	= 0x02 & resp[1];
119662306a36Sopenharmony_ci	report->rssilint	= 0x01 & resp[1];
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	report->bltf		= 0x80 & resp[2];
119962306a36Sopenharmony_ci	report->snr_ready	= 0x20 & resp[2];
120062306a36Sopenharmony_ci	report->rssiready	= 0x08 & resp[2];
120162306a36Sopenharmony_ci	report->afcrl		= 0x02 & resp[2];
120262306a36Sopenharmony_ci	report->valid		= 0x01 & resp[2];
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	report->readfreq	= get_unaligned_be16(resp + 3);
120562306a36Sopenharmony_ci	report->freqoff		= resp[5];
120662306a36Sopenharmony_ci	report->rssi		= resp[6];
120762306a36Sopenharmony_ci	report->snr		= resp[7];
120862306a36Sopenharmony_ci	report->lassi		= resp[9];
120962306a36Sopenharmony_ci	report->hassi		= resp[10];
121062306a36Sopenharmony_ci	report->mult		= resp[11];
121162306a36Sopenharmony_ci	report->dev		= resp[12];
121262306a36Sopenharmony_ci	report->readantcap	= get_unaligned_be16(resp + 13);
121362306a36Sopenharmony_ci	report->assi		= resp[15];
121462306a36Sopenharmony_ci	report->usn		= resp[16];
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	return err;
121762306a36Sopenharmony_ci}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_cistatic int si476x_core_cmd_fm_rsq_status_a20(struct si476x_core *core,
122062306a36Sopenharmony_ci				     struct si476x_rsq_status_args *rsqargs,
122162306a36Sopenharmony_ci				     struct si476x_rsq_status_report *report)
122262306a36Sopenharmony_ci{
122362306a36Sopenharmony_ci	int err;
122462306a36Sopenharmony_ci	u8       resp[CMD_FM_RSQ_STATUS_A10_NRESP];
122562306a36Sopenharmony_ci	const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
122662306a36Sopenharmony_ci		rsqargs->primary << 4 | rsqargs->rsqack << 3 |
122762306a36Sopenharmony_ci		rsqargs->attune  << 2 | rsqargs->cancel << 1 |
122862306a36Sopenharmony_ci		rsqargs->stcack,
122962306a36Sopenharmony_ci	};
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
123262306a36Sopenharmony_ci				       args, ARRAY_SIZE(args),
123362306a36Sopenharmony_ci				       resp, ARRAY_SIZE(resp),
123462306a36Sopenharmony_ci				       SI476X_DEFAULT_TIMEOUT);
123562306a36Sopenharmony_ci	/*
123662306a36Sopenharmony_ci	 * Besides getting received signal quality information this
123762306a36Sopenharmony_ci	 * command can be used to just acknowledge different interrupt
123862306a36Sopenharmony_ci	 * flags in those cases it is useless to copy and parse
123962306a36Sopenharmony_ci	 * received data so user can pass NULL, and thus avoid
124062306a36Sopenharmony_ci	 * unnecessary copying.
124162306a36Sopenharmony_ci	 */
124262306a36Sopenharmony_ci	if (err < 0 || report == NULL)
124362306a36Sopenharmony_ci		return err;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	report->multhint	= 0x80 & resp[1];
124662306a36Sopenharmony_ci	report->multlint	= 0x40 & resp[1];
124762306a36Sopenharmony_ci	report->snrhint		= 0x08 & resp[1];
124862306a36Sopenharmony_ci	report->snrlint		= 0x04 & resp[1];
124962306a36Sopenharmony_ci	report->rssihint	= 0x02 & resp[1];
125062306a36Sopenharmony_ci	report->rssilint	= 0x01 & resp[1];
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	report->bltf		= 0x80 & resp[2];
125362306a36Sopenharmony_ci	report->snr_ready	= 0x20 & resp[2];
125462306a36Sopenharmony_ci	report->rssiready	= 0x08 & resp[2];
125562306a36Sopenharmony_ci	report->afcrl		= 0x02 & resp[2];
125662306a36Sopenharmony_ci	report->valid		= 0x01 & resp[2];
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	report->readfreq	= get_unaligned_be16(resp + 3);
125962306a36Sopenharmony_ci	report->freqoff		= resp[5];
126062306a36Sopenharmony_ci	report->rssi		= resp[6];
126162306a36Sopenharmony_ci	report->snr		= resp[7];
126262306a36Sopenharmony_ci	report->lassi		= resp[9];
126362306a36Sopenharmony_ci	report->hassi		= resp[10];
126462306a36Sopenharmony_ci	report->mult		= resp[11];
126562306a36Sopenharmony_ci	report->dev		= resp[12];
126662306a36Sopenharmony_ci	report->readantcap	= get_unaligned_be16(resp + 13);
126762306a36Sopenharmony_ci	report->assi		= resp[15];
126862306a36Sopenharmony_ci	report->usn		= resp[16];
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	return err;
127162306a36Sopenharmony_ci}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_cistatic int si476x_core_cmd_fm_rsq_status_a30(struct si476x_core *core,
127562306a36Sopenharmony_ci					struct si476x_rsq_status_args *rsqargs,
127662306a36Sopenharmony_ci					struct si476x_rsq_status_report *report)
127762306a36Sopenharmony_ci{
127862306a36Sopenharmony_ci	int err;
127962306a36Sopenharmony_ci	u8       resp[CMD_FM_RSQ_STATUS_A30_NRESP];
128062306a36Sopenharmony_ci	const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
128162306a36Sopenharmony_ci		rsqargs->primary << 4 | rsqargs->rsqack << 3 |
128262306a36Sopenharmony_ci		rsqargs->attune << 2 | rsqargs->cancel << 1 |
128362306a36Sopenharmony_ci		rsqargs->stcack,
128462306a36Sopenharmony_ci	};
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
128762306a36Sopenharmony_ci				       args, ARRAY_SIZE(args),
128862306a36Sopenharmony_ci				       resp, ARRAY_SIZE(resp),
128962306a36Sopenharmony_ci				       SI476X_DEFAULT_TIMEOUT);
129062306a36Sopenharmony_ci	/*
129162306a36Sopenharmony_ci	 * Besides getting received signal quality information this
129262306a36Sopenharmony_ci	 * command can be used to just acknowledge different interrupt
129362306a36Sopenharmony_ci	 * flags in those cases it is useless to copy and parse
129462306a36Sopenharmony_ci	 * received data so user can pass NULL, and thus avoid
129562306a36Sopenharmony_ci	 * unnecessary copying.
129662306a36Sopenharmony_ci	 */
129762306a36Sopenharmony_ci	if (err < 0 || report == NULL)
129862306a36Sopenharmony_ci		return err;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	report->multhint	= 0x80 & resp[1];
130162306a36Sopenharmony_ci	report->multlint	= 0x40 & resp[1];
130262306a36Sopenharmony_ci	report->snrhint		= 0x08 & resp[1];
130362306a36Sopenharmony_ci	report->snrlint		= 0x04 & resp[1];
130462306a36Sopenharmony_ci	report->rssihint	= 0x02 & resp[1];
130562306a36Sopenharmony_ci	report->rssilint	= 0x01 & resp[1];
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	report->bltf		= 0x80 & resp[2];
130862306a36Sopenharmony_ci	report->snr_ready	= 0x20 & resp[2];
130962306a36Sopenharmony_ci	report->rssiready	= 0x08 & resp[2];
131062306a36Sopenharmony_ci	report->injside         = 0x04 & resp[2];
131162306a36Sopenharmony_ci	report->afcrl		= 0x02 & resp[2];
131262306a36Sopenharmony_ci	report->valid		= 0x01 & resp[2];
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	report->readfreq	= get_unaligned_be16(resp + 3);
131562306a36Sopenharmony_ci	report->freqoff		= resp[5];
131662306a36Sopenharmony_ci	report->rssi		= resp[6];
131762306a36Sopenharmony_ci	report->snr		= resp[7];
131862306a36Sopenharmony_ci	report->issi		= resp[8];
131962306a36Sopenharmony_ci	report->lassi		= resp[9];
132062306a36Sopenharmony_ci	report->hassi		= resp[10];
132162306a36Sopenharmony_ci	report->mult		= resp[11];
132262306a36Sopenharmony_ci	report->dev		= resp[12];
132362306a36Sopenharmony_ci	report->readantcap	= get_unaligned_be16(resp + 13);
132462306a36Sopenharmony_ci	report->assi		= resp[15];
132562306a36Sopenharmony_ci	report->usn		= resp[16];
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	report->pilotdev	= resp[17];
132862306a36Sopenharmony_ci	report->rdsdev		= resp[18];
132962306a36Sopenharmony_ci	report->assidev		= resp[19];
133062306a36Sopenharmony_ci	report->strongdev	= resp[20];
133162306a36Sopenharmony_ci	report->rdspi		= get_unaligned_be16(resp + 21);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	return err;
133462306a36Sopenharmony_ci}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_cistatic int si476x_core_cmd_fm_tune_freq_a10(struct si476x_core *core,
133762306a36Sopenharmony_ci					struct si476x_tune_freq_args *tuneargs)
133862306a36Sopenharmony_ci{
133962306a36Sopenharmony_ci	u8       resp[CMD_FM_TUNE_FREQ_NRESP];
134062306a36Sopenharmony_ci	const u8 args[CMD_FM_TUNE_FREQ_A10_NARGS] = {
134162306a36Sopenharmony_ci		(tuneargs->hd << 6) | (tuneargs->tunemode << 4)
134262306a36Sopenharmony_ci		| (tuneargs->smoothmetrics << 2),
134362306a36Sopenharmony_ci		msb(tuneargs->freq),
134462306a36Sopenharmony_ci		lsb(tuneargs->freq),
134562306a36Sopenharmony_ci		msb(tuneargs->antcap),
134662306a36Sopenharmony_ci		lsb(tuneargs->antcap)
134762306a36Sopenharmony_ci	};
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
135062306a36Sopenharmony_ci					 args, sizeof(args),
135162306a36Sopenharmony_ci					 resp, sizeof(resp));
135262306a36Sopenharmony_ci}
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_cistatic int si476x_core_cmd_fm_tune_freq_a20(struct si476x_core *core,
135562306a36Sopenharmony_ci					struct si476x_tune_freq_args *tuneargs)
135662306a36Sopenharmony_ci{
135762306a36Sopenharmony_ci	u8       resp[CMD_FM_TUNE_FREQ_NRESP];
135862306a36Sopenharmony_ci	const u8 args[CMD_FM_TUNE_FREQ_A20_NARGS] = {
135962306a36Sopenharmony_ci		(tuneargs->hd << 6) | (tuneargs->tunemode << 4)
136062306a36Sopenharmony_ci		|  (tuneargs->smoothmetrics << 2) | (tuneargs->injside),
136162306a36Sopenharmony_ci		msb(tuneargs->freq),
136262306a36Sopenharmony_ci		lsb(tuneargs->freq),
136362306a36Sopenharmony_ci	};
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
136662306a36Sopenharmony_ci					 args, sizeof(args),
136762306a36Sopenharmony_ci					 resp, sizeof(resp));
136862306a36Sopenharmony_ci}
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_cistatic int si476x_core_cmd_agc_status_a20(struct si476x_core *core,
137162306a36Sopenharmony_ci					struct si476x_agc_status_report *report)
137262306a36Sopenharmony_ci{
137362306a36Sopenharmony_ci	int err;
137462306a36Sopenharmony_ci	u8 resp[CMD_AGC_STATUS_NRESP_A20];
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	if (!report)
137762306a36Sopenharmony_ci		return -EINVAL;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	err = si476x_core_send_command(core, CMD_AGC_STATUS,
138062306a36Sopenharmony_ci				       NULL, 0,
138162306a36Sopenharmony_ci				       resp, ARRAY_SIZE(resp),
138262306a36Sopenharmony_ci				       SI476X_DEFAULT_TIMEOUT);
138362306a36Sopenharmony_ci	if (err < 0)
138462306a36Sopenharmony_ci		return err;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	report->mxhi		= resp[1] & SI476X_AGC_MXHI;
138762306a36Sopenharmony_ci	report->mxlo		= resp[1] & SI476X_AGC_MXLO;
138862306a36Sopenharmony_ci	report->lnahi		= resp[1] & SI476X_AGC_LNAHI;
138962306a36Sopenharmony_ci	report->lnalo		= resp[1] & SI476X_AGC_LNALO;
139062306a36Sopenharmony_ci	report->fmagc1		= resp[2];
139162306a36Sopenharmony_ci	report->fmagc2		= resp[3];
139262306a36Sopenharmony_ci	report->pgagain		= resp[4];
139362306a36Sopenharmony_ci	report->fmwblang	= resp[5];
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	return err;
139662306a36Sopenharmony_ci}
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_cistatic int si476x_core_cmd_agc_status_a10(struct si476x_core *core,
139962306a36Sopenharmony_ci					struct si476x_agc_status_report *report)
140062306a36Sopenharmony_ci{
140162306a36Sopenharmony_ci	int err;
140262306a36Sopenharmony_ci	u8 resp[CMD_AGC_STATUS_NRESP_A10];
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	if (!report)
140562306a36Sopenharmony_ci		return -EINVAL;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	err = si476x_core_send_command(core, CMD_AGC_STATUS,
140862306a36Sopenharmony_ci				       NULL, 0,
140962306a36Sopenharmony_ci				       resp, ARRAY_SIZE(resp),
141062306a36Sopenharmony_ci				       SI476X_DEFAULT_TIMEOUT);
141162306a36Sopenharmony_ci	if (err < 0)
141262306a36Sopenharmony_ci		return err;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	report->mxhi	= resp[1] & SI476X_AGC_MXHI;
141562306a36Sopenharmony_ci	report->mxlo	= resp[1] & SI476X_AGC_MXLO;
141662306a36Sopenharmony_ci	report->lnahi	= resp[1] & SI476X_AGC_LNAHI;
141762306a36Sopenharmony_ci	report->lnalo	= resp[1] & SI476X_AGC_LNALO;
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	return err;
142062306a36Sopenharmony_ci}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_citypedef int (*tune_freq_func_t) (struct si476x_core *core,
142362306a36Sopenharmony_ci				 struct si476x_tune_freq_args *tuneargs);
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_cistatic struct {
142662306a36Sopenharmony_ci	int (*power_up)(struct si476x_core *,
142762306a36Sopenharmony_ci			struct si476x_power_up_args *);
142862306a36Sopenharmony_ci	int (*power_down)(struct si476x_core *,
142962306a36Sopenharmony_ci			  struct si476x_power_down_args *);
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	tune_freq_func_t fm_tune_freq;
143262306a36Sopenharmony_ci	tune_freq_func_t am_tune_freq;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	int (*fm_rsq_status)(struct si476x_core *,
143562306a36Sopenharmony_ci			     struct si476x_rsq_status_args *,
143662306a36Sopenharmony_ci			     struct si476x_rsq_status_report *);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	int (*agc_status)(struct si476x_core *,
143962306a36Sopenharmony_ci			  struct si476x_agc_status_report *);
144062306a36Sopenharmony_ci	int (*intb_pin_cfg)(struct si476x_core *core,
144162306a36Sopenharmony_ci			    enum si476x_intb_config intb,
144262306a36Sopenharmony_ci			    enum si476x_a1_config a1);
144362306a36Sopenharmony_ci} si476x_cmds_vtable[] = {
144462306a36Sopenharmony_ci	[SI476X_REVISION_A10] = {
144562306a36Sopenharmony_ci		.power_up	= si476x_core_cmd_power_up_a10,
144662306a36Sopenharmony_ci		.power_down	= si476x_core_cmd_power_down_a10,
144762306a36Sopenharmony_ci		.fm_tune_freq	= si476x_core_cmd_fm_tune_freq_a10,
144862306a36Sopenharmony_ci		.am_tune_freq	= si476x_core_cmd_am_tune_freq_a10,
144962306a36Sopenharmony_ci		.fm_rsq_status	= si476x_core_cmd_fm_rsq_status_a10,
145062306a36Sopenharmony_ci		.agc_status	= si476x_core_cmd_agc_status_a10,
145162306a36Sopenharmony_ci		.intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a10,
145262306a36Sopenharmony_ci	},
145362306a36Sopenharmony_ci	[SI476X_REVISION_A20] = {
145462306a36Sopenharmony_ci		.power_up	= si476x_core_cmd_power_up_a20,
145562306a36Sopenharmony_ci		.power_down	= si476x_core_cmd_power_down_a20,
145662306a36Sopenharmony_ci		.fm_tune_freq	= si476x_core_cmd_fm_tune_freq_a20,
145762306a36Sopenharmony_ci		.am_tune_freq	= si476x_core_cmd_am_tune_freq_a20,
145862306a36Sopenharmony_ci		.fm_rsq_status	= si476x_core_cmd_fm_rsq_status_a20,
145962306a36Sopenharmony_ci		.agc_status	= si476x_core_cmd_agc_status_a20,
146062306a36Sopenharmony_ci		.intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a20,
146162306a36Sopenharmony_ci	},
146262306a36Sopenharmony_ci	[SI476X_REVISION_A30] = {
146362306a36Sopenharmony_ci		.power_up	= si476x_core_cmd_power_up_a20,
146462306a36Sopenharmony_ci		.power_down	= si476x_core_cmd_power_down_a20,
146562306a36Sopenharmony_ci		.fm_tune_freq	= si476x_core_cmd_fm_tune_freq_a20,
146662306a36Sopenharmony_ci		.am_tune_freq	= si476x_core_cmd_am_tune_freq_a20,
146762306a36Sopenharmony_ci		.fm_rsq_status	= si476x_core_cmd_fm_rsq_status_a30,
146862306a36Sopenharmony_ci		.agc_status	= si476x_core_cmd_agc_status_a20,
146962306a36Sopenharmony_ci		.intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a20,
147062306a36Sopenharmony_ci	},
147162306a36Sopenharmony_ci};
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ciint si476x_core_cmd_power_up(struct si476x_core *core,
147462306a36Sopenharmony_ci			     struct si476x_power_up_args *args)
147562306a36Sopenharmony_ci{
147662306a36Sopenharmony_ci	BUG_ON(core->revision > SI476X_REVISION_A30 ||
147762306a36Sopenharmony_ci	       core->revision == -1);
147862306a36Sopenharmony_ci	return si476x_cmds_vtable[core->revision].power_up(core, args);
147962306a36Sopenharmony_ci}
148062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_power_up);
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ciint si476x_core_cmd_power_down(struct si476x_core *core,
148362306a36Sopenharmony_ci			       struct si476x_power_down_args *args)
148462306a36Sopenharmony_ci{
148562306a36Sopenharmony_ci	BUG_ON(core->revision > SI476X_REVISION_A30 ||
148662306a36Sopenharmony_ci	       core->revision == -1);
148762306a36Sopenharmony_ci	return si476x_cmds_vtable[core->revision].power_down(core, args);
148862306a36Sopenharmony_ci}
148962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_power_down);
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ciint si476x_core_cmd_fm_tune_freq(struct si476x_core *core,
149262306a36Sopenharmony_ci				 struct si476x_tune_freq_args *args)
149362306a36Sopenharmony_ci{
149462306a36Sopenharmony_ci	BUG_ON(core->revision > SI476X_REVISION_A30 ||
149562306a36Sopenharmony_ci	       core->revision == -1);
149662306a36Sopenharmony_ci	return si476x_cmds_vtable[core->revision].fm_tune_freq(core, args);
149762306a36Sopenharmony_ci}
149862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq);
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ciint si476x_core_cmd_am_tune_freq(struct si476x_core *core,
150162306a36Sopenharmony_ci				 struct si476x_tune_freq_args *args)
150262306a36Sopenharmony_ci{
150362306a36Sopenharmony_ci	BUG_ON(core->revision > SI476X_REVISION_A30 ||
150462306a36Sopenharmony_ci	       core->revision == -1);
150562306a36Sopenharmony_ci	return si476x_cmds_vtable[core->revision].am_tune_freq(core, args);
150662306a36Sopenharmony_ci}
150762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq);
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ciint si476x_core_cmd_fm_rsq_status(struct si476x_core *core,
151062306a36Sopenharmony_ci				  struct si476x_rsq_status_args *args,
151162306a36Sopenharmony_ci				  struct si476x_rsq_status_report *report)
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci{
151462306a36Sopenharmony_ci	BUG_ON(core->revision > SI476X_REVISION_A30 ||
151562306a36Sopenharmony_ci	       core->revision == -1);
151662306a36Sopenharmony_ci	return si476x_cmds_vtable[core->revision].fm_rsq_status(core, args,
151762306a36Sopenharmony_ci								report);
151862306a36Sopenharmony_ci}
151962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status);
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ciint si476x_core_cmd_agc_status(struct si476x_core *core,
152262306a36Sopenharmony_ci				  struct si476x_agc_status_report *report)
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci{
152562306a36Sopenharmony_ci	BUG_ON(core->revision > SI476X_REVISION_A30 ||
152662306a36Sopenharmony_ci	       core->revision == -1);
152762306a36Sopenharmony_ci	return si476x_cmds_vtable[core->revision].agc_status(core, report);
152862306a36Sopenharmony_ci}
152962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status);
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ciint si476x_core_cmd_intb_pin_cfg(struct si476x_core *core,
153262306a36Sopenharmony_ci			    enum si476x_intb_config intb,
153362306a36Sopenharmony_ci			    enum si476x_a1_config a1)
153462306a36Sopenharmony_ci{
153562306a36Sopenharmony_ci	BUG_ON(core->revision > SI476X_REVISION_A30 ||
153662306a36Sopenharmony_ci	       core->revision == -1);
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	return si476x_cmds_vtable[core->revision].intb_pin_cfg(core, intb, a1);
153962306a36Sopenharmony_ci}
154062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg);
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
154362306a36Sopenharmony_ciMODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
154462306a36Sopenharmony_ciMODULE_DESCRIPTION("API for command exchange for si476x");
1545