1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Copyright(c) 2021-2022 Intel Corporation. All rights reserved. |
4 | // |
5 | // Author: Cezary Rojewski <cezary.rojewski@intel.com> |
6 | // |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/pm_runtime.h> |
10 | #include <sound/soc.h> |
11 | #include <sound/hdaudio_ext.h> |
12 | #include <sound/hda_i915.h> |
13 | #include <sound/hda_codec.h> |
14 | #include "hda.h" |
15 | |
16 | static int hda_codec_create_dais(struct hda_codec *codec, int pcm_count, |
17 | struct snd_soc_dai_driver **drivers) |
18 | { |
19 | struct device *dev = &codec->core.dev; |
20 | struct snd_soc_dai_driver *drvs; |
21 | struct hda_pcm *pcm; |
22 | int i; |
23 | |
24 | drvs = devm_kcalloc(dev, n: pcm_count, size: sizeof(*drvs), GFP_KERNEL); |
25 | if (!drvs) |
26 | return -ENOMEM; |
27 | |
28 | pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list); |
29 | |
30 | for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) { |
31 | struct snd_soc_pcm_stream *stream; |
32 | int dir; |
33 | |
34 | dev_info(dev, "creating for %s %d\n" , pcm->name, i); |
35 | drvs[i].id = i; |
36 | drvs[i].name = pcm->name; |
37 | drvs[i].ops = &snd_soc_hda_codec_dai_ops; |
38 | |
39 | dir = SNDRV_PCM_STREAM_PLAYBACK; |
40 | stream = &drvs[i].playback; |
41 | if (!pcm->stream[dir].substreams) { |
42 | dev_info(dev, "skipping playback dai for %s\n" , pcm->name); |
43 | goto capture_dais; |
44 | } |
45 | |
46 | stream->stream_name = |
47 | devm_kasprintf(dev, GFP_KERNEL, fmt: "%s %s" , pcm->name, |
48 | snd_pcm_direction_name(direction: dir)); |
49 | if (!stream->stream_name) |
50 | return -ENOMEM; |
51 | stream->channels_min = pcm->stream[dir].channels_min; |
52 | stream->channels_max = pcm->stream[dir].channels_max; |
53 | stream->rates = pcm->stream[dir].rates; |
54 | stream->formats = pcm->stream[dir].formats; |
55 | stream->subformats = pcm->stream[dir].subformats; |
56 | stream->sig_bits = pcm->stream[dir].maxbps; |
57 | |
58 | capture_dais: |
59 | dir = SNDRV_PCM_STREAM_CAPTURE; |
60 | stream = &drvs[i].capture; |
61 | if (!pcm->stream[dir].substreams) { |
62 | dev_info(dev, "skipping capture dai for %s\n" , pcm->name); |
63 | continue; |
64 | } |
65 | |
66 | stream->stream_name = |
67 | devm_kasprintf(dev, GFP_KERNEL, fmt: "%s %s" , pcm->name, |
68 | snd_pcm_direction_name(direction: dir)); |
69 | if (!stream->stream_name) |
70 | return -ENOMEM; |
71 | stream->channels_min = pcm->stream[dir].channels_min; |
72 | stream->channels_max = pcm->stream[dir].channels_max; |
73 | stream->rates = pcm->stream[dir].rates; |
74 | stream->formats = pcm->stream[dir].formats; |
75 | stream->subformats = pcm->stream[dir].subformats; |
76 | stream->sig_bits = pcm->stream[dir].maxbps; |
77 | } |
78 | |
79 | *drivers = drvs; |
80 | return 0; |
81 | } |
82 | |
83 | static int hda_codec_register_dais(struct hda_codec *codec, struct snd_soc_component *component) |
84 | { |
85 | struct snd_soc_dai_driver *drvs = NULL; |
86 | struct snd_soc_dapm_context *dapm; |
87 | struct hda_pcm *pcm; |
88 | int ret, pcm_count = 0; |
89 | |
90 | if (list_empty(head: &codec->pcm_list_head)) |
91 | return -EINVAL; |
92 | list_for_each_entry(pcm, &codec->pcm_list_head, list) |
93 | pcm_count++; |
94 | |
95 | ret = hda_codec_create_dais(codec, pcm_count, drivers: &drvs); |
96 | if (ret < 0) |
97 | return ret; |
98 | |
99 | dapm = snd_soc_component_get_dapm(component); |
100 | |
101 | list_for_each_entry(pcm, &codec->pcm_list_head, list) { |
102 | struct snd_soc_dai *dai; |
103 | |
104 | dai = snd_soc_register_dai(component, dai_drv: drvs, legacy_dai_naming: false); |
105 | if (!dai) { |
106 | dev_err(component->dev, "register dai for %s failed\n" , pcm->name); |
107 | return -EINVAL; |
108 | } |
109 | |
110 | ret = snd_soc_dapm_new_dai_widgets(dapm, dai); |
111 | if (ret < 0) { |
112 | dev_err(component->dev, "create widgets failed: %d\n" , ret); |
113 | snd_soc_unregister_dai(dai); |
114 | return ret; |
115 | } |
116 | |
117 | snd_soc_dai_init_dma_data(dai, playback: &pcm->stream[0], capture: &pcm->stream[1]); |
118 | drvs++; |
119 | } |
120 | |
121 | return 0; |
122 | } |
123 | |
124 | static void hda_codec_unregister_dais(struct hda_codec *codec, |
125 | struct snd_soc_component *component) |
126 | { |
127 | struct snd_soc_dai *dai, *save; |
128 | struct hda_pcm *pcm; |
129 | |
130 | for_each_component_dais_safe(component, dai, save) { |
131 | int stream; |
132 | |
133 | list_for_each_entry(pcm, &codec->pcm_list_head, list) { |
134 | if (strcmp(dai->driver->name, pcm->name)) |
135 | continue; |
136 | |
137 | for_each_pcm_streams(stream) |
138 | snd_soc_dapm_free_widget(w: snd_soc_dai_get_widget(dai, stream)); |
139 | |
140 | snd_soc_unregister_dai(dai); |
141 | break; |
142 | } |
143 | } |
144 | } |
145 | |
146 | int hda_codec_probe_complete(struct hda_codec *codec) |
147 | { |
148 | struct hdac_device *hdev = &codec->core; |
149 | struct hdac_bus *bus = hdev->bus; |
150 | int ret; |
151 | |
152 | ret = snd_hda_codec_build_controls(codec); |
153 | if (ret < 0) { |
154 | dev_err(&hdev->dev, "unable to create controls %d\n" , ret); |
155 | goto out; |
156 | } |
157 | |
158 | /* Bus suspended codecs as it does not manage their pm */ |
159 | pm_runtime_set_active(dev: &hdev->dev); |
160 | /* rpm was forbidden in snd_hda_codec_device_new() */ |
161 | snd_hda_codec_set_power_save(codec, delay: 2000); |
162 | snd_hda_codec_register(codec); |
163 | out: |
164 | /* Complement pm_runtime_get_sync(bus) in probe */ |
165 | pm_runtime_mark_last_busy(dev: bus->dev); |
166 | pm_runtime_put_autosuspend(dev: bus->dev); |
167 | |
168 | return ret; |
169 | } |
170 | EXPORT_SYMBOL_GPL(hda_codec_probe_complete); |
171 | |
172 | /* Expects codec with usage_count=1 and status=suspended */ |
173 | static int hda_codec_probe(struct snd_soc_component *component) |
174 | { |
175 | struct hda_codec *codec = dev_to_hda_codec(component->dev); |
176 | struct hdac_device *hdev = &codec->core; |
177 | struct hdac_bus *bus = hdev->bus; |
178 | struct hdac_ext_link *hlink; |
179 | hda_codec_patch_t patch; |
180 | int ret; |
181 | |
182 | #ifdef CONFIG_PM |
183 | WARN_ON(atomic_read(&hdev->dev.power.usage_count) != 1 || |
184 | !pm_runtime_status_suspended(&hdev->dev)); |
185 | #endif |
186 | |
187 | hlink = snd_hdac_ext_bus_get_hlink_by_addr(bus, addr: hdev->addr); |
188 | if (!hlink) { |
189 | dev_err(&hdev->dev, "hdac link not found\n" ); |
190 | return -EIO; |
191 | } |
192 | |
193 | pm_runtime_get_sync(dev: bus->dev); |
194 | if (hda_codec_is_display(codec)) |
195 | snd_hdac_display_power(bus, idx: hdev->addr, enable: true); |
196 | snd_hdac_ext_bus_link_get(bus, hlink); |
197 | |
198 | ret = snd_hda_codec_device_new(bus: codec->bus, card: component->card->snd_card, codec_addr: hdev->addr, codec, |
199 | snddev_managed: false); |
200 | if (ret < 0) { |
201 | dev_err(&hdev->dev, "codec create failed: %d\n" , ret); |
202 | goto device_new_err; |
203 | } |
204 | |
205 | ret = snd_hda_codec_set_name(codec, name: codec->preset->name); |
206 | if (ret < 0) { |
207 | dev_err(&hdev->dev, "set name: %s failed: %d\n" , codec->preset->name, ret); |
208 | goto err; |
209 | } |
210 | |
211 | ret = snd_hdac_regmap_init(codec: &codec->core); |
212 | if (ret < 0) { |
213 | dev_err(&hdev->dev, "regmap init failed: %d\n" , ret); |
214 | goto err; |
215 | } |
216 | |
217 | patch = (hda_codec_patch_t)codec->preset->driver_data; |
218 | if (!patch) { |
219 | dev_err(&hdev->dev, "no patch specified\n" ); |
220 | ret = -EINVAL; |
221 | goto err; |
222 | } |
223 | |
224 | ret = patch(codec); |
225 | if (ret < 0) { |
226 | dev_err(&hdev->dev, "codec init failed: %d\n" , ret); |
227 | goto err; |
228 | } |
229 | |
230 | ret = snd_hda_codec_parse_pcms(codec); |
231 | if (ret < 0) { |
232 | dev_err(&hdev->dev, "unable to map pcms to dai: %d\n" , ret); |
233 | goto parse_pcms_err; |
234 | } |
235 | |
236 | ret = hda_codec_register_dais(codec, component); |
237 | if (ret < 0) { |
238 | dev_err(&hdev->dev, "update dais failed: %d\n" , ret); |
239 | goto parse_pcms_err; |
240 | } |
241 | |
242 | if (!hda_codec_is_display(codec)) { |
243 | ret = hda_codec_probe_complete(codec); |
244 | if (ret < 0) |
245 | goto complete_err; |
246 | } |
247 | |
248 | codec->core.lazy_cache = true; |
249 | |
250 | return 0; |
251 | |
252 | complete_err: |
253 | hda_codec_unregister_dais(codec, component); |
254 | parse_pcms_err: |
255 | if (codec->patch_ops.free) |
256 | codec->patch_ops.free(codec); |
257 | err: |
258 | snd_hda_codec_cleanup_for_unbind(codec); |
259 | device_new_err: |
260 | if (hda_codec_is_display(codec)) |
261 | snd_hdac_display_power(bus, idx: hdev->addr, enable: false); |
262 | |
263 | snd_hdac_ext_bus_link_put(bus, hlink); |
264 | |
265 | pm_runtime_mark_last_busy(dev: bus->dev); |
266 | pm_runtime_put_autosuspend(dev: bus->dev); |
267 | return ret; |
268 | } |
269 | |
270 | /* Leaves codec with usage_count=1 and status=suspended */ |
271 | static void hda_codec_remove(struct snd_soc_component *component) |
272 | { |
273 | struct hda_codec *codec = dev_to_hda_codec(component->dev); |
274 | struct hdac_device *hdev = &codec->core; |
275 | struct hdac_bus *bus = hdev->bus; |
276 | struct hdac_ext_link *hlink; |
277 | bool was_registered = codec->core.registered; |
278 | |
279 | /* Don't allow any more runtime suspends */ |
280 | pm_runtime_forbid(dev: &hdev->dev); |
281 | |
282 | hda_codec_unregister_dais(codec, component); |
283 | |
284 | if (codec->patch_ops.free) |
285 | codec->patch_ops.free(codec); |
286 | |
287 | snd_hda_codec_cleanup_for_unbind(codec); |
288 | pm_runtime_put_noidle(dev: &hdev->dev); |
289 | /* snd_hdac_device_exit() is only called on bus remove */ |
290 | pm_runtime_set_suspended(dev: &hdev->dev); |
291 | |
292 | if (hda_codec_is_display(codec)) |
293 | snd_hdac_display_power(bus, idx: hdev->addr, enable: false); |
294 | |
295 | hlink = snd_hdac_ext_bus_get_hlink_by_addr(bus, addr: hdev->addr); |
296 | if (hlink) |
297 | snd_hdac_ext_bus_link_put(bus, hlink); |
298 | /* |
299 | * HDMI card's hda_codec_probe_complete() (see late_probe()) may |
300 | * not be called due to early error, leaving bus uc unbalanced |
301 | */ |
302 | if (!was_registered) { |
303 | pm_runtime_mark_last_busy(dev: bus->dev); |
304 | pm_runtime_put_autosuspend(dev: bus->dev); |
305 | } |
306 | |
307 | #ifdef CONFIG_PM |
308 | WARN_ON(atomic_read(&hdev->dev.power.usage_count) != 1 || |
309 | !pm_runtime_status_suspended(&hdev->dev)); |
310 | #endif |
311 | } |
312 | |
313 | static const struct snd_soc_dapm_route hda_dapm_routes[] = { |
314 | {"AIF1TX" , NULL, "Codec Input Pin1" }, |
315 | {"AIF2TX" , NULL, "Codec Input Pin2" }, |
316 | {"AIF3TX" , NULL, "Codec Input Pin3" }, |
317 | |
318 | {"Codec Output Pin1" , NULL, "AIF1RX" }, |
319 | {"Codec Output Pin2" , NULL, "AIF2RX" }, |
320 | {"Codec Output Pin3" , NULL, "AIF3RX" }, |
321 | }; |
322 | |
323 | static const struct snd_soc_dapm_widget hda_dapm_widgets[] = { |
324 | /* Audio Interface */ |
325 | SND_SOC_DAPM_AIF_IN("AIF1RX" , "Analog Codec Playback" , 0, SND_SOC_NOPM, 0, 0), |
326 | SND_SOC_DAPM_AIF_IN("AIF2RX" , "Digital Codec Playback" , 0, SND_SOC_NOPM, 0, 0), |
327 | SND_SOC_DAPM_AIF_IN("AIF3RX" , "Alt Analog Codec Playback" , 0, SND_SOC_NOPM, 0, 0), |
328 | SND_SOC_DAPM_AIF_OUT("AIF1TX" , "Analog Codec Capture" , 0, SND_SOC_NOPM, 0, 0), |
329 | SND_SOC_DAPM_AIF_OUT("AIF2TX" , "Digital Codec Capture" , 0, SND_SOC_NOPM, 0, 0), |
330 | SND_SOC_DAPM_AIF_OUT("AIF3TX" , "Alt Analog Codec Capture" , 0, SND_SOC_NOPM, 0, 0), |
331 | |
332 | /* Input Pins */ |
333 | SND_SOC_DAPM_INPUT("Codec Input Pin1" ), |
334 | SND_SOC_DAPM_INPUT("Codec Input Pin2" ), |
335 | SND_SOC_DAPM_INPUT("Codec Input Pin3" ), |
336 | |
337 | /* Output Pins */ |
338 | SND_SOC_DAPM_OUTPUT("Codec Output Pin1" ), |
339 | SND_SOC_DAPM_OUTPUT("Codec Output Pin2" ), |
340 | SND_SOC_DAPM_OUTPUT("Codec Output Pin3" ), |
341 | }; |
342 | |
343 | static struct snd_soc_dai_driver card_binder_dai = { |
344 | .id = -1, |
345 | .name = "codec-probing-DAI" , |
346 | }; |
347 | |
348 | static int hda_hdev_attach(struct hdac_device *hdev) |
349 | { |
350 | struct hda_codec *codec = dev_to_hda_codec(&hdev->dev); |
351 | struct snd_soc_component_driver *comp_drv; |
352 | |
353 | if (hda_codec_is_display(codec) && !hdev->bus->audio_component) { |
354 | dev_dbg(&hdev->dev, "no i915, skip registration for 0x%08x\n" , hdev->vendor_id); |
355 | return -ENODEV; |
356 | } |
357 | |
358 | comp_drv = devm_kzalloc(dev: &hdev->dev, size: sizeof(*comp_drv), GFP_KERNEL); |
359 | if (!comp_drv) |
360 | return -ENOMEM; |
361 | |
362 | /* |
363 | * It's save to rely on dev_name() rather than a copy as component |
364 | * driver's lifetime is directly tied to hda codec one |
365 | */ |
366 | comp_drv->name = dev_name(dev: &hdev->dev); |
367 | comp_drv->probe = hda_codec_probe; |
368 | comp_drv->remove = hda_codec_remove; |
369 | comp_drv->idle_bias_on = false; |
370 | if (!hda_codec_is_display(codec)) { |
371 | comp_drv->dapm_widgets = hda_dapm_widgets; |
372 | comp_drv->num_dapm_widgets = ARRAY_SIZE(hda_dapm_widgets); |
373 | comp_drv->dapm_routes = hda_dapm_routes; |
374 | comp_drv->num_dapm_routes = ARRAY_SIZE(hda_dapm_routes); |
375 | } |
376 | |
377 | return snd_soc_register_component(dev: &hdev->dev, component_driver: comp_drv, dai_drv: &card_binder_dai, num_dai: 1); |
378 | } |
379 | |
380 | static int hda_hdev_detach(struct hdac_device *hdev) |
381 | { |
382 | struct hda_codec *codec = dev_to_hda_codec(&hdev->dev); |
383 | |
384 | if (codec->core.registered) |
385 | cancel_delayed_work_sync(dwork: &codec->jackpoll_work); |
386 | |
387 | snd_soc_unregister_component(dev: &hdev->dev); |
388 | |
389 | return 0; |
390 | } |
391 | |
392 | const struct hdac_ext_bus_ops soc_hda_ext_bus_ops = { |
393 | .hdev_attach = hda_hdev_attach, |
394 | .hdev_detach = hda_hdev_detach, |
395 | }; |
396 | EXPORT_SYMBOL_GPL(soc_hda_ext_bus_ops); |
397 | |
398 | MODULE_DESCRIPTION("HD-Audio codec driver" ); |
399 | MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>" ); |
400 | MODULE_LICENSE("GPL" ); |
401 | |