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