From: Mauro Carvalho Chehab Date: Thu, 18 Apr 2013 23:51:00 +0000 (-0300) Subject: Revert "[media] mfd: Add commands abstraction layer for SI476X MFD" X-Git-Tag: next-20130422~89^2^2~2 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=cbce0957d16ec1218d40538f5becb6cf6108b66a;p=karo-tx-linux.git Revert "[media] mfd: Add commands abstraction layer for SI476X MFD" This reverts commit 626b67c1ad90b450e0afbd1c277fd81ad64c5539. Requested-by: Samuel Ortiz Signed-off-by: Mauro Carvalho Chehab --- diff --git a/drivers/mfd/si476x-cmd.c b/drivers/mfd/si476x-cmd.c deleted file mode 100644 index 71ac2e8d16d0..000000000000 --- a/drivers/mfd/si476x-cmd.c +++ /dev/null @@ -1,1554 +0,0 @@ -/* - * drivers/mfd/si476x-cmd.c -- Subroutines implementing command - * protocol of si476x series of chips - * - * Copyright (C) 2012 Innovative Converged Devices(ICD) - * Copyright (C) 2013 Andrey Smirnov - * - * Author: Andrey Smirnov - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define msb(x) ((u8)((u16) x >> 8)) -#define lsb(x) ((u8)((u16) x & 0x00FF)) - - - -#define CMD_POWER_UP 0x01 -#define CMD_POWER_UP_A10_NRESP 1 -#define CMD_POWER_UP_A10_NARGS 5 - -#define CMD_POWER_UP_A20_NRESP 1 -#define CMD_POWER_UP_A20_NARGS 5 - -#define POWER_UP_DELAY_MS 110 - -#define CMD_POWER_DOWN 0x11 -#define CMD_POWER_DOWN_A10_NRESP 1 - -#define CMD_POWER_DOWN_A20_NRESP 1 -#define CMD_POWER_DOWN_A20_NARGS 1 - -#define CMD_FUNC_INFO 0x12 -#define CMD_FUNC_INFO_NRESP 7 - -#define CMD_SET_PROPERTY 0x13 -#define CMD_SET_PROPERTY_NARGS 5 -#define CMD_SET_PROPERTY_NRESP 1 - -#define CMD_GET_PROPERTY 0x14 -#define CMD_GET_PROPERTY_NARGS 3 -#define CMD_GET_PROPERTY_NRESP 4 - -#define CMD_AGC_STATUS 0x17 -#define CMD_AGC_STATUS_NRESP_A10 2 -#define CMD_AGC_STATUS_NRESP_A20 6 - -#define PIN_CFG_BYTE(x) (0x7F & (x)) -#define CMD_DIG_AUDIO_PIN_CFG 0x18 -#define CMD_DIG_AUDIO_PIN_CFG_NARGS 4 -#define CMD_DIG_AUDIO_PIN_CFG_NRESP 5 - -#define CMD_ZIF_PIN_CFG 0x19 -#define CMD_ZIF_PIN_CFG_NARGS 4 -#define CMD_ZIF_PIN_CFG_NRESP 5 - -#define CMD_IC_LINK_GPO_CTL_PIN_CFG 0x1A -#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS 4 -#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP 5 - -#define CMD_ANA_AUDIO_PIN_CFG 0x1B -#define CMD_ANA_AUDIO_PIN_CFG_NARGS 1 -#define CMD_ANA_AUDIO_PIN_CFG_NRESP 2 - -#define CMD_INTB_PIN_CFG 0x1C -#define CMD_INTB_PIN_CFG_NARGS 2 -#define CMD_INTB_PIN_CFG_A10_NRESP 6 -#define CMD_INTB_PIN_CFG_A20_NRESP 3 - -#define CMD_FM_TUNE_FREQ 0x30 -#define CMD_FM_TUNE_FREQ_A10_NARGS 5 -#define CMD_FM_TUNE_FREQ_A20_NARGS 3 -#define CMD_FM_TUNE_FREQ_NRESP 1 - -#define CMD_FM_RSQ_STATUS 0x32 - -#define CMD_FM_RSQ_STATUS_A10_NARGS 1 -#define CMD_FM_RSQ_STATUS_A10_NRESP 17 -#define CMD_FM_RSQ_STATUS_A30_NARGS 1 -#define CMD_FM_RSQ_STATUS_A30_NRESP 23 - - -#define CMD_FM_SEEK_START 0x31 -#define CMD_FM_SEEK_START_NARGS 1 -#define CMD_FM_SEEK_START_NRESP 1 - -#define CMD_FM_RDS_STATUS 0x36 -#define CMD_FM_RDS_STATUS_NARGS 1 -#define CMD_FM_RDS_STATUS_NRESP 16 - -#define CMD_FM_RDS_BLOCKCOUNT 0x37 -#define CMD_FM_RDS_BLOCKCOUNT_NARGS 1 -#define CMD_FM_RDS_BLOCKCOUNT_NRESP 8 - -#define CMD_FM_PHASE_DIVERSITY 0x38 -#define CMD_FM_PHASE_DIVERSITY_NARGS 1 -#define CMD_FM_PHASE_DIVERSITY_NRESP 1 - -#define CMD_FM_PHASE_DIV_STATUS 0x39 -#define CMD_FM_PHASE_DIV_STATUS_NRESP 2 - -#define CMD_AM_TUNE_FREQ 0x40 -#define CMD_AM_TUNE_FREQ_NARGS 3 -#define CMD_AM_TUNE_FREQ_NRESP 1 - -#define CMD_AM_RSQ_STATUS 0x42 -#define CMD_AM_RSQ_STATUS_NARGS 1 -#define CMD_AM_RSQ_STATUS_NRESP 13 - -#define CMD_AM_SEEK_START 0x41 -#define CMD_AM_SEEK_START_NARGS 1 -#define CMD_AM_SEEK_START_NRESP 1 - - -#define CMD_AM_ACF_STATUS 0x45 -#define CMD_AM_ACF_STATUS_NRESP 6 -#define CMD_AM_ACF_STATUS_NARGS 1 - -#define CMD_FM_ACF_STATUS 0x35 -#define CMD_FM_ACF_STATUS_NRESP 8 -#define CMD_FM_ACF_STATUS_NARGS 1 - -#define CMD_MAX_ARGS_COUNT (10) - - -enum si476x_acf_status_report_bits { - SI476X_ACF_BLEND_INT = (1 << 4), - SI476X_ACF_HIBLEND_INT = (1 << 3), - SI476X_ACF_HICUT_INT = (1 << 2), - SI476X_ACF_CHBW_INT = (1 << 1), - SI476X_ACF_SOFTMUTE_INT = (1 << 0), - - SI476X_ACF_SMUTE = (1 << 0), - SI476X_ACF_SMATTN = 0b11111, - SI476X_ACF_PILOT = (1 << 7), - SI476X_ACF_STBLEND = ~SI476X_ACF_PILOT, -}; - -enum si476x_agc_status_report_bits { - SI476X_AGC_MXHI = (1 << 5), - SI476X_AGC_MXLO = (1 << 4), - SI476X_AGC_LNAHI = (1 << 3), - SI476X_AGC_LNALO = (1 << 2), -}; - -enum si476x_errors { - SI476X_ERR_BAD_COMMAND = 0x10, - SI476X_ERR_BAD_ARG1 = 0x11, - SI476X_ERR_BAD_ARG2 = 0x12, - SI476X_ERR_BAD_ARG3 = 0x13, - SI476X_ERR_BAD_ARG4 = 0x14, - SI476X_ERR_BUSY = 0x18, - SI476X_ERR_BAD_INTERNAL_MEMORY = 0x20, - SI476X_ERR_BAD_PATCH = 0x30, - SI476X_ERR_BAD_BOOT_MODE = 0x31, - SI476X_ERR_BAD_PROPERTY = 0x40, -}; - -static int si476x_core_parse_and_nag_about_error(struct si476x_core *core) -{ - int err; - char *cause; - u8 buffer[2]; - - if (core->revision != SI476X_REVISION_A10) { - err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, - buffer, sizeof(buffer)); - if (err == sizeof(buffer)) { - switch (buffer[1]) { - case SI476X_ERR_BAD_COMMAND: - cause = "Bad command"; - err = -EINVAL; - break; - case SI476X_ERR_BAD_ARG1: - cause = "Bad argument #1"; - err = -EINVAL; - break; - case SI476X_ERR_BAD_ARG2: - cause = "Bad argument #2"; - err = -EINVAL; - break; - case SI476X_ERR_BAD_ARG3: - cause = "Bad argument #3"; - err = -EINVAL; - break; - case SI476X_ERR_BAD_ARG4: - cause = "Bad argument #4"; - err = -EINVAL; - break; - case SI476X_ERR_BUSY: - cause = "Chip is busy"; - err = -EBUSY; - break; - case SI476X_ERR_BAD_INTERNAL_MEMORY: - cause = "Bad internal memory"; - err = -EIO; - break; - case SI476X_ERR_BAD_PATCH: - cause = "Bad patch"; - err = -EINVAL; - break; - case SI476X_ERR_BAD_BOOT_MODE: - cause = "Bad boot mode"; - err = -EINVAL; - break; - case SI476X_ERR_BAD_PROPERTY: - cause = "Bad property"; - err = -EINVAL; - break; - default: - cause = "Unknown"; - err = -EIO; - } - - dev_err(&core->client->dev, - "[Chip error status]: %s\n", cause); - } else { - dev_err(&core->client->dev, - "Failed to fetch error code\n"); - err = (err >= 0) ? -EIO : err; - } - } else { - err = -EIO; - } - - return err; -} - -/** - * si476x_core_send_command() - sends a command to si476x and waits its - * response - * @core: si476x_device structure for the device we are - * communicating with - * @command: command id - * @args: command arguments we are sending - * @argn: actual size of @args - * @response: buffer to place the expected response from the device - * @respn: actual size of @response - * @usecs: amount of time to wait before reading the response (in - * usecs) - * - * Function returns 0 on succsess and negative error code on - * failure - */ -static int 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) -{ - struct i2c_client *client = core->client; - int err; - u8 data[CMD_MAX_ARGS_COUNT + 1]; - - if (argn > CMD_MAX_ARGS_COUNT) { - err = -ENOMEM; - goto exit; - } - - if (!client->adapter) { - err = -ENODEV; - goto exit; - } - - /* First send the command and its arguments */ - data[0] = command; - memcpy(&data[1], args, argn); - dev_dbg(&client->dev, "Command:\n %*ph\n", argn + 1, data); - - err = si476x_core_i2c_xfer(core, SI476X_I2C_SEND, - (char *) data, argn + 1); - if (err != argn + 1) { - dev_err(&core->client->dev, - "Error while sending command 0x%02x\n", - command); - err = (err >= 0) ? -EIO : err; - goto exit; - } - /* Set CTS to zero only after the command is send to avoid - * possible racing conditions when working in polling mode */ - atomic_set(&core->cts, 0); - - /* if (unlikely(command == CMD_POWER_DOWN) */ - if (!wait_event_timeout(core->command, - atomic_read(&core->cts), - usecs_to_jiffies(usecs) + 1)) - dev_warn(&core->client->dev, - "(%s) [CMD 0x%02x] Answer timeout.\n", - __func__, command); - - /* - When working in polling mode, for some reason the tuner will - report CTS bit as being set in the first status byte read, - but all the consequtive ones will return zeros until the - tuner is actually completed the POWER_UP command. To - workaround that we wait for second CTS to be reported - */ - if (unlikely(!core->client->irq && command == CMD_POWER_UP)) { - if (!wait_event_timeout(core->command, - atomic_read(&core->cts), - usecs_to_jiffies(usecs) + 1)) - dev_warn(&core->client->dev, - "(%s) Power up took too much time.\n", - __func__); - } - - /* Then get the response */ - err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, resp, respn); - if (err != respn) { - dev_err(&core->client->dev, - "Error while reading response for command 0x%02x\n", - command); - err = (err >= 0) ? -EIO : err; - goto exit; - } - dev_dbg(&client->dev, "Response:\n %*ph\n", respn, resp); - - err = 0; - - if (resp[0] & SI476X_ERR) { - dev_err(&core->client->dev, - "[CMD 0x%02x] Chip set error flag\n", command); - err = si476x_core_parse_and_nag_about_error(core); - goto exit; - } - - if (!(resp[0] & SI476X_CTS)) - err = -EBUSY; -exit: - return err; -} - -static int si476x_cmd_clear_stc(struct si476x_core *core) -{ - int err; - struct si476x_rsq_status_args args = { - .primary = false, - .rsqack = false, - .attune = false, - .cancel = false, - .stcack = true, - }; - - switch (core->power_up_parameters.func) { - case SI476X_FUNC_FM_RECEIVER: - err = si476x_core_cmd_fm_rsq_status(core, &args, NULL); - break; - case SI476X_FUNC_AM_RECEIVER: - err = si476x_core_cmd_am_rsq_status(core, &args, NULL); - break; - default: - err = -EINVAL; - } - - return err; -} - -static int 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) -{ - int err; - - - atomic_set(&core->stc, 0); - err = si476x_core_send_command(core, cmd, args, argn, resp, respn, - SI476X_TIMEOUT_TUNE); - if (!err) { - wait_event_killable(core->tuning, - atomic_read(&core->stc)); - si476x_cmd_clear_stc(core); - } - - return err; -} - -/** - * si476x_cmd_func_info() - send 'FUNC_INFO' command to the device - * @core: device to send the command to - * @info: struct si476x_func_info to fill all the information - * returned by the command - * - * The command requests the firmware and patch version for currently - * loaded firmware (dependent on the function of the device FM/AM/WB) - * - * Function returns 0 on succsess and negative error code on - * failure - */ -int si476x_core_cmd_func_info(struct si476x_core *core, - struct si476x_func_info *info) -{ - int err; - u8 resp[CMD_FUNC_INFO_NRESP]; - - err = si476x_core_send_command(core, CMD_FUNC_INFO, - NULL, 0, - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); - - info->firmware.major = resp[1]; - info->firmware.minor[0] = resp[2]; - info->firmware.minor[1] = resp[3]; - - info->patch_id = ((u16) resp[4] << 8) | resp[5]; - info->func = resp[6]; - - return err; -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_func_info); - -/** - * si476x_cmd_set_property() - send 'SET_PROPERTY' command to the device - * @core: device to send the command to - * @property: property address - * @value: property value - * - * Function returns 0 on succsess and negative error code on - * failure - */ -int si476x_core_cmd_set_property(struct si476x_core *core, - u16 property, u16 value) -{ - u8 resp[CMD_SET_PROPERTY_NRESP]; - const u8 args[CMD_SET_PROPERTY_NARGS] = { - 0x00, - msb(property), - lsb(property), - msb(value), - lsb(value), - }; - - return si476x_core_send_command(core, CMD_SET_PROPERTY, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_set_property); - -/** - * si476x_cmd_get_property() - send 'GET_PROPERTY' command to the device - * @core: device to send the command to - * @property: property address - * - * Function return the value of property as u16 on success or a - * negative error on failure - */ -int si476x_core_cmd_get_property(struct si476x_core *core, u16 property) -{ - int err; - u8 resp[CMD_GET_PROPERTY_NRESP]; - const u8 args[CMD_GET_PROPERTY_NARGS] = { - 0x00, - msb(property), - lsb(property), - }; - - err = si476x_core_send_command(core, CMD_GET_PROPERTY, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); - if (err < 0) - return err; - else - return be16_to_cpup((__be16 *)(resp + 2)); -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_get_property); - -/** - * si476x_cmd_dig_audio_pin_cfg() - send 'DIG_AUDIO_PIN_CFG' command to - * the device - * @core: device to send the command to - * @dclk: DCLK pin function configuration: - * #SI476X_DCLK_NOOP - do not modify the behaviour - * #SI476X_DCLK_TRISTATE - put the pin in tristate condition, - * enable 1MOhm pulldown - * #SI476X_DCLK_DAUDIO - set the pin to be a part of digital - * audio interface - * @dfs: DFS pin function configuration: - * #SI476X_DFS_NOOP - do not modify the behaviour - * #SI476X_DFS_TRISTATE - put the pin in tristate condition, - * enable 1MOhm pulldown - * SI476X_DFS_DAUDIO - set the pin to be a part of digital - * audio interface - * @dout - DOUT pin function configuration: - * SI476X_DOUT_NOOP - do not modify the behaviour - * SI476X_DOUT_TRISTATE - put the pin in tristate condition, - * enable 1MOhm pulldown - * SI476X_DOUT_I2S_OUTPUT - set this pin to be digital out on I2S - * port 1 - * SI476X_DOUT_I2S_INPUT - set this pin to be digital in on I2S - * port 1 - * @xout - XOUT pin function configuration: - * SI476X_XOUT_NOOP - do not modify the behaviour - * SI476X_XOUT_TRISTATE - put the pin in tristate condition, - * enable 1MOhm pulldown - * SI476X_XOUT_I2S_INPUT - set this pin to be digital in on I2S - * port 1 - * SI476X_XOUT_MODE_SELECT - set this pin to be the input that - * selects the mode of the I2S audio - * combiner (analog or HD) - * [SI4761/63/65/67 Only] - * - * Function returns 0 on success and negative error code on failure - */ -int 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) -{ - u8 resp[CMD_DIG_AUDIO_PIN_CFG_NRESP]; - const u8 args[CMD_DIG_AUDIO_PIN_CFG_NARGS] = { - PIN_CFG_BYTE(dclk), - PIN_CFG_BYTE(dfs), - PIN_CFG_BYTE(dout), - PIN_CFG_BYTE(xout), - }; - - return si476x_core_send_command(core, CMD_DIG_AUDIO_PIN_CFG, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg); - -/** - * si476x_cmd_zif_pin_cfg - send 'ZIF_PIN_CFG_COMMAND' - * @core - device to send the command to - * @iqclk - IQCL pin function configuration: - * SI476X_IQCLK_NOOP - do not modify the behaviour - * SI476X_IQCLK_TRISTATE - put the pin in tristate condition, - * enable 1MOhm pulldown - * SI476X_IQCLK_IQ - set pin to be a part of I/Q interace - * in master mode - * @iqfs - IQFS pin function configuration: - * SI476X_IQFS_NOOP - do not modify the behaviour - * SI476X_IQFS_TRISTATE - put the pin in tristate condition, - * enable 1MOhm pulldown - * SI476X_IQFS_IQ - set pin to be a part of I/Q interace - * in master mode - * @iout - IOUT pin function configuration: - * SI476X_IOUT_NOOP - do not modify the behaviour - * SI476X_IOUT_TRISTATE - put the pin in tristate condition, - * enable 1MOhm pulldown - * SI476X_IOUT_OUTPUT - set pin to be I out - * @qout - QOUT pin function configuration: - * SI476X_QOUT_NOOP - do not modify the behaviour - * SI476X_QOUT_TRISTATE - put the pin in tristate condition, - * enable 1MOhm pulldown - * SI476X_QOUT_OUTPUT - set pin to be Q out - * - * Function returns 0 on success and negative error code on failure - */ -int 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) -{ - u8 resp[CMD_ZIF_PIN_CFG_NRESP]; - const u8 args[CMD_ZIF_PIN_CFG_NARGS] = { - PIN_CFG_BYTE(iqclk), - PIN_CFG_BYTE(iqfs), - PIN_CFG_BYTE(iout), - PIN_CFG_BYTE(qout), - }; - - return si476x_core_send_command(core, CMD_ZIF_PIN_CFG, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg); - -/** - * si476x_cmd_ic_link_gpo_ctl_pin_cfg - send - * 'IC_LINK_GPIO_CTL_PIN_CFG' comand to the device - * @core - device to send the command to - * @icin - ICIN pin function configuration: - * SI476X_ICIN_NOOP - do not modify the behaviour - * SI476X_ICIN_TRISTATE - put the pin in tristate condition, - * enable 1MOhm pulldown - * SI476X_ICIN_GPO1_HIGH - set pin to be an output, drive it high - * SI476X_ICIN_GPO1_LOW - set pin to be an output, drive it low - * SI476X_ICIN_IC_LINK - set the pin to be a part of Inter-Chip link - * @icip - ICIP pin function configuration: - * SI476X_ICIP_NOOP - do not modify the behaviour - * SI476X_ICIP_TRISTATE - put the pin in tristate condition, - * enable 1MOhm pulldown - * SI476X_ICIP_GPO1_HIGH - set pin to be an output, drive it high - * SI476X_ICIP_GPO1_LOW - set pin to be an output, drive it low - * SI476X_ICIP_IC_LINK - set the pin to be a part of Inter-Chip link - * @icon - ICON pin function configuration: - * SI476X_ICON_NOOP - do not modify the behaviour - * SI476X_ICON_TRISTATE - put the pin in tristate condition, - * enable 1MOhm pulldown - * SI476X_ICON_I2S - set the pin to be a part of audio - * interface in slave mode (DCLK) - * SI476X_ICON_IC_LINK - set the pin to be a part of Inter-Chip link - * @icop - ICOP pin function configuration: - * SI476X_ICOP_NOOP - do not modify the behaviour - * SI476X_ICOP_TRISTATE - put the pin in tristate condition, - * enable 1MOhm pulldown - * SI476X_ICOP_I2S - set the pin to be a part of audio - * interface in slave mode (DOUT) - * [Si4761/63/65/67 Only] - * SI476X_ICOP_IC_LINK - set the pin to be a part of Inter-Chip link - * - * Function returns 0 on success and negative error code on failure - */ -int 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) -{ - u8 resp[CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP]; - const u8 args[CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS] = { - PIN_CFG_BYTE(icin), - PIN_CFG_BYTE(icip), - PIN_CFG_BYTE(icon), - PIN_CFG_BYTE(icop), - }; - - return si476x_core_send_command(core, CMD_IC_LINK_GPO_CTL_PIN_CFG, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_ic_link_gpo_ctl_pin_cfg); - -/** - * si476x_cmd_ana_audio_pin_cfg - send 'ANA_AUDIO_PIN_CFG' to the - * device - * @core - device to send the command to - * @lrout - LROUT pin function configuration: - * SI476X_LROUT_NOOP - do not modify the behaviour - * SI476X_LROUT_TRISTATE - put the pin in tristate condition, - * enable 1MOhm pulldown - * SI476X_LROUT_AUDIO - set pin to be audio output - * SI476X_LROUT_MPX - set pin to be MPX output - * - * Function returns 0 on success and negative error code on failure - */ -int si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *core, - enum si476x_lrout_config lrout) -{ - u8 resp[CMD_ANA_AUDIO_PIN_CFG_NRESP]; - const u8 args[CMD_ANA_AUDIO_PIN_CFG_NARGS] = { - PIN_CFG_BYTE(lrout), - }; - - return si476x_core_send_command(core, CMD_ANA_AUDIO_PIN_CFG, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_ana_audio_pin_cfg); - - -/** - * si476x_cmd_intb_pin_cfg - send 'INTB_PIN_CFG' command to the device - * @core - device to send the command to - * @intb - INTB pin function configuration: - * SI476X_INTB_NOOP - do not modify the behaviour - * SI476X_INTB_TRISTATE - put the pin in tristate condition, - * enable 1MOhm pulldown - * SI476X_INTB_DAUDIO - set pin to be a part of digital - * audio interface in slave mode - * SI476X_INTB_IRQ - set pin to be an interrupt request line - * @a1 - A1 pin function configuration: - * SI476X_A1_NOOP - do not modify the behaviour - * SI476X_A1_TRISTATE - put the pin in tristate condition, - * enable 1MOhm pulldown - * SI476X_A1_IRQ - set pin to be an interrupt request line - * - * Function returns 0 on success and negative error code on failure - */ -static int si476x_core_cmd_intb_pin_cfg_a10(struct si476x_core *core, - enum si476x_intb_config intb, - enum si476x_a1_config a1) -{ - u8 resp[CMD_INTB_PIN_CFG_A10_NRESP]; - const u8 args[CMD_INTB_PIN_CFG_NARGS] = { - PIN_CFG_BYTE(intb), - PIN_CFG_BYTE(a1), - }; - - return si476x_core_send_command(core, CMD_INTB_PIN_CFG, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); -} - -static int si476x_core_cmd_intb_pin_cfg_a20(struct si476x_core *core, - enum si476x_intb_config intb, - enum si476x_a1_config a1) -{ - u8 resp[CMD_INTB_PIN_CFG_A20_NRESP]; - const u8 args[CMD_INTB_PIN_CFG_NARGS] = { - PIN_CFG_BYTE(intb), - PIN_CFG_BYTE(a1), - }; - - return si476x_core_send_command(core, CMD_INTB_PIN_CFG, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); -} - - - -/** - * si476x_cmd_am_rsq_status - send 'AM_RSQ_STATUS' command to the - * device - * @core - device to send the command to - * @rsqack - if set command clears RSQINT, SNRINT, SNRLINT, RSSIHINT, - * RSSSILINT, BLENDINT, MULTHINT and MULTLINT - * @attune - when set the values in the status report are the values - * that were calculated at tune - * @cancel - abort ongoing seek/tune opertation - * @stcack - clear the STCINT bin in status register - * @report - all signal quality information retured by the command - * (if NULL then the output of the command is ignored) - * - * Function returns 0 on success and negative error code on failure - */ -int si476x_core_cmd_am_rsq_status(struct si476x_core *core, - struct si476x_rsq_status_args *rsqargs, - struct si476x_rsq_status_report *report) -{ - int err; - u8 resp[CMD_AM_RSQ_STATUS_NRESP]; - const u8 args[CMD_AM_RSQ_STATUS_NARGS] = { - rsqargs->rsqack << 3 | rsqargs->attune << 2 | - rsqargs->cancel << 1 | rsqargs->stcack, - }; - - err = si476x_core_send_command(core, CMD_AM_RSQ_STATUS, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); - /* - * Besides getting received signal quality information this - * command can be used to just acknowledge different interrupt - * flags in those cases it is useless to copy and parse - * received data so user can pass NULL, and thus avoid - * unnecessary copying. - */ - if (!report) - return err; - - report->snrhint = 0b00001000 & resp[1]; - report->snrlint = 0b00000100 & resp[1]; - report->rssihint = 0b00000010 & resp[1]; - report->rssilint = 0b00000001 & resp[1]; - - report->bltf = 0b10000000 & resp[2]; - report->snr_ready = 0b00100000 & resp[2]; - report->rssiready = 0b00001000 & resp[2]; - report->afcrl = 0b00000010 & resp[2]; - report->valid = 0b00000001 & resp[2]; - - report->readfreq = be16_to_cpup((__be16 *)(resp + 3)); - report->freqoff = resp[5]; - report->rssi = resp[6]; - report->snr = resp[7]; - report->lassi = resp[9]; - report->hassi = resp[10]; - report->mult = resp[11]; - report->dev = resp[12]; - - return err; -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status); - -int si476x_core_cmd_fm_acf_status(struct si476x_core *core, - struct si476x_acf_status_report *report) -{ - int err; - u8 resp[CMD_FM_ACF_STATUS_NRESP]; - const u8 args[CMD_FM_ACF_STATUS_NARGS] = { - 0x0, - }; - - if (!report) - return -EINVAL; - - err = si476x_core_send_command(core, CMD_FM_ACF_STATUS, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); - if (err < 0) - return err; - - report->blend_int = resp[1] & SI476X_ACF_BLEND_INT; - report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT; - report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT; - report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT; - report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT; - report->smute = resp[2] & SI476X_ACF_SMUTE; - report->smattn = resp[3] & SI476X_ACF_SMATTN; - report->chbw = resp[4]; - report->hicut = resp[5]; - report->hiblend = resp[6]; - report->pilot = resp[7] & SI476X_ACF_PILOT; - report->stblend = resp[7] & SI476X_ACF_STBLEND; - - return err; -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status); - -int si476x_core_cmd_am_acf_status(struct si476x_core *core, - struct si476x_acf_status_report *report) -{ - int err; - u8 resp[CMD_AM_ACF_STATUS_NRESP]; - const u8 args[CMD_AM_ACF_STATUS_NARGS] = { - 0x0, - }; - - if (!report) - return -EINVAL; - - err = si476x_core_send_command(core, CMD_AM_ACF_STATUS, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); - if (err < 0) - return err; - - report->blend_int = resp[1] & SI476X_ACF_BLEND_INT; - report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT; - report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT; - report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT; - report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT; - report->smute = resp[2] & SI476X_ACF_SMUTE; - report->smattn = resp[3] & SI476X_ACF_SMATTN; - report->chbw = resp[4]; - report->hicut = resp[5]; - - return err; -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status); - - -/** - * si476x_cmd_fm_seek_start - send 'FM_SEEK_START' command to the - * device - * @core - device to send the command to - * @seekup - if set the direction of the search is 'up' - * @wrap - if set seek wraps when hitting band limit - * - * This function begins search for a valid station. The station is - * considered valid when 'FM_VALID_SNR_THRESHOLD' and - * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria - * are met. -} * - * Function returns 0 on success and negative error code on failure - */ -int si476x_core_cmd_fm_seek_start(struct si476x_core *core, - bool seekup, bool wrap) -{ - u8 resp[CMD_FM_SEEK_START_NRESP]; - const u8 args[CMD_FM_SEEK_START_NARGS] = { - seekup << 3 | wrap << 2, - }; - - return si476x_cmd_tune_seek_freq(core, CMD_FM_SEEK_START, - args, sizeof(args), - resp, sizeof(resp)); -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start); - -/** - * si476x_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the - * device - * @core - device to send the command to - * @status_only - if set the data is not removed from RDSFIFO, - * RDSFIFOUSED is not decremented and data in all the - * rest RDS data contains the last valid info received - * @mtfifo if set the command clears RDS receive FIFO - * @intack if set the command clards the RDSINT bit. - * - * Function returns 0 on success and negative error code on failure - */ -int si476x_core_cmd_fm_rds_status(struct si476x_core *core, - bool status_only, - bool mtfifo, - bool intack, - struct si476x_rds_status_report *report) -{ - int err; - u8 resp[CMD_FM_RDS_STATUS_NRESP]; - const u8 args[CMD_FM_RDS_STATUS_NARGS] = { - status_only << 2 | mtfifo << 1 | intack, - }; - - err = si476x_core_send_command(core, CMD_FM_RDS_STATUS, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); - /* - * Besides getting RDS status information this command can be - * used to just acknowledge different interrupt flags in those - * cases it is useless to copy and parse received data so user - * can pass NULL, and thus avoid unnecessary copying. - */ - if (err < 0 || report == NULL) - return err; - - report->rdstpptyint = 0b00010000 & resp[1]; - report->rdspiint = 0b00001000 & resp[1]; - report->rdssyncint = 0b00000010 & resp[1]; - report->rdsfifoint = 0b00000001 & resp[1]; - - report->tpptyvalid = 0b00010000 & resp[2]; - report->pivalid = 0b00001000 & resp[2]; - report->rdssync = 0b00000010 & resp[2]; - report->rdsfifolost = 0b00000001 & resp[2]; - - report->tp = 0b00100000 & resp[3]; - report->pty = 0b00011111 & resp[3]; - - report->pi = be16_to_cpup((__be16 *)(resp + 4)); - report->rdsfifoused = resp[6]; - - report->ble[V4L2_RDS_BLOCK_A] = 0b11000000 & resp[7]; - report->ble[V4L2_RDS_BLOCK_B] = 0b00110000 & resp[7]; - report->ble[V4L2_RDS_BLOCK_C] = 0b00001100 & resp[7]; - report->ble[V4L2_RDS_BLOCK_D] = 0b00000011 & resp[7]; - - report->rds[V4L2_RDS_BLOCK_A].block = V4L2_RDS_BLOCK_A; - report->rds[V4L2_RDS_BLOCK_A].msb = resp[8]; - report->rds[V4L2_RDS_BLOCK_A].lsb = resp[9]; - - report->rds[V4L2_RDS_BLOCK_B].block = V4L2_RDS_BLOCK_B; - report->rds[V4L2_RDS_BLOCK_B].msb = resp[10]; - report->rds[V4L2_RDS_BLOCK_B].lsb = resp[11]; - - report->rds[V4L2_RDS_BLOCK_C].block = V4L2_RDS_BLOCK_C; - report->rds[V4L2_RDS_BLOCK_C].msb = resp[12]; - report->rds[V4L2_RDS_BLOCK_C].lsb = resp[13]; - - report->rds[V4L2_RDS_BLOCK_D].block = V4L2_RDS_BLOCK_D; - report->rds[V4L2_RDS_BLOCK_D].msb = resp[14]; - report->rds[V4L2_RDS_BLOCK_D].lsb = resp[15]; - - return err; -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status); - -int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *core, - bool clear, - struct si476x_rds_blockcount_report *report) -{ - int err; - u8 resp[CMD_FM_RDS_BLOCKCOUNT_NRESP]; - const u8 args[CMD_FM_RDS_BLOCKCOUNT_NARGS] = { - clear, - }; - - if (!report) - return -EINVAL; - - err = si476x_core_send_command(core, CMD_FM_RDS_BLOCKCOUNT, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); - - if (!err) { - report->expected = be16_to_cpup((__be16 *)(resp + 2)); - report->received = be16_to_cpup((__be16 *)(resp + 4)); - report->uncorrectable = be16_to_cpup((__be16 *)(resp + 6)); - } - - return err; -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount); - -int si476x_core_cmd_fm_phase_diversity(struct si476x_core *core, - enum si476x_phase_diversity_mode mode) -{ - u8 resp[CMD_FM_PHASE_DIVERSITY_NRESP]; - const u8 args[CMD_FM_PHASE_DIVERSITY_NARGS] = { - mode & 0b111, - }; - - return si476x_core_send_command(core, CMD_FM_PHASE_DIVERSITY, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity); -/** - * si476x_core_cmd_fm_phase_div_status() - get the phase diversity - * status - * - * @core: si476x device - * - * NOTE caller must hold core lock - * - * Function returns the value of the status bit in case of success and - * negative error code in case of failre. - */ -int si476x_core_cmd_fm_phase_div_status(struct si476x_core *core) -{ - int err; - u8 resp[CMD_FM_PHASE_DIV_STATUS_NRESP]; - - err = si476x_core_send_command(core, CMD_FM_PHASE_DIV_STATUS, - NULL, 0, - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); - - return (err < 0) ? err : resp[1]; -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status); - - -/** - * si476x_cmd_am_seek_start - send 'FM_SEEK_START' command to the - * device - * @core - device to send the command to - * @seekup - if set the direction of the search is 'up' - * @wrap - if set seek wraps when hitting band limit - * - * This function begins search for a valid station. The station is - * considered valid when 'FM_VALID_SNR_THRESHOLD' and - * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria - * are met. - * - * Function returns 0 on success and negative error code on failure - */ -int si476x_core_cmd_am_seek_start(struct si476x_core *core, - bool seekup, bool wrap) -{ - u8 resp[CMD_AM_SEEK_START_NRESP]; - const u8 args[CMD_AM_SEEK_START_NARGS] = { - seekup << 3 | wrap << 2, - }; - - return si476x_cmd_tune_seek_freq(core, CMD_AM_SEEK_START, - args, sizeof(args), - resp, sizeof(resp)); -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start); - - - -static int si476x_core_cmd_power_up_a10(struct si476x_core *core, - struct si476x_power_up_args *puargs) -{ - u8 resp[CMD_POWER_UP_A10_NRESP]; - const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ); - const bool ctsen = (core->client->irq != 0); - const u8 args[CMD_POWER_UP_A10_NARGS] = { - 0xF7, /* Reserved, always 0xF7 */ - 0x3F & puargs->xcload, /* First two bits are reserved to be - * zeros */ - ctsen << 7 | intsel << 6 | 0x07, /* Last five bits - * are reserved to - * be written as 0x7 */ - puargs->func << 4 | puargs->freq, - 0x11, /* Reserved, always 0x11 */ - }; - - return si476x_core_send_command(core, CMD_POWER_UP, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_TIMEOUT_POWER_UP); -} - -static int si476x_core_cmd_power_up_a20(struct si476x_core *core, - struct si476x_power_up_args *puargs) -{ - u8 resp[CMD_POWER_UP_A20_NRESP]; - const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ); - const bool ctsen = (core->client->irq != 0); - const u8 args[CMD_POWER_UP_A20_NARGS] = { - puargs->ibias6x << 7 | puargs->xstart, - 0x3F & puargs->xcload, /* First two bits are reserved to be - * zeros */ - ctsen << 7 | intsel << 6 | puargs->fastboot << 5 | - puargs->xbiashc << 3 | puargs->xbias, - puargs->func << 4 | puargs->freq, - 0x10 | puargs->xmode, - }; - - return si476x_core_send_command(core, CMD_POWER_UP, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_TIMEOUT_POWER_UP); -} - -static int si476x_core_cmd_power_down_a10(struct si476x_core *core, - struct si476x_power_down_args *pdargs) -{ - u8 resp[CMD_POWER_DOWN_A10_NRESP]; - - return si476x_core_send_command(core, CMD_POWER_DOWN, - NULL, 0, - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); -} - -static int si476x_core_cmd_power_down_a20(struct si476x_core *core, - struct si476x_power_down_args *pdargs) -{ - u8 resp[CMD_POWER_DOWN_A20_NRESP]; - const u8 args[CMD_POWER_DOWN_A20_NARGS] = { - pdargs->xosc, - }; - return si476x_core_send_command(core, CMD_POWER_DOWN, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); -} - -static int si476x_core_cmd_am_tune_freq_a10(struct si476x_core *core, - struct si476x_tune_freq_args *tuneargs) -{ - - const int am_freq = tuneargs->freq; - u8 resp[CMD_AM_TUNE_FREQ_NRESP]; - const u8 args[CMD_AM_TUNE_FREQ_NARGS] = { - (tuneargs->hd << 6), - msb(am_freq), - lsb(am_freq), - }; - - return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, args, - sizeof(args), - resp, sizeof(resp)); -} - -static int si476x_core_cmd_am_tune_freq_a20(struct si476x_core *core, - struct si476x_tune_freq_args *tuneargs) -{ - const int am_freq = tuneargs->freq; - u8 resp[CMD_AM_TUNE_FREQ_NRESP]; - const u8 args[CMD_AM_TUNE_FREQ_NARGS] = { - (tuneargs->zifsr << 6) | (tuneargs->injside & 0b11), - msb(am_freq), - lsb(am_freq), - }; - - return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, - args, sizeof(args), - resp, sizeof(resp)); -} - -static int si476x_core_cmd_fm_rsq_status_a10(struct si476x_core *core, - struct si476x_rsq_status_args *rsqargs, - struct si476x_rsq_status_report *report) -{ - int err; - u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP]; - const u8 args[CMD_FM_RSQ_STATUS_A10_NARGS] = { - rsqargs->rsqack << 3 | rsqargs->attune << 2 | - rsqargs->cancel << 1 | rsqargs->stcack, - }; - - err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); - /* - * Besides getting received signal quality information this - * command can be used to just acknowledge different interrupt - * flags in those cases it is useless to copy and parse - * received data so user can pass NULL, and thus avoid - * unnecessary copying. - */ - if (err < 0 || report == NULL) - return err; - - report->multhint = 0b10000000 & resp[1]; - report->multlint = 0b01000000 & resp[1]; - report->snrhint = 0b00001000 & resp[1]; - report->snrlint = 0b00000100 & resp[1]; - report->rssihint = 0b00000010 & resp[1]; - report->rssilint = 0b00000001 & resp[1]; - - report->bltf = 0b10000000 & resp[2]; - report->snr_ready = 0b00100000 & resp[2]; - report->rssiready = 0b00001000 & resp[2]; - report->afcrl = 0b00000010 & resp[2]; - report->valid = 0b00000001 & resp[2]; - - report->readfreq = be16_to_cpup((__be16 *)(resp + 3)); - report->freqoff = resp[5]; - report->rssi = resp[6]; - report->snr = resp[7]; - report->lassi = resp[9]; - report->hassi = resp[10]; - report->mult = resp[11]; - report->dev = resp[12]; - report->readantcap = be16_to_cpup((__be16 *)(resp + 13)); - report->assi = resp[15]; - report->usn = resp[16]; - - return err; -} - -static int si476x_core_cmd_fm_rsq_status_a20(struct si476x_core *core, - struct si476x_rsq_status_args *rsqargs, - struct si476x_rsq_status_report *report) -{ - int err; - u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP]; - const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = { - rsqargs->primary << 4 | rsqargs->rsqack << 3 | - rsqargs->attune << 2 | rsqargs->cancel << 1 | - rsqargs->stcack, - }; - - err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); - /* - * Besides getting received signal quality information this - * command can be used to just acknowledge different interrupt - * flags in those cases it is useless to copy and parse - * received data so user can pass NULL, and thus avoid - * unnecessary copying. - */ - if (err < 0 || report == NULL) - return err; - - report->multhint = 0b10000000 & resp[1]; - report->multlint = 0b01000000 & resp[1]; - report->snrhint = 0b00001000 & resp[1]; - report->snrlint = 0b00000100 & resp[1]; - report->rssihint = 0b00000010 & resp[1]; - report->rssilint = 0b00000001 & resp[1]; - - report->bltf = 0b10000000 & resp[2]; - report->snr_ready = 0b00100000 & resp[2]; - report->rssiready = 0b00001000 & resp[2]; - report->afcrl = 0b00000010 & resp[2]; - report->valid = 0b00000001 & resp[2]; - - report->readfreq = be16_to_cpup((__be16 *)(resp + 3)); - report->freqoff = resp[5]; - report->rssi = resp[6]; - report->snr = resp[7]; - report->lassi = resp[9]; - report->hassi = resp[10]; - report->mult = resp[11]; - report->dev = resp[12]; - report->readantcap = be16_to_cpup((__be16 *)(resp + 13)); - report->assi = resp[15]; - report->usn = resp[16]; - - return err; -} - - -static int si476x_core_cmd_fm_rsq_status_a30(struct si476x_core *core, - struct si476x_rsq_status_args *rsqargs, - struct si476x_rsq_status_report *report) -{ - int err; - u8 resp[CMD_FM_RSQ_STATUS_A30_NRESP]; - const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = { - rsqargs->primary << 4 | rsqargs->rsqack << 3 | - rsqargs->attune << 2 | rsqargs->cancel << 1 | - rsqargs->stcack, - }; - - err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS, - args, ARRAY_SIZE(args), - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); - /* - * Besides getting received signal quality information this - * command can be used to just acknowledge different interrupt - * flags in those cases it is useless to copy and parse - * received data so user can pass NULL, and thus avoid - * unnecessary copying. - */ - if (err < 0 || report == NULL) - return err; - - report->multhint = 0b10000000 & resp[1]; - report->multlint = 0b01000000 & resp[1]; - report->snrhint = 0b00001000 & resp[1]; - report->snrlint = 0b00000100 & resp[1]; - report->rssihint = 0b00000010 & resp[1]; - report->rssilint = 0b00000001 & resp[1]; - - report->bltf = 0b10000000 & resp[2]; - report->snr_ready = 0b00100000 & resp[2]; - report->rssiready = 0b00001000 & resp[2]; - report->injside = 0b00000100 & resp[2]; - report->afcrl = 0b00000010 & resp[2]; - report->valid = 0b00000001 & resp[2]; - - report->readfreq = be16_to_cpup((__be16 *)(resp + 3)); - report->freqoff = resp[5]; - report->rssi = resp[6]; - report->snr = resp[7]; - report->issi = resp[8]; - report->lassi = resp[9]; - report->hassi = resp[10]; - report->mult = resp[11]; - report->dev = resp[12]; - report->readantcap = be16_to_cpup((__be16 *)(resp + 13)); - report->assi = resp[15]; - report->usn = resp[16]; - - report->pilotdev = resp[17]; - report->rdsdev = resp[18]; - report->assidev = resp[19]; - report->strongdev = resp[20]; - report->rdspi = be16_to_cpup((__be16 *)(resp + 21)); - - return err; -} - -static int si476x_core_cmd_fm_tune_freq_a10(struct si476x_core *core, - struct si476x_tune_freq_args *tuneargs) -{ - u8 resp[CMD_FM_TUNE_FREQ_NRESP]; - const u8 args[CMD_FM_TUNE_FREQ_A10_NARGS] = { - (tuneargs->hd << 6) | (tuneargs->tunemode << 4) - | (tuneargs->smoothmetrics << 2), - msb(tuneargs->freq), - lsb(tuneargs->freq), - msb(tuneargs->antcap), - lsb(tuneargs->antcap) - }; - - return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ, - args, sizeof(args), - resp, sizeof(resp)); -} - -static int si476x_core_cmd_fm_tune_freq_a20(struct si476x_core *core, - struct si476x_tune_freq_args *tuneargs) -{ - u8 resp[CMD_FM_TUNE_FREQ_NRESP]; - const u8 args[CMD_FM_TUNE_FREQ_A20_NARGS] = { - (tuneargs->hd << 6) | (tuneargs->tunemode << 4) - | (tuneargs->smoothmetrics << 2) | (tuneargs->injside), - msb(tuneargs->freq), - lsb(tuneargs->freq), - }; - - return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ, - args, sizeof(args), - resp, sizeof(resp)); -} - -static int si476x_core_cmd_agc_status_a20(struct si476x_core *core, - struct si476x_agc_status_report *report) -{ - int err; - u8 resp[CMD_AGC_STATUS_NRESP_A20]; - - if (!report) - return -EINVAL; - - err = si476x_core_send_command(core, CMD_AGC_STATUS, - NULL, 0, - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); - if (err < 0) - return err; - - report->mxhi = resp[1] & SI476X_AGC_MXHI; - report->mxlo = resp[1] & SI476X_AGC_MXLO; - report->lnahi = resp[1] & SI476X_AGC_LNAHI; - report->lnalo = resp[1] & SI476X_AGC_LNALO; - report->fmagc1 = resp[2]; - report->fmagc2 = resp[3]; - report->pgagain = resp[4]; - report->fmwblang = resp[5]; - - return err; -} - -static int si476x_core_cmd_agc_status_a10(struct si476x_core *core, - struct si476x_agc_status_report *report) -{ - int err; - u8 resp[CMD_AGC_STATUS_NRESP_A10]; - - if (!report) - return -EINVAL; - - err = si476x_core_send_command(core, CMD_AGC_STATUS, - NULL, 0, - resp, ARRAY_SIZE(resp), - SI476X_DEFAULT_TIMEOUT); - if (err < 0) - return err; - - report->mxhi = resp[1] & SI476X_AGC_MXHI; - report->mxlo = resp[1] & SI476X_AGC_MXLO; - report->lnahi = resp[1] & SI476X_AGC_LNAHI; - report->lnalo = resp[1] & SI476X_AGC_LNALO; - - return err; -} - -typedef int (*tune_freq_func_t) (struct si476x_core *core, - struct si476x_tune_freq_args *tuneargs); - -static struct { - int (*power_up) (struct si476x_core *, - struct si476x_power_up_args *); - int (*power_down) (struct si476x_core *, - struct si476x_power_down_args *); - - tune_freq_func_t fm_tune_freq; - tune_freq_func_t am_tune_freq; - - int (*fm_rsq_status)(struct si476x_core *, - struct si476x_rsq_status_args *, - struct si476x_rsq_status_report *); - - int (*agc_status)(struct si476x_core *, - struct si476x_agc_status_report *); - int (*intb_pin_cfg)(struct si476x_core *core, - enum si476x_intb_config intb, - enum si476x_a1_config a1); -} si476x_cmds_vtable[] = { - [SI476X_REVISION_A10] = { - .power_up = si476x_core_cmd_power_up_a10, - .power_down = si476x_core_cmd_power_down_a10, - .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a10, - .am_tune_freq = si476x_core_cmd_am_tune_freq_a10, - .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a10, - .agc_status = si476x_core_cmd_agc_status_a10, - .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a10, - }, - [SI476X_REVISION_A20] = { - .power_up = si476x_core_cmd_power_up_a20, - .power_down = si476x_core_cmd_power_down_a20, - .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20, - .am_tune_freq = si476x_core_cmd_am_tune_freq_a20, - .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a20, - .agc_status = si476x_core_cmd_agc_status_a20, - .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20, - }, - [SI476X_REVISION_A30] = { - .power_up = si476x_core_cmd_power_up_a20, - .power_down = si476x_core_cmd_power_down_a20, - .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20, - .am_tune_freq = si476x_core_cmd_am_tune_freq_a20, - .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a30, - .agc_status = si476x_core_cmd_agc_status_a20, - .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20, - }, -}; - -int si476x_core_cmd_power_up(struct si476x_core *core, - struct si476x_power_up_args *args) -{ - BUG_ON(core->revision > SI476X_REVISION_A30 || - core->revision == -1); - return si476x_cmds_vtable[core->revision].power_up(core, args); -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up); - -int si476x_core_cmd_power_down(struct si476x_core *core, - struct si476x_power_down_args *args) -{ - BUG_ON(core->revision > SI476X_REVISION_A30 || - core->revision == -1); - return si476x_cmds_vtable[core->revision].power_down(core, args); -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down); - -int si476x_core_cmd_fm_tune_freq(struct si476x_core *core, - struct si476x_tune_freq_args *args) -{ - BUG_ON(core->revision > SI476X_REVISION_A30 || - core->revision == -1); - return si476x_cmds_vtable[core->revision].fm_tune_freq(core, args); -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq); - -int si476x_core_cmd_am_tune_freq(struct si476x_core *core, - struct si476x_tune_freq_args *args) -{ - BUG_ON(core->revision > SI476X_REVISION_A30 || - core->revision == -1); - return si476x_cmds_vtable[core->revision].am_tune_freq(core, args); -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq); - -int si476x_core_cmd_fm_rsq_status(struct si476x_core *core, - struct si476x_rsq_status_args *args, - struct si476x_rsq_status_report *report) - -{ - BUG_ON(core->revision > SI476X_REVISION_A30 || - core->revision == -1); - return si476x_cmds_vtable[core->revision].fm_rsq_status(core, args, - report); -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status); - -int si476x_core_cmd_agc_status(struct si476x_core *core, - struct si476x_agc_status_report *report) - -{ - BUG_ON(core->revision > SI476X_REVISION_A30 || - core->revision == -1); - return si476x_cmds_vtable[core->revision].agc_status(core, report); -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status); - -int si476x_core_cmd_intb_pin_cfg(struct si476x_core *core, - enum si476x_intb_config intb, - enum si476x_a1_config a1) -{ - BUG_ON(core->revision > SI476X_REVISION_A30 || - core->revision == -1); - - return si476x_cmds_vtable[core->revision].intb_pin_cfg(core, intb, a1); -} -EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Andrey Smirnov "); -MODULE_DESCRIPTION("API for command exchange for si476x");