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