• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Intel Broadwell Wildcatpoint SST Audio
4  *
5  * Copyright (C) 2013, Intel Corporation. All rights reserved.
6  */
7 
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>
16 
17 #include "../common/sst-dsp.h"
18 #include "../haswell/sst-haswell-ipc.h"
19 
20 #include "../../codecs/rt286.h"
21 
22 static struct snd_soc_jack broadwell_headset;
23 /* Headset jack detection DAPM pins */
24 static struct snd_soc_jack_pin broadwell_headset_pins[] = {
25 	{
26 		.pin = "Mic Jack",
27 		.mask = SND_JACK_MICROPHONE,
28 	},
29 	{
30 		.pin = "Headphone Jack",
31 		.mask = SND_JACK_HEADPHONE,
32 	},
33 };
34 
35 static const struct snd_kcontrol_new broadwell_controls[] = {
36 	SOC_DAPM_PIN_SWITCH("Speaker"),
37 	SOC_DAPM_PIN_SWITCH("Headphone Jack"),
38 };
39 
40 static const struct snd_soc_dapm_widget broadwell_widgets[] = {
41 	SND_SOC_DAPM_HP("Headphone Jack", NULL),
42 	SND_SOC_DAPM_SPK("Speaker", NULL),
43 	SND_SOC_DAPM_MIC("Mic Jack", NULL),
44 	SND_SOC_DAPM_MIC("DMIC1", NULL),
45 	SND_SOC_DAPM_MIC("DMIC2", NULL),
46 	SND_SOC_DAPM_LINE("Line Jack", NULL),
47 };
48 
49 static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
50 
51 	/* speaker */
52 	{"Speaker", NULL, "SPOR"},
53 	{"Speaker", NULL, "SPOL"},
54 
55 	/* HP jack connectors - unknown if we have jack deteck */
56 	{"Headphone Jack", NULL, "HPO Pin"},
57 
58 	/* other jacks */
59 	{"MIC1", NULL, "Mic Jack"},
60 	{"LINE1", NULL, "Line Jack"},
61 
62 	/* digital mics */
63 	{"DMIC1 Pin", NULL, "DMIC1"},
64 	{"DMIC2 Pin", NULL, "DMIC2"},
65 
66 	/* CODEC BE connections */
67 	{"SSP0 CODEC IN", NULL, "AIF1 Capture"},
68 	{"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
69 };
70 
broadwell_rt286_codec_init(struct snd_soc_pcm_runtime * rtd)71 static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
72 {
73 	struct snd_soc_component *component = rtd->codec_dai->component;
74 	int ret = 0;
75 	ret = snd_soc_card_jack_new(rtd->card, "Headset",
76 		SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset,
77 		broadwell_headset_pins, ARRAY_SIZE(broadwell_headset_pins));
78 	if (ret)
79 		return ret;
80 
81 	rt286_mic_detect(component, &broadwell_headset);
82 	return 0;
83 }
84 
85 
broadwell_ssp0_fixup(struct snd_soc_pcm_runtime * rtd,struct snd_pcm_hw_params * params)86 static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
87 			struct snd_pcm_hw_params *params)
88 {
89 	struct snd_interval *rate = hw_param_interval(params,
90 			SNDRV_PCM_HW_PARAM_RATE);
91 	struct snd_interval *channels = hw_param_interval(params,
92 						SNDRV_PCM_HW_PARAM_CHANNELS);
93 
94 	/* The ADSP will covert the FE rate to 48k, stereo */
95 	rate->min = rate->max = 48000;
96 	channels->min = channels->max = 2;
97 
98 	/* set SSP0 to 16 bit */
99 	params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
100 	return 0;
101 }
102 
broadwell_rt286_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)103 static int broadwell_rt286_hw_params(struct snd_pcm_substream *substream,
104 	struct snd_pcm_hw_params *params)
105 {
106 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
107 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
108 	int ret;
109 
110 	ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000,
111 		SND_SOC_CLOCK_IN);
112 
113 	if (ret < 0) {
114 		dev_err(rtd->dev, "can't set codec sysclk configuration\n");
115 		return ret;
116 	}
117 
118 	return ret;
119 }
120 
121 static const struct snd_soc_ops broadwell_rt286_ops = {
122 	.hw_params = broadwell_rt286_hw_params,
123 };
124 
125 #if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
broadwell_rtd_init(struct snd_soc_pcm_runtime * rtd)126 static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd)
127 {
128 	struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
129 	struct sst_pdata *pdata = dev_get_platdata(component->dev);
130 	struct sst_hsw *broadwell = pdata->dsp;
131 	int ret;
132 
133 	/* Set ADSP SSP port settings */
134 	ret = sst_hsw_device_set_config(broadwell, SST_HSW_DEVICE_SSP_0,
135 		SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
136 		SST_HSW_DEVICE_CLOCK_MASTER, 9);
137 	if (ret < 0) {
138 		dev_err(rtd->dev, "error: failed to set device config\n");
139 		return ret;
140 	}
141 
142 	return 0;
143 }
144 #endif
145 
146 SND_SOC_DAILINK_DEF(system,
147 	DAILINK_COMP_ARRAY(COMP_CPU("System Pin")));
148 
149 SND_SOC_DAILINK_DEF(offload0,
150 	DAILINK_COMP_ARRAY(COMP_CPU("Offload0 Pin")));
151 
152 SND_SOC_DAILINK_DEF(offload1,
153 	DAILINK_COMP_ARRAY(COMP_CPU("Offload1 Pin")));
154 
155 SND_SOC_DAILINK_DEF(loopback,
156 	DAILINK_COMP_ARRAY(COMP_CPU("Loopback Pin")));
157 
158 SND_SOC_DAILINK_DEF(dummy,
159 	DAILINK_COMP_ARRAY(COMP_DUMMY()));
160 
161 SND_SOC_DAILINK_DEF(platform,
162 	DAILINK_COMP_ARRAY(COMP_PLATFORM("haswell-pcm-audio")));
163 
164 SND_SOC_DAILINK_DEF(codec,
165 	DAILINK_COMP_ARRAY(COMP_CODEC("i2c-INT343A:00", "rt286-aif1")));
166 
167 /* broadwell digital audio interface glue - connects codec <--> CPU */
168 static struct snd_soc_dai_link broadwell_rt286_dais[] = {
169 	/* Front End DAI links */
170 	{
171 		.name = "System PCM",
172 		.stream_name = "System Playback/Capture",
173 		.dynamic = 1,
174 #if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
175 		.init = broadwell_rtd_init,
176 #endif
177 		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
178 		.dpcm_playback = 1,
179 		.dpcm_capture = 1,
180 		SND_SOC_DAILINK_REG(system, dummy, platform),
181 	},
182 	{
183 		.name = "Offload0",
184 		.stream_name = "Offload0 Playback",
185 		.dynamic = 1,
186 		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
187 		.dpcm_playback = 1,
188 		SND_SOC_DAILINK_REG(offload0, dummy, platform),
189 	},
190 	{
191 		.name = "Offload1",
192 		.stream_name = "Offload1 Playback",
193 		.dynamic = 1,
194 		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
195 		.dpcm_playback = 1,
196 		SND_SOC_DAILINK_REG(offload1, dummy, platform),
197 	},
198 	{
199 		.name = "Loopback PCM",
200 		.stream_name = "Loopback",
201 		.dynamic = 1,
202 		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
203 		.dpcm_capture = 1,
204 		SND_SOC_DAILINK_REG(loopback, dummy, platform),
205 	},
206 	/* Back End DAI links */
207 	{
208 		/* SSP0 - Codec */
209 		.name = "Codec",
210 		.id = 0,
211 		.no_pcm = 1,
212 		.init = broadwell_rt286_codec_init,
213 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
214 			SND_SOC_DAIFMT_CBS_CFS,
215 		.ignore_suspend = 1,
216 		.ignore_pmdown_time = 1,
217 		.be_hw_params_fixup = broadwell_ssp0_fixup,
218 		.ops = &broadwell_rt286_ops,
219 		.dpcm_playback = 1,
220 		.dpcm_capture = 1,
221 		SND_SOC_DAILINK_REG(dummy, codec, dummy),
222 	},
223 };
224 
broadwell_suspend(struct snd_soc_card * card)225 static int broadwell_suspend(struct snd_soc_card *card){
226 	struct snd_soc_component *component;
227 
228 	for_each_card_components(card, component) {
229 		if (!strcmp(component->name, "i2c-INT343A:00")) {
230 
231 			dev_dbg(component->dev, "disabling jack detect before going to suspend.\n");
232 			rt286_mic_detect(component, NULL);
233 			break;
234 		}
235 	}
236 	return 0;
237 }
238 
broadwell_resume(struct snd_soc_card * card)239 static int broadwell_resume(struct snd_soc_card *card){
240 	struct snd_soc_component *component;
241 
242 	for_each_card_components(card, component) {
243 		if (!strcmp(component->name, "i2c-INT343A:00")) {
244 
245 			dev_dbg(component->dev, "enabling jack detect for resume.\n");
246 			rt286_mic_detect(component, &broadwell_headset);
247 			break;
248 		}
249 	}
250 	return 0;
251 }
252 
253 /* broadwell audio machine driver for WPT + RT286S */
254 static struct snd_soc_card broadwell_rt286 = {
255 	.name = "broadwell-rt286",
256 	.owner = THIS_MODULE,
257 	.dai_link = broadwell_rt286_dais,
258 	.num_links = ARRAY_SIZE(broadwell_rt286_dais),
259 	.controls = broadwell_controls,
260 	.num_controls = ARRAY_SIZE(broadwell_controls),
261 	.dapm_widgets = broadwell_widgets,
262 	.num_dapm_widgets = ARRAY_SIZE(broadwell_widgets),
263 	.dapm_routes = broadwell_rt286_map,
264 	.num_dapm_routes = ARRAY_SIZE(broadwell_rt286_map),
265 	.fully_routed = true,
266 	.suspend_pre = broadwell_suspend,
267 	.resume_post = broadwell_resume,
268 };
269 
broadwell_audio_probe(struct platform_device * pdev)270 static int broadwell_audio_probe(struct platform_device *pdev)
271 {
272 	struct snd_soc_acpi_mach *mach;
273 	int ret;
274 
275 	broadwell_rt286.dev = &pdev->dev;
276 
277 	/* override plaform name, if required */
278 	mach = (&pdev->dev)->platform_data;
279 	ret = snd_soc_fixup_dai_links_platform_name(&broadwell_rt286,
280 						    mach->mach_params.platform);
281 	if (ret)
282 		return ret;
283 
284 	return devm_snd_soc_register_card(&pdev->dev, &broadwell_rt286);
285 }
286 
287 static struct platform_driver broadwell_audio = {
288 	.probe = broadwell_audio_probe,
289 	.driver = {
290 		.name = "broadwell-audio",
291 	},
292 };
293 
294 module_platform_driver(broadwell_audio)
295 
296 /* Module information */
297 MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
298 MODULE_DESCRIPTION("Intel SST Audio for WPT/Broadwell");
299 MODULE_LICENSE("GPL v2");
300 MODULE_ALIAS("platform:broadwell-audio");
301