Commit | Line | Data |
---|---|---|
8e8e69d6 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
a03bdaa5 DD |
2 | /* |
3 | * bytcht_es8316.c - ASoc Machine driver for Intel Baytrail/Cherrytrail | |
4 | * platforms with Everest ES8316 SoC | |
5 | * | |
6 | * Copyright (C) 2017 Endless Mobile, Inc. | |
7 | * Authors: David Yang <yangxiaohua@everest-semi.com>, | |
8 | * Daniel Drake <drake@endlessm.com> | |
9 | * | |
10 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
11 | * | |
a03bdaa5 DD |
12 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
13 | */ | |
0d3e91da | 14 | #include <linux/acpi.h> |
6ca382c4 HG |
15 | #include <linux/clk.h> |
16 | #include <linux/device.h> | |
a8d218f4 | 17 | #include <linux/dmi.h> |
0d3e91da HG |
18 | #include <linux/gpio/consumer.h> |
19 | #include <linux/i2c.h> | |
a03bdaa5 | 20 | #include <linux/init.h> |
4bf538b4 | 21 | #include <linux/input.h> |
a03bdaa5 DD |
22 | #include <linux/module.h> |
23 | #include <linux/platform_device.h> | |
a03bdaa5 | 24 | #include <linux/slab.h> |
4bf538b4 | 25 | #include <sound/jack.h> |
a03bdaa5 DD |
26 | #include <sound/pcm.h> |
27 | #include <sound/pcm_params.h> | |
28 | #include <sound/soc.h> | |
7feb2f78 | 29 | #include <sound/soc-acpi.h> |
a03bdaa5 | 30 | #include "../atom/sst-atom-controls.h" |
a03bdaa5 | 31 | #include "../common/sst-dsp.h" |
536cfd2f | 32 | #include "../common/soc-intel-quirks.h" |
a03bdaa5 | 33 | |
ba49cf6f PC |
34 | /* jd-inv + terminating entry */ |
35 | #define MAX_NO_PROPS 2 | |
36 | ||
a03bdaa5 DD |
37 | struct byt_cht_es8316_private { |
38 | struct clk *mclk; | |
4bf538b4 | 39 | struct snd_soc_jack jack; |
0d3e91da HG |
40 | struct gpio_desc *speaker_en_gpio; |
41 | bool speaker_en; | |
a03bdaa5 DD |
42 | }; |
43 | ||
730501a9 HG |
44 | enum { |
45 | BYT_CHT_ES8316_INTMIC_IN1_MAP, | |
46 | BYT_CHT_ES8316_INTMIC_IN2_MAP, | |
47 | }; | |
48 | ||
49 | #define BYT_CHT_ES8316_MAP(quirk) ((quirk) & GENMASK(3, 0)) | |
349e1386 | 50 | #define BYT_CHT_ES8316_SSP0 BIT(16) |
249d2fc9 | 51 | #define BYT_CHT_ES8316_MONO_SPEAKER BIT(17) |
ba49cf6f | 52 | #define BYT_CHT_ES8316_JD_INVERTED BIT(18) |
349e1386 | 53 | |
1fb1e93a | 54 | static unsigned long quirk; |
349e1386 HG |
55 | |
56 | static int quirk_override = -1; | |
57 | module_param_named(quirk, quirk_override, int, 0444); | |
58 | MODULE_PARM_DESC(quirk, "Board-specific quirk override"); | |
59 | ||
60 | static void log_quirks(struct device *dev) | |
61 | { | |
730501a9 HG |
62 | if (BYT_CHT_ES8316_MAP(quirk) == BYT_CHT_ES8316_INTMIC_IN1_MAP) |
63 | dev_info(dev, "quirk IN1_MAP enabled"); | |
64 | if (BYT_CHT_ES8316_MAP(quirk) == BYT_CHT_ES8316_INTMIC_IN2_MAP) | |
65 | dev_info(dev, "quirk IN2_MAP enabled"); | |
349e1386 HG |
66 | if (quirk & BYT_CHT_ES8316_SSP0) |
67 | dev_info(dev, "quirk SSP0 enabled"); | |
249d2fc9 HG |
68 | if (quirk & BYT_CHT_ES8316_MONO_SPEAKER) |
69 | dev_info(dev, "quirk MONO_SPEAKER enabled\n"); | |
ba49cf6f PC |
70 | if (quirk & BYT_CHT_ES8316_JD_INVERTED) |
71 | dev_info(dev, "quirk JD_INVERTED enabled\n"); | |
349e1386 HG |
72 | } |
73 | ||
0d3e91da HG |
74 | static int byt_cht_es8316_speaker_power_event(struct snd_soc_dapm_widget *w, |
75 | struct snd_kcontrol *kcontrol, int event) | |
76 | { | |
77 | struct snd_soc_card *card = w->dapm->card; | |
78 | struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card); | |
79 | ||
80 | if (SND_SOC_DAPM_EVENT_ON(event)) | |
81 | priv->speaker_en = true; | |
82 | else | |
83 | priv->speaker_en = false; | |
84 | ||
85 | gpiod_set_value_cansleep(priv->speaker_en_gpio, priv->speaker_en); | |
86 | ||
87 | return 0; | |
88 | } | |
89 | ||
a03bdaa5 | 90 | static const struct snd_soc_dapm_widget byt_cht_es8316_widgets[] = { |
0d3e91da | 91 | SND_SOC_DAPM_SPK("Speaker", NULL), |
a03bdaa5 | 92 | SND_SOC_DAPM_HP("Headphone", NULL), |
4bf538b4 | 93 | SND_SOC_DAPM_MIC("Headset Mic", NULL), |
730501a9 | 94 | SND_SOC_DAPM_MIC("Internal Mic", NULL), |
0d3e91da HG |
95 | |
96 | SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0, | |
97 | byt_cht_es8316_speaker_power_event, | |
98 | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), | |
a03bdaa5 DD |
99 | }; |
100 | ||
101 | static const struct snd_soc_dapm_route byt_cht_es8316_audio_map[] = { | |
a03bdaa5 DD |
102 | {"Headphone", NULL, "HPOL"}, |
103 | {"Headphone", NULL, "HPOR"}, | |
0d3e91da HG |
104 | |
105 | /* | |
106 | * There is no separate speaker output instead the speakers are muxed to | |
107 | * the HP outputs. The mux is controlled by the "Speaker Power" supply. | |
108 | */ | |
109 | {"Speaker", NULL, "HPOL"}, | |
110 | {"Speaker", NULL, "HPOR"}, | |
111 | {"Speaker", NULL, "Speaker Power"}, | |
349e1386 HG |
112 | }; |
113 | ||
730501a9 HG |
114 | static const struct snd_soc_dapm_route byt_cht_es8316_intmic_in1_map[] = { |
115 | {"MIC1", NULL, "Internal Mic"}, | |
116 | {"MIC2", NULL, "Headset Mic"}, | |
117 | }; | |
118 | ||
119 | static const struct snd_soc_dapm_route byt_cht_es8316_intmic_in2_map[] = { | |
120 | {"MIC2", NULL, "Internal Mic"}, | |
121 | {"MIC1", NULL, "Headset Mic"}, | |
122 | }; | |
123 | ||
349e1386 HG |
124 | static const struct snd_soc_dapm_route byt_cht_es8316_ssp0_map[] = { |
125 | {"Playback", NULL, "ssp0 Tx"}, | |
126 | {"ssp0 Tx", NULL, "modem_out"}, | |
127 | {"modem_in", NULL, "ssp0 Rx"}, | |
128 | {"ssp0 Rx", NULL, "Capture"}, | |
129 | }; | |
a03bdaa5 | 130 | |
349e1386 | 131 | static const struct snd_soc_dapm_route byt_cht_es8316_ssp2_map[] = { |
a03bdaa5 DD |
132 | {"Playback", NULL, "ssp2 Tx"}, |
133 | {"ssp2 Tx", NULL, "codec_out0"}, | |
134 | {"ssp2 Tx", NULL, "codec_out1"}, | |
135 | {"codec_in0", NULL, "ssp2 Rx" }, | |
136 | {"codec_in1", NULL, "ssp2 Rx" }, | |
137 | {"ssp2 Rx", NULL, "Capture"}, | |
138 | }; | |
139 | ||
140 | static const struct snd_kcontrol_new byt_cht_es8316_controls[] = { | |
0d3e91da | 141 | SOC_DAPM_PIN_SWITCH("Speaker"), |
a03bdaa5 | 142 | SOC_DAPM_PIN_SWITCH("Headphone"), |
4bf538b4 | 143 | SOC_DAPM_PIN_SWITCH("Headset Mic"), |
730501a9 | 144 | SOC_DAPM_PIN_SWITCH("Internal Mic"), |
a03bdaa5 DD |
145 | }; |
146 | ||
4bf538b4 HG |
147 | static struct snd_soc_jack_pin byt_cht_es8316_jack_pins[] = { |
148 | { | |
149 | .pin = "Headphone", | |
150 | .mask = SND_JACK_HEADPHONE, | |
151 | }, | |
152 | { | |
153 | .pin = "Headset Mic", | |
154 | .mask = SND_JACK_MICROPHONE, | |
155 | }, | |
156 | }; | |
157 | ||
a03bdaa5 DD |
158 | static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime) |
159 | { | |
4bf538b4 | 160 | struct snd_soc_component *codec = runtime->codec_dai->component; |
a03bdaa5 DD |
161 | struct snd_soc_card *card = runtime->card; |
162 | struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card); | |
349e1386 HG |
163 | const struct snd_soc_dapm_route *custom_map; |
164 | int num_routes; | |
a03bdaa5 DD |
165 | int ret; |
166 | ||
167 | card->dapm.idle_bias_off = true; | |
168 | ||
730501a9 HG |
169 | switch (BYT_CHT_ES8316_MAP(quirk)) { |
170 | case BYT_CHT_ES8316_INTMIC_IN1_MAP: | |
171 | default: | |
172 | custom_map = byt_cht_es8316_intmic_in1_map; | |
173 | num_routes = ARRAY_SIZE(byt_cht_es8316_intmic_in1_map); | |
174 | break; | |
175 | case BYT_CHT_ES8316_INTMIC_IN2_MAP: | |
176 | custom_map = byt_cht_es8316_intmic_in2_map; | |
177 | num_routes = ARRAY_SIZE(byt_cht_es8316_intmic_in2_map); | |
178 | break; | |
179 | } | |
180 | ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes); | |
181 | if (ret) | |
182 | return ret; | |
183 | ||
349e1386 HG |
184 | if (quirk & BYT_CHT_ES8316_SSP0) { |
185 | custom_map = byt_cht_es8316_ssp0_map; | |
186 | num_routes = ARRAY_SIZE(byt_cht_es8316_ssp0_map); | |
187 | } else { | |
188 | custom_map = byt_cht_es8316_ssp2_map; | |
189 | num_routes = ARRAY_SIZE(byt_cht_es8316_ssp2_map); | |
190 | } | |
191 | ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes); | |
192 | if (ret) | |
193 | return ret; | |
194 | ||
a03bdaa5 DD |
195 | /* |
196 | * The firmware might enable the clock at boot (this information | |
197 | * may or may not be reflected in the enable clock register). | |
198 | * To change the rate we must disable the clock first to cover these | |
199 | * cases. Due to common clock framework restrictions that do not allow | |
200 | * to disable a clock that has not been enabled, we need to enable | |
201 | * the clock first. | |
202 | */ | |
203 | ret = clk_prepare_enable(priv->mclk); | |
204 | if (!ret) | |
205 | clk_disable_unprepare(priv->mclk); | |
206 | ||
207 | ret = clk_set_rate(priv->mclk, 19200000); | |
208 | if (ret) | |
209 | dev_err(card->dev, "unable to set MCLK rate\n"); | |
210 | ||
211 | ret = clk_prepare_enable(priv->mclk); | |
212 | if (ret) | |
213 | dev_err(card->dev, "unable to enable MCLK\n"); | |
214 | ||
215 | ret = snd_soc_dai_set_sysclk(runtime->codec_dai, 0, 19200000, | |
216 | SND_SOC_CLOCK_IN); | |
217 | if (ret < 0) { | |
218 | dev_err(card->dev, "can't set codec clock %d\n", ret); | |
219 | return ret; | |
220 | } | |
221 | ||
4bf538b4 HG |
222 | ret = snd_soc_card_jack_new(card, "Headset", |
223 | SND_JACK_HEADSET | SND_JACK_BTN_0, | |
224 | &priv->jack, byt_cht_es8316_jack_pins, | |
225 | ARRAY_SIZE(byt_cht_es8316_jack_pins)); | |
226 | if (ret) { | |
227 | dev_err(card->dev, "jack creation failed %d\n", ret); | |
228 | return ret; | |
229 | } | |
230 | ||
231 | snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); | |
232 | snd_soc_component_set_jack(codec, &priv->jack, NULL); | |
233 | ||
a03bdaa5 DD |
234 | return 0; |
235 | } | |
236 | ||
237 | static const struct snd_soc_pcm_stream byt_cht_es8316_dai_params = { | |
238 | .formats = SNDRV_PCM_FMTBIT_S24_LE, | |
239 | .rate_min = 48000, | |
240 | .rate_max = 48000, | |
241 | .channels_min = 2, | |
242 | .channels_max = 2, | |
243 | }; | |
244 | ||
245 | static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd, | |
246 | struct snd_pcm_hw_params *params) | |
247 | { | |
248 | struct snd_interval *rate = hw_param_interval(params, | |
249 | SNDRV_PCM_HW_PARAM_RATE); | |
250 | struct snd_interval *channels = hw_param_interval(params, | |
251 | SNDRV_PCM_HW_PARAM_CHANNELS); | |
349e1386 | 252 | int ret, bits; |
a03bdaa5 DD |
253 | |
254 | /* The DSP will covert the FE rate to 48k, stereo */ | |
255 | rate->min = rate->max = 48000; | |
256 | channels->min = channels->max = 2; | |
257 | ||
349e1386 HG |
258 | if (quirk & BYT_CHT_ES8316_SSP0) { |
259 | /* set SSP0 to 16-bit */ | |
260 | params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); | |
261 | bits = 16; | |
262 | } else { | |
263 | /* set SSP2 to 24-bit */ | |
264 | params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); | |
265 | bits = 24; | |
266 | } | |
a03bdaa5 DD |
267 | |
268 | /* | |
269 | * Default mode for SSP configuration is TDM 4 slot, override config | |
270 | * with explicit setting to I2S 2ch 24-bit. The word length is set with | |
271 | * dai_set_tdm_slot() since there is no other API exposed | |
272 | */ | |
273 | ret = snd_soc_dai_set_fmt(rtd->cpu_dai, | |
274 | SND_SOC_DAIFMT_I2S | | |
275 | SND_SOC_DAIFMT_NB_NF | | |
276 | SND_SOC_DAIFMT_CBS_CFS | |
277 | ); | |
278 | if (ret < 0) { | |
279 | dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); | |
280 | return ret; | |
281 | } | |
282 | ||
349e1386 | 283 | ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, bits); |
a03bdaa5 DD |
284 | if (ret < 0) { |
285 | dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); | |
286 | return ret; | |
287 | } | |
288 | ||
289 | return 0; | |
290 | } | |
291 | ||
292 | static int byt_cht_es8316_aif1_startup(struct snd_pcm_substream *substream) | |
293 | { | |
294 | return snd_pcm_hw_constraint_single(substream->runtime, | |
295 | SNDRV_PCM_HW_PARAM_RATE, 48000); | |
296 | } | |
297 | ||
298 | static const struct snd_soc_ops byt_cht_es8316_aif1_ops = { | |
299 | .startup = byt_cht_es8316_aif1_startup, | |
300 | }; | |
301 | ||
143029db KM |
302 | SND_SOC_DAILINK_DEF(dummy, |
303 | DAILINK_COMP_ARRAY(COMP_DUMMY())); | |
304 | ||
305 | SND_SOC_DAILINK_DEF(media, | |
306 | DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai"))); | |
307 | ||
308 | SND_SOC_DAILINK_DEF(deepbuffer, | |
309 | DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai"))); | |
310 | ||
311 | SND_SOC_DAILINK_DEF(ssp2_port, | |
312 | DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port"))); | |
313 | SND_SOC_DAILINK_DEF(ssp2_codec, | |
314 | DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8316:00", "ES8316 HiFi"))); | |
315 | ||
316 | SND_SOC_DAILINK_DEF(platform, | |
317 | DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform"))); | |
318 | ||
a03bdaa5 DD |
319 | static struct snd_soc_dai_link byt_cht_es8316_dais[] = { |
320 | [MERR_DPCM_AUDIO] = { | |
321 | .name = "Audio Port", | |
322 | .stream_name = "Audio", | |
a03bdaa5 DD |
323 | .nonatomic = true, |
324 | .dynamic = 1, | |
325 | .dpcm_playback = 1, | |
326 | .dpcm_capture = 1, | |
327 | .ops = &byt_cht_es8316_aif1_ops, | |
143029db | 328 | SND_SOC_DAILINK_REG(media, dummy, platform), |
a03bdaa5 DD |
329 | }, |
330 | ||
331 | [MERR_DPCM_DEEP_BUFFER] = { | |
332 | .name = "Deep-Buffer Audio Port", | |
333 | .stream_name = "Deep-Buffer Audio", | |
a03bdaa5 DD |
334 | .nonatomic = true, |
335 | .dynamic = 1, | |
336 | .dpcm_playback = 1, | |
337 | .ops = &byt_cht_es8316_aif1_ops, | |
143029db | 338 | SND_SOC_DAILINK_REG(deepbuffer, dummy, platform), |
a03bdaa5 DD |
339 | }, |
340 | ||
a03bdaa5 DD |
341 | /* back ends */ |
342 | { | |
343 | /* Only SSP2 has been tested here, so BYT-CR platforms that | |
344 | * require SSP0 will not work. | |
345 | */ | |
346 | .name = "SSP2-Codec", | |
149f7757 | 347 | .id = 0, |
a03bdaa5 | 348 | .no_pcm = 1, |
a03bdaa5 DD |
349 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
350 | | SND_SOC_DAIFMT_CBS_CFS, | |
351 | .be_hw_params_fixup = byt_cht_es8316_codec_fixup, | |
352 | .nonatomic = true, | |
353 | .dpcm_playback = 1, | |
354 | .dpcm_capture = 1, | |
355 | .init = byt_cht_es8316_init, | |
143029db | 356 | SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform), |
a03bdaa5 DD |
357 | }, |
358 | }; | |
359 | ||
360 | ||
361 | /* SoC card */ | |
4bf538b4 | 362 | static char codec_name[SND_ACPI_I2C_ID_LEN]; |
249d2fc9 | 363 | static char long_name[50]; /* = "bytcht-es8316-*-spk-*-mic" */ |
4bf538b4 HG |
364 | |
365 | static int byt_cht_es8316_suspend(struct snd_soc_card *card) | |
366 | { | |
367 | struct snd_soc_component *component; | |
368 | ||
369 | for_each_card_components(card, component) { | |
370 | if (!strcmp(component->name, codec_name)) { | |
371 | dev_dbg(component->dev, "disabling jack detect before suspend\n"); | |
372 | snd_soc_component_set_jack(component, NULL, NULL); | |
373 | break; | |
374 | } | |
375 | } | |
376 | ||
377 | return 0; | |
378 | } | |
379 | ||
380 | static int byt_cht_es8316_resume(struct snd_soc_card *card) | |
381 | { | |
382 | struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card); | |
383 | struct snd_soc_component *component; | |
384 | ||
385 | for_each_card_components(card, component) { | |
386 | if (!strcmp(component->name, codec_name)) { | |
387 | dev_dbg(component->dev, "re-enabling jack detect after resume\n"); | |
388 | snd_soc_component_set_jack(component, &priv->jack, NULL); | |
389 | break; | |
390 | } | |
391 | } | |
392 | ||
0d3e91da HG |
393 | /* |
394 | * Some Cherry Trail boards with an ES8316 codec have a bug in their | |
395 | * ACPI tables where the MSSL1680 touchscreen's _PS0 and _PS3 methods | |
396 | * wrongly also set the speaker-enable GPIO to 1/0. Testing has shown | |
397 | * that this really is a bug and the GPIO has no influence on the | |
398 | * touchscreen at all. | |
399 | * | |
400 | * The silead.c touchscreen driver does not support runtime suspend, so | |
401 | * the GPIO can only be changed underneath us during a system suspend. | |
402 | * This resume() function runs from a pm complete() callback, and thus | |
403 | * is guaranteed to run after the touchscreen driver/ACPI-subsys has | |
404 | * brought the touchscreen back up again (and thus changed the GPIO). | |
405 | * | |
406 | * So to work around this we pass GPIOD_FLAGS_BIT_NONEXCLUSIVE when | |
407 | * requesting the GPIO and we set its value here to undo any changes | |
408 | * done by the touchscreen's broken _PS0 ACPI method. | |
409 | */ | |
410 | gpiod_set_value_cansleep(priv->speaker_en_gpio, priv->speaker_en); | |
411 | ||
4bf538b4 HG |
412 | return 0; |
413 | } | |
414 | ||
a03bdaa5 DD |
415 | static struct snd_soc_card byt_cht_es8316_card = { |
416 | .name = "bytcht-es8316", | |
417 | .owner = THIS_MODULE, | |
418 | .dai_link = byt_cht_es8316_dais, | |
419 | .num_links = ARRAY_SIZE(byt_cht_es8316_dais), | |
420 | .dapm_widgets = byt_cht_es8316_widgets, | |
421 | .num_dapm_widgets = ARRAY_SIZE(byt_cht_es8316_widgets), | |
422 | .dapm_routes = byt_cht_es8316_audio_map, | |
423 | .num_dapm_routes = ARRAY_SIZE(byt_cht_es8316_audio_map), | |
424 | .controls = byt_cht_es8316_controls, | |
425 | .num_controls = ARRAY_SIZE(byt_cht_es8316_controls), | |
426 | .fully_routed = true, | |
4bf538b4 HG |
427 | .suspend_pre = byt_cht_es8316_suspend, |
428 | .resume_post = byt_cht_es8316_resume, | |
a03bdaa5 DD |
429 | }; |
430 | ||
0d3e91da HG |
431 | static const struct acpi_gpio_params first_gpio = { 0, 0, false }; |
432 | ||
433 | static const struct acpi_gpio_mapping byt_cht_es8316_gpios[] = { | |
434 | { "speaker-enable-gpios", &first_gpio, 1 }, | |
435 | { }, | |
436 | }; | |
437 | ||
a8d218f4 PC |
438 | /* Please keep this list alphabetically sorted */ |
439 | static const struct dmi_system_id byt_cht_es8316_quirk_table[] = { | |
aa2ba991 HG |
440 | { /* Irbis NB41 */ |
441 | .matches = { | |
442 | DMI_MATCH(DMI_SYS_VENDOR, "IRBIS"), | |
443 | DMI_MATCH(DMI_PRODUCT_NAME, "NB41"), | |
444 | }, | |
445 | .driver_data = (void *)(BYT_CHT_ES8316_INTMIC_IN2_MAP | |
446 | | BYT_CHT_ES8316_JD_INVERTED), | |
447 | }, | |
a8d218f4 PC |
448 | { /* Teclast X98 Plus II */ |
449 | .matches = { | |
450 | DMI_MATCH(DMI_SYS_VENDOR, "TECLAST"), | |
451 | DMI_MATCH(DMI_PRODUCT_NAME, "X98 Plus II"), | |
452 | }, | |
453 | .driver_data = (void *)(BYT_CHT_ES8316_INTMIC_IN1_MAP | |
454 | | BYT_CHT_ES8316_JD_INVERTED), | |
455 | }, | |
456 | {} | |
457 | }; | |
458 | ||
a03bdaa5 DD |
459 | static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev) |
460 | { | |
4ffdca62 | 461 | static const char * const mic_name[] = { "in1", "in2" }; |
ba49cf6f | 462 | struct property_entry props[MAX_NO_PROPS] = {}; |
a03bdaa5 | 463 | struct byt_cht_es8316_private *priv; |
a8d218f4 | 464 | const struct dmi_system_id *dmi_id; |
86909c8f | 465 | struct device *dev = &pdev->dev; |
3c22a73f | 466 | struct snd_soc_acpi_mach *mach; |
e4bc6b11 | 467 | const char *platform_name; |
645056da | 468 | struct acpi_device *adev; |
0d3e91da | 469 | struct device *codec_dev; |
ba49cf6f | 470 | unsigned int cnt = 0; |
3c22a73f PLB |
471 | int dai_index = 0; |
472 | int i; | |
473 | int ret = 0; | |
a03bdaa5 | 474 | |
86909c8f | 475 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
a03bdaa5 DD |
476 | if (!priv) |
477 | return -ENOMEM; | |
478 | ||
86909c8f | 479 | mach = dev->platform_data; |
3c22a73f PLB |
480 | /* fix index of codec dai */ |
481 | for (i = 0; i < ARRAY_SIZE(byt_cht_es8316_dais); i++) { | |
143029db | 482 | if (!strcmp(byt_cht_es8316_dais[i].codecs->name, |
3c22a73f PLB |
483 | "i2c-ESSX8316:00")) { |
484 | dai_index = i; | |
485 | break; | |
486 | } | |
487 | } | |
488 | ||
489 | /* fixup codec name based on HID */ | |
645056da AS |
490 | adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1); |
491 | if (adev) { | |
3c22a73f | 492 | snprintf(codec_name, sizeof(codec_name), |
645056da AS |
493 | "i2c-%s", acpi_dev_name(adev)); |
494 | put_device(&adev->dev); | |
143029db | 495 | byt_cht_es8316_dais[dai_index].codecs->name = codec_name; |
3c22a73f PLB |
496 | } |
497 | ||
e4bc6b11 | 498 | /* override plaform name, if required */ |
79136a01 | 499 | byt_cht_es8316_card.dev = dev; |
e4bc6b11 PLB |
500 | platform_name = mach->mach_params.platform; |
501 | ||
502 | ret = snd_soc_fixup_dai_links_platform_name(&byt_cht_es8316_card, | |
503 | platform_name); | |
504 | if (ret) | |
505 | return ret; | |
506 | ||
349e1386 | 507 | /* Check for BYTCR or other platform and setup quirks */ |
a8d218f4 PC |
508 | dmi_id = dmi_first_match(byt_cht_es8316_quirk_table); |
509 | if (dmi_id) { | |
1fb1e93a | 510 | quirk = (unsigned long)dmi_id->driver_data; |
536cfd2f PLB |
511 | } else if (soc_intel_is_byt() && |
512 | mach->mach_params.acpi_ipc_irq_index == 0) { | |
249d2fc9 HG |
513 | /* On BYTCR default to SSP0, internal-mic-in2-map, mono-spk */ |
514 | quirk = BYT_CHT_ES8316_SSP0 | BYT_CHT_ES8316_INTMIC_IN2_MAP | | |
515 | BYT_CHT_ES8316_MONO_SPEAKER; | |
349e1386 | 516 | } else { |
249d2fc9 HG |
517 | /* Others default to internal-mic-in1-map, mono-speaker */ |
518 | quirk = BYT_CHT_ES8316_INTMIC_IN1_MAP | | |
519 | BYT_CHT_ES8316_MONO_SPEAKER; | |
349e1386 HG |
520 | } |
521 | if (quirk_override != -1) { | |
1fb1e93a PLB |
522 | dev_info(dev, "Overriding quirk 0x%x => 0x%x\n", |
523 | (unsigned int)quirk, | |
349e1386 HG |
524 | quirk_override); |
525 | quirk = quirk_override; | |
526 | } | |
527 | log_quirks(dev); | |
528 | ||
529 | if (quirk & BYT_CHT_ES8316_SSP0) | |
143029db | 530 | byt_cht_es8316_dais[dai_index].cpus->dai_name = "ssp0-port"; |
349e1386 | 531 | |
86909c8f HG |
532 | /* get the clock */ |
533 | priv->mclk = devm_clk_get(dev, "pmc_plt_clk_3"); | |
a03bdaa5 DD |
534 | if (IS_ERR(priv->mclk)) { |
535 | ret = PTR_ERR(priv->mclk); | |
86909c8f | 536 | dev_err(dev, "clk_get pmc_plt_clk_3 failed: %d\n", ret); |
a03bdaa5 DD |
537 | return ret; |
538 | } | |
539 | ||
0d3e91da HG |
540 | /* get speaker enable GPIO */ |
541 | codec_dev = bus_find_device_by_name(&i2c_bus_type, NULL, codec_name); | |
542 | if (!codec_dev) | |
543 | return -EPROBE_DEFER; | |
544 | ||
ba49cf6f PC |
545 | if (quirk & BYT_CHT_ES8316_JD_INVERTED) |
546 | props[cnt++] = PROPERTY_ENTRY_BOOL("everest,jack-detect-inverted"); | |
547 | ||
548 | if (cnt) { | |
549 | ret = device_add_properties(codec_dev, props); | |
550 | if (ret) | |
551 | return ret; | |
552 | } | |
553 | ||
0d3e91da HG |
554 | devm_acpi_dev_add_driver_gpios(codec_dev, byt_cht_es8316_gpios); |
555 | priv->speaker_en_gpio = | |
556 | gpiod_get_index(codec_dev, "speaker-enable", 0, | |
557 | /* see comment in byt_cht_es8316_resume */ | |
558 | GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); | |
559 | put_device(codec_dev); | |
560 | ||
561 | if (IS_ERR(priv->speaker_en_gpio)) { | |
562 | ret = PTR_ERR(priv->speaker_en_gpio); | |
563 | switch (ret) { | |
564 | case -ENOENT: | |
565 | priv->speaker_en_gpio = NULL; | |
566 | break; | |
567 | default: | |
568 | dev_err(dev, "get speaker GPIO failed: %d\n", ret); | |
569 | /* fall through */ | |
570 | case -EPROBE_DEFER: | |
571 | return ret; | |
572 | } | |
573 | } | |
574 | ||
86909c8f | 575 | /* register the soc card */ |
249d2fc9 HG |
576 | snprintf(long_name, sizeof(long_name), "bytcht-es8316-%s-spk-%s-mic", |
577 | (quirk & BYT_CHT_ES8316_MONO_SPEAKER) ? "mono" : "stereo", | |
578 | mic_name[BYT_CHT_ES8316_MAP(quirk)]); | |
579 | byt_cht_es8316_card.long_name = long_name; | |
86909c8f HG |
580 | snd_soc_card_set_drvdata(&byt_cht_es8316_card, priv); |
581 | ||
582 | ret = devm_snd_soc_register_card(dev, &byt_cht_es8316_card); | |
a03bdaa5 | 583 | if (ret) { |
0d3e91da | 584 | gpiod_put(priv->speaker_en_gpio); |
86909c8f | 585 | dev_err(dev, "snd_soc_register_card failed: %d\n", ret); |
a03bdaa5 DD |
586 | return ret; |
587 | } | |
588 | platform_set_drvdata(pdev, &byt_cht_es8316_card); | |
86909c8f | 589 | return 0; |
a03bdaa5 DD |
590 | } |
591 | ||
0d3e91da HG |
592 | static int snd_byt_cht_es8316_mc_remove(struct platform_device *pdev) |
593 | { | |
f833fe20 WY |
594 | struct snd_soc_card *card = platform_get_drvdata(pdev); |
595 | struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card); | |
0d3e91da HG |
596 | |
597 | gpiod_put(priv->speaker_en_gpio); | |
598 | return 0; | |
599 | } | |
600 | ||
a03bdaa5 DD |
601 | static struct platform_driver snd_byt_cht_es8316_mc_driver = { |
602 | .driver = { | |
603 | .name = "bytcht_es8316", | |
604 | }, | |
605 | .probe = snd_byt_cht_es8316_mc_probe, | |
0d3e91da | 606 | .remove = snd_byt_cht_es8316_mc_remove, |
a03bdaa5 DD |
607 | }; |
608 | ||
609 | module_platform_driver(snd_byt_cht_es8316_mc_driver); | |
610 | MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Machine driver"); | |
611 | MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>"); | |
612 | MODULE_LICENSE("GPL v2"); | |
613 | MODULE_ALIAS("platform:bytcht_es8316"); |