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