Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/qcom/sc7180.c
26428 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_dapm(kcontrol);
291
struct sc7180_snd_data *data = snd_soc_card_get_drvdata(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(dapm->card);
302
303
data->dmic_switch = ucontrol->value.integer.value[0];
304
gpiod_set_value(data->dmic_sel, 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 = snd_soc_substream_to_rtd(substream);
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(cpu_dai,
319
LPASS_MCLK0,
320
0,
321
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 = snd_soc_substream_to_rtd(substream);
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(cpu_dai,
347
Q6AFE_LPASS_CLK_ID_MCLK_1,
348
0,
349
SNDRV_PCM_STREAM_PLAYBACK);
350
snd_soc_dai_set_sysclk(cpu_dai,
351
Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
352
0,
353
SNDRV_PCM_STREAM_PLAYBACK);
354
}
355
break;
356
case TERTIARY_MI2S_RX:
357
snd_soc_dai_set_sysclk(cpu_dai,
358
Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
359
0,
360
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 = snd_soc_substream_to_rtd(substream);
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(codec_dai,
400
SND_SOC_DAIFMT_CBC_CFC |
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, 0, 32, 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, 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_present(dev->of_node, "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(&pdev->dev, "dmic", GPIOD_OUT_LOW);
524
if (IS_ERR(data->dmic_sel)) {
525
dev_err(&pdev->dev, "DMIC gpio failed err=%ld\n", PTR_ERR(data->dmic_sel));
526
return PTR_ERR(data->dmic_sel);
527
}
528
}
529
530
if (of_device_is_compatible(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(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
582