2019-05-27 08:55:21 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2014-07-14 10:35:40 +08:00
/*
* Intel Broadwell Wildcatpoint SST Audio
*
* Copyright ( C ) 2013 , Intel Corporation . All rights reserved .
*/
# include <linux/module.h>
# include <linux/platform_device.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/soc.h>
2014-10-30 21:16:23 +08:00
# include <sound/jack.h>
2014-07-14 10:35:40 +08:00
# include <sound/pcm_params.h>
2019-01-25 14:34:57 -06:00
# include <sound/soc-acpi.h>
2014-07-14 10:35:40 +08:00
2015-04-02 15:37:02 +08:00
# include "../common/sst-dsp.h"
# include "../haswell/sst-haswell-ipc.h"
2014-07-14 10:35:40 +08:00
2015-04-02 15:37:02 +08:00
# include "../../codecs/rt286.h"
2014-07-14 10:35:40 +08:00
2014-10-30 21:16:23 +08:00
static struct snd_soc_jack broadwell_headset ;
/* Headset jack detection DAPM pins */
static struct snd_soc_jack_pin broadwell_headset_pins [ ] = {
{
. pin = " Mic Jack " ,
. mask = SND_JACK_MICROPHONE ,
} ,
{
. pin = " Headphone Jack " ,
. mask = SND_JACK_HEADPHONE ,
} ,
} ;
static const struct snd_kcontrol_new broadwell_controls [ ] = {
SOC_DAPM_PIN_SWITCH ( " Speaker " ) ,
SOC_DAPM_PIN_SWITCH ( " Headphone Jack " ) ,
} ;
2014-07-14 10:35:40 +08:00
static const struct snd_soc_dapm_widget broadwell_widgets [ ] = {
2014-10-30 21:16:23 +08:00
SND_SOC_DAPM_HP ( " Headphone Jack " , NULL ) ,
2014-07-14 10:35:40 +08:00
SND_SOC_DAPM_SPK ( " Speaker " , NULL ) ,
SND_SOC_DAPM_MIC ( " Mic Jack " , NULL ) ,
SND_SOC_DAPM_MIC ( " DMIC1 " , NULL ) ,
SND_SOC_DAPM_MIC ( " DMIC2 " , NULL ) ,
SND_SOC_DAPM_LINE ( " Line Jack " , NULL ) ,
} ;
static const struct snd_soc_dapm_route broadwell_rt286_map [ ] = {
/* speaker */
{ " Speaker " , NULL , " SPOR " } ,
{ " Speaker " , NULL , " SPOL " } ,
/* HP jack connectors - unknown if we have jack deteck */
2014-10-30 21:16:23 +08:00
{ " Headphone Jack " , NULL , " HPO Pin " } ,
2014-07-14 10:35:40 +08:00
/* other jacks */
{ " MIC1 " , NULL , " Mic Jack " } ,
{ " LINE1 " , NULL , " Line Jack " } ,
/* digital mics */
{ " DMIC1 Pin " , NULL , " DMIC1 " } ,
{ " DMIC2 Pin " , NULL , " DMIC2 " } ,
/* CODEC BE connections */
{ " SSP0 CODEC IN " , NULL , " AIF1 Capture " } ,
{ " AIF1 Playback " , NULL , " SSP0 CODEC OUT " } ,
} ;
2014-10-30 21:16:23 +08:00
static int broadwell_rt286_codec_init ( struct snd_soc_pcm_runtime * rtd )
{
2020-03-23 14:19:05 +09:00
struct snd_soc_component * component = asoc_rtd_to_codec ( rtd , 0 ) - > component ;
2014-10-30 21:16:23 +08:00
int ret = 0 ;
2015-03-04 10:33:21 +01:00
ret = snd_soc_card_jack_new ( rtd - > card , " Headset " ,
SND_JACK_HEADSET | SND_JACK_BTN_0 , & broadwell_headset ,
broadwell_headset_pins , ARRAY_SIZE ( broadwell_headset_pins ) ) ;
2014-10-30 21:16:23 +08:00
if ( ret )
return ret ;
2018-01-29 04:36:54 +00:00
rt286_mic_detect ( component , & broadwell_headset ) ;
2014-10-30 21:16:23 +08:00
return 0 ;
}
2014-07-14 10:35:40 +08:00
static int broadwell_ssp0_fixup ( struct snd_soc_pcm_runtime * rtd ,
struct snd_pcm_hw_params * params )
{
struct snd_interval * rate = hw_param_interval ( params ,
2020-08-13 12:58:37 -05:00
SNDRV_PCM_HW_PARAM_RATE ) ;
struct snd_interval * chan = hw_param_interval ( params ,
SNDRV_PCM_HW_PARAM_CHANNELS ) ;
2014-07-14 10:35:40 +08:00
/* The ADSP will covert the FE rate to 48k, stereo */
rate - > min = rate - > max = 48000 ;
2020-08-13 12:58:37 -05:00
chan - > min = chan - > max = 2 ;
2014-07-14 10:35:40 +08:00
/* set SSP0 to 16 bit */
2015-02-09 00:18:12 -08:00
params_set_format ( params , SNDRV_PCM_FORMAT_S16_LE ) ;
2014-07-14 10:35:40 +08:00
return 0 ;
}
static int broadwell_rt286_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
2020-07-27 10:08:29 +09:00
struct snd_soc_pcm_runtime * rtd = asoc_substream_to_rtd ( substream ) ;
2020-03-23 14:19:05 +09:00
struct snd_soc_dai * codec_dai = asoc_rtd_to_codec ( rtd , 0 ) ;
2014-07-14 10:35:40 +08:00
int ret ;
ret = snd_soc_dai_set_sysclk ( codec_dai , RT286_SCLK_S_PLL , 24000000 ,
SND_SOC_CLOCK_IN ) ;
if ( ret < 0 ) {
dev_err ( rtd - > dev , " can't set codec sysclk configuration \n " ) ;
return ret ;
}
return ret ;
}
2016-10-15 16:55:45 +02:00
static const struct snd_soc_ops broadwell_rt286_ops = {
2014-07-14 10:35:40 +08:00
. hw_params = broadwell_rt286_hw_params ,
} ;
2019-04-12 11:09:03 -05:00
# if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
2014-07-14 10:35:40 +08:00
static int broadwell_rtd_init ( struct snd_soc_pcm_runtime * rtd )
{
2018-01-29 02:42:21 +00:00
struct snd_soc_component * component = snd_soc_rtdcom_lookup ( rtd , DRV_NAME ) ;
struct sst_pdata * pdata = dev_get_platdata ( component - > dev ) ;
2014-07-14 10:35:40 +08:00
struct sst_hsw * broadwell = pdata - > dsp ;
int ret ;
/* Set ADSP SSP port settings */
ret = sst_hsw_device_set_config ( broadwell , SST_HSW_DEVICE_SSP_0 ,
SST_HSW_DEVICE_MCLK_FREQ_24_MHZ ,
SST_HSW_DEVICE_CLOCK_MASTER , 9 ) ;
if ( ret < 0 ) {
dev_err ( rtd - > dev , " error: failed to set device config \n " ) ;
return ret ;
}
return 0 ;
}
2019-04-12 11:09:03 -05:00
# endif
2014-07-14 10:35:40 +08:00
2020-04-28 01:13:34 +08:00
static const unsigned int channels [ ] = {
2 ,
} ;
static const struct snd_pcm_hw_constraint_list constraints_channels = {
. count = ARRAY_SIZE ( channels ) ,
. list = channels ,
. mask = 0 ,
} ;
static int broadwell_fe_startup ( struct snd_pcm_substream * substream )
{
struct snd_pcm_runtime * runtime = substream - > runtime ;
/* Board supports stereo configuration only */
runtime - > hw . channels_max = 2 ;
return snd_pcm_hw_constraint_list ( runtime , 0 ,
SNDRV_PCM_HW_PARAM_CHANNELS ,
& constraints_channels ) ;
}
static const struct snd_soc_ops broadwell_fe_ops = {
. startup = broadwell_fe_startup ,
} ;
2019-06-06 13:19:40 +09:00
SND_SOC_DAILINK_DEF ( system ,
DAILINK_COMP_ARRAY ( COMP_CPU ( " System Pin " ) ) ) ;
SND_SOC_DAILINK_DEF ( offload0 ,
DAILINK_COMP_ARRAY ( COMP_CPU ( " Offload0 Pin " ) ) ) ;
SND_SOC_DAILINK_DEF ( offload1 ,
DAILINK_COMP_ARRAY ( COMP_CPU ( " Offload1 Pin " ) ) ) ;
SND_SOC_DAILINK_DEF ( loopback ,
DAILINK_COMP_ARRAY ( COMP_CPU ( " Loopback Pin " ) ) ) ;
SND_SOC_DAILINK_DEF ( dummy ,
DAILINK_COMP_ARRAY ( COMP_DUMMY ( ) ) ) ;
SND_SOC_DAILINK_DEF ( platform ,
DAILINK_COMP_ARRAY ( COMP_PLATFORM ( " haswell-pcm-audio " ) ) ) ;
SND_SOC_DAILINK_DEF ( codec ,
DAILINK_COMP_ARRAY ( COMP_CODEC ( " i2c-INT343A:00 " , " rt286-aif1 " ) ) ) ;
2020-01-10 17:57:46 -06:00
# if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
SND_SOC_DAILINK_DEF ( ssp0_port ,
DAILINK_COMP_ARRAY ( COMP_CPU ( " ssp0-port " ) ) ) ;
# endif
2014-07-14 10:35:40 +08:00
/* broadwell digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link broadwell_rt286_dais [ ] = {
/* Front End DAI links */
{
. name = " System PCM " ,
2014-11-28 21:52:11 +08:00
. stream_name = " System Playback/Capture " ,
2014-07-14 10:35:40 +08:00
. dynamic = 1 ,
2019-04-12 11:09:03 -05:00
# if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
2014-07-14 10:35:40 +08:00
. init = broadwell_rtd_init ,
2019-04-12 11:09:03 -05:00
# endif
2014-07-14 10:35:40 +08:00
. trigger = { SND_SOC_DPCM_TRIGGER_POST , SND_SOC_DPCM_TRIGGER_POST } ,
2020-04-28 01:13:34 +08:00
. ops = & broadwell_fe_ops ,
2014-07-14 10:35:40 +08:00
. dpcm_playback = 1 ,
2014-11-28 21:52:11 +08:00
. dpcm_capture = 1 ,
2019-06-06 13:19:40 +09:00
SND_SOC_DAILINK_REG ( system , dummy , platform ) ,
2014-07-14 10:35:40 +08:00
} ,
{
. name = " Offload0 " ,
. stream_name = " Offload0 Playback " ,
. dynamic = 1 ,
. trigger = { SND_SOC_DPCM_TRIGGER_POST , SND_SOC_DPCM_TRIGGER_POST } ,
. dpcm_playback = 1 ,
2019-06-06 13:19:40 +09:00
SND_SOC_DAILINK_REG ( offload0 , dummy , platform ) ,
2014-07-14 10:35:40 +08:00
} ,
{
. name = " Offload1 " ,
. stream_name = " Offload1 Playback " ,
. dynamic = 1 ,
. trigger = { SND_SOC_DPCM_TRIGGER_POST , SND_SOC_DPCM_TRIGGER_POST } ,
. dpcm_playback = 1 ,
2019-06-06 13:19:40 +09:00
SND_SOC_DAILINK_REG ( offload1 , dummy , platform ) ,
2014-07-14 10:35:40 +08:00
} ,
{
. name = " Loopback PCM " ,
. stream_name = " Loopback " ,
2018-12-18 16:24:54 +08:00
. dynamic = 1 ,
2014-07-14 10:35:40 +08:00
. trigger = { SND_SOC_DPCM_TRIGGER_POST , SND_SOC_DPCM_TRIGGER_POST } ,
. dpcm_capture = 1 ,
2019-06-06 13:19:40 +09:00
SND_SOC_DAILINK_REG ( loopback , dummy , platform ) ,
2014-07-14 10:35:40 +08:00
} ,
/* Back End DAI links */
{
/* SSP0 - Codec */
. name = " Codec " ,
2016-04-19 13:12:35 +08:00
. id = 0 ,
2014-07-14 10:35:40 +08:00
. no_pcm = 1 ,
2014-10-30 21:16:23 +08:00
. init = broadwell_rt286_codec_init ,
2014-07-14 10:35:40 +08:00
. dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS ,
. ignore_pmdown_time = 1 ,
. be_hw_params_fixup = broadwell_ssp0_fixup ,
. ops = & broadwell_rt286_ops ,
. dpcm_playback = 1 ,
. dpcm_capture = 1 ,
2020-03-25 14:16:09 +01:00
# if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
SND_SOC_DAILINK_REG ( dummy , codec , dummy ) ,
# else
2020-01-10 17:57:46 -06:00
SND_SOC_DAILINK_REG ( ssp0_port , codec , platform ) ,
2020-03-25 14:16:09 +01:00
# endif
2014-07-14 10:35:40 +08:00
} ,
} ;
2020-04-27 09:29:53 -07:00
static int broadwell_disable_jack ( struct snd_soc_card * card )
{
2016-11-30 06:22:36 +00:00
struct snd_soc_component * component ;
2018-09-18 01:29:55 +00:00
for_each_card_components ( card , component ) {
2016-11-30 06:22:36 +00:00
if ( ! strcmp ( component - > name , " i2c-INT343A:00 " ) ) {
2015-02-27 12:54:29 +08:00
2018-01-29 04:36:54 +00:00
dev_dbg ( component - > dev , " disabling jack detect before going to suspend. \n " ) ;
rt286_mic_detect ( component , NULL ) ;
2015-02-27 12:54:29 +08:00
break ;
}
}
2020-04-27 09:29:53 -07:00
2015-02-27 12:54:29 +08:00
return 0 ;
}
2020-04-27 09:29:53 -07:00
static int broadwell_suspend ( struct snd_soc_card * card )
{
return broadwell_disable_jack ( card ) ;
}
2015-02-27 12:54:29 +08:00
static int broadwell_resume ( struct snd_soc_card * card ) {
2016-11-30 06:22:36 +00:00
struct snd_soc_component * component ;
2018-09-18 01:29:55 +00:00
for_each_card_components ( card , component ) {
2016-11-30 06:22:36 +00:00
if ( ! strcmp ( component - > name , " i2c-INT343A:00 " ) ) {
2015-02-27 12:54:29 +08:00
2018-01-29 04:36:54 +00:00
dev_dbg ( component - > dev , " enabling jack detect for resume. \n " ) ;
rt286_mic_detect ( component , & broadwell_headset ) ;
2015-02-27 12:54:29 +08:00
break ;
}
}
return 0 ;
}
2020-06-17 11:56:16 -05:00
# if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
/* use space before codec name to simplify card ID, and simplify driver name */
# define CARD_NAME "bdw rt286" /* card name will be 'sof-bdw rt286' */
# define DRIVER_NAME "SOF"
# else
# define CARD_NAME "broadwell-rt286"
# define DRIVER_NAME NULL /* card name will be used for driver name */
# endif
2014-07-14 10:35:40 +08:00
/* broadwell audio machine driver for WPT + RT286S */
static struct snd_soc_card broadwell_rt286 = {
2020-06-17 11:56:16 -05:00
. name = CARD_NAME ,
. driver_name = DRIVER_NAME ,
2014-07-14 10:35:40 +08:00
. owner = THIS_MODULE ,
. dai_link = broadwell_rt286_dais ,
. num_links = ARRAY_SIZE ( broadwell_rt286_dais ) ,
2014-10-30 21:16:23 +08:00
. controls = broadwell_controls ,
. num_controls = ARRAY_SIZE ( broadwell_controls ) ,
2014-07-14 10:35:40 +08:00
. dapm_widgets = broadwell_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( broadwell_widgets ) ,
. dapm_routes = broadwell_rt286_map ,
. num_dapm_routes = ARRAY_SIZE ( broadwell_rt286_map ) ,
. fully_routed = true ,
2015-02-27 12:54:29 +08:00
. suspend_pre = broadwell_suspend ,
. resume_post = broadwell_resume ,
2014-07-14 10:35:40 +08:00
} ;
static int broadwell_audio_probe ( struct platform_device * pdev )
{
2019-01-25 14:34:57 -06:00
struct snd_soc_acpi_mach * mach ;
int ret ;
2014-07-14 10:35:40 +08:00
broadwell_rt286 . dev = & pdev - > dev ;
2019-01-25 14:34:57 -06:00
/* override plaform name, if required */
2020-03-12 14:48:56 -05:00
mach = pdev - > dev . platform_data ;
2019-01-25 14:34:57 -06:00
ret = snd_soc_fixup_dai_links_platform_name ( & broadwell_rt286 ,
2019-08-22 13:36:16 +02:00
mach - > mach_params . platform ) ;
2019-01-25 14:34:57 -06:00
if ( ret )
return ret ;
2015-09-02 12:01:27 +08:00
return devm_snd_soc_register_card ( & pdev - > dev , & broadwell_rt286 ) ;
2014-07-14 10:35:40 +08:00
}
2020-04-27 09:29:53 -07:00
static int broadwell_audio_remove ( struct platform_device * pdev )
{
struct snd_soc_card * card = platform_get_drvdata ( pdev ) ;
return broadwell_disable_jack ( card ) ;
}
2014-07-14 10:35:40 +08:00
static struct platform_driver broadwell_audio = {
. probe = broadwell_audio_probe ,
2020-04-27 09:29:53 -07:00
. remove = broadwell_audio_remove ,
2014-07-14 10:35:40 +08:00
. driver = {
. name = " broadwell-audio " ,
} ,
} ;
module_platform_driver ( broadwell_audio )
/* Module information */
MODULE_AUTHOR ( " Liam Girdwood, Xingchao Wang " ) ;
MODULE_DESCRIPTION ( " Intel SST Audio for WPT/Broadwell " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:broadwell-audio " ) ;