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