Commit | Line | Data |
---|---|---|
8e8e69d6 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
2bd5bd15 PLB |
2 | /* |
3 | * bytcr_rt5651.c - ASoc Machine driver for Intel Byt CR platform | |
4 | * (derived from bytcr_rt5640.c) | |
5 | * | |
6 | * Copyright (C) 2015 Intel Corp | |
7 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
8 | * | |
2bd5bd15 PLB |
9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
10 | */ | |
11 | ||
12 | #include <linux/init.h> | |
46058aeb | 13 | #include <linux/i2c.h> |
2bd5bd15 PLB |
14 | #include <linux/module.h> |
15 | #include <linux/platform_device.h> | |
46058aeb | 16 | #include <linux/property.h> |
2bd5bd15 | 17 | #include <linux/acpi.h> |
02c0a3b3 | 18 | #include <linux/clk.h> |
2bd5bd15 PLB |
19 | #include <linux/device.h> |
20 | #include <linux/dmi.h> | |
caed9d63 | 21 | #include <linux/input.h> |
5f6fb23d HG |
22 | #include <linux/gpio/consumer.h> |
23 | #include <linux/gpio/machine.h> | |
2bd5bd15 PLB |
24 | #include <linux/slab.h> |
25 | #include <sound/pcm.h> | |
26 | #include <sound/pcm_params.h> | |
27 | #include <sound/soc.h> | |
28 | #include <sound/jack.h> | |
7feb2f78 | 29 | #include <sound/soc-acpi.h> |
2bd5bd15 PLB |
30 | #include "../../codecs/rt5651.h" |
31 | #include "../atom/sst-atom-controls.h" | |
536cfd2f | 32 | #include "../common/soc-intel-quirks.h" |
02c0a3b3 PLB |
33 | |
34 | enum { | |
35 | BYT_RT5651_DMIC_MAP, | |
36 | BYT_RT5651_IN1_MAP, | |
ac275ee5 | 37 | BYT_RT5651_IN2_MAP, |
ea261bd0 | 38 | BYT_RT5651_IN1_IN2_MAP, |
02c0a3b3 PLB |
39 | }; |
40 | ||
46058aeb HG |
41 | enum { |
42 | BYT_RT5651_JD_NULL = (RT5651_JD_NULL << 4), | |
43 | BYT_RT5651_JD1_1 = (RT5651_JD1_1 << 4), | |
44 | BYT_RT5651_JD1_2 = (RT5651_JD1_2 << 4), | |
45 | BYT_RT5651_JD2 = (RT5651_JD2 << 4), | |
46 | }; | |
47 | ||
8ffaa6a1 HG |
48 | enum { |
49 | BYT_RT5651_OVCD_TH_600UA = (6 << 8), | |
50 | BYT_RT5651_OVCD_TH_1500UA = (15 << 8), | |
51 | BYT_RT5651_OVCD_TH_2000UA = (20 << 8), | |
52 | }; | |
53 | ||
54 | enum { | |
55 | BYT_RT5651_OVCD_SF_0P5 = (RT5651_OVCD_SF_0P5 << 13), | |
56 | BYT_RT5651_OVCD_SF_0P75 = (RT5651_OVCD_SF_0P75 << 13), | |
57 | BYT_RT5651_OVCD_SF_1P0 = (RT5651_OVCD_SF_1P0 << 13), | |
58 | BYT_RT5651_OVCD_SF_1P5 = (RT5651_OVCD_SF_1P5 << 13), | |
59 | }; | |
60 | ||
46058aeb HG |
61 | #define BYT_RT5651_MAP(quirk) ((quirk) & GENMASK(3, 0)) |
62 | #define BYT_RT5651_JDSRC(quirk) (((quirk) & GENMASK(7, 4)) >> 4) | |
8ffaa6a1 HG |
63 | #define BYT_RT5651_OVCD_TH(quirk) (((quirk) & GENMASK(12, 8)) >> 8) |
64 | #define BYT_RT5651_OVCD_SF(quirk) (((quirk) & GENMASK(14, 13)) >> 13) | |
46058aeb HG |
65 | #define BYT_RT5651_DMIC_EN BIT(16) |
66 | #define BYT_RT5651_MCLK_EN BIT(17) | |
67 | #define BYT_RT5651_MCLK_25MHZ BIT(18) | |
8a880a20 HG |
68 | #define BYT_RT5651_SSP2_AIF2 BIT(19) /* default is using AIF1 */ |
69 | #define BYT_RT5651_SSP0_AIF1 BIT(20) | |
70 | #define BYT_RT5651_SSP0_AIF2 BIT(21) | |
8f250e70 | 71 | #define BYT_RT5651_HP_LR_SWAPPED BIT(22) |
a0d1d867 | 72 | #define BYT_RT5651_MONO_SPEAKER BIT(23) |
a0cb2d43 | 73 | #define BYT_RT5651_JD_NOT_INV BIT(24) |
46058aeb | 74 | |
fc7c460f HG |
75 | #define BYT_RT5651_DEFAULT_QUIRKS (BYT_RT5651_MCLK_EN | \ |
76 | BYT_RT5651_JD1_1 | \ | |
77 | BYT_RT5651_OVCD_TH_2000UA | \ | |
78 | BYT_RT5651_OVCD_SF_0P75) | |
79 | ||
a0cb2d43 HG |
80 | /* jack-detect-source + inv + dmic-en + ovcd-th + -sf + terminating entry */ |
81 | #define MAX_NO_PROPS 6 | |
02c0a3b3 PLB |
82 | |
83 | struct byt_rt5651_private { | |
84 | struct clk *mclk; | |
5f6fb23d | 85 | struct gpio_desc *ext_amp_gpio; |
90768eaf | 86 | struct gpio_desc *hp_detect; |
d9f8f9b2 | 87 | struct snd_soc_jack jack; |
c50f126b | 88 | struct device *codec_dev; |
02c0a3b3 PLB |
89 | }; |
90 | ||
fee3e1cb HG |
91 | static const struct acpi_gpio_mapping *byt_rt5651_gpios; |
92 | ||
ac275ee5 | 93 | /* Default: jack-detect on JD1_1, internal mic on in2, headsetmic on in3 */ |
fc7c460f | 94 | static unsigned long byt_rt5651_quirk = BYT_RT5651_DEFAULT_QUIRKS | |
ac275ee5 | 95 | BYT_RT5651_IN2_MAP; |
02c0a3b3 | 96 | |
fb45befa PLB |
97 | static int quirk_override = -1; |
98 | module_param_named(quirk, quirk_override, int, 0444); | |
7eb18731 HG |
99 | MODULE_PARM_DESC(quirk, "Board-specific quirk override"); |
100 | ||
02c0a3b3 PLB |
101 | static void log_quirks(struct device *dev) |
102 | { | |
103 | if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_DMIC_MAP) | |
104 | dev_info(dev, "quirk DMIC_MAP enabled"); | |
105 | if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN1_MAP) | |
106 | dev_info(dev, "quirk IN1_MAP enabled"); | |
ac275ee5 HG |
107 | if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN2_MAP) |
108 | dev_info(dev, "quirk IN2_MAP enabled"); | |
366780df HG |
109 | if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN1_IN2_MAP) |
110 | dev_info(dev, "quirk IN1_IN2_MAP enabled"); | |
8ffaa6a1 | 111 | if (BYT_RT5651_JDSRC(byt_rt5651_quirk)) { |
46058aeb HG |
112 | dev_info(dev, "quirk realtek,jack-detect-source %ld\n", |
113 | BYT_RT5651_JDSRC(byt_rt5651_quirk)); | |
8ffaa6a1 HG |
114 | dev_info(dev, "quirk realtek,over-current-threshold-microamp %ld\n", |
115 | BYT_RT5651_OVCD_TH(byt_rt5651_quirk) * 100); | |
116 | dev_info(dev, "quirk realtek,over-current-scale-factor %ld\n", | |
117 | BYT_RT5651_OVCD_SF(byt_rt5651_quirk)); | |
118 | } | |
02c0a3b3 PLB |
119 | if (byt_rt5651_quirk & BYT_RT5651_DMIC_EN) |
120 | dev_info(dev, "quirk DMIC enabled"); | |
121 | if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) | |
122 | dev_info(dev, "quirk MCLK_EN enabled"); | |
123 | if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ) | |
124 | dev_info(dev, "quirk MCLK_25MHZ enabled"); | |
8a880a20 HG |
125 | if (byt_rt5651_quirk & BYT_RT5651_SSP2_AIF2) |
126 | dev_info(dev, "quirk SSP2_AIF2 enabled\n"); | |
127 | if (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF1) | |
128 | dev_info(dev, "quirk SSP0_AIF1 enabled\n"); | |
129 | if (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2) | |
130 | dev_info(dev, "quirk SSP0_AIF2 enabled\n"); | |
a0d1d867 HG |
131 | if (byt_rt5651_quirk & BYT_RT5651_MONO_SPEAKER) |
132 | dev_info(dev, "quirk MONO_SPEAKER enabled\n"); | |
a0cb2d43 HG |
133 | if (byt_rt5651_quirk & BYT_RT5651_JD_NOT_INV) |
134 | dev_info(dev, "quirk JD_NOT_INV enabled\n"); | |
02c0a3b3 PLB |
135 | } |
136 | ||
137 | #define BYT_CODEC_DAI1 "rt5651-aif1" | |
8a880a20 | 138 | #define BYT_CODEC_DAI2 "rt5651-aif2" |
02c0a3b3 | 139 | |
aeec6cc0 HG |
140 | static int byt_rt5651_prepare_and_enable_pll1(struct snd_soc_dai *codec_dai, |
141 | int rate, int bclk_ratio) | |
142 | { | |
143 | int clk_id, clk_freq, ret; | |
144 | ||
145 | /* Configure the PLL before selecting it */ | |
146 | if (!(byt_rt5651_quirk & BYT_RT5651_MCLK_EN)) { | |
edc3f5b4 | 147 | clk_id = RT5651_PLL1_S_BCLK1; |
aeec6cc0 HG |
148 | clk_freq = rate * bclk_ratio; |
149 | } else { | |
150 | clk_id = RT5651_PLL1_S_MCLK; | |
151 | if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ) | |
152 | clk_freq = 25000000; | |
153 | else | |
154 | clk_freq = 19200000; | |
155 | } | |
156 | ret = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, rate * 512); | |
157 | if (ret < 0) { | |
2759ba9b | 158 | dev_err(codec_dai->component->dev, "can't set pll: %d\n", ret); |
aeec6cc0 HG |
159 | return ret; |
160 | } | |
161 | ||
162 | ret = snd_soc_dai_set_sysclk(codec_dai, RT5651_SCLK_S_PLL1, | |
163 | rate * 512, SND_SOC_CLOCK_IN); | |
164 | if (ret < 0) { | |
2759ba9b | 165 | dev_err(codec_dai->component->dev, "can't set clock %d\n", ret); |
aeec6cc0 HG |
166 | return ret; |
167 | } | |
168 | ||
169 | return 0; | |
170 | } | |
171 | ||
02c0a3b3 PLB |
172 | static int platform_clock_control(struct snd_soc_dapm_widget *w, |
173 | struct snd_kcontrol *k, int event) | |
174 | { | |
175 | struct snd_soc_dapm_context *dapm = w->dapm; | |
176 | struct snd_soc_card *card = dapm->card; | |
177 | struct snd_soc_dai *codec_dai; | |
178 | struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card); | |
179 | int ret; | |
180 | ||
dfb6ec7a | 181 | codec_dai = snd_soc_card_get_codec_dai(card, BYT_CODEC_DAI1); |
8a880a20 HG |
182 | if (!codec_dai) |
183 | codec_dai = snd_soc_card_get_codec_dai(card, BYT_CODEC_DAI2); | |
02c0a3b3 PLB |
184 | if (!codec_dai) { |
185 | dev_err(card->dev, | |
186 | "Codec dai not found; Unable to set platform clock\n"); | |
187 | return -EIO; | |
188 | } | |
189 | ||
190 | if (SND_SOC_DAPM_EVENT_ON(event)) { | |
a8627df5 AS |
191 | ret = clk_prepare_enable(priv->mclk); |
192 | if (ret < 0) { | |
193 | dev_err(card->dev, "could not configure MCLK state"); | |
194 | return ret; | |
02c0a3b3 | 195 | } |
aeec6cc0 | 196 | ret = byt_rt5651_prepare_and_enable_pll1(codec_dai, 48000, 50); |
02c0a3b3 PLB |
197 | } else { |
198 | /* | |
199 | * Set codec clock source to internal clock before | |
200 | * turning off the platform clock. Codec needs clock | |
201 | * for Jack detection and button press | |
202 | */ | |
203 | ret = snd_soc_dai_set_sysclk(codec_dai, RT5651_SCLK_S_RCCLK, | |
204 | 48000 * 512, | |
205 | SND_SOC_CLOCK_IN); | |
206 | if (!ret) | |
a8627df5 | 207 | clk_disable_unprepare(priv->mclk); |
02c0a3b3 PLB |
208 | } |
209 | ||
210 | if (ret < 0) { | |
211 | dev_err(card->dev, "can't set codec sysclk: %d\n", ret); | |
212 | return ret; | |
213 | } | |
214 | ||
215 | return 0; | |
216 | } | |
2bd5bd15 | 217 | |
5f6fb23d HG |
218 | static int rt5651_ext_amp_power_event(struct snd_soc_dapm_widget *w, |
219 | struct snd_kcontrol *kcontrol, int event) | |
220 | { | |
221 | struct snd_soc_card *card = w->dapm->card; | |
222 | struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card); | |
223 | ||
224 | if (SND_SOC_DAPM_EVENT_ON(event)) | |
225 | gpiod_set_value_cansleep(priv->ext_amp_gpio, 1); | |
226 | else | |
227 | gpiod_set_value_cansleep(priv->ext_amp_gpio, 0); | |
228 | ||
229 | return 0; | |
230 | } | |
231 | ||
2bd5bd15 PLB |
232 | static const struct snd_soc_dapm_widget byt_rt5651_widgets[] = { |
233 | SND_SOC_DAPM_HP("Headphone", NULL), | |
234 | SND_SOC_DAPM_MIC("Headset Mic", NULL), | |
235 | SND_SOC_DAPM_MIC("Internal Mic", NULL), | |
236 | SND_SOC_DAPM_SPK("Speaker", NULL), | |
ea39bdcf | 237 | SND_SOC_DAPM_LINE("Line In", NULL), |
02c0a3b3 PLB |
238 | SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, |
239 | platform_clock_control, SND_SOC_DAPM_PRE_PMU | | |
240 | SND_SOC_DAPM_POST_PMD), | |
5f6fb23d HG |
241 | SND_SOC_DAPM_SUPPLY("Ext Amp Power", SND_SOC_NOPM, 0, 0, |
242 | rt5651_ext_amp_power_event, | |
243 | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), | |
2bd5bd15 PLB |
244 | }; |
245 | ||
246 | static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = { | |
02c0a3b3 PLB |
247 | {"Headphone", NULL, "Platform Clock"}, |
248 | {"Headset Mic", NULL, "Platform Clock"}, | |
249 | {"Internal Mic", NULL, "Platform Clock"}, | |
250 | {"Speaker", NULL, "Platform Clock"}, | |
5f6fb23d | 251 | {"Speaker", NULL, "Ext Amp Power"}, |
ea39bdcf | 252 | {"Line In", NULL, "Platform Clock"}, |
02c0a3b3 | 253 | |
2bd5bd15 | 254 | {"Headset Mic", NULL, "micbias1"}, /* lowercase for rt5651 */ |
2bd5bd15 PLB |
255 | {"Headphone", NULL, "HPOL"}, |
256 | {"Headphone", NULL, "HPOR"}, | |
257 | {"Speaker", NULL, "LOUTL"}, | |
258 | {"Speaker", NULL, "LOUTR"}, | |
ea39bdcf PLB |
259 | {"IN2P", NULL, "Line In"}, |
260 | {"IN2N", NULL, "Line In"}, | |
261 | ||
2bd5bd15 PLB |
262 | }; |
263 | ||
6356c78c PLB |
264 | static const struct snd_soc_dapm_route byt_rt5651_intmic_dmic_map[] = { |
265 | {"DMIC L1", NULL, "Internal Mic"}, | |
266 | {"DMIC R1", NULL, "Internal Mic"}, | |
aee48a9f | 267 | {"IN2P", NULL, "Headset Mic"}, |
2bd5bd15 PLB |
268 | }; |
269 | ||
270 | static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_map[] = { | |
271 | {"Internal Mic", NULL, "micbias1"}, | |
272 | {"IN1P", NULL, "Internal Mic"}, | |
de231479 | 273 | {"IN3P", NULL, "Headset Mic"}, |
2bd5bd15 PLB |
274 | }; |
275 | ||
ac275ee5 HG |
276 | static const struct snd_soc_dapm_route byt_rt5651_intmic_in2_map[] = { |
277 | {"Internal Mic", NULL, "micbias1"}, | |
278 | {"IN2P", NULL, "Internal Mic"}, | |
279 | {"IN3P", NULL, "Headset Mic"}, | |
280 | }; | |
281 | ||
ea261bd0 CC |
282 | static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_in2_map[] = { |
283 | {"Internal Mic", NULL, "micbias1"}, | |
284 | {"IN1P", NULL, "Internal Mic"}, | |
285 | {"IN2P", NULL, "Internal Mic"}, | |
286 | {"IN3P", NULL, "Headset Mic"}, | |
287 | }; | |
288 | ||
8a880a20 HG |
289 | static const struct snd_soc_dapm_route byt_rt5651_ssp0_aif1_map[] = { |
290 | {"ssp0 Tx", NULL, "modem_out"}, | |
291 | {"modem_in", NULL, "ssp0 Rx"}, | |
292 | ||
293 | {"AIF1 Playback", NULL, "ssp0 Tx"}, | |
294 | {"ssp0 Rx", NULL, "AIF1 Capture"}, | |
295 | }; | |
296 | ||
297 | static const struct snd_soc_dapm_route byt_rt5651_ssp0_aif2_map[] = { | |
298 | {"ssp0 Tx", NULL, "modem_out"}, | |
299 | {"modem_in", NULL, "ssp0 Rx"}, | |
300 | ||
301 | {"AIF2 Playback", NULL, "ssp0 Tx"}, | |
302 | {"ssp0 Rx", NULL, "AIF2 Capture"}, | |
303 | }; | |
304 | ||
305 | static const struct snd_soc_dapm_route byt_rt5651_ssp2_aif1_map[] = { | |
306 | {"ssp2 Tx", NULL, "codec_out0"}, | |
307 | {"ssp2 Tx", NULL, "codec_out1"}, | |
308 | {"codec_in0", NULL, "ssp2 Rx"}, | |
309 | {"codec_in1", NULL, "ssp2 Rx"}, | |
310 | ||
311 | {"AIF1 Playback", NULL, "ssp2 Tx"}, | |
312 | {"ssp2 Rx", NULL, "AIF1 Capture"}, | |
313 | }; | |
314 | ||
315 | static const struct snd_soc_dapm_route byt_rt5651_ssp2_aif2_map[] = { | |
316 | {"ssp2 Tx", NULL, "codec_out0"}, | |
317 | {"ssp2 Tx", NULL, "codec_out1"}, | |
318 | {"codec_in0", NULL, "ssp2 Rx"}, | |
319 | {"codec_in1", NULL, "ssp2 Rx"}, | |
320 | ||
321 | {"AIF2 Playback", NULL, "ssp2 Tx"}, | |
322 | {"ssp2 Rx", NULL, "AIF2 Capture"}, | |
323 | }; | |
324 | ||
2bd5bd15 PLB |
325 | static const struct snd_kcontrol_new byt_rt5651_controls[] = { |
326 | SOC_DAPM_PIN_SWITCH("Headphone"), | |
327 | SOC_DAPM_PIN_SWITCH("Headset Mic"), | |
328 | SOC_DAPM_PIN_SWITCH("Internal Mic"), | |
329 | SOC_DAPM_PIN_SWITCH("Speaker"), | |
ea39bdcf | 330 | SOC_DAPM_PIN_SWITCH("Line In"), |
2bd5bd15 PLB |
331 | }; |
332 | ||
d9f8f9b2 CC |
333 | static struct snd_soc_jack_pin bytcr_jack_pins[] = { |
334 | { | |
335 | .pin = "Headphone", | |
336 | .mask = SND_JACK_HEADPHONE, | |
337 | }, | |
338 | { | |
339 | .pin = "Headset Mic", | |
340 | .mask = SND_JACK_MICROPHONE, | |
341 | }, | |
342 | }; | |
343 | ||
2bd5bd15 PLB |
344 | static int byt_rt5651_aif1_hw_params(struct snd_pcm_substream *substream, |
345 | struct snd_pcm_hw_params *params) | |
346 | { | |
a2c1125e KM |
347 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
348 | struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); | |
8a880a20 | 349 | snd_pcm_format_t format = params_format(params); |
aeec6cc0 | 350 | int rate = params_rate(params); |
8a880a20 | 351 | int bclk_ratio; |
2bd5bd15 | 352 | |
8a880a20 HG |
353 | if (format == SNDRV_PCM_FORMAT_S16_LE) |
354 | bclk_ratio = 32; | |
355 | else | |
356 | bclk_ratio = 50; | |
357 | ||
358 | return byt_rt5651_prepare_and_enable_pll1(codec_dai, rate, bclk_ratio); | |
2bd5bd15 PLB |
359 | } |
360 | ||
fee3e1cb HG |
361 | static const struct acpi_gpio_params pov_p1006w_hp_detect = { 1, 0, false }; |
362 | static const struct acpi_gpio_params pov_p1006w_ext_amp_en = { 2, 0, true }; | |
363 | ||
364 | static const struct acpi_gpio_mapping byt_rt5651_pov_p1006w_gpios[] = { | |
365 | { "hp-detect-gpios", &pov_p1006w_hp_detect, 1, }, | |
366 | { "ext-amp-enable-gpios", &pov_p1006w_ext_amp_en, 1, }, | |
367 | { }, | |
368 | }; | |
369 | ||
370 | static int byt_rt5651_pov_p1006w_quirk_cb(const struct dmi_system_id *id) | |
371 | { | |
372 | byt_rt5651_quirk = (unsigned long)id->driver_data; | |
373 | byt_rt5651_gpios = byt_rt5651_pov_p1006w_gpios; | |
374 | return 1; | |
375 | } | |
376 | ||
02c0a3b3 PLB |
377 | static int byt_rt5651_quirk_cb(const struct dmi_system_id *id) |
378 | { | |
379 | byt_rt5651_quirk = (unsigned long)id->driver_data; | |
380 | return 1; | |
381 | } | |
382 | ||
2bd5bd15 | 383 | static const struct dmi_system_id byt_rt5651_quirk_table[] = { |
02c0a3b3 | 384 | { |
55d69c03 | 385 | /* Chuwi Hi8 Pro (CWI513) */ |
02c0a3b3 PLB |
386 | .callback = byt_rt5651_quirk_cb, |
387 | .matches = { | |
55d69c03 HG |
388 | DMI_MATCH(DMI_SYS_VENDOR, "Hampoo"), |
389 | DMI_MATCH(DMI_PRODUCT_NAME, "X1D3_C806N"), | |
02c0a3b3 | 390 | }, |
55d69c03 | 391 | .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS | |
ac275ee5 | 392 | BYT_RT5651_IN2_MAP | |
a0d1d867 HG |
393 | BYT_RT5651_HP_LR_SWAPPED | |
394 | BYT_RT5651_MONO_SPEAKER), | |
02c0a3b3 | 395 | }, |
416f2b51 | 396 | { |
55d69c03 | 397 | /* Chuwi Vi8 Plus (CWI519) */ |
416f2b51 PLB |
398 | .callback = byt_rt5651_quirk_cb, |
399 | .matches = { | |
55d69c03 HG |
400 | DMI_MATCH(DMI_SYS_VENDOR, "Hampoo"), |
401 | DMI_MATCH(DMI_PRODUCT_NAME, "D2D3_Vi8A1"), | |
416f2b51 | 402 | }, |
55d69c03 | 403 | .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS | |
ac275ee5 | 404 | BYT_RT5651_IN2_MAP | |
a0d1d867 HG |
405 | BYT_RT5651_HP_LR_SWAPPED | |
406 | BYT_RT5651_MONO_SPEAKER), | |
416f2b51 | 407 | }, |
a0cb2d43 HG |
408 | { |
409 | /* Complet Electro Serv MY8307 */ | |
410 | .callback = byt_rt5651_quirk_cb, | |
411 | .matches = { | |
412 | DMI_MATCH(DMI_SYS_VENDOR, "Complet Electro Serv"), | |
413 | DMI_MATCH(DMI_PRODUCT_NAME, "MY8307"), | |
414 | }, | |
415 | .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS | | |
416 | BYT_RT5651_IN2_MAP | | |
417 | BYT_RT5651_MONO_SPEAKER | | |
418 | BYT_RT5651_JD_NOT_INV), | |
419 | }, | |
06aa6e51 HG |
420 | { |
421 | /* I.T.Works TW701, Ployer Momo7w and Trekstor ST70416-6 | |
422 | * (these all use the same mainboard) */ | |
423 | .callback = byt_rt5651_quirk_cb, | |
424 | .matches = { | |
425 | DMI_MATCH(DMI_BIOS_VENDOR, "INSYDE Corp."), | |
426 | /* Partial match for all of itWORKS.G.WI71C.JGBMRBA, | |
427 | * TREK.G.WI71C.JGBMRBA0x and MOMO.G.WI71C.MABMRBA02 */ | |
428 | DMI_MATCH(DMI_BIOS_VERSION, ".G.WI71C."), | |
429 | }, | |
430 | .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS | | |
431 | BYT_RT5651_IN2_MAP | | |
432 | BYT_RT5651_SSP0_AIF1 | | |
433 | BYT_RT5651_MONO_SPEAKER), | |
434 | }, | |
df8359c5 HG |
435 | { |
436 | /* Jumper EZpad 7 */ | |
437 | .callback = byt_rt5651_quirk_cb, | |
438 | .matches = { | |
439 | DMI_MATCH(DMI_SYS_VENDOR, "Jumper"), | |
440 | DMI_MATCH(DMI_PRODUCT_NAME, "EZpad"), | |
441 | /* Jumper12x.WJ2012.bsBKRCP05 with the version dropped */ | |
442 | DMI_MATCH(DMI_BIOS_VERSION, "Jumper12x.WJ2012.bsBKRCP"), | |
443 | }, | |
444 | .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS | | |
445 | BYT_RT5651_IN2_MAP | | |
446 | BYT_RT5651_JD_NOT_INV), | |
447 | }, | |
2fe30129 | 448 | { |
55d69c03 | 449 | /* KIANO SlimNote 14.2 */ |
2fe30129 CC |
450 | .callback = byt_rt5651_quirk_cb, |
451 | .matches = { | |
452 | DMI_MATCH(DMI_SYS_VENDOR, "KIANO"), | |
453 | DMI_MATCH(DMI_PRODUCT_NAME, "KIANO SlimNote 14.2"), | |
454 | }, | |
fc7c460f | 455 | .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS | |
56e49aa4 | 456 | BYT_RT5651_IN1_IN2_MAP), |
2fe30129 | 457 | }, |
8f250e70 | 458 | { |
55d69c03 | 459 | /* Minnowboard Max B3 */ |
8f250e70 HG |
460 | .callback = byt_rt5651_quirk_cb, |
461 | .matches = { | |
55d69c03 HG |
462 | DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), |
463 | DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"), | |
8f250e70 | 464 | }, |
55d69c03 | 465 | .driver_data = (void *)(BYT_RT5651_IN1_MAP), |
8f250e70 | 466 | }, |
f026e063 | 467 | { |
55d69c03 | 468 | /* Minnowboard Turbot */ |
f026e063 HG |
469 | .callback = byt_rt5651_quirk_cb, |
470 | .matches = { | |
55d69c03 HG |
471 | DMI_MATCH(DMI_SYS_VENDOR, "ADI"), |
472 | DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Turbot"), | |
f026e063 | 473 | }, |
55d69c03 HG |
474 | .driver_data = (void *)(BYT_RT5651_MCLK_EN | |
475 | BYT_RT5651_IN1_MAP), | |
f026e063 | 476 | }, |
fee3e1cb HG |
477 | { |
478 | /* Point of View mobii wintab p1006w (v1.0) */ | |
479 | .callback = byt_rt5651_pov_p1006w_quirk_cb, | |
480 | .matches = { | |
481 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Insyde"), | |
482 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BayTrail"), | |
483 | /* Note 105b is Foxcon's USB/PCI vendor id */ | |
484 | DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "105B"), | |
485 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "0E57"), | |
486 | }, | |
487 | .driver_data = (void *)(BYT_RT5651_DMIC_MAP | | |
488 | BYT_RT5651_OVCD_TH_2000UA | | |
489 | BYT_RT5651_OVCD_SF_0P75 | | |
490 | BYT_RT5651_DMIC_EN | | |
491 | BYT_RT5651_MCLK_EN | | |
492 | BYT_RT5651_SSP0_AIF1), | |
493 | }, | |
f9877eb5 HG |
494 | { |
495 | /* VIOS LTH17 */ | |
496 | .callback = byt_rt5651_quirk_cb, | |
497 | .matches = { | |
498 | DMI_MATCH(DMI_SYS_VENDOR, "VIOS"), | |
499 | DMI_MATCH(DMI_PRODUCT_NAME, "LTH17"), | |
500 | }, | |
8627fb25 HG |
501 | .driver_data = (void *)(BYT_RT5651_IN1_IN2_MAP | |
502 | BYT_RT5651_JD1_1 | | |
503 | BYT_RT5651_OVCD_TH_2000UA | | |
504 | BYT_RT5651_OVCD_SF_1P0 | | |
505 | BYT_RT5651_MCLK_EN), | |
f9877eb5 | 506 | }, |
06aa6e51 HG |
507 | { |
508 | /* Yours Y8W81 (and others using the same mainboard) */ | |
509 | .callback = byt_rt5651_quirk_cb, | |
510 | .matches = { | |
511 | DMI_MATCH(DMI_BIOS_VENDOR, "INSYDE Corp."), | |
512 | /* Partial match for all devs with a W86C mainboard */ | |
513 | DMI_MATCH(DMI_BIOS_VERSION, ".F.W86C."), | |
514 | }, | |
515 | .driver_data = (void *)(BYT_RT5651_DEFAULT_QUIRKS | | |
516 | BYT_RT5651_IN2_MAP | | |
517 | BYT_RT5651_SSP0_AIF1 | | |
518 | BYT_RT5651_MONO_SPEAKER), | |
519 | }, | |
2bd5bd15 PLB |
520 | {} |
521 | }; | |
522 | ||
46058aeb HG |
523 | /* |
524 | * Note this MUST be called before snd_soc_register_card(), so that the props | |
525 | * are in place before the codec component driver's probe function parses them. | |
526 | */ | |
0bd3c071 HK |
527 | static int byt_rt5651_add_codec_device_props(struct device *i2c_dev, |
528 | struct byt_rt5651_private *priv) | |
46058aeb HG |
529 | { |
530 | struct property_entry props[MAX_NO_PROPS] = {}; | |
0bd3c071 | 531 | struct fwnode_handle *fwnode; |
2c375204 | 532 | int cnt = 0; |
0bd3c071 | 533 | int ret; |
46058aeb HG |
534 | |
535 | props[cnt++] = PROPERTY_ENTRY_U32("realtek,jack-detect-source", | |
536 | BYT_RT5651_JDSRC(byt_rt5651_quirk)); | |
537 | ||
8ffaa6a1 HG |
538 | props[cnt++] = PROPERTY_ENTRY_U32("realtek,over-current-threshold-microamp", |
539 | BYT_RT5651_OVCD_TH(byt_rt5651_quirk) * 100); | |
540 | ||
541 | props[cnt++] = PROPERTY_ENTRY_U32("realtek,over-current-scale-factor", | |
542 | BYT_RT5651_OVCD_SF(byt_rt5651_quirk)); | |
543 | ||
c2f26938 HG |
544 | if (byt_rt5651_quirk & BYT_RT5651_DMIC_EN) |
545 | props[cnt++] = PROPERTY_ENTRY_BOOL("realtek,dmic-en"); | |
546 | ||
a0cb2d43 HG |
547 | if (byt_rt5651_quirk & BYT_RT5651_JD_NOT_INV) |
548 | props[cnt++] = PROPERTY_ENTRY_BOOL("realtek,jack-detect-not-inverted"); | |
549 | ||
0bd3c071 HK |
550 | fwnode = fwnode_create_software_node(props, NULL); |
551 | if (IS_ERR(fwnode)) { | |
552 | /* put_device(i2c_dev) is handled in caller */ | |
553 | return PTR_ERR(fwnode); | |
554 | } | |
555 | ||
556 | ret = device_add_software_node(i2c_dev, to_software_node(fwnode)); | |
557 | ||
558 | fwnode_handle_put(fwnode); | |
559 | ||
560 | return ret; | |
46058aeb HG |
561 | } |
562 | ||
2bd5bd15 PLB |
563 | static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) |
564 | { | |
2bd5bd15 | 565 | struct snd_soc_card *card = runtime->card; |
a2c1125e | 566 | struct snd_soc_component *codec = snd_soc_rtd_to_codec(runtime, 0)->component; |
02c0a3b3 | 567 | struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card); |
2bd5bd15 PLB |
568 | const struct snd_soc_dapm_route *custom_map; |
569 | int num_routes; | |
90768eaf | 570 | int report; |
02c0a3b3 | 571 | int ret; |
2bd5bd15 PLB |
572 | |
573 | card->dapm.idle_bias_off = true; | |
574 | ||
c22969d7 HG |
575 | /* Start with RC clk for jack-detect (we disable MCLK below) */ |
576 | if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) | |
577 | snd_soc_component_update_bits(codec, RT5651_GLB_CLK, | |
578 | RT5651_SCLK_SRC_MASK, RT5651_SCLK_SRC_RCCLK); | |
579 | ||
2bd5bd15 PLB |
580 | switch (BYT_RT5651_MAP(byt_rt5651_quirk)) { |
581 | case BYT_RT5651_IN1_MAP: | |
582 | custom_map = byt_rt5651_intmic_in1_map; | |
583 | num_routes = ARRAY_SIZE(byt_rt5651_intmic_in1_map); | |
584 | break; | |
ac275ee5 HG |
585 | case BYT_RT5651_IN2_MAP: |
586 | custom_map = byt_rt5651_intmic_in2_map; | |
587 | num_routes = ARRAY_SIZE(byt_rt5651_intmic_in2_map); | |
588 | break; | |
ea261bd0 CC |
589 | case BYT_RT5651_IN1_IN2_MAP: |
590 | custom_map = byt_rt5651_intmic_in1_in2_map; | |
591 | num_routes = ARRAY_SIZE(byt_rt5651_intmic_in1_in2_map); | |
592 | break; | |
2bd5bd15 | 593 | default: |
6356c78c PLB |
594 | custom_map = byt_rt5651_intmic_dmic_map; |
595 | num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic_map); | |
2bd5bd15 | 596 | } |
6356c78c PLB |
597 | ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes); |
598 | if (ret) | |
599 | return ret; | |
2bd5bd15 | 600 | |
8a880a20 HG |
601 | if (byt_rt5651_quirk & BYT_RT5651_SSP2_AIF2) { |
602 | ret = snd_soc_dapm_add_routes(&card->dapm, | |
603 | byt_rt5651_ssp2_aif2_map, | |
604 | ARRAY_SIZE(byt_rt5651_ssp2_aif2_map)); | |
605 | } else if (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF1) { | |
606 | ret = snd_soc_dapm_add_routes(&card->dapm, | |
607 | byt_rt5651_ssp0_aif1_map, | |
608 | ARRAY_SIZE(byt_rt5651_ssp0_aif1_map)); | |
609 | } else if (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2) { | |
610 | ret = snd_soc_dapm_add_routes(&card->dapm, | |
611 | byt_rt5651_ssp0_aif2_map, | |
612 | ARRAY_SIZE(byt_rt5651_ssp0_aif2_map)); | |
613 | } else { | |
614 | ret = snd_soc_dapm_add_routes(&card->dapm, | |
615 | byt_rt5651_ssp2_aif1_map, | |
616 | ARRAY_SIZE(byt_rt5651_ssp2_aif1_map)); | |
617 | } | |
618 | if (ret) | |
619 | return ret; | |
620 | ||
2bd5bd15 PLB |
621 | ret = snd_soc_add_card_controls(card, byt_rt5651_controls, |
622 | ARRAY_SIZE(byt_rt5651_controls)); | |
623 | if (ret) { | |
624 | dev_err(card->dev, "unable to add card controls\n"); | |
625 | return ret; | |
626 | } | |
2bd5bd15 | 627 | |
a8627df5 AS |
628 | /* |
629 | * The firmware might enable the clock at boot (this information | |
630 | * may or may not be reflected in the enable clock register). | |
631 | * To change the rate we must disable the clock first to cover | |
632 | * these cases. Due to common clock framework restrictions that | |
633 | * do not allow to disable a clock that has not been enabled, | |
634 | * we need to enable the clock first. | |
635 | */ | |
636 | ret = clk_prepare_enable(priv->mclk); | |
637 | if (!ret) | |
638 | clk_disable_unprepare(priv->mclk); | |
02c0a3b3 | 639 | |
a8627df5 AS |
640 | if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ) |
641 | ret = clk_set_rate(priv->mclk, 25000000); | |
642 | else | |
643 | ret = clk_set_rate(priv->mclk, 19200000); | |
02c0a3b3 | 644 | |
a8627df5 AS |
645 | if (ret) |
646 | dev_err(card->dev, "unable to set MCLK rate\n"); | |
02c0a3b3 | 647 | |
90768eaf HG |
648 | report = 0; |
649 | if (BYT_RT5651_JDSRC(byt_rt5651_quirk)) | |
650 | report = SND_JACK_HEADSET | SND_JACK_BTN_0; | |
651 | else if (priv->hp_detect) | |
652 | report = SND_JACK_HEADSET; | |
653 | ||
654 | if (report) { | |
19aed2d6 AO |
655 | ret = snd_soc_card_jack_new_pins(runtime->card, "Headset", |
656 | report, &priv->jack, | |
657 | bytcr_jack_pins, | |
658 | ARRAY_SIZE(bytcr_jack_pins)); | |
aed859a2 HG |
659 | if (ret) { |
660 | dev_err(runtime->dev, "jack creation failed %d\n", ret); | |
661 | return ret; | |
662 | } | |
d9f8f9b2 | 663 | |
90768eaf HG |
664 | if (report & SND_JACK_BTN_0) |
665 | snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, | |
666 | KEY_PLAYPAUSE); | |
caed9d63 | 667 | |
90768eaf HG |
668 | ret = snd_soc_component_set_jack(codec, &priv->jack, |
669 | priv->hp_detect); | |
aed859a2 HG |
670 | if (ret) |
671 | return ret; | |
672 | } | |
d9f8f9b2 | 673 | |
aed859a2 | 674 | return 0; |
2bd5bd15 PLB |
675 | } |
676 | ||
2bd5bd15 PLB |
677 | static int byt_rt5651_codec_fixup(struct snd_soc_pcm_runtime *rtd, |
678 | struct snd_pcm_hw_params *params) | |
679 | { | |
680 | struct snd_interval *rate = hw_param_interval(params, | |
681 | SNDRV_PCM_HW_PARAM_RATE); | |
682 | struct snd_interval *channels = hw_param_interval(params, | |
683 | SNDRV_PCM_HW_PARAM_CHANNELS); | |
8a880a20 | 684 | int ret, bits; |
2bd5bd15 | 685 | |
4088355a | 686 | /* The DSP will convert the FE rate to 48k, stereo */ |
2bd5bd15 PLB |
687 | rate->min = rate->max = 48000; |
688 | channels->min = channels->max = 2; | |
689 | ||
8a880a20 HG |
690 | if ((byt_rt5651_quirk & BYT_RT5651_SSP0_AIF1) || |
691 | (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2)) { | |
692 | /* set SSP0 to 16-bit */ | |
693 | params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); | |
694 | bits = 16; | |
695 | } else { | |
696 | /* set SSP2 to 24-bit */ | |
697 | params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); | |
698 | bits = 24; | |
699 | } | |
2bd5bd15 PLB |
700 | |
701 | /* | |
702 | * Default mode for SSP configuration is TDM 4 slot, override config | |
8a880a20 | 703 | * with explicit setting to I2S 2ch. The word length is set with |
2bd5bd15 PLB |
704 | * dai_set_tdm_slot() since there is no other API exposed |
705 | */ | |
a2c1125e | 706 | ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0), |
2bd5bd15 | 707 | SND_SOC_DAIFMT_I2S | |
f12f5c84 | 708 | SND_SOC_DAIFMT_NB_NF | |
add9ee8c | 709 | SND_SOC_DAIFMT_BP_FP |
2bd5bd15 PLB |
710 | ); |
711 | ||
712 | if (ret < 0) { | |
713 | dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); | |
714 | return ret; | |
715 | } | |
716 | ||
a2c1125e | 717 | ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, bits); |
2bd5bd15 PLB |
718 | if (ret < 0) { |
719 | dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); | |
720 | return ret; | |
721 | } | |
722 | ||
723 | return 0; | |
724 | } | |
725 | ||
1ebb4d9d | 726 | static const unsigned int rates_48000[] = { |
2bd5bd15 PLB |
727 | 48000, |
728 | }; | |
729 | ||
1ebb4d9d | 730 | static const struct snd_pcm_hw_constraint_list constraints_48000 = { |
2bd5bd15 PLB |
731 | .count = ARRAY_SIZE(rates_48000), |
732 | .list = rates_48000, | |
733 | }; | |
734 | ||
735 | static int byt_rt5651_aif1_startup(struct snd_pcm_substream *substream) | |
736 | { | |
737 | return snd_pcm_hw_constraint_list(substream->runtime, 0, | |
738 | SNDRV_PCM_HW_PARAM_RATE, | |
739 | &constraints_48000); | |
740 | } | |
741 | ||
9b6fdef6 | 742 | static const struct snd_soc_ops byt_rt5651_aif1_ops = { |
2bd5bd15 PLB |
743 | .startup = byt_rt5651_aif1_startup, |
744 | }; | |
745 | ||
9b6fdef6 | 746 | static const struct snd_soc_ops byt_rt5651_be_ssp2_ops = { |
2bd5bd15 PLB |
747 | .hw_params = byt_rt5651_aif1_hw_params, |
748 | }; | |
749 | ||
01fee62a KM |
750 | SND_SOC_DAILINK_DEF(dummy, |
751 | DAILINK_COMP_ARRAY(COMP_DUMMY())); | |
752 | ||
753 | SND_SOC_DAILINK_DEF(media, | |
754 | DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai"))); | |
755 | ||
756 | SND_SOC_DAILINK_DEF(deepbuffer, | |
757 | DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai"))); | |
758 | ||
759 | SND_SOC_DAILINK_DEF(ssp2_port, | |
760 | DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port"))); | |
761 | SND_SOC_DAILINK_DEF(ssp2_codec, | |
762 | DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5651:00", "rt5651-aif1"))); | |
763 | ||
764 | SND_SOC_DAILINK_DEF(platform, | |
765 | DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform"))); | |
766 | ||
2bd5bd15 PLB |
767 | static struct snd_soc_dai_link byt_rt5651_dais[] = { |
768 | [MERR_DPCM_AUDIO] = { | |
769 | .name = "Audio Port", | |
770 | .stream_name = "Audio", | |
2bd5bd15 PLB |
771 | .nonatomic = true, |
772 | .dynamic = 1, | |
773 | .dpcm_playback = 1, | |
774 | .dpcm_capture = 1, | |
775 | .ops = &byt_rt5651_aif1_ops, | |
01fee62a | 776 | SND_SOC_DAILINK_REG(media, dummy, platform), |
2bd5bd15 PLB |
777 | }, |
778 | [MERR_DPCM_DEEP_BUFFER] = { | |
779 | .name = "Deep-Buffer Audio Port", | |
780 | .stream_name = "Deep-Buffer Audio", | |
2bd5bd15 PLB |
781 | .nonatomic = true, |
782 | .dynamic = 1, | |
783 | .dpcm_playback = 1, | |
784 | .ops = &byt_rt5651_aif1_ops, | |
01fee62a | 785 | SND_SOC_DAILINK_REG(deepbuffer, dummy, platform), |
2bd5bd15 | 786 | }, |
2bd5bd15 PLB |
787 | /* CODEC<->CODEC link */ |
788 | /* back ends */ | |
789 | { | |
790 | .name = "SSP2-Codec", | |
149f7757 | 791 | .id = 0, |
2bd5bd15 | 792 | .no_pcm = 1, |
2bd5bd15 | 793 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
5374b921 | 794 | | SND_SOC_DAIFMT_CBC_CFC, |
2bd5bd15 | 795 | .be_hw_params_fixup = byt_rt5651_codec_fixup, |
2bd5bd15 PLB |
796 | .dpcm_playback = 1, |
797 | .dpcm_capture = 1, | |
798 | .init = byt_rt5651_init, | |
799 | .ops = &byt_rt5651_be_ssp2_ops, | |
01fee62a | 800 | SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform), |
2bd5bd15 PLB |
801 | }, |
802 | }; | |
803 | ||
804 | /* SoC card */ | |
b91f432c | 805 | static char byt_rt5651_codec_name[SND_ACPI_I2C_ID_LEN]; |
b5706f8e | 806 | #if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES) |
a0d1d867 | 807 | static char byt_rt5651_long_name[50]; /* = "bytcr-rt5651-*-spk-*-mic[-swapped-hp]" */ |
b5706f8e | 808 | #endif |
0d5c8187 | 809 | static char byt_rt5651_components[50]; /* = "cfg-spk:* cfg-mic:*" */ |
b91f432c HG |
810 | |
811 | static int byt_rt5651_suspend(struct snd_soc_card *card) | |
812 | { | |
813 | struct snd_soc_component *component; | |
814 | ||
815 | if (!BYT_RT5651_JDSRC(byt_rt5651_quirk)) | |
816 | return 0; | |
817 | ||
f70f18f7 | 818 | for_each_card_components(card, component) { |
b91f432c HG |
819 | if (!strcmp(component->name, byt_rt5651_codec_name)) { |
820 | dev_dbg(component->dev, "disabling jack detect before suspend\n"); | |
821 | snd_soc_component_set_jack(component, NULL, NULL); | |
822 | break; | |
823 | } | |
824 | } | |
825 | ||
826 | return 0; | |
827 | } | |
828 | ||
829 | static int byt_rt5651_resume(struct snd_soc_card *card) | |
830 | { | |
831 | struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card); | |
832 | struct snd_soc_component *component; | |
833 | ||
834 | if (!BYT_RT5651_JDSRC(byt_rt5651_quirk)) | |
835 | return 0; | |
836 | ||
f70f18f7 | 837 | for_each_card_components(card, component) { |
b91f432c HG |
838 | if (!strcmp(component->name, byt_rt5651_codec_name)) { |
839 | dev_dbg(component->dev, "re-enabling jack detect after resume\n"); | |
90768eaf HG |
840 | snd_soc_component_set_jack(component, &priv->jack, |
841 | priv->hp_detect); | |
b91f432c HG |
842 | break; |
843 | } | |
844 | } | |
845 | ||
846 | return 0; | |
847 | } | |
848 | ||
b4ecd58b | 849 | /* use space before codec name to simplify card ID, and simplify driver name */ |
41656c3d PLB |
850 | #define SOF_CARD_NAME "bytcht rt5651" /* card name will be 'sof-bytcht rt5651' */ |
851 | #define SOF_DRIVER_NAME "SOF" | |
852 | ||
b4ecd58b PLB |
853 | #define CARD_NAME "bytcr-rt5651" |
854 | #define DRIVER_NAME NULL /* card name will be used for driver name */ | |
b4ecd58b | 855 | |
2bd5bd15 | 856 | static struct snd_soc_card byt_rt5651_card = { |
b4ecd58b PLB |
857 | .name = CARD_NAME, |
858 | .driver_name = DRIVER_NAME, | |
2bd5bd15 PLB |
859 | .owner = THIS_MODULE, |
860 | .dai_link = byt_rt5651_dais, | |
861 | .num_links = ARRAY_SIZE(byt_rt5651_dais), | |
862 | .dapm_widgets = byt_rt5651_widgets, | |
863 | .num_dapm_widgets = ARRAY_SIZE(byt_rt5651_widgets), | |
864 | .dapm_routes = byt_rt5651_audio_map, | |
865 | .num_dapm_routes = ARRAY_SIZE(byt_rt5651_audio_map), | |
866 | .fully_routed = true, | |
b91f432c HG |
867 | .suspend_pre = byt_rt5651_suspend, |
868 | .resume_post = byt_rt5651_resume, | |
2bd5bd15 PLB |
869 | }; |
870 | ||
4d1f7a6e | 871 | static const struct acpi_gpio_params ext_amp_enable_gpios = { 0, 0, false }; |
5f6fb23d | 872 | |
4d1f7a6e AS |
873 | static const struct acpi_gpio_mapping cht_rt5651_gpios[] = { |
874 | /* | |
875 | * Some boards have I2cSerialBusV2, GpioIo, GpioInt as ACPI resources, | |
876 | * other boards may have I2cSerialBusV2, GpioInt, GpioIo instead. | |
877 | * We want the GpioIo one for the ext-amp-enable-gpio. | |
878 | */ | |
879 | { "ext-amp-enable-gpios", &ext_amp_enable_gpios, 1, ACPI_GPIO_QUIRK_ONLY_GPIOIO }, | |
0a3badd1 HG |
880 | { }, |
881 | }; | |
882 | ||
8a880a20 HG |
883 | struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ |
884 | u64 aif_value; /* 1: AIF1, 2: AIF2 */ | |
885 | u64 mclock_value; /* usually 25MHz (0x17d7940), ignored */ | |
886 | }; | |
02c0a3b3 | 887 | |
2bd5bd15 PLB |
888 | static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) |
889 | { | |
0c465e7a | 890 | struct device *dev = &pdev->dev; |
4ffdca62 | 891 | static const char * const mic_name[] = { "dmic", "in1", "in2", "in12" }; |
0c465e7a | 892 | struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); |
02c0a3b3 | 893 | struct byt_rt5651_private *priv; |
0b2c2093 | 894 | const char *platform_name; |
7075e9ba | 895 | struct acpi_device *adev; |
2c375204 | 896 | struct device *codec_dev; |
41656c3d | 897 | bool sof_parent; |
8a880a20 | 898 | bool is_bytcr = false; |
2bd5bd15 | 899 | int ret_val = 0; |
2193eb96 | 900 | int dai_index = 0; |
02c0a3b3 PLB |
901 | int i; |
902 | ||
269da8f7 | 903 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
02c0a3b3 PLB |
904 | if (!priv) |
905 | return -ENOMEM; | |
2bd5bd15 PLB |
906 | |
907 | /* register the soc card */ | |
269da8f7 | 908 | byt_rt5651_card.dev = dev; |
02c0a3b3 PLB |
909 | snd_soc_card_set_drvdata(&byt_rt5651_card, priv); |
910 | ||
911 | /* fix index of codec dai */ | |
02c0a3b3 | 912 | for (i = 0; i < ARRAY_SIZE(byt_rt5651_dais); i++) { |
7d99a70b HG |
913 | if (byt_rt5651_dais[i].codecs->name && |
914 | !strcmp(byt_rt5651_dais[i].codecs->name, | |
01fee62a | 915 | "i2c-10EC5651:00")) { |
02c0a3b3 PLB |
916 | dai_index = i; |
917 | break; | |
918 | } | |
919 | } | |
920 | ||
921 | /* fixup codec name based on HID */ | |
7075e9ba AS |
922 | adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1); |
923 | if (adev) { | |
924 | snprintf(byt_rt5651_codec_name, sizeof(byt_rt5651_codec_name), | |
925 | "i2c-%s", acpi_dev_name(adev)); | |
01fee62a | 926 | byt_rt5651_dais[dai_index].codecs->name = byt_rt5651_codec_name; |
7075e9ba | 927 | } else { |
269da8f7 | 928 | dev_err(dev, "Error cannot find '%s' dev\n", mach->id); |
69efe3b8 | 929 | return -ENXIO; |
02c0a3b3 PLB |
930 | } |
931 | ||
d3409eb2 | 932 | codec_dev = acpi_get_first_physical_node(adev); |
72185882 | 933 | acpi_dev_put(adev); |
2c375204 HG |
934 | if (!codec_dev) |
935 | return -EPROBE_DEFER; | |
d3409eb2 | 936 | priv->codec_dev = get_device(codec_dev); |
2c375204 | 937 | |
8a880a20 HG |
938 | /* |
939 | * swap SSP0 if bytcr is detected | |
940 | * (will be overridden if DMI quirk is detected) | |
941 | */ | |
536cfd2f | 942 | if (soc_intel_is_byt()) { |
3ee1cd4f | 943 | if (mach->mach_params.acpi_ipc_irq_index == 0) |
8a880a20 HG |
944 | is_bytcr = true; |
945 | } | |
946 | ||
947 | if (is_bytcr) { | |
948 | /* | |
949 | * Baytrail CR platforms may have CHAN package in BIOS, try | |
950 | * to find relevant routing quirk based as done on Windows | |
951 | * platforms. We have to read the information directly from the | |
952 | * BIOS, at this stage the card is not created and the links | |
953 | * with the codec driver/pdata are non-existent | |
954 | */ | |
955 | ||
9972773c | 956 | struct acpi_chan_package chan_package = { 0 }; |
8a880a20 HG |
957 | |
958 | /* format specified: 2 64-bit integers */ | |
959 | struct acpi_buffer format = {sizeof("NN"), "NN"}; | |
960 | struct acpi_buffer state = {0, NULL}; | |
961 | struct snd_soc_acpi_package_context pkg_ctx; | |
962 | bool pkg_found = false; | |
963 | ||
964 | state.length = sizeof(chan_package); | |
965 | state.pointer = &chan_package; | |
966 | ||
967 | pkg_ctx.name = "CHAN"; | |
968 | pkg_ctx.length = 2; | |
969 | pkg_ctx.format = &format; | |
970 | pkg_ctx.state = &state; | |
971 | pkg_ctx.data_valid = false; | |
972 | ||
973 | pkg_found = snd_soc_acpi_find_package_from_hid(mach->id, | |
974 | &pkg_ctx); | |
975 | if (pkg_found) { | |
976 | if (chan_package.aif_value == 1) { | |
269da8f7 | 977 | dev_info(dev, "BIOS Routing: AIF1 connected\n"); |
8a880a20 HG |
978 | byt_rt5651_quirk |= BYT_RT5651_SSP0_AIF1; |
979 | } else if (chan_package.aif_value == 2) { | |
269da8f7 | 980 | dev_info(dev, "BIOS Routing: AIF2 connected\n"); |
8a880a20 HG |
981 | byt_rt5651_quirk |= BYT_RT5651_SSP0_AIF2; |
982 | } else { | |
269da8f7 | 983 | dev_info(dev, "BIOS Routing isn't valid, ignored\n"); |
8a880a20 HG |
984 | pkg_found = false; |
985 | } | |
986 | } | |
987 | ||
988 | if (!pkg_found) { | |
989 | /* no BIOS indications, assume SSP0-AIF2 connection */ | |
990 | byt_rt5651_quirk |= BYT_RT5651_SSP0_AIF2; | |
991 | } | |
8a880a20 HG |
992 | } |
993 | ||
02c0a3b3 PLB |
994 | /* check quirks before creating card */ |
995 | dmi_check_system(byt_rt5651_quirk_table); | |
46058aeb | 996 | |
fb45befa | 997 | if (quirk_override != -1) { |
269da8f7 | 998 | dev_info(dev, "Overriding quirk 0x%lx => 0x%x\n", |
2697f3af | 999 | byt_rt5651_quirk, quirk_override); |
7eb18731 HG |
1000 | byt_rt5651_quirk = quirk_override; |
1001 | } | |
1002 | ||
46058aeb | 1003 | /* Must be called before register_card, also see declaration comment. */ |
0bd3c071 | 1004 | ret_val = byt_rt5651_add_codec_device_props(codec_dev, priv); |
c50f126b | 1005 | if (ret_val) |
f1f8a961 | 1006 | goto err_device; |
5f6fb23d HG |
1007 | |
1008 | /* Cherry Trail devices use an external amplifier enable gpio */ | |
536cfd2f | 1009 | if (soc_intel_is_cht() && !byt_rt5651_gpios) |
4d1f7a6e | 1010 | byt_rt5651_gpios = cht_rt5651_gpios; |
fee3e1cb HG |
1011 | |
1012 | if (byt_rt5651_gpios) { | |
1013 | devm_acpi_dev_add_driver_gpios(codec_dev, byt_rt5651_gpios); | |
269da8f7 | 1014 | priv->ext_amp_gpio = devm_fwnode_gpiod_get(dev, codec_dev->fwnode, |
e26c4e90 DT |
1015 | "ext-amp-enable", |
1016 | GPIOD_OUT_LOW, | |
1017 | "speaker-amp"); | |
5f6fb23d HG |
1018 | if (IS_ERR(priv->ext_amp_gpio)) { |
1019 | ret_val = PTR_ERR(priv->ext_amp_gpio); | |
1020 | switch (ret_val) { | |
1021 | case -ENOENT: | |
1022 | priv->ext_amp_gpio = NULL; | |
1023 | break; | |
1024 | default: | |
269da8f7 | 1025 | dev_err(dev, "Failed to get ext-amp-enable GPIO: %d\n", ret_val); |
df561f66 | 1026 | fallthrough; |
5f6fb23d | 1027 | case -EPROBE_DEFER: |
c50f126b | 1028 | goto err; |
5f6fb23d HG |
1029 | } |
1030 | } | |
269da8f7 | 1031 | priv->hp_detect = devm_fwnode_gpiod_get(dev, codec_dev->fwnode, |
e26c4e90 DT |
1032 | "hp-detect", |
1033 | GPIOD_IN, | |
1034 | "hp-detect"); | |
90768eaf HG |
1035 | if (IS_ERR(priv->hp_detect)) { |
1036 | ret_val = PTR_ERR(priv->hp_detect); | |
1037 | switch (ret_val) { | |
1038 | case -ENOENT: | |
1039 | priv->hp_detect = NULL; | |
1040 | break; | |
1041 | default: | |
269da8f7 | 1042 | dev_err(dev, "Failed to get hp-detect GPIO: %d\n", ret_val); |
df561f66 | 1043 | fallthrough; |
90768eaf | 1044 | case -EPROBE_DEFER: |
c50f126b | 1045 | goto err; |
90768eaf HG |
1046 | } |
1047 | } | |
5f6fb23d HG |
1048 | } |
1049 | ||
269da8f7 | 1050 | log_quirks(dev); |
02c0a3b3 | 1051 | |
8a880a20 | 1052 | if ((byt_rt5651_quirk & BYT_RT5651_SSP2_AIF2) || |
fcce38d8 JU |
1053 | (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2)) |
1054 | byt_rt5651_dais[dai_index].codecs->dai_name = "rt5651-aif2"; | |
8a880a20 HG |
1055 | |
1056 | if ((byt_rt5651_quirk & BYT_RT5651_SSP0_AIF1) || | |
fcce38d8 JU |
1057 | (byt_rt5651_quirk & BYT_RT5651_SSP0_AIF2)) |
1058 | byt_rt5651_dais[dai_index].cpus->dai_name = "ssp0-port"; | |
8a880a20 | 1059 | |
02c0a3b3 | 1060 | if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) { |
a8627df5 | 1061 | priv->mclk = devm_clk_get_optional(dev, "pmc_plt_clk_3"); |
02c0a3b3 | 1062 | if (IS_ERR(priv->mclk)) { |
45c5dc45 AS |
1063 | ret_val = dev_err_probe(dev, PTR_ERR(priv->mclk), |
1064 | "Failed to get MCLK from pmc_plt_clk_3\n"); | |
a8627df5 | 1065 | goto err; |
02c0a3b3 | 1066 | } |
a8627df5 AS |
1067 | /* |
1068 | * Fall back to bit clock usage when clock is not | |
1069 | * available likely due to missing dependencies. | |
1070 | */ | |
1071 | if (!priv->mclk) | |
1072 | byt_rt5651_quirk &= ~BYT_RT5651_MCLK_EN; | |
02c0a3b3 PLB |
1073 | } |
1074 | ||
0d5c8187 JK |
1075 | snprintf(byt_rt5651_components, sizeof(byt_rt5651_components), |
1076 | "cfg-spk:%s cfg-mic:%s%s", | |
1077 | (byt_rt5651_quirk & BYT_RT5651_MONO_SPEAKER) ? "1" : "2", | |
1078 | mic_name[BYT_RT5651_MAP(byt_rt5651_quirk)], | |
1079 | (byt_rt5651_quirk & BYT_RT5651_HP_LR_SWAPPED) ? | |
1080 | " cfg-hp:lrswap" : ""); | |
1081 | byt_rt5651_card.components = byt_rt5651_components; | |
b5706f8e | 1082 | #if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES) |
64484cce | 1083 | snprintf(byt_rt5651_long_name, sizeof(byt_rt5651_long_name), |
a0d1d867 HG |
1084 | "bytcr-rt5651-%s-spk-%s-mic%s", |
1085 | (byt_rt5651_quirk & BYT_RT5651_MONO_SPEAKER) ? | |
1086 | "mono" : "stereo", | |
0d5c8187 JK |
1087 | mic_name[BYT_RT5651_MAP(byt_rt5651_quirk)], |
1088 | (byt_rt5651_quirk & BYT_RT5651_HP_LR_SWAPPED) ? | |
1089 | "-hp-swapped" : ""); | |
64484cce | 1090 | byt_rt5651_card.long_name = byt_rt5651_long_name; |
b5706f8e | 1091 | #endif |
64484cce | 1092 | |
f1eebb3b | 1093 | /* override platform name, if required */ |
0b2c2093 PLB |
1094 | platform_name = mach->mach_params.platform; |
1095 | ||
1096 | ret_val = snd_soc_fixup_dai_links_platform_name(&byt_rt5651_card, | |
1097 | platform_name); | |
1098 | if (ret_val) | |
c50f126b | 1099 | goto err; |
0b2c2093 | 1100 | |
269da8f7 | 1101 | sof_parent = snd_soc_acpi_sof_parent(dev); |
41656c3d PLB |
1102 | |
1103 | /* set card and driver name */ | |
1104 | if (sof_parent) { | |
1105 | byt_rt5651_card.name = SOF_CARD_NAME; | |
1106 | byt_rt5651_card.driver_name = SOF_DRIVER_NAME; | |
1107 | } else { | |
1108 | byt_rt5651_card.name = CARD_NAME; | |
1109 | byt_rt5651_card.driver_name = DRIVER_NAME; | |
1110 | } | |
1111 | ||
05ff312b PLB |
1112 | /* set pm ops */ |
1113 | if (sof_parent) | |
269da8f7 | 1114 | dev->driver->pm = &snd_soc_pm_ops; |
2bd5bd15 | 1115 | |
269da8f7 | 1116 | ret_val = devm_snd_soc_register_card(dev, &byt_rt5651_card); |
2bd5bd15 | 1117 | if (ret_val) { |
269da8f7 | 1118 | dev_err(dev, "devm_snd_soc_register_card failed %d\n", ret_val); |
c50f126b | 1119 | goto err; |
2bd5bd15 PLB |
1120 | } |
1121 | platform_set_drvdata(pdev, &byt_rt5651_card); | |
1122 | return ret_val; | |
c50f126b PLB |
1123 | |
1124 | err: | |
0bd3c071 | 1125 | device_remove_software_node(priv->codec_dev); |
f1f8a961 | 1126 | err_device: |
c50f126b PLB |
1127 | put_device(priv->codec_dev); |
1128 | return ret_val; | |
1129 | } | |
1130 | ||
00f2ac22 | 1131 | static void snd_byt_rt5651_mc_remove(struct platform_device *pdev) |
c50f126b PLB |
1132 | { |
1133 | struct snd_soc_card *card = platform_get_drvdata(pdev); | |
1134 | struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card); | |
1135 | ||
0bd3c071 | 1136 | device_remove_software_node(priv->codec_dev); |
c50f126b | 1137 | put_device(priv->codec_dev); |
2bd5bd15 PLB |
1138 | } |
1139 | ||
1140 | static struct platform_driver snd_byt_rt5651_mc_driver = { | |
1141 | .driver = { | |
1142 | .name = "bytcr_rt5651", | |
2bd5bd15 PLB |
1143 | }, |
1144 | .probe = snd_byt_rt5651_mc_probe, | |
00f2ac22 | 1145 | .remove_new = snd_byt_rt5651_mc_remove, |
2bd5bd15 PLB |
1146 | }; |
1147 | ||
1148 | module_platform_driver(snd_byt_rt5651_mc_driver); | |
1149 | ||
1150 | MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver for RT5651"); | |
1151 | MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>"); | |
1152 | MODULE_LICENSE("GPL v2"); | |
1153 | MODULE_ALIAS("platform:bytcr_rt5651"); |