2006-10-06 20:36:07 +04:00
/*
* wm8731 . c - - WM8731 ALSA SoC Audio driver
*
* Copyright 2005 Openedhand Ltd .
*
* Author : Richard Purdie < richard @ openedhand . com >
*
* Based on wm8753 . c by Liam Girdwood
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/pm.h>
# include <linux/i2c.h>
# include <linux/platform_device.h>
2008-09-01 21:47:03 +04:00
# include <linux/spi/spi.h>
2006-10-06 20:36:07 +04:00
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
# include <sound/soc-dapm.h>
# include <sound/initval.h>
# include "wm8731.h"
struct snd_soc_codec_device soc_codec_dev_wm8731 ;
2007-02-02 19:14:56 +03:00
/* codec private data */
struct wm8731_priv {
unsigned int sysclk ;
} ;
2006-10-06 20:36:07 +04:00
/*
* wm8731 register cache
* We can ' t read the WM8731 register space when we are
* using 2 wire for device control , so we cache them instead .
* There is no point in caching the reset register
*/
static const u16 wm8731_reg [ WM8731_CACHEREGNUM ] = {
0x0097 , 0x0097 , 0x0079 , 0x0079 ,
0x000a , 0x0008 , 0x009f , 0x000a ,
0x0000 , 0x0000
} ;
/*
* read wm8731 register cache
*/
static inline unsigned int wm8731_read_reg_cache ( struct snd_soc_codec * codec ,
unsigned int reg )
{
u16 * cache = codec - > reg_cache ;
if ( reg = = WM8731_RESET )
return 0 ;
if ( reg > = WM8731_CACHEREGNUM )
return - 1 ;
return cache [ reg ] ;
}
/*
* write wm8731 register cache
*/
static inline void wm8731_write_reg_cache ( struct snd_soc_codec * codec ,
u16 reg , unsigned int value )
{
u16 * cache = codec - > reg_cache ;
if ( reg > = WM8731_CACHEREGNUM )
return ;
cache [ reg ] = value ;
}
/*
* write to the WM8731 register space
*/
static int wm8731_write ( struct snd_soc_codec * codec , unsigned int reg ,
unsigned int value )
{
u8 data [ 2 ] ;
/* data is
* D15 . . D9 WM8731 register offset
* D8 . . . D0 register data
*/
data [ 0 ] = ( reg < < 1 ) | ( ( value > > 8 ) & 0x0001 ) ;
data [ 1 ] = value & 0x00ff ;
2008-04-23 17:16:46 +04:00
wm8731_write_reg_cache ( codec , reg , value ) ;
2006-10-06 20:36:07 +04:00
if ( codec - > hw_write ( codec - > control_data , data , 2 ) = = 2 )
return 0 ;
else
return - EIO ;
}
# define wm8731_reset(c) wm8731_write(c, WM8731_RESET, 0)
static const char * wm8731_input_select [ ] = { " Line In " , " Mic " } ;
static const char * wm8731_deemph [ ] = { " None " , " 32Khz " , " 44.1Khz " , " 48Khz " } ;
static const struct soc_enum wm8731_enum [ ] = {
SOC_ENUM_SINGLE ( WM8731_APANA , 2 , 2 , wm8731_input_select ) ,
SOC_ENUM_SINGLE ( WM8731_APDIGI , 1 , 4 , wm8731_deemph ) ,
} ;
static const struct snd_kcontrol_new wm8731_snd_controls [ ] = {
2006-11-09 18:35:01 +03:00
SOC_DOUBLE_R ( " Master Playback Volume " , WM8731_LOUT1V , WM8731_ROUT1V ,
0 , 127 , 0 ) ,
SOC_DOUBLE_R ( " Master Playback ZC Switch " , WM8731_LOUT1V , WM8731_ROUT1V ,
7 , 1 , 0 ) ,
2006-10-06 20:36:07 +04:00
SOC_DOUBLE_R ( " Capture Volume " , WM8731_LINVOL , WM8731_RINVOL , 0 , 31 , 0 ) ,
SOC_DOUBLE_R ( " Line Capture Switch " , WM8731_LINVOL , WM8731_RINVOL , 7 , 1 , 1 ) ,
SOC_SINGLE ( " Mic Boost (+20dB) " , WM8731_APANA , 0 , 1 , 0 ) ,
SOC_SINGLE ( " Capture Mic Switch " , WM8731_APANA , 1 , 1 , 1 ) ,
SOC_SINGLE ( " Sidetone Playback Volume " , WM8731_APANA , 6 , 3 , 1 ) ,
SOC_SINGLE ( " ADC High Pass Filter Switch " , WM8731_APDIGI , 0 , 1 , 1 ) ,
SOC_SINGLE ( " Store DC Offset Switch " , WM8731_APDIGI , 4 , 1 , 0 ) ,
SOC_ENUM ( " Playback De-emphasis " , wm8731_enum [ 1 ] ) ,
} ;
/* Output Mixer */
static const struct snd_kcontrol_new wm8731_output_mixer_controls [ ] = {
SOC_DAPM_SINGLE ( " Line Bypass Switch " , WM8731_APANA , 3 , 1 , 0 ) ,
SOC_DAPM_SINGLE ( " Mic Sidetone Switch " , WM8731_APANA , 5 , 1 , 0 ) ,
SOC_DAPM_SINGLE ( " HiFi Playback Switch " , WM8731_APANA , 4 , 1 , 0 ) ,
} ;
/* Input mux */
static const struct snd_kcontrol_new wm8731_input_mux_controls =
SOC_DAPM_ENUM ( " Input Select " , wm8731_enum [ 0 ] ) ;
static const struct snd_soc_dapm_widget wm8731_dapm_widgets [ ] = {
SND_SOC_DAPM_MIXER ( " Output Mixer " , WM8731_PWR , 4 , 1 ,
& wm8731_output_mixer_controls [ 0 ] ,
ARRAY_SIZE ( wm8731_output_mixer_controls ) ) ,
SND_SOC_DAPM_DAC ( " DAC " , " HiFi Playback " , WM8731_PWR , 3 , 1 ) ,
SND_SOC_DAPM_OUTPUT ( " LOUT " ) ,
SND_SOC_DAPM_OUTPUT ( " LHPOUT " ) ,
SND_SOC_DAPM_OUTPUT ( " ROUT " ) ,
SND_SOC_DAPM_OUTPUT ( " RHPOUT " ) ,
SND_SOC_DAPM_ADC ( " ADC " , " HiFi Capture " , WM8731_PWR , 2 , 1 ) ,
SND_SOC_DAPM_MUX ( " Input Mux " , SND_SOC_NOPM , 0 , 0 , & wm8731_input_mux_controls ) ,
SND_SOC_DAPM_PGA ( " Line Input " , WM8731_PWR , 0 , 1 , NULL , 0 ) ,
SND_SOC_DAPM_MICBIAS ( " Mic Bias " , WM8731_PWR , 1 , 1 ) ,
SND_SOC_DAPM_INPUT ( " MICIN " ) ,
SND_SOC_DAPM_INPUT ( " RLINEIN " ) ,
SND_SOC_DAPM_INPUT ( " LLINEIN " ) ,
} ;
2008-05-13 16:54:43 +04:00
static const struct snd_soc_dapm_route intercon [ ] = {
2006-10-06 20:36:07 +04:00
/* output mixer */
{ " Output Mixer " , " Line Bypass Switch " , " Line Input " } ,
{ " Output Mixer " , " HiFi Playback Switch " , " DAC " } ,
{ " Output Mixer " , " Mic Sidetone Switch " , " Mic Bias " } ,
/* outputs */
{ " RHPOUT " , NULL , " Output Mixer " } ,
{ " ROUT " , NULL , " Output Mixer " } ,
{ " LHPOUT " , NULL , " Output Mixer " } ,
{ " LOUT " , NULL , " Output Mixer " } ,
/* input mux */
{ " Input Mux " , " Line In " , " Line Input " } ,
{ " Input Mux " , " Mic " , " Mic Bias " } ,
{ " ADC " , NULL , " Input Mux " } ,
/* inputs */
{ " Line Input " , NULL , " LLINEIN " } ,
{ " Line Input " , NULL , " RLINEIN " } ,
{ " Mic Bias " , NULL , " MICIN " } ,
} ;
static int wm8731_add_widgets ( struct snd_soc_codec * codec )
{
2008-05-13 16:54:43 +04:00
snd_soc_dapm_new_controls ( codec , wm8731_dapm_widgets ,
ARRAY_SIZE ( wm8731_dapm_widgets ) ) ;
2006-10-06 20:36:07 +04:00
2008-05-13 16:54:43 +04:00
snd_soc_dapm_add_routes ( codec , intercon , ARRAY_SIZE ( intercon ) ) ;
2006-10-06 20:36:07 +04:00
snd_soc_dapm_new_widgets ( codec ) ;
return 0 ;
}
struct _coeff_div {
u32 mclk ;
u32 rate ;
u16 fs ;
u8 sr : 4 ;
u8 bosr : 1 ;
u8 usb : 1 ;
} ;
/* codec mclk clock divider coefficients */
static const struct _coeff_div coeff_div [ ] = {
/* 48k */
{ 12288000 , 48000 , 256 , 0x0 , 0x0 , 0x0 } ,
{ 18432000 , 48000 , 384 , 0x0 , 0x1 , 0x0 } ,
{ 12000000 , 48000 , 250 , 0x0 , 0x0 , 0x1 } ,
/* 32k */
{ 12288000 , 32000 , 384 , 0x6 , 0x0 , 0x0 } ,
{ 18432000 , 32000 , 576 , 0x6 , 0x1 , 0x0 } ,
2007-01-31 12:02:56 +03:00
{ 12000000 , 32000 , 375 , 0x6 , 0x0 , 0x1 } ,
2006-10-06 20:36:07 +04:00
/* 8k */
{ 12288000 , 8000 , 1536 , 0x3 , 0x0 , 0x0 } ,
{ 18432000 , 8000 , 2304 , 0x3 , 0x1 , 0x0 } ,
{ 11289600 , 8000 , 1408 , 0xb , 0x0 , 0x0 } ,
{ 16934400 , 8000 , 2112 , 0xb , 0x1 , 0x0 } ,
{ 12000000 , 8000 , 1500 , 0x3 , 0x0 , 0x1 } ,
/* 96k */
{ 12288000 , 96000 , 128 , 0x7 , 0x0 , 0x0 } ,
{ 18432000 , 96000 , 192 , 0x7 , 0x1 , 0x0 } ,
{ 12000000 , 96000 , 125 , 0x7 , 0x0 , 0x1 } ,
/* 44.1k */
{ 11289600 , 44100 , 256 , 0x8 , 0x0 , 0x0 } ,
{ 16934400 , 44100 , 384 , 0x8 , 0x1 , 0x0 } ,
{ 12000000 , 44100 , 272 , 0x8 , 0x1 , 0x1 } ,
/* 88.2k */
{ 11289600 , 88200 , 128 , 0xf , 0x0 , 0x0 } ,
{ 16934400 , 88200 , 192 , 0xf , 0x1 , 0x0 } ,
{ 12000000 , 88200 , 136 , 0xf , 0x1 , 0x1 } ,
} ;
static inline int get_coeff ( int mclk , int rate )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( coeff_div ) ; i + + ) {
if ( coeff_div [ i ] . rate = = rate & & coeff_div [ i ] . mclk = = mclk )
return i ;
}
return 0 ;
}
2007-02-02 19:14:56 +03:00
static int wm8731_hw_params ( struct snd_pcm_substream * substream ,
2008-11-19 01:11:38 +03:00
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
2006-10-06 20:36:07 +04:00
{
2007-02-02 19:14:56 +03:00
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
struct snd_soc_device * socdev = rtd - > socdev ;
2009-01-24 01:55:23 +03:00
struct snd_soc_codec * codec = socdev - > card - > codec ;
2007-02-02 19:14:56 +03:00
struct wm8731_priv * wm8731 = codec - > private_data ;
u16 iface = wm8731_read_reg_cache ( codec , WM8731_IFACE ) & 0xfff3 ;
int i = get_coeff ( wm8731 - > sysclk , params_rate ( params ) ) ;
u16 srate = ( coeff_div [ i ] . sr < < 2 ) |
( coeff_div [ i ] . bosr < < 1 ) | coeff_div [ i ] . usb ;
2006-10-06 20:36:07 +04:00
2007-02-02 19:14:56 +03:00
wm8731_write ( codec , WM8731_SRATE , srate ) ;
/* bit size */
switch ( params_format ( params ) ) {
case SNDRV_PCM_FORMAT_S16_LE :
break ;
case SNDRV_PCM_FORMAT_S20_3LE :
iface | = 0x0004 ;
break ;
case SNDRV_PCM_FORMAT_S24_LE :
iface | = 0x0008 ;
break ;
}
2006-10-06 20:36:07 +04:00
2007-02-02 19:14:56 +03:00
wm8731_write ( codec , WM8731_IFACE , iface ) ;
return 0 ;
2006-10-06 20:36:07 +04:00
}
2008-11-19 01:11:38 +03:00
static int wm8731_pcm_prepare ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
2006-10-06 20:36:07 +04:00
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
struct snd_soc_device * socdev = rtd - > socdev ;
2009-01-24 01:55:23 +03:00
struct snd_soc_codec * codec = socdev - > card - > codec ;
2007-02-02 19:14:56 +03:00
/* set active */
wm8731_write ( codec , WM8731_ACTIVE , 0x0001 ) ;
return 0 ;
}
2008-11-19 01:11:38 +03:00
static void wm8731_shutdown ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
2007-02-02 19:14:56 +03:00
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
struct snd_soc_device * socdev = rtd - > socdev ;
2009-01-24 01:55:23 +03:00
struct snd_soc_codec * codec = socdev - > card - > codec ;
2007-02-02 19:14:56 +03:00
/* deactivate */
if ( ! codec - > active ) {
udelay ( 50 ) ;
wm8731_write ( codec , WM8731_ACTIVE , 0x0 ) ;
}
}
2008-07-07 19:07:52 +04:00
static int wm8731_mute ( struct snd_soc_dai * dai , int mute )
2007-02-02 19:14:56 +03:00
{
struct snd_soc_codec * codec = dai - > codec ;
u16 mute_reg = wm8731_read_reg_cache ( codec , WM8731_APDIGI ) & 0xfff7 ;
if ( mute )
wm8731_write ( codec , WM8731_APDIGI , mute_reg | 0x8 ) ;
else
wm8731_write ( codec , WM8731_APDIGI , mute_reg ) ;
return 0 ;
}
2008-07-07 19:07:52 +04:00
static int wm8731_set_dai_sysclk ( struct snd_soc_dai * codec_dai ,
2007-02-02 19:14:56 +03:00
int clk_id , unsigned int freq , int dir )
{
struct snd_soc_codec * codec = codec_dai - > codec ;
struct wm8731_priv * wm8731 = codec - > private_data ;
switch ( freq ) {
case 11289600 :
case 12000000 :
case 12288000 :
case 16934400 :
case 18432000 :
wm8731 - > sysclk = freq ;
return 0 ;
}
return - EINVAL ;
}
2008-07-07 19:07:52 +04:00
static int wm8731_set_dai_fmt ( struct snd_soc_dai * codec_dai ,
2007-02-02 19:14:56 +03:00
unsigned int fmt )
{
struct snd_soc_codec * codec = codec_dai - > codec ;
u16 iface = 0 ;
2006-10-06 20:36:07 +04:00
/* set master/slave audio interface */
2007-02-02 19:14:56 +03:00
switch ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) {
2006-10-06 20:36:07 +04:00
case SND_SOC_DAIFMT_CBM_CFM :
iface | = 0x0040 ;
break ;
case SND_SOC_DAIFMT_CBS_CFS :
break ;
2007-02-02 19:14:56 +03:00
default :
return - EINVAL ;
2006-10-06 20:36:07 +04:00
}
/* interface format */
2007-02-02 19:14:56 +03:00
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
2006-10-06 20:36:07 +04:00
case SND_SOC_DAIFMT_I2S :
iface | = 0x0002 ;
break ;
case SND_SOC_DAIFMT_RIGHT_J :
break ;
case SND_SOC_DAIFMT_LEFT_J :
iface | = 0x0001 ;
break ;
case SND_SOC_DAIFMT_DSP_A :
iface | = 0x0003 ;
break ;
case SND_SOC_DAIFMT_DSP_B :
iface | = 0x0013 ;
break ;
2007-02-02 19:14:56 +03:00
default :
return - EINVAL ;
2006-10-06 20:36:07 +04:00
}
/* clock inversion */
2007-02-02 19:14:56 +03:00
switch ( fmt & SND_SOC_DAIFMT_INV_MASK ) {
2006-10-06 20:36:07 +04:00
case SND_SOC_DAIFMT_NB_NF :
break ;
case SND_SOC_DAIFMT_IB_IF :
iface | = 0x0090 ;
break ;
case SND_SOC_DAIFMT_IB_NF :
iface | = 0x0080 ;
break ;
case SND_SOC_DAIFMT_NB_IF :
iface | = 0x0010 ;
break ;
2007-02-02 19:14:56 +03:00
default :
return - EINVAL ;
2006-10-06 20:36:07 +04:00
}
/* set iface */
wm8731_write ( codec , WM8731_IFACE , iface ) ;
return 0 ;
}
2008-05-19 14:31:28 +04:00
static int wm8731_set_bias_level ( struct snd_soc_codec * codec ,
enum snd_soc_bias_level level )
2006-10-06 20:36:07 +04:00
{
2009-02-16 22:20:15 +03:00
u16 reg ;
2006-10-06 20:36:07 +04:00
2008-05-19 14:31:28 +04:00
switch ( level ) {
case SND_SOC_BIAS_ON :
2006-10-06 20:36:07 +04:00
break ;
2008-05-19 14:31:28 +04:00
case SND_SOC_BIAS_PREPARE :
2006-10-06 20:36:07 +04:00
break ;
2008-05-19 14:31:28 +04:00
case SND_SOC_BIAS_STANDBY :
2009-02-16 22:20:15 +03:00
/* Clear PWROFF, gate CLKOUT, everything else as-is */
reg = wm8731_read_reg_cache ( codec , WM8731_PWR ) & 0xff7f ;
2006-10-06 20:36:07 +04:00
wm8731_write ( codec , WM8731_PWR , reg | 0x0040 ) ;
break ;
2008-05-19 14:31:28 +04:00
case SND_SOC_BIAS_OFF :
2006-10-06 20:36:07 +04:00
wm8731_write ( codec , WM8731_ACTIVE , 0x0 ) ;
wm8731_write ( codec , WM8731_PWR , 0xffff ) ;
break ;
}
2008-05-19 14:31:28 +04:00
codec - > bias_level = level ;
2006-10-06 20:36:07 +04:00
return 0 ;
}
2007-02-02 19:14:56 +03:00
# define WM8731_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
SNDRV_PCM_RATE_96000 )
# define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE )
2008-07-07 19:07:52 +04:00
struct snd_soc_dai wm8731_dai = {
2006-10-06 20:36:07 +04:00
. name = " WM8731 " ,
. playback = {
. stream_name = " Playback " ,
. channels_min = 1 ,
. channels_max = 2 ,
2007-02-02 19:14:56 +03:00
. rates = WM8731_RATES ,
. formats = WM8731_FORMATS , } ,
2006-10-06 20:36:07 +04:00
. capture = {
. stream_name = " Capture " ,
. channels_min = 1 ,
. channels_max = 2 ,
2007-02-02 19:14:56 +03:00
. rates = WM8731_RATES ,
. formats = WM8731_FORMATS , } ,
2006-10-06 20:36:07 +04:00
. ops = {
. prepare = wm8731_pcm_prepare ,
2007-02-02 19:14:56 +03:00
. hw_params = wm8731_hw_params ,
2006-10-06 20:36:07 +04:00
. shutdown = wm8731_shutdown ,
2007-02-02 19:14:56 +03:00
. digital_mute = wm8731_mute ,
. set_sysclk = wm8731_set_dai_sysclk ,
. set_fmt = wm8731_set_dai_fmt ,
}
2006-10-06 20:36:07 +04:00
} ;
EXPORT_SYMBOL_GPL ( wm8731_dai ) ;
static int wm8731_suspend ( struct platform_device * pdev , pm_message_t state )
{
struct snd_soc_device * socdev = platform_get_drvdata ( pdev ) ;
2009-01-24 01:55:23 +03:00
struct snd_soc_codec * codec = socdev - > card - > codec ;
2006-10-06 20:36:07 +04:00
wm8731_write ( codec , WM8731_ACTIVE , 0x0 ) ;
2008-05-19 14:31:28 +04:00
wm8731_set_bias_level ( codec , SND_SOC_BIAS_OFF ) ;
2006-10-06 20:36:07 +04:00
return 0 ;
}
static int wm8731_resume ( struct platform_device * pdev )
{
struct snd_soc_device * socdev = platform_get_drvdata ( pdev ) ;
2009-01-24 01:55:23 +03:00
struct snd_soc_codec * codec = socdev - > card - > codec ;
2006-10-06 20:36:07 +04:00
int i ;
u8 data [ 2 ] ;
u16 * cache = codec - > reg_cache ;
/* Sync reg_cache with the hardware */
for ( i = 0 ; i < ARRAY_SIZE ( wm8731_reg ) ; i + + ) {
data [ 0 ] = ( i < < 1 ) | ( ( cache [ i ] > > 8 ) & 0x0001 ) ;
data [ 1 ] = cache [ i ] & 0x00ff ;
codec - > hw_write ( codec - > control_data , data , 2 ) ;
}
2008-05-19 14:31:28 +04:00
wm8731_set_bias_level ( codec , SND_SOC_BIAS_STANDBY ) ;
wm8731_set_bias_level ( codec , codec - > suspend_bias_level ) ;
2006-10-06 20:36:07 +04:00
return 0 ;
}
/*
* initialise the WM8731 driver
* register the mixer and dsp interfaces with the kernel
*/
static int wm8731_init ( struct snd_soc_device * socdev )
{
2009-01-24 01:55:23 +03:00
struct snd_soc_codec * codec = socdev - > card - > codec ;
2006-10-06 20:36:07 +04:00
int reg , ret = 0 ;
codec - > name = " WM8731 " ;
codec - > owner = THIS_MODULE ;
codec - > read = wm8731_read_reg_cache ;
codec - > write = wm8731_write ;
2008-05-19 14:31:28 +04:00
codec - > set_bias_level = wm8731_set_bias_level ;
2006-10-06 20:36:07 +04:00
codec - > dai = & wm8731_dai ;
codec - > num_dai = 1 ;
2008-06-11 16:47:06 +04:00
codec - > reg_cache_size = ARRAY_SIZE ( wm8731_reg ) ;
2007-02-05 16:56:20 +03:00
codec - > reg_cache = kmemdup ( wm8731_reg , sizeof ( wm8731_reg ) , GFP_KERNEL ) ;
2006-10-06 20:36:07 +04:00
if ( codec - > reg_cache = = NULL )
return - ENOMEM ;
wm8731_reset ( codec ) ;
/* register pcms */
ret = snd_soc_new_pcms ( socdev , SNDRV_DEFAULT_IDX1 , SNDRV_DEFAULT_STR1 ) ;
if ( ret < 0 ) {
2007-01-31 12:02:23 +03:00
printk ( KERN_ERR " wm8731: failed to create pcms \n " ) ;
goto pcm_err ;
2006-10-06 20:36:07 +04:00
}
/* power on device */
2008-05-19 14:31:28 +04:00
wm8731_set_bias_level ( codec , SND_SOC_BIAS_STANDBY ) ;
2006-10-06 20:36:07 +04:00
/* set the update bits */
reg = wm8731_read_reg_cache ( codec , WM8731_LOUT1V ) ;
2007-11-26 10:58:24 +03:00
wm8731_write ( codec , WM8731_LOUT1V , reg & ~ 0x0100 ) ;
2006-10-06 20:36:07 +04:00
reg = wm8731_read_reg_cache ( codec , WM8731_ROUT1V ) ;
2007-11-26 10:58:24 +03:00
wm8731_write ( codec , WM8731_ROUT1V , reg & ~ 0x0100 ) ;
2006-10-06 20:36:07 +04:00
reg = wm8731_read_reg_cache ( codec , WM8731_LINVOL ) ;
2007-11-26 10:58:24 +03:00
wm8731_write ( codec , WM8731_LINVOL , reg & ~ 0x0100 ) ;
2006-10-06 20:36:07 +04:00
reg = wm8731_read_reg_cache ( codec , WM8731_RINVOL ) ;
2007-11-26 10:58:24 +03:00
wm8731_write ( codec , WM8731_RINVOL , reg & ~ 0x0100 ) ;
2006-10-06 20:36:07 +04:00
2009-01-09 03:23:21 +03:00
snd_soc_add_controls ( codec , wm8731_snd_controls ,
ARRAY_SIZE ( wm8731_snd_controls ) ) ;
2006-10-06 20:36:07 +04:00
wm8731_add_widgets ( codec ) ;
2008-11-28 14:49:07 +03:00
ret = snd_soc_init_card ( socdev ) ;
2006-10-06 20:36:07 +04:00
if ( ret < 0 ) {
2007-01-31 12:02:23 +03:00
printk ( KERN_ERR " wm8731: failed to register card \n " ) ;
goto card_err ;
2006-10-06 20:36:07 +04:00
}
return ret ;
2007-01-31 12:02:23 +03:00
card_err :
snd_soc_free_pcms ( socdev ) ;
snd_soc_dapm_free ( socdev ) ;
pcm_err :
kfree ( codec - > reg_cache ) ;
return ret ;
2006-10-06 20:36:07 +04:00
}
static struct snd_soc_device * wm8731_socdev ;
2008-04-23 17:16:46 +04:00
# if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
2006-10-06 20:36:07 +04:00
/*
* WM8731 2 wire address is determined by GPIO5
* state during powerup .
* low = 0x1a
* high = 0x1b
*/
2008-09-01 21:47:01 +04:00
static int wm8731_i2c_probe ( struct i2c_client * i2c ,
const struct i2c_device_id * id )
2006-10-06 20:36:07 +04:00
{
struct snd_soc_device * socdev = wm8731_socdev ;
2009-01-24 01:55:23 +03:00
struct snd_soc_codec * codec = socdev - > card - > codec ;
2006-10-06 20:36:07 +04:00
int ret ;
i2c_set_clientdata ( i2c , codec ) ;
codec - > control_data = i2c ;
ret = wm8731_init ( socdev ) ;
2008-09-01 21:47:01 +04:00
if ( ret < 0 )
2008-06-23 17:51:29 +04:00
pr_err ( " failed to initialise WM8731 \n " ) ;
2006-10-06 20:36:07 +04:00
return ret ;
}
2008-09-01 21:47:01 +04:00
static int wm8731_i2c_remove ( struct i2c_client * client )
2006-10-06 20:36:07 +04:00
{
2008-04-23 17:16:46 +04:00
struct snd_soc_codec * codec = i2c_get_clientdata ( client ) ;
2006-10-06 20:36:07 +04:00
kfree ( codec - > reg_cache ) ;
return 0 ;
}
2008-09-01 21:47:01 +04:00
static const struct i2c_device_id wm8731_i2c_id [ ] = {
{ " wm8731 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , wm8731_i2c_id ) ;
2006-10-06 20:36:07 +04:00
static struct i2c_driver wm8731_i2c_driver = {
. driver = {
. name = " WM8731 I2C Codec " ,
. owner = THIS_MODULE ,
} ,
2008-09-01 21:47:01 +04:00
. probe = wm8731_i2c_probe ,
. remove = wm8731_i2c_remove ,
. id_table = wm8731_i2c_id ,
2006-10-06 20:36:07 +04:00
} ;
2008-09-01 21:47:01 +04:00
static int wm8731_add_i2c_device ( struct platform_device * pdev ,
const struct wm8731_setup_data * setup )
{
struct i2c_board_info info ;
struct i2c_adapter * adapter ;
struct i2c_client * client ;
int ret ;
ret = i2c_add_driver ( & wm8731_i2c_driver ) ;
if ( ret ! = 0 ) {
dev_err ( & pdev - > dev , " can't add i2c driver \n " ) ;
return ret ;
}
memset ( & info , 0 , sizeof ( struct i2c_board_info ) ) ;
info . addr = setup - > i2c_address ;
strlcpy ( info . type , " wm8731 " , I2C_NAME_SIZE ) ;
adapter = i2c_get_adapter ( setup - > i2c_bus ) ;
if ( ! adapter ) {
dev_err ( & pdev - > dev , " can't get i2c adapter %d \n " ,
setup - > i2c_bus ) ;
goto err_driver ;
}
client = i2c_new_device ( adapter , & info ) ;
i2c_put_adapter ( adapter ) ;
if ( ! client ) {
dev_err ( & pdev - > dev , " can't add i2c device at 0x%x \n " ,
( unsigned int ) info . addr ) ;
goto err_driver ;
}
return 0 ;
err_driver :
i2c_del_driver ( & wm8731_i2c_driver ) ;
return - ENODEV ;
}
2006-10-06 20:36:07 +04:00
# endif
2008-09-01 21:47:03 +04:00
# if defined(CONFIG_SPI_MASTER)
static int __devinit wm8731_spi_probe ( struct spi_device * spi )
{
struct snd_soc_device * socdev = wm8731_socdev ;
2009-01-24 01:55:23 +03:00
struct snd_soc_codec * codec = socdev - > card - > codec ;
2008-09-01 21:47:03 +04:00
int ret ;
codec - > control_data = spi ;
ret = wm8731_init ( socdev ) ;
if ( ret < 0 )
dev_err ( & spi - > dev , " failed to initialise WM8731 \n " ) ;
return ret ;
}
static int __devexit wm8731_spi_remove ( struct spi_device * spi )
{
return 0 ;
}
static struct spi_driver wm8731_spi_driver = {
. driver = {
. name = " wm8731 " ,
. bus = & spi_bus_type ,
. owner = THIS_MODULE ,
} ,
. probe = wm8731_spi_probe ,
. remove = __devexit_p ( wm8731_spi_remove ) ,
} ;
static int wm8731_spi_write ( struct spi_device * spi , const char * data , int len )
{
struct spi_transfer t ;
struct spi_message m ;
2008-09-09 22:25:49 +04:00
u8 msg [ 2 ] ;
2008-09-01 21:47:03 +04:00
if ( len < = 0 )
return 0 ;
2008-09-09 22:25:49 +04:00
msg [ 0 ] = data [ 0 ] ;
msg [ 1 ] = data [ 1 ] ;
2008-09-01 21:47:03 +04:00
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 ;
}
# endif /* CONFIG_SPI_MASTER */
2006-10-06 20:36:07 +04:00
static int wm8731_probe ( struct platform_device * pdev )
{
struct snd_soc_device * socdev = platform_get_drvdata ( pdev ) ;
struct wm8731_setup_data * setup ;
struct snd_soc_codec * codec ;
2007-02-02 19:14:56 +03:00
struct wm8731_priv * wm8731 ;
2006-10-06 20:36:07 +04:00
int ret = 0 ;
setup = socdev - > codec_data ;
codec = kzalloc ( sizeof ( struct snd_soc_codec ) , GFP_KERNEL ) ;
if ( codec = = NULL )
return - ENOMEM ;
2007-02-02 19:14:56 +03:00
wm8731 = kzalloc ( sizeof ( struct wm8731_priv ) , GFP_KERNEL ) ;
if ( wm8731 = = NULL ) {
kfree ( codec ) ;
return - ENOMEM ;
}
codec - > private_data = wm8731 ;
2009-01-24 01:55:23 +03:00
socdev - > card - > codec = codec ;
2006-10-06 20:36:07 +04:00
mutex_init ( & codec - > mutex ) ;
INIT_LIST_HEAD ( & codec - > dapm_widgets ) ;
INIT_LIST_HEAD ( & codec - > dapm_paths ) ;
wm8731_socdev = socdev ;
2008-09-01 21:47:03 +04:00
ret = - ENODEV ;
2008-04-23 17:16:46 +04:00
# if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
2006-10-06 20:36:07 +04:00
if ( setup - > i2c_address ) {
codec - > hw_write = ( hw_write_t ) i2c_master_send ;
2008-09-01 21:47:01 +04:00
ret = wm8731_add_i2c_device ( pdev , setup ) ;
2006-10-06 20:36:07 +04:00
}
2008-09-01 21:47:03 +04:00
# endif
# if defined(CONFIG_SPI_MASTER)
if ( setup - > spi ) {
codec - > hw_write = ( hw_write_t ) wm8731_spi_write ;
ret = spi_register_driver ( & wm8731_spi_driver ) ;
if ( ret ! = 0 )
printk ( KERN_ERR " can't add spi driver " ) ;
}
2006-10-06 20:36:07 +04:00
# endif
2008-08-25 14:49:20 +04:00
if ( ret ! = 0 ) {
kfree ( codec - > private_data ) ;
kfree ( codec ) ;
}
2006-10-06 20:36:07 +04:00
return ret ;
}
/* power down chip */
static int wm8731_remove ( struct platform_device * pdev )
{
struct snd_soc_device * socdev = platform_get_drvdata ( pdev ) ;
2009-01-24 01:55:23 +03:00
struct snd_soc_codec * codec = socdev - > card - > codec ;
2006-10-06 20:36:07 +04:00
if ( codec - > control_data )
2008-05-19 14:31:28 +04:00
wm8731_set_bias_level ( codec , SND_SOC_BIAS_OFF ) ;
2006-10-06 20:36:07 +04:00
snd_soc_free_pcms ( socdev ) ;
snd_soc_dapm_free ( socdev ) ;
2008-04-23 17:16:46 +04:00
# if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
2008-09-01 21:47:01 +04:00
i2c_unregister_device ( codec - > control_data ) ;
2006-10-06 20:36:07 +04:00
i2c_del_driver ( & wm8731_i2c_driver ) ;
2008-09-01 21:47:03 +04:00
# endif
# if defined(CONFIG_SPI_MASTER)
spi_unregister_driver ( & wm8731_spi_driver ) ;
2006-10-06 20:36:07 +04:00
# endif
2007-02-02 19:14:56 +03:00
kfree ( codec - > private_data ) ;
2006-10-06 20:36:07 +04:00
kfree ( codec ) ;
return 0 ;
}
struct snd_soc_codec_device soc_codec_dev_wm8731 = {
. probe = wm8731_probe ,
. remove = wm8731_remove ,
. suspend = wm8731_suspend ,
. resume = wm8731_resume ,
} ;
EXPORT_SYMBOL_GPL ( soc_codec_dev_wm8731 ) ;
2008-12-10 09:47:22 +03:00
static int __init wm8731_modinit ( void )
2008-12-08 22:17:58 +03:00
{
return snd_soc_register_dai ( & wm8731_dai ) ;
}
module_init ( wm8731_modinit ) ;
static void __exit wm8731_exit ( void )
{
snd_soc_unregister_dai ( & wm8731_dai ) ;
}
module_exit ( wm8731_exit ) ;
2006-10-06 20:36:07 +04:00
MODULE_DESCRIPTION ( " ASoC WM8731 driver " ) ;
MODULE_AUTHOR ( " Richard Purdie " ) ;
MODULE_LICENSE ( " GPL " ) ;