2013-04-18 20:58:27 +04:00
/*
* 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 < andrew . smirnov @ gmail . com >
*
* 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 <linux/module.h>
# include <linux/completion.h>
# include <linux/delay.h>
# include <linux/atomic.h>
# include <linux/i2c.h>
# include <linux/device.h>
# include <linux/gpio.h>
# include <linux/videodev2.h>
# include <linux/mfd/si476x-core.h>
2013-05-09 16:01:05 +04:00
# include <asm/unaligned.h>
2013-04-18 20:58:27 +04:00
# 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 ) ,
2013-05-09 00:23:42 +04:00
SI476X_ACF_SMATTN = 0x1f ,
2013-04-18 20:58:27 +04:00
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
2013-05-09 16:01:05 +04:00
return get_unaligned_be16 ( resp + 2 ) ;
2013-04-18 20:58:27 +04:00
}
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 1 MOhm 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 1 MOhm 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 1 MOhm 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 1 MOhm 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 1 MOhm 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 1 MOhm 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 1 MOhm 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 1 MOhm 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 1 MOhm 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 1 MOhm 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 1 MOhm 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 1 MOhm 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 1 MOhm 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 1 MOhm 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 1 MOhm 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 ;
2013-05-09 00:23:42 +04:00
report - > snrhint = 0x08 & resp [ 1 ] ;
report - > snrlint = 0x04 & resp [ 1 ] ;
report - > rssihint = 0x02 & resp [ 1 ] ;
report - > rssilint = 0x01 & resp [ 1 ] ;
2013-04-18 20:58:27 +04:00
2013-05-09 00:23:42 +04:00
report - > bltf = 0x80 & resp [ 2 ] ;
report - > snr_ready = 0x20 & resp [ 2 ] ;
report - > rssiready = 0x08 & resp [ 2 ] ;
report - > afcrl = 0x02 & resp [ 2 ] ;
report - > valid = 0x01 & resp [ 2 ] ;
2013-04-18 20:58:27 +04:00
2013-05-09 16:01:05 +04:00
report - > readfreq = get_unaligned_be16 ( resp + 3 ) ;
2013-04-18 20:58:27 +04:00
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 ;
2013-05-09 00:23:42 +04:00
report - > rdstpptyint = 0x10 & resp [ 1 ] ;
report - > rdspiint = 0x08 & resp [ 1 ] ;
report - > rdssyncint = 0x02 & resp [ 1 ] ;
report - > rdsfifoint = 0x01 & resp [ 1 ] ;
2013-04-18 20:58:27 +04:00
2013-05-09 00:23:42 +04:00
report - > tpptyvalid = 0x10 & resp [ 2 ] ;
report - > pivalid = 0x08 & resp [ 2 ] ;
report - > rdssync = 0x02 & resp [ 2 ] ;
report - > rdsfifolost = 0x01 & resp [ 2 ] ;
2013-04-18 20:58:27 +04:00
2013-05-09 00:23:42 +04:00
report - > tp = 0x20 & resp [ 3 ] ;
report - > pty = 0x1f & resp [ 3 ] ;
2013-04-18 20:58:27 +04:00
2013-05-09 16:01:05 +04:00
report - > pi = get_unaligned_be16 ( resp + 4 ) ;
2013-04-18 20:58:27 +04:00
report - > rdsfifoused = resp [ 6 ] ;
2013-05-09 00:23:42 +04:00
report - > ble [ V4L2_RDS_BLOCK_A ] = 0xc0 & resp [ 7 ] ;
report - > ble [ V4L2_RDS_BLOCK_B ] = 0x30 & resp [ 7 ] ;
report - > ble [ V4L2_RDS_BLOCK_C ] = 0x0c & resp [ 7 ] ;
report - > ble [ V4L2_RDS_BLOCK_D ] = 0x03 & resp [ 7 ] ;
2013-04-18 20:58:27 +04:00
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 ) {
2013-05-09 16:01:05 +04:00
report - > expected = get_unaligned_be16 ( resp + 2 ) ;
report - > received = get_unaligned_be16 ( resp + 4 ) ;
report - > uncorrectable = get_unaligned_be16 ( resp + 6 ) ;
2013-04-18 20:58:27 +04:00
}
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 ] = {
2013-05-09 00:23:42 +04:00
mode & 0x07 ,
2013-04-18 20:58:27 +04:00
} ;
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 ] = {
2013-05-09 00:23:42 +04:00
( tuneargs - > zifsr < < 6 ) | ( tuneargs - > injside & 0x03 ) ,
2013-04-18 20:58:27 +04:00
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 ;
2013-05-09 00:23:42 +04:00
report - > multhint = 0x80 & resp [ 1 ] ;
report - > multlint = 0x40 & resp [ 1 ] ;
report - > snrhint = 0x08 & resp [ 1 ] ;
report - > snrlint = 0x04 & resp [ 1 ] ;
report - > rssihint = 0x02 & resp [ 1 ] ;
report - > rssilint = 0x01 & resp [ 1 ] ;
2013-04-18 20:58:27 +04:00
2013-05-09 00:23:42 +04:00
report - > bltf = 0x80 & resp [ 2 ] ;
report - > snr_ready = 0x20 & resp [ 2 ] ;
report - > rssiready = 0x08 & resp [ 2 ] ;
report - > afcrl = 0x02 & resp [ 2 ] ;
report - > valid = 0x01 & resp [ 2 ] ;
2013-04-18 20:58:27 +04:00
2013-05-09 16:01:05 +04:00
report - > readfreq = get_unaligned_be16 ( resp + 3 ) ;
2013-04-18 20:58:27 +04:00
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 ] ;
2013-05-09 16:01:05 +04:00
report - > readantcap = get_unaligned_be16 ( resp + 13 ) ;
2013-04-18 20:58:27 +04:00
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 ;
2013-05-09 00:23:42 +04:00
report - > multhint = 0x80 & resp [ 1 ] ;
report - > multlint = 0x40 & resp [ 1 ] ;
report - > snrhint = 0x08 & resp [ 1 ] ;
report - > snrlint = 0x04 & resp [ 1 ] ;
report - > rssihint = 0x02 & resp [ 1 ] ;
report - > rssilint = 0x01 & resp [ 1 ] ;
2013-04-18 20:58:27 +04:00
2013-05-09 00:23:42 +04:00
report - > bltf = 0x80 & resp [ 2 ] ;
report - > snr_ready = 0x20 & resp [ 2 ] ;
report - > rssiready = 0x08 & resp [ 2 ] ;
report - > afcrl = 0x02 & resp [ 2 ] ;
report - > valid = 0x01 & resp [ 2 ] ;
2013-04-18 20:58:27 +04:00
2013-05-09 16:01:05 +04:00
report - > readfreq = get_unaligned_be16 ( resp + 3 ) ;
2013-04-18 20:58:27 +04:00
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 ] ;
2013-05-09 16:01:05 +04:00
report - > readantcap = get_unaligned_be16 ( resp + 13 ) ;
2013-04-18 20:58:27 +04:00
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 ;
2013-05-09 00:23:42 +04:00
report - > multhint = 0x80 & resp [ 1 ] ;
report - > multlint = 0x40 & resp [ 1 ] ;
report - > snrhint = 0x08 & resp [ 1 ] ;
report - > snrlint = 0x04 & resp [ 1 ] ;
report - > rssihint = 0x02 & resp [ 1 ] ;
report - > rssilint = 0x01 & resp [ 1 ] ;
report - > bltf = 0x80 & resp [ 2 ] ;
report - > snr_ready = 0x20 & resp [ 2 ] ;
report - > rssiready = 0x08 & resp [ 2 ] ;
report - > injside = 0x04 & resp [ 2 ] ;
report - > afcrl = 0x02 & resp [ 2 ] ;
report - > valid = 0x01 & resp [ 2 ] ;
2013-04-18 20:58:27 +04:00
2013-05-09 16:01:05 +04:00
report - > readfreq = get_unaligned_be16 ( resp + 3 ) ;
2013-04-18 20:58:27 +04:00
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 ] ;
2013-05-09 16:01:05 +04:00
report - > readantcap = get_unaligned_be16 ( resp + 13 ) ;
2013-04-18 20:58:27 +04:00
report - > assi = resp [ 15 ] ;
report - > usn = resp [ 16 ] ;
report - > pilotdev = resp [ 17 ] ;
report - > rdsdev = resp [ 18 ] ;
report - > assidev = resp [ 19 ] ;
report - > strongdev = resp [ 20 ] ;
2013-05-09 16:01:05 +04:00
report - > rdspi = get_unaligned_be16 ( resp + 21 ) ;
2013-04-18 20:58:27 +04:00
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 <andrew.smirnov@gmail.com> " ) ;
MODULE_DESCRIPTION ( " API for command exchange for si476x " ) ;