Commit | Line | Data |
---|---|---|
da76e9f3 AY |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * virtio-snd: Virtio sound device | |
4 | * Copyright (C) 2021 OpenSynergy GmbH | |
5 | */ | |
6 | #include <sound/pcm_params.h> | |
7 | ||
8 | #include "virtio_card.h" | |
9 | ||
10 | /* | |
11 | * I/O messages lifetime | |
12 | * --------------------- | |
13 | * | |
14 | * Allocation: | |
15 | * Messages are initially allocated in the ops->hw_params() after the size and | |
16 | * number of periods have been successfully negotiated. | |
17 | * | |
18 | * Freeing: | |
19 | * Messages can be safely freed after the queue has been successfully flushed | |
20 | * (RELEASE command in the ops->sync_stop()) and the ops->hw_free() has been | |
21 | * called. | |
22 | * | |
23 | * When the substream stops, the ops->sync_stop() waits until the device has | |
24 | * completed all pending messages. This wait can be interrupted either by a | |
25 | * signal or due to a timeout. In this case, the device can still access | |
26 | * messages even after calling ops->hw_free(). It can also issue an interrupt, | |
27 | * and the interrupt handler will also try to access message structures. | |
28 | * | |
29 | * Therefore, freeing of already allocated messages occurs: | |
30 | * | |
31 | * - in ops->hw_params(), if this operator was called several times in a row, | |
32 | * or if ops->hw_free() failed to free messages previously; | |
33 | * | |
34 | * - in ops->hw_free(), if the queue has been successfully flushed; | |
35 | * | |
36 | * - in dev->release(). | |
37 | */ | |
38 | ||
39 | /* Map for converting ALSA format to VirtIO format. */ | |
40 | struct virtsnd_a2v_format { | |
41 | snd_pcm_format_t alsa_bit; | |
42 | unsigned int vio_bit; | |
43 | }; | |
44 | ||
45 | static const struct virtsnd_a2v_format g_a2v_format_map[] = { | |
46 | { SNDRV_PCM_FORMAT_IMA_ADPCM, VIRTIO_SND_PCM_FMT_IMA_ADPCM }, | |
47 | { SNDRV_PCM_FORMAT_MU_LAW, VIRTIO_SND_PCM_FMT_MU_LAW }, | |
48 | { SNDRV_PCM_FORMAT_A_LAW, VIRTIO_SND_PCM_FMT_A_LAW }, | |
49 | { SNDRV_PCM_FORMAT_S8, VIRTIO_SND_PCM_FMT_S8 }, | |
50 | { SNDRV_PCM_FORMAT_U8, VIRTIO_SND_PCM_FMT_U8 }, | |
51 | { SNDRV_PCM_FORMAT_S16_LE, VIRTIO_SND_PCM_FMT_S16 }, | |
52 | { SNDRV_PCM_FORMAT_U16_LE, VIRTIO_SND_PCM_FMT_U16 }, | |
53 | { SNDRV_PCM_FORMAT_S18_3LE, VIRTIO_SND_PCM_FMT_S18_3 }, | |
54 | { SNDRV_PCM_FORMAT_U18_3LE, VIRTIO_SND_PCM_FMT_U18_3 }, | |
55 | { SNDRV_PCM_FORMAT_S20_3LE, VIRTIO_SND_PCM_FMT_S20_3 }, | |
56 | { SNDRV_PCM_FORMAT_U20_3LE, VIRTIO_SND_PCM_FMT_U20_3 }, | |
57 | { SNDRV_PCM_FORMAT_S24_3LE, VIRTIO_SND_PCM_FMT_S24_3 }, | |
58 | { SNDRV_PCM_FORMAT_U24_3LE, VIRTIO_SND_PCM_FMT_U24_3 }, | |
59 | { SNDRV_PCM_FORMAT_S20_LE, VIRTIO_SND_PCM_FMT_S20 }, | |
60 | { SNDRV_PCM_FORMAT_U20_LE, VIRTIO_SND_PCM_FMT_U20 }, | |
61 | { SNDRV_PCM_FORMAT_S24_LE, VIRTIO_SND_PCM_FMT_S24 }, | |
62 | { SNDRV_PCM_FORMAT_U24_LE, VIRTIO_SND_PCM_FMT_U24 }, | |
63 | { SNDRV_PCM_FORMAT_S32_LE, VIRTIO_SND_PCM_FMT_S32 }, | |
64 | { SNDRV_PCM_FORMAT_U32_LE, VIRTIO_SND_PCM_FMT_U32 }, | |
65 | { SNDRV_PCM_FORMAT_FLOAT_LE, VIRTIO_SND_PCM_FMT_FLOAT }, | |
66 | { SNDRV_PCM_FORMAT_FLOAT64_LE, VIRTIO_SND_PCM_FMT_FLOAT64 }, | |
67 | { SNDRV_PCM_FORMAT_DSD_U8, VIRTIO_SND_PCM_FMT_DSD_U8 }, | |
68 | { SNDRV_PCM_FORMAT_DSD_U16_LE, VIRTIO_SND_PCM_FMT_DSD_U16 }, | |
69 | { SNDRV_PCM_FORMAT_DSD_U32_LE, VIRTIO_SND_PCM_FMT_DSD_U32 }, | |
70 | { SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE, | |
71 | VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME } | |
72 | }; | |
73 | ||
74 | /* Map for converting ALSA frame rate to VirtIO frame rate. */ | |
75 | struct virtsnd_a2v_rate { | |
76 | unsigned int rate; | |
77 | unsigned int vio_bit; | |
78 | }; | |
79 | ||
80 | static const struct virtsnd_a2v_rate g_a2v_rate_map[] = { | |
81 | { 5512, VIRTIO_SND_PCM_RATE_5512 }, | |
82 | { 8000, VIRTIO_SND_PCM_RATE_8000 }, | |
83 | { 11025, VIRTIO_SND_PCM_RATE_11025 }, | |
84 | { 16000, VIRTIO_SND_PCM_RATE_16000 }, | |
85 | { 22050, VIRTIO_SND_PCM_RATE_22050 }, | |
86 | { 32000, VIRTIO_SND_PCM_RATE_32000 }, | |
87 | { 44100, VIRTIO_SND_PCM_RATE_44100 }, | |
88 | { 48000, VIRTIO_SND_PCM_RATE_48000 }, | |
89 | { 64000, VIRTIO_SND_PCM_RATE_64000 }, | |
90 | { 88200, VIRTIO_SND_PCM_RATE_88200 }, | |
91 | { 96000, VIRTIO_SND_PCM_RATE_96000 }, | |
92 | { 176400, VIRTIO_SND_PCM_RATE_176400 }, | |
93 | { 192000, VIRTIO_SND_PCM_RATE_192000 } | |
94 | }; | |
95 | ||
96 | static int virtsnd_pcm_sync_stop(struct snd_pcm_substream *substream); | |
97 | ||
98 | /** | |
99 | * virtsnd_pcm_open() - Open the PCM substream. | |
100 | * @substream: Kernel ALSA substream. | |
101 | * | |
102 | * Context: Process context. | |
103 | * Return: 0 on success, -errno on failure. | |
104 | */ | |
105 | static int virtsnd_pcm_open(struct snd_pcm_substream *substream) | |
106 | { | |
107 | struct virtio_pcm *vpcm = snd_pcm_substream_chip(substream); | |
108 | struct virtio_pcm_stream *vs = &vpcm->streams[substream->stream]; | |
109 | struct virtio_pcm_substream *vss = vs->substreams[substream->number]; | |
110 | ||
111 | substream->runtime->hw = vss->hw; | |
112 | substream->private_data = vss; | |
113 | ||
114 | snd_pcm_hw_constraint_integer(substream->runtime, | |
115 | SNDRV_PCM_HW_PARAM_PERIODS); | |
116 | ||
117 | vss->stopped = !!virtsnd_pcm_msg_pending_num(vss); | |
575483e9 | 118 | vss->suspended = false; |
da76e9f3 AY |
119 | |
120 | /* | |
121 | * If the substream has already been used, then the I/O queue may be in | |
122 | * an invalid state. Just in case, we do a check and try to return the | |
123 | * queue to its original state, if necessary. | |
124 | */ | |
125 | return virtsnd_pcm_sync_stop(substream); | |
126 | } | |
127 | ||
128 | /** | |
129 | * virtsnd_pcm_close() - Close the PCM substream. | |
130 | * @substream: Kernel ALSA substream. | |
131 | * | |
132 | * Context: Process context. | |
133 | * Return: 0. | |
134 | */ | |
135 | static int virtsnd_pcm_close(struct snd_pcm_substream *substream) | |
136 | { | |
137 | return 0; | |
138 | } | |
139 | ||
140 | /** | |
141 | * virtsnd_pcm_dev_set_params() - Set the parameters of the PCM substream on | |
142 | * the device side. | |
143 | * @vss: VirtIO PCM substream. | |
144 | * @buffer_bytes: Size of the hardware buffer. | |
145 | * @period_bytes: Size of the hardware period. | |
146 | * @channels: Selected number of channels. | |
147 | * @format: Selected sample format (SNDRV_PCM_FORMAT_XXX). | |
148 | * @rate: Selected frame rate. | |
149 | * | |
150 | * Context: Any context that permits to sleep. | |
151 | * Return: 0 on success, -errno on failure. | |
152 | */ | |
153 | static int virtsnd_pcm_dev_set_params(struct virtio_pcm_substream *vss, | |
154 | unsigned int buffer_bytes, | |
155 | unsigned int period_bytes, | |
156 | unsigned int channels, | |
157 | snd_pcm_format_t format, | |
158 | unsigned int rate) | |
159 | { | |
160 | struct virtio_snd_msg *msg; | |
161 | struct virtio_snd_pcm_set_params *request; | |
162 | unsigned int i; | |
163 | int vformat = -1; | |
164 | int vrate = -1; | |
165 | ||
166 | for (i = 0; i < ARRAY_SIZE(g_a2v_format_map); ++i) | |
167 | if (g_a2v_format_map[i].alsa_bit == format) { | |
168 | vformat = g_a2v_format_map[i].vio_bit; | |
169 | ||
170 | break; | |
171 | } | |
172 | ||
173 | for (i = 0; i < ARRAY_SIZE(g_a2v_rate_map); ++i) | |
174 | if (g_a2v_rate_map[i].rate == rate) { | |
175 | vrate = g_a2v_rate_map[i].vio_bit; | |
176 | ||
177 | break; | |
178 | } | |
179 | ||
180 | if (vformat == -1 || vrate == -1) | |
181 | return -EINVAL; | |
182 | ||
183 | msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_SET_PARAMS, | |
184 | GFP_KERNEL); | |
185 | if (!msg) | |
186 | return -ENOMEM; | |
187 | ||
188 | request = virtsnd_ctl_msg_request(msg); | |
189 | request->buffer_bytes = cpu_to_le32(buffer_bytes); | |
190 | request->period_bytes = cpu_to_le32(period_bytes); | |
191 | request->channels = channels; | |
192 | request->format = vformat; | |
193 | request->rate = vrate; | |
194 | ||
195 | if (vss->features & (1U << VIRTIO_SND_PCM_F_MSG_POLLING)) | |
196 | request->features |= | |
197 | cpu_to_le32(1U << VIRTIO_SND_PCM_F_MSG_POLLING); | |
198 | ||
199 | if (vss->features & (1U << VIRTIO_SND_PCM_F_EVT_XRUNS)) | |
200 | request->features |= | |
201 | cpu_to_le32(1U << VIRTIO_SND_PCM_F_EVT_XRUNS); | |
202 | ||
203 | return virtsnd_ctl_msg_send_sync(vss->snd, msg); | |
204 | } | |
205 | ||
206 | /** | |
207 | * virtsnd_pcm_hw_params() - Set the parameters of the PCM substream. | |
208 | * @substream: Kernel ALSA substream. | |
209 | * @hw_params: Hardware parameters. | |
210 | * | |
211 | * Context: Process context. | |
212 | * Return: 0 on success, -errno on failure. | |
213 | */ | |
214 | static int virtsnd_pcm_hw_params(struct snd_pcm_substream *substream, | |
215 | struct snd_pcm_hw_params *hw_params) | |
216 | { | |
217 | struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); | |
218 | struct virtio_device *vdev = vss->snd->vdev; | |
219 | int rc; | |
220 | ||
221 | if (virtsnd_pcm_msg_pending_num(vss)) { | |
222 | dev_err(&vdev->dev, "SID %u: invalid I/O queue state\n", | |
223 | vss->sid); | |
224 | return -EBADFD; | |
225 | } | |
226 | ||
227 | rc = virtsnd_pcm_dev_set_params(vss, params_buffer_bytes(hw_params), | |
228 | params_period_bytes(hw_params), | |
229 | params_channels(hw_params), | |
230 | params_format(hw_params), | |
231 | params_rate(hw_params)); | |
232 | if (rc) | |
233 | return rc; | |
234 | ||
235 | /* | |
236 | * Free previously allocated messages if ops->hw_params() is called | |
237 | * several times in a row, or if ops->hw_free() failed to free messages. | |
238 | */ | |
239 | virtsnd_pcm_msg_free(vss); | |
240 | ||
241 | return virtsnd_pcm_msg_alloc(vss, params_periods(hw_params), | |
242 | params_period_bytes(hw_params)); | |
243 | } | |
244 | ||
245 | /** | |
246 | * virtsnd_pcm_hw_free() - Reset the parameters of the PCM substream. | |
247 | * @substream: Kernel ALSA substream. | |
248 | * | |
249 | * Context: Process context. | |
250 | * Return: 0 | |
251 | */ | |
252 | static int virtsnd_pcm_hw_free(struct snd_pcm_substream *substream) | |
253 | { | |
254 | struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); | |
255 | ||
256 | /* If the queue is flushed, we can safely free the messages here. */ | |
257 | if (!virtsnd_pcm_msg_pending_num(vss)) | |
258 | virtsnd_pcm_msg_free(vss); | |
259 | ||
260 | return 0; | |
261 | } | |
262 | ||
263 | /** | |
264 | * virtsnd_pcm_prepare() - Prepare the PCM substream. | |
265 | * @substream: Kernel ALSA substream. | |
266 | * | |
267 | * Context: Process context. | |
268 | * Return: 0 on success, -errno on failure. | |
269 | */ | |
270 | static int virtsnd_pcm_prepare(struct snd_pcm_substream *substream) | |
271 | { | |
272 | struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); | |
273 | struct virtio_device *vdev = vss->snd->vdev; | |
274 | struct virtio_snd_msg *msg; | |
275 | ||
575483e9 AY |
276 | if (!vss->suspended) { |
277 | if (virtsnd_pcm_msg_pending_num(vss)) { | |
278 | dev_err(&vdev->dev, "SID %u: invalid I/O queue state\n", | |
279 | vss->sid); | |
280 | return -EBADFD; | |
281 | } | |
282 | ||
283 | vss->buffer_bytes = snd_pcm_lib_buffer_bytes(substream); | |
284 | vss->hw_ptr = 0; | |
575483e9 AY |
285 | } else { |
286 | struct snd_pcm_runtime *runtime = substream->runtime; | |
287 | unsigned int buffer_bytes = snd_pcm_lib_buffer_bytes(substream); | |
288 | unsigned int period_bytes = snd_pcm_lib_period_bytes(substream); | |
289 | int rc; | |
290 | ||
291 | rc = virtsnd_pcm_dev_set_params(vss, buffer_bytes, period_bytes, | |
292 | runtime->channels, | |
293 | runtime->format, runtime->rate); | |
294 | if (rc) | |
295 | return rc; | |
da76e9f3 AY |
296 | } |
297 | ||
da76e9f3 | 298 | vss->xfer_xrun = false; |
575483e9 | 299 | vss->suspended = false; |
da76e9f3 AY |
300 | vss->msg_count = 0; |
301 | ||
fe981e67 MEVL |
302 | memset(&vss->pcm_indirect, 0, sizeof(vss->pcm_indirect)); |
303 | vss->pcm_indirect.sw_buffer_size = | |
304 | vss->pcm_indirect.hw_buffer_size = | |
305 | snd_pcm_lib_buffer_bytes(substream); | |
306 | ||
da76e9f3 AY |
307 | msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_PREPARE, |
308 | GFP_KERNEL); | |
309 | if (!msg) | |
310 | return -ENOMEM; | |
311 | ||
312 | return virtsnd_ctl_msg_send_sync(vss->snd, msg); | |
313 | } | |
314 | ||
315 | /** | |
316 | * virtsnd_pcm_trigger() - Process command for the PCM substream. | |
317 | * @substream: Kernel ALSA substream. | |
318 | * @command: Substream command (SNDRV_PCM_TRIGGER_XXX). | |
319 | * | |
320 | * Context: Any context. Takes and releases the VirtIO substream spinlock. | |
321 | * May take and release the tx/rx queue spinlock. | |
322 | * Return: 0 on success, -errno on failure. | |
323 | */ | |
324 | static int virtsnd_pcm_trigger(struct snd_pcm_substream *substream, int command) | |
325 | { | |
326 | struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); | |
327 | struct virtio_snd *snd = vss->snd; | |
328 | struct virtio_snd_queue *queue; | |
329 | struct virtio_snd_msg *msg; | |
330 | unsigned long flags; | |
fe981e67 | 331 | int rc = 0; |
da76e9f3 AY |
332 | |
333 | switch (command) { | |
334 | case SNDRV_PCM_TRIGGER_START: | |
335 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
336 | queue = virtsnd_pcm_queue(vss); | |
337 | ||
338 | spin_lock_irqsave(&queue->lock, flags); | |
339 | spin_lock(&vss->lock); | |
fe981e67 MEVL |
340 | if (vss->direction == SNDRV_PCM_STREAM_CAPTURE) |
341 | rc = virtsnd_pcm_msg_send(vss, 0, vss->buffer_bytes); | |
da76e9f3 AY |
342 | if (!rc) |
343 | vss->xfer_enabled = true; | |
344 | spin_unlock(&vss->lock); | |
345 | spin_unlock_irqrestore(&queue->lock, flags); | |
346 | if (rc) | |
347 | return rc; | |
348 | ||
349 | msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_START, | |
350 | GFP_KERNEL); | |
351 | if (!msg) { | |
352 | spin_lock_irqsave(&vss->lock, flags); | |
353 | vss->xfer_enabled = false; | |
354 | spin_unlock_irqrestore(&vss->lock, flags); | |
355 | ||
356 | return -ENOMEM; | |
357 | } | |
358 | ||
359 | return virtsnd_ctl_msg_send_sync(snd, msg); | |
575483e9 AY |
360 | case SNDRV_PCM_TRIGGER_SUSPEND: |
361 | vss->suspended = true; | |
362 | fallthrough; | |
da76e9f3 AY |
363 | case SNDRV_PCM_TRIGGER_STOP: |
364 | vss->stopped = true; | |
365 | fallthrough; | |
366 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
367 | spin_lock_irqsave(&vss->lock, flags); | |
368 | vss->xfer_enabled = false; | |
369 | spin_unlock_irqrestore(&vss->lock, flags); | |
370 | ||
371 | msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_STOP, | |
372 | GFP_KERNEL); | |
373 | if (!msg) | |
374 | return -ENOMEM; | |
375 | ||
376 | return virtsnd_ctl_msg_send_sync(snd, msg); | |
377 | default: | |
378 | return -EINVAL; | |
379 | } | |
380 | } | |
381 | ||
382 | /** | |
383 | * virtsnd_pcm_sync_stop() - Synchronous PCM substream stop. | |
384 | * @substream: Kernel ALSA substream. | |
385 | * | |
386 | * The function can be called both from the upper level or from the driver | |
387 | * itself. | |
388 | * | |
389 | * Context: Process context. Takes and releases the VirtIO substream spinlock. | |
390 | * Return: 0 on success, -errno on failure. | |
391 | */ | |
392 | static int virtsnd_pcm_sync_stop(struct snd_pcm_substream *substream) | |
393 | { | |
394 | struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); | |
395 | struct virtio_snd *snd = vss->snd; | |
396 | struct virtio_snd_msg *msg; | |
397 | unsigned int js = msecs_to_jiffies(virtsnd_msg_timeout_ms); | |
398 | int rc; | |
399 | ||
400 | cancel_work_sync(&vss->elapsed_period); | |
401 | ||
402 | if (!vss->stopped) | |
403 | return 0; | |
404 | ||
405 | msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_RELEASE, | |
406 | GFP_KERNEL); | |
407 | if (!msg) | |
408 | return -ENOMEM; | |
409 | ||
410 | rc = virtsnd_ctl_msg_send_sync(snd, msg); | |
411 | if (rc) | |
412 | return rc; | |
413 | ||
414 | /* | |
415 | * The spec states that upon receipt of the RELEASE command "the device | |
416 | * MUST complete all pending I/O messages for the specified stream ID". | |
417 | * Thus, we consider the absence of I/O messages in the queue as an | |
418 | * indication that the substream has been released. | |
419 | */ | |
420 | rc = wait_event_interruptible_timeout(vss->msg_empty, | |
421 | !virtsnd_pcm_msg_pending_num(vss), | |
422 | js); | |
423 | if (rc <= 0) { | |
424 | dev_warn(&snd->vdev->dev, "SID %u: failed to flush I/O queue\n", | |
425 | vss->sid); | |
426 | ||
427 | return !rc ? -ETIMEDOUT : rc; | |
428 | } | |
429 | ||
430 | vss->stopped = false; | |
431 | ||
432 | return 0; | |
433 | } | |
434 | ||
435 | /** | |
fe981e67 MEVL |
436 | * virtsnd_pcm_pb_pointer() - Get the current hardware position for the PCM |
437 | * substream for playback. | |
da76e9f3 AY |
438 | * @substream: Kernel ALSA substream. |
439 | * | |
fe981e67 | 440 | * Context: Any context. |
da76e9f3 AY |
441 | * Return: Hardware position in frames inside [0 ... buffer_size) range. |
442 | */ | |
443 | static snd_pcm_uframes_t | |
fe981e67 MEVL |
444 | virtsnd_pcm_pb_pointer(struct snd_pcm_substream *substream) |
445 | { | |
446 | struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); | |
447 | ||
448 | return snd_pcm_indirect_playback_pointer(substream, | |
449 | &vss->pcm_indirect, | |
450 | vss->hw_ptr); | |
451 | } | |
452 | ||
453 | /** | |
454 | * virtsnd_pcm_cp_pointer() - Get the current hardware position for the PCM | |
455 | * substream for capture. | |
456 | * @substream: Kernel ALSA substream. | |
457 | * | |
458 | * Context: Any context. | |
459 | * Return: Hardware position in frames inside [0 ... buffer_size) range. | |
460 | */ | |
461 | static snd_pcm_uframes_t | |
462 | virtsnd_pcm_cp_pointer(struct snd_pcm_substream *substream) | |
463 | { | |
464 | struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); | |
465 | ||
466 | return snd_pcm_indirect_capture_pointer(substream, | |
467 | &vss->pcm_indirect, | |
468 | vss->hw_ptr); | |
469 | } | |
470 | ||
471 | static void virtsnd_pcm_trans_copy(struct snd_pcm_substream *substream, | |
472 | struct snd_pcm_indirect *rec, size_t bytes) | |
da76e9f3 AY |
473 | { |
474 | struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); | |
fe981e67 MEVL |
475 | |
476 | virtsnd_pcm_msg_send(vss, rec->sw_data, bytes); | |
477 | } | |
478 | ||
479 | static int virtsnd_pcm_pb_ack(struct snd_pcm_substream *substream) | |
480 | { | |
481 | struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); | |
482 | struct virtio_snd_queue *queue = virtsnd_pcm_queue(vss); | |
483 | unsigned long flags; | |
484 | int rc; | |
485 | ||
486 | spin_lock_irqsave(&queue->lock, flags); | |
487 | spin_lock(&vss->lock); | |
488 | ||
489 | rc = snd_pcm_indirect_playback_transfer(substream, &vss->pcm_indirect, | |
490 | virtsnd_pcm_trans_copy); | |
491 | ||
492 | spin_unlock(&vss->lock); | |
493 | spin_unlock_irqrestore(&queue->lock, flags); | |
494 | ||
495 | return rc; | |
496 | } | |
497 | ||
498 | static int virtsnd_pcm_cp_ack(struct snd_pcm_substream *substream) | |
499 | { | |
500 | struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); | |
501 | struct virtio_snd_queue *queue = virtsnd_pcm_queue(vss); | |
da76e9f3 | 502 | unsigned long flags; |
fe981e67 MEVL |
503 | int rc; |
504 | ||
505 | spin_lock_irqsave(&queue->lock, flags); | |
506 | spin_lock(&vss->lock); | |
507 | ||
508 | rc = snd_pcm_indirect_capture_transfer(substream, &vss->pcm_indirect, | |
509 | virtsnd_pcm_trans_copy); | |
da76e9f3 | 510 | |
fe981e67 MEVL |
511 | spin_unlock(&vss->lock); |
512 | spin_unlock_irqrestore(&queue->lock, flags); | |
da76e9f3 | 513 | |
fe981e67 | 514 | return rc; |
da76e9f3 AY |
515 | } |
516 | ||
517 | /* PCM substream operators map. */ | |
fe981e67 MEVL |
518 | const struct snd_pcm_ops virtsnd_pcm_ops[] = { |
519 | { | |
520 | .open = virtsnd_pcm_open, | |
521 | .close = virtsnd_pcm_close, | |
522 | .ioctl = snd_pcm_lib_ioctl, | |
523 | .hw_params = virtsnd_pcm_hw_params, | |
524 | .hw_free = virtsnd_pcm_hw_free, | |
525 | .prepare = virtsnd_pcm_prepare, | |
526 | .trigger = virtsnd_pcm_trigger, | |
527 | .sync_stop = virtsnd_pcm_sync_stop, | |
528 | .pointer = virtsnd_pcm_pb_pointer, | |
529 | .ack = virtsnd_pcm_pb_ack, | |
530 | }, | |
531 | { | |
532 | .open = virtsnd_pcm_open, | |
533 | .close = virtsnd_pcm_close, | |
534 | .ioctl = snd_pcm_lib_ioctl, | |
535 | .hw_params = virtsnd_pcm_hw_params, | |
536 | .hw_free = virtsnd_pcm_hw_free, | |
537 | .prepare = virtsnd_pcm_prepare, | |
538 | .trigger = virtsnd_pcm_trigger, | |
539 | .sync_stop = virtsnd_pcm_sync_stop, | |
540 | .pointer = virtsnd_pcm_cp_pointer, | |
541 | .ack = virtsnd_pcm_cp_ack, | |
542 | }, | |
da76e9f3 | 543 | }; |