Lines Matching +full:virtio +full:- +full:device

1 // SPDX-License-Identifier: GPL-2.0+
3 * virtio-snd: Virtio sound device
31 /* Map for converting VirtIO format to ALSA format. */
61 /* Map for converting VirtIO frame rate to ALSA frame rate. */
84 * virtsnd_pcm_build_hw() - Parse substream config and build HW descriptor.
85 * @vss: VirtIO substream.
86 * @info: VirtIO substream information entry.
89 * Return: 0 on success, -EINVAL if configuration is invalid.
94 struct virtio_device *vdev = vss->snd->vdev; in virtsnd_pcm_build_hw()
100 vss->features = le32_to_cpu(info->features); in virtsnd_pcm_build_hw()
103 * TODO: set SNDRV_PCM_INFO_{BATCH,BLOCK_TRANSFER} if device supports in virtsnd_pcm_build_hw()
104 * only message-based transport. in virtsnd_pcm_build_hw()
106 vss->hw.info = in virtsnd_pcm_build_hw()
116 if (!info->channels_min || info->channels_min > info->channels_max) { in virtsnd_pcm_build_hw()
117 dev_err(&vdev->dev, in virtsnd_pcm_build_hw()
119 vss->sid, info->channels_min, info->channels_max); in virtsnd_pcm_build_hw()
120 return -EINVAL; in virtsnd_pcm_build_hw()
123 vss->hw.channels_min = info->channels_min; in virtsnd_pcm_build_hw()
124 vss->hw.channels_max = info->channels_max; in virtsnd_pcm_build_hw()
126 values = le64_to_cpu(info->formats); in virtsnd_pcm_build_hw()
128 vss->hw.formats = 0; in virtsnd_pcm_build_hw()
141 vss->hw.formats |= pcm_format_to_bits(alsa_fmt); in virtsnd_pcm_build_hw()
144 if (!vss->hw.formats) { in virtsnd_pcm_build_hw()
145 dev_err(&vdev->dev, in virtsnd_pcm_build_hw()
147 vss->sid); in virtsnd_pcm_build_hw()
148 return -EINVAL; in virtsnd_pcm_build_hw()
151 values = le64_to_cpu(info->rates); in virtsnd_pcm_build_hw()
153 vss->hw.rates = 0; in virtsnd_pcm_build_hw()
157 if (!vss->hw.rate_min || in virtsnd_pcm_build_hw()
158 vss->hw.rate_min > g_v2a_rate_map[i].rate) in virtsnd_pcm_build_hw()
159 vss->hw.rate_min = g_v2a_rate_map[i].rate; in virtsnd_pcm_build_hw()
161 if (vss->hw.rate_max < g_v2a_rate_map[i].rate) in virtsnd_pcm_build_hw()
162 vss->hw.rate_max = g_v2a_rate_map[i].rate; in virtsnd_pcm_build_hw()
164 vss->hw.rates |= g_v2a_rate_map[i].alsa_bit; in virtsnd_pcm_build_hw()
167 if (!vss->hw.rates) { in virtsnd_pcm_build_hw()
168 dev_err(&vdev->dev, in virtsnd_pcm_build_hw()
170 vss->sid); in virtsnd_pcm_build_hw()
171 return -EINVAL; in virtsnd_pcm_build_hw()
174 vss->hw.periods_min = pcm_periods_min; in virtsnd_pcm_build_hw()
175 vss->hw.periods_max = pcm_periods_max; in virtsnd_pcm_build_hw()
184 vss->hw.buffer_bytes_max = in virtsnd_pcm_build_hw()
185 PAGE_ALIGN(sample_max * vss->hw.channels_max * pcm_buffer_ms * in virtsnd_pcm_build_hw()
186 (vss->hw.rate_max / MSEC_PER_SEC)); in virtsnd_pcm_build_hw()
195 vss->hw.period_bytes_min = in virtsnd_pcm_build_hw()
196 sample_min * vss->hw.channels_min * pcm_period_ms_min * in virtsnd_pcm_build_hw()
197 (vss->hw.rate_min / MSEC_PER_SEC); in virtsnd_pcm_build_hw()
203 vss->hw.period_bytes_max = in virtsnd_pcm_build_hw()
204 sample_max * vss->hw.channels_max * pcm_period_ms_max * in virtsnd_pcm_build_hw()
205 (vss->hw.rate_max / MSEC_PER_SEC); in virtsnd_pcm_build_hw()
211 * virtsnd_pcm_find() - Find the PCM device for the specified node ID.
212 * @snd: VirtIO sound device.
216 * Return: a pointer to the PCM device or ERR_PTR(-ENOENT).
222 list_for_each_entry(vpcm, &snd->pcm_list, list) in virtsnd_pcm_find()
223 if (vpcm->nid == nid) in virtsnd_pcm_find()
226 return ERR_PTR(-ENOENT); in virtsnd_pcm_find()
230 * virtsnd_pcm_find_or_create() - Find or create the PCM device for the
232 * @snd: VirtIO sound device.
236 * Return: a pointer to the PCM device or ERR_PTR(-errno).
240 struct virtio_device *vdev = snd->vdev; in virtsnd_pcm_find_or_create()
247 vpcm = devm_kzalloc(&vdev->dev, sizeof(*vpcm), GFP_KERNEL); in virtsnd_pcm_find_or_create()
249 return ERR_PTR(-ENOMEM); in virtsnd_pcm_find_or_create()
251 vpcm->nid = nid; in virtsnd_pcm_find_or_create()
252 list_add_tail(&vpcm->list, &snd->pcm_list); in virtsnd_pcm_find_or_create()
258 * virtsnd_pcm_validate() - Validate if the device can be started.
259 * @vdev: VirtIO parent device.
262 * Return: 0 on success, -EINVAL on failure.
267 dev_err(&vdev->dev, in virtsnd_pcm_validate()
270 return -EINVAL; in virtsnd_pcm_validate()
274 dev_err(&vdev->dev, in virtsnd_pcm_validate()
277 return -EINVAL; in virtsnd_pcm_validate()
281 dev_err(&vdev->dev, in virtsnd_pcm_validate()
284 return -EINVAL; in virtsnd_pcm_validate()
288 dev_err(&vdev->dev, in virtsnd_pcm_validate()
291 return -EINVAL; in virtsnd_pcm_validate()
298 * virtsnd_pcm_period_elapsed() - Kernel work function to handle the elapsed
304 * devices operate in non-atomic mode.
313 snd_pcm_period_elapsed(vss->substream); in virtsnd_pcm_period_elapsed()
317 * virtsnd_pcm_parse_cfg() - Parse the stream configuration.
318 * @snd: VirtIO sound device.
320 * This function is called during initial device initialization.
323 * Return: 0 on success, -errno on failure.
327 struct virtio_device *vdev = snd->vdev; in virtsnd_pcm_parse_cfg()
333 &snd->nsubstreams); in virtsnd_pcm_parse_cfg()
334 if (!snd->nsubstreams) in virtsnd_pcm_parse_cfg()
337 snd->substreams = devm_kcalloc(&vdev->dev, snd->nsubstreams, in virtsnd_pcm_parse_cfg()
338 sizeof(*snd->substreams), GFP_KERNEL); in virtsnd_pcm_parse_cfg()
339 if (!snd->substreams) in virtsnd_pcm_parse_cfg()
340 return -ENOMEM; in virtsnd_pcm_parse_cfg()
342 info = kcalloc(snd->nsubstreams, sizeof(*info), GFP_KERNEL); in virtsnd_pcm_parse_cfg()
344 return -ENOMEM; in virtsnd_pcm_parse_cfg()
347 snd->nsubstreams, sizeof(*info), info); in virtsnd_pcm_parse_cfg()
351 for (i = 0; i < snd->nsubstreams; ++i) { in virtsnd_pcm_parse_cfg()
352 struct virtio_pcm_substream *vss = &snd->substreams[i]; in virtsnd_pcm_parse_cfg()
355 vss->snd = snd; in virtsnd_pcm_parse_cfg()
356 vss->sid = i; in virtsnd_pcm_parse_cfg()
357 INIT_WORK(&vss->elapsed_period, virtsnd_pcm_period_elapsed); in virtsnd_pcm_parse_cfg()
358 init_waitqueue_head(&vss->msg_empty); in virtsnd_pcm_parse_cfg()
359 spin_lock_init(&vss->lock); in virtsnd_pcm_parse_cfg()
365 vss->nid = le32_to_cpu(info[i].hdr.hda_fn_nid); in virtsnd_pcm_parse_cfg()
367 vpcm = virtsnd_pcm_find_or_create(snd, vss->nid); in virtsnd_pcm_parse_cfg()
375 vss->direction = SNDRV_PCM_STREAM_PLAYBACK; in virtsnd_pcm_parse_cfg()
378 vss->direction = SNDRV_PCM_STREAM_CAPTURE; in virtsnd_pcm_parse_cfg()
381 dev_err(&vdev->dev, "SID %u: unknown direction (%u)\n", in virtsnd_pcm_parse_cfg()
382 vss->sid, info[i].direction); in virtsnd_pcm_parse_cfg()
383 rc = -EINVAL; in virtsnd_pcm_parse_cfg()
387 vpcm->streams[vss->direction].nsubstreams++; in virtsnd_pcm_parse_cfg()
397 * virtsnd_pcm_build_devs() - Build ALSA PCM devices.
398 * @snd: VirtIO sound device.
401 * Return: 0 on success, -errno on failure.
405 struct virtio_device *vdev = snd->vdev; in virtsnd_pcm_build_devs()
410 list_for_each_entry(vpcm, &snd->pcm_list, list) { in virtsnd_pcm_build_devs()
412 vpcm->streams[SNDRV_PCM_STREAM_PLAYBACK].nsubstreams; in virtsnd_pcm_build_devs()
414 vpcm->streams[SNDRV_PCM_STREAM_CAPTURE].nsubstreams; in virtsnd_pcm_build_devs()
419 rc = snd_pcm_new(snd->card, VIRTIO_SND_CARD_DRIVER, vpcm->nid, in virtsnd_pcm_build_devs()
420 npbs, ncps, &vpcm->pcm); in virtsnd_pcm_build_devs()
422 dev_err(&vdev->dev, "snd_pcm_new[%u] failed: %d\n", in virtsnd_pcm_build_devs()
423 vpcm->nid, rc); in virtsnd_pcm_build_devs()
427 vpcm->pcm->info_flags = 0; in virtsnd_pcm_build_devs()
428 vpcm->pcm->dev_class = SNDRV_PCM_CLASS_GENERIC; in virtsnd_pcm_build_devs()
429 vpcm->pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; in virtsnd_pcm_build_devs()
430 snprintf(vpcm->pcm->name, sizeof(vpcm->pcm->name), in virtsnd_pcm_build_devs()
431 VIRTIO_SND_PCM_NAME " %u", vpcm->pcm->device); in virtsnd_pcm_build_devs()
432 vpcm->pcm->private_data = vpcm; in virtsnd_pcm_build_devs()
433 vpcm->pcm->nonatomic = true; in virtsnd_pcm_build_devs()
435 for (i = 0; i < ARRAY_SIZE(vpcm->streams); ++i) { in virtsnd_pcm_build_devs()
436 struct virtio_pcm_stream *stream = &vpcm->streams[i]; in virtsnd_pcm_build_devs()
438 if (!stream->nsubstreams) in virtsnd_pcm_build_devs()
441 stream->substreams = in virtsnd_pcm_build_devs()
442 devm_kcalloc(&vdev->dev, stream->nsubstreams, in virtsnd_pcm_build_devs()
443 sizeof(*stream->substreams), in virtsnd_pcm_build_devs()
445 if (!stream->substreams) in virtsnd_pcm_build_devs()
446 return -ENOMEM; in virtsnd_pcm_build_devs()
448 stream->nsubstreams = 0; in virtsnd_pcm_build_devs()
452 for (i = 0; i < snd->nsubstreams; ++i) { in virtsnd_pcm_build_devs()
454 struct virtio_pcm_substream *vss = &snd->substreams[i]; in virtsnd_pcm_build_devs()
456 vpcm = virtsnd_pcm_find(snd, vss->nid); in virtsnd_pcm_build_devs()
460 vs = &vpcm->streams[vss->direction]; in virtsnd_pcm_build_devs()
461 vs->substreams[vs->nsubstreams++] = vss; in virtsnd_pcm_build_devs()
464 list_for_each_entry(vpcm, &snd->pcm_list, list) { in virtsnd_pcm_build_devs()
465 for (i = 0; i < ARRAY_SIZE(vpcm->streams); ++i) { in virtsnd_pcm_build_devs()
466 struct virtio_pcm_stream *vs = &vpcm->streams[i]; in virtsnd_pcm_build_devs()
467 struct snd_pcm_str *ks = &vpcm->pcm->streams[i]; in virtsnd_pcm_build_devs()
470 if (!vs->nsubstreams) in virtsnd_pcm_build_devs()
473 for (kss = ks->substream; kss; kss = kss->next) in virtsnd_pcm_build_devs()
474 vs->substreams[kss->number]->substream = kss; in virtsnd_pcm_build_devs()
476 snd_pcm_set_ops(vpcm->pcm, i, &virtsnd_pcm_ops[i]); in virtsnd_pcm_build_devs()
479 snd_pcm_set_managed_buffer_all(vpcm->pcm, in virtsnd_pcm_build_devs()
488 * virtsnd_pcm_event() - Handle the PCM device event notification.
489 * @snd: VirtIO sound device.
490 * @event: VirtIO sound event.
497 u32 sid = le32_to_cpu(event->data); in virtsnd_pcm_event()
499 if (sid >= snd->nsubstreams) in virtsnd_pcm_event()
502 vss = &snd->substreams[sid]; in virtsnd_pcm_event()
504 switch (le32_to_cpu(event->hdr.code)) { in virtsnd_pcm_event()
509 spin_lock(&vss->lock); in virtsnd_pcm_event()
510 if (vss->xfer_enabled) in virtsnd_pcm_event()
511 vss->xfer_xrun = true; in virtsnd_pcm_event()
512 spin_unlock(&vss->lock); in virtsnd_pcm_event()