2011-12-19 23:51:52 +04:00
/*
2012-05-23 02:09:51 +04:00
* tegra_alc5632 . c - - Toshiba AC100 ( PAZ00 ) machine ASoC driver
2012-03-21 00:55:48 +04:00
*
* Copyright ( C ) 2011 The AC100 Kernel Team < ac100 @ lists . lauchpad . net >
2012-03-21 00:55:49 +04:00
* Copyright ( C ) 2012 - NVIDIA , Inc .
2012-03-21 00:55:48 +04:00
*
* Authors : Leon Romanovsky < leon @ leon . nu >
* Andrey Danin < danindrey @ mail . ru >
* Marc Dietrich < marvin24 @ gmx . de >
*
* 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 .
*/
2011-12-19 23:51:52 +04:00
# include <asm/mach-types.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <linux/gpio.h>
2012-02-03 00:13:37 +04:00
# include <linux/of_gpio.h>
2011-12-19 23:51:52 +04:00
# include <sound/core.h>
# include <sound/jack.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
# include "../codecs/alc5632.h"
# include "tegra_asoc_utils.h"
# define DRV_NAME "tegra-alc5632"
2012-02-03 00:13:37 +04:00
# define GPIO_HP_DET BIT(0)
2011-12-19 23:51:52 +04:00
struct tegra_alc5632 {
struct tegra_asoc_utils_data util_data ;
2012-02-03 00:13:37 +04:00
int gpio_requested ;
int gpio_hp_det ;
2011-12-19 23:51:52 +04:00
} ;
static int tegra_alc5632_asoc_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
struct snd_soc_dai * codec_dai = rtd - > codec_dai ;
struct snd_soc_codec * codec = rtd - > codec ;
struct snd_soc_card * card = codec - > card ;
struct tegra_alc5632 * alc5632 = snd_soc_card_get_drvdata ( card ) ;
int srate , mclk ;
int err ;
srate = params_rate ( params ) ;
mclk = 512 * srate ;
err = tegra_asoc_utils_set_rate ( & alc5632 - > util_data , srate , mclk ) ;
if ( err < 0 ) {
dev_err ( card - > dev , " Can't configure clocks \n " ) ;
return err ;
}
err = snd_soc_dai_set_sysclk ( codec_dai , 0 , mclk ,
SND_SOC_CLOCK_IN ) ;
if ( err < 0 ) {
dev_err ( card - > dev , " codec_dai clock not set \n " ) ;
return err ;
}
return 0 ;
}
static struct snd_soc_ops tegra_alc5632_asoc_ops = {
. hw_params = tegra_alc5632_asoc_hw_params ,
} ;
static struct snd_soc_jack tegra_alc5632_hs_jack ;
static struct snd_soc_jack_pin tegra_alc5632_hs_jack_pins [ ] = {
{
. pin = " Headset Mic " ,
. mask = SND_JACK_MICROPHONE ,
} ,
{
. pin = " Headset Stereophone " ,
. mask = SND_JACK_HEADPHONE ,
} ,
} ;
2012-02-03 00:13:37 +04:00
static struct snd_soc_jack_gpio tegra_alc5632_hp_jack_gpio = {
. name = " Headset detection " ,
. report = SND_JACK_HEADSET ,
. debounce_time = 150 ,
. invert = 1 ,
} ;
2011-12-19 23:51:52 +04:00
static const struct snd_soc_dapm_widget tegra_alc5632_dapm_widgets [ ] = {
SND_SOC_DAPM_SPK ( " Int Spk " , NULL ) ,
SND_SOC_DAPM_HP ( " Headset Stereophone " , NULL ) ,
SND_SOC_DAPM_MIC ( " Headset Mic " , NULL ) ,
2012-02-13 23:27:36 +04:00
SND_SOC_DAPM_MIC ( " Digital Mic " , NULL ) ,
2011-12-19 23:51:52 +04:00
} ;
static const struct snd_kcontrol_new tegra_alc5632_controls [ ] = {
SOC_DAPM_PIN_SWITCH ( " Int Spk " ) ,
} ;
static int tegra_alc5632_asoc_init ( struct snd_soc_pcm_runtime * rtd )
{
struct snd_soc_codec * codec = rtd - > codec ;
struct snd_soc_dapm_context * dapm = & codec - > dapm ;
2012-02-03 00:13:37 +04:00
struct tegra_alc5632 * machine = snd_soc_card_get_drvdata ( codec - > card ) ;
2011-12-19 23:51:52 +04:00
snd_soc_jack_new ( codec , " Headset Jack " , SND_JACK_HEADSET ,
& tegra_alc5632_hs_jack ) ;
snd_soc_jack_add_pins ( & tegra_alc5632_hs_jack ,
ARRAY_SIZE ( tegra_alc5632_hs_jack_pins ) ,
tegra_alc5632_hs_jack_pins ) ;
2012-02-03 00:13:37 +04:00
if ( gpio_is_valid ( machine - > gpio_hp_det ) ) {
tegra_alc5632_hp_jack_gpio . gpio = machine - > gpio_hp_det ;
snd_soc_jack_add_gpios ( & tegra_alc5632_hs_jack ,
1 ,
& tegra_alc5632_hp_jack_gpio ) ;
machine - > gpio_requested | = GPIO_HP_DET ;
}
2011-12-19 23:51:52 +04:00
snd_soc_dapm_force_enable_pin ( dapm , " MICBIAS1 " ) ;
return 0 ;
}
static struct snd_soc_dai_link tegra_alc5632_dai = {
. name = " ALC5632 " ,
. stream_name = " ALC5632 PCM " ,
. codec_dai_name = " alc5632-hifi " ,
. init = tegra_alc5632_asoc_init ,
. ops = & tegra_alc5632_asoc_ops ,
. dai_fmt = SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS ,
} ;
static struct snd_soc_card snd_soc_tegra_alc5632 = {
. name = " tegra-alc5632 " ,
2011-12-22 17:23:01 +04:00
. owner = THIS_MODULE ,
2011-12-19 23:51:52 +04:00
. dai_link = & tegra_alc5632_dai ,
. num_links = 1 ,
. controls = tegra_alc5632_controls ,
. num_controls = ARRAY_SIZE ( tegra_alc5632_controls ) ,
. dapm_widgets = tegra_alc5632_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( tegra_alc5632_dapm_widgets ) ,
. fully_routed = true ,
} ;
static __devinit int tegra_alc5632_probe ( struct platform_device * pdev )
{
2012-05-23 02:09:51 +04:00
struct device_node * np = pdev - > dev . of_node ;
2011-12-19 23:51:52 +04:00
struct snd_soc_card * card = & snd_soc_tegra_alc5632 ;
struct tegra_alc5632 * alc5632 ;
int ret ;
alc5632 = devm_kzalloc ( & pdev - > dev ,
sizeof ( struct tegra_alc5632 ) , GFP_KERNEL ) ;
if ( ! alc5632 ) {
dev_err ( & pdev - > dev , " Can't allocate tegra_alc5632 \n " ) ;
2012-01-31 11:26:59 +04:00
ret = - ENOMEM ;
goto err ;
2011-12-19 23:51:52 +04:00
}
card - > dev = & pdev - > dev ;
platform_set_drvdata ( pdev , card ) ;
snd_soc_card_set_drvdata ( card , alc5632 ) ;
2012-01-31 11:26:59 +04:00
if ( ! ( pdev - > dev . of_node ) ) {
dev_err ( & pdev - > dev , " Must be instantiated using device tree \n " ) ;
ret = - EINVAL ;
goto err ;
}
2012-05-23 02:09:51 +04:00
alc5632 - > gpio_hp_det = of_get_named_gpio ( np , " nvidia,hp-det-gpios " , 0 ) ;
if ( alc5632 - > gpio_hp_det = = - ENODEV )
return - EPROBE_DEFER ;
2012-01-31 11:26:59 +04:00
ret = snd_soc_of_parse_card_name ( card , " nvidia,model " ) ;
if ( ret )
goto err ;
ret = snd_soc_of_parse_audio_routing ( card , " nvidia,audio-routing " ) ;
if ( ret )
goto err ;
tegra_alc5632_dai . codec_of_node = of_parse_phandle (
pdev - > dev . of_node , " nvidia,audio-codec " , 0 ) ;
if ( ! tegra_alc5632_dai . codec_of_node ) {
dev_err ( & pdev - > dev ,
" Property 'nvidia,audio-codec' missing or invalid \n " ) ;
ret = - EINVAL ;
goto err ;
}
tegra_alc5632_dai . cpu_dai_of_node = of_parse_phandle (
pdev - > dev . of_node , " nvidia,i2s-controller " , 0 ) ;
if ( ! tegra_alc5632_dai . cpu_dai_of_node ) {
dev_err ( & pdev - > dev ,
" Property 'nvidia,i2s-controller' missing or invalid \n " ) ;
ret = - EINVAL ;
goto err ;
}
2012-03-21 00:55:49 +04:00
tegra_alc5632_dai . platform_of_node = tegra_alc5632_dai . cpu_dai_of_node ;
2012-01-31 11:26:59 +04:00
ret = tegra_asoc_utils_init ( & alc5632 - > util_data , & pdev - > dev ) ;
if ( ret )
2012-03-21 00:55:49 +04:00
goto err ;
2012-01-31 11:26:59 +04:00
2011-12-19 23:51:52 +04:00
ret = snd_soc_register_card ( card ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " snd_soc_register_card failed (%d) \n " ,
ret ) ;
2012-01-31 11:26:59 +04:00
goto err_fini_utils ;
2011-12-19 23:51:52 +04:00
}
return 0 ;
2012-01-31 11:26:59 +04:00
err_fini_utils :
tegra_asoc_utils_fini ( & alc5632 - > util_data ) ;
err :
return ret ;
2011-12-19 23:51:52 +04:00
}
static int __devexit tegra_alc5632_remove ( struct platform_device * pdev )
{
struct snd_soc_card * card = platform_get_drvdata ( pdev ) ;
2012-02-03 00:13:37 +04:00
struct tegra_alc5632 * machine = snd_soc_card_get_drvdata ( card ) ;
if ( machine - > gpio_requested & GPIO_HP_DET )
snd_soc_jack_free_gpios ( & tegra_alc5632_hs_jack ,
1 ,
& tegra_alc5632_hp_jack_gpio ) ;
machine - > gpio_requested = 0 ;
2011-12-19 23:51:52 +04:00
snd_soc_unregister_card ( card ) ;
2012-02-03 00:13:37 +04:00
tegra_asoc_utils_fini ( & machine - > util_data ) ;
2011-12-19 23:51:52 +04:00
return 0 ;
}
2012-01-31 11:26:59 +04:00
static const struct of_device_id tegra_alc5632_of_match [ ] __devinitconst = {
{ . compatible = " nvidia,tegra-audio-alc5632 " , } ,
{ } ,
} ;
2011-12-19 23:51:52 +04:00
static struct platform_driver tegra_alc5632_driver = {
. driver = {
. name = DRV_NAME ,
. owner = THIS_MODULE ,
. pm = & snd_soc_pm_ops ,
2012-01-31 11:26:59 +04:00
. of_match_table = tegra_alc5632_of_match ,
2011-12-19 23:51:52 +04:00
} ,
. probe = tegra_alc5632_probe ,
. remove = __devexit_p ( tegra_alc5632_remove ) ,
} ;
module_platform_driver ( tegra_alc5632_driver ) ;
MODULE_AUTHOR ( " Leon Romanovsky <leon@leon.nu> " ) ;
MODULE_DESCRIPTION ( " Tegra+ALC5632 machine ASoC driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform: " DRV_NAME ) ;
2012-01-31 11:26:59 +04:00
MODULE_DEVICE_TABLE ( of , tegra_alc5632_of_match ) ;