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 <media/si476x.h>
31 #include <linux/mfd/si476x-core.h>
33 #define msb(x) ((u8)((u16) x >> 8))
34 #define lsb(x) ((u8)((u16) x & 0x00FF))
38 #define CMD_POWER_UP 0x01
39 #define CMD_POWER_UP_A10_NRESP 1
40 #define CMD_POWER_UP_A10_NARGS 5
42 #define CMD_POWER_UP_A20_NRESP 1
43 #define CMD_POWER_UP_A20_NARGS 5
45 #define POWER_UP_DELAY_MS 110
47 #define CMD_POWER_DOWN 0x11
48 #define CMD_POWER_DOWN_A10_NRESP 1
50 #define CMD_POWER_DOWN_A20_NRESP 1
51 #define CMD_POWER_DOWN_A20_NARGS 1
53 #define CMD_FUNC_INFO 0x12
54 #define CMD_FUNC_INFO_NRESP 7
56 #define CMD_SET_PROPERTY 0x13
57 #define CMD_SET_PROPERTY_NARGS 5
58 #define CMD_SET_PROPERTY_NRESP 1
60 #define CMD_GET_PROPERTY 0x14
61 #define CMD_GET_PROPERTY_NARGS 3
62 #define CMD_GET_PROPERTY_NRESP 4
64 #define CMD_AGC_STATUS 0x17
65 #define CMD_AGC_STATUS_NRESP_A10 2
66 #define CMD_AGC_STATUS_NRESP_A20 6
68 #define PIN_CFG_BYTE(x) (0x7F & (x))
69 #define CMD_DIG_AUDIO_PIN_CFG 0x18
70 #define CMD_DIG_AUDIO_PIN_CFG_NARGS 4
71 #define CMD_DIG_AUDIO_PIN_CFG_NRESP 5
73 #define CMD_ZIF_PIN_CFG 0x19
74 #define CMD_ZIF_PIN_CFG_NARGS 4
75 #define CMD_ZIF_PIN_CFG_NRESP 5
77 #define CMD_IC_LINK_GPO_CTL_PIN_CFG 0x1A
78 #define CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS 4
79 #define CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP 5
81 #define CMD_ANA_AUDIO_PIN_CFG 0x1B
82 #define CMD_ANA_AUDIO_PIN_CFG_NARGS 1
83 #define CMD_ANA_AUDIO_PIN_CFG_NRESP 2
85 #define CMD_INTB_PIN_CFG 0x1C
86 #define CMD_INTB_PIN_CFG_NARGS 2
87 #define CMD_INTB_PIN_CFG_A10_NRESP 6
88 #define CMD_INTB_PIN_CFG_A20_NRESP 3
90 #define CMD_FM_TUNE_FREQ 0x30
91 #define CMD_FM_TUNE_FREQ_A10_NARGS 5
92 #define CMD_FM_TUNE_FREQ_A20_NARGS 3
93 #define CMD_FM_TUNE_FREQ_NRESP 1
95 #define CMD_FM_RSQ_STATUS 0x32
97 #define CMD_FM_RSQ_STATUS_A10_NARGS 1
98 #define CMD_FM_RSQ_STATUS_A10_NRESP 17
99 #define CMD_FM_RSQ_STATUS_A30_NARGS 1
100 #define CMD_FM_RSQ_STATUS_A30_NRESP 23
103 #define CMD_FM_SEEK_START 0x31
104 #define CMD_FM_SEEK_START_NARGS 1
105 #define CMD_FM_SEEK_START_NRESP 1
107 #define CMD_FM_RDS_STATUS 0x36
108 #define CMD_FM_RDS_STATUS_NARGS 1
109 #define CMD_FM_RDS_STATUS_NRESP 16
111 #define CMD_FM_RDS_BLOCKCOUNT 0x37
112 #define CMD_FM_RDS_BLOCKCOUNT_NARGS 1
113 #define CMD_FM_RDS_BLOCKCOUNT_NRESP 8
115 #define CMD_FM_PHASE_DIVERSITY 0x38
116 #define CMD_FM_PHASE_DIVERSITY_NARGS 1
117 #define CMD_FM_PHASE_DIVERSITY_NRESP 1
119 #define CMD_FM_PHASE_DIV_STATUS 0x39
120 #define CMD_FM_PHASE_DIV_STATUS_NRESP 2
122 #define CMD_AM_TUNE_FREQ 0x40
123 #define CMD_AM_TUNE_FREQ_NARGS 3
124 #define CMD_AM_TUNE_FREQ_NRESP 1
126 #define CMD_AM_RSQ_STATUS 0x42
127 #define CMD_AM_RSQ_STATUS_NARGS 1
128 #define CMD_AM_RSQ_STATUS_NRESP 13
130 #define CMD_AM_SEEK_START 0x41
131 #define CMD_AM_SEEK_START_NARGS 1
132 #define CMD_AM_SEEK_START_NRESP 1
135 #define CMD_AM_ACF_STATUS 0x45
136 #define CMD_AM_ACF_STATUS_NRESP 6
137 #define CMD_AM_ACF_STATUS_NARGS 1
139 #define CMD_FM_ACF_STATUS 0x35
140 #define CMD_FM_ACF_STATUS_NRESP 8
141 #define CMD_FM_ACF_STATUS_NARGS 1
143 #define CMD_MAX_ARGS_COUNT (10)
146 enum si476x_acf_status_report_bits {
147 SI476X_ACF_BLEND_INT = (1 << 4),
148 SI476X_ACF_HIBLEND_INT = (1 << 3),
149 SI476X_ACF_HICUT_INT = (1 << 2),
150 SI476X_ACF_CHBW_INT = (1 << 1),
151 SI476X_ACF_SOFTMUTE_INT = (1 << 0),
153 SI476X_ACF_SMUTE = (1 << 0),
154 SI476X_ACF_SMATTN = 0b11111,
155 SI476X_ACF_PILOT = (1 << 7),
156 SI476X_ACF_STBLEND = ~SI476X_ACF_PILOT,
159 enum si476x_agc_status_report_bits {
160 SI476X_AGC_MXHI = (1 << 5),
161 SI476X_AGC_MXLO = (1 << 4),
162 SI476X_AGC_LNAHI = (1 << 3),
163 SI476X_AGC_LNALO = (1 << 2),
167 SI476X_ERR_BAD_COMMAND = 0x10,
168 SI476X_ERR_BAD_ARG1 = 0x11,
169 SI476X_ERR_BAD_ARG2 = 0x12,
170 SI476X_ERR_BAD_ARG3 = 0x13,
171 SI476X_ERR_BAD_ARG4 = 0x14,
172 SI476X_ERR_BUSY = 0x18,
173 SI476X_ERR_BAD_INTERNAL_MEMORY = 0x20,
174 SI476X_ERR_BAD_PATCH = 0x30,
175 SI476X_ERR_BAD_BOOT_MODE = 0x31,
176 SI476X_ERR_BAD_PROPERTY = 0x40,
179 static int si476x_core_parse_and_nag_about_error(struct si476x_core *core)
185 if (core->revision != SI476X_REVISION_A10) {
186 err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV,
187 buffer, sizeof(buffer));
188 if (err == sizeof(buffer)) {
190 case SI476X_ERR_BAD_COMMAND:
191 cause = "Bad command";
194 case SI476X_ERR_BAD_ARG1:
195 cause = "Bad argument #1";
198 case SI476X_ERR_BAD_ARG2:
199 cause = "Bad argument #2";
202 case SI476X_ERR_BAD_ARG3:
203 cause = "Bad argument #3";
206 case SI476X_ERR_BAD_ARG4:
207 cause = "Bad argument #4";
210 case SI476X_ERR_BUSY:
211 cause = "Chip is busy";
214 case SI476X_ERR_BAD_INTERNAL_MEMORY:
215 cause = "Bad internal memory";
218 case SI476X_ERR_BAD_PATCH:
222 case SI476X_ERR_BAD_BOOT_MODE:
223 cause = "Bad boot mode";
226 case SI476X_ERR_BAD_PROPERTY:
227 cause = "Bad property";
235 dev_err(&core->client->dev,
236 "[Chip error status]: %s\n", cause);
238 dev_err(&core->client->dev,
239 "Failed to fetch error code\n");
240 err = (err >= 0) ? -EIO : err;
250 * si476x_core_send_command() - sends a command to si476x and waits its
252 * @core: si476x_device structure for the device we are
254 * @command: command id
255 * @args: command arguments we are sending
256 * @argn: actual size of @args
257 * @response: buffer to place the expected response from the device
258 * @respn: actual size of @response
259 * @usecs: amount of time to wait before reading the response (in
262 * Function returns 0 on succsess and negative error code on
265 static int si476x_core_send_command(struct si476x_core *core,
273 struct i2c_client *client = core->client;
275 u8 data[CMD_MAX_ARGS_COUNT + 1];
277 if (argn > CMD_MAX_ARGS_COUNT) {
282 if (!client->adapter) {
287 /* First send the command and its arguments */
289 memcpy(&data[1], args, argn);
290 dev_dbg(&client->dev, "Command:\n %*ph\n", argn + 1, data);
292 err = si476x_core_i2c_xfer(core, SI476X_I2C_SEND,
293 (char *) data, argn + 1);
294 if (err != argn + 1) {
295 dev_err(&core->client->dev,
296 "Error while sending command 0x%02x\n",
298 err = (err >= 0) ? -EIO : err;
301 /* Set CTS to zero only after the command is send to avoid
302 * possible racing conditions when working in polling mode */
303 atomic_set(&core->cts, 0);
305 /* if (unlikely(command == CMD_POWER_DOWN) */
306 if (!wait_event_timeout(core->command,
307 atomic_read(&core->cts),
308 usecs_to_jiffies(usecs) + 1))
309 dev_warn(&core->client->dev,
310 "(%s) [CMD 0x%02x] Answer timeout.\n",
314 When working in polling mode, for some reason the tuner will
315 report CTS bit as being set in the first status byte read,
316 but all the consequtive ones will return zeros until the
317 tuner is actually completed the POWER_UP command. To
318 workaround that we wait for second CTS to be reported
320 if (unlikely(!core->client->irq && command == CMD_POWER_UP)) {
321 if (!wait_event_timeout(core->command,
322 atomic_read(&core->cts),
323 usecs_to_jiffies(usecs) + 1))
324 dev_warn(&core->client->dev,
325 "(%s) Power up took too much time.\n",
329 /* Then get the response */
330 err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, resp, respn);
332 dev_err(&core->client->dev,
333 "Error while reading response for command 0x%02x\n",
335 err = (err >= 0) ? -EIO : err;
338 dev_dbg(&client->dev, "Response:\n %*ph\n", respn, resp);
342 if (resp[0] & SI476X_ERR) {
343 dev_err(&core->client->dev,
344 "[CMD 0x%02x] Chip set error flag\n", command);
345 err = si476x_core_parse_and_nag_about_error(core);
349 if (!(resp[0] & SI476X_CTS))
355 static int si476x_cmd_clear_stc(struct si476x_core *core)
358 struct si476x_rsq_status_args args = {
366 switch (core->power_up_parameters.func) {
367 case SI476X_FUNC_FM_RECEIVER:
368 err = si476x_core_cmd_fm_rsq_status(core, &args, NULL);
370 case SI476X_FUNC_AM_RECEIVER:
371 err = si476x_core_cmd_am_rsq_status(core, &args, NULL);
380 static int si476x_cmd_tune_seek_freq(struct si476x_core *core,
382 const uint8_t args[], size_t argn,
383 uint8_t *resp, size_t respn)
388 atomic_set(&core->stc, 0);
389 err = si476x_core_send_command(core, cmd, args, argn, resp, respn,
390 SI476X_TIMEOUT_TUNE);
392 wait_event_killable(core->tuning,
393 atomic_read(&core->stc));
394 si476x_cmd_clear_stc(core);
401 * si476x_cmd_func_info() - send 'FUNC_INFO' command to the device
402 * @core: device to send the command to
403 * @info: struct si476x_func_info to fill all the information
404 * returned by the command
406 * The command requests the firmware and patch version for currently
407 * loaded firmware (dependent on the function of the device FM/AM/WB)
409 * Function returns 0 on succsess and negative error code on
412 int si476x_core_cmd_func_info(struct si476x_core *core,
413 struct si476x_func_info *info)
416 u8 resp[CMD_FUNC_INFO_NRESP];
418 err = si476x_core_send_command(core, CMD_FUNC_INFO,
420 resp, ARRAY_SIZE(resp),
421 SI476X_DEFAULT_TIMEOUT);
423 info->firmware.major = resp[1];
424 info->firmware.minor[0] = resp[2];
425 info->firmware.minor[1] = resp[3];
427 info->patch_id = ((u16) resp[4] << 8) | resp[5];
428 info->func = resp[6];
432 EXPORT_SYMBOL_GPL(si476x_core_cmd_func_info);
435 * si476x_cmd_set_property() - send 'SET_PROPERTY' command to the device
436 * @core: device to send the command to
437 * @property: property address
438 * @value: property value
440 * Function returns 0 on succsess and negative error code on
443 int si476x_core_cmd_set_property(struct si476x_core *core,
444 u16 property, u16 value)
446 u8 resp[CMD_SET_PROPERTY_NRESP];
447 const u8 args[CMD_SET_PROPERTY_NARGS] = {
455 return si476x_core_send_command(core, CMD_SET_PROPERTY,
456 args, ARRAY_SIZE(args),
457 resp, ARRAY_SIZE(resp),
458 SI476X_DEFAULT_TIMEOUT);
460 EXPORT_SYMBOL_GPL(si476x_core_cmd_set_property);
463 * si476x_cmd_get_property() - send 'GET_PROPERTY' command to the device
464 * @core: device to send the command to
465 * @property: property address
467 * Function return the value of property as u16 on success or a
468 * negative error on failure
470 int si476x_core_cmd_get_property(struct si476x_core *core, u16 property)
473 u8 resp[CMD_GET_PROPERTY_NRESP];
474 const u8 args[CMD_GET_PROPERTY_NARGS] = {
480 err = si476x_core_send_command(core, CMD_GET_PROPERTY,
481 args, ARRAY_SIZE(args),
482 resp, ARRAY_SIZE(resp),
483 SI476X_DEFAULT_TIMEOUT);
487 return be16_to_cpup((__be16 *)(resp + 2));
489 EXPORT_SYMBOL_GPL(si476x_core_cmd_get_property);
492 * si476x_cmd_dig_audio_pin_cfg() - send 'DIG_AUDIO_PIN_CFG' command to
494 * @core: device to send the command to
495 * @dclk: DCLK pin function configuration:
496 * #SI476X_DCLK_NOOP - do not modify the behaviour
497 * #SI476X_DCLK_TRISTATE - put the pin in tristate condition,
498 * enable 1MOhm pulldown
499 * #SI476X_DCLK_DAUDIO - set the pin to be a part of digital
501 * @dfs: DFS pin function configuration:
502 * #SI476X_DFS_NOOP - do not modify the behaviour
503 * #SI476X_DFS_TRISTATE - put the pin in tristate condition,
504 * enable 1MOhm pulldown
505 * SI476X_DFS_DAUDIO - set the pin to be a part of digital
507 * @dout - DOUT pin function configuration:
508 * SI476X_DOUT_NOOP - do not modify the behaviour
509 * SI476X_DOUT_TRISTATE - put the pin in tristate condition,
510 * enable 1MOhm pulldown
511 * SI476X_DOUT_I2S_OUTPUT - set this pin to be digital out on I2S
513 * SI476X_DOUT_I2S_INPUT - set this pin to be digital in on I2S
515 * @xout - XOUT pin function configuration:
516 * SI476X_XOUT_NOOP - do not modify the behaviour
517 * SI476X_XOUT_TRISTATE - put the pin in tristate condition,
518 * enable 1MOhm pulldown
519 * SI476X_XOUT_I2S_INPUT - set this pin to be digital in on I2S
521 * SI476X_XOUT_MODE_SELECT - set this pin to be the input that
522 * selects the mode of the I2S audio
523 * combiner (analog or HD)
524 * [SI4761/63/65/67 Only]
526 * Function returns 0 on success and negative error code on failure
528 int si476x_core_cmd_dig_audio_pin_cfg(struct si476x_core *core,
529 enum si476x_dclk_config dclk,
530 enum si476x_dfs_config dfs,
531 enum si476x_dout_config dout,
532 enum si476x_xout_config xout)
534 u8 resp[CMD_DIG_AUDIO_PIN_CFG_NRESP];
535 const u8 args[CMD_DIG_AUDIO_PIN_CFG_NARGS] = {
542 return si476x_core_send_command(core, CMD_DIG_AUDIO_PIN_CFG,
543 args, ARRAY_SIZE(args),
544 resp, ARRAY_SIZE(resp),
545 SI476X_DEFAULT_TIMEOUT);
547 EXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg);
550 * si476x_cmd_zif_pin_cfg - send 'ZIF_PIN_CFG_COMMAND'
551 * @core - device to send the command to
552 * @iqclk - IQCL pin function configuration:
553 * SI476X_IQCLK_NOOP - do not modify the behaviour
554 * SI476X_IQCLK_TRISTATE - put the pin in tristate condition,
555 * enable 1MOhm pulldown
556 * SI476X_IQCLK_IQ - set pin to be a part of I/Q interace
558 * @iqfs - IQFS pin function configuration:
559 * SI476X_IQFS_NOOP - do not modify the behaviour
560 * SI476X_IQFS_TRISTATE - put the pin in tristate condition,
561 * enable 1MOhm pulldown
562 * SI476X_IQFS_IQ - set pin to be a part of I/Q interace
564 * @iout - IOUT pin function configuration:
565 * SI476X_IOUT_NOOP - do not modify the behaviour
566 * SI476X_IOUT_TRISTATE - put the pin in tristate condition,
567 * enable 1MOhm pulldown
568 * SI476X_IOUT_OUTPUT - set pin to be I out
569 * @qout - QOUT pin function configuration:
570 * SI476X_QOUT_NOOP - do not modify the behaviour
571 * SI476X_QOUT_TRISTATE - put the pin in tristate condition,
572 * enable 1MOhm pulldown
573 * SI476X_QOUT_OUTPUT - set pin to be Q out
575 * Function returns 0 on success and negative error code on failure
577 int si476x_core_cmd_zif_pin_cfg(struct si476x_core *core,
578 enum si476x_iqclk_config iqclk,
579 enum si476x_iqfs_config iqfs,
580 enum si476x_iout_config iout,
581 enum si476x_qout_config qout)
583 u8 resp[CMD_ZIF_PIN_CFG_NRESP];
584 const u8 args[CMD_ZIF_PIN_CFG_NARGS] = {
591 return si476x_core_send_command(core, CMD_ZIF_PIN_CFG,
592 args, ARRAY_SIZE(args),
593 resp, ARRAY_SIZE(resp),
594 SI476X_DEFAULT_TIMEOUT);
596 EXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg);
599 * si476x_cmd_ic_link_gpo_ctl_pin_cfg - send
600 * 'IC_LINK_GPIO_CTL_PIN_CFG' comand to the device
601 * @core - device to send the command to
602 * @icin - ICIN pin function configuration:
603 * SI476X_ICIN_NOOP - do not modify the behaviour
604 * SI476X_ICIN_TRISTATE - put the pin in tristate condition,
605 * enable 1MOhm pulldown
606 * SI476X_ICIN_GPO1_HIGH - set pin to be an output, drive it high
607 * SI476X_ICIN_GPO1_LOW - set pin to be an output, drive it low
608 * SI476X_ICIN_IC_LINK - set the pin to be a part of Inter-Chip link
609 * @icip - ICIP pin function configuration:
610 * SI476X_ICIP_NOOP - do not modify the behaviour
611 * SI476X_ICIP_TRISTATE - put the pin in tristate condition,
612 * enable 1MOhm pulldown
613 * SI476X_ICIP_GPO1_HIGH - set pin to be an output, drive it high
614 * SI476X_ICIP_GPO1_LOW - set pin to be an output, drive it low
615 * SI476X_ICIP_IC_LINK - set the pin to be a part of Inter-Chip link
616 * @icon - ICON pin function configuration:
617 * SI476X_ICON_NOOP - do not modify the behaviour
618 * SI476X_ICON_TRISTATE - put the pin in tristate condition,
619 * enable 1MOhm pulldown
620 * SI476X_ICON_I2S - set the pin to be a part of audio
621 * interface in slave mode (DCLK)
622 * SI476X_ICON_IC_LINK - set the pin to be a part of Inter-Chip link
623 * @icop - ICOP pin function configuration:
624 * SI476X_ICOP_NOOP - do not modify the behaviour
625 * SI476X_ICOP_TRISTATE - put the pin in tristate condition,
626 * enable 1MOhm pulldown
627 * SI476X_ICOP_I2S - set the pin to be a part of audio
628 * interface in slave mode (DOUT)
629 * [Si4761/63/65/67 Only]
630 * SI476X_ICOP_IC_LINK - set the pin to be a part of Inter-Chip link
632 * Function returns 0 on success and negative error code on failure
634 int si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(struct si476x_core *core,
635 enum si476x_icin_config icin,
636 enum si476x_icip_config icip,
637 enum si476x_icon_config icon,
638 enum si476x_icop_config icop)
640 u8 resp[CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP];
641 const u8 args[CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS] = {
648 return si476x_core_send_command(core, CMD_IC_LINK_GPO_CTL_PIN_CFG,
649 args, ARRAY_SIZE(args),
650 resp, ARRAY_SIZE(resp),
651 SI476X_DEFAULT_TIMEOUT);
653 EXPORT_SYMBOL_GPL(si476x_core_cmd_ic_link_gpo_ctl_pin_cfg);
656 * si476x_cmd_ana_audio_pin_cfg - send 'ANA_AUDIO_PIN_CFG' to the
658 * @core - device to send the command to
659 * @lrout - LROUT pin function configuration:
660 * SI476X_LROUT_NOOP - do not modify the behaviour
661 * SI476X_LROUT_TRISTATE - put the pin in tristate condition,
662 * enable 1MOhm pulldown
663 * SI476X_LROUT_AUDIO - set pin to be audio output
664 * SI476X_LROUT_MPX - set pin to be MPX output
666 * Function returns 0 on success and negative error code on failure
668 int si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *core,
669 enum si476x_lrout_config lrout)
671 u8 resp[CMD_ANA_AUDIO_PIN_CFG_NRESP];
672 const u8 args[CMD_ANA_AUDIO_PIN_CFG_NARGS] = {
676 return si476x_core_send_command(core, CMD_ANA_AUDIO_PIN_CFG,
677 args, ARRAY_SIZE(args),
678 resp, ARRAY_SIZE(resp),
679 SI476X_DEFAULT_TIMEOUT);
681 EXPORT_SYMBOL_GPL(si476x_core_cmd_ana_audio_pin_cfg);
685 * si476x_cmd_intb_pin_cfg - send 'INTB_PIN_CFG' command to the device
686 * @core - device to send the command to
687 * @intb - INTB pin function configuration:
688 * SI476X_INTB_NOOP - do not modify the behaviour
689 * SI476X_INTB_TRISTATE - put the pin in tristate condition,
690 * enable 1MOhm pulldown
691 * SI476X_INTB_DAUDIO - set pin to be a part of digital
692 * audio interface in slave mode
693 * SI476X_INTB_IRQ - set pin to be an interrupt request line
694 * @a1 - A1 pin function configuration:
695 * SI476X_A1_NOOP - do not modify the behaviour
696 * SI476X_A1_TRISTATE - put the pin in tristate condition,
697 * enable 1MOhm pulldown
698 * SI476X_A1_IRQ - set pin to be an interrupt request line
700 * Function returns 0 on success and negative error code on failure
702 static int si476x_core_cmd_intb_pin_cfg_a10(struct si476x_core *core,
703 enum si476x_intb_config intb,
704 enum si476x_a1_config a1)
706 u8 resp[CMD_INTB_PIN_CFG_A10_NRESP];
707 const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
712 return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
713 args, ARRAY_SIZE(args),
714 resp, ARRAY_SIZE(resp),
715 SI476X_DEFAULT_TIMEOUT);
718 static int si476x_core_cmd_intb_pin_cfg_a20(struct si476x_core *core,
719 enum si476x_intb_config intb,
720 enum si476x_a1_config a1)
722 u8 resp[CMD_INTB_PIN_CFG_A20_NRESP];
723 const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
728 return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
729 args, ARRAY_SIZE(args),
730 resp, ARRAY_SIZE(resp),
731 SI476X_DEFAULT_TIMEOUT);
737 * si476x_cmd_am_rsq_status - send 'AM_RSQ_STATUS' command to the
739 * @core - device to send the command to
740 * @rsqack - if set command clears RSQINT, SNRINT, SNRLINT, RSSIHINT,
741 * RSSSILINT, BLENDINT, MULTHINT and MULTLINT
742 * @attune - when set the values in the status report are the values
743 * that were calculated at tune
744 * @cancel - abort ongoing seek/tune opertation
745 * @stcack - clear the STCINT bin in status register
746 * @report - all signal quality information retured by the command
747 * (if NULL then the output of the command is ignored)
749 * Function returns 0 on success and negative error code on failure
751 int si476x_core_cmd_am_rsq_status(struct si476x_core *core,
752 struct si476x_rsq_status_args *rsqargs,
753 struct si476x_rsq_status_report *report)
756 u8 resp[CMD_AM_RSQ_STATUS_NRESP];
757 const u8 args[CMD_AM_RSQ_STATUS_NARGS] = {
758 rsqargs->rsqack << 3 | rsqargs->attune << 2 |
759 rsqargs->cancel << 1 | rsqargs->stcack,
762 err = si476x_core_send_command(core, CMD_AM_RSQ_STATUS,
763 args, ARRAY_SIZE(args),
764 resp, ARRAY_SIZE(resp),
765 SI476X_DEFAULT_TIMEOUT);
767 * Besides getting received signal quality information this
768 * command can be used to just acknowledge different interrupt
769 * flags in those cases it is useless to copy and parse
770 * received data so user can pass NULL, and thus avoid
771 * unnecessary copying.
776 report->snrhint = 0b00001000 & resp[1];
777 report->snrlint = 0b00000100 & resp[1];
778 report->rssihint = 0b00000010 & resp[1];
779 report->rssilint = 0b00000001 & resp[1];
781 report->bltf = 0b10000000 & resp[2];
782 report->snr_ready = 0b00100000 & resp[2];
783 report->rssiready = 0b00001000 & resp[2];
784 report->afcrl = 0b00000010 & resp[2];
785 report->valid = 0b00000001 & resp[2];
787 report->readfreq = be16_to_cpup((__be16 *)(resp + 3));
788 report->freqoff = resp[5];
789 report->rssi = resp[6];
790 report->snr = resp[7];
791 report->lassi = resp[9];
792 report->hassi = resp[10];
793 report->mult = resp[11];
794 report->dev = resp[12];
798 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status);
800 int si476x_core_cmd_fm_acf_status(struct si476x_core *core,
801 struct si476x_acf_status_report *report)
804 u8 resp[CMD_FM_ACF_STATUS_NRESP];
805 const u8 args[CMD_FM_ACF_STATUS_NARGS] = {
812 err = si476x_core_send_command(core, CMD_FM_ACF_STATUS,
813 args, ARRAY_SIZE(args),
814 resp, ARRAY_SIZE(resp),
815 SI476X_DEFAULT_TIMEOUT);
819 report->blend_int = resp[1] & SI476X_ACF_BLEND_INT;
820 report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT;
821 report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT;
822 report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT;
823 report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT;
824 report->smute = resp[2] & SI476X_ACF_SMUTE;
825 report->smattn = resp[3] & SI476X_ACF_SMATTN;
826 report->chbw = resp[4];
827 report->hicut = resp[5];
828 report->hiblend = resp[6];
829 report->pilot = resp[7] & SI476X_ACF_PILOT;
830 report->stblend = resp[7] & SI476X_ACF_STBLEND;
834 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status);
836 int si476x_core_cmd_am_acf_status(struct si476x_core *core,
837 struct si476x_acf_status_report *report)
840 u8 resp[CMD_AM_ACF_STATUS_NRESP];
841 const u8 args[CMD_AM_ACF_STATUS_NARGS] = {
848 err = si476x_core_send_command(core, CMD_AM_ACF_STATUS,
849 args, ARRAY_SIZE(args),
850 resp, ARRAY_SIZE(resp),
851 SI476X_DEFAULT_TIMEOUT);
855 report->blend_int = resp[1] & SI476X_ACF_BLEND_INT;
856 report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT;
857 report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT;
858 report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT;
859 report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT;
860 report->smute = resp[2] & SI476X_ACF_SMUTE;
861 report->smattn = resp[3] & SI476X_ACF_SMATTN;
862 report->chbw = resp[4];
863 report->hicut = resp[5];
867 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status);
871 * si476x_cmd_fm_seek_start - send 'FM_SEEK_START' command to the
873 * @core - device to send the command to
874 * @seekup - if set the direction of the search is 'up'
875 * @wrap - if set seek wraps when hitting band limit
877 * This function begins search for a valid station. The station is
878 * considered valid when 'FM_VALID_SNR_THRESHOLD' and
879 * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
882 * Function returns 0 on success and negative error code on failure
884 int si476x_core_cmd_fm_seek_start(struct si476x_core *core,
885 bool seekup, bool wrap)
887 u8 resp[CMD_FM_SEEK_START_NRESP];
888 const u8 args[CMD_FM_SEEK_START_NARGS] = {
889 seekup << 3 | wrap << 2,
892 return si476x_cmd_tune_seek_freq(core, CMD_FM_SEEK_START,
896 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start);
899 * si476x_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the
901 * @core - device to send the command to
902 * @status_only - if set the data is not removed from RDSFIFO,
903 * RDSFIFOUSED is not decremented and data in all the
904 * rest RDS data contains the last valid info received
905 * @mtfifo if set the command clears RDS receive FIFO
906 * @intack if set the command clards the RDSINT bit.
908 * Function returns 0 on success and negative error code on failure
910 int si476x_core_cmd_fm_rds_status(struct si476x_core *core,
914 struct si476x_rds_status_report *report)
917 u8 resp[CMD_FM_RDS_STATUS_NRESP];
918 const u8 args[CMD_FM_RDS_STATUS_NARGS] = {
919 status_only << 2 | mtfifo << 1 | intack,
922 err = si476x_core_send_command(core, CMD_FM_RDS_STATUS,
923 args, ARRAY_SIZE(args),
924 resp, ARRAY_SIZE(resp),
925 SI476X_DEFAULT_TIMEOUT);
927 * Besides getting RDS status information this command can be
928 * used to just acknowledge different interrupt flags in those
929 * cases it is useless to copy and parse received data so user
930 * can pass NULL, and thus avoid unnecessary copying.
932 if (err < 0 || report == NULL)
935 report->rdstpptyint = 0b00010000 & resp[1];
936 report->rdspiint = 0b00001000 & resp[1];
937 report->rdssyncint = 0b00000010 & resp[1];
938 report->rdsfifoint = 0b00000001 & resp[1];
940 report->tpptyvalid = 0b00010000 & resp[2];
941 report->pivalid = 0b00001000 & resp[2];
942 report->rdssync = 0b00000010 & resp[2];
943 report->rdsfifolost = 0b00000001 & resp[2];
945 report->tp = 0b00100000 & resp[3];
946 report->pty = 0b00011111 & resp[3];
948 report->pi = be16_to_cpup((__be16 *)(resp + 4));
949 report->rdsfifoused = resp[6];
951 report->ble[V4L2_RDS_BLOCK_A] = 0b11000000 & resp[7];
952 report->ble[V4L2_RDS_BLOCK_B] = 0b00110000 & resp[7];
953 report->ble[V4L2_RDS_BLOCK_C] = 0b00001100 & resp[7];
954 report->ble[V4L2_RDS_BLOCK_D] = 0b00000011 & resp[7];
956 report->rds[V4L2_RDS_BLOCK_A].block = V4L2_RDS_BLOCK_A;
957 report->rds[V4L2_RDS_BLOCK_A].msb = resp[8];
958 report->rds[V4L2_RDS_BLOCK_A].lsb = resp[9];
960 report->rds[V4L2_RDS_BLOCK_B].block = V4L2_RDS_BLOCK_B;
961 report->rds[V4L2_RDS_BLOCK_B].msb = resp[10];
962 report->rds[V4L2_RDS_BLOCK_B].lsb = resp[11];
964 report->rds[V4L2_RDS_BLOCK_C].block = V4L2_RDS_BLOCK_C;
965 report->rds[V4L2_RDS_BLOCK_C].msb = resp[12];
966 report->rds[V4L2_RDS_BLOCK_C].lsb = resp[13];
968 report->rds[V4L2_RDS_BLOCK_D].block = V4L2_RDS_BLOCK_D;
969 report->rds[V4L2_RDS_BLOCK_D].msb = resp[14];
970 report->rds[V4L2_RDS_BLOCK_D].lsb = resp[15];
974 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status);
976 int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *core,
978 struct si476x_rds_blockcount_report *report)
981 u8 resp[CMD_FM_RDS_BLOCKCOUNT_NRESP];
982 const u8 args[CMD_FM_RDS_BLOCKCOUNT_NARGS] = {
989 err = si476x_core_send_command(core, CMD_FM_RDS_BLOCKCOUNT,
990 args, ARRAY_SIZE(args),
991 resp, ARRAY_SIZE(resp),
992 SI476X_DEFAULT_TIMEOUT);
995 report->expected = be16_to_cpup((__be16 *)(resp + 2));
996 report->received = be16_to_cpup((__be16 *)(resp + 4));
997 report->uncorrectable = be16_to_cpup((__be16 *)(resp + 6));
1002 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount);
1004 int si476x_core_cmd_fm_phase_diversity(struct si476x_core *core,
1005 enum si476x_phase_diversity_mode mode)
1007 u8 resp[CMD_FM_PHASE_DIVERSITY_NRESP];
1008 const u8 args[CMD_FM_PHASE_DIVERSITY_NARGS] = {
1012 return si476x_core_send_command(core, CMD_FM_PHASE_DIVERSITY,
1013 args, ARRAY_SIZE(args),
1014 resp, ARRAY_SIZE(resp),
1015 SI476X_DEFAULT_TIMEOUT);
1017 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity);
1019 * si476x_core_cmd_fm_phase_div_status() - get the phase diversity
1022 * @core: si476x device
1024 * NOTE caller must hold core lock
1026 * Function returns the value of the status bit in case of success and
1027 * negative error code in case of failre.
1029 int si476x_core_cmd_fm_phase_div_status(struct si476x_core *core)
1032 u8 resp[CMD_FM_PHASE_DIV_STATUS_NRESP];
1034 err = si476x_core_send_command(core, CMD_FM_PHASE_DIV_STATUS,
1036 resp, ARRAY_SIZE(resp),
1037 SI476X_DEFAULT_TIMEOUT);
1039 return (err < 0) ? err : resp[1];
1041 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status);
1045 * si476x_cmd_am_seek_start - send 'FM_SEEK_START' command to the
1047 * @core - device to send the command to
1048 * @seekup - if set the direction of the search is 'up'
1049 * @wrap - if set seek wraps when hitting band limit
1051 * This function begins search for a valid station. The station is
1052 * considered valid when 'FM_VALID_SNR_THRESHOLD' and
1053 * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
1056 * Function returns 0 on success and negative error code on failure
1058 int si476x_core_cmd_am_seek_start(struct si476x_core *core,
1059 bool seekup, bool wrap)
1061 u8 resp[CMD_AM_SEEK_START_NRESP];
1062 const u8 args[CMD_AM_SEEK_START_NARGS] = {
1063 seekup << 3 | wrap << 2,
1066 return si476x_cmd_tune_seek_freq(core, CMD_AM_SEEK_START,
1068 resp, sizeof(resp));
1070 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start);
1074 static int si476x_core_cmd_power_up_a10(struct si476x_core *core,
1075 struct si476x_power_up_args *puargs)
1077 u8 resp[CMD_POWER_UP_A10_NRESP];
1078 const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1079 const bool ctsen = (core->client->irq != 0);
1080 const u8 args[CMD_POWER_UP_A10_NARGS] = {
1081 0xF7, /* Reserved, always 0xF7 */
1082 0x3F & puargs->xcload, /* First two bits are reserved to be
1084 ctsen << 7 | intsel << 6 | 0x07, /* Last five bits
1086 * be written as 0x7 */
1087 puargs->func << 4 | puargs->freq,
1088 0x11, /* Reserved, always 0x11 */
1091 return si476x_core_send_command(core, CMD_POWER_UP,
1092 args, ARRAY_SIZE(args),
1093 resp, ARRAY_SIZE(resp),
1094 SI476X_TIMEOUT_POWER_UP);
1097 static int si476x_core_cmd_power_up_a20(struct si476x_core *core,
1098 struct si476x_power_up_args *puargs)
1100 u8 resp[CMD_POWER_UP_A20_NRESP];
1101 const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1102 const bool ctsen = (core->client->irq != 0);
1103 const u8 args[CMD_POWER_UP_A20_NARGS] = {
1104 puargs->ibias6x << 7 | puargs->xstart,
1105 0x3F & puargs->xcload, /* First two bits are reserved to be
1107 ctsen << 7 | intsel << 6 | puargs->fastboot << 5 |
1108 puargs->xbiashc << 3 | puargs->xbias,
1109 puargs->func << 4 | puargs->freq,
1110 0x10 | puargs->xmode,
1113 return si476x_core_send_command(core, CMD_POWER_UP,
1114 args, ARRAY_SIZE(args),
1115 resp, ARRAY_SIZE(resp),
1116 SI476X_TIMEOUT_POWER_UP);
1119 static int si476x_core_cmd_power_down_a10(struct si476x_core *core,
1120 struct si476x_power_down_args *pdargs)
1122 u8 resp[CMD_POWER_DOWN_A10_NRESP];
1124 return si476x_core_send_command(core, CMD_POWER_DOWN,
1126 resp, ARRAY_SIZE(resp),
1127 SI476X_DEFAULT_TIMEOUT);
1130 static int si476x_core_cmd_power_down_a20(struct si476x_core *core,
1131 struct si476x_power_down_args *pdargs)
1133 u8 resp[CMD_POWER_DOWN_A20_NRESP];
1134 const u8 args[CMD_POWER_DOWN_A20_NARGS] = {
1137 return si476x_core_send_command(core, CMD_POWER_DOWN,
1138 args, ARRAY_SIZE(args),
1139 resp, ARRAY_SIZE(resp),
1140 SI476X_DEFAULT_TIMEOUT);
1143 static int si476x_core_cmd_am_tune_freq_a10(struct si476x_core *core,
1144 struct si476x_tune_freq_args *tuneargs)
1147 const int am_freq = tuneargs->freq;
1148 u8 resp[CMD_AM_TUNE_FREQ_NRESP];
1149 const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1150 (tuneargs->hd << 6),
1155 return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, args,
1157 resp, sizeof(resp));
1160 static int si476x_core_cmd_am_tune_freq_a20(struct si476x_core *core,
1161 struct si476x_tune_freq_args *tuneargs)
1163 const int am_freq = tuneargs->freq;
1164 u8 resp[CMD_AM_TUNE_FREQ_NRESP];
1165 const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1166 (tuneargs->zifsr << 6) | (tuneargs->injside & 0b11),
1171 return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ,
1173 resp, sizeof(resp));
1176 static int si476x_core_cmd_fm_rsq_status_a10(struct si476x_core *core,
1177 struct si476x_rsq_status_args *rsqargs,
1178 struct si476x_rsq_status_report *report)
1181 u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1182 const u8 args[CMD_FM_RSQ_STATUS_A10_NARGS] = {
1183 rsqargs->rsqack << 3 | rsqargs->attune << 2 |
1184 rsqargs->cancel << 1 | rsqargs->stcack,
1187 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1188 args, ARRAY_SIZE(args),
1189 resp, ARRAY_SIZE(resp),
1190 SI476X_DEFAULT_TIMEOUT);
1192 * Besides getting received signal quality information this
1193 * command can be used to just acknowledge different interrupt
1194 * flags in those cases it is useless to copy and parse
1195 * received data so user can pass NULL, and thus avoid
1196 * unnecessary copying.
1198 if (err < 0 || report == NULL)
1201 report->multhint = 0b10000000 & resp[1];
1202 report->multlint = 0b01000000 & resp[1];
1203 report->snrhint = 0b00001000 & resp[1];
1204 report->snrlint = 0b00000100 & resp[1];
1205 report->rssihint = 0b00000010 & resp[1];
1206 report->rssilint = 0b00000001 & resp[1];
1208 report->bltf = 0b10000000 & resp[2];
1209 report->snr_ready = 0b00100000 & resp[2];
1210 report->rssiready = 0b00001000 & resp[2];
1211 report->afcrl = 0b00000010 & resp[2];
1212 report->valid = 0b00000001 & resp[2];
1214 report->readfreq = be16_to_cpup((__be16 *)(resp + 3));
1215 report->freqoff = resp[5];
1216 report->rssi = resp[6];
1217 report->snr = resp[7];
1218 report->lassi = resp[9];
1219 report->hassi = resp[10];
1220 report->mult = resp[11];
1221 report->dev = resp[12];
1222 report->readantcap = be16_to_cpup((__be16 *)(resp + 13));
1223 report->assi = resp[15];
1224 report->usn = resp[16];
1229 static int si476x_core_cmd_fm_rsq_status_a20(struct si476x_core *core,
1230 struct si476x_rsq_status_args *rsqargs,
1231 struct si476x_rsq_status_report *report)
1234 u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1235 const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1236 rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1237 rsqargs->attune << 2 | rsqargs->cancel << 1 |
1241 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1242 args, ARRAY_SIZE(args),
1243 resp, ARRAY_SIZE(resp),
1244 SI476X_DEFAULT_TIMEOUT);
1246 * Besides getting received signal quality information this
1247 * command can be used to just acknowledge different interrupt
1248 * flags in those cases it is useless to copy and parse
1249 * received data so user can pass NULL, and thus avoid
1250 * unnecessary copying.
1252 if (err < 0 || report == NULL)
1255 report->multhint = 0b10000000 & resp[1];
1256 report->multlint = 0b01000000 & resp[1];
1257 report->snrhint = 0b00001000 & resp[1];
1258 report->snrlint = 0b00000100 & resp[1];
1259 report->rssihint = 0b00000010 & resp[1];
1260 report->rssilint = 0b00000001 & resp[1];
1262 report->bltf = 0b10000000 & resp[2];
1263 report->snr_ready = 0b00100000 & resp[2];
1264 report->rssiready = 0b00001000 & resp[2];
1265 report->afcrl = 0b00000010 & resp[2];
1266 report->valid = 0b00000001 & resp[2];
1268 report->readfreq = be16_to_cpup((__be16 *)(resp + 3));
1269 report->freqoff = resp[5];
1270 report->rssi = resp[6];
1271 report->snr = resp[7];
1272 report->lassi = resp[9];
1273 report->hassi = resp[10];
1274 report->mult = resp[11];
1275 report->dev = resp[12];
1276 report->readantcap = be16_to_cpup((__be16 *)(resp + 13));
1277 report->assi = resp[15];
1278 report->usn = resp[16];
1284 static int si476x_core_cmd_fm_rsq_status_a30(struct si476x_core *core,
1285 struct si476x_rsq_status_args *rsqargs,
1286 struct si476x_rsq_status_report *report)
1289 u8 resp[CMD_FM_RSQ_STATUS_A30_NRESP];
1290 const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1291 rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1292 rsqargs->attune << 2 | rsqargs->cancel << 1 |
1296 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1297 args, ARRAY_SIZE(args),
1298 resp, ARRAY_SIZE(resp),
1299 SI476X_DEFAULT_TIMEOUT);
1301 * Besides getting received signal quality information this
1302 * command can be used to just acknowledge different interrupt
1303 * flags in those cases it is useless to copy and parse
1304 * received data so user can pass NULL, and thus avoid
1305 * unnecessary copying.
1307 if (err < 0 || report == NULL)
1310 report->multhint = 0b10000000 & resp[1];
1311 report->multlint = 0b01000000 & resp[1];
1312 report->snrhint = 0b00001000 & resp[1];
1313 report->snrlint = 0b00000100 & resp[1];
1314 report->rssihint = 0b00000010 & resp[1];
1315 report->rssilint = 0b00000001 & resp[1];
1317 report->bltf = 0b10000000 & resp[2];
1318 report->snr_ready = 0b00100000 & resp[2];
1319 report->rssiready = 0b00001000 & resp[2];
1320 report->injside = 0b00000100 & resp[2];
1321 report->afcrl = 0b00000010 & resp[2];
1322 report->valid = 0b00000001 & resp[2];
1324 report->readfreq = be16_to_cpup((__be16 *)(resp + 3));
1325 report->freqoff = resp[5];
1326 report->rssi = resp[6];
1327 report->snr = resp[7];
1328 report->issi = resp[8];
1329 report->lassi = resp[9];
1330 report->hassi = resp[10];
1331 report->mult = resp[11];
1332 report->dev = resp[12];
1333 report->readantcap = be16_to_cpup((__be16 *)(resp + 13));
1334 report->assi = resp[15];
1335 report->usn = resp[16];
1337 report->pilotdev = resp[17];
1338 report->rdsdev = resp[18];
1339 report->assidev = resp[19];
1340 report->strongdev = resp[20];
1341 report->rdspi = be16_to_cpup((__be16 *)(resp + 21));
1346 static int si476x_core_cmd_fm_tune_freq_a10(struct si476x_core *core,
1347 struct si476x_tune_freq_args *tuneargs)
1349 u8 resp[CMD_FM_TUNE_FREQ_NRESP];
1350 const u8 args[CMD_FM_TUNE_FREQ_A10_NARGS] = {
1351 (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1352 | (tuneargs->smoothmetrics << 2),
1353 msb(tuneargs->freq),
1354 lsb(tuneargs->freq),
1355 msb(tuneargs->antcap),
1356 lsb(tuneargs->antcap)
1359 return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1361 resp, sizeof(resp));
1364 static int si476x_core_cmd_fm_tune_freq_a20(struct si476x_core *core,
1365 struct si476x_tune_freq_args *tuneargs)
1367 u8 resp[CMD_FM_TUNE_FREQ_NRESP];
1368 const u8 args[CMD_FM_TUNE_FREQ_A20_NARGS] = {
1369 (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1370 | (tuneargs->smoothmetrics << 2) | (tuneargs->injside),
1371 msb(tuneargs->freq),
1372 lsb(tuneargs->freq),
1375 return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1377 resp, sizeof(resp));
1380 static int si476x_core_cmd_agc_status_a20(struct si476x_core *core,
1381 struct si476x_agc_status_report *report)
1384 u8 resp[CMD_AGC_STATUS_NRESP_A20];
1389 err = si476x_core_send_command(core, CMD_AGC_STATUS,
1391 resp, ARRAY_SIZE(resp),
1392 SI476X_DEFAULT_TIMEOUT);
1396 report->mxhi = resp[1] & SI476X_AGC_MXHI;
1397 report->mxlo = resp[1] & SI476X_AGC_MXLO;
1398 report->lnahi = resp[1] & SI476X_AGC_LNAHI;
1399 report->lnalo = resp[1] & SI476X_AGC_LNALO;
1400 report->fmagc1 = resp[2];
1401 report->fmagc2 = resp[3];
1402 report->pgagain = resp[4];
1403 report->fmwblang = resp[5];
1408 static int si476x_core_cmd_agc_status_a10(struct si476x_core *core,
1409 struct si476x_agc_status_report *report)
1412 u8 resp[CMD_AGC_STATUS_NRESP_A10];
1417 err = si476x_core_send_command(core, CMD_AGC_STATUS,
1419 resp, ARRAY_SIZE(resp),
1420 SI476X_DEFAULT_TIMEOUT);
1424 report->mxhi = resp[1] & SI476X_AGC_MXHI;
1425 report->mxlo = resp[1] & SI476X_AGC_MXLO;
1426 report->lnahi = resp[1] & SI476X_AGC_LNAHI;
1427 report->lnalo = resp[1] & SI476X_AGC_LNALO;
1432 typedef int (*tune_freq_func_t) (struct si476x_core *core,
1433 struct si476x_tune_freq_args *tuneargs);
1436 int (*power_up) (struct si476x_core *,
1437 struct si476x_power_up_args *);
1438 int (*power_down) (struct si476x_core *,
1439 struct si476x_power_down_args *);
1441 tune_freq_func_t fm_tune_freq;
1442 tune_freq_func_t am_tune_freq;
1444 int (*fm_rsq_status)(struct si476x_core *,
1445 struct si476x_rsq_status_args *,
1446 struct si476x_rsq_status_report *);
1448 int (*agc_status)(struct si476x_core *,
1449 struct si476x_agc_status_report *);
1450 int (*intb_pin_cfg)(struct si476x_core *core,
1451 enum si476x_intb_config intb,
1452 enum si476x_a1_config a1);
1453 } si476x_cmds_vtable[] = {
1454 [SI476X_REVISION_A10] = {
1455 .power_up = si476x_core_cmd_power_up_a10,
1456 .power_down = si476x_core_cmd_power_down_a10,
1457 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a10,
1458 .am_tune_freq = si476x_core_cmd_am_tune_freq_a10,
1459 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a10,
1460 .agc_status = si476x_core_cmd_agc_status_a10,
1461 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a10,
1463 [SI476X_REVISION_A20] = {
1464 .power_up = si476x_core_cmd_power_up_a20,
1465 .power_down = si476x_core_cmd_power_down_a20,
1466 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20,
1467 .am_tune_freq = si476x_core_cmd_am_tune_freq_a20,
1468 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a20,
1469 .agc_status = si476x_core_cmd_agc_status_a20,
1470 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20,
1472 [SI476X_REVISION_A30] = {
1473 .power_up = si476x_core_cmd_power_up_a20,
1474 .power_down = si476x_core_cmd_power_down_a20,
1475 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20,
1476 .am_tune_freq = si476x_core_cmd_am_tune_freq_a20,
1477 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a30,
1478 .agc_status = si476x_core_cmd_agc_status_a20,
1479 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20,
1483 int si476x_core_cmd_power_up(struct si476x_core *core,
1484 struct si476x_power_up_args *args)
1486 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1487 core->revision == -1);
1488 return si476x_cmds_vtable[core->revision].power_up(core, args);
1490 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up);
1492 int si476x_core_cmd_power_down(struct si476x_core *core,
1493 struct si476x_power_down_args *args)
1495 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1496 core->revision == -1);
1497 return si476x_cmds_vtable[core->revision].power_down(core, args);
1499 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down);
1501 int si476x_core_cmd_fm_tune_freq(struct si476x_core *core,
1502 struct si476x_tune_freq_args *args)
1504 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1505 core->revision == -1);
1506 return si476x_cmds_vtable[core->revision].fm_tune_freq(core, args);
1508 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq);
1510 int si476x_core_cmd_am_tune_freq(struct si476x_core *core,
1511 struct si476x_tune_freq_args *args)
1513 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1514 core->revision == -1);
1515 return si476x_cmds_vtable[core->revision].am_tune_freq(core, args);
1517 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq);
1519 int si476x_core_cmd_fm_rsq_status(struct si476x_core *core,
1520 struct si476x_rsq_status_args *args,
1521 struct si476x_rsq_status_report *report)
1524 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1525 core->revision == -1);
1526 return si476x_cmds_vtable[core->revision].fm_rsq_status(core, args,
1529 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status);
1531 int si476x_core_cmd_agc_status(struct si476x_core *core,
1532 struct si476x_agc_status_report *report)
1535 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1536 core->revision == -1);
1537 return si476x_cmds_vtable[core->revision].agc_status(core, report);
1539 EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status);
1541 int si476x_core_cmd_intb_pin_cfg(struct si476x_core *core,
1542 enum si476x_intb_config intb,
1543 enum si476x_a1_config a1)
1545 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1546 core->revision == -1);
1548 return si476x_cmds_vtable[core->revision].intb_pin_cfg(core, intb, a1);
1550 EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg);
1552 MODULE_LICENSE("GPL");
1553 MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
1554 MODULE_DESCRIPTION("API for command exchange for si476x");