2012-03-16 12:56:44 +04:00
/*
* Copyright 2012 Freescale Semiconductor , Inc .
* Copyright 2012 Linaro Ltd .
*
* The code contained herein is licensed under the GNU General Public
* License . You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations :
*
* http : //www.opensource.org/licenses/gpl-license.html
* http : //www.gnu.org/copyleft/gpl.html
*/
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_platform.h>
2013-07-11 15:56:15 +04:00
# include <linux/i2c.h>
2012-04-27 11:02:57 +04:00
# include <linux/clk.h>
2012-03-16 12:56:44 +04:00
# include <sound/soc.h>
# include "../codecs/sgtl5000.h"
# include "imx-audmux.h"
# define DAI_NAME_SIZE 32
struct imx_sgtl5000_data {
struct snd_soc_dai_link dai ;
struct snd_soc_card card ;
char codec_dai_name [ DAI_NAME_SIZE ] ;
char platform_name [ DAI_NAME_SIZE ] ;
2012-04-27 11:02:57 +04:00
struct clk * codec_clk ;
2012-03-16 12:56:44 +04:00
unsigned int clk_frequency ;
} ;
static int imx_sgtl5000_dai_init ( struct snd_soc_pcm_runtime * rtd )
{
2014-02-08 09:20:35 +04:00
struct imx_sgtl5000_data * data = snd_soc_card_get_drvdata ( rtd - > card ) ;
2012-03-16 12:56:44 +04:00
struct device * dev = rtd - > card - > dev ;
int ret ;
ret = snd_soc_dai_set_sysclk ( rtd - > codec_dai , SGTL5000_SYSCLK ,
data - > clk_frequency , SND_SOC_CLOCK_IN ) ;
if ( ret ) {
dev_err ( dev , " could not set codec driver clock params \n " ) ;
return ret ;
}
return 0 ;
}
2012-03-29 20:13:03 +04:00
static const struct snd_soc_dapm_widget imx_sgtl5000_dapm_widgets [ ] = {
SND_SOC_DAPM_MIC ( " Mic Jack " , NULL ) ,
SND_SOC_DAPM_LINE ( " Line In Jack " , NULL ) ,
SND_SOC_DAPM_HP ( " Headphone Jack " , NULL ) ,
SND_SOC_DAPM_SPK ( " Line Out Jack " , NULL ) ,
SND_SOC_DAPM_SPK ( " Ext Spk " , NULL ) ,
} ;
2012-12-07 18:26:16 +04:00
static int imx_sgtl5000_probe ( struct platform_device * pdev )
2012-03-16 12:56:44 +04:00
{
struct device_node * np = pdev - > dev . of_node ;
struct device_node * ssi_np , * codec_np ;
struct platform_device * ssi_pdev ;
2012-04-27 11:02:57 +04:00
struct i2c_client * codec_dev ;
2013-09-25 17:22:01 +04:00
struct imx_sgtl5000_data * data = NULL ;
2012-03-16 12:56:44 +04:00
int int_port , ext_port ;
int ret ;
ret = of_property_read_u32 ( np , " mux-int-port " , & int_port ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " mux-int-port missing or invalid \n " ) ;
return ret ;
}
ret = of_property_read_u32 ( np , " mux-ext-port " , & ext_port ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " mux-ext-port missing or invalid \n " ) ;
return ret ;
}
/*
* The port numbering in the hardware manual starts at 1 , while
* the audmux API expects it starts at 0.
*/
int_port - - ;
ext_port - - ;
ret = imx_audmux_v2_configure_port ( int_port ,
IMX_AUDMUX_V2_PTCR_SYN |
IMX_AUDMUX_V2_PTCR_TFSEL ( ext_port ) |
IMX_AUDMUX_V2_PTCR_TCSEL ( ext_port ) |
IMX_AUDMUX_V2_PTCR_TFSDIR |
IMX_AUDMUX_V2_PTCR_TCLKDIR ,
IMX_AUDMUX_V2_PDCR_RXDSEL ( ext_port ) ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " audmux internal port setup failed \n " ) ;
return ret ;
}
2012-08-19 11:02:52 +04:00
ret = imx_audmux_v2_configure_port ( ext_port ,
2012-07-04 11:38:27 +04:00
IMX_AUDMUX_V2_PTCR_SYN ,
2012-03-16 12:56:44 +04:00
IMX_AUDMUX_V2_PDCR_RXDSEL ( int_port ) ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " audmux external port setup failed \n " ) ;
return ret ;
}
ssi_np = of_parse_phandle ( pdev - > dev . of_node , " ssi-controller " , 0 ) ;
codec_np = of_parse_phandle ( pdev - > dev . of_node , " audio-codec " , 0 ) ;
if ( ! ssi_np | | ! codec_np ) {
dev_err ( & pdev - > dev , " phandle missing or invalid \n " ) ;
2012-04-27 11:02:56 +04:00
ret = - EINVAL ;
goto fail ;
2012-03-16 12:56:44 +04:00
}
ssi_pdev = of_find_device_by_node ( ssi_np ) ;
if ( ! ssi_pdev ) {
dev_err ( & pdev - > dev , " failed to find SSI platform device \n " ) ;
2013-06-21 01:20:49 +04:00
ret = - EPROBE_DEFER ;
2012-04-27 11:02:56 +04:00
goto fail ;
2012-03-16 12:56:44 +04:00
}
2012-04-27 11:02:57 +04:00
codec_dev = of_find_i2c_device_by_node ( codec_np ) ;
if ( ! codec_dev ) {
dev_err ( & pdev - > dev , " failed to find codec platform device \n " ) ;
2013-06-21 01:20:49 +04:00
return - EPROBE_DEFER ;
2012-04-27 11:02:57 +04:00
}
2012-03-16 12:56:44 +04:00
data = devm_kzalloc ( & pdev - > dev , sizeof ( * data ) , GFP_KERNEL ) ;
2012-04-27 11:02:56 +04:00
if ( ! data ) {
ret = - ENOMEM ;
goto fail ;
}
2012-03-16 12:56:44 +04:00
2013-09-25 17:21:08 +04:00
data - > codec_clk = clk_get ( & codec_dev - > dev , NULL ) ;
2013-07-16 16:05:07 +04:00
if ( IS_ERR ( data - > codec_clk ) ) {
ret = PTR_ERR ( data - > codec_clk ) ;
2013-06-10 05:07:46 +04:00
goto fail ;
2013-07-16 16:05:07 +04:00
}
2013-06-10 05:07:46 +04:00
data - > clk_frequency = clk_get_rate ( data - > codec_clk ) ;
2012-03-16 12:56:44 +04:00
data - > dai . name = " HiFi " ;
data - > dai . stream_name = " HiFi " ;
data - > dai . codec_dai_name = " sgtl5000 " ;
data - > dai . codec_of_node = codec_np ;
2013-03-25 07:19:12 +04:00
data - > dai . cpu_of_node = ssi_np ;
2013-04-25 07:18:46 +04:00
data - > dai . platform_of_node = ssi_np ;
2012-03-16 12:56:44 +04:00
data - > dai . init = & imx_sgtl5000_dai_init ;
data - > dai . dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM ;
data - > card . dev = & pdev - > dev ;
ret = snd_soc_of_parse_card_name ( & data - > card , " model " ) ;
2012-03-29 20:13:03 +04:00
if ( ret )
2013-06-10 05:07:46 +04:00
goto fail ;
2012-03-29 20:13:03 +04:00
ret = snd_soc_of_parse_audio_routing ( & data - > card , " audio-routing " ) ;
2012-03-16 12:56:44 +04:00
if ( ret )
2013-06-10 05:07:46 +04:00
goto fail ;
2012-03-16 12:56:44 +04:00
data - > card . num_links = 1 ;
2012-11-22 16:31:08 +04:00
data - > card . owner = THIS_MODULE ;
2012-03-16 12:56:44 +04:00
data - > card . dai_link = & data - > dai ;
2012-03-29 20:13:03 +04:00
data - > card . dapm_widgets = imx_sgtl5000_dapm_widgets ;
data - > card . num_dapm_widgets = ARRAY_SIZE ( imx_sgtl5000_dapm_widgets ) ;
2012-03-16 12:56:44 +04:00
2014-02-08 09:20:35 +04:00
platform_set_drvdata ( pdev , & data - > card ) ;
snd_soc_card_set_drvdata ( & data - > card , data ) ;
2013-09-17 08:12:46 +04:00
ret = devm_snd_soc_register_card ( & pdev - > dev , & data - > card ) ;
2012-03-16 12:56:44 +04:00
if ( ret ) {
dev_err ( & pdev - > dev , " snd_soc_register_card failed (%d) \n " , ret ) ;
2013-06-10 05:07:46 +04:00
goto fail ;
2012-03-16 12:56:44 +04:00
}
2013-04-24 18:54:43 +04:00
of_node_put ( ssi_np ) ;
of_node_put ( codec_np ) ;
return 0 ;
2012-04-27 11:02:56 +04:00
fail :
2013-09-25 17:22:01 +04:00
if ( data & & ! IS_ERR ( data - > codec_clk ) )
2013-09-25 17:21:08 +04:00
clk_put ( data - > codec_clk ) ;
2012-04-27 11:02:56 +04:00
if ( ssi_np )
of_node_put ( ssi_np ) ;
if ( codec_np )
of_node_put ( codec_np ) ;
2012-03-16 12:56:44 +04:00
2012-04-27 11:02:56 +04:00
return ret ;
2012-03-16 12:56:44 +04:00
}
2012-12-07 18:26:16 +04:00
static int imx_sgtl5000_remove ( struct platform_device * pdev )
2012-03-16 12:56:44 +04:00
{
2014-02-08 09:20:35 +04:00
struct snd_soc_card * card = platform_get_drvdata ( pdev ) ;
struct imx_sgtl5000_data * data = snd_soc_card_get_drvdata ( card ) ;
2012-03-16 12:56:44 +04:00
2013-09-25 17:21:08 +04:00
clk_put ( data - > codec_clk ) ;
2012-03-16 12:56:44 +04:00
return 0 ;
}
static const struct of_device_id imx_sgtl5000_dt_ids [ ] = {
{ . compatible = " fsl,imx-audio-sgtl5000 " , } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , imx_sgtl5000_dt_ids ) ;
static struct platform_driver imx_sgtl5000_driver = {
. driver = {
. name = " imx-sgtl5000 " ,
. owner = THIS_MODULE ,
2013-10-24 14:15:29 +04:00
. pm = & snd_soc_pm_ops ,
2012-03-16 12:56:44 +04:00
. of_match_table = imx_sgtl5000_dt_ids ,
} ,
. probe = imx_sgtl5000_probe ,
2013-10-01 05:17:37 +04:00
. remove = imx_sgtl5000_remove ,
2012-03-16 12:56:44 +04:00
} ;
module_platform_driver ( imx_sgtl5000_driver ) ;
MODULE_AUTHOR ( " Shawn Guo <shawn.guo@linaro.org> " ) ;
MODULE_DESCRIPTION ( " Freescale i.MX SGTL5000 ASoC machine driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:imx-sgtl5000 " ) ;