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