1 // SPDX-License-Identifier: GPL-2.0-only
3 * Intel Broadwell Wildcatpoint SST Audio
5 * Copyright (C) 2013, Intel Corporation. All rights reserved.
8 #include <linux/module.h>
9 #include <linux/platform_device.h>
10 #include <sound/core.h>
11 #include <sound/pcm.h>
12 #include <sound/soc.h>
13 #include <sound/jack.h>
14 #include <sound/pcm_params.h>
15 #include <sound/soc-acpi.h>
17 #include "../../codecs/rt286.h"
19 static struct snd_soc_jack broadwell_headset
;
20 /* Headset jack detection DAPM pins */
21 static struct snd_soc_jack_pin broadwell_headset_pins
[] = {
24 .mask
= SND_JACK_MICROPHONE
,
27 .pin
= "Headphone Jack",
28 .mask
= SND_JACK_HEADPHONE
,
32 static const struct snd_kcontrol_new broadwell_controls
[] = {
33 SOC_DAPM_PIN_SWITCH("Speaker"),
34 SOC_DAPM_PIN_SWITCH("Headphone Jack"),
37 static const struct snd_soc_dapm_widget broadwell_widgets
[] = {
38 SND_SOC_DAPM_HP("Headphone Jack", NULL
),
39 SND_SOC_DAPM_SPK("Speaker", NULL
),
40 SND_SOC_DAPM_MIC("Mic Jack", NULL
),
41 SND_SOC_DAPM_MIC("DMIC1", NULL
),
42 SND_SOC_DAPM_MIC("DMIC2", NULL
),
43 SND_SOC_DAPM_LINE("Line Jack", NULL
),
46 static const struct snd_soc_dapm_route broadwell_rt286_map
[] = {
49 {"Speaker", NULL
, "SPOR"},
50 {"Speaker", NULL
, "SPOL"},
52 /* HP jack connectors - unknown if we have jack deteck */
53 {"Headphone Jack", NULL
, "HPO Pin"},
56 {"MIC1", NULL
, "Mic Jack"},
57 {"LINE1", NULL
, "Line Jack"},
60 {"DMIC1 Pin", NULL
, "DMIC1"},
61 {"DMIC2 Pin", NULL
, "DMIC2"},
63 /* CODEC BE connections */
64 {"SSP0 CODEC IN", NULL
, "AIF1 Capture"},
65 {"AIF1 Playback", NULL
, "SSP0 CODEC OUT"},
68 static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime
*rtd
)
70 struct snd_soc_component
*component
= asoc_rtd_to_codec(rtd
, 0)->component
;
72 ret
= snd_soc_card_jack_new(rtd
->card
, "Headset",
73 SND_JACK_HEADSET
| SND_JACK_BTN_0
, &broadwell_headset
,
74 broadwell_headset_pins
, ARRAY_SIZE(broadwell_headset_pins
));
78 rt286_mic_detect(component
, &broadwell_headset
);
83 static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime
*rtd
,
84 struct snd_pcm_hw_params
*params
)
86 struct snd_interval
*rate
= hw_param_interval(params
,
87 SNDRV_PCM_HW_PARAM_RATE
);
88 struct snd_interval
*chan
= hw_param_interval(params
,
89 SNDRV_PCM_HW_PARAM_CHANNELS
);
91 /* The ADSP will covert the FE rate to 48k, stereo */
92 rate
->min
= rate
->max
= 48000;
93 chan
->min
= chan
->max
= 2;
95 /* set SSP0 to 16 bit */
96 params_set_format(params
, SNDRV_PCM_FORMAT_S16_LE
);
100 static int broadwell_rt286_hw_params(struct snd_pcm_substream
*substream
,
101 struct snd_pcm_hw_params
*params
)
103 struct snd_soc_pcm_runtime
*rtd
= asoc_substream_to_rtd(substream
);
104 struct snd_soc_dai
*codec_dai
= asoc_rtd_to_codec(rtd
, 0);
107 ret
= snd_soc_dai_set_sysclk(codec_dai
, RT286_SCLK_S_PLL
, 24000000,
111 dev_err(rtd
->dev
, "can't set codec sysclk configuration\n");
118 static const struct snd_soc_ops broadwell_rt286_ops
= {
119 .hw_params
= broadwell_rt286_hw_params
,
122 static const unsigned int channels
[] = {
126 static const struct snd_pcm_hw_constraint_list constraints_channels
= {
127 .count
= ARRAY_SIZE(channels
),
132 static int broadwell_fe_startup(struct snd_pcm_substream
*substream
)
134 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
136 /* Board supports stereo configuration only */
137 runtime
->hw
.channels_max
= 2;
138 return snd_pcm_hw_constraint_list(runtime
, 0,
139 SNDRV_PCM_HW_PARAM_CHANNELS
,
140 &constraints_channels
);
143 static const struct snd_soc_ops broadwell_fe_ops
= {
144 .startup
= broadwell_fe_startup
,
147 SND_SOC_DAILINK_DEF(system
,
148 DAILINK_COMP_ARRAY(COMP_CPU("System Pin")));
150 SND_SOC_DAILINK_DEF(offload0
,
151 DAILINK_COMP_ARRAY(COMP_CPU("Offload0 Pin")));
153 SND_SOC_DAILINK_DEF(offload1
,
154 DAILINK_COMP_ARRAY(COMP_CPU("Offload1 Pin")));
156 SND_SOC_DAILINK_DEF(loopback
,
157 DAILINK_COMP_ARRAY(COMP_CPU("Loopback Pin")));
159 SND_SOC_DAILINK_DEF(dummy
,
160 DAILINK_COMP_ARRAY(COMP_DUMMY()));
162 SND_SOC_DAILINK_DEF(platform
,
163 DAILINK_COMP_ARRAY(COMP_PLATFORM("haswell-pcm-audio")));
165 SND_SOC_DAILINK_DEF(codec
,
166 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-INT343A:00", "rt286-aif1")));
168 SND_SOC_DAILINK_DEF(ssp0_port
,
169 DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port")));
171 /* broadwell digital audio interface glue - connects codec <--> CPU */
172 static struct snd_soc_dai_link broadwell_rt286_dais
[] = {
173 /* Front End DAI links */
175 .name
= "System PCM",
176 .stream_name
= "System Playback/Capture",
179 .trigger
= {SND_SOC_DPCM_TRIGGER_POST
, SND_SOC_DPCM_TRIGGER_POST
},
180 .ops
= &broadwell_fe_ops
,
183 SND_SOC_DAILINK_REG(system
, dummy
, platform
),
187 .stream_name
= "Offload0 Playback",
190 .trigger
= {SND_SOC_DPCM_TRIGGER_POST
, SND_SOC_DPCM_TRIGGER_POST
},
192 SND_SOC_DAILINK_REG(offload0
, dummy
, platform
),
196 .stream_name
= "Offload1 Playback",
199 .trigger
= {SND_SOC_DPCM_TRIGGER_POST
, SND_SOC_DPCM_TRIGGER_POST
},
201 SND_SOC_DAILINK_REG(offload1
, dummy
, platform
),
204 .name
= "Loopback PCM",
205 .stream_name
= "Loopback",
208 .trigger
= {SND_SOC_DPCM_TRIGGER_POST
, SND_SOC_DPCM_TRIGGER_POST
},
210 SND_SOC_DAILINK_REG(loopback
, dummy
, platform
),
212 /* Back End DAI links */
218 .init
= broadwell_rt286_codec_init
,
219 .dai_fmt
= SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
|
220 SND_SOC_DAIFMT_CBS_CFS
,
221 .ignore_pmdown_time
= 1,
222 .be_hw_params_fixup
= broadwell_ssp0_fixup
,
223 .ops
= &broadwell_rt286_ops
,
226 SND_SOC_DAILINK_REG(ssp0_port
, codec
, platform
),
230 static int broadwell_disable_jack(struct snd_soc_card
*card
)
232 struct snd_soc_component
*component
;
234 for_each_card_components(card
, component
) {
235 if (!strcmp(component
->name
, "i2c-INT343A:00")) {
237 dev_dbg(component
->dev
, "disabling jack detect before going to suspend.\n");
238 rt286_mic_detect(component
, NULL
);
246 static int broadwell_suspend(struct snd_soc_card
*card
)
248 return broadwell_disable_jack(card
);
251 static int broadwell_resume(struct snd_soc_card
*card
){
252 struct snd_soc_component
*component
;
254 for_each_card_components(card
, component
) {
255 if (!strcmp(component
->name
, "i2c-INT343A:00")) {
257 dev_dbg(component
->dev
, "enabling jack detect for resume.\n");
258 rt286_mic_detect(component
, &broadwell_headset
);
265 /* use space before codec name to simplify card ID, and simplify driver name */
266 #define SOF_CARD_NAME "bdw rt286" /* card name will be 'sof-bdw rt286' */
267 #define SOF_DRIVER_NAME "SOF"
269 #define CARD_NAME "broadwell-rt286"
270 #define DRIVER_NAME NULL /* card name will be used for driver name */
272 /* broadwell audio machine driver for WPT + RT286S */
273 static struct snd_soc_card broadwell_rt286
= {
274 .owner
= THIS_MODULE
,
275 .dai_link
= broadwell_rt286_dais
,
276 .num_links
= ARRAY_SIZE(broadwell_rt286_dais
),
277 .controls
= broadwell_controls
,
278 .num_controls
= ARRAY_SIZE(broadwell_controls
),
279 .dapm_widgets
= broadwell_widgets
,
280 .num_dapm_widgets
= ARRAY_SIZE(broadwell_widgets
),
281 .dapm_routes
= broadwell_rt286_map
,
282 .num_dapm_routes
= ARRAY_SIZE(broadwell_rt286_map
),
283 .fully_routed
= true,
284 .suspend_pre
= broadwell_suspend
,
285 .resume_post
= broadwell_resume
,
288 static int broadwell_audio_probe(struct platform_device
*pdev
)
290 struct snd_soc_acpi_mach
*mach
;
293 broadwell_rt286
.dev
= &pdev
->dev
;
295 /* override plaform name, if required */
296 mach
= pdev
->dev
.platform_data
;
297 ret
= snd_soc_fixup_dai_links_platform_name(&broadwell_rt286
,
298 mach
->mach_params
.platform
);
302 /* set card and driver name */
303 if (snd_soc_acpi_sof_parent(&pdev
->dev
)) {
304 broadwell_rt286
.name
= SOF_CARD_NAME
;
305 broadwell_rt286
.driver_name
= SOF_DRIVER_NAME
;
307 broadwell_rt286
.name
= CARD_NAME
;
308 broadwell_rt286
.driver_name
= DRIVER_NAME
;
311 return devm_snd_soc_register_card(&pdev
->dev
, &broadwell_rt286
);
314 static int broadwell_audio_remove(struct platform_device
*pdev
)
316 struct snd_soc_card
*card
= platform_get_drvdata(pdev
);
318 return broadwell_disable_jack(card
);
321 static struct platform_driver broadwell_audio
= {
322 .probe
= broadwell_audio_probe
,
323 .remove
= broadwell_audio_remove
,
325 .name
= "broadwell-audio",
326 .pm
= &snd_soc_pm_ops
330 module_platform_driver(broadwell_audio
)
332 /* Module information */
333 MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
334 MODULE_DESCRIPTION("Intel SST Audio for WPT/Broadwell");
335 MODULE_LICENSE("GPL v2");
336 MODULE_ALIAS("platform:broadwell-audio");