2010-05-27 12:58:55 +04:00
/*
* eukrea - tlv320 . c - - SoC audio for eukrea_cpuimxXX in I2S mode
*
* Copyright 2010 Eric Bénard , Eukréa Electromatique < eric @ eukrea . com >
*
* based on sound / soc / s3c24xx / s3c24xx_simtec_tlv320aic23 . c
* which is Copyright 2009 Simtec Electronics
* and on sound / soc / imx / phycore - ac97 . c which is
* Copyright 2009 Sascha Hauer , Pengutronix < s . hauer @ pengutronix . de >
2012-03-16 12:56:38 +04:00
*
2010-05-27 12:58:55 +04:00
* 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 .
*
*/
2014-01-15 21:23:25 +04:00
# include <linux/errno.h>
2010-05-27 12:58:55 +04:00
# include <linux/module.h>
# include <linux/moduleparam.h>
2014-01-15 21:23:25 +04:00
# include <linux/of.h>
# include <linux/of_platform.h>
2010-05-27 12:58:55 +04:00
# include <linux/device.h>
# include <linux/i2c.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/soc.h>
# include <asm/mach-types.h>
# include "../codecs/tlv320aic23.h"
# include "imx-ssi.h"
2014-01-15 21:23:25 +04:00
# include "fsl_ssi.h"
2012-03-05 18:30:53 +04:00
# include "imx-audmux.h"
2010-05-27 12:58:55 +04:00
# define CODEC_CLOCK 12000000
static int eukrea_tlv320_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2010-11-04 19:05:40 +03:00
struct snd_soc_dai * codec_dai = rtd - > codec_dai ;
struct snd_soc_dai * cpu_dai = rtd - > cpu_dai ;
2010-05-27 12:58:55 +04:00
int ret ;
ret = snd_soc_dai_set_sysclk ( codec_dai , 0 ,
CODEC_CLOCK , SND_SOC_CLOCK_OUT ) ;
if ( ret ) {
2013-10-24 16:13:48 +04:00
dev_err ( cpu_dai - > dev ,
" Failed to set the codec sysclk. \n " ) ;
2010-05-27 12:58:55 +04:00
return ret ;
}
2014-01-15 21:23:25 +04:00
2015-01-12 12:27:18 +03:00
snd_soc_dai_set_tdm_slot ( cpu_dai , 0x3 , 0x3 , 2 , 0 ) ;
2010-05-27 12:58:55 +04:00
ret = snd_soc_dai_set_sysclk ( cpu_dai , IMX_SSP_SYS_CLK , 0 ,
SND_SOC_CLOCK_IN ) ;
2014-01-15 21:23:25 +04:00
/* fsl_ssi lacks the set_sysclk ops */
if ( ret & & ret ! = - EINVAL ) {
2013-10-24 16:13:48 +04:00
dev_err ( cpu_dai - > dev ,
" Can't set the IMX_SSP_SYS_CLK CPU system clock. \n " ) ;
2010-05-27 12:58:55 +04:00
return ret ;
}
return 0 ;
}
static struct snd_soc_ops eukrea_tlv320_snd_ops = {
. hw_params = eukrea_tlv320_hw_params ,
} ;
static struct snd_soc_dai_link eukrea_tlv320_dai = {
. name = " tlv320aic23 " ,
. stream_name = " TLV320AIC23 " ,
2010-11-04 19:05:40 +03:00
. codec_dai_name = " tlv320aic23-hifi " ,
2015-01-01 19:16:12 +03:00
. dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM ,
2010-05-27 12:58:55 +04:00
. ops = & eukrea_tlv320_snd_ops ,
} ;
static struct snd_soc_card eukrea_tlv320 = {
2011-12-23 10:47:08 +04:00
. owner = THIS_MODULE ,
2010-05-27 12:58:55 +04:00
. dai_link = & eukrea_tlv320_dai ,
. num_links = 1 ,
} ;
2012-12-07 18:26:16 +04:00
static int eukrea_tlv320_probe ( struct platform_device * pdev )
2010-05-27 12:58:55 +04:00
{
int ret ;
2012-03-05 18:30:49 +04:00
int int_port = 0 , ext_port ;
2014-01-15 21:23:25 +04:00
struct device_node * np = pdev - > dev . of_node ;
2014-10-07 22:56:29 +04:00
struct device_node * ssi_np = NULL , * codec_np = NULL ;
2012-03-05 18:30:49 +04:00
2014-01-15 21:23:25 +04:00
eukrea_tlv320 . dev = & pdev - > dev ;
if ( np ) {
ret = snd_soc_of_parse_card_name ( & eukrea_tlv320 ,
" eukrea,model " ) ;
if ( ret ) {
dev_err ( & pdev - > dev ,
" eukrea,model node missing or invalid. \n " ) ;
goto err ;
}
ssi_np = of_parse_phandle ( pdev - > dev . of_node ,
" ssi-controller " , 0 ) ;
if ( ! ssi_np ) {
dev_err ( & pdev - > dev ,
" ssi-controller missing or invalid. \n " ) ;
ret = - ENODEV ;
goto err ;
}
codec_np = of_parse_phandle ( ssi_np , " codec-handle " , 0 ) ;
if ( codec_np )
eukrea_tlv320_dai . codec_of_node = codec_np ;
else
dev_err ( & pdev - > dev , " codec-handle node missing or invalid. \n " ) ;
ret = of_property_read_u32 ( np , " fsl,mux-int-port " , & int_port ) ;
if ( ret ) {
dev_err ( & pdev - > dev ,
" fsl,mux-int-port node missing or invalid. \n " ) ;
return ret ;
}
ret = of_property_read_u32 ( np , " fsl,mux-ext-port " , & ext_port ) ;
if ( ret ) {
dev_err ( & pdev - > dev ,
" fsl,mux-ext-port node 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 - - ;
eukrea_tlv320_dai . cpu_of_node = ssi_np ;
eukrea_tlv320_dai . platform_of_node = ssi_np ;
} else {
eukrea_tlv320_dai . cpu_dai_name = " imx-ssi.0 " ;
eukrea_tlv320_dai . platform_name = " imx-ssi.0 " ;
eukrea_tlv320_dai . codec_name = " tlv320aic23-codec.0-001a " ;
eukrea_tlv320 . name = " cpuimx-audio " ;
}
if ( machine_is_eukrea_cpuimx27 ( ) | |
of_find_compatible_node ( NULL , NULL , " fsl,imx21-audmux " ) ) {
2012-03-05 18:30:54 +04:00
imx_audmux_v1_configure_port ( MX27_AUDMUX_HPCR1_SSI0 ,
IMX_AUDMUX_V1_PCR_SYN |
IMX_AUDMUX_V1_PCR_TFSDIR |
IMX_AUDMUX_V1_PCR_TCLKDIR |
IMX_AUDMUX_V1_PCR_RFSDIR |
IMX_AUDMUX_V1_PCR_RCLKDIR |
IMX_AUDMUX_V1_PCR_TFCSEL ( MX27_AUDMUX_HPCR3_SSI_PINS_4 ) |
IMX_AUDMUX_V1_PCR_RFCSEL ( MX27_AUDMUX_HPCR3_SSI_PINS_4 ) |
IMX_AUDMUX_V1_PCR_RXDSEL ( MX27_AUDMUX_HPCR3_SSI_PINS_4 )
2012-03-05 18:30:49 +04:00
) ;
2012-03-05 18:30:54 +04:00
imx_audmux_v1_configure_port ( MX27_AUDMUX_HPCR3_SSI_PINS_4 ,
IMX_AUDMUX_V1_PCR_SYN |
IMX_AUDMUX_V1_PCR_RXDSEL ( MX27_AUDMUX_HPCR1_SSI0 )
2012-03-05 18:30:49 +04:00
) ;
} else if ( machine_is_eukrea_cpuimx25sd ( ) | |
machine_is_eukrea_cpuimx35sd ( ) | |
2014-01-15 21:23:25 +04:00
machine_is_eukrea_cpuimx51sd ( ) | |
of_find_compatible_node ( NULL , NULL , " fsl,imx31-audmux " ) ) {
if ( ! np )
ext_port = machine_is_eukrea_cpuimx25sd ( ) ?
4 : 3 ;
2012-03-05 18:30:54 +04:00
imx_audmux_v2_configure_port ( int_port ,
IMX_AUDMUX_V2_PTCR_SYN |
IMX_AUDMUX_V2_PTCR_TFSDIR |
IMX_AUDMUX_V2_PTCR_TFSEL ( ext_port ) |
IMX_AUDMUX_V2_PTCR_TCLKDIR |
IMX_AUDMUX_V2_PTCR_TCSEL ( ext_port ) ,
IMX_AUDMUX_V2_PDCR_RXDSEL ( ext_port )
2012-03-05 18:30:49 +04:00
) ;
2012-03-05 18:30:54 +04:00
imx_audmux_v2_configure_port ( ext_port ,
IMX_AUDMUX_V2_PTCR_SYN ,
IMX_AUDMUX_V2_PDCR_RXDSEL ( int_port )
2012-03-05 18:30:49 +04:00
) ;
} else {
2014-01-15 21:23:25 +04:00
if ( np ) {
/* The eukrea,asoc-tlv320 driver was explicitely
* requested ( through the device tree ) .
*/
dev_err ( & pdev - > dev ,
" Missing or invalid audmux DT node. \n " ) ;
return - ENODEV ;
} else {
/* Return happy.
* We might run on a totally different machine .
*/
return 0 ;
}
2012-03-05 18:30:49 +04:00
}
2010-05-27 12:58:55 +04:00
2012-09-22 19:27:31 +04:00
ret = snd_soc_register_card ( & eukrea_tlv320 ) ;
2014-01-15 21:23:25 +04:00
err :
2012-09-22 19:27:31 +04:00
if ( ret )
dev_err ( & pdev - > dev , " snd_soc_register_card failed (%d) \n " , ret ) ;
2014-10-07 22:29:57 +04:00
of_node_put ( ssi_np ) ;
2010-05-27 12:58:55 +04:00
return ret ;
}
2012-12-07 18:26:16 +04:00
static int eukrea_tlv320_remove ( struct platform_device * pdev )
2010-05-27 12:58:55 +04:00
{
2012-09-22 19:27:31 +04:00
snd_soc_unregister_card ( & eukrea_tlv320 ) ;
return 0 ;
2010-05-27 12:58:55 +04:00
}
2014-01-15 21:23:25 +04:00
static const struct of_device_id imx_tlv320_dt_ids [ ] = {
{ . compatible = " eukrea,asoc-tlv320 " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , imx_tlv320_dt_ids ) ;
2012-09-22 19:27:31 +04:00
static struct platform_driver eukrea_tlv320_driver = {
. driver = {
. name = " eukrea_tlv320 " ,
2014-01-15 21:23:25 +04:00
. of_match_table = imx_tlv320_dt_ids ,
2012-09-22 19:27:31 +04:00
} ,
. probe = eukrea_tlv320_probe ,
2013-10-24 16:13:48 +04:00
. remove = eukrea_tlv320_remove ,
} ;
2012-09-22 19:27:31 +04:00
module_platform_driver ( eukrea_tlv320_driver ) ;
2010-05-27 12:58:55 +04:00
MODULE_AUTHOR ( " Eric Bénard <eric@eukrea.com> " ) ;
MODULE_DESCRIPTION ( " CPUIMX ALSA SoC driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
2012-09-22 19:27:31 +04:00
MODULE_ALIAS ( " platform:eukrea_tlv320 " ) ;