2019-04-19 13:22:01 +03:00
// SPDX-License-Identifier: GPL-2.0+
//
// Copyright (C) 2015 - 2016 Samsung Electronics Co., Ltd.
//
// Authors: Inha Song <ideal.song@samsung.com>
// Sylwester Nawrocki <s.nawrocki@samsung.com>
2016-11-02 19:05:45 +03:00
# include <linux/clk.h>
# include <linux/gpio.h>
2016-12-09 21:12:50 +03:00
# include <linux/gpio/consumer.h>
2016-11-02 19:05:45 +03:00
# include <linux/module.h>
# include <linux/of.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
# include "i2s.h"
# include "../codecs/wm5110.h"
/*
* The source clock is XCLKOUT with its mux set to the external fixed rate
* oscillator ( XXTI ) .
*/
# define MCLK_RATE 24000000U
# define TM2_DAI_AIF1 0
# define TM2_DAI_AIF2 1
struct tm2_machine_priv {
2018-02-13 05:03:12 +03:00
struct snd_soc_component * component ;
2016-11-02 19:05:45 +03:00
unsigned int sysclk_rate ;
struct gpio_desc * gpio_mic_bias ;
} ;
static int tm2_start_sysclk ( struct snd_soc_card * card )
{
struct tm2_machine_priv * priv = snd_soc_card_get_drvdata ( card ) ;
2018-02-13 05:03:12 +03:00
struct snd_soc_component * component = priv - > component ;
2016-11-02 19:05:45 +03:00
int ret ;
2018-02-13 05:03:12 +03:00
ret = snd_soc_component_set_pll ( component , WM5110_FLL1_REFCLK ,
2016-11-02 19:05:45 +03:00
ARIZONA_FLL_SRC_MCLK1 ,
MCLK_RATE ,
priv - > sysclk_rate ) ;
if ( ret < 0 ) {
2018-02-13 05:03:12 +03:00
dev_err ( component - > dev , " Failed to set FLL1 source: %d \n " , ret ) ;
2016-11-02 19:05:45 +03:00
return ret ;
}
2018-02-13 05:03:12 +03:00
ret = snd_soc_component_set_pll ( component , WM5110_FLL1 ,
2016-11-02 19:05:45 +03:00
ARIZONA_FLL_SRC_MCLK1 ,
MCLK_RATE ,
priv - > sysclk_rate ) ;
if ( ret < 0 ) {
2018-02-13 05:03:12 +03:00
dev_err ( component - > dev , " Failed to start FLL1: %d \n " , ret ) ;
2016-11-02 19:05:45 +03:00
return ret ;
}
2018-02-13 05:03:12 +03:00
ret = snd_soc_component_set_sysclk ( component , ARIZONA_CLK_SYSCLK ,
2016-11-02 19:05:45 +03:00
ARIZONA_CLK_SRC_FLL1 ,
priv - > sysclk_rate ,
SND_SOC_CLOCK_IN ) ;
if ( ret < 0 ) {
2018-02-13 05:03:12 +03:00
dev_err ( component - > dev , " Failed to set SYSCLK source: %d \n " , ret ) ;
2016-11-02 19:05:45 +03:00
return ret ;
}
return 0 ;
}
static int tm2_stop_sysclk ( struct snd_soc_card * card )
{
struct tm2_machine_priv * priv = snd_soc_card_get_drvdata ( card ) ;
2018-02-13 05:03:12 +03:00
struct snd_soc_component * component = priv - > component ;
2016-11-02 19:05:45 +03:00
int ret ;
2018-02-13 05:03:12 +03:00
ret = snd_soc_component_set_pll ( component , WM5110_FLL1 , 0 , 0 , 0 ) ;
2016-11-02 19:05:45 +03:00
if ( ret < 0 ) {
2018-02-13 05:03:12 +03:00
dev_err ( component - > dev , " Failed to stop FLL1: %d \n " , ret ) ;
2016-11-02 19:05:45 +03:00
return ret ;
}
2018-02-13 05:03:12 +03:00
ret = snd_soc_component_set_sysclk ( component , ARIZONA_CLK_SYSCLK ,
2016-11-02 19:05:45 +03:00
ARIZONA_CLK_SRC_FLL1 , 0 , 0 ) ;
if ( ret < 0 ) {
2018-02-13 05:03:12 +03:00
dev_err ( component - > dev , " Failed to stop SYSCLK: %d \n " , ret ) ;
2016-11-02 19:05:45 +03:00
return ret ;
}
return 0 ;
}
static int tm2_aif1_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
2023-09-12 02:49:45 +03:00
struct snd_soc_pcm_runtime * rtd = snd_soc_substream_to_rtd ( substream ) ;
struct snd_soc_component * component = snd_soc_rtd_to_codec ( rtd , 0 ) - > component ;
2016-11-02 19:05:45 +03:00
struct tm2_machine_priv * priv = snd_soc_card_get_drvdata ( rtd - > card ) ;
switch ( params_rate ( params ) ) {
case 4000 :
case 8000 :
case 12000 :
case 16000 :
case 24000 :
case 32000 :
case 48000 :
case 96000 :
case 192000 :
/* Highest possible SYSCLK frequency: 147.456MHz */
priv - > sysclk_rate = 147456000U ;
break ;
case 11025 :
case 22050 :
case 44100 :
case 88200 :
case 176400 :
/* Highest possible SYSCLK frequency: 135.4752 MHz */
priv - > sysclk_rate = 135475200U ;
break ;
default :
2018-02-13 05:03:12 +03:00
dev_err ( component - > dev , " Not supported sample rate: %d \n " ,
2016-11-02 19:05:45 +03:00
params_rate ( params ) ) ;
return - EINVAL ;
}
return tm2_start_sysclk ( rtd - > card ) ;
}
2021-07-28 20:25:48 +03:00
static const struct snd_soc_ops tm2_aif1_ops = {
2016-11-02 19:05:45 +03:00
. hw_params = tm2_aif1_hw_params ,
} ;
static int tm2_aif2_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
2023-09-12 02:49:45 +03:00
struct snd_soc_pcm_runtime * rtd = snd_soc_substream_to_rtd ( substream ) ;
struct snd_soc_component * component = snd_soc_rtd_to_codec ( rtd , 0 ) - > component ;
2016-11-02 19:05:45 +03:00
unsigned int asyncclk_rate ;
int ret ;
switch ( params_rate ( params ) ) {
case 8000 :
case 12000 :
case 16000 :
/* Highest possible ASYNCCLK frequency: 49.152MHz */
asyncclk_rate = 49152000U ;
break ;
case 11025 :
/* Highest possible ASYNCCLK frequency: 45.1584 MHz */
asyncclk_rate = 45158400U ;
break ;
default :
2018-02-13 05:03:12 +03:00
dev_err ( component - > dev , " Not supported sample rate: %d \n " ,
2016-11-02 19:05:45 +03:00
params_rate ( params ) ) ;
return - EINVAL ;
}
2018-02-13 05:03:12 +03:00
ret = snd_soc_component_set_pll ( component , WM5110_FLL2_REFCLK ,
2016-11-02 19:05:45 +03:00
ARIZONA_FLL_SRC_MCLK1 ,
MCLK_RATE ,
asyncclk_rate ) ;
if ( ret < 0 ) {
2018-02-13 05:03:12 +03:00
dev_err ( component - > dev , " Failed to set FLL2 source: %d \n " , ret ) ;
2016-11-02 19:05:45 +03:00
return ret ;
}
2018-02-13 05:03:12 +03:00
ret = snd_soc_component_set_pll ( component , WM5110_FLL2 ,
2016-11-02 19:05:45 +03:00
ARIZONA_FLL_SRC_MCLK1 ,
MCLK_RATE ,
asyncclk_rate ) ;
if ( ret < 0 ) {
2018-02-13 05:03:12 +03:00
dev_err ( component - > dev , " Failed to start FLL2: %d \n " , ret ) ;
2016-11-02 19:05:45 +03:00
return ret ;
}
2018-02-13 05:03:12 +03:00
ret = snd_soc_component_set_sysclk ( component , ARIZONA_CLK_ASYNCCLK ,
2016-11-02 19:05:45 +03:00
ARIZONA_CLK_SRC_FLL2 ,
asyncclk_rate ,
SND_SOC_CLOCK_IN ) ;
if ( ret < 0 ) {
2018-02-13 05:03:12 +03:00
dev_err ( component - > dev , " Failed to set ASYNCCLK source: %d \n " , ret ) ;
2016-11-02 19:05:45 +03:00
return ret ;
}
return 0 ;
}
static int tm2_aif2_hw_free ( struct snd_pcm_substream * substream )
{
2023-09-12 02:49:45 +03:00
struct snd_soc_pcm_runtime * rtd = snd_soc_substream_to_rtd ( substream ) ;
struct snd_soc_component * component = snd_soc_rtd_to_codec ( rtd , 0 ) - > component ;
2016-11-02 19:05:45 +03:00
int ret ;
/* disable FLL2 */
2018-02-13 05:03:12 +03:00
ret = snd_soc_component_set_pll ( component , WM5110_FLL2 , ARIZONA_FLL_SRC_MCLK1 ,
2016-11-02 19:05:45 +03:00
0 , 0 ) ;
if ( ret < 0 )
2018-02-13 05:03:12 +03:00
dev_err ( component - > dev , " Failed to stop FLL2: %d \n " , ret ) ;
2016-11-02 19:05:45 +03:00
return ret ;
}
2021-07-28 20:25:48 +03:00
static const struct snd_soc_ops tm2_aif2_ops = {
2016-11-02 19:05:45 +03:00
. hw_params = tm2_aif2_hw_params ,
. hw_free = tm2_aif2_hw_free ,
} ;
2018-02-12 19:15:37 +03:00
static int tm2_hdmi_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
2023-09-12 02:49:45 +03:00
struct snd_soc_pcm_runtime * rtd = snd_soc_substream_to_rtd ( substream ) ;
struct snd_soc_dai * cpu_dai = snd_soc_rtd_to_cpu ( rtd , 0 ) ;
2018-02-12 19:15:37 +03:00
unsigned int bfs ;
int bitwidth , ret ;
bitwidth = snd_pcm_format_width ( params_format ( params ) ) ;
if ( bitwidth < 0 ) {
dev_err ( rtd - > card - > dev , " Invalid bit-width: %d \n " , bitwidth ) ;
return bitwidth ;
}
switch ( bitwidth ) {
case 48 :
bfs = 64 ;
break ;
case 16 :
bfs = 32 ;
break ;
default :
dev_err ( rtd - > card - > dev , " Unsupported bit-width: %d \n " , bitwidth ) ;
return - EINVAL ;
}
switch ( params_rate ( params ) ) {
case 48000 :
case 96000 :
case 192000 :
break ;
default :
dev_err ( rtd - > card - > dev , " Unsupported sample rate: %d \n " ,
params_rate ( params ) ) ;
return - EINVAL ;
}
ret = snd_soc_dai_set_sysclk ( cpu_dai , SAMSUNG_I2S_OPCLK ,
0 , SAMSUNG_I2S_OPCLK_PCLK ) ;
if ( ret < 0 )
return ret ;
ret = snd_soc_dai_set_clkdiv ( cpu_dai , SAMSUNG_I2S_DIV_BCLK , bfs ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
2021-07-28 20:25:48 +03:00
static const struct snd_soc_ops tm2_hdmi_ops = {
2018-02-12 19:15:37 +03:00
. hw_params = tm2_hdmi_hw_params ,
} ;
2016-11-02 19:05:45 +03:00
static int tm2_mic_bias ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
struct snd_soc_card * card = w - > dapm - > card ;
struct tm2_machine_priv * priv = snd_soc_card_get_drvdata ( card ) ;
switch ( event ) {
case SND_SOC_DAPM_PRE_PMU :
gpiod_set_value_cansleep ( priv - > gpio_mic_bias , 1 ) ;
break ;
case SND_SOC_DAPM_POST_PMD :
gpiod_set_value_cansleep ( priv - > gpio_mic_bias , 0 ) ;
break ;
}
return 0 ;
}
static int tm2_set_bias_level ( struct snd_soc_card * card ,
struct snd_soc_dapm_context * dapm ,
enum snd_soc_bias_level level )
{
struct snd_soc_pcm_runtime * rtd ;
2019-12-10 03:34:08 +03:00
rtd = snd_soc_get_pcm_runtime ( card , & card - > dai_link [ 0 ] ) ;
2016-11-02 19:05:45 +03:00
2023-09-12 02:49:45 +03:00
if ( dapm - > dev ! = snd_soc_rtd_to_codec ( rtd , 0 ) - > dev )
2016-11-02 19:05:45 +03:00
return 0 ;
switch ( level ) {
case SND_SOC_BIAS_STANDBY :
if ( card - > dapm . bias_level = = SND_SOC_BIAS_OFF )
tm2_start_sysclk ( card ) ;
break ;
case SND_SOC_BIAS_OFF :
tm2_stop_sysclk ( card ) ;
break ;
default :
break ;
}
return 0 ;
}
static struct snd_soc_aux_dev tm2_speaker_amp_dev ;
static int tm2_late_probe ( struct snd_soc_card * card )
{
struct tm2_machine_priv * priv = snd_soc_card_get_drvdata ( card ) ;
unsigned int ch_map [ ] = { 0 , 1 } ;
struct snd_soc_dai * amp_pdm_dai ;
struct snd_soc_pcm_runtime * rtd ;
struct snd_soc_dai * aif1_dai ;
struct snd_soc_dai * aif2_dai ;
int ret ;
2019-12-10 03:34:08 +03:00
rtd = snd_soc_get_pcm_runtime ( card , & card - > dai_link [ TM2_DAI_AIF1 ] ) ;
2023-09-12 02:49:45 +03:00
aif1_dai = snd_soc_rtd_to_codec ( rtd , 0 ) ;
priv - > component = snd_soc_rtd_to_codec ( rtd , 0 ) - > component ;
2016-11-02 19:05:45 +03:00
ret = snd_soc_dai_set_sysclk ( aif1_dai , ARIZONA_CLK_SYSCLK , 0 , 0 ) ;
if ( ret < 0 ) {
dev_err ( aif1_dai - > dev , " Failed to set SYSCLK: %d \n " , ret ) ;
return ret ;
}
2019-12-10 03:34:08 +03:00
rtd = snd_soc_get_pcm_runtime ( card , & card - > dai_link [ TM2_DAI_AIF2 ] ) ;
2023-09-12 02:49:45 +03:00
aif2_dai = snd_soc_rtd_to_codec ( rtd , 0 ) ;
2016-11-02 19:05:45 +03:00
ret = snd_soc_dai_set_sysclk ( aif2_dai , ARIZONA_CLK_ASYNCCLK , 0 , 0 ) ;
if ( ret < 0 ) {
dev_err ( aif2_dai - > dev , " Failed to set ASYNCCLK: %d \n " , ret ) ;
return ret ;
}
2019-08-08 08:54:20 +03:00
amp_pdm_dai = snd_soc_find_dai ( & tm2_speaker_amp_dev . dlc ) ;
2016-11-02 19:05:45 +03:00
if ( ! amp_pdm_dai )
return - ENODEV ;
/* Set the MAX98504 V/I sense PDM Tx DAI channel mapping */
ret = snd_soc_dai_set_channel_map ( amp_pdm_dai , ARRAY_SIZE ( ch_map ) ,
ch_map , 0 , NULL ) ;
if ( ret < 0 )
return ret ;
ret = snd_soc_dai_set_tdm_slot ( amp_pdm_dai , 0x3 , 0x0 , 2 , 16 ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static const struct snd_kcontrol_new tm2_controls [ ] = {
SOC_DAPM_PIN_SWITCH ( " HP " ) ,
SOC_DAPM_PIN_SWITCH ( " SPK " ) ,
SOC_DAPM_PIN_SWITCH ( " RCV " ) ,
SOC_DAPM_PIN_SWITCH ( " VPS " ) ,
SOC_DAPM_PIN_SWITCH ( " HDMI " ) ,
SOC_DAPM_PIN_SWITCH ( " Main Mic " ) ,
SOC_DAPM_PIN_SWITCH ( " Sub Mic " ) ,
SOC_DAPM_PIN_SWITCH ( " Third Mic " ) ,
SOC_DAPM_PIN_SWITCH ( " Headset Mic " ) ,
} ;
2017-08-13 18:37:15 +03:00
static const struct snd_soc_dapm_widget tm2_dapm_widgets [ ] = {
2016-11-02 19:05:45 +03:00
SND_SOC_DAPM_HP ( " HP " , NULL ) ,
SND_SOC_DAPM_SPK ( " SPK " , NULL ) ,
SND_SOC_DAPM_SPK ( " RCV " , NULL ) ,
SND_SOC_DAPM_LINE ( " VPS " , NULL ) ,
SND_SOC_DAPM_LINE ( " HDMI " , NULL ) ,
SND_SOC_DAPM_MIC ( " Main Mic " , tm2_mic_bias ) ,
SND_SOC_DAPM_MIC ( " Sub Mic " , NULL ) ,
SND_SOC_DAPM_MIC ( " Third Mic " , NULL ) ,
SND_SOC_DAPM_MIC ( " Headset Mic " , NULL ) ,
} ;
static const struct snd_soc_component_driver tm2_component = {
. name = " tm2-audio " ,
} ;
static struct snd_soc_dai_driver tm2_ext_dai [ ] = {
{
. name = " Voice call " ,
. playback = {
. channels_min = 1 ,
. channels_max = 4 ,
. rate_min = 8000 ,
. rate_max = 48000 ,
. rates = ( SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_48000 ) ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
} ,
. capture = {
. channels_min = 1 ,
. channels_max = 4 ,
. rate_min = 8000 ,
. rate_max = 48000 ,
. rates = ( SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_48000 ) ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
} ,
} ,
{
. name = " Bluetooth " ,
. playback = {
. channels_min = 1 ,
. channels_max = 4 ,
. rate_min = 8000 ,
. rate_max = 16000 ,
. rates = ( SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 ) ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
} ,
. capture = {
. channels_min = 1 ,
. channels_max = 2 ,
. rate_min = 8000 ,
. rate_max = 16000 ,
. rates = ( SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 ) ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
} ,
} ,
} ;
2019-06-06 07:09:44 +03:00
SND_SOC_DAILINK_DEFS ( aif1 ,
DAILINK_COMP_ARRAY ( COMP_CPU ( SAMSUNG_I2S_DAI ) ) ,
2019-06-28 04:48:27 +03:00
DAILINK_COMP_ARRAY ( COMP_CODEC ( NULL , " wm5110-aif1 " ) ) ,
DAILINK_COMP_ARRAY ( COMP_EMPTY ( ) ) ) ;
2019-06-06 07:09:44 +03:00
SND_SOC_DAILINK_DEFS ( voice ,
DAILINK_COMP_ARRAY ( COMP_CPU ( SAMSUNG_I2S_DAI ) ) ,
2019-06-28 04:48:27 +03:00
DAILINK_COMP_ARRAY ( COMP_CODEC ( NULL , " wm5110-aif2 " ) ) ,
DAILINK_COMP_ARRAY ( COMP_EMPTY ( ) ) ) ;
2019-06-06 07:09:44 +03:00
SND_SOC_DAILINK_DEFS ( bt ,
DAILINK_COMP_ARRAY ( COMP_CPU ( SAMSUNG_I2S_DAI ) ) ,
2019-06-28 04:48:27 +03:00
DAILINK_COMP_ARRAY ( COMP_CODEC ( NULL , " wm5110-aif3 " ) ) ,
DAILINK_COMP_ARRAY ( COMP_EMPTY ( ) ) ) ;
2019-06-06 07:09:44 +03:00
SND_SOC_DAILINK_DEFS ( hdmi ,
2019-06-28 04:48:27 +03:00
DAILINK_COMP_ARRAY ( COMP_EMPTY ( ) ) ,
2019-06-06 07:09:44 +03:00
DAILINK_COMP_ARRAY ( COMP_EMPTY ( ) ) ,
DAILINK_COMP_ARRAY ( COMP_EMPTY ( ) ) ) ;
2016-11-02 19:05:45 +03:00
static struct snd_soc_dai_link tm2_dai_links [ ] = {
{
. name = " WM5110 AIF1 " ,
. stream_name = " HiFi Primary " ,
. ops = & tm2_aif1_ops ,
. dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM ,
2019-06-06 07:09:44 +03:00
SND_SOC_DAILINK_REG ( aif1 ) ,
2016-11-02 19:05:45 +03:00
} , {
. name = " WM5110 Voice " ,
. stream_name = " Voice call " ,
. ops = & tm2_aif2_ops ,
. dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM ,
. ignore_suspend = 1 ,
2019-06-06 07:09:44 +03:00
SND_SOC_DAILINK_REG ( voice ) ,
2016-11-02 19:05:45 +03:00
} , {
. name = " WM5110 BT " ,
. stream_name = " Bluetooth " ,
. dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM ,
. ignore_suspend = 1 ,
2019-06-06 07:09:44 +03:00
SND_SOC_DAILINK_REG ( bt ) ,
2018-02-12 19:15:37 +03:00
} , {
. name = " HDMI " ,
. stream_name = " i2s1 " ,
. ops = & tm2_hdmi_ops ,
. dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS ,
2019-06-06 07:09:44 +03:00
SND_SOC_DAILINK_REG ( hdmi ) ,
2016-11-02 19:05:45 +03:00
}
} ;
static struct snd_soc_card tm2_card = {
. owner = THIS_MODULE ,
. dai_link = tm2_dai_links ,
. controls = tm2_controls ,
. num_controls = ARRAY_SIZE ( tm2_controls ) ,
. dapm_widgets = tm2_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( tm2_dapm_widgets ) ,
. aux_dev = & tm2_speaker_amp_dev ,
. num_aux_devs = 1 ,
. late_probe = tm2_late_probe ,
. set_bias_level = tm2_set_bias_level ,
} ;
static int tm2_probe ( struct platform_device * pdev )
{
2018-02-12 19:15:37 +03:00
struct device_node * cpu_dai_node [ 2 ] = { } ;
struct device_node * codec_dai_node [ 2 ] = { } ;
const char * cells_name = NULL ;
2016-11-02 19:05:45 +03:00
struct device * dev = & pdev - > dev ;
struct snd_soc_card * card = & tm2_card ;
struct tm2_machine_priv * priv ;
2018-09-18 04:28:49 +03:00
struct snd_soc_dai_link * dai_link ;
2018-02-12 19:15:37 +03:00
int num_codecs , ret , i ;
2016-11-02 19:05:45 +03:00
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
snd_soc_card_set_drvdata ( card , priv ) ;
card - > dev = dev ;
2017-09-06 04:04:15 +03:00
priv - > gpio_mic_bias = devm_gpiod_get ( dev , " mic-bias " , GPIOD_OUT_HIGH ) ;
2016-11-02 19:05:45 +03:00
if ( IS_ERR ( priv - > gpio_mic_bias ) ) {
dev_err ( dev , " Failed to get mic bias gpio \n " ) ;
return PTR_ERR ( priv - > gpio_mic_bias ) ;
}
ret = snd_soc_of_parse_card_name ( card , " model " ) ;
if ( ret < 0 ) {
dev_err ( dev , " Card name is not specified \n " ) ;
return ret ;
}
2023-08-10 09:32:59 +03:00
ret = snd_soc_of_parse_audio_routing ( card , " audio-routing " ) ;
2016-11-02 19:05:45 +03:00
if ( ret < 0 ) {
2023-08-10 09:32:59 +03:00
/* Backwards compatible way */
ret = snd_soc_of_parse_audio_routing ( card , " samsung,audio-routing " ) ;
if ( ret < 0 ) {
dev_err ( dev , " Audio routing is not specified or invalid \n " ) ;
return ret ;
}
2016-11-02 19:05:45 +03:00
}
2019-08-08 08:54:20 +03:00
card - > aux_dev [ 0 ] . dlc . of_node = of_parse_phandle ( dev - > of_node ,
2016-11-02 19:05:45 +03:00
" audio-amplifier " , 0 ) ;
2019-08-08 08:54:20 +03:00
if ( ! card - > aux_dev [ 0 ] . dlc . of_node ) {
2016-11-02 19:05:45 +03:00
dev_err ( dev , " audio-amplifier property invalid or missing \n " ) ;
return - EINVAL ;
}
2018-02-12 19:15:37 +03:00
num_codecs = of_count_phandle_with_args ( dev - > of_node , " audio-codec " ,
NULL ) ;
/* Skip the HDMI link if not specified in DT */
if ( num_codecs > 1 ) {
card - > num_links = ARRAY_SIZE ( tm2_dai_links ) ;
cells_name = " #sound-dai-cells " ;
} else {
card - > num_links = ARRAY_SIZE ( tm2_dai_links ) - 1 ;
2016-11-02 19:05:45 +03:00
}
2018-02-12 19:15:37 +03:00
for ( i = 0 ; i < num_codecs ; i + + ) {
struct of_phandle_args args ;
ret = of_parse_phandle_with_args ( dev - > of_node , " i2s-controller " ,
cells_name , i , & args ) ;
2021-03-12 21:02:30 +03:00
if ( ret ) {
2018-02-12 19:15:37 +03:00
dev_err ( dev , " i2s-controller property parse error: %d \n " , i ) ;
ret = - EINVAL ;
goto dai_node_put ;
}
cpu_dai_node [ i ] = args . np ;
codec_dai_node [ i ] = of_parse_phandle ( dev - > of_node ,
" audio-codec " , i ) ;
if ( ! codec_dai_node [ i ] ) {
dev_err ( dev , " audio-codec property parse error \n " ) ;
ret = - EINVAL ;
goto dai_node_put ;
}
2016-11-02 19:05:45 +03:00
}
2018-02-12 19:15:37 +03:00
/* Initialize WM5110 - I2S and HDMI - I2S1 DAI links */
2018-09-18 04:28:49 +03:00
for_each_card_prelinks ( card , i , dai_link ) {
2018-02-12 19:15:37 +03:00
unsigned int dai_index = 0 ; /* WM5110 */
2019-06-06 07:09:44 +03:00
dai_link - > cpus - > name = NULL ;
2019-06-28 04:48:27 +03:00
dai_link - > platforms - > name = NULL ;
2018-02-12 19:15:37 +03:00
if ( num_codecs > 1 & & i = = card - > num_links - 1 )
dai_index = 1 ; /* HDMI */
2019-06-06 07:09:44 +03:00
dai_link - > codecs - > of_node = codec_dai_node [ dai_index ] ;
dai_link - > cpus - > of_node = cpu_dai_node [ dai_index ] ;
2019-06-28 04:48:27 +03:00
dai_link - > platforms - > of_node = cpu_dai_node [ dai_index ] ;
2018-02-12 19:15:37 +03:00
}
if ( num_codecs > 1 ) {
2021-03-12 21:02:31 +03:00
struct of_phandle_args args ;
2018-02-12 19:15:37 +03:00
/* HDMI DAI link (I2S1) */
i = card - > num_links - 1 ;
ret = of_parse_phandle_with_fixed_args ( dev - > of_node ,
" audio-codec " , 0 , 1 , & args ) ;
if ( ret ) {
dev_err ( dev , " audio-codec property parse error \n " ) ;
goto dai_node_put ;
}
2019-06-06 07:09:44 +03:00
ret = snd_soc_get_dai_name ( & args , & card - > dai_link [ i ] . codecs - > dai_name ) ;
2018-02-12 19:15:37 +03:00
if ( ret ) {
dev_err ( dev , " Unable to get codec_dai_name \n " ) ;
goto dai_node_put ;
}
2016-11-02 19:05:45 +03:00
}
ret = devm_snd_soc_register_component ( dev , & tm2_component ,
tm2_ext_dai , ARRAY_SIZE ( tm2_ext_dai ) ) ;
if ( ret < 0 ) {
dev_err ( dev , " Failed to register component: %d \n " , ret ) ;
2018-02-12 19:15:37 +03:00
goto dai_node_put ;
2016-11-02 19:05:45 +03:00
}
ret = devm_snd_soc_register_card ( dev , card ) ;
if ( ret < 0 ) {
2021-12-14 05:08:41 +03:00
dev_err_probe ( dev , ret , " Failed to register card \n " ) ;
2018-02-12 19:15:37 +03:00
goto dai_node_put ;
}
dai_node_put :
for ( i = 0 ; i < num_codecs ; i + + ) {
of_node_put ( codec_dai_node [ i ] ) ;
of_node_put ( cpu_dai_node [ i ] ) ;
2016-11-02 19:05:45 +03:00
}
2019-08-08 08:54:20 +03:00
of_node_put ( card - > aux_dev [ 0 ] . dlc . of_node ) ;
2018-02-12 19:15:37 +03:00
2016-11-02 19:05:45 +03:00
return ret ;
}
static int tm2_pm_prepare ( struct device * dev )
{
struct snd_soc_card * card = dev_get_drvdata ( dev ) ;
return tm2_stop_sysclk ( card ) ;
}
static void tm2_pm_complete ( struct device * dev )
{
struct snd_soc_card * card = dev_get_drvdata ( dev ) ;
tm2_start_sysclk ( card ) ;
}
2017-08-13 18:37:15 +03:00
static const struct dev_pm_ops tm2_pm_ops = {
2016-11-02 19:05:45 +03:00
. prepare = tm2_pm_prepare ,
. suspend = snd_soc_suspend ,
. resume = snd_soc_resume ,
. complete = tm2_pm_complete ,
. freeze = snd_soc_suspend ,
. thaw = snd_soc_resume ,
. poweroff = snd_soc_poweroff ,
. restore = snd_soc_resume ,
} ;
static const struct of_device_id tm2_of_match [ ] = {
{ . compatible = " samsung,tm2-audio " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , tm2_of_match ) ;
static struct platform_driver tm2_driver = {
. driver = {
. name = " tm2-audio " ,
. pm = & tm2_pm_ops ,
. of_match_table = tm2_of_match ,
} ,
. probe = tm2_probe ,
} ;
module_platform_driver ( tm2_driver ) ;
MODULE_AUTHOR ( " Inha Song <ideal.song@samsung.com> " ) ;
MODULE_DESCRIPTION ( " ALSA SoC Exynos TM2 Audio Support " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;