xref: /openbmc/linux/drivers/mfd/si476x-cmd.c (revision aa0dc6a7)
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  * @resp:     buffer to place the expected response from the device
250  * @respn:    actual size of @resp
251  * @usecs:    amount of time to wait before reading the response (in
252  *            usecs)
253  *
254  * Function returns 0 on succsess and negative error code on
255  * failure
256  */
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_core_cmd_func_info() - send 'FUNC_INFO' command to the device
394  * @core: device to send the command to
395  * @info:  struct si476x_func_info to fill all the information
396  *         returned by the command
397  *
398  * The command requests the firmware and patch version for currently
399  * loaded firmware (dependent on the function of the device FM/AM/WB)
400  *
401  * Function returns 0 on succsess and negative error code on
402  * failure
403  */
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_core_cmd_set_property() - send 'SET_PROPERTY' command to the device
428  * @core:    device to send the command to
429  * @property: property address
430  * @value:    property value
431  *
432  * Function returns 0 on succsess and negative error code on
433  * failure
434  */
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_core_cmd_get_property() - send 'GET_PROPERTY' command to the device
456  * @core:    device to send the command to
457  * @property: property address
458  *
459  * Function return the value of property as u16 on success or a
460  * negative error on failure
461  */
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_core_cmd_dig_audio_pin_cfg() - send 'DIG_AUDIO_PIN_CFG' command to
485  * the device
486  * @core: device to send the command to
487  * @dclk:  DCLK pin function configuration:
488  *	   #SI476X_DCLK_NOOP     - do not modify the behaviour
489  *         #SI476X_DCLK_TRISTATE - put the pin in tristate condition,
490  *                                 enable 1MOhm pulldown
491  *         #SI476X_DCLK_DAUDIO   - set the pin to be a part of digital
492  *                                 audio interface
493  * @dfs:   DFS pin function configuration:
494  *         #SI476X_DFS_NOOP      - do not modify the behaviour
495  *         #SI476X_DFS_TRISTATE  - put the pin in tristate condition,
496  *                             enable 1MOhm pulldown
497  *      SI476X_DFS_DAUDIO    - set the pin to be a part of digital
498  *                             audio interface
499  * @dout: - DOUT pin function configuration:
500  *      SI476X_DOUT_NOOP       - do not modify the behaviour
501  *      SI476X_DOUT_TRISTATE   - put the pin in tristate condition,
502  *                               enable 1MOhm pulldown
503  *      SI476X_DOUT_I2S_OUTPUT - set this pin to be digital out on I2S
504  *                               port 1
505  *      SI476X_DOUT_I2S_INPUT  - set this pin to be digital in on I2S
506  *                               port 1
507  * @xout: - XOUT pin function configuration:
508  *	SI476X_XOUT_NOOP        - do not modify the behaviour
509  *      SI476X_XOUT_TRISTATE    - put the pin in tristate condition,
510  *                                enable 1MOhm pulldown
511  *      SI476X_XOUT_I2S_INPUT   - set this pin to be digital in on I2S
512  *                                port 1
513  *      SI476X_XOUT_MODE_SELECT - set this pin to be the input that
514  *                                selects the mode of the I2S audio
515  *                                combiner (analog or HD)
516  *                                [SI4761/63/65/67 Only]
517  *
518  * Function returns 0 on success and negative error code on failure
519  */
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_core_cmd_zif_pin_cfg - send 'ZIF_PIN_CFG_COMMAND'
543  * @core: - device to send the command to
544  * @iqclk: - IQCL pin function configuration:
545  *       SI476X_IQCLK_NOOP     - do not modify the behaviour
546  *       SI476X_IQCLK_TRISTATE - put the pin in tristate condition,
547  *                               enable 1MOhm pulldown
548  *       SI476X_IQCLK_IQ       - set pin to be a part of I/Q interace
549  *                               in master mode
550  * @iqfs: - IQFS pin function configuration:
551  *       SI476X_IQFS_NOOP     - do not modify the behaviour
552  *       SI476X_IQFS_TRISTATE - put the pin in tristate condition,
553  *                              enable 1MOhm pulldown
554  *       SI476X_IQFS_IQ       - set pin to be a part of I/Q interace
555  *                              in master mode
556  * @iout: - IOUT pin function configuration:
557  *       SI476X_IOUT_NOOP     - do not modify the behaviour
558  *       SI476X_IOUT_TRISTATE - put the pin in tristate condition,
559  *                              enable 1MOhm pulldown
560  *       SI476X_IOUT_OUTPUT   - set pin to be I out
561  * @qout: - QOUT pin function configuration:
562  *       SI476X_QOUT_NOOP     - do not modify the behaviour
563  *       SI476X_QOUT_TRISTATE - put the pin in tristate condition,
564  *                              enable 1MOhm pulldown
565  *       SI476X_QOUT_OUTPUT   - set pin to be Q out
566  *
567  * Function returns 0 on success and negative error code on failure
568  */
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_core_cmd_ic_link_gpo_ctl_pin_cfg - send
592  * 'IC_LINK_GPIO_CTL_PIN_CFG' comand to the device
593  * @core: - device to send the command to
594  * @icin: - ICIN pin function configuration:
595  *      SI476X_ICIN_NOOP      - do not modify the behaviour
596  *      SI476X_ICIN_TRISTATE  - put the pin in tristate condition,
597  *                              enable 1MOhm pulldown
598  *      SI476X_ICIN_GPO1_HIGH - set pin to be an output, drive it high
599  *      SI476X_ICIN_GPO1_LOW  - set pin to be an output, drive it low
600  *      SI476X_ICIN_IC_LINK   - set the pin to be a part of Inter-Chip link
601  * @icip: - ICIP pin function configuration:
602  *      SI476X_ICIP_NOOP      - do not modify the behaviour
603  *      SI476X_ICIP_TRISTATE  - put the pin in tristate condition,
604  *                              enable 1MOhm pulldown
605  *      SI476X_ICIP_GPO1_HIGH - set pin to be an output, drive it high
606  *      SI476X_ICIP_GPO1_LOW  - set pin to be an output, drive it low
607  *      SI476X_ICIP_IC_LINK   - set the pin to be a part of Inter-Chip link
608  * @icon: - ICON pin function configuration:
609  *      SI476X_ICON_NOOP     - do not modify the behaviour
610  *      SI476X_ICON_TRISTATE - put the pin in tristate condition,
611  *                             enable 1MOhm pulldown
612  *      SI476X_ICON_I2S      - set the pin to be a part of audio
613  *                             interface in slave mode (DCLK)
614  *      SI476X_ICON_IC_LINK  - set the pin to be a part of Inter-Chip link
615  * @icop: - ICOP pin function configuration:
616  *      SI476X_ICOP_NOOP     - do not modify the behaviour
617  *      SI476X_ICOP_TRISTATE - put the pin in tristate condition,
618  *                             enable 1MOhm pulldown
619  *      SI476X_ICOP_I2S      - set the pin to be a part of audio
620  *                             interface in slave mode (DOUT)
621  *                             [Si4761/63/65/67 Only]
622  *      SI476X_ICOP_IC_LINK  - set the pin to be a part of Inter-Chip link
623  *
624  * Function returns 0 on success and negative error code on failure
625  */
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_core_cmd_ana_audio_pin_cfg - send 'ANA_AUDIO_PIN_CFG' to the
649  * device
650  * @core: - device to send the command to
651  * @lrout: - LROUT pin function configuration:
652  *       SI476X_LROUT_NOOP     - do not modify the behaviour
653  *       SI476X_LROUT_TRISTATE - put the pin in tristate condition,
654  *                               enable 1MOhm pulldown
655  *       SI476X_LROUT_AUDIO    - set pin to be audio output
656  *       SI476X_LROUT_MPX      - set pin to be MPX output
657  *
658  * Function returns 0 on success and negative error code on failure
659  */
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_core_cmd_intb_pin_cfg_a10 - send 'INTB_PIN_CFG' command to the device
678  * @core: - device to send the command to
679  * @intb: - INTB pin function configuration:
680  *      SI476X_INTB_NOOP     - do not modify the behaviour
681  *      SI476X_INTB_TRISTATE - put the pin in tristate condition,
682  *                             enable 1MOhm pulldown
683  *      SI476X_INTB_DAUDIO   - set pin to be a part of digital
684  *                             audio interface in slave mode
685  *      SI476X_INTB_IRQ      - set pin to be an interrupt request line
686  * @a1: - A1 pin function configuration:
687  *      SI476X_A1_NOOP     - do not modify the behaviour
688  *      SI476X_A1_TRISTATE - put the pin in tristate condition,
689  *                           enable 1MOhm pulldown
690  *      SI476X_A1_IRQ      - set pin to be an interrupt request line
691  *
692  * Function returns 0 on success and negative error code on failure
693  */
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_core_cmd_am_rsq_status - send 'AM_RSQ_STATUS' command to the
730  * device
731  * @core:  - device to send the command to
732  * @rsqargs: - pointer to a structure containing a group of sub-args
733  *             relevant to sending the RSQ status command
734  * @report: - all signal quality information returned by the command
735  *           (if NULL then the output of the command is ignored)
736  *
737  * Function returns 0 on success and negative error code on failure
738  */
739 int si476x_core_cmd_am_rsq_status(struct si476x_core *core,
740 				  struct si476x_rsq_status_args *rsqargs,
741 				  struct si476x_rsq_status_report *report)
742 {
743 	int err;
744 	u8       resp[CMD_AM_RSQ_STATUS_NRESP];
745 	const u8 args[CMD_AM_RSQ_STATUS_NARGS] = {
746 		rsqargs->rsqack << 3 | rsqargs->attune << 2 |
747 		rsqargs->cancel << 1 | rsqargs->stcack,
748 	};
749 
750 	err = si476x_core_send_command(core, CMD_AM_RSQ_STATUS,
751 				       args, ARRAY_SIZE(args),
752 				       resp, ARRAY_SIZE(resp),
753 				       SI476X_DEFAULT_TIMEOUT);
754 	/*
755 	 * Besides getting received signal quality information this
756 	 * command can be used to just acknowledge different interrupt
757 	 * flags in those cases it is useless to copy and parse
758 	 * received data so user can pass NULL, and thus avoid
759 	 * unnecessary copying.
760 	 */
761 	if (!report)
762 		return err;
763 
764 	report->snrhint		= 0x08 & resp[1];
765 	report->snrlint		= 0x04 & resp[1];
766 	report->rssihint	= 0x02 & resp[1];
767 	report->rssilint	= 0x01 & resp[1];
768 
769 	report->bltf		= 0x80 & resp[2];
770 	report->snr_ready	= 0x20 & resp[2];
771 	report->rssiready	= 0x08 & resp[2];
772 	report->afcrl		= 0x02 & resp[2];
773 	report->valid		= 0x01 & resp[2];
774 
775 	report->readfreq	= get_unaligned_be16(resp + 3);
776 	report->freqoff		= resp[5];
777 	report->rssi		= resp[6];
778 	report->snr		= resp[7];
779 	report->lassi		= resp[9];
780 	report->hassi		= resp[10];
781 	report->mult		= resp[11];
782 	report->dev		= resp[12];
783 
784 	return err;
785 }
786 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status);
787 
788 int si476x_core_cmd_fm_acf_status(struct si476x_core *core,
789 			     struct si476x_acf_status_report *report)
790 {
791 	int err;
792 	u8       resp[CMD_FM_ACF_STATUS_NRESP];
793 	const u8 args[CMD_FM_ACF_STATUS_NARGS] = {
794 		0x0,
795 	};
796 
797 	if (!report)
798 		return -EINVAL;
799 
800 	err = si476x_core_send_command(core, CMD_FM_ACF_STATUS,
801 				       args, ARRAY_SIZE(args),
802 				       resp, ARRAY_SIZE(resp),
803 				       SI476X_DEFAULT_TIMEOUT);
804 	if (err < 0)
805 		return err;
806 
807 	report->blend_int	= resp[1] & SI476X_ACF_BLEND_INT;
808 	report->hblend_int	= resp[1] & SI476X_ACF_HIBLEND_INT;
809 	report->hicut_int	= resp[1] & SI476X_ACF_HICUT_INT;
810 	report->chbw_int	= resp[1] & SI476X_ACF_CHBW_INT;
811 	report->softmute_int	= resp[1] & SI476X_ACF_SOFTMUTE_INT;
812 	report->smute		= resp[2] & SI476X_ACF_SMUTE;
813 	report->smattn		= resp[3] & SI476X_ACF_SMATTN;
814 	report->chbw		= resp[4];
815 	report->hicut		= resp[5];
816 	report->hiblend		= resp[6];
817 	report->pilot		= resp[7] & SI476X_ACF_PILOT;
818 	report->stblend		= resp[7] & SI476X_ACF_STBLEND;
819 
820 	return err;
821 }
822 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status);
823 
824 int si476x_core_cmd_am_acf_status(struct si476x_core *core,
825 				  struct si476x_acf_status_report *report)
826 {
827 	int err;
828 	u8       resp[CMD_AM_ACF_STATUS_NRESP];
829 	const u8 args[CMD_AM_ACF_STATUS_NARGS] = {
830 		0x0,
831 	};
832 
833 	if (!report)
834 		return -EINVAL;
835 
836 	err = si476x_core_send_command(core, CMD_AM_ACF_STATUS,
837 				       args, ARRAY_SIZE(args),
838 				       resp, ARRAY_SIZE(resp),
839 				       SI476X_DEFAULT_TIMEOUT);
840 	if (err < 0)
841 		return err;
842 
843 	report->blend_int	= resp[1] & SI476X_ACF_BLEND_INT;
844 	report->hblend_int	= resp[1] & SI476X_ACF_HIBLEND_INT;
845 	report->hicut_int	= resp[1] & SI476X_ACF_HICUT_INT;
846 	report->chbw_int	= resp[1] & SI476X_ACF_CHBW_INT;
847 	report->softmute_int	= resp[1] & SI476X_ACF_SOFTMUTE_INT;
848 	report->smute		= resp[2] & SI476X_ACF_SMUTE;
849 	report->smattn		= resp[3] & SI476X_ACF_SMATTN;
850 	report->chbw		= resp[4];
851 	report->hicut		= resp[5];
852 
853 	return err;
854 }
855 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status);
856 
857 
858 /**
859  * si476x_core_cmd_fm_seek_start - send 'FM_SEEK_START' command to the
860  * device
861  * @core:  - device to send the command to
862  * @seekup: - if set the direction of the search is 'up'
863  * @wrap:   - if set seek wraps when hitting band limit
864  *
865  * This function begins search for a valid station. The station is
866  * considered valid when 'FM_VALID_SNR_THRESHOLD' and
867  * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
868  * are met.
869 } *
870  * Function returns 0 on success and negative error code on failure
871  */
872 int si476x_core_cmd_fm_seek_start(struct si476x_core *core,
873 				  bool seekup, bool wrap)
874 {
875 	u8       resp[CMD_FM_SEEK_START_NRESP];
876 	const u8 args[CMD_FM_SEEK_START_NARGS] = {
877 		seekup << 3 | wrap << 2,
878 	};
879 
880 	return si476x_cmd_tune_seek_freq(core, CMD_FM_SEEK_START,
881 					 args, sizeof(args),
882 					 resp, sizeof(resp));
883 }
884 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start);
885 
886 /**
887  * si476x_core_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the
888  * device
889  * @core: - device to send the command to
890  * @status_only: - if set the data is not removed from RDSFIFO,
891  *                RDSFIFOUSED is not decremented and data in all the
892  *                rest RDS data contains the last valid info received
893  * @mtfifo: if set the command clears RDS receive FIFO
894  * @intack: if set the command clards the RDSINT bit.
895  * @report: - all signal quality information returned by the command
896  *           (if NULL then the output of the command is ignored)
897  *
898  * Function returns 0 on success and negative error code on failure
899  */
900 int si476x_core_cmd_fm_rds_status(struct si476x_core *core,
901 				  bool status_only,
902 				  bool mtfifo,
903 				  bool intack,
904 				  struct si476x_rds_status_report *report)
905 {
906 	int err;
907 	u8       resp[CMD_FM_RDS_STATUS_NRESP];
908 	const u8 args[CMD_FM_RDS_STATUS_NARGS] = {
909 		status_only << 2 | mtfifo << 1 | intack,
910 	};
911 
912 	err = si476x_core_send_command(core, CMD_FM_RDS_STATUS,
913 				       args, ARRAY_SIZE(args),
914 				       resp, ARRAY_SIZE(resp),
915 				       SI476X_DEFAULT_TIMEOUT);
916 	/*
917 	 * Besides getting RDS status information this command can be
918 	 * used to just acknowledge different interrupt flags in those
919 	 * cases it is useless to copy and parse received data so user
920 	 * can pass NULL, and thus avoid unnecessary copying.
921 	 */
922 	if (err < 0 || report == NULL)
923 		return err;
924 
925 	report->rdstpptyint	= 0x10 & resp[1];
926 	report->rdspiint	= 0x08 & resp[1];
927 	report->rdssyncint	= 0x02 & resp[1];
928 	report->rdsfifoint	= 0x01 & resp[1];
929 
930 	report->tpptyvalid	= 0x10 & resp[2];
931 	report->pivalid		= 0x08 & resp[2];
932 	report->rdssync		= 0x02 & resp[2];
933 	report->rdsfifolost	= 0x01 & resp[2];
934 
935 	report->tp		= 0x20 & resp[3];
936 	report->pty		= 0x1f & resp[3];
937 
938 	report->pi		= get_unaligned_be16(resp + 4);
939 	report->rdsfifoused	= resp[6];
940 
941 	report->ble[V4L2_RDS_BLOCK_A]	= 0xc0 & resp[7];
942 	report->ble[V4L2_RDS_BLOCK_B]	= 0x30 & resp[7];
943 	report->ble[V4L2_RDS_BLOCK_C]	= 0x0c & resp[7];
944 	report->ble[V4L2_RDS_BLOCK_D]	= 0x03 & resp[7];
945 
946 	report->rds[V4L2_RDS_BLOCK_A].block = V4L2_RDS_BLOCK_A;
947 	report->rds[V4L2_RDS_BLOCK_A].msb = resp[8];
948 	report->rds[V4L2_RDS_BLOCK_A].lsb = resp[9];
949 
950 	report->rds[V4L2_RDS_BLOCK_B].block = V4L2_RDS_BLOCK_B;
951 	report->rds[V4L2_RDS_BLOCK_B].msb = resp[10];
952 	report->rds[V4L2_RDS_BLOCK_B].lsb = resp[11];
953 
954 	report->rds[V4L2_RDS_BLOCK_C].block = V4L2_RDS_BLOCK_C;
955 	report->rds[V4L2_RDS_BLOCK_C].msb = resp[12];
956 	report->rds[V4L2_RDS_BLOCK_C].lsb = resp[13];
957 
958 	report->rds[V4L2_RDS_BLOCK_D].block = V4L2_RDS_BLOCK_D;
959 	report->rds[V4L2_RDS_BLOCK_D].msb = resp[14];
960 	report->rds[V4L2_RDS_BLOCK_D].lsb = resp[15];
961 
962 	return err;
963 }
964 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status);
965 
966 int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *core,
967 				bool clear,
968 				struct si476x_rds_blockcount_report *report)
969 {
970 	int err;
971 	u8       resp[CMD_FM_RDS_BLOCKCOUNT_NRESP];
972 	const u8 args[CMD_FM_RDS_BLOCKCOUNT_NARGS] = {
973 		clear,
974 	};
975 
976 	if (!report)
977 		return -EINVAL;
978 
979 	err = si476x_core_send_command(core, CMD_FM_RDS_BLOCKCOUNT,
980 				       args, ARRAY_SIZE(args),
981 				       resp, ARRAY_SIZE(resp),
982 				       SI476X_DEFAULT_TIMEOUT);
983 
984 	if (!err) {
985 		report->expected	= get_unaligned_be16(resp + 2);
986 		report->received	= get_unaligned_be16(resp + 4);
987 		report->uncorrectable	= get_unaligned_be16(resp + 6);
988 	}
989 
990 	return err;
991 }
992 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount);
993 
994 int si476x_core_cmd_fm_phase_diversity(struct si476x_core *core,
995 				       enum si476x_phase_diversity_mode mode)
996 {
997 	u8       resp[CMD_FM_PHASE_DIVERSITY_NRESP];
998 	const u8 args[CMD_FM_PHASE_DIVERSITY_NARGS] = {
999 		mode & 0x07,
1000 	};
1001 
1002 	return si476x_core_send_command(core, CMD_FM_PHASE_DIVERSITY,
1003 					args, ARRAY_SIZE(args),
1004 					resp, ARRAY_SIZE(resp),
1005 					SI476X_DEFAULT_TIMEOUT);
1006 }
1007 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity);
1008 /**
1009  * si476x_core_cmd_fm_phase_div_status() - get the phase diversity
1010  * status
1011  *
1012  * @core: si476x device
1013  *
1014  * NOTE caller must hold core lock
1015  *
1016  * Function returns the value of the status bit in case of success and
1017  * negative error code in case of failre.
1018  */
1019 int si476x_core_cmd_fm_phase_div_status(struct si476x_core *core)
1020 {
1021 	int err;
1022 	u8 resp[CMD_FM_PHASE_DIV_STATUS_NRESP];
1023 
1024 	err = si476x_core_send_command(core, CMD_FM_PHASE_DIV_STATUS,
1025 				       NULL, 0,
1026 				       resp, ARRAY_SIZE(resp),
1027 				       SI476X_DEFAULT_TIMEOUT);
1028 
1029 	return (err < 0) ? err : resp[1];
1030 }
1031 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status);
1032 
1033 
1034 /**
1035  * si476x_core_cmd_am_seek_start - send 'FM_SEEK_START' command to the
1036  * device
1037  * @core:  - device to send the command to
1038  * @seekup: - if set the direction of the search is 'up'
1039  * @wrap:   - if set seek wraps when hitting band limit
1040  *
1041  * This function begins search for a valid station. The station is
1042  * considered valid when 'FM_VALID_SNR_THRESHOLD' and
1043  * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
1044  * are met.
1045  *
1046  * Function returns 0 on success and negative error code on failure
1047  */
1048 int si476x_core_cmd_am_seek_start(struct si476x_core *core,
1049 				  bool seekup, bool wrap)
1050 {
1051 	u8       resp[CMD_AM_SEEK_START_NRESP];
1052 	const u8 args[CMD_AM_SEEK_START_NARGS] = {
1053 		seekup << 3 | wrap << 2,
1054 	};
1055 
1056 	return si476x_cmd_tune_seek_freq(core,  CMD_AM_SEEK_START,
1057 					 args, sizeof(args),
1058 					 resp, sizeof(resp));
1059 }
1060 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start);
1061 
1062 
1063 
1064 static int si476x_core_cmd_power_up_a10(struct si476x_core *core,
1065 					struct si476x_power_up_args *puargs)
1066 {
1067 	u8       resp[CMD_POWER_UP_A10_NRESP];
1068 	const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1069 	const bool ctsen  = (core->client->irq != 0);
1070 	const u8 args[CMD_POWER_UP_A10_NARGS] = {
1071 		0xF7,		/* Reserved, always 0xF7 */
1072 		0x3F & puargs->xcload,	/* First two bits are reserved to be
1073 				 * zeros */
1074 		ctsen << 7 | intsel << 6 | 0x07, /* Last five bits
1075 						   * are reserved to
1076 						   * be written as 0x7 */
1077 		puargs->func << 4 | puargs->freq,
1078 		0x11,		/* Reserved, always 0x11 */
1079 	};
1080 
1081 	return si476x_core_send_command(core, CMD_POWER_UP,
1082 					args, ARRAY_SIZE(args),
1083 					resp, ARRAY_SIZE(resp),
1084 					SI476X_TIMEOUT_POWER_UP);
1085 }
1086 
1087 static int si476x_core_cmd_power_up_a20(struct si476x_core *core,
1088 				 struct si476x_power_up_args *puargs)
1089 {
1090 	u8       resp[CMD_POWER_UP_A20_NRESP];
1091 	const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1092 	const bool ctsen  = (core->client->irq != 0);
1093 	const u8 args[CMD_POWER_UP_A20_NARGS] = {
1094 		puargs->ibias6x << 7 | puargs->xstart,
1095 		0x3F & puargs->xcload,	/* First two bits are reserved to be
1096 					 * zeros */
1097 		ctsen << 7 | intsel << 6 | puargs->fastboot << 5 |
1098 		puargs->xbiashc << 3 | puargs->xbias,
1099 		puargs->func << 4 | puargs->freq,
1100 		0x10 | puargs->xmode,
1101 	};
1102 
1103 	return si476x_core_send_command(core, CMD_POWER_UP,
1104 					args, ARRAY_SIZE(args),
1105 					resp, ARRAY_SIZE(resp),
1106 					SI476X_TIMEOUT_POWER_UP);
1107 }
1108 
1109 static int si476x_core_cmd_power_down_a10(struct si476x_core *core,
1110 					  struct si476x_power_down_args *pdargs)
1111 {
1112 	u8 resp[CMD_POWER_DOWN_A10_NRESP];
1113 
1114 	return si476x_core_send_command(core, CMD_POWER_DOWN,
1115 					NULL, 0,
1116 					resp, ARRAY_SIZE(resp),
1117 					SI476X_DEFAULT_TIMEOUT);
1118 }
1119 
1120 static int si476x_core_cmd_power_down_a20(struct si476x_core *core,
1121 					  struct si476x_power_down_args *pdargs)
1122 {
1123 	u8 resp[CMD_POWER_DOWN_A20_NRESP];
1124 	const u8 args[CMD_POWER_DOWN_A20_NARGS] = {
1125 		pdargs->xosc,
1126 	};
1127 	return si476x_core_send_command(core, CMD_POWER_DOWN,
1128 					args, ARRAY_SIZE(args),
1129 					resp, ARRAY_SIZE(resp),
1130 					SI476X_DEFAULT_TIMEOUT);
1131 }
1132 
1133 static int si476x_core_cmd_am_tune_freq_a10(struct si476x_core *core,
1134 					struct si476x_tune_freq_args *tuneargs)
1135 {
1136 
1137 	const int am_freq = tuneargs->freq;
1138 	u8       resp[CMD_AM_TUNE_FREQ_NRESP];
1139 	const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1140 		(tuneargs->hd << 6),
1141 		msb(am_freq),
1142 		lsb(am_freq),
1143 	};
1144 
1145 	return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, args,
1146 					 sizeof(args),
1147 					 resp, sizeof(resp));
1148 }
1149 
1150 static int si476x_core_cmd_am_tune_freq_a20(struct si476x_core *core,
1151 					struct si476x_tune_freq_args *tuneargs)
1152 {
1153 	const int am_freq = tuneargs->freq;
1154 	u8       resp[CMD_AM_TUNE_FREQ_NRESP];
1155 	const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1156 		(tuneargs->zifsr << 6) | (tuneargs->injside & 0x03),
1157 		msb(am_freq),
1158 		lsb(am_freq),
1159 	};
1160 
1161 	return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ,
1162 					 args, sizeof(args),
1163 					 resp, sizeof(resp));
1164 }
1165 
1166 static int si476x_core_cmd_fm_rsq_status_a10(struct si476x_core *core,
1167 					struct si476x_rsq_status_args *rsqargs,
1168 					struct si476x_rsq_status_report *report)
1169 {
1170 	int err;
1171 	u8       resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1172 	const u8 args[CMD_FM_RSQ_STATUS_A10_NARGS] = {
1173 		rsqargs->rsqack << 3 | rsqargs->attune << 2 |
1174 		rsqargs->cancel << 1 | rsqargs->stcack,
1175 	};
1176 
1177 	err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1178 				       args, ARRAY_SIZE(args),
1179 				       resp, ARRAY_SIZE(resp),
1180 				       SI476X_DEFAULT_TIMEOUT);
1181 	/*
1182 	 * Besides getting received signal quality information this
1183 	 * command can be used to just acknowledge different interrupt
1184 	 * flags in those cases it is useless to copy and parse
1185 	 * received data so user can pass NULL, and thus avoid
1186 	 * unnecessary copying.
1187 	 */
1188 	if (err < 0 || report == NULL)
1189 		return err;
1190 
1191 	report->multhint	= 0x80 & resp[1];
1192 	report->multlint	= 0x40 & resp[1];
1193 	report->snrhint		= 0x08 & resp[1];
1194 	report->snrlint		= 0x04 & resp[1];
1195 	report->rssihint	= 0x02 & resp[1];
1196 	report->rssilint	= 0x01 & resp[1];
1197 
1198 	report->bltf		= 0x80 & resp[2];
1199 	report->snr_ready	= 0x20 & resp[2];
1200 	report->rssiready	= 0x08 & resp[2];
1201 	report->afcrl		= 0x02 & resp[2];
1202 	report->valid		= 0x01 & resp[2];
1203 
1204 	report->readfreq	= get_unaligned_be16(resp + 3);
1205 	report->freqoff		= resp[5];
1206 	report->rssi		= resp[6];
1207 	report->snr		= resp[7];
1208 	report->lassi		= resp[9];
1209 	report->hassi		= resp[10];
1210 	report->mult		= resp[11];
1211 	report->dev		= resp[12];
1212 	report->readantcap	= get_unaligned_be16(resp + 13);
1213 	report->assi		= resp[15];
1214 	report->usn		= resp[16];
1215 
1216 	return err;
1217 }
1218 
1219 static int si476x_core_cmd_fm_rsq_status_a20(struct si476x_core *core,
1220 				     struct si476x_rsq_status_args *rsqargs,
1221 				     struct si476x_rsq_status_report *report)
1222 {
1223 	int err;
1224 	u8       resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1225 	const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1226 		rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1227 		rsqargs->attune  << 2 | rsqargs->cancel << 1 |
1228 		rsqargs->stcack,
1229 	};
1230 
1231 	err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1232 				       args, ARRAY_SIZE(args),
1233 				       resp, ARRAY_SIZE(resp),
1234 				       SI476X_DEFAULT_TIMEOUT);
1235 	/*
1236 	 * Besides getting received signal quality information this
1237 	 * command can be used to just acknowledge different interrupt
1238 	 * flags in those cases it is useless to copy and parse
1239 	 * received data so user can pass NULL, and thus avoid
1240 	 * unnecessary copying.
1241 	 */
1242 	if (err < 0 || report == NULL)
1243 		return err;
1244 
1245 	report->multhint	= 0x80 & resp[1];
1246 	report->multlint	= 0x40 & resp[1];
1247 	report->snrhint		= 0x08 & resp[1];
1248 	report->snrlint		= 0x04 & resp[1];
1249 	report->rssihint	= 0x02 & resp[1];
1250 	report->rssilint	= 0x01 & resp[1];
1251 
1252 	report->bltf		= 0x80 & resp[2];
1253 	report->snr_ready	= 0x20 & resp[2];
1254 	report->rssiready	= 0x08 & resp[2];
1255 	report->afcrl		= 0x02 & resp[2];
1256 	report->valid		= 0x01 & resp[2];
1257 
1258 	report->readfreq	= get_unaligned_be16(resp + 3);
1259 	report->freqoff		= resp[5];
1260 	report->rssi		= resp[6];
1261 	report->snr		= resp[7];
1262 	report->lassi		= resp[9];
1263 	report->hassi		= resp[10];
1264 	report->mult		= resp[11];
1265 	report->dev		= resp[12];
1266 	report->readantcap	= get_unaligned_be16(resp + 13);
1267 	report->assi		= resp[15];
1268 	report->usn		= resp[16];
1269 
1270 	return err;
1271 }
1272 
1273 
1274 static int si476x_core_cmd_fm_rsq_status_a30(struct si476x_core *core,
1275 					struct si476x_rsq_status_args *rsqargs,
1276 					struct si476x_rsq_status_report *report)
1277 {
1278 	int err;
1279 	u8       resp[CMD_FM_RSQ_STATUS_A30_NRESP];
1280 	const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1281 		rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1282 		rsqargs->attune << 2 | rsqargs->cancel << 1 |
1283 		rsqargs->stcack,
1284 	};
1285 
1286 	err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1287 				       args, ARRAY_SIZE(args),
1288 				       resp, ARRAY_SIZE(resp),
1289 				       SI476X_DEFAULT_TIMEOUT);
1290 	/*
1291 	 * Besides getting received signal quality information this
1292 	 * command can be used to just acknowledge different interrupt
1293 	 * flags in those cases it is useless to copy and parse
1294 	 * received data so user can pass NULL, and thus avoid
1295 	 * unnecessary copying.
1296 	 */
1297 	if (err < 0 || report == NULL)
1298 		return err;
1299 
1300 	report->multhint	= 0x80 & resp[1];
1301 	report->multlint	= 0x40 & resp[1];
1302 	report->snrhint		= 0x08 & resp[1];
1303 	report->snrlint		= 0x04 & resp[1];
1304 	report->rssihint	= 0x02 & resp[1];
1305 	report->rssilint	= 0x01 & resp[1];
1306 
1307 	report->bltf		= 0x80 & resp[2];
1308 	report->snr_ready	= 0x20 & resp[2];
1309 	report->rssiready	= 0x08 & resp[2];
1310 	report->injside         = 0x04 & resp[2];
1311 	report->afcrl		= 0x02 & resp[2];
1312 	report->valid		= 0x01 & resp[2];
1313 
1314 	report->readfreq	= get_unaligned_be16(resp + 3);
1315 	report->freqoff		= resp[5];
1316 	report->rssi		= resp[6];
1317 	report->snr		= resp[7];
1318 	report->issi		= resp[8];
1319 	report->lassi		= resp[9];
1320 	report->hassi		= resp[10];
1321 	report->mult		= resp[11];
1322 	report->dev		= resp[12];
1323 	report->readantcap	= get_unaligned_be16(resp + 13);
1324 	report->assi		= resp[15];
1325 	report->usn		= resp[16];
1326 
1327 	report->pilotdev	= resp[17];
1328 	report->rdsdev		= resp[18];
1329 	report->assidev		= resp[19];
1330 	report->strongdev	= resp[20];
1331 	report->rdspi		= get_unaligned_be16(resp + 21);
1332 
1333 	return err;
1334 }
1335 
1336 static int si476x_core_cmd_fm_tune_freq_a10(struct si476x_core *core,
1337 					struct si476x_tune_freq_args *tuneargs)
1338 {
1339 	u8       resp[CMD_FM_TUNE_FREQ_NRESP];
1340 	const u8 args[CMD_FM_TUNE_FREQ_A10_NARGS] = {
1341 		(tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1342 		| (tuneargs->smoothmetrics << 2),
1343 		msb(tuneargs->freq),
1344 		lsb(tuneargs->freq),
1345 		msb(tuneargs->antcap),
1346 		lsb(tuneargs->antcap)
1347 	};
1348 
1349 	return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1350 					 args, sizeof(args),
1351 					 resp, sizeof(resp));
1352 }
1353 
1354 static int si476x_core_cmd_fm_tune_freq_a20(struct si476x_core *core,
1355 					struct si476x_tune_freq_args *tuneargs)
1356 {
1357 	u8       resp[CMD_FM_TUNE_FREQ_NRESP];
1358 	const u8 args[CMD_FM_TUNE_FREQ_A20_NARGS] = {
1359 		(tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1360 		|  (tuneargs->smoothmetrics << 2) | (tuneargs->injside),
1361 		msb(tuneargs->freq),
1362 		lsb(tuneargs->freq),
1363 	};
1364 
1365 	return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1366 					 args, sizeof(args),
1367 					 resp, sizeof(resp));
1368 }
1369 
1370 static int si476x_core_cmd_agc_status_a20(struct si476x_core *core,
1371 					struct si476x_agc_status_report *report)
1372 {
1373 	int err;
1374 	u8 resp[CMD_AGC_STATUS_NRESP_A20];
1375 
1376 	if (!report)
1377 		return -EINVAL;
1378 
1379 	err = si476x_core_send_command(core, CMD_AGC_STATUS,
1380 				       NULL, 0,
1381 				       resp, ARRAY_SIZE(resp),
1382 				       SI476X_DEFAULT_TIMEOUT);
1383 	if (err < 0)
1384 		return err;
1385 
1386 	report->mxhi		= resp[1] & SI476X_AGC_MXHI;
1387 	report->mxlo		= resp[1] & SI476X_AGC_MXLO;
1388 	report->lnahi		= resp[1] & SI476X_AGC_LNAHI;
1389 	report->lnalo		= resp[1] & SI476X_AGC_LNALO;
1390 	report->fmagc1		= resp[2];
1391 	report->fmagc2		= resp[3];
1392 	report->pgagain		= resp[4];
1393 	report->fmwblang	= resp[5];
1394 
1395 	return err;
1396 }
1397 
1398 static int si476x_core_cmd_agc_status_a10(struct si476x_core *core,
1399 					struct si476x_agc_status_report *report)
1400 {
1401 	int err;
1402 	u8 resp[CMD_AGC_STATUS_NRESP_A10];
1403 
1404 	if (!report)
1405 		return -EINVAL;
1406 
1407 	err = si476x_core_send_command(core, CMD_AGC_STATUS,
1408 				       NULL, 0,
1409 				       resp, ARRAY_SIZE(resp),
1410 				       SI476X_DEFAULT_TIMEOUT);
1411 	if (err < 0)
1412 		return err;
1413 
1414 	report->mxhi	= resp[1] & SI476X_AGC_MXHI;
1415 	report->mxlo	= resp[1] & SI476X_AGC_MXLO;
1416 	report->lnahi	= resp[1] & SI476X_AGC_LNAHI;
1417 	report->lnalo	= resp[1] & SI476X_AGC_LNALO;
1418 
1419 	return err;
1420 }
1421 
1422 typedef int (*tune_freq_func_t) (struct si476x_core *core,
1423 				 struct si476x_tune_freq_args *tuneargs);
1424 
1425 static struct {
1426 	int (*power_up)(struct si476x_core *,
1427 			struct si476x_power_up_args *);
1428 	int (*power_down)(struct si476x_core *,
1429 			  struct si476x_power_down_args *);
1430 
1431 	tune_freq_func_t fm_tune_freq;
1432 	tune_freq_func_t am_tune_freq;
1433 
1434 	int (*fm_rsq_status)(struct si476x_core *,
1435 			     struct si476x_rsq_status_args *,
1436 			     struct si476x_rsq_status_report *);
1437 
1438 	int (*agc_status)(struct si476x_core *,
1439 			  struct si476x_agc_status_report *);
1440 	int (*intb_pin_cfg)(struct si476x_core *core,
1441 			    enum si476x_intb_config intb,
1442 			    enum si476x_a1_config a1);
1443 } si476x_cmds_vtable[] = {
1444 	[SI476X_REVISION_A10] = {
1445 		.power_up	= si476x_core_cmd_power_up_a10,
1446 		.power_down	= si476x_core_cmd_power_down_a10,
1447 		.fm_tune_freq	= si476x_core_cmd_fm_tune_freq_a10,
1448 		.am_tune_freq	= si476x_core_cmd_am_tune_freq_a10,
1449 		.fm_rsq_status	= si476x_core_cmd_fm_rsq_status_a10,
1450 		.agc_status	= si476x_core_cmd_agc_status_a10,
1451 		.intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a10,
1452 	},
1453 	[SI476X_REVISION_A20] = {
1454 		.power_up	= si476x_core_cmd_power_up_a20,
1455 		.power_down	= si476x_core_cmd_power_down_a20,
1456 		.fm_tune_freq	= si476x_core_cmd_fm_tune_freq_a20,
1457 		.am_tune_freq	= si476x_core_cmd_am_tune_freq_a20,
1458 		.fm_rsq_status	= si476x_core_cmd_fm_rsq_status_a20,
1459 		.agc_status	= si476x_core_cmd_agc_status_a20,
1460 		.intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a20,
1461 	},
1462 	[SI476X_REVISION_A30] = {
1463 		.power_up	= si476x_core_cmd_power_up_a20,
1464 		.power_down	= si476x_core_cmd_power_down_a20,
1465 		.fm_tune_freq	= si476x_core_cmd_fm_tune_freq_a20,
1466 		.am_tune_freq	= si476x_core_cmd_am_tune_freq_a20,
1467 		.fm_rsq_status	= si476x_core_cmd_fm_rsq_status_a30,
1468 		.agc_status	= si476x_core_cmd_agc_status_a20,
1469 		.intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a20,
1470 	},
1471 };
1472 
1473 int si476x_core_cmd_power_up(struct si476x_core *core,
1474 			     struct si476x_power_up_args *args)
1475 {
1476 	BUG_ON(core->revision > SI476X_REVISION_A30 ||
1477 	       core->revision == -1);
1478 	return si476x_cmds_vtable[core->revision].power_up(core, args);
1479 }
1480 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up);
1481 
1482 int si476x_core_cmd_power_down(struct si476x_core *core,
1483 			       struct si476x_power_down_args *args)
1484 {
1485 	BUG_ON(core->revision > SI476X_REVISION_A30 ||
1486 	       core->revision == -1);
1487 	return si476x_cmds_vtable[core->revision].power_down(core, args);
1488 }
1489 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down);
1490 
1491 int si476x_core_cmd_fm_tune_freq(struct si476x_core *core,
1492 				 struct si476x_tune_freq_args *args)
1493 {
1494 	BUG_ON(core->revision > SI476X_REVISION_A30 ||
1495 	       core->revision == -1);
1496 	return si476x_cmds_vtable[core->revision].fm_tune_freq(core, args);
1497 }
1498 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq);
1499 
1500 int si476x_core_cmd_am_tune_freq(struct si476x_core *core,
1501 				 struct si476x_tune_freq_args *args)
1502 {
1503 	BUG_ON(core->revision > SI476X_REVISION_A30 ||
1504 	       core->revision == -1);
1505 	return si476x_cmds_vtable[core->revision].am_tune_freq(core, args);
1506 }
1507 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq);
1508 
1509 int si476x_core_cmd_fm_rsq_status(struct si476x_core *core,
1510 				  struct si476x_rsq_status_args *args,
1511 				  struct si476x_rsq_status_report *report)
1512 
1513 {
1514 	BUG_ON(core->revision > SI476X_REVISION_A30 ||
1515 	       core->revision == -1);
1516 	return si476x_cmds_vtable[core->revision].fm_rsq_status(core, args,
1517 								report);
1518 }
1519 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status);
1520 
1521 int si476x_core_cmd_agc_status(struct si476x_core *core,
1522 				  struct si476x_agc_status_report *report)
1523 
1524 {
1525 	BUG_ON(core->revision > SI476X_REVISION_A30 ||
1526 	       core->revision == -1);
1527 	return si476x_cmds_vtable[core->revision].agc_status(core, report);
1528 }
1529 EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status);
1530 
1531 int si476x_core_cmd_intb_pin_cfg(struct si476x_core *core,
1532 			    enum si476x_intb_config intb,
1533 			    enum si476x_a1_config a1)
1534 {
1535 	BUG_ON(core->revision > SI476X_REVISION_A30 ||
1536 	       core->revision == -1);
1537 
1538 	return si476x_cmds_vtable[core->revision].intb_pin_cfg(core, intb, a1);
1539 }
1540 EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg);
1541 
1542 MODULE_LICENSE("GPL");
1543 MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
1544 MODULE_DESCRIPTION("API for command exchange for si476x");
1545