1 // SPDX-License-Identifier: GPL-2.0-only
2 // Copyright (c) 2020 Intel Corporation
5 * ehl_rt5660 - ASOC Machine driver for Elkhart Lake platforms
9 #include <linux/acpi.h>
10 #include <sound/core.h>
11 #include <linux/device.h>
12 #include <linux/errno.h>
13 #include <linux/gfp.h>
14 #include <sound/jack.h>
15 #include <linux/kernel.h>
16 #include <linux/list.h>
17 #include <linux/module.h>
18 #include <sound/pcm.h>
19 #include <sound/pcm_params.h>
20 #include <sound/soc.h>
21 #include <sound/soc-acpi.h>
23 #include "hda_dsp_common.h"
24 #include "../../codecs/rt5660.h"
26 #define DUAL_CHANNEL 2
27 #define HDMI_LINK_START 3
28 #define HDMI_LINE_END 6
30 #define IDISP_CODEC_MASK 0x4
32 struct sof_card_private
{
33 struct list_head hdmi_pcm_list
;
37 static const struct snd_kcontrol_new rt5660_controls
[] = {
38 SOC_DAPM_PIN_SWITCH("Speaker"),
39 /* There are two MICBIAS in rt5660, each for one MIC */
40 SOC_DAPM_PIN_SWITCH("Headset Mic"),
41 SOC_DAPM_PIN_SWITCH("Headset Mic2"),
42 SOC_DAPM_PIN_SWITCH("Line Out"),
45 static const struct snd_soc_dapm_widget rt5660_widgets
[] = {
46 SND_SOC_DAPM_SPK("Speaker", NULL
),
47 SND_SOC_DAPM_MIC("Headset Mic", NULL
),
48 SND_SOC_DAPM_MIC("Headset Mic2", NULL
),
49 SND_SOC_DAPM_MIC("SoC DMIC", NULL
),
50 SND_SOC_DAPM_LINE("Line Out", NULL
),
53 static const struct snd_soc_dapm_route rt5660_map
[] = {
54 {"Speaker", NULL
, "SPO"},
56 {"Headset Mic", NULL
, "MICBIAS1"},
57 {"Headset Mic2", NULL
, "MICBIAS2"},
59 {"IN1P", NULL
, "Headset Mic"},
60 {"IN2P", NULL
, "Headset Mic2"},
62 {"Line Out", NULL
, "LOUTL"},
63 {"Line Out", NULL
, "LOUTR"},
65 {"DMic", NULL
, "SoC DMIC"},
69 struct list_head head
;
70 struct snd_soc_dai
*codec_dai
;
74 static int hdmi_init(struct snd_soc_pcm_runtime
*rtd
)
76 struct sof_card_private
*ctx
= snd_soc_card_get_drvdata(rtd
->card
);
77 struct snd_soc_dai
*dai
= asoc_rtd_to_codec(rtd
, 0);
78 struct sof_hdmi_pcm
*pcm
;
80 pcm
= devm_kzalloc(rtd
->card
->dev
, sizeof(*pcm
), GFP_KERNEL
);
84 /* dai_link id is 1:1 mapped to the PCM device */
85 pcm
->device
= rtd
->dai_link
->id
;
88 list_add_tail(&pcm
->head
, &ctx
->hdmi_pcm_list
);
93 static int card_late_probe(struct snd_soc_card
*card
)
95 struct sof_card_private
*ctx
= snd_soc_card_get_drvdata(card
);
96 struct sof_hdmi_pcm
*pcm
;
98 if (list_empty(&ctx
->hdmi_pcm_list
))
101 if (!ctx
->idisp_codec
)
104 pcm
= list_first_entry(&ctx
->hdmi_pcm_list
, struct sof_hdmi_pcm
, head
);
106 return hda_dsp_hdmi_build_controls(card
, pcm
->codec_dai
->component
);
109 static int rt5660_hw_params(struct snd_pcm_substream
*substream
,
110 struct snd_pcm_hw_params
*params
)
112 struct snd_soc_pcm_runtime
*rtd
= asoc_substream_to_rtd(substream
);
113 struct snd_soc_dai
*codec_dai
= asoc_rtd_to_codec(rtd
, 0);
116 ret
= snd_soc_dai_set_sysclk(codec_dai
,
118 params_rate(params
) * 512,
121 dev_err(rtd
->dev
, "snd_soc_dai_set_sysclk err = %d\n", ret
);
125 ret
= snd_soc_dai_set_pll(codec_dai
, 0,
127 params_rate(params
) * 50,
128 params_rate(params
) * 512);
130 dev_err(codec_dai
->dev
, "can't set codec pll: %d\n", ret
);
135 static struct snd_soc_ops rt5660_ops
= {
136 .hw_params
= rt5660_hw_params
,
139 SND_SOC_DAILINK_DEF(ssp0_pin
,
140 DAILINK_COMP_ARRAY(COMP_CPU("SSP0 Pin")));
142 SND_SOC_DAILINK_DEF(rt5660_codec
,
143 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5660:00", "rt5660-aif1")));
145 SND_SOC_DAILINK_DEF(platform
,
146 DAILINK_COMP_ARRAY(COMP_PLATFORM("0000:00:1f.3")));
148 SND_SOC_DAILINK_DEF(dmic_pin
,
149 DAILINK_COMP_ARRAY(COMP_CPU("DMIC01 Pin")));
150 SND_SOC_DAILINK_DEF(dmic_codec
,
151 DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
152 SND_SOC_DAILINK_DEF(dmic16k
,
153 DAILINK_COMP_ARRAY(COMP_CPU("DMIC16k Pin")));
155 SND_SOC_DAILINK_DEF(idisp1_pin
,
156 DAILINK_COMP_ARRAY(COMP_CPU("iDisp1 Pin")));
157 SND_SOC_DAILINK_DEF(idisp1_codec
,
158 DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi1")));
160 SND_SOC_DAILINK_DEF(idisp2_pin
,
161 DAILINK_COMP_ARRAY(COMP_CPU("iDisp2 Pin")));
162 SND_SOC_DAILINK_DEF(idisp2_codec
,
163 DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi2")));
165 SND_SOC_DAILINK_DEF(idisp3_pin
,
166 DAILINK_COMP_ARRAY(COMP_CPU("iDisp3 Pin")));
167 SND_SOC_DAILINK_DEF(idisp3_codec
,
168 DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi3")));
170 SND_SOC_DAILINK_DEF(idisp4_pin
,
171 DAILINK_COMP_ARRAY(COMP_CPU("iDisp4 Pin")));
172 SND_SOC_DAILINK_DEF(idisp4_codec
,
173 DAILINK_COMP_ARRAY(COMP_CODEC("ehdaudio0D2", "intel-hdmi-hifi4")));
175 static struct snd_soc_dai_link ehl_rt5660_dailink
[] = {
178 .name
= "SSP0-Codec",
185 SND_SOC_DAILINK_REG(ssp0_pin
, rt5660_codec
, platform
),
193 SND_SOC_DAILINK_REG(dmic_pin
, dmic_codec
, platform
),
201 SND_SOC_DAILINK_REG(dmic16k
, dmic_codec
, platform
),
209 SND_SOC_DAILINK_REG(idisp1_pin
, idisp1_codec
, platform
),
217 SND_SOC_DAILINK_REG(idisp2_pin
, idisp2_codec
, platform
),
225 SND_SOC_DAILINK_REG(idisp3_pin
, idisp3_codec
, platform
),
233 SND_SOC_DAILINK_REG(idisp4_pin
, idisp4_codec
, platform
),
238 static struct snd_soc_card snd_soc_card_ehl_rt5660
= {
239 .name
= "ehl-rt5660",
240 .owner
= THIS_MODULE
,
241 .dai_link
= ehl_rt5660_dailink
,
242 .num_links
= ARRAY_SIZE(ehl_rt5660_dailink
),
243 .dapm_widgets
= rt5660_widgets
,
244 .num_dapm_widgets
= ARRAY_SIZE(rt5660_widgets
),
245 .dapm_routes
= rt5660_map
,
246 .num_dapm_routes
= ARRAY_SIZE(rt5660_map
),
247 .controls
= rt5660_controls
,
248 .num_controls
= ARRAY_SIZE(rt5660_controls
),
249 .fully_routed
= true,
250 .late_probe
= card_late_probe
,
253 /* If hdmi codec is not supported, switch to use dummy codec */
254 static void hdmi_link_init(struct snd_soc_card
*card
,
255 struct sof_card_private
*ctx
,
256 struct snd_soc_acpi_mach
*mach
)
258 struct snd_soc_dai_link
*link
;
261 if (mach
->mach_params
.common_hdmi_codec_drv
&&
262 (mach
->mach_params
.codec_mask
& IDISP_CODEC_MASK
)) {
263 ctx
->idisp_codec
= true;
268 * if HDMI is not enabled in kernel config, or
269 * hdmi codec is not supported
271 for (i
= HDMI_LINK_START
; i
<= HDMI_LINE_END
; i
++) {
272 link
= &card
->dai_link
[i
];
273 link
->codecs
[0].name
= "snd-soc-dummy";
274 link
->codecs
[0].dai_name
= "snd-soc-dummy-dai";
278 static int snd_ehl_rt5660_probe(struct platform_device
*pdev
)
280 struct snd_soc_acpi_mach
*mach
;
281 struct snd_soc_card
*card
= &snd_soc_card_ehl_rt5660
;
282 struct sof_card_private
*ctx
;
285 card
->dev
= &pdev
->dev
;
287 ctx
= devm_kzalloc(&pdev
->dev
, sizeof(*ctx
), GFP_KERNEL
);
290 INIT_LIST_HEAD(&ctx
->hdmi_pcm_list
);
291 snd_soc_card_set_drvdata(card
, ctx
);
293 mach
= pdev
->dev
.platform_data
;
294 ret
= snd_soc_fixup_dai_links_platform_name(card
,
295 mach
->mach_params
.platform
);
299 hdmi_link_init(card
, ctx
, mach
);
301 return devm_snd_soc_register_card(&pdev
->dev
, card
);
304 static const struct platform_device_id ehl_board_ids
[] = {
305 { .name
= "ehl_rt5660" },
309 static struct platform_driver snd_ehl_rt5660_driver
= {
311 .name
= "ehl_rt5660",
312 .pm
= &snd_soc_pm_ops
,
314 .probe
= snd_ehl_rt5660_probe
,
315 .id_table
= ehl_board_ids
,
318 module_platform_driver(snd_ehl_rt5660_driver
);
320 MODULE_DESCRIPTION("ASoC Intel(R) Elkhartlake + rt5660 Machine driver");
321 MODULE_AUTHOR("libin.yang@intel.com");
322 MODULE_LICENSE("GPL v2");
323 MODULE_ALIAS("platform:ehl_rt5660");