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