2011-03-23 02:34:40 +03:00
/*
* Load Analog Devices SigmaStudio firmware files
*
2014-11-19 20:29:05 +03:00
* Copyright 2009 - 2014 Analog Devices Inc .
2011-03-23 02:34:40 +03:00
*
* Licensed under the GPL - 2 or later .
*/
# include <linux/crc32.h>
# include <linux/firmware.h>
# include <linux/kernel.h>
# include <linux/i2c.h>
2011-11-28 12:44:20 +04:00
# include <linux/regmap.h>
2011-07-26 04:13:21 +04:00
# include <linux/module.h>
2014-11-19 20:29:05 +03:00
# include <linux/slab.h>
2014-11-19 20:29:06 +03:00
# include <sound/control.h>
2014-11-19 20:29:05 +03:00
# include <sound/soc.h>
2011-11-28 12:44:17 +04:00
# include "sigmadsp.h"
2011-03-23 02:34:40 +03:00
2011-11-28 12:44:19 +04:00
# define SIGMA_MAGIC "ADISIGM"
2014-11-19 20:29:06 +03:00
# define SIGMA_FW_CHUNK_TYPE_DATA 0
# define SIGMA_FW_CHUNK_TYPE_CONTROL 1
# define SIGMA_FW_CHUNK_TYPE_SAMPLERATES 2
struct sigmadsp_control {
struct list_head head ;
uint32_t samplerates ;
unsigned int addr ;
unsigned int num_bytes ;
const char * name ;
struct snd_kcontrol * kcontrol ;
bool cached ;
uint8_t cache [ ] ;
} ;
2014-11-19 20:29:05 +03:00
struct sigmadsp_data {
struct list_head head ;
2014-11-19 20:29:06 +03:00
uint32_t samplerates ;
2014-11-19 20:29:05 +03:00
unsigned int addr ;
unsigned int length ;
uint8_t data [ ] ;
} ;
2014-11-19 20:29:06 +03:00
struct sigma_fw_chunk {
__le32 length ;
__le32 tag ;
__le32 samplerates ;
} __packed ;
struct sigma_fw_chunk_data {
struct sigma_fw_chunk chunk ;
__le16 addr ;
uint8_t data [ ] ;
} __packed ;
struct sigma_fw_chunk_control {
struct sigma_fw_chunk chunk ;
__le16 type ;
__le16 addr ;
__le16 num_bytes ;
const char name [ ] ;
} __packed ;
struct sigma_fw_chunk_samplerate {
struct sigma_fw_chunk chunk ;
__le32 samplerates [ ] ;
} __packed ;
2011-11-28 12:44:19 +04:00
struct sigma_firmware_header {
unsigned char magic [ 7 ] ;
u8 version ;
__le32 crc ;
} __packed ;
enum {
SIGMA_ACTION_WRITEXBYTES = 0 ,
SIGMA_ACTION_WRITESINGLE ,
SIGMA_ACTION_WRITESAFELOAD ,
SIGMA_ACTION_END ,
} ;
2014-11-19 20:29:05 +03:00
struct sigma_action {
u8 instr ;
u8 len_hi ;
__le16 len ;
__be16 addr ;
unsigned char payload [ ] ;
} __packed ;
static int sigmadsp_write ( struct sigmadsp * sigmadsp , unsigned int addr ,
const uint8_t data [ ] , size_t len )
{
return sigmadsp - > write ( sigmadsp - > control_data , addr , data , len ) ;
}
2014-11-19 20:29:06 +03:00
static int sigmadsp_read ( struct sigmadsp * sigmadsp , unsigned int addr ,
uint8_t data [ ] , size_t len )
{
return sigmadsp - > read ( sigmadsp - > control_data , addr , data , len ) ;
}
static int sigmadsp_ctrl_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * info )
{
struct sigmadsp_control * ctrl = ( void * ) kcontrol - > private_value ;
info - > type = SNDRV_CTL_ELEM_TYPE_BYTES ;
info - > count = ctrl - > num_bytes ;
return 0 ;
}
static int sigmadsp_ctrl_write ( struct sigmadsp * sigmadsp ,
struct sigmadsp_control * ctrl , void * data )
{
/* safeload loads up to 20 bytes in a atomic operation */
if ( ctrl - > num_bytes > 4 & & ctrl - > num_bytes < = 20 & & sigmadsp - > ops & &
sigmadsp - > ops - > safeload )
return sigmadsp - > ops - > safeload ( sigmadsp , ctrl - > addr , data ,
ctrl - > num_bytes ) ;
else
return sigmadsp_write ( sigmadsp , ctrl - > addr , data ,
ctrl - > num_bytes ) ;
}
static int sigmadsp_ctrl_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct sigmadsp_control * ctrl = ( void * ) kcontrol - > private_value ;
struct sigmadsp * sigmadsp = snd_kcontrol_chip ( kcontrol ) ;
uint8_t * data ;
int ret = 0 ;
mutex_lock ( & sigmadsp - > lock ) ;
data = ucontrol - > value . bytes . data ;
if ( ! ( kcontrol - > vd [ 0 ] . access & SNDRV_CTL_ELEM_ACCESS_INACTIVE ) )
ret = sigmadsp_ctrl_write ( sigmadsp , ctrl , data ) ;
if ( ret = = 0 ) {
memcpy ( ctrl - > cache , data , ctrl - > num_bytes ) ;
ctrl - > cached = true ;
}
mutex_unlock ( & sigmadsp - > lock ) ;
return ret ;
}
static int sigmadsp_ctrl_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct sigmadsp_control * ctrl = ( void * ) kcontrol - > private_value ;
struct sigmadsp * sigmadsp = snd_kcontrol_chip ( kcontrol ) ;
int ret = 0 ;
mutex_lock ( & sigmadsp - > lock ) ;
if ( ! ctrl - > cached ) {
ret = sigmadsp_read ( sigmadsp , ctrl - > addr , ctrl - > cache ,
ctrl - > num_bytes ) ;
}
if ( ret = = 0 ) {
ctrl - > cached = true ;
memcpy ( ucontrol - > value . bytes . data , ctrl - > cache ,
ctrl - > num_bytes ) ;
}
mutex_unlock ( & sigmadsp - > lock ) ;
return ret ;
}
static void sigmadsp_control_free ( struct snd_kcontrol * kcontrol )
{
struct sigmadsp_control * ctrl = ( void * ) kcontrol - > private_value ;
ctrl - > kcontrol = NULL ;
}
static bool sigma_fw_validate_control_name ( const char * name , unsigned int len )
{
unsigned int i ;
for ( i = 0 ; i < len ; i + + ) {
/* Normal ASCII characters are valid */
if ( name [ i ] < ' ' | | name [ i ] > ' ~ ' )
return false ;
}
return true ;
}
static int sigma_fw_load_control ( struct sigmadsp * sigmadsp ,
const struct sigma_fw_chunk * chunk , unsigned int length )
{
const struct sigma_fw_chunk_control * ctrl_chunk ;
struct sigmadsp_control * ctrl ;
unsigned int num_bytes ;
size_t name_len ;
char * name ;
int ret ;
if ( length < = sizeof ( * ctrl_chunk ) )
return - EINVAL ;
ctrl_chunk = ( const struct sigma_fw_chunk_control * ) chunk ;
name_len = length - sizeof ( * ctrl_chunk ) ;
if ( name_len > = SNDRV_CTL_ELEM_ID_NAME_MAXLEN )
name_len = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1 ;
/* Make sure there are no non-displayable characaters in the string */
if ( ! sigma_fw_validate_control_name ( ctrl_chunk - > name , name_len ) )
return - EINVAL ;
num_bytes = le16_to_cpu ( ctrl_chunk - > num_bytes ) ;
ctrl = kzalloc ( sizeof ( * ctrl ) + num_bytes , GFP_KERNEL ) ;
if ( ! ctrl )
return - ENOMEM ;
name = kzalloc ( name_len + 1 , GFP_KERNEL ) ;
if ( ! name ) {
ret = - ENOMEM ;
goto err_free_ctrl ;
}
memcpy ( name , ctrl_chunk - > name , name_len ) ;
name [ name_len ] = ' \0 ' ;
ctrl - > name = name ;
ctrl - > addr = le16_to_cpu ( ctrl_chunk - > addr ) ;
ctrl - > num_bytes = num_bytes ;
2014-11-21 20:53:52 +03:00
ctrl - > samplerates = le32_to_cpu ( chunk - > samplerates ) ;
2014-11-19 20:29:06 +03:00
list_add_tail ( & ctrl - > head , & sigmadsp - > ctrl_list ) ;
return 0 ;
err_free_ctrl :
kfree ( ctrl ) ;
return ret ;
}
static int sigma_fw_load_data ( struct sigmadsp * sigmadsp ,
const struct sigma_fw_chunk * chunk , unsigned int length )
{
const struct sigma_fw_chunk_data * data_chunk ;
struct sigmadsp_data * data ;
if ( length < = sizeof ( * data_chunk ) )
return - EINVAL ;
data_chunk = ( struct sigma_fw_chunk_data * ) chunk ;
length - = sizeof ( * data_chunk ) ;
data = kzalloc ( sizeof ( * data ) + length , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
data - > addr = le16_to_cpu ( data_chunk - > addr ) ;
data - > length = length ;
2014-11-21 20:53:52 +03:00
data - > samplerates = le32_to_cpu ( chunk - > samplerates ) ;
2014-11-19 20:29:06 +03:00
memcpy ( data - > data , data_chunk - > data , length ) ;
list_add_tail ( & data - > head , & sigmadsp - > data_list ) ;
return 0 ;
}
static int sigma_fw_load_samplerates ( struct sigmadsp * sigmadsp ,
const struct sigma_fw_chunk * chunk , unsigned int length )
{
const struct sigma_fw_chunk_samplerate * rate_chunk ;
unsigned int num_rates ;
unsigned int * rates ;
unsigned int i ;
rate_chunk = ( const struct sigma_fw_chunk_samplerate * ) chunk ;
num_rates = ( length - sizeof ( * rate_chunk ) ) / sizeof ( __le32 ) ;
if ( num_rates > 32 | | num_rates = = 0 )
return - EINVAL ;
/* We only allow one samplerates block per file */
if ( sigmadsp - > rate_constraints . count )
return - EINVAL ;
rates = kcalloc ( num_rates , sizeof ( * rates ) , GFP_KERNEL ) ;
if ( ! rates )
return - ENOMEM ;
for ( i = 0 ; i < num_rates ; i + + )
rates [ i ] = le32_to_cpu ( rate_chunk - > samplerates [ i ] ) ;
sigmadsp - > rate_constraints . count = num_rates ;
sigmadsp - > rate_constraints . list = rates ;
return 0 ;
}
static int sigmadsp_fw_load_v2 ( struct sigmadsp * sigmadsp ,
const struct firmware * fw )
{
struct sigma_fw_chunk * chunk ;
unsigned int length , pos ;
int ret ;
/*
* Make sure that there is at least one chunk to avoid integer
* underflows later on . Empty firmware is still valid though .
*/
if ( fw - > size < sizeof ( * chunk ) + sizeof ( struct sigma_firmware_header ) )
return 0 ;
pos = sizeof ( struct sigma_firmware_header ) ;
while ( pos < fw - > size - sizeof ( * chunk ) ) {
chunk = ( struct sigma_fw_chunk * ) ( fw - > data + pos ) ;
length = le32_to_cpu ( chunk - > length ) ;
if ( length > fw - > size - pos | | length < sizeof ( * chunk ) )
return - EINVAL ;
2014-11-21 20:53:52 +03:00
switch ( le32_to_cpu ( chunk - > tag ) ) {
2014-11-19 20:29:06 +03:00
case SIGMA_FW_CHUNK_TYPE_DATA :
ret = sigma_fw_load_data ( sigmadsp , chunk , length ) ;
break ;
case SIGMA_FW_CHUNK_TYPE_CONTROL :
ret = sigma_fw_load_control ( sigmadsp , chunk , length ) ;
break ;
case SIGMA_FW_CHUNK_TYPE_SAMPLERATES :
ret = sigma_fw_load_samplerates ( sigmadsp , chunk , length ) ;
break ;
default :
dev_warn ( sigmadsp - > dev , " Unknown chunk type: %d \n " ,
chunk - > tag ) ;
ret = 0 ;
break ;
}
if ( ret )
return ret ;
/*
* This can not overflow since if length is larger than the
* maximum firmware size ( 0x4000000 ) we ' ll error out earilier .
*/
pos + = ALIGN ( length , sizeof ( __le32 ) ) ;
}
return 0 ;
}
2011-11-28 12:44:19 +04:00
static inline u32 sigma_action_len ( struct sigma_action * sa )
{
return ( sa - > len_hi < < 16 ) | le16_to_cpu ( sa - > len ) ;
}
2011-11-28 12:44:14 +04:00
static size_t sigma_action_size ( struct sigma_action * sa )
{
size_t payload = 0 ;
switch ( sa - > instr ) {
case SIGMA_ACTION_WRITEXBYTES :
case SIGMA_ACTION_WRITESINGLE :
case SIGMA_ACTION_WRITESAFELOAD :
payload = sigma_action_len ( sa ) ;
break ;
default :
break ;
}
payload = ALIGN ( payload , 2 ) ;
return payload + sizeof ( struct sigma_action ) ;
}
/*
* Returns a negative error value in case of an error , 0 if processing of
* the firmware should be stopped after this action , 1 otherwise .
*/
2014-11-19 20:29:05 +03:00
static int process_sigma_action ( struct sigmadsp * sigmadsp ,
struct sigma_action * sa )
2011-03-23 02:34:40 +03:00
{
size_t len = sigma_action_len ( sa ) ;
2014-11-19 20:29:05 +03:00
struct sigmadsp_data * data ;
2011-03-23 02:34:40 +03:00
pr_debug ( " %s: instr:%i addr:%#x len:%zu \n " , __func__ ,
sa - > instr , sa - > addr , len ) ;
switch ( sa - > instr ) {
case SIGMA_ACTION_WRITEXBYTES :
case SIGMA_ACTION_WRITESINGLE :
case SIGMA_ACTION_WRITESAFELOAD :
2014-11-19 20:29:05 +03:00
if ( len < 3 )
2011-03-23 02:34:40 +03:00
return - EINVAL ;
2014-11-19 20:29:05 +03:00
data = kzalloc ( sizeof ( * data ) + len - 2 , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
data - > addr = be16_to_cpu ( sa - > addr ) ;
data - > length = len - 2 ;
memcpy ( data - > data , sa - > payload , data - > length ) ;
list_add_tail ( & data - > head , & sigmadsp - > data_list ) ;
2011-03-23 02:34:40 +03:00
break ;
case SIGMA_ACTION_END :
2011-11-28 12:44:14 +04:00
return 0 ;
2011-03-23 02:34:40 +03:00
default :
return - EINVAL ;
}
2011-11-28 12:44:14 +04:00
return 1 ;
2011-03-23 02:34:40 +03:00
}
2014-11-19 20:29:05 +03:00
static int sigmadsp_fw_load_v1 ( struct sigmadsp * sigmadsp ,
const struct firmware * fw )
2011-03-23 02:34:40 +03:00
{
2011-11-28 12:44:14 +04:00
struct sigma_action * sa ;
2014-11-19 20:29:05 +03:00
size_t size , pos ;
2011-11-28 12:44:14 +04:00
int ret ;
2014-11-19 20:29:05 +03:00
pos = sizeof ( struct sigma_firmware_header ) ;
while ( pos + sizeof ( * sa ) < = fw - > size ) {
sa = ( struct sigma_action * ) ( fw - > data + pos ) ;
2011-11-28 12:44:14 +04:00
size = sigma_action_size ( sa ) ;
2014-11-19 20:29:05 +03:00
pos + = size ;
if ( pos > fw - > size | | size = = 0 )
2011-11-28 12:44:14 +04:00
break ;
2014-11-19 20:29:05 +03:00
ret = process_sigma_action ( sigmadsp , sa ) ;
2011-03-23 02:34:40 +03:00
pr_debug ( " %s: action returned %i \n " , __func__ , ret ) ;
2011-11-28 12:44:14 +04:00
if ( ret < = 0 )
2011-03-23 02:34:40 +03:00
return ret ;
}
2011-11-28 12:44:14 +04:00
2014-11-19 20:29:05 +03:00
if ( pos ! = fw - > size )
2011-11-28 12:44:14 +04:00
return - EINVAL ;
return 0 ;
2011-03-23 02:34:40 +03:00
}
2014-11-19 20:29:05 +03:00
static void sigmadsp_firmware_release ( struct sigmadsp * sigmadsp )
2011-03-23 02:34:40 +03:00
{
2014-11-19 20:29:06 +03:00
struct sigmadsp_control * ctrl , * _ctrl ;
2014-11-19 20:29:05 +03:00
struct sigmadsp_data * data , * _data ;
2014-11-19 20:29:06 +03:00
list_for_each_entry_safe ( ctrl , _ctrl , & sigmadsp - > ctrl_list , head ) {
kfree ( ctrl - > name ) ;
kfree ( ctrl ) ;
}
2014-11-19 20:29:05 +03:00
list_for_each_entry_safe ( data , _data , & sigmadsp - > data_list , head )
kfree ( data ) ;
2014-11-19 20:29:06 +03:00
INIT_LIST_HEAD ( & sigmadsp - > ctrl_list ) ;
2014-11-19 20:29:05 +03:00
INIT_LIST_HEAD ( & sigmadsp - > data_list ) ;
}
static void devm_sigmadsp_release ( struct device * dev , void * res )
{
sigmadsp_firmware_release ( ( struct sigmadsp * ) res ) ;
}
static int sigmadsp_firmware_load ( struct sigmadsp * sigmadsp , const char * name )
{
const struct sigma_firmware_header * ssfw_head ;
2011-03-23 02:34:40 +03:00
const struct firmware * fw ;
2014-11-19 20:29:05 +03:00
int ret ;
2011-03-23 02:34:40 +03:00
u32 crc ;
/* first load the blob */
2014-11-19 20:29:05 +03:00
ret = request_firmware ( & fw , name , sigmadsp - > dev ) ;
2011-03-23 02:34:40 +03:00
if ( ret ) {
pr_debug ( " %s: request_firmware() failed with %i \n " , __func__ , ret ) ;
2014-11-19 20:29:05 +03:00
goto done ;
2011-03-23 02:34:40 +03:00
}
/* then verify the header */
ret = - EINVAL ;
2011-11-28 12:44:14 +04:00
/*
* Reject too small or unreasonable large files . The upper limit has been
* chosen a bit arbitrarily , but it should be enough for all practical
* purposes and having the limit makes it easier to avoid integer
* overflows later in the loading process .
*/
2011-11-28 12:44:18 +04:00
if ( fw - > size < sizeof ( * ssfw_head ) | | fw - > size > = 0x4000000 ) {
2014-11-19 20:29:05 +03:00
dev_err ( sigmadsp - > dev , " Failed to load firmware: Invalid size \n " ) ;
2011-03-23 02:34:40 +03:00
goto done ;
2011-11-28 12:44:18 +04:00
}
2011-03-23 02:34:40 +03:00
ssfw_head = ( void * ) fw - > data ;
2011-11-28 12:44:18 +04:00
if ( memcmp ( ssfw_head - > magic , SIGMA_MAGIC , ARRAY_SIZE ( ssfw_head - > magic ) ) ) {
2014-11-19 20:29:05 +03:00
dev_err ( sigmadsp - > dev , " Failed to load firmware: Invalid magic \n " ) ;
2014-11-19 20:29:02 +03:00
goto done ;
}
2011-11-28 12:44:15 +04:00
crc = crc32 ( 0 , fw - > data + sizeof ( * ssfw_head ) ,
fw - > size - sizeof ( * ssfw_head ) ) ;
2011-03-23 02:34:40 +03:00
pr_debug ( " %s: crc=%x \n " , __func__ , crc ) ;
2011-11-28 12:44:18 +04:00
if ( crc ! = le32_to_cpu ( ssfw_head - > crc ) ) {
2014-11-19 20:29:05 +03:00
dev_err ( sigmadsp - > dev , " Failed to load firmware: Wrong crc checksum: expected %x got %x \n " ,
2011-11-28 12:44:18 +04:00
le32_to_cpu ( ssfw_head - > crc ) , crc ) ;
2011-03-23 02:34:40 +03:00
goto done ;
2011-11-28 12:44:18 +04:00
}
2011-03-23 02:34:40 +03:00
2014-11-19 20:29:05 +03:00
switch ( ssfw_head - > version ) {
case 1 :
ret = sigmadsp_fw_load_v1 ( sigmadsp , fw ) ;
break ;
2014-11-19 20:29:06 +03:00
case 2 :
ret = sigmadsp_fw_load_v2 ( sigmadsp , fw ) ;
break ;
2014-11-19 20:29:05 +03:00
default :
dev_err ( sigmadsp - > dev ,
2014-11-19 20:29:06 +03:00
" Failed to load firmware: Invalid version %d. Supported firmware versions: 1, 2 \n " ,
2014-11-19 20:29:05 +03:00
ssfw_head - > version ) ;
ret = - EINVAL ;
break ;
}
2011-03-23 02:34:40 +03:00
2014-11-19 20:29:05 +03:00
if ( ret )
sigmadsp_firmware_release ( sigmadsp ) ;
2011-03-23 02:34:40 +03:00
2014-11-19 20:29:05 +03:00
done :
2011-03-23 02:34:40 +03:00
release_firmware ( fw ) ;
2014-11-19 20:29:05 +03:00
return ret ;
}
static int sigmadsp_init ( struct sigmadsp * sigmadsp , struct device * dev ,
const struct sigmadsp_ops * ops , const char * firmware_name )
{
sigmadsp - > ops = ops ;
sigmadsp - > dev = dev ;
2014-11-19 20:29:06 +03:00
INIT_LIST_HEAD ( & sigmadsp - > ctrl_list ) ;
2014-11-19 20:29:05 +03:00
INIT_LIST_HEAD ( & sigmadsp - > data_list ) ;
2014-11-19 20:29:06 +03:00
mutex_init ( & sigmadsp - > lock ) ;
2014-11-19 20:29:05 +03:00
return sigmadsp_firmware_load ( sigmadsp , firmware_name ) ;
}
/**
* devm_sigmadsp_init ( ) - Initialize SigmaDSP instance
* @ dev : The parent device
* @ ops : The sigmadsp_ops to use for this instance
* @ firmware_name : Name of the firmware file to load
*
* Allocates a SigmaDSP instance and loads the specified firmware file .
*
* Returns a pointer to a struct sigmadsp on success , or a PTR_ERR ( ) on error .
*/
struct sigmadsp * devm_sigmadsp_init ( struct device * dev ,
const struct sigmadsp_ops * ops , const char * firmware_name )
{
struct sigmadsp * sigmadsp ;
int ret ;
sigmadsp = devres_alloc ( devm_sigmadsp_release , sizeof ( * sigmadsp ) ,
GFP_KERNEL ) ;
if ( ! sigmadsp )
return ERR_PTR ( - ENOMEM ) ;
ret = sigmadsp_init ( sigmadsp , dev , ops , firmware_name ) ;
if ( ret ) {
devres_free ( sigmadsp ) ;
return ERR_PTR ( ret ) ;
}
devres_add ( dev , sigmadsp ) ;
return sigmadsp ;
}
EXPORT_SYMBOL_GPL ( devm_sigmadsp_init ) ;
2014-11-19 20:29:06 +03:00
static int sigmadsp_rate_to_index ( struct sigmadsp * sigmadsp , unsigned int rate )
{
unsigned int i ;
for ( i = 0 ; i < sigmadsp - > rate_constraints . count ; i + + ) {
if ( sigmadsp - > rate_constraints . list [ i ] = = rate )
return i ;
}
return - EINVAL ;
}
static unsigned int sigmadsp_get_samplerate_mask ( struct sigmadsp * sigmadsp ,
unsigned int samplerate )
{
int samplerate_index ;
if ( samplerate = = 0 )
return 0 ;
if ( sigmadsp - > rate_constraints . count ) {
samplerate_index = sigmadsp_rate_to_index ( sigmadsp , samplerate ) ;
if ( samplerate_index < 0 )
return 0 ;
return BIT ( samplerate_index ) ;
} else {
return ~ 0 ;
}
}
static bool sigmadsp_samplerate_valid ( unsigned int supported ,
unsigned int requested )
{
/* All samplerates are supported */
if ( ! supported )
return true ;
return supported & requested ;
}
static int sigmadsp_alloc_control ( struct sigmadsp * sigmadsp ,
struct sigmadsp_control * ctrl , unsigned int samplerate_mask )
{
struct snd_kcontrol_new template ;
struct snd_kcontrol * kcontrol ;
memset ( & template , 0 , sizeof ( template ) ) ;
template . iface = SNDRV_CTL_ELEM_IFACE_MIXER ;
template . name = ctrl - > name ;
template . info = sigmadsp_ctrl_info ;
template . get = sigmadsp_ctrl_get ;
template . put = sigmadsp_ctrl_put ;
template . private_value = ( unsigned long ) ctrl ;
template . access = SNDRV_CTL_ELEM_ACCESS_READWRITE ;
if ( ! sigmadsp_samplerate_valid ( ctrl - > samplerates , samplerate_mask ) )
template . access | = SNDRV_CTL_ELEM_ACCESS_INACTIVE ;
kcontrol = snd_ctl_new1 ( & template , sigmadsp ) ;
if ( ! kcontrol )
return - ENOMEM ;
kcontrol - > private_free = sigmadsp_control_free ;
ctrl - > kcontrol = kcontrol ;
2014-11-24 23:49:44 +03:00
return snd_ctl_add ( sigmadsp - > component - > card - > snd_card , kcontrol ) ;
2014-11-19 20:29:06 +03:00
}
static void sigmadsp_activate_ctrl ( struct sigmadsp * sigmadsp ,
struct sigmadsp_control * ctrl , unsigned int samplerate_mask )
{
struct snd_card * card = sigmadsp - > component - > card - > snd_card ;
struct snd_kcontrol_volatile * vd ;
struct snd_ctl_elem_id id ;
2014-11-27 01:35:16 +03:00
bool active ;
bool changed = false ;
2014-11-19 20:29:06 +03:00
active = sigmadsp_samplerate_valid ( ctrl - > samplerates , samplerate_mask ) ;
down_write ( & card - > controls_rwsem ) ;
if ( ! ctrl - > kcontrol ) {
up_write ( & card - > controls_rwsem ) ;
return ;
}
id = ctrl - > kcontrol - > id ;
vd = & ctrl - > kcontrol - > vd [ 0 ] ;
if ( active = = ( bool ) ( vd - > access & SNDRV_CTL_ELEM_ACCESS_INACTIVE ) ) {
vd - > access ^ = SNDRV_CTL_ELEM_ACCESS_INACTIVE ;
changed = true ;
}
up_write ( & card - > controls_rwsem ) ;
if ( active & & changed ) {
mutex_lock ( & sigmadsp - > lock ) ;
if ( ctrl - > cached )
sigmadsp_ctrl_write ( sigmadsp , ctrl , ctrl - > cache ) ;
mutex_unlock ( & sigmadsp - > lock ) ;
}
if ( changed )
snd_ctl_notify ( card , SNDRV_CTL_EVENT_MASK_INFO , & id ) ;
}
2014-11-19 20:29:05 +03:00
/**
* sigmadsp_attach ( ) - Attach a sigmadsp instance to a ASoC component
* @ sigmadsp : The sigmadsp instance to attach
* @ component : The component to attach to
*
* Typically called in the components probe callback .
*
* Note , once this function has been called the firmware must not be released
* until after the ALSA snd_card that the component belongs to has been
* disconnected , even if sigmadsp_attach ( ) returns an error .
*/
int sigmadsp_attach ( struct sigmadsp * sigmadsp ,
struct snd_soc_component * component )
{
2014-11-19 20:29:06 +03:00
struct sigmadsp_control * ctrl ;
unsigned int samplerate_mask ;
int ret ;
2014-11-19 20:29:05 +03:00
sigmadsp - > component = component ;
2014-11-19 20:29:06 +03:00
samplerate_mask = sigmadsp_get_samplerate_mask ( sigmadsp ,
sigmadsp - > current_samplerate ) ;
list_for_each_entry ( ctrl , & sigmadsp - > ctrl_list , head ) {
ret = sigmadsp_alloc_control ( sigmadsp , ctrl , samplerate_mask ) ;
if ( ret )
return ret ;
}
2014-11-19 20:29:05 +03:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( sigmadsp_attach ) ;
/**
* sigmadsp_setup ( ) - Setup the DSP for the specified samplerate
* @ sigmadsp : The sigmadsp instance to configure
* @ samplerate : The samplerate the DSP should be configured for
*
* Loads the appropriate firmware program and parameter memory ( if not already
* loaded ) and enables the controls for the specified samplerate . Any control
* parameter changes that have been made previously will be restored .
*
* Returns 0 on success , a negative error code otherwise .
*/
int sigmadsp_setup ( struct sigmadsp * sigmadsp , unsigned int samplerate )
{
2014-11-19 20:29:06 +03:00
struct sigmadsp_control * ctrl ;
unsigned int samplerate_mask ;
2014-11-19 20:29:05 +03:00
struct sigmadsp_data * data ;
int ret ;
if ( sigmadsp - > current_samplerate = = samplerate )
return 0 ;
2014-11-19 20:29:06 +03:00
samplerate_mask = sigmadsp_get_samplerate_mask ( sigmadsp , samplerate ) ;
if ( samplerate_mask = = 0 )
return - EINVAL ;
2014-11-19 20:29:05 +03:00
list_for_each_entry ( data , & sigmadsp - > data_list , head ) {
2014-11-19 20:29:06 +03:00
if ( ! sigmadsp_samplerate_valid ( data - > samplerates ,
samplerate_mask ) )
continue ;
2014-11-19 20:29:05 +03:00
ret = sigmadsp_write ( sigmadsp , data - > addr , data - > data ,
data - > length ) ;
if ( ret )
goto err ;
}
2014-11-19 20:29:06 +03:00
list_for_each_entry ( ctrl , & sigmadsp - > ctrl_list , head )
sigmadsp_activate_ctrl ( sigmadsp , ctrl , samplerate_mask ) ;
2014-11-19 20:29:05 +03:00
sigmadsp - > current_samplerate = samplerate ;
return 0 ;
err :
sigmadsp_reset ( sigmadsp ) ;
2011-03-23 02:34:40 +03:00
return ret ;
}
2014-11-19 20:29:05 +03:00
EXPORT_SYMBOL_GPL ( sigmadsp_setup ) ;
/**
* sigmadsp_reset ( ) - Notify the sigmadsp instance that the DSP has been reset
* @ sigmadsp : The sigmadsp instance to reset
*
* Should be called whenever the DSP has been reset and parameter and program
* memory need to be re - loaded .
*/
void sigmadsp_reset ( struct sigmadsp * sigmadsp )
{
2014-11-19 20:29:06 +03:00
struct sigmadsp_control * ctrl ;
list_for_each_entry ( ctrl , & sigmadsp - > ctrl_list , head )
sigmadsp_activate_ctrl ( sigmadsp , ctrl , false ) ;
2014-11-19 20:29:05 +03:00
sigmadsp - > current_samplerate = 0 ;
}
EXPORT_SYMBOL_GPL ( sigmadsp_reset ) ;
/**
* sigmadsp_restrict_params ( ) - Applies DSP firmware specific constraints
* @ sigmadsp : The sigmadsp instance
* @ substream : The substream to restrict
*
* Applies samplerate constraints that may be required by the firmware Should
* typically be called from the CODEC / component drivers startup callback .
*
* Returns 0 on success , a negative error code otherwise .
*/
int sigmadsp_restrict_params ( struct sigmadsp * sigmadsp ,
struct snd_pcm_substream * substream )
{
2014-11-19 20:29:06 +03:00
if ( sigmadsp - > rate_constraints . count = = 0 )
return 0 ;
return snd_pcm_hw_constraint_list ( substream - > runtime , 0 ,
SNDRV_PCM_HW_PARAM_RATE , & sigmadsp - > rate_constraints ) ;
2014-11-19 20:29:05 +03:00
}
EXPORT_SYMBOL_GPL ( sigmadsp_restrict_params ) ;
2011-11-28 12:44:20 +04:00
2011-07-26 04:13:21 +04:00
MODULE_LICENSE ( " GPL " ) ;