2010-05-05 12:14:22 +04:00
/*
* rx51 . c - - SoC audio for Nokia RX - 51
*
* Copyright ( C ) 2008 - 2009 Nokia Corporation
*
* Contact : Peter Ujfalusi < peter . ujfalusi @ nokia . com >
* Eduardo Valentin < eduardo . valentin @ nokia . com >
* Jarkko Nikula < jhnikula @ gmail . com >
*
* 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 .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA
* 02110 - 1301 USA
*
*/
# include <linux/delay.h>
# include <linux/gpio.h>
# include <linux/platform_device.h>
# include <sound/core.h>
2010-06-21 15:15:00 +04:00
# include <sound/jack.h>
2010-05-05 12:14:22 +04:00
# include <sound/pcm.h>
# include <sound/soc.h>
2010-03-17 23:15:21 +03:00
# include <plat/mcbsp.h>
2011-01-27 17:47:11 +03:00
# include "../codecs/tpa6130a2.h"
2010-05-05 12:14:22 +04:00
# include <asm/mach-types.h>
# include "omap-mcbsp.h"
# include "omap-pcm.h"
2010-06-21 15:14:59 +04:00
# define RX51_TVOUT_SEL_GPIO 40
2010-06-21 15:15:00 +04:00
# define RX51_JACK_DETECT_GPIO 177
2011-02-14 18:20:21 +03:00
# define RX51_ECI_SW_GPIO 182
2010-05-05 12:14:22 +04:00
/*
* REVISIT : TWL4030 GPIO base in RX - 51. Now statically defined to 192. This
* gpio is reserved in arch / arm / mach - omap2 / board - rx51 - peripherals . c
*/
# define RX51_SPEAKER_AMP_TWL_GPIO (192 + 7)
2010-06-21 15:14:59 +04:00
enum {
RX51_JACK_DISABLED ,
2011-01-27 17:47:11 +03:00
RX51_JACK_TVOUT , /* tv-out with stereo output */
RX51_JACK_HP , /* headphone: stereo output, no mic */
2011-02-14 18:20:21 +03:00
RX51_JACK_HS , /* headset: stereo output with mic */
2010-06-21 15:14:59 +04:00
} ;
2010-05-05 12:14:22 +04:00
static int rx51_spk_func ;
static int rx51_dmic_func ;
2010-06-21 15:14:59 +04:00
static int rx51_jack_func ;
2010-05-05 12:14:22 +04:00
static void rx51_ext_control ( struct snd_soc_codec * codec )
{
2010-11-05 16:53:46 +03:00
struct snd_soc_dapm_context * dapm = & codec - > dapm ;
2011-02-14 18:20:21 +03:00
int hp = 0 , hs = 0 , tvout = 0 ;
2011-01-27 17:47:11 +03:00
switch ( rx51_jack_func ) {
case RX51_JACK_TVOUT :
tvout = 1 ;
2011-02-14 18:20:21 +03:00
hp = 1 ;
break ;
case RX51_JACK_HS :
hs = 1 ;
2011-01-27 17:47:11 +03:00
case RX51_JACK_HP :
hp = 1 ;
break ;
}
2010-11-05 16:53:46 +03:00
2010-05-05 12:14:22 +04:00
if ( rx51_spk_func )
2010-11-05 16:53:46 +03:00
snd_soc_dapm_enable_pin ( dapm , " Ext Spk " ) ;
2010-05-05 12:14:22 +04:00
else
2010-11-05 16:53:46 +03:00
snd_soc_dapm_disable_pin ( dapm , " Ext Spk " ) ;
2010-05-05 12:14:22 +04:00
if ( rx51_dmic_func )
2010-11-05 16:53:46 +03:00
snd_soc_dapm_enable_pin ( dapm , " DMic " ) ;
2010-05-05 12:14:22 +04:00
else
2010-11-05 16:53:46 +03:00
snd_soc_dapm_disable_pin ( dapm , " DMic " ) ;
2011-01-27 17:47:11 +03:00
if ( hp )
snd_soc_dapm_enable_pin ( dapm , " Headphone Jack " ) ;
else
snd_soc_dapm_disable_pin ( dapm , " Headphone Jack " ) ;
2011-02-14 18:20:21 +03:00
if ( hs )
snd_soc_dapm_enable_pin ( dapm , " HS Mic " ) ;
else
snd_soc_dapm_disable_pin ( dapm , " HS Mic " ) ;
2010-05-05 12:14:22 +04:00
2011-01-27 17:47:11 +03:00
gpio_set_value ( RX51_TVOUT_SEL_GPIO , tvout ) ;
2010-06-21 15:14:59 +04:00
2010-11-05 16:53:46 +03:00
snd_soc_dapm_sync ( dapm ) ;
2010-05-05 12:14:22 +04:00
}
static int rx51_startup ( struct snd_pcm_substream * substream )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2010-03-17 23:15:21 +03:00
struct snd_soc_codec * codec = rtd - > codec ;
2010-05-05 12:14:22 +04:00
snd_pcm_hw_constraint_minmax ( runtime ,
SNDRV_PCM_HW_PARAM_CHANNELS , 2 , 2 ) ;
rx51_ext_control ( codec ) ;
return 0 ;
}
static int rx51_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2010-03-17 23:15:21 +03:00
struct snd_soc_dai * codec_dai = rtd - > codec_dai ;
struct snd_soc_dai * cpu_dai = rtd - > cpu_dai ;
2010-05-05 12:14:22 +04:00
int err ;
/* Set codec DAI configuration */
err = snd_soc_dai_set_fmt ( codec_dai ,
SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_NF |
SND_SOC_DAIFMT_CBM_CFM ) ;
if ( err < 0 )
return err ;
/* Set cpu DAI configuration */
err = snd_soc_dai_set_fmt ( cpu_dai ,
SND_SOC_DAIFMT_DSP_A |
SND_SOC_DAIFMT_IB_NF |
SND_SOC_DAIFMT_CBM_CFM ) ;
if ( err < 0 )
return err ;
/* Set the codec system clock for DAC and ADC */
return snd_soc_dai_set_sysclk ( codec_dai , 0 , 19200000 ,
SND_SOC_CLOCK_IN ) ;
}
static struct snd_soc_ops rx51_ops = {
. startup = rx51_startup ,
. hw_params = rx51_hw_params ,
} ;
static int rx51_get_spk ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
ucontrol - > value . integer . value [ 0 ] = rx51_spk_func ;
return 0 ;
}
static int rx51_set_spk ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
if ( rx51_spk_func = = ucontrol - > value . integer . value [ 0 ] )
return 0 ;
rx51_spk_func = ucontrol - > value . integer . value [ 0 ] ;
rx51_ext_control ( codec ) ;
return 1 ;
}
static int rx51_spk_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * k , int event )
{
if ( SND_SOC_DAPM_EVENT_ON ( event ) )
2010-08-23 11:36:41 +04:00
gpio_set_value_cansleep ( RX51_SPEAKER_AMP_TWL_GPIO , 1 ) ;
2010-05-05 12:14:22 +04:00
else
2010-08-23 11:36:41 +04:00
gpio_set_value_cansleep ( RX51_SPEAKER_AMP_TWL_GPIO , 0 ) ;
2010-05-05 12:14:22 +04:00
return 0 ;
}
2011-01-27 17:47:11 +03:00
static int rx51_hp_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * k , int event )
{
struct snd_soc_codec * codec = w - > dapm - > codec ;
if ( SND_SOC_DAPM_EVENT_ON ( event ) )
tpa6130a2_stereo_enable ( codec , 1 ) ;
else
tpa6130a2_stereo_enable ( codec , 0 ) ;
return 0 ;
}
2010-05-05 12:14:22 +04:00
static int rx51_get_input ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
ucontrol - > value . integer . value [ 0 ] = rx51_dmic_func ;
return 0 ;
}
static int rx51_set_input ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
if ( rx51_dmic_func = = ucontrol - > value . integer . value [ 0 ] )
return 0 ;
rx51_dmic_func = ucontrol - > value . integer . value [ 0 ] ;
rx51_ext_control ( codec ) ;
return 1 ;
}
2010-06-21 15:14:59 +04:00
static int rx51_get_jack ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
ucontrol - > value . integer . value [ 0 ] = rx51_jack_func ;
return 0 ;
}
static int rx51_set_jack ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
if ( rx51_jack_func = = ucontrol - > value . integer . value [ 0 ] )
return 0 ;
rx51_jack_func = ucontrol - > value . integer . value [ 0 ] ;
rx51_ext_control ( codec ) ;
return 1 ;
}
2010-06-21 15:15:00 +04:00
static struct snd_soc_jack rx51_av_jack ;
static struct snd_soc_jack_gpio rx51_av_jack_gpios [ ] = {
{
. gpio = RX51_JACK_DETECT_GPIO ,
. name = " avdet-gpio " ,
2011-02-14 18:20:22 +03:00
. report = SND_JACK_HEADSET ,
2010-06-21 15:15:00 +04:00
. invert = 1 ,
. debounce_time = 200 ,
} ,
} ;
2010-05-05 12:14:22 +04:00
static const struct snd_soc_dapm_widget aic34_dapm_widgets [ ] = {
SND_SOC_DAPM_SPK ( " Ext Spk " , rx51_spk_event ) ,
SND_SOC_DAPM_MIC ( " DMic " , NULL ) ,
2011-01-27 17:47:11 +03:00
SND_SOC_DAPM_HP ( " Headphone Jack " , rx51_hp_event ) ,
2011-02-14 18:20:21 +03:00
SND_SOC_DAPM_MIC ( " HS Mic " , NULL ) ,
2011-02-21 15:57:22 +03:00
SND_SOC_DAPM_LINE ( " FM Transmitter " , NULL ) ,
2010-05-05 12:14:22 +04:00
} ;
2011-01-31 15:43:48 +03:00
static const struct snd_soc_dapm_widget aic34_dapm_widgetsb [ ] = {
SND_SOC_DAPM_SPK ( " Earphone " , NULL ) ,
} ;
2010-05-05 12:14:22 +04:00
static const struct snd_soc_dapm_route audio_map [ ] = {
{ " Ext Spk " , NULL , " HPLOUT " } ,
{ " Ext Spk " , NULL , " HPROUT " } ,
2011-01-27 17:47:11 +03:00
{ " Headphone Jack " , NULL , " LLOUT " } ,
{ " Headphone Jack " , NULL , " RLOUT " } ,
2011-02-21 15:57:22 +03:00
{ " FM Transmitter " , NULL , " LLOUT " } ,
{ " FM Transmitter " , NULL , " RLOUT " } ,
2010-05-05 12:14:22 +04:00
{ " DMic Rate 64 " , NULL , " Mic Bias 2V " } ,
{ " Mic Bias 2V " , NULL , " DMic " } ,
} ;
2011-01-31 15:43:48 +03:00
static const struct snd_soc_dapm_route audio_mapb [ ] = {
{ " b LINE2R " , NULL , " MONO_LOUT " } ,
{ " Earphone " , NULL , " b HPLOUT " } ,
2011-02-14 18:20:21 +03:00
{ " LINE1L " , NULL , " b Mic Bias 2.5V " } ,
{ " b Mic Bias 2.5V " , NULL , " HS Mic " }
2011-01-31 15:43:48 +03:00
} ;
2010-05-05 12:14:22 +04:00
static const char * spk_function [ ] = { " Off " , " On " } ;
static const char * input_function [ ] = { " ADC " , " Digital Mic " } ;
2011-02-14 18:20:21 +03:00
static const char * jack_function [ ] = { " Off " , " TV-OUT " , " Headphone " , " Headset " } ;
2010-05-05 12:14:22 +04:00
static const struct soc_enum rx51_enum [ ] = {
SOC_ENUM_SINGLE_EXT ( ARRAY_SIZE ( spk_function ) , spk_function ) ,
SOC_ENUM_SINGLE_EXT ( ARRAY_SIZE ( input_function ) , input_function ) ,
2010-06-21 15:14:59 +04:00
SOC_ENUM_SINGLE_EXT ( ARRAY_SIZE ( jack_function ) , jack_function ) ,
2010-05-05 12:14:22 +04:00
} ;
static const struct snd_kcontrol_new aic34_rx51_controls [ ] = {
SOC_ENUM_EXT ( " Speaker Function " , rx51_enum [ 0 ] ,
rx51_get_spk , rx51_set_spk ) ,
SOC_ENUM_EXT ( " Input Select " , rx51_enum [ 1 ] ,
rx51_get_input , rx51_set_input ) ,
2010-06-21 15:14:59 +04:00
SOC_ENUM_EXT ( " Jack Function " , rx51_enum [ 2 ] ,
rx51_get_jack , rx51_set_jack ) ,
2011-02-21 15:57:22 +03:00
SOC_DAPM_PIN_SWITCH ( " FM Transmitter " ) ,
2010-05-05 12:14:22 +04:00
} ;
2011-01-31 15:43:48 +03:00
static const struct snd_kcontrol_new aic34_rx51_controlsb [ ] = {
SOC_DAPM_PIN_SWITCH ( " Earphone " ) ,
} ;
2010-03-17 23:15:21 +03:00
static int rx51_aic34_init ( struct snd_soc_pcm_runtime * rtd )
2010-05-05 12:14:22 +04:00
{
2010-03-17 23:15:21 +03:00
struct snd_soc_codec * codec = rtd - > codec ;
2010-11-05 16:53:46 +03:00
struct snd_soc_dapm_context * dapm = & codec - > dapm ;
2010-05-05 12:14:22 +04:00
int err ;
/* Set up NC codec pins */
2010-11-05 16:53:46 +03:00
snd_soc_dapm_nc_pin ( dapm , " MIC3L " ) ;
snd_soc_dapm_nc_pin ( dapm , " MIC3R " ) ;
snd_soc_dapm_nc_pin ( dapm , " LINE1R " ) ;
2010-05-05 12:14:22 +04:00
/* Add RX-51 specific controls */
err = snd_soc_add_controls ( codec , aic34_rx51_controls ,
ARRAY_SIZE ( aic34_rx51_controls ) ) ;
if ( err < 0 )
return err ;
/* Add RX-51 specific widgets */
2010-11-05 16:53:46 +03:00
snd_soc_dapm_new_controls ( dapm , aic34_dapm_widgets ,
2010-05-05 12:14:22 +04:00
ARRAY_SIZE ( aic34_dapm_widgets ) ) ;
/* Set up RX-51 specific audio path audio_map */
2010-11-05 16:53:46 +03:00
snd_soc_dapm_add_routes ( dapm , audio_map , ARRAY_SIZE ( audio_map ) ) ;
2010-05-05 12:14:22 +04:00
2011-01-27 17:47:11 +03:00
err = tpa6130a2_add_controls ( codec ) ;
if ( err < 0 )
return err ;
snd_soc_limit_volume ( codec , " TPA6130A2 Headphone Playback Volume " , 42 ) ;
2011-03-09 12:25:00 +03:00
err = omap_mcbsp_st_add_controls ( codec , 1 ) ;
if ( err < 0 )
return err ;
2010-11-05 16:53:46 +03:00
snd_soc_dapm_sync ( dapm ) ;
2010-05-05 12:14:22 +04:00
2010-06-21 15:15:00 +04:00
/* AV jack detection */
2010-03-17 23:15:21 +03:00
err = snd_soc_jack_new ( codec , " AV Jack " ,
2011-02-14 18:20:22 +03:00
SND_JACK_HEADSET | SND_JACK_VIDEOOUT ,
& rx51_av_jack ) ;
2010-06-21 15:15:00 +04:00
if ( err )
return err ;
err = snd_soc_jack_add_gpios ( & rx51_av_jack ,
ARRAY_SIZE ( rx51_av_jack_gpios ) ,
rx51_av_jack_gpios ) ;
return err ;
2010-05-05 12:14:22 +04:00
}
2011-01-31 15:43:48 +03:00
static int rx51_aic34b_init ( struct snd_soc_dapm_context * dapm )
{
int err ;
err = snd_soc_add_controls ( dapm - > codec , aic34_rx51_controlsb ,
ARRAY_SIZE ( aic34_rx51_controlsb ) ) ;
if ( err < 0 )
return err ;
err = snd_soc_dapm_new_controls ( dapm , aic34_dapm_widgetsb ,
ARRAY_SIZE ( aic34_dapm_widgetsb ) ) ;
if ( err < 0 )
return 0 ;
return snd_soc_dapm_add_routes ( dapm , audio_mapb ,
ARRAY_SIZE ( audio_mapb ) ) ;
}
2010-05-05 12:14:22 +04:00
/* Digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link rx51_dai [ ] = {
{
. name = " TLV320AIC34 " ,
. stream_name = " AIC34 " ,
2010-03-17 23:15:21 +03:00
. cpu_dai_name = " omap-mcbsp-dai.1 " ,
. codec_dai_name = " tlv320aic3x-hifi " ,
. platform_name = " omap-pcm-audio " ,
. codec_name = " tlv320aic3x-codec.2-0018 " ,
2010-05-05 12:14:22 +04:00
. init = rx51_aic34_init ,
. ops = & rx51_ops ,
} ,
} ;
2011-01-31 15:43:48 +03:00
struct snd_soc_aux_dev rx51_aux_dev [ ] = {
{
. name = " TLV320AIC34b " ,
. codec_name = " tlv320aic3x-codec.2-0019 " ,
. init = rx51_aic34b_init ,
} ,
} ;
static struct snd_soc_codec_conf rx51_codec_conf [ ] = {
{
. dev_name = " tlv320aic3x-codec.2-0019 " ,
. name_prefix = " b " ,
} ,
} ;
2010-05-05 12:14:22 +04:00
/* Audio card */
static struct snd_soc_card rx51_sound_card = {
. name = " RX-51 " ,
. dai_link = rx51_dai ,
. num_links = ARRAY_SIZE ( rx51_dai ) ,
2011-01-31 15:43:48 +03:00
. aux_dev = rx51_aux_dev ,
. num_aux_devs = ARRAY_SIZE ( rx51_aux_dev ) ,
. codec_conf = rx51_codec_conf ,
. num_configs = ARRAY_SIZE ( rx51_codec_conf ) ,
2010-05-05 12:14:22 +04:00
} ;
static struct platform_device * rx51_snd_device ;
static int __init rx51_soc_init ( void )
{
int err ;
if ( ! machine_is_nokia_rx51 ( ) )
return - ENODEV ;
2011-02-14 18:20:20 +03:00
err = gpio_request_one ( RX51_TVOUT_SEL_GPIO ,
GPIOF_DIR_OUT | GPIOF_INIT_LOW , " tvout_sel " ) ;
2010-06-21 15:14:59 +04:00
if ( err )
goto err_gpio_tvout_sel ;
2011-02-14 18:20:21 +03:00
err = gpio_request_one ( RX51_ECI_SW_GPIO ,
GPIOF_DIR_OUT | GPIOF_INIT_HIGH , " eci_sw " ) ;
if ( err )
goto err_gpio_eci_sw ;
2010-06-21 15:14:59 +04:00
2010-05-05 12:14:22 +04:00
rx51_snd_device = platform_device_alloc ( " soc-audio " , - 1 ) ;
if ( ! rx51_snd_device ) {
err = - ENOMEM ;
goto err1 ;
}
2010-03-17 23:15:21 +03:00
platform_set_drvdata ( rx51_snd_device , & rx51_sound_card ) ;
2010-05-05 12:14:22 +04:00
err = platform_device_add ( rx51_snd_device ) ;
if ( err )
goto err2 ;
return 0 ;
err2 :
platform_device_put ( rx51_snd_device ) ;
err1 :
2011-02-14 18:20:21 +03:00
gpio_free ( RX51_ECI_SW_GPIO ) ;
err_gpio_eci_sw :
2010-06-21 15:14:59 +04:00
gpio_free ( RX51_TVOUT_SEL_GPIO ) ;
err_gpio_tvout_sel :
2010-05-05 12:14:22 +04:00
return err ;
}
static void __exit rx51_soc_exit ( void )
{
2010-06-21 15:15:00 +04:00
snd_soc_jack_free_gpios ( & rx51_av_jack , ARRAY_SIZE ( rx51_av_jack_gpios ) ,
rx51_av_jack_gpios ) ;
2010-05-05 12:14:22 +04:00
platform_device_unregister ( rx51_snd_device ) ;
2011-02-14 18:20:21 +03:00
gpio_free ( RX51_ECI_SW_GPIO ) ;
2010-06-21 15:14:59 +04:00
gpio_free ( RX51_TVOUT_SEL_GPIO ) ;
2010-05-05 12:14:22 +04:00
}
module_init ( rx51_soc_init ) ;
module_exit ( rx51_soc_exit ) ;
MODULE_AUTHOR ( " Nokia Corporation " ) ;
MODULE_DESCRIPTION ( " ALSA SoC Nokia RX-51 " ) ;
MODULE_LICENSE ( " GPL " ) ;