]>
Commit | Line | Data |
---|---|---|
29b96bf5 AY |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * virtio-snd: Virtio sound device | |
4 | * Copyright (C) 2021 OpenSynergy GmbH | |
5 | */ | |
6 | #include <linux/moduleparam.h> | |
7 | #include <linux/virtio_config.h> | |
8 | ||
9 | #include "virtio_card.h" | |
10 | ||
11 | static u32 pcm_buffer_ms = 160; | |
12 | module_param(pcm_buffer_ms, uint, 0644); | |
13 | MODULE_PARM_DESC(pcm_buffer_ms, "PCM substream buffer time in milliseconds"); | |
14 | ||
15 | static u32 pcm_periods_min = 2; | |
16 | module_param(pcm_periods_min, uint, 0644); | |
17 | MODULE_PARM_DESC(pcm_periods_min, "Minimum number of PCM periods"); | |
18 | ||
19 | static u32 pcm_periods_max = 16; | |
20 | module_param(pcm_periods_max, uint, 0644); | |
21 | MODULE_PARM_DESC(pcm_periods_max, "Maximum number of PCM periods"); | |
22 | ||
23 | static u32 pcm_period_ms_min = 10; | |
24 | module_param(pcm_period_ms_min, uint, 0644); | |
25 | MODULE_PARM_DESC(pcm_period_ms_min, "Minimum PCM period time in milliseconds"); | |
26 | ||
27 | static u32 pcm_period_ms_max = 80; | |
28 | module_param(pcm_period_ms_max, uint, 0644); | |
29 | MODULE_PARM_DESC(pcm_period_ms_max, "Maximum PCM period time in milliseconds"); | |
30 | ||
31 | /* Map for converting VirtIO format to ALSA format. */ | |
32 | static const snd_pcm_format_t g_v2a_format_map[] = { | |
33 | [VIRTIO_SND_PCM_FMT_IMA_ADPCM] = SNDRV_PCM_FORMAT_IMA_ADPCM, | |
34 | [VIRTIO_SND_PCM_FMT_MU_LAW] = SNDRV_PCM_FORMAT_MU_LAW, | |
35 | [VIRTIO_SND_PCM_FMT_A_LAW] = SNDRV_PCM_FORMAT_A_LAW, | |
36 | [VIRTIO_SND_PCM_FMT_S8] = SNDRV_PCM_FORMAT_S8, | |
37 | [VIRTIO_SND_PCM_FMT_U8] = SNDRV_PCM_FORMAT_U8, | |
38 | [VIRTIO_SND_PCM_FMT_S16] = SNDRV_PCM_FORMAT_S16_LE, | |
39 | [VIRTIO_SND_PCM_FMT_U16] = SNDRV_PCM_FORMAT_U16_LE, | |
40 | [VIRTIO_SND_PCM_FMT_S18_3] = SNDRV_PCM_FORMAT_S18_3LE, | |
41 | [VIRTIO_SND_PCM_FMT_U18_3] = SNDRV_PCM_FORMAT_U18_3LE, | |
42 | [VIRTIO_SND_PCM_FMT_S20_3] = SNDRV_PCM_FORMAT_S20_3LE, | |
43 | [VIRTIO_SND_PCM_FMT_U20_3] = SNDRV_PCM_FORMAT_U20_3LE, | |
44 | [VIRTIO_SND_PCM_FMT_S24_3] = SNDRV_PCM_FORMAT_S24_3LE, | |
45 | [VIRTIO_SND_PCM_FMT_U24_3] = SNDRV_PCM_FORMAT_U24_3LE, | |
46 | [VIRTIO_SND_PCM_FMT_S20] = SNDRV_PCM_FORMAT_S20_LE, | |
47 | [VIRTIO_SND_PCM_FMT_U20] = SNDRV_PCM_FORMAT_U20_LE, | |
48 | [VIRTIO_SND_PCM_FMT_S24] = SNDRV_PCM_FORMAT_S24_LE, | |
49 | [VIRTIO_SND_PCM_FMT_U24] = SNDRV_PCM_FORMAT_U24_LE, | |
50 | [VIRTIO_SND_PCM_FMT_S32] = SNDRV_PCM_FORMAT_S32_LE, | |
51 | [VIRTIO_SND_PCM_FMT_U32] = SNDRV_PCM_FORMAT_U32_LE, | |
52 | [VIRTIO_SND_PCM_FMT_FLOAT] = SNDRV_PCM_FORMAT_FLOAT_LE, | |
53 | [VIRTIO_SND_PCM_FMT_FLOAT64] = SNDRV_PCM_FORMAT_FLOAT64_LE, | |
54 | [VIRTIO_SND_PCM_FMT_DSD_U8] = SNDRV_PCM_FORMAT_DSD_U8, | |
55 | [VIRTIO_SND_PCM_FMT_DSD_U16] = SNDRV_PCM_FORMAT_DSD_U16_LE, | |
56 | [VIRTIO_SND_PCM_FMT_DSD_U32] = SNDRV_PCM_FORMAT_DSD_U32_LE, | |
57 | [VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME] = | |
58 | SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE | |
59 | }; | |
60 | ||
61 | /* Map for converting VirtIO frame rate to ALSA frame rate. */ | |
62 | struct virtsnd_v2a_rate { | |
63 | unsigned int alsa_bit; | |
64 | unsigned int rate; | |
65 | }; | |
66 | ||
67 | static const struct virtsnd_v2a_rate g_v2a_rate_map[] = { | |
68 | [VIRTIO_SND_PCM_RATE_5512] = { SNDRV_PCM_RATE_5512, 5512 }, | |
69 | [VIRTIO_SND_PCM_RATE_8000] = { SNDRV_PCM_RATE_8000, 8000 }, | |
70 | [VIRTIO_SND_PCM_RATE_11025] = { SNDRV_PCM_RATE_11025, 11025 }, | |
71 | [VIRTIO_SND_PCM_RATE_16000] = { SNDRV_PCM_RATE_16000, 16000 }, | |
72 | [VIRTIO_SND_PCM_RATE_22050] = { SNDRV_PCM_RATE_22050, 22050 }, | |
73 | [VIRTIO_SND_PCM_RATE_32000] = { SNDRV_PCM_RATE_32000, 32000 }, | |
74 | [VIRTIO_SND_PCM_RATE_44100] = { SNDRV_PCM_RATE_44100, 44100 }, | |
75 | [VIRTIO_SND_PCM_RATE_48000] = { SNDRV_PCM_RATE_48000, 48000 }, | |
76 | [VIRTIO_SND_PCM_RATE_64000] = { SNDRV_PCM_RATE_64000, 64000 }, | |
77 | [VIRTIO_SND_PCM_RATE_88200] = { SNDRV_PCM_RATE_88200, 88200 }, | |
78 | [VIRTIO_SND_PCM_RATE_96000] = { SNDRV_PCM_RATE_96000, 96000 }, | |
79 | [VIRTIO_SND_PCM_RATE_176400] = { SNDRV_PCM_RATE_176400, 176400 }, | |
80 | [VIRTIO_SND_PCM_RATE_192000] = { SNDRV_PCM_RATE_192000, 192000 } | |
81 | }; | |
82 | ||
83 | /** | |
84 | * virtsnd_pcm_build_hw() - Parse substream config and build HW descriptor. | |
85 | * @vss: VirtIO substream. | |
86 | * @info: VirtIO substream information entry. | |
87 | * | |
88 | * Context: Any context. | |
89 | * Return: 0 on success, -EINVAL if configuration is invalid. | |
90 | */ | |
91 | static int virtsnd_pcm_build_hw(struct virtio_pcm_substream *vss, | |
92 | struct virtio_snd_pcm_info *info) | |
93 | { | |
94 | struct virtio_device *vdev = vss->snd->vdev; | |
95 | unsigned int i; | |
96 | u64 values; | |
97 | size_t sample_max = 0; | |
98 | size_t sample_min = 0; | |
99 | ||
100 | vss->features = le32_to_cpu(info->features); | |
101 | ||
102 | /* | |
103 | * TODO: set SNDRV_PCM_INFO_{BATCH,BLOCK_TRANSFER} if device supports | |
104 | * only message-based transport. | |
105 | */ | |
106 | vss->hw.info = | |
107 | SNDRV_PCM_INFO_MMAP | | |
108 | SNDRV_PCM_INFO_MMAP_VALID | | |
109 | SNDRV_PCM_INFO_BATCH | | |
110 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | |
111 | SNDRV_PCM_INFO_INTERLEAVED | | |
112 | SNDRV_PCM_INFO_PAUSE; | |
113 | ||
114 | if (!info->channels_min || info->channels_min > info->channels_max) { | |
115 | dev_err(&vdev->dev, | |
116 | "SID %u: invalid channel range [%u %u]\n", | |
117 | vss->sid, info->channels_min, info->channels_max); | |
118 | return -EINVAL; | |
119 | } | |
120 | ||
121 | vss->hw.channels_min = info->channels_min; | |
122 | vss->hw.channels_max = info->channels_max; | |
123 | ||
124 | values = le64_to_cpu(info->formats); | |
125 | ||
126 | vss->hw.formats = 0; | |
127 | ||
128 | for (i = 0; i < ARRAY_SIZE(g_v2a_format_map); ++i) | |
129 | if (values & (1ULL << i)) { | |
130 | snd_pcm_format_t alsa_fmt = g_v2a_format_map[i]; | |
131 | int bytes = snd_pcm_format_physical_width(alsa_fmt) / 8; | |
132 | ||
133 | if (!sample_min || sample_min > bytes) | |
134 | sample_min = bytes; | |
135 | ||
136 | if (sample_max < bytes) | |
137 | sample_max = bytes; | |
138 | ||
139 | vss->hw.formats |= pcm_format_to_bits(alsa_fmt); | |
140 | } | |
141 | ||
142 | if (!vss->hw.formats) { | |
143 | dev_err(&vdev->dev, | |
144 | "SID %u: no supported PCM sample formats found\n", | |
145 | vss->sid); | |
146 | return -EINVAL; | |
147 | } | |
148 | ||
149 | values = le64_to_cpu(info->rates); | |
150 | ||
151 | vss->hw.rates = 0; | |
152 | ||
153 | for (i = 0; i < ARRAY_SIZE(g_v2a_rate_map); ++i) | |
154 | if (values & (1ULL << i)) { | |
155 | if (!vss->hw.rate_min || | |
156 | vss->hw.rate_min > g_v2a_rate_map[i].rate) | |
157 | vss->hw.rate_min = g_v2a_rate_map[i].rate; | |
158 | ||
159 | if (vss->hw.rate_max < g_v2a_rate_map[i].rate) | |
160 | vss->hw.rate_max = g_v2a_rate_map[i].rate; | |
161 | ||
162 | vss->hw.rates |= g_v2a_rate_map[i].alsa_bit; | |
163 | } | |
164 | ||
165 | if (!vss->hw.rates) { | |
166 | dev_err(&vdev->dev, | |
167 | "SID %u: no supported PCM frame rates found\n", | |
168 | vss->sid); | |
169 | return -EINVAL; | |
170 | } | |
171 | ||
172 | vss->hw.periods_min = pcm_periods_min; | |
173 | vss->hw.periods_max = pcm_periods_max; | |
174 | ||
175 | /* | |
176 | * We must ensure that there is enough space in the buffer to store | |
177 | * pcm_buffer_ms ms for the combination (Cmax, Smax, Rmax), where: | |
178 | * Cmax = maximum supported number of channels, | |
179 | * Smax = maximum supported sample size in bytes, | |
180 | * Rmax = maximum supported frame rate. | |
181 | */ | |
182 | vss->hw.buffer_bytes_max = | |
183 | PAGE_ALIGN(sample_max * vss->hw.channels_max * pcm_buffer_ms * | |
184 | (vss->hw.rate_max / MSEC_PER_SEC)); | |
185 | ||
186 | /* | |
187 | * We must ensure that the minimum period size is enough to store | |
188 | * pcm_period_ms_min ms for the combination (Cmin, Smin, Rmin), where: | |
189 | * Cmin = minimum supported number of channels, | |
190 | * Smin = minimum supported sample size in bytes, | |
191 | * Rmin = minimum supported frame rate. | |
192 | */ | |
193 | vss->hw.period_bytes_min = | |
194 | sample_min * vss->hw.channels_min * pcm_period_ms_min * | |
195 | (vss->hw.rate_min / MSEC_PER_SEC); | |
196 | ||
197 | /* | |
198 | * We must ensure that the maximum period size is enough to store | |
199 | * pcm_period_ms_max ms for the combination (Cmax, Smax, Rmax). | |
200 | */ | |
201 | vss->hw.period_bytes_max = | |
202 | sample_max * vss->hw.channels_max * pcm_period_ms_max * | |
203 | (vss->hw.rate_max / MSEC_PER_SEC); | |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
208 | /** | |
209 | * virtsnd_pcm_find() - Find the PCM device for the specified node ID. | |
210 | * @snd: VirtIO sound device. | |
211 | * @nid: Function node ID. | |
212 | * | |
213 | * Context: Any context. | |
214 | * Return: a pointer to the PCM device or ERR_PTR(-ENOENT). | |
215 | */ | |
216 | struct virtio_pcm *virtsnd_pcm_find(struct virtio_snd *snd, u32 nid) | |
217 | { | |
218 | struct virtio_pcm *vpcm; | |
219 | ||
220 | list_for_each_entry(vpcm, &snd->pcm_list, list) | |
221 | if (vpcm->nid == nid) | |
222 | return vpcm; | |
223 | ||
224 | return ERR_PTR(-ENOENT); | |
225 | } | |
226 | ||
227 | /** | |
228 | * virtsnd_pcm_find_or_create() - Find or create the PCM device for the | |
229 | * specified node ID. | |
230 | * @snd: VirtIO sound device. | |
231 | * @nid: Function node ID. | |
232 | * | |
233 | * Context: Any context that permits to sleep. | |
234 | * Return: a pointer to the PCM device or ERR_PTR(-errno). | |
235 | */ | |
236 | struct virtio_pcm *virtsnd_pcm_find_or_create(struct virtio_snd *snd, u32 nid) | |
237 | { | |
238 | struct virtio_device *vdev = snd->vdev; | |
239 | struct virtio_pcm *vpcm; | |
240 | ||
241 | vpcm = virtsnd_pcm_find(snd, nid); | |
242 | if (!IS_ERR(vpcm)) | |
243 | return vpcm; | |
244 | ||
245 | vpcm = devm_kzalloc(&vdev->dev, sizeof(*vpcm), GFP_KERNEL); | |
246 | if (!vpcm) | |
247 | return ERR_PTR(-ENOMEM); | |
248 | ||
249 | vpcm->nid = nid; | |
250 | list_add_tail(&vpcm->list, &snd->pcm_list); | |
251 | ||
252 | return vpcm; | |
253 | } | |
254 | ||
255 | /** | |
256 | * virtsnd_pcm_validate() - Validate if the device can be started. | |
257 | * @vdev: VirtIO parent device. | |
258 | * | |
259 | * Context: Any context. | |
260 | * Return: 0 on success, -EINVAL on failure. | |
261 | */ | |
262 | int virtsnd_pcm_validate(struct virtio_device *vdev) | |
263 | { | |
264 | if (pcm_periods_min < 2 || pcm_periods_min > pcm_periods_max) { | |
265 | dev_err(&vdev->dev, | |
266 | "invalid range [%u %u] of the number of PCM periods\n", | |
267 | pcm_periods_min, pcm_periods_max); | |
268 | return -EINVAL; | |
269 | } | |
270 | ||
271 | if (!pcm_period_ms_min || pcm_period_ms_min > pcm_period_ms_max) { | |
272 | dev_err(&vdev->dev, | |
273 | "invalid range [%u %u] of the size of the PCM period\n", | |
274 | pcm_period_ms_min, pcm_period_ms_max); | |
275 | return -EINVAL; | |
276 | } | |
277 | ||
278 | if (pcm_buffer_ms < pcm_periods_min * pcm_period_ms_min) { | |
279 | dev_err(&vdev->dev, | |
280 | "pcm_buffer_ms(=%u) value cannot be < %u ms\n", | |
281 | pcm_buffer_ms, pcm_periods_min * pcm_period_ms_min); | |
282 | return -EINVAL; | |
283 | } | |
284 | ||
285 | if (pcm_period_ms_max > pcm_buffer_ms / 2) { | |
286 | dev_err(&vdev->dev, | |
287 | "pcm_period_ms_max(=%u) value cannot be > %u ms\n", | |
288 | pcm_period_ms_max, pcm_buffer_ms / 2); | |
289 | return -EINVAL; | |
290 | } | |
291 | ||
292 | return 0; | |
293 | } | |
294 | ||
295 | /** | |
296 | * virtsnd_pcm_period_elapsed() - Kernel work function to handle the elapsed | |
297 | * period state. | |
298 | * @work: Elapsed period work. | |
299 | * | |
300 | * The main purpose of this function is to call snd_pcm_period_elapsed() in | |
301 | * a process context, not in an interrupt context. This is necessary because PCM | |
302 | * devices operate in non-atomic mode. | |
303 | * | |
304 | * Context: Process context. | |
305 | */ | |
306 | static void virtsnd_pcm_period_elapsed(struct work_struct *work) | |
307 | { | |
308 | struct virtio_pcm_substream *vss = | |
309 | container_of(work, struct virtio_pcm_substream, elapsed_period); | |
310 | ||
311 | snd_pcm_period_elapsed(vss->substream); | |
312 | } | |
313 | ||
314 | /** | |
315 | * virtsnd_pcm_parse_cfg() - Parse the stream configuration. | |
316 | * @snd: VirtIO sound device. | |
317 | * | |
318 | * This function is called during initial device initialization. | |
319 | * | |
320 | * Context: Any context that permits to sleep. | |
321 | * Return: 0 on success, -errno on failure. | |
322 | */ | |
323 | int virtsnd_pcm_parse_cfg(struct virtio_snd *snd) | |
324 | { | |
325 | struct virtio_device *vdev = snd->vdev; | |
326 | struct virtio_snd_pcm_info *info; | |
327 | u32 i; | |
328 | int rc; | |
329 | ||
330 | virtio_cread_le(vdev, struct virtio_snd_config, streams, | |
331 | &snd->nsubstreams); | |
332 | if (!snd->nsubstreams) | |
333 | return 0; | |
334 | ||
335 | snd->substreams = devm_kcalloc(&vdev->dev, snd->nsubstreams, | |
336 | sizeof(*snd->substreams), GFP_KERNEL); | |
337 | if (!snd->substreams) | |
338 | return -ENOMEM; | |
339 | ||
340 | info = kcalloc(snd->nsubstreams, sizeof(*info), GFP_KERNEL); | |
341 | if (!info) | |
342 | return -ENOMEM; | |
343 | ||
344 | rc = virtsnd_ctl_query_info(snd, VIRTIO_SND_R_PCM_INFO, 0, | |
345 | snd->nsubstreams, sizeof(*info), info); | |
346 | if (rc) | |
347 | goto on_exit; | |
348 | ||
349 | for (i = 0; i < snd->nsubstreams; ++i) { | |
350 | struct virtio_pcm_substream *vss = &snd->substreams[i]; | |
351 | struct virtio_pcm *vpcm; | |
352 | ||
353 | vss->snd = snd; | |
354 | vss->sid = i; | |
355 | INIT_WORK(&vss->elapsed_period, virtsnd_pcm_period_elapsed); | |
f40a2867 AY |
356 | init_waitqueue_head(&vss->msg_empty); |
357 | spin_lock_init(&vss->lock); | |
29b96bf5 AY |
358 | |
359 | rc = virtsnd_pcm_build_hw(vss, &info[i]); | |
360 | if (rc) | |
361 | goto on_exit; | |
362 | ||
363 | vss->nid = le32_to_cpu(info[i].hdr.hda_fn_nid); | |
364 | ||
365 | vpcm = virtsnd_pcm_find_or_create(snd, vss->nid); | |
366 | if (IS_ERR(vpcm)) { | |
367 | rc = PTR_ERR(vpcm); | |
368 | goto on_exit; | |
369 | } | |
370 | ||
371 | switch (info[i].direction) { | |
372 | case VIRTIO_SND_D_OUTPUT: | |
373 | vss->direction = SNDRV_PCM_STREAM_PLAYBACK; | |
374 | break; | |
375 | case VIRTIO_SND_D_INPUT: | |
376 | vss->direction = SNDRV_PCM_STREAM_CAPTURE; | |
377 | break; | |
378 | default: | |
379 | dev_err(&vdev->dev, "SID %u: unknown direction (%u)\n", | |
380 | vss->sid, info[i].direction); | |
381 | rc = -EINVAL; | |
382 | goto on_exit; | |
383 | } | |
384 | ||
385 | vpcm->streams[vss->direction].nsubstreams++; | |
386 | } | |
387 | ||
388 | on_exit: | |
389 | kfree(info); | |
390 | ||
391 | return rc; | |
392 | } | |
393 | ||
394 | /** | |
395 | * virtsnd_pcm_build_devs() - Build ALSA PCM devices. | |
396 | * @snd: VirtIO sound device. | |
397 | * | |
398 | * Context: Any context that permits to sleep. | |
399 | * Return: 0 on success, -errno on failure. | |
400 | */ | |
401 | int virtsnd_pcm_build_devs(struct virtio_snd *snd) | |
402 | { | |
403 | struct virtio_device *vdev = snd->vdev; | |
404 | struct virtio_pcm *vpcm; | |
405 | u32 i; | |
406 | int rc; | |
407 | ||
408 | list_for_each_entry(vpcm, &snd->pcm_list, list) { | |
409 | unsigned int npbs = | |
410 | vpcm->streams[SNDRV_PCM_STREAM_PLAYBACK].nsubstreams; | |
411 | unsigned int ncps = | |
412 | vpcm->streams[SNDRV_PCM_STREAM_CAPTURE].nsubstreams; | |
413 | ||
414 | if (!npbs && !ncps) | |
415 | continue; | |
416 | ||
417 | rc = snd_pcm_new(snd->card, VIRTIO_SND_CARD_DRIVER, vpcm->nid, | |
418 | npbs, ncps, &vpcm->pcm); | |
419 | if (rc) { | |
420 | dev_err(&vdev->dev, "snd_pcm_new[%u] failed: %d\n", | |
421 | vpcm->nid, rc); | |
422 | return rc; | |
423 | } | |
424 | ||
425 | vpcm->pcm->info_flags = 0; | |
426 | vpcm->pcm->dev_class = SNDRV_PCM_CLASS_GENERIC; | |
427 | vpcm->pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; | |
428 | snprintf(vpcm->pcm->name, sizeof(vpcm->pcm->name), | |
429 | VIRTIO_SND_PCM_NAME " %u", vpcm->pcm->device); | |
430 | vpcm->pcm->private_data = vpcm; | |
431 | vpcm->pcm->nonatomic = true; | |
432 | ||
433 | for (i = 0; i < ARRAY_SIZE(vpcm->streams); ++i) { | |
434 | struct virtio_pcm_stream *stream = &vpcm->streams[i]; | |
435 | ||
436 | if (!stream->nsubstreams) | |
437 | continue; | |
438 | ||
439 | stream->substreams = | |
440 | devm_kcalloc(&vdev->dev, stream->nsubstreams, | |
441 | sizeof(*stream->substreams), | |
442 | GFP_KERNEL); | |
443 | if (!stream->substreams) | |
444 | return -ENOMEM; | |
445 | ||
446 | stream->nsubstreams = 0; | |
447 | } | |
448 | } | |
449 | ||
450 | for (i = 0; i < snd->nsubstreams; ++i) { | |
451 | struct virtio_pcm_stream *vs; | |
452 | struct virtio_pcm_substream *vss = &snd->substreams[i]; | |
453 | ||
454 | vpcm = virtsnd_pcm_find(snd, vss->nid); | |
455 | if (IS_ERR(vpcm)) | |
456 | return PTR_ERR(vpcm); | |
457 | ||
458 | vs = &vpcm->streams[vss->direction]; | |
459 | vs->substreams[vs->nsubstreams++] = vss; | |
460 | } | |
461 | ||
462 | list_for_each_entry(vpcm, &snd->pcm_list, list) { | |
463 | for (i = 0; i < ARRAY_SIZE(vpcm->streams); ++i) { | |
464 | struct virtio_pcm_stream *vs = &vpcm->streams[i]; | |
465 | struct snd_pcm_str *ks = &vpcm->pcm->streams[i]; | |
466 | struct snd_pcm_substream *kss; | |
467 | ||
468 | if (!vs->nsubstreams) | |
469 | continue; | |
470 | ||
471 | for (kss = ks->substream; kss; kss = kss->next) | |
472 | vs->substreams[kss->number]->substream = kss; | |
da76e9f3 AY |
473 | |
474 | snd_pcm_set_ops(vpcm->pcm, i, &virtsnd_pcm_ops); | |
29b96bf5 AY |
475 | } |
476 | ||
477 | snd_pcm_set_managed_buffer_all(vpcm->pcm, | |
478 | SNDRV_DMA_TYPE_VMALLOC, NULL, | |
479 | 0, 0); | |
480 | } | |
481 | ||
482 | return 0; | |
483 | } | |
f40a2867 AY |
484 | |
485 | /** | |
486 | * virtsnd_pcm_event() - Handle the PCM device event notification. | |
487 | * @snd: VirtIO sound device. | |
488 | * @event: VirtIO sound event. | |
489 | * | |
490 | * Context: Interrupt context. | |
491 | */ | |
492 | void virtsnd_pcm_event(struct virtio_snd *snd, struct virtio_snd_event *event) | |
493 | { | |
494 | struct virtio_pcm_substream *vss; | |
495 | u32 sid = le32_to_cpu(event->data); | |
496 | ||
497 | if (sid >= snd->nsubstreams) | |
498 | return; | |
499 | ||
500 | vss = &snd->substreams[sid]; | |
501 | ||
502 | switch (le32_to_cpu(event->hdr.code)) { | |
503 | case VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED: | |
504 | /* TODO: deal with shmem elapsed period */ | |
505 | break; | |
506 | case VIRTIO_SND_EVT_PCM_XRUN: | |
507 | spin_lock(&vss->lock); | |
508 | if (vss->xfer_enabled) | |
509 | vss->xfer_xrun = true; | |
510 | spin_unlock(&vss->lock); | |
511 | break; | |
512 | } | |
513 | } |