2006-10-12 14:33:09 +02:00
/*
* tosa . c - - SoC audio for Tosa
*
* Copyright 2005 Wolfson Microelectronics PLC .
* Copyright 2005 Openedhand Ltd .
*
2008-10-12 13:17:36 +01:00
* Authors : Liam Girdwood < lrg @ slimlogic . co . uk >
2006-10-12 14:33:09 +02:00
* Richard Purdie < richard @ openedhand . 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 .
*
* GPIO ' s
* 1 - Jack Insertion
* 5 - Hookswitch ( headset answer / hang up switch )
*
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/device.h>
2008-06-14 11:42:57 +01:00
# include <linux/gpio.h>
2006-10-12 14:33:09 +02:00
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/soc.h>
# include <asm/mach-types.h>
2008-08-05 16:14:15 +01:00
# include <mach/tosa.h>
# include <mach/audio.h>
2006-10-12 14:33:09 +02:00
# include "../codecs/wm9712.h"
2007-02-02 17:23:11 +01:00
# include "pxa2xx-ac97.h"
2006-10-12 14:33:09 +02:00
# define TOSA_HP 0
# define TOSA_MIC_INT 1
# define TOSA_HEADSET 2
# define TOSA_HP_OFF 3
# define TOSA_SPK_ON 0
# define TOSA_SPK_OFF 1
static int tosa_jack_func ;
static int tosa_spk_func ;
static void tosa_ext_control ( struct snd_soc_codec * codec )
{
2010-11-05 15:53:46 +02:00
struct snd_soc_dapm_context * dapm = & codec - > dapm ;
2006-10-12 14:33:09 +02:00
/* set up jack connection */
switch ( tosa_jack_func ) {
case TOSA_HP :
2010-11-05 15:53:46 +02:00
snd_soc_dapm_disable_pin ( dapm , " Mic (Internal) " ) ;
snd_soc_dapm_enable_pin ( dapm , " Headphone Jack " ) ;
snd_soc_dapm_disable_pin ( dapm , " Headset Jack " ) ;
2006-10-12 14:33:09 +02:00
break ;
case TOSA_MIC_INT :
2010-11-05 15:53:46 +02:00
snd_soc_dapm_enable_pin ( dapm , " Mic (Internal) " ) ;
snd_soc_dapm_disable_pin ( dapm , " Headphone Jack " ) ;
snd_soc_dapm_disable_pin ( dapm , " Headset Jack " ) ;
2006-10-12 14:33:09 +02:00
break ;
case TOSA_HEADSET :
2010-11-05 15:53:46 +02:00
snd_soc_dapm_disable_pin ( dapm , " Mic (Internal) " ) ;
snd_soc_dapm_disable_pin ( dapm , " Headphone Jack " ) ;
snd_soc_dapm_enable_pin ( dapm , " Headset Jack " ) ;
2006-10-12 14:33:09 +02:00
break ;
}
if ( tosa_spk_func = = TOSA_SPK_ON )
2010-11-05 15:53:46 +02:00
snd_soc_dapm_enable_pin ( dapm , " Speaker " ) ;
2008-07-07 13:35:17 +01:00
else
2010-11-05 15:53:46 +02:00
snd_soc_dapm_disable_pin ( dapm , " Speaker " ) ;
2006-10-12 14:33:09 +02:00
2010-11-05 15:53:46 +02:00
snd_soc_dapm_sync ( dapm ) ;
2006-10-12 14:33:09 +02:00
}
static int tosa_startup ( struct snd_pcm_substream * substream )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2010-11-02 05:10:07 +08:00
struct snd_soc_codec * codec = rtd - > codec ;
2006-10-12 14:33:09 +02:00
2010-11-05 13:50:48 -04:00
mutex_lock ( & codec - > mutex ) ;
2006-10-12 14:33:09 +02:00
/* check the jack status at stream startup */
tosa_ext_control ( codec ) ;
2010-11-05 13:50:48 -04:00
mutex_unlock ( & codec - > mutex ) ;
2006-10-12 14:33:09 +02:00
return 0 ;
}
static struct snd_soc_ops tosa_ops = {
. startup = tosa_startup ,
} ;
static int tosa_get_jack ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
ucontrol - > value . integer . value [ 0 ] = tosa_jack_func ;
return 0 ;
}
static int tosa_set_jack ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
if ( tosa_jack_func = = ucontrol - > value . integer . value [ 0 ] )
return 0 ;
tosa_jack_func = ucontrol - > value . integer . value [ 0 ] ;
tosa_ext_control ( codec ) ;
return 1 ;
}
static int tosa_get_spk ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
ucontrol - > value . integer . value [ 0 ] = tosa_spk_func ;
return 0 ;
}
static int tosa_set_spk ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_codec * codec = snd_kcontrol_chip ( kcontrol ) ;
if ( tosa_spk_func = = ucontrol - > value . integer . value [ 0 ] )
return 0 ;
tosa_spk_func = ucontrol - > value . integer . value [ 0 ] ;
tosa_ext_control ( codec ) ;
return 1 ;
}
/* tosa dapm event handlers */
2008-02-28 12:34:48 +01:00
static int tosa_hp_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * k , int event )
2006-10-12 14:33:09 +02:00
{
2008-06-14 11:42:57 +01:00
gpio_set_value ( TOSA_GPIO_L_MUTE , SND_SOC_DAPM_EVENT_ON ( event ) ? 1 : 0 ) ;
2006-10-12 14:33:09 +02:00
return 0 ;
}
/* tosa machine dapm widgets */
static const struct snd_soc_dapm_widget tosa_dapm_widgets [ ] = {
SND_SOC_DAPM_HP ( " Headphone Jack " , tosa_hp_event ) ,
SND_SOC_DAPM_HP ( " Headset Jack " , NULL ) ,
SND_SOC_DAPM_MIC ( " Mic (Internal) " , NULL ) ,
SND_SOC_DAPM_SPK ( " Speaker " , NULL ) ,
} ;
/* tosa audio map */
2008-07-08 19:45:20 +04:00
static const struct snd_soc_dapm_route audio_map [ ] = {
2006-10-12 14:33:09 +02:00
/* headphone connected to HPOUTL, HPOUTR */
{ " Headphone Jack " , NULL , " HPOUTL " } ,
{ " Headphone Jack " , NULL , " HPOUTR " } ,
/* ext speaker connected to LOUT2, ROUT2 */
{ " Speaker " , NULL , " LOUT2 " } ,
{ " Speaker " , NULL , " ROUT2 " } ,
/* internal mic is connected to mic1, mic2 differential - with bias */
{ " MIC1 " , NULL , " Mic Bias " } ,
{ " MIC2 " , NULL , " Mic Bias " } ,
{ " Mic Bias " , NULL , " Mic (Internal) " } ,
/* headset is connected to HPOUTR, and LINEINR with bias */
{ " Headset Jack " , NULL , " HPOUTR " } ,
{ " LINEINR " , NULL , " Mic Bias " } ,
{ " Mic Bias " , NULL , " Headset Jack " } ,
} ;
static const char * jack_function [ ] = { " Headphone " , " Mic " , " Line " , " Headset " ,
" Off " } ;
static const char * spk_function [ ] = { " On " , " Off " } ;
static const struct soc_enum tosa_enum [ ] = {
SOC_ENUM_SINGLE_EXT ( 5 , jack_function ) ,
SOC_ENUM_SINGLE_EXT ( 2 , spk_function ) ,
} ;
static const struct snd_kcontrol_new tosa_controls [ ] = {
SOC_ENUM_EXT ( " Jack Function " , tosa_enum [ 0 ] , tosa_get_jack ,
tosa_set_jack ) ,
SOC_ENUM_EXT ( " Speaker Function " , tosa_enum [ 1 ] , tosa_get_spk ,
tosa_set_spk ) ,
} ;
2010-03-17 20:15:21 +00:00
static int tosa_ac97_init ( struct snd_soc_pcm_runtime * rtd )
2006-10-12 14:33:09 +02:00
{
2010-03-17 20:15:21 +00:00
struct snd_soc_codec * codec = rtd - > codec ;
2010-11-05 15:53:46 +02:00
struct snd_soc_dapm_context * dapm = & codec - > dapm ;
2009-03-12 11:07:54 +01:00
int err ;
2006-10-12 14:33:09 +02:00
2010-11-05 15:53:46 +02:00
snd_soc_dapm_nc_pin ( dapm , " OUT3 " ) ;
snd_soc_dapm_nc_pin ( dapm , " MONOOUT " ) ;
2006-10-12 14:33:09 +02:00
/* add tosa specific controls */
2012-02-03 17:43:09 +00:00
err = snd_soc_add_codec_controls ( codec , tosa_controls ,
2009-03-12 11:07:54 +01:00
ARRAY_SIZE ( tosa_controls ) ) ;
if ( err < 0 )
return err ;
2006-10-12 14:33:09 +02:00
/* add tosa specific widgets */
2010-11-05 15:53:46 +02:00
snd_soc_dapm_new_controls ( dapm , tosa_dapm_widgets ,
2008-05-13 14:55:48 +02:00
ARRAY_SIZE ( tosa_dapm_widgets ) ) ;
2006-10-12 14:33:09 +02:00
/* set up tosa specific audio path audio_map */
2010-11-05 15:53:46 +02:00
snd_soc_dapm_add_routes ( dapm , audio_map , ARRAY_SIZE ( audio_map ) ) ;
2006-10-12 14:33:09 +02:00
return 0 ;
}
static struct snd_soc_dai_link tosa_dai [ ] = {
{
. name = " AC97 " ,
. stream_name = " AC97 HiFi " ,
2011-02-23 02:29:11 +03:00
. cpu_dai_name = " pxa2xx-ac97 " ,
2010-03-17 20:15:21 +00:00
. codec_dai_name = " wm9712-hifi " ,
. platform_name = " pxa-pcm-audio " ,
. codec_name = " wm9712-codec " ,
2006-10-12 14:33:09 +02:00
. init = tosa_ac97_init ,
2007-02-02 17:23:11 +01:00
. ops = & tosa_ops ,
2006-10-12 14:33:09 +02:00
} ,
{
. name = " AC97 Aux " ,
. stream_name = " AC97 Aux " ,
2011-02-23 02:29:11 +03:00
. cpu_dai_name = " pxa2xx-ac97-aux " ,
2010-03-17 20:15:21 +00:00
. codec_dai_name = " wm9712-aux " ,
. platform_name = " pxa-pcm-audio " ,
. codec_name = " wm9712-codec " ,
2007-02-02 17:23:11 +01:00
. ops = & tosa_ops ,
2006-10-12 14:33:09 +02:00
} ,
} ;
2008-11-18 20:50:34 +00:00
static struct snd_soc_card tosa = {
2006-10-12 14:33:09 +02:00
. name = " Tosa " ,
2011-12-22 09:44:43 +08:00
. owner = THIS_MODULE ,
2006-10-12 14:33:09 +02:00
. dai_link = tosa_dai ,
. num_links = ARRAY_SIZE ( tosa_dai ) ,
} ;
2012-12-07 09:26:17 -05:00
static int tosa_probe ( struct platform_device * pdev )
2006-10-12 14:33:09 +02:00
{
2011-12-15 10:57:22 +08:00
struct snd_soc_card * card = & tosa ;
2006-10-12 14:33:09 +02:00
int ret ;
2011-12-15 10:57:22 +08:00
ret = gpio_request_one ( TOSA_GPIO_L_MUTE , GPIOF_OUT_INIT_LOW ,
" Headphone Jack " ) ;
if ( ret )
return ret ;
2008-06-14 11:42:57 +01:00
2011-12-15 10:57:22 +08:00
card - > dev = & pdev - > dev ;
2008-06-14 11:42:57 +01:00
2011-12-15 10:57:22 +08:00
ret = snd_soc_register_card ( card ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " snd_soc_register_card() failed: %d \n " ,
ret ) ;
gpio_free ( TOSA_GPIO_L_MUTE ) ;
}
2006-10-12 14:33:09 +02:00
return ret ;
}
2012-12-07 09:26:17 -05:00
static int tosa_remove ( struct platform_device * pdev )
2006-10-12 14:33:09 +02:00
{
2011-12-15 10:57:22 +08:00
struct snd_soc_card * card = platform_get_drvdata ( pdev ) ;
gpio_free ( TOSA_GPIO_L_MUTE ) ;
snd_soc_unregister_card ( card ) ;
return 0 ;
2006-10-12 14:33:09 +02:00
}
2011-12-15 10:57:22 +08:00
static struct platform_driver tosa_driver = {
. driver = {
. name = " tosa-audio " ,
. owner = THIS_MODULE ,
} ,
. probe = tosa_probe ,
2012-12-07 09:26:17 -05:00
. remove = tosa_remove ,
2011-12-15 10:57:22 +08:00
} ;
module_platform_driver ( tosa_driver ) ;
2006-10-12 14:33:09 +02:00
/* Module information */
MODULE_AUTHOR ( " Richard Purdie " ) ;
MODULE_DESCRIPTION ( " ALSA SoC Tosa " ) ;
MODULE_LICENSE ( " GPL " ) ;
2011-12-15 10:57:22 +08:00
MODULE_ALIAS ( " platform:tosa-audio " ) ;