2009-07-05 17:24:50 +01: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 .
*/
2009-07-10 22:24:27 +01:00
# include <linux/i2c.h>
2009-07-10 23:28:16 +01:00
# include <linux/spi/spi.h>
2009-07-05 17:24:50 +01:00
# include <sound/soc.h>
2010-01-27 11:46:17 +08:00
static unsigned int snd_soc_4_12_read ( struct snd_soc_codec * codec ,
unsigned int reg )
{
u16 * cache = codec - > reg_cache ;
if ( reg > = codec - > reg_cache_size )
return - 1 ;
return cache [ reg ] ;
}
static int snd_soc_4_12_write ( struct snd_soc_codec * codec , unsigned int reg ,
unsigned int value )
{
u16 * cache = codec - > reg_cache ;
u8 data [ 2 ] ;
int ret ;
BUG_ON ( codec - > volatile_register ) ;
data [ 0 ] = ( reg < < 4 ) | ( ( value > > 8 ) & 0x000f ) ;
data [ 1 ] = value & 0x00ff ;
if ( reg < codec - > reg_cache_size )
cache [ reg ] = value ;
2010-02-01 18:46:10 +00:00
2010-02-01 18:48:03 +00:00
if ( codec - > cache_only ) {
codec - > cache_sync = 1 ;
2010-02-01 18:46:10 +00:00
return 0 ;
2010-02-01 18:48:03 +00:00
}
2010-02-01 18:46:10 +00:00
2010-05-03 16:25:52 +01:00
dev_dbg ( codec - > dev , " 0x%x = 0x%x \n " , reg , value ) ;
2010-01-27 11:46:17 +08:00
ret = codec - > hw_write ( codec - > control_data , data , 2 ) ;
if ( ret = = 2 )
return 0 ;
if ( ret < 0 )
return ret ;
else
return - EIO ;
}
# if defined(CONFIG_SPI_MASTER)
static int snd_soc_4_12_spi_write ( void * control_data , const char * data ,
int len )
{
struct spi_device * spi = control_data ;
struct spi_transfer t ;
struct spi_message m ;
u8 msg [ 2 ] ;
if ( len < = 0 )
return 0 ;
msg [ 0 ] = data [ 1 ] ;
msg [ 1 ] = data [ 0 ] ;
spi_message_init ( & m ) ;
memset ( & t , 0 , ( sizeof t ) ) ;
t . tx_buf = & msg [ 0 ] ;
t . len = len ;
spi_message_add_tail ( & t , & m ) ;
spi_sync ( spi , & m ) ;
return len ;
}
# else
# define snd_soc_4_12_spi_write NULL
# endif
2009-07-05 17:24:50 +01:00
static unsigned int snd_soc_7_9_read ( struct snd_soc_codec * codec ,
unsigned int reg )
{
u16 * cache = codec - > reg_cache ;
if ( reg > = codec - > reg_cache_size )
return - 1 ;
return cache [ reg ] ;
}
static int snd_soc_7_9_write ( struct snd_soc_codec * codec , unsigned int reg ,
unsigned int value )
{
u16 * cache = codec - > reg_cache ;
u8 data [ 2 ] ;
int ret ;
BUG_ON ( codec - > volatile_register ) ;
data [ 0 ] = ( reg < < 1 ) | ( ( value > > 8 ) & 0x0001 ) ;
data [ 1 ] = value & 0x00ff ;
if ( reg < codec - > reg_cache_size )
cache [ reg ] = value ;
2010-02-01 18:46:10 +00:00
2010-02-01 18:48:03 +00:00
if ( codec - > cache_only ) {
codec - > cache_sync = 1 ;
2010-02-01 18:46:10 +00:00
return 0 ;
2010-02-01 18:48:03 +00:00
}
2010-02-01 18:46:10 +00:00
2010-05-03 16:25:52 +01:00
dev_dbg ( codec - > dev , " 0x%x = 0x%x \n " , reg , value ) ;
2009-07-05 17:24:50 +01:00
ret = codec - > hw_write ( codec - > control_data , data , 2 ) ;
if ( ret = = 2 )
return 0 ;
if ( ret < 0 )
return ret ;
else
return - EIO ;
}
2009-07-10 23:28:16 +01:00
# if defined(CONFIG_SPI_MASTER)
static int snd_soc_7_9_spi_write ( void * control_data , const char * data ,
int len )
{
struct spi_device * spi = control_data ;
struct spi_transfer t ;
struct spi_message m ;
u8 msg [ 2 ] ;
if ( len < = 0 )
return 0 ;
msg [ 0 ] = data [ 0 ] ;
msg [ 1 ] = data [ 1 ] ;
spi_message_init ( & m ) ;
memset ( & t , 0 , ( sizeof t ) ) ;
t . tx_buf = & msg [ 0 ] ;
t . len = len ;
spi_message_add_tail ( & t , & m ) ;
spi_sync ( spi , & m ) ;
return len ;
}
# else
# define snd_soc_7_9_spi_write NULL
# endif
2009-09-07 12:04:37 +09:00
static int snd_soc_8_8_write ( struct snd_soc_codec * codec , unsigned int reg ,
unsigned int value )
{
u8 * cache = codec - > reg_cache ;
u8 data [ 2 ] ;
BUG_ON ( codec - > volatile_register ) ;
2010-03-18 16:17:01 +08:00
reg & = 0xff ;
data [ 0 ] = reg ;
2009-09-07 12:04:37 +09:00
data [ 1 ] = value & 0xff ;
if ( reg < codec - > reg_cache_size )
cache [ reg ] = value ;
2010-02-01 18:48:03 +00:00
if ( codec - > cache_only ) {
codec - > cache_sync = 1 ;
2010-02-01 18:46:10 +00:00
return 0 ;
2010-02-01 18:48:03 +00:00
}
2010-02-01 18:46:10 +00:00
2010-05-03 16:25:52 +01:00
dev_dbg ( codec - > dev , " 0x%x = 0x%x \n " , reg , value ) ;
2009-09-07 12:04:37 +09:00
if ( codec - > hw_write ( codec - > control_data , data , 2 ) = = 2 )
return 0 ;
else
return - EIO ;
}
static unsigned int snd_soc_8_8_read ( struct snd_soc_codec * codec ,
unsigned int reg )
{
u8 * cache = codec - > reg_cache ;
2010-03-18 16:17:01 +08:00
reg & = 0xff ;
2009-09-07 12:04:37 +09:00
if ( reg > = codec - > reg_cache_size )
return - 1 ;
return cache [ reg ] ;
}
2009-07-10 23:11:24 +01:00
static int snd_soc_8_16_write ( struct snd_soc_codec * codec , unsigned int reg ,
unsigned int value )
{
u16 * reg_cache = codec - > reg_cache ;
u8 data [ 3 ] ;
data [ 0 ] = reg ;
data [ 1 ] = ( value > > 8 ) & 0xff ;
data [ 2 ] = value & 0xff ;
2010-09-21 15:03:26 +01:00
if ( ! snd_soc_codec_volatile_register ( codec , reg )
2010-09-22 18:47:40 +01:00
& & reg < codec - > reg_cache_size )
2010-09-21 15:03:26 +01:00
reg_cache [ reg ] = value ;
2009-07-10 23:11:24 +01:00
2010-02-01 18:48:03 +00:00
if ( codec - > cache_only ) {
codec - > cache_sync = 1 ;
2010-02-01 18:46:10 +00:00
return 0 ;
2010-02-01 18:48:03 +00:00
}
2010-02-01 18:46:10 +00:00
2010-05-03 16:25:52 +01:00
dev_dbg ( codec - > dev , " 0x%x = 0x%x \n " , reg , value ) ;
2009-07-10 23:11:24 +01:00
if ( codec - > hw_write ( codec - > control_data , data , 3 ) = = 3 )
return 0 ;
else
return - EIO ;
}
static unsigned int snd_soc_8_16_read ( struct snd_soc_codec * codec ,
unsigned int reg )
{
u16 * cache = codec - > reg_cache ;
if ( reg > = codec - > reg_cache_size | |
2010-02-01 18:46:10 +00:00
snd_soc_codec_volatile_register ( codec , reg ) ) {
if ( codec - > cache_only )
return - EINVAL ;
2009-07-10 23:11:24 +01:00
return codec - > hw_read ( codec , reg ) ;
2010-02-01 18:46:10 +00:00
} else {
2009-07-10 23:11:24 +01:00
return cache [ reg ] ;
2010-02-01 18:46:10 +00:00
}
2009-07-10 23:11:24 +01:00
}
2010-03-18 16:17:00 +08:00
# if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int snd_soc_8_8_read_i2c ( struct snd_soc_codec * codec ,
unsigned int r )
{
struct i2c_msg xfer [ 2 ] ;
u8 reg = r ;
u8 data ;
int ret ;
struct i2c_client * client = codec - > control_data ;
/* Write register */
xfer [ 0 ] . addr = client - > addr ;
xfer [ 0 ] . flags = 0 ;
xfer [ 0 ] . len = 1 ;
xfer [ 0 ] . buf = & reg ;
/* Read data */
xfer [ 1 ] . addr = client - > addr ;
xfer [ 1 ] . flags = I2C_M_RD ;
xfer [ 1 ] . len = 1 ;
xfer [ 1 ] . buf = & data ;
ret = i2c_transfer ( client - > adapter , xfer , 2 ) ;
if ( ret ! = 2 ) {
dev_err ( & client - > dev , " i2c_transfer() returned %d \n " , ret ) ;
return 0 ;
}
return data ;
}
# else
# define snd_soc_8_8_read_i2c NULL
# endif
2009-08-10 16:04:39 -07:00
# if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
2009-07-10 23:11:24 +01:00
static unsigned int snd_soc_8_16_read_i2c ( struct snd_soc_codec * codec ,
unsigned int r )
{
struct i2c_msg xfer [ 2 ] ;
u8 reg = r ;
u16 data ;
int ret ;
struct i2c_client * client = codec - > control_data ;
/* Write register */
xfer [ 0 ] . addr = client - > addr ;
xfer [ 0 ] . flags = 0 ;
xfer [ 0 ] . len = 1 ;
xfer [ 0 ] . buf = & reg ;
/* Read data */
xfer [ 1 ] . addr = client - > addr ;
xfer [ 1 ] . flags = I2C_M_RD ;
xfer [ 1 ] . len = 2 ;
xfer [ 1 ] . buf = ( u8 * ) & data ;
ret = i2c_transfer ( client - > adapter , xfer , 2 ) ;
if ( ret ! = 2 ) {
dev_err ( & client - > dev , " i2c_transfer() returned %d \n " , ret ) ;
return 0 ;
}
return ( data > > 8 ) | ( ( data & 0xff ) < < 8 ) ;
}
# else
# define snd_soc_8_16_read_i2c NULL
# endif
2009-07-05 17:24:50 +01:00
2010-01-27 11:46:18 +08:00
# if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int snd_soc_16_8_read_i2c ( struct snd_soc_codec * codec ,
unsigned int r )
{
struct i2c_msg xfer [ 2 ] ;
u16 reg = r ;
u8 data ;
int ret ;
struct i2c_client * client = codec - > control_data ;
/* Write register */
xfer [ 0 ] . addr = client - > addr ;
xfer [ 0 ] . flags = 0 ;
xfer [ 0 ] . len = 2 ;
xfer [ 0 ] . buf = ( u8 * ) & reg ;
/* Read data */
xfer [ 1 ] . addr = client - > addr ;
xfer [ 1 ] . flags = I2C_M_RD ;
xfer [ 1 ] . len = 1 ;
xfer [ 1 ] . buf = & data ;
ret = i2c_transfer ( client - > adapter , xfer , 2 ) ;
if ( ret ! = 2 ) {
dev_err ( & client - > dev , " i2c_transfer() returned %d \n " , ret ) ;
return 0 ;
}
return data ;
}
# else
# define snd_soc_16_8_read_i2c NULL
# endif
static unsigned int snd_soc_16_8_read ( struct snd_soc_codec * codec ,
unsigned int reg )
{
2010-08-07 11:16:27 -04:00
u8 * cache = codec - > reg_cache ;
2010-01-27 11:46:18 +08:00
reg & = 0xff ;
if ( reg > = codec - > reg_cache_size )
return - 1 ;
return cache [ reg ] ;
}
static int snd_soc_16_8_write ( struct snd_soc_codec * codec , unsigned int reg ,
unsigned int value )
{
2010-08-07 11:16:27 -04:00
u8 * cache = codec - > reg_cache ;
2010-01-27 11:46:18 +08:00
u8 data [ 3 ] ;
int ret ;
BUG_ON ( codec - > volatile_register ) ;
data [ 0 ] = ( reg > > 8 ) & 0xff ;
data [ 1 ] = reg & 0xff ;
data [ 2 ] = value ;
reg & = 0xff ;
if ( reg < codec - > reg_cache_size )
cache [ reg ] = value ;
2010-02-01 18:46:10 +00:00
2010-02-01 18:48:03 +00:00
if ( codec - > cache_only ) {
codec - > cache_sync = 1 ;
2010-02-01 18:46:10 +00:00
return 0 ;
2010-02-01 18:48:03 +00:00
}
2010-02-01 18:46:10 +00:00
2010-05-03 16:25:52 +01:00
dev_dbg ( codec - > dev , " 0x%x = 0x%x \n " , reg , value ) ;
2010-01-27 11:46:18 +08:00
ret = codec - > hw_write ( codec - > control_data , data , 3 ) ;
if ( ret = = 3 )
return 0 ;
if ( ret < 0 )
return ret ;
else
return - EIO ;
}
# if defined(CONFIG_SPI_MASTER)
static int snd_soc_16_8_spi_write ( void * control_data , const char * data ,
int len )
{
struct spi_device * spi = control_data ;
struct spi_transfer t ;
struct spi_message m ;
u8 msg [ 3 ] ;
if ( len < = 0 )
return 0 ;
msg [ 0 ] = data [ 0 ] ;
msg [ 1 ] = data [ 1 ] ;
msg [ 2 ] = data [ 2 ] ;
spi_message_init ( & m ) ;
memset ( & t , 0 , ( sizeof t ) ) ;
t . tx_buf = & msg [ 0 ] ;
t . len = len ;
spi_message_add_tail ( & t , & m ) ;
spi_sync ( spi , & m ) ;
return len ;
}
# else
# define snd_soc_16_8_spi_write NULL
# endif
2010-03-05 16:27:15 +00:00
# if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
static unsigned int snd_soc_16_16_read_i2c ( struct snd_soc_codec * codec ,
unsigned int r )
{
struct i2c_msg xfer [ 2 ] ;
u16 reg = cpu_to_be16 ( r ) ;
u16 data ;
int ret ;
struct i2c_client * client = codec - > control_data ;
/* Write register */
xfer [ 0 ] . addr = client - > addr ;
xfer [ 0 ] . flags = 0 ;
xfer [ 0 ] . len = 2 ;
xfer [ 0 ] . buf = ( u8 * ) & reg ;
/* Read data */
xfer [ 1 ] . addr = client - > addr ;
xfer [ 1 ] . flags = I2C_M_RD ;
xfer [ 1 ] . len = 2 ;
xfer [ 1 ] . buf = ( u8 * ) & data ;
ret = i2c_transfer ( client - > adapter , xfer , 2 ) ;
if ( ret ! = 2 ) {
dev_err ( & client - > dev , " i2c_transfer() returned %d \n " , ret ) ;
return 0 ;
}
return be16_to_cpu ( data ) ;
}
# else
# define snd_soc_16_16_read_i2c NULL
# endif
static unsigned int snd_soc_16_16_read ( struct snd_soc_codec * codec ,
unsigned int reg )
{
u16 * cache = codec - > reg_cache ;
if ( reg > = codec - > reg_cache_size | |
snd_soc_codec_volatile_register ( codec , reg ) ) {
if ( codec - > cache_only )
return - EINVAL ;
return codec - > hw_read ( codec , reg ) ;
}
return cache [ reg ] ;
}
static int snd_soc_16_16_write ( struct snd_soc_codec * codec , unsigned int reg ,
unsigned int value )
{
u16 * cache = codec - > reg_cache ;
u8 data [ 4 ] ;
int ret ;
data [ 0 ] = ( reg > > 8 ) & 0xff ;
data [ 1 ] = reg & 0xff ;
data [ 2 ] = ( value > > 8 ) & 0xff ;
data [ 3 ] = value & 0xff ;
if ( reg < codec - > reg_cache_size )
cache [ reg ] = value ;
if ( codec - > cache_only ) {
codec - > cache_sync = 1 ;
return 0 ;
}
2010-05-03 16:25:52 +01:00
dev_dbg ( codec - > dev , " 0x%x = 0x%x \n " , reg , value ) ;
2010-03-05 16:27:15 +00:00
ret = codec - > hw_write ( codec - > control_data , data , 4 ) ;
if ( ret = = 4 )
return 0 ;
if ( ret < 0 )
return ret ;
else
return - EIO ;
}
2010-01-27 11:46:18 +08:00
2009-07-05 17:24:50 +01:00
static struct {
int addr_bits ;
int data_bits ;
2009-07-10 23:11:24 +01:00
int ( * write ) ( struct snd_soc_codec * codec , unsigned int , unsigned int ) ;
2009-07-10 23:28:16 +01:00
int ( * spi_write ) ( void * , const char * , int ) ;
2009-07-05 17:24:50 +01:00
unsigned int ( * read ) ( struct snd_soc_codec * , unsigned int ) ;
2009-07-10 23:11:24 +01:00
unsigned int ( * i2c_read ) ( struct snd_soc_codec * , unsigned int ) ;
2009-07-05 17:24:50 +01:00
} io_types [ ] = {
2010-01-27 11:46:17 +08:00
{
. addr_bits = 4 , . data_bits = 12 ,
. write = snd_soc_4_12_write , . read = snd_soc_4_12_read ,
. spi_write = snd_soc_4_12_spi_write ,
} ,
2009-09-21 04:21:47 -07:00
{
. addr_bits = 7 , . data_bits = 9 ,
. write = snd_soc_7_9_write , . read = snd_soc_7_9_read ,
2009-12-31 10:30:34 +08:00
. spi_write = snd_soc_7_9_spi_write ,
2009-09-21 04:21:47 -07:00
} ,
{
. addr_bits = 8 , . data_bits = 8 ,
. write = snd_soc_8_8_write , . read = snd_soc_8_8_read ,
2010-03-18 16:17:00 +08:00
. i2c_read = snd_soc_8_8_read_i2c ,
2009-09-21 04:21:47 -07:00
} ,
{
. addr_bits = 8 , . data_bits = 16 ,
. write = snd_soc_8_16_write , . read = snd_soc_8_16_read ,
. i2c_read = snd_soc_8_16_read_i2c ,
} ,
2010-01-27 11:46:18 +08:00
{
. addr_bits = 16 , . data_bits = 8 ,
. write = snd_soc_16_8_write , . read = snd_soc_16_8_read ,
. i2c_read = snd_soc_16_8_read_i2c ,
. spi_write = snd_soc_16_8_spi_write ,
} ,
2010-03-05 16:27:15 +00:00
{
. addr_bits = 16 , . data_bits = 16 ,
. write = snd_soc_16_16_write , . read = snd_soc_16_16_read ,
. i2c_read = snd_soc_16_16_read_i2c ,
} ,
2009-07-05 17:24:50 +01:00
} ;
/**
* snd_soc_codec_set_cache_io : Set up standard I / O functions .
*
* @ codec : CODEC to configure .
* @ type : Type of cache .
* @ addr_bits : Number of bits of register address data .
* @ data_bits : Number of bits of data per register .
2009-07-10 22:24:27 +01:00
* @ control : Control bus used .
2009-07-05 17:24:50 +01:00
*
* Register formats are frequently shared between many I2C and SPI
* devices . In order to promote code reuse the ASoC core provides
* some standard implementations of CODEC read and write operations
* which can be set up using this function .
*
* The caller is responsible for allocating and initialising the
* actual cache .
*
* Note that at present this code cannot be used by CODECs with
* volatile registers .
*/
int snd_soc_codec_set_cache_io ( struct snd_soc_codec * codec ,
2009-07-10 22:24:27 +01:00
int addr_bits , int data_bits ,
enum snd_soc_control_type control )
2009-07-05 17:24:50 +01:00
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( io_types ) ; i + + )
if ( io_types [ i ] . addr_bits = = addr_bits & &
io_types [ i ] . data_bits = = data_bits )
break ;
if ( i = = ARRAY_SIZE ( io_types ) ) {
printk ( KERN_ERR
" No I/O functions for %d bit address %d bit data \n " ,
addr_bits , data_bits ) ;
return - EINVAL ;
}
codec - > write = io_types [ i ] . write ;
codec - > read = io_types [ i ] . read ;
2009-07-10 22:24:27 +01:00
switch ( control ) {
case SND_SOC_CUSTOM :
break ;
case SND_SOC_I2C :
2009-08-10 16:04:39 -07:00
# if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
2009-07-10 22:24:27 +01:00
codec - > hw_write = ( hw_write_t ) i2c_master_send ;
# endif
2009-07-10 23:11:24 +01:00
if ( io_types [ i ] . i2c_read )
codec - > hw_read = io_types [ i ] . i2c_read ;
2009-07-10 22:24:27 +01:00
break ;
case SND_SOC_SPI :
2009-07-10 23:28:16 +01:00
if ( io_types [ i ] . spi_write )
codec - > hw_write = io_types [ i ] . spi_write ;
2009-07-10 22:24:27 +01:00
break ;
}
2009-07-05 17:24:50 +01:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( snd_soc_codec_set_cache_io ) ;