1 // SPDX-License-Identifier: GPL-2.0-only
2 // Copyright(c) 2019 Intel Corporation.
5 * Intel SOF Machine driver for DA7219 + MAX98373/MAX98360A codec
8 #include <linux/input.h>
9 #include <linux/module.h>
10 #include <sound/pcm.h>
11 #include <sound/pcm_params.h>
12 #include <linux/platform_device.h>
13 #include <sound/soc.h>
14 #include <sound/soc-acpi.h>
15 #include "../../codecs/da7219.h"
16 #include "../../codecs/da7219-aad.h"
17 #include "hda_dsp_common.h"
19 #define DIALOG_CODEC_DAI "da7219-hifi"
20 #define MAX98373_CODEC_DAI "max98373-aif1"
21 #define MAXIM_DEV0_NAME "i2c-MX98373:00"
22 #define MAXIM_DEV1_NAME "i2c-MX98373:01"
25 struct list_head head
;
26 struct snd_soc_dai
*codec_dai
;
31 struct snd_soc_jack headset
;
32 struct list_head hdmi_pcm_list
;
33 struct snd_soc_jack hdmi
[3];
36 static int platform_clock_control(struct snd_soc_dapm_widget
*w
,
37 struct snd_kcontrol
*k
, int event
)
39 struct snd_soc_dapm_context
*dapm
= w
->dapm
;
40 struct snd_soc_card
*card
= dapm
->card
;
41 struct snd_soc_dai
*codec_dai
;
44 codec_dai
= snd_soc_card_get_codec_dai(card
, DIALOG_CODEC_DAI
);
46 dev_err(card
->dev
, "Codec dai not found; Unable to set/unset codec pll\n");
50 if (SND_SOC_DAPM_EVENT_OFF(event
)) {
51 ret
= snd_soc_dai_set_pll(codec_dai
, 0, DA7219_SYSCLK_MCLK
,
54 dev_err(card
->dev
, "failed to stop PLL: %d\n", ret
);
55 } else if (SND_SOC_DAPM_EVENT_ON(event
)) {
56 ret
= snd_soc_dai_set_pll(codec_dai
, 0, DA7219_SYSCLK_PLL_SRM
,
57 0, DA7219_PLL_FREQ_OUT_98304
);
59 dev_err(card
->dev
, "failed to start PLL: %d\n", ret
);
65 static const struct snd_kcontrol_new controls
[] = {
66 SOC_DAPM_PIN_SWITCH("Headphone Jack"),
67 SOC_DAPM_PIN_SWITCH("Headset Mic"),
68 SOC_DAPM_PIN_SWITCH("Left Spk"),
69 SOC_DAPM_PIN_SWITCH("Right Spk"),
72 static const struct snd_kcontrol_new m98360a_controls
[] = {
73 SOC_DAPM_PIN_SWITCH("Headphone Jack"),
74 SOC_DAPM_PIN_SWITCH("Headset Mic"),
75 SOC_DAPM_PIN_SWITCH("Spk"),
78 /* For MAX98373 amp */
79 static const struct snd_soc_dapm_widget widgets
[] = {
80 SND_SOC_DAPM_HP("Headphone Jack", NULL
),
81 SND_SOC_DAPM_MIC("Headset Mic", NULL
),
83 SND_SOC_DAPM_SPK("Left Spk", NULL
),
84 SND_SOC_DAPM_SPK("Right Spk", NULL
),
86 SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM
, 0, 0,
87 platform_clock_control
, SND_SOC_DAPM_POST_PMD
|
88 SND_SOC_DAPM_PRE_PMU
),
90 SND_SOC_DAPM_MIC("SoC DMIC", NULL
),
93 static const struct snd_soc_dapm_route audio_map
[] = {
94 { "Headphone Jack", NULL
, "HPL" },
95 { "Headphone Jack", NULL
, "HPR" },
97 { "MIC", NULL
, "Headset Mic" },
99 { "Headphone Jack", NULL
, "Platform Clock" },
100 { "Headset Mic", NULL
, "Platform Clock" },
102 { "Left Spk", NULL
, "Left BE_OUT" },
103 { "Right Spk", NULL
, "Right BE_OUT" },
106 {"DMic", NULL
, "SoC DMIC"},
109 /* For MAX98360A amp */
110 static const struct snd_soc_dapm_widget max98360a_widgets
[] = {
111 SND_SOC_DAPM_HP("Headphone Jack", NULL
),
112 SND_SOC_DAPM_MIC("Headset Mic", NULL
),
114 SND_SOC_DAPM_SPK("Spk", NULL
),
116 SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM
, 0, 0,
117 platform_clock_control
, SND_SOC_DAPM_POST_PMD
|
118 SND_SOC_DAPM_PRE_PMU
),
120 SND_SOC_DAPM_MIC("SoC DMIC", NULL
),
123 static const struct snd_soc_dapm_route max98360a_map
[] = {
124 { "Headphone Jack", NULL
, "HPL" },
125 { "Headphone Jack", NULL
, "HPR" },
127 { "MIC", NULL
, "Headset Mic" },
129 { "Headphone Jack", NULL
, "Platform Clock" },
130 { "Headset Mic", NULL
, "Platform Clock" },
132 {"Spk", NULL
, "Speaker"},
135 {"DMic", NULL
, "SoC DMIC"},
138 static struct snd_soc_jack headset
;
140 static int da7219_codec_init(struct snd_soc_pcm_runtime
*rtd
)
142 struct snd_soc_dai
*codec_dai
= asoc_rtd_to_codec(rtd
, 0);
143 struct snd_soc_component
*component
= codec_dai
->component
;
144 struct snd_soc_jack
*jack
;
147 /* Configure sysclk for codec */
148 ret
= snd_soc_dai_set_sysclk(codec_dai
, DA7219_CLKSRC_MCLK
, 24000000,
151 dev_err(rtd
->dev
, "can't set codec sysclk configuration\n");
156 * Headset buttons map to the google Reference headset.
157 * These can be configured by userspace.
159 ret
= snd_soc_card_jack_new(rtd
->card
, "Headset Jack",
160 SND_JACK_HEADSET
| SND_JACK_BTN_0
|
161 SND_JACK_BTN_1
| SND_JACK_BTN_2
|
162 SND_JACK_BTN_3
| SND_JACK_LINEOUT
,
165 dev_err(rtd
->dev
, "Headset Jack creation failed: %d\n", ret
);
170 snd_jack_set_key(jack
->jack
, SND_JACK_BTN_0
, KEY_PLAYPAUSE
);
171 snd_jack_set_key(jack
->jack
, SND_JACK_BTN_1
, KEY_VOLUMEUP
);
172 snd_jack_set_key(jack
->jack
, SND_JACK_BTN_2
, KEY_VOLUMEDOWN
);
173 snd_jack_set_key(jack
->jack
, SND_JACK_BTN_3
, KEY_VOICECOMMAND
);
174 da7219_aad_jack_det(component
, jack
);
179 static int ssp1_hw_params(struct snd_pcm_substream
*substream
,
180 struct snd_pcm_hw_params
*params
)
182 struct snd_soc_pcm_runtime
*runtime
= asoc_substream_to_rtd(substream
);
185 for (j
= 0; j
< runtime
->num_codecs
; j
++) {
186 struct snd_soc_dai
*codec_dai
= asoc_rtd_to_codec(runtime
, j
);
188 if (!strcmp(codec_dai
->component
->name
, MAXIM_DEV0_NAME
)) {
189 /* vmon_slot_no = 0 imon_slot_no = 1 for TX slots */
190 ret
= snd_soc_dai_set_tdm_slot(codec_dai
, 0x3, 3, 4, 16);
192 dev_err(runtime
->dev
, "DEV0 TDM slot err:%d\n", ret
);
196 if (!strcmp(codec_dai
->component
->name
, MAXIM_DEV1_NAME
)) {
197 /* vmon_slot_no = 2 imon_slot_no = 3 for TX slots */
198 ret
= snd_soc_dai_set_tdm_slot(codec_dai
, 0xC, 3, 4, 16);
200 dev_err(runtime
->dev
, "DEV1 TDM slot err:%d\n", ret
);
209 static struct snd_soc_ops ssp1_ops
= {
210 .hw_params
= ssp1_hw_params
,
213 static struct snd_soc_codec_conf max98373_codec_conf
[] = {
215 .dlc
= COMP_CODEC_CONF(MAXIM_DEV0_NAME
),
216 .name_prefix
= "Right",
219 .dlc
= COMP_CODEC_CONF(MAXIM_DEV1_NAME
),
220 .name_prefix
= "Left",
224 static int hdmi_init(struct snd_soc_pcm_runtime
*rtd
)
226 struct card_private
*ctx
= snd_soc_card_get_drvdata(rtd
->card
);
227 struct snd_soc_dai
*dai
= asoc_rtd_to_codec(rtd
, 0);
228 struct hdmi_pcm
*pcm
;
230 pcm
= devm_kzalloc(rtd
->card
->dev
, sizeof(*pcm
), GFP_KERNEL
);
234 pcm
->device
= dai
->id
;
235 pcm
->codec_dai
= dai
;
237 list_add_tail(&pcm
->head
, &ctx
->hdmi_pcm_list
);
242 static int card_late_probe(struct snd_soc_card
*card
)
244 struct card_private
*ctx
= snd_soc_card_get_drvdata(card
);
245 struct snd_soc_acpi_mach
*mach
= (card
->dev
)->platform_data
;
246 struct hdmi_pcm
*pcm
;
248 if (mach
->mach_params
.common_hdmi_codec_drv
) {
249 pcm
= list_first_entry(&ctx
->hdmi_pcm_list
, struct hdmi_pcm
,
251 return hda_dsp_hdmi_build_controls(card
,
252 pcm
->codec_dai
->component
);
258 SND_SOC_DAILINK_DEF(ssp0_pin
,
259 DAILINK_COMP_ARRAY(COMP_CPU("SSP0 Pin")));
260 SND_SOC_DAILINK_DEF(ssp0_codec
,
261 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-DLGS7219:00", DIALOG_CODEC_DAI
)));
263 SND_SOC_DAILINK_DEF(ssp1_pin
,
264 DAILINK_COMP_ARRAY(COMP_CPU("SSP1 Pin")));
265 SND_SOC_DAILINK_DEF(ssp1_amps
,
267 /* Left */ COMP_CODEC(MAXIM_DEV0_NAME
, MAX98373_CODEC_DAI
),
268 /* Right */ COMP_CODEC(MAXIM_DEV1_NAME
, MAX98373_CODEC_DAI
)));
270 SND_SOC_DAILINK_DEF(ssp1_m98360a
,
271 DAILINK_COMP_ARRAY(COMP_CODEC("MX98360A:00", "HiFi")));
273 SND_SOC_DAILINK_DEF(dmic_pin
,
274 DAILINK_COMP_ARRAY(COMP_CPU("DMIC01 Pin")));
275 SND_SOC_DAILINK_DEF(dmic_codec
,
276 DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
278 SND_SOC_DAILINK_DEF(dmic16k_pin
,
279 DAILINK_COMP_ARRAY(COMP_CPU("DMIC16k Pin")));
281 SND_SOC_DAILINK_DEF(idisp1_pin
,
282 DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin")));
283 SND_SOC_DAILINK_DEF(idisp1_codec
,
284 DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi1")));
286 SND_SOC_DAILINK_DEF(idisp2_pin
,
287 DAILINK_COMP_ARRAY(COMP_CPU("iDisp2 Pin")));
288 SND_SOC_DAILINK_DEF(idisp2_codec
,
289 DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi2")));
291 SND_SOC_DAILINK_DEF(idisp3_pin
,
292 DAILINK_COMP_ARRAY(COMP_CPU("iDisp3 Pin")));
293 SND_SOC_DAILINK_DEF(idisp3_codec
,
294 DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi3")));
296 SND_SOC_DAILINK_DEF(platform
, /* subject to be overridden during probe */
297 DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3")));
299 static struct snd_soc_dai_link dais
[] = {
300 /* Back End DAI links */
302 .name
= "SSP1-Codec",
304 .ignore_pmdown_time
= 1,
307 .dpcm_capture
= 1, /* IV feedback */
309 SND_SOC_DAILINK_REG(ssp1_pin
, ssp1_amps
, platform
),
312 .name
= "SSP0-Codec",
315 .init
= da7219_codec_init
,
316 .ignore_pmdown_time
= 1,
319 SND_SOC_DAILINK_REG(ssp0_pin
, ssp0_codec
, platform
),
327 SND_SOC_DAILINK_REG(dmic_pin
, dmic_codec
, platform
),
335 SND_SOC_DAILINK_REG(idisp1_pin
, idisp1_codec
, platform
),
343 SND_SOC_DAILINK_REG(idisp2_pin
, idisp2_codec
, platform
),
351 SND_SOC_DAILINK_REG(idisp3_pin
, idisp3_codec
, platform
),
359 SND_SOC_DAILINK_REG(dmic16k_pin
, dmic_codec
, platform
),
363 static struct snd_soc_card card_da7219_m98373
= {
365 .owner
= THIS_MODULE
,
367 .num_links
= ARRAY_SIZE(dais
),
368 .controls
= controls
,
369 .num_controls
= ARRAY_SIZE(controls
),
370 .dapm_widgets
= widgets
,
371 .num_dapm_widgets
= ARRAY_SIZE(widgets
),
372 .dapm_routes
= audio_map
,
373 .num_dapm_routes
= ARRAY_SIZE(audio_map
),
374 .codec_conf
= max98373_codec_conf
,
375 .num_configs
= ARRAY_SIZE(max98373_codec_conf
),
376 .fully_routed
= true,
377 .late_probe
= card_late_probe
,
380 static struct snd_soc_card card_da7219_m98360a
= {
381 .name
= "da7219max98360a",
382 .owner
= THIS_MODULE
,
384 .num_links
= ARRAY_SIZE(dais
),
385 .controls
= m98360a_controls
,
386 .num_controls
= ARRAY_SIZE(m98360a_controls
),
387 .dapm_widgets
= max98360a_widgets
,
388 .num_dapm_widgets
= ARRAY_SIZE(max98360a_widgets
),
389 .dapm_routes
= max98360a_map
,
390 .num_dapm_routes
= ARRAY_SIZE(max98360a_map
),
391 .fully_routed
= true,
392 .late_probe
= card_late_probe
,
395 static int audio_probe(struct platform_device
*pdev
)
397 static struct snd_soc_card
*card
;
398 struct snd_soc_acpi_mach
*mach
;
399 struct card_private
*ctx
;
402 ctx
= devm_kzalloc(&pdev
->dev
, sizeof(*ctx
), GFP_KERNEL
);
406 /* By default dais[0] is configured for max98373 */
407 if (!strcmp(pdev
->name
, "sof_da7219_max98360a")) {
408 dais
[0] = (struct snd_soc_dai_link
) {
409 .name
= "SSP1-Codec",
413 .ignore_pmdown_time
= 1,
414 SND_SOC_DAILINK_REG(ssp1_pin
, ssp1_m98360a
, platform
) };
417 INIT_LIST_HEAD(&ctx
->hdmi_pcm_list
);
418 card
= (struct snd_soc_card
*)pdev
->id_entry
->driver_data
;
419 card
->dev
= &pdev
->dev
;
421 mach
= pdev
->dev
.platform_data
;
422 ret
= snd_soc_fixup_dai_links_platform_name(card
,
423 mach
->mach_params
.platform
);
427 snd_soc_card_set_drvdata(card
, ctx
);
429 return devm_snd_soc_register_card(&pdev
->dev
, card
);
432 static const struct platform_device_id board_ids
[] = {
434 .name
= "sof_da7219_max98373",
435 .driver_data
= (kernel_ulong_t
)&card_da7219_m98373
,
438 .name
= "sof_da7219_max98360a",
439 .driver_data
= (kernel_ulong_t
)&card_da7219_m98360a
,
444 static struct platform_driver audio
= {
445 .probe
= audio_probe
,
447 .name
= "sof_da7219_max98_360a_373",
448 .pm
= &snd_soc_pm_ops
,
450 .id_table
= board_ids
,
452 module_platform_driver(audio
)
454 /* Module information */
455 MODULE_DESCRIPTION("ASoC Intel(R) SOF Machine driver");
456 MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>");
457 MODULE_LICENSE("GPL v2");
458 MODULE_ALIAS("platform:sof_da7219_max98360a");
459 MODULE_ALIAS("platform:sof_da7219_max98373");