1 // SPDX-License-Identifier: GPL-2.0-only
3 * ASoC machine driver for Intel Broadwell platforms with RT5650 codec
5 * Copyright 2019, The Chromium OS Authors. All rights reserved.
8 #include <linux/delay.h>
9 #include <linux/gpio/consumer.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <sound/core.h>
13 #include <sound/jack.h>
14 #include <sound/pcm.h>
15 #include <sound/pcm_params.h>
16 #include <sound/soc.h>
17 #include <sound/soc-acpi.h>
19 #include "../common/sst-dsp.h"
20 #include "../haswell/sst-haswell-ipc.h"
22 #include "../../codecs/rt5645.h"
24 struct bdw_rt5650_priv
{
25 struct gpio_desc
*gpio_hp_en
;
26 struct snd_soc_component
*component
;
29 static const struct snd_soc_dapm_widget bdw_rt5650_widgets
[] = {
30 SND_SOC_DAPM_HP("Headphone", NULL
),
31 SND_SOC_DAPM_SPK("Speaker", NULL
),
32 SND_SOC_DAPM_MIC("Headset Mic", NULL
),
33 SND_SOC_DAPM_MIC("DMIC Pair1", NULL
),
34 SND_SOC_DAPM_MIC("DMIC Pair2", NULL
),
37 static const struct snd_soc_dapm_route bdw_rt5650_map
[] = {
39 {"Speaker", NULL
, "SPOL"},
40 {"Speaker", NULL
, "SPOR"},
42 /* Headset jack connectors */
43 {"Headphone", NULL
, "HPOL"},
44 {"Headphone", NULL
, "HPOR"},
45 {"IN1P", NULL
, "Headset Mic"},
46 {"IN1N", NULL
, "Headset Mic"},
49 * DMIC Pair1 are the two DMICs connected on the DMICN1 connector.
50 * DMIC Pair2 are the two DMICs connected on the DMICN2 connector.
51 * Facing the camera, DMIC Pair1 are on the left side, DMIC Pair2
52 * are on the right side.
54 {"DMIC L1", NULL
, "DMIC Pair1"},
55 {"DMIC R1", NULL
, "DMIC Pair1"},
56 {"DMIC L2", NULL
, "DMIC Pair2"},
57 {"DMIC R2", NULL
, "DMIC Pair2"},
59 /* CODEC BE connections */
60 {"SSP0 CODEC IN", NULL
, "AIF1 Capture"},
61 {"AIF1 Playback", NULL
, "SSP0 CODEC OUT"},
64 static const struct snd_kcontrol_new bdw_rt5650_controls
[] = {
65 SOC_DAPM_PIN_SWITCH("Speaker"),
66 SOC_DAPM_PIN_SWITCH("Headphone"),
67 SOC_DAPM_PIN_SWITCH("Headset Mic"),
68 SOC_DAPM_PIN_SWITCH("DMIC Pair1"),
69 SOC_DAPM_PIN_SWITCH("DMIC Pair2"),
73 static struct snd_soc_jack headphone_jack
;
74 static struct snd_soc_jack mic_jack
;
76 static struct snd_soc_jack_pin headphone_jack_pin
= {
78 .mask
= SND_JACK_HEADPHONE
,
81 static struct snd_soc_jack_pin mic_jack_pin
= {
83 .mask
= SND_JACK_MICROPHONE
,
86 static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime
*rtd
,
87 struct snd_pcm_hw_params
*params
)
89 struct snd_interval
*rate
= hw_param_interval(params
,
90 SNDRV_PCM_HW_PARAM_RATE
);
91 struct snd_interval
*channels
= hw_param_interval(params
,
92 SNDRV_PCM_HW_PARAM_CHANNELS
);
94 /* The ADSP will covert the FE rate to 48k, max 4-channels */
95 rate
->min
= rate
->max
= 48000;
99 /* set SSP0 to 24 bit */
100 snd_mask_set_format(hw_param_mask(params
, SNDRV_PCM_HW_PARAM_FORMAT
),
101 SNDRV_PCM_FORMAT_S24_LE
);
106 static int bdw_rt5650_hw_params(struct snd_pcm_substream
*substream
,
107 struct snd_pcm_hw_params
*params
)
109 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
110 struct snd_soc_dai
*codec_dai
= rtd
->codec_dai
;
113 /* Workaround: set codec PLL to 19.2MHz that PLL source is
114 * from MCLK(24MHz) to conform 2.4MHz DMIC clock.
116 ret
= snd_soc_dai_set_pll(codec_dai
, 0, RT5645_PLL1_S_MCLK
,
119 dev_err(rtd
->dev
, "can't set codec pll: %d\n", ret
);
123 /* The actual MCLK freq is 24MHz. The codec is told that MCLK is
124 * 24.576MHz to satisfy the requirement of rl6231_get_clk_info.
125 * ASRC is enabled on AD and DA filters to ensure good audio quality.
127 ret
= snd_soc_dai_set_sysclk(codec_dai
, RT5645_SCLK_S_PLL1
, 24576000,
130 dev_err(rtd
->dev
, "can't set codec sysclk configuration\n");
137 static struct snd_soc_ops bdw_rt5650_ops
= {
138 .hw_params
= bdw_rt5650_hw_params
,
141 #if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
142 static int bdw_rt5650_rtd_init(struct snd_soc_pcm_runtime
*rtd
)
144 struct snd_soc_component
*component
=
145 snd_soc_rtdcom_lookup(rtd
, DRV_NAME
);
146 struct sst_pdata
*pdata
= dev_get_platdata(component
->dev
);
147 struct sst_hsw
*broadwell
= pdata
->dsp
;
150 /* Set ADSP SSP port settings
151 * clock_divider = 4 means BCLK = MCLK/5 = 24MHz/5 = 4.8MHz
153 ret
= sst_hsw_device_set_config(broadwell
, SST_HSW_DEVICE_SSP_0
,
154 SST_HSW_DEVICE_MCLK_FREQ_24_MHZ
,
155 SST_HSW_DEVICE_TDM_CLOCK_MASTER
, 4);
157 dev_err(rtd
->dev
, "error: failed to set device config\n");
165 static int bdw_rt5650_init(struct snd_soc_pcm_runtime
*rtd
)
167 struct bdw_rt5650_priv
*bdw_rt5650
=
168 snd_soc_card_get_drvdata(rtd
->card
);
169 struct snd_soc_component
*component
= rtd
->codec_dai
->component
;
170 struct snd_soc_dai
*codec_dai
= rtd
->codec_dai
;
173 /* Enable codec ASRC function for Stereo DAC/Stereo1 ADC/DMIC/I2S1.
174 * The ASRC clock source is clk_i2s1_asrc.
176 rt5645_sel_asrc_clk_src(component
,
177 RT5645_DA_STEREO_FILTER
|
178 RT5645_DA_MONO_L_FILTER
|
179 RT5645_DA_MONO_R_FILTER
|
180 RT5645_AD_STEREO_FILTER
|
181 RT5645_AD_MONO_L_FILTER
|
182 RT5645_AD_MONO_R_FILTER
,
183 RT5645_CLK_SEL_I2S1_ASRC
);
185 /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
186 ret
= snd_soc_dai_set_tdm_slot(codec_dai
, 0xF, 0xF, 4, 24);
189 dev_err(rtd
->dev
, "can't set codec TDM slot %d\n", ret
);
193 /* Create and initialize headphone jack */
194 if (snd_soc_card_jack_new(rtd
->card
, "Headphone Jack",
195 SND_JACK_HEADPHONE
, &headphone_jack
,
196 &headphone_jack_pin
, 1)) {
197 dev_err(component
->dev
, "Can't create headphone jack\n");
200 /* Create and initialize mic jack */
201 if (snd_soc_card_jack_new(rtd
->card
, "Mic Jack", SND_JACK_MICROPHONE
,
202 &mic_jack
, &mic_jack_pin
, 1)) {
203 dev_err(component
->dev
, "Can't create mic jack\n");
206 rt5645_set_jack_detect(component
, &headphone_jack
, &mic_jack
, NULL
);
208 bdw_rt5650
->component
= component
;
213 /* broadwell digital audio interface glue - connects codec <--> CPU */
214 SND_SOC_DAILINK_DEF(dummy
,
215 DAILINK_COMP_ARRAY(COMP_DUMMY()));
217 SND_SOC_DAILINK_DEF(fe
,
218 DAILINK_COMP_ARRAY(COMP_CPU("System Pin")));
220 SND_SOC_DAILINK_DEF(platform
,
221 DAILINK_COMP_ARRAY(COMP_PLATFORM("haswell-pcm-audio")));
223 SND_SOC_DAILINK_DEF(be
,
224 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5650:00", "rt5645-aif1")));
226 #if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
227 SND_SOC_DAILINK_DEF(ssp0_port
,
228 DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port")));
230 SND_SOC_DAILINK_DEF(ssp0_port
,
231 DAILINK_COMP_ARRAY(COMP_DUMMY()));
234 static struct snd_soc_dai_link bdw_rt5650_dais
[] = {
235 /* Front End DAI links */
237 .name
= "System PCM",
238 .stream_name
= "System Playback",
240 #if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
241 .init
= bdw_rt5650_rtd_init
,
244 SND_SOC_DPCM_TRIGGER_POST
,
245 SND_SOC_DPCM_TRIGGER_POST
249 SND_SOC_DAILINK_REG(fe
, dummy
, platform
),
252 /* Back End DAI links */
258 .dai_fmt
= SND_SOC_DAIFMT_DSP_B
| SND_SOC_DAIFMT_NB_NF
|
259 SND_SOC_DAIFMT_CBS_CFS
,
261 .ignore_pmdown_time
= 1,
262 .be_hw_params_fixup
= broadwell_ssp0_fixup
,
263 .ops
= &bdw_rt5650_ops
,
266 .init
= bdw_rt5650_init
,
267 SND_SOC_DAILINK_REG(ssp0_port
, be
, platform
),
271 /* ASoC machine driver for Broadwell DSP + RT5650 */
272 static struct snd_soc_card bdw_rt5650_card
= {
273 .name
= "bdw-rt5650",
274 .owner
= THIS_MODULE
,
275 .dai_link
= bdw_rt5650_dais
,
276 .num_links
= ARRAY_SIZE(bdw_rt5650_dais
),
277 .dapm_widgets
= bdw_rt5650_widgets
,
278 .num_dapm_widgets
= ARRAY_SIZE(bdw_rt5650_widgets
),
279 .dapm_routes
= bdw_rt5650_map
,
280 .num_dapm_routes
= ARRAY_SIZE(bdw_rt5650_map
),
281 .controls
= bdw_rt5650_controls
,
282 .num_controls
= ARRAY_SIZE(bdw_rt5650_controls
),
283 .fully_routed
= true,
286 static int bdw_rt5650_probe(struct platform_device
*pdev
)
288 struct bdw_rt5650_priv
*bdw_rt5650
;
289 struct snd_soc_acpi_mach
*mach
;
292 bdw_rt5650_card
.dev
= &pdev
->dev
;
294 /* Allocate driver private struct */
295 bdw_rt5650
= devm_kzalloc(&pdev
->dev
, sizeof(struct bdw_rt5650_priv
),
300 /* override plaform name, if required */
301 mach
= (&pdev
->dev
)->platform_data
;
302 ret
= snd_soc_fixup_dai_links_platform_name(&bdw_rt5650_card
,
303 mach
->mach_params
.platform
);
308 snd_soc_card_set_drvdata(&bdw_rt5650_card
, bdw_rt5650
);
310 return devm_snd_soc_register_card(&pdev
->dev
, &bdw_rt5650_card
);
313 static struct platform_driver bdw_rt5650_audio
= {
314 .probe
= bdw_rt5650_probe
,
316 .name
= "bdw-rt5650",
317 .pm
= &snd_soc_pm_ops
,
321 module_platform_driver(bdw_rt5650_audio
)
323 /* Module information */
324 MODULE_AUTHOR("Ben Zhang <benzh@chromium.org>");
325 MODULE_DESCRIPTION("Intel Broadwell RT5650 machine driver");
326 MODULE_LICENSE("GPL v2");
327 MODULE_ALIAS("platform:bdw-rt5650");