2009-07-05 20:24:50 +04:00
/*
* soc - cache . c - - ASoC register cache helpers
*
* Copyright 2009 Wolfson Microelectronics PLC .
*
* Author : Mark Brown < broonie @ opensource . wolfsonmicro . 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 ; either version 2 of the License , or ( at your
* option ) any later version .
*/
# include <sound/soc.h>
2011-09-22 17:34:58 +04:00
# include <linux/export.h>
2013-08-31 22:31:15 +04:00
# include <linux/slab.h>
2009-07-05 20:24:50 +04:00
2011-01-21 18:29:02 +03:00
# include <trace/events/asoc.h>
2011-01-11 14:29:49 +03:00
static bool snd_soc_set_cache_val ( void * base , unsigned int idx ,
unsigned int val , unsigned int word_size )
{
switch ( word_size ) {
case 1 : {
u8 * cache = base ;
if ( cache [ idx ] = = val )
return true ;
cache [ idx ] = val ;
break ;
}
case 2 : {
u16 * cache = base ;
if ( cache [ idx ] = = val )
return true ;
cache [ idx ] = val ;
break ;
}
default :
2013-11-06 14:07:19 +04:00
WARN ( 1 , " Invalid word_size %d \n " , word_size ) ;
break ;
2011-01-11 14:29:49 +03:00
}
return false ;
}
static unsigned int snd_soc_get_cache_val ( const void * base , unsigned int idx ,
unsigned int word_size )
{
2011-06-06 14:26:15 +04:00
if ( ! base )
return - 1 ;
2011-01-11 14:29:49 +03:00
switch ( word_size ) {
case 1 : {
const u8 * cache = base ;
return cache [ idx ] ;
}
case 2 : {
const u16 * cache = base ;
return cache [ idx ] ;
}
default :
2013-11-06 14:07:19 +04:00
WARN ( 1 , " Invalid word_size %d \n " , word_size ) ;
break ;
2011-01-11 14:29:49 +03:00
}
/* unreachable */
return - 1 ;
}
2013-08-31 22:31:15 +04:00
int snd_soc_cache_init ( struct snd_soc_codec * codec )
2010-11-11 13:04:57 +03:00
{
2013-08-31 22:31:13 +04:00
const struct snd_soc_codec_driver * codec_drv = codec - > driver ;
2013-08-31 22:31:14 +04:00
size_t reg_size ;
reg_size = codec_drv - > reg_cache_size * codec_drv - > reg_word_size ;
2013-08-31 22:31:13 +04:00
2014-05-19 12:32:09 +04:00
if ( ! reg_size )
2014-06-02 19:08:21 +04:00
return 0 ;
2014-05-19 12:32:09 +04:00
2013-08-31 22:31:15 +04:00
mutex_init ( & codec - > cache_rw_mutex ) ;
dev_dbg ( codec - > dev , " ASoC: Initializing cache for %s codec \n " ,
2014-06-16 20:13:02 +04:00
codec - > component . name ) ;
2013-08-31 22:31:15 +04:00
2013-08-31 22:31:13 +04:00
if ( codec_drv - > reg_cache_default )
codec - > reg_cache = kmemdup ( codec_drv - > reg_cache_default ,
2013-08-31 22:31:14 +04:00
reg_size , GFP_KERNEL ) ;
2010-11-11 13:04:57 +03:00
else
2013-08-31 22:31:14 +04:00
codec - > reg_cache = kzalloc ( reg_size , GFP_KERNEL ) ;
2010-11-11 13:04:57 +03:00
if ( ! codec - > reg_cache )
return - ENOMEM ;
return 0 ;
}
/*
* NOTE : keep in mind that this function might be called
* multiple times .
*/
int snd_soc_cache_exit ( struct snd_soc_codec * codec )
{
2013-08-31 22:31:15 +04:00
dev_dbg ( codec - > dev , " ASoC: Destroying cache for %s codec \n " ,
2014-06-16 20:13:02 +04:00
codec - > component . name ) ;
2013-08-31 22:31:15 +04:00
kfree ( codec - > reg_cache ) ;
codec - > reg_cache = NULL ;
return 0 ;
2010-11-11 13:04:57 +03:00
}
/**
* snd_soc_cache_read : Fetch the value of a given register from the cache .
*
* @ codec : CODEC to configure .
* @ reg : The register index .
* @ value : The value to be returned .
*/
int snd_soc_cache_read ( struct snd_soc_codec * codec ,
unsigned int reg , unsigned int * value )
{
2013-08-31 22:31:15 +04:00
if ( ! value )
return - EINVAL ;
2010-11-11 13:04:57 +03:00
mutex_lock ( & codec - > cache_rw_mutex ) ;
2014-02-28 06:48:19 +04:00
if ( ! ZERO_OR_NULL_PTR ( codec - > reg_cache ) )
* value = snd_soc_get_cache_val ( codec - > reg_cache , reg ,
codec - > driver - > reg_word_size ) ;
2010-11-11 13:04:57 +03:00
mutex_unlock ( & codec - > cache_rw_mutex ) ;
2013-08-31 22:31:15 +04:00
return 0 ;
2010-11-11 13:04:57 +03:00
}
EXPORT_SYMBOL_GPL ( snd_soc_cache_read ) ;
/**
* snd_soc_cache_write : Set the value of a given register in the cache .
*
* @ codec : CODEC to configure .
* @ reg : The register index .
* @ value : The new register value .
*/
int snd_soc_cache_write ( struct snd_soc_codec * codec ,
unsigned int reg , unsigned int value )
{
2013-08-31 22:31:15 +04:00
mutex_lock ( & codec - > cache_rw_mutex ) ;
2014-02-28 06:48:19 +04:00
if ( ! ZERO_OR_NULL_PTR ( codec - > reg_cache ) )
snd_soc_set_cache_val ( codec - > reg_cache , reg , value ,
codec - > driver - > reg_word_size ) ;
2013-08-31 22:31:15 +04:00
mutex_unlock ( & codec - > cache_rw_mutex ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( snd_soc_cache_write ) ;
static int snd_soc_flat_cache_sync ( struct snd_soc_codec * codec )
{
int i ;
2010-11-11 13:04:57 +03:00
int ret ;
2013-08-31 22:31:15 +04:00
const struct snd_soc_codec_driver * codec_drv ;
unsigned int val ;
2010-11-11 13:04:57 +03:00
2013-08-31 22:31:15 +04:00
codec_drv = codec - > driver ;
for ( i = 0 ; i < codec_drv - > reg_cache_size ; + + i ) {
ret = snd_soc_cache_read ( codec , i , & val ) ;
if ( ret )
return ret ;
if ( codec_drv - > reg_cache_default )
if ( snd_soc_get_cache_val ( codec_drv - > reg_cache_default ,
i , codec_drv - > reg_word_size ) = = val )
continue ;
2010-11-11 13:04:57 +03:00
2013-08-31 22:31:15 +04:00
ret = snd_soc_write ( codec , i , val ) ;
if ( ret )
return ret ;
dev_dbg ( codec - > dev , " ASoC: Synced register %#x, value = %#x \n " ,
i , val ) ;
}
return 0 ;
2010-11-11 13:04:57 +03:00
}
/**
* snd_soc_cache_sync : Sync the register cache with the hardware .
*
* @ codec : CODEC to configure .
*
* Any registers that should not be synced should be marked as
* volatile . In general drivers can choose not to use the provided
* syncing functionality if they so require .
*/
int snd_soc_cache_sync ( struct snd_soc_codec * codec )
{
2013-08-31 22:31:15 +04:00
const char * name = " flat " ;
2010-11-11 13:04:57 +03:00
int ret ;
2013-08-31 22:31:15 +04:00
if ( ! codec - > cache_sync )
2010-11-11 13:04:57 +03:00
return 0 ;
2011-01-21 18:29:02 +03:00
2013-08-31 22:31:15 +04:00
dev_dbg ( codec - > dev , " ASoC: Syncing cache for %s codec \n " ,
2014-06-16 20:13:02 +04:00
codec - > component . name ) ;
2011-02-07 22:01:41 +03:00
trace_snd_soc_cache_sync ( codec , name , " start " ) ;
2013-08-31 22:31:15 +04:00
ret = snd_soc_flat_cache_sync ( codec ) ;
2011-02-07 22:01:41 +03:00
if ( ! ret )
codec - > cache_sync = 0 ;
trace_snd_soc_cache_sync ( codec , name , " end " ) ;
return ret ;
2010-11-11 13:04:57 +03:00
}
EXPORT_SYMBOL_GPL ( snd_soc_cache_sync ) ;