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