Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/qcom/sc7180.c
51347 views
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, "Headset Jack",
67
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
&pdata->hs_jack,
72
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, SND_JACK_BTN_0, KEY_PLAYPAUSE);
83
snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
84
snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
85
snd_jack_set_key(jack, 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, &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, "HDMI Jack",
104
SND_JACK_LINEOUT,
105
&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, &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(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(codec_dai, pll_id, pll_source,
185
pll_in, 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(codec_dai, clk_id, 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 = snd_soc_substream_to_rtd(substream);
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(cpu_dai,
213
LPASS_MCLK0,
214
DEFAULT_MCLK_RATE,
215
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 = snd_soc_substream_to_rtd(substream);
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(cpu_dai,
249
Q6AFE_LPASS_CLK_ID_MCLK_1,
250
DEFAULT_MCLK_RATE,
251
SNDRV_PCM_STREAM_PLAYBACK);
252
snd_soc_dai_set_sysclk(cpu_dai,
253
Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
254
MI2S_BCLK_RATE,
255
SNDRV_PCM_STREAM_PLAYBACK);
256
}
257
258
snd_soc_dai_set_fmt(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(cpu_dai,
267
Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
268
MI2S_BCLK_RATE,
269
SNDRV_PCM_STREAM_PLAYBACK);
270
271
snd_soc_dai_set_fmt(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(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_to_dapm(kcontrol);
291
struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
292
struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
293
294
ucontrol->value.integer.value[0] = data->dmic_switch;
295
return 0;
296
}
297
298
static int dmic_set(struct snd_kcontrol *kcontrol,
299
struct snd_ctl_elem_value *ucontrol)
300
{
301
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
302
struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
303
struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
304
305
data->dmic_switch = ucontrol->value.integer.value[0];
306
gpiod_set_value(data->dmic_sel, data->dmic_switch);
307
return 0;
308
}
309
310
static void sc7180_snd_shutdown(struct snd_pcm_substream *substream)
311
{
312
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
313
struct snd_soc_card *card = rtd->card;
314
struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
315
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
316
317
switch (cpu_dai->id) {
318
case MI2S_PRIMARY:
319
if (--data->pri_mi2s_clk_count == 0) {
320
snd_soc_dai_set_sysclk(cpu_dai,
321
LPASS_MCLK0,
322
0,
323
SNDRV_PCM_STREAM_PLAYBACK);
324
}
325
break;
326
case MI2S_SECONDARY:
327
break;
328
case LPASS_DP_RX:
329
break;
330
default:
331
dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
332
cpu_dai->id);
333
break;
334
}
335
}
336
337
static void sc7180_qdsp_snd_shutdown(struct snd_pcm_substream *substream)
338
{
339
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
340
struct snd_soc_card *card = rtd->card;
341
struct sc7180_snd_data *data = snd_soc_card_get_drvdata(card);
342
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
343
344
switch (cpu_dai->id) {
345
case PRIMARY_MI2S_RX:
346
case PRIMARY_MI2S_TX:
347
if (--data->pri_mi2s_clk_count == 0) {
348
snd_soc_dai_set_sysclk(cpu_dai,
349
Q6AFE_LPASS_CLK_ID_MCLK_1,
350
0,
351
SNDRV_PCM_STREAM_PLAYBACK);
352
snd_soc_dai_set_sysclk(cpu_dai,
353
Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
354
0,
355
SNDRV_PCM_STREAM_PLAYBACK);
356
}
357
break;
358
case TERTIARY_MI2S_RX:
359
snd_soc_dai_set_sysclk(cpu_dai,
360
Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
361
0,
362
SNDRV_PCM_STREAM_PLAYBACK);
363
break;
364
case DISPLAY_PORT_RX:
365
break;
366
default:
367
dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
368
cpu_dai->id);
369
break;
370
}
371
}
372
373
static int sc7180_adau7002_init(struct snd_soc_pcm_runtime *rtd)
374
{
375
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
376
377
switch (cpu_dai->id) {
378
case MI2S_PRIMARY:
379
return 0;
380
case MI2S_SECONDARY:
381
return 0;
382
case LPASS_DP_RX:
383
return sc7180_hdmi_init(rtd);
384
default:
385
dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
386
cpu_dai->id);
387
return -EINVAL;
388
}
389
return 0;
390
}
391
392
static int sc7180_adau7002_snd_startup(struct snd_pcm_substream *substream)
393
{
394
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
395
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
396
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
397
struct snd_pcm_runtime *runtime = substream->runtime;
398
399
switch (cpu_dai->id) {
400
case MI2S_PRIMARY:
401
snd_soc_dai_set_fmt(codec_dai,
402
SND_SOC_DAIFMT_CBC_CFC |
403
SND_SOC_DAIFMT_NB_NF |
404
SND_SOC_DAIFMT_I2S);
405
runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
406
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 32);
407
408
break;
409
case MI2S_SECONDARY:
410
break;
411
case LPASS_DP_RX:
412
break;
413
default:
414
dev_err(rtd->dev, "%s: invalid dai id 0x%x\n", __func__,
415
cpu_dai->id);
416
return -EINVAL;
417
}
418
return 0;
419
}
420
421
static int sc7180_qdsp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
422
struct snd_pcm_hw_params *params)
423
{
424
struct snd_interval *rate = hw_param_interval(params,
425
SNDRV_PCM_HW_PARAM_RATE);
426
struct snd_interval *channels = hw_param_interval(params,
427
SNDRV_PCM_HW_PARAM_CHANNELS);
428
429
rate->min = rate->max = 48000;
430
channels->min = channels->max = 2;
431
432
return 0;
433
}
434
435
static const struct snd_soc_ops sc7180_ops = {
436
.startup = sc7180_snd_startup,
437
.shutdown = sc7180_snd_shutdown,
438
};
439
440
static const struct snd_soc_ops sc7180_qdsp_ops = {
441
.startup = sc7180_qdsp_snd_startup,
442
.shutdown = sc7180_qdsp_snd_shutdown,
443
};
444
445
static const struct snd_soc_ops sc7180_adau7002_ops = {
446
.startup = sc7180_adau7002_snd_startup,
447
};
448
449
static const struct snd_soc_dapm_widget sc7180_snd_widgets[] = {
450
SND_SOC_DAPM_HP("Headphone Jack", NULL),
451
SND_SOC_DAPM_MIC("Headset Mic", NULL),
452
};
453
454
static const struct snd_kcontrol_new sc7180_snd_controls[] = {
455
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
456
SOC_DAPM_PIN_SWITCH("Headset Mic"),
457
};
458
459
static const struct snd_soc_dapm_widget sc7180_adau7002_snd_widgets[] = {
460
SND_SOC_DAPM_MIC("DMIC", NULL),
461
};
462
463
static const char * const dmic_mux_text[] = {
464
"Front Mic",
465
"Rear Mic",
466
};
467
468
static SOC_ENUM_SINGLE_DECL(sc7180_dmic_enum,
469
SND_SOC_NOPM, 0, dmic_mux_text);
470
471
static const struct snd_kcontrol_new sc7180_dmic_mux_control =
472
SOC_DAPM_ENUM_EXT("DMIC Select Mux", sc7180_dmic_enum,
473
dmic_get, dmic_set);
474
475
static const struct snd_soc_dapm_widget sc7180_snd_dual_mic_widgets[] = {
476
SND_SOC_DAPM_HP("Headphone Jack", NULL),
477
SND_SOC_DAPM_MIC("Headset Mic", NULL),
478
SND_SOC_DAPM_MIC("DMIC", NULL),
479
SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0, &sc7180_dmic_mux_control),
480
};
481
482
static const struct snd_kcontrol_new sc7180_snd_dual_mic_controls[] = {
483
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
484
SOC_DAPM_PIN_SWITCH("Headset Mic"),
485
};
486
487
static const struct snd_soc_dapm_route sc7180_snd_dual_mic_audio_route[] = {
488
{"Dmic Mux", "Front Mic", "DMIC"},
489
{"Dmic Mux", "Rear Mic", "DMIC"},
490
};
491
492
static int sc7180_snd_platform_probe(struct platform_device *pdev)
493
{
494
struct snd_soc_card *card;
495
struct sc7180_snd_data *data;
496
struct device *dev = &pdev->dev;
497
struct snd_soc_dai_link *link;
498
int ret;
499
int i;
500
bool qdsp = false, no_headphone = false;
501
502
/* Allocate the private data */
503
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
504
if (!data)
505
return -ENOMEM;
506
507
card = &data->card;
508
snd_soc_card_set_drvdata(card, data);
509
510
card->owner = THIS_MODULE;
511
card->driver_name = DRIVER_NAME;
512
card->dev = dev;
513
card->dapm_widgets = sc7180_snd_widgets;
514
card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_widgets);
515
card->controls = sc7180_snd_controls;
516
card->num_controls = ARRAY_SIZE(sc7180_snd_controls);
517
518
if (of_property_present(dev->of_node, "dmic-gpios")) {
519
card->dapm_widgets = sc7180_snd_dual_mic_widgets,
520
card->num_dapm_widgets = ARRAY_SIZE(sc7180_snd_dual_mic_widgets),
521
card->controls = sc7180_snd_dual_mic_controls,
522
card->num_controls = ARRAY_SIZE(sc7180_snd_dual_mic_controls),
523
card->dapm_routes = sc7180_snd_dual_mic_audio_route,
524
card->num_dapm_routes = ARRAY_SIZE(sc7180_snd_dual_mic_audio_route),
525
data->dmic_sel = devm_gpiod_get(&pdev->dev, "dmic", GPIOD_OUT_LOW);
526
if (IS_ERR(data->dmic_sel)) {
527
dev_err(&pdev->dev, "DMIC gpio failed err=%ld\n", PTR_ERR(data->dmic_sel));
528
return PTR_ERR(data->dmic_sel);
529
}
530
}
531
532
if (of_device_is_compatible(dev->of_node, "google,sc7180-coachz")) {
533
no_headphone = true;
534
card->dapm_widgets = sc7180_adau7002_snd_widgets;
535
card->num_dapm_widgets = ARRAY_SIZE(sc7180_adau7002_snd_widgets);
536
} else if (of_device_is_compatible(dev->of_node, "qcom,sc7180-qdsp6-sndcard")) {
537
qdsp = true;
538
}
539
540
ret = qcom_snd_parse_of(card);
541
if (ret)
542
return ret;
543
544
for_each_card_prelinks(card, i, link) {
545
if (no_headphone) {
546
link->ops = &sc7180_adau7002_ops;
547
link->init = sc7180_adau7002_init;
548
} else if (qdsp) {
549
if (link->no_pcm == 1) {
550
link->ops = &sc7180_qdsp_ops;
551
link->be_hw_params_fixup = sc7180_qdsp_be_hw_params_fixup;
552
link->init = sc7180_qdsp_init;
553
}
554
} else {
555
link->ops = &sc7180_ops;
556
link->init = sc7180_init;
557
}
558
}
559
560
return devm_snd_soc_register_card(dev, card);
561
}
562
563
static const struct of_device_id sc7180_snd_device_id[] = {
564
{.compatible = "google,sc7180-trogdor"},
565
{.compatible = "google,sc7180-coachz"},
566
{.compatible = "qcom,sc7180-qdsp6-sndcard"},
567
{},
568
};
569
MODULE_DEVICE_TABLE(of, sc7180_snd_device_id);
570
571
static struct platform_driver sc7180_snd_driver = {
572
.probe = sc7180_snd_platform_probe,
573
.driver = {
574
.name = "msm-snd-sc7180",
575
.of_match_table = sc7180_snd_device_id,
576
.pm = &snd_soc_pm_ops,
577
},
578
};
579
module_platform_driver(sc7180_snd_driver);
580
581
MODULE_DESCRIPTION("sc7180 ASoC Machine Driver");
582
MODULE_LICENSE("GPL");
583
584