2010-05-17 19:53:10 -05:00
/*
2011-12-07 15:54:50 +02:00
* omap - abe - twl6040 . c - - SoC audio for TI OMAP based boards with ABE and
* twl6040 codec
2010-05-17 19:53:10 -05:00
*
2011-12-07 15:31:26 +02:00
* Author : Misael Lopez Cruz < misael . lopez @ ti . com >
2010-05-17 19:53:10 -05:00
*
* 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/clk.h>
# include <linux/platform_device.h>
2011-05-01 21:27:00 -05:00
# include <linux/mfd/twl6040.h>
2011-07-15 12:38:28 -04:00
# include <linux/module.h>
2012-07-25 16:09:11 +03:00
# include <linux/of.h>
2011-05-01 21:27:00 -05:00
2010-05-17 19:53:10 -05:00
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/soc.h>
2010-12-10 20:45:19 -06:00
# include <sound/jack.h>
2010-05-17 19:53:10 -05:00
2011-11-28 15:45:43 +02:00
# include "omap-dmic.h"
2011-07-05 19:50:45 +03:00
# include "omap-mcpdm.h"
2010-05-17 19:53:10 -05:00
# include "../codecs/twl6040.h"
2012-04-24 12:52:53 +03:00
struct abe_twl6040 {
int jack_detection ; /* board can detect jack events */
int mclk_freq ; /* MCLK frequency speed for twl6040 */
} ;
2016-08-19 09:34:24 +03:00
struct platform_device * dmic_codec_dev ;
2011-12-07 15:54:50 +02:00
static int omap_abe_hw_params ( struct snd_pcm_substream * substream ,
2010-05-17 19:53:10 -05:00
struct snd_pcm_hw_params * params )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2010-03-17 20:15:21 +00:00
struct snd_soc_dai * codec_dai = rtd - > codec_dai ;
2014-05-19 11:41:46 +02:00
struct snd_soc_card * card = rtd - > card ;
2012-04-24 12:52:53 +03:00
struct abe_twl6040 * priv = snd_soc_card_get_drvdata ( card ) ;
2010-05-17 19:53:10 -05:00
int clk_id , freq ;
int ret ;
2018-01-29 04:20:09 +00:00
clk_id = twl6040_get_clk_id ( codec_dai - > component ) ;
2011-06-27 17:03:14 +03:00
if ( clk_id = = TWL6040_SYSCLK_SEL_HPPLL )
2012-04-24 12:52:53 +03:00
freq = priv - > mclk_freq ;
2011-06-27 17:03:14 +03:00
else if ( clk_id = = TWL6040_SYSCLK_SEL_LPPLL )
2010-05-17 19:53:10 -05:00
freq = 32768 ;
2011-06-27 17:03:14 +03:00
else
return - EINVAL ;
2010-05-17 19:53:10 -05:00
/* set the codec mclk */
ret = snd_soc_dai_set_sysclk ( codec_dai , clk_id , freq ,
SND_SOC_CLOCK_IN ) ;
if ( ret ) {
printk ( KERN_ERR " can't set codec system clock \n " ) ;
return ret ;
}
2010-03-17 20:15:21 +00:00
return ret ;
2010-05-17 19:53:10 -05:00
}
2017-03-18 19:34:11 +05:30
static const struct snd_soc_ops omap_abe_ops = {
2011-12-07 15:54:50 +02:00
. hw_params = omap_abe_hw_params ,
2010-05-17 19:53:10 -05:00
} ;
2011-12-07 15:54:50 +02:00
static int omap_abe_dmic_hw_params ( struct snd_pcm_substream * substream ,
2011-11-28 15:45:43 +02:00
struct snd_pcm_hw_params * params )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
struct snd_soc_dai * cpu_dai = rtd - > cpu_dai ;
int ret = 0 ;
ret = snd_soc_dai_set_sysclk ( cpu_dai , OMAP_DMIC_SYSCLK_PAD_CLKS ,
19200000 , SND_SOC_CLOCK_IN ) ;
if ( ret < 0 ) {
printk ( KERN_ERR " can't set DMIC cpu system clock \n " ) ;
return ret ;
}
ret = snd_soc_dai_set_sysclk ( cpu_dai , OMAP_DMIC_ABE_DMIC_CLK , 2400000 ,
SND_SOC_CLOCK_OUT ) ;
if ( ret < 0 ) {
printk ( KERN_ERR " can't set DMIC output clock \n " ) ;
return ret ;
}
return 0 ;
}
2011-12-07 15:54:50 +02:00
static struct snd_soc_ops omap_abe_dmic_ops = {
. hw_params = omap_abe_dmic_hw_params ,
2011-11-28 15:45:43 +02:00
} ;
2010-12-10 20:45:19 -06:00
/* Headset jack */
static struct snd_soc_jack hs_jack ;
/*Headset jack detection DAPM pins */
static struct snd_soc_jack_pin hs_jack_pins [ ] = {
{
. pin = " Headset Mic " ,
. mask = SND_JACK_MICROPHONE ,
} ,
{
. pin = " Headset Stereophone " ,
. mask = SND_JACK_HEADPHONE ,
} ,
} ;
2010-05-17 19:53:10 -05:00
/* SDP4430 machine DAPM */
2011-12-07 15:54:50 +02:00
static const struct snd_soc_dapm_widget twl6040_dapm_widgets [ ] = {
2011-12-22 12:55:49 +02:00
/* Outputs */
2010-05-17 19:53:10 -05:00
SND_SOC_DAPM_HP ( " Headset Stereophone " , NULL ) ,
2010-05-18 12:44:17 -05:00
SND_SOC_DAPM_SPK ( " Earphone Spk " , NULL ) ,
2011-12-22 12:55:49 +02:00
SND_SOC_DAPM_SPK ( " Ext Spk " , NULL ) ,
SND_SOC_DAPM_LINE ( " Line Out " , NULL ) ,
SND_SOC_DAPM_SPK ( " Vibrator " , NULL ) ,
/* Inputs */
SND_SOC_DAPM_MIC ( " Headset Mic " , NULL ) ,
SND_SOC_DAPM_MIC ( " Main Handset Mic " , NULL ) ,
SND_SOC_DAPM_MIC ( " Sub Handset Mic " , NULL ) ,
SND_SOC_DAPM_LINE ( " Line In " , NULL ) ,
2012-04-24 12:41:32 +03:00
/* Digital microphones */
SND_SOC_DAPM_MIC ( " Digital Mic " , NULL ) ,
2010-05-17 19:53:10 -05:00
} ;
static const struct snd_soc_dapm_route audio_map [ ] = {
2011-12-22 12:55:49 +02:00
/* Routings for outputs */
{ " Headset Stereophone " , NULL , " HSOL " } ,
{ " Headset Stereophone " , NULL , " HSOR " } ,
{ " Earphone Spk " , NULL , " EP " } ,
2010-05-17 19:53:10 -05:00
{ " Ext Spk " , NULL , " HFL " } ,
{ " Ext Spk " , NULL , " HFR " } ,
2011-12-22 12:55:49 +02:00
{ " Line Out " , NULL , " AUXL " } ,
{ " Line Out " , NULL , " AUXR " } ,
{ " Vibrator " , NULL , " VIBRAL " } ,
{ " Vibrator " , NULL , " VIBRAR " } ,
/* Routings for inputs */
2011-12-22 12:47:21 +02:00
{ " HSMIC " , NULL , " Headset Mic " } ,
{ " Headset Mic " , NULL , " Headset Mic Bias " } ,
2010-05-17 19:53:10 -05:00
2011-12-22 12:55:49 +02:00
{ " MAINMIC " , NULL , " Main Handset Mic " } ,
{ " Main Handset Mic " , NULL , " Main Mic Bias " } ,
2010-05-18 12:44:17 -05:00
2011-12-22 12:55:49 +02:00
{ " SUBMIC " , NULL , " Sub Handset Mic " } ,
{ " Sub Handset Mic " , NULL , " Main Mic Bias " } ,
2010-12-08 10:55:05 -06:00
2011-12-22 12:55:49 +02:00
{ " AFML " , NULL , " Line In " } ,
{ " AFMR " , NULL , " Line In " } ,
2010-05-17 19:53:10 -05:00
} ;
2011-12-07 15:54:50 +02:00
static int omap_abe_twl6040_init ( struct snd_soc_pcm_runtime * rtd )
2010-05-17 19:53:10 -05:00
{
2018-01-29 04:20:09 +00:00
struct snd_soc_component * component = rtd - > codec_dai - > component ;
2014-05-19 11:41:46 +02:00
struct snd_soc_card * card = rtd - > card ;
2012-04-24 12:52:53 +03:00
struct abe_twl6040 * priv = snd_soc_card_get_drvdata ( card ) ;
2011-12-22 13:58:01 +02:00
int hs_trim ;
int ret = 0 ;
2011-09-26 16:05:59 +03:00
/*
* Configure McPDM offset cancellation based on the HSOTRIM value from
* twl6040 .
*/
2018-01-29 04:20:09 +00:00
hs_trim = twl6040_get_trim_value ( component , TWL6040_TRIM_HSOTRIM ) ;
2011-09-26 16:05:59 +03:00
omap_mcpdm_configure_dn_offsets ( rtd , TWL6040_HSF_TRIM_LEFT ( hs_trim ) ,
TWL6040_HSF_TRIM_RIGHT ( hs_trim ) ) ;
2011-12-22 13:58:01 +02:00
/* Headset jack detection only if it is supported */
2012-04-24 12:52:53 +03:00
if ( priv - > jack_detection ) {
2015-03-04 10:33:26 +01:00
ret = snd_soc_card_jack_new ( rtd - > card , " Headset Jack " ,
SND_JACK_HEADSET , & hs_jack ,
hs_jack_pins ,
ARRAY_SIZE ( hs_jack_pins ) ) ;
2011-12-22 13:58:01 +02:00
if ( ret )
return ret ;
2010-12-10 20:45:19 -06:00
2018-01-29 04:20:09 +00:00
twl6040_hs_jack_detect ( component , & hs_jack , SND_JACK_HEADSET ) ;
2011-12-22 13:58:01 +02:00
}
2010-05-17 19:53:10 -05:00
2015-03-04 10:33:26 +01:00
return 0 ;
2010-05-17 19:53:10 -05:00
}
2011-11-28 15:45:43 +02:00
static const struct snd_soc_dapm_route dmic_audio_map [ ] = {
2011-12-22 12:47:21 +02:00
{ " DMic " , NULL , " Digital Mic " } ,
{ " Digital Mic " , NULL , " Digital Mic1 Bias " } ,
2011-11-28 15:45:43 +02:00
} ;
2011-12-07 15:54:50 +02:00
static int omap_abe_dmic_init ( struct snd_soc_pcm_runtime * rtd )
2011-11-28 15:45:43 +02:00
{
2014-03-12 15:27:32 +01:00
struct snd_soc_dapm_context * dapm = & rtd - > card - > dapm ;
2011-11-28 15:45:43 +02:00
return snd_soc_dapm_add_routes ( dapm , dmic_audio_map ,
ARRAY_SIZE ( dmic_audio_map ) ) ;
}
2010-05-17 19:53:10 -05:00
/* Digital audio interface glue - connects codec <--> CPU */
2012-04-24 12:36:25 +03:00
static struct snd_soc_dai_link abe_twl6040_dai_links [ ] = {
2011-11-28 15:45:43 +02:00
{
. name = " TWL6040 " ,
. stream_name = " TWL6040 " ,
. codec_dai_name = " twl6040-legacy " ,
. codec_name = " twl6040-codec " ,
2011-12-07 15:54:50 +02:00
. init = omap_abe_twl6040_init ,
. ops = & omap_abe_ops ,
2011-11-28 15:45:43 +02:00
} ,
{
. name = " DMIC " ,
. stream_name = " DMIC Capture " ,
. codec_dai_name = " dmic-hifi " ,
. codec_name = " dmic-codec " ,
2011-12-07 15:54:50 +02:00
. init = omap_abe_dmic_init ,
. ops = & omap_abe_dmic_ops ,
2011-11-28 15:45:43 +02:00
} ,
2010-05-17 19:53:10 -05:00
} ;
/* Audio machine driver */
2011-12-07 15:54:50 +02:00
static struct snd_soc_card omap_abe_card = {
2011-12-22 11:08:59 +08:00
. owner = THIS_MODULE ,
2011-10-10 15:34:15 +03:00
2011-12-07 15:54:50 +02:00
. dapm_widgets = twl6040_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( twl6040_dapm_widgets ) ,
2011-10-10 15:34:15 +03:00
. dapm_routes = audio_map ,
. num_dapm_routes = ARRAY_SIZE ( audio_map ) ,
2010-05-17 19:53:10 -05:00
} ;
2012-12-07 09:26:29 -05:00
static int omap_abe_probe ( struct platform_device * pdev )
2010-05-17 19:53:10 -05:00
{
2012-07-25 16:09:11 +03:00
struct device_node * node = pdev - > dev . of_node ;
2011-12-08 11:07:42 +02:00
struct snd_soc_card * card = & omap_abe_card ;
2013-07-31 19:00:32 +02:00
struct device_node * dai_node ;
2012-04-24 12:52:53 +03:00
struct abe_twl6040 * priv ;
2012-04-24 12:36:25 +03:00
int num_links = 0 ;
2012-07-25 16:09:11 +03:00
int ret = 0 ;
2010-05-17 19:53:10 -05:00
2013-07-31 19:00:32 +02:00
if ( ! node ) {
dev_err ( & pdev - > dev , " of node is missing. \n " ) ;
return - ENODEV ;
}
2011-12-08 11:07:42 +02:00
card - > dev = & pdev - > dev ;
2010-05-17 19:53:10 -05:00
2012-04-24 12:52:53 +03:00
priv = devm_kzalloc ( & pdev - > dev , sizeof ( struct abe_twl6040 ) , GFP_KERNEL ) ;
if ( priv = = NULL )
return - ENOMEM ;
2013-07-31 19:00:32 +02:00
if ( snd_soc_of_parse_card_name ( card , " ti,model " ) ) {
dev_err ( & pdev - > dev , " Card name is not provided \n " ) ;
return - ENODEV ;
}
2012-07-25 16:09:11 +03:00
2013-07-31 19:00:32 +02:00
ret = snd_soc_of_parse_audio_routing ( card , " ti,audio-routing " ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Error while parsing DAPM routing \n " ) ;
return ret ;
}
2012-07-25 16:09:11 +03:00
2013-07-31 19:00:32 +02:00
dai_node = of_parse_phandle ( node , " ti,mcpdm " , 0 ) ;
if ( ! dai_node ) {
dev_err ( & pdev - > dev , " McPDM node is not provided \n " ) ;
return - EINVAL ;
}
abe_twl6040_dai_links [ 0 ] . cpu_of_node = dai_node ;
2014-04-16 15:46:21 +03:00
abe_twl6040_dai_links [ 0 ] . platform_of_node = dai_node ;
2012-07-25 16:09:11 +03:00
2013-07-31 19:00:32 +02:00
dai_node = of_parse_phandle ( node , " ti,dmic " , 0 ) ;
if ( dai_node ) {
num_links = 2 ;
abe_twl6040_dai_links [ 1 ] . cpu_of_node = dai_node ;
2014-04-16 15:46:21 +03:00
abe_twl6040_dai_links [ 1 ] . platform_of_node = dai_node ;
2011-12-08 11:07:42 +02:00
} else {
2013-07-31 19:00:32 +02:00
num_links = 1 ;
}
priv - > jack_detection = of_property_read_bool ( node , " ti,jack-detection " ) ;
of_property_read_u32 ( node , " ti,mclk-freq " , & priv - > mclk_freq ) ;
if ( ! priv - > mclk_freq ) {
dev_err ( & pdev - > dev , " MCLK frequency not provided \n " ) ;
2016-08-19 09:34:24 +03:00
return - EINVAL ;
2011-12-08 11:07:42 +02:00
}
2010-05-17 19:53:10 -05:00
2013-07-31 19:00:32 +02:00
card - > fully_routed = 1 ;
2012-04-24 12:52:53 +03:00
if ( ! priv - > mclk_freq ) {
2012-01-24 12:36:10 +02:00
dev_err ( & pdev - > dev , " MCLK frequency missing \n " ) ;
2016-08-19 09:34:24 +03:00
return - ENODEV ;
2012-01-24 12:36:10 +02:00
}
2012-04-24 12:36:25 +03:00
card - > dai_link = abe_twl6040_dai_links ;
card - > num_links = num_links ;
2011-12-22 13:10:02 +02:00
2012-04-24 12:52:53 +03:00
snd_soc_card_set_drvdata ( card , priv ) ;
2016-08-23 23:10:17 +00:00
ret = devm_snd_soc_register_card ( & pdev - > dev , card ) ;
2016-08-19 09:34:24 +03:00
if ( ret )
2016-08-23 23:10:17 +00:00
dev_err ( & pdev - > dev , " devm_snd_soc_register_card() failed: %d \n " ,
2011-12-08 11:07:42 +02:00
ret ) ;
2010-05-17 19:53:10 -05:00
return ret ;
}
2012-07-25 16:09:11 +03:00
static const struct of_device_id omap_abe_of_match [ ] = {
{ . compatible = " ti,abe-twl6040 " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , omap_abe_of_match ) ;
2011-12-08 11:07:42 +02:00
static struct platform_driver omap_abe_driver = {
. driver = {
. name = " omap-abe-twl6040 " ,
. pm = & snd_soc_pm_ops ,
2012-07-25 16:09:11 +03:00
. of_match_table = omap_abe_of_match ,
2011-12-08 11:07:42 +02:00
} ,
. probe = omap_abe_probe ,
} ;
2016-08-19 09:34:24 +03:00
static int __init omap_abe_init ( void )
{
int ret ;
dmic_codec_dev = platform_device_register_simple ( " dmic-codec " , - 1 , NULL ,
0 ) ;
if ( IS_ERR ( dmic_codec_dev ) ) {
pr_err ( " %s: dmic-codec device registration failed \n " , __func__ ) ;
return PTR_ERR ( dmic_codec_dev ) ;
}
ret = platform_driver_register ( & omap_abe_driver ) ;
if ( ret ) {
pr_err ( " %s: platform driver registration failed \n " , __func__ ) ;
platform_device_unregister ( dmic_codec_dev ) ;
}
return ret ;
}
module_init ( omap_abe_init ) ;
static void __exit omap_abe_exit ( void )
{
platform_driver_unregister ( & omap_abe_driver ) ;
platform_device_unregister ( dmic_codec_dev ) ;
}
module_exit ( omap_abe_exit ) ;
2010-05-17 19:53:10 -05:00
2011-12-07 15:31:26 +02:00
MODULE_AUTHOR ( " Misael Lopez Cruz <misael.lopez@ti.com> " ) ;
2011-12-07 15:54:50 +02:00
MODULE_DESCRIPTION ( " ALSA SoC for OMAP boards with ABE and twl6040 codec " ) ;
2010-05-17 19:53:10 -05:00
MODULE_LICENSE ( " GPL " ) ;
2011-12-08 11:07:42 +02:00
MODULE_ALIAS ( " platform:omap-abe-twl6040 " ) ;