2019-04-19 12:21:44 +02:00
// SPDX-License-Identifier: GPL-2.0+
//
// Lowland audio support
//
// Copyright 2011 Wolfson Microelectronics
2011-08-23 17:40:01 +01:00
# include <sound/soc.h>
# include <sound/soc-dapm.h>
# include <sound/jack.h>
# include <linux/gpio.h>
# include <linux/module.h>
# include "../codecs/wm5100.h"
# include "../codecs/wm9081.h"
# define MCLK1_RATE (44100 * 512)
# define CLKOUT_RATE (44100 * 256)
static struct snd_soc_jack lowland_headset ;
/* Headset jack detection DAPM pins */
static struct snd_soc_jack_pin lowland_headset_pins [ ] = {
{
. pin = " Headphone " ,
2023-08-02 20:57:36 +03:00
. mask = SND_JACK_HEADPHONE ,
2011-08-23 17:40:01 +01:00
} ,
{
. pin = " Headset Mic " ,
. mask = SND_JACK_MICROPHONE ,
} ,
2023-08-02 20:57:36 +03:00
{
. pin = " Line Out " ,
. mask = SND_JACK_LINEOUT ,
} ,
2011-08-23 17:40:01 +01:00
} ;
static int lowland_wm5100_init ( struct snd_soc_pcm_runtime * rtd )
{
2023-09-11 23:49:45 +00:00
struct snd_soc_component * component = snd_soc_rtd_to_codec ( rtd , 0 ) - > component ;
2011-08-23 17:40:01 +01:00
int ret ;
2018-01-29 02:59:35 +00:00
ret = snd_soc_component_set_sysclk ( component , WM5100_CLK_SYSCLK ,
2011-08-23 17:40:01 +01:00
WM5100_CLKSRC_MCLK1 , MCLK1_RATE ,
SND_SOC_CLOCK_IN ) ;
if ( ret < 0 ) {
pr_err ( " Failed to set SYSCLK clock source: %d \n " , ret ) ;
return ret ;
}
/* Clock OPCLK, used by the other audio components. */
2018-01-29 02:59:35 +00:00
ret = snd_soc_component_set_sysclk ( component , WM5100_CLK_OPCLK , 0 ,
2011-08-23 17:40:01 +01:00
CLKOUT_RATE , 0 ) ;
if ( ret < 0 ) {
pr_err ( " Failed to set OPCLK rate: %d \n " , ret ) ;
return ret ;
}
2022-04-08 13:11:14 +09:00
ret = snd_soc_card_jack_new_pins ( rtd - > card , " Headset " ,
SND_JACK_LINEOUT | SND_JACK_HEADSET |
SND_JACK_BTN_0 ,
& lowland_headset , lowland_headset_pins ,
ARRAY_SIZE ( lowland_headset_pins ) ) ;
2011-08-23 17:40:01 +01:00
if ( ret )
return ret ;
2018-01-29 02:59:35 +00:00
wm5100_detect ( component , & lowland_headset ) ;
2011-08-23 17:40:01 +01:00
return 0 ;
}
2012-05-08 11:11:46 +01:00
static int lowland_wm9081_init ( struct snd_soc_pcm_runtime * rtd )
{
2023-09-11 23:49:45 +00:00
struct snd_soc_component * component = snd_soc_rtd_to_codec ( rtd , 0 ) - > component ;
2012-05-08 11:11:46 +01:00
2015-05-03 19:27:06 +02:00
snd_soc_dapm_nc_pin ( & rtd - > card - > dapm , " LINEOUT " ) ;
2012-05-08 11:11:46 +01:00
/* At any time the WM9081 is active it will have this clock */
2018-01-29 02:59:35 +00:00
return snd_soc_component_set_sysclk ( component , WM9081_SYSCLK_MCLK , 0 ,
2012-05-08 11:11:46 +01:00
CLKOUT_RATE , 0 ) ;
}
static const struct snd_soc_pcm_stream sub_params = {
. formats = SNDRV_PCM_FMTBIT_S32_LE ,
. rate_min = 44100 ,
. rate_max = 44100 ,
. channels_min = 2 ,
. channels_max = 2 ,
} ;
2019-06-06 13:10:36 +09:00
SND_SOC_DAILINK_DEFS ( cpu ,
DAILINK_COMP_ARRAY ( COMP_CPU ( " samsung-i2s.0 " ) ) ,
DAILINK_COMP_ARRAY ( COMP_CODEC ( " wm5100.1-001a " , " wm5100-aif1 " ) ) ,
DAILINK_COMP_ARRAY ( COMP_PLATFORM ( " samsung-i2s.0 " ) ) ) ;
SND_SOC_DAILINK_DEFS ( baseband ,
DAILINK_COMP_ARRAY ( COMP_CPU ( " wm5100-aif2 " ) ) ,
DAILINK_COMP_ARRAY ( COMP_CODEC ( " wm1250-ev1.1-0027 " , " wm1250-ev1 " ) ) ) ;
SND_SOC_DAILINK_DEFS ( speaker ,
DAILINK_COMP_ARRAY ( COMP_CPU ( " wm5100-aif3 " ) ) ,
DAILINK_COMP_ARRAY ( COMP_CODEC ( " wm9081.1-006c " , " wm9081-hifi " ) ) ) ;
2011-08-23 17:40:01 +01:00
static struct snd_soc_dai_link lowland_dai [ ] = {
{
. name = " CPU " ,
. stream_name = " CPU " ,
2012-05-08 09:49:45 +01:00
. dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM ,
2011-08-23 17:40:01 +01:00
. init = lowland_wm5100_init ,
2019-06-06 13:10:36 +09:00
SND_SOC_DAILINK_REG ( cpu ) ,
2011-08-23 17:40:01 +01:00
} ,
{
. name = " Baseband " ,
. stream_name = " Baseband " ,
2012-05-08 09:49:45 +01:00
. dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM ,
2011-08-23 17:40:01 +01:00
. ignore_suspend = 1 ,
2019-06-06 13:10:36 +09:00
SND_SOC_DAILINK_REG ( baseband ) ,
2011-08-23 17:40:01 +01:00
} ,
{
2012-05-08 11:11:46 +01:00
. name = " Sub Speaker " ,
. stream_name = " Sub Speaker " ,
. dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM ,
. ignore_suspend = 1 ,
2023-04-02 23:00:07 +00:00
. c2c_params = & sub_params ,
. num_c2c_params = 1 ,
2011-08-23 17:40:01 +01:00
. init = lowland_wm9081_init ,
2019-06-06 13:10:36 +09:00
SND_SOC_DAILINK_REG ( speaker ) ,
2011-08-23 17:40:01 +01:00
} ,
} ;
static struct snd_soc_codec_conf lowland_codec_conf [ ] = {
{
2019-12-13 09:55:34 +09:00
. dlc = COMP_CODEC_CONF ( " wm9081.1-006c " ) ,
2011-08-23 17:40:01 +01:00
. name_prefix = " Sub " ,
} ,
} ;
static const struct snd_kcontrol_new controls [ ] = {
SOC_DAPM_PIN_SWITCH ( " Main Speaker " ) ,
SOC_DAPM_PIN_SWITCH ( " Main DMIC " ) ,
SOC_DAPM_PIN_SWITCH ( " Main AMIC " ) ,
SOC_DAPM_PIN_SWITCH ( " WM1250 Input " ) ,
SOC_DAPM_PIN_SWITCH ( " WM1250 Output " ) ,
SOC_DAPM_PIN_SWITCH ( " Headphone " ) ,
2023-08-02 20:57:36 +03:00
SOC_DAPM_PIN_SWITCH ( " Line Out " ) ,
2011-08-23 17:40:01 +01:00
} ;
2022-03-30 22:42:27 +02:00
static const struct snd_soc_dapm_widget widgets [ ] = {
2011-08-23 17:40:01 +01:00
SND_SOC_DAPM_HP ( " Headphone " , NULL ) ,
SND_SOC_DAPM_MIC ( " Headset Mic " , NULL ) ,
2023-08-02 20:57:36 +03:00
SND_SOC_DAPM_LINE ( " Line Out " , NULL ) ,
2011-08-23 17:40:01 +01:00
SND_SOC_DAPM_SPK ( " Main Speaker " , NULL ) ,
SND_SOC_DAPM_MIC ( " Main AMIC " , NULL ) ,
SND_SOC_DAPM_MIC ( " Main DMIC " , NULL ) ,
} ;
2022-03-30 22:42:27 +02:00
static const struct snd_soc_dapm_route audio_paths [ ] = {
2011-08-23 17:40:01 +01:00
{ " Sub IN1 " , NULL , " HPOUT2L " } ,
{ " Sub IN2 " , NULL , " HPOUT2R " } ,
{ " Main Speaker " , NULL , " Sub SPKN " } ,
{ " Main Speaker " , NULL , " Sub SPKP " } ,
{ " Main Speaker " , NULL , " SPKDAT1 " } ,
} ;
static struct snd_soc_card lowland = {
. name = " Lowland " ,
2011-12-22 10:53:15 +08:00
. owner = THIS_MODULE ,
2011-08-23 17:40:01 +01:00
. dai_link = lowland_dai ,
. num_links = ARRAY_SIZE ( lowland_dai ) ,
. codec_conf = lowland_codec_conf ,
. num_configs = ARRAY_SIZE ( lowland_codec_conf ) ,
. controls = controls ,
. num_controls = ARRAY_SIZE ( controls ) ,
. dapm_widgets = widgets ,
. num_dapm_widgets = ARRAY_SIZE ( widgets ) ,
. dapm_routes = audio_paths ,
. num_dapm_routes = ARRAY_SIZE ( audio_paths ) ,
} ;
2012-12-07 09:26:15 -05:00
static int lowland_probe ( struct platform_device * pdev )
2011-08-23 17:40:01 +01:00
{
struct snd_soc_card * card = & lowland ;
int ret ;
card - > dev = & pdev - > dev ;
2014-05-21 08:52:17 +05:30
ret = devm_snd_soc_register_card ( & pdev - > dev , card ) ;
2021-12-14 11:08:41 +09:00
if ( ret )
dev_err_probe ( & pdev - > dev , ret , " snd_soc_register_card() failed \n " ) ;
2011-08-23 17:40:01 +01:00
2014-05-21 08:52:17 +05:30
return ret ;
2011-08-23 17:40:01 +01:00
}
static struct platform_driver lowland_driver = {
. driver = {
. name = " lowland " ,
. pm = & snd_soc_pm_ops ,
} ,
. probe = lowland_probe ,
} ;
2011-11-23 15:20:13 +00:00
module_platform_driver ( lowland_driver ) ;
2011-08-23 17:40:01 +01:00
MODULE_DESCRIPTION ( " Lowland audio support " ) ;
MODULE_AUTHOR ( " Mark Brown <broonie@opensource.wolfsonmicro.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:lowland " ) ;