2007-05-14 13:03:52 +04:00
/*
* neo1973_wm8753 . c - - SoC audio for Neo1973
*
* Copyright 2007 Wolfson Microelectronics PLC .
* Author : Graeme Gregory
* graeme . gregory @ wolfsonmicro . com or linux @ 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 <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/timer.h>
# include <linux/interrupt.h>
# include <linux/platform_device.h>
# include <linux/i2c.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/soc.h>
# include <sound/soc-dapm.h>
2008-06-11 16:47:14 +04:00
# include <sound/tlv.h>
2007-05-14 13:03:52 +04:00
2008-10-08 16:02:20 +04:00
# include <asm/mach-types.h>
2007-05-14 13:03:52 +04:00
# include <asm/hardware/scoop.h>
2008-08-05 19:14:15 +04:00
# include <mach/regs-clock.h>
# include <mach/regs-gpio.h>
# include <mach/hardware.h>
# include <mach/audio.h>
2008-04-30 22:24:54 +04:00
# include <linux/io.h>
2008-08-05 19:14:15 +04:00
# include <mach/spi-gpio.h>
2007-12-19 17:37:49 +03:00
# include <asm/plat-s3c24xx/regs-iis.h>
2007-05-14 13:03:52 +04:00
# include "../codecs/wm8753.h"
# include "lm4857.h"
# include "s3c24xx-pcm.h"
# include "s3c24xx-i2s.h"
2008-05-05 16:16:12 +04:00
/* Debugging stuff */
# define S3C24XX_SOC_NEO1973_WM8753_DEBUG 0
# if S3C24XX_SOC_NEO1973_WM8753_DEBUG
# define DBG(x...) printk(KERN_DEBUG "s3c24xx-soc-neo1973-wm8753: " x)
# else
# define DBG(x...)
# endif
2007-05-14 13:03:52 +04:00
/* define the scenarios */
# define NEO_AUDIO_OFF 0
# define NEO_GSM_CALL_AUDIO_HANDSET 1
# define NEO_GSM_CALL_AUDIO_HEADSET 2
# define NEO_GSM_CALL_AUDIO_BLUETOOTH 3
# define NEO_STEREO_TO_SPEAKERS 4
# define NEO_STEREO_TO_HEADPHONES 5
# define NEO_CAPTURE_HANDSET 6
# define NEO_CAPTURE_HEADSET 7
# define NEO_CAPTURE_BLUETOOTH 8
2008-11-18 23:50:34 +03:00
static struct snd_soc_card neo1973 ;
2007-05-14 13:03:52 +04:00
static struct i2c_client * i2c ;
static int neo1973_hifi_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2008-07-07 19:08:24 +04:00
struct snd_soc_dai * codec_dai = rtd - > dai - > codec_dai ;
struct snd_soc_dai * cpu_dai = rtd - > dai - > cpu_dai ;
2007-05-14 13:03:52 +04:00
unsigned int pll_out = 0 , bclk = 0 ;
int ret = 0 ;
unsigned long iis_clkrate ;
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2007-05-14 13:03:52 +04:00
iis_clkrate = s3c24xx_i2s_get_clockrate ( ) ;
switch ( params_rate ( params ) ) {
case 8000 :
case 16000 :
pll_out = 12288000 ;
break ;
case 48000 :
bclk = WM8753_BCLK_DIV_4 ;
pll_out = 12288000 ;
break ;
case 96000 :
bclk = WM8753_BCLK_DIV_2 ;
pll_out = 12288000 ;
break ;
case 11025 :
bclk = WM8753_BCLK_DIV_16 ;
pll_out = 11289600 ;
break ;
case 22050 :
bclk = WM8753_BCLK_DIV_8 ;
pll_out = 11289600 ;
break ;
case 44100 :
bclk = WM8753_BCLK_DIV_4 ;
pll_out = 11289600 ;
break ;
case 88200 :
bclk = WM8753_BCLK_DIV_2 ;
pll_out = 11289600 ;
break ;
}
/* set codec DAI configuration */
2008-07-08 16:19:18 +04:00
ret = snd_soc_dai_set_fmt ( codec_dai ,
2007-05-14 13:03:52 +04:00
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM ) ;
if ( ret < 0 )
return ret ;
/* set cpu DAI configuration */
2008-07-08 16:19:18 +04:00
ret = snd_soc_dai_set_fmt ( cpu_dai ,
2007-05-14 13:03:52 +04:00
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM ) ;
if ( ret < 0 )
return ret ;
/* set the codec system clock for DAC and ADC */
2008-07-08 16:19:18 +04:00
ret = snd_soc_dai_set_sysclk ( codec_dai , WM8753_MCLK , pll_out ,
2007-05-14 13:03:52 +04:00
SND_SOC_CLOCK_IN ) ;
if ( ret < 0 )
return ret ;
/* set MCLK division for sample rate */
2008-07-08 16:19:18 +04:00
ret = snd_soc_dai_set_clkdiv ( cpu_dai , S3C24XX_DIV_MCLK ,
2008-04-30 22:24:54 +04:00
S3C2410_IISMOD_32FS ) ;
2007-05-14 13:03:52 +04:00
if ( ret < 0 )
return ret ;
/* set codec BCLK division for sample rate */
2008-07-08 16:19:18 +04:00
ret = snd_soc_dai_set_clkdiv ( codec_dai , WM8753_BCLKDIV , bclk ) ;
2007-05-14 13:03:52 +04:00
if ( ret < 0 )
return ret ;
/* set prescaler division for sample rate */
2008-07-08 16:19:18 +04:00
ret = snd_soc_dai_set_clkdiv ( cpu_dai , S3C24XX_DIV_PRESCALER ,
2008-04-30 22:24:54 +04:00
S3C24XX_PRESCALE ( 4 , 4 ) ) ;
2007-05-14 13:03:52 +04:00
if ( ret < 0 )
return ret ;
/* codec PLL input is PCLK/4 */
2008-07-08 16:19:18 +04:00
ret = snd_soc_dai_set_pll ( codec_dai , WM8753_PLL1 ,
2007-05-14 13:03:52 +04:00
iis_clkrate / 4 , pll_out ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static int neo1973_hifi_hw_free ( struct snd_pcm_substream * substream )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2008-07-07 19:08:24 +04:00
struct snd_soc_dai * codec_dai = rtd - > dai - > codec_dai ;
2007-05-14 13:03:52 +04:00
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2007-05-14 13:03:52 +04:00
/* disable the PLL */
2008-07-08 16:19:18 +04:00
return snd_soc_dai_set_pll ( codec_dai , WM8753_PLL1 , 0 , 0 ) ;
2007-05-14 13:03:52 +04:00
}
/*
* Neo1973 WM8753 HiFi DAI opserations .
*/
static struct snd_soc_ops neo1973_hifi_ops = {
. hw_params = neo1973_hifi_hw_params ,
. hw_free = neo1973_hifi_hw_free ,
} ;
static int neo1973_voice_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2008-07-07 19:08:24 +04:00
struct snd_soc_dai * codec_dai = rtd - > dai - > codec_dai ;
2007-05-14 13:03:52 +04:00
unsigned int pcmdiv = 0 ;
int ret = 0 ;
unsigned long iis_clkrate ;
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2007-05-14 13:03:52 +04:00
iis_clkrate = s3c24xx_i2s_get_clockrate ( ) ;
if ( params_rate ( params ) ! = 8000 )
return - EINVAL ;
if ( params_channels ( params ) ! = 1 )
return - EINVAL ;
pcmdiv = WM8753_PCM_DIV_6 ; /* 2.048 MHz */
/* todo: gg check mode (DSP_B) against CSR datasheet */
/* set codec DAI configuration */
2008-07-08 16:19:18 +04:00
ret = snd_soc_dai_set_fmt ( codec_dai , SND_SOC_DAIFMT_DSP_B |
2007-05-14 13:03:52 +04:00
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS ) ;
if ( ret < 0 )
return ret ;
/* set the codec system clock for DAC and ADC */
2008-07-08 16:19:18 +04:00
ret = snd_soc_dai_set_sysclk ( codec_dai , WM8753_PCMCLK , 12288000 ,
2007-05-14 13:03:52 +04:00
SND_SOC_CLOCK_IN ) ;
if ( ret < 0 )
return ret ;
/* set codec PCM division for sample rate */
2008-07-08 16:19:18 +04:00
ret = snd_soc_dai_set_clkdiv ( codec_dai , WM8753_PCMDIV , pcmdiv ) ;
2007-05-14 13:03:52 +04:00
if ( ret < 0 )
return ret ;
/* configue and enable PLL for 12.288MHz output */
2008-07-08 16:19:18 +04:00
ret = snd_soc_dai_set_pll ( codec_dai , WM8753_PLL2 ,
2007-05-14 13:03:52 +04:00
iis_clkrate / 4 , 12288000 ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static int neo1973_voice_hw_free ( struct snd_pcm_substream * substream )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2008-07-07 19:08:24 +04:00
struct snd_soc_dai * codec_dai = rtd - > dai - > codec_dai ;
2007-05-14 13:03:52 +04:00
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2007-05-14 13:03:52 +04:00
/* disable the PLL */
2008-07-08 16:19:18 +04:00
return snd_soc_dai_set_pll ( codec_dai , WM8753_PLL2 , 0 , 0 ) ;
2007-05-14 13:03:52 +04:00
}
static struct snd_soc_ops neo1973_voice_ops = {
. hw_params = neo1973_voice_hw_params ,
. hw_free = neo1973_voice_hw_free ,
} ;
2008-04-30 22:24:54 +04:00
static int neo1973_scenario ;
2007-05-14 13:03:52 +04:00
static int neo1973_get_scenario ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
ucontrol - > value . integer . value [ 0 ] = neo1973_scenario ;
return 0 ;
}
static int set_scenario_endpoints ( struct snd_soc_codec * codec , int scenario )
{
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2008-04-30 22:24:54 +04:00
switch ( neo1973_scenario ) {
2007-05-14 13:03:52 +04:00
case NEO_AUDIO_OFF :
2008-07-07 16:35:17 +04:00
snd_soc_dapm_disable_pin ( codec , " Audio Out " ) ;
snd_soc_dapm_disable_pin ( codec , " GSM Line Out " ) ;
snd_soc_dapm_disable_pin ( codec , " GSM Line In " ) ;
snd_soc_dapm_disable_pin ( codec , " Headset Mic " ) ;
snd_soc_dapm_disable_pin ( codec , " Call Mic " ) ;
2007-05-14 13:03:52 +04:00
break ;
case NEO_GSM_CALL_AUDIO_HANDSET :
2008-07-07 16:35:17 +04:00
snd_soc_dapm_enable_pin ( codec , " Audio Out " ) ;
snd_soc_dapm_enable_pin ( codec , " GSM Line Out " ) ;
snd_soc_dapm_enable_pin ( codec , " GSM Line In " ) ;
snd_soc_dapm_disable_pin ( codec , " Headset Mic " ) ;
snd_soc_dapm_enable_pin ( codec , " Call Mic " ) ;
2007-05-14 13:03:52 +04:00
break ;
case NEO_GSM_CALL_AUDIO_HEADSET :
2008-07-07 16:35:17 +04:00
snd_soc_dapm_enable_pin ( codec , " Audio Out " ) ;
snd_soc_dapm_enable_pin ( codec , " GSM Line Out " ) ;
snd_soc_dapm_enable_pin ( codec , " GSM Line In " ) ;
snd_soc_dapm_enable_pin ( codec , " Headset Mic " ) ;
snd_soc_dapm_disable_pin ( codec , " Call Mic " ) ;
2007-05-14 13:03:52 +04:00
break ;
case NEO_GSM_CALL_AUDIO_BLUETOOTH :
2008-07-07 16:35:17 +04:00
snd_soc_dapm_disable_pin ( codec , " Audio Out " ) ;
snd_soc_dapm_enable_pin ( codec , " GSM Line Out " ) ;
snd_soc_dapm_enable_pin ( codec , " GSM Line In " ) ;
snd_soc_dapm_disable_pin ( codec , " Headset Mic " ) ;
snd_soc_dapm_disable_pin ( codec , " Call Mic " ) ;
2007-05-14 13:03:52 +04:00
break ;
case NEO_STEREO_TO_SPEAKERS :
2008-07-07 16:35:17 +04:00
snd_soc_dapm_enable_pin ( codec , " Audio Out " ) ;
snd_soc_dapm_disable_pin ( codec , " GSM Line Out " ) ;
snd_soc_dapm_disable_pin ( codec , " GSM Line In " ) ;
snd_soc_dapm_disable_pin ( codec , " Headset Mic " ) ;
snd_soc_dapm_disable_pin ( codec , " Call Mic " ) ;
2007-05-14 13:03:52 +04:00
break ;
case NEO_STEREO_TO_HEADPHONES :
2008-07-07 16:35:17 +04:00
snd_soc_dapm_enable_pin ( codec , " Audio Out " ) ;
snd_soc_dapm_disable_pin ( codec , " GSM Line Out " ) ;
snd_soc_dapm_disable_pin ( codec , " GSM Line In " ) ;
snd_soc_dapm_disable_pin ( codec , " Headset Mic " ) ;
snd_soc_dapm_disable_pin ( codec , " Call Mic " ) ;
2007-05-14 13:03:52 +04:00
break ;
case NEO_CAPTURE_HANDSET :
2008-07-07 16:35:17 +04:00
snd_soc_dapm_disable_pin ( codec , " Audio Out " ) ;
snd_soc_dapm_disable_pin ( codec , " GSM Line Out " ) ;
snd_soc_dapm_disable_pin ( codec , " GSM Line In " ) ;
snd_soc_dapm_disable_pin ( codec , " Headset Mic " ) ;
snd_soc_dapm_enable_pin ( codec , " Call Mic " ) ;
2007-05-14 13:03:52 +04:00
break ;
case NEO_CAPTURE_HEADSET :
2008-07-07 16:35:17 +04:00
snd_soc_dapm_disable_pin ( codec , " Audio Out " ) ;
snd_soc_dapm_disable_pin ( codec , " GSM Line Out " ) ;
snd_soc_dapm_disable_pin ( codec , " GSM Line In " ) ;
snd_soc_dapm_enable_pin ( codec , " Headset Mic " ) ;
snd_soc_dapm_disable_pin ( codec , " Call Mic " ) ;
2007-05-14 13:03:52 +04:00
break ;
case NEO_CAPTURE_BLUETOOTH :
2008-07-07 16:35:17 +04:00
snd_soc_dapm_disable_pin ( codec , " Audio Out " ) ;
snd_soc_dapm_disable_pin ( codec , " GSM Line Out " ) ;
snd_soc_dapm_disable_pin ( codec , " GSM Line In " ) ;
snd_soc_dapm_disable_pin ( codec , " Headset Mic " ) ;
snd_soc_dapm_disable_pin ( codec , " Call Mic " ) ;
2007-05-14 13:03:52 +04:00
break ;
default :
2008-07-07 16:35:17 +04:00
snd_soc_dapm_disable_pin ( codec , " Audio Out " ) ;
snd_soc_dapm_disable_pin ( codec , " GSM Line Out " ) ;
snd_soc_dapm_disable_pin ( codec , " GSM Line In " ) ;
snd_soc_dapm_disable_pin ( codec , " Headset Mic " ) ;
snd_soc_dapm_disable_pin ( codec , " Call Mic " ) ;
2007-05-14 13:03:52 +04:00
}
2008-07-07 16:35:17 +04:00
snd_soc_dapm_sync ( codec ) ;
2007-05-14 13:03:52 +04:00
return 0 ;
}
static int neo1973_set_scenario ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2007-05-14 13:03:52 +04:00
if ( neo1973_scenario = = ucontrol - > value . integer . value [ 0 ] )
return 0 ;
neo1973_scenario = ucontrol - > value . integer . value [ 0 ] ;
set_scenario_endpoints ( codec , neo1973_scenario ) ;
return 1 ;
}
static u8 lm4857_regs [ 4 ] = { 0x00 , 0x40 , 0x80 , 0xC0 } ;
static void lm4857_write_regs ( void )
{
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2007-05-14 13:03:52 +04:00
if ( i2c_master_send ( i2c , lm4857_regs , 4 ) ! = 4 )
printk ( KERN_ERR " lm4857: i2c write failed \n " ) ;
}
static int lm4857_get_reg ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2008-04-30 22:24:54 +04:00
int reg = kcontrol - > private_value & 0xFF ;
2007-05-14 13:03:52 +04:00
int shift = ( kcontrol - > private_value > > 8 ) & 0x0F ;
int mask = ( kcontrol - > private_value > > 16 ) & 0xFF ;
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2007-05-14 13:03:52 +04:00
ucontrol - > value . integer . value [ 0 ] = ( lm4857_regs [ reg ] > > shift ) & mask ;
return 0 ;
}
static int lm4857_set_reg ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
int reg = kcontrol - > private_value & 0xFF ;
int shift = ( kcontrol - > private_value > > 8 ) & 0x0F ;
int mask = ( kcontrol - > private_value > > 16 ) & 0xFF ;
2008-04-30 22:24:54 +04:00
if ( ( ( lm4857_regs [ reg ] > > shift ) & mask ) = =
2007-05-14 13:03:52 +04:00
ucontrol - > value . integer . value [ 0 ] )
return 0 ;
2008-04-30 22:24:54 +04:00
lm4857_regs [ reg ] & = ~ ( mask < < shift ) ;
2007-05-14 13:03:52 +04:00
lm4857_regs [ reg ] | = ucontrol - > value . integer . value [ 0 ] < < shift ;
lm4857_write_regs ( ) ;
return 1 ;
}
static int lm4857_get_mode ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
u8 value = lm4857_regs [ LM4857_CTRL ] & 0x0F ;
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2007-05-14 13:03:52 +04:00
if ( value )
value - = 5 ;
ucontrol - > value . integer . value [ 0 ] = value ;
return 0 ;
}
static int lm4857_set_mode ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
u8 value = ucontrol - > value . integer . value [ 0 ] ;
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2007-05-14 13:03:52 +04:00
if ( value )
value + = 5 ;
if ( ( lm4857_regs [ LM4857_CTRL ] & 0x0F ) = = value )
return 0 ;
lm4857_regs [ LM4857_CTRL ] & = 0xF0 ;
lm4857_regs [ LM4857_CTRL ] | = value ;
lm4857_write_regs ( ) ;
return 1 ;
}
static const struct snd_soc_dapm_widget wm8753_dapm_widgets [ ] = {
SND_SOC_DAPM_LINE ( " Audio Out " , NULL ) ,
SND_SOC_DAPM_LINE ( " GSM Line Out " , NULL ) ,
SND_SOC_DAPM_LINE ( " GSM Line In " , NULL ) ,
SND_SOC_DAPM_MIC ( " Headset Mic " , NULL ) ,
SND_SOC_DAPM_MIC ( " Call Mic " , NULL ) ,
} ;
2008-05-13 16:58:03 +04:00
static const struct snd_soc_dapm_route dapm_routes [ ] = {
2007-05-14 13:03:52 +04:00
/* Connections to the lm4857 amp */
{ " Audio Out " , NULL , " LOUT1 " } ,
{ " Audio Out " , NULL , " ROUT1 " } ,
/* Connections to the GSM Module */
{ " GSM Line Out " , NULL , " MONO1 " } ,
{ " GSM Line Out " , NULL , " MONO2 " } ,
{ " RXP " , NULL , " GSM Line In " } ,
{ " RXN " , NULL , " GSM Line In " } ,
/* Connections to Headset */
{ " MIC1 " , NULL , " Mic Bias " } ,
{ " Mic Bias " , NULL , " Headset Mic " } ,
/* Call Mic */
{ " MIC2 " , NULL , " Mic Bias " } ,
{ " MIC2N " , NULL , " Mic Bias " } ,
{ " Mic Bias " , NULL , " Call Mic " } ,
/* Connect the ALC pins */
{ " ACIN " , NULL , " ACOP " } ,
} ;
static const char * lm4857_mode [ ] = {
" Off " ,
" Call Speaker " ,
" Stereo Speakers " ,
" Stereo Speakers + Headphones " ,
" Headphones "
} ;
static const struct soc_enum lm4857_mode_enum [ ] = {
SOC_ENUM_SINGLE_EXT ( ARRAY_SIZE ( lm4857_mode ) , lm4857_mode ) ,
} ;
static const char * neo_scenarios [ ] = {
" Off " ,
" GSM Handset " ,
" GSM Headset " ,
" GSM Bluetooth " ,
" Speakers " ,
" Headphones " ,
" Capture Handset " ,
" Capture Headset " ,
" Capture Bluetooth "
} ;
static const struct soc_enum neo_scenario_enum [ ] = {
2008-04-30 22:24:54 +04:00
SOC_ENUM_SINGLE_EXT ( ARRAY_SIZE ( neo_scenarios ) , neo_scenarios ) ,
2007-05-14 13:03:52 +04:00
} ;
2008-06-11 16:47:14 +04:00
static const DECLARE_TLV_DB_SCALE ( stereo_tlv , - 4050 , 150 , 0 ) ;
static const DECLARE_TLV_DB_SCALE ( mono_tlv , - 3450 , 150 , 0 ) ;
2007-05-14 13:03:52 +04:00
static const struct snd_kcontrol_new wm8753_neo1973_controls [ ] = {
2008-06-11 16:47:14 +04:00
SOC_SINGLE_EXT_TLV ( " Amp Left Playback Volume " , LM4857_LVOL , 0 , 31 , 0 ,
lm4857_get_reg , lm4857_set_reg , stereo_tlv ) ,
SOC_SINGLE_EXT_TLV ( " Amp Right Playback Volume " , LM4857_RVOL , 0 , 31 , 0 ,
lm4857_get_reg , lm4857_set_reg , stereo_tlv ) ,
SOC_SINGLE_EXT_TLV ( " Amp Mono Playback Volume " , LM4857_MVOL , 0 , 31 , 0 ,
lm4857_get_reg , lm4857_set_reg , mono_tlv ) ,
2007-05-14 13:03:52 +04:00
SOC_ENUM_EXT ( " Amp Mode " , lm4857_mode_enum [ 0 ] ,
lm4857_get_mode , lm4857_set_mode ) ,
SOC_ENUM_EXT ( " Neo Mode " , neo_scenario_enum [ 0 ] ,
neo1973_get_scenario , neo1973_set_scenario ) ,
SOC_SINGLE_EXT ( " Amp Spk 3D Playback Switch " , LM4857_LVOL , 5 , 1 , 0 ,
lm4857_get_reg , lm4857_set_reg ) ,
SOC_SINGLE_EXT ( " Amp HP 3d Playback Switch " , LM4857_RVOL , 5 , 1 , 0 ,
lm4857_get_reg , lm4857_set_reg ) ,
SOC_SINGLE_EXT ( " Amp Fast Wakeup Playback Switch " , LM4857_CTRL , 5 , 1 , 0 ,
lm4857_get_reg , lm4857_set_reg ) ,
SOC_SINGLE_EXT ( " Amp Earpiece 6dB Playback Switch " , LM4857_CTRL , 4 , 1 , 0 ,
lm4857_get_reg , lm4857_set_reg ) ,
} ;
/*
* This is an example machine initialisation for a wm8753 connected to a
* neo1973 II . It is missing logic to detect hp / mic insertions and logic
* to re - route the audio in such an event .
*/
static int neo1973_wm8753_init ( struct snd_soc_codec * codec )
{
int i , err ;
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2007-05-14 13:03:52 +04:00
/* set up NC codec pins */
2008-09-24 14:33:05 +04:00
snd_soc_dapm_nc_pin ( codec , " LOUT2 " ) ;
snd_soc_dapm_nc_pin ( codec , " ROUT2 " ) ;
snd_soc_dapm_nc_pin ( codec , " OUT3 " ) ;
snd_soc_dapm_nc_pin ( codec , " OUT4 " ) ;
snd_soc_dapm_nc_pin ( codec , " LINE1 " ) ;
snd_soc_dapm_nc_pin ( codec , " LINE2 " ) ;
2007-05-14 13:03:52 +04:00
/* Add neo1973 specific widgets */
2008-05-13 16:58:03 +04:00
snd_soc_dapm_new_controls ( codec , wm8753_dapm_widgets ,
ARRAY_SIZE ( wm8753_dapm_widgets ) ) ;
2007-05-14 13:03:52 +04:00
2008-10-01 21:17:12 +04:00
/* set endpoints to default mode */
set_scenario_endpoints ( codec , NEO_AUDIO_OFF ) ;
2007-05-14 13:03:52 +04:00
/* add neo1973 specific controls */
for ( i = 0 ; i < ARRAY_SIZE ( wm8753_neo1973_controls ) ; i + + ) {
err = snd_ctl_add ( codec - > card ,
snd_soc_cnew ( & wm8753_neo1973_controls [ i ] ,
codec , NULL ) ) ;
if ( err < 0 )
return err ;
}
2008-05-13 16:58:03 +04:00
/* set up neo1973 specific audio routes */
err = snd_soc_dapm_add_routes ( codec , dapm_routes ,
ARRAY_SIZE ( dapm_routes ) ) ;
2007-05-14 13:03:52 +04:00
2008-07-07 16:35:17 +04:00
snd_soc_dapm_sync ( codec ) ;
2007-05-14 13:03:52 +04:00
return 0 ;
}
/*
* BT Codec DAI
*/
2008-07-07 19:08:24 +04:00
static struct snd_soc_dai bt_dai = {
2008-04-30 22:24:54 +04:00
. name = " Bluetooth " ,
2007-05-14 13:03:52 +04:00
. id = 0 ,
. playback = {
. channels_min = 1 ,
. channels_max = 1 ,
. rates = SNDRV_PCM_RATE_8000 ,
. formats = SNDRV_PCM_FMTBIT_S16_LE , } ,
. capture = {
. channels_min = 1 ,
. channels_max = 1 ,
. rates = SNDRV_PCM_RATE_8000 ,
. formats = SNDRV_PCM_FMTBIT_S16_LE , } ,
} ;
static struct snd_soc_dai_link neo1973_dai [ ] = {
{ /* Hifi Playback - for similatious use with voice below */
. name = " WM8753 " ,
. stream_name = " WM8753 HiFi " ,
. cpu_dai = & s3c24xx_i2s_dai ,
. codec_dai = & wm8753_dai [ WM8753_DAI_HIFI ] ,
. init = neo1973_wm8753_init ,
. ops = & neo1973_hifi_ops ,
} ,
{ /* Voice via BT */
. name = " Bluetooth " ,
. stream_name = " Voice " ,
. cpu_dai = & bt_dai ,
. codec_dai = & wm8753_dai [ WM8753_DAI_VOICE ] ,
. ops = & neo1973_voice_ops ,
} ,
} ;
2008-11-18 23:50:34 +03:00
static struct snd_soc_card neo1973 = {
2007-05-14 13:03:52 +04:00
. name = " neo1973 " ,
2008-12-02 19:01:14 +03:00
. platform = & s3c24xx_soc_platform ,
2007-05-14 13:03:52 +04:00
. dai_link = neo1973_dai ,
. num_links = ARRAY_SIZE ( neo1973_dai ) ,
} ;
static struct wm8753_setup_data neo1973_wm8753_setup = {
2008-09-01 19:45:34 +04:00
. i2c_bus = 0 ,
2007-05-14 13:03:52 +04:00
. i2c_address = 0x1a ,
} ;
static struct snd_soc_device neo1973_snd_devdata = {
2008-11-18 23:50:34 +03:00
. card = & neo1973 ,
2007-05-14 13:03:52 +04:00
. codec_dev = & soc_codec_dev_wm8753 ,
. codec_data = & neo1973_wm8753_setup ,
} ;
2008-09-01 19:46:57 +04:00
static int lm4857_i2c_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
2007-05-14 13:03:52 +04:00
{
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2008-10-01 23:47:19 +04:00
i2c = client ;
2007-05-14 13:03:52 +04:00
lm4857_write_regs ( ) ;
return 0 ;
}
2008-09-01 19:46:57 +04:00
static int lm4857_i2c_remove ( struct i2c_client * client )
2007-05-14 13:03:52 +04:00
{
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2008-10-01 23:47:19 +04:00
i2c = NULL ;
2008-09-01 19:46:57 +04:00
return 0 ;
2007-05-14 13:03:52 +04:00
}
2008-04-30 22:26:45 +04:00
static u8 lm4857_state ;
static int lm4857_suspend ( struct i2c_client * dev , pm_message_t state )
{
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2008-04-30 22:26:45 +04:00
dev_dbg ( & dev - > dev , " lm4857_suspend \n " ) ;
lm4857_state = lm4857_regs [ LM4857_CTRL ] & 0xf ;
if ( lm4857_state ) {
lm4857_regs [ LM4857_CTRL ] & = 0xf0 ;
lm4857_write_regs ( ) ;
}
return 0 ;
}
static int lm4857_resume ( struct i2c_client * dev )
{
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2008-04-30 22:26:45 +04:00
if ( lm4857_state ) {
lm4857_regs [ LM4857_CTRL ] | = ( lm4857_state & 0x0f ) ;
lm4857_write_regs ( ) ;
}
return 0 ;
}
static void lm4857_shutdown ( struct i2c_client * dev )
{
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2008-04-30 22:26:45 +04:00
dev_dbg ( & dev - > dev , " lm4857_shutdown \n " ) ;
lm4857_regs [ LM4857_CTRL ] & = 0xf0 ;
lm4857_write_regs ( ) ;
}
2008-09-01 19:46:57 +04:00
static const struct i2c_device_id lm4857_i2c_id [ ] = {
2008-09-24 14:57:27 +04:00
{ " neo1973_lm4857 " , 0 } ,
2008-09-01 19:46:57 +04:00
{ }
} ;
2007-05-14 13:03:52 +04:00
static struct i2c_driver lm4857_i2c_driver = {
. driver = {
. name = " LM4857 I2C Amp " ,
. owner = THIS_MODULE ,
} ,
2008-04-30 22:26:45 +04:00
. suspend = lm4857_suspend ,
. resume = lm4857_resume ,
. shutdown = lm4857_shutdown ,
2008-09-01 19:46:57 +04:00
. probe = lm4857_i2c_probe ,
. remove = lm4857_i2c_remove ,
. id_table = lm4857_i2c_id ,
2007-05-14 13:03:52 +04:00
} ;
static struct platform_device * neo1973_snd_device ;
static int __init neo1973_init ( void )
{
int ret ;
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2008-10-08 16:02:20 +04:00
if ( ! machine_is_neo1973_gta01 ( ) ) {
printk ( KERN_INFO
" Only GTA01 hardware supported by ASoC driver \n " ) ;
return - ENODEV ;
}
2007-05-14 13:03:52 +04:00
neo1973_snd_device = platform_device_alloc ( " soc-audio " , - 1 ) ;
if ( ! neo1973_snd_device )
return - ENOMEM ;
platform_set_drvdata ( neo1973_snd_device , & neo1973_snd_devdata ) ;
neo1973_snd_devdata . dev = & neo1973_snd_device - > dev ;
ret = platform_device_add ( neo1973_snd_device ) ;
2008-09-01 19:44:05 +04:00
if ( ret ) {
2007-05-14 13:03:52 +04:00
platform_device_put ( neo1973_snd_device ) ;
2008-09-01 19:44:05 +04:00
return ret ;
}
2007-05-14 13:03:52 +04:00
2008-10-01 23:47:19 +04:00
ret = i2c_add_driver ( & lm4857_i2c_driver ) ;
2008-09-01 19:46:57 +04:00
if ( ret ! = 0 )
2008-09-01 19:44:05 +04:00
platform_device_unregister ( neo1973_snd_device ) ;
2007-05-14 13:03:52 +04:00
return ret ;
}
static void __exit neo1973_exit ( void )
{
2008-05-05 16:16:12 +04:00
DBG ( " Entered %s \n " , __func__ ) ;
2008-04-22 19:10:23 +04:00
i2c_del_driver ( & lm4857_i2c_driver ) ;
2007-05-14 13:03:52 +04:00
platform_device_unregister ( neo1973_snd_device ) ;
}
module_init ( neo1973_init ) ;
module_exit ( neo1973_exit ) ;
/* Module information */
2008-04-30 22:25:23 +04:00
MODULE_AUTHOR ( " Graeme Gregory, graeme@openmoko.org, www.openmoko.org " ) ;
2007-05-14 13:03:52 +04:00
MODULE_DESCRIPTION ( " ALSA SoC WM8753 Neo1973 " ) ;
MODULE_LICENSE ( " GPL " ) ;