1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // |
3 | // Copyright (c) 2020, The Linux Foundation. All rights reserved. |
4 | // |
5 | // sc7180.c -- ALSA SoC Machine driver for SC7180 |
6 | |
7 | #include <dt-bindings/sound/sc7180-lpass.h> |
8 | #include <dt-bindings/sound/qcom,q6afe.h> |
9 | #include <linux/gpio/consumer.h> |
10 | #include <linux/module.h> |
11 | #include <linux/of.h> |
12 | #include <linux/platform_device.h> |
13 | #include <sound/core.h> |
14 | #include <sound/jack.h> |
15 | #include <sound/pcm.h> |
16 | #include <sound/soc.h> |
17 | #include <uapi/linux/input-event-codes.h> |
18 | |
19 | #include "../codecs/rt5682.h" |
20 | #include "../codecs/rt5682s.h" |
21 | #include "common.h" |
22 | #include "qdsp6/q6afe.h" |
23 | |
24 | #define DEFAULT_MCLK_RATE 19200000 |
25 | #define MI2S_BCLK_RATE 1536000 |
26 | #define RT5682_PLL1_FREQ (48000 * 512) |
27 | |
28 | #define DRIVER_NAME "SC7180" |
29 | |
30 | struct sc7180_snd_data { |
31 | struct snd_soc_card card; |
32 | u32 pri_mi2s_clk_count; |
33 | struct snd_soc_jack hs_jack; |
34 | struct snd_soc_jack hdmi_jack; |
35 | struct gpio_desc *dmic_sel; |
36 | int dmic_switch; |
37 | }; |
38 | |
39 | static void sc7180_jack_free(struct snd_jack *jack) |
40 | { |
41 | struct snd_soc_component *component = jack->private_data; |
42 | |
43 | snd_soc_component_set_jack(component, NULL, NULL); |
44 | } |
45 | |
46 | static struct snd_soc_jack_pin sc7180_jack_pins[] = { |
47 | { |
48 | .pin = "Headphone Jack" , |
49 | .mask = SND_JACK_HEADPHONE, |
50 | }, |
51 | { |
52 | .pin = "Headset Mic" , |
53 | .mask = SND_JACK_MICROPHONE, |
54 | }, |
55 | }; |
56 | |
57 | static int sc7180_headset_init(struct snd_soc_pcm_runtime *rtd) |
58 | { |
59 | struct snd_soc_card *card = rtd->card; |
60 | struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card); |
61 | struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); |
62 | struct snd_soc_component *component = codec_dai->component; |
63 | struct snd_jack *jack; |
64 | int rval; |
65 | |
66 | rval = snd_soc_card_jack_new_pins(card, id: "Headset Jack" , |
67 | type: SND_JACK_HEADSET | |
68 | SND_JACK_HEADPHONE | |
69 | SND_JACK_BTN_0 | SND_JACK_BTN_1 | |
70 | SND_JACK_BTN_2 | SND_JACK_BTN_3, |
71 | jack: &pdata->hs_jack, |
72 | pins: sc7180_jack_pins, |
73 | ARRAY_SIZE(sc7180_jack_pins)); |
74 | |
75 | if (rval < 0) { |
76 | dev_err(card->dev, "Unable to add Headset Jack\n" ); |
77 | return rval; |
78 | } |
79 | |
80 | jack = pdata->hs_jack.jack; |
81 | |
82 | snd_jack_set_key(jack, type: SND_JACK_BTN_0, KEY_PLAYPAUSE); |
83 | snd_jack_set_key(jack, type: SND_JACK_BTN_1, KEY_VOICECOMMAND); |
84 | snd_jack_set_key(jack, type: SND_JACK_BTN_2, KEY_VOLUMEUP); |
85 | snd_jack_set_key(jack, type: SND_JACK_BTN_3, KEY_VOLUMEDOWN); |
86 | |
87 | jack->private_data = component; |
88 | jack->private_free = sc7180_jack_free; |
89 | |
90 | return snd_soc_component_set_jack(component, jack: &pdata->hs_jack, NULL); |
91 | } |
92 | |
93 | static int sc7180_hdmi_init(struct snd_soc_pcm_runtime *rtd) |
94 | { |
95 | struct snd_soc_card *card = rtd->card; |
96 | struct sc7180_snd_data *pdata = snd_soc_card_get_drvdata(card); |
97 | struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); |
98 | struct snd_soc_component *component = codec_dai->component; |
99 | struct snd_jack *jack; |
100 | int rval; |
101 | |
102 | rval = snd_soc_card_jack_new( |
103 | card, id: "HDMI Jack" , |
104 | type: SND_JACK_LINEOUT, |
105 | jack: &pdata->hdmi_jack); |
106 | |
107 | if (rval < 0) { |
108 | dev_err(card->dev, "Unable to add HDMI Jack\n" ); |
109 | return rval; |
110 | } |
111 | |
112 | jack = pdata->hdmi_jack.jack; |
113 | jack->private_data = component; |
114 | jack->private_free = sc7180_jack_free; |
115 | |
116 | return snd_soc_component_set_jack(component, jack: &pdata->hdmi_jack, NULL); |
117 | } |
118 | |
119 | static int sc7180_init(struct snd_soc_pcm_runtime *rtd) |
120 | { |
121 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
122 | |
123 | switch (cpu_dai->id) { |
124 | case MI2S_PRIMARY: |
125 | return sc7180_headset_init(rtd); |
126 | case MI2S_SECONDARY: |
127 | return 0; |
128 | case LPASS_DP_RX: |
129 | return sc7180_hdmi_init(rtd); |
130 | default: |
131 | dev_err(rtd->dev, "%s: invalid dai id 0x%x\n" , __func__, |
132 | cpu_dai->id); |
133 | return -EINVAL; |
134 | } |
135 | return 0; |
136 | } |
137 | |
138 | static int sc7180_qdsp_init(struct snd_soc_pcm_runtime *rtd) |
139 | { |
140 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
141 | |
142 | switch (cpu_dai->id) { |
143 | case PRIMARY_MI2S_RX: |
144 | return sc7180_headset_init(rtd); |
145 | case PRIMARY_MI2S_TX: |
146 | case TERTIARY_MI2S_RX: |
147 | return 0; |
148 | case DISPLAY_PORT_RX: |
149 | return sc7180_hdmi_init(rtd); |
150 | default: |
151 | dev_err(rtd->dev, "%s: invalid dai id 0x%x\n" , __func__, |
152 | cpu_dai->id); |
153 | return -EINVAL; |
154 | } |
155 | return 0; |
156 | } |
157 | |
158 | static int sc7180_startup_realtek_codec(struct snd_soc_pcm_runtime *rtd) |
159 | { |
160 | struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); |
161 | int pll_id, pll_source, pll_in, pll_out, clk_id, ret; |
162 | |
163 | if (!strcmp(codec_dai->name, "rt5682-aif1" )) { |
164 | pll_source = RT5682_PLL1_S_MCLK; |
165 | pll_id = 0; |
166 | clk_id = RT5682_SCLK_S_PLL1; |
167 | pll_out = RT5682_PLL1_FREQ; |
168 | pll_in = DEFAULT_MCLK_RATE; |
169 | } else if (!strcmp(codec_dai->name, "rt5682s-aif1" )) { |
170 | pll_source = RT5682S_PLL_S_MCLK; |
171 | pll_id = RT5682S_PLL2; |
172 | clk_id = RT5682S_SCLK_S_PLL2; |
173 | pll_out = RT5682_PLL1_FREQ; |
174 | pll_in = DEFAULT_MCLK_RATE; |
175 | } else { |
176 | return 0; |
177 | } |
178 | snd_soc_dai_set_fmt(dai: codec_dai, |
179 | SND_SOC_DAIFMT_BC_FC | |
180 | SND_SOC_DAIFMT_NB_NF | |
181 | SND_SOC_DAIFMT_I2S); |
182 | |
183 | /* Configure PLL1 for codec */ |
184 | ret = snd_soc_dai_set_pll(dai: codec_dai, pll_id, source: pll_source, |
185 | freq_in: pll_in, freq_out: pll_out); |
186 | if (ret) { |
187 | dev_err(rtd->dev, "can't set codec pll: %d\n" , ret); |
188 | return ret; |
189 | } |
190 | |
191 | /* Configure sysclk for codec */ |
192 | ret = snd_soc_dai_set_sysclk(dai: codec_dai, clk_id, freq: pll_out, |
193 | SND_SOC_CLOCK_IN); |
194 | if (ret) |
195 | dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n" , |
196 | ret); |
197 | |
198 | return ret; |
199 | } |
200 | |
201 | static int sc7180_snd_startup(struct snd_pcm_substream *substream) |
202 | { |
203 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
204 | struct snd_soc_card *card = rtd->card; |
205 | struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card); |
206 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
207 | int ret; |
208 | |
209 | switch (cpu_dai->id) { |
210 | case MI2S_PRIMARY: |
211 | if (++data->pri_mi2s_clk_count == 1) { |
212 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
213 | LPASS_MCLK0, |
214 | DEFAULT_MCLK_RATE, |
215 | dir: SNDRV_PCM_STREAM_PLAYBACK); |
216 | } |
217 | |
218 | ret = sc7180_startup_realtek_codec(rtd); |
219 | if (ret) |
220 | return ret; |
221 | |
222 | break; |
223 | case MI2S_SECONDARY: |
224 | break; |
225 | case LPASS_DP_RX: |
226 | break; |
227 | default: |
228 | dev_err(rtd->dev, "%s: invalid dai id 0x%x\n" , __func__, |
229 | cpu_dai->id); |
230 | return -EINVAL; |
231 | } |
232 | return 0; |
233 | } |
234 | |
235 | static int sc7180_qdsp_snd_startup(struct snd_pcm_substream *substream) |
236 | { |
237 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
238 | struct snd_soc_card *card = rtd->card; |
239 | struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card); |
240 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
241 | struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); |
242 | int ret; |
243 | |
244 | switch (cpu_dai->id) { |
245 | case PRIMARY_MI2S_RX: |
246 | case PRIMARY_MI2S_TX: |
247 | if (++data->pri_mi2s_clk_count == 1) { |
248 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
249 | Q6AFE_LPASS_CLK_ID_MCLK_1, |
250 | DEFAULT_MCLK_RATE, |
251 | dir: SNDRV_PCM_STREAM_PLAYBACK); |
252 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
253 | Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, |
254 | MI2S_BCLK_RATE, |
255 | dir: SNDRV_PCM_STREAM_PLAYBACK); |
256 | } |
257 | |
258 | snd_soc_dai_set_fmt(dai: cpu_dai, SND_SOC_DAIFMT_BP_FP); |
259 | |
260 | ret = sc7180_startup_realtek_codec(rtd); |
261 | if (ret) |
262 | return ret; |
263 | |
264 | break; |
265 | case TERTIARY_MI2S_RX: |
266 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
267 | Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, |
268 | MI2S_BCLK_RATE, |
269 | dir: SNDRV_PCM_STREAM_PLAYBACK); |
270 | |
271 | snd_soc_dai_set_fmt(dai: codec_dai, |
272 | SND_SOC_DAIFMT_BC_FC | |
273 | SND_SOC_DAIFMT_NB_NF | |
274 | SND_SOC_DAIFMT_I2S); |
275 | snd_soc_dai_set_fmt(dai: cpu_dai, SND_SOC_DAIFMT_BP_FP); |
276 | break; |
277 | case DISPLAY_PORT_RX: |
278 | break; |
279 | default: |
280 | dev_err(rtd->dev, "%s: invalid dai id 0x%x\n" , __func__, |
281 | cpu_dai->id); |
282 | return -EINVAL; |
283 | } |
284 | return 0; |
285 | } |
286 | |
287 | static int dmic_get(struct snd_kcontrol *kcontrol, |
288 | struct snd_ctl_elem_value *ucontrol) |
289 | { |
290 | struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); |
291 | struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card: dapm->card); |
292 | |
293 | ucontrol->value.integer.value[0] = data->dmic_switch; |
294 | return 0; |
295 | } |
296 | |
297 | static int dmic_set(struct snd_kcontrol *kcontrol, |
298 | struct snd_ctl_elem_value *ucontrol) |
299 | { |
300 | struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); |
301 | struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card: dapm->card); |
302 | |
303 | data->dmic_switch = ucontrol->value.integer.value[0]; |
304 | gpiod_set_value(desc: data->dmic_sel, value: data->dmic_switch); |
305 | return 0; |
306 | } |
307 | |
308 | static void sc7180_snd_shutdown(struct snd_pcm_substream *substream) |
309 | { |
310 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
311 | struct snd_soc_card *card = rtd->card; |
312 | struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card); |
313 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
314 | |
315 | switch (cpu_dai->id) { |
316 | case MI2S_PRIMARY: |
317 | if (--data->pri_mi2s_clk_count == 0) { |
318 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
319 | LPASS_MCLK0, |
320 | freq: 0, |
321 | dir: SNDRV_PCM_STREAM_PLAYBACK); |
322 | } |
323 | break; |
324 | case MI2S_SECONDARY: |
325 | break; |
326 | case LPASS_DP_RX: |
327 | break; |
328 | default: |
329 | dev_err(rtd->dev, "%s: invalid dai id 0x%x\n" , __func__, |
330 | cpu_dai->id); |
331 | break; |
332 | } |
333 | } |
334 | |
335 | static void sc7180_qdsp_snd_shutdown(struct snd_pcm_substream *substream) |
336 | { |
337 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
338 | struct snd_soc_card *card = rtd->card; |
339 | struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card); |
340 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
341 | |
342 | switch (cpu_dai->id) { |
343 | case PRIMARY_MI2S_RX: |
344 | case PRIMARY_MI2S_TX: |
345 | if (--data->pri_mi2s_clk_count == 0) { |
346 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
347 | Q6AFE_LPASS_CLK_ID_MCLK_1, |
348 | freq: 0, |
349 | dir: SNDRV_PCM_STREAM_PLAYBACK); |
350 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
351 | Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, |
352 | freq: 0, |
353 | dir: SNDRV_PCM_STREAM_PLAYBACK); |
354 | } |
355 | break; |
356 | case TERTIARY_MI2S_RX: |
357 | snd_soc_dai_set_sysclk(dai: cpu_dai, |
358 | Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT, |
359 | freq: 0, |
360 | dir: SNDRV_PCM_STREAM_PLAYBACK); |
361 | break; |
362 | case DISPLAY_PORT_RX: |
363 | break; |
364 | default: |
365 | dev_err(rtd->dev, "%s: invalid dai id 0x%x\n" , __func__, |
366 | cpu_dai->id); |
367 | break; |
368 | } |
369 | } |
370 | |
371 | static int sc7180_adau7002_init(struct snd_soc_pcm_runtime *rtd) |
372 | { |
373 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
374 | |
375 | switch (cpu_dai->id) { |
376 | case MI2S_PRIMARY: |
377 | return 0; |
378 | case MI2S_SECONDARY: |
379 | return 0; |
380 | case LPASS_DP_RX: |
381 | return sc7180_hdmi_init(rtd); |
382 | default: |
383 | dev_err(rtd->dev, "%s: invalid dai id 0x%x\n" , __func__, |
384 | cpu_dai->id); |
385 | return -EINVAL; |
386 | } |
387 | return 0; |
388 | } |
389 | |
390 | static int sc7180_adau7002_snd_startup(struct snd_pcm_substream *substream) |
391 | { |
392 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
393 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
394 | struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); |
395 | struct snd_pcm_runtime *runtime = substream->runtime; |
396 | |
397 | switch (cpu_dai->id) { |
398 | case MI2S_PRIMARY: |
399 | snd_soc_dai_set_fmt(dai: codec_dai, |
400 | SND_SOC_DAIFMT_CBS_CFS | |
401 | SND_SOC_DAIFMT_NB_NF | |
402 | SND_SOC_DAIFMT_I2S); |
403 | runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; |
404 | snd_pcm_hw_constraint_msbits(runtime, cond: 0, width: 32, msbits: 32); |
405 | |
406 | break; |
407 | case MI2S_SECONDARY: |
408 | break; |
409 | case LPASS_DP_RX: |
410 | break; |
411 | default: |
412 | dev_err(rtd->dev, "%s: invalid dai id 0x%x\n" , __func__, |
413 | cpu_dai->id); |
414 | return -EINVAL; |
415 | } |
416 | return 0; |
417 | } |
418 | |
419 | static int sc7180_qdsp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, |
420 | struct snd_pcm_hw_params *params) |
421 | { |
422 | struct snd_interval *rate = hw_param_interval(params, |
423 | SNDRV_PCM_HW_PARAM_RATE); |
424 | struct snd_interval *channels = hw_param_interval(params, |
425 | SNDRV_PCM_HW_PARAM_CHANNELS); |
426 | |
427 | rate->min = rate->max = 48000; |
428 | channels->min = channels->max = 2; |
429 | |
430 | return 0; |
431 | } |
432 | |
433 | static const struct snd_soc_ops sc7180_ops = { |
434 | .startup = sc7180_snd_startup, |
435 | .shutdown = sc7180_snd_shutdown, |
436 | }; |
437 | |
438 | static const struct snd_soc_ops sc7180_qdsp_ops = { |
439 | .startup = sc7180_qdsp_snd_startup, |
440 | .shutdown = sc7180_qdsp_snd_shutdown, |
441 | }; |
442 | |
443 | static const struct snd_soc_ops sc7180_adau7002_ops = { |
444 | .startup = sc7180_adau7002_snd_startup, |
445 | }; |
446 | |
447 | static const struct snd_soc_dapm_widget sc7180_snd_widgets[] = { |
448 | SND_SOC_DAPM_HP("Headphone Jack" , NULL), |
449 | SND_SOC_DAPM_MIC("Headset Mic" , NULL), |
450 | }; |
451 | |
452 | static const struct snd_kcontrol_new sc7180_snd_controls[] = { |
453 | SOC_DAPM_PIN_SWITCH("Headphone Jack" ), |
454 | SOC_DAPM_PIN_SWITCH("Headset Mic" ), |
455 | }; |
456 | |
457 | static const struct snd_soc_dapm_widget sc7180_adau7002_snd_widgets[] = { |
458 | SND_SOC_DAPM_MIC("DMIC" , NULL), |
459 | }; |
460 | |
461 | static const char * const dmic_mux_text[] = { |
462 | "Front Mic" , |
463 | "Rear Mic" , |
464 | }; |
465 | |
466 | static SOC_ENUM_SINGLE_DECL(sc7180_dmic_enum, |
467 | SND_SOC_NOPM, 0, dmic_mux_text); |
468 | |
469 | static const struct snd_kcontrol_new sc7180_dmic_mux_control = |
470 | SOC_DAPM_ENUM_EXT("DMIC Select Mux" , sc7180_dmic_enum, |
471 | dmic_get, dmic_set); |
472 | |
473 | static const struct snd_soc_dapm_widget sc7180_snd_dual_mic_widgets[] = { |
474 | SND_SOC_DAPM_HP("Headphone Jack" , NULL), |
475 | SND_SOC_DAPM_MIC("Headset Mic" , NULL), |
476 | SND_SOC_DAPM_MIC("DMIC" , NULL), |
477 | SND_SOC_DAPM_MUX("Dmic Mux" , SND_SOC_NOPM, 0, 0, &sc7180_dmic_mux_control), |
478 | }; |
479 | |
480 | static const struct snd_kcontrol_new sc7180_snd_dual_mic_controls[] = { |
481 | SOC_DAPM_PIN_SWITCH("Headphone Jack" ), |
482 | SOC_DAPM_PIN_SWITCH("Headset Mic" ), |
483 | }; |
484 | |
485 | static const struct snd_soc_dapm_route sc7180_snd_dual_mic_audio_route[] = { |
486 | {"Dmic Mux" , "Front Mic" , "DMIC" }, |
487 | {"Dmic Mux" , "Rear Mic" , "DMIC" }, |
488 | }; |
489 | |
490 | static int sc7180_snd_platform_probe(struct platform_device *pdev) |
491 | { |
492 | struct snd_soc_card *card; |
493 | struct sc7180_snd_data *data; |
494 | struct device *dev = &pdev->dev; |
495 | struct snd_soc_dai_link *link; |
496 | int ret; |
497 | int i; |
498 | bool qdsp = false, no_headphone = false; |
499 | |
500 | /* Allocate the private data */ |
501 | data = devm_kzalloc(dev, size: sizeof(*data), GFP_KERNEL); |
502 | if (!data) |
503 | return -ENOMEM; |
504 | |
505 | card = &data->card; |
506 | snd_soc_card_set_drvdata(card, data); |
507 | |
508 | card->owner = THIS_MODULE; |
509 | card->driver_name = DRIVER_NAME; |
510 | card->dev = dev; |
511 | card->dapm_widgets = sc7180_snd_widgets; |
512 | card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_widgets); |
513 | card->controls = sc7180_snd_controls; |
514 | card->num_controls = ARRAY_SIZE(sc7180_snd_controls); |
515 | |
516 | if (of_property_read_bool(np: dev->of_node, propname: "dmic-gpios" )) { |
517 | card->dapm_widgets = sc7180_snd_dual_mic_widgets, |
518 | card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_dual_mic_widgets), |
519 | card->controls = sc7180_snd_dual_mic_controls, |
520 | card->num_controls = ARRAY_SIZE(sc7180_snd_dual_mic_controls), |
521 | card->dapm_routes = sc7180_snd_dual_mic_audio_route, |
522 | card->num_dapm_routes = ARRAY_SIZE(sc7180_snd_dual_mic_audio_route), |
523 | data->dmic_sel = devm_gpiod_get(dev: &pdev->dev, con_id: "dmic" , flags: GPIOD_OUT_LOW); |
524 | if (IS_ERR(ptr: data->dmic_sel)) { |
525 | dev_err(&pdev->dev, "DMIC gpio failed err=%ld\n" , PTR_ERR(data->dmic_sel)); |
526 | return PTR_ERR(ptr: data->dmic_sel); |
527 | } |
528 | } |
529 | |
530 | if (of_device_is_compatible(device: dev->of_node, "google,sc7180-coachz" )) { |
531 | no_headphone = true; |
532 | card->dapm_widgets = sc7180_adau7002_snd_widgets; |
533 | card->num_dapm_widgets = ARRAY_SIZE(sc7180_adau7002_snd_widgets); |
534 | } else if (of_device_is_compatible(device: dev->of_node, "qcom,sc7180-qdsp6-sndcard" )) { |
535 | qdsp = true; |
536 | } |
537 | |
538 | ret = qcom_snd_parse_of(card); |
539 | if (ret) |
540 | return ret; |
541 | |
542 | for_each_card_prelinks(card, i, link) { |
543 | if (no_headphone) { |
544 | link->ops = &sc7180_adau7002_ops; |
545 | link->init = sc7180_adau7002_init; |
546 | } else if (qdsp) { |
547 | if (link->no_pcm == 1) { |
548 | link->ops = &sc7180_qdsp_ops; |
549 | link->be_hw_params_fixup = sc7180_qdsp_be_hw_params_fixup; |
550 | link->init = sc7180_qdsp_init; |
551 | } |
552 | } else { |
553 | link->ops = &sc7180_ops; |
554 | link->init = sc7180_init; |
555 | } |
556 | } |
557 | |
558 | return devm_snd_soc_register_card(dev, card); |
559 | } |
560 | |
561 | static const struct of_device_id sc7180_snd_device_id[] = { |
562 | {.compatible = "google,sc7180-trogdor" }, |
563 | {.compatible = "google,sc7180-coachz" }, |
564 | {.compatible = "qcom,sc7180-qdsp6-sndcard" }, |
565 | {}, |
566 | }; |
567 | MODULE_DEVICE_TABLE(of, sc7180_snd_device_id); |
568 | |
569 | static struct platform_driver sc7180_snd_driver = { |
570 | .probe = sc7180_snd_platform_probe, |
571 | .driver = { |
572 | .name = "msm-snd-sc7180" , |
573 | .of_match_table = sc7180_snd_device_id, |
574 | .pm = &snd_soc_pm_ops, |
575 | }, |
576 | }; |
577 | module_platform_driver(sc7180_snd_driver); |
578 | |
579 | MODULE_DESCRIPTION("sc7180 ASoC Machine Driver" ); |
580 | MODULE_LICENSE("GPL" ); |
581 | |