]>
Commit | Line | Data |
---|---|---|
8e8e69d6 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
e18acdc0 FY |
2 | /* |
3 | * cht-bsw-rt5645.c - ASoc Machine driver for Intel Cherryview-based platforms | |
4 | * Cherrytrail and Braswell, with RT5645 codec. | |
5 | * | |
6 | * Copyright (C) 2015 Intel Corp | |
7 | * Author: Fang, Yang A <[email protected]> | |
8 | * N,Harshapriya <[email protected]> | |
9 | * This file is modified from cht_bsw_rt5672.c | |
10 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
11 | * | |
e18acdc0 FY |
12 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
13 | */ | |
14 | ||
15 | #include <linux/module.h> | |
16 | #include <linux/platform_device.h> | |
6cdf01a5 PLB |
17 | #include <linux/acpi.h> |
18 | #include <linux/clk.h> | |
22af2911 | 19 | #include <linux/dmi.h> |
e18acdc0 FY |
20 | #include <linux/slab.h> |
21 | #include <sound/pcm.h> | |
22 | #include <sound/pcm_params.h> | |
23 | #include <sound/soc.h> | |
24 | #include <sound/jack.h> | |
7feb2f78 | 25 | #include <sound/soc-acpi.h> |
e56c72d5 | 26 | #include "../../codecs/rt5645.h" |
b97169da | 27 | #include "../atom/sst-atom-controls.h" |
536cfd2f | 28 | #include "../common/soc-intel-quirks.h" |
e18acdc0 FY |
29 | |
30 | #define CHT_PLAT_CLK_3_HZ 19200000 | |
22af2911 PLB |
31 | #define CHT_CODEC_DAI1 "rt5645-aif1" |
32 | #define CHT_CODEC_DAI2 "rt5645-aif2" | |
e18acdc0 | 33 | |
c4ba51ba FY |
34 | struct cht_acpi_card { |
35 | char *codec_id; | |
36 | int codec_type; | |
37 | struct snd_soc_card *soc_card; | |
38 | }; | |
39 | ||
e18acdc0 | 40 | struct cht_mc_private { |
673c4f89 | 41 | struct snd_soc_jack jack; |
c4ba51ba | 42 | struct cht_acpi_card *acpi_card; |
2be2d579 | 43 | char codec_name[SND_ACPI_I2C_ID_LEN]; |
a50477e5 | 44 | struct clk *mclk; |
e18acdc0 FY |
45 | }; |
46 | ||
6cdf01a5 | 47 | #define CHT_RT5645_MAP(quirk) ((quirk) & GENMASK(7, 0)) |
22af2911 PLB |
48 | #define CHT_RT5645_SSP2_AIF2 BIT(16) /* default is using AIF1 */ |
49 | #define CHT_RT5645_SSP0_AIF1 BIT(17) | |
50 | #define CHT_RT5645_SSP0_AIF2 BIT(18) | |
adebb111 | 51 | #define CHT_RT5645_PMC_PLT_CLK_0 BIT(19) |
22af2911 PLB |
52 | |
53 | static unsigned long cht_rt5645_quirk = 0; | |
54 | ||
55 | static void log_quirks(struct device *dev) | |
56 | { | |
57 | if (cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) | |
58 | dev_info(dev, "quirk SSP2_AIF2 enabled"); | |
59 | if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) | |
60 | dev_info(dev, "quirk SSP0_AIF1 enabled"); | |
61 | if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2) | |
62 | dev_info(dev, "quirk SSP0_AIF2 enabled"); | |
adebb111 SM |
63 | if (cht_rt5645_quirk & CHT_RT5645_PMC_PLT_CLK_0) |
64 | dev_info(dev, "quirk PMC_PLT_CLK_0 enabled"); | |
22af2911 PLB |
65 | } |
66 | ||
e18acdc0 FY |
67 | static int platform_clock_control(struct snd_soc_dapm_widget *w, |
68 | struct snd_kcontrol *k, int event) | |
69 | { | |
70 | struct snd_soc_dapm_context *dapm = w->dapm; | |
71 | struct snd_soc_card *card = dapm->card; | |
72 | struct snd_soc_dai *codec_dai; | |
a50477e5 | 73 | struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card); |
e18acdc0 FY |
74 | int ret; |
75 | ||
dfb6ec7a PLB |
76 | codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI1); |
77 | if (!codec_dai) | |
78 | codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI2); | |
79 | ||
e18acdc0 FY |
80 | if (!codec_dai) { |
81 | dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n"); | |
82 | return -EIO; | |
83 | } | |
84 | ||
a50477e5 | 85 | if (SND_SOC_DAPM_EVENT_ON(event)) { |
6cdf01a5 PLB |
86 | ret = clk_prepare_enable(ctx->mclk); |
87 | if (ret < 0) { | |
88 | dev_err(card->dev, | |
89 | "could not configure MCLK state"); | |
90 | return ret; | |
a50477e5 PLB |
91 | } |
92 | } else { | |
93 | /* Set codec sysclk source to its internal clock because codec PLL will | |
94 | * be off when idle and MCLK will also be off when codec is | |
95 | * runtime suspended. Codec needs clock for jack detection and button | |
96 | * press. MCLK is turned off with clock framework or ACPI. | |
97 | */ | |
98 | ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK, | |
99 | 48000 * 512, SND_SOC_CLOCK_IN); | |
100 | if (ret < 0) { | |
101 | dev_err(card->dev, "can't set codec sysclk: %d\n", ret); | |
102 | return ret; | |
103 | } | |
e18acdc0 | 104 | |
6cdf01a5 | 105 | clk_disable_unprepare(ctx->mclk); |
e18acdc0 FY |
106 | } |
107 | ||
108 | return 0; | |
109 | } | |
110 | ||
111 | static const struct snd_soc_dapm_widget cht_dapm_widgets[] = { | |
112 | SND_SOC_DAPM_HP("Headphone", NULL), | |
113 | SND_SOC_DAPM_MIC("Headset Mic", NULL), | |
114 | SND_SOC_DAPM_MIC("Int Mic", NULL), | |
b70b3099 | 115 | SND_SOC_DAPM_MIC("Int Analog Mic", NULL), |
e18acdc0 FY |
116 | SND_SOC_DAPM_SPK("Ext Spk", NULL), |
117 | SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, | |
a50477e5 | 118 | platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), |
e18acdc0 FY |
119 | }; |
120 | ||
c4ba51ba | 121 | static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = { |
e18acdc0 FY |
122 | {"IN1P", NULL, "Headset Mic"}, |
123 | {"IN1N", NULL, "Headset Mic"}, | |
124 | {"DMIC L1", NULL, "Int Mic"}, | |
125 | {"DMIC R1", NULL, "Int Mic"}, | |
b70b3099 HG |
126 | {"IN2P", NULL, "Int Analog Mic"}, |
127 | {"IN2N", NULL, "Int Analog Mic"}, | |
e18acdc0 FY |
128 | {"Headphone", NULL, "HPOL"}, |
129 | {"Headphone", NULL, "HPOR"}, | |
130 | {"Ext Spk", NULL, "SPOL"}, | |
131 | {"Ext Spk", NULL, "SPOR"}, | |
e18acdc0 FY |
132 | {"Headphone", NULL, "Platform Clock"}, |
133 | {"Headset Mic", NULL, "Platform Clock"}, | |
134 | {"Int Mic", NULL, "Platform Clock"}, | |
b70b3099 HG |
135 | {"Int Analog Mic", NULL, "Platform Clock"}, |
136 | {"Int Analog Mic", NULL, "micbias1"}, | |
137 | {"Int Analog Mic", NULL, "micbias2"}, | |
e18acdc0 FY |
138 | {"Ext Spk", NULL, "Platform Clock"}, |
139 | }; | |
140 | ||
c4ba51ba FY |
141 | static const struct snd_soc_dapm_route cht_rt5650_audio_map[] = { |
142 | {"IN1P", NULL, "Headset Mic"}, | |
143 | {"IN1N", NULL, "Headset Mic"}, | |
144 | {"DMIC L2", NULL, "Int Mic"}, | |
145 | {"DMIC R2", NULL, "Int Mic"}, | |
146 | {"Headphone", NULL, "HPOL"}, | |
147 | {"Headphone", NULL, "HPOR"}, | |
148 | {"Ext Spk", NULL, "SPOL"}, | |
149 | {"Ext Spk", NULL, "SPOR"}, | |
22af2911 PLB |
150 | {"Headphone", NULL, "Platform Clock"}, |
151 | {"Headset Mic", NULL, "Platform Clock"}, | |
152 | {"Int Mic", NULL, "Platform Clock"}, | |
153 | {"Ext Spk", NULL, "Platform Clock"}, | |
154 | }; | |
155 | ||
156 | static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif1_map[] = { | |
c4ba51ba FY |
157 | {"AIF1 Playback", NULL, "ssp2 Tx"}, |
158 | {"ssp2 Tx", NULL, "codec_out0"}, | |
159 | {"ssp2 Tx", NULL, "codec_out1"}, | |
160 | {"codec_in0", NULL, "ssp2 Rx" }, | |
161 | {"codec_in1", NULL, "ssp2 Rx" }, | |
162 | {"ssp2 Rx", NULL, "AIF1 Capture"}, | |
22af2911 PLB |
163 | }; |
164 | ||
165 | static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif2_map[] = { | |
166 | {"AIF2 Playback", NULL, "ssp2 Tx"}, | |
167 | {"ssp2 Tx", NULL, "codec_out0"}, | |
168 | {"ssp2 Tx", NULL, "codec_out1"}, | |
169 | {"codec_in0", NULL, "ssp2 Rx" }, | |
170 | {"codec_in1", NULL, "ssp2 Rx" }, | |
171 | {"ssp2 Rx", NULL, "AIF2 Capture"}, | |
172 | }; | |
173 | ||
174 | static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif1_map[] = { | |
175 | {"AIF1 Playback", NULL, "ssp0 Tx"}, | |
176 | {"ssp0 Tx", NULL, "modem_out"}, | |
177 | {"modem_in", NULL, "ssp0 Rx" }, | |
178 | {"ssp0 Rx", NULL, "AIF1 Capture"}, | |
179 | }; | |
180 | ||
181 | static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif2_map[] = { | |
182 | {"AIF2 Playback", NULL, "ssp0 Tx"}, | |
183 | {"ssp0 Tx", NULL, "modem_out"}, | |
184 | {"modem_in", NULL, "ssp0 Rx" }, | |
185 | {"ssp0 Rx", NULL, "AIF2 Capture"}, | |
c4ba51ba FY |
186 | }; |
187 | ||
e18acdc0 FY |
188 | static const struct snd_kcontrol_new cht_mc_controls[] = { |
189 | SOC_DAPM_PIN_SWITCH("Headphone"), | |
190 | SOC_DAPM_PIN_SWITCH("Headset Mic"), | |
191 | SOC_DAPM_PIN_SWITCH("Int Mic"), | |
b70b3099 | 192 | SOC_DAPM_PIN_SWITCH("Int Analog Mic"), |
e18acdc0 FY |
193 | SOC_DAPM_PIN_SWITCH("Ext Spk"), |
194 | }; | |
195 | ||
2303b32f CC |
196 | static struct snd_soc_jack_pin cht_bsw_jack_pins[] = { |
197 | { | |
198 | .pin = "Headphone", | |
199 | .mask = SND_JACK_HEADPHONE, | |
200 | }, | |
201 | { | |
202 | .pin = "Headset Mic", | |
203 | .mask = SND_JACK_MICROPHONE, | |
204 | }, | |
205 | }; | |
206 | ||
e18acdc0 FY |
207 | static int cht_aif1_hw_params(struct snd_pcm_substream *substream, |
208 | struct snd_pcm_hw_params *params) | |
209 | { | |
a2c1125e KM |
210 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
211 | struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); | |
e18acdc0 FY |
212 | int ret; |
213 | ||
214 | /* set codec PLL source to the 19.2MHz platform clock (MCLK) */ | |
215 | ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK, | |
216 | CHT_PLAT_CLK_3_HZ, params_rate(params) * 512); | |
217 | if (ret < 0) { | |
218 | dev_err(rtd->dev, "can't set codec pll: %d\n", ret); | |
219 | return ret; | |
220 | } | |
221 | ||
222 | ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_PLL1, | |
223 | params_rate(params) * 512, SND_SOC_CLOCK_IN); | |
224 | if (ret < 0) { | |
225 | dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret); | |
226 | return ret; | |
227 | } | |
228 | ||
229 | return 0; | |
230 | } | |
231 | ||
22af2911 PLB |
232 | static int cht_rt5645_quirk_cb(const struct dmi_system_id *id) |
233 | { | |
234 | cht_rt5645_quirk = (unsigned long)id->driver_data; | |
235 | return 1; | |
236 | } | |
22af2911 PLB |
237 | |
238 | static const struct dmi_system_id cht_rt5645_quirk_table[] = { | |
adebb111 SM |
239 | { |
240 | /* Strago family Chromebooks */ | |
241 | .callback = cht_rt5645_quirk_cb, | |
242 | .matches = { | |
243 | DMI_MATCH(DMI_PRODUCT_FAMILY, "Intel_Strago"), | |
244 | }, | |
245 | .driver_data = (void *)CHT_RT5645_PMC_PLT_CLK_0, | |
246 | }, | |
22af2911 PLB |
247 | { |
248 | }, | |
249 | }; | |
250 | ||
e18acdc0 FY |
251 | static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) |
252 | { | |
22af2911 | 253 | struct snd_soc_card *card = runtime->card; |
e18acdc0 | 254 | struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card); |
a2c1125e | 255 | struct snd_soc_component *component = snd_soc_rtd_to_codec(runtime, 0)->component; |
6cdf01a5 PLB |
256 | int jack_type; |
257 | int ret; | |
e18acdc0 | 258 | |
d74390b5 PLB |
259 | if ((cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) || |
260 | (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) { | |
261 | /* Select clk_i2s2_asrc as ASRC clock source */ | |
79223bf1 | 262 | rt5645_sel_asrc_clk_src(component, |
d74390b5 PLB |
263 | RT5645_DA_STEREO_FILTER | |
264 | RT5645_DA_MONO_L_FILTER | | |
265 | RT5645_DA_MONO_R_FILTER | | |
266 | RT5645_AD_STEREO_FILTER, | |
267 | RT5645_CLK_SEL_I2S2_ASRC); | |
268 | } else { | |
269 | /* Select clk_i2s1_asrc as ASRC clock source */ | |
79223bf1 | 270 | rt5645_sel_asrc_clk_src(component, |
d74390b5 PLB |
271 | RT5645_DA_STEREO_FILTER | |
272 | RT5645_DA_MONO_L_FILTER | | |
273 | RT5645_DA_MONO_R_FILTER | | |
274 | RT5645_AD_STEREO_FILTER, | |
275 | RT5645_CLK_SEL_I2S1_ASRC); | |
276 | } | |
e18acdc0 | 277 | |
22af2911 PLB |
278 | if (cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) { |
279 | ret = snd_soc_dapm_add_routes(&card->dapm, | |
280 | cht_rt5645_ssp2_aif2_map, | |
281 | ARRAY_SIZE(cht_rt5645_ssp2_aif2_map)); | |
282 | } else if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) { | |
283 | ret = snd_soc_dapm_add_routes(&card->dapm, | |
284 | cht_rt5645_ssp0_aif1_map, | |
285 | ARRAY_SIZE(cht_rt5645_ssp0_aif1_map)); | |
286 | } else if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2) { | |
287 | ret = snd_soc_dapm_add_routes(&card->dapm, | |
288 | cht_rt5645_ssp0_aif2_map, | |
289 | ARRAY_SIZE(cht_rt5645_ssp0_aif2_map)); | |
290 | } else { | |
291 | ret = snd_soc_dapm_add_routes(&card->dapm, | |
292 | cht_rt5645_ssp2_aif1_map, | |
293 | ARRAY_SIZE(cht_rt5645_ssp2_aif1_map)); | |
294 | } | |
295 | if (ret) | |
296 | return ret; | |
297 | ||
673c4f89 FY |
298 | if (ctx->acpi_card->codec_type == CODEC_TYPE_RT5650) |
299 | jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | | |
300 | SND_JACK_BTN_0 | SND_JACK_BTN_1 | | |
301 | SND_JACK_BTN_2 | SND_JACK_BTN_3; | |
302 | else | |
303 | jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE; | |
e18acdc0 | 304 | |
19aed2d6 AO |
305 | ret = snd_soc_card_jack_new_pins(runtime->card, "Headset", jack_type, |
306 | &ctx->jack, cht_bsw_jack_pins, | |
307 | ARRAY_SIZE(cht_bsw_jack_pins)); | |
e18acdc0 | 308 | if (ret) { |
673c4f89 | 309 | dev_err(runtime->dev, "Headset jack creation failed %d\n", ret); |
e18acdc0 FY |
310 | return ret; |
311 | } | |
312 | ||
79223bf1 | 313 | rt5645_set_jack_detect(component, &ctx->jack, &ctx->jack, &ctx->jack); |
e18acdc0 | 314 | |
a50477e5 | 315 | |
6cdf01a5 PLB |
316 | /* |
317 | * The firmware might enable the clock at | |
318 | * boot (this information may or may not | |
319 | * be reflected in the enable clock register). | |
320 | * To change the rate we must disable the clock | |
321 | * first to cover these cases. Due to common | |
322 | * clock framework restrictions that do not allow | |
323 | * to disable a clock that has not been enabled, | |
324 | * we need to enable the clock first. | |
325 | */ | |
326 | ret = clk_prepare_enable(ctx->mclk); | |
327 | if (!ret) | |
328 | clk_disable_unprepare(ctx->mclk); | |
329 | ||
330 | ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ); | |
331 | ||
332 | if (ret) | |
333 | dev_err(runtime->dev, "unable to set MCLK rate\n"); | |
a50477e5 | 334 | |
e18acdc0 FY |
335 | return ret; |
336 | } | |
337 | ||
338 | static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, | |
339 | struct snd_pcm_hw_params *params) | |
340 | { | |
22af2911 | 341 | int ret; |
e18acdc0 FY |
342 | struct snd_interval *rate = hw_param_interval(params, |
343 | SNDRV_PCM_HW_PARAM_RATE); | |
344 | struct snd_interval *channels = hw_param_interval(params, | |
345 | SNDRV_PCM_HW_PARAM_CHANNELS); | |
346 | ||
4088355a | 347 | /* The DSP will convert the FE rate to 48k, stereo, 24bits */ |
e18acdc0 FY |
348 | rate->min = rate->max = 48000; |
349 | channels->min = channels->max = 2; | |
350 | ||
22af2911 PLB |
351 | if ((cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) || |
352 | (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) { | |
353 | ||
354 | /* set SSP0 to 16-bit */ | |
355 | params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); | |
356 | ||
357 | /* | |
358 | * Default mode for SSP configuration is TDM 4 slot, override config | |
359 | * with explicit setting to I2S 2ch 16-bit. The word length is set with | |
360 | * dai_set_tdm_slot() since there is no other API exposed | |
361 | */ | |
a2c1125e | 362 | ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0), |
22af2911 | 363 | SND_SOC_DAIFMT_I2S | |
7bde09df | 364 | SND_SOC_DAIFMT_NB_NF | |
add9ee8c | 365 | SND_SOC_DAIFMT_BP_FP |
7bde09df PLB |
366 | ); |
367 | if (ret < 0) { | |
368 | dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); | |
369 | return ret; | |
370 | } | |
371 | ||
a2c1125e | 372 | ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_codec(rtd, 0), |
7bde09df PLB |
373 | SND_SOC_DAIFMT_I2S | |
374 | SND_SOC_DAIFMT_NB_NF | | |
add9ee8c | 375 | SND_SOC_DAIFMT_BC_FC |
22af2911 PLB |
376 | ); |
377 | if (ret < 0) { | |
378 | dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); | |
379 | return ret; | |
380 | } | |
381 | ||
a2c1125e | 382 | ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 16); |
22af2911 PLB |
383 | if (ret < 0) { |
384 | dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); | |
385 | return ret; | |
386 | } | |
387 | ||
388 | } else { | |
389 | ||
390 | /* set SSP2 to 24-bit */ | |
391 | params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); | |
392 | ||
7bde09df PLB |
393 | /* |
394 | * Default mode for SSP configuration is TDM 4 slot | |
395 | */ | |
a2c1125e | 396 | ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_codec(rtd, 0), |
7bde09df PLB |
397 | SND_SOC_DAIFMT_DSP_B | |
398 | SND_SOC_DAIFMT_IB_NF | | |
add9ee8c | 399 | SND_SOC_DAIFMT_BC_FC); |
7bde09df PLB |
400 | if (ret < 0) { |
401 | dev_err(rtd->dev, "can't set format to TDM %d\n", ret); | |
402 | return ret; | |
403 | } | |
404 | ||
405 | /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ | |
a2c1125e | 406 | ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_codec(rtd, 0), 0xF, 0xF, 4, 24); |
7bde09df PLB |
407 | if (ret < 0) { |
408 | dev_err(rtd->dev, "can't set codec TDM slot %d\n", ret); | |
409 | return ret; | |
410 | } | |
22af2911 | 411 | } |
e18acdc0 FY |
412 | return 0; |
413 | } | |
414 | ||
e18acdc0 FY |
415 | static int cht_aif1_startup(struct snd_pcm_substream *substream) |
416 | { | |
3d6a76c4 LPC |
417 | return snd_pcm_hw_constraint_single(substream->runtime, |
418 | SNDRV_PCM_HW_PARAM_RATE, 48000); | |
e18acdc0 FY |
419 | } |
420 | ||
9b6fdef6 | 421 | static const struct snd_soc_ops cht_aif1_ops = { |
e18acdc0 FY |
422 | .startup = cht_aif1_startup, |
423 | }; | |
424 | ||
9b6fdef6 | 425 | static const struct snd_soc_ops cht_be_ssp2_ops = { |
e18acdc0 FY |
426 | .hw_params = cht_aif1_hw_params, |
427 | }; | |
428 | ||
75909d7e KM |
429 | SND_SOC_DAILINK_DEF(dummy, |
430 | DAILINK_COMP_ARRAY(COMP_DUMMY())); | |
431 | ||
432 | SND_SOC_DAILINK_DEF(media, | |
433 | DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai"))); | |
434 | ||
435 | SND_SOC_DAILINK_DEF(deepbuffer, | |
436 | DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai"))); | |
437 | ||
438 | SND_SOC_DAILINK_DEF(ssp2_port, | |
439 | DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port"))); | |
440 | SND_SOC_DAILINK_DEF(ssp2_codec, | |
441 | DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5645:00", "rt5645-aif1"))); | |
442 | ||
443 | SND_SOC_DAILINK_DEF(platform, | |
444 | DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform"))); | |
445 | ||
e18acdc0 FY |
446 | static struct snd_soc_dai_link cht_dailink[] = { |
447 | [MERR_DPCM_AUDIO] = { | |
448 | .name = "Audio Port", | |
449 | .stream_name = "Audio", | |
c4ba51ba | 450 | .nonatomic = true, |
e18acdc0 FY |
451 | .dynamic = 1, |
452 | .dpcm_playback = 1, | |
453 | .dpcm_capture = 1, | |
454 | .ops = &cht_aif1_ops, | |
75909d7e | 455 | SND_SOC_DAILINK_REG(media, dummy, platform), |
e18acdc0 | 456 | }, |
d35eb96a PLB |
457 | [MERR_DPCM_DEEP_BUFFER] = { |
458 | .name = "Deep-Buffer Audio Port", | |
459 | .stream_name = "Deep-Buffer Audio", | |
d35eb96a PLB |
460 | .nonatomic = true, |
461 | .dynamic = 1, | |
462 | .dpcm_playback = 1, | |
463 | .ops = &cht_aif1_ops, | |
75909d7e | 464 | SND_SOC_DAILINK_REG(deepbuffer, dummy, platform), |
d35eb96a | 465 | }, |
e18acdc0 FY |
466 | /* CODEC<->CODEC link */ |
467 | /* back ends */ | |
468 | { | |
469 | .name = "SSP2-Codec", | |
149f7757 | 470 | .id = 0, |
e18acdc0 | 471 | .no_pcm = 1, |
e18acdc0 FY |
472 | .init = cht_codec_init, |
473 | .be_hw_params_fixup = cht_codec_fixup, | |
e18acdc0 FY |
474 | .dpcm_playback = 1, |
475 | .dpcm_capture = 1, | |
476 | .ops = &cht_be_ssp2_ops, | |
75909d7e | 477 | SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform), |
e18acdc0 FY |
478 | }, |
479 | }; | |
480 | ||
7bfbddfc | 481 | /* use space before codec name to simplify card ID, and simplify driver name */ |
41656c3d PLB |
482 | #define SOF_CARD_RT5645_NAME "bytcht rt5645" /* card name 'sof-bytcht rt5645' */ |
483 | #define SOF_CARD_RT5650_NAME "bytcht rt5650" /* card name 'sof-bytcht rt5650' */ | |
484 | #define SOF_DRIVER_NAME "SOF" | |
485 | ||
7bfbddfc PLB |
486 | #define CARD_RT5645_NAME "chtrt5645" |
487 | #define CARD_RT5650_NAME "chtrt5650" | |
488 | #define DRIVER_NAME NULL /* card name will be used for driver name */ | |
7bfbddfc | 489 | |
e18acdc0 | 490 | /* SoC card */ |
c4ba51ba | 491 | static struct snd_soc_card snd_soc_card_chtrt5645 = { |
54d8697f | 492 | .owner = THIS_MODULE, |
e18acdc0 FY |
493 | .dai_link = cht_dailink, |
494 | .num_links = ARRAY_SIZE(cht_dailink), | |
495 | .dapm_widgets = cht_dapm_widgets, | |
496 | .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets), | |
c4ba51ba FY |
497 | .dapm_routes = cht_rt5645_audio_map, |
498 | .num_dapm_routes = ARRAY_SIZE(cht_rt5645_audio_map), | |
e18acdc0 FY |
499 | .controls = cht_mc_controls, |
500 | .num_controls = ARRAY_SIZE(cht_mc_controls), | |
501 | }; | |
502 | ||
c4ba51ba | 503 | static struct snd_soc_card snd_soc_card_chtrt5650 = { |
54d8697f | 504 | .owner = THIS_MODULE, |
c4ba51ba FY |
505 | .dai_link = cht_dailink, |
506 | .num_links = ARRAY_SIZE(cht_dailink), | |
507 | .dapm_widgets = cht_dapm_widgets, | |
508 | .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets), | |
509 | .dapm_routes = cht_rt5650_audio_map, | |
510 | .num_dapm_routes = ARRAY_SIZE(cht_rt5650_audio_map), | |
511 | .controls = cht_mc_controls, | |
512 | .num_controls = ARRAY_SIZE(cht_mc_controls), | |
513 | }; | |
514 | ||
515 | static struct cht_acpi_card snd_soc_cards[] = { | |
07d5c17b | 516 | {"10EC5640", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645}, |
c4ba51ba | 517 | {"10EC5645", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645}, |
11ad8089 | 518 | {"10EC5648", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645}, |
bf92c6ef | 519 | {"10EC3270", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645}, |
c4ba51ba FY |
520 | {"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650}, |
521 | }; | |
522 | ||
2be2d579 | 523 | static char cht_rt5645_codec_name[SND_ACPI_I2C_ID_LEN]; |
07d5c17b | 524 | |
22af2911 PLB |
525 | struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ |
526 | u64 aif_value; /* 1: AIF1, 2: AIF2 */ | |
527 | u64 mclock_value; /* usually 25MHz (0x17d7940), ignored */ | |
528 | }; | |
529 | ||
e18acdc0 FY |
530 | static int snd_cht_mc_probe(struct platform_device *pdev) |
531 | { | |
c4ba51ba | 532 | struct snd_soc_card *card = snd_soc_cards[0].soc_card; |
7feb2f78 | 533 | struct snd_soc_acpi_mach *mach; |
3a934e7c | 534 | const char *platform_name; |
6cdf01a5 | 535 | struct cht_mc_private *drv; |
fe4c283a | 536 | struct acpi_device *adev; |
f87b4402 | 537 | struct device *codec_dev; |
41656c3d | 538 | bool sof_parent; |
42648c22 | 539 | bool found = false; |
22af2911 | 540 | bool is_bytcr = false; |
6cdf01a5 PLB |
541 | int dai_index = 0; |
542 | int ret_val = 0; | |
543 | int i; | |
adebb111 | 544 | const char *mclk_name; |
e18acdc0 | 545 | |
f3cc330a | 546 | drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); |
e18acdc0 FY |
547 | if (!drv) |
548 | return -ENOMEM; | |
549 | ||
42432196 | 550 | mach = pdev->dev.platform_data; |
42648c22 | 551 | |
c4ba51ba | 552 | for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) { |
42648c22 PLB |
553 | if (acpi_dev_found(snd_soc_cards[i].codec_id) && |
554 | (!strncmp(snd_soc_cards[i].codec_id, mach->id, 8))) { | |
c4ba51ba FY |
555 | dev_dbg(&pdev->dev, |
556 | "found codec %s\n", snd_soc_cards[i].codec_id); | |
557 | card = snd_soc_cards[i].soc_card; | |
558 | drv->acpi_card = &snd_soc_cards[i]; | |
42648c22 | 559 | found = true; |
c4ba51ba FY |
560 | break; |
561 | } | |
562 | } | |
42648c22 PLB |
563 | |
564 | if (!found) { | |
565 | dev_err(&pdev->dev, "No matching HID found in supported list\n"); | |
566 | return -ENODEV; | |
567 | } | |
568 | ||
c4ba51ba | 569 | card->dev = &pdev->dev; |
a823a179 | 570 | sprintf(drv->codec_name, "i2c-%s:00", drv->acpi_card->codec_id); |
c8560b7c | 571 | |
c4ba51ba | 572 | /* set correct codec name */ |
c8560b7c | 573 | for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) |
7d99a70b HG |
574 | if (card->dai_link[i].codecs->name && |
575 | !strcmp(card->dai_link[i].codecs->name, | |
75909d7e KM |
576 | "i2c-10EC5645:00")) { |
577 | card->dai_link[i].codecs->name = drv->codec_name; | |
07d5c17b VK |
578 | dai_index = i; |
579 | } | |
580 | ||
581 | /* fixup codec name based on HID */ | |
fe4c283a AS |
582 | adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1); |
583 | if (adev) { | |
22af2911 | 584 | snprintf(cht_rt5645_codec_name, sizeof(cht_rt5645_codec_name), |
fe4c283a | 585 | "i2c-%s", acpi_dev_name(adev)); |
75909d7e | 586 | cht_dailink[dai_index].codecs->name = cht_rt5645_codec_name; |
22af2911 | 587 | } |
f87b4402 HG |
588 | /* acpi_get_first_physical_node() returns a borrowed ref, no need to deref */ |
589 | codec_dev = acpi_get_first_physical_node(adev); | |
5360a1c0 | 590 | acpi_dev_put(adev); |
f87b4402 HG |
591 | if (!codec_dev) |
592 | return -EPROBE_DEFER; | |
593 | ||
594 | snd_soc_card_chtrt5645.components = rt5645_components(codec_dev); | |
595 | snd_soc_card_chtrt5650.components = rt5645_components(codec_dev); | |
22af2911 PLB |
596 | |
597 | /* | |
598 | * swap SSP0 if bytcr is detected | |
599 | * (will be overridden if DMI quirk is detected) | |
600 | */ | |
536cfd2f | 601 | if (soc_intel_is_byt()) { |
3ee1cd4f | 602 | if (mach->mach_params.acpi_ipc_irq_index == 0) |
22af2911 PLB |
603 | is_bytcr = true; |
604 | } | |
605 | ||
606 | if (is_bytcr) { | |
607 | /* | |
608 | * Baytrail CR platforms may have CHAN package in BIOS, try | |
609 | * to find relevant routing quirk based as done on Windows | |
610 | * platforms. We have to read the information directly from the | |
611 | * BIOS, at this stage the card is not created and the links | |
612 | * with the codec driver/pdata are non-existent | |
613 | */ | |
614 | ||
9972773c | 615 | struct acpi_chan_package chan_package = { 0 }; |
22af2911 PLB |
616 | |
617 | /* format specified: 2 64-bit integers */ | |
618 | struct acpi_buffer format = {sizeof("NN"), "NN"}; | |
619 | struct acpi_buffer state = {0, NULL}; | |
7feb2f78 | 620 | struct snd_soc_acpi_package_context pkg_ctx; |
22af2911 PLB |
621 | bool pkg_found = false; |
622 | ||
623 | state.length = sizeof(chan_package); | |
624 | state.pointer = &chan_package; | |
625 | ||
626 | pkg_ctx.name = "CHAN"; | |
627 | pkg_ctx.length = 2; | |
628 | pkg_ctx.format = &format; | |
629 | pkg_ctx.state = &state; | |
630 | pkg_ctx.data_valid = false; | |
631 | ||
7feb2f78 PLB |
632 | pkg_found = snd_soc_acpi_find_package_from_hid(mach->id, |
633 | &pkg_ctx); | |
22af2911 PLB |
634 | if (pkg_found) { |
635 | if (chan_package.aif_value == 1) { | |
636 | dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n"); | |
637 | cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF1; | |
638 | } else if (chan_package.aif_value == 2) { | |
639 | dev_info(&pdev->dev, "BIOS Routing: AIF2 connected\n"); | |
640 | cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF2; | |
641 | } else { | |
642 | dev_info(&pdev->dev, "BIOS Routing isn't valid, ignored\n"); | |
643 | pkg_found = false; | |
644 | } | |
645 | } | |
646 | ||
647 | if (!pkg_found) { | |
648 | /* no BIOS indications, assume SSP0-AIF2 connection */ | |
649 | cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF2; | |
650 | } | |
651 | } | |
652 | ||
653 | /* check quirks before creating card */ | |
654 | dmi_check_system(cht_rt5645_quirk_table); | |
655 | log_quirks(&pdev->dev); | |
656 | ||
657 | if ((cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) || | |
791a0059 DS |
658 | (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) |
659 | cht_dailink[dai_index].codecs->dai_name = "rt5645-aif2"; | |
22af2911 PLB |
660 | |
661 | if ((cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) || | |
791a0059 DS |
662 | (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) |
663 | cht_dailink[dai_index].cpus->dai_name = "ssp0-port"; | |
c8560b7c | 664 | |
f1eebb3b | 665 | /* override platform name, if required */ |
3a934e7c PLB |
666 | platform_name = mach->mach_params.platform; |
667 | ||
668 | ret_val = snd_soc_fixup_dai_links_platform_name(card, | |
669 | platform_name); | |
670 | if (ret_val) | |
671 | return ret_val; | |
672 | ||
adebb111 SM |
673 | if (cht_rt5645_quirk & CHT_RT5645_PMC_PLT_CLK_0) |
674 | mclk_name = "pmc_plt_clk_0"; | |
675 | else | |
676 | mclk_name = "pmc_plt_clk_3"; | |
677 | ||
678 | drv->mclk = devm_clk_get(&pdev->dev, mclk_name); | |
7735bce0 | 679 | if (IS_ERR(drv->mclk)) { |
adebb111 SM |
680 | dev_err(&pdev->dev, "Failed to get MCLK from %s: %ld\n", |
681 | mclk_name, PTR_ERR(drv->mclk)); | |
7735bce0 | 682 | return PTR_ERR(drv->mclk); |
a50477e5 PLB |
683 | } |
684 | ||
c4ba51ba | 685 | snd_soc_card_set_drvdata(card, drv); |
41656c3d PLB |
686 | |
687 | sof_parent = snd_soc_acpi_sof_parent(&pdev->dev); | |
688 | ||
689 | /* set card and driver name */ | |
690 | if (sof_parent) { | |
691 | snd_soc_card_chtrt5645.name = SOF_CARD_RT5645_NAME; | |
692 | snd_soc_card_chtrt5645.driver_name = SOF_DRIVER_NAME; | |
693 | snd_soc_card_chtrt5650.name = SOF_CARD_RT5650_NAME; | |
694 | snd_soc_card_chtrt5650.driver_name = SOF_DRIVER_NAME; | |
695 | } else { | |
696 | snd_soc_card_chtrt5645.name = CARD_RT5645_NAME; | |
697 | snd_soc_card_chtrt5645.driver_name = DRIVER_NAME; | |
698 | snd_soc_card_chtrt5650.name = CARD_RT5650_NAME; | |
699 | snd_soc_card_chtrt5650.driver_name = DRIVER_NAME; | |
700 | } | |
701 | ||
05ff312b PLB |
702 | /* set pm ops */ |
703 | if (sof_parent) | |
704 | pdev->dev.driver->pm = &snd_soc_pm_ops; | |
705 | ||
c4ba51ba | 706 | ret_val = devm_snd_soc_register_card(&pdev->dev, card); |
e18acdc0 FY |
707 | if (ret_val) { |
708 | dev_err(&pdev->dev, | |
709 | "snd_soc_register_card failed %d\n", ret_val); | |
710 | return ret_val; | |
711 | } | |
c4ba51ba | 712 | platform_set_drvdata(pdev, card); |
e18acdc0 FY |
713 | return ret_val; |
714 | } | |
715 | ||
716 | static struct platform_driver snd_cht_mc_driver = { | |
717 | .driver = { | |
e18acdc0 | 718 | .name = "cht-bsw-rt5645", |
e18acdc0 FY |
719 | }, |
720 | .probe = snd_cht_mc_probe, | |
721 | }; | |
722 | ||
723 | module_platform_driver(snd_cht_mc_driver) | |
724 | ||
725 | MODULE_DESCRIPTION("ASoC Intel(R) Braswell Machine driver"); | |
726 | MODULE_AUTHOR("Fang, Yang A,N,Harshapriya"); | |
727 | MODULE_LICENSE("GPL v2"); | |
728 | MODULE_ALIAS("platform:cht-bsw-rt5645"); |