2018-07-24 15:48:33 +03:00
// SPDX-License-Identifier: GPL-2.0+
//
// Copyright 2012 Freescale Semiconductor, Inc.
// Copyright 2012 Linaro Ltd.
2012-03-16 12:56:44 +04:00
# 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 ;
2020-03-23 08:18:30 +03:00
ret = snd_soc_dai_set_sysclk ( asoc_rtd_to_codec ( rtd , 0 ) , SGTL5000_SYSCLK ,
2012-03-16 12:56:44 +04:00
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 ;
2019-06-06 07:16:02 +03:00
struct snd_soc_dai_link_component * comp ;
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 ) {
2019-01-18 12:06:53 +03:00
dev_dbg ( & 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
}
2019-02-18 18:13:47 +03:00
put_device ( & ssi_pdev - > dev ) ;
2012-04-27 11:02:57 +04:00
codec_dev = of_find_i2c_device_by_node ( codec_np ) ;
if ( ! codec_dev ) {
2019-01-18 12:06:53 +03:00
dev_dbg ( & pdev - > dev , " failed to find codec platform device \n " ) ;
2019-01-18 12:06:52 +03:00
ret = - EPROBE_DEFER ;
goto fail ;
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 ;
2022-05-11 09:58:03 +03:00
goto put_device ;
2012-04-27 11:02:56 +04:00
}
2012-03-16 12:56:44 +04:00
2019-06-28 04:47:26 +03:00
comp = devm_kzalloc ( & pdev - > dev , 3 * sizeof ( * comp ) , GFP_KERNEL ) ;
2019-06-06 07:16:02 +03:00
if ( ! comp ) {
ret = - ENOMEM ;
2022-05-11 09:58:03 +03:00
goto put_device ;
2019-06-06 07:16:02 +03: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 ) ;
2022-05-11 09:58:03 +03:00
goto put_device ;
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
2019-06-06 07:16:02 +03:00
data - > dai . cpus = & comp [ 0 ] ;
data - > dai . codecs = & comp [ 1 ] ;
2019-06-28 04:47:26 +03:00
data - > dai . platforms = & comp [ 2 ] ;
2019-06-06 07:16:02 +03:00
data - > dai . num_cpus = 1 ;
data - > dai . num_codecs = 1 ;
2019-06-28 04:47:26 +03:00
data - > dai . num_platforms = 1 ;
2019-06-06 07:16:02 +03:00
2012-03-16 12:56:44 +04:00
data - > dai . name = " HiFi " ;
data - > dai . stream_name = " HiFi " ;
2019-06-06 07:16:02 +03:00
data - > dai . codecs - > dai_name = " sgtl5000 " ;
data - > dai . codecs - > of_node = codec_np ;
data - > dai . cpus - > of_node = ssi_np ;
2019-06-28 04:47:26 +03:00
data - > dai . platforms - > 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 |
2021-09-22 00:35:39 +03:00
SND_SOC_DAIFMT_CBP_CFP ;
2012-03-16 12:56:44 +04:00
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 )
2022-05-11 09:58:03 +03:00
goto put_device ;
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 )
2022-05-11 09:58:03 +03:00
goto put_device ;
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 ) {
2021-12-14 05:08:34 +03:00
dev_err_probe ( & pdev - > dev , ret , " snd_soc_register_card failed \n " ) ;
2022-05-11 09:58:03 +03:00
goto put_device ;
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 ;
2022-05-11 09:58:03 +03:00
put_device :
put_device ( & codec_dev - > dev ) ;
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 ) ;
2014-10-07 22:13:25 +04:00
of_node_put ( ssi_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 " ,
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 " ) ;