2020-06-14 23:24:11 +03:00
// SPDX-License-Identifier: GPL-2.0+
# include <linux/extcon.h>
# include <linux/iio/consumer.h>
# include <linux/iio/iio.h>
# include <linux/input-event-codes.h>
# include <linux/mfd/wm8994/registers.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/of_gpio.h>
# include <linux/regulator/consumer.h>
# include <sound/jack.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
# include "i2s.h"
# include "../codecs/wm8994.h"
# define ARIES_MCLK1_FREQ 24000000
struct aries_wm8994_variant {
unsigned int modem_dai_fmt ;
bool has_fm_radio ;
} ;
struct aries_wm8994_data {
struct extcon_dev * usb_extcon ;
struct regulator * reg_main_micbias ;
struct regulator * reg_headset_micbias ;
struct gpio_desc * gpio_headset_detect ;
struct gpio_desc * gpio_headset_key ;
struct gpio_desc * gpio_earpath_sel ;
struct iio_channel * adc ;
const struct aries_wm8994_variant * variant ;
} ;
/* USB dock */
static struct snd_soc_jack aries_dock ;
static struct snd_soc_jack_pin dock_pins [ ] = {
{
. pin = " LINE " ,
. mask = SND_JACK_LINEOUT ,
} ,
} ;
static int aries_extcon_notifier ( struct notifier_block * this ,
unsigned long connected , void * _cmd )
{
if ( connected )
snd_soc_jack_report ( & aries_dock , SND_JACK_LINEOUT ,
SND_JACK_LINEOUT ) ;
else
snd_soc_jack_report ( & aries_dock , 0 , SND_JACK_LINEOUT ) ;
return NOTIFY_DONE ;
}
static struct notifier_block aries_extcon_notifier_block = {
. notifier_call = aries_extcon_notifier ,
} ;
/* Headset jack */
static struct snd_soc_jack aries_headset ;
static struct snd_soc_jack_pin jack_pins [ ] = {
{
. pin = " HP " ,
. mask = SND_JACK_HEADPHONE ,
} , {
. pin = " Headset Mic " ,
. mask = SND_JACK_MICROPHONE ,
} ,
} ;
static struct snd_soc_jack_zone headset_zones [ ] = {
{
. min_mv = 0 ,
. max_mv = 241 ,
. jack_type = SND_JACK_HEADPHONE ,
} , {
. min_mv = 242 ,
. max_mv = 2980 ,
. jack_type = SND_JACK_HEADSET ,
} , {
. min_mv = 2981 ,
. max_mv = UINT_MAX ,
. jack_type = SND_JACK_HEADPHONE ,
} ,
} ;
static irqreturn_t headset_det_irq_thread ( int irq , void * data )
{
struct aries_wm8994_data * priv = ( struct aries_wm8994_data * ) data ;
int ret = 0 ;
int time_left_ms = 300 ;
int adc ;
while ( time_left_ms > 0 ) {
if ( ! gpiod_get_value ( priv - > gpio_headset_detect ) ) {
snd_soc_jack_report ( & aries_headset , 0 ,
SND_JACK_HEADSET ) ;
gpiod_set_value ( priv - > gpio_earpath_sel , 0 ) ;
return IRQ_HANDLED ;
}
msleep ( 20 ) ;
time_left_ms - = 20 ;
}
/* Temporarily enable micbias and earpath selector */
ret = regulator_enable ( priv - > reg_headset_micbias ) ;
if ( ret )
pr_err ( " %s failed to enable micbias: %d " , __func__ , ret ) ;
gpiod_set_value ( priv - > gpio_earpath_sel , 1 ) ;
ret = iio_read_channel_processed ( priv - > adc , & adc ) ;
if ( ret < 0 ) {
/* failed to read ADC, so assume headphone */
pr_err ( " %s failed to read ADC, assuming headphones " , __func__ ) ;
snd_soc_jack_report ( & aries_headset , SND_JACK_HEADPHONE ,
SND_JACK_HEADSET ) ;
} else {
snd_soc_jack_report ( & aries_headset ,
snd_soc_jack_get_type ( & aries_headset , adc ) ,
SND_JACK_HEADSET ) ;
}
ret = regulator_disable ( priv - > reg_headset_micbias ) ;
if ( ret )
pr_err ( " %s failed disable micbias: %d " , __func__ , ret ) ;
/* Disable earpath selector when no mic connected */
if ( ! ( aries_headset . status & SND_JACK_MICROPHONE ) )
gpiod_set_value ( priv - > gpio_earpath_sel , 0 ) ;
return IRQ_HANDLED ;
}
static int headset_button_check ( void * data )
{
struct aries_wm8994_data * priv = ( struct aries_wm8994_data * ) data ;
/* Filter out keypresses when 4 pole jack not detected */
if ( gpiod_get_value_cansleep ( priv - > gpio_headset_key ) & &
aries_headset . status & SND_JACK_MICROPHONE )
return SND_JACK_BTN_0 ;
return 0 ;
}
static struct snd_soc_jack_gpio headset_button_gpio [ ] = {
{
. name = " Media Button " ,
. report = SND_JACK_BTN_0 ,
. debounce_time = 30 ,
. jack_status_check = headset_button_check ,
} ,
} ;
static int aries_spk_cfg ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
struct snd_soc_card * card = w - > dapm - > card ;
struct snd_soc_pcm_runtime * rtd ;
struct snd_soc_component * component ;
int ret = 0 ;
rtd = snd_soc_get_pcm_runtime ( card , & card - > dai_link [ 0 ] ) ;
component = asoc_rtd_to_codec ( rtd , 0 ) - > component ;
/**
* We have an odd setup - the SPKMODE pin is pulled up so
* we only have access to the left side SPK configs ,
* but SPKOUTR isn ' t bridged so when playing back in
* stereo , we only get the left hand channel . The only
* option we ' re left with is to force the AIF into mono
* mode .
*/
switch ( event ) {
case SND_SOC_DAPM_POST_PMU :
ret = snd_soc_component_update_bits ( component ,
WM8994_AIF1_DAC1_FILTERS_1 ,
WM8994_AIF1DAC1_MONO , WM8994_AIF1DAC1_MONO ) ;
break ;
case SND_SOC_DAPM_PRE_PMD :
ret = snd_soc_component_update_bits ( component ,
WM8994_AIF1_DAC1_FILTERS_1 ,
WM8994_AIF1DAC1_MONO , 0 ) ;
break ;
}
return ret ;
}
static int aries_main_bias ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
struct snd_soc_card * card = w - > dapm - > card ;
struct aries_wm8994_data * priv = snd_soc_card_get_drvdata ( card ) ;
int ret = 0 ;
switch ( event ) {
case SND_SOC_DAPM_PRE_PMU :
ret = regulator_enable ( priv - > reg_main_micbias ) ;
break ;
case SND_SOC_DAPM_POST_PMD :
ret = regulator_disable ( priv - > reg_main_micbias ) ;
break ;
}
return ret ;
}
static int aries_headset_bias ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
struct snd_soc_card * card = w - > dapm - > card ;
struct aries_wm8994_data * priv = snd_soc_card_get_drvdata ( card ) ;
int ret = 0 ;
switch ( event ) {
case SND_SOC_DAPM_PRE_PMU :
ret = regulator_enable ( priv - > reg_headset_micbias ) ;
break ;
case SND_SOC_DAPM_POST_PMD :
ret = regulator_disable ( priv - > reg_headset_micbias ) ;
break ;
}
return ret ;
}
static const struct snd_kcontrol_new aries_controls [ ] = {
SOC_DAPM_PIN_SWITCH ( " Modem In " ) ,
SOC_DAPM_PIN_SWITCH ( " Modem Out " ) ,
} ;
static const struct snd_soc_dapm_widget aries_dapm_widgets [ ] = {
SND_SOC_DAPM_HP ( " HP " , NULL ) ,
SND_SOC_DAPM_SPK ( " SPK " , aries_spk_cfg ) ,
SND_SOC_DAPM_SPK ( " RCV " , NULL ) ,
SND_SOC_DAPM_LINE ( " LINE " , NULL ) ,
SND_SOC_DAPM_MIC ( " Main Mic " , aries_main_bias ) ,
SND_SOC_DAPM_MIC ( " Headset Mic " , aries_headset_bias ) ,
SND_SOC_DAPM_MIC ( " Bluetooth Mic " , NULL ) ,
SND_SOC_DAPM_SPK ( " Bluetooth SPK " , NULL ) ,
SND_SOC_DAPM_LINE ( " Modem In " , NULL ) ,
SND_SOC_DAPM_LINE ( " Modem Out " , NULL ) ,
/* This must be last as it is conditionally not used */
SND_SOC_DAPM_LINE ( " FM In " , NULL ) ,
} ;
static int aries_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
2020-07-20 04:18:14 +03:00
struct snd_soc_pcm_runtime * rtd = asoc_substream_to_rtd ( substream ) ;
2020-06-14 23:24:11 +03:00
struct snd_soc_dai * codec_dai = asoc_rtd_to_codec ( rtd , 0 ) ;
unsigned int pll_out ;
int ret ;
/* AIF1CLK should be >=3MHz for optimal performance */
if ( params_width ( params ) = = 24 )
pll_out = params_rate ( params ) * 384 ;
else if ( params_rate ( params ) = = 8000 | | params_rate ( params ) = = 11025 )
pll_out = params_rate ( params ) * 512 ;
else
pll_out = params_rate ( params ) * 256 ;
ret = snd_soc_dai_set_pll ( codec_dai , WM8994_FLL1 , WM8994_FLL_SRC_MCLK1 ,
ARIES_MCLK1_FREQ , pll_out ) ;
if ( ret < 0 )
return ret ;
ret = snd_soc_dai_set_sysclk ( codec_dai , WM8994_SYSCLK_FLL1 ,
pll_out , SND_SOC_CLOCK_IN ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static int aries_hw_free ( struct snd_pcm_substream * substream )
{
2020-07-20 04:18:14 +03:00
struct snd_soc_pcm_runtime * rtd = asoc_substream_to_rtd ( substream ) ;
2020-06-14 23:24:11 +03:00
struct snd_soc_dai * codec_dai = asoc_rtd_to_codec ( rtd , 0 ) ;
int ret ;
/* Switch sysclk to MCLK1 */
ret = snd_soc_dai_set_sysclk ( codec_dai , WM8994_SYSCLK_MCLK1 ,
ARIES_MCLK1_FREQ , SND_SOC_CLOCK_IN ) ;
if ( ret < 0 )
return ret ;
/* Stop PLL */
ret = snd_soc_dai_set_pll ( codec_dai , WM8994_FLL1 , WM8994_FLL_SRC_MCLK1 ,
ARIES_MCLK1_FREQ , 0 ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
/*
* Main DAI operations
*/
2021-07-28 20:25:48 +03:00
static const struct snd_soc_ops aries_ops = {
2020-06-14 23:24:11 +03:00
. hw_params = aries_hw_params ,
. hw_free = aries_hw_free ,
} ;
static int aries_baseband_init ( struct snd_soc_pcm_runtime * rtd )
{
struct snd_soc_dai * codec_dai = asoc_rtd_to_codec ( rtd , 0 ) ;
unsigned int pll_out ;
int ret ;
pll_out = 8000 * 512 ;
/* Set the codec FLL */
ret = snd_soc_dai_set_pll ( codec_dai , WM8994_FLL2 , WM8994_FLL_SRC_MCLK1 ,
ARIES_MCLK1_FREQ , pll_out ) ;
if ( ret < 0 )
return ret ;
/* Set the codec system clock */
ret = snd_soc_dai_set_sysclk ( codec_dai , WM8994_SYSCLK_FLL2 ,
pll_out , SND_SOC_CLOCK_IN ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static int aries_late_probe ( struct snd_soc_card * card )
{
struct aries_wm8994_data * priv = snd_soc_card_get_drvdata ( card ) ;
int ret , irq ;
ret = snd_soc_card_jack_new ( card , " Dock " , SND_JACK_LINEOUT ,
& aries_dock , dock_pins , ARRAY_SIZE ( dock_pins ) ) ;
if ( ret )
return ret ;
ret = devm_extcon_register_notifier ( card - > dev ,
priv - > usb_extcon , EXTCON_JACK_LINE_OUT ,
& aries_extcon_notifier_block ) ;
if ( ret )
return ret ;
if ( extcon_get_state ( priv - > usb_extcon ,
EXTCON_JACK_LINE_OUT ) > 0 )
snd_soc_jack_report ( & aries_dock , SND_JACK_LINEOUT ,
SND_JACK_LINEOUT ) ;
else
snd_soc_jack_report ( & aries_dock , 0 , SND_JACK_LINEOUT ) ;
ret = snd_soc_card_jack_new ( card , " Headset " ,
SND_JACK_HEADSET | SND_JACK_BTN_0 ,
& aries_headset ,
jack_pins , ARRAY_SIZE ( jack_pins ) ) ;
if ( ret )
return ret ;
ret = snd_soc_jack_add_zones ( & aries_headset , ARRAY_SIZE ( headset_zones ) ,
headset_zones ) ;
if ( ret )
return ret ;
irq = gpiod_to_irq ( priv - > gpio_headset_detect ) ;
if ( irq < 0 ) {
dev_err ( card - > dev , " Failed to map headset detect gpio to irq " ) ;
return - EINVAL ;
}
ret = devm_request_threaded_irq ( card - > dev , irq , NULL ,
headset_det_irq_thread ,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
IRQF_ONESHOT , " headset_detect " , priv ) ;
if ( ret ) {
dev_err ( card - > dev , " Failed to request headset detect irq " ) ;
return ret ;
}
headset_button_gpio [ 0 ] . data = priv ;
headset_button_gpio [ 0 ] . desc = priv - > gpio_headset_key ;
snd_jack_set_key ( aries_headset . jack , SND_JACK_BTN_0 , KEY_MEDIA ) ;
return snd_soc_jack_add_gpios ( & aries_headset ,
ARRAY_SIZE ( headset_button_gpio ) , headset_button_gpio ) ;
}
static const struct snd_soc_pcm_stream baseband_params = {
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
. rate_min = 8000 ,
. rate_max = 8000 ,
. channels_min = 1 ,
. channels_max = 1 ,
} ;
static const struct snd_soc_pcm_stream bluetooth_params = {
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
. rate_min = 8000 ,
. rate_max = 8000 ,
. channels_min = 1 ,
. channels_max = 2 ,
} ;
static const struct snd_soc_dapm_widget aries_modem_widgets [ ] = {
SND_SOC_DAPM_INPUT ( " Modem RX " ) ,
SND_SOC_DAPM_OUTPUT ( " Modem TX " ) ,
} ;
static const struct snd_soc_dapm_route aries_modem_routes [ ] = {
{ " Modem Capture " , NULL , " Modem RX " } ,
{ " Modem TX " , NULL , " Modem Playback " } ,
} ;
static const struct snd_soc_component_driver aries_component = {
. name = " aries-audio " ,
. dapm_widgets = aries_modem_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( aries_modem_widgets ) ,
. dapm_routes = aries_modem_routes ,
. num_dapm_routes = ARRAY_SIZE ( aries_modem_routes ) ,
. idle_bias_on = 1 ,
. use_pmdown_time = 1 ,
. endianness = 1 ,
. non_legacy_dai_naming = 1 ,
} ;
static struct snd_soc_dai_driver aries_ext_dai [ ] = {
{
. name = " Voice call " ,
. playback = {
. stream_name = " Modem Playback " ,
. channels_min = 1 ,
. channels_max = 1 ,
. rate_min = 8000 ,
. rate_max = 8000 ,
. rates = SNDRV_PCM_RATE_8000 ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
} ,
. capture = {
. stream_name = " Modem Capture " ,
. channels_min = 1 ,
. channels_max = 1 ,
. rate_min = 8000 ,
. rate_max = 8000 ,
. rates = SNDRV_PCM_RATE_8000 ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
} ,
} ,
} ;
SND_SOC_DAILINK_DEFS ( aif1 ,
DAILINK_COMP_ARRAY ( COMP_CPU ( SAMSUNG_I2S_DAI ) ) ,
DAILINK_COMP_ARRAY ( COMP_CODEC ( NULL , " wm8994-aif1 " ) ) ,
DAILINK_COMP_ARRAY ( COMP_EMPTY ( ) ) ) ;
SND_SOC_DAILINK_DEFS ( baseband ,
DAILINK_COMP_ARRAY ( COMP_CPU ( " Voice call " ) ) ,
DAILINK_COMP_ARRAY ( COMP_CODEC ( NULL , " wm8994-aif2 " ) ) ) ;
SND_SOC_DAILINK_DEFS ( bluetooth ,
DAILINK_COMP_ARRAY ( COMP_CPU ( " bt-sco-pcm " ) ) ,
DAILINK_COMP_ARRAY ( COMP_CODEC ( NULL , " wm8994-aif3 " ) ) ) ;
static struct snd_soc_dai_link aries_dai [ ] = {
{
. name = " WM8994 AIF1 " ,
. stream_name = " HiFi " ,
. dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM ,
. ops = & aries_ops ,
SND_SOC_DAILINK_REG ( aif1 ) ,
} ,
{
. name = " WM8994 AIF2 " ,
. stream_name = " Baseband " ,
. init = & aries_baseband_init ,
. params = & baseband_params ,
. ignore_suspend = 1 ,
SND_SOC_DAILINK_REG ( baseband ) ,
} ,
{
. name = " WM8994 AIF3 " ,
. stream_name = " Bluetooth " ,
. params = & bluetooth_params ,
. ignore_suspend = 1 ,
SND_SOC_DAILINK_REG ( bluetooth ) ,
} ,
} ;
static struct snd_soc_card aries_card = {
. name = " ARIES " ,
. owner = THIS_MODULE ,
. dai_link = aries_dai ,
. num_links = ARRAY_SIZE ( aries_dai ) ,
. controls = aries_controls ,
. num_controls = ARRAY_SIZE ( aries_controls ) ,
. dapm_widgets = aries_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( aries_dapm_widgets ) ,
. late_probe = aries_late_probe ,
} ;
static const struct aries_wm8994_variant fascinate4g_variant = {
. modem_dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS
| SND_SOC_DAIFMT_IB_NF ,
. has_fm_radio = false ,
} ;
static const struct aries_wm8994_variant aries_variant = {
. modem_dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM
| SND_SOC_DAIFMT_IB_NF ,
. has_fm_radio = true ,
} ;
static const struct of_device_id samsung_wm8994_of_match [ ] = {
{
. compatible = " samsung,fascinate4g-wm8994 " ,
. data = & fascinate4g_variant ,
} ,
{
. compatible = " samsung,aries-wm8994 " ,
. data = & aries_variant ,
} ,
{ /* sentinel */ } ,
} ;
MODULE_DEVICE_TABLE ( of , samsung_wm8994_of_match ) ;
static int aries_audio_probe ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct device_node * cpu , * codec , * extcon_np ;
struct device * dev = & pdev - > dev ;
struct snd_soc_card * card = & aries_card ;
struct aries_wm8994_data * priv ;
struct snd_soc_dai_link * dai_link ;
const struct of_device_id * match ;
int ret , i ;
if ( ! np )
return - EINVAL ;
card - > dev = dev ;
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
snd_soc_card_set_drvdata ( card , priv ) ;
match = of_match_node ( samsung_wm8994_of_match , np ) ;
priv - > variant = match - > data ;
/* Remove FM widget if not present */
if ( ! priv - > variant - > has_fm_radio )
card - > num_dapm_widgets - - ;
priv - > reg_main_micbias = devm_regulator_get ( dev , " main-micbias " ) ;
if ( IS_ERR ( priv - > reg_main_micbias ) ) {
dev_err ( dev , " Failed to get main micbias regulator \n " ) ;
return PTR_ERR ( priv - > reg_main_micbias ) ;
}
priv - > reg_headset_micbias = devm_regulator_get ( dev , " headset-micbias " ) ;
if ( IS_ERR ( priv - > reg_headset_micbias ) ) {
dev_err ( dev , " Failed to get headset micbias regulator \n " ) ;
return PTR_ERR ( priv - > reg_headset_micbias ) ;
}
priv - > gpio_earpath_sel = devm_gpiod_get ( dev , " earpath-sel " ,
GPIOD_OUT_LOW ) ;
if ( IS_ERR ( priv - > gpio_earpath_sel ) ) {
dev_err ( dev , " Failed to get earpath selector gpio " ) ;
return PTR_ERR ( priv - > gpio_earpath_sel ) ;
}
extcon_np = of_parse_phandle ( np , " extcon " , 0 ) ;
priv - > usb_extcon = extcon_find_edev_by_node ( extcon_np ) ;
if ( IS_ERR ( priv - > usb_extcon ) ) {
if ( PTR_ERR ( priv - > usb_extcon ) ! = - EPROBE_DEFER )
dev_err ( dev , " Failed to get extcon device " ) ;
return PTR_ERR ( priv - > usb_extcon ) ;
}
of_node_put ( extcon_np ) ;
priv - > adc = devm_iio_channel_get ( dev , " headset-detect " ) ;
if ( IS_ERR ( priv - > adc ) ) {
if ( PTR_ERR ( priv - > adc ) ! = - EPROBE_DEFER )
dev_err ( dev , " Failed to get ADC channel " ) ;
return PTR_ERR ( priv - > adc ) ;
}
if ( priv - > adc - > channel - > type ! = IIO_VOLTAGE )
return - EINVAL ;
priv - > gpio_headset_key = devm_gpiod_get ( dev , " headset-key " ,
GPIOD_IN ) ;
if ( IS_ERR ( priv - > gpio_headset_key ) ) {
dev_err ( dev , " Failed to get headset key gpio " ) ;
return PTR_ERR ( priv - > gpio_headset_key ) ;
}
priv - > gpio_headset_detect = devm_gpiod_get ( dev ,
" headset-detect " , GPIOD_IN ) ;
if ( IS_ERR ( priv - > gpio_headset_detect ) ) {
dev_err ( dev , " Failed to get headset detect gpio " ) ;
return PTR_ERR ( priv - > gpio_headset_detect ) ;
}
/* Update card-name if provided through DT, else use default name */
snd_soc_of_parse_card_name ( card , " model " ) ;
ret = snd_soc_of_parse_audio_routing ( card , " samsung,audio-routing " ) ;
if ( ret < 0 ) {
dev_err ( dev , " Audio routing invalid/unspecified \n " ) ;
return ret ;
}
aries_dai [ 1 ] . dai_fmt = priv - > variant - > modem_dai_fmt ;
cpu = of_get_child_by_name ( dev - > of_node , " cpu " ) ;
if ( ! cpu )
return - EINVAL ;
codec = of_get_child_by_name ( dev - > of_node , " codec " ) ;
if ( ! codec )
return - EINVAL ;
for_each_card_prelinks ( card , i , dai_link ) {
dai_link - > codecs - > of_node = of_parse_phandle ( codec ,
" sound-dai " , 0 ) ;
if ( ! dai_link - > codecs - > of_node ) {
ret = - EINVAL ;
goto out ;
}
}
/* Set CPU and platform of_node for main DAI */
aries_dai [ 0 ] . cpus - > of_node = of_parse_phandle ( cpu ,
" sound-dai " , 0 ) ;
if ( ! aries_dai [ 0 ] . cpus - > of_node ) {
ret = - EINVAL ;
goto out ;
}
aries_dai [ 0 ] . platforms - > of_node = aries_dai [ 0 ] . cpus - > of_node ;
/* Set CPU of_node for BT DAI */
aries_dai [ 2 ] . cpus - > of_node = of_parse_phandle ( cpu ,
" sound-dai " , 1 ) ;
if ( ! aries_dai [ 2 ] . cpus - > of_node ) {
ret = - EINVAL ;
goto out ;
}
ret = devm_snd_soc_register_component ( dev , & aries_component ,
aries_ext_dai , ARRAY_SIZE ( aries_ext_dai ) ) ;
if ( ret < 0 ) {
dev_err ( dev , " Failed to register component: %d \n " , ret ) ;
goto out ;
}
ret = devm_snd_soc_register_card ( dev , card ) ;
if ( ret )
dev_err ( dev , " snd_soc_register_card() failed:%d \n " , ret ) ;
out :
of_node_put ( cpu ) ;
of_node_put ( codec ) ;
return ret ;
}
static struct platform_driver aries_audio_driver = {
. driver = {
. name = " aries-audio-wm8994 " ,
. of_match_table = of_match_ptr ( samsung_wm8994_of_match ) ,
. pm = & snd_soc_pm_ops ,
} ,
. probe = aries_audio_probe ,
} ;
module_platform_driver ( aries_audio_driver ) ;
MODULE_DESCRIPTION ( " ALSA SoC ARIES WM8994 " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:aries-audio-wm8994 " ) ;