2 * drivers/mfd/si476x-cmd.c -- Subroutines implementing command
3 * protocol of si476x series of chips
5 * Copyright (C) 2012 Innovative Converged Devices(ICD)
6 * Copyright (C) 2013 Andrey Smirnov
8 * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
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.
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.
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>
30 #include <linux/mfd/si476x-core.h>
32 #define msb(x) ((u8)((u16) x >> 8))
33 #define lsb(x) ((u8)((u16) x & 0x00FF))
37 #define CMD_POWER_UP 0x01
38 #define CMD_POWER_UP_A10_NRESP 1
39 #define CMD_POWER_UP_A10_NARGS 5
41 #define CMD_POWER_UP_A20_NRESP 1
42 #define CMD_POWER_UP_A20_NARGS 5
44 #define POWER_UP_DELAY_MS 110
46 #define CMD_POWER_DOWN 0x11
47 #define CMD_POWER_DOWN_A10_NRESP 1
49 #define CMD_POWER_DOWN_A20_NRESP 1
50 #define CMD_POWER_DOWN_A20_NARGS 1
52 #define CMD_FUNC_INFO 0x12
53 #define CMD_FUNC_INFO_NRESP 7
55 #define CMD_SET_PROPERTY 0x13
56 #define CMD_SET_PROPERTY_NARGS 5
57 #define CMD_SET_PROPERTY_NRESP 1
59 #define CMD_GET_PROPERTY 0x14
60 #define CMD_GET_PROPERTY_NARGS 3
61 #define CMD_GET_PROPERTY_NRESP 4
63 #define CMD_AGC_STATUS 0x17
64 #define CMD_AGC_STATUS_NRESP_A10 2
65 #define CMD_AGC_STATUS_NRESP_A20 6
67 #define PIN_CFG_BYTE(x) (0x7F & (x))
68 #define CMD_DIG_AUDIO_PIN_CFG 0x18
69 #define CMD_DIG_AUDIO_PIN_CFG_NARGS 4
70 #define CMD_DIG_AUDIO_PIN_CFG_NRESP 5
72 #define CMD_ZIF_PIN_CFG 0x19
73 #define CMD_ZIF_PIN_CFG_NARGS 4
74 #define CMD_ZIF_PIN_CFG_NRESP 5
76 #define CMD_IC_LINK_GPO_CTL_PIN_CFG 0x1A
77 #define CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS 4
78 #define CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP 5
80 #define CMD_ANA_AUDIO_PIN_CFG 0x1B
81 #define CMD_ANA_AUDIO_PIN_CFG_NARGS 1
82 #define CMD_ANA_AUDIO_PIN_CFG_NRESP 2
84 #define CMD_INTB_PIN_CFG 0x1C
85 #define CMD_INTB_PIN_CFG_NARGS 2
86 #define CMD_INTB_PIN_CFG_A10_NRESP 6
87 #define CMD_INTB_PIN_CFG_A20_NRESP 3
89 #define CMD_FM_TUNE_FREQ 0x30
90 #define CMD_FM_TUNE_FREQ_A10_NARGS 5
91 #define CMD_FM_TUNE_FREQ_A20_NARGS 3
92 #define CMD_FM_TUNE_FREQ_NRESP 1
94 #define CMD_FM_RSQ_STATUS 0x32
96 #define CMD_FM_RSQ_STATUS_A10_NARGS 1
97 #define CMD_FM_RSQ_STATUS_A10_NRESP 17
98 #define CMD_FM_RSQ_STATUS_A30_NARGS 1
99 #define CMD_FM_RSQ_STATUS_A30_NRESP 23
102 #define CMD_FM_SEEK_START 0x31
103 #define CMD_FM_SEEK_START_NARGS 1
104 #define CMD_FM_SEEK_START_NRESP 1
106 #define CMD_FM_RDS_STATUS 0x36
107 #define CMD_FM_RDS_STATUS_NARGS 1
108 #define CMD_FM_RDS_STATUS_NRESP 16
110 #define CMD_FM_RDS_BLOCKCOUNT 0x37
111 #define CMD_FM_RDS_BLOCKCOUNT_NARGS 1
112 #define CMD_FM_RDS_BLOCKCOUNT_NRESP 8
114 #define CMD_FM_PHASE_DIVERSITY 0x38
115 #define CMD_FM_PHASE_DIVERSITY_NARGS 1
116 #define CMD_FM_PHASE_DIVERSITY_NRESP 1
118 #define CMD_FM_PHASE_DIV_STATUS 0x39
119 #define CMD_FM_PHASE_DIV_STATUS_NRESP 2
121 #define CMD_AM_TUNE_FREQ 0x40
122 #define CMD_AM_TUNE_FREQ_NARGS 3
123 #define CMD_AM_TUNE_FREQ_NRESP 1
125 #define CMD_AM_RSQ_STATUS 0x42
126 #define CMD_AM_RSQ_STATUS_NARGS 1
127 #define CMD_AM_RSQ_STATUS_NRESP 13
129 #define CMD_AM_SEEK_START 0x41
130 #define CMD_AM_SEEK_START_NARGS 1
131 #define CMD_AM_SEEK_START_NRESP 1
134 #define CMD_AM_ACF_STATUS 0x45
135 #define CMD_AM_ACF_STATUS_NRESP 6
136 #define CMD_AM_ACF_STATUS_NARGS 1
138 #define CMD_FM_ACF_STATUS 0x35
139 #define CMD_FM_ACF_STATUS_NRESP 8
140 #define CMD_FM_ACF_STATUS_NARGS 1
142 #define CMD_MAX_ARGS_COUNT (10)
145 enum si476x_acf_status_report_bits {
146 SI476X_ACF_BLEND_INT = (1 << 4),
147 SI476X_ACF_HIBLEND_INT = (1 << 3),
148 SI476X_ACF_HICUT_INT = (1 << 2),
149 SI476X_ACF_CHBW_INT = (1 << 1),
150 SI476X_ACF_SOFTMUTE_INT = (1 << 0),
152 SI476X_ACF_SMUTE = (1 << 0),
153 SI476X_ACF_SMATTN = 0b11111,
154 SI476X_ACF_PILOT = (1 << 7),
155 SI476X_ACF_STBLEND = ~SI476X_ACF_PILOT,
158 enum si476x_agc_status_report_bits {
159 SI476X_AGC_MXHI = (1 << 5),
160 SI476X_AGC_MXLO = (1 << 4),
161 SI476X_AGC_LNAHI = (1 << 3),
162 SI476X_AGC_LNALO = (1 << 2),
166 SI476X_ERR_BAD_COMMAND = 0x10,
167 SI476X_ERR_BAD_ARG1 = 0x11,
168 SI476X_ERR_BAD_ARG2 = 0x12,
169 SI476X_ERR_BAD_ARG3 = 0x13,
170 SI476X_ERR_BAD_ARG4 = 0x14,
171 SI476X_ERR_BUSY = 0x18,
172 SI476X_ERR_BAD_INTERNAL_MEMORY = 0x20,
173 SI476X_ERR_BAD_PATCH = 0x30,
174 SI476X_ERR_BAD_BOOT_MODE = 0x31,
175 SI476X_ERR_BAD_PROPERTY = 0x40,
178 static int si476x_core_parse_and_nag_about_error(struct si476x_core *core)
184 if (core->revision != SI476X_REVISION_A10) {
185 err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV,
186 buffer, sizeof(buffer));
187 if (err == sizeof(buffer)) {
189 case SI476X_ERR_BAD_COMMAND:
190 cause = "Bad command";
193 case SI476X_ERR_BAD_ARG1:
194 cause = "Bad argument #1";
197 case SI476X_ERR_BAD_ARG2:
198 cause = "Bad argument #2";
201 case SI476X_ERR_BAD_ARG3:
202 cause = "Bad argument #3";
205 case SI476X_ERR_BAD_ARG4:
206 cause = "Bad argument #4";
209 case SI476X_ERR_BUSY:
210 cause = "Chip is busy";
213 case SI476X_ERR_BAD_INTERNAL_MEMORY:
214 cause = "Bad internal memory";
217 case SI476X_ERR_BAD_PATCH:
221 case SI476X_ERR_BAD_BOOT_MODE:
222 cause = "Bad boot mode";
225 case SI476X_ERR_BAD_PROPERTY:
226 cause = "Bad property";
234 dev_err(&core->client->dev,
235 "[Chip error status]: %s\n", cause);
237 dev_err(&core->client->dev,
238 "Failed to fetch error code\n");
239 err = (err >= 0) ? -EIO : err;
249 * si476x_core_send_command() - sends a command to si476x and waits its
251 * @core: si476x_device structure for the device we are
253 * @command: command id
254 * @args: command arguments we are sending
255 * @argn: actual size of @args
256 * @response: buffer to place the expected response from the device
257 * @respn: actual size of @response
258 * @usecs: amount of time to wait before reading the response (in
261 * Function returns 0 on succsess and negative error code on
264 static int si476x_core_send_command(struct si476x_core *core,
272 struct i2c_client *client = core->client;
274 u8 data[CMD_MAX_ARGS_COUNT + 1];
276 if (argn > CMD_MAX_ARGS_COUNT) {
281 if (!client->adapter) {
286 /* First send the command and its arguments */
288 memcpy(&data[1], args, argn);
289 dev_dbg(&client->dev, "Command:\n %*ph\n", argn + 1, data);
291 err = si476x_core_i2c_xfer(core, SI476X_I2C_SEND,
292 (char *) data, argn + 1);
293 if (err != argn + 1) {
294 dev_err(&core->client->dev,
295 "Error while sending command 0x%02x\n",
297 err = (err >= 0) ? -EIO : err;
300 /* Set CTS to zero only after the command is send to avoid
301 * possible racing conditions when working in polling mode */
302 atomic_set(&core->cts, 0);
304 /* if (unlikely(command == CMD_POWER_DOWN) */
305 if (!wait_event_timeout(core->command,
306 atomic_read(&core->cts),
307 usecs_to_jiffies(usecs) + 1))
308 dev_warn(&core->client->dev,
309 "(%s) [CMD 0x%02x] Answer timeout.\n",
313 When working in polling mode, for some reason the tuner will
314 report CTS bit as being set in the first status byte read,
315 but all the consequtive ones will return zeros until the
316 tuner is actually completed the POWER_UP command. To
317 workaround that we wait for second CTS to be reported
319 if (unlikely(!core->client->irq && command == CMD_POWER_UP)) {
320 if (!wait_event_timeout(core->command,
321 atomic_read(&core->cts),
322 usecs_to_jiffies(usecs) + 1))
323 dev_warn(&core->client->dev,
324 "(%s) Power up took too much time.\n",
328 /* Then get the response */
329 err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, resp, respn);
331 dev_err(&core->client->dev,
332 "Error while reading response for command 0x%02x\n",
334 err = (err >= 0) ? -EIO : err;
337 dev_dbg(&client->dev, "Response:\n %*ph\n", respn, resp);
341 if (resp[0] & SI476X_ERR) {
342 dev_err(&core->client->dev,
343 "[CMD 0x%02x] Chip set error flag\n", command);
344 err = si476x_core_parse_and_nag_about_error(core);
348 if (!(resp[0] & SI476X_CTS))
354 static int si476x_cmd_clear_stc(struct si476x_core *core)
357 struct si476x_rsq_status_args args = {
365 switch (core->power_up_parameters.func) {
366 case SI476X_FUNC_FM_RECEIVER:
367 err = si476x_core_cmd_fm_rsq_status(core, &args, NULL);
369 case SI476X_FUNC_AM_RECEIVER:
370 err = si476x_core_cmd_am_rsq_status(core, &args, NULL);
379 static int si476x_cmd_tune_seek_freq(struct si476x_core *core,
381 const uint8_t args[], size_t argn,
382 uint8_t *resp, size_t respn)
387 atomic_set(&core->stc, 0);
388 err = si476x_core_send_command(core, cmd, args, argn, resp, respn,
389 SI476X_TIMEOUT_TUNE);
391 wait_event_killable(core->tuning,
392 atomic_read(&core->stc));
393 si476x_cmd_clear_stc(core);
400 * si476x_cmd_func_info() - send 'FUNC_INFO' command to the device
401 * @core: device to send the command to
402 * @info: struct si476x_func_info to fill all the information
403 * returned by the command
405 * The command requests the firmware and patch version for currently
406 * loaded firmware (dependent on the function of the device FM/AM/WB)
408 * Function returns 0 on succsess and negative error code on
411 int si476x_core_cmd_func_info(struct si476x_core *core,
412 struct si476x_func_info *info)
415 u8 resp[CMD_FUNC_INFO_NRESP];
417 err = si476x_core_send_command(core, CMD_FUNC_INFO,
419 resp, ARRAY_SIZE(resp),
420 SI476X_DEFAULT_TIMEOUT);
422 info->firmware.major = resp[1];
423 info->firmware.minor[0] = resp[2];
424 info->firmware.minor[1] = resp[3];
426 info->patch_id = ((u16) resp[4] << 8) | resp[5];
427 info->func = resp[6];
431 EXPORT_SYMBOL_GPL(si476x_core_cmd_func_info);
434 * si476x_cmd_set_property() - send 'SET_PROPERTY' command to the device
435 * @core: device to send the command to
436 * @property: property address
437 * @value: property value
439 * Function returns 0 on succsess and negative error code on
442 int si476x_core_cmd_set_property(struct si476x_core *core,
443 u16 property, u16 value)
445 u8 resp[CMD_SET_PROPERTY_NRESP];
446 const u8 args[CMD_SET_PROPERTY_NARGS] = {
454 return si476x_core_send_command(core, CMD_SET_PROPERTY,
455 args, ARRAY_SIZE(args),
456 resp, ARRAY_SIZE(resp),
457 SI476X_DEFAULT_TIMEOUT);
459 EXPORT_SYMBOL_GPL(si476x_core_cmd_set_property);
462 * si476x_cmd_get_property() - send 'GET_PROPERTY' command to the device
463 * @core: device to send the command to
464 * @property: property address
466 * Function return the value of property as u16 on success or a
467 * negative error on failure
469 int si476x_core_cmd_get_property(struct si476x_core *core, u16 property)
472 u8 resp[CMD_GET_PROPERTY_NRESP];
473 const u8 args[CMD_GET_PROPERTY_NARGS] = {
479 err = si476x_core_send_command(core, CMD_GET_PROPERTY,
480 args, ARRAY_SIZE(args),
481 resp, ARRAY_SIZE(resp),
482 SI476X_DEFAULT_TIMEOUT);
486 return be16_to_cpup((__be16 *)(resp + 2));
488 EXPORT_SYMBOL_GPL(si476x_core_cmd_get_property);
491 * si476x_cmd_dig_audio_pin_cfg() - send 'DIG_AUDIO_PIN_CFG' command to
493 * @core: device to send the command to
494 * @dclk: DCLK pin function configuration:
495 * #SI476X_DCLK_NOOP - do not modify the behaviour
496 * #SI476X_DCLK_TRISTATE - put the pin in tristate condition,
497 * enable 1MOhm pulldown
498 * #SI476X_DCLK_DAUDIO - set the pin to be a part of digital
500 * @dfs: DFS pin function configuration:
501 * #SI476X_DFS_NOOP - do not modify the behaviour
502 * #SI476X_DFS_TRISTATE - put the pin in tristate condition,
503 * enable 1MOhm pulldown
504 * SI476X_DFS_DAUDIO - set the pin to be a part of digital
506 * @dout - DOUT pin function configuration:
507 * SI476X_DOUT_NOOP - do not modify the behaviour
508 * SI476X_DOUT_TRISTATE - put the pin in tristate condition,
509 * enable 1MOhm pulldown
510 * SI476X_DOUT_I2S_OUTPUT - set this pin to be digital out on I2S
512 * SI476X_DOUT_I2S_INPUT - set this pin to be digital in on I2S
514 * @xout - XOUT pin function configuration:
515 * SI476X_XOUT_NOOP - do not modify the behaviour
516 * SI476X_XOUT_TRISTATE - put the pin in tristate condition,
517 * enable 1MOhm pulldown
518 * SI476X_XOUT_I2S_INPUT - set this pin to be digital in on I2S
520 * SI476X_XOUT_MODE_SELECT - set this pin to be the input that
521 * selects the mode of the I2S audio
522 * combiner (analog or HD)
523 * [SI4761/63/65/67 Only]
525 * Function returns 0 on success and negative error code on failure
527 int si476x_core_cmd_dig_audio_pin_cfg(struct si476x_core *core,
528 enum si476x_dclk_config dclk,
529 enum si476x_dfs_config dfs,
530 enum si476x_dout_config dout,
531 enum si476x_xout_config xout)
533 u8 resp[CMD_DIG_AUDIO_PIN_CFG_NRESP];
534 const u8 args[CMD_DIG_AUDIO_PIN_CFG_NARGS] = {
541 return si476x_core_send_command(core, CMD_DIG_AUDIO_PIN_CFG,
542 args, ARRAY_SIZE(args),
543 resp, ARRAY_SIZE(resp),
544 SI476X_DEFAULT_TIMEOUT);
546 EXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg);
549 * si476x_cmd_zif_pin_cfg - send 'ZIF_PIN_CFG_COMMAND'
550 * @core - device to send the command to
551 * @iqclk - IQCL pin function configuration:
552 * SI476X_IQCLK_NOOP - do not modify the behaviour
553 * SI476X_IQCLK_TRISTATE - put the pin in tristate condition,
554 * enable 1MOhm pulldown
555 * SI476X_IQCLK_IQ - set pin to be a part of I/Q interace
557 * @iqfs - IQFS pin function configuration:
558 * SI476X_IQFS_NOOP - do not modify the behaviour
559 * SI476X_IQFS_TRISTATE - put the pin in tristate condition,
560 * enable 1MOhm pulldown
561 * SI476X_IQFS_IQ - set pin to be a part of I/Q interace
563 * @iout - IOUT pin function configuration:
564 * SI476X_IOUT_NOOP - do not modify the behaviour
565 * SI476X_IOUT_TRISTATE - put the pin in tristate condition,
566 * enable 1MOhm pulldown
567 * SI476X_IOUT_OUTPUT - set pin to be I out
568 * @qout - QOUT pin function configuration:
569 * SI476X_QOUT_NOOP - do not modify the behaviour
570 * SI476X_QOUT_TRISTATE - put the pin in tristate condition,
571 * enable 1MOhm pulldown
572 * SI476X_QOUT_OUTPUT - set pin to be Q out
574 * Function returns 0 on success and negative error code on failure
576 int si476x_core_cmd_zif_pin_cfg(struct si476x_core *core,
577 enum si476x_iqclk_config iqclk,
578 enum si476x_iqfs_config iqfs,
579 enum si476x_iout_config iout,
580 enum si476x_qout_config qout)
582 u8 resp[CMD_ZIF_PIN_CFG_NRESP];
583 const u8 args[CMD_ZIF_PIN_CFG_NARGS] = {
590 return si476x_core_send_command(core, CMD_ZIF_PIN_CFG,
591 args, ARRAY_SIZE(args),
592 resp, ARRAY_SIZE(resp),
593 SI476X_DEFAULT_TIMEOUT);
595 EXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg);
598 * si476x_cmd_ic_link_gpo_ctl_pin_cfg - send
599 * 'IC_LINK_GPIO_CTL_PIN_CFG' comand to the device
600 * @core - device to send the command to
601 * @icin - ICIN pin function configuration:
602 * SI476X_ICIN_NOOP - do not modify the behaviour
603 * SI476X_ICIN_TRISTATE - put the pin in tristate condition,
604 * enable 1MOhm pulldown
605 * SI476X_ICIN_GPO1_HIGH - set pin to be an output, drive it high
606 * SI476X_ICIN_GPO1_LOW - set pin to be an output, drive it low
607 * SI476X_ICIN_IC_LINK - set the pin to be a part of Inter-Chip link
608 * @icip - ICIP pin function configuration:
609 * SI476X_ICIP_NOOP - do not modify the behaviour
610 * SI476X_ICIP_TRISTATE - put the pin in tristate condition,
611 * enable 1MOhm pulldown
612 * SI476X_ICIP_GPO1_HIGH - set pin to be an output, drive it high
613 * SI476X_ICIP_GPO1_LOW - set pin to be an output, drive it low
614 * SI476X_ICIP_IC_LINK - set the pin to be a part of Inter-Chip link
615 * @icon - ICON pin function configuration:
616 * SI476X_ICON_NOOP - do not modify the behaviour
617 * SI476X_ICON_TRISTATE - put the pin in tristate condition,
618 * enable 1MOhm pulldown
619 * SI476X_ICON_I2S - set the pin to be a part of audio
620 * interface in slave mode (DCLK)
621 * SI476X_ICON_IC_LINK - set the pin to be a part of Inter-Chip link
622 * @icop - ICOP pin function configuration:
623 * SI476X_ICOP_NOOP - do not modify the behaviour
624 * SI476X_ICOP_TRISTATE - put the pin in tristate condition,
625 * enable 1MOhm pulldown
626 * SI476X_ICOP_I2S - set the pin to be a part of audio
627 * interface in slave mode (DOUT)
628 * [Si4761/63/65/67 Only]
629 * SI476X_ICOP_IC_LINK - set the pin to be a part of Inter-Chip link
631 * Function returns 0 on success and negative error code on failure
633 int si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(struct si476x_core *core,
634 enum si476x_icin_config icin,
635 enum si476x_icip_config icip,
636 enum si476x_icon_config icon,
637 enum si476x_icop_config icop)
639 u8 resp[CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP];
640 const u8 args[CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS] = {
647 return si476x_core_send_command(core, CMD_IC_LINK_GPO_CTL_PIN_CFG,
648 args, ARRAY_SIZE(args),
649 resp, ARRAY_SIZE(resp),
650 SI476X_DEFAULT_TIMEOUT);
652 EXPORT_SYMBOL_GPL(si476x_core_cmd_ic_link_gpo_ctl_pin_cfg);
655 * si476x_cmd_ana_audio_pin_cfg - send 'ANA_AUDIO_PIN_CFG' to the
657 * @core - device to send the command to
658 * @lrout - LROUT pin function configuration:
659 * SI476X_LROUT_NOOP - do not modify the behaviour
660 * SI476X_LROUT_TRISTATE - put the pin in tristate condition,
661 * enable 1MOhm pulldown
662 * SI476X_LROUT_AUDIO - set pin to be audio output
663 * SI476X_LROUT_MPX - set pin to be MPX output
665 * Function returns 0 on success and negative error code on failure
667 int si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *core,
668 enum si476x_lrout_config lrout)
670 u8 resp[CMD_ANA_AUDIO_PIN_CFG_NRESP];
671 const u8 args[CMD_ANA_AUDIO_PIN_CFG_NARGS] = {
675 return si476x_core_send_command(core, CMD_ANA_AUDIO_PIN_CFG,
676 args, ARRAY_SIZE(args),
677 resp, ARRAY_SIZE(resp),
678 SI476X_DEFAULT_TIMEOUT);
680 EXPORT_SYMBOL_GPL(si476x_core_cmd_ana_audio_pin_cfg);
684 * si476x_cmd_intb_pin_cfg - send 'INTB_PIN_CFG' command to the device
685 * @core - device to send the command to
686 * @intb - INTB pin function configuration:
687 * SI476X_INTB_NOOP - do not modify the behaviour
688 * SI476X_INTB_TRISTATE - put the pin in tristate condition,
689 * enable 1MOhm pulldown
690 * SI476X_INTB_DAUDIO - set pin to be a part of digital
691 * audio interface in slave mode
692 * SI476X_INTB_IRQ - set pin to be an interrupt request line
693 * @a1 - A1 pin function configuration:
694 * SI476X_A1_NOOP - do not modify the behaviour
695 * SI476X_A1_TRISTATE - put the pin in tristate condition,
696 * enable 1MOhm pulldown
697 * SI476X_A1_IRQ - set pin to be an interrupt request line
699 * Function returns 0 on success and negative error code on failure
701 static int si476x_core_cmd_intb_pin_cfg_a10(struct si476x_core *core,
702 enum si476x_intb_config intb,
703 enum si476x_a1_config a1)
705 u8 resp[CMD_INTB_PIN_CFG_A10_NRESP];
706 const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
711 return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
712 args, ARRAY_SIZE(args),
713 resp, ARRAY_SIZE(resp),
714 SI476X_DEFAULT_TIMEOUT);
717 static int si476x_core_cmd_intb_pin_cfg_a20(struct si476x_core *core,
718 enum si476x_intb_config intb,
719 enum si476x_a1_config a1)
721 u8 resp[CMD_INTB_PIN_CFG_A20_NRESP];
722 const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
727 return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
728 args, ARRAY_SIZE(args),
729 resp, ARRAY_SIZE(resp),
730 SI476X_DEFAULT_TIMEOUT);
736 * si476x_cmd_am_rsq_status - send 'AM_RSQ_STATUS' command to the
738 * @core - device to send the command to
739 * @rsqack - if set command clears RSQINT, SNRINT, SNRLINT, RSSIHINT,
740 * RSSSILINT, BLENDINT, MULTHINT and MULTLINT
741 * @attune - when set the values in the status report are the values
742 * that were calculated at tune
743 * @cancel - abort ongoing seek/tune opertation
744 * @stcack - clear the STCINT bin in status register
745 * @report - all signal quality information retured by the command
746 * (if NULL then the output of the command is ignored)
748 * Function returns 0 on success and negative error code on failure
750 int si476x_core_cmd_am_rsq_status(struct si476x_core *core,
751 struct si476x_rsq_status_args *rsqargs,
752 struct si476x_rsq_status_report *report)
755 u8 resp[CMD_AM_RSQ_STATUS_NRESP];
756 const u8 args[CMD_AM_RSQ_STATUS_NARGS] = {
757 rsqargs->rsqack << 3 | rsqargs->attune << 2 |
758 rsqargs->cancel << 1 | rsqargs->stcack,
761 err = si476x_core_send_command(core, CMD_AM_RSQ_STATUS,
762 args, ARRAY_SIZE(args),
763 resp, ARRAY_SIZE(resp),
764 SI476X_DEFAULT_TIMEOUT);
766 * Besides getting received signal quality information this
767 * command can be used to just acknowledge different interrupt
768 * flags in those cases it is useless to copy and parse
769 * received data so user can pass NULL, and thus avoid
770 * unnecessary copying.
775 report->snrhint = 0b00001000 & resp[1];
776 report->snrlint = 0b00000100 & resp[1];
777 report->rssihint = 0b00000010 & resp[1];
778 report->rssilint = 0b00000001 & resp[1];
780 report->bltf = 0b10000000 & resp[2];
781 report->snr_ready = 0b00100000 & resp[2];
782 report->rssiready = 0b00001000 & resp[2];
783 report->afcrl = 0b00000010 & resp[2];
784 report->valid = 0b00000001 & resp[2];
786 report->readfreq = be16_to_cpup((__be16 *)(resp + 3));
787 report->freqoff = resp[5];
788 report->rssi = resp[6];
789 report->snr = resp[7];
790 report->lassi = resp[9];
791 report->hassi = resp[10];
792 report->mult = resp[11];
793 report->dev = resp[12];
797 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status);
799 int si476x_core_cmd_fm_acf_status(struct si476x_core *core,
800 struct si476x_acf_status_report *report)
803 u8 resp[CMD_FM_ACF_STATUS_NRESP];
804 const u8 args[CMD_FM_ACF_STATUS_NARGS] = {
811 err = si476x_core_send_command(core, CMD_FM_ACF_STATUS,
812 args, ARRAY_SIZE(args),
813 resp, ARRAY_SIZE(resp),
814 SI476X_DEFAULT_TIMEOUT);
818 report->blend_int = resp[1] & SI476X_ACF_BLEND_INT;
819 report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT;
820 report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT;
821 report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT;
822 report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT;
823 report->smute = resp[2] & SI476X_ACF_SMUTE;
824 report->smattn = resp[3] & SI476X_ACF_SMATTN;
825 report->chbw = resp[4];
826 report->hicut = resp[5];
827 report->hiblend = resp[6];
828 report->pilot = resp[7] & SI476X_ACF_PILOT;
829 report->stblend = resp[7] & SI476X_ACF_STBLEND;
833 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status);
835 int si476x_core_cmd_am_acf_status(struct si476x_core *core,
836 struct si476x_acf_status_report *report)
839 u8 resp[CMD_AM_ACF_STATUS_NRESP];
840 const u8 args[CMD_AM_ACF_STATUS_NARGS] = {
847 err = si476x_core_send_command(core, CMD_AM_ACF_STATUS,
848 args, ARRAY_SIZE(args),
849 resp, ARRAY_SIZE(resp),
850 SI476X_DEFAULT_TIMEOUT);
854 report->blend_int = resp[1] & SI476X_ACF_BLEND_INT;
855 report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT;
856 report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT;
857 report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT;
858 report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT;
859 report->smute = resp[2] & SI476X_ACF_SMUTE;
860 report->smattn = resp[3] & SI476X_ACF_SMATTN;
861 report->chbw = resp[4];
862 report->hicut = resp[5];
866 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status);
870 * si476x_cmd_fm_seek_start - send 'FM_SEEK_START' command to the
872 * @core - device to send the command to
873 * @seekup - if set the direction of the search is 'up'
874 * @wrap - if set seek wraps when hitting band limit
876 * This function begins search for a valid station. The station is
877 * considered valid when 'FM_VALID_SNR_THRESHOLD' and
878 * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
881 * Function returns 0 on success and negative error code on failure
883 int si476x_core_cmd_fm_seek_start(struct si476x_core *core,
884 bool seekup, bool wrap)
886 u8 resp[CMD_FM_SEEK_START_NRESP];
887 const u8 args[CMD_FM_SEEK_START_NARGS] = {
888 seekup << 3 | wrap << 2,
891 return si476x_cmd_tune_seek_freq(core, CMD_FM_SEEK_START,
895 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start);
898 * si476x_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the
900 * @core - device to send the command to
901 * @status_only - if set the data is not removed from RDSFIFO,
902 * RDSFIFOUSED is not decremented and data in all the
903 * rest RDS data contains the last valid info received
904 * @mtfifo if set the command clears RDS receive FIFO
905 * @intack if set the command clards the RDSINT bit.
907 * Function returns 0 on success and negative error code on failure
909 int si476x_core_cmd_fm_rds_status(struct si476x_core *core,
913 struct si476x_rds_status_report *report)
916 u8 resp[CMD_FM_RDS_STATUS_NRESP];
917 const u8 args[CMD_FM_RDS_STATUS_NARGS] = {
918 status_only << 2 | mtfifo << 1 | intack,
921 err = si476x_core_send_command(core, CMD_FM_RDS_STATUS,
922 args, ARRAY_SIZE(args),
923 resp, ARRAY_SIZE(resp),
924 SI476X_DEFAULT_TIMEOUT);
926 * Besides getting RDS status information this command can be
927 * used to just acknowledge different interrupt flags in those
928 * cases it is useless to copy and parse received data so user
929 * can pass NULL, and thus avoid unnecessary copying.
931 if (err < 0 || report == NULL)
934 report->rdstpptyint = 0b00010000 & resp[1];
935 report->rdspiint = 0b00001000 & resp[1];
936 report->rdssyncint = 0b00000010 & resp[1];
937 report->rdsfifoint = 0b00000001 & resp[1];
939 report->tpptyvalid = 0b00010000 & resp[2];
940 report->pivalid = 0b00001000 & resp[2];
941 report->rdssync = 0b00000010 & resp[2];
942 report->rdsfifolost = 0b00000001 & resp[2];
944 report->tp = 0b00100000 & resp[3];
945 report->pty = 0b00011111 & resp[3];
947 report->pi = be16_to_cpup((__be16 *)(resp + 4));
948 report->rdsfifoused = resp[6];
950 report->ble[V4L2_RDS_BLOCK_A] = 0b11000000 & resp[7];
951 report->ble[V4L2_RDS_BLOCK_B] = 0b00110000 & resp[7];
952 report->ble[V4L2_RDS_BLOCK_C] = 0b00001100 & resp[7];
953 report->ble[V4L2_RDS_BLOCK_D] = 0b00000011 & resp[7];
955 report->rds[V4L2_RDS_BLOCK_A].block = V4L2_RDS_BLOCK_A;
956 report->rds[V4L2_RDS_BLOCK_A].msb = resp[8];
957 report->rds[V4L2_RDS_BLOCK_A].lsb = resp[9];
959 report->rds[V4L2_RDS_BLOCK_B].block = V4L2_RDS_BLOCK_B;
960 report->rds[V4L2_RDS_BLOCK_B].msb = resp[10];
961 report->rds[V4L2_RDS_BLOCK_B].lsb = resp[11];
963 report->rds[V4L2_RDS_BLOCK_C].block = V4L2_RDS_BLOCK_C;
964 report->rds[V4L2_RDS_BLOCK_C].msb = resp[12];
965 report->rds[V4L2_RDS_BLOCK_C].lsb = resp[13];
967 report->rds[V4L2_RDS_BLOCK_D].block = V4L2_RDS_BLOCK_D;
968 report->rds[V4L2_RDS_BLOCK_D].msb = resp[14];
969 report->rds[V4L2_RDS_BLOCK_D].lsb = resp[15];
973 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status);
975 int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *core,
977 struct si476x_rds_blockcount_report *report)
980 u8 resp[CMD_FM_RDS_BLOCKCOUNT_NRESP];
981 const u8 args[CMD_FM_RDS_BLOCKCOUNT_NARGS] = {
988 err = si476x_core_send_command(core, CMD_FM_RDS_BLOCKCOUNT,
989 args, ARRAY_SIZE(args),
990 resp, ARRAY_SIZE(resp),
991 SI476X_DEFAULT_TIMEOUT);
994 report->expected = be16_to_cpup((__be16 *)(resp + 2));
995 report->received = be16_to_cpup((__be16 *)(resp + 4));
996 report->uncorrectable = be16_to_cpup((__be16 *)(resp + 6));
1001 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount);
1003 int si476x_core_cmd_fm_phase_diversity(struct si476x_core *core,
1004 enum si476x_phase_diversity_mode mode)
1006 u8 resp[CMD_FM_PHASE_DIVERSITY_NRESP];
1007 const u8 args[CMD_FM_PHASE_DIVERSITY_NARGS] = {
1011 return si476x_core_send_command(core, CMD_FM_PHASE_DIVERSITY,
1012 args, ARRAY_SIZE(args),
1013 resp, ARRAY_SIZE(resp),
1014 SI476X_DEFAULT_TIMEOUT);
1016 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity);
1018 * si476x_core_cmd_fm_phase_div_status() - get the phase diversity
1021 * @core: si476x device
1023 * NOTE caller must hold core lock
1025 * Function returns the value of the status bit in case of success and
1026 * negative error code in case of failre.
1028 int si476x_core_cmd_fm_phase_div_status(struct si476x_core *core)
1031 u8 resp[CMD_FM_PHASE_DIV_STATUS_NRESP];
1033 err = si476x_core_send_command(core, CMD_FM_PHASE_DIV_STATUS,
1035 resp, ARRAY_SIZE(resp),
1036 SI476X_DEFAULT_TIMEOUT);
1038 return (err < 0) ? err : resp[1];
1040 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status);
1044 * si476x_cmd_am_seek_start - send 'FM_SEEK_START' command to the
1046 * @core - device to send the command to
1047 * @seekup - if set the direction of the search is 'up'
1048 * @wrap - if set seek wraps when hitting band limit
1050 * This function begins search for a valid station. The station is
1051 * considered valid when 'FM_VALID_SNR_THRESHOLD' and
1052 * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
1055 * Function returns 0 on success and negative error code on failure
1057 int si476x_core_cmd_am_seek_start(struct si476x_core *core,
1058 bool seekup, bool wrap)
1060 u8 resp[CMD_AM_SEEK_START_NRESP];
1061 const u8 args[CMD_AM_SEEK_START_NARGS] = {
1062 seekup << 3 | wrap << 2,
1065 return si476x_cmd_tune_seek_freq(core, CMD_AM_SEEK_START,
1067 resp, sizeof(resp));
1069 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start);
1073 static int si476x_core_cmd_power_up_a10(struct si476x_core *core,
1074 struct si476x_power_up_args *puargs)
1076 u8 resp[CMD_POWER_UP_A10_NRESP];
1077 const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1078 const bool ctsen = (core->client->irq != 0);
1079 const u8 args[CMD_POWER_UP_A10_NARGS] = {
1080 0xF7, /* Reserved, always 0xF7 */
1081 0x3F & puargs->xcload, /* First two bits are reserved to be
1083 ctsen << 7 | intsel << 6 | 0x07, /* Last five bits
1085 * be written as 0x7 */
1086 puargs->func << 4 | puargs->freq,
1087 0x11, /* Reserved, always 0x11 */
1090 return si476x_core_send_command(core, CMD_POWER_UP,
1091 args, ARRAY_SIZE(args),
1092 resp, ARRAY_SIZE(resp),
1093 SI476X_TIMEOUT_POWER_UP);
1096 static int si476x_core_cmd_power_up_a20(struct si476x_core *core,
1097 struct si476x_power_up_args *puargs)
1099 u8 resp[CMD_POWER_UP_A20_NRESP];
1100 const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1101 const bool ctsen = (core->client->irq != 0);
1102 const u8 args[CMD_POWER_UP_A20_NARGS] = {
1103 puargs->ibias6x << 7 | puargs->xstart,
1104 0x3F & puargs->xcload, /* First two bits are reserved to be
1106 ctsen << 7 | intsel << 6 | puargs->fastboot << 5 |
1107 puargs->xbiashc << 3 | puargs->xbias,
1108 puargs->func << 4 | puargs->freq,
1109 0x10 | puargs->xmode,
1112 return si476x_core_send_command(core, CMD_POWER_UP,
1113 args, ARRAY_SIZE(args),
1114 resp, ARRAY_SIZE(resp),
1115 SI476X_TIMEOUT_POWER_UP);
1118 static int si476x_core_cmd_power_down_a10(struct si476x_core *core,
1119 struct si476x_power_down_args *pdargs)
1121 u8 resp[CMD_POWER_DOWN_A10_NRESP];
1123 return si476x_core_send_command(core, CMD_POWER_DOWN,
1125 resp, ARRAY_SIZE(resp),
1126 SI476X_DEFAULT_TIMEOUT);
1129 static int si476x_core_cmd_power_down_a20(struct si476x_core *core,
1130 struct si476x_power_down_args *pdargs)
1132 u8 resp[CMD_POWER_DOWN_A20_NRESP];
1133 const u8 args[CMD_POWER_DOWN_A20_NARGS] = {
1136 return si476x_core_send_command(core, CMD_POWER_DOWN,
1137 args, ARRAY_SIZE(args),
1138 resp, ARRAY_SIZE(resp),
1139 SI476X_DEFAULT_TIMEOUT);
1142 static int si476x_core_cmd_am_tune_freq_a10(struct si476x_core *core,
1143 struct si476x_tune_freq_args *tuneargs)
1146 const int am_freq = tuneargs->freq;
1147 u8 resp[CMD_AM_TUNE_FREQ_NRESP];
1148 const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1149 (tuneargs->hd << 6),
1154 return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, args,
1156 resp, sizeof(resp));
1159 static int si476x_core_cmd_am_tune_freq_a20(struct si476x_core *core,
1160 struct si476x_tune_freq_args *tuneargs)
1162 const int am_freq = tuneargs->freq;
1163 u8 resp[CMD_AM_TUNE_FREQ_NRESP];
1164 const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1165 (tuneargs->zifsr << 6) | (tuneargs->injside & 0b11),
1170 return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ,
1172 resp, sizeof(resp));
1175 static int si476x_core_cmd_fm_rsq_status_a10(struct si476x_core *core,
1176 struct si476x_rsq_status_args *rsqargs,
1177 struct si476x_rsq_status_report *report)
1180 u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1181 const u8 args[CMD_FM_RSQ_STATUS_A10_NARGS] = {
1182 rsqargs->rsqack << 3 | rsqargs->attune << 2 |
1183 rsqargs->cancel << 1 | rsqargs->stcack,
1186 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1187 args, ARRAY_SIZE(args),
1188 resp, ARRAY_SIZE(resp),
1189 SI476X_DEFAULT_TIMEOUT);
1191 * Besides getting received signal quality information this
1192 * command can be used to just acknowledge different interrupt
1193 * flags in those cases it is useless to copy and parse
1194 * received data so user can pass NULL, and thus avoid
1195 * unnecessary copying.
1197 if (err < 0 || report == NULL)
1200 report->multhint = 0b10000000 & resp[1];
1201 report->multlint = 0b01000000 & resp[1];
1202 report->snrhint = 0b00001000 & resp[1];
1203 report->snrlint = 0b00000100 & resp[1];
1204 report->rssihint = 0b00000010 & resp[1];
1205 report->rssilint = 0b00000001 & resp[1];
1207 report->bltf = 0b10000000 & resp[2];
1208 report->snr_ready = 0b00100000 & resp[2];
1209 report->rssiready = 0b00001000 & resp[2];
1210 report->afcrl = 0b00000010 & resp[2];
1211 report->valid = 0b00000001 & resp[2];
1213 report->readfreq = be16_to_cpup((__be16 *)(resp + 3));
1214 report->freqoff = resp[5];
1215 report->rssi = resp[6];
1216 report->snr = resp[7];
1217 report->lassi = resp[9];
1218 report->hassi = resp[10];
1219 report->mult = resp[11];
1220 report->dev = resp[12];
1221 report->readantcap = be16_to_cpup((__be16 *)(resp + 13));
1222 report->assi = resp[15];
1223 report->usn = resp[16];
1228 static int si476x_core_cmd_fm_rsq_status_a20(struct si476x_core *core,
1229 struct si476x_rsq_status_args *rsqargs,
1230 struct si476x_rsq_status_report *report)
1233 u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1234 const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1235 rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1236 rsqargs->attune << 2 | rsqargs->cancel << 1 |
1240 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1241 args, ARRAY_SIZE(args),
1242 resp, ARRAY_SIZE(resp),
1243 SI476X_DEFAULT_TIMEOUT);
1245 * Besides getting received signal quality information this
1246 * command can be used to just acknowledge different interrupt
1247 * flags in those cases it is useless to copy and parse
1248 * received data so user can pass NULL, and thus avoid
1249 * unnecessary copying.
1251 if (err < 0 || report == NULL)
1254 report->multhint = 0b10000000 & resp[1];
1255 report->multlint = 0b01000000 & resp[1];
1256 report->snrhint = 0b00001000 & resp[1];
1257 report->snrlint = 0b00000100 & resp[1];
1258 report->rssihint = 0b00000010 & resp[1];
1259 report->rssilint = 0b00000001 & resp[1];
1261 report->bltf = 0b10000000 & resp[2];
1262 report->snr_ready = 0b00100000 & resp[2];
1263 report->rssiready = 0b00001000 & resp[2];
1264 report->afcrl = 0b00000010 & resp[2];
1265 report->valid = 0b00000001 & resp[2];
1267 report->readfreq = be16_to_cpup((__be16 *)(resp + 3));
1268 report->freqoff = resp[5];
1269 report->rssi = resp[6];
1270 report->snr = resp[7];
1271 report->lassi = resp[9];
1272 report->hassi = resp[10];
1273 report->mult = resp[11];
1274 report->dev = resp[12];
1275 report->readantcap = be16_to_cpup((__be16 *)(resp + 13));
1276 report->assi = resp[15];
1277 report->usn = resp[16];
1283 static int si476x_core_cmd_fm_rsq_status_a30(struct si476x_core *core,
1284 struct si476x_rsq_status_args *rsqargs,
1285 struct si476x_rsq_status_report *report)
1288 u8 resp[CMD_FM_RSQ_STATUS_A30_NRESP];
1289 const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1290 rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1291 rsqargs->attune << 2 | rsqargs->cancel << 1 |
1295 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1296 args, ARRAY_SIZE(args),
1297 resp, ARRAY_SIZE(resp),
1298 SI476X_DEFAULT_TIMEOUT);
1300 * Besides getting received signal quality information this
1301 * command can be used to just acknowledge different interrupt
1302 * flags in those cases it is useless to copy and parse
1303 * received data so user can pass NULL, and thus avoid
1304 * unnecessary copying.
1306 if (err < 0 || report == NULL)
1309 report->multhint = 0b10000000 & resp[1];
1310 report->multlint = 0b01000000 & resp[1];
1311 report->snrhint = 0b00001000 & resp[1];
1312 report->snrlint = 0b00000100 & resp[1];
1313 report->rssihint = 0b00000010 & resp[1];
1314 report->rssilint = 0b00000001 & resp[1];
1316 report->bltf = 0b10000000 & resp[2];
1317 report->snr_ready = 0b00100000 & resp[2];
1318 report->rssiready = 0b00001000 & resp[2];
1319 report->injside = 0b00000100 & resp[2];
1320 report->afcrl = 0b00000010 & resp[2];
1321 report->valid = 0b00000001 & resp[2];
1323 report->readfreq = be16_to_cpup((__be16 *)(resp + 3));
1324 report->freqoff = resp[5];
1325 report->rssi = resp[6];
1326 report->snr = resp[7];
1327 report->issi = resp[8];
1328 report->lassi = resp[9];
1329 report->hassi = resp[10];
1330 report->mult = resp[11];
1331 report->dev = resp[12];
1332 report->readantcap = be16_to_cpup((__be16 *)(resp + 13));
1333 report->assi = resp[15];
1334 report->usn = resp[16];
1336 report->pilotdev = resp[17];
1337 report->rdsdev = resp[18];
1338 report->assidev = resp[19];
1339 report->strongdev = resp[20];
1340 report->rdspi = be16_to_cpup((__be16 *)(resp + 21));
1345 static int si476x_core_cmd_fm_tune_freq_a10(struct si476x_core *core,
1346 struct si476x_tune_freq_args *tuneargs)
1348 u8 resp[CMD_FM_TUNE_FREQ_NRESP];
1349 const u8 args[CMD_FM_TUNE_FREQ_A10_NARGS] = {
1350 (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1351 | (tuneargs->smoothmetrics << 2),
1352 msb(tuneargs->freq),
1353 lsb(tuneargs->freq),
1354 msb(tuneargs->antcap),
1355 lsb(tuneargs->antcap)
1358 return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1360 resp, sizeof(resp));
1363 static int si476x_core_cmd_fm_tune_freq_a20(struct si476x_core *core,
1364 struct si476x_tune_freq_args *tuneargs)
1366 u8 resp[CMD_FM_TUNE_FREQ_NRESP];
1367 const u8 args[CMD_FM_TUNE_FREQ_A20_NARGS] = {
1368 (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1369 | (tuneargs->smoothmetrics << 2) | (tuneargs->injside),
1370 msb(tuneargs->freq),
1371 lsb(tuneargs->freq),
1374 return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1376 resp, sizeof(resp));
1379 static int si476x_core_cmd_agc_status_a20(struct si476x_core *core,
1380 struct si476x_agc_status_report *report)
1383 u8 resp[CMD_AGC_STATUS_NRESP_A20];
1388 err = si476x_core_send_command(core, CMD_AGC_STATUS,
1390 resp, ARRAY_SIZE(resp),
1391 SI476X_DEFAULT_TIMEOUT);
1395 report->mxhi = resp[1] & SI476X_AGC_MXHI;
1396 report->mxlo = resp[1] & SI476X_AGC_MXLO;
1397 report->lnahi = resp[1] & SI476X_AGC_LNAHI;
1398 report->lnalo = resp[1] & SI476X_AGC_LNALO;
1399 report->fmagc1 = resp[2];
1400 report->fmagc2 = resp[3];
1401 report->pgagain = resp[4];
1402 report->fmwblang = resp[5];
1407 static int si476x_core_cmd_agc_status_a10(struct si476x_core *core,
1408 struct si476x_agc_status_report *report)
1411 u8 resp[CMD_AGC_STATUS_NRESP_A10];
1416 err = si476x_core_send_command(core, CMD_AGC_STATUS,
1418 resp, ARRAY_SIZE(resp),
1419 SI476X_DEFAULT_TIMEOUT);
1423 report->mxhi = resp[1] & SI476X_AGC_MXHI;
1424 report->mxlo = resp[1] & SI476X_AGC_MXLO;
1425 report->lnahi = resp[1] & SI476X_AGC_LNAHI;
1426 report->lnalo = resp[1] & SI476X_AGC_LNALO;
1431 typedef int (*tune_freq_func_t) (struct si476x_core *core,
1432 struct si476x_tune_freq_args *tuneargs);
1435 int (*power_up) (struct si476x_core *,
1436 struct si476x_power_up_args *);
1437 int (*power_down) (struct si476x_core *,
1438 struct si476x_power_down_args *);
1440 tune_freq_func_t fm_tune_freq;
1441 tune_freq_func_t am_tune_freq;
1443 int (*fm_rsq_status)(struct si476x_core *,
1444 struct si476x_rsq_status_args *,
1445 struct si476x_rsq_status_report *);
1447 int (*agc_status)(struct si476x_core *,
1448 struct si476x_agc_status_report *);
1449 int (*intb_pin_cfg)(struct si476x_core *core,
1450 enum si476x_intb_config intb,
1451 enum si476x_a1_config a1);
1452 } si476x_cmds_vtable[] = {
1453 [SI476X_REVISION_A10] = {
1454 .power_up = si476x_core_cmd_power_up_a10,
1455 .power_down = si476x_core_cmd_power_down_a10,
1456 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a10,
1457 .am_tune_freq = si476x_core_cmd_am_tune_freq_a10,
1458 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a10,
1459 .agc_status = si476x_core_cmd_agc_status_a10,
1460 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a10,
1462 [SI476X_REVISION_A20] = {
1463 .power_up = si476x_core_cmd_power_up_a20,
1464 .power_down = si476x_core_cmd_power_down_a20,
1465 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20,
1466 .am_tune_freq = si476x_core_cmd_am_tune_freq_a20,
1467 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a20,
1468 .agc_status = si476x_core_cmd_agc_status_a20,
1469 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20,
1471 [SI476X_REVISION_A30] = {
1472 .power_up = si476x_core_cmd_power_up_a20,
1473 .power_down = si476x_core_cmd_power_down_a20,
1474 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20,
1475 .am_tune_freq = si476x_core_cmd_am_tune_freq_a20,
1476 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a30,
1477 .agc_status = si476x_core_cmd_agc_status_a20,
1478 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20,
1482 int si476x_core_cmd_power_up(struct si476x_core *core,
1483 struct si476x_power_up_args *args)
1485 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1486 core->revision == -1);
1487 return si476x_cmds_vtable[core->revision].power_up(core, args);
1489 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up);
1491 int si476x_core_cmd_power_down(struct si476x_core *core,
1492 struct si476x_power_down_args *args)
1494 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1495 core->revision == -1);
1496 return si476x_cmds_vtable[core->revision].power_down(core, args);
1498 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down);
1500 int si476x_core_cmd_fm_tune_freq(struct si476x_core *core,
1501 struct si476x_tune_freq_args *args)
1503 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1504 core->revision == -1);
1505 return si476x_cmds_vtable[core->revision].fm_tune_freq(core, args);
1507 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq);
1509 int si476x_core_cmd_am_tune_freq(struct si476x_core *core,
1510 struct si476x_tune_freq_args *args)
1512 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1513 core->revision == -1);
1514 return si476x_cmds_vtable[core->revision].am_tune_freq(core, args);
1516 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq);
1518 int si476x_core_cmd_fm_rsq_status(struct si476x_core *core,
1519 struct si476x_rsq_status_args *args,
1520 struct si476x_rsq_status_report *report)
1523 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1524 core->revision == -1);
1525 return si476x_cmds_vtable[core->revision].fm_rsq_status(core, args,
1528 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status);
1530 int si476x_core_cmd_agc_status(struct si476x_core *core,
1531 struct si476x_agc_status_report *report)
1534 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1535 core->revision == -1);
1536 return si476x_cmds_vtable[core->revision].agc_status(core, report);
1538 EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status);
1540 int si476x_core_cmd_intb_pin_cfg(struct si476x_core *core,
1541 enum si476x_intb_config intb,
1542 enum si476x_a1_config a1)
1544 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1545 core->revision == -1);
1547 return si476x_cmds_vtable[core->revision].intb_pin_cfg(core, intb, a1);
1549 EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg);
1551 MODULE_LICENSE("GPL");
1552 MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
1553 MODULE_DESCRIPTION("API for command exchange for si476x");