1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright(c) 2015-18 Intel Corporation. |
3 | |
4 | /* |
5 | * hdac_hda.c - ASoC extensions to reuse the legacy HDA codec drivers |
6 | * with ASoC platform drivers. These APIs are called by the legacy HDA |
7 | * codec drivers using hdac_ext_bus_ops ops. |
8 | */ |
9 | |
10 | #include <linux/firmware.h> |
11 | #include <linux/init.h> |
12 | #include <linux/delay.h> |
13 | #include <linux/module.h> |
14 | #include <linux/pm_runtime.h> |
15 | #include <sound/pcm_params.h> |
16 | #include <sound/soc.h> |
17 | #include <sound/hdaudio_ext.h> |
18 | #include <sound/hda_i915.h> |
19 | #include <sound/hda_codec.h> |
20 | #include <sound/hda_register.h> |
21 | |
22 | #include "hdac_hda.h" |
23 | |
24 | #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ |
25 | SNDRV_PCM_FMTBIT_U8 | \ |
26 | SNDRV_PCM_FMTBIT_S16_LE | \ |
27 | SNDRV_PCM_FMTBIT_U16_LE | \ |
28 | SNDRV_PCM_FMTBIT_S24_LE | \ |
29 | SNDRV_PCM_FMTBIT_U24_LE | \ |
30 | SNDRV_PCM_FMTBIT_S32_LE | \ |
31 | SNDRV_PCM_FMTBIT_U32_LE | \ |
32 | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) |
33 | |
34 | #define STUB_HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ |
35 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ |
36 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ |
37 | SNDRV_PCM_RATE_192000) |
38 | |
39 | #ifdef CONFIG_SND_HDA_PATCH_LOADER |
40 | static char *loadable_patch[HDA_MAX_CODECS]; |
41 | |
42 | module_param_array_named(patch, loadable_patch, charp, NULL, 0444); |
43 | MODULE_PARM_DESC(patch, "Patch file array for Intel HD audio interface. The array index is the codec address." ); |
44 | #endif |
45 | |
46 | static int hdac_hda_dai_open(struct snd_pcm_substream *substream, |
47 | struct snd_soc_dai *dai); |
48 | static void hdac_hda_dai_close(struct snd_pcm_substream *substream, |
49 | struct snd_soc_dai *dai); |
50 | static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream, |
51 | struct snd_soc_dai *dai); |
52 | static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream, |
53 | struct snd_pcm_hw_params *params, |
54 | struct snd_soc_dai *dai); |
55 | static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream, |
56 | struct snd_soc_dai *dai); |
57 | static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai, void *stream, |
58 | int direction); |
59 | static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, |
60 | struct snd_soc_dai *dai); |
61 | |
62 | static const struct snd_soc_dai_ops hdac_hda_dai_ops = { |
63 | .startup = hdac_hda_dai_open, |
64 | .shutdown = hdac_hda_dai_close, |
65 | .prepare = hdac_hda_dai_prepare, |
66 | .hw_params = hdac_hda_dai_hw_params, |
67 | .hw_free = hdac_hda_dai_hw_free, |
68 | .set_stream = hdac_hda_dai_set_stream, |
69 | }; |
70 | |
71 | static struct snd_soc_dai_driver hdac_hda_dais[] = { |
72 | { |
73 | .id = HDAC_ANALOG_DAI_ID, |
74 | .name = "Analog Codec DAI" , |
75 | .ops = &hdac_hda_dai_ops, |
76 | .playback = { |
77 | .stream_name = "Analog Codec Playback" , |
78 | .channels_min = 1, |
79 | .channels_max = 16, |
80 | .rates = SNDRV_PCM_RATE_8000_192000, |
81 | .formats = STUB_FORMATS, |
82 | .sig_bits = 24, |
83 | }, |
84 | .capture = { |
85 | .stream_name = "Analog Codec Capture" , |
86 | .channels_min = 1, |
87 | .channels_max = 16, |
88 | .rates = SNDRV_PCM_RATE_8000_192000, |
89 | .formats = STUB_FORMATS, |
90 | .sig_bits = 24, |
91 | }, |
92 | }, |
93 | { |
94 | .id = HDAC_DIGITAL_DAI_ID, |
95 | .name = "Digital Codec DAI" , |
96 | .ops = &hdac_hda_dai_ops, |
97 | .playback = { |
98 | .stream_name = "Digital Codec Playback" , |
99 | .channels_min = 1, |
100 | .channels_max = 16, |
101 | .rates = SNDRV_PCM_RATE_8000_192000, |
102 | .formats = STUB_FORMATS, |
103 | .sig_bits = 24, |
104 | }, |
105 | .capture = { |
106 | .stream_name = "Digital Codec Capture" , |
107 | .channels_min = 1, |
108 | .channels_max = 16, |
109 | .rates = SNDRV_PCM_RATE_8000_192000, |
110 | .formats = STUB_FORMATS, |
111 | .sig_bits = 24, |
112 | }, |
113 | }, |
114 | { |
115 | .id = HDAC_ALT_ANALOG_DAI_ID, |
116 | .name = "Alt Analog Codec DAI" , |
117 | .ops = &hdac_hda_dai_ops, |
118 | .playback = { |
119 | .stream_name = "Alt Analog Codec Playback" , |
120 | .channels_min = 1, |
121 | .channels_max = 16, |
122 | .rates = SNDRV_PCM_RATE_8000_192000, |
123 | .formats = STUB_FORMATS, |
124 | .sig_bits = 24, |
125 | }, |
126 | .capture = { |
127 | .stream_name = "Alt Analog Codec Capture" , |
128 | .channels_min = 1, |
129 | .channels_max = 16, |
130 | .rates = SNDRV_PCM_RATE_8000_192000, |
131 | .formats = STUB_FORMATS, |
132 | .sig_bits = 24, |
133 | }, |
134 | }, |
135 | }; |
136 | |
137 | static struct snd_soc_dai_driver hdac_hda_hdmi_dais[] = { |
138 | { |
139 | .id = HDAC_HDMI_0_DAI_ID, |
140 | .name = "intel-hdmi-hifi1" , |
141 | .ops = &hdac_hda_dai_ops, |
142 | .playback = { |
143 | .stream_name = "hifi1" , |
144 | .channels_min = 1, |
145 | .channels_max = 32, |
146 | .rates = STUB_HDMI_RATES, |
147 | .formats = STUB_FORMATS, |
148 | .sig_bits = 24, |
149 | }, |
150 | }, |
151 | { |
152 | .id = HDAC_HDMI_1_DAI_ID, |
153 | .name = "intel-hdmi-hifi2" , |
154 | .ops = &hdac_hda_dai_ops, |
155 | .playback = { |
156 | .stream_name = "hifi2" , |
157 | .channels_min = 1, |
158 | .channels_max = 32, |
159 | .rates = STUB_HDMI_RATES, |
160 | .formats = STUB_FORMATS, |
161 | .sig_bits = 24, |
162 | }, |
163 | }, |
164 | { |
165 | .id = HDAC_HDMI_2_DAI_ID, |
166 | .name = "intel-hdmi-hifi3" , |
167 | .ops = &hdac_hda_dai_ops, |
168 | .playback = { |
169 | .stream_name = "hifi3" , |
170 | .channels_min = 1, |
171 | .channels_max = 32, |
172 | .rates = STUB_HDMI_RATES, |
173 | .formats = STUB_FORMATS, |
174 | .sig_bits = 24, |
175 | }, |
176 | }, |
177 | { |
178 | .id = HDAC_HDMI_3_DAI_ID, |
179 | .name = "intel-hdmi-hifi4" , |
180 | .ops = &hdac_hda_dai_ops, |
181 | .playback = { |
182 | .stream_name = "hifi4" , |
183 | .channels_min = 1, |
184 | .channels_max = 32, |
185 | .rates = STUB_HDMI_RATES, |
186 | .formats = STUB_FORMATS, |
187 | .sig_bits = 24, |
188 | }, |
189 | }, |
190 | |
191 | }; |
192 | |
193 | static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai, |
194 | void *stream, int direction) |
195 | { |
196 | struct snd_soc_component *component = dai->component; |
197 | struct hdac_hda_priv *hda_pvt; |
198 | struct hdac_hda_pcm *pcm; |
199 | struct hdac_stream *hstream; |
200 | |
201 | if (!stream) |
202 | return -EINVAL; |
203 | |
204 | hda_pvt = snd_soc_component_get_drvdata(c: component); |
205 | pcm = &hda_pvt->pcm[dai->id]; |
206 | hstream = (struct hdac_stream *)stream; |
207 | |
208 | pcm->stream_tag[direction] = hstream->stream_tag; |
209 | |
210 | return 0; |
211 | } |
212 | |
213 | static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream, |
214 | struct snd_pcm_hw_params *params, |
215 | struct snd_soc_dai *dai) |
216 | { |
217 | struct snd_soc_component *component = dai->component; |
218 | struct hdac_hda_priv *hda_pvt; |
219 | unsigned int format_val; |
220 | unsigned int maxbps; |
221 | unsigned int bits; |
222 | |
223 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
224 | maxbps = dai->driver->playback.sig_bits; |
225 | else |
226 | maxbps = dai->driver->capture.sig_bits; |
227 | bits = snd_hdac_stream_format_bits(format: params_format(p: params), SNDRV_PCM_SUBFORMAT_STD, maxbits: maxbps); |
228 | |
229 | hda_pvt = snd_soc_component_get_drvdata(c: component); |
230 | format_val = snd_hdac_stream_format(channels: params_channels(p: params), bits, rate: params_rate(p: params)); |
231 | if (!format_val) { |
232 | dev_err(dai->dev, |
233 | "invalid format_val, rate=%d, ch=%d, format=%d, maxbps=%d\n" , |
234 | params_rate(params), params_channels(params), |
235 | params_format(params), maxbps); |
236 | |
237 | return -EINVAL; |
238 | } |
239 | |
240 | hda_pvt->pcm[dai->id].format_val[substream->stream] = format_val; |
241 | return 0; |
242 | } |
243 | |
244 | static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream, |
245 | struct snd_soc_dai *dai) |
246 | { |
247 | struct snd_soc_component *component = dai->component; |
248 | struct hdac_hda_priv *hda_pvt; |
249 | struct hda_pcm_stream *hda_stream; |
250 | struct hda_pcm *pcm; |
251 | |
252 | hda_pvt = snd_soc_component_get_drvdata(c: component); |
253 | pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); |
254 | if (!pcm) |
255 | return -EINVAL; |
256 | |
257 | hda_stream = &pcm->stream[substream->stream]; |
258 | snd_hda_codec_cleanup(codec: hda_pvt->codec, hinfo: hda_stream, substream); |
259 | |
260 | return 0; |
261 | } |
262 | |
263 | static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream, |
264 | struct snd_soc_dai *dai) |
265 | { |
266 | struct snd_soc_component *component = dai->component; |
267 | struct hda_pcm_stream *hda_stream; |
268 | struct hdac_hda_priv *hda_pvt; |
269 | struct hdac_device *hdev; |
270 | unsigned int format_val; |
271 | struct hda_pcm *pcm; |
272 | unsigned int stream; |
273 | int ret = 0; |
274 | |
275 | hda_pvt = snd_soc_component_get_drvdata(c: component); |
276 | hdev = &hda_pvt->codec->core; |
277 | pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); |
278 | if (!pcm) |
279 | return -EINVAL; |
280 | |
281 | hda_stream = &pcm->stream[substream->stream]; |
282 | |
283 | stream = hda_pvt->pcm[dai->id].stream_tag[substream->stream]; |
284 | format_val = hda_pvt->pcm[dai->id].format_val[substream->stream]; |
285 | |
286 | ret = snd_hda_codec_prepare(codec: hda_pvt->codec, hinfo: hda_stream, |
287 | stream, format: format_val, substream); |
288 | if (ret < 0) |
289 | dev_err(&hdev->dev, "codec prepare failed %d\n" , ret); |
290 | |
291 | return ret; |
292 | } |
293 | |
294 | static int hdac_hda_dai_open(struct snd_pcm_substream *substream, |
295 | struct snd_soc_dai *dai) |
296 | { |
297 | struct snd_soc_component *component = dai->component; |
298 | struct hdac_hda_priv *hda_pvt; |
299 | struct hda_pcm_stream *hda_stream; |
300 | struct hda_pcm *pcm; |
301 | |
302 | hda_pvt = snd_soc_component_get_drvdata(c: component); |
303 | pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); |
304 | if (!pcm) |
305 | return -EINVAL; |
306 | |
307 | snd_hda_codec_pcm_get(pcm); |
308 | |
309 | hda_stream = &pcm->stream[substream->stream]; |
310 | |
311 | return hda_stream->ops.open(hda_stream, hda_pvt->codec, substream); |
312 | } |
313 | |
314 | static void hdac_hda_dai_close(struct snd_pcm_substream *substream, |
315 | struct snd_soc_dai *dai) |
316 | { |
317 | struct snd_soc_component *component = dai->component; |
318 | struct hdac_hda_priv *hda_pvt; |
319 | struct hda_pcm_stream *hda_stream; |
320 | struct hda_pcm *pcm; |
321 | |
322 | hda_pvt = snd_soc_component_get_drvdata(c: component); |
323 | pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); |
324 | if (!pcm) |
325 | return; |
326 | |
327 | hda_stream = &pcm->stream[substream->stream]; |
328 | |
329 | hda_stream->ops.close(hda_stream, hda_pvt->codec, substream); |
330 | |
331 | snd_hda_codec_pcm_put(pcm); |
332 | } |
333 | |
334 | static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, |
335 | struct snd_soc_dai *dai) |
336 | { |
337 | struct hda_codec *hcodec = hda_pvt->codec; |
338 | struct hda_pcm *cpcm; |
339 | const char *pcm_name; |
340 | |
341 | /* |
342 | * map DAI ID to the closest matching PCM name, using the naming |
343 | * scheme used by hda-codec snd_hda_gen_build_pcms() and for |
344 | * HDMI in hda_codec patch_hdmi.c) |
345 | */ |
346 | |
347 | switch (dai->id) { |
348 | case HDAC_ANALOG_DAI_ID: |
349 | pcm_name = "Analog" ; |
350 | break; |
351 | case HDAC_DIGITAL_DAI_ID: |
352 | pcm_name = "Digital" ; |
353 | break; |
354 | case HDAC_ALT_ANALOG_DAI_ID: |
355 | pcm_name = "Alt Analog" ; |
356 | break; |
357 | case HDAC_HDMI_0_DAI_ID: |
358 | pcm_name = "HDMI 0" ; |
359 | break; |
360 | case HDAC_HDMI_1_DAI_ID: |
361 | pcm_name = "HDMI 1" ; |
362 | break; |
363 | case HDAC_HDMI_2_DAI_ID: |
364 | pcm_name = "HDMI 2" ; |
365 | break; |
366 | case HDAC_HDMI_3_DAI_ID: |
367 | pcm_name = "HDMI 3" ; |
368 | break; |
369 | default: |
370 | dev_err(&hcodec->core.dev, "invalid dai id %d\n" , dai->id); |
371 | return NULL; |
372 | } |
373 | |
374 | list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) { |
375 | if (strstr(cpcm->name, pcm_name)) { |
376 | if (strcmp(pcm_name, "Analog" ) == 0) { |
377 | if (strstr(cpcm->name, "Alt Analog" )) |
378 | continue; |
379 | } |
380 | return cpcm; |
381 | } |
382 | } |
383 | |
384 | dev_err(&hcodec->core.dev, "didn't find PCM for DAI %s\n" , dai->name); |
385 | return NULL; |
386 | } |
387 | |
388 | static bool is_hdmi_codec(struct hda_codec *hcodec) |
389 | { |
390 | struct hda_pcm *cpcm; |
391 | |
392 | list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) { |
393 | if (cpcm->pcm_type == HDA_PCM_TYPE_HDMI) |
394 | return true; |
395 | } |
396 | |
397 | return false; |
398 | } |
399 | |
400 | static int hdac_hda_codec_probe(struct snd_soc_component *component) |
401 | { |
402 | struct hdac_hda_priv *hda_pvt = |
403 | snd_soc_component_get_drvdata(c: component); |
404 | struct snd_soc_dapm_context *dapm = |
405 | snd_soc_component_get_dapm(component); |
406 | struct hdac_device *hdev = &hda_pvt->codec->core; |
407 | struct hda_codec *hcodec = hda_pvt->codec; |
408 | struct hdac_ext_link *hlink; |
409 | hda_codec_patch_t patch; |
410 | int ret; |
411 | |
412 | hlink = snd_hdac_ext_bus_get_hlink_by_name(bus: hdev->bus, codec_name: dev_name(dev: &hdev->dev)); |
413 | if (!hlink) { |
414 | dev_err(&hdev->dev, "hdac link not found\n" ); |
415 | return -EIO; |
416 | } |
417 | |
418 | snd_hdac_ext_bus_link_get(bus: hdev->bus, hlink); |
419 | |
420 | /* |
421 | * Ensure any HDA display is powered at codec probe. |
422 | * After snd_hda_codec_device_new(), display power is |
423 | * managed by runtime PM. |
424 | */ |
425 | if (hda_pvt->need_display_power) |
426 | snd_hdac_display_power(bus: hdev->bus, |
427 | HDA_CODEC_IDX_CONTROLLER, enable: true); |
428 | |
429 | ret = snd_hda_codec_device_new(bus: hcodec->bus, card: component->card->snd_card, |
430 | codec_addr: hdev->addr, codec: hcodec, snddev_managed: true); |
431 | if (ret < 0) { |
432 | dev_err(&hdev->dev, "failed to create hda codec %d\n" , ret); |
433 | goto error_no_pm; |
434 | } |
435 | |
436 | #ifdef CONFIG_SND_HDA_PATCH_LOADER |
437 | if (loadable_patch[hda_pvt->dev_index] && *loadable_patch[hda_pvt->dev_index]) { |
438 | const struct firmware *fw; |
439 | |
440 | dev_info(&hdev->dev, "Applying patch firmware '%s'\n" , |
441 | loadable_patch[hda_pvt->dev_index]); |
442 | ret = request_firmware(fw: &fw, name: loadable_patch[hda_pvt->dev_index], |
443 | device: &hdev->dev); |
444 | if (ret < 0) |
445 | goto error_no_pm; |
446 | if (fw) { |
447 | ret = snd_hda_load_patch(bus: hcodec->bus, size: fw->size, buf: fw->data); |
448 | if (ret < 0) { |
449 | dev_err(&hdev->dev, "failed to load hda patch %d\n" , ret); |
450 | goto error_no_pm; |
451 | } |
452 | release_firmware(fw); |
453 | } |
454 | } |
455 | #endif |
456 | /* |
457 | * Overwrite type to HDA_DEV_ASOC since it is a ASoC driver |
458 | * hda_codec.c will check this flag to determine if unregister |
459 | * device is needed. |
460 | */ |
461 | hdev->type = HDA_DEV_ASOC; |
462 | |
463 | /* |
464 | * snd_hda_codec_device_new decrements the usage count so call get pm |
465 | * else the device will be powered off |
466 | */ |
467 | pm_runtime_get_noresume(dev: &hdev->dev); |
468 | |
469 | hcodec->bus->card = dapm->card->snd_card; |
470 | |
471 | ret = snd_hda_codec_set_name(codec: hcodec, name: hcodec->preset->name); |
472 | if (ret < 0) { |
473 | dev_err(&hdev->dev, "name failed %s\n" , hcodec->preset->name); |
474 | goto error_pm; |
475 | } |
476 | |
477 | ret = snd_hdac_regmap_init(codec: &hcodec->core); |
478 | if (ret < 0) { |
479 | dev_err(&hdev->dev, "regmap init failed\n" ); |
480 | goto error_pm; |
481 | } |
482 | |
483 | patch = (hda_codec_patch_t)hcodec->preset->driver_data; |
484 | if (patch) { |
485 | ret = patch(hcodec); |
486 | if (ret < 0) { |
487 | dev_err(&hdev->dev, "patch failed %d\n" , ret); |
488 | goto error_regmap; |
489 | } |
490 | } else { |
491 | dev_dbg(&hdev->dev, "no patch file found\n" ); |
492 | } |
493 | |
494 | ret = snd_hda_codec_parse_pcms(codec: hcodec); |
495 | if (ret < 0) { |
496 | dev_err(&hdev->dev, "unable to map pcms to dai %d\n" , ret); |
497 | goto error_patch; |
498 | } |
499 | |
500 | /* HDMI controls need to be created in machine drivers */ |
501 | if (!is_hdmi_codec(hcodec)) { |
502 | ret = snd_hda_codec_build_controls(codec: hcodec); |
503 | if (ret < 0) { |
504 | dev_err(&hdev->dev, "unable to create controls %d\n" , |
505 | ret); |
506 | goto error_patch; |
507 | } |
508 | } |
509 | |
510 | hcodec->core.lazy_cache = true; |
511 | |
512 | if (hda_pvt->need_display_power) |
513 | snd_hdac_display_power(bus: hdev->bus, |
514 | HDA_CODEC_IDX_CONTROLLER, enable: false); |
515 | |
516 | /* match for forbid call in snd_hda_codec_device_new() */ |
517 | pm_runtime_allow(dev: &hdev->dev); |
518 | |
519 | /* |
520 | * hdac_device core already sets the state to active and calls |
521 | * get_noresume. So enable runtime and set the device to suspend. |
522 | * pm_runtime_enable is also called during codec registeration |
523 | */ |
524 | pm_runtime_put(dev: &hdev->dev); |
525 | pm_runtime_suspend(dev: &hdev->dev); |
526 | |
527 | return 0; |
528 | |
529 | error_patch: |
530 | if (hcodec->patch_ops.free) |
531 | hcodec->patch_ops.free(hcodec); |
532 | error_regmap: |
533 | snd_hdac_regmap_exit(codec: hdev); |
534 | error_pm: |
535 | pm_runtime_put(dev: &hdev->dev); |
536 | error_no_pm: |
537 | snd_hdac_ext_bus_link_put(bus: hdev->bus, hlink); |
538 | return ret; |
539 | } |
540 | |
541 | static void hdac_hda_codec_remove(struct snd_soc_component *component) |
542 | { |
543 | struct hdac_hda_priv *hda_pvt = |
544 | snd_soc_component_get_drvdata(c: component); |
545 | struct hdac_device *hdev = &hda_pvt->codec->core; |
546 | struct hda_codec *codec = hda_pvt->codec; |
547 | struct hdac_ext_link *hlink = NULL; |
548 | |
549 | hlink = snd_hdac_ext_bus_get_hlink_by_name(bus: hdev->bus, codec_name: dev_name(dev: &hdev->dev)); |
550 | if (!hlink) { |
551 | dev_err(&hdev->dev, "hdac link not found\n" ); |
552 | return; |
553 | } |
554 | |
555 | pm_runtime_disable(dev: &hdev->dev); |
556 | snd_hdac_ext_bus_link_put(bus: hdev->bus, hlink); |
557 | |
558 | if (codec->patch_ops.free) |
559 | codec->patch_ops.free(codec); |
560 | |
561 | snd_hda_codec_cleanup_for_unbind(codec); |
562 | } |
563 | |
564 | static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = { |
565 | {"AIF1TX" , NULL, "Codec Input Pin1" }, |
566 | {"AIF2TX" , NULL, "Codec Input Pin2" }, |
567 | {"AIF3TX" , NULL, "Codec Input Pin3" }, |
568 | |
569 | {"Codec Output Pin1" , NULL, "AIF1RX" }, |
570 | {"Codec Output Pin2" , NULL, "AIF2RX" }, |
571 | {"Codec Output Pin3" , NULL, "AIF3RX" }, |
572 | }; |
573 | |
574 | static const struct snd_soc_dapm_widget hdac_hda_dapm_widgets[] = { |
575 | /* Audio Interface */ |
576 | SND_SOC_DAPM_AIF_IN("AIF1RX" , "Analog Codec Playback" , 0, |
577 | SND_SOC_NOPM, 0, 0), |
578 | SND_SOC_DAPM_AIF_IN("AIF2RX" , "Digital Codec Playback" , 0, |
579 | SND_SOC_NOPM, 0, 0), |
580 | SND_SOC_DAPM_AIF_IN("AIF3RX" , "Alt Analog Codec Playback" , 0, |
581 | SND_SOC_NOPM, 0, 0), |
582 | SND_SOC_DAPM_AIF_OUT("AIF1TX" , "Analog Codec Capture" , 0, |
583 | SND_SOC_NOPM, 0, 0), |
584 | SND_SOC_DAPM_AIF_OUT("AIF2TX" , "Digital Codec Capture" , 0, |
585 | SND_SOC_NOPM, 0, 0), |
586 | SND_SOC_DAPM_AIF_OUT("AIF3TX" , "Alt Analog Codec Capture" , 0, |
587 | SND_SOC_NOPM, 0, 0), |
588 | |
589 | /* Input Pins */ |
590 | SND_SOC_DAPM_INPUT("Codec Input Pin1" ), |
591 | SND_SOC_DAPM_INPUT("Codec Input Pin2" ), |
592 | SND_SOC_DAPM_INPUT("Codec Input Pin3" ), |
593 | |
594 | /* Output Pins */ |
595 | SND_SOC_DAPM_OUTPUT("Codec Output Pin1" ), |
596 | SND_SOC_DAPM_OUTPUT("Codec Output Pin2" ), |
597 | SND_SOC_DAPM_OUTPUT("Codec Output Pin3" ), |
598 | }; |
599 | |
600 | static const struct snd_soc_component_driver hdac_hda_codec = { |
601 | .probe = hdac_hda_codec_probe, |
602 | .remove = hdac_hda_codec_remove, |
603 | .dapm_widgets = hdac_hda_dapm_widgets, |
604 | .num_dapm_widgets = ARRAY_SIZE(hdac_hda_dapm_widgets), |
605 | .dapm_routes = hdac_hda_dapm_routes, |
606 | .num_dapm_routes = ARRAY_SIZE(hdac_hda_dapm_routes), |
607 | .idle_bias_on = false, |
608 | .endianness = 1, |
609 | }; |
610 | |
611 | static const struct snd_soc_component_driver hdac_hda_hdmi_codec = { |
612 | .probe = hdac_hda_codec_probe, |
613 | .remove = hdac_hda_codec_remove, |
614 | .idle_bias_on = false, |
615 | .endianness = 1, |
616 | }; |
617 | |
618 | static int hdac_hda_dev_probe(struct hdac_device *hdev) |
619 | { |
620 | struct hdac_hda_priv *hda_pvt = dev_get_drvdata(dev: &hdev->dev); |
621 | struct hdac_ext_link *hlink; |
622 | int ret; |
623 | |
624 | /* hold the ref while we probe */ |
625 | hlink = snd_hdac_ext_bus_get_hlink_by_name(bus: hdev->bus, codec_name: dev_name(dev: &hdev->dev)); |
626 | if (!hlink) { |
627 | dev_err(&hdev->dev, "hdac link not found\n" ); |
628 | return -EIO; |
629 | } |
630 | snd_hdac_ext_bus_link_get(bus: hdev->bus, hlink); |
631 | |
632 | /* ASoC specific initialization */ |
633 | if (hda_pvt->need_display_power) |
634 | ret = devm_snd_soc_register_component(dev: &hdev->dev, |
635 | component_driver: &hdac_hda_hdmi_codec, dai_drv: hdac_hda_hdmi_dais, |
636 | ARRAY_SIZE(hdac_hda_hdmi_dais)); |
637 | else |
638 | ret = devm_snd_soc_register_component(dev: &hdev->dev, |
639 | component_driver: &hdac_hda_codec, dai_drv: hdac_hda_dais, |
640 | ARRAY_SIZE(hdac_hda_dais)); |
641 | |
642 | if (ret < 0) { |
643 | dev_err(&hdev->dev, "failed to register HDA codec %d\n" , ret); |
644 | return ret; |
645 | } |
646 | |
647 | snd_hdac_ext_bus_link_put(bus: hdev->bus, hlink); |
648 | |
649 | return ret; |
650 | } |
651 | |
652 | static int hdac_hda_dev_remove(struct hdac_device *hdev) |
653 | { |
654 | /* |
655 | * Resources are freed in hdac_hda_codec_remove(). This |
656 | * function is kept to keep hda_codec_driver_remove() happy. |
657 | */ |
658 | return 0; |
659 | } |
660 | |
661 | static struct hdac_ext_bus_ops hdac_ops = { |
662 | .hdev_attach = hdac_hda_dev_probe, |
663 | .hdev_detach = hdac_hda_dev_remove, |
664 | }; |
665 | |
666 | struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void) |
667 | { |
668 | return &hdac_ops; |
669 | } |
670 | EXPORT_SYMBOL_GPL(snd_soc_hdac_hda_get_ops); |
671 | |
672 | MODULE_LICENSE("GPL v2" ); |
673 | MODULE_DESCRIPTION("ASoC Extensions for legacy HDA Drivers" ); |
674 | MODULE_AUTHOR("Rakesh Ughreja<rakesh.a.ughreja@intel.com>" ); |
675 | |